diff --git a/Makefile.griffin b/Makefile.griffin index 4838988047..4790d931b3 100644 --- a/Makefile.griffin +++ b/Makefile.griffin @@ -82,7 +82,7 @@ else ifeq ($(platform), ps3-cobra) # NGC/Wii - libogc else ifeq ($(libogc_platform), 1) -EXTERNAL_LIBOGC=1 +EXTERNAL_LIBOGC=0 CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT) CXX = $(DEVKITPPC)/bin/powerpc-eabi-g++$(EXE_EXT) LD = $(DEVKITPPC)/bin/powerpc-eabi-ld$(EXE_EXT) @@ -93,6 +93,8 @@ EXTERNAL_LIBOGC=1 INCLUDE += -I. -I$(DEVKITPRO)/libogc/include -Ideps/libz -Iwii/libogc/include ifeq ($(EXTERNAL_LIBOGC), 1) + CFLAGS += -DEXTERNAL_LIBOGC + CXXFLAGS += -DEXTERNAL_LIBOGC ifeq ($(platform), ngc) LIBDIRS += -L$(DEVKITPRO)/libogc/lib/cube @@ -101,6 +103,8 @@ LIBDIRS += -L$(DEVKITPRO)/libogc/lib/wii endif else + CFLAGS += -DINTERNAL_LIBOGC + CXXFLAGS += -DINTERNAL_LIBOGC ifeq ($(platform), ngc) LIBDIRS += -Lwii/libogc/libs/cube @@ -130,7 +134,10 @@ ifeq ($(BIG_STACK), 1) LDFLAGS += -T bootstrap/gx/rvl.ld endif endif - LIBS += -lfat -logc + LIBS += -logc +ifeq ($(EXTERNAL_LIBOGC), 1) + LIBS += -lfat +endif ifeq ($(platform), wii) LIBS += -lwiiuse -lbte diff --git a/Makefile.wii.salamander b/Makefile.wii.salamander index 29f57d78cb..24c6f28d77 100644 --- a/Makefile.wii.salamander +++ b/Makefile.wii.salamander @@ -6,7 +6,7 @@ DEBUG = 0 HAVE_LOGGER = 0 HAVE_FILE_LOGGER = 0 -EXTERNAL_LIBOGC = 1 +EXTERNAL_LIBOGC = 0 # system platform system_platform = unix @@ -43,7 +43,11 @@ endif MACHDEP := -DGEKKO -DHW_RVL -mrvl -mcpu=750 -meabi -mhard-float CFLAGS += -Wall -std=gnu99 $(MACHDEP) $(INCLUDE) LDFLAGS := $(MACHDEP) -Wl,-Map,$(notdir $(ELF_TARGET)).map -LIBS := -lfat -lwiiuse -logc -lbte +LIBS := -lwiiuse -logc -lbte + +ifeq ($(EXTERNAL_LIBOGC), 1) +LIBS += -lfat +endif APP_BOOTER_DIR = wii/app_booter @@ -67,6 +71,20 @@ OBJ = frontend/frontend_salamander.o \ verbosity.o \ $(APP_BOOTER_DIR)/app_booter.binobj +ifeq ($(EXTERNAL_LIBOGC), 1) +else +OBJ += wii/libogc/libfat/cache.o \ + wii/libogc/libfat/directory.o \ + wii/libogc/libfat/disc.o \ + wii/libogc/libfat/fatdir.o \ + wii/libogc/libfat/fatfile.o \ + wii/libogc/libfat/file_allocation_table.o \ + wii/libogc/libfat/filetime.o \ + wii/libogc/libfat/libfat.o \ + wii/libogc/libfat/lock.o \ + wii/libogc/libfat/partition.o +endif + ifeq ($(HAVE_LOGGER), 1) CFLAGS += -DHAVE_LOGGER CFLAGS += -DPC_DEVELOPMENT_IP_ADDRESS=\"$(PC_DEVELOPMENT_IP_ADDRESS)\" -DPC_DEVELOPMENT_UDP_PORT=$(PC_DEVELOPMENT_UDP_PORT) diff --git a/griffin/griffin.c b/griffin/griffin.c index df49afc43a..9eeebde565 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -45,6 +45,21 @@ CONSOLE EXTENSIONS #include "../memory/ngc/ssaram.c" #endif +#ifdef INTERNAL_LIBOGC +#ifdef HW_RVL +#include "../wii/libogc/libfat/cache.c" +#include "../wii/libogc/libfat/directory.c" +#include "../wii/libogc/libfat/disc.c" +#include "../wii/libogc/libfat/fatdir.c" +#include "../wii/libogc/libfat/fatfile.c" +#include "../wii/libogc/libfat/file_allocation_table.c" +#include "../wii/libogc/libfat/filetime.c" +#include "../wii/libogc/libfat/libfat.c" +#include "../wii/libogc/libfat/lock.c" +#include "../wii/libogc/libfat/partition.c" +#endif +#endif + #endif /*============================================================ diff --git a/wii/app_booter/Makefile b/wii/app_booter/Makefile index 22576a832c..6a4b48fc92 100644 --- a/wii/app_booter/Makefile +++ b/wii/app_booter/Makefile @@ -14,7 +14,7 @@ else ifneq ($(findstring MINGW,$(shell uname -a)),) system_platform = win endif -EXTERNAL_LIBOGC = 1 +EXTERNAL_LIBOGC = 0 CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT) LD = $(DEVKITPPC)/bin/powerpc-eabi-ld$(EXE_EXT) OBJCOPY = $(DEVKITPPC)/bin/powerpc-eabi-objcopy$(EXE_EXT) diff --git a/wii/libogc/include/aesndlib.h b/wii/libogc/include/aesndlib.h new file mode 100644 index 0000000000..4d47d045c1 --- /dev/null +++ b/wii/libogc/include/aesndlib.h @@ -0,0 +1,61 @@ +#ifndef __AESNDLIB_H__ +#define __AESNDLIB_H__ + +#include + +#define MAX_VOICES 32 +#define SND_BUFFERSIZE 384 // output 2ms sound data at 48KHz +#define DSP_STREAMBUFFER_SIZE 1152 // input 2ms sound data at max. 144KHz + +#if defined(HW_DOL) + #define DSP_DEFAULT_FREQ 48044 +#elif defined(HW_RVL) + #define DSP_DEFAULT_FREQ 48000 +#endif + +#define VOICE_STATE_STOPPED 0 +#define VOICE_STATE_RUNNING 1 +#define VOICE_STATE_STREAM 2 + +#define VOICE_MONO8 0x00000000 +#define VOICE_STEREO8 0x00000001 +#define VOICE_MONO16 0x00000002 +#define VOICE_STEREO16 0x00000003 + +#define VOICE_FREQ32KHZ 32000 +#define VOICE_FREQ48KHZ 48000 + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct aesndpb_t AESNDPB; + +typedef void (*AESNDVoiceCallback)(AESNDPB *pb,u32 state); +typedef void (*AESNDAudioCallback)(void *audio_buffer,u32 len); + +void AESND_Init(); +void AESND_Reset(); +void AESND_Pause(bool pause); +u32 AESND_GetDSPProcessTime(); +f32 AESND_GetDSPProcessUsage(); +AESNDAudioCallback AESND_RegisterAudioCallback(AESNDAudioCallback cb); + +AESNDPB* AESND_AllocateVoice(AESNDVoiceCallback cb); +void AESND_FreeVoice(AESNDPB *pb); +void AESND_SetVoiceStop(AESNDPB *pb,bool stop); +void AESND_SetVoiceMute(AESNDPB *pb,bool mute); +void AESND_SetVoiceLoop(AESNDPB *pb,bool loop); +void AESND_SetVoiceFormat(AESNDPB *pb,u32 format); +void AESND_SetVoiceStream(AESNDPB *pb,bool stream); +void AESND_SetVoiceFrequency(AESNDPB *pb,u32 freq); +void AESND_SetVoiceVolume(AESNDPB *pb,u16 volume_l,u16 volume_r); +void AESND_SetVoiceBuffer(AESNDPB *pb,const void *buffer,u32 len); +void AESND_PlayVoice(AESNDPB *pb,u32 format,const void *buffer,u32 len,u32 freq,u32 delay,bool looped); +AESNDVoiceCallback AESND_RegisterVoiceCallback(AESNDPB *pb,AESNDVoiceCallback cb); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/asndlib.h b/wii/libogc/include/asndlib.h new file mode 100644 index 0000000000..ed62daf57a --- /dev/null +++ b/wii/libogc/include/asndlib.h @@ -0,0 +1,383 @@ +/* ASNDLIB -> accelerated sound lib using the DSP + +Copyright (c) 2008 Hermes +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. +- The names of the contributors may not be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + + +#ifndef __ASNDLIB_H__ +#define __ASNDLIB_H__ + +#define __SNDLIB_H__ + +/*! + * \file asndlib.h + * \brief ASND library + * + */ + +#define ASND_LIB 0x100 +#define SND_LIB (ASND_LIB+2) + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*! \addtogroup sndretvals SND return values + * @{ + */ +#define SND_OK 0 +#define SND_INVALID -1 +#define SND_ISNOTASONGVOICE -2 +#define SND_BUSY 1 +/*! @} */ + +/*! \addtogroup sndiavretvals SND_IsActiveVoice additional return values + * @{ + */ +#define SND_UNUSED 0 /*!< This voice is available for use. */ +#define SND_WORKING 1 /*!< This voice is currently in progress. */ +#define SND_WAITING 2 /*!< This voice is currently in progress and waiting to one SND_AddVoice() function (the voice handler is called continuously) */ +/*! @} */ + +/*! \addtogroup sndsetvoiceformats Voice format + * @{ + */ +#define VOICE_MONO_8BIT 0 +#define VOICE_MONO_16BIT 1 +#define VOICE_MONO_16BIT_BE 1 +#define VOICE_STEREO_8BIT 2 +#define VOICE_STEREO_16BIT 3 +#define VOICE_STEREO_16BIT_BE 3 +#define VOICE_MONO_8BIT_U 4 +#define VOICE_MONO_16BIT_LE 5 +#define VOICE_STEREO_8BIT_U 6 +#define VOICE_STEREO_16BIT_LE 7 +/*! @} */ + +/*! \addtogroup voicevol Voice volume + * @{ + */ +#define MIN_VOLUME 0 +#define MID_VOLUME 127 +#define MAX_VOLUME 255 +/*! @} */ + +/*! \addtogroup pitchrange Pitch Range + * @{ + */ +#define MIN_PITCH 1 /*!< 1Hz */ +#define F44100HZ_PITCH 44100 /*!< 44100Hz */ +#define MAX_PITCH 144000 /*!< 144000Hz (more process time for pitch>48000) */ +#define INIT_RATE_48000 +/*! @} */ + +/*! \addtogroup notecode Note codification + * @{ + */ +enum +{ +NOTE_DO=0, +NOTE_DOs, +NOTE_REb=NOTE_DOs, +NOTE_RE, +NOTE_REs, +NOTE_MIb=NOTE_REs, +NOTE_MI, +NOTE_FA, +NOTE_FAs, +NOTE_SOLb=NOTE_FAs, +NOTE_SOL, +NOTE_SOLs, +NOTE_LAb=NOTE_SOLs, +NOTE_LA, +NOTE_LAs, +NOTE_SIb=NOTE_LAs, +NOTE_SI +}; + +enum +{ +NOTE_C=0, +NOTE_Cs, +NOTE_Db=NOTE_Cs, +NOTE_D, +NOTE_Ds, +NOTE_Eb=NOTE_Ds, +NOTE_E, +NOTE_F, +NOTE_Fs, +NOTE_Gb=NOTE_Fs, +NOTE_G, +NOTE_Gs, +NOTE_Ab=NOTE_Gs, +NOTE_A, +NOTE_As, +NOTE_Bb=NOTE_As, +NOTE_B +}; + +#define NOTE(note,octave) (note+(octave<<3)+(octave<<2)) /*!< Final note codification. Used in Note2Freq(). */ +/*! @} */ + +/*------------------------------------------------------------------------------------------------------------------------------------------------------*/ + +/*! \addtogroup sndlibcompat SNDLIB compatibility macros + * \details These defines are for compatability with SNDLIB functions. + * @{ + */ +#define Note2Freq ANote2Freq +#define SND_Init ASND_Init +#define SND_End ASND_End +#define SND_Pause ASND_Pause +#define SND_Is_Paused ASND_Is_Paused +#define SND_GetTime ASND_GetTime +#define SND_GetSampleCounter ASND_GetSampleCounter +#define SND_GetSamplesPerTick ASND_GetSamplesPerTick +#define SND_SetTime ASND_SetTime +#define SND_SetCallback ASND_SetCallback +#define SND_GetAudioRate ASND_GetAudioRate +#define SND_SetVoice ASND_SetVoice +#define SND_AddVoice ASND_AddVoice +#define SND_StopVoice ASND_StopVoice +#define SND_PauseVoice ASND_PauseVoice +#define SND_StatusVoice ASND_StatusVoice +#define SND_GetFirstUnusedVoice ASND_GetFirstUnusedVoice +#define SND_ChangePitchVoice ASND_ChangePitchVoice +#define SND_ChangeVolumeVoice ASND_ChangeVolumeVoice +#define SND_ChangeVolumeVoice ASND_ChangeVolumeVoice +#define SND_GetTickCounterVoice ASND_GetTickCounterVoice +#define SND_GetTimerVoice ASND_GetTimerVoice +#define SND_TestPointer ASND_TestPointer +/*! @} */ + +/*------------------------------------------------------------------------------------------------------------------------------------------------------*/ + +/** \brief Callback type for ASND_SetVoice(). */ +typedef void (*ASNDVoiceCallback)(s32 voice); + +/*------------------------------------------------------------------------------------------------------------------------------------------------------*/ + +/** \brief Initializes the SND lib and fixes the hardware sample rate. + * \param[in] note \ref notecode to play. for example: NOTE(C,4) for note C and octave 4. + * \param[in] freq_base Frequency base of the sample. For example 8000Hz. + * \param[in] note_base \ref notecode of the sample. For example: NOTE(L, 3) for note L and octave 3 (LA 3). + * \return Frequency, in Hz. */ +int ANote2Freq(int note, int freq_base,int note_base); + +/*------------------------------------------------------------------------------------------------------------------------------------------------------*/ + +/*! \addtogroup General sound functions + * @{ + */ + +/*! \brief Initializes the ASND lib and fixes the hardware sample rate to 48000. + * \return None. */ +void ASND_Init(); + +/*! \brief De-initializes the ASND lib. + * \return None. */ +void ASND_End(); + +/*! \brief Used to pause (or unpause) the sound. + * \note The sound starts paused when ASND_Init() is called. + * \param[in] paused If 1, sound is paused; sound can be unpaused with 0. + * \return None. */ +void ASND_Pause(s32 paused); + +/*! \brief Returns sound paused status. + * \return 1 if paused, 0 if unpaused. */ +s32 ASND_Is_Paused(); + +/*! \brief Returns the global time. + * \details The time is updated from the IRQ. + * \return The current time, in milliseconds. */ +u32 ASND_GetTime(); + +/*! \brief Retrieves the global sample counter. + * \details This counter is updated from the IRQ in steps of ASND_GetSamplesPerTick(). + * \note You can use this to implement one timer with high precision. + * \return Current sample. */ +u32 ASND_GetSampleCounter(); + +/*! \brief Retrieves the samples sent from the IRQ in one tick. + * \return Samples per tick. */ +u32 ASND_GetSamplesPerTick(); + +/*! \brief Set the global time. + * \details This time is updated from the IRQ. + * \param[in] time Fix the current time, in milliseconds. + * \return None. */ +void ASND_SetTime(u32 time); + +/*! \brief Sets a global callback for general purposes. + * \details This callback is called from the IRQ. + * \param[in] callback Callback function to assign. + * \return None. */ +void ASND_SetCallback(void (*callback)()); + +/*! \brief Returns the current audio rate. + * \note This function is implemented for compatibility with SNDLIB. + * \return Audio rate (48000). */ +s32 ASND_GetAudioRate(); + +/*! @} */ + +/*! \addtogroup voicefuncs Voice functions + * @{ + */ + +// NOTE: I'm keeping this description around because I couldn't fully understand it; if someone else knows exactly what it's doing, they can come around +// and make sure the description is correct. +/* callback: can be NULL or one callback function is used to implement a double buffer use. When the second buffer is empty, the callback is called sending + the voice number as parameter. You can use "void callback(s32 voice)" function to call ASND_AddVoice() and add one voice to the second buffer. + NOTE: When callback is fixed the voice never stops and it turn in SND_WAITING status if success one timeout condition. +*/ + +/*! \brief Sets a PCM voice to play. + * \details This function stops one previous voice. Use ASND_StatusVoice() to test the status condition. + * \note The voices are played in 16-bit stereo, regardless of the source format.

+ * + * \note \a callback is used to implement a double buffer. When the second buffer is empty, the callback function is called with the voice number + * as an argument. You can use void callback (s32 voice) to call ASND_AddVoice() and add one voice to the second buffer. When the callback + * is non-NULL the, the voice never stops and returns SND_WAITING if successful on timeout condition. + * \param[in] voice Voice slot to use for this sound; valid values are 0 to (MAX_SND_VOICES-1). + * \param[in] format \ref sndsetvoiceformats to use for this sound. + * \param[in] pitch Frequency to use, in Hz. + * \param[in] delay Delay to wait before playing this voice; value is in milliseconds. + * \param[in] snd Buffer containing samples to play back; the buffer must be aligned and padded to 32 bytes! + * \param[in] size_snd Size of the buffer samples, in bytes. + * \param[in] volume_l \ref voicevol of the left channel; value can be 0 - 255 inclusive. + * \param[in] volume_r \ref voicevol of the right channel; value can be 0 - 255 inclusive. + * \param[in] callback Optional callback function to use; set to NULL for no callback. See the note above for details. + * \return SND_OK or SND_INVALID. */ +s32 ASND_SetVoice(s32 voice, s32 format, s32 pitch,s32 delay, void *snd, s32 size_snd, s32 volume_l, s32 volume_r, ASNDVoiceCallback callback); + +/*! \brief Sets a PCM voice to play infinitely. + * \note See ASND_SetVoice() for a detailed description, as it is largely identical. */ +s32 ASND_SetInfiniteVoice(s32 voice, s32 format, s32 pitch,s32 delay, void *snd, s32 size_snd, s32 volume_l, s32 volume_r); + +/*! \brief Adds a PCM voice to play from the second buffer. + * \note This function requires a call to ASND_SetVoice() and its subsequent return value to be a status other than SND_UNUSED prior to calling this one. + * \param[in] voice Voice slot to attach this buffer to; value must be 0 to (MAX_SND_VOICES-1). + * \param[in] snd Buffer containing the samples; it must be aligned and padded to 32 bytes AND have the same sample format as the first buffer. + * \param[in] size_snd Size of the buffer samples, in bytes. + * \return SND_OK or SND_INVALID; it may also return SND_BUSY if the second buffer is not empty and the voice cannot be added. */ +s32 ASND_AddVoice(s32 voice, void *snd, s32 size_snd); + +/*! \brief Stops the selected voice. + * \details If the voice is used in song mode, you need to assign the samples with ASND_SetSongSampleVoice() again. In this case, use ANS_PauseSongVoice() + * to stop the voice without loss of samples. + * \param[in] voice Voice to stop, from 0 to (MAX_SND_VOICES-1). + * \return SND_OK or SND_INVALID. */ +s32 ASND_StopVoice(s32 voice); + +/*! \brief Pauses the selected voice. + * \param[in] voice Voice to pause, from 0 to (MAX_SND_VOICES-1). + * \return SND_OK or SND_INVALID. */ +s32 ASND_PauseVoice(s32 voice, s32 pause); + +/*! \brief Returns the status of the selected voice. + * \param[in] voice Voice slot to get the status from, from 0 to (MAX_SND_VOICES-1). + * \return SND_INVALID if invalid, else a value from \ref sndiavretvals. */ +s32 ASND_StatusVoice(s32 voice); + +/*! \brief Returns the first unused voice. + * \note Voice 0 is the last possible voice that can be returned. The idea is that this voice is reserved for a MOD/OGG/MP3/etc. player. With this in mind, + * you can use this function to determine that the rest of the voices are working if the return value is < 1. + * \return SND_INVALID or the first free voice from 0 to (MAX_SND_VOICES-1). */ +s32 ASND_GetFirstUnusedVoice(); + +/*! \brief Changes the voice pitch in real-time. + * \details This function can be used to create audio effects such as Doppler effect emulation. + * \param[in] voice Voice to change the pitch of, from 0 to (MAX_SND_VOICES-1). + * \return SND_OK or SND_INVALID. */ +s32 ASND_ChangePitchVoice(s32 voice, s32 pitch); + +/*! \brief Changes the voice volume in real-time. + * \details This function can be used to create audio effects like distance attenuation. + * \param[in] voice Voice to change the volume of, from 0 to (MAX_SND_VOICES-1). + * \param[in] volume_l \ref voicevol to set the left channel to, from 0 to 255. + * \param[in] volume_r \ref voicevol to set the right channel to, from 0 to 255. + * \return SND_OK or SND_INVALID. */ +s32 ASND_ChangeVolumeVoice(s32 voice, s32 volume_l, s32 volume_r); + +/*! \brief Returns the voice tick counter. + * \details This value represents the number of ticks since this voice started to play, sans delay time. It uses the same resolution as the internal + * sound buffer. For example, if the lib is initialized with INIT_RATE_48000, a return value of 24000 is equal to 0.5 seconds. This value can be used, for + * example, to synchronize audio and video. + * \note This function does not return error codes. + * \param[in] voice Voice to retrieve the counter value from, from 0 to (MAX_SND_VOICES-1). + * \return Number of ticks since this voice started playing. */ +u32 ASND_GetTickCounterVoice(s32 voice); + +/*! \brief Returns the voice playback time. + * \details This value represents the time, in milliseconds, since this voice started playing, sans delay time. This value can be used, for example, to + * synchronize audio and video. + * \note This function does not return error codes. + * \param[in] voice Voice to retrieve the time value from, from 0 to (MAX_SND_VOICES-1). + * \return Amount of time since this voice has started playing. */ +u32 ASND_GetTimerVoice(s32 voice); + +/*! \brief Tests if \a pointer is in use by \a voice as a buffer. + * \param[in] voice Voice to test, from 0 to (MAX_SND_VOICES-1). + * \param[in] pointer Address to test. This must be the same pointer sent to ASND_AddVoice() or ASND_SetVoice(). + * \return SND_INVALID if invalid + * \return 0 if the pointer is unused + * \return 1 if the pointer used as a buffer. */ +s32 ASND_TestPointer(s32 voice, void *pointer); + +/*! \brief Tests to determine if the \a voice is ready to receive a new buffer sample with ASND_AddVoice(). + * \details You can use this function to block a reader when double buffering is used. It works similarly to ASND_TestPointer() without needing to pass a + * pointer. + * \param[in] voice Voice to test, from 0 to (MAX_SND_VOICES-1). + * \return SND_INVALID + * \return 0 if voice is NOT ready to receive a new voice. + * \return 1 if voice is ready to receive a new voice with ASND_AddVoice(). */ +s32 ASND_TestVoiceBufferReady(s32 voice); + +/*! @} */ + +/*! \addtogroup dspfuncs DSP functions + * @{ + */ + +/*! \brief Returns the DSP usage. + * \details The value is a percentage of DSP usage. + * \return DSP usage, in percent. */ +u32 ASND_GetDSP_PercentUse(); + +u32 ASND_GetDSP_ProcessTime(); + +/*! @} */ + +#ifdef __cplusplus + } +#endif + +#endif + diff --git a/wii/libogc/include/bte/bd_addr.h b/wii/libogc/include/bte/bd_addr.h new file mode 100644 index 0000000000..87faf92675 --- /dev/null +++ b/wii/libogc/include/bte/bd_addr.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + +#ifndef __BD_ADDR_H__ +#define __BD_ADDR_H__ + +#include + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +struct bd_addr { + u8 addr[6]; +}; + +#define BD_ADDR_LEN 6 + +#define BD_ADDR_ANY (&(struct bd_addr){{0,0,0,0,0,0}}) +#define BD_ADDR_LOCAL (&(struct bd_addr){{0,0,0,0xff,0xff,0xff}}) + +#define BD_ADDR(bdaddr, a, b, c, d, e, f) do{ \ + (bdaddr)->addr[0] = a; \ + (bdaddr)->addr[1] = b; \ + (bdaddr)->addr[2] = c; \ + (bdaddr)->addr[3] = d; \ + (bdaddr)->addr[4] = e; \ + (bdaddr)->addr[5] = f; }while(0) +//TODO: USE memcmp???? +#define bd_addr_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ + ((addr1)->addr[1] == (addr2)->addr[1]) && \ + ((addr1)->addr[2] == (addr2)->addr[2]) && \ + ((addr1)->addr[3] == (addr2)->addr[3]) && \ + ((addr1)->addr[4] == (addr2)->addr[4]) && \ + ((addr1)->addr[5] == (addr2)->addr[5])) +//TODO: USE memcpy???? +#define bd_addr_set(addr1, addr2) do { \ + (addr1)->addr[0] = (addr2)->addr[0]; \ + (addr1)->addr[1] = (addr2)->addr[1]; \ + (addr1)->addr[2] = (addr2)->addr[2]; \ + (addr1)->addr[3] = (addr2)->addr[3]; \ + (addr1)->addr[4] = (addr2)->addr[4]; \ + (addr1)->addr[5] = (addr2)->addr[5]; }while(0) + + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif /* __BD_ADDR_H__ */ diff --git a/wii/libogc/include/bte/bte.h b/wii/libogc/include/bte/bte.h new file mode 100644 index 0000000000..e05e9cdddb --- /dev/null +++ b/wii/libogc/include/bte/bte.h @@ -0,0 +1,154 @@ +#ifndef __BTE_H__ +#define __BTE_H__ + +#include +#include "bd_addr.h" + +#define ERR_OK 0 +#define ERR_MEM -1 +#define ERR_BUF -2 +#define ERR_ABRT -3 +#define ERR_RST -4 +#define ERR_CLSD -5 +#define ERR_CONN -6 +#define ERR_VAL -7 +#define ERR_ARG -8 +#define ERR_RTE -9 +#define ERR_USE -10 +#define ERR_IF -11 +#define ERR_PKTSIZE -17 + +#define HIDP_STATE_READY 0x00 +#define HIDP_STATE_LISTEN 0x01 +#define HIDP_STATE_CONNECTING 0x02 +#define HIDP_STATE_CONNECTED 0x04 + +#define HIDP_CONTROL_CHANNEL 0x11 +#define HIDP_DATA_CHANNEL 0x13 + +#define HIDP_HDR_TRANS_MASK 0xf0 +#define HIDP_HDR_PARAM_MASK 0x0f + +#define HIDP_TRANS_HANDSHAKE 0x00 +#define HIDP_TRANS_HIDCONTROL 0x10 +#define HIDP_TRANS_GETREPORT 0x40 +#define HIDP_TRANS_SETREPORT 0x50 +#define HIDP_TRANS_GETPROTOCOL 0x60 +#define HIDP_TRANS_SETPROTOCOL 0x70 +#define HIDP_TRANS_GETIDLE 0x80 +#define HIDP_TRANS_SETIDLE 0x90 +#define HIDP_TRANS_DATA 0xa0 +#define HIDP_TRANS_DATAC 0xb0 + +#define HIDP_HSHK_SUCCESSFULL 0x00 +#define HIDP_HSHK_NOTREADY 0x01 +#define HIDP_HSHK_INV_REPORTID 0x02 +#define HIDP_HSHK_NOTSUPPORTED 0x03 +#define HIDP_HSHK_IVALIDPARAM 0x04 +#define HIDP_HSHK_UNKNOWNERROR 0x0e +#define HIDP_HSHK_FATALERROR 0x0f + +#define HIDP_CTRL_NOP 0x00 +#define HIDP_CTRL_HARDRESET 0x01 +#define HIDP_CTRL_SOFTRESET 0x02 +#define HIDP_CTRL_SUSPEND 0x03 +#define HIDP_CTRL_RESUME 0x04 +#define HIDP_CTRL_VC_UNPLUG 0x05 + +/* HIDP data transaction headers */ +#define HIDP_DATA_RTYPE_MASK 0x03 +#define HIDP_DATA_RSRVD_MASK 0x0c +#define HIDP_DATA_RTYPE_OTHER 0x00 +#define HIDP_DATA_RTYPE_INPUT 0x01 +#define HIDP_DATA_RTYPE_OUPUT 0x02 +#define HIDP_DATA_RTYPE_FEATURE 0x03 + +#define HIDP_PROTO_BOOT 0x00 +#define HIDP_PROTO_REPORT 0x01 + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +struct l2cap_pcb; +struct ctrl_req_t; + +struct inquiry_info +{ + struct bd_addr bdaddr; + u8 cod[3]; +}; + +struct inquiry_info_ex +{ + struct bd_addr bdaddr; + u8 cod[3]; + u8 psrm; + u8 psm; + u16 co; +}; + +struct linkkey_info +{ + struct bd_addr bdaddr; + u8 key[16]; +}; + +struct bte_pcb +{ + u8 err; + u32 state; + void *cbarg; + + struct ctrl_req_t *ctrl_req_head; + struct ctrl_req_t *ctrl_req_tail; + + lwpq_t cmdq; + + struct bd_addr bdaddr; + + struct l2cap_pcb *ctl_pcb; + struct l2cap_pcb *data_pcb; + + + s32 (*recv)(void *arg,void *buffer,u16 len); + s32 (*conn_cfm)(void *arg,struct bte_pcb *pcb,u8 err); + s32 (*disconn_cfm)(void *arg,struct bte_pcb *pcb,u8 err); +}; + +typedef s32 (*btecallback)(s32 result,void *userdata); + +void BTE_Init(); +void BTE_Shutdown(); +s32 BTE_InitCore(btecallback cb); +s32 BTE_ApplyPatch(btecallback cb); +s32 BTE_InitSub(btecallback cb); +s32 BTE_ReadStoredLinkKey(struct linkkey_info *keys,u8 max_cnt,btecallback cb); +s32 BTE_ReadBdAddr(struct bd_addr *bdaddr, btecallback cb); +void (*BTE_SetDisconnectCallback(void (*callback)(struct bd_addr *bdaddr,u8 reason)))(struct bd_addr *bdaddr,u8 reason); + +struct bte_pcb* bte_new(); +void bte_arg(struct bte_pcb *pcb,void *arg); +void bte_received(struct bte_pcb *pcb, s32 (*recv)(void *arg,void *buffer,u16 len)); +void bte_disconnected(struct bte_pcb *pcb,s32 (disconn_cfm)(void *arg,struct bte_pcb *pcb,u8 err)); + +s32 bte_registerdeviceasync(struct bte_pcb *pcb,struct bd_addr *bdaddr,s32 (*conn_cfm)(void *arg,struct bte_pcb *pcb,u8 err)); + +s32 bte_disconnect(struct bte_pcb *pcb); + +//s32 bte_listen(struct bte_pcb *pcb,struct bd_addr *bdaddr,u8 psm); +//s32 bte_accept(struct bte_pcb *pcb,s32 (*recv)(void *arg,void *buffer,u16 len)); +s32 bte_inquiry(struct inquiry_info *info,u8 max_cnt,u8 flush); +s32 bte_inquiry_ex(struct inquiry_info_ex *info,u8 max_cnt,u8 flush); +//s32 bte_connect(struct bte_pcb *pcb,struct bd_addr *bdaddr,u8 psm,s32 (*recv)(void *arg,void *buffer,u16 len)); +//s32 bte_connect_ex(struct bte_pcb *pcb,struct inquiry_info_ex *info,u8 psm,s32 (*recv)(void *arg,void *buffer,u16 len)); +s32 bte_senddata(struct bte_pcb *pcb,void *message,u16 len); +s32 bte_sendmessage(struct bte_pcb *pcb,void *message,u16 len); +s32 bte_sendmessageasync(struct bte_pcb *pcb,void *message,u16 len,s32 (*sent)(void *arg,struct bte_pcb *pcb,u8 err)); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif + diff --git a/wii/libogc/include/debug.h b/wii/libogc/include/debug.h new file mode 100644 index 0000000000..f87f726e38 --- /dev/null +++ b/wii/libogc/include/debug.h @@ -0,0 +1,43 @@ +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +#include + +#define GDBSTUB_DEVICE_USB 0 /*!< device type: USBGecko */ +#define GDBSTUB_DEVICE_TCP 1 /*!< device type: BBA-TCP */ + +#define GDBSTUB_DEF_CHANNEL 0 /*!< default EXI channel. channel can be 0 or 1. Note: Used for device type USBGecko */ +#define GDBSTUB_DEF_TCPPORT 2828 /*!< default TCP port. Note: Used for device type TCP */ + +#ifdef __cplusplus + extern "C" { +#endif + +extern const char *tcp_localip; +extern const char *tcp_netmask; +extern const char *tcp_gateway; + + +/*!\fn void _break() + * \brief Stub function to insert the hardware break instruction. This function is used to enter the debug stub and to + * connect with the host. The developer is free to insert this function at any position in project's source code. + * + * \return none. + */ +void _break(); + + +/*!\fn void DEBUG_Init(s32 device_type,s32 channel_port) + * \brief Performs the initialization of the debug stub. + * \param[in] device_type type of device to use. can be either USB or TCP. + * \param[in] channel_port depending on the used device this can be either the EXI channel or the TCP port. + * + * \return none. + */ +void DEBUG_Init(s32 device_type,s32 channel_port); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/fat.h b/wii/libogc/include/fat.h new file mode 100644 index 0000000000..7d828f6e72 --- /dev/null +++ b/wii/libogc/include/fat.h @@ -0,0 +1,122 @@ +/* + fat.h + Simple functionality for startup, mounting and unmounting of FAT-based devices. + + Copyright (c) 2006 - 2012 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef _LIBFAT_H +#define _LIBFAT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "libfatversion.h" + +// When compiling for NDS, make sure NDS is defined +#ifndef NDS + #if defined ARM9 || defined ARM7 + #define NDS + #endif +#endif + +#include + +#if defined(__gamecube__) || defined (__wii__) +# include +#else +# ifdef NDS +# include +# else +# include +# endif +#endif + +/* +Initialise any inserted block-devices. +Add the fat device driver to the devoptab, making it available for standard file functions. +cacheSize: The number of pages to allocate for each inserted block-device +setAsDefaultDevice: if true, make this the default device driver for file operations +*/ +extern bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice); + +/* +Calls fatInit with setAsDefaultDevice = true and cacheSize optimised for the host system. +*/ +extern bool fatInitDefault (void); + +/* +Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". +You can then access the filesystem using "name:/". +This will mount the active partition or the first valid partition on the disc, +and will use a cache size optimized for the host system. +*/ +extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface); + +/* +Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". +You can then access the filesystem using "name:/". +If startSector = 0, it will mount the active partition of the first valid partition on +the disc. Otherwise it will try to mount the partition starting at startSector. +cacheSize specifies the number of pages to allocate for the cache. +This will not startup the disc, so you need to call interface->startup(); first. +*/ +extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage); + +/* +Unmount the partition specified by name. +If there are open files, it will attempt to synchronise them to disc. +*/ +extern void fatUnmount (const char* name); + +/* +Get Volume Label +*/ +extern void fatGetVolumeLabel (const char* name, char *label); + +// File attributes +#define ATTR_ARCHIVE 0x20 // Archive +#define ATTR_DIRECTORY 0x10 // Directory +#define ATTR_VOLUME 0x08 // Volume +#define ATTR_SYSTEM 0x04 // System +#define ATTR_HIDDEN 0x02 // Hidden +#define ATTR_READONLY 0x01 // Read only + +/* +Methods to modify DOS File Attributes +*/ +int FAT_getAttr(const char *file); +int FAT_setAttr(const char *file, uint8_t attr ); + +#define LIBFAT_FEOS_MULTICWD + +#ifdef __cplusplus +} +#endif + +#endif // _LIBFAT_H diff --git a/wii/libogc/include/gccore.h b/wii/libogc/include/gccore.h new file mode 100644 index 0000000000..f45b68345a --- /dev/null +++ b/wii/libogc/include/gccore.h @@ -0,0 +1,150 @@ +/*------------------------------------------------------------- + +gccore.h -- GC core header + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#ifndef __GCCORE_H__ +#define __GCCORE_H__ + +/*! \file gccore.h +\brief Core header which includes all subsequent subsystem headers + +*/ + +#include "ogc/dsp.h" +#include "ogc/aram.h" +#include "ogc/arqueue.h" +#include "ogc/arqmgr.h" +#include "ogc/audio.h" +#include "ogc/cache.h" +#include "ogc/card.h" +#include "ogc/cast.h" +#include "ogc/color.h" +#include "ogc/consol.h" +#include "ogc/dvd.h" +#include "ogc/exi.h" +#include "ogc/gu.h" +#include "ogc/gx.h" +#include "ogc/si.h" +#include "ogc/gx_struct.h" +#include "ogc/irq.h" +#include "ogc/lwp.h" +#include "ogc/mutex.h" +#include "ogc/message.h" +#include "ogc/semaphore.h" +#include "ogc/pad.h" +#include "ogc/tpl.h" +#include "ogc/system.h" +#include "ogc/video.h" +#include "ogc/usbgecko.h" +#include "ogc/video_types.h" +#include "ogc/texconv.h" + +#if defined(HW_RVL) +#include "ogc/ipc.h" +#include "ogc/es.h" +#include "ogc/stm.h" +#include "ogc/ios.h" +#include "ogc/usb.h" +#include "ogc/isfs.h" +#include "ogc/conf.h" +#include "ogc/usbstorage.h" + +#include "ogc/wiilaunch.h" + +#endif + +/* + * Error returns + */ +#define RNC_FILE_IS_NOT_RNC -1 +#define RNC_HUF_DECODE_ERROR -2 +#define RNC_FILE_SIZE_MISMATCH -3 +#define RNC_PACKED_CRC_ERROR -4 +#define RNC_UNPACKED_CRC_ERROR -5 + +#ifndef ATTRIBUTE_ALIGN +# define ATTRIBUTE_ALIGN(v) __attribute__((aligned(v))) +#endif +#ifndef ATTRIBUTE_PACKED +# define ATTRIBUTE_PACKED __attribute__((packed)) +#endif + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + +/*! + * \mainpage + * + * - \subpage intro + * - \subpage api_doc + */ + + +/*! + * \page intro Introduction + * Welcome to the libOGC reference documentation. + */ + +/*! + * \page api_doc Detailed API description + * + * - \ref aram.h "ARAM subsystem" + * - \ref arqmgr.h "ARAM queue management subsystem" + * - \ref audio.h "AUDIO subsystem" + * - \ref asndlib.h "ASND library" + * - \ref exi.h "EXI subsystem" + * - \ref irq.h "IRQ subsystem" + * - \ref dsp.h "DSP subsystem" + * - \ref dvd.h "DVD subsystem" + * - \ref gx.h "GX subsystem" + * - \ref gu.h "gu/Matrix subsystem" + * - \ref video.h "VIDEO subsystem" + * - \ref cache.h "Cache subsystem" + * - \ref card.h "Memory card subsystem" + * - \ref consol.h "Console subsystem" + * - \ref system.h "OS functions and initialization" + * - \ref lwp.h "Thread subsystem I" + * - \ref message.h "Thread subsystem II" + * - \ref mutex.h "Thread subsystem III" + * - \ref semaphore.h "Thread subsystem IV" + * - \ref cond.h "Thread subsystem V" + */ + +s32 depackrnc1_ulen(void *packed); +s32 depackrnc1(void *packed,void *unpacked); + +void depackrnc2(void *packed,void *unpacked); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/gcmodplay.h b/wii/libogc/include/gcmodplay.h new file mode 100644 index 0000000000..a59d077533 --- /dev/null +++ b/wii/libogc/include/gcmodplay.h @@ -0,0 +1,44 @@ +#ifndef __GCMODPLAY_H__ +#define __GCMODPLAY_H__ + +#include +#include "modplay/modplay.h" + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef struct _modsndbuf { + u32 freq; + u16 fmt; + u32 chans; + f32 samples; + void *usr_data; + void (*callback)(void *,u8 *,u32); +} MODSNDBUF; + +typedef struct _modplay { + MOD mod; + BOOL playing,paused; + BOOL bits,stereo,manual_polling; + u32 playfreq,numSFXChans; + MODSNDBUF soundBuf; +} MODPlay; + +void MODPlay_Init(MODPlay *mod); +s32 MODPlay_SetFrequency(MODPlay *mod,u32 freq); +void MODPlay_SetStereo(MODPlay *mod,BOOL stereo); +s32 MODPlay_SetMOD(MODPlay *mod,const void *mem); +void MODPlay_Unload(MODPlay *mod); +s32 MODPlay_AllocSFXChannels(MODPlay *mod,u32 sfxchans); +s32 MODPlay_Start(MODPlay *mod); +s32 MODPlay_Stop(MODPlay *mod); +s32 MODPlay_TriggerNote(MODPlay *mod,u32 chan,u8 inst,u16 freq,u8 vol); +s32 MODPlay_Pause(MODPlay *mod,BOOL); +void MODPlay_SetVolume(MODPlay * mod, s32 musicvolume, s32 sfxvolume); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/gctypes.h b/wii/libogc/include/gctypes.h new file mode 100644 index 0000000000..8d93799e4b --- /dev/null +++ b/wii/libogc/include/gctypes.h @@ -0,0 +1,110 @@ +#ifndef __GCTYPES_H__ +#define __GCTYPES_H__ + +/*! \file gctypes.h +\brief Data type definitions + +*/ + +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/*+----------------------------------------------------------------------------------------------+*/ +typedef uint8_t u8; ///< 8bit unsigned integer +typedef uint16_t u16; ///< 16bit unsigned integer +typedef uint32_t u32; ///< 32bit unsigned integer +typedef uint64_t u64; ///< 64bit unsigned integer +/*+----------------------------------------------------------------------------------------------+*/ +typedef int8_t s8; ///< 8bit signed integer +typedef int16_t s16; ///< 16bit signed integer +typedef int32_t s32; ///< 32bit signed integer +typedef int64_t s64; ///< 64bit signed integer +/*+----------------------------------------------------------------------------------------------+*/ +typedef volatile u8 vu8; ///< 8bit unsigned volatile integer +typedef volatile u16 vu16; ///< 16bit unsigned volatile integer +typedef volatile u32 vu32; ///< 32bit unsigned volatile integer +typedef volatile u64 vu64; ///< 64bit unsigned volatile integer +/*+----------------------------------------------------------------------------------------------+*/ +typedef volatile s8 vs8; ///< 8bit signed volatile integer +typedef volatile s16 vs16; ///< 16bit signed volatile integer +typedef volatile s32 vs32; ///< 32bit signed volatile integer +typedef volatile s64 vs64; ///< 64bit signed volatile integer +/*+----------------------------------------------------------------------------------------------+*/ +// fixed point math typedefs +typedef s16 sfp16; ///< signed 8:8 fixed point +typedef s32 sfp32; ///< signed 20:8 fixed point +typedef u16 ufp16; ///< unsigned 8:8 fixed point +typedef u32 ufp32; ///< unsigned 24:8 fixed point +/*+----------------------------------------------------------------------------------------------+*/ +typedef float f32; +typedef double f64; +/*+----------------------------------------------------------------------------------------------+*/ +typedef volatile float vf32; +typedef volatile double vf64; +/*+----------------------------------------------------------------------------------------------+*/ + + +typedef unsigned int BOOL; +/*+----------------------------------------------------------------------------------------------+*/ +// alias type typedefs +#define FIXED s32 ///< Alias type for sfp32 +/*+----------------------------------------------------------------------------------------------+*/ +#ifndef TRUE +#define TRUE 1 ///< True +#endif +/*+----------------------------------------------------------------------------------------------+*/ +#ifndef FALSE +#define FALSE 0 ///< False +#endif +/*+----------------------------------------------------------------------------------------------+*/ +#ifndef NULL +#define NULL 0 ///< Pointer to 0 +#endif +/*+----------------------------------------------------------------------------------------------+*/ +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 3412 +#endif /* LITTLE_ENDIAN */ +/*+----------------------------------------------------------------------------------------------+*/ +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 1234 +#endif /* BIGE_ENDIAN */ +/*+----------------------------------------------------------------------------------------------+*/ +#ifndef BYTE_ORDER +#define BYTE_ORDER BIG_ENDIAN +#endif /* BYTE_ORDER */ +/*+----------------------------------------------------------------------------------------------+*/ + + +//! argv structure +/*! \struct __argv + + structure used to set up argc/argv + +*/ +struct __argv { + int argvMagic; //!< argv magic number, set to 0x5f617267 ('_arg') if valid + char *commandLine; //!< base address of command line, set of null terminated strings + int length;//!< total length of command line + int argc; + char **argv; + char **endARGV; +}; + +//! Default location for the system argv structure. +extern struct __argv *__system_argv; + +// argv struct magic number +#define ARGV_MAGIC 0x5f617267 + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif /* TYPES_H */ + + +/* END OF FILE */ diff --git a/wii/libogc/include/gcutil.h b/wii/libogc/include/gcutil.h new file mode 100644 index 0000000000..3458ee037d --- /dev/null +++ b/wii/libogc/include/gcutil.h @@ -0,0 +1,12 @@ +#ifndef __GCUTIL_H__ +#define __GCUTIL_H__ + +#ifndef ATTRIBUTE_ALIGN +# define ATTRIBUTE_ALIGN(v) __attribute__((aligned(v))) +#endif +#ifndef ATTRIBUTE_PACKED +# define ATTRIBUTE_PACKED __attribute__((packed)) +#endif + +#endif /* _GCUTIL_H */ + diff --git a/wii/libogc/include/iso9660.h b/wii/libogc/include/iso9660.h new file mode 100644 index 0000000000..498cf9aeda --- /dev/null +++ b/wii/libogc/include/iso9660.h @@ -0,0 +1,27 @@ +/**************************************************************************** + * ISO9660 devoptab + * + * Copyright (C) 2008-2010 + * tipoloski, clava, shagkur, Tantric, joedj + ****************************************************************************/ + +#ifndef __ISO9660_H__ +#define __ISO9660_H__ + +#include + +#define ISO_MAXPATHLEN 128 + +#ifdef __cplusplus +extern "C" { +#endif + +bool ISO9660_Mount(const char* name, const DISC_INTERFACE* disc_interface); +bool ISO9660_Unmount(const char* name); +const char *ISO9660_GetVolumeLabel(const char *name); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wii/libogc/include/libfatversion.h b/wii/libogc/include/libfatversion.h new file mode 100644 index 0000000000..765ac2f3f7 --- /dev/null +++ b/wii/libogc/include/libfatversion.h @@ -0,0 +1,10 @@ +#ifndef __LIBFATVERSION_H__ +#define __LIBFATVERSION_H__ + +#define _LIBFAT_MAJOR_ 1 +#define _LIBFAT_MINOR_ 1 +#define _LIBFAT_PATCH_ 1 + +#define _LIBFAT_STRING "libFAT Release 1.1.1" + +#endif // __LIBFATVERSION_H__ diff --git a/wii/libogc/include/network.h b/wii/libogc/include/network.h new file mode 100644 index 0000000000..91a526d1f1 --- /dev/null +++ b/wii/libogc/include/network.h @@ -0,0 +1,287 @@ +#ifndef __NETWORK_H__ +#define __NETWORK_H__ + +#include +#include +#include + +#define INVALID_SOCKET (~0) +#define SOCKET_ERROR (-1) + +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 +#define SOCK_RAW 3 + +/* + * Option flags per-socket. + */ +#define SO_DEBUG 0x0001 /* turn on debugging info recording */ +#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */ +#define SO_REUSEADDR 0x0004 /* allow local address reuse */ +#define SO_KEEPALIVE 0x0008 /* keep connections alive */ +#define SO_DONTROUTE 0x0010 /* just use interface addresses */ +#define SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */ +#define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */ +#define SO_LINGER 0x0080 /* linger on close if data present */ +#define SO_OOBINLINE 0x0100 /* leave received OOB data in line */ +#define SO_REUSEPORT 0x0200 /* allow local address & port reuse */ + +#define SO_DONTLINGER (int)(~SO_LINGER) + +/* + * Additional options, not kept in so_options. + */ +#define SO_SNDBUF 0x1001 /* send buffer size */ +#define SO_RCVBUF 0x1002 /* receive buffer size */ +#define SO_SNDLOWAT 0x1003 /* send low-water mark */ +#define SO_RCVLOWAT 0x1004 /* receive low-water mark */ +#define SO_SNDTIMEO 0x1005 /* send timeout */ +#define SO_RCVTIMEO 0x1006 /* receive timeout */ +#define SO_ERROR 0x1007 /* get error status and clear */ +#define SO_TYPE 0x1008 /* get socket type */ + + + +/* + * Structure used for manipulating linger option. + */ +struct linger { + int l_onoff; /* option on/off */ + int l_linger; /* linger time */ +}; + +/* + * Level number for (get/set)sockopt() to apply to socket itself. + */ +#define SOL_SOCKET 0xffff /* options for socket level */ + +#define AF_UNSPEC 0 +#define AF_INET 2 +#define PF_INET AF_INET +#define PF_UNSPEC AF_UNSPEC + +#define IPPROTO_IP 0 +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 + +#define INADDR_ANY 0 +#define INADDR_BROADCAST 0xffffffff + +/* Flags we can use with send and recv. */ +#define MSG_DONTWAIT 0x40 /* Nonblocking i/o for this operation only */ + +/* + * Options for level IPPROTO_IP + */ +#define IP_TOS 1 +#define IP_TTL 2 + + +#define IPTOS_TOS_MASK 0x1E +#define IPTOS_TOS(tos) ((tos) & IPTOS_TOS_MASK) +#define IPTOS_LOWDELAY 0x10 +#define IPTOS_THROUGHPUT 0x08 +#define IPTOS_RELIABILITY 0x04 +#define IPTOS_LOWCOST 0x02 +#define IPTOS_MINCOST IPTOS_LOWCOST + +/* + * Definitions for IP precedence (also in ip_tos) (hopefully unused) + */ +#define IPTOS_PREC_MASK 0xe0 +#define IPTOS_PREC(tos) ((tos) & IPTOS_PREC_MASK) +#define IPTOS_PREC_NETCONTROL 0xe0 +#define IPTOS_PREC_INTERNETCONTROL 0xc0 +#define IPTOS_PREC_CRITIC_ECP 0xa0 +#define IPTOS_PREC_FLASHOVERRIDE 0x80 +#define IPTOS_PREC_FLASH 0x60 +#define IPTOS_PREC_IMMEDIATE 0x40 +#define IPTOS_PREC_PRIORITY 0x20 +#define IPTOS_PREC_ROUTINE 0x00 + + +/* + * Commands for ioctlsocket(), taken from the BSD file fcntl.h. + * + * + * Ioctl's have the command encoded in the lower word, + * and the size of any in or out parameters in the upper + * word. The high 2 bits of the upper word are used + * to encode the in/out status of the parameter; for now + * we restrict parameters to at most 128 bytes. + */ +#if !defined(FIONREAD) || !defined(FIONBIO) +#define IOCPARM_MASK 0x7f /* parameters must be < 128 bytes */ +#define IOC_VOID 0x20000000 /* no parameters */ +#define IOC_OUT 0x40000000 /* copy out parameters */ +#define IOC_IN 0x80000000 /* copy in parameters */ +#define IOC_INOUT (IOC_IN|IOC_OUT) + /* 0x20000000 distinguishes new & + old ioctl's */ +#define _IO(x,y) (IOC_VOID|((x)<<8)|(y)) + +#define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) + +#define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)) +#endif + +#ifndef FIONREAD +#define FIONREAD _IOR('f', 127, unsigned long) /* get # bytes to read */ +#endif +#ifndef FIONBIO +#define FIONBIO _IOW('f', 126, unsigned long) /* set/clear non-blocking i/o */ +#endif + +/* Socket I/O Controls */ +#ifndef SIOCSHIWAT +#define SIOCSHIWAT _IOW('s', 0, unsigned long) /* set high watermark */ +#define SIOCGHIWAT _IOR('s', 1, unsigned long) /* get high watermark */ +#define SIOCSLOWAT _IOW('s', 2, unsigned long) /* set low watermark */ +#define SIOCGLOWAT _IOR('s', 3, unsigned long) /* get low watermark */ +#define SIOCATMARK _IOR('s', 7, unsigned long) /* at oob mark? */ +#endif + +#ifndef O_NONBLOCK +#define O_NONBLOCK 04000U +#endif + +#ifndef FD_SET + #undef FD_SETSIZE + #define FD_SETSIZE 16 + #define FD_SET(n, p) ((p)->fd_bits[(n)/8] |= (1 << ((n) & 7))) + #define FD_CLR(n, p) ((p)->fd_bits[(n)/8] &= ~(1 << ((n) & 7))) + #define FD_ISSET(n,p) ((p)->fd_bits[(n)/8] & (1 << ((n) & 7))) + #define FD_ZERO(p) memset((void*)(p),0,sizeof(*(p))) + + typedef struct fd_set { + u8 fd_bits [(FD_SETSIZE+7)/8]; + } fd_set; + +#endif + +#ifndef TCP_NODELAY +#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */ +#endif +#ifndef TCP_KEEPALIVE +#define TCP_KEEPALIVE 0x02 /* send KEEPALIVE probes when idle for pcb->keepalive miliseconds */ +#endif + +#ifndef socklen_t +#define socklen_t u32 +#endif + +#ifndef htons +#define htons(x) (x) +#endif +#ifndef ntohs +#define ntohs(x) (x) +#endif +#ifndef htonl +#define htonl(x) (x) +#endif +#ifndef ntohl +#define ntohl(x) (x) +#endif + +#ifndef h_addr +#define h_addr h_addr_list[0] +#endif + +#ifndef IP4_ADDR +#define IP4_ADDR(ipaddr, a,b,c,d) (ipaddr)->s_addr = htonl(((u32)(a&0xff)<<24)|((u32)(b&0xff)<<16)|((u32)(c&0xff)<<8)|(u32)(d&0xff)) +#define ip4_addr1(ipaddr) ((u32)(ntohl((ipaddr)->s_addr) >> 24) & 0xff) +#define ip4_addr2(ipaddr) ((u32)(ntohl((ipaddr)->s_addr) >> 16) & 0xff) +#define ip4_addr3(ipaddr) ((u32)(ntohl((ipaddr)->s_addr) >> 8) & 0xff) +#define ip4_addr4(ipaddr) ((u32)(ntohl((ipaddr)->s_addr)) & 0xff) +#endif + +#define POLLIN 0x0001 +#define POLLPRI 0x0002 +#define POLLOUT 0x0004 +#define POLLERR 0x0008 +#define POLLHUP 0x0010 +#define POLLNVAL 0x0020 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HAVE_IN_ADDR +#define HAVE_IN_ADDR +struct in_addr { + u32 s_addr; +}; +#endif + +struct sockaddr_in { + u8 sin_len; + u8 sin_family; + u16 sin_port; + struct in_addr sin_addr; + s8 sin_zero[8]; +}; + +struct sockaddr { + u8 sa_len; + u8 sa_family; + s8 sa_data[14]; +}; + +struct hostent { + char *h_name; /* official name of host */ + char **h_aliases; /* alias list */ + u16 h_addrtype; /* host address type */ + u16 h_length; /* length of address */ + char **h_addr_list; /* list of addresses from name server */ +}; + +struct pollsd { + s32 socket; + u32 events; + u32 revents; +}; + +u32 inet_addr(const char *cp); +s8 inet_aton(const char *cp, struct in_addr *addr); +char *inet_ntoa(struct in_addr addr); /* returns ptr to static buffer; not reentrant! */ + +s32 if_config( char *local_ip, char *netmask, char *gateway,bool use_dhcp, int max_retries); +s32 if_configex(struct in_addr *local_ip, struct in_addr *netmask, struct in_addr *gateway, bool use_dhcp, int max_retries); + +s32 net_init(); +#ifdef HW_RVL +typedef s32 (*netcallback)(s32 result, void *usrdata); +s32 net_init_async(netcallback cb, void *usrdata); +s32 net_get_status(void); +void net_wc24cleanup(); +s32 net_get_mac_address(void *mac_buf); +#endif +void net_deinit(); + +u32 net_gethostip(); +s32 net_socket(u32 domain,u32 type,u32 protocol); +s32 net_bind(s32 s,struct sockaddr *name,socklen_t namelen); +s32 net_listen(s32 s,u32 backlog); +s32 net_accept(s32 s,struct sockaddr *addr,socklen_t *addrlen); +s32 net_connect(s32 s,struct sockaddr *,socklen_t); +s32 net_write(s32 s,const void *data,s32 size); +s32 net_send(s32 s,const void *data,s32 size,u32 flags); +s32 net_sendto(s32 s,const void *data,s32 len,u32 flags,struct sockaddr *to,socklen_t tolen); +s32 net_recv(s32 s,void *mem,s32 len,u32 flags); +s32 net_recvfrom(s32 s,void *mem,s32 len,u32 flags,struct sockaddr *from,socklen_t *fromlen); +s32 net_read(s32 s,void *mem,s32 len); +s32 net_close(s32 s); +s32 net_select(s32 maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,struct timeval *timeout); +s32 net_setsockopt(s32 s,u32 level,u32 optname,const void *optval,socklen_t optlen); +s32 net_ioctl(s32 s, u32 cmd, void *argp); +s32 net_fcntl(s32 s, u32 cmd, u32 flags); +s32 net_poll(struct pollsd *sds,s32 nsds,s32 timeout); +s32 net_shutdown(s32 s, u32 how); + +struct hostent * net_gethostbyname(const char *addrString); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/aram.h b/wii/libogc/include/ogc/aram.h new file mode 100644 index 0000000000..af382a5cca --- /dev/null +++ b/wii/libogc/include/ogc/aram.h @@ -0,0 +1,272 @@ +/*------------------------------------------------------------- + +aram.h -- ARAM subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#ifndef __ARAM_H__ +#define __ARAM_H__ + +/*! + * \file aram.h + * \brief ARAM subsystem + * + */ + +#include + + +/*! + * \addtogroup dmamode ARAM DMA transfer direction + * @{ + */ + +#define AR_MRAMTOARAM 0 /*!< direction: MRAM -> ARAM (write) */ +#define AR_ARAMTOMRAM 1 /*!< direction: ARAM -> MRAM (read) */ + +/*! + * @} + */ + + + +/*! + * \addtogroup memmode ARAM memory access modes + * @{ + */ + +#define AR_ARAMINTALL 0 /*!< use all the internal ARAM memory */ +#define AR_ARAMINTUSER 1 /*!< use only the internal user space of the ARAM memory */ + +/*! + * @} + */ + + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + +/*! + * \typedef void (*ARCallback)(void) + * \brief function pointer typedef for the user's ARAM interrupt callback + * + * \param none + */ +typedef void (*ARCallback)(void); + + +/*! + * \fn ARCallback AR_RegisterCallback(ARCallback callback) + * \brief Register the given function as a DMA callback + * + * Any existing callback is replaced unconditionally + * + * \param[in] callback to be invoked upon completion of DMA transaction + * + * \return pointer to the previously registered callback and NULL respectively + */ +ARCallback AR_RegisterCallback(ARCallback callback); + + +/*! + * \fn u32 AR_GetDMAStatus() + * \brief Get current status of DMA + * + * \return zero if DMA is idle, non-zero if a DMA is in progress + */ +u32 AR_GetDMAStatus(); + + +/*! + * \fn u32 AR_Init(u32 *stack_idx_array,u32 num_entries) + * \brief Initializes ARAM subsystem. + * + * Following tasks are performed: + * + * - Disables ARAM DMA + * - Sets DMA callback to NULL + * - Initializes ARAM controller + * - Determines size of ARAM memory + * - Initializes the ARAM stack based memory allocation system
+ * + * The parameter u32 *stack_idx_array points to an array of u32 integers. The parameter u32 num_entries specifies the number of entries in this array.
+ * The user application is responsible for determining how many ARAM blocks the device driver can allocate.
+ * + * As an example, consider: + * \code + * #define MAX_NUM_BLOCKS 10 + * + * u32 aram_blocks[MAX_NUM_BLOCKS]; + * ... + * void func(void) + * { + * AR_Init(aram_blocks, MAX_NUM_BLOCKS); + * } + * \endcode + * + * Here, we are telling AR that the application will allocate, at most, 10 blocks (of arbitrary size), and that AR should store addresses for those blocks in the array aram_blocks. Note that the array is simply storage supplied by the application so that AR can track the number and size of memory blocks allocated by AR_Alloc(). + * AR_Free()also uses this array to release blocks.
+ * If you do not wish to use AR_Alloc() and AR_Free() and would rather manage ARAM usage within your application, then call AR_Init() like so:
+ * + * AR_Init(NULL, 0);
+ * + * The AR_Init() function also calculates the total size of the ARAM aggregate. Note that this procedure is destructive - i.e., any data stored in ARAM will be corrupted.
+ * AR_Init()may be invoked multiple times. This function checks the state of an initialization flag; if asserted, this function will simply exit on subsequent calls. To perform another initialization of the ARAM driver, call AR_Reset() before invoking AR_Init() again. + * + * \param[in] stack_idx_array pointer to an array of u32 integer + * \param[in] num_entries number of entries in the specified array + * + * \return base address of the "user" ARAM area. As of this writing, the operating system reserves the bottom 16 KB of ARAM. Therefore, AR_Init() returns 0x04000 to indicate the starting location of the ARAM user area. + */ +u32 AR_Init(u32 *stack_idx_array,u32 num_entries); + + +/*! + * \fn void AR_StartDMA(u32 dir,u32 memaddr,u32 aramaddr,u32 len) + * \brief Initiates a DMA between main memory and ARAM. + * + * This function: + * + * - Does not perform boundery-checking on addresses and lengths. + * - Will assert failure if a DMA is allready in progress. + * - Is provided for debugging purpose. Application programmers must use the ARQ API in order to access ARAM. + * + * \param[in] dir specifies the \ref dmamode "direction" of transfer. + * \param[in] memaddr specifies main memory address for the transfer + * \param[in] aramaddr specifies the ARAM address for the transfer. NOTE: Addresses are 27bits wide and refer to bytes + * \param[in] len specifies the length of the block to transfer. NOTE: Must be in bytes and a multiple of 32 + * + * \return none + */ +void AR_StartDMA(u32 dir,u32 memaddr,u32 aramaddr,u32 len); + + +/*! + * \fn u32 AR_Alloc(u32 len) + * \brief Allocate a block of memory from ARAM having len bytes. + * + * The len parameter must be a multiple of 32 + * + * \param[in] len length of the specified block of memory in ARAM + * + * \return address of the block if successful, otherwise NULL + */ +u32 AR_Alloc(u32 len); + + +/*! + * \fn u32 AR_Free(u32 *len) + * \brief Free a block from ARAM + * + * \param[out] len pointer to receive the length of the free'd ARAM block. This is optional and can be NULL. + * + * \return ARAM current baseaddress after free'ing the block + */ +u32 AR_Free(u32 *len); + + +/*! + * \fn void AR_Clear(u32 flag) + * \brief Clear ARAM memory + * + * \param[in] flag specifies the region of ARAM to clear + * + * \return none + */ +void AR_Clear(u32 flag); + + +/*! + * \fn BOOL AR_CheckInit() + * \brief Get the ARAM subsystem initialization flag + * + * \return TRUE if the ARAM subsystem has been initialized(via AR_Init())
+ * FALSE if the ARAM subsystem has not been initialized, or has been reset(via AR_Reset()) + */ +BOOL AR_CheckInit(); + + +/*! + * \fn void AR_Reset() + * \brief Clears the ARAM subsystem initialization flag. + * + * Calling AR_Init() after this function will cause a "real" initialization of ARAM + * + * \return none + */ +void AR_Reset(); + + +/*! + * \fn u32 AR_GetSize() + * \brief Get the total size - in bytes - of ARAM as calculated during AR_Init() + * + * \return size of the specified ARAM block + */ +u32 AR_GetSize(); + + +/*! + * \fn u32 AR_GetBaseAddress() + * \brief Get the baseaddress of ARAM memory + * + * \return ARAM memory baseaddress + */ +u32 AR_GetBaseAddress(); + + +/*! + * \fn u32 AR_GetInternalSize() + * \brief Get the size of the internal ARAM memory + * + * \return ARAM internal memory size + */ +u32 AR_GetInternalSize(); + + +/*! + * \def AR_StartDMARead(maddr,araddr,tlen) + * \brief Wraps the DMA read operation done by AR_StartDMA() + */ +#define AR_StartDMARead(maddr,araddr,tlen) \ + AR_StartDMA(AR_ARAMTOMRAM,maddr,araddr,tlen); + + +/*! + * \def AR_StartDMAWrite(maddr,araddr,tlen) + * \brief Wraps the DMA write operation done by AR_StartDMA() + */ +#define AR_StartDMAWrite(maddr,araddr,tlen) \ + AR_StartDMA(AR_MRAMTOARAM,maddr,araddr,tlen); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif //__ARAM_H__ diff --git a/wii/libogc/include/ogc/arqmgr.h b/wii/libogc/include/ogc/arqmgr.h new file mode 100644 index 0000000000..3c64628851 --- /dev/null +++ b/wii/libogc/include/ogc/arqmgr.h @@ -0,0 +1,113 @@ +/*------------------------------------------------------------- + + +arqmgr.h -- ARAM task queue management + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ + + +#ifndef __ARQMGR_H__ +#define __ARQMGR_H__ + + +/*! + * \file arqmgr.h + * \brief ARAM queue managemnt subsystem + * + */ + + +#include + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + +/*! + * \typedef void (*ARQMCallback)() + * \brief function pointer typedef for the user's callback when ARAM operation has completed + * \param none + */ +typedef void (*ARQMCallback)(s32 result); + + +/*! + * \fn void ARQM_Init(u32 arambase,s32 len) + * \brief Initialize the ARAM queue management system + * + * \param[in] arambase ARAM startaddress to take for the queue stack + * \param[in] len maximum amount of memory to be reserved from the ARAM for the queue management + * + * \return none + */ +void ARQM_Init(u32 arambase,s32 len); + + +/*! + * \fn u32 ARQM_PushData(void *buff,s32 len) + * \brief Push the data onto the ARAM queue + * + * \param[in] buff startaddress of buffer to be pushed onto the queue. NOTE: Must be 32-bytealigned. + * \param[in] len length of data to be pushed onto the queue. + * + * \return none + */ +u32 ARQM_PushData(void *buffer,s32 len); + + +/*! + * \fn u32 ARQM_GetZeroBuffer() + * \brief Returns ARAM address of 'zero buffer' + * + * \return See description + */ +u32 ARQM_GetZeroBuffer(); + + +/*! + * \fn u32 ARQM_GetStackPointer() + * \brief Return the ARAM address of the next free stack pointer + * + * \return See description + */ +u32 ARQM_GetStackPointer(); + + +/*! + * \fn u32 ARQM_GetFreeSize() + * \brief Return Returns remaining number of bytes on stack + * + * \return See description + */ +u32 ARQM_GetFreeSize(); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/arqueue.h b/wii/libogc/include/ogc/arqueue.h new file mode 100644 index 0000000000..2aff9e1780 --- /dev/null +++ b/wii/libogc/include/ogc/arqueue.h @@ -0,0 +1,115 @@ +/*------------------------------------------------------------- + +arqueue.h -- ARAM task request queue implementation + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ + + +#ifndef __ARQUEUE_H__ +#define __ARQUEUE_H__ + +#include +#include +#include "aram.h" + +#define ARQ_MRAMTOARAM AR_MRAMTOARAM +#define ARQ_ARAMTOMRAM AR_ARAMTOMRAM + +#define ARQ_DEF_CHUNK_SIZE 4096 + +#define ARQ_PRIO_LO 0 +#define ARQ_PRIO_HI 1 + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +enum { + ARQ_TASK_READY = 0, + ARQ_TASK_RUNNING, + ARQ_TASK_FINISHED +}; + +typedef struct _arq_request ARQRequest; +typedef void (*ARQCallback)(ARQRequest *); + +struct _arq_request { + lwp_node node; + u32 owner,dir,prio,state; + u32 aram_addr,mram_addr,len; + ARQCallback callback; +}; + +void ARQ_Init(); +void ARQ_Reset(); + + +/*! + * \fn void ARQ_PostRequest(ARQRequest *req,u32 owner,u32 dir,u32 prio,u32 aram_addr,u32 mram_addr,u32 len) + * \brief Enqueue a ARAM DMA transfer request. + * + * \param[in] req structure to hold ARAM DMA request informations. + * \param[in] owner unique owner id. + * \param[in] dir direction of ARAM DMA transfer. + * \param[in] prio priority of request. + * \param[in] aram_addr startaddress of buffer to be pushed onto the queue. NOTE: Must be 32-bytealigned. + * \param[in] mram_addr length of data to be pushed onto the queue. + * \param[in] len startaddress of buffer to be pushed onto the queue. NOTE: Must be 32-bytealigned. + * \param[in] cb length of data to be pushed onto the queue. + * + * \return none + */ +void ARQ_PostRequest(ARQRequest *req,u32 owner,u32 dir,u32 prio,u32 aram_addr,u32 mram_addr,u32 len); + + +/*! + * \fn void ARQ_PostRequestAsync(ARQRequest *req,u32 owner,u32 dir,u32 prio,u32 aram_addr,u32 mram_addr,u32 len,ARQCallback cb) + * \brief Enqueue a ARAM DMA transfer request. + * + * \param[in] req structure to hold ARAM DMA request informations. + * \param[in] owner unique owner id. + * \param[in] dir direction of ARAM DMA transfer. + * \param[in] prio priority of request. + * \param[in] aram_addr startaddress of buffer to be pushed onto the queue. NOTE: Must be 32-bytealigned. + * \param[in] mram_addr length of data to be pushed onto the queue. + * \param[in] len startaddress of buffer to be pushed onto the queue. NOTE: Must be 32-bytealigned. + * \param[in] cb length of data to be pushed onto the queue. + * + * \return none + */ +void ARQ_PostRequestAsync(ARQRequest *req,u32 owner,u32 dir,u32 prio,u32 aram_addr,u32 mram_addr,u32 len,ARQCallback cb); +void ARQ_RemoveRequest(ARQRequest *req); +void ARQ_SetChunkSize(u32 size); +u32 ARQ_GetChunkSize(); +void ARQ_FlushQueue(); +u32 ARQ_RemoveOwnerRequest(u32 owner); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/audio.h b/wii/libogc/include/ogc/audio.h new file mode 100644 index 0000000000..8d13f68042 --- /dev/null +++ b/wii/libogc/include/ogc/audio.h @@ -0,0 +1,319 @@ +/*------------------------------------------------------------- + +audio.h -- Audio subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + + +-------------------------------------------------------------*/ + + +#ifndef __AUDIO_H__ +#define __AUDIO_H__ + +/*! \file audio.h +\brief AUDIO subsystem + +*/ + +#include + +/*! + * \addtogroup ai_stream_mode AI streaming modes + * @{ + */ + +#define AI_STREAM_STOP 0x00000000 /*!< Stop streaming audio playback */ +#define AI_STREAM_START 0x00000001 /*!< Start streaming audio playback */ + +/*! + * @} + */ + + + +/*! + * \addtogroup ai_sample_rates AI sampling rates + * @{ + */ + +#define AI_SAMPLERATE_32KHZ 0x00000000 /*!< AI sampling rate at 32kHz */ +#define AI_SAMPLERATE_48KHZ 0x00000001 /*!< AI sampling rate at 48kHz */ + +/*! + * @} + */ + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/*! + * \typedef void (*AIDCallback)(void) + * \brief function pointer typedef for the user's Audio DMA interrupt callback + * + * \param none + */ +typedef void (*AIDCallback)(void); + + +/*! + * \typedef void (*AISCallback)(u32 smp_cnt) + * \brief function pointer typedef for the user's Audio Streaming interrupt callback + * + * \param smp_cnt AI sample count + */ +typedef void (*AISCallback)(u32 smp_cnt); + + +/*! + * \fn AISCallback AUDIO_RegisterStreamCallback(AISCallback callback) + * \brief Register a user callback function for the AUDIO streaming interface + * + * \param[in] callback pointer to the function which to call when AIS interrupt has triggered. + * + * \return pointer to old callback function or NULL respectively. + */ +AISCallback AUDIO_RegisterStreamCallback(AISCallback callback); + + +/*! + * \fn void AUDIO_Init(u8 *stack) + * \brief Initialize the AUDIO subsystem + * + * \param[in] stack pointer to a memory area to work as stack when calling the callbacks. May be NULL + * + * \return none + */ +void AUDIO_Init(u8 *stack); + + +/*! + * \fn void AUDIO_SetStreamVolLeft(u8 vol) + * \brief Set streaming volume on the left channel. + * + * \param[in] vol level of volume 0<= vol <=255 + * + * \return none + */ +void AUDIO_SetStreamVolLeft(u8 vol); + + +/*! + * \fn u8 AUDIO_GetStreamVolLeft() + * \brief Get streaming volume of the left channel. + * + * \return level of volume. + */ +u8 AUDIO_GetStreamVolLeft(); + + +/*! + * \fn void AUDIO_SetStreamVolRight(u8 vol) + * \brief set streaming volume of the right channel. + * + * \param[in] vol level of volume 0<= vol <=255 + * + * \return none + */ +void AUDIO_SetStreamVolRight(u8 vol); + + +/*! + * \fn u8 AUDIO_GetStreamVolRight() + * \brief Get streaming volume of the right channel. + * + * \return level of volume. + */ +u8 AUDIO_GetStreamVolRight(); + + +/*! + * \fn void AUDIO_SetStreamSampleRate(u32 rate) + * \brief Set streaming sample rate + * + * \param[in] rate streaming \ref ai_sample_rates "sample rate" + * + * \return none + */ +void AUDIO_SetStreamSampleRate(u32 rate); + + +/*! + * \fn u32 AUDIO_GetStreamSampleRate() + * \brief Get streaming sample rate + * + * \return \ref ai_sample_rates "sample rate" + */ +u32 AUDIO_GetStreamSampleRate(); + + +/*! + * \fn AIDCallback AUDIO_RegisterDMACallback(AIDCallback callback) + * \brief Register a user callback function for the audio DMA interface. + * + * This callback will be called whenever the audio DMA requests new data.
+ * Internally the DMA buffers are double buffered. + * + * \param[in] callback pointer to the function which to call when AID interrupt has triggered. + * + * \return pointer to old callback function or NULL respectively. + */ +AIDCallback AUDIO_RegisterDMACallback(AIDCallback callback); + + +/*! + * \fn void AUDIO_InitDMA(u32 startaddr,u32 len) + * \brief Initialize an audio DMA transfer + * + * \param[in] startaddr start address of the memory region to load into the audio DMA. NOTE: Has to be aligned on a 32byte boundery! + * \param[in] len lenght of data to load into the audio DMA. NOTE: Should be a multiple of 32 + * + * \return none + */ +void AUDIO_InitDMA(u32 startaddr,u32 len); + + +/*! + * \fn u16 AUDIO_GetDMAEnableFlag() + * \brief Get the audio DMA flag + * + * \return state of the current DMA operation. + */ +u16 AUDIO_GetDMAEnableFlag(); + + +/*! + * \fn void AUDIO_StartDMA() + * \brief Start the audio DMA operation. + * + * Starts to transfer the data from main memory to the audio interface thru DMA.
+ * This call should follow the call to AUDIO_InitDMA() which is used to initialize DMA transfers. + * + * \return none + */ +void AUDIO_StartDMA(); + + +/*! + * \fn void AUDIO_StopDMA() + * \brief Stop the previously started audio DMA operation. + * + * \return none + */ +void AUDIO_StopDMA(); + + +/*! + * \fn u32 AUDIO_GetDMABytesLeft() + * \brief Get the count of bytes, left to play, from the audio DMA interface + * + * \return count of bytes left to play. + */ +u32 AUDIO_GetDMABytesLeft(); + + +/*! + * \fn u32 AUDIO_GetDMALength() + * \brief Get the DMA transfer length configured in the audio DMA interface. + * + * \return length of data loaded into the audio DMA interface. + */ +u32 AUDIO_GetDMALength(); + + +/*! + * \fn u32 AUDIO_GetDMAStartAddr() + * \brief Get the main memory address for the DMA operation. + * + * \return start address of mainmemory loaded into the audio DMA interface. + */ +u32 AUDIO_GetDMAStartAddr(); + + +/*! + * \fn void AUDIO_SetStreamTrigger(u32 cnt) + * \brief Set the sample count for the stream trigger + * + * \param[in] cnt count of samples when to trigger the audio stream. + * + * \return none + */ +void AUDIO_SetStreamTrigger(u32 cnt); + + +/*! + * \fn void AUDIO_ResetStreamSampleCnt() + * \brief Reset the stream sample count register. + * + * \return none + */ +void AUDIO_ResetStreamSampleCnt(); + + +/*! + * \fn void AUDIO_SetDSPSampleRate(u8 rate) + * \brief Set the sampling rate for the DSP interface + * + * \param[in] rate sampling rate to set for the DSP (AI_SAMPLERATE_32KHZ,AI_SAMPLERATE_48KHZ) + * + * \return none + */ +void AUDIO_SetDSPSampleRate(u8 rate); + + +/*! + * \fn u32 AUDIO_GetDSPSampleRate() + * \brief Get the sampling rate for the DSP interface + * + * \return DSP sampling rate (AI_SAMPLERATE_32KHZ,AI_SAMPLERATE_48KHZ) + */ +u32 AUDIO_GetDSPSampleRate(); + + +/*! + * \fn void AUDIO_SetStreamPlayState(u32 state) + * \brief Set the play state for the streaming audio interface + * + * \param[in] state playstate of the streaming audio interface (AI_STREAM_STOP,AI_STREAM_START) + * + * \return none + */ +void AUDIO_SetStreamPlayState(u32 state); + + +/*! + * \fn u32 AUDIO_GetStreamPlayState() + * \brief Get the play state from the streaming audio interface + * + * \return playstate (AI_STREAM_STOP,AI_STREAM_START) + */ +u32 AUDIO_GetStreamPlayState(); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/cache.h b/wii/libogc/include/ogc/cache.h new file mode 100644 index 0000000000..0753956a1f --- /dev/null +++ b/wii/libogc/include/ogc/cache.h @@ -0,0 +1,326 @@ +/*------------------------------------------------------------- + + +cache.h -- Cache interface + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ + + +#ifndef __CACHE_H__ +#define __CACHE_H__ + +/*! \file cache.h +\brief Cache subsystem + +*/ + +#include + +#define LC_BASEPREFIX 0xe000 +#define LC_BASE (LC_BASEPREFIX<<16) + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + +/*! + * \fn void DCEnable() + * \brief Enable L1 d-cache + * + * \return none + */ +void DCEnable(); + + +/*! + * \fn void DCDisable() + * \brief Disable L1 d-cache + * + * \return none + */ +void DCDisable(); + + +/*! + * \fn void DCFreeze() + * \brief Current contents of the L1 d-cache are locked down and will not be cast out. + * + * Hits are still serviced, but misses go straight to L2 or 60x bus. Most cache operations, such as DCFlushRange(), will still execute regardless of whether the cache is frozen.
+ * NOTE: In PowerPC architecture jargon, this feature is referred to as "locking" the data cache. We use the word "freeze" to distinguish it from the locked cache and DMA features. + * + * \return none + */ +void DCFreeze(); + + +/*! + * \fn void DCUnfreeze() + * \brief Undoes actions of DCFreeze(). + * + * Old cache blocks will now be cast out on subsequent L1 misses.
+ * NOTE: In PowerPC architecture jargon, this feature is referred to as "locking" the data cache. We use the word "freeze" to distinguish it from the locked cache and DMA features. + * + * \return none + */ +void DCUnfreeze(); + + +/*! + * \fn void DCFlashInvalidate() + * \brief Invalidate L1 d-cache. + * + * An invalidate operation is issued that marks the state of each data cache block as invalid without writing back modified cache blocks to memory.
+ * Cache access is blocked during this time.Bus accesses to the cache are signaled as a miss during invalidate-all operations. + * + * \return none + */ +void DCFlashInvalidate(); + + +/*! + * \fn void DCInvalidateRange(void *startaddress,u32 len) + * \brief Invalidates a given range of the d-cache. + * + * If any part of the range hits in the d-cache, the corresponding block will be invalidated. + * + * \param[in] startaddress pointer to the startaddress of the memory range to invalidate. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of the range to invalidate. NOTE: Should be a multiple of 32 + * + * \return none + */ +void DCInvalidateRange(void *startaddress,u32 len); + + +/*! + * \fn void DCFlushRange(void *startaddress,u32 len) + * \brief Flushes a given range. + * + * If any part of the range hits in the d-cache the corresponding block will be flushed to main memory and invalidated.
+ * NOTE: This function invokes a "sync" after flushing the range. This means the function will stall until the CPU knows that the data has been writen to main memory + * + * \param[in] startaddress pointer to the startaddress of the memory range to flush. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of range to be flushed. NOTE: Should be a multiple of 32 + * + *\return none + */ +void DCFlushRange(void *startaddress,u32 len); + +/*! + * \fn void DCStoreRange(void *startaddress,u32 len) + * \brief Ensures a range of memory is updated with any modified data in the cache. + * + * NOTE: This function invokes a "sync" after storing the range. This means the function will stall until the CPU knows that the data has been writen to main memory + * + * \param[in] startaddress pointer to the startaddress of the memory range to store. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of the range to store. NOTE: Should be a multiple of 32 + * + * \return none + */ +void DCStoreRange(void *startaddress,u32 len); + + +/*! + * \fn void DCFlushRangeNoSync(void *startaddress,u32 len) + * \brief Flushes a given range. + * + * If any part of the range hits in the d-cache the corresponding block will be flushed to main memory and invalidated.
+ * NOTE: This routine does not perform a "sync" to ensure that the range has been flushed to memory. That is, the cache blocks are sent to the bus interface unit for storage to main memory, but by the time this function returns, you are not guaranteed that the blocks have been written to memory. + * + * \param[in] startaddress pointer to the startaddress of the memory range to flush. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of range to be flushed. NOTE: Should be a multiple of 32 + * + * \return none + */ +void DCFlushRangeNoSync(void *startaddress,u32 len); + + +/*! + * \fn void DCStoreRangeNoSync(void *startaddress,u32 len) + * \brief Ensures a range of memory is updated with any modified data in the cache. + * + * NOTE: This routine does not perform a "sync" to ensure that the range has been flushed to memory. That is, the cache blocks are sent to the bus interface unit for storage to main memory, but by the time this function returns, you are not guaranteed that the blocks have been written to memory + * + * \param[in] startaddress pointer to the startaddress of the memory range to store. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of the range to store. NOTE: Should be a multiple of 32 + * + * \return none + */ +void DCStoreRangeNoSync(void *startaddress,u32 len); + + +/*! + * \fn void DCZeroRange(void *startaddress,u32 len) + * \brief Loads a range of memory into cache and zeroes all the cache lines. + * + * \param[in] startaddress pointer to the startaddress of the memory range to load/zero. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of the range to load/zero. NOTE: Should be a multiple of 32 + * + * \return none + */ +void DCZeroRange(void *startaddress,u32 len); + + +/*! + * \fn void DCTouchRange(void *startaddress,u32 len) + * \brief Loads a range of memory into cache. + * + * \param[in] startaddress pointer to the startaddress of the memory range to load. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of the range to load. NOTE: Should be a multiple of 32 + * + * \return none + */ +void DCTouchRange(void *startaddress,u32 len); + + +/*! + * \fn void ICSync() + * \brief Performs an instruction cache synchronization. + * + * This ensures that all instructions preceding this instruction have completed before this instruction completes. + * + * \return none + */ +void ICSync(); + + +/*! + * \fn void ICFlashInvalidate() + * \brief Invalidate the L1 i-cache. + * + * An invalidate operation is issued that marks the state of each instruction cache block as invalid without writing back modified cache blocks to memory.
+ * Cache access is blocked during this time. Bus accesses to the cache are signaled as a miss during invalidate-all operations. + * + * \return none + */ +void ICFlashInvalidate(); + + +/*! + * \fn void ICEnable() + * \brief Enable L1 i-cache + * + * \return none + */ +void ICEnable(); + + +/*! + * \fn void ICDisable() + * \brief Disable L1 i-cache + * + * \return none + */ +void ICDisable(); + + +/*! + * \fn void ICFreeze() + * \brief Current contents of the L1 i-cache are locked down and will not be cast out. + * + * Hits are still serviced, but misses go straight to L2 or 60x bus.
+ * NOTE: In PowerPC architecture jargon, this feature is referred to as "locking" the data cache. We use the word "freeze" to distinguish it from the locked cache and DMA features. + * + * \return none + */ +void ICFreeze(); + + +/*! + * \fn void ICUnfreeze() + * \brief Undoes actions of ICFreeze(). + * + * Old cache blocks will now be cast out on subsequent L1 misses.
+ * NOTE: In PowerPC architecture jargon, this feature is referred to as "locking" the data cache. We use the word "freeze" to distinguish it from the locked cache and DMA features. + * + * \return none + */ +void ICUnfreeze(); + + +/*! + * \fn void ICBlockInvalidate(void *startaddress) + * \brief Invalidates a block in the i-cache. + * + * If the block hits in the i-cache, the corresponding block will be invalidated. + * + * \param[in] startaddress pointer to the startaddress of the memory block to invalidate. NOTE: Has to be aligned on a 32byte boundery + * + *\return none + */ +void ICBlockInvalidate(void *startaddress); + + +/*! + * \fn void ICInvalidateRange(void *startaddress,u32 len) + * \brief Invalidate a range in the L1 i-cache. + * + * If any part of the range hits in the i-cache, the corresponding block will be invalidated. + * + * \param[in] startaddress pointer to the startaddress of the memory range to invalidate. NOTE: Has to be aligned on a 32byte boundery + * \param[in] len length of the range to invalidate. NOTE: Should be a multiple of 32 + * + * \return none + */ +void ICInvalidateRange(void *startaddress,u32 len); + +/*! + * \fn void L2Enhance() + * \brief Turn on extra L2 cache features + * + * Sets the following bits in the HID4 register which affect the L2 cache: + * - L2FM=01 (64-byte fetch mode) + * - BCO=1 (dual 64-byte castout buffers) + * - L2MUM=1 (configured as 2-deep miss-under-miss cache) + * Since these features can't be enabled safely, any HID4 writes in the HBC stub will be removed. + * + * \return none + */ +#ifdef HW_RVL +void L2Enhance(); +#endif + +void LCEnable(); +void LCDisable(); +void LCLoadBlocks(void *,void *,u32); +void LCStoreBlocks(void *,void *,u32); +u32 LCLoadData(void *,void *,u32); +u32 LCStoreData(void *,void *,u32); +u32 LCQueueLength(); +u32 LCQueueWait(u32); +void LCFlushQueue(); +void LCAlloc(void *,u32); +void LCAllocNoInvalidate(void *,u32); +void LCAllocOneTag(BOOL,void *); +void LCAllocTags(BOOL,void *,u32); + +#define LCGetBase() ((void*)LC_BASE) +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/card.h b/wii/libogc/include/ogc/card.h new file mode 100644 index 0000000000..ff7d70b56b --- /dev/null +++ b/wii/libogc/include/ogc/card.h @@ -0,0 +1,605 @@ +/*------------------------------------------------------------- + +card.h -- Memory card subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#ifndef __CARD_H__ +#define __CARD_H__ + +/*! +\file card.h +\brief Memory card subsystem + +*/ + + +#include + +/*! \addtogroup cardsolts Memory card slots + * @{ + */ + +#define CARD_SLOTA 0 /*!< memory card slot A */ +#define CARD_SLOTB 1 /*!< memory card slot B */ + +/*! @} */ + + +#define CARD_WORKAREA (5*8*1024) /*!< minimum size of the workarea passed to Mount[Async]() */ +#define CARD_READSIZE 512 /*!< minimum size of block to read from memory card */ +#define CARD_FILENAMELEN 32 /*!< maximum filename length */ +#define CARD_MAXFILES 128 /*!< maximum number of files on the memory card */ + +/*! \addtogroup card_errors Memory card error codes + * @{ + */ + +#define CARD_ERROR_UNLOCKED 1 /*!< card beeing unlocked or allready unlocked. */ +#define CARD_ERROR_READY 0 /*!< card is ready. */ +#define CARD_ERROR_BUSY -1 /*!< card is busy. */ +#define CARD_ERROR_WRONGDEVICE -2 /*!< wrong device connected in slot */ +#define CARD_ERROR_NOCARD -3 /*!< no memory card in slot */ +#define CARD_ERROR_NOFILE -4 /*!< specified file not found */ +#define CARD_ERROR_IOERROR -5 /*!< internal EXI I/O error */ +#define CARD_ERROR_BROKEN -6 /*!< directory structure or file entry broken */ +#define CARD_ERROR_EXIST -7 /*!< file allready exists with the specified parameters */ +#define CARD_ERROR_NOENT -8 /*!< found no empty block to create the file */ +#define CARD_ERROR_INSSPACE -9 /*!< not enough space to write file to memory card */ +#define CARD_ERROR_NOPERM -10 /*!< not enough permissions to operate on the file */ +#define CARD_ERROR_LIMIT -11 /*!< card size limit reached */ +#define CARD_ERROR_NAMETOOLONG -12 /*!< filename too long */ +#define CARD_ERROR_ENCODING -13 /*!< font encoding PAL/SJIS mismatch*/ +#define CARD_ERROR_CANCELED -14 /*!< card operation canceled */ +#define CARD_ERROR_FATAL_ERROR -128 /*!< fatal error, non recoverable */ + +/*! @} */ + + +/* File attribute defines */ +#define CARD_ATTRIB_PUBLIC 0x04 +#define CARD_ATTRIB_NOCOPY 0x08 +#define CARD_ATTRIB_NOMOVE 0x10 + +/* Banner & Icon Attributes */ +#define CARD_BANNER_W 96 +#define CARD_BANNER_H 32 + +#define CARD_BANNER_NONE 0x00 +#define CARD_BANNER_CI 0x01 +#define CARD_BANNER_RGB 0x02 +#define CARD_BANNER_MASK 0x03 + +#define CARD_MAXICONS 8 +#define CARD_ICON_W 32 +#define CARD_ICON_H 32 + +#define CARD_ICON_NONE 0x00 +#define CARD_ICON_CI 0x01 +#define CARD_ICON_RGB 0x02 +#define CARD_ICON_MASK 0x03 + +#define CARD_ANIM_LOOP 0x00 +#define CARD_ANIM_BOUNCE 0x04 +#define CARD_ANIM_MASK 0x04 + +#define CARD_SPEED_END 0x00 +#define CARD_SPEED_FAST 0x01 +#define CARD_SPEED_MIDDLE 0x02 +#define CARD_SPEED_SLOW 0x03 +#define CARD_SPEED_MASK 0x03 + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + +/*! \typedef struct _card_file card_file +\brief structure to hold the fileinformations upon open and for later use. +\param chn CARD slot. +\param filenum file index in the card directory structure. +\param offset offset into the file. +\param len length of file. +\param iblock block index on memory card. +*/ +typedef struct _card_file { + s32 chn; + s32 filenum; + s32 offset; + s32 len; + u16 iblock; +} card_file; + + +/*! \typedef struct card_dir +\brief structure to hold the information of a directory entry +\param chn CARD slot. +\param fileno file index in the card directory structure. +\param filelen length of file. +\param filename[CARD_FILENAMELEN] name of the file on card. +\param gamecode[4] string identifier <=4. +\param company[2] string identifier <=2. +\param showall boolean flag whether to showall entries or ony those identified by card_gamecode and card_company, previously set within the call to CARD_Init() +*/ +typedef struct _card_dir { + s32 chn; + u32 fileno; + u32 filelen; + u8 permissions; + u8 filename[CARD_FILENAMELEN]; + u8 gamecode[4]; + u8 company[2]; + bool showall; +} card_dir; + + +/*! \typedef struct card_stat +\brief structure to hold the additional statistical informations. +\param filename[CARD_FILENAMELEN] name of the file on card. +\param len length of file. +\param gamecode[4] string identifier <=4. +\param company[2] string identifier <=2. +\param banner_fmt format of banner. +\param icon_addr icon image address in file. +\param icon_speed speed of an animated icon. +\param comment_addr address in file of the comment block. +\param offset_banner offset in file to the banner's image data. +\param offset_banner_tlut offset in file to the banner's texture lookup table. +\param offset_icon[CARD_MAXICONS] array of offsets in file to the icon's image data banner_fmt)&CARD_BANNER_MASK) +#define CARD_SetBannerFmt(stat,fmt) ((stat)->banner_fmt = (u8)(((stat)->banner_fmt&~CARD_BANNER_MASK)|(fmt))) +#define CARD_GetIconFmt(stat,n) (((stat)->icon_fmt>>(2*(n)))&CARD_ICON_MASK) +#define CARD_SetIconFmt(stat,n,fmt) ((stat)->icon_fmt = (u16)(((stat)->icon_fmt&~(CARD_ICON_MASK<<(2*(n))))|((fmt)<<(2*(n))))) +#define CARD_GetIconSpeed(stat,n) (((stat)->icon_speed>>(2*(n)))&~CARD_SPEED_MASK); +#define CARD_SetIconSpeed(stat,n,speed) ((stat)->icon_speed = (u16)(((stat)->icon_fmt&~(CARD_SPEED_MASK<<(2*(n))))|((speed)<<(2*(n))))) +#define CARD_SetIconAddr(stat,addr) ((stat)->icon_addr = (u32)(addr)) +#define CARD_SetCommentAddr(stat,addr) ((stat)->comment_addr = (u32)(addr)) + +/*! \typedef void (*cardcallback)(s32 chan,s32 result) +\brief function pointer typedef for the user's operation callback +\param chan CARD slot +\param result result of operation upon call of callback. +*/ +typedef void (*cardcallback)(s32 chan,s32 result); + + +/*! \fn s32 CARD_Init(const char *gamecode,const char *company) +\brief Performs the initialization of the memory card subsystem +\param[in] gamecode pointer to a 4byte long string to specify the vendors game code. May be NULL +\param[in] company pointer to a 2byte long string to specify the vendors company code. May be NULL + +\return \ref card_errors "card error codes" +*/ +s32 CARD_Init(const char *gamecode,const char *company); + + +/*! \fn s32 CARD_Probe(s32 chn) +\brief Performs a check against the desired EXI channel if a device is inserted +\param[in] chn CARD slot + +\return \ref card_errors "card error codes" +*/ +s32 CARD_Probe(s32 chn); + + +/*! \fn s32 CARD_ProbeEx(s32 chn,s32 *mem_size,s32 *sect_size) +\brief Performs a check against the desired EXI channel if a memory card is inserted or mounted +\param[in] chn CARD slot +\param[out] mem_size pointer to a integer variable, ready to take the resulting value (this param is optional and can be NULL) +\param[out] sect_size pointer to a integer variable, ready to take the resulting value (this param is optional and can be NULL) + +\return \ref card_errors "card error codes" +*/ +s32 CARD_ProbeEx(s32 chn,s32 *mem_size,s32 *sect_size); + + +/*! \fn s32 CARD_Mount(s32 chn,void *workarea,cardcallback detach_cb) +\brief Mounts the memory card in the slot CHN. Synchronous version. +\param[in] chn CARD slot +\param[in] workarea pointer to memory area to hold the cards system area. The startaddress of the workdarea should be aligned on a 32byte boundery +\param[in] detach_cb pointer to a callback function. This callback function will be called when the card is removed from the slot. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_Mount(s32 chn,void *workarea,cardcallback detach_cb); + + +/*! \fn s32 CARD_MountAsync(s32 chn,void *workarea,cardcallback detach_cb,cardcallback attach_cb) +\brief Mounts the memory card in the slot CHN. This function returns immediately. Asynchronous version. +\param[in] chn CARD slot +\param[in] workarea pointer to memory area to hold the cards system area. The startaddress of the workdarea should be aligned on a 32byte boundery +\param[in] detach_cb pointer to a callback function. This callback function will be called when the card is removed from the slot. +\param[in] attach_cb pointer to a callback function. This callback function will be called when the mount process has finished. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_MountAsync(s32 chn,void *workarea,cardcallback detach_cb,cardcallback attach_cb); + + +/*! \fn s32 CARD_Unmount(s32 chn) +\brief Unmounts the memory card in the slot CHN and releases the EXI bus. +\param[in] chn CARD slot + +\return \ref card_errors "card error codes" +*/ +s32 CARD_Unmount(s32 chn); + + +/*! \fn s32 CARD_Read(card_file *file,void *buffer,u32 len,u32 offset) +\brief Reads the data from the file into the buffer from the given offset with the given length. Synchronous version +\param[in] file pointer to the card_file structure. It holds the fileinformations to read from. +\param[out] buffer pointer to memory area read-in the data. The startaddress of the buffer should be aligned to a 32byte boundery. +\param[in] len length of data to read. +\param[in] offset offset into the file to read from. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_Read(card_file *file,void *buffer,u32 len,u32 offset); + + +/*! \fn s32 CARD_ReadAsync(card_file *file,void *buffer,u32 len,u32 offset,cardcallback callback) +\brief Reads the data from the file into the buffer from the given offset with the given length. This function returns immediately. Asynchronous version +\param[in] file pointer to the card_file structure. It holds the fileinformations to read from. +\param[out] buffer pointer to memory area read-in the data. The startaddress of the buffer should be aligned to a 32byte boundery. +\param[in] len length of data to read. +\param[in] offset offset into the file to read from. +\param[in] callback pointer to a callback function. This callback will be called when the read process has finished. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_ReadAsync(card_file *file,void *buffer,u32 len,u32 offset,cardcallback callback); + + +/*! \fn s32 CARD_Open(s32 chn,const char *filename,card_file *file) +\brief Opens the file with the given filename and fills in the fileinformations. +\param[in] chn CARD slot +\param[in] filename name of the file to open. +\param[out] file pointer to the card_file structure. It receives the fileinformations for later usage. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_Open(s32 chn,const char *filename,card_file *file); + + +/*! \fn s32 CARD_OpenEntry(s32 chn,card_dir *entry,card_file *file) +\brief Opens the file with the given filename and fills in the fileinformations. +\param[in] chn CARD slot +\param[in] entry pointer to the directory entry to open. +\param[out] file pointer to the card_file structure. It receives the fileinformations for later usage. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_OpenEntry(s32 chn,card_dir *entry,card_file *file); + + +/*! \fn s32 CARD_Close(card_file *file) +\brief Closes the file with the given card_file structure and releases the handle. +\param[in] file pointer to the card_file structure to close. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_Close(card_file *file); + + +/*! \fn s32 CARD_Create(s32 chn,const char *filename,u32 size,card_file *file) +\brief Creates a new file with the given filename and fills in the fileinformations. Synchronous version. +\param[in] chn CARD slot +\param[in] filename name of the file to create. +\param[in] size size of the newly created file. This must be a multiple of the memory card's sector size. +\param[out] file pointer to the card_file structure. It receives the fileinformations for later usage. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_Create(s32 chn,const char *filename,u32 size,card_file *file); + + +/*! \fn s32 CARD_CreateAsync(s32 chn,const char *filename,u32 size,card_file *file,cardcallback callback) +\brief Creates a new file with the given filename and fills in the fileinformations. This function returns immediately. Asynchronous version. +\param[in] chn CARD slot +\param[in] filename name of the file to create. +\param[in] size size of the newly created file. This must be a multiple of the memory card's sector size. +\param[out] file pointer to the card_file structure. It receives the fileinformations for later usage. +\param[in] callback pointer to a callback function. This callback will be called when the create process has finished. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_CreateAsync(s32 chn,const char *filename,u32 size,card_file *file,cardcallback callback); + + +/*! \fn s32 CARD_CreateEntry(s32 chn,card_dir *entry,card_file *file) +\brief Creates a new file with the given filename and fills in the fileinformations. Synchronous version. +\param[in] chn CARD slot +\param[in] entry pointer to the directory entry to create. +\param[out] file pointer to the card_file structure. It receives the fileinformations for later usage. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_CreateEntry(s32 chn,card_dir *direntry,card_file *file); + + +/*! \fn s32 CARD_CreateEntryAsync(s32 chn,card_dir *entry,card_file *file,cardcallback callback) +\brief Creates a new file with the given filename and fills in the fileinformations. This function returns immediately. Asynchronous version. +\param[in] chn CARD slot +\param[in] entry pointer to the directory entry to create +\param[out] file pointer to the card_file structure. It receives the fileinformations for later usage. +\param[in] callback pointer to a callback function. This callback will be called when the create process has finished. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_CreateEntryAsync(s32 chn,card_dir *direntry,card_file *file,cardcallback callback); + + +/*! \fn s32 CARD_Delete(s32 chn,const char *filename) +\brief Deletes a file with the given filename. Synchronous version. +\param[in] chn CARD slot +\param[in] filename name of the file to delete. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_Delete(s32 chn,const char *filename); + + +/*! \fn s32 CARD_DeleteAsync(s32 chn,const char *filename,cardcallback callback) +\brief Deletes a file with the given filename. This function returns immediately. Asynchronous version. +\param[in] chn CARD slot +\param[in] filename name of the file to delete. +\param[in] callback pointer to a callback function. This callback will be called when the delete process has finished. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_DeleteAsync(s32 chn,const char *filename,cardcallback callback); + + +/*! \fn s32 CARD_DeleteEntry(s32 chn,card_dir *dir_entry) +\brief Deletes a file with the given directory entry informations. +\param[in] chn CARD slot +\param[in] dir_entry pointer to the card_dir structure which holds the informations for the delete operation. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_DeleteEntry(s32 chn,card_dir *dir_entry); + + +/*! \fn s32 CARD_DeleteEntryAsync(s32 chn,card_dir *dir_entry,cardcallback callback) +\brief Deletes a file with the given directory entry informations. This function returns immediately. Asynchronous version. +\param[in] chn CARD slot +\param[in] dir_entry pointer to the card_dir structure which holds the informations for the delete operation. +\param[in] callback pointer to a callback function. This callback will be called when the delete process has finished. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_DeleteEntryAsync(s32 chn,card_dir *dir_entry,cardcallback callback); + + +/*! \fn s32 CARD_Write(card_file *file,void *buffer,u32 len,u32 offset) +\brief Writes the data to the file from the buffer to the given offset with the given length. Synchronous version +\param[in] file pointer to the card_file structure which holds the fileinformations. +\param[in] buffer pointer to the memory area to read from. The startaddress of the buffer should be aligned on a 32byte boundery. +\param[in] len length of data to write. +\param[in] offset starting point in the file to start writing. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_Write(card_file *file,void *buffer,u32 len,u32 offset); + + +/*! \fn s32 CARD_WriteAsync(card_file *file,void *buffer,u32 len,u32 offset,cardcallback callback) +\brief Writes the data to the file from the buffer to the given offset with the given length. This function returns immediately. Asynchronous version +\param[in] file pointer to the card_file structure which holds the fileinformations. +\param[in] buffer pointer to the memory area to read from. The startaddress of the buffer should be aligned on a 32byte boundery. +\param[in] len length of data to write. +\param[in] offset starting point in the file to start writing. +\param[in] callback pointer to a callback function. This callback will be called when the write process has finished. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_WriteAsync(card_file *file,void *buffer,u32 len,u32 offset,cardcallback callback); + + +/*! \fn s32 CARD_GetErrorCode(s32 chn) +\brief Returns the result code from the last operation. +\param[in] chn CARD slot + +\return \ref card_errors "card error codes" of last operation +*/ +s32 CARD_GetErrorCode(s32 chn); + + +/*! \fn s32 CARD_FindFirst(s32 chn, card_dir *dir, bool showall) +\brief Start to iterate thru the memory card's directory structure and returns the first directory entry. +\param[in] chn CARD slot +\param[out] dir pointer to card_dir structure to receive the result set. +\param[in] showall Whether to show all files of the memory card or only those which are identified by the company and gamecode string. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_FindFirst(s32 chn, card_dir *dir, bool showall); + + +/*! \fn s32 CARD_FindNext(card_dir *dir) +\brief Returns the next directory entry from the memory cards directory structure. +\param[out] dir pointer to card_dir structure to receive the result set. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_FindNext(card_dir *dir); + + +/*! \fn s32 CARD_GetDirectory(s32 chn, card_dir *dir_entries, s32 *count, bool showall) +\brief Returns the directory entries. size of entries is max. 128. +\param[in] chn CARD slot +\param[out] dir_entries pointer to card_dir structure to receive the result set. +\param[out] count pointer to an integer to receive the counted entries. +\param[in] showall Whether to show all files of the memory card or only those which are identified by the company and gamecode string. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_GetDirectory(s32 chn, card_dir *dir_entries, s32 *count, bool showall); + + +/*! \fn s32 CARD_GetSectorSize(s32 chn,u32 *sector_size) +\brief Returns the next directory entry from the memory cards directory structure. +\param[in] chn CARD slot. +\param[out] sector_size pointer to receive the result. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_GetSectorSize(s32 chn,u32 *sector_size); + + +/*! \fn s32 CARD_GetBlockCount(s32 chn,u32 *block_count) +\brief Returns the next directory entry from the memory cards directory structure. +\param[in] chn CARD slot. +\param[out] sector_size pointer to receive the result. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_GetBlockCount(s32 chn,u32 *block_count); + + +/*! \fn s32 CARD_GetStatus(s32 chn,s32 fileno,card_stat *stats) +\brief Get additional file statistic informations. +\param[in] chn CARD slot. +\param[in] fileno file index. returned by a previous call to CARD_Open(). +\param[out] stats pointer to receive the result set. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_GetStatus(s32 chn,s32 fileno,card_stat *stats); + + +/*! \fn s32 CARD_SetStatus(s32 chn,s32 fileno,card_stat *stats) +\brief Set additional file statistic informations. Synchronous version. +\param[in] chn CARD slot. +\param[in] fileno file index. returned by a previous call to CARD_Open(). +\param[out] stats pointer which holds the informations to set. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_SetStatus(s32 chn,s32 fileno,card_stat *stats); + + +/*! \fn s32 CARD_SetStatusAsync(s32 chn,s32 fileno,card_stat *stats,cardcallback callback) +\brief Set additional file statistic informations. This function returns immediately. Asynchronous version. +\param[in] chn CARD slot. +\param[in] fileno file index. returned by a previous call to CARD_Open(). +\param[out] stats pointer which holds the informations to set. +\param[in] callback pointer to a callback function. This callback will be called when the setstatus process has finished. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_SetStatusAsync(s32 chn,s32 fileno,card_stat *stats,cardcallback callback); + + +/*! \fn s32 CARD_GetAttributes(s32 chn,s32 fileno,u8 *attr) +\brief Get additional file attributes. Synchronous version. +\param[in] chn CARD slot. +\param[in] fileno file index. returned by a previous call to CARD_Open(). +\param[out] attr pointer to receive attribute value. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_GetAttributes(s32 chn,s32 fileno,u8 *attr); + + +/*! \fn s32 CARD_SetAttributes(s32 chn,s32 fileno,u8 attr) +\brief Set additional file attributes. Synchronous version. +\param[in] chn CARD slot. +\param[in] fileno file index. returned by a previous call to CARD_Open(). +\param[in] attr attribute value to set. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_SetAttributes(s32 chn,s32 fileno,u8 attr); + + +/*! \fn s32 CARD_SetAttributesAsync(s32 chn,s32 fileno,u8 attr,cardcallback callback) +\brief Set additional file attributes. This function returns immediately. Asynchronous version. +\param[in] chn CARD slot. +\param[in] fileno file index. returned by a previous call to CARD_Open(). +\param[in] attr attribute value to set. +\param[in] callback pointer to a callback function. This callback will be called when the setattributes process has finished. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_SetAttributesAsync(s32 chn,s32 fileno,u8 attr,cardcallback callback); + +/** + * Not finished function +*/ +s32 CARD_Format(s32 chn); +/** + * Not finished function +*/ +s32 CARD_FormatAsync(s32 chn,cardcallback callback); + + +/*! \fn s32 CARD_SetCompany(const char *company) +\brief Set additional file attributes. This function returns immediately. Asynchronous version. +\param[in] chn CARD slot. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_SetCompany(const char *company); + + +/*! \fn s32 CARD_SetGamecode(const char *gamecode) +\brief Set additional file attributes. This function returns immediately. Asynchronous version. +\param[in] chn CARD slot. + +\return \ref card_errors "card error codes" +*/ +s32 CARD_SetGamecode(const char *gamecode); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/cast.h b/wii/libogc/include/ogc/cast.h new file mode 100644 index 0000000000..49016dc3b4 --- /dev/null +++ b/wii/libogc/include/ogc/cast.h @@ -0,0 +1,239 @@ +#ifndef __CAST_H__ +#define __CAST_H__ + +#include + +#define GQR2 914 +#define GQR3 915 +#define GQR4 916 +#define GQR5 917 +#define GQR6 918 +#define GQR7 919 + +#define GQR_TYPE_F32 0 +#define GQR_TYPE_U8 4 +#define GQR_TYPE_U16 5 +#define GQR_TYPE_S8 6 +#define GQR_TYPE_S16 7 + +#define GQR_CAST_U8 2 +#define GQR_CAST_U16 3 +#define GQR_CAST_S8 4 +#define GQR_CAST_S16 5 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef GEKKO + +#define __set_gqr(_reg,_val) asm volatile("mtspr %0,%1" : : "i"(_reg), "b"(_val)) + +// does a default init +static inline void CAST_Init() +{ + __asm__ __volatile__ ( + "li 3,0x0004\n\ + oris 3,3,0x0004\n\ + mtspr 914,3\n\ + li 3,0x0005\n\ + oris 3,3,0x0005\n\ + mtspr 915,3\n\ + li 3,0x0006\n\ + oris 3,3,0x0006\n\ + mtspr 916,3\n\ + li 3,0x0007\n\ + oris 3,3,0x0007\n\ + mtspr 917,3\n" + : : : "r3" + ); +} + +static inline void CAST_SetGQR2(u32 type,u32 scale) +{ + register u32 val = (((((scale)<<8)|(type))<<16)|(((scale)<<8)|(type))); + __set_gqr(GQR2,val); +} + +static inline void CAST_SetGQR3(u32 type,u32 scale) +{ + register u32 val = (((((scale)<<8)|(type))<<16)|(((scale)<<8)|(type))); + __set_gqr(GQR3,val); +} + +static inline void CAST_SetGQR4(u32 type,u32 scale) +{ + register u32 val = (((((scale)<<8)|(type))<<16)|(((scale)<<8)|(type))); + __set_gqr(GQR4,val); +} + +static inline void CAST_SetGQR5(u32 type,u32 scale) +{ + register u32 val = (((((scale)<<8)|(type))<<16)|(((scale)<<8)|(type))); + __set_gqr(GQR5,val); +} + +static inline void CAST_SetGQR6(u32 type,u32 scale) +{ + register u32 val = (((((scale)<<8)|(type))<<16)|(((scale)<<8)|(type))); + __set_gqr(GQR6,val); +} + +static inline void CAST_SetGQR7(u32 type,u32 scale) +{ + register u32 val = (((((scale)<<8)|(type))<<16)|(((scale)<<8)|(type))); + __set_gqr(GQR7,val); +} + + +/******************************************************************/ +/* */ +/* cast from int to float */ +/* */ +/******************************************************************/ + +static inline f32 __castu8f32(register u8 *in) +{ + register f32 rval; + __asm__ __volatile__ ( + "psq_l %[rval],0(%[in]),1,2" : [rval]"=f"(rval) : [in]"r"(in) + ); + return rval; +} + +static inline f32 __castu16f32(register u16 *in) +{ + register f32 rval; + __asm__ __volatile__ ( + "psq_l %[rval],0(%[in]),1,3" : [rval]"=f"(rval) : [in]"r"(in) + ); + return rval; +} + +static inline f32 __casts8f32(register s8 *in) +{ + register f32 rval; + __asm__ __volatile__ ( + "psq_l %[rval],0(%[in]),1,4" : [rval]"=f"(rval) : [in]"r"(in) + ); + return rval; +} + +static inline f32 __casts16f32(register s16 *in) +{ + register f32 rval; + __asm__ __volatile__ ( + "psq_l %[rval],0(%[in]),1,5" : [rval]"=f"(rval) : [in]"r"(in) + ); + return rval; +} + +static inline void castu8f32(register u8 *in,register volatile f32 *out) +{ + *out = __castu8f32(in); +} + +static inline void castu16f32(register u16 *in,register volatile f32 *out) +{ + *out = __castu16f32(in); +} + +static inline void casts8f32(register s8 *in,register volatile f32 *out) +{ + *out = __casts8f32(in); +} + +static inline void casts16f32(register s16 *in,register volatile f32 *out) +{ + *out = __casts16f32(in); +} + +/******************************************************************/ +/* */ +/* cast from float to int */ +/* */ +/******************************************************************/ + +static inline u8 __castf32u8(register f32 in) +{ + f32 a; + register u8 rval; + register f32 *ptr = &a; + + __asm__ __volatile__ ( + "psq_st %[in],0(%[ptr]),1,2\n" + "lbz %[out],0(%[ptr])\n" + : [out]"=r"(rval), [ptr]"+r"(ptr) : [in]"f"(in) + ); + return rval; +} + +static inline u16 __castf32u16(register f32 in) +{ + f32 a; + register u16 rval; + register f32 *ptr = &a; + + __asm__ __volatile__ ( + "psq_st %[in],0(%[ptr]),1,3\n" + "lhz %[out],0(%[ptr])\n" + : [out]"=r"(rval), [ptr]"+r"(ptr) : [in]"f"(in) + ); + return rval; +} + +static inline s8 __castf32s8(register f32 in) +{ + f32 a; + register s8 rval; + register f32 *ptr = &a; + + __asm__ __volatile__ ( + "psq_st %[in],0(%[ptr]),1,4\n" + "lbz %[out],0(%[ptr])\n" + : [out]"=r"(rval), [ptr]"+r"(ptr) : [in]"f"(in) + ); + return rval; +} + +static inline s16 __castf32s16(register f32 in) +{ + f32 a; + register s16 rval; + register f32 *ptr = &a; + + __asm__ __volatile__ ( + "psq_st %[in],0(%[ptr]),1,5\n" + "lha %[out],0(%[ptr])\n" + : [out]"=r"(rval), [ptr]"+r"(ptr) : [in]"f"(in) + ); + return rval; +} + +static inline void castf32u8(register f32 *in,register vu8 *out) +{ + *out = __castf32u8(*in); +} + +static inline void castf32u16(register f32 *in,register vu16 *out) +{ + *out = __castf32u16(*in); +} + +static inline void castf32s8(register f32 *in,register vs8 *out) +{ + *out = __castf32s8(*in); +} + +static inline void castf32s16(register f32 *in,register vs16 *out) +{ + *out = __castf32s16(*in); +} + +#endif //GEKKO + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wii/libogc/include/ogc/color.h b/wii/libogc/include/ogc/color.h new file mode 100644 index 0000000000..cb5cfef48c --- /dev/null +++ b/wii/libogc/include/ogc/color.h @@ -0,0 +1,28 @@ +#ifndef __COLOR_H__ +#define __COLOR_H__ + +// luminance is stored twice, thus one of the lum. is +// redundant, but this way we can fill the screen. + +#define COLOR_BLACK (0x00800080) +#define COLOR_MAROON (0x266A26C0) +#define COLOR_GREEN (0x4B554B4A) +#define COLOR_OLIVE (0x7140718A) +#define COLOR_NAVY (0x0EC00E75) +#define COLOR_PURPLE (0x34AA34B5) +#define COLOR_TEAL (0x59955940) +#define COLOR_GRAY (0x80808080) +#define COLOR_SILVER (0xC080C080) +#define COLOR_RED (0x4C544CFF) +#define COLOR_LIME (0x952B9515) +#define COLOR_YELLOW (0xE100E194) +#define COLOR_BLUE (0x1DFF1D6B) +#define COLOR_FUCHSIA (0x69D469EA) +#define COLOR_AQUA (0xB2ABB200) +#define COLOR_WHITE (0xFF80FF80) +#define COLOR_MONEYGREEN (0xD076D074) +#define COLOR_SKYBLUE (0xC399C36A) +#define COLOR_CREAM (0xFA79FA82) +#define COLOR_MEDGRAY (0xA082A07F) + +#endif /* COLOR_H */ diff --git a/wii/libogc/include/ogc/cond.h b/wii/libogc/include/ogc/cond.h new file mode 100644 index 0000000000..ce38e61996 --- /dev/null +++ b/wii/libogc/include/ogc/cond.h @@ -0,0 +1,115 @@ +/*------------------------------------------------------------- + +cond.h -- Thread subsystem V + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#ifndef __COND_H__ +#define __COND_H__ + +/*! \file cond.h +\brief Thread subsystem V + +*/ + +#include +#include + +#define LWP_COND_NULL 0xffffffff + +#ifdef __cplusplus +extern "C" { +#endif + + +/*! \typedef u32 cond_t +\brief typedef for the condition variable handle +*/ +typedef u32 cond_t; + + +/*! \fn s32 LWP_CondInit(cond_t *cond) +\brief Initialize condition variable +\param[out] cond pointer to the cond_t handle + +\return 0 on success, <0 on error +*/ +s32 LWP_CondInit(cond_t *cond); + + +/*! \fn s32 LWP_CondWait(cond_t cond,mutex_t mutex) +\brief Wait on condition variable. +\param[in] cond handle to the cond_t structure +\param[in] mutex handle to the mutex_t structure + +\return 0 on success, <0 on error +*/ +s32 LWP_CondWait(cond_t cond,mutex_t mutex); + + +/*! \fn s32 LWP_CondSignal(cond_t cond) +\brief Signal a specific thread waiting on this condition variable to wake up. +\param[in] cond handle to the cond_t structure + +\return 0 on success, <0 on error +*/ +s32 LWP_CondSignal(cond_t cond); + + +/*! \fn s32 LWP_CondBroadcast(cond_t cond) +\brief Broadcast all threads waiting on this condition variable to wake up. +\param[in] cond handle to the cond_t structure + +\return 0 on success, <0 on error +*/ +s32 LWP_CondBroadcast(cond_t cond); + + +/*! \fn s32 LWP_CondTimedWait(cond_t cond,mutex_t mutex,const struct timespec *abstime) +\brief Timed wait on a conditionvariable. +\param[in] cond handle to the cond_t structure +\param[in] mutex handle to the mutex_t structure +\param[in] abstime pointer to a timespec structure holding the abs time for the timeout. + +\return 0 on success, <0 on error +*/ +s32 LWP_CondTimedWait(cond_t cond,mutex_t mutex,const struct timespec *abstime); + + +/*! \fn s32 LWP_CondDestroy(cond_t cond) +\brief Destroy condition variable, release all threads and handles blocked on that condition variable. +\param[in] cond handle to the cond_t structure + +\return 0 on success, <0 on error +*/ +s32 LWP_CondDestroy(cond_t cond); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/conf.h b/wii/libogc/include/ogc/conf.h new file mode 100644 index 0000000000..4cdf2962a7 --- /dev/null +++ b/wii/libogc/include/ogc/conf.h @@ -0,0 +1,183 @@ +/*------------------------------------------------------------- + +conf.h -- SYSCONF support + +Copyright (C) 2008 +Hector Martin (marcan) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#ifndef __CONF_H__ +#define __CONF_H__ + +#if defined(HW_RVL) + +#include +#include + +#define CONF_EBADFILE -0x6001 +#define CONF_ENOENT -0x6002 +#define CONF_ETOOBIG -0x6003 +#define CONF_ENOTINIT -0x6004 +#define CONF_ENOTIMPL -0x6005 +#define CONF_EBADVALUE -0x6006 +#define CONF_ENOMEM -0x6007 +#define CONF_ERR_OK 0 + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +enum { + CONF_BIGARRAY = 1, + CONF_SMALLARRAY, + CONF_BYTE, + CONF_SHORT, + CONF_LONG, + CONF_BOOL = 7 +}; + +enum { + CONF_VIDEO_NTSC = 0, + CONF_VIDEO_PAL, + CONF_VIDEO_MPAL +}; + +enum { + CONF_REGION_JP = 0, + CONF_REGION_US = 1, + CONF_REGION_EU = 2, + CONF_REGION_KR = 4, + CONF_REGION_CN = 5 +}; + +enum { + CONF_AREA_JPN = 0, + CONF_AREA_USA, + CONF_AREA_EUR, + CONF_AREA_AUS, + CONF_AREA_BRA, + CONF_AREA_TWN, + CONF_AREA_ROC, + CONF_AREA_KOR, + CONF_AREA_HKG, + CONF_AREA_ASI, + CONF_AREA_LTN, + CONF_AREA_SAF, + CONF_AREA_CHN +}; + +enum { + CONF_SHUTDOWN_STANDBY = 0, + CONF_SHUTDOWN_IDLE +}; + +enum { + CONF_LED_OFF = 0, + CONF_LED_DIM, + CONF_LED_BRIGHT +}; + +enum { + CONF_SOUND_MONO = 0, + CONF_SOUND_STEREO, + CONF_SOUND_SURROUND +}; + +enum { + CONF_LANG_JAPANESE = 0, + CONF_LANG_ENGLISH, + CONF_LANG_GERMAN, + CONF_LANG_FRENCH, + CONF_LANG_SPANISH, + CONF_LANG_ITALIAN, + CONF_LANG_DUTCH, + CONF_LANG_SIMP_CHINESE, + CONF_LANG_TRAD_CHINESE, + CONF_LANG_KOREAN +}; + +enum { + CONF_ASPECT_4_3 = 0, + CONF_ASPECT_16_9 +}; + +enum { + CONF_SENSORBAR_BOTTOM = 0, + CONF_SENSORBAR_TOP +}; + +#define CONF_PAD_MAX_REGISTERED 10 +#define CONF_PAD_MAX_ACTIVE 4 + +typedef struct _conf_pad_device conf_pad_device; + +struct _conf_pad_device { + u8 bdaddr[6]; + char name[0x40]; +} ATTRIBUTE_PACKED; + +typedef struct _conf_pads conf_pads; + +struct _conf_pads { + u8 num_registered; + conf_pad_device registered[CONF_PAD_MAX_REGISTERED]; + conf_pad_device active[CONF_PAD_MAX_ACTIVE]; + conf_pad_device balance_board; + conf_pad_device unknown; +} ATTRIBUTE_PACKED; + +s32 CONF_Init(void); +s32 CONF_GetLength(const char *name); +s32 CONF_GetType(const char *name); +s32 CONF_Get(const char *name, void *buffer, u32 length); +s32 CONF_GetShutdownMode(void); +s32 CONF_GetIdleLedMode(void); +s32 CONF_GetProgressiveScan(void); +s32 CONF_GetEuRGB60(void); +s32 CONF_GetIRSensitivity(void); +s32 CONF_GetSensorBarPosition(void); +s32 CONF_GetPadSpeakerVolume(void); +s32 CONF_GetPadMotorMode(void); +s32 CONF_GetSoundMode(void); +s32 CONF_GetLanguage(void); +s32 CONF_GetCounterBias(u32 *bias); +s32 CONF_GetScreenSaverMode(void); +s32 CONF_GetDisplayOffsetH(s8 *offset); +s32 CONF_GetPadDevices(conf_pads *pads); +s32 CONF_GetNickName(u8 *nickname); +s32 CONF_GetAspectRatio(void); +s32 CONF_GetEULA(void); +s32 CONF_GetParentalPassword(s8 *password); +s32 CONF_GetParentalAnswer(s8 *answer); +s32 CONF_GetWiiConnect24(void); +s32 CONF_GetRegion(void); +s32 CONF_GetArea(void); +s32 CONF_GetVideo(void); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif + +#endif diff --git a/wii/libogc/include/ogc/consol.h b/wii/libogc/include/ogc/consol.h new file mode 100644 index 0000000000..294f7e6b11 --- /dev/null +++ b/wii/libogc/include/ogc/consol.h @@ -0,0 +1,81 @@ +#ifndef __CONSOL_H__ +#define __CONSOL_H__ + +/*! + * \file consol.h + * \brief Console subsystem + * + */ + +#include "gx_struct.h" + +/* macros to support old function names */ +#define console_init CON_Init +#define SYS_ConsoleInit CON_InitEx + +#ifdef __cplusplus + extern "C" { +#endif + +/*! + * \fn CON_Init(void *framebuffer,int xstart,int ystart,int xres,int yres,int stride) + * \brief Initializes the console subsystem with given parameters + * + * \param[in] framebuffer pointer to the framebuffer used for drawing the characters + * \param[in] xstart,ystart start position of the console output in pixel + * \param[in] xres,yres size of the console in pixel + * \param[in] stride size of one line of the framebuffer in bytes + * + * \return none + */ +void CON_Init(void *framebuffer,int xstart,int ystart,int xres,int yres,int stride); + +/*! + * \fn s32 CON_InitEx(GXRModeObj *rmode, s32 conXOrigin,s32 conYOrigin,s32 conWidth,s32 conHeight) + * \brief Initialize stdout console + * \param[in] rmode pointer to the video/render mode configuration + * \param[in] conXOrigin starting pixel in X direction of the console output on the external framebuffer + * \param[in] conYOrigin starting pixel in Y direction of the console output on the external framebuffer + * \param[in] conWidth width of the console output 'window' to be drawn + * \param[in] conHeight height of the console output 'window' to be drawn + * + * \return 0 on success, <0 on error + */ +s32 CON_InitEx(GXRModeObj *rmode, s32 conXOrigin,s32 conYOrigin,s32 conWidth,s32 conHeight); + +/*! + * \fn CON_GetMetrics(int *cols, int *rows) + * \brief retrieve the columns and rows of the current console + * + * \param[out] cols,rows number of columns and rows of the current console + * + * \return none + */ +void CON_GetMetrics(int *cols, int *rows); + +/*! + * \fn CON_GetPosition(int *col, int *row) + * \brief retrieve the current cursor position of the current console + * + * \param[out] col,row current cursor position + * + * \return none + */ +void CON_GetPosition(int *cols, int *rows); + +/*! + * \fn CON_EnableGecko(int channel, int safe) + * \brief Enable or disable the USB gecko console. + * + * \param[in] channel EXI channel, or -1 ¨to disable the gecko console + * \param[in] safe If true, use safe mode (wait for peer) + * + * \return none + */ +void CON_EnableGecko(int channel,int safe); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/context.h b/wii/libogc/include/ogc/context.h new file mode 100644 index 0000000000..7814bb5db5 --- /dev/null +++ b/wii/libogc/include/ogc/context.h @@ -0,0 +1,51 @@ +#ifndef __EXCONTEXT_H__ +#define __EXCONTEXT_H__ + +#define NUM_EXCEPTIONS 15 + +#define EX_SYS_RESET 0 +#define EX_MACH_CHECK 1 +#define EX_DSI 2 +#define EX_ISI 3 +#define EX_INT 4 +#define EX_ALIGN 5 +#define EX_PRG 6 +#define EX_FP 7 +#define EX_DEC 8 +#define EX_SYS_CALL 9 +#define EX_TRACE 10 +#define EX_PERF 11 +#define EX_IABR 12 +#define EX_RESV 13 +#define EX_THERM 14 + +#ifndef _LANGUAGE_ASSEMBLY + +#include + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef struct _excption_frame { + u32 EXCPT_Number; + u32 SRR0,SRR1; + u32 GPR[32]; + u32 GQR[8]; + u32 CR, LR, CTR, XER, MSR, DAR; + + u16 state; //used to determine whether to restore the fpu context or not + u16 mode; //unused + + f64 FPR[32]; + u64 FPSCR; + f64 PSFPR[32]; +} frame_context; + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif //!_LANGUAGE_ASSEMBLY + +#endif diff --git a/wii/libogc/include/ogc/disc_io.h b/wii/libogc/include/ogc/disc_io.h new file mode 100644 index 0000000000..0a6ef3e5a4 --- /dev/null +++ b/wii/libogc/include/ogc/disc_io.h @@ -0,0 +1,68 @@ +/* + disc_io.h + Interface template for low level disc functions. + + Copyright (c) 2006 Michael "Chishm" Chisholm + Based on code originally written by MightyMax + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef OGC_DISC_IO_INCLUDE +#define OGC_DISC_IO_INCLUDE + +#include +#include + + +#define FEATURE_MEDIUM_CANREAD 0x00000001 +#define FEATURE_MEDIUM_CANWRITE 0x00000002 +#define FEATURE_GAMECUBE_SLOTA 0x00000010 +#define FEATURE_GAMECUBE_SLOTB 0x00000020 +#define FEATURE_GAMECUBE_DVD 0x00000040 +#define FEATURE_WII_SD 0x00000100 +#define FEATURE_WII_USB 0x00000200 +#define FEATURE_WII_DVD 0x00000400 + +typedef uint32_t sec_t; + +typedef bool (* FN_MEDIUM_STARTUP)(void) ; +typedef bool (* FN_MEDIUM_ISINSERTED)(void) ; +typedef bool (* FN_MEDIUM_READSECTORS)(sec_t sector, sec_t numSectors, void* buffer) ; +typedef bool (* FN_MEDIUM_WRITESECTORS)(sec_t sector, sec_t numSectors, const void* buffer) ; +typedef bool (* FN_MEDIUM_CLEARSTATUS)(void) ; +typedef bool (* FN_MEDIUM_SHUTDOWN)(void) ; + +struct DISC_INTERFACE_STRUCT { + unsigned long ioType ; + unsigned long features ; + FN_MEDIUM_STARTUP startup ; + FN_MEDIUM_ISINSERTED isInserted ; + FN_MEDIUM_READSECTORS readSectors ; + FN_MEDIUM_WRITESECTORS writeSectors ; + FN_MEDIUM_CLEARSTATUS clearStatus ; + FN_MEDIUM_SHUTDOWN shutdown ; +} ; + +typedef struct DISC_INTERFACE_STRUCT DISC_INTERFACE ; + +#endif // define OGC_DISC_IO_INCLUDE diff --git a/wii/libogc/include/ogc/dsp.h b/wii/libogc/include/ogc/dsp.h new file mode 100644 index 0000000000..eba38aeefe --- /dev/null +++ b/wii/libogc/include/ogc/dsp.h @@ -0,0 +1,228 @@ +/*------------------------------------------------------------- + +dsp.h -- DSP subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#ifndef __DSP_H__ +#define __DSP_H__ + +/*! \file dsp.h +\brief DSP subsystem + +*/ + +#include + +/*! + * \addtogroup dsp_taskstate DSP task states + * \brief DSP task state indicating DSP task's current operation + * @{ + */ + +#define DSPTASK_INIT 0 /*!< DSP task is initializing */ +#define DSPTASK_RUN 1 /*!< DSP task is running */ +#define DSPTASK_YIELD 2 /*!< DSP task has yield */ +#define DSPTASK_DONE 3 /*!< DSP task is done/ready */ + +/*! + * @} + */ + + +/*! + * \addtogroup dsp_taskflag DSP task flags + * \brief DSP task queue state flag indicating the task's current queue state. Multiple states are OR'd. + * @{ + */ + +#define DSPTASK_CLEARALL 0x00000000 /*!< DSP task emtpy/ready */ +#define DSPTASK_ATTACH 0x00000001 /*!< DSP task attached */ +#define DSPTASK_CANCEL 0x00000002 /*!< DSP task canceled */ + +/*! + * @} + */ + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + +/*! +\typedef struct _dsp_task dsptask_t +\brief forward typdef to struct _dsp_task. This struture holds certain DSP task information for execution. +*/ +typedef struct _dsp_task dsptask_t; + + +/*! \typedef void (*DSPTaskCallback)(dsptask_t *task) +\brief function pointer typedef for the user's DSP task callbacks +\param[in] task pointer to the dsp_task structure. +*/ +typedef void (*DSPTaskCallback)(dsptask_t *task); + + +/*! \typedef void (*DSPCallback)(void) +\brief function pointer typedef for the user's DSP interrupt callback +*/ +typedef void (*DSPCallback)(void); + + +/*! \typedef struct _dsp_task dsptask_t +\param state current task \ref dsp_taskstate "state" set +\param prio priority of the task +\param flags currnet task \ref dsp_taskflag "flag(s)" set. +\param init_vec initialization vector. depends on the DSP code to execute. +\param resume_vec resume vector. depends on the DSP code to execute. +\param iram_maddr main memory address of i-ram image. NOTE: Has to be aligned on a 32byte boundery! +\param iram_len size of i-ram image. NOTE: Should be a multiple of 32 +\param iram_addr DSP i-ram address to load the image to. +\param dram_maddr main memory address of d-ram image. NOTE: Has to be aligned on a 32byte boundery! +\param dram_len size of d-ram image. NOTE: Should be a multiple of 32 +\param dram_addr DSP d-ram address to load the image to. +\param init_cb pointer to the user's init callback function. Called durring task initialization. +\param res_cb pointer to the user's resume callback function. Called when the task should resume. +\param done_cb pointer to the user's done callback function. Called when the task has finished. +\param req_cb pointer to the user's request callback function. Used to retrieve data from main application durring execution. +\param next pointer to the next task in the doubly linked list. +\param prev pointer to the previous task in the doubly linked list. +*/ +struct _dsp_task { + vu32 state; + vu32 prio; + vu32 flags; + + void *iram_maddr; + u32 iram_len; + u32 iram_addr; + + void *dram_maddr; + u32 dram_len; + u32 dram_addr; + + u16 init_vec; + u16 resume_vec; + + DSPTaskCallback init_cb; + DSPTaskCallback res_cb; + DSPTaskCallback done_cb; + DSPTaskCallback req_cb; + + struct _dsp_task *next; + struct _dsp_task *prev; +}; + + +/*! \fn void DSP_Init() +\brief Initialize DSP subsystem. + +\return none +*/ +void DSP_Init(); + + +/*! \fn u32 DSP_CheckMailTo() +\brief Check if mail was sent to DSP + +\return 1: mail sent, 0: mail on route +*/ +u32 DSP_CheckMailTo(); + + +/*! \fn u32 DSP_CheckMailFrom() +\brief Check for mail from DSP + +\return 1: has mail, 0: no mail +*/ +u32 DSP_CheckMailFrom(); + + +/*! \fn u32 DSP_ReadMailFrom() +\brief Read mail from DSP + +\return mail value received +*/ +u32 DSP_ReadMailFrom(); + + +/*! \fn void DSP_AssertInt() +\brief Asserts the processor interface interrupt + +\return none +*/ +void DSP_AssertInt(); + + +/*! \fn void DSP_SendMailTo(u32 mail) +\brief Send mail to DSP +\param[in] mail value to send + +\return none +*/ +void DSP_SendMailTo(u32 mail); + + +/*! \fn u32 DSP_ReadCPUtoDSP() +\brief Read back CPU->DSP mailbox + +\return mail value received +*/ +u32 DSP_ReadCPUtoDSP(); + + +/*! \fn dsptask_t* DSP_AddTask(dsptask_t *task) +\brief Add a DSP task to the tasklist and start executing if necessary. +\param[in] task pointer to a dsptask_t structure which holds all necessary values for DSP task execution. + +\return current task +*/ +dsptask_t* DSP_AddTask(dsptask_t *task); + +dsptask_t* DSP_AssertTask(dsptask_t *task); + +void DSP_CancelTask(dsptask_t *task); + +void DSP_Reset(); + +void DSP_Halt(); + +void DSP_Unhalt(); + +/*! \fn DSPCallback DSP_RegisterCallback(DSPCallback usr_cb) +\brief Register an user's interrupt callback. This may be used to handle DSP interrupts on its own. By default a system default callback is installed on DSP_Init(). +\param[in] user_cb pointer to the user's interrupt callback function. +\ +\return pointer to old interrupt callback function. +*/ +DSPCallback DSP_RegisterCallback(DSPCallback usr_cb); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/dvd.h b/wii/libogc/include/ogc/dvd.h new file mode 100644 index 0000000000..b9e4c6369a --- /dev/null +++ b/wii/libogc/include/ogc/dvd.h @@ -0,0 +1,369 @@ +/*------------------------------------------------------------- + +dvd.h -- DVD subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#ifndef __DVD_H__ +#define __DVD_H__ + +/*! + * \file dvd.h + * \brief DVD subsystem + * + */ + +#include +#include +#include + +/*! + * \addtogroup dvd_statecodes DVD state codes + * @{ + */ + +#define DVD_STATE_FATAL_ERROR -1 +#define DVD_STATE_END 0 +#define DVD_STATE_BUSY 1 +#define DVD_STATE_WAITING 2 +#define DVD_STATE_COVER_CLOSED 3 +#define DVD_STATE_NO_DISK 4 +#define DVD_STATE_COVER_OPEN 5 +#define DVD_STATE_WRONG_DISK 6 +#define DVD_STATE_MOTOR_STOPPED 7 +#define DVD_STATE_IGNORED 8 +#define DVD_STATE_CANCELED 10 +#define DVD_STATE_RETRY 11 + +#define DVD_ERROR_OK 0 +#define DVD_ERROR_FATAL -1 +#define DVD_ERROR_IGNORED -2 +#define DVD_ERROR_CANCELED -3 +#define DVD_ERROR_COVER_CLOSED -4 + +/*! + * @} + */ + + +/*! + * \addtogroup dvd_resetmode DVD reset modes + * @{ + */ + +#define DVD_RESETHARD 0 /*!< Performs a hard reset. Complete new boot of FW. */ +#define DVD_RESETSOFT 1 /*!< Performs a soft reset. FW restart and drive spinup */ +#define DVD_RESETNONE 2 /*!< Only initiate DI registers */ + +/*! + * @} + */ + + +/*! + * \addtogroup dvd_motorctrlmode DVD motor control modes + * @{ + */ + +#define DVD_SPINMOTOR_DOWN 0x00000000 /*!< Stop DVD drive */ +#define DVD_SPINMOTOR_UP 0x00000100 /*!< Start DVD drive */ +#define DVD_SPINMOTOR_ACCEPT 0x00004000 /*!< Force DVD to accept the disk */ +#define DVD_SPINMOTOR_CHECKDISK 0x00008000 /*!< Force DVD to perform a disc check */ + +/*! + * @} + */ + + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + +/*! + * \typedef struct _dvddiskid dvddiskid + * \brief forward typedef for struct _dvddiskid + */ +typedef struct _dvddiskid dvddiskid; + +/*! + * \typedef struct _dvddiskid dvddiskid + * + * This structure holds the game vendors copyright informations.
+ * Additionally it holds certain parameters for audiocontrol and
+ * multidisc support. + * + * \param gamename[4] vendors game key + * \param company[2] vendors company key + * \param disknum number of disc when multidisc support is used. + * \param gamever version of game + * \param streaming flag to control audio streaming + * \param streambufsize size of buffer used for audio streaming + * \param pad[22] padding + */ +struct _dvddiskid { + s8 gamename[4]; + s8 company[2]; + u8 disknum; + u8 gamever; + u8 streaming; + u8 streambufsize; + u8 pad[22]; +}; + +/*! + * \typedef struct _dvdcmdblk dvdcmdblk + * \brief forward typedef for struct _dvdcmdblk + */ +typedef struct _dvdcmdblk dvdcmdblk; + + +/*! + * \typedef void (*dvdcbcallback)(s32 result,dvdcmdblk *block) + * \brief function pointer typedef for the user's operations callback + */ +typedef void (*dvdcbcallback)(s32 result,dvdcmdblk *block); + + +/*! + * \typedef struct _dvdcmdblk dvdcmdblk + * + * This structure is used internally to control the requested operation. + */ +struct _dvdcmdblk { + lwp_node node; + u32 cmd; + s32 state; + s64 offset; + u32 len; + void *buf; + u32 currtxsize; + u32 txdsize; + dvddiskid *id; + dvdcbcallback cb; + void *usrdata; +}; + + +/*! + * \typedef struct _dvddrvinfo dvddrvinfo + * \brief forward typedef for struct _dvddrvinfo + */ +typedef struct _dvddrvinfo dvddrvinfo; + + +/*! + * \typedef struct _dvddrvinfo dvddrvinfo + * + * This structure structure holds the drive version infromation.
+ * Use DVD_Inquiry() to retrieve this information. + * + * \param rev_leve revision level + * \param dev_code device code + * \param rel_date release date + * \param pad[24] padding + */ +struct _dvddrvinfo { + u16 rev_level; + u16 dev_code; + u32 rel_date; + u8 pad[24]; +}; + + +/*! + * \typedef struct _dvdfileinfo dvdfileinfo + * \brief forward typedef for struct _dvdfileinfo + */ +typedef struct _dvdfileinfo dvdfileinfo; + + +/*! + * \typedef void (*dvdcallback)(s32 result,dvdfileinfo *info) + * \brief function pointer typedef for the user's DVD operation callback + * + * \param[in] result error code of last operation + * \param[in] info pointer to user's file info strucutre + */ +typedef void (*dvdcallback)(s32 result,dvdfileinfo *info); + + +/*! + * \typedef struct _dvdfileinfo dvdfileinfo + * + * This structure is used internally to control the requested file operation. + */ +struct _dvdfileinfo { + dvdcmdblk block; + u32 addr; + u32 len; + dvdcallback cb; +}; + + +/*! + * \fn void DVD_Init() + * \brief Initializes the DVD subsystem + * + * You must call this function before calling any other DVD function + * + * \return none + */ +void DVD_Init(); +void DVD_Pause(); + + +/*! + * \fn void DVD_Reset(u32 reset_mode) + * \brief Performs a reset of the drive and FW respectively. + * + * \param[in] reset_mode \ref dvd_resetmode "type" of reset + * + * \return none + */ +void DVD_Reset(u32 reset_mode); + + +/*! + * \fn s32 DVD_Mount() + * \brief Mounts the DVD drive. + * + * This is a synchronous version of DVD_MountAsync(). + * + * \return none + */ +s32 DVD_Mount(); +s32 DVD_GetDriveStatus(); + + +/*! + * \fn s32 DVD_MountAsync(dvdcmdblk *block,dvdcbcallback cb) + * \brief Mounts the DVD drive. + * + * You must call this function in order to access the DVD. + * + * Following tasks are performed: + * - Issue a hard reset to the drive. + * - Turn on drive's debug mode. + * - Patch drive's FW. + * - Enable extensions. + * - Read disc ID + * + * The patch code and procedure was taken from the gc-linux DVD device driver. + * + * \param[in] block pointer to a dvdcmdblk structure used to process the operation + * \param[in] cb callback to be invoked upon completion of operation + * + * \return none + */ +s32 DVD_MountAsync(dvdcmdblk *block,dvdcbcallback cb); + + +/*! + * \fn s32 DVD_ControlDrive(dvdcmdblk *block,u32 cmd) + * \brief Controls the drive's motor and behavior. + * + * This is a synchronous version of DVD_ControlDriveAsync(). + * + * \param[in] block pointer to a dvdcmdblk structure used to process the operation + * \param[in] cmd \ref dvd_motorctrlmode "command" to control the drive. + * + * \return none + */ +s32 DVD_ControlDrive(dvdcmdblk *block,u32 cmd); + + +/*! + * \fn s32 DVD_ControlDriveAsync(dvdcmdblk *block,u32 cmd,dvdcbcallback cb) + * \brief Controls the drive's motor and behavior. + * + * \param[in] block pointer to a dvdcmdblk structure used to process the operation + * \param[in] cmd \ref dvd_motorctrlmode "command" to control the drive. + * \param[in] cb callback to be invoked upon completion of operation. + * + * \return none + */ +s32 DVD_ControlDriveAsync(dvdcmdblk *block,u32 cmd,dvdcbcallback cb); + + +/*! + * \fn s32 DVD_SetGCMOffset(dvdcmdblk *block,u32 offset) + * \brief Sets the offset to the GCM. Used for multigame discs. + * + * This is a synchronous version of DVD_SetGCMOffsetAsync(). + * + * \param[in] block pointer to a dvdcmdblk structure used to process the operation + * \param[in] offset offset to the GCM on disc. + * + * \return \ref dvd_errorcodes "dvd error code" + */ +s32 DVD_SetGCMOffset(dvdcmdblk *block,s64 offset); + + +/*! + * \fn s32 DVD_SetGCMOffsetAsync(dvdcmdblk *block,u32 offset,dvdcbcallback cb) + * \brief Sets the offset to the GCM. Used for multigame discs. + * + * This is a synchronous version of DVD_SetGCMOffsetAsync(). + * + * \param[in] block pointer to a dvdcmdblk structure used to process the operation + * \param[in] offset offset to the GCM on disc. + * \param[in] cb callback to be invoked upon completion of operation. + * + * \return \ref dvd_errorcodes "dvd error code" + */ +s32 DVD_SetGCMOffsetAsync(dvdcmdblk *block,s64 offset,dvdcbcallback cb); + +s32 DVD_GetCmdBlockStatus(dvdcmdblk *block); +s32 DVD_SpinUpDrive(dvdcmdblk *block); +s32 DVD_SpinUpDriveAsync(dvdcmdblk *block,dvdcbcallback cb); +s32 DVD_Inquiry(dvdcmdblk *block,dvddrvinfo *info); +s32 DVD_InquiryAsync(dvdcmdblk *block,dvddrvinfo *info,dvdcbcallback cb); +s32 DVD_ReadPrio(dvdcmdblk *block,void *buf,u32 len,s64 offset,s32 prio); +s32 DVD_ReadAbsAsyncPrio(dvdcmdblk *block,void *buf,u32 len,s64 offset,dvdcbcallback cb,s32 prio); +s32 DVD_ReadAbsAsyncForBS(dvdcmdblk *block,void *buf,u32 len,s64 offset,dvdcbcallback cb); +s32 DVD_SeekPrio(dvdcmdblk *block,s64 offset,s32 prio); +s32 DVD_SeekAbsAsyncPrio(dvdcmdblk *block,s64 offset,dvdcbcallback cb,s32 prio); +s32 DVD_CancelAllAsync(dvdcbcallback cb); +s32 DVD_StopStreamAtEndAsync(dvdcmdblk *block,dvdcbcallback cb); +s32 DVD_StopStreamAtEnd(dvdcmdblk *block); +s32 DVD_ReadDiskID(dvdcmdblk *block,dvddiskid *id,dvdcbcallback cb); +u32 DVD_SetAutoInvalidation(u32 auto_inv); +dvddiskid* DVD_GetCurrentDiskID(); +dvddrvinfo* DVD_GetDriveInfo(); + +#define DVD_SetUserData(block, data) ((block)->usrdata = (data)) +#define DVD_GetUserData(block) ((block)->usrdata) + +#define DEVICE_TYPE_GAMECUBE_DVD (('G'<<24)|('D'<<16)|('V'<<8)|'D') +extern const DISC_INTERFACE __io_gcdvd; + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/es.h b/wii/libogc/include/ogc/es.h new file mode 100644 index 0000000000..c675ed800c --- /dev/null +++ b/wii/libogc/include/ogc/es.h @@ -0,0 +1,309 @@ +/*------------------------------------------------------------- + +es.h -- tik services + +Copyright (C) 2008 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) +Hector Martin (marcan) +Andre Heider (dhewg) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#ifndef __ES_H__ +#define __ES_H__ + +#if defined(HW_RVL) + +#include +#include + +#define ES_EINVAL -0x1004 +#define ES_ENOMEM -0x100C +#define ES_ENOTINIT -0x1100 +#define ES_EALIGN -0x1101 + +#define ES_SIG_RSA4096 0x10000 +#define ES_SIG_RSA2048 0x10001 +#define ES_SIG_ECDSA 0x10002 + +#define ES_CERT_RSA4096 0 +#define ES_CERT_RSA2048 1 +#define ES_CERT_ECDSA 2 + +#define ES_KEY_COMMON 4 +#define ES_KEY_SDCARD 6 + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef u32 sigtype; +typedef sigtype sig_header; +typedef sig_header signed_blob; + +typedef u8 sha1[20]; +typedef u8 aeskey[16]; + +typedef struct _sig_rsa2048 { + sigtype type; + u8 sig[256]; + u8 fill[60]; +} __attribute__((packed)) sig_rsa2048; + +typedef struct _sig_rsa4096 { + sigtype type; + u8 sig[512]; + u8 fill[60]; +} __attribute__((packed)) sig_rsa4096; + +typedef struct _sig_ecdsa { + sigtype type; + u8 sig[60]; + u8 fill[64]; +} __attribute__((packed)) sig_ecdsa; + +typedef char sig_issuer[0x40]; + +typedef struct _tiklimit { + u32 tag; + u32 value; +} __attribute__((packed)) tiklimit; + +typedef struct _tikview { + u32 view; + u64 ticketid; + u32 devicetype; + u64 titleid; + u16 access_mask; + u8 reserved[0x3c]; + u8 cidx_mask[0x40]; + u16 padding; + tiklimit limits[8]; +} __attribute__((packed)) tikview; + +typedef struct _tik { + sig_issuer issuer; + u8 fill[63]; //TODO: not really fill + aeskey cipher_title_key; + u8 fill2; + u64 ticketid; + u32 devicetype; + u64 titleid; + u16 access_mask; + u8 reserved[0x3c]; + u8 cidx_mask[0x40]; + u16 padding; + tiklimit limits[8]; +} __attribute__((packed)) tik; + +typedef struct _tmd_content { + u32 cid; + u16 index; + u16 type; + u64 size; + sha1 hash; +} __attribute__((packed)) tmd_content; + +typedef struct _tmd { + sig_issuer issuer; //0x140 + u8 version; //0x180 + u8 ca_crl_version; //0x181 + u8 signer_crl_version; //0x182 + u8 fill2; //0x183 + u64 sys_version; //0x184 + u64 title_id; //0x18c + u32 title_type; //0x194 + u16 group_id; //0x198 + u16 zero; //0x19a + u16 region; //0x19c + u8 ratings[16]; //0x19e + u8 reserved[12]; //0x1ae + u8 ipc_mask[12]; + u8 reserved2[18]; + u32 access_rights; + u16 title_version; + u16 num_contents; + u16 boot_index; + u16 fill3; + // content records follow + // C99 flexible array + tmd_content contents[]; +} __attribute__((packed)) tmd; + +typedef struct _tmd_view_content +{ + u32 cid; + u16 index; + u16 type; + u64 size; +} __attribute__((packed)) tmd_view_content; + +typedef struct _tmdview +{ + u8 version; // 0x0000; + u8 filler[3]; + u64 sys_version; //0x0004 + u64 title_id; // 0x00c + u32 title_type; //0x0014 + u16 group_id; //0x0018 + u8 reserved[0x3e]; //0x001a this is the same reserved 0x3e bytes from the tmd + u16 title_version; //0x0058 + u16 num_contents; //0x005a + tmd_view_content contents[]; //0x005c +}__attribute__((packed)) tmd_view; + +typedef struct _cert_header { + sig_issuer issuer; + u32 cert_type; + char cert_name[64]; + u32 cert_id; //??? +} __attribute__((packed)) cert_header; + +typedef struct _cert_rsa2048 { + sig_issuer issuer; + u32 cert_type; + char cert_name[64]; + u32 cert_id; + u8 modulus[256]; + u32 exponent; + u8 pad[0x34]; +} __attribute__((packed)) cert_rsa2048; + +typedef struct _cert_rsa4096 { + sig_issuer issuer; + u32 cert_type; + char cert_name[64]; + u32 cert_id; + u8 modulus[512]; + u32 exponent; + u8 pad[0x34]; +} __attribute__((packed)) cert_rsa4096; + +typedef struct _cert_ecdsa { + sig_issuer issuer; + u32 cert_type; + char cert_name[64]; + u32 cert_id; // ng key id + u8 r[30]; + u8 s[30]; + u8 pad[0x3c]; +} __attribute__((packed)) cert_ecdsa; + +#define TMD_SIZE(x) (((x)->num_contents)*sizeof(tmd_content) + sizeof(tmd)) +// backwards compatibility +#define TMD_CONTENTS(x) ((x)->contents) + +//TODO: add ECC stuff + +#define IS_VALID_SIGNATURE(x) ( \ + ((*(x))==ES_SIG_RSA2048) || \ + ((*(x))==ES_SIG_RSA4096) || \ + ((*(x))==ES_SIG_ECDSA)) + +#define SIGNATURE_SIZE(x) (\ + ((*(x))==ES_SIG_RSA2048) ? sizeof(sig_rsa2048) : ( \ + ((*(x))==ES_SIG_RSA4096) ? sizeof(sig_rsa4096) : ( \ + ((*(x))==ES_SIG_ECDSA) ? sizeof(sig_ecdsa) : 0 ))) + +#define SIGNATURE_SIG(x) (((u8*)x)+4) + +#define IS_VALID_CERT(x) ( \ + (((x)->cert_type)==ES_CERT_RSA2048) || \ + (((x)->cert_type)==ES_CERT_RSA4096) || \ + (((x)->cert_type)==ES_CERT_ECDSA)) + +#define CERTIFICATE_SIZE(x) (\ + (((x)->cert_type)==ES_CERT_RSA2048) ? sizeof(cert_rsa2048) : ( \ + (((x)->cert_type)==ES_CERT_RSA4096) ? sizeof(cert_rsa4096) : ( \ + (((x)->cert_type)==ES_CERT_ECDSA) ? sizeof(cert_ecdsa) : 0 ))) + +#define SIGNATURE_PAYLOAD(x) ((void *)(((u8*)(x)) + SIGNATURE_SIZE(x))) + +#define SIGNED_TMD_SIZE(x) ( TMD_SIZE((tmd*)SIGNATURE_PAYLOAD(x)) + SIGNATURE_SIZE(x)) +#define SIGNED_TIK_SIZE(x) ( sizeof(tik) + SIGNATURE_SIZE(x) ) +#define SIGNED_CERT_SIZE(x) ( CERTIFICATE_SIZE((cert_header*)SIGNATURE_PAYLOAD(x)) + SIGNATURE_SIZE(x)) + +#define STD_SIGNED_TIK_SIZE ( sizeof(tik) + sizeof(sig_rsa2048) ) + +#define MAX_NUM_TMD_CONTENTS 512 + +#define MAX_TMD_SIZE ( sizeof(tmd) + MAX_NUM_TMD_CONTENTS*sizeof(tmd_content) ) +#define MAX_SIGNED_TMD_SIZE ( MAX_TMD_SIZE + sizeof(sig_rsa2048) ) + +s32 __ES_Init(void); +s32 __ES_Close(void); +s32 __ES_Reset(void); +s32 ES_GetTitleID(u64 *titleID); +s32 ES_SetUID(u64 uid); +s32 ES_GetDataDir(u64 titleID, char *filepath); +s32 ES_GetNumTicketViews(u64 titleID, u32 *cnt); +s32 ES_GetTicketViews(u64 titleID, tikview *views, u32 cnt); +s32 ES_GetNumOwnedTitles(u32 *cnt); +s32 ES_GetOwnedTitles(u64 *titles, u32 cnt); +s32 ES_GetNumTitles(u32 *cnt); +s32 ES_GetTitles(u64 *titles, u32 cnt); +s32 ES_GetNumStoredTMDContents(const signed_blob *stmd, u32 tmd_size, u32 *cnt); +s32 ES_GetStoredTMDContents(const signed_blob *stmd, u32 tmd_size, u32 *contents, u32 cnt); +s32 ES_GetStoredTMDSize(u64 titleID, u32 *size); +s32 ES_GetStoredTMD(u64 titleID, signed_blob *stmd, u32 size); +s32 ES_GetTitleContentsCount(u64 titleID, u32 *num); +s32 ES_GetTitleContents(u64 titleID, u8 *data, u32 size); +s32 ES_GetTMDViewSize(u64 titleID, u32 *size); +s32 ES_GetTMDView(u64 titleID, u8 *data, u32 size); +s32 ES_GetNumSharedContents(u32 *cnt); +s32 ES_GetSharedContents(sha1 *contents, u32 cnt); +s32 ES_LaunchTitle(u64 titleID, const tikview *view); +s32 ES_LaunchTitleBackground(u64 titleID, const tikview *view); +s32 ES_Identify(const signed_blob *certificates, u32 certificates_size, const signed_blob *tmd, u32 tmd_size, const signed_blob *ticket, u32 ticket_size, u32 *keyid); +s32 ES_AddTicket(const signed_blob *tik, u32 tik_size, const signed_blob *certificates, u32 certificates_size, const signed_blob *crl, u32 crl_size); +s32 ES_DeleteTicket(const tikview *view); +s32 ES_AddTitleTMD(const signed_blob *tmd, u32 tmd_size); +s32 ES_AddTitleStart(const signed_blob *tmd, u32 tmd_size, const signed_blob *certificatess, u32 certificatess_size, const signed_blob *crl, u32 crl_size); +s32 ES_AddContentStart(u64 titleID, u32 cid); +s32 ES_AddContentData(s32 cid, u8 *data, u32 data_size); +s32 ES_AddContentFinish(u32 cid); +s32 ES_AddTitleFinish(void); +s32 ES_AddTitleCancel(void); +s32 ES_ImportBoot(const signed_blob *tik, u32 tik_size,const signed_blob *tik_certs, u32 tik_certs_size,const signed_blob *tmd, u32 tmd_size,const signed_blob *tmd_certs, u32 tmd_certs_size,const u8 *content, u32 content_size); +s32 ES_OpenContent(u16 index); +s32 ES_OpenTitleContent(u64 titleID, tikview *views, u16 index); +s32 ES_ReadContent(s32 cfd, u8 *data, u32 data_size); +s32 ES_SeekContent(s32 cfd, s32 where, s32 whence); +s32 ES_CloseContent(s32 cfd); +s32 ES_DeleteTitle(u64 titleID); +s32 ES_DeleteTitleContent(u64 titleID); +s32 ES_Encrypt(u32 keynum, u8 *iv, u8 *source, u32 size, u8 *dest); +s32 ES_Decrypt(u32 keynum, u8 *iv, u8 *source, u32 size, u8 *dest); +s32 ES_Sign(u8 *source, u32 size, u8 *sig, u8 *certs); +s32 ES_GetDeviceCert(u8 *outbuf); +s32 ES_GetDeviceID(u32 *device_id); +s32 ES_GetBoot2Version(u32 *version); +signed_blob *ES_NextCert(const signed_blob *certs); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif /* defined(HW_RVL) */ + +#endif diff --git a/wii/libogc/include/ogc/exi.h b/wii/libogc/include/ogc/exi.h new file mode 100644 index 0000000000..b3f104ebd0 --- /dev/null +++ b/wii/libogc/include/ogc/exi.h @@ -0,0 +1,325 @@ +/*------------------------------------------------------------- + +exi.h -- EXI subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#ifndef __EXI_H__ +#define __EXI_H__ + +/*! +\file exi.h +\brief EXI subsystem + +*/ + + +#include "gctypes.h" + +/*! + * \addtogroup exi_tx_mode EXI tranfer types + * @{ + */ + +#define EXI_READ 0 /*!< EXI transfer type read */ +#define EXI_WRITE 1 /*!< EXI transfer type write */ +#define EXI_READWRITE 2 /*!< EXI transfer type read-write */ + +/*! + * @} + */ + + +/*! + * \addtogroup exi_channels EXI channels + * @{ + */ + +#define EXI_CHANNEL_0 0 /*!< EXI channel 0 (memory card slot A) */ +#define EXI_CHANNEL_1 1 /*!< EXI channel 1 (memory card slot B) */ +#define EXI_CHANNEL_2 2 /*!< EXI channel 2 (other EXI devices connected, e.g. BBA) */ +#define EXI_CHANNEL_MAX 3 /*!< _Termination */ + +/*! + * @} + */ + + +/*! + * \addtogroup exi_devices EXI devices + * @{ + */ + +#define EXI_DEVICE_0 0 /*!< EXI device 0 */ +#define EXI_DEVICE_1 1 /*!< EXI device 1 */ +#define EXI_DEVICE_2 2 /*!< EXI device 2 */ +#define EXI_DEVICE_MAX 3 /*!< _Termination */ + +/*! + * @} + */ + + +/*! + * \addtogroup exi_speed EXI device frequencies + * @{ + */ + +#define EXI_SPEED1MHZ 0 /*!< EXI device frequency 1MHz */ +#define EXI_SPEED2MHZ 1 /*!< EXI device frequency 2MHz */ +#define EXI_SPEED4MHZ 2 /*!< EXI device frequency 4MHz */ +#define EXI_SPEED8MHZ 3 /*!< EXI device frequency 8MHz */ +#define EXI_SPEED16MHZ 4 /*!< EXI device frequency 16MHz */ +#define EXI_SPEED32MHZ 5 /*!< EXI device frequency 32MHz */ + +/*! + * @} + */ + + +/*! + * \addtogroup exi_flags EXI device operation flags + * @{ + */ + +#define EXI_FLAG_DMA 0x0001 /*!< EXI DMA mode transfer in progress */ +#define EXI_FLAG_IMM 0x0002 /*!< EXI immediate mode transfer in progress */ +#define EXI_FLAG_SELECT 0x0004 /*!< EXI channel and device selected */ +#define EXI_FLAG_ATTACH 0x0008 /*!< EXI device on selected channel and device attached */ +#define EXI_FLAG_LOCKED 0x0010 /*!< EXI channel and device locked for device operations */ + +/*! + * @} + */ + + +/*! + * \addtogroup exi_mcident EXI memory card identifier + * @{ + */ + +#define EXI_MEMCARD59 0x00000004 /*!< Nintendo memory card: 64/ 4/ 0.5 (blocks/Mbits/MB). 3rd party vendors do have the same identification */ +#define EXI_MEMCARD123 0x00000008 /*!< Nintendo memory card: 128/ 8/ 1.0 (blocks/Mbits/MB). 3rd party vendors do have the same identification */ +#define EXI_MEMCARD251 0x00000010 /*!< Nintendo memory card: 256/ 16/ 2.0 (blocks/Mbits/MB). 3rd party vendors do have the same identification */ +#define EXI_MEMCARD507 0x00000020 /*!< Nintendo memory card: 512/ 32/ 4.0 (blocks/Mbits/MB). 3rd party vendors do have the same identification */ +#define EXI_MEMCARD1019 0x00000040 /*!< Nintendo memory card: 1024/ 64/ 8.0 (blocks/Mbits/MB). 3rd party vendors do have the same identification */ +#define EXI_MEMCARD2043 0x00000080 /*!< Nintendo memory card: 2048/128/16.0 (blocks/Mbits/MB). 3rd party vendors do have the same identification */ + +/*! + * @} + */ + + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/*! \typedef s32 (*EXICallback)(s32 chn,s32 dev) +\brief function pointer typedef for the user's EXI callback +\param chn EXI channel +\param dev EXI device +*/ +typedef s32 (*EXICallback)(s32 chn,s32 dev); + + +/*! \fn s32 EXI_ProbeEx(s32 nChn) +\brief Performs an extended probe of the EXI channel +\param[in] nChn EXI channel to probe + +\return 1 on success, <=0 on error +*/ +s32 EXI_ProbeEx(s32 nChn); + + +/*! \fn s32 EXI_Probe(s32 nChn) +\brief Probes the EXI channel +\param[in] nChn EXI channel to probe + +\return 1 on success, <=0 on error +*/ +s32 EXI_Probe(s32 nChn); + + +/*! \fn s32 EXI_Lock(s32 nChn,s32 nDev,EXICallback unlockCB) +\brief Try to lock the desired EXI channel on the given device. +\param[in] nChn EXI channel to lock +\param[in] nDev EXI device to lock +\param[in] unlockCB pointer to callback to call when EXI_Unlock() is called. Thus allowing us a small way of mutual exclusion. + +\return 1 on success, <=0 on error +*/ +s32 EXI_Lock(s32 nChn,s32 nDev,EXICallback unlockCB); + + +/*! \fn s32 EXI_Unlock(s32 nChn) +\brief Unlock the desired EXI channel. +\param[in] nChn EXI channel to unlock + +\return 1 on success, <=0 on error +*/ +s32 EXI_Unlock(s32 nChn); + + +/*! \fn s32 EXI_Select(s32 nChn,s32 nDev,s32 nFrq) +\brief Selects the spedified EXI channel on the given device with the given frequency +\param[in] nChn EXI channel to select +\param[in] nDev EXI device to select +\param[in] nFrq EXI frequency to select + +\return 1 on success, <=0 on error +*/ +s32 EXI_Select(s32 nChn,s32 nDev,s32 nFrq); + + +/*! \fn s32 EXI_SelectSD(s32 nChn,s32 nDev,s32 nFrq) +\brief Performs a special select, for SD cards or adapters respectively, on the given device with the given frequence +\param[in] nChn EXI channel to select +\param[in] nDev EXI device to select +\param[in] nFrq EXI frequency to select + +\return 1 on success, <=0 on error +*/ +s32 EXI_SelectSD(s32 nChn,s32 nDev,s32 nFrq); + + +/*! \fn s32 EXI_Deselect(s32 nChn) +\brief Deselects the EXI channel. +\param[in] nChn EXI channel to deselect + +\return 1 on success, <=0 on error +*/ +s32 EXI_Deselect(s32 nChn); + + +/*! \fn s32 EXI_Sync(s32 nChn) +\brief Synchronize or finish respectively the last EXI transfer. +\param[in] nChn EXI channel to select + +\return 1 on success, <=0 on error +*/ +s32 EXI_Sync(s32 nChn); + + +/*! \fn s32 EXI_Imm(s32 nChn,void *pData,u32 nLen,u32 nMode,EXICallback tc_cb) +\brief Initializes an immediate mode EXI transfer. +\param[in] nChn EXI channel to select +\param[in,out] pData pointer to a buffer to read/copy from/to data. +\param[in] nLen lenght of data to transfer <=4. +\param[in] nMode direction of transferoperation(EXI_READ,EXI_WRITE,EXI_READWRITE) +\param[in] tc_cb pointer to a callback to call when transfer has completed. May be NULL. + +\return 1 on success, <=0 on error +*/ +s32 EXI_Imm(s32 nChn,void *pData,u32 nLen,u32 nMode,EXICallback tc_cb); + + +/*! \fn s32 EXI_ImmEx(s32 nChn,void *pData,u32 nLen,u32 nMode) +\brief Initializes an extended immediate mode EXI transfer. +\param[in] nChn EXI channel to select +\param[in,out] pData pointer to a buffer to read/copy from/to data. +\param[in] nLen lenght of data to transfer. +\param[in] nMode direction of transferoperation(EXI_READ,EXI_WRITE,EXI_READWRITE) + +\return 1 on success, <=0 on error +*/ +s32 EXI_ImmEx(s32 nChn,void *pData,u32 nLen,u32 nMode); + + +/*! \fn s32 EXI_Dma(s32 nChn,void *pData,u32 nLen,u32 nMode,EXICallback tc_cb) +\brief Initializes a DMA mode EXI transfer. +\param[in] nChn EXI channel to select +\param[in,out] pData pointer to a buffer to read/copy from/to data. +\param[in] nLen lenght of data to transfer. +\param[in] nMode direction of transferoperation(EXI_READ,EXI_WRITE,EXI_READWRITE) +\param[in] tc_cb pointer to a callback to call when transfer has completed. May be NULL. + +\return 1 on success, <=0 on error +*/ +s32 EXI_Dma(s32 nChn,void *pData,u32 nLen,u32 nMode,EXICallback tc_cb); + + +/*! \fn s32 EXI_GetState(s32 nChn) +\brief Get the EXI state +\param[in] nChn EXI channel to select + +\return EXI channels state flag. +*/ +s32 EXI_GetState(s32 nChn); + + +/*! \fn s32 EXI_GetID(s32 nChn,s32 nDev,u32 *nId) +\brief Get the ID of the connected EXI device on the given channel +\param[in] nChn EXI channel to select +\param[in] nDev EXI device to select +\param[out] nId EXI device ID to return. + +\return 1 on success, <=0 on error +*/ +s32 EXI_GetID(s32 nChn,s32 nDev,u32 *nId); + + +/*! \fn s32 EXI_Attach(s32 nChn,EXICallback ext_cb) +\brief Attach the device on the given channel +\param[in] nChn EXI channel to select +\param[in] ext_cb pointer to callback to call when device is physically removed. + +\return 1 on success, <=0 on error +*/ +s32 EXI_Attach(s32 nChn,EXICallback ext_cb); + + +/*! \fn s32 EXI_Detach(s32 nChn) +\brief Detach the device on the given channel +\param[in] nChn EXI channel to select + +\return 1 on success, <=0 on error +*/ +s32 EXI_Detach(s32 nChn); + + +/*! \fn void EXI_ProbeReset() +\brief Resets certain internal flags and counters and performs a probe on all 3 channels. + +\return nothing +*/ +void EXI_ProbeReset(); + + +/*! \fn EXICallback EXI_RegisterEXICallback(s32 nChn,EXICallback exi_cb) +\brief Register a callback function in the EXI driver for the EXI interrupt. +\param[in] nChn EXI channel to select +\param[in] exi_cb pointer to the function which to call when EXI interrupt has triggered. + +\return old callback function pointer or NULL +*/ +EXICallback EXI_RegisterEXICallback(s32 nChn,EXICallback exi_cb); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/gu.h b/wii/libogc/include/ogc/gu.h new file mode 100644 index 0000000000..3ba8db46af --- /dev/null +++ b/wii/libogc/include/ogc/gu.h @@ -0,0 +1,499 @@ +#ifndef __GU_H__ +#define __GU_H__ + +/*! + * \file gu.h + * \brief GU/Matrix subsystem + * + * \details The GU/Matrix subsystem is used for many matrix- , vector- and quaternion-related operations. + * + * The matrix functions are coupled tightly with GX (GX will take Mtx for many of its own matrix-related functions, for example). + * This library supports 3x3, 3x4, 4x3 and 4x4 matrices. + * + * This library has functions for manipulating vectors as well; some of its functions are for transforming matrices using vectors. + * + * It also includes functions for using and converting between quaternions. Although quaternions are not used natively in libogc, + * they work well with rotation functions as they aren't susceptible to "gimbal lock". You can use the appropriate functions to + * freely convert between quaternions and matrices. + * + * \note Many of the functions come in two flavors: C and "paired single". Both perform the same exact operations, but with a key + * difference: the C versions are written in pure C and are slightly more accurate, while the PS versions are hand-written + * assembly routines utilizing the Gekko's "paired-single" extension, which is much faster for almost every operation but slightly + * less accurate. When building for the GameCube or Wii (which is probably always), the library is configured to automatically use + * the paired-single tuned versions, as the speed difference is worth the accuracy hit. If you want to use the C routine and take + * the performance hit instead, prefix the function with "c_". You are not limited to using only one or the other collection; you + * can use both in your code if you wish. + * + * \warning Some functions (notably guFrustum() and related) take a 4x4 matrix, while the rest work only on 4x3 matrices. Make sure + * you are passing the correct matrix type to each function, as passing the wrong one can create subtle bugs. + */ + +#include + +#ifdef GEKKO +#define MTX_USE_PS +#undef MTX_USE_C +#endif + +#ifndef GEKKO +#define MTX_USE_C +#undef MTX_USE_PS +#endif + +#ifndef MAX +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +#define M_PI 3.14159265358979323846 +#define M_DTOR (3.14159265358979323846/180.0) + +#define FTOFIX32(x) (s32)((x) * (f32)0x00010000) +#define FIX32TOF(x) ((f32)(x) * (1.0f / (f32)0x00010000)) +#define FTOFRAC8(x) ((s32) MIN(((x) * (128.0f)), 127.0f) & 0xff) + +#define DegToRad(a) ( (a) * 0.01745329252f ) +#define RadToDeg(a) ( (a) * 57.29577951f ) + +/*! + * \def guMtxRowCol(mt,row,col) + * \brief Provides storage-safe access to elements of Mtx and Mtx44. + * + * \details This macro provides storage-safe access to elements of Mtx and Mtx44. Matrix storage format is transparent to the + * programmer as long as matrices are initialized and manipulated exclusively with the matrix API. Do not initialize matrices + * when they are first declared and do not set values by hand. To insulate code from changes to matrix storage format, you should + * use this macro instead of directly accessing individual matrix elements. + * + * \note When using this function, think of the matrix in row-major format. + * + * \param[in] mt Matrix to be accessed. + * \param[in] r Row index of element to access. + * \param[in] c Column index of element to access. + * + * \return none + */ +#define guMtxRowCol(mt,row,col) (mt[row][col]) + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/*! \struct guVector + * \brief 3-element vector with x, y and z components. + * + * \details When used in 3D transformations, it is treated as a column vector with an implied fourth 'w' coordinate of 1. + * For example, to multiply a vector vOld by a matrix m: vNew = m x vOld. In code: + * + * \code guVecMultiply( m, &vOld, &vNew ); \endcode + * + * \note This is a generic structure which can be used in any situation or function that accepts an array or struct with + * three f32 values. + */ +typedef struct _vecf { + f32 x,y,z; +} guVector; + +/*! \struct guQuaternion + * \brief Quaternion type consisting of an (x,y,z) vector component and a (w) scalar component. + * + * \details This struct is used by gu library function such as guQuatMtx(), which generates a rotation matrix from a + * quaternion. + * + * \note This is a generic structure which can be used in any situation or function that accepts an array or struct with + * four f32 values. + */ +typedef struct _qrtn { + f32 x,y,z,w; +} guQuaternion; + +/*! \typedef f32 Mtx[3][4] + * \brief Standard 3x4 matrix. + * \warning Some functions take the 4x4 matrix type rather than this one, so make sure you don't mix up the two. + */ +typedef f32 Mtx[3][4]; +typedef f32 (*MtxP)[4]; + +/*! \typedef f32 ROMtx[4][3] + * \brief Column-major representation of the standard Mtx structure. + * + * \details It is not a true transpose, as it is a 4x3 matrix. These structures are only accepted by functions that explicitly + * require reordered matrices. + */ +typedef f32 ROMtx[4][3]; +typedef f32 (*ROMtxP)[3]; + +/*! \typedef f32 Mtx33[3][3] + * \brief 3x3 matrix. + */ +typedef f32 Mtx33[3][3]; +typedef f32 (*Mtx33P)[3]; + +/*! \typedef f32 Mtx44[4][4] + * \brief 4x4 matrix. + * \warning Some functions take this instead of the 3x4 matrix, so make sure you don't mix up the two. + */ +typedef f32 Mtx44[4][4]; +typedef f32 (*Mtx44P)[4]; + +/*! + * \fn void guFrustum(Mtx44 mt,f32 t,f32 b,f32 l,f32 r,f32 n,f32 f) + * \brief Sets a 4x4 perspective projection matrix from viewing volume dimensions. + * + * \details This matrix is used by the GX API to transform points to screen space. + * + * For normal perspective projection, the axis of projection is the -z axis, so \a t = positive, \a b = -\a t, \a r = + * positive, \a l = -\a r. \a n and \a f must both be given as positive distances. + * + * \note \a m negates a point's 'z' values, so pre-transformed points should have negative 'z' values in eye space in + * order to be visible after projection. + * + * \param[out] mt New projection matrix. + * \param[in] t Top edge of view volume at the near clipping plane. + * \param[in] b Bottom edge of view volume at the near clipping plane. + * \param[in] l Left edge of view volume at the near clipping plane. + * \param[in] r Right edge of view volume at the near clipping plane. + * \param[in] n Positive distance to the near clipping plane. + * \param[in] f Positive distance to the far clipping plane. + * + * \return none + */ +void guFrustum(Mtx44 mt,f32 t,f32 b,f32 l,f32 r,f32 n,f32 f); + +/*! + * \fn void guPerspective(Mtx44 mt,f32 fovy,f32 aspect,f32 n,f32 f) + * \brief Sets a 4x4 perspective projection matrix from field of view and aspect ratio parameters. + * + * \details This matrix is used by the GX API to transform points to screen space. + * + * This function generates a projection matrix equivalent to that created by guFrustum() with the axis of projection + * centered around Z. It is included to provide an alternative method of specifying view volume dimensions. + * + * The field of view (\a fovy) is the total field of view in degrees in the Y-Z plane. \a aspect is the ratio + * (width/height) of the view window in screen space. \a n and \a f must both be given as positive distances. + * + * \note \a m negates a point's 'z' values, so pre-transformed points should have negative 'z' values in eye space in order to + * be visible after projection. + * + * \param[out] mt New perspective projection matrix. + * \param[in] fovy Total field of view in the Y-Z plane measured in degrees. + * \param[in] aspect View window aspect ratio (width/height) + * \param[in] n Positive distance to near clipping plane. + * \param[in] f Positive distance to far clipping plane. + * + * \return none + */ +void guPerspective(Mtx44 mt,f32 fovy,f32 aspect,f32 n,f32 f); + +/*! + * \fn void guOrtho(Mtx44 mt,f32 t,f32 b,f32 l,f32 r,f32 n,f32 f) + * \brief Sets a 4x4 matrix for orthographic projection. + * + * \details This matrix is used by the GX API to transform points from eye space to screen space. + * + * For normal parallel projections, the axis of projection is the -z axis, so \a t = positive, \a b = -\a t, \a r = + * positive, \a l = -\a r. \a n and \a f must both be given as positive distances. + * + * \note \a m negates \a a point's 'z' values, so pre-transformed points should have negative 'z' values in eye space in order + * to be visible after projection. + * + * \param[out] mt New parallel projection matrix. + * \param[in] t Top edge of view volume. + * \param[in] b Bottom edge of view volume. + * \param[in] l Left edge of view volume. + * \param[in] r Right edge of view volume. + * \param[in] n Positive distance to the near clipping plane. + * \param[in] f Positive distance to the far clipping plane. + * + * \return none + */ +void guOrtho(Mtx44 mt,f32 t,f32 b,f32 l,f32 r,f32 n,f32 f); + + +/*! + * \fn void guLightPerspective(Mtx mt,f32 fovY,f32 aspect,f32 scaleS,f32 scaleT,f32 transS,f32 transT) + * \brief Sets a 3x4 perspective projection matrix from field of view and aspect ratio parameters, two scale values, and two + * translation values. + * + * \details This matrix is used to project points into texture space and yield texture coordinates. + * + * This function generates a projection matrix, equivalent to that created by guLightFrustum(), with the axis of projection + * centered around Z. This function is included to provide an alternative method of specifying texture projection volume + * dimensions. + * + * The field of view (\a fovy) is the total field of view in degrees in the YZ plane. \a aspect is the ratio (width / height) + * of the view window in screen space. + * + * Standard projection yields values ranging from -1.0 to 1.0 in both dimensions of the front clipping plane. Since texture + * coordinates should usually be within the range of 0.0 to 1.0, we have added a scale and translation value for both S and T. + * The most common way to use these values is to set all of them to 0.5 (so that points in the range of -1.0 to 1.0 are first + * scaled by 0.5) to be in the range of -0.5 to 0.5. Then they are translated by 0.5 to be in the range of 0.0 to 1.0. Other + * values can be used for translation and scale to yield different effects. + * + * \param[out] mt New projection matrix. + * \param[in] fovy Total field of view in the YZ plane measured in degrees. + * \param[in] aspect View window aspect ratio (width / height) + * \param[in] scaleS Scale in the S direction for projected coordinates (usually 0.5). + * \param[in] scaleT Scale in the T direction for projected coordinates (usually 0.5). + * \param[in] transS Translate in the S direction for projected coordinates (usually 0.5). + * \param[in] transT Translate in the T direction for projected coordinates (usually 0.5). + * + * \return none + */ +void guLightPerspective(Mtx mt,f32 fovY,f32 aspect,f32 scaleS,f32 scaleT,f32 transS,f32 transT); + +/*! + * \fn void guLightOrtho(Mtx mt,f32 t,f32 b,f32 l,f32 r,f32 scaleS,f32 scaleT,f32 transS,f32 transT) + * \brief Sets a 3x4 matrix for orthographic projection. + * + * \details Use this matrix to project points into texture space and yield texture coordinates. + * + * For normal parallel projections, the axis of projection is the -z axis, so \a t = positive, \a b = -\a t, \a r = positive, + * \a l = -\a r. + * + * Standard projection yields values ranging from -1.0 to 1.0 in both dimensions of the front clipping plane. Since texture + * coordinates should usually be within the range of 0.0 to 1.0, we have added a scale and translation value for both S and T. + * The most common way to use these values is to set all of them to 0.5 so that points in the range of -1.0 to 1.0 are first + * scaled by 0.5 (to be in the range of -0.5 to 0.5). Then they are translated by 0.5 to be in the range of 0.0 to 1.0. Other + * values can be used for translation and scale to yield different effects. + * + * \param[out] mt New parallel projection matrix. + * \param[in] t Top edge of view volume. + * \param[in] b Bottom edge of view volume. + * \param[in] l Left edge of view volume. + * \param[in] r Right edge of view volume. + * \param[in] scaleS Scale in the S direction for projected coordinates (usually 0.5). + * \param[in] scaleT Scale in the T direction for projected coordinates (usually 0.5). + * \param[in] transS Translate in the S direction for projected coordinates (usually 0.5). + * \param[in] transT Translate in the T direction for projected coordinates (usually 0.5). + * + * \return none + */ +void guLightOrtho(Mtx mt,f32 t,f32 b,f32 l,f32 r,f32 scaleS,f32 scaleT,f32 transS,f32 transT); + +/*! + * \fn void guLightFrustum(Mtx mt,f32 t,f32 b,f32 l,f32 r,f32 n,f32 scaleS,f32 scaleT,f32 transS,f32 transT) + * \brief Sets a 3x4 perspective projection matrix from viewing volume dimensions, two scale values, and two translation values. + * + * \details This matrix is used to project points into texture space and yield texture coordinates. + * + * For normal perspective projection, the axis of projection is the -z axis, so \a t = positive, \a b = -\a t, \a r = positive, + * \a l = -\a r. \a n must be given as a positive distance. + * + * Standard projection yields values ranging from -1.0 to 1.0 in both dimensions of the front clipping plane. Since texture + * coordinates usually should be within the range of 0.0 to 1.0, we have added a scale and translation value for both S and T. + * The most common usage of these values is to set all of them to 0.5 so that points in the range of -1.0 to 1.0 are first + * scaled by 0.5 to be in the range of -0.5 to 0.5, and are then translated by 0.5 to be in the range of 0.0 to 1.0. Other + * values can be used for translation and scale to yield different effects. + * + * \param[out] mt New projection matrix. + * \param[in] t Top edge of view volume at the near clipping plane. + * \param[in] b Bottom edge of view volume at the near clipping plane. + * \param[in] l Left edge of view volume at the near clipping plane. + * \param[in] r Right edge of view volume at the near clipping plane. + * \param[in] n Positive distance to the near clipping plane. + * \param[in] scaleS Scale in the S direction for projected coordinates (usually 0.5). + * \param[in] scaleT Scale in the T direction for projected coordinates (usually 0.5). + * \param[in] transS Translate in the S direction for projected coordinates (usually 0.5). + * \param[in] transT Translate in the T direction for projected coordinates (usually 0.5). + * + * \return none + */ +void guLightFrustum(Mtx mt,f32 t,f32 b,f32 l,f32 r,f32 n,f32 scaleS,f32 scaleT,f32 transS,f32 transT); + + +/*! + * \fn void guLookAt(Mtx mt,guVector *camPos,guVector *camUp,guVector *target) + * \brief Sets a world-space to camera-space transformation matrix. + * + * \details Create the matrix \a m by specifying a camera position (\a camPos), a camera "up" direction (\a camUp), and a target + * position (\a target). + * + * The camera's reference viewing direction is the -z axis. The camera's reference 'up' direction is the +y axis. + * + * This function is especially convenient for creating a tethered camera, aiming at an object, panning, or specifying an + * arbitrary view. + * + * \param[out] mt New viewing matrix. + * \param[in] camPos Vector giving 3D camera position in world space. + * \param[in] camUp Vector containing camera "up" vector; does not have to be a unit vector. + * \param[in] target Vector giving 3D target position in world space. + * + * \return none + */ +void guLookAt(Mtx mt,guVector *camPos,guVector *camUp,guVector *target); + + +/*! + * \fn void guVecHalfAngle(guVector *a,guVector *b,guVector *half) + * \brief Computes a vector that lies halfway between \a a and \a b. + * + * \details The halfway vector is useful in specular reflection calculations. It is interpreted as pointing from the reflecting + * surface to the general viewing direction. + * + * \a a and \a b do not have to be unit vectors. Both of these vectors are assumed to be pointing towards the surface from the + * light or viewer, respectively. Local copies of these vectors are negated, normalized and added head to tail. + * + * \a half is computed as a unit vector that points from the surface to halfway between the light and the viewing direction. + * + * \param[in] a Pointer to incident vector. Must point from the light source to the surface. + * \param[in] b Pointer to viewing vector. Must point from the viewer to the surface. + * \param[out] half Pointer to resultant half-angle unit vector; points from the surface to halfway between the light and the viewing direction. + * + * \return none + */ +void guVecHalfAngle(guVector *a,guVector *b,guVector *half); + +void c_guVecAdd(guVector *a,guVector *b,guVector *ab); +void c_guVecSub(guVector *a,guVector *b,guVector *ab); +void c_guVecScale(guVector *src,guVector *dst,f32 scale); +void c_guVecNormalize(guVector *v); +void c_guVecMultiply(Mtx mt,guVector *src,guVector *dst); +void c_guVecCross(guVector *a,guVector *b,guVector *axb); +void c_guVecMultiplySR(Mtx mt,guVector *src,guVector *dst); +f32 c_guVecDotProduct(guVector *a,guVector *b); + +#ifdef GEKKO +void ps_guVecAdd(register guVector *a,register guVector *b,register guVector *ab); +void ps_guVecSub(register guVector *a,register guVector *b,register guVector *ab); +void ps_guVecScale(register guVector *src,register guVector *dst,f32 scale); +void ps_guVecNormalize(register guVector *v); +void ps_guVecCross(register guVector *a,register guVector *b,register guVector *axb); +void ps_guVecMultiply(register Mtx mt,register guVector *src,register guVector *dst); +void ps_guVecMultiplySR(register Mtx mt,register guVector *src,register guVector *dst); +f32 ps_guVecDotProduct(register guVector *a,register guVector *b); +#endif //GEKKO + +void c_guQuatAdd(guQuaternion *a,guQuaternion *b,guQuaternion *ab); +void c_guQuatSub(guQuaternion *a,guQuaternion *b,guQuaternion *ab); +void c_guQuatMultiply(guQuaternion *a,guQuaternion *b,guQuaternion *ab); +void c_guQuatNormalize(guQuaternion *a,guQuaternion *d); +void c_guQuatInverse(guQuaternion *a,guQuaternion *d); +void c_guQuatMtx(guQuaternion *a,Mtx m); + +#ifdef GEKKO +void ps_guQuatAdd(register guQuaternion *a,register guQuaternion *b,register guQuaternion *ab); +void ps_guQuatSub(register guQuaternion *a,register guQuaternion *b,register guQuaternion *ab); +void ps_guQuatMultiply(register guQuaternion *a,register guQuaternion *b,register guQuaternion *ab); +void ps_guQuatNormalize(register guQuaternion *a,register guQuaternion *d); +void ps_guQuatInverse(register guQuaternion *a,register guQuaternion *d); +#endif + +void c_guMtxIdentity(Mtx mt); +void c_guMtxCopy(Mtx src,Mtx dst); +void c_guMtxConcat(Mtx a,Mtx b,Mtx ab); +void c_guMtxScale(Mtx mt,f32 xS,f32 yS,f32 zS); +void c_guMtxScaleApply(Mtx src,Mtx dst,f32 xS,f32 yS,f32 zS); +void c_guMtxApplyScale(Mtx src,Mtx dst,f32 xS,f32 yS,f32 zS); +void c_guMtxTrans(Mtx mt,f32 xT,f32 yT,f32 zT); +void c_guMtxTransApply(Mtx src,Mtx dst,f32 xT,f32 yT,f32 zT); +void c_guMtxApplyTrans(Mtx src,Mtx dst,f32 xT,f32 yT,f32 zT); +u32 c_guMtxInverse(Mtx src,Mtx inv); +u32 c_guMtxInvXpose(Mtx src,Mtx xPose); +void c_guMtxTranspose(Mtx src,Mtx xPose); +void c_guMtxRotRad(Mtx mt,const char axis,f32 rad); +void c_guMtxRotTrig(Mtx mt,const char axis,f32 sinA,f32 cosA); +void c_guMtxRotAxisRad(Mtx mt,guVector *axis,f32 rad); +void c_guMtxReflect(Mtx m,guVector *p,guVector *n); +void c_guMtxQuat(Mtx m,guQuaternion *a); + +#ifdef GEKKO +void ps_guMtxIdentity(register Mtx mt); +void ps_guMtxCopy(register Mtx src,register Mtx dst); +void ps_guMtxConcat(register Mtx a,register Mtx b,register Mtx ab); +void ps_guMtxTranspose(register Mtx src,register Mtx xPose); +u32 ps_guMtxInverse(register Mtx src,register Mtx inv); +u32 ps_guMtxInvXpose(register Mtx src,register Mtx xPose); +void ps_guMtxScale(register Mtx mt,register f32 xS,register f32 yS,register f32 zS); +void ps_guMtxScaleApply(register Mtx src,register Mtx dst,register f32 xS,register f32 yS,register f32 zS); +void ps_guMtxApplyScale(register Mtx src,register Mtx dst,register f32 xS,register f32 yS,register f32 zS); +void ps_guMtxTrans(register Mtx mt,register f32 xT,register f32 yT,register f32 zT); +void ps_guMtxTransApply(register Mtx src,register Mtx dst,register f32 xT,register f32 yT,register f32 zT); +void ps_guMtxApplyTrans(register Mtx src,register Mtx dst,register f32 xT,register f32 yT,register f32 zT); +void ps_guMtxRotRad(register Mtx mt,register const char axis,register f32 rad); +void ps_guMtxRotTrig(register Mtx mt,register const char axis,register f32 sinA,register f32 cosA); +void ps_guMtxRotAxisRad(register Mtx mt,register guVector *axis,register f32 tmp0); +void ps_guMtxReflect(register Mtx m,register guVector *p,register guVector *n); +#endif //GEKKO + +#ifdef MTX_USE_C + +#define guVecAdd c_guVecAdd +#define guVecSub c_guVecSub +#define guVecScale c_guVecScale +#define guVecNormalize c_guVecNormalize +#define guVecMultiply c_guVecMultiply +#define guVecCross c_guVecCross +#define guVecMultiplySR c_guVecMultiplySR +#define guVecDotProduct c_guVecDotProduct + +#define guQuatAdd c_guQuatAdd +#define guQuatSub c_guQuatSub +#define guQuatMultiply c_guQuatMultiply +#define guQuatNoramlize c_guQuatNormalize +#define guQuatInverse c_guQuatInverse +#define guQuatMtx c_guQuatMtx + +#define guMtxIdentity c_guMtxIdentity +#define guMtxCopy c_guMtxCopy +#define guMtxConcat c_guMtxConcat +#define guMtxScale c_guMtxScale +#define guMtxScaleApply c_guMtxScaleApply +#define guMtxApplyScale c_guMtxApplyScale +#define guMtxTrans c_guMtxTrans +#define guMtxTransApply c_guMtxTransApply +#define guMtxApplyTrans c_guMtxApplyTrans +#define guMtxInverse c_guMtxInverse +#define guMtxTranspose c_guMtxTranspose +#define guMtxInvXpose c_guMtxInvXpose +#define guMtxRotRad c_guMtxRotRad +#define guMtxRotTrig c_guMtxRotTrig +#define guMtxRotAxisRad c_guMtxRotAxisRad +#define guMtxReflect c_guMtxReflect +#define guMtxQuat c_guMtxQuat + +#else //MTX_USE_C + +#define guVecAdd ps_guVecAdd +#define guVecSub ps_guVecSub +#define guVecScale ps_guVecScale +#define guVecNormalize ps_guVecNormalize +#define guVecMultiply ps_guVecMultiply +#define guVecCross ps_guVecCross +#define guVecMultiplySR ps_guVecMultiplySR +#define guVecDotProduct ps_guVecDotProduct + +#define guQuatAdd ps_guQuatAdd +#define guQuatSub ps_guQuatSub +#define guQuatMultiply ps_guQuatMultiply +#define guQuatNormalize ps_guQuatNormalize +#define guQuatInverse ps_guQuatInverse + +#define guMtxIdentity ps_guMtxIdentity +#define guMtxCopy ps_guMtxCopy +#define guMtxConcat ps_guMtxConcat +#define guMtxScale ps_guMtxScale +#define guMtxScaleApply ps_guMtxScaleApply +#define guMtxApplyScale ps_guMtxApplyScale +#define guMtxTrans ps_guMtxTrans +#define guMtxTransApply ps_guMtxTransApply +#define guMtxApplyTrans ps_guMtxApplyTrans +#define guMtxInverse ps_guMtxInverse +#define guMtxTranspose ps_guMtxTranspose +#define guMtxInvXpose ps_guMtxInvXpose +#define guMtxRotRad ps_guMtxRotRad +#define guMtxRotTrig ps_guMtxRotTrig +#define guMtxRotAxisRad ps_guMtxRotAxisRad +#define guMtxReflect ps_guMtxReflect + +#endif //MTX_USE_PS + +#define guMtxRotDeg(mt,axis,deg) guMtxRotRad(mt,axis,DegToRad(deg)) +#define guMtxRotAxisDeg(mt,axis,deg) guMtxRotAxisRad(mt,axis,DegToRad(deg)) + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/gx.h b/wii/libogc/include/ogc/gx.h new file mode 100644 index 0000000000..b58d35f69c --- /dev/null +++ b/wii/libogc/include/ogc/gx.h @@ -0,0 +1,5256 @@ +#ifndef __GX_H__ +#define __GX_H__ + +/*! + * \file gx.h + * \brief GX subsystem + * + */ + +#include +#include "lwp.h" +#include "gx_struct.h" +#include "gu.h" + +#define GX_FALSE 0 +#define GX_TRUE 1 +#define GX_DISABLE 0 +#define GX_ENABLE 1 + +/*! \addtogroup clipmode Clipping mode + * @{ + */ +#define GX_CLIP_DISABLE 1 +#define GX_CLIP_ENABLE 0 +/*! @} */ + +#define GX_FIFO_MINSIZE (64*1024) /*!< Smallest usable graphics FIFO size. */ +#define GX_FIFO_HIWATERMARK (16*1024) /*!< Default hi watermark for FIFO buffer control. */ +#define GX_FIFO_OBJSIZE 128 + +#define GX_PERSPECTIVE 0 +#define GX_ORTHOGRAPHIC 1 + +#define GX_MT_NULL 0 +#define GX_MT_XF_FLUSH 1 +#define GX_MT_DL_SAVE_CTX 2 + +#define GX_XF_FLUSH_NONE 0 +#define GX_XF_FLUSH_SAFE 1 + +/*! \addtogroup channelid Color channel ID + * @{ + */ +#define GX_COLOR0 0 +#define GX_COLOR1 1 +#define GX_ALPHA0 2 +#define GX_ALPHA1 3 +#define GX_COLOR0A0 4 +#define GX_COLOR1A1 5 +#define GX_COLORZERO 6 +#define GX_ALPHA_BUMP 7 +#define GX_ALPHA_BUMPN 8 +#define GX_COLORNULL 0xff +/*! @} */ + +/*! \addtogroup mtxtype Matrix type + * @{ + */ +#define GX_MTX2x4 0 +#define GX_MTX3x4 1 +/*! @} */ + +/*! \addtogroup vtxfmt Vertex format index + * @{ + */ +#define GX_VTXFMT0 0 +#define GX_VTXFMT1 1 +#define GX_VTXFMT2 2 +#define GX_VTXFMT3 3 +#define GX_VTXFMT4 4 +#define GX_VTXFMT5 5 +#define GX_VTXFMT6 6 +#define GX_VTXFMT7 7 +#define GX_MAXVTXFMT 8 + +/*! @} */ + + +/*! \addtogroup vtxattrin Vertex data input type + * @{ + */ +#define GX_NONE 0 /*!< Input data is not used */ +#define GX_DIRECT 1 /*!< Input data is set direct */ +#define GX_INDEX8 2 /*!< Input data is set by a 8bit index */ +#define GX_INDEX16 3 /*!< Input data is set by a 16bit index */ + +/*! @} */ + + +/*! \addtogroup compsize Number of components in an attribute + * @{ + */ +#define GX_U8 0 /*!< Unsigned 8-bit integer */ +#define GX_S8 1 /*!< Signed 8-bit integer */ +#define GX_U16 2 /*!< Unsigned 16-bit integer */ +#define GX_S16 3 /*!< Signed 16-bit integer */ +#define GX_F32 4 /*!< 32-bit floating-point */ +#define GX_RGB565 0 /*!< 16-bit RGB */ +#define GX_RGB8 1 /*!< 24-bit RGB */ +#define GX_RGBX8 2 /*!< 32-bit RGBX */ +#define GX_RGBA4 3 /*!< 16-bit RGBA */ +#define GX_RGBA6 4 /*!< 24-bit RGBA */ +#define GX_RGBA8 5 /*!< 32-bit RGBA */ +/*! @} */ + +/*! \addtogroup comptype Attribute component type + * @{ + */ +#define GX_POS_XY 0 /*!< X,Y position */ +#define GX_POS_XYZ 1 /*!< X,Y,Z position */ +#define GX_NRM_XYZ 0 /*!< X,Y,Z normal */ +#define GX_NRM_NBT 1 +#define GX_NRM_NBT3 2 +#define GX_CLR_RGB 0 /*!< RGB color */ +#define GX_CLR_RGBA 1 /*!< RGBA color */ +#define GX_TEX_S 0 /*!< One texture dimension */ +#define GX_TEX_ST 1 /*!< Two texture dimensions */ +/*! @} */ + +/*! \addtogroup vtxattr Vertex attribute array type + * @{ + */ +#define GX_VA_PTNMTXIDX 0 +#define GX_VA_TEX0MTXIDX 1 +#define GX_VA_TEX1MTXIDX 2 +#define GX_VA_TEX2MTXIDX 3 +#define GX_VA_TEX3MTXIDX 4 +#define GX_VA_TEX4MTXIDX 5 +#define GX_VA_TEX5MTXIDX 6 +#define GX_VA_TEX6MTXIDX 7 +#define GX_VA_TEX7MTXIDX 8 +#define GX_VA_POS 9 +#define GX_VA_NRM 10 +#define GX_VA_CLR0 11 +#define GX_VA_CLR1 12 +#define GX_VA_TEX0 13 +#define GX_VA_TEX1 14 +#define GX_VA_TEX2 15 +#define GX_VA_TEX3 16 +#define GX_VA_TEX4 17 +#define GX_VA_TEX5 18 +#define GX_VA_TEX6 19 +#define GX_VA_TEX7 20 +#define GX_POSMTXARRAY 21 +#define GX_NRMMTXARRAY 22 +#define GX_TEXMTXARRAY 23 +#define GX_LIGHTARRAY 24 +#define GX_VA_NBT 25 +#define GX_VA_MAXATTR 26 +#define GX_VA_NULL 0xff +/*! @} */ + +/*! \addtogroup primtype Primitive type + * \brief Collection of primitive types that can be drawn by the GP. + * + * \note Which type you use depends on your needs; however, performance can increase by using triangle strips or fans instead of discrete triangles. + * @{ + */ +#define GX_POINTS 0xB8 /*!< Draws a series of points. Each vertex is a single point. */ +#define GX_LINES 0xA8 /*!< Draws a series of unconnected line segments. Each pair of vertices makes a line. */ +#define GX_LINESTRIP 0xB0 /*!< Draws a series of lines. Each vertex (besides the first) makes a line between it and the previous. */ +#define GX_TRIANGLES 0x90 /*!< Draws a series of unconnected triangles. Three vertices make a single triangle. */ +#define GX_TRIANGLESTRIP 0x98 /*!< Draws a series of triangles. Each triangle (besides the first) shares a side with the previous triangle. + * Each vertex (besides the first two) completes a triangle. */ +#define GX_TRIANGLEFAN 0xA0 /*!< Draws a single triangle fan. The first vertex is the "centerpoint". The second and third vertex complete + * the first triangle. Each subsequent vertex completes another triangle which shares a side with the previous + * triangle (except the first triangle) and has the centerpoint vertex as one of the vertices. */ +#define GX_QUADS 0x80 /*!< Draws a series of unconnected quads. Every four vertices completes a quad. Internally, each quad is + * translated into a pair of triangles. */ +/*! @} */ + +#define GX_SRC_REG 0 +#define GX_SRC_VTX 1 + +/*! \addtogroup lightid Light ID + * @{ + */ +#define GX_LIGHT0 0x001 /*!< Light 0 */ +#define GX_LIGHT1 0x002 /*!< Light 2 */ +#define GX_LIGHT2 0x004 /*!< Light 3 */ +#define GX_LIGHT3 0x008 /*!< Light 4 */ +#define GX_LIGHT4 0x010 /*!< Light 5 */ +#define GX_LIGHT5 0x020 /*!< Light 6 */ +#define GX_LIGHT6 0x040 /*!< Light 7 */ +#define GX_LIGHT7 0x080 /*!< Light 8 */ +#define GX_MAXLIGHT 0x100 /*!< All lights */ +#define GX_LIGHTNULL 0x000 /*!< No lights */ +/*! @} */ + +/*! \addtogroup difffn Diffuse function + * @{ + */ +#define GX_DF_NONE 0 +#define GX_DF_SIGNED 1 +#define GX_DF_CLAMP 2 +/*! @} */ + +/*! \addtogroup attenfunc Attenuation function + * @{ + */ +#define GX_AF_SPEC 0 /*!< Specular computation */ +#define GX_AF_SPOT 1 /*!< Spot light attenuation */ +#define GX_AF_NONE 2 /*!< No attenuation */ +/*! @} */ + +/* pos,nrm,tex,dtt matrix */ +/*! \addtogroup pnmtx Position-normal matrix index + * @{ + */ +#define GX_PNMTX0 0 +#define GX_PNMTX1 3 +#define GX_PNMTX2 6 +#define GX_PNMTX3 9 +#define GX_PNMTX4 12 +#define GX_PNMTX5 15 +#define GX_PNMTX6 18 +#define GX_PNMTX7 21 +#define GX_PNMTX8 24 +#define GX_PNMTX9 27 +/*! @} */ + +/*! \addtogroup texmtx Texture matrix index + * @{ + */ +#define GX_TEXMTX0 30 +#define GX_TEXMTX1 33 +#define GX_TEXMTX2 36 +#define GX_TEXMTX3 39 +#define GX_TEXMTX4 42 +#define GX_TEXMTX5 45 +#define GX_TEXMTX6 48 +#define GX_TEXMTX7 51 +#define GX_TEXMTX8 54 +#define GX_TEXMTX9 57 +#define GX_IDENTITY 60 +/*! @} */ + +/*! \addtogroup dttmtx Post-transform texture matrix index + * @{ + */ +#define GX_DTTMTX0 64 +#define GX_DTTMTX1 67 +#define GX_DTTMTX2 70 +#define GX_DTTMTX3 73 +#define GX_DTTMTX4 76 +#define GX_DTTMTX5 79 +#define GX_DTTMTX6 82 +#define GX_DTTMTX7 85 +#define GX_DTTMTX8 88 +#define GX_DTTMTX9 91 +#define GX_DTTMTX10 94 +#define GX_DTTMTX11 97 +#define GX_DTTMTX12 100 +#define GX_DTTMTX13 103 +#define GX_DTTMTX14 106 +#define GX_DTTMTX15 109 +#define GX_DTTMTX16 112 +#define GX_DTTMTX17 115 +#define GX_DTTMTX18 118 +#define GX_DTTMTX19 121 +#define GX_DTTIDENTITY 125 +/*! @} */ + +/* tex coord id + used by: XF: 0x1040,0x1050 + BP: 0x30 +*/ +/*! \addtogroup texcoordid texture coordinate slot + * @{ + */ +#define GX_TEXCOORD0 0x0 +#define GX_TEXCOORD1 0x1 +#define GX_TEXCOORD2 0x2 +#define GX_TEXCOORD3 0x3 +#define GX_TEXCOORD4 0x4 +#define GX_TEXCOORD5 0x5 +#define GX_TEXCOORD6 0x6 +#define GX_TEXCOORD7 0x7 +#define GX_MAXCOORD 0x8 +#define GX_TEXCOORDNULL 0xff +/*! @} */ + +/* tex format */ +#define _GX_TF_ZTF 0x10 +#define _GX_TF_CTF 0x20 + +/*! \addtogroup texfmt Texture format + * @{ + */ + +#define GX_TF_I4 0x0 +#define GX_TF_I8 0x1 +#define GX_TF_IA4 0x2 +#define GX_TF_IA8 0x3 +#define GX_TF_RGB565 0x4 +#define GX_TF_RGB5A3 0x5 +#define GX_TF_RGBA8 0x6 +#define GX_TF_CI4 0x8 +#define GX_TF_CI8 0x9 +#define GX_TF_CI14 0xa +#define GX_TF_CMPR 0xE /*!< Compressed */ + +#define GX_TL_IA8 0x00 +#define GX_TL_RGB565 0x01 +#define GX_TL_RGB5A3 0x02 + +#define GX_CTF_R4 (0x0|_GX_TF_CTF) /*!< For copying 4 bits from red */ +#define GX_CTF_RA4 (0x2|_GX_TF_CTF) /*!< For copying 4 bits from red, 4 bits from alpha */ +#define GX_CTF_RA8 (0x3|_GX_TF_CTF) /*!< For copying 8 bits from red, 8 bits from alpha */ +#define GX_CTF_YUVA8 (0x6|_GX_TF_CTF) +#define GX_CTF_A8 (0x7|_GX_TF_CTF) /*!< For copying 8 bits from alpha */ +#define GX_CTF_R8 (0x8|_GX_TF_CTF) /*!< For copying 8 bits from red */ +#define GX_CTF_G8 (0x9|_GX_TF_CTF) /*!< For copying 8 bits from green */ +#define GX_CTF_B8 (0xA|_GX_TF_CTF) /*!< For copying 8 bits from blue */ +#define GX_CTF_RG8 (0xB|_GX_TF_CTF) /*!< For copying 8 bits from red, 8 bits from green */ +#define GX_CTF_GB8 (0xC|_GX_TF_CTF) /*!< For copying 8 bits from green, 8 bits from blue */ + +/*! \addtogroup ztexfmt Z Texture format + * @{ + */ + +#define GX_TF_Z8 (0x1|_GX_TF_ZTF) /*!< For texture copy, specifies upper 8 bits of Z */ +#define GX_TF_Z16 (0x3|_GX_TF_ZTF) /*!< For texture copy, specifies upper 16 bits of Z */ +#define GX_TF_Z24X8 (0x6|_GX_TF_ZTF) /*!< For texture copy, copies 24 Z bits and 0xFF */ + +/*! @} */ + +#define GX_CTF_Z4 (0x0|_GX_TF_ZTF|_GX_TF_CTF) /*!< For copying 4 upper bits from Z */ +#define GX_CTF_Z8M (0x9|_GX_TF_ZTF|_GX_TF_CTF) /*!< For copying the middle 8 bits of Z */ +#define GX_CTF_Z8L (0xA|_GX_TF_ZTF|_GX_TF_CTF) /*!< For copying the lower 8 bits of Z */ +#define GX_CTF_Z16L (0xC|_GX_TF_ZTF|_GX_TF_CTF) /*!< For copying the lower 16 bits of Z */ + +#define GX_TF_A8 GX_CTF_A8 + +/*! @} */ + +/* gx tlut size */ +#define GX_TLUT_16 1 // number of 16 entry blocks. +#define GX_TLUT_32 2 +#define GX_TLUT_64 4 +#define GX_TLUT_128 8 +#define GX_TLUT_256 16 +#define GX_TLUT_512 32 +#define GX_TLUT_1K 64 +#define GX_TLUT_2K 128 +#define GX_TLUT_4K 256 +#define GX_TLUT_8K 512 +#define GX_TLUT_16K 1024 + +/*! \addtogroup ztexop Z Texture operator + * @{ + */ + +#define GX_ZT_DISABLE 0 +#define GX_ZT_ADD 1 /*!< Add a Z texel to reference Z */ +#define GX_ZT_REPLACE 2 /*!< Replace reference Z with Z texel */ +#define GX_MAX_ZTEXOP 3 + +/*! @} */ + + +/*! \addtogroup texgentyp Texture coordinate generation type + * @{ + */ +#define GX_TG_MTX3x4 0 /*!< 2x4 matrix multiply on the input attribute and generate S,T texture coordinates. */ +#define GX_TG_MTX2x4 1 /*!< 3x4 matrix multiply on the input attribute and generate S,T,Q coordinates; S,T are then divided + * by Q to produce the actual 2D texture coordinates. */ +#define GX_TG_BUMP0 2 /*!< Use light 0 in the bump map calculation. */ +#define GX_TG_BUMP1 3 /*!< Use light 1 in the bump map calculation. */ +#define GX_TG_BUMP2 4 /*!< Use light 2 in the bump map calculation. */ +#define GX_TG_BUMP3 5 /*!< Use light 3 in the bump map calculation. */ +#define GX_TG_BUMP4 6 /*!< Use light 4 in the bump map calculation. */ +#define GX_TG_BUMP5 7 /*!< Use light 5 in the bump map calculation. */ +#define GX_TG_BUMP6 8 /*!< Use light 6 in the bump map calculation. */ +#define GX_TG_BUMP7 9 /*!< Use light 7 in the bump map calculation. */ +#define GX_TG_SRTG 10 /*!< Coordinates generated from vertex lighting results; one of the color channel results is converted + * into texture coordinates. */ +/*! @} */ + +/*! \addtogroup texgensrc Texture coordinate source + * @{ + */ +#define GX_TG_POS 0 +#define GX_TG_NRM 1 +#define GX_TG_BINRM 2 +#define GX_TG_TANGENT 3 +#define GX_TG_TEX0 4 +#define GX_TG_TEX1 5 +#define GX_TG_TEX2 6 +#define GX_TG_TEX3 7 +#define GX_TG_TEX4 8 +#define GX_TG_TEX5 9 +#define GX_TG_TEX6 10 +#define GX_TG_TEX7 11 +#define GX_TG_TEXCOORD0 12 +#define GX_TG_TEXCOORD1 13 +#define GX_TG_TEXCOORD2 14 +#define GX_TG_TEXCOORD3 15 +#define GX_TG_TEXCOORD4 16 +#define GX_TG_TEXCOORD5 17 +#define GX_TG_TEXCOORD6 18 +#define GX_TG_COLOR0 19 +#define GX_TG_COLOR1 20 +/*! @} */ + +/*! \addtogroup compare Compare type + * @{ + */ +#define GX_NEVER 0 +#define GX_LESS 1 +#define GX_EQUAL 2 +#define GX_LEQUAL 3 +#define GX_GREATER 4 +#define GX_NEQUAL 5 +#define GX_GEQUAL 6 +#define GX_ALWAYS 7 +/*! @} */ + +/* Text Wrap Mode */ +#define GX_CLAMP 0 +#define GX_REPEAT 1 +#define GX_MIRROR 2 +#define GX_MAXTEXWRAPMODE 3 + +/*! \addtogroup blendmode Blending type + * @{ + */ +#define GX_BM_NONE 0 /*!< Write input directly to EFB */ +#define GX_BM_BLEND 1 /*!< Blend using blending equation */ +#define GX_BM_LOGIC 2 /*!< Blend using bitwise operation */ +#define GX_BM_SUBTRACT 3 /*!< Input subtracts from existing pixel */ +#define GX_MAX_BLENDMODE 4 +/*! @} */ + +/*! \addtogroup blendfactor Blending control + * \details Each pixel (source or destination) is multiplied by any of these controls. + * @{ + */ +#define GX_BL_ZERO 0 /*!< 0.0 */ +#define GX_BL_ONE 1 /*!< 1.0 */ +#define GX_BL_SRCCLR 2 /*!< source color */ +#define GX_BL_INVSRCCLR 3 /*!< 1.0 - (source color) */ +#define GX_BL_SRCALPHA 4 /*!< source alpha */ +#define GX_BL_INVSRCALPHA 5 /*!< 1.0 - (source alpha) */ +#define GX_BL_DSTALPHA 6 /*!< framebuffer alpha */ +#define GX_BL_INVDSTALPHA 7 /*!< 1.0 - (FB alpha) */ +#define GX_BL_DSTCLR GX_BL_SRCCLR +#define GX_BL_INVDSTCLR GX_BL_INVSRCCLR +/*! @} */ + +/*! \addtogroup logicop Logical operation type + * \details Destination (dst) acquires the value of one of these operations, given in C syntax. + * @{ + */ +#define GX_LO_CLEAR 0 /*!< 0 */ +#define GX_LO_AND 1 /*!< src & dst */ +#define GX_LO_REVAND 2 /*!< src & ~dst */ +#define GX_LO_COPY 3 /*!< src */ +#define GX_LO_INVAND 4 /*!< ~src & dst */ +#define GX_LO_NOOP 5 /*!< dst */ +#define GX_LO_XOR 6 /*!< src ^ dst */ +#define GX_LO_OR 7 /*!< src | dst */ +#define GX_LO_NOR 8 /*!< ~(src | dst) */ +#define GX_LO_EQUIV 9 /*!< ~(src ^ dst) */ +#define GX_LO_INV 10 /*!< ~dst */ +#define GX_LO_REVOR 11 /*!< src | ~dst */ +#define GX_LO_INVCOPY 12 /*!< ~src */ +#define GX_LO_INVOR 13 /*!< ~src | dst */ +#define GX_LO_NAND 14 /*!< ~(src & dst) */ +#define GX_LO_SET 15 /*!< 1 */ +/*! @} */ + +/*! \addtogroup texoff Texture offset value + * \brief Used for texturing points or lines. + * @{ + */ +#define GX_TO_ZERO 0 +#define GX_TO_SIXTEENTH 1 +#define GX_TO_EIGHTH 2 +#define GX_TO_FOURTH 3 +#define GX_TO_HALF 4 +#define GX_TO_ONE 5 +#define GX_MAX_TEXOFFSET 6 +/*! @} */ + +/*! \addtogroup tevdefmode TEV combiner operation + * \brief Color/Alpha combiner modes for GX_SetTevOp(). + * + * \details For these equations, Cv is the output color for the stage, Cr is the output color of previous stage, and Ct is the texture color. Av is the output + * alpha for a stage, Ar is the output alpha of previous stage, and At is the texture alpha. As a special case, rasterized color + * (GX_CC_RASC) is used as Cr and rasterized alpha (GX_CA_RASA) is used as Ar at the first TEV stage because there is no previous stage. + * + * @{ + */ + +#define GX_MODULATE 0 /*!< Cv=CrCt; Av=ArAt */ +#define GX_DECAL 1 /*!< Cv=(1-At)Cr + AtCt; Av=Ar */ +#define GX_BLEND 2 /*!< Cv=(1-Ct)Cr + Ct; Av=AtAr */ +#define GX_REPLACE 3 /*!< Cv=Ct; Ar=At */ +#define GX_PASSCLR 4 /*!< Cv=Cr; Av=Ar */ + +/*! @} */ + + +/*! \addtogroup tevcolorarg TEV color combiner input + * @{ + */ + +#define GX_CC_CPREV 0 /*!< Use the color value from previous TEV stage */ +#define GX_CC_APREV 1 /*!< Use the alpha value from previous TEV stage */ +#define GX_CC_C0 2 /*!< Use the color value from the color/output register 0 */ +#define GX_CC_A0 3 /*!< Use the alpha value from the color/output register 0 */ +#define GX_CC_C1 4 /*!< Use the color value from the color/output register 1 */ +#define GX_CC_A1 5 /*!< Use the alpha value from the color/output register 1 */ +#define GX_CC_C2 6 /*!< Use the color value from the color/output register 2 */ +#define GX_CC_A2 7 /*!< Use the alpha value from the color/output register 2 */ +#define GX_CC_TEXC 8 /*!< Use the color value from texture */ +#define GX_CC_TEXA 9 /*!< Use the alpha value from texture */ +#define GX_CC_RASC 10 /*!< Use the color value from rasterizer */ +#define GX_CC_RASA 11 /*!< Use the alpha value from rasterizer */ +#define GX_CC_ONE 12 +#define GX_CC_HALF 13 +#define GX_CC_KONST 14 +#define GX_CC_ZERO 15 /*!< Use to pass zero value */ + +/*! @} */ + + +/*! \addtogroup tevalphaarg TEV alpha combiner input + * @{ + */ + +#define GX_CA_APREV 0 /*!< Use the alpha value from previous TEV stage */ +#define GX_CA_A0 1 /*!< Use the alpha value from the color/output register 0 */ +#define GX_CA_A1 2 /*!< Use the alpha value from the color/output register 1 */ +#define GX_CA_A2 3 /*!< Use the alpha value from the color/output register 2 */ +#define GX_CA_TEXA 4 /*!< Use the alpha value from texture */ +#define GX_CA_RASA 5 /*!< Use the alpha value from rasterizer */ +#define GX_CA_KONST 6 +#define GX_CA_ZERO 7 /*!< Use to pass zero value */ + +/*! @} */ + + +/*! \addtogroup tevstage TEV stage + * \details The GameCube's Graphics Processor (GP) can use up to 16 stages to compute a texel for a particular surface. + * By default, each texture will use two stages, but it can be configured through various functions calls. + * + * \note This is different from \ref texmapid s, where textures are loaded into. + * @{ + */ + +#define GX_TEVSTAGE0 0 +#define GX_TEVSTAGE1 1 +#define GX_TEVSTAGE2 2 +#define GX_TEVSTAGE3 3 +#define GX_TEVSTAGE4 4 +#define GX_TEVSTAGE5 5 +#define GX_TEVSTAGE6 6 +#define GX_TEVSTAGE7 7 +#define GX_TEVSTAGE8 8 +#define GX_TEVSTAGE9 9 +#define GX_TEVSTAGE10 10 +#define GX_TEVSTAGE11 11 +#define GX_TEVSTAGE12 12 +#define GX_TEVSTAGE13 13 +#define GX_TEVSTAGE14 14 +#define GX_TEVSTAGE15 15 +#define GX_MAX_TEVSTAGE 16 + +/*! @} */ + + +/*! \addtogroup tevop TEV combiner operator + * @{ + */ + +#define GX_TEV_ADD 0 +#define GX_TEV_SUB 1 +#define GX_TEV_COMP_R8_GT 8 +#define GX_TEV_COMP_R8_EQ 9 +#define GX_TEV_COMP_GR16_GT 10 +#define GX_TEV_COMP_GR16_EQ 11 +#define GX_TEV_COMP_BGR24_GT 12 +#define GX_TEV_COMP_BGR24_EQ 13 +#define GX_TEV_COMP_RGB8_GT 14 +#define GX_TEV_COMP_RGB8_EQ 15 +#define GX_TEV_COMP_A8_GT GX_TEV_COMP_RGB8_GT // for alpha channel +#define GX_TEV_COMP_A8_EQ GX_TEV_COMP_RGB8_EQ // for alpha channel + +/*! @} */ + + +/*! \addtogroup tevbias TEV bias value + * @{ + */ + +#define GX_TB_ZERO 0 +#define GX_TB_ADDHALF 1 +#define GX_TB_SUBHALF 2 +#define GX_MAX_TEVBIAS 3 + +/*! @} */ + + +/*! \addtogroup tevclampmode TEV clamping mode + * \note These modes are used for a function which is not implementable on production (i.e. retail) GameCube hardware. + * @{ + */ + +#define GX_TC_LINEAR 0 +#define GX_TC_GE 1 +#define GX_TC_EQ 2 +#define GX_TC_LE 3 +#define GX_MAX_TEVCLAMPMODE 4 + +/*! @} */ + + +/*! \addtogroup tevscale TEV scale value + * @{ + */ + +#define GX_CS_SCALE_1 0 +#define GX_CS_SCALE_2 1 +#define GX_CS_SCALE_4 2 +#define GX_CS_DIVIDE_2 3 +#define GX_MAX_TEVSCALE 4 + +/*! @} */ + + +/*! \addtogroup tevcoloutreg TEV color/output register + * @{ + */ + +#define GX_TEVPREV 0 /*!< Default register for passing results from one stage to another. */ +#define GX_TEVREG0 1 +#define GX_TEVREG1 2 +#define GX_TEVREG2 3 +#define GX_MAX_TEVREG 4 + +/*! @} */ + + +/*! \addtogroup cullmode Backface culling mode + * @{ + */ +#define GX_CULL_NONE 0 /*!< Do not cull any primitives. */ +#define GX_CULL_FRONT 1 /*!< Cull front-facing primitives. */ +#define GX_CULL_BACK 2 /*!< Cull back-facing primitives. */ +#define GX_CULL_ALL 3 /*!< Cull all primitives. */ +/*! @} */ + +/*! \addtogroup texmapid texture map slot + * \brief Texture map slots to hold textures in. + * + * \details The GameCube's Graphics Processor (GP) can apply up to eight textures to a single surface. Those textures + * are assigned one of these slots. Various operations used on or with a particular texture will also take one of these + * items, including operations regarding texture coordinate generation (although not necessarily on the same slot). + * + * \note This is different from \ref tevstage s, which are the actual quanta for work with textures. + * @{ + */ +#define GX_TEXMAP0 0 /*!< Texture map slot 0 */ +#define GX_TEXMAP1 1 /*!< Texture map slot 1 */ +#define GX_TEXMAP2 2 /*!< Texture map slot 2 */ +#define GX_TEXMAP3 3 /*!< Texture map slot 3 */ +#define GX_TEXMAP4 4 /*!< Texture map slot 4 */ +#define GX_TEXMAP5 5 /*!< Texture map slot 5 */ +#define GX_TEXMAP6 6 /*!< Texture map slot 6 */ +#define GX_TEXMAP7 7 /*!< Texture map slot 7 */ +#define GX_MAX_TEXMAP 8 +#define GX_TEXMAP_NULL 0xff /*!< No texmap */ +#define GX_TEXMAP_DISABLE 0x100 /*!< Disable texmap lookup for this texmap slot (use bitwise OR with a texture map slot). */ +/*! @} */ + +/*! \addtogroup alphaop Alpha combine control + * @{ + */ +#define GX_AOP_AND 0 +#define GX_AOP_OR 1 +#define GX_AOP_XOR 2 +#define GX_AOP_XNOR 3 +#define GX_MAX_ALPHAOP 4 +/*! @} */ + +/*! \addtogroup tevkcolorid TEV constant color register + * @{ + */ +#define GX_KCOLOR0 0 /*!< Constant register 0 */ +#define GX_KCOLOR1 1 /*!< Constant register 1 */ +#define GX_KCOLOR2 2 /*!< Constant register 2 */ +#define GX_KCOLOR3 3 /*!< Constant register 3 */ +#define GX_KCOLOR_MAX 4 +/*! @} */ + +/*! \addtogroup tevkcolorsel TEV constant color selection + * @{ + */ +#define GX_TEV_KCSEL_1 0x00 /*!< constant 1.0 */ +#define GX_TEV_KCSEL_7_8 0x01 /*!< constant 7/8 */ +#define GX_TEV_KCSEL_3_4 0x02 /*!< constant 3/4 */ +#define GX_TEV_KCSEL_5_8 0x03 /*!< constant 5/8 */ +#define GX_TEV_KCSEL_1_2 0x04 /*!< constant 1/2 */ +#define GX_TEV_KCSEL_3_8 0x05 /*!< constant 3/8 */ +#define GX_TEV_KCSEL_1_4 0x06 /*!< constant 1/4 */ +#define GX_TEV_KCSEL_1_8 0x07 /*!< constant 1/8 */ +#define GX_TEV_KCSEL_K0 0x0C /*!< K0[RGB] register */ +#define GX_TEV_KCSEL_K1 0x0D /*!< K1[RGB] register */ +#define GX_TEV_KCSEL_K2 0x0E /*!< K2[RGB] register */ +#define GX_TEV_KCSEL_K3 0x0F /*!< K3[RGB] register */ +#define GX_TEV_KCSEL_K0_R 0x10 /*!< K0[RRR] register */ +#define GX_TEV_KCSEL_K1_R 0x11 /*!< K1[RRR] register */ +#define GX_TEV_KCSEL_K2_R 0x12 /*!< K2[RRR] register */ +#define GX_TEV_KCSEL_K3_R 0x13 /*!< K3[RRR] register */ +#define GX_TEV_KCSEL_K0_G 0x14 /*!< K0[GGG] register */ +#define GX_TEV_KCSEL_K1_G 0x15 /*!< K1[GGG] register */ +#define GX_TEV_KCSEL_K2_G 0x16 /*!< K2[GGG] register */ +#define GX_TEV_KCSEL_K3_G 0x17 /*!< K3[GGG] register */ +#define GX_TEV_KCSEL_K0_B 0x18 /*!< K0[BBB] register */ +#define GX_TEV_KCSEL_K1_B 0x19 /*!< K1[BBB] register */ +#define GX_TEV_KCSEL_K2_B 0x1A /*!< K2[BBB] register */ +#define GX_TEV_KCSEL_K3_B 0x1B /*!< K3[RBB] register */ +#define GX_TEV_KCSEL_K0_A 0x1C /*!< K0[AAA] register */ +#define GX_TEV_KCSEL_K1_A 0x1D /*!< K1[AAA] register */ +#define GX_TEV_KCSEL_K2_A 0x1E /*!< K2[AAA] register */ +#define GX_TEV_KCSEL_K3_A 0x1F /*!< K3[AAA] register */ +/*! @} */ + +/*! \addtogroup tevkalphasel TEV constant alpha selection + * @{ + */ +#define GX_TEV_KASEL_1 0x00 /*!< constant 1.0 */ +#define GX_TEV_KASEL_7_8 0x01 /*!< constant 7/8 */ +#define GX_TEV_KASEL_3_4 0x02 /*!< constant 3/4 */ +#define GX_TEV_KASEL_5_8 0x03 /*!< constant 5/8 */ +#define GX_TEV_KASEL_1_2 0x04 /*!< constant 1/2 */ +#define GX_TEV_KASEL_3_8 0x05 /*!< constant 3/8 */ +#define GX_TEV_KASEL_1_4 0x06 /*!< constant 1/4 */ +#define GX_TEV_KASEL_1_8 0x07 /*!< constant 1/8 */ +#define GX_TEV_KASEL_K0_R 0x10 /*!< K0[R] register */ +#define GX_TEV_KASEL_K1_R 0x11 /*!< K1[R] register */ +#define GX_TEV_KASEL_K2_R 0x12 /*!< K2[R] register */ +#define GX_TEV_KASEL_K3_R 0x13 /*!< K3[R] register */ +#define GX_TEV_KASEL_K0_G 0x14 /*!< K0[G] register */ +#define GX_TEV_KASEL_K1_G 0x15 /*!< K1[G] register */ +#define GX_TEV_KASEL_K2_G 0x16 /*!< K2[G] register */ +#define GX_TEV_KASEL_K3_G 0x17 /*!< K3[G] register */ +#define GX_TEV_KASEL_K0_B 0x18 /*!< K0[B] register */ +#define GX_TEV_KASEL_K1_B 0x19 /*!< K1[B] register */ +#define GX_TEV_KASEL_K2_B 0x1A /*!< K2[B] register */ +#define GX_TEV_KASEL_K3_B 0x1B /*!< K3[B] register */ +#define GX_TEV_KASEL_K0_A 0x1C /*!< K0[A] register */ +#define GX_TEV_KASEL_K1_A 0x1D /*!< K1[A] register */ +#define GX_TEV_KASEL_K2_A 0x1E /*!< K2[A] register */ +#define GX_TEV_KASEL_K3_A 0x1F /*!< K3[A] register */ +/*! @} */ + + +/*! \addtogroup tevswapsel TEV color swap table entry + * @{ + */ + +#define GX_TEV_SWAP0 0 +#define GX_TEV_SWAP1 1 +#define GX_TEV_SWAP2 2 +#define GX_TEV_SWAP3 3 +#define GX_MAX_TEVSWAP 4 + +/*! @} */ + + +/* tev color chan */ +#define GX_CH_RED 0 +#define GX_CH_GREEN 1 +#define GX_CH_BLUE 2 +#define GX_CH_ALPHA 3 + +/*! \addtogroup indtexstage Indirect texture stage + * @{ + */ +#define GX_INDTEXSTAGE0 0 +#define GX_INDTEXSTAGE1 1 +#define GX_INDTEXSTAGE2 2 +#define GX_INDTEXSTAGE3 3 +#define GX_MAX_INDTEXSTAGE 4 +/*! @} */ + +/*! \addtogroup indtexformat Indirect texture format + * \details Bits for the indirect offsets are extracted from the high end of each component byte. Bits for the bump alpha + * are extraced off the low end of the byte. For GX_ITF_8, the byte is duplicated for the offset and the bump alpha. + * @{ + */ +#define GX_ITF_8 0 +#define GX_ITF_5 1 +#define GX_ITF_4 2 +#define GX_ITF_3 3 +#define GX_MAX_ITFORMAT 4 +/*! @} */ + +/*! \addtogroup indtexbias Indirect texture bias select + * \brief Indicates which components of the indirect offset should receive a bias value. + * + * \details The bias is fixed at -128 for GX_ITF_8 and +1 for the other formats. The bias happens prior to the indirect matrix multiply. + * @{ + */ +#define GX_ITB_NONE 0 +#define GX_ITB_S 1 +#define GX_ITB_T 2 +#define GX_ITB_ST 3 +#define GX_ITB_U 4 +#define GX_ITB_SU 5 +#define GX_ITB_TU 6 +#define GX_ITB_STU 7 +#define GX_MAX_ITBIAS 8 +/*! @} */ + +/*! \addtogroup indtexmtx Indirect texture matrix + * @{ + */ +#define GX_ITM_OFF 0 /*!< Specifies a matrix of all zeroes. */ +#define GX_ITM_0 1 /*!< Specifies indirect matrix 0, indirect scale 0. */ +#define GX_ITM_1 2 /*!< Specifies indirect matrix 1, indirect scale 1. */ +#define GX_ITM_2 3 /*!< Specifies indirect matrix 2, indirect scale 2. */ +#define GX_ITM_S0 5 /*!< Specifies dynamic S-type matrix, indirect scale 0. */ +#define GX_ITM_S1 6 /*!< Specifies dynamic S-type matrix, indirect scale 1. */ +#define GX_ITM_S2 7 /*!< Specifies dynamic S-type matrix, indirect scale 2. */ +#define GX_ITM_T0 9 /*!< Specifies dynamic T-type matrix, indirect scale 0. */ +#define GX_ITM_T1 10 /*!< Specifies dynamic T-type matrix, indirect scale 1. */ +#define GX_ITM_T2 11 /*!< Specifies dynamic T-type matrix, indirect scale 2. */ +/*! @} */ + +/*! \addtogroup indtexwrap Indirect texture wrap value + * \brief Indicates whether the regular texture coordinate should be wrapped before being added to the offset. + * + * \details GX_ITW_OFF specifies no wrapping. GX_ITW_0 will zero out the regular texture coordinate. + * @{ + */ +#define GX_ITW_OFF 0 +#define GX_ITW_256 1 +#define GX_ITW_128 2 +#define GX_ITW_64 3 +#define GX_ITW_32 4 +#define GX_ITW_16 5 +#define GX_ITW_0 6 +#define GX_MAX_ITWRAP 7 +/*! @} */ + +/*! \addtogroup indtexalphasel Indirect texture bump alpha select + * \brief Indicates which offset component should provide the "bump" alpha output for the given TEV stage. + * + * \note Bump alpha is not available for TEV stage 0. + * @{ + */ +#define GX_ITBA_OFF 0 +#define GX_ITBA_S 1 +#define GX_ITBA_T 2 +#define GX_ITBA_U 3 +#define GX_MAX_ITBALPHA 4 +/*! @} */ + +/*! \addtogroup indtexscale Indirect texture scale + * \brief Specifies an additional scale value that may be applied to the texcoord used for an indirect initial lookup (not a TEV stage regular lookup). + * + * \details The scale value is a fraction; thus GX_ITS_32 means to divide the texture coordinate values by 32. + * @{ + */ +#define GX_ITS_1 0 +#define GX_ITS_2 1 +#define GX_ITS_4 2 +#define GX_ITS_8 3 +#define GX_ITS_16 4 +#define GX_ITS_32 5 +#define GX_ITS_64 6 +#define GX_ITS_128 7 +#define GX_ITS_256 8 +#define GX_MAX_ITSCALE 9 +/*! @} */ + +/*! \addtogroup fogtype Fog equation control + * @{ + */ +#define GX_FOG_NONE 0 + +#define GX_FOG_PERSP_LIN 2 +#define GX_FOG_PERSP_EXP 4 +#define GX_FOG_PERSP_EXP2 5 +#define GX_FOG_PERSP_REVEXP 6 +#define GX_FOG_PERSP_REVEXP2 7 + +#define GX_FOG_ORTHO_LIN 10 +#define GX_FOG_ORTHO_EXP 12 +#define GX_FOG_ORTHO_EXP2 13 +#define GX_FOG_ORTHO_REVEXP 14 +#define GX_FOG_ORTHO_REVEXP2 15 + +#define GX_FOG_LIN GX_FOG_PERSP_LIN +#define GX_FOG_EXP GX_FOG_PERSP_EXP +#define GX_FOG_EXP2 GX_FOG_PERSP_EXP2 +#define GX_FOG_REVEXP GX_FOG_PERSP_REVEXP +#define GX_FOG_REVEXP2 GX_FOG_PERSP_REVEXP2 +/*! @} */ + + +/* pixel format */ +#define GX_PF_RGB8_Z24 0 +#define GX_PF_RGBA6_Z24 1 +#define GX_PF_RGB565_Z16 2 +#define GX_PF_Z24 3 +#define GX_PF_Y8 4 +#define GX_PF_U8 5 +#define GX_PF_V8 6 +#define GX_PF_YUV420 7 + +/*! \addtogroup zfmt Compressed Z format + * @{ + */ +#define GX_ZC_LINEAR 0 +#define GX_ZC_NEAR 1 +#define GX_ZC_MID 2 +#define GX_ZC_FAR 3 +/*! @} */ + +/*! \addtogroup xfbclamp XFB clamp modes + * @{ + */ + +#define GX_CLAMP_NONE 0 +#define GX_CLAMP_TOP 1 +#define GX_CLAMP_BOTTOM 2 + +/*! @} */ + + +/*! \addtogroup gammamode Gamma values + * @{ + */ + +#define GX_GM_1_0 0 +#define GX_GM_1_7 1 +#define GX_GM_2_2 2 + +/*! @} */ + + +/*! \addtogroup copymode EFB copy mode + * \brief Controls whether all lines, only even lines, or only odd lines are copied from the EFB. + * @{ + */ +#define GX_COPY_PROGRESSIVE 0 +#define GX_COPY_INTLC_EVEN 2 +#define GX_COPY_INTLC_ODD 3 +/*! @} */ + +/*! \addtogroup alphareadmode Alpha read mode + * @{ + */ +#define GX_READ_00 0 /*!< Always read 0x00. */ +#define GX_READ_FF 1 /*!< Always read 0xFF. */ +#define GX_READ_NONE 2 /*!< Always read the real alpha value. */ +/*! @} */ + +/*! \addtogroup texcachesize Texture cache size + * \brief Size of texture cache regions. + * @{ + */ +#define GX_TEXCACHE_32K 0 +#define GX_TEXCACHE_128K 1 +#define GX_TEXCACHE_512K 2 +#define GX_TEXCACHE_NONE 3 +/*! @} */ + +/*! \addtogroup distattnfn Brightness decreasing function + * \brief Type of the brightness decreasing function by distance. + * @{ + */ +#define GX_DA_OFF 0 +#define GX_DA_GENTLE 1 +#define GX_DA_MEDIUM 2 +#define GX_DA_STEEP 3 +/*! @} */ + +/*! \addtogroup spotfn Spot illumination distribution function + * @{ + */ +#define GX_SP_OFF 0 +#define GX_SP_FLAT 1 +#define GX_SP_COS 2 +#define GX_SP_COS2 3 +#define GX_SP_SHARP 4 +#define GX_SP_RING1 5 +#define GX_SP_RING2 6 +/*! @} */ + +/*! \addtogroup texfilter Texture filter types + * @{ + */ +#define GX_NEAR 0 /*!< Point sampling, no mipmap */ +#define GX_LINEAR 1 /*!< Bilinear filtering, no mipmap */ +#define GX_NEAR_MIP_NEAR 2 /*!< Point sampling, discrete mipmap */ +#define GX_LIN_MIP_NEAR 3 /*!< Bilinear filtering, discrete mipmap */ +#define GX_NEAR_MIP_LIN 4 /*!< Point sampling, linear mipmap */ +#define GX_LIN_MIP_LIN 5 /*!< Trilinear filtering */ +/*! @} */ + +/*! \addtogroup anisotropy Maximum anisotropy filter control + * @{ + */ +#define GX_ANISO_1 0 +#define GX_ANISO_2 1 +#define GX_ANISO_4 2 +#define GX_MAX_ANISOTROPY 3 +/*! @} */ + +/*! \addtogroup vcachemetrics Vertex cache performance counter + * @{ + */ +#define GX_VC_POS 0 +#define GX_VC_NRM 1 +#define GX_VC_CLR0 2 +#define GX_VC_CLR1 3 +#define GX_VC_TEX0 4 +#define GX_VC_TEX1 5 +#define GX_VC_TEX2 6 +#define GX_VC_TEX3 7 +#define GX_VC_TEX4 8 +#define GX_VC_TEX5 9 +#define GX_VC_TEX6 10 +#define GX_VC_TEX7 11 +#define GX_VC_ALL 15 +/*! @} */ + +/*! \addtogroup perf0metrics Performance counter 0 metric + * \details Performance counter 0 is used to measure attributes dealing with geometry and primitives, such as triangle counts and clipping ratios. + * + * \note GX_PERF0_XF_* measure how many GP cycles are spent in each stage of the XF.

+ * + * \note The triangle metrics (GX_PERF0_TRIANGLES_*) allow counting triangles under specific conditions or with specific attributes.

+ * + * \note GX_PERF0_TRIANGLES_*TEX count triangles based on the number of texture coordinates supplied; GX_PERF0_TRIANGLES_*CLR count + * triangles based on the number of colors supplied.

+ * + * \note The quad metrics allow you to count the number of quads (2x2 pixels) the GP processes. The term coverage is used to indicate how many + * pixels in the quad are actually part of the triangle being rasterized. For example, a coverage of 4 means all pixels in the quad intersect the + * triangle. A coverage of 1 indicates that only 1 pixel in the quad intersected the triangle. + * @{ + */ +#define GX_PERF0_VERTICES 0 /*!< Number of vertices processed by the GP. */ +#define GX_PERF0_CLIP_VTX 1 /*!< Number of vertices that were clipped by the GP. */ +#define GX_PERF0_CLIP_CLKS 2 /*!< Number of GP clocks spent clipping. */ +#define GX_PERF0_XF_WAIT_IN 3 /*!< Number of cycles the XF is waiting on input. If the XF is waiting a large percentage + * of the total time, it may indicate that the CPU is not supplying data fast enough to + * keep the GP busy. */ +#define GX_PERF0_XF_WAIT_OUT 4 /*!< Number of cycles the XF waits to send its output to the rest of the GP pipeline. If + * the XF cannot output, it may indicate that the GP is currently fill-rate limited. */ +#define GX_PERF0_XF_XFRM_CLKS 5 /*!< Number of cycles the transform engine is busy. */ +#define GX_PERF0_XF_LIT_CLKS 6 /*!< Number of cycles the lighting engine is busy. */ +#define GX_PERF0_XF_BOT_CLKS 7 /*!< Number of cycles the bottom of the pipe (result combiner) is busy. */ +#define GX_PERF0_XF_REGLD_CLKS 8 /*!< Number of cycles are spent loading XF state registers. */ +#define GX_PERF0_XF_REGRD_CLKS 9 /*!< Number of cycles the XF reads the state registers. */ +#define GX_PERF0_CLIP_RATIO 10 +#define GX_PERF0_TRIANGLES 11 /*!< Number of triangles. */ +#define GX_PERF0_TRIANGLES_CULLED 12 /*!< Number of triangles that failed the front-face/back-face culling test. */ +#define GX_PERF0_TRIANGLES_PASSED 13 /*!< Number of triangles that passed the front-face/back-face culling test. */ +#define GX_PERF0_TRIANGLES_SCISSORED 14 /*!< Number of triangles that are scissored. */ +#define GX_PERF0_TRIANGLES_0TEX 15 +#define GX_PERF0_TRIANGLES_1TEX 16 +#define GX_PERF0_TRIANGLES_2TEX 17 +#define GX_PERF0_TRIANGLES_3TEX 18 +#define GX_PERF0_TRIANGLES_4TEX 19 +#define GX_PERF0_TRIANGLES_5TEX 20 +#define GX_PERF0_TRIANGLES_6TEX 21 +#define GX_PERF0_TRIANGLES_7TEX 22 +#define GX_PERF0_TRIANGLES_8TEX 23 +#define GX_PERF0_TRIANGLES_0CLR 24 +#define GX_PERF0_TRIANGLES_1CLR 25 +#define GX_PERF0_TRIANGLES_2CLR 26 +#define GX_PERF0_QUAD_0CVG 27 /*!< Number of quads having zero coverage. */ +#define GX_PERF0_QUAD_NON0CVG 28 /*!< Number of quads having coverage greater than zero. */ +#define GX_PERF0_QUAD_1CVG 29 /*!< Number of quads with 1 pixel coverage. */ +#define GX_PERF0_QUAD_2CVG 30 /*!< Number of quads with 2 pixel coverage. */ +#define GX_PERF0_QUAD_3CVG 31 /*!< Number of quads with 3 pixel coverage. */ +#define GX_PERF0_QUAD_4CVG 32 /*!< Number of quads with 4 pixel coverage. */ +#define GX_PERF0_AVG_QUAD_CNT 33 /*!< Average quad count; average based on what is unknown */ +#define GX_PERF0_CLOCKS 34 /*!< Number of GP clocks that have elapsed since the previous call to GX_ReadGP0Metric(). */ +#define GX_PERF0_NONE 35 /*!< Disables performance measurement for perf0 and resets the counter. */ +/*! @} */ + +/*! \addtogroup perf1metrics Performance counter 1 metric + * \details Performance counter 1 is used for measuring texturing and caching performance as well as FIFO performance. + * + * \note GX_PERF1_TC_* can be used to compute the texture cache (TC) miss rate. The TC_CHECK* parameters count how many texture cache lines are + * accessed for each pixel. In the worst case, for a mipmap, up to 8 cache lines may be accessed to produce one textured pixel. + * GX_PERF1_TC_MISS counts how many of those accesses missed the texture cache. To compute the miss rate, divide GX_PERF1_TC_MISS by the sum of all four + * GX_PERF1_TC_CHECK* values.

+ * + * \note GX_PERF1_VC_* count different vertex cache stall conditions. + * @{ + */ +#define GX_PERF1_TEXELS 0 /*!< Number of texels processed by the GP. */ +#define GX_PERF1_TX_IDLE 1 /*!< Number of clocks that the texture unit (TX) is idle. */ +#define GX_PERF1_TX_REGS 2 /*!< Number of GP clocks spent writing to state registers in the TX unit. */ +#define GX_PERF1_TX_MEMSTALL 3 /*!< Number of GP clocks the TX unit is stalled waiting for main memory. */ +#define GX_PERF1_TC_CHECK1_2 4 +#define GX_PERF1_TC_CHECK3_4 5 +#define GX_PERF1_TC_CHECK5_6 6 +#define GX_PERF1_TC_CHECK7_8 7 +#define GX_PERF1_TC_MISS 8 /*!< Number of texture cache misses in total? */ +#define GX_PERF1_VC_ELEMQ_FULL 9 +#define GX_PERF1_VC_MISSQ_FULL 10 +#define GX_PERF1_VC_MEMREQ_FULL 11 +#define GX_PERF1_VC_STATUS7 12 +#define GX_PERF1_VC_MISSREP_FULL 13 +#define GX_PERF1_VC_STREAMBUF_LOW 14 +#define GX_PERF1_VC_ALL_STALLS 15 +#define GX_PERF1_VERTICES 16 /*!< Number of vertices processed by the GP. */ +#define GX_PERF1_FIFO_REQ 17 /*!< Number of lines (32B) read from the GP FIFO. */ +#define GX_PERF1_CALL_REQ 18 /*!< Number of lines (32B) read from called display lists. */ +#define GX_PERF1_VC_MISS_REQ 19 /*!< Number vertex cache miss request. Each miss requests a 32B transfer from main memory. */ +#define GX_PERF1_CP_ALL_REQ 20 /*!< Counts all requests (32B/request) from the GP Command Processor (CP). It should be equal to + * the sum of counts returned by GX_PERF1_FIFO_REQ, GX_PERF1_CALL_REQ, and GX_PERF1_VC_MISS_REQ. */ +#define GX_PERF1_CLOCKS 21 /*!< Number of GP clocks that have elapsed since the last call to GX_ReadGP1Metric(). */ +#define GX_PERF1_NONE 22 /*!< Disables performance measurement for perf1 and resets the counter. */ +/*! @} */ + +/*! \addtogroup tlutname TLUT name + * \brief Name of Texture Look-Up Table (TLUT) in texture memory. + * + * \details Each table GX_TLUT0-GX_TLUT15 contains 256 entries,16b per entry. GX_BIGTLUT0-3 + * contains 1024 entries, 16b per entry. Used for configuring texture memory in GX_Init(). + * @{ + */ +#define GX_TLUT0 0 +#define GX_TLUT1 1 +#define GX_TLUT2 2 +#define GX_TLUT3 3 +#define GX_TLUT4 4 +#define GX_TLUT5 5 +#define GX_TLUT6 6 +#define GX_TLUT7 7 +#define GX_TLUT8 8 +#define GX_TLUT9 9 +#define GX_TLUT10 10 +#define GX_TLUT11 11 +#define GX_TLUT12 12 +#define GX_TLUT13 13 +#define GX_TLUT14 14 +#define GX_TLUT15 15 +#define GX_BIGTLUT0 16 +#define GX_BIGTLUT1 17 +#define GX_BIGTLUT2 18 +#define GX_BIGTLUT3 19 +/*! @} */ + +#define GX_MAX_VTXDESC GX_VA_MAXATTR +#define GX_MAX_VTXDESC_LISTSIZE (GX_VA_MAXATTR+1) + +#define GX_MAX_VTXATTRFMT GX_VA_MAXATTR +#define GX_MAX_VTXATTRFMT_LISTSIZE (GX_VA_MAXATTR+1) + +#define GX_MAX_Z24 0x00ffffff + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef union _wgpipe +{ + vu8 U8; + vs8 S8; + vu16 U16; + vs16 S16; + vu32 U32; + vs32 S32; + vf32 F32; +} WGPipe; + +/*! \typedef struct _gx_color GXColor + * \brief Structure used to pass colors to some GX functions. + */ +typedef struct _gx_color { + u8 r; /*!< Red color component. */ + u8 g; /*!< Green color component. */ + u8 b; /*!< Blue alpha component. */ + u8 a; /*!< Alpha component. If a function does not use the alpha value, it is safely ignored. */ +} GXColor; + +/*! \typedef struct _gx_colors10 GXColorS10 + * \brief Structure used to pass signed 10-bit colors to some GX functions. + */ +typedef struct _gx_colors10 { + s16 r; /*!< Red color component. */ + s16 g; /*!< Green color component. */ + s16 b; /*!< Blue color component. */ + s16 a; /*!< Alpha component. If a function does not use the alpha value, it is safely ignored. */ +} GXColorS10; + +/*! \typedef struct _gx_texobj GXTexObj + * \brief Object containing information about a texture. + * + * \details This structure contains precompiled register state setting commands and data. The application must use the GX_InitTexObj*() + * function to initialize or change this object. The proper size of the object is returned by + * + * \code sizeof(GXTexObj) \endcode + * + * \details but the internal data representation is not visible to the application. + */ +typedef struct _gx_texobj { + u32 val[8]; +} GXTexObj; + +/*! \typedef struct _gx_tlutobj GXTlutObj + * \brief Object containing information on a TLUT. + * + * \details This structure contains precompiled register state setting commands and data. The application must use the GX_InitTlutObj() + * function to initialize or change this object. The proper size of the object is returned by + * + * \code sizeof(GXTlutObj) \endcode + * + * \details but the internal data representation is not visible to the application. + */ +typedef struct _gx_tlutobj { + u32 val[3]; +} GXTlutObj; + +/*! \typedef struct _gx_texreg GXTexRegion + * \brief Object containing information on a texture cache region. + * + * \details This structure contains precompiled register state setting commands and data. The application must use the + * GX_InitTexCacheRegion() function to initialize or change this object. The proper size of the object is returned by + * + * \code sizeof(GXTexRegion) \endcode + * + * \details but the internal data representation is not visible to the application. + */ +typedef struct _gx_texreg { + u32 val[4]; +} GXTexRegion; + +/*! \typedef struct _gx_tlutreg GXTlutRegion + * \brief Object containing information on a TLUT cache region. + * + * \details This structure contains precompiled register state setting commands and data. The application must use the GX_InitTlutRegion() + * function to initialize or change this object. The proper size of the object is returned by + * + * \code sizeof(GXTlutRegion) \endcode + * + * \details but the internal data representation is not visible to the application. + */ +typedef struct _gx_tlutreg { + u32 val[4]; +} GXTlutRegion; + +/*! \typedef _gx_litobj GXLightObj + * \brief Object containing information on a light. + * + * \details This structure contains precompiled register state setting commands and data. The application must use the GX_InitLight*() functions + * to initialize or change this object. The proper size of the object is returned by + * + * \code sizeof(GXLightObj) \endcode + * + * \details but the internal data representation is not visible to the application. + */ +typedef struct _gx_litobj { + u32 val[16]; +} GXLightObj; + +typedef struct _vtx { + f32 x,y,z; + u16 s,t; + u32 rgba; +} Vtx; + +/*! \struct GXVtxDesc + * \brief Structure describing how a single vertex attribute will be referenced. + * + * \details An array of these structures can be used to describe all the attributes in a vertex. The attribute GX_VA_NULL should be + * used to mark the end of the array. + */ +typedef struct { + u8 attr; /*!< \ref vtxattr for this element. */ + u8 type; /*!< \ref vtxattrin for this element. */ +} GXVtxDesc; + +/*! \struct GXVtxAttrFmt + * \brief Structure describing the attribute format for one attribute. + * + * \details An array of these structures can be used to describe the format of all attributes in a vertex. The attribute GX_VA_NULL + * should be used to mark the end of the array. + */ +typedef struct { + u32 vtxattr; /*!< \ref vtxattr for this element. */ + u32 comptype; /*!< \ref comptype for this element. */ + u32 compsize; /*!< \ref compsize for this element. */ + u32 frac; /*!< Number of fractional bits for a fixed-point number. */ +} GXVtxAttrFmt; + +/*! \struct GXFifoObj + * \brief Object describing a graphics FIFO. + * + * \details This structure contains precompiled register state setting commands and data. The application must use the GX_InitFifo*() functions + * to initialize or change this object. The proper size of the object is returned by + * + * \code sizeof(GXFifoObj) \endcode + * + * but the internal data representation is not visible to the application. + */ +typedef struct { + u8 pad[GX_FIFO_OBJSIZE]; +} GXFifoObj; + +typedef struct { + u8 dummy[4]; +} GXTexReg; + +/*! \struct GXFogAdjTbl + * \brief Fog range adjustment parameter table. + */ +typedef struct { + u16 r[10]; /*!< u4.8 format range parameter. */ +} GXFogAdjTbl; + +/*! \typedef void (*GXBreakPtCallback)(void) + * \brief function pointer typedef for the GP breakpoint-token callback + */ +typedef void (*GXBreakPtCallback)(void); + +/*! \typedef void (*GXDrawDoneCallback)(void) + * \brief function pointer typedef for the GP drawdone-token callback + */ +typedef void (*GXDrawDoneCallback)(void); + +/*! \typedef void (*GXDrawSyncCallback)(u16 token) + * \brief function pointer typedef for the drawsync-token callback + * \param[out] token tokenvalue most recently encountered. + */ +typedef void (*GXDrawSyncCallback)(u16 token); + +/*! \typedef GXTexRegion* (*GXTexRegionCallback)(GXTexObj *obj,u8 mapid) + * \brief function pointer typedef for the texture region callback + * \param[out] token tokenvalue most recently encountered. + */ +typedef GXTexRegion* (*GXTexRegionCallback)(GXTexObj *obj,u8 mapid); + +/*! \typedef GXTlutRegion* (*GXTlutRegionCallback)(u32 tlut_name) + * \brief function pointer typedef for the TLUT region callback + * \param[out] token tokenvalue most recently encountered. + */ +typedef GXTlutRegion* (*GXTlutRegionCallback)(u32 tlut_name); + +extern WGPipe* const wgPipe; +/*! + * \fn GXFifoObj* GX_Init(void *base,u32 size) + * \brief Initializes the graphics processor to its initial state. + * + * \details This function sets the default state of the graphics processor and should be called before any other GX functions. + * This function sets up an immediate-mode method of communicating graphics commands from the CPU to the Graphics Processor + * (GP). This function will initialize a FIFO and attach it to both the CPU and GP. The CPU will write commands to the FIFO + * and the GP will read the commands. This function returns a pointer to the initialized FIFO. The application must allocate + * the memory for the FIFO. The parameter \a base is a pointer to the allocated main memory and must be aligned to 32B. \a size + * is the size of the FIFO in bytes and must be a multiple of 32B. Refer to additional notes in GX_InitFifoBase() concerning + * the FIFO memory. + * + * \note It is also possible to override the default immediate-mode style and instead buffer the graphics for frame n+1 + * while the GP is reading the graphics for frame n. See GX_SetCPUFifo() and GX_SetGPFifo() for further information.

+ * + * \note This function also designates the calling thread as the default GX thread; i.e., it assumes the calling thread is the + * one responsible for generating graphics data. This thread will be the thread to be suspended when the FIFO gets too full. + * The current GX thread can be changed by calling GX_SetCurrentGXThread(). + * + * \param[in] base pointer to the GX FIFO buffer base address. Must be aligned on a 32 Byte boundery. + * \param[in] size size of buffer. Must be a multiple of 32. + * + * \return pointer to the intialized GXFifoObj object. + */ +GXFifoObj* GX_Init(void *base,u32 size); + +/*! + * \fn void GX_InitFifoBase(GXFifoObj *fifo,void *base,u32 size) + * \brief Describes the area of main memory that will be used for this \a fifo. + * + * The Graphics FIFO is the mechanism used to communicate graphics commands from the CPU to the Graphics Processor (GP). The FIFO + * base pointer should be 32-byte aligned. memalign() can return 32-byte aligned pointers. The size should also be a multiple of + * 32B. + * + * The CPU's write-gather pipe is used to write data to the FIFO. Therefore, the FIFO memory area must be forced out of the CPU + * cache prior to being used. DCInvalidateRange() may be used for this purpose. Due to the mechanics of flushing the write-gather + * pipe, the FIFO memory area should be at least 32 bytes larger than the maximum expected amount of data stored. Up to 32 NOPs + * may be written at the end during flushing. + * + * \note GX_Init() also takes the arguments \a base and \a size and initializes a FIFO using these values and attaches the FIFO + * to both the CPU and GP. The application must allocate the memory for the graphics FIFO before calling GX_Init(). Therefore, it + * is not necessary to call this function unless you want to resize the default FIFO sometime after GX_Init() has been called or + * you are creating a new FIFO. The minimum size is 64kB defined by GX_FIFO_MINSIZE.

+ * + * \note This function will also set the read and write pointers for the FIFO to the base address, so ordinarily it is not + * necessary to call GX_InitFifoPtrs() when initializing the FIFO. In addition, This function sets the FIFO's high water mark to + * (size-16kB) and the low water mark to (size/2), so it is also not necessary to call GX_InitFifoLimits(). + * + * \param[in] fifo the fifo struct to use + * \param[in] base ptr to the base of allocation; must be 32-byte aligned + * \param[in] size size of the FIFO in bytes; must be multiple of 32; size must be GX_FIFO_MINSIZE or larger + * + * \return none + */ +void GX_InitFifoBase(GXFifoObj *fifo,void *base,u32 size); + +/*! + * \fn void GX_InitFifoLimits(GXFifoObj *fifo,u32 hiwatermark,u32 lowatermark) + * \brief Sets the high and low water mark for the \a fifo. + * + * \details The high and low water marks are used in immediate-mode, i.e. when the \a fifo is attached to both the CPU and + * Graphics Processor (GP) (see GX_SetCPUFifo() and GX_SetGPFifo()). + * + * The hardware keeps track of the number of bytes between the read and write pointers. This number represents how full the FIFO is, + * and when it is greater than or equal to the \a hiwatermark, the hardware issues an interrupt. The GX API will suspend sending + * graphics to the Graphics FIFO until it has emptied to a certain point. The \a lowatermark is used to set the point at which the + * FIFO is empty enough to resume sending graphics commands to the FIFO. Both \a hiwatermark and \a lowatermark should be in + * multiples of 32B. The count for \a lowatermark should be less than \a hiwatermark. Of course, \a hiwatermark and \a lowatermark + * must be less than the size of the FIFO. + * + * \note When the FIFO is only attached to the CPU or only attached to the GP, the high and low water mark interrupts are disabled. + * + * \param[in] fifo the fifo struct to use + * \param[in] hiwatermark number of bytes to be queued before libogc stops writing commands to the FIFO + * \param[in] lowatermark number of bytes to be queued before libogc resumes writing commands to the FIFO + * + * \return none + */ +void GX_InitFifoLimits(GXFifoObj *fifo,u32 hiwatermark,u32 lowatermark); + +/*! + * \fn void GX_InitFifoPtrs(GXFifoObj *fifo,void *rd_ptr,void *wt_ptr) + * \brief Sets the \a fifo read and write pointers. + * + * \note This is normally done only during initialization of the FIFO. After that, the graphics hardware manages the FIFO pointers. + * + * \param[in] fifo the fifo struct to use + * \param[in] rd_ptr the pointer to use for the FIFO read pointer; must be 32-byte aligned + * \param[in] wt_ptr the pointer to use for the FIFO write pointer; must be 32-byte aligned + * + * \return none + */ +void GX_InitFifoPtrs(GXFifoObj *fifo,void *rd_ptr,void *wt_ptr); + +/*! + * \fn void GX_GetFifoPtrs(GXFifoObj *fifo,void **rd_ptr,void **wt_ptr) + * \brief Returns the current value of the Graphics FIFO read and write pointers. + * + * \note See GX_EnableBreakPt() for an example of why you would do this. + * + * \param[in] fifo pointer to a FIFO struct + * \param[out] rd_ptr address of the FIFO read pointer + * \param[out] wt_ptr address of the FIFO write pointer + * + * \return none + */ +void GX_GetFifoPtrs(GXFifoObj *fifo,void **rd_ptr,void **wt_ptr); + +/*! + * \fn void GX_SetCPUFifo(GXFifoObj *fifo) + * \brief Attaches a FIFO to the CPU. + * + * \note If the FIFO being attached is one already attached to the GP, the FIFO can be considered to be in immediate mode. If not, + * the CPU can write commands, and the GP will execute them when the GP attaches to this FIFO (multi-buffered mode). + * + * \param[in] fifo fifo struct containing info on the FIFO to attach + * + * \return none + */ +void GX_SetCPUFifo(GXFifoObj *fifo); + +/*! + * \fn void GX_SetGPFifo(GXFifoObj *fifo) + * \brief Attaches \a fifo to the GP. + * + * \note If the FIFO is also attached to the CPU, the system is in immediate-mode, and the fifo acts like a true FIFO. In immediate-mode, + * graphics commands are fed directly from the CPU to the GP. In immediate-mode the FIFO's high and low water marks are active. The high + * and low water marks implement the flow-control mechanism between the CPU and GP. When the FIFO becomes more full than the high water + * mark, the CPU will stop writing graphics commands into the FIFO. When the FIFO empties to a point lower than the low water mark, the + * CPU will resume writing graphics commands into the FIFO. The high and low water marks are set when intializing the FIFO using + * GX_InitFifoLimits().

+ * + * \note If the FIFO is only attached to the GP, the FIFO acts like a buffer. In this case, high and low water marks are disabled, and + * the GP reads the FIFO until it is empty. Before attaching a new FIFO to the GP, you should make sure the previous FIFO is empty, using + * the \a cmdIdle status returned by GX_GetGPStatus().

+ * + * \note The break point mechanism can be used to force the FIFO to stop reading commands at a certain point; see GX_EnableBreakPt(). + * + * \param[in] fifo struct containing info on the FIFO to attach + * + * \return none + */ +void GX_SetGPFifo(GXFifoObj *fifo); + +/*! + * \fn void GX_GetCPUFifo(GXFifoObj *fifo) + * \brief Copies the information from the currently attached CPU FIFO into \a fifo. + * + * \param[out] fifo the object to copy the current CPU FIFO object data into + * + * \return none + */ +void GX_GetCPUFifo(GXFifoObj *fifo); + +/*! + * \fn void GX_GetGPFifo(GXFifoObj *fifo) + * \brief Copies the information from the currently attached GP FIFO info \a fifo. + * + * \param[out] fifo the object to copy the current GP FIFO object data into + * + * \return none + */ +void GX_GetGPFifo(GXFifoObj *fifo); + +/*! + * \fn void* GX_GetFifoBase(GXFifoObj *fifo) + * \brief Get the base address for a given \a fifo. + * + * \param[in] fifo the object to get the address from + * + * \return pointer to the base address of the FIFO in main memory. + */ +void* GX_GetFifoBase(GXFifoObj *fifo); + +/*! + * \fn u32 GX_GetFifoCount(GXFifoObj *fifo) + * \brief Returns number of cache lines in the FIFO. + * + * \note The count is incorrect if an overflow has occurred (i.e. you have written more data than the size of the fifo), as the + * hardware cannot detect an overflow in general. + * + * \param[in] fifo the FIFO to get the count from + * + * \return number of cache lines in the FIFO + */ +u32 GX_GetFifoCount(GXFifoObj *fifo); + +/*! + * \fn u32 GX_GetFifoSize(GXFifoObj *fifo) + * \brief Get the size of a given \a fifo. + * + * \param[in] fifo the object to get the size from + * + * \return size of the FIFO, in bytes + */ +u32 GX_GetFifoSize(GXFifoObj *fifo); + +/*! + * \fn u8 GX_GetFifoWrap(GXFifoObj *fifo) + * \brief Returns a non-zero value if the write pointer has passed the TOP of the FIFO. + * + * \details Returns true only if the FIFO is attached to the CPU and the FIFO write pointer has passed the top of the FIFO. Use the + * return value to detect whether or not an overflow has occured by initializing the FIFO's write pointer to the base of the FIFO + * before sending any commands to the FIFO. + * + * \note If the FIFO write pointer is not explicitly set to the base of the FIFO, you cannot rely on this function to detect overflows. + * + * \param[in] fifo the object to get the wrap status from + * + * \return wrap value + */ +u8 GX_GetFifoWrap(GXFifoObj *fifo); + +/*! + * \fn GXDrawDoneCallback GX_SetDrawDoneCallback(GXDrawDoneCallback cb) + * \brief Installs a callback that is invoked whenever a DrawDone command is encountered by the GP. + * + * \details The DrawDone command is sent by GX_SetDrawDone(). + * + * \note By the time the callback is invoked, the GP will already have resumed reading from the FIFO, if there are any commands in it. + * + * \param[in] cb callback to be invoked when DrawDone is encountered + * + * \return pointer to the previous callback + */ +GXDrawDoneCallback GX_SetDrawDoneCallback(GXDrawDoneCallback cb); + +/*! + * \fn GXBreakPtCallback GX_SetBreakPtCallback(GXBreakPtCallback cb) + * \brief Registers \a cb as a function to be invoked when a break point is encountered. + * + * \warning The callback will run with interrupts disabled, so it should terminate as quickly as possible. + * + * \param[in] cb function to be invoked when the breakpoint is encountered; NULL means no function will run + * + * \return pointer to the previous callback function + */ +GXBreakPtCallback GX_SetBreakPtCallback(GXBreakPtCallback cb); + +/*! + * \fn void GX_AbortFrame() + * \brief Aborts the current frame. + * + * \details This command will reset the entire graphics pipeline, including any commands in the graphics FIFO. + * + * \note Texture memory will not be reset, so currently loaded textures will still be valid; however, when loading texture using + * GX_PreloadEntireTexture() or TLUTs using GX_LoadTlut(), you must make sure the command completed. You can use the draw sync mechanism to + * do this; see GX_SetDrawSync() and GX_GetDrawSync(). + * + * \return none + */ +void GX_AbortFrame(); + +/*! + * \fn void GX_Flush() + * \brief Flushes all commands to the GP. + * + * \details Specifically, it flushes the write-gather FIFO in the CPU to make sure that all commands are sent to the GP. + * + * \return none + */ +void GX_Flush(); + +/*! + * \fn void GX_SetMisc(u32 token,u32 value) + * \brief Sets miscellanous settings in the GP. + * + * \param[in] token setting to change + * \param[in] value value to change the setting to + * + * \return none + */ +void GX_SetMisc(u32 token,u32 value); + +/*! + * \fn void GX_SetDrawDone() + * \brief Sends a DrawDone command to the GP. + * + * \details When all previous commands have been processed and the pipeline is empty, a DrawDone status bit will be set, + * and an interrupt will occur. You can receive notification of this event by installing a callback on the interrupt with + * GX_SetDrawDoneCallback(), or you can poll the status bit with GX_WaitDrawDone(). This function also flushes the write-gather + * FIFO in the CPU to make sure that all commands are sent to the graphics processor. + * + * \note This function is normally used in multibuffer mode (see GX_SetCPUFifo()). In immediate mode, the GX_DrawDone() command + * can be used, which both sends the command and stalls until the DrawDone status is true. + * + * \return none + */ +void GX_SetDrawDone(); + +/*! + * \fn void GX_WaitDrawDone() + * \brief Stalls until DrawDone is encountered by the GP. + * + * \details It means all graphics commands sent before this DrawDone command have executed and the last pixel has been written to + * the frame buffer. You may want to execute some non-graphics operations between executing GX_SetDrawDone() and this function, but + * if you simply want to wait and have nothing to execute, you can use GX_DrawDone(). + * + * \note This function is normally used in immediate mode (see GX_SetCPUFifo()). In multibuffer mode, sending the 'done' command is + * separated from polling the 'done' status (see GX_SetDrawDone() and GX_WaitDrawDone()). + * + * \return none + */ +void GX_WaitDrawDone(); + +/*! + * \fn u16 GX_GetDrawSync() + * \brief Returns the value of the token register, which is written using the GX_SetDrawSync() function. + * + * \return the value of the token register. + */ +u16 GX_GetDrawSync(); + +/*! + * \fn void GX_SetDrawSync(u16 token) + * \brief This function sends a token into the command stream. + * + * \details When the token register is set, an interrupt will also be received by the CPU. You can install a callback on this interrupt + * with GX_SetDrawSyncCallback(). Draw syncs can be used to notify the CPU that the graphics processor is finished using a shared + * resource (a vertex array for instance). + * + * \param[in] token 16-bit value to write to the token register. + * + * \return none + */ +void GX_SetDrawSync(u16 token); + +/*! + * \fn GXDrawSyncCallback GX_SetDrawSyncCallback(GXDrawSyncCallback cb) + * \brief Installs a callback that is invoked whenever a DrawSync token is encountered by the graphics pipeline. + * + * \details The callback's argument is the value of the token most recently encountered. Since it is possible to + * miss tokens (graphics processing does not stop while the callback is running), your code should be + * capable of deducing if any tokens have been missed. + * + * \param[in] cb callback to be invoked when the DrawSync tokens are encountered in the graphics pipeline. + * + * \return pointer to the previously set callback function. + */ +GXDrawSyncCallback GX_SetDrawSyncCallback(GXDrawSyncCallback cb); + +/*! + * \fn void GX_DisableBreakPt() + * \brief Allows reads from the FIFO currently attached to the Graphics Processor (GP) to resume. + * + * \details See GX_EnableBreakPt() for an explanation of the FIFO break point feature. + * + * \note The breakpoint applies to the FIFO currently attached to the Graphics Processor (GP) (see GX_SetGPFifo()). + * + * \return none + */ +void GX_DisableBreakPt(); + +/*! + * \fn void GX_EnableBreakPt(void *break_pt) + * \brief Sets a breakpoint that causes the GP to halt when encountered. + * + * \note The break point feature allows the application to have two frames of graphics in the FIFO at the same time, overlapping + * one frame's processing by the graphics processor with another frame's processing by the CPU. For example, suppose you finish + * writing the graphics commands for one frame and are ready to start on the next. First, execute a GX_Flush() command to make + * sure all the data in the CPU write gatherer is flushed into the FIFO. This will also align the FIFO write pointer to a 32B + * boundary. Next, read the value of the current write pointer using GX_GetFifoPtrs(). Write the value of the write pointer as + * the break point address using GX_EnableBreakPt(). When the FIFO read pointer reaches the break point address the hardware + * will disable reads from the FIFO. The status \a brkpt, returned by GX_GetGPStatus(), can be polled to detect when the break point + * is reached. The application can then decide when to disable the break point, using GX_DisableBreakPt(), which will allow the FIFO + * to resume reading graphics commands.

+ * + * \note FIFO reads will stall when the GP FIFO read pointer is equal to the break point address \a break_pt. To re-enable reads of + * the GP FIFO, use GX_DisableBreakPt().

+ * + * \note Use GX_SetBreakPtCallback() to set what function runs when the breakpoint is encountered. + * + * \param[in] break_pt address for GP to break on when read. + * + * \return none + */ +void GX_EnableBreakPt(void *break_pt); + +/*! + * \fn void GX_DrawDone() + * \brief Sends a DrawDone command to the GP and stalls until its subsequent execution. + * + * \note This function is equivalent to calling GX_SetDrawDone() then GX_WaitDrawDone(). + * + * \return none + */ +void GX_DrawDone(); + +/*! + * \fn void GX_TexModeSync() + * \brief Inserts a synchronization command into the graphics FIFO. When the Graphics Processor sees this command, it will + * allow the texture pipeline to flush before continuing. + * + * \details This command is necessary when changing the usage of regions of texture memory from preloaded or TLUT to cached areas. + * It makes sure that the texture pipeline is finished with that area of the texture memory prior to changing its usage. + * This function should be called prior to drawing any primitives that uses the texture memory region in its new mode. It is not + * necessary to call this command when changing texture memory regions from cached to preloaded (or TLUT), since the commands to + * load the regions with data will cause the necessary synchronization to happen automatically. + * + * \return none + */ +void GX_TexModeSync(); + +/*! + * \fn void GX_InvVtxCache(); + * \brief Invalidates the vertex cache. + * + * \details Specifically, this functions invalidates the vertex cache tags. This function should be used whenever you relocate or modify + * data that is read by, or may be cached by, the vertex cache. The invalidate is very fast, taking only two Graphics Processor (GP) clock + * cycles to complete. + * + * \note The vertex cache is used to cache indexed attribute data. Any attribute that is set to GX_INDEX8 or GX_INDEX16 in the current + * vertex descriptor (see GX_SetVtxDesc()) is indexed. Direct data bypasses the vertex cache. Direct data is any attribute that is set to + * GX_DIRECT in the current vertex descriptor. + * + * \return none + */ +void GX_InvVtxCache(); + +/*! + * \fn void GX_ClearVtxDesc() + * \brief Clears all vertex attributes of the current vertex descriptor to GX_NONE. + * + * \note The same functionality can be obtained using GX_SetVtxDescv(), however using GX_ClearVtxDesc() is much more efficient. + * + * \return none + */ +void GX_ClearVtxDesc(); + +/*! + * \fn void GX_LoadProjectionMtx(Mtx44 mt,u8 type) + * \brief Sets the projection matrix. + * + * \note Only two types of projection matrices are supported: GX_PERSPECTIVE or GX_ORTHOGRAPHIC. + * + * \param[in] mt matrix to use for the perspective + * \param[in] type which perspective type to use + * + * \return none + */ +void GX_LoadProjectionMtx(Mtx44 mt,u8 type); + +/*! + * \fn void GX_SetViewport(f32 xOrig,f32 yOrig,f32 wd,f32 ht,f32 nearZ,f32 farZ) + * \brief Sets the viewport rectangle in screen coordinates. + * + * \details The screen origin (\a xOrig = 0.0f, \a yOrig = 0.0f) is at the top left corner of the display. Floating point arguments allow the + * viewport to be adjusted by 1/2 line for interlaced field rendering modes; see GX_SetViewportJitter(). The viewport depth parameters are normalized coordinates + * from 0.0f - 1.0f. The GX API will convert the depth range values to proper scale values depending on the type and format of the Z-buffer. + * + * \note You should avoid using negative values for \a xOrig or \a yOrig. While this may work, it may cause problems with points and lines being clipped incorrectly. If + * you need to shift the viewport up or left, consider using GX_SetScissorBoxOffset() instead. + * + * \param[in] xOrig left-most X coordinate on the screen + * \param[in] yOrig top-most Y coordinate on the screen + * \param[in] wd width of the viewport + * \param[in] ht height of the viewport + * \param[in] nearZ value to use for near depth scale + * \param[in] farZ value to use for far depth scale + * + * \return none + */ +void GX_SetViewport(f32 xOrig,f32 yOrig,f32 wd,f32 ht,f32 nearZ,f32 farZ); + +/*! + * \fn void GX_SetViewportJitter(f32 xOrig,f32 yOrig,f32 wd,f32 ht,f32 nearZ,f32 farZ,u32 field) + * \brief Sets the viewport and adjusts the viewport's line offset for interlaced field rendering. + * + * \details Depending on whether the viewport starts on an even or odd line, and whether the next \a field to be rendered is + * even or odd, the viewport may be adjusted by half a line. This has the same effect as slightly tilting the camera down and is necessary + * for interlaced rendering. No other camera adjustment (i.e. don't change the projection matrix) is needed for interlaced field rendering. + * + * \note To set a viewport without jitter, use GX_SetViewport(). + * + * \param[in] xOrig left-most X coordinate on the screen + * \param[in] yOrig top-most Y coordinate on the screen + * \param[in] wd width of the viewport + * \param[in] ht height of the viewport + * \param[in] nearZ value to use for near depth scale + * \param[in] farZ value to use for far depth scale + * \param[in] field whether the next field is even (0) or odd (1) + * + * \return none + */ +void GX_SetViewportJitter(f32 xOrig,f32 yOrig,f32 wd,f32 ht,f32 nearZ,f32 farZ,u32 field); + +/*! + * \fn void GX_SetChanCtrl(s32 channel,u8 enable,u8 ambsrc,u8 matsrc,u8 litmask,u8 diff_fn,u8 attn_fn) + * \brief Sets the lighting controls for a particular color channel. + * + * \details The color channel can have one or more (up to 8) lights associated with it, set using \a litmask. The \a diff_fn and \a attn_fn parameters + * control the lighting equation for all lights associated with this channel; the \a ambsrc and \a matsrc can be used to select whether the input + * source colors come from register colors or vertex colors. When the channel \a enable is set to GX_FALSE, the material color source (set by \a matsrc) + * is passed through as the channel's output color. When the channel \a enable is GX_TRUE, the output color depends on the settings of the other + * controls (i.e., the lighting equation). GX_Init() sets the \a enable for all channels to GX_FALSE. This function only configures the lighting + * channel; to output the result of the channel computation, use GX_SetNumChans(). + * + * \note Even though channels GX_COLOR0 and GX_ALPHA0 are controlled separately for lighting, they are rasterized together as one RGBA color, effectively + * GX_COLOR0A0. The same is true for GX_COLOR1 and GX_ALPHA1-- effectively, they are rasterized as GX_COLOR1A1. Since there is only one rasterizer for + * color in the graphics hardware, you must choose which color to rasterize for each stage in the Texture Environment (TEV) unit. This is accomplished + * using GX_SetTevOrder().

+ * + * \note In order to use a vertex color in channel GX_COLOR1A1, two colors per vertex must be supplied, i.e. both GX_VA_CLR0 and GX_VA_CLR1 must be + * enabled in the current vertex descriptor. If only GX_VA_CLR0 or GX_VA_CLR1 is enabled in the current vertex descriptor, the vertex color is + * directed to the channel GX_VA_COLOR0A0.

+ * + * \note When \a ambsrc is set to GX_SRC_REG, the color set by GX_SetChanAmbColor() is used as the ambient color. When \a matsrc is GX_SRC_REG, the color set + * by GX_SetChanMatColor() is used as the material color. + * + * \param[in] channel color channel to use + * \param[in] enable whether or not to enable lighting for this channel + * \param[in] ambsrc source for the ambient color + * \param[in] matsrc source for the material color + * \param[in] litmask \ref lightid or IDs to associate with this channel + * \param[in] diff_fn \ref difffn to use + * \param[in] attn_fn \ref attenfunc to use + * + * \return none + */ +void GX_SetChanCtrl(s32 channel,u8 enable,u8 ambsrc,u8 matsrc,u8 litmask,u8 diff_fn,u8 attn_fn); + +/*! + * \fn void GX_SetChanAmbColor(s32 channel,GXColor color) + * \brief Sets the ambient color register for color channel \a chan. + * + * \details This color will be used by the channel as the ambient color if the ambient source, set by GX_SetChanCtrl(), is GX_SRC_REG. + * + * \param[in] channel channel to set + * \param[in] color color to set it to + * + * \return none + */ +void GX_SetChanAmbColor(s32 channel,GXColor color); + +/*! + * \fn void GX_SetChanMatColor(s32 channel,GXColor color) + * \brief Sets the material color register for color channel \a chan. + * + * \details This color will be used by the channel as the material color if the material source, set by GX_SetChanCtrl(), is GX_SRC_REG. + * + * \param[in] channel channel to set + * \param[in] color color to set it to + * + * \return none + */ +void GX_SetChanMatColor(s32 channel,GXColor color); + +/*! + * \fn void GX_SetArray(u32 attr,void *ptr,u8 stride) + * \brief Sets the array base pointer and stride for a single attribute. + * + * \details The array base and stride are used to compute the address of indexed attribute data using the equation:

+ * + *       attr_addr = \a ptr + attr_idx * \a stride + * + * When drawing a graphics primitive that has been enabled to use indexed attributes (see GX_SetVtxDesc()), attr_idx is supplied in the vertex + * data. The format and size of the data in the array must also be described using GX_SetVtxAttrFmt(). You can also index other data, such as + * matrices (see GX_LoadPosMtxIdx(), GX_LoadNrmMtxIdx3x3(), and GX_LoadTexMtxIdx()), and light objects (see GX_LoadLightObjIdx()). In the case + * of matrices and light objects, the size and format of the data to be loaded is implied by the function call. + * + * There is a base pointer, \a ptr, for each type of attribute as well as for light data and matrices. Each attribute can be stored in its own + * array for maximum data compression (i.e., removal of redundant attribute data). The \a stride is in byte units and is the distance between + * attributes in the array. + * + * \note Indexed data is loaded into a vertex cache in the graphics processor. The vertex cache fetches 32B of data for each cache miss; + * therefore, there is a small performance benefit to aligning attribute arrays to 32B, and possibly for arranging vertex data so that it + * doesn't span 32B boundaries. Conveniently enough, memalign() returns 32-byte aligned pointers. For static data arrays, you can use the + * ATTRIBUTE_ALIGN(32) attribute macro to align the \a ptr to 32B. + * + * \param[in] attr \ref vtxattr that the array is storing + * \param[in] ptr pointer to the array itself + * \param[in] stride stride (in bytes) between each element in the array + * + * \return none + */ +void GX_SetArray(u32 attr,void *ptr,u8 stride); + +/*! + * \fn void GX_SetVtxAttrFmt(u8 vtxfmt,u32 vtxattr,u32 comptype,u32 compsize,u32 frac) + * \brief Sets the attribute format (\a vtxattr) for a single attribute in the Vertex Attribute Table (VAT). + * + * \details Each attribute format describes the data type (comptype), number of elements (compsize), and fixed point format (frac), if required. The + * are eight vertex formats available in the VAT. The vertex format describes the format of all attributes in a vertex. The application can pre-program + * all eight vertex formats and then select one of them during the actual drawing of geometry (See GX_Begin()). Note that all vertices used to draw a + * particular graphics primitive will have the same format. The vertex format set using this function, along with the current vertex descriptor set + * using GX_SetVtxDesc(), completely define the vertex data format. + * + * \note The vertex format allows data to be sent to the graphics processor in its most quantized format. The graphics hardware will inverse-quantize + * the data (into floating point format) before it is used. The vertex attribute format is used to communicate the data quantization format to the hardware.

+ * + * \note GX_VA_NRM and GX_VA_NBT attributes share the same type. Also, the frac for these attributes is fixed according to the type. The component count + * (compsize) for GX_VA_NRM must be set to GX_NRM_XYZ. The component count for GX_VA_NBT must be set to GX_NRM_NBT or GX_NRM_NBT3. + * + * \param[in] vtxfmt \ref vtxfmt + * \param[in] vtxattr \ref vtxattr + * \param[in] comptype \ref comptype + * \param[in] compsize \ref compsize + * \param[in] frac number of fractional bits in a fixed-point number + * + * \return none + */ +void GX_SetVtxAttrFmt(u8 vtxfmt,u32 vtxattr,u32 comptype,u32 compsize,u32 frac); + +/*! + * \fn void GX_SetVtxAttrFmtv(u8 vtxfmt,GXVtxAttrFmt *attr_list) + * \brief Sets multiple attribute formats within a single vertex format. + * + * \details This is useful when you need to set all the attributes in a vertex format at once (e.g., during graphics initialization). + * + * \note The constant GX_MAX_VTXATTRFMT_LISTSIZE should be used to allocate the list. You can get a current vertex format using GX_GetVtxAttrFmtv(). + * + * \param[in] vtxfmt \ref vtxfmt + * \param[in] attr_list pointer to array of GXVtxAttrFmt structs to draw from + * + * \return none + */ +void GX_SetVtxAttrFmtv(u8 vtxfmt,GXVtxAttrFmt *attr_list); + +/*! + * \fn void GX_SetVtxDesc(u8 attr,u8 type) + * \brief Sets the \a type of a single attribute (\a attr) in the current vertex descriptor. + * + * \details The current vertex descriptor defines which attributes are present in a vertex and how each attribute is referenced. The current + * vertex descriptor is used by the Graphics Processor (GP) to interpret the graphics command stream produced by the GX API. In particular, + * the current vertex descriptor is used to parse the vertex data that is present in the command stream. + * + * \param[in] attr \ref vtxattr + * \param[in] type \ref vtxattrin + * + * \return none + */ +void GX_SetVtxDesc(u8 attr,u8 type); + +/*! + * \fn void GX_SetVtxDescv(GXVtxDesc *attr_list) + * \brief Sets the type of multiple attributes. + * + * \details This function is used when more than one attribute needs to be set (e.g., during initialization of geometry). + * + * \note The constant GX_MAX_VTXATTRFMT_LISTSIZE can be used to allocate memory for \a attr_list + * + * \param[in] attr_list array of pointers to GXVtxDesc structs; last element of the array should be GX_VA_NULL + * + * \return none + */ +void GX_SetVtxDescv(GXVtxDesc *attr_list); + +/*! + * \fn void GX_GetVtxDescv(GXVtxDesc *attr_list) + * \brief Gets the type of multiple attributes. + * + * \details This function saves the attributes that are current set. This is usually used in conjunction with GX_SetVtxDescv + * + * \note The constant GX_MAX_VTXATTRFMT_LISTSIZE must be used to allocate memory for \a attr_list + * + * \param[in] attr_list array of pointers to GXVtxDesc structs + * + * \return none + */ +void GX_GetVtxDescv(GXVtxDesc *attr_list); + +/*! + * \fn u32 GX_EndDispList() + * \brief Ends a display list and resumes writing graphics commands to the CPU FIFO. + * + * \details This function returns the size of the display list written to the display list buffer since GX_BeginDispList() was called. If + * the display list size exceeds the size of the buffer allocated, a zero length size will be returned. The display list size is a + * multiple of 32B and any unsed commands in the last 32B will be padded with GX_NOP. The size returned should be passed to + * GX_CallDispList() when the display list needs to be executed. + * + * \note Due to the mechanics of flushing the write-gather pipe (which is used to create the display list), the display buffer should be + * at least 32 bytes larger than the maximum expected amount of data stored. This function calls GX_Flush(), and thus it is not necessary + * to call GX_Flush() explicitly after creating the display list.

+ * + * \note A display list cannot be nested; i.e., no display list functions (GX_BeginDispList(), GX_EndDispList() and GX_CallDispList()) can + * be called between a GX_BeginDispList() and GX_EndDispList() pair.

+ * + * \note To execute a display list, use GX_CallDispList(). + * + * \return 0 if display list size exceeds buffer, otherwise gives list size in bytes + * + * \bug Specifying a display list buffer size for GX_BeginDispList() the exact size that the display list will be (after padding) will cause + * this function to return a very large (and very incorrect) value. + */ +u32 GX_EndDispList(); + +/*! + * \fn void GX_Begin(u8 primitve,u8 vtxfmt,u16 vtxcnt) + * \brief Begins drawing of a graphics primitive. + * + * \details To draw a graphics primitive, a stream of vertex data matching the description of both GX_SetVtxDesc() and GX_SetVtxAttrFmt() is + * enclosed between GX_Begin()/GX_End() pairs. The number of vertices between GX_Begin() and GX_End() must match that specified by the \a vtxcnt + * parameter. The type of the primitive will determine the minimum number of vertices required. For example, a GX_TRIANGLES primitive must + * have at least 3 vertices. + * + * \note Primitives in which the vertex order is clockwise to the viewer are considered front-facing (for culling purposes). + * + * \param[in] primitve \ref primtype to draw + * \param[in] vtxfmt \ref vtxfmt to use + * \param[in] vtxcnt number of vertices being drawn; maximum is 65536 + */ +void GX_Begin(u8 primitve,u8 vtxfmt,u16 vtxcnt); + +/*! + * \fn void GX_BeginDispList(void *list,u32 size) + * \brief Begins a display list and disables writes to the FIFO currently attached to the CPU. + * + * \details After this function is called, GX API functions that normally send command or data into the CPU FIFO will send them to the + * display list buffer instead of the FIFO until GX_EndDispList() is called. Writes to the CPU FIFO will be re-enabled when the function + * GX_EndDispList() executes. + * + * Basically you can put most of GX API commands into a display list. However, since the use of display list can bypass all state + * coherences controlled by GX API in run-time, sometimes it brings some state collisions or incoherences that may lead to unexpected + * behavior or even graphics pipeline hang. The most recommended safe way is putting only primitives (regions enclosed by GX_Begin() and + * GX_End()) that don't cause any state collisions. + * + * \note The application is expected to allocate the memory for the display list buffer. If the display list exceeds the maximum size + * of the buffer, GX_EndDispList() will return 0. The address of the buffer must be 32-byte aligned; memalign() can return 32-byte-aligned + * pointers. You can use the macro ATTRIBUTE_ALIGN(32) to align statically allocated buffers.

+ * + * \note The CPU's write-gather pipe is used to write graphics commands to the display list. Therefore, the display list buffer must be + * forced out of the CPU cache prior to being filled. DCInvalidateRange() may be used for this purpose. In addition, due to the mechanics + * of flushing the write-gather pipe, the display list buffer should be at least 63 bytes larger than the maximum expected amount of data + * stored.

+ * + * \note A display list cannot be nested; i.e., no display list functions (GX_BeginDispList(), GX_EndDispList() and GX_CallDispList()) can + * be called between a GX_BeginDispList() and GX_EndDispList() pair.

+ * + * \note To execute a display list, use GX_CallDispList(). + * + * \param[in] list 32-byte aligned buffer to hold the list + * \param[in] size size of the buffer, multiple of 32 + * + * \return none + */ +void GX_BeginDispList(void *list,u32 size); + +/*! + * \fn void GX_CallDispList(void *list,u32 nbytes) + * \brief Causes the GP to execute graphics commands from the display \a list instead of from the GP FIFO. + * + * \details When the number of bytes specified by \a nbytes have been read, the graphics processor will resume executing + * commands from the graphics FIFO. Graphics commands from a display list are prefetched into a separate 4KB FIFO. This prevents + * any data prefetched for the main graphics command stream from being lost during the display list call. + * + * \note A display list cannot call another display list.

+ * + * \note The display list must be padded to a length of 32B. All the data in the display list is interpreted by the graphics + * processor, so any unused memory at the end of a display list should be set to GX_NOP. If you create the display list using + * GX_BeginDispList()/GX_EndDispList(), this padding will be inserted automatically. + * + * \param[in] list 32-byte aligned pointer to the display list buffer + * \param[in] nbytes number of bytes in the display list. Use the return value of GX_EndDispList() here. + * + * \return none + */ +void GX_CallDispList(void *list,u32 nbytes); + +/*! + * \fn static inline void GX_End() + * \brief Used to end the drawing of a graphics primitive. This does nothing in libogc. + * + * \return none + */ +static inline void GX_End() +{ +} + +static inline void GX_Position3f32(f32 x,f32 y,f32 z) +{ + wgPipe->F32 = x; + wgPipe->F32 = y; + wgPipe->F32 = z; +} + +static inline void GX_Position3u16(u16 x,u16 y,u16 z) +{ + wgPipe->U16 = x; + wgPipe->U16 = y; + wgPipe->U16 = z; +} + +static inline void GX_Position3s16(s16 x,s16 y,s16 z) +{ + wgPipe->S16 = x; + wgPipe->S16 = y; + wgPipe->S16 = z; +} + +static inline void GX_Position3u8(u8 x,u8 y,u8 z) +{ + wgPipe->U8 = x; + wgPipe->U8 = y; + wgPipe->U8 = z; +} + +static inline void GX_Position3s8(s8 x,s8 y,s8 z) +{ + wgPipe->S8 = x; + wgPipe->S8 = y; + wgPipe->S8 = z; +} + +static inline void GX_Position2f32(f32 x,f32 y) +{ + wgPipe->F32 = x; + wgPipe->F32 = y; +} + +static inline void GX_Position2u16(u16 x,u16 y) +{ + wgPipe->U16 = x; + wgPipe->U16 = y; +} + +static inline void GX_Position2s16(s16 x,s16 y) +{ + wgPipe->S16 = x; + wgPipe->S16 = y; +} + +static inline void GX_Position2u8(u8 x,u8 y) +{ + wgPipe->U8 = x; + wgPipe->U8 = y; +} + +static inline void GX_Position2s8(s8 x,s8 y) +{ + wgPipe->S8 = x; + wgPipe->S8 = y; +} + +static inline void GX_Position1x8(u8 index) +{ + wgPipe->U8 = index; +} + +static inline void GX_Position1x16(u16 index) +{ + wgPipe->U16 = index; +} + +static inline void GX_Normal3f32(f32 nx,f32 ny,f32 nz) +{ + wgPipe->F32 = nx; + wgPipe->F32 = ny; + wgPipe->F32 = nz; +} + +static inline void GX_Normal3s16(s16 nx,s16 ny,s16 nz) +{ + wgPipe->S16 = nx; + wgPipe->S16 = ny; + wgPipe->S16 = nz; +} + +static inline void GX_Normal3s8(s8 nx,s8 ny,s8 nz) +{ + wgPipe->S8 = nx; + wgPipe->S8 = ny; + wgPipe->S8 = nz; +} + +static inline void GX_Normal1x8(u8 index) +{ + wgPipe->U8 = index; +} + +static inline void GX_Normal1x16(u16 index) +{ + wgPipe->U16 = index; +} + +static inline void GX_Color4u8(u8 r,u8 g,u8 b,u8 a) +{ + wgPipe->U8 = r; + wgPipe->U8 = g; + wgPipe->U8 = b; + wgPipe->U8 = a; +} + +static inline void GX_Color3u8(u8 r,u8 g,u8 b) +{ + wgPipe->U8 = r; + wgPipe->U8 = g; + wgPipe->U8 = b; +} + +static inline void GX_Color3f32(f32 r, f32 g, f32 b) +{ + wgPipe->U8 = (u8)(r * 255.0); + wgPipe->U8 = (u8)(g * 255.0); + wgPipe->U8 = (u8)(b * 255.0); +} + +static inline void GX_Color1u32(u32 clr) +{ + wgPipe->U32 = clr; +} + +static inline void GX_Color1u16(u16 clr) +{ + wgPipe->U16 = clr; +} + +static inline void GX_Color1x8(u8 index) +{ + wgPipe->U8 = index; +} + +static inline void GX_Color1x16(u16 index) +{ + wgPipe->U16 = index; +} + +static inline void GX_TexCoord2f32(f32 s,f32 t) +{ + wgPipe->F32 = s; + wgPipe->F32 = t; +} + +static inline void GX_TexCoord2u16(u16 s,u16 t) +{ + wgPipe->U16 = s; + wgPipe->U16 = t; +} + +static inline void GX_TexCoord2s16(s16 s,s16 t) +{ + wgPipe->S16 = s; + wgPipe->S16 = t; +} + +static inline void GX_TexCoord2u8(u8 s,u8 t) +{ + wgPipe->U8 = s; + wgPipe->U8 = t; +} + +static inline void GX_TexCoord2s8(s8 s,s8 t) +{ + wgPipe->S8 = s; + wgPipe->S8 = t; +} + +static inline void GX_TexCoord1f32(f32 s) +{ + wgPipe->F32 = s; +} + +static inline void GX_TexCoord1u16(u16 s) +{ + wgPipe->U16 = s; +} + +static inline void GX_TexCoord1s16(s16 s) +{ + wgPipe->S16 = s; +} + +static inline void GX_TexCoord1u8(u8 s) +{ + wgPipe->U8 = s; +} + +static inline void GX_TexCoord1s8(s8 s) +{ + wgPipe->S8 = s; +} + +static inline void GX_TexCoord1x8(u8 index) +{ + wgPipe->U8 = index; +} + +static inline void GX_TexCoord1x16(u16 index) +{ + wgPipe->U16 = index; +} + +static inline void GX_MatrixIndex1x8(u8 index) +{ + wgPipe->U8 = index; +} + +/*! + * \fn void GX_AdjustForOverscan(GXRModeObj *rmin,GXRModeObj *rmout,u16 hor,u16 ver) + * \brief Takes a given render mode and returns a version that is reduced in size to account for overscan. + * + * \details The number of pixels specified by \a hor is subtracted from each side of the screen, and the number of pixels specified + * by \a ver is subtracted from both the top and the bottom of the screen. The active screen area is centered within what the render + * mode specified before the adjustment. + * + * \note Due to the wide possibilities of how a render mode may be configured, this function may not work in all cases. For instance, + * if you use Y-scaling to create a size difference between the EFB and XFB, this function may not do the right thing. In such cases, + * you should configure the desired render mode manually (or else call this function and then fix up the results). + * + * \param[in] rmin rmode that is being copied + * \param[in] rmout rmode to hold the adjusted version. Needs to be allocated but can be uninitialized. + * \param[in] hor pixels to trim from each side of the screen + * \param[in] ver pixels to tim from top and bottom of the screen + * + * \returns none + */ +void GX_AdjustForOverscan(GXRModeObj *rmin,GXRModeObj *rmout,u16 hor,u16 ver); + +/*! + * \fn void GX_LoadPosMtxImm(Mtx mt,u32 pnidx) + * \brief Used to load a 3x4 modelview matrix \a mt into matrix memory at location \a pnidx. + * + * \details This matrix can be used to transform positions in model space to view space, either by making the matrix the current one (see + * GX_SetCurrentMtx()), or by setting a matrix \a pnidx for each vertex. The parameter \a mt is a pointer to a 3x4 (row x column) matrix. The + * parameter \a pnidx is used to refer to the matrix location (see \ref pnmtx) in matrix memory. + * + * You can also load a normal matrix (GX_LoadNrmMtxImm() or GX_LoadNrmMtxIdx3x3()) to the same \a pnidx. Generally, the normal matrix + * will be the inverse transpose of the position matrix. The normal matrix is used during vertex lighting. In cases where the modelview + * and inverse transpose of the modelview (excluding translation) are the same, you can load the same matrix for both position and normal + * transforms. + * + * \note The matrix data is copied from DRAM through the CPU cache into the Graphics FIFO, so matrices loaded using this function are always + * coherent with the CPU cache. + * + * \param[in] mt the matrix to load + * \param[in] pnidx \ref pnmtx to load into + * + * \return none + */ +void GX_LoadPosMtxImm(Mtx mt,u32 pnidx); + +/*! + * \fn void GX_LoadPosMtxIdx(u16 mtxidx,u32 pnidx) + * \brief Loads a 3x4 modelview matrix at index \a mtxidx from the array in main memory. + * + * \details The array is set by GX_SetArray(), and the matrix is loaded into matrix memory at index \a pnidx (see \ref pnmtx). This + * modelview matrix is used to transform positions in model space to view space, either by making the matrix the current one (see + * GX_SetCurrentMtx()) or by setting a matrix \a pnidx for each vertex (see GX_SetVtxDesc()). The matrix will be loaded through the vertex cache. + * + * You can also load a normal matrix (GX_LoadNrmMtxImm() or GX_LoadNrmMtxIdx3x3()) to the same \a pnidx. Generally, the normal matrix + * will be the inverse transpose of the position matrix. The normal matrix is used during vertex lighting. In cases where the modelview + * and inverse transpose of the modelview (excluding translation) are the same, you can load the same matrix for both position and normal + * transforms. Since indexed matrix loads are through the vertex cache, you will only incur the main memory bandwidth load of one matrix load. + * + * \note The matrix is loaded directly from main memory into the matrix memory thrugh the vertex cache, so it is incoherent with the CPU's cache. + * It is the application's responsibility to flush any matrix data from the CPU cache (see DCStoreRange()) before calling this function. + * + * \param[in] mtxidx index to the matrix array to load + * \param[in] pnidx \ref pnmtx to load into + * + * \return none + */ +void GX_LoadPosMtxIdx(u16 mtxidx,u32 pnidx); + +/*! + * \fn void GX_LoadNrmMtxImm(Mtx mt,u32 pnidx) + * \brief Used to load a normal transform matrix into matrix memory at location \a pnidx from the 4x3 matrix \a mt. + * + * \details This matrix is used to transform normals in model space to view space, either by making it the current matrix (see GX_SetCurrentMtx()), + * or by setting a matrix pnidx for each vertex. The parameter \a mt is a pointer to a 3x4 (row x column) matrix. The translation terms + * in the 3x4 matrix are not needed for normal rotation and are ignored during the load. The parameter \a pnidx is used to refer to the + * matrix location (see \ref pnmtx) in matrix memory. + * + * \note You can also load a position matrix (GX_LoadPosMtxImm()) to the same \a pnidx. Normally, the normal matrix will be the inverse transpose of + * the position (modelview) matrix and is used during vertex lighting. In cases where the modelview and the inverse transpose of the modelview + * matrix (excluding translation) are the same, the same matrix can be loaded for both normal and position matrices.

+ * + * \note The matrix data is copied from main memory or the CPU cache into the Graphics FIFO, so matrices loaded by this function are always coherent + * with the CPU cache.

+ * + * \param[in] mt the matrix to load + * \param[in] pnidx \ref pnmtx to load into + * + * \return none + */ +void GX_LoadNrmMtxImm(Mtx mt,u32 pnidx); + +/*! + * \fn void GX_LoadNrmMtxIdx3x3(u16 mtxidx,u32 pnidx) + * \brief Loads a 3x3 normal matrix into matrix memory at location \a pnidx from a 3x3 matrix located at index \a mtxidx + * from the array in main memory. + * + * \details The array is set by set by GX_SetArray(), and the matrix is loaded into matrix memory at index \a pnidx. This matrix can be used to + * transform normals in model space to view space, either by making the matrix the current one (see GX_SetCurrentMtx()), or by setting a + * matrix \a pnidx for each vertex (see GX_SetVtxDesc()). The matrix will be loaded through the vertex cache. You can also load a position + * matrix (GX_LoadPosMtxImm() or GX_LoadPosMtxIdx()) to the same \a pnidx. + * + * \note You cannot use an indexed load to load a 3x3 matrix from an indexed 3x4 matrix in main memory; you must use GX_LoadNrmMtxImm() for + * this case.

+ * + * \note The matrix is loaded directly from main memory into the matrix memory through the vertex cache, therefore it is incoherent with the + * CPU's cache. It is the application's responsibility to flush any matrix data from the CPU cache (see DCStoreRange()) before calling this + * function. + * + * \param[in] mtxidx index to the matrix array to load + * \param[in] pnidx \ref pnmtx to load into + * + * \return none + */ +void GX_LoadNrmMtxIdx3x3(u16 mtxidx,u32 pnidx); + +/*! + * \fn void GX_LoadTexMtxImm(Mtx mt,u32 texidx,u8 type) + * \brief Loads a texture matrix \a mt into the matrix memory at location \a texidx. + * + * \details The matrix loaded can be either a 2x4 or 3x4 matrix as indicated by \a type. You can use the loaded matrix to + * transform texture coordinates, or to generate texture coordinates from position or normal vectors. Such generated texture + * coordinates are used for projected textures, reflection mapping, etc. See GX_SetTexCoordGen() for more details. + * + * Texture matrices can be either 2x4 or 3x4. GX_MTX_2x4 matrices can be used for simple translations and/or rotations of texture + * coordinates. GX_MTX_3x4 matrices are used when projection is required. + * + * \note The default matrix memory configuration allows for ten (3x4 or 2x4) texture matrices, and a 3x4 identity matrix. The GX_IDENTITY + * matrix is preloaded by GX_Init().

+ * + * \note This function allows one to load post-transform texture matrices as well. Specifying a texidx in the range of \ref dttmtx will load a + * post-transform texture matrix instead of a regular, first-pass texture matrix. Note that post-transform matrices are always 3x4. Refer to + * GX_SetTexCoordGen2() for information about how to use post-transform texture matrices. + * + * \param[in] mt the matrix to load + * \param[in] texidx \ref texmtx + * \param[in] type \ref mtxtype + * + * \return none + */ +void GX_LoadTexMtxImm(Mtx mt,u32 texidx,u8 type); + +/*! + * \fn void GX_LoadTexMtxIdx(u16 mtxidx,u32 texidx,u8 type) + * \brief Loads a texture matrix at index \a mtxidx from the array in main memory + * + * \details The array is set by GX_SetArray(), and the matrix is loaded into matrix memory at location \a texid. The loaded matrix can be + * either 2x4 or 3x4 as indicated by \a type. This matrix can be used to generate texture coordinates from positions, normals, and input + * texture coordinates (see GX_SetTexCoordGen()). The matrix is loaded through the vertex cache. The size of the matrix to load is indicated by its + * \a type. Texture matrices can be either 2x4 or 3x4. GX_MTX_2x4 matrices can be used for simple translations and/or rotations of texture + * coordinates; GX_MTX_3x4 matrices are used when projection is required. + * + * \note The matrix is loaded directly from main memory into the matrix memory through the vertex cache, so it is incoherent with the CPU's + * cache. It is the application's responsibility to flush any matrix data from the CPU cache (see DCStoreRange()) before calling this function.

+ * + * \note This function allows one to load post-transform texture matrices as well. Specifying a \a texidx in the range of \ref dttmtx will load a + * post-transform texture matrix instead of a regular, first-pass texture matrix. Note that post-transform matrices are always 3x4. Refer to + * GX_SetTexCoordGen2() for information about how to use post-transform texture matrices. + * + * \param[in] mtxidx index to the matrix array to load + * \param[in] texidx \ref texmtx + * \param[in] type \ref mtxtype + * + * \return none + */ +void GX_LoadTexMtxIdx(u16 mtxidx,u32 texidx,u8 type); + +/*! + * \fn void GX_SetCurrentMtx(u32 mtx) + * \brief Selects a specific matrix to use for transformations. + * + * \details The matrix \a mtx specified will be used to select the current modelview transform matrix and normal transform matrix, + * as long as a matrix index is not present in the vertex data (see GX_SetVtxDesc()). If the current vertex descriptor enables GX_VA_PTNMTXIDX, + * the matrix \a mtx specified by this function will be overwritten when the vertices are drawn. + * + * \param[in] mtx \ref pnmtx + * + * \return none + */ +void GX_SetCurrentMtx(u32 mtx); + +/*! + * \fn void GX_SetTevOp(u8 tevstage,u8 mode) + * \brief Simplified function to set various TEV parameters for this \a tevstage based on a predefined combiner \a mode. + * + * \details This is a convenience function designed to make initial programming of the Texture Environment unit (TEV) easier. This function calls + * GX_SetTevColorIn(), GX_SetTevColorOp(), GX_SetTevAlphaIn() and GX_SetTevAlphaOp() with predefined arguments to implement familiar texture + * combining functions. + * + * \note To enable a consecutive set of TEV stages, you must call GX_SetNumTevStages(). + * + * \param[in] tevstage \ref tevstage. + * \param[in] mode \ref tevdefmode + * + * \return none + */ +void GX_SetTevOp(u8 tevstage,u8 mode); + +/*! + * \fn void GX_SetTevColor(u8 tev_regid,GXColor color) + * \brief Used to set one of the primary color registers in the TEV unit. + * + * \details These registers are available to all TEV stages. At least one of these registers is used to pass the output of one TEV stage to + * the next in a multi-texture configuration. The application is responsible for allocating these registers so that no collisions in usage occur. + * + * \note This function can only set unsigned 8-bit colors. To set signed, 10-bit colors use GX_SetTevColorS10(). + * + * \param[in] tev_regid \ref tevcoloutreg. + * \param[in] color Constant color value. + * + * \return none + */ +void GX_SetTevColor(u8 tev_regid,GXColor color); + +/*! + * \fn void GX_SetTevColorS10(u8 tev_regid,GXColorS10 color) + * \brief Used to set one of the constant color registers in the TEV unit. + * + * \details These registers are available to all TEV stages. At least one of these registers is used to pass the output of one TEV stage to the + * next in a multi-texture configuration. The application is responsible for allocating these registers so that no collisions in usage occur. + * + * \note This function enables the color components to be signed 10-bit numbers. To set 8-bit unsigned colors (the common case), use GX_SetTevColor(). + * + * \param[in] tev_regid \ref tevcoloutreg. + * \param[in] color Constant color value in S10 format. + * + * \return none + */ +void GX_SetTevColorS10(u8 tev_regid,GXColorS10 color); + +/*! + * \fn void GX_SetTevColorIn(u8 tevstage,u8 a,u8 b,u8 c,u8 d) + * \brief Sets the color input sources for one \a tevstage of the Texture Environment (TEV) color combiner. + * + * \details This includes constant (register) colors and alphas, texture color/alpha, rasterized color/alpha (the result of per-vertex lighting), + * and a few useful constants. + * + * \note The input controls are independent for each TEV stage. + * + * \param[in] tevstage \ref tevstage + * \param[in] a \ref tevcolorarg + * \param[in] b \ref tevcolorarg + * \param[in] c \ref tevcolorarg + * \param[in] d \ref tevcolorarg + * + * \return none + */ +void GX_SetTevColorIn(u8 tevstage,u8 a,u8 b,u8 c,u8 d); + +/*! + * \fn void GX_SetTevAlphaIn(u8 tevstage,u8 a,u8 b,u8 c,u8 d) + * \brief Sets the alpha input sources for one \a tevstage of the Texture Environment (TEV) alpha combiner. + * + * \details There are fewer alpha inputs than color inputs, and there are no color channels available in the alpha combiner. + * + * \note The input controls are independent for each TEV stage. + * + * \param[in] tevstage \ref tevstage + * \param[in] a \ref tevalphaarg + * \param[in] b \ref tevalphaarg + * \param[in] c \ref tevalphaarg + * \param[in] d \ref tevalphaarg + * + * \return none + */ +void GX_SetTevAlphaIn(u8 tevstage,u8 a,u8 b,u8 c,u8 d); + +/*! + * \fn void GX_SetTevColorOp(u8 tevstage,u8 tevop,u8 tevbias,u8 tevscale,u8 clamp,u8 tevregid) + * \brief Sets the \a tevop, \a tevbias, \a tevscale and \a clamp-mode operation for the color combiner + * for this \a tevstage of the TEV unit. + * + * \details This function also specifies the register, \a tevregid, that will contain the result of the color combiner function. The color + * combiner function is:

+ * + *       \a tevregid = (d (\a tevop) ((1.0 - c)*a + c*b) + \a tevbias) * \a tevscale;

+ * + * + * The input sources a,b,c and d are set using GX_SetTevColorIn(). + * + * \param[in] tevstage \ref tevstage. + * \param[in] tevop \ref tevop + * \param[in] tevbias \ref tevbias. + * \param[in] tevscale \ref tevscale. + * \param[in] clamp Clamp results when GX_TRUE. + * \param[in] tevregid \ref tevcoloutreg + * + * \return none + */ +void GX_SetTevColorOp(u8 tevstage,u8 tevop,u8 tevbias,u8 tevscale,u8 clamp,u8 tevregid); + + +/*! + * \fn void GX_SetTevAlphaOp(u8 tevstage,u8 tevop,u8 tevbias,u8 tevscale,u8 clamp,u8 tevregid) + * \brief Sets the \a tevop, \a tevbias, \a tevscale and \a clamp-mode operation for the alpha combiner + * for this \a tevstage of the TEV unit. + * + * \details This function also specifies the register, \a tevregid, that will contain the result of the alpha combiner function. The alpha + * combiner function is:

+ * + *       \a tevregid = (d (\a tevop) ((1.0 - c)*a + c*b) + \a tevbias) * \a tevscale;

+ * + * The input sources a,b,c and d are set using GX_SetTevAlphaIn(). + * + * \param[in] tevstage \ref tevstage. + * \param[in] tevop \ref tevop + * \param[in] tevbias \ref tevbias. + * \param[in] tevscale \ref tevscale. + * \param[in] clamp Clamp results when GX_TRUE. + * \param[in] tevregid \ref tevcoloutreg + * + * \return none + */ +void GX_SetTevAlphaOp(u8 tevstage,u8 tevop,u8 tevbias,u8 tevscale,u8 clamp,u8 tevregid); + +/*! + * \fn void GX_SetNumTexGens(u32 nr) + * \brief Sets the number of texture coordinates that are generated and available for use in the Texture Environment TEV stages. + * + * \details Texture coordinates are generated from input data as described by GX_SetTexCoordGen(). The generated texture coordinates are linked to + * specific textures and specific TEV stages using GX_SetTevOrder(). + * + * \note A consecutive number of texture coordinates may be generated, starting at GX_TEXCOORD0. A maximum of 8 texture coordinates may be generated. + * If \a nr is set to 0, no texture coordinates will be generated. In this case, at least one color channel must be output (see GX_SetNumChans()). + * + * \param[in] nr number of tex coords to generate, between 0 and 8 inclusive + * + * \return none + */ +void GX_SetNumTexGens(u32 nr); + +/*! + * \fn void GX_SetTexCoordGen(u16 texcoord,u32 tgen_typ,u32 tgen_src,u32 mtxsrc) + * \brief Specifies how texture coordinates are generated. + * + * \details Output texture coordinates are usually the result of some transform of an input attribute; either position, normal, or texture coordinate. + * You can also generate texture coordinates from the output color channel of the per-vertex lighting calculations. In C-language syntax the texture + * coordinate generation function would look like this:

+ * + *       \a texcoord = \a tgen_typ(\a tgen_src, \a mtxsrc);

+ * + * The current vertex descriptor as set by GX_SetVtxDesc() only describes the data input to the graphics processor. Using this function, you can create + * new output texture coordinates from the input data. The texcoord parameter is used to give the output texture coordinate a name. This texture + * coordinate can be bound to a texture using GX_SetTevOrder(). GX_SetNumTexGens() specifies a consecutive number of texture coordinates, starting at + * GX_TEXCOORD0, that are available to GX_SetTevOrder(). + * + * \ref texmtx defines a default set of texture matrix names that can be supplied as mtxsrc. The matrix names are actually row addresses (4 floats per + * row) in the matrix memory that indicate the first row of a loaded matrix. The user may define another memory map of matrix memory to suit their + * needs. Keep in mind, however, that modelview matrices (see GX_LoadPosMtxImm() and \ref pnmtx) and texture matrices share matrix memory. + * + * \note Input texture coordinates must always go through the texture coordinate generation hardware. GX_Init() initializes the hardware (by calling + * this function) so that all texture coordinates are transformed by the GX_IDENTITY matrix in order to appear as if input coordinates are passed + * unchanged through to the texture hardware. + * + * There are 8 output texture coordinates that can be referenced in any of the 16 TEV stages. There are a maximum of 8 input texture coordinates. + * + * \param[in] texcoord \ref texcoordid + * \param[in] tgen_typ \ref texgentyp + * \param[in] tgen_src \ref texgensrc + * \param[in] mtxsrc \ref texmtx + * + * \return none + */ +void GX_SetTexCoordGen(u16 texcoord,u32 tgen_typ,u32 tgen_src,u32 mtxsrc); + +/*! + * \fn void GX_SetTexCoordGen2(u16 texcoord,u32 tgen_typ,u32 tgen_src,u32 mtxsrc,u32 normalize,u32 postmtx) + * \brief An extension of GX_SetTexCoordGen(). It allows one to specify additional texgen options. + * + * \details The first four arguments are identical to those for GX_SetTexCoordGen() and function in the same way. All requirements for the first + * four arguments are the same as they are for that function as well. The new options only apply for "ordinary" texgens, where the texgen type is + * GX_TG_MTX2x4 or GX_TG_MTX3x4. They do not work for light-based texgens or emboss texgens. + * + * The \a normalize argument allows the computed texcoord to be normalized after the multiplication by \a mtxsrc (the first-pass transformation). + * After the optional normalization step, the texcoord is then multiplied by the 3x4 matrix \a postmtx. This matrix is refered to as the + * post-transform matrix. + * + * The result of this step is the texture coordinate that is used to look up the texture. + * + * \note The post-transform matrices are separate from the first pass matrices. They are stored in a separate memory area in the same format as the + * first pass matrices, except that all matrices have three rows.

+ * + * \note When a vertex contains only position and one texture coordinate and the texgen type is GX_TG_MTX2x4, there are certain limitations. In + * this special performance case, normalization is not performed (even if specified). + * + * \param[in] texcoord \ref texcoordid + * \param[in] tgen_typ \ref texgentyp + * \param[in] tgen_src \ref texgensrc + * \param[in] mtxsrc \ref texmtx + * \param[in] normalize if GX_TRUE, normalize tex coord after first-pass transform. Only used with GX_TG_MTX*. + * \param[in] postmtx \ref dttmtx + * + * \return none + */ +void GX_SetTexCoordGen2(u16 texcoord,u32 tgen_typ,u32 tgen_src,u32 mtxsrc,u32 normalize,u32 postmtx); + +/*! + * \fn void GX_SetZTexture(u8 op,u8 fmt,u32 bias) + * \brief Controls Z texture operations. + * + * \details Z textures can be used to implement image-based rendering algorithms. A composite image consisting of color and depth image planes can + * be merged into the Embedded Frame Buffer (EFB). + * + * Normally, the Z for a quad (2x2) of pixels is computed as a reference Z and two slopes. Once Z texturing is enabled, the Z is computed by adding + * a Z texel to the reference Z (\a op = GX_ZT_ADD) or by replacing the reference Z with the Z texel value (\a op = GX_ZT_REPLACE). + * + * Z textures are always the output from the last active Texture Environment (TEV) stage (see GX_SetNumTevStages()) when enabled. When Z texturing is + * enabled, the texture color of the last TEV stage is not available, but all other color inputs and operations are available. The pixel color is + * always output from the last active TEV stage. You cannot use the TEV to operate on the Z texture, it is fed directly into the Z texture logic. + * + * Z texel formats can be unsigned 8-bit (GX_TF_Z8), 16-bit (GX_TF_Z16), or 24-bit (GX_TF_Z24X8 (32-bit texture)) are used. The Graphics Processor + * converts the Z-textures to 24-bit values by placing the texel value in the least-significant bits and inserting zero's in the remaining + * most-significant bits. The 24-bit constant \a bias is added to the Z texture. If the pixel format is GX_PF_RGB565_Z16 the 24-bit result is converted + * to the current 16-bit Z format before comparing with the EFB's Z. + * + * \note The Z-texture calculation is done before the fog range calculation.

+ * + * \note GX_Init() disables Z texturing. + * + * \param[in] op \ref ztexop to perform + * \param[in] fmt \ref ztexfmt to use + * \param[in] bias Bias value. Format is 24bit unsigned. + * + * \return none + */ +void GX_SetZTexture(u8 op,u8 fmt,u32 bias); + +/*! + * \fn void GX_SetZMode(u8 enable,u8 func,u8 update_enable) + * \brief Sets the Z-buffer compare mode. + * + * \details The result of the Z compare is used to conditionally write color values to the Embedded Frame Buffer (EFB). + * + * When \a enable is set to GX_DISABLE, Z buffering is disabled and the Z buffer is not updated. + * + * The \a func parameter determines the comparison that is performed. In the comparison function, the newly rasterized Z value is on the left + * while the Z value from the Z buffer is on the right. If the result of the comparison is false, the newly rasterized pixel is discarded. + * + * The parameter \a update_enable determines whether or not the Z buffer is updated with the new Z value after a comparison is performed. This + * parameter also affects whether the Z buffer is cleared during copy operations (see GX_CopyDisp() and GX_CopyTex()). + * + * \param[in] enable Enables comparisons with source and destination Z values if GX_TRUE; disables compares otherwise. + * \param[in] func \ref compare + * \param[in] update_enable Enables Z-buffer updates when GX_TRUE; otherwise, Z-buffer updates are disabled, but compares may still be enabled. + * + * \return none + */ +void GX_SetZMode(u8 enable,u8 func,u8 update_enable); + +/*! + * \fn void GX_SetZCompLoc(u8 before_tex) + * \brief Sets whether Z buffering happens before or after texturing. + * + * \details Normally, Z buffering should happen before texturing, as this enables better performance by not texturing pixels that are not + * visible; however, when alpha compare is used, Z buffering must be done after texturing (see GX_SetAlphaCompare()). + * + * \param[in] before_tex Enables Z-buffering before texturing when set to GX_TRUE; otherwise, Z-buffering takes place after texturing. + * + * \return none + */ +void GX_SetZCompLoc(u8 before_tex); + +/*! + * \fn void GX_SetLineWidth(u8 width,u8 fmt) + * \brief Sets the width of line primitives. + * + * The parameter \a fmt is added to the texture coordinate to obtain texture coordinates at the other corners of a wide line. The \a fmt + * values are added after the texture coordinate generation operation; see GX_SetTexCoordGen(). + * + * \param[in] width width of the line in 1/16th pixel increments; maximum width is 42.5 px + * \param[in] fmt \ref texoff + * + * \return none + */ +void GX_SetLineWidth(u8 width,u8 fmt); + +/*! + * \fn void GX_SetPointSize(u8 width,u8 fmt) + * \brief Sets the size of point primitives. + * + * \details The parameter \a fmt is added to the texture coordinate(s), if any, to obtain texture coordinates at the other corners of a point. The + * \a fmts are added after the texture coordinate generation operation; see GX_SetTexCoordGen(). + * + * \param[in] width width of the point in 1/16th pixel increments; maximum width is 42.5 px + * \param[in] fmt \ref texoff + * + * \return none + */ +void GX_SetPointSize(u8 width,u8 fmt); + +/*! + * \fn void GX_SetBlendMode(u8 type,u8 src_fact,u8 dst_fact,u8 op) + * \brief Determines how the source image, generated by the graphics processor, is blended with the Embedded Frame Buffer (EFB). + * + * \details When \a type is set to GX_BM_NONE, the source data is written directly to the EFB. When \a type is set to GX_BM_BLEND, the source color and EFB + * pixels are blended using the following equation: + * + *       dst_pix_clr = src_pix_clr * \a src_fact + dst_pix_clr * \a dst_fact + * + * The GX_BL_DSTALPHA / GX_BL_INVDSTALPHA can be used only when the EFB has GX_PF_RGBA6_Z24 as the pixel format (see GX_SetPixelFmt()). If the pixel + * format is GX_PF_RGBA6_Z24 then the \a src_fact and \a dst_fact are also applied to the alpha channel. To write the alpha channel to the EFB you must + * call GX_SetAlphaUpdate(). + * + * When type is set to GX_BM_LOGIC, the source and EFB pixels are blended using logical bitwise operations. When type is set to GX_BM_SUBTRACT, the destination + * pixel is computed as follows: + * + *       dst_pix_clr = dst_pix_clr - src_pix_clr [clamped to zero] + * + * Note that \a src_fact and \a dst_fact are not part of this equation. + * + * \note Color updates should be enabled by calling GX_SetColorUpdate(). + * + * \param[in] type \ref blendmode + * \param[in] src_fact \ref blendfactor + * \param[in] dst_fact \ref blendfactor + * \param[in] op \ref logicop + * + * \return none + */ +void GX_SetBlendMode(u8 type,u8 src_fact,u8 dst_fact,u8 op); + +/*! + * \fn void GX_SetCullMode(u8 mode) + * \brief Enables or disables culling of geometry based on its orientation to the viewer. + * + * \details Primitives in which the vertex order is clockwise to the viewer are considered front-facing. + * + * \note GX_Init() sets this to GX_CULL_BACK. + * + * \param[in] mode \ref cullmode + * + * \return none + */ +void GX_SetCullMode(u8 mode); + +/*! + * \fn void GX_SetCoPlanar(u8 enable) + * \brief Enables or disables coplanar triangle processing. + * + * \details While coplanar mode is enabled, all subsequent triangles (called decal triangles) will have the same Z coefficients as the reference + * plane. Coplanar mode should be enabled immediately after a reference triangle is drawn. + * + * \note The reference triangle can be culled using GX_SetCullMode(GX_CULL_ALL) to create an invisible reference plane; however, the reference + * triangle must not be completely out of view, i.e. trivially rejected by clipping.

+ * + * \note GX_Init() disables coplanar mode. + * + * \param[in] enable when GX_ENABLE, coplanar mode is enabled; GX_DISABLE disables this mode + * + * \return none + */ +void GX_SetCoPlanar(u8 enable); + +/*! + * \fn void GX_EnableTexOffsets(u8 coord,u8 line_enable,u8 point_enable) + * \brief Enables a special texture offset feature for points and lines. + * + * \details When a point's size is defined using GX_SetPointSize() or a line's width is described using GX_SetLineWidth(), you can also specify a second + * parameter. The parameter \a fmt is added to the texture coordinate(s), if any, to obtain texture coordinates at the other corners of a + * point or line. The \a fmts are added after the texture coordinate generation operation; see GX_SetTexCoordGen(). This function enables this + * operation for a particular texture coordinate. Offset operations for points and lines are enabled separately. If the enables are false, the same + * texture coordinate is used for every vertex of the line or point. + * + * \param[in] coord \ref texcoordid + * \param[in] line_enable enable or disable tex offset calculation for lines + * \param[in] point_enable enable or disable tex offset calculation for points + * + * \return none + */ +void GX_EnableTexOffsets(u8 coord,u8 line_enable,u8 point_enable); + +/*! + * \fn void GX_SetClipMode(u8 mode) + * \brief Enables or disables clipping of geometry. + * + * \details This may help performance issues that occur when objects are far-clipped; however, any near-clipped objects will be rendered incorrectly. + * + * \note GX_Init() sets this to GX_CLIP_ENABLE. + * + * \param[in] mode \ref clipmode + * + * \return none + */ +void GX_SetClipMode(u8 mode); + +/*! + * \fn void GX_SetScissor(u32 xOrigin,u32 yOrigin,u32 wd,u32 ht) + * \brief Sets the scissor rectangle. + * + * \details The scissor rectangle specifies an area of the screen outside of which all primitives are culled. This function sets the scissor rectangle in + * screen coordinates. The screen origin (\a xOrigin=0, \a yOrigin=0) is at the top left corner of the display. + * + * The values may be within the range of 0 - 2047 inclusive. Using values that extend beyond the EFB size is allowable since the scissor box may be + * repositioned within the EFB using GX_SetScissorBoxOffset(). + * + * \note By default, the scissor rectangle is set to match the viewport rectangle. GX_Init() initializes the scissor rectangle to match the viewport + * given the current render mode. + * + * \param[in] xOrigin left-most coord in screen coordinates + * \param[in] yOrigin top-most coord in screen coordinates + * \param[in] wd width of the scissorbox in screen coordinates + * \param[in] ht height of the scissorbox in screen coordinates + * + * \return none + */ +void GX_SetScissor(u32 xOrigin,u32 yOrigin,u32 wd,u32 ht); + +/*! + * \fn void GX_SetScissorBoxOffset(s32 xoffset,s32 yoffset) + * \brief Repositions the scissorbox rectangle within the Embedded Frame Buffer (EFB) memory space. + * + * \details The offsets are subtracted from the screen coordinates to determine the actual EFB coordinates where the pixels are stored. Thus with + * positive offsets, the scissor box may be shifted left and/or up; and with negative offsets, the scissor box may be shifted right and/or down. + * + * The intended use for this command is to make it easy to do two-pass antialiased rendering. To draw the top half of the screen, the scissor box is set to + * the top and the offset set to zero. To draw the bottom half, the scissor box is set to the bottom, and the offset is set to shift the scissor box back up + * to the top. + * + * Another use for the offset is to displace how an image is rendered with respect to the dither matrix. Since the dither matrix is 4x4, a \a yoffset of -2 + * shifts the image down by 2 lines with respect to the matrix. This can be useful for field-rendering mode. + * + * \note Achieving an offset of an odd number of lines is possible, but more difficult than just changing the scissor box: one must render and copy 2 + * additional lines, then skip one by adjusting the argument of VIDEO_SetNextFrameBuffer().

+ * + * \note GX_Init() initializes the scissor box offset to zero. Since the GP works on 2x2 regions of pixels, only even offsets are allowed. + * + * \param[in] xoffset number of pixels to shift the scissorbox left, between -342 - 382 inclusive; must be even + * \param[in] yoffset number of pixels to shift the scissorbox up, between -342 - 494 inclusive; must be even + * + * \return none + */ +void GX_SetScissorBoxOffset(s32 xoffset,s32 yoffset); + +/*! + * \fn void GX_SetNumChans(u8 num) + * \brief Sets the number of color channels that are output to the TEV stages. + * + * \details Color channels are the mechanism used to compute per-vertex lighting effects. Color channels are controlled using GX_SetChanCtrl(). + * Color channels are linked to specific TEV stages using GX_SetTevOrder(). + * + * This function basically defines the number of per-vertex colors that get rasterized. If \a num is set to 0, then at least one texture coordinate + * must be generated (see GX_SetNumTexGens()). If \a num is set to 1, then channel GX_COLOR0A0 will be rasterized. If \a num is set to 2 (the maximum + * value), then GX_COLOR0A0 and GX_COLOR1A1 will be rasterized. + * + * \param[in] num number of color channels to rasterize; number must be 0, 1 or 2 + * + * \return none + */ +void GX_SetNumChans(u8 num); + +/*! + * \fn void GX_SetTevOrder(u8 tevstage,u8 texcoord,u32 texmap,u8 color) + * \brief Specifies the texture and rasterized color that will be available as inputs to this TEV \a tevstage. + * + * The texture coordinate \a texcoord is generated from input attributes using the GX_SetTexCoordGen() function and is used to look up the + * texture map, previously loaded by GX_LoadTexObj(). The \a color to rasterize for this \a tevstage is also specified. The color + * is the result of per-vertex lighting which is controlled by GX_SetChanCtrl(). + * + * This function will scale the normalized texture coordinates produced by GX_SetTexCoordGen() according to the size of the texture map in the + * function call. For this reason, texture coordinates can only be broadcast to multiple texture maps if and only if the maps are the same size. In + * some cases, you may want to generate a texture coordinate having a certain scale, but disable the texture lookup (this comes up when generating + * texture coordinates for indirect bump mapping). To accomplish this, use the GX_TEXMAP_DISABLE flag: + * + * \code GX_SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD0, GX_TEXMAP3 | GX_TEXMAP_DISABLE, GX_COLORNULL); \endcode + * + * \details This will scale GX_TEXCOORD0 using GX_TEXMAP3 but disable the lookup of GX_TEXMAP3. + * + * \note This function does not enable the TEV stage. To enable a consecutive number of TEV stages, starting at stage GX_TEVSTAGE0, use GX_SetNumTevStages().

+ * + * \note The operation of each TEV stage is independent. The color operations are controlled by GX_SetTevColorIn() and GX_SetTevColorOp(). The alpha + * operations are controlled by GX_SetTevAlphaIn() and GX_SetTevAlphaOp().

+ * + * \note The number of texture coordinates available for all the active TEV stages is set using GX_SetNumTexGens(). The number of color channels + * available for all the active TEV stages is set using GX_SetNumChans(). Active TEV stages should not reference more texture coordinates or colors + * than are being generated.

+ * + * \note There are some special settings for the \a color argument. If you specify GX_COLOR_ZERO, you always get zero as rasterized color. If you specify + * GX_ALPHA_BUMP or GX_ALPHA_BUMPN, you can use "Bump alpha" component from indirect texture unit as rasterized color input (see GX_SetTevIndirect() + * for details about how to configure bump alpha). Since bump alpha contains only 5-bit data, GX_ALPHA_BUMP shifts them to higher bits, which makes the + * value range 0-248. Meanwhile GX_ALPHA_BUMPN performs normalization and you can get the value range 0-255. + * + * \param[in] tevstage \ref tevstage + * \param[in] texcoord \ref texcoordid + * \param[in] texmap \ref texmapid + * \param[in] color \ref channelid + * + * \return none + */ +void GX_SetTevOrder(u8 tevstage,u8 texcoord,u32 texmap,u8 color); + +/*! + * \fn void GX_SetNumTevStages(u8 num) + * \brief Enables a consecutive number of TEV stages. + * + * \details The output pixel color (before fogging and blending) is the result from the last stage. The last TEV stage must write to register GX_TEVPREV; + * see GX_SetTevColorOp() and GX_SetTevAlphaOp(). At least one TEV stage must be enabled. If a Z-texture is enabled, the Z texture must be looked up on + * the last stage; see GX_SetZTexture(). + * + * \note The association of lighting colors, texture coordinates, and texture maps with a TEV stage is set using GX)SetTevOrder(). The number of texture + * coordinates available is set using GX_SetNumTexGens(). The number of color channels available is set using GX_SetNumChans().

+ * + * \note GX_Init() will set \a num to 1. + * + * \param[in] num number of active TEV stages, between 1 and 16 inclusive + * + * \return none + */ +void GX_SetNumTevStages(u8 num); + +/*! + * \fn void GX_SetAlphaCompare(u8 comp0,u8 ref0,u8 aop,u8 comp1,u8 ref1) + * \brief Sets the parameters for the alpha compare function which uses the alpha output from the last active TEV stage. + * + * \details The alpha compare operation is:

+ * + *       alpha_pass = (alpha_src (\a comp0) \a ref0) (\a op) (alpha_src (\a comp1) \a ref1)

+ * + * where alpha_src is the alpha from the last active TEV stage. As an example, you can implement these equations:

+ * + *       alpha_pass = (alpha_src \> \a ref0) AND (alpha_src \< \a ref1) + * + * or + * + *       alpha_pass = (alpha_src \> \a ref0) OR (alpha_src \< \a ref1) + * + * \note The output alpha can be used in the blending equation (see GX_SetBlendMode()) to control how source and destination (frame buffer) + * pixels are combined.

+ * + * \note The Z compare can occur either before or after texturing (see GX_SetZCompLoc()). In the case where Z compare occurs before texturing, the Z is + * written based only on the Z test. The color is written if both the Z test and alpha test pass. When Z compare occurs after texturing, the color + * and Z are written if both the Z test and alpha test pass. When using texture to make cutout shapes (like billboard trees) that need to be correctly Z + * buffered, you should configure the pipeline to Z buffer after texturing.

+ * + * \note The number of active TEV stages is specified using GX_SetNumTevStages(). + * + * \param[in] comp0 \ref compare subfunction 0 + * \param[in] ref0 reference val for subfunction 0 + * \param[in] aop \ref alphaop for combining subfunctions 0 and 1; must not be GX_MAX_ALPHAOP + * \param[in] comp1 \ref compare subfunction 1 + * \param[in] ref1 reference val for subfunction 1 + * + * \return none + */ +void GX_SetAlphaCompare(u8 comp0,u8 ref0,u8 aop,u8 comp1,u8 ref1); + +/*! + * \fn void GX_SetTevKColor(u8 sel, GXColor col) + * \brief Sets one of the "konstant" color registers in the TEV unit. + * + * \details These registers are available to all TEV stages. They are constant in the sense that they cannot be written to be the TEV itself. + * + * \param[in] sel \ref tevkcolorid + * \param[in] col constant color value + * + * \return none + */ +void GX_SetTevKColor(u8 sel, GXColor col); + +/*! + * \fn void GX_SetTevKColorSel(u8 tevstage,u8 sel) + * \brief Selects a "konstant" color input to be used in a given TEV stage. + * + * The constant color input is used only if GX_CC_KONST is selected for an input for that TEV stage. Only one constant color selection is + * available for a given TEV stage, though it may be used for more than one input. + * + * \param[in] tevstage \ref tevstage + * \param[in] sel \ref tevkcolorsel + * + * \return none + */ +void GX_SetTevKColorSel(u8 tevstage,u8 sel); + +/*! + * \fn void GX_SetTevKAlphaSel(u8 tevstage,u8 sel) + * \brief Selects a "konstant" alpha input to be used in a given TEV stage. + * + * \details The constant alpha input is used only if GX_CA_KONST is selected for an input for that TEV stage. Only one constant alpha selection is + * available for a given TEV stage, though it may be used for more than one input. + * + * \param[in] tevstage \ref tevstage + * \param[in] sel \ref tevkalphasel + * + * \return none + */ +void GX_SetTevKAlphaSel(u8 tevstage,u8 sel); + +/*! + * \fn void GX_SetTevKColorS10(u8 sel, GXColorS10 col) + * \brief Used to set one of the constant color registers in the Texture Environment (TEV) unit. + * + * \details These registers are available to all TEV stages. At least one of these registers is used to pass the output of one TEV stage to the next + * in a multi-texture configuration. + * + * \note The application is responsible for allocating these registers so that no collisions in usage occur.

+ * + * \note This function takes 10-bit signed values as color values; use GX_SetTevColor() to give 8-bit values. + * + * \param[in] sel \ref tevcoloutreg + * \param[in] col constant color value + * + * \return none + */ +void GX_SetTevKColorS10(u8 sel, GXColorS10 col); + +/*! + * \fn void GX_SetTevSwapMode(u8 tevstage,u8 ras_sel,u8 tex_sel) + * \brief Selects a set of swap modes for the rasterized color and texture color for a given TEV stage. + * + * \details This allows the color components of these inputs to be rearranged or duplicated. + * + * \note There are four different swap mode table entries, and each entry in the table specifies how the RGBA inputs map to the RGBA outputs. + * + * \param[in] tevstage \ref tevstage + * \param[in] ras_sel selects a swap mode for the rasterized color input. + * \param[in] tex_sel selects a swap mode for the texture color input. + * + * \return none + */ +void GX_SetTevSwapMode(u8 tevstage,u8 ras_sel,u8 tex_sel); + +/*! + * \fn void GX_SetTevSwapModeTable(u8 swapid,u8 r,u8 g,u8 b,u8 a) + * \brief Sets up the TEV color swap table. + * + * \details The swap table allows the rasterized color and texture color to be swapped component-wise. An entry in the table specifies how the + * input color components map to the output color components. + * + * \param[in] swapid \ref tevswapsel + * \param[in] r input color component that should be mapped to the red output component. + * \param[in] g input color component that should be mapped to the green output component. + * \param[in] b input color component that should be mapped to the blue output component. + * \param[in] a input color component that should be mapped to the alpha output component. + * + * \return none + */ +void GX_SetTevSwapModeTable(u8 swapid,u8 r,u8 g,u8 b,u8 a); + +/*! + * \fn void GX_SetTevIndirect(u8 tevstage,u8 indtexid,u8 format,u8 bias,u8 mtxid,u8 wrap_s,u8 wrap_t,u8 addprev,u8 utclod,u8 a) + * \brief Controls how the results from an indirect lookup will be used to modify a given regular TEV stage lookup. + * + * \param[in] tevstage \ref tevstage being affected + * \param[in] indtexid \ref indtexstage results to use with this TEV stage + * \param[in] format \ref indtexformat, i.e. how many bits to extract from the indirect result color to use in indirect offsets and the indirect "bump" alpha + * \param[in] bias \ref indtexbias to be applied to each component of the indirect offset + * \param[in] mtxid which \ref indtexmtx and scale value to multiply the offsets with + * \param[in] wrap_s \ref indtexwrap to use with the S component of the regular texture coordinate + * \param[in] wrap_t \ref indtexwrap to use with the T component of the regular texture coordinate + * \param[in] addprev whether the tex coords results from the previous TEV stage should be added in + * \param[in] utclod whether to the unmodified (GX_TRUE) or modified (GX_FALSE) tex coords for mipmap LOD computation + * \param[in] a which offset component will supply the \ref indtexalphasel, if any + * + * \return none + */ +void GX_SetTevIndirect(u8 tevstage,u8 indtexid,u8 format,u8 bias,u8 mtxid,u8 wrap_s,u8 wrap_t,u8 addprev,u8 utclod,u8 a); + +/*! + * \fn void GX_SetTevDirect(u8 tevstage) + * \brief Used to turn off all indirect texture processing for the specified regular TEV stage. + * + * \param[in] tevstage the \ref tevstage to change + * + * \return none + */ +void GX_SetTevDirect(u8 tevstage); + +/*! + * \fn void GX_SetNumIndStages(u8 nstages) + * \brief Used to set how many indirect lookups will take place. + * + * \details The results from these indirect lookups may then be used to alter the lookups for any number of regular TEV stages. + * + * \param[in] nstages number of indirect lookup stages + * + * \return none + */ +void GX_SetNumIndStages(u8 nstages); + +/*! + * \fn void GX_SetIndTexOrder(u8 indtexstage,u8 texcoord,u8 texmap) + * \brief Used to specify the \a texcoord and \a texmap to used with a given indirect lookup. + * + * \param[in] indtexstage \ref indtexstage being affected + * \param[in] texcoord \ref texcoordid to be used for this stage + * \param[in] texmap \ref texmapid to be used for this stage + * + * \return none + */ +void GX_SetIndTexOrder(u8 indtexstage,u8 texcoord,u8 texmap); + +/*! + * \fn void GX_SetIndTexCoordScale(u8 indtexid,u8 scale_s,u8 scale_t) + * \brief Allows the sharing of a texcoord between an indirect stage and a regular TEV stage. + * + * It allows the texture coordinates to be scaled down for use with an indirect map that is smaller than the corresponding regular map. + * + * \param[in] indtexid \ref indtexstage being affected + * \param[in] scale_s \ref indtexscale factor for the S coord + * \param[in] scale_t \ref indtexscale factor for the T coord + * + * \return none + */ +void GX_SetIndTexCoordScale(u8 indtexid,u8 scale_s,u8 scale_t); + +/*! + * \fn void GX_SetFog(u8 type,f32 startz,f32 endz,f32 nearz,f32 farz,GXColor col) + * \brief Enables fog. + * + * \details Using \a type, the programmer may select one of several functions to control the fog density as a function of range to a quad (2x2 pixels). + * Range is cosine corrected Z in the x-z plane (eye coordinates), but is not corrected in the y direction (see GX_SetFogRangeAdj()). The parameters \a startz and + * \a endz allow further control over the fog behavior. The parameters \a nearz and \a farz should be set consistent with the projection matrix parameters. Note that these + * parameters are defined in eye-space. The fog color, in RGBX format (i.e. the alpha component is ignored), is set using the \a col parameter. This will be the + * color of the pixel when fully fogged. + * + * \note GX_Init() turns fog off by default. + * + * \param[in] type \ref fogtype to use + * \param[in] startz minimum Z value at which the fog function is active + * \param[in] endz maximum Z value at which the fog function is active + * \param[in] nearz near plane (which should match the projection matrix parameters) + * \param[in] farz far plane (which should match the projection matrix parameters) + * \param[in] col fog color; alpha component is ignored + * + * \return none + */ +void GX_SetFog(u8 type,f32 startz,f32 endz,f32 nearz,f32 farz,GXColor col); + +/*! + * \fn void GX_SetFogRangeAdj(u8 enable,u16 center,GXFogAdjTbl *table) + * \brief Enables or disables horizontal fog-range adjustment. + * + * \details This adjustment is a factor that is multiplied by the eye-space Z used for fog computation; it is based upon the X position of the pixels being + * rendered. The Y direction is not compensated. This effectively increases the fog density at the edges of the screen, making for a more realistic fog + * effect. The adjustment is computed per quad (2x2 pixels), not per-pixel. The center of the viewport is specified using \a center. The range adjustment + * table is specified using \a table. The range adjust function is mirrored horizontally about the \a center. + * + * \note GX_Init() disables range adjustment. + * + * \sa GX_InitFogAdjTable() + * + * \param[in] enable enables adjustment when GX_ENABLE is passed; disabled with GX_DISABLE + * \param[in] center centers the range adjust function; normally corresponds with the center of the viewport + * \param[in] table range adjustment parameter table + * + * \return none + */ +void GX_SetFogRangeAdj(u8 enable,u16 center,GXFogAdjTbl *table); + +/*! + * \fn GX_SetFogColor(GXColor color) + * \brief Sets the fog color. + * + * \details \a color is the color that a pixel will be if fully fogged. Alpha channel is ignored. + * + * \param[in] color color to set fog to + */ +void GX_SetFogColor(GXColor color); + +/*! + * \fn void GX_InitFogAdjTable(GXFogAdjTbl *table,u16 width,f32 projmtx[4][4]) + * \brief Generates the standard range adjustment table and puts the results into \a table. + * + * \details This table can be used by GX_SetFogRangeAdj() to adjust the eye-space Z used for fog based upon the X position of the pixels being rendered. + * The Y direction is not compensated. This effectively increases the fog density at the edges of the screen, making for a more realistic fog effect. The + * width of the viewport is specified using \a width. The \a projmtx parameter is the projection matrix that is used to render into the viewport. It must + * be specified so that the function can compute the X extent of the viewing frustum in eye space. + * + * \note You must allocate \a table yourself. + * + * \param[in] table range adjustment parameter table + * \param[in] width width of the viewport + * \param[in] projmtx projection matrix used to render into the viewport + */ +void GX_InitFogAdjTable(GXFogAdjTbl *table,u16 width,f32 projmtx[4][4]); + +/*! + * \fn void GX_SetIndTexMatrix(u8 indtexmtx,f32 offset_mtx[2][3],s8 scale_exp) + * \brief Sets one of the three static indirect matrices and the associated scale factor. + * + * \details The indirect matrix and scale is used to process the results of an indirect lookup in order to produce offsets to use during a regular lookup. + * The matrix is multiplied by the [S T U] offsets that have been extracted (and optionally biased) from the indirect lookup color. In this matrix-vector + * multiply, the matrix is on the left and the [S T U] column vector is on the right. + * + * \note The matrix values are stored in the hardware as a sign and 10 fractional bits (two's complement); thus the smallest number that can be stored is + * -1 and the largest is (1 - 1/1024) or approximately 0.999. Since +1 cannot be stored, you may consider dividing all the matrix values by 2 (thus +1 + * becomes +0.5) and adding one to the scale value in order to compensate. + * + * \param[in] indtexmtx \ref indtexmtx that is being affected + * \param[in] offset_mtx values to assign to the indirect matrix + * \param[in] scale_exp exponent to use for the associated scale factor + * + * \return none + */ +void GX_SetIndTexMatrix(u8 indtexmtx,f32 offset_mtx[2][3],s8 scale_exp); + +/*! + * \fn void GX_SetTevIndBumpST(u8 tevstage,u8 indstage,u8 mtx_sel) + * \brief Sets up an environment-mapped bump-mapped indirect lookup. + * + * \details The indirect map specifies offsets in (S,T) space. This kind of lookup requires 3 TEV stages to compute. As a result of all this work, a simple + * 2D bump map is properly oriented to the surface to which it is applied. It is used to alter a normal-based texgen which then looks up an environment map. + * The environment map may be a simple light map, or else it may be a reflection map of the surrounding scenery. + * + * \note When using this function, texture lookup should be disabled for the first two TEV stages. The third stage is where the texture lookup is actually performed. + * The associated geometry must supply normal/binormal/tangent coordinates at each vertex. Appropriate texgens must supply each of these to the proper stages + * (binormal to the first, tangent to the second, and normal to the third). Although a static indirect matrix is not used, one must choose a matrix slot and set up + * the associated scale value to be used with this lookup. + * + * \param[in] tevstage \ref tevstage that is being affected + * \param[in] indstage \ref indtexstage results to use with this TEV stage + * \param[in] mtx_sel which \ref indtexmtx to multiply the offsets with + * + * \return none + */ +void GX_SetTevIndBumpST(u8 tevstage,u8 indstage,u8 mtx_sel); + +/*! + * \fn void GX_SetTevIndBumpXYZ(u8 tevstage,u8 indstage,u8 mtx_sel) + * \brief Sets up an environment-mapped bump-mapped indirect lookup. + * + * \details The indirect map specifies offsets in object (X, Y, Z) space. This kind of lookup requires only one TEV stage to compute; however, the bump map (indirect + * map) used is geometry-specific. Thus there is a space/computation tradeoff between using this function and using GX_SetTevIndBumpST(). + * + * \note The indirect matrix must be loaded with a transformation for normals from object space to texture space (similar to eye space, but possibly with an inverted + * Y axis). The surface geometry need only provide regular normals at each vertex. A normal-based texgen must be set up for the regular texture coordinate. + * + * \param[in] tevstage \ref tevstage that is being affected + * \param[in] indstage \ref indtexstage results to use with this TEV stage + * \param[in] mtx_sel which \ref indtexmtx to multiply the offsets with + * + * \return none + */ +void GX_SetTevIndBumpXYZ(u8 tevstage,u8 indstage,u8 mtx_sel); + +/*! + * \fn void GX_SetTevIndTile(u8 tevstage,u8 indtexid,u16 tilesize_x,u16 tilesize_y,u16 tilespacing_x,u16 tilespacing_y,u8 indtexfmt,u8 indtexmtx,u8 bias_sel,u8 alpha_sel) + * \brief Used to implement tiled texturing using indirect textures. + * + * \details It will set up the correct values in the given indirect matrix; you only need to specify which matrix slot to use. + * + * \note The regular texture map contains only the tile definitions. The actual texture size to be applied to the polygon being drawn is the product of the base tile + * size and the size of the indirect map. In order to set the proper texture coordinate scale, one must call GX_SetTexCoordScaleManually(). One can also use + * GX_SetIndTexCoordScale() in order to use the same texcoord for the indirect stage as the regular TEV stage. + * + * \param[in] tevstage \ref tevstage that is being affected + * \param[in] indtexid \ref indtexstage results to use with this TEV stage + * \param[in] tilesize_x size of the tile in the X dimension + * \param[in] tilesize_y size of the tile in the Y dimension + * \param[in] tilespacing_x spacing of the tiles (in the tile-definition map) in the X dimension + * \param[in] tilespacing_y spacing of the tiles (in the tile-definition map) in the Y dimension + * \param[in] indtexfmt \ref indtexformat to use + * \param[in] indtexmtx \ref indtexmtx to multiply the offsets with + * \param[in] bias_sel \ref indtexbias to indicate tile stacking direction for pseudo-3D textures + * \param[in] alpha_sel which \ref indtexalphasel will supply the indirect "bump" alpha, if any (for pseudo-3D textures). + * + * \return none + */ +void GX_SetTevIndTile(u8 tevstage,u8 indtexid,u16 tilesize_x,u16 tilesize_y,u16 tilespacing_x,u16 tilespacing_y,u8 indtexfmt,u8 indtexmtx,u8 bias_sel,u8 alpha_sel); + +/*! + * \fn void GX_SetTevIndRepeat(u8 tevstage) + * \brief Set a given TEV stage to use the same texture coordinates as were computed in the previous stage. + * + * \note This is only useful when the previous stage texture coordinates took more than one stage to compute, as is the case for GX_SetTevIndBumpST(). + * + * \param[in] tevstage \ref tevstage to modify + * + * \return none + */ +void GX_SetTevIndRepeat(u8 tevstage); + +/*! + * \fn void GX_SetColorUpdate(u8 enable) + * \brief Enables or disables color-buffer updates when rendering into the Embedded Frame Buffer (EFB). + * + * \note This function also affects whether the color buffer is cleared during copies; see GX_CopyDisp() and GX_CopyTex(). + * + * \param[in] enable enables color-buffer updates with GX_TRUE + * + * \return none + */ +void GX_SetColorUpdate(u8 enable); + +/*! + * \fn void GX_SetAlphaUpdate(u8 enable) + * \brief Enables or disables alpha-buffer updates of the Embedded Frame Buffer (EFB). + * + * \note This function also affects whether the alpha buffer is cleared during copy operations; see GX_CopyDisp() and GX_CopyTex().

+ * + * \note The only EFB pixel format supporting an alpha buffer is GX_PF_RGBA6_Z24; see GX_SetPixelFmt(). The alpha \a enable is ignored for non-alpha + * pixel formats. + * + * \param[in] enable enables alpha-buffer updates with GX_TRUE + * + * \return none + */ +void GX_SetAlphaUpdate(u8 enable); + +/*! + * \fn void GX_SetPixelFmt(u8 pix_fmt,u8 z_fmt) + * \brief Sets the format of pixels in the Embedded Frame Buffer (EFB). + * + * \details There are two non-antialiased \a pix_fmts: GX_PF_RGB8_Z24 and GX_PF_RGBA6_Z24. The stride of the EFB is fixed at 640 pixels. The + * non-antialiased EFB has 528 lines available. + * + * When \a pix_fmt is set to GX_PF_RGB565_Z16, multi-sample antialiasing is enabled. In order to get proper results, one must also call GX_SetCopyFilter(). + * The position of the subsamples and the antialiasing filter coefficients are set using GX_SetCopyFilter(). When antialiasing, three 16b color/Z + * samples are computed for each pixel, and the total available number of pixels in the EFB is reduced by half (640 pixels x 264 lines). This function also sets the + * compression type for 16-bit Z formats, which allows trading off Z precision for range. The following guidelines apply:

+ * + *       a) far/near ratio <= 2^16, use GX_ZC_LINEAR
+ *       b) far/near ratio <= 2^18, use GX_ZC_NEAR
+ *       c) far/near ratio <= 2^20, use GX_ZC_MID
+ *       d) far/near ratio <= 2^24, use GX_ZC_FAR

+ * + * It is always best to use as little compression as possible (choice "a" is least compressed, choice "d" is most compressed). You get less precision with higher compression. + * The "far" in the above list does not necessarily refer to the far clipping plane. You should think of it as the farthest object you want correct occlusion for. + * + * \note This function also controls antialiasing (AA) mode.

+ * + * \note Since changing pixel format requires the pixel pipeline to be synchronized, the use of this function causes stall of the graphics processor as a result. Therefore, + * you should avoid redundant calls of this function. + * + * \param[in] pix_fmt GX_PF_RGB8_Z24 or GX_PF_RGBA6_Z24 for non-AA, GX_PF_RGB565_Z16 for AA + * \param[in] z_fmt \ref zfmt to use + * + * \return none + */ +void GX_SetPixelFmt(u8 pix_fmt,u8 z_fmt); + +/*! + * \fn void GX_SetDither(u8 dither) + * \brief Enables or disables dithering. + * + * \details A 4x4 Bayer matrix is used for dithering. + * + * \note Only valid when the pixel format (see GX_SetPixelFmt()) is either GX_PF_RGBA6_Z24 or GX_PF_RGB565_Z16.

+ * + * \note Dithering should probably be turned off if you are planning on using the result of rendering for comparisons (e.g. outline rendering + * algorithm that writes IDs to the alpha channel, copies the alpha channel to a texture, and later compares the texture in the TEV). + * + * \param[in] dither enables dithering if GX_TRUE is given and pixel format is one of the two above, otherwise disabled + * + * \return none + */ +void GX_SetDither(u8 dither); + +/*! + * \fn void GX_SetDstAlpha(u8 enable,u8 a) + * \brief Sets a constant alpha value for writing to the Embedded Frame Buffer (EFB). + * + * \note To be effective, the EFB pixel type must have an alpha channel (see GX_SetPixelFmt()). The alpha compare operation (see + * GX_SetAlphaCompare()) and blending operations (see GX_SetBlendMode()) still use source alpha (output from the last TEV stage) but when + * writing the pixel color, the constant alpha will replace the pixel alpha in the EFB. + * + * \param[in] enable \a a will be written to the framebuffer if GX_ENABLE is here and frame buffer pixel format supports destination alpha + * \param[in] a constant alpha value + * + * \return none + */ +void GX_SetDstAlpha(u8 enable,u8 a); + +/*! + * \fn void GX_SetFieldMask(u8 even_mask,u8 odd_mask) + * \brief selectively enables and disables interlacing of the frame buffer image. + * + * \details This function is used when rendering fields to an interlaced Embedded Frame Buffer (EFB). + * + * \note When the mask is GX_FALSE, that field will not be written to the EFB, but the other field will be computed. In other words, you pay the + * fill rate price of a frame to produce a field. + * + * \param[in] even_mask whether to write pixels with even Y coordinate + * \param[in] odd_mask whether to write pixels with odd Y coordinate + * + * \return none + */ +void GX_SetFieldMask(u8 even_mask,u8 odd_mask); + +/*! + * \fn void GX_SetFieldMode(u8 field_mode,u8 half_aspect_ratio) + * \brief Controls various rasterization and texturing parameters that relate to field-mode and double-strike rendering. + * + * \details In field-mode rendering, one must adjust the vertical part of the texture LOD computation to account for the fact that pixels cover only half of + * the space from one rendered scan line to the next (with the other half of the space filled by a pixel from the other field). In both field-mode and + * double-strike rendering, one must adjust the aspect ratio for points and lines to account for the fact that pixels will be double-height when displayed + * (the pixel aspect ratio is 1/2). + * + * \note The values set here usually come directly from the render mode. The \a field_rendering flags goes straight into \a field_mode. The \a half_aspect_ratio + * parameter is true if the \a xfbHeight is half of the \a viHeight, false otherwise.

+ * + * \note GX_Init() sets both fields according to the default render mode.

+ * + * \note On production hardware (i.e. a retail GameCube), only line aspect-ratio adjustment is implemented. Points are not adjusted. + * + * \param[in] field_mode adjusts texture LOD computation as described above if true, otherwise does not + * \param[in] half_aspect_ratio adjusts line aspect ratio accordingly, otherwise does not + * + * \return none + */ +void GX_SetFieldMode(u8 field_mode,u8 half_aspect_ratio); + +/*! + * \fn f32 GX_GetYScaleFactor(u16 efbHeight,u16 xfbHeight) + * \brief Calculates an appropriate Y scale factor value for GX_SetDispCopyYScale() based on the height of the EFB and + * the height of the XFB. + * + * \param[in] efbHeight Height of embedded framebuffer. Range from 2 to 528. Should be a multiple of 2. + * \param[in] xfbHeight Height of external framebuffer. Range from 2 to 1024. Should be equal or greater than \a efbHeight. + * + * \return Y scale factor which can be used as argument of GX_SetDispCopyYScale(). + */ +f32 GX_GetYScaleFactor(u16 efbHeight,u16 xfbHeight); + +/*! + * \fn u32 GX_SetDispCopyYScale(f32 yscale) + * \brief Sets the vertical scale factor for the EFB to XFB copy operation. + * + * \details The number of actual lines copied is returned, based on the current EFB height. You can use this number to allocate the proper XFB size. You + * have to call GX_SetDispCopySrc() prior to this function call if you want to get the number of lines by using this function. + * + * \param[in] yscale Vertical scale value. Range from 1.0 to 256.0. + * + * \return Number of lines that will be copied. + */ +u32 GX_SetDispCopyYScale(f32 yscale); + +/*! + * \fn void GX_SetDispCopySrc(u16 left,u16 top,u16 wd,u16 ht) + * \brief Sets the source parameters for the EFB to XFB copy operation. + * + * \param[in] left left most source pixel to copy. Must be a multiple of 2 pixels. + * \param[in] top top most source line to copy. Must be a multiple of 2 lines. + * \param[in] wd width in pixels to copy. Must be a multiple of 2 pixels. + * \param[in] ht height in lines to copy. Must be a multiple of 2 lines. + * + * \return none + */ +void GX_SetDispCopySrc(u16 left,u16 top,u16 wd,u16 ht); + +/*! + * \fn void GX_SetDispCopyDst(u16 wd,u16 ht) + * \brief Sets the witdth and height of the display buffer in pixels. + * + * \details The application typical renders an image into the EFB(source) and then copies it into the XFB(destination) in main memory. \a wd + * specifies the number of pixels between adjacent lines in the destination buffer and can be different than the width of the EFB. + * + * \param[in] wd Distance between successive lines in the XFB, in pixels. Must be a multiple of 16. + * \param[in] ht Height of the XFB in lines. + * + * \return none + */ +void GX_SetDispCopyDst(u16 wd,u16 ht); + +/*! + * \fn void GX_SetCopyClamp(u8 clamp) + * \brief Sets the vertical clamping mode to use during the EFB to XFB or texture copy. + * + * \param[in] clamp bit-wise OR of desired \ref xfbclamp. Use GX_CLAMP_NONE for no clamping. + * + * \return none + */ +void GX_SetCopyClamp(u8 clamp); + +/*! + * \fn void GX_SetDispCopyGamma(u8 gamma) + * \brief Sets the gamma correction applied to pixels during EFB to XFB copy operation. + * + * \param[in] gamma \ref gammamode + * + * \return none + */ +void GX_SetDispCopyGamma(u8 gamma); + +/*! + * \fn void GX_SetCopyFilter(u8 aa,u8 sample_pattern[12][2],u8 vf,u8 vfilter[7]) + * \brief Sets the subpixel sample patterns and vertical filter coefficients used to filter subpixels into pixels. + * + * \details This function normally uses the \a aa, \a sample_pattern and \a vfilter provided by the render mode struct:

+ * + * \code GXRModeObj* rmode = VIDEO_GetPreferredMode(NULL); + * GX_SetCopyFilter(rmode->aa,rmode->sample_pattern,GX_TRUE,rmode->vfilter); \endcode + * + * \note In order to make use of the \a sample_pattern, antialiasing must be enabled by setting the Embedded Frame Buffer (EFB) format to + * GX_PF_RGB565_Z16; see GX_SetPixelFmt(). + * + * \param[in] aa utilizes \a sample_pattern if GX_TRUE, otherwise all sample points are centered + * \param[in] sample_pattern array of coordinates for sample points; valid range is 1 - 11 inclusive + * \param[in] vf use \a vfilter if GX_TRUE, otherwise use default 1-line filter + * \param[in] vfilter vertical filter coefficients; valid coefficient range is 0 - 63 inclusive; sum should equal 64 + * + * \return none + */ +void GX_SetCopyFilter(u8 aa,u8 sample_pattern[12][2],u8 vf,u8 vfilter[7]); + +/*! + * \fn void GX_SetDispCopyFrame2Field(u8 mode) + * \brief Determines which lines are read from the Embedded Frame Buffer (EFB) when using GX_CopyDisp(). + * + * \details Specifically, it determines whether all lines, only even lines, or only odd lines are read. + * + * \note The opposite function, which determines whether all lines, only even lines or only odd lines are written to the EFB, is GX_SetFieldMask().

+ * + * \note Only applies to display copies, GX_CopyTex() always uses the GX_COPY_PROGRESSIVE mode. + * + * \param[in] mode \ref copymode to determine which field to copy (or both) + * + * \return none + */ +void GX_SetDispCopyFrame2Field(u8 mode); + +/*! + * \fn void GX_SetCopyClear(GXColor color,u32 zvalue) + * \brief Sets color and Z value to clear the EFB to during copy operations. + * + * \details These values are used during both display copies and texture copies. + * + * \param[in] color RGBA color (8-bit/component) to use during clear operation. + * \param[in] zvalue 24-bit Z value to use during clear operation. Use the constant GX_MAX_Z24 to specify the maximum depth value. + * + * \return none + */ +void GX_SetCopyClear(GXColor color,u32 zvalue); + +/*! + * \fn void GX_CopyDisp(void *dest,u8 clear) + * \brief Copies the embedded framebuffer (EFB) to the external framebuffer(XFB) in main memory. + * + * \note The stride of the XFB is set using GX_SetDispCopyDst(). The source image in the EFB is described using GX_SetDispCopySrc().

+ * + * \note The graphics processor will stall all graphics commands util the copy is complete.

+ * + * \note If the \a clear flag is true, the color and Z buffers will be cleared during the copy. They will be cleared to the constant + * values set using GX_SetCopyClear(). + * + * \param[in] dest pointer to the external framebuffer. \a dest should be 32B aligned. + * \param[in] clear flag that indicates framebuffer should be cleared if GX_TRUE. + * + * \return none + */ +void GX_CopyDisp(void *dest,u8 clear); + +/*! + * \fn void GX_SetTexCopySrc(u16 left,u16 top,u16 wd,u16 ht) + * \brief Sets the source parameters for the Embedded Frame Buffer (EFB) to texture image copy. + * + * \details The GP will copy textures into the tiled texture format specified in GX_CopyTex(). The GP always copies tiles (32B) so image widths and + * heights that are not a multiple of the tile width will be padded with undefined data in the copied image + * + * \param[in] left left-most source pixel to copy, multiple of two + * \param[in] top top-most source line to copy, multiple of two + * \param[in] wd width to copy in pixels, multiple of two + * \param[in] ht height to copy in pixels, multiple of two + * + * \return none + */ +void GX_SetTexCopySrc(u16 left,u16 top,u16 wd,u16 ht); + +/*! + * \fn void GX_SetTexCopyDst(u16 wd,u16 ht,u32 fmt,u8 mipmap) + * \brief This function sets the width and height of the destination texture buffer in texels. + * + * \details This function sets the width (\a wd) and height (\a ht) of the destination texture buffer in texels. The application may render an image into + * the EFB and then copy it into a texture buffer in main memory. \a wd specifies the number of texels between adjacent lines in the texture buffer and can + * be different than the width of the source image. This function also sets the texture format (\a fmt) to be created during the copy operation. An + * optional box filter can be enabled using \a mipmap. This flag will scale the source image by 1/2. + * + * Normally, the width of the EFB and destination \a wd are the same. When rendering smaller images that get copied and composited into a larger texture + * buffer, however, the EFB width and texture buffer \a wd are not necessarily the same. + * + * The Z buffer can be copied to a Z texture format by setting \a fmt to GX_TF_Z24X8. This operation is only valid when the EFB format is + * GX_PF_RGB8_Z24 or GX_PF_RGBA6_Z24. + * + * The alpha channel can be copied from an EFB with format GX_PF_RGBA6_Z24 by setting \a fmt to GX_TF_A8. + * + * \param[in] wd distance between successive lines in the texture buffer, in texels; must be a multiple of the texture tile width, which depends on \a fmt. + * \param[in] ht height of the texture buffer + * \param[in] fmt \ref texfmt + * \param[in] mipmap flag that indicates framebuffer should be cleared if GX_TRUE. + * + * \return none + */ +void GX_SetTexCopyDst(u16 wd,u16 ht,u32 fmt,u8 mipmap); + +/*! + * \fn void GX_CopyTex(void *dest,u8 clear) + * \brief Copies the embedded framebuffer (EFB) to the texture image buffer \a dest in main memory. + * + * \details This is useful when creating textures using the Graphics Processor (GP). If the \a clear flag is set to GX_TRUE, the EFB will be cleared + * to the current color(see GX_SetCopyClear()) during the copy operation. + * + * \param[in] dest pointer to the image buffer in main memory. \a dest should be 32B aligned. + * \param[in] clear flag that indicates framebuffer should be cleared if GX_TRUE. + * + * \return none + */ +void GX_CopyTex(void *dest,u8 clear); + +/*! + * \fn void GX_PixModeSync() + * \brief Causes the GPU to wait for the pipe to flush. + * + * \details This function inserts a synchronization command into the graphics FIFO. When the GPU sees this command it will allow the rest of the pipe to + * flush before continuing. This command is useful in certain situation such as after using GX_CopyTex() and before a primitive that uses the copied texture. + * + * \note The command is actually implemented by writing the control register that determines the format of the embedded frame buffer (EFB). As a result, care + * should be used if this command is placed within a display list. + * + * \return none + */ +void GX_PixModeSync(); + +/*! + * \fn void GX_ClearBoundingBox() + * \brief Clears the bounding box values before a new image is drawn. + * + * \details The graphics hardware keeps track of a bounding box of pixel coordinates that are drawn in the Embedded Frame Buffer (EFB). + * + * \return none + */ +void GX_ClearBoundingBox(); + +/*! + * \fn GX_PokeAlphaMode(u8 func,u8 threshold) + * \brief Sets a threshold which is compared to the alpha of pixels written to the Embedded Frame Buffer (EFB) using the GX_Poke*() functions. + * + * \details The compare function order is:

+ * + *       src_alpha \a func \a threshold + * + * \note The alpha compare function can be used to conditionally write pixels to the EFB using the source alpha channel as a template. If the compare function is + * true, the source color will be written to the EFB based on the result of the Z compare (see GX_PokeZMode()). If the alpha compare function is false, the source + * color is not written to the EFB.

+ * + * \note The alpha compare test happens before the Z compare and before blending (see GX_PokeBlendMode()). + * + * \param[in] func \ref compare to use + * \param[in] threshold to which the source alpha will be compared to + * + * \return none + */ +void GX_PokeAlphaMode(u8 func,u8 threshold); + +/*! + * \fn void GX_PokeAlphaUpdate(u8 update_enable) + * \brief Enables or disables alpha-buffer updates for GX_Poke*() functions. + * + * \details The normal rendering state (set by GX_SetAlphaUpdate()) is not affected. + * + * \param[in] update_enable enables alpha-buffer updates with GX_TRUE, otherwise does not + * + * \return none + */ +void GX_PokeAlphaUpdate(u8 update_enable); + +/*! + * \fn void GX_PokeColorUpdate(u8 update_enable) + * \brief Enables or disables color-buffer updates when writing the Embedded Frame Buffer (EFB) using the GX_Poke*() functions. + * + * \param[in] update_enable enables color-buffer updates with GX_TRUE, otherwise does not + * + * \return none + */ +void GX_PokeColorUpdate(u8 update_enable); + +/*! + * \fn void GX_PokeDither(u8 dither) + * \brief Enables dithering when writing the Embedded Frame Buffer (EFB) using GX_Poke*() functions. + * + * \note The \a dither enable is only valid when the pixel format (see GX_SetPixelFmt()) is either GX_PF_RGBA6_Z24 or GX_PF_RGB565_Z16.

+ * + * \note A 4x4 Bayer matrix is used for dithering. + * + * \param[in] dither if set to GX_TRUE and pixel format is one of the above, dithering is enabled; otherwise disabled + * + * \return none + */ +void GX_PokeDither(u8 dither); + +/*! + * \fn void GX_PokeBlendMode(u8 type,u8 src_fact,u8 dst_fact,u8 op) + * \brief Determines how the source image, is blended with the current Embedded Frame Buffer (EFB). + * + * \details When type is set to GX_BM_NONE, no color data is written to the EFB. When type is set to GX_BM_BLEND, the source and EFB pixels + * are blended using the following equation:

+ * + *       dst_pix_clr = src_pix_clr * \a src_fact + dst_pix_clr * \a dst_fact

+ * + * When type is set to GX_BM_SUBTRACT, the destination pixel is computed as follows:

+ * + *       dst_pix_clr = dst_pix_clr - src_pix_clr [clamped to zero]

+ * + * Note that \a src_fact and \a dst_fact are not part of the equation. + * + * \note \a dst_fact can be used only when the frame buffer has GX_PF_RGBA6_Z24 as the pixel format (see GX_SetPixelFmt()).

+ * + * \note When type is set to GX_BM_LOGIC, the source and EFB pixels are blended using logical bitwise operations.

+ * + * \note This function does not effect the normal rendering state; see GX_SetBlendMode(). + * + * \param[in] type \ref blendmode + * \param[in] src_fact source \ref blendfactor; the pixel color produced by the graphics processor is multiplied by this factor + * \param[in] dst_fact destination \ref blendfactor; the current frame buffer pixel color is multiplied by this factor + * \param[in] op \ref logicop to use + */ +void GX_PokeBlendMode(u8 type,u8 src_fact,u8 dst_fact,u8 op); + +/*! + * \fn void GX_PokeAlphaRead(u8 mode) + * \brief Determines what value of alpha will be read from the Embedded Frame Buffer (EFB). + * + * \details The mode only applies to GX_Peek*() functions. + * + * \note This feature works no matter what pixel type (see GX_SetPixelFmt()) you are using. If you are using the EFB with alpha plane, it is + * recommended that you use GX_READ_NONE so that you can read correct alpha value from the EFB. If you are using the EFB with no alpha, you should + * set either of GX_READ_00 or GX_READ_FF in order to get a certain value.

+ * + * \param[in] mode \ref alphareadmode that determines value of alpha read from a frame buffer with no alpha channel. + * + * \return none + */ +void GX_PokeAlphaRead(u8 mode); + +/*! + * \fn void GX_PokeDstAlpha(u8 enable,u8 a) + * \brief Sets a constant alpha value for writing to the Embedded Frame Buffer (EFB). + * + * \details The EFB pixel type must have an alpha channel for this function to be effective (see GX_SetPixelFmt()). The blending operations (see + * GX_PokeBlendMode()) still use source alpha but when writing the pixel color, the constant \a a will replace the pixel alpha in the EFB. + * + * \param[in] enable if set to GX_ENABLE and pixel format supports dest alpha, \a a will be written to the framebuffer + * \param[in] a constant alpha value + * + * \return none + */ +void GX_PokeDstAlpha(u8 enable,u8 a); + +/*! + * \fn void GX_PokeARGB(u16 x,u16 y,GXColor color) + * \brief Allows the CPU to write \a color directly to the Embedded Frame Buffer (EFB) at position \a x,\a y. + * + * \details The alpha value in \a color can be compared with the current alpha threshold (see GX_PokeAlphaMode()). The color will be blended + * into the EFB using the blend mode set by GX_PokeBlendMode(). + * + * \note For an antialiased frame buffer, all 3 subsamples of a pixel are affected by the poke. + * + * \param[in] x coordinate, in pixels; must be 0 - 639 inclusive + * \param[in] y coordinate, in lines; must be 0 - 527 inclusive + * \param[in] color color to write at the location + * + * \return none + */ +void GX_PokeARGB(u16 x,u16 y,GXColor color); + +/*! + * \fn void GX_PeekARGB(u16 x,u16 y,GXColor *color) + * \brief Allows the CPU to read a color value directly from the Embedded Frame Buffer (EFB) at position \a x,\a y. + * + * \note For an antialiased frame buffer, only subsample 0 of a pixel is read. + * + * \param[in] x coordinate, in pixels; must be 0 - 639 inclusive + * \param[in] y coordinate, in lines; must be 0 - 527 inclusive + * \param[out] color struct to store color in + * + * \return none + */ +void GX_PeekARGB(u16 x,u16 y,GXColor *color); + +/*! + * \fn void GX_PokeZ(u16 x,u16 y,u32 z) + * \brief Allows the CPU to write a z value directly to the Embedded Frame Buffer (EFB) at position \a x,\a y. + * + * \details The \a z value can be compared with the current contents of the EFB. The Z compare fuction is set using GX_PokeZMode(). + * + * \note The \a z value should be in the range of 0x00000000 <= \a z < 0x00FFFFFF in the case of non-antialiased frame buffer. For an antialiased + * frame buffer, the \a z value should be in the compressed 16-bit format (0x00000000 <= \a z <= 0x0000FFFF), and the poke will affect all 3 + * subsamples of a pixel. + * + * \param[in] x coordinate, in pixels; must be 0 - 639 inclusive + * \param[in] y coordinate, in lines; must be 0 - 527 inclusive + * \param[in] z value to write at position \a x,\a y in the EFB + * + * \return none + */ +void GX_PokeZ(u16 x,u16 y,u32 z); + +/*! + * \fn void GX_PeekZ(u16 x,u16 y,u32 *z) + * \brief Allows the CPU to read a z value directly from the Embedded Frame Buffer (EFB) at position x,y. + * + * \details The z value is raw integer value from the Z buffer. + * + * \note The value range is 24-bit when reading from non-antialiased frame buffer. When reading from an antialiased frame buffer, subsample + * 0 is read and returned. The value will be compressed 16-bit form in this case. + * + * \param[in] x coordinate, in pixels; must be 0 - 639 inclusive + * \param[in] y coordinate, in lines; must be 0 - 527 inclusive + * \param[out] z pointer to a returned Z value + * + * \return none + */ +void GX_PeekZ(u16 x,u16 y,u32 *z); + +/*! + * \fn void GX_PokeZMode(u8 comp_enable,u8 func,u8 update_enable) + * \brief Sets the Z-buffer compare mode when writing the Embedded Frame Buffer (EFB). + * + * \details The result of the Z compare is used to conditionally write color values to the EFB. The Z value will be updated according to the + * result of the compare if Z update is enabled. + * + * When \a comp_enable is set to GX_DISABLE, poke Z buffering is disabled and the Z buffer is not updated. The \a func parameter determines the + * comparison that is performed. In the comparison function, the poked Z value is on the left while the Z value from the Z buffer is on the + * right. If the result of the comparison is false, the poked Z value is discarded. The parameter \a update_enable determines whether or not the + * Z buffer is updated with the new Z value after a comparison is performed. + * + * \note The normal rendering Z mode (set by GX_SetZMode()) is not affected by this function.

+ * + * \note Even if update_enable is GX_FALSE, compares may still be enabled. + * + * \param[in] comp_enable enables comparisons with source and destination Z values if GX_TRUE + * \param[in] func \ref compare function to use + * \param[in] update_enable enables Z-buffer updates when GX_TRUE + * + * \return none + */ +void GX_PokeZMode(u8 comp_enable,u8 func,u8 update_enable); + +/*! + * \fn u32 GX_GetTexObjFmt(GXTexObj *obj) + * \brief Returns the texture format described by texture object \a obj. + * + * \note Use GX_InitTexObj() or GX_InitTexObjCI() to initialize the texture format. + * + * \param[in] obj ptr to a texture object + * + * \return texture format of the given texture object + */ +u32 GX_GetTexObjFmt(GXTexObj *obj); + +/*! + * \fn u32 GX_GetTexObjMipMap(GXTexObj *obj) + * \brief Returns the texture mipmap enable described by texture object \a obj. + * + * \note Use GX_InitTexObj() or GX_InitTexObjCI() to initialize the texture mipmap enable. + * + * \param[in] obj ptr to a texture object + * + * \return mipmap enable flag + */ +u32 GX_GetTexObjMipMap(GXTexObj *obj); + +/*! + * \fn void* GX_GetTexObjUserData(GXTexObj *obj) + * \brief Used to get a pointer to user data from the \ref GXTexObj structure. + * + * \details You can use this function to retrieve private data structures from the texture object. This pointer is set using GX_InitTexObjUserData(). + * + * \param[in] obj ptr to object to read data from + * + * \return Pointer to user data. + */ +void* GX_GetTexObjUserData(GXTexObj *obj); + +/*! + * \fn void* GX_GetTexObjData(GXTexObj *obj) + * \brief Used to get a pointer to texture data from the \ref GXTexObj structure. + * + * \note The returned pointer is a physical address. + * + * \param[in] obj ptr to a texture object + * + * \return Physical pointer to texture data. + */ +void* GX_GetTexObjData(GXTexObj *obj); + +/*! + * \fn u8 GX_GetTexObjWrapS(GXTexObj* obj) + * \brief Returns the texture wrap s mode described by texture object \a obj. + * + * \note Use GX_InitTexObj() or GX_InitTexObjCI() to initialize the texture wrap s mode. + * + * \param[in] obj ptr to a texture object + * + * \return wrap s mode + */ +u8 GX_GetTexObjWrapS(GXTexObj* obj); + +/*! + * \fn u8 GX_GetTexObjWrapT(GXTexObj* obj) + * \brief Returns the texture wrap t mode described by texture object \a obj. + * + * \note Use GX_InitTexObj() or GX_InitTexObjCI() to initialize the texture wrap t mode. + * + * \param[in] obj ptr to a texture object + * + * \return wrap t mode + */ +u8 GX_GetTexObjWrapT(GXTexObj* obj); + +/*! + * \fn u16 GX_GetTexObjHeight(GXTexObj* obj) + * \brief Returns the texture height described by texture object \a obj. + * + * \note Use GX_InitTexObj() or GX_InitTexObjCI() to initialize the texture height. + * + * \param[in] obj ptr to a texture object + * + * \return texture height + */ +u16 GX_GetTexObjHeight(GXTexObj* obj); + +/*! + * \fn u16 GX_GetTexObjWidth(GXTexObj* obj) + * \brief Returns the texture width described by texture object \a obj. + * + * \note Use GX_InitTexObj() or GX_InitTexObjCI() to initialize the texture width. + * + * \param[in] obj ptr to a texture object + * + * \return texture width + */ +u16 GX_GetTexObjWidth(GXTexObj* obj); + +/*! + * \fn void GX_GetTexObjAll(GXTexObj* obj, void** image_ptr, u16* width, u16* height, u8* format, u8* wrap_s, u8* wrap_t, u8* mipmap); + * \brief Returns the parameters described by a texture object. Texture objects are used to describe all the parameters associated with a texture, including size, format, wrap modes, filter modes, etc. Texture objects are initialized using either GX_InitTexObj() or, for color index format textures, GX_InitTexObjCI(). + * + * \param[in] obj ptr to a texture object + * \param[out] image_ptr Returns a physical pointer to the image data for a texture. + * \param[out] width Returns the width of the texture or LOD 0 for mipmaps + * \param[out] height Returns the height of the texture or LOD 0 for mipmaps + * \param[out] format Returns the texel format + * \param[out] mipmap Returns the mipmap enable flag. + * + * \return none + */ +void GX_GetTexObjAll(GXTexObj* obj, void** image_ptr, u16* width, u16* height, u8* format, u8* wrap_s, u8* wrap_t, u8* mipmap); + +/*! + * \fn u32 GX_GetTexBufferSize(u16 wd,u16 ht,u32 fmt,u8 mipmap,u8 maxlod) + * \brief Returns the amount of memory in bytes needed to store a texture of the given size and \a fmt. + * + * \details If the \a mipmap flag is GX_TRUE, then the size of buffer needed for the mipmap pyramid up to \a maxlod will be returned. + * \a maxlod will be clamped to the number of LODs possible given the map \a wd and \a ht. For mipmaps, \a wd and \a ht must be a power of two. + * + * \note This function takes into account the tiling and padding requirements of the GameCube's native texture format. The resulting size can be used + * along with memalign() to allocate texture buffers (see GX_CopyTex()). + * + * \param[in] wd width of the texture in texels + * \param[in] ht height of the texture in texels + * \param[in] fmt format of the texture; use GX_TexFmt() or GX_CITexFmt() to get it + * \param[in] mipmap flag indicating whether or not the texture is a mipmap + * \param[in] maxlod if \a mipmap is \a GX_TRUE, texture size will include mipmap pyramid up to this value + * + * \return number of bytes needed for the texture, including tile padding + */ +u32 GX_GetTexBufferSize(u16 wd,u16 ht,u32 fmt,u8 mipmap,u8 maxlod); + +/*! + * \fn void GX_InvalidateTexAll() + * \brief Invalidates the current caches of the Texture Memory (TMEM). + * + * \details It takes about 512 GP clocks to invalidate all the texture caches. + * + * \note Preloaded textures (see GX_PreloadEntireTexture()) are not affected. + * + * \return none + */ +void GX_InvalidateTexAll(); + +/*! + * \fn void GX_InvalidateTexRegion(GXTexRegion *region) + * \brief Invalidates the texture cache in Texture Memory (TMEM) described by \a region. + * + * \details This function should be called when the CPU is used to modify a texture in main memory, or a new texture is loaded into main memory that + * is possibly cached in the texture region. + * + * \note In reality, this function invalidates the cache tags, forcing the texture cache to load new data. Preloaded textures (see + * GX_PreloadEntireTexture()) do not use the tags.

+ * + * \note The texture hardware can invalidate 4 tags each GP clock. Each tag represents a superline or 512B of TMEM. Therefore, it takes 16 + * GP clocks to invalidate a 32KB texture region. + * + * \param[in] region ptr to GXTexRegion object + * + * \return none + */ +void GX_InvalidateTexRegion(GXTexRegion *region); + +/*! + * \fn void GX_InitTexCacheRegion(GXTexRegion *region,u8 is32bmipmap,u32 tmem_even,u8 size_even,u32 tmem_odd,u8 size_odd) + * \brief Initializes a texture memory (TMEM) region object for cache. + * + * \details The region is allocated by the application and can be used as a cache. An application can create many region objects and some of them can + * overlap; however, no two overlapping regions can be active at the same time. + * + * The possible sizes of a TMEM cache region are 32K, 128K or 512K. + * + * \note For pre-loaded textures, the region must be defined by using GX_InitTexPreloadRegion().

+ * + * \note GX_Init() creates default texture regions, so it is not necessary for the application to use this function unless a different Texture Memory + * configuration is desired. In that case, the application should also define a region allocator using GX_SetTexRegionCallback().

+ * + * \note The function GX_InvalidateTexRegion() can be used to force the texture in main memory associated with this region to be reloaded. This will be + * necessary whenever the texture data in main memory changes. You may invalidate all cached regions at once using GX_InvalidateTexAll(). + * + * \param[in] region ptr to a GXTexRegion struct + * \param[in] is32bmipmap should be set to GX_TRUE to interpret parameters according to the 32b mipmap meaning. + * \param[in] tmem_even base ptr in TMEM for even LODs; must be multiple of 2KB + * \param[in] size_even even \ref texcachesize other than GX_TEXCACHE_NONE + * \param[in] tmem_odd base ptr in TMEM for odd LODs; must be multiple of 2KB + * \param[in] size_odd odd \ref texcachesize other than GX_TEXCACHE_NONE + * + * \return none + */ +void GX_InitTexCacheRegion(GXTexRegion *region,u8 is32bmipmap,u32 tmem_even,u8 size_even,u32 tmem_odd,u8 size_odd); + +/*! + * \fn void GX_InitTexPreloadRegion(GXTexRegion *region,u32 tmem_even,u32 size_even,u32 tmem_odd,u32 size_odd) + * \brief Initializes a Texture Memory (TMEM) region object for preloading. + * + * \details The region is allocated in TMEM by the application and can be used only as a pre-loaded buffer. Cache regions must be allocated + * by using GX_InitTexCacheRegion(). For pre-loaded textures, the size of the region must match the size of the texture. An application can + * create many region objects and some of them can overlap; however, no two overlapping regions can be active at the same time. + * + * \note The maximum size of a region is 512K. + * + * \warning GX_Init() creates no region for preloading, so the application should allocate appropriate regions if preloading is necessary. It + * is also required to create cache regions and its allocator by using GX_InitTexCacheRegion() and GX_SetTexRegionCallback(), otherwise new + * cache regions may overwrite the preloaded areas. (Alternatively, if you do not use any color-index textures, you may preload textures into + * the portion of texture memory normally allocated to color-index usage by the default allocator.) + * + * \param[in] region ptr to a GXTexRegion struct + * \param[in] tmem_even base ptr in TMEM for even LODs; must be 32B aligned + * \param[in] size_even size of the even cache, in bytes; should be multiple of 32B + * \param[in] tmem_odd base ptr in TMEM for odd LODs; must be 32B aligned + * \param[in] size_odd size of the odd cache, in bytes; should be multiple of 32B + * + * \return none + */ +void GX_InitTexPreloadRegion(GXTexRegion *region,u32 tmem_even,u32 size_even,u32 tmem_odd,u32 size_odd); + +/*! + * \fn void GX_InitTexObj(GXTexObj *obj,void *img_ptr,u16 wd,u16 ht,u8 fmt,u8 wrap_s,u8 wrap_t,u8 mipmap) + * \brief Used to initialize or change a texture object for non-color index textures. + * + * \details Texture objects are used to describe all the parameters associated with a texture, including size, format, wrap modes, filter modes, + * etc. It is the application's responsibility to provide memory for a texture object. Once initialized, a texture object can be associated with + * one of eight active texture IDs using GX_LoadTexObj(). + * + * \note To initialize a texture object for color index format textures, use GX_InitTexObjCI().

+ * + * \note If the mipmap flag is GX_TRUE, then the texture is a mipmap and the texture will be trilerped. If the mipmap flag is GX_FALSE, the texture + * is not a mipmap and the texture will be bilerped. To override the filter modes and other mipmap controls, see GX_InitTexObjLOD(). + * + * \param[out] obj ptr to a texture object + * \param[in] img_ptr ptr to the image data for a texture, aligned to 32B + * \param[in] wd width of the texture, or LOD level 0 for mipmaps; max value is 1024; mipmaps must be a power of two + * \param[in] ht height of the texture, or LOD level 0 for mipmaps; max value is 1024; mipmaps must be a power of two + * \param[in] fmt \ref texfmt + * \param[in] wrap_s texture coordinate wrapping strategy in the S direction; use GX_CLAMP, GX_REPEAT or GX_MIRROR + * \param[in] wrap_t texture coordinate wrapping strategy in the T direction; use GX_CLAMP, GX_REPEAT or GX_MIRROR + * \param[in] mipmap trilinear filtering will be used if GX_TRUE, otherwise bilinear is used + * + * \return none + */ +void GX_InitTexObj(GXTexObj *obj,void *img_ptr,u16 wd,u16 ht,u8 fmt,u8 wrap_s,u8 wrap_t,u8 mipmap); + +/*! + * \fn void GX_InitTexObjCI(GXTexObj *obj,void *img_ptr,u16 wd,u16 ht,u8 fmt,u8 wrap_s,u8 wrap_t,u8 mipmap,u32 tlut_name) + * \brief Used to initialize or change a texture object when the texture is color index format. + * + * \details Texture objects are used to describe all the parameters associated with a texture, including size, format, wrap modes, filter modes, + * etc. It is the application's responsibility to provide memory for a texture object. Once initialized, a texture object can be associated with + * one of eight active texture IDs using GX_LoadTexObj(). + * + * \note If the \a mipmap flag is GX_TRUE, then the texture is a mipmap and the texture will be filtered using the GX_LIN_MIP_NEAR filter mode + * (color index mipmaps cannot use the GX_LIN_MIP_LIN or GX_NEAR_MIP_LIN mode). If the \a mipmap flag is GX_FALSE, the texture is not a mipmap + * and the texture will be bilerped. To override the filter modes and other mipmap controls, use GX_InitTexObjLOD(). Mipmap textures should + * set the width and height to a power of two, but mipmaps do not need to be square.

+ * + * \note Non-mipmap (planar) textures do not have to be a power of two. However, to use the GX_REPEAT or GX_MIRROR modes for \a wrap_s and \a wrap_t + * the width and height, respectively, must be a power of two.

+ * + * \note The \a tlut_name is used to indicate which texture lookup table (TLUT) to use for the index to color conversion. To load the TLUT into + * texture memory, use GX_LoadTlut(). + * + * \param[in] obj ptr to a texture object + * \param[in] img_ptr ptr to the image data for a texture, aligned to 32B + * \param[in] wd width of the texture, or LOD level 0 for mipmaps; max value is 1024; mipmaps must be a power of two + * \param[in] ht height of the texture, or LOD level 0 for mipmaps; max value is 1024; mipmaps must be a power of two + * \param[in] fmt \ref texfmt + * \param[in] wrap_s texture coordinate wrapping strategy in the S direction; use GX_CLAMP, GX_REPEAT or GX_MIRROR + * \param[in] wrap_t texture coordinate wrapping strategy in the T direction; use GX_CLAMP, GX_REPEAT or GX_MIRROR + * \param[in] mipmap if GX_TRUE, it is a mipmap texture, else it is a planar texture + * \param[in] tlut_name TLUT name to use for this texture; default texture configuration recognizes \ref tlutname + * + * \return none + */ +void GX_InitTexObjCI(GXTexObj *obj,void *img_ptr,u16 wd,u16 ht,u8 fmt,u8 wrap_s,u8 wrap_t,u8 mipmap,u32 tlut_name); + +/*! + * \fn void GX_InitTexObjTlut(GXTexObj *obj,u32 tlut_name) + * \brief Allows one to modify the TLUT that is associated with an existing texture object. + * + * \param[in] obj ptr to a texture object + * \param[in] tlut_name TLUT name to use for this texture; default texture configuration recognizes \ref tlutname + * + * \return none + */ +void GX_InitTexObjTlut(GXTexObj *obj,u32 tlut_name); + +/*! + * \fn void GX_InitTexObjData(GXTexObj *obj,void *img_ptr) + * \brief Allows one to modify the image data pointer for an existing texture object. + * + * \note The image format and size for the new data must agree with what they were when the texture object was first initialized using + * GX_InitTexObj() or GX_InitTexObjCI(). + * + * \param[in] obj ptr to a texture object + * \param[in] img_ptr ptr to the texture data in main memory + * + * \return none + */ +void GX_InitTexObjData(GXTexObj *obj,void *img_ptr); + +/*! + * \fn void GX_InitTexObjWrapMode(GXTexObj *obj,u8 wrap_s,u8 wrap_t) + * \brief Allows one to modify the texture coordinate wrap modes for an existing texture object. + * + * \param[in] obj ptr to a texture object + * \param[in] wrap_s texture coordinate wrapping strategy in the S direction; use GX_CLAMP, GX_REPEAT or GX_MIRROR + * \param[in] wrap_t texture coordinate wrapping strategy in the T direction; use GX_CLAMP, GX_REPEAT or GX_MIRROR + * + * \return none + */ +void GX_InitTexObjWrapMode(GXTexObj *obj,u8 wrap_s,u8 wrap_t); + +/*! + * \fn void GX_InitTexObjFilterMode(GXTexObj *obj,u8 minfilt,u8 magfilt) + * \brief Sets the filter mode for a texture. + * + * \details When the ratio of texels for this texture to pixels is not 1:1, the filter type for \a minfilt or \a magfilt is used. + * + * \param[in] obj texture object to set the filters for + * \param[in] minfilt filter mode to use when the texel/pixel ratio is >= 1.0; needs to be one of \ref texfilter. + * \param[in] magfilt filter mode to use when the texel/pixel ratio is < 1.0; needs to be \a GX_NEAR or \a GX_LINEAR + */ +void GX_InitTexObjFilterMode(GXTexObj *obj,u8 minfilt,u8 magfilt); + +/*! + * \fn void GX_InitTexObjMinLOD(GXTexObj *obj,f32 minlod) + * \brief Sets the minimum LOD for a given texture. + * + * \param[in] obj texture to set the minimum LOD for + * \param[in] minlod minimum LOD value; the hardware will use MAX(min_lod, lod); range is 0.0 to 10.0. + */ +void GX_InitTexObjMinLOD(GXTexObj *obj,f32 minlod); + +/*! + * void GX_InitTexObjMaxLOD(GXTexObj *obj,f32 maxlod) + * \brief Sets the maximum LOD for a given texture. + * + * \param[in] obj texture to set the maximum LOD for + * \param[in] maxlod maximum LOD value; the hardware will use MIN(max_lod, lod); range is 0.0 to 10.0. + */ +void GX_InitTexObjMaxLOD(GXTexObj *obj,f32 maxlod); + +/*! + * \fn void GX_InitTexObjLODBias(GXTexObj *obj,f32 lodbias) + * \brief Sets the LOD bias for a given texture. + * + * \details The LOD computed by the graphics hardware can be biased using this function. The \a lodbias is added to the computed lod and the + * result is clamped between the values given to GX_InitTexObjMinLOD() and GX_InitTexObjMaxLOD(). If \a GX_ENABLE is given to + * GX_InitTexObjBiasClamp(), the effect of \a lodbias will diminish as the polygon becomes more perpendicular to the view direction. + * + * \param[in] obj texture to set the LOD bias for + * \param[in] lodbias bias to add to computed LOD value + */ +void GX_InitTexObjLODBias(GXTexObj *obj,f32 lodbias); + +/*! + * \fn void GX_InitTexObjBiasClamp(GXTexObj *obj,u8 biasclamp) + * \brief Enables bias clamping for texture LOD. + * + * \details If \a biasclamp is \a GX_ENABLE, the sum of LOD and \a lodbias (given in GX_InitTexObjLODBias()) is clamped so that it is never + * less than the minimum extent of the pixel projected in texture space. This prevents over-biasing the LOD when the polygon is perpendicular + * to the view direction. + * + * \param[in] obj texture to set the bias clamp value for + * \param[in] biasclamp whether or not to enable the bias clamp + */ +void GX_InitTexObjBiasClamp(GXTexObj *obj,u8 biasclamp); + +/*! + * \fn void GX_InitTexObjEdgeLOD(GXTexObj *obj,u8 edgelod) + * \brief Changes LOD computing mode. + * + * \details When set to \a GX_ENABLE, the LOD is computed using adjacent texels; when \a GX_DISABLE, diagonal texels are used instead. This + * should be set to \a GX_ENABLE if you use bias clamping (see GX_InitTexObjBiasClamp()) or anisotropic filtering (GX_ANISO_2 or GX_ANISO_4 + * for GX_InitTexObjMaxAniso() argument). + * + * \param[in] obj texture to set the edge LOD for + * \param[in] edgelod mode to set LOD computation to + */ +void GX_InitTexObjEdgeLOD(GXTexObj *obj,u8 edgelod); + +/*! + * \fn void GX_InitTexObjMaxAniso(GXTexObj *obj,u8 maxaniso) + * \brief Sets the maximum anisotropic filter to use for a texture. + * + * \details Anisotropic filtering is accomplished by iterating the square filter along the direction of anisotropy to better approximate the + * quadralateral. This type of filtering results in sharper textures at the expense of multiple cycles per quad. The hardware will only use + * multiple cycles when necessary, and the maximum number of cycles is clamped by the \a maxaniso parameter, so setting \a maxaniso to + * \a GX_ANISO_2 will use at most 2 filter cycles per texture. + * + * \note These filter cycles are internal to the texture filter hardware and do not affect the available number of TEV stages. When setting + * \a maxaniso to \a GX_ANISO_2 or \a GX_ANISO_4, the \a minfilt parameter given to GX_InitTexObjFilterMode() should be set to + * \a GX_LIN_MIP_LIN. + * + * \param[in] obj texture to set the max anisotropy value to + * \param[in] maxaniso the maximum anistropic filter to use; must be one of \ref anisotropy + */ +void GX_InitTexObjMaxAniso(GXTexObj *obj,u8 maxaniso); + +/*! + * \fn GX_InitTexObjUserData(GXTexObj *obj,void *userdata) + * \brief Used to set a pointer to user data in the \ref GXTexObj structure. + * + * \details You can use this function to attach private data structures to the texture object. This pointer can be retrieved using GX_GetTexObjUserData(). + * + * \param[in] obj ptr to a texture object + * \param[in] userdata pointer to your data to attach to this texture + */ +void GX_InitTexObjUserData(GXTexObj *obj,void *userdata); + +/*! + * \fn void GX_LoadTexObj(GXTexObj *obj,u8 mapid) + * \brief Loads the state describing a texture into one of eight hardware register sets. + * + * \details Before this happens, the texture object \a obj should be initialized using GX_InitTexObj() or GX_InitTexObjCI(). The \a id parameter refers to + * the texture state register set. Once loaded, the texture can be used in any Texture Environment (TEV) stage using GX_SetTevOrder(). + * + * \note This function will call the functions set by GX_SetTexRegionCallback() (and GX_SetTlutRegionCallback() if the texture is color-index + * format) to obtain the texture regions associated with this texture object. These callbacks are set to default functions by GX_Init(). + * + * \warning If the texture is a color-index texture, you must load the associated TLUT (using GX_LoadTlut()) before calling GX_LoadTexObj(). + * + * \param[in] obj ptr to a texture object + * \param[in] mapid \ref texmapid, GX_TEXMAP0 to GX_TEXMAP7 only + * + * \return none + */ +void GX_LoadTexObj(GXTexObj *obj,u8 mapid); + +/*! + * \fn void GX_LoadTlut(GXTlutObj *obj,u32 tlut_name) + * \brief Copies a Texture Look-Up Table (TLUT) from main memory to Texture Memory (TMEM). + * + * \details The \a tlut_name parameter is the name of a pre-allocated area of TMEM. The callback function set by GX_SetTlutRegionCallback() converts + * the \a tlut_name into a \ref GXTlutRegion pointer. The TLUT is loaded in the TMEM region described by this pointer. The TLUT object \a obj describes the + * location of the TLUT in main memory, the TLUT format, and the TLUT size. \a obj should have been previously initialized using GX_InitTlutObj(). + * + * \note GX_Init() sets a default callback to convert \a tlut_names from \ref tlutname to \ref GXTlutRegion pointers. The default configuration of + * TMEM has 20 TLUTs, 16 each 256 entries by 16 bits, and 4 each 1k entries by 16 bits. This configuration can be overriden by calling + * GX_InitTlutRegion() and GX_InitTexCacheRegion() to allocate TMEM. Then you can define your own region allocation scheme using GX_SetTlutRegionCallback() + * and GX_SetTexRegionCallback(). + * + * \param[in] obj ptr to a TLUT object; application must allocate this + * \param[in] tlut_name \ref tlutname + * + * \return none + */ +void GX_LoadTlut(GXTlutObj *obj,u32 tlut_name); + +/*! + * \fn void GX_LoadTexObjPreloaded(GXTexObj *obj,GXTexRegion *region,u8 mapid) + * \brief Loads the state describing a preloaded texture into one of eight hardware register sets. + * + * \details Before this happens, the texture object \a obj should be initialized using GX_InitTexObj() or GX_InitTexObjCI(). The \a mapid parameter refers to + * the texture state register set. The texture should be loaded beforehand using GX_PreloadEntireTexture(). Once loaded, the texture can be used in any Texture Environment + * (TEV) stage using GX_SetTevOrder(). + * + * \note GX_Init() initially calls GX_SetTevOrder() to make a simple texture pipeline that associates GX_TEXMAP0 with GX_TEVSTAGE0, + * GX_TEXMAP1 with GX_TEVSTAGE1, etc.

+ * + * \note GX_LoadTexObjPreloaded() will not call the functions set by GX_SetTexRegionCallback() (and GX_SetTlutRegionCallback() if the texture is color + * index format) because the region is set explicitly; however, these callback functions must be aware of all regions that are preloaded. The default + * callbacks set by GX_Init() assume there are no preloaded regions. + * + * \param[in] obj ptr to a texture object + * \param[in] region ptr to a region object that describes an area of texture memory + * \param[in] mapid \ref texmapid for reference in a TEV stage + * + * \return none + */ +void GX_LoadTexObjPreloaded(GXTexObj *obj,GXTexRegion *region,u8 mapid); + +/*! + * \fn void GX_PreloadEntireTexture(GXTexObj *obj,GXTexRegion *region) + * \brief Loads a given texture from DRAM into the texture memory. + * + * \details Accesses to this texture will bypass the texture cache tag look-up and instead read the texels directly from texture memory. The + * texture region must be the same size as the texture (see GX_InitTexPreloadRegion()). + * + * \note This function loads the texture into texture memory, but to use it as a source for the Texture Environment (TEV) unit, you must first + * call GX_LoadTexObjPreloaded(). The default configuration (as set by GX_Init()) of texture memory has no preloaded regions, so you must install + * your own region allocator callbacks using GX_SetTexRegionCallback() and GX_SetTlutRegionCallback(). + * + * \param[in] obj ptr to object describing the texture to laod + * \param[in] region TMEM texture region to load the texture into + * + * \return none + */ +void GX_PreloadEntireTexture(GXTexObj *obj,GXTexRegion *region); + +/*! + * \fn void GX_InitTlutObj(GXTlutObj *obj,void *lut,u8 fmt,u16 entries) + * \brief Initializes a Texture Look-Up Table (TLUT) object. + * + * \details The TLUT object describes the location of the TLUT in main memory, its format and the number of entries. The TLUT in main + * memory described by this object can be loaded into a TLUT allocated in the texture memory using the GX_LoadTlut() function. + * + * \param[in] obj ptr to a TLUT object + * \param[in] lut ptr to look-up table data; must be 32B aligned + * \param[in] fmt format of the entries in the TLUt; GX_TL_IA8, GX_TL_RGB565 or GX_TL_RGB5A3 + * \param[in] entries number of entries in this table; maximum is 16,384 + * + * \return none + */ +void GX_InitTlutObj(GXTlutObj *obj,void *lut,u8 fmt,u16 entries); + +/*! + * \fn void GX_InitTlutRegion(GXTlutRegion *region,u32 tmem_addr,u8 tlut_sz) + * \brief Initializes a Texture Look-Up Table (TLUT) region object. + * + * \note GX_Init() creates default TLUT regions, so the application does not need to call this function unless a new configuration + * of Texture Memory is desired. In that case, the application should also set a new TLUT region allocator using GX_SetTlutRegionCallback(). + * + * \param[in] region obj ptr to a TLUT region struct; application must allocate this + * \param[in] tmem_addr location of the TLU in TMEM; ptr must be aligned to table size + * \param[in] tlut_sz size of the table + * + * \return none + */ +void GX_InitTlutRegion(GXTlutRegion *region,u32 tmem_addr,u8 tlut_sz); + +/*! + * \fn void GX_InitTexObjLOD(GXTexObj *obj,u8 minfilt,u8 magfilt,f32 minlod,f32 maxlod,f32 lodbias,u8 biasclamp,u8 edgelod,u8 maxaniso) + * \brief Sets texture Level Of Detail (LOD) controls explicitly for a texture object. + * + * \details It is the application's responsibility to provide memory for a texture object. When initializing a texture object using GX_InitTexObj() + * or GX_InitTexObjCI(), this information is set to default values based on the mipmap flag. This function allows the programmer to override those + * defaults. + * + * \note This function should be called after GX_InitTexObj() or GX_InitTexObjCI() for a particular texture object.

+ * + * \note Setting \a biasclamp prevents over-biasing the LOD when the polygon is perpendicular to the view direction.

+ * + * \note \a edgelod should be set if \a biasclamp is set or \a maxaniso is set to GX_ANISO_2 or GX_ANISO_4.

+ * + * \note Theoretically, there is no performance difference amongst various magnification/minification filter settings except GX_LIN_MIP_LIN filter with + * GX_TF_RGBA8 texture format which takes twice as much as other formats. However, this argument is assuming an environment where texture cache always + * hits. On real environments, you will see some performance differences by changing filter modes (especially minification filter) because cache-hit ratio + * changes according to which filter mode is being used. + * + * \param[in] obj ptr to a texture object + * \param[in] minfilt \ref texfilter to use when the texel/pixel ratio is >= 1.0 + * \param[in] magfilt \ref texfilter to use when the texel/pixel ratio is < 1.0; use only GX_NEAR or GX_LINEAR + * \param[in] minlod minimum LOD value from 0.0 - 10.0 inclusive + * \param[in] maxlod maximum LOD value from 0.0 - 10.0 inclusive + * \param[in] lodbias bias to add to computed LOD value + * \param[in] biasclamp if GX_ENABLE, clamp (LOD+lodbias) so that it is never less than the minimum extent of the pixel projected in texture space + * \param[in] edgelod if GX_ENABLE, compute LOD using adjacent texels + * \param[in] maxaniso \ref anisotropy to use + * + * \return none + */ +void GX_InitTexObjLOD(GXTexObj *obj,u8 minfilt,u8 magfilt,f32 minlod,f32 maxlod,f32 lodbias,u8 biasclamp,u8 edgelod,u8 maxaniso); + +/*! + * \fn void GX_SetTexCoordScaleManually(u8 texcoord,u8 enable,u16 ss,u16 ts) + * \brief Overrides the automatic texture coordinate scaling (based upon the associated map size) and lets one manually assign the scale values that + * are used for a given \a texcoord. + * + * \details Setting the \a enable parameter to GX_TRUE gives this behavior. The given \a texcoord retains these manual scale values until this function is + * called again. This function is also used to return a given texture coordinate back to normal, automatic scaling (by setting \a enable to GX_FALSE). + * + * \note A texture coordinate is scaled after being computed by the relevant texgen and before the actual texture lookup Normally, the scale value is set + * according to the texture map that is associated with the texcoord by GX_SetTevOrder(). However, there are certain cases where a different scale value is + * desirable. One such case is when using indirect tiled textures (see GX_SetTevIndTile()). + * + * \param[in] texcoord the \ref texcoordid being changed + * \param[in] enable if GX_TRUE, scale will be set manually, otherwise set automatically and \a ss and \a ts ignored + * \param[in] ss manual scale value for the S component of the coordinate + * \param[in] ts manual scale value for the T component of the coordinate + * + * \return none + */ +void GX_SetTexCoordScaleManually(u8 texcoord,u8 enable,u16 ss,u16 ts); + +/*! + * \fn void GX_SetTexCoordBias(u8 texcoord,u8 s_enable,u8 t_enable) + * \brief Sets the texture coordinate bias of a particular texture. + * + * \details Range bias is used with texture coordinates applied in GX_REPEAT wrap mode in order to increase the precision of texture coordinates + * that spread out over a large range. The texture coordinate values for a primitive are biased (by an equal integer) towards zero early in the + * graphics pipeline, thus preserving bits for calculation later in the pipe. Since the coordinates are repeated, this bias by an integer should + * have no effect upon the actual appearance of the texture. + * + * \note Texture coordinate range bias is something that is normally set automatically by the GX API (during GX_Begin()); however, when a texture + * coordinate is being scaled manually (by using GX_SetTexCoordScaleManually()), the associated bias is no longer modified by GX. Thus, + * GX_SetTexCoordBias() allows the bias to be changed while a texture coordinate is being manually controlled. + * + * \param[in] texcoord \ref texcoordid being changed + * \param[in] s_enable enable or disable range bias in the S direction with GX_ENABLE/GX_DISABLE + * \param[in] t_enable enable or disable range bias in the T direction with GX_ENABLE/GX_DISABLE + * + * \return none + */ +void GX_SetTexCoordBias(u8 texcoord,u8 s_enable,u8 t_enable); + +/*! + * \fn GXTexRegionCallback GX_SetTexRegionCallback(GXTexRegionCallback cb) + * \brief Sets the callback function called by GX_LoadTexObj() to obtain an available texture region. + * + * \details GX_Init() calls this function to set a default region-assignment policy. A programmer can override this default region assignment + * by implementing his own callback function. A pointer to the texture object and the texture map ID that are passed + * to GX_LoadTexObj() are provided to the callback function. + * + * \param[in] cb ptr to a function that takes a pointer to a GXTexObj and a \ref texmapid as a parameter and returns a pointer to a \ref GXTexRegion. + * + * \return pointer to the previously set callback + */ +GXTexRegionCallback GX_SetTexRegionCallback(GXTexRegionCallback cb); + +/*! + * \fn GXTlutRegionCallback GX_SetTlutRegionCallback(GXTlutRegionCallback cb) + * \brief Sets the callback function called by GX_LoadTlut() to find the region into which to load the TLUT. + * + * \details GX_LoadTexObj() will also call \a cb to obtain the Texture Look-up Table (TLUT) region when the texture forma + * is color-index. + * + * GX_Init() calls GX_SetTlutRegionCallback() to set a default TLUT index-to-region mapping. The name for the TLUT from the texture + * object is provided as an argument to the callback. The callback should return a pointer to the \ref GXTlutRegion for this TLUT index. + * + * \note For a given \a tlut_name (in the \ref GXTlutRegionCallback struct), \a cb must always return the same \ref GXTlutRegion; this is because + * GX_LoadTlut() will initialize data into the \ref GXTlutRegion which GX_LoadTexObj() will subsequently use. + * + * \param[in] cb ptr to a function that takes a u32 TLUT name as a parameter and returns a pointer to a \ref GXTlutRegion. + * + * \return pointer to the previously set callback + */ +GXTlutRegionCallback GX_SetTlutRegionCallback(GXTlutRegionCallback cb); + +/*! + * \fn void GX_InitLightPos(GXLightObj *lit_obj,f32 x,f32 y,f32 z) + * \brief Sets the position of the light in the light object. + * + * \details The GameCube graphics hardware supports local diffuse lights. The position of the light should be in the same space as a transformed + * vertex position (i.e., view space). + * + * \note Although the hardware doesn't support parallel directional diffuse lights, it is possible to get "almost parallel" lights by setting + * sufficient large values to position parameters (x, y and z) which makes the light position very far away from objects to be lit and all rays + * considered almost parallel.

+ * + * \note The memory for the light object must be allocated by the application; this function does not load any hardware registers directly. To + * load a light object into a hardware light, use GX_LoadLightObj() or GX_LoadLightObjIdx(). + * + * \param[in] lit_obj ptr to the light object + * \param[in] x X coordinate to place the light at + * \param[in] y Y coordinate to place the light at + * \param[in] z Z coordinate to place the light at + * + * \return none + */ +void GX_InitLightPos(GXLightObj *lit_obj,f32 x,f32 y,f32 z); + +/*! + * \fn void GX_InitLightColor(GXLightObj *lit_obj,GXColor col) + * \brief Sets the color of the light in the light object. + * + * \note The memory for the light object should be allocated by the application; this function does not load any hardware register directly. To + * load a light object into a hardware light, use GX_LoadLightObj() or GX_LoadLightObjIdx(). + * + * \param[in] lit_obj ptr to the light object + * \param[in] col color to set the light to + * + * \return none + */ +void GX_InitLightColor(GXLightObj *lit_obj,GXColor col); + +/*! + * \fn void GX_InitLightDir(GXLightObj *lit_obj,f32 nx,f32 ny,f32 nz) + * \brief Sets the direction of a light in the light object. + * + * \details This direction is used when the light object is used as spotlight or a specular light (see the attn_fn parameter of GX_SetChanCtrl()). + * + * \note The coordinate space of the light normal should be consistent with a vertex normal transformed by a normal matrix; i.e., it should be + * transformed to view space.

+ * + * \note This function does not set the direction of parallel directional diffuse lights. If you want parallel diffuse lights, you may put the light + * position very far from every objects to be lit. (See GX_InitLightPos() and GX_SetChanCtrl())

+ * + * \note The memory for the light object must be allocated by the application; this function does not load any hardware registers. To load a light + * object into a hardware light, use GX_LoadLightObj() or GX_LoadLightObjIdx(). + * + * \param[in] lit_obj ptr to the light object + * \param[in] nx X coordinate of the light normal + * \param[in] ny Y coordinate of the light normal + * \param[in] nz Z coordinate of the light normal + * + * \return none + */ +void GX_InitLightDir(GXLightObj *lit_obj,f32 nx,f32 ny,f32 nz); + +/*! + * \fn void GX_LoadLightObj(GXLightObj *lit_obj,u8 lit_id) + * \brief Loads a light object into a set of hardware registers associated with a \ref lightid. + * + * \details This function copies the light object data into the graphics FIFO through the CPU write-gather buffer mechanism. This guarantees that + * the light object is coherent with the CPU cache. + * + * \note The light object must have been initialized first using the necessary GX_InitLight*() functions.

+ * + * \note Another way to load a light object is with GX_LoadLightObjIdx(). + * + * \param[in] lit_obj ptr to the light object to load + * \param[in] lit_id \ref lightid to load this light into + * + * \return none + */ +void GX_LoadLightObj(GXLightObj *lit_obj,u8 lit_id); + +/*! + * \fn void GX_LoadLightObjIdx(u32 litobjidx,u8 litid) + * \brief Instructs the GP to fetch the light object at \a ltobjindx from an array. + * + * \details The light object is retrieved from the array to which GX_SetArray(GX_VA_LIGHTARRAY, ...) points. Then it loads the object into + * the hardware register associated with \ref lightid. + * + * \note Data flows directly from the array in DRAM to the GP; therefore, the light object data may not be coherent with the CPU's cache. The + * application is responsible for storing the light object data from the CPU cache (using DCStoreRange()) before calling GX_LoadLightObjIdx(). + * + * \param[in] litobjidx index to a light object + * \param[in] litid \ref lightid to load this light into + * + * \return none + */ +void GX_LoadLightObjIdx(u32 litobjidx,u8 litid); + +/*! + * \fn void GX_InitLightDistAttn(GXLightObj *lit_obj,f32 ref_dist,f32 ref_brite,u8 dist_fn) + * \brief Sets coefficients for distance attenuation in a light object. + * + * \details This function uses three easy-to-control parameters instead of k0, k1, and k2 in GX_InitLightAttn(). + * + * In this function, you can specify the brightness on an assumed reference point. The parameter \a ref_distance is distance between the light + * and the reference point. The parameter \a ref_brite specifies ratio of the brightness on the reference point. The value for \a ref_dist should + * be greater than 0 and that for \a ref_brite should be within 0 < \a ref_brite < 1, otherwise distance attenuation feature is turned off. The + * parameter \a dist_fn defines type of the brightness decreasing curve by distance; GX_DA_OFF turns distance attenuation feature off. + * + * \note If you want more flexible control, it is better to use GX_InitLightAttn() and calculate appropriate coefficients.

+ * + * \note This function sets parameters only for distance attenuation. Parameters for angular attenuation should be set by using + * GX_InitLightSpot() or GX_InitLightAttnA().

+ * + * \note This function does not load any hardware registers directly. To load a light object into a hardware light, use GX_LoadLightObj() or + * GX_LoadLightObjIdx(). + * + * \param[in] lit_obj ptr to a light object + * \param[in] ref_dist distance between the light and reference point + * \param[in] ref_brite brightness of the reference point + * \param[in] dist_fn \ref distattnfn to use + * + * \return none + */ +void GX_InitLightDistAttn(GXLightObj *lit_obj,f32 ref_dist,f32 ref_brite,u8 dist_fn); + +/*! + * \fn void GX_InitLightAttn(GXLightObj *lit_obj,f32 a0,f32 a1,f32 a2,f32 k0,f32 k1,f32 k2) + * \brief Sts coefficients used in the lighting attenuation calculation in a given light object. + * + * \details The parameters \a a0, \a a1, and \a a2 are used for angular (spotlight) attenuation. The coefficients \a k0, \a k1, and \a k2 are used for + * distance attenuation. The attenuation function is: + * + *       atten = clamp0(\a a2^2 * aattn^2 + \a a1 * aattn + \a a0) / (\a k2 * d^2 + \a k1 * d + \a k0) + * + * where aattn is the cosine of the angle between the light direction and the vector from the light position to the vertex, and d is + * the distance from the light position to the vertex when the channel attenuation function is GX_AF_SPOT. The light color will be + * multiplied by the atten factor when the attenuation function for the color channel referencing this light is set to GX_AF_SPOT + * (see GX_SetChanCtrl()). + * + * \note The convenience function GX_InitLightSpot() can be used to set the angle attenuation coefficents based on several spot light + * types. The convenience function GX_InitLightDistAttn() can be used to set the distance attenuation coefficients using one of several + * common attenuation functions.

+ * + * \note The convenience macro GX_InitLightShininess() can be used to set the attenuation parameters for specular lights.

+ * + * \note When the channel attenuation function is set to GX_AF_SPEC, the aattn and d parameter are equal to the dot product of the + * eye-space vertex normal and the half-angle vector set by GX_InitSpecularDir().

+ * + * \note This function does not load any hardware registers directly. To load a light object into a hardware light, use GX_LoadLightObj() + * or GX_LoadLightObjIdx(). + * + * \param[in] lit_obj ptr to a light object + * \param[in] a0 angle attenuation coefficient + * \param[in] a1 angle attenuation coefficient + * \param[in] a2 angle attenuation coefficient + * \param[in] k0 distance attenuation coefficient + * \param[in] k1 distance attenuation coefficient + * \param[in] k2 distance attenuation coefficient + * + * \return none + */ +void GX_InitLightAttn(GXLightObj *lit_obj,f32 a0,f32 a1,f32 a2,f32 k0,f32 k1,f32 k2); + +/*! + * \fn void GX_InitLightAttnA(GXLightObj *lit_obj,f32 a0,f32 a1,f32 a2) + * \brief Sets coefficients used in the lighting angle attenuation calculation in a given light object. + * + * \details The parameters \a a0, \a a1, and \a a2 are used for angular (spotlight) attenuation. The attenuation + * function is: + * + *       atten = clamp0(\a a2^2 * cos(theta)^2 + \a a1 * cos(theta) + \a a0) / (\a k2 * d^2 + \a k1 * d + \a k0) + * + * where cos(theta) is the cosine of the angle between the light normal and the vector from the light position to the vertex, and d is the distance + * from the light position to the vertex. The \a k0-\a 2 coefficients can be set using GX_InitLightAttnK(). You can set both the \a a0-\a 2 and \a k0-\a 2 coefficients + * can be set using GX_InitLightAttn(). The light color will be multiplied by the atten factor when the attenuation function for the color channel + * referencing this light is set to GX_AF_SPOT (see GX_SetChanCtrl()). + * + * \note The convenience function GX_InitLightSpot() can be used to set the angle attenuation coefficents based on several spot light types. The + * convenience function GX_InitLightDistAttn() can be used to set the distance attenuation coefficients using one of several common attenuation functions.

+ * + * \note This function does not load any hardware registers directly. To load a light object into a hardware light, use GX_LoadLightObj() or GX_LoadLightObjIdx(). + * + * \param[in] lit_obj ptr to a light object + * \param[in] a0 angle attenuation coefficient + * \param[in] a1 angle attenuation coefficient + * \param[in] a2 angle attenuation coefficient + * + * \return none + */ +void GX_InitLightAttnA(GXLightObj *lit_obj,f32 a0,f32 a1,f32 a2); + +/*! + * \fn void GX_InitLightAttnK(GXLightObj *lit_obj,f32 k0,f32 k1,f32 k2) + * \brief Sets coefficients used in the lighting distance attenuation calculation in a given light object. + * + * \details The coefficients \a k0, \a k1, and \a k2 are used for distance attenuation. The attenuation function is: + * + *       atten = clamp0(\a a2^2 * cos(theta)^2 + \a a1 * cos(theta) + \a a0) / (\a k2 * d^2 + \a k1 * d + \a k0) + * + * where cos(theta) is the cosine of the angle between the light normal and the vector from the light position to the vertex, and d is the distance + * from the light position to the vertex. The \a a0-\a 2 coefficients can be set using GX_InitLightAttnA(). You can set both the \a a0-\a 2 and \a k0-\a 2 coefficients + * can be set using GX_InitLightAttn(). The light color will be multiplied by the atten factor when the attenuation function for the color channel + * referencing this light is set to GX_AF_SPOT (see GX_SetChanCtrl()). + * + * \note The convenience function GX_InitLightSpot() can be used to set the angle attenuation coefficents based on several spot light types. The convenience + * function GX_InitLightDistAttn() can be used to set the distance attenuation coefficients using one of several common attenuation functions.

+ * + * \note Note that this function does not load any hardware registers directly. To load a light object into a hardware light, use GX_LoadLightObj() or + * GX_LoadLightObjIdx(). + * + * \param[in] lit_obj ptr to a light object + * \param[in] k0 distance attenuation coefficient + * \param[in] k1 distance attenuation coefficient + * \param[in] k2 distance attenuation coefficient + * + * \return none + */ +void GX_InitLightAttnK(GXLightObj *lit_obj,f32 k0,f32 k1,f32 k2); + +/*! + * \fn void GX_InitSpecularDirHA(GXLightObj *lit_obj,f32 nx,f32 ny,f32 nz,f32 hx,f32 hy,f32 hz) + * \brief Sets the direction and half-angle vector of a specular light in the light object. + * + * \details These vectors are used when the light object is used only as specular light. In contrast to GX_InitSpecularDir(), + * which caclulates half-angle vector automatically by assuming the view vector as (0, 0, 1), this function allows users to + * specify half-angle vector directly as input arguments. It is useful to do detailed control for orientation of highlights. + * + * \note This function does not load any hardware registers. To load a light object into a hardware light, use GX_LoadLightObj() + * or GX_LoadLightObjIdx().

+ * + * \note Other notes are similar to those described in GX_InitSpecularDir(). + * + * \param[in] lit_obj ptr to a light object + * \param[in] nx X coordinate of the light normal + * \param[in] ny Y coordinate of the light normal + * \param[in] nz Z coordinate of the light normal + * \param[in] hx X coordinate of half-angle + * \param[in] hy Y coordinate of half-angle + * \param[in] hz Z coordinate of half-angle + * + * \return none + */ +void GX_InitSpecularDirHA(GXLightObj *lit_obj,f32 nx,f32 ny,f32 nz,f32 hx,f32 hy,f32 hz); + +/*! + * \fn void GX_InitSpecularDir(GXLightObj *lit_obj,f32 nx,f32 ny,f32 nz) + * \brief Sets the direction of a specular light in the light object. + * + * \details This direction is used when the light object is used only as specular light. The coordinate space of the light normal + * should be consistent with a vertex normal transformed by a normal matrix; i.e., it should be transformed to view space. + * + * \note This function should be used if and only if the light object is used as specular light. One specifies a specular light in + * GX_SetChanCtrl() by setting the \ref attenfunc to GX_AF_SPEC. Furthermore, one must not use GX_InitLightDir() or + * GX_InitLightPos() to set up a light object which will be used as a specular light since these functions will destroy the information + * set by GX_InitSpecularDir(). In contrast to diffuse lights (including spotlights) that are considered local lights, a specular light + * is a parallel light (i.e. the specular light is infinitely far away such that all the rays of the light are parallel), and thus one + * can only specify directional information. + * + * \note This function does not load any hardware registers. To load a light object into a hardware light, use GX_LoadLightObj() + * or GX_LoadLightObjIdx(). + * + * \param[in] lit_obj ptr to a light object + * \param[in] nx X coordinate of the light normal + * \param[in] ny Y coordinate of the light normal + * \param[in] nz Z coordinate of the light normal + * + * \return none + */ +void GX_InitSpecularDir(GXLightObj *lit_obj,f32 nx,f32 ny,f32 nz); + +/*! + * \fn void GX_InitLightSpot(GXLightObj *lit_obj,f32 cut_off,u8 spotfn) + * \brief Sets coefficients for angular (spotlight) attenuation in light object. + * + * \details This function uses two easy-to-control parameters instead of \a a0, \a a1, and \a a2 on GX_InitLightAttn(). + * + * \details The parameter \a cut_off specifies cutoff angle of the spotlight by degree. The spotlight works while the angle between the ray for a vertex and + * the light direction given by GX_InitLightDir() is smaller than this cutoff angle. The value for \a cut_off should be within 0 < \a cut_off <= 90.0, otherwise + * given light object doesn't become a spotlight. + * + * The parameter \a spotfn defines type of the illumination distribution within cutoff angle. The value GX_SP_OFF turns spotlight feature off even if + * color channel setting is using GX_AF_SPOT (see GX_SetChanCtrl()). + * + * \note This function can generate only some kind of simple spotlights. If you want more flexible control, it is better to use GX_InitLightAttn() and calculate + * appropriate coefficients.

+ * + * \note This function sets parameters only for angular attenuation. Parameters for distance attenuation should be set by using GX_InitLightDistAttn() or + * GX_InitLightAttnK().

+ * + * \note This function does not load any hardware registers directly. To load a light object into a hardware light, use GX_LoadLightObj() or GX_LoadLightObjIdx(). + * + * \param[in] lit_obj ptr to a light object + * \param[in] cut_off cutoff angle of the spotlight, in degrees + * \param[in] spotfn \ref spotfn to use for this light + * + * \return none + */ +void GX_InitLightSpot(GXLightObj *lit_obj,f32 cut_off,u8 spotfn); + +u32 GX_ReadClksPerVtx(); +u32 GX_GetOverflowCount(); +u32 GX_ResetOverflowCount(); + +/*! + * \fn lwp_t GX_GetCurrentGXThread() + * \brief Returns the current GX thread. + * + * \details The current GX thread should be the thread that is currently responsible for generating graphics data. By default, + * the GX thread is the thread that invoked GX_Init(); however, it may be changed by calling GX_SetCurrentGXThread(). + * + * \note When graphics data is being generated in immediate mode (that is, the CPU FIFO = GP FIFO, and the GP is actively consuming + * data), the high watermark may be triggered. When this happens, the high watermark interrupt handler will suspend the GX thread, thus + * preventing any further graphics data from being generated. The low watermark interrupt handler will resume the thread. + * + * \return the current GX thread + */ +lwp_t GX_GetCurrentGXThread(); + +/*! + * \fn lwp_t GX_SetCurrentGXThread() + * \brief Sets the current GX thread to the calling thread. + * + * \details The new thread should be the thread that will be responsible for generating graphics data. By default, the GX thread is + * the thread that invoked GX_Init(); however, it may be changed by calling this function. + * + * \note It is a programming error to change GX thread while the current GX thread is suspended by a high water mark interrupt. This + * indicates that you have two threads about to generate GX data.

+ * + * \note When graphics data is being generated in immediate mode (that is, the CPU FIFO = GP FIFO, and the GP is actively consuming + * data), the high watermark may be triggered. When this happens, the high watermark interrupt handler will suspend the GX thread, thus + * preventing any further graphics data from being generated. The low watermark interrupt handler will resume the thread. + * + * \return the previous GX thread ID + */ +lwp_t GX_SetCurrentGXThread(); + +/*! + * \fn void GX_RestoreWriteGatherPipe() + * \brief Restores the write-gather pipe. + * + * \details The CPU fifo that was attached at the time GX_RedirectWriteGatherPipe() was called will be re-attached. If there is data pending + * in the write gather pipe (e.g. if the amount of data written was not a multiple of 32 bytes), the data will be padded with zeroes and + * flushed out. + * + * \warning This function must be called between successive calls to GX_RedirectWriteGatherPipe(). + * + * \return none + */ +void GX_RestoreWriteGatherPipe(); + +/*! + * \fn void GX_SetGPMetric(u32 perf0,u32 perf1) + * \brief Sets two performance metrics to measure in the GP. + * + * \details perf0 and perf1 are set to measure. The initial metrics measured are GX_PERF0_NONE and GX_PERF1_NONE, which return counts of zero + * for the first call to GX_ReadGPMetric(). + * + * Each performance counter has a unique set of events or ratios that it can count. In some cases the same metric can be counted using both + * counters, for example GX_PERF0_VERTICES and GX_PERF1_VERTICES. Ratios (the metric name ends in _RATIO) are multiplied by + * 1000 (1000 = all misses/clips, etc., 0 = no misses/clips, etc.). + * + * \note GX_ReadGPMetric() and GX_ClearGPMetric() can be used in the callback associated with the draw sync interrupt (see GX_SetDrawSyncCallback()). + * This function should not be used in the draw sync callback because it will insert tokens in the GP command stream at random times. + * + * \warning This function reads results from CPU-accessible registers in the GP, therefore, this command must not be used in a display list. In + * addition, the performance counters in some cases are triggered by sending tokens through the Graphics FIFO to the GP. This implies that + * the function should only be used in immediate mode (when the Graphics FIFO is connected to the CPU and the GP at the same time). It may + * also be necessary to send a draw sync token using GX_SetDrawSync() or call GX_SetDrawDone() after GX_ReadGPMetric() to ensure that the + * state has actually been processed by the GP. + * + * \param[in] perf0 \ref perf0metrics to measure + * \param[in] perf1 \ref perf1metrics to measure + * + * \returns none + */ +void GX_SetGPMetric(u32 perf0,u32 perf1); + +/*! + * \fn void GX_ClearGPMetric() + * \brief Clears the two virtual GP performance counters to zero. + * + * \note The counter's function is set using GX_SetGPMetric(); the counter's value is read using GX_ReadGPMetric(). Consult these for more details. + * + * \warning This function resets CPU accessible counters, so it should not be used in a display list. + * + * \return none + */ +void GX_ClearGPMetric(); + +/*! + * \fn void GX_InitXfRasMetric() + * \brief Initialize the transformation unit (XF) rasterizer unit (RAS) to take performance measurements. + * + * \warning This function should be avoided; use the GP performance metric functions instead. + * + * \return none + */ +void GX_InitXfRasMetric(); + +/*! + * \fn void GX_ReadXfRasMetric(u32 *xfwaitin,u32 *xfwaitout,u32 *rasbusy,u32 *clks) + * \brief Read performance metric values from the XF and RAS units. + * + * \warning This function should be avoided; use the GP performance metric functions instead.

+ * + * \warning The parameters for this function are a best guess based on names and existing code. + * + * \param[out] xfwaitin Number of clocks the XF has waited for data to arrive? + * \param[out] xfwaitout Number of clocks the XF has waited to push finished data down? + * \param[out] rasbusy Number of clocks the RAS has spent being busy? + * \param[out] clks Clocks that have passed since last count reset? + * + * \return none + */ +void GX_ReadXfRasMetric(u32 *xfwaitin,u32 *xfwaitout,u32 *rasbusy,u32 *clks); + +/*! + * \fn void GX_ClearVCacheMetric() + * \brief Clears the Vertex Cache performance counter. + * + * \details This function clears the performance counter by sending a special clear token via the Graphics FIFO. + * + * \note To set the metric for the counter, call GX_SetVCacheMetric(); to read the counter value, call GX_ReadVCacheMetric(). + * + * \return none + */ +void GX_ClearVCacheMetric(); + +/*! + * \fn void GX_ReadVCacheMetric(u32 *check,u32 *miss,u32 *stall) + * \brief Returns Vertex Cache performance counters. + * + * \details Each call to this function resets the counter to zero. GX_SetVCacheMetric() sets the metric to be measured by + * the Vertex Cache performance counter. + * + * \warning This function reads CPU-accessible registers in the GP and so should not be called in a display list. + * + * \param[out] check total number of accesses to the vertex cache + * \param[out] miss total number of cache misses to the vertex cache + * \param[out] stall number of GP clocks that the vertex cache was stalled + * + * \return none + */ +void GX_ReadVCacheMetric(u32 *check,u32 *miss,u32 *stall); + +/*! + * \fn void GX_SetVCacheMetric(u32 attr) + * \brief Sets the metric the Vertex Cache performance counter will measure. + * + * \details It is possible to monitor a particular attribute or all attributes using \a attr. + * + * \note To clear the counter, call GX_ClearVCacheMetric(); to read the counter value, call GX_ReadVCacheMetric(). + * + * \param[in] attr \ref vcachemetrics to measure + * + * \return none + */ +void GX_SetVCacheMetric(u32 attr); + +/*! + * \fn void GX_GetGPStatus(u8 *overhi,u8 *underlow,u8 *readIdle,u8 *cmdIdle,u8 *brkpt) + * \brief Reads the current status of the GP. + * + * \details \a overhi and \a underlow will indicate whether or not the watermarks have been reached. If the CPU and GP FIFOs + * are the same, then \a overhi will indicate whether or not the current GX thread is suspended. The value of \a brkpt can be + * used to determine if a breakpoint is in progress (i.e. GP reads are suspended; they are resumed by a call to + * GX_DisableBreakPt()). A callback can also be used to notify your application that the break point has been reached. (see + * GX_SetBreakPtCallback()) + * + * \param[out] overhi GX_TRUE if high watermark has been passed + * \param[out] underlow GX_TRUE if low watermark has been passed + * \param[out] readIdle GX_TRUE if the GP read unit is idle + * \param[out] cmdIdle GX_TRUE if all commands have been flushed to XF + * \param[out] brkpt GX_TRUE if FIFO has reached a breakpoint and GP reads have been stopped + * + * \return none + */ +void GX_GetGPStatus(u8 *overhi,u8 *underlow,u8 *readIdle,u8 *cmdIdle,u8 *brkpt); + +/*! + * \fn void GX_ReadGPMetric(u32 *cnt0,u32 *cnt1) + * \brief Returns the count of the previously set performance metrics. + * + * \note The performance metrics can be set using GX_SetGPMetric(); the counters can be cleared using GX_ClearGPMetric().

+ * + * \note GX_ReadGPMetric() and GX_ClearGPMetric() can be used in the callback associated with the draw sync interrupt (see GX_SetDrawSyncCallback()). + * The function GX_SetGPMetric() should not be used in the draw sync callback because it will insert tokens in the GP command stream at random times.

+ * + * \warning This function reads results from CPU-accessible registers in the GP, therefore, this command must not be used in a display list. It + * may also be necessary to send a draw sync token using GX_SetDrawSync() or GX_SetDrawDone() before GX_ReadGPMetric() is called to ensure that the + * state has actually been processed by the GP. + * + * \param[out] cnt0 current value of GP counter 0 + * \param[out] cnt1 current value of GP counter 1 + * + * \return none + */ +void GX_ReadGPMetric(u32 *cnt0,u32 *cnt1); + +/*! + * \fn void GX_ReadBoundingBox(u16 *top,u16 *bottom,u16 *left,u16 *right) + * \brief Returns the bounding box of pixel coordinates that are drawn in the Embedded Framebuffer (EFB). + * + * \details This function reads the bounding box values. GX_ClearBoundingBox() can be used reset the values of the bounding box. + * + * \note Since the hardware can only test the bounding box in quads (2x2 pixel blocks), the result of this function may contain error + * of plus or minus 1 pixel. Also because of this, left and top are always even-numbered and right and bottom + * are always odd-numbered. + * + * \param[out] top uppermost line in the bounding box + * \param[out] bottom lowest line in the bounding box + * \param[out] left leftmost pixel in the bounding box + * \param[out] right rightmost pixel in the bounding box + * + * \return none + */ +void GX_ReadBoundingBox(u16 *top,u16 *bottom,u16 *left,u16 *right); + +/*! + * \fn volatile void* GX_RedirectWriteGatherPipe(void *ptr) + * \brief Temporarily points the CPU's write-gather pipe at a new location. + * + * \details After calling this function, subsequent writes to the address returned by this function (or the WGPipe union) + * will be gathered and sent to a destination buffer. The write pointer is automatically incremented by the GP. The write + * gather pipe can be restored by calling GX_RestoreWriteGatherPipe(). This function cannot be called between a + * GX_Begin()/GX_End() pair. + * + * \note The destination buffer, referred to by \a ptr, must be 32 byte aligned. The amount of data written should + * also be 32-byte aligned. If it is not, zeroes will be added to pad the destination buffer to 32 bytes. No part of the + * destination buffer should be modified inside the CPU caches - this may introduce cache incoherency problems.

+ * + * \note The write gather pipe is one of the fastest ways to move data out of the CPU (the other being the locked cache DMA). + * In general, you are compute-bound when sending data from the CPU.

+ * + * \note This function is cheaper than trying to create a fake CPU fifo around a destination buffer, which requires calls to + * GX_SetCPUFifo(), GX_InitFifoBase(), etc. This function performs very light weight state saves by assuming that the CPU and + * GP FIFOs never change. + * + * \warning No GX commands can be called until the write gather pipe is restored. You MUST call + * GX_RestoreWriteGatherPipe() before calling this function again, or else the final call to restore the pipe will fail. + * + * \param[in] ptr to destination buffer, 32-byte aligned + * + * \return real address of the write-gather "port". All writes to this address will be gathered by the CPU write gather pipe. + * You may also use the WGPipe union. If you do not use the WGPipe union, ensure that your local variable is volatile. + */ +volatile void* GX_RedirectWriteGatherPipe(void *ptr); + +/*! + * \def GX_InitLightPosv(lo,vec) + * \brief Sets the position of the light in the light object using a vector structure. + * + * \note The GameCube graphics hardware supports local diffuse lights. The position of the light should be in the same space as a + * transformed vertex position (i.e. view space).

+ * + * \note The memory for the light object must be allocated by the application; this function does not load any hardware registers directly. To + * load a light object into a hardware light, use GX_LoadLightObj() or GX_LoadLightObjIdx(). + * + * \param[in] lo ptr to the light object + * \param[in] vec struct or array of three values for the position + * + * \return none + */ +#define GX_InitLightPosv(lo,vec) \ + (GX_InitLightPos((lo), *(f32*)(vec), *((f32*)(vec)+1), *((f32*)(vec)+2))) + +/*! + * \def GX_InitLightDirv(lo,vec) + * \brief Sets the direction of a light in the light object using a vector structure. + * + * \details This direction is used when the light object is used as a spotlight or a specular light, see the \a attn_fn parameter of + * GX_SetChanCtrl(). + * + * \note The memory for the light object must be allocated by the application; this function does not load any hardware registers. To load a + * light object into a hardware light, use GX_LoadLightObj() or GX_LoadLightObjIdx().

+ * + * \note The coordinate space of the light normal should be consistent with a vertex normal transformed by a normal matrix; i.e., it should be + * transformed to view space.

+ * + * \note This function does not set the direction of parallel lights. + * + * \param[in] lo ptr to the light object + * \param[in] vec struct or array of three values for the direction + * + * \return none + */ +#define GX_InitLightDirv(lo,vec) \ + (GX_InitLightDir((lo), *(f32*)(vec), *((f32*)(vec)+1), *((f32*)(vec)+2))) + +/*! + * \def GX_InitSpecularDirv(lo,vec) + * \brief Sets the direction of a specular light in the light object using a vector. + * + * \details This direction is used when the light object is used only as specular light. + * + * \note The memory for the light object must be allocated by the application; this function does not load any hardware registers. To load a + * light object into a hardware light, use GX_LoadLightObj() or GX_LoadLightObjIdx().

+ * + * \note The coordinate space of the light normal should be consistent with a vertex normal transformed by a normal matrix; i.e., it should + * be transformed to view space. + * + * \warning This function should be used if and only if the light object is used as specular light. One specifies a specular light in + * GX_SetChanCtrl() by setting \a attn_fn to GX_AF_SPEC. Furthermore, one must not use GX_InitLightDir() or GX_InitLightPos() to + * set up a light object which will be used as a specular light since these functions will destroy the information set by GX_InitSpecularDir(). + * In contrast to diffuse lights (including spotlights) that are considered local lights, a specular light is a parallel light (i.e. the + * specular light is infinitely far away such that all the rays of the light are parallel), and thus one can only specify directional + * information. + * + * \param[in] lo ptr to the light object + * \param[in] vec struct or array of three values for the direction + * + * \return none + */ +#define GX_InitSpecularDirv(lo,vec) \ + (GX_InitSpecularDir((lo), *(f32*)(vec), *((f32*)(vec)+1), *((f32*)(vec)+2))) + +/*! + * \def GX_InitSpecularDirHAv(lo,vec0,vec1) + * \brief Sets the direction and half-angle vector of a specular light in the light object using a vector. + * + * \details These vectors are used when the light object is used only as specular light. + * + * \note The memory for the light object must be allocated by the application; this function does not load any hardware registers. To load a + * light object into a hardware light, use GX_LoadLightObj() or GX_LoadLightObjIdx().

+ * + * \note In contrast to GX_InitSpecularDirv(), which caclulates half-angle vector automatically by assuming the view vector as (0,0,1), this + * function allows users to specify half-angle vector directly as input arguments. It is useful to do detailed control for orientation of + * highlights.

+ * + * \note Other notes are similar to that described in GX_InitSpecularDirv(). + * + * \param[in] lo ptr to the light object + * \param[in] vec0 struct or array of three values for the direction + * \param[in] vec1 struct or array of three values for the half-angle + * + * \return none + */ +#define GX_InitSpecularDirHAv(lo,vec0,vec1) \ + (GX_InitSpecularDirHA((lo), \ + *(f32*)(vec0), *((f32*)(vec0)+1), *((f32*)(vec0)+2), \ + *(f32*)(vec1), *((f32*)(vec1)+1), *((f32*)(vec1)+2))) + +/*! + * \def GX_InitLightShininess(lobj, shininess) + * \brief Sets \a shininess of a per-vertex specular light. + * + * \details In reality, shininess is a property of the material being lit, not the light. However, in the Graphics Processor, the specular + * calculation is implemented by reusing the diffuse angle/distance attenuation function, so shininess is determined by the light attenuation + * parameters (see GX_InitLightAttn()). Note that the equation is attempting to approximate the function (N*H)^shininess. Since the attenuation + * equation is only a ratio of quadratics, a true exponential function is not possible. To enable the specular calculation, you must set the + * attenuation parameter of the lighting channel to GX_AF_SPEC using GX_SetChanCtrl(). + * + * \param[in] lobj ptr to the light object + * \param[in] shininess shininess parameter + * + * \return none + */ +#define GX_InitLightShininess(lobj, shininess) \ + (GX_InitLightAttn(lobj, 0.0F, 0.0F, 1.0F, \ + (shininess)/2.0F, 0.0F, \ + 1.0F-(shininess)/2.0F )) + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/gx_struct.h b/wii/libogc/include/ogc/gx_struct.h new file mode 100644 index 0000000000..107e7a7b72 --- /dev/null +++ b/wii/libogc/include/ogc/gx_struct.h @@ -0,0 +1,78 @@ +/*------------------------------------------------------------- + +gx_struct.h -- support header + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#ifndef __GX_STRUCT_H__ +#define __GX_STRUCT_H__ + +/*! +\file gx_struct.h +\brief support header +*/ + +#include + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + +/*! +\typedef struct _gx_rmodeobj GXRModeObj +\brief structure to hold the selected video and render settings +\param viTVMode mode and type of TV +\param fbWidth width of external framebuffer +\param efbHeight height of embedded framebuffer +\param xfbHeight height of external framebuffer +\param viXOrigin x starting point of first pixel to draw on VI +\param viYOrigin y starting point of first pixel to draw on VI +\param viWidth width of configured VI +\param viHeight height of configured VI +*/ +typedef struct _gx_rmodeobj { + u32 viTVMode; + u16 fbWidth; + u16 efbHeight; + u16 xfbHeight; + u16 viXOrigin; + u16 viYOrigin; + u16 viWidth; + u16 viHeight; + u32 xfbMode; + u8 field_rendering; + u8 aa; + u8 sample_pattern[12][2]; + u8 vfilter[7]; +} GXRModeObj; + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/ios.h b/wii/libogc/include/ogc/ios.h new file mode 100644 index 0000000000..c308d02345 --- /dev/null +++ b/wii/libogc/include/ogc/ios.h @@ -0,0 +1,65 @@ +/*------------------------------------------------------------- + +ios.h -- IOS control + +Copyright (C) 2008 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) +Hector Martin (marcan) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#ifndef __IOS_H__ +#define __IOS_H__ + +#if defined(HW_RVL) + +#include +#include + +#define IOS_EINVAL -0x3004 +#define IOS_EBADVERSION -0x3100 +#define IOS_ETOOMANYVIEWS -0x3101 +#define IOS_EMISMATCH -0x3102 + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +s32 __IOS_InitializeSubsystems(void); +s32 __IOS_ShutdownSubsystems(void); +s32 __IOS_LoadStartupIOS(void); +s32 __IOS_LaunchNewIOS(int version); +s32 IOS_GetPreferredVersion(void); +s32 IOS_ReloadIOS(int version); +s32 IOS_GetVersion(); +s32 IOS_GetRevision(); +s32 IOS_GetRevisionMajor(); +s32 IOS_GetRevisionMinor(); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif + +#endif diff --git a/wii/libogc/include/ogc/ipc.h b/wii/libogc/include/ogc/ipc.h new file mode 100644 index 0000000000..c65c44be19 --- /dev/null +++ b/wii/libogc/include/ogc/ipc.h @@ -0,0 +1,103 @@ +/*------------------------------------------------------------- + +ipc.h -- Interprocess Communication with Starlet + +Copyright (C) 2008 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) +Hector Martin (marcan) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#ifndef __IPC_H__ +#define __IPC_H__ + +#include + +#define IPC_HEAP -1 + +#define IPC_OPEN_NONE 0 +#define IPC_OPEN_READ 1 +#define IPC_OPEN_WRITE 2 +#define IPC_OPEN_RW (IPC_OPEN_READ|IPC_OPEN_WRITE) + +#define IPC_MAXPATH_LEN 64 + +#define IPC_OK 0 +#define IPC_EINVAL -4 +#define IPC_ENOHEAP -5 +#define IPC_ENOENT -6 +#define IPC_EQUEUEFULL -8 +#define IPC_ENOMEM -22 + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef struct _ioctlv +{ + void *data; + u32 len; +} ioctlv; + +void __IPC_Reinitialize(void); + +typedef s32 (*ipccallback)(s32 result,void *usrdata); + +s32 iosCreateHeap(s32 size); +void* iosAlloc(s32 hid,s32 size); +void iosFree(s32 hid,void *ptr); + +void* IPC_GetBufferLo(); +void* IPC_GetBufferHi(); +void IPC_SetBufferLo(void *bufferlo); +void IPC_SetBufferHi(void *bufferhi); + +s32 IOS_Open(const char *filepath,u32 mode); +s32 IOS_OpenAsync(const char *filepath,u32 mode,ipccallback ipc_cb,void *usrdata); + +s32 IOS_Close(s32 fd); +s32 IOS_CloseAsync(s32 fd,ipccallback ipc_cb,void *usrdata); + +s32 IOS_Seek(s32 fd,s32 where,s32 whence); +s32 IOS_SeekAsync(s32 fd,s32 where,s32 whence,ipccallback ipc_cb,void *usrdata); +s32 IOS_Read(s32 fd,void *buf,s32 len); +s32 IOS_ReadAsync(s32 fd,void *buf,s32 len,ipccallback ipc_cb,void *usrdata); +s32 IOS_Write(s32 fd,const void *buf,s32 len); +s32 IOS_WriteAsync(s32 fd,const void *buf,s32 len,ipccallback ipc_cb,void *usrdata); + +s32 IOS_Ioctl(s32 fd,s32 ioctl,void *buffer_in,s32 len_in,void *buffer_io,s32 len_io); +s32 IOS_IoctlAsync(s32 fd,s32 ioctl,void *buffer_in,s32 len_in,void *buffer_io,s32 len_io,ipccallback ipc_cb,void *usrdata); +s32 IOS_Ioctlv(s32 fd,s32 ioctl,s32 cnt_in,s32 cnt_io,ioctlv *argv); +s32 IOS_IoctlvAsync(s32 fd,s32 ioctl,s32 cnt_in,s32 cnt_io,ioctlv *argv,ipccallback ipc_cb,void *usrdata); + +s32 IOS_IoctlvFormat(s32 hId,s32 fd,s32 ioctl,const char *format,...); +s32 IOS_IoctlvFormatAsync(s32 hId,s32 fd,s32 ioctl,ipccallback usr_cb,void *usr_data,const char *format,...); + +s32 IOS_IoctlvReboot(s32 fd,s32 ioctl,s32 cnt_in,s32 cnt_io,ioctlv *argv); +s32 IOS_IoctlvRebootBackground(s32 fd,s32 ioctl,s32 cnt_in,s32 cnt_io,ioctlv *argv); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/irq.h b/wii/libogc/include/ogc/irq.h new file mode 100644 index 0000000000..2dae7bdc90 --- /dev/null +++ b/wii/libogc/include/ogc/irq.h @@ -0,0 +1,189 @@ +/*------------------------------------------------------------- + +irq.h -- Interrupt subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ + + +#ifndef __IRQ_H__ +#define __IRQ_H__ + +/*! \file irq.h +\brief Interrupt subsystem + +*/ + +#include +#include "context.h" + +#define IM_NONE (0xffffffff) +#define IRQ_MEM0 0 +#define IRQ_MEM1 1 +#define IRQ_MEM2 2 +#define IRQ_MEM3 3 +#define IRQ_MEMADDRESS 4 +#define IRQ_DSP_AI 5 +#define IRQ_DSP_ARAM 6 +#define IRQ_DSP_DSP 7 +#define IRQ_AI 8 +#define IRQ_EXI0_EXI 9 +#define IRQ_EXI0_TC 10 +#define IRQ_EXI0_EXT 11 +#define IRQ_EXI1_EXI 12 +#define IRQ_EXI1_TC 13 +#define IRQ_EXI1_EXT 14 +#define IRQ_EXI2_EXI 15 +#define IRQ_EXI2_TC 16 +#define IRQ_PI_CP 17 +#define IRQ_PI_PETOKEN 18 +#define IRQ_PI_PEFINISH 19 +#define IRQ_PI_SI 20 +#define IRQ_PI_DI 21 +#define IRQ_PI_RSW 22 +#define IRQ_PI_ERROR 23 +#define IRQ_PI_VI 24 +#define IRQ_PI_DEBUG 25 +#define IRQ_PI_HSP 26 +#if defined(HW_RVL) +#define IRQ_PI_ACR 27 +#endif +#define IRQ_MAX 32 + +#define IRQMASK(irq) (0x80000000u>>irq) + +#define IM_MEM0 IRQMASK(IRQ_MEM0) +#define IM_MEM1 IRQMASK(IRQ_MEM1) +#define IM_MEM2 IRQMASK(IRQ_MEM2) +#define IM_MEM3 IRQMASK(IRQ_MEM3) +#define IM_MEMADDRESS IRQMASK(IRQ_MEMADDRESS) +#define IM_MEM (IM_MEM0|IM_MEM1|IM_MEM2|IM_MEM3|IM_MEMADDRESS) + +#define IM_DSP_AI IRQMASK(IRQ_DSP_AI) +#define IM_DSP_ARAM IRQMASK(IRQ_DSP_ARAM) +#define IM_DSP_DSP IRQMASK(IRQ_DSP_DSP) +#define IM_DSP (IM_DSP_AI|IM_DSP_ARAM|IM_DSP_DSP) + +#define IM_AI IRQMASK(IRQ_AI) + +#define IM_EXI0_EXI IRQMASK(IRQ_EXI0_EXI) +#define IM_EXI0_TC IRQMASK(IRQ_EXI0_TC) +#define IM_EXI0_EXT IRQMASK(IRQ_EXI0_EXT) +#define IM_EXI0 (IM_EXI0_EXI|IM_EXI0_TC|IM_EXI0_EXT) + +#define IM_EXI1_EXI IRQMASK(IRQ_EXI1_EXI) +#define IM_EXI1_TC IRQMASK(IRQ_EXI1_TC) +#define IM_EXI1_EXT IRQMASK(IRQ_EXI1_EXT) +#define IM_EXI1 (IM_EXI1_EXI|IM_EXI1_TC|IM_EXI1_EXT) + +#define IM_EXI2_EXI IRQMASK(IRQ_EXI2_EXI) +#define IM_EXI2_TC IRQMASK(IRQ_EXI2_TC) +#define IM_EXI2 (IM_EXI2_EXI|IM_EXI2_TC) +#define IM_EXI (IM_EXI0|IM_EXI1|IM_EXI2) + +#define IM_PI_CP IRQMASK(IRQ_PI_CP) +#define IM_PI_PETOKEN IRQMASK(IRQ_PI_PETOKEN) +#define IM_PI_PEFINISH IRQMASK(IRQ_PI_PEFINISH) +#define IM_PI_SI IRQMASK(IRQ_PI_SI) +#define IM_PI_DI IRQMASK(IRQ_PI_DI) +#define IM_PI_RSW IRQMASK(IRQ_PI_RSW) +#define IM_PI_ERROR IRQMASK(IRQ_PI_ERROR) +#define IM_PI_VI IRQMASK(IRQ_PI_VI) +#define IM_PI_DEBUG IRQMASK(IRQ_PI_DEBUG) +#define IM_PI_HSP IRQMASK(IRQ_PI_HSP) +#if defined(HW_DOL) +#define IM_PI (IM_PI_CP|IM_PI_PETOKEN|IM_PI_PEFINISH|IM_PI_SI|IM_PI_DI|IM_PI_RSW|IM_PI_ERROR|IM_PI_VI|IM_PI_DEBUG|IM_PI_HSP) +#elif defined(HW_RVL) +#define IM_PI_ACR IRQMASK(IRQ_PI_ACR) +#define IM_PI (IM_PI_CP|IM_PI_PETOKEN|IM_PI_PEFINISH|IM_PI_SI|IM_PI_DI|IM_PI_RSW|IM_PI_ERROR|IM_PI_VI|IM_PI_DEBUG|IM_PI_HSP|IM_PI_ACR) +#endif + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + +/*! \typedef void (*raw_irq_handler_t)(u32 irq,void *ctx) +\brief function pointer typedef for the interrupt handler callback +\param[in] irq interrupt number of triggered interrupt. +\param[in] ctx pointer to the user data. +*/ +typedef void (*raw_irq_handler_t)(u32 irq,void *ctx); + + +/*! \fn raw_irq_handler_t IRQ_Request(u32 nIrq,raw_irq_handler_t pHndl,void *pCtx) +\brief Register an interrupt handler. +\param[in] nIrq interrupt number to which to register the handler +\param[in] pHndl pointer to the handler callback function which to call when interrupt has triggered +\param[in] pCtx pointer to user data to pass with, when handler is called + +\return Old interrupt handler, else NULL +*/ +raw_irq_handler_t IRQ_Request(u32 nIrq,raw_irq_handler_t pHndl,void *pCtx); + + +/*! \fn raw_irq_handler_t IRQ_Free(u32 nIrq) +\brief Free an interrupt handler. +\param[in] nIrq interrupt number for which to free the handler + +\return Old interrupt handler, else NULL +*/ +raw_irq_handler_t IRQ_Free(u32 nIrq); + + +/*! \fn raw_irq_handler_t IRQ_GetHandler(u32 nIrq) +\brief Get the handler from interrupt number +\param[in] nIrq interrupt number for which to retrieve the handler + +\return interrupt handler, else NULL +*/ +raw_irq_handler_t IRQ_GetHandler(u32 nIrq); + + +/*! \fn u32 IRQ_Disable() +\brief Disable the complete IRQ subsystem. No interrupts will be served. Multithreading kernel fully disabled. + +\return Old state of the IRQ subsystem +*/ +u32 IRQ_Disable(void); + + +/*! \fn u32 IRQ_Restore(u32 level) +\brief Restore the IRQ subsystem with the given level. This is function should be used together with IRQ_Disable() +\param[in] level IRQ level to restore to. + +\return none +*/ +void IRQ_Restore(u32 level); + +void __MaskIrq(u32 nMask); +void __UnmaskIrq(u32 nMask); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/isfs.h b/wii/libogc/include/ogc/isfs.h new file mode 100644 index 0000000000..584848c57d --- /dev/null +++ b/wii/libogc/include/ogc/isfs.h @@ -0,0 +1,70 @@ +#ifndef __ISFS_H__ +#define __ISFS_H__ + +#if defined(HW_RVL) + +#include + +#define ISFS_MAXPATH IPC_MAXPATH_LEN + +#define ISFS_OPEN_READ 0x01 +#define ISFS_OPEN_WRITE 0x02 +#define ISFS_OPEN_RW (ISFS_OPEN_READ | ISFS_OPEN_WRITE) + +#define ISFS_OK 0 +#define ISFS_ENOMEM -22 +#define ISFS_EINVAL -101 + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef struct _fstats +{ + u32 file_length; + u32 file_pos; +} fstats; + +typedef s32 (*isfscallback)(s32 result,void *usrdata); + +s32 ISFS_Initialize(); +s32 ISFS_Deinitialize(); + +s32 ISFS_Open(const char *filepath,u8 mode); +s32 ISFS_OpenAsync(const char *filepath,u8 mode,isfscallback cb,void *usrdata); +s32 ISFS_Close(s32 fd); +s32 ISFS_CloseAsync(s32 fd,isfscallback cb,void *usrdata); +s32 ISFS_Delete(const char *filepath); +s32 ISFS_DeleteAsync(const char *filepath,isfscallback cb,void *usrdata); +s32 ISFS_ReadDir(const char *filepath,char *name_list,u32 *num); +s32 ISFS_ReadDirAsync(const char *filepath,char *name_list,u32 *num,isfscallback cb,void *usrdata); +s32 ISFS_CreateFile(const char *filepath,u8 attributes,u8 owner_perm,u8 group_perm,u8 other_perm); +s32 ISFS_CreateFileAsync(const char *filepath,u8 attributes,u8 owner_perm,u8 group_perm,u8 other_perm,isfscallback cb,void *usrdata); +s32 ISFS_Write(s32 fd,const void *buffer,u32 length); +s32 ISFS_WriteAsync(s32 fd,const void *buffer,u32 length,isfscallback cb,void *usrdata); +s32 ISFS_Read(s32 fd,void *buffer,u32 length); +s32 ISFS_ReadAsync(s32 fd,void *buffer,u32 length,isfscallback cb,void *usrdata); +s32 ISFS_Seek(s32 fd,s32 where,s32 whence); +s32 ISFS_SeekAsync(s32 fd,s32 where,s32 whence,isfscallback cb,void *usrdata); +s32 ISFS_CreateDir(const char *filepath,u8 attributes,u8 owner_perm,u8 group_perm,u8 other_perm); +s32 ISFS_CreateDirAsync(const char *filepath,u8 attributes,u8 owner_perm,u8 group_perm,u8 other_perm,isfscallback cb,void *usrdata); +s32 ISFS_GetStats(void *stats); +s32 ISFS_GetStatsAsync(void *stats,isfscallback cb,void *usrdata); +s32 ISFS_GetFileStats(s32 fd,fstats *status); +s32 ISFS_GetFileStatsAsync(s32 fd,fstats *status,isfscallback cb,void *usrdata); +s32 ISFS_GetAttr(const char *filepath,u32 *ownerID,u16 *groupID,u8 *attributes,u8 *ownerperm,u8 *groupperm,u8 *otherperm); +s32 ISFS_GetAttrAsync(const char *filepath,u32 *ownerID,u16 *groupID,u8 *attributes,u8 *ownerperm,u8 *groupperm,u8 *otherperm,isfscallback cb,void *usrdata); +s32 ISFS_Rename(const char *filepathOld,const char *filepathNew); +s32 ISFS_RenameAsync(const char *filepathOld,const char *filepathNew,isfscallback cb,void *usrdata); +s32 ISFS_SetAttr(const char *filepath,u32 ownerID,u16 groupID,u8 attributes,u8 ownerperm,u8 groupperm,u8 otherperm); +s32 ISFS_SetAttrAsync(const char *filepath,u32 ownerID,u16 groupID,u8 attributes,u8 ownerperm,u8 groupperm,u8 otherperm,isfscallback cb,void *usrdata); +s32 ISFS_GetUsage(const char* filepath, u32* usage1, u32* usage2); +s32 ISFS_GetUsageAsync(const char* filepath, u32* usage1, u32* usage2,isfscallback cb,void *usrdata); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif /* defined(HW_RVL) */ + +#endif diff --git a/wii/libogc/include/ogc/libversion.h b/wii/libogc/include/ogc/libversion.h new file mode 100644 index 0000000000..88d910daff --- /dev/null +++ b/wii/libogc/include/ogc/libversion.h @@ -0,0 +1,13 @@ +#ifndef __LIBVERSION_H__ +#define __LIBVERSION_H__ + +#define _V_MAJOR_ 1 +#define _V_MINOR_ 8 +#define _V_PATCH_ 17 + +#define _V_DATE_ __DATE__ +#define _V_TIME_ __TIME__ + +#define _V_STRING "libOGC Release 1.8.17" + +#endif // __LIBVERSION_H__ diff --git a/wii/libogc/include/ogc/lwp.h b/wii/libogc/include/ogc/lwp.h new file mode 100644 index 0000000000..b68bf065f2 --- /dev/null +++ b/wii/libogc/include/ogc/lwp.h @@ -0,0 +1,204 @@ +/*------------------------------------------------------------- + +lwp.h -- Thread subsystem I + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + + +-------------------------------------------------------------*/ + + +#ifndef __LWP_H__ +#define __LWP_H__ + +/*! \file lwp.h +\brief Thread subsystem I + +*/ + +#include + +#define LWP_CLOSED -1 +#define LWP_SUCCESSFUL 0 +#define LWP_ALREADY_SUSPENDED 1 +#define LWP_NOT_SUSPENDED 2 + +#define LWP_PRIO_IDLE 0 +#define LWP_PRIO_HIGHEST 127 + +#define LWP_THREAD_NULL 0xffffffff +#define LWP_TQUEUE_NULL 0xffffffff + +#ifdef __cplusplus +extern "C" { +#endif + + +/*! \typedef u32 lwp_t +\brief typedef for the thread context handle +*/ +typedef u32 lwp_t; + + +/*! \typedef u32 lwpq_t +\brief typedef for the thread queue's context handle +*/ +typedef u32 lwpq_t; + +/*! \fn s32 LWP_CreateThread(lwp_t *thethread,void* (*entry)(void *),void *arg,void *stackbase,u32 stack_size,u8 prio) +\brief Spawn a new thread with the given parameters +\param[out] thethread pointer to a lwp_t handle +\param[in] entry pointer to the thread's entry function. +\param[in] arg pointer to an argument for the thread's entry function. +\param[in] stackbase pointer to the threads stackbase address. If NULL, the stack is allocated by the thread system. +\param[in] stack_size size of the provided stack. If 0, the default STACKSIZE of 8Kb is taken. +\param[in] prio priority on which the newly created thread runs. + +\return 0 on success, <0 on error +*/ +s32 LWP_CreateThread(lwp_t *thethread,void* (*entry)(void *),void *arg,void *stackbase,u32 stack_size,u8 prio); + + +/*! \fn s32 LWP_SuspendThread(lwp_t thethread) +\brief Suspend the given thread. +\param[in] thethread handle to the thread context which should be suspended. + +\return 0 on success, <0 on error +*/ +s32 LWP_SuspendThread(lwp_t thethread); + + +/*! \fn s32 LWP_ResumeThread(lwp_t thethread) +\brief Resume the given thread. +\param[in] thethread handle to the thread context which should be resumed. + +\return 0 on success, <0 on error +*/ +s32 LWP_ResumeThread(lwp_t thethread); + + +/*! \fn BOOL LWP_ThreadIsSuspended(lwp_t thethread) +\brief Test whether the given thread is suspended or not +\param[in] thethread handle to the thread context which should be tested. + +\return TRUE or FALSE +*/ +BOOL LWP_ThreadIsSuspended(lwp_t thethread); + + +/*! \fn lwp_t LWP_GetSelf() +\brief Return the handle to the current thread. + +\return thread context handle +*/ +lwp_t LWP_GetSelf(); + + +/*! \fn void LWP_SetThreadPriority(lwp_t thethread,u32 prio) +\brief Set the priority of the given thread. +\param[in] thethread handle to the thread context whos priority should be changed. If NULL, the current thread will be taken. +\param[in] prio new priority to set + +\return none +*/ +void LWP_SetThreadPriority(lwp_t thethread,u32 prio); + + +/*! \fn void LWP_YieldThread() +\brief Yield the current thread to another one with higher priority or if not running at the same priority which state is runnable. + +\return none +*/ +void LWP_YieldThread(); + + +/*! \fn void LWP_Reschedule(u32 prio) +\brief Reschedule all threads running at the given priority +\param[in] prio priority level to reschedule + +\return none +*/ +void LWP_Reschedule(u32 prio); + + +/*! \fn s32 LWP_JoinThread(lwp_t thethread,void **value_ptr) +\brief Join the given thread. +\param[in] thethread handle to the thread's context which should be joined to wait on termination. +\param[in] value_ptr pointer-pointer to a variable to receive the return code of the terminated thread. + +\return 0 on success, <0 on error +*/ +s32 LWP_JoinThread(lwp_t thethread,void **value_ptr); + + +/*! \fn void LWP_InitQueue(lwpq_t *thequeue) +\brief Initialize the thread synchronization queue +\param[in] thequeue pointer to a lwpq_t handle. + +\return 0 on success, <0 on error +*/ +s32 LWP_InitQueue(lwpq_t *thequeue); + + +/*! \fn void LWP_CloseQueue(lwpq_t thequeue) +\brief Close the thread synchronization queue and releas the handle +\param[in] thequeue handle to the thread's synchronization queue + +\return none +*/ +void LWP_CloseQueue(lwpq_t thequeue); + + +/*! \fn s32 LWP_ThreadSleep(lwpq_t thequeue) +\brief Pushes the current thread onto the given thread synchronization queue and sets the thread state to blocked. +\param[in] thequeue handle to the thread's synchronization queue to push the thread on + +\return none +*/ +s32 LWP_ThreadSleep(lwpq_t thequeue); + + +/*! \fn void LWP_ThreadSignal(lwpq_t thequeue) +\brief Signals one thread to be revmoved from the thread synchronization queue and sets it back to running state. +\param[in] thequeue handle to the thread's synchronization queue to pop the blocked thread off + +\return none +*/ +void LWP_ThreadSignal(lwpq_t thequeue); + + +/*! \fn void LWP_ThreadBroadcast(lwpq_t thequeue) +\brief Removes all blocked threads from the thread synchronization queue and sets them back to running state. +\param[in] thequeue handle to the thread's synchronization queue to pop the blocked threads off + +\return none +*/ +void LWP_ThreadBroadcast(lwpq_t thequeue); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/lwp_config.h b/wii/libogc/include/ogc/lwp_config.h new file mode 100644 index 0000000000..beda421e54 --- /dev/null +++ b/wii/libogc/include/ogc/lwp_config.h @@ -0,0 +1,19 @@ +#ifndef __LWP_CONFIG_H__ +#define __LWP_CONFIG_H__ + + +#define LWP_MAX_MQUEUES 64 + +#define LWP_MAX_MUTEXES 64 + +#define LWP_MAX_THREADS 16 + +#define LWP_MAX_SEMAS 64 + +#define LWP_MAX_CONDVARS 64 + +#define LWP_MAX_TQUEUES 64 + +#define LWP_MAX_WATCHDOGS 64 + +#endif diff --git a/wii/libogc/include/ogc/lwp_heap.h b/wii/libogc/include/ogc/lwp_heap.h new file mode 100644 index 0000000000..3c9ac48502 --- /dev/null +++ b/wii/libogc/include/ogc/lwp_heap.h @@ -0,0 +1,59 @@ +#ifndef __LWP_HEAP_H__ +#define __LWP_HEAP_H__ + +#include +#include "machine/asm.h" + +#define HEAP_BLOCK_USED 1 +#define HEAP_BLOCK_FREE 0 + +#define HEAP_DUMMY_FLAG (0+HEAP_BLOCK_USED) + +#define HEAP_OVERHEAD (sizeof(u32)*2) +#define HEAP_BLOCK_USED_OVERHEAD (sizeof(void*)*2) +#define HEAP_MIN_SIZE (HEAP_OVERHEAD+sizeof(heap_block)) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _heap_block_st heap_block; +struct _heap_block_st { + u32 back_flag; + u32 front_flag; + heap_block *next; + heap_block *prev; +}; + +typedef struct _heap_iblock_st { + u32 free_blocks; + u32 free_size; + u32 used_blocks; + u32 used_size; +} heap_iblock; + +typedef struct _heap_cntrl_st { + heap_block *start; + heap_block *final; + + heap_block *first; + heap_block *perm_null; + heap_block *last; + u32 pg_size; + u32 reserved; +} heap_cntrl; + +u32 __lwp_heap_init(heap_cntrl *theheap,void *start_addr,u32 size,u32 pg_size); +void* __lwp_heap_allocate(heap_cntrl *theheap,u32 size); +BOOL __lwp_heap_free(heap_cntrl *theheap,void *ptr); +u32 __lwp_heap_getinfo(heap_cntrl *theheap,heap_iblock *theinfo); + +#ifdef LIBOGC_INTERNAL +#include +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/lwp_messages.h b/wii/libogc/include/ogc/lwp_messages.h new file mode 100644 index 0000000000..2529b6331d --- /dev/null +++ b/wii/libogc/include/ogc/lwp_messages.h @@ -0,0 +1,79 @@ +#ifndef __LWP_MESSAGES_H__ +#define __LWP_MESSAGES_H__ + +#include +#include +#include +#include + +//#define _LWPMQ_DEBUG + +#define LWP_MQ_FIFO 0 +#define LWP_MQ_PRIORITY 1 + +#define LWP_MQ_STATUS_SUCCESSFUL 0 +#define LWP_MQ_STATUS_INVALID_SIZE 1 +#define LWP_MQ_STATUS_TOO_MANY 2 +#define LWP_MQ_STATUS_UNSATISFIED 3 +#define LWP_MQ_STATUS_UNSATISFIED_NOWAIT 4 +#define LWP_MQ_STATUS_DELETED 5 +#define LWP_MQ_STATUS_TIMEOUT 6 +#define LWP_MQ_STATUS_UNSATISFIED_WAIT 7 + +#define LWP_MQ_SEND_REQUEST INT_MAX +#define LWP_MQ_SEND_URGENT INT_MIN + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*mq_notifyhandler)(void *); + +typedef struct _mqbuffer { + u32 size; + u32 buffer[1]; +} mq_buffer; + +typedef struct _mqbuffercntrl { + lwp_node node; + u32 prio; + mq_buffer contents; +} mq_buffercntrl; + +//the following struct is extensible +typedef struct _mqattr { + u32 mode; +} mq_attr; + +typedef struct _mqcntrl { + lwp_thrqueue wait_queue; + mq_attr attr; + u32 max_pendingmsgs; + u32 num_pendingmsgs; + u32 max_msgsize; + lwp_queue pending_msgs; + mq_buffer *msq_buffers; + mq_notifyhandler notify_handler; + void *notify_arg; + lwp_queue inactive_msgs; +} mq_cntrl; + +u32 __lwpmq_initialize(mq_cntrl *mqueue,mq_attr *attrs,u32 max_pendingmsgs,u32 max_msgsize); +void __lwpmq_close(mq_cntrl *mqueue,u32 status); +u32 __lwpmq_seize(mq_cntrl *mqueue,u32 id,void *buffer,u32 *size,u32 wait,u64 timeout); +u32 __lwpmq_submit(mq_cntrl *mqueue,u32 id,void *buffer,u32 size,u32 type,u32 wait,u64 timeout); +u32 __lwpmq_broadcast(mq_cntrl *mqueue,void *buffer,u32 size,u32 id,u32 *count); +void __lwpmq_msg_insert(mq_cntrl *mqueue,mq_buffercntrl *msg,u32 type); +u32 __lwpmq_flush(mq_cntrl *mqueue); +u32 __lwpmq_flush_support(mq_cntrl *mqueue); +void __lwpmq_flush_waitthreads(mq_cntrl *mqueue); + +#ifdef LIBOGC_INTERNAL +#include +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/lwp_mutex.h b/wii/libogc/include/ogc/lwp_mutex.h new file mode 100644 index 0000000000..fb8c495eaa --- /dev/null +++ b/wii/libogc/include/ogc/lwp_mutex.h @@ -0,0 +1,76 @@ +#ifndef __LWP_MUTEX_H__ +#define __LWP_MUTEX_H__ + +#include +#include + +#define LWP_MUTEX_LOCKED 0 +#define LWP_MUTEX_UNLOCKED 1 + +#define LWP_MUTEX_NEST_ACQUIRE 0 +#define LWP_MUTEX_NEST_ERROR 1 +#define LWP_MUTEX_NEST_BLOCK 2 + +#define LWP_MUTEX_FIFO 0 +#define LWP_MUTEX_PRIORITY 1 +#define LWP_MUTEX_INHERITPRIO 2 +#define LWP_MUTEX_PRIORITYCEIL 3 + +#define LWP_MUTEX_SUCCESSFUL 0 +#define LWP_MUTEX_UNSATISFIED_NOWAIT 1 +#define LWP_MUTEX_NEST_NOTALLOWED 2 +#define LWP_MUTEX_NOTOWNER 3 +#define LWP_MUTEX_DELETED 4 +#define LWP_MUTEX_TIMEOUT 5 +#define LWP_MUTEX_CEILINGVIOL 6 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _lwpmutexattr { + u32 mode; + u32 nest_behavior; + u8 prioceil,onlyownerrelease; +} lwp_mutex_attr; + +typedef struct _lwpmutex { + lwp_thrqueue wait_queue; + lwp_mutex_attr atrrs; + u32 lock,nest_cnt,blocked_cnt; + lwp_cntrl *holder; +} lwp_mutex; + +void __lwp_mutex_initialize(lwp_mutex *mutex,lwp_mutex_attr *attrs,u32 init_lock); +u32 __lwp_mutex_surrender(lwp_mutex *mutex); +void __lwp_mutex_seize_irq_blocking(lwp_mutex *mutex,u64 timeout); +void __lwp_mutex_flush(lwp_mutex *mutex,u32 status); + +static __inline__ u32 __lwp_mutex_seize_irq_trylock(lwp_mutex *mutex,u32 *isr_level); + +#define __lwp_mutex_seize(_mutex_t,_id,_wait,_timeout,_level) \ + do { \ + if(__lwp_mutex_seize_irq_trylock(_mutex_t,&_level)) { \ + if(!_wait) { \ + _CPU_ISR_Restore(_level); \ + _thr_executing->wait.ret_code = LWP_MUTEX_UNSATISFIED_NOWAIT; \ + } else { \ + __lwp_threadqueue_csenter(&(_mutex_t)->wait_queue); \ + _thr_executing->wait.queue = &(_mutex_t)->wait_queue; \ + _thr_executing->wait.id = _id; \ + __lwp_thread_dispatchdisable(); \ + _CPU_ISR_Restore(_level); \ + __lwp_mutex_seize_irq_blocking(_mutex_t,(u64)_timeout); \ + } \ + } \ + } while(0) + +#ifdef LIBOGC_INTERNAL +#include +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/lwp_objmgr.h b/wii/libogc/include/ogc/lwp_objmgr.h new file mode 100644 index 0000000000..00c6e788fb --- /dev/null +++ b/wii/libogc/include/ogc/lwp_objmgr.h @@ -0,0 +1,49 @@ +#ifndef __LWP_OBJMGR_H__ +#define __LWP_OBJMGR_H__ + +#include +#include "lwp_queue.h" + +#define LWP_OBJMASKTYPE(type) ((type)<<16) +#define LWP_OBJMASKID(id) ((id)&0xffff) +#define LWP_OBJTYPE(id) ((id)>>16) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _lwp_objinfo lwp_objinfo; + +typedef struct _lwp_obj { + lwp_node node; + s32 id; + lwp_objinfo *information; +} lwp_obj; + +struct _lwp_objinfo { + u32 min_id; + u32 max_id; + u32 max_nodes; + u32 node_size; + lwp_obj **local_table; + void *obj_blocks; + lwp_queue inactives; + u32 inactives_cnt; +}; + +void __lwp_objmgr_initinfo(lwp_objinfo *info,u32 max_nodes,u32 node_size); +void __lwp_objmgr_free(lwp_objinfo *info,lwp_obj *object); +lwp_obj* __lwp_objmgr_allocate(lwp_objinfo *info); +lwp_obj* __lwp_objmgr_get(lwp_objinfo *info,u32 id); +lwp_obj* __lwp_objmgr_getisrdisable(lwp_objinfo *info,u32 id,u32 *p_level); +lwp_obj* __lwp_objmgr_getnoprotection(lwp_objinfo *info,u32 id); + +#ifdef LIBOGC_INTERNAL +#include +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/lwp_priority.h b/wii/libogc/include/ogc/lwp_priority.h new file mode 100644 index 0000000000..3a55f3ac98 --- /dev/null +++ b/wii/libogc/include/ogc/lwp_priority.h @@ -0,0 +1,33 @@ +#ifndef __LWP_PRIORITY_H__ +#define __LWP_PRIORITY_H__ + +#include +#include "machine/processor.h" + +#define LWP_PRIO_MIN 0 +#define LWP_PRIO_MAX 255 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _priocntrl { + u32 *minor; + u32 ready_minor,ready_major; + u32 block_minor,block_major; +} prio_cntrl; + +extern vu32 _prio_major_bitmap; +extern u32 _prio_bitmap[]; + +void __lwp_priority_init(); + +#ifdef LIBOGC_INTERNAL +#include +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/lwp_queue.h b/wii/libogc/include/ogc/lwp_queue.h new file mode 100644 index 0000000000..4f6993c2f6 --- /dev/null +++ b/wii/libogc/include/ogc/lwp_queue.h @@ -0,0 +1,41 @@ +#ifndef __LWP_QUEUE_H__ +#define __LWP_QUEUE_H__ + +#include + +//#define _LWPQ_DEBUG + +#ifdef _LWPQ_DEBUG +extern int printk(const char *fmt,...); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _lwpnode { + struct _lwpnode *next; + struct _lwpnode *prev; +} lwp_node; + +typedef struct _lwpqueue { + lwp_node *first; + lwp_node *perm_null; + lwp_node *last; +} lwp_queue; + +void __lwp_queue_initialize(lwp_queue *,void *,u32,u32); +lwp_node* __lwp_queue_get(lwp_queue *); +void __lwp_queue_append(lwp_queue *,lwp_node *); +void __lwp_queue_extract(lwp_node *); +void __lwp_queue_insert(lwp_node *,lwp_node *); + +#ifdef LIBOGC_INTERNAL +#include +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/lwp_sema.h b/wii/libogc/include/ogc/lwp_sema.h new file mode 100644 index 0000000000..f4c5816ad3 --- /dev/null +++ b/wii/libogc/include/ogc/lwp_sema.h @@ -0,0 +1,44 @@ +#ifndef __LWP_SEMA_H__ +#define __LWP_SEMA_H__ + +#include +#include + +#define LWP_SEMA_MODEFIFO 0 +#define LWP_SEMA_MODEPRIORITY 1 + +#define LWP_SEMA_SUCCESSFUL 0 +#define LWP_SEMA_UNSATISFIED_NOWAIT 1 +#define LWP_SEMA_DELETED 2 +#define LWP_SEMA_TIMEOUT 3 +#define LWP_SEMA_MAXCNT_EXCEEDED 4 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _lwpsemattr { + u32 max_cnt; + u32 mode; +} lwp_semattr; + +typedef struct _lwpsema { + lwp_thrqueue wait_queue; + lwp_semattr attrs; + u32 count; +} lwp_sema; + +void __lwp_sema_initialize(lwp_sema *sema,lwp_semattr *attrs,u32 init_count); +u32 __lwp_sema_surrender(lwp_sema *sema,u32 id); +u32 __lwp_sema_seize(lwp_sema *sema,u32 id,u32 wait,u64 timeout); +void __lwp_sema_flush(lwp_sema *sema,u32 status); + +#ifdef LIBOGC_INTERNAL +#include +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/lwp_stack.h b/wii/libogc/include/ogc/lwp_stack.h new file mode 100644 index 0000000000..b047b700c3 --- /dev/null +++ b/wii/libogc/include/ogc/lwp_stack.h @@ -0,0 +1,27 @@ +#ifndef __LWP_STACK_H__ +#define __LWP_STACK_H__ + +#include +#include + +#define CPU_STACK_ALIGNMENT 8 +#define CPU_MINIMUM_STACK_SIZE 1024*8 +#define CPU_MINIMUM_STACK_FRAME_SIZE 16 +#define CPU_MODES_INTERRUPT_MASK 0x00000001 /* interrupt level in mode */ + +#ifdef __cplusplus +extern "C" { +#endif + +u32 __lwp_stack_allocate(lwp_cntrl *,u32); +void __lwp_stack_free(lwp_cntrl *); + +#ifdef LIBOGC_INTERNAL +#include +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/lwp_states.h b/wii/libogc/include/ogc/lwp_states.h new file mode 100644 index 0000000000..6fc1236e04 --- /dev/null +++ b/wii/libogc/include/ogc/lwp_states.h @@ -0,0 +1,48 @@ +#ifndef ___LWP_STATES_H__ +#define ___LWP_STATES_H__ + +#include + +#define LWP_STATES_READY 0x00000000 +#define LWP_STATES_DORMANT 0x00000001 +#define LWP_STATES_SUSPENDED 0x00000002 +#define LWP_STATES_TRANSIENT 0x00000004 +#define LWP_STATES_DELAYING 0x00000008 +#define LWP_STATES_WAITING_FOR_TIME 0x00000010 +#define LWP_STATES_WAITING_FOR_BUFFER 0x00000020 +#define LWP_STATES_WAITING_FOR_SEGMENT 0x00000040 +#define LWP_STATES_WAITING_FOR_MESSAGE 0x00000080 +#define LWP_STATES_WAITING_FOR_EVENT 0x00000100 +#define LWP_STATES_WAITING_FOR_MUTEX 0x00000200 +#define LWP_STATES_WAITING_FOR_SEMAPHORE 0x00000400 +#define LWP_STATES_WAITING_FOR_CONDVAR 0x00000800 +#define LWP_STATES_WAITING_FOR_JOINATEXIT 0x00001000 +#define LWP_STATES_WAITING_FOR_RPCREPLAY 0x00002000 +#define LWP_STATES_WAITING_FOR_PERIOD 0x00004000 +#define LWP_STATES_WAITING_FOR_SIGNAL 0x00008000 +#define LWP_STATES_INTERRUPTIBLE_BY_SIGNAL 0x00010000 + +#define LWP_STATES_LOCALLY_BLOCKED (LWP_STATES_WAITING_FOR_BUFFER | LWP_STATES_WAITING_FOR_SEGMENT | \ + LWP_STATES_WAITING_FOR_MESSAGE | LWP_STATES_WAITING_FOR_SEMAPHORE | \ + LWP_STATES_WAITING_FOR_MUTEX | LWP_STATES_WAITING_FOR_CONDVAR | \ + LWP_STATES_WAITING_FOR_JOINATEXIT | LWP_STATES_WAITING_FOR_SIGNAL) + +#define LWP_STATES_WAITING_ON_THREADQ (LWP_STATES_LOCALLY_BLOCKED | LWP_STATES_WAITING_FOR_RPCREPLAY) + +#define LWP_STATES_BLOCKED (LWP_STATES_DELAYING | LWP_STATES_WAITING_FOR_TIME | \ + LWP_STATES_WAITING_FOR_PERIOD | LWP_STATES_WAITING_FOR_EVENT | \ + LWP_STATES_WAITING_ON_THREADQ | LWP_STATES_INTERRUPTIBLE_BY_SIGNAL) + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef LIBOGC_INTERNAL +#include +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/lwp_threadq.h b/wii/libogc/include/ogc/lwp_threadq.h new file mode 100644 index 0000000000..ed9c6bbd4b --- /dev/null +++ b/wii/libogc/include/ogc/lwp_threadq.h @@ -0,0 +1,39 @@ +#ifndef __LWP_THREADQ_H__ +#define __LWP_THREADQ_H__ + +#include +#include +#include +#include + +#define LWP_THREADQ_NOTIMEOUT LWP_WD_NOTIMEOUT + +#ifdef __cplusplus +extern "C" { +#endif + +lwp_cntrl* __lwp_threadqueue_firstfifo(lwp_thrqueue *queue); +lwp_cntrl* __lwp_threadqueue_firstpriority(lwp_thrqueue *queue); +void __lwp_threadqueue_enqueuefifo(lwp_thrqueue *queue,lwp_cntrl *thethread,u64 timeout); +lwp_cntrl* __lwp_threadqueue_dequeuefifo(lwp_thrqueue *queue); +void __lwp_threadqueue_enqueuepriority(lwp_thrqueue *queue,lwp_cntrl *thethread,u64 timeout); +lwp_cntrl* __lwp_threadqueue_dequeuepriority(lwp_thrqueue *queue); +void __lwp_threadqueue_init(lwp_thrqueue *queue,u32 mode,u32 state,u32 timeout_state); +lwp_cntrl* __lwp_threadqueue_first(lwp_thrqueue *queue); +void __lwp_threadqueue_enqueue(lwp_thrqueue *queue,u64 timeout); +lwp_cntrl* __lwp_threadqueue_dequeue(lwp_thrqueue *queue); +void __lwp_threadqueue_flush(lwp_thrqueue *queue,u32 status); +void __lwp_threadqueue_extract(lwp_thrqueue *queue,lwp_cntrl *thethread); +void __lwp_threadqueue_extractfifo(lwp_thrqueue *queue,lwp_cntrl *thethread); +void __lwp_threadqueue_extractpriority(lwp_thrqueue *queue,lwp_cntrl *thethread); +u32 __lwp_threadqueue_extractproxy(lwp_cntrl *thethread); + +#ifdef LIBOGC_INTERNAL +#include +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/lwp_threads.h b/wii/libogc/include/ogc/lwp_threads.h new file mode 100644 index 0000000000..5b504aef2a --- /dev/null +++ b/wii/libogc/include/ogc/lwp_threads.h @@ -0,0 +1,107 @@ +#ifndef __LWP_THREADS_H__ +#define __LWP_THREADS_H__ + +#include +#include +#include "lwp_states.h" +#include "lwp_tqdata.h" +#include "lwp_watchdog.h" +#include "lwp_objmgr.h" +#include "context.h" + +//#define _LWPTHREADS_DEBUG +#define LWP_TIMESLICE_TIMER_ID 0x00070040 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum +{ + LWP_CPU_BUDGET_ALGO_NONE = 0, + LWP_CPU_BUDGET_ALGO_TIMESLICE +} lwp_cpu_budget_algorithms; + +typedef struct _lwpwaitinfo { + u32 id; + u32 cnt; + void *ret_arg; + void *ret_arg_1; + u32 option; + u32 ret_code; + lwp_queue block2n; + lwp_thrqueue *queue; +} lwp_waitinfo; + +typedef struct _lwpcntrl { + lwp_obj object; + u8 cur_prio,real_prio; + u32 suspendcnt,res_cnt; + u32 isr_level; + u32 cur_state; + u32 cpu_time_budget; + lwp_cpu_budget_algorithms budget_algo; + bool is_preemptible; + lwp_waitinfo wait; + prio_cntrl priomap; + wd_cntrl timer; + + void* (*entry)(void *); + void *arg; + void *stack; + u32 stack_size; + u8 stack_allocated; + lwp_queue *ready; + lwp_thrqueue join_list; + frame_context context; //16 + void *libc_reent; +} lwp_cntrl, *lwp_cntrl_t; + +extern lwp_cntrl *_thr_main; +extern lwp_cntrl *_thr_idle; +extern lwp_cntrl *_thr_executing; +extern lwp_cntrl *_thr_heir; +extern lwp_cntrl *_thr_allocated_fp; +extern vu32 _context_switch_want; +extern vu32 _thread_dispatch_disable_level; + +extern wd_cntrl _lwp_wd_timeslice; +extern void **__lwp_thr_libc_reent; +extern lwp_queue _lwp_thr_ready[]; + +void __thread_dispatch(); +void __lwp_thread_yield(); +void __lwp_thread_closeall(); +void __lwp_thread_setstate(lwp_cntrl *,u32); +void __lwp_thread_clearstate(lwp_cntrl *,u32); +void __lwp_thread_changepriority(lwp_cntrl *,u32,u32); +void __lwp_thread_setpriority(lwp_cntrl *,u32); +void __lwp_thread_settransient(lwp_cntrl *); +void __lwp_thread_suspend(lwp_cntrl *); +void __lwp_thread_resume(lwp_cntrl *,u32); +void __lwp_thread_loadenv(lwp_cntrl *); +void __lwp_thread_ready(lwp_cntrl *); +u32 __lwp_thread_init(lwp_cntrl *,void *,u32,u32,u32,bool); +u32 __lwp_thread_start(lwp_cntrl *,void* (*)(void*),void *); +void __lwp_thread_exit(void *); +void __lwp_thread_close(lwp_cntrl *); +void __lwp_thread_startmultitasking(); +void __lwp_thread_stopmultitasking(void (*exitfunc)()); +lwp_obj* __lwp_thread_getobject(lwp_cntrl *); +u32 __lwp_evaluatemode(); + +u32 __lwp_isr_in_progress(); +void __lwp_thread_resettimeslice(); +void __lwp_rotate_readyqueue(u32); +void __lwp_thread_delayended(void *); +void __lwp_thread_tickle_timeslice(void *); + +#ifdef LIBOGC_INTERNAL +#include +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/lwp_tqdata.h b/wii/libogc/include/ogc/lwp_tqdata.h new file mode 100644 index 0000000000..0ebaffd2d3 --- /dev/null +++ b/wii/libogc/include/ogc/lwp_tqdata.h @@ -0,0 +1,38 @@ +#ifndef __LWP_TQDATA_H__ +#define __LWP_TQDATA_H__ + +#define LWP_THREADQ_NUM_PRIOHEADERS 4 +#define LWP_THREADQ_PRIOPERHEADER 64 +#define LWP_THREADQ_REVERSESEARCHMASK 0x20 + +#define LWP_THREADQ_SYNCHRONIZED 0 +#define LWP_THREADQ_NOTHINGHAPPEND 1 +#define LWP_THREADQ_TIMEOUT 2 +#define LWP_THREADQ_SATISFIED 3 + +#define LWP_THREADQ_MODEFIFO 0 +#define LWP_THREADQ_MODEPRIORITY 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lwp_queue.h" +#include "lwp_priority.h" + +typedef struct _lwpthrqueue { + union { + lwp_queue fifo; + lwp_queue priority[LWP_THREADQ_NUM_PRIOHEADERS]; + } queues; + u32 sync_state; + u32 mode; + u32 state; + u32 timeout_state; +} lwp_thrqueue; + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/lwp_watchdog.h b/wii/libogc/include/ogc/lwp_watchdog.h new file mode 100644 index 0000000000..421373cd25 --- /dev/null +++ b/wii/libogc/include/ogc/lwp_watchdog.h @@ -0,0 +1,107 @@ +#ifndef __LWP_WATCHDOG_H__ +#define __LWP_WATCHDOG_H__ + +#include +#include "lwp_queue.h" +#include + +#if defined(HW_RVL) + #define TB_BUS_CLOCK 243000000u + #define TB_CORE_CLOCK 729000000u +#elif defined(HW_DOL) + #define TB_BUS_CLOCK 162000000u + #define TB_CORE_CLOCK 486000000u +#endif +#define TB_TIMER_CLOCK (TB_BUS_CLOCK/4000) //4th of the bus frequency + +#define TB_SECSPERMIN 60 +#define TB_MINSPERHR 60 +#define TB_MONSPERYR 12 +#define TB_DAYSPERYR 365 +#define TB_HRSPERDAY 24 +#define TB_SECSPERDAY (TB_SECSPERMIN*TB_MINSPERHR*TB_HRSPERDAY) +#define TB_SECSPERNYR (365*TB_SECSPERDAY) + +#define TB_MSPERSEC 1000 +#define TB_USPERSEC 1000000 +#define TB_NSPERSEC 1000000000 +#define TB_NSPERMS 1000000 +#define TB_NSPERUS 1000 +#define TB_USPERTICK 10000 + +#define ticks_to_cycles(ticks) ((((u64)(ticks)*(u64)((TB_CORE_CLOCK*2)/TB_TIMER_CLOCK))/2)) +#define ticks_to_secs(ticks) (((u64)(ticks)/(u64)(TB_TIMER_CLOCK*1000))) +#define ticks_to_millisecs(ticks) (((u64)(ticks)/(u64)(TB_TIMER_CLOCK))) +#define ticks_to_microsecs(ticks) ((((u64)(ticks)*8)/(u64)(TB_TIMER_CLOCK/125))) +#define ticks_to_nanosecs(ticks) ((((u64)(ticks)*8000)/(u64)(TB_TIMER_CLOCK/125))) + +#define tick_microsecs(ticks) ((((u64)(ticks)*8)%(u64)(TB_TIMER_CLOCK/125))) +#define tick_nanosecs(ticks) ((((u64)(ticks)*8000)%(u64)(TB_TIMER_CLOCK/125))) + + +#define secs_to_ticks(sec) ((u64)(sec)*(TB_TIMER_CLOCK*1000)) +#define millisecs_to_ticks(msec) ((u64)(msec)*(TB_TIMER_CLOCK)) +#define microsecs_to_ticks(usec) (((u64)(usec)*(TB_TIMER_CLOCK/125))/8) +#define nanosecs_to_ticks(nsec) (((u64)(nsec)*(TB_TIMER_CLOCK/125))/8000) + +#define diff_ticks(tick0,tick1) (((u64)(tick1)<(u64)(tick0))?((u64)-1-(u64)(tick0)+(u64)(tick1)):((u64)(tick1)-(u64)(tick0))) + +#define LWP_WD_INACTIVE 0 +#define LWP_WD_INSERTED 1 +#define LWP_WD_ACTIVE 2 +#define LWP_WD_REMOVE 3 + +#define LWP_WD_FORWARD 0 +#define LWP_WD_BACKWARD 1 + +#define LWP_WD_NOTIMEOUT 0 + +#define LWP_WD_ABS(x) ((s64)(x)>0?(s64)(x):-((s64)(x))) + +#ifdef __cplusplus +extern "C" { +#endif + +extern vu32 _wd_sync_level; +extern vu32 _wd_sync_count; +extern u32 _wd_ticks_since_boot; + +extern lwp_queue _wd_ticks_queue; + +extern u32 gettick(); +extern u64 gettime(); +extern void settime(u64); + +u32 diff_sec(u64 start,u64 end); +u32 diff_msec(u64 start,u64 end); +u32 diff_usec(u64 start,u64 end); +u32 diff_nsec(u64 start,u64 end); + +typedef void (*wd_service_routine)(void *); + +typedef struct _wdcntrl { + lwp_node node; + u64 start; + u32 id; + u32 state; + u64 fire; + wd_service_routine routine; + void *usr_data; +} wd_cntrl; + +void __lwp_watchdog_init(); +void __lwp_watchdog_settimer(wd_cntrl *wd); +void __lwp_wd_insert(lwp_queue *header,wd_cntrl *wd); +u32 __lwp_wd_remove(lwp_queue *header,wd_cntrl *wd); +void __lwp_wd_tickle(lwp_queue *queue); +void __lwp_wd_adjust(lwp_queue *queue,u32 dir,s64 interval); + +#ifdef LIBOGC_INTERNAL +#include +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/lwp_wkspace.h b/wii/libogc/include/ogc/lwp_wkspace.h new file mode 100644 index 0000000000..c5dd500a23 --- /dev/null +++ b/wii/libogc/include/ogc/lwp_wkspace.h @@ -0,0 +1,23 @@ +#ifndef __LWP_WKSPACE_H__ +#define __LWP_WKSPACE_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern heap_cntrl __wkspace_heap; + +void __lwp_wkspace_init(u32 size); + +#ifdef LIBOGC_INTERNAL +#include +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/machine/asm.h b/wii/libogc/include/ogc/machine/asm.h new file mode 100644 index 0000000000..2feed006f4 --- /dev/null +++ b/wii/libogc/include/ogc/machine/asm.h @@ -0,0 +1,338 @@ +#ifndef __ASM_H__ +#define __ASM_H__ + +#ifdef _LANGUAGE_ASSEMBLY +/* Condition Register Bit Fields */ + +#define cr0 0 +#define cr1 1 +#define cr2 2 +#define cr3 3 +#define cr4 4 +#define cr5 5 +#define cr6 6 +#define cr7 7 + + +/* General Purpose Registers (GPRs) */ + +#define r0 0 +#define r1 1 +#define sp 1 +#define r2 2 +#define toc 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + + +/* Floating Point Registers (FPRs) */ + +#define fr0 0 +#define fr1 1 +#define fr2 2 +#define fr3 3 +#define fr4 4 +#define fr5 5 +#define fr6 6 +#define fr7 7 +#define fr8 8 +#define fr9 9 +#define fr10 10 +#define fr11 11 +#define fr12 12 +#define fr13 13 +#define fr14 14 +#define fr15 15 +#define fr16 16 +#define fr17 17 +#define fr18 18 +#define fr19 19 +#define fr20 20 +#define fr21 21 +#define fr22 22 +#define fr23 23 +#define fr24 24 +#define fr25 25 +#define fr26 26 +#define fr27 27 +#define fr28 28 +#define fr29 29 +#define fr30 30 +#define fr31 31 + +#define vr0 0 +#define vr1 1 +#define vr2 2 +#define vr3 3 +#define vr4 4 +#define vr5 5 +#define vr6 6 +#define vr7 7 +#define vr8 8 +#define vr9 9 +#define vr10 10 +#define vr11 11 +#define vr12 12 +#define vr13 13 +#define vr14 14 +#define vr15 15 +#define vr16 16 +#define vr17 17 +#define vr18 18 +#define vr19 19 +#define vr20 20 +#define vr21 21 +#define vr22 22 +#define vr23 23 +#define vr24 24 +#define vr25 25 +#define vr26 26 +#define vr27 27 +#define vr28 28 +#define vr29 29 +#define vr30 30 +#define vr31 31 + +#endif //_LANGUAGE_ASSEMBLY + +#define SPRG0 272 +#define SPRG1 273 +#define SPRG2 274 +#define SPRG3 275 + +#define PMC1 953 +#define PMC2 954 +#define PMC3 957 +#define PMC4 958 + +#define MMCR0 952 +#define MMCR1 956 + + +#define LINK_REGISTER_CALLEE_UPDATE_ROOM 4 +#define EXCEPTION_NUMBER 8 +#define SRR0_OFFSET 12 +#define SRR1_OFFSET 16 +#define GPR0_OFFSET 20 +#define GPR1_OFFSET 24 +#define GPR2_OFFSET 28 +#define GPR3_OFFSET 32 +#define GPR4_OFFSET 36 +#define GPR5_OFFSET 40 +#define GPR6_OFFSET 44 +#define GPR7_OFFSET 48 +#define GPR8_OFFSET 52 +#define GPR9_OFFSET 56 +#define GPR10_OFFSET 60 +#define GPR11_OFFSET 64 +#define GPR12_OFFSET 68 +#define GPR13_OFFSET 72 +#define GPR14_OFFSET 76 +#define GPR15_OFFSET 80 +#define GPR16_OFFSET 84 +#define GPR17_OFFSET 88 +#define GPR18_OFFSET 92 +#define GPR19_OFFSET 96 +#define GPR20_OFFSET 100 +#define GPR21_OFFSET 104 +#define GPR22_OFFSET 108 +#define GPR23_OFFSET 112 +#define GPR24_OFFSET 116 +#define GPR25_OFFSET 120 +#define GPR26_OFFSET 124 +#define GPR27_OFFSET 128 +#define GPR28_OFFSET 132 +#define GPR29_OFFSET 136 +#define GPR30_OFFSET 140 +#define GPR31_OFFSET 144 + +#define GQR0_OFFSET 148 +#define GQR1_OFFSET 152 +#define GQR2_OFFSET 156 +#define GQR3_OFFSET 160 +#define GQR4_OFFSET 164 +#define GQR5_OFFSET 168 +#define GQR6_OFFSET 172 +#define GQR7_OFFSET 176 + +#define CR_OFFSET 180 +#define LR_OFFSET 184 +#define CTR_OFFSET 188 +#define XER_OFFSET 192 +#define MSR_OFFSET 196 +#define DAR_OFFSET 200 + +#define STATE_OFFSET 204 +#define MODE_OFFSET 206 + +#define FPR0_OFFSET 208 +#define FPR1_OFFSET 216 +#define FPR2_OFFSET 224 +#define FPR3_OFFSET 232 +#define FPR4_OFFSET 240 +#define FPR5_OFFSET 248 +#define FPR6_OFFSET 256 +#define FPR7_OFFSET 264 +#define FPR8_OFFSET 272 +#define FPR9_OFFSET 280 +#define FPR10_OFFSET 288 +#define FPR11_OFFSET 296 +#define FPR12_OFFSET 304 +#define FPR13_OFFSET 312 +#define FPR14_OFFSET 320 +#define FPR15_OFFSET 328 +#define FPR16_OFFSET 336 +#define FPR17_OFFSET 344 +#define FPR18_OFFSET 352 +#define FPR19_OFFSET 360 +#define FPR20_OFFSET 368 +#define FPR21_OFFSET 376 +#define FPR22_OFFSET 384 +#define FPR23_OFFSET 392 +#define FPR24_OFFSET 400 +#define FPR25_OFFSET 408 +#define FPR26_OFFSET 416 +#define FPR27_OFFSET 424 +#define FPR28_OFFSET 432 +#define FPR29_OFFSET 440 +#define FPR30_OFFSET 448 +#define FPR31_OFFSET 456 + +#define FPSCR_OFFSET 464 + +#define PSR0_OFFSET 472 +#define PSR1_OFFSET 480 +#define PSR2_OFFSET 488 +#define PSR3_OFFSET 496 +#define PSR4_OFFSET 504 +#define PSR5_OFFSET 512 +#define PSR6_OFFSET 520 +#define PSR7_OFFSET 528 +#define PSR8_OFFSET 536 +#define PSR9_OFFSET 544 +#define PSR10_OFFSET 552 +#define PSR11_OFFSET 560 +#define PSR12_OFFSET 568 +#define PSR13_OFFSET 576 +#define PSR14_OFFSET 584 +#define PSR15_OFFSET 592 +#define PSR16_OFFSET 600 +#define PSR17_OFFSET 608 +#define PSR18_OFFSET 616 +#define PSR19_OFFSET 624 +#define PSR20_OFFSET 632 +#define PSR21_OFFSET 640 +#define PSR22_OFFSET 648 +#define PSR23_OFFSET 656 +#define PSR24_OFFSET 664 +#define PSR25_OFFSET 672 +#define PSR26_OFFSET 680 +#define PSR27_OFFSET 688 +#define PSR28_OFFSET 696 +#define PSR29_OFFSET 704 +#define PSR30_OFFSET 712 +#define PSR31_OFFSET 720 +/* + * maintain the EABI requested 8 bytes aligment + * As SVR4 ABI requires 16, make it 16 (as some + * exception may need more registers to be processed...) + */ +#define EXCEPTION_FRAME_END 728 + +#define IBAT0U 528 +#define IBAT0L 529 +#define IBAT1U 530 +#define IBAT1L 531 +#define IBAT2U 532 +#define IBAT2L 533 +#define IBAT3U 534 +#define IBAT3L 535 +#define IBAT4U 560 +#define IBAT4L 561 +#define IBAT5U 562 +#define IBAT5L 563 +#define IBAT6U 564 +#define IBAT6L 565 +#define IBAT7U 566 +#define IBAT7L 567 + +#define DBAT0U 536 +#define DBAT0L 537 +#define DBAT1U 538 +#define DBAT1L 539 +#define DBAT2U 540 +#define DBAT2L 541 +#define DBAT3U 542 +#define DBAT3L 543 +#define DBAT4U 568 +#define DBAT4L 569 +#define DBAT5U 570 +#define DBAT5L 571 +#define DBAT6U 572 +#define DBAT6L 573 +#define DBAT7U 574 +#define DBAT7L 575 + +#define HID0 1008 +#define HID1 1009 +#define HID2 920 +#define HID4 1011 + +#define GQR0 912 +#define GQR1 913 +#define GQR2 914 +#define GQR3 915 +#define GQR4 916 +#define GQR5 917 +#define GQR6 918 +#define GQR7 919 + +#define L2CR 1017 + +#define WPAR 921 + +#define DMAU 922 +#define DMAL 923 + +#define MSR_RI 0x00000002 +#define MSR_DR 0x00000010 +#define MSR_IR 0x00000020 +#define MSR_IP 0x00000040 +#define MSR_SE 0x00000400 +#define MSR_ME 0x00001000 +#define MSR_FP 0x00002000 +#define MSR_POW 0x00004000 +#define MSR_EE 0x00008000 + +#define PPC_ALIGNMENT 8 + +#define PPC_CACHE_ALIGNMENT 32 + +#endif //__ASM_H__ diff --git a/wii/libogc/include/ogc/machine/processor.h b/wii/libogc/include/ogc/machine/processor.h new file mode 100644 index 0000000000..f8693927ac --- /dev/null +++ b/wii/libogc/include/ogc/machine/processor.h @@ -0,0 +1,251 @@ +#ifndef __PROCESSOR_H__ +#define __PROCESSOR_H__ + +#include +#include "asm.h" + +#define __stringify(rn) #rn +#define ATTRIBUTE_ALIGN(v) __attribute__((aligned(v))) +// courtesy of Marcan +#define STACK_ALIGN(type, name, cnt, alignment) u8 _al__##name[((sizeof(type)*(cnt)) + (alignment) + (((sizeof(type)*(cnt))%(alignment)) > 0 ? ((alignment) - ((sizeof(type)*(cnt))%(alignment))) : 0))]; \ + type *name = (type*)(((u32)(_al__##name)) + ((alignment) - (((u32)(_al__##name))&((alignment)-1)))) + +#define _sync() asm volatile("sync") +#define _nop() asm volatile("nop") +#define ppcsync() asm volatile("sc") +#define ppchalt() ({ \ + asm volatile("sync"); \ + while(1) { \ + asm volatile("nop"); \ + asm volatile("li 3,0"); \ + asm volatile("nop"); \ + } \ +}) + +#define mfpvr() ({register u32 _rval; \ + asm volatile("mfpvr %0" : "=r"(_rval)); _rval;}) + +#define mfdcr(_rn) ({register u32 _rval; \ + asm volatile("mfdcr %0," __stringify(_rn) \ + : "=r" (_rval)); _rval;}) +#define mtdcr(rn, val) asm volatile("mtdcr " __stringify(rn) ",%0" : : "r" (val)) + +#define mfmsr() ({register u32 _rval; \ + asm volatile("mfmsr %0" : "=r" (_rval)); _rval;}) +#define mtmsr(val) asm volatile("mtmsr %0" : : "r" (val)) + +#define mfdec() ({register u32 _rval; \ + asm volatile("mfdec %0" : "=r" (_rval)); _rval;}) +#define mtdec(_val) asm volatile("mtdec %0" : : "r" (_val)) + +#define mfspr(_rn) \ +({ register u32 _rval = 0; \ + asm volatile("mfspr %0," __stringify(_rn) \ + : "=r" (_rval));\ + _rval; \ +}) + +#define mtspr(_rn, _val) asm volatile("mtspr " __stringify(_rn) ",%0" : : "r" (_val)) + +#define mfwpar() mfspr(WPAR) +#define mtwpar(_val) mtspr(WPAR,_val) + +#define mfmmcr0() mfspr(MMCR0) +#define mtmmcr0(_val) mtspr(MMCR0,_val) +#define mfmmcr1() mfspr(MMCR1) +#define mtmmcr1(_val) mtspr(MMCR1,_val) + +#define mfpmc1() mfspr(PMC1) +#define mtpmc1(_val) mtspr(PMC1,_val) +#define mfpmc2() mfspr(PMC2) +#define mtpmc2(_val) mtspr(PMC2,_val) +#define mfpmc3() mfspr(PMC3) +#define mtpmc3(_val) mtspr(PMC3,_val) +#define mfpmc4() mfspr(PMC4) +#define mtpmc4(_val) mtspr(PMC4,_val) + +#define mfhid0() mfspr(HID0) +#define mthid0(_val) mtspr(HID0,_val) +#define mfhid1() mfspr(HID1) +#define mthid1(_val) mtspr(HID1,_val) +#define mfhid2() mfspr(HID2) +#define mthid2(_val) mtspr(HID2,_val) +#define mfhid4() mfspr(HID4) +#define mthid4(_val) mtspr(HID4,_val) + +#define __lhbrx(base,index) \ +({ register u16 res; \ + __asm__ volatile ("lhbrx %0,%1,%2" : "=r"(res) : "b%"(index), "r"(base) : "memory"); \ + res; }) + +#define __lwbrx(base,index) \ +({ register u32 res; \ + __asm__ volatile ("lwbrx %0,%1,%2" : "=r"(res) : "b%"(index), "r"(base) : "memory"); \ + res; }) + +#define __sthbrx(base,index,value) \ + __asm__ volatile ("sthbrx %0,%1,%2" : : "r"(value), "b%"(index), "r"(base) : "memory") + +#define __stwbrx(base,index,value) \ + __asm__ volatile ("stwbrx %0,%1,%2" : : "r"(value), "b%"(index), "r"(base) : "memory") + +#define cntlzw(_val) ({register u32 _rval; \ + asm volatile("cntlzw %0, %1" : "=r"((_rval)) : "r"((_val))); _rval;}) + +#define _CPU_MSR_GET( _msr_value ) \ + do { \ + _msr_value = 0; \ + asm volatile ("mfmsr %0" : "=&r" ((_msr_value)) : "0" ((_msr_value))); \ + } while (0) + +#define _CPU_MSR_SET( _msr_value ) \ +{ asm volatile ("mtmsr %0" : "=&r" ((_msr_value)) : "0" ((_msr_value))); } + +#define _CPU_ISR_Enable() \ + { register u32 _val = 0; \ + __asm__ __volatile__ ( \ + "mfmsr %0\n" \ + "ori %0,%0,0x8000\n" \ + "mtmsr %0" \ + : "=&r" ((_val)) : "0" ((_val)) \ + ); \ + } + +#define _CPU_ISR_Disable( _isr_cookie ) \ + { register u32 _disable_mask = 0; \ + _isr_cookie = 0; \ + __asm__ __volatile__ ( \ + "mfmsr %0\n" \ + "rlwinm %1,%0,0,17,15\n" \ + "mtmsr %1\n" \ + "extrwi %0,%0,1,16" \ + : "=&r" ((_isr_cookie)), "=&r" ((_disable_mask)) \ + : "0" ((_isr_cookie)), "1" ((_disable_mask)) \ + ); \ + } + +#define _CPU_ISR_Restore( _isr_cookie ) \ + { register u32 _enable_mask = 0; \ + __asm__ __volatile__ ( \ + " cmpwi %0,0\n" \ + " beq 1f\n" \ + " mfmsr %1\n" \ + " ori %1,%1,0x8000\n" \ + " mtmsr %1\n" \ + "1:" \ + : "=r"((_isr_cookie)),"=&r" ((_enable_mask)) \ + : "0"((_isr_cookie)),"1" ((_enable_mask)) \ + ); \ + } + +#define _CPU_ISR_Flash( _isr_cookie ) \ + { register u32 _flash_mask = 0; \ + __asm__ __volatile__ ( \ + " cmpwi %0,0\n" \ + " beq 1f\n" \ + " mfmsr %1\n" \ + " ori %1,%1,0x8000\n" \ + " mtmsr %1\n" \ + " rlwinm %1,%1,0,17,15\n" \ + " mtmsr %1\n" \ + "1:" \ + : "=r" ((_isr_cookie)), "=&r" ((_flash_mask)) \ + : "0" ((_isr_cookie)), "1" ((_flash_mask)) \ + ); \ + } + +#define _CPU_FPR_Enable() \ +{ register u32 _val = 0; \ + asm volatile ("mfmsr %0; ori %0,%0,0x2000; mtmsr %0" : \ + "=&r" (_val) : "0" (_val));\ +} + +#define _CPU_FPR_Disable() \ +{ register u32 _val = 0; \ + asm volatile ("mfmsr %0; rlwinm %0,%0,0,19,17; mtmsr %0" : \ + "=&r" (_val) : "0" (_val));\ +} + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +static inline u16 bswap16(u16 val) +{ + u16 tmp = val; + return __lhbrx(&tmp,0); +} + +static inline u32 bswap32(u32 val) +{ + u32 tmp = val; + return __lwbrx(&tmp,0); +} + +static inline u64 bswap64(u64 val) +{ + union ullc { + u64 ull; + u32 ul[2]; + } outv; + u64 tmp = val; + + outv.ul[0] = __lwbrx(&tmp,4); + outv.ul[1] = __lwbrx(&tmp,0); + + return outv.ull; +} + +// Basic I/O + +static inline u32 read32(u32 addr) +{ + u32 x; + asm volatile("lwz %0,0(%1) ; sync" : "=r"(x) : "b"(0xc0000000 | addr)); + return x; +} + +static inline void write32(u32 addr, u32 x) +{ + asm("stw %0,0(%1) ; eieio" : : "r"(x), "b"(0xc0000000 | addr)); +} + +static inline void mask32(u32 addr, u32 clear, u32 set) +{ + write32(addr, (read32(addr)&(~clear)) | set); +} + +static inline u16 read16(u32 addr) +{ + u16 x; + asm volatile("lhz %0,0(%1) ; sync" : "=r"(x) : "b"(0xc0000000 | addr)); + return x; +} + +static inline void write16(u32 addr, u16 x) +{ + asm("sth %0,0(%1) ; eieio" : : "r"(x), "b"(0xc0000000 | addr)); +} + +static inline u8 read8(u32 addr) +{ + u8 x; + asm volatile("lbz %0,0(%1) ; sync" : "=r"(x) : "b"(0xc0000000 | addr)); + return x; +} + +static inline void write8(u32 addr, u8 x) +{ + asm("stb %0,0(%1) ; eieio" : : "r"(x), "b"(0xc0000000 | addr)); +} + +static inline void writef32(u32 addr, f32 x) +{ + asm("stfs %0,0(%1) ; eieio" : : "f"(x), "b"(0xc0000000 | addr)); +} + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/machine/spinlock.h b/wii/libogc/include/ogc/machine/spinlock.h new file mode 100644 index 0000000000..1d6022a2fb --- /dev/null +++ b/wii/libogc/include/ogc/machine/spinlock.h @@ -0,0 +1,191 @@ +#ifndef __SPINLOCK_H__ +#define __SPINLOCK_H__ + +#include +#include + +typedef struct { + vu32 lock; +} spinlock_t; + +#define SPIN_LOCK_UNLOCKED (spinlock_t){0} + +#define spin_lock_init(x) do { *(x) = SPIN_LOCK_UNLOCKED; }while(0) + +static __inline__ u32 _test_and_set(u32 *atomic) +{ + register u32 ret; + + __asm__ __volatile__ ("1: lwarx %0,0,%1\n" + " cmpwi 0,%0,0\n" + " bne- 2f\n" + " stwcx. %2,0,%1\n" + " bne- 1b\n" + " isync\n" + "2:" : "=&r"(ret) + : "r"(atomic), "r"(1) + : "cr0", "memory"); + + return ret; +} + +static __inline__ u32 atomic_inc(u32 *pint) +{ + register u32 ret; + __asm__ __volatile__( + "1: lwarx %0,0,%1\n\ + addi %0,%0,1\n\ + stwcx. %0,0,%1\n\ + bne- 1b\n\ + isync\n" + : "=&r"(ret) : "r"(pint) + : "cr0", "memory"); + return ret; +} + +static __inline__ u32 atomic_dec(u32 *pint) +{ + register u32 ret; + __asm__ __volatile__( + "1: lwarx %0,0,%1\n\ + addi %0,%0,-1\n\ + stwcx. %0,0,%1\n\ + bne- 1b\n\ + isync\n" + : "=&r"(ret) : "r"(pint) + : "cr0", "memory"); + return ret; +} + +static __inline__ void spin_lock(spinlock_t *lock) +{ + register u32 tmp; + + __asm__ __volatile__( + "b 1f # spin_lock\n\ +2: lwzx %0,0,%1\n\ + cmpwi 0,%0,0\n\ + bne+ 2b\n\ +1: lwarx %0,0,%1\n\ + cmpwi 0,%0,0\n\ + bne- 2b\n\ + stwcx. %2,0,%1\n\ + bne- 2b\n\ + isync" + : "=&r"(tmp) + : "r"(lock), "r"(1) + : "cr0", "memory"); +} + +static __inline__ void spin_lock_irqsave(spinlock_t *lock,register u32 *p_isr_level) +{ + register u32 level; + register u32 tmp; + + _CPU_ISR_Disable(level); + + __asm__ __volatile__( + " b 1f # spin_lock\n\ + 2: lwzx %0,0,%1\n\ + cmpwi 0,%0,0\n\ + bne+ 2b\n\ + 1: lwarx %0,0,%1\n\ + cmpwi 0,%0,0\n\ + bne- 2b\n\ + stwcx. %2,0,%1\n\ + bne- 2b\n\ + isync" + : "=&r"(tmp) + : "r"(lock), "r"(1) + : "cr0", "memory"); + + *p_isr_level = level; +} + +static __inline__ void spin_unlock(spinlock_t *lock) +{ + __asm__ __volatile__("eieio # spin_unlock": : :"memory"); + lock->lock = 0; +} + +static __inline__ void spin_unlock_irqrestore(spinlock_t *lock,register u32 isr_level) +{ + __asm__ __volatile__( + "eieio # spin_unlock" + : : :"memory"); + lock->lock = 0; + + _CPU_ISR_Restore(isr_level); +} + +typedef struct { + vu32 lock; +} rwlock_t; + +#define RW_LOCK_UNLOCKED (rwlock_t){0} + +#define read_lock_init(lp) do { *(lp) = RW_LOCK_UNLOCKED; }while(0) + +static __inline__ void read_lock(rwlock_t *rw) +{ + register u32 tmp; + + __asm__ __volatile__( + "b 2f # read_lock\n\ +1: lwzx %0,0,%1\n\ + cmpwi 0,%0,0\n\ + blt+ 1b\n\ +2: lwarx %0,0,%1\n\ + addic. %0,%0,1\n\ + ble- 1b\n\ + stwcx. %0,0,%1\n\ + bne- 2b\n\ + isync" + : "=&r"(tmp) + : "r"(&rw->lock) + : "cr0", "memory"); +} + +static __inline__ void read_unlock(rwlock_t *rw) +{ + register u32 tmp; + + __asm__ __volatile__( + "eieio # read_unlock\n\ +1: lwarx %0,0,%1\n\ + addic %0,%0,-1\n\ + stwcx. %0,0,%1\n\ + bne- 1b" + : "=&r"(tmp) + : "r"(&rw->lock) + : "cr0", "memory"); +} + +static __inline__ void write_lock(rwlock_t *rw) +{ + register u32 tmp; + + __asm__ __volatile__( + "b 2f # write_lock\n\ +1: lwzx %0,0,%1\n\ + cmpwi 0,%0,0\n\ + bne+ 1b\n\ +2: lwarx %0,0,%1\n\ + cmpwi 0,%0,0\n\ + bne- 1b\n\ + stwcx. %2,0,%1\n\ + bne- 2b\n\ + isync" + : "=&r"(tmp) + : "r"(&rw->lock), "r"(-1) + : "cr0", "memory"); +} + +static __inline__ void write_unlock(rwlock_t *rw) +{ + __asm__ __volatile__("eieio # write_unlock": : :"memory"); + rw->lock = 0; +} + + +#endif diff --git a/wii/libogc/include/ogc/message.h b/wii/libogc/include/ogc/message.h new file mode 100644 index 0000000000..445b1bc4d6 --- /dev/null +++ b/wii/libogc/include/ogc/message.h @@ -0,0 +1,123 @@ +/*------------------------------------------------------------- + +message.h -- Thread subsystem II + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#ifndef __MESSAGE_H__ +#define __MESSAGE_H__ + +/*! \file message.h +\brief Thread subsystem II + +*/ + +#include + +#define MQ_BOX_NULL 0xffffffff + +#define MQ_ERROR_SUCCESSFUL 0 +#define MQ_ERROR_TOOMANY -5 + +#define MQ_MSG_BLOCK 0 +#define MQ_MSG_NOBLOCK 1 + + +#ifdef __cplusplus +extern "C" { +#endif + + +/*! \typedef u32 mqbox_t +\brief typedef for the message queue handle +*/ +typedef u32 mqbox_t; + + +/*! \typedef void* mqmsg_t +\brief typedef for the message pointer +*/ +typedef void* mqmsg_t; + + + +/*! \fn u32 MQ_Init(mqbox_t *mqbox,u32 count) +\brief Initializes a message queue +\param[out] mqbox pointer to the mqbox_t handle. +\param[in] count maximum number of messages the queue can hold + +\return 0 on success, <0 on error +*/ +s32 MQ_Init(mqbox_t *mqbox,u32 count); + + +/*! \fn void MQ_Close(mqbox_t mqbox) +\brief Closes the message queue and releases all memory. +\param[in] mqbox handle to the mqbox_t structure. + +\return none +*/ +void MQ_Close(mqbox_t mqbox); + + +/*! \fn BOOL MQ_Send(mqbox_t mqbox,mqmsg_t msg,u32 flags) +\brief Sends a message to the given message queue. +\param[in] mqbox mqbox_t handle to the message queue +\param[in] msg message to send +\param[in] flags message flags (MQ_MSG_BLOCK, MQ_MSG_NOBLOCK) + +\return bool result +*/ +BOOL MQ_Send(mqbox_t mqbox,mqmsg_t msg,u32 flags); + + +/*! \fn BOOL MQ_Jam(mqbox_t mqbox,mqmsg_t msg,u32 flags) +\brief Sends a message to the given message queue and jams it in front of the queue. +\param[in] mqbox mqbox_t handle to the message queue +\param[in] msg message to send +\param[in] flags message flags (MQ_MSG_BLOCK, MQ_MSG_NOBLOCK) + +\return bool result +*/ +BOOL MQ_Jam(mqbox_t mqbox,mqmsg_t msg,u32 flags); + + +/*! \fn BOOL MQ_Receive(mqbox_t mqbox,mqmsg_t *msg,u32 flags) +\brief Sends a message to the given message queue. +\param[in] mqbox mqbox_t handle to the message queue +\param[in] msg pointer to a mqmsg_t_t-type message to receive. +\param[in] flags message flags (MQ_MSG_BLOCK, MQ_MSG_NOBLOCK) + +\return bool result +*/ +BOOL MQ_Receive(mqbox_t mqbox,mqmsg_t *msg,u32 flags); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/mutex.h b/wii/libogc/include/ogc/mutex.h new file mode 100644 index 0000000000..4faaf23926 --- /dev/null +++ b/wii/libogc/include/ogc/mutex.h @@ -0,0 +1,103 @@ +/*------------------------------------------------------------- + +mutex.h -- Thread subsystem III + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#ifndef __MUTEX_H__ +#define __MUTEX_H__ + +/*! \file mutex.h +\brief Thread subsystem III + +*/ + +#include + +#define LWP_MUTEX_NULL 0xffffffff + +#ifdef __cplusplus + extern "C" { +#endif + + +/*! \typedef u32 mutex_t +\brief typedef for the mutex handle +*/ +typedef u32 mutex_t; + + +/*! \fn s32 LWP_MutexInit(mutex_t *mutex,bool use_recursive) +\brief Initializes a mutex lock. +\param[out] mutex pointer to a mutex_t handle. +\param[in] use_recursive whether to allow the thread, whithin the same context, to enter multiple times the lock or not. + +\return 0 on success, <0 on error +*/ +s32 LWP_MutexInit(mutex_t *mutex,bool use_recursive); + + +/*! \fn s32 LWP_MutexDestroy(mutex_t mutex) +\brief Close mutex lock, release all threads and handles locked on this mutex. +\param[in] mutex handle to the mutex_t structure. + +\return 0 on success, <0 on error +*/ +s32 LWP_MutexDestroy(mutex_t mutex); + + +/*! \fn s32 LWP_MutexLock(mutex_t mutex) +\brief Enter the mutex lock. +\param[in] mutex handle to the mutext_t structure. + +\return 0 on success, <0 on error +*/ +s32 LWP_MutexLock(mutex_t mutex); + + +/*! \fn s32 LWP_MutexTryLock(mutex_t mutex) +\brief Try to enter the mutex lock. +\param[in] mutex handle to the mutex_t structure. + +\return 0: on first aquire, 1: would lock +*/ +s32 LWP_MutexTryLock(mutex_t mutex); + + +/*! \fn s32 LWP_MutexUnlock(mutex_t mutex) +\brief Release the mutex lock and let other threads process further on this mutex. +\param[in] mutex handle to the mutex_t structure. + +\return 0 on success, <0 on error +*/ +s32 LWP_MutexUnlock(mutex_t mutex); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/pad.h b/wii/libogc/include/ogc/pad.h new file mode 100644 index 0000000000..3488c5c5b4 --- /dev/null +++ b/wii/libogc/include/ogc/pad.h @@ -0,0 +1,97 @@ +#ifndef __PAD_H__ +#define __PAD_H__ + +#include + +#define PAD_CHAN0 0 +#define PAD_CHAN1 1 +#define PAD_CHAN2 2 +#define PAD_CHAN3 3 +#define PAD_CHANMAX 4 + +#define PAD_MOTOR_STOP 0 +#define PAD_MOTOR_RUMBLE 1 +#define PAD_MOTOR_STOP_HARD 2 + +#define PAD_ERR_NONE 0 +#define PAD_ERR_NO_CONTROLLER -1 +#define PAD_ERR_NOT_READY -2 +#define PAD_ERR_TRANSFER -3 + +#define PAD_BUTTON_LEFT 0x0001 +#define PAD_BUTTON_RIGHT 0x0002 +#define PAD_BUTTON_DOWN 0x0004 +#define PAD_BUTTON_UP 0x0008 +#define PAD_TRIGGER_Z 0x0010 +#define PAD_TRIGGER_R 0x0020 +#define PAD_TRIGGER_L 0x0040 +#define PAD_BUTTON_A 0x0100 +#define PAD_BUTTON_B 0x0200 +#define PAD_BUTTON_X 0x0400 +#define PAD_BUTTON_Y 0x0800 +#define PAD_BUTTON_MENU 0x1000 +#define PAD_BUTTON_START 0x1000 + +#define PAD_CHAN0_BIT 0x80000000 +#define PAD_CHAN1_BIT 0x40000000 +#define PAD_CHAN2_BIT 0x20000000 +#define PAD_CHAN3_BIT 0x10000000 +/*+----------------------------------------------------------------------------------------------+*/ +/*+----------------------------------------------------------------------------------------------+*/ +/*+----------------------------------------------------------------------------------------------+*/ +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ +/*+----------------------------------------------------------------------------------------------+*/ +typedef struct _padstatus { + u16 button; + s8 stickX; + s8 stickY; + s8 substickX; + s8 substickY; + u8 triggerL; + u8 triggerR; + u8 analogA; + u8 analogB; + s8 err; +} PADStatus; + +typedef void (*sampling_callback)(void); +/*+----------------------------------------------------------------------------------------------+*/ +/*+----------------------------------------------------------------------------------------------+*/ +/*+----------------------------------------------------------------------------------------------+*/ + +u32 PAD_Init(); +u32 PAD_Sync(); +u32 PAD_Read(PADStatus *status); +u32 PAD_Reset(u32 mask); +u32 PAD_Recalibrate(u32 mask); +void PAD_Clamp(PADStatus *status); +void PAD_ControlMotor(s32 chan,u32 cmd); +void PAD_SetSpec(u32 spec); + +u32 PAD_ScanPads(); + +u16 PAD_ButtonsUp(int pad); +u16 PAD_ButtonsDown(int pad); +u16 PAD_ButtonsHeld(int pad); + +s8 PAD_SubStickX(int pad); +s8 PAD_SubStickY(int pad); + +s8 PAD_StickX(int pad); +s8 PAD_StickY(int pad); + +u8 PAD_TriggerL(int pad); +u8 PAD_TriggerR(int pad); + + +sampling_callback PAD_SetSamplingCallback(sampling_callback cb); + +/*+----------------------------------------------------------------------------------------------+*/ + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/semaphore.h b/wii/libogc/include/ogc/semaphore.h new file mode 100644 index 0000000000..c7ba6c6d0e --- /dev/null +++ b/wii/libogc/include/ogc/semaphore.h @@ -0,0 +1,96 @@ +/*------------------------------------------------------------- + +semaphore.h -- Thread subsystem IV + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#ifndef __SEMAPHORE_H__ +#define __SEMAPHORE_H__ + +/*! \file semaphore.h +\brief Thread subsystem IV + +*/ + + +#include + +#define LWP_SEM_NULL 0xffffffff + +#ifdef __cplusplus +extern "C" { +#endif + + +/*! \typedef u32 sem_t +\brief typedef for the semaphore handle +*/ +typedef u32 sem_t; + + +/*! \fn s32 LWP_SemInit(sem_t *sem,u32 start,u32 max) +\brief Initializes a semaphore. +\param[out] sem pointer to a sem_t handle. +\param[in] start start count of the semaphore +\param[in] max maximum count of the semaphore + +\return 0 on success, <0 on error +*/ +s32 LWP_SemInit(sem_t *sem,u32 start,u32 max); + + +/*! \fn s32 LWP_SemDestroy(sem_t sem) +\brief Close and destroy a semaphore, release all threads and handles locked on this semaphore. +\param[in] sem handle to the sem_t structure. + +\return 0 on success, <0 on error +*/ +s32 LWP_SemDestroy(sem_t sem); + + +/*! \fn s32 LWP_SemWait(sem_t sem) +\brief Count down semaphore counter and enter lock if counter <=0 +\param[in] sem handle to the sem_t structure. + +\return 0 on success, <0 on error +*/ +s32 LWP_SemWait(sem_t sem); + + +/*! \fn s32 LWP_SemPost(sem_t sem) +\brief Count up semaphore counter and release lock if counter >0 +\param[in] sem handle to the sem_t structure. + +\return 0 on success, <0 on error +*/ +s32 LWP_SemPost(sem_t sem); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/si.h b/wii/libogc/include/ogc/si.h new file mode 100644 index 0000000000..df40c81709 --- /dev/null +++ b/wii/libogc/include/ogc/si.h @@ -0,0 +1,95 @@ +#ifndef __SI_H__ +#define __SI_H__ + +#include + +#define SI_CHAN0 0 +#define SI_CHAN1 1 +#define SI_CHAN2 2 +#define SI_CHAN3 3 +#define SI_MAX_CHAN 4 + +#define SI_CHAN0_BIT 0x80000000 +#define SI_CHAN1_BIT 0x40000000 +#define SI_CHAN2_BIT 0x20000000 +#define SI_CHAN3_BIT 0x10000000 +#define SI_CHAN_BIT(chn) (SI_CHAN0_BIT>>(chn)) + +#define SI_ERROR_UNDER_RUN 0x0001 +#define SI_ERROR_OVER_RUN 0x0002 +#define SI_ERROR_COLLISION 0x0004 +#define SI_ERROR_NO_RESPONSE 0x0008 +#define SI_ERROR_WRST 0x0010 +#define SI_ERROR_RDST 0x0020 +#define SI_ERR_UNKNOWN 0x0040 +#define SI_ERR_BUSY 0x0080 + +// +// CMD_TYPE_AND_STATUS response data +// +#define SI_TYPE_MASK 0x18000000u +#define SI_TYPE_N64 0x00000000u +#define SI_TYPE_DOLPHIN 0x08000000u +#define SI_TYPE_GC SI_TYPE_DOLPHIN + +// GameCube specific +#define SI_GC_WIRELESS 0x80000000u +#define SI_GC_NOMOTOR 0x20000000u // no rumble motor +#define SI_GC_STANDARD 0x01000000u // dolphin standard controller + +// WaveBird specific +#define SI_WIRELESS_RECEIVED 0x40000000u // 0: no wireless unit +#define SI_WIRELESS_IR 0x04000000u // 0: IR 1: RF +#define SI_WIRELESS_STATE 0x02000000u // 0: variable 1: fixed +#define SI_WIRELESS_ORIGIN 0x00200000u // 0: invalid 1: valid +#define SI_WIRELESS_FIX_ID 0x00100000u // 0: not fixed 1: fixed +#define SI_WIRELESS_TYPE 0x000f0000u +#define SI_WIRELESS_LITE_MASK 0x000c0000u // 0: normal 1: lite controller +#define SI_WIRELESS_LITE 0x00040000u // 0: normal 1: lite controller +#define SI_WIRELESS_CONT_MASK 0x00080000u // 0: non-controller 1: non-controller +#define SI_WIRELESS_CONT 0x00000000u +#define SI_WIRELESS_ID 0x00c0ff00u +#define SI_WIRELESS_TYPE_ID (SI_WIRELESS_TYPE | SI_WIRELESS_ID) + +#define SI_N64_CONTROLLER (SI_TYPE_N64 | 0x05000000) +#define SI_N64_MIC (SI_TYPE_N64 | 0x00010000) +#define SI_N64_KEYBOARD (SI_TYPE_N64 | 0x00020000) +#define SI_N64_MOUSE (SI_TYPE_N64 | 0x02000000) +#define SI_GBA (SI_TYPE_N64 | 0x00040000) +#define SI_GC_CONTROLLER (SI_TYPE_GC | SI_GC_STANDARD) +#define SI_GC_RECEIVER (SI_TYPE_GC | SI_GC_WIRELESS) +#define SI_GC_WAVEBIRD (SI_TYPE_GC | SI_GC_WIRELESS | SI_GC_STANDARD | SI_WIRELESS_STATE | SI_WIRELESS_FIX_ID) +#define SI_GC_KEYBOARD (SI_TYPE_GC | 0x00200000) +#define SI_GC_STEERING (SI_TYPE_GC | 0x00000000) + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef void (*SICallback)(s32,u32); +typedef void (*RDSTHandler)(u32,void*); + +u32 SI_Sync(); +u32 SI_Busy(); +u32 SI_IsChanBusy(s32 chan); +void SI_EnablePolling(u32 poll); +void SI_DisablePolling(u32 poll); +void SI_SetCommand(s32 chan,u32 cmd); +u32 SI_GetStatus(s32 chan); +u32 SI_GetResponse(s32 chan,void *buf); +u32 SI_GetResponseRaw(s32 chan); +void SI_RefreshSamplingRate(); +u32 SI_Transfer(s32 chan,void *out,u32 out_len,void *in,u32 in_len,SICallback cb,u32 us_delay); +u32 SI_GetTypeAsync(s32 chan,SICallback cb); +u32 SI_GetType(s32 chan); +u32 SI_GetCommand(s32 chan); +void SI_TransferCommands(); +u32 SI_RegisterPollingHandler(RDSTHandler handler); +u32 SI_UnregisterPollingHandler(RDSTHandler handler); +u32 SI_EnablePollingInterrupt(s32 enable); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/stm.h b/wii/libogc/include/ogc/stm.h new file mode 100644 index 0000000000..5d2307f4c5 --- /dev/null +++ b/wii/libogc/include/ogc/stm.h @@ -0,0 +1,66 @@ +/*------------------------------------------------------------- + +stm.h - System and miscellaneous hardware control functions + +Copyright (C) 2008 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) +Hector Martin (marcan) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#ifndef __STM_H__ +#define __STM_H__ + +#if defined(HW_RVL) + +#include +#include + +#define STM_EVENT_RESET 0x00020000 +#define STM_EVENT_POWER 0x00000800 + +#define STM_EINVAL -0x2004 +#define STM_ENOTINIT -0x2100 +#define STM_ENOHANDLER -0x2101 + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef void (*stmcallback)(u32 event); + +s32 __STM_Init(); +s32 __STM_Close(); +s32 STM_ShutdownToStandby(); +s32 STM_ShutdownToIdle(); +s32 STM_SetLedMode(u32 mode); +s32 STM_RebootSystem(); +stmcallback STM_RegisterEventHandler(stmcallback newhandler); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif /* defined(HW_RVL) */ + +#endif diff --git a/wii/libogc/include/ogc/sys_state.h b/wii/libogc/include/ogc/sys_state.h new file mode 100644 index 0000000000..b48837b2e2 --- /dev/null +++ b/wii/libogc/include/ogc/sys_state.h @@ -0,0 +1,27 @@ +#ifndef __SYS_STATE_H__ +#define __SYS_STATE_H__ + +#define SYS_STATE_BEFORE_INIT 0 +#define SYS_STATE_BEFORE_MT 1 +#define SYS_STATE_BEGIN_MT 2 +#define SYS_STATE_UP 3 +#define SYS_STATE_SHUTDOWN 4 +#define SYS_STATE_FAILED 5 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern u32 _sys_state_curr; + +#ifdef LIBOGC_INTERNAL +#include +#endif + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/ogc/system.h b/wii/libogc/include/ogc/system.h new file mode 100644 index 0000000000..b82e251438 --- /dev/null +++ b/wii/libogc/include/ogc/system.h @@ -0,0 +1,350 @@ +/*------------------------------------------------------------- + +system.h -- OS functions and initialization + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ + + +#ifndef __SYSTEM_H__ +#define __SYSTEM_H__ + + +/*! \file system.h +\brief OS functions and initialization + +*/ + +#include +#include +#include +#include +#include "gx_struct.h" + +#define SYS_BASE_CACHED (0x80000000) +#define SYS_BASE_UNCACHED (0xC0000000) + +#define SYS_WD_NULL 0xffffffff + +/*! + * \addtogroup sys_resettypes OS reset types + * @{ + */ + +#define SYS_RESTART 0 /*!< Reboot the gamecube, force, if necessary, to boot the IPL menu. Cold reset is issued */ +#define SYS_HOTRESET 1 /*!< Restart the application. Kind of softreset */ +#define SYS_SHUTDOWN 2 /*!< Shutdown the thread system, card management system etc. Leave current thread running and return to caller */ + +#define SYS_RETURNTOMENU 3 /*!< Directly load the Wii Channels menu, without actually cold-resetting the system */ +#define SYS_POWEROFF 4 /*!< Powers off the Wii, automatically choosing Standby or Idle mode depending on the user's configuration */ +#define SYS_POWEROFF_STANDBY 5 /*!< Powers off the Wii to standby (red LED, WC24 off) mode. */ +#define SYS_POWEROFF_IDLE 6 /*!< Powers off the Wii to idle (yellow LED, WC24 on) mode. */ + +/*! + *@} + */ + + +/*! + * \addtogroup sys_mprotchans OS memory protection channels + * @{ + */ + +#define SYS_PROTECTCHAN0 0 /*!< OS memory protection channel 0 */ +#define SYS_PROTECTCHAN1 1 /*!< OS memory protection channel 1 */ +#define SYS_PROTECTCHAN2 2 /*!< OS memory protection channel 2 */ +#define SYS_PROTECTCHAN3 3 /*!< OS memory protection channel 2 */ +#define SYS_PROTECTCHANMAX 4 /*!< _Termination */ + +/*! + *@} + */ + + +/*! + * \addtogroup sys_mprotmodes OS memory protection modes + * @{ + */ + +#define SYS_PROTECTNONE 0x00000000 /*!< Read and write operations on protected region is granted */ +#define SYS_PROTECTREAD 0x00000001 /*!< Read from protected region is permitted */ +#define SYS_PROTECTWRITE 0x00000002 /*!< Write to protected region is permitted */ +#define SYS_PROTECTRDWR (SYS_PROTECTREAD|SYS_PROTECTWRITE) /*!< Read and write operations on protected region is permitted */ + +/*! + *@} + */ + +#define SYS_FONTSIZE_ANSI (288 + 131072) +#define SYS_FONTSIZE_SJIS (3840 + 1179648) + + + +/*! + * \addtogroup sys_mcastmacros OS memory casting macros + * @{ + */ + +#define MEM_VIRTUAL_TO_PHYSICAL(x) (((u32)(x)) & ~SYS_BASE_UNCACHED) /*!< Cast virtual address to physical address, e.g. 0x8xxxxxxx -> 0x0xxxxxxx */ +#define MEM_PHYSICAL_TO_K0(x) (void*)((u32)(x) + SYS_BASE_CACHED) /*!< Cast physical address to cached virtual address, e.g. 0x0xxxxxxx -> 0x8xxxxxxx */ +#define MEM_PHYSICAL_TO_K1(x) (void*)((u32)(x) + SYS_BASE_UNCACHED) /*!< Cast physical address to uncached virtual address, e.g. 0x0xxxxxxx -> 0xCxxxxxxx */ +#define MEM_K0_TO_PHYSICAL(x) (void*)((u32)(x) - SYS_BASE_CACHED) /*!< Cast physical address to cached virtual address, e.g. 0x0xxxxxxx -> 0x8xxxxxxx */ +#define MEM_K1_TO_PHYSICAL(x) (void*)((u32)(x) - SYS_BASE_UNCACHED) /*!< Cast physical address to uncached virtual address, e.g. 0x0xxxxxxx -> 0xCxxxxxxx */ +#define MEM_K0_TO_K1(x) (void*)((u32)(x) + (SYS_BASE_UNCACHED - SYS_BASE_CACHED)) /*!< Cast cached virtual address to uncached virtual address, e.g. 0x8xxxxxxx -> 0xCxxxxxxx */ +#define MEM_K1_TO_K0(x) (void*)((u32)(x) - (SYS_BASE_UNCACHED - SYS_BASE_CACHED)) /*!< Cast uncached virtual address to cached virtual address, e.g. 0xCxxxxxxx -> 0x8xxxxxxx */ + +/*! + *@} + */ + +#define SYS_GetArenaLo SYS_GetArena1Lo +#define SYS_SetArenaLo SYS_SetArena1Lo +#define SYS_GetArenaHi SYS_GetArena1Hi +#define SYS_SetArenaHi SYS_SetArena1Hi +#define SYS_GetArenaSize SYS_GetArena1Size + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + +/*! + * \typedef u32 syswd_t + * \brief handle typedef for the alarm context + */ +typedef u32 syswd_t; + + + /*! + * \typedef struct _syssram syssram + * \brief holds the stored configuration value from the system SRAM area + * \param checksum holds the block checksum. + * \param checksum_in holds the inverse block checksum + * \param ead0 unknown attribute + * \param ead1 unknown attribute + * \param counter_bias bias value for the realtime clock + * \param display_offsetH pixel offset for the VI + * \param ntd unknown attribute + * \param lang language of system + * \param flags device and operations flag + */ +typedef struct _syssram syssram; + +struct _syssram { + u16 checksum; + u16 checksum_inv; + u32 ead0; + u32 ead1; + u32 counter_bias; + s8 display_offsetH; + u8 ntd; + u8 lang; + u8 flags; +} ATTRIBUTE_PACKED; + + +/*! + * \typedef struct _syssramex syssramex + * \brief holds the stored configuration value from the extended SRAM area + * \param flash_id[2][12] 96bit memorycard unlock flash ID + * \param wirelessKbd_id Device ID of last connected wireless keyboard + * \param wirelessPad_id[4] 16bit device ID of last connected pad. + * \param dvderr_code last non-recoverable error from DVD interface + * \param __padding0 padding + * \param flashID_chksum[2] 16bit checksum of unlock flash ID + * \param __padding1[4] padding + */ +typedef struct _syssramex syssramex; + +struct _syssramex { + u8 flash_id[2][12]; + u32 wirelessKbd_id; + u16 wirelessPad_id[4]; + u8 dvderr_code; + u8 __padding0; + u16 flashID_chksum[2]; + u8 __padding1[4]; +} ATTRIBUTE_PACKED; + +typedef void (*alarmcallback)(syswd_t alarm,void *cb_arg); + +typedef struct _sys_fontheader sys_fontheader; + +struct _sys_fontheader { + u16 font_type; + u16 first_char; + u16 last_char; + u16 inval_char; + u16 asc; + u16 desc; + u16 width; + u16 leading; + u16 cell_width; + u16 cell_height; + u32 sheet_size; + u16 sheet_format; + u16 sheet_column; + u16 sheet_row; + u16 sheet_width; + u16 sheet_height; + u16 width_table; + u32 sheet_image; + u32 sheet_fullsize; + u8 c0; + u8 c1; + u8 c2; + u8 c3; +} ATTRIBUTE_PACKED; + +typedef void (*resetcallback)(void); +typedef void (*powercallback)(void); +typedef s32 (*resetfunction)(s32 final); +typedef struct _sys_resetinfo sys_resetinfo; + +struct _sys_resetinfo { + lwp_node node; + resetfunction func; + u32 prio; +}; + +/*! \fn void SYS_Init() +\deprecated Performs basic system initialization such as EXI init etc. This function is called from within the crt0 startup code. + +\return none +*/ +void SYS_Init(); + + +/*! + * \fn void* SYS_AllocateFramebuffer(GXRModeObj *rmode) + * \brief Allocate cacheline aligned memory for the external framebuffer based on the rendermode object. + * \param[in] rmode pointer to the video/render mode configuration + * + * \return pointer to the framebuffer's startaddress. NOTE: Address returned is aligned to a 32byte boundery! + */ +void* SYS_AllocateFramebuffer(GXRModeObj *rmode); + + +void SYS_ProtectRange(u32 chan,void *addr,u32 bytes,u32 cntrl); +void SYS_StartPMC(u32 mcr0val,u32 mcr1val); +void SYS_DumpPMC(); +void SYS_StopPMC(); + + +/*! \fn s32 SYS_CreateAlarm(syswd_t *thealarm) +\brief Create/initialize sysalarm structure +\param[in] thealarm pointer to the handle to store the created alarm context identifier + +\return 0 on succuess, non-zero on error +*/ +s32 SYS_CreateAlarm(syswd_t *thealarm); + + +/*! \fn s32 SYS_SetAlarm(syswd_t thealarm,const struct timespec *tp,alarmcallback cb) +\brief Set the alarm parameters for a one-shot alarm, add to the list of alarms and start. +\param[in] thealarm identifier to the alarm context to be initialize for a one-shot alarm +\param[in] tp pointer to timespec structure holding the time to fire the alarm +\param[in] cb pointer to callback which is called when the alarm fires. + +\return 0 on succuess, non-zero on error +*/ +s32 SYS_SetAlarm(syswd_t thealarm,const struct timespec *tp,alarmcallback cb,void *cbarg); + + +/*! \fn s32 SYS_SetPeriodicAlarm(syswd_t thealarm,const struct timespec *tp_start,const struct timespec *tp_period,alarmcallback cb) +\brief Set the alarm parameters for a periodioc alarm, add to the list of alarms and start. The alarm and interval persists as long as SYS_CancelAlarm() isn't called. +\param[in] thealarm identifier to the alarm context to be initialized for a periodic alarm +\param[in] tp_start pointer to timespec structure holding the time to fire first time the alarm +\param[in] tp_period pointer to timespec structure holding the interval for all following alarm triggers. +\param[in] cb pointer to callback which is called when the alarm fires. + +\return 0 on succuess, non-zero on error +*/ +s32 SYS_SetPeriodicAlarm(syswd_t thealarm,const struct timespec *tp_start,const struct timespec *tp_period,alarmcallback cb,void *cbarg); + + +/*! \fn s32 SYS_RemoveAlarm(syswd_t thealarm) +\brief Remove the given alarm context from the list of contexts and destroy it +\param[in] thealarm identifier to the alarm context to be removed and destroyed + +\return 0 on succuess, non-zero on error +*/ +s32 SYS_RemoveAlarm(syswd_t thealarm); + + +/*! \fn s32 SYS_CancelAlarm(syswd_t thealarm) +\brief Cancel the alarm, but do not remove from the list of contexts. +\param[in] thealarm identifier to the alram context to be canceled + +\return 0 on succuess, non-zero on error +*/ +s32 SYS_CancelAlarm(syswd_t thealarm); + + +void SYS_SetWirelessID(u32 chan,u32 id); +u32 SYS_GetWirelessID(u32 chan); +u32 SYS_GetFontEncoding(); +u32 SYS_InitFont(sys_fontheader *font_data); +void SYS_GetFontTexture(s32 c,void **image,s32 *xpos,s32 *ypos,s32 *width); +void SYS_GetFontTexel(s32 c,void *image,s32 pos,s32 stride,s32 *width); +void SYS_ResetSystem(s32 reset,u32 reset_code,s32 force_menu); +void SYS_RegisterResetFunc(sys_resetinfo *info); +void SYS_UnregisterResetFunc(sys_resetinfo *info); +void SYS_SwitchFiber(u32 arg0,u32 arg1,u32 arg2,u32 arg3,u32 pc,u32 newsp); + +void* SYS_GetArena1Lo(); +void SYS_SetArena1Lo(void *newLo); +void* SYS_GetArena1Hi(); +void SYS_SetArena1Hi(void *newHi); +u32 SYS_GetArena1Size(); + +resetcallback SYS_SetResetCallback(resetcallback cb); + +u32 SYS_ResetButtonDown(); + +#if defined(HW_RVL) +u32 SYS_GetHollywoodRevision(); +void* SYS_GetArena2Lo(); +void SYS_SetArena2Lo(void *newLo); +void* SYS_GetArena2Hi(); +void SYS_SetArena2Hi(void *newHi); +u32 SYS_GetArena2Size(); +powercallback SYS_SetPowerCallback(powercallback cb); +#endif + +/* \fn u64 SYS_Time() +\brief Returns the current time in ticks since 2000 (proper Dolphin/Wii time) +\return ticks since 2000 +*/ +u64 SYS_Time(); + +void kprintf(const char *str, ...); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/texconv.h b/wii/libogc/include/ogc/texconv.h new file mode 100644 index 0000000000..3a07ee0ca7 --- /dev/null +++ b/wii/libogc/include/ogc/texconv.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------- + +texconv.h -- GX texture helper functions + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#ifndef __TEXCONV_H__ +#define __TEXTCONV_H__ + +/*! +\file texconv.h +\brief GX texture helper functions +*/ + +#include + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +void MakeTexture565(const void *src,void *dst,s32 width,s32 height); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/tpl.h b/wii/libogc/include/ogc/tpl.h new file mode 100644 index 0000000000..740b7613ae --- /dev/null +++ b/wii/libogc/include/ogc/tpl.h @@ -0,0 +1,31 @@ +#ifndef __TPL_H__ +#define __TPL_H__ + +#include "gx.h" + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef void* FHANDLE; + +// tdf file +typedef struct _tplfile { + int type; + int ntextures; + void *texdesc; + FHANDLE tpl_file; +} TPLFile; + +s32 TPL_OpenTPLFromFile(TPLFile* tdf, const char* file_name); +s32 TPL_OpenTPLFromMemory(TPLFile* tdf, void *memory,u32 len); +s32 TPL_GetTexture(TPLFile *tdf,s32 id,GXTexObj *texObj); +s32 TPL_GetTextureCI(TPLFile *tdf,s32 id,GXTexObj *texObj,GXTlutObj *tlutObj,u8 tluts); +s32 TPL_GetTextureInfo(TPLFile *tdf,s32 id,u32 *fmt,u16 *width,u16 *height); +void TPL_CloseTPLFile(TPLFile *tdf); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/usb.h b/wii/libogc/include/ogc/usb.h new file mode 100644 index 0000000000..706b81139f --- /dev/null +++ b/wii/libogc/include/ogc/usb.h @@ -0,0 +1,239 @@ +#ifndef __USB_H__ +#define __USB_H__ + +#if defined(HW_RVL) + +#include +#include + +#define USB_MAXPATH IPC_MAXPATH_LEN + +#define USB_OK 0 +#define USB_FAILED 1 + +#define USB_CLASS_HID 0x03 +#define USB_SUBCLASS_BOOT 0x01 +#define USB_PROTOCOL_KEYBOARD 0x01 +#define USB_PROTOCOL_MOUSE 0x02 + +#define USB_REPTYPE_INPUT 0x01 +#define USB_REPTYPE_OUTPUT 0x02 +#define USB_REPTYPE_FEATURE 0x03 + +/* Descriptor types */ +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 +#define USB_DT_DEVICE_QUALIFIER 0x06 +#define USB_DT_OTHER_SPEED_CONFIG 0x07 +#define USB_DT_INTERFACE_POWER 0x08 +#define USB_DT_OTG 0x09 +#define USB_DT_DEBUG 0x10 +#define USB_DT_INTERFACE_ASSOCIATION 0x11 +#define USB_DT_HID 0x21 +#define USB_DT_REPORT 0x22 +#define USB_DT_PHYSICAL 0x23 +#define USB_DT_CLASS_SPECIFIC_INTERFACE 0x24 +#define USB_DT_CLASS_SPECIFIC_ENDPOINT 0x25 +#define USB_DT_HUB 0x29 + +/* Standard requests */ +#define USB_REQ_GETSTATUS 0x00 +#define USB_REQ_CLEARFEATURE 0x01 +#define USB_REQ_SETFEATURE 0x03 +#define USB_REQ_SETADDRESS 0x05 +#define USB_REQ_GETDESCRIPTOR 0x06 +#define USB_REQ_SETDESCRIPTOR 0x07 +#define USB_REQ_GETCONFIG 0x08 +#define USB_REQ_SETCONFIG 0x09 +#define USB_REQ_GETINTERFACE 0x0A +#define USB_REQ_SETINTERFACE 0x0B +#define USB_REQ_SYNCFRAME 0x0C + +#define USB_REQ_GETREPORT 0x01 +#define USB_REQ_GETIDLE 0x02 +#define USB_REQ_GETPROTOCOL 0x03 +#define USB_REQ_SETREPORT 0x09 +#define USB_REQ_SETIDLE 0x0A +#define USB_REQ_SETPROTOCOL 0x0B + +/* Descriptor sizes per descriptor type */ +#define USB_DT_DEVICE_SIZE 18 +#define USB_DT_CONFIG_SIZE 9 +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define USB_DT_HID_SIZE 9 +#define USB_DT_HUB_NONVAR_SIZE 7 + +/* control message request type bitmask */ +#define USB_CTRLTYPE_DIR_HOST2DEVICE (0<<7) +#define USB_CTRLTYPE_DIR_DEVICE2HOST (1<<7) +#define USB_CTRLTYPE_TYPE_STANDARD (0<<5) +#define USB_CTRLTYPE_TYPE_CLASS (1<<5) +#define USB_CTRLTYPE_TYPE_VENDOR (2<<5) +#define USB_CTRLTYPE_TYPE_RESERVED (3<<5) +#define USB_CTRLTYPE_REC_DEVICE 0 +#define USB_CTRLTYPE_REC_INTERFACE 1 +#define USB_CTRLTYPE_REC_ENDPOINT 2 +#define USB_CTRLTYPE_REC_OTHER 3 + +#define USB_REQTYPE_INTERFACE_GET (USB_CTRLTYPE_DIR_DEVICE2HOST|USB_CTRLTYPE_TYPE_CLASS|USB_CTRLTYPE_REC_INTERFACE) +#define USB_REQTYPE_INTERFACE_SET (USB_CTRLTYPE_DIR_HOST2DEVICE|USB_CTRLTYPE_TYPE_CLASS|USB_CTRLTYPE_REC_INTERFACE) +#define USB_REQTYPE_ENDPOINT_GET (USB_CTRLTYPE_DIR_DEVICE2HOST|USB_CTRLTYPE_TYPE_CLASS|USB_CTRLTYPE_REC_ENDPOINT) +#define USB_REQTYPE_ENDPOINT_SET (USB_CTRLTYPE_DIR_HOST2DEVICE|USB_CTRLTYPE_TYPE_CLASS|USB_CTRLTYPE_REC_ENDPOINT) + +#define USB_FEATURE_ENDPOINT_HALT 0 + +#define USB_ENDPOINT_INTERRUPT 0x03 +#define USB_ENDPOINT_IN 0x80 +#define USB_ENDPOINT_OUT 0x00 + +#define USB_OH0_DEVICE_ID 0x00000000 // for completion +#define USB_OH1_DEVICE_ID 0x00200000 + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef struct _usbendpointdesc +{ + u8 bLength; + u8 bDescriptorType; + u8 bEndpointAddress; + u8 bmAttributes; + u16 wMaxPacketSize; + u8 bInterval; +} ATTRIBUTE_PACKED usb_endpointdesc; + +typedef struct _usbinterfacedesc +{ + u8 bLength; + u8 bDescriptorType; + u8 bInterfaceNumber; + u8 bAlternateSetting; + u8 bNumEndpoints; + u8 bInterfaceClass; + u8 bInterfaceSubClass; + u8 bInterfaceProtocol; + u8 iInterface; + u8 *extra; + u16 extra_size; + struct _usbendpointdesc *endpoints; +} ATTRIBUTE_PACKED usb_interfacedesc; + +typedef struct _usbconfdesc +{ + u8 bLength; + u8 bDescriptorType; + u16 wTotalLength; + u8 bNumInterfaces; + u8 bConfigurationValue; + u8 iConfiguration; + u8 bmAttributes; + u8 bMaxPower; + struct _usbinterfacedesc *interfaces; +} ATTRIBUTE_PACKED usb_configurationdesc; + +typedef struct _usbdevdesc +{ + u8 bLength; + u8 bDescriptorType; + u16 bcdUSB; + u8 bDeviceClass; + u8 bDeviceSubClass; + u8 bDeviceProtocol; + u8 bMaxPacketSize0; + u16 idVendor; + u16 idProduct; + u16 bcdDevice; + u8 iManufacturer; + u8 iProduct; + u8 iSerialNumber; + u8 bNumConfigurations; + struct _usbconfdesc *configurations; +} ATTRIBUTE_PACKED usb_devdesc; + +typedef struct _usbhiddesc +{ + u8 bLength; + u8 bDescriptorType; + u16 bcdHID; + u8 bCountryCode; + u8 bNumDescriptors; + struct { + u8 bDescriptorType; + u16 wDescriptorLength; + } ATTRIBUTE_PACKED descr[1]; +} ATTRIBUTE_PACKED usb_hiddesc; + +typedef struct _usb_device_entry { + s32 device_id; + u16 vid; + u16 pid; + u32 token; +} usb_device_entry; + +typedef s32 (*usbcallback)(s32 result,void *usrdata); + +s32 USB_Initialize(); +s32 USB_Deinitialize(); + +s32 USB_OpenDevice(s32 device_id,u16 vid,u16 pid,s32 *fd); +s32 USB_CloseDevice(s32 *fd); +s32 USB_CloseDeviceAsync(s32 *fd,usbcallback cb,void *usrdata); + +s32 USB_GetDescriptors(s32 fd, usb_devdesc *udd); +void USB_FreeDescriptors(usb_devdesc *udd); + +s32 USB_GetGenericDescriptor(s32 fd,u8 type,u8 index,u8 interface,void *data,u32 size); +s32 USB_GetHIDDescriptor(s32 fd,u8 interface,usb_hiddesc *uhd,u32 size); + +s32 USB_GetDeviceDescription(s32 fd,usb_devdesc *devdesc); +s32 USB_DeviceRemovalNotifyAsync(s32 fd,usbcallback cb,void *userdata); +s32 USB_DeviceChangeNotifyAsync(u8 interface_class,usbcallback cb,void *userdata); + +s32 USB_SuspendDevice(s32 fd); +s32 USB_ResumeDevice(s32 fd); + +s32 USB_ReadIsoMsg(s32 fd,u8 bEndpoint,u8 bPackets,u16 *rpPacketSizes,void *rpData); +s32 USB_ReadIsoMsgAsync(s32 fd,u8 bEndpoint,u8 bPackets,u16 *rpPacketSizes,void *rpData,usbcallback cb,void *userdata); + +s32 USB_ReadIntrMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData); +s32 USB_ReadIntrMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *usrdata); + +s32 USB_ReadBlkMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData); +s32 USB_ReadBlkMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *usrdata); + +s32 USB_ReadCtrlMsg(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData); +s32 USB_ReadCtrlMsgAsync(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData,usbcallback cb,void *usrdata); + +s32 USB_WriteIsoMsg(s32 fd,u8 bEndpoint,u8 bPackets,u16 *rpPacketSizes,void *rpData); +s32 USB_WriteIsoMsgAsync(s32 fd,u8 bEndpoint,u8 bPackets,u16 *rpPacketSizes,void *rpData,usbcallback cb,void *userdata); + +s32 USB_WriteIntrMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData); +s32 USB_WriteIntrMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *usrdata); + +s32 USB_WriteBlkMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData); +s32 USB_WriteBlkMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *usrdata); + +s32 USB_WriteCtrlMsg(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData); +s32 USB_WriteCtrlMsgAsync(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData,usbcallback cb,void *usrdata); + +s32 USB_GetConfiguration(s32 fd, u8 *configuration); +s32 USB_SetConfiguration(s32 fd, u8 configuration); +s32 USB_SetAlternativeInterface(s32 fd, u8 interface, u8 alternateSetting); +s32 USB_ClearHalt(s32 fd, u8 endpointAddress); +s32 USB_GetDeviceList(usb_device_entry *descr_buffer,u8 num_descr,u8 interface_class,u8 *cnt_descr); + +s32 USB_GetAsciiString(s32 fd,u8 bIndex,u16 wLangID,u16 wLength,void *rpData); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif /* defined(HW_RVL) */ + +#endif diff --git a/wii/libogc/include/ogc/usbgecko.h b/wii/libogc/include/ogc/usbgecko.h new file mode 100644 index 0000000000..dbbbbe928c --- /dev/null +++ b/wii/libogc/include/ogc/usbgecko.h @@ -0,0 +1,28 @@ +#ifndef __USBGECKO_H___ +#define __USBGECKO_H___ + +#include + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +void usb_flush(s32 chn); +int usb_isgeckoalive(s32 chn); +int usb_recvbuffer(s32 chn,void *buffer,int size); +int usb_sendbuffer(s32 chn,const void *buffer,int size); +int usb_recvbuffer_safe(s32 chn,void *buffer,int size); +int usb_sendbuffer_safe(s32 chn,const void *buffer,int size); +int usb_recvbuffer_ex(s32 chn,void *buffer,int size, int retries); +int usb_sendbuffer_ex(s32 chn,const void *buffer,int size, int retries); +int usb_recvbuffer_safe_ex(s32 chn,void *buffer,int size, int retries); +int usb_sendbuffer_safe_ex(s32 chn,const void *buffer,int size, int retries); +int usb_flashread(s32 chn, u32 offset, void *buffer, size_t length); +int usb_flashwrite(s32 chn, u32 offset, const void *buffer, size_t length); +int usb_flashverify(s32 chn); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/usbmouse.h b/wii/libogc/include/ogc/usbmouse.h new file mode 100644 index 0000000000..28b8581120 --- /dev/null +++ b/wii/libogc/include/ogc/usbmouse.h @@ -0,0 +1,30 @@ +#ifndef __USBMOUSE_H__ +#define __USBMOUSE_H__ + +#if defined(HW_RVL) + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef struct { + u8 button; + int rx; + int ry; + int rz; +} mouse_event; + +s32 MOUSE_Init(void); +s32 MOUSE_Deinit(void); + +s32 MOUSE_GetEvent(mouse_event *event); +s32 MOUSE_FlushEvents(void); +bool MOUSE_IsConnected(void); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif + +#endif diff --git a/wii/libogc/include/ogc/usbstorage.h b/wii/libogc/include/ogc/usbstorage.h new file mode 100644 index 0000000000..04efa5fbed --- /dev/null +++ b/wii/libogc/include/ogc/usbstorage.h @@ -0,0 +1,92 @@ +#ifndef __USBSTORAGE_H__ +#define __USBSTORAGE_H__ + +#if defined(HW_RVL) + +#include +#include +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +#define USBSTORAGE_OK 0 +#define USBSTORAGE_ENOINTERFACE -10000 +#define USBSTORAGE_ESENSE -10001 +#define USBSTORAGE_ESHORTWRITE -10002 +#define USBSTORAGE_ESHORTREAD -10003 +#define USBSTORAGE_ESIGNATURE -10004 +#define USBSTORAGE_ETAG -10005 +#define USBSTORAGE_ESTATUS -10006 +#define USBSTORAGE_EDATARESIDUE -10007 +#define USBSTORAGE_ETIMEDOUT -10008 +#define USBSTORAGE_EINIT -10009 +#define USBSTORAGE_PROCESSING -10010 + +typedef struct +{ + u8 configuration; + u32 interface; + u32 altInterface; + u8 bInterfaceSubClass; + + u8 ep_in; + u8 ep_out; + + u8 max_lun; + u32 *sector_size; + + s32 usb_fd; + + mutex_t lock; + syswd_t alarm; + s32 retval; + + u32 tag; + u8 suspended; + + u8 *buffer; +} usbstorage_handle; + +#define B_RAW_DEVICE_DATA_IN 0x01 +#define B_RAW_DEVICE_COMMAND 0 + +typedef struct { + uint8_t command[16]; + uint8_t command_length; + uint8_t flags; + uint8_t scsi_status; + void* data; + size_t data_length; +} raw_device_command; + +s32 USBStorage_Initialize(); + +s32 USBStorage_Open(usbstorage_handle *dev, s32 device_id, u16 vid, u16 pid); +s32 USBStorage_Close(usbstorage_handle *dev); +s32 USBStorage_Reset(usbstorage_handle *dev); + +s32 USBStorage_GetMaxLUN(usbstorage_handle *dev); +s32 USBStorage_MountLUN(usbstorage_handle *dev, u8 lun); +s32 USBStorage_Suspend(usbstorage_handle *dev); +s32 USBStorage_IsDVD(); +s32 USBStorage_ioctl(int request, ...); + +s32 USBStorage_ReadCapacity(usbstorage_handle *dev, u8 lun, u32 *sector_size, u32 *n_sectors); +s32 USBStorage_Read(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, u8 *buffer); +s32 USBStorage_Write(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, const u8 *buffer); +s32 USBStorage_StartStop(usbstorage_handle *dev, u8 lun, u8 lo_ej, u8 start, u8 imm); + +#define DEVICE_TYPE_WII_USB (('W'<<24)|('U'<<16)|('S'<<8)|'B') + +extern DISC_INTERFACE __io_usbstorage; + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif /* HW_RVL */ + +#endif /* __USBSTORAGE_H__ */ diff --git a/wii/libogc/include/ogc/video.h b/wii/libogc/include/ogc/video.h new file mode 100644 index 0000000000..4e01bdc2f8 --- /dev/null +++ b/wii/libogc/include/ogc/video.h @@ -0,0 +1,206 @@ +/*------------------------------------------------------------- + +video.h -- VIDEO subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#ifndef __VIDEO_H__ +#define __VIDEO_H__ + +/*! + * \file video.h + * \brief VIDEO subsystem + * + */ + +#include +#include "gx_struct.h" +#include "video_types.h" + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + + +/*! + * \typedef void (*VIRetraceCallback)(u32 retraceCnt) + * \brief function pointer typedef for the user's retrace callback + * \param[in] retraceCnt current retrace count + */ +typedef void (*VIRetraceCallback)(u32 retraceCnt); + +typedef void (*VIPositionCallback)(u32 posX,u32 posY); + +void* VIDEO_GetNextFramebuffer(); +void* VIDEO_GetCurrentFramebuffer(); + + +/*! + * \fn void VIDEO_Init() + * \brief Initializes the VIDEO subsystem. This call should be done in the early stages of your main() + * + * \return none + */ +void VIDEO_Init(); + + +/*! + * \fn void VIDEO_Flush() + * \brief Flush the shadow registers to the drivers video registers. + * + * \return none + */ +void VIDEO_Flush(); + + +/*! + * \fn void VIDEO_SetBlack(bool black) + * \brief Blackout the VIDEO interface. + * + * \param[in] black Boolean flag to determine whether to blackout the VI or not. + * + * \return none + */ +void VIDEO_SetBlack(bool black); + + +/*! + * \fn u32 VIDEO_GetNextField() + * \brief Get the next field in DS mode. + * + * \return \ref vi_fielddef "field" + */ +u32 VIDEO_GetNextField(); + + +/*! + * \fn u32 VIDEO_GetCurrentLine() + * \brief Get current video line + * + * \return linenumber + */ +u32 VIDEO_GetCurrentLine(); + + +/*! + * \fn u32 VIDEO_GetCurrentTvMode() + * \brief Get current configured TV mode + * + * \return \ref vi_standardtypedef "tvmode" + */ +u32 VIDEO_GetCurrentTvMode(); + + +/*! + * \fn void VIDEO_Configure(GXRModeObj *rmode) + * \brief Configure the VI with the given render mode object + * + * \param[in] rmode pointer to the video/render mode \ref gxrmode_obj "configuration". + * + * \return none + */ +void VIDEO_Configure(GXRModeObj *rmode); + +u32 VIDEO_GetFrameBufferSize(GXRModeObj *rmode); + +/*! + * \fn void VIDEO_ClearFrameBuffer(GXRModeObj *rmode,void *fb,u32 color) + * \brief Clear the given framebuffer. + * + * \param[in] rmode pointer to a GXRModeObj, specifying the mode. + * \param[in] fb pointer to the startaddress of the framebuffer to clear. + * \param[in] color YUYUV value to use for clearing. + * + * \return none + */ +void VIDEO_ClearFrameBuffer(GXRModeObj *rmode,void *fb,u32 color); + + +/*! + * \fn void VIDEO_WaitVSync(void) + * \brief Wait on the next vertical retrace + * + * \return none + */ +void VIDEO_WaitVSync(void); + + +/*! + * \fn void VIDEO_SetNextFramebuffer(void *fb) + * \brief Set the framebuffer for the next VI register update. + * + * \return none + */ +void VIDEO_SetNextFramebuffer(void *fb); + + +/*! + * \fn void VIDEO_SetNextRightFramebuffer(void *fb) + * \brief Set the right framebuffer for the next VI register update. This is used for 3D Gloves for instance. + * + * \return none + */ +void VIDEO_SetNextRightFramebuffer(void *fb); + + +/*! + * \fn VIRetraceCallback VIDEO_SetPreRetraceCallback(VIRetraceCallback callback) + * \brief Set the Pre-Retrace callback function. This function is called within the video interrupt handler before the VI registers will be updated. + * + * \param[in] callback pointer to the callback function which is called at pre-retrace. + * + * \return Old pre-retrace callback or NULL + */ +VIRetraceCallback VIDEO_SetPreRetraceCallback(VIRetraceCallback callback); + + +/*! + * \fn VIRetraceCallback VIDEO_SetPostRetraceCallback(VIRetraceCallback callback) + * \brief Set the Post-Retrace callback function. This function is called within the video interrupt handler after the VI registers are updated. + * + * \param[in] callback pointer to the callback function which is called at post-retrace. + * + * \return Old post-retrace callback or NULL + */ +VIRetraceCallback VIDEO_SetPostRetraceCallback(VIRetraceCallback callback); + + +/*! + * \fn u32 VIDEO_HaveComponentCable(void) + * \brief Check for a component cable. This function returns 1 when a Component (YPbPr) cable is connected. + * + * \return 1 if a component cable is connected, 0 otherwise + */ +u32 VIDEO_HaveComponentCable(void); + +GXRModeObj * VIDEO_GetPreferredMode(GXRModeObj *mode); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/ogc/video_types.h b/wii/libogc/include/ogc/video_types.h new file mode 100644 index 0000000000..9a03873d82 --- /dev/null +++ b/wii/libogc/include/ogc/video_types.h @@ -0,0 +1,195 @@ +/*------------------------------------------------------------- + +video_types.h -- support header + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#ifndef __VIDEO_TYPES_H__ +#define __VIDEO_TYPES_H__ + +/*! +\file video_types.h +\brief support header +*/ + +#include + +/*! + * \addtogroup vi_defines List of defines used for the VIDEO subsystem + * @{ + */ + + +#define VI_DISPLAY_PIX_SZ 2 /*!< multiplier to get real pixel size in bytes */ + +/*! + * \addtogroup vi_modetypedef VIDEO mode types + * @{ + */ + +#define VI_INTERLACE 0 /*!< Video mode INTERLACED. */ +#define VI_NON_INTERLACE 1 /*!< Video mode NON INTERLACED */ +#define VI_PROGRESSIVE 2 /*!< Video mode PROGRESSIVE. Special mode for higher quality */ + +/*! + * @} + */ + + +/*! + * \addtogroup vi_standardtypedef VIDEO standard types + * @{ + */ + +#define VI_NTSC 0 /*!< Video standard used in North America and Japan */ +#define VI_PAL 1 /*!< Video standard used in Europe */ +#define VI_MPAL 2 /*!< Video standard, similar to NTSC, used in Brazil */ +#define VI_DEBUG 3 /*!< Video standard, for debugging purpose, used in North America and Japan. Special decoder needed */ +#define VI_DEBUG_PAL 4 /*!< Video standard, for debugging purpose, used in Europe. Special decoder needed */ +#define VI_EURGB60 5 /*!< RGB 60Hz, 480 lines mode (same timing and aspect ratio as NTSC) used in Europe */ + +/*! + * @} + */ + + +#define VI_XFBMODE_SF 0 +#define VI_XFBMODE_DF 1 + + +/*! + * \addtogroup vi_fielddef VIDEO field types + * @{ + */ + +#define VI_FIELD_ABOVE 1 /*!< Upper field in DS mode */ +#define VI_FIELD_BELOW 0 /*!< Lower field in DS mode */ + +/*! + * @} + */ + + +// Maximum screen space +#define VI_MAX_WIDTH_NTSC 720 +#define VI_MAX_HEIGHT_NTSC 480 + +#define VI_MAX_WIDTH_PAL 720 +#define VI_MAX_HEIGHT_PAL 576 + +#define VI_MAX_WIDTH_MPAL 720 +#define VI_MAX_HEIGHT_MPAL 480 + +#define VI_MAX_WIDTH_EURGB60 VI_MAX_WIDTH_NTSC +#define VI_MAX_HEIGHT_EURGB60 VI_MAX_HEIGHT_NTSC + +#define VIDEO_PadFramebufferWidth(width) ((u16)(((u16)(width) + 15) & ~15)) /*!< macro to pad the width to a multiple of 16 */ + +/*! + * @} + */ + + +#define VI_TVMODE(fmt, mode) ( ((fmt) << 2) + (mode) ) + +#define VI_TVMODE_NTSC_INT VI_TVMODE(VI_NTSC, VI_INTERLACE) +#define VI_TVMODE_NTSC_DS VI_TVMODE(VI_NTSC, VI_NON_INTERLACE) +#define VI_TVMODE_NTSC_PROG VI_TVMODE(VI_NTSC, VI_PROGRESSIVE) + +#define VI_TVMODE_PAL_INT VI_TVMODE(VI_PAL, VI_INTERLACE) +#define VI_TVMODE_PAL_DS VI_TVMODE(VI_PAL, VI_NON_INTERLACE) +#define VI_TVMODE_PAL_PROG VI_TVMODE(VI_PAL, VI_PROGRESSIVE) + +#define VI_TVMODE_EURGB60_INT VI_TVMODE(VI_EURGB60, VI_INTERLACE) +#define VI_TVMODE_EURGB60_DS VI_TVMODE(VI_EURGB60, VI_NON_INTERLACE) +#define VI_TVMODE_EURGB60_PROG VI_TVMODE(VI_EURGB60, VI_PROGRESSIVE) + +#define VI_TVMODE_MPAL_INT VI_TVMODE(VI_MPAL, VI_INTERLACE) +#define VI_TVMODE_MPAL_DS VI_TVMODE(VI_MPAL, VI_NON_INTERLACE) +#define VI_TVMODE_MPAL_PROG VI_TVMODE(VI_MPAL, VI_PROGRESSIVE) + +#define VI_TVMODE_DEBUG_INT VI_TVMODE(VI_DEBUG, VI_INTERLACE) + +#define VI_TVMODE_DEBUG_PAL_INT VI_TVMODE(VI_DEBUG_PAL, VI_INTERLACE) +#define VI_TVMODE_DEBUG_PAL_DS VI_TVMODE(VI_DEBUG_PAL, VI_NON_INTERLACE) + + +/*! + * \addtogroup vi_defines List of defines used for the VIDEO subsystem + * @{ + */ + + +/*! + * \addtogroup gxrmode_obj VIDEO render modes + * @{ + */ + +extern GXRModeObj TVNtsc240Ds; /*!< Video and render mode configuration for 240 lines,singlefield NTSC mode */ +extern GXRModeObj TVNtsc240DsAa; /*!< Video and render mode configuration for 240 lines,singlefield,antialiased NTSC mode */ +extern GXRModeObj TVNtsc240Int; /*!< Video and render mode configuration for 240 lines,interlaced NTSC mode */ +extern GXRModeObj TVNtsc240IntAa; /*!< Video and render mode configuration for 240 lines,interlaced,antialiased NTSC mode */ +extern GXRModeObj TVNtsc480Int; /*!< Video and render mode configuration for 480 lines,interlaced NTSC mode */ +extern GXRModeObj TVNtsc480IntDf; /*!< Video and render mode configuration for 480 lines,interlaced,doublefield NTSC mode */ +extern GXRModeObj TVNtsc480IntAa; /*!< Video and render mode configuration for 480 lines,interlaced,doublefield,antialiased NTSC mode */ +extern GXRModeObj TVNtsc480Prog; /*!< Video and render mode configuration for 480 lines,progressive,singlefield NTSC mode */ +extern GXRModeObj TVNtsc480ProgSoft; +extern GXRModeObj TVNtsc480ProgAa; +extern GXRModeObj TVMpal240Ds; +extern GXRModeObj TVMpal240DsAa; +extern GXRModeObj TVMpal480IntDf; /*!< Video and render mode configuration for 480 lines,interlaced,doublefield,antialiased MPAL mode */ +extern GXRModeObj TVMpal480IntAa; +extern GXRModeObj TVMpal480Prog; +extern GXRModeObj TVPal264Ds; /*!< Video and render mode configuration for 264 lines,singlefield PAL mode */ +extern GXRModeObj TVPal264DsAa; /*!< Video and render mode configuration for 264 lines,singlefield,antialiased PAL mode */ +extern GXRModeObj TVPal264Int; /*!< Video and render mode configuration for 264 lines,interlaced PAL mode */ +extern GXRModeObj TVPal264IntAa; /*!< Video and render mode configuration for 264 lines,interlaced,antialiased PAL mode */ +extern GXRModeObj TVPal524IntAa; /*!< Video and render mode configuration for 524 lines,interlaced,antialiased PAL mode */ +extern GXRModeObj TVPal528Int; /*!< Video and render mode configuration for 528 lines,interlaced,antialiased PAL mode */ +extern GXRModeObj TVPal528IntDf; /*!< Video and render mode configuration for 264 lines,interlaced,doublefield antialiased PAL mode */ +extern GXRModeObj TVPal576IntDfScale; +extern GXRModeObj TVPal576ProgScale; +extern GXRModeObj TVEurgb60Hz240Ds; +extern GXRModeObj TVEurgb60Hz240DsAa; +extern GXRModeObj TVEurgb60Hz240Int; +extern GXRModeObj TVEurgb60Hz240IntAa; +extern GXRModeObj TVEurgb60Hz480Int; +extern GXRModeObj TVEurgb60Hz480IntDf; +extern GXRModeObj TVEurgb60Hz480IntAa; +extern GXRModeObj TVEurgb60Hz480Prog; +extern GXRModeObj TVEurgb60Hz480ProgSoft; +extern GXRModeObj TVEurgb60Hz480ProgAa; + +/*! + * @} + */ + +/*! + * @} + */ + +#endif diff --git a/wii/libogc/include/ogc/wiilaunch.h b/wii/libogc/include/ogc/wiilaunch.h new file mode 100644 index 0000000000..18475465c0 --- /dev/null +++ b/wii/libogc/include/ogc/wiilaunch.h @@ -0,0 +1,79 @@ +/*------------------------------------------------------------- + +wiilaunch.h -- Wii NAND title launching and argument passing + +Copyright (C) 2008 +Hector Martin (marcan) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#if defined(HW_RVL) + +#ifndef __WIILAUNCH_H__ +#define __WIILAUNCH_H__ + +#include +#include + +// not initialized +#define WII_ENOTINIT -0x9001 +// internal error +#define WII_EINTERNAL -0x9002 +// checksum error +#define WII_ECHECKSUM -0x9003 +// required title not installed +#define WII_EINSTALL -0x9004 +// argument list too big +#define WII_E2BIG -0x9005 + +// you probably shouldn't use anything not in this list, since those may change +// these are guaranteed to exist because Nintendo hardcodes them +// any category not on this list will cause a hang when the settings menu tries to do the animation +// however, settings items contained in one of the following categories will work +// nonexistent items will cause a 404 +#define SETTINGS_CALENDAR "Calendar/Calendar_index.html" +#define SETTINGS_DISPLAY "Display/Display_index.html" +#define SETTINGS_SOUND "Sound/Sound_index.html" +#define SETTINGS_PARENTAL "Parental_Control/Parental_Control_index.html" +#define SETTINGS_INTERNET "Internet/Internet_index.html" +#define SETTINGS_WC24 "WiiConnect24/Wiiconnect24_index.html" +#define SETTINGS_UPDATE "Update/Update_index.html" + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +s32 WII_Initialize(void); +s32 WII_ReturnToMenu(void); +s32 WII_ReturnToSettings(void); +s32 WII_ReturnToSettingsPage(const char *page); +s32 WII_LaunchTitle(u64 titleID); +s32 WII_LaunchTitleWithArgs(u64 titleID, int launchcode, ...); +s32 WII_OpenURL(const char *url); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif + +#endif diff --git a/wii/libogc/include/ogcsys.h b/wii/libogc/include/ogcsys.h new file mode 100644 index 0000000000..3c52eccd90 --- /dev/null +++ b/wii/libogc/include/ogcsys.h @@ -0,0 +1,41 @@ +#ifndef __OGCSYS_H__ +#define __OGCSYS_H__ + +#include +#include + +#if defined(HW_RVL) + #define TB_BUS_CLOCK 243000000u + #define TB_CORE_CLOCK 729000000u +#elif defined(HW_DOL) + #define TB_BUS_CLOCK 162000000u + #define TB_CORE_CLOCK 486000000u +#endif +#define TB_TIMER_CLOCK (TB_BUS_CLOCK/4000) //4th of the bus frequency + +#define TB_MSPERSEC 1000 +#define TB_USPERSEC 1000000 +#define TB_NSPERSEC 1000000000 +#define TB_NSPERMS 1000000 +#define TB_NSPERUS 1000 +#define TB_USPERTICK 10000 + +#define TB_SECSPERMIN 60 +#define TB_MINSPERHR 60 +#define TB_MONSPERYR 12 +#define TB_DAYSPERYR 365 +#define TB_HRSPERDAY 24 +#define TB_SECSPERDAY (TB_SECSPERMIN*TB_MINSPERHR*TB_HRSPERDAY) +#define TB_SECSPERNYR (365*TB_SECSPERDAY) + +#ifdef __cplusplus + extern "C" { +#endif + +int nanosleep(struct timespec *tb); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/samplerate.h b/wii/libogc/include/samplerate.h new file mode 100644 index 0000000000..9232dc29da --- /dev/null +++ b/wii/libogc/include/samplerate.h @@ -0,0 +1,196 @@ +/* +** Copyright (C) 2002-2004 Erik de Castro Lopo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* +** API documentation is available here: +** http://www.mega-nerd.com/SRC/api.html +*/ + +#ifndef SAMPLERATE_H +#define SAMPLERATE_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* Opaque data type SRC_STATE. */ +typedef struct SRC_STATE_tag SRC_STATE ; + +/* SRC_DATA is used to pass data to src_simple() and src_process(). */ +typedef struct +{ float *data_in, *data_out ; + + long input_frames, output_frames ; + long input_frames_used, output_frames_gen ; + + int end_of_input ; + + double src_ratio ; +} SRC_DATA ; + +/* SRC_CB_DATA is used with callback based API. */ +typedef struct +{ long frames ; + float *data_in ; +} SRC_CB_DATA ; + +/* +** User supplied callback function type for use with src_callback_new() +** and src_callback_read(). First parameter is the same pointer that was +** passed into src_callback_new(). Second parameter is pointer to a +** pointer. The user supplied callback function must modify *data to +** point to the start of the user supplied float array. The user supplied +** function must return the number of frames that **data points to. +*/ + +typedef long (*src_callback_t) (void *cb_data, float **data) ; + +/* +** Standard initialisation function : return an anonymous pointer to the +** internal state of the converter. Choose a converter from the enums below. +** Error returned in *error. +*/ + +SRC_STATE* src_new (int converter_type, int channels, int *error) ; + +/* +** Initilisation for callback based API : return an anonymous pointer to the +** internal state of the converter. Choose a converter from the enums below. +** The cb_data pointer can point to any data or be set to NULL. Whatever the +** value, when processing, user supplied function "func" gets called with +** cb_data as first parameter. +*/ + +SRC_STATE* src_callback_new (src_callback_t func, int converter_type, int channels, + int *error, void* cb_data) ; + +/* +** Cleanup all internal allocations. +** Always returns NULL. +*/ + +SRC_STATE* src_delete (SRC_STATE *state) ; + +/* +** Standard processing function. +** Returns non zero on error. +*/ + +int src_process (SRC_STATE *state, SRC_DATA *data) ; + +/* +** Callback based processing function. Read up to frames worth of data from +** the converter int *data and return frames read or -1 on error. +*/ +long src_callback_read (SRC_STATE *state, double src_ratio, long frames, float *data) ; + +/* +** Simple interface for performing a single conversion from input buffer to +** output buffer at a fixed conversion ratio. +** Simple interface does not require initialisation as it can only operate on +** a single buffer worth of audio. +*/ + +int src_simple (SRC_DATA *data, int converter_type, int channels) ; + +/* +** This library contains a number of different sample rate converters, +** numbered 0 through N. +** +** Return a string giving either a name or a more full description of each +** sample rate converter or NULL if no sample rate converter exists for +** the given value. The converters are sequentially numbered from 0 to N. +*/ + +const char *src_get_name (int converter_type) ; +const char *src_get_description (int converter_type) ; +const char *src_get_version (void) ; + +/* +** Set a new SRC ratio. This allows step responses +** in the conversion ratio. +** Returns non zero on error. +*/ + +int src_set_ratio (SRC_STATE *state, double new_ratio) ; + +/* +** Reset the internal SRC state. +** Does not modify the quality settings. +** Does not free any memory allocations. +** Returns non zero on error. +*/ + +int src_reset (SRC_STATE *state) ; + +/* +** Return TRUE if ratio is a valid conversion ratio, FALSE +** otherwise. +*/ + +int src_is_valid_ratio (double ratio) ; + +/* +** Return an error number. +*/ + +int src_error (SRC_STATE *state) ; + +/* +** Convert the error number into a string. +*/ +const char* src_strerror (int error) ; + +/* +** The following enums can be used to set the interpolator type +** using the function src_set_converter(). +*/ + +enum +{ + SRC_SINC_BEST_QUALITY = 0, + SRC_SINC_MEDIUM_QUALITY = 1, + SRC_SINC_FASTEST = 2, + SRC_ZERO_ORDER_HOLD = 3, + SRC_LINEAR = 4 +} ; + +/* +** Extra helper functions for converting from short to float and +** back again. +*/ + +void src_short_to_float_array (const short *in, float *out, int len) ; +void src_float_to_short_array (const float *in, short *out, int len) ; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* SAMPLERATE_H */ + +/* +** Do not edit or modify anything in this comment block. +** The arch-tag line is a file identity tag for the GNU Arch +** revision control system. +** +** arch-tag: 5421ef3e-c898-4ec3-8671-ea03d943ee00 +*/ + diff --git a/wii/libogc/include/sdcard/card_buf.h b/wii/libogc/include/sdcard/card_buf.h new file mode 100644 index 0000000000..0590f2e176 --- /dev/null +++ b/wii/libogc/include/sdcard/card_buf.h @@ -0,0 +1,18 @@ +#ifndef __CARD_BUF_H__ +#define __CARD_BUF_H__ + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +void sdgecko_initBufferPool(); +u8* sdgecko_allocBuffer(); +void sdgecko_freeBuffer(u8 *buf); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/sdcard/card_cmn.h b/wii/libogc/include/sdcard/card_cmn.h new file mode 100644 index 0000000000..e35cece8a1 --- /dev/null +++ b/wii/libogc/include/sdcard/card_cmn.h @@ -0,0 +1,65 @@ +#ifndef __CARD_CMN_H__ +#define __CARD_CMN_H__ + +#include + +#define CARDIO_ERROR_READY 0 +#define CARDIO_ERROR_BUSY -1 +#define CARDIO_ERROR_WRONGDEVICE -2 +#define CARDIO_ERROR_NOCARD -3 +#define CARDIO_ERROR_IDLE -4 +#define CARDIO_ERROR_IOERROR -5 +#define CARDIO_ERROR_IOTIMEOUT -6 + +#define CARDIO_ERROR_NOTPERMITTED -107 +#define CARDIO_ERROR_ROOTENTRY -108 +#define CARDIO_ERROR_OUTOFROOTENTRY -109 +#define CARDIO_ERROR_FILEEXIST -110 +#define CARDIO_ERROR_NOEMPTYCLUSTER -111 +#define CARDIO_ERROR_EOF -112 +#define CARDIO_ERROR_SYSTEMPARAM -113 +#define CARDIO_ERROR_FILEOPENED -114 +#define CARDIO_ERROR_FILENOTOPENED -115 +#define CARDIO_ERROR_FILENAMETOOLONG -116 +#define CARDIO_ERROR_INVALIDNAME -117 +#define CARDIO_ERROR_NOLONGNAME -118 +#define CARDIO_ERROR_INCORRECTFAT -119 +#define CARDIO_ERROR_NOTFOUND -120 +#define CARDIO_ERROR_OUTOFMEMORY -121 +#define CARDIO_ERROR_INVALIDFAT -122 +#define CARDIO_ERROR_INVALIDMBR -123 +#define CARDIO_ERROR_INVALIDPBR -124 +#define CARDIO_ERROR_NOEMPTYBLOCK -125 +#define CARDIO_ERROR_INTERNAL -126 +#define CARDIO_ERROR_INVALIDPARAM -127 +#define CARDIO_ERROR_FATALERROR -128 + +#define MAX_DRIVE 2 +#define SECTOR_SIZE 512 + +#define NOT_INITIALIZED 0 +#define INITIALIZING 1 +#define INITIALIZED 2 + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef struct _dev_info { + u32 CpV; + u32 HpC; + u32 SpH; + u32 allS; + u32 szS; + u32 PBpV; + u32 LBpV; + u32 SpB; + u32 PpB; + u32 szP; +} DEV_INFO; + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/sdcard/card_io.h b/wii/libogc/include/sdcard/card_io.h new file mode 100644 index 0000000000..b0c2bdc166 --- /dev/null +++ b/wii/libogc/include/sdcard/card_io.h @@ -0,0 +1,51 @@ +#ifndef __CARD_IO_H__ +#define __CARD_IO_H__ + +#include + +#define MAX_MI_NUM 1 +#define MAX_DI_NUM 5 + +#define PAGE_SIZE256 256 +#define PAGE_SIZE512 512 + +/* CID Register */ +#define MANUFACTURER_ID(drv_no) ((u8)(g_CID[drv_no][0])) + +/* CSD Register */ +#define READ_BL_LEN(drv_no) ((u8)(g_CSD[drv_no][5]&0x0f)) +#define WRITE_BL_LEN(drv_no) ((u8)((g_CSD[drv_no][12]&0x03)<<2)|((g_CSD[drv_no][13]>>6)&0x03)) +#define C_SIZE(drv_no) ((u16)(((g_CSD[drv_no][6]&0x03)<<10)|(g_CSD[drv_no][7]<<2)|((g_CSD[drv_no][8]>>6)&0x03))) +#define C_SIZE_MULT(drv_no) ((u8)((g_CSD[drv_no][9]&0x03)<<1)|((g_CSD[drv_no][10]>>7)&0x01)) + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +extern u8 g_CSD[MAX_DRIVE][16]; +extern u8 g_CID[MAX_DRIVE][16]; +extern u8 g_mCode[MAX_MI_NUM]; +extern u16 g_dCode[MAX_MI_NUM][MAX_DI_NUM]; + + +void sdgecko_initIODefault(); +s32 sdgecko_initIO(s32 drv_no); +s32 sdgecko_preIO(s32 drv_no); +s32 sdgecko_readCID(s32 drv_no); +s32 sdgecko_readCSD(s32 drv_no); +s32 sdgecko_readStatus(s32 drv_no); +s32 sdgecko_readSectors(s32 drv_no,u32 sector_no,u32 num_sectors,void *buf); +s32 sdgecko_writeSector(s32 drv_no,u32 sector_no,const void *buf,u32 len); +s32 sdgecko_writeSectors(s32 drv_no,u32 sector_no,u32 num_sectors,const void *buf); + +s32 sdgecko_doUnmount(s32 drv_no); + +void sdgecko_insertedCB(s32 drv_no); +void sdgecko_ejectedCB(s32 drv_no); + + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/include/sdcard/gcsd.h b/wii/libogc/include/sdcard/gcsd.h new file mode 100644 index 0000000000..b64b159548 --- /dev/null +++ b/wii/libogc/include/sdcard/gcsd.h @@ -0,0 +1,45 @@ +/* + + gcsd.h + + Hardware routines for reading and writing to SD geckos connected + to the memory card ports. + + These functions are just wrappers around libsdcard's functions. + + Copyright (c) 2008 Sven "svpe" Peter + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __GCSD_H__ +#define __GCSD_H__ + +#include +#include + +#define DEVICE_TYPE_GC_SD (('G'<<24)|('C'<<16)|('S'<<8)|'D') + +extern const DISC_INTERFACE __io_gcsda; +extern const DISC_INTERFACE __io_gcsdb; + +#endif diff --git a/wii/libogc/include/sdcard/wiisd_io.h b/wii/libogc/include/sdcard/wiisd_io.h new file mode 100644 index 0000000000..2c3de8a035 --- /dev/null +++ b/wii/libogc/include/sdcard/wiisd_io.h @@ -0,0 +1,44 @@ +/* + + wii_sd.h + + Hardware interface for libfat Wii internal SD + + Copyright (c) 2008 + Michael Wiedenbauer (shagkur) + Dave Murphy (WinterMute) + + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __WIISD_IO_H__ +#define __WIISD_IO_H__ + +#include +#include + +#define DEVICE_TYPE_WII_SD (('W'<<24)|('I'<<16)|('S'<<8)|'D') + +extern const DISC_INTERFACE __io_wiisd; + +#endif diff --git a/wii/libogc/include/smb.h b/wii/libogc/include/smb.h new file mode 100644 index 0000000000..579f1979c4 --- /dev/null +++ b/wii/libogc/include/smb.h @@ -0,0 +1,150 @@ +/**************************************************************************** + * TinySMB + * Nintendo Wii/GameCube SMB implementation + * + * Copyright softdev + * Modified by Tantric to utilize NTLM authentication + * PathInfo added by rodries + * SMB devoptab by scip, rodries + * + * You will find WireShark (http://www.wireshark.org/) + * invaluable for debugging SAMBA implementations. + * + * Recommended Reading + * Implementing CIFS - Christopher R Hertel + * http://www.ubiqx.org/cifs/SMB.html + * + * License: + * + * 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 + ****************************************************************************/ + +#ifndef __NBTSMB_H__ +#define __NBTSMB_H__ + +#include +#include + +#define SMB_MAXPATH 4096 + +/** +* SMB Error codes +*/ +#define SMB_SUCCESS 0 +#define SMB_ERROR -1 +#define SMB_BAD_PROTOCOL -2 +#define SMB_BAD_COMMAND -3 +#define SMB_PROTO_FAIL -4 +#define SMB_NOT_USER -5 +#define SMB_BAD_KEYLEN -6 +#define SMB_BAD_DATALEN -7 +#define SMB_BAD_LOGINDATA -8 + +/** +* SMB File Open Function +*/ +#define SMB_OF_OPEN 1 +#define SMB_OF_TRUNCATE 2 +#define SMB_OF_CREATE 16 + +/** +* FileSearch +*/ +#define SMB_SRCH_READONLY 1 +#define SMB_SRCH_HIDDEN 2 +#define SMB_SRCH_SYSTEM 4 +#define SMB_SRCH_VOLUME 8 +#define SMB_SRCH_DIRECTORY 16 +#define SMB_SRCH_ARCHIVE 32 + +/** +* SMB File Access Modes +*/ +#define SMB_OPEN_READING 0 +#define SMB_OPEN_WRITING 1 +#define SMB_OPEN_READWRITE 2 +#define SMB_OPEN_COMPATIBLE 0 +#define SMB_DENY_READWRITE 0x10 +#define SMB_DENY_WRITE 0x20 +#define SMB_DENY_READ 0x30 +#define SMB_DENY_NONE 0x40 + + +#ifdef __cplusplus +extern "C" { +#endif + +/*** + * SMB Connection Handle + */ +typedef u32 SMBCONN; + +/*** + * SMB File Handle + */ +typedef void* SMBFILE; + +/*** SMB_FILEENTRY + SMB Long Filename Directory Entry + ***/ + typedef struct +{ + u64 size; + u64 ctime; + u64 atime; + u64 mtime; + u32 attributes; + u16 sid; + char name[768]; //unicode +} SMBDIRENTRY; + +/** + * Prototypes + */ + +/*** Functions to be used with stdio API ***/ +bool smbInitDevice(const char* name, const char *user, const char *password, const char *share, const char *ip); +bool smbInit(const char *user, const char *password, const char *share, const char *ip); +void smbClose(const char* name); +bool smbCheckConnection(const char* name); +void smbSetSearchFlags(unsigned short flags); + +/*** Session ***/ +s32 SMB_Connect(SMBCONN *smbhndl, const char *user, const char *password, const char *share, const char *IP); +void SMB_Close(SMBCONN smbhndl); +s32 SMB_Reconnect(SMBCONN *_smbhndl, bool test_conn); + +/*** File Find ***/ +s32 SMB_PathInfo(const char *filename, SMBDIRENTRY *sdir, SMBCONN smbhndl); +s32 SMB_FindFirst(const char *filename, unsigned short flags, SMBDIRENTRY *sdir, SMBCONN smbhndl); +s32 SMB_FindNext(SMBDIRENTRY *sdir,SMBCONN smbhndl); +s32 SMB_FindClose(SMBDIRENTRY *sdir,SMBCONN smbhndl); + +/*** File I/O ***/ +SMBFILE SMB_OpenFile(const char *filename, unsigned short access, unsigned short creation,SMBCONN smbhndl); +void SMB_CloseFile(SMBFILE sfid); +s32 SMB_ReadFile(char *buffer, size_t size, off_t offset, SMBFILE sfid); +s32 SMB_WriteFile(const char *buffer, size_t size, off_t offset, SMBFILE sfid); +s32 SMB_CreateDirectory(const char *dirname, SMBCONN smbhndl); +s32 SMB_DeleteDirectory(const char *dirname, SMBCONN smbhndl); +s32 SMB_DeleteFile(const char *filename, SMBCONN smbhndl); +s32 SMB_Rename(const char *filename, const char * newname, SMBCONN smbhndl); +s32 SMB_DiskInformation(struct statvfs *buf, SMBCONN smbhndl); + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/wii/libogc/include/wiiuse/wiiuse.h b/wii/libogc/include/wiiuse/wiiuse.h new file mode 100644 index 0000000000..7be2a7d9e9 --- /dev/null +++ b/wii/libogc/include/wiiuse/wiiuse.h @@ -0,0 +1,748 @@ +#ifndef __WIIUSE_H__ +#define __WIIUSE_H__ + +#if defined(_WIN32) + /* windows */ + #include +#elif defined(GEKKO) + /* wii */ + #include +#elif defined(__linux__) + /* nix */ + #include +#endif + +#ifdef WIIUSE_INTERNAL_H_INCLUDED + #define WCONST +#else + #define WCONST const +#endif + +/* led bit masks */ +#define WIIMOTE_LED_NONE 0x00 +#define WIIMOTE_LED_1 0x10 +#define WIIMOTE_LED_2 0x20 +#define WIIMOTE_LED_3 0x40 +#define WIIMOTE_LED_4 0x80 + +/* button codes */ +#define WIIMOTE_BUTTON_TWO 0x0001 +#define WIIMOTE_BUTTON_ONE 0x0002 +#define WIIMOTE_BUTTON_B 0x0004 +#define WIIMOTE_BUTTON_A 0x0008 +#define WIIMOTE_BUTTON_MINUS 0x0010 +#define WIIMOTE_BUTTON_ZACCEL_BIT6 0x0020 +#define WIIMOTE_BUTTON_ZACCEL_BIT7 0x0040 +#define WIIMOTE_BUTTON_HOME 0x0080 +#define WIIMOTE_BUTTON_LEFT 0x0100 +#define WIIMOTE_BUTTON_RIGHT 0x0200 +#define WIIMOTE_BUTTON_DOWN 0x0400 +#define WIIMOTE_BUTTON_UP 0x0800 +#define WIIMOTE_BUTTON_PLUS 0x1000 +#define WIIMOTE_BUTTON_ZACCEL_BIT4 0x2000 +#define WIIMOTE_BUTTON_ZACCEL_BIT5 0x4000 +#define WIIMOTE_BUTTON_UNKNOWN 0x8000 +#define WIIMOTE_BUTTON_ALL 0x1F9F + +/* nunchul button codes */ +#define NUNCHUK_BUTTON_Z 0x01 +#define NUNCHUK_BUTTON_C 0x02 +#define NUNCHUK_BUTTON_ALL 0x03 + +/* classic controller button codes */ +#define CLASSIC_CTRL_BUTTON_UP 0x0001 +#define CLASSIC_CTRL_BUTTON_LEFT 0x0002 +#define CLASSIC_CTRL_BUTTON_ZR 0x0004 +#define CLASSIC_CTRL_BUTTON_X 0x0008 +#define CLASSIC_CTRL_BUTTON_A 0x0010 +#define CLASSIC_CTRL_BUTTON_Y 0x0020 +#define CLASSIC_CTRL_BUTTON_B 0x0040 +#define CLASSIC_CTRL_BUTTON_ZL 0x0080 +#define CLASSIC_CTRL_BUTTON_FULL_R 0x0200 +#define CLASSIC_CTRL_BUTTON_PLUS 0x0400 +#define CLASSIC_CTRL_BUTTON_HOME 0x0800 +#define CLASSIC_CTRL_BUTTON_MINUS 0x1000 +#define CLASSIC_CTRL_BUTTON_FULL_L 0x2000 +#define CLASSIC_CTRL_BUTTON_DOWN 0x4000 +#define CLASSIC_CTRL_BUTTON_RIGHT 0x8000 +#define CLASSIC_CTRL_BUTTON_ALL 0xFEFF + +/* guitar hero 3 button codes */ +#define GUITAR_HERO_3_BUTTON_STRUM_UP 0x0001 +#define GUITAR_HERO_3_BUTTON_YELLOW 0x0008 +#define GUITAR_HERO_3_BUTTON_GREEN 0x0010 +#define GUITAR_HERO_3_BUTTON_BLUE 0x0020 +#define GUITAR_HERO_3_BUTTON_RED 0x0040 +#define GUITAR_HERO_3_BUTTON_ORANGE 0x0080 +#define GUITAR_HERO_3_BUTTON_PLUS 0x0400 +#define GUITAR_HERO_3_BUTTON_MINUS 0x1000 +#define GUITAR_HERO_3_BUTTON_STRUM_DOWN 0x4000 +#define GUITAR_HERO_3_BUTTON_ALL 0xFEFF + +/* guitar hero world tour touch bar codes */ +#define GUITAR_HERO_3_TOUCH_AVAILABLE 0x1000 +#define GUITAR_HERO_3_TOUCH_GREEN 0x1001 +#define GUITAR_HERO_3_TOUCH_RED 0x1002 +#define GUITAR_HERO_3_TOUCH_YELLOW 0x1004 +#define GUITAR_HERO_3_TOUCH_BLUE 0x1008 +#define GUITAR_HERO_3_TOUCH_ORANGE 0x1010 + +/* wiimote option flags */ +#define WIIUSE_SMOOTHING 0x01 +#define WIIUSE_CONTINUOUS 0x02 +#define WIIUSE_ACCEL_THRESH 0x04 +#define WIIUSE_IR_THRESH 0x08 +#define WIIUSE_JS_THRESH 0x10 +#define WIIUSE_INIT_FLAGS WIIUSE_SMOOTHING + +#define WIIUSE_ORIENT_PRECISION 100.0f + +/* expansion codes */ +#define EXP_NONE 0 +#define EXP_NUNCHUK 1 +#define EXP_CLASSIC 2 +#define EXP_GUITAR_HERO_3 3 +#define EXP_WII_BOARD 4 +#define EXP_MOTION_PLUS 5 + +/* IR correction types */ +typedef enum ir_position_t { + WIIUSE_IR_ABOVE, + WIIUSE_IR_BELOW +} ir_position_t; + +/** + * @brief Check if a button is pressed. + * @param dev Pointer to a wiimote_t or expansion structure. + * @param button The button you are interested in. + * @return 1 if the button is pressed, 0 if not. + */ +#define IS_PRESSED(dev, button) ((dev->btns & button) == button) + +/** + * @brief Check if a button is being held. + * @param dev Pointer to a wiimote_t or expansion structure. + * @param button The button you are interested in. + * @return 1 if the button is held, 0 if not. + */ +#define IS_HELD(dev, button) ((dev->btns_held & button) == button) + +/** + * @brief Check if a button is released on this event. \n\n + * This does not mean the button is not pressed, it means \n + * this button was just now released. + * @param dev Pointer to a wiimote_t or expansion structure. + * @param button The button you are interested in. + * @return 1 if the button is released, 0 if not. + * + */ +#define IS_RELEASED(dev, button) ((dev->btns_released & button) == button) + +/** + * @brief Check if a button has just been pressed this event. + * @param dev Pointer to a wiimote_t or expansion structure. + * @param button The button you are interested in. + * @return 1 if the button is pressed, 0 if not. + */ +#define IS_JUST_PRESSED(dev, button) (IS_PRESSED(dev, button) && !IS_HELD(dev, button)) + +/** + * @brief Return the IR sensitivity level. + * @param wm Pointer to a wiimote_t structure. + * @param lvl [out] Pointer to an int that will hold the level setting. + * If no level is set 'lvl' will be set to 0. + */ +#define WIIUSE_GET_IR_SENSITIVITY(dev, lvl) \ + do { \ + if ((wm->state & 0x01000) == 0x01000) *lvl = 1; \ + else if ((wm->state & 0x02000) == 0x02000) *lvl = 2; \ + else if ((wm->state & 0x04000) == 0x04000) *lvl = 3; \ + else if ((wm->state & 0x08000) == 0x08000) *lvl = 4; \ + else if ((wm->state & 0x10000) == 0x10000) *lvl = 5; \ + else *lvl = 0; \ + } while (0) + +#define WIIUSE_USING_ACC(wm) ((wm->state & 0x00100) == 0x00100) +#define WIIUSE_USING_EXP(wm) ((wm->state & 0x00200) == 0x00200) +#define WIIUSE_USING_IR(wm) ((wm->state & 0x00400) == 0x00400) +#define WIIUSE_USING_SPEAKER(wm) ((wm->state & 0x00800) == 0x00800) + +#define WIIUSE_IS_LED_SET(wm, num) ((wm->leds & WIIMOTE_LED_##num) == WIIMOTE_LED_##num) + +/* + * Largest known payload is 21 bytes. + * Add 2 for the prefix and round up to a power of 2. + */ +#define MAX_PAYLOAD 32 + +/* + * This is left over from an old hack, but it may actually + * be a useful feature to keep so it wasn't removed. + */ +#ifdef WIN32 + #define WIIMOTE_DEFAULT_TIMEOUT 100 + #define WIIMOTE_EXP_TIMEOUT 100 +#endif + +typedef unsigned char ubyte; +typedef char sbyte; +typedef unsigned short uword; +typedef short sword; +typedef unsigned int uint; +typedef char sint; + + +struct wiimote_t; +struct vec3b_t; +struct orient_t; +struct gforce_t; + +#ifdef GEKKO + typedef void (*wii_event_cb)(struct wiimote_t*, s32 event); +#endif + +/** + * @brief Callback that handles a read event. + * + * @param wm Pointer to a wiimote_t structure. + * @param data Pointer to the filled data block. + * @param len Length in bytes of the data block. + * + * @see wiiuse_init() + * + * A registered function of this type is called automatically by the wiiuse + * library when the wiimote has returned the full data requested by a previous + * call to wiiuse_read_data(). + */ +typedef void (*wiiuse_data_cb)(struct wiimote_t* wm, ubyte* data, unsigned short len); + +typedef enum data_req_s +{ + REQ_READY = 0, + REQ_SENT, + REQ_DONE +} data_req_s; + +/** + * @struct data_req_t + * @brief Data read request structure. + */ +struct data_req_t { + lwp_node node; + ubyte data[48]; /**< buffer where read data is written */ + unsigned int len; + data_req_s state; /**< set to 1 if not using callback and needs to be cleaned up */ + wiiuse_data_cb cb; /**< read data callback */ + struct data_req_t *next; +}; + +typedef void (*cmd_blk_cb)(struct wiimote_t *wm,ubyte *data,uword len); + +typedef enum cmd_blk_s +{ + CMD_READY = 0, + CMD_SENT, + CMD_DONE +} cmd_blk_s; + +struct cmd_blk_t +{ + lwp_node node; + + ubyte data[48]; + uint len; + + cmd_blk_s state; + cmd_blk_cb cb; + + struct cmd_blk_t *next; +}; + + +/** + * @struct vec2b_t + * @brief Unsigned x,y byte vector. + */ +typedef struct vec2b_t { + ubyte x, y; +} vec2b_t; + + +/** +* @struct vec3b_t +* @brief Unsigned x,y,z byte vector. +*/ +typedef struct vec3b_t { + ubyte x, y, z; +} vec3b_t; + +/** +* @struct vec3b_t +* @brief Unsigned x,y,z byte vector. +*/ +typedef struct vec3w_t { + uword x, y, z; +} vec3w_t; + + +/** + * @struct vec3f_t + * @brief Signed x,y,z float struct. + */ +typedef struct vec3f_t { + float x, y, z; +} vec3f_t; + + +/** + * @struct orient_t + * @brief Orientation struct. + * + * Yaw, pitch, and roll range from -180 to 180 degrees. + */ +typedef struct orient_t { + float roll; /**< roll, this may be smoothed if enabled */ + float pitch; /**< pitch, this may be smoothed if enabled */ + float yaw; + + float a_roll; /**< absolute roll, unsmoothed */ + float a_pitch; /**< absolute pitch, unsmoothed */ +} orient_t; + + +/** + * @struct gforce_t + * @brief Gravity force struct. + */ +typedef struct gforce_t { + float x, y, z; +} gforce_t; + + +/** + * @struct accel_t + * @brief Accelerometer struct. For any device with an accelerometer. + */ +typedef struct accel_t { + struct vec3w_t cal_zero; /**< zero calibration */ + struct vec3w_t cal_g; /**< 1g difference around 0cal */ + + float st_roll; /**< last smoothed roll value */ + float st_pitch; /**< last smoothed roll pitch */ + float st_alpha; /**< alpha value for smoothing [0-1] */ +} accel_t; + + +/** + * @struct ir_dot_t + * @brief A single IR source. + */ +typedef struct ir_dot_t { + ubyte visible; /**< if the IR source is visible */ + + short rx; /**< raw X coordinate (0-1023) */ + short ry; /**< raw Y coordinate (0-767) */ + + ubyte size; /**< size of the IR dot (0-15) */ +} ir_dot_t; + + +typedef struct fdot_t { + float x,y; +} fdot_t; + +typedef struct sb_t { + fdot_t dots[2]; + fdot_t acc_dots[2]; + fdot_t rot_dots[2]; + float angle; + float off_angle; + float score; +} sb_t; + +/** + * @enum aspect_t + * @brief Screen aspect ratio. + */ +typedef enum aspect_t { + WIIUSE_ASPECT_4_3, + WIIUSE_ASPECT_16_9 +} aspect_t; + + +/** + * @struct ir_t + * @brief IR struct. Hold all data related to the IR tracking. + */ +typedef struct ir_t { + struct ir_dot_t dot[4]; /**< IR dots */ + ubyte num_dots; /**< number of dots at this time */ + + int state; /**< keeps track of the IR state */ + + int raw_valid; /**< is the raw position valid? */ + sb_t sensorbar; /**< sensor bar, detected or guessed */ + float ax; /**< raw X coordinate */ + float ay; /**< raw Y coordinate */ + float distance; /**< pixel width of the sensor bar */ + float z; /**< calculated distance in meters */ + float angle; /**< angle of the wiimote to the sensor bar*/ + + int smooth_valid; /**< is the smoothed position valid? */ + float sx; /**< smoothed X coordinate */ + float sy; /**< smoothed Y coordinate */ + float error_cnt; /**< error count, for smoothing algorithm*/ + float glitch_cnt; /**< glitch count, same */ + + int valid; /**< is the bounded position valid? */ + float x; /**< bounded X coordinate */ + float y; /**< bounded Y coordinate */ + enum aspect_t aspect; /**< aspect ratio of the screen */ + enum ir_position_t pos; /**< IR sensor bar position */ + unsigned int vres[2]; /**< IR virtual screen resolution */ + int offset[2]; /**< IR XY correction offset */ + +} ir_t; + + +/** + * @struct joystick_t + * @brief Joystick calibration structure. + * + * The angle \a ang is relative to the positive y-axis into quadrant I + * and ranges from 0 to 360 degrees. So if the joystick is held straight + * upwards then angle is 0 degrees. If it is held to the right it is 90, + * down is 180, and left is 270. + * + * The magnitude \a mag is the distance from the center to where the + * joystick is being held. The magnitude ranges from 0 to 1. + * If the joystick is only slightly tilted from the center the magnitude + * will be low, but if it is closer to the outter edge the value will + * be higher. + */ +typedef struct joystick_t { + struct vec2b_t max; /**< maximum joystick values */ + struct vec2b_t min; /**< minimum joystick values */ + struct vec2b_t center; /**< center joystick values */ + struct vec2b_t pos; /**< raw position values */ + + float ang; /**< angle the joystick is being held */ + float mag; /**< magnitude of the joystick (range 0-1) */ +} joystick_t; + + +/** + * @struct nunchuk_t + * @brief Nunchuk expansion device. + */ +typedef struct nunchuk_t { + struct accel_t accel_calib; /**< nunchuk accelerometer calibration */ + struct joystick_t js; /**< joystick calibration */ + + int* flags; /**< options flag (points to wiimote_t.flags) */ + + ubyte btns; /**< what buttons have just been pressed */ + ubyte btns_last; /**< what buttons have just been pressed */ + ubyte btns_held; /**< what buttons are being held down */ + ubyte btns_released; /**< what buttons were just released this */ + + struct vec3w_t accel; /**< current raw acceleration data */ + struct orient_t orient; /**< current orientation on each axis */ + struct gforce_t gforce; /**< current gravity forces on each axis */ +} nunchuk_t; + + +/** + * @struct classic_ctrl_t + * @brief Classic controller expansion device. + */ +typedef struct classic_ctrl_t { + short btns; /**< what buttons have just been pressed */ + short btns_last; /**< what buttons have just been pressed */ + short btns_held; /**< what buttons are being held down */ + short btns_released; /**< what buttons were just released this */ + + ubyte rs_raw; + ubyte ls_raw; + + float r_shoulder; /**< right shoulder button (range 0-1) */ + float l_shoulder; /**< left shoulder button (range 0-1) */ + + struct joystick_t ljs; /**< left joystick calibration */ + struct joystick_t rjs; /**< right joystick calibration */ + ubyte type; /**< original, pro, wiiu pro */ +} classic_ctrl_t; + + +/** + * @struct guitar_hero_3_t + * @brief Guitar Hero 3 expansion device. + */ +typedef struct guitar_hero_3_t { + short btns; /**< what buttons have just been pressed */ + short btns_last; /**< what buttons have just been pressed */ + short btns_held; /**< what buttons are being held down */ + short btns_released; /**< what buttons were just released this */ + + ubyte wb_raw; + float whammy_bar; /**< whammy bar (range 0-1) */ + + ubyte tb_raw; + int touch_bar; /**< touch bar */ + + struct joystick_t js; /**< joystick calibration */ +} guitar_hero_3_t; + +/** + * @struct wii_board_t + * @brief Wii Balance Board expansion device. + */ +typedef struct wii_board_t { + float tl; /* Interpolated */ + float tr; + float bl; + float br; /* End interp */ + short rtl; /* RAW */ + short rtr; + short rbl; + short rbr; /* /RAW */ + short ctl[3]; /* Calibration */ + short ctr[3]; + short cbl[3]; + short cbr[3]; /* /Calibration */ + float x; + float y; +} wii_board_t; + +typedef struct motion_plus_t +{ + short rx, ry, rz; + ubyte status; + ubyte ext; +} motion_plus_t; + +/** + * @struct expansion_t + * @brief Generic expansion device plugged into wiimote. + */ +typedef struct expansion_t { + int type; /**< type of expansion attached */ + + union { + struct nunchuk_t nunchuk; + struct classic_ctrl_t classic; + struct guitar_hero_3_t gh3; + struct wii_board_t wb; + struct motion_plus_t mp; + }; +} expansion_t; + + +/** + * @enum win32_bt_stack_t + * @brief Available bluetooth stacks for Windows. + */ +typedef enum win_bt_stack_t { + WIIUSE_STACK_UNKNOWN, + WIIUSE_STACK_MS, + WIIUSE_STACK_BLUESOLEIL +} win_bt_stack_t; + + +/** + * @struct wiimote_state_t + * @brief Significant data from the previous event. + */ +typedef struct wiimote_state_t { + unsigned short btns; + + struct ir_t ir; + struct vec3w_t accel; + struct expansion_t exp; +} wiimote_state_t; + + +/** + * @enum WIIUSE_EVENT_TYPE + * @brief Events that wiiuse can generate from a poll. + */ +typedef enum WIIUSE_EVENT_TYPE { + WIIUSE_NONE = 0, + WIIUSE_EVENT, + WIIUSE_STATUS, + WIIUSE_CONNECT, + WIIUSE_DISCONNECT, + WIIUSE_UNEXPECTED_DISCONNECT, + WIIUSE_READ_DATA, + WIIUSE_ACK, + WIIUSE_NUNCHUK_INSERTED, + WIIUSE_NUNCHUK_REMOVED, + WIIUSE_CLASSIC_CTRL_INSERTED, + WIIUSE_CLASSIC_CTRL_REMOVED, + WIIUSE_GUITAR_HERO_3_CTRL_INSERTED, + WIIUSE_GUITAR_HERO_3_CTRL_REMOVED, + WIIUSE_WII_BOARD_INSERTED, + WIIUSE_WII_BOARD_REMOVED, + WIIUSE_MOTION_PLUS_ACTIVATED, + WIIUSE_MOTION_PLUS_REMOVED +} WIIUSE_EVENT_TYPE; + +/** + * @struct wiimote_t + * @brief Wiimote structure. + */ +typedef struct wiimote_t { + WCONST int unid; /**< user specified id */ + + #if defined(_WIN32) + WCONST HANDLE dev_handle; /**< HID handle */ + WCONST OVERLAPPED hid_overlap; /**< overlap handle */ + WCONST enum win_bt_stack_t stack; /**< type of bluetooth stack to use */ + WCONST int timeout; /**< read timeout */ + WCONST ubyte normal_timeout; /**< normal timeout */ + WCONST ubyte exp_timeout; /**< timeout for expansion handshake */ + #elif defined(GEKKO) + WCONST lwp_queue cmdq; + WCONST struct bd_addr bdaddr; /**< bt address */ + WCONST char bdaddr_str[18]; /**< readable bt address */ + WCONST struct bte_pcb *sock; /**< output socket */ + WCONST wii_event_cb event_cb; /**< event callback */ + #elif defined(unix) + WCONST bdaddr_t bdaddr; /**< bt address */ + WCONST char bdaddr_str[18]; /**< readable bt address */ + WCONST int out_sock; /**< output socket */ + WCONST int in_sock; /**< input socket */ + #endif + + WCONST int state; /**< various state flags */ + WCONST ubyte leds; /**< currently lit leds */ +#ifdef GEKKO + WCONST ubyte battery_level; /**< battery level */ +#else + WCONST float battery_level; /**< battery level */ +#endif + + WCONST int flags; /**< options flag */ + + WCONST ubyte handshake_state; /**< the state of the connection handshake */ + WCONST ubyte expansion_state; /**< the state of the expansion handshake */ + + WCONST struct data_req_t* data_req; /**< list of data read requests */ + + WCONST struct cmd_blk_t *cmd_head; + WCONST struct cmd_blk_t *cmd_tail; + + WCONST struct accel_t accel_calib; /**< wiimote accelerometer calibration */ + WCONST struct expansion_t exp; /**< wiimote expansion device */ + + WCONST struct vec3w_t accel; /**< current raw acceleration data */ + WCONST struct orient_t orient; /**< current orientation on each axis */ + WCONST struct gforce_t gforce; /**< current gravity forces on each axis */ + + WCONST struct ir_t ir; /**< IR data */ + + WCONST unsigned short btns; /**< what buttons are down */ + WCONST unsigned short btns_last; /**< what buttons were down before */ + WCONST unsigned short btns_held; /**< what buttons are and were held down */ + WCONST unsigned short btns_released; /**< what buttons were just released */ + + WCONST struct wiimote_state_t lstate; /**< last saved state */ + + WCONST WIIUSE_EVENT_TYPE event; /**< type of event that occured */ + WCONST ubyte event_buf[MAX_PAYLOAD]; /**< event buffer */ + + WCONST ubyte motion_plus_id[6]; +} wiimote; + +#if defined(GEKKO) +/** + * @struct wiimote_listen_t + * @brief Wiimote listen structure. + */ +typedef struct wiimote_listen_t { + WCONST struct bd_addr bdaddr; + WCONST struct bte_pcb *sock; + WCONST struct wiimote_t *(*assign_cb)(struct bd_addr *bdaddr); + WCONST struct wiimote_t *wm; +} wiimote_listen; +#endif + +/***************************************** + * + * Include API specific stuff + * + *****************************************/ + +#ifdef _WIN32 + #define WIIUSE_EXPORT_DECL __declspec(dllexport) + #define WIIUSE_IMPORT_DECL __declspec(dllimport) +#else + #define WIIUSE_EXPORT_DECL + #define WIIUSE_IMPORT_DECL +#endif + +#ifdef WIIUSE_COMPILE_LIB + #define WIIUSE_EXPORT WIIUSE_EXPORT_DECL +#else + #define WIIUSE_EXPORT WIIUSE_IMPORT_DECL +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* wiiuse.c */ +WIIUSE_EXPORT extern const char* wiiuse_version(); + +#ifndef GEKKO +WIIUSE_EXPORT extern struct wiimote_t** wiiuse_init(int wiimotes); +#else +WIIUSE_EXPORT extern int wiiuse_register(struct wiimote_listen_t *wml, struct bd_addr *bdaddr, struct wiimote_t *(*assign_cb)(struct bd_addr *bdaddr)); +WIIUSE_EXPORT extern struct wiimote_t** wiiuse_init(int wiimotes, wii_event_cb event_cb); +WIIUSE_EXPORT extern void wiiuse_sensorbar_enable(int enable); +#endif + +WIIUSE_EXPORT extern void wiiuse_disconnected(struct wiimote_t* wm); +WIIUSE_EXPORT extern void wiiuse_cleanup(struct wiimote_t** wm, int wiimotes); +WIIUSE_EXPORT extern void wiiuse_rumble(struct wiimote_t* wm, int status); +WIIUSE_EXPORT extern void wiiuse_toggle_rumble(struct wiimote_t* wm); +WIIUSE_EXPORT extern void wiiuse_set_leds(struct wiimote_t* wm, int leds,cmd_blk_cb cb); +WIIUSE_EXPORT extern void wiiuse_motion_sensing(struct wiimote_t* wm, int status); +WIIUSE_EXPORT extern int wiiuse_read_data(struct wiimote_t* wm, ubyte* buffer, unsigned int offset, unsigned short len, cmd_blk_cb cb); +WIIUSE_EXPORT extern int wiiuse_write_data(struct wiimote_t *wm,unsigned int addr,ubyte *data,ubyte len,cmd_blk_cb cb); +WIIUSE_EXPORT extern void wiiuse_status(struct wiimote_t *wm,cmd_blk_cb cb); +WIIUSE_EXPORT extern struct wiimote_t* wiiuse_get_by_id(struct wiimote_t** wm, int wiimotes, int unid); +WIIUSE_EXPORT extern int wiiuse_set_flags(struct wiimote_t* wm, int enable, int disable); +WIIUSE_EXPORT extern float wiiuse_set_smooth_alpha(struct wiimote_t* wm, float alpha); +WIIUSE_EXPORT extern void wiiuse_set_bluetooth_stack(struct wiimote_t** wm, int wiimotes, enum win_bt_stack_t type); +WIIUSE_EXPORT extern void wiiuse_resync(struct wiimote_t* wm); +WIIUSE_EXPORT extern void wiiuse_set_timeout(struct wiimote_t** wm, int wiimotes, ubyte normal_timeout, ubyte exp_timeout); +WIIUSE_EXPORT extern int wiiuse_write_streamdata(struct wiimote_t *wm,ubyte *data,ubyte len,cmd_blk_cb cb); + +/* connect.c */ +WIIUSE_EXPORT extern int wiiuse_find(struct wiimote_t** wm, int max_wiimotes, int timeout); +WIIUSE_EXPORT extern int wiiuse_connect(struct wiimote_t** wm, int wiimotes); +WIIUSE_EXPORT extern void wiiuse_disconnect(struct wiimote_t* wm); + +/* events.c */ +WIIUSE_EXPORT extern int wiiuse_poll(struct wiimote_t** wm, int wiimotes); + +/* ir.c */ +WIIUSE_EXPORT extern void wiiuse_set_ir_mode(struct wiimote_t *wm); +WIIUSE_EXPORT extern void wiiuse_set_ir(struct wiimote_t* wm, int status); +WIIUSE_EXPORT extern void wiiuse_set_ir_vres(struct wiimote_t* wm, unsigned int x, unsigned int y); +WIIUSE_EXPORT extern void wiiuse_set_ir_position(struct wiimote_t* wm, enum ir_position_t pos); +WIIUSE_EXPORT extern void wiiuse_set_aspect_ratio(struct wiimote_t* wm, enum aspect_t aspect); +WIIUSE_EXPORT extern void wiiuse_set_ir_sensitivity(struct wiimote_t* wm, int level); + +/* motion_plus.c */ +WIIUSE_EXPORT extern void wiiuse_set_motion_plus(struct wiimote_t *wm, int status); + +/* speaker.c */ +WIIUSE_EXPORT extern void wiiuse_set_speaker(struct wiimote_t *wm, int status); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wii/libogc/include/wiiuse/wpad.h b/wii/libogc/include/wiiuse/wpad.h new file mode 100644 index 0000000000..3f7f5c496e --- /dev/null +++ b/wii/libogc/include/wiiuse/wpad.h @@ -0,0 +1,208 @@ +/*------------------------------------------------------------- + +wpad.h -- Wiimote Application Programmers Interface + +Copyright (C) 2008 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) +Hector Martin (marcan) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#ifndef __WPAD_H__ +#define __WPAD_H__ + +#include +#include + +#define WPAD_MAX_IR_DOTS 4 + +enum { + WPAD_CHAN_ALL = -1, + WPAD_CHAN_0, + WPAD_CHAN_1, + WPAD_CHAN_2, + WPAD_CHAN_3, + WPAD_BALANCE_BOARD, + WPAD_MAX_WIIMOTES, +}; + +#define WPAD_BUTTON_2 0x0001 +#define WPAD_BUTTON_1 0x0002 +#define WPAD_BUTTON_B 0x0004 +#define WPAD_BUTTON_A 0x0008 +#define WPAD_BUTTON_MINUS 0x0010 +#define WPAD_BUTTON_HOME 0x0080 +#define WPAD_BUTTON_LEFT 0x0100 +#define WPAD_BUTTON_RIGHT 0x0200 +#define WPAD_BUTTON_DOWN 0x0400 +#define WPAD_BUTTON_UP 0x0800 +#define WPAD_BUTTON_PLUS 0x1000 + +#define WPAD_NUNCHUK_BUTTON_Z (0x0001<<16) +#define WPAD_NUNCHUK_BUTTON_C (0x0002<<16) + +#define WPAD_CLASSIC_BUTTON_UP (0x0001<<16) +#define WPAD_CLASSIC_BUTTON_LEFT (0x0002<<16) +#define WPAD_CLASSIC_BUTTON_ZR (0x0004<<16) +#define WPAD_CLASSIC_BUTTON_X (0x0008<<16) +#define WPAD_CLASSIC_BUTTON_A (0x0010<<16) +#define WPAD_CLASSIC_BUTTON_Y (0x0020<<16) +#define WPAD_CLASSIC_BUTTON_B (0x0040<<16) +#define WPAD_CLASSIC_BUTTON_ZL (0x0080<<16) +#define WPAD_CLASSIC_BUTTON_FULL_R (0x0200<<16) +#define WPAD_CLASSIC_BUTTON_PLUS (0x0400<<16) +#define WPAD_CLASSIC_BUTTON_HOME (0x0800<<16) +#define WPAD_CLASSIC_BUTTON_MINUS (0x1000<<16) +#define WPAD_CLASSIC_BUTTON_FULL_L (0x2000<<16) +#define WPAD_CLASSIC_BUTTON_DOWN (0x4000<<16) +#define WPAD_CLASSIC_BUTTON_RIGHT (0x8000<<16) + +#define WPAD_GUITAR_HERO_3_BUTTON_STRUM_UP (0x0001<<16) +#define WPAD_GUITAR_HERO_3_BUTTON_YELLOW (0x0008<<16) +#define WPAD_GUITAR_HERO_3_BUTTON_GREEN (0x0010<<16) +#define WPAD_GUITAR_HERO_3_BUTTON_BLUE (0x0020<<16) +#define WPAD_GUITAR_HERO_3_BUTTON_RED (0x0040<<16) +#define WPAD_GUITAR_HERO_3_BUTTON_ORANGE (0x0080<<16) +#define WPAD_GUITAR_HERO_3_BUTTON_PLUS (0x0400<<16) +#define WPAD_GUITAR_HERO_3_BUTTON_MINUS (0x1000<<16) +#define WPAD_GUITAR_HERO_3_BUTTON_STRUM_DOWN (0x4000<<16) + +enum { + WPAD_EXP_NONE = 0, + WPAD_EXP_NUNCHUK, + WPAD_EXP_CLASSIC, + WPAD_EXP_GUITARHERO3, + WPAD_EXP_WIIBOARD, + WPAD_EXP_UNKNOWN = 255 +}; + +enum { + WPAD_FMT_BTNS = 0, + WPAD_FMT_BTNS_ACC, + WPAD_FMT_BTNS_ACC_IR +}; + +enum { + WPAD_STATE_DISABLED, + WPAD_STATE_ENABLING, + WPAD_STATE_ENABLED +}; + +#define WPAD_ERR_NONE 0 +#define WPAD_ERR_NO_CONTROLLER -1 +#define WPAD_ERR_NOT_READY -2 +#define WPAD_ERR_TRANSFER -3 +#define WPAD_ERR_NONEREGISTERED -4 +#define WPAD_ERR_UNKNOWN -5 +#define WPAD_ERR_BAD_CHANNEL -6 +#define WPAD_ERR_QUEUE_EMPTY -7 +#define WPAD_ERR_BADVALUE -8 +#define WPAD_ERR_BADCONF -9 + +#define WPAD_DATA_BUTTONS 0x01 +#define WPAD_DATA_ACCEL 0x02 +#define WPAD_DATA_EXPANSION 0x04 +#define WPAD_DATA_IR 0x08 + +#define WPAD_ENC_FIRST 0x00 +#define WPAD_ENC_CONT 0x01 + +#define WPAD_THRESH_IGNORE -1 +#define WPAD_THRESH_ANY 0 +#define WPAD_THRESH_DEFAULT_BUTTONS 0 +#define WPAD_THRESH_DEFAULT_IR WPAD_THRESH_IGNORE +#define WPAD_THRESH_DEFAULT_ACCEL 20 +#define WPAD_THRESH_DEFAULT_JOYSTICK 2 +#define WPAD_THRESH_DEFAULT_BALANCEBOARD 60 +#define WPAD_THRESH_DEFAULT_MOTION_PLUS 100 + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef struct _wpad_data +{ + s16 err; + + u32 data_present; + u8 battery_level; + + u32 btns_h; + u32 btns_l; + u32 btns_d; + u32 btns_u; + + struct ir_t ir; + struct vec3w_t accel; + struct orient_t orient; + struct gforce_t gforce; + struct expansion_t exp; +} WPADData; + +typedef struct _wpad_encstatus +{ + u8 data[32]; +}WPADEncStatus; + +typedef void (*WPADDataCallback)(s32 chan, const WPADData *data); +typedef void (*WPADShutdownCallback)(s32 chan); + +s32 WPAD_Init(); +s32 WPAD_ControlSpeaker(s32 chan,s32 enable); +s32 WPAD_ReadEvent(s32 chan, WPADData *data); +s32 WPAD_DroppedEvents(s32 chan); +s32 WPAD_Flush(s32 chan); +s32 WPAD_ReadPending(s32 chan, WPADDataCallback datacb); +s32 WPAD_SetDataFormat(s32 chan, s32 fmt); +s32 WPAD_SetMotionPlus(s32 chan, u8 enable); +s32 WPAD_SetVRes(s32 chan,u32 xres,u32 yres); +s32 WPAD_GetStatus(); +s32 WPAD_Probe(s32 chan,u32 *type); +s32 WPAD_SetEventBufs(s32 chan, WPADData *bufs, u32 cnt); +s32 WPAD_Disconnect(s32 chan); +s32 WPAD_IsSpeakerEnabled(s32 chan); +s32 WPAD_SendStreamData(s32 chan,void *buf,u32 len); +void WPAD_Shutdown(); +void WPAD_SetIdleTimeout(u32 seconds); +void WPAD_SetPowerButtonCallback(WPADShutdownCallback cb); +void WPAD_SetBatteryDeadCallback(WPADShutdownCallback cb); +s32 WPAD_ScanPads(); +s32 WPAD_Rumble(s32 chan, int status); +s32 WPAD_SetIdleThresholds(s32 chan, s32 btns, s32 ir, s32 accel, s32 js, s32 wb, s32 mp); +void WPAD_EncodeData(WPADEncStatus *info,u32 flag,const s16 *pcmSamples,s32 numSamples,u8 *encData); +WPADData *WPAD_Data(int chan); +u8 WPAD_BatteryLevel(int chan); +u32 WPAD_ButtonsUp(int chan); +u32 WPAD_ButtonsDown(int chan); +u32 WPAD_ButtonsHeld(int chan); +void WPAD_IR(int chan, struct ir_t *ir); +void WPAD_Orientation(int chan, struct orient_t *orient); +void WPAD_GForce(int chan, struct gforce_t *gforce); +void WPAD_Accel(int chan, struct vec3w_t *accel); +void WPAD_Expansion(int chan, struct expansion_t *exp); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/wii/libogc/libdb/debug.c b/wii/libogc/libdb/debug.c new file mode 100644 index 0000000000..f9dce171de --- /dev/null +++ b/wii/libogc/libdb/debug.c @@ -0,0 +1,653 @@ +#include +#include +#include +#include +#include +#include "asm.h" +#include "processor.h" +#include "spinlock.h" +#include "lwp.h" +#include "lwp_threads.h" +#include "sys_state.h" +#include "context.h" +#include "cache.h" +#include "video.h" +#include "ogcsys.h" + +#include "lwp_config.h" + +#include "tcpip.h" +#include "geckousb.h" +#include "debug_if.h" +#include "debug_supp.h" + +#define GEKKO_MAX_BP 256 + +#define SP_REGNUM 1 //register no. for stackpointer +#define PC_REGNUM 64 //register no. for programcounter (srr0) + +#define BUFMAX 2048 //we take the same as in ppc-stub.c + +#define BPCODE 0x7d821008 + +#define highhex(x) hexchars [((x)>>4)&0xf] +#define lowhex(x) hexchars [(x)&0xf] + +#if UIP_LOGGING == 1 +#include +#define UIP_LOG(m) uip_log(__FILE__,__LINE__,m) +#else +#define UIP_LOG(m) +#endif /* UIP_LOGGING == 1 */ + +static s32 dbg_active = 0; +static s32 dbg_instep = 0; +static s32 dbg_initialized = 0; + +static struct dbginterface *current_device = NULL; + +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; + +const char hexchars[]="0123456789abcdef"; + +static struct hard_trap_info { + u32 tt; + u8 signo; +} hard_trap_info[] = { + {EX_MACH_CHECK,SIGSEGV},/* Machine Check */ + {EX_DSI,SIGSEGV}, /* Adress Error(store) DSI */ + {EX_ISI,SIGBUS}, /* Instruction Bus Error ISI */ + {EX_INT,SIGINT}, /* Interrupt */ + {EX_ALIGN,SIGBUS}, /* Alignment */ + {EX_PRG,SIGTRAP}, /* Breakpoint Trap */ + {EX_FP,SIGFPE}, /* FPU unavail */ + {EX_DEC,SIGALRM}, /* Decrementer */ + {EX_SYS_CALL,SIGSYS}, /* System Call */ + {EX_TRACE,SIGTRAP}, /* Singel-Step/Watch */ + {0xB,SIGILL}, /* Reserved */ + {EX_IABR,SIGTRAP}, /* Instruction Address Breakpoint (GEKKO) */ + {0xD,SIGFPE}, /* FP assist */ + {0,0} /* MUST be the last */ +}; + +static struct bp_entry { + u32 *address; + u32 instr; + struct bp_entry *next; +} bp_entries[GEKKO_MAX_BP]; + +static struct bp_entry *p_bpentries = NULL; +static frame_context current_thread_registers; + +void __breakinst(); +void c_debug_handler(frame_context *ctx); + +extern void dbg_exceptionhandler(); +extern void __exception_sethandler(u32 nExcept, void (*pHndl)(frame_context*)); + +extern void __clr_iabr(); +extern void __enable_iabr(); +extern void __disable_iabr(); +extern void __set_iabr(void *); + +extern const char *tcp_localip; +extern const char *tcp_netmask; +extern const char *tcp_gateway; +extern u8 __text_start[],__data_start[],__bss_start[]; +extern u8 __text_fstart[],__data_fstart[],__bss_fstart[]; + +static __inline__ void bp_init() +{ + s32 i; + + for(i=0;i= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +static s32 hexToInt(char **ptr, s32 *ival) +{ + s32 cnt; + s32 val,nibble; + + val = 0; + cnt = 0; + while(**ptr) { + nibble = hex(**ptr); + if(nibble<0) break; + + val = (val<<4)|nibble; + cnt++; + + (*ptr)++; + } + *ival = val; + return cnt; +} + +static s32 computeSignal(s32 excpt) +{ + struct hard_trap_info *ht; + for(ht = hard_trap_info;ht->tt && ht->signo;ht++) { + if(ht->tt==excpt) return ht->signo; + } + return SIGHUP; +} + +static u32 insert_bp(void *mem) +{ + u32 i; + struct bp_entry *p; + + for(i=0;inext = p_bpentries; + p->address = mem; + p_bpentries = p; + + p->instr = *(p->address); + *(p->address) = BPCODE; + + DCStoreRangeNoSync((void*)((u32)mem&~0x1f),32); + ICInvalidateRange((void*)((u32)mem&~0x1f),32); + _sync(); + + return 1; +} + +static u32 remove_bp(void *mem) +{ + struct bp_entry *e = p_bpentries; + struct bp_entry *f = NULL; + + if(!e) return 0; + if(e->address==mem) { + p_bpentries = e->next; + f = e; + } else { + for(;e->next;e=e->next) { + if(e->next->address==mem) { + f = e->next; + e->next = f->next; + break; + } + } + } + if(!f) return 0; + + *(f->address) = f->instr; + f->instr = 0xffffffff; + f->address = NULL; + + DCStoreRangeNoSync((void*)((u32)mem&~0x1f),32); + ICInvalidateRange((void*)((u32)mem&~0x1f),32); + _sync(); + + return 1; +} + +static char getdbgchar() +{ + char ch = 0; + s32 len = 0; + + len = current_device->read(current_device,&ch,1); + + return (len>0)?ch:0; +} + +static void putdbgchar(char ch) +{ + current_device->write(current_device,&ch,1); +} + +static void putdbgstr(const char *str) +{ + current_device->write(current_device,str,strlen(str)); +} + +static void putpacket(const char *buffer) +{ + u8 recv; + u8 chksum,ch; + char *ptr; + const char *inp; + static char outbuf[2048]; + + do { + inp = buffer; + ptr = outbuf; + + *ptr++ = '$'; + + chksum = 0; + while((ch=*inp++)!='\0') { + *ptr++ = ch; + chksum += ch; + } + + *ptr++ = '#'; + *ptr++ = hexchars[chksum>>4]; + *ptr++ = hexchars[chksum&0x0f]; + *ptr = '\0'; + + putdbgstr(outbuf); + + recv = getdbgchar(); + } while((recv&0x7f)!='+'); +} + +static void getpacket(char *buffer) +{ + char ch; + u8 chksum,xmitsum; + s32 i,cnt; + + do { + while((ch=(getdbgchar()&0x7f))!='$'); + + cnt = 0; + chksum = 0; + xmitsum = -1; + + while(cnt=BUFMAX) continue; + + buffer[cnt] = 0; + if(ch=='#') { + xmitsum = hex(getdbgchar()&0x7f)<<4; + xmitsum |= hex(getdbgchar()&0x7f); + + if(chksum!=xmitsum) putdbgchar('-'); + else { + putdbgchar('+'); + if(buffer[2]==':') { + putdbgchar(buffer[0]); + putdbgchar(buffer[1]); + + cnt = strlen((const char*)buffer); + for(i=3;i<=cnt;i++) buffer[i-3] = buffer[i]; + } + } + } + } while(chksum!=xmitsum); +} + +static void process_query(const char *inp,char *outp,s32 thread) +{ + char *optr; + + switch(inp[1]) { + case 'C': + optr = outp; + *optr++ = 'Q'; + *optr++ = 'C'; + optr = thread2vhstr(optr,thread); + *optr++ = 0; + break; + case 'P': + { + s32 ret,rthread,mask; + struct gdbstub_threadinfo info; + + ret = parseqp(inp,&mask,&rthread); + if(!ret || mask&~0x1f) { + strcpy(outp,"E01"); + break; + } + + ret = gdbstub_getthreadinfo(rthread,&info); + if(!ret) { + strcpy(outp,"E02"); + break; + } + packqq(outp,mask,rthread,&info); + } + break; + case 'L': + { + s32 ret,athread,first,max_cnt,i,done,rthread; + + ret = parseql(inp,&first,&max_cnt,&athread); + if(!ret) { + strcpy(outp,"E02"); + break; + } + if(max_cnt==0) { + strcpy(outp,"E02"); + break; + } + if(max_cnt>QM_MAXTHREADS) max_cnt = QM_MAXTHREADS; + + optr = reserve_qmheader(outp); + if(first) rthread = 0; + else rthread = athread; + + done = 0; + for(i=0;iGPR,th->context.GPR,(32*4)); + memcpy(frame->FPR,th->context.FPR,(32*8)); + frame->SRR0 = th->context.LR; + frame->SRR1 = th->context.MSR; + frame->CR = th->context.CR; + frame->LR = th->context.LR; + frame->CTR = th->context.CTR; + frame->XER = th->context.XER; + frame->FPSCR = th->context.FPSCR; + return 1; + } + return 0; +} + +static void gdbstub_report_exception(frame_context *frame,s32 thread) +{ + s32 sigval; + char *ptr; + + ptr = remcomOutBuffer; + sigval = computeSignal(frame->EXCPT_Number); + *ptr++ = 'T'; + *ptr++ = highhex(sigval); + *ptr++ = lowhex(sigval); + *ptr++ = highhex(SP_REGNUM); + *ptr++ = lowhex(SP_REGNUM); + *ptr++ = ':'; + ptr = mem2hstr(ptr,(char*)&frame->GPR[1],4); + *ptr++ = ';'; + *ptr++ = highhex(PC_REGNUM); + *ptr++ = lowhex(PC_REGNUM); + *ptr++ = ':'; + ptr = mem2hstr(ptr,(char*)&frame->SRR0,4); + *ptr++ = ';'; + + *ptr++ = 't'; + *ptr++ = 'h'; + *ptr++ = 'r'; + *ptr++ = 'e'; + *ptr++ = 'a'; + *ptr++ = 'd'; + *ptr++ = ':'; + ptr = thread2vhstr(ptr,thread); + *ptr++ = ';'; + + *ptr++ = '\0'; + +} + + +void c_debug_handler(frame_context *frame) +{ + char *ptr; + s32 addr,len; + s32 thread,current_thread; + s32 host_has_detached; + frame_context *regptr; + + thread = gdbstub_getcurrentthread(); + current_thread = thread; + + if(current_device->open(current_device)<0) return; + + if(dbg_active) { + gdbstub_report_exception(frame,thread); + putpacket(remcomOutBuffer); + } + + if(frame->SRR0==(u32)__breakinst) frame->SRR0 += 4; + + host_has_detached = 0; + while(!host_has_detached) { + remcomOutBuffer[0]= 0; + getpacket(remcomInBuffer); + switch(remcomInBuffer[0]) { + case '?': + gdbstub_report_exception(frame,thread); + break; + case 'D': + dbg_instep = 0; + dbg_active = 0; + frame->SRR1 &= ~MSR_SE; + strcpy(remcomOutBuffer,"OK"); + host_has_detached = 1; + break; + case 'k': + dbg_instep = 0; + dbg_active = 0; + frame->SRR1 &= ~MSR_SE; + frame->SRR0 = 0x80001800; + host_has_detached = 1; + goto exit; + case 'g': + regptr = frame; + ptr = remcomOutBuffer; + if(current_thread!=thread) regptr = ¤t_thread_registers; + + ptr = mem2hstr(ptr,(char*)regptr->GPR,32*4); + ptr = mem2hstr(ptr,(char*)regptr->FPR,32*8); + ptr = mem2hstr(ptr,(char*)®ptr->SRR0,4); + ptr = mem2hstr(ptr,(char*)®ptr->SRR1,4); + ptr = mem2hstr(ptr,(char*)®ptr->CR,4); + ptr = mem2hstr(ptr,(char*)®ptr->LR,4); + ptr = mem2hstr(ptr,(char*)®ptr->CTR,4); + ptr = mem2hstr(ptr,(char*)®ptr->XER,4); + ptr = mem2hstr(ptr,(char*)®ptr->FPSCR,4); + break; + case 'm': + ptr = &remcomInBuffer[1]; + if(hexToInt(&ptr,&addr) && ((addr&0xC0000000)==0xC0000000 || (addr&0xC0000000)==0x80000000) + && *ptr++==',' + && hexToInt(&ptr,&len) && len<=((BUFMAX - 4)/2)) + mem2hstr(remcomOutBuffer,(void*)addr,len); + else + strcpy(remcomOutBuffer,"E00"); + break; + case 'q': + process_query(remcomInBuffer,remcomOutBuffer,thread); + break; + case 'c': + dbg_instep = 0; + dbg_active = 1; + frame->SRR1 &= ~MSR_SE; + current_device->wait(current_device); + goto exit; + case 's': + dbg_instep = 1; + dbg_active = 1; + frame->SRR1 |= MSR_SE; + current_device->wait(current_device); + goto exit; + case 'z': + { + s32 ret,type; + u32 len; + char *addr; + + ret = parsezbreak(remcomInBuffer,&type,&addr,&len); + if(!ret) { + strcpy(remcomOutBuffer,"E01"); + break; + } + if(type!=0) break; + + if(len<4) { + strcpy(remcomOutBuffer,"E02"); + break; + } + + ret = remove_bp(addr); + if(!ret) { + strcpy(remcomOutBuffer,"E03"); + break; + } + strcpy(remcomOutBuffer,"OK"); + } + break; + case 'H': + if(remcomInBuffer[1]=='g') + { + s32 tmp,ret; + + if(vhstr2thread(&remcomInBuffer[2],&tmp)==NULL) { + strcpy(remcomOutBuffer,"E01"); + break; + } + if(!tmp) tmp = thread; + if(tmp==current_thread) { + strcpy(remcomOutBuffer,"OK"); + break; + } + + if(current_thread!=thread) ret = gdbstub_setthreadregs(current_thread,¤t_thread_registers); + if(tmp!=thread) { + ret = gdbstub_getthreadregs(tmp,¤t_thread_registers); + if(!ret) { + strcpy(remcomOutBuffer,"E02"); + break; + } + } + current_thread= tmp; + } + strcpy(remcomOutBuffer,"OK"); + break; + case 'T': + { + s32 tmp; + + if(vhstr2thread(&remcomInBuffer[1],&tmp)==NULL) { + strcpy(remcomOutBuffer,"E01"); + break; + } + if(gdbstub_indextoid(tmp)==NULL) strcpy(remcomOutBuffer,"E02"); + else strcpy(remcomOutBuffer,"OK"); + } + break; + case 'Z': + { + s32 ret,type; + u32 len; + char *addr; + + ret = parsezbreak(remcomInBuffer,&type,&addr,&len); + if(!ret) { + strcpy(remcomOutBuffer,"E01"); + break; + } + if(type!=0) { + strcpy(remcomOutBuffer,"E02"); + break; + } + if(len<4) { + strcpy(remcomOutBuffer,"E03"); + break; + } + + ret = insert_bp(addr); + if(!ret) { + strcpy(remcomOutBuffer,"E04"); + break; + } + strcpy(remcomOutBuffer,"OK"); + } + break; + } + putpacket(remcomOutBuffer); + } + current_device->close(current_device); +exit: + return; +} + +void _break(void) +{ + if(!dbg_initialized) return; + __asm__ __volatile__ (".globl __breakinst\n\ + __breakinst: .long 0x7d821008"); +} + +void DEBUG_Init(s32 device_type,s32 channel_port) +{ + u32 level; + struct uip_ip_addr localip,netmask,gateway; + + UIP_LOG("DEBUG_Init()\n"); + + __lwp_thread_dispatchdisable(); + + bp_init(); + + if(device_type==GDBSTUB_DEVICE_USB) { + current_device = usb_init(channel_port); + } else { + localip.addr = uip_ipaddr((const u8_t*)tcp_localip); + netmask.addr = uip_ipaddr((const u8_t*)tcp_netmask); + gateway.addr = uip_ipaddr((const u8_t*)tcp_gateway); + + current_device = tcpip_init(&localip,&netmask,&gateway,(u16)channel_port); + } + + if(current_device!=NULL) { + _CPU_ISR_Disable(level); + __exception_sethandler(EX_DSI,dbg_exceptionhandler); + __exception_sethandler(EX_PRG,dbg_exceptionhandler); + __exception_sethandler(EX_TRACE,dbg_exceptionhandler); + __exception_sethandler(EX_IABR,dbg_exceptionhandler); + _CPU_ISR_Restore(level); + + dbg_initialized = 1; + + } + __lwp_thread_dispatchenable(); +} + diff --git a/wii/libogc/libdb/debug_handler.S b/wii/libogc/libdb/debug_handler.S new file mode 100644 index 0000000000..7a311a3c13 --- /dev/null +++ b/wii/libogc/libdb/debug_handler.S @@ -0,0 +1,169 @@ +#include + +#define EXCEPTION_PROLOG \ + mfspr r0,912; \ + stw r0,GQR0_OFFSET(sp); \ + mfspr r0,913; \ + stw r0,GQR1_OFFSET(sp); \ + mfspr r0,914; \ + stw r0,GQR2_OFFSET(sp); \ + mfspr r0,915; \ + stw r0,GQR3_OFFSET(sp); \ + mfspr r0,916; \ + stw r0,GQR4_OFFSET(sp); \ + mfspr r0,917; \ + stw r0,GQR5_OFFSET(sp); \ + mfspr r0,918; \ + stw r0,GQR6_OFFSET(sp); \ + mfspr r0,919; \ + stw r0,GQR7_OFFSET(sp); \ + stmw r6,GPR6_OFFSET(sp) + +#define EXCEPTION_EPILOG \ + lwz r4,GQR0_OFFSET(sp); \ + mtspr 912,r4; \ + lwz r4,GQR1_OFFSET(sp); \ + mtspr 913,r4; \ + lwz r4,GQR2_OFFSET(sp); \ + mtspr 914,r4; \ + lwz r4,GQR3_OFFSET(sp); \ + mtspr 915,r4; \ + lwz r4,GQR4_OFFSET(sp); \ + mtspr 916,r4; \ + lwz r4,GQR5_OFFSET(sp); \ + mtspr 917,r4; \ + lwz r4,GQR6_OFFSET(sp); \ + mtspr 918,r4; \ + lwz r4,GQR7_OFFSET(sp); \ + mtspr 919,r4; \ + lmw r5,GPR5_OFFSET(sp) + + .extern c_debug_handler + .extern _cpu_context_save_fp + .globl dbg_exceptionhandler +dbg_exceptionhandler: + stwu sp,-EXCEPTION_FRAME_END(sp) //now we're able to adjust the stackpointer with it's cached address + + EXCEPTION_PROLOG + + mfmsr r4 + ori r4,r4,MSR_FP + mtmsr r4 + isync + + addi r14,sp,0 + + lis r15,__debug_nestlevel@ha + lwz r6,__debug_nestlevel@l(r15) + cmpwi r6,0 + bne nested + + lis sp,__debugstack@h + ori sp,sp,__debugstack@l + lis r0,0 + stwu r0,-16(sp) + +nested: + addi r6,r6,1 + stw r6,__debug_nestlevel@l(r15) + + addi r3,r14,0x08 + bl _cpu_context_save_fp + bl c_debug_handler + + lwz r6,__debug_nestlevel@l(r15) + addi r6,r6,-1 + stw r6,__debug_nestlevel@l(r15) + + addi sp,r14,0 + +exit: + lwz r4,CR_OFFSET(sp) + mtcr r4 + lwz r4,LR_OFFSET(sp) + mtlr r4 + lwz r4,CTR_OFFSET(sp) + mtctr r4 + lwz r4,XER_OFFSET(sp) + mtxer r4 + + EXCEPTION_EPILOG + + mfmsr r4 + rlwinm r4,r4,0,19,17 + mtmsr r4 + isync + + lwz toc,GPR2_OFFSET(sp) + lwz r0,GPR0_OFFSET(sp) + + lwz r4,SRR0_OFFSET(sp) + mtsrr0 r4 + lwz r4,SRR1_OFFSET(sp) + mtsrr1 r4 + + lwz r4,GPR4_OFFSET(sp) + lwz r3,GPR3_OFFSET(sp) + addi sp,sp,EXCEPTION_FRAME_END + rfi + + .globl __set_iabr +__set_iabr: + mfmsr r4 + rlwinm r5,r4,0,18,16 + mtmsr r5 + clrrwi r3,r3,2 + mtspr 1010,r3 + isync + sync + mtmsr r4 + blr + + .globl __enable_iabr +__enable_iabr: + mfmsr r4 + rlwinm r5,r4,0,18,16 + mtmsr r5 + mfspr r3,1010 + ori r3,r3,0x0003 + mtspr 1010,r3 + isync + sync + mtmsr r4 + blr + + .globl __disable_iabr +__disable_iabr: + mfmsr r4 + rlwinm r5,r4,0,18,16 + mtmsr r5 + mfspr r3,1010 + clrrwi r3,r3,2 + mtspr 1010,r3 + isync + sync + mtmsr r4 + blr + + .globl __clr_iabr +__clr_iabr: + mfmsr r4 + rlwinm r5,r4,0,18,16 + mtmsr r5 + mtspr 1010,0 + isync + sync + mtmsr r4 + blr + + .section .bss + + .balign 4 +__debug_nestlevel: + .long 0 + + .balign 8 + .globl __debugstack_end,__debugstack +__debugstack_end: + .space 0x4000 +__debugstack: diff --git a/wii/libogc/libdb/debug_if.h b/wii/libogc/libdb/debug_if.h new file mode 100644 index 0000000000..d739a7cde8 --- /dev/null +++ b/wii/libogc/libdb/debug_if.h @@ -0,0 +1,20 @@ +#ifndef __DEBUG_IF_H__ +#define __DEBUG_IF_H__ + +#include + +#define GDBSTUB_DEVICE_USB 0 +#define GDBSTUB_DEVICE_TCP 1 + +struct dbginterface +{ + s32 fhndl; + + int (*open)(struct dbginterface *device); + int (*close)(struct dbginterface *device); + int (*wait)(struct dbginterface *device); + int (*read)(struct dbginterface *device,void *buffer,int size); + int (*write)(struct dbginterface *devicec,const void *buffer,int size); +}; + +#endif diff --git a/wii/libogc/libdb/debug_supp.c b/wii/libogc/libdb/debug_supp.c new file mode 100644 index 0000000000..dbbc7d0c5f --- /dev/null +++ b/wii/libogc/libdb/debug_supp.c @@ -0,0 +1,477 @@ +#include +#include +#include +#include + +#include "lwp_threads.h" +#include "debug_supp.h" + +extern lwp_objinfo _lwp_cond_objects; +extern lwp_objinfo _lwp_thr_objects; +extern lwp_objinfo _lwp_tqueue_objects; +extern lwp_objinfo _lwp_mqbox_objects; +extern lwp_objinfo _lwp_mutex_objects; +extern lwp_objinfo _lwp_sema_objects; + +extern const u8 hexchars[]; + +extern u8 __text_start[],__data_start[],__bss_start[]; +extern u8 __text_fstart[],__data_fstart[],__bss_fstart[]; + +s32 hstr2nibble(const char *buf,s32 *nibble) +{ + s32 ch; + + ch = *buf; + if(ch>='0' && ch<='9') { + *nibble = ch - '0'; + return 1; + } + if(ch>='a' && ch<='f') { + *nibble = ch - 'a' + 10; + return 1; + } + if(ch>='A' && ch<='F') { + *nibble = ch - 'A' + 10; + return 1; + } + return 0; +} + +s32 hstr2byte(const char *buf,s32 *bval) +{ + s32 hnib,lnib; + + if(!hstr2nibble(buf,&hnib) || !hstr2nibble(buf+1,&lnib)) return 0; + + *bval = (hnib<<4)|lnib; + return 1; +} + +const char* vhstr2int(const char *buf,s32 *ival) +{ + s32 i,val,nibble; + s32 found0,lim; + + found0 = 0; + for(i=0;i<8;i++,buf++) { + if(*buf!='0') break; + + found0 = 1; + } + + val = 0; + lim = 8 - i; + for(i=0;i>shift)&0x0f; + *buf = hexchars[nibble]; + } + return buf; +} + +char* int2vhstr(char *buf,s32 val) +{ + s32 i,nibble,shift; + + for(i=0,shift=28;i<8;i++,shift-=4) { + nibble = (val>>shift)&0x0f; + if(nibble) break; + } + if(i==8) { + *buf++ = '0'; + return buf; + } + + *buf++ = hexchars[nibble]; + for(i++,shift-=4;i<8;i++,shift-=4,buf++) { + nibble = (val>>shift)&0x0f; + *buf = hexchars[nibble]; + } + return buf; +} + +char* mem2hstr(char *buf,const char *mem,s32 count) +{ + s32 i; + char ch; + + for(i=0;i>4]; + *buf++ = hexchars[ch&0x0f]; + } + *buf = 0; + return buf; +} +char* thread2fhstr(char *buf,s32 thread) +{ + s32 i,nibble,shift; + + for(i=0;i<8;i++,buf++) *buf = '0'; + for(i=0,shift=28;i<8;i++,shift-=4,buf++) { + nibble = (thread>>shift)&0x0f; + *buf = hexchars[nibble]; + } + return buf; +} + +char* thread2vhstr(char *buf,s32 thread) +{ + s32 i,nibble,shift; + + for(i=0,shift=28;i<8;i++,shift-=4) { + nibble = (thread>>shift)&0x0f; + if(nibble) break; + } + if(i==8) { + *buf++ = '0'; + return buf; + } + + *buf++ = hexchars[nibble]; + for(i++,shift-=4;i<8;i++,shift-=4,buf++) { + nibble = (thread>>shift)&0x0f; + *buf = hexchars[nibble]; + } + return buf; +} + +const char* fhstr2thread(const char *buf,s32 *thread) +{ + s32 i,nibble,val; + + for(i=0;i<8;i++,buf++) + if(*buf!='0') return NULL; + + val = 0; + for(i=0;i<8;i++,buf++) { + if(!hstr2nibble(buf,&nibble)) return NULL; + + val = (val<<4)|nibble; + } + + *thread = val; + return buf; +} + +const char* vhstr2thread(const char *buf,s32 *thread) +{ + s32 i,val,nibble; + s32 found0,lim; + + found0 = 0; + for(i=0;i<16;i++,buf++) { + if(*buf!='0') break; + + found0 = 1; + } + + val = 0; + lim = 16 - i; + for(i=0;i=min_id && objidobject.id); +} + +s32 gdbstub_getnextthread(s32 athread) +{ + s32 id,start; + s32 first_id,min_id,max_id,lim; + + if(athread<1) return 1; + + first_id = 1; + min_id = _lwp_thr_objects.min_id; + max_id = _lwp_thr_objects.max_id; + lim = first_id + max_id - min_id; + if(athreaddisplay,"idle thread"); + strcpy(info->name,"IDLE"); + info->more_display[0] = 0; + return 1; + } + + first_id = 1; + min_id = _lwp_thr_objects.min_id; + max_id = _lwp_thr_objects.max_id; + if(thread<=(first_id + (max_id - min_id))){ + th = (lwp_cntrl*)_lwp_thr_objects.local_table[thread - first_id]; + if(th==NULL) return 0; + + strcpy(info->display,"libogc task: control at: 0x"); + tmp_buf[0] = hexchars[(((int)th)>>28)&0x0f]; + tmp_buf[1] = hexchars[(((int)th)>>24)&0x0f]; + tmp_buf[2] = hexchars[(((int)th)>>20)&0x0f]; + tmp_buf[3] = hexchars[(((int)th)>>16)&0x0f]; + tmp_buf[4] = hexchars[(((int)th)>>12)&0x0f]; + tmp_buf[5] = hexchars[(((int)th)>>8)&0x0f]; + tmp_buf[6] = hexchars[(((int)th)>>4)&0x0f]; + tmp_buf[7] = hexchars[((int)th)&0x0f]; + tmp_buf[8] = 0; + strcat(info->display,tmp_buf); + + info->name[0] = 0; + info->name[1] = 0; + info->name[2] = 0; + info->name[3] = 0; + info->name[4] = 0; + + info->more_display[0] = 0; + return 1; + } + return 0; +} + +s32 parsezbreak(const char *in,s32 *type,char **addr,u32 *len) +{ + s32 ttmp,atmp,ltmp; + + in++; + if(!hstr2nibble(in,&ttmp) || *(in+1)!=',') return 0; + + in += 2; + in = vhstr2int(in,&atmp); + if(in==NULL || *in!=',') return 0; + + in++; + in = vhstr2int(in,<mp); + if(in==NULL || ltmp<1) return 0; + + *type = ttmp; + *addr = (char*)atmp; + *len = ltmp; + + return 1; +} + +s32 parseqp(const char *in,s32 *mask,s32 *thread) +{ + const char *ptr; + + ptr = fhstr2int(in+2,mask); + if(ptr==NULL) return 0; + + ptr = fhstr2thread(ptr,thread); + if(ptr==NULL) return 0; + + return 1; +} + +void packqq(char *out,s32 mask,s32 thread,struct gdbstub_threadinfo *info) +{ + s32 len; + + *out++ = 'q'; + *out++ = 'Q'; + out = int2fhstr(out,mask); + out = thread2fhstr(out,thread); + + if(mask&0x01) { + memcpy(out,"00000001",8); + out += 8; + *out++ = '1'; + *out++ = '0'; + out = thread2fhstr(out,thread); + } + if(mask&0x02) { + memcpy(out,"00000002",8); + out += 8; + *out++ = '0'; + *out++ = '1'; + *out++ = '1'; + } + if(mask&0x04) { + memcpy(out,"00000004",8); + out += 8; + + info->display[sizeof(info->display)-1] = 0; //for god sake + len = strlen(info->display); + + *out++ = hexchars[(len>>4)&0x0f]; + *out++ = hexchars[len&0x0f]; + + memcpy(out,info->display,len); + out += len; + } + if(mask&0x08) { + memcpy(out,"00000008",8); + out += 8; + + info->display[sizeof(info->name)-1] = 0; //for god sake + len = strlen(info->name); + + *out++ = hexchars[(len>>4)&0x0f]; + *out++ = hexchars[len&0x0f]; + + memcpy(out,info->name,len); + out += len; + } + if(mask&0x10) { + memcpy(out,"00000010",8); + out += 8; + + info->display[sizeof(info->more_display)-1] = 0; //for god sake + len = strlen(info->more_display); + + *out++ = hexchars[(len>>4)&0x0f]; + *out++ = hexchars[len&0x0f]; + + memcpy(out,info->more_display,len); + out += len; + } + *out = 0; +} + +s32 parseql(const char *in,s32 *first,s32 *max_cnt,s32 *athread) +{ + const char *ptr; + + ptr = in+2; + if(!hstr2nibble(ptr,first)) return 0; + + ptr++; + if(!hstr2byte(ptr,max_cnt)) return 0; + + ptr += 2; + ptr = fhstr2thread(ptr,athread); + if(ptr==NULL) return 0; + + return 1; +} + +char* reserve_qmheader(char *out) +{ + return (out+21); +} + +char* packqmthread(char *out,s32 thread) +{ + return thread2fhstr(out,thread); +} + +void packqmheader(char *out,s32 count,s32 done,s32 athread) +{ + *out++ = 'q'; + *out++ = 'M'; + *out++ = hexchars[(count>>4)&0x0f]; + *out++ = hexchars[count&0x0f]; + + if(done) *out++ = '1'; + else *out++ = '0'; + + thread2fhstr(out,athread); +} diff --git a/wii/libogc/libdb/debug_supp.h b/wii/libogc/libdb/debug_supp.h new file mode 100644 index 0000000000..a12e38663c --- /dev/null +++ b/wii/libogc/libdb/debug_supp.h @@ -0,0 +1,32 @@ +#ifndef __DEBUG_SUPP_H__ +#define __DEBUG_SUPP_H__ + +#include + +#define QM_MAXTHREADS (20) + +struct gdbstub_threadinfo { + char display[256]; + char more_display[256]; + char name[256]; +}; + +s32 gdbstub_getcurrentthread(); +s32 hstr2nibble(const char *buf,s32 *nibble); +char* int2vhstr(char *buf,s32 val); +char* mem2hstr(char *buf,const char *mem,s32 count); +char* thread2vhstr(char *buf,s32 thread); +const char* vhstr2thread(const char *buf,s32 *thread); +lwp_cntrl* gdbstub_indextoid(s32 thread); +s32 gdbstub_getoffsets(char **textaddr,char **dataaddr,char **bssaddr); +s32 parsezbreak(const char *in,s32 *type,char **addr,u32 *len); +s32 gdbstub_getthreadinfo(s32 thread,struct gdbstub_threadinfo *info); +s32 parseqp(const char *in,s32 *mask,s32 *thread); +void packqq(char *out,s32 mask,s32 thread,struct gdbstub_threadinfo *info); +char* reserve_qmheader(char *out); +s32 parseql(const char *in,s32 *first,s32 *max_cnt,s32 *athread); +s32 gdbstub_getnextthread(s32 athread); +char* packqmthread(char *out,s32 thread); +void packqmheader(char *out,s32 count,s32 done,s32 athread); + +#endif diff --git a/wii/libogc/libdb/geckousb.c b/wii/libogc/libdb/geckousb.c new file mode 100644 index 0000000000..2e92e2dd79 --- /dev/null +++ b/wii/libogc/libdb/geckousb.c @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include +#include + +#include "geckousb.h" + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +static struct dbginterface usb_device; + +static __inline__ int __send_command(s32 chn,u16 *cmd) +{ + s32 ret; + + ret = 0; + if(!EXI_Select(chn,EXI_DEVICE_0,EXI_SPEED32MHZ)) ret |= 0x01; + if(!EXI_Imm(chn,cmd,sizeof(u16),EXI_READWRITE,NULL)) ret |= 0x02; + if(!EXI_Sync(chn)) ret |= 0x04; + if(!EXI_Deselect(chn)) ret |= 0x08; + + if(ret) return 0; + return 1; +} + +static int __usb_sendbyte(s32 chn,char ch) +{ + s32 ret; + u16 val; + + val = (0xB000|_SHIFTL(ch,4,8)); + ret = __send_command(chn,&val); + if(ret==1 && !(val&0x0400)) ret = 0; + + return ret; +} + +static int __usb_recvbyte(s32 chn,char *ch) +{ + s32 ret; + u16 val; + + *ch = 0; + val = 0xA000; + ret = __send_command(chn,&val); + if(ret==1 && !(val&0x0800)) ret = 0; + else if(ret==1) *ch = (val&0xff); + + return ret; +} + +static int __usb_checksend(s32 chn) +{ + s32 ret; + u16 val; + + val = 0xC000; + ret = __send_command(chn,&val); + if(ret==1 && !(val&0x0400)) ret = 0; + + return ret; +} + +static int __usb_checkrecv(s32 chn) +{ + s32 ret; + u16 val; + + val = 0xD000; + ret = __send_command(chn,&val); + if(ret==1 && !(val&0x0400)) ret = 0; + + return ret; +} + +static void __usb_flush(s32 chn) +{ + char tmp; + + if(!EXI_Lock(chn,EXI_DEVICE_0,NULL)) return; + + while(__usb_recvbyte(chn,&tmp)); + + EXI_Unlock(chn); +} + + +static int __usb_isgeckoalive(s32 chn) +{ + s32 ret; + u16 val; + + if(!EXI_Lock(chn,EXI_DEVICE_0,NULL)) return 0; + + val = 0x9000; + ret = __send_command(chn,&val); + if(ret==1 && !(val&0x0470)) ret = 0; + + EXI_Unlock(chn); + return ret; +} + +static int __usb_recvbuffer(s32 chn,void *buffer,int size) +{ + s32 ret; + s32 left = size; + char *ptr = (char*)buffer; + + if(!EXI_Lock(chn,EXI_DEVICE_0,NULL)) return 0; + + while(left>0) { + if(__usb_checkrecv(chn)) { + ret = __usb_recvbyte(chn,ptr); + if(ret==0) break; + + ptr++; + left--; + } + } + + EXI_Unlock(chn); + return (size - left); +} + +static int __usb_sendbuffer(s32 chn,const void *buffer,int size) +{ + s32 ret; + s32 left = size; + char *ptr = (char*)buffer; + + if(!EXI_Lock(chn,EXI_DEVICE_0,NULL)) return 0; + + while(left>0) { + if(__usb_checksend(chn)) { + ret = __usb_sendbyte(chn,*ptr); + if(ret==0) break; + + ptr++; + left--; + } + } + + EXI_Unlock(chn); + return (size - left); +} + +static int usbopen(struct dbginterface *device) +{ + if(!__usb_isgeckoalive(device->fhndl)) { + return -1; + } + + return 0; +} + +static int usbclose(struct dbginterface *device) +{ + return 0; +} + +static int usbwait(struct dbginterface *device) +{ + return 0; +} + +static int usbread(struct dbginterface *device,void *buffer,int size) +{ + int ret; + ret = __usb_recvbuffer(device->fhndl,buffer,size); + return ret; +} + +static int usbwrite(struct dbginterface *device,const void *buffer,int size) +{ + int ret; + ret = __usb_sendbuffer(device->fhndl,buffer,size); + return ret; +} + +struct dbginterface* usb_init(s32 channel) +{ + usb_device.fhndl = channel; + if(__usb_isgeckoalive(channel)) + __usb_flush(channel); + + usb_device.open = usbopen; + usb_device.close = usbclose; + usb_device.wait = usbwait; + usb_device.read = usbread; + usb_device.write = usbwrite; + + return &usb_device; +} diff --git a/wii/libogc/libdb/geckousb.h b/wii/libogc/libdb/geckousb.h new file mode 100644 index 0000000000..7e14a42d90 --- /dev/null +++ b/wii/libogc/libdb/geckousb.h @@ -0,0 +1,8 @@ +#ifndef __GECKOUSB_H___ +#define __GECKOUSB_H___ + +#include "debug_if.h" + +struct dbginterface* usb_init(s32 channel); + +#endif diff --git a/wii/libogc/libdb/tcpip.c b/wii/libogc/libdb/tcpip.c new file mode 100644 index 0000000000..66a4889553 --- /dev/null +++ b/wii/libogc/libdb/tcpip.c @@ -0,0 +1,466 @@ +#include +#include +#include + +#include "asm.h" +#include "processor.h" + +#include "uIP/bba.h" +#include "uIP/memr.h" +#include "uIP/memb.h" +#include "uIP/uip_ip.h" +#include "uIP/uip_arp.h" +#include "uIP/uip_tcp.h" +#include "uIP/uip_pbuf.h" +#include "uIP/uip_netif.h" + +#include "tcpip.h" +#include "debug_if.h" + +#if UIP_LOGGING == 1 +#include +#define UIP_LOG(m) uip_log(__FILE__,__LINE__,m) +#else +#define UIP_LOG(m) +#endif /* UIP_LOGGING == 1 */ + +#if UIP_STATISTICS == 1 +struct uip_stats uip_stat; +#define UIP_STAT(s) s +#else +#define UIP_STAT(s) +#endif /* UIP_STATISTICS == 1 */ + +const char *tcp_localip __attribute__ ((weak)) = ""; +const char *tcp_netmask __attribute__ ((weak)) = ""; +const char *tcp_gateway __attribute__ ((weak)) = ""; + +struct tcpip_sock { + struct tcpip_sock *next; + struct uip_tcp_pcb *pcb; + struct uip_pbuf *lastdata; + s32 lastoffset,recvevt,sendevt,flags; + s32 err,socket; +} tcpip_socks[UIP_TCPIP_SOCKS]; + +static s64 tcpip_time = 0; +static s32 listensock = -1; +static struct uip_netif netif; +static struct dbginterface netif_device; +static struct tcpip_sock *tcpip_accepted_sockets = NULL; + +extern s64 gettime(); +extern u32 diff_msec(s64 start,s64 end); + +static s32_t tcpip_allocsocket(struct uip_tcp_pcb *pcb) +{ + s32_t i; + + for(i=0;i=UIP_TCPIP_SOCKS) return NULL; + + sock = &tcpip_socks[s]; + if(!sock->pcb) return NULL; + + return sock; +} + +//device callbacks +static int opentcpip(struct dbginterface *device) +{ + if(listensock>=0 && (device->fhndl<0 )) + device->fhndl = tcpip_accept(listensock); + + if(device->fhndl<0) + return -1; + else + tcpip_starttimer(device->fhndl); + + return 0; +} + +static int closetcpip(struct dbginterface *device) +{ + tcpip_stoptimer(device->fhndl); + tcpip_close(device->fhndl); + device->fhndl = -1; + return 0; +} + +static int waittcpip(struct dbginterface *device) +{ + tcpip_stoptimer(device->fhndl); + return 0; +} + +static int readtcpip(struct dbginterface *device,void *buffer,int size) +{ + if(device->fhndl>=0) + return tcpip_read(device->fhndl,buffer,size); + + return 0; +} + +static int writetcpip(struct dbginterface *device,const void *buffer,int size) +{ + if(device->fhndl>=0) + return tcpip_write(device->fhndl,buffer,size); + + return 0; +} + +static void tcpip_err(void *arg,s8_t err) +{ +// printf("tcpip_err: err_code(%d)\n",err); +} + +static s8_t tcpip_poll(void *arg,struct uip_tcp_pcb *pcb) +{ + UIP_LOG("tcpip_poll()"); + return UIP_ERR_OK; +} + +static s8_t tcpip_sent(void *arg,struct uip_tcp_pcb *pcb,u16_t space) +{ +// printf("tcpip_sent(%d)\n",space); + return UIP_ERR_OK; +} + +//static u32 qcnt = 0; + +static s8_t tcpip_recved(void *arg,struct uip_tcp_pcb *pcb,struct uip_pbuf *p,s8_t err) +{ + u16_t len; + struct tcpip_sock *sock = (struct tcpip_sock*)arg; + + //printf("tcpip_recved(%s (%d/%d))\n",(u8_t*)p->payload,p->len,p->tot_len); + if(!sock) { + uip_pbuf_free(p); + return UIP_ERR_VAL; + } + + if(p) { + len = p->tot_len; + if(sock->lastdata==NULL) { + sock->lastdata = p; + } else { +/* + qcnt++; + printf("tcpip_recved(queuing %d)\n",qcnt); +*/ + uip_pbuf_queue(sock->lastdata,p); + } + } else + len = 1; + + uip_tcp_recved(pcb,len); + return UIP_ERR_OK; +} + + +static s8_t tcpip_accept_func(void *arg,struct uip_tcp_pcb *newpcb,s8_t err) +{ + s32_t s; + struct tcpip_sock *ptr,*newsock = NULL; + struct tcpip_sock *sock = (struct tcpip_sock*)arg; + + UIP_LOG("tcpip_accept_func()"); + if(!sock) return UIP_ERR_ABRT; + + s = tcpip_allocsocket(newpcb); + if(s<0) { + uip_tcp_close(newpcb); + return UIP_ERR_ABRT; + } + + newsock = tcpip_getsocket(s); + newsock->pcb->flags |= UIP_TF_NODELAY; + + ptr = tcpip_accepted_sockets; + while(ptr && ptr->next) ptr = ptr->next; + if(!ptr) tcpip_accepted_sockets = newsock; + else ptr->next = newsock; + + uip_tcp_arg(newpcb,newsock); + uip_tcp_recv(newpcb,tcpip_recved); + uip_tcp_sent(newpcb,tcpip_sent); + uip_tcp_err(newpcb,tcpip_err); + uip_tcp_poll(newpcb,tcpip_poll,4); + + return UIP_ERR_OK; +} + +static void __tcpip_poll() +{ + u32 diff; + s64 now; + + if(uip_netif_default==NULL) return; + + uip_bba_poll(uip_netif_default); + + if(tcpip_time && (uip_tcp_active_pcbs || uip_tcp_tw_pcbs)) { + now = gettime(); + diff = diff_msec(tcpip_time,now); + if(diff>=UIP_TCP_TMR_INTERVAL) { + uip_tcp_tmr(); + tcpip_time = gettime(); + } + } else + tcpip_time = 0; +} + +void tcpip_tmr_needed() +{ + if(!tcpip_time && (uip_tcp_active_pcbs || uip_tcp_tw_pcbs)) { + tcpip_time = gettime(); + } +} + +struct dbginterface* tcpip_init(struct uip_ip_addr *localip,struct uip_ip_addr *netmask,struct uip_ip_addr *gateway,u16 port) +{ + uipdev_s hbba; + struct uip_netif *pnet ; + struct sockaddr_in name; + socklen_t namelen = sizeof(struct sockaddr); + + memr_init(); + uip_ipinit(); + uip_pbuf_init(); + uip_netif_init(); + uip_tcp_init(); + + UIP_MEMSET(tcpip_socks,0,(UIP_TCPIP_SOCKS*sizeof(struct tcpip_sock))); + + hbba = uip_bba_create(&netif); + pnet = uip_netif_add(&netif,localip,netmask,gateway,hbba,uip_bba_init,uip_ipinput); + if(pnet) { + uip_netif_setdefault(pnet); + + listensock = tcpip_socket(); + if(listensock<0) return NULL; + + name.sin_addr.s_addr = INADDR_ANY; + name.sin_port = htons(port); + name.sin_family = AF_INET; + + if(tcpip_bind(listensock,(struct sockaddr*)&name,&namelen)<0){ + tcpip_close(listensock); + listensock = -1; + return NULL; + } + if(tcpip_listen(listensock,1)<0) { + tcpip_close(listensock); + listensock = -1; + return NULL; + } + + netif_device.fhndl = -1; + netif_device.wait = waittcpip; + netif_device.open = opentcpip; + netif_device.close = closetcpip; + netif_device.read = readtcpip; + netif_device.write = writetcpip; + + return &netif_device; + } + return NULL; +} + +s32_t tcpip_socket() +{ + s32_t s; + struct tcpip_sock *sock; + struct uip_tcp_pcb *pcb; + + pcb = uip_tcp_new(); + if(!pcb) return -1; + + s = tcpip_allocsocket(pcb); + if(s<0) { + uip_tcp_close(pcb); + return -1; + } + + sock = tcpip_getsocket(s); + uip_tcp_arg(pcb,sock); + + return s; +} + +s32_t tcpip_bind(s32_t s,struct sockaddr *name,socklen_t *namelen) +{ + struct tcpip_sock *sock; + struct uip_ip_addr local_ip; + u16_t local_port; + s8_t err; + + sock = tcpip_getsocket(s); + if(!sock) return -1; + + local_ip.addr = ((struct sockaddr_in*)name)->sin_addr.s_addr; + local_port = ((struct sockaddr_in*)name)->sin_port; + + err = uip_tcp_bind(sock->pcb,&local_ip,local_port); + + return (s32_t)err; +} + +s32_t tcpip_listen(s32_t s,u32_t backlog) +{ + struct tcpip_sock *sock; + + sock = tcpip_getsocket(s); + if(!sock) return -1; + + sock->pcb = uip_tcp_listen(sock->pcb); + if(sock->pcb==NULL) return -1; + + uip_tcp_accept(sock->pcb,tcpip_accept_func); + + return 0; +} + +s32_t tcpip_accept(s32_t s) +{ + s32_t newsock = -1; + struct tcpip_sock *sock; + + sock = tcpip_getsocket(s); + if(sock==NULL) return -1; + + do { + __tcpip_poll(); + } while(!tcpip_accepted_sockets); + + newsock = tcpip_accepted_sockets->socket; + tcpip_accepted_sockets = tcpip_accepted_sockets->next; + + return newsock; +} + +s32_t tcpip_read(s32_t s,void *buffer,u32_t len) +{ + u32_t off,copy; + u8_t *ptr; + struct uip_pbuf *p; + struct tcpip_sock *sock; + + sock = tcpip_getsocket(s); + if(!sock) return -1; + + do { + __tcpip_poll(); + } while(sock->lastdata==NULL); + + if(sock->lastdata) { + off = 0; + ptr = buffer; + while(len>0 && sock->lastdata) { + p = sock->lastdata; + + if(len>p->len-sock->lastoffset) copy = (p->len-sock->lastoffset); + else copy = len; + + UIP_MEMCPY(ptr+off,(u8_t*)p->payload+sock->lastoffset,copy); + + off += copy; + len -= copy; + sock->lastoffset += copy; + + if(sock->lastoffset>=p->len) { + sock->lastoffset = 0; + sock->lastdata = uip_pbuf_dequeue(p); + uip_pbuf_free(p); +/* + if(qcnt>0) { + printf("tcpip_read(dequeuing %d)\n",--qcnt); + } +*/ + } + } + return off; + } + return -1; +} + +s32_t tcpip_write(s32_t s,const void *buffer,u32_t len) +{ + s8_t err; + u16_t snd_buf,copy; + struct tcpip_sock *sock; + + sock = tcpip_getsocket(s); + if(!sock) return -1; + +// printf("tcpip_write()\n"); + while(len>0) { + do { + __tcpip_poll(); + } while((snd_buf=uip_tcp_sndbuf(sock->pcb))==0); + + if(len>snd_buf) copy = snd_buf; + else copy = len; + + err = uip_tcp_write(sock->pcb,buffer,copy,1); + if(err==UIP_ERR_OK && (!sock->pcb->unacked || sock->pcb->flags&UIP_TF_NODELAY || sock->pcb->snd_queuelen>1)) + uip_tcpoutput(sock->pcb); + + buffer = buffer+copy; + len -= copy; + } + return UIP_ERR_OK; +} + +void tcpip_close(s32_t s) +{ + struct tcpip_sock *sock; + + sock = tcpip_getsocket(s); + if(sock==NULL) return; + + uip_tcp_close(sock->pcb); + sock->pcb = NULL; +} + +// does basically only stop the tcpip timer +void tcpip_stoptimer(s32_t s) +{ + struct tcpip_sock *sock; + + sock = tcpip_getsocket(s); + if(!sock) return; + + if(tcpip_time && sock->pcb && (uip_tcp_active_pcbs || uip_tcp_tw_pcbs)) tcpip_time = 0; +} + +// does basically only restart the tcpip timer +void tcpip_starttimer(s32_t s) +{ + struct tcpip_sock *sock; + + sock = tcpip_getsocket(s); + if(!sock) return; + + if(tcpip_time==0 && sock->pcb && (uip_tcp_active_pcbs || uip_tcp_tw_pcbs)) tcpip_time = gettime(); +} + diff --git a/wii/libogc/libdb/tcpip.h b/wii/libogc/libdb/tcpip.h new file mode 100644 index 0000000000..99f9db73e2 --- /dev/null +++ b/wii/libogc/libdb/tcpip.h @@ -0,0 +1,53 @@ +#ifndef __TCPIP_H__ +#define __TCPIP_H__ + +#include "uIP/uip.h" +#include +#include + +#define AF_UNSPEC 0 +#define AF_INET 2 +#define PF_INET AF_INET +#define PF_UNSPEC AF_UNSPEC + +#define INADDR_ANY 0 +#define INADDR_BROADCAST 0xffffffff + +#ifndef socklen_t +#define socklen_t u32_t +#endif + +#ifndef HAVE_IN_ADDR +#define HAVE_IN_ADDR +struct in_addr { + u32 s_addr; +}; +#endif + +struct sockaddr_in { + u8 sin_len; + u8 sin_family; + u16 sin_port; + struct in_addr sin_addr; + s8 sin_zero[8]; +}; + +struct sockaddr { + u8 sa_len; + u8 sa_family; + s8 sa_data[14]; +}; + +struct dbginterface* tcpip_init(struct uip_ip_addr *localip,struct uip_ip_addr *netmask,struct uip_ip_addr *gateway,u16 port); + +void tcpip_close(s32_t s); +void tcpip_starttimer(s32_t s); +void tcpip_stoptimer(s32_t s); +s32_t tcpip_socket(); +s32_t tcpip_listen(s32_t s,u32_t backlog); +s32_t tcpip_bind(s32_t s,struct sockaddr *name,socklen_t *namelen); +s32_t tcpip_accept(s32_t s); +s32_t tcpip_read(s32_t s,void *buffer,u32_t len); +s32_t tcpip_write(s32_t s,const void *buffer,u32_t len); + +#endif diff --git a/wii/libogc/libdb/uIP/bba.c b/wii/libogc/libdb/uIP/bba.c new file mode 100644 index 0000000000..0e6075ecf8 --- /dev/null +++ b/wii/libogc/libdb/uIP/bba.c @@ -0,0 +1,835 @@ +#include +#include +#include +#include "asm.h" +#include "processor.h" +#include "exi.h" +#include "cache.h" +#include "bba.h" +#include "uip_pbuf.h" +#include "uip_netif.h" +#include "uip_arp.h" + +#define IFNAME0 'e' +#define IFNAME1 't' + +#define BBA_MINPKTSIZE 60 + +#define BBA_CID 0x04020200 + +#define BBA_CMD_IRMASKALL 0x00 +#define BBA_CMD_IRMASKNONE 0xF8 + +#define BBA_NCRA 0x00 /* Network Control Register A, RW */ +#define BBA_NCRA_RESET (1<<0) /* RESET */ +#define BBA_NCRA_ST0 (1<<1) /* ST0, Start transmit command/status */ +#define BBA_NCRA_ST1 (1<<2) /* ST1, " */ +#define BBA_NCRA_SR (1<<3) /* SR, Start Receive */ + +#define BBA_NCRB 0x01 /* Network Control Register B, RW */ +#define BBA_NCRB_PR (1<<0) /* PR, Promiscuous Mode */ +#define BBA_NCRB_CA (1<<1) /* CA, Capture Effect Mode */ +#define BBA_NCRB_PM (1<<2) /* PM, Pass Multicast */ +#define BBA_NCRB_PB (1<<3) /* PB, Pass Bad Frame */ +#define BBA_NCRB_AB (1<<4) /* AB, Accept Broadcast */ +#define BBA_NCRB_HBD (1<<5) /* HBD, reserved */ +#define BBA_NCRB_RXINTC0 (1<<6) /* RXINTC, Receive Interrupt Counter */ +#define BBA_NCRB_RXINTC1 (1<<7) /* " */ +#define BBA_NCRB_1_PACKET_PER_INT (0<<6) /* 0 0 */ +#define BBA_NCRB_2_PACKETS_PER_INT (1<<6) /* 0 1 */ +#define BBA_NCRB_4_PACKETS_PER_INT (2<<6) /* 1 0 */ +#define BBA_NCRB_8_PACKETS_PER_INT (3<<6) /* 1 1 */ + +#define BBA_LTPS 0x04 /* Last Transmitted Packet Status, RO */ +#define BBA_LRPS 0x05 /* Last Received Packet Status, RO */ + +#define BBA_IMR 0x08 /* Interrupt Mask Register, RW, 00h */ +#define BBA_IMR_FRAGIM (1<<0) /* FRAGIM, Fragment Counter Int Mask */ +#define BBA_IMR_RIM (1<<1) /* RIM, Receive Interrupt Mask */ +#define BBA_IMR_TIM (1<<2) /* TIM, Transmit Interrupt Mask */ +#define BBA_IMR_REIM (1<<3) /* REIM, Receive Error Interrupt Mask */ +#define BBA_IMR_TEIM (1<<4) /* TEIM, Transmit Error Interrupt Mask */ +#define BBA_IMR_FIFOEIM (1<<5) /* FIFOEIM, FIFO Error Interrupt Mask */ +#define BBA_IMR_BUSEIM (1<<6) /* BUSEIM, BUS Error Interrupt Mask */ +#define BBA_IMR_RBFIM (1<<7) /* RBFIM, RX Buffer Full Interrupt Mask */ + +#define BBA_IR 0x09 /* Interrupt Register, RW, 00h */ +#define BBA_IR_FRAGI (1<<0) /* FRAGI, Fragment Counter Interrupt */ +#define BBA_IR_RI (1<<1) /* RI, Receive Interrupt */ +#define BBA_IR_TI (1<<2) /* TI, Transmit Interrupt */ +#define BBA_IR_REI (1<<3) /* REI, Receive Error Interrupt */ +#define BBA_IR_TEI (1<<4) /* TEI, Transmit Error Interrupt */ +#define BBA_IR_FIFOEI (1<<5) /* FIFOEI, FIFO Error Interrupt */ +#define BBA_IR_BUSEI (1<<6) /* BUSEI, BUS Error Interrupt */ +#define BBA_IR_RBFI (1<<7) /* RBFI, RX Buffer Full Interrupt */ + +#define BBA_BP 0x0a/*+0x0b*/ /* Boundary Page Pointer Register */ +#define BBA_TLBP 0x0c/*+0x0d*/ /* TX Low Boundary Page Pointer Register */ +#define BBA_TWP 0x0e/*+0x0f*/ /* Transmit Buffer Write Page Pointer Register */ +#define BBA_TRP 0x12/*+0x13*/ /* Transmit Buffer Read Page Pointer Register */ +#define BBA_RWP 0x16/*+0x17*/ /* Receive Buffer Write Page Pointer Register */ +#define BBA_RRP 0x18/*+0x19*/ /* Receive Buffer Read Page Pointer Register */ +#define BBA_RHBP 0x1a/*+0x1b*/ /* Receive High Boundary Page Pointer Register */ + +#define BBA_RXINTT 0x14/*+0x15*/ /* Receive Interrupt Timer Register */ + +#define BBA_NAFR_PAR0 0x20 /* Physical Address Register Byte 0 */ +#define BBA_NAFR_PAR1 0x21 /* Physical Address Register Byte 1 */ +#define BBA_NAFR_PAR2 0x22 /* Physical Address Register Byte 2 */ +#define BBA_NAFR_PAR3 0x23 /* Physical Address Register Byte 3 */ +#define BBA_NAFR_PAR4 0x24 /* Physical Address Register Byte 4 */ +#define BBA_NAFR_PAR5 0x25 /* Physical Address Register Byte 5 */ + +#define BBA_NWAYC 0x30 /* NWAY Configuration Register, RW, 84h */ +#define BBA_NWAYC_FD (1<<0) /* FD, Full Duplex Mode */ +#define BBA_NWAYC_PS100 (1<<1) /* PS100/10, Port Select 100/10 */ +#define BBA_NWAYC_ANE (1<<2) /* ANE, Autonegotiation Enable */ +#define BBA_NWAYC_ANS_RA (1<<3) /* ANS, Restart Autonegotiation */ +#define BBA_NWAYC_LTE (1<<7) /* LTE, Link Test Enable */ + +#define BBA_NWAYS 0x31 +#define BBA_NWAYS_LS10 (1<<0) +#define BBA_NWAYS_LS100 (1<<1) +#define BBA_NWAYS_LPNWAY (1<<2) +#define BBA_NWAYS_ANCLPT (1<<3) +#define BBA_NWAYS_100TXF (1<<4) +#define BBA_NWAYS_100TXH (1<<5) +#define BBA_NWAYS_10TXF (1<<6) +#define BBA_NWAYS_10TXH (1<<7) + +#define BBA_GCA 0x32 /* GMAC Configuration A Register, RW, 00h */ +#define BBA_GCA_ARXERRB (1<<3) /* ARXERRB, Accept RX packet with error */ + +#define BBA_MISC 0x3d /* MISC Control Register 1, RW, 3ch */ +#define BBA_MISC_BURSTDMA (1<<0) +#define BBA_MISC_DISLDMA (1<<1) + +#define BBA_TXFIFOCNT 0x3e/*0x3f*/ /* Transmit FIFO Counter Register */ +#define BBA_WRTXFIFOD 0x48/*-0x4b*/ /* Write TX FIFO Data Port Register */ + +#define BBA_MISC2 0x50 /* MISC Control Register 2, RW, 00h */ +#define BBA_MISC2_HBRLEN0 (1<<0) /* HBRLEN, Host Burst Read Length */ +#define BBA_MISC2_HBRLEN1 (1<<1) /* " */ +#define BBA_MISC2_RUNTSIZE (1<<2) /* " */ +#define BBA_MISC2_DREQBCTRL (1<<3) /* " */ +#define BBA_MISC2_RINTSEL (1<<4) /* " */ +#define BBA_MISC2_ITPSEL (3<<5) /* " */ +#define BBA_MISC2_AUTORCVR (1<<7) /* Auto RX Full Recovery */ + +#define BBA_RX_STATUS_BF (1<<0) +#define BBA_RX_STATUS_CRC (1<<1) +#define BBA_RX_STATUS_FAE (1<<2) +#define BBA_RX_STATUS_FO (1<<3) +#define BBA_RX_STATUS_RW (1<<4) +#define BBA_RX_STATUS_MF (1<<5) +#define BBA_RX_STATUS_RF (1<<6) +#define BBA_RX_STATUS_RERR (1<<7) + +#define BBA_TX_STATUS_CC0 (1<<0) +#define BBA_TX_STATUS_CC1 (1<<1) +#define BBA_TX_STATUS_CC2 (1<<2) +#define BBA_TX_STATUS_CC3 (1<<3) +#define BBA_TX_STATUS_CCMASK (0x0f) +#define BBA_TX_STATUS_CRSLOST (1<<4) +#define BBA_TX_STATUS_UF (1<<5) +#define BBA_TX_STATUS_OWC (1<<6) +#define BBA_TX_STATUS_OWN (1<<7) +#define BBA_TX_STATUS_TERR (1<<7) + +#define BBA_TX_MAX_PACKET_SIZE 1518 /* 14+1500+4 */ +#define BBA_RX_MAX_PACKET_SIZE 1536 /* 6 pages * 256 bytes */ + +#define BBA_INIT_TLBP 0x00 +#define BBA_INIT_BP 0x01 +#define BBA_INIT_RHBP 0x0f +#define BBA_INIT_RWP BBA_INIT_BP +#define BBA_INIT_RRP BBA_INIT_BP + +#define BBA_NAPI_WEIGHT 16 + +#define RX_BUFFERS 16 + +#define cpu_to_be16(x) (x) +#define cpu_to_be32(x) (x) +static inline u16 cpu_to_le16(u16 x) { return (x<<8) | (x>>8);} +static inline u32 cpu_to_le32(u32 x) { return((x>>24) | ((x>>8)&0xff00) | ((x<<8)&0xff0000) | (x<<24));} + +#define cpu_to_le16p(addr) (cpu_to_le16(*(addr))) +#define cpu_to_le32p(addr) (cpu_to_le32(*(addr))) +#define cpu_to_be16p(addr) (cpu_to_be16(*(addr))) +#define cpu_to_be32p(addr) (cpu_to_be32(*(addr))) + +static inline void cpu_to_le16s(u16 *a) {*a = cpu_to_le16(*a);} +static inline void cpu_to_le32s(u32 *a) {*a = cpu_to_le32(*a);} +static inline void cpu_to_be16s(u16 *a) {*a = cpu_to_be16(*a);} +static inline void cpu_to_be32s(u32 *a) {*a = cpu_to_be32(*a);} + +#define le16_to_cpup(x) cpu_to_le16p(x) +#define le32_to_cpup(x) cpu_to_le32p(x) +#define be16_to_cpup(x) cpu_to_be16p(x) +#define be32_to_cpup(x) cpu_to_be32p(x) + +#define le16_to_cpus(x) cpu_to_le16s(x) +#define le32_to_cpus(x) cpu_to_le32s(x) +#define be16_to_cpus(x) cpu_to_be16s(x) +#define be32_to_cpus(x) cpu_to_be32s(x) + +struct bba_priv { + u8 revid; + u16 devid; + u8 acstart; + s8_t state; + struct uip_eth_addr *ethaddr; +}; + +#define X(a,b) b,a +struct bba_descr { + u32 X(X(next_packet_ptr:12, packet_len:12), status:8); +} __attribute((packed)); + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +/* new functions */ +#define bba_select() EXI_Select(EXI_CHANNEL_0,EXI_DEVICE_2,EXI_SPEED32MHZ) +#define bba_deselect() EXI_Deselect(EXI_CHANNEL_0) + +#define bba_in12(reg) ((bba_in8(reg)&0xff)|((bba_in8((reg)+1)&0x0f)<<8)) +#define bba_out12(reg,val) do { \ + bba_out8((reg),(val)&0xff); \ + bba_out8((reg)+1,((val)&0x0f00)>>8); \ + } while(0) + +#if UIP_LOGGING == 1 +#include +#define UIP_LOG(m) uip_log(__FILE__,__LINE__,m) +#else +#define UIP_LOG(m) +#endif /* UIP_LOGGING == 1 */ + +#if UIP_STATISTICS == 1 +struct uip_stats uip_stat; +#define UIP_STAT(s) s +#else +#define UIP_STAT(s) +#endif /* UIP_STATISTICS == 1 */ + +static s64 bba_arp_tmr = 0; + +static struct uip_pbuf *bba_recv_pbufs = NULL; +static struct uip_netif *bba_netif = NULL; +static struct bba_priv bba_device; +static struct bba_descr cur_descr; + +static void bba_cmd_ins(u32 reg,void *val,u32 len); +static void bba_cmd_outs(u32 reg,void *val,u32 len); +static void bba_ins(u32 reg,void *val,u32 len); +static void bba_outs(u32 reg,void *val,u32 len); + +static void bba_devpoll(u16 *pstatus); + +extern void udelay(int us); +extern u32 diff_msec(long long start,long long end); +extern u32 diff_usec(long long start,long long end); +extern long long gettime(); + +static __inline__ void bba_cmd_insnosel(u32 reg,void *val,u32 len) +{ + u16 req; + req = reg<<8; + EXI_Imm(EXI_CHANNEL_0,&req,sizeof(req),EXI_WRITE,NULL); + EXI_Sync(EXI_CHANNEL_0); + EXI_ImmEx(EXI_CHANNEL_0,val,len,EXI_READ); +} + +static void bba_cmd_ins(u32 reg,void *val,u32 len) +{ + bba_select(); + bba_cmd_insnosel(reg,val,len); + bba_deselect(); +} + +static __inline__ void bba_cmd_outsnosel(u32 reg,void *val,u32 len) +{ + u16 req; + req = (reg<<8)|0x4000; + EXI_Imm(EXI_CHANNEL_0,&req,sizeof(req),EXI_WRITE,NULL); + EXI_Sync(EXI_CHANNEL_0); + EXI_ImmEx(EXI_CHANNEL_0,val,len,EXI_WRITE); +} + +static void bba_cmd_outs(u32 reg,void *val,u32 len) +{ + bba_select(); + bba_cmd_outsnosel(reg,val,len); + bba_deselect(); +} + +static inline u8 bba_cmd_in8(u32 reg) +{ + u8 val; + bba_cmd_ins(reg,&val,sizeof(val)); + return val; +} + +static inline u8 bba_cmd_in8_slow(u32 reg) +{ + u8 val; + bba_select(); + bba_cmd_insnosel(reg,&val,sizeof(val)); + udelay(200); //usleep doesn't work on this amount, decrementer is based on 10ms, wait is 200us + bba_deselect(); + return val; +} + +static inline void bba_cmd_out8(u32 reg,u8 val) +{ + bba_cmd_outs(reg,&val,sizeof(val)); +} + +static inline u8 bba_in8(u32 reg) +{ + u8 val; + bba_ins(reg,&val,sizeof(val)); + return val; +} + +static inline void bba_out8(u32 reg,u8 val) +{ + bba_outs(reg,&val,sizeof(val)); +} + +static inline void bba_insnosel(u32 reg,void *val,u32 len) +{ + u32 req; + req = (reg<<8)|0x80000000; + EXI_Imm(EXI_CHANNEL_0,&req,sizeof(req),EXI_WRITE,NULL); + EXI_Sync(EXI_CHANNEL_0); + EXI_ImmEx(EXI_CHANNEL_0,val,len,EXI_READ); +} + +static void bba_ins(u32 reg,void *val,u32 len) +{ + bba_select(); + bba_insnosel(reg,val,len); + bba_deselect(); +} + +static inline void bba_outsnoselect(u32 reg,void *val,u32 len) +{ + u32 req; + req = (reg<<8)|0xC0000000; + EXI_Imm(EXI_CHANNEL_0,&req,sizeof(req),EXI_WRITE,NULL); + EXI_Sync(EXI_CHANNEL_0); + EXI_ImmEx(EXI_CHANNEL_0,val,len,EXI_WRITE); +} + +static void bba_outs(u32 reg,void *val,u32 len) +{ + bba_select(); + bba_outsnoselect(reg,val,len); + bba_deselect(); +} + +static inline void bba_insregister(u32 reg) +{ + u32 req; + req = (reg<<8)|0x80000000; + EXI_Imm(EXI_CHANNEL_0,&req,sizeof(req),EXI_WRITE,NULL); + EXI_Sync(EXI_CHANNEL_0); +} + +static inline void bba_insdata(void *val,u32 len) +{ + EXI_ImmEx(EXI_CHANNEL_0,val,len,EXI_READ); +} + + +static inline void bba_outsregister(u32 reg) +{ + u32 req; + req = (reg<<8)|0xC0000000; + EXI_Imm(EXI_CHANNEL_0,&req,sizeof(req),EXI_WRITE,NULL); + EXI_Sync(EXI_CHANNEL_0); +} + +static inline void bba_outsdata(void *val,u32 len) +{ + EXI_ImmEx(EXI_CHANNEL_0,val,len,EXI_WRITE); +} + +static __inline__ u32 __linkstate() +{ + u8 nways = 0; + + nways = bba_in8(BBA_NWAYS); + if(nways&BBA_NWAYS_LS10 || nways&BBA_NWAYS_LS100) return 1; + return 0; +} + +static u32 __bba_getlink_state_async() +{ + u32 ret; + + + if(EXI_Lock(EXI_CHANNEL_0,EXI_DEVICE_2,NULL)==0) return 0; + ret = __linkstate(); + EXI_Unlock(EXI_CHANNEL_0); + return ret; +} + + +static u32 __bba_read_cid() +{ + u16 cmd = 0; + u32 cid = 0; + + bba_select(); + EXI_Imm(EXI_CHANNEL_0,&cmd,2,EXI_WRITE,NULL); + EXI_Sync(EXI_CHANNEL_0); + EXI_Imm(EXI_CHANNEL_0,&cid,4,EXI_READ,NULL); + EXI_Sync(EXI_CHANNEL_0); + bba_deselect(); + + return cid; +} +static void __bba_reset() +{ + bba_out8(0x60,0x00); + udelay(10000); + bba_cmd_in8_slow(0x0F); + udelay(10000); + bba_out8(BBA_NCRA,BBA_NCRA_RESET); + bba_out8(BBA_NCRA,0x00); +} + +static void __bba_recv_init() +{ + bba_out8(BBA_NCRB,(BBA_NCRB_CA|BBA_NCRB_AB)); + bba_out8(BBA_MISC2,(BBA_MISC2_AUTORCVR)); + + bba_out12(BBA_TLBP, BBA_INIT_TLBP); + bba_out12(BBA_BP,BBA_INIT_BP); + bba_out12(BBA_RWP,BBA_INIT_RWP); + bba_out12(BBA_RRP,BBA_INIT_RRP); + bba_out12(BBA_RHBP,BBA_INIT_RHBP); + + bba_out8(BBA_GCA,BBA_GCA_ARXERRB); + bba_out8(BBA_NCRA,BBA_NCRA_SR); +} + +static void bba_process(struct uip_pbuf *p,struct uip_netif *dev) +{ + struct uip_eth_hdr *ethhdr = NULL; + struct bba_priv *priv = (struct bba_priv*)dev->state; + const s32 ethhlen = sizeof(struct uip_eth_hdr); + + if(p) { + ethhdr = p->payload; + switch(htons(ethhdr->type)) { + case UIP_ETHTYPE_IP: + uip_arp_ipin(dev,p); + uip_pbuf_header(p,-(ethhlen)); + dev->input(p,dev); + break; + case UIP_ETHTYPE_ARP: + uip_arp_arpin(dev,priv->ethaddr,p); + break; + default: + uip_pbuf_free(p); + break; + } + } +} + +static s8_t bba_start_rx(struct uip_netif *dev,u32 budget) +{ + s32 size; + u16 top,pos,rwp,rrp; + u32 pkt_status,recvd; + struct uip_pbuf *p,*q; + + UIP_LOG("bba_start_rx()\n"); + + recvd = 0; + rwp = bba_in12(BBA_RWP); + rrp = bba_in12(BBA_RRP); + while(recvd(BBA_RX_MAX_PACKET_SIZE+4)) { + UIP_LOG("bba_start_rx: packet dropped due to big buffer.\n"); + continue; + } + + if(pkt_status&(BBA_RX_STATUS_RERR|BBA_RX_STATUS_FAE)) { + UIP_LOG("bba_start_rx: packet dropped due to receive errors.\n"); + rwp = bba_in12(BBA_RWP); + rrp = bba_in12(BBA_RRP); + continue; + } + + pos = (rrp<<8)+4; + top = (BBA_INIT_RHBP+1)<<8; + + p = uip_pbuf_alloc(UIP_PBUF_RAW,size,UIP_PBUF_POOL); + if(p) { + for(q=p;q!=NULL;q=q->next) { + bba_select(); + bba_insregister(pos); + if((pos+size)payload,size); + } else { + s32 chunk = top-pos; + + size -= chunk; + pos = BBA_INIT_RRP<<8; + bba_insdata(q->payload,chunk); + bba_deselect(); + + bba_select(); + bba_insregister(pos); + bba_insdata(q->payload+chunk,size); + } + bba_deselect(); + pos += size; + } + + EXI_Unlock(EXI_CHANNEL_0); + bba_process(p,dev); + EXI_Lock(EXI_CHANNEL_0,EXI_DEVICE_2,NULL); + } else + break; + + recvd++; + + bba_out12(BBA_RRP,(rrp=cur_descr.next_packet_ptr)); + rwp = bba_in12(BBA_RWP); + } + return UIP_ERR_OK; +} + +static inline void bba_interrupt(u16 *pstatus) +{ + u8 ir,imr,status; + + ir = bba_in8(BBA_IR); + imr = bba_in8(BBA_IMR); + status = ir&imr; + + if(status&BBA_IR_FRAGI) { + bba_out8(BBA_IR,BBA_IR_FRAGI); + } + if(status&BBA_IR_RI) { + bba_start_rx(bba_netif,0x10); + bba_out8(BBA_IR,BBA_IR_RI); + } + if(status&BBA_IR_REI) { + bba_out8(BBA_IR,BBA_IR_REI); + } + if(status&BBA_IR_TI) { + bba_out8(BBA_IR,BBA_IR_TI); + } + if(status&BBA_IR_TEI) { + bba_out8(BBA_IR,BBA_IR_TEI); + } + if(status&BBA_IR_FIFOEI) { + bba_out8(BBA_IR,BBA_IR_FIFOEI); + } + if(status&BBA_IR_BUSEI) { + bba_out8(BBA_IR,BBA_IR_BUSEI); + } + if(status&BBA_IR_RBFI) { + bba_start_rx(bba_netif,0x10); + bba_out8(BBA_IR,BBA_IR_RBFI); + } + *pstatus |= status; +} + +static s8_t bba_dochallengeresponse() +{ + u16 status; + s32 cnt; + + UIP_LOG("bba_dochallengeresponse()\n"); + /* as we do not have interrupts we've to poll the irqs */ + cnt = 0; + do { + cnt++; + bba_devpoll(&status); + if(status==0x1000) cnt = 0; + } while(cnt<100 && !(status&0x0800)); + + if(cnt>=1000) return UIP_ERR_IF; + return UIP_ERR_OK; +} + +static s8_t __bba_init(struct uip_netif *dev) +{ + struct bba_priv *priv = (struct bba_priv*)dev->state; + if(!priv) return UIP_ERR_IF; + + __bba_reset(); + + priv->revid = bba_cmd_in8(0x01); + + bba_cmd_outs(0x04,&priv->devid,2); + bba_cmd_out8(0x05,priv->acstart); + + bba_out8(0x5b, (bba_in8(0x5b)&~0x80)); + bba_out8(0x5e, 0x01); + bba_out8(0x5c, (bba_in8(0x5c)|0x04)); + + __bba_recv_init(); + + bba_ins(BBA_NAFR_PAR0,priv->ethaddr->addr, 6); + + bba_out8(BBA_IR,0xFF); + bba_out8(BBA_IMR,0xFF&~BBA_IMR_FIFOEIM); + + bba_cmd_out8(0x02,BBA_CMD_IRMASKNONE); + + return UIP_ERR_OK; +} + +static s8_t bba_init_one(struct uip_netif *dev) +{ + s32 ret; + struct bba_priv *priv = (struct bba_priv*)dev->state; + + if(!priv) return UIP_ERR_IF; + + priv->revid = 0x00; + priv->devid = 0xD107; + priv->acstart = 0x4E; + + ret = __bba_init(dev); + + return ret; +} + +static s8_t bba_probe(struct uip_netif *dev) +{ + s32 ret; + u32 cid; + + if(EXI_Lock(EXI_CHANNEL_0,EXI_DEVICE_2,NULL)==0) return -1; + + cid = __bba_read_cid(); + if(cid!=BBA_CID) { + EXI_Unlock(EXI_CHANNEL_0); + return -1; + } + + ret = bba_init_one(dev); + + EXI_Unlock(EXI_CHANNEL_0); + return ret; +} + +static u32 bba_calc_response(struct uip_netif *dev,u32 val) +{ + u8 revid_0, revid_eth_0, revid_eth_1; + struct bba_priv *priv = (struct bba_priv*)dev->state; + + UIP_LOG("bba_calc_response()\n"); + + revid_0 = priv->revid; + revid_eth_0 = _SHIFTR(priv->devid,8,8); + revid_eth_1 = priv->devid&0xff; + + u8 i0, i1, i2, i3; + i0 = (val & 0xff000000) >> 24; + i1 = (val & 0x00ff0000) >> 16; + i2 = (val & 0x0000ff00) >> 8; + i3 = (val & 0x000000ff); + + u8 c0, c1, c2, c3; + c0 = ((i0 + i1 * 0xc1 + 0x18 + revid_0) ^ (i3 * i2 + 0x90) + ) & 0xff; + c1 = ((i1 + i2 + 0x90) ^ (c0 + i0 - 0xc1) + ) & 0xff; + c2 = ((i2 + 0xc8) ^ (c0 + ((revid_eth_0 + revid_0 * 0x23) ^ 0x19)) + ) & 0xff; + c3 = ((i0 + 0xc1) ^ (i3 + ((revid_eth_1 + 0xc8) ^ 0x90)) + ) & 0xff; + + return ((c0 << 24) | (c1 << 16) | (c2 << 8) | c3); +} + +static void bba_devpoll(u16 *pstatus) +{ + u8 status; + s64 now; + + UIP_LOG("bba_devpoll()\n"); + + now = gettime(); + if(diff_msec(bba_arp_tmr,now)>=UIP_ARP_TMRINTERVAL) { + uip_arp_timer(); + bba_arp_tmr = gettime(); + } + + status = 0; + *pstatus = 0; + if(EXI_Lock(EXI_CHANNEL_0,EXI_DEVICE_2,NULL)==1) { + status = bba_cmd_in8(0x03); + if(status) { + bba_cmd_out8(0x02,BBA_CMD_IRMASKALL); + if(status&0x80) { + *pstatus |= (status<<8); + bba_interrupt(pstatus); + bba_cmd_out8(0x03,0x80); + bba_cmd_out8(0x02,BBA_CMD_IRMASKNONE); + EXI_Unlock(EXI_CHANNEL_0); + return; + } + if(status&0x40) { + *pstatus |= (status<<8); + __bba_init(bba_netif); + bba_cmd_out8(0x03, 0x40); + bba_cmd_out8(0x02,BBA_CMD_IRMASKNONE); + EXI_Unlock(EXI_CHANNEL_0); + return; + } + if(status&0x20) { + *pstatus |= (status<<8); + bba_cmd_out8(0x03, 0x20); + bba_cmd_out8(0x02,BBA_CMD_IRMASKNONE); + EXI_Unlock(EXI_CHANNEL_0); + return; + } + if(status&0x10) { + u32 response,challange; + + *pstatus |= (status<<8); + bba_cmd_out8(0x05,bba_device.acstart); + bba_cmd_ins(0x08,&challange,sizeof(challange)); + response = bba_calc_response(bba_netif,challange); + bba_cmd_outs(0x09,&response,sizeof(response)); + bba_cmd_out8(0x03, 0x10); + bba_cmd_out8(0x02,BBA_CMD_IRMASKNONE); + EXI_Unlock(EXI_CHANNEL_0); + return; + } + if(status&0x08) { + *pstatus |= (status<<8); + bba_cmd_out8(0x03, 0x08); + bba_cmd_out8(0x02,BBA_CMD_IRMASKNONE); + EXI_Unlock(EXI_CHANNEL_0); + return; + } + + *pstatus |= (status<<8); + bba_interrupt(pstatus); + bba_cmd_out8(0x02,BBA_CMD_IRMASKNONE); + } + EXI_Unlock(EXI_CHANNEL_0); + } +} + +static s8_t __bba_start_tx(struct uip_netif *dev,struct uip_pbuf *p,struct uip_ip_addr *ipaddr) +{ + return uip_arp_out(dev,ipaddr,p); +} + +static s8_t __bba_link_tx(struct uip_netif *dev,struct uip_pbuf *p) +{ + u8 pad[60]; + u32 len; + struct uip_pbuf *tmp; + + if(EXI_Lock(EXI_CHANNEL_0,EXI_DEVICE_2,NULL)==0) return UIP_ERR_IF; + + if(p->tot_len>BBA_TX_MAX_PACKET_SIZE) { + UIP_LOG("__bba_link_tx: packet dropped due to big buffer.\n"); + EXI_Unlock(EXI_CHANNEL_0); + return UIP_ERR_PKTSIZE; + } + + if(!__linkstate()) { + EXI_Unlock(EXI_CHANNEL_0); + return UIP_ERR_ABRT; + } + + while((bba_in8(BBA_NCRA)&(BBA_NCRA_ST0|BBA_NCRA_ST1))); + + len = p->tot_len; + bba_out12(BBA_TXFIFOCNT,len); + + bba_select(); + bba_outsregister(BBA_WRTXFIFOD); + for(tmp=p;tmp!=NULL;tmp=tmp->next) { + bba_outsdata(tmp->payload,tmp->len); + } + if(lenflags |= UIP_NETIF_FLAG_LINK_UP; + uip_netif_setup(dev); + uip_arp_init(); + + bba_recv_pbufs = NULL; + bba_arp_tmr = gettime(); + + return UIP_ERR_OK; +} + +uipdev_s uip_bba_create(struct uip_netif *dev) +{ + dev->name[0] = IFNAME0; + dev->name[1] = IFNAME1; + + dev->output = __bba_start_tx; + dev->linkoutput = __bba_link_tx; + dev->mtu = 1500; + dev->flags = UIP_NETIF_FLAG_BROADCAST; + dev->hwaddr_len = 6; + + bba_device.ethaddr = (struct uip_eth_addr*)dev->hwaddr; + bba_device.state = UIP_ERR_OK; + + bba_netif = dev; + return &bba_device; +} + +void uip_bba_poll(struct uip_netif *dev) +{ + u16 status; + + UIP_LOG("uip_bba_poll()\n"); + + bba_devpoll(&status); + +} diff --git a/wii/libogc/libdb/uIP/bba.h b/wii/libogc/libdb/uIP/bba.h new file mode 100644 index 0000000000..4fa92f60e4 --- /dev/null +++ b/wii/libogc/libdb/uIP/bba.h @@ -0,0 +1,14 @@ +#ifndef __BBA_DBG_H__ +#define __BBA_DBG_H__ + +#include "uip.h" + +struct uip_netif; + +typedef void* uipdev_s; + +uipdev_s uip_bba_create(struct uip_netif *dev); +s8_t uip_bba_init(struct uip_netif *dev); +void uip_bba_poll(struct uip_netif *dev); + +#endif diff --git a/wii/libogc/libdb/uIP/memb.c b/wii/libogc/libdb/uIP/memb.c new file mode 100644 index 0000000000..5787a6db4c --- /dev/null +++ b/wii/libogc/libdb/uIP/memb.c @@ -0,0 +1,48 @@ +#include +#include + +#include "uip.h" +#include "memb.h" + +void memb_init(struct memb_blks *blk) +{ + UIP_MEMSET(blk->mem,0,(MEM_ALIGN_SIZE(blk->size)+sizeof(u32))*blk->num); +} + +void* memb_alloc(struct memb_blks *blk) +{ + s32 i; + u32 *ptr; + + ptr = (u32*)blk->mem; + for(i=0;inum;i++) { + if(*ptr==0) { + ++(*ptr); + return (void*)(ptr+1); + } + ptr = (u32*)(u8*)ptr+(MEM_ALIGN_SIZE(blk->size)+sizeof(u32)); + } + return NULL; +} + +u8 memb_free(struct memb_blks *blk,void *ptr) +{ + s32 i; + u32 *ptr2,*ptr1; + + ptr1 = ptr; + ptr2 = (u32*)blk->mem; + for(i=0;inum;i++) { + if(ptr2==(ptr1 - 1)) { + return --(*ptr2); + } + ptr2 = (u32*)(u8*)ptr2+(MEM_ALIGN_SIZE(blk->size)+sizeof(u32)); + } + return -1; +} + +u8 memb_ref(struct memb_blks *blk,void *ptr) +{ + u32 *pref = ptr-sizeof(u32); + return ++(*pref); +} diff --git a/wii/libogc/libdb/uIP/memb.h b/wii/libogc/libdb/uIP/memb.h new file mode 100644 index 0000000000..8da054ed7a --- /dev/null +++ b/wii/libogc/libdb/uIP/memb.h @@ -0,0 +1,21 @@ +#ifndef __MEMB_H__ +#define __MEMB_H__ + +#include + +#define MEMB(name,size,num) \ + static u8 memb_mem_##name[(MEM_ALIGN_SIZE(size)+sizeof(u32))*num]; \ + static struct memb_blks name = {size,num,memb_mem_##name} + +struct memb_blks { + u16 size; + u16 num; + u8 *mem; +}; + +void memb_init(struct memb_blks *blk); +void* memb_alloc(struct memb_blks *blk); +u8 memb_free(struct memb_blks *blk,void *ptr); +u8 memb_ref(struct memb_blks *blk,void *ptr); + +#endif diff --git a/wii/libogc/libdb/uIP/memr.c b/wii/libogc/libdb/uIP/memr.c new file mode 100644 index 0000000000..c4df89cafa --- /dev/null +++ b/wii/libogc/libdb/uIP/memr.c @@ -0,0 +1,155 @@ +#include +#include + +#include "uip.h" +#include "memr.h" + +#if UIP_ERRORING == 1 +#include +#define UIP_ERROR(m) uip_log(__FILE__,__LINE__,m) +#else +#define UIP_ERROR(m) +#endif /* UIP_ERRORING == 1 */ + +#define MIN_SIZE 12 +#define SIZEOF_STRUCT_MEM (sizeof(struct mem)+(((sizeof(struct mem)%MEM_ALIGNMENT)==0)?0:(4-(sizeof(struct mem)%MEM_ALIGNMENT)))) + +struct mem { + u32 next,prev; + u32 used; +}; + +static struct mem *ram_free; +static struct mem *ram_end; +static u8 ram_block[sizeof(struct mem)+UIP_MEM_SIZE+MEM_ALIGNMENT]; + +static void plug_holes(struct mem *rmem) +{ + struct mem *nmem; + struct mem *pmem; + + nmem = (struct mem*)&ram_block[rmem->next]; + if(rmem!=nmem && nmem->used==0 && (u8_t*)nmem!=(u8_t*)ram_end) { + if(ram_free==nmem) ram_free = rmem; + + rmem->next = nmem->next; + ((struct mem*)&ram_block[nmem->next])->prev = (u8_t*)rmem - ram_block; + } + + pmem = (struct mem*)&ram_block[rmem->prev]; + if(pmem!=rmem && pmem->used==0) { + if(ram_free==rmem) ram_free = pmem; + pmem->next = rmem->next; + ((struct mem*)&ram_block[rmem->next])->prev = (u8_t*)pmem - ram_block; + } +} + +void memr_init() +{ + struct mem *rmem; + + UIP_MEMSET(ram_block,0,UIP_MEM_SIZE); + rmem = (struct mem*)ram_block; + rmem->next = UIP_MEM_SIZE; + rmem->prev = 0; + rmem->used = 0; + + ram_end = (struct mem*)&ram_block[UIP_MEM_SIZE]; + ram_end->used = 1; + ram_end->prev = UIP_MEM_SIZE; + ram_end->next = UIP_MEM_SIZE; + + ram_free = (struct mem*)ram_block; +} + +void* memr_malloc(u32 size) +{ + u32 ptr,ptr2; + struct mem *rmem,*rmem2; + + if(size==0) return NULL; + + if(size%MEM_ALIGNMENT) size += MEM_ALIGNMENT - ((size+SIZEOF_STRUCT_MEM)%SIZEOF_STRUCT_MEM); + if(size>UIP_MEM_SIZE) return NULL; + + for(ptr = (u8_t*)ram_free - ram_block;ptrnext) { + rmem = (struct mem*)&ram_block[ptr]; + if(!rmem->used && rmem->next - (ptr + SIZEOF_STRUCT_MEM)>=size + SIZEOF_STRUCT_MEM) { + ptr2 = ptr + SIZEOF_STRUCT_MEM + size; + rmem2 = (struct mem*)&ram_block[ptr2]; + + rmem2->prev = ptr; + rmem2->next = rmem->next; + rmem->next = ptr2; + if(rmem->next!=UIP_MEM_SIZE) ((struct mem*)&ram_block[rmem2->next])->prev = ptr2; + + rmem2->used = 0; + rmem->used = 1; + + if(rmem==ram_free) { + while(ram_free->used && ram_free!=ram_end) ram_free = (struct mem*)&ram_block[ram_free->next]; + } + + return (u8_t*)rmem+SIZEOF_STRUCT_MEM; + } + } + return NULL; +} + +void memr_free(void *ptr) +{ + struct mem *rmem; + + if(ptr==NULL) return; + if((u8_t*)ptr<(u8_t*)ram_block || (u8_t*)ptr>=(u8_t*)ram_end) return; + + rmem = (struct mem*)((u8_t*)ptr - SIZEOF_STRUCT_MEM); + rmem->used = 0; + + if(rmemUIP_MEM_SIZE) return NULL; + if((u8_t*)ptr<(u8_t*)ram_block || (u8_t*)ptr>=(u8_t*)ram_end) { + UIP_ERROR("memr_realloc: illegal memory.\n"); + return ptr; + } + rmem = (struct mem*)((u8_t*)ptr - SIZEOF_STRUCT_MEM); + ptr1 = (u8_t*)rmem - ram_block; + size = rmem->next - ptr1 - SIZEOF_STRUCT_MEM; + + if(newsize+SIZEOF_STRUCT_MEM+MIN_SIZEused = 0; + rmem2->next = rmem->next; + rmem2->prev = ptr1; + rmem->next = ptr2; + if(rmem2->next!=UIP_MEM_SIZE) ((struct mem*)&ram_block[rmem2->next])->prev = ptr2; + + plug_holes(rmem2); + } + + return ptr; +} + +void* memr_reallocm(void *ptr,u32 newsize) +{ + void *nmem; + + nmem = memr_malloc(newsize); + if(nmem==NULL) return memr_realloc(ptr,newsize); + + UIP_MEMCPY(nmem,ptr,newsize); + memr_free(ptr); + + return nmem; +} diff --git a/wii/libogc/libdb/uIP/memr.h b/wii/libogc/libdb/uIP/memr.h new file mode 100644 index 0000000000..a2455d6cc4 --- /dev/null +++ b/wii/libogc/libdb/uIP/memr.h @@ -0,0 +1,12 @@ +#ifndef __MEMR_H__ +#define __MEMR_H__ + +#include + +void memr_init(); +void* memr_malloc(u32 size); +void memr_free(void *ptr); +void* memr_realloc(void *ptr,u32 newsize); +void* memr_reallocm(void *ptr,u32 newsize); + +#endif diff --git a/wii/libogc/libdb/uIP/uip.h b/wii/libogc/libdb/uIP/uip.h new file mode 100644 index 0000000000..2bc5713d17 --- /dev/null +++ b/wii/libogc/libdb/uIP/uip.h @@ -0,0 +1,176 @@ +/** + * \addtogroup uip + * @{ + */ + +/** + * \file + * Header file for the uIP TCP/IP stack. + * \author Adam Dunkels + * + * The uIP TCP/IP stack header file contains definitions for a number + * of C macros that are used by uIP programs as well as internal uIP + * structures, TCP/IP header structures and function declarations. + * + */ + + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * + */ + +#ifndef __UIP_H__ +#define __UIP_H__ + +#include "uipopt.h" +#include "uip_arch.h" +#include "uip_ip.h" + +#define UIP_ERR_OK 0 +#define UIP_ERR_MEM -1 +#define UIP_ERR_BUF -2 +#define UIP_ERR_ABRT -3 +#define UIP_ERR_RST -4 +#define UIP_ERR_CLSD -5 +#define UIP_ERR_CONN -6 +#define UIP_ERR_VAL -7 +#define UIP_ERR_ARG -8 +#define UIP_ERR_RTE -9 +#define UIP_ERR_USE -10 +#define UIP_ERR_IF -11 +#define UIP_ERR_PKTSIZE -17 + +#define UIP_PROTO_ICMP 1 +#define UIP_PROTO_TCP 6 +#define UIP_PROTO_UDP 17 + +/* Header sizes. */ +#define UIP_IP_HLEN 20 /* Size of IP header */ +#define UIP_TRANSPORT_HLEN 20 + +#define UIP_UDP_HLEN 8 /* Size of UDP header */ +#define UIP_TCP_HLEN 20 /* Size of TCP header */ +#define UIP_IPUDP_HLEN 28 /* Size of IP + UDP header */ +#define UIP_IPTCP_HLEN 40 /* Size of IP + TCP header */ + +/** + * Convert 16-bit quantity from host byte order to network byte order. + * + * This macro is primarily used for converting constants from host + * byte order to network byte order. For converting variables to + * network byte order, use the htons() function instead. + * + * \hideinitializer + */ +#ifndef HTONS +# if BYTE_ORDER == BIG_ENDIAN +# define HTONS(n) (n) +# else /* BYTE_ORDER == BIG_ENDIAN */ +# define HTONS(n) ((((u16_t)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8)) +# endif /* BYTE_ORDER == BIG_ENDIAN */ +#endif /* HTONS */ + +/** + * The structure holding the TCP/IP statistics that are gathered if + * UIP_STATISTICS is set to 1. + * + */ +struct uip_stats { + struct { + uip_stats_t drop; /**< Number of dropped packets at the IP + layer. */ + uip_stats_t recv; /**< Number of received packets at the IP + layer. */ + uip_stats_t sent; /**< Number of sent packets at the IP + layer. */ + uip_stats_t vhlerr; /**< Number of packets dropped due to wrong + IP version or header length. */ + uip_stats_t hblenerr; /**< Number of packets dropped due to wrong + IP length, high byte. */ + uip_stats_t lblenerr; /**< Number of packets dropped due to wrong + IP length, low byte. */ + uip_stats_t fragerr; /**< Number of packets dropped since they + were IP fragments. */ + uip_stats_t chkerr; /**< Number of packets dropped due to IP + checksum errors. */ + uip_stats_t protoerr; /**< Number of packets dropped since they + were neither ICMP, UDP nor TCP. */ + } ip; /**< IP statistics. */ + struct { + uip_stats_t drop; /**< Number of dropped ICMP packets. */ + uip_stats_t recv; /**< Number of received ICMP packets. */ + uip_stats_t sent; /**< Number of sent ICMP packets. */ + uip_stats_t typeerr; /**< Number of ICMP packets with a wrong + type. */ + } icmp; /**< ICMP statistics. */ + struct { + uip_stats_t drop; /**< Number of dropped TCP segments. */ + uip_stats_t recv; /**< Number of recived TCP segments. */ + uip_stats_t sent; /**< Number of sent TCP segments. */ + uip_stats_t chkerr; /**< Number of TCP segments with a bad + checksum. */ + uip_stats_t ackerr; /**< Number of TCP segments with a bad ACK + number. */ + uip_stats_t rst; /**< Number of recevied TCP RST (reset) segments. */ + uip_stats_t rexmit; /**< Number of retransmitted TCP segments. */ + uip_stats_t syndrop; /**< Number of dropped SYNs due to too few + connections was avaliable. */ + uip_stats_t synrst; /**< Number of SYNs for closed ports, + triggering a RST. */ + } tcp; /**< TCP statistics. */ +}; + +/** + * The uIP TCP/IP statistics. + * + * This is the variable in which the uIP TCP/IP statistics are gathered. + */ +extern struct uip_stats uip_stat; + +/** + * Convert 16-bit quantity from host byte order to network byte order. + * + * This function is primarily used for converting variables from host + * byte order to network byte order. For converting constants to + * network byte order, use the HTONS() macro instead. + */ +#ifndef htons +u16_t htons(u16_t val); +#endif /* htons */ + +/** @} */ + +#endif /* __UIP_H__ */ + + +/** @} */ + diff --git a/wii/libogc/libdb/uIP/uip_arch.c b/wii/libogc/libdb/uIP/uip_arch.c new file mode 100644 index 0000000000..5a973caf81 --- /dev/null +++ b/wii/libogc/libdb/uIP/uip_arch.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2001, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * + */ + +#include +#include +#include + +#include "uip.h" +#include "uip_arch.h" +#include "uip_ip.h" +#include "uip_tcp.h" +#include "uip_pbuf.h" + + +/*-----------------------------------------------------------------------------------*/ +u16_t uip_chksum(u16_t *sdata, u32_t len) +{ + u32_t acc; + + for(acc = 0;len > 1;len -= 2) { + acc += *sdata++; + } + + /* add up any odd byte */ + if(len==1) { + acc += htons((u16_t)((((u8_t *)sdata)[0]&0xff)<<8)); + } + while(acc>>16) acc = (acc&0xffffUL)+(acc>>16); + + return (u16_t)acc; +} + +/*-----------------------------------------------------------------------------------*/ +u16_t uip_chksum_pseudo(struct uip_pbuf *p,struct uip_ip_addr *src,struct uip_ip_addr *dst,u8_t proto,u16_t proto_len) +{ + u32_t acc,len,rem; + struct uip_pbuf *q; + + acc = 0; + + rem = proto_len; + for(q=p;q!=NULL && rem>0;q=q->next) { + len = (rem>q->len)?q->len:rem; + acc += uip_chksum(q->payload,len); + rem -= len; + } + + acc += (src->addr&0xffffUL); + acc += ((src->addr>>16)&0xffffUL); + acc += (dst->addr&0xffffUL); + acc += ((dst->addr>>16)&0xffffUL); + acc += (u32_t)htons(proto); + acc += (u32_t)htons(proto_len); + + while(acc>>16) acc = (acc&0xffffUL)+(acc>>16); + + return (u16_t)~(acc&0xffffUL); +} +/*-----------------------------------------------------------------------------------*/ +u16_t +uip_ipchksum(void *dataptr,u16_t len) +{ + return ~(uip_chksum(dataptr,len)); +} + +u16_t uip_ipchksum_pbuf(struct uip_pbuf *p) +{ + u32_t acc; + struct uip_pbuf *q; + + acc = 0; + for(q = p; q != NULL; q = q->next) { + acc += uip_chksum(q->payload,q->len); + } + while(acc>>16) acc = (acc&0xffffUL)+(acc>>16); + + return (u16_t)~(acc & 0xffffUL); +} + +void uip_log(const char *filename,int line_nb,char *msg) +{ + printf("%s(%d):\n%s\n",filename,line_nb,msg); +} + +/*-----------------------------------------------------------------------------------*/ diff --git a/wii/libogc/libdb/uIP/uip_arch.h b/wii/libogc/libdb/uIP/uip_arch.h new file mode 100644 index 0000000000..2a0e26a8b9 --- /dev/null +++ b/wii/libogc/libdb/uIP/uip_arch.h @@ -0,0 +1,170 @@ +/** + * \defgroup uiparch Architecture specific uIP functions + * @{ + * + * The functions in the architecture specific module implement the IP + * check sum and 32-bit additions. + * + * The IP checksum calculation is the most computationally expensive + * operation in the TCP/IP stack and it therefore pays off to + * implement this in efficient assembler. The purpose of the uip-arch + * module is to let the checksum functions to be implemented in + * architecture specific assembler. + * + */ + +/** + * \file + * Declarations of architecture specific functions. + * \author Adam Dunkels + */ + +/* + * Copyright (c) 2001, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * + */ + +#ifndef __UIP_ARCH_H__ +#define __UIP_ARCH_H__ + +#include "uip.h" + +#define UIP_MIN(x,y) (x)<(y)?(x):(y) + +#define MEM_ALIGNMENT 4 +#define MEM_ALIGN(mem) ((void*)(((u32_t)(mem)+MEM_ALIGNMENT-1)&~(u32_t)(MEM_ALIGNMENT-1))) +#define MEM_ALIGN_SIZE(size) (((size)+MEM_ALIGNMENT-1)&~(u32_t)(MEM_ALIGNMENT-1)) + +#define PACK_STRUCT_STRUCT __attribute__((packed)) +#define PACK_STRUCT_FIELD(x) x +#define PACK_STRUCT_BEGIN +#define PACK_STRUCT_END + +#ifndef htons +#define htons(x) (x) +#endif +#ifndef ntohs +#define ntohs(x) (x) +#endif +#ifndef htonl +#define htonl(x) (x) +#endif +#ifndef ntohl +#define ntohl(x) (x) +#endif + +struct uip_pbuf; +struct uip_ip_addr; + +/** + * Calculate the Internet checksum over a buffer. + * + * The Internet checksum is the one's complement of the one's + * complement sum of all 16-bit words in the buffer. + * + * See RFC1071. + * + * \note This function is not called in the current version of uIP, + * but future versions might make use of it. + * + * \param buf A pointer to the buffer over which the checksum is to be + * computed. + * + * \param len The length of the buffer over which the checksum is to + * be computed. + * + * \return The Internet checksum of the buffer. + */ +u16_t uip_chksum(u16_t *buf, u32_t len); + +/** + * Calculate the IP header checksum of the packet header in uip_buf. + * + * The IP header checksum is the Internet checksum of the 20 bytes of + * the IP header. + * + * \return The IP header checksum of the IP header in the uip_buf + * buffer. + */ +u16_t uip_ipchksum(void *dataptr,u16_t len); + +u16_t uip_ipchksum_pbuf(struct uip_pbuf *p); + +/** + * Calculate the TCP checksum of the packet in uip_buf and uip_appdata. + * + * The TCP checksum is the Internet checksum of data contents of the + * TCP segment, and a pseudo-header as defined in RFC793. + * + * \note The uip_appdata pointer that points to the packet data may + * point anywhere in memory, so it is not possible to simply calculate + * the Internet checksum of the contents of the uip_buf buffer. + * + * \return The TCP checksum of the TCP segment in uip_buf and pointed + * to by uip_appdata. + */ +u16_t uip_chksum_pseudo(struct uip_pbuf *p,struct uip_ip_addr *src,struct uip_ip_addr *dst,u8_t proto,u16_t proto_len); + + + +extern void tcpip_tmr_needed(); +#define tcp_tmr_needed tcpip_tmr_needed + +#if UIP_LIBC_MEMFUNCREPLACE +static __inline__ void uip_memcpy(void *dest,const void *src,s32_t len) +{ + u8_t *dest0 = (u8_t*)dest; + u8_t *src0 = (u8_t*)src; + + while(len--) { + *dest0++ = *src0++; + } +} + +static __inline__ void uip_memset(void *dest,s32_t c,s32_t len) +{ + u8_t *dest0 = (u8_t*)dest; + + while(len--) { + *dest0++ = (s8_t)c; + } +} + +#define UIP_MEMCPY uip_memcpy +#define UIP_MEMSET uip_memset +#else +#define UIP_MEMCPY memcpy +#define UIP_MEMSET memset +#endif + +/** @} */ + +#endif /* __UIP_ARCH_H__ */ diff --git a/wii/libogc/libdb/uIP/uip_arp.c b/wii/libogc/libdb/uIP/uip_arp.c new file mode 100644 index 0000000000..44cb1f5300 --- /dev/null +++ b/wii/libogc/libdb/uIP/uip_arp.c @@ -0,0 +1,464 @@ +/** + * \addtogroup uip + * @{ + */ + +/** + * \defgroup uiparp uIP Address Resolution Protocol + * @{ + * + * The Address Resolution Protocol ARP is used for mapping between IP + * addresses and link level addresses such as the Ethernet MAC + * addresses. ARP uses broadcast queries to ask for the link level + * address of a known IP address and the host which is configured with + * the IP address for which the query was meant, will respond with its + * link level address. + * + * \note This ARP implementation only supports Ethernet. + */ + +/** + * \file + * Implementation of the ARP Address Resolution Protocol. + * \author Adam Dunkels + * + */ + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * + */ + + +#include "uip_pbuf.h" +#include "uip_netif.h" +#include "uip_arp.h" + +#include + +#if UIP_LOGGING == 1 +#include +#define UIP_LOG(m) uip_log(__FILE__,__LINE__,m) +#else +#define UIP_LOG(m) +#endif /* UIP_LOGGING == 1 */ + +#if UIP_STATISTICS == 1 +struct uip_stats uip_stat; +#define UIP_STAT(s) s +#else +#define UIP_STAT(s) +#endif /* UIP_STATISTICS == 1 */ + + +#define ARP_TRY_HARD 0x01 + +#define ARP_MAXAGE 240 +#define ARP_MAXPENDING 2 + +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +#define ARP_HWTYPE_ETH 1 + +#define ARPH_HWLEN(hdr) (ntohs((hdr)->_hwlen_protolen) >> 8) +#define ARPH_PROTOLEN(hdr) (ntohs((hdr)->_hwlen_protolen) & 0xff) + +#define ARPH_HWLEN_SET(hdr, len) (hdr)->_hwlen_protolen = htons(ARPH_PROTOLEN(hdr) | ((len) << 8)) +#define ARPH_PROTOLEN_SET(hdr, len) (hdr)->_hwlen_protolen = htons((len) | (ARPH_HWLEN(hdr) << 8)) + +enum arp_state { + ARP_STATE_EMPTY, + ARP_STATE_PENDING, + ARP_STATE_STABLE, + ARP_STATE_EXPIRED +}; + +struct arp_entry { + struct uip_ip_addr ipaddr; + struct uip_eth_addr ethaddr; + enum arp_state state; + u8_t time; +}; + +static const struct uip_eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; +static struct arp_entry arp_table[UIP_ARPTAB_SIZE]; +/*-----------------------------------------------------------------------------------*/ +/** + * Initialize the ARP module. + * + */ +/*-----------------------------------------------------------------------------------*/ +void +uip_arp_init(void) +{ + s32_t i; + for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { + arp_table[i].state = ARP_STATE_EMPTY; + arp_table[i].time = 0; + } +} +/*-----------------------------------------------------------------------------------*/ +/** + * Periodic ARP processing function. + * + * This function performs periodic timer processing in the ARP module + * and should be called at regular intervals. The recommended interval + * is 10 seconds between the calls. + * + */ +/*-----------------------------------------------------------------------------------*/ +void +uip_arp_timer(void) +{ + u8_t i; + + for(i=0;i=ARP_MAXAGE) { + arp_table[i].state = ARP_STATE_EXPIRED; + } else if(arp_table[i].state==ARP_STATE_PENDING) { + if(arp_table[i].time>=ARP_MAXPENDING) arp_table[i].state = ARP_STATE_EXPIRED; + } + + if(arp_table[i].state==ARP_STATE_EXPIRED) arp_table[i].state = ARP_STATE_EMPTY; + } +} + +static s8_t uip_arp_findentry(struct uip_ip_addr *ipaddr,u8_t flags) +{ + s8_t old_pending = UIP_ARPTAB_SIZE, old_stable = UIP_ARPTAB_SIZE; + s8_t empty = UIP_ARPTAB_SIZE; + u8_t i = 0,age_pending = 0,age_stable = 0; + + /* Walk through the ARP mapping table and try to find an entry to + update. If none is found, the IP -> MAC address mapping is + inserted in the ARP table. */ + for(i = 0; i < UIP_ARPTAB_SIZE; ++i) { + if(empty==UIP_ARPTAB_SIZE && arp_table[i].state==ARP_STATE_EMPTY) { + empty = i; + } else if(arp_table[i].state==ARP_STATE_PENDING) { + if(ipaddr && ip_addr_cmp(ipaddr,&arp_table[i].ipaddr)) return i; + else if(arp_table[i].time>=age_pending) { + old_pending = i; + age_pending = arp_table[i].time; + } + } else if(arp_table[i].state==ARP_STATE_STABLE) { + if(ipaddr && ip_addr_cmp(ipaddr,&arp_table[i].ipaddr)) return i; + else if(arp_table[i].time>=age_stable) { + old_stable = i; + age_stable = arp_table[i].time; + } + } + } + if(empty==UIP_ARPTAB_SIZE && !(flags&ARP_TRY_HARD)) return UIP_ERR_MEM; + + if(emptyhwaddr_len;k++) arp_table[i].ethaddr.addr[k] = ethaddr->addr[k]; + + return UIP_ERR_OK; +} +/*-----------------------------------------------------------------------------------*/ +/** + * ARP processing for incoming IP packets + * + * This function should be called by the device driver when an IP + * packet has been received. The function will check if the address is + * in the ARP cache, and if so the ARP cache entry will be + * refreshed. If no ARP cache entry was found, a new one is created. + * + * This function expects an IP packet with a prepended Ethernet header + * in the uip_buf[] buffer, and the length of the packet in the global + * variable uip_len. + */ +/*-----------------------------------------------------------------------------------*/ +void +uip_arp_ipin(struct uip_netif *netif,struct uip_pbuf *p) +{ + struct uip_ethip_hdr *hdr; + + hdr = p->payload; + if(!ip_addr_netcmp(&hdr->ip.src,&netif->ip_addr,&netif->netmask)) return; + + uip_arp_update(netif,&hdr->ip.src,&hdr->ethhdr.src,0); + } + +/*-----------------------------------------------------------------------------------*/ +/** + * ARP processing for incoming ARP packets. + * + * This function should be called by the device driver when an ARP + * packet has been received. The function will act differently + * depending on the ARP packet type: if it is a reply for a request + * that we previously sent out, the ARP cache will be filled in with + * the values from the ARP reply. If the incoming ARP packet is an ARP + * request for our IP address, an ARP reply packet is created and put + * into the uip_buf[] buffer. + * + * When the function returns, the value of the global variable uip_len + * indicates whether the device driver should send out a packet or + * not. If uip_len is zero, no packet should be sent. If uip_len is + * non-zero, it contains the length of the outbound packet that is + * present in the uip_buf[] buffer. + * + * This function expects an ARP packet with a prepended Ethernet + * header in the uip_buf[] buffer, and the length of the packet in the + * global variable uip_len. + */ +/*-----------------------------------------------------------------------------------*/ +void +uip_arp_arpin(struct uip_netif *netif,struct uip_eth_addr *ethaddr,struct uip_pbuf *p) +{ + u8_t i,for_us; + struct uip_ip_addr sipaddr,dipaddr; + struct uip_arp_hdr *hdr; + + if(p->tot_lenpayload; + + *(struct uip_ip_addr2*)((void*)&sipaddr) = hdr->sipaddr; + *(struct uip_ip_addr2*)((void*)&dipaddr) = hdr->dipaddr; + + if(netif->ip_addr.addr==0) for_us = 0; + else for_us = ip_addr_cmp(&dipaddr,&netif->ip_addr); + + if(for_us) uip_arp_update(netif,&sipaddr,&hdr->shwaddr,ARP_TRY_HARD); + else uip_arp_update(netif,&sipaddr,&hdr->shwaddr,0); + + switch(htons(hdr->opcode)) { + case ARP_REQUEST: + if(for_us) { + hdr->opcode = htons(ARP_REPLY); + hdr->dipaddr = hdr->sipaddr; + hdr->sipaddr = *(struct uip_ip_addr2*)((void*)&netif->ip_addr); + + for(i=0;ihwaddr_len;i++) { + hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i]; + hdr->shwaddr.addr[i] = ethaddr->addr[i]; + hdr->ethhdr.dest.addr[i] = hdr->dhwaddr.addr[i]; + hdr->ethhdr.src.addr[i] = ethaddr->addr[i]; + } + + hdr->hwtype = htons(ARP_HWTYPE_ETH); + ARPH_HWLEN_SET(hdr,netif->hwaddr_len); + + hdr->protocol = htons(UIP_ETHTYPE_IP); + ARPH_PROTOLEN_SET(hdr,sizeof(struct uip_ip_addr)); + + netif->linkoutput(netif,p); + } else { + UIP_LOG("uip_arp_arpin: ip packet not for us.\n"); + } + break; + case ARP_REPLY: + break; + default: + UIP_LOG("uip_arp_arpin: ARP unknown opcode type.\n"); + break; + } + uip_pbuf_free(p); +} +/*-----------------------------------------------------------------------------------*/ +/** + * Prepend Ethernet header to an outbound IP packet and see if we need + * to send out an ARP request. + * + * This function should be called before sending out an IP packet. The + * function checks the destination IP address of the IP packet to see + * what Ethernet MAC address that should be used as a destination MAC + * address on the Ethernet. + * + * If the destination IP address is in the local network (determined + * by logical ANDing of netmask and our IP address), the function + * checks the ARP cache to see if an entry for the destination IP + * address is found. If so, an Ethernet header is prepended and the + * function returns. If no ARP cache entry is found for the + * destination IP address, the packet in the uip_buf[] is replaced by + * an ARP request packet for the IP address. The IP packet is dropped + * and it is assumed that they higher level protocols (e.g., TCP) + * eventually will retransmit the dropped packet. + * + * If the destination IP address is not on the local network, the IP + * address of the default router is used instead. + * + * When the function returns, a packet is present in the uip_buf[] + * buffer, and the length of the packet is in the global variable + * uip_len. + */ +/*-----------------------------------------------------------------------------------*/ +s8_t uip_arp_out(struct uip_netif *netif,struct uip_ip_addr *ipaddr,struct uip_pbuf *q) +{ + u8_t i; + struct uip_eth_addr *dest,*srcaddr,mcastaddr; + struct uip_eth_hdr *ethhdr; + + if(uip_pbuf_header(q,sizeof(struct uip_eth_hdr))!=0) { + UIP_LOG("uip_arp_out: could not allocate room for header.\n"); + return UIP_ERR_BUF; + } + + dest = NULL; + if(ip_addr_isbroadcast(ipaddr,netif)) { + dest = (struct uip_eth_addr*)ðbroadcast; + } else if(ip_addr_ismulticast(ipaddr)) { + /* Hash IP multicast address to MAC address.*/ + mcastaddr.addr[0] = 0x01; + mcastaddr.addr[1] = 0x00; + mcastaddr.addr[2] = 0x5e; + mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; + mcastaddr.addr[4] = ip4_addr3(ipaddr); + mcastaddr.addr[5] = ip4_addr4(ipaddr); + /* destination Ethernet address is multicast */ + dest = &mcastaddr; + } else { + if(!ip_addr_netcmp(ipaddr,&netif->ip_addr,&netif->netmask)) { + if(netif->gw.addr!=0) ipaddr = &netif->gw; + else return UIP_ERR_RTE; + } + return uip_arp_arpquery(netif,ipaddr,q); + } + + srcaddr = (struct uip_eth_addr*)netif->hwaddr; + ethhdr = q->payload; + for(i=0;ihwaddr_len;i++) { + ethhdr->dest.addr[i] = dest->addr[i]; + ethhdr->src.addr[i] = srcaddr->addr[i]; + } + + ethhdr->type = htons(UIP_ETHTYPE_IP); + return netif->linkoutput(netif,q); +} +/*-----------------------------------------------------------------------------------*/ + +s8_t uip_arp_arpquery(struct uip_netif *netif,struct uip_ip_addr *ipaddr,struct uip_pbuf *q) +{ + s8_t i,k; + s8_t err = UIP_ERR_MEM; + struct uip_eth_addr *srcaddr = (struct uip_eth_addr*)netif->hwaddr; + + if(ip_addr_isbroadcast(ipaddr,netif) || + ip_addr_ismulticast(ipaddr) || + ip_addr_isany(ipaddr)) return UIP_ERR_ARG; + + i = uip_arp_findentry(ipaddr,ARP_TRY_HARD); + if(i<0) return i; + + if(arp_table[i].state==ARP_STATE_EMPTY) arp_table[i].state = ARP_STATE_PENDING; + if(arp_table[i].state==ARP_STATE_PENDING || q==NULL) err = uip_arp_arprequest(netif,ipaddr); + + if(q!=NULL) { + if(arp_table[i].state==ARP_STATE_STABLE) { + + struct uip_eth_hdr *hdr = q->payload; + for(k=0;khwaddr_len;k++) { + hdr->dest.addr[k] = arp_table[i].ethaddr.addr[k]; + hdr->src.addr[k] = srcaddr->addr[k]; + } + + hdr->type = htons(UIP_ETHTYPE_IP); + err = netif->linkoutput(netif,q); + } else if(arp_table[i].state==ARP_STATE_PENDING) { + UIP_LOG("uip_arp_query: Ethernet destination address unknown, queueing disabled, packet dropped.\n"); + } + } + return err; +} + +s8_t uip_arp_arprequest(struct uip_netif *netif,struct uip_ip_addr *ipaddr) +{ + s8_t k; + s8_t err = UIP_ERR_MEM; + struct uip_arp_hdr *hdr; + struct uip_pbuf *p; + struct uip_eth_addr *srcaddr = (struct uip_eth_addr*)netif->hwaddr; + + p = uip_pbuf_alloc(UIP_PBUF_LINK,sizeof(struct uip_arp_hdr),UIP_PBUF_RAM); + if(p==NULL) return err; + + hdr = p->payload; + hdr->opcode = htons(ARP_REQUEST); + + for(k=0;khwaddr_len;k++) { + hdr->shwaddr.addr[k] = srcaddr->addr[k]; + hdr->dhwaddr.addr[k] = 0; + } + + hdr->dipaddr = *(struct uip_ip_addr2*)((void*)ipaddr); + hdr->sipaddr = *(struct uip_ip_addr2*)((void*)&netif->ip_addr); + + hdr->hwtype = htons(ARP_HWTYPE_ETH); + ARPH_HWLEN_SET(hdr,netif->hwaddr_len); + + hdr->protocol = htons(UIP_ETHTYPE_IP); + ARPH_PROTOLEN_SET(hdr,sizeof(struct uip_ip_addr)); + for(k=0;khwaddr_len;k++) { + hdr->ethhdr.dest.addr[k] = 0xff; + hdr->ethhdr.src.addr[k] = srcaddr->addr[k]; + } + hdr->ethhdr.type = htons(UIP_ETHTYPE_ARP); + + err = netif->linkoutput(netif,p); + uip_pbuf_free(p); + + return err; +} + +/** @} */ +/** @} */ diff --git a/wii/libogc/libdb/uIP/uip_arp.h b/wii/libogc/libdb/uIP/uip_arp.h new file mode 100644 index 0000000000..16569cf43a --- /dev/null +++ b/wii/libogc/libdb/uIP/uip_arp.h @@ -0,0 +1,150 @@ +/** + * \addtogroup uip + * @{ + */ + +/** + * \addtogroup uiparp + * @{ + */ + +/** + * \file + * Macros and definitions for the ARP module. + * \author Adam Dunkels + */ + + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * + */ + +#ifndef __UIP_ARP_H__ +#define __UIP_ARP_H__ + +#include "uip.h" +#include "uip_arch.h" + +#define UIP_ARP_TMRINTERVAL 5000 + +#define UIP_ETHTYPE_ARP 0x0806 +#define UIP_ETHTYPE_IP 0x0800 +#define UIP_ETHTYPE_IP6 0x86dd +/** + * Representation of a 48-bit Ethernet address. + */ +PACK_STRUCT_BEGIN +struct uip_eth_addr { + PACK_STRUCT_FIELD(u8_t addr[6]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +/** + * The Ethernet header. + */ +PACK_STRUCT_BEGIN +struct uip_eth_hdr { + PACK_STRUCT_FIELD(struct uip_eth_addr dest); + PACK_STRUCT_FIELD(struct uip_eth_addr src); + PACK_STRUCT_FIELD(u16_t type); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +PACK_STRUCT_BEGIN +struct uip_arp_hdr { + PACK_STRUCT_FIELD(struct uip_eth_hdr ethhdr); + PACK_STRUCT_FIELD(u16_t hwtype); + PACK_STRUCT_FIELD(u16_t protocol); + PACK_STRUCT_FIELD(u16_t _hwlen_protolen); + PACK_STRUCT_FIELD(u16_t opcode); + PACK_STRUCT_FIELD(struct uip_eth_addr shwaddr); + PACK_STRUCT_FIELD(struct uip_ip_addr2 sipaddr); + PACK_STRUCT_FIELD(struct uip_eth_addr dhwaddr); + PACK_STRUCT_FIELD(struct uip_ip_addr2 dipaddr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +PACK_STRUCT_BEGIN +struct uip_ethip_hdr { + PACK_STRUCT_FIELD(struct uip_eth_hdr ethhdr); + PACK_STRUCT_FIELD(struct uip_ip_hdr ip); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END + + +extern struct uip_eth_addr uip_ethaddr; + +struct uip_pbuf; +struct uip_netif; + +/* The uip_arp_init() function must be called before any of the other + ARP functions. */ +void uip_arp_init(void); + +/* The uip_arp_ipin() function should be called whenever an IP packet + arrives from the Ethernet. This function refreshes the ARP table or + inserts a new mapping if none exists. The function assumes that an + IP packet with an Ethernet header is present in the uip_buf buffer + and that the length of the packet is in the uip_len variable. */ +void uip_arp_ipin(struct uip_netif *netif,struct uip_pbuf *p); + +/* The uip_arp_arpin() should be called when an ARP packet is received + by the Ethernet driver. This function also assumes that the + Ethernet frame is present in the uip_buf buffer. When the + uip_arp_arpin() function returns, the contents of the uip_buf + buffer should be sent out on the Ethernet if the uip_len variable + is > 0. */ +void uip_arp_arpin(struct uip_netif *netif,struct uip_eth_addr *ethaddr,struct uip_pbuf *p); + +/* The uip_arp_out() function should be called when an IP packet + should be sent out on the Ethernet. This function creates an + Ethernet header before the IP header in the uip_buf buffer. The + Ethernet header will have the correct Ethernet MAC destination + address filled in if an ARP table entry for the destination IP + address (or the IP address of the default router) is present. If no + such table entry is found, the IP packet is overwritten with an ARP + request and we rely on TCP to retransmit the packet that was + overwritten. In any case, the uip_len variable holds the length of + the Ethernet frame that should be transmitted. */ +s8_t uip_arp_out(struct uip_netif *netif,struct uip_ip_addr *ipaddr,struct uip_pbuf *q); + +/* The uip_arp_timer() function should be called every ten seconds. It + is responsible for flushing old entries in the ARP table. */ +void uip_arp_timer(void); + +s8_t uip_arp_arpquery(struct uip_netif *netif,struct uip_ip_addr *ipaddr,struct uip_pbuf *q); + +s8_t uip_arp_arprequest(struct uip_netif *netif,struct uip_ip_addr *ipaddr); + +#endif /* __UIP_ARP_H__ */ + + diff --git a/wii/libogc/libdb/uIP/uip_icmp.c b/wii/libogc/libdb/uIP/uip_icmp.c new file mode 100644 index 0000000000..5c96e65355 --- /dev/null +++ b/wii/libogc/libdb/uIP/uip_icmp.c @@ -0,0 +1,101 @@ +#include +#include + +#include "uip_ip.h" +#include "uip_pbuf.h" +#include "uip_netif.h" +#include "uip_icmp.h" + +#if UIP_LOGGING == 1 +#include +#define UIP_LOG(m) uip_log(__FILE__,__LINE__,m) +#else +#define UIP_LOG(m) +#endif /* UIP_LOGGING == 1 */ + +#if UIP_STATISTICS == 1 +struct uip_stats uip_stat; +#define UIP_STAT(s) s +#else +#define UIP_STAT(s) +#endif /* UIP_STATISTICS == 1 */ + +void uip_icmpinput(struct uip_pbuf *p,struct uip_netif *inp) +{ + u8_t type; + u16_t hlen; + struct uip_ip_addr tmpaddr; + struct uip_ip_hdr *iphdr; + struct uip_icmp_echo_hdr *iecho; + + iphdr = p->payload; + hlen = UIP_IPH_HL(iphdr)*4; + if(uip_pbuf_header(p,-((s16_t)hlen)) || p->tot_lenpayload); + //code = *((u8_t*)p->payload+1); + switch(type) { + case UIP_ICMP_ECHO: + if(ip_addr_isbroadcast(&iphdr->dst,inp) || ip_addr_ismulticast(&iphdr->dst)) { + UIP_LOG("uip_icmpinput: Not echoing to broadcast pings.\n"); + uip_pbuf_free(p); + return; + } + + if(p->tot_lenpayload; + if(uip_ipchksum_pbuf(p)!=0) { + UIP_LOG("uip_icmpinput: checksum failed for received ICMP echo.\n"); + uip_pbuf_free(p); + return; + } + + tmpaddr.addr = iphdr->src.addr; + iphdr->src.addr = iphdr->dst.addr; + iphdr->dst.addr = tmpaddr.addr; + UIP_ICMPH_TYPE_SET(iecho,UIP_ICMP_ER); + + if(iecho->chksum>=htons(0xffff-(UIP_ICMP_ECHO<<8))) iecho->chksum += htons(UIP_ICMP_ECHO<<8)+1; + else iecho->chksum += htons(UIP_ICMP_ECHO<<8); + + uip_pbuf_header(p,hlen); + uip_ipoutput_if(p,&iphdr->src,NULL,UIP_IPH_TTL(iphdr),0,UIP_PROTO_ICMP,inp); + break; + default: + UIP_LOG("uip_icmpinput: ICMP type/code not supported.\n"); + break; + } + uip_pbuf_free(p); +} + +void uip_icmp_destunreach(struct uip_pbuf *p,enum uip_icmp_dur_type t) +{ + struct uip_pbuf *q; + struct uip_ip_hdr *iphdr; + struct uip_icmp_dur_hdr *idur; + + q = uip_pbuf_alloc(UIP_PBUF_IP,sizeof(struct uip_icmp_dur_hdr)+UIP_IP_HLEN+8,UIP_PBUF_RAM); + + iphdr = p->payload; + idur = q->payload; + + UIP_ICMPH_TYPE_SET(idur,UIP_ICMP_DUR); + UIP_ICMPH_CODE_SET(idur,t); + + UIP_MEMCPY((u8_t*)q->payload+sizeof(struct uip_icmp_dur_hdr),p->payload,UIP_IP_HLEN+8); + + idur->chksum = 0; + idur->chksum = uip_ipchksum(idur,q->len); + + uip_ipoutput(q,NULL,&iphdr->src,UIP_ICMP_TTL,0,UIP_PROTO_ICMP); + uip_pbuf_free(q); +} diff --git a/wii/libogc/libdb/uIP/uip_icmp.h b/wii/libogc/libdb/uIP/uip_icmp.h new file mode 100644 index 0000000000..42928a0fab --- /dev/null +++ b/wii/libogc/libdb/uIP/uip_icmp.h @@ -0,0 +1,60 @@ +#ifndef __UIP_ICMP_H__ +#define __UIP_ICMP_H__ + +#include "uip.h" +#include "uip_arch.h" + +#define UIP_ICMP_TTL 255 + +#define UIP_ICMP_ER 0 /* echo reply */ +#define UIP_ICMP_DUR 3 /* destination unreachable */ +#define UIP_ICMP_SQ 4 /* source quench */ +#define UIP_ICMP_RD 5 /* redirect */ +#define UIP_ICMP_ECHO 8 /* echo */ +#define UIP_ICMP_TE 11 /* time exceeded */ +#define UIP_ICMP_PP 12 /* parameter problem */ +#define UIP_ICMP_TS 13 /* timestamp */ +#define UIP_ICMP_TSR 14 /* timestamp reply */ +#define UIP_ICMP_IRQ 15 /* information request */ +#define UIP_ICMP_IR 16 /* information reply */ + +#define UIP_ICMPH_TYPE(hdr) (ntohs((hdr)->_type_code) >> 8) +#define UIP_ICMPH_CODE(hdr) (ntohs((hdr)->_type_code) & 0xff) + +#define UIP_ICMPH_TYPE_SET(hdr, type) ((hdr)->_type_code = htons(UIP_ICMPH_CODE(hdr) | ((type) << 8))) +#define UIP_ICMPH_CODE_SET(hdr, code) ((hdr)->_type_code = htons((code) | (UIP_ICMPH_TYPE(hdr) << 8))) + +enum uip_icmp_dur_type { + UIP_ICMP_DUR_NET = 0, /* net unreachable */ + UIP_ICMP_DUR_HOST = 1, /* host unreachable */ + UIP_ICMP_DUR_PROTO = 2, /* protocol unreachable */ + UIP_ICMP_DUR_PORT = 3, /* port unreachable */ + UIP_ICMP_DUR_FRAG = 4, /* fragmentation needed and DF set */ + UIP_ICMP_DUR_SR = 5 /* source route failed */ +}; + +PACK_STRUCT_BEGIN +struct uip_icmp_echo_hdr { + PACK_STRUCT_FIELD(u16_t _type_code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t id); + PACK_STRUCT_FIELD(u16_t seqno); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +PACK_STRUCT_BEGIN +struct uip_icmp_dur_hdr { + PACK_STRUCT_FIELD(u16_t _type_code); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u32_t unused); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +struct uip_pbuf; +struct uip_netif; + + +void uip_icmpinput(struct uip_pbuf *p,struct uip_netif *inp); +void uip_icmp_destunreach(struct uip_pbuf *p,enum uip_icmp_dur_type t); + +#endif diff --git a/wii/libogc/libdb/uIP/uip_ip.c b/wii/libogc/libdb/uIP/uip_ip.c new file mode 100644 index 0000000000..baac2014b9 --- /dev/null +++ b/wii/libogc/libdb/uIP/uip_ip.c @@ -0,0 +1,490 @@ +#include +#include +#include + +#include "uip_ip.h" +#include "uip_tcp.h" +#include "uip_icmp.h" +#include "uip_netif.h" +#include "uip_pbuf.h" + +#if UIP_LOGGING == 1 +#include +#define UIP_LOG(m) uip_log(__FILE__,__LINE__,m) +#else +#define UIP_LOG(m) +#endif /* UIP_LOGGING == 1 */ + +#if UIP_ERRORING == 1 +#include +#define UIP_ERROR(m) uip_log(__FILE__,__LINE__,m) +#else +#define UIP_ERROR(m) +#endif /* UIP_ERRORING == 1 */ + +#if UIP_STATISTICS == 1 +struct uip_stats uip_stat; +#define UIP_STAT(s) s +#else +#define UIP_STAT(s) +#endif /* UIP_STATISTICS == 1 */ + +const struct uip_ip_addr ipaddr_any = { 0x0000000UL }; +const struct uip_ip_addr ipaddr_broadcast = { 0xffffffffUL }; + +#if UIP_IP_REASSEMBLY + +#define UIP_REASS_FLAG_LASTFRAG 0x01 +#define UIP_REASS_BUFSIZE 5760 + +static u8_t uip_reassbuf[UIP_IP_HLEN+UIP_REASS_BUFSIZE]; +static u8_t uip_reassbitmap[UIP_REASS_BUFSIZE / (8 * 8)]; +static const u8_t bitmap_bits[8] = {0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01}; +static u16_t uip_reasslen; +static u8_t uip_reassflags; +static u8_t uip_reasstmr; +static s64 uip_reasstime = 0; + +extern s64 gettime(); + +static struct uip_pbuf* uip_copyfrom_pbuf(struct uip_pbuf *p,u16_t *offset,u8_t *buffer,u16_t len) +{ + u16_t l; + + p->payload = (u8_t*)p->payload+(*offset); + p->len -= (*offset); + while(len) { + l = lenlen?len:p->len; + UIP_MEMCPY(buffer,p->payload,l); + buffer += l; + len -= l; + if(len) p = p->next; + else *offset = l; + } + return p; +} + +static struct uip_pbuf* uip_ipreass(struct uip_pbuf *p) +{ + u16_t offset, len; + u16_t i; + struct uip_pbuf *q; + struct uip_ip_hdr *iphdr,*fraghdr; + + /* If ip_reasstmr is zero, no packet is present in the buffer, so we + write the IP header of the fragment into the reassembly + buffer. The timer is updated with the maximum age. */ + iphdr = (struct uip_ip_hdr*)uip_reassbuf; + fraghdr = (struct uip_ip_hdr*)p->payload; + if(uip_reasstmr == 0) { + UIP_MEMCPY(iphdr, fraghdr, UIP_IP_HLEN); + uip_reasstmr = UIP_REASS_MAXAGE; + uip_reassflags = 0; + uip_reasstime = gettime(); + /* Clear the bitmap. */ + UIP_MEMSET(uip_reassbitmap, 0,sizeof(uip_reassbitmap)); + } + + /* Check if the incoming fragment matches the one currently present + in the reasembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if(ip_addr_cmp(&iphdr->src,&fraghdr->src) + && ip_addr_cmp(&iphdr->dst,&fraghdr->dst) + && UIP_IPH_ID(iphdr) == UIP_IPH_ID(fraghdr)) { + + len = ntohs(UIP_IPH_LEN(fraghdr)) - UIP_IPH_HL(fraghdr)*4; + offset = (ntohs(UIP_IPH_OFFSET(fraghdr))&UIP_IP_OFFMASK)*8; + + /* If the offset or the offset + fragment length overflows the + reassembly buffer, we discard the entire packet. */ + if(offset > UIP_REASS_BUFSIZE || + offset + len > UIP_REASS_BUFSIZE) { + uip_reasstmr = 0; + uip_reasstime = 0; + goto nullreturn; + } + + /* Copy the fragment into the reassembly buffer, at the right + offset. */ + i = UIP_IPH_HL(fraghdr)*4; + uip_copyfrom_pbuf(p,&i,&uip_reassbuf[UIP_IP_HLEN+offset],len); + + /* Update the bitmap. */ + if(offset / (8 * 8) == (offset + len) / (8 * 8)) { + /* If the two endpoints are in the same byte, we only update + that byte. */ + + uip_reassbitmap[offset / (8 * 8)] |= + bitmap_bits[(offset / 8 ) & 7] & + ~bitmap_bits[((offset + len) / 8 ) & 7]; + } else { + /* If the two endpoints are in different bytes, we update the + bytes in the endpoints and fill the stuff inbetween with + 0xff. */ + uip_reassbitmap[offset / (8 * 8)] |= + bitmap_bits[(offset / 8 ) & 7]; + for(i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) { + uip_reassbitmap[i] = 0xff; + } + uip_reassbitmap[(offset + len) / (8 * 8)] |= + ~bitmap_bits[((offset + len) / 8 ) & 7]; + } + + /* If this fragment has the More Fragments flag set to zero, we + know that this is the last fragment, so we can calculate the + size of the entire packet. We also set the + IP_REASS_FLAG_LASTFRAG flag to indicate that we have received + the final fragment. */ + + if((ntohs(UIP_IPH_OFFSET(fraghdr))&UIP_IP_MF)==0) { + uip_reassflags |= UIP_REASS_FLAG_LASTFRAG; + uip_reasslen = offset + len; + } + + /* Finally, we check if we have a full packet in the buffer. We do + this by checking if we have the last fragment and if all bits + in the bitmap are set. */ + if(uip_reassflags & UIP_REASS_FLAG_LASTFRAG) { + /* Check all bytes up to and including all but the last byte in + the bitmap. */ + for(i = 0; i < uip_reasslen / (8 * 8) - 1; ++i) { + if(uip_reassbitmap[i] != 0xff) { + goto nullreturn; + } + } + /* Check the last byte in the bitmap. It should contain just the + right amount of bits. */ + if(uip_reassbitmap[uip_reasslen / (8 * 8)] != + (u8_t)~bitmap_bits[uip_reasslen / 8 & 7]) { + goto nullreturn; + } + + uip_reasslen += UIP_IP_HLEN; + + /* Pretend to be a "normal" (i.e., not fragmented) IP packet + from now on. */ + UIP_IPH_LEN_SET(iphdr,htons(uip_reasslen)); + UIP_IPH_OFFSET_SET(iphdr,0); + UIP_IPH_CHKSUM_SET(iphdr,0); + UIP_IPH_CHKSUM_SET(iphdr,uip_ipchksum(iphdr,UIP_IP_HLEN)); + + /* If we have come this far, we have a full packet in the + buffer, so we allocate a pbuf and copy the packet into it. We + also reset the timer. */ + uip_reasstmr = 0; + uip_reasstime = 0; + uip_pbuf_free(p); + p = uip_pbuf_alloc(UIP_PBUF_LINK,uip_reasslen,UIP_PBUF_POOL); + if(p==NULL) return NULL; + + i = 0; + for(q=p;q!=NULL;q=q->next) { + UIP_MEMCPY(q->payload,&uip_reassbuf[i],((q->len>(uip_reasslen-i))?(uip_reasslen-i):q->len)); + i += q->len; + } + return p; + } + } + + nullreturn: + uip_pbuf_free(p); + return NULL; +} +#endif /* UIP_IP_REASSEMBLY */ + +#if UIP_IP_FRAG +#define MAX_MTU 1500 +static u8_t buf[MEM_ALIGN_SIZE(MAX_MTU)]; + +s8_t uip_ipfrag(struct uip_pbuf *p,struct uip_netif *netif,struct uip_ip_addr *ipaddr) +{ + struct uip_pbuf *rambuf; + struct uip_pbuf *header; + struct uip_ip_hdr *iphdr; + u16_t left,cop,ofo,omf,last,tmp; + u16_t mtu = netif->mtu; + u16_t poff = UIP_IP_HLEN; + u16_t nfb = 0; + + rambuf = uip_pbuf_alloc(UIP_PBUF_LINK,0,UIP_PBUF_REF); + rambuf->tot_len = rambuf->len = mtu; + rambuf->payload = MEM_ALIGN(buf); + + iphdr = rambuf->payload; + UIP_MEMCPY(iphdr,p->payload,UIP_IP_HLEN); + + tmp = ntohs(UIP_IPH_OFFSET(iphdr)); + ofo = tmp&UIP_IP_OFFMASK; + omf = tmp&UIP_IP_MF; + + left = p->tot_len - UIP_IP_HLEN; + while(left) { + last = (left<=(mtu-UIP_IP_HLEN)); + + ofo += nfb; + tmp = omf|(UIP_IP_OFFMASK&ofo); + + if(!last) tmp |= UIP_IP_MF; + UIP_IPH_OFFSET_SET(iphdr,htons(tmp)); + + nfb = (mtu - UIP_IP_HLEN)/8; + cop = last?left:nfb*8; + + p = uip_copyfrom_pbuf(p,&poff,(u8_t*)iphdr+UIP_IP_HLEN,cop); + + UIP_IPH_LEN_SET(iphdr,htons(cop+UIP_IP_HLEN)); + UIP_IPH_CHKSUM_SET(iphdr,0); + UIP_IPH_CHKSUM_SET(iphdr,uip_ipchksum(iphdr,UIP_IP_HLEN)); + + if(last) uip_pbuf_realloc(rambuf,left+UIP_IP_HLEN); + + header = uip_pbuf_alloc(UIP_PBUF_LINK,0,UIP_PBUF_RAM); + uip_pbuf_chain(header,rambuf); + netif->output(netif,header,ipaddr); + uip_pbuf_free(header); + + left -= cop; + } + uip_pbuf_free(rambuf); + return UIP_ERR_OK; +} +#endif /* UIP_IP_FRAG */ + +struct uip_netif* uip_iproute(struct uip_ip_addr *dst) +{ + struct uip_netif *netif; + + for(netif=uip_netif_list;netif!=NULL;netif=netif->next) { + if(ip_addr_netcmp(dst,&netif->ip_addr,&netif->netmask)) return netif; + } + + return uip_netif_default; +} + +u8_t uip_ipaddr_isbroadcast(struct uip_ip_addr *addr,struct uip_netif *netif) +{ + if(addr->addr==ipaddr_broadcast.addr + || addr->addr==ipaddr_any.addr) + return 1; + else if(!(netif->flags&UIP_NETIF_FLAG_BROADCAST)) + return 0; + else if(addr->addr==netif->ip_addr.addr) + return 0; + else if(ip_addr_netcmp(addr,&netif->ip_addr,&netif->netmask) + && ((addr->addr&~netif->netmask.addr)==(ipaddr_broadcast.addr&~netif->netmask.addr))) + return 1; + else + return 0; +} + +s8_t uip_ipinput(struct uip_pbuf *p,struct uip_netif *inp) +{ + u16_t iphdr_len; + struct uip_ip_hdr *iphdr; + struct uip_netif *netif; + + iphdr = p->payload; + if(UIP_IPH_V(iphdr)!=4) { + UIP_ERROR("uip_ipinput: ip packet dropped due to bad version number.\n"); + uip_pbuf_free(p); + return 0; + } + + iphdr_len = UIP_IPH_HL(iphdr); + iphdr_len *= 4; + + if(iphdr_len>p->len) { + UIP_ERROR("uip_ipinput: ip packet dropped due to too small packet size.\n"); + uip_pbuf_free(p); + return 0; + } + + if(uip_ipchksum(iphdr,iphdr_len)!=0) { + UIP_STAT(++uip_stat.ip.drop); + UIP_STAT(++uip_stat.ip.chkerr); + UIP_ERROR("uip_ipinput: bad checksum.\n"); + uip_pbuf_free(p); + return 0; + } + + uip_pbuf_realloc(p,ntohs(UIP_IPH_LEN(iphdr))); + + for(netif=uip_netif_list;netif!=NULL;netif=netif->next) { + if(uip_netif_isup(netif) && !ip_addr_isany(&netif->ip_addr)) { + if(ip_addr_cmp(&iphdr->dst,&netif->ip_addr) || + ip_addr_isbroadcast(&iphdr->dst,netif)) break; + } + } + + if(!netif) { + UIP_ERROR("uip_ipinput: no route found.\n"); + uip_pbuf_free(p); + return 0; + } + + if((UIP_IPH_OFFSET(iphdr)&htons(UIP_IP_OFFMASK|UIP_IP_MF))!=0) { +#if UIP_IP_REASSEMBLY + p = uip_ipreass(p); + if(p==NULL) return UIP_ERR_OK; + + iphdr = (struct uip_ip_hdr*)p->payload; +#else + uip_pbuf_free(p); + UIP_STAT(++uip_stat.ip.drop); + UIP_ERROR("ip: fragment dropped.\n"); + return 0; +#endif + } + + switch(UIP_IPH_PROTO(iphdr)) { + case UIP_PROTO_TCP: + uip_tcpinput(p,inp); + break; + case UIP_PROTO_ICMP: + uip_icmpinput(p,inp); + break; + default: + UIP_LOG("uip_ipinput: Unsupported protocol.\n"); + if(!ip_addr_isbroadcast(&(iphdr->dst),inp) + && !ip_addr_ismulticast(&(iphdr->dst))) { + p->payload = iphdr; + uip_icmp_destunreach(p,UIP_ICMP_DUR_PROTO); + } + uip_pbuf_free(p); + break; + } + return 0; +} + +s8_t uip_ipoutput_if(struct uip_pbuf *p,struct uip_ip_addr *src,struct uip_ip_addr *dst,u8_t ttl,u8_t tos,u8_t proto,struct uip_netif *netif) +{ + struct uip_ip_hdr *iphdr = NULL; + u16_t ip_id = 0; + + if(dst!=NULL) { + if(uip_pbuf_header(p,UIP_IP_HLEN)) { + UIP_ERROR("uip_ipoutput_if: not enough room for IP header in pbuf.\n"); + return UIP_ERR_BUF; + } + + iphdr = p->payload; + + UIP_IPH_TTL_SET(iphdr,ttl); + UIP_IPH_PROTO_SET(iphdr,proto); + + ip_addr_set(&iphdr->dst,dst); + + UIP_IPH_VHLTOS_SET(iphdr,4,(UIP_IP_HLEN/4),tos); + UIP_IPH_LEN_SET(iphdr,htons(p->tot_len)); + UIP_IPH_OFFSET_SET(iphdr,htons(UIP_IP_DF)); + UIP_IPH_ID_SET(iphdr,htons(ip_id)); + ++ip_id; + + if(ip_addr_isany(src)) ip_addr_set(&iphdr->src,&netif->ip_addr); + else ip_addr_set(&iphdr->src,src); + + UIP_IPH_CHKSUM_SET(iphdr,0); + UIP_IPH_CHKSUM_SET(iphdr,uip_ipchksum(iphdr,UIP_IP_HLEN)); + } else { + iphdr = p->payload; + dst = &iphdr->dst; + } +#if UIP_IP_FRAG + if(netif->mtu && p->tot_len>netif->mtu) + return uip_ipfrag(p,netif,dst); +#endif + return netif->output(netif,p,dst); +} + +s8_t uip_ipoutput(struct uip_pbuf *p,struct uip_ip_addr *src,struct uip_ip_addr *dst,u8_t ttl,u8_t tos,u8_t proto) +{ + struct uip_netif *netif; + + if((netif=uip_iproute(dst))==NULL) { + UIP_ERROR("uip_ipoutput: No route found.\n"); + return UIP_ERR_RTE; + } + return uip_ipoutput_if(p,src,dst,ttl,tos,proto,netif); +} + +void uip_ipinit() +{ + +} + +s32_t uip_ipaton(const u8_t *cp,struct in_addr *addr) +{ + u32_t val; + u8_t c; + u32_t parts[4]; + u32_t *pp = parts; + int base,n; + + c = *cp; + for(;;) { + if(!isdigit(c)) return 0; + + val = 0; base = 10; + if(c=='0') { + c = *++cp; + if(c=='x' || c=='X') + base = 16, c = *++cp; + else + base = 8; + } + for(;;) { + if(isdigit(c)) { + val = (val*base)+(int)(c-'0'); + c = *++cp; + } else if(base==16 && isxdigit(c)) { + val = (val<<4)|(int)(c+10-(islower(c)?'a':'A')); + c = *++cp; + } else + break; + } + if(c=='.') { + if(pp>=parts+3) return 0; + *pp++ = val; + c = *++cp; + } else + break; + } + + if(c!='\0' && (!isascii(c) || isspace(c))) return 0; + + n = pp-parts+1; + switch(n) { + case 0: + return 0; + case 1: + break; + case 2: + if(val>0x00ffffff) return 0; + + val |= (parts[0]<<24); + break; + case 3: + if(val>0x0000ffff) return 0; + + val |= (parts[0]<<24)|(parts[1]<<16); + break; + case 4: + if(val>0x000000ff) return 0; + + val |= (parts[0]<<24)|(parts[1]<<16)|(parts[2]<<8); + break; + } + if(addr) + addr->s_addr = htonl(val); + + return 1; +} + +u32_t uip_ipaddr(const u8_t *cp) +{ + struct in_addr val; + + if(uip_ipaton(cp,&val)) return (val.s_addr); + + return (UIP_INADDR_NONE); +} diff --git a/wii/libogc/libdb/uIP/uip_ip.h b/wii/libogc/libdb/uIP/uip_ip.h new file mode 100644 index 0000000000..366829efee --- /dev/null +++ b/wii/libogc/libdb/uIP/uip_ip.h @@ -0,0 +1,135 @@ +#ifndef __UIP_IP_H__ +#define __UIP_IP_H__ + +#include "uip.h" + +#define UIP_INADDR_NONE ((u32_t) 0xffffffff) /* 255.255.255.255 */ +#define UIP_INADDR_LOOPBACK ((u32_t) 0x7f000001) /* 127.0.0.1 */ + +#define UIP_IPH_V(hdr) (ntohs((hdr)->_v_hl_tos) >> 12) +#define UIP_IPH_HL(hdr) ((ntohs((hdr)->_v_hl_tos) >> 8) & 0x0f) +#define UIP_IPH_TOS(hdr) (ntohs((hdr)->_v_hl_tos) & 0xff) +#define UIP_IPH_LEN(hdr) ((hdr)->_len) +#define UIP_IPH_ID(hdr) ((hdr)->_id) +#define UIP_IPH_OFFSET(hdr) ((hdr)->_offset) +#define UIP_IPH_TTL(hdr) (ntohs((hdr)->_ttl_proto) >> 8) +#define UIP_IPH_PROTO(hdr) (ntohs((hdr)->_ttl_proto) & 0xff) +#define UIP_IPH_CHKSUM(hdr) ((hdr)->_chksum) + +#define UIP_IPH_VHLTOS_SET(hdr, v, hl, tos) (hdr)->_v_hl_tos = (htons(((v) << 12) | ((hl) << 8) | (tos))) +#define UIP_IPH_LEN_SET(hdr, len) (hdr)->_len = (len) +#define UIP_IPH_ID_SET(hdr, id) (hdr)->_id = (id) +#define UIP_IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off) +#define UIP_IPH_TTL_SET(hdr, ttl) (hdr)->_ttl_proto = (htons(UIP_IPH_PROTO(hdr) | ((ttl) << 8))) +#define UIP_IPH_PROTO_SET(hdr, proto) (hdr)->_ttl_proto = (htons((proto) | (UIP_IPH_TTL(hdr) << 8))) +#define UIP_IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum) + +/* + * Option flags per-socket. These are the same like SO_XXX. + */ +#define UIP_SOF_DEBUG (u16_t)0x0001U /* turn on debugging info recording */ +#define UIP_SOF_ACCEPTCONN (u16_t)0x0002U /* socket has had listen() */ +#define UIP_SOF_REUSEADDR (u16_t)0x0004U /* allow local address reuse */ +#define UIP_SOF_KEEPALIVE (u16_t)0x0008U /* keep connections alive */ +#define UIP_SOF_DONTROUTE (u16_t)0x0010U /* just use interface addresses */ +#define UIP_SOF_BROADCAST (u16_t)0x0020U /* permit sending of broadcast msgs */ +#define UIP_SOF_USELOOPBACK (u16_t)0x0040U /* bypass hardware when possible */ +#define UIP_SOF_LINGER (u16_t)0x0080U /* linger on close if data present */ +#define UIP_SOF_OOBINLINE (u16_t)0x0100U /* leave received OOB data in line */ +#define UIP_SOF_REUSEPORT (u16_t)0x0200U /* allow local address & port reuse */ + +#define IP4_ADDR(ipaddr, a,b,c,d) (ipaddr)->addr = htonl(((u32_t)(a & 0xff) << 24) | ((u32_t)(b & 0xff) << 16) | \ + ((u32_t)(c & 0xff) << 8) | (u32_t)(d & 0xff)) + +#define ip_addr_set(dest, src) (dest)->addr = \ + ((src) == NULL? 0:\ + (src)->addr) + +/** + * Determine if two address are on the same network. + * + * @arg addr1 IP address 1 + * @arg addr2 IP address 2 + * @arg mask network identifier mask + * @return !0 if the network identifiers of both address match + */ +#define ip_addr_netcmp(addr1, addr2, mask) (((addr1)->addr & \ + (mask)->addr) == \ + ((addr2)->addr & \ + (mask)->addr)) +#define ip_addr_cmp(addr1, addr2) ((addr1)->addr == (addr2)->addr) + +#define ip_addr_isany(addr1) ((addr1) == NULL || (addr1)->addr == 0) + +#define ip_addr_isbroadcast uip_ipaddr_isbroadcast + +#define ip_addr_ismulticast(addr1) (((addr1)->addr & ntohl(0xf0000000)) == ntohl(0xe0000000)) + +#define ip4_addr1(ipaddr) ((u16_t)(ntohl((ipaddr)->addr) >> 24) & 0xff) +#define ip4_addr2(ipaddr) ((u16_t)(ntohl((ipaddr)->addr) >> 16) & 0xff) +#define ip4_addr3(ipaddr) ((u16_t)(ntohl((ipaddr)->addr) >> 8) & 0xff) +#define ip4_addr4(ipaddr) ((u16_t)(ntohl((ipaddr)->addr)) & 0xff) + +#ifndef HAVE_IN_ADDR +#define HAVE_IN_ADDR +struct in_addr { + u32 s_addr; +}; +#endif + +/* The IP Address */ +PACK_STRUCT_BEGIN +struct uip_ip_addr { + PACK_STRUCT_FIELD(u32_t addr); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +PACK_STRUCT_BEGIN +struct uip_ip_addr2 { + PACK_STRUCT_FIELD(u16_t addrw[2]); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +/* The IP Header */ +PACK_STRUCT_BEGIN +struct uip_ip_hdr { +#define UIP_IP_RF 0x8000 +#define UIP_IP_DF 0x4000 +#define UIP_IP_MF 0x2000 +#define UIP_IP_OFFMASK 0x1fff + PACK_STRUCT_FIELD(u16_t _v_hl_tos); + PACK_STRUCT_FIELD(u16_t _len); + PACK_STRUCT_FIELD(u16_t _id); + PACK_STRUCT_FIELD(u16_t _offset); + PACK_STRUCT_FIELD(u16_t _ttl_proto); + PACK_STRUCT_FIELD(u16_t _chksum); + + PACK_STRUCT_FIELD(struct uip_ip_addr src); + PACK_STRUCT_FIELD(struct uip_ip_addr dst); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +#define UIP_IP_PCB \ + struct uip_ip_addr local_ip; \ + struct uip_ip_addr remote_ip; \ + u16_t so_options; \ + u8_t tos; \ + u8_t ttl + +struct uip_pbuf; +struct uip_netif; +struct ip_addr; + + +void uip_ipinit(); + +u32_t uip_ipaddr(const u8_t *cp); +s32_t uip_ipaton(const u8_t *cp,struct in_addr *addr); +s8_t uip_ipinput(struct uip_pbuf *p,struct uip_netif *inp); +s8_t uip_ipoutput(struct uip_pbuf *p,struct uip_ip_addr *src,struct uip_ip_addr *dst,u8_t ttl,u8_t tos,u8_t proto); +s8_t uip_ipoutput_if(struct uip_pbuf *p,struct uip_ip_addr *src,struct uip_ip_addr *dst,u8_t ttl,u8_t tos,u8_t proto,struct uip_netif *netif); +struct uip_netif* uip_iproute(struct uip_ip_addr *dst); +u8_t uip_ipaddr_isbroadcast(struct uip_ip_addr *addr,struct uip_netif *netif); + + +#endif diff --git a/wii/libogc/libdb/uIP/uip_netif.c b/wii/libogc/libdb/uIP/uip_netif.c new file mode 100644 index 0000000000..5c7e1b77ff --- /dev/null +++ b/wii/libogc/libdb/uIP/uip_netif.c @@ -0,0 +1,133 @@ +#include +#include + +#include "uip_ip.h" +#include "uip_tcp.h" +#include "uip_pbuf.h" +#include "uip_netif.h" + +#if UIP_LOGGING == 1 +#include +#define UIP_LOG(m) uip_log(__FILE__,__LINE__,m) +#else +#define UIP_LOG(m) +#endif /* UIP_LOGGING == 1 */ + +#if UIP_ERRORING == 1 +#include +#define UIP_ERROR(m) uip_log(__FILE__,__LINE__,m) +#else +#define UIP_ERROR(m) +#endif /* UIP_ERRORING == 1 */ + +#if UIP_STATISTICS == 1 +struct uip_stats uip_stat; +#define UIP_STAT(s) s +#else +#define UIP_STAT(s) +#endif /* UIP_STATISTICS == 1 */ + + +struct uip_netif *uip_netif_list; +struct uip_netif *uip_netif_default; + +void uip_netif_init() +{ + uip_netif_list = uip_netif_default = NULL; +} + +void uip_netif_setup(struct uip_netif *netif) +{ + netif->flags |= UIP_NETIF_FLAG_UP; +} + +u8_t uip_netif_isup(struct uip_netif *netif) +{ + return (netif->flags&UIP_NETIF_FLAG_UP); +} + +struct uip_netif* uip_netif_add(struct uip_netif *netif,struct uip_ip_addr *ipaddr,struct uip_ip_addr *netmask,struct uip_ip_addr *gw,void *state,s8_t (*init)(struct uip_netif *netif),s8_t (*input)(struct uip_pbuf *p,struct uip_netif *netif)) +{ + static int netif_num = 0; + + netif->state = state; + netif->num = netif_num++; + netif->input = input; + + uip_netif_setaddr(netif,ipaddr,netmask,gw); + + if(init(netif)!=0) return NULL; + + UIP_LOG("uip_netif_add: netif is up.\n"); + + netif->next = uip_netif_list; + uip_netif_list = netif; + + return netif; +} + +void uip_netif_setaddr(struct uip_netif *netif,struct uip_ip_addr *ipaddr,struct uip_ip_addr *netmask,struct uip_ip_addr *gw) +{ + uip_netif_setipaddr(netif,ipaddr); + uip_netif_setnetmask(netif,netmask); + uip_netif_setgw(netif,gw); +} + +void uip_netif_setipaddr(struct uip_netif *netif,struct uip_ip_addr *ipaddr) +{ +#if UIP_TCP + struct uip_tcp_pcb *pcb; + struct uip_tcp_pcb_listen *lpcb; + + if((ip_addr_cmp(ipaddr,&netif->ip_addr))==0) { + pcb = uip_tcp_active_pcbs; + while(pcb!=NULL) { + if(ip_addr_cmp(&pcb->local_ip,&netif->ip_addr)) { + struct uip_tcp_pcb *next = pcb->next; + pcb = next; + } else { + pcb = pcb->next; + } + } + for(lpcb=uip_tcp_listen_pcbs.listen_pcbs;lpcb!=NULL;lpcb=lpcb->next) { + if(ip_addr_cmp(&lpcb->local_ip,&netif->ip_addr)) { + ip_addr_set(&lpcb->local_ip,ipaddr); + } + } + } +#endif + ip_addr_set(&netif->ip_addr,ipaddr); +} + +void uip_netif_setnetmask(struct uip_netif *netif,struct uip_ip_addr *netmask) +{ + ip_addr_set(&netif->netmask,netmask); +} + +void uip_netif_setgw(struct uip_netif *netif,struct uip_ip_addr *gw) +{ + ip_addr_set(&netif->gw,gw); +} + +void uip_netif_setdefault(struct uip_netif *netif) +{ + uip_netif_default = netif; +} + +struct uip_netif* uip_netif_find(const char *name) +{ + u8_t num; + struct uip_netif *netif; + + if(name==NULL) return NULL; + + num = name[2] - '0'; + + for(netif=uip_netif_list;netif!=NULL;netif=netif->next) { + if(netif->num==num && + netif->name[0]==name[0] && + netif->name[1]==name[1]) return netif; + } + + return NULL; +} diff --git a/wii/libogc/libdb/uIP/uip_netif.h b/wii/libogc/libdb/uIP/uip_netif.h new file mode 100644 index 0000000000..364d825c88 --- /dev/null +++ b/wii/libogc/libdb/uIP/uip_netif.h @@ -0,0 +1,65 @@ +#ifndef __UIP_NETIF_H__ +#define __UIP_NETIF_H__ + +#include "uip.h" + +#define UIP_NETIF_MAX_HWADDR_LEN 6U + +/** TODO: define the use (where, when, whom) of netif flags */ + +/** whether the network interface is 'up'. this is + * a software flag used to control whether this network + * interface is enabled and processes traffic. + */ +#define UIP_NETIF_FLAG_UP 0x1U +/** if set, the netif has broadcast capability */ +#define UIP_NETIF_FLAG_BROADCAST 0x2U +/** if set, the netif is one end of a point-to-point connection */ +#define UIP_NETIF_FLAG_POINTTOPOINT 0x4U +/** if set, the interface is configured using DHCP */ +#define UIP_NETIF_FLAG_DHCP 0x08U +/** if set, the interface has an active link + * (set by the network interface driver) */ +#define UIP_NETIF_FLAG_LINK_UP 0x10U + +struct uip_netif; +struct uip_pbuf; +struct uip_ip_addr; + +struct uip_netif { + struct uip_netif *next; + + struct uip_ip_addr ip_addr; + struct uip_ip_addr netmask; + struct uip_ip_addr gw; + + s8_t (*input)(struct uip_pbuf *p,struct uip_netif *inp); + s8_t (*output)(struct uip_netif *netif,struct uip_pbuf *p,struct uip_ip_addr *ipaddr); + s8_t (*linkoutput)(struct uip_netif *netif,struct uip_pbuf *p); + + void *state; + + u8_t hwaddr_len; + u8_t hwaddr[UIP_NETIF_MAX_HWADDR_LEN]; + + u16_t mtu; + u8_t flags; + + s8_t name[2]; + u8_t num; +}; + +extern struct uip_netif *uip_netif_list; +extern struct uip_netif *uip_netif_default; + +void uip_netif_init(); +void uip_netif_setup(struct uip_netif *netif); +void uip_netif_setaddr(struct uip_netif *netif,struct uip_ip_addr *ipaddr,struct uip_ip_addr *netmask,struct uip_ip_addr *gw); +void uip_netif_setipaddr(struct uip_netif *netif,struct uip_ip_addr *ipaddr); +void uip_netif_setnetmask(struct uip_netif *netif,struct uip_ip_addr *netmask); +void uip_netif_setgw(struct uip_netif *netif,struct uip_ip_addr *gw); +void uip_netif_setdefault(struct uip_netif *netif); +u8_t uip_netif_isup(struct uip_netif *netif); +struct uip_netif* uip_netif_add(struct uip_netif *netif,struct uip_ip_addr *ipaddr,struct uip_ip_addr *netmask,struct uip_ip_addr *gw,void *state,s8_t (*init)(struct uip_netif *netif),s8_t (*input)(struct uip_pbuf *p,struct uip_netif *netif)); + +#endif diff --git a/wii/libogc/libdb/uIP/uip_pbuf.c b/wii/libogc/libdb/uIP/uip_pbuf.c new file mode 100644 index 0000000000..bb7612488a --- /dev/null +++ b/wii/libogc/libdb/uIP/uip_pbuf.c @@ -0,0 +1,287 @@ +#include +#include + +#include "memb.h" +#include "memr.h" +#include "uip_pbuf.h" + +#if UIP_LOGGING == 1 +#include +#define UIP_LOG(m) uip_log(__FILE__,__LINE__,m) +#else +#define UIP_LOG(m) +#endif /* UIP_LOGGING == 1 */ + +#if UIP_ERRORING == 1 +#include +#define UIP_ERROR(m) uip_log(__FILE__,__LINE__,m) +#else +#define UIP_ERROR(m) +#endif /* UIP_ERRORING == 1 */ + +#if UIP_STATISTICS == 1 +struct uip_stats uip_stat; +#define UIP_STAT(s) s +#else +#define UIP_STAT(s) +#endif /* UIP_STATISTICS == 1 */ + +MEMB(uip_pool_pbufs,sizeof(struct uip_pbuf)+UIP_PBUF_POOL_BUFSIZE,UIP_PBUF_POOL_NUM); +MEMB(uip_rom_pbufs,sizeof(struct uip_pbuf),UIP_PBUF_ROM_NUM); + +void uip_pbuf_init() +{ + memb_init(&uip_pool_pbufs); + memb_init(&uip_rom_pbufs); +} + +struct uip_pbuf* uip_pbuf_alloc(uip_pbuf_layer layer,u16_t len,uip_pbuf_flag flag) +{ + u16_t offset; + s32_t rem_len; + struct uip_pbuf *p,*q,*r; + + offset = 0; + switch(layer) { + case UIP_PBUF_TRANSPORT: + offset += UIP_TRANSPORT_HLEN; + case UIP_PBUF_IP: + offset += UIP_IP_HLEN; + case UIP_PBUF_LINK: + offset += UIP_LL_HLEN; + break; + case UIP_PBUF_RAW: + break; + default: + UIP_ERROR("uip_pbuf_alloc: bad pbuf layer.\n"); + return NULL; + } + + switch(flag) { + case UIP_PBUF_POOL: + p = memb_alloc(&uip_pool_pbufs); + if(p==NULL) { + UIP_ERROR("uip_pbuf_alloc: couldn't allocate pbuf(p) from pool\n"); + return NULL; + } + + p->next = NULL; + p->payload = MEM_ALIGN((void*)((u8_t*)p+(sizeof(struct uip_pbuf)+offset))); + p->tot_len = len; + p->len = (len>(UIP_PBUF_POOL_BUFSIZE-offset)?(UIP_PBUF_POOL_BUFSIZE-offset):len); + p->flags = UIP_PBUF_FLAG_POOL; + p->ref = 1; + + r = p; + rem_len = len - p->len; + while(rem_len>0) { + q = memb_alloc(&uip_pool_pbufs); + if(q==NULL) { + UIP_ERROR("uip_pbuf_alloc: couldn't allocate pbuf(q) from pool\n"); + uip_pbuf_free(p); + return NULL; + } + + q->next = NULL; + r->next = q; + q->tot_len = rem_len; + q->len = (rem_len>UIP_PBUF_POOL_BUFSIZE?UIP_PBUF_POOL_BUFSIZE:rem_len); + q->payload = (void*)((u8_t*)q+sizeof(struct uip_pbuf)); + q->flags = UIP_PBUF_FLAG_POOL; + q->ref = 1; + + rem_len -= q->len; + r = q; + } + break; + case UIP_PBUF_RAM: + p = memr_malloc(MEM_ALIGN_SIZE(sizeof(struct uip_pbuf)+offset)+MEM_ALIGN_SIZE(len)); + if(p==NULL) { + UIP_ERROR("uip_pbuf_alloc: couldn't allocate pbuf from ram\n"); + return NULL; + } + p->payload = MEM_ALIGN((u8_t*)p+sizeof(struct uip_pbuf)+offset); + p->len = p->tot_len = len; + p->next = NULL; + p->flags = UIP_PBUF_FLAG_RAM; + break; + case UIP_PBUF_ROM: + case UIP_PBUF_REF: + p = memb_alloc(&uip_rom_pbufs); + if(p==NULL) { + UIP_ERROR("uip_pbuf_alloc: couldn't allocate pbuf from rom/ref\n"); + return NULL; + } + p->payload = NULL; + p->next = NULL; + p->len = p->tot_len = len; + p->flags = (flag==UIP_PBUF_ROM?UIP_PBUF_FLAG_ROM:UIP_PBUF_FLAG_REF); + break; + default: + UIP_ERROR("uip_pbuf_alloc: bad flag value.\n"); + return NULL; + } + + p->ref = 1; + return p; +} + +u8_t uip_pbuf_free(struct uip_pbuf *p) +{ + u8_t cnt; + struct uip_pbuf *q; + + if(p==NULL) return 0; + + cnt = 0; + while(p!=NULL) { + p->ref--; + if(p->ref==0) { + q = p->next; + if(p->flags==UIP_PBUF_FLAG_POOL) { + memb_free(&uip_pool_pbufs,p); + } else if(p->flags==UIP_PBUF_FLAG_ROM || p->flags==UIP_PBUF_FLAG_REF) { + memb_free(&uip_rom_pbufs,p); + } else { + memr_free(p); + } + cnt++; + p = q; + } else + p = NULL; + } + return cnt; +} + +void uip_pbuf_realloc(struct uip_pbuf *p,u16_t new_len) +{ + u16_t rem_len; + s16_t grow; + struct uip_pbuf *q; + + if(new_len>=p->tot_len) return; + + grow = new_len - p->tot_len; + rem_len = new_len; + q = p; + while(rem_len>q->len) { + rem_len -= q->len; + q->tot_len += grow; + q = q->next; + } + + if(q->flags==UIP_PBUF_FLAG_RAM && rem_len!=q->len) + memr_realloc(q,(u8_t*)q->payload-(u8_t*)q+rem_len); + + q->len = rem_len; + q->tot_len = q->len; + + if(q->next!=NULL) uip_pbuf_free(q->next); + q->next = NULL; +} + +u8_t uip_pbuf_header(struct uip_pbuf *p,s16_t hdr_size_inc) +{ + void *payload; + + if(hdr_size_inc==0 || p==NULL) return 0; + + + payload = p->payload; + if(p->flags==UIP_PBUF_FLAG_POOL || p->flags==UIP_PBUF_FLAG_RAM) { + p->payload = (u8_t*)p->payload-hdr_size_inc; + if((u8_t*)p->payload<(u8_t*)p+sizeof(struct uip_pbuf)) { + p->payload = payload; + return 1; + } + } else if(p->flags==UIP_PBUF_FLAG_ROM || p->flags==UIP_PBUF_FLAG_REF) { + if(hdr_size_inc<0 && hdr_size_inc-p->len<=0) p->payload = (u8_t*)p->payload-hdr_size_inc; + else return 1; + } + p->tot_len += hdr_size_inc; + p->len += hdr_size_inc; + + return 0; +} + +u8_t uip_pbuf_clen(struct uip_pbuf *p) +{ + u8_t len; + + len = 0; + while(p!=NULL) { + len++; + p = p->next; + } + return len; +} + +void uip_pbuf_ref(struct uip_pbuf *p) +{ + if(p!=NULL) { + ++(p->ref); + } +} + +void uip_pbuf_cat(struct uip_pbuf *h,struct uip_pbuf *t) +{ + struct uip_pbuf *p; + + if(h==NULL || t==NULL) return; + + for(p=h;p->next!=NULL;p=p->next) { + p->tot_len += t->tot_len; + } + p->tot_len += t->tot_len; + p->next = t; +} + +void uip_pbuf_queue(struct uip_pbuf *p,struct uip_pbuf *n) +{ + if(p==NULL || n==NULL || p==n) return; + + while(p->next!=NULL) p = p->next; + + p->next = n; + uip_pbuf_ref(n); +} + +struct uip_pbuf* uip_pbuf_dequeue(struct uip_pbuf *p) +{ + struct uip_pbuf *q; + u8_t tail_gone = 1; + + if(p==NULL) return NULL; + + while(p->tot_len!=p->len) p = p->next; + + q = p->next; + if(q!=NULL) { + p->next = NULL; + tail_gone = uip_pbuf_free(q); + } + return (tail_gone>0?NULL:q); +} + +void uip_pbuf_chain(struct uip_pbuf *h,struct uip_pbuf *t) +{ + uip_pbuf_cat(h,t); + uip_pbuf_ref(t); +} + +struct uip_pbuf* uip_pbuf_dechain(struct uip_pbuf *p) +{ + struct uip_pbuf *q; + u8_t tail_gone = 1; + + q = p->next; + if(q!=NULL) { + q->tot_len = p->tot_len - p->len; + p->next = NULL; + p->tot_len = p->len; + + tail_gone = uip_pbuf_free(q); + } + + return (tail_gone>0?NULL:q); +} diff --git a/wii/libogc/libdb/uIP/uip_pbuf.h b/wii/libogc/libdb/uIP/uip_pbuf.h new file mode 100644 index 0000000000..c3e55af3cf --- /dev/null +++ b/wii/libogc/libdb/uIP/uip_pbuf.h @@ -0,0 +1,49 @@ +#ifndef __UIP_PBUF_H__ +#define __UIP_PBUF_H__ + +#include "uip.h" + +/* Definitions for the pbuf flag field. These are NOT the flags that + * are passed to pbuf_alloc(). */ +#define UIP_PBUF_FLAG_RAM 0x00U /* Flags that pbuf data is stored in RAM */ +#define UIP_PBUF_FLAG_ROM 0x01U /* Flags that pbuf data is stored in ROM */ +#define UIP_PBUF_FLAG_POOL 0x02U /* Flags that the pbuf comes from the pbuf pool */ +#define UIP_PBUF_FLAG_REF 0x04U /* Flags thet the pbuf payload refers to RAM */ + +typedef enum { + UIP_PBUF_TRANSPORT, + UIP_PBUF_IP, + UIP_PBUF_LINK, + UIP_PBUF_RAW +} uip_pbuf_layer; + +typedef enum { + UIP_PBUF_POOL, + UIP_PBUF_RAM, + UIP_PBUF_ROM, + UIP_PBUF_REF +} uip_pbuf_flag; + +struct uip_pbuf { + struct uip_pbuf *next; + void *payload; + u16_t tot_len; + u16_t len; + u16_t flags; + u16_t ref; +}; + +void uip_pbuf_init(); +struct uip_pbuf* uip_pbuf_alloc(uip_pbuf_layer layer,u16_t len,uip_pbuf_flag flag); +u8_t uip_pbuf_free(struct uip_pbuf *p); +void uip_pbuf_realloc(struct uip_pbuf *p,u16_t new_len); +u8_t uip_pbuf_header(struct uip_pbuf *p,s16_t hdr_size_inc); +void uip_pbuf_cat(struct uip_pbuf *h,struct uip_pbuf *t); +u8_t uip_pbuf_clen(struct uip_pbuf *p); +void uip_pbuf_queue(struct uip_pbuf *p,struct uip_pbuf *n); +void uip_pbuf_ref(struct uip_pbuf *p); +void uip_pbuf_chain(struct uip_pbuf *h,struct uip_pbuf *t); +struct uip_pbuf* uip_pbuf_dequeue(struct uip_pbuf *p); +struct uip_pbuf* uip_pbuf_dechain(struct uip_pbuf *p); + +#endif diff --git a/wii/libogc/libdb/uIP/uip_tcp.c b/wii/libogc/libdb/uIP/uip_tcp.c new file mode 100644 index 0000000000..004e2af978 --- /dev/null +++ b/wii/libogc/libdb/uIP/uip_tcp.c @@ -0,0 +1,1478 @@ +#include +#include + +#include "memb.h" +#include "uip.h" +#include "uip_arch.h" +#include "uip_ip.h" +#include "uip_tcp.h" +#include "uip_pbuf.h" +#include "uip_netif.h" + +#if UIP_LOGGING == 1 +#include +#define UIP_LOG(m) uip_log(__FILE__,__LINE__,m) +#else +#define UIP_LOG(m) +#endif /* UIP_LOGGING == 1 */ + +#if UIP_ERRORING == 1 +#include +#define UIP_ERROR(m) uip_log(__FILE__,__LINE__,m) +#else +#define UIP_ERROR(m) +#endif /* UIP_ERRORING == 1 */ + +#if UIP_STATISTICS == 1 +struct uip_stats uip_stat; +#define UIP_STAT(s) s +#else +#define UIP_STAT(s) +#endif /* UIP_STATISTICS == 1 */ + +static u8_t uip_tcp_timer; +static u8_t uip_flags,uip_recv_flags; +static u16_t uip_tcplen; +static u32_t uip_seqno,uip_ackno; + +static struct uip_tcpseg uip_inseg; + +static struct uip_pbuf *uip_recv_data = NULL; +static struct uip_ip_hdr *uip_iphdr = NULL; +static struct uip_tcp_hdr *uip_tcphdr = NULL; + +u32_t uip_tcp_ticks; +struct uip_tcp_pcb *uip_tcp_tmp_pcb = NULL; +struct uip_tcp_pcb *uip_tcp_input_pcb = NULL; +struct uip_tcp_pcb *uip_tcp_active_pcbs = NULL; +struct uip_tcp_pcb *uip_tcp_tw_pcbs = NULL; + +union uip_tcp_listen_pcbs_t uip_tcp_listen_pcbs; + +const u8_t uip_tcp_backoff[13] = { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; + +MEMB(uip_listen_tcp_pcbs,sizeof(struct uip_tcp_pcb_listen),UIP_LISTEN_TCP_PCBS); +MEMB(uip_tcp_pcbs,sizeof(struct uip_tcp_pcb),UIP_TCP_PCBS); +MEMB(uip_tcp_segs,sizeof(struct uip_tcpseg),UIP_TCP_SEGS); + +static s8_t uip_tcp_nullaccept(void *arg,struct uip_tcp_pcb *pcb,s8_t err); +static s8_t uip_tcp_nullrecv(void *arg,struct uip_tcp_pcb *pcb,struct uip_pbuf *p,s8_t err); + +static void uip_tcp_parseopt(struct uip_tcp_pcb *pcb); +static void uip_tcpoutput_segments(struct uip_tcpseg *seg,struct uip_tcp_pcb *pcb); +static s8_t uip_tcpinput_listen(struct uip_tcp_pcb_listen *pcb); +static s8_t uip_tcpinput_timewait(struct uip_tcp_pcb *pcb); +static s8_t uip_tcpprocess(struct uip_tcp_pcb *pcb); +static void uip_tcpreceive(struct uip_tcp_pcb *pcb); +static u16_t uip_tcp_newport(); + +s8_t uip_tcp_sendctrl(struct uip_tcp_pcb *pcb,u8_t flags) +{ + return uip_tcpenqueue(pcb,NULL,0,flags,1,NULL,0); +} + +s8_t uip_tcp_write(struct uip_tcp_pcb *pcb,const void *arg,u16_t len,u8_t copy) +{ + if(pcb->state==UIP_ESTABLISHED || pcb->state==UIP_CLOSE_WAIT || + pcb->state==UIP_SYN_SENT || pcb->state==UIP_SYN_RCVD) { + if(len>0) { + return uip_tcpenqueue(pcb,(void*)arg,len,0,copy,NULL,0); + } + return UIP_ERR_OK; + } + return UIP_ERR_CONN; +} + +s8_t uip_tcpenqueue(struct uip_tcp_pcb *pcb,void *arg,u16_t len,u8_t flags,u8_t copy,u8_t *optdata,u8_t optlen) +{ + struct uip_pbuf *p; + struct uip_tcpseg *seg,*useg,*queue = NULL; + u32_t left,seqno; + u16_t seglen; + void *ptr; + u8_t queue_len; + + if(len>pcb->snd_buf) { + UIP_ERROR("uip_tcpenqueue: too much data (len>pcb->snd_buf).\n"); + return UIP_ERR_MEM; + } + + left = len; + ptr = arg; + + seqno = pcb->snd_lbb; + queue_len = pcb->snd_queuelen; + + if(queue_len>=UIP_TCP_SND_QUEUELEN) { + UIP_ERROR("uip_tcpenqueue: too long queue."); + goto memerr; + } + useg = seg = queue = NULL; + seglen = 0; + while(queue==NULL || left>0) { + seglen = left>pcb->mss?pcb->mss:len; + seg = memb_alloc(&uip_tcp_segs); + if(seg==NULL) { + UIP_ERROR("uip_tcpenqueue: could not allocate memory for tcp_seg."); + goto memerr; + } + + seg->next = NULL; + seg->p = NULL; + + if(queue==NULL) queue = seg; + else useg->next = seg; + + useg = seg; + + if(optdata!=NULL) { + if((seg->p=uip_pbuf_alloc(UIP_PBUF_TRANSPORT,optlen,UIP_PBUF_RAM))==NULL) { + UIP_ERROR("uip_tcpenqueue: could not allocate memory for pbuf opdata."); + goto memerr; + } + ++queue_len; + seg->dataptr = seg->p->payload; + } else if(copy) { + if((seg->p=uip_pbuf_alloc(UIP_PBUF_TRANSPORT,seglen,UIP_PBUF_RAM))==NULL) { + UIP_ERROR("uip_tcpenqueue: could not allocate memory for pbuf copy size."); + goto memerr; + } + + ++queue_len; + if(ptr!=NULL) UIP_MEMCPY(seg->p->payload,ptr,seglen); + + seg->dataptr = seg->p->payload; + } else { + if((p=uip_pbuf_alloc(UIP_PBUF_TRANSPORT,seglen,UIP_PBUF_ROM))==NULL) { + UIP_ERROR("uip_tcpenqueue: could not allocate memory for zero-copy pbuf."); + goto memerr; + } + + ++queue_len; + p->payload = ptr; + seg->dataptr = ptr; + if((seg->p=uip_pbuf_alloc(UIP_PBUF_TRANSPORT,0,UIP_PBUF_RAM))==NULL) { + UIP_LOG("uip_tcpenqueue: could not allocate memory for header pbuf."); + uip_pbuf_free(p); + goto memerr; + } + + ++queue_len; + uip_pbuf_cat(seg->p,p); + p = NULL; + } + + if(queue_len>UIP_TCP_SND_QUEUELEN) { + UIP_ERROR("uip_tcpenqueue: queue too long."); + goto memerr; + } + + seg->len = seglen; + if(uip_pbuf_header(seg->p,UIP_TCP_HLEN)) { + UIP_ERROR("uip_tcpenqueue: no room for TCP header in pbuf."); + goto memerr; + } + + seg->tcphdr = seg->p->payload; + seg->tcphdr->src = htons(pcb->local_port); + seg->tcphdr->dst = htons(pcb->remote_port); + seg->tcphdr->seqno = htonl(seqno); + seg->tcphdr->urgp = 0; + UIP_TCPH_FLAGS_SET(seg->tcphdr,flags); + if(optdata==NULL) UIP_TCPH_HDRLEN_SET(seg->tcphdr,5); + else { + UIP_TCPH_HDRLEN_SET(seg->tcphdr,(5+(optlen/4))); + UIP_MEMCPY(seg->dataptr,optdata,optlen); + } + left -= seglen; + seqno += seglen; + ptr = (void*)((u8_t*)ptr+seglen); + } + + if(pcb->unsent==NULL) useg = NULL; + else { + for(useg=pcb->unsent;useg->next!=NULL;useg=useg->next); + } + + if(useg!=NULL && + UIP_TCP_TCPLEN(useg)!=0 && + !(UIP_TCPH_FLAGS(useg->tcphdr)&(UIP_TCP_SYN|UIP_TCP_FIN)) && + !(flags&(UIP_TCP_SYN|UIP_TCP_FIN)) && + useg->len+queue->len<=pcb->mss) { + + uip_pbuf_header(queue->p,-UIP_TCP_HLEN); + uip_pbuf_cat(useg->p,queue->p); + + useg->len += queue->len; + useg->next = queue->next; + if(seg==queue) seg = NULL; + + memb_free(&uip_tcp_segs,queue); + } else { + if(useg==NULL) pcb->unsent = queue; + else useg->next = queue; + } + + if(flags&UIP_TCP_SYN || flags&UIP_TCP_FIN) len++; + + pcb->snd_lbb += len; + pcb->snd_buf -= len; + pcb->snd_queuelen = queue_len; + + if(seg!=NULL && seglen>0 && seg->tcphdr!=NULL) UIP_TCPH_SET_FLAG(seg->tcphdr,UIP_TCP_PSH); + + return UIP_ERR_OK; +memerr: + if(queue!=NULL) uip_tcpsegs_free(queue); + return UIP_ERR_MEM; +} + +void uip_tcpinput(struct uip_pbuf *p,struct uip_netif *inp) +{ + s8_t err; + u8_t hdr_len; + struct uip_tcp_pcb *pcb,*prev; + struct uip_tcp_pcb_listen *lpcb; + + uip_iphdr = p->payload; + uip_tcphdr = (struct uip_tcp_hdr*)((u8_t*)p->payload+UIP_IPH_HL(uip_iphdr)*4); + + if(uip_pbuf_header(p,-((s16_t)(UIP_IPH_HL(uip_iphdr)*4))) || p->tot_lendst,inp) || + ip_addr_ismulticast(&uip_iphdr->dst)) { + uip_pbuf_free(p); + return; + } + + if(uip_chksum_pseudo(p,&uip_iphdr->src,&uip_iphdr->dst,UIP_PROTO_TCP,p->tot_len)!=0) { + UIP_LOG("uip_tcpinput: packet discarded due to failing checksum."); + uip_pbuf_free(p); + return; + } + + hdr_len = UIP_TCPH_HDRLEN(uip_tcphdr); + uip_pbuf_header(p,-(hdr_len*4)); + + uip_tcphdr->src = ntohs(uip_tcphdr->src); + uip_tcphdr->dst = ntohs(uip_tcphdr->dst); + uip_seqno = uip_tcphdr->seqno = ntohl(uip_tcphdr->seqno); + uip_ackno = uip_tcphdr->ackno = ntohl(uip_tcphdr->ackno); + uip_tcphdr->wnd = ntohs(uip_tcphdr->wnd); + + uip_flags = UIP_TCPH_FLAGS(uip_tcphdr)&UIP_TCP_FLAGS; + uip_tcplen = p->tot_len+((uip_flags&UIP_TCP_FIN||uip_flags&UIP_TCP_SYN)?1:0); + + prev = NULL; + for(pcb=uip_tcp_active_pcbs;pcb!=NULL;pcb=pcb->next) { + if(pcb->state!=UIP_CLOSED && pcb->state!=UIP_TIME_WAIT && pcb->state!=UIP_LISTEN) { + if(pcb->remote_port==uip_tcphdr->src && + pcb->local_port==uip_tcphdr->dst && + ip_addr_cmp(&pcb->remote_ip,&uip_iphdr->src) && + ip_addr_cmp(&pcb->local_ip,&uip_iphdr->dst)) { + if(prev!=NULL) { + prev->next = pcb->next; + pcb->next = uip_tcp_active_pcbs; + uip_tcp_active_pcbs = pcb; + } + break; + } + prev = pcb; + } + } + + if(pcb==NULL) { + for(pcb=uip_tcp_tw_pcbs;pcb!=NULL;pcb=pcb->next) { + if(pcb->state==UIP_TIME_WAIT && + pcb->remote_port==uip_tcphdr->src && + pcb->local_port==uip_tcphdr->dst && + ip_addr_cmp(&pcb->remote_ip,&uip_iphdr->src) && + ip_addr_cmp(&pcb->local_ip,&uip_iphdr->dst)) { + uip_tcpinput_timewait(pcb); + return; + } + } + + prev = NULL; + for(lpcb=uip_tcp_listen_pcbs.listen_pcbs;lpcb!=NULL;lpcb=lpcb->next) { + if((ip_addr_isany(&lpcb->local_ip) || ip_addr_cmp(&lpcb->local_ip,&uip_iphdr->dst)) && + lpcb->local_port==uip_tcphdr->dst) { + if(prev!=NULL) { + ((struct uip_tcp_pcb_listen*)prev)->next = lpcb->next; + lpcb->next = uip_tcp_listen_pcbs.listen_pcbs; + uip_tcp_listen_pcbs.listen_pcbs = lpcb; + } + uip_tcpinput_listen(lpcb); + return; + } + prev = (struct uip_tcp_pcb*)lpcb; + } + } + + if(pcb!=NULL) { + uip_inseg.next = NULL; + uip_inseg.len = p->tot_len; + uip_inseg.dataptr = p->payload; + uip_inseg.p = p; + uip_inseg.tcphdr = uip_tcphdr; + + uip_recv_data = NULL; + uip_recv_flags = 0; + + uip_tcp_input_pcb = pcb; + err = uip_tcpprocess(pcb); + uip_tcp_input_pcb = NULL; + + if(err!=UIP_ERR_ABRT) { + if(uip_recv_flags&UIP_TF_RESET) { + if(pcb->errf) pcb->errf(pcb->cb_arg,UIP_ERR_RST); + uip_tcp_pcbremove(&uip_tcp_active_pcbs,pcb); + memb_free(&uip_tcp_pcbs,pcb); + } else if(uip_recv_flags&UIP_TF_CLOSED) { + uip_tcp_pcbremove(&uip_tcp_active_pcbs,pcb); + memb_free(&uip_tcp_pcbs,pcb); + } else { + err = UIP_ERR_OK; + + if(pcb->acked>0) { + if(pcb->sent) err = pcb->sent(pcb->cb_arg,pcb,pcb->acked); + } + + if(uip_recv_data!=NULL) { + if(pcb->recv) err = pcb->recv(pcb->cb_arg,pcb,uip_recv_data,UIP_ERR_OK); + } + + if(err==UIP_ERR_OK) uip_tcpoutput(pcb); + } + } + if(uip_inseg.p!=NULL) uip_pbuf_free(uip_inseg.p); + } else { + if(!(UIP_TCPH_FLAGS(uip_tcphdr)&UIP_TCP_RST)) + uip_tcp_rst(uip_ackno,uip_seqno+uip_tcplen,&uip_iphdr->dst,&uip_iphdr->src,uip_tcphdr->dst,uip_tcphdr->src); + + uip_pbuf_free(p); + } +} + +s8_t uip_tcpoutput(struct uip_tcp_pcb *pcb) +{ + u32_t wnd; + struct uip_pbuf *p; + struct uip_tcp_hdr *tcphdr; + struct uip_tcpseg *seg,*useg; + + if(uip_tcp_input_pcb==pcb) return 0; + + wnd = UIP_MIN(pcb->snd_wnd,pcb->cwnd); + seg = pcb->unsent; + useg = pcb->unacked; + if(useg!=NULL) { + for(;useg->next!=NULL;useg=useg->next); + } + + if(pcb->flags&UIP_TF_ACK_NOW && + (seg==NULL || ntohl(seg->tcphdr->seqno)-pcb->lastack+seg->len>wnd)) { + //printf("uip_tcpout: ACK - seqno = %u, ackno = %u\n",pcb->snd_nxt,pcb->rcv_nxt); + p = uip_pbuf_alloc(UIP_PBUF_IP,UIP_TCP_HLEN,UIP_PBUF_RAM); + if(p==NULL) { + UIP_ERROR("uip_tcpoutput: (ACK) could not allocate pbuf."); + return UIP_ERR_BUF; + } + pcb->flags &= ~(UIP_TF_ACK_DELAY|UIP_TF_ACK_NOW); + + tcphdr = p->payload; + tcphdr->src = htons(pcb->local_port); + tcphdr->dst = htons(pcb->remote_port); + tcphdr->seqno = htonl(pcb->snd_nxt); + tcphdr->ackno = htonl(pcb->rcv_nxt); + UIP_TCPH_FLAGS_SET(tcphdr,UIP_TCP_ACK); + tcphdr->wnd = htons(pcb->rcv_wnd); + tcphdr->urgp = 0; + UIP_TCPH_HDRLEN_SET(tcphdr,5); + + tcphdr->chksum = 0; + tcphdr->chksum = uip_chksum_pseudo(p,&pcb->local_ip,&pcb->remote_ip,UIP_PROTO_TCP,p->tot_len); + + uip_ipoutput(p,&pcb->local_ip,&pcb->remote_ip,pcb->ttl,pcb->tos,UIP_PROTO_TCP); + uip_pbuf_free(p); + + return UIP_ERR_OK; + } + + while(seg!=NULL && ntohl(seg->tcphdr->seqno)-pcb->lastack+seg->len<=wnd) { + pcb->unsent = seg->next; + if(pcb->state!=UIP_SYN_SENT) { + UIP_TCPH_SET_FLAG(seg->tcphdr,UIP_TCP_ACK); + pcb->flags &= ~(UIP_TF_ACK_DELAY|UIP_TF_ACK_NOW); + } + + uip_tcpoutput_segments(seg,pcb); + + pcb->snd_nxt = ntohl(seg->tcphdr->seqno)+UIP_TCP_TCPLEN(seg); + if(UIP_TCP_SEQ_LT(pcb->snd_max,pcb->snd_nxt)) pcb->snd_max = pcb->snd_nxt; + + if(UIP_TCP_TCPLEN(seg)>0) { + seg->next = NULL; + if(pcb->unacked==NULL) { + pcb->unacked = seg; + useg = seg; + } else { + if(UIP_TCP_SEQ_LT(ntohl(seg->tcphdr->seqno),ntohl(useg->tcphdr->seqno))) { + seg->next = pcb->unacked; + pcb->unacked = seg; + } else { + useg->next = seg; + useg = useg->next; + } + } + } else + uip_tcpseg_free(seg); + + seg = pcb->unsent; + } + return UIP_ERR_OK; +} + +void uip_tcp_tmr() +{ + uip_tcp_fasttmr(); + + if(++uip_tcp_timer&1) uip_tcp_slowtmr(); +} + +void uip_tcp_init() +{ + memb_init(&uip_tcp_pcbs); + memb_init(&uip_listen_tcp_pcbs); + memb_init(&uip_tcp_segs); + + uip_tcp_listen_pcbs.listen_pcbs = NULL; + uip_tcp_active_pcbs = NULL; + uip_tcp_tw_pcbs = NULL; + + uip_tcp_ticks = 0; + uip_tcp_timer = 0; +} + +void uip_tcp_accept(struct uip_tcp_pcb *pcb,s8_t (*accept)(void *,struct uip_tcp_pcb *,s8_t)) +{ + ((struct uip_tcp_pcb_listen*)pcb)->accept = accept; +} + +void uip_tcp_err(struct uip_tcp_pcb *pcb,void (*errf)(void *,s8_t)) +{ + pcb->errf = errf; +} + +void uip_tcp_recv(struct uip_tcp_pcb *pcb,s8_t (*recv)(void *,struct uip_tcp_pcb *,struct uip_pbuf *,s8_t)) +{ + pcb->recv = recv; +} + +void uip_tcp_sent(struct uip_tcp_pcb *pcb,s8_t (*sent)(void *,struct uip_tcp_pcb *,u16_t)) +{ + pcb->sent = sent; +} + +void uip_tcp_poll(struct uip_tcp_pcb *pcb,s8_t (*poll)(void *,struct uip_tcp_pcb *),u8_t interval) +{ + pcb->poll = poll; + pcb->pollinterval = interval; +} + +void uip_tcp_arg(struct uip_tcp_pcb *pcb,void *arg) +{ + pcb->cb_arg = arg; +} + +struct uip_tcp_pcb* uip_tcp_pcballoc(u8_t prio) +{ + u32_t iss; + struct uip_tcp_pcb *pcb = NULL; + + pcb = memb_alloc(&uip_tcp_pcbs); + if(pcb!=NULL) { + UIP_MEMSET(pcb,0,sizeof(struct uip_tcp_pcb)); + pcb->prio = UIP_TCP_PRIO_NORMAL; + pcb->snd_buf = UIP_TCP_SND_BUF; + pcb->snd_queuelen = 0; + pcb->rcv_wnd = UIP_TCP_WND; + pcb->tos = 0; + pcb->ttl = UIP_TCP_TTL; + pcb->mss = UIP_TCP_MSS; + pcb->rto = 3000/UIP_TCP_SLOW_INTERVAL; + pcb->sa = 0; + pcb->sv = 3000/UIP_TCP_SLOW_INTERVAL; + pcb->rtime = 0; + pcb->cwnd = 1; + iss = uip_tcpiss_next(); + pcb->snd_wl2 = iss; + pcb->snd_nxt = iss; + pcb->snd_max = iss; + pcb->lastack = iss; + pcb->snd_lbb = iss; + pcb->tmr = uip_tcp_ticks; + pcb->polltmr = 0; + + pcb->recv = uip_tcp_nullrecv; + + pcb->keepalive = UIP_TCP_KEEPDEFAULT; + pcb->keepcnt = 0; + } + return pcb; +} + +void uip_tcp_pcbremove(struct uip_tcp_pcb **pcblist,struct uip_tcp_pcb *pcb) +{ + UIP_TCP_RMV(pcblist,pcb); + + uip_tcp_pcbpurge(pcb); + if(pcb->state!=UIP_TIME_WAIT && + pcb->state!=UIP_LISTEN && + pcb->flags&UIP_TF_ACK_DELAY) { + pcb->flags |= UIP_TF_ACK_NOW; + uip_tcpoutput(pcb); + } + pcb->state = UIP_CLOSED; +} + +void uip_tcp_pcbpurge(struct uip_tcp_pcb *pcb) +{ + if(pcb->state!=UIP_CLOSED && + pcb->state!=UIP_TIME_WAIT && + pcb->state!=UIP_LISTEN) { + uip_tcpsegs_free(pcb->ooseq); + uip_tcpsegs_free(pcb->unsent); + uip_tcpsegs_free(pcb->unacked); + pcb->unsent = pcb->unacked = pcb->ooseq = NULL; + } +} + +struct uip_tcp_pcb* uip_tcp_new() +{ + return uip_tcp_pcballoc(UIP_TCP_PRIO_NORMAL); +} + +s8_t uip_tcp_bind(struct uip_tcp_pcb *pcb,struct uip_ip_addr *ipaddr,u16_t port) +{ + struct uip_tcp_pcb *cpcb; + + if(port==0) port = uip_tcp_newport(); + + for(cpcb=(struct uip_tcp_pcb*)uip_tcp_listen_pcbs.pcbs;cpcb!=NULL;cpcb=cpcb->next) { + if(cpcb->local_port==port) { + if(ip_addr_isany(&cpcb->local_ip) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&cpcb->local_ip,ipaddr)) return UIP_ERR_USE; + } + } + + for(cpcb=uip_tcp_active_pcbs;cpcb!=NULL;cpcb=cpcb->next) { + if(cpcb->local_port==port) { + if(ip_addr_isany(&cpcb->local_ip) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&cpcb->local_ip,ipaddr)) return UIP_ERR_USE; + } + } + + if(!ip_addr_isany(ipaddr)) pcb->local_ip = *ipaddr; + pcb->local_port = port; + + return UIP_ERR_OK; +} + +struct uip_tcp_pcb* uip_tcp_listen(struct uip_tcp_pcb *pcb) +{ + struct uip_tcp_pcb_listen *lpcb; + + if(pcb->state==UIP_LISTEN) return pcb; + + lpcb = memb_alloc(&uip_listen_tcp_pcbs); + if(lpcb==NULL) return NULL; + + lpcb->cb_arg = pcb->cb_arg; + lpcb->local_port = pcb->local_port; + lpcb->state = UIP_LISTEN; + lpcb->so_options = pcb->so_options; + lpcb->so_options |= UIP_SOF_ACCEPTCONN; + lpcb->ttl = pcb->ttl; + lpcb->tos = pcb->tos; + ip_addr_set(&lpcb->local_ip,&pcb->local_ip); + + memb_free(&uip_tcp_pcbs,pcb); + + lpcb->accept = uip_tcp_nullaccept; + + UIP_TCP_REG(&uip_tcp_listen_pcbs.listen_pcbs,lpcb); + return (struct uip_tcp_pcb*)lpcb; +} + +void uip_tcp_recved(struct uip_tcp_pcb *pcb,u16_t len) +{ + if((u32_t)pcb->rcv_wnd+len>UIP_TCP_WND) pcb->rcv_wnd = UIP_TCP_WND; + else pcb->rcv_wnd += len; + + if(!(pcb->flags&UIP_TF_ACK_DELAY) && !(pcb->flags&UIP_TF_ACK_NOW)) { + uip_tcp_ack(pcb); + } else if(pcb->flags&UIP_TF_ACK_DELAY && pcb->rcv_wnd>=UIP_TCP_WND/2) { + uip_tcp_acknow(pcb); + } +} + +s8_t uip_tcp_close(struct uip_tcp_pcb *pcb) +{ + s8_t err; + + switch(pcb->state) { + case UIP_CLOSED: + err = UIP_ERR_OK; + memb_free(&uip_tcp_pcbs,pcb); + pcb = NULL; + break; + case UIP_LISTEN: + err = UIP_ERR_OK; + uip_tcp_pcbremove((struct uip_tcp_pcb**)&uip_tcp_listen_pcbs.pcbs,pcb); + memb_free(&uip_listen_tcp_pcbs,pcb); + pcb = NULL; + break; + case UIP_SYN_SENT: + err = UIP_ERR_OK; + uip_tcp_pcbremove(&uip_tcp_active_pcbs,pcb); + memb_free(&uip_tcp_pcbs,pcb); + pcb = NULL; + break; + case UIP_SYN_RCVD: + case UIP_ESTABLISHED: + err = uip_tcp_sendctrl(pcb,UIP_TCP_FIN); + if(err==UIP_ERR_OK) pcb->state = UIP_FIN_WAIT_1; + break; + case UIP_CLOSE_WAIT: + err = uip_tcp_sendctrl(pcb,UIP_TCP_FIN); + if(err==UIP_ERR_OK) pcb->state = UIP_LAST_ACK; + break; + default: + err = UIP_ERR_OK; + pcb = NULL; + break; + } + if(pcb!=NULL && err==UIP_ERR_OK) uip_tcpoutput(pcb); + + return err; +} + +void uip_tcp_rst(u32_t seqno,u32_t ackno,struct uip_ip_addr *lipaddr,struct uip_ip_addr *ripaddr,u16_t lport,u16_t rport) +{ + struct uip_pbuf *p; + struct uip_tcp_hdr *tcphdr; + + p = uip_pbuf_alloc(UIP_PBUF_IP,UIP_TCP_HLEN,UIP_PBUF_RAM); + if(p==NULL) { + UIP_LOG("uip_tcp_rst: could not allocate memory for pbuf.\n"); + return; + } + + tcphdr = p->payload; + tcphdr->src = htons(lport); + tcphdr->dst = htons(rport); + tcphdr->seqno = htonl(seqno); + tcphdr->ackno = htonl(ackno); + UIP_TCPH_FLAGS_SET(tcphdr,UIP_TCP_RST|UIP_TCP_ACK); + tcphdr->wnd = htons(UIP_TCP_WND); + tcphdr->urgp = 0; + UIP_TCPH_HDRLEN_SET(tcphdr,5); + + tcphdr->chksum = 0; + tcphdr->chksum = uip_chksum_pseudo(p,lipaddr,ripaddr,UIP_PROTO_TCP,p->tot_len); + + uip_ipoutput(p,lipaddr,ripaddr,UIP_TCP_TTL,0,UIP_PROTO_TCP); + uip_pbuf_free(p); +} + +void uip_tcp_abort(struct uip_tcp_pcb *pcb) +{ + u32_t seqno,ackno; + u16_t remote_port,local_port; + struct uip_ip_addr remote_ip,local_ip; + void (*errf)(void *arg,s8_t err); + void *errf_arg; + + if(pcb->state==UIP_TIME_WAIT) { + memb_free(&uip_tcp_pcbs,pcb); + } else { + seqno = pcb->snd_nxt; + ackno = pcb->rcv_nxt; + + ip_addr_set(&local_ip,&pcb->local_ip); + ip_addr_set(&remote_ip,&pcb->remote_ip); + local_port = pcb->local_port; + remote_port = pcb->remote_port; + + errf = pcb->errf; + errf_arg = pcb->cb_arg; + + uip_tcp_pcbremove(&uip_tcp_active_pcbs,pcb); + + if(pcb->unacked!=NULL) + uip_tcpsegs_free(pcb->unacked); + if(pcb->unsent!=NULL) + uip_tcpsegs_free(pcb->unsent); + if(pcb->ooseq!=NULL) + uip_tcpsegs_free(pcb->ooseq); + + memb_free(&uip_tcp_pcbs,pcb); + if(errf) errf(errf_arg,UIP_ERR_ABRT); + + uip_tcp_rst(seqno,ackno,&local_ip,&remote_ip,local_port,remote_port); + } +} + +void uip_tcp_keepalive(struct uip_tcp_pcb *pcb) +{ + struct uip_pbuf *p; + struct uip_tcp_hdr *tcphdr; + + p = uip_pbuf_alloc(UIP_PBUF_IP,UIP_TCP_HLEN,UIP_PBUF_RAM); + if(p==NULL) return; + + tcphdr = p->payload; + tcphdr->src = htons(pcb->local_port); + tcphdr->dst = htons(pcb->remote_port); + tcphdr->seqno = htonl(pcb->snd_nxt-1); + tcphdr->ackno = htonl(pcb->rcv_nxt); + tcphdr->wnd = htons(pcb->rcv_wnd); + tcphdr->urgp = 0; + UIP_TCPH_HDRLEN_SET(tcphdr,5); + + tcphdr->chksum = 0; + tcphdr->chksum = uip_chksum_pseudo(p,&pcb->local_ip,&pcb->remote_ip,UIP_PROTO_TCP,p->tot_len); + + uip_ipoutput(p,&pcb->local_ip,&pcb->remote_ip,pcb->ttl,0,UIP_PROTO_TCP); + uip_pbuf_free(p); +} + +void uip_tcp_rexmit(struct uip_tcp_pcb *pcb) +{ + struct uip_tcpseg *seg; + + if(pcb->unacked==NULL) return; + + seg = pcb->unacked->next; + pcb->unacked->next = pcb->unsent; + pcb->unsent = pcb->unacked; + pcb->unacked = seg; + + pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno); + pcb->nrtx++; + pcb->rttest = 0; + + uip_tcpoutput(pcb); +} + +void uip_tcp_rexmit_rto(struct uip_tcp_pcb *pcb) +{ + struct uip_tcpseg *seg; + + if(pcb->unacked==NULL) return; + + for(seg=pcb->unacked;seg->next!=NULL;seg=seg->next); + + seg->next = pcb->unsent; + pcb->unsent = pcb->unacked; + pcb->unacked = NULL; + + pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno); + pcb->nrtx++; + + uip_tcpoutput(pcb); +} + +void uip_tcp_fasttmr() +{ + struct uip_tcp_pcb *pcb; + + for(pcb=uip_tcp_active_pcbs;pcb!=NULL;pcb=pcb->next) { + if(pcb->flags&UIP_TF_ACK_DELAY) { + uip_tcp_acknow(pcb); + pcb->flags &= ~(UIP_TF_ACK_DELAY|UIP_TF_ACK_NOW); + } + } +} + +void uip_tcp_slowtmr() +{ + struct uip_tcp_pcb *prev,*pcb,*pcb2; + u32 eff_wnd; + u8_t pcb_remove; + s8_t err; + + err = UIP_ERR_OK; + + uip_tcp_ticks++; + + prev = NULL; + pcb = uip_tcp_active_pcbs; + while(pcb!=NULL) { + pcb_remove = 0; + + if(pcb->state==UIP_SYN_SENT && pcb->nrtx==UIP_MAXSYNRTX) pcb_remove++; + else if(pcb->nrtx==UIP_MAXRTX) pcb_remove++; + else { + pcb->rtime++; + if(pcb->unacked!=NULL && pcb->rtime>=pcb->rto) { + if(pcb->state==UIP_SYN_SENT) pcb->rto = ((pcb->sa>>3)+pcb->sv)<nrtx]; + + eff_wnd = UIP_MIN(pcb->cwnd,pcb->snd_wnd); + pcb->ssthresh = eff_wnd>>1; + + if(pcb->ssthreshmss) pcb->ssthresh = pcb->mss*2; + pcb->cwnd = pcb->mss; + + uip_tcp_rexmit_rto(pcb); + } + } + + if(pcb->state==UIP_FIN_WAIT_2) { + if((u32_t)(uip_tcp_ticks-pcb->tmr)>UIP_TCP_FIN_WAIT_TIMEOUT/UIP_TCP_SLOW_INTERVAL) pcb_remove++; + } + + if(pcb->so_options&UIP_SOF_KEEPALIVE && + (pcb->state==UIP_ESTABLISHED || pcb->state==UIP_CLOSE_WAIT)) { + if((u32_t)(uip_tcp_ticks-pcb->tmr)>(pcb->keepalive+UIP_TCP_MAXIDLE)/UIP_TCP_SLOW_INTERVAL) uip_tcp_abort(pcb); + else if((u32_t)(uip_tcp_ticks-pcb->tmr)>(pcb->keepalive+pcb->keepcnt*UIP_TCP_KEEPINTVL)/UIP_TCP_SLOW_INTERVAL) { + uip_tcp_keepalive(pcb); + pcb->keepcnt++; + } + } + + if(pcb->ooseq!=NULL && (u32_t)uip_tcp_ticks-pcb->tmr>=pcb->rto*UIP_TCP_OOSEQ_TIMEOUT) { + uip_tcpsegs_free(pcb->ooseq); + pcb->ooseq = NULL; + } + if(pcb->state==UIP_SYN_RCVD) { + if((u32_t)(uip_tcp_ticks-pcb->tmr)>UIP_TCP_SYN_RCVD_TIMEOUT/UIP_TCP_SLOW_INTERVAL) pcb_remove++; + } + + if(pcb_remove) { + uip_tcp_pcbpurge(pcb); + + if(prev!=NULL) prev->next = pcb->next; + else uip_tcp_active_pcbs = pcb->next; + + if(pcb->errf) pcb->errf(pcb->cb_arg,UIP_ERR_ABRT); + + pcb2 = pcb->next; + memb_free(&uip_tcp_pcbs,pcb); + pcb = pcb2; + } else { + pcb->polltmr++; + if(pcb->polltmr>=pcb->pollinterval) { + pcb->polltmr = 0; + if(pcb->poll) err = pcb->poll(pcb->cb_arg,pcb); + + if(err==UIP_ERR_OK) uip_tcpoutput(pcb); + } + + prev = pcb; + pcb = pcb->next; + } + } + + prev = NULL; + pcb = uip_tcp_tw_pcbs; + while(pcb!=NULL) { + pcb_remove = 0; + + if((u32_t)(uip_tcp_ticks-pcb->tmr)>2*UIP_TCP_MSL/UIP_TCP_SLOW_INTERVAL) pcb_remove++; + + if(pcb_remove) { + uip_tcp_pcbpurge(pcb); + + if(prev!=NULL) prev->next = pcb->next; + else uip_tcp_tw_pcbs = pcb->next; + + pcb2 = pcb->next; + memb_free(&uip_tcp_pcbs,pcb); + pcb = pcb2; + } else { + prev = pcb; + pcb = pcb->next; + } + } +} + +u32_t uip_tcpiss_next() +{ + static u32_t iss = 6510; + iss += uip_tcp_ticks; + return iss; +} + +struct uip_tcpseg* uip_tcpseg_copy(struct uip_tcpseg *seg) +{ + struct uip_tcpseg *cseg; + + cseg = memb_alloc(&uip_tcp_segs); + if(cseg==NULL) return NULL; + + UIP_MEMCPY(cseg,seg,sizeof(struct uip_tcpseg)); + uip_pbuf_ref(cseg->p); + + return cseg; +} + +u8_t uip_tcpsegs_free(struct uip_tcpseg *seg) +{ + u8_t cnt = 0; + struct uip_tcpseg *next; + + while(seg!=NULL) { + next = seg->next; + cnt += uip_tcpseg_free(seg); + seg = next; + } + + return cnt; +} + +u8_t uip_tcpseg_free(struct uip_tcpseg *seg) +{ + u8_t cnt = 0; + + if(seg!=NULL) { + if(seg->p!=NULL) { + cnt = uip_pbuf_free(seg->p); + } + memb_free(&uip_tcp_segs,seg); + } + return cnt; +} + +#ifndef TCP_LOCAL_PORT_RANGE_START +#define TCP_LOCAL_PORT_RANGE_START 4096 +#define TCP_LOCAL_PORT_RANGE_END 0x7fff +#endif + +static u16_t uip_tcp_newport() +{ + struct uip_tcp_pcb *pcb; + static u16_t port = TCP_LOCAL_PORT_RANGE_START; + +again: + if(++port>=TCP_LOCAL_PORT_RANGE_END) port = TCP_LOCAL_PORT_RANGE_START; + + for(pcb=uip_tcp_active_pcbs;pcb!=NULL;pcb=pcb->next) { + if(pcb->local_port==port) goto again; + } + for(pcb=uip_tcp_tw_pcbs;pcb!=NULL;pcb=pcb->next) { + if(pcb->local_port==port) goto again; + } + for(pcb=(struct uip_tcp_pcb*)uip_tcp_listen_pcbs.pcbs;pcb!=NULL;pcb=pcb->next) { + if(pcb->local_port==port) goto again; + } + return port; +} + +static void uip_tcp_parseopt(struct uip_tcp_pcb *pcb) +{ + u8_t c; + u8_t *opts,opt; + u16_t mss; + + opts = (u8_t*)uip_tcphdr+UIP_TCP_HLEN; + if(UIP_TCPH_HDRLEN(uip_tcphdr)>0x05) { + for(c=0;c<((UIP_TCPH_HDRLEN(uip_tcphdr)-5)<<2);) { + opt = opts[c]; + if(opt==0x00) break; + else if(opt==0x01) c++; + else if(opt==0x02 && opts[c+1]==0x04) { + mss = (opts[c+2]<<8|opts[c+3]); + pcb->mss = mss>UIP_TCP_MSS?UIP_TCP_MSS:mss; + break; + } else { + if(opts[c+1]==0) break; + c += opts[c+1]; + } + } + } +} + +static void uip_tcpoutput_segments(struct uip_tcpseg *seg,struct uip_tcp_pcb *pcb) +{ + u16_t len; + struct uip_netif *netif; + + seg->tcphdr->ackno = htonl(pcb->rcv_nxt); + if(pcb->rcv_wndmss) seg->tcphdr->wnd = 0; + else seg->tcphdr->wnd = htons(pcb->rcv_wnd); + + if(ip_addr_isany(&pcb->local_ip)) { + netif = uip_iproute(&pcb->remote_ip); + if(netif==NULL) { + UIP_ERROR("uip_tcpoutput_segments: no route found."); + return; + } + ip_addr_set(&pcb->local_ip,&netif->ip_addr); + } + + pcb->rtime = 0; + if(pcb->rttest==0) { + pcb->rttest = uip_tcp_ticks; + pcb->rtseq = ntohl(seg->tcphdr->seqno); + } + + len = (u16_t)((u8_t*)seg->tcphdr-(u8_t*)seg->p->payload); + seg->p->len -= len; + seg->p->tot_len -= len; + seg->p->payload = seg->tcphdr; + + seg->tcphdr->chksum = 0; + seg->tcphdr->chksum = uip_chksum_pseudo(seg->p,&pcb->local_ip,&pcb->remote_ip,UIP_PROTO_TCP,seg->p->tot_len); + + uip_ipoutput(seg->p,&pcb->local_ip,&pcb->remote_ip,pcb->ttl,pcb->tos,UIP_PROTO_TCP); +} + +static s8_t uip_tcpinput_listen(struct uip_tcp_pcb_listen *pcb) +{ + u32_t optdata; + struct uip_tcp_pcb *npcb; + + if(uip_flags&UIP_TCP_ACK) { + UIP_LOG("uip_tcp_listen_input: ACK in LISTEN, sending reset.\n"); + uip_tcp_rst(uip_ackno+1,uip_seqno+uip_tcplen,&uip_iphdr->dst,&uip_iphdr->src,uip_tcphdr->dst,uip_tcphdr->src); + } else if(uip_flags&UIP_TCP_SYN) { + npcb = uip_tcp_pcballoc(pcb->prio); + if(!npcb) return UIP_ERR_MEM; + + ip_addr_set(&npcb->local_ip,&uip_iphdr->dst); + npcb->local_port = pcb->local_port; + ip_addr_set(&npcb->remote_ip,&uip_iphdr->src); + npcb->remote_port = uip_tcphdr->src; + npcb->state = UIP_SYN_RCVD; + npcb->rcv_nxt = uip_seqno+1; + npcb->snd_wnd = uip_tcphdr->wnd; + npcb->ssthresh = npcb->snd_wnd; + npcb->snd_wl1 = uip_seqno-1; + npcb->cb_arg = pcb->cb_arg; + npcb->accept = pcb->accept; + + npcb->so_options = pcb->so_options&(UIP_SOF_DEBUG|UIP_SOF_DONTROUTE|UIP_SOF_KEEPALIVE|UIP_SOF_OOBINLINE|UIP_SOF_LINGER); + + UIP_TCP_REG(&uip_tcp_active_pcbs,npcb); + + uip_tcp_parseopt(npcb); + + optdata = htonl((((u32_t)2<<24)|((u32_t)4<<16)|(((u32_t)npcb->mss/256)<<8)|((u32_t)npcb->mss&255))); + + uip_tcpenqueue(npcb,NULL,0,UIP_TCP_SYN|UIP_TCP_ACK,0,(u8_t*)&optdata,4); + return uip_tcpoutput(npcb); + } + return UIP_ERR_OK; +} + +static s8_t uip_tcpinput_timewait(struct uip_tcp_pcb *pcb) +{ + if(UIP_TCP_SEQ_GT(uip_seqno+uip_tcplen,pcb->rcv_nxt)) pcb->rcv_nxt = uip_seqno+uip_tcplen; + if(uip_tcplen>0) { + uip_tcp_acknow(pcb); + } + + return uip_tcpoutput(pcb); +} + +static s8_t uip_tcpprocess(struct uip_tcp_pcb *pcb) +{ + struct uip_tcpseg *rseg; + u8_t acceptable = 0; + s8_t err; + + err = 0; + if(uip_flags&UIP_TCP_RST) { + if(pcb->state==UIP_SYN_SENT) { + if(uip_ackno==pcb->snd_nxt) acceptable = 1; + } else { + if(UIP_TCP_SEQ_BETWEEN(uip_seqno,pcb->rcv_nxt,pcb->rcv_nxt+pcb->rcv_wnd)) acceptable = 1; + } + if(acceptable) { + uip_recv_flags = UIP_TF_RESET; + pcb->flags &= ~UIP_TF_ACK_DELAY; + return UIP_ERR_RST; + } else + return UIP_ERR_OK; + } + + pcb->tmr = uip_tcp_ticks; + pcb->keepcnt = 0; + + switch(pcb->state) { + case UIP_SYN_SENT: + if(uip_flags&UIP_TCP_ACK && uip_flags&UIP_TCP_SYN && + uip_ackno==ntohl(pcb->unacked->tcphdr->seqno)+1) { + pcb->snd_buf++; + pcb->rcv_nxt = uip_seqno+1; + pcb->lastack = uip_ackno; + pcb->snd_wnd = uip_tcphdr->wnd; + pcb->snd_wl1 = uip_seqno-1; + pcb->state = UIP_ESTABLISHED; + pcb->cwnd = pcb->mss; + pcb->snd_queuelen--; + + rseg = pcb->unacked; + pcb->unacked = rseg->next; + uip_tcpseg_free(rseg); + + uip_tcp_parseopt(pcb); + + if(pcb->connected!=NULL) err = pcb->connected(pcb->cb_arg,pcb,UIP_ERR_OK); + + uip_tcp_ack(pcb); + } else if(uip_flags&UIP_TCP_ACK) { + uip_tcp_rst(uip_ackno,uip_seqno+uip_tcplen,&uip_iphdr->dst,&uip_iphdr->src,uip_tcphdr->dst,uip_tcphdr->src); + } + break; + case UIP_SYN_RCVD: + if(uip_flags&UIP_TCP_ACK && !(uip_flags&UIP_TCP_RST)) { + if(UIP_TCP_SEQ_BETWEEN(uip_ackno,pcb->lastack+1,pcb->snd_nxt)) { + pcb->state = UIP_ESTABLISHED; + + if(pcb->accept!=NULL) err = pcb->accept(pcb->cb_arg,pcb,UIP_ERR_OK); + if(err!=UIP_ERR_OK) { + uip_tcp_abort(pcb); + return UIP_ERR_ABRT; + } + uip_tcpreceive(pcb); + pcb->cwnd = pcb->mss; + } else { + uip_tcp_rst(uip_ackno,uip_seqno+uip_tcplen,&uip_iphdr->dst,&uip_iphdr->src,uip_tcphdr->dst,uip_tcphdr->src); + } + } + break; + case UIP_CLOSE_WAIT: + case UIP_ESTABLISHED: + uip_tcpreceive(pcb); + if(uip_flags&UIP_TCP_FIN) { + uip_tcp_acknow(pcb); + pcb->state = UIP_CLOSE_WAIT; + } + break; + case UIP_FIN_WAIT_1: + uip_tcpreceive(pcb); + if(uip_flags&UIP_TCP_FIN) { + if(uip_flags&UIP_TCP_ACK && uip_ackno==pcb->snd_nxt) { + uip_tcp_acknow(pcb); + uip_tcp_pcbpurge(pcb); + UIP_TCP_RMV(&uip_tcp_active_pcbs,pcb); + pcb->state = UIP_TIME_WAIT; + UIP_TCP_REG(&uip_tcp_tw_pcbs,pcb); + } else { + uip_tcp_acknow(pcb); + pcb->state = UIP_CLOSING; + } + } else if(uip_flags&UIP_TCP_ACK && uip_ackno==pcb->snd_nxt) { + pcb->state = UIP_FIN_WAIT_2; + } + break; + case UIP_FIN_WAIT_2: + uip_tcpreceive(pcb); + if(uip_flags&UIP_TCP_FIN) { + uip_tcp_acknow(pcb); + uip_tcp_pcbpurge(pcb); + UIP_TCP_RMV(&uip_tcp_active_pcbs,pcb); + pcb->state = UIP_TIME_WAIT; + UIP_TCP_REG(&uip_tcp_tw_pcbs,pcb); + } + break; + case UIP_CLOSING: + uip_tcpreceive(pcb); + if(uip_flags&UIP_TCP_ACK && uip_ackno==pcb->snd_nxt) { + uip_tcp_acknow(pcb); + uip_tcp_pcbpurge(pcb); + UIP_TCP_RMV(&uip_tcp_active_pcbs,pcb); + pcb->state = UIP_TIME_WAIT; + UIP_TCP_REG(&uip_tcp_tw_pcbs,pcb); + } + break; + case UIP_LAST_ACK: + uip_tcpreceive(pcb); + if(uip_flags&UIP_TCP_ACK && uip_ackno==pcb->snd_nxt) { + pcb->state = UIP_CLOSED; + uip_recv_flags = UIP_TF_CLOSED; + } + break; + default: + break; + + } + return UIP_ERR_OK; +} + +static void uip_tcpreceive(struct uip_tcp_pcb *pcb) +{ + struct uip_tcpseg *next,*prev; + struct uip_tcpseg *cseg; + struct uip_pbuf *p; + s32_t off,m; + u32_t right_wnd_edge; + u16_t new_tot_len; + + if(uip_flags&UIP_TCP_ACK) { + right_wnd_edge = pcb->snd_wnd+pcb->snd_wl1; + if(UIP_TCP_SEQ_LT(pcb->snd_wl1,uip_seqno) || + (pcb->snd_wl1==uip_seqno && UIP_TCP_SEQ_LT(pcb->snd_wl2,uip_ackno)) || + (pcb->snd_wl2==uip_ackno && uip_tcphdr->wnd>pcb->snd_wnd)) { + pcb->snd_wnd = uip_tcphdr->wnd; + pcb->snd_wl1 = uip_seqno; + pcb->snd_wl2 = uip_ackno; + } + + if(pcb->lastack==uip_ackno) { + pcb->acked = 0; + if(pcb->snd_wl1+pcb->snd_wnd==right_wnd_edge) { + pcb->dupacks++; + if(pcb->dupacks>=3 && pcb->unacked!=NULL) { + if(!(uip_flags&UIP_TF_INFR)) { + UIP_LOG("uip_tcpreceive: dupacks, fast retransmit."); + uip_tcp_rexmit(pcb) ; + + if(pcb->cwnd>pcb->snd_wnd) pcb->ssthresh = pcb->snd_wnd/2; + else pcb->ssthresh = pcb->cwnd/2; + + pcb->cwnd = pcb->ssthresh+3*pcb->mss; + pcb->flags |= UIP_TF_INFR; + } else { + if((u16_t)(pcb->cwnd+pcb->mss)>pcb->cwnd) pcb->cwnd += pcb->mss; + } + } + } + } else if(UIP_TCP_SEQ_BETWEEN(uip_ackno,pcb->lastack+1,pcb->snd_max)) { + if(pcb->flags&UIP_TF_INFR) { + pcb->flags &= ~UIP_TF_INFR; + pcb->cwnd = pcb->ssthresh; + } + + pcb->nrtx = 0; + pcb->rto = (pcb->sa>>3)+pcb->sv; + pcb->acked = uip_ackno-pcb->lastack; + pcb->snd_buf += pcb->acked; + pcb->dupacks = 0; + pcb->lastack = uip_ackno; + + if(pcb->state>=UIP_ESTABLISHED) { + if(pcb->cwndssthresh) { + if((u16_t)(pcb->cwnd+pcb->mss)>pcb->cwnd) pcb->cwnd += pcb->mss; + + } else { + u16_t new_cwnd = (pcb->cwnd+pcb->mss*pcb->mss/pcb->cwnd); + if(new_cwnd>pcb->cwnd) pcb->cwnd = new_cwnd; + } + } + + while(pcb->unacked!=NULL && + UIP_TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno)+UIP_TCP_TCPLEN(pcb->unacked),uip_ackno)) { + + next = pcb->unacked; + pcb->unacked = pcb->unacked->next; + pcb->snd_queuelen -= uip_pbuf_clen(next->p); + + uip_tcpseg_free(next); + } + pcb->polltmr = 0; + } + + while(pcb->unsent!=NULL && + UIP_TCP_SEQ_BETWEEN(uip_ackno,ntohl(pcb->unsent->tcphdr->seqno)+UIP_TCP_TCPLEN(pcb->unsent),pcb->snd_max)) { + + next = pcb->unsent; + pcb->unsent = pcb->unsent->next; + pcb->snd_queuelen -= uip_pbuf_clen(next->p); + + uip_tcpseg_free(next); + + if(pcb->unsent!=NULL) pcb->snd_nxt = htonl(pcb->unsent->tcphdr->seqno); + } + + if(pcb->rttest && UIP_TCP_SEQ_LT(pcb->rtseq,uip_ackno)) { + m = uip_tcp_ticks - pcb->rttest; + m = m - (pcb->sa>>3); + pcb->sa += m; + + if(m<0) m -= m; + + m = m - (pcb->sv>>2); + pcb->sv += m; + pcb->rto = (pcb->sa>>3)+pcb->sv; + + pcb->rttest = 0; + } + } + + if(uip_tcplen>0) { + if(UIP_TCP_SEQ_BETWEEN(pcb->rcv_nxt,uip_seqno+1,uip_seqno+uip_tcplen-1)) { + off = pcb->rcv_nxt - uip_seqno; + p = uip_inseg.p; + if(uip_inseg.p->lentot_len - off; + while(p->lenlen; + p->tot_len = new_tot_len; + p->len = 0; + p = p->next; + } + uip_pbuf_header(p,-off); + } else { + uip_pbuf_header(uip_inseg.p,-off); + } + + uip_inseg.dataptr = p->payload; + uip_inseg.len -= pcb->rcv_nxt - uip_seqno; + uip_inseg.tcphdr->seqno = uip_seqno = pcb->rcv_nxt; + } else { + if(UIP_TCP_SEQ_LT(uip_seqno,pcb->rcv_nxt)) { + UIP_LOG("uip_tcpreceive: duplicate seqno."); + uip_tcp_acknow(pcb); + } + } + + if(UIP_TCP_SEQ_BETWEEN(uip_seqno,pcb->rcv_nxt,pcb->rcv_nxt+pcb->rcv_wnd-1)) { + //printf("uip_tcpreceive: seqno = %u, rcv_nxt = %u, wnd = %u\n",uip_seqno,pcb->rcv_nxt,(pcb->rcv_nxt+pcb->rcv_wnd-1)); + if(pcb->rcv_nxt==uip_seqno) { + if(pcb->ooseq!=NULL && UIP_TCP_SEQ_LEQ(pcb->ooseq->tcphdr->seqno,uip_seqno+uip_inseg.len)) { + uip_inseg.len = pcb->ooseq->tcphdr->seqno - uip_seqno; + uip_pbuf_realloc(uip_inseg.p,uip_inseg.len); + } + + uip_tcplen = UIP_TCP_TCPLEN(&uip_inseg); + if(pcb->state!=UIP_CLOSE_WAIT) pcb->rcv_nxt += uip_tcplen; + //printf("uip_tcpreceive: uip_tcplen = %u, rcv_nxt = %u\n",uip_tcplen,pcb->rcv_nxt); + + if(pcb->rcv_wndrcv_wnd = 0; + else pcb->rcv_wnd -= uip_tcplen; + + if(uip_inseg.p->tot_len>0) { + uip_recv_data = uip_inseg.p; + uip_inseg.p = NULL; + } + + if(UIP_TCPH_FLAGS(uip_inseg.tcphdr)&UIP_TCP_FIN) { + UIP_LOG("uip_tcpreceive: received FIN.\n"); + uip_recv_flags = UIP_TF_GOT_FIN; + } + + while(pcb->ooseq!=NULL && pcb->ooseq->tcphdr->seqno==pcb->rcv_nxt) { + cseg = pcb->ooseq; + uip_seqno = pcb->ooseq->tcphdr->seqno; + + pcb->rcv_nxt += UIP_TCP_TCPLEN(cseg); + if(pcb->rcv_wndrcv_wnd = 0; + else pcb->rcv_wnd -= UIP_TCP_TCPLEN(cseg); + + if(cseg->p->tot_len>0) { + if(uip_recv_data) uip_pbuf_cat(uip_recv_data,cseg->p); + else uip_recv_data = cseg->p; + + cseg->p = NULL; + } + + if(UIP_TCPH_FLAGS(cseg->tcphdr)&UIP_TCP_FIN) { + UIP_LOG("uip_tcpreceive: dequeued FIN.\n"); + uip_recv_flags = UIP_TF_GOT_FIN; + } + + pcb->ooseq = cseg->next; + uip_tcpseg_free(cseg); + } + //printf("uip_tcpreceive: pcb->flags = %02x\n",pcb->flags); + uip_tcp_ack(pcb); + //printf("uip_tcpreceive: pcb->flags = %02x\n",pcb->flags); + } else { + UIP_LOG("uip_tcpreceive: packet out-of-sequence."); + uip_tcp_acknow(pcb); + if(pcb->ooseq==NULL) + uip_tcpseg_copy(&uip_inseg); + else { + prev = NULL; + for(next=pcb->ooseq;next!=NULL;next=next->next) { + if(uip_seqno==next->tcphdr->seqno) { + if(uip_inseg.len>next->len) { + cseg = uip_tcpseg_copy(&uip_inseg); + if(cseg!=NULL) { + cseg->next = next->next; + if(prev!=NULL) prev->next = cseg; + else pcb->ooseq = cseg; + } + break; + } else + break; + } else { + if(prev==NULL) { + if(UIP_TCP_SEQ_LT(uip_seqno,next->tcphdr->seqno)) { + if(UIP_TCP_SEQ_GT(uip_seqno+uip_inseg.len,next->tcphdr->seqno)) { + uip_inseg.len = next->tcphdr->seqno - uip_seqno; + uip_pbuf_realloc(uip_inseg.p,uip_inseg.len); + } + + cseg = uip_tcpseg_copy(&uip_inseg); + if(cseg!=NULL) { + cseg->next = next; + pcb->ooseq = cseg; + } + break; + } + } else if(UIP_TCP_SEQ_BETWEEN(uip_seqno,prev->tcphdr->seqno+1,next->tcphdr->seqno-1)) { + if(UIP_TCP_SEQ_GT(uip_seqno+uip_inseg.len,next->tcphdr->seqno)) { + uip_inseg.len = next->tcphdr->seqno - uip_seqno; + uip_pbuf_realloc(uip_inseg.p,uip_inseg.len); + } + + cseg = uip_tcpseg_copy(&uip_inseg); + if(cseg!=NULL) { + cseg->next = next; + prev->next = cseg; + if(UIP_TCP_SEQ_GT(prev->tcphdr->seqno+prev->len,uip_seqno)) { + prev->len = uip_seqno - prev->tcphdr->seqno; + uip_pbuf_realloc(prev->p,prev->len); + } + } + break; + } + + if(next->next==NULL && UIP_TCP_SEQ_GT(uip_seqno,next->tcphdr->seqno)) { + next->next = uip_tcpseg_copy(&uip_inseg); + if(next->next!=NULL) { + if(UIP_TCP_SEQ_GT(next->tcphdr->seqno+next->len,uip_seqno)) { + next->len = uip_seqno - next->tcphdr->seqno; + uip_pbuf_realloc(next->p,next->len); + } + } + } + } + prev = next; + } + } + } + } else { + if(!UIP_TCP_SEQ_BETWEEN(uip_seqno,pcb->rcv_nxt,pcb->rcv_nxt+pcb->rcv_wnd-1)) { + uip_tcp_acknow(pcb); + } + } + } else { + if(!UIP_TCP_SEQ_BETWEEN(uip_seqno,pcb->rcv_nxt,pcb->rcv_nxt+pcb->rcv_wnd-1)) { + uip_tcp_acknow(pcb); + } + } +} + +static s8_t uip_tcp_nullaccept(void *arg,struct uip_tcp_pcb *pcb,s8_t err) +{ + return UIP_ERR_ABRT; +} + +static s8_t uip_tcp_nullrecv(void *arg,struct uip_tcp_pcb *pcb,struct uip_pbuf *p,s8_t err) +{ + if(p!=NULL) uip_pbuf_free(p); + else if(err==UIP_ERR_OK) return uip_tcp_close(pcb); + + return UIP_ERR_OK; +} diff --git a/wii/libogc/libdb/uIP/uip_tcp.h b/wii/libogc/libdb/uIP/uip_tcp.h new file mode 100644 index 0000000000..9c058c9e1e --- /dev/null +++ b/wii/libogc/libdb/uIP/uip_tcp.h @@ -0,0 +1,256 @@ +#ifndef __UIP_TCP_H__ +#define __UIP_TCP_H__ + +#include "uip.h" +#include "uip_ip.h" +#include "uip_pbuf.h" + +#define UIP_TCP_PRIO_MIN 1 +#define UIP_TCP_PRIO_NORMAL 64 +#define UIP_TCP_PRIO_MAX 127 + +/* Keepalive values */ +#define UIP_TCP_KEEPDEFAULT 7200000 /* KEEPALIVE timer in miliseconds */ +#define UIP_TCP_KEEPINTVL 75000 /* Time between KEEPALIVE probes in miliseconds */ +#define UIP_TCP_KEEPCNT 9 /* Counter for KEEPALIVE probes */ +#define UIP_TCP_MAXIDLE (UIP_TCP_KEEPCNT*UIP_TCP_KEEPINTVL) /* Maximum KEEPALIVE probe time */ + +#define UIP_TCP_TMR_INTERVAL 250 +#define UIP_TCP_SLOW_INTERVAL (2*UIP_TCP_TMR_INTERVAL) + +#define UIP_TCP_OOSEQ_TIMEOUT 6 /* x RTO */ + +#define UIP_TCP_MSL 60000 /* The maximum segment lifetime in microseconds */ + +#define UIP_TCP_SEQ_LT(a,b) ((s32_t)((a)-(b)) < 0) +#define UIP_TCP_SEQ_LEQ(a,b) ((s32_t)((a)-(b)) <= 0) +#define UIP_TCP_SEQ_GT(a,b) ((s32_t)((a)-(b)) > 0) +#define UIP_TCP_SEQ_GEQ(a,b) ((s32_t)((a)-(b)) >= 0) + +#define UIP_TCP_SEQ_BETWEEN(a,b,c) (UIP_TCP_SEQ_GEQ(a,b) && UIP_TCP_SEQ_LEQ(a,c)) +#define UIP_TCP_FIN 0x01U +#define UIP_TCP_SYN 0x02U +#define UIP_TCP_RST 0x04U +#define UIP_TCP_PSH 0x08U +#define UIP_TCP_ACK 0x10U +#define UIP_TCP_URG 0x20U +#define UIP_TCP_ECE 0x40U +#define UIP_TCP_CWR 0x80U + +#define UIP_TCP_FLAGS 0x3fU + +/* Length of the TCP header, excluding options. */ +#define UIP_TCP_HLEN 20 + +#define UIP_TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */ +#define UIP_TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */ + +#define UIP_TCPH_OFFSET(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 8) +#define UIP_TCPH_HDRLEN(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 12) +#define UIP_TCPH_FLAGS(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) & UIP_TCP_FLAGS) + +#define UIP_TCPH_OFFSET_SET(phdr, offset) (phdr)->_hdrlen_rsvd_flags = htons(((offset) << 8)|UIP_TCPH_FLAGS(phdr)) +#define UIP_TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = htons(((len)<<12)|UIP_TCPH_FLAGS(phdr)) +#define UIP_TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = htons((ntohs((phdr)->_hdrlen_rsvd_flags)&~UIP_TCP_FLAGS)|(flags)) +#define UIP_TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = htons(ntohs((phdr)->_hdrlen_rsvd_flags)|(flags)) +#define UIP_TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = htons(ntohs((phdr)->_hdrlen_rsvd_flags)|(UIP_TCPH_FLAGS(phdr)&~(flags)) ) + +#define UIP_TCP_TCPLEN(seg) ((seg)->len+((UIP_TCPH_FLAGS((seg)->tcphdr)&UIP_TCP_FIN || UIP_TCPH_FLAGS((seg)->tcphdr)&UIP_TCP_SYN)?1:0)) + +#define UIP_TCP_REG(pcbs,npcb) \ + do { \ + npcb->next = *pcbs; \ + *(pcbs) = npcb; \ + tcp_tmr_needed(); \ + } while(0) + +#define UIP_TCP_RMV(pcbs,npcb) \ + do { \ + if(*(pcbs)==npcb) { \ + *(pcbs) = (*pcbs)->next; \ + } else { \ + for(uip_tcp_tmp_pcb=*pcbs;uip_tcp_tmp_pcb!=NULL;uip_tcp_tmp_pcb=uip_tcp_tmp_pcb->next) { \ + if(uip_tcp_tmp_pcb->next!=NULL && uip_tcp_tmp_pcb->next==npcb) { \ + uip_tcp_tmp_pcb->next = npcb->next; \ + break; \ + } \ + } \ + } \ + } while(0) + +#define uip_tcp_sndbuf(pcb) (pcb)->snd_buf + +#define uip_tcp_acknow(pcb) \ + (pcb)->flags |= UIP_TF_ACK_NOW; \ + uip_tcpoutput((pcb)) + +#define uip_tcp_ack(pcb) \ + if((pcb)->flags&UIP_TF_ACK_DELAY) { \ + (pcb)->flags &= ~UIP_TF_ACK_DELAY; \ + (pcb)->flags |= UIP_TF_ACK_NOW; \ + uip_tcpoutput((pcb)); \ + } else { \ + (pcb)->flags |= UIP_TF_ACK_DELAY; \ + } + +/* The TCP Header */ +PACK_STRUCT_BEGIN +struct uip_tcp_hdr { + PACK_STRUCT_FIELD(u16_t src); + PACK_STRUCT_FIELD(u16_t dst); + PACK_STRUCT_FIELD(u32_t seqno); + PACK_STRUCT_FIELD(u32_t ackno); + PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags); + PACK_STRUCT_FIELD(u16_t wnd); + PACK_STRUCT_FIELD(u16_t chksum); + PACK_STRUCT_FIELD(u16_t urgp); +} PACK_STRUCT_STRUCT; +PACK_STRUCT_END + +enum uip_tcp_state { + UIP_CLOSED = 0, + UIP_LISTEN = 1, + UIP_SYN_SENT = 2, + UIP_SYN_RCVD = 3, + UIP_ESTABLISHED = 4, + UIP_FIN_WAIT_1 = 5, + UIP_FIN_WAIT_2 = 6, + UIP_CLOSE_WAIT = 7, + UIP_CLOSING = 8, + UIP_LAST_ACK = 9, + UIP_TIME_WAIT = 10 +}; + +struct uip_tcpseg { + struct uip_tcpseg *next; + struct uip_pbuf *p; + u8_t *dataptr; + u16_t len; + struct uip_tcp_hdr *tcphdr; +}; + +struct uip_tcp_pcb { + UIP_IP_PCB; + + struct uip_tcp_pcb *next; + enum uip_tcp_state state; + + u8_t prio; + void *cb_arg; + + u16_t local_port; + u16_t remote_port; + + u8_t flags; +#define UIP_TF_ACK_DELAY (u8_t)0x01U /* Delayed ACK. */ +#define UIP_TF_ACK_NOW (u8_t)0x02U /* Immediate ACK. */ +#define UIP_TF_INFR (u8_t)0x04U /* In fast recovery. */ +#define UIP_TF_RESET (u8_t)0x08U /* Connection was reset. */ +#define UIP_TF_CLOSED (u8_t)0x10U /* Connection was sucessfully closed. */ +#define UIP_TF_GOT_FIN (u8_t)0x20U /* Connection was closed by the remote end. */ +#define UIP_TF_NODELAY (u8_t)0x40U /* Disable Nagle algorithm */ + + u32_t rcv_nxt; + u16_t rcv_wnd; + + u32_t tmr; + u8_t polltmr,pollinterval; + + u16_t rtime; + u16_t mss; + + u32_t rttest; + u32_t rtseq; + s16_t sa,sv; + + u16_t rto; + u8_t nrtx; + + u32_t lastack; + u8_t dupacks; + + u16_t cwnd; + u16_t ssthresh; + + u32_t snd_nxt,snd_max,snd_wnd,snd_wl1,snd_wl2,snd_lbb; + + u16_t acked; + u16_t snd_buf; + u8_t snd_queuelen; + + struct uip_tcpseg *unsent; + struct uip_tcpseg *unacked; + struct uip_tcpseg *ooseq; + + s8_t (*accept)(void *arg,struct uip_tcp_pcb *newpcb,s8_t err); + s8_t (*connected)(void *arg,struct uip_tcp_pcb *newpcb,s8_t err); + s8_t (*poll)(void *arg,struct uip_tcp_pcb *pcb); + + s8_t (*sent)(void *arg,struct uip_tcp_pcb *pcb,u16_t space); + s8_t (*recv)(void *arg,struct uip_tcp_pcb *pcb,struct uip_pbuf *p,s8_t err); + + void (*errf)(void *arg,s8_t err); + + u32_t keepalive; + u8_t keepcnt; +}; + +struct uip_tcp_pcb_listen { + UIP_IP_PCB; + + struct uip_tcp_pcb_listen *next; + enum uip_tcp_state state; + + u8_t prio; + void *cb_arg; + + u16_t local_port; + + s8_t (*accept)(void *arg,struct uip_tcp_pcb *newpcb,s8_t err); +}; + +union uip_tcp_listen_pcbs_t { + struct uip_tcp_pcb_listen *listen_pcbs; + struct uip_tcp_pcb *pcbs; +}; + +extern struct uip_tcp_pcb *uip_tcp_tmp_pcb; +extern struct uip_tcp_pcb *uip_tcp_active_pcbs; +extern struct uip_tcp_pcb *uip_tcp_tw_pcbs; +extern union uip_tcp_listen_pcbs_t uip_tcp_listen_pcbs; + +void uip_tcp_tmr(); +void uip_tcp_slowtmr(); +void uip_tcp_fasttmr(); +void uip_tcp_init(); +void uip_tcp_rst(u32_t seqno,u32_t ackno,struct uip_ip_addr *lipaddr,struct uip_ip_addr *ripaddr,u16_t lport,u16_t rport); +void uip_tcp_abort(struct uip_tcp_pcb *pcb); +void uip_tcp_pcbremove(struct uip_tcp_pcb **pcblist,struct uip_tcp_pcb *pcb); +void uip_tcp_pcbpurge(struct uip_tcp_pcb *pcb); +void uip_tcp_rexmit(struct uip_tcp_pcb *pcb); +void uip_tcp_rexmit_rto(struct uip_tcp_pcb *pcb); +void uip_tcp_accept(struct uip_tcp_pcb *pcb,s8_t (*accept)(void *,struct uip_tcp_pcb *,s8_t)); +void uip_tcp_recved(struct uip_tcp_pcb *pcb,u16_t len); +void uip_tcp_err(struct uip_tcp_pcb *pcb,void (*errf)(void *,s8_t)); +void uip_tcp_keepalive(struct uip_tcp_pcb *pcb); +void uip_tcp_arg(struct uip_tcp_pcb *pcb,void *arg); +void uip_tcp_recv(struct uip_tcp_pcb *pcb,s8_t (*recv)(void *,struct uip_tcp_pcb *,struct uip_pbuf *,s8_t)); +void uip_tcp_sent(struct uip_tcp_pcb *pcb,s8_t (*sent)(void *,struct uip_tcp_pcb *,u16_t)); +void uip_tcp_poll(struct uip_tcp_pcb *pcb,s8_t (*poll)(void *,struct uip_tcp_pcb *),u8_t interval); +s8_t uip_tcp_close(struct uip_tcp_pcb *pcb); +s8_t uip_tcp_bind(struct uip_tcp_pcb *pcb,struct uip_ip_addr *ipaddr,u16_t port); +s8_t uip_tcp_write(struct uip_tcp_pcb *pcb,const void *arg,u16_t len,u8_t copy); +struct uip_tcp_pcb* uip_tcp_listen(struct uip_tcp_pcb *pcb); +struct uip_tcp_pcb* uip_tcp_new(); +struct uip_tcp_pcb* uip_tcp_pcballoc(u8_t prio); + +s8_t uip_tcp_sendctrl(struct uip_tcp_pcb *pcb,u8_t flags); +s8_t uip_tcpoutput(struct uip_tcp_pcb *pcb); +s8_t uip_tcpenqueue(struct uip_tcp_pcb *pcb,void *arg,u16_t len,u8_t flags,u8_t copy,u8_t *optdata,u8_t optlen); +u8_t uip_tcpseg_free(struct uip_tcpseg *seg); +u8_t uip_tcpsegs_free(struct uip_tcpseg *seg); +u32_t uip_tcpiss_next(); +void uip_tcpinput(struct uip_pbuf *p,struct uip_netif *inp); +struct uip_tcpseg* uip_tcpseg_copy(struct uip_tcpseg *seg); + +#endif diff --git a/wii/libogc/libdb/uIP/uipopt.h b/wii/libogc/libdb/uIP/uipopt.h new file mode 100644 index 0000000000..e21f967639 --- /dev/null +++ b/wii/libogc/libdb/uIP/uipopt.h @@ -0,0 +1,500 @@ +/** + * \defgroup uipopt Configuration options for uIP + * @{ + * + * uIP is configured using the per-project configuration file + * "uipopt.h". This file contains all compile-time options for uIP and + * should be tweaked to match each specific project. The uIP + * distribution contains a documented example "uipopt.h" that can be + * copied and modified for each project. + */ + +/** + * \file + * Configuration options for uIP. + * \author Adam Dunkels + * + * This file is used for tweaking various configuration options for + * uIP. You should make a copy of this file into one of your project's + * directories instead of editing this example "uipopt.h" file that + * comes with the uIP distribution. + */ + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * + */ + +#ifndef __UIPOPT_H__ +#define __UIPOPT_H__ + +#include +#include +#include + +/*------------------------------------------------------------------------------*/ +/** + * \defgroup uipopttypedef uIP type definitions + * @{ + */ + +/** + * The 8-bit unsigned data type. + * + * This may have to be tweaked for your particular compiler. "unsigned + * char" works for most compilers. + */ +typedef u8 u8_t; + +/** + * The 8-bit signed data type. + * + * This may have to be tweaked for your particular compiler. "unsigned + * char" works for most compilers. + */ +typedef s8 s8_t; + +/** + * The 16-bit unsigned data type. + * + * This may have to be tweaked for your particular compiler. "unsigned + * short" works for most compilers. + */ +typedef u16 u16_t; + +/** + * The 16-bit signed data type. + * + * This may have to be tweaked for your particular compiler. "unsigned + * short" works for most compilers. + */ +typedef s16 s16_t; + +/** + * The 32-bit signed data type. + * + * This may have to be tweaked for your particular compiler. "unsigned + * short" works for most compilers. + */ +typedef s32 s32_t; + +/** + * The 32-bit unsigned data type. + * + * This may have to be tweaked for your particular compiler. "unsigned + * short" works for most compilers. + */ +typedef u32 u32_t; + +/** + * The statistics data type. + * + * This datatype determines how high the statistics counters are able + * to count. + */ +typedef u16 uip_stats_t; + +/** @} */ +/*------------------------------------------------------------------------------*/ +/** + * \defgroup uipoptip IP configuration options + * @{ + * + */ +/** + * The IP TTL (time to live) of IP packets sent by uIP. + * + * This should normally not be changed. + */ +#define UIP_TCP_TTL 255 + +#define UIP_TCP 1 + +/** + * Turn on support for IP packet reassembly. + * + * uIP supports reassembly of fragmented IP packets. This features + * requires an additonal amount of RAM to hold the reassembly buffer + * and the reassembly code size is approximately 700 bytes. The + * reassembly buffer is of the same size as the uip_buf buffer + * (configured by UIP_BUFSIZE). + * + * \note IP packet reassembly is not heavily tested. + * + * \hideinitializer + */ +#define UIP_IP_REASSEMBLY 0 + +#define UIP_IP_FRAG 0 + +/** + * The maximum time an IP fragment should wait in the reassembly + * buffer before it is dropped. + * + */ +#define UIP_REASS_MAXAGE 30 + +/** @} */ + +/*------------------------------------------------------------------------------*/ +/** + * \defgroup uipoptudp UDP configuration options + * @{ + * + * \note The UDP support in uIP is still not entirely complete; there + * is no support for sending or receiving broadcast or multicast + * packets, but it works well enough to support a number of vital + * applications such as DNS queries, though + */ + +/** + * Toggles wether UDP support should be compiled in or not. + * + * \hideinitializer + */ +#define UIP_UDP 0 + +/** + * Toggles if UDP checksums should be used or not. + * + * \note Support for UDP checksums is currently not included in uIP, + * so this option has no function. + * + * \hideinitializer + */ +#define UIP_UDP_CHECKSUMS 0 + +/** + * The maximum amount of concurrent UDP connections. + * + * \hideinitializer + */ +#define UIP_UDP_CONNS 10 + +/** + * The name of the function that should be called when UDP datagrams arrive. + * + * \hideinitializer + */ +//#define UIP_UDP_APPCALL ((void*0) + +/** @} */ +/*------------------------------------------------------------------------------*/ +/** + * \defgroup uipopttcp TCP configuration options + * @{ + */ + +/** + * Determines if support for opening connections from uIP should be + * compiled in. + * + * If the applications that are running on top of uIP for this project + * do not need to open outgoing TCP connections, this configration + * option can be turned off to reduce the code size of uIP. + * + * \hideinitializer + */ +#define UIP_ACTIVE_OPEN 1 + +/** + * The maximum number of simultaneously open TCP connections. + * + * Since the TCP connections are statically allocated, turning this + * configuration knob down results in less RAM used. Each TCP + * connection requires approximatly 30 bytes of memory. + * + * \hideinitializer + */ +#define UIP_TCP_PCBS 4 + +/** + * The maximum number of simultaneously listening TCP ports. + * + * Each listening TCP port requires 2 bytes of memory. + * + * \hideinitializer + */ +#define UIP_LISTEN_TCP_PCBS 2 + +/** + * The size of the advertised receiver's window. + * + * Should be set low (i.e., to the size of the uip_buf buffer) is the + * application is slow to process incoming data, or high (32768 bytes) + * if the application processes data quickly. + * + * \hideinitializer + */ +#define UIP_TCPIP_SOCKS 32 + +#define UIP_TCP_SEGS 32 + + +/** + * Determines if support for TCP urgent data notification should be + * compiled in. + * + * Urgent data (out-of-band data) is a rarely used TCP feature that + * very seldom would be required. + * + * \hideinitializer + */ +#define UIP_URGDATA 1 + +/** + * The initial retransmission timeout counted in timer pulses. + * + * This should not be changed. + */ +#define UIP_RTO 3 + +/** + * The maximum number of times a segment should be retransmitted + * before the connection should be aborted. + * + * This should not be changed. + */ +#define UIP_MAXRTX 12 + +/** + * The maximum number of times a SYN segment should be retransmitted + * before a connection request should be deemed to have been + * unsuccessful. + * + * This should not need to be changed. + */ +#define UIP_MAXSYNRTX 4 + +/** + * The TCP maximum segment size. + * + * This is should not be to set to more than UIP_BUFSIZE - UIP_LLH_LEN - 40. + */ +#define UIP_TCP_MSS (1460) + + +#define UIP_TCP_SND_BUF (4*UIP_TCP_MSS) + +#define UIP_TCP_SND_QUEUELEN (4*UIP_TCP_SND_BUF/UIP_TCP_MSS) + +#define UIP_TCP_WND (4*UIP_TCP_MSS) + +/** + * How long a connection should stay in the TIME_WAIT state. + * + * This configiration option has no real implication, and it should be + * left untouched. + */ +#define UIP_TIME_WAIT_TIMEOUT 120 + + +/** @} */ +/*------------------------------------------------------------------------------*/ +/** + * \defgroup uipoptarp ARP configuration options + * @{ + */ + +/** + * The size of the ARP table. + * + * This option should be set to a larger value if this uIP node will + * have many connections from the local network. + * + * \hideinitializer + */ +#define UIP_ARPTAB_SIZE 8 + +/** + * The maxium age of ARP table entries measured in 10ths of seconds. + * + * An UIP_ARP_MAXAGE of 120 corresponds to 20 minutes (BSD + * default). + */ +#define UIP_ARP_MAXAGE 120 + +/** @} */ + +/*------------------------------------------------------------------------------*/ + +/** + * \defgroup uipoptgeneral General configuration options + * @{ + */ + +/** + * The size of the uIP packet buffer. + * + * The uIP packet buffer should not be smaller than 60 bytes, and does + * not need to be larger than 1500 bytes. Lower size results in lower + * TCP throughput, larger size results in higher TCP throughput. + * + * \hideinitializer + */ +#define UIP_MEM_SIZE (28*1024) + +#define UIP_PBUF_POOL_NUM 16 +#define UIP_PBUF_POOL_BUFSIZE 1600 + +#define UIP_PBUF_ROM_NUM 128 + + +/** + * Determines if statistics support should be compiled in. + * + * The statistics is useful for debugging and to show the user. + * + * \hideinitializer + */ +#define UIP_STATISTICS 0 + +/** + * Determines if logging of certain events should be compiled in. + * + * This is useful mostly for debugging. The function uip_log() + * must be implemented to suit the architecture of the project, if + * logging is turned on. + * + * \hideinitializer + */ +#define UIP_LOGGING 0 +#define UIP_ERRORING 0 + +/** + * Print out a uIP log message. + * + * This function must be implemented by the module that uses uIP, and + * is called by uIP whenever a log message is generated. + */ +void uip_log(const char *filename,int line_nb,char *msg); + +/** + * The link level header length. + * + * This is the offset into the uip_buf where the IP header can be + * found. For Ethernet, this should be set to 14. For SLIP, this + * should be set to 0. + * + * \hideinitializer + */ +#define UIP_LL_HLEN 16 + +#define UIP_TCPIP_HLEN 40 +/** @} */ +/*------------------------------------------------------------------------------*/ +/** + * \defgroup uipoptcpu CPU architecture configuration + * @{ + * + * The CPU architecture configuration is where the endianess of the + * CPU on which uIP is to be run is specified. Most CPUs today are + * little endian, and the most notable exception are the Motorolas + * which are big endian. The BYTE_ORDER macro should be changed to + * reflect the CPU architecture on which uIP is to be run. + */ +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 3412 +#endif /* LITTLE_ENDIAN */ +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 1234 +#endif /* BIGE_ENDIAN */ + +/** + * The byte order of the CPU architecture on which uIP is to be run. + * + * This option can be either BIG_ENDIAN (Motorola byte order) or + * LITTLE_ENDIAN (Intel byte order). + * + * \hideinitializer + */ +#ifndef BYTE_ORDER +#define BYTE_ORDER BIG_ENDIAN +#endif /* BYTE_ORDER */ + +/** @} */ +/*------------------------------------------------------------------------------*/ + +/** + * \defgroup uipoptapp Appication specific configurations + * @{ + * + * An uIP application is implemented using a single application + * function that is called by uIP whenever a TCP/IP event occurs. The + * name of this function must be registered with uIP at compile time + * using the UIP_APPCALL definition. + * + * uIP applications can store the application state within the + * uip_conn structure by specifying the size of the application + * structure with the UIP_APPSTATE_SIZE macro. + * + * The file containing the definitions must be included in the + * uipopt.h file. + * + * The following example illustrates how this can look. + \code + +void httpd_appcall(void); +#define UIP_APPCALL httpd_appcall + +struct httpd_state { + u8_t state; + u16_t count; + char *dataptr; + char *script; +}; +#define UIP_APPSTATE_SIZE (sizeof(struct httpd_state)) + \endcode + */ + +/** + * \var #define UIP_APPCALL + * + * The name of the application function that uIP should call in + * response to TCP/IP events. + * + */ + +/** + * \var #define UIP_APPSTATE_SIZE + * + * The size of the application state that is to be stored in the + * uip_conn structure. + */ +/** @} */ + +/* Include the header file for the application program that should be + used. If you don't use the example web server, you should change + this. */ + +#define UIP_LIBC_MEMFUNCREPLACE 1 + +#endif /* __UIPOPT_H__ */ diff --git a/wii/libogc/libfat/bit_ops.h b/wii/libogc/libfat/bit_ops.h new file mode 100644 index 0000000000..762be0b340 --- /dev/null +++ b/wii/libogc/libfat/bit_ops.h @@ -0,0 +1,57 @@ +/* + bit_ops.h + Functions for dealing with conversion of data between types + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _BIT_OPS_H +#define _BIT_OPS_H + +#include + +/*----------------------------------------------------------------- +Functions to deal with little endian values stored in uint8_t arrays +-----------------------------------------------------------------*/ +static inline uint16_t u8array_to_u16 (const uint8_t* item, int offset) { + return ( item[offset] | (item[offset + 1] << 8)); +} + +static inline uint32_t u8array_to_u32 (const uint8_t* item, int offset) { + return ( item[offset] | (item[offset + 1] << 8) | (item[offset + 2] << 16) | (item[offset + 3] << 24)); +} + +static inline void u16_to_u8array (uint8_t* item, int offset, uint16_t value) { + item[offset] = (uint8_t) value; + item[offset + 1] = (uint8_t)(value >> 8); +} + +static inline void u32_to_u8array (uint8_t* item, int offset, uint32_t value) { + item[offset] = (uint8_t) value; + item[offset + 1] = (uint8_t)(value >> 8); + item[offset + 2] = (uint8_t)(value >> 16); + item[offset + 3] = (uint8_t)(value >> 24); +} + +#endif // _BIT_OPS_H diff --git a/wii/libogc/libfat/cache.c b/wii/libogc/libfat/cache.c new file mode 100644 index 0000000000..07576b9cbf --- /dev/null +++ b/wii/libogc/libfat/cache.c @@ -0,0 +1,323 @@ +/* + cache.c + The cache is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This cache implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the cache. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include + +#include "common.h" +#include "cache.h" +#include "disc.h" + +#include "mem_allocate.h" +#include "bit_ops.h" +#include "file_allocation_table.h" + +#define CACHE_FREE UINT_MAX + +CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, unsigned int bytesPerSector) { + CACHE* cache; + unsigned int i; + CACHE_ENTRY* cacheEntries; + + if (numberOfPages < 2) { + numberOfPages = 2; + } + + if (sectorsPerPage < 8) { + sectorsPerPage = 8; + } + + cache = (CACHE*) _FAT_mem_allocate (sizeof(CACHE)); + if (cache == NULL) { + return NULL; + } + + cache->disc = discInterface; + cache->endOfPartition = endOfPartition; + cache->numberOfPages = numberOfPages; + cache->sectorsPerPage = sectorsPerPage; + cache->bytesPerSector = bytesPerSector; + + + cacheEntries = (CACHE_ENTRY*) _FAT_mem_allocate ( sizeof(CACHE_ENTRY) * numberOfPages); + if (cacheEntries == NULL) { + _FAT_mem_free (cache); + return NULL; + } + + for (i = 0; i < numberOfPages; i++) { + cacheEntries[i].sector = CACHE_FREE; + cacheEntries[i].count = 0; + cacheEntries[i].last_access = 0; + cacheEntries[i].dirty = false; + cacheEntries[i].cache = (uint8_t*) _FAT_mem_align ( sectorsPerPage * bytesPerSector ); + } + + cache->cacheEntries = cacheEntries; + + return cache; +} + +void _FAT_cache_destructor (CACHE* cache) { + unsigned int i; + // Clear out cache before destroying it + _FAT_cache_flush(cache); + + // Free memory in reverse allocation order + for (i = 0; i < cache->numberOfPages; i++) { + _FAT_mem_free (cache->cacheEntries[i].cache); + } + _FAT_mem_free (cache->cacheEntries); + _FAT_mem_free (cache); +} + + +static u32 accessCounter = 0; + +static u32 accessTime(){ + accessCounter++; + return accessCounter; +} + + +static CACHE_ENTRY* _FAT_cache_getPage(CACHE *cache,sec_t sector) +{ + unsigned int i; + CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + unsigned int sectorsPerPage = cache->sectorsPerPage; + + bool foundFree = false; + unsigned int oldUsed = 0; + unsigned int oldAccess = UINT_MAX; + + for(i=0;i=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) { + cacheEntries[i].last_access = accessTime(); + return &(cacheEntries[i]); + } + + if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_accessdisc,cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL; + cacheEntries[oldUsed].dirty = false; + } + + sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size + sec_t next_page = sector + sectorsPerPage; + if(next_page > cache->endOfPartition) next_page = cache->endOfPartition; + + if(!_FAT_disc_readSectors(cache->disc,sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL; + + cacheEntries[oldUsed].sector = sector; + cacheEntries[oldUsed].count = next_page-sector; + cacheEntries[oldUsed].last_access = accessTime(); + + return &(cacheEntries[oldUsed]); +} + +bool _FAT_cache_readSectors(CACHE *cache,sec_t sector,sec_t numSectors,void *buffer) +{ + sec_t sec; + sec_t secs_to_read; + CACHE_ENTRY *entry; + uint8_t *dest = (uint8_t *)buffer; + + while(numSectors>0) { + entry = _FAT_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + secs_to_read = entry->count - sec; + if(secs_to_read>numSectors) secs_to_read = numSectors; + + memcpy(dest,entry->cache + (sec*cache->bytesPerSector),(secs_to_read*cache->bytesPerSector)); + + dest += (secs_to_read*cache->bytesPerSector); + sector += secs_to_read; + numSectors -= secs_to_read; + } + + return true; +} + +/* +Reads some data from a cache page, determined by the sector number +*/ +bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + CACHE_ENTRY *entry; + + if (offset + size > cache->bytesPerSector) return false; + + entry = _FAT_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(buffer,entry->cache + ((sec*cache->bytesPerSector) + offset),size); + + return true; +} + +bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) { + uint8_t buf[4]; + if (!_FAT_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false; + + switch(num_bytes) { + case 1: *value = buf[0]; break; + case 2: *value = u8array_to_u16(buf,0); break; + case 4: *value = u8array_to_u32(buf,0); break; + default: return false; + } + return true; +} + +/* +Writes some data to a cache page, making sure it is loaded into memory first. +*/ +bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + CACHE_ENTRY *entry; + + if (offset + size > cache->bytesPerSector) return false; + + entry = _FAT_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(entry->cache + ((sec*cache->bytesPerSector) + offset),buffer,size); + + entry->dirty = true; + return true; +} + +bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) { + uint8_t buf[4] = {0, 0, 0, 0}; + + switch(size) { + case 1: buf[0] = value; break; + case 2: u16_to_u8array(buf, 0, value); break; + case 4: u32_to_u8array(buf, 0, value); break; + default: return false; + } + + return _FAT_cache_writePartialSector(cache, buf, sector, offset, size); +} + +/* +Writes some data to a cache page, zeroing out the page first +*/ +bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + CACHE_ENTRY *entry; + + if (offset + size > cache->bytesPerSector) return false; + + entry = _FAT_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memset(entry->cache + (sec*cache->bytesPerSector),0,cache->bytesPerSector); + memcpy(entry->cache + ((sec*cache->bytesPerSector) + offset),buffer,size); + + entry->dirty = true; + return true; +} + + +bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer) +{ + sec_t sec; + sec_t secs_to_write; + CACHE_ENTRY* entry; + const uint8_t *src = (const uint8_t *)buffer; + + while(numSectors>0) + { + entry = _FAT_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + secs_to_write = entry->count - sec; + if(secs_to_write>numSectors) secs_to_write = numSectors; + + memcpy(entry->cache + (sec*cache->bytesPerSector),src,(secs_to_write*cache->bytesPerSector)); + + src += (secs_to_write*cache->bytesPerSector); + sector += secs_to_write; + numSectors -= secs_to_write; + + entry->dirty = true; + } + return true; +} + +/* +Flushes all dirty pages to disc, clearing the dirty flag. +*/ +bool _FAT_cache_flush (CACHE* cache) { + unsigned int i; + + for (i = 0; i < cache->numberOfPages; i++) { + if (cache->cacheEntries[i].dirty) { + if (!_FAT_disc_writeSectors (cache->disc, cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) { + return false; + } + } + cache->cacheEntries[i].dirty = false; + } + + return true; +} + +void _FAT_cache_invalidate (CACHE* cache) { + unsigned int i; + _FAT_cache_flush(cache); + for (i = 0; i < cache->numberOfPages; i++) { + cache->cacheEntries[i].sector = CACHE_FREE; + cache->cacheEntries[i].last_access = 0; + cache->cacheEntries[i].count = 0; + cache->cacheEntries[i].dirty = false; + } +} diff --git a/wii/libogc/libfat/cache.h b/wii/libogc/libfat/cache.h new file mode 100644 index 0000000000..07bb14c23e --- /dev/null +++ b/wii/libogc/libfat/cache.h @@ -0,0 +1,128 @@ +/* + cache.h + The cache is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This cache implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the cache. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _CACHE_H +#define _CACHE_H + +#include "common.h" +#include "disc.h" + +typedef struct { + sec_t sector; + unsigned int count; + unsigned int last_access; + bool dirty; + uint8_t* cache; +} CACHE_ENTRY; + +typedef struct { + const DISC_INTERFACE* disc; + sec_t endOfPartition; + unsigned int numberOfPages; + unsigned int sectorsPerPage; + unsigned int bytesPerSector; + CACHE_ENTRY* cacheEntries; +} CACHE; + +/* +Read data from a sector in the cache +If the sector is not in the cache, it will be swapped in +offset is the position to start reading from +size is the amount of data to read +Precondition: offset + size <= BYTES_PER_READ +*/ +bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size); + +bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the cache +If the sector is not in the cache, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the cache, zeroing the sector first +If the sector is not in the cache, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +/* +Read several sectors from the cache +*/ +bool _FAT_cache_readSectors (CACHE* cache, sec_t sector, sec_t numSectors, void* buffer); + +/* +Read a full sector from the cache +*/ +static inline bool _FAT_cache_readSector (CACHE* cache, void* buffer, sec_t sector) { + return _FAT_cache_readPartialSector (cache, buffer, sector, 0, cache->bytesPerSector); +} + +/* +Write a full sector to the cache +*/ +static inline bool _FAT_cache_writeSector (CACHE* cache, const void* buffer, sec_t sector) { + return _FAT_cache_writePartialSector (cache, buffer, sector, 0, cache->bytesPerSector); +} + +bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer); + +/* +Write any dirty sectors back to disc and clear out the contents of the cache +*/ +bool _FAT_cache_flush (CACHE* cache); + +/* +Clear out the contents of the cache without writing any dirty sectors first +*/ +void _FAT_cache_invalidate (CACHE* cache); + +CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, unsigned int bytesPerSector); + +void _FAT_cache_destructor (CACHE* cache); + +#endif // _CACHE_H + diff --git a/wii/libogc/libfat/common.h b/wii/libogc/libfat/common.h new file mode 100644 index 0000000000..c5c5632555 --- /dev/null +++ b/wii/libogc/libfat/common.h @@ -0,0 +1,78 @@ +/* + common.h + Common definitions and included files for the FATlib + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _COMMON_H +#define _COMMON_H + +#include +#include +#include + +// When compiling for NDS, make sure NDS is defined +#ifndef NDS + #if defined ARM9 || defined ARM7 + #define NDS + #endif +#endif + +// Platform specific includes +#if defined(__gamecube__) || defined (__wii__) + #include + #include + #include +#elif defined(NDS) + #include + #include + #include +#elif defined(GBA) + #include + #include +#endif + +// Platform specific options +#if defined (__wii__) + #define DEFAULT_CACHE_PAGES 4 + #define DEFAULT_SECTORS_PAGE 64 + #define USE_LWP_LOCK + #define USE_RTC_TIME +#elif defined (__gamecube__) + #define DEFAULT_CACHE_PAGES 4 + #define DEFAULT_SECTORS_PAGE 64 + #define USE_LWP_LOCK + #define USE_RTC_TIME +#elif defined (NDS) + #define DEFAULT_CACHE_PAGES 16 + #define DEFAULT_SECTORS_PAGE 8 + #define USE_RTC_TIME +#elif defined (GBA) + #define DEFAULT_CACHE_PAGES 2 + #define DEFAULT_SECTORS_PAGE 8 + #define LIMIT_SECTORS 128 +#endif + +#endif // _COMMON_H diff --git a/wii/libogc/libfat/directory.c b/wii/libogc/libfat/directory.c new file mode 100644 index 0000000000..65906d844b --- /dev/null +++ b/wii/libogc/libfat/directory.c @@ -0,0 +1,1129 @@ +/* + directory.c + Reading, writing and manipulation of the directory structure on + a FAT partition + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include + +#include "directory.h" +#include "common.h" +#include "partition.h" +#include "file_allocation_table.h" +#include "bit_ops.h" +#include "filetime.h" + +// Directory entry codes +#define DIR_ENTRY_LAST 0x00 +#define DIR_ENTRY_FREE 0xE5 + +typedef unsigned short ucs2_t; + +// Long file name directory entry +enum LFN_offset { + LFN_offset_ordinal = 0x00, // Position within LFN + LFN_offset_char0 = 0x01, + LFN_offset_char1 = 0x03, + LFN_offset_char2 = 0x05, + LFN_offset_char3 = 0x07, + LFN_offset_char4 = 0x09, + LFN_offset_flag = 0x0B, // Should be equal to ATTRIB_LFN + LFN_offset_reserved1 = 0x0C, // Always 0x00 + LFN_offset_checkSum = 0x0D, // Checksum of short file name (alias) + LFN_offset_char5 = 0x0E, + LFN_offset_char6 = 0x10, + LFN_offset_char7 = 0x12, + LFN_offset_char8 = 0x14, + LFN_offset_char9 = 0x16, + LFN_offset_char10 = 0x18, + LFN_offset_reserved2 = 0x1A, // Always 0x0000 + LFN_offset_char11 = 0x1C, + LFN_offset_char12 = 0x1E +}; +static const int LFN_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E}; + +#define LFN_END 0x40 +#define LFN_DEL 0x80 + +static const char ILLEGAL_ALIAS_CHARACTERS[] = "\\/:;*?\"<>|&+,=[] "; +static const char ILLEGAL_LFN_CHARACTERS[] = "\\/:*?\"<>|"; + +/* +Returns number of UCS-2 characters needed to encode an LFN +Returns -1 if it is an invalid LFN +*/ +#define ABOVE_UCS_RANGE 0xF0 +static int _FAT_directory_lfnLength (const char* name) { + unsigned int i; + size_t nameLength; + int ucsLength; + const char* tempName = name; + + nameLength = strnlen(name, NAME_MAX); + // Make sure the name is short enough to be valid + if ( nameLength >= NAME_MAX) { + return -1; + } + // Make sure it doesn't contain any invalid characters + if (strpbrk (name, ILLEGAL_LFN_CHARACTERS) != NULL) { + return -1; + } + // Make sure the name doesn't contain any control codes or codes not representable in UCS-2 + for (i = 0; i < nameLength; i++) { + unsigned char ch = (unsigned char) name[i]; + if (ch < 0x20 || ch >= ABOVE_UCS_RANGE) { + return -1; + } + } + // Convert to UCS-2 and get the resulting length + ucsLength = mbsrtowcs(NULL, &tempName, MAX_LFN_LENGTH, NULL); + if (ucsLength < 0 || ucsLength >= MAX_LFN_LENGTH) { + return -1; + } + + // Otherwise it is valid + return ucsLength; +} + +/* +Convert a multibyte encoded string into a NUL-terminated UCS-2 string, storing at most len characters +return number of characters stored +*/ +static size_t _FAT_directory_mbstoucs2 (ucs2_t* dst, const char* src, size_t len) { + mbstate_t ps = {0}; + wchar_t tempChar; + int bytes; + size_t count = 0; + + while (count < len-1 && *src != '\0') { + bytes = mbrtowc (&tempChar, src, MB_CUR_MAX, &ps); + if (bytes > 0) { + *dst = (ucs2_t)tempChar; + src += bytes; + dst++; + count++; + } else if (bytes == 0) { + break; + } else { + return -1; + } + } + *dst = '\0'; + + return count; +} + +/* +Convert a UCS-2 string into a NUL-terminated multibyte string, storing at most len chars +return number of chars stored, or (size_t)-1 on error +*/ +static size_t _FAT_directory_ucs2tombs (char* dst, const ucs2_t* src, size_t len) { + mbstate_t ps = {0}; + size_t count = 0; + int bytes; + char buff[MB_CUR_MAX]; + int i; + + while (count < len - 1 && *src != '\0') { + bytes = wcrtomb (buff, *src, &ps); + if (bytes < 0) { + return -1; + } + if (count + bytes < len && bytes > 0) { + for (i = 0; i < bytes; i++) { + *dst++ = buff[i]; + } + src++; + count += bytes; + } else { + break; + } + } + *dst = L'\0'; + + return count; +} + +/* +Case-independent comparison of two multibyte encoded strings +*/ +static int _FAT_directory_mbsncasecmp (const char* s1, const char* s2, size_t len1) { + wchar_t wc1, wc2; + mbstate_t ps1 = {0}; + mbstate_t ps2 = {0}; + size_t b1 = 0; + size_t b2 = 0; + + if (len1 == 0) { + return 0; + } + + do { + s1 += b1; + s2 += b2; + b1 = mbrtowc(&wc1, s1, MB_CUR_MAX, &ps1); + b2 = mbrtowc(&wc2, s2, MB_CUR_MAX, &ps2); + if ((int)b1 < 0 || (int)b2 < 0) { + break; + } + len1 -= b1; + } while (len1 > 0 && towlower(wc1) == towlower(wc2) && wc1 != 0); + + return towlower(wc1) - towlower(wc2); +} + + +static bool _FAT_directory_entryGetAlias (const u8* entryData, char* destName) { + char c; + bool caseInfo; + int i = 0; + int j = 0; + + destName[0] = '\0'; + if (entryData[0] != DIR_ENTRY_FREE) { + if (entryData[0] == '.') { + destName[0] = '.'; + if (entryData[1] == '.') { + destName[1] = '.'; + destName[2] = '\0'; + } else { + destName[1] = '\0'; + } + } else { + // Copy the filename from the dirEntry to the string + caseInfo = entryData[DIR_ENTRY_caseInfo] & CASE_LOWER_BASE; + for (i = 0; (i < 8) && (entryData[DIR_ENTRY_name + i] != ' '); i++) { + c = entryData[DIR_ENTRY_name + i]; + destName[i] = (caseInfo ? tolower((unsigned char)c) : c); + } + // Copy the extension from the dirEntry to the string + if (entryData[DIR_ENTRY_extension] != ' ') { + destName[i++] = '.'; + caseInfo = entryData[DIR_ENTRY_caseInfo] & CASE_LOWER_EXT; + for ( j = 0; (j < 3) && (entryData[DIR_ENTRY_extension + j] != ' '); j++) { + c = entryData[DIR_ENTRY_extension + j]; + destName[i++] = (caseInfo ? tolower((unsigned char)c) : c); + } + } + destName[i] = '\0'; + } + } + + return (destName[0] != '\0'); +} + +uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData) { + if (partition->filesysType == FS_FAT32) { + // Only use high 16 bits of start cluster when we are certain they are correctly defined + return u8array_to_u16(entryData,DIR_ENTRY_cluster) | (u8array_to_u16(entryData, DIR_ENTRY_clusterHigh) << 16); + } else { + return u8array_to_u16(entryData,DIR_ENTRY_cluster); + } +} + +static bool _FAT_directory_incrementDirEntryPosition (PARTITION* partition, DIR_ENTRY_POSITION* entryPosition, bool extendDirectory) { + DIR_ENTRY_POSITION position = *entryPosition; + uint32_t tempCluster; + + // Increment offset, wrapping at the end of a sector + ++ position.offset; + if (position.offset == partition->bytesPerSector / DIR_ENTRY_DATA_SIZE) { + position.offset = 0; + // Increment sector when wrapping + ++ position.sector; + // But wrap at the end of a cluster + if ((position.sector == partition->sectorsPerCluster) && (position.cluster != FAT16_ROOT_DIR_CLUSTER)) { + position.sector = 0; + // Move onto the next cluster, making sure there is another cluster to go to + tempCluster = _FAT_fat_nextCluster(partition, position.cluster); + if (tempCluster == CLUSTER_EOF) { + if (extendDirectory) { + tempCluster = _FAT_fat_linkFreeClusterCleared (partition, position.cluster); + if (!_FAT_fat_isValidCluster(partition, tempCluster)) { + return false; // This will only happen if the disc is full + } + } else { + return false; // Got to the end of the directory, not extending it + } + } + position.cluster = tempCluster; + } else if ((position.cluster == FAT16_ROOT_DIR_CLUSTER) && (position.sector == (partition->dataStart - partition->rootDirStart))) { + return false; // Got to end of root directory, can't extend it + } + } + *entryPosition = position; + return true; +} + +bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry) { + DIR_ENTRY_POSITION entryStart; + DIR_ENTRY_POSITION entryEnd; + uint8_t entryData[0x20]; + ucs2_t lfn[MAX_LFN_LENGTH]; + bool notFound, found; + int lfnPos; + uint8_t lfnChkSum, chkSum; + bool lfnExists; + int i; + + lfnChkSum = 0; + + entryStart = entry->dataEnd; + + // Make sure we are using the correct root directory, in case of FAT32 + if (entryStart.cluster == FAT16_ROOT_DIR_CLUSTER) { + entryStart.cluster = partition->rootDirCluster; + } + + entryEnd = entryStart; + + lfnExists = false; + + found = false; + notFound = false; + + while (!found && !notFound) { + if (_FAT_directory_incrementDirEntryPosition (partition, &entryEnd, false) == false) { + notFound = true; + break; + } + + _FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, entryEnd.cluster) + entryEnd.sector, + entryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + + if (entryData[DIR_ENTRY_attributes] == ATTRIB_LFN) { + // It's an LFN + if (entryData[LFN_offset_ordinal] & LFN_DEL) { + lfnExists = false; + } else if (entryData[LFN_offset_ordinal] & LFN_END) { + // Last part of LFN, make sure it isn't deleted using previous if(Thanks MoonLight) + entryStart = entryEnd; // This is the start of a directory entry + lfnExists = true; + lfnPos = (entryData[LFN_offset_ordinal] & ~LFN_END) * 13; + if (lfnPos > MAX_LFN_LENGTH - 1) { + lfnPos = MAX_LFN_LENGTH - 1; + } + lfn[lfnPos] = '\0'; // Set end of lfn to null character + lfnChkSum = entryData[LFN_offset_checkSum]; + } + if (lfnChkSum != entryData[LFN_offset_checkSum]) { + lfnExists = false; + } + if (lfnExists) { + lfnPos = ((entryData[LFN_offset_ordinal] & ~LFN_END) - 1) * 13; + for (i = 0; i < 13; i++) { + if (lfnPos + i < MAX_LFN_LENGTH - 1) { + lfn[lfnPos + i] = entryData[LFN_offset_table[i]] | (entryData[LFN_offset_table[i]+1] << 8); + } + } + } + } else if (entryData[DIR_ENTRY_attributes] & ATTRIB_VOL) { + // This is a volume name, don't bother with it + } else if (entryData[0] == DIR_ENTRY_LAST) { + notFound = true; + } else if ((entryData[0] != DIR_ENTRY_FREE) && (entryData[0] > 0x20) && !(entryData[DIR_ENTRY_attributes] & ATTRIB_VOL)) { + if (lfnExists) { + // Calculate file checksum + chkSum = 0; + for (i=0; i < 11; i++) { + // NOTE: The operation is an unsigned char rotate right + chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + entryData[i]; + } + if (chkSum != lfnChkSum) { + lfnExists = false; + entry->filename[0] = '\0'; + } + } + + if (lfnExists) { + if (_FAT_directory_ucs2tombs (entry->filename, lfn, NAME_MAX) == (size_t)-1) { + // Failed to convert the file name to UTF-8. Maybe the wrong locale is set? + return false; + } + } else { + entryStart = entryEnd; + _FAT_directory_entryGetAlias (entryData, entry->filename); + } + found = true; + } + } + + // If no file is found, return false + if (notFound) { + return false; + } else { + // Fill in the directory entry struct + entry->dataStart = entryStart; + entry->dataEnd = entryEnd; + memcpy (entry->entryData, entryData, DIR_ENTRY_DATA_SIZE); + return true; + } +} + +bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster) { + entry->dataStart.cluster = dirCluster; + entry->dataStart.sector = 0; + entry->dataStart.offset = -1; // Start before the beginning of the directory + + entry->dataEnd = entry->dataStart; + + return _FAT_directory_getNextEntry (partition, entry); +} + +bool _FAT_directory_getRootEntry (PARTITION* partition, DIR_ENTRY* entry) { + entry->dataStart.cluster = 0; + entry->dataStart.sector = 0; + entry->dataStart.offset = 0; + + entry->dataEnd = entry->dataStart; + + memset (entry->filename, '\0', NAME_MAX); + entry->filename[0] = '.'; + + memset (entry->entryData, 0, DIR_ENTRY_DATA_SIZE); + memset (entry->entryData, ' ', 11); + entry->entryData[0] = '.'; + + entry->entryData[DIR_ENTRY_attributes] = ATTRIB_DIR; + + u16_to_u8array (entry->entryData, DIR_ENTRY_cluster, partition->rootDirCluster); + u16_to_u8array (entry->entryData, DIR_ENTRY_clusterHigh, partition->rootDirCluster >> 16); + + return true; +} + +bool _FAT_directory_getVolumeLabel (PARTITION* partition, char *label) { + DIR_ENTRY entry; + DIR_ENTRY_POSITION entryEnd; + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + int i; + bool end; + + _FAT_directory_getRootEntry(partition, &entry); + + entryEnd = entry.dataEnd; + + // Make sure we are using the correct root directory, in case of FAT32 + if (entryEnd.cluster == FAT16_ROOT_DIR_CLUSTER) { + entryEnd.cluster = partition->rootDirCluster; + } + + label[0]='\0'; + label[11]='\0'; + end = false; + //this entry should be among the first 3 entries in the root directory table, if not, then system can have trouble displaying the right volume label + while(!end) { + if(!_FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, entryEnd.cluster) + entryEnd.sector, + entryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE)) + { //error reading + return false; + } + + if (entryData[DIR_ENTRY_attributes] == ATTRIB_VOL && entryData[0] != DIR_ENTRY_FREE) { + for (i = 0; i < 11; i++) { + label[i] = entryData[DIR_ENTRY_name + i]; + } + return true; + } else if (entryData[0] == DIR_ENTRY_LAST) { + end = true; + } + + if (_FAT_directory_incrementDirEntryPosition (partition, &entryEnd, false) == false) { + end = true; + } + } + return false; +} + +bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry) { + DIR_ENTRY_POSITION entryStart = entry->dataStart; + DIR_ENTRY_POSITION entryEnd = entry->dataEnd; + bool entryStillValid; + bool finished; + ucs2_t lfn[MAX_LFN_LENGTH]; + int i; + int lfnPos; + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + + memset (entry->filename, '\0', NAME_MAX); + + // Create an empty directory entry to overwrite the old ones with + for ( entryStillValid = true, finished = false; + entryStillValid && !finished; + entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &entryStart, false)) + { + _FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, + entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + + if ((entryStart.cluster == entryEnd.cluster) + && (entryStart.sector == entryEnd.sector) + && (entryStart.offset == entryEnd.offset)) { + // Copy the entry data and stop, since this is the last section of the directory entry + memcpy (entry->entryData, entryData, DIR_ENTRY_DATA_SIZE); + finished = true; + } else { + // Copy the long file name data + lfnPos = ((entryData[LFN_offset_ordinal] & ~LFN_END) - 1) * 13; + for (i = 0; i < 13; i++) { + if (lfnPos + i < MAX_LFN_LENGTH - 1) { + lfn[lfnPos + i] = entryData[LFN_offset_table[i]] | (entryData[LFN_offset_table[i]+1] << 8); + } + } + } + } + + if (!entryStillValid) { + return false; + } + + entryStart = entry->dataStart; + if ((entryStart.cluster == entryEnd.cluster) + && (entryStart.sector == entryEnd.sector) + && (entryStart.offset == entryEnd.offset)) { + // Since the entry doesn't have a long file name, extract the short filename + if (!_FAT_directory_entryGetAlias (entry->entryData, entry->filename)) { + return false; + } + } else { + // Encode the long file name into a multibyte string + if (_FAT_directory_ucs2tombs (entry->filename, lfn, NAME_MAX) == (size_t)-1) { + return false; + } + } + + return true; +} + + + +bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd) { + size_t dirnameLength; + const char* pathPosition; + const char* nextPathPosition; + uint32_t dirCluster; + bool foundFile; + char alias[MAX_ALIAS_LENGTH]; + bool found, notFound; + + pathPosition = path; + + found = false; + notFound = false; + + if (pathEnd == NULL) { + // Set pathEnd to the end of the path string + pathEnd = strchr (path, '\0'); + } + + if (pathPosition[0] == DIR_SEPARATOR) { + // Start at root directory + dirCluster = partition->rootDirCluster; + // Consume separator(s) + while (pathPosition[0] == DIR_SEPARATOR) { + pathPosition++; + } + // If the path is only specifying a directory in the form of "/" return it + if (pathPosition >= pathEnd) { + _FAT_directory_getRootEntry (partition, entry); + found = true; + } + } else { + // Start in current working directory + dirCluster = partition->cwdCluster; + } + + while (!found && !notFound) { + // Get the name of the next required subdirectory within the path + nextPathPosition = strchr (pathPosition, DIR_SEPARATOR); + if (nextPathPosition != NULL) { + dirnameLength = nextPathPosition - pathPosition; + } else { + dirnameLength = strlen(pathPosition); + } + + if (dirnameLength > NAME_MAX) { + // The path is too long to bother with + return false; + } + + // Check for "." or ".." when the dirCluster is root cluster + // These entries do not exist, so we must fake it + if ((dirCluster == partition->rootDirCluster) + && ((strncmp(".", pathPosition, dirnameLength) == 0) + || (strncmp("..", pathPosition, dirnameLength) == 0))) { + foundFile = true; + _FAT_directory_getRootEntry(partition, entry); + } else { + // Look for the directory within the path + foundFile = _FAT_directory_getFirstEntry (partition, entry, dirCluster); + + while (foundFile && !found && !notFound) { // It hasn't already found the file + // Check if the filename matches + if ((dirnameLength == strnlen(entry->filename, NAME_MAX)) + && (_FAT_directory_mbsncasecmp(pathPosition, entry->filename, dirnameLength) == 0)) { + found = true; + } + + // Check if the alias matches + _FAT_directory_entryGetAlias (entry->entryData, alias); + if ((dirnameLength == strnlen(alias, MAX_ALIAS_LENGTH)) + && (strncasecmp(pathPosition, alias, dirnameLength) == 0)) { + found = true; + } + + if (found && !(entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) && (nextPathPosition != NULL)) { + // Make sure that we aren't trying to follow a file instead of a directory in the path + found = false; + } + + if (!found) { + foundFile = _FAT_directory_getNextEntry (partition, entry); + } + } + } + + if (!foundFile) { + // Check that the search didn't get to the end of the directory + notFound = true; + found = false; + } else if ((nextPathPosition == NULL) || (nextPathPosition >= pathEnd)) { + // Check that we reached the end of the path + found = true; + } else if (entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) { + dirCluster = _FAT_directory_entryGetCluster (partition, entry->entryData); + if (dirCluster == CLUSTER_ROOT) + dirCluster = partition->rootDirCluster; + pathPosition = nextPathPosition; + // Consume separator(s) + while (pathPosition[0] == DIR_SEPARATOR) { + pathPosition++; + } + // The requested directory was found + if (pathPosition >= pathEnd) { + found = true; + } else { + found = false; + } + } + } + + if (found && !notFound) { + if (partition->filesysType == FS_FAT32 && (entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) && + _FAT_directory_entryGetCluster (partition, entry->entryData) == CLUSTER_ROOT) + { + // On FAT32 it should specify an actual cluster for the root entry, + // not cluster 0 as on FAT16 + _FAT_directory_getRootEntry (partition, entry); + } + return true; + } else { + return false; + } +} + +bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry) { + DIR_ENTRY_POSITION entryStart = entry->dataStart; + DIR_ENTRY_POSITION entryEnd = entry->dataEnd; + bool entryStillValid; + bool finished; + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + + // Create an empty directory entry to overwrite the old ones with + for ( entryStillValid = true, finished = false; + entryStillValid && !finished; + entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &entryStart, false)) + { + _FAT_cache_readPartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + entryData[0] = DIR_ENTRY_FREE; + _FAT_cache_writePartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + if ((entryStart.cluster == entryEnd.cluster) && (entryStart.sector == entryEnd.sector) && (entryStart.offset == entryEnd.offset)) { + finished = true; + } + } + + if (!entryStillValid) { + return false; + } + + return true; +} + +static bool _FAT_directory_findEntryGap (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster, size_t size) { + DIR_ENTRY_POSITION gapStart; + DIR_ENTRY_POSITION gapEnd; + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + size_t dirEntryRemain; + bool endOfDirectory, entryStillValid; + + // Scan Dir for free entry + gapEnd.offset = 0; + gapEnd.sector = 0; + gapEnd.cluster = dirCluster; + + gapStart = gapEnd; + + entryStillValid = true; + dirEntryRemain = size; + endOfDirectory = false; + + while (entryStillValid && !endOfDirectory && (dirEntryRemain > 0)) { + _FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector, + gapEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + if (entryData[0] == DIR_ENTRY_LAST) { + if (dirEntryRemain == size) { + gapStart = gapEnd; + } + -- dirEntryRemain; + endOfDirectory = true; + } else if (entryData[0] == DIR_ENTRY_FREE) { + if (dirEntryRemain == size) { + gapStart = gapEnd; + } + -- dirEntryRemain; + } else { + dirEntryRemain = size; + } + + if (!endOfDirectory && (dirEntryRemain > 0)) { + entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &gapEnd, true); + } + } + + // Make sure the scanning didn't fail + if (!entryStillValid) { + return false; + } + + // Save the start entry, since we know it is valid + entry->dataStart = gapStart; + + if (endOfDirectory) { + memset (entryData, DIR_ENTRY_LAST, DIR_ENTRY_DATA_SIZE); + dirEntryRemain += 1; // Increase by one to take account of End Of Directory Marker + while ((dirEntryRemain > 0) && entryStillValid) { + // Get the gapEnd before incrementing it, so the second to last one is saved + entry->dataEnd = gapEnd; + // Increment gapEnd, moving onto the next entry + entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &gapEnd, true); + -- dirEntryRemain; + // Fill the entry with blanks + _FAT_cache_writePartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector, + gapEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + } + if (!entryStillValid) { + return false; + } + } else { + entry->dataEnd = gapEnd; + } + + return true; +} + +static bool _FAT_directory_entryExists (PARTITION* partition, const char* name, uint32_t dirCluster) { + DIR_ENTRY tempEntry; + bool foundFile; + char alias[MAX_ALIAS_LENGTH]; + size_t dirnameLength; + + dirnameLength = strnlen(name, NAME_MAX); + + if (dirnameLength >= NAME_MAX) { + return false; + } + + // Make sure the entry doesn't already exist + foundFile = _FAT_directory_getFirstEntry (partition, &tempEntry, dirCluster); + + while (foundFile) { // It hasn't already found the file + // Check if the filename matches + if ((dirnameLength == strnlen(tempEntry.filename, NAME_MAX)) + && (_FAT_directory_mbsncasecmp(name, tempEntry.filename, dirnameLength) == 0)) { + return true; + } + + // Check if the alias matches + _FAT_directory_entryGetAlias (tempEntry.entryData, alias); + if ((strncasecmp(name, alias, MAX_ALIAS_LENGTH) == 0)) { + return true; + } + foundFile = _FAT_directory_getNextEntry (partition, &tempEntry); + } + return false; +} + +/* +Creates an alias for a long file name. If the alias is not an exact match for the +filename, it returns the number of characters in the alias. If the two names match, +it returns 0. If there was an error, it returns -1. +*/ +static int _FAT_directory_createAlias (char* alias, const char* lfn) { + bool lossyConversion = false; // Set when the alias had to be modified to be valid + int lfnPos = 0; + int aliasPos = 0; + wchar_t lfnChar; + int oemChar; + mbstate_t ps = {0}; + int bytesUsed = 0; + const char* lfnExt; + int aliasExtLen; + + // Strip leading periods + while (lfn[lfnPos] == '.') { + lfnPos ++; + lossyConversion = true; + } + + // Primary portion of alias + while (aliasPos < 8 && lfn[lfnPos] != '.' && lfn[lfnPos] != '\0') { + bytesUsed = mbrtowc(&lfnChar, lfn + lfnPos, NAME_MAX - lfnPos, &ps); + if (bytesUsed < 0) { + return -1; + } + oemChar = wctob(towupper((wint_t)lfnChar)); + if (wctob((wint_t)lfnChar) != oemChar) { + // Case of letter was changed + lossyConversion = true; + } + if (oemChar == ' ') { + // Skip spaces in filename + lossyConversion = true; + lfnPos += bytesUsed; + continue; + } + if (oemChar == EOF) { + oemChar = '_'; // Replace unconvertable characters with underscores + lossyConversion = true; + } + if (strchr (ILLEGAL_ALIAS_CHARACTERS, oemChar) != NULL) { + // Invalid Alias character + oemChar = '_'; // Replace illegal characters with underscores + lossyConversion = true; + } + + alias[aliasPos] = (char)oemChar; + aliasPos++; + lfnPos += bytesUsed; + } + + if (lfn[lfnPos] != '.' && lfn[lfnPos] != '\0') { + // Name was more than 8 characters long + lossyConversion = true; + } + + // Alias extension + lfnExt = strrchr (lfn, '.'); + if (lfnExt != NULL && lfnExt != strchr (lfn, '.')) { + // More than one period in name + lossyConversion = true; + } + if (lfnExt != NULL && lfnExt[1] != '\0') { + lfnExt++; + alias[aliasPos] = '.'; + aliasPos++; + memset (&ps, 0, sizeof(ps)); + for (aliasExtLen = 0; aliasExtLen < MAX_ALIAS_EXT_LENGTH && *lfnExt != '\0'; aliasExtLen++) { + bytesUsed = mbrtowc(&lfnChar, lfnExt, NAME_MAX - lfnPos, &ps); + if (bytesUsed < 0) { + return -1; + } + oemChar = wctob(towupper((wint_t)lfnChar)); + if (wctob((wint_t)lfnChar) != oemChar) { + // Case of letter was changed + lossyConversion = true; + } + if (oemChar == ' ') { + // Skip spaces in alias + lossyConversion = true; + lfnExt += bytesUsed; + continue; + } + if (oemChar == EOF) { + oemChar = '_'; // Replace unconvertable characters with underscores + lossyConversion = true; + } + if (strchr (ILLEGAL_ALIAS_CHARACTERS, oemChar) != NULL) { + // Invalid Alias character + oemChar = '_'; // Replace illegal characters with underscores + lossyConversion = true; + } + + alias[aliasPos] = (char)oemChar; + aliasPos++; + lfnExt += bytesUsed; + } + if (*lfnExt != '\0') { + // Extension was more than 3 characters long + lossyConversion = true; + } + } + + alias[aliasPos] = '\0'; + if (lossyConversion) { + return aliasPos; + } else { + return 0; + } +} + +bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster) { + size_t entrySize; + uint8_t lfnEntry[DIR_ENTRY_DATA_SIZE]; + int i,j; // Must be signed for use when decrementing in for loop + char *tmpCharPtr; + DIR_ENTRY_POSITION curEntryPos; + bool entryStillValid; + uint8_t aliasCheckSum = 0; + char alias [MAX_ALIAS_LENGTH]; + int aliasLen; + int lfnLen; + + // Remove trailing spaces + for (i = strlen (entry->filename) - 1; (i >= 0) && (entry->filename[i] == ' '); --i) { + entry->filename[i] = '\0'; + } +#if 0 + // Remove leading spaces + for (i = 0; entry->filename[i] == ' '; ++i) ; + if (i > 0) { + memmove (entry->filename, entry->filename + i, strlen (entry->filename + i)); + } +#endif + + // Make sure the filename is not 0 length + if (strnlen (entry->filename, NAME_MAX) < 1) { + return false; + } + + // Make sure the filename is at least a valid LFN + lfnLen = _FAT_directory_lfnLength (entry->filename); + if (lfnLen < 0) { + return false; + } + + // Remove junk in filename + i = strlen (entry->filename); + memset (entry->filename + i, '\0', NAME_MAX - i); + + // Make sure the entry doesn't already exist + if (_FAT_directory_entryExists (partition, entry->filename, dirCluster)) { + return false; + } + + // Clear out alias, so we can generate a new one + memset (entry->entryData, ' ', 11); + + if ( strncmp(entry->filename, ".", NAME_MAX) == 0) { + // "." entry + entry->entryData[0] = '.'; + entrySize = 1; + } else if ( strncmp(entry->filename, "..", NAME_MAX) == 0) { + // ".." entry + entry->entryData[0] = '.'; + entry->entryData[1] = '.'; + entrySize = 1; + } else { + // Normal file name + aliasLen = _FAT_directory_createAlias (alias, entry->filename); + if (aliasLen < 0) { + return false; + } else if (aliasLen == 0) { + // It's a normal short filename + entrySize = 1; + } else { + // It's a long filename with an alias + entrySize = ((lfnLen + LFN_ENTRY_LENGTH - 1) / LFN_ENTRY_LENGTH) + 1; + + // Generate full alias for all cases except when the alias is simply an upper case version of the LFN + // and there isn't already a file with that name + if (strncasecmp (alias, entry->filename, MAX_ALIAS_LENGTH) != 0 || + _FAT_directory_entryExists (partition, alias, dirCluster)) + { + // expand primary part to 8 characters long by padding the end with underscores + i = 0; + j = MAX_ALIAS_PRI_LENGTH; + // Move extension to last 3 characters + while (alias[i] != '.' && alias[i] != '\0') i++; + if (i < j) { + memmove (alias + j, alias + i, aliasLen - i + 1); + // Pad primary component + memset (alias + i, '_', j - i); + } + // Generate numeric tail + for (i = 1; i <= MAX_NUMERIC_TAIL; i++) { + j = i; + tmpCharPtr = alias + MAX_ALIAS_PRI_LENGTH - 1; + while (j > 0) { + *tmpCharPtr = '0' + (j % 10); // ASCII numeric value + tmpCharPtr--; + j /= 10; + } + *tmpCharPtr = '~'; + if (!_FAT_directory_entryExists (partition, alias, dirCluster)) { + break; + } + } + if (i > MAX_NUMERIC_TAIL) { + // Couldn't get a valid alias + return false; + } + } + } + + // Copy alias or short file name into directory entry data + for (i = 0, j = 0; (j < 8) && (alias[i] != '.') && (alias[i] != '\0'); i++, j++) { + entry->entryData[j] = alias[i]; + } + while (j < 8) { + entry->entryData[j] = ' '; + ++ j; + } + if (alias[i] == '.') { + // Copy extension + ++ i; + while ((alias[i] != '\0') && (j < 11)) { + entry->entryData[j] = alias[i]; + ++ i; + ++ j; + } + } + while (j < 11) { + entry->entryData[j] = ' '; + ++ j; + } + + // Generate alias checksum + for (i=0; i < ALIAS_ENTRY_LENGTH; i++) { + // NOTE: The operation is an unsigned char rotate right + aliasCheckSum = ((aliasCheckSum & 1) ? 0x80 : 0) + (aliasCheckSum >> 1) + entry->entryData[i]; + } + } + + // Find or create space for the entry + if (_FAT_directory_findEntryGap (partition, entry, dirCluster, entrySize) == false) { + return false; + } + + // Write out directory entry + curEntryPos = entry->dataStart; + + { + // lfn is only pushed onto the stack here, reducing overall stack usage + ucs2_t lfn[MAX_LFN_LENGTH] = {0}; + _FAT_directory_mbstoucs2 (lfn, entry->filename, MAX_LFN_LENGTH); + + for (entryStillValid = true, i = entrySize; entryStillValid && i > 0; + entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &curEntryPos, false), -- i ) + { + if (i > 1) { + // Long filename entry + lfnEntry[LFN_offset_ordinal] = (i - 1) | ((size_t)i == entrySize ? LFN_END : 0); + for (j = 0; j < 13; j++) { + if (lfn [(i - 2) * 13 + j] == '\0') { + if ((j > 1) && (lfn [(i - 2) * 13 + (j-1)] == '\0')) { + u16_to_u8array (lfnEntry, LFN_offset_table[j], 0xffff); // Padding + } else { + u16_to_u8array (lfnEntry, LFN_offset_table[j], 0x0000); // Terminating null character + } + } else { + u16_to_u8array (lfnEntry, LFN_offset_table[j], lfn [(i - 2) * 13 + j]); + } + } + + lfnEntry[LFN_offset_checkSum] = aliasCheckSum; + lfnEntry[LFN_offset_flag] = ATTRIB_LFN; + lfnEntry[LFN_offset_reserved1] = 0; + u16_to_u8array (lfnEntry, LFN_offset_reserved2, 0); + _FAT_cache_writePartialSector (partition->cache, lfnEntry, _FAT_fat_clusterToSector(partition, curEntryPos.cluster) + curEntryPos.sector, curEntryPos.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + } else { + // Alias & file data + _FAT_cache_writePartialSector (partition->cache, entry->entryData, _FAT_fat_clusterToSector(partition, curEntryPos.cluster) + curEntryPos.sector, curEntryPos.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + } + } + } + + return true; +} + +bool _FAT_directory_chdir (PARTITION* partition, const char* path) { + DIR_ENTRY entry; + + if (!_FAT_directory_entryFromPath (partition, &entry, path, NULL)) { + return false; + } + + if (!(entry.entryData[DIR_ENTRY_attributes] & ATTRIB_DIR)) { + return false; + } + + partition->cwdCluster = _FAT_directory_entryGetCluster (partition, entry.entryData); + + return true; +} + +void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st) { + // Fill in the stat struct + // Some of the values are faked for the sake of compatibility + st->st_dev = _FAT_disc_hostType(partition->disc); // The device is the 32bit ioType value + st->st_ino = (ino_t)(_FAT_directory_entryGetCluster(partition, entry->entryData)); // The file serial number is the start cluster + st->st_mode = (_FAT_directory_isDirectory(entry) ? S_IFDIR : S_IFREG) | + (S_IRUSR | S_IRGRP | S_IROTH) | + (_FAT_directory_isWritable (entry) ? (S_IWUSR | S_IWGRP | S_IWOTH) : 0); // Mode bits based on dirEntry ATTRIB byte + st->st_nlink = 1; // Always one hard link on a FAT file + st->st_uid = 1; // Faked for FAT + st->st_gid = 2; // Faked for FAT + st->st_rdev = st->st_dev; + st->st_size = u8array_to_u32 (entry->entryData, DIR_ENTRY_fileSize); // File size + st->st_atime = _FAT_filetime_to_time_t ( + 0, + u8array_to_u16 (entry->entryData, DIR_ENTRY_aDate) + ); + st->st_spare1 = 0; + st->st_mtime = _FAT_filetime_to_time_t ( + u8array_to_u16 (entry->entryData, DIR_ENTRY_mTime), + u8array_to_u16 (entry->entryData, DIR_ENTRY_mDate) + ); + st->st_spare2 = 0; + st->st_ctime = _FAT_filetime_to_time_t ( + u8array_to_u16 (entry->entryData, DIR_ENTRY_cTime), + u8array_to_u16 (entry->entryData, DIR_ENTRY_cDate) + ); + st->st_spare3 = 0; + st->st_blksize = partition->bytesPerSector; // Prefered file I/O block size + st->st_blocks = (st->st_size + partition->bytesPerSector - 1) / partition->bytesPerSector; // File size in blocks + st->st_spare4[0] = 0; + st->st_spare4[1] = 0; +} diff --git a/wii/libogc/libfat/directory.h b/wii/libogc/libfat/directory.h new file mode 100644 index 0000000000..9f7af8ed35 --- /dev/null +++ b/wii/libogc/libfat/directory.h @@ -0,0 +1,181 @@ +/* + directory.h + Reading, writing and manipulation of the directory structure on + a FAT partition + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _DIRECTORY_H +#define _DIRECTORY_H + +#include +#include + +#include "common.h" +#include "partition.h" + +#define DIR_ENTRY_DATA_SIZE 0x20 +#define MAX_LFN_LENGTH 256 +#define MAX_ALIAS_LENGTH 13 +#define LFN_ENTRY_LENGTH 13 +#define ALIAS_ENTRY_LENGTH 11 +#define MAX_ALIAS_EXT_LENGTH 3 +#define MAX_ALIAS_PRI_LENGTH 8 +#define MAX_NUMERIC_TAIL 999999 +#define FAT16_ROOT_DIR_CLUSTER 0 + +#define DIR_SEPARATOR '/' + +// File attributes +#define ATTRIB_ARCH 0x20 // Archive +#define ATTRIB_DIR 0x10 // Directory +#define ATTRIB_LFN 0x0F // Long file name +#define ATTRIB_VOL 0x08 // Volume +#define ATTRIB_SYS 0x04 // System +#define ATTRIB_HID 0x02 // Hidden +#define ATTRIB_RO 0x01 // Read only + +#define CASE_LOWER_EXT 0x10 // WinNT lowercase extension +#define CASE_LOWER_BASE 0x08 // WinNT lowercase basename + +typedef enum {FT_DIRECTORY, FT_FILE} FILE_TYPE; + +typedef struct { + uint32_t cluster; + sec_t sector; + int32_t offset; +} DIR_ENTRY_POSITION; + +typedef struct { + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + DIR_ENTRY_POSITION dataStart; // Points to the start of the LFN entries of a file, or the alias for no LFN + DIR_ENTRY_POSITION dataEnd; // Always points to the file/directory's alias entry + char filename[NAME_MAX]; +} DIR_ENTRY; + +// Directory entry offsets +enum DIR_ENTRY_offset { + DIR_ENTRY_name = 0x00, + DIR_ENTRY_extension = 0x08, + DIR_ENTRY_attributes = 0x0B, + DIR_ENTRY_caseInfo = 0x0C, + DIR_ENTRY_cTime_ms = 0x0D, + DIR_ENTRY_cTime = 0x0E, + DIR_ENTRY_cDate = 0x10, + DIR_ENTRY_aDate = 0x12, + DIR_ENTRY_clusterHigh = 0x14, + DIR_ENTRY_mTime = 0x16, + DIR_ENTRY_mDate = 0x18, + DIR_ENTRY_cluster = 0x1A, + DIR_ENTRY_fileSize = 0x1C +}; + +/* +Returns true if the file specified by entry is a directory +*/ +static inline bool _FAT_directory_isDirectory (DIR_ENTRY* entry) { + return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) != 0); +} + +static inline bool _FAT_directory_isWritable (DIR_ENTRY* entry) { + return ((entry->entryData[DIR_ENTRY_attributes] & ATTRIB_RO) == 0); +} + +static inline bool _FAT_directory_isDot (DIR_ENTRY* entry) { + return ((entry->filename[0] == '.') && ((entry->filename[1] == '\0') || + ((entry->filename[1] == '.') && entry->filename[2] == '\0'))); +} + +/* +Reads the first directory entry from the directory starting at dirCluster +Places result in entry +entry will be destroyed even if no directory entry is found +Returns true on success, false on failure +*/ +bool _FAT_directory_getFirstEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster); + +/* +Reads the next directory entry after the one already pointed to by entry +Places result in entry +entry will be destroyed even if no directory entry is found +Returns true on success, false on failure +*/ +bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry); + +/* +Gets the directory entry corrsponding to the supplied path +entry will be destroyed even if no directory entry is found +pathEnd specifies the end of the path string, for cutting strings short if needed + specify NULL to use the full length of path + pathEnd is only a suggestion, and the path string will be searched up until the next PATH_SEPARATOR + after pathEND. +Returns true on success, false on failure +*/ +bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd); + +/* +Changes the current directory to the one specified by path +Returns true on success, false on failure +*/ +bool _FAT_directory_chdir (PARTITION* partition, const char* path); + +/* +Removes the directory entry specified by entry +Assumes that entry is valid +Returns true on success, false on failure +*/ +bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry); + +/* +Add a directory entry to the directory specified by dirCluster +The fileData, dataStart and dataEnd elements of the DIR_ENTRY struct are +updated with the new directory entry position and alias. +Returns true on success, false on failure +*/ +bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t dirCluster); + +/* +Get the start cluster of a file from it's entry data +*/ +uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData); + +/* +Fill in the file name and entry data of DIR_ENTRY* entry. +Assumes that the entry's dataStart and dataEnd are correct +Returns true on success, false on failure +*/ +bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry); + +/* +Fill in a stat struct based on a file entry +*/ +void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st); + +/* +Get volume label +*/ +bool _FAT_directory_getVolumeLabel (PARTITION* partition, char *label); + +#endif // _DIRECTORY_H diff --git a/wii/libogc/libfat/disc.c b/wii/libogc/libfat/disc.c new file mode 100644 index 0000000000..d27da2a099 --- /dev/null +++ b/wii/libogc/libfat/disc.c @@ -0,0 +1,112 @@ +/* + disc.c + Interface to the low level disc functions. Used by the higher level + file system code. + + Copyright (c) 2008 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "disc.h" + +/* +The list of interfaces consists of a series of name/interface pairs. +The interface is returned via a simple function. This allows for +platforms where the interface has to be "assembled" before it can +be used, like DLDI on the NDS. For cases where a simple struct +is available, wrapper functions are used. +The list is terminated by a NULL/NULL entry. +*/ + +/* ====================== Wii ====================== */ +#if defined (__wii__) +#include +#include +#include + +static const DISC_INTERFACE* get_io_wiisd (void) { + return &__io_wiisd; +} +static const DISC_INTERFACE* get_io_usbstorage (void) { + return &__io_usbstorage; +} + +static const DISC_INTERFACE* get_io_gcsda (void) { + return &__io_gcsda; +} + +static const DISC_INTERFACE* get_io_gcsdb (void) { + return &__io_gcsdb; +} + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"sd", get_io_wiisd}, + {"usb", get_io_usbstorage}, + {"carda", get_io_gcsda}, + {"cardb", get_io_gcsdb}, + {NULL, NULL} +}; + +/* ==================== Gamecube ==================== */ +#elif defined (__gamecube__) +#include + +static const DISC_INTERFACE* get_io_gcsda (void) { + return &__io_gcsda; +} +static const DISC_INTERFACE* get_io_gcsdb (void) { + return &__io_gcsdb; +} + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"carda", get_io_gcsda}, + {"cardb", get_io_gcsdb}, + {NULL, NULL} +}; + +/* ====================== NDS ====================== */ +#elif defined (NDS) +#include +#include + +static const DISC_INTERFACE* get_io_dsisd (void) { + return isDSiMode() ? &__io_dsisd : NULL; +} + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"sd", get_io_dsisd}, + {"fat", dldiGetInternal}, + {NULL, NULL} +}; + +/* ====================== GBA ====================== */ +#elif defined (GBA) +#include + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"fat", discGetInterface}, + {NULL, NULL} +}; + +#endif + diff --git a/wii/libogc/libfat/disc.h b/wii/libogc/libfat/disc.h new file mode 100644 index 0000000000..5c955f90ae --- /dev/null +++ b/wii/libogc/libfat/disc.h @@ -0,0 +1,110 @@ +/* + disc.h + Interface to the low level disc functions. Used by the higher level + file system code. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef _DISC_H +#define _DISC_H + +#include "common.h" + +/* +A list of all default devices to try at startup, +terminated by a {NULL,NULL} entry. +*/ +typedef struct { + const char* name; + const DISC_INTERFACE* (*getInterface)(void); +} INTERFACE_ID; +extern const INTERFACE_ID _FAT_disc_interfaces[]; + +/* +Check if a disc is inserted +Return true if a disc is inserted and ready, false otherwise +*/ +static inline bool _FAT_disc_isInserted (const DISC_INTERFACE* disc) { + return disc->isInserted(); +} + +/* +Read numSectors sectors from a disc, starting at sector. +numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined, +else it is at least 1 +sector is 0 or greater +buffer is a pointer to the memory to fill +*/ +static inline bool _FAT_disc_readSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, void* buffer) { + return disc->readSectors (sector, numSectors, buffer); +} + +/* +Write numSectors sectors to a disc, starting at sector. +numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined, +else it is at least 1 +sector is 0 or greater +buffer is a pointer to the memory to read from +*/ +static inline bool _FAT_disc_writeSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, const void* buffer) { + return disc->writeSectors (sector, numSectors, buffer); +} + +/* +Reset the card back to a ready state +*/ +static inline bool _FAT_disc_clearStatus (const DISC_INTERFACE* disc) { + return disc->clearStatus(); +} + +/* +Initialise the disc to a state ready for data reading or writing +*/ +static inline bool _FAT_disc_startup (const DISC_INTERFACE* disc) { + return disc->startup(); +} + +/* +Put the disc in a state ready for power down. +Complete any pending writes and disable the disc if necessary +*/ +static inline bool _FAT_disc_shutdown (const DISC_INTERFACE* disc) { + return disc->shutdown(); +} + +/* +Return a 32 bit value unique to each type of interface +*/ +static inline uint32_t _FAT_disc_hostType (const DISC_INTERFACE* disc) { + return disc->ioType; +} + +/* +Return a 32 bit value that specifies the capabilities of the disc +*/ +static inline uint32_t _FAT_disc_features (const DISC_INTERFACE* disc) { + return disc->features; +} + +#endif // _DISC_H diff --git a/wii/libogc/libfat/fatdir.c b/wii/libogc/libfat/fatdir.c new file mode 100644 index 0000000000..1ef5aec6d5 --- /dev/null +++ b/wii/libogc/libfat/fatdir.c @@ -0,0 +1,615 @@ +/* + fatdir.c + + Functions used by the newlib disc stubs to interface with + this library + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include + +#include "fatdir.h" + +#include "cache.h" +#include "file_allocation_table.h" +#include "partition.h" +#include "directory.h" +#include "bit_ops.h" +#include "filetime.h" +#include "lock.h" + + +int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st) { + PARTITION* partition = NULL; + DIR_ENTRY dirEntry; + + // Get the partition this file is on + partition = _FAT_partition_getPartitionFromPath (path); + if (partition == NULL) { + r->_errno = ENODEV; + return -1; + } + + // Move the path pointer to the start of the actual path + if (strchr (path, ':') != NULL) { + path = strchr (path, ':') + 1; + } + if (strchr (path, ':') != NULL) { + r->_errno = EINVAL; + return -1; + } + + _FAT_lock(&partition->lock); + + // Search for the file on the disc + if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOENT; + return -1; + } + + // Fill in the stat struct + _FAT_directory_entryStat (partition, &dirEntry, st); + + _FAT_unlock(&partition->lock); + return 0; +} + +int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink) { + r->_errno = ENOTSUP; + return -1; +} + +int _FAT_unlink_r (struct _reent *r, const char *path) { + PARTITION* partition = NULL; + DIR_ENTRY dirEntry; + DIR_ENTRY dirContents; + uint32_t cluster; + bool nextEntry; + bool errorOccured = false; + + // Get the partition this directory is on + partition = _FAT_partition_getPartitionFromPath (path); + if (partition == NULL) { + r->_errno = ENODEV; + return -1; + } + + // Make sure we aren't trying to write to a read-only disc + if (partition->readOnly) { + r->_errno = EROFS; + return -1; + } + + // Move the path pointer to the start of the actual path + if (strchr (path, ':') != NULL) { + path = strchr (path, ':') + 1; + } + if (strchr (path, ':') != NULL) { + r->_errno = EINVAL; + return -1; + } + + _FAT_lock(&partition->lock); + + // Search for the file on the disc + if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, NULL)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOENT; + return -1; + } + + cluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData); + + + // If this is a directory, make sure it is empty + if (_FAT_directory_isDirectory (&dirEntry)) { + nextEntry = _FAT_directory_getFirstEntry (partition, &dirContents, cluster); + + while (nextEntry) { + if (!_FAT_directory_isDot (&dirContents)) { + // The directory had something in it that isn't a reference to itself or it's parent + _FAT_unlock(&partition->lock); + r->_errno = ENOTEMPTY; + return -1; + } + nextEntry = _FAT_directory_getNextEntry (partition, &dirContents); + } + } + + if (_FAT_fat_isValidCluster(partition, cluster)) { + // Remove the cluster chain for this file + if (!_FAT_fat_clearLinks (partition, cluster)) { + r->_errno = EIO; + errorOccured = true; + } + } + + // Remove the directory entry for this file + if (!_FAT_directory_removeEntry (partition, &dirEntry)) { + r->_errno = EIO; + errorOccured = true; + } + + // Flush any sectors in the disc cache + if (!_FAT_cache_flush(partition->cache)) { + r->_errno = EIO; + errorOccured = true; + } + + _FAT_unlock(&partition->lock); + if (errorOccured) { + return -1; + } else { + return 0; + } +} + +int _FAT_chdir_r (struct _reent *r, const char *path) { + PARTITION* partition = NULL; + + // Get the partition this directory is on + partition = _FAT_partition_getPartitionFromPath (path); + if (partition == NULL) { + r->_errno = ENODEV; + return -1; + } + + // Move the path pointer to the start of the actual path + if (strchr (path, ':') != NULL) { + path = strchr (path, ':') + 1; + } + if (strchr (path, ':') != NULL) { + r->_errno = EINVAL; + return -1; + } + + _FAT_lock(&partition->lock); + + // Try changing directory + if (_FAT_directory_chdir (partition, path)) { + // Successful + _FAT_unlock(&partition->lock); + return 0; + } else { + // Failed + _FAT_unlock(&partition->lock); + r->_errno = ENOTDIR; + return -1; + } +} + +int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName) { + PARTITION* partition = NULL; + DIR_ENTRY oldDirEntry; + DIR_ENTRY newDirEntry; + const char *pathEnd; + uint32_t dirCluster; + + // Get the partition this directory is on + partition = _FAT_partition_getPartitionFromPath (oldName); + if (partition == NULL) { + r->_errno = ENODEV; + return -1; + } + + _FAT_lock(&partition->lock); + + // Make sure the same partition is used for the old and new names + if (partition != _FAT_partition_getPartitionFromPath (newName)) { + _FAT_unlock(&partition->lock); + r->_errno = EXDEV; + return -1; + } + + // Make sure we aren't trying to write to a read-only disc + if (partition->readOnly) { + _FAT_unlock(&partition->lock); + r->_errno = EROFS; + return -1; + } + + // Move the path pointer to the start of the actual path + if (strchr (oldName, ':') != NULL) { + oldName = strchr (oldName, ':') + 1; + } + if (strchr (oldName, ':') != NULL) { + _FAT_unlock(&partition->lock); + r->_errno = EINVAL; + return -1; + } + if (strchr (newName, ':') != NULL) { + newName = strchr (newName, ':') + 1; + } + if (strchr (newName, ':') != NULL) { + _FAT_unlock(&partition->lock); + r->_errno = EINVAL; + return -1; + } + + // Search for the file on the disc + if (!_FAT_directory_entryFromPath (partition, &oldDirEntry, oldName, NULL)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOENT; + return -1; + } + + // Make sure there is no existing file / directory with the new name + if (_FAT_directory_entryFromPath (partition, &newDirEntry, newName, NULL)) { + _FAT_unlock(&partition->lock); + r->_errno = EEXIST; + return -1; + } + + // Create the new file entry + // Get the directory it has to go in + pathEnd = strrchr (newName, DIR_SEPARATOR); + if (pathEnd == NULL) { + // No path was specified + dirCluster = partition->cwdCluster; + pathEnd = newName; + } else { + // Path was specified -- get the right dirCluster + // Recycling newDirEntry, since it needs to be recreated anyway + if (!_FAT_directory_entryFromPath (partition, &newDirEntry, newName, pathEnd) || + !_FAT_directory_isDirectory(&newDirEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOTDIR; + return -1; + } + dirCluster = _FAT_directory_entryGetCluster (partition, newDirEntry.entryData); + // Move the pathEnd past the last DIR_SEPARATOR + pathEnd += 1; + } + + // Copy the entry data + memcpy (&newDirEntry, &oldDirEntry, sizeof(DIR_ENTRY)); + + // Set the new name + strncpy (newDirEntry.filename, pathEnd, NAME_MAX - 1); + + // Write the new entry + if (!_FAT_directory_addEntry (partition, &newDirEntry, dirCluster)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOSPC; + return -1; + } + + // Remove the old entry + if (!_FAT_directory_removeEntry (partition, &oldDirEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = EIO; + return -1; + } + + // Flush any sectors in the disc cache + if (!_FAT_cache_flush (partition->cache)) { + _FAT_unlock(&partition->lock); + r->_errno = EIO; + return -1; + } + + _FAT_unlock(&partition->lock); + return 0; +} + +int _FAT_mkdir_r (struct _reent *r, const char *path, int mode) { + PARTITION* partition = NULL; + bool fileExists; + DIR_ENTRY dirEntry; + const char* pathEnd; + uint32_t parentCluster, dirCluster; + uint8_t newEntryData[DIR_ENTRY_DATA_SIZE]; + + partition = _FAT_partition_getPartitionFromPath (path); + if (partition == NULL) { + r->_errno = ENODEV; + return -1; + } + + // Move the path pointer to the start of the actual path + if (strchr (path, ':') != NULL) { + path = strchr (path, ':') + 1; + } + if (strchr (path, ':') != NULL) { + r->_errno = EINVAL; + return -1; + } + + _FAT_lock(&partition->lock); + + // Search for the file/directory on the disc + fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL); + + // Make sure it doesn't exist + if (fileExists) { + _FAT_unlock(&partition->lock); + r->_errno = EEXIST; + return -1; + } + + if (partition->readOnly) { + // We can't write to a read-only partition + _FAT_unlock(&partition->lock); + r->_errno = EROFS; + return -1; + } + + // Get the directory it has to go in + pathEnd = strrchr (path, DIR_SEPARATOR); + if (pathEnd == NULL) { + // No path was specified + parentCluster = partition->cwdCluster; + pathEnd = path; + } else { + // Path was specified -- get the right parentCluster + // Recycling dirEntry, since it needs to be recreated anyway + if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, pathEnd) || + !_FAT_directory_isDirectory(&dirEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOTDIR; + return -1; + } + parentCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData); + // Move the pathEnd past the last DIR_SEPARATOR + pathEnd += 1; + } + // Create the entry data + strncpy (dirEntry.filename, pathEnd, NAME_MAX - 1); + memset (dirEntry.entryData, 0, DIR_ENTRY_DATA_SIZE); + + // Set the creation time and date + dirEntry.entryData[DIR_ENTRY_cTime_ms] = 0; + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cTime, _FAT_filetime_getTimeFromRTC()); + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cDate, _FAT_filetime_getDateFromRTC()); + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_mTime, _FAT_filetime_getTimeFromRTC()); + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_mDate, _FAT_filetime_getDateFromRTC()); + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_aDate, _FAT_filetime_getDateFromRTC()); + + // Set the directory attribute + dirEntry.entryData[DIR_ENTRY_attributes] = ATTRIB_DIR; + + // Get a cluster for the new directory + dirCluster = _FAT_fat_linkFreeClusterCleared (partition, CLUSTER_FREE); + if (!_FAT_fat_isValidCluster(partition, dirCluster)) { + // No space left on disc for the cluster + _FAT_unlock(&partition->lock); + r->_errno = ENOSPC; + return -1; + } + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cluster, dirCluster); + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_clusterHigh, dirCluster >> 16); + + // Write the new directory's entry to it's parent + if (!_FAT_directory_addEntry (partition, &dirEntry, parentCluster)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOSPC; + return -1; + } + + // Create the dot entry within the directory + memset (newEntryData, 0, DIR_ENTRY_DATA_SIZE); + memset (newEntryData, ' ', 11); + newEntryData[DIR_ENTRY_name] = '.'; + newEntryData[DIR_ENTRY_attributes] = ATTRIB_DIR; + u16_to_u8array (newEntryData, DIR_ENTRY_cluster, dirCluster); + u16_to_u8array (newEntryData, DIR_ENTRY_clusterHigh, dirCluster >> 16); + + // Write it to the directory, erasing that sector in the process + _FAT_cache_eraseWritePartialSector ( partition->cache, newEntryData, + _FAT_fat_clusterToSector (partition, dirCluster), 0, DIR_ENTRY_DATA_SIZE); + + + // Create the double dot entry within the directory + + // if ParentDir == Rootdir then ".."" always link to Cluster 0 + if(parentCluster == partition->rootDirCluster) + parentCluster = FAT16_ROOT_DIR_CLUSTER; + + newEntryData[DIR_ENTRY_name + 1] = '.'; + u16_to_u8array (newEntryData, DIR_ENTRY_cluster, parentCluster); + u16_to_u8array (newEntryData, DIR_ENTRY_clusterHigh, parentCluster >> 16); + + // Write it to the directory + _FAT_cache_writePartialSector ( partition->cache, newEntryData, + _FAT_fat_clusterToSector (partition, dirCluster), DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + + // Flush any sectors in the disc cache + if (!_FAT_cache_flush(partition->cache)) { + _FAT_unlock(&partition->lock); + r->_errno = EIO; + return -1; + } + + _FAT_unlock(&partition->lock); + return 0; +} + +int _FAT_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf) +{ + PARTITION* partition = NULL; + unsigned int freeClusterCount; + + // Get the partition of the requested path + partition = _FAT_partition_getPartitionFromPath (path); + if (partition == NULL) { + r->_errno = ENODEV; + return -1; + } + + _FAT_lock(&partition->lock); + + if(partition->filesysType == FS_FAT32) { + // Sync FSinfo block + _FAT_partition_readFSinfo(partition); + freeClusterCount = partition->fat.numberFreeCluster; + } else { + freeClusterCount = _FAT_fat_freeClusterCount (partition); + } + + // FAT clusters = POSIX blocks + buf->f_bsize = partition->bytesPerCluster; // File system block size. + buf->f_frsize = partition->bytesPerCluster; // Fundamental file system block size. + + buf->f_blocks = partition->fat.lastCluster - CLUSTER_FIRST + 1; // Total number of blocks on file system in units of f_frsize. + buf->f_bfree = freeClusterCount; // Total number of free blocks. + buf->f_bavail = freeClusterCount; // Number of free blocks available to non-privileged process. + + // Treat requests for info on inodes as clusters + buf->f_files = partition->fat.lastCluster - CLUSTER_FIRST + 1; // Total number of file serial numbers. + buf->f_ffree = freeClusterCount; // Total number of free file serial numbers. + buf->f_favail = freeClusterCount; // Number of file serial numbers available to non-privileged process. + + // File system ID. 32bit ioType value + buf->f_fsid = _FAT_disc_hostType(partition->disc); + + // Bit mask of f_flag values. + buf->f_flag = ST_NOSUID /* No support for ST_ISUID and ST_ISGID file mode bits */ + | (partition->readOnly ? ST_RDONLY /* Read only file system */ : 0 ) ; + // Maximum filename length. + buf->f_namemax = NAME_MAX; + + _FAT_unlock(&partition->lock); + return 0; +} + +DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path) { + DIR_ENTRY dirEntry; + DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct); + bool fileExists; + + state->partition = _FAT_partition_getPartitionFromPath (path); + if (state->partition == NULL) { + r->_errno = ENODEV; + return NULL; + } + + // Move the path pointer to the start of the actual path + if (strchr (path, ':') != NULL) { + path = strchr (path, ':') + 1; + } + if (strchr (path, ':') != NULL) { + r->_errno = EINVAL; + return NULL; + } + + _FAT_lock(&state->partition->lock); + + // Get the start cluster of the directory + fileExists = _FAT_directory_entryFromPath (state->partition, &dirEntry, path, NULL); + + if (!fileExists) { + _FAT_unlock(&state->partition->lock); + r->_errno = ENOENT; + return NULL; + } + + // Make sure it is a directory + if (! _FAT_directory_isDirectory (&dirEntry)) { + _FAT_unlock(&state->partition->lock); + r->_errno = ENOTDIR; + return NULL; + } + + // Save the start cluster for use when resetting the directory data + state->startCluster = _FAT_directory_entryGetCluster (state->partition, dirEntry.entryData); + + // Get the first entry for use with a call to dirnext + state->validEntry = + _FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster); + + // We are now using this entry + state->inUse = true; + _FAT_unlock(&state->partition->lock); + return (DIR_ITER*) state; +} + +int _FAT_dirreset_r (struct _reent *r, DIR_ITER *dirState) { + DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct); + + _FAT_lock(&state->partition->lock); + + // Make sure we are still using this entry + if (!state->inUse) { + _FAT_unlock(&state->partition->lock); + r->_errno = EBADF; + return -1; + } + + // Get the first entry for use with a call to dirnext + state->validEntry = + _FAT_directory_getFirstEntry (state->partition, &(state->currentEntry), state->startCluster); + + _FAT_unlock(&state->partition->lock); + return 0; +} + +int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) { + DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct); + + _FAT_lock(&state->partition->lock); + + // Make sure we are still using this entry + if (!state->inUse) { + _FAT_unlock(&state->partition->lock); + r->_errno = EBADF; + return -1; + } + + // Make sure there is another file to report on + if (! state->validEntry) { + _FAT_unlock(&state->partition->lock); + return -1; + } + + // Get the filename + strncpy (filename, state->currentEntry.filename, NAME_MAX); + // Get the stats, if requested + if (filestat != NULL) { + _FAT_directory_entryStat (state->partition, &(state->currentEntry), filestat); + } + + // Look for the next entry for use next time + state->validEntry = + _FAT_directory_getNextEntry (state->partition, &(state->currentEntry)); + + _FAT_unlock(&state->partition->lock); + return 0; +} + +int _FAT_dirclose_r (struct _reent *r, DIR_ITER *dirState) { + DIR_STATE_STRUCT* state = (DIR_STATE_STRUCT*) (dirState->dirStruct); + + // We are no longer using this entry + _FAT_lock(&state->partition->lock); + state->inUse = false; + _FAT_unlock(&state->partition->lock); + + return 0; +} diff --git a/wii/libogc/libfat/fatdir.h b/wii/libogc/libfat/fatdir.h new file mode 100644 index 0000000000..426dd30bcd --- /dev/null +++ b/wii/libogc/libfat/fatdir.h @@ -0,0 +1,73 @@ +/* + fatdir.h + + Functions used by the newlib disc stubs to interface with + this library + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef _FATDIR_H +#define _FATDIR_H + +#include +#include +#include +#include +#include "common.h" +#include "directory.h" + +typedef struct { + PARTITION* partition; + DIR_ENTRY currentEntry; + uint32_t startCluster; + bool inUse; + bool validEntry; +} DIR_STATE_STRUCT; + +extern int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st); + +extern int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink); + +extern int _FAT_unlink_r (struct _reent *r, const char *name); + +extern int _FAT_chdir_r (struct _reent *r, const char *name); + +extern int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName); + +extern int _FAT_mkdir_r (struct _reent *r, const char *path, int mode); + +extern int _FAT_statvfs_r (struct _reent *r, const char *path, struct statvfs *buf); + +/* +Directory iterator functions +*/ +extern DIR_ITER* _FAT_diropen_r(struct _reent *r, DIR_ITER *dirState, const char *path); +extern int _FAT_dirreset_r (struct _reent *r, DIR_ITER *dirState); +extern int _FAT_dirnext_r (struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat); +extern int _FAT_dirclose_r (struct _reent *r, DIR_ITER *dirState); + + +#endif // _FATDIR_H diff --git a/wii/libogc/libfat/fatfile.c b/wii/libogc/libfat/fatfile.c new file mode 100644 index 0000000000..06c2ca7c1e --- /dev/null +++ b/wii/libogc/libfat/fatfile.c @@ -0,0 +1,1213 @@ +/* + fatfile.c + + Functions used by the newlib disc stubs to interface with + this library + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 2009-10-23 oggzee: fixes for cluster aligned file size (write, truncate, seek) +*/ + + +#include "fatfile.h" + +#include +#include +#include +#include +#include + +#include "cache.h" +#include "file_allocation_table.h" +#include "bit_ops.h" +#include "filetime.h" +#include "lock.h" + +bool _FAT_findEntry(const char *path, DIR_ENTRY *dirEntry) { + PARTITION *partition = _FAT_partition_getPartitionFromPath(path); + + // Check Partition + if( !partition ) + return false; + + // Move the path pointer to the start of the actual path + if (strchr (path, ':') != NULL) { + path = strchr (path, ':') + 1; + } + if (strchr (path, ':') != NULL) { + return false; + } + + // Search for the file on the disc + return _FAT_directory_entryFromPath (partition, dirEntry, path, NULL); + +} + +int FAT_getAttr(const char *file) { + DIR_ENTRY dirEntry; + if (!_FAT_findEntry(file,&dirEntry)) return -1; + + return dirEntry.entryData[DIR_ENTRY_attributes]; +} + +int FAT_setAttr(const char *file, uint8_t attr) { + + // Defines... + DIR_ENTRY_POSITION entryEnd; + PARTITION *partition = NULL; + DIR_ENTRY dirEntry; + + // Get Partition + partition = _FAT_partition_getPartitionFromPath( file ); + + // Check Partition + if( !partition ) + return -1; + + // Move the path pointer to the start of the actual path + if (strchr (file, ':') != NULL) + file = strchr (file, ':') + 1; + if (strchr (file, ':') != NULL) + return -1; + + // Get DIR_ENTRY + if( !_FAT_directory_entryFromPath (partition, &dirEntry, file, NULL) ) + return -1; + + // Get Entry-End + entryEnd = dirEntry.dataEnd; + + // Lock Partition + _FAT_lock(&partition->lock); + + + // Write Data + _FAT_cache_writePartialSector ( + partition->cache // Cache to write + , &attr // Value to be written + , _FAT_fat_clusterToSector( partition , entryEnd.cluster ) + entryEnd.sector // cluster + , entryEnd.offset * DIR_ENTRY_DATA_SIZE + DIR_ENTRY_attributes // offset + , 1 // Size in bytes + ); + + // Flush any sectors in the disc cache + if ( !_FAT_cache_flush( partition->cache ) ) { + _FAT_unlock(&partition->lock); // Unlock Partition + return -1; + } + + // Unlock Partition + _FAT_unlock(&partition->lock); + + return 0; +} + + +int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) { + PARTITION* partition = NULL; + bool fileExists; + DIR_ENTRY dirEntry; + const char* pathEnd; + uint32_t dirCluster; + FILE_STRUCT* file = (FILE_STRUCT*) fileStruct; + partition = _FAT_partition_getPartitionFromPath (path); + + if (partition == NULL) { + r->_errno = ENODEV; + return -1; + } + + // Move the path pointer to the start of the actual path + if (strchr (path, ':') != NULL) { + path = strchr (path, ':') + 1; + } + if (strchr (path, ':') != NULL) { + r->_errno = EINVAL; + return -1; + } + + // Determine which mode the file is openned for + if ((flags & 0x03) == O_RDONLY) { + // Open the file for read-only access + file->read = true; + file->write = false; + file->append = false; + } else if ((flags & 0x03) == O_WRONLY) { + // Open file for write only access + file->read = false; + file->write = true; + file->append = false; + } else if ((flags & 0x03) == O_RDWR) { + // Open file for read/write access + file->read = true; + file->write = true; + file->append = false; + } else { + r->_errno = EACCES; + return -1; + } + + // Make sure we aren't trying to write to a read-only disc + if (file->write && partition->readOnly) { + r->_errno = EROFS; + return -1; + } + + // Search for the file on the disc + _FAT_lock(&partition->lock); + fileExists = _FAT_directory_entryFromPath (partition, &dirEntry, path, NULL); + + // The file shouldn't exist if we are trying to create it + if ((flags & O_CREAT) && (flags & O_EXCL) && fileExists) { + _FAT_unlock(&partition->lock); + r->_errno = EEXIST; + return -1; + } + + // It should not be a directory if we're openning a file, + if (fileExists && _FAT_directory_isDirectory(&dirEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = EISDIR; + return -1; + } + + // We haven't modified the file yet + file->modified = false; + + // If the file doesn't exist, create it if we're allowed to + if (!fileExists) { + if (flags & O_CREAT) { + if (partition->readOnly) { + // We can't write to a read-only partition + _FAT_unlock(&partition->lock); + r->_errno = EROFS; + return -1; + } + // Create the file + // Get the directory it has to go in + pathEnd = strrchr (path, DIR_SEPARATOR); + if (pathEnd == NULL) { + // No path was specified + dirCluster = partition->cwdCluster; + pathEnd = path; + } else { + // Path was specified -- get the right dirCluster + // Recycling dirEntry, since it needs to be recreated anyway + if (!_FAT_directory_entryFromPath (partition, &dirEntry, path, pathEnd) || + !_FAT_directory_isDirectory(&dirEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOTDIR; + return -1; + } + dirCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData); + // Move the pathEnd past the last DIR_SEPARATOR + pathEnd += 1; + } + // Create the entry data + strncpy (dirEntry.filename, pathEnd, NAME_MAX - 1); + memset (dirEntry.entryData, 0, DIR_ENTRY_DATA_SIZE); + + // Set the creation time and date + dirEntry.entryData[DIR_ENTRY_cTime_ms] = 0; + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cTime, _FAT_filetime_getTimeFromRTC()); + u16_to_u8array (dirEntry.entryData, DIR_ENTRY_cDate, _FAT_filetime_getDateFromRTC()); + + if (!_FAT_directory_addEntry (partition, &dirEntry, dirCluster)) { + _FAT_unlock(&partition->lock); + r->_errno = ENOSPC; + return -1; + } + + // File entry is modified + file->modified = true; + } else { + // file doesn't exist, and we aren't creating it + _FAT_unlock(&partition->lock); + r->_errno = ENOENT; + return -1; + } + } + + file->filesize = u8array_to_u32 (dirEntry.entryData, DIR_ENTRY_fileSize); + + /* Allow LARGEFILEs with undefined results + // Make sure that the file size can fit in the available space + if (!(flags & O_LARGEFILE) && (file->filesize >= (1<<31))) { + r->_errno = EFBIG; + return -1; + } + */ + + // Make sure we aren't trying to write to a read-only file + if (file->write && !_FAT_directory_isWritable(&dirEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = EROFS; + return -1; + } + + // Associate this file with a particular partition + file->partition = partition; + + file->startCluster = _FAT_directory_entryGetCluster (partition, dirEntry.entryData); + + // Truncate the file if requested + if ((flags & O_TRUNC) && file->write && (file->startCluster != 0)) { + _FAT_fat_clearLinks (partition, file->startCluster); + file->startCluster = CLUSTER_FREE; + file->filesize = 0; + // File is modified since we just cut it all off + file->modified = true; + } + + // Remember the position of this file's directory entry + file->dirEntryStart = dirEntry.dataStart; // Points to the start of the LFN entries of a file, or the alias for no LFN + file->dirEntryEnd = dirEntry.dataEnd; + + // Reset read/write pointer + file->currentPosition = 0; + file->rwPosition.cluster = file->startCluster; + file->rwPosition.sector = 0; + file->rwPosition.byte = 0; + + if (flags & O_APPEND) { + file->append = true; + + // Set append pointer to the end of the file + file->appendPosition.cluster = _FAT_fat_lastCluster (partition, file->startCluster); + file->appendPosition.sector = (file->filesize % partition->bytesPerCluster) / partition->bytesPerSector; + file->appendPosition.byte = file->filesize % partition->bytesPerSector; + + // Check if the end of the file is on the end of a cluster + if ( (file->filesize > 0) && ((file->filesize % partition->bytesPerCluster)==0) ){ + // Set flag to allocate a new cluster + file->appendPosition.sector = partition->sectorsPerCluster; + file->appendPosition.byte = 0; + } + } else { + file->append = false; + // Use something sane for the append pointer, so the whole file struct contains known values + file->appendPosition = file->rwPosition; + } + + file->inUse = true; + + // Insert this file into the double-linked list of open files + partition->openFileCount += 1; + if (partition->firstOpenFile) { + file->nextOpenFile = partition->firstOpenFile; + partition->firstOpenFile->prevOpenFile = file; + } else { + file->nextOpenFile = NULL; + } + file->prevOpenFile = NULL; + partition->firstOpenFile = file; + + _FAT_unlock(&partition->lock); + + return (int) file; +} + +/* +Synchronizes the file data to disc. +Does no locking of its own -- lock the partition before calling. +Returns 0 on success, an error code on failure. +*/ +int _FAT_syncToDisc (FILE_STRUCT* file) { + uint8_t dirEntryData[DIR_ENTRY_DATA_SIZE]; + + if (!file || !file->inUse) { + return EBADF; + } + + if (file->write && file->modified) { + // Load the old entry + _FAT_cache_readPartialSector (file->partition->cache, dirEntryData, + _FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector, + file->dirEntryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + + // Write new data to the directory entry + // File size + u32_to_u8array (dirEntryData, DIR_ENTRY_fileSize, file->filesize); + + // Start cluster + u16_to_u8array (dirEntryData, DIR_ENTRY_cluster, file->startCluster); + u16_to_u8array (dirEntryData, DIR_ENTRY_clusterHigh, file->startCluster >> 16); + + // Modification time and date + u16_to_u8array (dirEntryData, DIR_ENTRY_mTime, _FAT_filetime_getTimeFromRTC()); + u16_to_u8array (dirEntryData, DIR_ENTRY_mDate, _FAT_filetime_getDateFromRTC()); + + // Access date + u16_to_u8array (dirEntryData, DIR_ENTRY_aDate, _FAT_filetime_getDateFromRTC()); + + // Set archive attribute + dirEntryData[DIR_ENTRY_attributes] |= ATTRIB_ARCH; + + // Write the new entry + _FAT_cache_writePartialSector (file->partition->cache, dirEntryData, + _FAT_fat_clusterToSector(file->partition, file->dirEntryEnd.cluster) + file->dirEntryEnd.sector, + file->dirEntryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); + + // Flush any sectors in the disc cache + if (!_FAT_cache_flush(file->partition->cache)) { + return EIO; + } + } + + file->modified = false; + + return 0; +} + + +int _FAT_close_r (struct _reent *r, void *fd) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + int ret = 0; + + if (!file->inUse) { + r->_errno = EBADF; + return -1; + } + + _FAT_lock(&file->partition->lock); + + if (file->write) { + ret = _FAT_syncToDisc (file); + if (ret != 0) { + r->_errno = ret; + ret = -1; + } + } + + file->inUse = false; + + // Remove this file from the double-linked list of open files + file->partition->openFileCount -= 1; + if (file->nextOpenFile) { + file->nextOpenFile->prevOpenFile = file->prevOpenFile; + } + if (file->prevOpenFile) { + file->prevOpenFile->nextOpenFile = file->nextOpenFile; + } else { + file->partition->firstOpenFile = file->nextOpenFile; + } + + _FAT_unlock(&file->partition->lock); + + return ret; +} + +ssize_t _FAT_read_r (struct _reent *r, void *fd, char *ptr, size_t len) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + PARTITION* partition; + CACHE* cache; + FILE_POSITION position; + uint32_t tempNextCluster; + unsigned int tempVar; + size_t remain; + bool flagNoError = true; + + // Short circuit cases where len is 0 (or less) + if (len <= 0) { + return 0; + } + + // Make sure we can actually read from the file + if ((file == NULL) || !file->inUse || !file->read) { + r->_errno = EBADF; + return -1; + } + + partition = file->partition; + _FAT_lock(&partition->lock); + + // Don't try to read if the read pointer is past the end of file + if (file->currentPosition >= file->filesize || file->startCluster == CLUSTER_FREE) { + r->_errno = EOVERFLOW; + _FAT_unlock(&partition->lock); + return 0; + } + + // Don't read past end of file + if (len + file->currentPosition > file->filesize) { + r->_errno = EOVERFLOW; + len = file->filesize - file->currentPosition; + } + + remain = len; + position = file->rwPosition; + cache = file->partition->cache; + + // Align to sector + tempVar = partition->bytesPerSector - position.byte; + if (tempVar > remain) { + tempVar = remain; + } + + if ((tempVar < partition->bytesPerSector) && flagNoError) + { + _FAT_cache_readPartialSector ( cache, ptr, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, + position.byte, tempVar); + + remain -= tempVar; + ptr += tempVar; + + position.byte += tempVar; + if (position.byte >= partition->bytesPerSector) { + position.byte = 0; + position.sector++; + } + } + + // align to cluster + // tempVar is number of sectors to read + if (remain > (partition->sectorsPerCluster - position.sector) * partition->bytesPerSector) { + tempVar = partition->sectorsPerCluster - position.sector; + } else { + tempVar = remain / partition->bytesPerSector; + } + + if ((tempVar > 0) && flagNoError) { + if (! _FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, + tempVar, ptr)) + { + flagNoError = false; + r->_errno = EIO; + } else { + ptr += tempVar * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; + position.sector += tempVar; + } + } + + // Move onto next cluster + // It should get to here without reading anything if a cluster is due to be allocated + if ((position.sector >= partition->sectorsPerCluster) && flagNoError) { + tempNextCluster = _FAT_fat_nextCluster(partition, position.cluster); + if ((remain == 0) && (tempNextCluster == CLUSTER_EOF)) { + position.sector = partition->sectorsPerCluster; + } else if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + r->_errno = EIO; + flagNoError = false; + } else { + position.sector = 0; + position.cluster = tempNextCluster; + } + } + + // Read in whole clusters, contiguous blocks at a time + while ((remain >= partition->bytesPerCluster) && flagNoError) { + uint32_t chunkEnd; + uint32_t nextChunkStart = position.cluster; + size_t chunkSize = 0; + + do { + chunkEnd = nextChunkStart; + nextChunkStart = _FAT_fat_nextCluster (partition, chunkEnd); + chunkSize += partition->bytesPerCluster; + } while ((nextChunkStart == chunkEnd + 1) && +#ifdef LIMIT_SECTORS + (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * partition->bytesPerSector) && +#endif + (chunkSize + partition->bytesPerCluster <= remain)); + + if (!_FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), + chunkSize / partition->bytesPerSector, ptr)) + { + flagNoError = false; + r->_errno = EIO; + break; + } + ptr += chunkSize; + remain -= chunkSize; + + // Advance to next cluster + if ((remain == 0) && (nextChunkStart == CLUSTER_EOF)) { + position.sector = partition->sectorsPerCluster; + position.cluster = chunkEnd; + } else if (!_FAT_fat_isValidCluster(partition, nextChunkStart)) { + r->_errno = EIO; + flagNoError = false; + } else { + position.sector = 0; + position.cluster = nextChunkStart; + } + } + + // Read remaining sectors + tempVar = remain / partition->bytesPerSector; // Number of sectors left + if ((tempVar > 0) && flagNoError) { + if (!_FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), + tempVar, ptr)) + { + flagNoError = false; + r->_errno = EIO; + } else { + ptr += tempVar * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; + position.sector += tempVar; + } + } + + // Last remaining sector + // Check if anything is left + if ((remain > 0) && flagNoError) { + _FAT_cache_readPartialSector ( cache, ptr, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain); + position.byte += remain; + remain = 0; + } + + // Length read is the wanted length minus the stuff not read + len = len - remain; + + // Update file information + file->rwPosition = position; + file->currentPosition += len; + + _FAT_unlock(&partition->lock); + return len; +} + +// if current position is on the cluster border and more data has to be written +// then get next cluster or allocate next cluster +// this solves the over-allocation problems when file size is aligned to cluster size +// return true on succes, false on error +static bool _FAT_check_position_for_next_cluster(struct _reent *r, + FILE_POSITION *position, PARTITION* partition, size_t remain, bool *flagNoError) +{ + uint32_t tempNextCluster; + // do nothing if no more data to write + if (remain == 0) return true; + if (flagNoError && *flagNoError == false) return false; + if (position->sector > partition->sectorsPerCluster) { + // invalid arguments - internal error + r->_errno = EINVAL; + goto err; + } + if (position->sector == partition->sectorsPerCluster) { + // need to advance to next cluster + tempNextCluster = _FAT_fat_nextCluster(partition, position->cluster); + if ((tempNextCluster == CLUSTER_EOF) || (tempNextCluster == CLUSTER_FREE)) { + // Ran out of clusters so get a new one + tempNextCluster = _FAT_fat_linkFreeCluster(partition, position->cluster); + } + if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + // Couldn't get a cluster, so abort + r->_errno = ENOSPC; + goto err; + } + position->sector = 0; + position->cluster = tempNextCluster; + } + return true; +err: + if (flagNoError) *flagNoError = false; + return false; +} + +/* +Extend a file so that the size is the same as the rwPosition +*/ +static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) { + PARTITION* partition = file->partition; + CACHE* cache = file->partition->cache; + FILE_POSITION position; + uint8_t zeroBuffer [partition->bytesPerSector]; + memset(zeroBuffer, 0, partition->bytesPerSector); + uint32_t remain; + uint32_t tempNextCluster; + unsigned int sector; + + position.byte = file->filesize % partition->bytesPerSector; + position.sector = (file->filesize % partition->bytesPerCluster) / partition->bytesPerSector; + // It is assumed that there is always a startCluster + // This will be true when _FAT_file_extend_r is called from _FAT_write_r + position.cluster = _FAT_fat_lastCluster (partition, file->startCluster); + + remain = file->currentPosition - file->filesize; + + if ((remain > 0) && (file->filesize > 0) && (position.sector == 0) && (position.byte == 0)) { + // Get a new cluster on the edge of a cluster boundary + tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster); + if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + // Couldn't get a cluster, so abort + r->_errno = ENOSPC; + return false; + } + position.cluster = tempNextCluster; + position.sector = 0; + } + + if (remain + position.byte < partition->bytesPerSector) { + // Only need to clear to the end of the sector + _FAT_cache_writePartialSector (cache, zeroBuffer, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, remain); + position.byte += remain; + } else { + if (position.byte > 0) { + _FAT_cache_writePartialSector (cache, zeroBuffer, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, + partition->bytesPerSector - position.byte); + remain -= (partition->bytesPerSector - position.byte); + position.byte = 0; + position.sector ++; + } + + while (remain >= partition->bytesPerSector) { + if (position.sector >= partition->sectorsPerCluster) { + position.sector = 0; + // Ran out of clusters so get a new one + tempNextCluster = _FAT_fat_linkFreeCluster(partition, position.cluster); + if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + // Couldn't get a cluster, so abort + r->_errno = ENOSPC; + return false; + } + position.cluster = tempNextCluster; + } + + sector = _FAT_fat_clusterToSector (partition, position.cluster) + position.sector; + _FAT_cache_writeSectors (cache, sector, 1, zeroBuffer); + + remain -= partition->bytesPerSector; + position.sector ++; + } + + if (!_FAT_check_position_for_next_cluster(r, &position, partition, remain, NULL)) { + // error already marked + return false; + } + + if (remain > 0) { + _FAT_cache_writePartialSector (cache, zeroBuffer, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain); + position.byte = remain; + } + } + + file->rwPosition = position; + file->filesize = file->currentPosition; + return true; +} + +ssize_t _FAT_write_r (struct _reent *r, void *fd, const char *ptr, size_t len) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + PARTITION* partition; + CACHE* cache; + FILE_POSITION position; + uint32_t tempNextCluster; + unsigned int tempVar; + size_t remain; + bool flagNoError = true; + bool flagAppending = false; + + // Make sure we can actually write to the file + if ((file == NULL) || !file->inUse || !file->write) { + r->_errno = EBADF; + return -1; + } + + partition = file->partition; + cache = file->partition->cache; + _FAT_lock(&partition->lock); + + // Only write up to the maximum file size, taking into account wrap-around of ints + if (len + file->filesize > FILE_MAX_SIZE || len + file->filesize < file->filesize) { + len = FILE_MAX_SIZE - file->filesize; + } + + // Short circuit cases where len is 0 (or less) + if (len <= 0) { + _FAT_unlock(&partition->lock); + return 0; + } + + remain = len; + + // Get a new cluster for the start of the file if required + if (file->startCluster == CLUSTER_FREE) { + tempNextCluster = _FAT_fat_linkFreeCluster (partition, CLUSTER_FREE); + if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + // Couldn't get a cluster, so abort immediately + _FAT_unlock(&partition->lock); + r->_errno = ENOSPC; + return -1; + } + file->startCluster = tempNextCluster; + + // Appending starts at the begining for a 0 byte file + file->appendPosition.cluster = file->startCluster; + file->appendPosition.sector = 0; + file->appendPosition.byte = 0; + + file->rwPosition.cluster = file->startCluster; + file->rwPosition.sector = 0; + file->rwPosition.byte = 0; + } + + if (file->append) { + position = file->appendPosition; + flagAppending = true; + } else { + // If the write pointer is past the end of the file, extend the file to that size + if (file->currentPosition > file->filesize) { + if (!_FAT_file_extend_r (r, file)) { + _FAT_unlock(&partition->lock); + return -1; + } + } + + // Write at current read pointer + position = file->rwPosition; + + // If it is writing past the current end of file, set appending flag + if (len + file->currentPosition > file->filesize) { + flagAppending = true; + } + } + + // Move onto next cluster if needed + _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError); + + // Align to sector + tempVar = partition->bytesPerSector - position.byte; + if (tempVar > remain) { + tempVar = remain; + } + + if ((tempVar < partition->bytesPerSector) && flagNoError) { + // Write partial sector to disk + _FAT_cache_writePartialSector (cache, ptr, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, tempVar); + + remain -= tempVar; + ptr += tempVar; + position.byte += tempVar; + + + // Move onto next sector + if (position.byte >= partition->bytesPerSector) { + position.byte = 0; + position.sector ++; + } + } + + // Align to cluster + // tempVar is number of sectors to write + if (remain > (partition->sectorsPerCluster - position.sector) * partition->bytesPerSector) { + tempVar = partition->sectorsPerCluster - position.sector; + } else { + tempVar = remain / partition->bytesPerSector; + } + + if ((tempVar > 0 && tempVar < partition->sectorsPerCluster) && flagNoError) { + if (!_FAT_cache_writeSectors (cache, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, tempVar, ptr)) + { + flagNoError = false; + r->_errno = EIO; + } else { + ptr += tempVar * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; + position.sector += tempVar; + } + } + + // Write whole clusters + while ((remain >= partition->bytesPerCluster) && flagNoError) { + // allocate next cluster + _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError); + if (!flagNoError) break; + // set indexes to the current position + uint32_t chunkEnd = position.cluster; + uint32_t nextChunkStart = position.cluster; + size_t chunkSize = partition->bytesPerCluster; + FILE_POSITION next_position = position; + + // group consecutive clusters + while (flagNoError && +#ifdef LIMIT_SECTORS + (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * partition->bytesPerSector) && +#endif + (chunkSize + partition->bytesPerCluster < remain)) + { + // pretend to use up all sectors in next_position + next_position.sector = partition->sectorsPerCluster; + // get or allocate next cluster + _FAT_check_position_for_next_cluster(r, &next_position, partition, + remain - chunkSize, &flagNoError); + if (!flagNoError) break; // exit loop on error + nextChunkStart = next_position.cluster; + if (nextChunkStart != chunkEnd + 1) break; // exit loop if not consecutive + chunkEnd = nextChunkStart; + chunkSize += partition->bytesPerCluster; + } + + if ( !_FAT_cache_writeSectors (cache, + _FAT_fat_clusterToSector(partition, position.cluster), chunkSize / partition->bytesPerSector, ptr)) + { + flagNoError = false; + r->_errno = EIO; + break; + } + ptr += chunkSize; + remain -= chunkSize; + + if ((chunkEnd != nextChunkStart) && _FAT_fat_isValidCluster(partition, nextChunkStart)) { + // new cluster is already allocated (because it was not consecutive) + position.cluster = nextChunkStart; + position.sector = 0; + } else { + // Allocate a new cluster when next writing the file + position.cluster = chunkEnd; + position.sector = partition->sectorsPerCluster; + } + } + + // allocate next cluster if needed + _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError); + + // Write remaining sectors + tempVar = remain / partition->bytesPerSector; // Number of sectors left + if ((tempVar > 0) && flagNoError) { + if (!_FAT_cache_writeSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), tempVar, ptr)) + { + flagNoError = false; + r->_errno = EIO; + } else { + ptr += tempVar * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; + position.sector += tempVar; + } + } + + // Last remaining sector + if ((remain > 0) && flagNoError) { + if (flagAppending) { + _FAT_cache_eraseWritePartialSector ( cache, ptr, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain); + } else { + _FAT_cache_writePartialSector ( cache, ptr, + _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, 0, remain); + } + position.byte += remain; + remain = 0; + } + + + // Amount written is the originally requested amount minus stuff remaining + len = len - remain; + + // Update file information + file->modified = true; + if (file->append) { + // Appending doesn't affect the read pointer + file->appendPosition = position; + file->filesize += len; + } else { + // Writing also shifts the read pointer + file->rwPosition = position; + file->currentPosition += len; + if (file->filesize < file->currentPosition) { + file->filesize = file->currentPosition; + } + } + _FAT_unlock(&partition->lock); + + return len; +} + + +off_t _FAT_seek_r (struct _reent *r, void *fd, off_t pos, int dir) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + PARTITION* partition; + uint32_t cluster, nextCluster; + int clusCount; + off_t newPosition; + uint32_t position; + + if ((file == NULL) || (file->inUse == false)) { + // invalid file + r->_errno = EBADF; + return -1; + } + + partition = file->partition; + _FAT_lock(&partition->lock); + + switch (dir) { + case SEEK_SET: + newPosition = pos; + break; + case SEEK_CUR: + newPosition = (off_t)file->currentPosition + pos; + break; + case SEEK_END: + newPosition = (off_t)file->filesize + pos; + break; + default: + _FAT_unlock(&partition->lock); + r->_errno = EINVAL; + return -1; + } + + if ((pos > 0) && (newPosition < 0)) { + _FAT_unlock(&partition->lock); + r->_errno = EOVERFLOW; + return -1; + } + + // newPosition can only be larger than the FILE_MAX_SIZE on platforms where + // off_t is larger than 32 bits. + if (newPosition < 0 || ((sizeof(newPosition) > 4) && newPosition > (off_t)FILE_MAX_SIZE)) { + _FAT_unlock(&partition->lock); + r->_errno = EINVAL; + return -1; + } + + position = (uint32_t)newPosition; + + // Only change the read/write position if it is within the bounds of the current filesize, + // or at the very edge of the file + if (position <= file->filesize && file->startCluster != CLUSTER_FREE) { + // Calculate where the correct cluster is + // how many clusters from start of file + clusCount = position / partition->bytesPerCluster; + cluster = file->startCluster; + if (position >= file->currentPosition) { + // start from current cluster + int currentCount = file->currentPosition / partition->bytesPerCluster; + if (file->rwPosition.sector == partition->sectorsPerCluster) { + currentCount--; + } + clusCount -= currentCount; + cluster = file->rwPosition.cluster; + } + // Calculate the sector and byte of the current position, + // and store them + file->rwPosition.sector = (position % partition->bytesPerCluster) / partition->bytesPerSector; + file->rwPosition.byte = position % partition->bytesPerSector; + + nextCluster = _FAT_fat_nextCluster (partition, cluster); + while ((clusCount > 0) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) { + clusCount--; + cluster = nextCluster; + nextCluster = _FAT_fat_nextCluster (partition, cluster); + } + + // Check if ran out of clusters and it needs to allocate a new one + if (clusCount > 0) { + if ((clusCount == 1) && (file->filesize == position) && (file->rwPosition.sector == 0)) { + // Set flag to allocate a new cluster + file->rwPosition.sector = partition->sectorsPerCluster; + file->rwPosition.byte = 0; + } else { + _FAT_unlock(&partition->lock); + r->_errno = EINVAL; + return -1; + } + } + + file->rwPosition.cluster = cluster; + } + + // Save position + file->currentPosition = position; + + _FAT_unlock(&partition->lock); + return position; +} + + + +int _FAT_fstat_r (struct _reent *r, void *fd, struct stat *st) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + PARTITION* partition; + DIR_ENTRY fileEntry; + + if ((file == NULL) || (file->inUse == false)) { + // invalid file + r->_errno = EBADF; + return -1; + } + + partition = file->partition; + _FAT_lock(&partition->lock); + + // Get the file's entry data + fileEntry.dataStart = file->dirEntryStart; + fileEntry.dataEnd = file->dirEntryEnd; + + if (!_FAT_directory_entryFromPosition (partition, &fileEntry)) { + _FAT_unlock(&partition->lock); + r->_errno = EIO; + return -1; + } + + // Fill in the stat struct + _FAT_directory_entryStat (partition, &fileEntry, st); + + // Fix stats that have changed since the file was openned + st->st_ino = (ino_t)(file->startCluster); // The file serial number is the start cluster + st->st_size = file->filesize; // File size + + _FAT_unlock(&partition->lock); + return 0; +} + +int _FAT_ftruncate_r (struct _reent *r, void *fd, off_t len) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + PARTITION* partition; + int ret=0; + uint32_t newSize = (uint32_t)len; + + if (len < 0) { + // Trying to truncate to a negative size + r->_errno = EINVAL; + return -1; + } + + if ((sizeof(len) > 4) && len > (off_t)FILE_MAX_SIZE) { + // Trying to extend the file beyond what FAT supports + r->_errno = EFBIG; + return -1; + } + + if (!file || !file->inUse) { + // invalid file + r->_errno = EBADF; + return -1; + } + + if (!file->write) { + // Read-only file + r->_errno = EINVAL; + return -1; + } + + partition = file->partition; + _FAT_lock(&partition->lock); + + if (newSize > file->filesize) { + // Expanding the file + FILE_POSITION savedPosition; + uint32_t savedOffset; + // Get a new cluster for the start of the file if required + if (file->startCluster == CLUSTER_FREE) { + uint32_t tempNextCluster = _FAT_fat_linkFreeCluster (partition, CLUSTER_FREE); + if (!_FAT_fat_isValidCluster(partition, tempNextCluster)) { + // Couldn't get a cluster, so abort immediately + _FAT_unlock(&partition->lock); + r->_errno = ENOSPC; + return -1; + } + file->startCluster = tempNextCluster; + + file->rwPosition.cluster = file->startCluster; + file->rwPosition.sector = 0; + file->rwPosition.byte = 0; + } + // Save the read/write pointer + savedPosition = file->rwPosition; + savedOffset = file->currentPosition; + // Set the position to the new size + file->currentPosition = newSize; + // Extend the file to the new position + if (!_FAT_file_extend_r (r, file)) { + ret = -1; + } + // Set the append position to the new rwPointer + if (file->append) { + file->appendPosition = file->rwPosition; + } + // Restore the old rwPointer; + file->rwPosition = savedPosition; + file->currentPosition = savedOffset; + } else if (newSize < file->filesize){ + // Shrinking the file + if (len == 0) { + // Cutting the file down to nothing, clear all clusters used + _FAT_fat_clearLinks (partition, file->startCluster); + file->startCluster = CLUSTER_FREE; + + file->appendPosition.cluster = CLUSTER_FREE; + file->appendPosition.sector = 0; + file->appendPosition.byte = 0; + } else { + // Trimming the file down to the required size + unsigned int chainLength; + uint32_t lastCluster; + + // Drop the unneeded end of the cluster chain. + // If the end falls on a cluster boundary, drop that cluster too, + // then set a flag to allocate a cluster as needed + chainLength = ((newSize-1) / partition->bytesPerCluster) + 1; + lastCluster = _FAT_fat_trimChain (partition, file->startCluster, chainLength); + + if (file->append) { + file->appendPosition.byte = newSize % partition->bytesPerSector; + // Does the end of the file fall on the edge of a cluster? + if (newSize % partition->bytesPerCluster == 0) { + // Set a flag to allocate a new cluster + file->appendPosition.sector = partition->sectorsPerCluster; + } else { + file->appendPosition.sector = (newSize % partition->bytesPerCluster) / partition->bytesPerSector; + } + file->appendPosition.cluster = lastCluster; + } + } + } else { + // Truncating to same length, so don't do anything + } + + file->filesize = newSize; + file->modified = true; + + _FAT_unlock(&partition->lock); + return ret; +} + +int _FAT_fsync_r (struct _reent *r, void *fd) { + FILE_STRUCT* file = (FILE_STRUCT*) fd; + int ret = 0; + + if (!file->inUse) { + r->_errno = EBADF; + return -1; + } + + _FAT_lock(&file->partition->lock); + + ret = _FAT_syncToDisc (file); + if (ret != 0) { + r->_errno = ret; + ret = -1; + } + + _FAT_unlock(&file->partition->lock); + + return ret; +} diff --git a/wii/libogc/libfat/fatfile.h b/wii/libogc/libfat/fatfile.h new file mode 100644 index 0000000000..3d9836c061 --- /dev/null +++ b/wii/libogc/libfat/fatfile.h @@ -0,0 +1,105 @@ +/* + fatfile.h + + Functions used by the newlib disc stubs to interface with + this library + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef _FATFILE_H +#define _FATFILE_H + +#include +#include + +#include "common.h" +#include "partition.h" +#include "directory.h" + +#define FILE_MAX_SIZE ((uint32_t)0xFFFFFFFF) // 4GiB - 1B + +typedef struct { + u32 cluster; + sec_t sector; + s32 byte; +} FILE_POSITION; + +struct _FILE_STRUCT; + +struct _FILE_STRUCT { + uint32_t filesize; + uint32_t startCluster; + uint32_t currentPosition; + FILE_POSITION rwPosition; + FILE_POSITION appendPosition; + DIR_ENTRY_POSITION dirEntryStart; // Points to the start of the LFN entries of a file, or the alias for no LFN + DIR_ENTRY_POSITION dirEntryEnd; // Always points to the file's alias entry + PARTITION* partition; + struct _FILE_STRUCT* prevOpenFile; // The previous entry in a double-linked list of open files + struct _FILE_STRUCT* nextOpenFile; // The next entry in a double-linked list of open files + bool read; + bool write; + bool append; + bool inUse; + bool modified; +}; + +typedef struct _FILE_STRUCT FILE_STRUCT; + +int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode); + +int _FAT_close_r (struct _reent *r, void *fd); + +ssize_t _FAT_write_r (struct _reent *r,void *fd, const char *ptr, size_t len); + +ssize_t _FAT_read_r (struct _reent *r, void *fd, char *ptr, size_t len); + +off_t _FAT_seek_r (struct _reent *r, void *fd, off_t pos, int dir); + +int _FAT_fstat_r (struct _reent *r, void *fd, struct stat *st); + +int _FAT_stat_r (struct _reent *r, const char *path, struct stat *st); + +int _FAT_link_r (struct _reent *r, const char *existing, const char *newLink); + +int _FAT_unlink_r (struct _reent *r, const char *name); + +int _FAT_chdir_r (struct _reent *r, const char *name); + +int _FAT_rename_r (struct _reent *r, const char *oldName, const char *newName); + +int _FAT_ftruncate_r (struct _reent *r, void *fd, off_t len); + +int _FAT_fsync_r (struct _reent *r, void *fd); + +/* +Synchronizes the file data to disc. +Does no locking of its own -- lock the partition before calling. +Returns 0 on success, an error code on failure. +*/ +extern int _FAT_syncToDisc (FILE_STRUCT* file); + +#endif // _FATFILE_H diff --git a/wii/libogc/libfat/file_allocation_table.c b/wii/libogc/libfat/file_allocation_table.c new file mode 100644 index 0000000000..72f8aa74aa --- /dev/null +++ b/wii/libogc/libfat/file_allocation_table.c @@ -0,0 +1,393 @@ +/* + file_allocation_table.c + Reading, writing and manipulation of the FAT structure on + a FAT partition + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "file_allocation_table.h" +#include "partition.h" +#include "mem_allocate.h" +#include + +/* +Gets the cluster linked from input cluster +*/ +uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster) +{ + uint32_t nextCluster = CLUSTER_FREE; + sec_t sector; + int offset; + + if (cluster == CLUSTER_FREE) { + return CLUSTER_FREE; + } + + switch (partition->filesysType) + { + case FS_UNKNOWN: + return CLUSTER_ERROR; + break; + + case FS_FAT12: + { + u32 nextCluster_h; + sector = partition->fat.fatStart + (((cluster * 3) / 2) / partition->bytesPerSector); + offset = ((cluster * 3) / 2) % partition->bytesPerSector; + + + _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u8)); + + offset++; + + if (offset >= partition->bytesPerSector) { + offset = 0; + sector++; + } + nextCluster_h = 0; + + _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster_h, sector, offset, sizeof(u8)); + nextCluster |= (nextCluster_h << 8); + + if (cluster & 0x01) { + nextCluster = nextCluster >> 4; + } else { + nextCluster &= 0x0FFF; + } + + if (nextCluster >= 0x0FF7) + { + nextCluster = CLUSTER_EOF; + } + + break; + } + case FS_FAT16: + sector = partition->fat.fatStart + ((cluster << 1) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 1)) << 1; + + _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u16)); + + if (nextCluster >= 0xFFF7) { + nextCluster = CLUSTER_EOF; + } + break; + + case FS_FAT32: + sector = partition->fat.fatStart + ((cluster << 2) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 2)) << 2; + + _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u32)); + + if (nextCluster >= 0x0FFFFFF7) { + nextCluster = CLUSTER_EOF; + } + break; + + default: + return CLUSTER_ERROR; + break; + } + + return nextCluster; +} + +/* +writes value into the correct offset within a partition's FAT, based +on the cluster number. +*/ +static bool _FAT_fat_writeFatEntry (PARTITION* partition, uint32_t cluster, uint32_t value) { + sec_t sector; + int offset; + uint32_t oldValue; + + if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */)) + { + return false; + } + + switch (partition->filesysType) + { + case FS_UNKNOWN: + return false; + break; + + case FS_FAT12: + sector = partition->fat.fatStart + (((cluster * 3) / 2) / partition->bytesPerSector); + offset = ((cluster * 3) / 2) % partition->bytesPerSector; + + if (cluster & 0x01) { + + _FAT_cache_readLittleEndianValue (partition->cache, &oldValue, sector, offset, sizeof(u8)); + + value = (value << 4) | (oldValue & 0x0F); + + _FAT_cache_writeLittleEndianValue (partition->cache, value & 0xFF, sector, offset, sizeof(u8)); + + offset++; + if (offset >= partition->bytesPerSector) { + offset = 0; + sector++; + } + + _FAT_cache_writeLittleEndianValue (partition->cache, (value >> 8) & 0xFF, sector, offset, sizeof(u8)); + + } else { + + _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u8)); + + offset++; + if (offset >= partition->bytesPerSector) { + offset = 0; + sector++; + } + + _FAT_cache_readLittleEndianValue (partition->cache, &oldValue, sector, offset, sizeof(u8)); + + value = ((value >> 8) & 0x0F) | (oldValue & 0xF0); + + _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u8)); + } + + break; + + case FS_FAT16: + sector = partition->fat.fatStart + ((cluster << 1) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 1)) << 1; + + _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u16)); + + break; + + case FS_FAT32: + sector = partition->fat.fatStart + ((cluster << 2) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 2)) << 2; + + _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u32)); + + break; + + default: + return false; + break; + } + + return true; +} + +/*----------------------------------------------------------------- +gets the first available free cluster, sets it +to end of file, links the input cluster to it then returns the +cluster number +If an error occurs, return CLUSTER_ERROR +-----------------------------------------------------------------*/ +uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster) { + uint32_t firstFree; + uint32_t curLink; + uint32_t lastCluster; + bool loopedAroundFAT = false; + + lastCluster = partition->fat.lastCluster; + + if (cluster > lastCluster) { + return CLUSTER_ERROR; + } + + // Check if the cluster already has a link, and return it if so + curLink = _FAT_fat_nextCluster(partition, cluster); + if ((curLink >= CLUSTER_FIRST) && (curLink <= lastCluster)) { + return curLink; // Return the current link - don't allocate a new one + } + + // Get a free cluster + firstFree = partition->fat.firstFree; + // Start at first valid cluster + if (firstFree < CLUSTER_FIRST) { + firstFree = CLUSTER_FIRST; + } + + // Search until a free cluster is found + while (_FAT_fat_nextCluster(partition, firstFree) != CLUSTER_FREE) { + firstFree++; + if (firstFree > lastCluster) { + if (loopedAroundFAT) { + // If couldn't get a free cluster then return an error + partition->fat.firstFree = firstFree; + return CLUSTER_ERROR; + } else { + // Try looping back to the beginning of the FAT + // This was suggested by loopy + firstFree = CLUSTER_FIRST; + loopedAroundFAT = true; + } + } + } + partition->fat.firstFree = firstFree; + if(partition->fat.numberFreeCluster) + partition->fat.numberFreeCluster--; + partition->fat.numberLastAllocCluster = firstFree; + + if ((cluster >= CLUSTER_FIRST) && (cluster <= lastCluster)) + { + // Update the linked from FAT entry + _FAT_fat_writeFatEntry (partition, cluster, firstFree); + } + // Create the linked to FAT entry + _FAT_fat_writeFatEntry (partition, firstFree, CLUSTER_EOF); + + return firstFree; +} + +/*----------------------------------------------------------------- +gets the first available free cluster, sets it +to end of file, links the input cluster to it, clears the new +cluster to 0 valued bytes, then returns the cluster number +If an error occurs, return CLUSTER_ERROR +-----------------------------------------------------------------*/ +uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster) { + uint32_t newCluster; + uint32_t i; + uint8_t *emptySector; + + // Link the cluster + newCluster = _FAT_fat_linkFreeCluster(partition, cluster); + + if (newCluster == CLUSTER_FREE || newCluster == CLUSTER_ERROR) { + return CLUSTER_ERROR; + } + + emptySector = (uint8_t*) _FAT_mem_allocate(partition->bytesPerSector); + + // Clear all the sectors within the cluster + memset (emptySector, 0, partition->bytesPerSector); + for (i = 0; i < partition->sectorsPerCluster; i++) { + _FAT_cache_writeSectors (partition->cache, + _FAT_fat_clusterToSector (partition, newCluster) + i, + 1, emptySector); + } + + _FAT_mem_free(emptySector); + + return newCluster; +} + + +/*----------------------------------------------------------------- +_FAT_fat_clearLinks +frees any cluster used by a file +-----------------------------------------------------------------*/ +bool _FAT_fat_clearLinks (PARTITION* partition, uint32_t cluster) { + uint32_t nextCluster; + + if ((cluster < CLUSTER_FIRST) || (cluster > partition->fat.lastCluster /* This will catch CLUSTER_ERROR */)) + return false; + + // If this clears up more space in the FAT before the current free pointer, move it backwards + if (cluster < partition->fat.firstFree) { + partition->fat.firstFree = cluster; + } + + while ((cluster != CLUSTER_EOF) && (cluster != CLUSTER_FREE) && (cluster != CLUSTER_ERROR)) { + // Store next cluster before erasing the link + nextCluster = _FAT_fat_nextCluster (partition, cluster); + + // Erase the link + _FAT_fat_writeFatEntry (partition, cluster, CLUSTER_FREE); + + if(partition->fat.numberFreeCluster < (partition->numberOfSectors/partition->sectorsPerCluster)) + partition->fat.numberFreeCluster++; + // Move onto next cluster + cluster = nextCluster; + } + + return true; +} + +/*----------------------------------------------------------------- +_FAT_fat_trimChain +Drop all clusters past the chainLength. +If chainLength is 0, all clusters are dropped. +If chainLength is 1, the first cluster is kept and the rest are +dropped, and so on. +Return the last cluster left in the chain. +-----------------------------------------------------------------*/ +uint32_t _FAT_fat_trimChain (PARTITION* partition, uint32_t startCluster, unsigned int chainLength) { + uint32_t nextCluster; + + if (chainLength == 0) { + // Drop the entire chain + _FAT_fat_clearLinks (partition, startCluster); + return CLUSTER_FREE; + } else { + // Find the last cluster in the chain, and the one after it + chainLength--; + nextCluster = _FAT_fat_nextCluster (partition, startCluster); + while ((chainLength > 0) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) { + chainLength--; + startCluster = nextCluster; + nextCluster = _FAT_fat_nextCluster (partition, startCluster); + } + + // Drop all clusters after the last in the chain + if (nextCluster != CLUSTER_FREE && nextCluster != CLUSTER_EOF) { + _FAT_fat_clearLinks (partition, nextCluster); + } + + // Mark the last cluster in the chain as the end of the file + _FAT_fat_writeFatEntry (partition, startCluster, CLUSTER_EOF); + + return startCluster; + } +} + +/*----------------------------------------------------------------- +_FAT_fat_lastCluster +Trace the cluster links until the last one is found +-----------------------------------------------------------------*/ +uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster) { + while ((_FAT_fat_nextCluster(partition, cluster) != CLUSTER_FREE) && (_FAT_fat_nextCluster(partition, cluster) != CLUSTER_EOF)) { + cluster = _FAT_fat_nextCluster(partition, cluster); + } + return cluster; +} + +/*----------------------------------------------------------------- +_FAT_fat_freeClusterCount +Return the number of free clusters available +-----------------------------------------------------------------*/ +unsigned int _FAT_fat_freeClusterCount (PARTITION* partition) { + unsigned int count = 0; + uint32_t curCluster; + + for (curCluster = CLUSTER_FIRST; curCluster <= partition->fat.lastCluster; curCluster++) { + if (_FAT_fat_nextCluster(partition, curCluster) == CLUSTER_FREE) { + count++; + } + } + + return count; +} + diff --git a/wii/libogc/libfat/file_allocation_table.h b/wii/libogc/libfat/file_allocation_table.h new file mode 100644 index 0000000000..d9c43614dd --- /dev/null +++ b/wii/libogc/libfat/file_allocation_table.h @@ -0,0 +1,70 @@ +/* + file_allocation_table.h + Reading, writing and manipulation of the FAT structure on + a FAT partition + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _FAT_H +#define _FAT_H + +#include "common.h" +#include "partition.h" + +#define CLUSTER_EOF_16 0xFFFF +#define CLUSTER_EOF 0x0FFFFFFF +#define CLUSTER_FREE 0x00000000 +#define CLUSTER_ROOT 0x00000000 +#define CLUSTER_FIRST 0x00000002 +#define CLUSTER_ERROR 0xFFFFFFFF + +#define CLUSTERS_PER_FAT12 4085 +#define CLUSTERS_PER_FAT16 65525 + + +uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster); + +uint32_t _FAT_fat_linkFreeCluster(PARTITION* partition, uint32_t cluster); +uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster); + +bool _FAT_fat_clearLinks (PARTITION* partition, uint32_t cluster); + +uint32_t _FAT_fat_trimChain (PARTITION* partition, uint32_t startCluster, unsigned int chainLength); + +uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster); + +unsigned int _FAT_fat_freeClusterCount (PARTITION* partition); + +static inline sec_t _FAT_fat_clusterToSector (PARTITION* partition, uint32_t cluster) { + return (cluster >= CLUSTER_FIRST) ? + ((cluster - CLUSTER_FIRST) * (sec_t)partition->sectorsPerCluster) + partition->dataStart : + partition->rootDirStart; +} + +static inline bool _FAT_fat_isValidCluster (PARTITION* partition, uint32_t cluster) { + return (cluster >= CLUSTER_FIRST) && (cluster <= partition->fat.lastCluster /* This will catch CLUSTER_ERROR */); +} + +#endif // _FAT_H diff --git a/wii/libogc/libfat/filetime.c b/wii/libogc/libfat/filetime.c new file mode 100644 index 0000000000..d297bf6400 --- /dev/null +++ b/wii/libogc/libfat/filetime.c @@ -0,0 +1,107 @@ +/* + filetime.c + Conversion of file time and date values to various other types + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include +#include "filetime.h" +#include "common.h" + +#define MAX_HOUR 23 +#define MAX_MINUTE 59 +#define MAX_SECOND 59 + +#define MAX_MONTH 11 +#define MIN_MONTH 0 +#define MAX_DAY 31 +#define MIN_DAY 1 + +uint16_t _FAT_filetime_getTimeFromRTC (void) { +#ifdef USE_RTC_TIME + struct tm timeParts; + time_t epochTime; + + if (time(&epochTime) == (time_t)-1) { + return 0; + } + localtime_r(&epochTime, &timeParts); + + // Check that the values are all in range. + // If they are not, return 0 (no timestamp) + if ((timeParts.tm_hour < 0) || (timeParts.tm_hour > MAX_HOUR)) return 0; + if ((timeParts.tm_min < 0) || (timeParts.tm_min > MAX_MINUTE)) return 0; + if ((timeParts.tm_sec < 0) || (timeParts.tm_sec > MAX_SECOND)) return 0; + + return ( + ((timeParts.tm_hour & 0x1F) << 11) | + ((timeParts.tm_min & 0x3F) << 5) | + ((timeParts.tm_sec >> 1) & 0x1F) + ); +#else + return 0; +#endif +} + + +uint16_t _FAT_filetime_getDateFromRTC (void) { +#ifdef USE_RTC_TIME + struct tm timeParts; + time_t epochTime; + + if (time(&epochTime) == (time_t)-1) { + return 0; + } + localtime_r(&epochTime, &timeParts); + + if ((timeParts.tm_mon < MIN_MONTH) || (timeParts.tm_mon > MAX_MONTH)) return 0; + if ((timeParts.tm_mday < MIN_DAY) || (timeParts.tm_mday > MAX_DAY)) return 0; + + return ( + (((timeParts.tm_year - 80) & 0x7F) <<9) | // Adjust for MS-FAT base year (1980 vs 1900 for tm_year) + (((timeParts.tm_mon + 1) & 0xF) << 5) | + (timeParts.tm_mday & 0x1F) + ); +#else + return 0; +#endif +} + +time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d) { + struct tm timeParts; + + timeParts.tm_hour = t >> 11; + timeParts.tm_min = (t >> 5) & 0x3F; + timeParts.tm_sec = (t & 0x1F) << 1; + + timeParts.tm_mday = d & 0x1F; + timeParts.tm_mon = ((d >> 5) & 0x0F) - 1; + timeParts.tm_year = (d >> 9) + 80; + + timeParts.tm_isdst = 0; + + return mktime(&timeParts); +} diff --git a/wii/libogc/libfat/filetime.h b/wii/libogc/libfat/filetime.h new file mode 100644 index 0000000000..3bfd8ed8af --- /dev/null +++ b/wii/libogc/libfat/filetime.h @@ -0,0 +1,41 @@ +/* + filetime.h + Conversion of file time and date values to various other types + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _FILETIME_H +#define _FILETIME_H + +#include "common.h" +#include + +uint16_t _FAT_filetime_getTimeFromRTC (void); +uint16_t _FAT_filetime_getDateFromRTC (void); + +time_t _FAT_filetime_to_time_t (uint16_t t, uint16_t d); + + +#endif // _FILETIME_H diff --git a/wii/libogc/libfat/libfat.c b/wii/libogc/libfat/libfat.c new file mode 100644 index 0000000000..4d323cae1a --- /dev/null +++ b/wii/libogc/libfat/libfat.c @@ -0,0 +1,255 @@ +/* + libfat.c + Simple functionality for startup, mounting and unmounting of FAT-based devices. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include + +#include "common.h" +#include "partition.h" +#include "fatfile.h" +#include "fatdir.h" +#include "lock.h" +#include "mem_allocate.h" +#include "disc.h" + +static const devoptab_t dotab_fat = { + "fat", + sizeof (FILE_STRUCT), + _FAT_open_r, + _FAT_close_r, + _FAT_write_r, + _FAT_read_r, + _FAT_seek_r, + _FAT_fstat_r, + _FAT_stat_r, + _FAT_link_r, + _FAT_unlink_r, + _FAT_chdir_r, + _FAT_rename_r, + _FAT_mkdir_r, + sizeof (DIR_STATE_STRUCT), + _FAT_diropen_r, + _FAT_dirreset_r, + _FAT_dirnext_r, + _FAT_dirclose_r, + _FAT_statvfs_r, + _FAT_ftruncate_r, + _FAT_fsync_r, + NULL, /* Device data */ + NULL, // chmod_r + NULL, // fchmod_r + NULL // rmdir_r +}; + +bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage) { + PARTITION* partition; + devoptab_t* devops; + char* nameCopy; + + if(!name || strlen(name) > 8 || !interface) + return false; + + if(!interface->startup()) + return false; + + if(!interface->isInserted()) + return false; + + char devname[10]; + strcpy(devname, name); + strcat(devname, ":"); + if(FindDevice(devname) >= 0) + return true; + + devops = _FAT_mem_allocate (sizeof(devoptab_t) + strlen(name) + 1); + if (!devops) { + return false; + } + // Use the space allocated at the end of the devoptab struct for storing the name + nameCopy = (char*)(devops+1); + + // Initialize the file system + partition = _FAT_partition_constructor (interface, cacheSize, SectorsPerPage, startSector); + if (!partition) { + _FAT_mem_free (devops); + return false; + } + + // Add an entry for this device to the devoptab table + memcpy (devops, &dotab_fat, sizeof(dotab_fat)); + strcpy (nameCopy, name); + devops->name = nameCopy; + devops->deviceData = partition; + + AddDevice (devops); + + return true; +} + +bool fatMountSimple (const char* name, const DISC_INTERFACE* interface) { + return fatMount (name, interface, 0, DEFAULT_CACHE_PAGES, DEFAULT_SECTORS_PAGE); +} + +void fatUnmount (const char* name) { + devoptab_t *devops; + PARTITION* partition; + + if(!name) + return; + + devops = (devoptab_t*)GetDeviceOpTab (name); + if (!devops) { + return; + } + + // Perform a quick check to make sure we're dealing with a libfat controlled device + if (devops->open_r != dotab_fat.open_r) { + return; + } + + if (RemoveDevice (name) == -1) { + return; + } + + partition = (PARTITION*)devops->deviceData; + _FAT_partition_destructor (partition); + _FAT_mem_free (devops); +} + +bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice) { + int i; + int defaultDevice = -1; + const DISC_INTERFACE *disc; + + for (i = 0; + _FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL; + i++) + { + disc = _FAT_disc_interfaces[i].getInterface(); + if (!disc) { + continue; + } + if (fatMount (_FAT_disc_interfaces[i].name, disc, 0, cacheSize, DEFAULT_SECTORS_PAGE)) { + // The first device to successfully mount is set as the default + if (defaultDevice < 0) { + defaultDevice = i; + } + } + } + + if (defaultDevice < 0) { + // None of our devices mounted + return false; + } + + if (setAsDefaultDevice) { + char filePath[PATH_MAX]; + strcpy (filePath, _FAT_disc_interfaces[defaultDevice].name); + strcat (filePath, ":/"); +#ifdef ARGV_MAGIC + if ( __system_argv->argvMagic == ARGV_MAGIC && __system_argv->argc >= 1 && strrchr( __system_argv->argv[0], '/' )!=NULL ) { + // Check the app's path against each of our mounted devices, to see + // if we can support it. If so, change to that path. + for (i = 0; + _FAT_disc_interfaces[i].name != NULL && _FAT_disc_interfaces[i].getInterface != NULL; + i++) + { + if ( !strncasecmp( __system_argv->argv[0], _FAT_disc_interfaces[i].name, + strlen(_FAT_disc_interfaces[i].name))) + { + char *lastSlash; + strcpy(filePath, __system_argv->argv[0]); + lastSlash = strrchr( filePath, '/' ); + + if ( NULL != lastSlash) { + if ( *(lastSlash - 1) == ':') lastSlash++; + *lastSlash = 0; + } + } + } + } +#endif + chdir (filePath); + } + + return true; +} + +bool fatInitDefault (void) { + return fatInit (DEFAULT_CACHE_PAGES, true); +} + +void fatGetVolumeLabel (const char* name, char *label) { + devoptab_t *devops; + PARTITION* partition; + char *buf; + int namelen,i; + + if(!name || !label) + return; + + namelen = strlen(name); + buf=(char*)_FAT_mem_allocate(sizeof(char)*namelen+2); + strcpy(buf,name); + + if (name[namelen-1] == '/') { + buf[namelen-1]='\0'; + namelen--; + } + + if (name[namelen-1] != ':') { + buf[namelen]=':'; + buf[namelen+1]='\0'; + } + + devops = (devoptab_t*)GetDeviceOpTab(buf); + + for(i=0;buf[i]!='\0' && buf[i]!=':';i++); + if (!devops || strncasecmp(buf,devops->name,i)) { + _FAT_mem_free(buf); + return; + } + + _FAT_mem_free(buf); + + // Perform a quick check to make sure we're dealing with a libfat controlled device + if (devops->open_r != dotab_fat.open_r) { + return; + } + + partition = (PARTITION*)devops->deviceData; + + if(!_FAT_directory_getVolumeLabel(partition, label)) { + strncpy(label,partition->label,11); + label[11]='\0'; + } + if(!strncmp(label, "NO NAME", 7)) label[0]='\0'; +} diff --git a/wii/libogc/libfat/lock.c b/wii/libogc/libfat/lock.c new file mode 100644 index 0000000000..59c3444c51 --- /dev/null +++ b/wii/libogc/libfat/lock.c @@ -0,0 +1,29 @@ +#include "common.h" + +#ifndef USE_LWP_LOCK + +#ifndef mutex_t +typedef int mutex_t; +#endif + +void __attribute__ ((weak)) _FAT_lock_init(mutex_t *mutex) +{ + return; +} + +void __attribute__ ((weak)) _FAT_lock_deinit(mutex_t *mutex) +{ + return; +} + +void __attribute__ ((weak)) _FAT_lock(mutex_t *mutex) +{ + return; +} + +void __attribute__ ((weak)) _FAT_unlock(mutex_t *mutex) +{ + return; +} + +#endif // USE_LWP_LOCK diff --git a/wii/libogc/libfat/lock.h b/wii/libogc/libfat/lock.h new file mode 100644 index 0000000000..de5723a9cb --- /dev/null +++ b/wii/libogc/libfat/lock.h @@ -0,0 +1,72 @@ +/* + lock.h + + Copyright (c) 2008 Sven Peter + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef _LOCK_H +#define _LOCK_H + +#include "common.h" + +#ifdef USE_LWP_LOCK + +static inline void _FAT_lock_init(mutex_t *mutex) +{ + LWP_MutexInit(mutex, false); +} + +static inline void _FAT_lock_deinit(mutex_t *mutex) +{ + LWP_MutexDestroy(*mutex); +} + +static inline void _FAT_lock(mutex_t *mutex) +{ + LWP_MutexLock(*mutex); +} + +static inline void _FAT_unlock(mutex_t *mutex) +{ + LWP_MutexUnlock(*mutex); +} + +#else + +// We still need a blank lock type +#ifndef mutex_t +typedef int mutex_t; +#endif + +void _FAT_lock_init(mutex_t *mutex); +void _FAT_lock_deinit(mutex_t *mutex); +void _FAT_lock(mutex_t *mutex); +void _FAT_unlock(mutex_t *mutex); + +#endif // USE_LWP_LOCK + + +#endif // _LOCK_H + diff --git a/wii/libogc/libfat/mem_allocate.h b/wii/libogc/libfat/mem_allocate.h new file mode 100644 index 0000000000..3308807ad0 --- /dev/null +++ b/wii/libogc/libfat/mem_allocate.h @@ -0,0 +1,52 @@ +/* + mem_allocate.h + Memory allocation and destruction calls + Replace these calls with custom allocators if + malloc is unavailable + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _MEM_ALLOCATE_H +#define _MEM_ALLOCATE_H + +#include + +static inline void* _FAT_mem_allocate (size_t size) { + return malloc (size); +} + +static inline void* _FAT_mem_align (size_t size) { +#ifdef __wii__ + return memalign (32, size); +#else + return malloc (size); +#endif +} + +static inline void _FAT_mem_free (void* mem) { + free (mem); +} + +#endif // _MEM_ALLOCATE_H diff --git a/wii/libogc/libfat/partition.c b/wii/libogc/libfat/partition.c new file mode 100644 index 0000000000..1584e0ed59 --- /dev/null +++ b/wii/libogc/libfat/partition.c @@ -0,0 +1,448 @@ +/* + partition.c + Functions for mounting and dismounting partitions + on various block devices. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "partition.h" +#include "bit_ops.h" +#include "file_allocation_table.h" +#include "directory.h" +#include "mem_allocate.h" +#include "fatfile.h" + +#include +#include +#include + +/* +Data offsets +*/ + +// BIOS Parameter Block offsets +enum BPB { + BPB_jmpBoot = 0x00, + BPB_OEMName = 0x03, + // BIOS Parameter Block + BPB_bytesPerSector = 0x0B, + BPB_sectorsPerCluster = 0x0D, + BPB_reservedSectors = 0x0E, + BPB_numFATs = 0x10, + BPB_rootEntries = 0x11, + BPB_numSectorsSmall = 0x13, + BPB_mediaDesc = 0x15, + BPB_sectorsPerFAT = 0x16, + BPB_sectorsPerTrk = 0x18, + BPB_numHeads = 0x1A, + BPB_numHiddenSectors = 0x1C, + BPB_numSectors = 0x20, + // Ext BIOS Parameter Block for FAT16 + BPB_FAT16_driveNumber = 0x24, + BPB_FAT16_reserved1 = 0x25, + BPB_FAT16_extBootSig = 0x26, + BPB_FAT16_volumeID = 0x27, + BPB_FAT16_volumeLabel = 0x2B, + BPB_FAT16_fileSysType = 0x36, + // Bootcode + BPB_FAT16_bootCode = 0x3E, + // FAT32 extended block + BPB_FAT32_sectorsPerFAT32 = 0x24, + BPB_FAT32_extFlags = 0x28, + BPB_FAT32_fsVer = 0x2A, + BPB_FAT32_rootClus = 0x2C, + BPB_FAT32_fsInfo = 0x30, + BPB_FAT32_bkBootSec = 0x32, + // Ext BIOS Parameter Block for FAT32 + BPB_FAT32_driveNumber = 0x40, + BPB_FAT32_reserved1 = 0x41, + BPB_FAT32_extBootSig = 0x42, + BPB_FAT32_volumeID = 0x43, + BPB_FAT32_volumeLabel = 0x47, + BPB_FAT32_fileSysType = 0x52, + // Bootcode + BPB_FAT32_bootCode = 0x5A, + BPB_bootSig_55 = 0x1FE, + BPB_bootSig_AA = 0x1FF +}; + +// File system information block offsets +enum FSIB +{ + FSIB_SIG1 = 0x00, + FSIB_SIG2 = 0x1e4, + FSIB_numberOfFreeCluster = 0x1e8, + FSIB_numberLastAllocCluster = 0x1ec, + FSIB_bootSig_55 = 0x1FE, + FSIB_bootSig_AA = 0x1FF +}; + +static const char FAT_SIG[3] = {'F', 'A', 'T'}; +static const char FS_INFO_SIG1[4] = {'R', 'R', 'a', 'A'}; +static const char FS_INFO_SIG2[4] = {'r', 'r', 'A', 'a'}; + +sec_t FindFirstValidPartition_buf(const DISC_INTERFACE* disc, uint8_t *sectorBuffer) +{ + uint8_t part_table[16*4]; + uint8_t *ptr; + int i; + + // Read first sector of disc + if (!_FAT_disc_readSectors (disc, 0, 1, sectorBuffer)) { + return 0; + } + + memcpy(part_table,sectorBuffer+0x1BE,16*4); + ptr = part_table; + + for(i=0;i<4;i++,ptr+=16) { + sec_t part_lba = u8array_to_u32(ptr, 0x8); + + if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) || + !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) { + return part_lba; + } + + if(ptr[4]==0) continue; + + if(ptr[4]==0x0F) { + sec_t part_lba2=part_lba; + sec_t next_lba2=0; + int n; + + for(n=0;n<8;n++) // max 8 logic partitions + { + if(!_FAT_disc_readSectors (disc, part_lba+next_lba2, 1, sectorBuffer)) return 0; + + part_lba2 = part_lba + next_lba2 + u8array_to_u32(sectorBuffer, 0x1C6) ; + next_lba2 = u8array_to_u32(sectorBuffer, 0x1D6); + + if(!_FAT_disc_readSectors (disc, part_lba2, 1, sectorBuffer)) return 0; + + if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) || + !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) + { + return part_lba2; + } + + if(next_lba2==0) break; + } + } else { + if(!_FAT_disc_readSectors (disc, part_lba, 1, sectorBuffer)) return 0; + if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) || + !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) { + return part_lba; + } + } + } + return 0; +} + +sec_t FindFirstValidPartition(const DISC_INTERFACE* disc) +{ + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(MAX_SECTOR_SIZE); + if (!sectorBuffer) return 0; + sec_t ret = FindFirstValidPartition_buf(disc, sectorBuffer); + _FAT_mem_free(sectorBuffer); + return ret; +} + + +PARTITION* _FAT_partition_constructor_buf (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t sectorsPerPage, sec_t startSector, uint8_t *sectorBuffer) +{ + PARTITION* partition; + + // Read first sector of disc + if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) { + return NULL; + } + + // Make sure it is a valid MBR or boot sector + if ( (sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA)) { + return NULL; + } + + if (startSector != 0) { + // We're told where to start the partition, so just accept it + } else if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) { + // Check if there is a FAT string, which indicates this is a boot sector + startSector = 0; + } else if (!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) { + // Check for FAT32 + startSector = 0; + } else { + startSector = FindFirstValidPartition_buf(disc, sectorBuffer); + if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) { + return NULL; + } + } + + // Now verify that this is indeed a FAT partition + if (memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) && + memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) + { + return NULL; + } + + partition = (PARTITION*) _FAT_mem_allocate (sizeof(PARTITION)); + if (partition == NULL) { + return NULL; + } + + // Init the partition lock + _FAT_lock_init(&partition->lock); + + if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) + strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT16_volumeLabel), 11); + else + strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT32_volumeLabel), 11); + partition->label[11] = '\0'; + + // Set partition's disc interface + partition->disc = disc; + + // Store required information about the file system + partition->fat.sectorsPerFat = u8array_to_u16(sectorBuffer, BPB_sectorsPerFAT); + if (partition->fat.sectorsPerFat == 0) { + partition->fat.sectorsPerFat = u8array_to_u32( sectorBuffer, BPB_FAT32_sectorsPerFAT32); + } + + partition->numberOfSectors = u8array_to_u16( sectorBuffer, BPB_numSectorsSmall); + if (partition->numberOfSectors == 0) { + partition->numberOfSectors = u8array_to_u32( sectorBuffer, BPB_numSectors); + } + + partition->bytesPerSector = u8array_to_u16(sectorBuffer, BPB_bytesPerSector); + if(partition->bytesPerSector < MIN_SECTOR_SIZE || partition->bytesPerSector > MAX_SECTOR_SIZE) { + // Unsupported sector size + _FAT_mem_free(partition); + return NULL; + } + + partition->sectorsPerCluster = sectorBuffer[BPB_sectorsPerCluster]; + partition->bytesPerCluster = partition->bytesPerSector * partition->sectorsPerCluster; + partition->fat.fatStart = startSector + u8array_to_u16(sectorBuffer, BPB_reservedSectors); + + partition->rootDirStart = partition->fat.fatStart + (sectorBuffer[BPB_numFATs] * partition->fat.sectorsPerFat); + partition->dataStart = partition->rootDirStart + + (( u8array_to_u16(sectorBuffer, BPB_rootEntries) * DIR_ENTRY_DATA_SIZE) / partition->bytesPerSector); + + partition->totalSize = ((uint64_t)partition->numberOfSectors - (partition->dataStart - startSector)) * (uint64_t)partition->bytesPerSector; + + //FS info sector + partition->fsInfoSector = startSector + (u8array_to_u16(sectorBuffer, BPB_FAT32_fsInfo) ? u8array_to_u16(sectorBuffer, BPB_FAT32_fsInfo) : 1); + + // Store info about FAT + uint32_t clusterCount = (partition->numberOfSectors - (uint32_t)(partition->dataStart - startSector)) / partition->sectorsPerCluster; + partition->fat.lastCluster = clusterCount + CLUSTER_FIRST - 1; + partition->fat.firstFree = CLUSTER_FIRST; + partition->fat.numberFreeCluster = 0; + partition->fat.numberLastAllocCluster = 0; + + if (clusterCount < CLUSTERS_PER_FAT12) { + partition->filesysType = FS_FAT12; // FAT12 volume + } else if (clusterCount < CLUSTERS_PER_FAT16) { + partition->filesysType = FS_FAT16; // FAT16 volume + } else { + partition->filesysType = FS_FAT32; // FAT32 volume + } + + if (partition->filesysType != FS_FAT32) { + partition->rootDirCluster = FAT16_ROOT_DIR_CLUSTER; + } else { + // Set up for the FAT32 way + partition->rootDirCluster = u8array_to_u32(sectorBuffer, BPB_FAT32_rootClus); + // Check if FAT mirroring is enabled + if (!(sectorBuffer[BPB_FAT32_extFlags] & 0x80)) { + // Use the active FAT + partition->fat.fatStart = partition->fat.fatStart + ( partition->fat.sectorsPerFat * (sectorBuffer[BPB_FAT32_extFlags] & 0x0F)); + } + } + + // Create a cache to use + partition->cache = _FAT_cache_constructor (cacheSize, sectorsPerPage, partition->disc, startSector+partition->numberOfSectors, partition->bytesPerSector); + + // Set current directory to the root + partition->cwdCluster = partition->rootDirCluster; + + // Check if this disc is writable, and set the readOnly property appropriately + partition->readOnly = !(_FAT_disc_features(disc) & FEATURE_MEDIUM_CANWRITE); + + // There are currently no open files on this partition + partition->openFileCount = 0; + partition->firstOpenFile = NULL; + + _FAT_partition_readFSinfo(partition); + + return partition; +} + +PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t sectorsPerPage, sec_t startSector) +{ + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(MAX_SECTOR_SIZE); + if (!sectorBuffer) return NULL; + PARTITION *ret = _FAT_partition_constructor_buf(disc, cacheSize, + sectorsPerPage, startSector, sectorBuffer); + _FAT_mem_free(sectorBuffer); + return ret; +} + + +void _FAT_partition_destructor (PARTITION* partition) { + FILE_STRUCT* nextFile; + + _FAT_lock(&partition->lock); + + // Synchronize open files + nextFile = partition->firstOpenFile; + while (nextFile) { + _FAT_syncToDisc (nextFile); + nextFile = nextFile->nextOpenFile; + } + + // Write out the fs info sector + _FAT_partition_writeFSinfo(partition); + + // Free memory used by the cache, writing it to disc at the same time + _FAT_cache_destructor (partition->cache); + + // Unlock the partition and destroy the lock + _FAT_unlock(&partition->lock); + _FAT_lock_deinit(&partition->lock); + + // Free memory used by the partition + _FAT_mem_free (partition); +} + +PARTITION* _FAT_partition_getPartitionFromPath (const char* path) { + const devoptab_t *devops; + + devops = GetDeviceOpTab (path); + + if (!devops) { + return NULL; + } + + return (PARTITION*)devops->deviceData; +} + +static void _FAT_updateFS_INFO(PARTITION * partition, uint8_t *sectorBuffer) { + partition->fat.numberFreeCluster = _FAT_fat_freeClusterCount(partition); + u32_to_u8array(sectorBuffer, FSIB_numberOfFreeCluster, partition->fat.numberFreeCluster); + u32_to_u8array(sectorBuffer, FSIB_numberLastAllocCluster, partition->fat.numberLastAllocCluster); + _FAT_disc_writeSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer); +} + +void _FAT_partition_createFSinfo(PARTITION * partition) +{ + if(partition->readOnly || partition->filesysType != FS_FAT32) + return; + + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(partition->bytesPerSector); + if (!sectorBuffer) return; + memset(sectorBuffer, 0, partition->bytesPerSector); + + int i; + for(i = 0; i < 4; ++i) + { + sectorBuffer[FSIB_SIG1+i] = FS_INFO_SIG1[i]; + sectorBuffer[FSIB_SIG2+i] = FS_INFO_SIG2[i]; + } + + sectorBuffer[FSIB_bootSig_55] = 0x55; + sectorBuffer[FSIB_bootSig_AA] = 0xAA; + + _FAT_updateFS_INFO(partition,sectorBuffer); + + _FAT_mem_free(sectorBuffer); +} + +void _FAT_partition_readFSinfo(PARTITION * partition) +{ + if(partition->filesysType != FS_FAT32) + return; + + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(partition->bytesPerSector); + if (!sectorBuffer) return; + memset(sectorBuffer, 0, partition->bytesPerSector); + // Read first sector of disc + if (!_FAT_disc_readSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) { + _FAT_mem_free(sectorBuffer); + return; + } + + if(memcmp(sectorBuffer+FSIB_SIG1, FS_INFO_SIG1, 4) != 0 || + memcmp(sectorBuffer+FSIB_SIG2, FS_INFO_SIG2, 4) != 0 || + u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster) == 0) + { + //sector does not yet exist, create one! + _FAT_partition_createFSinfo(partition); + } else { + partition->fat.numberFreeCluster = u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster); + if(partition->fat.numberFreeCluster == 0xffffffff) { + _FAT_updateFS_INFO(partition,sectorBuffer); + partition->fat.numberFreeCluster = u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster); + } + partition->fat.numberLastAllocCluster = u8array_to_u32(sectorBuffer, FSIB_numberLastAllocCluster); + } + _FAT_mem_free(sectorBuffer); +} + +void _FAT_partition_writeFSinfo(PARTITION * partition) +{ + if(partition->filesysType != FS_FAT32) + return; + + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_align(partition->bytesPerSector); + if (!sectorBuffer) return; + memset(sectorBuffer, 0, partition->bytesPerSector); + // Read first sector of disc + if (!_FAT_disc_readSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) { + _FAT_mem_free(sectorBuffer); + return; + } + + if(memcmp(sectorBuffer+FSIB_SIG1, FS_INFO_SIG1, 4) || memcmp(sectorBuffer+FSIB_SIG2, FS_INFO_SIG2, 4)) { + _FAT_mem_free(sectorBuffer); + return; + } + + u32_to_u8array(sectorBuffer, FSIB_numberOfFreeCluster, partition->fat.numberFreeCluster); + u32_to_u8array(sectorBuffer, FSIB_numberLastAllocCluster, partition->fat.numberLastAllocCluster); + + // Write first sector of disc + _FAT_disc_writeSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer); + _FAT_mem_free(sectorBuffer); +} + +uint32_t* _FAT_getCwdClusterPtr(const char* name) { + PARTITION *partition = _FAT_partition_getPartitionFromPath(name); + + if (!partition) { + return NULL; + } + + return &partition->cwdCluster; +} diff --git a/wii/libogc/libfat/partition.h b/wii/libogc/libfat/partition.h new file mode 100644 index 0000000000..ec27a0ebed --- /dev/null +++ b/wii/libogc/libfat/partition.h @@ -0,0 +1,107 @@ +/* + partition.h + Functions for mounting and dismounting partitions + on various block devices. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _PARTITION_H +#define _PARTITION_H + +#include "common.h" +#include "cache.h" +#include "lock.h" + +#define MIN_SECTOR_SIZE 512 +#define MAX_SECTOR_SIZE 4096 + +// Filesystem type +typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE; + +typedef struct { + sec_t fatStart; + uint32_t sectorsPerFat; + uint32_t lastCluster; + uint32_t firstFree; + uint32_t numberFreeCluster; + uint32_t numberLastAllocCluster; +} FAT; + +typedef struct { + const DISC_INTERFACE* disc; + CACHE* cache; + // Info about the partition + FS_TYPE filesysType; + uint64_t totalSize; + sec_t rootDirStart; + uint32_t rootDirCluster; + uint32_t numberOfSectors; + sec_t dataStart; + uint32_t bytesPerSector; + uint32_t sectorsPerCluster; + uint32_t bytesPerCluster; + uint32_t fsInfoSector; + FAT fat; + // Values that may change after construction + uint32_t cwdCluster; // Current working directory cluster + int openFileCount; + struct _FILE_STRUCT* firstOpenFile; // The start of a linked list of files + mutex_t lock; // A lock for partition operations + bool readOnly; // If this is set, then do not try writing to the disc + char label[12]; // Volume label +} PARTITION; + +/* +Mount the supplied device and return a pointer to the struct necessary to use it +*/ +PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t SectorsPerPage, sec_t startSector); + +/* +Dismount the device and free all structures used. +Will also attempt to synchronise all open files to disc. +*/ +void _FAT_partition_destructor (PARTITION* partition); + +/* +Return the partition specified in a path, as taken from the devoptab. +*/ +PARTITION* _FAT_partition_getPartitionFromPath (const char* path); + +/* +Create the fs info sector. +*/ +void _FAT_partition_createFSinfo(PARTITION * partition); + +/* +Read the fs info sector data. +*/ +void _FAT_partition_readFSinfo(PARTITION * partition); + +/* +Write the fs info sector data. +*/ +void _FAT_partition_writeFSinfo(PARTITION * partition); + +#endif // _PARTITION_H diff --git a/wii/libogc/libogc/aram.c b/wii/libogc/libogc/aram.c new file mode 100644 index 0000000000..a306c4564b --- /dev/null +++ b/wii/libogc/libogc/aram.c @@ -0,0 +1,378 @@ +/*------------------------------------------------------------- + +aram.c -- ARAM subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#include +#include +#include +#include "asm.h" +#include "processor.h" +#include "aram.h" +#include "irq.h" +#include "cache.h" + +// DSPCR bits +#define DSPCR_DSPRESET 0x0800 // Reset DSP +#define DSPCR_DSPDMA 0x0200 // ARAM dma in progress, if set +#define DSPCR_DSPINTMSK 0x0100 // * interrupt mask (RW) +#define DSPCR_DSPINT 0x0080 // * interrupt active (RWC) +#define DSPCR_ARINTMSK 0x0040 +#define DSPCR_ARINT 0x0020 +#define DSPCR_AIINTMSK 0x0010 +#define DSPCR_AIINT 0x0008 +#define DSPCR_HALT 0x0004 // halt DSP +#define DSPCR_PIINT 0x0002 // assert DSP PI interrupt +#define DSPCR_RES 0x0001 // reset DSP + +#define AR_ARAMEXPANSION 2 + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +static vu16* const _dspReg = (u16*)0xCC005000; + +static ARCallback __ARDmaCallback = NULL; +static u32 __ARInit_Flag = 0; +static u32 __ARStackPointer = 0; +static u32 __ARFreeBlocks = 0; +static u32 *__ARBlockLen = NULL; + +static u32 __ARInternalSize = 0; +static u32 __ARExpansionSize = 0; +static u32 __ARSize = 0; + +static void __ARHandler(u32 irq,void *ctx); +static void __ARCheckSize(void); +static void __ARClearArea(u32 aramaddr,u32 len); + +ARCallback AR_RegisterCallback(ARCallback callback) +{ + u32 level; + ARCallback old; + + _CPU_ISR_Disable(level); + old = __ARDmaCallback; + __ARDmaCallback = callback; + _CPU_ISR_Restore(level); + return old; +} + +u32 AR_GetDMAStatus() +{ + u32 level,ret; + _CPU_ISR_Disable(level); + ret = ((_dspReg[5]&DSPCR_DSPDMA)==DSPCR_DSPDMA); + _CPU_ISR_Restore(level); + return ret; +} + +u32 AR_Init(u32 *stack_idx_array,u32 num_entries) +{ + u32 level; + u32 aram_base = 0x4000; + + if(__ARInit_Flag) return aram_base; + + _CPU_ISR_Disable(level); + + __ARDmaCallback = NULL; + + IRQ_Request(IRQ_DSP_ARAM,__ARHandler,NULL); + __UnmaskIrq(IRQMASK(IRQ_DSP_ARAM)); + + __ARStackPointer = aram_base; + __ARFreeBlocks = num_entries; + __ARBlockLen = stack_idx_array; + _dspReg[13] = (_dspReg[13]&~0xff)|(_dspReg[13]&0xff); + + __ARCheckSize(); + __ARInit_Flag = 1; + + _CPU_ISR_Restore(level); + return __ARStackPointer; +} + +void AR_StartDMA(u32 dir,u32 memaddr,u32 aramaddr,u32 len) +{ + u32 level; + + _CPU_ISR_Disable(level); + + // set main memory address + _dspReg[16] = (_dspReg[16]&~0x03ff)|_SHIFTR(memaddr,16,16); + _dspReg[17] = (_dspReg[17]&~0xffe0)|_SHIFTR(memaddr, 0,16); + + // set aram address + _dspReg[18] = (_dspReg[18]&~0x03ff)|_SHIFTR(aramaddr,16,16); + _dspReg[19] = (_dspReg[19]&~0xffe0)|_SHIFTR(aramaddr, 0,16); + + // set cntrl bits + _dspReg[20] = (_dspReg[20]&~0x8000)|_SHIFTL(dir,15,1); + _dspReg[20] = (_dspReg[20]&~0x03ff)|_SHIFTR(len,16,16); + _dspReg[21] = (_dspReg[21]&~0xffe0)|_SHIFTR(len, 0,16); + + _CPU_ISR_Restore(level); +} + +u32 AR_Alloc(u32 len) +{ + u32 level; + u32 curraddr; + + _CPU_ISR_Disable(level); + curraddr = __ARStackPointer; + __ARStackPointer += len; + *__ARBlockLen++ = len; + __ARFreeBlocks--; + _CPU_ISR_Restore(level); + + return curraddr; +} + +u32 AR_Free(u32 *len) +{ + u32 level; + + _CPU_ISR_Disable(level); + __ARBlockLen--; + if(len) *len = *__ARBlockLen; + __ARStackPointer -= *__ARBlockLen; + __ARFreeBlocks++; + _CPU_ISR_Restore(level); + + return __ARStackPointer; +} + +void AR_Clear(u32 flag) +{ + switch(flag) { + case AR_ARAMINTALL: + if(__ARInternalSize) + __ARClearArea(0,__ARInternalSize); + break; + case AR_ARAMINTUSER: + if(__ARInternalSize) + __ARClearArea(0x4000,__ARInternalSize-0x4000); + break; + case AR_ARAMEXPANSION: + if(__ARInternalSize && __ARExpansionSize) + __ARClearArea(__ARInternalSize,__ARExpansionSize); + break; + default: + break; + } +} + +BOOL AR_CheckInit() +{ + return __ARInit_Flag; +} + +void AR_Reset() +{ + __ARInit_Flag = 0; +} + +u32 AR_GetSize() +{ + return __ARSize; +} + +u32 AR_GetBaseAddress() +{ + return 0x4000; +} + +u32 AR_GetInternalSize() +{ + return __ARInternalSize; +} + +static __inline__ void __ARClearInterrupt() +{ + u16 cause; + + cause = _dspReg[5]&~(DSPCR_DSPINT|DSPCR_AIINT); + _dspReg[5] = (cause|DSPCR_ARINT); +} + +static __inline__ void __ARWaitDma() +{ + while(_dspReg[5]&DSPCR_DSPDMA); +} + +static void __ARReadDMA(u32 memaddr,u32 aramaddr,u32 len) +{ + // set main memory address + _dspReg[16] = (_dspReg[16]&~0x03ff)|_SHIFTR(memaddr,16,16); + _dspReg[17] = (_dspReg[17]&~0xffe0)|_SHIFTR(memaddr, 0,16); + + // set aram address + _dspReg[18] = (_dspReg[18]&~0x03ff)|_SHIFTR(aramaddr,16,16); + _dspReg[19] = (_dspReg[19]&~0xffe0)|_SHIFTR(aramaddr, 0,16); + + // set cntrl bits + _dspReg[20] = (_dspReg[20]&~0x8000)|0x8000; + _dspReg[20] = (_dspReg[20]&~0x03ff)|_SHIFTR(len,16,16); + _dspReg[21] = (_dspReg[21]&~0xffe0)|_SHIFTR(len, 0,16); + + __ARWaitDma(); + __ARClearInterrupt(); + +} + +static void __ARWriteDMA(u32 memaddr,u32 aramaddr,u32 len) +{ + // set main memory address + _dspReg[16] = (_dspReg[16]&~0x03ff)|_SHIFTR(memaddr,16,16); + _dspReg[17] = (_dspReg[17]&~0xffe0)|_SHIFTR(memaddr, 0,16); + + // set aram address + _dspReg[18] = (_dspReg[18]&~0x03ff)|_SHIFTR(aramaddr,16,16); + _dspReg[19] = (_dspReg[19]&~0xffe0)|_SHIFTR(aramaddr, 0,16); + + // set cntrl bits + _dspReg[20] = (_dspReg[20]&~0x8000); + _dspReg[20] = (_dspReg[20]&~0x03ff)|_SHIFTR(len,16,16); + _dspReg[21] = (_dspReg[21]&~0xffe0)|_SHIFTR(len, 0,16); + + __ARWaitDma(); + __ARClearInterrupt(); +} + +static void __ARClearArea(u32 aramaddr,u32 len) +{ + u32 currlen,curraddr,endaddr; + static u8 zero_buffer[2048] ATTRIBUTE_ALIGN(32); + + while(!(_dspReg[11]&0x0001)); + + memset(zero_buffer,0,2048); + DCFlushRange(zero_buffer,2048); + + curraddr = aramaddr; + endaddr = aramaddr+len; + + currlen = 2048; + while(curraddr + +extern u8 __Arena1Lo[]; +extern char *__argvArena1Lo; +void build_argv (struct __argv* argstruct ); + +void __CheckARGV() { + + if ( __system_argv->argvMagic != ARGV_MAGIC ) { + __system_argv->argc = 0; + __system_argv->argv = NULL; + return; + } + + u8 *dest = (u8 *)( ((int)__Arena1Lo + 3) & ~3); + + memmove(dest, __system_argv->commandLine, __system_argv->length); + __system_argv->commandLine = (char *)dest; + build_argv(__system_argv); + + __argvArena1Lo = (char *)__system_argv->endARGV; + +} diff --git a/wii/libogc/libogc/arqmgr.c b/wii/libogc/libogc/arqmgr.c new file mode 100644 index 0000000000..ed3a99936b --- /dev/null +++ b/wii/libogc/libogc/arqmgr.c @@ -0,0 +1,155 @@ +/*------------------------------------------------------------- + +arqmgr.c -- ARAM task request queue management + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ + + +#include + +#include "asm.h" +#include "processor.h" +#include "arqueue.h" +#include "arqmgr.h" + +#define ARQM_STACKENTRIES 16 +#define ARQM_ZEROBYTES 256 +#define ROUNDUP32(x) (((u32)(x)+0x1f)&~0x1f) + +typedef struct _arqm_info { + ARQRequest arqhandle; + ARQMCallback callback; + void *buffer; + u32 file_len; + u32 read_len; + u32 aram_start; + u32 curr_read_offset; + u32 curr_aram_offset; + volatile BOOL polled; +} ARQM_Info; + +static u32 __ARQMStackLocation; +static u32 __ARQMFreeBytes; +static u32 __ARQMStackPointer[ARQM_STACKENTRIES]; +static ARQM_Info __ARQMInfo[ARQM_STACKENTRIES]; +static u8 __ARQMZeroBuffer[ARQM_ZEROBYTES] ATTRIBUTE_ALIGN(32); + +static void __ARQMPollCallback(ARQRequest *req) +{ + u32 i; + ARQM_Info *ptr = NULL; + + for(i=0;iarqhandle) break; + } + if(i>=ARQM_STACKENTRIES) return; + + ptr->callback = NULL; + ptr->polled = TRUE; +} + +void ARQM_Init(u32 arambase,s32 len) +{ + u32 i; + + if(len<=0) return; + + __ARQMStackLocation = 0; + __ARQMStackPointer[0] = arambase; + __ARQMFreeBytes = len; + + for(i=0;i=rlen && __ARQMStackLocation<(ARQM_STACKENTRIES-1)) { + ptr = &__ARQMInfo[__ARQMStackLocation]; + + _CPU_ISR_Disable(level); + ptr->polled = FALSE; + ptr->aram_start = __ARQMStackPointer[__ARQMStackLocation++]; + __ARQMStackPointer[__ARQMStackLocation] = ptr->aram_start+rlen; + __ARQMFreeBytes -= rlen; + + ARQ_PostRequestAsync(&ptr->arqhandle,__ARQMStackLocation-1,ARQ_MRAMTOARAM,ARQ_PRIO_HI,ptr->aram_start,(u32)buffer,rlen,__ARQMPollCallback); + _CPU_ISR_Restore(level); + + while(ptr->polled==FALSE); + return (ptr->aram_start); + } + return 0; +} + +void ARQM_Pop() +{ + u32 level; + + _CPU_ISR_Disable(level); + + if(__ARQMStackLocation>1) { + __ARQMFreeBytes += (__ARQMStackPointer[__ARQMStackLocation]-__ARQMStackPointer[__ARQMStackLocation-1]); + __ARQMStackLocation--; + } + _CPU_ISR_Restore(level); +} + +u32 ARQM_GetZeroBuffer() +{ + return __ARQMStackPointer[0]; +} + +u32 ARQM_GetStackPointer() +{ + u32 level,tmp; + + _CPU_ISR_Disable(level) + tmp = __ARQMStackPointer[__ARQMStackLocation]; + _CPU_ISR_Restore(level); + + return tmp; +} + +u32 ARQM_GetFreeSize() +{ + u32 level,tmp; + + _CPU_ISR_Disable(level) + tmp = __ARQMFreeBytes; + _CPU_ISR_Restore(level); + + return tmp; +} diff --git a/wii/libogc/libogc/arqueue.c b/wii/libogc/libogc/arqueue.c new file mode 100644 index 0000000000..15b55e0644 --- /dev/null +++ b/wii/libogc/libogc/arqueue.c @@ -0,0 +1,257 @@ +/*------------------------------------------------------------- + +arqueue.c -- ARAM task request queue implementation + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#include +#include +#include + +#include "asm.h" +#include "processor.h" +#include "arqueue.h" + +static u32 __ARQChunkSize; +static u32 __ARQInitFlag = 0; +static lwpq_t __ARQSyncQueue; + +static lwp_queue __ARQReqQueueLo; +static lwp_queue __ARQReqQueueHi; +static ARQRequest *__ARQReqPendingLo; +static ARQRequest *__ARQReqPendingHi; +static ARQCallback __ARQCallbackLo = NULL; +static ARQCallback __ARQCallbackHi = NULL; + +static __inline__ void __ARQPopTaskQueueHi() +{ + ARQRequest *req; + + req = (ARQRequest*)__lwp_queue_getI(&__ARQReqQueueHi); + if(!req) return; + + req->state = ARQ_TASK_RUNNING; + AR_StartDMA(req->dir,req->mram_addr,req->aram_addr,req->len); + __ARQCallbackHi = req->callback; + __ARQReqPendingHi = req; +} + +static void __ARQCallbackDummy(ARQRequest *req) +{ +} + +static void __ARQCallbackSync(ARQRequest *req) +{ + LWP_ThreadBroadcast(__ARQSyncQueue); +} + +static void __ARQServiceQueueLo() +{ + ARQRequest *req; + + if(!__ARQReqPendingLo) { + req = (ARQRequest*)__lwp_queue_getI(&__ARQReqQueueLo); + __ARQReqPendingLo = req; + } + + req = __ARQReqPendingLo; + if(req) { + req->state = ARQ_TASK_RUNNING; + if(req->len<=__ARQChunkSize) { + AR_StartDMA(req->dir,req->mram_addr,req->aram_addr,req->len); + __ARQCallbackLo = __ARQReqPendingLo->callback; + } else { + AR_StartDMA(req->dir,req->mram_addr,req->aram_addr,__ARQChunkSize); + __ARQReqPendingLo->len -= __ARQChunkSize; + __ARQReqPendingLo->aram_addr += __ARQChunkSize; + __ARQReqPendingLo->mram_addr += __ARQChunkSize; + } + } +} + +static void __ARInterruptServiceRoutine() +{ + if(__ARQCallbackHi) { + __ARQReqPendingHi->state = ARQ_TASK_FINISHED; + __ARQCallbackHi(__ARQReqPendingHi); + __ARQReqPendingHi = NULL; + __ARQCallbackHi = NULL; + } else if(__ARQCallbackLo) { + __ARQReqPendingLo->state = ARQ_TASK_FINISHED; + __ARQCallbackLo(__ARQReqPendingLo); + __ARQReqPendingLo = NULL; + __ARQCallbackLo = NULL; + } + __ARQPopTaskQueueHi(); + if(!__ARQReqPendingHi) __ARQServiceQueueLo(); +} + +void ARQ_Init() +{ + u32 level; + if(__ARQInitFlag) return; + + _CPU_ISR_Disable(level); + + __ARQReqPendingLo = NULL; + __ARQReqPendingHi = NULL; + __ARQCallbackLo = NULL; + __ARQCallbackHi = NULL; + + __ARQChunkSize = ARQ_DEF_CHUNK_SIZE; + + LWP_InitQueue(&__ARQSyncQueue); + + __lwp_queue_init_empty(&__ARQReqQueueLo); + __lwp_queue_init_empty(&__ARQReqQueueHi); + + AR_RegisterCallback(__ARInterruptServiceRoutine); + + __ARQInitFlag = 1; + _CPU_ISR_Restore(level); +} + +void ARQ_Reset() +{ + u32 level; + _CPU_ISR_Disable(level); + __ARQInitFlag = 0; + _CPU_ISR_Restore(level); +} + +void ARQ_SetChunkSize(u32 size) +{ + u32 level; + _CPU_ISR_Disable(level); + __ARQChunkSize = (size+31)&~31; + _CPU_ISR_Restore(level); +} + +u32 ARQ_GetChunkSize() +{ + return __ARQChunkSize; +} + +void ARQ_FlushQueue() +{ + u32 level; + + _CPU_ISR_Disable(level); + + __lwp_queue_init_empty(&__ARQReqQueueLo); + __lwp_queue_init_empty(&__ARQReqQueueHi); + if(!__ARQCallbackLo) __ARQReqPendingLo = NULL; + + _CPU_ISR_Restore(level); +} + +void ARQ_PostRequestAsync(ARQRequest *req,u32 owner,u32 dir,u32 prio,u32 aram_addr,u32 mram_addr,u32 len,ARQCallback cb) +{ + u32 level; + ARQRequest *p; + + req->state = ARQ_TASK_READY; + req->dir = dir; + req->owner = owner; + req->aram_addr = aram_addr; + req->mram_addr = mram_addr; + req->len = len; + req->prio = prio; + req->callback = (cb==NULL) ? __ARQCallbackDummy : cb; + + _CPU_ISR_Disable(level); + + if(prio==ARQ_PRIO_LO) __lwp_queue_appendI(&__ARQReqQueueLo,&req->node); + else __lwp_queue_appendI(&__ARQReqQueueHi,&req->node); + + if(!__ARQReqPendingLo && !__ARQReqPendingHi) { + p = (ARQRequest*)__lwp_queue_getI(&__ARQReqQueueHi); + if(p) { + p->state = ARQ_TASK_RUNNING; + AR_StartDMA(p->dir,p->mram_addr,p->aram_addr,p->len); + __ARQCallbackHi = p->callback; + __ARQReqPendingHi = p; + } + if(!__ARQReqPendingHi) __ARQServiceQueueLo(); + } + _CPU_ISR_Restore(level); +} + +void ARQ_PostRequest(ARQRequest *req,u32 owner,u32 dir,u32 prio,u32 aram_addr,u32 mram_addr,u32 len) +{ + u32 level; + + ARQ_PostRequestAsync(req,owner,dir,prio,aram_addr,mram_addr,len,__ARQCallbackSync); + + _CPU_ISR_Disable(level); + while(req->state!=ARQ_TASK_FINISHED) { + LWP_ThreadSleep(__ARQSyncQueue); + } + _CPU_ISR_Restore(level); +} + +void ARQ_RemoveRequest(ARQRequest *req) +{ + u32 level; + + _CPU_ISR_Disable(level); + __lwp_queue_extractI(&req->node); + if(__ARQReqPendingLo && __ARQReqPendingLo==req && __ARQCallbackLo==NULL) __ARQReqPendingLo = NULL; + _CPU_ISR_Restore(level); +} + +u32 ARQ_RemoveOwnerRequest(u32 owner) +{ + u32 level,cnt; + ARQRequest *req; + + _CPU_ISR_Disable(level); + + cnt = 0; + req = (ARQRequest*)__ARQReqQueueHi.first; + while(req!=(ARQRequest*)__lwp_queue_tail(&__ARQReqQueueHi)) { + if(req->owner==owner) { + __lwp_queue_extractI(&req->node); + cnt++; + } + req = (ARQRequest*)req->node.next; + } + + req = (ARQRequest*)__ARQReqQueueLo.first; + while(req!=(ARQRequest*)__lwp_queue_tail(&__ARQReqQueueLo)) { + if(req->owner==owner) { + __lwp_queue_extractI(&req->node); + cnt++; + } + req = (ARQRequest*)req->node.next; + } + if(__ARQReqPendingLo && __ARQReqPendingLo==req && __ARQCallbackLo==NULL) __ARQReqPendingLo = NULL; + _CPU_ISR_Restore(level); + + return cnt; +} + diff --git a/wii/libogc/libogc/audio.c b/wii/libogc/libogc/audio.c new file mode 100644 index 0000000000..037688660a --- /dev/null +++ b/wii/libogc/libogc/audio.c @@ -0,0 +1,386 @@ +/*------------------------------------------------------------- + +audio.c -- Audio subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ + + +#include +#include "asm.h" +#include "processor.h" +#include "irq.h" +#include "audio.h" +#include "lwp_watchdog.h" + +#define STACKSIZE 16384 + +// DSPCR bits +#define DSPCR_DSPRESET 0x0800 // Reset DSP +#define DSPCR_DSPDMA 0x0200 // ARAM dma in progress, if set +#define DSPCR_DSPINTMSK 0x0100 // * interrupt mask (RW) +#define DSPCR_DSPINT 0x0080 // * interrupt active (RWC) +#define DSPCR_ARINTMSK 0x0040 +#define DSPCR_ARINT 0x0020 +#define DSPCR_AIINTMSK 0x0010 +#define DSPCR_AIINT 0x0008 +#define DSPCR_HALT 0x0004 // halt DSP +#define DSPCR_PIINT 0x0002 // assert DSP PI interrupt +#define DSPCR_RES 0x0001 // reset DSP + +// Audio Interface Registers +#define AI_CONTROL 0 +#define AI_STREAM_VOL 1 +#define AI_SAMPLE_COUNT 2 +#define AI_INT_TIMING 3 + +#define AI_PSTAT 0x01 +#define AI_AISFR 0x02 +#define AI_AIINTMSK 0x04 +#define AI_AIINT 0x08 +#define AI_AIINTVLD 0x10 +#define AI_SCRESET 0x20 +#define AI_DMAFR 0x40 + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +#if defined(HW_DOL) + static vu32* const _aiReg = (u32*)0xCC006C00; +#elif defined(HW_RVL) + static vu32* const _aiReg = (u32*)0xCD006C00; +#else + #error HW model not supported. +#endif + +static vu16* const _dspReg = (u16*)0xCC005000; + +static u32 __AIInitFlag = 0; +static u32 __AIActive = 0; +static u8 *__CallbackStack __attribute__((used)) = NULL; +static u8 *__OldStack __attribute__((used)) = NULL; + +static u64 bound_32KHz,bound_48KHz,min_wait,max_wait,buffer; + +#if defined(HW_DOL) +static AISCallback __AIS_Callback; +#endif +static AIDCallback __AID_Callback; + +static void __AICallbackStackSwitch(AIDCallback handler) +{ + __asm__ __volatile__("mflr %r0\n\t\ + stw %r0,4(%r1)\n\t\ + stwu %r1,-24(%r1)\n\t\ + stw %r31,20(%r1)\n\t\ + mr %r31,%r3\n\t\ + lis %r5,__OldStack@ha\n\t\ + addi %r5,%r5,__OldStack@l\n\t\ + stw %r1,0(%r5)\n\t\ + lis %r5,__CallbackStack@ha\n\t\ + addi %r5,%r5,__CallbackStack@l\n\t\ + lwz %r1,0(%r5)\n\t\ + subi %r1,%r1,8\n\t\ + mtlr %r31\n\t\ + blrl\n\t\ + lis %r5,__OldStack@ha\n\t\ + addi %r5,%r5,__OldStack@l\n\t\ + lwz %r1,0(%r5)\n\t\ + lwz %r0,28(%r1)\n\t\ + lwz %r31,20(%r1)\n\t\ + addi %r1,%r1,24\n\t\ + mtlr %r0\n" + ); +} + +#if defined(HW_DOL) +static void __AISHandler(u32 nIrq,void *pCtx) +{ + if(__AIS_Callback) + __AIS_Callback(_aiReg[AI_SAMPLE_COUNT]); + _aiReg[AI_CONTROL] |= AI_AIINT; +} +#endif + +static void __AIDHandler(u32 nIrq,void *pCtx) +{ + _dspReg[5] = (_dspReg[5]&~(DSPCR_DSPINT|DSPCR_ARINT))|DSPCR_AIINT; + if(__AID_Callback) { + if(!__AIActive) { + __AIActive = 1; + if(__CallbackStack) + __AICallbackStackSwitch(__AID_Callback); + else + __AID_Callback(); + __AIActive = 0; + } + } +} + +static void __AISRCINIT() +{ + int done = 0; + u32 sample_counter; + u64 time1, time2, tdiff; + u64 wait = 0; + + while (!done) { + _aiReg[AI_CONTROL] |= AI_SCRESET; + _aiReg[AI_CONTROL] &= ~AI_AISFR; + _aiReg[AI_CONTROL] |= AI_PSTAT; + +#ifdef HW_DOL + sample_counter = _aiReg[AI_SAMPLE_COUNT]; + while (sample_counter == _aiReg[AI_SAMPLE_COUNT]) {} +#else + sample_counter = _aiReg[AI_SAMPLE_COUNT] & 0x7fffffff; + while (sample_counter == (_aiReg[AI_SAMPLE_COUNT] & 0x7fffffff)) {} +#endif + + time1 = gettime(); + + _aiReg[AI_CONTROL] |= AI_AISFR; + _aiReg[AI_CONTROL] |= AI_PSTAT; + +#ifdef HW_DOL + sample_counter = _aiReg[AI_SAMPLE_COUNT]; + while (sample_counter == _aiReg[AI_SAMPLE_COUNT]) {} +#else + sample_counter = _aiReg[AI_SAMPLE_COUNT] & 0x7fffffff; + while (sample_counter == (_aiReg[AI_SAMPLE_COUNT] & 0x7fffffff)) {} +#endif + + time2 = gettime(); + tdiff = time2 - time1; + + _aiReg[AI_CONTROL] &= ~AI_AISFR; + _aiReg[AI_CONTROL] &= ~AI_PSTAT; + + if ((tdiff > (bound_32KHz - buffer)) && + (tdiff < (bound_32KHz + buffer))) { + if (tdiff < (bound_48KHz - buffer)) { + wait = max_wait; + done = 1; + } + } else { + wait = min_wait; + done = 1; + } + } + + while (diff_ticks(time2, gettime()) < wait) {} +} + +void AUDIO_Init(u8 *stack) +{ + u32 rate,level; + + if(!__AIInitFlag) { + bound_32KHz = nanosecs_to_ticks(31524); + bound_48KHz = nanosecs_to_ticks(42024); + min_wait = nanosecs_to_ticks(42000); + max_wait = nanosecs_to_ticks(63000); + buffer = nanosecs_to_ticks(3000); + + _aiReg[AI_CONTROL] &= ~(AI_AIINTVLD|AI_AIINTMSK|AI_PSTAT); + _aiReg[1] = 0; + _aiReg[3] = 0; + + _aiReg[AI_CONTROL] = (_aiReg[AI_CONTROL]&~AI_SCRESET)|AI_SCRESET; + + rate = (_SHIFTR(_aiReg[AI_CONTROL],6,1))^1; + if(rate==AI_SAMPLERATE_48KHZ) { + _aiReg[AI_CONTROL] &= ~AI_DMAFR; + _CPU_ISR_Disable(level); + __AISRCINIT(); + _aiReg[AI_CONTROL] |= AI_DMAFR; + _CPU_ISR_Restore(level); + } + + __AID_Callback = NULL; + + __OldStack = NULL; // davem - use it or lose it + // looks like 3.4 isn't picking up the use from the asm below + __CallbackStack = stack; + + IRQ_Request(IRQ_DSP_AI,__AIDHandler,NULL); + __UnmaskIrq(IRQMASK(IRQ_DSP_AI)); +#if defined(HW_DOL) + __AIS_Callback = NULL; + + IRQ_Request(IRQ_AI,__AISHandler,NULL); + __UnmaskIrq(IRQMASK(IRQ_AI)); +#endif + __AIInitFlag = 1; + } +} + +#if defined(HW_DOL) +void AUDIO_SetStreamVolLeft(u8 vol) +{ + _aiReg[1] = (_aiReg[1]&~0x000000ff)|(vol&0xff); +} + +u8 AUDIO_GetStreamVolLeft() +{ + return (u8)(_aiReg[1]&0xff); +} + +void AUDIO_SetStreamVolRight(u8 vol) +{ + _aiReg[1] = (_aiReg[1]&~0x0000ff00)|(_SHIFTL(vol,8,8)); +} + +u8 AUDIO_GetStreamVolRight() +{ + return (u8)(_SHIFTR(_aiReg[1],8,8)); +} + +void AUDIO_SetStreamSampleRate(u32 rate) +{ + _aiReg[AI_CONTROL] = (_aiReg[AI_CONTROL]&~AI_AISFR)|(_SHIFTL(rate,1,1)); +} + +u32 AUDIO_GetStreamSampleRate() +{ + return _SHIFTR(_aiReg[AI_CONTROL],1,1); +} + +void AUDIO_SetStreamTrigger(u32 cnt) +{ + _aiReg[3] = cnt; +} + +void AUDIO_ResetStreamSampleCnt() +{ + _aiReg[AI_CONTROL] = (_aiReg[AI_CONTROL]&~AI_SCRESET)|AI_SCRESET; +} + +void AUDIO_SetStreamPlayState(u32 state) +{ + u32 playstate,streamrate; + u32 volright,volleft,level; + + playstate = AUDIO_GetStreamPlayState(); + streamrate = AUDIO_GetStreamSampleRate(); + if(playstate!=state && state==AI_STREAM_START && streamrate==AI_SAMPLERATE_32KHZ ) { + volright = AUDIO_GetStreamVolRight(); + AUDIO_SetStreamVolRight(0); + volleft = AUDIO_GetStreamVolLeft(); + AUDIO_SetStreamVolLeft(0); + + _CPU_ISR_Disable(level); + __AISRCINIT(); + _aiReg[AI_CONTROL] = (_aiReg[AI_CONTROL]&~AI_SCRESET)|AI_SCRESET; + _aiReg[AI_CONTROL] = (_aiReg[AI_CONTROL]&~0x01)|0x01; + _CPU_ISR_Restore(level); + AUDIO_SetStreamVolRight(volright); + AUDIO_SetStreamVolLeft(volleft); + } else { + _aiReg[AI_CONTROL] = (_aiReg[AI_CONTROL]&~AI_PSTAT)|(state&AI_PSTAT); + } +} + +u32 AUDIO_GetStreamPlayState() +{ + return (_aiReg[AI_CONTROL]&AI_PSTAT); +} +#endif + +AIDCallback AUDIO_RegisterDMACallback(AIDCallback callback) +{ + u32 level; + AIDCallback old; + + _CPU_ISR_Disable(level); + old = __AID_Callback; + __AID_Callback = callback; + _CPU_ISR_Restore(level); + return old; +} + +void AUDIO_InitDMA(u32 startaddr,u32 len) +{ + u32 level; + + _CPU_ISR_Disable(level); + _dspReg[24] = (_dspReg[24]&~0x1fff)|(_SHIFTR(startaddr,16,13)); + _dspReg[25] = (_dspReg[25]&~0xffe0)|(startaddr&0xffff); + _dspReg[27] = (_dspReg[27]&~0x7fff)|(_SHIFTR(len,5,15)); + _CPU_ISR_Restore(level); +} + +u16 AUDIO_GetDMAEnableFlag() +{ + return (_SHIFTR(_dspReg[27],15,1)); +} + +void AUDIO_StartDMA() +{ + _dspReg[27] = (_dspReg[27]&~0x8000)|0x8000; +} + +void AUDIO_StopDMA() +{ + _dspReg[27] = (_dspReg[27]&~0x8000); +} + +u32 AUDIO_GetDMABytesLeft() +{ + return (_SHIFTL(_dspReg[29],5,15)); +} + +u32 AUDIO_GetDMAStartAddr() +{ + return (_SHIFTL((_dspReg[24]&0x1fff),16,13)|(_dspReg[25]&0xffe0)); +} + +u32 AUDIO_GetDMALength() +{ + return ((_dspReg[27]&0x7fff)<<5); +} + +void AUDIO_SetDSPSampleRate(u8 rate) +{ + u32 level; + + if(AUDIO_GetDSPSampleRate()!=rate) { + _aiReg[AI_CONTROL] &= ~AI_DMAFR; + if(rate==AI_SAMPLERATE_32KHZ) { + _CPU_ISR_Disable(level); + __AISRCINIT(); + _aiReg[AI_CONTROL] |= AI_DMAFR; + _CPU_ISR_Restore(level); + } + } +} + +u32 AUDIO_GetDSPSampleRate() +{ + return (_SHIFTR(_aiReg[AI_CONTROL],6,1))^1; //0^1(1) = 48Khz, 1^1(0) = 32Khz +} diff --git a/wii/libogc/libogc/cache.c b/wii/libogc/libogc/cache.c new file mode 100644 index 0000000000..e9b35ade13 --- /dev/null +++ b/wii/libogc/libogc/cache.c @@ -0,0 +1,177 @@ +/*------------------------------------------------------------- + +cache.c -- Cache interface + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#include +#include +#include "cache.h" + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +extern void __LCEnable(); +extern void L2GlobalInvalidate(); +extern void L2Enable(); + +void LCEnable() +{ + u32 level; + + _CPU_ISR_Disable(level); + __LCEnable(); + _CPU_ISR_Restore(level); +} + +u32 LCLoadData(void *dstAddr,void *srcAddr,u32 nCount) +{ + u32 cnt,blocks; + + if((s32)nCount<=0) return 0; + + cnt = (nCount+31)>>5; + blocks = (cnt+127)>>7; + while(cnt) { + if(cnt<0x80) { + LCLoadBlocks(dstAddr,srcAddr,cnt); + cnt = 0; + break; + } + LCLoadBlocks(dstAddr,srcAddr,0); + cnt -= 128; + dstAddr += 4096; + srcAddr += 4096; + } + return blocks; +} + +u32 LCStoreData(void *dstAddr,void *srcAddr,u32 nCount) +{ + u32 cnt,blocks; + + if((s32)nCount<=0) return 0; + + cnt = (nCount+31)>>5; + blocks = (cnt+127)>>7; + while(cnt) { + if(cnt<0x80) { + LCStoreBlocks(dstAddr,srcAddr,cnt); + cnt = 0; + break; + } + LCStoreBlocks(dstAddr,srcAddr,0); + cnt -= 128; + dstAddr += 4096; + srcAddr += 4096; + } + return blocks; +} + +u32 LCQueueLength() +{ + u32 hid2 = mfspr(920); + return _SHIFTR(hid2,4,4); +} + +u32 LCQueueWait(u32 len) +{ + len++; + while(_SHIFTR(mfspr(920),4,4)>=len); + return len; +} + +void LCFlushQueue() +{ + mtspr(922,0); + mtspr(923,1); + ppcsync(); +} + +void LCAlloc(void *addr,u32 bytes) +{ + u32 level; + u32 cnt = bytes>>5; + u32 hid2 = mfspr(920); + if(!(hid2&0x10000000)) { + _CPU_ISR_Disable(level); + __LCEnable(); + _CPU_ISR_Restore(level); + } + LCAllocTags(TRUE,addr,cnt); +} + +void LCAllocNoInvalidate(void *addr,u32 bytes) +{ + u32 level; + u32 cnt = bytes>>5; + u32 hid2 = mfspr(920); + if(!(hid2&0x10000000)) { + _CPU_ISR_Disable(level); + __LCEnable(); + _CPU_ISR_Restore(level); + } + LCAllocTags(FALSE,addr,cnt); +} +#ifdef HW_RVL +void L2Enhance() +{ + u32 level, hid4; + u32 *stub = (u32*)0x80001800; + _CPU_ISR_Disable(level); + hid4 = mfspr(HID4); + // make sure H4A is set before doing anything + if (hid4 & 0x80000000) { + // There's no easy way to flush only L2, so just flush everything + // L2GlobalInvalidate will take care of syncing + DCFlushRangeNoSync((void*)0x80000000, 0x01800000); + DCFlushRangeNoSync((void*)0x90000000, 0x04000000); + + // Invalidate L2 (this will disable it first) + L2GlobalInvalidate(); + // set bits: L2FM=01, BCO=1, L2MUM=1 + hid4 |= 0x24200000; + mtspr(HID4, hid4); + // Re-enable L2 + L2Enable(); + + // look for HBC stub (STUBHAXX) + if (stub[1]==0x53545542 && stub[2]==0x48415858) { + // look for a HID4 write + for (stub += 3; (u32)stub < 0x80003000; stub++) { + if ((stub[0] & 0xFC1FFFFF)==0x7C13FBA6) { + write32((u32)stub, 0x60000000); + break; + } + } + } + } + _CPU_ISR_Restore(level); +} +#endif diff --git a/wii/libogc/libogc/cache_asm.S b/wii/libogc/libogc/cache_asm.S new file mode 100644 index 0000000000..a6e39de916 --- /dev/null +++ b/wii/libogc/libogc/cache_asm.S @@ -0,0 +1,471 @@ +/*------------------------------------------------------------- + +cache_asm.S -- Cache interface + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ + + +#include + + .globl DCFlashInvalidate +DCFlashInvalidate: + mfspr r3,HID0 + ori r3,r3,0x0400 + mtspr HID0,r3 + isync + blr + + .globl DCEnable +DCEnable: + mfspr r3,HID0 + ori r3,r3,0x4000 + mtspr HID0,r3 + isync + blr + + .globl DCDisable +DCDisable: + mfspr r3,HID0 + rlwinm r3,r3,0,18,16 + mtspr HID0,r3 + isync + blr + + .globl DCFreeze +DCFreeze: + mfspr r3,HID0 + ori r3,r3,0x1000 + mtspr HID0,r3 + isync + blr + + .globl DCUnfreeze +DCUnfreeze: + mfspr r3,HID0 + rlwinm r3,r3,0,20,18 + mtspr HID0,r3 + isync + blr + + .globl DCTouchLoad +DCTouchLoad: + dcbt r0,r3 + blr + + .globl DCBlockZero +DCBlockZero: + dcbz r0,r3 + blr + + .globl DCBlockStore +DCBlockStore: + dcbst r0,r3 + blr + + .globl DCBlockFlush +DCBlockFlush: + dcbf r0,r3 + blr + + .globl DCBlockInvalidate +DCBlockInvalidate: + dcbi r0,r3 + blr + + .globl DCInvalidateRange +DCInvalidateRange: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + dcbi r0, r3 + addi r3, r3, 0x20 + bdnz 2b + blr + + .globl DCFlushRange +DCFlushRange: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + dcbf r0, r3 + addi r3, r3, 0x20 + bdnz 2b + sc + blr + + .globl DCStoreRange +DCStoreRange: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + dcbst r0, r3 + addi r3, r3, 0x20 + bdnz 2b + sc + blr + + .globl DCFlushRangeNoSync +DCFlushRangeNoSync: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + dcbf r0, r3 + addi r3, r3, 0x20 + bdnz 2b + blr + + .globl DCStoreRangeNoSync +DCStoreRangeNoSync: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + dcbst r0, r3 + addi r3, r3, 0x20 + bdnz 2b + blr + + .globl DCZeroRange +DCZeroRange: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + dcbz r0, r3 + addi r3, r3, 0x20 + bdnz 2b + blr + + .globl DCTouchRange +DCTouchRange: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + dcbt r0, r3 + addi r3, r3, 0x20 + bdnz 2b + blr + + .globl ICInvalidateRange +ICInvalidateRange: + cmplwi r4, 0 # zero or negative size? + blelr + clrlwi. r5, r3, 27 # check for lower bits set in address + beq 1f + addi r4, r4, 0x20 +1: + addi r4, r4, 0x1f + srwi r4, r4, 5 + mtctr r4 +2: + icbi r0, r3 + addi r3, r3, 0x20 + bdnz 2b + sync + isync + blr + + .globl ICFlashInvalidate +ICFlashInvalidate: + mfspr r3,HID0 + ori r3,r3,0x0800 + mtspr HID0,r3 + isync + blr + + .globl ICEnable +ICEnable: + mfspr r3,HID0 + ori r3,r3,0x8000 + mtspr HID0,r3 + isync + blr + + .globl ICDisable +ICDisable: + mfspr r3,HID0 + rlwinm r3,r3,0,17,15 + mtspr HID0,r3 + isync + blr + + .globl ICFreeze +ICFreeze: + mfspr r3,HID0 + ori r3,r3,0x2000 + mtspr HID0,r3 + isync + blr + + .globl ICUnfreeze +ICUnfreeze: + mfspr r3,HID0 + rlwinm r3,r3,0,19,17 + mtspr HID0,r3 + isync + blr + + .globl ICBlockInvalidate +ICBlockInvalidate: + icbi r0,r3 + blr + + .globl ICSync +ICSync: + isync + blr + + .globl L2Init +L2Init: + mflr r0 + stw r0,4(sp) + stwu sp,-16(sp) + stw r31,12(sp) + mfmsr r3 + mr r31,r3 + sync + li r3,48 + mtmsr r3 + sync + bl L2Disable + bl L2GlobalInvalidate + mr r3,r31 + mtmsr r3 + lwz r0,20(sp) + lwz r31,12(sp) + mtlr r0 + blr + + .globl L2Enable +L2Enable: + sync + mfspr r3,L2CR; + oris r0,r3,0x8000 + rlwinm r3,r0,0,11,9 + mtspr L2CR,r3 + sync + blr + + .globl L2Disable +L2Disable: + sync + mfspr r3,L2CR + clrlwi r3,r3,1 + mtspr L2CR,r3 + sync + blr + + .globl L2GlobalInvalidate +L2GlobalInvalidate: + mflr r0 + stw r0,4(sp) + stwu sp,-8(sp) + bl L2Disable + mfspr r3,L2CR + oris r3,r3,0x0020 + mtspr L2CR,r3 +1: mfspr r3,L2CR + clrlwi r0,r3,31 + cmplwi r0,0x0000 + bne 1b + mfspr r3,L2CR + rlwinm r3,r3,0,11,9 + mtspr L2CR,r3 +2: mfspr r3,L2CR + clrlwi r0,r3,31 + cmplwi r0,0x0000 + bne 2b + lwz r0,12(sp) + addi sp,sp,8 + mtlr r0 + blr + + .globl __LCEnable +__LCEnable: + mfmsr r5 + ori r5,r5,0x1000 + mtmsr r5 + lis r3,0x8000 + li r4,1024 + mtctr r4 +1: dcbt r0,r3 + dcbst r0,r3 + addi r3,r3,32 + bdnz 1b + mfspr r4,HID2 + oris r4,r4,0x100f + mtspr HID2,r4 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + lis r3,0xe000 + ori r3,r3,0x0002 + mtspr DBAT3L,r3 + ori r3,r3,0x01fe + mtspr DBAT3U,r3 + isync + lis r3,0xe000 + li r6,512 + mtctr r6 + li r6,0 +2: dcbz_l r6,r3 + addi r3,r3,32 + bdnz 2b + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + blr + + .globl LCDisable +LCDisable: + lis r3,0xe000 + li r4,512 + mtctr r4 +1: dcbi r0,r3 + addi r3,r3,32 + bdnz 1b + mfspr r4,HID2 + rlwinm r4,r4,0,4,2 + mtspr HID2,r4 + blr + + .globl LCAllocOneTag +LCAllocOneTag: + cmpwi r3,0 + beq 1f + dcbi r0,r4 +1: dcbz_l r0,r4 + blr + + .globl LCAllocTags +LCAllocTags: + mflr r6 + cmplwi r5,0 + ble 2f + mtctr r5 + cmpwi r3,0 + beq 3f +1: dcbi r0,r4 + dcbz_l r0,r4 + addi r4,r4,32 + bdnz 1b + b 2f +3: dcbz_l r0,r4 + addi r4,r4,32 + bdnz 3b +2: mtlr r6 + blr + + .globl LCLoadBlocks +LCLoadBlocks: + extrwi r6,r5,5,25 + clrlwi r4,r4,4 + or r6,r6,r4 + mtspr DMAU,r6 + clrlslwi r6,r5,30,2 + or r6,r6,r3 + ori r6,r6,0x0012 + mtspr DMAL,r6 + blr + + .globl LCStoreBlocks +LCStoreBlocks: + extrwi r6,r5,5,25 + clrlwi r4,r4,4 + or r6,r6,r3 + mtspr DMAU,r6 + clrlslwi r6,r5,30,2 + or r6,r6,r4 + ori r6,r6,0x0002 + mtspr DMAL,r6 + blr + diff --git a/wii/libogc/libogc/card.c b/wii/libogc/libogc/card.c new file mode 100644 index 0000000000..6afaa6e2d7 --- /dev/null +++ b/wii/libogc/libogc/card.c @@ -0,0 +1,2977 @@ +/*------------------------------------------------------------- + +card.c -- Memory card subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#include +#include +#include +#include +#include +#include +#include "asm.h" +#include "processor.h" +#include "system.h" +#include "ogcsys.h" +#include "cache.h" +#include "dsp.h" +#include "lwp.h" +#include "exi.h" +#include "card.h" + +#define CARD_SYSAREA 5 +#define CARD_SYSDIR 0x2000 +#define CARD_SYSDIR_BACK 0x4000 +#define CARD_SYSBAT 0x6000 +#define CARD_SYSBAT_BACK 0x8000 + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) +#define _ROTL(v,s) \ + (((u32)v<>(0x20-s))) + +#define CARD_STATUS_UNLOCKED 0x40 + +struct card_header { + u32 serial[0x08]; + u16 device_id; + u16 size; + u16 encoding; + u8 padding[0x1d6]; + u16 chksum1; + u16 chksum2; +} ATTRIBUTE_PACKED; + +struct card_direntry { + u8 gamecode[4]; + u8 company[2]; + u8 pad_00; + u8 bannerfmt; + u8 filename[CARD_FILENAMELEN]; + u32 lastmodified; + u32 iconaddr; + u16 iconfmt; + u16 iconspeed; + u8 permission; + u8 copytimes; + u16 block; + u16 length; + u16 pad_01; + u32 commentaddr; +} ATTRIBUTE_PACKED; + +struct card_dat { // dir allocation table + struct card_direntry entries[CARD_MAXFILES]; +}; + +struct card_dircntrl { + u8 pad[58]; + u16 updated; + u16 chksum1; + u16 chksum2; +} ATTRIBUTE_PACKED; + +struct card_bat { + u16 chksum1; + u16 chksum2; + u16 updated; + u16 freeblocks; + u16 lastalloc; + u16 fat[0xffc]; +} ATTRIBUTE_PACKED; + +typedef struct _card_block { + u8 cmd[9]; + u32 cmd_len; + u32 cmd_mode; + u32 cmd_blck_cnt; + u32 cmd_sector_addr; + u32 cmd_retries; + u32 attached; + s32 result; + u32 cid; + u16 card_size; + u32 mount_step; + u32 format_step; + u32 sector_size; + u16 blocks; + u32 latency; + u32 cipher; + u32 key[3]; + u32 transfer_cnt; + u16 curr_fileblock; + card_file *curr_file; + struct card_dat *curr_dir; + struct card_bat *curr_fat; + void *workarea; + void *cmd_usr_buf; + lwpq_t wait_sync_queue; + syswd_t timeout_svc; + dsptask_t dsp_task; + + cardcallback card_ext_cb; + cardcallback card_tx_cb; + cardcallback card_exi_cb; + cardcallback card_api_cb; + cardcallback card_xfer_cb; + cardcallback card_erase_cb; + cardcallback card_unlock_cb; +} card_block; + +#if defined(HW_RVL) + +static u32 _cardunlockdata[0x160] ATTRIBUTE_ALIGN(32) = +{ + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000021,0x02ff0021, + 0x13061203,0x12041305,0x009200ff,0x0088ffff, + 0x0089ffff,0x008affff,0x008bffff,0x8f0002bf, + 0x008816fc,0xdcd116fd,0x000016fb,0x000102bf, + 0x008e25ff,0x0380ff00,0x02940027,0x02bf008e, + 0x1fdf24ff,0x02403fff,0x00980400,0x009a0010, + 0x00990000,0x8e0002bf,0x009402bf,0x864402bf, + 0x008816fc,0xdcd116fd,0x000316fb,0x00018f00, + 0x02bf008e,0x0380cdd1,0x02940048,0x27ff0380, + 0x00010295,0x005a0380,0x00020295,0x8000029f, + 0x00480021,0x8e0002bf,0x008e25ff,0x02bf008e, + 0x25ff02bf,0x008e25ff,0x02bf008e,0x00c5ffff, + 0x03403fff,0x1c9f02bf,0x008e00c7,0xffff02bf, + 0x008e00c6,0xffff02bf,0x008e00c0,0xffff02bf, + 0x008e20ff,0x03403fff,0x1f5f02bf,0x008e21ff, + 0x02bf008e,0x23ff1205,0x1206029f,0x80b50021, + 0x27fc03c0,0x8000029d,0x008802df,0x27fe03c0, + 0x8000029c,0x008e02df,0x2ece2ccf,0x00f8ffcd, + 0x00f9ffc9,0x00faffcb,0x26c902c0,0x0004029d, + 0x009c02df,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000 +}; + +#elif defined(HW_DOL) + +static u32 _cardunlockdata[0x160] ATTRIBUTE_ALIGN(32) = +{ + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000021,0x02ff0021, + 0x13061203,0x12041305,0x009200ff,0x0088ffff, + 0x0089ffff,0x008affff,0x008bffff,0x8f0002bf, + 0x008816fc,0xdcd116fd,0x000016fb,0x000102bf, + 0x008e25ff,0x0380ff00,0x02940027,0x02bf008e, + 0x1fdf24ff,0x02400fff,0x00980400,0x009a0010, + 0x00990000,0x8e0002bf,0x009402bf,0x864402bf, + 0x008816fc,0xdcd116fd,0x000316fb,0x00018f00, + 0x02bf008e,0x0380cdd1,0x02940048,0x27ff0380, + 0x00010295,0x005a0380,0x00020295,0x8000029f, + 0x00480021,0x8e0002bf,0x008e25ff,0x02bf008e, + 0x25ff02bf,0x008e25ff,0x02bf008e,0x00c5ffff, + 0x03400fff,0x1c9f02bf,0x008e00c7,0xffff02bf, + 0x008e00c6,0xffff02bf,0x008e00c0,0xffff02bf, + 0x008e20ff,0x03400fff,0x1f5f02bf,0x008e21ff, + 0x02bf008e,0x23ff1205,0x1206029f,0x80b50021, + 0x27fc03c0,0x8000029d,0x008802df,0x27fe03c0, + 0x8000029c,0x008e02df,0x2ece2ccf,0x00f8ffcd, + 0x00f9ffc9,0x00faffcb,0x26c902c0,0x0004029d, + 0x009c02df,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000 +}; +#endif + +static u32 card_sector_size[] = +{ + 0x0002000, + 0x0004000, + 0x0008000, + 0x0010000, + 0x0020000, + 0x0040000, + 0x0000000, + 0x0000000 +}; + +static u32 card_latency[] = +{ + 0x00000004, + 0x00000008, + 0x00000010, + 0x00000020, + 0x00000030, + 0x00000080, + 0x00000100, + 0x00000200 +}; + +static u32 card_inited = 0; +static u32 crand_next = 1; + +static u8 card_gamecode[4] = {0xff,0xff,0xff,0xff}; +static u8 card_company[2] = {0xff,0xff}; +static card_block cardmap[2]; + +static void __card_mountcallback(s32 chn,s32 result); +static void __erase_callback(s32 chn,s32 result); +static s32 __dounlock(s32 chn,u32 *key); +static s32 __card_readsegment(s32 chn,cardcallback callback); +static s32 __card_read(s32 chn,u32 address,u32 block_len,void *buffer,cardcallback callback); +static s32 __card_updatefat(s32 chn,struct card_bat *fatblock,cardcallback callback); +static s32 __card_updatedir(s32 chn,cardcallback callback); +static s32 __card_write(s32 chn,u32 address,u32 block_len,void *buffer,cardcallback callback); +static s32 __card_writepage(s32 chn,cardcallback callback); +static s32 __card_sectorerase(s32 chn,u32 sector,cardcallback callback); +static s32 __card_onreset(s32 final); + +static sys_resetinfo card_resetinfo = { + {}, + __card_onreset, + 127 +}; + +extern unsigned long gettick(); +extern long long gettime(); +extern syssram* __SYS_LockSram(); +extern syssramex* __SYS_LockSramEx(); +extern u32 __SYS_UnlockSram(u32 write); +extern u32 __SYS_UnlockSramEx(u32 write); + +static vu16* const _viReg = (u16*)0xCC002000; + +/* new api */ +static s32 __card_onreset(s32 final) +{ + if(final==FALSE) { + if(CARD_Unmount(CARD_SLOTA)==-1) return 0; + if(CARD_Unmount(CARD_SLOTB)==-1) return 0; + } + return 1; +} + +static void __card_checksum(u16 *buff,u32 len,u16 *cs1,u16 *cs2) +{ + u32 i; + *cs1 = 0; + *cs2 = 0; + len /= 2; + for (i = 0; i < len; ++i) { + *cs1 += buff[i]; + *cs2 += (buff[i] ^ 0xffff); + } + if (*cs1 == 0xffff) *cs1 = 0; + if (*cs2 == 0xffff) *cs2 = 0; +} + +static s32 __card_putcntrlblock(card_block *card,s32 result) +{ + u32 level; + + _CPU_ISR_Disable(level); + if(card->attached) card->result = result; + else if(card->result==CARD_ERROR_BUSY) card->result = result; + _CPU_ISR_Restore(level); + return result; +} + +static s32 __card_getcntrlblock(s32 chn,card_block **card) +{ + s32 ret; + u32 level; + card_block *rcard = NULL; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_FATAL_ERROR; + + _CPU_ISR_Disable(level); + rcard = &cardmap[chn]; + if(!rcard->attached) { + _CPU_ISR_Restore(level); + return CARD_ERROR_NOCARD; + } + + ret = CARD_ERROR_BUSY; + if(rcard->result!=CARD_ERROR_BUSY) { + rcard->result = CARD_ERROR_BUSY; + rcard->card_api_cb = NULL; + *card = rcard; + ret = CARD_ERROR_READY; + } + _CPU_ISR_Restore(level); + return ret; +} + +static __inline__ struct card_dat* __card_getdirblock(card_block *card) +{ + return card->curr_dir; +} + +static __inline__ struct card_bat* __card_getbatblock(card_block *card) +{ + return card->curr_fat; +} + +static s32 __card_sync(s32 chn) +{ + s32 ret; + u32 level; + card_block *card = &cardmap[chn]; + + _CPU_ISR_Disable(level); + while((ret=CARD_GetErrorCode(chn))==CARD_ERROR_BUSY) { + LWP_ThreadSleep(card->wait_sync_queue); + } + _CPU_ISR_Restore(level); + return ret; +} + +static void __card_synccallback(s32 chn,s32 result) +{ + u32 level; + card_block *card = &cardmap[chn]; + _CPU_ISR_Disable(level); + LWP_ThreadBroadcast(card->wait_sync_queue); + _CPU_ISR_Restore(level); +} + +static void __card_updateiconoffsets(struct card_direntry *entry,card_stat *stats) +{ + s32 i; + u8 bnrfmt,nicons; + u32 iconaddr,iconbase; + + iconaddr = entry->iconaddr; + if(iconaddr==-1) { + stats->banner_fmt = 0; + stats->icon_fmt = 0; + stats->icon_speed = 0; + iconaddr = 0; + } + + if(entry->bannerfmt&CARD_BANNER_MASK) { + if(!(entry->bannerfmt&0x10)) { + bnrfmt = (entry->bannerfmt&CARD_BANNER_MASK); + if(bnrfmt==CARD_BANNER_CI) { + stats->banner_fmt = bnrfmt; + stats->offset_banner = iconaddr; + stats->offset_banner_tlut = iconaddr+3072; + iconaddr += (3072+512); + } else if(bnrfmt==CARD_BANNER_RGB) { + stats->banner_fmt = bnrfmt; + stats->offset_banner = iconaddr; + stats->offset_banner_tlut = -1; + iconaddr += 6144; + } + } else { + stats->offset_banner = -1; + stats->offset_banner_tlut = -1; + } + } + + nicons = 0; + for(i=0;iiconfmt[i] = ((entry->iconfmt>>(i<<1))&CARD_ICON_MASK); + stats->iconspeed[i] = ((entry->iconspeed>>(i<<1))&CARD_SPEED_MASK); + if(stats->iconspeed[i]==0) stats->iconfmt[i] = 0; + if(stats->iconfmt[i]) nicons++; + } + + iconbase = iconaddr; + for(i=0;iiconfmt[i]) { + case 1: //CARD_ICON_CI with shared palette + stats->offset_icon[i] = iconaddr; + stats->offset_icon_tlut[i] = iconbase + (nicons*1024); + iconaddr += 1024; + break; + case 2: //CARD_ICON_RGB + stats->offset_icon[i] = iconaddr; + stats->offset_icon_tlut[i] = -1; + iconaddr += 3072; + break; + case 3: //CARD_ICON_CI with own palette + stats->offset_icon[i] = iconaddr; + stats->offset_icon_tlut[i] = iconaddr + 1024; + iconaddr += 1536; + break; + default: //CARD_ICON_NONE + stats->offset_icon[i] = -1; + stats->offset_icon_tlut[i] = -1; + break; + + + } + } +// stats->offset_data = iconaddr; +} + +static s32 __card_getstatusex(s32 chn,s32 fileno,struct card_direntry *entry) +{ + s32 ret; + card_block *card = NULL; + struct card_dat *dirblock = NULL; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + if(fileno<0 || fileno>=CARD_MAXFILES) return CARD_ERROR_FATAL_ERROR; + if((ret=__card_getcntrlblock(chn,&card))<0) return ret; + + ret = CARD_ERROR_BROKEN; + dirblock = __card_getdirblock(card); + if(dirblock) { + ret = CARD_ERROR_READY; + memcpy(entry,&dirblock->entries[fileno],sizeof(struct card_direntry)); + } + return __card_putcntrlblock(card,ret); +} + +static s32 __card_setstatusexasync(s32 chn,s32 fileno,struct card_direntry *entry,cardcallback callback) +{ + s32 ret,i,bend; + card_block *card = NULL; + struct card_dat *dirblock = NULL; + struct card_direntry *entries = NULL; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + if(fileno<0 || fileno>=CARD_MAXFILES) return CARD_ERROR_FATAL_ERROR; + if(entry->filename[0]==0xff || entry->filename[0]==0) return CARD_ERROR_FATAL_ERROR; + if((ret=__card_getcntrlblock(chn,&card))<0) return ret; + + ret = CARD_ERROR_BROKEN; + dirblock = __card_getdirblock(card); + if(dirblock) { + i = 0; bend = 0; + ret = CARD_ERROR_READY; + entries = dirblock->entries; + while(ifilename[i]==0) { + entry->filename[i] = 0; + bend = 1; + } + i++; + } + + if(memcmp(entries[fileno].filename,entry->filename,CARD_FILENAMELEN) + || memcmp(entries[fileno].gamecode,entry->gamecode,4) + || memcmp(entries[fileno].company,entry->company,2)) { + i = 0; + while(igamecode,4)==0 + && memcmp(entries[i].company,entry->company,2)==0 + && memcmp(entries[i].filename,entry->filename,CARD_FILENAMELEN)==0) { + return __card_putcntrlblock(card,CARD_ERROR_EXIST); + } + i++; + } + memcpy(entries[fileno].filename,entry->filename,CARD_FILENAMELEN); + memcpy(entries[fileno].gamecode,entry->gamecode,4); + memcpy(entries[fileno].company,entry->company,2); + } + + entries[fileno].lastmodified = entry->lastmodified; + entries[fileno].bannerfmt = entry->bannerfmt; + entries[fileno].iconaddr = entry->iconaddr; + entries[fileno].iconfmt = entry->iconfmt; + entries[fileno].iconspeed = entry->iconspeed; + entries[fileno].commentaddr = entry->commentaddr; + entries[fileno].permission = entry->permission; + entries[fileno].copytimes = entry->copytimes; + + if((ret=__card_updatedir(chn,callback))>=0) return ret; + } + return __card_putcntrlblock(card,ret); +} + +static s32 __card_getfilenum(card_block *card,const char *filename,const char *gamecode,const char *company,s32 *fileno) +{ + u32 i = 0; + struct card_direntry *entries = NULL; + struct card_dat *dirblock = NULL; + if(!card->attached) return CARD_ERROR_NOCARD; + dirblock = __card_getdirblock(card); + + entries = dirblock->entries; + for(i=0;i=CARD_MAXFILES) return CARD_ERROR_NOFILE; + return CARD_ERROR_READY; +} + +static s32 __card_seek(card_file *file,s32 len,s32 offset,card_block **rcard) +{ + s32 ret; + s32 i,entry_len; + card_block *card = NULL; + struct card_direntry *entry = NULL; + struct card_dat *dirblock = NULL; + struct card_bat *fatblock = NULL; + if(file->filenum<0 || file->filenum>=CARD_MAXFILES) return CARD_ERROR_FATAL_ERROR; + if((ret=__card_getcntrlblock(file->chn,&card))<0) return ret; + if(file->iblockiblock>=card->blocks) { + __card_putcntrlblock(card,CARD_ERROR_FATAL_ERROR); + return CARD_ERROR_FATAL_ERROR; + } + + dirblock = __card_getdirblock(card); + entry = &dirblock->entries[file->filenum]; + if(entry->gamecode[0]!=0xff) { + entry_len = entry->length*card->sector_size; + if(entry_lencurr_file = file; + file->len = len; + + if(offsetoffset) { + file->offset = 0; + file->iblock = entry->block; + if(file->iblockiblock>=card->blocks) { + __card_putcntrlblock(card,CARD_ERROR_BROKEN); + return CARD_ERROR_BROKEN; + } + } + + fatblock = __card_getbatblock(card); + for(i=file->iblock;iblocks && file->offset<(offset&~(card->sector_size-1));i=file->iblock) { + file->offset += card->sector_size; + file->iblock = fatblock->fat[i-CARD_SYSAREA]; + if(file->iblockiblock>=card->blocks) { + __card_putcntrlblock(card,CARD_ERROR_BROKEN); + return CARD_ERROR_BROKEN; + } + } + file->offset = offset; + *rcard = card; + } + return CARD_ERROR_READY; +} + +static u32 __card_checkdir(card_block *card,u32 *currdir) +{ + u32 dir,bad,bad_dir; + u16 chksum0,chksum1; + struct card_dircntrl *dircntrl[2]; + struct card_dat *dirblock[2]; + dir = 0; + bad = 0; + bad_dir = 0; + while(dir<2) { + dirblock[dir] = card->workarea+((dir+1)<<13); + dircntrl[dir] = (card->workarea+((dir+1)<<13))+8128; + __card_checksum((u16*)dirblock[dir],0x1ffc,&chksum0,&chksum1); + if(chksum0!=dircntrl[dir]->chksum1 || chksum1!=dircntrl[dir]->chksum2) { + card->curr_dir = NULL; + bad_dir = dir; + bad++; + } + dir++; + } + + dir = bad_dir; + if(!bad) { + if(dircntrl[0]->updatedupdated) dir = 0; + else dir = 1; + } + if(card->curr_dir==NULL) { + card->curr_dir = dirblock[dir]; + memcpy(dirblock[dir],dirblock[dir^1],8192); + } + else if(card->curr_dir==dirblock[0]) dir = 0; + else dir = 1; + + if(currdir) *currdir = dir; + return bad; +} + +static u32 __card_checkfat(card_block *card,u32 *currfat) +{ + u32 fat,bad,bad_fat; + u16 chksum0,chksum1; + struct card_bat *fatblock[2]; + fat = 0; + bad = 0; + bad_fat = 0; + while(fat<2) { + fatblock[fat] = card->workarea+((fat+3)<<13); + __card_checksum((u16*)(((u32)fatblock[fat])+4),0x1ffc,&chksum0,&chksum1); + if(chksum0!=fatblock[fat]->chksum1 || chksum1!=fatblock[fat]->chksum2) { + card->curr_fat = NULL; + bad_fat = fat; + bad++; + } else { + u16 curblock = CARD_SYSAREA; + u16 freeblocks = 0; + while(curblockblocks) { + if(!fatblock[fat]->fat[curblock-CARD_SYSAREA]) freeblocks++; + curblock++; + } + if(freeblocks!=fatblock[fat]->freeblocks) { + card->curr_fat = NULL; + bad_fat = fat; + bad++; + } + } + fat++; + } + + fat = bad_fat; + if(!bad) { + if(fatblock[0]->updatedupdated) fat = 0; + else fat = 1; + } + if(card->curr_fat==NULL) { + card->curr_fat = fatblock[fat]; + memcpy(fatblock[fat],fatblock[fat^1],8192); + } + else if(card->curr_fat==fatblock[0]) fat = 0; + else fat = 1; + + if(currfat) *currfat = fat; + return bad; +} + +static s32 __card_verify(card_block *card) +{ + u32 ret = 0; + + ret += __card_checkdir(card,NULL); + ret += __card_checkfat(card,NULL); + if(ret<=2) { + if(card->curr_dir && card->curr_fat) return CARD_ERROR_READY; + } + return CARD_ERROR_BROKEN; +} + +static u32 __card_iscard(u32 id) +{ + u32 ret; + u32 idx,tmp,secsize; + + if(id&~0xffff) return 0; + if(id&0x03) return 0; + + ret = 0; + tmp = id&0xfc; + if(tmp==EXI_MEMCARD59 || tmp==EXI_MEMCARD123 + || tmp==EXI_MEMCARD251 || tmp==EXI_MEMCARD507 + || tmp==EXI_MEMCARD1019 || tmp==EXI_MEMCARD2043) { + idx = _ROTL(id,23)&0x1c; + if((secsize=card_sector_size[idx>>2])==0) return 0; + tmp = ((tmp<<20)&0x1FFE0000)/secsize; + if(tmp>8) ret = 1; + } + return ret; +} + +static s32 __card_allocblock(s32 chn,u32 blocksneed,cardcallback callback) +{ + s32 ret; + u16 block,currblock = 0,prevblock = 0; + u32 i,count; + card_block *card = NULL; + struct card_bat *fatblock = NULL; + if(chn=EXI_CHANNEL_2) return CARD_ERROR_FATAL_ERROR; + card = &cardmap[chn]; + + if(!card->attached) return CARD_ERROR_NOCARD; + fatblock = __card_getbatblock(card); + + if(fatblock->freeblockslastalloc; + i = blocksneed; + while(1) { + if(i==0) { + // Done allocating blocks + fatblock->freeblocks -= blocksneed; + fatblock->lastalloc = currblock; + card->curr_fileblock = block; + ret = __card_updatefat(chn,fatblock,callback); + break; + } + + /* + Since testing free space has already been done, if all the blocks + the file takes up cannot be entered into the FAT, something is + wrong. + */ + count++; + if(count>=(card->blocks-CARD_SYSAREA)) return CARD_ERROR_BROKEN; + + currblock++; + if(currblock=card->blocks) currblock = CARD_SYSAREA; + if(fatblock->fat[currblock-CARD_SYSAREA]==0) { + if(block!=0xffff) + fatblock->fat[prevblock-CARD_SYSAREA] = currblock; + else + block = currblock; + + fatblock->fat[currblock-CARD_SYSAREA] = 0xffff; + prevblock = currblock; + i--; + } + } + return ret; +} + +static s32 __card_freeblock(s32 chn,u16 block,cardcallback callback) +{ + u16 next = 0xffff,prev = 0xffff; + card_block *card = NULL; + struct card_bat *fatblock = NULL; + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + card = &cardmap[chn]; + + if(!card->attached) return CARD_ERROR_NOCARD; + + fatblock = __card_getbatblock(card); + next = fatblock->fat[block-CARD_SYSAREA]; + while(1) { + if(next==0xffff) break; + if(next=card->blocks) return CARD_ERROR_BROKEN; + + // Get the file's next block and clear the previous one from the fat + prev = next; + next = fatblock->fat[prev-CARD_SYSAREA]; + fatblock->fat[prev-CARD_SYSAREA] = 0; + fatblock->freeblocks++; + } + return __card_updatefat(chn,fatblock,callback); +} + +static s32 __card_unlockedhandler(s32 chn,s32 dev) +{ + s32 ret; + cardcallback cb = NULL; + card_block *card = NULL; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + card = &cardmap[chn]; + + ret = CARD_ERROR_READY; + cb = card->card_unlock_cb; + if(cb) { + card->card_unlock_cb = NULL; + if(EXI_Probe(chn)==0) ret = CARD_ERROR_NOCARD; + cb(chn,ret); + } + return CARD_ERROR_UNLOCKED; +} + +static s32 __card_readstatus(s32 chn,u8 *pstatus) +{ + u8 val[2]; + u32 err; + s32 ret; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + if(EXI_Select(chn,EXI_DEVICE_0,EXI_SPEED16MHZ)==0) return CARD_ERROR_NOCARD; + + err = 0; + val[0] = 0x83; val[1] = 0x00; + if(EXI_Imm(chn,val,2,EXI_WRITE,NULL)==0) err |= 0x01; + if(EXI_Sync(chn)==0) err |= 0x02; + if(EXI_Imm(chn,pstatus,1,EXI_READ,NULL)==0) err |= 0x04; + if(EXI_Sync(chn)==0) err |= 0x08; + if(EXI_Deselect(chn)==0) err |= 0x10; + + if(err) ret = CARD_ERROR_NOCARD; + else ret = CARD_ERROR_READY; + return ret; +} + +static s32 __card_clearstatus(s32 chn) +{ + u8 val; + u32 err; + s32 ret; + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + if(EXI_Select(chn,EXI_DEVICE_0,EXI_SPEED16MHZ)==0) return CARD_ERROR_NOCARD; + + err = 0; + val = 0x89; + if(EXI_Imm(chn,&val,1,EXI_WRITE,NULL)==0) err |= 0x01; + if(EXI_Sync(chn)==0) err |= 0x02; + if(EXI_Deselect(chn)==0) err |= 0x04; + + if(err) ret = CARD_ERROR_NOCARD; + else ret = CARD_ERROR_READY; + + return ret; +} + +static s32 __card_enableinterrupt(s32 chn,u32 enable) +{ + u8 val[2]; + u32 err; + s32 ret; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + if(EXI_Select(chn,EXI_DEVICE_0,EXI_SPEED16MHZ)==0) return CARD_ERROR_NOCARD; + + err = 0; + val[0] = 0x81; + if(enable) val[1] = 0x01; + else val[1] = 0x00; + if(EXI_Imm(chn,val,2,EXI_WRITE,NULL)==0) err |= 0x01; + if(EXI_Sync(chn)==0) err |= 0x02; + if(EXI_Deselect(chn)==0) err |= 0x04; + + if(err) ret = CARD_ERROR_BUSY; + else ret = CARD_ERROR_READY; + + return ret; +} + +static s32 __card_txhandler(s32 chn,s32 dev) +{ + u32 err; + s32 ret = CARD_ERROR_READY; + cardcallback cb = NULL; + card_block *card = NULL; + if(chn=EXI_CHANNEL_2) return 0; + card = &cardmap[chn]; + + err = 0; + if(EXI_Deselect(chn)==0) ret |= err; + if(EXI_Unlock(chn)==0) ret |= err; + + cb = card->card_tx_cb; + if(cb) { + card->card_tx_cb = NULL; + if(!err) { + if(EXI_Probe(chn)==0) ret = CARD_ERROR_NOCARD; + } else ret = CARD_ERROR_NOCARD; + cb(chn,ret); + } + return 1; +} + +static void __timeouthandler(syswd_t alarm,void *cbarg) +{ + u32 chn; + s32 ret = CARD_ERROR_READY; + cardcallback cb; + card_block *card = NULL; + chn = 0; + while(chntimeout_svc==alarm) break; + chn++; + } + if(chn=EXI_CHANNEL_2) return; + + + if(card->attached) { + EXI_RegisterEXICallback(chn,NULL); + cb = card->card_exi_cb; + if(cb) { + card->card_exi_cb = NULL; + ret = CARD_ERROR_IOERROR; + cb(chn,ret); + } + } +} + +static void __setuptimeout(card_block *card) +{ + struct timespec tb; + SYS_CancelAlarm(card->timeout_svc); + + if(card->cmd[0]==0xf1 || card->cmd[0]==0xf4) { + tb.tv_sec = 1*(card->sector_size/8192); + tb.tv_nsec = 0; + SYS_SetAlarm(card->timeout_svc,&tb,__timeouthandler,NULL); + } else if(card->cmd[0]==0xf2) { + tb.tv_sec = 0; + tb.tv_nsec = 100*TB_NSPERMS; + SYS_SetAlarm(card->timeout_svc,&tb,__timeouthandler,NULL); + } +} + +static s32 __retry(s32 chn) +{ + u32 len; + card_block *card = NULL; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + card = &cardmap[chn]; + if(EXI_Select(chn,EXI_DEVICE_0,EXI_SPEED16MHZ)==0) { + EXI_Unlock(chn); + return CARD_ERROR_NOCARD; + } + + __setuptimeout(card); + + if(EXI_ImmEx(chn,card->cmd,card->cmd_len,EXI_WRITE)==0) { + EXI_Deselect(chn); + EXI_Unlock(chn); + return CARD_ERROR_NOCARD; + } + + if(card->cmd[0]==0x52) { + if(EXI_ImmEx(chn,card->workarea+CARD_READSIZE,card->latency,EXI_WRITE)==0) { + EXI_Deselect(chn); + EXI_Unlock(chn); + return CARD_ERROR_NOCARD; + } + } + + if(card->cmd_mode==-1) { + EXI_Deselect(chn); + EXI_Unlock(chn); + return CARD_ERROR_READY; + } + + len = 128; + if(card->cmd[0]==0x52) len = CARD_READSIZE; + if(EXI_Dma(chn,card->cmd_usr_buf,len,card->cmd_mode,__card_txhandler)==0) { + EXI_Deselect(chn); + EXI_Unlock(chn); + return CARD_ERROR_NOCARD; + } + return CARD_ERROR_READY; +} + +static void __card_defaultapicallback(s32 chn,s32 result) +{ + return; +} + +static s32 __card_exihandler(s32 chn,s32 dev) +{ + u8 status; + s32 ret = CARD_ERROR_READY; + card_block *card = NULL; + cardcallback cb; + if(chn=EXI_CHANNEL_2) return 1; + card = &cardmap[chn]; + + SYS_CancelAlarm(card->timeout_svc); + if(card->attached) { + if(EXI_Lock(chn,EXI_DEVICE_0,NULL)==1) { + if((ret=__card_readstatus(chn,&status))>=0 + && (ret=__card_clearstatus(chn))>=0) { + if(status&0x18) ret = CARD_ERROR_IOERROR; + else ret = CARD_ERROR_READY; + + if(ret==CARD_ERROR_IOERROR) { + if((--card->cmd_retries)>0) { + ret = __retry(chn); + if(ret<0) goto exit; + return 1; + } + } + } + EXI_Unlock(chn); + } else ret = CARD_ERROR_FATAL_ERROR; +exit: + cb = card->card_exi_cb; + if(cb) { + card->card_exi_cb = NULL; + cb(chn,ret); + } + } + return 1; +} + +static s32 __card_exthandler(s32 chn,s32 dev) +{ + cardcallback cb; + card_block *card = NULL; + if(chn=EXI_CHANNEL_2) return 0; + card = &cardmap[chn]; + + if(card->attached) { + if(card->card_tx_cb) { + printf("error: card->card_tx_cb!=NULL\n"); + } + card->attached = 0; + EXI_RegisterEXICallback(chn,NULL); + SYS_CancelAlarm(card->timeout_svc); + + cb = card->card_exi_cb; + if(cb) { + card->card_exi_cb = NULL; + cb(chn,CARD_ERROR_NOCARD); + } + + cb = card->card_ext_cb; + if(cb) { + card->card_ext_cb = NULL; + cb(chn,CARD_ERROR_NOCARD); + } + + } + return 1; +} + +static void __write_callback(s32 chn,s32 result) +{ + s32 ret; + cardcallback cb = NULL; + card_file *file = NULL; + struct card_bat *fatblock = NULL; + struct card_dat *dirblock = NULL; + struct card_direntry *entry = NULL; + card_block *card = &cardmap[chn]; + ret = result; + if(ret>=0) { + file = card->curr_file; + if(file->len>=0) { + file->len = (card->sector_size-file->len); + if(file->len<=0) { + dirblock = __card_getdirblock(card); + entry = &dirblock->entries[file->filenum]; + entry->lastmodified = time(NULL); + cb = card->card_api_cb; + card->card_api_cb = NULL; + if((ret=__card_updatedir(chn,cb))>=0) return; + } else { + fatblock = __card_getbatblock(card); + file->offset += card->sector_size; + file->iblock = fatblock->fat[file->iblock-CARD_SYSAREA]; + if(file->iblockiblock>=card->blocks) { + ret = CARD_ERROR_BROKEN; + goto exit; + } + if((ret=__card_sectorerase(chn,(file->iblock*card->sector_size),__erase_callback))>=0) return; + } + } else + ret = CARD_ERROR_CANCELED; + } + +exit: + cb = card->card_api_cb; + card->card_api_cb = NULL; + __card_putcntrlblock(card,ret); + if(cb) cb(chn,ret); +} + +static void __erase_callback(s32 chn,s32 result) +{ + s32 ret; + cardcallback cb = NULL; + card_file *file = NULL; + card_block *card = &cardmap[chn]; + ret = result; + if(ret>=0) { + file = card->curr_file; + if((ret=__card_write(chn,(file->iblock*card->sector_size),card->sector_size,card->cmd_usr_buf,__write_callback))>=0) return; + } + + cb = card->card_api_cb; + card->card_api_cb = NULL; + __card_putcntrlblock(card,ret); + if(cb) cb(chn,ret); +} + +static void __read_callback(s32 chn,s32 result) +{ + s32 ret; + s32 len; + cardcallback cb = NULL; + card_file *file = NULL; + card_block *card = 0; + struct card_bat *fatblock = NULL; + if(chn=EXI_CHANNEL_2) return; + card = &cardmap[chn]; + + ret = result; + file = card->curr_file; + if(ret>=0) { + if(file->len>=0) { + file->len = file->len-(((file->offset+card->sector_size)&~(card->sector_size-1))-file->offset); + if(file->len>0) { + fatblock = __card_getbatblock(card); + file->offset += (((file->offset+card->sector_size)&~(card->sector_size-1))-file->offset); + file->iblock = fatblock->fat[file->iblock-CARD_SYSAREA]; + if(file->iblockiblock>=card->blocks) { + ret = CARD_ERROR_BROKEN; + goto exit; + } + len = file->lensector_size?card->sector_size:file->len; + if(__card_read(chn,(file->iblock*card->sector_size),len,card->cmd_usr_buf,__read_callback)>=0) return; + + } + } else + ret = CARD_ERROR_CANCELED; + } + +exit: + cb = card->card_api_cb; + card->card_api_cb = NULL; + __card_putcntrlblock(card,ret); + if(cb) cb(chn,ret); +} + +static void __delete_callback(s32 chn,s32 result) +{ + s32 ret; + cardcallback cb = NULL; + card_block *card = &cardmap[chn]; + cb = card->card_api_cb; + card->card_api_cb = NULL; + + ret = result; + if(ret>=0 && (ret=__card_freeblock(chn,card->curr_fileblock,cb))>=0) return; + + __card_putcntrlblock(card,ret); + if(cb) cb(chn,ret); +} + +static void __format_callback(s32 chn,s32 result) +{ + s32 ret; + cardcallback cb = NULL; + card_block *card = &cardmap[chn]; + + ret = result; + if(ret>=0) { + if((++card->format_step)format_step*card->sector_size),__format_callback))>=0) return; + goto exit; + } + if(card->format_step<10) { + if((ret=__card_write(chn,((card->format_step-CARD_SYSAREA)*card->sector_size),8192,card->workarea+((card->format_step-CARD_SYSAREA)<<13),__format_callback))>=0) return; + goto exit; + } + + card->curr_dir = card->workarea+CARD_SYSDIR; + memcpy(card->curr_dir,card->workarea+CARD_SYSDIR_BACK,8192); + + card->curr_fat = card->workarea+CARD_SYSBAT; + memcpy(card->curr_fat,card->workarea+CARD_SYSBAT_BACK,8192); + } +exit: + cb = card->card_api_cb; + card->card_api_cb = NULL; + __card_putcntrlblock(card,ret); + if(cb) cb(chn,ret); +} + +static void __blockwritecallback(s32 chn,s32 result) +{ + s32 ret = CARD_ERROR_READY; + cardcallback cb = NULL; + card_block *card = &cardmap[chn]; + ret = result; + if(ret>=0) { + card->transfer_cnt += 128; + card->cmd_sector_addr += 128; + card->cmd_usr_buf += 128; + if((--card->cmd_blck_cnt)>0) { + if((ret=__card_writepage(chn,__blockwritecallback))>=CARD_ERROR_READY) return; + } + } + + if(!card->card_api_cb) __card_putcntrlblock(card,ret); + + cb = card->card_xfer_cb; + if(cb) { + card->card_xfer_cb = NULL; + cb(chn,ret); + } +} + +static void __blockreadcallback(s32 chn,s32 result) +{ + s32 ret = CARD_ERROR_READY; + cardcallback cb = NULL; + card_block *card = &cardmap[chn]; + ret = result; + if(ret>=0) { + card->transfer_cnt += CARD_READSIZE; + card->cmd_sector_addr += CARD_READSIZE; + card->cmd_usr_buf += CARD_READSIZE; + if((--card->cmd_blck_cnt)>0) { + if((ret=__card_readsegment(chn,__blockreadcallback))>=CARD_ERROR_READY) return; + } + } + + if(!card->card_api_cb) __card_putcntrlblock(card,ret); + cb = card->card_xfer_cb; + if(cb) { + card->card_xfer_cb = NULL; + cb(chn,ret); + } +} + +static void __unlocked_callback(s32 chn,s32 result) +{ + s32 ret; + card_block *card; + cardcallback cb; + if(chn=EXI_CHANNEL_2) return; + card = &cardmap[chn]; + + ret = result; + if(ret>=0) { + card->card_unlock_cb = __unlocked_callback; + if(EXI_Lock(chn,EXI_DEVICE_0,__card_unlockedhandler)==1) { + card->card_unlock_cb = NULL; + ret = __retry(chn); + } else + ret = 0; + } + if(ret<0) { + if(card->cmd[0]==0xf3 || card->cmd[0]>=0xf5) return; + else if(card->cmd[0]==0x52) { + cb = card->card_tx_cb; + if(cb) { + card->card_tx_cb = NULL; + cb(chn,ret); + } + } else if(card->cmd[0]>=0xf1) { + cb = card->card_exi_cb; + if(cb) { + card->card_exi_cb = NULL; + cb(chn,ret); + } + } + } +} + +static s32 __card_start(s32 chn,cardcallback tx_cb,cardcallback exi_cb) +{ + u32 level; + card_block *card = NULL; + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + card = &cardmap[chn]; + + _CPU_ISR_Disable(level); + if(tx_cb) card->card_tx_cb = tx_cb; + if(exi_cb) card->card_exi_cb = exi_cb; + + card->card_unlock_cb = __unlocked_callback; + if(EXI_Lock(chn,EXI_DEVICE_0,__card_unlockedhandler)==0) { + _CPU_ISR_Restore(level); + return CARD_ERROR_BUSY; + } + card->card_unlock_cb = NULL; + + if(EXI_Select(chn,EXI_DEVICE_0,EXI_SPEED16MHZ)==0) { + EXI_Unlock(chn); + _CPU_ISR_Restore(level); + return CARD_ERROR_NOCARD; + } + + __setuptimeout(card); + _CPU_ISR_Restore(level); + + return CARD_ERROR_READY; +} + +static s32 __card_writepage(s32 chn,cardcallback callback) +{ + s32 ret; + card_block *card = NULL; + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + card = &cardmap[chn]; + + card->cmd[0] = 0xf2; + card->cmd[1] = (card->cmd_sector_addr>>17)&0x3f; + card->cmd[2] = (card->cmd_sector_addr>>9)&0xff; + card->cmd[3] = (card->cmd_sector_addr>>7)&3; + card->cmd[4] = card->cmd_sector_addr&0x7f; + card->cmd_len = 5; + card->cmd_mode = EXI_WRITE; + card->cmd_retries = 3; + + ret = __card_start(chn,NULL,callback); + if(ret<0) return ret; + + if(EXI_ImmEx(chn,card->cmd,card->cmd_len,EXI_WRITE)==1 + && EXI_Dma(chn,card->cmd_usr_buf,128,card->cmd_mode,__card_txhandler)==1) return CARD_ERROR_READY; + + card->card_exi_cb = NULL; + EXI_Deselect(chn); + EXI_Unlock(chn); + return CARD_ERROR_NOCARD; +} + +static s32 __card_readsegment(s32 chn,cardcallback callback) +{ + u32 err; + s32 ret; + card_block *card = NULL; + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + card = &cardmap[chn]; + + card->cmd[0] = 0x52; + card->cmd[1] = (card->cmd_sector_addr&0xFE0000)>>17; + card->cmd[2] = (card->cmd_sector_addr&0x01FE00)>>9; + card->cmd[3] = (card->cmd_sector_addr&0x000180)>>7; + card->cmd[4] = (card->cmd_sector_addr&0x00007F); + card->cmd_len = 5; + card->cmd_mode = EXI_READ; + card->cmd_retries = 0; + + ret = __card_start(chn,callback,NULL); + if(ret<0) return ret; + + err = 0; + if(EXI_ImmEx(chn,card->cmd,card->cmd_len,EXI_WRITE)==0) err |= 0x01; + if(EXI_ImmEx(chn,card->workarea+CARD_READSIZE,card->latency,EXI_WRITE)==0) err |= 0x02; + if(EXI_Dma(chn,card->cmd_usr_buf,CARD_READSIZE,card->cmd_mode,__card_txhandler)==0) err |= 0x04; + + if(err) { + card->card_tx_cb = NULL; + EXI_Deselect(chn); + EXI_Unlock(chn); + return CARD_ERROR_NOCARD; + } + return CARD_ERROR_READY; +} + +static void __card_fatwritecallback(s32 chn,s32 result) +{ + s32 ret; + cardcallback cb = NULL; + struct card_bat *fat1,*fat2; + card_block *card = &cardmap[chn]; + ret = result; + if(ret>=0) { + fat1 = (card->workarea+0x6000); + fat2 = (card->workarea+0x8000); + if(card->curr_fat==fat1) { + card->curr_fat = fat2; + memcpy(fat2,fat1,8192); + } else { + card->curr_fat = fat1; + memcpy(fat1,fat2,8192); + } + } + if(!card->card_api_cb) __card_putcntrlblock(card,ret); + cb = card->card_erase_cb; + if(cb) { + card->card_erase_cb = NULL; + cb(chn,ret); + } +} + +static void __card_dirwritecallback(s32 chn,s32 result) +{ + s32 ret; + cardcallback cb = NULL; + struct card_dat *dir1,*dir2; + card_block *card = &cardmap[chn]; + ret = result; + if(ret>=0) { + dir1 = (card->workarea+0x2000); + dir2 = (card->workarea+0x4000); + if(card->curr_dir==dir1) { + card->curr_dir = dir2; + memcpy(dir2,dir1,8192); + } else { + card->curr_dir = dir1; + memcpy(dir1,dir2,8192); + } + } + if(!card->card_api_cb) __card_putcntrlblock(card,ret); + cb = card->card_erase_cb; + if(cb) { + card->card_erase_cb = NULL; + cb(chn,ret); + } +} + +static s32 __card_write(s32 chn,u32 address,u32 block_len,void *buffer,cardcallback callback) +{ + s32 ret; + card_block *card = NULL; + if(chn= EXI_CHANNEL_2) return CARD_ERROR_FATAL_ERROR; + card = &cardmap[chn]; + + if(!card->attached) return CARD_ERROR_NOCARD; + + card->cmd_blck_cnt = block_len>>7; + card->cmd_sector_addr = address; + card->cmd_usr_buf = buffer; + card->card_xfer_cb = callback; + ret = __card_writepage(chn,__blockwritecallback); + + return ret; +} + +static s32 __card_read(s32 chn,u32 address,u32 block_len,void *buffer,cardcallback callback) +{ + s32 ret; + card_block *card = NULL; + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + card = &cardmap[chn]; + + card->cmd_sector_addr = address; + card->cmd_blck_cnt = block_len>>9; + card->cmd_usr_buf = buffer; + card->card_xfer_cb = callback; + ret = __card_readsegment(chn,__blockreadcallback); + + return ret; +} + +static s32 __card_formatregion(s32 chn,u32 encode,cardcallback callback) +{ + s32 ret; + u16 tmp; + u32 cnt; + u64 time; + u64 rnd_val; + void *workarea,*memblock; + cardcallback cb = NULL; + card_block *card = NULL; + struct card_header *header; + struct card_bat *fatblock = NULL; + struct card_dircntrl *dircntrl = NULL; + syssram *sram; + syssramex *sramex; + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + + if((ret=__card_getcntrlblock(chn,&card))<0) return ret; + + header = workarea = card->workarea; + memset(header,0xff,8192); + + tmp = _viReg[55]; + header->encoding = encode; + + sram = __SYS_LockSram(); + header->serial[5] = sram->counter_bias; + header->serial[6] = sram->lang; + __SYS_UnlockSram(0); + + cnt = 0; + rnd_val = time = gettime(); + sramex = __SYS_LockSramEx(); + while(cnt<12) { + rnd_val = (((rnd_val*(u64)0x0000000041c64e6d)+(u64)0x0000000000003039)>>16); + ((u8*)header->serial)[cnt] = (sramex->flash_id[chn][cnt]+(u32)rnd_val); + + rnd_val = (((rnd_val*(u64)0x0000000041c64e6d)+(u64)0x0000000000003039)>>16); + rnd_val &= (u64)0x0000000000007fff; + + cnt++; + } + __SYS_UnlockSramEx(0); + + *(u64*)&(header->serial[3]) = time; + header->serial[7] = tmp; + header->device_id = 0; + header->size = card->card_size; + __card_checksum((u16*)header,508,&header->chksum1,&header->chksum2); + + cnt = 0; + while(cnt<2) { + memblock = workarea+((cnt+1)<<13); + dircntrl = memblock+8128; + memset(memblock,0xff,8192); + __card_checksum(memblock,8188,&dircntrl->chksum1,&dircntrl->chksum2); + cnt++; + } + + cnt = 0; + while(cnt<2) { + memblock = workarea+((cnt+3)<<13); + fatblock = memblock; + memset(memblock,0,8192); + fatblock->updated = cnt; + fatblock->freeblocks = card->blocks-CARD_SYSAREA; + fatblock->lastalloc = 4; + __card_checksum(memblock+4,8188,&fatblock->chksum1,&fatblock->chksum2); + cnt++; + } + + cb = callback; + if(!cb) cb = __card_defaultapicallback; + card->card_api_cb = cb; + + DCStoreRange(card->workarea,0xA000); + + card->format_step = 0; + if((ret=__card_sectorerase(chn,(card->sector_size*card->format_step),__format_callback))>=0) return ret; + + __card_putcntrlblock(card,ret); + return ret; +} + +static s32 __card_sectorerase(s32 chn,u32 sector,cardcallback callback) +{ + s32 ret; + card_block *card = NULL; + if(chn= EXI_CHANNEL_2) return CARD_ERROR_FATAL_ERROR; + card = &cardmap[chn]; + + if(sector%card->sector_size) return CARD_ERROR_FATAL_ERROR; + + card->cmd[0] = 0xf1; + card->cmd[1] = (sector>>17)&0x7f; + card->cmd[2] = (sector>>9)&0xff; + card->cmd_len = 3; + card->cmd_mode = -1; + card->cmd_retries = 3; + + ret = __card_start(chn,NULL,callback); + if(ret<0) return ret; + + if(EXI_ImmEx(chn,card->cmd,card->cmd_len,EXI_WRITE)==0) { + card->card_exi_cb = NULL; + return CARD_ERROR_NOCARD; + } + + EXI_Deselect(chn); + EXI_Unlock(chn); + return ret; +} + +static void __card_faterasecallback(s32 chn,s32 result) +{ + s32 ret; + cardcallback cb = NULL; + struct card_bat *fatblock = NULL; + card_block *card = &cardmap[chn]; + ret = result; + if(ret>=0) { + fatblock = __card_getbatblock(card); + if((ret=__card_write(chn,(((u32)fatblock-(u32)card->workarea)>>13)*card->sector_size,8192,fatblock,__card_fatwritecallback))>=0) return; + } + if(!card->card_api_cb) __card_putcntrlblock(card,ret); + + cb = card->card_erase_cb; + if(cb) { + card->card_erase_cb = NULL; + cb(chn,ret); + } +} + +static void __card_direrasecallback(s32 chn,s32 result) +{ + s32 ret; + cardcallback cb = NULL; + struct card_dat *dirblock = NULL; + card_block *card = &cardmap[chn]; + ret = result; + if(ret>=0) { + dirblock = __card_getdirblock(card); + if((ret=__card_write(chn,(((u32)dirblock-(u32)card->workarea)>>13)*card->sector_size,8192,dirblock,__card_dirwritecallback))>=0) return; + } + if(!card->card_api_cb) __card_putcntrlblock(card,ret); + + cb = card->card_erase_cb; + if(cb) { + card->card_erase_cb = NULL; + cb(chn,ret); + } +} + +static void __card_createfatcallback(s32 chn,s32 result) +{ + s32 ret; + cardcallback cb = NULL; + card_file *file = NULL; + struct card_direntry *entry = NULL; + struct card_dat *dirblock = NULL; + card_block *card = &cardmap[chn]; + cb = card->card_api_cb; + card->card_api_cb = NULL; + + dirblock = __card_getdirblock(card); + + file = card->curr_file; + entry = &dirblock->entries[file->filenum]; + + memset(entry->gamecode,0,4); + memset(entry->company,0,2); + if(card_gamecode[0]!=0xff) memcpy(entry->gamecode,card_gamecode,4); + if(card_gamecode[0]!=0xff) memcpy(entry->company,card_company,2); + entry->block = card->curr_fileblock; + entry->permission = CARD_ATTRIB_PUBLIC; + entry->pad_00 = 0xff; + entry->copytimes = 0; + entry->iconaddr = -1; + entry->iconfmt = 0; + entry->iconspeed = 0; + entry->pad_01 = 0xffff; + entry->iconspeed = (entry->iconspeed&~CARD_SPEED_MASK)|CARD_SPEED_FAST; + entry->lastmodified = time(NULL); + + file->offset = 0; + file->iblock = card->curr_fileblock; + + if((ret=__card_updatedir(chn,cb))<0) { + __card_putcntrlblock(card,ret); + if(cb) cb(chn,ret); + } +} + +static s32 __card_updatefat(s32 chn,struct card_bat *fatblock,cardcallback callback) +{ + card_block *card = NULL; + if(chn=EXI_CHANNEL_2) return CARD_ERROR_FATAL_ERROR; + card = &cardmap[chn]; + + if(!card->attached) return CARD_ERROR_NOCARD; + + ++fatblock->updated; + __card_checksum((u16*)(((u32)fatblock)+4),0x1ffc,&fatblock->chksum1,&fatblock->chksum2); + DCStoreRange(fatblock,8192); + card->card_erase_cb = callback; + + return __card_sectorerase(chn,(((u32)fatblock-(u32)card->workarea)>>13)*card->sector_size,__card_faterasecallback); +} + +static s32 __card_updatedir(s32 chn,cardcallback callback) +{ + card_block *card = NULL; + void *dirblock = NULL; + struct card_dircntrl *dircntrl = NULL; + if(chn=EXI_CHANNEL_2) return CARD_ERROR_FATAL_ERROR; + card = &cardmap[chn]; + + if(!card->attached) return CARD_ERROR_NOCARD; + + dirblock = __card_getdirblock(card); + dircntrl = dirblock+8128; + ++dircntrl->updated; + __card_checksum((u16*)dirblock,0x1ffc,&dircntrl->chksum1,&dircntrl->chksum2); + DCStoreRange(dirblock,0x2000); + card->card_erase_cb = callback; + + return __card_sectorerase(chn,(((u32)dirblock-(u32)card->workarea)>>13)*card->sector_size,__card_direrasecallback); +} + +static void __card_dounmount(s32 chn,s32 result) +{ + u32 level; + card_block *card; + + if(chn=EXI_CHANNEL_2) return; + card = &cardmap[chn]; + + _CPU_ISR_Disable(level); + if(card->attached) { + card->attached = 0; + card->mount_step = 0; + card->result = result; + EXI_RegisterEXICallback(chn,NULL); + EXI_Detach(chn); + SYS_CancelAlarm(card->timeout_svc); + } + _CPU_ISR_Restore(level); +} + +static s32 __card_domount(s32 chn) +{ + u8 status,kval; + s32 ret = CARD_ERROR_READY; + u32 sum; + u32 id,idx,cnt; + card_block *card; + syssramex *sramex; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + card = &cardmap[chn]; + if(card->mount_step==0) { + ret = 0; + id = 0; + if(EXI_GetID(chn,EXI_DEVICE_0,&id)==0) ret = CARD_ERROR_NOCARD; + else if(!__card_iscard(id)) ret = CARD_ERROR_WRONGDEVICE; + + if(ret<0) goto exit; + card->cid = id; + card->card_size = (id&0xfc); + if(card->card_size) { + idx = _ROTL(id,23)&0x1c; + card->sector_size = card_sector_size[idx>>2]; + card->blocks = ((card->card_size<<20)>>3)/card->sector_size; + + if(card->blocks>0x0008) { + idx = _ROTL(id,26)&0x1c; + card->latency = card_latency[idx>>2]; + + if((ret=__card_clearstatus(chn))<0) goto exit; + if((ret=__card_readstatus(chn,&status))<0) goto exit; + + if(EXI_Probe(chn)==0) { + ret = CARD_ERROR_NOCARD; + goto exit; + } + if(!(status&CARD_STATUS_UNLOCKED)) { + if((ret=__dounlock(chn,card->key))<0) goto exit; + + cnt = 0; + sum = 0; + sramex = __SYS_LockSramEx(); + while(cnt<12) { + kval = ((u8*)card->key)[cnt]; + sramex->flash_id[chn][cnt] = kval; + sum += kval; + cnt++; + } + sum = (sum^-1)&0xff; + sramex->flashID_chksum[chn] = (sum<<8)|sum; + __SYS_UnlockSramEx(1); + return ret; + } + card->mount_step = 1; + + cnt = 0; + sum = 0; + sramex = __SYS_LockSramEx(); + while(cnt<12) { + sum += sramex->flash_id[chn][cnt]; + cnt++; + } + cnt = sramex->flashID_chksum[chn]; + __SYS_UnlockSramEx(0); + + sum = (sum^-1)&0xff; + sum |= (sum<<8); + if(cnt!=sum) { + ret = CARD_ERROR_IOERROR; + goto exit; + } + } + } + } + if(card->mount_step==1) { + card->mount_step = 2; + if((ret=__card_enableinterrupt(chn,1))<0) goto exit; + EXI_RegisterEXICallback(chn,__card_exihandler); + EXI_Unlock(chn); + + DCInvalidateRange(card->workarea,0xA000); + } + + if((ret=__card_read(chn,(card->sector_size*(card->mount_step-2)),card->sector_size,card->workarea+((card->mount_step-2)<<13),__card_mountcallback))<0) goto exit; + return ret; + +exit: + EXI_Unlock(chn); + __card_dounmount(chn,ret); + + return ret; +} + +static void __card_mountcallback(s32 chn,s32 result) +{ + s32 ret; + cardcallback cb; + card_block *card = &cardmap[chn]; + + ret = result; + if(ret==CARD_ERROR_NOCARD || ret==CARD_ERROR_IOERROR) { + __card_dounmount(chn,ret); + __card_putcntrlblock(card,ret); + }else if(ret==CARD_ERROR_UNLOCKED) { + if((ret=__card_domount(chn))>=0) return; + } else { + if((++card->mount_step)<7) { + if((ret=__card_domount(chn))>=0) return; + } else { + ret = __card_verify(card); + __card_putcntrlblock(card,ret); + } + } + + cb = card->card_api_cb; + card->card_api_cb = NULL; + if(cb) cb(chn,ret); +} + +static __inline__ void __card_srand(u32 val) +{ + crand_next = val; +} + +static __inline__ u32 __card_rand() +{ + crand_next = (crand_next*0x41C64E6D)+12345; + return _SHIFTR(crand_next,16,15); +} + +static u32 __card_initval() +{ + u32 ticks = gettick(); + + __card_srand(ticks); + return ((0x7FEC8000|__card_rand())&~0x00000fff); +} + +static u32 __card_dummylen() +{ + u32 ticks = gettick(); + u32 val = 0,cnt = 0,shift = 1; + + __card_srand(ticks); + val = (__card_rand()&0x1f)+1; + + do { + ticks = gettick(); + val = ticks<16) shift = 1; + __card_srand(val); + val = (__card_rand()&0x1f)+1; + cnt++; + }while(val<4 && cnt<10); + if(val<4) val = 4; + + return val; + +} + +static u32 exnor_1st(u32 a,u32 b) +{ + u32 c,d,e,f,r1,r2,r3,r4; + + c = 0; + while(c>23); + e = (a>>15); + f = (a>>7); + r1 = (a^f); + r2 = (e^r1); + r3 = ~(d^r2); //eqv(d,r2) + e = (a>>1); + r4 = ((r3<<30)&0x40000000); + a = (e|r4); + c++; + }; + return a; +} + +static u32 exnor(u32 a,u32 b) +{ + u32 c,d,e,f,r1,r2,r3,r4; + + c = 0; + while(c>30)&0x02); + a = (e|r4); + c++; + }; + return a; +} + +static u32 bitrev(u32 val) +{ + u32 cnt,val1,ret,shift,shift1; + + cnt = 0; + ret = 0; + shift = 1; + shift1 = 0; + while(cnt<32) { + if(cnt<=15) { + val1 = val&(1<>31; + ret |= val1; + } else { + val1 = 1; + val1 = val&(1<>= shift; + ret |= val1; + shift += 2; + } + cnt++; + } + return ret; +} + +static s32 __card_readarrayunlock(s32 chn,u32 address,void *buffer,u32 len,u32 flag) +{ + s32 ret; + u32 err; + u8 regbuf[5]; + card_block *card = &cardmap[chn]; + if(EXI_Select(chn,EXI_DEVICE_0,EXI_SPEED16MHZ)==0) return CARD_ERROR_NOCARD; + + address &= 0xFFFFF000; + memset(regbuf,0,5); + + regbuf[0] = 0x52; + if(!flag) { + regbuf[1] = ((address&0x60000000)>>29)&0xff; + regbuf[2] = ((address&0x1FE00000)>>21)&0xff; + regbuf[3] = ((address&0x00180000)>>19)&0xff; + regbuf[4] = ((address&0x0007F000)>>12)&0xff; + } else { + regbuf[1] = (address>>24)&0xff; + regbuf[2] = ((address&0x00FF0000)>>16)&0xff; + } + + err = 0; + if(EXI_ImmEx(chn,regbuf,5,EXI_WRITE)==0) err |= 0x01; + if(EXI_ImmEx(chn,card->workarea+CARD_READSIZE,card->latency,EXI_WRITE)==0) err |= 0x02; + if(EXI_ImmEx(chn,buffer,len,EXI_READ)==0) err |= 0x04; + if(EXI_Deselect(chn)==0) err |= 0x08; + + if(err) ret = CARD_ERROR_NOCARD; + else ret = CARD_ERROR_READY; + + return ret; +} + +static void __dsp_initcallback(dsptask_t *task) +{ + u32 chn; + card_block *card = NULL; + chn = 0; + while(chndsp_task==task) break; + chn++; + } + if(chn>=EXI_CHANNEL_2) return; + + DSP_SendMailTo(0xFF000000); + while(DSP_CheckMailTo()); + DSP_SendMailTo((u32)card->workarea); + while(DSP_CheckMailTo()); +} + +static u8 tmp_buffer[64] ATTRIBUTE_ALIGN(32); +static void __dsp_donecallback(dsptask_t *task) +{ + + u8 status; + s32 ret; + u32 chn,len,key; + u32 workarea,val; + card_block *card = NULL; + chn = 0; + while(chndsp_task==task) break; + chn++; + } + if(chn>=EXI_CHANNEL_2) return; + + workarea = (u32)card->workarea; + workarea = ((workarea+47)&~0x1f); + key = ((u32*)workarea)[8]; + + val = (key^card->cipher)&~0xffff; + len = __card_dummylen(); + if(__card_readarrayunlock(chn,val,tmp_buffer,len,1)<0) { + EXI_Unlock(chn); + __card_mountcallback(chn,CARD_ERROR_NOCARD); + return; + } + + val = exnor(card->cipher,((len+card->latency+4)<<3)+1); + { + u32 a,b,c,r1,r2,r3; + a = (val<<23); + b = (val<<15); + c = (val<<7); + r1 = (val^c); + r2 = (b^r1); + r3 = ~(a^r2); //eqv(a,r2) + r1 = (val|(r3>>31)); + card->cipher = r1; + } + + val = ((key<<16)^card->cipher)&~0xffff; + len = __card_dummylen(); + if(__card_readarrayunlock(chn,val,tmp_buffer,len,1)<0) { + EXI_Unlock(chn); + __card_mountcallback(chn,CARD_ERROR_NOCARD); + return; + } + + ret = __card_readstatus(chn,&status); + if(EXI_Probe(chn)==0) { + EXI_Unlock(chn); + __card_mountcallback(chn,CARD_ERROR_NOCARD); + return; + } + if(!ret && !(status&CARD_STATUS_UNLOCKED)) { + EXI_Unlock(chn); + ret = CARD_ERROR_IOERROR; + } + __card_mountcallback(chn,ret); +} + +static s32 __dounlock(s32 chn,u32 *key) +{ + u32 array_addr,len,val; + u32 a,b,c,d,e; + card_block *card = &cardmap[chn]; + u32 *workarea = card->workarea; + u32 *cipher1 = (u32*)(((u32)card->workarea+47)&~31); + u32 *cipher2 = &cipher1[8]; + array_addr = __card_initval(); + len = __card_dummylen(); + + if(__card_readarrayunlock(chn,array_addr,tmp_buffer,len,0)<0) return CARD_ERROR_NOCARD; + + + val = exnor_1st(array_addr,(len<<3)+1); + { + u32 a,b,c,r1,r2,r3; + a = (val>>23); + b = (val>>15); + c = (val>>7); + r1 = (val^c); + r2 = (b^r1); + r3 = ~(a^r2); //eqv(a,r2) + r1 = (val|(r3<<31)); + card->cipher = r1; + } + card->cipher = bitrev(card->cipher); + + array_addr = 0; + len = __card_dummylen(); + if(__card_readarrayunlock(chn,array_addr,tmp_buffer,len+20,1)<0) return CARD_ERROR_NOCARD; + + a = ((u32*)tmp_buffer)[0]; + b = ((u32*)tmp_buffer)[1]; + c = ((u32*)tmp_buffer)[2]; + d = ((u32*)tmp_buffer)[3]; + e = ((u32*)tmp_buffer)[4]; + + a = a^card->cipher; + val = exnor(card->cipher,32); + { + u32 a,b,c,r1,r2,r3; + a = (val<<23); + b = (val<<15); + c = (val<<7); + r1 = (val^c); + r2 = (b^r1); + r3 = ~(a^r2); //eqv(a,r2) + r1 = (val|(r3>>31)); + card->cipher = r1; + } + + b = b^card->cipher; + val = exnor(card->cipher,32); + { + u32 a,b,c,r1,r2,r3; + a = (val<<23); + b = (val<<15); + c = (val<<7); + r1 = (val^c); + r2 = (b^r1); + r3 = ~(a^r2); //eqv(a,r2) + r1 = (val|(r3>>31)); + card->cipher = r1; + } + + c = c^card->cipher; + val = exnor(card->cipher,32); + { + u32 a,b,c,r1,r2,r3; + a = (val<<23); + b = (val<<15); + c = (val<<7); + r1 = (val^c); + r2 = (b^r1); + r3 = ~(a^r2); //eqv(a,r2) + r1 = (val|(r3>>31)); + card->cipher = r1; + } + + d = d^card->cipher; + val = exnor(card->cipher,32); + { + u32 a,b,c,r1,r2,r3; + a = (val<<23); + b = (val<<15); + c = (val<<7); + r1 = (val^c); + r2 = (b^r1); + r3 = ~(a^r2); //eqv(a,r2) + r1 = (val|(r3>>31)); + card->cipher = r1; + } + + e = e^card->cipher; + val = exnor(card->cipher,(len<<3)); + { + u32 a,b,c,r1,r2,r3; + a = (val<<23); + b = (val<<15); + c = (val<<7); + r1 = (val^c); + r2 = (b^r1); + r3 = ~(a^r2); //eqv(a,r2) + r1 = (val|(r3>>31)); + card->cipher = r1; + } + + val = exnor(card->cipher,33); + { + u32 a,b,c,r1,r2,r3; + a = (val<<23); + b = (val<<15); + c = (val<<7); + r1 = (val^c); + r2 = (b^r1); + r3 = ~(a^r2); //eqv(a,r2) + r1 = (val|(r3>>31)); + card->cipher = r1; + } + + cipher1[0] = d; + cipher1[1] = e; + workarea[0] = (u32)cipher1; + workarea[1] = 8; +#ifdef HW_RVL + workarea[2] = 0x10000000; // use MEM2 base +#else + workarea[2] = 0; // use ARAM base +#endif + workarea[3] = (u32)cipher2; + DCFlushRange(cipher1,8); + DCInvalidateRange(cipher2,4); + DCFlushRange(workarea,16); + + card->dsp_task.prio = 255; + card->dsp_task.iram_maddr = (u16*)MEM_VIRTUAL_TO_PHYSICAL(_cardunlockdata); + card->dsp_task.iram_len = 352; + card->dsp_task.iram_addr = 0x0000; + card->dsp_task.init_vec = 16; + card->dsp_task.res_cb = NULL; + card->dsp_task.req_cb = NULL; + card->dsp_task.init_cb = __dsp_initcallback; + card->dsp_task.done_cb = __dsp_donecallback; + DSP_AddTask(&card->dsp_task); + + key[0] = a; + key[1] = b; + key[2] = c; + + return CARD_ERROR_READY; +} + +s32 CARD_Init(const char *gamecode,const char *company) +{ + u32 i,level; + + if(card_inited) return CARD_ERROR_READY; + if(gamecode && strlen(gamecode)<=4) memcpy(card_gamecode,gamecode,4); + if(company && strlen(company)<=2) memcpy(card_company,company,2); + + _CPU_ISR_Disable(level); + DSP_Init(); + + memset(cardmap,0,sizeof(card_block)*2); + for(i=0;i<2;i++) { + cardmap[i].result = CARD_ERROR_NOCARD; + LWP_InitQueue(&cardmap[i].wait_sync_queue); + SYS_CreateAlarm(&cardmap[i].timeout_svc); + } + SYS_RegisterResetFunc(&card_resetinfo); + card_inited = 1; + _CPU_ISR_Restore(level); + return CARD_ERROR_READY; +} + +s32 CARD_Probe(s32 chn) +{ + return EXI_Probe(chn); +} + +s32 CARD_ProbeEx(s32 chn,s32 *mem_size,s32 *sect_size) +{ + s32 ret; + u32 level,card_id; + card_block *card = NULL; + if(chn=EXI_CHANNEL_2) return CARD_ERROR_FATAL_ERROR; + card = &cardmap[chn]; + + _CPU_ISR_Disable(level); + ret = EXI_ProbeEx(chn); + if(ret<=0) { + if(!ret) ret = CARD_ERROR_BUSY; + else ret = CARD_ERROR_NOCARD; + _CPU_ISR_Restore(level); + return ret; + } + + if(card->attached) { + if(card->mount_step<1) { + _CPU_ISR_Restore(level); + return CARD_ERROR_BUSY; + } + if(mem_size) *mem_size = card->card_size; + if(sect_size) *sect_size = card->sector_size; + + _CPU_ISR_Restore(level); + return CARD_ERROR_READY; + } + + if(EXI_GetState(chn)&EXI_FLAG_ATTACH) ret = CARD_ERROR_WRONGDEVICE; + else { + ret = CARD_ERROR_BUSY; + if(EXI_GetID(chn,EXI_DEVICE_0,&card_id)) { + if(!__card_iscard(card_id)) ret = CARD_ERROR_WRONGDEVICE; + else { + if(mem_size) *mem_size = card_id&0xFC; + if(sect_size) { + u32 idx = _ROTL(card_id,23)&0x1c; + *sect_size = card_sector_size[idx>>2]; + } + ret = CARD_ERROR_READY; + } + } + } + + _CPU_ISR_Restore(level); + return ret; +} + +s32 CARD_MountAsync(s32 chn,void *workarea,cardcallback detach_cb,cardcallback attach_cb) +{ + s32 ret = CARD_ERROR_READY; + u32 level; + cardcallback attachcb = NULL; + card_block *card = NULL; + if(!workarea) return CARD_ERROR_NOCARD; + if(chn=EXI_CHANNEL_2) return CARD_ERROR_FATAL_ERROR; + card = &cardmap[chn]; + + _CPU_ISR_Disable(level); + if(card->result==CARD_ERROR_BUSY) { + _CPU_ISR_Restore(level); + return CARD_ERROR_BUSY; + } + if(card->attached || !(EXI_GetState(chn)&EXI_FLAG_ATTACH)) { + card->result = CARD_ERROR_BUSY; + card->workarea = workarea; + card->card_ext_cb = detach_cb; + + attachcb = attach_cb; + if(!attachcb) attachcb = __card_defaultapicallback; + card->card_api_cb = attachcb; + card->card_exi_cb = NULL; + + if(!card->attached) { + if(EXI_Attach(chn,__card_exthandler)==0) { + card->result = CARD_ERROR_NOCARD; + _CPU_ISR_Restore(level); + return CARD_ERROR_NOCARD; + } + } + card->mount_step = 0; + card->attached = 1; + EXI_RegisterEXICallback(chn,NULL); + SYS_CancelAlarm(card->timeout_svc); + card->curr_dir = NULL; + card->curr_fat = NULL; + _CPU_ISR_Restore(level); + + card->card_unlock_cb = __card_mountcallback; + if(EXI_Lock(chn,EXI_DEVICE_0,__card_unlockedhandler)==0) return 0; + + card->card_unlock_cb = NULL; + __card_domount(chn); + return 1; + } + + ret = CARD_ERROR_WRONGDEVICE; + _CPU_ISR_Restore(level); + return ret; +} + +s32 CARD_Mount(s32 chn,void *workarea,cardcallback detach_cb) +{ + s32 ret; + if((ret=CARD_MountAsync(chn,workarea,detach_cb,__card_synccallback))>=0) { + ret = __card_sync(chn); + } + return ret; +} + +s32 CARD_Unmount(s32 chn) +{ + s32 ret; + card_block *card = NULL; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + + if((ret=__card_getcntrlblock(chn,&card))<0) ret = CARD_ERROR_NOCARD; + + __card_dounmount(chn,ret); + return CARD_ERROR_READY; +} + +s32 CARD_ReadAsync(card_file *file,void *buffer,u32 len,u32 offset,cardcallback callback) +{ + s32 ret; + cardcallback cb = NULL; + card_block *card = NULL; + + if(len<=0 || (len&0x1ff) || (offset>0 && (offset&0x1ff))) return CARD_ERROR_FATAL_ERROR; + if((ret=__card_seek(file,len,offset,&card))<0) return ret; + + DCInvalidateRange(buffer,len); + + cb = callback; + if(!cb) cb = __card_defaultapicallback; + card->card_api_cb = cb; + + if(len>=(card->sector_size-(file->offset&(card->sector_size-1)))) len = (card->sector_size-(file->offset&(card->sector_size-1))); + + if((ret=__card_read(file->chn,(file->iblock*card->sector_size),len,buffer,__read_callback))<0) { + __card_putcntrlblock(card,ret); + return ret; + } + return 0; +} + +s32 CARD_Read(card_file *file,void *buffer,u32 len,u32 offset) +{ + s32 ret; + + if((ret=CARD_ReadAsync(file,buffer,len,offset,__card_synccallback))>=0) { + ret = __card_sync(file->chn); + } + return ret; +} + +s32 CARD_WriteAsync(card_file *file,void *buffer,u32 len,u32 offset,cardcallback callback) +{ + s32 ret; + cardcallback cb = NULL; + card_block *card = NULL; + + if((ret=__card_seek(file,len,offset,&card))<0) return ret; + if(len<0 || (len&(card->sector_size-1)) || (offset>0 && offset&(card->sector_size-1))) { + __card_putcntrlblock(card,CARD_ERROR_FATAL_ERROR); + return CARD_ERROR_FATAL_ERROR; + } + + DCStoreRange(buffer,len); + cb = callback; + if(!cb) cb = __card_defaultapicallback; + card->card_api_cb = cb; + + card->cmd_usr_buf = buffer; + if((ret=__card_sectorerase(file->chn,(file->iblock*card->sector_size),__erase_callback))>=0) return ret; + __card_putcntrlblock(card,ret); + return ret; +} + +s32 CARD_Write(card_file *file,void *buffer,u32 len,u32 offset) +{ + s32 ret; + + if((ret=CARD_WriteAsync(file,buffer,len,offset,__card_synccallback))>=0) { + ret = __card_sync(file->chn); + } + return ret; +} + +s32 CARD_CreateAsync(s32 chn,const char *filename,u32 size,card_file *file,cardcallback callback) +{ + u32 i,len; + s32 ret,filenum; + cardcallback cb = NULL; + card_block *card = NULL; + struct card_bat *fatblock = NULL; + struct card_dat *dirblock = NULL; + struct card_direntry *entry = NULL; + len = strlen(filename); + if(len>CARD_FILENAMELEN) return CARD_ERROR_NAMETOOLONG; + + if((ret=__card_getcntrlblock(chn,&card))<0) return ret; + if(size<=0 || size%card->sector_size) return CARD_ERROR_FATAL_ERROR; + + dirblock = __card_getdirblock(card); + + filenum = -1; + entry = dirblock->entries; + for(i=0;ifreeblocks*card->sector_size)card_api_cb = cb; + + entry[filenum].length = size/card->sector_size; + memset(entry[filenum].filename,0,CARD_FILENAMELEN); + memcpy(entry[filenum].filename,filename,len+1); + + card->curr_file = file; + file->chn = chn; + file->filenum = filenum; + if((ret=__card_allocblock(chn,(size/card->sector_size),__card_createfatcallback))<0) { + __card_putcntrlblock(card,ret); + return ret; + } + + return 0; +} + +s32 CARD_Create(s32 chn,const char *filename,u32 size,card_file *file) +{ + s32 ret; + + if((ret=CARD_CreateAsync(chn,filename,size,file,__card_synccallback))>=0) { + ret = __card_sync(chn); + } + return ret; +} + +s32 CARD_CreateEntryAsync(s32 chn,card_dir *direntry,card_file *file,cardcallback callback) +{ + u32 i,len; + s32 ret,filenum; + cardcallback cb = NULL; + card_block *card = NULL; + struct card_bat *fatblock = NULL; + struct card_dat *dirblock = NULL; + struct card_direntry *entry = NULL; + len = strlen((const char*)direntry->filename); + if(len>CARD_FILENAMELEN) return CARD_ERROR_NAMETOOLONG; + + if((ret=__card_getcntrlblock(chn,&card))<0) return ret; + if(direntry->filelen<=0 || direntry->filelen%card->sector_size) return CARD_ERROR_FATAL_ERROR; + + dirblock = __card_getdirblock(card); + + filenum = -1; + entry = dirblock->entries; + for(i=0;ifilename,len)==0) { + if((entry->gamecode[0]==0xff || entry->company[0]==0xff) + || ((entry->gamecode[0]!=0xff && memcmp(entry[i].gamecode,entry->gamecode,4)==0) + && (entry->company[0]!=0xff && memcmp(entry[i].company,entry->company,2)==0))) { + __card_putcntrlblock(card,CARD_ERROR_EXIST); + return CARD_ERROR_EXIST; + } + } + } + if(filenum==-1) { + __card_putcntrlblock(card,CARD_ERROR_NOENT); + return CARD_ERROR_NOENT; + } + + fatblock = __card_getbatblock(card); + if((fatblock->freeblocks*card->sector_size)filelen) { + __card_putcntrlblock(card,CARD_ERROR_INSSPACE); + return CARD_ERROR_INSSPACE; + } + + cb = callback; + if(!cb) cb = __card_defaultapicallback; + card->card_api_cb = cb; + + entry[filenum].length = direntry->filelen/card->sector_size; + memset(entry[filenum].filename,0,CARD_FILENAMELEN); + memcpy(entry[filenum].filename,direntry->filename,len+1); + + card->curr_file = file; + file->chn = chn; + file->filenum = filenum; + if((ret=__card_allocblock(chn,(direntry->filelen/card->sector_size),__card_createfatcallback))<0) { + __card_putcntrlblock(card,ret); + return ret; + } + + return 0; +} + +s32 CARD_CreateEntry(s32 chn,card_dir *direntry,card_file *file) +{ + s32 ret; + + if((ret=CARD_CreateEntryAsync(chn,direntry,file,__card_synccallback))>=0) { + ret = __card_sync(chn); + } + return ret; +} + +s32 CARD_Open(s32 chn,const char *filename,card_file *file) +{ + s32 ret,fileno; + struct card_dat *dirblock = NULL; + card_block *card = NULL; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + + file->filenum = -1; + if((ret=__card_getcntrlblock(chn,&card))<0) return ret; + if((ret=__card_getfilenum(card,filename,(const char*)card_gamecode,(const char*)card_company,&fileno))<0) { + __card_putcntrlblock(card,ret); + return ret; + } + dirblock = __card_getdirblock(card); + if(dirblock->entries[fileno].block<5 || dirblock->entries[fileno].block>=card->blocks) { + __card_putcntrlblock(card,CARD_ERROR_BROKEN); + return CARD_ERROR_BROKEN; + } + file->chn = chn; + file->filenum = fileno; + file->offset = 0; + file->len = dirblock->entries[fileno].length*card->sector_size; + file->iblock = dirblock->entries[fileno].block; + + __card_putcntrlblock(card,CARD_ERROR_READY); + return CARD_ERROR_READY; +} + +s32 CARD_OpenEntry(s32 chn,card_dir *entry,card_file *file) +{ + s32 ret,fileno; + struct card_dat *dirblock = NULL; + card_block *card = NULL; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + + file->filenum = -1; + if((ret=__card_getcntrlblock(chn,&card))<0) return ret; + if((ret=__card_getfilenum(card,(const char*)entry->filename,(const char*)entry->gamecode,(const char*)entry->company,&fileno))<0) { + __card_putcntrlblock(card,ret); + return ret; + } + + dirblock = __card_getdirblock(card); + if(dirblock->entries[fileno].block<5 || dirblock->entries[fileno].block>=card->blocks) { + __card_putcntrlblock(card,CARD_ERROR_BROKEN); + return CARD_ERROR_BROKEN; + } + + file->chn = chn; + file->filenum = entry->fileno; + file->offset = 0; + file->len = dirblock->entries[fileno].length*card->sector_size; + file->iblock = dirblock->entries[fileno].block; + + __card_putcntrlblock(card,CARD_ERROR_READY); + return CARD_ERROR_READY; +} + +s32 CARD_Close(card_file *file) +{ + s32 ret; + card_block *card = NULL; + + if(file->chnchn>=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + if(file->filenum<0 || file->filenum>=CARD_MAXFILES) return CARD_ERROR_NOFILE; + if((ret=__card_getcntrlblock(file->chn,&card))<0) return ret; + + file->chn = -1; + __card_putcntrlblock(card,CARD_ERROR_READY); + return CARD_ERROR_READY; +} + +s32 CARD_DeleteAsync(s32 chn,const char *filename,cardcallback callback) +{ + s32 ret,fileno; + cardcallback cb = NULL; + card_block *card = NULL; + struct card_dat *dirblock = NULL; + struct card_direntry *entry = NULL; + if((ret=__card_getcntrlblock(chn,&card))<0) return ret; + if((ret=__card_getfilenum(card,filename,(const char*)card_gamecode,(const char*)card_company,&fileno))<0) { + __card_putcntrlblock(card,ret); + return ret; + } + + dirblock = __card_getdirblock(card); + entry = &dirblock->entries[fileno]; + + card->curr_fileblock = entry->block; + memset(entry,-1,sizeof(struct card_direntry)); + + cb = callback; + if(!cb) cb = __card_defaultapicallback; + card->card_api_cb = cb; + + if((ret=__card_updatedir(chn,__delete_callback))>=0) return ret; + + __card_putcntrlblock(card,ret); + return ret; +} + +s32 CARD_Delete(s32 chn,const char *filename) +{ + s32 ret; + if((ret=CARD_DeleteAsync(chn,filename,__card_synccallback))>=0) { + ret = __card_sync(chn); + } + return ret; +} + +s32 CARD_DeleteEntryAsync(s32 chn,card_dir *dir_entry,cardcallback callback) +{ + s32 ret; + cardcallback cb = NULL; + card_block *card = NULL; + struct card_dat *dirblock = NULL; + struct card_direntry *entry = NULL; + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + if((ret=__card_getcntrlblock(chn,&card))<0) return ret; + + dirblock = __card_getdirblock(card); + entry = &dirblock->entries[dir_entry->fileno]; + + card->curr_fileblock = entry->block; + memset(entry,-1,sizeof(struct card_direntry)); + + cb = callback; + if(!cb) cb = __card_defaultapicallback; + card->card_api_cb = cb; + + if((ret=__card_updatedir(chn,__delete_callback))>=0) return ret; + + __card_putcntrlblock(card,ret); + return ret; +} + +s32 CARD_DeleteEntry(s32 chn,card_dir *dir_entry) +{ + s32 ret; + if((ret=CARD_DeleteEntryAsync(chn,dir_entry,__card_synccallback))>=0) { + ret = __card_sync(chn); + } + return ret; +} + +s32 CARD_FormatAsync(s32 chn,cardcallback callback) +{ + u32 enc; + + enc = SYS_GetFontEncoding(); + return __card_formatregion(chn,enc,callback); +} + +s32 CARD_Format(s32 chn) +{ + s32 ret; + u32 enc; + + enc = SYS_GetFontEncoding(); + if((ret=__card_formatregion(chn,enc,__card_synccallback))>=0) { + ret = __card_sync(chn); + } + return ret; +} + +s32 CARD_GetErrorCode(s32 chn) +{ + card_block *card = NULL; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + card = &cardmap[chn]; + return card->result; +} + +s32 __card_findnext(card_dir *dir) +{ + s32 ret; + struct card_dat *dirblock = NULL; + struct card_direntry *entries = NULL; + card_block *card = NULL; + + if(dir->chnchn>=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + if(dir->fileno>=CARD_MAXFILES) return CARD_ERROR_NOFILE; + if((ret=__card_getcntrlblock(dir->chn,&card))<0) return ret; + + if(!card->attached) return CARD_ERROR_NOCARD; + dirblock = __card_getdirblock(card); + + entries = dirblock->entries; + do { + //printf("%s\n", entries[dir->fileno].filename); + if(entries[dir->fileno].gamecode[0]!=0xff) { + if ((dir->showall || memcmp(entries[dir->fileno].gamecode,card_gamecode,4)==0) + && (dir->showall || memcmp(entries[dir->fileno].company,card_company,2)==0)) { + dir->filelen = entries[dir->fileno].length*card->sector_size; + memcpy(dir->filename, entries[dir->fileno].filename, CARD_FILENAMELEN); + memcpy(dir->gamecode, entries[dir->fileno].gamecode, 4); + memcpy(dir->company, entries[dir->fileno].company, 2); + + __card_putcntrlblock(card,CARD_ERROR_READY); + return CARD_ERROR_READY; + } + } + dir->fileno++; + } while (dir->fileno < CARD_MAXFILES); + __card_putcntrlblock(card,CARD_ERROR_NOFILE); + return CARD_ERROR_NOFILE; +} + +s32 CARD_FindFirst(s32 chn, card_dir *dir, bool showall) +{ + // initialise structure + dir->chn = chn; + dir->fileno = 0; + dir->filelen = 0; + dir->filename[0] = 0; + dir->gamecode[0] = 0; + dir->company[0] = 0; + dir->showall = showall; + return __card_findnext(dir); +} + +s32 CARD_FindNext(card_dir *dir) +{ + dir->fileno++; + + return __card_findnext(dir); +} + +s32 CARD_GetDirectory(s32 chn,card_dir *dir_entries,s32 *count,bool showall) +{ + s32 i,cnt; + s32 ret = CARD_ERROR_READY; + struct card_dat *dirblock = NULL; + struct card_direntry *entries = NULL; + card_block *card = NULL; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + if((ret=__card_getcntrlblock(chn,&card))<0) return ret; + + if(!card->attached) return CARD_ERROR_NOCARD; + dirblock = __card_getdirblock(card); + + entries = dirblock->entries; + for(i=0,cnt=0;isector_size; + memcpy(dir_entries[cnt].gamecode,entries[i].gamecode,4); + memcpy(dir_entries[cnt].company,entries[i].company,2); + memcpy(dir_entries[cnt].filename,entries[i].filename,CARD_FILENAMELEN); + cnt++; + } + } + } + if(count) *count = cnt; + if(cnt==0) ret = CARD_ERROR_NOFILE; + __card_putcntrlblock(card,ret); + return ret; +} + +s32 CARD_GetSectorSize(s32 chn,u32 *sector_size) +{ + s32 ret; + card_block *card = NULL; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + if((ret=__card_getcntrlblock(chn,&card))<0) return ret; + + *sector_size = card->sector_size; + ret = __card_putcntrlblock(card,CARD_ERROR_READY); + + return ret; +} + +s32 CARD_GetBlockCount(s32 chn,u32 *block_count) +{ + s32 ret; + card_block *card = NULL; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + if((ret=__card_getcntrlblock(chn,&card))<0) return ret; + + *block_count = card->blocks; + ret = __card_putcntrlblock(card,CARD_ERROR_READY); + + return ret; +} + +s32 CARD_GetStatus(s32 chn,s32 fileno,card_stat *stats) +{ + s32 ret; + card_block *card = NULL; + struct card_dat *dirblock = NULL; + struct card_direntry *entry = NULL; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + if(fileno<0 || fileno>=CARD_MAXFILES) return CARD_ERROR_FATAL_ERROR; + + if((ret=__card_getcntrlblock(chn,&card))<0) return ret; + + dirblock = __card_getdirblock(card); + if(dirblock) { + entry = &dirblock->entries[fileno]; + memcpy(stats->gamecode,entry->gamecode,4); + memcpy(stats->company,entry->company,2); + memcpy(stats->filename,entry->filename,CARD_FILENAMELEN); + stats->len = entry->length*card->sector_size; + stats->time = entry->lastmodified; + stats->banner_fmt = entry->bannerfmt; + stats->icon_addr = entry->iconaddr; + stats->icon_fmt = entry->iconfmt; + stats->icon_speed = entry->iconspeed; + stats->comment_addr = entry->commentaddr; + __card_updateiconoffsets(entry,stats); + } + + return __card_putcntrlblock(card,CARD_ERROR_READY); +} + +s32 CARD_SetStatusAsync(s32 chn,s32 fileno,card_stat *stats,cardcallback callback) +{ + s32 ret; + card_block *card = NULL; + struct card_dat *dirblock = NULL; + struct card_direntry *entry = NULL; + + if(chn=EXI_CHANNEL_2) return CARD_ERROR_NOCARD; + if(fileno<0 || fileno>=CARD_MAXFILES) return CARD_ERROR_FATAL_ERROR; + if(stats->icon_addr!=-1 && stats->icon_addr>CARD_READSIZE) return CARD_ERROR_FATAL_ERROR; + if(stats->comment_addr!=-1 && stats->comment_addr>8128) return CARD_ERROR_FATAL_ERROR; + if((ret=__card_getcntrlblock(chn,&card))<0) return ret; + + ret = CARD_ERROR_BROKEN; + dirblock = __card_getdirblock(card); + if(dirblock) { + entry = &dirblock->entries[fileno]; + entry->bannerfmt = stats->banner_fmt; + entry->iconaddr = stats->icon_addr; + entry->iconfmt = stats->icon_fmt; + entry->iconspeed = stats->icon_speed; + entry->commentaddr = stats->comment_addr; + __card_updateiconoffsets(entry,stats); + + if(entry->iconaddr==-1) entry->iconfmt = ((entry->iconfmt&~CARD_ICON_MASK)|CARD_ICON_CI); + + entry->lastmodified = time(NULL); + if((ret=__card_updatedir(chn,callback))>=0) return ret; + } + + return __card_putcntrlblock(card,ret); +} + +s32 CARD_SetStatus(s32 chn,s32 fileno,card_stat *stats) +{ + s32 ret; + + if((ret=CARD_SetStatusAsync(chn,fileno,stats,__card_synccallback))>=0) { + ret = __card_sync(chn); + } + return ret; +} + +s32 CARD_GetAttributes(s32 chn,s32 fileno,u8 *attr) +{ + s32 ret; + struct card_direntry entry; + + if((ret=__card_getstatusex(chn,fileno,&entry))==CARD_ERROR_READY) { + *attr = entry.permission; + } + return ret; +} + +s32 CARD_SetAttributesAsync(s32 chn,s32 fileno,u8 attr,cardcallback callback) +{ + s32 ret; + struct card_direntry entry; + + if((ret=__card_getstatusex(chn,fileno,&entry))>=0) { + entry.permission = attr; + ret = __card_setstatusexasync(chn,fileno,&entry,callback); + } + return ret; +} + +s32 CARD_SetAttributes(s32 chn,s32 fileno,u8 attr) +{ + s32 ret; + + if((ret=CARD_SetAttributesAsync(chn,fileno,attr,__card_synccallback))>=0) { + ret = __card_sync(chn); + } + return ret; +} + +s32 CARD_SetCompany(const char *company) +{ + u32 level,i; + + _CPU_ISR_Disable(level); + for(i=0;i<2;i++) card_company[i] = 0xff; + if(company && strlen(company)<=2) memcpy(card_company,company,2) ; + _CPU_ISR_Restore(level); + + return CARD_ERROR_READY; +} + +s32 CARD_SetGamecode(const char *gamecode) +{ + u32 level,i; + + _CPU_ISR_Disable(level); + for(i=0;i<4;i++) card_gamecode[i] = 0xff; + if(gamecode && strlen(gamecode)<=4) memcpy(card_gamecode,gamecode,4) ; + _CPU_ISR_Restore(level); + + return CARD_ERROR_READY; +} diff --git a/wii/libogc/libogc/cond.c b/wii/libogc/libogc/cond.c new file mode 100644 index 0000000000..9b893a83f8 --- /dev/null +++ b/wii/libogc/libogc/cond.c @@ -0,0 +1,199 @@ +/*------------------------------------------------------------- + +cond.c -- Thread subsystem V + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#include +#include +#include "asm.h" +#include "mutex.h" +#include "lwp_threadq.h" +#include "lwp_objmgr.h" +#include "lwp_config.h" +#include "cond.h" + +#define LWP_OBJTYPE_COND 5 + +#define LWP_CHECK_COND(hndl) \ +{ \ + if(((hndl)==LWP_COND_NULL) || (LWP_OBJTYPE(hndl)!=LWP_OBJTYPE_COND)) \ + return NULL; \ +} + +typedef struct _cond_st { + lwp_obj object; + mutex_t lock; + lwp_thrqueue wait_queue; +} cond_st; + +lwp_objinfo _lwp_cond_objects; + +extern int clock_gettime(struct timespec *tp); +extern void timespec_subtract(const struct timespec *tp_start,const struct timespec *tp_end,struct timespec *result); + +void __lwp_cond_init() +{ + __lwp_objmgr_initinfo(&_lwp_cond_objects,LWP_MAX_CONDVARS,sizeof(cond_st)); +} + +static __inline__ cond_st* __lwp_cond_open(cond_t cond) +{ + LWP_CHECK_COND(cond); + return (cond_st*)__lwp_objmgr_get(&_lwp_cond_objects,LWP_OBJMASKID(cond)); +} + +static __inline__ void __lwp_cond_free(cond_st *cond) +{ + __lwp_objmgr_close(&_lwp_cond_objects,&cond->object); + __lwp_objmgr_free(&_lwp_cond_objects,&cond->object); +} + +static cond_st* __lwp_cond_allocate() +{ + cond_st *cond; + + __lwp_thread_dispatchdisable(); + cond = (cond_st*)__lwp_objmgr_allocate(&_lwp_cond_objects); + if(cond) { + __lwp_objmgr_open(&_lwp_cond_objects,&cond->object); + return cond; + } + __lwp_thread_dispatchenable(); + return NULL; +} + +static s32 __lwp_cond_waitsupp(cond_t cond,mutex_t mutex,u64 timeout,u8 timedout) +{ + u32 status,mstatus,level; + cond_st *thecond = __lwp_cond_open(cond); + + if(!thecond) return -1; + + if(thecond->lock!=LWP_MUTEX_NULL && thecond->lock!=mutex) { + __lwp_thread_dispatchenable(); + return EINVAL; + } + + + LWP_MutexUnlock(mutex); + if(!timedout) { + thecond->lock = mutex; + _CPU_ISR_Disable(level); + __lwp_threadqueue_csenter(&thecond->wait_queue); + _thr_executing->wait.ret_code = 0; + _thr_executing->wait.queue = &thecond->wait_queue; + _thr_executing->wait.id = cond; + _CPU_ISR_Restore(level); + __lwp_threadqueue_enqueue(&thecond->wait_queue,timeout); + __lwp_thread_dispatchenable(); + + status = _thr_executing->wait.ret_code; + if(status && status!=ETIMEDOUT) + return status; + } else { + __lwp_thread_dispatchenable(); + status = ETIMEDOUT; + } + + mstatus = LWP_MutexLock(mutex); + if(mstatus) + return EINVAL; + + return status; +} + +static s32 __lwp_cond_signalsupp(cond_t cond,u8 isbroadcast) +{ + lwp_cntrl *thethread; + cond_st *thecond = __lwp_cond_open(cond); + if(!thecond) return -1; + + do { + thethread = __lwp_threadqueue_dequeue(&thecond->wait_queue); + if(!thethread) thecond->lock = LWP_MUTEX_NULL; + } while(isbroadcast && thethread); + __lwp_thread_dispatchenable(); + return 0; +} + +s32 LWP_CondInit(cond_t *cond) +{ + cond_st *ret; + + if(!cond) return -1; + + ret = __lwp_cond_allocate(); + if(!ret) return ENOMEM; + + ret->lock = LWP_MUTEX_NULL; + __lwp_threadqueue_init(&ret->wait_queue,LWP_THREADQ_MODEFIFO,LWP_STATES_WAITING_FOR_CONDVAR,ETIMEDOUT); + + *cond = (cond_t)(LWP_OBJMASKTYPE(LWP_OBJTYPE_COND)|LWP_OBJMASKID(ret->object.id)); + __lwp_thread_dispatchenable(); + + return 0; +} + +s32 LWP_CondWait(cond_t cond,mutex_t mutex) +{ + return __lwp_cond_waitsupp(cond,mutex,LWP_THREADQ_NOTIMEOUT,FALSE); +} + +s32 LWP_CondSignal(cond_t cond) +{ + return __lwp_cond_signalsupp(cond,FALSE); +} + +s32 LWP_CondBroadcast(cond_t cond) +{ + return __lwp_cond_signalsupp(cond,TRUE); +} + +s32 LWP_CondTimedWait(cond_t cond,mutex_t mutex,const struct timespec *abstime) +{ + u64 timeout = LWP_THREADQ_NOTIMEOUT; + bool timedout = FALSE; + + if(abstime) timeout = __lwp_wd_calc_ticks(abstime); + return __lwp_cond_waitsupp(cond,mutex,timeout,timedout); +} + +s32 LWP_CondDestroy(cond_t cond) +{ + cond_st *ptr = __lwp_cond_open(cond); + if(!ptr) return -1; + + if(__lwp_threadqueue_first(&ptr->wait_queue)) { + __lwp_thread_dispatchenable(); + return EBUSY; + } + __lwp_thread_dispatchenable(); + + __lwp_cond_free(ptr); + return 0; +} diff --git a/wii/libogc/libogc/conf.c b/wii/libogc/libogc/conf.c new file mode 100644 index 0000000000..2492e45c8a --- /dev/null +++ b/wii/libogc/libogc/conf.c @@ -0,0 +1,479 @@ +/*------------------------------------------------------------- + +conf.c -- SYSCONF support + +Copyright (C) 2008 +Hector Martin (marcan) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#if defined(HW_RVL) + +#include +#include +#include +#include "ipc.h" +#include "asm.h" +#include "processor.h" +#include "conf.h" + + +static int __conf_inited = 0; +static u8 __conf_buffer[0x4000] ATTRIBUTE_ALIGN(32); +static char __conf_txt_buffer[0x101] ATTRIBUTE_ALIGN(32); + +static const char __conf_file[] ATTRIBUTE_ALIGN(32) = "/shared2/sys/SYSCONF"; +static const char __conf_txt_file[] ATTRIBUTE_ALIGN(32) = "/title/00000001/00000002/data/setting.txt"; + +void __CONF_DecryptTextBuffer(void) +{ + u32 key = 0x73B5DBFA; + int i; + + for(i=0; i<0x100; i++) { + __conf_txt_buffer[i] ^= key & 0xff; + key = (key<<1) | (key>>31); + } +} + +s32 CONF_Init(void) +{ + int fd; + int ret; + + if(__conf_inited) return 0; + + fd = IOS_Open(__conf_file,1); + if(fd < 0) return fd; + + memset(__conf_buffer,0,0x4000); + memset(__conf_txt_buffer,0,0x101); + + ret = IOS_Read(fd, __conf_buffer, 0x4000); + IOS_Close(fd); + if(ret != 0x4000) return CONF_EBADFILE; + + fd = IOS_Open(__conf_txt_file,1); + if(fd < 0) return fd; + + ret = IOS_Read(fd, __conf_txt_buffer, 0x100); + IOS_Close(fd); + if(ret != 0x100) return CONF_EBADFILE; + + if(memcmp(__conf_buffer, "SCv0", 4)) return CONF_EBADFILE; + + __CONF_DecryptTextBuffer(); + + __conf_inited = 1; + return 0; +} + +int __CONF_GetTxt(const char *name, char *buf, int length) +{ + char *line = __conf_txt_buffer; + char *delim, *end; + int slen; + int nlen = strlen(name); + + if(!__conf_inited) return CONF_ENOTINIT; + + while(line < (__conf_txt_buffer+0x100) ) { + delim = strchr(line, '='); + if(delim && ((delim - line) == nlen) && !memcmp(name, line, nlen)) { + delim++; + end = strchr(line, '\r'); + if (!end) end = strchr(line, '\n'); + if(end) { + slen = end - delim; + if(slen < length) { + memcpy(buf, delim, slen); + buf[slen] = 0; + return slen; + } else { + return CONF_ETOOBIG; + } + } + } + + // skip to line end + while(line < (__conf_txt_buffer+0x100) && *line++ != '\n'); + } + return CONF_ENOENT; +} + +u8 *__CONF_Find(const char *name) +{ + u16 count; + u16 *offset; + int nlen = strlen(name); + count = *((u16*)(&__conf_buffer[4])); + offset = (u16*)&__conf_buffer[6]; + + while(count--) { + if((nlen == ((__conf_buffer[*offset]&0x0F)+1)) && !memcmp(name, &__conf_buffer[*offset+1], nlen)) + return &__conf_buffer[*offset]; + offset++; + } + return NULL; +} + +s32 CONF_GetLength(const char *name) +{ + u8 *entry; + + if(!__conf_inited) return CONF_ENOTINIT; + + entry = __CONF_Find(name); + if(!entry) return CONF_ENOENT; + + switch(*entry>>5) { + case 1: + return *((u16*)&entry[strlen(name)+1]) + 1; + case 2: + return entry[strlen(name)+1] + 1; + case 3: + return 1; + case 4: + return 2; + case 5: + return 4; + case 7: + return 1; + default: + return CONF_ENOTIMPL; + } +} + +s32 CONF_GetType(const char *name) +{ + u8 *entry; + if(!__conf_inited) return CONF_ENOTINIT; + + entry = __CONF_Find(name); + if(!entry) return CONF_ENOENT; + + return *entry>>5; +} + +s32 CONF_Get(const char *name, void *buffer, u32 length) +{ + u8 *entry; + s32 len; + if(!__conf_inited) return CONF_ENOTINIT; + + entry = __CONF_Find(name); + if(!entry) return CONF_ENOENT; + + len = CONF_GetLength(name); + if(len<0) return len; + if(len>length) return CONF_ETOOBIG; + + switch(*entry>>5) { + case CONF_BIGARRAY: + memcpy(buffer, &entry[strlen(name)+3], len); + break; + case CONF_SMALLARRAY: + memcpy(buffer, &entry[strlen(name)+2], len); + break; + case CONF_BYTE: + case CONF_SHORT: + case CONF_LONG: + case CONF_BOOL: + memset(buffer, 0, length); + memcpy(buffer, &entry[strlen(name)+1], len); + break; + default: + return CONF_ENOTIMPL; + } + return len; +} + +s32 CONF_GetShutdownMode(void) +{ + u8 idleconf[2] = {0,0}; + int res; + + res = CONF_Get("IPL.IDL", idleconf, 2); + if(res<0) return res; + if(res!=2) return CONF_EBADVALUE; + return idleconf[0]; +} + +s32 CONF_GetIdleLedMode(void) +{ + int res; + u8 idleconf[2] = {0,0}; + res = CONF_Get("IPL.IDL", idleconf, 2); + if(res<0) return res; + if(res!=2) return CONF_EBADVALUE; + return idleconf[1]; +} + +s32 CONF_GetProgressiveScan(void) +{ + int res; + u8 val = 0; + res = CONF_Get("IPL.PGS", &val, 1); + if(res<0) return res; + if(res!=1) return CONF_EBADVALUE; + return val; +} + +s32 CONF_GetEuRGB60(void) +{ + int res; + u8 val = 0; + res = CONF_Get("IPL.E60", &val, 1); + if(res<0) return res; + if(res!=1) return CONF_EBADVALUE; + return val; +} + +s32 CONF_GetIRSensitivity(void) +{ + int res; + u32 val = 0; + res = CONF_Get("BT.SENS", &val, 4); + if(res<0) return res; + if(res!=4) return CONF_EBADVALUE; + return val; +} + +s32 CONF_GetSensorBarPosition(void) +{ + int res; + u8 val = 0; + res = CONF_Get("BT.BAR", &val, 1); + if(res<0) return res; + if(res!=1) return CONF_EBADVALUE; + return val; +} + +s32 CONF_GetPadSpeakerVolume(void) +{ + int res; + u8 val = 0; + res = CONF_Get("BT.SPKV", &val, 1); + if(res<0) return res; + if(res!=1) return CONF_EBADVALUE; + return val; +} + +s32 CONF_GetPadMotorMode(void) +{ + int res; + u8 val = 0; + res = CONF_Get("BT.MOT", &val, 1); + if(res<0) return res; + if(res!=1) return CONF_EBADVALUE; + return val; +} + +s32 CONF_GetSoundMode(void) +{ + int res; + u8 val = 0; + res = CONF_Get("IPL.SND", &val, 1); + if(res<0) return res; + if(res!=1) return CONF_EBADVALUE; + return val; +} + +s32 CONF_GetLanguage(void) +{ + int res; + u8 val = 0; + res = CONF_Get("IPL.LNG", &val, 1); + if(res<0) return res; + if(res!=1) return CONF_EBADVALUE; + return val; +} + +s32 CONF_GetCounterBias(u32 *bias) +{ + int res; + res = CONF_Get("IPL.CB", bias, 4); + if(res<0) return res; + if(res!=4) return CONF_EBADVALUE; + return CONF_ERR_OK; +} + +s32 CONF_GetScreenSaverMode(void) +{ + int res; + u8 val = 0; + res = CONF_Get("IPL.SSV", &val, 1); + if(res<0) return res; + if(res!=1) return CONF_EBADVALUE; + return val; +} + +s32 CONF_GetDisplayOffsetH(s8 *offset) +{ + int res; + res = CONF_Get("IPL.DH", offset, 1); + if(res<0) return res; + if(res!=1) return CONF_EBADVALUE; + return 0; +} + +s32 CONF_GetPadDevices(conf_pads *pads) +{ + int res; + + res = CONF_Get("BT.DINF", pads, sizeof(conf_pads)); + if(res < 0) return res; + if(res < sizeof(conf_pads)) return CONF_EBADVALUE; + return 0; +} + +s32 CONF_GetNickName(u8 *nickname) +{ + int i, res; + u16 buf[11]; + + res = CONF_Get("IPL.NIK", buf, 0x16); + if(res < 0) return res; + if((res != 0x16) || (!buf[0])) return CONF_EBADVALUE; + + for(i=0; i<10; i++) + nickname[i] = buf[i]; + nickname[10] = 0; + + return res; +} + +s32 CONF_GetAspectRatio(void) +{ + int res; + u8 val = 0; + + res = CONF_Get("IPL.AR", &val, 1); + if(res < 0) return res; + if(res!=1) return CONF_EBADVALUE; + return val; +} + +s32 CONF_GetEULA(void) +{ + int res; + u8 val = 0; + + res = CONF_Get("IPL.EULA", &val, 1); + if(res < 0) return res; + if(res!=1) return CONF_EBADVALUE; + return val; +} + +s32 CONF_GetParentalPassword(s8 *password) +{ + int res; + u8 buf[0x4A]; + + res = CONF_Get("IPL.PC", buf, 0x4A); + if(res < 0) return res; + if(res!=1) return CONF_EBADVALUE; + + memcpy(password, buf+3, 4); + password[4] = 0; + + return res; +} + +s32 CONF_GetParentalAnswer(s8 *answer) +{ + int res; + u8 buf[0x4A]; + + res = CONF_Get("IPL.PC", buf, 0x4A); + if(res < 0) return res; + if(res!=1) return CONF_EBADVALUE; + + memcpy(answer, buf+8, 32); + answer[32] = 0; + + return res; +} + +s32 CONF_GetWiiConnect24(void) +{ + int res; + u32 val = 0; + + res = CONF_Get("NET.WCFG", &val, 4); + if(res < 0) return res; + if(res!=4) return CONF_EBADVALUE; + return val; +} + +s32 CONF_GetRegion(void) +{ + int res; + char buf[3]; + + res = __CONF_GetTxt("GAME", buf, 3); + if(res < 0) return res; + if(!strcmp(buf, "JP")) return CONF_REGION_JP; + if(!strcmp(buf, "US")) return CONF_REGION_US; + if(!strcmp(buf, "EU")) return CONF_REGION_EU; + if(!strcmp(buf, "KR")) return CONF_REGION_KR; + if(!strcmp(buf, "CN")) return CONF_REGION_CN; + return CONF_EBADVALUE; +} + +s32 CONF_GetArea(void) +{ + int res; + char buf[4]; + + res = __CONF_GetTxt("AREA", buf, 4); + if(res < 0) return res; + if(!strcmp(buf, "JPN")) return CONF_AREA_JPN; + if(!strcmp(buf, "USA")) return CONF_AREA_USA; + if(!strcmp(buf, "EUR")) return CONF_AREA_EUR; + if(!strcmp(buf, "AUS")) return CONF_AREA_AUS; + if(!strcmp(buf, "BRA")) return CONF_AREA_BRA; + if(!strcmp(buf, "TWN")) return CONF_AREA_TWN; + if(!strcmp(buf, "ROC")) return CONF_AREA_ROC; + if(!strcmp(buf, "KOR")) return CONF_AREA_KOR; + if(!strcmp(buf, "HKG")) return CONF_AREA_HKG; + if(!strcmp(buf, "ASI")) return CONF_AREA_ASI; + if(!strcmp(buf, "LTN")) return CONF_AREA_LTN; + if(!strcmp(buf, "SAF")) return CONF_AREA_SAF; + if(!strcmp(buf, "CHN")) return CONF_AREA_CHN; + return CONF_EBADVALUE; +} + +s32 CONF_GetVideo(void) +{ + int res; + char buf[5]; + + res = __CONF_GetTxt("VIDEO", buf, 5); + if(res < 0) return res; + if(!strcmp(buf, "NTSC")) return CONF_VIDEO_NTSC; + if(!strcmp(buf, "PAL")) return CONF_VIDEO_PAL; + if(!strcmp(buf, "MPAL")) return CONF_VIDEO_MPAL; + return CONF_EBADVALUE; +} + +#endif diff --git a/wii/libogc/libogc/console.c b/wii/libogc/libogc/console.c new file mode 100644 index 0000000000..be43822a2e --- /dev/null +++ b/wii/libogc/libogc/console.c @@ -0,0 +1,644 @@ +#include +#include +#include +#include + +#include "asm.h" +#include "processor.h" +#include "color.h" +#include "cache.h" +#include "video.h" +#include "system.h" + +#include "console.h" +#include "consol.h" +#include "usbgecko.h" + +#include +#include + +//--------------------------------------------------------------------------------- +const devoptab_t dotab_stdout = { +//--------------------------------------------------------------------------------- + "stdout", // device name + 0, // size of file structure + NULL, // device open + NULL, // device close + __console_write, // device write + NULL, // device read + NULL, // device seek + NULL, // device fstat + NULL, // device stat + NULL, // device link + NULL, // device unlink + NULL, // device chdir + NULL, // device rename + NULL, // device mkdir + 0, // dirStateSize + NULL, // device diropen_r + NULL, // device dirreset_r + NULL, // device dirnext_r + NULL, // device dirclose_r + NULL, // device statvfs_r + NULL, // device ftrunctate_r + NULL, // device fsync_r + NULL, // deviceData; +}; + +//color table +static const unsigned int color_table[] = +{ + 0x00800080, // 30 normal black + 0x246A24BE, // 31 normal red + 0x4856484B, // 32 normal green + 0x6D416D8A, // 33 normal yellow + 0x0DBE0D75, // 34 normal blue + 0x32A932B4, // 35 normal magenta + 0x56955641, // 36 normal cyan + 0xC580C580, // 37 normal white + 0x7B807B80, // 30 bright black + 0x4C544CFF, // 31 bright red + 0x95299512, // 32 bright green + 0xE200E294, // 33 bright yellow + 0x1CFF1C6B, // 34 bright blue + 0x69D669ED, // 35 bright magenta + 0xB2ABB200, // 36 bright cyan + 0xFF80FF80, // 37 bright white +}; + +static u32 do_xfb_copy = FALSE; +static struct _console_data_s stdcon; +static struct _console_data_s *curr_con = NULL; +static void *_console_buffer = NULL; + +static s32 __gecko_status = -1; +static u32 __gecko_safe = 0; + +extern u8 console_font_8x16[]; + +void __console_vipostcb(u32 retraceCnt) +{ + u32 ycnt,xcnt, fb_stride; + u32 *fb,*ptr; + + do_xfb_copy = TRUE; + + ptr = curr_con->destbuffer; + fb = VIDEO_GetCurrentFramebuffer()+(curr_con->target_y*curr_con->tgt_stride) + curr_con->target_x*VI_DISPLAY_PIX_SZ; + fb_stride = curr_con->tgt_stride/4 - (curr_con->con_xres/VI_DISPLAY_PIX_SZ); + + for(ycnt=curr_con->con_yres;ycnt>0;ycnt--) + { + for(xcnt=curr_con->con_xres;xcnt>0;xcnt-=VI_DISPLAY_PIX_SZ) + { + *fb++ = *ptr++; + } + fb += fb_stride; + } + + do_xfb_copy = FALSE; +} + + +static void __console_drawc(int c) +{ + console_data_s *con; + int ay; + unsigned int *ptr; + unsigned char *pbits; + unsigned char bits; + unsigned int color; + unsigned int fgcolor, bgcolor; + unsigned int nextline; + + if(do_xfb_copy==TRUE) return; + if(!curr_con) return; + con = curr_con; + + ptr = (unsigned int*)(con->destbuffer + ( con->con_stride * con->cursor_row * FONT_YSIZE ) + ((con->cursor_col * FONT_XSIZE / 2) * 4)); + pbits = &con->font[c * FONT_YSIZE]; + nextline = con->con_stride/4 - 4; + fgcolor = con->foreground; + bgcolor = con->background; + + for (ay = 0; ay < FONT_YSIZE; ay++) + { + /* hard coded loop unrolling ! */ + /* this depends on FONT_XSIZE = 8*/ +#if FONT_XSIZE == 8 + bits = *pbits++; + + /* bits 1 & 2 */ + if ( bits & 0x80) + color = fgcolor & 0xFFFF00FF; + else + color = bgcolor & 0xFFFF00FF; + if (bits & 0x40) + color |= fgcolor & 0x0000FF00; + else + color |= bgcolor & 0x0000FF00; + *ptr++ = color; + + /* bits 3 & 4 */ + if ( bits & 0x20) + color = fgcolor & 0xFFFF00FF; + else + color = bgcolor & 0xFFFF00FF; + if (bits & 0x10) + color |= fgcolor & 0x0000FF00; + else + color |= bgcolor & 0x0000FF00; + *ptr++ = color; + + /* bits 5 & 6 */ + if ( bits & 0x08) + color = fgcolor & 0xFFFF00FF; + else + color = bgcolor & 0xFFFF00FF; + if (bits & 0x04) + color |= fgcolor & 0x0000FF00; + else + color |= bgcolor & 0x0000FF00; + *ptr++ = color; + + /* bits 7 & 8 */ + if ( bits & 0x02) + color = fgcolor & 0xFFFF00FF; + else + color = bgcolor & 0xFFFF00FF; + if (bits & 0x01) + color |= fgcolor & 0x0000FF00; + else + color |= bgcolor & 0x0000FF00; + *ptr++ = color; + + /* next line */ + ptr += nextline; +#endif + } +} +static void __console_clear_line( int line, int from, int to ) { + console_data_s *con; + unsigned int c; + unsigned int *p; + unsigned int x_pixels; + unsigned int px_per_col = FONT_XSIZE/2; + unsigned int line_height = FONT_YSIZE; + unsigned int line_width; + + if( !(con = curr_con) ) return; + // For some reason there are xres/2 pixels per screen width + x_pixels = con->con_xres / 2; + + line_width = (to - from)*px_per_col; + p = (unsigned int*)con->destbuffer; + + // Move pointer to the current line and column offset + p += line*(FONT_YSIZE*x_pixels) + from*px_per_col; + + // Clears 1 line of pixels at a time, line_height times + while( line_height-- ) { + c = line_width; + while( c-- ) + *p++ = con->background; + p -= line_width; + p += x_pixels; + } +} +static void __console_clear(void) +{ + console_data_s *con; + unsigned int c; + unsigned int *p; + + if( !(con = curr_con) ) return; + + c = (con->con_xres*con->con_yres)/2; + p = (unsigned int*)con->destbuffer; + + while(c--) + *p++ = con->background; + + con->cursor_row = 0; + con->cursor_col = 0; + con->saved_row = 0; + con->saved_col = 0; +} +static void __console_clear_from_cursor() { + console_data_s *con; + int cur_row; + + if( !(con = curr_con) ) return; + cur_row = con->cursor_row; + + __console_clear_line( cur_row, con->cursor_col, con->con_cols ); + + while( cur_row++ < con->con_rows ) + __console_clear_line( cur_row, 0, con->con_cols ); + +} +static void __console_clear_to_cursor() { + console_data_s *con; + int cur_row; + + if( !(con = curr_con) ) return; + cur_row = con->cursor_row; + + __console_clear_line( cur_row, 0, con->cursor_col ); + + while( cur_row-- ) + __console_clear_line( cur_row, 0, con->con_cols ); +} + +void __console_init(void *framebuffer,int xstart,int ystart,int xres,int yres,int stride) +{ + unsigned int level; + console_data_s *con = &stdcon; + + _CPU_ISR_Disable(level); + + con->destbuffer = framebuffer; + con->con_xres = xres; + con->con_yres = yres; + con->con_cols = xres / FONT_XSIZE; + con->con_rows = yres / FONT_YSIZE; + con->con_stride = con->tgt_stride = stride; + con->target_x = xstart; + con->target_y = ystart; + + con->font = console_font_8x16; + + con->foreground = COLOR_WHITE; + con->background = COLOR_BLACK; + + curr_con = con; + + __console_clear(); + + devoptab_list[STD_OUT] = &dotab_stdout; + devoptab_list[STD_ERR] = &dotab_stdout; + _CPU_ISR_Restore(level); + + setvbuf(stdout, NULL , _IONBF, 0); + setvbuf(stderr, NULL , _IONBF, 0); +} + +void __console_init_ex(void *conbuffer,int tgt_xstart,int tgt_ystart,int tgt_stride,int con_xres,int con_yres,int con_stride) +{ + unsigned int level; + console_data_s *con = &stdcon; + + _CPU_ISR_Disable(level); + + con->destbuffer = conbuffer; + con->target_x = tgt_xstart; + con->target_y = tgt_ystart; + con->con_xres = con_xres; + con->con_yres = con_yres; + con->tgt_stride = tgt_stride; + con->con_stride = con_stride; + con->con_cols = con_xres / FONT_XSIZE; + con->con_rows = con_yres / FONT_YSIZE; + con->cursor_row = 0; + con->cursor_col = 0; + con->saved_row = 0; + con->saved_col = 0; + + con->font = console_font_8x16; + + con->foreground = COLOR_WHITE; + con->background = COLOR_BLACK; + + curr_con = con; + + __console_clear(); + + devoptab_list[STD_OUT] = &dotab_stdout; + devoptab_list[STD_ERR] = &dotab_stdout; + + VIDEO_SetPostRetraceCallback(__console_vipostcb); + + _CPU_ISR_Restore(level); + + setvbuf(stdout, NULL , _IONBF, 0); + setvbuf(stderr, NULL , _IONBF, 0); +} + +static int __console_parse_escsequence(char *pchr) +{ + char chr; + console_data_s *con; + int i; + int parameters[3]; + int para; + + if(!curr_con) return -1; + con = curr_con; + + /* set default value */ + para = 0; + parameters[0] = 0; + parameters[1] = 0; + parameters[2] = 0; + + /* scan parameters */ + i = 0; + chr = *pchr; + while( (para < 3) && (chr >= '0') && (chr <= '9') ) + { + while( (chr >= '0') && (chr <= '9') ) + { + /* parse parameter */ + parameters[para] *= 10; + parameters[para] += chr - '0'; + pchr++; + i++; + chr = *pchr; + } + para++; + + if( *pchr == ';' ) + { + /* skip parameter delimiter */ + pchr++; + i++; + } + chr = *pchr; + } + + /* get final character */ + chr = *pchr++; + i++; + switch(chr) + { + ///////////////////////////////////////// + // Cursor directional movement + ///////////////////////////////////////// + case 'A': + { + curr_con->cursor_row -= parameters[0]; + if(curr_con->cursor_row < 0) curr_con->cursor_row = 0; + break; + } + case 'B': + { + curr_con->cursor_row += parameters[0]; + if(curr_con->cursor_row >= curr_con->con_rows) curr_con->cursor_row = curr_con->con_rows - 1; + break; + } + case 'C': + { + curr_con->cursor_col += parameters[0]; + if(curr_con->cursor_col >= curr_con->con_cols) curr_con->cursor_col = curr_con->con_cols - 1; + break; + } + case 'D': + { + curr_con->cursor_col -= parameters[0]; + if(curr_con->cursor_col < 0) curr_con->cursor_col = 0; + break; + } + ///////////////////////////////////////// + // Cursor position movement + ///////////////////////////////////////// + case 'H': + case 'f': + { + curr_con->cursor_col = parameters[1]; + curr_con->cursor_row = parameters[0]; + if(curr_con->cursor_row >= curr_con->con_rows) curr_con->cursor_row = curr_con->con_rows - 1; + if(curr_con->cursor_col >= curr_con->con_cols) curr_con->cursor_col = curr_con->con_cols - 1; + break; + } + ///////////////////////////////////////// + // Screen clear + ///////////////////////////////////////// + case 'J': + { + if( parameters[0] == 0 ) + __console_clear_from_cursor(); + if( parameters[0] == 1 ) + __console_clear_to_cursor(); + if( parameters[0] == 2 ) + __console_clear(); + + break; + } + ///////////////////////////////////////// + // Line clear + ///////////////////////////////////////// + case 'K': + { + if( parameters[0] == 0 ) + __console_clear_line( curr_con->cursor_row, curr_con->cursor_col, curr_con->con_cols ); + if( parameters[0] == 1 ) + __console_clear_line( curr_con->cursor_row, 0, curr_con->cursor_col ); + if( parameters[0] == 2 ) + __console_clear_line( curr_con->cursor_row, 0, curr_con->con_cols); + + break; + } + ///////////////////////////////////////// + // Save cursor position + ///////////////////////////////////////// + case 's': + { + con->saved_col = con->cursor_col; + con->saved_row = con->cursor_row; + break; + } + ///////////////////////////////////////// + // Load cursor position + ///////////////////////////////////////// + case 'u': + con->cursor_col = con->saved_col; + con->cursor_row = con->saved_row; + break; + ///////////////////////////////////////// + // SGR Select Graphic Rendition + ///////////////////////////////////////// + case 'm': + { + // handle 30-37,39 for foreground color changes + if( (parameters[0] >= 30) && (parameters[0] <= 39) ) + { + parameters[0] -= 30; + + //39 is the reset code + if(parameters[0] == 9){ + parameters[0] = 15; + } + else if(parameters[0] > 7){ + parameters[0] = 7; + } + + if(parameters[1] == 1) + { + // Intensity: Bold makes color bright + parameters[0] += 8; + } + con->foreground = color_table[parameters[0]]; + } + // handle 40-47 for background color changes + else if( (parameters[0] >= 40) && (parameters[0] <= 47) ) + { + parameters[0] -= 40; + + if(parameters[1] == 1) + { + // Intensity: Bold makes color bright + parameters[0] += 8; + } + con->background = color_table[parameters[0]]; + } + break; + } + } + + return(i); +} + +int __console_write(struct _reent *r,void *fd,const char *ptr,size_t len) +{ + size_t i = 0; + char *tmp = (char*)ptr; + console_data_s *con; + char chr; + + if(__gecko_status>=0) { + if(__gecko_safe) + usb_sendbuffer_safe(__gecko_status,ptr,len); + else + usb_sendbuffer(__gecko_status,ptr,len); + } + + if(!curr_con) return -1; + con = curr_con; + if(!tmp || len<=0) return -1; + + i = 0; + while(*tmp!='\0' && icursor_row++; + con->cursor_col = 0; + break; + case '\r': + con->cursor_col = 0; + break; + case '\b': + con->cursor_col--; + if(con->cursor_col < 0) + { + con->cursor_col = 0; + } + break; + case '\f': + con->cursor_row++; + break; + case '\t': + if(con->cursor_col%TAB_SIZE) con->cursor_col += (con->cursor_col%TAB_SIZE); + else con->cursor_col += TAB_SIZE; + break; + default: + __console_drawc(chr); + con->cursor_col++; + + if( con->cursor_col >= con->con_cols) + { + /* if right border reached wrap around */ + con->cursor_row++; + con->cursor_col = 0; + } + } + } + + if( con->cursor_row >= con->con_rows) + { + /* if bottom border reached scroll */ + memcpy(con->destbuffer, + con->destbuffer+con->con_stride*(FONT_YSIZE*FONT_YFACTOR+FONT_YGAP), + con->con_stride*con->con_yres-FONT_YSIZE); + + unsigned int cnt = (con->con_stride * (FONT_YSIZE * FONT_YFACTOR + FONT_YGAP))/4; + unsigned int *ptr = (unsigned int*)(con->destbuffer + con->con_stride * (con->con_yres - FONT_YSIZE)); + while(cnt--) + *ptr++ = con->background; + con->cursor_row--; + } + } + + return i; +} + +void CON_Init(void *framebuffer,int xstart,int ystart,int xres,int yres,int stride) +{ + __console_init(framebuffer,xstart,ystart,xres,yres,stride); +} + +s32 CON_InitEx(GXRModeObj *rmode, s32 conXOrigin,s32 conYOrigin,s32 conWidth,s32 conHeight) +{ + VIDEO_SetPostRetraceCallback(NULL); + if(_console_buffer) + free(_console_buffer); + + _console_buffer = malloc(conWidth*conHeight*VI_DISPLAY_PIX_SZ); + if(!_console_buffer) return -1; + + __console_init_ex(_console_buffer,conXOrigin,conYOrigin,rmode->fbWidth*VI_DISPLAY_PIX_SZ,conWidth,conHeight,conWidth*VI_DISPLAY_PIX_SZ); + + return 0; +} + +void CON_GetMetrics(int *cols, int *rows) +{ + if(curr_con) { + *cols = curr_con->con_cols; + *rows = curr_con->con_rows; + } +} + +void CON_GetPosition(int *col, int *row) +{ + if(curr_con) { + *col = curr_con->cursor_col; + *row = curr_con->cursor_row; + } +} + +void CON_EnableGecko(int channel,int safe) +{ + if(channel && (channel>1 || !usb_isgeckoalive(channel))) channel = -1; + + __gecko_status = channel; + __gecko_safe = safe; + + if(__gecko_status!=-1) { + devoptab_list[STD_OUT] = &dotab_stdout; + devoptab_list[STD_ERR] = &dotab_stdout; + + // line buffered output for threaded apps when only using the usbgecko + if(!curr_con) { + setvbuf(stdout, NULL, _IOLBF, 0); + setvbuf(stderr, NULL, _IOLBF, 0); + } + } +} + diff --git a/wii/libogc/libogc/console.h b/wii/libogc/libogc/console.h new file mode 100644 index 0000000000..df05b1e798 --- /dev/null +++ b/wii/libogc/libogc/console.h @@ -0,0 +1,30 @@ +#ifndef __CONSOLE_H__ +#define __CONSOLE_H__ + + +#define FONT_XSIZE 8 +#define FONT_YSIZE 16 +#define FONT_XFACTOR 1 +#define FONT_YFACTOR 1 +#define FONT_XGAP 0 +#define FONT_YGAP 0 +#define TAB_SIZE 4 + +typedef struct _console_data_s { + void *destbuffer; + unsigned char *font; + int con_xres,con_yres,con_stride; + int target_x,target_y, tgt_stride; + int cursor_row,cursor_col; + int saved_row,saved_col; + int con_rows, con_cols; + + unsigned int foreground,background; +} console_data_s; + +extern int __console_write(struct _reent *r,void *fd,const char *ptr,size_t len); +extern void __console_init(void *framebuffer,int xstart,int ystart,int xres,int yres,int stride); + +//extern const devoptab_t dotab_stdout; + +#endif diff --git a/wii/libogc/libogc/console_font_8x16.c b/wii/libogc/libogc/console_font_8x16.c new file mode 100644 index 0000000000..21ef8da057 --- /dev/null +++ b/wii/libogc/libogc/console_font_8x16.c @@ -0,0 +1,4613 @@ +unsigned char console_font_8x16[] = { + + /* 0 0x00 '^@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 1 0x01 '^A' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x81, /* 10000001 */ + 0xa5, /* 10100101 */ + 0x81, /* 10000001 */ + 0x81, /* 10000001 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0x81, /* 10000001 */ + 0x81, /* 10000001 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 2 0x02 '^B' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xdb, /* 11011011 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 3 0x03 '^C' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 4 0x04 '^D' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 5 0x05 '^E' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0xe7, /* 11100111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 6 0x06 '^F' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 7 0x07 '^G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 8 0x08 '^H' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xe7, /* 11100111 */ + 0xc3, /* 11000011 */ + 0xc3, /* 11000011 */ + 0xe7, /* 11100111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 9 0x09 '^I' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x42, /* 01000010 */ + 0x42, /* 01000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 10 0x0a '^J' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xc3, /* 11000011 */ + 0x99, /* 10011001 */ + 0xbd, /* 10111101 */ + 0xbd, /* 10111101 */ + 0x99, /* 10011001 */ + 0xc3, /* 11000011 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 11 0x0b '^K' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1e, /* 00011110 */ + 0x0e, /* 00001110 */ + 0x1a, /* 00011010 */ + 0x32, /* 00110010 */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 12 0x0c '^L' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 13 0x0d '^M' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x33, /* 00110011 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x70, /* 01110000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 14 0x0e '^N' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x7f, /* 01111111 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x63, /* 01100011 */ + 0x67, /* 01100111 */ + 0xe7, /* 11100111 */ + 0xe6, /* 11100110 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 15 0x0f '^O' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xdb, /* 11011011 */ + 0x3c, /* 00111100 */ + 0xe7, /* 11100111 */ + 0x3c, /* 00111100 */ + 0xdb, /* 11011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 16 0x10 '^P' */ + 0x00, /* 00000000 */ + 0x80, /* 10000000 */ + 0xc0, /* 11000000 */ + 0xe0, /* 11100000 */ + 0xf0, /* 11110000 */ + 0xf8, /* 11111000 */ + 0xfe, /* 11111110 */ + 0xf8, /* 11111000 */ + 0xf0, /* 11110000 */ + 0xe0, /* 11100000 */ + 0xc0, /* 11000000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 17 0x11 '^Q' */ + 0x00, /* 00000000 */ + 0x02, /* 00000010 */ + 0x06, /* 00000110 */ + 0x0e, /* 00001110 */ + 0x1e, /* 00011110 */ + 0x3e, /* 00111110 */ + 0xfe, /* 11111110 */ + 0x3e, /* 00111110 */ + 0x1e, /* 00011110 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 18 0x12 '^R' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 19 0x13 '^S' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 20 0x14 '^T' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7f, /* 01111111 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7b, /* 01111011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 21 0x15 '^U' */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 22 0x16 '^V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 23 0x17 '^W' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 24 0x18 '^X' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 25 0x19 '^Y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 26 0x1a '^Z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 27 0x1b '^[' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xfe, /* 11111110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 28 0x1c '^\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 29 0x1d '^]' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x28, /* 00101000 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x28, /* 00101000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 30 0x1e '^^' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x7c, /* 01111100 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 31 0x1f '^_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0x7c, /* 01111100 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 32 0x20 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 33 0x21 '!' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 34 0x22 '"' */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x24, /* 00100100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 35 0x23 '#' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 36 0x24 '$' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x86, /* 10000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 37 0x25 '%' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc2, /* 11000010 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0x86, /* 10000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 38 0x26 '&' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 39 0x27 ''' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 40 0x28 '(' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 41 0x29 ')' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 42 0x2a '*' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0xff, /* 11111111 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 43 0x2b '+' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 44 0x2c ',' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 45 0x2d '-' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 46 0x2e '.' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 47 0x2f '/' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x02, /* 00000010 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x80, /* 10000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 48 0x30 '0' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 49 0x31 '1' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x38, /* 00111000 */ + 0x78, /* 01111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 50 0x32 '2' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 51 0x33 '3' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x3c, /* 00111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 52 0x34 '4' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x1c, /* 00011100 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 53 0x35 '5' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 54 0x36 '6' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xfc, /* 11111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 55 0x37 '7' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 56 0x38 '8' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 57 0x39 '9' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 58 0x3a ':' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 59 0x3b ';' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 60 0x3c '<' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 61 0x3d '=' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 62 0x3e '>' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 63 0x3f '?' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 64 0x40 '@' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xde, /* 11011110 */ + 0xdc, /* 11011100 */ + 0xc0, /* 11000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 65 0x41 'A' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 66 0x42 'B' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 67 0x43 'C' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc2, /* 11000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 68 0x44 'D' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 69 0x45 'E' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x66, /* 01100110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x60, /* 01100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 70 0x46 'F' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x66, /* 01100110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 71 0x47 'G' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xde, /* 11011110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x66, /* 01100110 */ + 0x3a, /* 00111010 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 72 0x48 'H' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 73 0x49 'I' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 74 0x4a 'J' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1e, /* 00011110 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 75 0x4b 'K' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe6, /* 11100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 76 0x4c 'L' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 77 0x4d 'M' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xee, /* 11101110 */ + 0xfe, /* 11111110 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 78 0x4e 'N' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xfe, /* 11111110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 79 0x4f 'O' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 80 0x50 'P' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 81 0x51 'Q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xde, /* 11011110 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 82 0x52 'R' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfc, /* 11111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 83 0x53 'S' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 84 0x54 'T' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x5a, /* 01011010 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 85 0x55 'U' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 86 0x56 'V' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 87 0x57 'W' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0xee, /* 11101110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 88 0x58 'X' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x7c, /* 01111100 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x7c, /* 01111100 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 89 0x59 'Y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 90 0x5a 'Z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x86, /* 10000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc2, /* 11000010 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 91 0x5b '[' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 92 0x5c '\' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x80, /* 10000000 */ + 0xc0, /* 11000000 */ + 0xe0, /* 11100000 */ + 0x70, /* 01110000 */ + 0x38, /* 00111000 */ + 0x1c, /* 00011100 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x02, /* 00000010 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 93 0x5d ']' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 94 0x5e '^' */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 95 0x5f '_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 96 0x60 '`' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 97 0x61 'a' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 98 0x62 'b' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 99 0x63 'c' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 100 0x64 'd' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 00011100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 101 0x65 'e' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 102 0x66 'f' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 00011100 */ + 0x36, /* 00110110 */ + 0x32, /* 00110010 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 103 0x67 'g' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0xcc, /* 11001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 104 0x68 'h' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x6c, /* 01101100 */ + 0x76, /* 01110110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 105 0x69 'i' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 106 0x6a 'j' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + + /* 107 0x6b 'k' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xe0, /* 11100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x78, /* 01111000 */ + 0x78, /* 01111000 */ + 0x6c, /* 01101100 */ + 0x66, /* 01100110 */ + 0xe6, /* 11100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 108 0x6c 'l' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 109 0x6d 'm' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0xfe, /* 11111110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 110 0x6e 'n' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 111 0x6f 'o' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 112 0x70 'p' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + + /* 113 0x71 'q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x7c, /* 01111100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x1e, /* 00011110 */ + 0x00, /* 00000000 */ + + /* 114 0x72 'r' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x66, /* 01100110 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 115 0x73 's' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x38, /* 00111000 */ + 0x0c, /* 00001100 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 116 0x74 't' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0xfc, /* 11111100 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x36, /* 00110110 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 117 0x75 'u' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 118 0x76 'v' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 119 0x77 'w' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xd6, /* 11010110 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 120 0x78 'x' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 121 0x79 'y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + + /* 122 0x7a 'z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xcc, /* 11001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 123 0x7b '{' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 124 0x7c '|' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 125 0x7d '}' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x70, /* 01110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x0e, /* 00001110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 126 0x7e '~' */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 127 0x7f '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 128 0x80 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0xc2, /* 11000010 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc2, /* 11000010 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 129 0x81 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 130 0x82 '' */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 131 0x83 '' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 132 0x84 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 133 0x85 '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 134 0x86 '' */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 135 0x87 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 136 0x88 '' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 137 0x89 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 138 0x8a '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 139 0x8b '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 140 0x8c '' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 141 0x8d '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 142 0x8e '' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 143 0x8f '' */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 144 0x90 '' */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x66, /* 01100110 */ + 0x62, /* 01100010 */ + 0x68, /* 01101000 */ + 0x78, /* 01111000 */ + 0x68, /* 01101000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 145 0x91 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xec, /* 11101100 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x6e, /* 01101110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 146 0x92 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3e, /* 00111110 */ + 0x6c, /* 01101100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xfe, /* 11111110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xce, /* 11001110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 147 0x93 '' */ + 0x00, /* 00000000 */ + 0x10, /* 00010000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 148 0x94 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 149 0x95 '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 150 0x96 '' */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 151 0x97 '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 152 0x98 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7e, /* 01111110 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x78, /* 01111000 */ + 0x00, /* 00000000 */ + + /* 153 0x99 '' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 154 0x9a '' */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 155 0x9b '' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 156 0x9c '' */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x64, /* 01100100 */ + 0x60, /* 01100000 */ + 0xf0, /* 11110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xe6, /* 11100110 */ + 0xfc, /* 11111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 157 0x9d '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 158 0x9e '' */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xf8, /* 11111000 */ + 0xc4, /* 11000100 */ + 0xcc, /* 11001100 */ + 0xde, /* 11011110 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 159 0x9f '' */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 160 0xa0 '' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0x0c, /* 00001100 */ + 0x7c, /* 01111100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 161 0xa1 '' */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 162 0xa2 '' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 163 0xa3 '' */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x00, /* 00000000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 164 0xa4 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xdc, /* 11011100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 165 0xa5 '' */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0xc6, /* 11000110 */ + 0xe6, /* 11100110 */ + 0xf6, /* 11110110 */ + 0xfe, /* 11111110 */ + 0xde, /* 11011110 */ + 0xce, /* 11001110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 166 0xa6 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 167 0xa7 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 168 0xa8 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x7c, /* 01111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 169 0xa9 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 170 0xaa '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 171 0xab '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0xe0, /* 11100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xdc, /* 11011100 */ + 0x86, /* 10000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x3e, /* 00111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 172 0xac '' */ + 0x00, /* 00000000 */ + 0x60, /* 01100000 */ + 0xe0, /* 11100000 */ + 0x62, /* 01100010 */ + 0x66, /* 01100110 */ + 0x6c, /* 01101100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x66, /* 01100110 */ + 0xce, /* 11001110 */ + 0x9a, /* 10011010 */ + 0x3f, /* 00111111 */ + 0x06, /* 00000110 */ + 0x06, /* 00000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 173 0xad '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 174 0xae '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x36, /* 00110110 */ + 0x6c, /* 01101100 */ + 0xd8, /* 11011000 */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 175 0xaf '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xd8, /* 11011000 */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x6c, /* 01101100 */ + 0xd8, /* 11011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 176 0xb0 '' */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + 0x11, /* 00010001 */ + 0x44, /* 01000100 */ + + /* 177 0xb1 '' */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + 0x55, /* 01010101 */ + 0xaa, /* 10101010 */ + + /* 178 0xb2 '' */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + 0xdd, /* 11011101 */ + 0x77, /* 01110111 */ + + /* 179 0xb3 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 180 0xb4 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 181 0xb5 '' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 182 0xb6 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 183 0xb7 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 184 0xb8 '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 185 0xb9 '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 186 0xba '' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 187 0xbb '' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x06, /* 00000110 */ + 0xf6, /* 11110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 188 0xbc ' ' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf6, /* 11110110 */ + 0x06, /* 00000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 189 0xbd ' ' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 190 0xbe ' ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 191 0xbf ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xf8, /* 11111000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 192 0xc0 ' ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 193 0xc1 ' ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 194 0xc2 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 195 0xc3 ' ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 196 0xc4 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 197 0xc5 ' ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 198 0xc6 ' ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 199 0xc7 ' ' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 200 0xc8 ' ' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 201 0xc9 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 202 0xca ' ' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 203 0xcb ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 204 0xcc ' ' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x37, /* 00110111 */ + 0x30, /* 00110000 */ + 0x37, /* 00110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 205 0xcd ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 206 0xce ' ' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xf7, /* 11110111 */ + 0x00, /* 00000000 */ + 0xf7, /* 11110111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 207 0xcf ' ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 208 0xd0 ' ' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 209 0xd1 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 210 0xd2 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 211 0xd3 ' ' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x3f, /* 00111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 212 0xd4 ' ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 213 0xd5 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 214 0xd6 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3f, /* 00111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 215 0xd7 ' ' */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0xff, /* 11111111 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + + /* 216 0xd8 ' ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0xff, /* 11111111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 217 0xd9 ' ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xf8, /* 11111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 218 0xda ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1f, /* 00011111 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 219 0xdb ' ' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 220 0xdc ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + + /* 221 0xdd ' ' */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + 0xf0, /* 11110000 */ + + /* 222 0xde ' ' */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + 0x0f, /* 00001111 */ + + /* 223 0xdf ' ' */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0xff, /* 11111111 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 224 0xe0 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xdc, /* 11011100 */ + 0x76, /* 01110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 225 0xe1 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 01111000 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xcc, /* 11001100 */ + 0xd8, /* 11011000 */ + 0xcc, /* 11001100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xcc, /* 11001100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 226 0xe2 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 227 0xe3 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 228 0xe4 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 229 0xe5 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 230 0xe6 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + + /* 231 0xe7 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 232 0xe8 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 233 0xe9 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xfe, /* 11111110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 234 0xea ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0xee, /* 11101110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 235 0xeb ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1e, /* 00011110 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x3e, /* 00111110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x66, /* 01100110 */ + 0x3c, /* 00111100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 236 0xec ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 237 0xed ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x03, /* 00000011 */ + 0x06, /* 00000110 */ + 0x7e, /* 01111110 */ + 0xdb, /* 11011011 */ + 0xdb, /* 11011011 */ + 0xf3, /* 11110011 */ + 0x7e, /* 01111110 */ + 0x60, /* 01100000 */ + 0xc0, /* 11000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 238 0xee ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x1c, /* 00011100 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x7c, /* 01111100 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 239 0xef ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 01111100 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0xc6, /* 11000110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 240 0xf0 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0xfe, /* 11111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 241 0xf1 'ñ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x7e, /* 01111110 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 242 0xf2 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x06, /* 00000110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 243 0xf3 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x30, /* 00110000 */ + 0x60, /* 01100000 */ + 0x30, /* 00110000 */ + 0x18, /* 00011000 */ + 0x0c, /* 00001100 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 244 0xf4 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0e, /* 00001110 */ + 0x1b, /* 00011011 */ + 0x1b, /* 00011011 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + + /* 245 0xf5 ' ' */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0xd8, /* 11011000 */ + 0x70, /* 01110000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 246 0xf6 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 247 0xf7 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x76, /* 01110110 */ + 0xdc, /* 11011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 248 0xf8 ' ' */ + 0x00, /* 00000000 */ + 0x38, /* 00111000 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x38, /* 00111000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 249 0xf9 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 250 0xfa ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 00011000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 251 0xfb ' ' */ + 0x00, /* 00000000 */ + 0x0f, /* 00001111 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0x0c, /* 00001100 */ + 0xec, /* 11101100 */ + 0x6c, /* 01101100 */ + 0x6c, /* 01101100 */ + 0x3c, /* 00111100 */ + 0x1c, /* 00011100 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 252 0xfc '' */ + 0x00, /* 00000000 */ + 0x6c, /* 01101100 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x36, /* 00110110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 253 0xfd '¸' */ + 0x00, /* 00000000 */ + 0x3c, /* 00111100 */ + 0x66, /* 01100110 */ + 0x0c, /* 00001100 */ + 0x18, /* 00011000 */ + 0x32, /* 00110010 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 254 0xfe ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x7e, /* 01111110 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 255 0xff 'ÿ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + +}; + + diff --git a/wii/libogc/libogc/decrementer.c b/wii/libogc/libogc/decrementer.c new file mode 100644 index 0000000000..eb1c962c33 --- /dev/null +++ b/wii/libogc/libogc/decrementer.c @@ -0,0 +1,47 @@ +/*------------------------------------------------------------- + +decrementer.c -- PPC decrementer exception support + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ + + +#include +#include +#include "asm.h" +#include "processor.h" +#include "lwp_threads.h" +#include "lwp_watchdog.h" +#include "context.h" + +void __decrementer_init() +{ +} + +void c_decrementer_handler(frame_context *ctx) +{ + __lwp_wd_tickle_ticks(); +} diff --git a/wii/libogc/libogc/decrementer_handler.S b/wii/libogc/libogc/decrementer_handler.S new file mode 100644 index 0000000000..6e824d94be --- /dev/null +++ b/wii/libogc/libogc/decrementer_handler.S @@ -0,0 +1,167 @@ +/*------------------------------------------------------------- + +decrementer_handler.S -- PPC decrementer exception support + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ + + +#include + +#define EXCEPTION_PROLOG \ + mfspr r0,912; \ + stw r0,GQR0_OFFSET(sp); \ + mfspr r0,913; \ + stw r0,GQR1_OFFSET(sp); \ + mfspr r0,914; \ + stw r0,GQR2_OFFSET(sp); \ + mfspr r0,915; \ + stw r0,GQR3_OFFSET(sp); \ + mfspr r0,916; \ + stw r0,GQR4_OFFSET(sp); \ + mfspr r0,917; \ + stw r0,GQR5_OFFSET(sp); \ + mfspr r0,918; \ + stw r0,GQR6_OFFSET(sp); \ + mfspr r0,919; \ + stw r0,GQR7_OFFSET(sp); \ + stw r6,GPR6_OFFSET(sp); \ + stw r7,GPR7_OFFSET(sp); \ + stw r8,GPR8_OFFSET(sp); \ + stw r9,GPR9_OFFSET(sp); \ + stw r10,GPR10_OFFSET(sp); \ + stw r11,GPR11_OFFSET(sp); \ + stw r12,GPR12_OFFSET(sp); \ + stw r13,GPR13_OFFSET(sp); \ + stw r14,GPR14_OFFSET(sp); \ + stw r15,GPR15_OFFSET(sp) + +#define EXCEPTION_EPILOG \ + lwz r4,GQR0_OFFSET(sp); \ + mtspr 912,r4; \ + lwz r4,GQR1_OFFSET(sp); \ + mtspr 913,r4; \ + lwz r4,GQR2_OFFSET(sp); \ + mtspr 914,r4; \ + lwz r4,GQR3_OFFSET(sp); \ + mtspr 915,r4; \ + lwz r4,GQR4_OFFSET(sp); \ + mtspr 916,r4; \ + lwz r4,GQR5_OFFSET(sp); \ + mtspr 917,r4; \ + lwz r4,GQR6_OFFSET(sp); \ + mtspr 918,r4; \ + lwz r4,GQR7_OFFSET(sp); \ + mtspr 919,r4; \ + lwz r15,GPR15_OFFSET(sp); \ + lwz r14,GPR14_OFFSET(sp); \ + lwz r13,GPR13_OFFSET(sp); \ + lwz r12,GPR12_OFFSET(sp); \ + lwz r11,GPR11_OFFSET(sp); \ + lwz r10,GPR10_OFFSET(sp); \ + lwz r9,GPR9_OFFSET(sp); \ + lwz r8,GPR8_OFFSET(sp); \ + lwz r7,GPR7_OFFSET(sp); \ + lwz r6,GPR6_OFFSET(sp); \ + lwz r5,GPR5_OFFSET(sp) + + .extern c_decrementer_handler + .globl dec_exceptionhandler +dec_exceptionhandler: + stwu sp,-EXCEPTION_FRAME_END(sp) //now we're able to adjust the stackpointer with it's cached address + + EXCEPTION_PROLOG + + mfmsr r3 + ori r3,r3,MSR_RI + mtmsr r3 + isync + + addi r14,sp,0 + lis r15,_thread_dispatch_disable_level@ha + + mfspr r3,SPRG0 + cmpwi r3,0 + bne nested + mfspr sp,SPRG1 + +nested: + addi r3,r3,1 + lwz r6,_thread_dispatch_disable_level@l(r15) + mtspr SPRG0,r3 + addi r6,r6,1 + stw r6,_thread_dispatch_disable_level@l(r15) + + addi r3,r14,0x08 + bl c_decrementer_handler + + mfspr r4,SPRG0 + lwz r3,_thread_dispatch_disable_level@l(r15) + addi r4,r4,-1 + addic. r3,r3,-1 + mtspr SPRG0,r4 + stw r3,_thread_dispatch_disable_level@l(r15) + addi sp,r14,0 + bne easy_exit + + lis r4,_context_switch_want@ha + lwz r5,_context_switch_want@l(r4) + cmpwi r5,0 + beq easy_exit + +switch: + bl __thread_dispatch + +easy_exit: + lwz r4,CR_OFFSET(sp) + mtcr r4 + lwz r4,LR_OFFSET(sp) + mtlr r4 + lwz r4,CTR_OFFSET(sp) + mtctr r4 + lwz r4,XER_OFFSET(sp) + mtxer r4 + + EXCEPTION_EPILOG + + mfmsr r4 + rlwinm r4,r4,0,31,29 + mtmsr r4 + isync + + lwz r0,GPR0_OFFSET(sp) + lwz toc,GPR2_OFFSET(sp) + + lwz r4,SRR0_OFFSET(sp) + mtsrr0 r4 + lwz r4,SRR1_OFFSET(sp) + rlwinm r4, r4, 0, 19, 17 + mtsrr1 r4 + + lwz r4,GPR4_OFFSET(sp) + lwz r3,GPR3_OFFSET(sp) + addi sp,sp,EXCEPTION_FRAME_END + rfi diff --git a/wii/libogc/libogc/depackrnc.S b/wii/libogc/libogc/depackrnc.S new file mode 100644 index 0000000000..18b70e3b65 --- /dev/null +++ b/wii/libogc/libogc/depackrnc.S @@ -0,0 +1,279 @@ +#include + +/**********************************/ +/* register map */ +/* */ +/* r0 = tmp */ +/* r3 = in = r2 */ +/* r4 = out = r4 */ +/* r5 = A = r5 */ +/* r6 = X = r6 */ +/* r7 = Y = r7 */ +/* r8 = carry flag = r3 */ +/* r10 = tmp = r8 */ +/* r11 = store data = r9 */ +/* r29 = copy in(r3) = r0 */ +/* r30 = copy out(r4) = r1 */ +/* */ +/**********************************/ + +#define LOAD \ + lbzu r5,1(r29); \ + slwi r5,r5,1; \ + insrwi r5,r8,1,31; \ + extrwi r8,r5,1,23; \ + clrlwi. r5,r5,24 + +#define GETBIT \ + slwi r5,r5,1; \ + extrwi r8,r5,1,23; \ + clrlwi. r5,r5,24 + +#define COPYRAW \ + lbzu r11,1(r29); \ + stbu r11,1(r30) + + //r3 is input, r4 is output + .globl depackrnc2 +depackrnc2: + mflr r0 + stw r0,4(sp) + stwu sp,-64(sp) + stw r3,8(sp) + stw r4,12(sp) + stw r29,48(sp) + mr r29,r3 + stw r30,52(sp) + mr r30,r4 + + li r8,1 + addi r30,r30,-1 + addi r29,r29,17 + + LOAD + GETBIT + b _xloop + +_fetch3: + LOAD + b _back3 +_fetch4: + LOAD + b _back4 +_fetch5: + LOAD + b _back5 +_fetch6: + LOAD + b _back6 +_fetch7: + LOAD + b _back7 + +_raw: + li r7,4 +_x4bits: + GETBIT + beq _fetch7 +_back7: + slwi r6,r6,1 //ROL(X) + insrwi r6,r8,1,31 + extrwi r8,r6,1,23 + clrlwi r6,r6,24 + + subic. r7,r7,1 + bne _x4bits + addi r7,r6,3 + + slwi r7,r7,1 //ROL(Y) + insrwi r7,r8,1,31 + extrwi r8,r7,1,23 + clrlwi r7,r7,24 +_rawlpb: + COPYRAW + COPYRAW + + subic. r7,r7,1 + bne _rawlpb + b _xloop + +_fetch0: + LOAD + rlwinm. r0,r8,0,31,31 + bne _smalls +_getlen: + GETBIT + beq _fetch3 +_back3: + slwi r7,r7,1 //ROL(Y) + insrwi r7,r8,1,31 + extrwi r8,r7,1,23 + clrlwi r7,r7,24 + + GETBIT + beq _fetch4 +_back4: + rlwinm. r0,r8,0,31,31 + beq _copy + + GETBIT + beq _fetch5 +_back5: + subic r7,r7,1 + slwi r7,r7,1 //ROL(Y) + insrwi r7,r8,1,31 + extrwi r8,r7,1,23 + clrlwi r7,r7,24 + + cmpwi r7,9 + beq _raw + +_copy: + GETBIT + beq _fetch6 +_back6: + rlwinm. r0,r8,0,31,31 + beq _bytedisp + + GETBIT + bne _skip0 + + LOAD +_skip0: + slwi r6,r6,1 //ROL(X) + insrwi r6,r8,1,31 + extrwi r8,r6,1,23 + clrlwi r6,r6,24 + + slwi r5,r5,1 //ROL(A) + insrwi r5,r8,1,31 + extrwi r8,r5,1,23 + clrlwi. r5,r5,24 + bne _skip1 + + LOAD +_skip1: + rlwinm. r0,r8,0,31,31 + bne _bigdisp + cmpwi r6,0 + bne _bytedisp + addic r6,r6,1 + +_another: + GETBIT + bne _dispx + + LOAD +_dispx: + slwi r6,r6,1 //ROL(X) + insrwi r6,r8,1,31 + extrwi r8,r6,1,23 + clrlwi r6,r6,24 + +_bytedisp: + lbzu r8,1(r29) + andi. r10,r30,0xff + sub r8,r10,r8 + srwi r10,r30,8 + sub r10,r10,r6 + slwi r10,r10,8 + add r8,r8,r10 + subic r8,r8,1 +_bytelp: + lbzu r11,1(r8) + stbu r11,1(r30) + + subic. r7,r7,1 + bne _bytelp + b _xloop + +_getbits: + LOAD + rlwinm. r0,r8,0,31,31 + bne _string +_xbyte: + COPYRAW +_xloop: + GETBIT + rlwinm. r0,r8,0,31,31 + bne _chkz + + COPYRAW + + GETBIT + rlwinm. r0,r8,0,31,31 + beq _xbyte + + li r8,1 +_chkz: + rlwinm. r0,r5,0,24,31 + beq _getbits + +_string: + li r6,0 + li r7,2 + GETBIT + beq _fetch0 + rlwinm. r0,r8,0,31,31 + beq _getlen +_smalls: + GETBIT + beq _fetch1 +_back1: + rlwinm. r0,r8,0,31,31 + beq _bytedisp + + addic r7,r7,1 + + GETBIT + beq _fetch2 +_back2: + rlwinm. r0,r8,0,31,31 + beq _copy + lbzu r7,1(r29) + cmpwi r7,0 + beq _overnout + addic r7,r7,8 + b _copy + +_bigdisp: + GETBIT + bne _skip2 + LOAD +_skip2: + slwi r6,r6,1 //ROL(X) + insrwi r6,r8,1,31 + extrwi r8,r6,1,23 + clrlwi r6,r6,24 + ori r6,r6,4 + + GETBIT + bne _skip3 + LOAD +_skip3: + rlwinm. r0,r8,0,31,31 + bne _bytedisp + b _another + +_fetch1: + LOAD + b _back1 +_fetch2: + LOAD + b _back2 +_overnout: + GETBIT + bne _check4end + LOAD +_check4end: + rlwinm. r0,r8,0,31,31 + bne _xloop + + lwz r29,48(sp) + lwz r30,52(sp) + lwz r4,12(sp) + lwz r3,8(sp) + lwz r0,68(sp) + addi sp,sp,64 + mtlr r0 + blr diff --git a/wii/libogc/libogc/depackrnc1.c b/wii/libogc/libogc/depackrnc1.c new file mode 100644 index 0000000000..e9bb782e5e --- /dev/null +++ b/wii/libogc/libogc/depackrnc1.c @@ -0,0 +1,309 @@ +#include + +#define RNC_SIGNATURE 0x524E4301 /* "RNC\001" */ + +typedef struct { + unsigned long bitbuf; /* holds between 16 and 32 bits */ + int bitcount; /* how many bits does bitbuf hold? */ +} bit_stream; + +typedef struct { + int num; /* number of nodes in the tree */ + struct { + unsigned long code; + int codelen; + int value; + } table[32]; +} huf_table; + +static long rnc_crc (void *data, long len); +static void read_huftable (huf_table *h, bit_stream *bs, unsigned char **p); +static unsigned long huf_read (huf_table *h, bit_stream *bs,unsigned char **p); + +static void bitread_init (bit_stream *bs, unsigned char **p); +static void bitread_fix (bit_stream *bs, unsigned char **p); +static unsigned long bit_peek (bit_stream *bs, unsigned long mask); +static void bit_advance (bit_stream *bs, int n, unsigned char **p); +static unsigned long bit_read (bit_stream *bs, unsigned long mask,int n, unsigned char **p); + +static unsigned long blong (unsigned char *p); +static unsigned long bword (unsigned char *p); +static unsigned long lword (unsigned char *p); + +static unsigned long mirror (unsigned long x, int n); + +s32 depackrnc1_ulen(void *packed) +{ + unsigned char *p = packed; + if (blong (p) != RNC_SIGNATURE) + return RNC_FILE_IS_NOT_RNC; + return blong (p+4); +} + +s32 depackrnc1(void *packed,void *unpacked) +{ + unsigned char *input = packed; + unsigned char *output = unpacked; + unsigned char *inputend, *outputend; + bit_stream bs; + huf_table raw, dist, len; + unsigned long ch_count; + unsigned long ret_len; + unsigned out_crc; + + if (blong(input) != RNC_SIGNATURE) + return RNC_FILE_IS_NOT_RNC; + ret_len = blong (input+4); + outputend = output + ret_len; + inputend = input + 18 + blong(input+8); + + input += 18; /* skip header */ + + /* + * Check the packed-data CRC. Also save the unpacked-data CRC + * for later. + */ + if (rnc_crc(input, inputend-input) != bword(input-4)) + return RNC_PACKED_CRC_ERROR; + out_crc = bword(input-6); + + bitread_init (&bs, &input); + bit_advance (&bs, 2, &input); /* discard first two bits */ + + /* + * Process chunks. + */ + while (output < outputend) { + read_huftable (&raw, &bs, &input); + read_huftable (&dist, &bs, &input); + read_huftable (&len, &bs, &input); + ch_count = bit_read (&bs, 0xFFFF, 16, &input); + + while (1) { + long length, posn; + + length = huf_read (&raw, &bs, &input); + if (length == -1) + return RNC_HUF_DECODE_ERROR; + if (length) { + while (length--) + *output++ = *input++; + bitread_fix (&bs, &input); + } + if (--ch_count <= 0) + break; + + posn = huf_read (&dist, &bs, &input); + if (posn == -1) + return RNC_HUF_DECODE_ERROR; + length = huf_read (&len, &bs, &input); + if (length == -1) + return RNC_HUF_DECODE_ERROR; + posn += 1; + length += 2; + while (length--) { + *output = output[-posn]; + output++; + } + } + } + if (outputend != output) + return RNC_FILE_SIZE_MISMATCH; + + if (rnc_crc(outputend-ret_len, ret_len) != out_crc) + return RNC_UNPACKED_CRC_ERROR; + + return ret_len; +} + +/* + * Read a Huffman table out of the bit stream and data stream given. + */ +static void read_huftable (huf_table *h, bit_stream *bs, unsigned char **p) { + int i, j, k, num; + int leaflen[32]; + int leafmax; + unsigned long codeb; /* big-endian form of code */ + + num = bit_read (bs, 0x1F, 5, p); + if (!num) + return; + + leafmax = 1; + for (i=0; itable[k].code = mirror (codeb, i); + h->table[k].codelen = i; + h->table[k].value = j; + codeb++; + k++; + } + codeb <<= 1; + } + + h->num = k; +} + +/* + * Read a value out of the bit stream using the given Huffman table. + */ +static unsigned long huf_read (huf_table *h, bit_stream *bs, + unsigned char **p) { + int i; + unsigned long val; + + for (i=0; inum; i++) { + unsigned long mask = (1 << h->table[i].codelen) - 1; + if (bit_peek(bs, mask) == h->table[i].code) + break; + } + if (i == h->num) + return -1; + bit_advance (bs, h->table[i].codelen, p); + + val = h->table[i].value; + + if (val >= 2) { + val = 1 << (val-1); + val |= bit_read (bs, val-1, h->table[i].value - 1, p); + } + return val; +} + +/* + * Initialises a bit stream with the first two bytes of the packed + * data. + */ +static void bitread_init (bit_stream *bs, unsigned char **p) { + bs->bitbuf = lword (*p); + bs->bitcount = 16; +} + +/* + * Fixes up a bit stream after literals have been read out of the + * data stream. + */ +static void bitread_fix (bit_stream *bs, unsigned char **p) { + bs->bitcount -= 16; + bs->bitbuf &= (1<bitcount)-1; /* remove the top 16 bits */ + bs->bitbuf |= (lword(*p)<bitcount);/* replace with what's at *p */ + bs->bitcount += 16; +} + +/* + * Returns some bits. + */ +static unsigned long bit_peek (bit_stream *bs, unsigned long mask) { + return bs->bitbuf & mask; +} + +/* + * Advances the bit stream. + */ +static void bit_advance (bit_stream *bs, int n, unsigned char **p) { + bs->bitbuf >>= n; + bs->bitcount -= n; + if (bs->bitcount < 16) { + (*p) += 2; + bs->bitbuf |= (lword(*p)<bitcount); + bs->bitcount += 16; + } +} + +/* + * Reads some bits in one go (ie the above two routines combined). + */ +static unsigned long bit_read (bit_stream *bs, unsigned long mask, + int n, unsigned char **p) { + unsigned long result = bit_peek (bs, mask); + bit_advance (bs, n, p); + return result; +} + +/* + * Return the big-endian longword at p. + */ +static unsigned long blong (unsigned char *p) { + unsigned long n; + n = p[0]; + n = (n << 8) + p[1]; + n = (n << 8) + p[2]; + n = (n << 8) + p[3]; + return n; +} + +/* + * Return the big-endian word at p. + */ +static unsigned long bword (unsigned char *p) { + unsigned long n; + n = p[0]; + n = (n << 8) + p[1]; + return n; +} + +/* + * Return the little-endian word at p. + */ +static unsigned long lword (unsigned char *p) { + unsigned long n; + n = p[1]; + n = (n << 8) + p[0]; + return n; +} + +/* + * Mirror the bottom n bits of x. + */ +static unsigned long mirror (unsigned long x, int n) { + unsigned long top = 1 << (n-1), bottom = 1; + while (top > bottom) { + unsigned long mask = top | bottom; + unsigned long masked = x & mask; + if (masked != 0 && masked != mask) + x ^= mask; + top >>= 1; + bottom <<= 1; + } + return x; +} + +/* + * Calculate a CRC, the RNC way. It re-computes its CRC table every + * time it's run, but who cares? ;-) + */ +static long rnc_crc (void *data, long len) { + unsigned short crctab[256]; + unsigned short val; + int i, j; + unsigned char *p = data; + + for (i=0; i<256; i++) { + val = i; + + for (j=0; j<8; j++) { + if (val & 1) + val = (val >> 1) ^ 0xA001; + else + val = (val >> 1); + } + crctab[i] = val; + } + + val = 0; + while (len--) { + val ^= *p++; + val = (val >> 8) ^ crctab[val & 0xFF]; + } + + return val; +} diff --git a/wii/libogc/libogc/dsp.c b/wii/libogc/libogc/dsp.c new file mode 100644 index 0000000000..01b16f8013 --- /dev/null +++ b/wii/libogc/libogc/dsp.c @@ -0,0 +1,459 @@ +/*------------------------------------------------------------- + +dsp.c -- DSP subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ + + +#include +#include +#include "asm.h" +#include "processor.h" +#include "irq.h" +#include "dsp.h" + +// DSPCR bits +#define DSPCR_DSPRESET 0x0800 // Reset DSP +#define DSPCR_DSPDMA 0x0200 // ARAM dma in progress, if set +#define DSPCR_DSPINTMSK 0x0100 // * interrupt mask (RW) +#define DSPCR_DSPINT 0x0080 // * interrupt active (RWC) +#define DSPCR_ARINTMSK 0x0040 +#define DSPCR_ARINT 0x0020 +#define DSPCR_AIINTMSK 0x0010 +#define DSPCR_AIINT 0x0008 +#define DSPCR_HALT 0x0004 // halt DSP +#define DSPCR_PIINT 0x0002 // assert DSP PI interrupt +#define DSPCR_RES 0x0001 // reset DSP + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +static u32 __dsp_inited = FALSE; +static u32 __dsp_rudetask_pend = FALSE; +static DSPCallback __dsp_intcb = NULL; +static dsptask_t *__dsp_currtask,*__dsp_lasttask,*__dsp_firsttask,*__dsp_rudetask,*tmp_task; + +static vu16* const _dspReg = (u16*)0xCC005000; + +static void __dsp_inserttask(dsptask_t *task) +{ + dsptask_t *t; + + if(!__dsp_firsttask) { + __dsp_currtask = task; + __dsp_lasttask = task; + __dsp_firsttask = task; + task->next = NULL; + task->prev = NULL; + return; + } + + t = __dsp_firsttask; + while(t) { + if(task->prioprio) { + task->prev = t->prev; + t->prev = task; + task->next = t; + if(!task->prev) { + __dsp_firsttask = task; + break; + } else { + task->prev->next = task; + break; + } + } + t = t->next; + } + if(t) return; + + __dsp_lasttask->next = task; + task->next = NULL; + task->prev = __dsp_lasttask; + __dsp_lasttask = task; +} + +static void __dsp_removetask(dsptask_t *task) +{ + task->flags = DSPTASK_CLEARALL; + task->state = DSPTASK_DONE; + if(__dsp_firsttask==task) { + if(task->next) { + __dsp_firsttask = task->next; + __dsp_firsttask->prev = NULL; + return; + } + __dsp_currtask = NULL; + __dsp_lasttask = NULL; + __dsp_firsttask = NULL; + return; + } + if(__dsp_lasttask==task) { + __dsp_lasttask = task->prev; + __dsp_lasttask->next = NULL; + __dsp_currtask = __dsp_firsttask; + return; + } + __dsp_currtask = __dsp_currtask->next; +} + +static void __dsp_boottask(dsptask_t *task) +{ + u32 mail; + while(!DSP_CheckMailFrom()); + mail = DSP_ReadMailFrom(); + DSP_SendMailTo(0x80F3A001); + while(DSP_CheckMailTo()); + DSP_SendMailTo((u32)task->iram_maddr); + while(DSP_CheckMailTo()); + DSP_SendMailTo(0x80F3C002); + while(DSP_CheckMailTo()); + DSP_SendMailTo((task->iram_addr&0xffff)); + while(DSP_CheckMailTo()); + DSP_SendMailTo(0x80F3A002); + while(DSP_CheckMailTo()); + DSP_SendMailTo(task->iram_len); + while(DSP_CheckMailTo()); + DSP_SendMailTo(0x80F3B002); + while(DSP_CheckMailTo()); + DSP_SendMailTo(0); + while(DSP_CheckMailTo()); + DSP_SendMailTo(0x80F3D001); + while(DSP_CheckMailTo()); + DSP_SendMailTo(task->init_vec); + while(DSP_CheckMailTo()); +} + +static void __dsp_exectask(dsptask_t *exec,dsptask_t *hire) +{ + if(!exec) { + DSP_SendMailTo(0); + while(DSP_CheckMailTo()); + DSP_SendMailTo(0); + while(DSP_CheckMailTo()); + DSP_SendMailTo(0); + while(DSP_CheckMailTo()); + } else { + DSP_SendMailTo((u32)exec->dram_maddr); + while(DSP_CheckMailTo()); + DSP_SendMailTo(exec->dram_len); + while(DSP_CheckMailTo()); + DSP_SendMailTo(exec->dram_addr); + while(DSP_CheckMailTo()); + } + + DSP_SendMailTo((u32)hire->iram_maddr); + while(DSP_CheckMailTo()); + DSP_SendMailTo(hire->iram_len); + while(DSP_CheckMailTo()); + DSP_SendMailTo(hire->iram_addr); + while(DSP_CheckMailTo()); + if(hire->state==DSPTASK_INIT) { + DSP_SendMailTo(hire->init_vec); + while(DSP_CheckMailTo()); + DSP_SendMailTo(0); + while(DSP_CheckMailTo()); + DSP_SendMailTo(0); + while(DSP_CheckMailTo()); + DSP_SendMailTo(0); + while(DSP_CheckMailTo()); + return; + } + + DSP_SendMailTo(hire->resume_vec); + while(DSP_CheckMailTo()); + + DSP_SendMailTo((u32)hire->dram_maddr); + while(DSP_CheckMailTo()); + DSP_SendMailTo(hire->dram_len); + while(DSP_CheckMailTo()); + DSP_SendMailTo(hire->dram_addr); + while(DSP_CheckMailTo()); +} + +static void __dsp_def_taskcb() +{ + u32 mail; + while(!DSP_CheckMailFrom()); + + mail = DSP_ReadMailFrom(); + if(__dsp_currtask->flags&DSPTASK_CANCEL) { + if(mail==0xDCD10002) mail = 0xDCD10003; + } + + switch(mail) { + case 0xDCD10000: + __dsp_currtask->state = DSPTASK_RUN; + if(__dsp_currtask->init_cb) __dsp_currtask->init_cb(__dsp_currtask); + break; + case 0xDCD10001: + __dsp_currtask->state = DSPTASK_RUN; + if(__dsp_currtask->res_cb) __dsp_currtask->res_cb(__dsp_currtask); + break; + case 0xDCD10002: + if(__dsp_rudetask_pend==TRUE) { + if(__dsp_rudetask==__dsp_currtask) { + DSP_SendMailTo(0xCDD10003); + while(DSP_CheckMailTo()); + + __dsp_rudetask = NULL; + __dsp_rudetask_pend = FALSE; + if(__dsp_currtask->res_cb) __dsp_currtask->res_cb(__dsp_currtask); + } else { + DSP_SendMailTo(0xCDD10001); + while(DSP_CheckMailTo()); + + __dsp_exectask(__dsp_currtask,__dsp_rudetask); + __dsp_currtask->flags = DSPTASK_YIELD; + __dsp_currtask = __dsp_rudetask; + __dsp_rudetask = NULL; + __dsp_rudetask_pend = FALSE; + } + } else if(__dsp_currtask->next==NULL) { + if(__dsp_firsttask==__dsp_currtask) { + DSP_SendMailTo(0xCDD10003); + while(DSP_CheckMailTo()); + + if(__dsp_currtask->res_cb) __dsp_currtask->res_cb(__dsp_currtask); + } else { + DSP_SendMailTo(0xCDD10001); + while(DSP_CheckMailTo()); + + __dsp_exectask(__dsp_currtask,__dsp_firsttask); + __dsp_currtask->state = DSPTASK_YIELD; + __dsp_currtask = __dsp_firsttask; + } + } else { + DSP_SendMailTo(0xCDD10001); + while(DSP_CheckMailTo()); + + __dsp_exectask(__dsp_currtask,__dsp_currtask->next); + __dsp_currtask->state = DSPTASK_YIELD; + __dsp_currtask = __dsp_currtask->next; + } + break; + case 0xDCD10003: + if(__dsp_rudetask_pend==TRUE) { + if(__dsp_currtask->done_cb) __dsp_currtask->done_cb(__dsp_currtask); + DSP_SendMailTo(0xCDD10001); + while(DSP_CheckMailTo()); + + __dsp_exectask(NULL,__dsp_rudetask); + __dsp_removetask(__dsp_currtask); + + __dsp_currtask = __dsp_rudetask; + __dsp_rudetask_pend = FALSE; + __dsp_rudetask = NULL; + } else if(__dsp_currtask->next==NULL) { + if(__dsp_firsttask==__dsp_currtask) { + if(__dsp_currtask->done_cb) __dsp_currtask->done_cb(__dsp_currtask); + DSP_SendMailTo(0xCDD10002); + while(DSP_CheckMailTo()); + + __dsp_currtask->state = DSPTASK_DONE; + __dsp_removetask(__dsp_currtask); + } + } else { + if(__dsp_currtask->done_cb) __dsp_currtask->done_cb(__dsp_currtask); + + DSP_SendMailTo(0xCDD10001); + while(DSP_CheckMailTo()); + + __dsp_currtask->state = DSPTASK_DONE; + __dsp_exectask(NULL,__dsp_firsttask); + __dsp_currtask = __dsp_firsttask; + __dsp_removetask(__dsp_lasttask); + } + break; + case 0xDCD10004: + if(__dsp_currtask->req_cb) __dsp_currtask->req_cb(__dsp_currtask); + break; + } + +} + +static void __dsp_inthandler(u32 nIrq,void *pCtx) +{ + _dspReg[5] = (_dspReg[5]&~(DSPCR_AIINT|DSPCR_ARINT))|DSPCR_DSPINT; + if(__dsp_intcb) __dsp_intcb(); +} + +void DSP_Init() +{ + u32 level; + _CPU_ISR_Disable(level); + if(__dsp_inited==FALSE) { + __dsp_intcb= __dsp_def_taskcb; + + IRQ_Request(IRQ_DSP_DSP,__dsp_inthandler,NULL); + __UnmaskIrq(IRQMASK(IRQ_DSP_DSP)); + + _dspReg[5] = (_dspReg[5]&~(DSPCR_AIINT|DSPCR_ARINT|DSPCR_DSPINT))|DSPCR_DSPRESET; + _dspReg[5] = (_dspReg[5]&~(DSPCR_HALT|DSPCR_AIINT|DSPCR_ARINT|DSPCR_DSPINT)); + + __dsp_currtask = NULL; + __dsp_firsttask = NULL; + __dsp_lasttask = NULL; + tmp_task = NULL; + __dsp_inited = TRUE; + } + _CPU_ISR_Restore(level); +} + +DSPCallback DSP_RegisterCallback(DSPCallback usr_cb) +{ + u32 level; + DSPCallback ret; + _CPU_ISR_Disable(level); + ret = __dsp_intcb; + if(usr_cb) + __dsp_intcb = usr_cb; + else + __dsp_intcb = __dsp_def_taskcb; + _CPU_ISR_Restore(level); + + return ret; +} + +u32 DSP_CheckMailTo() +{ + return _SHIFTR(_dspReg[0],15,1); +} + +u32 DSP_CheckMailFrom() +{ + return _SHIFTR(_dspReg[2],15,1); +} + +u32 DSP_ReadMailFrom() +{ + return (_SHIFTL(_dspReg[2],16,16)|(_dspReg[3]&0xffff)); +} + +void DSP_SendMailTo(u32 mail) +{ + _dspReg[0] = _SHIFTR(mail,16,16); + _dspReg[1] = (mail&0xffff); +} + +u32 DSP_ReadCPUtoDSP() +{ + u32 cpu_dsp; + cpu_dsp = (_SHIFTL(_dspReg[0],16,16)|(_dspReg[1]&0xffff)); + return cpu_dsp; +} + +void DSP_AssertInt() +{ + u32 level; + _CPU_ISR_Disable(level); + _dspReg[5] = (_dspReg[5]&~(DSPCR_AIINT|DSPCR_ARINT|DSPCR_DSPINT))|DSPCR_PIINT; + _CPU_ISR_Restore(level); +} + +void DSP_Reset() +{ + u16 old; + u32 level; + + _CPU_ISR_Disable(level); + old = _dspReg[5]; + _dspReg[5] = (old&~(DSPCR_AIINT|DSPCR_ARINT|DSPCR_DSPINT))|(DSPCR_DSPRESET|DSPCR_RES); + _CPU_ISR_Restore(level); +} + +void DSP_Halt() +{ + u32 level,old; + + _CPU_ISR_Disable(level); + old = _dspReg[5]; + _dspReg[5] = (old&~(DSPCR_AIINT|DSPCR_ARINT|DSPCR_DSPINT))|DSPCR_HALT; + _CPU_ISR_Restore(level); +} + +void DSP_Unhalt() +{ + u32 level; + + _CPU_ISR_Disable(level); + _dspReg[5] = (_dspReg[5]&~(DSPCR_AIINT|DSPCR_ARINT|DSPCR_DSPINT|DSPCR_HALT)); + _CPU_ISR_Restore(level); +} + +u32 DSP_GetDMAStatus() +{ + return _dspReg[5]&DSPCR_DSPDMA; +} + +dsptask_t* DSP_AddTask(dsptask_t *task) +{ + u32 level; + _CPU_ISR_Disable(level); + __dsp_inserttask(task); + task->state = DSPTASK_INIT; + task->flags = DSPTASK_ATTACH; + _CPU_ISR_Restore(level); + + if(__dsp_firsttask==task) __dsp_boottask(task); + return task; +} + +void DSP_CancelTask(dsptask_t *task) +{ + u32 level; + + _CPU_ISR_Disable(level); + task->flags |= DSPTASK_CANCEL; + _CPU_ISR_Restore(level); +} + +dsptask_t* DSP_AssertTask(dsptask_t *task) +{ + u32 level; + dsptask_t *ret = NULL; + + _CPU_ISR_Disable(level); + if(task==__dsp_currtask) { + __dsp_rudetask = task; + __dsp_rudetask_pend = TRUE; + ret = task; + } else { + if(task->prio<__dsp_currtask->prio) { + __dsp_rudetask = task; + __dsp_rudetask_pend = TRUE; + if(__dsp_currtask->state==DSPTASK_RUN) + _dspReg[5] = ((_dspReg[5]&~(DSPCR_DSPINT|DSPCR_ARINT|DSPCR_AIINT))|DSPCR_PIINT); + + ret = task; + } + } + _CPU_ISR_Restore(level); + + return ret; +} diff --git a/wii/libogc/libogc/dvd.c b/wii/libogc/libogc/dvd.c new file mode 100644 index 0000000000..3156954609 --- /dev/null +++ b/wii/libogc/libogc/dvd.c @@ -0,0 +1,2432 @@ +/*------------------------------------------------------------- + +dvd.h -- DVD subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +Additionally following copyrights apply for the patching system: + * Copyright (C) 2005 The GameCube Linux Team + * Copyright (C) 2005 Albert Herranz + +Thanks alot guys for that incredible patch!! + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ +#include +#include +#include +#include +#include "asm.h" +#include "processor.h" +#include "cache.h" +#include "lwp.h" +#include "irq.h" +#include "ogcsys.h" +#include "system.h" +#include "dvd.h" + +#define DVD_BRK (1<<0) +#define DVD_DE_MSK (1<<1) +#define DVD_DE_INT (1<<2) +#define DVD_TC_MSK (1<<3) +#define DVD_TC_INT (1<<4) +#define DVD_BRK_MSK (1<<5) +#define DVD_BRK_INT (1<<6) + +#define DVD_CVR_INT (1<<2) +#define DVD_CVR_MSK (1<<1) +#define DVD_CVR_STATE (1<<0) + +#define DVD_DI_MODE (1<<2) +#define DVD_DI_DMA (1<<1) +#define DVD_DI_START (1<<0) + +#define DVD_DISKIDSIZE 0x20 +#define DVD_DRVINFSIZE 0x20 +#define DVD_MAXCOMMANDS 0x12 + +#define DVD_DVDINQUIRY 0x12000000 +#define DVD_FWSETOFFSET 0x32000000 +#define DVD_FWENABLEEXT 0x55000000 +#define DVD_READSECTOR 0xA8000000 +#define DVD_READDISKID 0xA8000040 +#define DVD_SEEKSECTOR 0xAB000000 +#define DVD_REQUESTERROR 0xE0000000 +#define DVD_AUDIOSTREAM 0xE1000000 +#define DVD_AUDIOSTATUS 0xE2000000 +#define DVD_STOPMOTOR 0xE3000000 +#define DVD_AUDIOCONFIG 0xE4000000 +#define DVD_FWSETSTATUS 0xEE000000 +#define DVD_FWWRITEMEM 0xFE010100 +#define DVD_FWREADMEM 0xFE010000 +#define DVD_FWCTRLMOTOR 0xFE110000 +#define DVD_FWFUNCCALL 0xFE120000 + +#define DVD_MODEL04 0x20020402 +#define DVD_MODEL06 0x20010608 +#define DVD_MODEL08 0x20020823 +#define DVD_MODEL08Q 0x20010831 + +#define DVD_FWIRQVECTOR 0x00804c + +#define DVD_DRIVERESET 0x00000001 +#define DVD_CHIPPRESENT 0x00000002 +#define DVD_INTEROPER 0x00000004 + +/* drive status, status */ +#define DVD_STATUS(s) ((u8)((s)>>24)) + +#define DVD_STATUS_READY 0x00 +#define DVD_STATUS_COVER_OPENED 0x01 +#define DVD_STATUS_DISK_CHANGE 0x02 +#define DVD_STATUS_NO_DISK 0x03 +#define DVD_STATUS_MOTOR_STOP 0x04 +#define DVD_STATUS_DISK_ID_NOT_READ 0x05 + +/* drive status, error */ +#define DVD_ERROR(s) ((u32)((s)&0x00ffffff)) + +#define DVD_ERROR_NO_ERROR 0x000000 +#define DVD_ERROR_MOTOR_STOPPED 0x020400 +#define DVD_ERROR_DISK_ID_NOT_READ 0x020401 +#define DVD_ERROR_MEDIUM_NOT_PRESENT 0x023a00 +#define DVD_ERROR_SEEK_INCOMPLETE 0x030200 +#define DVD_ERROR_UNRECOVERABLE_READ 0x031100 +#define DVD_ERROR_TRANSFER_PROTOCOL 0x040800 +#define DVD_ERROR_INVALID_COMMAND 0x052000 +#define DVD_ERROR_AUDIOBUFFER_NOTSET 0x052001 +#define DVD_ERROR_BLOCK_OUT_OF_RANGE 0x052100 +#define DVD_ERROR_INVALID_FIELD 0x052400 +#define DVD_ERROR_INVALID_AUDIO_CMD 0x052401 +#define DVD_ERROR_INVALID_CONF_PERIOD 0x052402 +#define DVD_ERROR_END_OF_USER_AREA 0x056300 +#define DVD_ERROR_MEDIUM_CHANGED 0x062800 +#define DVD_ERROR_MEDIUM_CHANGE_REQ 0x0B5A01 + +#define DVD_SPINMOTOR_MASK 0x0000ff00 + +#define cpu_to_le32(x) (((x>>24)&0x000000ff) | ((x>>8)&0x0000ff00) | ((x<<8)&0x00ff0000) | ((x<<24)&0xff000000)) +#define dvd_may_retry(s) (DVD_STATUS(s) == DVD_STATUS_READY || DVD_STATUS(s) == DVD_STATUS_DISK_ID_NOT_READ) + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +typedef void (*dvdcallbacklow)(s32); +typedef void (*dvdstatecb)(dvdcmdblk *); + +typedef struct _dvdcmdl { + s32 cmd; + void *buf; + u32 len; + s64 offset; + dvdcallbacklow cb; +} dvdcmdl; + +typedef struct _dvdcmds { + void *buf; + u32 len; + s64 offset; +} dvdcmds; + +static u32 __dvd_initflag = 0; +static u32 __dvd_stopnextint = 0; +static vu32 __dvd_resetoccured = 0; +static u32 __dvd_waitcoverclose = 0; +static u32 __dvd_breaking = 0; +static vu32 __dvd_resetrequired = 0; +static u32 __dvd_canceling = 0; +static u32 __dvd_pauseflag = 0; +static u32 __dvd_pausingflag = 0; +static u64 __dvd_lastresetend = 0; +static u32 __dvd_ready = 0; +static u32 __dvd_resumefromhere = 0; +static u32 __dvd_fatalerror = 0; +static u32 __dvd_lasterror = 0; +static u32 __dvd_internalretries = 0; +static u32 __dvd_autofinishing = 0; +static u32 __dvd_autoinvalidation = 1; +static u32 __dvd_cancellasterror = 0; +static u32 __dvd_drivechecked = 0; +static u32 __dvd_drivestate = 0; +static u32 __dvd_extensionsenabled = TRUE; +static u32 __dvd_lastlen; +static u32 __dvd_nextcmdnum; +static u32 __dvd_workaround; +static u32 __dvd_workaroundseek; +static u32 __dvd_lastcmdwasread; +static u32 __dvd_currcmd; +static u32 __dvd_motorcntrl; +static lwpq_t __dvd_wait_queue; +static syswd_t __dvd_timeoutalarm; +static dvdcmdblk __dvd_block$15; +static dvdcmdblk __dvd_dummycmdblk; +static dvddiskid __dvd_tmpid0 ATTRIBUTE_ALIGN(32); +static dvddrvinfo __dvd_driveinfo ATTRIBUTE_ALIGN(32); +static dvdcallbacklow __dvd_callback = NULL; +static dvdcallbacklow __dvd_resetcovercb = NULL; +static dvdcallbacklow __dvd_finalunlockcb = NULL; +static dvdcallbacklow __dvd_finalreadmemcb = NULL; +static dvdcallbacklow __dvd_finalsudcb = NULL; +static dvdcallbacklow __dvd_finalstatuscb = NULL; +static dvdcallbacklow __dvd_finaladdoncb = NULL; +static dvdcallbacklow __dvd_finalpatchcb = NULL; +static dvdcallbacklow __dvd_finaloffsetcb = NULL; +static dvdcbcallback __dvd_cancelcallback = NULL; +static dvdcbcallback __dvd_mountusrcb = NULL; +static dvdstatecb __dvd_laststate = NULL; +static dvdcmdblk *__dvd_executing = NULL; +static void *__dvd_usrdata = NULL; +static dvddiskid *__dvd_diskID = (dvddiskid*)0x80000000; + +static lwp_queue __dvd_waitingqueue[4]; +static dvdcmdl __dvd_cmdlist[4]; +static dvdcmds __dvd_cmd_curr,__dvd_cmd_prev; + +static u32 __dvdpatchcode_size = 0; +static const u8 *__dvdpatchcode = NULL; + +static const u32 __dvd_patchcode04_size = 448; +static const u8 __dvd_patchcode04[] = +{ + 0xf7,0x10,0xff,0xf7,0xf4,0x74,0x25,0xd0,0x40,0xf7,0x20,0x4c,0x80,0xf4,0x74,0xd6, + 0x9c,0x08,0xf7,0x20,0xd6,0xfc,0xf4,0x74,0x28,0xae,0x08,0xf7,0x20,0xd2,0xfc,0x80, + 0x0c,0xc4,0xda,0xfc,0xfe,0xc8,0xda,0xfc,0xf5,0x00,0x01,0xe8,0x03,0xfc,0xc1,0x00, + 0xa0,0xf4,0x74,0x09,0xec,0x40,0x10,0xc8,0xda,0xfc,0xf5,0x00,0x02,0xe8,0x03,0xfc, + 0xbc,0x00,0xf4,0x74,0xf9,0xec,0x40,0x80,0x02,0xf0,0x20,0xc8,0x84,0x80,0xc0,0x9c, + 0x81,0xdc,0xb4,0x80,0xf5,0x30,0x00,0xf4,0x44,0xa1,0xd1,0x40,0xf8,0xaa,0x00,0x10, + 0xf4,0xd0,0x9c,0xd1,0x40,0xf0,0x01,0xdc,0xb4,0x80,0xf5,0x30,0x00,0xf7,0x48,0xaa, + 0x00,0xe9,0x07,0xf4,0xc4,0xa1,0xd1,0x40,0x10,0xfe,0xd8,0x32,0xe8,0x1d,0xf7,0x48, + 0xa8,0x00,0xe8,0x28,0xf7,0x48,0xab,0x00,0xe8,0x22,0xf7,0x48,0xe1,0x00,0xe8,0x1c, + 0xf7,0x48,0xee,0x00,0xe8,0x3d,0xd8,0x55,0xe8,0x31,0xfe,0x71,0x04,0xfd,0x22,0x00, + 0xf4,0x51,0xb0,0xd1,0x40,0xa0,0x40,0x04,0x40,0x06,0xea,0x33,0xf2,0xf9,0xf4,0xd2, + 0xb0,0xd1,0x40,0x71,0x04,0xfd,0x0a,0x00,0xf2,0x49,0xfd,0x05,0x00,0x51,0x04,0xf2, + 0x36,0xfe,0xf7,0x21,0xbc,0xff,0xf7,0x31,0xbc,0xff,0xfe,0xf5,0x30,0x01,0xfd,0x7e, + 0x00,0xea,0x0c,0xf5,0x30,0x01,0xc4,0xb0,0x81,0xf5,0x30,0x02,0xc4,0x94,0x81,0xdc, + 0xb4,0x80,0xf8,0xe0,0x00,0x10,0xa0,0xf5,0x10,0x01,0xf5,0x10,0x02,0xf5,0x10,0x03, + 0xfe,0xc8,0xda,0xfc,0xf7,0x00,0xfe,0xff,0xf7,0x31,0xd2,0xfc,0xea,0x0b,0xc8,0xda, + 0xfc,0xf7,0x00,0xfd,0xff,0xf7,0x31,0xd6,0xfc,0xc4,0xda,0xfc,0xcc,0x44,0xfc,0xf7, + 0x00,0xfe,0xff,0xc4,0x44,0xfc,0xf4,0x7d,0x28,0xae,0x08,0xe9,0x07,0xf4,0x75,0x60, + 0xd1,0x40,0xea,0x0c,0xf4,0x7d,0xd6,0x9c,0x08,0xe9,0x05,0xf4,0x75,0x94,0xd1,0x40, + 0xf2,0x7c,0xd0,0x04,0xcc,0x5b,0x80,0xd8,0x01,0xe9,0x02,0x7c,0x04,0x51,0x20,0x71, + 0x34,0xf4,0x7d,0xc1,0x85,0x08,0xe8,0x05,0xfe,0x80,0x01,0xea,0x02,0x80,0x00,0xa5, + 0xd8,0x00,0xe8,0x02,0x85,0x0c,0xc5,0xda,0xfc,0xf4,0x75,0xa0,0xd1,0x40,0x14,0xfe, + 0xf7,0x10,0xff,0xf7,0xf4,0xc9,0xa0,0xd1,0x40,0xd9,0x00,0xe8,0x22,0x21,0xf7,0x49, + 0x08,0x06,0xe9,0x05,0x85,0x02,0xf5,0x10,0x01,0xf4,0x79,0x00,0xf0,0x00,0xe9,0x05, + 0x80,0x00,0xf5,0x10,0x09,0xd9,0x06,0xe9,0x06,0x61,0x06,0xd5,0x06,0x41,0x06,0xf4, + 0xe0,0x9f,0xdc,0xc7,0xf4,0xe0,0xb5,0xcb,0xc7,0x00,0x00,0x00,0x74,0x0a,0x08,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +static const u32 __dvd_patchcode06_size = 448; +static const u8 __dvd_patchcode06[] = +{ + 0xf7,0x10,0xff,0xf7,0xf4,0x74,0x25,0xd0,0x40,0xf7,0x20,0x4c,0x80,0xf4,0x74,0x42, + 0x9d,0x08,0xf7,0x20,0xd6,0xfc,0xf4,0x74,0x45,0xb1,0x08,0xf7,0x20,0xd2,0xfc,0x80, + 0x0c,0xc4,0xda,0xfc,0xfe,0xc8,0xda,0xfc,0xf5,0x00,0x01,0xe8,0x03,0xfc,0xc1,0x00, + 0xa0,0xf4,0x74,0x09,0xec,0x40,0x10,0xc8,0xda,0xfc,0xf5,0x00,0x02,0xe8,0x03,0xfc, + 0xbc,0x00,0xf4,0x74,0x02,0xed,0x40,0x80,0x02,0xf0,0x20,0xc8,0x78,0x80,0xc0,0x90, + 0x81,0xdc,0xa8,0x80,0xf5,0x30,0x00,0xf4,0x44,0xa1,0xd1,0x40,0xf8,0xaa,0x00,0x10, + 0xf4,0xd0,0x9c,0xd1,0x40,0xf0,0x01,0xdc,0xa8,0x80,0xf5,0x30,0x00,0xf7,0x48,0xaa, + 0x00,0xe9,0x07,0xf4,0xc4,0xa1,0xd1,0x40,0x10,0xfe,0xd8,0x32,0xe8,0x1d,0xf7,0x48, + 0xa8,0x00,0xe8,0x28,0xf7,0x48,0xab,0x00,0xe8,0x22,0xf7,0x48,0xe1,0x00,0xe8,0x1c, + 0xf7,0x48,0xee,0x00,0xe8,0x3d,0xd8,0x55,0xe8,0x31,0xfe,0x71,0x04,0xfd,0x22,0x00, + 0xf4,0x51,0xb0,0xd1,0x40,0xa0,0x40,0x04,0x40,0x06,0xea,0x33,0xf2,0xf9,0xf4,0xd2, + 0xb0,0xd1,0x40,0x71,0x04,0xfd,0x0a,0x00,0xf2,0x49,0xfd,0x05,0x00,0x51,0x04,0xf2, + 0x36,0xfe,0xf7,0x21,0xbc,0xff,0xf7,0x31,0xbc,0xff,0xfe,0xf5,0x30,0x01,0xfd,0x7e, + 0x00,0xea,0x0c,0xf5,0x30,0x01,0xc4,0xa4,0x81,0xf5,0x30,0x02,0xc4,0x88,0x81,0xdc, + 0xa8,0x80,0xf8,0xe0,0x00,0x10,0xa0,0xf5,0x10,0x01,0xf5,0x10,0x02,0xf5,0x10,0x03, + 0xfe,0xc8,0xda,0xfc,0xf7,0x00,0xfe,0xff,0xf7,0x31,0xd2,0xfc,0xea,0x0b,0xc8,0xda, + 0xfc,0xf7,0x00,0xfd,0xff,0xf7,0x31,0xd6,0xfc,0xc4,0xda,0xfc,0xcc,0x44,0xfc,0xf7, + 0x00,0xfe,0xff,0xc4,0x44,0xfc,0xf4,0x7d,0x45,0xb1,0x08,0xe9,0x07,0xf4,0x75,0x60, + 0xd1,0x40,0xea,0x0c,0xf4,0x7d,0x42,0x9d,0x08,0xe9,0x05,0xf4,0x75,0x94,0xd1,0x40, + 0xf2,0x7c,0xd0,0x04,0xcc,0x5b,0x80,0xd8,0x01,0xe9,0x02,0x7c,0x04,0x51,0x20,0x71, + 0x34,0xf4,0x7d,0xb9,0x85,0x08,0xe8,0x05,0xfe,0x80,0x01,0xea,0x02,0x80,0x00,0xa5, + 0xd8,0x00,0xe8,0x02,0x85,0x0c,0xc5,0xda,0xfc,0xf4,0x75,0xa0,0xd1,0x40,0x14,0xfe, + 0xf7,0x10,0xff,0xf7,0xf4,0xc9,0xa0,0xd1,0x40,0xd9,0x00,0xe8,0x22,0x21,0xf7,0x49, + 0x08,0x06,0xe9,0x05,0x85,0x02,0xf5,0x10,0x01,0xf4,0x79,0x00,0xf0,0x00,0xe9,0x05, + 0x80,0x00,0xf5,0x10,0x09,0xd9,0x06,0xe9,0x06,0x61,0x06,0xd5,0x06,0x41,0x06,0xf4, + 0xe0,0xbc,0xdf,0xc7,0xf4,0xe0,0x37,0xcc,0xc7,0x00,0x00,0x00,0x74,0x0a,0x08,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +static const u32 __dvd_patchcode08_size = 448; +static const u8 __dvd_patchcode08[] = +{ + 0xf7,0x10,0xff,0xf7,0xf4,0x74,0x25,0xd0,0x40,0xf7,0x20,0x4c,0x80,0xf4,0x74,0x32, + 0x9d,0x08,0xf7,0x20,0xd6,0xfc,0xf4,0x74,0x75,0xae,0x08,0xf7,0x20,0xd2,0xfc,0x80, + 0x0c,0xc4,0xda,0xfc,0xfe,0xc8,0xda,0xfc,0xf5,0x00,0x01,0xe8,0x03,0xfc,0xc1,0x00, + 0xa0,0xf4,0x74,0x09,0xec,0x40,0x10,0xc8,0xda,0xfc,0xf5,0x00,0x02,0xe8,0x03,0xfc, + 0xbc,0x00,0xf4,0x74,0xf5,0xec,0x40,0x80,0x02,0xf0,0x20,0xc8,0x80,0x80,0xc0,0x98, + 0x81,0xdc,0xb0,0x80,0xf5,0x30,0x00,0xf4,0x44,0xa1,0xd1,0x40,0xf8,0xaa,0x00,0x10, + 0xf4,0xd0,0x9c,0xd1,0x40,0xf0,0x01,0xdc,0xb0,0x80,0xf5,0x30,0x00,0xf7,0x48,0xaa, + 0x00,0xe9,0x07,0xf4,0xc4,0xa1,0xd1,0x40,0x10,0xfe,0xd8,0x32,0xe8,0x1d,0xf7,0x48, + 0xa8,0x00,0xe8,0x28,0xf7,0x48,0xab,0x00,0xe8,0x22,0xf7,0x48,0xe1,0x00,0xe8,0x1c, + 0xf7,0x48,0xee,0x00,0xe8,0x3d,0xd8,0x55,0xe8,0x31,0xfe,0x71,0x04,0xfd,0x22,0x00, + 0xf4,0x51,0xb0,0xd1,0x40,0xa0,0x40,0x04,0x40,0x06,0xea,0x33,0xf2,0xf9,0xf4,0xd2, + 0xb0,0xd1,0x40,0x71,0x04,0xfd,0x0a,0x00,0xf2,0x49,0xfd,0x05,0x00,0x51,0x04,0xf2, + 0x36,0xfe,0xf7,0x21,0xbc,0xff,0xf7,0x31,0xbc,0xff,0xfe,0xf5,0x30,0x01,0xfd,0x7e, + 0x00,0xea,0x0c,0xf5,0x30,0x01,0xc4,0xac,0x81,0xf5,0x30,0x02,0xc4,0x90,0x81,0xdc, + 0xb0,0x80,0xf8,0xe0,0x00,0x10,0xa0,0xf5,0x10,0x01,0xf5,0x10,0x02,0xf5,0x10,0x03, + 0xfe,0xc8,0xda,0xfc,0xf7,0x00,0xfe,0xff,0xf7,0x31,0xd2,0xfc,0xea,0x0b,0xc8,0xda, + 0xfc,0xf7,0x00,0xfd,0xff,0xf7,0x31,0xd6,0xfc,0xc4,0xda,0xfc,0xcc,0x44,0xfc,0xf7, + 0x00,0xfe,0xff,0xc4,0x44,0xfc,0xf4,0x7d,0x75,0xae,0x08,0xe9,0x07,0xf4,0x75,0x60, + 0xd1,0x40,0xea,0x0c,0xf4,0x7d,0x32,0x9d,0x08,0xe9,0x05,0xf4,0x75,0x94,0xd1,0x40, + 0xf2,0x7c,0xd0,0x04,0xcc,0x5b,0x80,0xd8,0x01,0xe9,0x02,0x7c,0x04,0x51,0x20,0x71, + 0x34,0xf4,0x7d,0xc1,0x85,0x08,0xe8,0x05,0xfe,0x80,0x01,0xea,0x02,0x80,0x00,0xa5, + 0xd8,0x00,0xe8,0x02,0x85,0x0c,0xc5,0xda,0xfc,0xf4,0x75,0xa0,0xd1,0x40,0x14,0xfe, + 0xf7,0x10,0xff,0xf7,0xf4,0xc9,0xa0,0xd1,0x40,0xd9,0x00,0xe8,0x22,0x21,0xf7,0x49, + 0x08,0x06,0xe9,0x05,0x85,0x02,0xf5,0x10,0x01,0xf4,0x79,0x00,0xf0,0x00,0xe9,0x05, + 0x80,0x00,0xf5,0x10,0x09,0xd9,0x06,0xe9,0x06,0x61,0x06,0xd5,0x06,0x41,0x06,0xf4, + 0xe0,0xec,0xdc,0xc7,0xf4,0xe0,0x0e,0xcc,0xc7,0x00,0x00,0x00,0x74,0x0a,0x08,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +static const u32 __dvd_patchcodeQ08_size = 448; +static const u8 __dvd_patchcodeQ08[] = +{ + 0xf7,0x10,0xff,0xf7,0xf4,0x74,0x25,0xd0,0x40,0xf7,0x20,0x4c,0x80,0xf4,0x74,0x39, + 0x9e,0x08,0xf7,0x20,0xd6,0xfc,0xf4,0x74,0x02,0xb3,0x08,0xf7,0x20,0xd2,0xfc,0x80, + 0x0c,0xc4,0xda,0xfc,0xfe,0xc8,0xda,0xfc,0xf5,0x00,0x01,0xe8,0x03,0xfc,0xc1,0x00, + 0xa0,0xf4,0x74,0x09,0xec,0x40,0x10,0xc8,0xda,0xfc,0xf5,0x00,0x02,0xe8,0x03,0xfc, + 0xbc,0x00,0xf4,0x74,0x02,0xed,0x40,0x80,0x02,0xf0,0x20,0xc8,0x78,0x80,0xc0,0x92, + 0x81,0xdc,0xaa,0x80,0xf5,0x30,0x00,0xf4,0x44,0xa1,0xd1,0x40,0xf8,0xaa,0x00,0x10, + 0xf4,0xd0,0x9c,0xd1,0x40,0xf0,0x01,0xdc,0xaa,0x80,0xf5,0x30,0x00,0xf7,0x48,0xaa, + 0x00,0xe9,0x07,0xf4,0xc4,0xa1,0xd1,0x40,0x10,0xfe,0xd8,0x32,0xe8,0x1d,0xf7,0x48, + 0xa8,0x00,0xe8,0x28,0xf7,0x48,0xab,0x00,0xe8,0x22,0xf7,0x48,0xe1,0x00,0xe8,0x1c, + 0xf7,0x48,0xee,0x00,0xe8,0x3d,0xd8,0x55,0xe8,0x31,0xfe,0x71,0x04,0xfd,0x22,0x00, + 0xf4,0x51,0xb0,0xd1,0x40,0xa0,0x40,0x04,0x40,0x06,0xea,0x33,0xf2,0xf9,0xf4,0xd2, + 0xb0,0xd1,0x40,0x71,0x04,0xfd,0x0a,0x00,0xf2,0x49,0xfd,0x05,0x00,0x51,0x04,0xf2, + 0x36,0xfe,0xf7,0x21,0xbc,0xff,0xf7,0x31,0xbc,0xff,0xfe,0xf5,0x30,0x01,0xfd,0x7e, + 0x00,0xea,0x0c,0xf5,0x30,0x01,0xc4,0xa6,0x81,0xf5,0x30,0x02,0xc4,0x8a,0x81,0xdc, + 0xaa,0x80,0xf8,0xe0,0x00,0x10,0xa0,0xf5,0x10,0x01,0xf5,0x10,0x02,0xf5,0x10,0x03, + 0xfe,0xc8,0xda,0xfc,0xf7,0x00,0xfe,0xff,0xf7,0x31,0xd2,0xfc,0xea,0x0b,0xc8,0xda, + 0xfc,0xf7,0x00,0xfd,0xff,0xf7,0x31,0xd6,0xfc,0xc4,0xda,0xfc,0xcc,0x44,0xfc,0xf7, + 0x00,0xfe,0xff,0xc4,0x44,0xfc,0xf4,0x7d,0x02,0xb3,0x08,0xe9,0x07,0xf4,0x75,0x60, + 0xd1,0x40,0xea,0x0c,0xf4,0x7d,0x39,0x9e,0x08,0xe9,0x05,0xf4,0x75,0x94,0xd1,0x40, + 0xf2,0x7c,0xd0,0x04,0xcc,0x5b,0x80,0xd8,0x01,0xe9,0x02,0x7c,0x04,0x51,0x20,0x71, + 0x34,0xf4,0x7d,0x7f,0x86,0x08,0xe8,0x05,0xfe,0x80,0x01,0xea,0x02,0x80,0x00,0xa5, + 0xd8,0x00,0xe8,0x02,0x85,0x0c,0xc5,0xda,0xfc,0xf4,0x75,0xa0,0xd1,0x40,0x14,0xfe, + 0xf7,0x10,0xff,0xf7,0xf4,0xc9,0xa0,0xd1,0x40,0xd9,0x00,0xe8,0x22,0x21,0xf7,0x49, + 0x08,0x06,0xe9,0x05,0x85,0x02,0xf5,0x10,0x01,0xf4,0x79,0x00,0xf0,0x00,0xe9,0x05, + 0x80,0x00,0xf5,0x10,0x09,0xd9,0x06,0xe9,0x06,0x61,0x06,0xd5,0x06,0x41,0x06,0xf4, + 0xe0,0x79,0xe1,0xc7,0xf4,0xe0,0xeb,0xcd,0xc7,0x00,0x00,0x00,0xa4,0x0a,0x08,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +static vu32* const _piReg = (u32*)0xCC003000; + +#if defined(HW_RVL) + static vu32* const _diReg = (u32*)0xCD006000; +#elif defined(HW_DOL) + static vu32* const _diReg = (u32*)0xCC006000; +#endif + +static u8 __dvd_unlockcmd$221[12] = {0xff,0x01,'M','A','T','S','H','I','T','A',0x02,0x00}; +static u8 __dvd_unlockcmd$222[12] = {0xff,0x00,'D','V','D','-','G','A','M','E',0x03,0x00}; +static u32 __dvd_errortable[] = { + 0x00000000, 0x00023a00, 0x00062800, 0x00030200, + 0x00031100, 0x00052000, 0x00052001, 0x00052100, + 0x00052400, 0x00052401, 0x00052402, 0x000B5A01, + 0x00056300, 0x00020401, 0x00020400, 0x00040800, + 0x00100007, 0x00000000 +}; + +void __dvd_statecheckid(void); +void __dvd_stategettingerror(void); +void __dvd_statecoverclosed(void); +void __dvd_stateready(void); +void __dvd_statemotorstopped(void); +void __dvd_statetimeout(void); +void __dvd_stategotoretry(void); +void __dvd_stateerror(s32 result); +void __dvd_statecoverclosed_cmd(dvdcmdblk *block); +void __dvd_statebusy(dvdcmdblk *block); +void DVD_LowReset(u32 reset_mode); +s32 DVD_LowSeek(s64 offset,dvdcallbacklow cb); +s32 DVD_LowRead(void *buf,u32 len,s64 offset,dvdcallbacklow cb); +s32 DVD_LowReadId(dvddiskid *diskID,dvdcallbacklow cb); +s32 DVD_LowRequestError(dvdcallbacklow cb); +s32 DVD_LowStopMotor(dvdcallbacklow cb); +s32 DVD_LowInquiry(dvddrvinfo *info,dvdcallbacklow cb); +s32 DVD_LowWaitCoverClose(dvdcallbacklow cb); +s32 DVD_LowAudioStream(u32 subcmd,u32 len,s64 offset,dvdcallbacklow cb); +s32 DVD_LowAudioBufferConfig(s32 enable,u32 size,dvdcallbacklow cb); +s32 DVD_LowRequestAudioStatus(u32 subcmd,dvdcallbacklow cb); +s32 DVD_LowEnableExtensions(u8 enable,dvdcallbacklow cb); +s32 DVD_LowSpinMotor(u32 mode,dvdcallbacklow cb); +s32 DVD_LowSetStatus(u32 status,dvdcallbacklow cb); +s32 DVD_LowUnlockDrive(dvdcallbacklow cb); +s32 DVD_LowPatchDriveCode(dvdcallbacklow cb); +s32 DVD_LowSpinUpDrive(dvdcallbacklow cb); +s32 DVD_LowControlMotor(u32 mode,dvdcallbacklow cb); +s32 DVD_LowFuncCall(u32 address,dvdcallbacklow cb); +s32 DVD_LowReadmem(u32 address,void *buffer,dvdcallbacklow cb); +s32 DVD_LowSetGCMOffset(s64 offset,dvdcallbacklow cb); +s32 DVD_LowSetOffset(s64 offset,dvdcallbacklow cb); +s32 DVD_ReadAbsAsyncPrio(dvdcmdblk *block,void *buf,u32 len,s64 offset,dvdcbcallback cb,s32 prio); +s32 DVD_ReadDiskID(dvdcmdblk *block,dvddiskid *id,dvdcbcallback cb); +s32 __issuecommand(s32 prio,dvdcmdblk *block); + +extern void udelay(int us); +extern u32 diff_msec(unsigned long long start,unsigned long long end); +extern long long gettime(void); +extern void __MaskIrq(u32); +extern void __UnmaskIrq(u32); +extern syssramex* __SYS_LockSramEx(void); +extern u32 __SYS_UnlockSramEx(u32 write); + +static u8 err2num(u32 errorcode) +{ + u32 i; + + i=0; + while(i<18) { + if(errorcode==__dvd_errortable[i]) return i; + i++; + } + if(errorcode<0x00100000 || errorcode>0x00100008) return 29; + + return 17; +} + +static u8 convert(u32 errorcode) +{ + u8 err,err_num; + + if((errorcode-0x01230000)==0x4567) return 255; + else if((errorcode-0x01230000)==0x4568) return 254; + + err = _SHIFTR(errorcode,24,8); + err_num = err2num((errorcode&0x00ffffff)); + if(err>0x06) err = 0x06; + + return err_num+(err*30); +} + +static void __dvd_clearwaitingqueue() +{ + u32 i; + + for(i=0;i<4;i++) + __lwp_queue_init_empty(&__dvd_waitingqueue[i]); +} + +static s32 __dvd_checkwaitingqueue() +{ + u32 i; + u32 level; + + _CPU_ISR_Disable(level); + for(i=0;i<4;i++) { + if(!__lwp_queue_isempty(&__dvd_waitingqueue[i])) break; + } + _CPU_ISR_Restore(level); + return (i<4); +} + +static s32 __dvd_pushwaitingqueue(s32 prio,dvdcmdblk *block) +{ + u32 level; + _CPU_ISR_Disable(level); + __lwp_queue_appendI(&__dvd_waitingqueue[prio],&block->node); + _CPU_ISR_Restore(level); + return 1; +} + +static dvdcmdblk* __dvd_popwaitingqueueprio(s32 prio) +{ + u32 level; + dvdcmdblk *ret = NULL; + _CPU_ISR_Disable(level); + ret = (dvdcmdblk*)__lwp_queue_firstnodeI(&__dvd_waitingqueue[prio]); + _CPU_ISR_Restore(level); + return ret; +} + +static dvdcmdblk* __dvd_popwaitingqueue() +{ + u32 i,level; + dvdcmdblk *ret = NULL; + _CPU_ISR_Disable(level); + for(i=0;i<4;i++) { + if(!__lwp_queue_isempty(&__dvd_waitingqueue[i])) { + _CPU_ISR_Restore(level); + ret = __dvd_popwaitingqueueprio(i); + return ret; + } + } + _CPU_ISR_Restore(level); + return NULL; +} + +static void __dvd_timeouthandler(syswd_t alarm,void *cbarg) +{ + dvdcallbacklow cb; + + __MaskIrq(IRQMASK(IRQ_PI_DI)); + cb = __dvd_callback; + if(cb) cb(0x10); +} + +static void __dvd_storeerror(u32 errorcode) +{ + u8 err; + syssramex *ptr; + + err = convert(errorcode); + ptr = __SYS_LockSramEx(); + ptr->dvderr_code = err; + __SYS_UnlockSramEx(1); +} + +static u32 __dvd_categorizeerror(u32 errorcode) +{ + if((errorcode-0x20000)==0x0400) { + __dvd_lasterror = errorcode; + return 1; + } + + if(DVD_ERROR(errorcode)==DVD_ERROR_MEDIUM_CHANGED + || DVD_ERROR(errorcode)==DVD_ERROR_MEDIUM_NOT_PRESENT + || DVD_ERROR(errorcode)==DVD_ERROR_MEDIUM_CHANGE_REQ + || (DVD_ERROR(errorcode)-0x40000)==0x3100) return 0; + + __dvd_internalretries++; + if(__dvd_internalretries==2) { + if(__dvd_lasterror==DVD_ERROR(errorcode)) { + __dvd_lasterror = DVD_ERROR(errorcode); + return 1; + } + __dvd_lasterror = DVD_ERROR(errorcode); + return 2; + } + + __dvd_lasterror = DVD_ERROR(errorcode); + if(DVD_ERROR(errorcode)!=DVD_ERROR_UNRECOVERABLE_READ) { + if(__dvd_executing->cmd!=0x0005) return 3; + } + return 2; +} + +static void __SetupTimeoutAlarm(const struct timespec *tp) +{ + SYS_SetAlarm(__dvd_timeoutalarm,tp,__dvd_timeouthandler,NULL); +} + +static void __Read(void *buffer,u32 len,s64 offset,dvdcallbacklow cb) +{ + u32 val; + struct timespec tb; + __dvd_callback = cb; + __dvd_stopnextint = 0; + __dvd_lastcmdwasread = 1; + + _diReg[2] = DVD_READSECTOR; + _diReg[3] = (u32)(offset>>2); + _diReg[4] = len; + _diReg[5] = (u32)buffer; + _diReg[6] = len; + + __dvd_lastlen = len; + + _diReg[7] = (DVD_DI_DMA|DVD_DI_START); + val = _diReg[7]; + if(val>0x00a00000) { + tb.tv_sec = 20; + tb.tv_nsec = 0; + __SetupTimeoutAlarm(&tb); + } else { + tb.tv_sec = 10; + tb.tv_nsec = 0; + __SetupTimeoutAlarm(&tb); + } +} + +static void __DoRead(void *buffer,u32 len,s64 offset,dvdcallbacklow cb) +{ + __dvd_nextcmdnum = 0; + __dvd_cmdlist[0].cmd = -1; + __Read(buffer,len,offset,cb); +} + +static u32 __ProcessNextCmd() +{ + u32 cmd_num = __dvd_nextcmdnum; + if(__dvd_cmdlist[cmd_num].cmd==0x0001) { + __dvd_nextcmdnum++; + __Read(__dvd_cmdlist[cmd_num].buf,__dvd_cmdlist[cmd_num].len,__dvd_cmdlist[cmd_num].offset,__dvd_cmdlist[cmd_num].cb); + return 1; + } + + if(__dvd_cmdlist[cmd_num].cmd==0x0002) { + __dvd_nextcmdnum++; + DVD_LowSeek(__dvd_cmdlist[cmd_num].offset,__dvd_cmdlist[cmd_num].cb); + return 1; + } + return 0; +} + +static void __DVDLowWATypeSet(u32 workaround,u32 workaroundseek) +{ + u32 level; + + _CPU_ISR_Disable(level); + __dvd_workaround = workaround; + __dvd_workaroundseek = workaroundseek; + _CPU_ISR_Restore(level); +} + +static void __DVDInitWA() +{ + __dvd_nextcmdnum = 0; + __DVDLowWATypeSet(0,0); +} + +static s32 __dvd_checkcancel(u32 cancelpt) +{ + dvdcmdblk *block; + + if(__dvd_canceling) { + __dvd_resumefromhere = cancelpt; + __dvd_canceling = 0; + + block = __dvd_executing; + __dvd_executing = &__dvd_dummycmdblk; + + block->state = 10; + if(block->cb) block->cb(-3,block); + if(__dvd_cancelcallback) __dvd_cancelcallback(0,block); + + __dvd_stateready(); + return 1; + } + return 0; +} + +static void __dvd_stateretrycb(s32 result) +{ + if(result==0x0010) { + __dvd_executing->state = -1; + __dvd_statetimeout(); + return; + } + + if(result&0x0002) __dvd_stateerror(0x01234567); + if(result==0x0001) { + __dvd_statebusy(__dvd_executing); + return; + } +} + +static void __dvd_unrecoverederrorretrycb(s32 result) +{ + u32 val; + + if(result==0x0010) { + __dvd_executing->state = -1; + __dvd_statetimeout(); + return; + } + + __dvd_executing->state = -1; + if(result&0x0002) __dvd_stateerror(0x01234567); + else { + val = _diReg[8]; + __dvd_stateerror(val); + } +} + +static void __dvd_unrecoverederrorcb(s32 result) +{ + if(result==0x0010) { + __dvd_executing->state = -1; + __dvd_statetimeout(); + return; + } + if(result&0x0001) { + __dvd_stategotoretry(); + return; + } + if(!(result==0x0002)) DVD_LowRequestError(__dvd_unrecoverederrorretrycb); +} + +static void __dvd_stateerrorcb(s32 result) +{ + dvdcmdblk *block; + if(result==0x0010) { + __dvd_executing->state = -1; + __dvd_statetimeout(); + return; + } + + __dvd_fatalerror = 1; + block = __dvd_executing; + __dvd_executing = &__dvd_dummycmdblk; + if(block->cb) block->cb(-1,block); + if(__dvd_canceling) { + __dvd_canceling = 0; + if(__dvd_cancelcallback) __dvd_cancelcallback(0,block); + } + __dvd_stateready(); +} + +static void __dvd_stategettingerrorcb(s32 result) +{ + s32 ret; + u32 val,cnclpt; + if(result==0x0010) { + __dvd_executing->state = -1; + __dvd_statetimeout(); + return; + } + if(result&0x0002) { + __dvd_executing->state = -1; + __dvd_stateerror(0x01234567); + return; + } + if(result==0x0001) { + val = _diReg[8]; + ret = __dvd_categorizeerror(val); + if(ret==1) { + __dvd_executing->state = -1; + __dvd_stateerror(val); + return; + } else if(ret==2 || ret==3) cnclpt = 0; + else { + if(DVD_STATUS(val)==DVD_STATUS_COVER_OPENED) cnclpt = 4; + else if(DVD_STATUS(val)==DVD_STATUS_DISK_CHANGE) cnclpt = 6; + else if(DVD_STATUS(val)==DVD_STATUS_NO_DISK) cnclpt = 3; + else cnclpt = 5; + } + if(__dvd_checkcancel(cnclpt)) return; + + if(ret==2) { + if(dvd_may_retry(val)) { + // disable the extensions iff they're enabled and we were trying to read the disc ID + if(__dvd_executing->cmd==0x05) { + __dvd_lasterror = 0; + __dvd_extensionsenabled = FALSE; + DVD_LowSpinUpDrive(__dvd_stateretrycb); + return; + } + __dvd_statebusy(__dvd_executing); + } else { + __dvd_storeerror(val); + __dvd_stategotoretry(); + } + return; + } else if(ret==3) { + if(DVD_ERROR(val)==DVD_ERROR_UNRECOVERABLE_READ) { + DVD_LowSeek(__dvd_executing->offset,__dvd_unrecoverederrorcb); + return; + } else { + __dvd_laststate(__dvd_executing); + return; + } + } else if(DVD_STATUS(val)==DVD_STATUS_COVER_OPENED) { + __dvd_executing->state = 5; + __dvd_statemotorstopped(); + return; + } else if(DVD_STATUS(val)==DVD_STATUS_DISK_CHANGE) { + __dvd_executing->state = 3; + __dvd_statecoverclosed(); + return; + } else if(DVD_STATUS(val)==DVD_STATUS_NO_DISK) { + __dvd_executing->state = 4; + __dvd_statemotorstopped(); + return; + } + __dvd_executing->state = -1; + __dvd_stateerror(0x01234567); + return; + } +} + +static void __dvd_statebusycb(s32 result) +{ + u32 val; + dvdcmdblk *block; + if(result==0x0010) { + __dvd_executing->state = -1; + __dvd_statetimeout(); + return; + } + if(__dvd_currcmd==0x0003 || __dvd_currcmd==0x000f) { + if(result&0x0002) { + __dvd_executing->state = -1; + __dvd_stateerror(0x01234567); + return; + } + if(result==0x0001) { + __dvd_internalretries = 0; + if(__dvd_currcmd==0x000f) __dvd_resetrequired = 1; + if(__dvd_checkcancel(7)) return; + + __dvd_executing->state = 7; + __dvd_statemotorstopped(); + return; + } + } + if(result&0x0004) { + + } + if(__dvd_currcmd==0x0001 || __dvd_currcmd==0x0004 + || __dvd_currcmd==0x0005 || __dvd_currcmd==0x000e) { + __dvd_executing->txdsize += (__dvd_executing->currtxsize-_diReg[6]); + } + if(result&0x0008) { + __dvd_canceling = 0; + block = __dvd_executing; + __dvd_executing = &__dvd_dummycmdblk; + __dvd_executing->state = 10; + if(block->cb) block->cb(-3,block); + if(__dvd_cancelcallback) __dvd_cancelcallback(0,block); + __dvd_stateready(); + return; + } + if(result&0x0001) { + __dvd_internalretries = 0; + if(__dvd_checkcancel(0)) return; + + if(__dvd_currcmd==0x0001 || __dvd_currcmd==0x0004 + || __dvd_currcmd==0x0005 || __dvd_currcmd==0x000e) { + if(__dvd_executing->txdsize!=__dvd_executing->len) { + __dvd_statebusy(__dvd_executing); + return; + } + block = __dvd_executing; + __dvd_executing = &__dvd_dummycmdblk; + block->state = 0; + if(block->cb) block->cb(block->txdsize,block); + __dvd_stateready(); + return; + } + if(__dvd_currcmd==0x0009 || __dvd_currcmd==0x000a + || __dvd_currcmd==0x000b || __dvd_currcmd==0x000c) { + + val = _diReg[8]; + if(__dvd_currcmd==0x000a || __dvd_currcmd==0x000b) val <<= 2; + + block = __dvd_executing; + __dvd_executing = &__dvd_dummycmdblk; + block->state = 0; + if(block->cb) block->cb(val,block); + __dvd_stateready(); + return; + } + if(__dvd_currcmd==0x0006) { + if(!__dvd_executing->currtxsize) { + if(_diReg[8]&0x0001) { + block = __dvd_executing; + __dvd_executing = &__dvd_dummycmdblk; + block->state = 9; + if(block->cb) block->cb(-2,block); + __dvd_stateready(); + return; + } + __dvd_autofinishing = 0; + __dvd_executing->currtxsize = 1; + DVD_LowAudioStream(0,__dvd_executing->len,__dvd_executing->offset,__dvd_statebusycb); + return; + } + + block = __dvd_executing; + __dvd_executing = &__dvd_dummycmdblk; + block->state = 0; + if(block->cb) block->cb(0,block); + __dvd_stateready(); + return; + } + if(__dvd_currcmd==0x0010) { + if(__dvd_drivestate&DVD_CHIPPRESENT) { + block = __dvd_executing; + __dvd_executing = &__dvd_dummycmdblk; + block->state = 0; + if(block->cb) block->cb(DVD_DISKIDSIZE,block); + __dvd_stateready(); + return; + } + } + + block = __dvd_executing; + __dvd_executing = &__dvd_dummycmdblk; + block->state = 0; + if(block->cb) block->cb(0,block); + __dvd_stateready(); + return; + } + if(result==0x0002) { + if(__dvd_currcmd==0x000e) { + __dvd_executing->state = -1; + __dvd_stateerror(0x01234567); + return; + } + if((__dvd_currcmd==0x0001 || __dvd_currcmd==0x0004 + || __dvd_currcmd==0x0005 || __dvd_currcmd==0x000e) + && __dvd_executing->txdsize==__dvd_executing->len) { + if(__dvd_checkcancel(0)) return; + + block = __dvd_executing; + __dvd_executing = &__dvd_dummycmdblk; + block->state = 0; + if(block->cb) block->cb(block->txdsize,block); + __dvd_stateready(); + return; + } + } + __dvd_stategettingerror(); +} + +static void __dvd_mountsynccb(s32 result,dvdcmdblk *block) +{ + LWP_ThreadBroadcast(__dvd_wait_queue); +} + +static void __dvd_inquirysynccb(s32 result,dvdcmdblk *block) +{ + LWP_ThreadBroadcast(__dvd_wait_queue); +} + +static void __dvd_readsynccb(s32 result,dvdcmdblk *block) +{ + LWP_ThreadBroadcast(__dvd_wait_queue); +} + +static void __dvd_streamatendsynccb(s32 result,dvdcmdblk *block) +{ + LWP_ThreadBroadcast(__dvd_wait_queue); +} + +static void __dvd_seeksynccb(s32 result,dvdcmdblk *block) +{ + LWP_ThreadBroadcast(__dvd_wait_queue); +} + +static void __dvd_spinupdrivesynccb(s32 result,dvdcmdblk *block) +{ + LWP_ThreadBroadcast(__dvd_wait_queue); +} + +static void __dvd_motorcntrlsynccb(s32 result,dvdcmdblk *block) +{ + LWP_ThreadBroadcast(__dvd_wait_queue); +} + +static void __dvd_setgcmsynccb(s32 result,dvdcmdblk *block) +{ + LWP_ThreadBroadcast(__dvd_wait_queue); +} + +static void __dvd_statemotorstoppedcb(s32 result) +{ + _diReg[1] = 0; + __dvd_executing->state = 3; + __dvd_statecoverclosed(); +} + +static void __dvd_statecoverclosedcb(s32 result) +{ + if(result==0x0010) { + __dvd_executing->state = -1; + __dvd_statetimeout(); + return; + } + + if(!(result&0x0006)) { + __dvd_internalretries = 0; + __dvd_statecheckid(); + return; + } + + if(result==0x0002) __dvd_stategettingerror(); +} + +static void __dvd_statecheckid1cb(s32 result) +{ + __dvd_ready = 1; + if(memcmp(__dvd_diskID,&__dvd_tmpid0,DVD_DISKIDSIZE)) memcpy(__dvd_diskID,&__dvd_tmpid0,DVD_DISKIDSIZE); + LWP_ThreadBroadcast(__dvd_wait_queue); +} + +static void __dvd_stategotoretrycb(s32 result) +{ + if(result==0x0010) { + __dvd_executing->state = -1; + __dvd_statetimeout(); + return; + } + if(result&0x0002) { + __dvd_executing->state = -1; + __dvd_stateerror(0x01234567); + return; + } + if(result==0x0001) { + __dvd_internalretries = 0; + if(__dvd_currcmd==0x0004 || __dvd_currcmd==0x0005 + || __dvd_currcmd==0x000d || __dvd_currcmd==0x000f) { + __dvd_resetrequired = 1; + if(__dvd_checkcancel(2)) return; + + __dvd_executing->state = 11; + __dvd_statemotorstopped(); + } + } +} + +static void __dvd_getstatuscb(s32 result) +{ + u32 val,*pn_data; + dvdcallbacklow cb; + if(result==0x0010) { + __dvd_statetimeout(); + return; + } + if(result==0x0001) { + val = _diReg[8]; + + pn_data = __dvd_usrdata; + __dvd_usrdata = NULL; + if(pn_data) pn_data[0] = val; + + cb = __dvd_finalstatuscb; + __dvd_finalstatuscb = NULL; + if(cb) cb(result); + return; + } + __dvd_stategettingerror(); +} + +static void __dvd_readmemcb(s32 result) +{ + u32 val,*pn_data; + dvdcallbacklow cb; + if(result==0x0010) { + __dvd_statetimeout(); + return; + } + if(result==0x0001) { + val = _diReg[8]; + + pn_data = __dvd_usrdata; + __dvd_usrdata = NULL; + if(pn_data) pn_data[0] = val; + + cb = __dvd_finalreadmemcb; + __dvd_finalreadmemcb = NULL; + if(cb) cb(result); + return; + } + __dvd_stategettingerror(); +} + +static void __dvd_cntrldrivecb(s32 result) +{ + if(result==0x0010) { + __dvd_statetimeout(); + return; + } + if(result==0x0001) { + DVD_LowSpinMotor(__dvd_motorcntrl,__dvd_finalsudcb); + return; + } + __dvd_stategettingerror(); +} + +static void __dvd_setgcmoffsetcb(s32 result) +{ + s64 *pn_data,offset; + if(result==0x0010) { + __dvd_statetimeout(); + return; + } + if(result==0x0001) { + pn_data = (s64*)__dvd_usrdata; + __dvd_usrdata = NULL; + + offset = 0; + if(pn_data) offset = *pn_data; + DVD_LowSetOffset(offset,__dvd_finaloffsetcb); + return; + } + __dvd_stategettingerror(); +} + +static void __dvd_handlespinupcb(s32 result) +{ + static u32 step = 0; + if(result==0x0010) { + __dvd_statetimeout(); + return; + } + if(result==0x0001) { + if(step==0x0000) { + step++; + _diReg[1] = _diReg[1]; + DVD_LowEnableExtensions(__dvd_extensionsenabled,__dvd_handlespinupcb); + return; + } + if(step==0x0001) { + step++; + _diReg[1] = _diReg[1]; + DVD_LowSpinMotor((DVD_SPINMOTOR_ACCEPT|DVD_SPINMOTOR_UP),__dvd_handlespinupcb); + return; + } + if(step==0x0002) { + step = 0; + if(!__dvd_lasterror) { + _diReg[1] = _diReg[1]; + DVD_LowSetStatus((_SHIFTL((DVD_STATUS_DISK_ID_NOT_READ+1),16,8)|0x00000300),__dvd_finalsudcb); + return; + } + __dvd_finalsudcb(result); + return; + } + } + + step = 0; + __dvd_stategettingerror(); +} + +static void __dvd_fwpatchcb(s32 result) +{ + static u32 step = 0; + if(result==0x0010) { + __dvd_statetimeout(); + return; + } + if(result==0x0001) { + if(step==0x0000) { + step++; + _diReg[1] = _diReg[1]; + DVD_LowUnlockDrive(__dvd_fwpatchcb); + return; + } + if(step==0x0001) { + step = 0; + DVD_LowPatchDriveCode(__dvd_finalpatchcb); + return; + } + } + + step = 0; + __dvd_stategettingerror(); +} + +static void __dvd_checkaddonscb(s32 result) +{ + u32 txdsize; + dvdcallbacklow cb; + __dvd_drivechecked = 1; + txdsize = (DVD_DISKIDSIZE-_diReg[6]); + if(result&0x0001) { + // if the read was successful but was interrupted by a break we issue the read again. + if(txdsize!=DVD_DISKIDSIZE) { + _diReg[1] = _diReg[1]; + DCInvalidateRange(&__dvd_tmpid0,DVD_DISKIDSIZE); + DVD_LowReadId(&__dvd_tmpid0,__dvd_checkaddonscb); + return; + } + __dvd_drivestate |= DVD_CHIPPRESENT; + } + + cb = __dvd_finaladdoncb; + __dvd_finaladdoncb = NULL; + if(cb) cb(0x01); +} + +static void __dvd_checkaddons(dvdcallbacklow cb) +{ + __dvd_finaladdoncb = cb; + + // try to read disc ID. + _diReg[1] = _diReg[1]; + DCInvalidateRange(&__dvd_tmpid0,DVD_DISKIDSIZE); + DVD_LowReadId(&__dvd_tmpid0,__dvd_checkaddonscb); + return; +} + +static void __dvd_fwpatchmem(dvdcallbacklow cb) +{ + __dvd_finalpatchcb = cb; + + _diReg[1] = _diReg[1]; + DCInvalidateRange(&__dvd_driveinfo,DVD_DRVINFSIZE); + DVD_LowInquiry(&__dvd_driveinfo,__dvd_fwpatchcb); + return; +} + +static void __dvd_handlespinup() +{ + _diReg[1] = _diReg[1]; + DVD_LowUnlockDrive(__dvd_handlespinupcb); + return; +} + +static void __dvd_spinupdrivecb(s32 result) +{ + if(result==0x0010) { + __dvd_statetimeout(); + return; + } + if(result==0x0001) { + if(!__dvd_drivechecked) { + __dvd_checkaddons(__dvd_spinupdrivecb); + return; + } + if(!(__dvd_drivestate&DVD_CHIPPRESENT)) { + if(!(__dvd_drivestate&DVD_INTEROPER)) { + if(!(__dvd_drivestate&DVD_DRIVERESET)) { + DVD_Reset(DVD_RESETHARD); + udelay(1150*1000); + } + __dvd_fwpatchmem(__dvd_spinupdrivecb); + return; + } + __dvd_handlespinup(); + return; + } + + __dvd_finalsudcb(result); + return; + } + __dvd_stategettingerror(); +} + +static void __dvd_statecoverclosed_spinupcb(s32 result) +{ + DCInvalidateRange(&__dvd_tmpid0,DVD_DISKIDSIZE); + __dvd_laststate = __dvd_statecoverclosed_cmd; + __dvd_statecoverclosed_cmd(__dvd_executing); +} + +static void __DVDInterruptHandler(u32 nIrq,void *pCtx) +{ + s64 now; + u32 status,ir,irm,irmm,diff; + dvdcallbacklow cb; + + SYS_CancelAlarm(__dvd_timeoutalarm); + + irmm = 0; + if(__dvd_lastcmdwasread) { + __dvd_cmd_prev.buf = __dvd_cmd_curr.buf; + __dvd_cmd_prev.len = __dvd_cmd_curr.len; + __dvd_cmd_prev.offset = __dvd_cmd_curr.offset; + if(__dvd_stopnextint) irmm |= 0x0008; + } + __dvd_lastcmdwasread = 0; + __dvd_stopnextint = 0; + + status = _diReg[0]; + irm = (status&(DVD_DE_MSK|DVD_TC_MSK|DVD_BRK_MSK)); + ir = ((status&(DVD_DE_INT|DVD_TC_INT|DVD_BRK_INT))&(irm<<1)); + if(ir&DVD_BRK_INT) irmm |= 0x0008; + if(ir&DVD_TC_INT) irmm |= 0x0001; + if(ir&DVD_DE_INT) irmm |= 0x0002; + + if(irmm) __dvd_resetoccured = 0; + + _diReg[0] = (ir|irm); + + now = gettime(); + diff = diff_msec(__dvd_lastresetend,now); + if(__dvd_resetoccured && diff>200) { + status = _diReg[1]; + irm = status&DVD_CVR_MSK; + ir = (status&DVD_CVR_INT)&(irm<<1); + if(ir&0x0004) { + cb = __dvd_resetcovercb; + __dvd_resetcovercb = NULL; + if(cb) { + cb(0x0004); + } + } + _diReg[1] = _diReg[1]; + } else { + if(__dvd_waitcoverclose) { + status = _diReg[1]; + irm = status&DVD_CVR_MSK; + ir = (status&DVD_CVR_INT)&(irm<<1); + if(ir&DVD_CVR_INT) irmm |= 0x0004; + _diReg[1] = (ir|irm); + __dvd_waitcoverclose = 0; + } else + _diReg[1] = 0; + } + + if(irmm&0x0008) { + if(!__dvd_breaking) irmm &= ~0x0008; + } + + if(irmm&0x0001) { + if(__ProcessNextCmd()) return; + } else { + __dvd_cmdlist[0].cmd = -1; + __dvd_nextcmdnum = 0; + } + + cb = __dvd_callback; + __dvd_callback = NULL; + if(cb) cb(irmm); + + __dvd_breaking = 0; +} + +static void __dvd_patchdrivecb(s32 result) +{ + u32 cnt = 0; + static u32 cmd_buf[3]; + static u32 stage = 0; + static u32 nPos = 0; + static u32 drv_address = 0x0040d000; + const u32 chunk_size = 3*sizeof(u32); + + if(__dvdpatchcode==NULL || __dvdpatchcode_size<=0) return; + + while(stage!=0x0003) { + __dvd_callback = __dvd_patchdrivecb; + if(stage==0x0000) { + for(cnt=0;cnt=__dvdpatchcode_size) stage = 0x0002; + else stage = 0x0001; + + _diReg[1] = _diReg[1]; + _diReg[2] = DVD_FWWRITEMEM; + _diReg[3] = drv_address; + _diReg[4] = _SHIFTL(cnt,16,16); + _diReg[7] = (DVD_DI_DMA|DVD_DI_START); + + drv_address += cnt; + return; + } + + if(stage>=0x0001) { + if(stage>0x0001) stage = 0x0003; + else stage = 0; + + _diReg[1] = _diReg[1]; + _diReg[2] = cmd_buf[0]; + _diReg[3] = cmd_buf[1]; + _diReg[4] = cmd_buf[2]; + _diReg[7] = DVD_DI_START; + return; + } + } + __dvd_callback = NULL; + __dvdpatchcode = NULL; + __dvd_drivestate |= DVD_INTEROPER; + DVD_LowFuncCall(0x0040d000,__dvd_finalpatchcb); +} + + +static void __dvd_unlockdrivecb(s32 result) +{ + u32 i; + __dvd_callback = __dvd_finalunlockcb; + _diReg[1] = _diReg[1]; + for(i=0;i<3;i++) _diReg[2+i] = ((u32*)__dvd_unlockcmd$222)[i]; + _diReg[7] = DVD_DI_START; +} + +void __dvd_resetasync(dvdcbcallback cb) +{ + u32 level; + + _CPU_ISR_Disable(level); + __dvd_clearwaitingqueue(); + if(__dvd_canceling) __dvd_cancelcallback = cb; + else { + if(__dvd_executing) __dvd_executing->cb = NULL; + DVD_CancelAllAsync(cb); + } + _CPU_ISR_Restore(level); +} + +void __dvd_statebusy(dvdcmdblk *block) +{ + u32 len; + __dvd_laststate = __dvd_statebusy; + if(block->cmd>DVD_MAXCOMMANDS) return; + + switch(block->cmd) { + case 1: //Read(Sector) + case 4: + _diReg[1] = _diReg[1]; + len = block->len-block->txdsize; + if(len<0x80000) block->currtxsize = len; + else block->currtxsize = 0x80000; + DVD_LowRead(block->buf+block->txdsize,block->currtxsize,(block->offset+block->txdsize),__dvd_statebusycb); + return; + case 2: //Seek(Sector) + _diReg[1] = _diReg[1]; + DVD_LowSeek(block->offset,__dvd_statebusycb); + return; + case 3: + case 15: + DVD_LowStopMotor(__dvd_statebusycb); + return; + case 5: //ReadDiskID + _diReg[1] = _diReg[1]; + block->currtxsize = DVD_DISKIDSIZE; + DVD_LowReadId(block->buf,__dvd_statebusycb); + return; + case 6: + _diReg[1] = _diReg[1]; + if(__dvd_autofinishing) { + __dvd_executing->currtxsize = 0; + DVD_LowRequestAudioStatus(0,__dvd_statebusycb); + } else { + __dvd_executing->currtxsize = 1; + DVD_LowAudioStream(0,__dvd_executing->len,__dvd_executing->offset,__dvd_statebusycb); + } + return; + case 7: + _diReg[1] = _diReg[1]; + DVD_LowAudioStream(0x00010000,0,0,__dvd_statebusycb); + return; + case 8: + _diReg[1] = _diReg[1]; + __dvd_autofinishing = 1; + DVD_LowAudioStream(0,0,0,__dvd_statebusycb); + return; + case 9: + _diReg[1] = _diReg[1]; + DVD_LowRequestAudioStatus(0,__dvd_statebusycb); + return; + case 10: + _diReg[1] = _diReg[1]; + DVD_LowRequestAudioStatus(0x00010000,__dvd_statebusycb); + return; + case 11: + _diReg[1] = _diReg[1]; + DVD_LowRequestAudioStatus(0x00020000,__dvd_statebusycb); + return; + case 12: + _diReg[1] = _diReg[1]; + DVD_LowRequestAudioStatus(0x00030000,__dvd_statebusycb); + return; + case 13: + _diReg[1] = _diReg[1]; + DVD_LowAudioBufferConfig(__dvd_executing->offset,__dvd_executing->len,__dvd_statebusycb); + return; + case 14: //Inquiry + _diReg[1] = _diReg[1]; + block->currtxsize = 0x20; + DVD_LowInquiry(block->buf,__dvd_statebusycb); + return; + case 16: + __dvd_lasterror = 0; + __dvd_extensionsenabled = TRUE; + DVD_LowSpinUpDrive(__dvd_statebusycb); + return; + case 17: + _diReg[1] = _diReg[1]; + DVD_LowControlMotor(block->offset,__dvd_statebusycb); + return; + case 18: + _diReg[1] = _diReg[1]; + DVD_LowSetGCMOffset(block->offset,__dvd_statebusycb); + return; + default: + return; + } +} + +void __dvd_stateready() +{ + dvdcmdblk *block; + if(!__dvd_checkwaitingqueue()) { + __dvd_executing = NULL; + return; + } + + __dvd_executing = __dvd_popwaitingqueue(); + + if(__dvd_fatalerror) { + __dvd_executing->state = -1; + block = __dvd_executing; + __dvd_executing = &__dvd_dummycmdblk; + if(block->cb) block->cb(-1,block); + __dvd_stateready(); + return; + } + + __dvd_currcmd = __dvd_executing->cmd; + + if(__dvd_resumefromhere) { + if(__dvd_resumefromhere<=7) { + switch(__dvd_resumefromhere) { + case 1: + __dvd_executing->state = 6; + __dvd_statemotorstopped(); + break; + case 2: + __dvd_executing->state = 11; + __dvd_statemotorstopped(); + break; + case 3: + __dvd_executing->state = 4; + __dvd_statemotorstopped(); + break; + case 4: + __dvd_executing->state = 5; + __dvd_statemotorstopped(); + break; + case 5: + __dvd_executing->state = -1; + __dvd_stateerror(__dvd_cancellasterror); + break; + case 6: + __dvd_executing->state = 3; + __dvd_statecoverclosed(); + break; + case 7: + __dvd_executing->state = 7; + __dvd_statemotorstopped(); + break; + default: + break; + + } + } + __dvd_resumefromhere = 0; + return; + } + __dvd_executing->state = 1; + __dvd_statebusy(__dvd_executing); +} + +void __dvd_statecoverclosed() +{ + dvdcmdblk *blk; + if(__dvd_currcmd==0x0004 || __dvd_currcmd==0x0005 + || __dvd_currcmd==0x000d || __dvd_currcmd==0x000f + || __dvd_currcmd==0x0010) { + __dvd_clearwaitingqueue(); + blk = __dvd_executing; + __dvd_executing = &__dvd_dummycmdblk; + if(blk->cb) blk->cb(-4,blk); + __dvd_stateready(); + } else { + __dvd_extensionsenabled = TRUE; + DVD_LowSpinUpDrive(__dvd_statecoverclosed_spinupcb); + } +} + +void __dvd_statecoverclosed_cmd(dvdcmdblk *block) +{ + DVD_LowReadId(&__dvd_tmpid0,__dvd_statecoverclosedcb); +} + +void __dvd_statemotorstopped() +{ + DVD_LowWaitCoverClose(__dvd_statemotorstoppedcb); +} + +void __dvd_stateerror(s32 result) +{ + __dvd_storeerror(result); + DVD_LowStopMotor(__dvd_stateerrorcb); +} + +void __dvd_stategettingerror() +{ + DVD_LowRequestError(__dvd_stategettingerrorcb); +} + +void __dvd_statecheckid2(dvdcmdblk *block) +{ + +} + +void __dvd_statecheckid() +{ + dvdcmdblk *blk; + if(__dvd_currcmd==0x0003) { + if(memcmp(&__dvd_dummycmdblk,&__dvd_executing,sizeof(dvdcmdblk))) { + DVD_LowStopMotor(__dvd_statecheckid1cb); + return; + } + memcpy(__dvd_diskID,&__dvd_tmpid0,DVD_DISKIDSIZE); + + __dvd_executing->state = 1; + DCInvalidateRange(&__dvd_tmpid0,DVD_DISKIDSIZE); + __dvd_laststate = __dvd_statecheckid2; + __dvd_statecheckid2(__dvd_executing); + return; + } + if(__dvd_currcmd==0x0010) { + blk = __dvd_executing; + blk->state = 0; + __dvd_executing = &__dvd_dummycmdblk; + if(blk->cb) blk->cb(0,blk); + __dvd_stateready(); + return; + } + if(__dvd_currcmd!=0x0005 && memcmp(__dvd_diskID,&__dvd_tmpid0,DVD_DISKIDSIZE)) { + blk = __dvd_executing; + blk->state = -1; + __dvd_executing = &__dvd_dummycmdblk; + if(blk->cb) blk->cb(-1,blk); //terminate current operation + if(__dvd_mountusrcb) __dvd_mountusrcb(DVD_DISKIDSIZE,blk); //if we came across here, notify user callback of successful remount + __dvd_stateready(); + return; + } + __dvd_statebusy(__dvd_executing); +} + +void __dvd_statetimeout() +{ + __dvd_storeerror(0x01234568); + DVD_Reset(DVD_RESETSOFT); + __dvd_stateerrorcb(0); +} + +void __dvd_stategotoretry() +{ + DVD_LowStopMotor(__dvd_stategotoretrycb); +} + +s32 __issuecommand(s32 prio,dvdcmdblk *block) +{ + s32 ret; + u32 level; + if(__dvd_autoinvalidation && + (block->cmd==0x0001 || block->cmd==0x00004 + || block->cmd==0x0005 || block->cmd==0x000e)) DCInvalidateRange(block->buf,block->len); + + _CPU_ISR_Disable(level); + block->state = 0x0002; + ret = __dvd_pushwaitingqueue(prio,block); + if(!__dvd_executing) __dvd_stateready(); + _CPU_ISR_Restore(level); + return ret; +} + +s32 DVD_LowUnlockDrive(dvdcallbacklow cb) +{ + u32 i; + + __dvd_callback = __dvd_unlockdrivecb; + __dvd_finalunlockcb = cb; + __dvd_stopnextint = 0; + + for(i=0;i<3;i++) _diReg[2+i] = ((u32*)__dvd_unlockcmd$221)[i]; + _diReg[7] = DVD_DI_START; + + return 1; +} + +s32 DVD_LowPatchDriveCode(dvdcallbacklow cb) +{ + __dvd_finalpatchcb = cb; + __dvd_stopnextint = 0; + + if(__dvd_driveinfo.rel_date==DVD_MODEL04) { + __dvdpatchcode = __dvd_patchcode04; + __dvdpatchcode_size = __dvd_patchcode04_size; + } else if((__dvd_driveinfo.rel_date&0x0000ff00)==0x00000500) { // for wii: since i don't know the real date i have to mask & compare. + } else if(__dvd_driveinfo.rel_date==DVD_MODEL06) { + __dvdpatchcode = __dvd_patchcode06; + __dvdpatchcode_size = __dvd_patchcode06_size; + } else if(__dvd_driveinfo.rel_date==DVD_MODEL08) { + __dvdpatchcode = __dvd_patchcode08; + __dvdpatchcode_size = __dvd_patchcode08_size; + } else if(__dvd_driveinfo.rel_date==DVD_MODEL08Q) { + __dvdpatchcode = __dvd_patchcodeQ08; + __dvdpatchcode_size = __dvd_patchcodeQ08_size; + } else if((__dvd_driveinfo.rel_date&0x0000ff00)==0x00000900) { // for wii: since i don't know the real date i have to mask & compare. + } else { + __dvdpatchcode = NULL; + __dvdpatchcode_size = 0; + } + + __dvd_patchdrivecb(0); + return 1; +} + +s32 DVD_LowSetOffset(s64 offset,dvdcallbacklow cb) +{ + __dvd_stopnextint = 0; + __dvd_callback = cb; + + _diReg[2] = DVD_FWSETOFFSET; + _diReg[3] = (u32)(offset>>2); + _diReg[4] = 0; + _diReg[7] = DVD_DI_START; + + return 1; +} + +s32 DVD_LowSetGCMOffset(s64 offset,dvdcallbacklow cb) +{ + static s64 loc_offset = 0; + loc_offset = offset; + + __dvd_finaloffsetcb = cb; + __dvd_stopnextint = 0; + __dvd_usrdata = &loc_offset; + + DVD_LowUnlockDrive(__dvd_setgcmoffsetcb); + return 1; +} + +s32 DVD_LowReadmem(u32 address,void *buffer,dvdcallbacklow cb) +{ + __dvd_finalreadmemcb = cb; + __dvd_usrdata = buffer; + __dvd_callback = __dvd_readmemcb; + __dvd_stopnextint = 0; + + _diReg[2] = DVD_FWREADMEM; + _diReg[3] = address; + _diReg[4] = 0x00010000; + _diReg[7] = DVD_DI_START; + + return 1; +} + +s32 DVD_LowFuncCall(u32 address,dvdcallbacklow cb) +{ + __dvd_callback = cb; + __dvd_stopnextint = 0; + + _diReg[2] = DVD_FWFUNCCALL; + _diReg[3] = address; + _diReg[4] = 0x66756E63; + _diReg[7] = DVD_DI_START; + + return 1; +} + +s32 DVD_LowSpinMotor(u32 mode,dvdcallbacklow cb) +{ + __dvd_callback = cb; + __dvd_stopnextint = 0; + + _diReg[2] = DVD_FWCTRLMOTOR|(mode&0x0000ff00); + _diReg[3] = 0; + _diReg[4] = 0; + _diReg[7] = DVD_DI_START; + + return 1; +} + +s32 DVD_LowEnableExtensions(u8 enable,dvdcallbacklow cb) +{ + __dvd_callback = cb; + __dvd_stopnextint = 0; + + _diReg[2] = (DVD_FWENABLEEXT|_SHIFTL(enable,16,8)); + _diReg[3] = 0; + _diReg[4] = 0; + _diReg[7] = DVD_DI_START; + + return 1; +} + +s32 DVD_LowSetStatus(u32 status,dvdcallbacklow cb) +{ + __dvd_callback = cb; + __dvd_stopnextint = 0; + + _diReg[2] = (DVD_FWSETSTATUS|(status&0x00ffffff)); + _diReg[3] = 0; + _diReg[4] = 0; + _diReg[7] = DVD_DI_START; + + return 1; +} + +s32 DVD_LowGetStatus(u32 *status,dvdcallbacklow cb) +{ + __dvd_finalstatuscb = cb; + __dvd_usrdata = status; + + DVD_LowRequestError(__dvd_getstatuscb); + return 1; +} + +s32 DVD_LowControlMotor(u32 mode,dvdcallbacklow cb) +{ + __dvd_stopnextint = 0; + __dvd_motorcntrl = mode; + __dvd_finalsudcb = cb; + DVD_LowUnlockDrive(__dvd_cntrldrivecb); + + return 1; + +} + +s32 DVD_LowSpinUpDrive(dvdcallbacklow cb) +{ + __dvd_finalsudcb = cb; + __dvd_spinupdrivecb(1); + + return 1; +} + +s32 DVD_LowRead(void *buf,u32 len,s64 offset,dvdcallbacklow cb) +{ + _diReg[6] = len; + + __dvd_cmd_curr.buf = buf; + __dvd_cmd_curr.len = len; + __dvd_cmd_curr.offset = offset; + __DoRead(buf,len,offset,cb); + return 1; +} + +s32 DVD_LowSeek(s64 offset,dvdcallbacklow cb) +{ + struct timespec tb; + + __dvd_callback = cb; + __dvd_stopnextint = 0; + + _diReg[2] = DVD_SEEKSECTOR; + _diReg[3] = (u32)(offset>>2); + _diReg[7] = DVD_DI_START; + + tb.tv_sec = 10; + tb.tv_nsec = 0; + __SetupTimeoutAlarm(&tb); + + return 1; +} + +s32 DVD_LowReadId(dvddiskid *diskID,dvdcallbacklow cb) +{ + struct timespec tb; + + __dvd_callback = cb; + __dvd_stopnextint = 0; + + _diReg[2] = DVD_READDISKID; + _diReg[3] = 0; + _diReg[4] = DVD_DISKIDSIZE; + _diReg[5] = (u32)diskID; + _diReg[6] = DVD_DISKIDSIZE; + _diReg[7] = (DVD_DI_DMA|DVD_DI_START); + + tb.tv_sec = 10; + tb.tv_nsec = 0; + __SetupTimeoutAlarm(&tb); + + return 1; +} + +s32 DVD_LowRequestError(dvdcallbacklow cb) +{ + struct timespec tb; + + __dvd_callback = cb; + __dvd_stopnextint = 0; + + _diReg[2] = DVD_REQUESTERROR; + _diReg[7] = DVD_DI_START; + + tb.tv_sec = 10; + tb.tv_nsec = 0; + __SetupTimeoutAlarm(&tb); + + return 1; +} + +s32 DVD_LowStopMotor(dvdcallbacklow cb) +{ + struct timespec tb; + + __dvd_callback = cb; + __dvd_stopnextint = 0; + + _diReg[2] = DVD_STOPMOTOR; + _diReg[7] = DVD_DI_START; + + tb.tv_sec = 10; + tb.tv_nsec = 0; + __SetupTimeoutAlarm(&tb); + + return 1; +} + +s32 DVD_LowInquiry(dvddrvinfo *info,dvdcallbacklow cb) +{ + struct timespec tb; + + __dvd_callback = cb; + __dvd_stopnextint = 0; + + _diReg[2] = DVD_DVDINQUIRY; + _diReg[4] = 0x20; + _diReg[5] = (u32)info; + _diReg[6] = 0x20; + _diReg[7] = (DVD_DI_DMA|DVD_DI_START); + + tb.tv_sec = 10; + tb.tv_nsec = 0; + __SetupTimeoutAlarm(&tb); + + return 1; +} + +s32 DVD_LowWaitCoverClose(dvdcallbacklow cb) +{ + __dvd_callback = cb; + __dvd_waitcoverclose = 1; + __dvd_stopnextint = 0; + _diReg[1] = DVD_CVR_MSK; + return 1; +} + +void DVD_LowReset(u32 reset_mode) +{ + u32 val; + + _diReg[1] = DVD_CVR_MSK; + val = _piReg[9]; + _piReg[9] = ((val&~0x0004)|0x0001); + + udelay(12); + + if(reset_mode==DVD_RESETHARD) val |= 0x0004; + val |= 0x0001; + _piReg[9] = val; + + __dvd_resetoccured = 1; + __dvd_lastresetend = gettime(); + __dvd_drivestate |= DVD_DRIVERESET; +} + +s32 DVD_LowAudioStream(u32 subcmd,u32 len,s64 offset,dvdcallbacklow cb) +{ + struct timespec tb; + + __dvd_callback = cb; + __dvd_stopnextint = 0; + + _diReg[2] = DVD_AUDIOSTREAM|subcmd; + _diReg[3] = (u32)(offset>>2); + _diReg[4] = len; + _diReg[7] = DVD_DI_START; + + tb.tv_sec = 10; + tb.tv_nsec = 0; + __SetupTimeoutAlarm(&tb); + + return 1; +} + +s32 DVD_LowAudioBufferConfig(s32 enable,u32 size,dvdcallbacklow cb) +{ + u32 val; + struct timespec tb; + + __dvd_callback = cb; + __dvd_stopnextint = 0; + + val = 0; + if(enable) { + val |= 0x00010000; + if(!size) val |= 0x0000000a; + } + + _diReg[2] = DVD_AUDIOCONFIG|val; + _diReg[7] = DVD_DI_START; + + tb.tv_sec = 10; + tb.tv_nsec = 0; + __SetupTimeoutAlarm(&tb); + + return 1; +} + +s32 DVD_LowRequestAudioStatus(u32 subcmd,dvdcallbacklow cb) +{ + struct timespec tb; + + __dvd_callback = cb; + __dvd_stopnextint = 0; + + _diReg[2] = DVD_AUDIOSTATUS|subcmd; + _diReg[7] = DVD_DI_START; + + tb.tv_sec = 10; + tb.tv_nsec = 0; + __SetupTimeoutAlarm(&tb); + + return 1; +} + +//special, only used in bios replacement. therefor only there extern'd +s32 __DVDAudioBufferConfig(dvdcmdblk *block,u32 enable,u32 size,dvdcbcallback cb) +{ + if(!block) return 0; + + block->cmd = 0x000d; + block->offset = enable; + block->len = size; + block->cb = cb; + + return __issuecommand(2,block); +} + +s32 DVD_ReadDiskID(dvdcmdblk *block,dvddiskid *id,dvdcbcallback cb) +{ + if(!block || !id) return 0; + + block->cmd = 0x0005; + block->buf = id; + block->len = DVD_DISKIDSIZE; + block->offset = 0; + block->txdsize = 0; + block->cb = cb; + + return __issuecommand(2,block); +} + +s32 DVD_ReadAbsAsyncPrio(dvdcmdblk *block,void *buf,u32 len,s64 offset,dvdcbcallback cb,s32 prio) +{ + block->cmd = 0x0001; + block->buf = buf; + block->len = len; + block->offset = offset; + block->txdsize = 0; + block->cb = cb; + + return __issuecommand(prio,block); +} + +s32 DVD_ReadAbsAsyncForBS(dvdcmdblk *block,void *buf,u32 len,s64 offset,dvdcbcallback cb) +{ + block->cmd = 0x0004; + block->buf = buf; + block->len = len; + block->offset = offset; + block->txdsize = 0; + block->cb = cb; + + return __issuecommand(2,block); +} + +s32 DVD_SeekAbsAsyncPrio(dvdcmdblk *block,s64 offset,dvdcbcallback cb,s32 prio) +{ + block->cmd = 0x0002; + block->offset = offset; + block->cb = cb; + + return __issuecommand(prio,block); +} + +s32 DVD_InquiryAsync(dvdcmdblk *block,dvddrvinfo *info,dvdcbcallback cb) +{ + block->cmd = 0x000e; + block->buf = info; + block->len = 0x20; + block->txdsize = 0; + block->cb = cb; + return __issuecommand(2,block); +} + +s32 DVD_Inquiry(dvdcmdblk *block,dvddrvinfo *info) +{ + u32 level; + s32 state,ret; + ret = DVD_InquiryAsync(block,info,__dvd_inquirysynccb); + if(!ret) return -1; + + _CPU_ISR_Disable(level); + do { + state = block->state; + if(state==0) ret = block->txdsize; + else if(state==-1) ret = -1; + else if(state==10) ret = -3; + else LWP_ThreadSleep(__dvd_wait_queue); + } while(state!=0 && state!=-1 && state!=10); + _CPU_ISR_Restore(level); + return ret; +} + +s32 DVD_ReadPrio(dvdcmdblk *block,void *buf,u32 len,s64 offset,s32 prio) +{ + u32 level; + s32 state,ret; + if(offset>=0 && offset<8511160320LL) { + ret = DVD_ReadAbsAsyncPrio(block,buf,len,offset,__dvd_readsynccb,prio); + if(!ret) return -1; + + _CPU_ISR_Disable(level); + do { + state = block->state; + if(state==0) ret = block->txdsize; + else if(state==-1) ret = -1; + else if(state==10) ret = -3; + else LWP_ThreadSleep(__dvd_wait_queue); + } while(state!=0 && state!=-1 && state!=10); + _CPU_ISR_Restore(level); + return ret; + } + return -1; +} + +s32 DVD_SeekPrio(dvdcmdblk *block,s64 offset,s32 prio) +{ + u32 level; + s32 state,ret; + if(offset>0 && offset<8511160320LL) { + ret = DVD_SeekAbsAsyncPrio(block,offset,__dvd_seeksynccb,prio); + if(!ret) return -1; + + _CPU_ISR_Disable(level); + do { + state = block->state; + if(state==0) ret = 0; + else if(state==-1) ret = -1; + else if(state==10) ret = -3; + else LWP_ThreadSleep(__dvd_wait_queue); + } while(state!=0 && state!=-1 && state!=10); + _CPU_ISR_Restore(level); + + return ret; + } + return -1; +} + +s32 DVD_CancelAllAsync(dvdcbcallback cb) +{ + u32 level; + _CPU_ISR_Disable(level); + DVD_Pause(); + _CPU_ISR_Restore(level); + return 1; +} + +s32 DVD_StopStreamAtEndAsync(dvdcmdblk *block,dvdcbcallback cb) +{ + block->cmd = 0x08; + block->cb = cb; + return __issuecommand(1,block); +} + +s32 DVD_StopStreamAtEnd(dvdcmdblk *block) +{ + s32 ret,state; + u32 level; + ret = DVD_StopStreamAtEndAsync(block,__dvd_streamatendsynccb); + if(!ret) return -1; + + _CPU_ISR_Disable(level); + do { + state = block->state; + if(state==0 || state==-1) ret = -1; + else if(state==10) ret = block->txdsize; + else LWP_ThreadSleep(__dvd_wait_queue); + } while(state!=0 && state!=-1 && state!=10); + _CPU_ISR_Restore(level); + + return ret; +} + +s32 DVD_SpinUpDriveAsync(dvdcmdblk *block,dvdcbcallback cb) +{ + DVD_Reset(DVD_RESETNONE); + + block->cmd = 0x10; + block->cb = cb; + return __issuecommand(1,block); +} + +s32 DVD_SpinUpDrive(dvdcmdblk *block) +{ + s32 ret,state; + u32 level; + ret = DVD_SpinUpDriveAsync(block,__dvd_spinupdrivesynccb); + if(!ret) return -1; + + _CPU_ISR_Disable(level); + do { + state = block->state; + if(state==0 || state==-1) ret = -1; + else if(state==10) ret = block->txdsize; + else LWP_ThreadSleep(__dvd_wait_queue); + } while(state!=0 && state!=-1 && state!=10); + _CPU_ISR_Restore(level); + + return ret; +} + +s32 DVD_ControlDriveAsync(dvdcmdblk *block,u32 cmd,dvdcbcallback cb) +{ + block->cmd = 0x11; + block->cb = cb; + block->offset = cmd; + return __issuecommand(1,block); +} + +s32 DVD_ControlDrive(dvdcmdblk *block,u32 cmd) +{ + s32 ret,state; + u32 level; + ret = DVD_ControlDriveAsync(block,cmd,__dvd_motorcntrlsynccb); + + _CPU_ISR_Disable(level); + do { + state = block->state; + if(state==0 || state==-1) ret = -1; + else if(state==10) ret = block->txdsize; + else LWP_ThreadSleep(__dvd_wait_queue); + } while(state!=0 && state!=-1 && state!=10); + _CPU_ISR_Restore(level); + + return ret; +} + +s32 DVD_SetGCMOffsetAsync(dvdcmdblk *block,s64 offset,dvdcbcallback cb) +{ + block->cmd = 0x12; + block->cb = cb; + block->offset = offset; + return __issuecommand(1,block); +} + +s32 DVD_SetGCMOffset(dvdcmdblk *block,s64 offset) +{ + s32 ret,state; + u32 level; + ret = DVD_SetGCMOffsetAsync(block,offset,__dvd_setgcmsynccb); + + _CPU_ISR_Disable(level); + do { + state = block->state; + if(state==0 || state==-1) ret = -1; + else if(state==10) ret = block->txdsize; + else LWP_ThreadSleep(__dvd_wait_queue); + } while(state!=0 && state!=-1 && state!=10); + _CPU_ISR_Restore(level); + + return ret; +} + +s32 DVD_GetCmdBlockStatus(dvdcmdblk *block) +{ + u32 level; + s32 ret = -1; + + _CPU_ISR_Disable(level); + if(block) { + if((ret=block->state)==0x0003) ret = 1; + } + _CPU_ISR_Restore(level); + return ret; +} + +s32 DVD_GetDriveStatus() +{ + s32 ret; + u32 level; + + _CPU_ISR_Disable(level); + if(__dvd_fatalerror) ret = -1; + else { + if(__dvd_pausingflag) ret = 8; + else { + if(!__dvd_executing || __dvd_executing==&__dvd_dummycmdblk) ret = 0; + else ret = DVD_GetCmdBlockStatus(__dvd_executing); + } + } + _CPU_ISR_Restore(level); + return ret; +} + +void DVD_Pause() +{ + u32 level; + + _CPU_ISR_Disable(level); + __dvd_pauseflag = 1; + if(__dvd_executing==NULL) __dvd_pausingflag = 1; + _CPU_ISR_Restore(level); +} + +void DVD_Reset(u32 reset_mode) +{ + __dvd_drivestate &= ~(DVD_INTEROPER|DVD_CHIPPRESENT|DVD_DRIVERESET); + + if(reset_mode!=DVD_RESETNONE) + DVD_LowReset(reset_mode); + + _diReg[0] = (DVD_DE_MSK|DVD_TC_MSK|DVD_BRK_MSK); + _diReg[1] = _diReg[1]; + + __dvd_resetrequired = 0; + __dvd_internalretries = 0; +} + +void callback(s32 result,dvdcmdblk *block) +{ + if(result==0x00) { + DVD_ReadDiskID(block,&__dvd_tmpid0,callback); + return; + } + else if(result>=DVD_DISKIDSIZE) { + memcpy(__dvd_diskID,&__dvd_tmpid0,DVD_DISKIDSIZE); + } else if(result==-4) { + DVD_SpinUpDriveAsync(block,callback); + return; + } + if(__dvd_mountusrcb) __dvd_mountusrcb(result,block); +} + +s32 DVD_MountAsync(dvdcmdblk *block,dvdcbcallback cb) +{ + __dvd_mountusrcb = cb; + DVD_Reset(DVD_RESETHARD); + udelay(1150*1000); + return DVD_SpinUpDriveAsync(block,callback); +} + +s32 DVD_Mount() +{ + s32 ret = 0; + s32 state; + u32 level; + + ret = DVD_MountAsync(&__dvd_block$15,__dvd_mountsynccb); + if(!ret) return -1; + + _CPU_ISR_Disable(level); + do { + state = __dvd_block$15.state; + if(state==0) ret = 0; + else if(state==-1) ret = -1; + else if(state==10) ret = -3; + else LWP_ThreadSleep(__dvd_wait_queue); + } while(state!=0 && state!=-1 && state!=10); + __dvd_mountusrcb = NULL; //set to zero coz this is only used to sync for this function. + _CPU_ISR_Restore(level); + + return ret; +} + +dvddiskid* DVD_GetCurrentDiskID() +{ + return __dvd_diskID; +} + +dvddrvinfo* DVD_GetDriveInfo() +{ + return &__dvd_driveinfo; +} + +void DVD_Init() +{ + if(!__dvd_initflag) { + __dvd_initflag = 1; + __dvd_clearwaitingqueue(); + __DVDInitWA(); + + IRQ_Request(IRQ_PI_DI,__DVDInterruptHandler,NULL); + __UnmaskIrq(IRQMASK(IRQ_PI_DI)); + + SYS_CreateAlarm(&__dvd_timeoutalarm); + LWP_InitQueue(&__dvd_wait_queue); + } +} + +u32 DVD_SetAutoInvalidation(u32 auto_inv) +{ + u32 ret = __dvd_autoinvalidation; + __dvd_autoinvalidation= auto_inv; + return ret; +} + +static bool dvdio_Startup() +{ + DVD_Init(); + + if (mfpvr() == 0x00083214) // GameCube + { + DVD_Mount(); + } + else + { + DVD_Reset(DVD_RESETHARD); + DVD_ReadDiskID(&__dvd_block$15, &__dvd_tmpid0, callback); + } + return true; +} + +static bool dvdio_IsInserted() +{ + u32 status = 0; + DVD_LowGetStatus(&status, NULL); + + if(DVD_STATUS(status) == DVD_STATUS_READY) + return true; + + return false; +} + +static bool dvdio_ReadSectors(sec_t sector,sec_t numSectors,void *buffer) +{ + dvdcmdblk blk; + + if(DVD_ReadPrio(&blk, buffer, numSectors<<11, sector << 11, 2) <= 0) + return false; + + return true; +} + +static bool dvdio_WriteSectors(sec_t sector,sec_t numSectors,const void *buffer) +{ + return true; +} + +static bool dvdio_ClearStatus() +{ + return true; +} + +static bool dvdio_Shutdown() +{ + return true; +} + +const DISC_INTERFACE __io_gcdvd = { + DEVICE_TYPE_GAMECUBE_DVD, + FEATURE_MEDIUM_CANREAD | FEATURE_GAMECUBE_DVD, + (FN_MEDIUM_STARTUP)&dvdio_Startup, + (FN_MEDIUM_ISINSERTED)&dvdio_IsInserted, + (FN_MEDIUM_READSECTORS)&dvdio_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&dvdio_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&dvdio_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&dvdio_Shutdown +}; diff --git a/wii/libogc/libogc/es.c b/wii/libogc/libogc/es.c new file mode 100644 index 0000000000..2a9f1330ee --- /dev/null +++ b/wii/libogc/libogc/es.c @@ -0,0 +1,1091 @@ +/*------------------------------------------------------------- + +es.c -- ETicket services + +Copyright (C) 2008 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) +Hector Martin (marcan) +Andre Heider (dhewg) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#if defined(HW_RVL) + +#include +#include +#include +#include +#include +#include +#include +#include "ipc.h" +#include "asm.h" +#include "processor.h" +#include "mutex.h" +#include "es.h" + +#define IOCTL_ES_ADDTICKET 0x01 +#define IOCTL_ES_ADDTITLESTART 0x02 +#define IOCTL_ES_ADDCONTENTSTART 0x03 +#define IOCTL_ES_ADDCONTENTDATA 0x04 +#define IOCTL_ES_ADDCONTENTFINISH 0x05 +#define IOCTL_ES_ADDTITLEFINISH 0x06 +#define IOCTL_ES_GETDEVICEID 0x07 +#define IOCTL_ES_LAUNCH 0x08 +#define IOCTL_ES_OPENCONTENT 0x09 +#define IOCTL_ES_READCONTENT 0x0A +#define IOCTL_ES_CLOSECONTENT 0x0B +#define IOCTL_ES_GETOWNEDTITLECNT 0x0C +#define IOCTL_ES_GETOWNEDTITLES 0x0D +#define IOCTL_ES_GETTITLECNT 0x0E +#define IOCTL_ES_GETTITLES 0x0F +#define IOCTL_ES_GETTITLECONTENTSCNT 0x10 +#define IOCTL_ES_GETTITLECONTENTS 0x11 +#define IOCTL_ES_GETVIEWCNT 0x12 +#define IOCTL_ES_GETVIEWS 0x13 +#define IOCTL_ES_GETTMDVIEWCNT 0x14 +#define IOCTL_ES_GETTMDVIEWS 0x15 +//#define IOCTL_ES_GETCONSUMPTION 0x16 +#define IOCTL_ES_DELETETITLE 0x17 +#define IOCTL_ES_DELETETICKET 0x18 +//#define IOCTL_ES_DIGETTMDVIEWSIZE 0x19 +//#define IOCTL_ES_DIGETTMDVIEW 0x1A +//#define IOCTL_ES_DIGETTICKETVIEW 0x1B +#define IOCTL_ES_DIVERIFY 0x1C +#define IOCTL_ES_GETTITLEDIR 0x1D +#define IOCTL_ES_GETDEVICECERT 0x1E +#define IOCTL_ES_IMPORTBOOT 0x1F +#define IOCTL_ES_GETTITLEID 0x20 +#define IOCTL_ES_SETUID 0x21 +#define IOCTL_ES_DELETETITLECONTENT 0x22 +#define IOCTL_ES_SEEKCONTENT 0x23 +#define IOCTL_ES_OPENTITLECONTENT 0x24 +//#define IOCTL_ES_LAUNCHBC 0x25 +//#define IOCTL_ES_EXPORTTITLEINIT 0x26 +//#define IOCTL_ES_EXPORTCONTENTBEGIN 0x27 +//#define IOCTL_ES_EXPORTCONTENTDATA 0x28 +//#define IOCTL_ES_EXPORTCONTENTEND 0x29 +//#define IOCTL_ES_EXPORTTITLEDONE 0x2A +#define IOCTL_ES_ADDTMD 0x2B +#define IOCTL_ES_ENCRYPT 0x2C +#define IOCTL_ES_DECRYPT 0x2D +#define IOCTL_ES_GETBOOT2VERSION 0x2E +#define IOCTL_ES_ADDTITLECANCEL 0x2F +#define IOCTL_ES_SIGN 0x30 +//#define IOCTL_ES_VERIFYSIGN 0x31 +#define IOCTL_ES_GETSTOREDCONTENTCNT 0x32 +#define IOCTL_ES_GETSTOREDCONTENTS 0x33 +#define IOCTL_ES_GETSTOREDTMDSIZE 0x34 +#define IOCTL_ES_GETSTOREDTMD 0x35 +#define IOCTL_ES_GETSHAREDCONTENTCNT 0x36 +#define IOCTL_ES_GETSHAREDCONTENTS 0x37 + + +#define ES_HEAP_SIZE 0x800 + +#define ISALIGNED(x) ((((u32)x)&0x1F)==0) + +static char __es_fs[] ATTRIBUTE_ALIGN(32) = "/dev/es"; + +static s32 __es_fd = -1; +static s32 __es_hid = -1; + +static void __ES_InitFS(void); +static void __ES_DeinitFS(void); + +s32 __ES_Init(void) +{ + s32 ret = 0; + + if(__es_hid < 0 ) { + __es_hid = iosCreateHeap(ES_HEAP_SIZE); + if(__es_hid < 0) + return __es_hid; + } + + if (__es_fd < 0) { + ret = IOS_Open(__es_fs,0); + if(ret<0) + return ret; + __es_fd = ret; + } + + + __ES_InitFS(); + + return 0; +} + +s32 __ES_Close(void) +{ + s32 ret; + + if(__es_fd < 0) return 0; + + __ES_DeinitFS(); + + ret = IOS_Close(__es_fd); + + __es_fd = -1; + + if(ret<0) + return ret; + + return 0; +} + +// Used by ios.c after reloading IOS +s32 __ES_Reset(void) +{ + __es_fd = -1; + return 0; +} + +s32 ES_GetTitleID(u64 *titleID) +{ + s32 ret; + u64 title; + + if(__es_fd<0) return ES_ENOTINIT; + if(!titleID) return ES_EINVAL; + + ret = IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETTITLEID,":q",&title); + if(ret<0) return ret; + + *titleID = title; + return ret; +} + +s32 ES_SetUID(u64 uid) +{ + if(__es_fd<0) return ES_ENOTINIT; + + return IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_SETUID,"q:",uid); +} + +s32 ES_GetDataDir(u64 titleID,char *filepath) +{ + s32 ret; + + if(__es_fd<0) return ES_ENOTINIT; + if(!filepath) return ES_EINVAL; + if(!ISALIGNED(filepath)) return ES_EALIGN; + + ret = IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETTITLEDIR,"q:d",titleID,filepath,30); + + return ret; +} + +s32 ES_LaunchTitle(u64 titleID, const tikview *view) +{ + + s32 res; + STACK_ALIGN(u64,title,1,32); + STACK_ALIGN(ioctlv,vectors,2,32); + + if(__es_fd<0) return ES_ENOTINIT; + if(!view) return ES_EINVAL; + if(!ISALIGNED(view)) return ES_EALIGN; + + *title = titleID; + vectors[0].data = (void*)title; + vectors[0].len = sizeof(u64); + vectors[1].data = (void*)view; + vectors[1].len = sizeof(tikview); + res = IOS_IoctlvReboot(__es_fd,IOCTL_ES_LAUNCH,2,0,vectors); + + return res; +} + +s32 ES_LaunchTitleBackground(u64 titleID, const tikview *view) +{ + + s32 res; + STACK_ALIGN(u64,title,1,32); + STACK_ALIGN(ioctlv,vectors,2,32); + + if(__es_fd<0) return ES_ENOTINIT; + if(!view) return ES_EINVAL; + if(!ISALIGNED(view)) return ES_EALIGN; + + *title = titleID; + vectors[0].data = (void*)title; + vectors[0].len = sizeof(u64); + vectors[1].data = (void*)view; + vectors[1].len = sizeof(tikview); + res = IOS_IoctlvRebootBackground(__es_fd,IOCTL_ES_LAUNCH,2,0,vectors); + + return res; +} + +s32 ES_GetNumTicketViews(u64 titleID, u32 *cnt) +{ + s32 ret; + u32 cntviews; + + if(__es_fd<0) return ES_ENOTINIT; + if(!cnt) return ES_EINVAL; + + ret = IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETVIEWCNT,"q:i",titleID,&cntviews); + + if(ret<0) return ret; + *cnt = cntviews; + return ret; +} + +s32 ES_GetTicketViews(u64 titleID, tikview *views, u32 cnt) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(cnt <= 0) return ES_EINVAL; + if(!views) return ES_EINVAL; + if(!ISALIGNED(views)) return ES_EALIGN; + + return IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETVIEWS,"qi:d",titleID,cnt,views,sizeof(tikview)*cnt); +} + +s32 ES_GetNumOwnedTitles(u32 *cnt) +{ + s32 ret; + u32 cnttitles; + + if(__es_fd<0) return ES_ENOTINIT; + if(cnt == NULL) return ES_EINVAL; + + ret = IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETOWNEDTITLECNT,":i",&cnttitles); + + if(ret<0) return ret; + *cnt = cnttitles; + return ret; +} + +s32 ES_GetOwnedTitles(u64 *titles, u32 cnt) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(cnt <= 0) return ES_EINVAL; + if(!titles) return ES_EINVAL; + if(!ISALIGNED(titles)) return ES_EALIGN; + + return IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETOWNEDTITLES,"i:d",cnt,titles,sizeof(u64)*cnt); +} + +s32 ES_GetNumTitles(u32 *cnt) +{ + s32 ret; + u32 cnttitles; + + if(__es_fd<0) return ES_ENOTINIT; + if(cnt == NULL) return ES_EINVAL; + + ret = IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETTITLECNT,":i",&cnttitles); + + if(ret<0) return ret; + *cnt = cnttitles; + return ret; +} + +s32 ES_GetTitles(u64 *titles, u32 cnt) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(cnt <= 0) return ES_EINVAL; + if(!titles) return ES_EINVAL; + if(!ISALIGNED(titles)) return ES_EALIGN; + + return IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETTITLES,"i:d",cnt,titles,sizeof(u64)*cnt); +} + +s32 ES_GetNumStoredTMDContents(const signed_blob *stmd, u32 tmd_size, u32 *cnt) +{ + s32 ret; + u32 cntct; + + if(__es_fd<0) return ES_ENOTINIT; + if(!cnt) return ES_EINVAL; + if(!stmd || !IS_VALID_SIGNATURE(stmd)) return ES_EINVAL; + if(!ISALIGNED(stmd)) return ES_EALIGN; + + ret = IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETSTOREDCONTENTCNT,"d:i",stmd,tmd_size,&cntct); + + if(ret<0) return ret; + *cnt = cntct; + return ret; +} + +s32 ES_GetStoredTMDContents(const signed_blob *stmd, u32 tmd_size, u32 *contents, u32 cnt) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(cnt <= 0) return ES_EINVAL; + if(!contents) return ES_EINVAL; + if(!stmd || !IS_VALID_SIGNATURE(stmd)) return ES_EINVAL; + if(!ISALIGNED(stmd)) return ES_EALIGN; + if(!ISALIGNED(contents)) return ES_EALIGN; + if(tmd_size > MAX_SIGNED_TMD_SIZE) return ES_EINVAL; + + return IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETSTOREDCONTENTS,"di:d",stmd,tmd_size,cnt,contents,sizeof(u32)*cnt); +} + +s32 ES_GetTitleContentsCount(u64 titleID, u32 *num) +{ + s32 ret; + u32 _num; + + if(__es_fd<0) return ES_ENOTINIT; + if(!num) return ES_EINVAL; + + ret = IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETTITLECONTENTSCNT,"q:i",titleID,&_num); + + if(ret<0) return ret; + *num = _num; + return ret; +} + +s32 ES_GetTitleContents(u64 titleID, u8 *data, u32 size) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(size <= 0) return ES_EINVAL; + if(!data) return ES_EINVAL; + if(!ISALIGNED(data)) return ES_EALIGN; + + return IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETSTOREDTMD,"qi:d",titleID,size,data,size); +} + +s32 ES_GetTMDViewSize(u64 titleID, u32 *size) +{ + s32 ret; + u32 tmdsize; + + if(__es_fd<0) return ES_ENOTINIT; + if(!size) return ES_EINVAL; + + ret = IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETTMDVIEWCNT,"q:i",titleID,&tmdsize); + + if(ret<0) return ret; + *size = tmdsize; + return ret; +} + +s32 ES_GetTMDView(u64 titleID, u8 *data, u32 size) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(size <= 0) return ES_EINVAL; + if(!data) return ES_EINVAL; + if(!ISALIGNED(data)) return ES_EALIGN; + + return IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETTMDVIEWS,"qi:d",titleID,size,data,size); +} + +s32 ES_GetStoredTMDSize(u64 titleID, u32 *size) +{ + s32 ret; + u32 tmdsize; + + if(__es_fd<0) return ES_ENOTINIT; + if(!size) return ES_EINVAL; + + ret = IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETSTOREDTMDSIZE,"q:i",titleID,&tmdsize); + + if(ret<0) return ret; + *size = tmdsize; + return ret; +} + +s32 ES_GetStoredTMD(u64 titleID, signed_blob *stmd, u32 size) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(size <= 0) return ES_EINVAL; + if(!stmd) return ES_EINVAL; + if(!ISALIGNED(stmd)) return ES_EALIGN; + if(size > MAX_SIGNED_TMD_SIZE) return ES_EINVAL; + + return IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETSTOREDTMD,"qi:d",titleID,size,stmd,size); +} + +s32 ES_GetNumSharedContents(u32 *cnt) +{ + s32 ret; + u32 cntct; + + if(__es_fd<0) return ES_ENOTINIT; + if(!cnt) return ES_EINVAL; + + ret = IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETSHAREDCONTENTCNT,":i",&cntct); + + if(ret<0) return ret; + *cnt = cntct; + return ret; +} + +s32 ES_GetSharedContents(sha1 *contents, u32 cnt) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(cnt <= 0) return ES_EINVAL; + if(!contents) return ES_EINVAL; + if(!ISALIGNED(contents)) return ES_EALIGN; + + return IOS_IoctlvFormat(__es_hid,__es_fd,IOCTL_ES_GETSHAREDCONTENTS,"i:d",cnt,contents,sizeof(sha1)*cnt); +} + +signed_blob *ES_NextCert(const signed_blob *certs) +{ + cert_header *cert; + if(!SIGNATURE_SIZE(certs)) return NULL; + cert = SIGNATURE_PAYLOAD(certs); + if(!CERTIFICATE_SIZE(cert)) return NULL; + return (signed_blob*)(((u8*)cert) + CERTIFICATE_SIZE(cert)); +} + +s32 __ES_sanity_check_certlist(const signed_blob *certs, u32 certsize) +{ + int count = 0; + signed_blob *end; + + if(!certs || !certsize) return 0; + + end = (signed_blob*)(((u8*)certs) + certsize); + while(certs != end) { + certs = ES_NextCert(certs); + if(!certs) return 0; + count++; + } + return count; +} + +s32 ES_Identify(const signed_blob *certificates, u32 certificates_size, const signed_blob *stmd, u32 tmd_size, const signed_blob *sticket, u32 ticket_size, u32 *keyid) +{ + + tmd *p_tmd; + u8 *hashes; + s32 ret; + u32 *keyid_buf; + + if(__es_fd<0) return ES_ENOTINIT; + + if(ticket_size != STD_SIGNED_TIK_SIZE) return ES_EINVAL; + if(!stmd || !tmd_size || !IS_VALID_SIGNATURE(stmd)) return ES_EINVAL; + if(!sticket || !IS_VALID_SIGNATURE(sticket)) return ES_EINVAL; + if(!__ES_sanity_check_certlist(certificates, certificates_size)) return ES_EINVAL; + if(!ISALIGNED(certificates)) return ES_EALIGN; + if(!ISALIGNED(stmd)) return ES_EALIGN; + if(!ISALIGNED(sticket)) return ES_EALIGN; + if(tmd_size > MAX_SIGNED_TMD_SIZE) return ES_EINVAL; + + p_tmd = SIGNATURE_PAYLOAD(stmd); + + if(!(keyid_buf = iosAlloc(__es_hid, 4))) return ES_ENOMEM; + if(!(hashes = iosAlloc(__es_hid, p_tmd->num_contents*20))) { + iosFree(__es_hid, keyid_buf); + return ES_ENOMEM; + } + + ret = IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_DIVERIFY, "dddd:id", certificates, certificates_size, 0, 0, sticket, ticket_size, stmd, tmd_size, keyid_buf, hashes, p_tmd->num_contents*20); + if(ret >= 0 && keyid) *keyid = *keyid_buf; + + iosFree(__es_hid, keyid_buf); + iosFree(__es_hid, hashes); + + if(ret >= 0) { + __ES_Close(); + __ES_Init(); + } + + return ret; +} + +s32 ES_AddTicket(const signed_blob *stik, u32 stik_size, const signed_blob *certificates, u32 certificates_size, const signed_blob *crl, u32 crl_size) +{ + s32 ret; + + if(__es_fd<0) return ES_ENOTINIT; + if(stik_size != STD_SIGNED_TIK_SIZE) return ES_EINVAL; + if(!stik || !IS_VALID_SIGNATURE(stik)) return ES_EINVAL; + if(crl_size && (!crl || !IS_VALID_SIGNATURE(crl))) return ES_EINVAL; + if(!__ES_sanity_check_certlist(certificates, certificates_size)) return ES_EINVAL; + if(!certificates || !ISALIGNED(certificates)) return ES_EALIGN; + if(!ISALIGNED(stik)) return ES_EALIGN; + if(!ISALIGNED(certificates)) return ES_EALIGN; + if(!ISALIGNED(crl)) return ES_EALIGN; + + if(!crl_size) crl=NULL; + + ret = IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_ADDTICKET, "ddd:", stik, stik_size, certificates, certificates_size, crl, crl_size); + return ret; + +} + +s32 ES_DeleteTicket(const tikview *view) +{ + s32 ret; + + if(__es_fd<0) return ES_ENOTINIT; + if(!view) return ES_EINVAL; + if(!ISALIGNED(view)) return ES_EALIGN; + ret = IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_DELETETICKET, "d:", view, sizeof(tikview)); + return ret; +} + +s32 ES_AddTitleTMD(const signed_blob *stmd, u32 stmd_size) +{ + s32 ret; + + if(__es_fd<0) return ES_ENOTINIT; + if(!stmd || !IS_VALID_SIGNATURE(stmd)) return ES_EINVAL; + if(stmd_size != SIGNED_TMD_SIZE(stmd)) return ES_EINVAL; + if(!ISALIGNED(stmd)) return ES_EALIGN; + + ret = IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_ADDTMD, "d:", stmd, stmd_size); + return ret; + +} + +s32 ES_AddTitleStart(const signed_blob *stmd, u32 tmd_size, const signed_blob *certificates, u32 certificates_size, const signed_blob *crl, u32 crl_size) +{ + s32 ret; + + if(__es_fd<0) return ES_ENOTINIT; + if(!stmd || !IS_VALID_SIGNATURE(stmd)) return ES_EINVAL; + if(tmd_size != SIGNED_TMD_SIZE(stmd)) return ES_EINVAL; + if(crl_size && (!crl || !IS_VALID_SIGNATURE(crl))) return ES_EINVAL; + if(!__ES_sanity_check_certlist(certificates, certificates_size)) return ES_EINVAL; + if(!certificates || !ISALIGNED(certificates)) return ES_EALIGN; + if(!ISALIGNED(stmd)) return ES_EALIGN; + if(!ISALIGNED(certificates)) return ES_EALIGN; + if(!ISALIGNED(crl)) return ES_EALIGN; + + if(!crl_size) crl=NULL; + + ret = IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_ADDTITLESTART, "dddi:", stmd, tmd_size, certificates, certificates_size, crl, crl_size, 1); + return ret; +} + +s32 ES_AddContentStart(u64 titleID, u32 cid) +{ + if(__es_fd<0) return ES_ENOTINIT; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_ADDCONTENTSTART, "qi:", titleID, cid); +} + +s32 ES_AddContentData(s32 cfd, u8 *data, u32 data_size) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(cfd<0) return ES_EINVAL; + if(!data || !data_size) return ES_EINVAL; + if(!ISALIGNED(data)) return ES_EALIGN; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_ADDCONTENTDATA, "id:", cfd, data, data_size); +} + +s32 ES_AddContentFinish(u32 cid) +{ + if(__es_fd<0) return ES_ENOTINIT; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_ADDCONTENTFINISH, "i:", cid); +} + +s32 ES_AddTitleFinish(void) +{ + if(__es_fd<0) return ES_ENOTINIT; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_ADDTITLEFINISH, ""); +} + +s32 ES_AddTitleCancel(void) +{ + if(__es_fd<0) return ES_ENOTINIT; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_ADDTITLECANCEL, ""); +} + +s32 ES_ImportBoot(const signed_blob *tik, u32 tik_size,const signed_blob *tik_certs,u32 tik_certs_size,const signed_blob *tmd,u32 tmd_size,const signed_blob *tmd_certs,u32 tmd_certs_size,const u8 *content,u32 content_size) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(!tik || !tik_size) return ES_EINVAL; + if(!tik_certs || !tik_certs_size) return ES_EINVAL; + if(!tmd || !tmd_size) return ES_EINVAL; + if(!tmd_certs || !tmd_certs_size) return ES_EINVAL; + if(!content || !content_size) return ES_EINVAL; + if(!ISALIGNED(tik)) return ES_EALIGN; + if(!ISALIGNED(tmd)) return ES_EALIGN; + if(!ISALIGNED(tik_certs)) return ES_EALIGN; + if(!ISALIGNED(tmd_certs)) return ES_EALIGN; + if(!ISALIGNED(content)) return ES_EALIGN; + + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_IMPORTBOOT, "dddddd:", tik, tik_size, tik_certs, tik_certs_size, tmd, tmd_size, tmd_certs, tmd_certs_size, NULL, 0, content, content_size); +} + +s32 ES_OpenContent(u16 index) +{ + if(__es_fd<0) return ES_ENOTINIT; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_OPENCONTENT, "i:", index); +} + +s32 ES_OpenTitleContent(u64 titleID, tikview *views, u16 index) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(!ISALIGNED(views)) return ES_EALIGN; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_OPENTITLECONTENT, "qdi:", titleID, views, sizeof(tikview), index); +} + +s32 ES_ReadContent(s32 cfd, u8 *data, u32 data_size) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(cfd<0) return ES_EINVAL; + if(!data || !data_size) return ES_EINVAL; + if(!ISALIGNED(data)) return ES_EALIGN; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_READCONTENT, "i:d", cfd, data, data_size); +} + +s32 ES_SeekContent(s32 cfd, s32 where, s32 whence) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(cfd<0) return ES_EINVAL; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_SEEKCONTENT, "iii:", cfd, where, whence); +} + +s32 ES_CloseContent(s32 cfd) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(cfd<0) return ES_EINVAL; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_CLOSECONTENT, "i:", cfd); +} + +s32 ES_DeleteTitle(u64 titleID) +{ + if(__es_fd<0) return ES_ENOTINIT; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_DELETETITLE, "q:", titleID); +} + +s32 ES_DeleteTitleContent(u64 titleID) +{ + if(__es_fd<0) return ES_ENOTINIT; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_DELETETITLECONTENT, "q:", titleID); +} + +s32 ES_Encrypt(u32 keynum, u8 *iv, u8 *source, u32 size, u8 *dest) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(!iv || !source || !size || !dest) return ES_EINVAL; + if(!ISALIGNED(source) || !ISALIGNED(iv) || !ISALIGNED(dest)) return ES_EALIGN; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_ENCRYPT, "idd:dd", keynum, iv, 0x10, source, size, iv, 0x10, dest, size); +} + +s32 ES_Decrypt(u32 keynum, u8 *iv, u8 *source, u32 size, u8 *dest) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(!iv || !source || !size || !dest) return ES_EINVAL; + if(!ISALIGNED(source) || !ISALIGNED(iv) || !ISALIGNED(dest)) return ES_EALIGN; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_DECRYPT, "idd:dd", keynum, iv, 0x10, source, size, iv, 0x10, dest, size); +} + +s32 ES_Sign(u8 *source, u32 size, u8 *sig, u8 *certs) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(!source || !size || !sig || !certs) return ES_EINVAL; + if(!ISALIGNED(source) || !ISALIGNED(sig) || !ISALIGNED(certs)) return ES_EALIGN; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_SIGN, "d:dd", source, size, sig, 0x3C, certs, 0x180); +} + +s32 ES_GetDeviceCert(u8 *outbuf) +{ + if(__es_fd<0) return ES_ENOTINIT; + if(!outbuf) return ES_EINVAL; + if(!ISALIGNED(outbuf)) return ES_EALIGN; + return IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_GETDEVICECERT, ":d", outbuf, 0x180); +} + +s32 ES_GetDeviceID(u32 *device_id) +{ + s32 ret; + u32 _device_id = 0; + + if(__es_fd<0) return ES_ENOTINIT; + if(!device_id) return ES_EINVAL; + + ret = IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_GETDEVICEID, ":i", &_device_id); + if (ret>=0) *device_id = _device_id; + + return ret; +} + +s32 ES_GetBoot2Version(u32 *version) +{ + s32 ret; + u32 _version = 0; + + if(__es_fd<0) return ES_ENOTINIT; + if(!version) return ES_EINVAL; + + ret = IOS_IoctlvFormat(__es_hid, __es_fd, IOCTL_ES_GETBOOT2VERSION, ":i", &_version); + if (ret>=0) *version = _version; + + return ret; +} + +// 64k buffer size for alignment +#define ES_READ_BUF_SIZE 65536 + +typedef struct { + s32 cfd; + u64 titleID; + tmd_content content; + void *iobuf; + mutex_t mutex; +} es_fd; + +// valid path formats: +// format example description +// es:%08x es:00000004 Content index for current title ID +// es:ID%08x es:ID00000033 Content ID for current title ID +// es:%016llx/%08x es:0000000100000002/00000004 Content index for some title ID (not fully implemented yet) +// es:%016llx/ID%08x es:0000000100000002/ID00000033 Content ID for some title ID (not fully implemented yet) +// leading zeroes may be omitted, 0x prefix allowed. All numbers in hex. + +static s32 _ES_decodepath (struct _reent *r, const char *path, u64 *titleID, tmd_content *content) +{ + u64 _tid = 0; + u32 tmd_size; + STACK_ALIGN(u8, _tmdbuf, MAX_SIGNED_TMD_SIZE, 32); + signed_blob *_stmd; + tmd *_tmd; + tmd_content *_contents; + tmd_content _content; + int is_cid; + u32 arg; + char *bad; + int i; + u64 mytid; + + // check the device + if(strncmp(path,"es:",3)) { + r->_errno = EINVAL; + return -1; + } + path += 3; + + // Get our Title ID + if(ES_GetTitleID(&mytid) < 0) { + r->_errno = EIO; + return -1; + } + + // Read in Title ID, if this is an absolute path + if(path[0] == '/') { + path++; + if(!path[0]) { + r->_errno = EINVAL; + return -1; + } + r->_errno = 0; + _tid = _strtoull_r(r, path, &bad, 16); + if(r->_errno) return -1; + if((bad == path) || (bad[0] != '/')) { + r->_errno = EINVAL; + return -1; + } + path = bad + 1; + } else { + _tid = mytid; + } + + // check if path is a content ID + if(!strncmp(path,"ID",2)) { + path += 2; + is_cid = 1; + } else { + is_cid = 0; + } + + // read in the argument (content ID or content index) + r->_errno = 0; + arg = _strtoul_r(r, path, &bad, 16); + if(r->_errno) return -1; + if((bad == path) || (bad[0] != 0)) { + r->_errno = EINVAL; + return -1; + } + + // now get the TMD and find the content entry + if(ES_GetStoredTMDSize(_tid, &tmd_size) < 0) { + r->_errno = ENOENT; + return -1; + } + + _stmd = (signed_blob*)_tmdbuf; + if(ES_GetStoredTMD(_tid, _stmd, tmd_size) < 0) { + r->_errno = EIO; + return -1; + } + if(!IS_VALID_SIGNATURE(_stmd)) { + r->_errno = EIO; + return -1; + } + _tmd = SIGNATURE_PAYLOAD(_stmd); + _contents = TMD_CONTENTS(_tmd); + + for(i=0;i<_tmd->num_contents;i++) { + if(is_cid) { + if(_contents[i].cid == arg) { + _content = _contents[i]; + break; + } + } else { + if(_contents[i].index == arg) { + _content = _contents[i]; + break; + } + } + } + if(i >= _tmd->num_contents) { + r->_errno = ENOENT; + return -1; + } + + if(titleID) { + if(_tid == mytid) *titleID = 0; + else *titleID = _tid; + } + if(content) *content = _content; + return 0; +} + +static int _ES_open_r (struct _reent *r, void *fileStruct, const char *path, int flags, int mode) { + es_fd *file = (es_fd *) fileStruct; + + // decode the path + if(_ES_decodepath(r, path, &file->titleID, &file->content) < 0) { + return -1; + } + + // writing not supported + if ((flags & 0x03) != O_RDONLY) { + r->_errno = EROFS; + return -1; + } + + // open the content + if(file->titleID == 0) + file->cfd = ES_OpenContent(file->content.index); + else + { + u32 cnt ATTRIBUTE_ALIGN(32); + ES_GetNumTicketViews(file->titleID, &cnt); + tikview *views = (tikview *)memalign( 32, sizeof(tikview)*cnt ); + if(views == NULL) + { + return -1; + } + ES_GetTicketViews(file->titleID, views, cnt); + file->cfd = ES_OpenTitleContent(file->titleID, views, file->content.index); + free(views); + } + + if(file->cfd<0) { + r->_errno = EIO; + return -1; + } + + file->iobuf = NULL; + + LWP_MutexInit(&file->mutex, false); + + return (int)file; +} + +static int _ES_close_r (struct _reent *r, void *fd) { + es_fd *file = (es_fd *) fd; + + LWP_MutexLock(file->mutex); + + if(ES_CloseContent(file->cfd) < 0) { + r->_errno = EBADF; + return -1; + } + file->cfd = -1; + + if(file->iobuf) _free_r(r,file->iobuf); + + LWP_MutexUnlock(file->mutex); + LWP_MutexDestroy(file->mutex); + return 0; +} + +static int _ES_read_r (struct _reent *r, void *fd, char *ptr, size_t len) { + es_fd *file = (es_fd *) fd; + int read = 0; + int res; + + + LWP_MutexLock(file->mutex); + if(file->cfd < 0) { + LWP_MutexUnlock(file->mutex); + r->_errno = EBADF; + return -1; + } + + // if aligned, just pass through the read + if(ISALIGNED(ptr)) + { + res = ES_ReadContent(file->cfd, (u8*)ptr, len); + if(res < 0) { + LWP_MutexUnlock(file->mutex); + // we don't really know what the error codes mean... + r->_errno = EIO; + return -1; + } + read = res; + // otherwise read in blocks to an aligned buffer + } else { + int chunk; + if(!file->iobuf) { + file->iobuf = _memalign_r(r, 32, ES_READ_BUF_SIZE); + if(!file->iobuf) { + r->_errno = ENOMEM; + return -1; + } + } + while(len) { + if(len > ES_READ_BUF_SIZE) chunk = ES_READ_BUF_SIZE; + else chunk = len; + res = ES_ReadContent(file->cfd, file->iobuf, chunk); + if(res < 0) { + LWP_MutexUnlock(file->mutex); + // we don't really know what the error codes mean... + r->_errno = EIO; + return -1; + } + len -= res; + read += res; + memcpy(ptr, file->iobuf, res); + ptr += res; + if(res < chunk) break; + } + } + + LWP_MutexUnlock(file->mutex); + return read; +} + +static off_t _ES_seek_r (struct _reent *r, void *fd, off_t where, int whence) { + es_fd *file = (es_fd *) fd; + s32 res; + + LWP_MutexLock(file->mutex); + if(file->cfd < 0) { + LWP_MutexUnlock(file->mutex); + r->_errno = EBADF; + return -1; + } + + res = ES_SeekContent(file->cfd, where, whence); + LWP_MutexUnlock(file->mutex); + + if(res < 0) { + r->_errno = EINVAL; + return -1; + } + return res; +} + +static void _ES_fillstat(u64 titleID, tmd_content *content, struct stat *st) { + memset(st, 0, sizeof(*st)); + // the inode is the content ID + // (pretend each Title ID is a different filesystem) + st->st_ino = content->cid; + // st_dev is only a short, so do the best we can and use the middle two letters + // of the title ID if it's not a system content, otherwise use the low 16 bits + if((titleID>>32) == 1) + st->st_dev = titleID & 0xFFFF; + else + st->st_dev = (titleID>>8) & 0xFFFF; + + // not necessarily true due to shared contents, but + // we're not going to implement shared content scan here and now + st->st_nlink = 1; + // instead, give the file group read permissions if it's a shared content + st->st_mode = S_IFREG | S_IRUSR; + if(content->type & 0x8000) + st->st_mode |= S_IRGRP; + + // use st_dev as a uid too, see above + st->st_uid = st->st_dev; + // no group + st->st_gid = 0; + // content size + st->st_size = content->size; + st->st_blocks = (st->st_size + 511) / 512; + // NAND fs cluster size (not like anyone cares, but...) + st->st_blksize = 16384; +} + +static int _ES_fstat_r (struct _reent *r, void *fd, struct stat *st) { + es_fd *file = (es_fd *) fd; + + LWP_MutexLock(file->mutex); + if(file->cfd < 0) { + LWP_MutexUnlock(file->mutex); + r->_errno = EBADF; + return -1; + } + + _ES_fillstat(file->titleID, &file->content, st); + LWP_MutexUnlock(file->mutex); + + return 0; +} + +static int _ES_stat_r (struct _reent *r, const char *path, struct stat *st) { + tmd_content content; + u64 titleID; + + if(_ES_decodepath(r, path, &titleID, &content) < 0) { + return -1; + } + _ES_fillstat(titleID, &content, st); + return 0; +} + +static const devoptab_t dotab_es = { + "es", + sizeof (es_fd), + _ES_open_r, + _ES_close_r, + NULL, + _ES_read_r, + _ES_seek_r, + _ES_fstat_r, + _ES_stat_r, + NULL, + NULL, + NULL, + NULL, + NULL, + 0, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +static void __ES_InitFS(void) { + AddDevice(&dotab_es); +} + +static void __ES_DeinitFS(void) { + RemoveDevice("es:"); +} + +#endif /* defined(HW_RVL) */ diff --git a/wii/libogc/libogc/exception.c b/wii/libogc/libogc/exception.c new file mode 100644 index 0000000000..b901efa8d5 --- /dev/null +++ b/wii/libogc/libogc/exception.c @@ -0,0 +1,266 @@ +/*------------------------------------------------------------- + +exception.c -- PPC exception handling support + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#include +#include +#include +#include +#include "asm.h" +#include "processor.h" +#include "cache.h" +#include "irq.h" +#include "context.h" + +#include "system.h" + +#include "gx.h" +#include "pad.h" +#include "consol.h" +#include "console.h" +#include "lwp_threads.h" +#include "ios.h" + +#include "ogc/video_types.h" + +#define CPU_STACK_TRACE_DEPTH 10 + +typedef struct _framerec { + struct _framerec *up; + void *lr; +} frame_rec, *frame_rec_t; + +static void *exception_xfb = (void*)0xC1700000; //we use a static address above ArenaHi. +static int reload_timer = -1; + +void __exception_sethandler(u32 nExcept, void (*pHndl)(frame_context*)); + +extern void udelay(int us); +extern void fpu_exceptionhandler(); +extern void irq_exceptionhandler(); +extern void dec_exceptionhandler(); +extern void default_exceptionhandler(); +extern void VIDEO_SetFramebuffer(void *); +extern void __reload(); + +extern s8 exceptionhandler_start[],exceptionhandler_end[],exceptionhandler_patch[]; +extern s8 systemcallhandler_start[],systemcallhandler_end[]; + +void (*_exceptionhandlertable[NUM_EXCEPTIONS])(frame_context*); + +static u32 exception_location[NUM_EXCEPTIONS] = { + 0x00000100, 0x00000200, 0x00000300, 0x00000400, + 0x00000500, 0x00000600, 0x00000700, 0x00000800, + 0x00000900, 0x00000C00, 0x00000D00, 0x00000F00, + 0x00001300, 0x00001400, 0x00001700 }; + +static const char *exception_name[NUM_EXCEPTIONS] = { + "System Reset", "Machine Check", "DSI", "ISI", + "Interrupt", "Alignment", "Program", "Floating Point", + "Decrementer", "System Call", "Trace", "Performance", + "IABR", "Reserved", "Thermal"}; + +void __exception_load(u32 nExc,void *data,u32 len,void *patch) +{ + void *pAddr = (void*)(0x80000000|exception_location[nExc]); + memcpy(pAddr,data,len); + if(patch) + *(u32*)((u32)pAddr+(patch-data)) |= nExc; + + DCFlushRangeNoSync(pAddr,len); + ICInvalidateRange(pAddr,len); + _sync(); +} + +void __systemcall_init() +{ + __exception_load(EX_SYS_CALL,systemcallhandler_start,(systemcallhandler_end-systemcallhandler_start),NULL); +} + +void __exception_init() +{ + s32 i; + // init all exceptions with the default handler & vector code + for(i=0;iup;p=p->up,i++) { + if(i%4) kprintf(" --> "); + else { + if(i>0) kprintf(" -->\n\t"); + else kprintf("\n\t"); + } + + switch(i) { + case 0: + if(pc) kprintf("%p",pc); + break; + case 1: + if(!l) l = (frame_rec_t)mfspr(8); + kprintf("%p",(void*)l); + break; + default: + kprintf("%p",(void*)(p->up->lr)); + break; + } + } +} + +void __exception_setreload(int t) +{ + reload_timer = t*50; +} + +static void waitForReload() +{ + u32 level; + + PAD_Init(); + + if(reload_timer > 0) + kprintf("\n\tReloading in %d seconds\n", reload_timer/50); + + while ( 1 ) + { + PAD_ScanPads(); + + int buttonsDown = PAD_ButtonsDown(0); + + if( (buttonsDown & PAD_TRIGGER_Z) || SYS_ResetButtonDown() || + reload_timer == 0 ) + { + kprintf("\n\tReload\n\n\n"); + _CPU_ISR_Disable(level); + __reload (); + } + + if ( buttonsDown & PAD_BUTTON_A ) + { + kprintf("\n\tReset\n\n\n"); +#if defined(HW_DOL) + SYS_ResetSystem(SYS_HOTRESET,0,FALSE); +#else + __reload (); +#endif + } + + udelay(20000); + if(reload_timer > 0) + reload_timer--; + } +} + +//just implement core for unrecoverable exceptions. +void c_default_exceptionhandler(frame_context *pCtx) +{ + GX_AbortFrame(); + VIDEO_SetFramebuffer(exception_xfb); + __console_init(exception_xfb,20,20,640,574,1280); + CON_EnableGecko(1, true); + + kprintf("\n\n\n\tException (%s) occurred!\n", exception_name[pCtx->EXCPT_Number]); + + kprintf("\tGPR00 %08X GPR08 %08X GPR16 %08X GPR24 %08X\n",pCtx->GPR[0], pCtx->GPR[8], pCtx->GPR[16], pCtx->GPR[24]); + kprintf("\tGPR01 %08X GPR09 %08X GPR17 %08X GPR25 %08X\n",pCtx->GPR[1], pCtx->GPR[9], pCtx->GPR[17], pCtx->GPR[25]); + kprintf("\tGPR02 %08X GPR10 %08X GPR18 %08X GPR26 %08X\n",pCtx->GPR[2], pCtx->GPR[10], pCtx->GPR[18], pCtx->GPR[26]); + kprintf("\tGPR03 %08X GPR11 %08X GPR19 %08X GPR27 %08X\n",pCtx->GPR[3], pCtx->GPR[11], pCtx->GPR[19], pCtx->GPR[27]); + kprintf("\tGPR04 %08X GPR12 %08X GPR20 %08X GPR28 %08X\n",pCtx->GPR[4], pCtx->GPR[12], pCtx->GPR[20], pCtx->GPR[28]); + kprintf("\tGPR05 %08X GPR13 %08X GPR21 %08X GPR29 %08X\n",pCtx->GPR[5], pCtx->GPR[13], pCtx->GPR[21], pCtx->GPR[29]); + kprintf("\tGPR06 %08X GPR14 %08X GPR22 %08X GPR30 %08X\n",pCtx->GPR[6], pCtx->GPR[14], pCtx->GPR[22], pCtx->GPR[30]); + kprintf("\tGPR07 %08X GPR15 %08X GPR23 %08X GPR31 %08X\n",pCtx->GPR[7], pCtx->GPR[15], pCtx->GPR[23], pCtx->GPR[31]); + kprintf("\tLR %08X SRR0 %08x SRR1 %08x MSR %08x\n", pCtx->LR, pCtx->SRR0, pCtx->SRR1,pCtx->MSR); + kprintf("\tDAR %08X DSISR %08X\n", mfspr(19), mfspr(18)); + + _cpu_print_stack((void*)pCtx->SRR0,(void*)pCtx->LR,(void*)pCtx->GPR[1]); + + if((pCtx->EXCPT_Number==EX_DSI) || (pCtx->EXCPT_Number==EX_FP)) { + u32 i; + u32 *pAdd = (u32*)pCtx->SRR0; + kprintf("\n\n\tCODE DUMP:\n"); + for (i=0; i<12; i+=4) + kprintf("\t%p: %08X %08X %08X %08X\n", + &(pAdd[i]),pAdd[i], pAdd[i+1], pAdd[i+2], pAdd[i+3]); + } + + waitForReload(); +} + diff --git a/wii/libogc/libogc/exception_handler.S b/wii/libogc/libogc/exception_handler.S new file mode 100644 index 0000000000..6ff209ad3d --- /dev/null +++ b/wii/libogc/libogc/exception_handler.S @@ -0,0 +1,243 @@ +/*------------------------------------------------------------- + +exception_handler.S -- PPC exception handling support + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#include + +#define EXCEPTION_PROLOG \ + mfspr r0,912; \ + stw r0,GQR0_OFFSET(sp); \ + mfspr r0,913; \ + stw r0,GQR1_OFFSET(sp); \ + mfspr r0,914; \ + stw r0,GQR2_OFFSET(sp); \ + mfspr r0,915; \ + stw r0,GQR3_OFFSET(sp); \ + mfspr r0,916; \ + stw r0,GQR4_OFFSET(sp); \ + mfspr r0,917; \ + stw r0,GQR5_OFFSET(sp); \ + mfspr r0,918; \ + stw r0,GQR6_OFFSET(sp); \ + mfspr r0,919; \ + stw r0,GQR7_OFFSET(sp); \ + stw r6,GPR6_OFFSET(sp); \ + stw r7,GPR7_OFFSET(sp); \ + stw r8,GPR8_OFFSET(sp); \ + stw r9,GPR9_OFFSET(sp); \ + stw r10,GPR10_OFFSET(sp); \ + stw r11,GPR11_OFFSET(sp); \ + stw r12,GPR12_OFFSET(sp); \ + stw r13,GPR13_OFFSET(sp); \ + stw r14,GPR14_OFFSET(sp); \ + stw r15,GPR15_OFFSET(sp); + +#define EXCEPTION_EPILOG \ + lwz r4,GQR0_OFFSET(sp); \ + mtspr 912,r4; \ + lwz r4,GQR1_OFFSET(sp); \ + mtspr 913,r4; \ + lwz r4,GQR2_OFFSET(sp); \ + mtspr 914,r4; \ + lwz r4,GQR3_OFFSET(sp); \ + mtspr 915,r4; \ + lwz r4,GQR4_OFFSET(sp); \ + mtspr 916,r4; \ + lwz r4,GQR5_OFFSET(sp); \ + mtspr 917,r4; \ + lwz r4,GQR6_OFFSET(sp); \ + mtspr 918,r4; \ + lwz r4,GQR7_OFFSET(sp); \ + mtspr 919,r4; \ + lwz r15,GPR15_OFFSET(sp); \ + lwz r14,GPR14_OFFSET(sp); \ + lwz r13,GPR13_OFFSET(sp); \ + lwz r12,GPR12_OFFSET(sp); \ + lwz r11,GPR11_OFFSET(sp); \ + lwz r10,GPR10_OFFSET(sp); \ + lwz r9,GPR9_OFFSET(sp); \ + lwz r8,GPR8_OFFSET(sp); \ + lwz r7,GPR7_OFFSET(sp); \ + lwz r6,GPR6_OFFSET(sp); \ + lwz r5,GPR5_OFFSET(sp) + + + .globl exceptionhandler_start,exceptionhandler_end,exceptionhandler_patch +exceptionhandler_start: + mtspr SPRG3,r4 + clrlwi r4,sp,2 //make sp physical and move new value to r4 + stwu r4,-EXCEPTION_FRAME_END(r4) + stw r0,GPR0_OFFSET(r4) + stw sp,GPR1_OFFSET(r4) + stw toc,GPR2_OFFSET(r4) + stw r3,GPR3_OFFSET(r4) + mfspr r3,SPRG3 + stw r3,GPR4_OFFSET(r4) + stw r5,GPR5_OFFSET(r4) + mfcr r3 + stw r3,CR_OFFSET(r4) + mflr r3 + stw r3,LR_OFFSET(r4) + mfctr r3 + stw r3,CTR_OFFSET(r4) + mfxer r3 + stw r3,XER_OFFSET(r4) + mfmsr r3 + stw r3,MSR_OFFSET(r4) + mfdar r3 + stw r3,DAR_OFFSET(r4) + mfsrr0 r3 + stw r3,SRR0_OFFSET(r4) + mfsrr1 r3 + stw r3,SRR1_OFFSET(r4) + mr r5,r3 + nop + mfmsr r3 + ori r3,r3,MSR_IR|MSR_DR + mtsrr1 r3 + +exceptionhandler_patch: + li r3,0 + stw r3,EXCEPTION_NUMBER(r4) + + rlwinm. r5,r5,0,30,30 + lis r5,default_exceptionhandler@h + ori r5,r5,default_exceptionhandler@l + beq 1f + lis r5,_exceptionhandlertable@h + ori r5,r5,_exceptionhandlertable@l + clrlwi r5,r5,2 + clrlslwi r3,r3,24,2 + lwzx r5,r3,r5 +1: + mtsrr0 r5 + rfi +exceptionhandler_end: + nop + + .extern c_default_exceptionhandler + .globl default_exceptionhandler +default_exceptionhandler: + stwu sp,-EXCEPTION_FRAME_END(sp) //now we're able to adjust the stackpointer with it's cached address + + EXCEPTION_PROLOG + + stmw r16,GPR16_OFFSET(sp) + + addi r3,sp,0x08 + bl c_default_exceptionhandler + + lwz r4,CR_OFFSET(sp) + mtcr r4 + lwz r4,LR_OFFSET(sp) + mtlr r4 + lwz r4,CTR_OFFSET(sp) + mtctr r4 + lwz r4,XER_OFFSET(sp) + mtxer r4 + + EXCEPTION_EPILOG + + lmw r16,GPR16_OFFSET(sp) + + lwz toc,GPR2_OFFSET(sp) + lwz r0,GPR0_OFFSET(sp) + + lwz r4,SRR0_OFFSET(sp) + mtsrr0 r4 + lwz r4,SRR1_OFFSET(sp) + mtsrr1 r4 + + lwz r4,GPR4_OFFSET(sp) + lwz r3,GPR3_OFFSET(sp) + addi sp,sp,EXCEPTION_FRAME_END + rfi + + .extern _cpu_context_save_fp,_cpu_context_restore_fp + .globl fpu_exceptionhandler +fpu_exceptionhandler: + stwu sp,-EXCEPTION_FRAME_END(sp) //now we're able to adjust the stackpointer with it's cached address + + EXCEPTION_PROLOG + + mfmsr r4 + ori r4,r4,MSR_FP + mtmsr r4 + isync + + bl __thread_dispatch_fp + + lwz r4,CR_OFFSET(sp) + mtcr r4 + lwz r4,LR_OFFSET(sp) + mtlr r4 + lwz r4,CTR_OFFSET(sp) + mtctr r4 + lwz r4,XER_OFFSET(sp) + mtxer r4 + + EXCEPTION_EPILOG + + mfmsr r4 + rlwinm r4,r4,0,19,17 + mtmsr r4 + isync + + lwz toc,GPR2_OFFSET(sp) + lwz r0,GPR0_OFFSET(sp) + + lwz r4,SRR0_OFFSET(sp) + mtsrr0 r4 + lwz r4,SRR1_OFFSET(sp) + ori r4,r4,MSR_FP + mtsrr1 r4 + + lwz r4,GPR4_OFFSET(sp) + lwz r3,GPR3_OFFSET(sp) + addi sp,sp,EXCEPTION_FRAME_END + rfi + + .global systemcallhandler_start,systemcallhandler_end +systemcallhandler_start: + mtspr SPRG2,r9 + mtspr SPRG3,r10 + mfspr r9,HID0 + ori r10,r9,0x0008 + mtspr HID0,r10 + isync + sync + mtspr HID0,r9 + mfspr r9,SPRG2 + mfspr r10,SPRG3 + rfi +systemcallhandler_end: + nop + + + diff --git a/wii/libogc/libogc/exi.c b/wii/libogc/libogc/exi.c new file mode 100644 index 0000000000..961fd46592 --- /dev/null +++ b/wii/libogc/libogc/exi.c @@ -0,0 +1,866 @@ +/*------------------------------------------------------------- + +exi.c -- EXI subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#include +#include +#include +#include "asm.h" +#include "irq.h" +#include "processor.h" +#include "spinlock.h" +#include "exi.h" + +#define EXI_LOCK_DEVS 32 + +#define EXI_MAX_CHANNELS 3 +#define EXI_MAX_DEVICES 3 + +#define EXI_DEVICE0 0x0080 +#define EXI_DEVICE1 0x0100 +#define EXI_DEVICE2 0x0200 + +#define EXI_EXI_IRQ 0x0002 +#define EXI_TC_IRQ 0x0008 +#define EXI_EXT_IRQ 0x0800 +#define EXI_EXT_BIT 0x1000 + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + + +struct _lck_dev { + lwp_node node; + u32 dev; + EXICallback unlockcb; +}; + +typedef struct _exibus_priv { + EXICallback CallbackEXI; + EXICallback CallbackTC; + EXICallback CallbackEXT; + + u32 imm_len; + void *imm_buff; + u32 lockeddev; + u32 flags; + u32 lck_cnt; + u32 exi_id; + u64 exi_idtime; + lwp_queue lckd_dev; + u32 lckd_dev_bits; +} exibus_priv; + +static lwp_queue _lckdev_queue; +static struct _lck_dev lckdevs[EXI_LOCK_DEVS]; +static exibus_priv eximap[EXI_MAX_CHANNELS]; +static u64 last_exi_idtime[EXI_MAX_CHANNELS]; + +static u32 exi_id_serport1 = 0; + +static u32 exi_uart_chan = EXI_CHANNEL_0; +static u32 exi_uart_dev = EXI_DEVICE_0; +static u32 exi_uart_barnacle_enabled = 0; +static u32 exi_uart_enabled = 0; + +static void __exi_irq_handler(u32,void *); +static void __tc_irq_handler(u32,void *); +static void __ext_irq_handler(u32,void *); + +#if defined(HW_DOL) + static vu32* const _exiReg = (u32*)0xCC006800; +#elif defined(HW_RVL) + static vu32* const _exiReg = (u32*)0xCD006800; +#else + #error HW model unknown. +#endif + +static __inline__ void __exi_clearirqs(s32 nChn,u32 nEXIIrq,u32 nTCIrq,u32 nEXTIrq) +{ + u32 d; + d = (_exiReg[nChn*5]&~(EXI_EXI_IRQ|EXI_TC_IRQ|EXI_EXT_IRQ)); + if(nEXIIrq) d |= EXI_EXI_IRQ; + if(nTCIrq) d |= EXI_TC_IRQ; + if(nEXTIrq) d |= EXI_EXT_IRQ; + _exiReg[nChn*5] = d; +} + +static __inline__ void __exi_setinterrupts(s32 nChn,exibus_priv *exi) +{ + exibus_priv *pexi = &eximap[EXI_CHANNEL_2]; + if(nChn==EXI_CHANNEL_0) { + __MaskIrq((IRQMASK(IRQ_EXI0_EXI)|IRQMASK(IRQ_EXI2_EXI))); + if(!(exi->flags&EXI_FLAG_LOCKED) && (exi->CallbackEXI || pexi->CallbackEXI)) + __UnmaskIrq((IRQMASK(IRQ_EXI0_EXI)|IRQMASK(IRQ_EXI2_EXI))); + } else if(nChn==EXI_CHANNEL_1) { + __MaskIrq(IRQMASK(IRQ_EXI1_EXI)); + if(!(exi->flags&EXI_FLAG_LOCKED) && exi->CallbackEXI) __UnmaskIrq(IRQMASK(IRQ_EXI1_EXI)); + } else if(nChn==EXI_CHANNEL_2) { //explicitly use of channel 2 only if debugger is attached. + __MaskIrq(IRQMASK(IRQ_EXI0_EXI)); + if(!(exi->flags&EXI_FLAG_LOCKED) && IRQ_GetHandler(IRQ_PI_DEBUG)) __UnmaskIrq(IRQMASK(IRQ_EXI2_EXI)); + } +} + +static void __exi_initmap(exibus_priv *exim) +{ + s32 i; + exibus_priv *m; + + __lwp_queue_initialize(&_lckdev_queue,lckdevs,EXI_LOCK_DEVS,sizeof(struct _lck_dev)); + + for(i=0;iCallbackEXI = NULL; + m->CallbackEXT = NULL; + m->CallbackTC = NULL; + m->imm_buff = NULL; + m->exi_id = 0; + m->exi_idtime = 0; + m->flags = 0; + m->imm_len = 0; + m->lck_cnt = 0; + m->lockeddev = 0; + m->lckd_dev_bits = 0; + __lwp_queue_init_empty(&m->lckd_dev); + } +} + +static s32 __exi_probe(s32 nChn) +{ + u64 time; + s32 ret = 1; + u32 level; + u32 val; + exibus_priv *exi = &eximap[nChn]; + _CPU_ISR_Disable(level); + val = _exiReg[nChn*5]; + if(!(exi->flags&EXI_FLAG_ATTACH)) { + if(val&EXI_EXT_IRQ) { + __exi_clearirqs(nChn,0,0,1); + exi->exi_idtime = 0; + last_exi_idtime[nChn] = 0; + } + if(_exiReg[nChn*5]&EXI_EXT_BIT) { + time = gettime(); + if(last_exi_idtime[nChn]==0) last_exi_idtime[nChn] = time; + if((val=diff_usec(last_exi_idtime[nChn],time)+10)<30) ret = 0; + else ret = 1; + _CPU_ISR_Restore(level); + return ret; + } else { + exi->exi_idtime = 0; + last_exi_idtime[nChn] = 0; + _CPU_ISR_Restore(level); + return 0; + } + } + + if(!(_exiReg[nChn*5]&EXI_EXT_BIT) || (_exiReg[nChn*5]&EXI_EXT_IRQ)) { + exi->exi_idtime = 0; + last_exi_idtime[nChn] = 0; + ret = 0; + } + _CPU_ISR_Restore(level); + return ret; +} + +static s32 __exi_attach(s32 nChn,EXICallback ext_cb) +{ + s32 ret; + u32 level; + exibus_priv *exi = &eximap[nChn]; + _CPU_ISR_Disable(level); + ret = 0; + if(!(exi->flags&EXI_FLAG_ATTACH)) { + if(__exi_probe(nChn)==1) { + __exi_clearirqs(nChn,1,0,0); + exi->CallbackEXT = ext_cb; + __UnmaskIrq(((IRQMASK(IRQ_EXI0_EXT))>>(nChn*3))); + exi->flags |= EXI_FLAG_ATTACH; + ret = 1; + } + } + _CPU_ISR_Restore(level); + return ret; +} + +s32 EXI_Lock(s32 nChn,s32 nDev,EXICallback unlockCB) +{ + u32 level; + struct _lck_dev *lckd; + exibus_priv *exi = &eximap[nChn]; + _CPU_ISR_Disable(level); + if(exi->flags&EXI_FLAG_LOCKED) { + if(unlockCB && !(exi->lckd_dev_bits&(1<lck_cnt++; + exi->lckd_dev_bits |= (1<dev = nDev; + lckd->unlockcb = unlockCB; + __lwp_queue_appendI(&exi->lckd_dev,&lckd->node); + } + } + _CPU_ISR_Restore(level); + return 0; + } + + exi->lockeddev = nDev; + exi->flags |= EXI_FLAG_LOCKED; + __exi_setinterrupts(nChn,exi); + + _CPU_ISR_Restore(level); + return 1; +} + +s32 EXI_Unlock(s32 nChn) +{ + u32 level,dev; + EXICallback cb; + struct _lck_dev *lckd; + exibus_priv *exi = &eximap[nChn]; + _CPU_ISR_Disable(level); + if(!(exi->flags&EXI_FLAG_LOCKED)) { + _CPU_ISR_Restore(level); + return 0; + } + + exi->flags &= ~EXI_FLAG_LOCKED; + __exi_setinterrupts(nChn,exi); + + if(!exi->lck_cnt) { + _CPU_ISR_Restore(level); + return 1; + } + + exi->lck_cnt--; + lckd = (struct _lck_dev*)__lwp_queue_getI(&exi->lckd_dev); + __lwp_queue_appendI(&_lckdev_queue,&lckd->node); + + cb = lckd->unlockcb; + dev = lckd->dev; + exi->lckd_dev_bits &= ~(1<flags&EXI_FLAG_SELECT) { + _CPU_ISR_Restore(level); + return 0; + } + + if(nChn!=EXI_CHANNEL_2) { + if(nDev==EXI_DEVICE_0 && !(exi->flags&EXI_FLAG_ATTACH)) { + if(__exi_probe(nChn)==0) { + _CPU_ISR_Restore(level); + return 0; + } + } + if(!(exi->flags&EXI_FLAG_LOCKED) || exi->lockeddev!=nDev) { + _CPU_ISR_Restore(level); + return 0; + } + } + + exi->flags |= EXI_FLAG_SELECT; + val = _exiReg[nChn*5]; + val = (val&0x405)|(0x80<flags&EXI_FLAG_ATTACH) { + if(nChn==EXI_CHANNEL_0) __MaskIrq(IRQMASK(IRQ_EXI0_EXT)); + else if(nChn==EXI_CHANNEL_1) __MaskIrq(IRQMASK(IRQ_EXI1_EXT)); + } + + _CPU_ISR_Restore(level); + return 1; +} + +s32 EXI_SelectSD(s32 nChn,s32 nDev,s32 nFrq) +{ + u32 val,id; + s32 ret; + u32 level; + exibus_priv *exi = &eximap[nChn]; + _CPU_ISR_Disable(level); + + if(exi->flags&EXI_FLAG_SELECT) { + _CPU_ISR_Restore(level); + return 0; + } + + if(nChn!=EXI_CHANNEL_2) { + if(nDev==EXI_DEVICE_0 && !(exi->flags&EXI_FLAG_ATTACH)) { + if((ret=__exi_probe(nChn))==1) { + if(!exi->exi_idtime) ret = EXI_GetID(nChn,EXI_DEVICE_0,&id); + } + if(ret==0) { + _CPU_ISR_Restore(level); + return 0; + } + } + if(!(exi->flags&EXI_FLAG_LOCKED) || exi->lockeddev!=nDev) { + _CPU_ISR_Restore(level); + return 0; + } + } + + exi->flags |= EXI_FLAG_SELECT; + val = _exiReg[nChn*5]; + val = (val&0x405)|(nFrq<<4); + _exiReg[nChn*5] = val; + + if(exi->flags&EXI_FLAG_ATTACH) { + if(nChn==EXI_CHANNEL_0) __MaskIrq(IRQMASK(IRQ_EXI0_EXT)); + else if(nChn==EXI_CHANNEL_1) __MaskIrq(IRQMASK(IRQ_EXI1_EXT)); + } + + _CPU_ISR_Restore(level); + return 1; +} + +s32 EXI_Deselect(s32 nChn) +{ + u32 val; + u32 level; + exibus_priv *exi = &eximap[nChn]; + _CPU_ISR_Disable(level); + + if(!(exi->flags&EXI_FLAG_SELECT)) { + _CPU_ISR_Restore(level); + return 0; + } + + exi->flags &= ~EXI_FLAG_SELECT; + val = _exiReg[nChn*5]; + _exiReg[nChn*5] = (val&0x405); + + if(exi->flags&EXI_FLAG_ATTACH) { + if(nChn==EXI_CHANNEL_0) __UnmaskIrq(IRQMASK(IRQ_EXI0_EXT)); + else if(nChn==EXI_CHANNEL_1) __UnmaskIrq(IRQMASK(IRQ_EXI1_EXT)); + } + + if(nChn!=EXI_CHANNEL_2 && val&EXI_DEVICE0) { + if(__exi_probe(nChn)==0) { + _CPU_ISR_Restore(level); + return 0; + } + } + _CPU_ISR_Restore(level); + return 1; +} + +s32 EXI_Sync(s32 nChn) +{ + u8 *buf; + s32 ret; + u32 level,i,cnt,val; + exibus_priv *exi = &eximap[nChn]; + while(_exiReg[nChn*5+3]&0x0001); + + _CPU_ISR_Disable(level); + + ret = 0; + if(exi->flags&EXI_FLAG_SELECT && exi->flags&(EXI_FLAG_DMA|EXI_FLAG_IMM)) { + if(exi->flags&EXI_FLAG_IMM) { + cnt = exi->imm_len; + buf = exi->imm_buff; + if(buf && cnt>0) { + val = _exiReg[nChn*5+4]; + for(i=0;i>((3-i)*8))&0xFF; + } + } + exi->flags &= ~(EXI_FLAG_DMA|EXI_FLAG_IMM); + ret = 1; + } + _CPU_ISR_Restore(level); + return ret; +} + +s32 EXI_Imm(s32 nChn,void *pData,u32 nLen,u32 nMode,EXICallback tc_cb) +{ + u32 level; + u32 value,i; + exibus_priv *exi = &eximap[nChn]; + _CPU_ISR_Disable(level); + + if(exi->flags&(EXI_FLAG_DMA|EXI_FLAG_IMM) || !(exi->flags&EXI_FLAG_SELECT)) { + _CPU_ISR_Restore(level); + return 0; + } + + exi->CallbackTC = tc_cb; + if(tc_cb) { + __exi_clearirqs(nChn,0,1,0); + __UnmaskIrq(IRQMASK((IRQ_EXI0_TC+(nChn*3)))); + } + exi->flags |= EXI_FLAG_IMM; + + exi->imm_buff = pData; + exi->imm_len = nLen; + if(nMode!=EXI_READ) { + for(i=0,value=0;iimm_len = 0; + + _exiReg[nChn*5+3] = (((nLen-1)&0x03)<<4)|((nMode&0x03)<<2)|0x01; + + _CPU_ISR_Restore(level); + return 1; +} + +s32 EXI_ImmEx(s32 nChn,void *pData,u32 nLen,u32 nMode) +{ + u8 *buf = pData; + u32 tc; + s32 ret = 0; + while(nLen) { + ret = 0; + tc = nLen; + if(tc>4) tc = 4; + + if(!EXI_Imm(nChn,buf,tc,nMode,NULL)) break; + if(!EXI_Sync(nChn)) break; + nLen -= tc; + buf += tc; + + ret = 1; + } + return ret; +} + +s32 EXI_Dma(s32 nChn,void *pData,u32 nLen,u32 nMode,EXICallback tc_cb) +{ + u32 level; + exibus_priv *exi = &eximap[nChn]; + _CPU_ISR_Disable(level); + + if(exi->flags&(EXI_FLAG_DMA|EXI_FLAG_IMM) || !(exi->flags&EXI_FLAG_SELECT)) { + _CPU_ISR_Restore(level); + return 0; + } + exi->CallbackTC = tc_cb; + if(tc_cb) { + __exi_clearirqs(nChn,0,1,0); + __UnmaskIrq((IRQMASK((IRQ_EXI0_TC+(nChn*3))))); + } + + exi->imm_buff = NULL; + exi->imm_len = 0; + exi->flags |= EXI_FLAG_DMA; + + _exiReg[nChn*5+1] = (u32)pData&0x03FFFFE0; + _exiReg[nChn*5+2] = nLen; + _exiReg[nChn*5+3] = ((nMode&0x03)<<2)|0x03; + + _CPU_ISR_Restore(level); + return 1; +} + +s32 EXI_GetState(s32 nChn) +{ + exibus_priv *exi = &eximap[nChn]; + return exi->flags; +} + +static s32 __unlocked_handler(s32 nChn,s32 nDev) +{ + u32 nId; + EXI_GetID(nChn,nDev,&nId); + return 1; +} + +s32 EXI_GetID(s32 nChn,s32 nDev,u32 *nId) +{ + u64 idtime = 0; + s32 ret,lck; + u32 reg,level; + exibus_priv *exi = &eximap[nChn]; + + if(nChnexi_idtime==last_exi_idtime[nChn]) { + *nId = exi->exi_id; + return 1; + } + if(__exi_attach(nChn,NULL)==0) return 0; + idtime = last_exi_idtime[nChn]; + } + lck = 0; + if(nChnexi_idtime = idtime; + exi->exi_id = *nId; + ret = 1; + } + _CPU_ISR_Restore(level); + } + return ret; +} + +s32 EXI_Attach(s32 nChn,EXICallback ext_cb) +{ + s32 ret; + u32 level; + exibus_priv *exi = &eximap[nChn]; + EXI_Probe(nChn); + + _CPU_ISR_Disable(level); + if(exi->exi_idtime) { + ret = __exi_attach(nChn,ext_cb); + } else + ret = 0; + _CPU_ISR_Restore(level); + return ret; +} + +s32 EXI_Detach(s32 nChn) +{ + u32 level; + s32 ret = 1; + exibus_priv *exi = &eximap[nChn]; + _CPU_ISR_Disable(level); + if(exi->flags&EXI_FLAG_ATTACH) { + if(exi->flags&EXI_FLAG_LOCKED && exi->lockeddev!=EXI_DEVICE_0) ret = 0; + else { + exi->flags &= ~EXI_FLAG_ATTACH; + __MaskIrq(((IRQMASK(IRQ_EXI0_EXI)|IRQMASK(IRQ_EXI0_TC)|IRQMASK(IRQ_EXI0_EXT))>>(nChn*3))); + } + } + _CPU_ISR_Restore(level); + return ret; +} + +EXICallback EXI_RegisterEXICallback(s32 nChn,EXICallback exi_cb) +{ + u32 level; + EXICallback old = NULL; + exibus_priv *exi = &eximap[nChn]; + _CPU_ISR_Disable(level); + old = exi->CallbackEXI; + exi->CallbackEXI = exi_cb; + if(nChn==EXI_CHANNEL_2) __exi_setinterrupts(EXI_CHANNEL_0,&eximap[EXI_CHANNEL_0]); + else __exi_setinterrupts(nChn,exi); + _CPU_ISR_Restore(level); + return old; +} + +s32 EXI_Probe(s32 nChn) +{ + s32 ret; + u32 id; + exibus_priv *exi = &eximap[nChn]; + if((ret=__exi_probe(nChn))==1) { + if(exi->exi_idtime==0) { + if(EXI_GetID(nChn,EXI_DEVICE_0,&id)==0) ret = 0; + } + } + return ret; +} + +s32 EXI_ProbeEx(s32 nChn) +{ + if(EXI_Probe(nChn)==1) return 1; + if(last_exi_idtime[nChn]==0) return -1; + return 0; +} + +void EXI_ProbeReset() +{ + last_exi_idtime[0] = 0; + last_exi_idtime[1] = 0; + + eximap[0].exi_idtime = 0; + eximap[1].exi_idtime = 0; + + __exi_probe(0); + __exi_probe(1); + EXI_GetID(EXI_CHANNEL_0,EXI_DEVICE_2,&exi_id_serport1); +} + +void __exi_init() +{ + __MaskIrq(IM_EXI); + + _exiReg[0] = 0; + _exiReg[5] = 0; + _exiReg[10] = 0; + + _exiReg[0] = 0x2000; + + __exi_initmap(eximap); + + IRQ_Request(IRQ_EXI0_EXI,__exi_irq_handler,NULL); + IRQ_Request(IRQ_EXI0_TC,__tc_irq_handler,NULL); + IRQ_Request(IRQ_EXI0_EXT,__ext_irq_handler,NULL); + IRQ_Request(IRQ_EXI1_EXI,__exi_irq_handler,NULL); + IRQ_Request(IRQ_EXI1_TC,__tc_irq_handler,NULL); + IRQ_Request(IRQ_EXI1_EXT,__ext_irq_handler,NULL); + IRQ_Request(IRQ_EXI2_EXI,__exi_irq_handler,NULL); + IRQ_Request(IRQ_EXI2_TC,__tc_irq_handler,NULL); + + EXI_ProbeReset(); +} + +void __exi_irq_handler(u32 nIrq,void *pCtx) +{ + u32 chan,dev; + exibus_priv *exi = NULL; + const u32 fact = 0x55555556; + + chan = ((fact*(nIrq-IRQ_EXI0_EXI))>>1)&0x0f; + dev = _SHIFTR((_exiReg[chan*5]&0x380),8,2); + + exi = &eximap[chan]; + __exi_clearirqs(chan,1,0,0); + + if(!exi->CallbackEXI) return; + exi->CallbackEXI(chan,dev); +} + +void __tc_irq_handler(u32 nIrq,void *pCtx) +{ + u32 cnt,len,d,chan,dev; + EXICallback tccb; + void *buf = NULL; + exibus_priv *exi = NULL; + const u32 fact = 0x55555556; + + chan = ((fact*(nIrq-IRQ_EXI0_TC))>>1)&0x0f; + dev = _SHIFTR((_exiReg[chan*5]&0x380),8,2); + + exi = &eximap[chan]; + __MaskIrq(IRQMASK(nIrq)); + __exi_clearirqs(chan,0,1,0); + + tccb = exi->CallbackTC; + if(!tccb) return; + + exi->CallbackTC = NULL; + if(exi->flags&(EXI_FLAG_DMA|EXI_FLAG_IMM)) { + if(exi->flags&EXI_FLAG_IMM) { + len = exi->imm_len; + buf = exi->imm_buff; + if(len>0 && buf) { + d = _exiReg[chan*5+4]; + if(d>0) { + for(cnt=0;cnt>((3-cnt)*8))&0xFF; + } + } + } + exi->flags &= ~(EXI_FLAG_DMA|EXI_FLAG_IMM); + } + tccb(chan,dev); +} + +void __ext_irq_handler(u32 nIrq,void *pCtx) +{ + + u32 chan,dev; + exibus_priv *exi = NULL; + const u32 fact = 0x55555556; + + chan = ((fact*(nIrq-IRQ_EXI0_EXT))>>1)&0x0f; + dev = _SHIFTR((_exiReg[chan*5]&0x380),8,2); + + exi = &eximap[chan]; + __MaskIrq(IRQMASK(nIrq)); + __exi_clearirqs(chan,0,0,1); + + exi->flags &= ~EXI_FLAG_ATTACH; + if(exi->CallbackEXT) exi->CallbackEXT(chan,dev); +} + + +/* EXI UART stuff */ +static s32 __probebarnacle(s32 chn,u32 dev,u32 *rev) +{ + u32 ret,reg; + + if(chn!=EXI_CHANNEL_2 && dev==EXI_DEVICE_0) { + if(EXI_Attach(chn,NULL)==0) return 0; + } + + ret = 0; + if(EXI_Lock(chn,dev,NULL)==1) { + if(EXI_Select(chn,dev,EXI_SPEED1MHZ)==1) { + reg = 0x20011300; + if(EXI_Imm(chn,®,sizeof(u32),EXI_WRITE,NULL)==0) ret |= 0x0001; + if(EXI_Sync(chn)==0) ret |= 0x0002; + if(EXI_Imm(chn,rev,sizeof(u32),EXI_READ,NULL)==0) ret |= 0x0004; + if(EXI_Sync(chn)==0) ret |= 0x0008; + if(EXI_Deselect(chn)==0) ret |= 0x0010; + + } + EXI_Unlock(chn); + } + + if(chn!=EXI_CHANNEL_2 && dev==EXI_DEVICE_0) EXI_Detach(chn); + + if(ret) return 0; + if((*rev+0x00010000)==0xffff) return 0; + + return 1; +} + +static s32 __queuelength() +{ + u32 reg; + u8 len = 0; + + if(EXI_Select(exi_uart_chan,exi_uart_dev,EXI_SPEED8MHZ)==0) return -1; + + reg = 0x20010000; + EXI_Imm(exi_uart_chan,®,sizeof(u32),EXI_WRITE,NULL); + EXI_Sync(exi_uart_chan); + EXI_Imm(exi_uart_chan,&len,sizeof(u8),EXI_READ,NULL); + EXI_Sync(exi_uart_chan); + + EXI_Deselect(exi_uart_chan); + + return (16-len); +} + +void __SYS_EnableBarnacle(s32 chn,u32 dev) +{ + u32 id,rev; + + if(EXI_GetID(chn,dev,&id)==0) return; + + if(id==0x01020000 || id==0x0004 || id==0x80000010 || id==0x80000008 + || id==0x80000004 || id==0xffff || id==0x80000020 || id==0x0020 + || id==0x0010 || id==0x0008 || id==0x01010000 || id==0x04040404 + || id==0x04021000 || id==0x03010000 || id==0x02020000 + || id==0x04020300 || id==0x04020200 || id==0x04130000 + || id==0x04120000 || id==0x04060000 || id==0x04220000) return; + + if(__probebarnacle(chn,dev,&rev)==0) return; + + + exi_uart_chan = chn; + exi_uart_dev = dev; + exi_uart_barnacle_enabled = 0xa5ff005a; + exi_uart_enabled = 0xa5ff005a; +} + +s32 InitializeUART() +{ + if((exi_uart_enabled+0x5a010000)==0x005a) return 0; + + exi_uart_chan = EXI_CHANNEL_0; + exi_uart_dev = EXI_DEVICE_1; + + exi_uart_enabled = 0xa5ff005a; + return 0; +} + +s32 WriteUARTN(void *buf,u32 len) +{ + u8 *ptr; + u32 reg; + s32 ret,qlen,cnt; + + if((exi_uart_enabled+0x5a010000)!=0x005a) return 2; + if(EXI_Lock(exi_uart_chan,exi_uart_dev,NULL)==0) return 0; + + ptr = buf; + while((ptr-(u8*)buf)=12 || qlen>=len) { + if(EXI_Select(exi_uart_chan,exi_uart_dev,EXI_SPEED8MHZ)==0) { + ret = 3; + break; + } + + reg = 0xa0010000; + EXI_Imm(exi_uart_chan,®,sizeof(u32),EXI_WRITE,NULL); + EXI_Sync(exi_uart_chan); + + while(qlen>0 && len>0) { + cnt = 4; + if(qlen>=0x0004) { + if(len<4) cnt = len; + if(qlen + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +static int __gcsd_init = 0; + +static bool __gcsd_isInserted(int n) +{ + if(sdgecko_readStatus(n) == CARDIO_ERROR_NOCARD) + return false; + return true; +} + +static bool __gcsd_startup(int n) +{ + if(__gcsd_init == 1) + return __gcsd_isInserted(n); + sdgecko_initBufferPool(); + sdgecko_initIODefault(); + __gcsd_init = 1; + return __gcsd_isInserted(n); +} + + +static bool __gcsd_readSectors(int n, u32 sector, u32 numSectors, void *buffer) +{ + s32 ret; + + ret = sdgecko_readSectors(n,sector,numSectors,buffer); + if(ret == CARDIO_ERROR_READY) + return true; + + return false; +} + +static bool __gcsd_writeSectors(int n, u32 sector, u32 numSectors, const void *buffer) +{ + s32 ret; + + ret = sdgecko_writeSectors(n,sector,numSectors,buffer); + if(ret == CARDIO_ERROR_READY) + return true; + + return false; +} + +static bool __gcsd_clearStatus(int n) +{ + return true; // FIXME +} + +static bool __gcsd_shutdown(int n) +{ + sdgecko_doUnmount(n); + return true; +} + + +static bool __gcsda_startup(void) +{ + return __gcsd_startup(0); +} + +static bool __gcsda_isInserted(void) +{ + return __gcsd_isInserted(0); +} + +static bool __gcsda_readSectors(u32 sector, u32 numSectors, void *buffer) +{ + return __gcsd_readSectors(0, sector, numSectors, buffer); +} + +static bool __gcsda_writeSectors(u32 sector, u32 numSectors, void *buffer) +{ + return __gcsd_writeSectors(0, sector, numSectors, buffer); +} + +static bool __gcsda_clearStatus(void) +{ + return __gcsd_clearStatus(0); +} + +static bool __gcsda_shutdown(void) +{ + return __gcsd_shutdown(0); +} + + + +static bool __gcsdb_startup(void) +{ + return __gcsd_startup(1); +} + +static bool __gcsdb_isInserted(void) +{ + return __gcsd_isInserted(1); +} + +static bool __gcsdb_readSectors(u32 sector, u32 numSectors, void *buffer) +{ + return __gcsd_readSectors(1, sector, numSectors, buffer); +} + +static bool __gcsdb_writeSectors(u32 sector, u32 numSectors, void *buffer) +{ + return __gcsd_writeSectors(1, sector, numSectors, buffer); +} + +static bool __gcsdb_clearStatus(void) +{ + return __gcsd_clearStatus(1); +} + +static bool __gcsdb_shutdown(void) +{ + return __gcsd_shutdown(1); +} + +const DISC_INTERFACE __io_gcsda = { + DEVICE_TYPE_GC_SD, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_GAMECUBE_SLOTA, + (FN_MEDIUM_STARTUP)&__gcsda_startup, + (FN_MEDIUM_ISINSERTED)&__gcsda_isInserted, + (FN_MEDIUM_READSECTORS)&__gcsda_readSectors, + (FN_MEDIUM_WRITESECTORS)&__gcsda_writeSectors, + (FN_MEDIUM_CLEARSTATUS)&__gcsda_clearStatus, + (FN_MEDIUM_SHUTDOWN)&__gcsda_shutdown +} ; +const DISC_INTERFACE __io_gcsdb = { + DEVICE_TYPE_GC_SD, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_GAMECUBE_SLOTB, + (FN_MEDIUM_STARTUP)&__gcsdb_startup, + (FN_MEDIUM_ISINSERTED)&__gcsdb_isInserted, + (FN_MEDIUM_READSECTORS)&__gcsdb_readSectors, + (FN_MEDIUM_WRITESECTORS)&__gcsdb_writeSectors, + (FN_MEDIUM_CLEARSTATUS)&__gcsdb_clearStatus, + (FN_MEDIUM_SHUTDOWN)&__gcsdb_shutdown +} ; diff --git a/wii/libogc/libogc/gu.c b/wii/libogc/libogc/gu.c new file mode 100644 index 0000000000..8ee3e3052e --- /dev/null +++ b/wii/libogc/libogc/gu.c @@ -0,0 +1,916 @@ +#include +#include + +extern void __ps_guMtxRotAxisRadInternal(register Mtx mt,const register guVector *axis,register f32 sT,register f32 cT); + +void guFrustum(Mtx44 mt,f32 t,f32 b,f32 l,f32 r,f32 n,f32 f) +{ + f32 tmp; + + tmp = 1.0f/(r-l); + mt[0][0] = (2*n)*tmp; + mt[0][1] = 0.0f; + mt[0][2] = (r+l)*tmp; + mt[0][3] = 0.0f; + + tmp = 1.0f/(t-b); + mt[1][0] = 0.0f; + mt[1][1] = (2*n)*tmp; + mt[1][2] = (t+b)*tmp; + mt[1][3] = 0.0f; + + tmp = 1.0f/(f-n); + mt[2][0] = 0.0f; + mt[2][1] = 0.0f; + mt[2][2] = -(n)*tmp; + mt[2][3] = -(f*n)*tmp; + + mt[3][0] = 0.0f; + mt[3][1] = 0.0f; + mt[3][2] = -1.0f; + mt[3][3] = 0.0f; +} + +void guPerspective(Mtx44 mt,f32 fovy,f32 aspect,f32 n,f32 f) +{ + f32 cot,angle,tmp; + + angle = fovy*0.5f; + angle = DegToRad(angle); + + cot = 1.0f/tanf(angle); + + mt[0][0] = cot/aspect; + mt[0][1] = 0.0f; + mt[0][2] = 0.0f; + mt[0][3] = 0.0f; + + mt[1][0] = 0.0f; + mt[1][1] = cot; + mt[1][2] = 0.0f; + mt[1][3] = 0.0f; + + tmp = 1.0f/(f-n); + mt[2][0] = 0.0f; + mt[2][1] = 0.0f; + mt[2][2] = -(n)*tmp; + mt[2][3] = -(f*n)*tmp; + + mt[3][0] = 0.0f; + mt[3][1] = 0.0f; + mt[3][2] = -1.0f; + mt[3][3] = 0.0f; +} + +void guOrtho(Mtx44 mt,f32 t,f32 b,f32 l,f32 r,f32 n,f32 f) +{ + f32 tmp; + + tmp = 1.0f/(r-l); + mt[0][0] = 2.0f*tmp; + mt[0][1] = 0.0f; + mt[0][2] = 0.0f; + mt[0][3] = -(r+l)*tmp; + + tmp = 1.0f/(t-b); + mt[1][0] = 0.0f; + mt[1][1] = 2.0f*tmp; + mt[1][2] = 0.0f; + mt[1][3] = -(t+b)*tmp; + + tmp = 1.0f/(f-n); + mt[2][0] = 0.0f; + mt[2][1] = 0.0f; + mt[2][2] = -(1.0f)*tmp; + mt[2][3] = -(f)*tmp; + + mt[3][0] = 0.0f; + mt[3][1] = 0.0f; + mt[3][2] = 0.0f; + mt[3][3] = 1.0f; +} + +void guLightPerspective(Mtx mt,f32 fovY,f32 aspect,f32 scaleS,f32 scaleT,f32 transS,f32 transT) +{ + f32 angle; + f32 cot; + + angle = fovY*0.5f; + angle = DegToRad(angle); + + cot = 1.0f/tanf(angle); + + mt[0][0] = (cot / aspect) * scaleS; + mt[0][1] = 0.0f; + mt[0][2] = -transS; + mt[0][3] = 0.0f; + + mt[1][0] = 0.0f; + mt[1][1] = cot * scaleT; + mt[1][2] = -transT; + mt[1][3] = 0.0f; + + mt[2][0] = 0.0f; + mt[2][1] = 0.0f; + mt[2][2] = -1.0f; + mt[2][3] = 0.0f; +} + +void guLightOrtho(Mtx mt,f32 t,f32 b,f32 l,f32 r,f32 scaleS,f32 scaleT,f32 transS,f32 transT) +{ + f32 tmp; + + tmp = 1.0f / (r - l); + mt[0][0] = (2.0f * tmp * scaleS); + mt[0][1] = 0.0f; + mt[0][2] = 0.0f; + mt[0][3] = ((-(r + l) * tmp) * scaleS) + transS; + + tmp = 1.0f / (t - b); + mt[1][0] = 0.0f; + mt[1][1] = (2.0f * tmp) * scaleT; + mt[1][2] = 0.0f; + mt[1][3] = ((-(t + b) * tmp)* scaleT) + transT; + + mt[2][0] = 0.0f; + mt[2][1] = 0.0f; + mt[2][2] = 0.0f; + mt[2][3] = 1.0f; +} + +void guLightFrustum(Mtx mt,f32 t,f32 b,f32 l,f32 r,f32 n,f32 scaleS,f32 scaleT,f32 transS,f32 transT) +{ + f32 tmp; + + tmp = 1.0f / (r - l); + mt[0][0] = ((2*n) * tmp) * scaleS; + mt[0][1] = 0.0f; + mt[0][2] = (((r + l) * tmp) * scaleS) - transS; + mt[0][3] = 0.0f; + + tmp = 1.0f / (t - b); + mt[1][0] = 0.0f; + mt[1][1] = ((2*n) * tmp) * scaleT; + mt[1][2] = (((t + b) * tmp) * scaleT) - transT; + mt[1][3] = 0.0f; + + mt[2][0] = 0.0f; + mt[2][1] = 0.0f; + mt[2][2] = -1.0f; + mt[2][3] = 0.0f; +} + +void guLookAt(Mtx mt,guVector *camPos,guVector *camUp,guVector *target) +{ + guVector vLook,vRight,vUp; + + vLook.x = camPos->x - target->x; + vLook.y = camPos->y - target->y; + vLook.z = camPos->z - target->z; + guVecNormalize(&vLook); + + guVecCross(camUp,&vLook,&vRight); + guVecNormalize(&vRight); + + guVecCross(&vLook,&vRight,&vUp); + + mt[0][0] = vRight.x; + mt[0][1] = vRight.y; + mt[0][2] = vRight.z; + mt[0][3] = -( camPos->x * vRight.x + camPos->y * vRight.y + camPos->z * vRight.z ); + + mt[1][0] = vUp.x; + mt[1][1] = vUp.y; + mt[1][2] = vUp.z; + mt[1][3] = -( camPos->x * vUp.x + camPos->y * vUp.y + camPos->z * vUp.z ); + + mt[2][0] = vLook.x; + mt[2][1] = vLook.y; + mt[2][2] = vLook.z; + mt[2][3] = -( camPos->x * vLook.x + camPos->y * vLook.y + camPos->z * vLook.z ); +} + +void c_guMtxIdentity(Mtx mt) +{ + s32 i,j; + + for(i=0;i<3;i++) { + for(j=0;j<4;j++) { + if(i==j) mt[i][j] = 1.0; + else mt[i][j] = 0.0; + } + } +} + +void c_guMtxRotRad(Mtx mt,const char axis,f32 rad) +{ + f32 sinA,cosA; + + sinA = sinf(rad); + cosA = cosf(rad); + + c_guMtxRotTrig(mt,axis,sinA,cosA); +} + +#ifdef GEKKO +void ps_guMtxRotRad(register Mtx mt,const register char axis,register f32 rad) +{ + register f32 sinA = sinf(rad); + register f32 cosA = cosf(rad); + + ps_guMtxRotTrig(mt,axis,sinA,cosA); +} + +void ps_guMtxRotAxisRad(Mtx mt,guVector *axis,f32 rad) +{ + f32 sinT = sinf(rad); + f32 cosT = cosf(rad); + + __ps_guMtxRotAxisRadInternal(mt,axis,sinT,cosT); +} + +#endif + +void c_guMtxRotTrig(Mtx mt,const char axis,f32 sinA,f32 cosA) +{ + switch(axis) { + case 'x': + case 'X': + mt[0][0] = 1.0f; mt[0][1] = 0.0f; mt[0][2] = 0.0f; mt[0][3] = 0.0f; + mt[1][0] = 0.0f; mt[1][1] = cosA; mt[1][2] = -sinA; mt[1][3] = 0.0f; + mt[2][0] = 0.0f; mt[2][1] = sinA; mt[2][2] = cosA; mt[2][3] = 0.0f; + break; + case 'y': + case 'Y': + mt[0][0] = cosA; mt[0][1] = 0.0f; mt[0][2] = sinA; mt[0][3] = 0.0f; + mt[1][0] = 0.0f; mt[1][1] = 1.0f; mt[1][2] = 0.0f; mt[1][3] = 0.0f; + mt[2][0] = -sinA; mt[2][1] = 0.0f; mt[2][2] = cosA; mt[2][3] = 0.0f; + break; + case 'z': + case 'Z': + mt[0][0] = cosA; mt[0][1] = -sinA; mt[0][2] = 0.0f; mt[0][3] = 0.0f; + mt[1][0] = sinA; mt[1][1] = cosA; mt[1][2] = 0.0f; mt[1][3] = 0.0f; + mt[2][0] = 0.0f; mt[2][1] = 0.0f; mt[2][2] = 1.0f; mt[2][3] = 0.0f; + break; + default: + break; + } +} + +void c_guMtxRotAxisRad(Mtx mt,guVector *axis,f32 rad) +{ + f32 s,c; + f32 t; + f32 x,y,z; + f32 xSq,ySq,zSq; + + s = sinf(rad); + c = cosf(rad); + t = 1.0f-c; + + c_guVecNormalize(axis); + + x = axis->x; + y = axis->y; + z = axis->z; + + xSq = x*x; + ySq = y*y; + zSq = z*z; + + mt[0][0] = ( t * xSq ) + ( c ); + mt[0][1] = ( t * x * y ) - ( s * z ); + mt[0][2] = ( t * x * z ) + ( s * y ); + mt[0][3] = 0.0f; + + mt[1][0] = ( t * x * y ) + ( s * z ); + mt[1][1] = ( t * ySq ) + ( c ); + mt[1][2] = ( t * y * z ) - ( s * x ); + mt[1][3] = 0.0f; + + mt[2][0] = ( t * x * z ) - ( s * y ); + mt[2][1] = ( t * y * z ) + ( s * x ); + mt[2][2] = ( t * zSq ) + ( c ); + mt[2][3] = 0.0f; + +} + +void c_guMtxCopy(Mtx src,Mtx dst) +{ + if(src==dst) return; + + dst[0][0] = src[0][0]; dst[0][1] = src[0][1]; dst[0][2] = src[0][2]; dst[0][3] = src[0][3]; + dst[1][0] = src[1][0]; dst[1][1] = src[1][1]; dst[1][2] = src[1][2]; dst[1][3] = src[1][3]; + dst[2][0] = src[2][0]; dst[2][1] = src[2][1]; dst[2][2] = src[2][2]; dst[2][3] = src[2][3]; +} + +void c_guMtxConcat(Mtx a,Mtx b,Mtx ab) +{ + Mtx tmp; + MtxP m; + + if(ab==b || ab==a) + m = tmp; + else + m = ab; + + m[0][0] = a[0][0]*b[0][0] + a[0][1]*b[1][0] + a[0][2]*b[2][0]; + m[0][1] = a[0][0]*b[0][1] + a[0][1]*b[1][1] + a[0][2]*b[2][1]; + m[0][2] = a[0][0]*b[0][2] + a[0][1]*b[1][2] + a[0][2]*b[2][2]; + m[0][3] = a[0][0]*b[0][3] + a[0][1]*b[1][3] + a[0][2]*b[2][3] + a[0][3]; + + m[1][0] = a[1][0]*b[0][0] + a[1][1]*b[1][0] + a[1][2]*b[2][0]; + m[1][1] = a[1][0]*b[0][1] + a[1][1]*b[1][1] + a[1][2]*b[2][1]; + m[1][2] = a[1][0]*b[0][2] + a[1][1]*b[1][2] + a[1][2]*b[2][2]; + m[1][3] = a[1][0]*b[0][3] + a[1][1]*b[1][3] + a[1][2]*b[2][3] + a[1][3]; + + m[2][0] = a[2][0]*b[0][0] + a[2][1]*b[1][0] + a[2][2]*b[2][0]; + m[2][1] = a[2][0]*b[0][1] + a[2][1]*b[1][1] + a[2][2]*b[2][1]; + m[2][2] = a[2][0]*b[0][2] + a[2][1]*b[1][2] + a[2][2]*b[2][2]; + m[2][3] = a[2][0]*b[0][3] + a[2][1]*b[1][3] + a[2][2]*b[2][3] + a[2][3]; + + if(m==tmp) + c_guMtxCopy(tmp,ab); +} + +void c_guMtxScale(Mtx mt,f32 xS,f32 yS,f32 zS) +{ + mt[0][0] = xS; mt[0][1] = 0.0f; mt[0][2] = 0.0f; mt[0][3] = 0.0f; + mt[1][0] = 0.0f; mt[1][1] = yS; mt[1][2] = 0.0f; mt[1][3] = 0.0f; + mt[2][0] = 0.0f; mt[2][1] = 0.0f; mt[2][2] = zS; mt[2][3] = 0.0f; +} + +void c_guMtxScaleApply(Mtx src,Mtx dst,f32 xS,f32 yS,f32 zS) +{ + dst[0][0] = src[0][0] * xS; dst[0][1] = src[0][1] * xS; + dst[0][2] = src[0][2] * xS; dst[0][3] = src[0][3] * xS; + + dst[1][0] = src[1][0] * yS; dst[1][1] = src[1][1] * yS; + dst[1][2] = src[1][2] * yS; dst[1][3] = src[1][3] * yS; + + dst[2][0] = src[2][0] * zS; dst[2][1] = src[2][1] * zS; + dst[2][2] = src[2][2] * zS; dst[2][3] = src[2][3] * zS; +} + +void c_guMtxApplyScale(Mtx src,Mtx dst,f32 xS,f32 yS,f32 zS) +{ + dst[0][0] = src[0][0] * xS; dst[0][1] = src[0][1] * yS; + dst[0][2] = src[0][2] * zS; dst[0][3] = src[0][3]; + + dst[1][0] = src[1][0] * xS; dst[1][1] = src[1][1] * yS; + dst[1][2] = src[1][2] * zS; dst[1][3] = src[1][3]; + + dst[2][0] = src[2][0] * xS; dst[2][1] = src[2][1] * yS; + dst[2][2] = src[2][2] * zS; dst[2][3] = src[2][3]; +} + +void c_guMtxTrans(Mtx mt,f32 xT,f32 yT,f32 zT) +{ + mt[0][0] = 1.0f; mt[0][1] = 0.0f; mt[0][2] = 0.0f; mt[0][3] = xT; + mt[1][0] = 0.0f; mt[1][1] = 1.0f; mt[1][2] = 0.0f; mt[1][3] = yT; + mt[2][0] = 0.0f; mt[2][1] = 0.0f; mt[2][2] = 1.0f; mt[2][3] = zT; +} + +void c_guMtxTransApply(Mtx src,Mtx dst,f32 xT,f32 yT,f32 zT) +{ + if ( src != dst ) + { + dst[0][0] = src[0][0]; dst[0][1] = src[0][1]; dst[0][2] = src[0][2]; + dst[1][0] = src[1][0]; dst[1][1] = src[1][1]; dst[1][2] = src[1][2]; + dst[2][0] = src[2][0]; dst[2][1] = src[2][1]; dst[2][2] = src[2][2]; + } + + dst[0][3] = src[0][3] + xT; + dst[1][3] = src[1][3] + yT; + dst[2][3] = src[2][3] + zT; +} + +void c_guMtxApplyTrans(Mtx src,Mtx dst,f32 xT,f32 yT,f32 zT) +{ + if ( src != dst ) + { + dst[0][0] = src[0][0]; dst[0][1] = src[0][1]; dst[0][2] = src[0][2]; + dst[1][0] = src[1][0]; dst[1][1] = src[1][1]; dst[1][2] = src[1][2]; + dst[2][0] = src[2][0]; dst[2][1] = src[2][1]; dst[2][2] = src[2][2]; + } + + dst[0][3] = src[0][0]*xT + src[0][1]*yT + src[0][2]*zT + src[0][3]; + dst[1][3] = src[1][0]*xT + src[1][1]*yT + src[1][2]*zT + src[1][3]; + dst[2][3] = src[2][0]*xT + src[2][1]*yT + src[2][2]*zT + src[2][3]; +} + +u32 c_guMtxInverse(Mtx src,Mtx inv) +{ + Mtx mTmp; + MtxP m; + f32 det; + + if(src==inv) + m = mTmp; + else + m = inv; + + + // compute the determinant of the upper 3x3 submatrix + det = src[0][0]*src[1][1]*src[2][2] + src[0][1]*src[1][2]*src[2][0] + src[0][2]*src[1][0]*src[2][1] + - src[2][0]*src[1][1]*src[0][2] - src[1][0]*src[0][1]*src[2][2] - src[0][0]*src[2][1]*src[1][2]; + + + // check if matrix is singular + if(det==0.0f)return 0; + + + // compute the inverse of the upper submatrix: + + // find the transposed matrix of cofactors of the upper submatrix + // and multiply by (1/det) + + det = 1.0f / det; + + + m[0][0] = (src[1][1]*src[2][2] - src[2][1]*src[1][2]) * det; + m[0][1] = -(src[0][1]*src[2][2] - src[2][1]*src[0][2]) * det; + m[0][2] = (src[0][1]*src[1][2] - src[1][1]*src[0][2]) * det; + + m[1][0] = -(src[1][0]*src[2][2] - src[2][0]*src[1][2]) * det; + m[1][1] = (src[0][0]*src[2][2] - src[2][0]*src[0][2]) * det; + m[1][2] = -(src[0][0]*src[1][2] - src[1][0]*src[0][2]) * det; + + m[2][0] = (src[1][0]*src[2][1] - src[2][0]*src[1][1]) * det; + m[2][1] = -(src[0][0]*src[2][1] - src[2][0]*src[0][1]) * det; + m[2][2] = (src[0][0]*src[1][1] - src[1][0]*src[0][1]) * det; + + + // compute (invA)*(-C) + m[0][3] = -m[0][0]*src[0][3] - m[0][1]*src[1][3] - m[0][2]*src[2][3]; + m[1][3] = -m[1][0]*src[0][3] - m[1][1]*src[1][3] - m[1][2]*src[2][3]; + m[2][3] = -m[2][0]*src[0][3] - m[2][1]*src[1][3] - m[2][2]*src[2][3]; + + // copy back if needed + if( m == mTmp ) + c_guMtxCopy(mTmp,inv); + + return 1; +} + +void c_guMtxTranspose(Mtx src,Mtx xPose) +{ + Mtx mTmp; + MtxP m; + + if(src==xPose) + m = mTmp; + else + m = xPose; + + + m[0][0] = src[0][0]; m[0][1] = src[1][0]; m[0][2] = src[2][0]; m[0][3] = 0.0f; + m[1][0] = src[0][1]; m[1][1] = src[1][1]; m[1][2] = src[2][1]; m[1][3] = 0.0f; + m[2][0] = src[0][2]; m[2][1] = src[1][2]; m[2][2] = src[2][2]; m[2][3] = 0.0f; + + + // copy back if needed + if(m==mTmp) + c_guMtxCopy(mTmp,xPose); +} + +u32 c_guMtxInvXpose(Mtx src, Mtx xPose) +{ + Mtx mTmp; + MtxP m; + f32 det; + + if(src == xPose) + m = mTmp; + else + m = xPose; + + // Compute the determinant of the upper 3x3 submatrix + det = src[0][0]*src[1][1]*src[2][2] + src[0][1]*src[1][2]*src[2][0] + src[0][2]*src[1][0]*src[2][1] + - src[2][0]*src[1][1]*src[0][2] - src[1][0]*src[0][1]*src[2][2] - src[0][0]*src[2][1]*src[1][2]; + + // Check if matrix is singular + if(det == 0.0f) return 0; + + // Compute the inverse of the upper submatrix: + + // Find the transposed matrix of cofactors of the upper submatrix + // and multiply by (1/det) + + det = 1.0f / det; + + m[0][0] = (src[1][1]*src[2][2] - src[2][1]*src[1][2]) * det; + m[0][1] = -(src[1][0]*src[2][2] - src[2][0]*src[1][2]) * det; + m[0][2] = (src[1][0]*src[2][1] - src[2][0]*src[1][1]) * det; + + m[1][0] = -(src[0][1]*src[2][2] - src[2][1]*src[0][2]) * det; + m[1][1] = (src[0][0]*src[2][2] - src[2][0]*src[0][2]) * det; + m[1][2] = -(src[0][0]*src[2][1] - src[2][0]*src[0][1]) * det; + + m[2][0] = (src[0][1]*src[1][2] - src[1][1]*src[0][2]) * det; + m[2][1] = -(src[0][0]*src[1][2] - src[1][0]*src[0][2]) * det; + m[2][2] = (src[0][0]*src[1][1] - src[1][0]*src[0][1]) * det; + + + // The 4th columns should be zero + m[0][3] = 0.0F; + m[1][3] = 0.0F; + m[2][3] = 0.0F; + + // Copy back if needed + if(m == mTmp) + c_guMtxCopy(mTmp, xPose); + + return 1; +} + +void c_guMtxReflect(Mtx m,guVector *p,guVector *n) +{ + f32 vxy, vxz, vyz, pdotn; + + vxy = -2.0f * n->x * n->y; + vxz = -2.0f * n->x * n->z; + vyz = -2.0f * n->y * n->z; + pdotn = 2.0f * c_guVecDotProduct(p,n); + + m[0][0] = 1.0f - 2.0f * n->x * n->x; + m[0][1] = vxy; + m[0][2] = vxz; + m[0][3] = pdotn * n->x; + + m[1][0] = vxy; + m[1][1] = 1.0f - 2.0f * n->y * n->y; + m[1][2] = vyz; + m[1][3] = pdotn * n->y; + + m[2][0] = vxz; + m[2][1] = vyz; + m[2][2] = 1.0f - 2.0f * n->z * n->z; + m[2][3] = pdotn * n->z; +} + + +void c_guVecAdd(guVector *a,guVector *b,guVector *ab) +{ + ab->x = a->x + b->x; + ab->y = a->y + b->y; + ab->z = a->z + b->z; +} + +void c_guVecSub(guVector *a,guVector *b,guVector *ab) +{ + ab->x = a->x - b->x; + ab->y = a->y - b->y; + ab->z = a->z - b->z; +} + +void c_guVecScale(guVector *src,guVector *dst,f32 scale) +{ + dst->x = src->x * scale; + dst->y = src->y * scale; + dst->z = src->z * scale; +} + + +void c_guVecNormalize(guVector *v) +{ + f32 m; + + m = ((v->x)*(v->x)) + ((v->y)*(v->y)) + ((v->z)*(v->z)); + m = 1/sqrtf(m); + v->x *= m; + v->y *= m; + v->z *= m; +} + +void c_guVecCross(guVector *a,guVector *b,guVector *axb) +{ + guVector vTmp; + + vTmp.x = (a->y*b->z)-(a->z*b->y); + vTmp.y = (a->z*b->x)-(a->x*b->z); + vTmp.z = (a->x*b->y)-(a->y*b->x); + + axb->x = vTmp.x; + axb->y = vTmp.y; + axb->z = vTmp.z; +} + +void c_guVecMultiply(Mtx mt,guVector *src,guVector *dst) +{ + guVector tmp; + + tmp.x = mt[0][0]*src->x + mt[0][1]*src->y + mt[0][2]*src->z + mt[0][3]; + tmp.y = mt[1][0]*src->x + mt[1][1]*src->y + mt[1][2]*src->z + mt[1][3]; + tmp.z = mt[2][0]*src->x + mt[2][1]*src->y + mt[2][2]*src->z + mt[2][3]; + + dst->x = tmp.x; + dst->y = tmp.y; + dst->z = tmp.z; +} + +void c_guVecMultiplySR(Mtx mt,guVector *src,guVector *dst) +{ + guVector tmp; + + tmp.x = mt[0][0]*src->x + mt[0][1]*src->y + mt[0][2]*src->z; + tmp.y = mt[1][0]*src->x + mt[1][1]*src->y + mt[1][2]*src->z; + tmp.z = mt[2][0]*src->x + mt[2][1]*src->y + mt[2][2]*src->z; + + // copy back + dst->x = tmp.x; + dst->y = tmp.y; + dst->z = tmp.z; +} + +f32 c_guVecDotProduct(guVector *a,guVector *b) +{ + f32 dot; + + dot = (a->x * b->x) + (a->y * b->y) + (a->z * b->z); + + return dot; +} + +void c_guQuatAdd(guQuaternion *a,guQuaternion *b,guQuaternion *ab) +{ + ab->x = a->x + b->x; + ab->y = a->x + b->y; + ab->z = a->x + b->z; + ab->w = a->x + b->w; +} + +#ifdef GEKKO +void ps_guQuatAdd(register guQuaternion *a,register guQuaternion *b,register guQuaternion *ab) +{ + register f32 tmp0,tmp1; + + __asm__ __volatile__ ( + "psq_l %0,0(%2),0,0\n" // [ax][ay] + "psq_l %1,0(%3),0,0\n" // [bx][by] + "ps_add %1,%0,%1\n" // [ax+bx][ay+by] + "psq_st %1,0(%4),0,0\n" // X = [ax+bx], Y = [ay+by] + "psq_l %0,8(%2),0,0\n" // [az][aw] + "psq_l %1,8(%3),0,0\n" // [bz][bw] + "ps_add %1,%0,%1\n" // [az+bz][aw+bw] + "psq_st %1,8(%4),0,0" // Z = [az+bz], W = [aw+bw] + : "=&f"(tmp0),"=&f"(tmp1) + : "b"(a),"b"(b),"b"(ab) + : "memory" + ); +} +#endif + +void c_guQuatSub(guQuaternion *a,guQuaternion *b,guQuaternion *ab) +{ + ab->x = a->x - b->x; + ab->y = a->x - b->y; + ab->z = a->x - b->z; + ab->w = a->x - b->w; +} + +#ifdef GEKKO +void ps_guQuatSub(register guQuaternion *a,register guQuaternion *b,register guQuaternion *ab) +{ + register f32 tmp0,tmp1; + + __asm__ __volatile__ ( + "psq_l %0,0(%2),0,0\n" // [ax][ay] + "psq_l %1,0(%3),0,0\n" // [bx][by] + "ps_sub %1,%0,%1\n" // [ax-bx][ay-by] + "psq_st %1,0(%4),0,0\n" // X = [ax-bx], Y = [ay-by] + "psq_l %0,8(%2),0,0\n" // [az][aw] + "psq_l %1,8(%3),0,0\n" // [bz][bw] + "ps_sub %1,%0,%1\n" // [az-bz][aw-bw] + "psq_st %1,8(%4),0,0" // Z = [az-bz], W = [aw-bw] + : "=&f"(tmp0),"=&f"(tmp1) + : "b"(a),"b"(b),"b"(ab) + : "memory" + ); +} +#endif + +void c_guQuatMultiply(guQuaternion *a,guQuaternion *b,guQuaternion *ab) +{ + guQuaternion *r; + guQuaternion ab_tmp; + + if(a==ab || b==ab) r = &ab_tmp; + else r = ab; + + r->w = a->w*b->w - a->x*b->x - a->y*b->y - a->z*b->z; + r->x = a->w*b->x + a->x*b->w + a->y*b->z - a->z*b->y; + r->y = a->w*b->y + a->y*b->w + a->z*b->x - a->x*b->z; + r->z = a->w*b->z + a->z*b->w + a->x*b->y - a->y*b->x; + + if(r==&ab_tmp) *ab = ab_tmp; +} + +#ifdef GEKKO +void ps_guQuatMultiply(register guQuaternion *a,register guQuaternion *b,register guQuaternion *ab) +{ + register f32 aXY,aZW,bXY,bZW; + register f32 tmp0,tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,tmp7; + + __asm__ __volatile__ ( + "psq_l %0,0(%12),0,0\n" // [px][py] + "psq_l %1,8(%12),0,0\n" // [pz][pw] + "psq_l %2,0(%13),0,0\n" // [qx][qy] + "ps_neg %4,%0\n" // [-px][-py] + "psq_l %3,8(%13),0,0\n" // [qz][qw] + "ps_neg %5,%1\n" // [-pz][-pw] + "ps_merge01 %6,%4,%0\n" // [-px][py] + "ps_muls0 %8,%1,%2\n" // [pz*qx][pw*qx] + "ps_muls0 %9,%4,%2\n" // [-px*qx][-py*qx] + "ps_merge01 %7,%5,%1\n" // [-pz][pw] + "ps_muls1 %11,%6,%2\n" // [-px*qy][py*qy] + "ps_madds0 %8,%6,%3,%8\n" // [-px*qz+pz*qx][py*qz+pw*qx] + "ps_muls1 %10,%7,%2\n" // [-pz*qy][pw*qy] + "ps_madds0 %9,%7,%3,%9\n" // [-pz*qz+-px*qx][pw*qz+-py*qx] + "ps_madds1 %11,%5,%3,%11\n" // [-pz*qw+-px*qy][-pw*qw+py*qy] + "ps_merge10 %8,%8,%8\n" // [py*qz+pw*qx][-px*qz+pz*qx] + "ps_madds1 %10,%0,%3,%10\n" // [px*qw+-pz*qy][py*qw+pw*qy] + "ps_merge10 %9,%9,%9\n" // [pw*qz+-py*qx][-pz*qz+-px*qx] + "ps_add %8,%8,%10\n" // [py*qz+pw*qx+px*qw+-pz*qy][-px*qz+pz*qx+py*qw+pw*qy] + "psq_st %8,0(%14),0,0\n" // X = [py*qz+pw*qx+px*qw+-pz*qy], Y = [-px*qz+pz*qx+py*qw+pw*qy] + "ps_sub %9,%9,%11\n" // [pw*qz+-py*qx--pz*qw+-px*qy][-pz*qz+-px*qx--pw*qw+py*qy] + "psq_st %9,8(%14),0,0" // Z = [pw*qz+-py*qx--pz*qw+-px*qy], W = [-pz*qz+-px*qx--pw*qw+py*qy] + : "=&f"(aXY),"=&f"(aZW),"=&f"(bXY),"=&f"(bZW),"=&f"(tmp0),"=&f"(tmp1),"=&f"(tmp2),"=&f"(tmp3),"=&f"(tmp4),"=&f"(tmp5),"=&f"(tmp6),"=&f"(tmp7) + : "b"(a),"b"(b),"b"(ab) + : "memory" + ); +} +#endif + +void c_guQuatNormalize(guQuaternion *a,guQuaternion *d) +{ + f32 dot,scale; + + dot = (a->x*a->x) + (a->y*a->y) + (a->z*a->z) + (a->w*a->w); + if(dot==0.0f) d->x = d->y = d->z = d->w = 0.0f; + else { + scale = 1.0f/sqrtf(dot); + d->x = a->x*scale; + d->y = a->y*scale; + d->z = a->z*scale; + d->w = a->w*scale; + } +} + +#ifdef GEKKO +void ps_guQuatNormalize(register guQuaternion *a,register guQuaternion *d) +{ + register f32 c_zero = 0.0f; + register f32 c_half = 0.5f; + register f32 c_three = 3.0f; + register f32 axy,azw,tmp0,tmp1,tmp2,tmp3; + + __asm__ __volatile__ ( + "psq_l %0,0(%6),0,0\n" // [ax][ay] + "ps_mul %2,%0,%0\n" // [ax*ax][ay*ay] + "psq_l %1,8(%6),0,0\n" // [az][aw] + "ps_madd %2,%1,%1,%2\n" // [az*az+ax*ax][aw*aw+ay*ay] + "ps_sum0 %2,%2,%2,%2\n" // [az*az+ax*ax+aw*aw+ay*ay][aw*aw+ay*ay] + "frsqrte %3,%2\n" // reciprocal sqrt estimated + //Newton-Raphson refinement 1 step: (E/2)*(3 - x*E*E) + "fmul %4,%3,%3\n" // E*E + "fmul %5,%3,%8\n" // E*0.5 = E/2 + "fnmsub %4,%4,%2,%9\n" // -(E*E*x - 3) = (3 - x*E*E) + "fmul %3,%4,%5\n" // (E/2)*(3 - x*E*E) + "ps_sel %3,%2,%3,%10\n" // NaN check: if(mag==0.0f) + "ps_muls0 %0,%0,%3\n" // [ax*rsqmag][ay*rsqmag] + "ps_muls0 %1,%1,%3\n" // [az*rsqmag][aw*rsqmag] + "psq_st %0,0(%7),0,0\n" // X = [az*rsqmag], Y = [aw*rsqmag] + "psq_st %1,8(%7),0,0\n" // Z = [az*rsqmag], W = [aw*rsqmag] + : "=&f"(axy),"=&f"(azw),"=&f"(tmp0),"=&f"(tmp1),"=&f"(tmp2),"=&f"(tmp3) + : "b"(a),"b"(d),"f"(c_half),"f"(c_three),"f"(c_zero) + : "memory" + ); +} +#endif + +void c_guQuatInverse(guQuaternion *a,guQuaternion *d) +{ + f32 mag,nrminv; + + mag = (a->x*a->x) + (a->y*a->y) + (a->z*a->z) + (a->w*a->w); + if(mag==0.0f) mag = 1.0f; + + nrminv = 1.0f/mag; + d->x = -a->x*nrminv; + d->y = -a->y*nrminv; + d->z = -a->z*nrminv; + d->w = a->w*nrminv; +} + +#ifdef GEKKO +void ps_guQuatInverse(register guQuaternion *a,register guQuaternion *d) +{ + register f32 c_one = 1.0f; + register f32 axy,azw,tmp0,tmp1,tmp2,tmp3,tmp4,tmp5; + + __asm__ __volatile__ ( + "psq_l %0,0(%8),0,0\n" // [ax][ay] + "ps_mul %2,%0,%0\n" // [ax*ax][ay*ay] + "ps_sub %3,%10,%10\n" // [1 - 1][1 - 1] + "psq_l %1,8(%8),0,0\n" // [az][aw] + "ps_madd %2,%1,%1,%2\n" // [az*az+ax*ax][aw*aw+ay*ay] + "ps_add %7,%0,%10\n" // [1 + 1][1 + 1] + "ps_sum0 %2,%2,%2,%2\n" // [az*az+ax*ax+aw*aw+ay*ay][aw*aw+ay*ay] + "fcmpu cr0,%2,%3\n" // [az*az+ax*ax+aw*aw+ay*ay] == 0.0f + "beq- 1f\n" + "fres %4,%2\n" // 1.0f/mag + "ps_neg %5,%2\n" // -mag + // Newton-Rapson refinement (x1) : E' = 2E-X*E*E + "ps_nmsub %6,%2,%4,%7\n" // + "ps_mul %4,%4,%6\n" // + "b 2f\n" + "1:\n" + "fmr %4,%10\n" + "2:\n" + "ps_neg %7,%4\n" + "ps_muls1 %5,%4,%1\n" + "ps_muls0 %0,%0,%7\n" + "psq_st %5,12(%9),1,0\n" + "ps_muls0 %6,%1,%7\n" + "psq_st %0,0(%9),0,0\n" + "psq_st %6,8(%9),1,0\n" + : "=&f"(axy),"=&f"(azw),"=&f"(tmp0),"=&f"(tmp1),"=&f"(tmp2),"=&f"(tmp3),"=&f"(tmp4),"=&f"(tmp5) + : "b"(a),"b"(d),"f"(c_one) + ); +} +#endif + +void c_guQuatMtx(guQuaternion *a,Mtx m) +{ + const f32 diag = guMtxRowCol(m,0,0) + guMtxRowCol(m,1,1) + guMtxRowCol(m,2,2) + 1; + + if(diag>0.0f) { + const f32 scale = sqrtf(diag)*2.0f; + + a->x = (guMtxRowCol(m,2,1) - guMtxRowCol(m,1,2))/scale; + a->y = (guMtxRowCol(m,0,2) - guMtxRowCol(m,2,0))/scale; + a->z = (guMtxRowCol(m,1,0) - guMtxRowCol(m,0,1))/scale; + a->w = 0.25f*scale; + } else { + if(guMtxRowCol(m,0,0)>guMtxRowCol(m,1,1) && guMtxRowCol(m,0,0)>guMtxRowCol(m,2,2)) { + const f32 scale = sqrtf(1.0f + guMtxRowCol(m,0,0) + guMtxRowCol(m,1,1) + guMtxRowCol(m,2,2))*2.0f; + + a->x = 0.25f*scale; + a->y = (guMtxRowCol(m,0,1) + guMtxRowCol(m,1,0))/scale; + a->z = (guMtxRowCol(m,2,0) + guMtxRowCol(m,0,2))/scale; + a->w = (guMtxRowCol(m,2,1) - guMtxRowCol(m,1,2))/scale; + } else if(guMtxRowCol(m,1,1)>guMtxRowCol(m,2,2)) { + const f32 scale = sqrtf(1.0f + guMtxRowCol(m,0,0) + guMtxRowCol(m,1,1) + guMtxRowCol(m,2,2))*2.0f; + + a->x = (guMtxRowCol(m,0,1) + guMtxRowCol(m,1,0))/scale; + a->y = 0.25f*scale; + a->z = (guMtxRowCol(m,1,2) + guMtxRowCol(m,2,1))/scale; + a->w = (guMtxRowCol(m,0,2) - guMtxRowCol(m,2,0))/scale; + } else { + const f32 scale = sqrtf(1.0f + guMtxRowCol(m,0,0) + guMtxRowCol(m,1,1) + guMtxRowCol(m,2,2))*2.0f; + + a->x = (guMtxRowCol(m,0,2) + guMtxRowCol(m,2,0))/scale; + a->y = (guMtxRowCol(m,1,2) + guMtxRowCol(m,2,1))/scale; + a->z = 0.25f*scale; + a->w = (guMtxRowCol(m,1,0) - guMtxRowCol(m,0,1))/scale; + } + } + c_guQuatNormalize(a,a); +} + +void c_guMtxQuat(Mtx m,guQuaternion *a) +{ + guMtxRowCol(m,0,0) = 1.0f - 2.0f*a->y*a->y - 2.0f*a->z*a->z; + guMtxRowCol(m,1,0) = 2.0f*a->x*a->y - 2.0f*a->z*a->w; + guMtxRowCol(m,2,0) = 2.0f*a->x*a->z + 2.0f*a->y*a->w; + + guMtxRowCol(m,0,1) = 2.0f*a->x*a->y + 2.0f*a->z*a->w; + guMtxRowCol(m,1,1) = 1.0f - 2.0f*a->x*a->x - 2.0f*a->z*a->z; + guMtxRowCol(m,2,1) = 2.0f*a->z*a->y - 2.0f*a->x*a->w; + + guMtxRowCol(m,0,2) = 2.0f*a->x*a->z - 2.0f*a->y*a->w; + guMtxRowCol(m,1,2) = 2.0f*a->z*a->y + 2.0f*a->x*a->w; + guMtxRowCol(m,2,2) = 1.0f - 2.0f*a->x*a->x - 2.0f*a->y*a->y; +} + +void guVecHalfAngle(guVector *a,guVector *b,guVector *half) +{ + guVector tmp1,tmp2,tmp3; + + tmp1.x = -a->x; + tmp1.y = -a->y; + tmp1.z = -a->z; + + tmp2.x = -b->x; + tmp2.y = -b->y; + tmp2.z = -b->z; + + guVecNormalize(&tmp1); + guVecNormalize(&tmp2); + + guVecAdd(&tmp1,&tmp2,&tmp3); + if(guVecDotProduct(&tmp3,&tmp3)>0.0f) guVecNormalize(&tmp3); + + *half = tmp3; +} diff --git a/wii/libogc/libogc/gu_psasm.S b/wii/libogc/libogc/gu_psasm.S new file mode 100644 index 0000000000..485499c29d --- /dev/null +++ b/wii/libogc/libogc/gu_psasm.S @@ -0,0 +1,723 @@ +#include + +#define A00_A01 fr0 +#define A02_A03 fr1 +#define A10_A11 fr2 +#define A12_A13 fr3 +#define A20_A21 fr4 +#define A22_A23 fr5 + +#define B00_B01 fr6 +#define B02_B03 fr7 +#define B10_B11 fr8 +#define B12_B13 fr9 +#define B20_B21 fr10 +#define B22_B23 fr11 + +#define D00_D01 fr12 +#define D02_D03 fr13 +#define D10_D11 fr14 +#define D12_D13 fr15 +#define D20_D21 fr2 +#define D22_D23 fr0 + +#define UNIT01 fr31 + +#define RET_REG fr1 +#define V1_XY fr2 +#define V1_Z fr3 +#define V2_XY fr4 +#define V2_Z fr5 +#define D1_XY fr6 +#define D1_Z fr7 +#define D2_XY fr8 +#define D2_Z fr9 +#define W1_XY fr10 +#define W1_Z fr11 +#define W2_XY fr12 +#define W2_Z fr13 + + .globl ps_guMtxConcat + //r3 = mtxA, r4 = mtxB, r5 = mtxAB +ps_guMtxConcat: + stwu r1,-64(r1) + psq_l A00_A01,0(r3),0,0 + stfd fr14,8(r1) + psq_l B00_B01,0(r4),0,0 + psq_l B02_B03,8(r4),0,0 + stfd fr15,16(r1) + stfd fr31,40(r1) + psq_l B10_B11,16(r4),0,0 + ps_muls0 D00_D01,B00_B01,A00_A01 + psq_l A10_A11,16(r3),0,0 + ps_muls0 D02_D03,B02_B03,A00_A01 + psq_l UNIT01,Unit01@sdarel(r13),0,0 + ps_muls0 D10_D11,B00_B01,A10_A11 + psq_l B12_B13,24(r4),0,0 + ps_muls0 D12_D13,B02_B03,A10_A11 + psq_l A02_A03,8(r3),0,0 + ps_madds1 D00_D01,B10_B11,A00_A01,D00_D01 + psq_l A12_A13,24(r3),0,0 + ps_madds1 D10_D11,B10_B11,A10_A11,D10_D11 + psq_l B20_B21,32(r4),0,0 + ps_madds1 D02_D03,B12_B13,A00_A01,D02_D03 + psq_l B22_B23,40(r4),0,0 + ps_madds1 D12_D13,B12_B13,A10_A11,D12_D13 + psq_l A20_A21,32(r3),0,0 + psq_l A22_A23,40(r3),0,0 + ps_madds0 D00_D01,B20_B21,A02_A03,D00_D01 + ps_madds0 D02_D03,B22_B23,A02_A03,D02_D03 + ps_madds0 D10_D11,B20_B21,A12_A13,D10_D11 + ps_madds0 D12_D13,B22_B23,A12_A13,D12_D13 + psq_st D00_D01,0(r5),0,0 + ps_muls0 D20_D21,B00_B01,A20_A21 + ps_madds1 D02_D03,UNIT01,A02_A03,D02_D03 + ps_muls0 D22_D23,B02_B03,A20_A21 + psq_st D10_D11,16(r5),0,0 + ps_madds1 D12_D13,UNIT01,A12_A13,D12_D13 + psq_st D02_D03,8(r5),0,0 + ps_madds1 D20_D21,B10_B11,A20_A21,D20_D21 + ps_madds1 D22_D23,B12_B13,A20_A21,D22_D23 + ps_madds0 D20_D21,B20_B21,A22_A23,D20_D21 + lfd fr14,8(r1) + psq_st D12_D13,24(r5),0,0 + ps_madds0 D22_D23,B22_B23,A22_A23,D22_D23 + psq_st D20_D21,32(r5),0,0 + ps_madds1 D22_D23,UNIT01,A22_A23,D22_D23 + lfd fr15,16(r1) + psq_st D22_D23,40(r5),0,0 + lfd fr31,40(r1) + addi r1,r1,64 + blr + + .globl ps_guMtxIdentity + //r3 == mtx +ps_guMtxIdentity: + lfs fr0,Unit01@sdarel(r13) + lfs fr1,Unit01+4@sdarel(r13) + psq_st fr0,8(r3),0,0 + ps_merge01 fr2,fr0,fr1 + psq_st fr0,24(r3),0,0 + ps_merge10 fr3,fr1,fr0 + psq_st fr0,32(r3),0,0 + psq_st fr2,16(r3),0,0 + psq_st fr3,0(r3),0,0 + psq_st fr3,40(r3),0,0 + blr + + .globl ps_guMtxCopy + //r3 = src, r4 = dst +ps_guMtxCopy: + psq_l fr0,0(r3),0,0 + psq_st fr0,0(r4),0,0 + psq_l fr1,8(r3),0,0 + psq_st fr1,8(r4),0,0 + psq_l fr2,16(r3),0,0 + psq_st fr2,16(r4),0,0 + psq_l fr3,24(r3),0,0 + psq_st fr3,24(r4),0,0 + psq_l fr4,32(r3),0,0 + psq_st fr4,32(r4),0,0 + psq_l fr5,40(r3),0,0 + psq_st fr5,40(r4),0,0 + blr + + .globl ps_guMtxTranspose + //r3 = src, r4 = xpose +ps_guMtxTranspose: + lfs fr0,Unit01@sdarel(r13) + psq_l fr1,0(r3),0,0 + stfs fr0,44(r4) + psq_l fr2,16(r3),0,0 + ps_merge00 fr5,fr1,fr2 + psq_l fr3,8(r3),1,0 + ps_merge11 fr6,fr1,fr2 + psq_l fr4,24(r3),1,0 + psq_st fr5,0(r4),0,0 + psq_l fr1,32(r3),0,0 + ps_merge00 fr7,fr3,fr4 + psq_st fr6,16(r4),0,0 + ps_merge00 fr5,fr1,fr0 + psq_st fr7,32(r4),0,0 + ps_merge10 fr6,fr1,fr0 + psq_st fr5,8(r4),0,0 + lfs fr3,40(r3) + psq_st fr6,24(r4),0,0 + stfs fr3,40(r4) + blr + + .globl ps_guMtxInverse + //r3 = src, r4 = inv +ps_guMtxInverse: + psq_l fr0,0(r3),1,0 + psq_l fr1,4(r3),0,0 + psq_l fr2,16(r3),1,0 + ps_merge10 fr6,fr1,fr0 + psq_l fr3,20(r3),0,0 + psq_l fr4,32(r3),1,0 + ps_merge10 fr7,fr3,fr2 + psq_l fr5,36(r3),0,0 + ps_mul fr11,fr3,fr6 + ps_mul fr13,fr5,fr7 + ps_merge10 fr8,fr5,fr4 + ps_msub fr11,fr1,fr7,fr11 + ps_mul fr12,fr1,fr8 + ps_msub fr13,fr3,fr8,fr13 + ps_mul fr10,fr3,fr4 + ps_msub fr12,fr5,fr6,fr12 + ps_mul fr9,fr0,fr5 + ps_mul fr8,fr1,fr2 + ps_sub fr6,fr6,fr6 + ps_msub fr10,fr2,fr5,fr10 + ps_mul fr7,fr0,fr13 + ps_msub fr9,fr1,fr4,fr9 + ps_madd fr7,fr2,fr12,fr7 + ps_msub fr8,fr0,fr3,fr8 + ps_madd fr7,fr4,fr11,fr7 + ps_cmpo0 cr0,fr7,fr6 + bne 0f + li r3,0 + blr + +0: fres fr0,fr7 + ps_add fr6,fr0,fr0 + ps_mul fr5,fr0,fr0 + ps_nmsub fr0,fr7,fr5,fr6 + lfs fr1,12(r3) + ps_muls0 fr13,fr13,fr0 + lfs fr2,28(r3) + ps_muls0 fr12,fr12,fr0 + lfs fr3,44(r3) + ps_muls0 fr11,fr11,fr0 + ps_merge00 fr5,fr13,fr12 + ps_muls0 fr10,fr10,fr0 + ps_merge11 fr4,fr13,fr12 + ps_muls0 fr9,fr9,fr0 + psq_st fr5,0(r4),0,0 + ps_mul fr6,fr13,fr1 + psq_st fr4,16(r4),0,0 + ps_muls0 fr8,fr8,fr0 + ps_madd fr6,fr12,fr2,fr6 + psq_st fr10,32(r4),1,0 + ps_nmadd fr6,fr11,fr3,fr6 + psq_st fr9,36(r4),1,0 + ps_mul fr7,fr10,fr1 + ps_merge00 fr5,fr11,fr6 + psq_st fr8,40(r4),1,0 + ps_merge11 fr4,fr11,fr6 + psq_st fr5,8(r4),0,0 + ps_madd fr7,fr9,fr2,fr7 + psq_st fr4,24(r4),0,0 + ps_nmadd fr7,fr8,fr3,fr7 + li r3,1 + psq_st fr7,44(r4),1,0 + blr + + .globl ps_guMtxInvXpose + //r3 = src, r4 = invx +ps_guMtxInvXpose: + psq_l fr0, 0(r3), 1, 0 + psq_l fr1, 4(r3), 0, 0 + psq_l fr2, 16(r3), 1, 0 + ps_merge10 fr6, fr1, fr0 + psq_l fr3, 20(r3), 0, 0 + psq_l fr4, 32(r3), 1, 0 + ps_merge10 fr7, fr3, fr2 + psq_l fr5, 36(r3), 0, 0 + ps_mul fr11, fr3, fr6 + ps_merge10 fr8, fr5, fr4 + ps_mul fr13, fr5, fr7 + ps_msub fr11, fr1, fr7, fr11 + ps_mul fr12, fr1, fr8 + ps_msub fr13, fr3, fr8, fr13 + ps_msub fr12, fr5, fr6, fr12 + ps_mul fr10, fr3, fr4 + ps_mul fr9, fr0, fr5 + ps_mul fr8, fr1, fr2 + ps_msub fr10, fr2, fr5, fr10 + ps_msub fr9, fr1, fr4, fr9 + ps_msub fr8, fr0, fr3, fr8 + ps_mul fr7, fr0, fr13 + ps_sub fr1, fr1, fr1 + ps_madd fr7, fr2, fr12, fr7 + ps_madd fr7, fr4, fr11, fr7 + ps_cmpo0 cr0, fr7, fr1 + bne 0f + addi r3, 0, 0 + blr + +0: fres fr0, fr7 + psq_st fr1, 12(r4), 1, 0 + ps_add fr6, fr0, fr0 + ps_mul fr5, fr0, fr0 + psq_st fr1, 28(r4), 1, 0 + ps_nmsub fr0, fr7, fr5, fr6 + psq_st fr1, 44(r4), 1, 0 + ps_muls0 fr13, fr13, fr0 + ps_muls0 fr12, fr12, fr0 + ps_muls0 fr11, fr11, fr0 + psq_st fr13, 0(r4), 0, 0 + psq_st fr12, 16(r4), 0, 0 + ps_muls0 fr10, fr10, fr0 + ps_muls0 fr9, fr9, fr0 + psq_st fr11, 32(r4), 0, 0 + psq_st fr10, 8(r4), 1, 0 + ps_muls0 fr8, fr8, fr0 + addi r3, 0, 1 + psq_st fr9, 24(r4), 1, 0 + psq_st fr8, 40(r4), 1, 0 + blr + + .globl ps_guMtxScale + //r3 = mtx,fr1 = xS,fr2 = yS,fr3 = zS +ps_guMtxScale: + lfs fr0,Unit01@sdarel(r13) + stfs fr1,0(r3) + psq_st fr0,4(r3),0,0 + psq_st fr0,12(r3),0,0 + stfs fr2,20(r3) + psq_st fr0,24(r3),0,0 + psq_st fr0,32(r3),0,0 + stfs fr3,40(r3) + stfs fr0,44(r3) + blr + + .globl ps_guMtxScaleApply + //r3 = src,r4 = dst,fr1 = xS,fr2 = yS,fr3 = zS +ps_guMtxScaleApply: + frsp fr1,fr1 + psq_l fr4,0(r3),0,0 + frsp fr2,fr2 + psq_l fr5,8(r3),0,0 + frsp fr3,fr3 + ps_muls0 fr4,fr4,fr1 + psq_l fr6,16(r3),0,0 + ps_muls0 fr5,fr5,fr1 + psq_l fr7,24(r3),0,0 + ps_muls0 fr6,fr6,fr2 + psq_l fr8,32(r3),0,0 + psq_st fr4,0(r4),0,0 + ps_muls0 fr7,fr7,fr2 + psq_l fr2,40(r3),0,0 + psq_st fr5,8(r4),0,0 + ps_muls0 fr8,fr8,fr3 + psq_st fr6,16(r4),0,0 + ps_muls0 fr2,fr2,fr3 + psq_st fr7,24(r4),0,0 + psq_st fr8,32(r4),0,0 + psq_st fr2,40(r4),0,0 + blr + + .globl ps_guMtxApplyScale + //r3 = src,r4 = dst,fr1 = xS,fr2 = yS,fr3 = zS +ps_guMtxApplyScale: + lfs fr6,Unit01+4@sdarel(r13) + frsp fr1,fr1 + psq_l fr4,0(r3),0,0 + frsp fr2,fr2 + psq_l fr5,8(r3),0,0 + frsp fr3,fr3 + ps_merge00 fr10,fr1,fr2 + ps_merge00 fr11,fr3,fr6 + ps_mul fr4,fr4,fr10 + psq_l fr6,16(r3),0,0 + ps_mul fr5,fr5,fr11 + psq_l fr7,24(r3),0,0 + ps_mul fr6,fr6,fr10 + psq_l fr8,32(r3),0,0 + psq_st fr4,0(r4),0,0 + ps_mul fr7,fr7,fr11 + psq_l fr2,40(r3),0,0 + psq_st fr5,8(r4),0,0 + ps_mul fr8,fr8,fr10 + psq_st fr6,16(r4),0,0 + ps_mul fr2,fr2,fr11 + psq_st fr7,24(r4),0,0 + psq_st fr8,32(r4),0,0 + psq_st fr2,40(r4),0,0 + blr + + .globl ps_guMtxTrans + //r3 = mtx,fr1 = xT,fr2 = yT,fr3 = zT +ps_guMtxTrans: + lfs fr4,Unit01@sdarel(r13) + lfs fr5,Unit01+4@sdarel(r13) + stfs fr4,16(r3) + stfs fr1,12(r3) + stfs fr2,28(r3) + psq_st fr4,4(r3),0,0 + psq_st fr4,32(r3),0,0 + stfs fr5,20(r3) + stfs fr4,24(r3) + stfs fr5,40(r3) + stfs fr3,44(r3) + stfs fr5,0(r3) + blr + + .globl ps_guMtxTransApply + //r3 = src,r4 = dst,fr1 = xT,fr2 = yT,fr3 = zT +ps_guMtxTransApply: + psq_l fr4,0(r3),0,0 + frsp fr1,fr1 + psq_l fr5,8(r3),0,0 + frsp fr2,fr2 + psq_l fr7,24(r3),0,0 + frsp fr3,fr3 + psq_l fr8,40(r3),0,0 + ps_sum1 fr5,fr1,fr5,fr5 + psq_l fr6,16(r3),0,0 + ps_sum1 fr7,fr2,fr7,fr7 + psq_l fr9,32(r3),0,0 + ps_sum1 fr8,fr3,fr8,fr8 + psq_st fr4,0(r4),0,0 + psq_st fr5,8(r4),0,0 + psq_st fr6,16(r4),0,0 + psq_st fr7,24(r4),0,0 + psq_st fr9,32(r4),0,0 + psq_st fr8,40(r4),0,0 + blr + + .globl ps_guMtxApplyTrans + //r3 = src,r4 = dst,fr1 = xT,fr2 = yT,fr3 = zT +ps_guMtxApplyTrans: + lfs fr6,Unit01+4@sdarel(r13) + psq_l fr4,0(r3),0,0 + frsp fr1,fr1 + psq_l fr5,8(r3),0,0 + frsp fr2,fr2 + ps_merge00 fr10,fr1,fr2 + psq_l fr7,24(r3),0,0 + frsp fr3,fr3 + ps_mul fr1,fr4,fr10 + ps_merge00 fr11,fr3,fr6 + psq_l fr8,40(r3),0,0 + ps_madd fr2,fr5,fr11,fr1 + psq_l fr6,16(r3),0,0 + ps_sum0 fr3,fr2,fr3,fr2 + psq_l fr9,32(r3),0,0 + ps_mul fr12,fr6,fr10 + psq_st fr4,0(r4),0,0 + ps_madd fr4,fr7,fr11,fr12 + psq_st fr5,8(r4),1,0 + ps_sum0 fr12,fr4,fr12,fr4 + psq_st fr3,12(r4),1,0 + ps_mul fr3,fr9,fr10 + psq_st fr6,16(r4),0,0 + ps_madd fr2,fr8,fr11,fr3 + psq_st fr7,24(r4),1,0 + ps_sum0 fr3,fr2,fr3,fr2 + psq_st fr12,28(r4),1,0 + psq_st fr9,32(r4),0,0 + psq_st fr8,40(r4),1,0 + psq_st fr3,44(r4),1,0 + blr + + .globl ps_guMtxRotTrig + //r3 = mt,r4 = axis,fr1 = sinA,fr2 = cosA +ps_guMtxRotTrig: + frsp fr1,fr1 + lfs fr3,Unit01@sdarel(r13) + frsp fr2,fr2 + lfs fr4,Unit01+4@sdarel(r13) + ori r4,r4,0x20 + ps_neg fr5,fr1 + cmplwi r4,'x' + beq 0f + cmplwi r4,'y' + beq 1f + cmplwi r4,'z' + beq 2f + b 3f +0: + psq_st fr4,0(r3),1,0 + psq_st fr3,4(r3),0,0 + ps_merge00 fr6,fr1,fr2 + psq_st fr3,12(r3),0,0 + ps_merge00 fr7,fr2,fr5 + psq_st fr3,28(r3),0,0 + psq_st fr3,44(r3),1,0 + psq_st fr6,36(r3),0,0 + psq_st fr7,20(r3),0,0 + b 3f +1: + ps_merge00 fr6,fr2,fr3 + ps_merge00 fr7,fr3,fr4 + psq_st fr3,24(r3),0,0 + psq_st fr6,0(r3),0,0 + ps_merge00 fr8,fr5,fr3 + ps_merge00 fr9,fr1,fr3 + psq_st fr6,40(r3),0,0 + psq_st fr7,16(r3),0,0 + psq_st fr9,8(r3),0,0 + psq_st fr8,32(r3),0,0 + b 3f +2: + psq_st fr3,8(r3),0,0 + ps_merge00 fr6,fr1,fr2 + ps_merge00 fr8,fr2,fr5 + psq_st fr3,24(r3),0,0 + psq_st fr3,32(r3),0,0 + ps_merge00 fr7,fr4,fr3 + psq_st fr6,16(r3),0,0 + psq_st fr8,0(r3),0,0 + psq_st fr7,40(r3),0,0 +3: + blr + + .globl __ps_guMtxRotAxisRadInternal + //r3 = mtx, r4 = vec, fr1 = sT, fr2 = cT +__ps_guMtxRotAxisRadInternal: + stwu r1,-64(r1) + frsp fr2,fr2 + psq_l fr3,0(r4),0,0 + frsp fr1,fr1 + stfd fr14,8(r1) + lfs fr11,NrmData+4@sdarel(r13) + lfs fr12,NrmData@sdarel(r13) + ps_mul fr5,fr3,fr3 + lfs fr4,8(r4) + fadds fr10,fr12,fr12 + ps_madd fr6,fr4,fr4,fr5 + fsubs fr14,fr12,fr12 + ps_sum0 fr7,fr6,fr4,fr5 + fsubs fr13,fr10,fr2 + frsqrte fr8,fr7 + fmuls fr5,fr8,fr8 + fmuls fr6,fr8,fr12 + fnmsubs fr5,fr5,fr7,fr11 + fmuls fr8,fr5,fr6 + ps_merge00 fr2,fr2,fr2 + ps_muls0 fr3,fr3,fr8 + ps_muls0 fr4,fr4,fr8 + ps_muls0 fr7,fr3,fr13 + ps_muls0 fr12,fr3,fr1 + ps_muls0 fr8,fr4,fr13 + ps_muls1 fr6,fr7,fr3 + ps_muls0 fr5,fr7,fr3 + ps_muls0 fr7,fr7,fr4 + fnmsubs fr9,fr4,fr1,fr6 + fmadds fr10,fr4,fr1,fr6 + ps_neg fr3,fr12 + ps_sum0 fr11,fr7,fr14,fr12 + ps_sum0 fr5,fr5,fr9,fr2 + ps_sum1 fr6,fr2,fr10,fr6 + ps_sum0 fr9,fr3,fr14,fr7 + psq_st fr11,8(r3),0,0 + ps_sum0 fr3,fr7,fr7,fr3 + psq_st fr5,0(r3),0,0 + ps_muls0 fr8,fr8,fr4 + psq_st fr6,16(r3),0,0 + ps_sum1 fr7,fr12,fr3,fr7 + psq_st fr9,24(r3),0,0 + ps_sum0 fr8,fr8,fr14,fr2 + psq_st fr7,32(r3),0,0 + psq_st fr8,40(r3),0,0 + lfd fr14,8(r1) + addi r1,r1,64 + blr + + .globl ps_guMtxReflect + //r3 = mtx,r4 = vec1,r5 = vec2 +ps_guMtxReflect: + lfs fr0,Unit01+4@sdarel(r13) + psq_l fr1,8(r5),1,0 + psq_l fr2,0(r5),0,0 + psq_l fr3,0(r4),0,0 + ps_nmadd fr5,fr1,fr0,fr1 + psq_l fr4,8(r4),1,0 + ps_nmadd fr6,fr2,fr0,fr2 + ps_muls0 fr7,fr2,fr5 + ps_mul fr8,fr6,fr3 + ps_muls0 fr9,fr2,fr6 + ps_sum0 fr8,fr8,fr8,fr8 + ps_muls1 fr10,fr2,fr6 + psq_st fr7,32(r3),0,0 + ps_sum0 fr2,fr2,fr2,fr0 + ps_nmadd fr8,fr5,fr4,fr8 + ps_sum1 fr10,fr0,fr10,fr10 + psq_st fr9,0(r3),0,0 + ps_muls0 fr11,fr2,fr8 + ps_merge00 fr12,fr5,fr8 + psq_st fr10,16(r3),0,0 + ps_merge00 fr13,fr7,fr11 + ps_muls0 fr12,fr12,fr1 + ps_merge11 fr11,fr7,fr11 + psq_st fr13,8(r3),0,0 + ps_sum0 fr12,fr12,fr12,fr0 + psq_st fr11,24(r3),0,0 + psq_st fr12,40(r3),0,0 + blr + + .globl ps_guVecAdd + //r3 = v1,r4 = v2,r5 = dst +ps_guVecAdd: + psq_l V1_XY,0(r3),0,0 + psq_l V2_XY,0(r4),0,0 + ps_add D1_XY,V1_XY,V2_XY + psq_st D1_XY,0(r5),0,0 + psq_l V1_Z,8(r3),1,0 + psq_l V2_Z,8(r4),1,0 + ps_add D1_Z,V1_Z,V2_Z + psq_st D1_Z,8(r5),1,0 + blr + + .globl ps_guVecSub + //r3 = v1,r4 = v2,r5 = dst +ps_guVecSub: + psq_l V1_XY,0(r3),0,0 + psq_l V2_XY,0(r4),0,0 + ps_sub D1_XY,V1_XY,V2_XY + psq_st D1_XY,0(r5),0,0 + psq_l V1_Z,8(r3),1,0 + psq_l V2_Z,8(r4),1,0 + ps_sub D1_Z,V1_Z,V2_Z + psq_st D1_Z,8(r5),1,0 + blr + + .globl ps_guVecScale + //r3 = src,r4 = dst,fr1 = S +ps_guVecScale: + psq_l fr2,0(r3),0,0 + psq_l fr3,8(r3),1,0 + ps_muls0 fr4,fr2,fr1 + psq_st fr4,0(r4),0,0 + ps_muls0 fr4,fr3,fr1 + psq_st fr4,8(r4),1,0 + blr + + .globl ps_guVecNormalize + //r3 = v +ps_guVecNormalize: + lfs fr0,NrmData@sdarel(r13) + lfs fr1,NrmData+4@sdarel(r13) + psq_l fr2,0(r3),0,0 + ps_mul fr4,fr2,fr2 + psq_l fr3,8(r3),1,0 + ps_madd fr5,fr3,fr3,fr4 + ps_sum0 fr6,fr5,fr3,fr4 + frsqrte fr7,fr6 + fmuls fr8,fr7,fr7 + fmuls fr9,fr7,fr0 + fnmsubs fr8,fr8,fr6,fr1 + fmuls fr7,fr8,fr9 + ps_muls0 fr2,fr2,fr7 + psq_st fr2,0(r3),0,0 + ps_muls0 fr3,fr3,fr7 + psq_st fr3,8(r3),1,0 + blr + + .globl ps_guVecCross + //r3 = v1,r4 = v2,r5 = v12 +ps_guVecCross: + psq_l fr1,0(r4),0,0 + lfs fr2,8(r3) + psq_l fr0,0(r3),0,0 + ps_merge10 fr6,fr1,fr1 + lfs fr3,8(r4) + ps_mul fr4,fr1,fr2 + ps_muls0 fr7,fr1,fr0 + ps_msub fr5,fr0,fr3,fr4 + ps_msub fr8,fr0,fr6,fr7 + ps_merge11 fr9,fr5,fr5 + ps_merge01 fr10,fr5,fr8 + psq_st fr9,0(r5),1,0 + ps_neg fr10,fr10 + psq_st fr10,4(r5),0,0 + blr + + .globl ps_guVecDotProduct + //r3 = vec1,r4 = vec2 +ps_guVecDotProduct: + psq_l fr2,4(r3),0,0 + psq_l fr3,4(r4),0,0 + ps_mul fr2,fr2,fr3 + psq_l fr5,0(r3),0,0 + psq_l fr4,0(r4),0,0 + ps_madd fr3,fr5,fr4,fr2 + ps_sum0 fr1,fr3,fr2,fr2 + blr + + .globl ps_guVecMultiply +ps_guVecMultiply: + psq_l fr0,0(r4),0,0 + psq_l fr2,0(r3),0,0 + psq_l fr1,8(r4),1,0 + ps_mul fr4,fr2,fr0 + psq_l fr3,8(r3),0,0 + ps_madd fr5,fr3,fr1,fr4 + psq_l fr8,16(r3),0,0 + ps_sum0 fr6,fr5,fr6,fr5 + psq_l fr9,24(r3),0,0 + ps_mul fr10,fr8,fr0 + psq_st fr6,0(r5),1,0 + ps_madd fr11,fr9,fr1,fr10 + psq_l fr2,32(r3),0,0 + ps_sum0 fr12,fr11,fr12,fr11 + psq_l fr3,40(r3),0,0 + ps_mul fr4,fr2,fr0 + psq_st fr12,4(r5),1,0 + ps_madd fr5,fr3,fr1,fr4 + ps_sum0 fr6,fr5,fr6,fr5 + psq_st fr6,8(r5),1,0 + blr + + .globl ps_guVecMultiplySR + // r3 = mt, r4 = src, r5 = dst +ps_guVecMultiplySR: + psq_l fr0,0(r3),0,0 // m[0][0], m[0][1] GQR0 = 0 + // fp6 - x y + psq_l fr6,0(r4),0,0 + psq_l fr2,16(r3),0,0 // m[1][0], m[1][1] + // fp8 = m00x m01y // next X + ps_mul fr8,fr0,fr6 + psq_l fr4,32(r3),0,0 // m[2][0], m[2][1] + // fp10 = m10x m11y // next Y + ps_mul fr10,fr2,fr6 + psq_l fr7,8(r4),1,0 // fp7 - z,1.0 + // fp12 = m20x m21y // next Z + ps_mul fr12,fr4,fr6 // YYY last FP6 usage + psq_l fr3,24(r3),0,0 // m[1][2], m[1][3] + ps_sum0 fr8,fr8,fr8,fr8 + psq_l fr5,40(r3),0,0 // m[2][2], m[2][3] + ps_sum0 fr10,fr10,fr10,fr10 + psq_l fr1,8(r3),0,0 // m[0][2], m[0][3] + ps_sum0 fr12,fr12,fr12,fr12 + ps_madd fr9,fr1,fr7,fr8 + psq_st fr9,0(r5),1,0 // store X + ps_madd fr11,fr3,fr7,fr10 + psq_st fr11,4(r5),1,0 // store Y + ps_madd fr13,fr5,fr7,fr12 + psq_st fr13,8(r5),1,0 // sore Z + blr + + .globl ps_quQuatScale + //r3 = q,r4 = r, fr1 = scale +ps_guQuatScale: + psq_l fr4,0(r3),0,0 + psq_l fr5,8(r3),0,0 + ps_muls0 fr4,fr4,fr1 + psq_st fr4,0(r4),0,0 + ps_muls0 fr5,fr5,fr1 + psq_st fr5,8(r4),0,0 + blr + + .globl ps_guQuatDotProduct + //r3 = p, r4 = q ; fr1 = res +ps_guQuatDotProduct: + psq_l fr2,0(r3),0,0 + psq_l fr4,0(r4),0,0 + ps_mul fr1,fr2,fr4 + psq_l fr3,8(r3),0,0 + psq_l fr5,8(r4),0,0 + ps_madd fr1,fr3,fr5,fr1 + ps_sum0 fr1,fr1,fr1,fr1 + blr + + .section .sdata + .balign 16 +Unit01: + .float 0.0, 1.0 +QuatEpsilon: + .float 0.00001 +NrmData: + .float 0.5, 3.0 diff --git a/wii/libogc/libogc/gx.c b/wii/libogc/libogc/gx.c new file mode 100644 index 0000000000..ff9b413e78 --- /dev/null +++ b/wii/libogc/libogc/gx.c @@ -0,0 +1,5099 @@ +#include +#include +#include +#include +#include "asm.h" +#include "processor.h" +#include "irq.h" +#include "lwp.h" +#include "system.h" +#include "video.h" +#include "video_types.h" +#include "lwp_watchdog.h" +#include "gx.h" +#include "gx_regdef.h" + +#define TEXCACHE_TESTING + + +#define GX_FINISH 2 + +#if defined(HW_DOL) + #define LARGE_NUMBER (-1048576.0f) +#elif defined(HW_RVL) + #define LARGE_NUMBER (-1.0e+18f) +#endif + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +#define GX_LOAD_BP_REG(x) \ + do { \ + wgPipe->U8 = 0x61; \ + asm volatile ("" ::: "memory" ); \ + wgPipe->U32 = (u32)(x); \ + asm volatile ("" ::: "memory" ); \ + } while(0) + +#define GX_LOAD_CP_REG(x, y) \ + do { \ + wgPipe->U8 = 0x08; \ + asm volatile ("" ::: "memory" ); \ + wgPipe->U8 = (u8)(x); \ + asm volatile ("" ::: "memory" ); \ + wgPipe->U32 = (u32)(y); \ + asm volatile ("" ::: "memory" ); \ + } while(0) + +#define GX_LOAD_XF_REG(x, y) \ + do { \ + wgPipe->U8 = 0x10; \ + asm volatile ("" ::: "memory" ); \ + wgPipe->U32 = (u32)((x)&0xffff); \ + asm volatile ("" ::: "memory" ); \ + wgPipe->U32 = (u32)(y); \ + asm volatile ("" ::: "memory" ); \ + } while(0) + +#define GX_LOAD_XF_REGS(x, n) \ + do { \ + wgPipe->U8 = 0x10; \ + asm volatile ("" ::: "memory" ); \ + wgPipe->U32 = (u32)(((((n)&0xffff)-1)<<16)|((x)&0xffff)); \ + asm volatile ("" ::: "memory" ); \ + } while(0) + +#define XY(x, y) (((y) << 10) | (x)) + +#define GX_DEFAULT_BG {64,64,64,255} +#define BLACK {0,0,0,0} +#define WHITE {255,255,255,255} + +WGPipe* const wgPipe = (WGPipe*)0xCC008000; + +static GXFifoObj _gpfifo; +static GXFifoObj _cpufifo; +static GXFifoObj _gxfifoobj; +static GXFifoObj _gx_dl_fifoobj; +static GXFifoObj _gx_old_cpufifo; +static void *_gxcurrbp = NULL; +static lwp_t _gxcurrentlwp = LWP_THREAD_NULL; + +static u32 _gxcpufifoready = 0; +static u32 _gxgpfifoready = 0; +static u32 _cpgplinked = 0; +static u16 _gxgpstatus = 0; +static vu32 _gxoverflowsuspend = 0; +static vu32 _gxoverflowcount = 0; +static vu32 _gxfinished = 0; +static lwpq_t _gxwaitfinish; + +static GXBreakPtCallback breakPtCB = NULL; +static GXDrawDoneCallback drawDoneCB = NULL; +static GXDrawSyncCallback tokenCB = NULL; + +static GXTexRegionCallback regionCB = NULL; +static GXTlutRegionCallback tlut_regionCB = NULL; + +static vu32* const _piReg = (u32*)0xCC003000; +static vu16* const _cpReg = (u16*)0xCC000000; +static vu16* const _peReg = (u16*)0xCC001000; +static vu16* const _memReg = (u16*)0xCC004000; + +static u8 _gxtevcolid[9] = {0,1,0,1,0,1,7,5,6}; +static u8 _gxtexmode0ids[8] = {0x80,0x81,0x82,0x83,0xA0,0xA1,0xA2,0xA3}; +static u8 _gxtexmode1ids[8] = {0x84,0x85,0x86,0x87,0xA4,0xA5,0xA6,0xA7}; +static u8 _gxteximg0ids[8] = {0x88,0x89,0x8A,0x8B,0xA8,0xA9,0xAA,0xAB}; +static u8 _gxteximg1ids[8] = {0x8C,0x8D,0x8E,0x8F,0xAC,0xAD,0xAE,0xAF}; +static u8 _gxteximg2ids[8] = {0x90,0x91,0x92,0x93,0xB0,0xB1,0xB2,0xB3}; +static u8 _gxteximg3ids[8] = {0x94,0x95,0x96,0x97,0xB4,0xB5,0xB6,0xB7}; +static u8 _gxtextlutids[8] = {0x98,0x99,0x9A,0x9B,0xB8,0xB9,0xBA,0xBB}; + +#if defined(HW_RVL) +static u32 _gxtexregionaddrtable[48] = +{ + 0x00000000,0x00010000,0x00020000,0x00030000, + 0x00040000,0x00050000,0x00060000,0x00070000, + 0x00008000,0x00018000,0x00028000,0x00038000, + 0x00048000,0x00058000,0x00068000,0x00078000, + 0x00000000,0x00090000,0x00020000,0x000B0000, + 0x00040000,0x00098000,0x00060000,0x000B8000, + 0x00080000,0x00010000,0x000A0000,0x00030000, + 0x00088000,0x00050000,0x000A8000,0x00070000, + 0x00000000,0x00090000,0x00020000,0x000B0000, + 0x00040000,0x00090000,0x00060000,0x000B0000, + 0x00080000,0x00010000,0x000A0000,0x00030000, + 0x00080000,0x00050000,0x000A0000,0x00070000 +}; +#endif + + +extern u8 __gxregs[]; +static struct __gx_regdef *__gx = (struct __gx_regdef*)__gxregs; +static u8 _gx_saved_data[STRUCT_REGDEF_SIZE] ATTRIBUTE_ALIGN(32); + +static s32 __gx_onreset(s32 final); + +static sys_resetinfo __gx_resetinfo = { + {}, + __gx_onreset, + 127 +}; + +static __inline__ BOOL IsWriteGatherBufferEmpty() +{ + return !(mfwpar()&1); +} + +static __inline__ void DisableWriteGatherPipe() +{ + mthid2((mfhid2()&~0x40000000)); +} + +static __inline__ void EnableWriteGatherPipe() +{ + mtwpar(0x0C008000); + mthid2((mfhid2()|0x40000000)); +} + +static __inline__ void __GX_ResetWriteGatherPipe() +{ + while(mfwpar()&1); + mtwpar(0x0C008000); +} + +static __inline__ void __GX_FifoLink(u8 enable) +{ + __gx->cpCRreg = ((__gx->cpCRreg&~0x10)|(_SHIFTL(enable,4,1))); + _cpReg[1] = __gx->cpCRreg; +} + +static __inline__ void __GX_WriteFifoIntReset(u8 inthi,u8 intlo) +{ + __gx->cpCLreg = ((__gx->cpCLreg&~0x03)|(_SHIFTL(intlo,1,1))|(inthi&1)); + _cpReg[2] = __gx->cpCLreg; +} + +static __inline__ void __GX_WriteFifoIntEnable(u8 inthi, u8 intlo) +{ + __gx->cpCRreg = ((__gx->cpCRreg&~0x0C)|(_SHIFTL(intlo,3,1))|(_SHIFTL(inthi,2,1))); + _cpReg[1] = __gx->cpCRreg; +} + +static __inline__ void __GX_FifoReadEnable() +{ + __gx->cpCRreg = ((__gx->cpCRreg&~0x01)|1); + _cpReg[1] = __gx->cpCRreg; +} + +static __inline__ void __GX_FifoReadDisable() +{ + __gx->cpCRreg = ((__gx->cpCRreg&~0x01)|0); + _cpReg[1] = __gx->cpCRreg; +} + +static s32 __gx_onreset(s32 final) +{ + if(final==FALSE) { + GX_Flush(); + GX_AbortFrame(); + } + return 1; +} + +static u32 __GX_IsGPFifoReady() +{ + return _gxgpfifoready; +} + +static u32 __GX_CPGPLinkCheck() +{ + struct __gxfifo *gpfifo = (struct __gxfifo*)&_gpfifo; + struct __gxfifo *cpufifo = (struct __gxfifo*)&_cpufifo; + + if(!_gxcpufifoready || !_gxgpfifoready) return 0; + + if((cpufifo->buf_start==gpfifo->buf_start)&&(cpufifo->buf_end==gpfifo->buf_end)) return 1; + + return 0; +} + +static void __GX_InitRevBits() +{ + s32 i; + + i=0; + while(i<8) { + __gx->VAT0reg[i] = 0x40000000; + __gx->VAT1reg[i] = 0x80000000; + GX_LOAD_CP_REG((0x0080|i),__gx->VAT1reg[i]); + i++; + } + + GX_LOAD_XF_REG(0x1000,0x3f); + GX_LOAD_XF_REG(0x1012,0x01); + + GX_LOAD_BP_REG(0x5800000f); + +} + +static void __GX_WaitAbort(u32 delay) +{ + u64 start,end; + + start = gettime(); + while(1) { + end = gettime(); + if(diff_ticks(start,end)>=(u64)delay) break; + }; +} + +#ifdef HW_RVL +static u32 __GX_ReadMemCounterU32(u32 reg) +{ + u16 lcnt,ucnt,tmp; + + tmp = _memReg[reg]; + do { + ucnt = tmp; + lcnt = _memReg[reg+1]; + tmp = _memReg[reg]; + } while(tmp!=ucnt); + return (u32)((ucnt<<16)|lcnt); +} + +static void __GX_WaitAbortPixelEngine() +{ + u32 cnt,tmp; + + cnt = __GX_ReadMemCounterU32(39); + do { + tmp = cnt; + __GX_WaitAbort(8); + cnt = __GX_ReadMemCounterU32(39); + } while(cnt!=tmp); +} + +static void __GX_Abort() +{ + if(__gx->gxFifoInited && __GX_IsGPFifoReady()) + __GX_WaitAbortPixelEngine(); + + _piReg[6] = 1; + __GX_WaitAbort(50); + + _piReg[6] = 0; + __GX_WaitAbort(5); +} +#endif + +static void __GX_SaveFifo() +{ + s32 rdwt_dst; + u32 level,val; + struct __gxfifo *cpufifo = (struct __gxfifo*)&_cpufifo; + struct __gxfifo *gpfifo = (struct __gxfifo*)&_gpfifo; + + _CPU_ISR_Disable(level); + + if(_gxcpufifoready) { + val = _piReg[0x05]; + cpufifo->wt_ptr = (u32)MEM_PHYSICAL_TO_K0((val&0x1FFFFFE0)); + cpufifo->fifo_wrap = ((val&0x20000000)==0x20000000); + } + + if(_gxgpfifoready) { + gpfifo->rd_ptr = (u32)MEM_PHYSICAL_TO_K0(_SHIFTL(_cpReg[29],16,16)|(_cpReg[28]&0xffff)); + gpfifo->rdwt_dst = (_SHIFTL(_cpReg[25],16,16)|(_cpReg[24]&0xffff)); + } + + if(_cpgplinked) { + cpufifo->rd_ptr = gpfifo->rd_ptr; + cpufifo->rdwt_dst = gpfifo->rdwt_dst; + gpfifo->wt_ptr = cpufifo->wt_ptr; + } else if(_gxcpufifoready) { + rdwt_dst = (cpufifo->wt_ptr - cpufifo->rd_ptr); + if(rdwt_dst<0) cpufifo->rdwt_dst = (cpufifo->rdwt_dst + cpufifo->size); + else cpufifo->rdwt_dst = rdwt_dst; + } + + _CPU_ISR_Restore(level); +} + +static void __GX_CleanGPFifo() +{ + u32 level; + struct __gxfifo *gpfifo = (struct __gxfifo*)&_gpfifo; + struct __gxfifo *cpufifo = (struct __gxfifo*)&_cpufifo; + + if(!_gxgpfifoready) return; + + _CPU_ISR_Disable(level); + __GX_FifoReadDisable(); + __GX_WriteFifoIntEnable(FALSE,FALSE); + + gpfifo->rd_ptr = gpfifo->wt_ptr; + gpfifo->rdwt_dst = 0; + + /* setup rd<->wd dist */ + _cpReg[24] = _SHIFTL(gpfifo->rdwt_dst,0,16); + _cpReg[25] = _SHIFTR(gpfifo->rdwt_dst,16,16); + + /* setup wt ptr */ + _cpReg[26] = _SHIFTL(MEM_VIRTUAL_TO_PHYSICAL(gpfifo->wt_ptr),0,16); + _cpReg[27] = _SHIFTR(MEM_VIRTUAL_TO_PHYSICAL(gpfifo->wt_ptr),16,16); + + /* setup rd ptr */ + _cpReg[28] = _SHIFTL(MEM_VIRTUAL_TO_PHYSICAL(gpfifo->rd_ptr),0,16); + _cpReg[29] = _SHIFTR(MEM_VIRTUAL_TO_PHYSICAL(gpfifo->rd_ptr),16,16); + ppcsync(); + + if(_cpgplinked) { + cpufifo->rd_ptr = gpfifo->rd_ptr; + cpufifo->wt_ptr = gpfifo->wt_ptr; + cpufifo->rdwt_dst = gpfifo->rdwt_dst; + + _piReg[5] = (cpufifo->wt_ptr&0x1FFFFFE0); + __GX_WriteFifoIntEnable(TRUE,FALSE); + __GX_FifoLink(TRUE); + } + __gx->cpCRreg &= ~0x22; + _cpReg[1] = __gx->cpCRreg; + breakPtCB = NULL; + + __GX_WriteFifoIntReset(TRUE,TRUE); + __GX_FifoReadEnable(); + _CPU_ISR_Restore(level); +} + +static void __GXOverflowHandler() +{ + if(!_gxoverflowsuspend) { + _gxoverflowsuspend = 1; + _gxoverflowcount++; + __GX_WriteFifoIntEnable(GX_DISABLE,GX_ENABLE); + __GX_WriteFifoIntReset(GX_TRUE,GX_FALSE); + LWP_SuspendThread(_gxcurrentlwp); + } +} + +static void __GXUnderflowHandler() +{ + if(_gxoverflowsuspend) { + _gxoverflowsuspend = 0; + LWP_ResumeThread(_gxcurrentlwp); + __GX_WriteFifoIntReset(GX_TRUE,GX_TRUE); + __GX_WriteFifoIntEnable(GX_ENABLE,GX_DISABLE); + } +} + +static void __GXCPInterruptHandler(u32 irq,void *ctx) +{ + __gx->cpSRreg = _cpReg[0]; + + if((__gx->cpCRreg&0x08) && (__gx->cpSRreg&0x02)) + __GXUnderflowHandler(); + + if((__gx->cpCRreg&0x04) && (__gx->cpSRreg&0x01)) + __GXOverflowHandler(); + + if((__gx->cpCRreg&0x20) && (__gx->cpSRreg&0x10)) { + __gx->cpCRreg &= ~0x20; + _cpReg[1] = __gx->cpCRreg; + if(breakPtCB) + breakPtCB(); + } +} + +static void __GXTokenInterruptHandler(u32 irq,void *ctx) +{ + u16 token = _peReg[7]; + + if(tokenCB) + tokenCB(token); + + _peReg[5] = (_peReg[5]&~0x04)|0x04; +} + +static void __GXFinishInterruptHandler(u32 irq,void *ctx) +{ + _peReg[5] = (_peReg[5]&~0x08)|0x08; + _gxfinished = 1; + + if(drawDoneCB) + drawDoneCB(); + + LWP_ThreadBroadcast(_gxwaitfinish); +} + +static void __GX_PEInit() +{ + IRQ_Request(IRQ_PI_PETOKEN,__GXTokenInterruptHandler,NULL); + __UnmaskIrq(IRQMASK(IRQ_PI_PETOKEN)); + + IRQ_Request(IRQ_PI_PEFINISH,__GXFinishInterruptHandler,NULL); + __UnmaskIrq(IRQMASK(IRQ_PI_PEFINISH)); + + _peReg[5] = 0x0F; +} + +static void __GX_FifoInit() +{ + IRQ_Request(IRQ_PI_CP,__GXCPInterruptHandler,NULL); + __UnmaskIrq(IRQMASK(IRQ_PI_CP)); + + memset(&_cpufifo,0,sizeof(GXFifoObj)); + memset(&_gpfifo,0,sizeof(GXFifoObj)); + + _gxcpufifoready = 0; + _gxgpfifoready = 0; + _cpgplinked = 0; + _gxoverflowsuspend = 0; + _gxcurrentlwp = LWP_GetSelf(); +} + +static void __GX_SetTmemConfig(u8 nr) +{ + if(nr==0) { + // Set_TextureImage0-3, GXTexMapID=0-3 tmem_offset=00000000, cache_width=32 kb, cache_height=32 kb, image_type=cached + GX_LOAD_BP_REG(0x8c0d8000); + GX_LOAD_BP_REG(0x900dc000); + GX_LOAD_BP_REG(0x8d0d8400); + GX_LOAD_BP_REG(0x910dc400); + GX_LOAD_BP_REG(0x8e0d8800); + GX_LOAD_BP_REG(0x920dc800); + GX_LOAD_BP_REG(0x8f0d8c00); + GX_LOAD_BP_REG(0x930dcc00); + + // Set_TextureImage0-3, GXTexMapID=4-7 tmem_offset=00010000, cache_width=32 kb, cache_height=32 kb, image_type=cached + GX_LOAD_BP_REG(0xac0d9000); + GX_LOAD_BP_REG(0xb00dd000); + GX_LOAD_BP_REG(0xad0d9400); + GX_LOAD_BP_REG(0xb10dd400); + GX_LOAD_BP_REG(0xae0d9800); + GX_LOAD_BP_REG(0xb20dd800); + GX_LOAD_BP_REG(0xaf0d9c00); + GX_LOAD_BP_REG(0xb30ddc00); + + return; + } + + if(nr==1) { + // Set_TextureImage0-3, GXTexMapID=0-3 tmem_offset=00000000, cache_width=32 kb, cache_height=32 kb, image_type=cached + GX_LOAD_BP_REG(0x8c0d8000); + GX_LOAD_BP_REG(0x900dc000); + GX_LOAD_BP_REG(0x8d0d8800); + GX_LOAD_BP_REG(0x910dc800); + GX_LOAD_BP_REG(0x8e0d9000); + GX_LOAD_BP_REG(0x920dd000); + GX_LOAD_BP_REG(0x8f0d9800); + GX_LOAD_BP_REG(0x930dd800); + + // Set_TextureImage0-3, GXTexMapID=4-7 tmem_offset=00010000, cache_width=32 kb, cache_height=32 kb, image_type=cached + GX_LOAD_BP_REG(0xac0da000); + GX_LOAD_BP_REG(0xb00de000); + GX_LOAD_BP_REG(0xad0da800); + GX_LOAD_BP_REG(0xb10de800); + GX_LOAD_BP_REG(0xae0db000); + GX_LOAD_BP_REG(0xb20df000); + GX_LOAD_BP_REG(0xaf0db800); + GX_LOAD_BP_REG(0xb30df800); + + return; + } + + if(nr==2) { + // Set_TextureImage0-3, GXTexMapID=0-3 tmem_offset=00000000, cache_width=32 kb, cache_height=32 kb, image_type=cached + GX_LOAD_BP_REG(0x8c0d8000); + GX_LOAD_BP_REG(0x900dc000); + GX_LOAD_BP_REG(0x8d0d8800); + GX_LOAD_BP_REG(0x910dc800); + GX_LOAD_BP_REG(0x8e0d9000); + GX_LOAD_BP_REG(0x920dd000); + GX_LOAD_BP_REG(0x8f0d9800); + GX_LOAD_BP_REG(0x930dd800); + + // Set_TextureImage0-3, GXTexMapID=4-7 tmem_offset=00010000, cache_width=32 kb, cache_height=32 kb, image_type=cached + GX_LOAD_BP_REG(0xac0da000); + GX_LOAD_BP_REG(0xb00dc400); + GX_LOAD_BP_REG(0xad0da800); + GX_LOAD_BP_REG(0xb10dcc00); + GX_LOAD_BP_REG(0xae0db000); + GX_LOAD_BP_REG(0xb20dd400); + GX_LOAD_BP_REG(0xaf0db800); + GX_LOAD_BP_REG(0xb30ddc00); + + return; + } +} + +#if defined(HW_RVL) +static GXTexRegion* __GXDefTexRegionCallback(GXTexObj *obj,u8 mapid) +{ + u32 fmt,mipmap; + GXTexRegion *ret = NULL; + + fmt = GX_GetTexObjFmt(obj); + mipmap = GX_GetTexObjMipMap(obj); + if(fmt>=GX_TF_CI4 && fmt<=GX_TF_CI14) + return &__gx->texRegion[mapid]; + else if(fmt==GX_TF_CMPR) + ret = &__gx->texRegion[mapid]; + else + ret = &__gx->texRegion[mapid+8]; + + if(mipmap) ret = &__gx->texRegion[mapid+16]; + + return ret; +} +#else +static GXTexRegion* __GXDefTexRegionCallback(GXTexObj *obj,u8 mapid) +{ + u32 fmt; + u32 idx; + static u32 regionA = 0; + static u32 regionB = 0; + GXTexRegion *ret = NULL; + + fmt = GX_GetTexObjFmt(obj); + if(fmt==0x0008 || fmt==0x0009 || fmt==0x000a) { + idx = regionB++; + ret = &__gx->texRegion[(idx&3)+8]; + } else { + idx = regionA++; + ret = &__gx->texRegion[(idx&7)]; + } + return ret; +} +#endif + +static GXTlutRegion* __GXDefTlutRegionCallback(u32 tlut_name) +{ + return &__gx->tlutRegion[tlut_name]; +} + +static void __GX_InitGX() +{ + s32 i; + u32 flag; + GXRModeObj *rmode; + Mtx identity_matrix = + { + {1,0,0,0}, + {0,1,0,0}, + {0,0,1,0} + }; + + rmode = VIDEO_GetPreferredMode(NULL); + + GX_SetCopyClear((GXColor)BLACK,0xffffff); + GX_SetTexCoordGen(GX_TEXCOORD0,GX_TG_MTX2x4,GX_TG_TEX0,GX_IDENTITY); + GX_SetTexCoordGen(GX_TEXCOORD1,GX_TG_MTX2x4,GX_TG_TEX1,GX_IDENTITY); + GX_SetTexCoordGen(GX_TEXCOORD2,GX_TG_MTX2x4,GX_TG_TEX2,GX_IDENTITY); + GX_SetTexCoordGen(GX_TEXCOORD3,GX_TG_MTX2x4,GX_TG_TEX3,GX_IDENTITY); + GX_SetTexCoordGen(GX_TEXCOORD4,GX_TG_MTX2x4,GX_TG_TEX4,GX_IDENTITY); + GX_SetTexCoordGen(GX_TEXCOORD5,GX_TG_MTX2x4,GX_TG_TEX5,GX_IDENTITY); + GX_SetTexCoordGen(GX_TEXCOORD6,GX_TG_MTX2x4,GX_TG_TEX6,GX_IDENTITY); + GX_SetTexCoordGen(GX_TEXCOORD7,GX_TG_MTX2x4,GX_TG_TEX7,GX_IDENTITY); + GX_SetNumTexGens(1); + GX_ClearVtxDesc(); + GX_InvVtxCache(); + + GX_SetLineWidth(6,GX_TO_ZERO); + GX_SetPointSize(6,GX_TO_ZERO); + + GX_EnableTexOffsets(GX_TEXCOORD0,GX_DISABLE,GX_DISABLE); + GX_EnableTexOffsets(GX_TEXCOORD1,GX_DISABLE,GX_DISABLE); + GX_EnableTexOffsets(GX_TEXCOORD2,GX_DISABLE,GX_DISABLE); + GX_EnableTexOffsets(GX_TEXCOORD3,GX_DISABLE,GX_DISABLE); + GX_EnableTexOffsets(GX_TEXCOORD4,GX_DISABLE,GX_DISABLE); + GX_EnableTexOffsets(GX_TEXCOORD5,GX_DISABLE,GX_DISABLE); + GX_EnableTexOffsets(GX_TEXCOORD6,GX_DISABLE,GX_DISABLE); + GX_EnableTexOffsets(GX_TEXCOORD7,GX_DISABLE,GX_DISABLE); + + GX_LoadPosMtxImm(identity_matrix,GX_PNMTX0); + GX_LoadNrmMtxImm(identity_matrix,GX_PNMTX0); + GX_SetCurrentMtx(GX_PNMTX0); + GX_LoadTexMtxImm(identity_matrix,GX_IDENTITY,GX_MTX3x4); + GX_LoadTexMtxImm(identity_matrix,GX_DTTIDENTITY,GX_MTX3x4); + + GX_SetViewport(0,0,rmode->fbWidth,rmode->efbHeight,0,1); + GX_SetCoPlanar(GX_DISABLE); + GX_SetCullMode(GX_CULL_BACK); + GX_SetClipMode(GX_CLIP_ENABLE); + + GX_SetScissor(0,0,rmode->fbWidth,rmode->efbHeight); + GX_SetScissorBoxOffset(0,0); + + GX_SetNumChans(0); + + GX_SetChanCtrl(GX_COLOR0A0,GX_DISABLE,GX_SRC_REG,GX_SRC_VTX,GX_LIGHTNULL,GX_DF_NONE,GX_AF_NONE); + GX_SetChanAmbColor(GX_COLOR0A0,(GXColor)BLACK); + GX_SetChanMatColor(GX_COLOR0A0,(GXColor)WHITE); + + GX_SetChanCtrl(GX_COLOR1A1,GX_DISABLE,GX_SRC_REG,GX_SRC_VTX,GX_LIGHTNULL,GX_DF_NONE,GX_AF_NONE); + GX_SetChanAmbColor(GX_COLOR1A1,(GXColor)BLACK); + GX_SetChanMatColor(GX_COLOR1A1,(GXColor)WHITE); + + GX_InvalidateTexAll(); + GX_SetTexRegionCallback(__GXDefTexRegionCallback); + GX_SetTlutRegionCallback(__GXDefTlutRegionCallback); + + GX_SetTevOrder(GX_TEVSTAGE0,GX_TEXCOORD0,GX_TEXMAP0,GX_COLOR0A0); + GX_SetTevOrder(GX_TEVSTAGE1,GX_TEXCOORD1,GX_TEXMAP1,GX_COLOR0A0); + GX_SetTevOrder(GX_TEVSTAGE2,GX_TEXCOORD2,GX_TEXMAP2,GX_COLOR0A0); + GX_SetTevOrder(GX_TEVSTAGE3,GX_TEXCOORD3,GX_TEXMAP3,GX_COLOR0A0); + GX_SetTevOrder(GX_TEVSTAGE4,GX_TEXCOORD4,GX_TEXMAP4,GX_COLOR0A0); + GX_SetTevOrder(GX_TEVSTAGE5,GX_TEXCOORD5,GX_TEXMAP5,GX_COLOR0A0); + GX_SetTevOrder(GX_TEVSTAGE6,GX_TEXCOORD6,GX_TEXMAP6,GX_COLOR0A0); + GX_SetTevOrder(GX_TEVSTAGE7,GX_TEXCOORD7,GX_TEXMAP7,GX_COLOR0A0); + GX_SetTevOrder(GX_TEVSTAGE8,GX_TEXCOORDNULL,GX_TEXMAP_NULL,GX_COLORNULL); + GX_SetTevOrder(GX_TEVSTAGE9,GX_TEXCOORDNULL,GX_TEXMAP_NULL,GX_COLORNULL); + GX_SetTevOrder(GX_TEVSTAGE10,GX_TEXCOORDNULL,GX_TEXMAP_NULL,GX_COLORNULL); + GX_SetTevOrder(GX_TEVSTAGE11,GX_TEXCOORDNULL,GX_TEXMAP_NULL,GX_COLORNULL); + GX_SetTevOrder(GX_TEVSTAGE12,GX_TEXCOORDNULL,GX_TEXMAP_NULL,GX_COLORNULL); + GX_SetTevOrder(GX_TEVSTAGE13,GX_TEXCOORDNULL,GX_TEXMAP_NULL,GX_COLORNULL); + GX_SetTevOrder(GX_TEVSTAGE14,GX_TEXCOORDNULL,GX_TEXMAP_NULL,GX_COLORNULL); + GX_SetTevOrder(GX_TEVSTAGE15,GX_TEXCOORDNULL,GX_TEXMAP_NULL,GX_COLORNULL); + GX_SetNumTevStages(1); + GX_SetTevOp(GX_TEVSTAGE0,GX_REPLACE); + GX_SetAlphaCompare(GX_ALWAYS,0,GX_AOP_AND,GX_ALWAYS,0); + GX_SetZTexture(GX_ZT_DISABLE,GX_TF_Z8,0); + for(i=0;iviHeight==(rmode->xfbHeight<<1)) flag = 1; + GX_SetFieldMode(rmode->field_rendering,flag); + + GX_SetCopyClear((GXColor)GX_DEFAULT_BG,0x00ffffff); + GX_SetDispCopySrc(0,0,rmode->fbWidth,rmode->efbHeight); + GX_SetDispCopyDst(rmode->fbWidth,rmode->efbHeight); + GX_SetDispCopyYScale(1.0); + GX_SetCopyClamp(GX_CLAMP_TOP|GX_CLAMP_BOTTOM); + GX_SetCopyFilter(GX_FALSE,NULL,GX_FALSE,NULL); + GX_SetDispCopyGamma(GX_GM_1_0); + GX_SetDispCopyFrame2Field(GX_COPY_PROGRESSIVE); + GX_ClearBoundingBox(); + + GX_PokeColorUpdate(GX_TRUE); + GX_PokeAlphaUpdate(GX_TRUE); + GX_PokeDither(GX_FALSE); + GX_PokeBlendMode(GX_BM_NONE,GX_BL_ZERO,GX_BL_ONE,GX_LO_SET); + GX_PokeAlphaMode(GX_ALWAYS,0); + GX_PokeAlphaRead(GX_READ_FF); + GX_PokeDstAlpha(GX_DISABLE,0); + GX_PokeZMode(GX_TRUE,GX_ALWAYS,GX_TRUE); + + GX_SetGPMetric(GX_PERF0_NONE,GX_PERF1_NONE); + GX_ClearGPMetric(); +} + +static void __GX_FlushTextureState() +{ + GX_LOAD_BP_REG(__gx->tevIndMask); +} + +static void __GX_XfVtxSpecs() +{ + u32 xfvtxspecs = 0; + u32 nrms,texs,cols; + + cols = 0; + if(__gx->vcdLo&0x6000) cols++; + if(__gx->vcdLo&0x18000) cols++; + + nrms = 0; + if(__gx->vcdNrms==1) nrms = 1; + else if(__gx->vcdNrms==2) nrms = 2; + + texs = 0; + if(__gx->vcdHi&0x3) texs++; + if(__gx->vcdHi&0xc) texs++; + if(__gx->vcdHi&0x30) texs++; + if(__gx->vcdHi&0xc0) texs++; + if(__gx->vcdHi&0x300) texs++; + if(__gx->vcdHi&0xc00) texs++; + if(__gx->vcdHi&0x3000) texs++; + if(__gx->vcdHi&0xc000) texs++; + + xfvtxspecs = (_SHIFTL(texs,4,4))|(_SHIFTL(nrms,2,2))|(cols&0x3); + GX_LOAD_XF_REG(0x1008,xfvtxspecs); +} + +static void __GX_SetMatrixIndex(u32 mtx) +{ + if(mtx<5) { + GX_LOAD_CP_REG(0x30,__gx->mtxIdxLo); + GX_LOAD_XF_REG(0x1018,__gx->mtxIdxLo); + } else { + GX_LOAD_CP_REG(0x40,__gx->mtxIdxHi); + GX_LOAD_XF_REG(0x1019,__gx->mtxIdxHi); + } +} + +static void __GX_SendFlushPrim() +{ + u32 tmp,tmp2,cnt; + + tmp = (__gx->xfFlush*__gx->xfFlushExp); + + wgPipe->U8 = 0x98; + wgPipe->U16 = __gx->xfFlush; + + tmp2 = (tmp+3)/4; + if(tmp>0) { + cnt = tmp2/8; + while(cnt) { + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + cnt--; + } + tmp2 &= 0x0007; + if(tmp2) { + while(tmp2) { + wgPipe->U32 = 0; + tmp2--; + } + } + } + __gx->xfFlush = 1; +} + +static void __GX_SetVCD() +{ + GX_LOAD_CP_REG(0x50,__gx->vcdLo); + GX_LOAD_CP_REG(0x60,__gx->vcdHi); + __GX_XfVtxSpecs(); +} + +static void __GX_SetVAT() +{ + u8 setvtx = 0; + s32 i; + + for(i=0;i<8;i++) { + setvtx = (1<VATTable&setvtx) { + GX_LOAD_CP_REG((0x70+(i&7)),__gx->VAT0reg[i]); + GX_LOAD_CP_REG((0x80+(i&7)),__gx->VAT1reg[i]); + GX_LOAD_CP_REG((0x90+(i&7)),__gx->VAT2reg[i]); + } + } + __gx->VATTable = 0; +} + +static void __SetSURegs(u8 texmap,u8 texcoord) +{ + u32 reg; + u16 wd,ht; + u8 wrap_s,wrap_t; + + wd = __gx->texMapSize[texmap]&0x3ff; + ht = _SHIFTR(__gx->texMapSize[texmap],10,10); + wrap_s = __gx->texMapWrap[texmap]&3; + wrap_t = _SHIFTR(__gx->texMapWrap[texmap],2,2); + + reg = (texcoord&0x7); + __gx->suSsize[reg] = (__gx->suSsize[reg]&~0x0000ffff)|wd; + __gx->suTsize[reg] = (__gx->suTsize[reg]&~0x0000ffff)|ht; + __gx->suSsize[reg] = (__gx->suSsize[reg]&~0x00010000)|(_SHIFTL(wrap_s,16,1)); + __gx->suTsize[reg] = (__gx->suTsize[reg]&~0x00010000)|(_SHIFTL(wrap_t,16,1)); + + GX_LOAD_BP_REG(__gx->suSsize[reg]); + GX_LOAD_BP_REG(__gx->suTsize[reg]); +} + +static void __GX_SetSUTexRegs() +{ + u32 i; + u32 indtev,dirtev; + u8 texcoord,texmap; + u32 tevreg,tevm,texcm; + + dirtev = (_SHIFTR(__gx->genMode,10,4))+1; + indtev = _SHIFTR(__gx->genMode,16,3); + + //indirect texture order + for(i=0;itevRasOrder[2]&7; + texcoord = _SHIFTR(__gx->tevRasOrder[2],3,3); + break; + case GX_INDTEXSTAGE1: + texmap = _SHIFTR(__gx->tevRasOrder[2],6,3); + texcoord = _SHIFTR(__gx->tevRasOrder[2],9,3); + break; + case GX_INDTEXSTAGE2: + texmap = _SHIFTR(__gx->tevRasOrder[2],12,3); + texcoord = _SHIFTR(__gx->tevRasOrder[2],15,3); + break; + case GX_INDTEXSTAGE3: + texmap = _SHIFTR(__gx->tevRasOrder[2],18,3); + texcoord = _SHIFTR(__gx->tevRasOrder[2],21,3); + break; + default: + texmap = 0; + texcoord = 0; + break; + } + + texcm = _SHIFTL(1,texcoord,1); + if(!(__gx->texCoordManually&texcm)) + __SetSURegs(texmap,texcoord); + } + + //direct texture order + for(i=0;itevTexMap[i]&0xff); + + if(i&1) texcoord = _SHIFTR(__gx->tevRasOrder[tevreg],15,3); + else texcoord = _SHIFTR(__gx->tevRasOrder[tevreg],3,3); + + tevm = _SHIFTL(1,i,1); + texcm = _SHIFTL(1,texcoord,1); + if(texmap!=0xff && (__gx->tevTexCoordEnable&tevm) && !(__gx->texCoordManually&texcm)) { + __SetSURegs(texmap,texcoord); + } + } +} + +static void __GX_SetGenMode() +{ + GX_LOAD_BP_REG(__gx->genMode); + __gx->xfFlush = 0; +} + +static void __GX_UpdateBPMask() +{ +#if defined(HW_DOL) + u32 i; + u32 nbmp,nres; + u8 ntexmap; + + nbmp = _SHIFTR(__gx->genMode,16,3); + + nres = 0; + for(i=0;itevRasOrder[2]&7; + break; + case GX_INDTEXSTAGE1: + ntexmap = _SHIFTR(__gx->tevRasOrder[2],6,3); + break; + case GX_INDTEXSTAGE2: + ntexmap = _SHIFTR(__gx->tevRasOrder[2],12,3); + break; + case GX_INDTEXSTAGE3: + ntexmap = _SHIFTR(__gx->tevRasOrder[2],18,3); + break; + default: + ntexmap = 0; + break; + } + nres |= (1<tevIndMask&0xff)!=nres) { + __gx->tevIndMask = (__gx->tevIndMask&~0xff)|(nres&0xff); + GX_LOAD_BP_REG(__gx->tevIndMask); + } +#endif +} + +static void __GX_SetIndirectMask(u32 mask) +{ + __gx->tevIndMask = ((__gx->tevIndMask&~0xff)|(mask&0xff)); + GX_LOAD_BP_REG(__gx->tevIndMask); +} + +static void __GX_SetTexCoordGen() +{ + u32 i,mask; + u32 texcoord; + + if(__gx->dirtyState&0x02000000) GX_LOAD_XF_REG(0x103f,(__gx->genMode&0xf)); + + i = 0; + texcoord = 0x1040; + mask = _SHIFTR(__gx->dirtyState,16,8); + while(mask) { + if(mask&0x0001) { + GX_LOAD_XF_REG(texcoord,__gx->texCoordGen[i]); + GX_LOAD_XF_REG((texcoord+0x10),__gx->texCoordGen2[i]); + } + mask >>= 1; + texcoord++; + i++; + } +} + +static void __GX_SetChanColor() +{ + if(__gx->dirtyState&0x0100) + GX_LOAD_XF_REG(0x100a,__gx->chnAmbColor[0]); + if(__gx->dirtyState&0x0200) + GX_LOAD_XF_REG(0x100b,__gx->chnAmbColor[1]); + if(__gx->dirtyState&0x0400) + GX_LOAD_XF_REG(0x100c,__gx->chnMatColor[0]); + if(__gx->dirtyState&0x0800) + GX_LOAD_XF_REG(0x100d,__gx->chnMatColor[1]); +} + +static void __GX_SetChanCntrl() +{ + u32 i,chan,mask; + + if(__gx->dirtyState&0x01000000) GX_LOAD_XF_REG(0x1009,(_SHIFTR(__gx->genMode,4,3))); + + i = 0; + chan = 0x100e; + mask = _SHIFTR(__gx->dirtyState,12,4); + while(mask) { + if(mask&0x0001) GX_LOAD_XF_REG(chan,__gx->chnCntrl[i]); + + mask >>= 1; + chan++; + i++; + } +} + +static void __GX_SetDirtyState() +{ + if(__gx->dirtyState&0x0001) { + __GX_SetSUTexRegs(); + } + if(__gx->dirtyState&0x0002) { + __GX_UpdateBPMask(); + } + if(__gx->dirtyState&0x0004) { + __GX_SetGenMode(); + } + if(__gx->dirtyState&0x0008) { + __GX_SetVCD(); + } + if(__gx->dirtyState&0x0010) { + __GX_SetVAT(); + } + if(__gx->dirtyState&~0xff) { + if(__gx->dirtyState&0x0f00) { + __GX_SetChanColor(); + } + if(__gx->dirtyState&0x0100f000) { + __GX_SetChanCntrl(); + } + if(__gx->dirtyState&0x02ff0000) { + __GX_SetTexCoordGen(); + } + if(__gx->dirtyState&0x04000000) { + __GX_SetMatrixIndex(0); + __GX_SetMatrixIndex(5); + } + } + __gx->dirtyState = 0; +} + +static u32 __GX_GetNumXfbLines(u16 efbHeight,u32 yscale) +{ + u32 tmp,tmp1; + + tmp = (((efbHeight-1)<<8)/yscale)+1; + if(yscale>128 && yscale<256) { + while(yscale&0x01) yscale >>= 1; + tmp1 = yscale*(efbHeight/yscale); + if(!(efbHeight-tmp1)) tmp++; + } + if(tmp>1024) tmp = 1024; + + return tmp; +} + +GXFifoObj* GX_Init(void *base,u32 size) +{ + s32 i,re0,re1; +#if defined(HW_RVL) + u32 tmem; +#else + u32 tmem_even,tmem_odd; +#endif + u32 divis,res; + u32 divid = TB_BUS_CLOCK; + GXTexRegion *region = NULL; + GXTlutRegion *tregion = NULL; + + LWP_InitQueue(&_gxwaitfinish); + SYS_RegisterResetFunc(&__gx_resetinfo); + + memset(__gxregs,0,STRUCT_REGDEF_SIZE); + + __GX_FifoInit(); + GX_InitFifoBase(&_gxfifoobj,base,size); + GX_SetCPUFifo(&_gxfifoobj); + GX_SetGPFifo(&_gxfifoobj); + __GX_PEInit(); + EnableWriteGatherPipe(); + + __gx->gxFifoInited = 1; + + __gx->tevIndMask = 0xff; + __gx->tevIndMask = (__gx->tevIndMask&~0xff000000)|(_SHIFTL(0x0f,24,8)); + + i=0; + re0 = 0xc0; + re1 = 0xc1; + while(i<16) { + __gx->tevColorEnv[i] = (__gx->tevColorEnv[i]&~0xff000000)|(_SHIFTL(re0,24,8)); + __gx->tevAlphaEnv[i] = (__gx->tevAlphaEnv[i]&~0xff000000)|(_SHIFTL(re1,24,8)); + re0 += 2; re1 += 2; i++; + } + + __gx->texCoordManually = 0; + __gx->dirtyState = 0; + + __gx->saveDLctx = 1; + __gx->gxFifoUnlinked = 0; + + __gx->sciTLcorner = (__gx->sciTLcorner&~0xff000000)|(_SHIFTL(0x20,24,8)); + __gx->sciBRcorner = (__gx->sciBRcorner&~0xff000000)|(_SHIFTL(0x21,24,8)); + __gx->lpWidth = (__gx->lpWidth&~0xff000000)|(_SHIFTL(0x22,24,8)); + __gx->genMode = (__gx->genMode&~0xff000000)|(_SHIFTL(0x00,24,8)); + + i=0; + re0 = 0x30; + re1 = 0x31; + while(i<8) { + __gx->suSsize[i] = (__gx->suSsize[i]&~0xff000000)|(_SHIFTL(re0,24,8)); + __gx->suTsize[i] = (__gx->suTsize[i]&~0xff000000)|(_SHIFTL(re1,24,8)); + re0 += 2; re1 += 2; i++; + } + + __gx->peZMode = (__gx->peZMode&~0xff000000)|(_SHIFTL(0x40,24,8)); + __gx->peCMode0 = (__gx->peCMode0&~0xff000000)|(_SHIFTL(0x41,24,8)); + __gx->peCMode1 = (__gx->peCMode1&~0xff000000)|(_SHIFTL(0x42,24,8)); + __gx->peCntrl = (__gx->peCntrl&~0xff000000)|(_SHIFTL(0x43,24,8)); + + i=0; + re0 = 0x25; + while(i<11) { + __gx->tevRasOrder[i] = (__gx->tevRasOrder[i]&~0xff000000)|(_SHIFTL(re0,24,8)); + re0++; i++; + } + + divis = 500; + res = (u32)(divid/divis); + __GX_FlushTextureState(); + GX_LOAD_BP_REG(0x69000000|((_SHIFTR(res,11,24))|0x0400)); + + divis = 4224; + res = (u32)(res/divis); + __GX_FlushTextureState(); + GX_LOAD_BP_REG(0x46000000|(res|0x0200)); + + i=0; + re0 = 0xf6; + while(i<8) { + __gx->tevSwapModeTable[i] = (__gx->tevSwapModeTable[i]&~0xff000000)|(_SHIFTL(re0,24,8)); + re0++; i++; + } + + __gx->tevTexCoordEnable = 0; + __gx->perf0Mode = GX_PERF0_NONE; + __gx->perf1Mode = GX_PERF1_NONE; + __gx->cpPerfMode = 0; + + __GX_InitRevBits(); + + i=0; + while(i<16) { + __gx->tevTexMap[i] = 0xff; + i++; + } + +#if defined(HW_RVL) + i = 0; + while(i<8) { + region = &__gx->texRegion[i]; + GX_InitTexCacheRegion(region,GX_FALSE,_gxtexregionaddrtable[i+0],GX_TEXCACHE_32K,_gxtexregionaddrtable[i+8],GX_TEXCACHE_32K); + + region = &__gx->texRegion[i+8]; + GX_InitTexCacheRegion(region,GX_FALSE,_gxtexregionaddrtable[i+16],GX_TEXCACHE_32K,_gxtexregionaddrtable[i+24],GX_TEXCACHE_32K); + + region = &__gx->texRegion[i+16]; + GX_InitTexCacheRegion(region,GX_TRUE,_gxtexregionaddrtable[i+32],GX_TEXCACHE_32K,_gxtexregionaddrtable[i+40],GX_TEXCACHE_32K); + + i++; + } + + i=0; + while(i<16) { + tmem = 0x000C0000+(i<<13); + tregion = &__gx->tlutRegion[i]; + GX_InitTlutRegion(tregion,tmem,GX_TLUT_256); + i++; + } + + i=0; + while(i<4) { + tmem = 0x000E0000+(i<<15); + tregion = &__gx->tlutRegion[i+16]; + GX_InitTlutRegion(tregion,tmem,GX_TLUT_1K); + i++; + } +#else + for(i=0;i<8;i++) { + tmem_even = tmem_odd = (i<<15); + region = &__gx->texRegion[i]; + GX_InitTexCacheRegion(region,GX_FALSE,tmem_even,GX_TEXCACHE_32K,(tmem_odd+0x00080000),GX_TEXCACHE_32K); + } + for(i=0;i<4;i++) { + tmem_even = ((0x08+(i<<1))<<15); + tmem_odd = ((0x09+(i<<1))<<15); + region = &__gx->texRegion[i+8]; + GX_InitTexCacheRegion(region,GX_FALSE,tmem_even,GX_TEXCACHE_32K,tmem_odd,GX_TEXCACHE_32K); + } + for(i=0;i<16;i++) { + tmem_even = (i<<13)+0x000C0000; + tregion = &__gx->tlutRegion[i]; + GX_InitTlutRegion(tregion,tmem_even,GX_TLUT_256); + } + for(i=0;i<4;i++) { + tmem_even = (i<<15)+0x000E0000; + tregion = &__gx->tlutRegion[i+16]; + GX_InitTlutRegion(tregion,tmem_even,GX_TLUT_1K); + } +#endif + _cpReg[3] = 0; + GX_LOAD_CP_REG(0x20,0x00000000); + GX_LOAD_XF_REG(0x1006,0x0); + + GX_LOAD_BP_REG(0x23000000); + GX_LOAD_BP_REG(0x24000000); + GX_LOAD_BP_REG(0x67000000); + + __GX_SetIndirectMask(0); +#if defined(HW_RVL) + __GX_SetTmemConfig(2); +#else + __GX_SetTmemConfig(0); +#endif + __GX_InitGX(); + + return &_gxfifoobj; +} + +void GX_InitFifoBase(GXFifoObj *fifo,void *base,u32 size) +{ + struct __gxfifo *ptr = (struct __gxfifo*)fifo; + + if(!ptr || sizebuf_start = (u32)base; + ptr->buf_end = (u32)base + size - 4; + ptr->size = size; + ptr->rdwt_dst = 0; + + GX_InitFifoLimits(fifo,(size-GX_FIFO_HIWATERMARK),((size>>1)&0x7fffffe0)); + GX_InitFifoPtrs(fifo,base,base); +} + +void GX_InitFifoLimits(GXFifoObj *fifo,u32 hiwatermark,u32 lowatermark) +{ + struct __gxfifo *ptr = (struct __gxfifo*)fifo; + + ptr->hi_mark = hiwatermark; + ptr->lo_mark = lowatermark; +} + +void GX_InitFifoPtrs(GXFifoObj *fifo,void *rd_ptr,void *wt_ptr) +{ + u32 level; + s32 rdwt_dst; + struct __gxfifo *ptr = (struct __gxfifo*)fifo; + + _CPU_ISR_Disable(level); + rdwt_dst = wt_ptr-rd_ptr; + ptr->rd_ptr = (u32)rd_ptr; + ptr->wt_ptr = (u32)wt_ptr; + ptr->rdwt_dst = rdwt_dst; + if(rdwt_dst<0) { + rdwt_dst += ptr->size; + ptr->rd_ptr = rdwt_dst; + } + _CPU_ISR_Restore(level); +} + +void GX_GetFifoPtrs(GXFifoObj *fifo,void **rd_ptr,void **wt_ptr) +{ + struct __gxfifo *ptr = (struct __gxfifo*)fifo; + *rd_ptr = (void*)ptr->rd_ptr; + *wt_ptr = (void*)ptr->wt_ptr; +} + +void GX_SetCPUFifo(GXFifoObj *fifo) +{ + u32 level; + struct __gxfifo *ptr = (struct __gxfifo*)fifo; + struct __gxfifo *cpufifo = (struct __gxfifo*)&_cpufifo; + + _CPU_ISR_Disable(level); + if(!fifo) { + _gxcpufifoready = 0; + _cpgplinked = 0; + cpufifo->gpfifo_ready = 0; + cpufifo->cpufifo_ready = 0; + _CPU_ISR_Restore(level); + return; + } + + cpufifo->buf_start = ptr->buf_start; + cpufifo->buf_end = ptr->buf_end; + cpufifo->size = ptr->size; + cpufifo->hi_mark = ptr->hi_mark; + cpufifo->lo_mark = ptr->lo_mark; + cpufifo->rd_ptr = ptr->rd_ptr; + cpufifo->wt_ptr = ptr->wt_ptr; + cpufifo->rdwt_dst = ptr->rdwt_dst; + cpufifo->fifo_wrap = ptr->fifo_wrap; + cpufifo->gpfifo_ready = ptr->gpfifo_ready; + cpufifo->cpufifo_ready = 1; + + _gxcpufifoready = 1; + if(__GX_CPGPLinkCheck()) { + _cpgplinked = 1; + cpufifo->gpfifo_ready = 1; + + _piReg[3] = MEM_VIRTUAL_TO_PHYSICAL(cpufifo->buf_start); + _piReg[4] = MEM_VIRTUAL_TO_PHYSICAL(cpufifo->buf_end); + _piReg[5] = (cpufifo->wt_ptr&0x1FFFFFE0); + + __GX_WriteFifoIntReset(GX_TRUE,GX_TRUE); + __GX_WriteFifoIntEnable(GX_ENABLE,GX_DISABLE); + __GX_FifoLink(GX_TRUE); + + _CPU_ISR_Restore(level); + return; + } + + if(_cpgplinked) { + __GX_FifoLink(GX_FALSE); + _cpgplinked = 0; + } + + __GX_WriteFifoIntEnable(GX_DISABLE,GX_DISABLE); + + _piReg[3] = MEM_VIRTUAL_TO_PHYSICAL(cpufifo->buf_start); + _piReg[4] = MEM_VIRTUAL_TO_PHYSICAL(cpufifo->buf_end); + _piReg[5] = (cpufifo->wt_ptr&0x1FFFFFE0); + ppcsync(); + + _CPU_ISR_Restore(level); +} + +void GX_GetCPUFifo(GXFifoObj *fifo) +{ + struct __gxfifo* ptr = (struct __gxfifo*)fifo; + struct __gxfifo* cpufifo = (struct __gxfifo*)&_cpufifo; + + if(!_gxcpufifoready) return; + + GX_Flush(); + __GX_SaveFifo(); + + ptr->buf_start = cpufifo->buf_start; + ptr->buf_end = cpufifo->buf_end; + ptr->size = cpufifo->size; + ptr->rd_ptr = cpufifo->rd_ptr; + ptr->wt_ptr = cpufifo->wt_ptr; + ptr->rdwt_dst = cpufifo->rdwt_dst; + ptr->hi_mark = cpufifo->hi_mark; + ptr->lo_mark = cpufifo->lo_mark; + ptr->fifo_wrap = cpufifo->fifo_wrap; + ptr->cpufifo_ready = cpufifo->cpufifo_ready; + ptr->gpfifo_ready = cpufifo->gpfifo_ready; +} + +void GX_SetGPFifo(GXFifoObj *fifo) +{ + u32 level; + struct __gxfifo *ptr = (struct __gxfifo*)fifo; + struct __gxfifo *gpfifo = (struct __gxfifo*)&_gpfifo; + + _CPU_ISR_Disable(level); + __GX_FifoReadDisable(); + __GX_WriteFifoIntEnable(GX_DISABLE,GX_DISABLE); + + if(!fifo) { + _gxgpfifoready = 0; + _cpgplinked = 0; + gpfifo->cpufifo_ready = 0; + gpfifo->gpfifo_ready = 0; + __GX_FifoLink(GX_FALSE); + _CPU_ISR_Restore(level); + return; + } + + gpfifo->buf_start = ptr->buf_start; + gpfifo->buf_end = ptr->buf_end; + gpfifo->size = ptr->size; + gpfifo->hi_mark = ptr->hi_mark; + gpfifo->lo_mark = ptr->lo_mark; + gpfifo->rd_ptr = ptr->rd_ptr; + gpfifo->wt_ptr = ptr->wt_ptr; + gpfifo->rdwt_dst = ptr->rdwt_dst; + gpfifo->fifo_wrap = ptr->fifo_wrap; + gpfifo->cpufifo_ready = ptr->cpufifo_ready; + gpfifo->gpfifo_ready = 1; + _gxgpfifoready = 1; + + /* setup fifo base */ + _cpReg[16] = _SHIFTL(MEM_VIRTUAL_TO_PHYSICAL(gpfifo->buf_start),0,16); + _cpReg[17] = _SHIFTR(MEM_VIRTUAL_TO_PHYSICAL(gpfifo->buf_start),16,16); + + /* setup fifo end */ + _cpReg[18] = _SHIFTL(MEM_VIRTUAL_TO_PHYSICAL(gpfifo->buf_end),0,16); + _cpReg[19] = _SHIFTR(MEM_VIRTUAL_TO_PHYSICAL(gpfifo->buf_end),16,16); + + /* setup hiwater mark */ + _cpReg[20] = _SHIFTL(gpfifo->hi_mark,0,16); + _cpReg[21] = _SHIFTR(gpfifo->hi_mark,16,16); + + /* setup lowater mark */ + _cpReg[22] = _SHIFTL(gpfifo->lo_mark,0,16); + _cpReg[23] = _SHIFTR(gpfifo->lo_mark,16,16); + + /* setup rd<->wd dist */ + _cpReg[24] = _SHIFTL(gpfifo->rdwt_dst,0,16); + _cpReg[25] = _SHIFTR(gpfifo->rdwt_dst,16,16); + + /* setup wt ptr */ + _cpReg[26] = _SHIFTL(MEM_VIRTUAL_TO_PHYSICAL(gpfifo->wt_ptr),0,16); + _cpReg[27] = _SHIFTR(MEM_VIRTUAL_TO_PHYSICAL(gpfifo->wt_ptr),16,16); + + /* setup rd ptr */ + _cpReg[28] = _SHIFTL(MEM_VIRTUAL_TO_PHYSICAL(gpfifo->rd_ptr),0,16); + _cpReg[29] = _SHIFTR(MEM_VIRTUAL_TO_PHYSICAL(gpfifo->rd_ptr),16,16); + ppcsync(); + + if(__GX_CPGPLinkCheck()) { + _cpgplinked = 1; + gpfifo->cpufifo_ready = 1; + __GX_WriteFifoIntEnable(GX_ENABLE,GX_DISABLE); + __GX_FifoLink(GX_TRUE); + } else { + _cpgplinked = 0; + gpfifo->cpufifo_ready = 0; + __GX_WriteFifoIntEnable(GX_DISABLE,GX_DISABLE); + __GX_FifoLink(GX_FALSE); + } + + __GX_WriteFifoIntReset(GX_TRUE,GX_TRUE); + __GX_FifoReadEnable(); + _CPU_ISR_Restore(level); +} + +void GX_GetGPFifo(GXFifoObj *fifo) +{ + struct __gxfifo* ptr = (struct __gxfifo*)fifo; + struct __gxfifo* gpfifo = (struct __gxfifo*)&_gpfifo; + + if(!_gxgpfifoready) return; + + __GX_SaveFifo(); + + ptr->buf_start = gpfifo->buf_start; + ptr->buf_end = gpfifo->buf_end; + ptr->size = gpfifo->size; + ptr->rd_ptr = gpfifo->rd_ptr; + ptr->wt_ptr = gpfifo->wt_ptr; + ptr->rdwt_dst = gpfifo->rdwt_dst; + ptr->hi_mark = gpfifo->hi_mark; + ptr->lo_mark = gpfifo->lo_mark; + ptr->fifo_wrap = gpfifo->fifo_wrap; + ptr->gpfifo_ready = gpfifo->gpfifo_ready; + ptr->cpufifo_ready = gpfifo->cpufifo_ready; +} + +void* GX_GetFifoBase(GXFifoObj *fifo) +{ + return (void*)((struct __gxfifo*)fifo)->buf_start; +} + +u32 GX_GetFifoSize(GXFifoObj *fifo) +{ + return ((struct __gxfifo*)fifo)->size; +} + +u32 GX_GetFifoCount(GXFifoObj *fifo) +{ + return ((struct __gxfifo*)fifo)->rdwt_dst; +} + +u8 GX_GetFifoWrap(GXFifoObj *fifo) +{ + return ((struct __gxfifo*)fifo)->fifo_wrap; +} + +u32 GX_GetOverflowCount() +{ + return _gxoverflowcount; +} + +u32 GX_ResetOverflowCount() +{ + u32 ret = _gxoverflowcount; + _gxoverflowcount = 0; + return ret; +} + +lwp_t GX_GetCurrentGXThread() +{ + return _gxcurrentlwp; +} + +lwp_t GX_SetCurrentGXThread() +{ + u32 level; + + _CPU_ISR_Disable(level); + lwp_t ret = _gxcurrentlwp; + _gxcurrentlwp = LWP_GetSelf(); + _CPU_ISR_Restore(level); + + return ret; +} + +volatile void* GX_RedirectWriteGatherPipe(void *ptr) +{ + u32 level; + struct __gxfifo *cpufifo = (struct __gxfifo*)&_cpufifo; + + _CPU_ISR_Disable(level); + GX_Flush(); + while(!IsWriteGatherBufferEmpty()); + + mtwpar(0x0C008000); + if(_cpgplinked) { + __GX_FifoLink(GX_FALSE); + __GX_WriteFifoIntEnable(GX_DISABLE,GX_DISABLE); + } + cpufifo->wt_ptr = (u32)MEM_PHYSICAL_TO_K0(_piReg[5]&~0x04000000); + + _piReg[3] = 0; + _piReg[4] = 0x04000000; + _piReg[5] = (((u32)ptr&0x3FFFFFE0)&~0x04000000); + _sync(); + + _CPU_ISR_Restore(level); + + return (volatile void*)0x0C008000; +} + +void GX_RestoreWriteGatherPipe() +{ + u32 level; + struct __gxfifo *cpufifo = (struct __gxfifo*)&_cpufifo; + + _CPU_ISR_Disable(level); + + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + + ppcsync(); + while(!IsWriteGatherBufferEmpty()); + + mtwpar(0x0C008000); + _piReg[3] = MEM_VIRTUAL_TO_PHYSICAL(cpufifo->buf_start); + _piReg[4] = MEM_VIRTUAL_TO_PHYSICAL(cpufifo->buf_end); + _piReg[5] = (((u32)cpufifo->wt_ptr&0x3FFFFFE0)&~0x04000000); + if(_cpgplinked) { + __GX_WriteFifoIntReset(GX_TRUE,GX_TRUE); + __GX_WriteFifoIntEnable(GX_ENABLE,GX_DISABLE); + __GX_FifoLink(GX_TRUE); + } + _CPU_ISR_Restore(level); +} + +void GX_Flush() +{ + if(__gx->dirtyState) + __GX_SetDirtyState(); + + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + + ppcsync(); +} + +void GX_EnableBreakPt(void *break_pt) +{ + u32 level = 0; + _CPU_ISR_Disable(level); + __GX_FifoReadDisable(); + _cpReg[30] = _SHIFTL(MEM_VIRTUAL_TO_PHYSICAL(break_pt),0,16); + _cpReg[31] = _SHIFTR(MEM_VIRTUAL_TO_PHYSICAL(break_pt),16,16); + __gx->cpCRreg = (__gx->cpCRreg&~0x22)|0x22; + _cpReg[1] = __gx->cpCRreg; + _gxcurrbp = break_pt; + __GX_FifoReadEnable(); + _CPU_ISR_Restore(level); +} + +void GX_DisableBreakPt() +{ + u32 level = 0; + _CPU_ISR_Disable(level); + __gx->cpCRreg = (__gx->cpCRreg&~0x22); + _cpReg[1] = __gx->cpCRreg; + _gxcurrbp = NULL; + _CPU_ISR_Restore(level); +} + +#if defined(HW_DOL) +void GX_AbortFrame() +{ + _piReg[6] = 1; + __GX_WaitAbort(50); + _piReg[6] = 0; + __GX_WaitAbort(5); + + if(__GX_IsGPFifoReady()) + __GX_CleanGPFifo(); +} +#elif defined(HW_RVL) +void GX_AbortFrame() +{ + __GX_Abort(); + if(__GX_IsGPFifoReady()) { + __GX_CleanGPFifo(); + __GX_InitRevBits(); + + __gx->dirtyState = 0; + GX_Flush(); + } +} +#endif + +void GX_SetDrawSync(u16 token) +{ + u32 level = 0; + _CPU_ISR_Disable(level); + GX_LOAD_BP_REG(0x48000000 | token); + GX_LOAD_BP_REG(0x47000000 | token); + GX_Flush(); + _CPU_ISR_Restore(level); +} + +u16 GX_GetDrawSync() +{ + return _peReg[7]; +} + +void GX_SetDrawDone() +{ + u32 level; + _CPU_ISR_Disable(level); + GX_LOAD_BP_REG(0x45000002); // set draw done! + GX_Flush(); + _gxfinished = 0; + _CPU_ISR_Restore(level); +} + + +void GX_WaitDrawDone() +{ + u32 level; + _CPU_ISR_Disable(level); + while(!_gxfinished) + LWP_ThreadSleep(_gxwaitfinish); + _CPU_ISR_Restore(level); +} + +void GX_DrawDone() +{ + u32 level; + + _CPU_ISR_Disable(level); + GX_LOAD_BP_REG(0x45000002); // set draw done! + GX_Flush(); + + _gxfinished = 0; + _CPU_ISR_Flash(level); + + while(!_gxfinished) + LWP_ThreadSleep(_gxwaitfinish); + _CPU_ISR_Restore(level); +} + +GXDrawDoneCallback GX_SetDrawDoneCallback(GXDrawDoneCallback cb) +{ + u32 level; + + _CPU_ISR_Disable(level); + GXDrawDoneCallback ret = drawDoneCB; + drawDoneCB = cb; + _CPU_ISR_Restore(level); + return ret; +} + +GXDrawSyncCallback GX_SetDrawSyncCallback(GXDrawSyncCallback cb) +{ + u32 level; + + _CPU_ISR_Disable(level); + GXDrawSyncCallback ret = tokenCB; + tokenCB = cb; + _CPU_ISR_Restore(level); + return ret; +} + +GXBreakPtCallback GX_SetBreakPtCallback(GXBreakPtCallback cb) +{ + u32 level; + + _CPU_ISR_Disable(level); + GXBreakPtCallback ret = breakPtCB; + breakPtCB = cb; + _CPU_ISR_Restore(level); + return ret; +} + +void GX_PixModeSync() +{ + GX_LOAD_BP_REG(__gx->peCntrl); +} + +void GX_TexModeSync() +{ + GX_LOAD_BP_REG(0x63000000); +} + +void GX_SetMisc(u32 token,u32 value) +{ + u32 cnt; + + if(token==GX_MT_XF_FLUSH) { + __gx->xfFlushSafe = value; + cnt = cntlzw(__gx->xfFlushSafe); + __gx->xfFlushExp = _SHIFTR(cnt,5,16); + + __gx->xfFlush = 1; + if(!__gx->xfFlushSafe) return; + + __gx->dirtyState |= 0x0008; + } else if(token==GX_MT_DL_SAVE_CTX) { + __gx->saveDLctx = (value&0xff); + } + return; +} + +void GX_SetViewportJitter(f32 xOrig,f32 yOrig,f32 wd,f32 ht,f32 nearZ,f32 farZ,u32 field) +{ + f32 x0,y0,x1,y1,n,f,z; + static f32 Xfactor = 0.5; + static f32 Yfactor = 342.0; + static f32 Zfactor = 16777215.0; + + if(!field) yOrig -= Xfactor; + + x0 = wd*Xfactor; + y0 = (-ht)*Xfactor; + x1 = (xOrig+(wd*Xfactor))+Yfactor; + y1 = (yOrig+(ht*Xfactor))+Yfactor; + n = Zfactor*nearZ; + f = Zfactor*farZ; + z = f-n; + + GX_LOAD_XF_REGS(0x101a,6); + wgPipe->F32 = x0; + wgPipe->F32 = y0; + wgPipe->F32 = z; + wgPipe->F32 = x1; + wgPipe->F32 = y1; + wgPipe->F32 = f; +} + +void GX_SetViewport(f32 xOrig,f32 yOrig,f32 wd,f32 ht,f32 nearZ,f32 farZ) +{ + GX_SetViewportJitter(xOrig,yOrig,wd,ht,nearZ,farZ,1); +} + +void GX_LoadProjectionMtx(Mtx44 mt,u8 type) +{ + f32 tmp[7]; + + ((u32*)((void*)tmp))[6] = (u32)type; + tmp[0] = mt[0][0]; + tmp[2] = mt[1][1]; + tmp[4] = mt[2][2]; + tmp[5] = mt[2][3]; + + switch(type) { + case GX_PERSPECTIVE: + tmp[1] = mt[0][2]; + tmp[3] = mt[1][2]; + break; + case GX_ORTHOGRAPHIC: + tmp[1] = mt[0][3]; + tmp[3] = mt[1][3]; + break; + default: + tmp[1] = 0.0; + tmp[3] = 0.0; + break; + } + + GX_LOAD_XF_REGS(0x1020,7); + wgPipe->F32 = tmp[0]; + wgPipe->F32 = tmp[1]; + wgPipe->F32 = tmp[2]; + wgPipe->F32 = tmp[3]; + wgPipe->F32 = tmp[4]; + wgPipe->F32 = tmp[5]; + wgPipe->F32 = tmp[6]; +} + +static void __GetImageTileCount(u32 fmt,u16 wd,u16 ht,u32 *xtiles,u32 *ytiles,u32 *zplanes) +{ + u32 xshift,yshift,tile; + + switch(fmt) { + case GX_TF_I4: + case GX_TF_IA4: + case GX_CTF_R4: + case GX_CTF_RA4: + case GX_CTF_Z4: + xshift = 3; + yshift = 3; + break; + case GX_TF_Z8: + case GX_TF_I8: + case GX_CTF_A8: + case GX_CTF_R8: + case GX_CTF_G8: + case GX_CTF_B8: + case GX_CTF_Z8M: + case GX_CTF_Z8L: + xshift = 3; + yshift = 2; + break; + case GX_TF_IA8: + case GX_CTF_RA8: + case GX_CTF_RG8: + case GX_CTF_GB8: + case GX_TF_Z16: + case GX_TF_Z24X8: + case GX_CTF_Z16L: + case GX_TF_RGB565: + case GX_TF_RGB5A3: + case GX_TF_RGBA8: + xshift = 2; + yshift = 2; + break; + default: + xshift = 0; + yshift = 0; + break; + } + + if(!(wd&0xffff)) wd = 1; + if(!(ht&0xffff)) ht = 1; + + wd &= 0xffff; + tile = (wd+((1<>xshift; + *xtiles = tile; + + ht &= 0xffff; + tile = (ht+((1<>yshift; + *ytiles = tile; + + *zplanes = 1; + if(fmt==GX_TF_RGBA8 || fmt==GX_TF_Z24X8) *zplanes = 2; +} + +void GX_SetCopyClear(GXColor color,u32 zvalue) +{ + u32 val; + + val = (_SHIFTL(color.a,8,8))|(color.r&0xff); + GX_LOAD_BP_REG(0x4f000000|val); + + val = (_SHIFTL(color.g,8,8))|(color.b&0xff); + GX_LOAD_BP_REG(0x50000000|val); + + val = zvalue&0x00ffffff; + GX_LOAD_BP_REG(0x51000000|val); +} + +void GX_SetCopyClamp(u8 clamp) +{ + __gx->dispCopyCntrl = (__gx->dispCopyCntrl&~1)|(clamp&1); + __gx->dispCopyCntrl = (__gx->dispCopyCntrl&~2)|(clamp&2); +} + +void GX_SetDispCopyGamma(u8 gamma) +{ + __gx->dispCopyCntrl = (__gx->dispCopyCntrl&~0x180)|(_SHIFTL(gamma,7,2)); +} + +void GX_SetCopyFilter(u8 aa,u8 sample_pattern[12][2],u8 vf,u8 vfilter[7]) +{ + u32 reg01=0,reg02=0,reg03=0,reg04=0,reg53=0,reg54=0; + + if(aa) { + reg01 = sample_pattern[0][0]&0xf; + reg01 = (reg01&~0xf0)|(_SHIFTL(sample_pattern[0][1],4,4)); + reg01 = (reg01&~0xf00)|(_SHIFTL(sample_pattern[1][0],8,4)); + reg01 = (reg01&~0xf000)|(_SHIFTL(sample_pattern[1][1],12,4)); + reg01 = (reg01&~0xf0000)|(_SHIFTL(sample_pattern[2][0],16,4)); + reg01 = (reg01&~0xf00000)|(_SHIFTL(sample_pattern[2][1],20,4)); + reg01 = (reg01&~0xff000000)|(_SHIFTL(0x01,24,8)); + + reg02 = sample_pattern[3][0]&0xf; + reg02 = (reg02&~0xf0)|(_SHIFTL(sample_pattern[3][1],4,4)); + reg02 = (reg02&~0xf00)|(_SHIFTL(sample_pattern[4][0],8,4)); + reg02 = (reg02&~0xf000)|(_SHIFTL(sample_pattern[4][1],12,4)); + reg02 = (reg02&~0xf0000)|(_SHIFTL(sample_pattern[5][0],16,4)); + reg02 = (reg02&~0xf00000)|(_SHIFTL(sample_pattern[5][1],20,4)); + reg02 = (reg02&~0xff000000)|(_SHIFTL(0x02,24,8)); + + reg03 = sample_pattern[6][0]&0xf; + reg03 = (reg03&~0xf0)|(_SHIFTL(sample_pattern[6][1],4,4)); + reg03 = (reg03&~0xf00)|(_SHIFTL(sample_pattern[7][0],8,4)); + reg03 = (reg03&~0xf000)|(_SHIFTL(sample_pattern[7][1],12,4)); + reg03 = (reg03&~0xf0000)|(_SHIFTL(sample_pattern[8][0],16,4)); + reg03 = (reg03&~0xf00000)|(_SHIFTL(sample_pattern[8][1],20,4)); + reg03 = (reg03&~0xff000000)|(_SHIFTL(0x03,24,8)); + + reg04 = sample_pattern[9][0]&0xf; + reg04 = (reg04&~0xf0)|(_SHIFTL(sample_pattern[9][1],4,4)); + reg04 = (reg04&~0xf00)|(_SHIFTL(sample_pattern[10][0],8,4)); + reg04 = (reg04&~0xf000)|(_SHIFTL(sample_pattern[10][1],12,4)); + reg04 = (reg04&~0xf0000)|(_SHIFTL(sample_pattern[11][0],16,4)); + reg04 = (reg04&~0xf00000)|(_SHIFTL(sample_pattern[11][1],20,4)); + reg04 = (reg04&~0xff000000)|(_SHIFTL(0x04,24,8)); + } else { + reg01 = 0x01666666; + reg02 = 0x02666666; + reg03 = 0x03666666; + reg04 = 0x04666666; + } + GX_LOAD_BP_REG(reg01); + GX_LOAD_BP_REG(reg02); + GX_LOAD_BP_REG(reg03); + GX_LOAD_BP_REG(reg04); + + reg53 = 0x53595000; + reg54 = 0x54000015; + if(vf) { + reg53 = 0x53000000|(vfilter[0]&0x3f); + reg53 = (reg53&~0xfc0)|(_SHIFTL(vfilter[1],6,6)); + reg53 = (reg53&~0x3f000)|(_SHIFTL(vfilter[2],12,6)); + reg53 = (reg53&~0xfc0000)|(_SHIFTL(vfilter[3],18,6)); + + reg54 = 0x54000000|(vfilter[4]&0x3f); + reg54 = (reg54&~0xfc0)|(_SHIFTL(vfilter[5],6,6)); + reg54 = (reg54&~0x3f000)|(_SHIFTL(vfilter[6],12,6)); + } + GX_LOAD_BP_REG(reg53); + GX_LOAD_BP_REG(reg54); +} + +void GX_SetDispCopyFrame2Field(u8 mode) +{ + __gx->dispCopyCntrl = (__gx->dispCopyCntrl&~0x3000)|(_SHIFTL(mode,12,2)); +} + +u32 GX_SetDispCopyYScale(f32 yscale) +{ + u32 ht,yScale = 0; + + yScale = ((u32)(256.0f/yscale))&0x1ff; + GX_LOAD_BP_REG(0x4e000000|yScale); + + __gx->dispCopyCntrl = (__gx->dispCopyCntrl&~0x400)|(_SHIFTL(((256-yScale)>0),10,1)); + ht = _SHIFTR(__gx->dispCopyWH,12,10)+1; + return __GX_GetNumXfbLines(ht,yScale); +} + +void GX_SetDispCopyDst(u16 wd,u16 ht) +{ + __gx->dispCopyDst = (__gx->dispCopyDst&~0x3ff)|(_SHIFTR(wd,4,10)); + __gx->dispCopyDst = (__gx->dispCopyDst&~0xff000000)|(_SHIFTL(0x4d,24,8)); +} + +void GX_SetDispCopySrc(u16 left,u16 top,u16 wd,u16 ht) +{ + __gx->dispCopyTL = (__gx->dispCopyTL&~0x00ffffff)|XY(left,top); + __gx->dispCopyTL = (__gx->dispCopyTL&~0xff000000)|(_SHIFTL(0x49,24,8)); + __gx->dispCopyWH = (__gx->dispCopyWH&~0x00ffffff)|XY((wd-1),(ht-1)); + __gx->dispCopyWH = (__gx->dispCopyWH&~0xff000000)|(_SHIFTL(0x4a,24,8)); +} + +void GX_CopyDisp(void *dest,u8 clear) +{ + u8 clflag; + u32 val; + + if(clear) { + val= (__gx->peZMode&~0xf)|0xf; + GX_LOAD_BP_REG(val); + val = (__gx->peCMode0&~0x3); + GX_LOAD_BP_REG(val); + } + + clflag = 0; + if(clear || (__gx->peCntrl&0x7)==0x0003) { + if(__gx->peCntrl&0x40) { + clflag = 1; + val = (__gx->peCntrl&~0x40); + GX_LOAD_BP_REG(val); + } + } + + GX_LOAD_BP_REG(__gx->dispCopyTL); // set source top + GX_LOAD_BP_REG(__gx->dispCopyWH); + + GX_LOAD_BP_REG(__gx->dispCopyDst); + + val = 0x4b000000|(_SHIFTR(MEM_VIRTUAL_TO_PHYSICAL(dest),5,24)); + GX_LOAD_BP_REG(val); + + __gx->dispCopyCntrl = (__gx->dispCopyCntrl&~0x800)|(_SHIFTL(clear,11,1)); + __gx->dispCopyCntrl = (__gx->dispCopyCntrl&~0x4000)|0x4000; + __gx->dispCopyCntrl = (__gx->dispCopyCntrl&~0xff000000)|(_SHIFTL(0x52,24,8)); + + GX_LOAD_BP_REG(__gx->dispCopyCntrl); + + if(clear) { + GX_LOAD_BP_REG(__gx->peZMode); + GX_LOAD_BP_REG(__gx->peCMode0); + } + if(clflag) GX_LOAD_BP_REG(__gx->peCntrl); +} + +void GX_CopyTex(void *dest,u8 clear) +{ + u8 clflag; + u32 val; + + if(clear) { + val = (__gx->peZMode&~0xf)|0xf; + GX_LOAD_BP_REG(val); + val = (__gx->peCMode0&~0x3); + GX_LOAD_BP_REG(val); + } + + clflag = 0; + val = __gx->peCntrl; + if(__gx->texCopyZTex && (val&0x7)!=0x0003) { + clflag = 1; + val = (val&~0x7)|0x0003; + } + if(clear || (val&0x7)==0x0003) { + if(val&0x40) { + clflag = 1; + val = (val&~0x40); + } + } + if(clflag) GX_LOAD_BP_REG(val); + + val = 0x4b000000|(_SHIFTR(MEM_VIRTUAL_TO_PHYSICAL(dest),5,24)); + + GX_LOAD_BP_REG(__gx->texCopyTL); + GX_LOAD_BP_REG(__gx->texCopyWH); + GX_LOAD_BP_REG(__gx->texCopyDst); + GX_LOAD_BP_REG(val); + + __gx->texCopyCntrl = (__gx->texCopyCntrl&~0x800)|(_SHIFTL(clear,11,1)); + __gx->texCopyCntrl = (__gx->texCopyCntrl&~0x4000); + __gx->texCopyCntrl = (__gx->texCopyCntrl&~0xff000000)|(_SHIFTL(0x52,24,8)); + GX_LOAD_BP_REG(__gx->texCopyCntrl); + + if(clear) { + GX_LOAD_BP_REG(__gx->peZMode); + GX_LOAD_BP_REG(__gx->peCMode0); + } + if(clflag) GX_LOAD_BP_REG(__gx->peCntrl); +} + +void GX_SetTexCopySrc(u16 left,u16 top,u16 wd,u16 ht) +{ + __gx->texCopyTL = (__gx->texCopyTL&~0x00ffffff)|XY(left,top); + __gx->texCopyTL = (__gx->texCopyTL&~0xff000000)|(_SHIFTL(0x49,24,8)); + __gx->texCopyWH = (__gx->texCopyWH&~0x00ffffff)|XY((wd-1),(ht-1)); + __gx->texCopyWH = (__gx->texCopyWH&~0xff000000)|(_SHIFTL(0x4a,24,8)); +} + +void GX_SetTexCopyDst(u16 wd,u16 ht,u32 fmt,u8 mipmap) +{ + u8 lfmt = fmt&0xf; + u32 xtiles,ytiles,zplanes; + + __GetImageTileCount(fmt,wd,ht,&xtiles,&ytiles,&zplanes); + __gx->texCopyDst = (__gx->texCopyDst&~0x3ff)|((xtiles*zplanes)&0x3ff); + + if(fmt==GX_TF_Z16) lfmt = 11; + if(fmt==GX_CTF_YUVA8 || (fmt>=GX_TF_I4 && fmttexCopyCntrl = (__gx->texCopyCntrl&~0x18000)|0x18000; + else __gx->texCopyCntrl = (__gx->texCopyCntrl&~0x18000)|0x10000; + + __gx->texCopyCntrl = (__gx->texCopyCntrl&~0x8)|(lfmt&0x8); + __gx->texCopyCntrl = (__gx->texCopyCntrl&~0x200)|(_SHIFTL(mipmap,9,1)); + __gx->texCopyCntrl = (__gx->texCopyCntrl&~0x70)|(_SHIFTL(lfmt,4,3)); + + __gx->texCopyDst = (__gx->texCopyDst&~0xff000000)|(_SHIFTL(0x4d,24,8)); + + __gx->texCopyZTex = ((fmt&_GX_TF_ZTF)==_GX_TF_ZTF); +} + +void GX_ClearBoundingBox() +{ + GX_LOAD_BP_REG(0x550003ff); + GX_LOAD_BP_REG(0x560003ff); +} + +void GX_BeginDispList(void *list,u32 size) +{ + struct __gxfifo *fifo; + + if(__gx->dirtyState) + __GX_SetDirtyState(); + + if(__gx->saveDLctx) + memcpy(_gx_saved_data,__gxregs,STRUCT_REGDEF_SIZE); + + fifo = (struct __gxfifo*)&_gx_dl_fifoobj; + fifo->buf_start = (u32)list; + fifo->buf_end = (u32)list + size - 4; + fifo->size = size; + + fifo->rd_ptr = (u32)list; + fifo->wt_ptr = (u32)list; + fifo->rdwt_dst = 0; + + __gx->gxFifoUnlinked = 1; + + GX_GetCPUFifo(&_gx_old_cpufifo); + GX_SetCPUFifo(&_gx_dl_fifoobj); + __GX_ResetWriteGatherPipe(); +} + +u32 GX_EndDispList() +{ + u32 level; + u8 wrap = 0; + + GX_GetCPUFifo(&_gx_dl_fifoobj); + GX_SetCPUFifo(&_gx_old_cpufifo); + + if(__gx->saveDLctx) { + _CPU_ISR_Disable(level); + memcpy(__gxregs,_gx_saved_data,STRUCT_REGDEF_SIZE); + _CPU_ISR_Restore(level); + } + + __gx->gxFifoUnlinked = 0; + + wrap = GX_GetFifoWrap(&_gx_dl_fifoobj); + if(wrap) return 0; + + return GX_GetFifoCount(&_gx_dl_fifoobj); +} + +void GX_CallDispList(void *list,u32 nbytes) +{ + if(__gx->dirtyState) + __GX_SetDirtyState(); + + if(!__gx->vcdClear) + __GX_SendFlushPrim(); + + wgPipe->U8 = 0x40; //call displaylist + wgPipe->U32 = MEM_VIRTUAL_TO_PHYSICAL(list); + wgPipe->U32 = nbytes; +} + +void GX_SetChanCtrl(s32 channel,u8 enable,u8 ambsrc,u8 matsrc,u8 litmask,u8 diff_fn,u8 attn_fn) +{ + u32 reg,difffn = (attn_fn==GX_AF_SPEC)?GX_DF_NONE:diff_fn; + u32 val = (matsrc&1)|(_SHIFTL(enable,1,1))|(_SHIFTL(litmask,2,4))|(_SHIFTL(ambsrc,6,1))|(_SHIFTL(difffn,7,2))|(_SHIFTL(((GX_AF_NONE-attn_fn)>0),9,1))|(_SHIFTL((attn_fn>0),10,1))|(_SHIFTL((_SHIFTR(litmask,4,4)),11,4)); + + reg = (channel&0x03); + __gx->chnCntrl[reg] = val; + __gx->dirtyState |= (0x1000<chnCntrl[2] = val; + __gx->dirtyState |= 0x5000; + } else { + __gx->chnCntrl[3] = val; + __gx->dirtyState |= 0xa000; + } +} + +void GX_SetChanAmbColor(s32 channel,GXColor color) +{ + u32 reg,val = (_SHIFTL(color.r,24,8))|(_SHIFTL(color.g,16,8))|(_SHIFTL(color.b,8,8))|0x00; + switch(channel) { + case GX_COLOR0: + reg = 0; + val |= (__gx->chnAmbColor[0]&0xff); + break; + case GX_COLOR1: + reg = 1; + val |= (__gx->chnAmbColor[1]&0xff); + break; + case GX_ALPHA0: + reg = 0; + val = ((__gx->chnAmbColor[0]&~0xff)|(color.a&0xff)); + break; + case GX_ALPHA1: + reg = 1; + val = ((__gx->chnAmbColor[1]&~0xff)|(color.a&0xff)); + break; + case GX_COLOR0A0: + reg = 0; + val |= (color.a&0xFF); + break; + case GX_COLOR1A1: + reg = 1; + val |= (color.a&0xFF); + break; + default: + return; + } + + __gx->chnAmbColor[reg] = val; + __gx->dirtyState |= (0x0100<chnMatColor[0]&0xff); + break; + case GX_COLOR1: + reg = 1; + val |= (__gx->chnMatColor[1]&0xff); + break; + case GX_ALPHA0: + reg = 0; + val = ((__gx->chnMatColor[0]&~0xff)|(color.a&0xff)); + break; + case GX_ALPHA1: + reg = 1; + val = ((__gx->chnMatColor[1]&~0xff)|(color.a&0xff)); + break; + case GX_COLOR0A0: + reg = 0; + val |= (color.a&0xFF); + break; + case GX_COLOR1A1: + reg = 1; + val |= (color.a&0xFF); + break; + default: + return; + } + + __gx->chnMatColor[reg] = val; + __gx->dirtyState |= (0x0400<=GX_VA_POS && attr<=GX_LIGHTARRAY) { + idx = attr-GX_VA_POS; + GX_LOAD_CP_REG((0xA0+idx),(u32)MEM_VIRTUAL_TO_PHYSICAL(ptr)); + GX_LOAD_CP_REG((0xB0+idx),(u32)stride); + } +} + +static __inline__ void __SETVCDATTR(u8 attr,u8 type) +{ + switch(attr) { + case GX_VA_PTNMTXIDX: + __gx->vcdLo = (__gx->vcdLo&~0x1)|(type&0x1); + break; + case GX_VA_TEX0MTXIDX: + __gx->vcdLo = (__gx->vcdLo&~0x2)|(_SHIFTL(type,1,1)); + break; + case GX_VA_TEX1MTXIDX: + __gx->vcdLo = (__gx->vcdLo&~0x4)|(_SHIFTL(type,2,1)); + break; + case GX_VA_TEX2MTXIDX: + __gx->vcdLo = (__gx->vcdLo&~0x8)|(_SHIFTL(type,3,1)); + break; + case GX_VA_TEX3MTXIDX: + __gx->vcdLo = (__gx->vcdLo&~0x10)|(_SHIFTL(type,4,1)); + break; + case GX_VA_TEX4MTXIDX: + __gx->vcdLo = (__gx->vcdLo&~0x20)|(_SHIFTL(type,5,1)); + break; + case GX_VA_TEX5MTXIDX: + __gx->vcdLo = (__gx->vcdLo&~0x40)|(_SHIFTL(type,6,1)); + break; + case GX_VA_TEX6MTXIDX: + __gx->vcdLo = (__gx->vcdLo&~0x80)|(_SHIFTL(type,7,1)); + break; + case GX_VA_TEX7MTXIDX: + __gx->vcdLo = (__gx->vcdLo&~0x100)|(_SHIFTL(type,8,1)); + break; + case GX_VA_POS: + __gx->vcdLo = (__gx->vcdLo&~0x600)|(_SHIFTL(type,9,2)); + break; + case GX_VA_NRM: + __gx->vcdLo = (__gx->vcdLo&~0x1800)|(_SHIFTL(type,11,2)); + __gx->vcdNrms = 1; + break; + case GX_VA_NBT: + __gx->vcdLo = (__gx->vcdLo&~0x1800)|(_SHIFTL(type,11,2)); + __gx->vcdNrms = 2; + break; + case GX_VA_CLR0: + __gx->vcdLo = (__gx->vcdLo&~0x6000)|(_SHIFTL(type,13,2)); + break; + case GX_VA_CLR1: + __gx->vcdLo = (__gx->vcdLo&~0x18000)|(_SHIFTL(type,15,2)); + break; + case GX_VA_TEX0: + __gx->vcdHi = (__gx->vcdHi&~0x3)|(type&0x3); + break; + case GX_VA_TEX1: + __gx->vcdHi = (__gx->vcdHi&~0xc)|(_SHIFTL(type,2,2)); + break; + case GX_VA_TEX2: + __gx->vcdHi = (__gx->vcdHi&~0x30)|(_SHIFTL(type,4,2)); + break; + case GX_VA_TEX3: + __gx->vcdHi = (__gx->vcdHi&~0xc0)|(_SHIFTL(type,6,2)); + break; + case GX_VA_TEX4: + __gx->vcdHi = (__gx->vcdHi&~0x300)|(_SHIFTL(type,8,2)); + break; + case GX_VA_TEX5: + __gx->vcdHi = (__gx->vcdHi&~0xc00)|(_SHIFTL(type,10,2)); + break; + case GX_VA_TEX6: + __gx->vcdHi = (__gx->vcdHi&~0x3000)|(_SHIFTL(type,12,2)); + break; + case GX_VA_TEX7: + __gx->vcdHi = (__gx->vcdHi&~0xc000)|(_SHIFTL(type,14,2)); + break; + } +} + +void GX_SetVtxDesc(u8 attr,u8 type) +{ + __SETVCDATTR(attr,type); + __gx->dirtyState |= 0x0008; +} + +void GX_SetVtxDescv(GXVtxDesc *attr_list) +{ + u32 i; + + if(!attr_list) return; + + for(i=0;idirtyState |= 0x0008; +} + +void GX_GetVtxDescv(GXVtxDesc *attr_list) +{ + u32 count; + + // Clear everything first + for(count=0;countvcdLo&0x1) { + attr_list[count].attr = GX_VA_PTNMTXIDX; + attr_list[count].type = __gx->vcdLo&0x1; + count++; + } + + if(__gx->vcdLo&0x2) { + attr_list[count].attr = GX_VA_TEX0MTXIDX; + attr_list[count].type = _SHIFTR(__gx->vcdLo&0x2,1,1); + count++; + } + + if(__gx->vcdLo&0x4) { + attr_list[count].attr = GX_VA_TEX1MTXIDX; + attr_list[count].type = _SHIFTR(__gx->vcdLo&0x4,2,1); + count++; + } + + if(__gx->vcdLo&0x8) { + attr_list[count].attr = GX_VA_TEX2MTXIDX; + attr_list[count].type = _SHIFTR(__gx->vcdLo&0x8,3,1); + count++; + } + + if(__gx->vcdLo&0x10) { + attr_list[count].attr = GX_VA_TEX3MTXIDX; + attr_list[count].type = _SHIFTR(__gx->vcdLo&0x10,4,1); + count++; + } + + if(__gx->vcdLo&0x20) { + attr_list[count].attr = GX_VA_TEX4MTXIDX; + attr_list[count].type = _SHIFTR(__gx->vcdLo&0x20,5,1); + count++; + } + + if(__gx->vcdLo&0x40) { + attr_list[count].attr = GX_VA_TEX5MTXIDX; + attr_list[count].type = _SHIFTR(__gx->vcdLo&0x40,6,1); + count++; + } + + if(__gx->vcdLo&0x80) { + attr_list[count].attr = GX_VA_TEX6MTXIDX; + attr_list[count].type = _SHIFTR(__gx->vcdLo&0x80,7,1); + count++; + } + + if(__gx->vcdLo&0x100) { + attr_list[count].attr = GX_VA_TEX7MTXIDX; + attr_list[count].type = _SHIFTR(__gx->vcdLo&0x100,8,1); + count++; + } + + if(__gx->vcdLo&0x600) { + attr_list[count].attr = GX_VA_POS; + attr_list[count].type = _SHIFTR(__gx->vcdLo&0x600,9,2); + count++; + } + + if(__gx->vcdLo&0x1800) { + if(__gx->vcdNrms==1) { + attr_list[count].attr = GX_VA_NRM; + attr_list[count].type = _SHIFTR(__gx->vcdLo&0x1800,11,2); + count++; + } else if(__gx->vcdNrms==2){ + attr_list[count].attr = GX_VA_NBT; + attr_list[count].type = _SHIFTR(__gx->vcdLo&0x1800,11,2); + count++; + } + } + + if(__gx->vcdLo&0x6000) { + attr_list[count].attr = GX_VA_CLR0; + attr_list[count].type = _SHIFTR(__gx->vcdLo&0x6000,13,2); + count++; + } + + if(__gx->vcdLo&0x18000) { + attr_list[count].attr = GX_VA_CLR1; + attr_list[count].type = _SHIFTR(__gx->vcdLo&0x18000,15,2); + count++; + } + + if(__gx->vcdHi&0x3) { + attr_list[count].attr = GX_VA_TEX0; + attr_list[count].type = __gx->vcdHi&0x3; + count++; + } + + if(__gx->vcdHi&0xc) { + attr_list[count].attr = GX_VA_TEX1; + attr_list[count].type = _SHIFTR(__gx->vcdHi&0xc,2,2); + count++; + } + + if(__gx->vcdHi&0x30) { + attr_list[count].attr = GX_VA_TEX2; + attr_list[count].type = _SHIFTR(__gx->vcdHi&0x30,4,2); + count++; + } + + if(__gx->vcdHi&0xc0) { + attr_list[count].attr = GX_VA_TEX3; + attr_list[count].type = _SHIFTR(__gx->vcdHi&0xc0,6,2); + count++; + } + + if(__gx->vcdHi&0x300) { + attr_list[count].attr = GX_VA_TEX4; + attr_list[count].type = _SHIFTR(__gx->vcdHi&0x300,8,2); + count++; + } + + if(__gx->vcdHi&0xc00) { + attr_list[count].attr = GX_VA_TEX5; + attr_list[count].type = _SHIFTR(__gx->vcdHi&0xc00,10,2); + count++; + } + + if(__gx->vcdHi&0x3000) { + attr_list[count].attr = GX_VA_TEX6; + attr_list[count].type = _SHIFTR(__gx->vcdHi&0x3000,12,2); + count++; + } + + if(__gx->vcdHi&0xc000) { + attr_list[count].attr = GX_VA_TEX7; + attr_list[count].type = _SHIFTR(__gx->vcdHi&0xc000,14,2); + count++; + } +} + +static __inline__ void __SETVCDFMT(u8 vtxfmt,u32 vtxattr,u32 comptype,u32 compsize,u32 frac) +{ + u8 vat = (vtxfmt&7); + + if(vtxattr==GX_VA_POS && (comptype==GX_POS_XY || comptype==GX_POS_XYZ) + && (compsize>=GX_U8 && compsize<=GX_F32)) { + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x1)|(comptype&1); + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0xe)|(_SHIFTL(compsize,1,3)); + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x1f0)|(_SHIFTL(frac,4,5)); + if(frac) + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x40000000)|0x40000000; + } else if(vtxattr==GX_VA_NRM && comptype==GX_NRM_XYZ + && (compsize==GX_S8 || compsize==GX_S16 || compsize==GX_F32)) { + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x200); + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x1C00)|(_SHIFTL(compsize,10,3)); + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x80000000); + } else if(vtxattr==GX_VA_NBT && (comptype==GX_NRM_NBT || comptype==GX_NRM_NBT3) + && (compsize==GX_S8 || compsize==GX_S16 || compsize==GX_F32)) { + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x200)|0x200; + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x1C00)|(_SHIFTL(compsize,10,3)); + if(comptype==GX_NRM_NBT3) + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x80000000)|0x80000000; + } else if(vtxattr==GX_VA_CLR0 && (comptype==GX_CLR_RGB || comptype==GX_CLR_RGBA) + && (compsize>=GX_RGB565 && compsize<=GX_RGBA8)) { + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x2000)|(_SHIFTL(comptype,13,1)); + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x1C000)|(_SHIFTL(compsize,14,3)); + } else if(vtxattr==GX_VA_CLR1 && (comptype==GX_CLR_RGB || comptype==GX_CLR_RGBA) + && (compsize>=GX_RGB565 && compsize<=GX_RGBA8)) { + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x20000)|(_SHIFTL(comptype,17,1)); + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x1C0000)|(_SHIFTL(compsize,18,3)); + } else if(vtxattr==GX_VA_TEX0 && (comptype==GX_TEX_S || comptype==GX_TEX_ST) + && (compsize>=GX_U8 && compsize<=GX_F32)) { + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x200000)|(_SHIFTL(comptype,21,1)); + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x1C00000)|(_SHIFTL(compsize,22,3)); + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x3E000000)|(_SHIFTL(frac,25,5)); + if(frac) + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x40000000)|0x40000000; + } else if(vtxattr==GX_VA_TEX1 && (comptype==GX_TEX_S || comptype==GX_TEX_ST) + && (compsize>=GX_U8 && compsize<=GX_F32)) { + __gx->VAT1reg[vat] = (__gx->VAT1reg[vat]&~0x1)|(comptype&1); + __gx->VAT1reg[vat] = (__gx->VAT1reg[vat]&~0xe)|(_SHIFTL(compsize,1,3)); + __gx->VAT1reg[vat] = (__gx->VAT1reg[vat]&~0x1F0)|(_SHIFTL(frac,4,5)); + if(frac) + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x40000000)|0x40000000; + } else if(vtxattr==GX_VA_TEX2 && (comptype==GX_TEX_S || comptype==GX_TEX_ST) + && (compsize>=GX_U8 && compsize<=GX_F32)) { + __gx->VAT1reg[vat] = (__gx->VAT1reg[vat]&~0x200)|(_SHIFTL(comptype,9,1)); + __gx->VAT1reg[vat] = (__gx->VAT1reg[vat]&~0x1C00)|(_SHIFTL(compsize,10,3)); + __gx->VAT1reg[vat] = (__gx->VAT1reg[vat]&~0x3E000)|(_SHIFTL(frac,13,5)); + if(frac) + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x40000000)|0x40000000; + } else if(vtxattr==GX_VA_TEX3 && (comptype==GX_TEX_S || comptype==GX_TEX_ST) + && (compsize>=GX_U8 && compsize<=GX_F32)) { + __gx->VAT1reg[vat] = (__gx->VAT1reg[vat]&~0x40000)|(_SHIFTL(comptype,18,1)); + __gx->VAT1reg[vat] = (__gx->VAT1reg[vat]&~0x380000)|(_SHIFTL(compsize,19,3)); + __gx->VAT1reg[vat] = (__gx->VAT1reg[vat]&~0x7C00000)|(_SHIFTL(frac,22,5)); + if(frac) + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x40000000)|0x40000000; + } else if(vtxattr==GX_VA_TEX4 && (comptype==GX_TEX_S || comptype==GX_TEX_ST) + && (compsize>=GX_U8 && compsize<=GX_F32)) { + __gx->VAT1reg[vat] = (__gx->VAT1reg[vat]&~0x8000000)|(_SHIFTL(comptype,27,1)); + __gx->VAT1reg[vat] = (__gx->VAT1reg[vat]&~0x70000000)|(_SHIFTL(compsize,28,3)); + __gx->VAT2reg[vat] = (__gx->VAT2reg[vat]&~0x1f)|(frac&0x1f); + if(frac) + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x40000000)|0x40000000; + } else if(vtxattr==GX_VA_TEX5 && (comptype==GX_TEX_S || comptype==GX_TEX_ST) + && (compsize>=GX_U8 && compsize<=GX_F32)) { + __gx->VAT2reg[vat] = (__gx->VAT2reg[vat]&~0x20)|(_SHIFTL(comptype,5,1)); + __gx->VAT2reg[vat] = (__gx->VAT2reg[vat]&~0x1C0)|(_SHIFTL(compsize,6,3)); + __gx->VAT2reg[vat] = (__gx->VAT2reg[vat]&~0x3E00)|(_SHIFTL(frac,9,5)); + if(frac) + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x40000000)|0x40000000; + } else if(vtxattr==GX_VA_TEX6 && (comptype==GX_TEX_S || comptype==GX_TEX_ST) + && (compsize>=GX_U8 && compsize<=GX_F32)) { + __gx->VAT2reg[vat] = (__gx->VAT2reg[vat]&~0x4000)|(_SHIFTL(comptype,14,1)); + __gx->VAT2reg[vat] = (__gx->VAT2reg[vat]&~0x38000)|(_SHIFTL(compsize,15,3)); + __gx->VAT2reg[vat] = (__gx->VAT2reg[vat]&~0x7C0000)|(_SHIFTL(frac,18,5)); + if(frac) + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x40000000)|0x40000000; + } else if(vtxattr==GX_VA_TEX7 && (comptype==GX_TEX_S || comptype==GX_TEX_ST) + && (compsize>=GX_U8 && compsize<=GX_F32)) { + __gx->VAT2reg[vat] = (__gx->VAT2reg[vat]&~0x800000)|(_SHIFTL(comptype,23,1)); + __gx->VAT2reg[vat] = (__gx->VAT2reg[vat]&~0x7000000)|(_SHIFTL(compsize,24,3)); + __gx->VAT2reg[vat] = (__gx->VAT2reg[vat]&~0xF8000000)|(_SHIFTL(frac,27,5)); + if(frac) + __gx->VAT0reg[vat] = (__gx->VAT0reg[vat]&~0x40000000)|0x40000000; + } +} + +void GX_SetVtxAttrFmt(u8 vtxfmt,u32 vtxattr,u32 comptype,u32 compsize,u32 frac) +{ + __SETVCDFMT(vtxfmt,vtxattr,comptype,compsize,frac); + __gx->VATTable |= (1<dirtyState |= 0x0010; +} + +void GX_SetVtxAttrFmtv(u8 vtxfmt,GXVtxAttrFmt *attr_list) +{ + u32 i; + + for(i=0;iVATTable |= (1<dirtyState |= 0x0010; +} + +void GX_Begin(u8 primitve,u8 vtxfmt,u16 vtxcnt) +{ + u8 reg = primitve|(vtxfmt&7); + + if(__gx->dirtyState) + __GX_SetDirtyState(); + + wgPipe->U8 = reg; + wgPipe->U16 = vtxcnt; +} + +void GX_SetTexCoordGen(u16 texcoord,u32 tgen_typ,u32 tgen_src,u32 mtxsrc) +{ + GX_SetTexCoordGen2(texcoord,tgen_typ,tgen_src,mtxsrc,GX_FALSE,GX_DTTIDENTITY); +} + +void GX_SetTexCoordGen2(u16 texcoord,u32 tgen_typ,u32 tgen_src,u32 mtxsrc,u32 normalize,u32 postmtx) +{ + u32 txc; + u32 texcoords; + u8 vtxrow,stq; + + if(texcoord>=GX_MAXCOORD) return; + + stq = 0; + switch(tgen_src) { + case GX_TG_POS: + vtxrow = 0; + stq = 1; + break; + case GX_TG_NRM: + vtxrow = 1; + stq = 1; + break; + case GX_TG_BINRM: + vtxrow = 3; + stq = 1; + break; + case GX_TG_TANGENT: + vtxrow = 4; + stq = 1; + break; + case GX_TG_COLOR0: + vtxrow = 2; + break; + case GX_TG_COLOR1: + vtxrow = 2; + break; + case GX_TG_TEX0: + vtxrow = 5; + break; + case GX_TG_TEX1: + vtxrow = 6; + break; + case GX_TG_TEX2: + vtxrow = 7; + break; + case GX_TG_TEX3: + vtxrow = 8; + break; + case GX_TG_TEX4: + vtxrow = 9; + break; + case GX_TG_TEX5: + vtxrow = 10; + break; + case GX_TG_TEX6: + vtxrow = 11; + break; + case GX_TG_TEX7: + vtxrow = 12; + break; + default: + vtxrow = 5; + break; + } + + texcoords = 0; + txc = (texcoord&7); + if((tgen_typ==GX_TG_MTX3x4 || tgen_typ==GX_TG_MTX2x4)) + { + if(tgen_typ==GX_TG_MTX3x4) texcoords = 0x02; + + texcoords |= (_SHIFTL(stq,2,1)); + texcoords |= (_SHIFTL(vtxrow,7,5)); + } else if((tgen_typ>=GX_TG_BUMP0 && tgen_typ<=GX_TG_BUMP7)) + { + tgen_src -= GX_TG_TEXCOORD0; + tgen_typ -= GX_TG_BUMP0; + + texcoords = 0x10; + texcoords |= (_SHIFTL(stq,2,1)); + texcoords |= (_SHIFTL(vtxrow,7,5)); + texcoords |= (_SHIFTL(tgen_src,12,3)); + texcoords |= (_SHIFTL(tgen_typ,15,3)); + } else if(tgen_typ==GX_TG_SRTG) { + if(tgen_src==GX_TG_COLOR0) texcoords = 0x20; + else if(tgen_src==GX_TG_COLOR1) texcoords = 0x30; + texcoords |= (_SHIFTL(stq,2,1)); + texcoords |= (_SHIFTL(2,7,5)); + } + + postmtx -= GX_DTTMTX0; + __gx->texCoordGen[txc] = texcoords; + __gx->texCoordGen2[txc] = ((_SHIFTL(normalize,8,1))|(postmtx&0x3f)); + + switch(texcoord) { + case GX_TEXCOORD0: + __gx->mtxIdxLo = (__gx->mtxIdxLo&~0xfc0)|(_SHIFTL(mtxsrc,6,6)); + break; + case GX_TEXCOORD1: + __gx->mtxIdxLo = (__gx->mtxIdxLo&~0x3f000)|(_SHIFTL(mtxsrc,12,6)); + break; + case GX_TEXCOORD2: + __gx->mtxIdxLo = (__gx->mtxIdxLo&~0xfc0000)|(_SHIFTL(mtxsrc,18,6)); + break; + case GX_TEXCOORD3: + __gx->mtxIdxLo = (__gx->mtxIdxLo&~0x3f000000)|(_SHIFTL(mtxsrc,24,6)); + break; + case GX_TEXCOORD4: + __gx->mtxIdxHi = (__gx->mtxIdxHi&~0x3f)|(mtxsrc&0x3f); + break; + case GX_TEXCOORD5: + __gx->mtxIdxHi = (__gx->mtxIdxHi&~0xfc0)|(_SHIFTL(mtxsrc,6,6)); + break; + case GX_TEXCOORD6: + __gx->mtxIdxHi = (__gx->mtxIdxHi&~0x3f000)|(_SHIFTL(mtxsrc,12,6)); + break; + case GX_TEXCOORD7: + __gx->mtxIdxHi = (__gx->mtxIdxHi&~0xfc0000)|(_SHIFTL(mtxsrc,18,6)); + break; + } + __gx->dirtyState |= (0x04000000|(0x00010000<U8 = 0x20; + wgPipe->U32 = ((_SHIFTL(mtxidx,16,16))|0xb000|(_SHIFTL(pnidx,2,8))); +} + +void GX_LoadNrmMtxImm(Mtx mt,u32 pnidx) +{ + GX_LOAD_XF_REGS((0x0400|(pnidx*3)),9); + WriteMtxPS3x3from4x3(mt,(void*)wgPipe); +} + +void GX_LoadNrmMtxImm3x3(Mtx33 mt,u32 pnidx) +{ + GX_LOAD_XF_REGS((0x0400|(pnidx*3)),9); + WriteMtxPS3x3(mt,(void*)wgPipe); +} + +void GX_LoadNrmMtxIdx3x3(u16 mtxidx,u32 pnidx) +{ + wgPipe->U8 = 0x28; + wgPipe->U32 = ((_SHIFTL(mtxidx,16,16))|0x8000|(0x0400|(pnidx*3))); +} + +void GX_LoadTexMtxImm(Mtx mt,u32 texidx,u8 type) +{ + u32 addr = 0; + u32 rows = (type==GX_MTX2x4)?2:3; + + if(texidxU8 = 0x30; + wgPipe->U32 = ((_SHIFTL(mtxidx,16,16))|(_SHIFTL(size,12,4))|addr); +} + +void GX_SetCurrentMtx(u32 mtx) +{ + __gx->mtxIdxLo = (__gx->mtxIdxLo&~0x3f)|(mtx&0x3f); + __gx->dirtyState |= 0x04000000; +} + +void GX_SetNumTexGens(u32 nr) +{ + __gx->genMode = (__gx->genMode&~0xf)|(nr&0xf); + __gx->dirtyState |= 0x02000004; +} + +void GX_InvVtxCache() +{ + wgPipe->U8 = 0x48; // vertex cache weg +} + +void GX_SetZMode(u8 enable,u8 func,u8 update_enable) +{ + __gx->peZMode = (__gx->peZMode&~0x1)|(enable&1); + __gx->peZMode = (__gx->peZMode&~0xe)|(_SHIFTL(func,1,3)); + __gx->peZMode = (__gx->peZMode&~0x10)|(_SHIFTL(update_enable,4,1)); + GX_LOAD_BP_REG(__gx->peZMode); +} + +u32 GX_GetTexObjFmt(GXTexObj *obj) +{ + return ((struct __gx_texobj*)obj)->tex_fmt; +} + +u32 GX_GetTexObjMipMap(GXTexObj *obj) +{ + return (((struct __gx_texobj*)obj)->tex_flag&0x01); +} +void* GX_GetTexObjData(GXTexObj *obj) +{ + return (void*)(_SHIFTL(((struct __gx_texobj*)obj)->tex_maddr & 0x00ffffff,5,24)); +} + +u8 GX_GetTexObjWrapS(GXTexObj* obj) +{ + return ((struct __gx_texobj*)obj)->tex_filt & 0x03; +} + +u8 GX_GetTexObjWrapT(GXTexObj* obj) +{ + return _SHIFTR(((struct __gx_texobj*)obj)->tex_filt & 0x0c, 2, 2); +} + +u16 GX_GetTexObjHeight(GXTexObj* obj) +{ + return _SHIFTR(((struct __gx_texobj*)obj)->tex_size & 0xffc00, 10, 10) + 1; +} + +u16 GX_GetTexObjWidth(GXTexObj* obj) +{ + return (((struct __gx_texobj*)obj)->tex_size & 0x3ff) + 1; +} + + +void GX_GetTexObjAll(GXTexObj* obj, void** image_ptr, u16* width, u16* height, + u8* format, u8* wrap_s, u8* wrap_t, u8* mipmap) +{ + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + *image_ptr = (void*)(_SHIFTL(ptr->tex_maddr & 0x00ffffff,5,24)); + *width = (ptr->tex_size & 0x3ff) + 1; + *height = _SHIFTR(ptr->tex_size & 0xffc00, 10, 10) + 1; + *format = ptr->tex_fmt; + *wrap_s = ptr->tex_filt & 0x03; + *wrap_t = _SHIFTR(ptr->tex_filt & 0x0c, 2, 2); + *mipmap = ptr->tex_flag & 0x01; +} +u32 GX_GetTexBufferSize(u16 wd,u16 ht,u32 fmt,u8 mipmap,u8 maxlod) +{ + u32 xshift,yshift,xtiles,ytiles,bitsize,size; + + switch(fmt) { + case GX_TF_I4: + case GX_TF_CMPR: + case GX_CTF_R4: + case GX_CTF_RA4: + case GX_CTF_Z4: + xshift = 3; + yshift = 3; + break; + case GX_TF_Z8: + case GX_TF_I8: + case GX_TF_IA4: + case GX_CTF_A8: + case GX_CTF_R8: + case GX_CTF_G8: + case GX_CTF_B8: + case GX_CTF_RG8: + case GX_CTF_GB8: + case GX_CTF_Z8M: + case GX_CTF_Z8L: + xshift = 3; + yshift = 2; + break; + case GX_TF_IA8: + case GX_TF_Z16: + case GX_TF_Z24X8: + case GX_TF_RGB565: + case GX_TF_RGB5A3: + case GX_TF_RGBA8: + case GX_CTF_Z16L: + case GX_CTF_RA8: + xshift = 2; + yshift = 2; + break; + default: + xshift = 2; + yshift = 2; + break; + } + + bitsize = 32; + if(fmt==GX_TF_RGBA8 || fmt==GX_TF_Z24X8) bitsize = 64; + + size = 0; + if(mipmap) { + u32 cnt = (maxlod&0xff); + while(cnt) { + u32 w = wd&0xffff; + u32 h = ht&0xffff; + xtiles = ((w+(1<>xshift; + ytiles = ((h+(1<>yshift; + if(cnt==0) return size; + + size += ((xtiles*ytiles)*bitsize); + if(w==0x0001 && h==0x0001) return size; + if(wd>0x0001) wd = (w>>1); + else wd = 0x0001; + if(ht>0x0001) ht = (h>>1); + else ht = 0x0001; + + --cnt; + } + return size; + } + + wd &= 0xffff; + xtiles = (wd+((1<>xshift; + + ht &= 0xffff; + ytiles = (ht+((1<>yshift; + + size = ((xtiles*ytiles)*bitsize); + + return size; +} + +void GX_InitTexCacheRegion(GXTexRegion *region,u8 is32bmipmap,u32 tmem_even,u8 size_even,u32 tmem_odd,u8 size_odd) +{ + u32 sze = 0; + struct __gx_texregion *ptr = (struct __gx_texregion*)region; + + switch(size_even) { + case GX_TEXCACHE_32K: + sze = 3; + break; + case GX_TEXCACHE_128K: + sze = 4; + break; + case GX_TEXCACHE_512K: + sze = 5; + break; + default: + sze = 3; + break; + } + ptr->tmem_even = 0; + ptr->tmem_even = (ptr->tmem_even&~0x7fff)|(_SHIFTR(tmem_even,5,15)); + ptr->tmem_even = (ptr->tmem_even&~0x38000)|(_SHIFTL(sze,15,3)); + ptr->tmem_even = (ptr->tmem_even&~0x1C0000)|(_SHIFTL(sze,18,3)); + + switch(size_odd) { + case GX_TEXCACHE_32K: + sze = 3; + break; + case GX_TEXCACHE_128K: + sze = 4; + break; + case GX_TEXCACHE_512K: + sze = 5; + break; + default: + sze = 3; + break; + } + ptr->tmem_odd = 0; + ptr->tmem_odd = (ptr->tmem_odd&~0x7fff)|(_SHIFTR(tmem_odd,5,15)); + ptr->tmem_odd = (ptr->tmem_odd&~0x38000)|(_SHIFTL(sze,15,3)); + ptr->tmem_odd = (ptr->tmem_odd&~0x1C0000)|(_SHIFTL(sze,18,3)); + + ptr->ismipmap = is32bmipmap; + ptr->iscached = 1; +} + +void GX_InitTexPreloadRegion(GXTexRegion *region,u32 tmem_even,u32 size_even,u32 tmem_odd,u32 size_odd) +{ + struct __gx_texregion *ptr = (struct __gx_texregion*)region; + + ptr->tmem_even = 0; + ptr->tmem_even = (ptr->tmem_even&~0x7FFF)|(_SHIFTR(tmem_even,5,15)); + ptr->tmem_even = (ptr->tmem_even&~0x200000)|0x200000; + + ptr->tmem_odd = 0; + ptr->tmem_odd = (ptr->tmem_odd&~0x7FFF)|(_SHIFTR(tmem_odd,5,15)); + + ptr->size_even = _SHIFTR(size_even,5,16); + ptr->size_odd = _SHIFTR(size_odd,5,16); + + ptr->ismipmap = 0; + ptr->iscached = 0; +} + +void GX_InitTlutRegion(GXTlutRegion *region,u32 tmem_addr,u8 tlut_sz) +{ + struct __gx_tlutregion *ptr = (struct __gx_tlutregion*)region; + + tmem_addr -= 0x80000; + + ptr->tmem_addr_conf = 0; + ptr->tmem_addr_conf = (ptr->tmem_addr_conf&~0x3ff)|(_SHIFTR(tmem_addr,9,10)); + ptr->tmem_addr_conf = (ptr->tmem_addr_conf&~0x1FFC00)|(_SHIFTL(tlut_sz,10,10)); + ptr->tmem_addr_conf = (ptr->tmem_addr_conf&~0xff000000)|(_SHIFTL(0x65,24,8)); +} + +void GX_InitTexObj(GXTexObj *obj,void *img_ptr,u16 wd,u16 ht,u8 fmt,u8 wrap_s,u8 wrap_t,u8 mipmap) +{ + u32 nwd,nht; + u32 xshift,yshift; + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + + if(!obj) return; + + memset(obj,0,sizeof(GXTexObj)); + + ptr->tex_filt = (ptr->tex_filt&~0x03)|(wrap_s&3); + ptr->tex_filt = (ptr->tex_filt&~0x0c)|(_SHIFTL(wrap_t,2,2)); + ptr->tex_filt = (ptr->tex_filt&~0x10)|0x10; + + if(mipmap) { + ptr->tex_flag |= 0x01; + if(fmt==GX_TF_CI4 || fmt==GX_TF_CI8 || fmt==GX_TF_CI14) + ptr->tex_filt = (ptr->tex_filt&~0xe0)|0x00a0; + else + ptr->tex_filt = (ptr->tex_filt&~0xe0)|0x00c0; + } else + ptr->tex_filt= (ptr->tex_filt&~0xE0)|0x0080; + + ptr->tex_fmt = fmt; + ptr->tex_size = (ptr->tex_size&~0x3ff)|((wd-1)&0x3ff); + ptr->tex_size = (ptr->tex_size&~0xFFC00)|(_SHIFTL((ht-1),10,10)); + ptr->tex_size = (ptr->tex_size&~0xF00000)|(_SHIFTL(fmt,20,4)); + ptr->tex_maddr = (ptr->tex_maddr&~0x00ffffff)|(_SHIFTR(MEM_VIRTUAL_TO_PHYSICAL(img_ptr),5,24)); + + switch(fmt) { + case GX_TF_I4: + case GX_TF_CI4: + xshift = 3; + yshift = 3; + ptr->tex_tile_type = 1; + break; + case GX_TF_I8: + case GX_TF_IA4: + case GX_TF_CI8: + xshift = 3; + yshift = 2; + ptr->tex_tile_type = 2; + break; + case GX_TF_IA8: + case GX_TF_RGB565: + case GX_TF_RGB5A3: + case GX_TF_RGBA8: + xshift = 2; + yshift = 2; + ptr->tex_tile_type = 2; + break; + case GX_TF_CI14: + xshift = 2; + yshift = 2; + ptr->tex_tile_type = 3; + break; + case GX_TF_CMPR: + xshift = 3; + yshift = 3; + ptr->tex_tile_type = 0; + break; + default: + xshift = 2; + yshift = 2; + ptr->tex_tile_type = 2; + break; + } + + nwd = ((wd+(1<>xshift; + nht = ((ht+(1<>yshift; + ptr->tex_tile_cnt = (nwd*nht)&0x7fff; + + ptr->tex_flag |= 0x0002; +} + +void GX_InitTexObjCI(GXTexObj *obj,void *img_ptr,u16 wd,u16 ht,u8 fmt,u8 wrap_s,u8 wrap_t,u8 mipmap,u32 tlut_name) +{ + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + + GX_InitTexObj(obj,img_ptr,wd,ht,fmt,wrap_s,wrap_t,mipmap); + ptr->tex_flag &= ~0x02; + ptr->tex_tlut = tlut_name; +} + +void GX_InitTexObjLOD(GXTexObj *obj,u8 minfilt,u8 magfilt,f32 minlod,f32 maxlod,f32 lodbias,u8 biasclamp,u8 edgelod,u8 maxaniso) +{ + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + static u8 GX2HWFiltConv[] = {0x00,0x04,0x01,0x05,0x02,0x06,0x00,0x00}; + //static u8 HW2GXFiltConv[] = {0x00,0x02,0x04,0x00,0x01,0x03,0x05,0x00}; + + if(lodbias<-4.0f) lodbias = -4.0f; + else if(lodbias==4.0f) lodbias = 3.99f; + + ptr->tex_filt = (ptr->tex_filt&~0x1fe00)|(_SHIFTL(((u32)(32.0f*lodbias)),9,8)); + ptr->tex_filt = (ptr->tex_filt&~0x10)|(_SHIFTL((magfilt==GX_LINEAR?1:0),4,1)); + ptr->tex_filt = (ptr->tex_filt&~0xe0)|(_SHIFTL(GX2HWFiltConv[minfilt],5,3)); + ptr->tex_filt = (ptr->tex_filt&~0x100)|(_SHIFTL(!(edgelod&0xff),8,1)); + ptr->tex_filt = (ptr->tex_filt&~0x180000)|(_SHIFTL(maxaniso,19,2)); + ptr->tex_filt = (ptr->tex_filt&~0x200000)|(_SHIFTL(biasclamp,21,1)); + + if(minlod<0.0f) minlod = 0.0f; + else if(minlod>10.0f) minlod = 10.0f; + + if(maxlod<0.0f) maxlod = 0.0f; + else if(maxlod>10.0f) maxlod = 10.0f; + + ptr->tex_lod = (ptr->tex_lod&~0xff)|(((u32)(16.0f*minlod))&0xff); + ptr->tex_lod = (ptr->tex_lod&~0xff00)|(_SHIFTL(((u32)(16.0f*maxlod)),8,8)); +} + +void GX_InitTexObjData(GXTexObj *obj,void *img_ptr) +{ + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + ptr->tex_maddr = (ptr->tex_maddr&~0x00ffffff)|(_SHIFTR(MEM_VIRTUAL_TO_PHYSICAL(img_ptr),5,24)); +} + +void GX_InitTexObjTlut(GXTexObj *obj,u32 tlut_name) +{ + ((struct __gx_texobj*)obj)->tex_tlut = tlut_name; +} + +void GX_InitTexObjWrapMode(GXTexObj *obj,u8 wrap_s,u8 wrap_t) +{ + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + + ptr->tex_filt = (ptr->tex_filt&~0x03)|(wrap_s&3); + ptr->tex_filt = (ptr->tex_filt&~0x0c)|(_SHIFTL(wrap_t,2,2)); +} + +void GX_InitTexObjFilterMode(GXTexObj *obj,u8 minfilt,u8 magfilt) +{ + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + static u8 GX2HWFiltConv[] = {0x00,0x04,0x01,0x05,0x02,0x06,0x00,0x00}; + + ptr->tex_filt = (ptr->tex_filt&~0x10)|(_SHIFTL((magfilt==GX_LINEAR?1:0),4,1)); + ptr->tex_filt = (ptr->tex_filt&~0xe0)|(_SHIFTL(GX2HWFiltConv[minfilt],5,3)); +} + +void GX_InitTexObjMinLOD(GXTexObj *obj,f32 minlod) +{ + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + + if(minlod<0.0f) minlod = 0.0f; + else if(minlod>10.0f) minlod = 10.0f; + + ptr->tex_lod = (ptr->tex_lod&~0xff)|(((u32)(16.0f*minlod))&0xff); +} + +void GX_InitTexObjMaxLOD(GXTexObj *obj,f32 maxlod) +{ + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + + if(maxlod<0.0f) maxlod = 0.0f; + else if(maxlod>10.0f) maxlod = 10.0f; + + ptr->tex_lod = (ptr->tex_lod&~0xff00)|(_SHIFTL(((u32)(16.0f*maxlod)),8,8)); +} + +void GX_InitTexObjLODBias(GXTexObj *obj,f32 lodbias) +{ + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + + if(lodbias<-4.0f) lodbias = -4.0f; + else if(lodbias==4.0f) lodbias = 3.99f; + + ptr->tex_filt = (ptr->tex_filt&~0x1fe00)|(_SHIFTL(((u32)(32.0f*lodbias)),9,8)); +} + +void GX_InitTexObjBiasClamp(GXTexObj *obj,u8 biasclamp) +{ + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + ptr->tex_filt = (ptr->tex_filt&~0x200000)|(_SHIFTL(biasclamp,21,1)); +} + +void GX_InitTexObjEdgeLOD(GXTexObj *obj,u8 edgelod) +{ + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + ptr->tex_filt = (ptr->tex_filt&~0x100)|(_SHIFTL(!(edgelod&0xff),8,1)); +} + +void GX_InitTexObjMaxAniso(GXTexObj *obj,u8 maxaniso) +{ + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + ptr->tex_filt = (ptr->tex_filt&~0x180000)|(_SHIFTL(maxaniso,19,2)); +} + +void GX_InitTexObjUserData(GXTexObj *obj,void *userdata) +{ + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + ptr->usr_data = (u32)userdata; +} + +void* GX_GetTexObjUserData(GXTexObj *obj) +{ + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + return (void*)ptr->usr_data; +} + +void GX_InitTlutObj(GXTlutObj *obj,void *lut,u8 fmt,u16 entries) +{ + struct __gx_tlutobj *ptr = (struct __gx_tlutobj*)obj; + + memset(obj,0,sizeof(GXTlutObj)); + + ptr->tlut_fmt = _SHIFTL(fmt,10,2); + ptr->tlut_maddr = (ptr->tlut_maddr&~0x00ffffff)|(_SHIFTR(MEM_VIRTUAL_TO_PHYSICAL(lut),5,24)); + ptr->tlut_maddr = (ptr->tlut_maddr&~0xff000000)|(_SHIFTL(0x64,24,8)); + ptr->tlut_nentries = entries; +} + +void GX_LoadTexObj(GXTexObj *obj,u8 mapid) +{ + GXTexRegion *region = NULL; + + if(regionCB) + region = regionCB(obj,mapid); + + GX_LoadTexObjPreloaded(obj,region,mapid); +} + +void GX_LoadTexObjPreloaded(GXTexObj *obj,GXTexRegion *region,u8 mapid) +{ + u8 type; + struct __gx_tlutregion *tlut = NULL; + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + struct __gx_texregion *reg = (struct __gx_texregion*)region; + + ptr->tex_filt = (ptr->tex_filt&~0xff000000)|(_SHIFTL(_gxtexmode0ids[mapid],24,8)); + ptr->tex_lod = (ptr->tex_lod&~0xff000000)|(_SHIFTL(_gxtexmode1ids[mapid],24,8)); + ptr->tex_size = (ptr->tex_size&~0xff000000)|(_SHIFTL(_gxteximg0ids[mapid],24,8)); + ptr->tex_maddr = (ptr->tex_maddr&~0xff000000)|(_SHIFTL(_gxteximg3ids[mapid],24,8)); + + reg->tmem_even = (reg->tmem_even&~0xff000000)|(_SHIFTL(_gxteximg1ids[mapid],24,8)); + reg->tmem_odd = (reg->tmem_odd&~0xff000000)|(_SHIFTL(_gxteximg2ids[mapid],24,8)); + + GX_LOAD_BP_REG(ptr->tex_filt); + GX_LOAD_BP_REG(ptr->tex_lod); + GX_LOAD_BP_REG(ptr->tex_size); + + GX_LOAD_BP_REG(reg->tmem_even); + GX_LOAD_BP_REG(reg->tmem_odd); + + GX_LOAD_BP_REG(ptr->tex_maddr); + + type = ptr->tex_flag; + if(!(type&0x02)) { + if(tlut_regionCB) + tlut = (struct __gx_tlutregion*)tlut_regionCB(ptr->tex_tlut); + tlut->tmem_addr_base = (tlut->tmem_addr_base&~0xff000000)|(_SHIFTL(_gxtextlutids[mapid],24,8)); + GX_LOAD_BP_REG(tlut->tmem_addr_base); + } + + __gx->texMapSize[mapid] = ptr->tex_size; + __gx->texMapWrap[mapid] = ptr->tex_filt; + + __gx->dirtyState |= 0x0001; +} + +void GX_PreloadEntireTexture(GXTexObj *obj,GXTexRegion *region) +{ + u32 i,fmt; + s32 wd,ht; + u16 cnt = 0; + u32 regA = 0; + u32 regB = 0; + u32 regC = 0; + u32 regD = 0; + struct __gx_texobj *ptr = (struct __gx_texobj*)obj; + struct __gx_texregion *reg = (struct __gx_texregion*)region; + + regA = (regA&~0xff000000)|(_SHIFTL(0x60,24,8)); + regA = (regA&~0x00ffffff)|(ptr->tex_maddr&~0xff000000); + + regB = (regB&~0xff000000)|(_SHIFTL(0x61,24,8)); + regB = (regB&~0x00007fff)|(reg->tmem_even&0x00007fff); + + regC = (regC&~0xff000000)|(_SHIFTL(0x62,24,8)); + regC = (regC&~0x00007fff)|(reg->tmem_odd&0x00007fff); + + regD = (regD&~0xff000000)|(_SHIFTL(0x63,24,8)); + regD = (regD&~0x00007fff)|(ptr->tex_tile_cnt&0x00007fff); + regD = (regD&~0x00018000)|(_SHIFTL(ptr->tex_tile_type,15,2)); + + fmt = _SHIFTR(ptr->tex_size,20,4); + + __GX_FlushTextureState(); + GX_LOAD_BP_REG(regA); + GX_LOAD_BP_REG(regB); + GX_LOAD_BP_REG(regC); + GX_LOAD_BP_REG(regD); + + if(ptr->tex_flag&0x01) { + wd = (ptr->tex_size&0x3ff)+1; + ht = _SHIFTR(ptr->tex_size,10,10)+1; + if(wd>ht) + cnt = (31 - (cntlzw(wd))); + else + cnt = (31 - (cntlzw(ht))); + } + + if(cnt>0) { + u32 tmem_even,tmem_odd,maddr; + u32 tile_cnt = ptr->tex_tile_cnt; + + tmem_even = (reg->tmem_even&0xffff); + tmem_odd = (reg->tmem_odd&0xffff); + maddr = (ptr->tex_maddr&~0xff000000); + + i = 0; + while(cnt) { + u32 w,h; + u32 te,to; + u32 xshift,yshift; + + if(fmt==GX_TF_RGBA8) { + tmem_even += tile_cnt; + tmem_odd += tile_cnt; + maddr += (tile_cnt<<1); + } else { + maddr += tile_cnt; + if(i&1) tmem_odd += tile_cnt; + else tmem_even += tile_cnt; + } + + te = tmem_even; + to = tmem_odd; + if(i&1) { + te = tmem_odd; + to = tmem_even; + } + + w = wd>>(i+1); + h = wd>>(i+1); + switch(ptr->tex_fmt) { + case GX_TF_I4: + case GX_TF_IA4: + case GX_TF_CI4: + case GX_TF_CMPR: + xshift = 3; + yshift = 3; + break; + case GX_TF_I8: + case GX_TF_CI8: + xshift = 3; + yshift = 2; + break; + case GX_TF_IA8: + case GX_TF_RGB5A3: + case GX_TF_RGB565: + case GX_TF_CI14: + xshift = 2; + yshift = 2; + break; + default: + xshift = 0; + yshift = 0; + break; + } + + if(!w) w = 1; + if(!h) h = 1; + + regA = ((regA&~0x00ffffff)|(maddr&0x00ffffff)); + GX_LOAD_BP_REG(regA); + + regB = ((regB&~0x00007fff)|(te&0x00007fff)); + GX_LOAD_BP_REG(regB); + + regC = ((regC&~0x00007fff)|(to&0x00007fff)); + GX_LOAD_BP_REG(regC); + + tile_cnt = (((w+(1<>xshift)*(((h+(1<>yshift); + regD = ((regD&~0x00007fff)|(tile_cnt&0x00007fff)); + GX_LOAD_BP_REG(regD); + + ++i; + --cnt; + } + } + __GX_FlushTextureState(); +} + +void GX_InvalidateTexAll() +{ + __GX_FlushTextureState(); + GX_LOAD_BP_REG(0x66001000); + GX_LOAD_BP_REG(0x66001100); + __GX_FlushTextureState(); +} + +void GX_InvalidateTexRegion(GXTexRegion *region) +{ + u8 ismipmap; + s32 cw_e,ch_e,cw_o,ch_o; + u32 size,tmp,regvalA = 0,regvalB = 0; + struct __gx_texregion *ptr = (struct __gx_texregion*)region; + + cw_e = (_SHIFTR(ptr->tmem_even,15,3))-1; + ch_e = (_SHIFTR(ptr->tmem_even,18,3))-1; + + cw_o = (_SHIFTR(ptr->tmem_odd,15,3))-1; + ch_o = (_SHIFTR(ptr->tmem_odd,18,3))-1; + + if(cw_e<0) cw_e = 0; + if(ch_e<0) ch_e = 0; + if(cw_o<0) cw_o = 0; + if(ch_o<0) ch_o = 0; + + ismipmap = ptr->ismipmap; + + tmp = size = cw_e+ch_e; + if(ismipmap) size = (tmp+cw_o+ch_o)-2; + regvalA = _SHIFTR((ptr->tmem_even&0x7fff),6,9)|(_SHIFTL(size,9,4))|(_SHIFTL(0x66,24,8)); + + if(cw_o!=0) { + size = cw_o+ch_o; + if(ismipmap) size += (tmp-2); + regvalB = _SHIFTR((ptr->tmem_odd&0x7fff),6,9)|(_SHIFTL(size,9,4))|(_SHIFTL(0x66,24,8)); + } + __GX_FlushTextureState(); + GX_LOAD_BP_REG(regvalA); + if(cw_o!=0) GX_LOAD_BP_REG(regvalB); + __GX_FlushTextureState(); +} + +void GX_LoadTlut(GXTlutObj *obj,u32 tlut_name) +{ + struct __gx_tlutregion *region = NULL; + struct __gx_tlutobj *ptr = (struct __gx_tlutobj*)obj; + + if(tlut_regionCB) + region = (struct __gx_tlutregion*)tlut_regionCB(tlut_name); + + __GX_FlushTextureState(); + GX_LOAD_BP_REG(ptr->tlut_maddr); + GX_LOAD_BP_REG(region->tmem_addr_conf); + __GX_FlushTextureState(); + + region->tmem_addr_base = (ptr->tlut_fmt&~0x3ff)|(region->tmem_addr_conf&0x3ff); + region->tlut_maddr = ptr->tlut_maddr; + region->tlut_nentries = ptr->tlut_nentries; +} + +void GX_SetTexCoordScaleManually(u8 texcoord,u8 enable,u16 ss,u16 ts) +{ + u32 reg; + + __gx->texCoordManually = (__gx->texCoordManually&~(_SHIFTL(1,texcoord,1)))|(_SHIFTL(enable,texcoord,1)); + if(!enable) return; + + reg = (texcoord&0x7); + __gx->suSsize[reg] = (__gx->suSsize[reg]&~0xffff)|((ss-1)&0xffff); + __gx->suTsize[reg] = (__gx->suTsize[reg]&~0xffff)|((ts-1)&0xffff); + + GX_LOAD_BP_REG(__gx->suSsize[reg]); + GX_LOAD_BP_REG(__gx->suTsize[reg]); +} + +void GX_SetTexCoordCylWrap(u8 texcoord,u8 s_enable,u8 t_enable) +{ + u32 reg; + + reg = (texcoord&0x7); + __gx->suSsize[reg] = (__gx->suSsize[reg]&~0x20000)|(_SHIFTL(s_enable,17,1)); + __gx->suTsize[reg] = (__gx->suTsize[reg]&~0x20000)|(_SHIFTL(t_enable,17,1)); + + if(!(__gx->texCoordManually&(_SHIFTL(1,texcoord,1)))) return; + + GX_LOAD_BP_REG(__gx->suSsize[reg]); + GX_LOAD_BP_REG(__gx->suTsize[reg]); +} + +void GX_SetTexCoordBias(u8 texcoord,u8 s_enable,u8 t_enable) +{ + u32 reg; + + reg = (texcoord&0x7); + __gx->suSsize[reg] = (__gx->suSsize[reg]&~0x10000)|(_SHIFTL(s_enable,16,1)); + __gx->suTsize[reg] = (__gx->suTsize[reg]&~0x10000)|(_SHIFTL(t_enable,16,1)); + + if(!(__gx->texCoordManually&(_SHIFTL(1,texcoord,1)))) return; + + GX_LOAD_BP_REG(__gx->suSsize[reg]); + GX_LOAD_BP_REG(__gx->suTsize[reg]); +} + +GXTexRegionCallback GX_SetTexRegionCallback(GXTexRegionCallback cb) +{ + u32 level; + GXTexRegionCallback ret; + + _CPU_ISR_Disable(level); + ret = regionCB; + regionCB = cb; + _CPU_ISR_Restore(level); + + return ret; +} + +GXTlutRegionCallback GX_SetTlutRegionCallback(GXTlutRegionCallback cb) +{ + u32 level; + GXTlutRegionCallback ret; + + _CPU_ISR_Disable(level); + ret = tlut_regionCB; + tlut_regionCB = cb; + _CPU_ISR_Restore(level); + + return ret; +} + +void GX_SetBlendMode(u8 type,u8 src_fact,u8 dst_fact,u8 op) +{ + __gx->peCMode0 = (__gx->peCMode0&~0x1); + if(type==GX_BM_BLEND || type==GX_BM_SUBTRACT) __gx->peCMode0 |= 0x1; + + __gx->peCMode0 = (__gx->peCMode0&~0x800); + if(type==GX_BM_SUBTRACT) __gx->peCMode0 |= 0x800; + + __gx->peCMode0 = (__gx->peCMode0&~0x2); + if(type==GX_BM_LOGIC) __gx->peCMode0 |= 0x2; + + __gx->peCMode0 = (__gx->peCMode0&~0xF000)|(_SHIFTL(op,12,4)); + __gx->peCMode0 = (__gx->peCMode0&~0xE0)|(_SHIFTL(dst_fact,5,3)); + __gx->peCMode0 = (__gx->peCMode0&~0x700)|(_SHIFTL(src_fact,8,3)); + + GX_LOAD_BP_REG(__gx->peCMode0); +} + +void GX_ClearVtxDesc() +{ + __gx->vcdNrms = 0; + __gx->vcdClear = ((__gx->vcdClear&~0x0600)|0x0200); + __gx->vcdLo = __gx->vcdHi = 0; + __gx->dirtyState |= 0x0008; +} + +void GX_SetLineWidth(u8 width,u8 fmt) +{ + __gx->lpWidth = (__gx->lpWidth&~0xff)|(width&0xff); + __gx->lpWidth = (__gx->lpWidth&~0x70000)|(_SHIFTL(fmt,16,3)); + GX_LOAD_BP_REG(__gx->lpWidth); +} + +void GX_SetPointSize(u8 width,u8 fmt) +{ + __gx->lpWidth = (__gx->lpWidth&~0xFF00)|(_SHIFTL(width,8,8)); + __gx->lpWidth = (__gx->lpWidth&~0x380000)|(_SHIFTL(fmt,19,3)); + GX_LOAD_BP_REG(__gx->lpWidth); +} + +void GX_SetTevColor(u8 tev_regid,GXColor color) +{ + u32 reg; + + reg = (_SHIFTL((0xe0+(tev_regid<<1)),24,8)|(_SHIFTL(color.a,12,8))|(color.r&0xff)); + GX_LOAD_BP_REG(reg); + + reg = (_SHIFTL((0xe1+(tev_regid<<1)),24,8)|(_SHIFTL(color.g,12,8))|(color.b&0xff)); + GX_LOAD_BP_REG(reg); + + //this two calls should obviously flush the Write Gather Pipe. + GX_LOAD_BP_REG(reg); + GX_LOAD_BP_REG(reg); +} + +void GX_SetTevColorS10(u8 tev_regid,GXColorS10 color) +{ + u32 reg; + + reg = (_SHIFTL((0xe0+(tev_regid<<1)),24,8)|(_SHIFTL(color.a,12,11))|(color.r&0x7ff)); + GX_LOAD_BP_REG(reg); + + reg = (_SHIFTL((0xe1+(tev_regid<<1)),24,8)|(_SHIFTL(color.g,12,11))|(color.b&0x7ff)); + GX_LOAD_BP_REG(reg); + + //this two calls should obviously flush the Write Gather Pipe. + GX_LOAD_BP_REG(reg); + GX_LOAD_BP_REG(reg); +} + +void GX_SetTevKColor(u8 tev_kregid,GXColor color) +{ + u32 reg; + + reg = (_SHIFTL((0xe0+(tev_kregid<<1)),24,8)|(_SHIFTL(1,23,1))|(_SHIFTL(color.a,12,8))|(color.r&0xff)); + GX_LOAD_BP_REG(reg); + + reg = (_SHIFTL((0xe1+(tev_kregid<<1)),24,8)|(_SHIFTL(1,23,1))|(_SHIFTL(color.g,12,8))|(color.b&0xff)); + GX_LOAD_BP_REG(reg); + + //this two calls should obviously flush the Write Gather Pipe. + GX_LOAD_BP_REG(reg); + GX_LOAD_BP_REG(reg); +} + +void GX_SetTevKColorS10(u8 tev_kregid,GXColorS10 color) +{ + u32 reg; + + reg = (_SHIFTL((0xe0+(tev_kregid<<1)),24,8)|(_SHIFTL(1,23,1))|(_SHIFTL(color.a,12,11))|(color.r&0x7ff)); + GX_LOAD_BP_REG(reg); + + reg = (_SHIFTL((0xe1+(tev_kregid<<1)),24,8)|(_SHIFTL(1,23,1))|(_SHIFTL(color.g,12,11))|(color.b&0x7ff)); + GX_LOAD_BP_REG(reg); + + //this two calls should obviously flush the Write Gather Pipe. + GX_LOAD_BP_REG(reg); + GX_LOAD_BP_REG(reg); +} + +void GX_SetTevOp(u8 tevstage,u8 mode) +{ + u8 defcolor = GX_CC_RASC; + u8 defalpha = GX_CA_RASA; + + if(tevstage!=GX_TEVSTAGE0) { + defcolor = GX_CC_CPREV; + defalpha = GX_CA_APREV; + } + + switch(mode) { + case GX_MODULATE: + GX_SetTevColorIn(tevstage,GX_CC_ZERO,GX_CC_TEXC,defcolor,GX_CC_ZERO); + GX_SetTevAlphaIn(tevstage,GX_CA_ZERO,GX_CA_TEXA,defalpha,GX_CA_ZERO); + break; + case GX_DECAL: + GX_SetTevColorIn(tevstage,defcolor,GX_CC_TEXC,GX_CC_TEXA,GX_CC_ZERO); + GX_SetTevAlphaIn(tevstage,GX_CA_ZERO,GX_CA_ZERO,GX_CA_ZERO,defalpha); + break; + case GX_BLEND: + GX_SetTevColorIn(tevstage,defcolor,GX_CC_ONE,GX_CC_TEXC,GX_CC_ZERO); + GX_SetTevAlphaIn(tevstage,GX_CA_ZERO,GX_CA_TEXA,defalpha,GX_CA_RASA); + break; + case GX_REPLACE: + GX_SetTevColorIn(tevstage,GX_CC_ZERO,GX_CC_ZERO,GX_CC_ZERO,GX_CC_TEXC); + GX_SetTevAlphaIn(tevstage,GX_CA_ZERO,GX_CA_ZERO,GX_CA_ZERO,GX_CA_TEXA); + break; + case GX_PASSCLR: + GX_SetTevColorIn(tevstage,GX_CC_ZERO,GX_CC_ZERO,GX_CC_ZERO,defcolor); + GX_SetTevAlphaIn(tevstage,GX_CC_A2,GX_CC_A2,GX_CC_A2,defalpha); + break; + } + GX_SetTevColorOp(tevstage,GX_TEV_ADD,GX_TB_ZERO,GX_CS_SCALE_1,GX_TRUE,GX_TEVPREV); + GX_SetTevAlphaOp(tevstage,GX_TEV_ADD,GX_TB_ZERO,GX_CS_SCALE_1,GX_TRUE,GX_TEVPREV); +} + +void GX_SetTevColorIn(u8 tevstage,u8 a,u8 b,u8 c,u8 d) +{ + u32 reg = (tevstage&0xf); + __gx->tevColorEnv[reg] = (__gx->tevColorEnv[reg]&~0xF000)|(_SHIFTL(a,12,4)); + __gx->tevColorEnv[reg] = (__gx->tevColorEnv[reg]&~0xF00)|(_SHIFTL(b,8,4)); + __gx->tevColorEnv[reg] = (__gx->tevColorEnv[reg]&~0xF0)|(_SHIFTL(c,4,4)); + __gx->tevColorEnv[reg] = (__gx->tevColorEnv[reg]&~0xf)|(d&0xf); + + GX_LOAD_BP_REG(__gx->tevColorEnv[reg]); +} + +void GX_SetTevAlphaIn(u8 tevstage,u8 a,u8 b,u8 c,u8 d) +{ + u32 reg = (tevstage&0xf); + __gx->tevAlphaEnv[reg] = (__gx->tevAlphaEnv[reg]&~0xE000)|(_SHIFTL(a,13,3)); + __gx->tevAlphaEnv[reg] = (__gx->tevAlphaEnv[reg]&~0x1C00)|(_SHIFTL(b,10,3)); + __gx->tevAlphaEnv[reg] = (__gx->tevAlphaEnv[reg]&~0x380)|(_SHIFTL(c,7,3)); + __gx->tevAlphaEnv[reg] = (__gx->tevAlphaEnv[reg]&~0x70)|(_SHIFTL(d,4,3)); + + GX_LOAD_BP_REG(__gx->tevAlphaEnv[reg]); +} + +void GX_SetTevColorOp(u8 tevstage,u8 tevop,u8 tevbias,u8 tevscale,u8 clamp,u8 tevregid) +{ + /* set tev op add/sub*/ + u32 reg = (tevstage&0xf); + __gx->tevColorEnv[reg] = (__gx->tevColorEnv[reg]&~0x40000)|(_SHIFTL(tevop,18,1)); + if(tevop<=GX_TEV_SUB) { + __gx->tevColorEnv[reg] = (__gx->tevColorEnv[reg]&~0x300000)|(_SHIFTL(tevscale,20,2)); + __gx->tevColorEnv[reg] = (__gx->tevColorEnv[reg]&~0x30000)|(_SHIFTL(tevbias,16,2)); + } else { + __gx->tevColorEnv[reg] = (__gx->tevColorEnv[reg]&~0x300000)|((_SHIFTL(tevop,19,4))&0x300000); + __gx->tevColorEnv[reg] = (__gx->tevColorEnv[reg]&~0x30000)|0x30000; + } + __gx->tevColorEnv[reg] = (__gx->tevColorEnv[reg]&~0x80000)|(_SHIFTL(clamp,19,1)); + __gx->tevColorEnv[reg] = (__gx->tevColorEnv[reg]&~0xC00000)|(_SHIFTL(tevregid,22,2)); + + GX_LOAD_BP_REG(__gx->tevColorEnv[reg]); +} + +void GX_SetTevAlphaOp(u8 tevstage,u8 tevop,u8 tevbias,u8 tevscale,u8 clamp,u8 tevregid) +{ + /* set tev op add/sub*/ + u32 reg = (tevstage&0xf); + __gx->tevAlphaEnv[reg] = (__gx->tevAlphaEnv[reg]&~0x40000)|(_SHIFTL(tevop,18,1)); + if(tevop<=GX_TEV_SUB) { + __gx->tevAlphaEnv[reg] = (__gx->tevAlphaEnv[reg]&~0x300000)|(_SHIFTL(tevscale,20,2)); + __gx->tevAlphaEnv[reg] = (__gx->tevAlphaEnv[reg]&~0x30000)|(_SHIFTL(tevbias,16,2)); + } else { + __gx->tevAlphaEnv[reg] = (__gx->tevAlphaEnv[reg]&~0x300000)|((_SHIFTL(tevop,19,4))&0x300000); + __gx->tevAlphaEnv[reg] = (__gx->tevAlphaEnv[reg]&~0x30000)|0x30000; + } + __gx->tevAlphaEnv[reg] = (__gx->tevAlphaEnv[reg]&~0x80000)|(_SHIFTL(clamp,19,1)); + __gx->tevAlphaEnv[reg] = (__gx->tevAlphaEnv[reg]&~0xC00000)|(_SHIFTL(tevregid,22,2)); + + GX_LOAD_BP_REG(__gx->tevAlphaEnv[reg]); +} + +void GX_SetCullMode(u8 mode) +{ + static u8 cm2hw[] = { 0, 2, 1, 3 }; + + __gx->genMode = (__gx->genMode&~0xC000)|(_SHIFTL(cm2hw[mode],14,2)); + __gx->dirtyState |= 0x0004; +} + +void GX_SetCoPlanar(u8 enable) +{ + __gx->genMode = (__gx->genMode&~0x80000)|(_SHIFTL(enable,19,1)); + GX_LOAD_BP_REG(0xFE080000); + GX_LOAD_BP_REG(__gx->genMode); +} + +void GX_EnableTexOffsets(u8 coord,u8 line_enable,u8 point_enable) +{ + u32 reg = (coord&0x7); + __gx->suSsize[reg] = (__gx->suSsize[reg]&~0x40000)|(_SHIFTL(line_enable,18,1)); + __gx->suSsize[reg] = (__gx->suSsize[reg]&~0x80000)|(_SHIFTL(point_enable,19,1)); + GX_LOAD_BP_REG(__gx->suSsize[reg]); +} + +void GX_SetClipMode(u8 mode) +{ + GX_LOAD_XF_REG(0x1005,(mode&1)); +} + +void GX_SetScissor(u32 xOrigin,u32 yOrigin,u32 wd,u32 ht) +{ + u32 xo = xOrigin+0x156; + u32 yo = yOrigin+0x156; + u32 nwd = xo+(wd-1); + u32 nht = yo+(ht-1); + + __gx->sciTLcorner = (__gx->sciTLcorner&~0x7ff)|(yo&0x7ff); + __gx->sciTLcorner = (__gx->sciTLcorner&~0x7FF000)|(_SHIFTL(xo,12,11)); + + __gx->sciBRcorner = (__gx->sciBRcorner&~0x7ff)|(nht&0xfff); + __gx->sciBRcorner = (__gx->sciBRcorner&~0x7FF000)|(_SHIFTL(nwd,12,11)); + + GX_LOAD_BP_REG(__gx->sciTLcorner); + GX_LOAD_BP_REG(__gx->sciBRcorner); +} + +void GX_SetScissorBoxOffset(s32 xoffset,s32 yoffset) +{ + s32 xoff = _SHIFTR((xoffset+0x156),1,24); + s32 yoff = _SHIFTR((yoffset+0x156),1,24); + + GX_LOAD_BP_REG((0x59000000|(_SHIFTL(yoff,10,10))|(xoff&0x3ff))); +} + +void GX_SetNumChans(u8 num) +{ + __gx->genMode = (__gx->genMode&~0x70)|(_SHIFTL(num,4,3)); + __gx->dirtyState |= 0x01000004; +} + +void GX_SetTevOrder(u8 tevstage,u8 texcoord,u32 texmap,u8 color) +{ + u8 colid; + u32 texm,texc,tmp; + u32 reg = 3+(_SHIFTR(tevstage,1,3)); + + __gx->tevTexMap[(tevstage&0xf)] = texmap; + + texm = (texmap&~0x100); + if(texm>=GX_MAX_TEXMAP) texm = 0; + if(texcoord>=GX_MAXCOORD) { + texc = 0; + __gx->tevTexCoordEnable &= ~(_SHIFTL(1,tevstage,1)); + } else { + texc = texcoord; + __gx->tevTexCoordEnable |= (_SHIFTL(1,tevstage,1)); + } + + if(tevstage&1) { + __gx->tevRasOrder[reg] = (__gx->tevRasOrder[reg]&~0x7000)|(_SHIFTL(texm,12,3)); + __gx->tevRasOrder[reg] = (__gx->tevRasOrder[reg]&~0x38000)|(_SHIFTL(texc,15,3)); + + colid = GX_ALPHA_BUMP; + if(color!=GX_COLORNULL) colid = _gxtevcolid[color]; + __gx->tevRasOrder[reg] = (__gx->tevRasOrder[reg]&~0x380000)|(_SHIFTL(colid,19,3)); + + tmp = 1; + if(texmap==GX_TEXMAP_NULL || texmap&0x100) tmp = 0; + __gx->tevRasOrder[reg] = (__gx->tevRasOrder[reg]&~0x40000)|(_SHIFTL(tmp,18,1)); + } else { + __gx->tevRasOrder[reg] = (__gx->tevRasOrder[reg]&~0x7)|(texm&0x7); + __gx->tevRasOrder[reg] = (__gx->tevRasOrder[reg]&~0x38)|(_SHIFTL(texc,3,3)); + + colid = GX_ALPHA_BUMP; + if(color!=GX_COLORNULL) colid = _gxtevcolid[color]; + __gx->tevRasOrder[reg] = (__gx->tevRasOrder[reg]&~0x380)|(_SHIFTL(colid,7,3)); + + tmp = 1; + if(texmap==GX_TEXMAP_NULL || texmap&0x100) tmp = 0; + __gx->tevRasOrder[reg] = (__gx->tevRasOrder[reg]&~0x40)|(_SHIFTL(tmp,6,1)); + } + GX_LOAD_BP_REG(__gx->tevRasOrder[reg]); + __gx->dirtyState |= 0x0001; +} + +void GX_SetNumTevStages(u8 num) +{ + __gx->genMode = (__gx->genMode&~0x3C00)|(_SHIFTL((num-1),10,4)); + __gx->dirtyState |= 0x0004; +} + +void GX_SetAlphaCompare(u8 comp0,u8 ref0,u8 aop,u8 comp1,u8 ref1) +{ + u32 val = 0; + val = (_SHIFTL(aop,22,2))|(_SHIFTL(comp1,19,3))|(_SHIFTL(comp0,16,3))|(_SHIFTL(ref1,8,8))|(ref0&0xff); + GX_LOAD_BP_REG(0xf3000000|val); +} + +void GX_SetTevKColorSel(u8 tevstage,u8 sel) +{ + u32 reg = (_SHIFTR(tevstage,1,3)); + + if(tevstage&1) + __gx->tevSwapModeTable[reg] = (__gx->tevSwapModeTable[reg]&~0x7C000)|(_SHIFTL(sel,14,5)); + else + __gx->tevSwapModeTable[reg] = (__gx->tevSwapModeTable[reg]&~0x1F0)|(_SHIFTL(sel,4,5)); + GX_LOAD_BP_REG(__gx->tevSwapModeTable[reg]); +} + +void GX_SetTevKAlphaSel(u8 tevstage,u8 sel) +{ + u32 reg = (_SHIFTR(tevstage,1,3)); + + if(tevstage&1) + __gx->tevSwapModeTable[reg] = (__gx->tevSwapModeTable[reg]&~0xF80000)|(_SHIFTL(sel,19,5)); + else + __gx->tevSwapModeTable[reg] = (__gx->tevSwapModeTable[reg]&~0x3E00)|(_SHIFTL(sel,9,5)); + GX_LOAD_BP_REG(__gx->tevSwapModeTable[reg]); +} + +void GX_SetTevSwapMode(u8 tevstage,u8 ras_sel,u8 tex_sel) +{ + u32 reg = (tevstage&0xf); + __gx->tevAlphaEnv[reg] = (__gx->tevAlphaEnv[reg]&~0x3)|(ras_sel&0x3); + __gx->tevAlphaEnv[reg] = (__gx->tevAlphaEnv[reg]&~0xC)|(_SHIFTL(tex_sel,2,2)); + GX_LOAD_BP_REG(__gx->tevAlphaEnv[reg]); +} + +void GX_SetTevSwapModeTable(u8 swapid,u8 r,u8 g,u8 b,u8 a) +{ + u32 regA = 0+(_SHIFTL(swapid,1,3)); + u32 regB = 1+(_SHIFTL(swapid,1,3)); + + __gx->tevSwapModeTable[regA] = (__gx->tevSwapModeTable[regA]&~0x3)|(r&0x3); + __gx->tevSwapModeTable[regA] = (__gx->tevSwapModeTable[regA]&~0xC)|(_SHIFTL(g,2,2)); + GX_LOAD_BP_REG(__gx->tevSwapModeTable[regA]); + + __gx->tevSwapModeTable[regB] = (__gx->tevSwapModeTable[regB]&~0x3)|(b&0x3); + __gx->tevSwapModeTable[regB] = (__gx->tevSwapModeTable[regB]&~0xC)|(_SHIFTL(a,2,2)); + GX_LOAD_BP_REG(__gx->tevSwapModeTable[regB]); +} + +void GX_SetTevIndirect(u8 tevstage,u8 indtexid,u8 format,u8 bias,u8 mtxid,u8 wrap_s,u8 wrap_t,u8 addprev,u8 utclod,u8 a) +{ + u32 val = (0x10000000|(_SHIFTL(tevstage,24,4)))|(indtexid&3)|(_SHIFTL(format,2,2))|(_SHIFTL(bias,4,3))|(_SHIFTL(a,7,2))|(_SHIFTL(mtxid,9,4))|(_SHIFTL(wrap_s,13,3))|(_SHIFTL(wrap_t,16,3))|(_SHIFTL(utclod,19,1))|(_SHIFTL(addprev,20,1)); + GX_LOAD_BP_REG(val); +} + +void GX_SetTevDirect(u8 tevstage) +{ + GX_SetTevIndirect(tevstage,GX_INDTEXSTAGE0,GX_ITF_8,GX_ITB_NONE,GX_ITM_OFF,GX_ITW_OFF,GX_ITW_OFF,GX_FALSE,GX_FALSE,GX_ITBA_OFF); +} + +void GX_SetNumIndStages(u8 nstages) +{ + __gx->genMode = (__gx->genMode&~0x70000)|(_SHIFTL(nstages,16,3)); + __gx->dirtyState |= 0x0006; +} + +void GX_SetIndTexMatrix(u8 indtexmtx,f32 offset_mtx[2][3],s8 scale_exp) +{ + u32 ma,mb; + u32 val,s,idx; + + if(indtexmtx>0x00 && indtexmtx<0x04) indtexmtx -= 0x01; + else if(indtexmtx>0x04 && indtexmtx<0x08) indtexmtx -= 0x05; + else if(indtexmtx>0x08 && indtexmtx<0x0C) indtexmtx -= 0x09; + else indtexmtx = 0x00; + + s = (scale_exp+17); + idx = ((indtexmtx<<2)-indtexmtx); + + ma = (u32)(offset_mtx[0][0]*1024.0F); + mb = (u32)(offset_mtx[1][0]*1024.0F); + val = (_SHIFTL((0x06+idx),24,8)|_SHIFTL(s,22,2)|_SHIFTL(mb,11,11)|_SHIFTL(ma,0,11)); + GX_LOAD_BP_REG(val); + + ma = (u32)(offset_mtx[0][1]*1024.0F); + mb = (u32)(offset_mtx[1][1]*1024.0F); + val = (_SHIFTL((0x07+idx),24,8)|_SHIFTL((s>>2),22,2)|_SHIFTL(mb,11,11)|_SHIFTL(ma,0,11)); + GX_LOAD_BP_REG(val); + + ma = (u32)(offset_mtx[0][2]*1024.0F); + mb = (u32)(offset_mtx[1][2]*1024.0F); + val = (_SHIFTL((0x08+idx),24,8)|_SHIFTL((s>>4),22,2)|_SHIFTL(mb,11,11)|_SHIFTL(ma,0,11)); + GX_LOAD_BP_REG(val); +} + +void GX_SetTevIndBumpST(u8 tevstage,u8 indstage,u8 mtx_sel) +{ + u8 sel_s,sel_t; + + switch(mtx_sel) { + case GX_ITM_0: + sel_s = GX_ITM_S0; + sel_t = GX_ITM_T0; + break; + case GX_ITM_1: + sel_s = GX_ITM_S1; + sel_t = GX_ITM_T1; + break; + case GX_ITM_2: + sel_s = GX_ITM_S2; + sel_t = GX_ITM_T2; + break; + default: + sel_s = GX_ITM_OFF; + sel_t = GX_ITM_OFF; + break; + } + + GX_SetTevIndirect((tevstage+0),indstage,GX_ITF_8,GX_ITB_ST,sel_s,GX_ITW_0,GX_ITW_0,GX_FALSE,GX_FALSE,GX_ITBA_OFF); + GX_SetTevIndirect((tevstage+1),indstage,GX_ITF_8,GX_ITB_ST,sel_t,GX_ITW_0,GX_ITW_0,GX_TRUE,GX_FALSE,GX_ITBA_OFF); + GX_SetTevIndirect((tevstage+2),indstage,GX_ITF_8,GX_ITB_NONE,GX_ITM_OFF,GX_ITW_OFF,GX_ITW_OFF,GX_TRUE,GX_FALSE,GX_ITBA_OFF); +} + +void GX_SetTevIndBumpXYZ(u8 tevstage,u8 indstage,u8 mtx_sel) +{ + GX_SetTevIndirect(tevstage,indstage,GX_ITF_8,GX_ITB_STU,mtx_sel,GX_ITW_OFF,GX_ITW_OFF,GX_FALSE,GX_FALSE,GX_ITBA_OFF); +} + +void GX_SetTevIndRepeat(u8 tevstage) +{ + GX_SetTevIndirect(tevstage,GX_INDTEXSTAGE0,GX_ITF_8,GX_ITB_NONE,GX_ITM_OFF,GX_ITW_0,GX_ITW_0,GX_TRUE,GX_FALSE,GX_ITBA_OFF); +} + +void GX_SetIndTexCoordScale(u8 indtexid,u8 scale_s,u8 scale_t) +{ + switch(indtexid) { + case GX_INDTEXSTAGE0: + __gx->tevRasOrder[0] = (__gx->tevRasOrder[0]&~0x0f)|(scale_s&0x0f); + __gx->tevRasOrder[0] = (__gx->tevRasOrder[0]&~0xF0)|(_SHIFTL(scale_t,4,4)); + GX_LOAD_BP_REG(__gx->tevRasOrder[0]); + break; + case GX_INDTEXSTAGE1: + __gx->tevRasOrder[0] = (__gx->tevRasOrder[0]&~0xF00)|(_SHIFTL(scale_s,8,4)); + __gx->tevRasOrder[0] = (__gx->tevRasOrder[0]&~0xF000)|(_SHIFTL(scale_t,12,4)); + GX_LOAD_BP_REG(__gx->tevRasOrder[0]); + break; + case GX_INDTEXSTAGE2: + __gx->tevRasOrder[1] = (__gx->tevRasOrder[1]&~0x0f)|(scale_s&0x0f); + __gx->tevRasOrder[1] = (__gx->tevRasOrder[1]&~0xF0)|(_SHIFTL(scale_t,4,4)); + GX_LOAD_BP_REG(__gx->tevRasOrder[1]); + break; + case GX_INDTEXSTAGE3: + __gx->tevRasOrder[1] = (__gx->tevRasOrder[1]&~0xF00)|(_SHIFTL(scale_s,8,4)); + __gx->tevRasOrder[1] = (__gx->tevRasOrder[1]&~0xF000)|(_SHIFTL(scale_t,12,4)); + GX_LOAD_BP_REG(__gx->tevRasOrder[1]); + break; + } +} + +void GX_SetTevIndTile(u8 tevstage,u8 indtexid,u16 tilesize_x,u16 tilesize_y,u16 tilespacing_x,u16 tilespacing_y,u8 indtexfmt,u8 indtexmtx,u8 bias_sel,u8 alpha_sel) +{ + s32 wrap_s,wrap_t; + f32 offset_mtx[2][3]; + f64 fdspace_x,fdspace_y; + u32 fbuf_x[2] = { 0x43300000,tilespacing_x }; + u32 fbuf_y[2] = { 0x43300000,tilespacing_y }; + + wrap_s = GX_ITW_OFF; + if(tilesize_x==0x0010) wrap_s = GX_ITW_16; + else if(tilesize_x==0x0020) wrap_s = GX_ITW_32; + else if(tilesize_x==0x0040) wrap_s = GX_ITW_64; + else if(tilesize_x==0x0080) wrap_s = GX_ITW_128; + else if(tilesize_x==0x0100) wrap_s = GX_ITW_256; + + wrap_t = GX_ITW_OFF; + if(tilesize_y==0x0010) wrap_t = GX_ITW_16; + else if(tilesize_y==0x0020) wrap_t = GX_ITW_32; + else if(tilesize_y==0x0040) wrap_t = GX_ITW_64; + else if(tilesize_y==0x0080) wrap_t = GX_ITW_128; + else if(tilesize_y==0x0100) wrap_t = GX_ITW_256; + + fdspace_x = *(f64*)((void*)fbuf_x); + fdspace_y = *(f64*)((void*)fbuf_y); + + offset_mtx[0][0] = (f32)((fdspace_x - 4503599627370496.0F)*0.00097656250F); + offset_mtx[0][1] = 0.0F; + offset_mtx[0][2] = 0.0F; + offset_mtx[1][0] = 0.0F; + offset_mtx[1][1] = (f32)((fdspace_y - 4503599627370496.0F)*0.00097656250F); + offset_mtx[1][2] = 0.0F; + + GX_SetIndTexMatrix(indtexmtx,offset_mtx,10); + GX_SetTevIndirect(tevstage,indtexid,indtexfmt,bias_sel,indtexmtx,wrap_s,wrap_t,GX_FALSE,GX_TRUE,alpha_sel); +} + +void GX_SetFog(u8 type,f32 startz,f32 endz,f32 nearz,f32 farz,GXColor col) +{ + f32 A, B, B_mant, C, A_f; + u32 b_expn, b_m, a_hex, c_hex,val,proj = 0; + union ieee32 { f32 f; u32 i; } v; + + proj = _SHIFTR(type,3,1); + + // Calculate constants a, b, and c (TEV HW requirements). + if(proj) { // Orthographic Fog Type + if((farz==nearz) || (endz==startz)) { + // take care of the odd-ball case. + A_f = 0.0f; + C = 0.0f; + } else { + A = 1.0f/(endz-startz); + A_f = (farz-nearz) * A; + C = (startz-nearz) * A; + } + + b_expn = 0; + b_m = 0; + } else { // Perspective Fog Type + // Calculate constants a, b, and c (TEV HW requirements). + if((farz==nearz) || (endz==startz)) { + // take care of the odd-ball case. + A = 0.0f; + B = 0.5f; + C = 0.0f; + } else { + A = (farz*nearz)/((farz-nearz)*(endz-startz)); + B = farz/(farz-nearz); + C = startz/(endz-startz); + } + + B_mant = B; + b_expn = 1; + while(B_mant>1.0f) { + B_mant /= 2.0f; + b_expn++; + } + + while((B_mant>0.0f) && (B_mant<0.5f)) { + B_mant *= 2.0f; + b_expn--; + } + + A_f = A/(1<<(b_expn)); + b_m = (u32)(B_mant * 8388638.0f); + } + v.f = A_f; + a_hex = v.i; + + v.f = C; + c_hex = v.i; + + val = 0xee000000|(_SHIFTR(a_hex,12,20)); + GX_LOAD_BP_REG(val); + + val = 0xef000000|(b_m&0x00ffffff); + GX_LOAD_BP_REG(val); + + val = 0xf0000000|(b_expn&0x1f); + GX_LOAD_BP_REG(val); + + val = 0xf1000000|(_SHIFTL(type,21,3))|(_SHIFTL(proj,20,1))|(_SHIFTR(c_hex,12,20)); + GX_LOAD_BP_REG(val); + + val = 0xf2000000|(_SHIFTL(col.r,16,8))|(_SHIFTL(col.g,8,8))|(col.b&0xff); + GX_LOAD_BP_REG(val); +} + +void GX_InitFogAdjTable(GXFogAdjTbl *table,u16 width,f32 projmtx[4][4]) +{ + u32 i,val7; + f32 val0,val1,val2,val4,val5,val6; + + if(projmtx[3][3]==0.0f) { + val0 = projmtx[2][3]/(projmtx[2][2] - 1.0f); + val1 = val0/projmtx[0][0]; + } else { + val1 = 1.0f/projmtx[0][0]; + val0 = val1*1.7320499f; + } + + val2 = val0*val0; + val4 = 2.0f/(f32)width; + for(i=0;i<10;i++) { + val5 = (i+1)*32.0f; + val5 *= val4; + val5 *= val1; + val5 *= val5; + val5 /= val2; + val6 = sqrtf(val5 + 1.0f); + val7 = (u32)(val6*256.0f); + table->r[i] = (val7&0x0fff); + } +} + +void GX_SetFogRangeAdj(u8 enable,u16 center,GXFogAdjTbl *table) +{ + u32 val; + + if(enable) { + val = 0xe9000000|(_SHIFTL(table->r[1],12,12))|(table->r[0]&0x0fff); + GX_LOAD_BP_REG(val); + + val = 0xea000000|(_SHIFTL(table->r[3],12,12))|(table->r[2]&0x0fff); + GX_LOAD_BP_REG(val); + + val = 0xeb000000|(_SHIFTL(table->r[5],12,12))|(table->r[4]&0x0fff); + GX_LOAD_BP_REG(val); + + val = 0xec000000|(_SHIFTL(table->r[7],12,12))|(table->r[6]&0x0fff); + GX_LOAD_BP_REG(val); + + val = 0xed000000|(_SHIFTL(table->r[9],12,12))|(table->r[8]&0x0fff); + GX_LOAD_BP_REG(val); + } + val = 0xe8000000|(_SHIFTL(enable,10,1))|((center + 342)&0x03ff); + GX_LOAD_BP_REG(val); +} + +void GX_SetFogColor(GXColor color) +{ + GX_LOAD_BP_REG(0xf2000000|(_SHIFTL(color.r,16,8)|_SHIFTL(color.g,8,8)|(color.b&0xff))); +} + +void GX_SetColorUpdate(u8 enable) +{ + __gx->peCMode0 = (__gx->peCMode0&~0x8)|(_SHIFTL(enable,3,1)); + GX_LOAD_BP_REG(__gx->peCMode0); +} + +void GX_SetAlphaUpdate(u8 enable) +{ + __gx->peCMode0 = (__gx->peCMode0&~0x10)|(_SHIFTL(enable,4,1)); + GX_LOAD_BP_REG(__gx->peCMode0); +} + +void GX_SetZCompLoc(u8 before_tex) +{ + __gx->peCntrl = (__gx->peCntrl&~0x40)|(_SHIFTL(before_tex,6,1)); + GX_LOAD_BP_REG(__gx->peCntrl); +} + +void GX_SetPixelFmt(u8 pix_fmt,u8 z_fmt) +{ + u8 ms_en = 0; + u32 realfmt[8] = {0,1,2,3,4,4,4,5}; + + __gx->peCntrl = (__gx->peCntrl&~0x7)|(realfmt[pix_fmt]&0x7); + __gx->peCntrl = (__gx->peCntrl&~0x38)|(_SHIFTL(z_fmt,3,3)); + GX_LOAD_BP_REG(__gx->peCntrl); + __gx->dirtyState |= 0x0004; + + if(pix_fmt==GX_PF_RGB565_Z16) ms_en = 1; + __gx->genMode = (__gx->genMode&~0x200)|(_SHIFTL(ms_en,9,1)); + + if(realfmt[pix_fmt]==GX_PF_Y8) { + pix_fmt -= GX_PF_Y8; + __gx->peCMode1 = (__gx->peCMode1&~0xC00)|(_SHIFTL(pix_fmt,10,2)); + GX_LOAD_BP_REG(__gx->peCMode1); + } +} + +void GX_SetDither(u8 dither) +{ + __gx->peCMode0 = (__gx->peCMode0&~0x4)|(_SHIFTL(dither,2,1)); + GX_LOAD_BP_REG(__gx->peCMode0); +} + +void GX_SetDstAlpha(u8 enable,u8 a) +{ + __gx->peCMode1 = (__gx->peCMode1&~0xff)|(a&0xff); + __gx->peCMode1 = (__gx->peCMode1&~0x100)|(_SHIFTL(enable,8,1)); + GX_LOAD_BP_REG(__gx->peCMode1); +} + +void GX_SetFieldMask(u8 even_mask,u8 odd_mask) +{ + u32 val = 0; + + val = (_SHIFTL(even_mask,1,1))|(odd_mask&1); + GX_LOAD_BP_REG(0x44000000|val); +} + +void GX_SetFieldMode(u8 field_mode,u8 half_aspect_ratio) +{ + __gx->lpWidth = (__gx->lpWidth&~0x400000)|(_SHIFTL(half_aspect_ratio,22,1)); + GX_LOAD_BP_REG(__gx->lpWidth); + + __GX_FlushTextureState(); + GX_LOAD_BP_REG(0x68000000|(field_mode&1)); + __GX_FlushTextureState(); +} + +void GX_PokeAlphaMode(u8 func,u8 threshold) +{ + _peReg[3] = (_SHIFTL(func,8,8))|(threshold&0xFF); +} + +void GX_PokeAlphaRead(u8 mode) +{ + _peReg[4] = (mode&~0x4)|0x4; +} + +void GX_PokeDstAlpha(u8 enable,u8 a) +{ + _peReg[2] = (_SHIFTL(enable,8,1))|(a&0xff); +} + +void GX_PokeAlphaUpdate(u8 update_enable) +{ + _peReg[1] = (_peReg[1]&~0x10)|(_SHIFTL(update_enable,4,1)); +} + +void GX_PokeColorUpdate(u8 update_enable) +{ + _peReg[1] = (_peReg[1]&~0x8)|(_SHIFTL(update_enable,3,1)); +} + +void GX_PokeDither(u8 dither) +{ + _peReg[1] = (_peReg[1]&~0x4)|(_SHIFTL(dither,2,1)); +} + +void GX_PokeBlendMode(u8 type,u8 src_fact,u8 dst_fact,u8 op) +{ + u32 regval = _peReg[1]; + + regval = (regval&~0x1); + if(type==GX_BM_BLEND || type==GX_BM_SUBTRACT) regval |= 0x1; + + regval = (regval&~0x800); + if(type==GX_BM_SUBTRACT) regval |= 0x800; + + regval = (regval&~0x2); + if(type==GX_BM_LOGIC) regval |= 0x2; + + regval = (regval&~0xF000)|(_SHIFTL(op,12,4)); + regval = (regval&~0xE0)|(_SHIFTL(dst_fact,5,3)); + regval = (regval&~0x700)|(_SHIFTL(src_fact,8,3)); + + regval |= 0x41000000; + _peReg[1] = (u16)regval; +} + +void GX_PokeARGB(u16 x,u16 y,GXColor color) +{ + u32 regval; + + regval = 0xc8000000|(_SHIFTL(x,2,10)); + regval = (regval&~0x3FF000)|(_SHIFTL(y,12,10)); + *(u32*)regval = _SHIFTL(color.a,24,8)|_SHIFTL(color.r,16,8)|_SHIFTL(color.g,8,8)|(color.b&0xff); +} + +void GX_PeekARGB(u16 x,u16 y,GXColor *color) +{ + u32 regval,val; + + regval = 0xc8000000|(_SHIFTL(x,2,10)); + regval = (regval&~0x3FF000)|(_SHIFTL(y,12,10)); + val = *(u32*)regval; + color->a = _SHIFTR(val,24,8); + color->r = _SHIFTR(val,16,8); + color->g = _SHIFTR(val,8,8); + color->b = val&0xff; +} + +void GX_PokeZ(u16 x,u16 y,u32 z) +{ + u32 regval; + + regval = 0xc8000000|(_SHIFTL(x,2,10)); + regval = (regval&~0x3FF000)|(_SHIFTL(y,12,10)); + regval = (regval&~0xC00000)|0x400000; + *(u32*)regval = z; +} + +void GX_PeekZ(u16 x,u16 y,u32 *z) +{ + u32 regval; + + regval = 0xc8000000|(_SHIFTL(x,2,10)); + regval = (regval&~0x3FF000)|(_SHIFTL(y,12,10)); + regval = (regval&~0xC00000)|0x400000; + *z = *(u32*)regval; +} + +void GX_PokeZMode(u8 comp_enable,u8 func,u8 update_enable) +{ + u16 regval; + regval = comp_enable&0x1; + regval = (regval&~0xE)|(_SHIFTL(func,1,3)); + regval = (regval&0x10)|(_SHIFTL(update_enable,4,1)); + _peReg[0] = regval; +} + +void GX_SetIndTexOrder(u8 indtexstage,u8 texcoord,u8 texmap) +{ + switch(indtexstage) { + case GX_INDTEXSTAGE0: + __gx->tevRasOrder[2] = (__gx->tevRasOrder[2]&~0x7)|(texmap&0x7); + __gx->tevRasOrder[2] = (__gx->tevRasOrder[2]&~0x38)|(_SHIFTL(texcoord,3,3)); + break; + case GX_INDTEXSTAGE1: + __gx->tevRasOrder[2] = (__gx->tevRasOrder[2]&~0x1C0)|(_SHIFTL(texmap,6,3)); + __gx->tevRasOrder[2] = (__gx->tevRasOrder[2]&~0xE00)|(_SHIFTL(texcoord,9,3)); + break; + case GX_INDTEXSTAGE2: + __gx->tevRasOrder[2] = (__gx->tevRasOrder[2]&~0x7000)|(_SHIFTL(texmap,12,3)); + __gx->tevRasOrder[2] = (__gx->tevRasOrder[2]&~0x38000)|(_SHIFTL(texcoord,15,3)); + break; + case GX_INDTEXSTAGE3: + __gx->tevRasOrder[2] = (__gx->tevRasOrder[2]&~0x1C0000)|(_SHIFTL(texmap,18,3)); + __gx->tevRasOrder[2] = (__gx->tevRasOrder[2]&~0xE00000)|(_SHIFTL(texcoord,21,3)); + break; + } + GX_LOAD_BP_REG(__gx->tevRasOrder[2]); + __gx->dirtyState |= 0x0003; +} + +void GX_InitLightPos(GXLightObj *lit_obj,f32 x,f32 y,f32 z) +{ + struct __gx_litobj *lit = (struct __gx_litobj*)lit_obj; + + lit->px = x; + lit->py = y; + lit->pz = z; +} + +void GX_InitLightColor(GXLightObj *lit_obj,GXColor col) +{ + struct __gx_litobj *lit = (struct __gx_litobj*)lit_obj; + lit->col = ((_SHIFTL(col.r,24,8))|(_SHIFTL(col.g,16,8))|(_SHIFTL(col.b,8,8))|(col.a&0xff)); +} + +void GX_LoadLightObj(GXLightObj *lit_obj,u8 lit_id) +{ + u32 id; + u16 reg; + struct __gx_litobj *lit = (struct __gx_litobj*)lit_obj; + + switch(lit_id) { + case GX_LIGHT0: + id = 0; + break; + case GX_LIGHT1: + id = 1; + break; + case GX_LIGHT2: + id = 2; + break; + case GX_LIGHT3: + id = 3; + break; + case GX_LIGHT4: + id = 4; + break; + case GX_LIGHT5: + id = 5; + break; + case GX_LIGHT6: + id = 6; + break; + case GX_LIGHT7: + id = 7; + break; + default: + id = 0; + break; + } + + reg = 0x600|(_SHIFTL(id,4,8)); + GX_LOAD_XF_REGS(reg,16); + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = 0; + wgPipe->U32 = lit->col; + wgPipe->F32 = lit->a0; + wgPipe->F32 = lit->a1; + wgPipe->F32 = lit->a2; + wgPipe->F32 = lit->k0; + wgPipe->F32 = lit->k1; + wgPipe->F32 = lit->k2; + wgPipe->F32 = lit->px; + wgPipe->F32 = lit->py; + wgPipe->F32 = lit->pz; + wgPipe->F32 = lit->nx; + wgPipe->F32 = lit->ny; + wgPipe->F32 = lit->nz; +} + +void GX_LoadLightObjIdx(u32 litobjidx,u8 litid) +{ + u32 reg; + u32 idx = 0; + + switch(litid) { + case GX_LIGHT0: + idx = 0; + break; + case GX_LIGHT1: + idx = 1; + break; + case GX_LIGHT2: + idx = 2; + break; + case GX_LIGHT3: + idx = 3; + break; + case GX_LIGHT4: + idx = 4; + break; + case GX_LIGHT5: + idx = 5; + break; + case GX_LIGHT6: + idx = 6; + break; + case GX_LIGHT7: + idx = 7; + break; + default: + idx = 0; + break; + + } + + reg = 0xf600|(_SHIFTL(idx,4,8)); + reg = (reg&~0xffff0000)|(_SHIFTL(litobjidx,16,16)); + + wgPipe->U8 = 0x38; + wgPipe->U32 = reg; +} + +void GX_InitLightDir(GXLightObj *lit_obj,f32 nx,f32 ny,f32 nz) +{ + struct __gx_litobj *lit = (struct __gx_litobj*)lit_obj; + + lit->nx = -(nx); + lit->ny = -(ny); + lit->nz = -(nz); +} + +void GX_InitLightDistAttn(GXLightObj *lit_obj,f32 ref_dist,f32 ref_brite,u8 dist_fn) +{ + f32 k0,k1,k2; + struct __gx_litobj *lit = (struct __gx_litobj*)lit_obj; + + if(ref_dist<0.0f || + ref_brite<0.0f || ref_brite>=1.0f) dist_fn = GX_DA_OFF; + + switch(dist_fn) { + case GX_DA_GENTLE: + k0 = 1.0f; + k1 = (1.0f-ref_brite)/(ref_brite*ref_dist); + k2 = 0.0f; + break; + case GX_DA_MEDIUM: + k0 = 1.0f; + k1 = 0.5f*(1.0f-ref_brite)/(ref_brite*ref_dist); + k2 = 0.5f*(1.0f-ref_brite)/(ref_brite*ref_dist*ref_dist); + break; + case GX_DA_STEEP: + k0 = 1.0f; + k1 = 0.0f; + k2 = (1.0f-ref_brite)/(ref_brite*ref_dist*ref_dist); + break; + case GX_DA_OFF: + default: + k0 = 1.0f; + k1 = 0.0f; + k2 = 0.0f; + break; + } + + lit->k0 = k0; + lit->k1 = k1; + lit->k2 = k2; +} + +void GX_InitLightAttn(GXLightObj *lit_obj,f32 a0,f32 a1,f32 a2,f32 k0,f32 k1,f32 k2) +{ + struct __gx_litobj *lit = (struct __gx_litobj*)lit_obj; + + lit->a0 = a0; + lit->a1 = a1; + lit->a2 = a2; + lit->k0 = k0; + lit->k1 = k1; + lit->k2 = k2; +} + +void GX_InitLightAttnA(GXLightObj *lit_obj,f32 a0,f32 a1,f32 a2) +{ + struct __gx_litobj *lit = (struct __gx_litobj*)lit_obj; + + lit->a0 = a0; + lit->a1 = a1; + lit->a2 = a2; +} + +void GX_InitLightAttnK(GXLightObj *lit_obj,f32 k0,f32 k1,f32 k2) +{ + struct __gx_litobj *lit = (struct __gx_litobj*)lit_obj; + + lit->k0 = k0; + lit->k1 = k1; + lit->k2 = k2; +} + +void GX_InitSpecularDirHA(GXLightObj *lit_obj,f32 nx,f32 ny,f32 nz,f32 hx,f32 hy,f32 hz) +{ + f32 px, py, pz; + struct __gx_litobj *lit = (struct __gx_litobj*)lit_obj; + + px = (nx * LARGE_NUMBER); + py = (ny * LARGE_NUMBER); + pz = (nz * LARGE_NUMBER); + + lit->px = px; + lit->py = py; + lit->pz = pz; + lit->nx = hx; + lit->ny = hy; + lit->nz = hz; +} + +void GX_InitSpecularDir(GXLightObj *lit_obj,f32 nx,f32 ny,f32 nz) +{ + f32 px, py, pz; + f32 hx, hy, hz, mag; + struct __gx_litobj *lit = (struct __gx_litobj*)lit_obj; + + // Compute half-angle vector + hx = -nx; + hy = -ny; + hz = (-nz + 1.0f); + mag = ((hx * hx) + (hy * hy) + (hz * hz)); + if(mag!=0.0f) mag = 1.0f / sqrtf(mag); + + hx *= mag; + hy *= mag; + hz *= mag; + + px = (nx * LARGE_NUMBER); + py = (ny * LARGE_NUMBER); + pz = (nz * LARGE_NUMBER); + + lit->px = px; + lit->py = py; + lit->pz = pz; + lit->nx = hx; + lit->ny = hy; + lit->nz = hz; +} + +void GX_InitLightSpot(GXLightObj *lit_obj,f32 cut_off,u8 spotfn) +{ + f32 r,d,cr,a0,a1,a2; + struct __gx_litobj *lit = (struct __gx_litobj*)lit_obj; + + if(cut_off<0.0f || cut_off>90.0f) spotfn = GX_SP_OFF; + + r = (cut_off*M_PI)/180.0f; + cr = cosf(r); + + switch(spotfn) { + case GX_SP_FLAT: + a0 = -1000.0f*cr; + a1 = 1000.0f; + a2 = 0.0f; + break; + case GX_SP_COS: + a0 = -cr/(1.0f-cr); + a1 = 1.0f/(1.0f-cr); + a2 = 0.0f; + break; + case GX_SP_COS2: + a0 = 0.0f; + a1 = -cr/(1.0f-cr); + a2 = 1.0f/(1.0f-cr); + break; + case GX_SP_SHARP: + d = (1.0f-cr)*(1.0f-cr); + a0 = cr*(cr-2.0f); + a1 = 2.0f/d; + a2 = -1.0/d; + break; + case GX_SP_RING1: + d = (1.0f-cr)*(1.0f-cr); + a0 = -4.0f*cr/d; + a1 = 4.0f*(1.0f+cr)/d; + a2 = -4.0f/d; + break; + case GX_SP_RING2: + d = (1.0f-cr)*(1.0f-cr); + a0 = 1.0f-2.0f*cr*cr/d; + a1 = 4.0f*cr/d; + a2 = -2.0f/d; + break; + case GX_SP_OFF: + default: + a0 = 1.0f; + a1 = 0.0f; + a2 = 0.0f; + break; + } + + lit->a0 = a0; + lit->a1 = a1; + lit->a2 = a2; +} + +void GX_SetGPMetric(u32 perf0,u32 perf1) +{ + // check last setted perf0 counters + if(__gx->perf0Mode>=GX_PERF0_TRIANGLES && __gx->perf0Modeperf0Mode>=GX_PERF0_QUAD_0CVG && __gx->perf0Modeperf0Mode>=GX_PERF0_VERTICES && __gx->perf0Mode<=GX_PERF0_CLOCKS) + GX_LOAD_XF_REG(0x1006,0); + + // check last setted perf1 counters + if(__gx->perf1Mode>=GX_PERF1_VC_ELEMQ_FULL && __gx->perf1ModecpPerfMode = (__gx->cpPerfMode&~0xf0); + GX_LOAD_CP_REG(0x20,__gx->cpPerfMode); + } else if(__gx->perf1Mode>=GX_PERF1_FIFO_REQ && __gx->perf1Modeperf1Mode>=GX_PERF1_TEXELS && __gx->perf1Mode<=GX_PERF1_CLOCKS) { + GX_LOAD_BP_REG(0x67000000); + } + + __gx->perf0Mode = perf0; + switch(__gx->perf0Mode) { + case GX_PERF0_CLOCKS: + GX_LOAD_XF_REG(0x1006,0x00000273); + break; + case GX_PERF0_VERTICES: + GX_LOAD_XF_REG(0x1006,0x0000014a); + break; + case GX_PERF0_CLIP_VTX: + GX_LOAD_XF_REG(0x1006,0x0000016b); + break; + case GX_PERF0_CLIP_CLKS: + GX_LOAD_XF_REG(0x1006,0x00000084); + break; + case GX_PERF0_XF_WAIT_IN: + GX_LOAD_XF_REG(0x1006,0x000000c6); + break; + case GX_PERF0_XF_WAIT_OUT: + GX_LOAD_XF_REG(0x1006,0x00000210); + break; + case GX_PERF0_XF_XFRM_CLKS: + GX_LOAD_XF_REG(0x1006,0x00000252); + break; + case GX_PERF0_XF_LIT_CLKS: + GX_LOAD_XF_REG(0x1006,0x00000231); + break; + case GX_PERF0_XF_BOT_CLKS: + GX_LOAD_XF_REG(0x1006,0x000001ad); + break; + case GX_PERF0_XF_REGLD_CLKS: + GX_LOAD_XF_REG(0x1006,0x000001ce); + break; + case GX_PERF0_XF_REGRD_CLKS: + GX_LOAD_XF_REG(0x1006,0x00000021); + break; + case GX_PERF0_CLIP_RATIO: + GX_LOAD_XF_REG(0x1006,0x00000153); + break; + case GX_PERF0_TRIANGLES: + GX_LOAD_BP_REG(0x2300AE7F); + break; + case GX_PERF0_TRIANGLES_CULLED: + GX_LOAD_BP_REG(0x23008E7F); + break; + case GX_PERF0_TRIANGLES_PASSED: + GX_LOAD_BP_REG(0x23009E7F); + break; + case GX_PERF0_TRIANGLES_SCISSORED: + GX_LOAD_BP_REG(0x23001E7F); + break; + case GX_PERF0_TRIANGLES_0TEX: + GX_LOAD_BP_REG(0x2300AC3F); + break; + case GX_PERF0_TRIANGLES_1TEX: + GX_LOAD_BP_REG(0x2300AC7F); + break; + case GX_PERF0_TRIANGLES_2TEX: + GX_LOAD_BP_REG(0x2300ACBF); + break; + case GX_PERF0_TRIANGLES_3TEX: + GX_LOAD_BP_REG(0x2300ACFF); + break; + case GX_PERF0_TRIANGLES_4TEX: + GX_LOAD_BP_REG(0x2300AD3F); + break; + case GX_PERF0_TRIANGLES_5TEX: + GX_LOAD_BP_REG(0x2300AD7F); + break; + case GX_PERF0_TRIANGLES_6TEX: + GX_LOAD_BP_REG(0x2300ADBF); + break; + case GX_PERF0_TRIANGLES_7TEX: + GX_LOAD_BP_REG(0x2300ADFF); + break; + case GX_PERF0_TRIANGLES_8TEX: + GX_LOAD_BP_REG(0x2300AE3F); + break; + case GX_PERF0_TRIANGLES_0CLR: + GX_LOAD_BP_REG(0x2300A27F); + break; + case GX_PERF0_TRIANGLES_1CLR: + GX_LOAD_BP_REG(0x2300A67F); + break; + case GX_PERF0_TRIANGLES_2CLR: + GX_LOAD_BP_REG(0x2300AA7F); + break; + case GX_PERF0_QUAD_0CVG: + GX_LOAD_BP_REG(0x2402C0C6); + break; + case GX_PERF0_QUAD_NON0CVG: + GX_LOAD_BP_REG(0x2402C16B); + break; + case GX_PERF0_QUAD_1CVG: + GX_LOAD_BP_REG(0x2402C0E7); + break; + case GX_PERF0_QUAD_2CVG: + GX_LOAD_BP_REG(0x2402C108); + break; + case GX_PERF0_QUAD_3CVG: + GX_LOAD_BP_REG(0x2402C129); + break; + case GX_PERF0_QUAD_4CVG: + GX_LOAD_BP_REG(0x2402C14A); + break; + case GX_PERF0_AVG_QUAD_CNT: + GX_LOAD_BP_REG(0x2402C1AD); + break; + case GX_PERF0_NONE: + break; + } + + __gx->perf1Mode = perf1; + switch(__gx->perf1Mode) { + case GX_PERF1_CLOCKS: + GX_LOAD_BP_REG(0x67000042); + break; + case GX_PERF1_TEXELS: + GX_LOAD_BP_REG(0x67000084); + break; + case GX_PERF1_TX_IDLE: + GX_LOAD_BP_REG(0x67000063); + break; + case GX_PERF1_TX_REGS: + GX_LOAD_BP_REG(0x67000129); + break; + case GX_PERF1_TX_MEMSTALL: + GX_LOAD_BP_REG(0x67000252); + break; + case GX_PERF1_TC_CHECK1_2: + GX_LOAD_BP_REG(0x67000021); + break; + case GX_PERF1_TC_CHECK3_4: + GX_LOAD_BP_REG(0x6700014b); + break; + case GX_PERF1_TC_CHECK5_6: + GX_LOAD_BP_REG(0x6700018d); + break; + case GX_PERF1_TC_CHECK7_8: + GX_LOAD_BP_REG(0x670001cf); + break; + case GX_PERF1_TC_MISS: + GX_LOAD_BP_REG(0x67000211); + break; + case GX_PERF1_VC_ELEMQ_FULL: + __gx->cpPerfMode = (__gx->cpPerfMode&~0xf0)|0x20; + GX_LOAD_CP_REG(0x20,__gx->cpPerfMode); + break; + case GX_PERF1_VC_MISSQ_FULL: + __gx->cpPerfMode = (__gx->cpPerfMode&~0xf0)|0x30; + GX_LOAD_CP_REG(0x20,__gx->cpPerfMode); + break; + case GX_PERF1_VC_MEMREQ_FULL: + __gx->cpPerfMode = (__gx->cpPerfMode&~0xf0)|0x40; + GX_LOAD_CP_REG(0x20,__gx->cpPerfMode); + break; + case GX_PERF1_VC_STATUS7: + __gx->cpPerfMode = (__gx->cpPerfMode&~0xf0)|0x50; + GX_LOAD_CP_REG(0x20,__gx->cpPerfMode); + break; + case GX_PERF1_VC_MISSREP_FULL: + __gx->cpPerfMode = (__gx->cpPerfMode&~0xf0)|0x60; + GX_LOAD_CP_REG(0x20,__gx->cpPerfMode); + break; + case GX_PERF1_VC_STREAMBUF_LOW: + __gx->cpPerfMode = (__gx->cpPerfMode&~0xf0)|0x70; + GX_LOAD_CP_REG(0x20,__gx->cpPerfMode); + break; + case GX_PERF1_VC_ALL_STALLS: + __gx->cpPerfMode = (__gx->cpPerfMode&~0xf0)|0x90; + GX_LOAD_CP_REG(0x20,__gx->cpPerfMode); + break; + case GX_PERF1_VERTICES: + __gx->cpPerfMode = (__gx->cpPerfMode&~0xf0)|0x80; + GX_LOAD_CP_REG(0x20,__gx->cpPerfMode); + break; + case GX_PERF1_FIFO_REQ: + _cpReg[3] = 2; + break; + case GX_PERF1_CALL_REQ: + _cpReg[3] = 3; + break; + case GX_PERF1_VC_MISS_REQ: + _cpReg[3] = 4; + break; + case GX_PERF1_CP_ALL_REQ: + _cpReg[3] = 5; + break; + case GX_PERF1_NONE: + break; + } + +} + +void GX_ClearGPMetric() +{ + _cpReg[2] = 4; +} + +void GX_InitXfRasMetric() +{ + GX_LOAD_BP_REG(0x2402C022); + GX_LOAD_XF_REG(0x1006,0x31000); +} + +void GX_ReadXfRasMetric(u32 *xfwaitin,u32 *xfwaitout,u32 *rasbusy,u32 *clks) +{ + *rasbusy = _SHIFTL(_cpReg[33],16,16)|(_cpReg[32]&0xffff); + *clks = _SHIFTL(_cpReg[35],16,16)|(_cpReg[34]&0xffff); + *xfwaitin = _SHIFTL(_cpReg[37],16,16)|(_cpReg[36]&0xffff); + *xfwaitout = _SHIFTL(_cpReg[39],16,16)|(_cpReg[38]&0xffff); +} + +u32 GX_ReadClksPerVtx() +{ + GX_DrawDone(); + _cpReg[49] = 0x1007; + _cpReg[48] = 0x1007; + return (_cpReg[50]<<8); +} + +void GX_ClearVCacheMetric() +{ + GX_LOAD_CP_REG(0,0); +} + +void GX_ReadVCacheMetric(u32 *check,u32 *miss,u32 *stall) +{ + *check = _SHIFTL(_cpReg[41],16,16)|(_cpReg[40]&0xffff); + *miss = _SHIFTL(_cpReg[43],16,16)|(_cpReg[42]&0xffff); + *stall = _SHIFTL(_cpReg[45],16,16)|(_cpReg[44]&0xffff); +} + +void GX_SetVCacheMetric(u32 attr) +{ +} + +void GX_GetGPStatus(u8 *overhi,u8 *underlow,u8 *readIdle,u8 *cmdIdle,u8 *brkpt) +{ + _gxgpstatus = _cpReg[0]; + *overhi = !!(_gxgpstatus&1); + *underlow = !!(_gxgpstatus&2); + *readIdle = !!(_gxgpstatus&4); + *cmdIdle = !!(_gxgpstatus&8); + *brkpt = !!(_gxgpstatus&16); +} + +void GX_ReadGPMetric(u32 *cnt0,u32 *cnt1) +{ + u32 tmp,reg1,reg2; + + reg1 = (_SHIFTL(_cpReg[33],16,16))|(_cpReg[32]&0xffff); + reg2 = (_SHIFTL(_cpReg[35],16,16))|(_cpReg[34]&0xffff); + //reg3 = (_SHIFTL(_cpReg[37],16,16))|(_cpReg[36]&0xffff); + //reg4 = (_SHIFTL(_cpReg[39],16,16))|(_cpReg[38]&0xffff); + + *cnt0 = 0; + if(__gx->perf0Mode==GX_PERF0_CLIP_RATIO) { + tmp = reg2*1000; + *cnt0 = tmp/reg1; + } else if(__gx->perf0Mode>=GX_PERF0_VERTICES && __gx->perf0ModefbWidth = rmin->fbWidth-(hor<<1); + rmout->efbHeight = rmin->efbHeight-((rmin->efbHeight*(ver<<1))/rmin->xfbHeight); + if(rmin->xfbMode==VI_XFBMODE_SF && !(rmin->viTVMode&VI_PROGRESSIVE)) rmout->xfbHeight = rmin->xfbHeight-ver; + else rmout->xfbHeight = rmin->xfbHeight-(ver<<1); + + rmout->viWidth = rmin->viWidth-(hor<<1); + if(rmin->viTVMode&VI_PROGRESSIVE) rmout->viHeight = rmin->viHeight-(ver<<2); + else rmout->viHeight = rmin->viHeight-(ver<<1); + + rmout->viXOrigin += hor; + rmout->viYOrigin += ver; +} + +f32 GX_GetYScaleFactor(u16 efbHeight,u16 xfbHeight) +{ + u32 yScale,xfblines,cnt; + f32 yscale; + + yscale = (f32)efbHeight/(f32)xfbHeight; + yScale = (u32)((f32)256.0/yscale)&0x1ff; + + cnt = xfbHeight; + xfblines = __GX_GetNumXfbLines(efbHeight,yScale); + while(xfblines>=xfbHeight) { + yscale = (f32)(cnt--)/(f32)efbHeight; + yScale = (u32)((f32)256.0/yscale)&0x1ff; + xfblines = __GX_GetNumXfbLines(efbHeight,yScale); + } + + while(xfblines + +#define STRUCT_REGDEF_SIZE 1440 + +struct __gx_regdef +{ + u16 cpSRreg; + u16 cpCRreg; + u16 cpCLreg; + u16 xfFlush; + u16 xfFlushExp; + u16 xfFlushSafe; + u32 gxFifoInited; + u32 vcdClear; + u32 VATTable; + u32 mtxIdxLo; + u32 mtxIdxHi; + u32 texCoordManually; + u32 vcdLo; + u32 vcdHi; + u32 vcdNrms; + u32 dirtyState; + u32 perf0Mode; + u32 perf1Mode; + u32 cpPerfMode; + u32 VAT0reg[8]; + u32 VAT1reg[8]; + u32 VAT2reg[8]; + u32 texMapSize[8]; + u32 texMapWrap[8]; + u32 sciTLcorner; + u32 sciBRcorner; + u32 lpWidth; + u32 genMode; + u32 suSsize[8]; + u32 suTsize[8]; + u32 tevTexMap[16]; + u32 tevColorEnv[16]; + u32 tevAlphaEnv[16]; + u32 tevSwapModeTable[8]; + u32 tevRasOrder[11]; + u32 tevTexCoordEnable; + u32 tevIndMask; + u32 texCoordGen[8]; + u32 texCoordGen2[8]; + u32 dispCopyCntrl; + u32 dispCopyDst; + u32 dispCopyTL; + u32 dispCopyWH; + u32 texCopyCntrl; + u32 texCopyDst; + u32 texCopyTL; + u32 texCopyWH; + u32 peZMode; + u32 peCMode0; + u32 peCMode1; + u32 peCntrl; + u32 chnAmbColor[2]; + u32 chnMatColor[2]; + u32 chnCntrl[4]; + GXTexRegion texRegion[24]; + GXTlutRegion tlutRegion[20]; + u8 saveDLctx; + u8 gxFifoUnlinked; + u8 texCopyZTex; + u8 _pad; +} __attribute__((packed)); + +struct __gxfifo { + vu32 buf_start; + vu32 buf_end; + vu32 size; + vu32 hi_mark; + vu32 lo_mark; + vu32 rd_ptr; + vu32 wt_ptr; + vu32 rdwt_dst; + vu8 fifo_wrap; + vu8 cpufifo_ready; + vu8 gpfifo_ready; + u8 _pad[93]; +} __attribute__((packed)); + +struct __gx_litobj +{ + u32 _pad[3]; + u32 col; + f32 a0; + f32 a1; + f32 a2; + f32 k0; + f32 k1; + f32 k2; + f32 px; + f32 py; + f32 pz; + f32 nx; + f32 ny; + f32 nz; +} __attribute__((packed)); + +struct __gx_texobj +{ + u32 tex_filt; + u32 tex_lod; + u32 tex_size; + u32 tex_maddr; + u32 usr_data; + u32 tex_fmt; + u32 tex_tlut; + u16 tex_tile_cnt; + u8 tex_tile_type; + u8 tex_flag; +} __attribute__((packed)); + +struct __gx_tlutobj +{ + u32 tlut_fmt; + u32 tlut_maddr; + u16 tlut_nentries; + u8 _pad[2]; +} __attribute__((packed)); + +struct __gx_texregion +{ + u32 tmem_even; + u32 tmem_odd; + u16 size_even; + u16 size_odd; + u8 ismipmap; + u8 iscached; + u8 _pad[2]; +} __attribute__((packed)); + +struct __gx_tlutregion +{ + u32 tmem_addr_conf; + u32 tmem_addr_base; + u32 tlut_maddr; + u16 tlut_nentries; + u8 _pad[2]; +} __attribute__((packed)); + +#endif diff --git a/wii/libogc/libogc/ios.c b/wii/libogc/libogc/ios.c new file mode 100644 index 0000000000..05f043651d --- /dev/null +++ b/wii/libogc/libogc/ios.c @@ -0,0 +1,379 @@ +/*------------------------------------------------------------- + +ios.c -- IOS control + +Copyright (C) 2008 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) +Hector Martin (marcan) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#if defined(HW_RVL) + +#include +#include +#include "asm.h" +#include "processor.h" +#include "cache.h" +#include "ipc.h" +#include "stm.h" +#include "es.h" +#include "ios.h" +#include "irq.h" + +#define IOS_HEAP_SIZE 0x1000 +#define MAX_IPC_RETRIES 400 + +//#define DEBUG_IOS + +#define IOS_MAX_VERSION 61 +#define IOS_MIN_VERSION 28 + +static s32 __ios_hid = -1; +extern void udelay(int us); + +s32 __IOS_InitHeap(void) +{ + if(__ios_hid <0 ) { + __ios_hid = iosCreateHeap(IOS_HEAP_SIZE); + if(__ios_hid < 0) return __ios_hid; + } + return 0; +} + +// These two functions deal with the "internal" IOS subsystems that are used by default by libogc +// Other stuff should be inited by the user and deinited by the exit callbacks. The user is also responsible +// for deiniting other stuff before an IOS reload and reiniting them after. +s32 __IOS_InitializeSubsystems(void) +{ + s32 res; + s32 ret = 0; +#ifdef DEBUG_IOS + printf("IOS Subsystem Init\n"); +#endif + res = __ES_Init(); + if(res < 0) { + ret = res; +#ifdef DEBUG_IOS + printf("ES Init failed: %d\n",ret); +#endif + } + res = __STM_Init(); + if(res < 0) { + ret = res; +#ifdef DEBUG_IOS + printf("STM Init failed: %d\n",ret); +#endif + } +#ifdef DEBUG_IOS + printf("IOS Subsystem Init Done: %d\n",ret); +#endif + return ret; +} + +s32 __IOS_ShutdownSubsystems(void) +{ + s32 res; + s32 ret = 0; +#ifdef DEBUG_IOS + printf("IOS Subsystem Close\n"); +#endif + res = __STM_Close(); + if(res < 0) ret = res; + res = __ES_Close(); + if(res < 0) ret = res; +#ifdef DEBUG_IOS + printf("IOS Subsystem Close Done: %d\n",ret); +#endif + return ret; +} + +s32 IOS_GetPreferredVersion() +{ + int ver = IOS_EBADVERSION; + s32 res; + u32 count; + u64 *titles; + u32 tmd_size; + u32 i; + u32 a,b; + + res = __IOS_InitHeap(); + if(res<0) return res; + + res = ES_GetNumTitles(&count); + if(res < 0) { +#ifdef DEBUG_IOS + printf(" GetNumTitles failed: %d\n",res); +#endif + return res; + } +#ifdef DEBUG_IOS + printf(" %d titles on card:\n",count); +#endif + titles = iosAlloc(__ios_hid, sizeof(u64)*count); + if(!titles) { + printf(" iosAlloc titles failed\n"); + return -1; + } + res = ES_GetTitles(titles, count); + if(res < 0) { +#ifdef DEBUG_IOS + printf(" GetTitles failed: %d\n",res); +#endif + iosFree(__ios_hid, titles); + return res; + } + + u32 *tmdbuffer = memalign(32, MAX_SIGNED_TMD_SIZE); + + if(!tmdbuffer) + { + iosFree(__ios_hid, titles); + return -1; + } + + for(i=0; i>32; + b = titles[i]&0xFFFFFFFF; + if(a != 1) continue; + if(b < IOS_MIN_VERSION) continue; + if(b > IOS_MAX_VERSION) continue; + + if (ES_GetStoredTMDSize(titles[i], &tmd_size) < 0) + continue; + + if (tmd_size < 0 || tmd_size > 4096) + continue; + + if(ES_GetStoredTMD(titles[i], (signed_blob *)tmdbuffer, tmd_size) < 0) + continue; + + if (!tmdbuffer[1] && !tmdbuffer[2]) + continue; + + if((((s32)b) > ((s32)ver) && ver != 58) || b == 58) ver = b; + } +#ifdef DEBUG_IOS + printf(" Preferred verson: %d\n",ver); +#endif + iosFree(__ios_hid, titles); + free(tmdbuffer); + return ver; +} + +s32 IOS_GetVersion() +{ + u32 vercode; + u16 version; + DCInvalidateRange((void*)0x80003140,8); + vercode = *((u32*)0x80003140); + version = vercode >> 16; + if(version == 0) return IOS_EBADVERSION; + if(version > 0xff) return IOS_EBADVERSION; + return version; +} + +s32 IOS_GetRevision() +{ + u32 vercode; + u16 rev; + DCInvalidateRange((void*)0x80003140,8); + vercode = *((u32*)0x80003140); + rev = vercode & 0xFFFF; + if(vercode == 0 || rev == 0) return IOS_EBADVERSION; + return rev; +} + +s32 IOS_GetRevisionMajor() +{ + s32 rev; + rev = IOS_GetRevision(); + if(rev < 0) return rev; + return (rev>>8)&0xFF; +} + +s32 IOS_GetRevisionMinor() +{ + s32 rev; + rev = IOS_GetRevision(); + if(rev < 0) return rev; + return rev&0xFF; +} + +s32 __IOS_LaunchNewIOS(int version) +{ + u32 numviews; + s32 res; + u64 titleID = 0x100000000LL; + raw_irq_handler_t irq_handler; + u32 counter; + + STACK_ALIGN(tikview,views,4,32); +#ifdef DEBUG_IOS + s32 oldversion; +#endif + s32 newversion; + + if(version < 3 || version > 0xFF) { + return IOS_EBADVERSION; + } + +#ifdef DEBUG_IOS + oldversion = IOS_GetVersion(); + if(oldversion>0) printf("Current IOS Version: IOS%d\n",oldversion); +#endif + + titleID |= version; +#ifdef DEBUG_IOS + printf("Launching IOS TitleID: %016llx\n",titleID); +#endif + + res = ES_GetNumTicketViews(titleID, &numviews); + if(res < 0) { +#ifdef DEBUG_IOS + printf(" GetNumTicketViews failed: %d\n",res); +#endif + return res; + } + if(numviews > 4) { + printf(" GetNumTicketViews too many views: %lu\n",numviews); + return IOS_ETOOMANYVIEWS; + } + res = ES_GetTicketViews(titleID, views, numviews); + if(res < 0) { +#ifdef DEBUG_IOS + printf(" GetTicketViews failed: %d\n",res); +#endif + return res; + } + + write32(0x80003140, 0); + + res = ES_LaunchTitleBackground(titleID, &views[0]); + if(res < 0) { +#ifdef DEBUG_IOS + printf(" LaunchTitleBackground failed: %d\n",res); +#endif + return res; + } + + __ES_Reset(); + + // Mask IPC IRQ while we're busy reloading + __MaskIrq(IRQ_PI_ACR); + irq_handler = IRQ_Free(IRQ_PI_ACR); + +#ifdef DEBUG_IOS + printf("Waiting for IOS ...\n"); +#endif + while ((read32(0x80003140) >> 16) == 0) + udelay(1000); + +#ifdef DEBUG_IOS + u32 v = read32(0x80003140); + printf("IOS loaded: IOS%d v%d.%d\n", v >> 16, (v >> 8) & 0xff, v & 0xff); +#endif + +#ifdef DEBUG_IOS + printf("Waiting for IPC ...\n"); +#endif + for (counter = 0; !(read32(0x0d000004) & 2); counter++) { + udelay(1000); + + if (counter >= MAX_IPC_RETRIES) + break; + } + +#ifdef DEBUG_IOS + printf("IPC started (%u)\n", counter); +#endif + + IRQ_Request(IRQ_PI_ACR, irq_handler, NULL); + __UnmaskIrq(IRQ_PI_ACR); + + __IPC_Reinitialize(); + + newversion = IOS_GetVersion(); + + if(newversion != version) { +#ifdef DEBUG_IOS + printf(" Version mismatch!\n"); +#endif + return IOS_EMISMATCH; + } + + return version; +} + +s32 __attribute__((weak)) __IOS_LoadStartupIOS() +{ + return 0; +} + +s32 IOS_ReloadIOS(int version) +{ + int ret = 0; + int res; + +#ifdef DEBUG_IOS + printf("Reloading to IOS%d\n",version); +#endif + + res = __IOS_ShutdownSubsystems(); + if(res < 0) { +#ifdef DEBUG_IOS + printf("__IOS_ShutdownSubsystems failed: %d\n", res); +#endif + ret = res; + } + + res = __ES_Init(); + if(res < 0) { +#ifdef DEBUG_IOS + printf("__ES_Init failed: %d\n", res); +#endif + ret = res; + } else { + res = __IOS_LaunchNewIOS(version); + if(res < 0) { +#ifdef DEBUG_IOS + printf("__IOS_LaunchNewIOS failed: %d\n", res); +#endif + ret = res; + __ES_Close(); + } + } + + res = __IOS_InitializeSubsystems(); + if(res < 0) { +#ifdef DEBUG_IOS + printf("__IOS_InitializeSubsystems failed: %d\n", res); +#endif + ret = res; + } + + return ret; +} + +#endif /* defined(HW_RVL) */ diff --git a/wii/libogc/libogc/ipc.c b/wii/libogc/libogc/ipc.c new file mode 100644 index 0000000000..10fed37bce --- /dev/null +++ b/wii/libogc/libogc/ipc.c @@ -0,0 +1,1286 @@ +/*------------------------------------------------------------- + +ipc.c -- Interprocess Communication with Starlet + +Copyright (C) 2008 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) +Hector Martin (marcan) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#if defined(HW_RVL) + +#include +#include +#include +#include +#include +#include +#include +#include +#include "asm.h" +#include "processor.h" +#include "lwp.h" +#include "irq.h" +#include "ipc.h" +#include "cache.h" +#include "system.h" +#include "lwp_heap.h" +#include "lwp_wkspace.h" + +#define IPC_HEAP_SIZE 4096 +#define IPC_REQUESTSIZE 64 +#define IPC_NUMHEAPS 16 + +#define IOS_MAXFMT_PARAMS 32 + +#define IOS_OPEN 0x01 +#define IOS_CLOSE 0x02 +#define IOS_READ 0x03 +#define IOS_WRITE 0x04 +#define IOS_SEEK 0x05 +#define IOS_IOCTL 0x06 +#define IOS_IOCTLV 0x07 + +#define RELNCH_RELAUNCH 1 +#define RELNCH_BACKGROUND 2 + +struct _ipcreq +{ //ipc struct size: 32 + u32 cmd; //0 + s32 result; //4 + union { //8 + s32 fd; + u32 req_cmd; + }; + union { + struct { + char *filepath; + u32 mode; + } open; + struct { + void *data; + u32 len; + } read, write; + struct { + s32 where; + s32 whence; + } seek; + struct { + u32 ioctl; + void *buffer_in; + u32 len_in; + void *buffer_io; + u32 len_io; + } ioctl; + struct { + u32 ioctl; + u32 argcin; + u32 argcio; + struct _ioctlv *argv; + } ioctlv; + u32 args[5]; + }; + + ipccallback cb; //32 + void *usrdata; //36 + u32 relnch; //40 + lwpq_t syncqueue; //44 + u32 magic; //48 - used to avoid spurious responses, like from zelda. + u8 pad1[12]; //52 - 60 +} ATTRIBUTE_PACKED; + +struct _ipcreqres +{ + u32 cnt_sent; + u32 cnt_queue; + u32 req_send_no; + u32 req_queue_no; + struct _ipcreq *reqs[16]; +}; + +struct _ipcheap +{ + void *membase; + u32 size; + heap_cntrl heap; +}; + +struct _ioctlvfmt_bufent +{ + void *ipc_buf; + void *io_buf; + s32 copy_len; +}; + +struct _ioctlvfmt_cbdata +{ + ipccallback user_cb; + void *user_data; + s32 num_bufs; + u32 hId; + struct _ioctlvfmt_bufent *bufs; +}; + +static u32 IPC_REQ_MAGIC; + +static s32 _ipc_hid = -1; +static s32 _ipc_mailboxack = 1; +static u32 _ipc_relnchFl = 0; +static u32 _ipc_initialized = 0; +static u32 _ipc_clntinitialized = 0; +static u64 _ipc_spuriousresponsecnt = 0; +static struct _ipcreq *_ipc_relnchRpc = NULL; + +static void *_ipc_bufferlo = NULL; +static void *_ipc_bufferhi = NULL; +static void *_ipc_currbufferlo = NULL; +static void *_ipc_currbufferhi = NULL; + +static u32 _ipc_seed = 0xffffffff; + +static struct _ipcreqres _ipc_responses; + +static struct _ipcheap _ipc_heaps[IPC_NUMHEAPS] = +{ + {NULL, 0, {}} // all other elements should be inited to zero, says C standard, so this should do +}; + +static vu32* const _ipcReg = (u32*)0xCD000000; + +extern void __MaskIrq(u32 nMask); +extern void __UnmaskIrq(u32 nMask); +extern void* __SYS_GetIPCBufferLo(void); +extern void* __SYS_GetIPCBufferHi(void); + +extern u32 gettick(); + +static __inline__ u32 IPC_ReadReg(u32 reg) +{ + return _ipcReg[reg]; +} + +static __inline__ void IPC_WriteReg(u32 reg,u32 val) +{ + _ipcReg[reg] = val; +} + +static __inline__ void ACR_WriteReg(u32 reg,u32 val) +{ + _ipcReg[reg>>2] = val; +} + +static __inline__ void* __ipc_allocreq() +{ + return iosAlloc(_ipc_hid,IPC_REQUESTSIZE); +} + +static __inline__ void __ipc_freereq(void *ptr) +{ + iosFree(_ipc_hid,ptr); +} + +static __inline__ void __ipc_srand(u32 seed) +{ + _ipc_seed = seed; +} + +static __inline__ u32 __ipc_rand() +{ + _ipc_seed = (214013*_ipc_seed) + 2531011; + return _ipc_seed; +} + +static s32 __ioctlvfmtCB(s32 result,void *userdata) +{ + ipccallback user_cb; + void *user_data; + struct _ioctlvfmt_cbdata *cbdata; + struct _ioctlvfmt_bufent *pbuf; + + cbdata = (struct _ioctlvfmt_cbdata*)userdata; + + // deal with data buffers + if(cbdata->bufs) { + pbuf = cbdata->bufs; + while(cbdata->num_bufs--) { + if(pbuf->ipc_buf) { + // copy data if needed + if(pbuf->io_buf && pbuf->copy_len) + memcpy(pbuf->io_buf, pbuf->ipc_buf, pbuf->copy_len); + // then free the buffer + iosFree(cbdata->hId, pbuf->ipc_buf); + } + pbuf++; + } + } + + user_cb = cbdata->user_cb; + user_data = cbdata->user_data; + + // free buffer list + __lwp_wkspace_free(cbdata->bufs); + + // free callback data + __lwp_wkspace_free(cbdata); + + // call the user callback + if(user_cb) + return user_cb(result, user_data); + + return result; +} + +static s32 __ipc_queuerequest(struct _ipcreq *req) +{ + u32 cnt; + u32 level; + _CPU_ISR_Disable(level); + + cnt = (_ipc_responses.cnt_queue - _ipc_responses.cnt_sent); + if(cnt>=16) { + _CPU_ISR_Restore(level); + return IPC_EQUEUEFULL; + } + + _ipc_responses.reqs[_ipc_responses.req_queue_no] = req; + _ipc_responses.req_queue_no = ((_ipc_responses.req_queue_no+1)&0x0f); + _ipc_responses.cnt_queue++; + + _CPU_ISR_Restore(level); + return IPC_OK; +} + +static s32 __ipc_syncqueuerequest(struct _ipcreq *req) +{ + u32 cnt = (_ipc_responses.cnt_queue - _ipc_responses.cnt_sent); + if(cnt>=16) { + return IPC_EQUEUEFULL; + } + + _ipc_responses.reqs[_ipc_responses.req_queue_no] = req; + _ipc_responses.req_queue_no = ((_ipc_responses.req_queue_no+1)&0x0f); + _ipc_responses.cnt_queue++; + + return IPC_OK; +} + +static void __ipc_sendrequest() +{ + u32 ipc_send; + struct _ipcreq *req; + u32 cnt = (_ipc_responses.cnt_queue - _ipc_responses.cnt_sent); + if(cnt>0) { + req = _ipc_responses.reqs[_ipc_responses.req_send_no]; + if(req!=NULL) { + req->magic = IPC_REQ_MAGIC; + if(req->relnch&RELNCH_RELAUNCH) { + _ipc_relnchFl = 1; + _ipc_relnchRpc = req; + if(!(req->relnch&RELNCH_BACKGROUND)) + _ipc_mailboxack--; + } + DCFlushRange(req,sizeof(struct _ipcreq)); + + IPC_WriteReg(0,MEM_VIRTUAL_TO_PHYSICAL(req)); + _ipc_responses.req_send_no = ((_ipc_responses.req_send_no+1)&0x0f); + _ipc_responses.cnt_sent++; + _ipc_mailboxack--; + + ipc_send = ((IPC_ReadReg(1)&0x30)|0x01); + IPC_WriteReg(1,ipc_send); + } + } +} + +static void __ipc_replyhandler() +{ + u32 ipc_ack,cnt; + ioctlv *v = NULL; + struct _ipcreq *req = (struct _ipcreq*)IPC_ReadReg(2); + if(req==NULL) return; + + ipc_ack = ((IPC_ReadReg(1)&0x30)|0x04); + IPC_WriteReg(1,ipc_ack); + ACR_WriteReg(48,0x40000000); + + req = MEM_PHYSICAL_TO_K0(req); + DCInvalidateRange(req,32); + + if(req->magic==IPC_REQ_MAGIC) { + if(req->req_cmd==IOS_READ) { + if(req->read.data!=NULL) { + req->read.data = MEM_PHYSICAL_TO_K0(req->read.data); + if(req->result>0) DCInvalidateRange(req->read.data,req->result); + } + } else if(req->req_cmd==IOS_IOCTL) { + if(req->ioctl.buffer_io!=NULL) { + req->ioctl.buffer_io = MEM_PHYSICAL_TO_K0(req->ioctl.buffer_io); + DCInvalidateRange(req->ioctl.buffer_io,req->ioctl.len_io); + } + DCInvalidateRange(req->ioctl.buffer_in,req->ioctl.len_in); + } else if(req->req_cmd==IOS_IOCTLV) { + if(req->ioctlv.argv!=NULL) { + req->ioctlv.argv = MEM_PHYSICAL_TO_K0(req->ioctlv.argv); + DCInvalidateRange(req->ioctlv.argv,((req->ioctlv.argcin+req->ioctlv.argcio)*sizeof(struct _ioctlv))); + } + + cnt = 0; + v = (ioctlv*)req->ioctlv.argv; + while(cnt<(req->ioctlv.argcin+req->ioctlv.argcio)) { + if(v[cnt].data!=NULL) { + v[cnt].data = MEM_PHYSICAL_TO_K0(v[cnt].data); + DCInvalidateRange(v[cnt].data,v[cnt].len); + } + cnt++; + } + if(_ipc_relnchFl && _ipc_relnchRpc==req) { + _ipc_relnchFl = 0; + if(_ipc_mailboxack<1) _ipc_mailboxack++; + } + + } + + if(req->cb!=NULL) { + req->cb(req->result,req->usrdata); + __ipc_freereq(req); + } else + LWP_ThreadSignal(req->syncqueue); + } else { + // NOTE: we really want to find out if this ever happens + // and take steps to prevent it beforehand (because it will + // clobber memory, among other things). I suggest leaving this in + // even in non-DEBUG mode. Maybe even cause a system halt. + // It is the responsibility of the loader to clear these things, + // but we want to find out if they happen so loaders can be fixed. + _ipc_spuriousresponsecnt++; + } + ipc_ack = ((IPC_ReadReg(1)&0x30)|0x08); + IPC_WriteReg(1,ipc_ack); +} + +static void __ipc_ackhandler() +{ + u32 ipc_ack; + ipc_ack = ((IPC_ReadReg(1)&0x30)|0x02); + IPC_WriteReg(1,ipc_ack); + ACR_WriteReg(48,0x40000000); + + if(_ipc_mailboxack<1) _ipc_mailboxack++; + if(_ipc_mailboxack>0) { + if(_ipc_relnchFl){ + _ipc_relnchRpc->result = 0; + _ipc_relnchFl = 0; + + LWP_ThreadSignal(_ipc_relnchRpc->syncqueue); + + ipc_ack = ((IPC_ReadReg(1)&0x30)|0x08); + IPC_WriteReg(1,ipc_ack); + } + __ipc_sendrequest(); + } + +} + +static void __ipc_interrupthandler(u32 irq,void *ctx) +{ + u32 ipc_int = IPC_ReadReg(1); + if((ipc_int&0x0014)==0x0014) __ipc_replyhandler(); + + ipc_int = IPC_ReadReg(1); + if((ipc_int&0x0022)==0x0022) __ipc_ackhandler(); +} + +static s32 __ios_ioctlvformat_parse(const char *format,va_list args,struct _ioctlvfmt_cbdata *cbdata,s32 *cnt_in,s32 *cnt_io,struct _ioctlv **argv,s32 hId) +{ + s32 ret,i; + void *pdata; + void *iodata; + char type,*ps; + s32 len,maxbufs = 0; + ioctlv *argp = NULL; + struct _ioctlvfmt_bufent *bufp; + + if(hId == IPC_HEAP) hId = _ipc_hid; + if(hId < 0) return IPC_EINVAL; + + maxbufs = strnlen(format,IOS_MAXFMT_PARAMS); + if(maxbufs>=IOS_MAXFMT_PARAMS) return IPC_EINVAL; + + cbdata->hId = hId; + cbdata->bufs = __lwp_wkspace_allocate((sizeof(struct _ioctlvfmt_bufent)*(maxbufs+1))); + if(cbdata->bufs==NULL) return IPC_ENOMEM; + + argp = iosAlloc(hId,(sizeof(struct _ioctlv)*(maxbufs+1))); + if(argp==NULL) { + __lwp_wkspace_free(cbdata->bufs); + return IPC_ENOMEM; + } + + *argv = argp; + bufp = cbdata->bufs; + memset(argp,0,(sizeof(struct _ioctlv)*(maxbufs+1))); + memset(bufp,0,(sizeof(struct _ioctlvfmt_bufent)*(maxbufs+1))); + + cbdata->num_bufs = 1; + bufp->ipc_buf = argp; + bufp++; + + *cnt_in = 0; + *cnt_io = 0; + + ret = IPC_OK; + while(*format) { + type = tolower((int)*format); + switch(type) { + case 'b': + pdata = iosAlloc(hId,sizeof(u8)); + if(pdata==NULL) { + ret = IPC_ENOMEM; + goto free_and_error; + } + *(u8*)pdata = va_arg(args,u32); + argp->data = pdata; + argp->len = sizeof(u8); + bufp->ipc_buf = pdata; + cbdata->num_bufs++; + (*cnt_in)++; + argp++; + bufp++; + break; + case 'h': + pdata = iosAlloc(hId,sizeof(u16)); + if(pdata==NULL) { + ret = IPC_ENOMEM; + goto free_and_error; + } + *(u16*)pdata = va_arg(args,u32); + argp->data = pdata; + argp->len = sizeof(u16); + bufp->ipc_buf = pdata; + cbdata->num_bufs++; + (*cnt_in)++; + argp++; + bufp++; + break; + case 'i': + pdata = iosAlloc(hId,sizeof(u32)); + if(pdata==NULL) { + ret = IPC_ENOMEM; + goto free_and_error; + } + *(u32*)pdata = va_arg(args,u32); + argp->data = pdata; + argp->len = sizeof(u32); + bufp->ipc_buf = pdata; + cbdata->num_bufs++; + (*cnt_in)++; + argp++; + bufp++; + break; + case 'q': + pdata = iosAlloc(hId,sizeof(u64)); + if(pdata==NULL) { + ret = IPC_ENOMEM; + goto free_and_error; + } + *(u64*)pdata = va_arg(args,u64); + argp->data = pdata; + argp->len = sizeof(u64); + bufp->ipc_buf = pdata; + cbdata->num_bufs++; + (*cnt_in)++; + argp++; + bufp++; + break; + case 'd': + argp->data = va_arg(args, void*); + argp->len = va_arg(args, u32); + (*cnt_in)++; + argp++; + break; + case 's': + ps = va_arg(args, char*); + len = strnlen(ps,256); + if(len>=256) { + ret = IPC_EINVAL; + goto free_and_error; + } + + pdata = iosAlloc(hId,(len+1)); + if(pdata==NULL) { + ret = IPC_ENOMEM; + goto free_and_error; + } + memcpy(pdata,ps,(len+1)); + argp->data = pdata; + argp->len = (len+1); + bufp->ipc_buf = pdata; + cbdata->num_bufs++; + (*cnt_in)++; + argp++; + bufp++; + break; + case ':': + format++; + goto parse_io_params; + default: + ret = IPC_EINVAL; + goto free_and_error; + } + format++; + } + +parse_io_params: + while(*format) { + type = tolower((int)*format); + switch(type) { + case 'b': + pdata = iosAlloc(hId,sizeof(u8)); + if(pdata==NULL) { + ret = IPC_ENOMEM; + goto free_and_error; + } + iodata = va_arg(args,u8*); + *(u8*)pdata = *(u8*)iodata; + argp->data = pdata; + argp->len = sizeof(u8); + bufp->ipc_buf = pdata; + bufp->io_buf = iodata; + bufp->copy_len = sizeof(u8); + cbdata->num_bufs++; + (*cnt_io)++; + argp++; + bufp++; + break; + case 'h': + pdata = iosAlloc(hId,sizeof(u16)); + if(pdata==NULL) { + ret = IPC_ENOMEM; + goto free_and_error; + } + iodata = va_arg(args,u16*); + *(u16*)pdata = *(u16*)iodata; + argp->data = pdata; + argp->len = sizeof(u16); + bufp->ipc_buf = pdata; + bufp->io_buf = iodata; + bufp->copy_len = sizeof(u16); + cbdata->num_bufs++; + (*cnt_io)++; + argp++; + bufp++; + break; + case 'i': + pdata = iosAlloc(hId,sizeof(u32)); + if(pdata==NULL) { + ret = IPC_ENOMEM; + goto free_and_error; + } + iodata = va_arg(args,u32*); + *(u32*)pdata = *(u32*)iodata; + argp->data = pdata; + argp->len = sizeof(u32); + bufp->ipc_buf = pdata; + bufp->io_buf = iodata; + bufp->copy_len = sizeof(u32); + cbdata->num_bufs++; + (*cnt_io)++; + argp++; + bufp++; + break; + case 'q': + pdata = iosAlloc(hId,sizeof(u64)); + if(pdata==NULL) { + ret = IPC_ENOMEM; + goto free_and_error; + } + iodata = va_arg(args,u64*); + *(u64*)pdata = *(u64*)iodata; + argp->data = pdata; + argp->len = sizeof(u64); + bufp->ipc_buf = pdata; + bufp->io_buf = iodata; + bufp->copy_len = sizeof(u64); + cbdata->num_bufs++; + (*cnt_io)++; + argp++; + bufp++; + break; + case 'd': + argp->data = va_arg(args, void*); + argp->len = va_arg(args, u32); + (*cnt_io)++; + argp++; + break; + default: + ret = IPC_EINVAL; + goto free_and_error; + } + format++; + } + return IPC_OK; + +free_and_error: + for(i=0;inum_bufs;i++) { + if(cbdata->bufs[i].ipc_buf!=NULL) iosFree(hId,cbdata->bufs[i].ipc_buf); + } + __lwp_wkspace_free(cbdata->bufs); + return ret; +} + +static s32 __ipc_asyncrequest(struct _ipcreq *req) +{ + s32 ret; + u32 level; + + ret = __ipc_queuerequest(req); + if(ret) __ipc_freereq(req); + else { + _CPU_ISR_Disable(level); + if(_ipc_mailboxack>0) __ipc_sendrequest(); + _CPU_ISR_Restore(level); + } + return ret; +} + +static s32 __ipc_syncrequest(struct _ipcreq *req) +{ + s32 ret; + u32 level; + + LWP_InitQueue(&req->syncqueue); + + _CPU_ISR_Disable(level); + ret = __ipc_syncqueuerequest(req); + if(ret==0) { + if(_ipc_mailboxack>0) __ipc_sendrequest(); + LWP_ThreadSleep(req->syncqueue); + ret = req->result; + } + _CPU_ISR_Restore(level); + + LWP_CloseQueue(req->syncqueue); + return ret; +} + +s32 iosCreateHeap(s32 size) +{ + s32 i,ret; + s32 free; + u32 level; + u32 ipclo,ipchi; + _CPU_ISR_Disable(level); + + i=0; + while(i=IPC_NUMHEAPS) { + _CPU_ISR_Restore(level); + return IPC_ENOHEAP; + } + + ipclo = (((u32)IPC_GetBufferLo()+0x1f)&~0x1f); + ipchi = (u32)IPC_GetBufferHi(); + free = (ipchi - (ipclo + size)); + if(free<0) return IPC_ENOMEM; + + _ipc_heaps[i].membase = (void*)ipclo; + _ipc_heaps[i].size = size; + + ret = __lwp_heap_init(&_ipc_heaps[i].heap,(void*)ipclo,size,PPC_CACHE_ALIGNMENT); + if(ret<=0) return IPC_ENOMEM; + + IPC_SetBufferLo((void*)(ipclo+size)); + _CPU_ISR_Restore(level); + return i; +} + +void* iosAlloc(s32 hid,s32 size) +{ + if(hid<0 || hid>=IPC_NUMHEAPS || size<=0) return NULL; + return __lwp_heap_allocate(&_ipc_heaps[hid].heap,size); +} + +void iosFree(s32 hid,void *ptr) +{ + if(hid<0 || hid>=IPC_NUMHEAPS || ptr==NULL) return; + __lwp_heap_free(&_ipc_heaps[hid].heap,ptr); +} + +void* IPC_GetBufferLo() +{ + return _ipc_currbufferlo; +} + +void* IPC_GetBufferHi() +{ + return _ipc_currbufferhi; +} + +void IPC_SetBufferLo(void *bufferlo) +{ + if(_ipc_bufferlo<=bufferlo) _ipc_currbufferlo = bufferlo; +} + +void IPC_SetBufferHi(void *bufferhi) +{ + if(bufferhi<=_ipc_bufferhi) _ipc_currbufferhi = bufferhi; +} + +void __IPC_Init(void) +{ + if(!_ipc_initialized) { + _ipc_bufferlo = _ipc_currbufferlo = __SYS_GetIPCBufferLo(); + _ipc_bufferhi = _ipc_currbufferhi = __SYS_GetIPCBufferHi(); + _ipc_initialized = 1; + } +} + +u32 __IPC_ClntInit(void) +{ + if(!_ipc_clntinitialized) { + _ipc_clntinitialized = 1; + + // generate a random request magic + __ipc_srand(gettick()); + IPC_REQ_MAGIC = __ipc_rand(); + + __IPC_Init(); + + _ipc_hid = iosCreateHeap(IPC_HEAP_SIZE); + IRQ_Request(IRQ_PI_ACR,__ipc_interrupthandler,NULL); + __UnmaskIrq(IM_PI_ACR); + IPC_WriteReg(1,56); + } + return IPC_OK; +} + +void __IPC_Reinitialize(void) +{ + u32 level; + + _CPU_ISR_Disable(level); + + IPC_WriteReg(1,56); + + _ipc_mailboxack = 1; + _ipc_relnchFl = 0; + _ipc_relnchRpc = NULL; + + _ipc_responses.req_queue_no = 0; + _ipc_responses.cnt_queue = 0; + _ipc_responses.req_send_no = 0; + _ipc_responses.cnt_sent = 0; + + _CPU_ISR_Restore(level); +} + +s32 IOS_Open(const char *filepath,u32 mode) +{ + s32 ret; + struct _ipcreq *req; + + if(filepath==NULL) return IPC_EINVAL; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_OPEN; + req->cb = NULL; + req->relnch = 0; + + DCFlushRange((void*)filepath,strnlen(filepath,IPC_MAXPATH_LEN) + 1); + + req->open.filepath = (char*)MEM_VIRTUAL_TO_PHYSICAL(filepath); + req->open.mode = mode; + + ret = __ipc_syncrequest(req); + + if(req!=NULL) __ipc_freereq(req); + return ret; +} + +s32 IOS_OpenAsync(const char *filepath,u32 mode,ipccallback ipc_cb,void *usrdata) +{ + struct _ipcreq *req; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_OPEN; + req->cb = ipc_cb; + req->usrdata = usrdata; + req->relnch = 0; + + DCFlushRange((void*)filepath,strnlen(filepath,IPC_MAXPATH_LEN) + 1); + + req->open.filepath = (char*)MEM_VIRTUAL_TO_PHYSICAL(filepath); + req->open.mode = mode; + + return __ipc_asyncrequest(req); +} + +s32 IOS_Close(s32 fd) +{ + s32 ret; + struct _ipcreq *req; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_CLOSE; + req->fd = fd; + req->cb = NULL; + req->relnch = 0; + + ret = __ipc_syncrequest(req); + + if(req!=NULL) __ipc_freereq(req); + return ret; +} + +s32 IOS_CloseAsync(s32 fd,ipccallback ipc_cb,void *usrdata) +{ + struct _ipcreq *req; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_CLOSE; + req->fd = fd; + req->cb = ipc_cb; + req->usrdata = usrdata; + req->relnch = 0; + + return __ipc_asyncrequest(req); +} + +s32 IOS_Read(s32 fd,void *buf,s32 len) +{ + s32 ret; + struct _ipcreq *req; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_READ; + req->fd = fd; + req->cb = NULL; + req->relnch = 0; + + DCInvalidateRange(buf,len); + req->read.data = (void*)MEM_VIRTUAL_TO_PHYSICAL(buf); + req->read.len = len; + + ret = __ipc_syncrequest(req); + + if(req!=NULL) __ipc_freereq(req); + return ret; +} + +s32 IOS_ReadAsync(s32 fd,void *buf,s32 len,ipccallback ipc_cb,void *usrdata) +{ + struct _ipcreq *req; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_READ; + req->fd = fd; + req->cb = ipc_cb; + req->usrdata = usrdata; + req->relnch = 0; + + DCInvalidateRange(buf,len); + req->read.data = (void*)MEM_VIRTUAL_TO_PHYSICAL(buf); + req->read.len = len; + + return __ipc_asyncrequest(req); +} + +s32 IOS_Write(s32 fd,const void *buf,s32 len) +{ + s32 ret; + struct _ipcreq *req; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_WRITE; + req->fd = fd; + req->cb = NULL; + req->relnch = 0; + + DCFlushRange((void*)buf,len); + req->write.data = (void*)MEM_VIRTUAL_TO_PHYSICAL(buf); + req->write.len = len; + + ret = __ipc_syncrequest(req); + + if(req!=NULL) __ipc_freereq(req); + return ret; +} + +s32 IOS_WriteAsync(s32 fd,const void *buf,s32 len,ipccallback ipc_cb,void *usrdata) +{ + struct _ipcreq *req; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_WRITE; + req->fd = fd; + req->cb = ipc_cb; + req->usrdata = usrdata; + req->relnch = 0; + + DCFlushRange((void*)buf,len); + req->write.data = (void*)MEM_VIRTUAL_TO_PHYSICAL(buf); + req->write.len = len; + + return __ipc_asyncrequest(req); +} + +s32 IOS_Seek(s32 fd,s32 where,s32 whence) +{ + s32 ret; + struct _ipcreq *req; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_SEEK; + req->fd = fd; + req->cb = NULL; + req->relnch = 0; + + req->seek.where = where; + req->seek.whence = whence; + + ret = __ipc_syncrequest(req); + + if(req!=NULL) __ipc_freereq(req); + return ret; +} + +s32 IOS_SeekAsync(s32 fd,s32 where,s32 whence,ipccallback ipc_cb,void *usrdata) +{ + struct _ipcreq *req; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_SEEK; + req->fd = fd; + req->cb = ipc_cb; + req->usrdata = usrdata; + req->relnch = 0; + + req->seek.where = where; + req->seek.whence = whence; + + return __ipc_asyncrequest(req); +} + +s32 IOS_Ioctl(s32 fd,s32 ioctl,void *buffer_in,s32 len_in,void *buffer_io,s32 len_io) +{ + s32 ret; + struct _ipcreq *req; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_IOCTL; + req->fd = fd; + req->cb = NULL; + req->relnch = 0; + + req->ioctl.ioctl = ioctl; + req->ioctl.buffer_in = (void*)MEM_VIRTUAL_TO_PHYSICAL(buffer_in); + req->ioctl.len_in = len_in; + req->ioctl.buffer_io = (void*)MEM_VIRTUAL_TO_PHYSICAL(buffer_io); + req->ioctl.len_io = len_io; + + DCFlushRange(buffer_in,len_in); + DCFlushRange(buffer_io,len_io); + + ret = __ipc_syncrequest(req); + + if(req!=NULL) __ipc_freereq(req); + return ret; +} + +s32 IOS_IoctlAsync(s32 fd,s32 ioctl,void *buffer_in,s32 len_in,void *buffer_io,s32 len_io,ipccallback ipc_cb,void *usrdata) +{ + struct _ipcreq *req; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_IOCTL; + req->fd = fd; + req->cb = ipc_cb; + req->usrdata = usrdata; + req->relnch = 0; + + req->ioctl.ioctl = ioctl; + req->ioctl.buffer_in = (void*)MEM_VIRTUAL_TO_PHYSICAL(buffer_in); + req->ioctl.len_in = len_in; + req->ioctl.buffer_io = (void*)MEM_VIRTUAL_TO_PHYSICAL(buffer_io); + req->ioctl.len_io = len_io; + + DCFlushRange(buffer_in,len_in); + DCFlushRange(buffer_io,len_io); + + return __ipc_asyncrequest(req); +} + +s32 IOS_Ioctlv(s32 fd,s32 ioctl,s32 cnt_in,s32 cnt_io,ioctlv *argv) +{ + s32 i,ret; + struct _ipcreq *req; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_IOCTLV; + req->fd = fd; + req->cb = NULL; + req->relnch = 0; + + req->ioctlv.ioctl = ioctl; + req->ioctlv.argcin = cnt_in; + req->ioctlv.argcio = cnt_io; + req->ioctlv.argv = (struct _ioctlv*)MEM_VIRTUAL_TO_PHYSICAL(argv); + + i = 0; + while(i0) { + DCFlushRange(argv[i].data,argv[i].len); + argv[i].data = (void*)MEM_VIRTUAL_TO_PHYSICAL(argv[i].data); + } + i++; + } + + i = 0; + while(i0) { + DCFlushRange(argv[cnt_in+i].data,argv[cnt_in+i].len); + argv[cnt_in+i].data = (void*)MEM_VIRTUAL_TO_PHYSICAL(argv[cnt_in+i].data); + } + i++; + } + DCFlushRange(argv,((cnt_in+cnt_io)<<3)); + + ret = __ipc_syncrequest(req); + + if(req!=NULL) __ipc_freereq(req); + return ret; +} + + +s32 IOS_IoctlvAsync(s32 fd,s32 ioctl,s32 cnt_in,s32 cnt_io,ioctlv *argv,ipccallback ipc_cb,void *usrdata) +{ + s32 i; + struct _ipcreq *req; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_IOCTLV; + req->fd = fd; + req->cb = ipc_cb; + req->usrdata = usrdata; + req->relnch = 0; + + req->ioctlv.ioctl = ioctl; + req->ioctlv.argcin = cnt_in; + req->ioctlv.argcio = cnt_io; + req->ioctlv.argv = (struct _ioctlv*)MEM_VIRTUAL_TO_PHYSICAL(argv); + + i = 0; + while(i0) { + DCFlushRange(argv[i].data,argv[i].len); + argv[i].data = (void*)MEM_VIRTUAL_TO_PHYSICAL(argv[i].data); + } + i++; + } + + i = 0; + while(i0) { + DCFlushRange(argv[cnt_in+i].data,argv[cnt_in+i].len); + argv[cnt_in+i].data = (void*)MEM_VIRTUAL_TO_PHYSICAL(argv[cnt_in+i].data); + } + i++; + } + DCFlushRange(argv,((cnt_in+cnt_io)<<3)); + + return __ipc_asyncrequest(req); +} + +s32 IOS_IoctlvFormat(s32 hId,s32 fd,s32 ioctl,const char *format,...) +{ + s32 ret; + va_list args; + s32 cnt_in,cnt_io; + struct _ioctlv *argv; + struct _ioctlvfmt_cbdata *cbdata; + + cbdata = __lwp_wkspace_allocate(sizeof(struct _ioctlvfmt_cbdata)); + if(cbdata==NULL) return IPC_ENOMEM; + + memset(cbdata,0,sizeof(struct _ioctlvfmt_cbdata)); + + va_start(args,format); + ret = __ios_ioctlvformat_parse(format,args,cbdata,&cnt_in,&cnt_io,&argv,hId); + va_end(args); + if(ret<0) { + __lwp_wkspace_free(cbdata); + return ret; + } + + ret = IOS_Ioctlv(fd,ioctl,cnt_in,cnt_io,argv); + __ioctlvfmtCB(ret,cbdata); + + return ret; +} + +s32 IOS_IoctlvFormatAsync(s32 hId,s32 fd,s32 ioctl,ipccallback usr_cb,void *usr_data,const char *format,...) +{ + s32 ret; + va_list args; + s32 cnt_in,cnt_io; + struct _ioctlv *argv; + struct _ioctlvfmt_cbdata *cbdata; + + cbdata = __lwp_wkspace_allocate(sizeof(struct _ioctlvfmt_cbdata)); + if(cbdata==NULL) return IPC_ENOMEM; + + memset(cbdata,0,sizeof(struct _ioctlvfmt_cbdata)); + + va_start(args,format); + ret = __ios_ioctlvformat_parse(format,args,cbdata,&cnt_in,&cnt_io,&argv,hId); + va_end(args); + if(ret<0) { + __lwp_wkspace_free(cbdata); + return ret; + } + + cbdata->user_cb = usr_cb; + cbdata->user_data = usr_data; + return IOS_IoctlvAsync(fd,ioctl,cnt_in,cnt_io,argv,__ioctlvfmtCB,cbdata); +} + +s32 IOS_IoctlvReboot(s32 fd,s32 ioctl,s32 cnt_in,s32 cnt_io,ioctlv *argv) +{ + s32 i,ret; + struct _ipcreq *req; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_IOCTLV; + req->fd = fd; + req->cb = NULL; + req->relnch = RELNCH_RELAUNCH; + + req->ioctlv.ioctl = ioctl; + req->ioctlv.argcin = cnt_in; + req->ioctlv.argcio = cnt_io; + req->ioctlv.argv = (struct _ioctlv*)MEM_VIRTUAL_TO_PHYSICAL(argv); + + i = 0; + while(i0) { + DCFlushRange(argv[i].data,argv[i].len); + argv[i].data = (void*)MEM_VIRTUAL_TO_PHYSICAL(argv[i].data); + } + i++; + } + + i = 0; + while(i0) { + DCFlushRange(argv[cnt_in+i].data,argv[cnt_in+i].len); + argv[cnt_in+i].data = (void*)MEM_VIRTUAL_TO_PHYSICAL(argv[cnt_in+i].data); + } + i++; + } + DCFlushRange(argv,((cnt_in+cnt_io)<<3)); + + ret = __ipc_syncrequest(req); + + if(req!=NULL) __ipc_freereq(req); + return ret; +} + +s32 IOS_IoctlvRebootBackground(s32 fd,s32 ioctl,s32 cnt_in,s32 cnt_io,ioctlv *argv) +{ + s32 i,ret; + struct _ipcreq *req; + + req = __ipc_allocreq(); + if(req==NULL) return IPC_ENOMEM; + + req->cmd = IOS_IOCTLV; + req->result = 0; + req->fd = fd; + req->cb = NULL; + req->relnch = RELNCH_BACKGROUND|RELNCH_RELAUNCH; + + req->ioctlv.ioctl = ioctl; + req->ioctlv.argcin = cnt_in; + req->ioctlv.argcio = cnt_io; + req->ioctlv.argv = (struct _ioctlv*)MEM_VIRTUAL_TO_PHYSICAL(argv); + + i = 0; + while(i0) { + DCFlushRange(argv[i].data,argv[i].len); + argv[i].data = (void*)MEM_VIRTUAL_TO_PHYSICAL(argv[i].data); + } + i++; + } + + i = 0; + while(i0) { + DCFlushRange(argv[cnt_in+i].data,argv[cnt_in+i].len); + argv[cnt_in+i].data = (void*)MEM_VIRTUAL_TO_PHYSICAL(argv[cnt_in+i].data); + } + i++; + } + DCFlushRange(argv,((cnt_in+cnt_io)<<3)); + + ret = __ipc_syncrequest(req); + + if(req!=NULL) __ipc_freereq(req); + return ret; +} + +#endif diff --git a/wii/libogc/libogc/irq.c b/wii/libogc/libogc/irq.c new file mode 100644 index 0000000000..0e59f51e2b --- /dev/null +++ b/wii/libogc/libogc/irq.c @@ -0,0 +1,421 @@ +/*------------------------------------------------------------- + +irq.h -- Interrupt subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#include +#include +#include "asm.h" +#include "cache.h" +#include "context.h" +#include "processor.h" +#include "lwp_threads.h" +#include "irq.h" +#include "console.h" + +#define CPU_STACK_ALIGNMENT 8 +#define CPU_MINIMUM_STACK_FRAME_SIZE 16 + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +struct irq_handler_s { + raw_irq_handler_t pHndl; + void *pCtx; +}; + +static u64 spuriousIrq = 0; +static u32 prevIrqMask = 0; +static u32 currIrqMask = 0; +static struct irq_handler_s g_IRQHandler[32]; + +static vu32* const _piReg = (u32*)0xCC003000; +static vu16* const _memReg = (u16*)0xCC004000; +static vu16* const _dspReg = (u16*)0xCC005000; + +#if defined(HW_DOL) +static vu32* const _exiReg = (u32*)0xCC006800; +static vu32* const _aiReg = (u32*)0xCC006C00; +#elif defined(HW_RVL) +static vu32* const _exiReg = (u32*)0xCD006800; +static vu32* const _aiReg = (u32*)0xCD006C00; +#endif + +static u32 const _irqPrio[] = {IM_PI_ERROR,IM_PI_DEBUG,IM_MEM,IM_PI_RSW, + IM_PI_VI,(IM_PI_PETOKEN|IM_PI_PEFINISH), + IM_PI_HSP, + (IM_DSP_ARAM|IM_DSP_DSP|IM_AI|IM_EXI|IM_PI_SI|IM_PI_DI), + IM_DSP_AI,IM_PI_CP, +#if defined(HW_RVL) + IM_PI_ACR, +#endif + 0xffffffff}; + +extern void __exception_load(u32,void *,u32,void *); + +extern s8 irqhandler_start[],irqhandler_end[]; +extern u8 __intrstack_addr[],__intrstack_end[]; + +void c_irqdispatcher(frame_context *ctx) +{ + u32 i,icause,intmask,irq = 0; + u32 cause,mask; + + cause = _piReg[0]&~0x10000; + mask = _piReg[1]; + + if(!cause || !(cause&mask)) { + spuriousIrq++; + return; + } + + intmask = 0; + if(cause&0x00000080) { //Memory Interface + icause = _memReg[15]; + if(icause&0x00000001) { + intmask |= IRQMASK(IRQ_MEM0); + } + if(icause&0x00000002) { + intmask |= IRQMASK(IRQ_MEM1); + } + if(icause&0x00000004) { + intmask |= IRQMASK(IRQ_MEM2); + } + if(icause&0x00000008) { + intmask |= IRQMASK(IRQ_MEM3); + } + if(icause&0x00000010) { + intmask |= IRQMASK(IRQ_MEMADDRESS); + } + } + if(cause&0x00000040) { //DSP + icause = _dspReg[5]; + if(icause&0x00000008){ + intmask |= IRQMASK(IRQ_DSP_AI); + } + if(icause&0x00000020){ + intmask |= IRQMASK(IRQ_DSP_ARAM); + } + if(icause&0x00000080){ + intmask |= IRQMASK(IRQ_DSP_DSP); + } + } + if(cause&0x00000020) { //Streaming + icause = _aiReg[0]; + if(icause&0x00000008) { + intmask |= IRQMASK(IRQ_AI); + } + } + if(cause&0x00000010) { //EXI + //EXI 0 + icause = _exiReg[0]; + if(icause&0x00000002) { + intmask |= IRQMASK(IRQ_EXI0_EXI); + } + if(icause&0x00000008) { + intmask |= IRQMASK(IRQ_EXI0_TC); + } + if(icause&0x00000800) { + intmask |= IRQMASK(IRQ_EXI0_EXT); + } + //EXI 1 + icause = _exiReg[5]; + if(icause&0x00000002) { + intmask |= IRQMASK(IRQ_EXI1_EXI); + } + if(icause&0x00000008) { + intmask |= IRQMASK(IRQ_EXI1_TC); + } + if(icause&0x00000800) { + intmask |= IRQMASK(IRQ_EXI1_EXT); + } + //EXI 2 + icause = _exiReg[10]; + if(icause&0x00000002) { + intmask |= IRQMASK(IRQ_EXI2_EXI); + } + if(icause&0x00000008) { + intmask |= IRQMASK(IRQ_EXI2_TC); + } + } + if(cause&0x00002000) { //High Speed Port + intmask |= IRQMASK(IRQ_PI_HSP); + } + if(cause&0x00001000) { //External Debugger + intmask |= IRQMASK(IRQ_PI_DEBUG); + } + if(cause&0x00000400) { //Frame Ready (PE_FINISH) + intmask |= IRQMASK(IRQ_PI_PEFINISH); + } + if(cause&0x00000200) { //Token Assertion (PE_TOKEN) + intmask |= IRQMASK(IRQ_PI_PETOKEN); + } + if(cause&0x00000100) { //Video Interface + intmask |= IRQMASK(IRQ_PI_VI); + } + if(cause&0x00000008) { //Serial + intmask |= IRQMASK(IRQ_PI_SI); + } + if(cause&0x00000004) { //DVD + intmask |= IRQMASK(IRQ_PI_DI); + } + if(cause&0x00000002) { //Reset Switch + intmask |= IRQMASK(IRQ_PI_RSW); + } + if(cause&0x00000800) { //Command FIFO + intmask |= IRQMASK(IRQ_PI_CP); + } + if(cause&0x00000001) { //GP Runtime Error + intmask |= IRQMASK(IRQ_PI_ERROR); + } +#if defined(HW_RVL) + if(cause&0x00004000) { + intmask |= IRQMASK(IRQ_PI_ACR); + } +#endif + mask = intmask&~(prevIrqMask|currIrqMask); + if(mask) { + i=0; + irq = 0; + while(i<(sizeof(_irqPrio)/sizeof(u32))) { + if((irq=(mask&_irqPrio[i]))) { + irq = cntlzw(irq); + break; + } + i++; + } + + if(g_IRQHandler[irq].pHndl) g_IRQHandler[irq].pHndl(irq,g_IRQHandler[irq].pCtx); + } +} + +static u32 __SetInterrupts(u32 iMask,u32 nMask) +{ + u32 imask; + u32 irq = cntlzw(iMask); + + if(irq<=IRQ_MEMADDRESS) + { + imask = 0; + if(!(nMask&IM_MEM0)) imask |= 0x0001; + if(!(nMask&IM_MEM1)) imask |= 0x0002; + if(!(nMask&IM_MEM2)) imask |= 0x0004; + if(!(nMask&IM_MEM3)) imask |= 0x0008; + if(!(nMask&IM_MEMADDRESS)) imask |= 0x0010; + _memReg[14] = (u16)imask; + return (iMask&~IM_MEM); + } + + if(irq>=IRQ_DSP_AI && irq<=IRQ_DSP_DSP) { + imask = _dspReg[5]&~0x1f8; + if(!(nMask&IM_DSP_AI)) imask |= 0x0010; + if(!(nMask&IM_DSP_ARAM)) imask |= 0x0040; + if(!(nMask&IM_DSP_DSP)) imask |= 0x0100; + _dspReg[5] = (u16)imask; + return (iMask&~IM_DSP); + } + + if(irq==IRQ_AI) { + imask = _aiReg[0]&~0x2c; + if(!(nMask&IM_AI)) imask |= 0x0004; + _aiReg[0] = imask; + return (iMask&~IM_AI); + } + if(irq>=IRQ_EXI0_EXI && irq<=IRQ_EXI0_EXT) { + imask = _exiReg[0]&~0x2c0f; + if(!(nMask&IM_EXI0_EXI)) imask |= 0x0001; + if(!(nMask&IM_EXI0_TC)) imask |= 0x0004; + if(!(nMask&IM_EXI0_EXT)) imask |= 0x0400; + _exiReg[0] = imask; + return (iMask&~IM_EXI0); + } + + if(irq>=IRQ_EXI1_EXI && irq<=IRQ_EXI1_EXT) { + imask = _exiReg[5]&~0x0c0f; + if(!(nMask&IM_EXI1_EXI)) imask |= 0x0001; + if(!(nMask&IM_EXI1_TC)) imask |= 0x0004; + if(!(nMask&IM_EXI1_EXT)) imask |= 0x0400; + _exiReg[5] = imask; + return (iMask&~IM_EXI1); + } + + if(irq>=IRQ_EXI2_EXI && irq<=IRQ_EXI2_TC) { + imask = _exiReg[10]&~0x000f; + if(!(nMask&IM_EXI2_EXI)) imask |= 0x0001; + if(!(nMask&IM_EXI2_TC)) imask |= 0x0004; + _exiReg[10] = imask; + return (iMask&~IM_EXI2); + } + +#if defined(HW_DOL) + if(irq>=IRQ_PI_CP && irq<=IRQ_PI_HSP) { +#elif defined(HW_RVL) + if(irq>=IRQ_PI_CP && irq<=IRQ_PI_ACR) { +#endif + imask = 0xf0; + if(!(nMask&IM_PI_ERROR)) { + imask |= 0x00000001; + } + if(!(nMask&IM_PI_RSW)) { + imask |= 0x00000002; + } + if(!(nMask&IM_PI_DI)) { + imask |= 0x00000004; + } + if(!(nMask&IM_PI_SI)) { + imask |= 0x00000008; + } + if(!(nMask&IM_PI_VI)) { + imask |= 0x00000100; + } + if(!(nMask&IM_PI_PETOKEN)) { + imask |= 0x00000200; + } + if(!(nMask&IM_PI_PEFINISH)) { + imask |= 0x00000400; + } + if(!(nMask&IM_PI_CP)) { + imask |= 0x00000800; + } + if(!(nMask&IM_PI_DEBUG)) { + imask |= 0x00001000; + } + if(!(nMask&IM_PI_HSP)) { + imask |= 0x00002000; + } +#if defined(HW_RVL) + if(!(nMask&IM_PI_ACR)) { + imask |= 0x00004000; + } +#endif + _piReg[1] = imask; + return (iMask&~IM_PI); + } + return 0; +} + +void __UnmaskIrq(u32 nMask) +{ + u32 level; + u32 mask; + + _CPU_ISR_Disable(level); + mask = (nMask&(prevIrqMask|currIrqMask)); + nMask = (prevIrqMask&~nMask); + prevIrqMask = nMask; + while((mask=__SetInterrupts(mask,(nMask|currIrqMask)))!=0); + _CPU_ISR_Restore(level); +} + +void __MaskIrq(u32 nMask) +{ + u32 level; + u32 mask; + + _CPU_ISR_Disable(level); + mask = (nMask&~(prevIrqMask|currIrqMask)); + nMask = (nMask|prevIrqMask); + prevIrqMask = nMask; + while((mask=__SetInterrupts(mask,(nMask|currIrqMask)))!=0); + _CPU_ISR_Restore(level); +} + +void __irq_init() +{ + register u32 intrStack = (u32)__intrstack_addr; + register u32 intrStack_end = (u32)__intrstack_end; + register u32 irqNestingLevel = 0; + + memset(g_IRQHandler,0,32*sizeof(struct irq_handler_s)); + + *((u32*)intrStack_end) = 0xDEADBEEF; + intrStack = intrStack - CPU_MINIMUM_STACK_FRAME_SIZE; + intrStack &= ~(CPU_STACK_ALIGNMENT-1); + *((u32*)intrStack) = 0; + + mtspr(272,irqNestingLevel); + mtspr(273,intrStack); + + prevIrqMask = 0; + currIrqMask = 0; + _piReg[1] = 0xf0; + + __MaskIrq(-32); + + _piReg[0] = 0x01; + __UnmaskIrq(IM_PI_ERROR); +} + +raw_irq_handler_t IRQ_Request(u32 nIrq,raw_irq_handler_t pHndl,void *pCtx) +{ + u32 level; + + _CPU_ISR_Disable(level); + raw_irq_handler_t old = g_IRQHandler[nIrq].pHndl; + g_IRQHandler[nIrq].pHndl = pHndl; + g_IRQHandler[nIrq].pCtx = pCtx; + _CPU_ISR_Restore(level); + return old; +} + +raw_irq_handler_t IRQ_GetHandler(u32 nIrq) +{ + u32 level; + raw_irq_handler_t ret; + + _CPU_ISR_Disable(level); + ret = g_IRQHandler[nIrq].pHndl; + _CPU_ISR_Restore(level); + return ret; +} + +raw_irq_handler_t IRQ_Free(u32 nIrq) +{ + u32 level; + + _CPU_ISR_Disable(level); + raw_irq_handler_t old = g_IRQHandler[nIrq].pHndl; + g_IRQHandler[nIrq].pHndl = NULL; + g_IRQHandler[nIrq].pCtx = NULL; + _CPU_ISR_Restore(level); + return old; +} + +u32 IRQ_Disable() +{ + u32 level; + _CPU_ISR_Disable(level); + return level; +} + +void IRQ_Restore(u32 level) +{ + _CPU_ISR_Restore(level); +} diff --git a/wii/libogc/libogc/irq_handler.S b/wii/libogc/libogc/irq_handler.S new file mode 100644 index 0000000000..da37e43d81 --- /dev/null +++ b/wii/libogc/libogc/irq_handler.S @@ -0,0 +1,166 @@ +/*------------------------------------------------------------- + +irq_handler.S -- Interrupt subsystem + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#include + +#define EXCEPTION_PROLOG \ + mfspr r0,912; \ + stw r0,GQR0_OFFSET(sp); \ + mfspr r0,913; \ + stw r0,GQR1_OFFSET(sp); \ + mfspr r0,914; \ + stw r0,GQR2_OFFSET(sp); \ + mfspr r0,915; \ + stw r0,GQR3_OFFSET(sp); \ + mfspr r0,916; \ + stw r0,GQR4_OFFSET(sp); \ + mfspr r0,917; \ + stw r0,GQR5_OFFSET(sp); \ + mfspr r0,918; \ + stw r0,GQR6_OFFSET(sp); \ + mfspr r0,919; \ + stw r0,GQR7_OFFSET(sp); \ + stw r6,GPR6_OFFSET(sp); \ + stw r7,GPR7_OFFSET(sp); \ + stw r8,GPR8_OFFSET(sp); \ + stw r9,GPR9_OFFSET(sp); \ + stw r10,GPR10_OFFSET(sp); \ + stw r11,GPR11_OFFSET(sp); \ + stw r12,GPR12_OFFSET(sp); \ + stw r13,GPR13_OFFSET(sp); \ + stw r14,GPR14_OFFSET(sp); \ + stw r15,GPR15_OFFSET(sp) + +#define EXCEPTION_EPILOG \ + lwz r4,GQR0_OFFSET(sp); \ + mtspr 912,r4; \ + lwz r4,GQR1_OFFSET(sp); \ + mtspr 913,r4; \ + lwz r4,GQR2_OFFSET(sp); \ + mtspr 914,r4; \ + lwz r4,GQR3_OFFSET(sp); \ + mtspr 915,r4; \ + lwz r4,GQR4_OFFSET(sp); \ + mtspr 916,r4; \ + lwz r4,GQR5_OFFSET(sp); \ + mtspr 917,r4; \ + lwz r4,GQR6_OFFSET(sp); \ + mtspr 918,r4; \ + lwz r4,GQR7_OFFSET(sp); \ + mtspr 919,r4; \ + lwz r15,GPR15_OFFSET(sp); \ + lwz r14,GPR14_OFFSET(sp); \ + lwz r13,GPR13_OFFSET(sp); \ + lwz r12,GPR12_OFFSET(sp); \ + lwz r11,GPR11_OFFSET(sp); \ + lwz r10,GPR10_OFFSET(sp); \ + lwz r9,GPR9_OFFSET(sp); \ + lwz r8,GPR8_OFFSET(sp); \ + lwz r7,GPR7_OFFSET(sp); \ + lwz r6,GPR6_OFFSET(sp); \ + lwz r5,GPR5_OFFSET(sp) + + .extern c_irqdispatcher + .globl irq_exceptionhandler +irq_exceptionhandler: + stwu sp,-EXCEPTION_FRAME_END(sp) //now we're able to adjust the stackpointer with it's cached address + + EXCEPTION_PROLOG + + mfmsr r3 + ori r3,r3,MSR_RI + mtmsr r3 + isync + + addi r14,sp,0 + lis r15,_thread_dispatch_disable_level@ha + + mfspr r3,SPRG0 + cmpwi r3,0 + bne nested + mfspr sp,SPRG1 + +nested: + addi r3,r3,1 + lwz r6,_thread_dispatch_disable_level@l(r15) + mtspr SPRG0,r3 + addi r6,r6,1 + stw r6,_thread_dispatch_disable_level@l(r15) + + addi r3,r14,0x08 + bl c_irqdispatcher + + mfspr r4,SPRG0 + lwz r3,_thread_dispatch_disable_level@l(r15) + addi r4,r4,-1 + addic. r3,r3,-1 + mtspr SPRG0,r4 + stw r3,_thread_dispatch_disable_level@l(r15) + addi sp,r14,0 + bne easy_exit + + lis r4,_context_switch_want@ha + lwz r5,_context_switch_want@l(r4) + cmpwi r5,0 + beq easy_exit + +switch: + bl __thread_dispatch + +easy_exit: + lwz r4,CR_OFFSET(sp) + mtcr r4 + lwz r4,LR_OFFSET(sp) + mtlr r4 + lwz r4,CTR_OFFSET(sp) + mtctr r4 + lwz r4,XER_OFFSET(sp) + mtxer r4 + + EXCEPTION_EPILOG + + mfmsr r4 + rlwinm r4,r4,0,31,29 + mtmsr r4 + isync + + lwz r0,GPR0_OFFSET(sp) + lwz toc,GPR2_OFFSET(sp) + + lwz r4,SRR0_OFFSET(sp) + mtsrr0 r4 + lwz r4,SRR1_OFFSET(sp) + rlwinm r4, r4, 0, 19, 17 + mtsrr1 r4 + + lwz r4,GPR4_OFFSET(sp) + lwz r3,GPR3_OFFSET(sp) + addi sp,sp,EXCEPTION_FRAME_END + rfi diff --git a/wii/libogc/libogc/isfs.c b/wii/libogc/libogc/isfs.c new file mode 100644 index 0000000000..d7e768f7bf --- /dev/null +++ b/wii/libogc/libogc/isfs.c @@ -0,0 +1,844 @@ +/*------------------------------------------------------------- + +es.c -- ETicket services + +Copyright (C) 2008 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) +Hector Martin (marcan) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#if defined(HW_RVL) + +#include +#include +#include +#include +#include +#include +#include + +#include "isfs.h" + +#define ISFS_STRUCTSIZE (sizeof(struct isfs_cb)) +#define ISFS_HEAPSIZE (ISFS_STRUCTSIZE<<4) + +#define ISFS_FUNCNULL 0 +#define ISFS_FUNCGETSTAT 1 +#define ISFS_FUNCREADDIR 2 +#define ISFS_FUNCGETATTR 3 +#define ISFS_FUNCGETUSAGE 4 + +#define ISFS_IOCTL_FORMAT 1 +#define ISFS_IOCTL_GETSTATS 2 +#define ISFS_IOCTL_CREATEDIR 3 +#define ISFS_IOCTL_READDIR 4 +#define ISFS_IOCTL_SETATTR 5 +#define ISFS_IOCTL_GETATTR 6 +#define ISFS_IOCTL_DELETE 7 +#define ISFS_IOCTL_RENAME 8 +#define ISFS_IOCTL_CREATEFILE 9 +#define ISFS_IOCTL_SETFILEVERCTRL 10 +#define ISFS_IOCTL_GETFILESTATS 11 +#define ISFS_IOCTL_GETUSAGE 12 +#define ISFS_IOCTL_SHUTDOWN 13 + +struct isfs_cb +{ + char filepath[ISFS_MAXPATH]; + union { + struct { + char filepathOld[ISFS_MAXPATH]; + char filepathNew[ISFS_MAXPATH]; + } fsrename; + struct { + u32 owner_id; + u16 group_id; + char filepath[ISFS_MAXPATH]; + u8 ownerperm; + u8 groupperm; + u8 otherperm; + u8 attributes; + u8 pad0[2]; + } fsattr; + struct { + ioctlv vector[4]; + u32 no_entries; + } fsreaddir; + struct { + ioctlv vector[4]; + u32 usage1; + u8 pad0[28]; + u32 usage2; + } fsusage; + struct { + u32 a; + u32 b; + u32 c; + u32 d; + u32 e; + u32 f; + u32 g; + } fsstats; + }; + + isfscallback cb; + void *usrdata; + u32 functype; + void *funcargv[8]; +}; + + +static s32 hId = -1; +static s32 _fs_fd = -1; +static char _dev_fs[] ATTRIBUTE_ALIGN(32) = "/dev/fs"; + +static s32 __isfsGetStatsCB(s32 result,void *usrdata) +{ + struct isfs_cb *param = (struct isfs_cb*)usrdata; + if(result==0) memcpy(param->funcargv[0],¶m->fsstats,sizeof(param->fsstats)); + return result; +} + +static s32 __isfsGetAttrCB(s32 result,void *usrdata) +{ + struct isfs_cb *param = (struct isfs_cb*)usrdata; + if(result==0) { + *(u32*)(param->funcargv[0]) = param->fsattr.owner_id; + *(u16*)(param->funcargv[1]) = param->fsattr.group_id; + *(u8*)(param->funcargv[2]) = param->fsattr.attributes; + *(u8*)(param->funcargv[3]) = param->fsattr.ownerperm; + *(u8*)(param->funcargv[4]) = param->fsattr.groupperm; + *(u8*)(param->funcargv[5]) = param->fsattr.otherperm; + } + return result; +} + +static s32 __isfsGetUsageCB(s32 result,void *usrdata) +{ + struct isfs_cb *param = (struct isfs_cb*)usrdata; + if(result==0) { + *(u32*)(param->funcargv[0]) = param->fsusage.usage1; + *(u32*)(param->funcargv[1]) = param->fsusage.usage2; + } + return result; + +} +static s32 __isfsReadDirCB(s32 result,void *usrdata) +{ + struct isfs_cb *param = (struct isfs_cb*)usrdata; + if(result==0) *(u32*)(param->funcargv[0]) = param->fsreaddir.no_entries; + return result; +} + +static s32 __isfsFunctionCB(s32 result,void *usrdata) +{ + struct isfs_cb *param = (struct isfs_cb*)usrdata; + + if(result>=0) { + if(param->functype==ISFS_FUNCGETSTAT) __isfsGetStatsCB(result,usrdata); + else if(param->functype==ISFS_FUNCREADDIR) __isfsReadDirCB(result,usrdata); + else if(param->functype==ISFS_FUNCGETATTR) __isfsGetAttrCB(result,usrdata); + else if(param->functype==ISFS_FUNCGETUSAGE) __isfsGetUsageCB(result,usrdata); + } + if(param->cb!=NULL) param->cb(result,param->usrdata); + + iosFree(hId,param); + return result; +} + +s32 ISFS_Initialize() +{ + s32 ret = IPC_OK; + + if(_fs_fd<0) { + _fs_fd = IOS_Open(_dev_fs,0); + if(_fs_fd<0) return _fs_fd; + } + + if(hId<0) { + hId = iosCreateHeap(ISFS_HEAPSIZE); + if(hId<0) return IPC_ENOMEM; + } + return ret; +} + +s32 ISFS_Deinitialize() +{ + if(_fs_fd<0) return ISFS_EINVAL; + + IOS_Close(_fs_fd); + _fs_fd = -1; + + return 0; +} + +s32 ISFS_ReadDir(const char *filepath,char *name_list,u32 *num) +{ + s32 ret; + s32 len,cnt; + s32 ilen,olen; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL || num==NULL) return ISFS_EINVAL; + if(name_list!=NULL && ((u32)name_list%32)!=0) return ISFS_EINVAL; + + len = strnlen(filepath,ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + memcpy(param->filepath,filepath,(len+1)); + + param->fsreaddir.vector[0].data = param->filepath; + param->fsreaddir.vector[0].len = ISFS_MAXPATH; + param->fsreaddir.vector[1].data = ¶m->fsreaddir.no_entries; + param->fsreaddir.vector[1].len = sizeof(u32); + + if(name_list!=NULL) { + ilen = olen = 2; + cnt = *num; + param->fsreaddir.no_entries = cnt; + param->fsreaddir.vector[2].data = name_list; + param->fsreaddir.vector[2].len = (cnt*13); + param->fsreaddir.vector[3].data = ¶m->fsreaddir.no_entries; + param->fsreaddir.vector[3].len = sizeof(u32); + } else + ilen = olen = 1; + + ret = IOS_Ioctlv(_fs_fd,ISFS_IOCTL_READDIR,ilen,olen,param->fsreaddir.vector); + if(ret==0) *num = param->fsreaddir.no_entries; + + if(param!=NULL) iosFree(hId,param); + return ret; +} + +s32 ISFS_ReadDirAsync(const char *filepath,char *name_list,u32 *num,isfscallback cb,void *usrdata) +{ + s32 len,cnt; + s32 ilen,olen; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL || num==NULL) return ISFS_EINVAL; + if(name_list!=NULL && ((u32)name_list%32)!=0) return ISFS_EINVAL; + + len = strnlen(filepath,ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->funcargv[0] = num; + param->functype = ISFS_FUNCREADDIR; + memcpy(param->filepath,filepath,(len+1)); + + param->fsreaddir.vector[0].data = param->filepath; + param->fsreaddir.vector[0].len = ISFS_MAXPATH; + param->fsreaddir.vector[1].data = ¶m->fsreaddir.no_entries; + param->fsreaddir.vector[1].len = sizeof(u32); + + if(name_list!=NULL) { + ilen = olen = 2; + cnt = *num; + param->fsreaddir.no_entries = cnt; + param->fsreaddir.vector[2].data = name_list; + param->fsreaddir.vector[2].len = (cnt*13); + param->fsreaddir.vector[3].data = ¶m->fsreaddir.no_entries; + param->fsreaddir.vector[3].len = sizeof(u32); + } else + ilen = olen = 1; + + return IOS_IoctlvAsync(_fs_fd,ISFS_IOCTL_READDIR,ilen,olen,param->fsreaddir.vector,__isfsFunctionCB,param); +} + +s32 ISFS_CreateDir(const char *filepath,u8 attributes,u8 owner_perm,u8 group_perm,u8 other_perm) +{ + s32 ret; + s32 len; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL) return ISFS_EINVAL; + + len = strnlen(filepath,ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + memcpy(param->fsattr.filepath ,filepath,(len+1)); + + param->fsattr.attributes = attributes; + param->fsattr.ownerperm = owner_perm; + param->fsattr.groupperm = group_perm; + param->fsattr.otherperm = other_perm; + ret = IOS_Ioctl(_fs_fd,ISFS_IOCTL_CREATEDIR,¶m->fsattr,sizeof(param->fsattr),NULL,0); + + if(param!=NULL) iosFree(hId,param); + return ret; +} + +s32 ISFS_CreateDirAsync(const char *filepath,u8 attributes,u8 owner_perm,u8 group_perm,u8 other_perm,isfscallback cb,void *usrdata) +{ + s32 len; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL) return ISFS_EINVAL; + + len = strnlen(filepath,ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->functype = ISFS_FUNCNULL; + memcpy(param->fsattr.filepath,filepath,(len+1)); + + param->fsattr.attributes = attributes; + param->fsattr.ownerperm = owner_perm; + param->fsattr.groupperm = group_perm; + param->fsattr.otherperm = other_perm; + return IOS_IoctlAsync(_fs_fd,ISFS_IOCTL_CREATEDIR,¶m->fsattr,sizeof(param->fsattr),NULL,0,__isfsFunctionCB,param); +} + +s32 ISFS_Open(const char *filepath,u8 mode) +{ + s32 ret; + s32 len; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL) return ISFS_EINVAL; + + len = strnlen(filepath,ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + memcpy(param->filepath,filepath,(len+1)); + ret = IOS_Open(param->filepath,mode); + + if(param!=NULL) iosFree(hId,param); + return ret; +} + +s32 ISFS_OpenAsync(const char *filepath,u8 mode,isfscallback cb,void *usrdata) +{ + s32 len; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL) return ISFS_EINVAL; + + len = strnlen(filepath,ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->functype = ISFS_FUNCNULL; + memcpy(param->filepath,filepath,(len+1)); + return IOS_OpenAsync(param->filepath,mode,__isfsFunctionCB,param); +} + +s32 ISFS_Format() +{ + if(_fs_fd<0) return ISFS_EINVAL; + + return IOS_Ioctl(_fs_fd,ISFS_IOCTL_FORMAT,NULL,0,NULL,0); +} + +s32 ISFS_FormatAsync(isfscallback cb,void *usrdata) +{ + struct isfs_cb *param; + + if(_fs_fd<0) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->functype = ISFS_FUNCNULL; + return IOS_IoctlAsync(_fs_fd,ISFS_IOCTL_FORMAT,NULL,0,NULL,0,__isfsFunctionCB,param); +} + +s32 ISFS_GetStats(void *stats) +{ + s32 ret = ISFS_OK; + struct isfs_cb *param; + + if(_fs_fd<0 || stats==NULL) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + ret = IOS_Ioctl(_fs_fd,ISFS_IOCTL_GETSTATS,NULL,0,¶m->fsstats,sizeof(param->fsstats)); + if(ret==IPC_OK) memcpy(stats,¶m->fsstats,sizeof(param->fsstats)); + + if(param!=NULL) iosFree(hId,param); + return ret; +} + +s32 ISFS_GetStatsAsync(void *stats,isfscallback cb,void *usrdata) +{ + struct isfs_cb *param; + + if(_fs_fd<0 || stats==NULL) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->functype = ISFS_FUNCGETSTAT; + param->funcargv[0] = stats; + return IOS_IoctlAsync(_fs_fd,ISFS_IOCTL_GETSTATS,NULL,0,¶m->fsstats,sizeof(param->fsstats),__isfsFunctionCB,param); +} + +s32 ISFS_Write(s32 fd,const void *buffer,u32 length) +{ + if(length<=0 || buffer==NULL) return ISFS_EINVAL; + + return IOS_Write(fd,buffer,length); +} + +s32 ISFS_WriteAsync(s32 fd,const void *buffer,u32 length,isfscallback cb,void *usrdata) +{ + struct isfs_cb *param; + + if(length<=0 || buffer==NULL) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->functype = ISFS_FUNCNULL; + return IOS_WriteAsync(fd,buffer,length,__isfsFunctionCB,param); +} + +s32 ISFS_Read(s32 fd,void *buffer,u32 length) +{ + if(length<=0 || buffer==NULL || ((u32)buffer%32)!=0) return ISFS_EINVAL; + + return IOS_Read(fd,buffer,length); +} + +s32 ISFS_ReadAsync(s32 fd,void *buffer,u32 length,isfscallback cb,void *usrdata) +{ + struct isfs_cb *param; + + if(length<=0 || buffer==NULL || ((u32)buffer%32)!=0) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->functype = ISFS_FUNCNULL; + return IOS_ReadAsync(fd,buffer,length,__isfsFunctionCB,param); +} + +s32 ISFS_Seek(s32 fd,s32 where,s32 whence) +{ + return IOS_Seek(fd,where,whence); +} + +s32 ISFS_SeekAsync(s32 fd,s32 where,s32 whence,isfscallback cb,void *usrdata) +{ + struct isfs_cb *param; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->functype = ISFS_FUNCNULL; + return IOS_SeekAsync(fd,where,whence,__isfsFunctionCB,param); +} + +s32 ISFS_CreateFile(const char *filepath,u8 attributes,u8 owner_perm,u8 group_perm,u8 other_perm) +{ + s32 ret; + s32 len; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL) return ISFS_EINVAL; + + len = strnlen(filepath,ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + memcpy(param->fsattr.filepath,filepath,(len+1)); + + param->fsattr.attributes = attributes; + param->fsattr.ownerperm = owner_perm; + param->fsattr.groupperm = group_perm; + param->fsattr.otherperm = other_perm; + ret = IOS_Ioctl(_fs_fd,ISFS_IOCTL_CREATEFILE,¶m->fsattr,sizeof(param->fsattr),NULL,0); + + if(param!=NULL) iosFree(hId,param); + return ret; +} + +s32 ISFS_CreateFileAsync(const char *filepath,u8 attributes,u8 owner_perm,u8 group_perm,u8 other_perm,isfscallback cb,void *usrdata) +{ + s32 len; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL) return ISFS_EINVAL; + + len = strnlen(filepath,ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->functype = ISFS_FUNCNULL; + memcpy(param->fsattr.filepath,filepath,(len+1)); + + param->fsattr.attributes = attributes; + param->fsattr.ownerperm = owner_perm; + param->fsattr.groupperm = group_perm; + param->fsattr.otherperm = other_perm; + return IOS_IoctlAsync(_fs_fd,ISFS_IOCTL_CREATEFILE,¶m->fsattr,sizeof(param->fsattr),NULL,0,__isfsFunctionCB,param); +} + +s32 ISFS_Delete(const char *filepath) +{ + s32 ret; + s32 len; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL) return ISFS_EINVAL; + + len = strnlen(filepath,ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + memcpy(param->filepath,filepath,(len+1)); + ret = IOS_Ioctl(_fs_fd,ISFS_IOCTL_DELETE,param->filepath,ISFS_MAXPATH,NULL,0); + + if(param!=NULL) iosFree(hId,param); + return ret; +} + +s32 ISFS_DeleteAsync(const char *filepath,isfscallback cb,void *usrdata) +{ + s32 len; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL) return ISFS_EINVAL; + + len = strnlen(filepath,ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->functype = ISFS_FUNCNULL; + memcpy(param->filepath,filepath,(len+1)); + return IOS_IoctlAsync(_fs_fd,ISFS_IOCTL_DELETE,param->filepath,ISFS_MAXPATH,NULL,0,__isfsFunctionCB,param); +} + +s32 ISFS_Close(s32 fd) +{ + if(fd<0) return 0; + + return IOS_Close(fd); +} + +s32 ISFS_CloseAsync(s32 fd,isfscallback cb,void *usrdata) +{ + struct isfs_cb *param; + + if(fd<0) return 0; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->functype = ISFS_FUNCNULL; + return IOS_CloseAsync(fd,__isfsFunctionCB,param); +} + +s32 ISFS_GetFileStats(s32 fd,fstats *status) +{ + if(status==NULL || ((u32)status%32)!=0) return ISFS_EINVAL; + + return IOS_Ioctl(fd,ISFS_IOCTL_GETFILESTATS,NULL,0,status,sizeof(fstats)); +} + +s32 ISFS_GetFileStatsAsync(s32 fd,fstats *status,isfscallback cb,void *usrdata) +{ + struct isfs_cb *param; + + if(status==NULL || ((u32)status%32)!=0) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->functype = ISFS_FUNCNULL; + return IOS_IoctlAsync(fd,ISFS_IOCTL_GETFILESTATS,NULL,0,status,sizeof(fstats),__isfsFunctionCB,param); +} + +s32 ISFS_GetAttr(const char *filepath,u32 *ownerID,u16 *groupID,u8 *attributes,u8 *ownerperm,u8 *groupperm,u8 *otherperm) +{ + s32 ret; + s32 len; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL || ownerID==NULL || groupID==NULL + || attributes==NULL || ownerperm==NULL || groupperm==NULL || otherperm==NULL) return ISFS_EINVAL; + + len = strnlen(filepath,ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + memcpy(param->filepath,filepath,(len+1)); + ret = IOS_Ioctl(_fs_fd,ISFS_IOCTL_GETATTR,param->filepath,ISFS_MAXPATH,¶m->fsattr,sizeof(param->fsattr)); + if(ret==IPC_OK) { + *ownerID = param->fsattr.owner_id; + *groupID = param->fsattr.group_id; + *ownerperm = param->fsattr.ownerperm; + *groupperm = param->fsattr.groupperm; + *otherperm = param->fsattr.otherperm; + *attributes = param->fsattr.attributes; + } + + if(param!=NULL) iosFree(hId,param); + return ret; +} + +s32 ISFS_GetAttrAsync(const char *filepath,u32 *ownerID,u16 *groupID,u8 *attributes,u8 *ownerperm,u8 *groupperm,u8 *otherperm,isfscallback cb,void *usrdata) +{ + s32 len; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL || ownerID==NULL || groupID==NULL + || attributes==NULL || ownerperm==NULL || groupperm==NULL || otherperm==NULL) return ISFS_EINVAL; + + len = strnlen(filepath,ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->functype = ISFS_FUNCGETATTR; + param->funcargv[0] = ownerID; + param->funcargv[1] = groupID; + param->funcargv[2] = attributes; + param->funcargv[3] = ownerperm; + param->funcargv[4] = groupperm; + param->funcargv[5] = otherperm; + memcpy(param->filepath,filepath,(len+1)); + return IOS_IoctlAsync(_fs_fd,ISFS_IOCTL_GETATTR,param->filepath,ISFS_MAXPATH,¶m->fsattr,sizeof(param->fsattr),__isfsFunctionCB,param); +} + +s32 ISFS_Rename(const char *filepathOld,const char *filepathNew) +{ + s32 ret; + s32 len0,len1; + struct isfs_cb *param; + + if(_fs_fd<0 || filepathOld==NULL || filepathNew==NULL) return ISFS_EINVAL; + + len0 = strnlen(filepathOld,ISFS_MAXPATH); + if(len0>=ISFS_MAXPATH) return ISFS_EINVAL; + + len1 = strnlen(filepathNew,ISFS_MAXPATH); + if(len1>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + memcpy(param->fsrename.filepathOld,filepathOld,(len0+1)); + memcpy(param->fsrename.filepathNew,filepathNew,(len1+1)); + ret = IOS_Ioctl(_fs_fd,ISFS_IOCTL_RENAME,¶m->fsrename,sizeof(param->fsrename),NULL,0); + + if(param!=NULL) iosFree(hId,param); + return ret; +} + +s32 ISFS_RenameAsync(const char *filepathOld,const char *filepathNew,isfscallback cb,void *usrdata) +{ + s32 len0,len1; + struct isfs_cb *param; + + if(_fs_fd<0 || filepathOld==NULL || filepathNew==NULL) return ISFS_EINVAL; + + len0 = strnlen(filepathOld,ISFS_MAXPATH); + if(len0>=ISFS_MAXPATH) return ISFS_EINVAL; + + len1 = strnlen(filepathNew,ISFS_MAXPATH); + if(len1>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId,ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->functype = ISFS_FUNCNULL; + memcpy(param->fsrename.filepathOld,filepathOld,(len0+1)); + memcpy(param->fsrename.filepathNew,filepathNew,(len1+1)); + return IOS_IoctlAsync(_fs_fd,ISFS_IOCTL_RENAME,¶m->fsrename,sizeof(param->fsrename),NULL,0,__isfsFunctionCB,param); +} + +s32 ISFS_SetAttr(const char *filepath,u32 ownerID,u16 groupID,u8 attributes,u8 ownerperm,u8 groupperm,u8 otherperm) +{ + s32 ret, len; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL) return ISFS_EINVAL; + + len = strnlen(filepath, ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId, ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + memcpy(param->fsattr.filepath, filepath, (len+1)); + param->fsattr.owner_id = ownerID; + param->fsattr.group_id = groupID; + param->fsattr.ownerperm = ownerperm; + param->fsattr.groupperm = groupperm; + param->fsattr.otherperm = otherperm; + param->fsattr.attributes = attributes; + + ret = IOS_Ioctl(_fs_fd,ISFS_IOCTL_SETATTR,¶m->fsattr,sizeof(param->fsattr),NULL,0); + + if(param!=NULL) iosFree(hId,param); + return ret; +} + +s32 ISFS_SetAttrAsync(const char *filepath,u32 ownerID,u16 groupID,u8 attributes,u8 ownerperm,u8 groupperm,u8 otherperm,isfscallback cb,void *usrdata) +{ + s32 len; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL) return ISFS_EINVAL; + + len = strnlen(filepath, ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId, ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->functype = ISFS_FUNCNULL; + memcpy(param->fsattr.filepath, filepath, (len+1)); + param->fsattr.owner_id = ownerID; + param->fsattr.group_id = groupID; + param->fsattr.ownerperm = ownerperm; + param->fsattr.groupperm = groupperm; + param->fsattr.otherperm = otherperm; + param->fsattr.attributes = attributes; + return IOS_IoctlAsync(_fs_fd,ISFS_IOCTL_SETATTR,¶m->fsattr,sizeof(param->fsattr),NULL,0,__isfsFunctionCB,param); +} + +s32 ISFS_GetUsage(const char* filepath, u32* usage1, u32* usage2) +{ + s32 ret,len; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL || usage1==NULL || usage2 == NULL) + return ISFS_EINVAL; + + len = strnlen(filepath, ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId, ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + memcpy(param->filepath,filepath,(len+1)); + + param->fsusage.vector[0].data = param->filepath; + param->fsusage.vector[0].len = ISFS_MAXPATH; + param->fsusage.vector[1].data = ¶m->fsusage.usage1; + param->fsusage.vector[1].len = sizeof(u32); + param->fsusage.vector[2].data = ¶m->fsusage.usage2; + param->fsusage.vector[2].len = sizeof(u32); + ret = IOS_Ioctlv(_fs_fd,ISFS_IOCTL_GETUSAGE,1,2,param->fsusage.vector); + if(ret==IPC_OK) { + *usage1 = param->fsusage.usage1; + *usage2 = param->fsusage.usage2; + } + + if(param!=NULL) iosFree(hId, param); + return ret; +} + +s32 ISFS_GetUsageAsync(const char* filepath, u32* usage1, u32* usage2,isfscallback cb,void *usrdata) +{ + s32 len; + struct isfs_cb *param; + + if(_fs_fd<0 || filepath==NULL || usage1==NULL || usage2 == NULL) + return ISFS_EINVAL; + + len = strnlen(filepath, ISFS_MAXPATH); + if(len>=ISFS_MAXPATH) return ISFS_EINVAL; + + param = (struct isfs_cb*)iosAlloc(hId, ISFS_STRUCTSIZE); + if(param==NULL) return ISFS_ENOMEM; + + param->cb = cb; + param->usrdata = usrdata; + param->functype = ISFS_FUNCGETUSAGE; + param->funcargv[0] = usage1; + param->funcargv[1] = usage2; + memcpy(param->filepath,filepath,(len+1)); + + param->fsusage.vector[0].data = param->filepath; + param->fsusage.vector[0].len = ISFS_MAXPATH; + param->fsusage.vector[1].data = ¶m->fsusage.usage1; + param->fsusage.vector[1].len = sizeof(u32); + param->fsusage.vector[2].data = ¶m->fsusage.usage2; + param->fsusage.vector[2].len = sizeof(u32); + return IOS_IoctlvAsync(_fs_fd,ISFS_IOCTL_GETUSAGE,1,2,param->fsusage.vector,__isfsFunctionCB,param); +} + + +#endif /* defined(HW_RVL) */ diff --git a/wii/libogc/libogc/kprintf.c b/wii/libogc/libogc/kprintf.c new file mode 100644 index 0000000000..976a756704 --- /dev/null +++ b/wii/libogc/libogc/kprintf.c @@ -0,0 +1,291 @@ +/* + * linux/lib/vsprintf.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ +/* + * Wirzenius wrote this portably, Torvalds fucked it up :-) + */ + +#include +#include +#include + +/* we use this so that we can do without the ctype library */ +#define is_digit(c) ((c) >= '0' && (c) <= '9') + +static int skip_atoi(const char **s) +{ + int i=0; + + while (is_digit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +#define do_div(n,base) ({ \ +int __res; \ +__res = ((unsigned long) n) % (unsigned) base; \ +n = ((unsigned long) n) / (unsigned) base; \ +__res; }) + +static char * number(char * str, long num, int base, int size, int precision + ,int type) +{ + char c,sign,tmp[66]; + const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + + if (type & LARGE) + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return 0; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) + while(size-->0) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type & SPECIAL) { + if (base==8) + *str++ = '0'; + else if (base==16) { + *str++ = '0'; + *str++ = digits[33]; + } + } + if (!(type & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + return str; +} + +int kvsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + unsigned long num; + int i, base; + char * str; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + + for (str=buf ; *fmt ; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (is_digit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (is_digit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + if (!s) + s = ""; + + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + *str++ = '%'; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + if (qualifier == 'l') + num = va_arg(args, unsigned long); + else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & SIGN) + num = (short) num; + } else if (flags & SIGN) + num = va_arg(args, int); + else + num = va_arg(args, unsigned int); + str = number(str, num, base, field_width, precision, flags); + } + *str = '\0'; + return str-buf; +} + +#define __DOUTBUFSIZE 256 + +char __outstr[__DOUTBUFSIZE]; + +//--------------------------------------------------------------------------------- +void kprintf(const char *str, ...) +//--------------------------------------------------------------------------------- +{ + + int len; + + va_list args; + + va_start(args, str); + len=kvsprintf(__outstr,str,args); + va_end(args); + + write(2, __outstr, len); +} diff --git a/wii/libogc/libogc/lock_supp.c b/wii/libogc/libogc/lock_supp.c new file mode 100644 index 0000000000..6a30c37bf0 --- /dev/null +++ b/wii/libogc/libogc/lock_supp.c @@ -0,0 +1,64 @@ +#include <_ansi.h> +#include <_syslist.h> +#include +#include +#include +#ifndef REENTRANT_SYSCALLS_PROVIDED +#include +#endif +#include + +#include "asm.h" +#include "processor.h" +#include "mutex.h" + + +int __libogc_lock_init(int *lock,int recursive) +{ + s32 ret; + mutex_t retlck = LWP_MUTEX_NULL; + + if(!lock) return -1; + + *lock = 0; + ret = LWP_MutexInit(&retlck,(recursive?TRUE:FALSE)); + if(ret==0) *lock = (int)retlck; + + return ret; +} + +int __libogc_lock_close(int *lock) +{ + s32 ret; + mutex_t plock; + + if(!lock || *lock==0) return -1; + + plock = (mutex_t)*lock; + ret = LWP_MutexDestroy(plock); + if(ret==0) *lock = 0; + + return ret; +} + +int __libogc_lock_acquire(int *lock) +{ + mutex_t plock; + + if(!lock || *lock==0) return -1; + + plock = (mutex_t)*lock; + return LWP_MutexLock(plock); +} + + +int __libogc_lock_release(int *lock) +{ + mutex_t plock; + + if(!lock || *lock==0) return -1; + + plock = (mutex_t)*lock; + return LWP_MutexUnlock(plock); +} + diff --git a/wii/libogc/libogc/lwp.c b/wii/libogc/libogc/lwp.c new file mode 100644 index 0000000000..5927ebb638 --- /dev/null +++ b/wii/libogc/libogc/lwp.c @@ -0,0 +1,415 @@ +/*------------------------------------------------------------- + +lwp.c -- Thread subsystem I + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#include +#include +#include "asm.h" +#include "processor.h" +#include "lwp_threadq.h" +#include "lwp_threads.h" +#include "lwp_wkspace.h" +#include "lwp_objmgr.h" +#include "lwp_config.h" +#include "lwp.h" + +#define LWP_OBJTYPE_THREAD 1 +#define LWP_OBJTYPE_TQUEUE 2 + +#define LWP_CHECK_THREAD(hndl) \ +{ \ + if(((hndl)==LWP_THREAD_NULL) || (LWP_OBJTYPE(hndl)!=LWP_OBJTYPE_THREAD)) \ + return NULL; \ +} + +#define LWP_CHECK_TQUEUE(hndl) \ +{ \ + if(((hndl)==LWP_TQUEUE_NULL) || (LWP_OBJTYPE(hndl)!=LWP_OBJTYPE_TQUEUE)) \ + return NULL; \ +} + +typedef struct _tqueue_st { + lwp_obj object; + lwp_thrqueue tqueue; +} tqueue_st; + +lwp_objinfo _lwp_thr_objects; +lwp_objinfo _lwp_tqueue_objects; + +extern int __crtmain(); + +extern u8 __stack_addr[],__stack_end[]; + +static __inline__ u32 __lwp_priotocore(u32 prio) +{ + return (255 - prio); +} + +static __inline__ lwp_cntrl* __lwp_cntrl_open(lwp_t thr_id) +{ + LWP_CHECK_THREAD(thr_id); + return (lwp_cntrl*)__lwp_objmgr_get(&_lwp_thr_objects,LWP_OBJMASKID(thr_id)); +} + +static __inline__ tqueue_st* __lwp_tqueue_open(lwpq_t tqueue) +{ + LWP_CHECK_TQUEUE(tqueue); + return (tqueue_st*)__lwp_objmgr_get(&_lwp_tqueue_objects,LWP_OBJMASKID(tqueue)); +} + +static lwp_cntrl* __lwp_cntrl_allocate() +{ + lwp_cntrl *thethread; + + __lwp_thread_dispatchdisable(); + thethread = (lwp_cntrl*)__lwp_objmgr_allocate(&_lwp_thr_objects); + if(thethread) { + __lwp_objmgr_open(&_lwp_thr_objects,&thethread->object); + return thethread; + } + __lwp_thread_dispatchenable(); + return NULL; +} + +static tqueue_st* __lwp_tqueue_allocate() +{ + tqueue_st *tqueue; + + __lwp_thread_dispatchdisable(); + tqueue = (tqueue_st*)__lwp_objmgr_allocate(&_lwp_tqueue_objects); + if(tqueue) { + __lwp_objmgr_open(&_lwp_tqueue_objects,&tqueue->object); + return tqueue; + } + __lwp_thread_dispatchenable(); + return NULL; +} + +static __inline__ void __lwp_cntrl_free(lwp_cntrl *thethread) +{ + __lwp_objmgr_close(&_lwp_thr_objects,&thethread->object); + __lwp_objmgr_free(&_lwp_thr_objects,&thethread->object); +} + +static __inline__ void __lwp_tqueue_free(tqueue_st *tq) +{ + __lwp_objmgr_close(&_lwp_tqueue_objects,&tq->object); + __lwp_objmgr_free(&_lwp_tqueue_objects,&tq->object); +} + +static void* idle_func(void *arg) +{ + while(1); + return 0; +} + +void __lwp_sysinit() +{ + __lwp_objmgr_initinfo(&_lwp_thr_objects,LWP_MAX_THREADS,sizeof(lwp_cntrl)); + __lwp_objmgr_initinfo(&_lwp_tqueue_objects,LWP_MAX_TQUEUES,sizeof(tqueue_st)); + + // create idle thread, is needed iff all threads are locked on a queue + _thr_idle = (lwp_cntrl*)__lwp_objmgr_allocate(&_lwp_thr_objects); + __lwp_thread_init(_thr_idle,NULL,0,255,0,TRUE); + _thr_executing = _thr_heir = _thr_idle; + __lwp_thread_start(_thr_idle,idle_func,NULL); + __lwp_objmgr_open(&_lwp_thr_objects,&_thr_idle->object); + + // create main thread, as this is our entry point + // for every GC application. + _thr_main = (lwp_cntrl*)__lwp_objmgr_allocate(&_lwp_thr_objects); + __lwp_thread_init(_thr_main,__stack_end,((u32)__stack_addr-(u32)__stack_end),191,0,TRUE); + __lwp_thread_start(_thr_main,(void*)__crtmain,NULL); + __lwp_objmgr_open(&_lwp_thr_objects,&_thr_main->object); +} + +BOOL __lwp_thread_isalive(lwp_t thr_id) +{ + if(thr_id==LWP_THREAD_NULL || LWP_OBJTYPE(thr_id)!=LWP_OBJTYPE_THREAD) return FALSE; + + lwp_cntrl *thethread = (lwp_cntrl*)__lwp_objmgr_getnoprotection(&_lwp_thr_objects,LWP_OBJMASKID(thr_id)); + + if(thethread) { + u32 *stackbase = thethread->stack; + if(stackbase[0]==0xDEADBABE && !__lwp_statedormant(thethread->cur_state) && !__lwp_statetransient(thethread->cur_state)) + return TRUE; + } + + return FALSE; +} + +lwp_t __lwp_thread_currentid() +{ + return _thr_executing->object.id; +} + +BOOL __lwp_thread_exists(lwp_t thr_id) +{ + if(thr_id==LWP_THREAD_NULL || LWP_OBJTYPE(thr_id)!=LWP_OBJTYPE_THREAD) return FALSE; + return (__lwp_objmgr_getnoprotection(&_lwp_thr_objects,LWP_OBJMASKID(thr_id))!=NULL); +} + +frame_context* __lwp_thread_context(lwp_t thr_id) +{ + lwp_cntrl *thethread; + frame_context *pctx = NULL; + + LWP_CHECK_THREAD(thr_id); + thethread = (lwp_cntrl*)__lwp_objmgr_getnoprotection(&_lwp_thr_objects,LWP_OBJMASKID(thr_id)); + if(thethread) pctx = &thethread->context; + + return pctx; +} + +s32 LWP_CreateThread(lwp_t *thethread,void* (*entry)(void *),void *arg,void *stackbase,u32 stack_size,u8 prio) +{ + u32 status; + lwp_cntrl *lwp_thread; + + if(!thethread || !entry) return -1; + + lwp_thread = __lwp_cntrl_allocate(); + if(!lwp_thread) return -1; + + status = __lwp_thread_init(lwp_thread,stackbase,stack_size,__lwp_priotocore(prio),0,TRUE); + if(!status) { + __lwp_cntrl_free(lwp_thread); + __lwp_thread_dispatchenable(); + return -1; + } + + status = __lwp_thread_start(lwp_thread,entry,arg); + if(!status) { + __lwp_cntrl_free(lwp_thread); + __lwp_thread_dispatchenable(); + return -1; + } + + *thethread = (lwp_t)(LWP_OBJMASKTYPE(LWP_OBJTYPE_THREAD)|LWP_OBJMASKID(lwp_thread->object.id)); + __lwp_thread_dispatchenable(); + + return 0; +} + +s32 LWP_SuspendThread(lwp_t thethread) +{ + lwp_cntrl *lwp_thread; + + lwp_thread = __lwp_cntrl_open(thethread); + if(!lwp_thread) return -1; + + if(!__lwp_statesuspended(lwp_thread->cur_state)) { + __lwp_thread_suspend(lwp_thread); + __lwp_thread_dispatchenable(); + return LWP_SUCCESSFUL; + } + __lwp_thread_dispatchenable(); + return LWP_ALREADY_SUSPENDED; +} + +s32 LWP_ResumeThread(lwp_t thethread) +{ + lwp_cntrl *lwp_thread; + + lwp_thread = __lwp_cntrl_open(thethread); + if(!lwp_thread) return -1; + + if(__lwp_statesuspended(lwp_thread->cur_state)) { + __lwp_thread_resume(lwp_thread,TRUE); + __lwp_thread_dispatchenable(); + return LWP_SUCCESSFUL; + } + __lwp_thread_dispatchenable(); + return LWP_NOT_SUSPENDED; +} + +lwp_t LWP_GetSelf() +{ + lwp_t ret; + + __lwp_thread_dispatchdisable(); + ret = (lwp_t)(LWP_OBJMASKTYPE(LWP_OBJTYPE_THREAD)|LWP_OBJMASKID(_thr_executing->object.id)); + __lwp_thread_dispatchunnest(); + + return ret; +} + +void LWP_SetThreadPriority(lwp_t thethread,u32 prio) +{ + lwp_cntrl *lwp_thread; + + if(thethread==LWP_THREAD_NULL) thethread = LWP_GetSelf(); + + lwp_thread = __lwp_cntrl_open(thethread); + if(!lwp_thread) return; + + __lwp_thread_changepriority(lwp_thread,__lwp_priotocore(prio),TRUE); + __lwp_thread_dispatchenable(); +} + +void LWP_YieldThread() +{ + __lwp_thread_dispatchdisable(); + __lwp_thread_yield(); + __lwp_thread_dispatchenable(); +} + +void LWP_Reschedule(u32 prio) +{ + __lwp_thread_dispatchdisable(); + __lwp_rotate_readyqueue(prio); + __lwp_thread_dispatchenable(); +} + +BOOL LWP_ThreadIsSuspended(lwp_t thethread) +{ + BOOL state; + lwp_cntrl *lwp_thread; + + lwp_thread = __lwp_cntrl_open(thethread); + if(!lwp_thread) return FALSE; + + state = (__lwp_statesuspended(lwp_thread->cur_state) ? TRUE : FALSE); + + __lwp_thread_dispatchenable(); + return state; +} + + +s32 LWP_JoinThread(lwp_t thethread,void **value_ptr) +{ + u32 level; + void *return_ptr; + lwp_cntrl *exec,*lwp_thread; + + lwp_thread = __lwp_cntrl_open(thethread); + if(!lwp_thread) return 0; + + if(__lwp_thread_isexec(lwp_thread)) { + __lwp_thread_dispatchenable(); + return EDEADLK; //EDEADLK + } + + exec = _thr_executing; + _CPU_ISR_Disable(level); + __lwp_threadqueue_csenter(&lwp_thread->join_list); + exec->wait.ret_code = 0; + exec->wait.ret_arg_1 = NULL; + exec->wait.ret_arg = (void*)&return_ptr; + exec->wait.queue = &lwp_thread->join_list; + exec->wait.id = thethread; + _CPU_ISR_Restore(level); + __lwp_threadqueue_enqueue(&lwp_thread->join_list,LWP_WD_NOTIMEOUT); + __lwp_thread_dispatchenable(); + + if(value_ptr) *value_ptr = return_ptr; + return 0; +} + +s32 LWP_InitQueue(lwpq_t *thequeue) +{ + tqueue_st *tq; + + if(!thequeue) return -1; + + tq = __lwp_tqueue_allocate(); + if(!tq) return -1; + + __lwp_threadqueue_init(&tq->tqueue,LWP_THREADQ_MODEFIFO,LWP_STATES_WAITING_ON_THREADQ,0); + + *thequeue = (lwpq_t)(LWP_OBJMASKTYPE(LWP_OBJTYPE_TQUEUE)|LWP_OBJMASKID(tq->object.id)); + __lwp_thread_dispatchenable(); + + return 0; +} + +void LWP_CloseQueue(lwpq_t thequeue) +{ + lwp_cntrl *thethread; + tqueue_st *tq = (tqueue_st*)thequeue; + + tq = __lwp_tqueue_open(thequeue); + if(!tq) return; + + do { + thethread = __lwp_threadqueue_dequeue(&tq->tqueue); + } while(thethread); + __lwp_thread_dispatchenable(); + + __lwp_tqueue_free(tq); + return; +} + +s32 LWP_ThreadSleep(lwpq_t thequeue) +{ + u32 level; + tqueue_st *tq; + lwp_cntrl *exec = NULL; + + tq = __lwp_tqueue_open(thequeue); + if(!tq) return -1; + + exec = _thr_executing; + _CPU_ISR_Disable(level); + __lwp_threadqueue_csenter(&tq->tqueue); + exec->wait.ret_code = 0; + exec->wait.ret_arg = NULL; + exec->wait.ret_arg_1 = NULL; + exec->wait.queue = &tq->tqueue; + exec->wait.id = thequeue; + _CPU_ISR_Restore(level); + __lwp_threadqueue_enqueue(&tq->tqueue,LWP_THREADQ_NOTIMEOUT); + __lwp_thread_dispatchenable(); + return 0; +} + +void LWP_ThreadBroadcast(lwpq_t thequeue) +{ + tqueue_st *tq; + lwp_cntrl *thethread; + + tq = __lwp_tqueue_open(thequeue); + if(!tq) return; + + do { + thethread = __lwp_threadqueue_dequeue(&tq->tqueue); + } while(thethread); + __lwp_thread_dispatchenable(); +} + +void LWP_ThreadSignal(lwpq_t thequeue) +{ + tqueue_st *tq; + + tq = __lwp_tqueue_open(thequeue); + if(!tq) return; + + __lwp_threadqueue_dequeue(&tq->tqueue); + __lwp_thread_dispatchenable(); +} diff --git a/wii/libogc/libogc/lwp_handler.S b/wii/libogc/libogc/lwp_handler.S new file mode 100644 index 0000000000..edb8e633cd --- /dev/null +++ b/wii/libogc/libogc/lwp_handler.S @@ -0,0 +1,565 @@ +#include + + .set FP_SIZE, 8 + + .set GP_SRR0, (SRR0_OFFSET - 8) + .set GP_SRR1, (GP_SRR0 + 4) + + .set GP_1, (GPR1_OFFSET - 8) + .set GP_2, (GP_1 + 4) +#ifdef _DEBUG + .set GP_5, (GPR5_OFFSET - 8) + .set GP_6, (GP_5 + 4) +#endif + .set GP_13, (GPR13_OFFSET - 8) + .set GP_14, (GP_13 + 4) + .set GP_15, (GP_14 + 4) + .set GP_16, (GP_15 + 4) + .set GP_17, (GP_16 + 4) + .set GP_18, (GP_17 + 4) + .set GP_19, (GP_18 + 4) + .set GP_20, (GP_19 + 4) + .set GP_21, (GP_20 + 4) + .set GP_22, (GP_21 + 4) + .set GP_23, (GP_22 + 4) + .set GP_24, (GP_23 + 4) + .set GP_25, (GP_24 + 4) + .set GP_26, (GP_25 + 4) + .set GP_27, (GP_26 + 4) + .set GP_28, (GP_27 + 4) + .set GP_29, (GP_28 + 4) + .set GP_30, (GP_29 + 4) + .set GP_31, (GP_30 + 4) + + .set GQ_0, (GP_31 + 4) + .set GQ_1, (GQ_0 + 4) + .set GQ_2, (GQ_1 + 4) + .set GQ_3, (GQ_2 + 4) + .set GQ_4, (GQ_3 + 4) + .set GQ_5, (GQ_4 + 4) + .set GQ_6, (GQ_5 + 4) + .set GQ_7, (GQ_6 + 4) + + .set GP_CR, (GQ_7 + 4) + .set GP_LR, (GP_CR + 4) + .set GP_CTR, (GP_LR + 4) + .set GP_XER, (GP_CTR + 4) + .set GP_MSR, (GP_XER + 4) + .set GP_DAR, (GP_MSR + 4) + + .set STATE, (GP_DAR + 4) + .set MODE, (STATE + 2) + + .set FP_0, (FPR0_OFFSET - 8) + .set FP_1, (FP_0 + FP_SIZE) + .set FP_2, (FP_1 + FP_SIZE) + .set FP_3, (FP_2 + FP_SIZE) + .set FP_4, (FP_3 + FP_SIZE) + .set FP_5, (FP_4 + FP_SIZE) + .set FP_6, (FP_5 + FP_SIZE) + .set FP_7, (FP_6 + FP_SIZE) + .set FP_8, (FP_7 + FP_SIZE) + .set FP_9, (FP_8 + FP_SIZE) + .set FP_10, (FP_9 + FP_SIZE) + .set FP_11, (FP_10 + FP_SIZE) + .set FP_12, (FP_11 + FP_SIZE) + .set FP_13, (FP_12 + FP_SIZE) + .set FP_14, (FP_13 + FP_SIZE) + .set FP_15, (FP_14 + FP_SIZE) + .set FP_16, (FP_15 + FP_SIZE) + .set FP_17, (FP_16 + FP_SIZE) + .set FP_18, (FP_17 + FP_SIZE) + .set FP_19, (FP_18 + FP_SIZE) + .set FP_20, (FP_19 + FP_SIZE) + .set FP_21, (FP_20 + FP_SIZE) + .set FP_22, (FP_21 + FP_SIZE) + .set FP_23, (FP_22 + FP_SIZE) + .set FP_24, (FP_23 + FP_SIZE) + .set FP_25, (FP_24 + FP_SIZE) + .set FP_26, (FP_25 + FP_SIZE) + .set FP_27, (FP_26 + FP_SIZE) + .set FP_28, (FP_27 + FP_SIZE) + .set FP_29, (FP_28 + FP_SIZE) + .set FP_30, (FP_29 + FP_SIZE) + .set FP_31, (FP_30 + FP_SIZE) + .set FP_FPSCR, (FP_31 + FP_SIZE) + .set PSFP_0, (FP_FPSCR + FP_SIZE) + .set PSFP_1, (PSFP_0 + FP_SIZE) + .set PSFP_2, (PSFP_1 + FP_SIZE) + .set PSFP_3, (PSFP_2 + FP_SIZE) + .set PSFP_4, (PSFP_3 + FP_SIZE) + .set PSFP_5, (PSFP_4 + FP_SIZE) + .set PSFP_6, (PSFP_5 + FP_SIZE) + .set PSFP_7, (PSFP_6 + FP_SIZE) + .set PSFP_8, (PSFP_7 + FP_SIZE) + .set PSFP_9, (PSFP_8 + FP_SIZE) + .set PSFP_10, (PSFP_9 + FP_SIZE) + .set PSFP_11, (PSFP_10 + FP_SIZE) + .set PSFP_12, (PSFP_11 + FP_SIZE) + .set PSFP_13, (PSFP_12 + FP_SIZE) + .set PSFP_14, (PSFP_13 + FP_SIZE) + .set PSFP_15, (PSFP_14 + FP_SIZE) + .set PSFP_16, (PSFP_15 + FP_SIZE) + .set PSFP_17, (PSFP_16 + FP_SIZE) + .set PSFP_18, (PSFP_17 + FP_SIZE) + .set PSFP_19, (PSFP_18 + FP_SIZE) + .set PSFP_20, (PSFP_19 + FP_SIZE) + .set PSFP_21, (PSFP_20 + FP_SIZE) + .set PSFP_22, (PSFP_21 + FP_SIZE) + .set PSFP_23, (PSFP_22 + FP_SIZE) + .set PSFP_24, (PSFP_23 + FP_SIZE) + .set PSFP_25, (PSFP_24 + FP_SIZE) + .set PSFP_26, (PSFP_25 + FP_SIZE) + .set PSFP_27, (PSFP_26 + FP_SIZE) + .set PSFP_28, (PSFP_27 + FP_SIZE) + .set PSFP_29, (PSFP_28 + FP_SIZE) + .set PSFP_30, (PSFP_29 + FP_SIZE) + .set PSFP_31, (PSFP_30 + FP_SIZE) + + .align 5 + .globl _cpu_context_save_fp +_cpu_context_save_fp: + lhz r4,STATE(r3) + ori r4,r4,0x0001 + sth r4,STATE(r3) + stfd fr0, FP_0(r3) + stfd fr1, FP_1(r3) + stfd fr2, FP_2(r3) + stfd fr3, FP_3(r3) + stfd fr4, FP_4(r3) + stfd fr5, FP_5(r3) + stfd fr6, FP_6(r3) + stfd fr7, FP_7(r3) + stfd fr8, FP_8(r3) + stfd fr9, FP_9(r3) + stfd fr10, FP_10(r3) + stfd fr11, FP_11(r3) + stfd fr12, FP_12(r3) + stfd fr13, FP_13(r3) + stfd fr14, FP_14(r3) + stfd fr15, FP_15(r3) + stfd fr16, FP_16(r3) + stfd fr17, FP_17(r3) + stfd fr18, FP_18(r3) + stfd fr19, FP_19(r3) + stfd fr20, FP_20(r3) + stfd fr21, FP_21(r3) + stfd fr22, FP_22(r3) + stfd fr23, FP_23(r3) + stfd fr24, FP_24(r3) + stfd fr25, FP_25(r3) + stfd fr26, FP_26(r3) + stfd fr27, FP_27(r3) + stfd fr28, FP_28(r3) + stfd fr29, FP_29(r3) + stfd fr30, FP_30(r3) + stfd fr31, FP_31(r3) + mffs fr0 + stfd fr0, FP_FPSCR(r3) + lfd fr0, FP_0(r3) + mfspr r4,920 + extrwi. r4,r4,1,2 + beq 1f + psq_st fr0,PSFP_0(r3),0,0 + psq_st fr1,PSFP_1(r3),0,0 + psq_st fr2,PSFP_2(r3),0,0 + psq_st fr3,PSFP_3(r3),0,0 + psq_st fr4,PSFP_4(r3),0,0 + psq_st fr5,PSFP_5(r3),0,0 + psq_st fr6,PSFP_6(r3),0,0 + psq_st fr7,PSFP_7(r3),0,0 + psq_st fr8,PSFP_8(r3),0,0 + psq_st fr9,PSFP_9(r3),0,0 + psq_st fr10,PSFP_10(r3),0,0 + psq_st fr11,PSFP_11(r3),0,0 + psq_st fr12,PSFP_12(r3),0,0 + psq_st fr13,PSFP_13(r3),0,0 + psq_st fr14,PSFP_14(r3),0,0 + psq_st fr15,PSFP_15(r3),0,0 + psq_st fr16,PSFP_16(r3),0,0 + psq_st fr17,PSFP_17(r3),0,0 + psq_st fr18,PSFP_18(r3),0,0 + psq_st fr19,PSFP_19(r3),0,0 + psq_st fr20,PSFP_20(r3),0,0 + psq_st fr21,PSFP_21(r3),0,0 + psq_st fr22,PSFP_22(r3),0,0 + psq_st fr23,PSFP_23(r3),0,0 + psq_st fr24,PSFP_24(r3),0,0 + psq_st fr25,PSFP_25(r3),0,0 + psq_st fr26,PSFP_26(r3),0,0 + psq_st fr27,PSFP_27(r3),0,0 + psq_st fr28,PSFP_28(r3),0,0 + psq_st fr29,PSFP_29(r3),0,0 + psq_st fr30,PSFP_30(r3),0,0 + psq_st fr31,PSFP_31(r3),0,0 +1: blr + + .align 5 + .globl _cpu_context_restore_fp +_cpu_context_restore_fp: + lhz r4,STATE(r3) + clrlwi. r4,r4,31 + beq 2f + lfd fr0, FP_FPSCR(r3) + mtfsf 255, fr0 + mfspr r4,920 + extrwi. r4,r4,1,2 + beq 1f + psq_l fr0,PSFP_0(r3),0,0 + psq_l fr1,PSFP_1(r3),0,0 + psq_l fr2,PSFP_2(r3),0,0 + psq_l fr3,PSFP_3(r3),0,0 + psq_l fr4,PSFP_4(r3),0,0 + psq_l fr5,PSFP_5(r3),0,0 + psq_l fr6,PSFP_6(r3),0,0 + psq_l fr7,PSFP_7(r3),0,0 + psq_l fr8,PSFP_8(r3),0,0 + psq_l fr9,PSFP_9(r3),0,0 + psq_l fr10,PSFP_10(r3),0,0 + psq_l fr11,PSFP_11(r3),0,0 + psq_l fr12,PSFP_12(r3),0,0 + psq_l fr13,PSFP_13(r3),0,0 + psq_l fr14,PSFP_14(r3),0,0 + psq_l fr15,PSFP_15(r3),0,0 + psq_l fr16,PSFP_16(r3),0,0 + psq_l fr17,PSFP_17(r3),0,0 + psq_l fr18,PSFP_18(r3),0,0 + psq_l fr19,PSFP_19(r3),0,0 + psq_l fr20,PSFP_20(r3),0,0 + psq_l fr21,PSFP_21(r3),0,0 + psq_l fr22,PSFP_22(r3),0,0 + psq_l fr23,PSFP_23(r3),0,0 + psq_l fr24,PSFP_24(r3),0,0 + psq_l fr25,PSFP_25(r3),0,0 + psq_l fr26,PSFP_26(r3),0,0 + psq_l fr27,PSFP_27(r3),0,0 + psq_l fr28,PSFP_28(r3),0,0 + psq_l fr29,PSFP_29(r3),0,0 + psq_l fr30,PSFP_30(r3),0,0 + psq_l fr31,PSFP_31(r3),0,0 +1: lfd fr0, FP_0(r3) + lfd fr1, FP_1(r3) + lfd fr2, FP_2(r3) + lfd fr3, FP_3(r3) + lfd fr4, FP_4(r3) + lfd fr5, FP_5(r3) + lfd fr6, FP_6(r3) + lfd fr7, FP_7(r3) + lfd fr8, FP_8(r3) + lfd fr9, FP_9(r3) + lfd fr10, FP_10(r3) + lfd fr11, FP_11(r3) + lfd fr12, FP_12(r3) + lfd fr13, FP_13(r3) + lfd fr14, FP_14(r3) + lfd fr15, FP_15(r3) + lfd fr16, FP_16(r3) + lfd fr17, FP_17(r3) + lfd fr18, FP_18(r3) + lfd fr19, FP_19(r3) + lfd fr20, FP_20(r3) + lfd fr21, FP_21(r3) + lfd fr22, FP_22(r3) + lfd fr23, FP_23(r3) + lfd fr24, FP_24(r3) + lfd fr25, FP_25(r3) + lfd fr26, FP_26(r3) + lfd fr27, FP_27(r3) + lfd fr28, FP_28(r3) + lfd fr29, FP_29(r3) + lfd fr30, FP_30(r3) + lfd fr31, FP_31(r3) +2: blr + + .align 5 + .globl _cpu_context_switch +_cpu_context_switch: + sync + isync + + stw sp,GP_1(r3) + lwz sp,GP_1(r4) + stw toc,GP_2(r3) + lwz toc,GP_2(r4) + + stmw r13,GP_13(r3) + lmw r13,GP_13(r4) + + mfspr r5,912 + stw r5,GQ_0(r3) + lwz r6,GQ_0(r4) + mtspr 912,r6 + mfspr r5,913 + stw r5,GQ_1(r3) + lwz r6,GQ_1(r4) + mtspr 913,r6 + mfspr r5,914 + stw r5,GQ_2(r3) + lwz r6,GQ_2(r4) + mtspr 914,r6 + mfspr r5,915 + stw r5,GQ_3(r3) + lwz r6,GQ_3(r4) + mtspr 915,r6 + mfspr r5,916 + stw r5,GQ_4(r3) + lwz r6,GQ_4(r4) + mtspr 916,r6 + mfspr r5,917 + stw r5,GQ_5(r3) + lwz r6,GQ_5(r4) + mtspr 917,r6 + mfspr r5,918 + stw r5,GQ_6(r3) + lwz r6,GQ_6(r4) + mtspr 918,r6 + mfspr r5,919 + stw r5,GQ_7(r3) + lwz r6,GQ_7(r4) + mtspr 919,r6 + + mfcr r5 + stw r5, GP_CR(r3) + lwz r6, GP_CR(r4) + mtcrf 255, r6 + mflr r5 + stw r5, GP_LR(r3) + lwz r6, GP_LR(r4) + mtlr r6 + mfmsr r5 + stw r5, GP_MSR(r3) + lwz r6, GP_MSR(r4) + rlwinm r6, r6, 0, 19, 17 + mtmsr r6 + + blr + + .align 5 + .globl _cpu_context_save +_cpu_context_save: + sync + isync + + stw sp,GPR1_OFFSET-8(r3) + stw toc,GPR2_OFFSET-8(r3) + stmw r13,GPR13_OFFSET-8(r3) + + mfctr r6 + stw r6, CTR_OFFSET-8(r3) + mfcr r6 + stw r6, CR_OFFSET-8(r3) + mflr r7 + stw r7, LR_OFFSET-8(r3) + mfmsr r8 + stw r8, MSR_OFFSET-8(r3) + + mfspr r6,913 + stw r6,GQR1_OFFSET-8(r3) + mfspr r6,914 + stw r6,GQR2_OFFSET-8(r3) + mfspr r6,915 + stw r6,GQR3_OFFSET-8(r3) + mfspr r6,916 + stw r6,GQR4_OFFSET-8(r3) + mfspr r6,917 + stw r6,GQR5_OFFSET-8(r3) + mfspr r6,918 + stw r6,GQR6_OFFSET-8(r3) + mfspr r6,919 + stw r6,GQR7_OFFSET-8(r3) + + blr + + .align 5 + .globl _cpu_context_restore +_cpu_context_restore: + lwz sp,GPR1_OFFSET-8(r3) + lwz toc,GPR2_OFFSET-8(r3) + lmw r13,GPR13_OFFSET-8(r3) + + lwz r6, CTR_OFFSET-8(r3) + mtctr r6 + lwz r6, CR_OFFSET-8(r3) + mtcrf 255, r6 + lwz r7, LR_OFFSET-8(r3) + mtlr r7 + lwz r8, MSR_OFFSET-8(r3) + rlwinm r8,r8,0,19,17 + mtmsr r8 + + lwz r6, GQR1_OFFSET-8(r3) + mtspr 913,r6 + lwz r6, GQR2_OFFSET-8(r3) + mtspr 914,r6 + lwz r6, GQR3_OFFSET-8(r3) + mtspr 915,r6 + lwz r6, GQR4_OFFSET-8(r3) + mtspr 916,r6 + lwz r6, GQR5_OFFSET-8(r3) + mtspr 917,r6 + lwz r6, GQR6_OFFSET-8(r3) + mtspr 918,r6 + lwz r6, GQR7_OFFSET-8(r3) + mtspr 919,r6 + + blr + +#ifdef _DEBUG + .align 5 + .globl _cpu_context_switch_ex +_cpu_context_switch_ex: + sync + isync + + stw sp,GP_1(r3) + lwz sp,GP_1(r4) + stw toc,GP_2(r3) + lwz toc,GP_2(r4) + + stmw r5,GP_5(r3) + + mfspr r5,912 + stw r5,GQ_0(r3) + lwz r5,GQ_0(r4) + mfspr r6,913 + mtspr 912,r5 + stw r6,GQ_1(r3) + lwz r6,GQ_1(r4) + mfspr r5,914 + mtspr 913,r6 + stw r5,GQ_2(r3) + lwz r5,GQ_2(r4) + mfspr r6,915 + mtspr 914,r5 + stw r6,GQ_3(r3) + lwz r6,GQ_3(r4) + mfspr r5,916 + mtspr 915,r6 + stw r5,GQ_4(r3) + lwz r5,GQ_4(r4) + mfspr r6,917 + mtspr 916,r5 + stw r6,GQ_5(r3) + lwz r6,GQ_5(r4) + mfspr r5,918 + mtspr 917,r6 + stw r5,GQ_6(r3) + lwz r5,GQ_6(r4) + mfspr r6,919 + mtspr 918,r5 + stw r6,GQ_7(r3) + lwz r6,GQ_7(r4) + mtspr 919,r6 + + mfsrr0 r5 + stw r5, GP_SRR0(r3) + lwz r5, GP_SRR0(r4) + mfsrr1 r6 + mtsrr0 r5 + stw r6, GP_SRR1(r3) + lwz r6, GP_SRR1(r4) + mtsrr1 r6 + mfcr r5 + stw r5, GP_CR(r3) + lwz r5, GP_CR(r4) + mflr r6 + mtcrf 255, r5 + stw r6, GP_LR(r3) + lwz r6, GP_LR(r4) + mfmsr r5 + mtlr r6 + stw r5, GP_MSR(r3) + lwz r5, GP_MSR(r4) + lmw r6, GP_6(r4) + rlwinm r5, r5, 0, 19, 17 + mtmsr r5 + + lwz r5,GP_5(r4) + blr + + .align 5 + .globl _cpu_context_save_ex +_cpu_context_save_ex: + sync + isync + + stw r0,GPR0_OFFSET-8(r3) + stw sp,GPR1_OFFSET-8(r3) + stw toc,GPR2_OFFSET-8(r3) + stmw r3,GPR3_OFFSET-8(r3) + + mfctr r6 + stw r6, CTR_OFFSET-8(r3) + mfcr r6 + stw r6, CR_OFFSET-8(r3) + mflr r7 + stw r7, LR_OFFSET-8(r3) + mfxer r7 + stw r8, XER_OFFSET-8(r3) + mfmsr r8 + stw r8, MSR_OFFSET-8(r3) + mfdar r8 + stw r8, DAR_OFFSET-8(r3) + mfsrr0 r8 + stw r8, SRR0_OFFSET-8(r3) + mfsrr1 r8 + stw r8, SRR1_OFFSET-8(r3) + + mfspr r6,913 + stw r6,GQR1_OFFSET-8(r3) + mfspr r6,914 + stw r6,GQR2_OFFSET-8(r3) + mfspr r6,915 + stw r6,GQR3_OFFSET-8(r3) + mfspr r6,916 + stw r6,GQR4_OFFSET-8(r3) + mfspr r6,917 + stw r6,GQR5_OFFSET-8(r3) + mfspr r6,918 + stw r6,GQR6_OFFSET-8(r3) + mfspr r6,919 + stw r6,GQR7_OFFSET-8(r3) + + blr + + .align 5 + .globl _cpu_context_restore_ex +_cpu_context_restore_ex: + + lwz r6, CTR_OFFSET-8(r3) + mtctr r6 + lwz r6, CR_OFFSET-8(r3) + mtcrf 255, r6 + lwz r7, LR_OFFSET-8(r3) + mtlr r7 + lwz r7, XER_OFFSET-8(r3) + mtxer r7 + lwz r8, MSR_OFFSET-8(r3) + mtmsr r8 + lwz r8, SRR0_OFFSET-8(r3) + mtsrr0 r8 + lwz r8, SRR1_OFFSET-8(r3) + mtsrr1 r8 + + lwz r6, GQR1_OFFSET-8(r3) + mtspr 913,r6 + lwz r6, GQR2_OFFSET-8(r3) + mtspr 914,r6 + lwz r6, GQR3_OFFSET-8(r3) + mtspr 915,r6 + lwz r6, GQR4_OFFSET-8(r3) + mtspr 916,r6 + lwz r6, GQR5_OFFSET-8(r3) + mtspr 917,r6 + lwz r6, GQR6_OFFSET-8(r3) + mtspr 918,r6 + lwz r6, GQR7_OFFSET-8(r3) + mtspr 919,r6 + + lwz sp,GPR1_OFFSET-8(r3) + lwz toc,GPR2_OFFSET-8(r3) + lmw r4,GPR4_OFFSET-8(r3) + lwz r3,GPR3_OFFSET-8(r3) + + blr +#endif diff --git a/wii/libogc/libogc/lwp_heap.c b/wii/libogc/libogc/lwp_heap.c new file mode 100644 index 0000000000..5507dcca55 --- /dev/null +++ b/wii/libogc/libogc/lwp_heap.c @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include + +#include "lwp_heap.h" + + +u32 __lwp_heap_init(heap_cntrl *theheap,void *start_addr,u32 size,u32 pg_size) +{ + u32 dsize,level; + heap_block *block; + + if(!__lwp_heap_pgsize_valid(pg_size) || sizepg_size = pg_size; + dsize = (size - HEAP_OVERHEAD); + + block = (heap_block*)start_addr; + block->back_flag = HEAP_DUMMY_FLAG; + block->front_flag = dsize; + block->next = __lwp_heap_tail(theheap); + block->prev = __lwp_heap_head(theheap); + + theheap->start = block; + theheap->first = block; + theheap->perm_null = NULL; + theheap->last = block; + + block = __lwp_heap_nextblock(block); + block->back_flag = dsize; + block->front_flag = HEAP_DUMMY_FLAG; + theheap->final = block; + _CPU_ISR_Restore(level); + + return (dsize - HEAP_BLOCK_USED_OVERHEAD); +} + +void* __lwp_heap_allocate(heap_cntrl *theheap,u32 size) +{ + u32 excess; + u32 dsize; + heap_block *block; + heap_block *next_block; + heap_block *tmp_block; + void *ptr; + u32 offset,level; + + + if(size>=(-1-HEAP_BLOCK_USED_OVERHEAD)) return NULL; + + _CPU_ISR_Disable(level); + excess = (size % theheap->pg_size); + dsize = (size + theheap->pg_size + HEAP_BLOCK_USED_OVERHEAD); + + if(excess) + dsize += (theheap->pg_size - excess); + + if(dsizefirst;;block=block->next) { + if(block==__lwp_heap_tail(theheap)) { + _CPU_ISR_Restore(level); + return NULL; + } + if(block->front_flag>=dsize) break; + } + + if((block->front_flag-dsize)>(theheap->pg_size+HEAP_BLOCK_USED_OVERHEAD)) { + block->front_flag -= dsize; + next_block = __lwp_heap_nextblock(block); + next_block->back_flag = block->front_flag; + + tmp_block = __lwp_heap_blockat(next_block,dsize); + tmp_block->back_flag = next_block->front_flag = __lwp_heap_buildflag(dsize,HEAP_BLOCK_USED); + + ptr = __lwp_heap_startuser(next_block); + } else { + next_block = __lwp_heap_nextblock(block); + next_block->back_flag = __lwp_heap_buildflag(block->front_flag,HEAP_BLOCK_USED); + + block->front_flag = next_block->back_flag; + block->next->prev = block->prev; + block->prev->next = block->next; + + ptr = __lwp_heap_startuser(block); + } + + offset = (theheap->pg_size - ((u32)ptr&(theheap->pg_size-1))); + ptr += offset; + *(((u32*)ptr)-1) = offset; + _CPU_ISR_Restore(level); + + return ptr; +} + +BOOL __lwp_heap_free(heap_cntrl *theheap,void *ptr) +{ + heap_block *block; + heap_block *next_block; + heap_block *new_next; + heap_block *prev_block; + heap_block *tmp_block; + u32 dsize,level; + + _CPU_ISR_Disable(level); + + block = __lwp_heap_usrblockat(ptr); + if(!__lwp_heap_blockin(theheap,block) || __lwp_heap_blockfree(block)) { + _CPU_ISR_Restore(level); + return FALSE; + } + + dsize = __lwp_heap_blocksize(block); + next_block = __lwp_heap_blockat(block,dsize); + + if(!__lwp_heap_blockin(theheap,next_block) || (block->front_flag!=next_block->back_flag)) { + _CPU_ISR_Restore(level); + return FALSE; + } + + if(__lwp_heap_prev_blockfree(block)) { + prev_block = __lwp_heap_prevblock(block); + if(!__lwp_heap_blockin(theheap,prev_block)) { + _CPU_ISR_Restore(level); + return FALSE; + } + + if(__lwp_heap_blockfree(next_block)) { + prev_block->front_flag += next_block->front_flag+dsize; + tmp_block = __lwp_heap_nextblock(prev_block); + tmp_block->back_flag = prev_block->front_flag; + next_block->next->prev = next_block->prev; + next_block->prev->next = next_block->next; + } else { + prev_block->front_flag = next_block->back_flag = prev_block->front_flag+dsize; + } + } else if(__lwp_heap_blockfree(next_block)) { + block->front_flag = dsize+next_block->front_flag; + new_next = __lwp_heap_nextblock(block); + new_next->back_flag = block->front_flag; + block->next = next_block->next; + block->prev = next_block->prev; + next_block->prev->next = block; + next_block->next->prev = block; + + if(theheap->first==next_block) theheap->first = block; + } else { + next_block->back_flag = block->front_flag = dsize; + block->prev = __lwp_heap_head(theheap); + block->next = theheap->first; + theheap->first = block; + block->next->prev = block; + } + _CPU_ISR_Restore(level); + + return TRUE; +} + +u32 __lwp_heap_getinfo(heap_cntrl *theheap,heap_iblock *theinfo) +{ + u32 not_done = 1; + heap_block *theblock = NULL; + heap_block *nextblock = NULL; + + theinfo->free_blocks = 0; + theinfo->free_size = 0; + theinfo->used_blocks = 0; + theinfo->used_size = 0; + + if(!__sys_state_up(__sys_state_get())) return 1; + + theblock = theheap->start; + if(theblock->back_flag!=HEAP_DUMMY_FLAG) return 2; + + while(not_done) { + if(__lwp_heap_blockfree(theblock)) { + theinfo->free_blocks++; + theinfo->free_size += __lwp_heap_blocksize(theblock); + } else { + theinfo->used_blocks++; + theinfo->used_size += __lwp_heap_blocksize(theblock); + } + + if(theblock->front_flag!=HEAP_DUMMY_FLAG) { + nextblock = __lwp_heap_nextblock(theblock); + if(theblock->front_flag!=nextblock->back_flag) return 2; + } + + if(theblock->front_flag==HEAP_DUMMY_FLAG) + not_done = 0; + else + theblock = nextblock; + } + return 0; +} diff --git a/wii/libogc/libogc/lwp_heap.inl b/wii/libogc/libogc/lwp_heap.inl new file mode 100644 index 0000000000..913a84a79f --- /dev/null +++ b/wii/libogc/libogc/lwp_heap.inl @@ -0,0 +1,75 @@ +#ifndef __LWP_HEAP_INL__ +#define __LWP_HEAP_INL__ + +static __inline__ heap_block* __lwp_heap_head(heap_cntrl *theheap) +{ + return (heap_block*)&theheap->start; +} + +static __inline__ heap_block* __lwp_heap_tail(heap_cntrl *heap) +{ + return (heap_block*)&heap->final; +} + +static __inline__ heap_block* __lwp_heap_prevblock(heap_block *block) +{ + return (heap_block*)((char*)block - (block->back_flag&~HEAP_BLOCK_USED)); +} + +static __inline__ heap_block* __lwp_heap_nextblock(heap_block *block) +{ + return (heap_block*)((char*)block + (block->front_flag&~HEAP_BLOCK_USED)); +} + +static __inline__ heap_block* __lwp_heap_blockat(heap_block *block,u32 offset) +{ + return (heap_block*)((char*)block + offset); +} + +static __inline__ heap_block* __lwp_heap_usrblockat(void *ptr) +{ + u32 offset = *(((u32*)ptr)-1); + return __lwp_heap_blockat(ptr,-offset+-HEAP_BLOCK_USED_OVERHEAD); +} + +static __inline__ bool __lwp_heap_prev_blockfree(heap_block *block) +{ + return !(block->back_flag&HEAP_BLOCK_USED); +} + +static __inline__ bool __lwp_heap_blockfree(heap_block *block) +{ + return !(block->front_flag&HEAP_BLOCK_USED); +} + +static __inline__ bool __lwp_heap_blockused(heap_block *block) +{ + return (block->front_flag&HEAP_BLOCK_USED); +} + +static __inline__ u32 __lwp_heap_blocksize(heap_block *block) +{ + return (block->front_flag&~HEAP_BLOCK_USED); +} + +static __inline__ void* __lwp_heap_startuser(heap_block *block) +{ + return (void*)&block->next; +} + +static __inline__ bool __lwp_heap_blockin(heap_cntrl *heap,heap_block *block) +{ + return ((u32)block>=(u32)heap->start && (u32)block<=(u32)heap->final); +} + +static __inline__ bool __lwp_heap_pgsize_valid(u32 pgsize) +{ + return (pgsize!=0 && ((pgsize%PPC_ALIGNMENT)==0)); +} + +static __inline__ u32 __lwp_heap_buildflag(u32 size,u32 flag) +{ + return (size|flag); +} + +#endif diff --git a/wii/libogc/libogc/lwp_messages.c b/wii/libogc/libogc/lwp_messages.c new file mode 100644 index 0000000000..284de8a45c --- /dev/null +++ b/wii/libogc/libogc/lwp_messages.c @@ -0,0 +1,258 @@ +#include +#include "asm.h" +#include "lwp_messages.h" +#include "lwp_wkspace.h" + +void __lwpmq_msg_insert(mq_cntrl *mqueue,mq_buffercntrl *msg,u32 type) +{ + ++mqueue->num_pendingmsgs; + msg->prio = type; + +#ifdef _LWPMQ_DEBUG + printf("__lwpmq_msg_insert(%p,%p,%d)\n",mqueue,msg,type); +#endif + + switch(type) { + case LWP_MQ_SEND_REQUEST: + __lwpmq_msg_append(mqueue,msg); + break; + case LWP_MQ_SEND_URGENT: + __lwpmq_msg_prepend(mqueue,msg); + break; + default: + { + mq_buffercntrl *tmsg; + lwp_node *node; + lwp_queue *header; + + header = &mqueue->pending_msgs; + node = header->first; + while(!__lwp_queue_istail(header,node)) { + tmsg = (mq_buffercntrl*)node; + if(tmsg->prio<=msg->prio) { + node = node->next; + continue; + } + break; + } + __lwp_queue_insert(node->prev,&msg->node); + } + break; + } + + if(mqueue->num_pendingmsgs==1 && mqueue->notify_handler) + mqueue->notify_handler(mqueue->notify_arg); +} + +u32 __lwpmq_initialize(mq_cntrl *mqueue,mq_attr *attrs,u32 max_pendingmsgs,u32 max_msgsize) +{ + u32 alloc_msgsize; + u32 buffering_req; + +#ifdef _LWPMQ_DEBUG + printf("__lwpmq_initialize(%p,%p,%d,%d)\n",mqueue,attrs,max_pendingmsgs,max_msgsize); +#endif + mqueue->max_pendingmsgs = max_pendingmsgs; + mqueue->num_pendingmsgs = 0; + mqueue->max_msgsize = max_msgsize; + __lwpmq_set_notify(mqueue,NULL,NULL); + + alloc_msgsize = max_msgsize; + if(alloc_msgsize&(sizeof(u32)-1)) + alloc_msgsize = (alloc_msgsize+sizeof(u32))&~(sizeof(u32)-1); + + buffering_req = max_pendingmsgs*(alloc_msgsize+sizeof(mq_buffercntrl)); + mqueue->msq_buffers = (mq_buffer*)__lwp_wkspace_allocate(buffering_req); + + if(!mqueue->msq_buffers) return 0; + + __lwp_queue_initialize(&mqueue->inactive_msgs,mqueue->msq_buffers,max_pendingmsgs,(alloc_msgsize+sizeof(mq_buffercntrl))); + __lwp_queue_init_empty(&mqueue->pending_msgs); + __lwp_threadqueue_init(&mqueue->wait_queue,__lwpmq_is_priority(attrs)?LWP_THREADQ_MODEPRIORITY:LWP_THREADQ_MODEFIFO,LWP_STATES_WAITING_FOR_MESSAGE,LWP_MQ_STATUS_TIMEOUT); + + return 1; +} + +u32 __lwpmq_seize(mq_cntrl *mqueue,u32 id,void *buffer,u32 *size,u32 wait,u64 timeout) +{ + u32 level; + mq_buffercntrl *msg; + lwp_cntrl *exec,*thread; + + exec = _thr_executing; + exec->wait.ret_code = LWP_MQ_STATUS_SUCCESSFUL; +#ifdef _LWPMQ_DEBUG + printf("__lwpmq_seize(%p,%d,%p,%p,%d,%d)\n",mqueue,id,buffer,size,wait,mqueue->num_pendingmsgs); +#endif + + _CPU_ISR_Disable(level); + if(mqueue->num_pendingmsgs!=0) { + --mqueue->num_pendingmsgs; + msg = __lwpmq_get_pendingmsg(mqueue); + _CPU_ISR_Restore(level); + + *size = msg->contents.size; + exec->wait.cnt = msg->prio; + __lwpmq_buffer_copy(buffer,msg->contents.buffer,*size); + + thread = __lwp_threadqueue_dequeue(&mqueue->wait_queue); + if(!thread) { + __lwpmq_free_msg(mqueue,msg); + return LWP_MQ_STATUS_SUCCESSFUL; + } + + msg->prio = thread->wait.cnt; + msg->contents.size = (u32)thread->wait.ret_arg_1; + __lwpmq_buffer_copy(msg->contents.buffer,thread->wait.ret_arg,msg->contents.size); + + __lwpmq_msg_insert(mqueue,msg,msg->prio); + return LWP_MQ_STATUS_SUCCESSFUL; + } + + if(!wait) { + _CPU_ISR_Restore(level); + exec->wait.ret_code = LWP_MQ_STATUS_UNSATISFIED_NOWAIT; + return LWP_MQ_STATUS_UNSATISFIED_NOWAIT; + } + + __lwp_threadqueue_csenter(&mqueue->wait_queue); + exec->wait.queue = &mqueue->wait_queue; + exec->wait.id = id; + exec->wait.ret_arg = (void*)buffer; + exec->wait.ret_arg_1 = (void*)size; + _CPU_ISR_Restore(level); + + __lwp_threadqueue_enqueue(&mqueue->wait_queue,timeout); + return LWP_MQ_STATUS_SUCCESSFUL; +} + +u32 __lwpmq_submit(mq_cntrl *mqueue,u32 id,void *buffer,u32 size,u32 type,u32 wait,u64 timeout) +{ + u32 level; + lwp_cntrl *thread; + mq_buffercntrl *msg; + +#ifdef _LWPMQ_DEBUG + printf("__lwpmq_submit(%p,%p,%d,%d,%d,%d)\n",mqueue,buffer,size,id,type,wait); +#endif + if(size>mqueue->max_msgsize) + return LWP_MQ_STATUS_INVALID_SIZE; + + if(mqueue->num_pendingmsgs==0) { + thread = __lwp_threadqueue_dequeue(&mqueue->wait_queue); + if(thread) { + __lwpmq_buffer_copy(thread->wait.ret_arg,buffer,size); + *(u32*)thread->wait.ret_arg_1 = size; + thread->wait.cnt = type; + return LWP_MQ_STATUS_SUCCESSFUL; + } + } + + if(mqueue->num_pendingmsgsmax_pendingmsgs) { + msg = __lwpmq_allocate_msg(mqueue); + if(!msg) return LWP_MQ_STATUS_UNSATISFIED; + + __lwpmq_buffer_copy(msg->contents.buffer,buffer,size); + msg->contents.size = size; + msg->prio = type; + __lwpmq_msg_insert(mqueue,msg,type); + return LWP_MQ_STATUS_SUCCESSFUL; + } + + if(!wait) return LWP_MQ_STATUS_TOO_MANY; + if(__lwp_isr_in_progress()) return LWP_MQ_STATUS_UNSATISFIED; + + { + lwp_cntrl *exec = _thr_executing; + + _CPU_ISR_Disable(level); + __lwp_threadqueue_csenter(&mqueue->wait_queue); + exec->wait.queue = &mqueue->wait_queue; + exec->wait.id = id; + exec->wait.ret_arg = (void*)buffer; + exec->wait.ret_arg_1 = (void*)size; + exec->wait.cnt = type; + _CPU_ISR_Restore(level); + + __lwp_threadqueue_enqueue(&mqueue->wait_queue,timeout); + } + return LWP_MQ_STATUS_UNSATISFIED_WAIT; +} + +u32 __lwpmq_broadcast(mq_cntrl *mqueue,void *buffer,u32 size,u32 id,u32 *count) +{ + lwp_cntrl *thread; + u32 num_broadcast; + lwp_waitinfo *waitp; + u32 rsize; +#ifdef _LWPMQ_DEBUG + printf("__lwpmq_broadcast(%p,%p,%d,%d,%p)\n",mqueue,buffer,size,id,count); +#endif + if(mqueue->num_pendingmsgs!=0) { + *count = 0; + return LWP_MQ_STATUS_SUCCESSFUL; + } + + num_broadcast = 0; + while((thread=__lwp_threadqueue_dequeue(&mqueue->wait_queue))) { + waitp = &thread->wait; + ++num_broadcast; + + rsize = size; + if(size>mqueue->max_msgsize) + rsize = mqueue->max_msgsize; + + __lwpmq_buffer_copy(waitp->ret_arg,buffer,rsize); + *(u32*)waitp->ret_arg_1 = size; + } + *count = num_broadcast; + return LWP_MQ_STATUS_SUCCESSFUL; +} + +void __lwpmq_close(mq_cntrl *mqueue,u32 status) +{ + __lwp_threadqueue_flush(&mqueue->wait_queue,status); + __lwpmq_flush_support(mqueue); + __lwp_wkspace_free(mqueue->msq_buffers); +} + +u32 __lwpmq_flush(mq_cntrl *mqueue) +{ + if(mqueue->num_pendingmsgs!=0) + return __lwpmq_flush_support(mqueue); + else + return 0; +} + +u32 __lwpmq_flush_support(mq_cntrl *mqueue) +{ + u32 level; + lwp_node *inactive; + lwp_node *mqueue_first; + lwp_node *mqueue_last; + u32 cnt; + + _CPU_ISR_Disable(level); + + inactive = mqueue->inactive_msgs.first; + mqueue_first = mqueue->pending_msgs.first; + mqueue_last = mqueue->pending_msgs.last; + + mqueue->inactive_msgs.first = mqueue_first; + mqueue_last->next = inactive; + inactive->prev = mqueue_last; + mqueue_first->prev = __lwp_queue_head(&mqueue->inactive_msgs); + + __lwp_queue_init_empty(&mqueue->pending_msgs); + + cnt = mqueue->num_pendingmsgs; + mqueue->num_pendingmsgs = 0; + + _CPU_ISR_Restore(level); + return cnt; +} + +void __lwpmq_flush_waitthreads(mq_cntrl *mqueue) +{ + __lwp_threadqueue_flush(&mqueue->wait_queue,LWP_MQ_STATUS_UNSATISFIED_NOWAIT); +} diff --git a/wii/libogc/libogc/lwp_messages.inl b/wii/libogc/libogc/lwp_messages.inl new file mode 100644 index 0000000000..db023fa99e --- /dev/null +++ b/wii/libogc/libogc/lwp_messages.inl @@ -0,0 +1,62 @@ +#ifndef __MESSAGE_INL__ +#define __MESSAGE_INL__ + +static __inline__ void __lwpmq_set_notify(mq_cntrl *mqueue,mq_notifyhandler handler,void *arg) +{ + mqueue->notify_handler = handler; + mqueue->notify_arg = arg; +} + +static __inline__ u32 __lwpmq_is_priority(mq_attr *attr) +{ + return (attr->mode==LWP_MQ_PRIORITY); +} + +static __inline__ mq_buffercntrl* __lwpmq_allocate_msg(mq_cntrl *mqueue) +{ + return (mq_buffercntrl*)__lwp_queue_get(&mqueue->inactive_msgs); +} + +static __inline__ void __lwpmq_free_msg(mq_cntrl *mqueue,mq_buffercntrl *msg) +{ + __lwp_queue_append(&mqueue->inactive_msgs,&msg->node); +} + +static __inline__ void __lwpmq_msg_append(mq_cntrl *mqueue,mq_buffercntrl *msg) +{ +#ifdef _LWPMQ_DEBUG + printf("__lwpmq_msq_append(%p,%p,%p)\n",mqueue,&mqueue->inactive_msgs,msg); +#endif + __lwp_queue_append(&mqueue->pending_msgs,&msg->node); +} + +static __inline__ void __lwpmq_msg_prepend(mq_cntrl *mqueue,mq_buffercntrl *msg) +{ +#ifdef _LWPMQ_DEBUG + printf("__lwpmq_msq_prepend(%p,%p,%p)\n",mqueue,&mqueue->inactive_msgs,msg); +#endif + __lwp_queue_prepend(&mqueue->pending_msgs,&msg->node); +} + +static __inline__ u32 __lwpmq_send(mq_cntrl *mqueue,u32 id,void *buffer,u32 size,u32 wait,u32 timeout) +{ + return __lwpmq_submit(mqueue,id,buffer,size,LWP_MQ_SEND_REQUEST,wait,timeout); +} + +static __inline__ u32 __lwpmq_urgent(mq_cntrl *mqueue,void *buffer,u32 size,u32 id,u32 wait,u32 timeout) +{ + return __lwpmq_submit(mqueue,id,buffer,size,LWP_MQ_SEND_URGENT,wait,timeout); +} + +static __inline__ mq_buffercntrl* __lwpmq_get_pendingmsg(mq_cntrl *mqueue) +{ + return (mq_buffercntrl*)__lwp_queue_getI(&mqueue->pending_msgs); +} + +static __inline__ void __lwpmq_buffer_copy(void *dest,const void *src,u32 size) +{ + if(size==sizeof(u32)) *(u32*)dest = *(u32*)src; + else memcpy(dest,src,size); +} + +#endif diff --git a/wii/libogc/libogc/lwp_mutex.c b/wii/libogc/libogc/lwp_mutex.c new file mode 100644 index 0000000000..205074a952 --- /dev/null +++ b/wii/libogc/libogc/lwp_mutex.c @@ -0,0 +1,95 @@ +#include "asm.h" +#include "lwp_mutex.h" + +void __lwp_mutex_initialize(lwp_mutex *mutex,lwp_mutex_attr *attrs,u32 init_lock) +{ + mutex->atrrs = *attrs; + mutex->lock = init_lock; + mutex->blocked_cnt = 0; + + if(init_lock==LWP_MUTEX_LOCKED) { + mutex->nest_cnt = 1; + mutex->holder = _thr_executing; + if(__lwp_mutex_isinheritprio(attrs) || __lwp_mutex_isprioceiling(attrs)) + _thr_executing->res_cnt++; + } else { + mutex->nest_cnt = 0; + mutex->holder = NULL; + } + + __lwp_threadqueue_init(&mutex->wait_queue,__lwp_mutex_isfifo(attrs)?LWP_THREADQ_MODEFIFO:LWP_THREADQ_MODEPRIORITY,LWP_STATES_WAITING_FOR_MUTEX,LWP_MUTEX_TIMEOUT); +} + +u32 __lwp_mutex_surrender(lwp_mutex *mutex) +{ + lwp_cntrl *thethread; + lwp_cntrl *holder; + + holder = mutex->holder; + + if(mutex->atrrs.onlyownerrelease) { + if(!__lwp_thread_isexec(holder)) + return LWP_MUTEX_NOTOWNER; + } + + if(!mutex->nest_cnt) + return LWP_MUTEX_SUCCESSFUL; + + mutex->nest_cnt--; + if(mutex->nest_cnt!=0) { + switch(mutex->atrrs.nest_behavior) { + case LWP_MUTEX_NEST_ACQUIRE: + return LWP_MUTEX_SUCCESSFUL; + case LWP_MUTEX_NEST_ERROR: + return LWP_MUTEX_NEST_NOTALLOWED; + case LWP_MUTEX_NEST_BLOCK: + break; + } + } + + if(__lwp_mutex_isinheritprio(&mutex->atrrs) || __lwp_mutex_isprioceiling(&mutex->atrrs)) + holder->res_cnt--; + + mutex->holder = NULL; + if(__lwp_mutex_isinheritprio(&mutex->atrrs) || __lwp_mutex_isprioceiling(&mutex->atrrs)) { + if(holder->res_cnt==0 && holder->real_prio!=holder->cur_prio) + __lwp_thread_changepriority(holder,holder->real_prio,TRUE); + } + + if((thethread=__lwp_threadqueue_dequeue(&mutex->wait_queue))) { + mutex->nest_cnt = 1; + mutex->holder = thethread; + if(__lwp_mutex_isinheritprio(&mutex->atrrs) || __lwp_mutex_isprioceiling(&mutex->atrrs)) + thethread->res_cnt++; + } else + mutex->lock = LWP_MUTEX_UNLOCKED; + + return LWP_MUTEX_SUCCESSFUL; +} + +void __lwp_mutex_seize_irq_blocking(lwp_mutex *mutex,u64 timeout) +{ + lwp_cntrl *exec; + + exec = _thr_executing; + if(__lwp_mutex_isinheritprio(&mutex->atrrs)){ + if(mutex->holder->cur_prio>exec->cur_prio) + __lwp_thread_changepriority(mutex->holder,exec->cur_prio,FALSE); + } + + mutex->blocked_cnt++; + __lwp_threadqueue_enqueue(&mutex->wait_queue,timeout); + + if(_thr_executing->wait.ret_code==LWP_MUTEX_SUCCESSFUL) { + if(__lwp_mutex_isprioceiling(&mutex->atrrs)) { + if(mutex->atrrs.prioceilcur_prio) + __lwp_thread_changepriority(exec,mutex->atrrs.prioceil,FALSE); + } + } + __lwp_thread_dispatchenable(); +} + +void __lwp_mutex_flush(lwp_mutex *mutex,u32 status) +{ + __lwp_threadqueue_flush(&mutex->wait_queue,status); +} diff --git a/wii/libogc/libogc/lwp_mutex.inl b/wii/libogc/libogc/lwp_mutex.inl new file mode 100644 index 0000000000..afa9c82396 --- /dev/null +++ b/wii/libogc/libogc/lwp_mutex.inl @@ -0,0 +1,88 @@ +#ifndef __LWP_MUTEX_INL__ +#define __LWP_MUTEX_INL__ + +static __inline__ u32 __lwp_mutex_locked(lwp_mutex *mutex) +{ + return (mutex->lock==LWP_MUTEX_LOCKED); +} + +static __inline__ u32 __lwp_mutex_ispriority(lwp_mutex_attr *attrs) +{ + return (attrs->mode==LWP_MUTEX_PRIORITY); +} + +static __inline__ u32 __lwp_mutex_isfifo(lwp_mutex_attr *attrs) +{ + return (attrs->mode==LWP_MUTEX_FIFO); +} + +static __inline__ u32 __lwp_mutex_isinheritprio(lwp_mutex_attr *attrs) +{ + return (attrs->mode==LWP_MUTEX_INHERITPRIO); +} + +static __inline__ u32 __lwp_mutex_isprioceiling(lwp_mutex_attr *attrs) +{ + return (attrs->mode==LWP_MUTEX_PRIORITYCEIL); +} + +static __inline__ u32 __lwp_mutex_seize_irq_trylock(lwp_mutex *mutex,u32 *isr_level) +{ + lwp_cntrl *exec; + u32 level = *isr_level; + + exec = _thr_executing; + exec->wait.ret_code = LWP_MUTEX_SUCCESSFUL; + if(!__lwp_mutex_locked(mutex)) { + mutex->lock = LWP_MUTEX_LOCKED; + mutex->holder = exec; + mutex->nest_cnt = 1; + if(__lwp_mutex_isinheritprio(&mutex->atrrs) || __lwp_mutex_isprioceiling(&mutex->atrrs)) + exec->res_cnt++; + if(!__lwp_mutex_isprioceiling(&mutex->atrrs)) { + _CPU_ISR_Restore(level); + return 0; + } + { + u32 prioceiling,priocurr; + + prioceiling = mutex->atrrs.prioceil; + priocurr = exec->cur_prio; + if(priocurr==prioceiling) { + _CPU_ISR_Restore(level); + return 0; + } + if(priocurr>prioceiling) { + __lwp_thread_dispatchdisable(); + _CPU_ISR_Restore(level); + __lwp_thread_changepriority(mutex->holder,mutex->atrrs.prioceil,FALSE); + __lwp_thread_dispatchenable(); + return 0; + } + exec->wait.ret_code = LWP_MUTEX_CEILINGVIOL; + mutex->nest_cnt = 0; + exec->res_cnt--; + _CPU_ISR_Restore(level); + return 0; + } + return 0; + } + + if(__lwp_thread_isexec(mutex->holder)) { + switch(mutex->atrrs.nest_behavior) { + case LWP_MUTEX_NEST_ACQUIRE: + mutex->nest_cnt++; + _CPU_ISR_Restore(level); + return 0; + case LWP_MUTEX_NEST_ERROR: + exec->wait.ret_code = LWP_MUTEX_NEST_NOTALLOWED; + _CPU_ISR_Restore(level); + return 0; + case LWP_MUTEX_NEST_BLOCK: + break; + } + } + return 1; +} + +#endif diff --git a/wii/libogc/libogc/lwp_objmgr.c b/wii/libogc/libogc/lwp_objmgr.c new file mode 100644 index 0000000000..9b2149baff --- /dev/null +++ b/wii/libogc/libogc/lwp_objmgr.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "lwp_objmgr.h" + +static u32 _lwp_objmgr_memsize = 0; +static lwp_obj *null_local_table = NULL; + +u32 __lwp_objmgr_memsize() +{ + return _lwp_objmgr_memsize; +} + +void __lwp_objmgr_initinfo(lwp_objinfo *info,u32 max_nodes,u32 node_size) +{ + u32 idx,i,size; + lwp_obj *object; + lwp_queue inactives; + void **local_table; + + info->min_id = 0; + info->max_id = 0; + info->inactives_cnt = 0; + info->node_size = node_size; + info->max_nodes = max_nodes; + info->obj_blocks = NULL; + info->local_table = &null_local_table; + + __lwp_queue_init_empty(&info->inactives); + + size = ((info->max_nodes*sizeof(lwp_obj*))+(info->max_nodes*info->node_size)); + local_table = (void**)__lwp_wkspace_allocate(info->max_nodes*sizeof(lwp_obj*)); + if(!local_table) return; + + info->local_table = (lwp_obj**)local_table; + for(i=0;imax_nodes;i++) { + local_table[i] = NULL; + } + + info->obj_blocks = __lwp_wkspace_allocate(info->max_nodes*info->node_size); + if(!info->obj_blocks) { + __lwp_wkspace_free(local_table); + return; + } + + __lwp_queue_initialize(&inactives,info->obj_blocks,info->max_nodes,info->node_size); + + idx = info->min_id; + while((object=(lwp_obj*)__lwp_queue_get(&inactives))!=NULL) { + object->id = idx; + object->information = NULL; + __lwp_queue_append(&info->inactives,&object->node); + idx++; + } + + info->max_id += info->max_nodes; + info->inactives_cnt += info->max_nodes; + _lwp_objmgr_memsize += size; +} + +lwp_obj* __lwp_objmgr_getisrdisable(lwp_objinfo *info,u32 id,u32 *p_level) +{ + u32 level; + lwp_obj *object = NULL; + + _CPU_ISR_Disable(level); + if(info->max_id>=id) { + if((object=info->local_table[id])!=NULL) { + *p_level = level; + return object; + } + } + _CPU_ISR_Restore(level); + return NULL; +} + +lwp_obj* __lwp_objmgr_getnoprotection(lwp_objinfo *info,u32 id) +{ + lwp_obj *object = NULL; + + if(info->max_id>=id) { + if((object=info->local_table[id])!=NULL) return object; + } + return NULL; +} + +lwp_obj* __lwp_objmgr_get(lwp_objinfo *info,u32 id) +{ + lwp_obj *object = NULL; + + if(info->max_id>=id) { + __lwp_thread_dispatchdisable(); + if((object=info->local_table[id])!=NULL) return object; + __lwp_thread_dispatchenable(); + } + return NULL; +} + +lwp_obj* __lwp_objmgr_allocate(lwp_objinfo *info) +{ + u32 level; + lwp_obj* object; + + _CPU_ISR_Disable(level); + object = (lwp_obj*)__lwp_queue_getI(&info->inactives); + if(object) { + object->information = info; + info->inactives_cnt--; + } + _CPU_ISR_Restore(level); + + return object; +} + +void __lwp_objmgr_free(lwp_objinfo *info,lwp_obj *object) +{ + u32 level; + + _CPU_ISR_Disable(level); + __lwp_queue_appendI(&info->inactives,&object->node); + object->information = NULL; + info->inactives_cnt++; + _CPU_ISR_Restore(level); +} diff --git a/wii/libogc/libogc/lwp_objmgr.inl b/wii/libogc/libogc/lwp_objmgr.inl new file mode 100644 index 0000000000..357d16c7a6 --- /dev/null +++ b/wii/libogc/libogc/lwp_objmgr.inl @@ -0,0 +1,19 @@ +#ifndef __LWP_OBJMGR_INL__ +#define __LWP_OBJMGR_INL__ + +static __inline__ void __lwp_objmgr_setlocal(lwp_objinfo *info,u32 idx,lwp_obj *object) +{ + if(idxmax_nodes) info->local_table[idx] = object; +} + +static __inline__ void __lwp_objmgr_open(lwp_objinfo *info,lwp_obj *object) +{ + __lwp_objmgr_setlocal(info,object->id,object); +} + +static __inline__ void __lwp_objmgr_close(lwp_objinfo *info,lwp_obj *object) +{ + __lwp_objmgr_setlocal(info,object->id,NULL); +} + +#endif diff --git a/wii/libogc/libogc/lwp_priority.c b/wii/libogc/libogc/lwp_priority.c new file mode 100644 index 0000000000..d8d0e4c700 --- /dev/null +++ b/wii/libogc/libogc/lwp_priority.c @@ -0,0 +1,14 @@ +#include + +vu32 _prio_major_bitmap; +u32 _prio_bitmap[16] __attribute__((aligned(32))); + +void __lwp_priority_init() +{ + u32 index; + + _prio_major_bitmap = 0; + for(index=0;index<16;index++) + _prio_bitmap[index] = 0; + +} diff --git a/wii/libogc/libogc/lwp_priority.inl b/wii/libogc/libogc/lwp_priority.inl new file mode 100644 index 0000000000..3b4b54bba8 --- /dev/null +++ b/wii/libogc/libogc/lwp_priority.inl @@ -0,0 +1,42 @@ +#ifndef __LWP_PRIORITY_INL__ +#define __LWP_PRIORITY_INL__ + +static __inline__ void __lwp_priomap_init(prio_cntrl *theprio,u32 prio) +{ + u32 mask; + + u32 major = prio/16; + u32 minor = prio%16; + + theprio->minor = &_prio_bitmap[major]; + + mask = 0x80000000>>major; + theprio->ready_major = mask; + theprio->block_major = ~mask; + + mask = 0x80000000>>minor; + theprio->ready_minor = mask; + theprio->block_minor = ~mask; +} + +static __inline__ void __lwp_priomap_addto(prio_cntrl *theprio) +{ + *theprio->minor |= theprio->ready_minor; + _prio_major_bitmap |= theprio->ready_major; +} + +static __inline__ void __lwp_priomap_removefrom(prio_cntrl *theprio) +{ + *theprio->minor &= theprio->block_minor; + if(*theprio->minor==0) + _prio_major_bitmap &= theprio->block_major; +} + +static __inline__ u32 __lwp_priomap_highest() +{ + u32 major = cntlzw(_prio_major_bitmap); + u32 minor = cntlzw(_prio_bitmap[major]); + return ((major<<4)+minor); +} + +#endif diff --git a/wii/libogc/libogc/lwp_queue.c b/wii/libogc/libogc/lwp_queue.c new file mode 100644 index 0000000000..cb359c9ed5 --- /dev/null +++ b/wii/libogc/libogc/lwp_queue.c @@ -0,0 +1,68 @@ +#include +#include "asm.h" +#include "processor.h" +#include "lwp_queue.h" + +void __lwp_queue_initialize(lwp_queue *queue,void *start_addr,u32 num_nodes,u32 node_size) +{ + u32 count; + lwp_node *curr; + lwp_node *next; + +#ifdef _LWPQ_DEBUG + printf("__lwp_queue_initialize(%p,%p,%d,%d)\n",queue,start_addr,num_nodes,node_size); +#endif + count = num_nodes; + curr = __lwp_queue_head(queue); + queue->perm_null = NULL; + next = (lwp_node*)start_addr; + + while(count--) { + curr->next = next; + next->prev = curr; + curr = next; + next = (lwp_node*)(((void*)next)+node_size); + } + curr->next = __lwp_queue_tail(queue); + queue->last = curr; +} + +lwp_node* __lwp_queue_get(lwp_queue *queue) +{ + u32 level; + lwp_node *ret = NULL; + + _CPU_ISR_Disable(level); + if(!__lwp_queue_isempty(queue)) { + ret = __lwp_queue_firstnodeI(queue); + } + _CPU_ISR_Restore(level); + return ret; +} + +void __lwp_queue_append(lwp_queue *queue,lwp_node *node) +{ + u32 level; + + _CPU_ISR_Disable(level); + __lwp_queue_appendI(queue,node); + _CPU_ISR_Restore(level); +} + +void __lwp_queue_extract(lwp_node *node) +{ + u32 level; + + _CPU_ISR_Disable(level); + __lwp_queue_extractI(node); + _CPU_ISR_Restore(level); +} + +void __lwp_queue_insert(lwp_node *after,lwp_node *node) +{ + u32 level; + + _CPU_ISR_Disable(level); + __lwp_queue_insertI(after,node); + _CPU_ISR_Restore(level); +} diff --git a/wii/libogc/libogc/lwp_queue.inl b/wii/libogc/libogc/lwp_queue.inl new file mode 100644 index 0000000000..809aaa3d1c --- /dev/null +++ b/wii/libogc/libogc/lwp_queue.inl @@ -0,0 +1,96 @@ +#ifndef __LWP_QUEUE_INL__ +#define __LWP_QUEUE_INL__ + +static __inline__ lwp_node* __lwp_queue_head(lwp_queue *queue) +{ + return (lwp_node*)queue; +} + +static __inline__ lwp_node* __lwp_queue_tail(lwp_queue *queue) +{ + return (lwp_node*)&queue->perm_null; +} + +static __inline__ u32 __lwp_queue_istail(lwp_queue *queue,lwp_node *node) +{ + return (node==__lwp_queue_tail(queue)); +} + +static __inline__ u32 __lwp_queue_ishead(lwp_queue *queue,lwp_node *node) +{ + return (node==__lwp_queue_head(queue)); +} + +static __inline__ lwp_node* __lwp_queue_firstnodeI(lwp_queue *queue) +{ + lwp_node *ret = queue->first; + lwp_node *new_first = ret->next; + queue->first = new_first; + new_first->prev = __lwp_queue_head(queue); + return ret; +} + +static __inline__ void __lwp_queue_init_empty(lwp_queue *queue) +{ + queue->first = __lwp_queue_tail(queue); + queue->perm_null = NULL; + queue->last = __lwp_queue_head(queue); +} + +static __inline__ u32 __lwp_queue_isempty(lwp_queue *queue) +{ + return (queue->first==__lwp_queue_tail(queue)); +} + +static __inline__ u32 __lwp_queue_onenode(lwp_queue *queue) +{ + return (queue->first==queue->last); +} + +static __inline__ void __lwp_queue_appendI(lwp_queue *queue,lwp_node *node) +{ + lwp_node *old; + node->next = __lwp_queue_tail(queue); + old = queue->last; + queue->last = node; + old->next = node; + node->prev = old; +} + +static __inline__ void __lwp_queue_extractI(lwp_node *node) +{ + lwp_node *next = node->next; + lwp_node *prev = node->prev; + next->prev = prev; + prev->next = next; +} + +static __inline__ void __lwp_queue_insertI(lwp_node *after,lwp_node *node) +{ + lwp_node *before; + + node->prev = after; + before = after->next; + after->next = node; + node->next = before; + before->prev = node; +} + +static __inline__ void __lwp_queue_prepend(lwp_queue *queue,lwp_node *node) +{ + __lwp_queue_insert(__lwp_queue_head(queue),node); +} + +static __inline__ void __lwp_queue_prependI(lwp_queue *queue,lwp_node *node) +{ + __lwp_queue_insertI(__lwp_queue_head(queue),node); +} + +static __inline__ lwp_node* __lwp_queue_getI(lwp_queue *queue) +{ + if(!__lwp_queue_isempty(queue)) + return __lwp_queue_firstnodeI(queue); + return NULL; +} + +#endif diff --git a/wii/libogc/libogc/lwp_sema.c b/wii/libogc/libogc/lwp_sema.c new file mode 100644 index 0000000000..49b1901da2 --- /dev/null +++ b/wii/libogc/libogc/lwp_sema.c @@ -0,0 +1,63 @@ +#include "asm.h" +#include "lwp_sema.h" + +void __lwp_sema_initialize(lwp_sema *sema,lwp_semattr *attrs,u32 init_count) +{ + sema->attrs = *attrs; + sema->count = init_count; + + __lwp_threadqueue_init(&sema->wait_queue,__lwp_sema_ispriority(attrs)?LWP_THREADQ_MODEPRIORITY:LWP_THREADQ_MODEFIFO,LWP_STATES_WAITING_FOR_SEMAPHORE,LWP_SEMA_TIMEOUT); +} + +u32 __lwp_sema_surrender(lwp_sema *sema,u32 id) +{ + u32 level,ret; + lwp_cntrl *thethread; + + ret = LWP_SEMA_SUCCESSFUL; + if((thethread=__lwp_threadqueue_dequeue(&sema->wait_queue))) return ret; + else { + _CPU_ISR_Disable(level); + if(sema->count<=sema->attrs.max_cnt) + ++sema->count; + else + ret = LWP_SEMA_MAXCNT_EXCEEDED; + _CPU_ISR_Restore(level); + } + return ret; +} + +u32 __lwp_sema_seize(lwp_sema *sema,u32 id,u32 wait,u64 timeout) +{ + u32 level; + lwp_cntrl *exec; + + exec = _thr_executing; + exec->wait.ret_code = LWP_SEMA_SUCCESSFUL; + + _CPU_ISR_Disable(level); + if(sema->count!=0) { + --sema->count; + _CPU_ISR_Restore(level); + return LWP_SEMA_SUCCESSFUL; + } + + if(!wait) { + _CPU_ISR_Restore(level); + exec->wait.ret_code = LWP_SEMA_UNSATISFIED_NOWAIT; + return LWP_SEMA_UNSATISFIED_NOWAIT; + } + + __lwp_threadqueue_csenter(&sema->wait_queue); + exec->wait.queue = &sema->wait_queue; + exec->wait.id = id; + _CPU_ISR_Restore(level); + + __lwp_threadqueue_enqueue(&sema->wait_queue,timeout); + return LWP_SEMA_SUCCESSFUL; +} + +void __lwp_sema_flush(lwp_sema *sema,u32 status) +{ + __lwp_threadqueue_flush(&sema->wait_queue,status); +} diff --git a/wii/libogc/libogc/lwp_sema.inl b/wii/libogc/libogc/lwp_sema.inl new file mode 100644 index 0000000000..0f57c6dcda --- /dev/null +++ b/wii/libogc/libogc/lwp_sema.inl @@ -0,0 +1,38 @@ +#ifndef __LWP_SEMA_INL__ +#define __LWP_SEMA_INL__ + +static __inline__ u32 __lwp_sema_ispriority(lwp_semattr *attr) +{ + return (attr->mode==LWP_SEMA_MODEPRIORITY); +} + +static __inline__ void __lwp_sema_seize_isrdisable(lwp_sema *sema,u32 id,u32 wait,u32 *isrlevel) +{ + lwp_cntrl *exec; + u32 level = *isrlevel; + + exec = _thr_executing; + exec->wait.ret_code = LWP_SEMA_SUCCESSFUL; + if(sema->count!=0) { + --sema->count; + _CPU_ISR_Restore(level); + return; + } + + if(!wait) { + _CPU_ISR_Restore(level); + exec->wait.ret_code = LWP_SEMA_UNSATISFIED_NOWAIT; + return; + } + + __lwp_thread_dispatchdisable(); + __lwp_threadqueue_csenter(&sema->wait_queue); + exec->wait.queue = &sema->wait_queue; + exec->wait.id = id; + _CPU_ISR_Restore(level); + + __lwp_threadqueue_enqueue(&sema->wait_queue,0); + __lwp_thread_dispatchenable(); +} + +#endif diff --git a/wii/libogc/libogc/lwp_stack.c b/wii/libogc/libogc/lwp_stack.c new file mode 100644 index 0000000000..05eadebfc4 --- /dev/null +++ b/wii/libogc/libogc/lwp_stack.c @@ -0,0 +1,27 @@ +#include +#include "lwp_stack.h" +#include "lwp_wkspace.h" + +u32 __lwp_stack_allocate(lwp_cntrl *thethread,u32 size) +{ + void *stack_addr = NULL; + + if(!__lwp_stack_isenough(size)) + size = CPU_MINIMUM_STACK_SIZE; + + size = __lwp_stack_adjust(size); + stack_addr = __lwp_wkspace_allocate(size); + + if(!stack_addr) size = 0; + + thethread->stack = stack_addr; + return size; +} + +void __lwp_stack_free(lwp_cntrl *thethread) +{ + if(!thethread->stack_allocated) + return; + + __lwp_wkspace_free(thethread->stack); +} diff --git a/wii/libogc/libogc/lwp_stack.inl b/wii/libogc/libogc/lwp_stack.inl new file mode 100644 index 0000000000..65a2cd5df7 --- /dev/null +++ b/wii/libogc/libogc/lwp_stack.inl @@ -0,0 +1,14 @@ +#ifndef __LWP_STACK_INL__ +#define __LWP_STACK_INL__ + +static __inline__ u32 __lwp_stack_isenough(u32 size) +{ + return (size>=CPU_MINIMUM_STACK_SIZE); +} + +static __inline__ u32 __lwp_stack_adjust(u32 size) +{ + return (size+CPU_STACK_ALIGNMENT); +} + +#endif diff --git a/wii/libogc/libogc/lwp_states.inl b/wii/libogc/libogc/lwp_states.inl new file mode 100644 index 0000000000..79ee4ecf70 --- /dev/null +++ b/wii/libogc/libogc/lwp_states.inl @@ -0,0 +1,109 @@ +#ifndef __LWP_STATES_INL__ +#define __LWP_STATES_INL__ + +static __inline__ u32 __lwp_setstate(u32 curr_state,u32 stateset) +{ + return (curr_state|stateset); +} + +static __inline__ u32 __lwp_clearstate(u32 curr_state,u32 stateclear) +{ + return (curr_state&~stateclear); +} + +static __inline__ u32 __lwp_stateready(u32 curr_state) +{ + return (curr_state==LWP_STATES_READY); +} + +static __inline__ u32 __lwp_stateonlydormant(u32 curr_state) +{ + return (curr_state==LWP_STATES_DORMANT); +} + +static __inline__ u32 __lwp_statedormant(u32 curr_state) +{ + return (curr_state&LWP_STATES_DORMANT); +} + +static __inline__ u32 __lwp_statesuspended(u32 curr_state) +{ + return (curr_state&LWP_STATES_SUSPENDED); +} + +static __inline__ u32 __lwp_statetransient(u32 curr_state) +{ + return (curr_state&LWP_STATES_TRANSIENT); +} + +static __inline__ u32 __lwp_statedelaying(u32 curr_state) +{ + return (curr_state&LWP_STATES_DELAYING); +} + +static __inline__ u32 __lwp_statewaitbuffer(u32 curr_state) +{ + return (curr_state&LWP_STATES_WAITING_FOR_BUFFER); +} + +static __inline__ u32 __lwp_statewaitsegment(u32 curr_state) +{ + return (curr_state&LWP_STATES_WAITING_FOR_SEGMENT); +} + +static __inline__ u32 __lwp_statewaitmessage(u32 curr_state) +{ + return (curr_state&LWP_STATES_WAITING_FOR_MESSAGE); +} + +static __inline__ u32 __lwp_statewaitevent(u32 curr_state) +{ + return (curr_state&LWP_STATES_WAITING_FOR_EVENT); +} + +static __inline__ u32 __lwp_statewaitmutex(u32 curr_state) +{ + return (curr_state&LWP_STATES_WAITING_FOR_MUTEX); +} + +static __inline__ u32 __lwp_statewaitsemaphore(u32 curr_state) +{ + return (curr_state&LWP_STATES_WAITING_FOR_SEMAPHORE); +} + +static __inline__ u32 __lwp_statewaittime(u32 curr_state) +{ + return (curr_state&LWP_STATES_WAITING_FOR_TIME); +} + +static __inline__ u32 __lwp_statewaitrpcreply(u32 curr_state) +{ + return (curr_state&LWP_STATES_WAITING_FOR_RPCREPLAY); +} + +static __inline__ u32 __lwp_statewaitperiod(u32 curr_state) +{ + return (curr_state&LWP_STATES_WAITING_FOR_PERIOD); +} + +static __inline__ u32 __lwp_statewaitlocallyblocked(u32 curr_state) +{ + return (curr_state&LWP_STATES_LOCALLY_BLOCKED); +} + +static __inline__ u32 __lwp_statewaitthreadqueue(u32 curr_state) +{ + return (curr_state&LWP_STATES_WAITING_ON_THREADQ); +} + +static __inline__ u32 __lwp_stateblocked(u32 curr_state) +{ + return (curr_state&LWP_STATES_BLOCKED); +} + +static __inline__ u32 __lwp_statesset(u32 curr_state,u32 mask) +{ + return ((curr_state&mask)!=LWP_STATES_READY); +} + +#endif diff --git a/wii/libogc/libogc/lwp_threadq.c b/wii/libogc/libogc/lwp_threadq.c new file mode 100644 index 0000000000..285202be20 --- /dev/null +++ b/wii/libogc/libogc/lwp_threadq.c @@ -0,0 +1,497 @@ +#include +#include +#include "asm.h" +#include "lwp_threadq.h" + +//#define _LWPTHRQ_DEBUG + +static void __lwp_threadqueue_timeout(void *usr_data) +{ + lwp_cntrl *thethread; + lwp_thrqueue *thequeue; + + __lwp_thread_dispatchdisable(); + thethread = (lwp_cntrl*)usr_data; + thequeue = thethread->wait.queue; + if(thequeue->sync_state!=LWP_THREADQ_SYNCHRONIZED && __lwp_thread_isexec(thethread)) { + if(thequeue->sync_state!=LWP_THREADQ_SATISFIED) thequeue->sync_state = LWP_THREADQ_TIMEOUT; + } else { + thethread->wait.ret_code = thethread->wait.queue->timeout_state; + __lwp_threadqueue_extract(thethread->wait.queue,thethread); + } + __lwp_thread_dispatchunnest(); +} + +lwp_cntrl* __lwp_threadqueue_firstfifo(lwp_thrqueue *queue) +{ + if(!__lwp_queue_isempty(&queue->queues.fifo)) + return (lwp_cntrl*)queue->queues.fifo.first; + + return NULL; +} + +lwp_cntrl* __lwp_threadqueue_firstpriority(lwp_thrqueue *queue) +{ + u32 index; + + for(index=0;indexqueues.priority[index])) + return (lwp_cntrl*)queue->queues.priority[index].first; + } + return NULL; +} + +void __lwp_threadqueue_enqueuefifo(lwp_thrqueue *queue,lwp_cntrl *thethread,u64 timeout) +{ + u32 level,sync_state; + + _CPU_ISR_Disable(level); + + sync_state = queue->sync_state; + queue->sync_state = LWP_THREADQ_SYNCHRONIZED; +#ifdef _LWPTHRQ_DEBUG + printf("__lwp_threadqueue_enqueuefifo(%p,%d)\n",thethread,sync_state); +#endif + switch(sync_state) { + case LWP_THREADQ_SYNCHRONIZED: + break; + case LWP_THREADQ_NOTHINGHAPPEND: + __lwp_queue_appendI(&queue->queues.fifo,&thethread->object.node); + _CPU_ISR_Restore(level); + return; + case LWP_THREADQ_TIMEOUT: + thethread->wait.ret_code = thethread->wait.queue->timeout_state; + _CPU_ISR_Restore(level); + break; + case LWP_THREADQ_SATISFIED: + if(__lwp_wd_isactive(&thethread->timer)) { + __lwp_wd_deactivate(&thethread->timer); + _CPU_ISR_Restore(level); + __lwp_wd_remove_ticks(&thethread->timer); + } else + _CPU_ISR_Restore(level); + + break; + } + __lwp_thread_unblock(thethread); +} + +lwp_cntrl* __lwp_threadqueue_dequeuefifo(lwp_thrqueue *queue) +{ + u32 level; + lwp_cntrl *ret; + + _CPU_ISR_Disable(level); + if(!__lwp_queue_isempty(&queue->queues.fifo)) { + ret = (lwp_cntrl*)__lwp_queue_firstnodeI(&queue->queues.fifo); + if(!__lwp_wd_isactive(&ret->timer)) { + _CPU_ISR_Restore(level); + __lwp_thread_unblock(ret); + } else { + __lwp_wd_deactivate(&ret->timer); + _CPU_ISR_Restore(level); + __lwp_wd_remove_ticks(&ret->timer); + __lwp_thread_unblock(ret); + } + return ret; + } + + switch(queue->sync_state) { + case LWP_THREADQ_SYNCHRONIZED: + case LWP_THREADQ_SATISFIED: + _CPU_ISR_Restore(level); + return NULL; + case LWP_THREADQ_NOTHINGHAPPEND: + case LWP_THREADQ_TIMEOUT: + queue->sync_state = LWP_THREADQ_SATISFIED; + _CPU_ISR_Restore(level); + return _thr_executing; + } + return NULL; +} + +void __lwp_threadqueue_enqueuepriority(lwp_thrqueue *queue,lwp_cntrl *thethread,u64 timeout) +{ + u32 level,search_prio,header_idx,prio,block_state,sync_state; + lwp_cntrl *search_thread; + lwp_queue *header; + lwp_node *cur_node,*next_node,*prev_node,*search_node; + + __lwp_queue_init_empty(&thethread->wait.block2n); + + prio = thethread->cur_prio; + header_idx = prio/LWP_THREADQ_PRIOPERHEADER; + header = &queue->queues.priority[header_idx]; + block_state = queue->state; + + if(prio&LWP_THREADQ_REVERSESEARCHMASK) { +#ifdef _LWPTHRQ_DEBUG + printf("__lwp_threadqueue_enqueuepriority(%p,reverse_search)\n",thethread); +#endif + goto reverse_search; + } + +#ifdef _LWPTHRQ_DEBUG + printf("__lwp_threadqueue_enqueuepriority(%p,forward_search)\n",thethread); +#endif +forward_search: + search_prio = LWP_PRIO_MIN - 1; + _CPU_ISR_Disable(level); + search_thread = (lwp_cntrl*)header->first; + while(!__lwp_queue_istail(header,(lwp_node*)search_thread)) { + search_prio = search_thread->cur_prio; + if(prio<=search_prio) break; + _CPU_ISR_Flash(level); + + if(!__lwp_statesset(search_thread->cur_state,block_state)) { + _CPU_ISR_Restore(level); + goto forward_search; + } + search_thread = (lwp_cntrl*)search_thread->object.node.next; + } + if(queue->sync_state!=LWP_THREADQ_NOTHINGHAPPEND) goto synchronize; + queue->sync_state = LWP_THREADQ_SYNCHRONIZED; + if(prio==search_prio) goto equal_prio; + + search_node = (lwp_node*)search_thread; + prev_node = search_node->prev; + cur_node = (lwp_node*)thethread; + + cur_node->next = search_node; + cur_node->prev = prev_node; + prev_node->next = cur_node; + search_node->prev = cur_node; + _CPU_ISR_Restore(level); + return; + +reverse_search: + search_prio = LWP_PRIO_MAX + 1; + _CPU_ISR_Disable(level); + search_thread = (lwp_cntrl*)header->last; + while(!__lwp_queue_ishead(header,(lwp_node*)search_thread)) { + search_prio = search_thread->cur_prio; + if(prio>=search_prio) break; + _CPU_ISR_Flash(level); + + if(!__lwp_statesset(search_thread->cur_state,block_state)) { + _CPU_ISR_Restore(level); + goto reverse_search; + } + search_thread = (lwp_cntrl*)search_thread->object.node.prev; + } + if(queue->sync_state!=LWP_THREADQ_NOTHINGHAPPEND) goto synchronize; + queue->sync_state = LWP_THREADQ_SYNCHRONIZED; + if(prio==search_prio) goto equal_prio; + + search_node = (lwp_node*)search_thread; + next_node = search_node->next; + cur_node = (lwp_node*)thethread; + + cur_node->next = next_node; + cur_node->prev = search_node; + search_node->next = cur_node; + next_node->prev = cur_node; + _CPU_ISR_Restore(level); + return; + +equal_prio: +#ifdef _LWPTHRQ_DEBUG + printf("__lwp_threadqueue_enqueuepriority(%p,equal_prio)\n",thethread); +#endif + search_node = __lwp_queue_tail(&search_thread->wait.block2n); + prev_node = search_node->prev; + cur_node = (lwp_node*)thethread; + + cur_node->next = search_node; + cur_node->prev = prev_node; + prev_node->next = cur_node; + search_node->prev = cur_node; + _CPU_ISR_Restore(level); + return; + +synchronize: + sync_state = queue->sync_state; + queue->sync_state = LWP_THREADQ_SYNCHRONIZED; + +#ifdef _LWPTHRQ_DEBUG + printf("__lwp_threadqueue_enqueuepriority(%p,sync_state = %d)\n",thethread,sync_state); +#endif + switch(sync_state) { + case LWP_THREADQ_SYNCHRONIZED: + break; + case LWP_THREADQ_NOTHINGHAPPEND: + break; + case LWP_THREADQ_TIMEOUT: + thethread->wait.ret_code = thethread->wait.queue->timeout_state; + _CPU_ISR_Restore(level); + break; + case LWP_THREADQ_SATISFIED: + if(__lwp_wd_isactive(&thethread->timer)) { + __lwp_wd_deactivate(&thethread->timer); + _CPU_ISR_Restore(level); + __lwp_wd_remove_ticks(&thethread->timer); + } else + _CPU_ISR_Restore(level); + break; + } + __lwp_thread_unblock(thethread); +} + +lwp_cntrl* __lwp_threadqueue_dequeuepriority(lwp_thrqueue *queue) +{ + u32 level,idx; + lwp_cntrl *newfirstthr,*ret = NULL; + lwp_node *newfirstnode,*newsecnode,*last_node,*next_node,*prev_node; + + _CPU_ISR_Disable(level); + for(idx=0;idxqueues.priority[idx])) { + ret = (lwp_cntrl*)queue->queues.priority[idx].first; + goto dequeue; + } + } + +#ifdef _LWPTHRQ_DEBUG + printf("__lwp_threadqueue_dequeuepriority(%p,sync_state = %d)\n",ret,queue->sync_state); +#endif + switch(queue->sync_state) { + case LWP_THREADQ_SYNCHRONIZED: + case LWP_THREADQ_SATISFIED: + _CPU_ISR_Restore(level); + return NULL; + case LWP_THREADQ_NOTHINGHAPPEND: + case LWP_THREADQ_TIMEOUT: + queue->sync_state = LWP_THREADQ_SATISFIED; + _CPU_ISR_Restore(level); + return _thr_executing; + } + +dequeue: +#ifdef _LWPTHRQ_DEBUG + printf("__lwp_threadqueue_dequeuepriority(%p,dequeue)\n",ret); +#endif + newfirstnode = ret->wait.block2n.first; + newfirstthr = (lwp_cntrl*)newfirstnode; + next_node = ret->object.node.next; + prev_node = ret->object.node.prev; + if(!__lwp_queue_isempty(&ret->wait.block2n)) { + last_node = ret->wait.block2n.last; + newsecnode = newfirstnode->next; + prev_node->next = newfirstnode; + next_node->prev = newfirstnode; + newfirstnode->next = next_node; + newfirstnode->prev = prev_node; + + if(!__lwp_queue_onenode(&ret->wait.block2n)) { + newsecnode->prev = __lwp_queue_head(&newfirstthr->wait.block2n); + newfirstthr->wait.block2n.first = newsecnode; + newfirstthr->wait.block2n.last = last_node; + last_node->next = __lwp_queue_tail(&newfirstthr->wait.block2n); + } + } else { + prev_node->next = next_node; + next_node->prev = prev_node; + } + + if(!__lwp_wd_isactive(&ret->timer)) { + _CPU_ISR_Restore(level); + __lwp_thread_unblock(ret); + } else { + __lwp_wd_deactivate(&ret->timer); + _CPU_ISR_Restore(level); + __lwp_wd_remove_ticks(&ret->timer); + __lwp_thread_unblock(ret); + } + return ret; +} + +void __lwp_threadqueue_init(lwp_thrqueue *queue,u32 mode,u32 state,u32 timeout_state) +{ + u32 index; + + queue->state = state; + queue->mode = mode; + queue->timeout_state = timeout_state; + queue->sync_state = LWP_THREADQ_SYNCHRONIZED; +#ifdef _LWPTHRQ_DEBUG + printf("__lwp_threadqueue_init(%p,%08x,%d,%d)\n",queue,state,timeout_state,mode); +#endif + switch(mode) { + case LWP_THREADQ_MODEFIFO: + __lwp_queue_init_empty(&queue->queues.fifo); + break; + case LWP_THREADQ_MODEPRIORITY: + for(index=0;indexqueues.priority[index]); + break; + } +} + +lwp_cntrl* __lwp_threadqueue_first(lwp_thrqueue *queue) +{ + lwp_cntrl *ret; + + switch(queue->mode) { + case LWP_THREADQ_MODEFIFO: + ret = __lwp_threadqueue_firstfifo(queue); + break; + case LWP_THREADQ_MODEPRIORITY: + ret = __lwp_threadqueue_firstpriority(queue); + break; + default: + ret = NULL; + break; + } + + return ret; +} + +void __lwp_threadqueue_enqueue(lwp_thrqueue *queue,u64 timeout) +{ + lwp_cntrl *thethread; + + thethread = _thr_executing; + __lwp_thread_setstate(thethread,queue->state); + + if(timeout) { + __lwp_wd_initialize(&thethread->timer,__lwp_threadqueue_timeout,thethread->object.id,thethread); + __lwp_wd_insert_ticks(&thethread->timer,timeout); + } + +#ifdef _LWPTHRQ_DEBUG + printf("__lwp_threadqueue_enqueue(%p,%p,%d)\n",queue,thethread,queue->mode); +#endif + switch(queue->mode) { + case LWP_THREADQ_MODEFIFO: + __lwp_threadqueue_enqueuefifo(queue,thethread,timeout); + break; + case LWP_THREADQ_MODEPRIORITY: + __lwp_threadqueue_enqueuepriority(queue,thethread,timeout); + break; + } +} + +lwp_cntrl* __lwp_threadqueue_dequeue(lwp_thrqueue *queue) +{ + lwp_cntrl *ret = NULL; + +#ifdef _LWPTHRQ_DEBUG + printf("__lwp_threadqueue_dequeue(%p,%p,%d,%d)\n",queue,_thr_executing,queue->mode,queue->sync_state); +#endif + switch(queue->mode) { + case LWP_THREADQ_MODEFIFO: + ret = __lwp_threadqueue_dequeuefifo(queue); + break; + case LWP_THREADQ_MODEPRIORITY: + ret = __lwp_threadqueue_dequeuepriority(queue); + break; + default: + ret = NULL; + break; + } +#ifdef _LWPTHRQ_DEBUG + printf("__lwp_threadqueue_dequeue(%p,%p,%d,%d)\n",queue,ret,queue->mode,queue->sync_state); +#endif + return ret; +} + +void __lwp_threadqueue_flush(lwp_thrqueue *queue,u32 status) +{ + lwp_cntrl *thethread; + while((thethread=__lwp_threadqueue_dequeue(queue))) { + thethread->wait.ret_code = status; + } +} + +void __lwp_threadqueue_extract(lwp_thrqueue *queue,lwp_cntrl *thethread) +{ + switch(queue->mode) { + case LWP_THREADQ_MODEFIFO: + __lwp_threadqueue_extractfifo(queue,thethread); + break; + case LWP_THREADQ_MODEPRIORITY: + __lwp_threadqueue_extractpriority(queue,thethread); + break; + } + +} + +void __lwp_threadqueue_extractfifo(lwp_thrqueue *queue,lwp_cntrl *thethread) +{ + u32 level; + + _CPU_ISR_Disable(level); + if(!__lwp_statewaitthreadqueue(thethread->cur_state)) { + _CPU_ISR_Restore(level); + return; + } + + __lwp_queue_extractI(&thethread->object.node); + if(!__lwp_wd_isactive(&thethread->timer)) { + _CPU_ISR_Restore(level); + } else { + __lwp_wd_deactivate(&thethread->timer); + _CPU_ISR_Restore(level); + __lwp_wd_remove_ticks(&thethread->timer); + } + __lwp_thread_unblock(thethread); +} + +void __lwp_threadqueue_extractpriority(lwp_thrqueue *queue,lwp_cntrl *thethread) +{ + u32 level; + lwp_cntrl *first; + lwp_node *curr,*next,*prev,*new_first,*new_sec,*last; + + curr = (lwp_node*)thethread; + + _CPU_ISR_Disable(level); + if(__lwp_statewaitthreadqueue(thethread->cur_state)) { + next = curr->next; + prev = curr->prev; + + if(!__lwp_queue_isempty(&thethread->wait.block2n)) { + new_first = thethread->wait.block2n.first; + first = (lwp_cntrl*)new_first; + last = thethread->wait.block2n.last; + new_sec = new_first->next; + + prev->next = new_first; + next->prev = new_first; + new_first->next = next; + new_first->prev = prev; + + if(!__lwp_queue_onenode(&thethread->wait.block2n)) { + new_sec->prev = __lwp_queue_head(&first->wait.block2n); + first->wait.block2n.first = new_sec; + first->wait.block2n.last = last; + last->next = __lwp_queue_tail(&first->wait.block2n); + } + } else { + prev->next = next; + next->prev = prev; + } + if(!__lwp_wd_isactive(&thethread->timer)) { + _CPU_ISR_Restore(level); + __lwp_thread_unblock(thethread); + } else { + __lwp_wd_deactivate(&thethread->timer); + _CPU_ISR_Restore(level); + __lwp_wd_remove_ticks(&thethread->timer); + __lwp_thread_unblock(thethread); + } + } else + _CPU_ISR_Restore(level); +} + +u32 __lwp_threadqueue_extractproxy(lwp_cntrl *thethread) +{ + u32 state; + + state = thethread->cur_state; + if(__lwp_statewaitthreadqueue(state)) { + __lwp_threadqueue_extract(thethread->wait.queue,thethread); + return TRUE; + } + return FALSE; +} diff --git a/wii/libogc/libogc/lwp_threadq.inl b/wii/libogc/libogc/lwp_threadq.inl new file mode 100644 index 0000000000..cce5c04ad4 --- /dev/null +++ b/wii/libogc/libogc/lwp_threadq.inl @@ -0,0 +1,9 @@ +#ifndef __LWP_THREADQ_INL__ +#define __LWP_THREADQ_INL__ + +static __inline__ void __lwp_threadqueue_csenter(lwp_thrqueue *queue) +{ + queue->sync_state = LWP_THREADQ_NOTHINGHAPPEND; +} + +#endif diff --git a/wii/libogc/libogc/lwp_threads.c b/wii/libogc/libogc/lwp_threads.c new file mode 100644 index 0000000000..f6fe307d1b --- /dev/null +++ b/wii/libogc/libogc/lwp_threads.c @@ -0,0 +1,750 @@ +#include +#include +#include +#include "asm.h" +#include "processor.h" +#include "sys_state.h" +#include "lwp_stack.h" +#include "lwp_threads.h" +#include "lwp_threadq.h" +#include "lwp_watchdog.h" + +#define LWP_MAXPRIORITIES 256 + +/* new one */ +frame_context core_context; + +lwp_cntrl *_thr_main = NULL; +lwp_cntrl *_thr_idle = NULL; + +lwp_cntrl *_thr_executing = NULL; +lwp_cntrl *_thr_heir = NULL; +lwp_cntrl *_thr_allocated_fp = NULL; + +vu32 _context_switch_want; +vu32 _thread_dispatch_disable_level; + +wd_cntrl _lwp_wd_timeslice; +u32 _lwp_ticks_per_timeslice = 0; +void **__lwp_thr_libc_reent = NULL; +lwp_queue _lwp_thr_ready[LWP_MAXPRIORITIES]; + +static void (*_lwp_exitfunc)(void); + +extern void _cpu_context_switch(void *,void *); +extern void _cpu_context_switch_ex(void *,void *); +extern void _cpu_context_save(void *); +extern void _cpu_context_restore(void *); +extern void _cpu_context_save_fp(void *); +extern void _cpu_context_restore_fp(void *); + +extern int __libc_create_hook(lwp_cntrl *,lwp_cntrl *); +extern int __libc_start_hook(lwp_cntrl *,lwp_cntrl *); +extern int __libc_delete_hook(lwp_cntrl *, lwp_cntrl *); + +extern void kprintf(const char *str, ...); + +#ifdef _LWPTHREADS_DEBUG +static void __lwp_dumpcontext(frame_context *ctx) +{ + kprintf("GPR00 %08x GPR08 %08x GPR16 %08x GPR24 %08x\n",ctx->GPR[0], ctx->GPR[8], ctx->GPR[16], ctx->GPR[24]); + kprintf("GPR01 %08x GPR09 %08x GPR17 %08x GPR25 %08x\n",ctx->GPR[1], ctx->GPR[9], ctx->GPR[17], ctx->GPR[25]); + kprintf("GPR02 %08x GPR10 %08x GPR18 %08x GPR26 %08x\n",ctx->GPR[2], ctx->GPR[10], ctx->GPR[18], ctx->GPR[26]); + kprintf("GPR03 %08x GPR11 %08x GPR19 %08x GPR27 %08x\n",ctx->GPR[3], ctx->GPR[11], ctx->GPR[19], ctx->GPR[27]); + kprintf("GPR04 %08x GPR12 %08x GPR20 %08x GPR28 %08x\n",ctx->GPR[4], ctx->GPR[12], ctx->GPR[20], ctx->GPR[28]); + kprintf("GPR05 %08x GPR13 %08x GPR21 %08x GPR29 %08x\n",ctx->GPR[5], ctx->GPR[13], ctx->GPR[21], ctx->GPR[29]); + kprintf("GPR06 %08x GPR14 %08x GPR22 %08x GPR30 %08x\n",ctx->GPR[6], ctx->GPR[14], ctx->GPR[22], ctx->GPR[30]); + kprintf("GPR07 %08x GPR15 %08x GPR23 %08x GPR31 %08x\n",ctx->GPR[7], ctx->GPR[15], ctx->GPR[23], ctx->GPR[31]); + kprintf("LR %08x SRR0 %08x SRR1 %08x MSR %08x\n\n", ctx->LR, ctx->SRR0, ctx->SRR1,ctx->MSR); +} + +void __lwp_showmsr() +{ + register u32 msr; + _CPU_MSR_GET(msr); + kprintf("msr: %08x\n",msr); +} + +void __lwp_dumpcontext_fp(lwp_cntrl *thrA,lwp_cntrl *thrB) +{ + kprintf("_cpu_contextfp_dump(%p,%p)\n",thrA,thrB); +} +#endif + +/* +void __lwp_getthreadlist(lwp_obj **thrs) +{ + *thrs = _lwp_objects; +} +*/ +u32 __lwp_isr_in_progress() +{ + register u32 isr_nest_level; + isr_nest_level = mfspr(272); + return isr_nest_level; +} + +static inline void __lwp_msr_setlevel(u32 level) +{ + register u32 msr; + _CPU_MSR_GET(msr); + if(!(level&CPU_MODES_INTERRUPT_MASK)) + msr |= MSR_EE; + else + msr &= ~MSR_EE; + _CPU_MSR_SET(msr); +} + +static inline u32 __lwp_msr_getlevel() +{ + register u32 msr; + _CPU_MSR_GET(msr); + if(msr&MSR_EE) return 0; + else return 1; +} + +void __lwp_thread_delayended(void *arg) +{ + lwp_cntrl *thethread = (lwp_cntrl*)arg; +#ifdef _LWPTHREADS_DEBUG + kprintf("__lwp_thread_delayended(%p)\n",thethread); +#endif + if(!thethread) return; + + __lwp_thread_dispatchdisable(); + __lwp_thread_unblock(thethread); + __lwp_thread_dispatchunnest(); +} + +void __lwp_thread_tickle_timeslice(void *arg) +{ + s64 ticks; + lwp_cntrl *exec; + + exec = _thr_executing; + ticks = millisecs_to_ticks(1); + + __lwp_thread_dispatchdisable(); + + if(!exec->is_preemptible) { + __lwp_wd_insert_ticks(&_lwp_wd_timeslice,ticks); + __lwp_thread_dispatchunnest(); + return; + } + if(!__lwp_stateready(exec->cur_state)) { + __lwp_wd_insert_ticks(&_lwp_wd_timeslice,ticks); + __lwp_thread_dispatchunnest(); + return; + } + + switch(exec->budget_algo) { + case LWP_CPU_BUDGET_ALGO_NONE: + break; + case LWP_CPU_BUDGET_ALGO_TIMESLICE: + if((--exec->cpu_time_budget)==0) { + __lwp_thread_resettimeslice(); + exec->cpu_time_budget = _lwp_ticks_per_timeslice; + } + break; + } + + __lwp_wd_insert_ticks(&_lwp_wd_timeslice,ticks); + __lwp_thread_dispatchunnest(); +} + +void __thread_dispatch_fp() +{ + u32 level; + lwp_cntrl *exec; + + _CPU_ISR_Disable(level); + exec = _thr_executing; +#ifdef _LWPTHREADS_DEBUG + __lwp_dumpcontext_fp(exec,_thr_allocated_fp); +#endif + if(!__lwp_thread_isallocatedfp(exec)) { + if(_thr_allocated_fp) _cpu_context_save_fp(&_thr_allocated_fp->context); + _cpu_context_restore_fp(&exec->context); + _thr_allocated_fp = exec; + } + _CPU_ISR_Restore(level); +} + +void __thread_dispatch() +{ + u32 level; + lwp_cntrl *exec,*heir; + + _CPU_ISR_Disable(level); + exec = _thr_executing; + while(_context_switch_want==TRUE) { + heir = _thr_heir; + _thread_dispatch_disable_level = 1; + _context_switch_want = FALSE; + _thr_executing = heir; + _CPU_ISR_Restore(level); + + if(__lwp_thr_libc_reent) { + exec->libc_reent = *__lwp_thr_libc_reent; + *__lwp_thr_libc_reent = heir->libc_reent; + } +#ifdef _DEBUG + _cpu_context_switch_ex((void*)&exec->context,(void*)&heir->context); +#else + _cpu_context_switch((void*)&exec->context,(void*)&heir->context); +#endif + exec = _thr_executing; + _CPU_ISR_Disable(level); + } + _thread_dispatch_disable_level = 0; + _CPU_ISR_Restore(level); +} + +static void __lwp_thread_handler() +{ + u32 level; + lwp_cntrl *exec; + + exec = _thr_executing; +#ifdef _LWPTHREADS_DEBUG + kprintf("__lwp_thread_handler(%p,%d)\n",exec,_thread_dispatch_disable_level); +#endif + level = exec->isr_level; + __lwp_msr_setlevel(level); + __lwp_thread_dispatchenable(); + exec->wait.ret_arg = exec->entry(exec->arg); + + __lwp_thread_exit(exec->wait.ret_arg); +#ifdef _LWPTHREADS_DEBUG + kprintf("__lwp_thread_handler(%p): thread returned(%p)\n",exec,exec->wait.ret_arg); +#endif +} + +void __lwp_rotate_readyqueue(u32 prio) +{ + u32 level; + lwp_cntrl *exec; + lwp_queue *ready; + lwp_node *node; + + ready = &_lwp_thr_ready[prio]; + exec = _thr_executing; + + if(ready==exec->ready) { + __lwp_thread_yield(); + return; + } + + _CPU_ISR_Disable(level); + if(!__lwp_queue_isempty(ready) && !__lwp_queue_onenode(ready)) { + node = __lwp_queue_firstnodeI(ready); + __lwp_queue_appendI(ready,node); + } + _CPU_ISR_Flash(level); + + if(_thr_heir->ready==ready) + _thr_heir = (lwp_cntrl*)ready->first; + + if(exec!=_thr_heir) + _context_switch_want = TRUE; + +#ifdef _LWPTHREADS_DEBUG + kprintf("__lwp_rotate_readyqueue(%d,%p,%p)\n",prio,exec,_thr_heir); +#endif + _CPU_ISR_Restore(level); +} + +void __lwp_thread_yield() +{ + u32 level; + lwp_cntrl *exec; + lwp_queue *ready; + + exec = _thr_executing; + ready = exec->ready; + + _CPU_ISR_Disable(level); + if(!__lwp_queue_onenode(ready)) { + __lwp_queue_extractI(&exec->object.node); + __lwp_queue_appendI(ready,&exec->object.node); + _CPU_ISR_Flash(level); + if(__lwp_thread_isheir(exec)) + _thr_heir = (lwp_cntrl*)ready->first; + _context_switch_want = TRUE; + } else if(!__lwp_thread_isheir(exec)) + _context_switch_want = TRUE; + _CPU_ISR_Restore(level); +} + +void __lwp_thread_resettimeslice() +{ + u32 level; + lwp_cntrl *exec; + lwp_queue *ready; + + exec = _thr_executing; + ready = exec->ready; + + _CPU_ISR_Disable(level); + if(__lwp_queue_onenode(ready)) { + _CPU_ISR_Restore(level); + return; + } + + __lwp_queue_extractI(&exec->object.node); + __lwp_queue_appendI(ready,&exec->object.node); + + _CPU_ISR_Flash(level); + + if(__lwp_thread_isheir(exec)) + _thr_heir = (lwp_cntrl*)ready->first; + + _context_switch_want = TRUE; + _CPU_ISR_Restore(level); +} + +void __lwp_thread_setstate(lwp_cntrl *thethread,u32 state) +{ + u32 level; + lwp_queue *ready; + + ready = thethread->ready; +#ifdef _LWPTHREADS_DEBUG + kprintf("__lwp_thread_setstate(%d,%p,%p,%08x)\n",_context_switch_want,_thr_heir,thethread,thethread->cur_state); +#endif + _CPU_ISR_Disable(level); + if(!__lwp_stateready(thethread->cur_state)) { + thethread->cur_state = __lwp_clearstate(thethread->cur_state,state); + _CPU_ISR_Restore(level); + return; + } + + thethread->cur_state = state; + if(__lwp_queue_onenode(ready)) { + __lwp_queue_init_empty(ready); + __lwp_priomap_removefrom(&thethread->priomap); + } else + __lwp_queue_extractI(&thethread->object.node); + _CPU_ISR_Flash(level); + + if(__lwp_thread_isheir(thethread)) + __lwp_thread_calcheir(); + if(__lwp_thread_isexec(thethread)) + _context_switch_want = TRUE; +#ifdef _LWPTHREADS_DEBUG + kprintf("__lwp_thread_setstate(%d,%p,%p,%08x)\n",_context_switch_want,_thr_heir,thethread,thethread->cur_state); +#endif + _CPU_ISR_Restore(level); +} + +void __lwp_thread_clearstate(lwp_cntrl *thethread,u32 state) +{ + u32 level,cur_state; + + _CPU_ISR_Disable(level); + + cur_state = thethread->cur_state; + if(cur_state&state) { + cur_state = thethread->cur_state = __lwp_clearstate(cur_state,state); + if(__lwp_stateready(cur_state)) { + __lwp_priomap_addto(&thethread->priomap); + __lwp_queue_appendI(thethread->ready,&thethread->object.node); + _CPU_ISR_Flash(level); + + if(thethread->cur_prio<_thr_heir->cur_prio) { + _thr_heir = thethread; + if(_thr_executing->is_preemptible + || thethread->cur_prio==0) + _context_switch_want = TRUE; + } + } + } + + _CPU_ISR_Restore(level); +} + +u32 __lwp_evaluatemode() +{ + lwp_cntrl *exec; + + exec = _thr_executing; + if(!__lwp_stateready(exec->cur_state) + || (!__lwp_thread_isheir(exec) && exec->is_preemptible)){ + _context_switch_want = TRUE; + return TRUE; + } + return FALSE; +} + +void __lwp_thread_changepriority(lwp_cntrl *thethread,u32 prio,u32 prependit) +{ + u32 level; + + __lwp_thread_settransient(thethread); + + if(thethread->cur_prio!=prio) + __lwp_thread_setpriority(thethread,prio); + + _CPU_ISR_Disable(level); + + thethread->cur_state = __lwp_clearstate(thethread->cur_state,LWP_STATES_TRANSIENT); + if(!__lwp_stateready(thethread->cur_state)) { + _CPU_ISR_Restore(level); + return; + } + + __lwp_priomap_addto(&thethread->priomap); + if(prependit) + __lwp_queue_prependI(thethread->ready,&thethread->object.node); + else + __lwp_queue_appendI(thethread->ready,&thethread->object.node); + + _CPU_ISR_Flash(level); + + __lwp_thread_calcheir(); + + if(!(_thr_executing==_thr_heir) + && _thr_executing->is_preemptible) + _context_switch_want = TRUE; + + _CPU_ISR_Restore(level); +} + +void __lwp_thread_setpriority(lwp_cntrl *thethread,u32 prio) +{ + thethread->cur_prio = prio; + thethread->ready = &_lwp_thr_ready[prio]; + __lwp_priomap_init(&thethread->priomap,prio); +#ifdef _LWPTHREADS_DEBUG + kprintf("__lwp_thread_setpriority(%p,%d,%p)\n",thethread,prio,thethread->ready); +#endif +} + +void __lwp_thread_suspend(lwp_cntrl *thethread) +{ + u32 level; + lwp_queue *ready; + + ready = thethread->ready; + + _CPU_ISR_Disable(level); + thethread->suspendcnt++; + if(!__lwp_stateready(thethread->cur_state)) { + thethread->cur_state = __lwp_setstate(thethread->cur_state,LWP_STATES_SUSPENDED); + _CPU_ISR_Restore(level); + return; + } + + thethread->cur_state = LWP_STATES_SUSPENDED; + if(__lwp_queue_onenode(ready)) { + __lwp_queue_init_empty(ready); + __lwp_priomap_removefrom(&thethread->priomap); + } else { + __lwp_queue_extractI(&thethread->object.node); + } + _CPU_ISR_Flash(level); + + if(__lwp_thread_isheir(thethread)) + __lwp_thread_calcheir(); + + if(__lwp_thread_isexec(thethread)) + _context_switch_want = TRUE; + + _CPU_ISR_Restore(level); +} + +void __lwp_thread_settransient(lwp_cntrl *thethread) +{ + u32 level,oldstates; + lwp_queue *ready; + + ready = thethread->ready; + + _CPU_ISR_Disable(level); + + oldstates = thethread->cur_state; + thethread->cur_state = __lwp_setstate(oldstates,LWP_STATES_TRANSIENT); + + if(__lwp_stateready(oldstates)) { + if(__lwp_queue_onenode(ready)) { + __lwp_queue_init_empty(ready); + __lwp_priomap_removefrom(&thethread->priomap); + } else { + __lwp_queue_extractI(&thethread->object.node); + } + } + + _CPU_ISR_Restore(level); +} + +void __lwp_thread_resume(lwp_cntrl *thethread,u32 force) +{ + u32 level,state; + + _CPU_ISR_Disable(level); + + if(force==TRUE) + thethread->suspendcnt = 0; + else + thethread->suspendcnt--; + + if(thethread->suspendcnt>0) { + _CPU_ISR_Restore(level); + return; + } + + state = thethread->cur_state; + if(state&LWP_STATES_SUSPENDED) { + state = thethread->cur_state = __lwp_clearstate(thethread->cur_state,LWP_STATES_SUSPENDED); + if(__lwp_stateready(state)) { + __lwp_priomap_addto(&thethread->priomap); + __lwp_queue_appendI(thethread->ready,&thethread->object.node); + _CPU_ISR_Flash(level); + if(thethread->cur_prio<_thr_heir->cur_prio) { + _thr_heir = thethread; + if(_thr_executing->is_preemptible + || thethread->cur_prio==0) + _context_switch_want = TRUE; + } + } + } + _CPU_ISR_Restore(level); +} + +void __lwp_thread_loadenv(lwp_cntrl *thethread) +{ + u32 stackbase,sp,size; + u32 r2,r13,msr_value; + + thethread->context.FPSCR = 0x000000f8; + + stackbase = (u32)thethread->stack; + size = thethread->stack_size; + + // tag both bottom & head of stack + *((u32*)stackbase) = 0xDEADBABE; + sp = stackbase+size-CPU_MINIMUM_STACK_FRAME_SIZE; + sp &= ~(CPU_STACK_ALIGNMENT-1); + *((u32*)sp) = 0; + + thethread->context.GPR[1] = sp; + + msr_value = (MSR_ME|MSR_IR|MSR_DR|MSR_RI); + if(!(thethread->isr_level&CPU_MODES_INTERRUPT_MASK)) + msr_value |= MSR_EE; + + thethread->context.MSR = msr_value; + thethread->context.LR = (u32)__lwp_thread_handler; + + __asm__ __volatile__ ("mr %0,2; mr %1,13" : "=r" ((r2)), "=r" ((r13))); + thethread->context.GPR[2] = r2; + thethread->context.GPR[13] = r13; + +#ifdef _LWPTHREADS_DEBUG + kprintf("__lwp_thread_loadenv(%p,%p,%d,%p)\n",thethread,(void*)stackbase,size,(void*)sp); +#endif + +} + +void __lwp_thread_ready(lwp_cntrl *thethread) +{ + u32 level; + lwp_cntrl *heir; + + _CPU_ISR_Disable(level); +#ifdef _LWPTHREADS_DEBUG + kprintf("__lwp_thread_ready(%p)\n",thethread); +#endif + thethread->cur_state = LWP_STATES_READY; + __lwp_priomap_addto(&thethread->priomap); + __lwp_queue_appendI(thethread->ready,&thethread->object.node); + _CPU_ISR_Flash(level); + + __lwp_thread_calcheir(); + heir = _thr_heir; + if(!(__lwp_thread_isexec(heir)) && _thr_executing->is_preemptible) + _context_switch_want = TRUE; + + _CPU_ISR_Restore(level); +} + +u32 __lwp_thread_init(lwp_cntrl *thethread,void *stack_area,u32 stack_size,u32 prio,u32 isr_level,bool is_preemtible) +{ + u32 act_stack_size = 0; + +#ifdef _LWPTHREADS_DEBUG + kprintf("__lwp_thread_init(%p,%p,%d,%d,%d)\n",thethread,stack_area,stack_size,prio,isr_level); +#endif + + if(!stack_area) { + if(!__lwp_stack_isenough(stack_size)) + act_stack_size = CPU_MINIMUM_STACK_SIZE; + else + act_stack_size = stack_size; + + act_stack_size = __lwp_stack_allocate(thethread,act_stack_size); + if(!act_stack_size) return 0; + + thethread->stack_allocated = TRUE; + } else { + thethread->stack = stack_area; + act_stack_size = stack_size; + thethread->stack_allocated = FALSE; + } + thethread->stack_size = act_stack_size; + + __lwp_threadqueue_init(&thethread->join_list,LWP_THREADQ_MODEFIFO,LWP_STATES_WAITING_FOR_JOINATEXIT,0); + + memset(&thethread->context,0,sizeof(thethread->context)); + memset(&thethread->wait,0,sizeof(thethread->wait)); + + thethread->budget_algo = (prio<128 ? LWP_CPU_BUDGET_ALGO_NONE : LWP_CPU_BUDGET_ALGO_TIMESLICE); + thethread->is_preemptible = is_preemtible; + thethread->isr_level = isr_level; + thethread->real_prio = prio; + thethread->cur_state = LWP_STATES_DORMANT; + thethread->cpu_time_budget = _lwp_ticks_per_timeslice; + thethread->suspendcnt = 0; + thethread->res_cnt = 0; + __lwp_thread_setpriority(thethread,prio); + + __libc_create_hook(_thr_executing,thethread); + + return 1; +} + +void __lwp_thread_close(lwp_cntrl *thethread) +{ + u32 level; + void **value_ptr; + lwp_cntrl *p; + + __lwp_thread_setstate(thethread,LWP_STATES_TRANSIENT); + + if(!__lwp_threadqueue_extractproxy(thethread)) { + if(__lwp_wd_isactive(&thethread->timer)) + __lwp_wd_remove_ticks(&thethread->timer); + } + + _CPU_ISR_Disable(level); + value_ptr = (void**)thethread->wait.ret_arg; + while((p=__lwp_threadqueue_dequeue(&thethread->join_list))!=NULL) { + *(void**)p->wait.ret_arg = value_ptr; + } + thethread->cpu_time_budget = 0; + thethread->budget_algo = LWP_CPU_BUDGET_ALGO_NONE; + _CPU_ISR_Restore(level); + + __libc_delete_hook(_thr_executing,thethread); + + if(__lwp_thread_isallocatedfp(thethread)) + __lwp_thread_deallocatefp(); + + __lwp_stack_free(thethread); + + __lwp_objmgr_close(thethread->object.information,&thethread->object); + __lwp_objmgr_free(thethread->object.information,&thethread->object); +} + +void __lwp_thread_closeall() +{ + u32 i,level; + lwp_queue *header; + lwp_cntrl *ptr,*next; +#ifdef _LWPTHREADS_DEBUG + kprintf("__lwp_thread_closeall(enter)\n"); +#endif + _CPU_ISR_Disable(level); + for(i=0;ifirst; + while(ptr!=(lwp_cntrl*)__lwp_queue_tail(&_lwp_thr_ready[i])) { + next = (lwp_cntrl*)ptr->object.node.next; + if(ptr!=_thr_executing) + __lwp_thread_close(ptr); + + ptr = next; + } + } + _CPU_ISR_Restore(level); +#ifdef _LWPTHREADS_DEBUG + kprintf("__lwp_thread_closeall(leave)\n"); +#endif +} + +void __lwp_thread_exit(void *value_ptr) +{ + __lwp_thread_dispatchdisable(); + _thr_executing->wait.ret_arg = (u32*)value_ptr; + __lwp_thread_close(_thr_executing); + __lwp_thread_dispatchenable(); +} + +u32 __lwp_thread_start(lwp_cntrl *thethread,void* (*entry)(void*),void *arg) +{ +#ifdef _LWPTHREADS_DEBUG + kprintf("__lwp_thread_start(%p,%p,%p,%d)\n",thethread,entry,arg,thethread->cur_state); +#endif + if(__lwp_statedormant(thethread->cur_state)) { + thethread->entry = entry; + thethread->arg = arg; + __lwp_thread_loadenv(thethread); + __lwp_thread_ready(thethread); + __libc_start_hook(_thr_executing,thethread); + return 1; + } + return 0; +} + +void __lwp_thread_startmultitasking() +{ + _lwp_exitfunc = NULL; + + __sys_state_set(SYS_STATE_BEGIN_MT); + __sys_state_set(SYS_STATE_UP); + + _context_switch_want = FALSE; + _thr_executing = _thr_heir; +#ifdef _LWPTHREADS_DEBUG + kprintf("__lwp_start_multitasking(%p,%p)\n",_thr_executing,_thr_heir); +#endif + __lwp_thread_starttimeslice(); + _cpu_context_switch((void*)&core_context,(void*)&_thr_heir->context); + + if(_lwp_exitfunc) _lwp_exitfunc(); +} + +void __lwp_thread_stopmultitasking(void (*exitfunc)()) +{ + _lwp_exitfunc = exitfunc; + if(__sys_state_get()!=SYS_STATE_SHUTDOWN) { + __lwp_thread_stoptimeslice(); + __sys_state_set(SYS_STATE_SHUTDOWN); + _cpu_context_switch((void*)&_thr_executing->context,(void*)&core_context); + } +} + +void __lwp_thread_coreinit() +{ + u32 index; + +#ifdef _LWPTHREADS_DEBUG + kprintf("__lwp_sys_init()\n\n"); +#endif + __lwp_thread_dispatchinitialize(); + __lwp_thread_inittimeslice(); + + _context_switch_want = FALSE; + _thr_executing = NULL; + _thr_heir = NULL; + _thr_allocated_fp = NULL; + _lwp_ticks_per_timeslice = 10; + + memset(&core_context,0,sizeof(core_context)); + + for(index=0;index<=LWP_PRIO_MAX;index++) + __lwp_queue_init_empty(&_lwp_thr_ready[index]); + + __sys_state_set(SYS_STATE_BEFORE_MT); +} + diff --git a/wii/libogc/libogc/lwp_threads.inl b/wii/libogc/libogc/lwp_threads.inl new file mode 100644 index 0000000000..b6a7278eff --- /dev/null +++ b/wii/libogc/libogc/lwp_threads.inl @@ -0,0 +1,90 @@ +#ifndef __LWP_INL__ +#define __LWP_INL__ + +static __inline__ u32 __lwp_thread_isexec(lwp_cntrl *thethread) +{ + return (thethread==_thr_executing); +} + +static __inline__ u32 __lwp_thread_isheir(lwp_cntrl *thethread) +{ + return (thethread==_thr_heir); +} + +static __inline__ void __lwp_thread_calcheir() +{ + _thr_heir = (lwp_cntrl*)_lwp_thr_ready[__lwp_priomap_highest()].first; +} + +static __inline__ u32 __lwp_thread_isallocatedfp(lwp_cntrl *thethread) +{ + return (thethread==_thr_allocated_fp); +} + +static __inline__ void __lwp_thread_deallocatefp() +{ + _thr_allocated_fp = NULL; +} + +static __inline__ void __lwp_thread_dispatchinitialize() +{ + _thread_dispatch_disable_level = 1; +} + +static __inline__ void __lwp_thread_dispatchenable() +{ + if((--_thread_dispatch_disable_level)==0) + __thread_dispatch(); +} + +static __inline__ void __lwp_thread_dispatchdisable() +{ + ++_thread_dispatch_disable_level; +} + +static __inline__ void __lwp_thread_dispatchunnest() +{ + --_thread_dispatch_disable_level; +} + +static __inline__ void __lwp_thread_unblock(lwp_cntrl *thethread) +{ + __lwp_thread_clearstate(thethread,LWP_STATES_BLOCKED); +} + +static __inline__ void** __lwp_thread_getlibcreent() +{ + return __lwp_thr_libc_reent; +} + +static __inline__ void __lwp_thread_setlibcreent(void **libc_reent) +{ + __lwp_thr_libc_reent = libc_reent; +} + +static __inline__ bool __lwp_thread_isswitchwant() +{ + + return _context_switch_want; +} + +static __inline__ bool __lwp_thread_isdispatchenabled() +{ + return (_thread_dispatch_disable_level==0); +} + +static __inline__ void __lwp_thread_inittimeslice() +{ + __lwp_wd_initialize(&_lwp_wd_timeslice,__lwp_thread_tickle_timeslice,LWP_TIMESLICE_TIMER_ID,NULL); +} + +static __inline__ void __lwp_thread_starttimeslice() +{ + __lwp_wd_insert_ticks(&_lwp_wd_timeslice,millisecs_to_ticks(1)); +} + +static __inline__ void __lwp_thread_stoptimeslice() +{ + __lwp_wd_remove_ticks(&_lwp_wd_timeslice); +} +#endif diff --git a/wii/libogc/libogc/lwp_watchdog.c b/wii/libogc/libogc/lwp_watchdog.c new file mode 100644 index 0000000000..63390bb358 --- /dev/null +++ b/wii/libogc/libogc/lwp_watchdog.c @@ -0,0 +1,192 @@ +#include +#include +#include "asm.h" +#include "lwp_threads.h" +#include "lwp_watchdog.h" + +//#define _LWPWD_DEBUG + +#ifdef _LWPWD_DEBUG +#include +#endif + +vu32 _wd_sync_level; +vu32 _wd_sync_count; +u32 _wd_ticks_since_boot; + +lwp_queue _wd_ticks_queue; + +static void __lwp_wd_settimer(wd_cntrl *wd) +{ + u64 now; + s64 diff; + union uulc { + u64 ull; + u32 ul[2]; + } v; + + now = gettime(); + v.ull = diff = diff_ticks(now,wd->fire); +#ifdef _LWPWD_DEBUG + printf("__lwp_wd_settimer(%p,%llu,%lld)\n",wd,wd->fire,diff); +#endif + if(diff<=0) { +#ifdef _LWPWD_DEBUG + printf(" __lwp_wd_settimer(0): %lld<=0\n",diff); +#endif + wd->fire = 0; + mtdec(0); + } else if(diff<0x0000000080000000LL) { +#ifdef _LWPWD_DEBUG + printf("__lwp_wd_settimer(%d): %lld<0x0000000080000000LL\n",v.ul[1],diff); +#endif + mtdec(v.ul[1]); + } else { +#ifdef _LWPWD_DEBUG + printf("__lwp_wd_settimer(0x7fffffff)\n"); +#endif + mtdec(0x7fffffff); + } +} + +void __lwp_watchdog_init() +{ + _wd_sync_level = 0; + _wd_sync_count = 0; + _wd_ticks_since_boot = 0; + + __lwp_queue_init_empty(&_wd_ticks_queue); +} + +void __lwp_wd_insert(lwp_queue *header,wd_cntrl *wd) +{ + u32 level; + u64 fire; + u32 isr_nest_level; + wd_cntrl *after; +#ifdef _LWPWD_DEBUG + printf("__lwp_wd_insert(%p,%llu,%llu)\n",wd,wd->start,wd->fire); +#endif + isr_nest_level = __lwp_isr_in_progress(); + wd->state = LWP_WD_INSERTED; + + _wd_sync_count++; +restart: + _CPU_ISR_Disable(level); + fire = wd->fire; + for(after=__lwp_wd_first(header);;after=__lwp_wd_next(after)) { + if(fire==0 || !__lwp_wd_next(after)) break; + if(firefire) break; + + _CPU_ISR_Flash(level); + if(wd->state!=LWP_WD_INSERTED) goto exit_insert; + if(_wd_sync_level>isr_nest_level) { + _wd_sync_level = isr_nest_level; + _CPU_ISR_Restore(level); + goto restart; + } + } + __lwp_wd_activate(wd); + wd->fire = fire; + __lwp_queue_insertI(after->node.prev,&wd->node); + if(__lwp_wd_first(header)==wd) __lwp_wd_settimer(wd); + +exit_insert: + _wd_sync_level = isr_nest_level; + _wd_sync_count--; + _CPU_ISR_Restore(level); + return; +} + +u32 __lwp_wd_remove(lwp_queue *header,wd_cntrl *wd) +{ + u32 level; + u32 prev_state; + wd_cntrl *next; +#ifdef _LWPWD_DEBUG + printf("__lwp_wd_remove(%p)\n",wd); +#endif + _CPU_ISR_Disable(level); + prev_state = wd->state; + switch(prev_state) { + case LWP_WD_INACTIVE: + break; + case LWP_WD_INSERTED: + wd->state = LWP_WD_INACTIVE; + break; + case LWP_WD_ACTIVE: + case LWP_WD_REMOVE: + wd->state = LWP_WD_INACTIVE; + next = __lwp_wd_next(wd); + if(_wd_sync_count) _wd_sync_level = __lwp_isr_in_progress(); + __lwp_queue_extractI(&wd->node); + if(!__lwp_queue_isempty(header) && __lwp_wd_first(header)==next) __lwp_wd_settimer(next); + break; + } + _CPU_ISR_Restore(level); + return prev_state; +} + +void __lwp_wd_tickle(lwp_queue *queue) +{ + wd_cntrl *wd; + u64 now; + s64 diff; + + if(__lwp_queue_isempty(queue)) return; + + wd = __lwp_wd_first(queue); + now = gettime(); + diff = diff_ticks(now,wd->fire); +#ifdef _LWPWD_DEBUG + printf("__lwp_wd_tickle(%p,%08x%08x,%08x%08x,%08x%08x,%08x%08x)\n",wd,(u32)(now>>32),(u32)now,(u32)(wd->start>>32),(u32)wd->start,(u32)(wd->fire>>32),(u32)wd->fire,(u32)(diff>>32),(u32)diff); +#endif + if(diff<=0) { + do { + switch(__lwp_wd_remove(queue,wd)) { + case LWP_WD_ACTIVE: + wd->routine(wd->usr_data); + break; + case LWP_WD_INACTIVE: + break; + case LWP_WD_INSERTED: + break; + case LWP_WD_REMOVE: + break; + } + wd = __lwp_wd_first(queue); + } while(!__lwp_queue_isempty(queue) && wd->fire==0); + } else { + __lwp_wd_reset(wd); + } +} + +void __lwp_wd_adjust(lwp_queue *queue,u32 dir,s64 interval) +{ + u32 level; + u64 abs_int; + + _CPU_ISR_Disable(level); + abs_int = gettime()+LWP_WD_ABS(interval); + if(!__lwp_queue_isempty(queue)) { + switch(dir) { + case LWP_WD_BACKWARD: + __lwp_wd_first(queue)->fire += LWP_WD_ABS(interval); + break; + case LWP_WD_FORWARD: + while(abs_int) { + if(abs_int<__lwp_wd_first(queue)->fire) { + __lwp_wd_first(queue)->fire -= LWP_WD_ABS(interval); + break; + } else { + abs_int -= __lwp_wd_first(queue)->fire; + __lwp_wd_first(queue)->fire = gettime(); + __lwp_wd_tickle(queue); + if(__lwp_queue_isempty(queue)) break; + } + } + break; + } + } + _CPU_ISR_Restore(level); +} diff --git a/wii/libogc/libogc/lwp_watchdog.inl b/wii/libogc/libogc/lwp_watchdog.inl new file mode 100644 index 0000000000..5216e10996 --- /dev/null +++ b/wii/libogc/libogc/lwp_watchdog.inl @@ -0,0 +1,84 @@ +#ifndef __LWP_WATCHDOG_INL__ +#define __LWP_WATCHDOG_INL__ + +static __inline__ void __lwp_wd_initialize(wd_cntrl *wd,wd_service_routine routine,u32 id,void *usr_data) +{ + wd->state = LWP_WD_INACTIVE; + wd->id = id; + wd->routine = routine; + wd->usr_data = usr_data; +} + +static __inline__ wd_cntrl* __lwp_wd_first(lwp_queue *queue) +{ + return (wd_cntrl*)queue->first; +} + +static __inline__ wd_cntrl* __lwp_wd_last(lwp_queue *queue) +{ + return (wd_cntrl*)queue->last; +} + +static __inline__ wd_cntrl* __lwp_wd_next(wd_cntrl *wd) +{ + return (wd_cntrl*)wd->node.next; +} + +static __inline__ wd_cntrl* __lwp_wd_prev(wd_cntrl *wd) +{ + return (wd_cntrl*)wd->node.prev; +} + +static __inline__ void __lwp_wd_activate(wd_cntrl *wd) +{ + wd->state = LWP_WD_ACTIVE; +} + +static __inline__ void __lwp_wd_deactivate(wd_cntrl *wd) +{ + wd->state = LWP_WD_REMOVE; +} + +static __inline__ u32 __lwp_wd_isactive(wd_cntrl *wd) +{ + return (wd->state==LWP_WD_ACTIVE); +} + +static __inline__ u64 __lwp_wd_calc_ticks(const struct timespec *time) +{ + u64 ticks; + + ticks = secs_to_ticks(time->tv_sec); + ticks += nanosecs_to_ticks(time->tv_nsec); + + return ticks; +} + +static __inline__ void __lwp_wd_tickle_ticks() +{ + __lwp_wd_tickle(&_wd_ticks_queue); +} + +static __inline__ void __lwp_wd_insert_ticks(wd_cntrl *wd,s64 interval) +{ + wd->start = gettime(); + wd->fire = (wd->start+LWP_WD_ABS(interval)); + __lwp_wd_insert(&_wd_ticks_queue,wd); +} + +static __inline__ void __lwp_wd_adjust_ticks(u32 dir,s64 interval) +{ + __lwp_wd_adjust(&_wd_ticks_queue,dir,interval); +} + +static __inline__ void __lwp_wd_remove_ticks(wd_cntrl *wd) +{ + __lwp_wd_remove(&_wd_ticks_queue,wd); +} + +static __inline__ void __lwp_wd_reset(wd_cntrl *wd) +{ + __lwp_wd_remove(&_wd_ticks_queue,wd); + __lwp_wd_insert(&_wd_ticks_queue,wd); +} +#endif diff --git a/wii/libogc/libogc/lwp_wkspace.c b/wii/libogc/libogc/lwp_wkspace.c new file mode 100644 index 0000000000..a9d92d4968 --- /dev/null +++ b/wii/libogc/libogc/lwp_wkspace.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include "system.h" +#include "lwp_wkspace.h" + +#define ROUND32UP(v) (((u32)(v)+31)&~31) + +heap_cntrl __wkspace_heap; +static heap_iblock __wkspace_iblock; +static u32 __wkspace_heap_size = 0; + +u32 __lwp_wkspace_heapsize() +{ + return __wkspace_heap_size; +} + +u32 __lwp_wkspace_heapfree() +{ + __lwp_heap_getinfo(&__wkspace_heap,&__wkspace_iblock); + return __wkspace_iblock.free_size; +} + +u32 __lwp_wkspace_heapused() +{ + __lwp_heap_getinfo(&__wkspace_heap,&__wkspace_iblock); + return __wkspace_iblock.used_size; +} + +void __lwp_wkspace_init(u32 size) +{ + u32 arLo,level,dsize; + + // Get current ArenaLo and adjust to 32-byte boundary + _CPU_ISR_Disable(level); + arLo = ROUND32UP(SYS_GetArenaLo()); + dsize = (size - (arLo - (u32)SYS_GetArenaLo())); + SYS_SetArenaLo((void*)(arLo+dsize)); + _CPU_ISR_Restore(level); + + memset((void*)arLo,0,dsize); + __wkspace_heap_size += __lwp_heap_init(&__wkspace_heap,(void*)arLo,dsize,PPC_ALIGNMENT); +} diff --git a/wii/libogc/libogc/lwp_wkspace.inl b/wii/libogc/libogc/lwp_wkspace.inl new file mode 100644 index 0000000000..4aa8f0faf0 --- /dev/null +++ b/wii/libogc/libogc/lwp_wkspace.inl @@ -0,0 +1,14 @@ +#ifndef __LWP_WKSPACE_INL__ +#define __LWP_WKSPACE_INL__ + +static __inline__ void* __lwp_wkspace_allocate(u32 size) +{ + return __lwp_heap_allocate(&__wkspace_heap,size); +} + +static __inline__ BOOL __lwp_wkspace_free(void *ptr) +{ + return __lwp_heap_free(&__wkspace_heap,ptr); +} + +#endif diff --git a/wii/libogc/libogc/malloc_lock.c b/wii/libogc/libogc/malloc_lock.c new file mode 100644 index 0000000000..c907f86171 --- /dev/null +++ b/wii/libogc/libogc/malloc_lock.c @@ -0,0 +1,81 @@ +#include <_ansi.h> +#include <_syslist.h> +#ifndef REENTRANT_SYSCALLS_PROVIDED +#include +#endif +#include +#undef errno +extern int errno; + +#include "asm.h" +#include "processor.h" +#include "lwp_mutex.h" + +#define MEMLOCK_MUTEX_ID 0x00030040 + +static int initialized = 0; +static lwp_mutex mem_lock; + +void __memlock_init() +{ + __lwp_thread_dispatchdisable(); + if(!initialized) { + lwp_mutex_attr attr; + + initialized = 1; + + attr.mode = LWP_MUTEX_FIFO; + attr.nest_behavior = LWP_MUTEX_NEST_ACQUIRE; + attr.onlyownerrelease = TRUE; + attr.prioceil = 1; + __lwp_mutex_initialize(&mem_lock,&attr,LWP_MUTEX_UNLOCKED); + } + __lwp_thread_dispatchunnest(); +} + +#ifndef REENTRANT_SYSCALLS_PROVIDED +void _DEFUN(__libogc_malloc_lock,(r), + struct _reent *r) +{ + u32 level; + + if(!initialized) return; + + _CPU_ISR_Disable(level); + __lwp_mutex_seize(&mem_lock,MEMLOCK_MUTEX_ID,TRUE,LWP_THREADQ_NOTIMEOUT,level); +} + +void _DEFUN(__libogc_malloc_unlock,(r), + struct _reent *r) +{ + if(!initialized) return; + + __lwp_thread_dispatchdisable(); + __lwp_mutex_surrender(&mem_lock); + __lwp_thread_dispatchenable(); +} + +#else +void _DEFUN(__libogc_malloc_lock,(ptr), + struct _reent *ptr) +{ + unsigned int level; + + if(!initialized) return; + + _CPU_ISR_Disable(level); + __lwp_mutex_seize(&mem_lock,MEMLOCK_MUTEX_ID,TRUE,LWP_THREADQ_NOTIMEOUT,level); + ptr->_errno = _thr_executing->wait.ret_code; +} + +void _DEFUN(__libogc_malloc_unlock,(ptr), + struct _reent *ptr) +{ + if(!initialized) return; + + __lwp_thread_dispatchdisable(); + ptr->_errno = __lwp_mutex_surrender(&mem_lock); + __lwp_thread_dispatchenable(); +} + +#endif diff --git a/wii/libogc/libogc/message.c b/wii/libogc/libogc/message.c new file mode 100644 index 0000000000..4a529de321 --- /dev/null +++ b/wii/libogc/libogc/message.c @@ -0,0 +1,167 @@ +/*------------------------------------------------------------- + +message.c -- Thread subsystem II + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ + + +#include +#include +#include +#include +#include +#include "message.h" + +#define LWP_OBJTYPE_MBOX 6 + +#define LWP_CHECK_MBOX(hndl) \ +{ \ + if(((hndl)==MQ_BOX_NULL) || (LWP_OBJTYPE(hndl)!=LWP_OBJTYPE_MBOX)) \ + return NULL; \ +} + +typedef struct _mqbox_st +{ + lwp_obj object; + mq_cntrl mqueue; +} mqbox_st; + +lwp_objinfo _lwp_mqbox_objects; + +void __lwp_mqbox_init() +{ + __lwp_objmgr_initinfo(&_lwp_mqbox_objects,LWP_MAX_MQUEUES,sizeof(mqbox_st)); +} + +static __inline__ mqbox_st* __lwp_mqbox_open(mqbox_t mbox) +{ + LWP_CHECK_MBOX(mbox); + return (mqbox_st*)__lwp_objmgr_get(&_lwp_mqbox_objects,LWP_OBJMASKID(mbox)); +} + +static __inline__ void __lwp_mqbox_free(mqbox_st *mqbox) +{ + __lwp_objmgr_close(&_lwp_mqbox_objects,&mqbox->object); + __lwp_objmgr_free(&_lwp_mqbox_objects,&mqbox->object); +} + +static mqbox_st* __lwp_mqbox_allocate() +{ + mqbox_st *mqbox; + + __lwp_thread_dispatchdisable(); + mqbox = (mqbox_st*)__lwp_objmgr_allocate(&_lwp_mqbox_objects); + if(mqbox) { + __lwp_objmgr_open(&_lwp_mqbox_objects,&mqbox->object); + return mqbox; + } + __lwp_thread_dispatchenable(); + return NULL; +} + +s32 MQ_Init(mqbox_t *mqbox,u32 count) +{ + mq_attr attr; + mqbox_st *ret = NULL; + + if(!mqbox) return -1; + + ret = __lwp_mqbox_allocate(); + if(!ret) return MQ_ERROR_TOOMANY; + + attr.mode = LWP_MQ_FIFO; + if(!__lwpmq_initialize(&ret->mqueue,&attr,count,sizeof(mqmsg_t))) { + __lwp_mqbox_free(ret); + __lwp_thread_dispatchenable(); + return MQ_ERROR_TOOMANY; + } + + *mqbox = (mqbox_t)(LWP_OBJMASKTYPE(LWP_OBJTYPE_MBOX)|LWP_OBJMASKID(ret->object.id)); + __lwp_thread_dispatchenable(); + return MQ_ERROR_SUCCESSFUL; +} + +void MQ_Close(mqbox_t mqbox) +{ + mqbox_st *mbox; + + mbox = __lwp_mqbox_open(mqbox); + if(!mbox) return; + + __lwpmq_close(&mbox->mqueue,0); + __lwp_thread_dispatchenable(); + + __lwp_mqbox_free(mbox); +} + +BOOL MQ_Send(mqbox_t mqbox,mqmsg_t msg,u32 flags) +{ + BOOL ret; + mqbox_st *mbox; + u32 wait = (flags==MQ_MSG_BLOCK)?TRUE:FALSE; + + mbox = __lwp_mqbox_open(mqbox); + if(!mbox) return FALSE; + + ret = FALSE; + if(__lwpmq_submit(&mbox->mqueue,mbox->object.id,(void*)&msg,sizeof(mqmsg_t),LWP_MQ_SEND_REQUEST,wait,LWP_THREADQ_NOTIMEOUT)==LWP_MQ_STATUS_SUCCESSFUL) ret = TRUE; + __lwp_thread_dispatchenable(); + + return ret; +} + +BOOL MQ_Receive(mqbox_t mqbox,mqmsg_t *msg,u32 flags) +{ + BOOL ret; + mqbox_st *mbox; + u32 tmp,wait = (flags==MQ_MSG_BLOCK)?TRUE:FALSE; + + mbox = __lwp_mqbox_open(mqbox); + if(!mbox) return FALSE; + + ret = FALSE; + if(__lwpmq_seize(&mbox->mqueue,mbox->object.id,(void*)msg,&tmp,wait,LWP_THREADQ_NOTIMEOUT)==LWP_MQ_STATUS_SUCCESSFUL) ret = TRUE; + __lwp_thread_dispatchenable(); + + return ret; +} + +BOOL MQ_Jam(mqbox_t mqbox,mqmsg_t msg,u32 flags) +{ + BOOL ret; + mqbox_st *mbox; + u32 wait = (flags==MQ_MSG_BLOCK)?TRUE:FALSE; + + mbox = __lwp_mqbox_open(mqbox); + if(!mbox) return FALSE; + + ret = FALSE; + if(__lwpmq_submit(&mbox->mqueue,mbox->object.id,(void*)&msg,sizeof(mqmsg_t),LWP_MQ_SEND_URGENT,wait,LWP_THREADQ_NOTIMEOUT)==LWP_MQ_STATUS_SUCCESSFUL) ret = TRUE; + __lwp_thread_dispatchenable(); + + return ret; +} diff --git a/wii/libogc/libogc/mutex.c b/wii/libogc/libogc/mutex.c new file mode 100644 index 0000000000..546dad1402 --- /dev/null +++ b/wii/libogc/libogc/mutex.c @@ -0,0 +1,160 @@ +/*------------------------------------------------------------- + +mutex.c -- Thread subsystem III + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#include +#include +#include "asm.h" +#include "lwp_mutex.h" +#include "lwp_objmgr.h" +#include "lwp_config.h" +#include "mutex.h" + +#define LWP_OBJTYPE_MUTEX 3 + +#define LWP_CHECK_MUTEX(hndl) \ +{ \ + if(((hndl)==LWP_MUTEX_NULL) || (LWP_OBJTYPE(hndl)!=LWP_OBJTYPE_MUTEX)) \ + return NULL; \ +} + +typedef struct _mutex_st +{ + lwp_obj object; + lwp_mutex mutex; +} mutex_st; + +lwp_objinfo _lwp_mutex_objects; + +static s32 __lwp_mutex_locksupp(mutex_t lock,u32 timeout,u8 block) +{ + u32 level; + mutex_st *p; + + if(lock==LWP_MUTEX_NULL || LWP_OBJTYPE(lock)!=LWP_OBJTYPE_MUTEX) return -1; + + p = (mutex_st*)__lwp_objmgr_getisrdisable(&_lwp_mutex_objects,LWP_OBJMASKID(lock),&level); + if(!p) return -1; + + __lwp_mutex_seize(&p->mutex,p->object.id,block,timeout,level); + return _thr_executing->wait.ret_code; +} + +void __lwp_mutex_init() +{ + __lwp_objmgr_initinfo(&_lwp_mutex_objects,LWP_MAX_MUTEXES,sizeof(mutex_st)); +} + + +static __inline__ mutex_st* __lwp_mutex_open(mutex_t lock) +{ + LWP_CHECK_MUTEX(lock); + return (mutex_st*)__lwp_objmgr_get(&_lwp_mutex_objects,LWP_OBJMASKID(lock)); +} + +static __inline__ void __lwp_mutex_free(mutex_st *lock) +{ + __lwp_objmgr_close(&_lwp_mutex_objects,&lock->object); + __lwp_objmgr_free(&_lwp_mutex_objects,&lock->object); +} + +static mutex_st* __lwp_mutex_allocate() +{ + mutex_st *lock; + + __lwp_thread_dispatchdisable(); + lock = (mutex_st*)__lwp_objmgr_allocate(&_lwp_mutex_objects); + if(lock) { + __lwp_objmgr_open(&_lwp_mutex_objects,&lock->object); + return lock; + } + __lwp_thread_dispatchunnest(); + return NULL; +} + +s32 LWP_MutexInit(mutex_t *mutex,bool use_recursive) +{ + lwp_mutex_attr attr; + mutex_st *ret; + + if(!mutex) return -1; + + ret = __lwp_mutex_allocate(); + if(!ret) return -1; + + attr.mode = LWP_MUTEX_FIFO; + attr.nest_behavior = use_recursive?LWP_MUTEX_NEST_ACQUIRE:LWP_MUTEX_NEST_ERROR; + attr.onlyownerrelease = TRUE; + attr.prioceil = 1; //__lwp_priotocore(LWP_PRIO_MAX-1); + __lwp_mutex_initialize(&ret->mutex,&attr,LWP_MUTEX_UNLOCKED); + + *mutex = (mutex_t)(LWP_OBJMASKTYPE(LWP_OBJTYPE_MUTEX)|LWP_OBJMASKID(ret->object.id)); + __lwp_thread_dispatchunnest(); + return 0; +} + +s32 LWP_MutexDestroy(mutex_t mutex) +{ + mutex_st *p = __lwp_mutex_open(mutex); + if(!p) return 0; + + if(__lwp_mutex_locked(&p->mutex)) { + __lwp_thread_dispatchenable(); + return EBUSY; + } + __lwp_mutex_flush(&p->mutex,EINVAL); + __lwp_thread_dispatchenable(); + + __lwp_mutex_free(p); + return 0; +} + +s32 LWP_MutexLock(mutex_t mutex) +{ + return __lwp_mutex_locksupp(mutex,LWP_THREADQ_NOTIMEOUT,TRUE); +} + +s32 LWP_MutexTryLock(mutex_t mutex) +{ + return __lwp_mutex_locksupp(mutex,LWP_THREADQ_NOTIMEOUT,FALSE); +} + +s32 LWP_MutexUnlock(mutex_t mutex) +{ + u32 ret; + mutex_st *lock; + + lock = __lwp_mutex_open(mutex); + if(!lock) return -1; + + ret = __lwp_mutex_surrender(&lock->mutex); + __lwp_thread_dispatchenable(); + + return ret; +} diff --git a/wii/libogc/libogc/network_common.c b/wii/libogc/libogc/network_common.c new file mode 100644 index 0000000000..7442a35ec3 --- /dev/null +++ b/wii/libogc/libogc/network_common.c @@ -0,0 +1,198 @@ +#include "lwip/def.h" +#include "lwip/inet.h" + +/* Here for now until needed in other places in lwIP */ +#ifndef isascii +#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up) +#define isascii(c) in_range(c, 0x20, 0x7f) +#define isdigit(c) in_range(c, '0', '9') +#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F')) +#define islower(c) in_range(c, 'a', 'z') +#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v') +#endif + + + /* + * Ascii internet address interpretation routine. + * The value returned is in network order. + */ + + /* */ + /* inet_addr */ + u32_t inet_addr(const char *cp) + { + struct in_addr val; + + if (inet_aton(cp, &val)) { + return (val.s_addr); + } + return (INADDR_NONE); + } + + /* + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + */ + /* */ + /* inet_aton */ + s8_t + inet_aton(const char *cp, struct in_addr *addr) + { + u32_t val; + s32_t base, n; + char c; + u32_t parts[4]; + u32_t* pp = parts; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, isdigit=decimal. + */ + if (!isdigit(c)) + return (0); + val = 0; base = 10; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') + base = 16, c = *++cp; + else + base = 8; + } + for (;;) { + if (isdigit(c)) { + val = (val * base) + (s16_t)(c - '0'); + c = *++cp; + } else if (base == 16 && isxdigit(c)) { + val = (val << 4) | + (s16_t)(c + 10 - (islower(c) ? 'a' : 'A')); + c = *++cp; + } else + break; + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3) + return (0); + *pp++ = val; + c = *++cp; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && (!isascii(c) || !isspace(c))) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + switch (n) { + + case 0: + return (0); /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffff) + return (0); + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (addr) + addr->s_addr = htonl(val); + return (1); + } + +/* Convert numeric IP address into decimal dotted ASCII representation. + * returns ptr to static buffer; not reentrant! + */ +char *inet_ntoa(struct in_addr addr) +{ + static char str[16]; + u32_t s_addr = addr.s_addr; + char inv[3]; + char *rp; + u8_t *ap; + u8_t rem; + u8_t n; + u8_t i; + + rp = str; + ap = (u8_t *)&s_addr; + for(n = 0; n < 4; n++) { + i = 0; + do { + rem = *ap % (u8_t)10; + *ap /= (u8_t)10; + inv[i++] = '0' + rem; + } while(*ap); + while(i--) + *rp++ = inv[i]; + *rp++ = '.'; + ap++; + } + *--rp = 0; + return str; +} + + +#ifndef BYTE_ORDER +#error BYTE_ORDER is not defined +#endif +#if BYTE_ORDER == LITTLE_ENDIAN + +u16_t +htons(u16_t n) +{ + return ((n & 0xff) << 8) | ((n & 0xff00) >> 8); +} + +u16_t +ntohs(u16_t n) +{ + return htons(n); +} + +u32_t +htonl(u32_t n) +{ + return ((n & 0xff) << 24) | + ((n & 0xff00) << 8) | + ((n & 0xff0000) >> 8) | + ((n & 0xff000000) >> 24); +} + +u32_t +ntohl(u32_t n) +{ + return htonl(n); +} + +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ diff --git a/wii/libogc/libogc/network_wii.c b/wii/libogc/libogc/network_wii.c new file mode 100644 index 0000000000..5c76cd8f49 --- /dev/null +++ b/wii/libogc/libogc/network_wii.c @@ -0,0 +1,1167 @@ +/*------------------------------------------------------------- + +network_wii.c -- Wii network subsystem + +Copyright (C) 2008 bushing + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#if defined(HW_RVL) + + +#define MAX_IP_RETRIES 100 +#define MAX_INIT_RETRIES 32 + +//#define DEBUG_NET + +#ifdef DEBUG_NET +#define debug_printf(fmt, args...) \ + do { \ + fprintf(stderr, "%s:%d:" fmt, __FUNCTION__, __LINE__, ##args); \ + } while (0) +#else +#define debug_printf(fmt, args...) do { } while (0) +#endif // DEBUG_NET + +#include +#include +#include +#define __LINUX_ERRNO_EXTENSIONS__ +#include +#include +#include +#include + +#include "ipc.h" +#include "processor.h" +#include "network.h" +#include "ogcsys.h" +#include "lwp_heap.h" + +#define NET_HEAP_SIZE 64*1024 + +#define IOS_O_NONBLOCK 0x04 //(O_NONBLOCK >> 16) - it's in octal representation, so this shift leads to 0 and hence nonblocking sockets didn't work. changed it to the right value. + +#define IOCTL_NWC24_STARTUP 0x06 + +#define IOCTL_NCD_SETIFCONFIG3 0x03 +#define IOCTL_NCD_SETIFCONFIG4 0x04 +#define IOCTL_NCD_GETLINKSTATUS 0x07 +#define IOCTLV_NCD_GETMACADDRESS 0x08 + +#define NET_UNKNOWN_ERROR_OFFSET -10000 + +enum { + IOCTL_SO_ACCEPT = 1, + IOCTL_SO_BIND, + IOCTL_SO_CLOSE, + IOCTL_SO_CONNECT, + IOCTL_SO_FCNTL, + IOCTL_SO_GETPEERNAME, // todo + IOCTL_SO_GETSOCKNAME, // todo + IOCTL_SO_GETSOCKOPT, // todo 8 + IOCTL_SO_SETSOCKOPT, + IOCTL_SO_LISTEN, + IOCTL_SO_POLL, // todo b + IOCTLV_SO_RECVFROM, + IOCTLV_SO_SENDTO, + IOCTL_SO_SHUTDOWN, // todo e + IOCTL_SO_SOCKET, + IOCTL_SO_GETHOSTID, + IOCTL_SO_GETHOSTBYNAME, + IOCTL_SO_GETHOSTBYADDR,// todo + IOCTLV_SO_GETNAMEINFO, // todo 13 + IOCTL_SO_UNK14, // todo + IOCTL_SO_INETATON, // todo + IOCTL_SO_INETPTON, // todo + IOCTL_SO_INETNTOP, // todo + IOCTLV_SO_GETADDRINFO, // todo + IOCTL_SO_SOCKATMARK, // todo + IOCTLV_SO_UNK1A, // todo + IOCTLV_SO_UNK1B, // todo + IOCTLV_SO_GETINTERFACEOPT, // todo + IOCTLV_SO_SETINTERFACEOPT, // todo + IOCTL_SO_SETINTERFACE, // todo + IOCTL_SO_STARTUP, // 0x1f + IOCTL_SO_ICMPSOCKET = 0x30, // todo + IOCTLV_SO_ICMPPING, // todo + IOCTL_SO_ICMPCANCEL, // todo + IOCTL_SO_ICMPCLOSE // todo +}; + +struct init_data { + u32 state; + s32 fd; + s32 prevres; + s32 result; + syswd_t alarm; + u32 retries; + netcallback cb; + void *usrdata; + u8 *buf; +}; + +struct init_default_cb { + lwpq_t queue; + s32 result; +}; + +struct bind_params { + u32 socket; + u32 has_name; + u8 name[28]; +}; + +struct connect_params { + u32 socket; + u32 has_addr; + u8 addr[28]; +}; + +struct sendto_params { + u32 socket; + u32 flags; + u32 has_destaddr; + u8 destaddr[28]; +}; + +struct setsockopt_params { + u32 socket; + u32 level; + u32 optname; + u32 optlen; + u8 optval[20]; +}; + +// 0 means we don't know what this error code means +// I sense a pattern here... +static u8 _net_error_code_map[] = { + 0, // 0 + E2BIG, + EACCES, + EADDRINUSE, + EADDRNOTAVAIL, + EAFNOSUPPORT, // 5 + EAGAIN, + EALREADY, + EBADFD, + EBADMSG, + EBUSY, // 10 + ECANCELED, + ECHILD, + ECONNABORTED, + ECONNREFUSED, + ECONNRESET, // 15 + EDEADLK, + EDESTADDRREQ, + EDOM, + EDQUOT, + EEXIST, // 20 + EFAULT, + EFBIG, + EHOSTUNREACH, + EIDRM, + EILSEQ, // 25 + EINPROGRESS, + EINTR, + EINVAL, + EIO, + EISCONN, // 30 + EISDIR, + ELOOP, + EMFILE, + EMLINK, + EMSGSIZE, // 35 + EMULTIHOP, + ENAMETOOLONG, + ENETDOWN, + ENETRESET, + ENETUNREACH, // 40 + ENFILE, + ENOBUFS, + ENODATA, + ENODEV, + ENOENT, // 45 + ENOEXEC, + ENOLCK, + ENOLINK, + ENOMEM, + ENOMSG, // 50 + ENOPROTOOPT, + ENOSPC, + ENOSR, + ENOSTR, + ENOSYS, // 55 + ENOTCONN, + ENOTDIR, + ENOTEMPTY, + ENOTSOCK, + ENOTSUP, // 60 + ENOTTY, + ENXIO, + EOPNOTSUPP, + EOVERFLOW, + EPERM, // 65 + EPIPE, + EPROTO, + EPROTONOSUPPORT, + EPROTOTYPE, + ERANGE, // 70 + EROFS, + ESPIPE, + ESRCH, + ESTALE, + ETIME, // 75 + ETIMEDOUT, +}; + +static volatile bool _init_busy = false; +static volatile bool _init_abort = false; +static vs32 _last_init_result = -ENETDOWN; +static s32 net_ip_top_fd = -1; +static u8 __net_heap_inited = 0; +static s32 __net_hid=-1; +static heap_cntrl __net_heap; + +static char __manage_fs[] ATTRIBUTE_ALIGN(32) = "/dev/net/ncd/manage"; +static char __iptop_fs[] ATTRIBUTE_ALIGN(32) = "/dev/net/ip/top"; +static char __kd_fs[] ATTRIBUTE_ALIGN(32) = "/dev/net/kd/request"; + +#define ROUNDDOWN32(v) (((u32)(v)-0x1f)&~0x1f) + +static s32 NetCreateHeap() +{ + u32 level; + void *net_heap_ptr; + + _CPU_ISR_Disable(level); + + if(__net_heap_inited) + { + _CPU_ISR_Restore(level); + return IPC_OK; + } + + net_heap_ptr = (void *)ROUNDDOWN32(((u32)SYS_GetArena2Hi() - NET_HEAP_SIZE)); + if((u32)net_heap_ptr < (u32)SYS_GetArena2Lo()) + { + _CPU_ISR_Restore(level); + return IPC_ENOMEM; + } + SYS_SetArena2Hi(net_heap_ptr); + __lwp_heap_init(&__net_heap, net_heap_ptr, NET_HEAP_SIZE, 32); + __net_heap_inited=1; + _CPU_ISR_Restore(level); + return IPC_OK; +} + +static void* net_malloc(u32 size) +{ + return __lwp_heap_allocate(&__net_heap, size); +} + +static BOOL net_free(void *ptr) +{ + return __lwp_heap_free(&__net_heap, ptr); +} + +static s32 _net_convert_error(s32 ios_retval) +{ +// return ios_retval; + if (ios_retval >= 0) return ios_retval; + if (ios_retval < -sizeof(_net_error_code_map) + || !_net_error_code_map[-ios_retval]) + return NET_UNKNOWN_ERROR_OFFSET + ios_retval; + return -_net_error_code_map[-ios_retval]; +} + +u32 net_gethostip(void) +{ + u32 ip_addr=0; + int retries; + + if (net_ip_top_fd < 0) return 0; + for (retries=0, ip_addr=0; !ip_addr && retries < 5; retries++) { + ip_addr = IOS_Ioctl(net_ip_top_fd, IOCTL_SO_GETHOSTID, 0, 0, 0, 0); +#ifdef DEBUG_NET + debug_printf("."); + fflush(stdout); +#endif + if (!ip_addr) + usleep(100000); + } + + return ip_addr; +} + +static s32 net_init_chain(s32 result, void *usrdata); + +static void net_init_alarm(syswd_t alarm, void *cb_arg) { + debug_printf("net_init alarm\n"); + net_init_chain(0, cb_arg); +} + +static s32 net_init_chain(s32 result, void *usrdata) { + struct init_data *data = (struct init_data *) usrdata; + struct timespec tb; + + debug_printf("net_init chain entered: %u %d\n", data->state, result); + + if (_init_abort) { + data->state = 0xff; + goto error; + } + + switch (data->state) { + case 0: // open manage fd + data->state = 1; + data->result = IOS_OpenAsync(__manage_fs, 0, net_init_chain, data); + if (data->result < 0) { + data->result = _net_convert_error(data->result); + goto done; + } + return 0; + + case 1: // get link status + if (result == IPC_ENOENT) { + debug_printf("IPC_ENOENT, retrying...\n"); + data->state = 0; + tb.tv_sec = 0; + tb.tv_nsec = 100000000; + data->result = SYS_SetAlarm(data->alarm, &tb, net_init_alarm, data); + if (data->result) { + debug_printf("error setting the alarm: %d\n", data->result); + goto done; + } + return 0; + } + + if (result < 0) { + data->result = _net_convert_error(result); + debug_printf("error opening the manage fd: %d\n", data->result); + goto done; + } + + data->fd = result; + data->state = 2; + data->result = IOS_IoctlvFormatAsync(__net_hid, data->fd, IOCTL_NCD_GETLINKSTATUS, net_init_chain, data, ":d", data->buf, 0x20); + if (data->result < 0) { + data->result = _net_convert_error(data->result); + data->state = 0xff; + if (IOS_CloseAsync(data->fd, net_init_chain, data) < 0) + goto done; + } + return 0; + + case 2: // close manage fd + data->prevres = result; + data->state = 3; + data->result = IOS_CloseAsync(data->fd, net_init_chain, data); + if (data->result < 0) { + data->result = _net_convert_error(data->result); + goto done; + } + return 0; + + case 3: // open top fd + if (data->prevres < 0) { + data->result = _net_convert_error(data->prevres); + debug_printf("invalid link status %d\n", data->result); + goto done; + } + + data->state = 4; + data->result = IOS_OpenAsync(__iptop_fs, 0, net_init_chain, data); + if (data->result < 0) { + data->result = _net_convert_error(data->result); + goto done; + } + return 0; + + case 4: // open request fd + if (result < 0) { + data->result = _net_convert_error(result); + debug_printf("error opening the top fd: %d\n", data->result); + goto done; + } + + net_ip_top_fd = result; + data->state = 5; + data->result = IOS_OpenAsync(__kd_fs, 0, net_init_chain, data); + if (data->result < 0) { + data->result = _net_convert_error(data->result); + data->state = 0xff; + goto error; + } + return 0; + + case 5: // NWC24 startup + if (result < 0) { + data->result = _net_convert_error(result); + debug_printf("error opening the request fd: %d\n", data->result); + data->state = 0xff; + goto error; + } + + data->fd = result; + data->retries = MAX_INIT_RETRIES; + case 6: + data->state = 7; + data->result = IOS_IoctlAsync(data->fd, IOCTL_NWC24_STARTUP, NULL, 0, data->buf, 0x20, net_init_chain, data); + if (data->result < 0) { + data->result = _net_convert_error(data->result); + data->state = 0xff; + if (IOS_CloseAsync(data->fd, net_init_chain, data) < 0) + goto done; + } + return 0; + case 7: + if (result==0) { + memcpy(&result, data->buf, sizeof(result)); + if(result==-29 && --data->retries) { + data->state = 6; + tb.tv_sec = 0; + tb.tv_nsec = 100000000; + data->result = SYS_SetAlarm(data->alarm, &tb, net_init_alarm, data); + if (data->result) { + data->state = 0xff; + debug_printf("error setting the alarm: %d\n", data->result); + if (IOS_CloseAsync(data->fd, net_init_chain, data) < 0) + goto error; + } + return 0; + } else if (result == -15) // this happens if it's already been started + result = 0; + } + + data->prevres = result; + data->state = 8; + data->result = IOS_CloseAsync(data->fd, net_init_chain, data); + if (data->result < 0) { + data->result = _net_convert_error(data->result); + data->state = 0xff; + goto error; + } + return 0; + + case 8: // socket startup + if (data->prevres < 0) { + data->result = _net_convert_error(data->prevres); + debug_printf("NWC24 startup failed: %d\n", data->result); + data->state = 0xff; + goto error; + } + + data->state = 9; + data->retries = MAX_IP_RETRIES; + data->result = IOS_IoctlAsync(net_ip_top_fd, IOCTL_SO_STARTUP, 0, 0, 0, 0, net_init_chain, data); + if (data->result < 0) { + data->result = _net_convert_error(data->result); + data->state = 0xff; + goto error; + } + return 0; + + case 9: // check ip + if (result < 0) { + data->result = _net_convert_error(result); + debug_printf("socket startup failed: %d\n", data->result); + data->state = 0xff; + goto error; + } + + data->state = 10; + data->result = IOS_IoctlAsync(net_ip_top_fd, IOCTL_SO_GETHOSTID, 0, 0, 0, 0, net_init_chain, data); + if (data->result < 0) { + data->result = _net_convert_error(data->result); + data->state = 0xff; + goto error; + } + return 0; + + case 10: // done, check result + if (result == 0) { + if (!data->retries) { + data->result = -ETIMEDOUT; + debug_printf("unable to obtain ip\n"); + data->state = 0xff; + goto error; + } + + debug_printf("unable to obtain ip, retrying...\n"); + data->state = 9; + data->retries--; + tb.tv_sec = 0; + tb.tv_nsec = 100000000; + data->result = SYS_SetAlarm(data->alarm, &tb, net_init_alarm, data); + if (data->result) { + data->state = 0xff; + debug_printf("error setting the alarm: %d\n", data->result); + goto error; + } + return 0; + } + + data->result = 0; + goto done; + +error: + case 0xff: // error occured before, last async call finished + if (net_ip_top_fd >= 0) { + data->fd = net_ip_top_fd; + net_ip_top_fd = -1; + if (IOS_CloseAsync(data->fd, net_init_chain, data) < 0) + goto done; + return 0; + } + goto done; + + default: + debug_printf("unknown state in chain %d\n", data->state); + data->result = -1; + + break; + } + +done: + SYS_RemoveAlarm(data->alarm); + + _last_init_result = data->result; + + if (data->cb) + data->cb(data->result, data->usrdata); + + free(data->buf); + free(data); + + _init_busy = false; + + return 0; +} + +s32 net_init_async(netcallback cb, void *usrdata) { + s32 ret; + struct init_data *data; + + if (net_ip_top_fd >= 0) + return 0; + + if (_init_busy) + return -EBUSY; + + ret = NetCreateHeap(); + if (ret != IPC_OK) + return ret; + + if (__net_hid == -1) + __net_hid = iosCreateHeap(1024); //only needed for ios calls + + if (__net_hid < 0) + return __net_hid; + + data = malloc(sizeof(struct init_data)); + if (!data) + return -1; + + memset(data, 0, sizeof(struct init_data)); + + if (SYS_CreateAlarm(&data->alarm)) { + debug_printf("error creating alarm\n"); + free(data); + return -1; + } + + data->buf = memalign(32, 0x20); + if (!data->buf) { + free(data); + return -1; + } + + data->cb = cb; + data->usrdata = usrdata; + + // kick off the callback chain + _init_busy = true; + _init_abort = false; + _last_init_result = -EBUSY; + net_init_chain(IPC_ENOENT, data); + + return 0; +} + +static void net_init_callback(s32 result, void *usrdata) { + struct init_default_cb *data = (struct init_default_cb *) usrdata; + + data->result = result; + LWP_ThreadBroadcast(data->queue); + + return; +} + +s32 net_init(void) { + struct init_default_cb data; + + if (net_ip_top_fd >= 0) + return 0; + + LWP_InitQueue(&data.queue); + net_init_async((netcallback)net_init_callback, &data); + LWP_ThreadSleep(data.queue); + LWP_CloseQueue(data.queue); + + return data.result; +} + +s32 net_get_status(void) { + return _last_init_result; +} + +void net_deinit() { + if (_init_busy) { + debug_printf("aborting net_init_async\n"); + _init_abort = true; + while (_init_busy) + usleep(50); + debug_printf("net_init_async done\n"); + } + + if (net_ip_top_fd >= 0) IOS_Close(net_ip_top_fd); + net_ip_top_fd = -1; + _last_init_result = -ENETDOWN; +} + +void net_wc24cleanup() { + s32 kd_fd; + STACK_ALIGN(u8, kd_buf, 0x20, 32); + + kd_fd = IOS_Open(__kd_fs, 0); + if (kd_fd >= 0) { + IOS_Ioctl(kd_fd, 7, NULL, 0, kd_buf, 0x20); + IOS_Close(kd_fd); + } +} + +s32 net_get_mac_address(void *mac_buf) { + s32 fd; + s32 result; + void *_mac_buf; + STACK_ALIGN(u32, manage_buf, 0x20, 32); + + if (mac_buf==NULL) return -EINVAL; + + result = NetCreateHeap(); + if (result!=IPC_OK) return result; + + _mac_buf = net_malloc(6); + if (_mac_buf==NULL) return IPC_ENOMEM; + + fd = IOS_Open(__manage_fs, 0); + if (fd<0) { + net_free(_mac_buf); + return fd; + } + + result = IOS_IoctlvFormat(__net_hid, fd, IOCTLV_NCD_GETMACADDRESS, ":dd", manage_buf, 0x20, _mac_buf, 0x06); + IOS_Close(fd); + + if (result>=0) { + memcpy(mac_buf, _mac_buf, 6); + if (manage_buf[0]) result = manage_buf[0]; + } + + net_free(_mac_buf); + return result; +} + +/* Returned value is a static buffer -- this function is not threadsafe! */ +struct hostent * net_gethostbyname(const char *addrString) +{ + s32 ret, len, i; + u8 *params; + struct hostent *ipData; + u32 addrOffset; + static u8 ipBuffer[0x460] ATTRIBUTE_ALIGN(32); + + memset(ipBuffer, 0, 0x460); + + if (net_ip_top_fd < 0) { + errno = -ENXIO; + return NULL; + } + + len = strlen(addrString) + 1; + params = net_malloc(len); + if (params==NULL) { + errno = IPC_ENOMEM; + return NULL; + } + + memcpy(params, addrString, len); + + ret = _net_convert_error(IOS_Ioctl(net_ip_top_fd, IOCTL_SO_GETHOSTBYNAME, params, len, ipBuffer, 0x460)); + + if(params!=NULL) net_free(params); + + if (ret < 0) { + errno = ret; + return NULL; + } + + ipData = ((struct hostent*)ipBuffer); + addrOffset = (u32)MEM_PHYSICAL_TO_K0(ipData->h_name) - ((u32)ipBuffer + 0x10); + + ipData->h_name = MEM_PHYSICAL_TO_K0(ipData->h_name) - addrOffset; + ipData->h_aliases = MEM_PHYSICAL_TO_K0(ipData->h_aliases) - addrOffset; + + for (i=0; (i < 0x40) && (ipData->h_aliases[i] != 0); i++) { + ipData->h_aliases[i] = MEM_PHYSICAL_TO_K0(ipData->h_aliases[i]) - addrOffset; + } + + ipData->h_addr_list = MEM_PHYSICAL_TO_K0(ipData->h_addr_list) - addrOffset; + + for (i=0; (i < 0x40) && (ipData->h_addr_list[i] != 0); i++) { + ipData->h_addr_list[i] = MEM_PHYSICAL_TO_K0(ipData->h_addr_list[i]) - addrOffset; + } + + errno = 0; + return ipData; +} + +s32 net_socket(u32 domain, u32 type, u32 protocol) +{ + s32 ret; + STACK_ALIGN(u32, params, 3, 32); + + if (net_ip_top_fd < 0) return -ENXIO; + + params[0] = domain; + params[1] = type; + params[2] = protocol; + + ret = _net_convert_error(IOS_Ioctl(net_ip_top_fd, IOCTL_SO_SOCKET, params, 12, NULL, 0)); + if(ret>=0) // set tcp window size to 32kb + { + int window_size = 32768; + net_setsockopt(ret, SOL_SOCKET, SO_RCVBUF, (char *) &window_size, sizeof(window_size)); + } + debug_printf("net_socket(%d, %d, %d)=%d\n", domain, type, protocol, ret); + return ret; +} + +s32 net_shutdown(s32 s, u32 how) +{ + s32 ret; + STACK_ALIGN(u32, params, 2, 32); + + if (net_ip_top_fd < 0) return -ENXIO; + + params[0] = s; + params[1] = how; + ret = _net_convert_error(IOS_Ioctl(net_ip_top_fd, IOCTL_SO_SHUTDOWN, params, 8, NULL, 0)); + + debug_printf("net_shutdown(%d, %d)=%d\n", s, how, ret); + return ret; +} + +s32 net_bind(s32 s, struct sockaddr *name, socklen_t namelen) +{ + s32 ret; + STACK_ALIGN(struct bind_params,params,1,32); + + if (net_ip_top_fd < 0) return -ENXIO; + if (name->sa_family != AF_INET) return -EAFNOSUPPORT; + + name->sa_len = 8; + + memset(params, 0, sizeof(struct bind_params)); + params->socket = s; + params->has_name = 1; + memcpy(params->name, name, 8); + + ret = _net_convert_error(IOS_Ioctl(net_ip_top_fd, IOCTL_SO_BIND, params, sizeof (struct bind_params), NULL, 0)); + debug_printf("net_bind(%d, %p)=%d\n", s, name, ret); + + return ret; +} + +s32 net_listen(s32 s, u32 backlog) +{ + s32 ret; + STACK_ALIGN(u32, params, 2, 32); + + if (net_ip_top_fd < 0) return -ENXIO; + + params[0] = s; + params[1] = backlog; + + debug_printf("calling ios_ioctl(%d, %d, %p, %d)\n", net_ip_top_fd, IOCTL_SO_SOCKET, params, 8); + + ret = _net_convert_error(IOS_Ioctl(net_ip_top_fd, IOCTL_SO_LISTEN, params, 8, NULL, 0)); + debug_printf("net_listen(%d, %d)=%d\n", s, backlog, ret); + return ret; +} + +s32 net_accept(s32 s, struct sockaddr *addr, socklen_t *addrlen) +{ + s32 ret; + STACK_ALIGN(u32, _socket, 1, 32); + + debug_printf("net_accept()\n"); + + if (net_ip_top_fd < 0) return -ENXIO; + + if (!addr) return -EINVAL; + addr->sa_len = 8; + addr->sa_family = AF_INET; + + if (!addrlen) return -EINVAL; + + if (*addrlen < 8) return -ENOMEM; + + *addrlen = 8; + + *_socket = s; + debug_printf("calling ios_ioctl(%d, %d, %p, %d)\n", net_ip_top_fd, IOCTL_SO_ACCEPT, _socket, 4); + ret = _net_convert_error(IOS_Ioctl(net_ip_top_fd, IOCTL_SO_ACCEPT, _socket, 4, addr, *addrlen)); + + debug_printf("net_accept(%d, %p)=%d\n", s, addr, ret); + return ret; +} + +s32 net_connect(s32 s, struct sockaddr *addr, socklen_t addrlen) +{ + s32 ret; + STACK_ALIGN(struct connect_params,params,1,32); + + if (net_ip_top_fd < 0) return -ENXIO; + if (addr->sa_family != AF_INET) return -EAFNOSUPPORT; + if (addrlen < 8) return -EINVAL; + + addr->sa_len = 8; + + memset(params, 0, sizeof(struct connect_params)); + params->socket = s; + params->has_addr = 1; + memcpy(¶ms->addr, addr, addrlen); + + ret = _net_convert_error(IOS_Ioctl(net_ip_top_fd, IOCTL_SO_CONNECT, params, sizeof(struct connect_params), NULL, 0)); + if (ret < 0) + debug_printf("SOConnect(%d, %p)=%d\n", s, addr, ret); + + return ret; +} + +s32 net_write(s32 s, const void *data, s32 size) +{ + return net_send(s, data, size, 0); +} + +s32 net_send(s32 s, const void *data, s32 size, u32 flags) +{ + return net_sendto(s, data, size, flags, NULL, 0); +} + +s32 net_sendto(s32 s, const void *data, s32 len, u32 flags, struct sockaddr *to, socklen_t tolen) +{ + s32 ret; + u8 * message_buf = NULL; + STACK_ALIGN(struct sendto_params,params,1,32); + + if (net_ip_top_fd < 0) return -ENXIO; + if (tolen > 28) return -EOVERFLOW; + + message_buf = net_malloc(len); + if (message_buf == NULL) { + debug_printf("net_send: failed to alloc %d bytes\n", len); + return IPC_ENOMEM; + } + + debug_printf("net_sendto(%d, %p, %d, %d, %p, %d)\n", s, data, len, flags, to, tolen); + + if (to && to->sa_len != tolen) { + debug_printf("warning: to->sa_len was %d, setting to %d\n", to->sa_len, tolen); + to->sa_len = tolen; + } + + memset(params, 0, sizeof(struct sendto_params)); + memcpy(message_buf, data, len); // ensure message buf is aligned + + params->socket = s; + params->flags = flags; + if (to) { + params->has_destaddr = 1; + memcpy(params->destaddr, to, to->sa_len); + } else { + params->has_destaddr = 0; + } + + ret = _net_convert_error(IOS_IoctlvFormat(__net_hid, net_ip_top_fd, IOCTLV_SO_SENDTO, "dd:", message_buf, len, params, sizeof(struct sendto_params))); + debug_printf("net_send retuned %d\n", ret); + + if(message_buf!=NULL) net_free(message_buf); + return ret; +} + +s32 net_recv(s32 s, void *mem, s32 len, u32 flags) +{ + return net_recvfrom(s, mem, len, flags, NULL, NULL); +} + +s32 net_recvfrom(s32 s, void *mem, s32 len, u32 flags, struct sockaddr *from, socklen_t *fromlen) +{ + s32 ret; + u8* message_buf = NULL; + STACK_ALIGN(u32, params, 2, 32); + + if (net_ip_top_fd < 0) return -ENXIO; + if (len<=0) return -EINVAL; + + if (fromlen && from->sa_len != *fromlen) { + debug_printf("warning: from->sa_len was %d, setting to %d\n",from->sa_len, *fromlen); + from->sa_len = *fromlen; + } + + message_buf = net_malloc(len); + if (message_buf == NULL) { + debug_printf("SORecv: failed to alloc %d bytes\n", len); + return IPC_ENOMEM; + } + + debug_printf("net_recvfrom(%d, '%s', %d, %d, %p, %d)\n", s, (char *)mem, len, flags, from, fromlen?*fromlen:0); + + memset(message_buf, 0, len); + params[0] = s; + params[1] = flags; + + ret = _net_convert_error(IOS_IoctlvFormat(__net_hid, net_ip_top_fd, IOCTLV_SO_RECVFROM, "d:dd", params, 8, message_buf, len, from, (fromlen?*fromlen:0))); + debug_printf("net_recvfrom returned %d\n", ret); + + if (ret > 0) { + if (ret > len) { + ret = -EOVERFLOW; + goto done; + } + + memcpy(mem, message_buf, ret); + } + + if (fromlen && from) *fromlen = from->sa_len; + +done: + if(message_buf!=NULL) net_free(message_buf); + return ret; +} + +s32 net_read(s32 s, void *mem, s32 len) +{ + return net_recvfrom(s, mem, len, 0, NULL, NULL); +} + +s32 net_close(s32 s) +{ + s32 ret; + STACK_ALIGN(u32, _socket, 1, 32); + + if (net_ip_top_fd < 0) return -ENXIO; + + *_socket = s; + ret = _net_convert_error(IOS_Ioctl(net_ip_top_fd, IOCTL_SO_CLOSE, _socket, 4, NULL, 0)); + + if (ret < 0) + debug_printf("net_close(%d)=%d\n", s, ret); + + return ret; +} + +s32 net_select(s32 maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout) +{ + // not yet implemented + return -EINVAL; +} + +s32 net_setsockopt(s32 s, u32 level, u32 optname, const void *optval, socklen_t optlen) +{ + s32 ret; + STACK_ALIGN(struct setsockopt_params,params,1,32); + + if (net_ip_top_fd < 0) return -ENXIO; + if (optlen < 0 || optlen > 20) return -EINVAL; + + memset(params, 0, sizeof(struct setsockopt_params)); + params->socket = s; + params->level = level; + params->optname = optname; + params->optlen = optlen; + if (optval && optlen) memcpy (params->optval, optval, optlen); + + ret = _net_convert_error(IOS_Ioctl(net_ip_top_fd, IOCTL_SO_SETSOCKOPT, params, sizeof(struct setsockopt_params), NULL, 0)); + + debug_printf("net_setsockopt(%d, %u, %u, %p, %d)=%d\n", s, level, optname, optval, optlen, ret); + return ret; +} + +s32 net_ioctl(s32 s, u32 cmd, void *argp) +{ + u32 flags; + u32 *intp = (u32 *)argp; + + if (net_ip_top_fd < 0) return -ENXIO; + if (!intp) return -EINVAL; + + switch (cmd) { + case FIONBIO: + flags = net_fcntl(s, F_GETFL, 0); + flags &= ~IOS_O_NONBLOCK; + if (*intp) flags |= IOS_O_NONBLOCK; + return net_fcntl(s, F_SETFL, flags); + default: + return -EINVAL; + } +} + +s32 net_fcntl(s32 s, u32 cmd, u32 flags) +{ + s32 ret; + STACK_ALIGN(u32, params, 3, 32); + + if (net_ip_top_fd < 0) return -ENXIO; + if (cmd != F_GETFL && cmd != F_SETFL) return -EINVAL; + + + params[0] = s; + params[1] = cmd; + params[2] = flags; + + ret = _net_convert_error(IOS_Ioctl(net_ip_top_fd, IOCTL_SO_FCNTL, params, 12, NULL, 0)); + + debug_printf("net_fcntl(%d, %d, %x)=%d\n", params[0], params[1], params[2], ret); + + return ret; +} + + +/*! + * \fn s32 net_poll(struct pollsd *sds, u32 nsds, s64 timeout) + * \brief Poll a set of sockets for a set of events. + * + * \param[in] sds a pointer to an array of pollsd structures + * \param[in] nsds the number of elements in the sds array + * \param[in] time in milliseconds before the function should timeout + * + * \return the number of structures in sds that now have non-zero revent fields + */ +s32 net_poll(struct pollsd *sds,s32 nsds,s32 timeout) +{ + union ullc { + u64 ull; + u32 ul[2]; + }; + + s32 ret; + union ullc outv; + struct pollsd *psds; + STACK_ALIGN(u64,params,1,32); + + if(net_ip_top_fd<0) return -ENXIO; + if(sds==NULL || nsds==0) return -EINVAL; + + psds = net_malloc((nsds*sizeof(struct pollsd))); + if(psds==NULL) { + debug_printf("net_poll: failed to alloc %d bytes\n", nsds * sizeof(struct pollsd)); + return IPC_ENOMEM; + } + + outv.ul[0] = 0; + outv.ul[1] = timeout; + params[0] = outv.ull; + memcpy(psds,sds,(nsds*sizeof(struct pollsd))); + + ret = _net_convert_error(IOS_Ioctl(net_ip_top_fd, IOCTL_SO_POLL, params, 8, psds, (nsds * sizeof(struct pollsd)))); + + memcpy(sds,psds,(nsds*sizeof(struct pollsd))); + + net_free(psds); + + debug_printf("net_poll(sds, %d, %lld)=%d\n", nsds, params[0], ret); + + return ret; +} + +s32 if_config(char *local_ip, char *netmask, char *gateway,bool use_dhcp, int max_retries) +{ + s32 i,ret; + struct in_addr hostip; + + if (!use_dhcp) + return -EINVAL; + + for (i = 0; i < MAX_INIT_RETRIES; ++i) { + ret = net_init(); + + if ((ret != -EAGAIN) && (ret != -ETIMEDOUT)) + break; + + usleep(50 * 1000); + } + + if (ret < 0) + return ret; + + hostip.s_addr = net_gethostip(); + if (local_ip && hostip.s_addr) { + strcpy(local_ip, inet_ntoa(hostip)); + return 0; + } + + return -1; +} + +s32 if_configex(struct in_addr *local_ip, struct in_addr *netmask, struct in_addr *gateway,bool use_dhcp, int max_retries) +{ + s32 i,ret; + struct in_addr hostip; + + if (!use_dhcp) + return -EINVAL; + + for (i = 0; i < MAX_INIT_RETRIES; ++i) { + ret = net_init(); + + if ((ret != -EAGAIN) && (ret != -ETIMEDOUT)) + break; + + usleep(50 * 1000); + } + + if (ret < 0) + return ret; + + hostip.s_addr = net_gethostip(); + if (local_ip && hostip.s_addr) { + *local_ip = hostip; + return 0; + } + + return -1; +} + +#endif /* defined(HW_RVL) */ diff --git a/wii/libogc/libogc/newlibc.c b/wii/libogc/libogc/newlibc.c new file mode 100644 index 0000000000..c9ab5624fa --- /dev/null +++ b/wii/libogc/libogc/newlibc.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include "sys_state.h" +#include "lwp_threads.h" + +int libc_reentrant; +struct _reent libc_globl_reent; + +extern void _wrapup_reent(struct _reent *); +extern void _reclaim_reent(struct _reent *); + +int __libc_create_hook(lwp_cntrl *curr_thr,lwp_cntrl *create_thr) +{ + create_thr->libc_reent = NULL; + return 1; +} + +int __libc_start_hook(lwp_cntrl *curr_thr,lwp_cntrl *start_thr) +{ + struct _reent *ptr; + + ptr = (struct _reent*)calloc(1,sizeof(struct _reent)); + if(!ptr) abort(); + + _REENT_INIT_PTR((ptr)); + + start_thr->libc_reent = ptr; + return 1; +} + +int __libc_delete_hook(lwp_cntrl *curr_thr, lwp_cntrl *delete_thr) +{ + struct _reent *ptr; + + if(curr_thr==delete_thr) + ptr = _REENT; + else + ptr = (struct _reent*)delete_thr->libc_reent; + + if(ptr && ptr!=&libc_globl_reent) { + _reclaim_reent(ptr); + free(ptr); + } + delete_thr->libc_reent = 0; + + if(curr_thr==delete_thr) _REENT = 0; + + return 1; +} + +void __libc_init(int reentrant) +{ + libc_globl_reent = (struct _reent)_REENT_INIT((libc_globl_reent)); + _REENT = &libc_globl_reent; + + if(reentrant) { + __lwp_thread_setlibcreent((void*)&_REENT); + libc_reentrant = reentrant; + } +} + +void __libc_wrapup() +{ + if(!__sys_state_up(__sys_state_get())) return; + if(_REENT!=&libc_globl_reent) { + _wrapup_reent(&libc_globl_reent); + _REENT = &libc_globl_reent; + } +} + + diff --git a/wii/libogc/libogc/ogc_crt0.S b/wii/libogc/libogc/ogc_crt0.S new file mode 100644 index 0000000000..57f56026b3 --- /dev/null +++ b/wii/libogc/libogc/ogc_crt0.S @@ -0,0 +1,157 @@ +#include + + .text + .section .init + + # crt0.s file for the GameCube V1.1 by Costis (costis@gbaemu.com)! + # + # Updates: Added support for clearing the BSS section so that global + # variables are cleared to 0 upon start-up. + # + # This is start-up code for initializing the GameCube system and hardware + # before executing the actual user program code. It clears the GPR's, + # initializes the FPR's, initializes the Data, Code, and L2 caches, clears + # and initializes SPR's, and disables exceptions (interrupts). + # + # Have fun!!! Please e-mail any suggestions or bugs to costis@gbaemu.com. + + # Entry Point + + .extern __InitBATS + .extern __InitPS + .extern __InitFPRS + .extern __InitCache + .extern __InitSystem + .extern __sbss_start, __bss_end, __CheckARGV + .globl _start, __main, __system_argv +_start: + b startup + .long 0x5f617267 +__argv: + .long 0 # argv magic + .long 0 # command line + .long 0 # command line length + .long 0 # argc + .long 0 # argv + .long 0 # end address of argv + +startup: + bl __InitBATS # Initialize BATs to a clear and known state + bl __InitGPRS # Initialize the General Purpose Registers + bl __InitHardware # Initialize some aspects of the Hardware + bl __InitSystem # Initialize more cache aspects, clear a few SPR's, and disable interrupts. + + bl __CheckARGV # check for argv & initialise struct + + lis r3,__isIPL@h + ori r3,r3,__isIPL@l + cmplwi r3,0 + bne 1f + + # Clear the SBSS section! + lis r3,__sbss_start@h + ori r3,r3,__sbss_start@l + li r4,0 + lis r5,__sbss_end@h + ori r5,r5,__sbss_end@l + sub r5,r5,r3 + bl _memset + + # Clear the BSS section! + lis r3,__bss_start@h + ori r3,r3,__bss_start@l + li r4,0 + lis r5,__bss_end@h + ori r5,r5,__bss_end@l + sub r5,r5,r3 + bl _memset + +1: + b SYS_Init # Branch to the user code! + +__InitGPRS: + # Clear all of the GPR's to 0 + li r0,0 + li r3,0 + li r4,0 + li r5,0 + li r6,0 + li r7,0 + li r8,0 + li r9,0 + li r10,0 + li r11,0 + li r12,0 + li r14,0 + li r15,0 + li r16,0 + li r17,0 + li r18,0 + li r19,0 + li r20,0 + li r21,0 + li r22,0 + li r23,0 + li r24,0 + li r25,0 + li r26,0 + li r27,0 + li r28,0 + li r29,0 + li r30,0 + li r31,0 + + lis sp,__crt0stack@h # we take 0x8173FFF0 as the topmost starting point for our stack,this gives us ~128Kb Stack + ori sp,sp,__crt0stack@l + addi sp,sp,-4 + stw r0,0(sp) + stwu sp,-56(sp) + + lis r2,_SDA2_BASE_@h + ori r2,r2,_SDA2_BASE_@l # Set the Small Data 2 (Read Only) base register. + lis r13,_SDA_BASE_@h + ori r13,r13,_SDA_BASE_@l # Set the Small Data (Read\Write) base register. + blr + +__InitHardware: + # Enable the Floating Point Registers + mfmsr r3 + ori r3,r3,MSR_FP + mtmsr r3 + + mflr r31 + bl __InitPS # Initialize Paired Singles + bl __InitFPRS # Initialize the FPR's + bl __InitCache # Initialize the system caches + mtlr r31 + blr + + //r3 = ptr, r4 = fill, r5 = size + .globl _memset +_memset: + clrlwi. r6,r5,29 + srwi r5,r5,2 + subi r3,r3,4 + mtctr r5 +1: stwu r4,4(r3) + bdnz 1b + cmplwi r6,0 + beq 3f +2: stbu r4,1(r3) + addic. r6,r6,-1 + bne+ 2b +3: blr + + .section .bss + .balign 8 +__crt0stack_end: + .space 0x4000 +__crt0stack: + + .globl __system_argv + .section .sdata,"aw",@progbits + .align 2 + .type __system_argv, @object + .size __system_argv, 4 +__system_argv: + .long __argv diff --git a/wii/libogc/libogc/pad.c b/wii/libogc/libogc/pad.c new file mode 100644 index 0000000000..8a77a8b278 --- /dev/null +++ b/wii/libogc/libogc/pad.c @@ -0,0 +1,756 @@ +#include +#include +#include +#include +#include +#include +#include +#include "asm.h" +#include "processor.h" +#include "si.h" +#include "pad.h" + +#define PAD_PRODPADS 6 + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +#define PAD_ENABLEDMASK(chan) (0x80000000>>chan); + +typedef struct _keyinput { + s8 stickX; + s8 stickY; + s8 substickX; + s8 substickY; + u8 triggerL; + u8 triggerR; + u16 up; + u16 down; + u16 state; + u32 chan; +} keyinput; + +typedef void (*SPECCallback)(u32,u32*,PADStatus*); + +static sampling_callback __pad_samplingcallback = NULL; +static SPECCallback __pad_makestatus = NULL; +static u32 __pad_initialized = 0; +static u32 __pad_enabledbits = 0; +static u32 __pad_resettingbits = 0; +static u32 __pad_recalibratebits = 0; +static u32 __pad_waitingbits = 0; +static u32 __pad_pendingbits = 0; +static u32 __pad_checkingbits = 0; +static u32 __pad_resettingchan = 32; +static u32 __pad_spec = 5; + +static u32 __pad_analogmode = 0x00000300; +static u32 __pad_cmdreadorigin = 0x41000000; +static u32 __pad_cmdcalibrate = 0x42000000; +static u32 __pad_xpatchbits = 0xf0000000; + +static u32 __pad_recalibrated$207 = 0; + +static u32 __pad_type[PAD_CHANMAX]; +static s8 __pad_origin[PAD_CHANMAX][12]; +static u32 __pad_cmdprobedevice[PAD_CHANMAX]; + +static keyinput __pad_keys[PAD_CHANMAX]; +static u8 __pad_clampregion[8] = {30, 180, 15, 72, 40, 15, 59, 31}; + +static vu32* const _siReg = (u32*)0xCC006400; +static vu16* const _viReg = (u16*)0xCC002000; + +extern u32 __PADFixBits; + +static void __pad_enable(u32 chan); +static void __pad_disable(u32 chan); +static void __pad_doreset(); +static s32 __pad_onreset(s32 final); + +static sys_resetinfo pad_resetinfo = { + {}, + __pad_onreset, + 127 +}; + +extern void udelay(int); + +static s32 __pad_onreset(s32 final) +{ + u32 ret; + + if(__pad_samplingcallback!=NULL) PAD_SetSamplingCallback(NULL); + + if(final==FALSE) { + ret = PAD_Sync(); + if(__pad_recalibrated$207==0 && ret) { + __pad_recalibrated$207 = PAD_Recalibrate(0xf0000000); + return 0; + } + return ret; + } + __pad_recalibrated$207 = 0; + return 1; +} + +static void SPEC0_MakeStatus(u32 chan,u32 *data,PADStatus *status) +{ + status->button = 0; + + if(data[0]&0x00080000) status->button |= 0x0100; + if(data[0]&0x00200000) status->button |= 0x0200; + if(data[0]&0x01000000) status->button |= 0x0400; + if(data[0]&0x00010000) status->button |= 0x0800; + if(data[0]&0x00100000) status->button |= 0x1000; + + status->stickX = (s8)(data[1]>>16); + status->stickY = (s8)(data[1]>>24); + status->substickX = (s8)data[1]; + status->substickY = (s8)(data[1]>>8); + status->triggerL = (u8)_SHIFTR(data[0],8,8); + status->triggerR = (u8)(data[0]&0xff); + status->analogA = 0; + status->analogB = 0; + + if(status->triggerL>=0xaa) status->button |= 0x40; + if(status->triggerR>=0xaa) status->button |= 0x20; + + status->stickX -= 128; + status->stickY -= 128; + status->substickX -= 128; + status->substickY -= 128; +} + +static void SPEC1_MakeStatus(u32 chan,u32 *data,PADStatus *status) +{ + status->button = 0; + + if(data[0]&0x00800000) status->button |= 0x0100; + if(data[0]&0x01000000) status->button |= 0x0200; + if(data[0]&0x00200000) status->button |= 0x0400; + if(data[0]&0x00100000) status->button |= 0x0800; + if(data[0]&0x02000000) status->button |= 0x1000; + + status->stickX = (s8)(data[1]>>16); + status->stickY = (s8)(data[1]>>24); + status->substickX = (s8)data[1]; + status->substickY = (s8)(data[1]>>8); + status->triggerL = (u8)_SHIFTR(data[0],8,8); + status->triggerR = (u8)data[0]&0xff; + status->analogA = 0; + status->analogB = 0; + + if(status->triggerL>=0xaa) status->button |= 0x40; + if(status->triggerR>=0xaa) status->button |= 0x20; + + status->stickX -= 128; + status->stickY -= 128; + status->substickX -= 128; + status->substickY -= 128; +} + +static s8 __pad_clampS8(s8 var,s8 org) +{ + s32 siorg = (s32)org; + if(siorg>0) { + siorg -= 128; + if((s32)varbutton = _SHIFTR(data[0],16,14); + + status->stickX = (s8)(data[0]>>8); + status->stickY = (s8)data[0]; + mode = __pad_analogmode&0x0700; + if(mode==0x100) { + status->substickX = (s8)((data[1]>>24)&0xf0); + status->substickY = (s8)((data[1]>>8)&0xff); + status->triggerL = (u8)((data[1]>>16)&0xff); + status->triggerR = (u8)((data[1]>>8)&0xff); + status->analogA = (u8)(data[1]&0xf0); + status->analogB = (u8)((data[1]<<4)&0xf0); + } else if(mode==0x200) { + status->substickX = (s8)((data[1]>>24)&0xf0); + status->substickY = (s8)((data[1]>>20)&0xf0); + status->triggerL = (u8)((data[1]>>16)&0xf0); + status->triggerR = (u8)((data[1]>>12)&0xf0); + status->analogA = (u8)((data[1]>>8)&0xff); + status->analogB = (s8)data[1]&0xff; + } else if(mode==0x300) { + status->substickX = (s8)((data[1]>>24)&0xff); + status->substickY = (s8)((data[1]>>16)&0xff); + status->triggerL = (u8)((data[1]>>8)&0xff); + status->triggerR = (u8)data[1]&0xff; + status->analogA = 0; + status->analogB = 0; + } else if(mode==0x400) { + status->substickX = (s8)((data[1]>>24)&0xff); + status->substickY = (s8)((data[1]>>16)&0xff); + status->triggerL = 0; + status->triggerR = 0; + status->analogA = (u8)((data[1]>>8)&0xff); + status->analogB = (u8)data[1]&0xff ; + } else if(!mode || mode==0x500 || mode==0x600 || mode==0x700) { + status->substickX = (s8)((data[1]>>24)&0xff); + status->substickY = (s8)((data[1]>>16)&0xff); + status->triggerL = (u8)((data[1]>>8)&0xf0); + status->triggerR = (u8)((data[1]>>4)&0xf0); + status->analogA = (u8)(data[1]&0xf0); + status->analogB = (u8)((data[1]<<4)&0xf0); + } + + status->stickX -= 128; + status->stickY -= 128; + status->substickX -= 128; + status->substickY -= 128; + status->stickX = __pad_clampS8(status->stickX,__pad_origin[chan][2]); + status->stickY = __pad_clampS8(status->stickY,__pad_origin[chan][3]); + status->substickX = __pad_clampS8(status->substickX,__pad_origin[chan][4]); + status->substickY = __pad_clampS8(status->substickY,__pad_origin[chan][5]); + status->triggerL = __pad_clampU8(status->triggerL,__pad_origin[chan][6]); + status->triggerR = __pad_clampU8(status->triggerR,__pad_origin[chan][7]); +} + +static void __pad_clampstick(s8 *px,s8 *py,s8 max,s8 xy,s8 min) +{ + s32 x,y,signX,signY,d; + + x = *px; + y = *py; + if(x>=0) signX = 1; + else { signX = -1; x = -(x); } + + if(y>=0) signY = 1; + else { signY = -1; y = -(y); } + + if(x<=min) x = 0; + else x -= min; + + if(y<=min) y = 0; + else y -= min; + + if(x!=0 || y!=0) { + s32 xx,yy,maxy; + + xx = (x * xy); + yy= (y * xy); + maxy = (max * xy); + if(yy<=xx) { + d = ((x * xy) + (y * (max - xy))); + if(maxy*trigger) *trigger = 0; + else if(max<*trigger) *trigger = (max - min); + else *trigger -= min; +} + +static void __pad_updateorigin(s32 chan) +{ + u32 mode,mask,type; + + mask = PAD_ENABLEDMASK(chan); + mode = __pad_analogmode&0x0700; + if(mode==0x0100) { + __pad_origin[chan][4] &= ~0x0f; + __pad_origin[chan][5] &= ~0x0f; + __pad_origin[chan][8] &= ~0x0f; + __pad_origin[chan][9] &= ~0x0f; + } else if(mode==0x200) { + __pad_origin[chan][4] &= ~0x0f; + __pad_origin[chan][5] &= ~0x0f; + __pad_origin[chan][6] &= ~0x0f; + __pad_origin[chan][7] &= ~0x0f; + } + + __pad_origin[chan][2] -= 128; + __pad_origin[chan][3] -= 128; + __pad_origin[chan][4] -= 128; + __pad_origin[chan][5] -= 128; + + if(__pad_xpatchbits&mask && (s32)__pad_origin[chan][2]>64) { + type = SI_GetType(chan)&~0xffff; + if(!(type&~0x09ffffff)) __pad_origin[chan][2] = 0; + } +} + +static void __pad_probecallback(s32 chan,u32 type) +{ + if(!(type&0x0f)) { + __pad_enable(__pad_resettingchan); + __pad_waitingbits |= PAD_ENABLEDMASK(__pad_resettingchan); + } + __pad_doreset(); +} + +static void __pad_origincallback(s32 chan,u32 type) +{ + if(!(type&0x0f)) { + __pad_updateorigin(__pad_resettingchan); + __pad_enable(__pad_resettingchan); + } + __pad_doreset(); +} + +static void __pad_originupdatecallback(s32 chan,u32 type) +{ + u32 en_bits = __pad_enabledbits&PAD_ENABLEDMASK(chan); + + if(en_bits) { + if(!(type&0x0f)) __pad_updateorigin(chan); + if(type&SI_ERROR_NO_RESPONSE) __pad_disable(chan); + } +} + +static void __pad_typeandstatuscallback(s32 chan,u32 type) +{ + u32 recal_bits,mask,ret = 0; + mask = PAD_ENABLEDMASK(__pad_resettingchan); + recal_bits = __pad_recalibratebits&mask; + __pad_recalibratebits &= ~mask; + + if(type&0x0f) { + __pad_doreset(); + return; + } + + __pad_type[__pad_resettingchan] = (type&~0xff); + if(((type&SI_TYPE_MASK)-SI_TYPE_GC) + || !(type&SI_GC_STANDARD)) { + __pad_doreset(); + return; + } + + if(__pad_spec<2) { + __pad_enable(__pad_resettingchan); + __pad_doreset(); + return; + } + + if(!(type&SI_GC_WIRELESS) || type&SI_WIRELESS_IR) { + if(recal_bits) ret = SI_Transfer(__pad_resettingchan,&__pad_cmdcalibrate,3,__pad_origin[__pad_resettingchan],10,__pad_origincallback,0); + else ret = SI_Transfer(__pad_resettingchan,&__pad_cmdreadorigin,1,__pad_origin[__pad_resettingchan],10,__pad_origincallback,0); + } else if(type&SI_WIRELESS_FIX_ID && !(type&SI_WIRELESS_CONT_MASK) && !(type&SI_WIRELESS_LITE)) { + if(type&SI_WIRELESS_RECEIVED) ret = SI_Transfer(__pad_resettingchan,&__pad_cmdreadorigin,1,__pad_origin[__pad_resettingchan],10,__pad_origincallback,0); + else ret = SI_Transfer(__pad_resettingchan,&__pad_cmdprobedevice[__pad_resettingchan],3,__pad_origin[__pad_resettingchan],8,__pad_probecallback,0); + } + if(!ret) { + __pad_pendingbits |= mask; + __pad_doreset(); + } +} + +static void __pad_receivecheckcallback(s32 chan,u32 type) +{ + u32 mask,tmp; + mask = PAD_ENABLEDMASK(chan); + if(__pad_enabledbits&mask) { + tmp = type&0xff; + type &= ~0xff; + __pad_waitingbits &= ~mask; + __pad_checkingbits &= ~mask; + if(!(tmp&0x0f) + && (type&SI_GC_WIRELESS) && (type&SI_WIRELESS_RECEIVED) && (type&SI_WIRELESS_FIX_ID) + && !(type&SI_WIRELESS_IR) && !(type&SI_WIRELESS_CONT_MASK) && !(type&SI_WIRELESS_LITE)) SI_Transfer(chan,&__pad_cmdreadorigin,1,__pad_origin[chan],10,__pad_originupdatecallback,0); + else __pad_disable(chan); + } +} + +static void __pad_enable(u32 chan) +{ + u32 buf[2]; + __pad_enabledbits |= PAD_ENABLEDMASK(chan); + SI_GetResponse(chan,(void*)buf); + SI_SetCommand(chan,(__pad_analogmode|0x00400000)); + SI_EnablePolling(__pad_enabledbits); +} + +static void __pad_disable(u32 chan) +{ + u32 level,mask; + _CPU_ISR_Disable(level); + mask = PAD_ENABLEDMASK(chan); + SI_DisablePolling(mask); + __pad_enabledbits &= ~mask; + __pad_waitingbits &= ~mask; + __pad_pendingbits &= ~mask; + __pad_checkingbits &= ~mask; + SYS_SetWirelessID(chan,0); + _CPU_ISR_Restore(level); +} + +static void __pad_doreset() +{ + __pad_resettingchan = cntlzw(__pad_resettingbits); + if(__pad_resettingchan==32) return; + __pad_resettingbits &= ~PAD_ENABLEDMASK(__pad_resettingchan); + + memset(__pad_origin[__pad_resettingchan],0,12); + SI_GetTypeAsync(__pad_resettingchan,__pad_typeandstatuscallback); +} + +static void __pad_samplinghandler(u32 irq,void *ctx) +{ +} + +u32 __PADDisableRecalibration(s32 disable) +{ + u32 level,ret; + u8 *ram_recaldis = (u8*)0x800030e3; + + _CPU_ISR_Disable(level); + + ret = 0; + if(ram_recaldis[0]&0x40) ret = 1; + + ram_recaldis[0] &= 0xbf; + if(disable) ram_recaldis[0] |= 0x40; + + _CPU_ISR_Restore(level); + + return ret; +} + +u32 PAD_Init() +{ + u32 chan; + u16 prodpads = PAD_PRODPADS; + if(__pad_initialized) return 1; + + if(__pad_spec) PAD_SetSpec(__pad_spec); + + memset(__pad_keys,0,sizeof(keyinput)*PAD_CHANMAX); + + __pad_recalibratebits = 0xf0000000; + + chan = 0; + while(chan<4) { + __pad_keys[chan].chan = -1; + __pad_cmdprobedevice[chan] = 0x4d000000|(chan<<22)|_SHIFTL(prodpads,8,14); + chan++; + } + + SI_RefreshSamplingRate(); + SYS_RegisterResetFunc(&pad_resetinfo); + + __pad_initialized = 1; + return PAD_Reset(0xf0000000); +} + +u32 PAD_Read(PADStatus *status) +{ + u32 chan,mask,ret; + u32 level,sistatus,type; + u32 buf[2]; + _CPU_ISR_Disable(level); + chan = 0; + ret = 0; + while(chan<4) { + mask = PAD_ENABLEDMASK(chan); + if(__pad_pendingbits&mask) { + PAD_Reset(0); + memset(&status[chan],0,sizeof(PADStatus)); + status[chan].err = PAD_ERR_NOT_READY; + } else if(__pad_resettingbits&mask || __pad_resettingchan==chan) { + memset(&status[chan],0,sizeof(PADStatus)); + status[chan].err = PAD_ERR_NOT_READY; + } else if(!(__pad_enabledbits&mask)) { + memset(&status[chan],0,sizeof(PADStatus)); + status[chan].err = PAD_ERR_NO_CONTROLLER; + } else { + if(SI_IsChanBusy(chan)) { + memset(&status[chan],0,sizeof(PADStatus)); + status[chan].err = PAD_ERR_TRANSFER; + } else { + sistatus = SI_GetStatus(chan); + if(sistatus&SI_ERROR_NO_RESPONSE) { + SI_GetResponse(chan,(void*)buf); + if(!(__pad_waitingbits&mask)) { + memset(&status[chan],0,sizeof(PADStatus)); + status[chan].err = PAD_ERR_NONE; + if(!(__pad_checkingbits&mask)) { + __pad_checkingbits |= mask; + SI_GetTypeAsync(chan,__pad_receivecheckcallback); + } + } else { + __pad_disable(chan); + memset(&status[chan],0,sizeof(PADStatus)); + status[chan].err = PAD_ERR_NO_CONTROLLER; + } + } else { + type = SI_GetType(chan); + if(!(type&SI_WIRELESS_STATE)) ret |= mask; + if(!SI_GetResponse(chan,buf) + || buf[0]&0x80000000) { + memset(&status[chan],0,sizeof(PADStatus)); + status[chan].err = PAD_ERR_TRANSFER; + } else { + __pad_makestatus(chan,buf,&status[chan]); + if(status[chan].button&0x00002000) { + memset(&status[chan],0,sizeof(PADStatus)); + status[chan].err = PAD_ERR_TRANSFER; + SI_Transfer(chan,&__pad_cmdreadorigin,1,__pad_origin[chan],10,__pad_originupdatecallback,0); + } else { + status[chan].err = PAD_ERR_NONE; + status[chan].button &= ~0x80; + } + } + } + } + } + chan++; + + } + _CPU_ISR_Restore(level); + + return ret; +} + +u32 PAD_Reset(u32 mask) +{ + u32 level; + u32 pend_bits,en_bits; + + _CPU_ISR_Disable(level); + pend_bits = (__pad_pendingbits|mask); + __pad_pendingbits = 0; + + pend_bits &= ~(__pad_waitingbits|__pad_checkingbits); + __pad_resettingbits |= pend_bits; + + en_bits = (__pad_resettingbits&__pad_enabledbits); + __pad_enabledbits &= ~pend_bits; + + if(__pad_spec==4) __pad_recalibratebits |= pend_bits; + + SI_DisablePolling(en_bits); + if(__pad_resettingchan==32) __pad_doreset(); + _CPU_ISR_Restore(level); + + return 1; +} + +u32 PAD_Recalibrate(u32 mask) +{ + u32 level; + + _CPU_ISR_Disable(level); + + _CPU_ISR_Restore(level); + return 1; +} + +u32 PAD_Sync() +{ + u32 ret = 0; + + if(!__pad_resettingbits && __pad_resettingchan==32) { + if(SI_Busy()==0) ret = 1; + } + return ret; +} + +void PAD_SetSpec(u32 spec) +{ + if(__pad_initialized) return; + + __pad_spec = 0; + if(spec==0) __pad_makestatus = SPEC0_MakeStatus; + else if(spec==1) __pad_makestatus = SPEC1_MakeStatus; + else if(spec<6) __pad_makestatus = SPEC2_MakeStatus; + + __pad_spec = spec; +} + +void PAD_ControlMotor(s32 chan,u32 cmd) +{ + u32 level; + u32 mask,type; + + _CPU_ISR_Disable(level); + + mask = PAD_ENABLEDMASK(chan); + if(__pad_enabledbits&mask) { + type = SI_GetType(chan); + if(!(type&SI_GC_NOMOTOR)) { + if(__pad_spec<2 && cmd==PAD_MOTOR_STOP_HARD) cmd = 0; + + cmd = 0x00400000|__pad_analogmode|(cmd&0x03); + SI_SetCommand(chan,cmd); + SI_TransferCommands(); + } + } + _CPU_ISR_Restore(level); +} + +sampling_callback PAD_SetSamplingCallback(sampling_callback cb) +{ + sampling_callback ret; + + ret = __pad_samplingcallback; + __pad_samplingcallback = cb; + if(cb) { + SI_RegisterPollingHandler(__pad_samplinghandler); + } else { + SI_UnregisterPollingHandler(__pad_samplinghandler); + } + + return ret; +} + +void PAD_Clamp(PADStatus *status) +{ + s32 i; + + for(i=0;i>i); + + switch(padstatus[i].err) { + case PAD_ERR_NONE: + oldstate = __pad_keys[i].state; + state = padstatus[i].button; + __pad_keys[i].stickX = padstatus[i].stickX; + __pad_keys[i].stickY = padstatus[i].stickY; + __pad_keys[i].substickX = padstatus[i].substickX; + __pad_keys[i].substickY = padstatus[i].substickY; + __pad_keys[i].triggerL = padstatus[i].triggerL; + __pad_keys[i].triggerR = padstatus[i].triggerR; + __pad_keys[i].up = oldstate & ~state; + __pad_keys[i].down = state & (state ^ oldstate); + __pad_keys[i].state = state; + __pad_keys[i].chan = i; + + connected |= (1<PAD_CHAN3 || __pad_keys[pad].chan==-1) return 0; + return __pad_keys[pad].up; +} + +u16 PAD_ButtonsDown(int pad) +{ + if(padPAD_CHAN3 || __pad_keys[pad].chan==-1) return 0; + return __pad_keys[pad].down; +} + +u16 PAD_ButtonsHeld(int pad) +{ + if(padPAD_CHAN3 || __pad_keys[pad].chan==-1) return 0; + return __pad_keys[pad].state; +} + +s8 PAD_SubStickX(int pad) +{ + if(padPAD_CHAN3 || __pad_keys[pad].chan==-1) return 0; + return __pad_keys[pad].substickX; +} + +s8 PAD_SubStickY(int pad) +{ + if(padPAD_CHAN3 || __pad_keys[pad].chan==-1) return 0; + return __pad_keys[pad].substickY; +} + +s8 PAD_StickX(int pad) +{ + if(padPAD_CHAN3 || __pad_keys[pad].chan==-1) return 0; + return __pad_keys[pad].stickX; +} + +s8 PAD_StickY(int pad) +{ + if(padPAD_CHAN3 || __pad_keys[pad].chan==-1) return 0; + return __pad_keys[pad].stickY; +} + +u8 PAD_TriggerL(int pad) +{ + if(padPAD_CHAN3 || __pad_keys[pad].chan==-1) return 0; + return __pad_keys[pad].triggerL; +} + +u8 PAD_TriggerR(int pad) +{ + if(padPAD_CHAN3 || __pad_keys[pad].chan==-1) return 0; + return __pad_keys[pad].triggerR; +} diff --git a/wii/libogc/libogc/sbrk.c b/wii/libogc/libogc/sbrk.c new file mode 100644 index 0000000000..f74bc977f9 --- /dev/null +++ b/wii/libogc/libogc/sbrk.c @@ -0,0 +1,92 @@ +#include <_ansi.h> +#include <_syslist.h> +#include +#include +#include +#include + +#include "asm.h" +#include "processor.h" +#include "system.h" + +#if defined(HW_RVL) +u32 MALLOC_MEM2 __attribute__((weak)) = 1; +#endif + +void* _DEFUN(__libogc_sbrk_r,(ptr,incr), + struct _reent *ptr _AND + ptrdiff_t incr) +{ + u32 level; + char *heap_end = 0; + char *prev_heap = 0; +#if defined(HW_RVL) + static char *mem2_start = NULL; +#endif + + _CPU_ISR_Disable(level); +#if defined(HW_RVL) + if(MALLOC_MEM2) { + // use MEM2 aswell for malloc + if(mem2_start==NULL) + heap_end = (char*)SYS_GetArenaLo(); + else + heap_end = (char*)SYS_GetArena2Lo(); + + if(mem2_start) { + // we're in MEM2 + if((heap_end+incr)>(char*)SYS_GetArena2Hi()) { + // out of MEM2 case + ptr->_errno = ENOMEM; + prev_heap = (char *)-1; + } else if ((heap_end+incr) < mem2_start) { + // trying to sbrk() back below the MEM2 start barrier + ptr->_errno = EINVAL; + prev_heap = (char *)-1; + } else { + // success case + prev_heap = heap_end; + SYS_SetArena2Lo((void*)(heap_end+incr)); + } + // if MEM2 area is exactly at the barrier, transition back to MEM1 again + if(SYS_GetArena2Lo() == mem2_start) mem2_start = NULL; + } else { + // we're in MEM1 + if((heap_end+incr)>(char*)SYS_GetArenaHi()) { + // out of MEM1, transition into MEM2 + if(((char*)SYS_GetArena2Lo() + incr) > (char*)SYS_GetArena2Hi()) { + // this increment doesn't fit in available MEM2 + ptr->_errno = ENOMEM; + prev_heap = (char *)-1; + } else { + // MEM2 is available, move into it + mem2_start = heap_end = prev_heap = SYS_GetArena2Lo(); + SYS_SetArena2Lo((void*)(heap_end+incr)); + } + } else { + // MEM1 is available (or we're freeing memory) + prev_heap = heap_end; + SYS_SetArenaLo((void*)(heap_end+incr)); + } + } + } else { +#endif + heap_end = (char*)SYS_GetArenaLo(); + + if((heap_end+incr)>(char*)SYS_GetArenaHi()) { + + ptr->_errno = ENOMEM; + prev_heap = (char *)-1; + + } else { + + prev_heap = heap_end; + SYS_SetArenaLo((void*)(heap_end+incr)); + } +#if defined(HW_RVL) + } +#endif + _CPU_ISR_Restore(level); + + return (void*)prev_heap; +} diff --git a/wii/libogc/libogc/sdgecko_buf.c b/wii/libogc/libogc/sdgecko_buf.c new file mode 100644 index 0000000000..fd48531fd2 --- /dev/null +++ b/wii/libogc/libogc/sdgecko_buf.c @@ -0,0 +1,47 @@ +#include +#include + +#include "card_cmn.h" +#include "card_buf.h" + +#define BUF_POOL_CNT 3 + +typedef struct _buf_node { + struct _buf_node *next; + u8 data[SECTOR_SIZE+2]; + +} BufNode; + +static BufNode s_buf[BUF_POOL_CNT]; +static BufNode *s_freepool; + +void sdgecko_initBufferPool() +{ + u32 i; + for(i=0;idata; + s_freepool = s_freepool->next; + } + + return buf; +} + +void sdgecko_freeBuffer(u8 *buf) +{ + if(buf) { + BufNode *node = (BufNode*)(buf-offsetof(BufNode,data)); + node->next = s_freepool; + s_freepool = node; + } +} diff --git a/wii/libogc/libogc/sdgecko_io.c b/wii/libogc/libogc/sdgecko_io.c new file mode 100644 index 0000000000..13358dcdea --- /dev/null +++ b/wii/libogc/libogc/sdgecko_io.c @@ -0,0 +1,1356 @@ +#include +#include +#include +#include +#include + +#include "asm.h" +#include "processor.h" +#include "exi.h" +#include "lwp.h" +#include "system.h" +#include "semaphore.h" +#include "card_cmn.h" +//#include "card_fat.h" +#include "card_io.h" + +// SDHC support +// added by emu_kidid +#define SECTOR_ADDRESSING 0 +#define BYTE_ADDRESSING 1 +#define TYPE_SD 0 +#define TYPE_SDHC 1 + +#define MMC_ERROR_PARAM 0x0040 +#define MMC_ERROR_ADDRESS 0x0020 +#define MMC_ERROR_ERASE_SEQ 0x0010 +#define MMC_ERROR_CRC 0x0008 +#define MMC_ERROR_ILL 0x0004 +#define MMC_ERROR_ERASE_RES 0x0002 +#define MMC_ERROR_IDLE 0x0001 + +#define CARDIO_OP_INITFAILED 0x8000 +#define CARDIO_OP_TIMEDOUT 0x4000 +#define CARDIO_OP_IOERR_IDLE 0x2000 +#define CARDIO_OP_IOERR_PARAM 0x1000 +#define CARDIO_OP_IOERR_WRITE 0x0200 +#define CARDIO_OP_IOERR_ADDR 0x0100 +#define CARDIO_OP_IOERR_CRC 0x0002 +#define CARDIO_OP_IOERR_ILL 0x0001 +#define CARDIO_OP_IOERR_FATAL (CARDIO_OP_IOERR_PARAM|CARDIO_OP_IOERR_WRITE|CARDIO_OP_IOERR_ADDR|CARDIO_OP_IOERR_CRC|CARDIO_OP_IOERR_ILL) + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +typedef s32 (*cardiocallback)(s32 drv_no); + +u8 g_CID[MAX_DRIVE][16]; +u8 g_CSD[MAX_DRIVE][16]; +u8 g_CardStatus[MAX_DRIVE][64]; + +u8 g_mCode[MAX_MI_NUM] = { 0x03 }; + +u16 g_dCode[MAX_MI_NUM][MAX_DI_NUM] = +{ + { + 0x033f, /* SD 8Mb */ + 0x0383, /* SD 16Mb */ + 0x074b, /* SD 32Mb */ + 0x0edf, /* SD 64Mb */ + 0x0f03 /* SD 128Mb */ + } +}; + +static u8 _ioWPFlag; +static u8 _ioClrFlag; +static u32 _ioCardFreq; +static u32 _ioRetryCnt; +static cardiocallback _ioRetryCB = NULL; + +static lwpq_t _ioEXILock[MAX_DRIVE]; + +static u32 _ioPageSize[MAX_DRIVE]; +static u32 _ioFlag[MAX_DRIVE]; +static u32 _ioError[MAX_DRIVE]; +static bool _ioCardInserted[MAX_DRIVE]; + +static u8 _ioResponse[MAX_DRIVE][128]; +static u8 _ioCrc7Table[256]; +static u16 _ioCrc16Table[256]; + +// SDHC support +static u32 _initType[MAX_DRIVE]; +static u32 _ioAddressingType[MAX_DRIVE]; + +extern unsigned long gettick(); + +static __inline__ u32 __check_response(s32 drv_no,u8 res) +{ + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + _ioError[drv_no] = 0; + if(_ioFlag[drv_no]==INITIALIZING && res&MMC_ERROR_IDLE) { + _ioError[drv_no] |= CARDIO_OP_IOERR_IDLE; + return CARDIO_ERROR_READY; + } else { + if(res&MMC_ERROR_PARAM) _ioError[drv_no] |= CARDIO_OP_IOERR_PARAM; + if(res&MMC_ERROR_ADDRESS) _ioError[drv_no] |= CARDIO_OP_IOERR_ADDR; + if(res&MMC_ERROR_CRC) _ioError[drv_no] |= CARDIO_OP_IOERR_CRC; + if(res&MMC_ERROR_ILL) _ioError[drv_no] |= CARDIO_OP_IOERR_ILL; + } + return ((_ioError[drv_no]&CARDIO_OP_IOERR_FATAL)?CARDIO_ERROR_INTERNAL:CARDIO_ERROR_READY); +} + +static void __init_crc7() +{ + s32 i,j; + u8 c,crc7; + + crc7 = 0; + for(i=0;i<256;i++) { + c = i; + crc7 = 0; + for(j=0;j<8;j++) { + crc7 <<= 1; + if((crc7^c)&0x80) crc7 ^= 0x09; + c <<= 1; + } + crc7 &= 0x7f; + _ioCrc7Table[i] = crc7; + } +} + +static u8 __make_crc7(void *buffer,u32 len) +{ + s32 i; + u8 crc7; + u8 *ptr; + + crc7 = 0; + ptr = buffer; + for(i=0;i>bcnt)&0xff); + if(mask&val) { + res |= 0x01; + if(!(res&0x0008)) res |= 0x0008; + else res &= ~0x0008; + + } else if(res&0x0008) res |= 0x0008; + else res &= ~0x0008; + + mask >>= 1; + bcnt++; + } + ptr++; + cnt++; + } + return (res<<1)&0xff; +} +*/ +static void __init_crc16() +{ + s32 i,j; + u16 crc16,c; + + for(i=0;i<256;i++) { + crc16 = 0; + c = ((u16)i)<<8; + for(j=0;j<8;j++) { + if((crc16^c)&0x8000) crc16 = (crc16<<1)^0x1021; + else crc16 <<= 1; + + c <<= 1; + } + + _ioCrc16Table[i] = crc16; + } +} + +static u16 __make_crc16(void *buffer,u32 len) +{ + s32 i; + u8 *ptr; + u16 crc16; + + crc16 = 0; + ptr = buffer; + for(i=0;i>8)^(u16)(ptr[i]))]; + + return crc16; +} + +/* Old way, realtime +static u16 __make_crc16(void *buffer,u32 len) +{ + u32 mask,cnt,bcnt; + u32 res,val,tmp; + u8 *ptr = (u8*)buffer; + + cnt = 0; + res = 0; + while(cnt>(bcnt+8))&0xff); + if(mask&tmp) { + res = (res<<1)|0x0001; + if(!(res&0x0020)) res |= 0x0020; + else res &= ~0x0020; + if(!(res&0x1000)) res |= 0x1000; + else res &= ~0x1000; + } else { + res = (res<<1)&~0x0001; + if(res&0x0020) res |= 0x0020; + else res &= ~0x0020; + if(res&0x1000) res |= 0x1000; + else res &= ~0x1000; + } + mask >>= 1; + bcnt++; + } + ptr++; + cnt++; + } + + return (res&0xffff); +} +*/ + +static u32 __card_checktimeout(s32 drv_no,u32 startT,u32 timeout) +{ + u32 endT,diff; + u32 msec; + + endT = gettick(); + if(endT=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + _ioClrFlag = 0xff; + cmd[0] = 0x40; + crc = __make_crc7(cmd,5); + + if(_ioWPFlag) { + _ioClrFlag = 0x00; + for(cnt=0;cnt<5;cnt++) cmd[cnt] ^= -1; + } + + for(cnt=0;cnt<128;cnt++) dummy[cnt] = _ioClrFlag; + + __exi_wait(drv_no); + + if(EXI_SelectSD(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) { + EXI_Unlock(drv_no); + return CARDIO_ERROR_NOCARD; + } + + + cnt = 0; + while(cnt<20) { + if(EXI_ImmEx(drv_no,dummy,128,EXI_WRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + cnt++; + } + EXI_Deselect(drv_no); + + if(EXI_Select(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) { + EXI_Unlock(drv_no); + return CARDIO_ERROR_NOCARD; + } + + crc |= 0x01; + if(_ioWPFlag) crc ^= -1; + if(EXI_ImmEx(drv_no,cmd,5,EXI_WRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + + if(EXI_ImmEx(drv_no,&crc,1,EXI_WRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_READY; +} + +static s32 __card_writecmd(s32 drv_no,void *buf,s32 len) +{ + u8 crc,*ptr; + u8 dummy[32]; + u32 cnt; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + ptr = buf; + ptr[0] |= 0x40; + crc = __make_crc7(buf,len); + + if(_ioWPFlag) { + for(cnt=0;cnt=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + ret = CARDIO_ERROR_READY; + ptr = buf; + *ptr = _ioClrFlag; + + __exi_wait(drv_no); + + if(EXI_Select(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) { + EXI_Unlock(drv_no); + return CARDIO_ERROR_NOCARD; + } + + if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + + startT = gettick(); + while(*ptr&0x80) { + *ptr = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + if(!(*ptr&0x80)) break; + if(__card_checktimeout(drv_no,startT,500)!=0) { + *ptr = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + if(*ptr&0x80) ret = CARDIO_ERROR_IOTIMEOUT; + break; + } + } + if(len>1 && ret==CARDIO_ERROR_READY) { + *(++ptr) = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,len-1,EXI_READWRITE)==0) ret = CARDIO_ERROR_IOERROR; + } + + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return ret; +} + +static s32 __card_stopreadresponse(s32 drv_no,void *buf,s32 len) +{ + u8 *ptr,tmp; + s32 startT,ret; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + ptr = buf; + + __exi_wait(drv_no); + + if(EXI_Select(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) { + EXI_Unlock(drv_no); + return CARDIO_ERROR_NOCARD; + } + + ret = CARDIO_ERROR_READY; + *ptr = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + + *ptr = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + + startT = gettick(); + while(*ptr&0x80) { + *ptr = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + if(!(*ptr&0x80)) break; + if(__card_checktimeout(drv_no,startT,1500)!=0) { + *ptr = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + if(*ptr&0x80) ret = CARDIO_ERROR_IOTIMEOUT; + break; + } + } + + tmp = *ptr; + while(*ptr!=0xff) { + *ptr = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + if(*ptr==0xff) break; + if(__card_checktimeout(drv_no,startT,1500)!=0) { + *ptr = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + if(*ptr!=0xff) ret = CARDIO_ERROR_IOTIMEOUT; + break; + } + } + *ptr = tmp; + + if(len>1 && ret==CARDIO_ERROR_READY) { + *(++ptr) = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,len-1,EXI_READWRITE)==0) ret = CARDIO_ERROR_IOERROR; + } + + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return ret; +} + +static s32 __card_datares(s32 drv_no,void *buf) +{ + u8 *ptr; + s32 startT,ret; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + ptr = buf; + + __exi_wait(drv_no); + + if(EXI_Select(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) { + EXI_Unlock(drv_no); + return CARDIO_ERROR_NOCARD; + } + + ret = CARDIO_ERROR_READY; + *ptr = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + startT = gettick(); + while(*ptr&0x10) { + *ptr = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + if(!(*ptr&0x10)) break; + if(__card_checktimeout(drv_no,startT,1500)!=0) { + *ptr = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + if(*ptr&0x10) ret = CARDIO_ERROR_IOTIMEOUT; + break; + } + } + + *(++ptr) = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + + startT = gettick(); + while(!*ptr) { + *ptr = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + if(*ptr) break; + if(__card_checktimeout(drv_no,startT,1500)!=0) { + *ptr = _ioClrFlag; + if(EXI_ImmEx(drv_no,ptr,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + if(!*ptr) ret = CARDIO_ERROR_IOTIMEOUT; + break; + } + } + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + + return ret; +} + +static s32 __card_stopresponse(s32 drv_no) +{ + s32 ret; + + if((ret=__card_stopreadresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret; + ret = __check_response(drv_no,_ioResponse[drv_no][0]); + + return ret; +} + +static s32 __card_dataresponse(s32 drv_no) +{ + s32 ret; + u8 res; + + if((ret=__card_datares(drv_no,_ioResponse[drv_no]))!=0) return ret; + res = _SHIFTR(_ioResponse[drv_no][0],1,3); + if(res==0x0005) ret = CARDIO_OP_IOERR_CRC; + else if(res==0x0006) ret = CARDIO_OP_IOERR_WRITE; + + return ret; +} + +static s32 __card_dataread(s32 drv_no,void *buf,u32 len) +{ + u8 *ptr; + u32 cnt; + u8 res[2]; + u16 crc,crc_org; + s32 startT,ret; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + __exi_wait(drv_no); + + if(EXI_Select(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) { + EXI_Unlock(drv_no); + return CARDIO_ERROR_NOCARD; + } + + ret = CARDIO_ERROR_READY; + ptr = buf; + for(cnt=0;cnt=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + for(cnt=0;cnt<32;cnt++) dummy[cnt] = _ioClrFlag; + crc = __make_crc16(buf,len); + + __exi_wait(drv_no); + + if(EXI_Select(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) { + EXI_Unlock(drv_no); + return CARDIO_ERROR_NOCARD; + } + + dummy[0] = 0xfc; + if(EXI_ImmEx(drv_no,dummy,1,EXI_WRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + + if(EXI_ImmEx(drv_no,buf,len,EXI_WRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + + /* sleep 1us*/ + usleep(1); + + ret = CARDIO_ERROR_READY; + if(EXI_ImmEx(drv_no,&crc,2,EXI_WRITE)==0) ret = CARDIO_ERROR_IOERROR; + + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + + return ret; +} + +static s32 __card_multiwritestop(s32 drv_no) +{ + s32 ret,cnt,startT; + u8 dummy[32]; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + for(cnt=0;cnt<32;cnt++) dummy[cnt] = _ioClrFlag; + + __exi_wait(drv_no); + + if(EXI_Select(drv_no,EXI_DEVICE_0,_ioCardFreq)==0) { + EXI_Unlock(drv_no); + return CARDIO_ERROR_NOCARD; + } + + ret = CARDIO_ERROR_READY; + dummy[0] = 0xfd; + if(_ioWPFlag) dummy[0] = 0x02; //!0xfd + if(EXI_ImmEx(drv_no,dummy,1,EXI_WRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + + dummy[0] = _ioClrFlag; + if(EXI_ImmEx(drv_no,dummy,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + + dummy[0] = _ioClrFlag; + if(EXI_ImmEx(drv_no,dummy,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + + dummy[0] = _ioClrFlag; + if(EXI_ImmEx(drv_no,dummy,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + + dummy[0] = _ioClrFlag; + if(EXI_ImmEx(drv_no,dummy,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + + startT = gettick(); + ret = CARDIO_ERROR_READY; + while(dummy[0]==0) { + dummy[0] = _ioClrFlag; + if(EXI_ImmEx(drv_no,dummy,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + if(dummy[0]) break; + if(__card_checktimeout(drv_no,startT,1500)!=0) { + dummy[0] = _ioClrFlag; + if(EXI_ImmEx(drv_no,dummy,1,EXI_READWRITE)==0) { + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return CARDIO_ERROR_IOERROR; + } + if(!dummy[0]) ret = CARDIO_ERROR_IOTIMEOUT; + break; + } + } + + EXI_Deselect(drv_no); + EXI_Unlock(drv_no); + return ret; +} + +static s32 __card_response1(s32 drv_no) +{ + s32 ret; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret; + return __check_response(drv_no,_ioResponse[drv_no][0]); +} + +static s32 __card_response2(s32 drv_no) +{ + u32 ret; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],2))!=0) return ret; + if(!(_ioResponse[drv_no][0]&0x7c) && !(_ioResponse[drv_no][1]&0x9e)) return CARDIO_ERROR_READY; + return CARDIO_ERROR_FATALERROR; +} + +static s32 __card_sendappcmd(s32 drv_no) +{ + s32 ret; + u8 ccmd[5] = {0,0,0,0,0}; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + ccmd[0] = 0x37; + if((ret=__card_writecmd(drv_no,ccmd,5))!=0) { + return ret; + } + if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret; + ret = __check_response(drv_no,_ioResponse[drv_no][0]); + + return ret; +} + +static s32 __card_sendopcond(s32 drv_no) +{ + u8 ccmd[5] = {0,0,0,0,0}; + s32 ret; + s32 startT; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + ret = 0; + startT = gettick(); + do { + if(_initType[drv_no]==TYPE_SDHC) { + __card_sendappcmd(drv_no); + ccmd[0] = 0x29; + ccmd[1] = 0x40; + } else + ccmd[0] = 0x01; + + if((ret=__card_writecmd(drv_no,ccmd,5))!=0) { + return ret; + } + if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret; + if((ret=__check_response(drv_no,_ioResponse[drv_no][0]))!=0) return ret; + if(!(_ioError[drv_no]&CARDIO_OP_IOERR_IDLE)) return CARDIO_ERROR_READY; + + ret = __card_checktimeout(drv_no,startT,1500); + } while(ret==0); + + if(_initType[drv_no]==TYPE_SDHC) { + __card_sendappcmd(drv_no); + ccmd[0] = 0x29; + ccmd[1] = 0x40; + } else + ccmd[0] = 0x01; + + if((ret=__card_writecmd(drv_no,ccmd,5))!=0) { + return ret; + } + if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret; + if((ret=__check_response(drv_no,_ioResponse[drv_no][0]))!=0) return ret; + if(_ioError[drv_no]&CARDIO_OP_IOERR_IDLE) return CARDIO_ERROR_IOERROR; + + return CARDIO_ERROR_READY; +} + +static s32 __card_sendCMD8(s32 drv_no) +{ + s32 ret; + u8 ccmd[5] = {0,0,0,0,0}; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + ccmd[0] = 0x08; + ccmd[3] = 0x01; + ccmd[4] = 0xAA; + if((ret=__card_writecmd(drv_no,ccmd,5))!=0){ + return ret; + } + if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],5))!=0) return ret; + ret = __check_response(drv_no,_ioResponse[drv_no][0]); + + return ret; +} + +static s32 __card_sendCMD58(s32 drv_no) +{ + s32 ret; + u8 ccmd[5] = {0,0,0,0,0}; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + ccmd[0]= 0x3A; + if((ret=__card_writecmd(drv_no,ccmd,5))!=0) { + return ret; + } + if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],5))!=0) return ret; + ret = __check_response(drv_no,_ioResponse[drv_no][0]); + + return ret; +} + +static s32 __card_sendcmd(s32 drv_no,u8 cmd,u8 *arg) +{ + u8 ccmd[5] = {0,0,0,0,0}; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + ccmd[0] = cmd; + if(arg) { + ccmd[1] = arg[0]; + ccmd[2] = arg[1]; + ccmd[3] = arg[2]; + ccmd[4] = arg[3]; + } + return __card_writecmd(drv_no,ccmd,5); +} + +static s32 __card_setblocklen(s32 drv_no,u32 block_len) +{ + u8 cmd[5]; + s32 ret; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + if(block_len>PAGE_SIZE512) block_len = PAGE_SIZE512; + + cmd[0] = 0x10; + cmd[1] = (block_len>>24)&0xff; + cmd[2] = (block_len>>16)&0xff; + cmd[3] = (block_len>>8)&0xff; + cmd[4] = block_len&0xff; + if((ret=__card_writecmd(drv_no,cmd,5))!=0) { + return ret; + } + if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))<0) return ret; + ret = __check_response(drv_no,_ioResponse[drv_no][0]); + + return ret; +} + +static s32 __card_readcsd(s32 drv_no) +{ + u8 ccmd[5] = {0,0,0,0,0}; + s32 ret; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + ret = 0; + ccmd[0] = 0x09; + if((ret=__card_writecmd(drv_no,ccmd,5))!=0) { + return ret; + } + if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret; + ret = __check_response(drv_no,_ioResponse[drv_no][0]); + if(ret==0) { + if((ret=__card_dataread(drv_no,g_CSD[drv_no],16))!=0) return ret; + } + return ret; +} + +static s32 __card_readcid(s32 drv_no) +{ + u8 ccmd[5] = {0,0,0,0,0}; + s32 ret; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + ret = 0; + ccmd[0] = 0x0A; + if((ret=__card_writecmd(drv_no,ccmd,5))!=0) { + return ret; + } + if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret; + ret = __check_response(drv_no,_ioResponse[drv_no][0]); + if(ret==0) { + if((ret=__card_dataread(drv_no,g_CID[drv_no],16))!=0) return ret; + } + return ret; +} + +static s32 __card_sd_status(s32 drv_no) +{ + s32 ret; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + if(_ioPageSize[drv_no]!=64) { + _ioPageSize[drv_no] = 64; + if((ret=__card_setblocklen(drv_no,_ioPageSize[drv_no]))!=0) return ret; + } + if((ret=__card_sendappcmd(drv_no))!=0) return ret; + if((ret=__card_sendcmd(drv_no,0x0d,NULL))!=0) return ret; + if((ret=__card_response2(drv_no))!=0) return ret; + ret = __card_dataread(drv_no,g_CardStatus[drv_no],64); + + return ret; +} + +static s32 __card_softreset(s32 drv_no) +{ + s32 ret; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + ret = 0; + if((ret=__card_writecmd0(drv_no))!=0) { + return ret; + } + + if((ret=__card_readresponse(drv_no,_ioResponse[drv_no],1))!=0) return ret; + return __check_response(drv_no,_ioResponse[drv_no][0]); +} + +static bool __card_check(s32 drv_no) +{ + s32 ret; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return FALSE; + while((ret=EXI_ProbeEx(drv_no))==0); + if(ret!=1) return FALSE; + + if(!(EXI_GetState(drv_no)&EXI_FLAG_ATTACH)) { + if(EXI_Attach(drv_no,__card_exthandler)==0) return FALSE; + sdgecko_insertedCB(drv_no); + } + return TRUE; +} + +static s32 __card_retrycb(s32 drv_no) +{ + _ioRetryCB = NULL; + _ioRetryCnt++; + return sdgecko_initIO(drv_no); +} + +static void __convert_sector(s32 drv_no,u32 sector_no,u8 *arg) +{ + if(_ioAddressingType[drv_no]==BYTE_ADDRESSING) { + arg[0] = (sector_no>>15)&0xff; + arg[1] = (sector_no>>7)&0xff; + arg[2] = (sector_no<<1)&0xff; + arg[3] = (sector_no<<9)&0xff; + } else if(_ioAddressingType[drv_no]==SECTOR_ADDRESSING) { + arg[0] = (sector_no>>24)&0xff; + arg[1] = (sector_no>>16)&0xff; + arg[2] = (sector_no>>8)&0xff; + arg[3] = sector_no&0xff; + } +} + +void sdgecko_initIODefault() +{ + u32 i; + __init_crc7(); + __init_crc16(); + for(i=0;i=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + u32 id = 0; + EXI_GetID(drv_no,EXI_DEVICE_0,&id); + if ( id != -1 ) return CARDIO_ERROR_NOCARD; + + if(_ioRetryCnt>5) { + _ioRetryCnt = 0; + return CARDIO_ERROR_IOERROR; + } + + _ioCardInserted[drv_no] = __card_check(drv_no); + + if(_ioCardInserted[drv_no]==TRUE) { + _ioWPFlag = 0; + _ioCardFreq = EXI_SPEED16MHZ; + _initType[drv_no] = TYPE_SD; + _ioFlag[drv_no] = INITIALIZING; + _ioAddressingType[drv_no] = BYTE_ADDRESSING; + if(__card_softreset(drv_no)!=0) { + _ioWPFlag = 1; + if(__card_softreset(drv_no)!=0) goto exit; + } + + if(__card_sendCMD8(drv_no)!=0) goto exit; + if((_ioResponse[drv_no][3]==1) && (_ioResponse[drv_no][4]==0xAA)) _initType[drv_no] = TYPE_SDHC; + + if(__card_sendopcond(drv_no)!=0) goto exit; + if(__card_readcsd(drv_no)!=0) goto exit; + if(__card_readcid(drv_no)!=0) goto exit; + + if(_initType[drv_no]==TYPE_SDHC) { + if(__card_sendCMD58(drv_no)!=0) goto exit; + if(_ioResponse[drv_no][1]&0x40) _ioAddressingType[drv_no] = SECTOR_ADDRESSING; + } + + _ioPageSize[drv_no] = 1<=EXI_CHANNEL_2) return CARDIO_ERROR_NOCARD; + ret = sdgecko_preIO(drv_no); + if(ret!=0) return ret; + + return __card_readcid(drv_no); +} + +s32 sdgecko_readCSD(s32 drv_no) +{ + s32 ret; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + ret = sdgecko_preIO(drv_no); + if(ret!=0) return ret; + + return __card_readcsd(drv_no); +} + +s32 sdgecko_readStatus(s32 drv_no) +{ + s32 ret; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + ret = sdgecko_preIO(drv_no); + if(ret!=0) return ret; + + return __card_sd_status(drv_no); +} + +// Multiple sector read by emu_kidid +s32 sdgecko_readSectors(s32 drv_no,u32 sector_no,u32 num_sectors,void *buf) +{ + u32 i; + s32 ret; + u8 arg[4]; + char *ptr = (char*)buf; + + if(drv_no<0 || drv_no>=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + ret = sdgecko_preIO(drv_no); + if(ret!=0) return ret; + + if(num_sectors<1) return CARDIO_ERROR_INTERNAL; + + + // Must be 512b, otherwise fail! + if(PAGE_SIZE512!=_ioPageSize[drv_no]) { + _ioPageSize[drv_no] = PAGE_SIZE512; + if((ret=__card_setblocklen(drv_no,PAGE_SIZE512))!=0) return ret; + } + + // SDHC support fix + __convert_sector(drv_no,sector_no,arg); + + if((ret=__card_sendcmd(drv_no,0x12,arg))!=0) return ret; + if((ret=__card_response1(drv_no))!=0) return ret; + + for(i=0;i=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + ret = sdgecko_preIO(drv_no); + if(ret!=0) return ret; + + if(num_sectors<1) return CARDIO_ERROR_INTERNAL; + + + if(PAGE_SIZE512!=_ioPageSize[drv_no]) { + _ioPageSize[drv_no] = PAGE_SIZE512; + if((ret=__card_setblocklen(drv_no,_ioPageSize[drv_no]))!=0) return ret; + } + + // send SET_WRITE_BLK_ERASE_CNT cmd + arg[0] = (num_sectors>>24)&0xff; + arg[1] = (num_sectors>>16)&0xff; + arg[2] = (num_sectors>>8)&0xff; + arg[3] = num_sectors&0xff; + if((ret=__card_sendappcmd(drv_no))!=0) return ret; + if((ret=__card_sendcmd(drv_no,0x17,arg))!=0) return ret; + if((ret=__card_response1(drv_no))!=0) return ret; + + // SDHC support fix + __convert_sector(drv_no,sector_no,arg); + + if((ret=__card_sendcmd(drv_no,0x19,arg))!=0) return ret; + if((ret=__card_response1(drv_no))!=0) return ret; + + for(i=0;i=MAX_DRIVE) return CARDIO_ERROR_NOCARD; + + if(__card_check(drv_no)==TRUE && _ioFlag[drv_no]!=NOT_INITIALIZED) { + if((ret=__card_sendappcmd(drv_no))!=0) goto exit; + if((ret=__card_sendcmd(drv_no,0x2a,NULL))!=0) goto exit; + ret = __card_response1(drv_no); + } + _ioFlag[drv_no] = NOT_INITIALIZED; + +exit: + if(_ioCardInserted[drv_no]==TRUE) { + _ioCardInserted[drv_no] = FALSE; + EXI_Detach(drv_no); + } + if(_ioRetryCB) + return _ioRetryCB(drv_no); + + return CARDIO_ERROR_READY; +} + +static void (*pfCallbackIN[MAX_DRIVE])(s32) = {NULL, NULL}; +static void (*pfCallbackOUT[MAX_DRIVE])(s32) = {NULL, NULL}; + +void sdgecko_insertedCB(s32 drv_no) +{ + if(pfCallbackIN[drv_no]) + pfCallbackIN[drv_no](drv_no); +} + +void sdgecko_ejectedCB(s32 drv_no) +{ + if(pfCallbackOUT[drv_no]) + pfCallbackOUT[drv_no](drv_no); +} + +void sdgecko_setSpeed(u32 freq) +{ + _ioCardFreq = freq; +} + +u32 sdgecko_getPageSize(s32 drv_no) +{ + return _ioPageSize[drv_no]; +} + +u32 sdgecko_setPageSize(s32 drv_no, int size) +{ + if(_ioPageSize[drv_no]!=size) + _ioPageSize[drv_no] = size; + + return __card_setblocklen(drv_no, _ioPageSize[drv_no]); +} + +u32 sdgecko_getAddressingType(s32 drv_no) +{ + return _ioAddressingType[drv_no]; +} diff --git a/wii/libogc/libogc/semaphore.c b/wii/libogc/libogc/semaphore.c new file mode 100644 index 0000000000..4164d62a0e --- /dev/null +++ b/wii/libogc/libogc/semaphore.c @@ -0,0 +1,150 @@ +/*------------------------------------------------------------- + +semaphore.c -- Thread subsystem IV + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + + +-------------------------------------------------------------*/ + + +#include +#include +#include +#include "lwp_sema.h" +#include "lwp_objmgr.h" +#include "lwp_config.h" +#include "semaphore.h" + +#define LWP_OBJTYPE_SEM 4 + +#define LWP_CHECK_SEM(hndl) \ +{ \ + if(((hndl)==LWP_SEM_NULL) || (LWP_OBJTYPE(hndl)!=LWP_OBJTYPE_SEM)) \ + return NULL; \ +} + + +typedef struct _sema_st +{ + lwp_obj object; + lwp_sema sema; +} sema_st; + +lwp_objinfo _lwp_sema_objects; + +void __lwp_sema_init() +{ + __lwp_objmgr_initinfo(&_lwp_sema_objects,LWP_MAX_SEMAS,sizeof(sema_st)); +} + +static __inline__ sema_st* __lwp_sema_open(sem_t sem) +{ + LWP_CHECK_SEM(sem); + return (sema_st*)__lwp_objmgr_get(&_lwp_sema_objects,LWP_OBJMASKID(sem)); +} + +static __inline__ void __lwp_sema_free(sema_st *sema) +{ + __lwp_objmgr_close(&_lwp_sema_objects,&sema->object); + __lwp_objmgr_free(&_lwp_sema_objects,&sema->object); +} + +static sema_st* __lwp_sema_allocate() +{ + sema_st *sema; + + __lwp_thread_dispatchdisable(); + sema = (sema_st*)__lwp_objmgr_allocate(&_lwp_sema_objects); + if(sema) { + __lwp_objmgr_open(&_lwp_sema_objects,&sema->object); + return sema; + } + __lwp_thread_dispatchenable(); + return NULL; +} + +s32 LWP_SemInit(sem_t *sem,u32 start,u32 max) +{ + lwp_semattr attr; + sema_st *ret; + + if(!sem) return -1; + + ret = __lwp_sema_allocate(); + if(!ret) return -1; + + attr.max_cnt = max; + attr.mode = LWP_SEMA_MODEFIFO; + __lwp_sema_initialize(&ret->sema,&attr,start); + + *sem = (sem_t)(LWP_OBJMASKTYPE(LWP_OBJTYPE_SEM)|LWP_OBJMASKID(ret->object.id)); + __lwp_thread_dispatchenable(); + return 0; +} + +s32 LWP_SemWait(sem_t sem) +{ + sema_st *lwp_sem = __lwp_sema_open(sem); + if(!lwp_sem) return -1; + + __lwp_sema_seize(&lwp_sem->sema,lwp_sem->object.id,TRUE,LWP_THREADQ_NOTIMEOUT); + __lwp_thread_dispatchenable(); + + switch(_thr_executing->wait.ret_code) { + case LWP_SEMA_SUCCESSFUL: + break; + case LWP_SEMA_UNSATISFIED_NOWAIT: + return EAGAIN; + case LWP_SEMA_DELETED: + return EAGAIN; + case LWP_SEMA_TIMEOUT: + return ETIMEDOUT; + + } + return 0; +} + +s32 LWP_SemPost(sem_t sem) +{ + sema_st *lwp_sem = __lwp_sema_open(sem); + if(!lwp_sem) return -1; + + __lwp_sema_surrender(&lwp_sem->sema,lwp_sem->object.id); + __lwp_thread_dispatchenable(); + + return 0; +} + +s32 LWP_SemDestroy(sem_t sem) +{ + sema_st *lwp_sem = __lwp_sema_open(sem); + if(!lwp_sem) return -1; + + __lwp_sema_flush(&lwp_sem->sema,-1); + __lwp_thread_dispatchenable(); + + __lwp_sema_free(lwp_sem); + return 0; +} diff --git a/wii/libogc/libogc/si.c b/wii/libogc/libogc/si.c new file mode 100644 index 0000000000..5c3bc8c30d --- /dev/null +++ b/wii/libogc/libogc/si.c @@ -0,0 +1,693 @@ +#include +#include +#include +#include +#include "asm.h" +#include "processor.h" +#include "system.h" +#include "ogcsys.h" +#include "video.h" +#include "irq.h" +#include "si.h" +#include "lwp_watchdog.h" + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +#define SISR_ERRORMASK(chn) (0x0f000000>>((chn)<<3)) +#define SIPOLL_ENABLE(chn) (0x80000000>>((chn)+24)) + +#define SICOMCSR_TCINT (1<<31) +#define SICOMCSR_TCINT_ENABLE (1<<30) +#define SICOMCSR_COMERR (1<<29) +#define SICOMCSR_RDSTINT (1<<28) +#define SICOMCSR_RDSTINT_ENABLE (1<<27) +#define SICOMCSR_TSTART (1<<0) + +#define SISR_UNDERRUN 0x0001 +#define SISR_OVERRUN 0x0002 +#define SISR_COLLISION 0x0004 +#define SISR_NORESPONSE 0x0008 +#define SISR_WRST 0x0010 +#define SISR_RDST 0x0020 + +typedef union _sicomcsr { + u32 val; + struct { + u32 tcint : 1; + u32 tcintmsk : 1; + u32 comerr : 1; + u32 rdstint : 1; + u32 rdstintmsk : 1; + u32 pad2 : 4; + u32 outlen : 7; + u32 pad1 : 1; + u32 inlen : 7; + u32 pad0 : 5; + u32 channel : 2; + u32 tstart : 1; + } csrmap; +} sicomcsr; + +static struct _sipacket { + s32 chan; + void *out; + u32 out_bytes; + void *in; + u32 in_bytes; + SICallback callback; + u64 fire; +} sipacket[4]; + +static struct _sicntrl { + s32 chan; + u32 poll; + u32 in_bytes; + void *in; + SICallback callback; +} sicntrl = { + -1, + 0, + 0, + NULL, + NULL +}; + +static struct _xy { + u16 line; + u8 cnt; +} xy[2][12] = { + { + {0x00F6,0x02},{0x000F,0x12},{0x001E,0x09},{0x002C,0x06}, + {0x0034,0x05},{0x0041,0x04},{0x0057,0x03},{0x0057,0x03}, + {0x0057,0x03},{0x0083,0x02},{0x0083,0x02},{0x0083,0x02} + }, + + { + {0x0128,0x02},{0x000F,0x15},{0x001D,0x0B},{0x002D,0x07}, + {0x0034,0x06},{0x003F,0x05},{0x004E,0x04},{0x0068,0x03}, + {0x0068,0x03},{0x0068,0x03},{0x0068,0x03},{0x009C,0x02} + } +}; + +u32 __PADFixBits = 0; + +static u32 sampling_rate = 0; +static u32 cmdtypeandstatus$47 = 0; +static u32 cmdtypeandstatus$223 = 0; +static u32 cmdfixdevice[4] = {0,0,0,0}; +static u32 si_type[4] = {8,8,8,8}; +static u32 inputBufferVCount[4] = {0,0,0,0}; +static u32 inputBufferValid[4] = {0,0,0,0}; +static u32 inputBuffer[4][2] = {{0,0},{0,0},{0,0},{0,0}}; +static RDSTHandler rdstHandlers[4] = {NULL,NULL,NULL,NULL}; +static u64 typeTime[4] = {0,0,0,0}; +static u64 xferTime[4] = {0,0,0,0}; +static SICallback typeCallback[4][4] = {{NULL,NULL,NULL,NULL}, + {NULL,NULL,NULL,NULL}, + {NULL,NULL,NULL,NULL}, + {NULL,NULL,NULL,NULL}}; +static syswd_t si_alarm[4]; + +#if defined(HW_DOL) + static vu32* const _siReg = (u32*)0xCC006400; +#elif defined(HW_RVL) + static vu32* const _siReg = (u32*)0xCD006400; +#else + #error HW model unknown. +#endif + +static vu16* const _viReg = (u16*)0xCC002000; + +static u32 __si_transfer(s32 chan,void *out,u32 out_len,void *in,u32 in_len,SICallback cb); + +static __inline__ struct _xy* __si_getxy() +{ + switch(VIDEO_GetCurrentTvMode()) { + case VI_NTSC: + case VI_MPAL: + case VI_EURGB60: + return xy[0]; + break; + case VI_PAL: + return xy[1]; + break; + } + return NULL; +} + +static __inline__ void __si_cleartcinterrupt() +{ + _siReg[13] = (_siReg[13]|SICOMCSR_TCINT)&SICOMCSR_TCINT; +} + +static void __si_alarmhandler(syswd_t thealarm,void *cbarg) +{ + u32 chn = 0; + + while(chn<4) + { + if(si_alarm[chn]==thealarm) + break; + chn++; + } + if(chn==4) + return; + + if(sipacket[chn].chan!=-1) { + if(__si_transfer(sipacket[chn].chan,sipacket[chn].out,sipacket[chn].out_bytes,sipacket[chn].in,sipacket[chn].in_bytes,sipacket[chn].callback)) sipacket[chn].chan = -1; + } +} + +static u32 __si_completetransfer() +{ + u32 val,cnt,i; + u32 *in; + u32 sisr = _siReg[14]; + + __si_cleartcinterrupt(); + + if(sicntrl.chan==-1) return sisr; + + xferTime[sicntrl.chan] = gettime(); + + in = (u32*)sicntrl.in; + cnt = (sicntrl.in_bytes/4); + for(i=0;i>((3-i)*8))&0xff; + } + if(_siReg[13]&SICOMCSR_COMERR) { + sisr = (sisr>>((3-sicntrl.chan)*8))&0x0f; + if(sisr&SISR_NORESPONSE && !(si_type[sicntrl.chan]&SI_ERR_BUSY)) si_type[sicntrl.chan] = SI_ERROR_NO_RESPONSE; + if(!sisr) sisr = SISR_COLLISION; + } else { + typeTime[sicntrl.chan] = gettime(); + sisr = 0; + } + + sicntrl.chan = -1; + return sisr; +} + +static u32 __si_transfer(s32 chan,void *out,u32 out_len,void *in,u32 in_len,SICallback cb) +{ + u32 level,cnt,i; + sicomcsr csr; + _CPU_ISR_Disable(level); + if(sicntrl.chan!=-1) { + _CPU_ISR_Restore(level); + return 0; + } + _siReg[14] &= SISR_ERRORMASK(chan); + + sicntrl.chan = chan; + sicntrl.callback = cb; + sicntrl.in_bytes = in_len; + sicntrl.in = in; + cnt = ((out_len+3)/4); + for(i=0;i=0) { + if(!__si_transfer(sipacket[chan].chan,sipacket[chan].out,sipacket[chan].out_bytes,sipacket[chan].in,sipacket[chan].in_bytes,sipacket[chan].callback)) break; + SYS_CancelAlarm(si_alarm[chan]); + sipacket[chan].chan = -1; + } + } + cnt++; + } +} + +static void __si_interrupthandler(u32 irq,void *ctx) +{ + SICallback cb; + u32 chn,curr_line,line,ret; + sicomcsr csr; + + csr.val = _siReg[13]; + if(csr.csrmap.tcintmsk && csr.csrmap.tcint) { + chn = sicntrl.chan; + cb = sicntrl.callback; + sicntrl.callback = NULL; + + ret = __si_completetransfer(); + __si_transfernext(chn); + + if(cb) cb(chn,ret); + + _siReg[14] &= SISR_ERRORMASK(chn); + + if(si_type[chn]==SI_ERR_BUSY && !SI_IsChanBusy(chn)) SI_Transfer(chn,&cmdtypeandstatus$47,1,&si_type[chn],3,__si_gettypecallback,65); + } + + if(csr.csrmap.rdstintmsk && csr.csrmap.rdstint) { + curr_line = VIDEO_GetCurrentLine(); + curr_line++; + line = _SHIFTR(sicntrl.poll,16,10); + + chn = 0; + while(chn<4) { + if(SI_GetResponseRaw(chn)) inputBufferVCount[chn] = curr_line; + chn++; + } + + chn = 0; + while(chn<4) { + if(sicntrl.poll&SIPOLL_ENABLE(chn)) { + if(!inputBufferVCount[chn] || ((line>>1)+inputBufferVCount[chn])>= 24; + mask = (poll>>4)&0x0f; + sicntrl.poll &= ~mask; + + poll &= (0x03fffff0|mask); + + sicntrl.poll |= (poll&~0x03ffff00); + SI_TransferCommands(); + _siReg[12] = sicntrl.poll; + _CPU_ISR_Restore(level); +} + +void SI_DisablePolling(u32 poll) +{ + u32 level,mask; + _CPU_ISR_Disable(level); + mask = (poll>>24)&0xf0; + sicntrl.poll &= ~mask; + _siReg[12] = sicntrl.poll; + _CPU_ISR_Restore(level); +} + +void SI_SetSamplingRate(u32 samplingrate) +{ + u32 div,level; + struct _xy *xy = NULL; + + if(samplingrate>11) samplingrate = 11; + + _CPU_ISR_Disable(level); + sampling_rate = samplingrate; + xy = __si_getxy(); + + div = 1; + if(_viReg[54]&0x0001) div = 2; + + SI_SetXY(div*xy[samplingrate].line,xy[samplingrate].cnt); + _CPU_ISR_Restore(level); +} + +void SI_RefreshSamplingRate() +{ + SI_SetSamplingRate(sampling_rate); +} + +u32 SI_GetStatus(s32 chan) +{ + u32 level,sisr; + + _CPU_ISR_Disable(level); + sisr = (_siReg[14]>>((3-chan)<<3)); + if(sisr&SISR_NORESPONSE && !(si_type[chan]&SI_ERR_BUSY)) si_type[chan] = SI_ERROR_NO_RESPONSE; + _CPU_ISR_Restore(level); + return sisr; +} + +u32 SI_GetResponseRaw(s32 chan) +{ + u32 status,ret; + ret = 0; + status = SI_GetStatus(chan); + if(status&SISR_RDST) { + inputBuffer[chan][0] = _siReg[(chan*3)+1]; + inputBuffer[chan][1] = _siReg[(chan*3)+2]; + inputBufferValid[chan] = 1; + ret = 1; + } + return ret; +} + +u32 SI_GetResponse(s32 chan,void *buf) +{ + u32 level,valid; + _CPU_ISR_Disable(level); + SI_GetResponseRaw(chan); + valid = inputBufferValid[chan]; + inputBufferValid[chan] = 0; + if(valid) { + ((u32*)buf)[0] = inputBuffer[chan][0]; + ((u32*)buf)[1] = inputBuffer[chan][1]; + } + _CPU_ISR_Restore(level); + return valid; +} + +void SI_SetCommand(s32 chan,u32 cmd) +{ + _siReg[chan*3] = cmd; +} + +u32 SI_GetCommand(s32 chan) +{ + return (_siReg[chan*3]); +} + +u32 SI_Transfer(s32 chan,void *out,u32 out_len,void *in,u32 in_len,SICallback cb,u32 us_delay) +{ + u32 ret = 0; + u32 level; + s64 diff; + u64 now,fire; + struct timespec tb; + _CPU_ISR_Disable(level); + if(sipacket[chan].chan==-1 && sicntrl.chan!=chan) { + ret = 1; + fire = now = gettime(); + if(us_delay) fire = xferTime[chan]+microsecs_to_ticks(us_delay); + diff = (now - fire); + if(diff<0) { + tb.tv_sec = 0; + tb.tv_nsec = ticks_to_nanosecs((fire - now)); + SYS_SetAlarm(si_alarm[chan],&tb,__si_alarmhandler,NULL); + } else if(__si_transfer(chan,out,out_len,in,in_len,cb)) { + _CPU_ISR_Restore(level); + return ret; + } + sipacket[chan].chan = chan; + sipacket[chan].out = out; + sipacket[chan].out_bytes = out_len; + sipacket[chan].in = in; + sipacket[chan].in_bytes = in_len; + sipacket[chan].callback = cb; + sipacket[chan].fire = fire; + } + _CPU_ISR_Restore(level); + return ret; +} + +u32 SI_GetType(s32 chan) +{ + u32 level,type; + u64 now; + s64 diff; + _CPU_ISR_Disable(level); + now = gettime(); + type = si_type[chan]; + diff = (now - typeTime[chan]); + if(sicntrl.poll&(0x80>>chan)) { + if(type!=SI_ERROR_NO_RESPONSE) { + typeTime[chan] = gettime(); + _CPU_ISR_Restore(level); + return type; + } + si_type[chan] = type = SI_ERR_BUSY; + } else if(diff==millisecs_to_ticks(50) && type!=SI_ERROR_NO_RESPONSE) { + _CPU_ISR_Restore(level); + return type; + } else if(diff==millisecs_to_ticks(75)) si_type[chan] = SI_ERR_BUSY; + else si_type[chan] = type = SI_ERR_BUSY; + + typeTime[chan] = gettime(); + + SI_Transfer(chan,&cmdtypeandstatus$223,1,&si_type[chan],3,__si_gettypecallback,65); + _CPU_ISR_Restore(level); + + return type; +} + +u32 SI_GetTypeAsync(s32 chan,SICallback cb) +{ + u32 level; + u32 type,i; + _CPU_ISR_Disable(level); + type = SI_GetType(chan); + if(si_type[chan]&SI_ERR_BUSY) { + i=0; + for(i=0;i<4;i++) { + if(!typeCallback[chan][i] && typeCallback[chan][i]!=cb) { + typeCallback[chan][i] = cb; + break; + } + } + _CPU_ISR_Restore(level); + return type; + } + + cb(chan,type); + _CPU_ISR_Restore(level); + return type; +} + +void SI_TransferCommands() +{ + _siReg[14] = 0x80000000; +} + +u32 SI_RegisterPollingHandler(RDSTHandler handler) +{ + u32 level,i; + + _CPU_ISR_Disable(level); + + i = 0; + for(i=0;i<4;i++) { + if(rdstHandlers[i]==handler) { + _CPU_ISR_Restore(level); + return 1; + } + } + + for(i=0;i<4;i++) { + if(rdstHandlers[i]==NULL) { + rdstHandlers[i] = handler; + SI_EnablePollingInterrupt(TRUE); + _CPU_ISR_Restore(level); + return 1; + } + } + + _CPU_ISR_Restore(level); + return 0; +} + +u32 SI_UnregisterPollingHandler(RDSTHandler handler) +{ + u32 level,i; + + _CPU_ISR_Disable(level); + for(i=0;i<4;i++) { + if(rdstHandlers[i]==handler) { + rdstHandlers[i] = NULL; + for(i=0;i<4;i++) { + if(rdstHandlers[i]!=NULL) break; + } + if(i>=4) SI_EnablePollingInterrupt(FALSE); + + _CPU_ISR_Restore(level); + return 1; + } + } + _CPU_ISR_Restore(level); + return 0; +} + +u32 SI_EnablePollingInterrupt(s32 enable) +{ + sicomcsr csr; + u32 level,ret,i; + + _CPU_ISR_Disable(level); + + ret = 0; + csr.val = _siReg[13]; + if(csr.csrmap.rdstintmsk) ret = 1; + + if(enable) { + csr.csrmap.rdstintmsk = 1; + for(i=0;i<4;i++) inputBufferVCount[i] = 0; + } else + csr.csrmap.rdstintmsk = 0; + + csr.val &= 0x7ffffffe; + _siReg[13] = csr.val; + + _CPU_ISR_Restore(level); + return ret; +} + +void __si_init() +{ + u32 i; + for(i=0;i<4;i++) { + sipacket[i].chan = -1; + SYS_CreateAlarm(&si_alarm[i]); + } + sicntrl.poll = 0; + + SI_SetSamplingRate(0); + while(_siReg[13]&0x0001); + _siReg[13] = 0x80000000; + + _siReg[15] &= ~0x80000000; // permit exi clock to be set to 32MHz + + IRQ_Request(IRQ_PI_SI,__si_interrupthandler,NULL); + __UnmaskIrq(IRQMASK(IRQ_PI_SI)); + + SI_GetType(0); + SI_GetType(1); + SI_GetType(2); + SI_GetType(3); +} diff --git a/wii/libogc/libogc/stm.c b/wii/libogc/libogc/stm.c new file mode 100644 index 0000000000..396e15509e --- /dev/null +++ b/wii/libogc/libogc/stm.c @@ -0,0 +1,243 @@ +/*------------------------------------------------------------- + +stm.c - System and miscellaneous hardware control functions + +Copyright (C) 2008 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) +Hector Martin (marcan) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#if defined(HW_RVL) + +#include +#include "ipc.h" +#include "system.h" +#include "asm.h" +#include "processor.h" +#include "stm.h" + +#define IOCTL_STM_EVENTHOOK 0x1000 +#define IOCTL_STM_GET_IDLEMODE 0x3001 +#define IOCTL_STM_RELEASE_EH 0x3002 +#define IOCTL_STM_HOTRESET 0x2001 +#define IOCTL_STM_HOTRESET_FOR_PD 0x2002 +#define IOCTL_STM_SHUTDOWN 0x2003 +#define IOCTL_STM_IDLE 0x2004 +#define IOCTL_STM_WAKEUP 0x2005 +#define IOCTL_STM_VIDIMMING 0x5001 +#define IOCTL_STM_LEDFLASH 0x6001 +#define IOCTL_STM_LEDMODE 0x6002 +#define IOCTL_STM_READVER 0x7001 +#define IOCTL_STM_READDDRREG 0x4001 +#define IOCTL_STM_READDDRREG2 0x4002 + +static s32 __stm_eh_fd = -1; +static s32 __stm_imm_fd = -1; +static u32 __stm_vdinuse = 0; +static u32 __stm_initialized= 0; +static u32 __stm_ehregistered= 0; +static u32 __stm_ehclear= 0; + +static u32 __stm_ehbufin[0x08] ATTRIBUTE_ALIGN(32) = {0,0,0,0,0,0,0,0}; +static u32 __stm_ehbufout[0x08] ATTRIBUTE_ALIGN(32) = {0,0,0,0,0,0,0,0}; +static u32 __stm_immbufin[0x08] ATTRIBUTE_ALIGN(32) = {0,0,0,0,0,0,0,0}; +static u32 __stm_immbufout[0x08] ATTRIBUTE_ALIGN(32) = {0,0,0,0,0,0,0,0}; + +static char __stm_eh_fs[] ATTRIBUTE_ALIGN(32) = "/dev/stm/eventhook"; +static char __stm_imm_fs[] ATTRIBUTE_ALIGN(32) = "/dev/stm/immediate"; + +s32 __STM_SetEventHook(); +s32 __STM_ReleaseEventHook(); +static s32 __STMEventHandler(s32 result,void *usrdata); + +stmcallback __stm_eventcb = NULL; + +static vu16* const _viReg = (u16*)0xCC002000; + +s32 __STM_Init() +{ + if(__stm_initialized==1) return 1; + + __stm_vdinuse = 0; + __stm_imm_fd = IOS_Open(__stm_imm_fs,0); + if(__stm_imm_fd<0) return 0; + + __stm_eh_fd = IOS_Open(__stm_eh_fs,0); + if(__stm_eh_fd<0) return 0; + + __stm_initialized = 1; + __STM_SetEventHook(); + return 1; +} + +s32 __STM_Close() +{ + s32 res; + s32 ret = 0; + __STM_ReleaseEventHook(); + + if(__stm_imm_fd >= 0) { + res = IOS_Close(__stm_imm_fd); + if(res < 0) ret = res; + __stm_imm_fd = -1; + } + if(__stm_eh_fd >= 0) { + res = IOS_Close(__stm_eh_fd); + if(res < 0) ret = res; + __stm_eh_fd = -1; + } + __stm_initialized = 0; + return ret; +} + +s32 __STM_SetEventHook() +{ + s32 ret; + u32 level; + + if(__stm_initialized==0) return STM_ENOTINIT; + + __stm_ehclear = 0; + + _CPU_ISR_Disable(level); + ret = IOS_IoctlAsync(__stm_eh_fd,IOCTL_STM_EVENTHOOK,__stm_ehbufin,0x20,__stm_ehbufout,0x20,__STMEventHandler,NULL); + if(ret<0) __stm_ehregistered = 0; + else __stm_ehregistered = 1; + _CPU_ISR_Restore(level); + + return ret; +} + +s32 __STM_ReleaseEventHook() +{ + s32 ret; + + if(__stm_initialized==0) return STM_ENOTINIT; + if(__stm_ehregistered==0) return STM_ENOHANDLER; + + __stm_ehclear = 1; + + ret = IOS_Ioctl(__stm_imm_fd,IOCTL_STM_RELEASE_EH,__stm_immbufin,0x20,__stm_immbufout,0x20); + if(ret>=0) __stm_ehregistered = 0; + + return ret; +} + +static s32 __STMEventHandler(s32 result,void *usrdata) +{ + __stm_ehregistered = 0; + + if(result < 0) { // shouldn't happen + return result; + } + + if(__stm_ehclear) { //release + return 0; + } + + if(__stm_eventcb) { + __stm_eventcb(__stm_ehbufout[0]); + } + + __STM_SetEventHook(); + + return 0; +} + +stmcallback STM_RegisterEventHandler(stmcallback newhandler) +{ + stmcallback old; + old = __stm_eventcb; + __stm_eventcb = newhandler; + return old; +} + +s32 STM_ShutdownToStandby() +{ + int res; + + _viReg[1] = 0; + if(__stm_initialized==0) { + return STM_ENOTINIT; + } + __stm_immbufin[0] = 0; + res= IOS_Ioctl(__stm_imm_fd,IOCTL_STM_SHUTDOWN,__stm_immbufin,0x20,__stm_immbufout,0x20); + if(res<0) { + } + return res; +} + +s32 STM_ShutdownToIdle() +{ + int res; + + _viReg[1] = 0; + if(__stm_initialized==0) { + return STM_ENOTINIT; + } + switch(SYS_GetHollywoodRevision()) { + case 0: + case 1: + case 2: + __stm_immbufin[0] = 0xFCA08280; + default: + __stm_immbufin[0] = 0xFCE082C0; + } + res= IOS_Ioctl(__stm_imm_fd,IOCTL_STM_IDLE,__stm_immbufin,0x20,__stm_immbufout,0x20); + if(res<0) { + } + return res; +} + +s32 STM_SetLedMode(u32 mode) +{ + int res; + if(__stm_initialized==0) { + return STM_ENOTINIT; + } + __stm_immbufin[0] = mode; + res= IOS_Ioctl(__stm_imm_fd,IOCTL_STM_LEDMODE,__stm_immbufin,0x20,__stm_immbufout,0x20); + if(res<0) { + } + return res; +} + +s32 STM_RebootSystem() +{ + int res; + + _viReg[1] = 0; + if(__stm_initialized==0) { + return STM_ENOTINIT; + } + __stm_immbufin[0] = 0; + res= IOS_Ioctl(__stm_imm_fd,IOCTL_STM_HOTRESET,__stm_immbufin,0x20,__stm_immbufout,0x20); + if(res<0) { + } + return res; +} + + + +#endif /* defined(HW_RVL) */ diff --git a/wii/libogc/libogc/sys_state.c b/wii/libogc/libogc/sys_state.c new file mode 100644 index 0000000000..32a96ef55b --- /dev/null +++ b/wii/libogc/libogc/sys_state.c @@ -0,0 +1,3 @@ +#include "sys_state.h" + +u32 _sys_state_curr; diff --git a/wii/libogc/libogc/sys_state.inl b/wii/libogc/libogc/sys_state.inl new file mode 100644 index 0000000000..a3835a96cb --- /dev/null +++ b/wii/libogc/libogc/sys_state.inl @@ -0,0 +1,44 @@ +#ifndef __SYS_STATE_INL__ +#define __SYS_STATE_INL__ + +static __inline__ void __sys_state_init() +{ + _sys_state_curr = SYS_STATE_BEFORE_INIT; +} + +static __inline__ void __sys_state_set(u32 sys_state) +{ + _sys_state_curr = sys_state; +} + +static __inline__ u32 __sys_state_get() +{ + return _sys_state_curr; +} + +static __inline__ u32 __sys_state_beforeinit(u32 statecode) +{ + return (statecode==SYS_STATE_BEFORE_INIT); +} + +static __inline__ u32 __sys_state_beforemultitasking(u32 statecode) +{ + return (statecode==SYS_STATE_BEFORE_MT); +} + +static __inline__ u32 __sys_state_beginmultitasking(u32 statecode) +{ + return (statecode==SYS_STATE_BEGIN_MT); +} + +static __inline__ u32 __sys_state_up(u32 statecode) +{ + return (statecode==SYS_STATE_UP); +} + +static __inline__ u32 __sys_state_failed(u32 statecode) +{ + return (statecode==SYS_STATE_FAILED); +} + +#endif diff --git a/wii/libogc/libogc/system.c b/wii/libogc/libogc/system.c new file mode 100644 index 0000000000..9e76ae3e07 --- /dev/null +++ b/wii/libogc/libogc/system.c @@ -0,0 +1,1723 @@ +/*------------------------------------------------------------- + +system.c -- OS functions and initialization + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#include +#include +#include +#include +#include + +#include "asm.h" +#include "irq.h" +#include "exi.h" +#if defined(HW_RVL) +#include "ipc.h" +#include "ios.h" +#include "stm.h" +#include "es.h" +#include "conf.h" +#include "wiilaunch.h" +#endif +#include "cache.h" +#include "video.h" +#include "system.h" +#include "sys_state.h" +#include "lwp_threads.h" +#include "lwp_priority.h" +#include "lwp_watchdog.h" +#include "lwp_wkspace.h" +#include "lwp_objmgr.h" +#include "lwp_config.h" +#include "libversion.h" + +#define SYSMEM1_SIZE 0x01800000 +#if defined(HW_RVL) +#define SYSMEM2_SIZE 0x04000000 +#endif +#define KERNEL_HEAP (1*1024*1024) + +// DSPCR bits +#define DSPCR_DSPRESET 0x0800 // Reset DSP +#define DSPCR_DSPDMA 0x0200 // ARAM dma in progress, if set +#define DSPCR_DSPINTMSK 0x0100 // * interrupt mask (RW) +#define DSPCR_DSPINT 0x0080 // * interrupt active (RWC) +#define DSPCR_ARINTMSK 0x0040 +#define DSPCR_ARINT 0x0020 +#define DSPCR_AIINTMSK 0x0010 +#define DSPCR_AIINT 0x0008 +#define DSPCR_HALT 0x0004 // halt DSP +#define DSPCR_PIINT 0x0002 // assert DSP PI interrupt +#define DSPCR_RES 0x0001 // reset DSP + +#define LWP_OBJTYPE_SYSWD 7 + +#define LWP_CHECK_SYSWD(hndl) \ +{ \ + if(((hndl)==SYS_WD_NULL) || (LWP_OBJTYPE(hndl)!=LWP_OBJTYPE_SYSWD)) \ + return NULL; \ +} + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +struct _sramcntrl { + u8 srambuf[64]; + u32 offset; + s32 enabled; + s32 locked; + s32 sync; +} sramcntrl ATTRIBUTE_ALIGN(32); + +typedef struct _alarm_st +{ + lwp_obj object; + wd_cntrl alarm; + u64 ticks; + u64 periodic; + u64 start_per; + alarmcallback alarmhandler; + void *cb_arg; +} alarm_st; + +typedef struct _yay0header { + unsigned int id ATTRIBUTE_PACKED; + unsigned int dec_size ATTRIBUTE_PACKED; + unsigned int links_offset ATTRIBUTE_PACKED; + unsigned int chunks_offset ATTRIBUTE_PACKED; +} yay0header; + +static u16 sys_fontenc = 0xffff; +static u32 sys_fontcharsinsheet = 0; +static u8 *sys_fontwidthtab = NULL; +static u8 *sys_fontimage = NULL; +static sys_fontheader *sys_fontdata = NULL; + +static lwp_queue sys_reset_func_queue; +static u32 system_initialized = 0; +static lwp_objinfo sys_alarm_objects; + +static void *__sysarena1lo = NULL; +static void *__sysarena1hi = NULL; + +#if defined(HW_RVL) +static void *__sysarena2lo = NULL; +static void *__sysarena2hi = NULL; +static void *__ipcbufferlo = NULL; +static void *__ipcbufferhi = NULL; +#endif + +static void __RSWDefaultHandler(); +static resetcallback __RSWCallback = NULL; +#if defined(HW_RVL) +static void __POWDefaultHandler(); +static powercallback __POWCallback = NULL; + +static u32 __sys_resetdown = 0; +#endif + +static vu16* const _viReg = (u16*)0xCC002000; +static vu32* const _piReg = (u32*)0xCC003000; +static vu16* const _memReg = (u16*)0xCC004000; +static vu16* const _dspReg = (u16*)0xCC005000; + +void __SYS_ReadROM(void *buf,u32 len,u32 offset); +void* SYS_AllocArena1MemLo(u32 size,u32 align); + +static s32 __sram_sync(void); +static s32 __sram_writecallback(s32 chn,s32 dev); +static s32 __mem_onreset(s32 final); + +extern void __lwp_thread_coreinit(void); +extern void __lwp_sysinit(void); +extern void __heap_init(void); +extern void __exception_init(void); +extern void __exception_closeall(void); +extern void __systemcall_init(void); +extern void __decrementer_init(void); +extern void __lwp_mutex_init(void); +extern void __lwp_cond_init(void); +extern void __lwp_mqbox_init(void); +extern void __lwp_sema_init(void); +extern void __exi_init(void); +extern void __si_init(void); +extern void __irq_init(void); +extern void __lwp_start_multitasking(void); +extern void __timesystem_init(void); +extern void __memlock_init(void); +extern void __libc_init(int); + +extern void __libogc_malloc_lock( struct _reent *ptr ); +extern void __libogc_malloc_unlock( struct _reent *ptr ); + +extern void __exception_console(void); +extern void __exception_printf(const char *str, ...); + +extern void __realmode(void*); +extern void __configMEM1_24Mb(void); +extern void __configMEM1_48Mb(void); +extern void __configMEM2_64Mb(void); +extern void __configMEM2_128Mb(void); +extern void __reset(u32 reset_code); + +extern u32 __IPC_ClntInit(void); +extern u32 __PADDisableRecalibration(s32 disable); + +extern void __console_init_ex(void *conbuffer,int tgt_xstart,int tgt_ystart,int tgt_stride,int con_xres,int con_yres,int con_stride); + +extern int clock_gettime(struct timespec *tp); +extern void timespec_subtract(const struct timespec *tp_start,const struct timespec *tp_end,struct timespec *result); + + +extern int __libogc_lock_init(int *lock,int recursive); +extern int __libogc_lock_close(int *lock); +extern int __libogc_lock_release(int *lock); +extern int __libogc_lock_acquire(int *lock); +extern void __libogc_exit(int status); +extern void * __libogc_sbrk_r(struct _reent *ptr, ptrdiff_t incr); +extern int __libogc_gettod_r(struct _reent *ptr, struct timeval *tp, struct timezone *tz); + +extern u8 __gxregs[]; +extern u8 __text_start[]; +extern u8 __isIPL[]; +extern u8 __Arena1Lo[], __Arena1Hi[]; +#if defined(HW_RVL) +extern u8 __Arena2Lo[], __Arena2Hi[]; +extern u8 __ipcbufferLo[], __ipcbufferHi[]; +#endif + +u8 *__argvArena1Lo = (u8*)0xdeadbeef; + +static u32 __sys_inIPL = (u32)__isIPL; + +static u32 _dsp_initcode[] = +{ + 0x029F0010,0x029F0033,0x029F0034,0x029F0035, + 0x029F0036,0x029F0037,0x029F0038,0x029F0039, + 0x12061203,0x12041205,0x00808000,0x0088FFFF, + 0x00841000,0x0064001D,0x02180000,0x81001C1E, + 0x00441B1E,0x00840800,0x00640027,0x191E0000, + 0x00DEFFFC,0x02A08000,0x029C0028,0x16FC0054, + 0x16FD4348,0x002102FF,0x02FF02FF,0x02FF02FF, + 0x02FF02FF,0x00000000,0x00000000,0x00000000 +}; + +static sys_resetinfo mem_resetinfo = { + {}, + __mem_onreset, + 127 +}; + +static const char *__sys_versiondate; +static const char *__sys_versionbuild; + +static __inline__ alarm_st* __lwp_syswd_open(syswd_t wd) +{ + LWP_CHECK_SYSWD(wd); + return (alarm_st*)__lwp_objmgr_get(&sys_alarm_objects,LWP_OBJMASKID(wd)); +} + +static __inline__ void __lwp_syswd_free(alarm_st *alarm) +{ + __lwp_objmgr_close(&sys_alarm_objects,&alarm->object); + __lwp_objmgr_free(&sys_alarm_objects,&alarm->object); +} + +#ifdef HW_DOL +#define SOFTRESET_ADR *((vu32*)0xCC003024) +void __reload() { SOFTRESET_ADR=0; } + +void __libogc_exit(int status) +{ + SYS_ResetSystem(SYS_SHUTDOWN,0,0); + __lwp_thread_stopmultitasking(__reload); +} +#else +static void (*reload)() = (void(*)())0x80001800; + +static bool __stub_found() +{ + u64 sig = ((u64)(*(u32*)0x80001804) << 32) + *(u32*)0x80001808; + if (sig == 0x5354554248415858ULL) // 'STUBHAXX' + return true; + return false; +} + +void __reload() +{ + if(__stub_found()) { + __exception_closeall(); + reload(); + } + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); +} + +void __libogc_exit(int status) +{ + if(__stub_found()) { + SYS_ResetSystem(SYS_SHUTDOWN,0,0); + __lwp_thread_stopmultitasking(reload); + } + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); +} + +#endif + +static void __init_syscall_array() { + __syscalls.sbrk_r = __libogc_sbrk_r; + __syscalls.lock_init = __libogc_lock_init; + __syscalls.lock_close = __libogc_lock_close; + __syscalls.lock_release = __libogc_lock_release; + __syscalls.lock_acquire = __libogc_lock_acquire; + __syscalls.malloc_lock = __libogc_malloc_lock; + __syscalls.malloc_unlock = __libogc_malloc_unlock; + __syscalls.exit = __libogc_exit; + __syscalls.gettod_r = __libogc_gettod_r; + +} + +static alarm_st* __lwp_syswd_allocate() +{ + alarm_st *alarm; + + __lwp_thread_dispatchdisable(); + alarm = (alarm_st*)__lwp_objmgr_allocate(&sys_alarm_objects); + if(alarm) { + __lwp_objmgr_open(&sys_alarm_objects,&alarm->object); + return alarm; + } + __lwp_thread_dispatchenable(); + return NULL; +} + +static s32 __mem_onreset(s32 final) +{ + if(final==TRUE) { + _memReg[8] = 255; + __UnmaskIrq(IM_MEM0|IM_MEM1|IM_MEM2|IM_MEM3); + } + return 1; +} + +static void __sys_alarmhandler(void *arg) +{ + alarm_st *alarm; + syswd_t thealarm = (syswd_t)arg; + + if(thealarm==SYS_WD_NULL || LWP_OBJTYPE(thealarm)!=LWP_OBJTYPE_SYSWD) return; + + __lwp_thread_dispatchdisable(); + alarm = (alarm_st*)__lwp_objmgr_getnoprotection(&sys_alarm_objects,LWP_OBJMASKID(thealarm)); + if(alarm) { + if(alarm->alarmhandler) alarm->alarmhandler(thealarm,alarm->cb_arg); + if(alarm->periodic) __lwp_wd_insert_ticks(&alarm->alarm,alarm->periodic); + } + __lwp_thread_dispatchunnest(); +} + +#if defined(HW_DOL) +static void __dohotreset(u32 resetcode) +{ + u32 level; + + _CPU_ISR_Disable(level); + _viReg[1] = 0; + ICFlashInvalidate(); + __reset(resetcode<<3); +} +#endif + +static s32 __call_resetfuncs(s32 final) +{ + s32 ret; + sys_resetinfo *info; + lwp_queue *header = &sys_reset_func_queue; + + ret = 1; + info = (sys_resetinfo*)header->first; + while(info!=(sys_resetinfo*)__lwp_queue_tail(header)) { + if(info->func && info->func(final)==0) ret |= (ret<<1); + info = (sys_resetinfo*)info->node.next; + } + if(__sram_sync()==0) ret |= (ret<<1); + + if(ret&~0x01) return 0; + return 1; +} + +#if defined(HW_DOL) +static void __doreboot(u32 resetcode,s32 force_menu) +{ + u32 level; + + _CPU_ISR_Disable(level); + + *((u32*)0x817ffffc) = 0; + *((u32*)0x817ffff8) = 0; + *((u32*)0x800030e2) = 1; +} +#endif + +static void __MEMInterruptHandler() +{ + _memReg[16] = 0; +} + +static void __RSWDefaultHandler() +{ + +} + +#if defined(HW_RVL) +static void __POWDefaultHandler() +{ +} +#endif + +#if defined(HW_DOL) +static void __RSWHandler() +{ + s64 now; + static s64 hold_down = 0; + + hold_down = gettime(); + do { + now = gettime(); + if(diff_usec(hold_down,now)>=100) break; + } while(!(_piReg[0]&0x10000)); + + if(_piReg[0]&0x10000) { + __MaskIrq(IRQMASK(IRQ_PI_RSW)); + + if(__RSWCallback) { + __RSWCallback(); + } + } + _piReg[0] = 2; +} +#endif + +#if defined(HW_RVL) +static void __STMEventHandler(u32 event) +{ + s32 ret; + u32 level; + + if(event==STM_EVENT_RESET) { + ret = SYS_ResetButtonDown(); + if(ret) { + _CPU_ISR_Disable(level); + __sys_resetdown = 1; + __RSWCallback(); + _CPU_ISR_Restore(level); + } + } + + if(event==STM_EVENT_POWER) { + _CPU_ISR_Disable(level); + __POWCallback(); + _CPU_ISR_Restore(level); + } +} +#endif + +void * __attribute__ ((weak)) __myArena1Lo = 0; +void * __attribute__ ((weak)) __myArena1Hi = 0; + +static void __lowmem_init() +{ + u32 *_gx = (u32*)__gxregs; + +#if defined(HW_DOL) + void *ram_start = (void*)0x80000000; + void *ram_end = (void*)(0x80000000|SYSMEM1_SIZE); + void *arena_start = (void*)0x80003000; +#elif defined(HW_RVL) + void *arena_start = (void*)0x80003F00; +#endif + + memset(_gx,0,2048); + memset(arena_start,0,0x100); + if ( __argvArena1Lo == (u8*)0xdeadbeef ) __argvArena1Lo = __Arena1Lo; + if (__myArena1Lo == 0) __myArena1Lo = __argvArena1Lo; + if (__myArena1Hi == 0) __myArena1Hi = __Arena1Hi; + +#if defined(HW_DOL) + memset(ram_start,0,0x100); + *((u32*)(ram_start+0x20)) = 0x0d15ea5e; // magic word "disease" + *((u32*)(ram_start+0x24)) = 1; // version + *((u32*)(ram_start+0x28)) = SYSMEM1_SIZE; // physical memory size + *((u32*)(ram_start+0x2C)) = 1 + ((*(u32*)0xCC00302c)>>28); + + *((u32*)(ram_start+0x30)) = (u32)__myArena1Lo; + *((u32*)(ram_start+0x34)) = (u32)__myArena1Hi; + + *((u32*)(ram_start+0xEC)) = (u32)ram_end; // ram_end (??) + *((u32*)(ram_start+0xF0)) = SYSMEM1_SIZE; // simulated memory size + *((u32*)(ram_start+0xF8)) = TB_BUS_CLOCK; // bus speed: 162 MHz + *((u32*)(ram_start+0xFC)) = TB_CORE_CLOCK; // cpu speed: 486 Mhz + + *((u16*)(arena_start+0xE0)) = 6; // production pads + *((u32*)(arena_start+0xE4)) = 0xC0008000; + + DCFlushRangeNoSync(ram_start, 0x100); +#endif + + DCFlushRangeNoSync(arena_start, 0x100); + DCFlushRangeNoSync(_gx, 2048); + _sync(); + + SYS_SetArenaLo((void*)__myArena1Lo); + SYS_SetArenaHi((void*)__myArena1Hi); +#if defined(HW_RVL) + SYS_SetArena2Lo((void*)__Arena2Lo); + SYS_SetArena2Hi((void*)__Arena2Hi); +#endif +} + +#if defined(HW_RVL) +static void __ipcbuffer_init() +{ + __ipcbufferlo = (void*)__ipcbufferLo; + __ipcbufferhi = (void*)__ipcbufferHi; +} +#endif + +static void __memprotect_init() +{ + u32 level; + + _CPU_ISR_Disable(level); + + __MaskIrq((IM_MEM0|IM_MEM1|IM_MEM2|IM_MEM3)); + + _memReg[16] = 0; + _memReg[8] = 255; + + IRQ_Request(IRQ_MEM0,__MEMInterruptHandler,NULL); + IRQ_Request(IRQ_MEM1,__MEMInterruptHandler,NULL); + IRQ_Request(IRQ_MEM2,__MEMInterruptHandler,NULL); + IRQ_Request(IRQ_MEM3,__MEMInterruptHandler,NULL); + IRQ_Request(IRQ_MEMADDRESS,__MEMInterruptHandler,NULL); + + SYS_RegisterResetFunc(&mem_resetinfo); + __UnmaskIrq(IM_MEMADDRESS); //only enable memaddress irq atm + + _CPU_ISR_Restore(level); +} + +static __inline__ u32 __get_fontsize(void *buffer) +{ + u8 *ptr = (u8*)buffer; + + if(ptr[0]=='Y' && ptr[1]=='a' && ptr[2]=='y') return (((u32*)ptr)[1]); + else return 0; +} + +static u32 __read_rom(void *buf,u32 len,u32 offset) +{ + u32 ret; + u32 loff; + + DCInvalidateRange(buf,len); + + if(EXI_Lock(EXI_CHANNEL_0,EXI_DEVICE_1,NULL)==0) return 0; + if(EXI_Select(EXI_CHANNEL_0,EXI_DEVICE_1,EXI_SPEED8MHZ)==0) { + EXI_Unlock(EXI_CHANNEL_0); + return 0; + } + + ret = 0; + loff = offset<<6; + if(EXI_Imm(EXI_CHANNEL_0,&loff,4,EXI_WRITE,NULL)==0) ret |= 0x0001; + if(EXI_Sync(EXI_CHANNEL_0)==0) ret |= 0x0002; + if(EXI_Dma(EXI_CHANNEL_0,buf,len,EXI_READ,NULL)==0) ret |= 0x0004; + if(EXI_Sync(EXI_CHANNEL_0)==0) ret |= 0x0008; + if(EXI_Deselect(EXI_CHANNEL_0)==0) ret |= 0x0010; + if(EXI_Unlock(EXI_CHANNEL_0)==0) ret |= 0x00020; + + if(ret) return 0; + return 1; +} + +static u32 __getrtc(u32 *gctime) +{ + u32 ret; + u32 cmd; + u32 time; + + if(EXI_Lock(EXI_CHANNEL_0,EXI_DEVICE_1,NULL)==0) return 0; + if(EXI_Select(EXI_CHANNEL_0,EXI_DEVICE_1,EXI_SPEED8MHZ)==0) { + EXI_Unlock(EXI_CHANNEL_0); + return 0; + } + + ret = 0; + time = 0; + cmd = 0x20000000; + if(EXI_Imm(EXI_CHANNEL_0,&cmd,4,EXI_WRITE,NULL)==0) ret |= 0x01; + if(EXI_Sync(EXI_CHANNEL_0)==0) ret |= 0x02; + if(EXI_Imm(EXI_CHANNEL_0,&time,4,EXI_READ,NULL)==0) ret |= 0x04; + if(EXI_Sync(EXI_CHANNEL_0)==0) ret |= 0x08; + if(EXI_Deselect(EXI_CHANNEL_0)==0) ret |= 0x10; + + EXI_Unlock(EXI_CHANNEL_0); + *gctime = time; + if(ret) return 0; + + return 1; +} + +static u32 __sram_read(void *buffer) +{ + u32 command,ret; + + DCInvalidateRange(buffer,64); + + if(EXI_Lock(EXI_CHANNEL_0,EXI_DEVICE_1,NULL)==0) return 0; + if(EXI_Select(EXI_CHANNEL_0,EXI_DEVICE_1,EXI_SPEED8MHZ)==0) { + EXI_Unlock(EXI_CHANNEL_0); + return 0; + } + + ret = 0; + command = 0x20000100; + if(EXI_Imm(EXI_CHANNEL_0,&command,4,EXI_WRITE,NULL)==0) ret |= 0x01; + if(EXI_Sync(EXI_CHANNEL_0)==0) ret |= 0x02; + if(EXI_Dma(EXI_CHANNEL_0,buffer,64,EXI_READ,NULL)==0) ret |= 0x04; + if(EXI_Sync(EXI_CHANNEL_0)==0) ret |= 0x08; + if(EXI_Deselect(EXI_CHANNEL_0)==0) ret |= 0x10; + if(EXI_Unlock(EXI_CHANNEL_0)==0) ret |= 0x20; + + if(ret) return 0; + return 1; +} + +static u32 __sram_write(void *buffer,u32 loc,u32 len) +{ + u32 cmd,ret; + + if(EXI_Lock(EXI_CHANNEL_0,EXI_DEVICE_1,__sram_writecallback)==0) return 0; + if(EXI_Select(EXI_CHANNEL_0,EXI_DEVICE_1,EXI_SPEED8MHZ)==0) { + EXI_Unlock(EXI_CHANNEL_0); + return 0; + } + + ret = 0; + cmd = 0xa0000100+(loc<<6); + if(EXI_Imm(EXI_CHANNEL_0,&cmd,4,EXI_WRITE,NULL)==0) ret |= 0x01; + if(EXI_Sync(EXI_CHANNEL_0)==0) ret |= 0x02; + if(EXI_ImmEx(EXI_CHANNEL_0,buffer,len,EXI_WRITE)==0) ret |= 0x04; + if(EXI_Deselect(EXI_CHANNEL_0)==0) ret |= 0x08; + if(EXI_Unlock(EXI_CHANNEL_0)==0) ret |= 0x10; + + if(ret) return 0; + return 1; +} + +static s32 __sram_writecallback(s32 chn,s32 dev) +{ + sramcntrl.sync = __sram_write(sramcntrl.srambuf+sramcntrl.offset,sramcntrl.offset,(64-sramcntrl.offset)); + if(sramcntrl.sync) sramcntrl.offset = 64; + + return 1; +} + +static s32 __sram_sync() +{ + return sramcntrl.sync; +} + +void __sram_init() +{ + sramcntrl.enabled = 0; + sramcntrl.locked = 0; + sramcntrl.sync = __sram_read(sramcntrl.srambuf); + + sramcntrl.offset = 64; +} + +static void DisableWriteGatherPipe() +{ + mtspr(920,(mfspr(920)&~0x40000000)); +} + +static void __buildchecksum(u16 *buffer,u16 *c1,u16 *c2) +{ + u32 i; + + *c1 = 0; + *c2 = 0; + for(i=0;i<4;i++) { + *c1 += buffer[6+i]; + *c2 += buffer[6+i]^-1; + } +} + +static void* __locksram(u32 loc) +{ + u32 level; + + _CPU_ISR_Disable(level); + if(!sramcntrl.locked) { + sramcntrl.enabled = level; + sramcntrl.locked = 1; + return (void*)((u32)sramcntrl.srambuf+loc); + } + _CPU_ISR_Restore(level); + return NULL; +} + +static u32 __unlocksram(u32 write,u32 loc) +{ + syssram *sram = (syssram*)sramcntrl.srambuf; + + if(write) { + if(!loc) { + if((sram->flags&0x03)>0x02) sram->flags = (sram->flags&~0x03); + __buildchecksum((u16*)sramcntrl.srambuf,&sram->checksum,&sram->checksum_inv); + } + if(locsheet_format==0x0000) { + cnt = (sys_fontdata->sheet_fullsize/2)-1; + + while(cnt>=0) { + idx = _SHIFTR(src[cnt],6,2); + val1 = data[idx]; + + idx = _SHIFTR(src[cnt],4,2); + val2 = data[idx]; + + dest[(cnt<<1)+0] =((val1&0xf0)|(val2&0x0f)); + + idx = _SHIFTR(src[cnt],2,2); + val1 = data[idx]; + + idx = _SHIFTR(src[cnt],0,2); + val2 = data[idx]; + + dest[(cnt<<1)+1] =((val1&0xf0)|(val2&0x0f)); + + cnt--; + } + } + DCStoreRange(dest,sys_fontdata->sheet_fullsize); +} + +static void __dsp_bootstrap() +{ + u16 status; + u32 tick; + + memcpy(SYS_GetArenaHi()-128,(void*)0x81000000,128); + memcpy((void*)0x81000000,_dsp_initcode,128); + DCFlushRange((void*)0x81000000,128); + + _dspReg[9] = 67; + _dspReg[5] = (DSPCR_DSPRESET|DSPCR_DSPINT|DSPCR_ARINT|DSPCR_AIINT|DSPCR_HALT); + _dspReg[5] |= DSPCR_RES; + while(_dspReg[5]&DSPCR_RES); + + _dspReg[0] = 0; + while((_SHIFTL(_dspReg[2],16,16)|(_dspReg[3]&0xffff))&0x80000000); + + ((u32*)_dspReg)[8] = 0x01000000; + ((u32*)_dspReg)[9] = 0; + ((u32*)_dspReg)[10] = 32; + + status = _dspReg[5]; + while(!(status&DSPCR_ARINT)) status = _dspReg[5]; + _dspReg[5] = status; + + tick = gettick(); + while((gettick()-tick)<2194); + + ((u32*)_dspReg)[8] = 0x01000000; + ((u32*)_dspReg)[9] = 0; + ((u32*)_dspReg)[10] = 32; + + status = _dspReg[5]; + while(!(status&DSPCR_ARINT)) status = _dspReg[5]; + _dspReg[5] = status; + + _dspReg[5] &= ~DSPCR_DSPRESET; + while(_dspReg[5]&0x400); + + _dspReg[5] &= ~DSPCR_HALT; + while(!(_dspReg[2]&0x8000)); + status = _dspReg[3]; + + _dspReg[5] |= DSPCR_HALT; + _dspReg[5] = (DSPCR_DSPRESET|DSPCR_DSPINT|DSPCR_ARINT|DSPCR_AIINT|DSPCR_HALT); + _dspReg[5] |= DSPCR_RES; + while(_dspReg[5]&DSPCR_RES); + + memcpy((void*)0x81000000,SYS_GetArenaHi()-128,128); +#ifdef _SYS_DEBUG + printf("__audiosystem_init(finish)\n"); +#endif +} + +static void __dsp_shutdown() +{ + u32 tick; + + _dspReg[5] = (DSPCR_DSPRESET|DSPCR_HALT); + _dspReg[27] &= ~0x8000; + while(_dspReg[5]&0x400); + while(_dspReg[5]&0x200); + + _dspReg[5] = (DSPCR_DSPRESET|DSPCR_DSPINT|DSPCR_ARINT|DSPCR_AIINT|DSPCR_HALT); + _dspReg[0] = 0; + while((_SHIFTL(_dspReg[2],16,16)|(_dspReg[3]&0xffff))&0x80000000); + + tick = gettick(); + while((gettick()-tick)<44); + + _dspReg[5] |= DSPCR_RES; + while(_dspReg[5]&DSPCR_RES); +} + +static void decode_szp(void *src,void *dest) +{ + u32 i,k,link; + u8 *dest8,*tmp; + u32 loff,coff,roff; + u32 size,cnt,cmask,bcnt; + yay0header *header; + + dest8 = (u8*)dest; + header = (yay0header*)src; + size = header->dec_size; + loff = header->links_offset; + coff = header->chunks_offset; + + roff = sizeof(yay0header); + cmask = 0; + cnt = 0; + bcnt = 0; + + do { + if(!bcnt) { + cmask = *(u32*)(src+roff); + roff += 4; + bcnt = 32; + } + + if(cmask&0x80000000) { + dest8[cnt++] = *(u8*)(src+coff); + coff++; + } else { + link = *(u16*)(src+loff); + loff += 2; + + tmp = dest8+(cnt-(link&0x0fff)-1); + k = link>>12; + if(k==0) { + k = (*(u8*)(src+coff))+18; + coff++; + } else k += 2; + + for(i=0;i0) { + cpy_cnt = (len>256)?256:len; + while(__read_rom(buf,cpy_cnt,offset)==0); + offset += cpy_cnt; + buf += cpy_cnt; + len -= cpy_cnt; + } +} + +u32 __SYS_GetRTC(u32 *gctime) +{ + u32 cnt,ret; + u32 time1,time2; + + cnt = 0; + ret = 0; + while(cnt<16) { + if(__getrtc(&time1)==0) ret |= 0x01; + if(__getrtc(&time2)==0) ret |= 0x02; + if(ret) return 0; + if(time1==time2) { + *gctime = time1; + return 1; + } + cnt++; + } + return 0; +} + +void __SYS_SetTime(s64 time) +{ + u32 level; + s64 now; + s64 *pBootTime = (s64*)0x800030d8; + + _CPU_ISR_Disable(level); + now = gettime(); + now -= time; + now += *pBootTime; + *pBootTime = now; + settime(now); + EXI_ProbeReset(); + _CPU_ISR_Restore(level); +} + +s64 __SYS_GetSystemTime() +{ + u32 level; + s64 now; + s64 *pBootTime = (s64*)0x800030d8; + + _CPU_ISR_Disable(level); + now = gettime(); + now += *pBootTime; + _CPU_ISR_Restore(level); + return now; +} + +void __SYS_SetBootTime() +{ + u32 gctime; + + __SYS_LockSram(); + __SYS_GetRTC(&gctime); + __SYS_SetTime(secs_to_ticks(gctime)); + __SYS_UnlockSram(0); +} + +u32 __SYS_LoadFont(void *src,void *dest) +{ + if(__read_font(src)==0) return 0; + + decode_szp(src,dest); + + sys_fontdata = (sys_fontheader*)dest; + sys_fontwidthtab = (u8*)dest+sys_fontdata->width_table; + sys_fontcharsinsheet = sys_fontdata->sheet_column*sys_fontdata->sheet_row; + + /* TODO: implement SJIS handling */ + return 1; +} + +#if defined(HW_RVL) +void* __SYS_GetIPCBufferLo() +{ + return __ipcbufferlo; +} + +void* __SYS_GetIPCBufferHi() +{ + return __ipcbufferhi; +} + +#endif + +void _V_EXPORTNAME(void) +{ __sys_versionbuild = _V_STRING; __sys_versiondate = _V_DATE_; } + +#if defined(HW_RVL) +void __SYS_DoPowerCB(void) +{ + u32 level; + powercallback powcb; + + _CPU_ISR_Disable(level); + powcb = __POWCallback; + __POWCallback = __POWDefaultHandler; + powcb(); + _CPU_ISR_Restore(level); +} +#endif + +void __SYS_InitCallbacks() +{ +#if defined(HW_RVL) + __POWCallback = __POWDefaultHandler; + __sys_resetdown = 0; +#endif + __RSWCallback = __RSWDefaultHandler; +} + +void __attribute__((weak)) __SYS_PreInit() +{ + +} + +void SYS_Init() +{ + u32 level; + + _CPU_ISR_Disable(level); + + __SYS_PreInit(); + + if(system_initialized) return; + system_initialized = 1; + + _V_EXPORTNAME(); + + __init_syscall_array(); + __lowmem_init(); +#if defined(HW_RVL) + __ipcbuffer_init(); +#endif + __lwp_wkspace_init(KERNEL_HEAP); + __lwp_queue_init_empty(&sys_reset_func_queue); + __lwp_objmgr_initinfo(&sys_alarm_objects,LWP_MAX_WATCHDOGS,sizeof(alarm_st)); + __sys_state_init(); + __lwp_priority_init(); + __lwp_watchdog_init(); + __exception_init(); + __systemcall_init(); + __decrementer_init(); + __irq_init(); + __exi_init(); + __sram_init(); + __si_init(); + __lwp_thread_coreinit(); + __lwp_sysinit(); + __memlock_init(); + __lwp_mqbox_init(); + __lwp_sema_init(); + __lwp_mutex_init(); + __lwp_cond_init(); + __timesystem_init(); + __dsp_bootstrap(); + + if(!__sys_inIPL) + __memprotect_init(); + +#ifdef SDLOADER_FIX + __SYS_SetBootTime(); +#endif + DisableWriteGatherPipe(); + __SYS_InitCallbacks(); +#if defined(HW_RVL) + __IPC_ClntInit(); +#elif defined(HW_DOL) + IRQ_Request(IRQ_PI_RSW,__RSWHandler,NULL); + __MaskIrq(IRQMASK(IRQ_PI_RSW)); +#endif + __libc_init(1); + __lwp_thread_startmultitasking(); + _CPU_ISR_Restore(level); +} + +// This function gets called inside the main thread, prior to the application's main() function +void SYS_PreMain() +{ +#if defined(HW_RVL) + u32 i; + + for (i = 0; i < 32; ++i) + IOS_Close(i); + + __IOS_LoadStartupIOS(); + __IOS_InitializeSubsystems(); + STM_RegisterEventHandler(__STMEventHandler); + CONF_Init(); + WII_Initialize(); +#endif +} + +u32 SYS_ResetButtonDown() +{ + return (!(_piReg[0]&0x00010000)); +} + +#if defined(HW_DOL) +void SYS_ResetSystem(s32 reset,u32 reset_code,s32 force_menu) +{ + u32 ret = 0; + syssram *sram; + + __dsp_shutdown(); + + if(reset==SYS_SHUTDOWN) { + ret = __PADDisableRecalibration(TRUE); + } + + while(__call_resetfuncs(FALSE)==0); + + if(reset==SYS_HOTRESET && force_menu==TRUE) { + sram = __SYS_LockSram(); + sram->flags |= 0x40; + __SYS_UnlockSram(TRUE); + while(!__SYS_SyncSram()); + } + + __exception_closeall(); + __call_resetfuncs(TRUE); + + LCDisable(); + + __lwp_thread_dispatchdisable(); + if(reset==SYS_HOTRESET) { + __dohotreset(reset_code); + } else if(reset==SYS_RESTART) { + __lwp_thread_closeall(); + __lwp_thread_dispatchunnest(); + __doreboot(reset_code,force_menu); + } + + __lwp_thread_closeall(); + + memset((void*)0x80000040,0,140); + memset((void*)0x800000D4,0,20); + memset((void*)0x800000F4,0,4); + memset((void*)0x80003000,0,192); + memset((void*)0x800030C8,0,12); + memset((void*)0x800030E2,0,1); + + __PADDisableRecalibration(ret); +} +#endif + +#if defined(HW_RVL) + +void SYS_ResetSystem(s32 reset,u32 reset_code,s32 force_menu) +{ + u32 ret = 0; + + __dsp_shutdown(); + + if(reset==SYS_SHUTDOWN) { + ret = __PADDisableRecalibration(TRUE); + } + + while(__call_resetfuncs(FALSE)==0); + + switch(reset) { + case SYS_RESTART: + STM_RebootSystem(); + break; + case SYS_POWEROFF: + if(CONF_GetShutdownMode() == CONF_SHUTDOWN_IDLE) { + ret = CONF_GetIdleLedMode(); + if(ret <= 2) STM_SetLedMode(ret); + STM_ShutdownToIdle(); + } else { + STM_ShutdownToStandby(); + } + break; + case SYS_POWEROFF_STANDBY: + STM_ShutdownToStandby(); + break; + case SYS_POWEROFF_IDLE: + ret = CONF_GetIdleLedMode(); + if(ret >= 0 && ret <= 2) STM_SetLedMode(ret); + STM_ShutdownToIdle(); + break; + case SYS_RETURNTOMENU: + WII_ReturnToMenu(); + break; + } + + //TODO: implement SYS_HOTRESET + // either restart failed or this is SYS_SHUTDOWN + + __IOS_ShutdownSubsystems(); + + __exception_closeall(); + __call_resetfuncs(TRUE); + + LCDisable(); + + __lwp_thread_dispatchdisable(); + __lwp_thread_closeall(); + + memset((void*)0x80000040,0,140); + memset((void*)0x800000D4,0,20); + memset((void*)0x800000F4,0,4); + memset((void*)0x80003000,0,192); + memset((void*)0x800030C8,0,12); + memset((void*)0x800030E2,0,1); + + __PADDisableRecalibration(ret); +} +#endif + +void SYS_RegisterResetFunc(sys_resetinfo *info) +{ + u32 level; + sys_resetinfo *after; + lwp_queue *header = &sys_reset_func_queue; + + _CPU_ISR_Disable(level); + for(after=(sys_resetinfo*)header->first;after->node.next!=NULL && info->prio>=after->prio;after=(sys_resetinfo*)after->node.next); + __lwp_queue_insertI(after->node.prev,&info->node); + _CPU_ISR_Restore(level); +} + +void SYS_UnregisterResetFunc(sys_resetinfo *info) { + u32 level; + lwp_node *n; + + _CPU_ISR_Disable(level); + for (n = sys_reset_func_queue.first; n->next; n = n->next) { + if (n == &info->node) { + __lwp_queue_extractI(n); + break; + } + } + _CPU_ISR_Restore(level); +} + +void SYS_SetArena1Lo(void *newLo) +{ + u32 level; + + _CPU_ISR_Disable(level); + __sysarena1lo = newLo; + _CPU_ISR_Restore(level); +} + +void* SYS_GetArena1Lo() +{ + u32 level; + void *arenalo; + + _CPU_ISR_Disable(level); + arenalo = __sysarena1lo; + _CPU_ISR_Restore(level); + + return arenalo; +} + +void SYS_SetArena1Hi(void *newHi) +{ + u32 level; + + _CPU_ISR_Disable(level); + __sysarena1hi = newHi; + _CPU_ISR_Restore(level); +} + +void* SYS_GetArena1Hi() +{ + u32 level; + void *arenahi; + + _CPU_ISR_Disable(level); + arenahi = __sysarena1hi; + _CPU_ISR_Restore(level); + + return arenahi; +} + +u32 SYS_GetArena1Size() +{ + u32 level,size; + + _CPU_ISR_Disable(level); + size = ((u32)__sysarena1hi - (u32)__sysarena1lo); + _CPU_ISR_Restore(level); + + return size; +} + +void* SYS_AllocArena1MemLo(u32 size,u32 align) +{ + u32 mem1lo; + void *ptr = NULL; + + mem1lo = (u32)SYS_GetArena1Lo(); + ptr = (void*)((mem1lo+(align-1))&~(align-1)); + mem1lo = ((((u32)ptr+size+align)-1)&~(align-1)); + SYS_SetArena1Lo((void*)mem1lo); + + return ptr; +} + +#if defined(HW_RVL) +void SYS_SetArena2Lo(void *newLo) +{ + u32 level; + + _CPU_ISR_Disable(level); + __sysarena2lo = newLo; + _CPU_ISR_Restore(level); +} + +void* SYS_GetArena2Lo() +{ + u32 level; + void *arenalo; + + _CPU_ISR_Disable(level); + arenalo = __sysarena2lo; + _CPU_ISR_Restore(level); + + return arenalo; +} + +void SYS_SetArena2Hi(void *newHi) +{ + u32 level; + + _CPU_ISR_Disable(level); + __sysarena2hi = newHi; + _CPU_ISR_Restore(level); +} + +void* SYS_GetArena2Hi() +{ + u32 level; + void *arenahi; + + _CPU_ISR_Disable(level); + arenahi = __sysarena2hi; + _CPU_ISR_Restore(level); + + return arenahi; +} + +u32 SYS_GetArena2Size() +{ + u32 level,size; + + _CPU_ISR_Disable(level); + size = ((u32)__sysarena2hi - (u32)__sysarena2lo); + _CPU_ISR_Restore(level); + + return size; +} + +void* SYS_AllocArena2MemLo(u32 size,u32 align) +{ + u32 mem2lo; + void *ptr = NULL; + + mem2lo = (u32)SYS_GetArena2Lo(); + ptr = (void*)((mem2lo+(align-1))&~(align-1)); + mem2lo = ((((u32)ptr+size+align)-1)&~(align-1)); + SYS_SetArena2Lo((void*)mem2lo); + + return ptr; +} +#endif + +void SYS_ProtectRange(u32 chan,void *addr,u32 bytes,u32 cntrl) +{ + u16 rcntrl; + u32 pstart,pend,level; + + if(chansheet_image)+31)&~31); + __expand_font((u8*)font_data+font_data->sheet_image,sys_fontimage); + return 1; + } + + return 0; +} + +void SYS_GetFontTexture(s32 c,void **image,s32 *xpos,s32 *ypos,s32 *width) +{ + u32 sheets,rem; + + *xpos = 0; + *ypos = 0; + *image = NULL; + if(!sys_fontwidthtab || ! sys_fontimage) return; + + if(cfirst_char || c>sys_fontdata->last_char) c = sys_fontdata->inval_char; + else c -= sys_fontdata->first_char; + + sheets = c/sys_fontcharsinsheet; + rem = c%sys_fontcharsinsheet; + *image = sys_fontimage+(sys_fontdata->sheet_size*sheets); + *xpos = (rem%sys_fontdata->sheet_column)*sys_fontdata->cell_width; + *ypos = (rem/sys_fontdata->sheet_column)*sys_fontdata->cell_height; + *width = sys_fontwidthtab[c]; +} + +void SYS_GetFontTexel(s32 c,void *image,s32 pos,s32 stride,s32 *width) +{ + u32 sheets,rem; + u32 xoff,yoff; + u32 xpos,ypos; + u8 *img_start; + u8 *ptr1,*ptr2; + + if(!sys_fontwidthtab || ! sys_fontimage) return; + + if(cfirst_char || c>sys_fontdata->last_char) c = sys_fontdata->inval_char; + else c -= sys_fontdata->first_char; + + sheets = c/sys_fontcharsinsheet; + rem = c%sys_fontcharsinsheet; + xoff = (rem%sys_fontdata->sheet_column)*sys_fontdata->cell_width; + yoff = (rem/sys_fontdata->sheet_column)*sys_fontdata->cell_height; + img_start = sys_fontimage+(sys_fontdata->sheet_size*sheets); + + ypos = 0; + while(yposcell_height) { + xpos = 0; + while(xposcell_width) { + ptr1 = img_start+(((sys_fontdata->sheet_width/8)<<5)*((ypos+yoff)/8)); + ptr1 = ptr1+(((xpos+xoff)/8)<<5); + ptr1 = ptr1+(((ypos+yoff)%8)<<2); + ptr1 = ptr1+(((xpos+xoff)%8)/2); + + ptr2 = image+((ypos/8)*(((stride<<1)/8)<<5)); + ptr2 = ptr2+(((xpos+pos)/8)<<5); + ptr2 = ptr2+(((xpos+pos)%8)/2); + ptr2 = ptr2+((ypos%8)<<2); + + *ptr2 = *ptr1; + + xpos += 2; + } + ypos++; + } + *width = sys_fontwidthtab[c]; +} + +s32 SYS_CreateAlarm(syswd_t *thealarm) +{ + alarm_st *alarm; + + alarm = __lwp_syswd_allocate(); + if(!alarm) return -1; + + alarm->alarmhandler = NULL; + alarm->ticks = 0; + alarm->start_per = 0; + alarm->periodic = 0; + + *thealarm = (LWP_OBJMASKTYPE(LWP_OBJTYPE_SYSWD)|LWP_OBJMASKID(alarm->object.id)); + __lwp_thread_dispatchenable(); + return 0; +} + +s32 SYS_SetAlarm(syswd_t thealarm,const struct timespec *tp,alarmcallback cb,void *cbarg) +{ + alarm_st *alarm; + + alarm = __lwp_syswd_open(thealarm); + if(!alarm) return -1; + + alarm->cb_arg = cbarg; + alarm->alarmhandler = cb; + alarm->ticks = __lwp_wd_calc_ticks(tp); + + alarm->periodic = 0; + alarm->start_per = 0; + + __lwp_wd_initialize(&alarm->alarm,__sys_alarmhandler,alarm->object.id,(void*)thealarm); + __lwp_wd_insert_ticks(&alarm->alarm,alarm->ticks); + __lwp_thread_dispatchenable(); + return 0; +} + +s32 SYS_SetPeriodicAlarm(syswd_t thealarm,const struct timespec *tp_start,const struct timespec *tp_period,alarmcallback cb,void *cbarg) +{ + alarm_st *alarm; + + alarm = __lwp_syswd_open(thealarm); + if(!alarm) return -1; + + alarm->start_per = __lwp_wd_calc_ticks(tp_start); + alarm->periodic = __lwp_wd_calc_ticks(tp_period); + alarm->alarmhandler = cb; + alarm->cb_arg = cbarg; + + alarm->ticks = 0; + + __lwp_wd_initialize(&alarm->alarm,__sys_alarmhandler,alarm->object.id,(void*)thealarm); + __lwp_wd_insert_ticks(&alarm->alarm,alarm->start_per); + __lwp_thread_dispatchenable(); + return 0; +} + +s32 SYS_RemoveAlarm(syswd_t thealarm) +{ + alarm_st *alarm; + + alarm = __lwp_syswd_open(thealarm); + if(!alarm) return -1; + + alarm->alarmhandler = NULL; + alarm->ticks = 0; + alarm->periodic = 0; + alarm->start_per = 0; + + __lwp_wd_remove_ticks(&alarm->alarm); + __lwp_syswd_free(alarm); + __lwp_thread_dispatchenable(); + return 0; +} + +s32 SYS_CancelAlarm(syswd_t thealarm) +{ + alarm_st *alarm; + + alarm = __lwp_syswd_open(thealarm); + if(!alarm) return -1; + + alarm->alarmhandler = NULL; + alarm->ticks = 0; + alarm->periodic = 0; + alarm->start_per = 0; + + __lwp_wd_remove_ticks(&alarm->alarm); + __lwp_thread_dispatchenable(); + return 0; +} + +resetcallback SYS_SetResetCallback(resetcallback cb) +{ + u32 level; + resetcallback old; + + _CPU_ISR_Disable(level); + old = __RSWCallback; + __RSWCallback = cb; +#if defined(HW_DOL) + if(__RSWCallback) { + _piReg[0] = 2; + __UnmaskIrq(IRQMASK(IRQ_PI_RSW)); + } else + __MaskIrq(IRQMASK(IRQ_PI_RSW)); +#endif + _CPU_ISR_Restore(level); + return old; +} + +#if defined(HW_RVL) +powercallback SYS_SetPowerCallback(powercallback cb) +{ + u32 level; + powercallback old; + + _CPU_ISR_Disable(level); + old = __POWCallback; + __POWCallback = cb; + _CPU_ISR_Restore(level); + return old; +} +#endif + +void SYS_StartPMC(u32 mcr0val,u32 mcr1val) +{ + mtmmcr0(mcr0val); + mtmmcr1(mcr1val); +} + +void SYS_StopPMC() +{ + mtmmcr0(0); + mtmmcr1(0); +} + +void SYS_ResetPMC() +{ + mtpmc1(0); + mtpmc2(0); + mtpmc3(0); + mtpmc4(0); +} + +void SYS_DumpPMC() +{ + printf("<%lu load/stores / %lu miss cycles / %lu cycles / %lu instructions>\n",mfpmc1(),mfpmc2(),mfpmc3(),mfpmc4()); +} + +void SYS_SetWirelessID(u32 chan,u32 id) +{ + u32 write; + syssramex *sram; + + write = 0; + sram = __SYS_LockSramEx(); + if(sram->wirelessPad_id[chan]!=(u16)id) { + sram->wirelessPad_id[chan] = (u16)id; + write = 1; + } + __SYS_UnlockSramEx(write); +} + +u32 SYS_GetWirelessID(u32 chan) +{ + u16 id; + syssramex *sram; + + id = 0; + sram = __SYS_LockSramEx(); + id = sram->wirelessPad_id[chan]; + __SYS_UnlockSramEx(0); + return id; +} + +#if defined(HW_RVL) +u32 SYS_GetHollywoodRevision() +{ + u32 rev; + DCInvalidateRange((void*)0x80003138,8); + rev = *((u32*)0x80003138); + return rev; +} +#endif + +u64 SYS_Time() +{ + u64 current_time = 0; + u32 gmtime =0; + __SYS_GetRTC(&gmtime); + current_time = gmtime; +#ifdef HW_RVL + u32 bias; + if (CONF_GetCounterBias(&bias) >= 0) + current_time += bias; +#else + syssram* sram = __SYS_LockSram(); + current_time += sram->counter_bias; + __SYS_UnlockSram(0); +#endif + return (TB_TIMER_CLOCK * 1000) * current_time; +} diff --git a/wii/libogc/libogc/system_asm.S b/wii/libogc/libogc/system_asm.S new file mode 100644 index 0000000000..2b8580ca7e --- /dev/null +++ b/wii/libogc/libogc/system_asm.S @@ -0,0 +1,380 @@ +#include + + .globl __realmode +__realmode: + clrlwi r3,r3,2 + mtsrr0 r3 + mfmsr r3 + rlwinm r3,r3,0,28,25 + mtsrr1 r3 + rfi + + // complete init sequence taken from bootmii's ppc skeleton. thanks to segher + // after a talk with dhewg we came to that point that it's good to wipe+setup BATS correctly + .globl __configBATS +__configBATS: + // HID0 = 00110c64: + // bus checkstops off, sleep modes off, + // caches off, caches invalidate, + // store gathering off, enable data cache + // flush assist, enable branch target cache, + // enable branch history table + lis r3,0x0011 + ori r3,r3,0x0c64 + mtspr HID0,r3 + isync + +#if defined(HW_RVL) + lis r3,0x8200 //bits set: H4A(HID4 access), SBE(2nd BAT enabled) + mtspr HID4,r3 + isync +#endif + + // clear all BATs + li r0,0 + mtspr IBAT0U,r0; mtspr IBAT1U,r0; mtspr IBAT2U,r0; mtspr IBAT3U,r0 // IBAT0...3 + mtspr DBAT0U,r0; mtspr DBAT1U,r0; mtspr DBAT2U,r0; mtspr DBAT3U,r0 // DBAT0...3 +#if defined(HW_RVL) + mtspr IBAT4U,r0; mtspr IBAT5U,r0; mtspr IBAT6U,r0; mtspr IBAT7U,r0 // IBAT4...7 + mtspr DBAT4U,r0; mtspr DBAT5U,r0; mtspr DBAT6U,r0; mtspr DBAT7U,r0 // DBAT4...7 +#endif + isync + + // clear all SRs + lis r0,0x8000 + mtsr 0,r0; mtsr 1,r0; mtsr 2,r0; mtsr 3,r0; mtsr 4,r0; mtsr 5,r0; mtsr 6,r0 + mtsr 7,r0; mtsr 8,r0; mtsr 9,r0; mtsr 10,r0; mtsr 11,r0; mtsr 12,r0; mtsr 13,r0 + mtsr 14,r0; mtsr 15,r0 + isync + + // set [DI]BAT0 for 256MB@80000000, + // real 00000000, WIMG=0000, R/W + li r3,2 + lis r4,0x8000 + ori r4,r4,0x1fff + mtspr IBAT0L,r3 + mtspr IBAT0U,r4 + mtspr DBAT0L,r3 + mtspr DBAT0U,r4 + isync + +#if defined(HW_RVL) + // set [DI]BAT4 for 256MB@90000000, + // real 10000000, WIMG=0000, R/W + addis r3,r3,0x1000 + addis r4,r4,0x1000 + mtspr IBAT4L,r3 + mtspr IBAT4U,r4 + mtspr DBAT4L,r3 + mtspr DBAT4U,r4 + isync +#endif + + // set DBAT1 for 256MB@c0000000, + // real 00000000, WIMG=0101, R/W + li r3,0x2a + lis r4,0xc000 + ori r4,r4,0x1fff + mtspr DBAT1L,r3 + mtspr DBAT1U,r4 + isync + +#if defined(HW_RVL) + // set DBAT5 for 256MB@d0000000, + // real 10000000, WIMG=0101, R/W + addis r3,r3,0x1000 + addis r4,r4,0x1000 + mtspr DBAT5L,r3 + mtspr DBAT5U,r4 + isync +#endif + + mfmsr r3 + ori r3,r3,MSR_DR|MSR_IR + mtsrr1 r3 + mflr r3 + oris r3,r3,0x8000 + mtsrr0 r3 + rfi + + .globl __InitFPRS +__InitFPRS: + # Enable the Floating Point Registers + mfmsr r3 + ori r3,r3,MSR_FP + mtmsr r3 + mfspr r3,920 + extrwi. r3,r3,1,2 + beq 1f + + # Clear all of the PS FPR's to 0 + lis r3,zeroPS@ha + addi r3,r3,zeroPS@l + psq_l fr0,0(r3),0,0 + ps_mr fr1,fr0 + ps_mr fr2,fr0 + ps_mr fr3,fr0 + ps_mr fr4,fr0 + ps_mr fr5,fr0 + ps_mr fr6,fr0 + ps_mr fr7,fr0 + ps_mr fr8,fr0 + ps_mr fr9,fr0 + ps_mr fr10,fr0 + ps_mr fr11,fr0 + ps_mr fr12,fr0 + ps_mr fr13,fr0 + ps_mr fr14,fr0 + ps_mr fr15,fr0 + ps_mr fr16,fr0 + ps_mr fr17,fr0 + ps_mr fr18,fr0 + ps_mr fr19,fr0 + ps_mr fr20,fr0 + ps_mr fr21,fr0 + ps_mr fr22,fr0 + ps_mr fr23,fr0 + ps_mr fr24,fr0 + ps_mr fr25,fr0 + ps_mr fr26,fr0 + ps_mr fr27,fr0 + ps_mr fr28,fr0 + ps_mr fr29,fr0 + ps_mr fr30,fr0 + ps_mr fr31,fr0 + + # Clear all of the FPR's to 0 +1: lis r3,zeroF@ha + lfd fr0,zeroF@l(r3) + fmr fr1,fr0 + fmr fr2,fr0 + fmr fr3,fr0 + fmr fr4,fr0 + fmr fr5,fr0 + fmr fr6,fr0 + fmr fr7,fr0 + fmr fr8,fr0 + fmr fr9,fr0 + fmr fr10,fr0 + fmr fr11,fr0 + fmr fr12,fr0 + fmr fr13,fr0 + fmr fr14,fr0 + fmr fr15,fr0 + fmr fr16,fr0 + fmr fr17,fr0 + fmr fr18,fr0 + fmr fr19,fr0 + fmr fr20,fr0 + fmr fr21,fr0 + fmr fr22,fr0 + fmr fr23,fr0 + fmr fr24,fr0 + fmr fr25,fr0 + fmr fr26,fr0 + fmr fr27,fr0 + fmr fr28,fr0 + fmr fr29,fr0 + fmr fr30,fr0 + fmr fr31,fr0 + mtfsf 255,fr0 + + # Return + blr + + .extern ICFlashInvalidate + .globl __InitPS +__InitPS: + mflr r0 + stw r0,4(sp) + stwu sp,-8(sp) + mfspr r3,HID2 + oris r3,r3,0xA000 + mtspr HID2,r3 + isync + bl ICFlashInvalidate + sync + li r3,0 + mtspr GQR0,r3 + mtspr GQR1,r3 + mtspr GQR2,r3 + mtspr GQR3,r3 + mtspr GQR4,r3 + mtspr GQR5,r3 + mtspr GQR6,r3 + mtspr GQR7,r3 + isync + lwz r0,12(sp) + addi sp,sp,8 + mtlr r0 + blr + + .extern ICEnable + .extern DCEnable + .extern L2Init + .extern L2Enable + .globl __InitCache +__InitCache: + mflr r0 + stw r0, 4(sp) + stwu sp, -16(sp) + stw r31, 12(sp) + + mfspr r3,HID0 + rlwinm. r0,r3, 0, 16, 16 // Check if the Instruction Cache has been enabled or not. + bne ICEnabled + + bl ICEnable +ICEnabled: + mfspr r3, HID0 + rlwinm. r0, r3, 0, 17, 17 // Check if the Data Cache has been enabled or not. + bne DCEnabled + + bl DCEnable +DCEnabled: + + mfspr r3, L2CR + clrrwi. r0, r3, 31 // Check if the Locked Cache has been enabled or not. + bne L2Enabled + + bl L2Init + bl L2Enable + +L2Enabled: + # Restore the non-volatile registers to their previous values and return. + lwz r0, 20(sp) + lwz r31, 12(sp) + addi sp, sp, 16 + mtlr r0 + blr + + .globl __InitSystem +__InitSystem: + mflr r0 + stw r0, 4(sp) + stwu sp, -24(sp) + stmw r29, 12(sp) + + # Disable interrupts! + mfmsr r3 + rlwinm r4,r3,0,17,15 + rlwinm r4,r4,0,26,24 + mtmsr r4 + + # Clear various SPR's + li r3,0 + mtspr 952, r3 + mtspr 956, r3 + mtspr 953, r3 + mtspr 954, r3 + mtspr 957, r3 + mtspr 958, r3 + isync + +#if defined(HW_RVL) + mfspr r3,HID4 + oris r3,r3,0x0190 //set additional bits in HID4: SR0(store 0), LPE(PS LE exception), L2CFI(L2 castout prior to L2 inv. flash) + mtspr HID4,r3 + isync +#endif + + # Disable Speculative Bus Accesses to non-guarded space from both caches. + mfspr r3, HID0 + ori r3, r3, 0x0200 + mtspr HID0, r3 + isync + + # Set the Non-IEEE mode in the FPSCR + mtfsb1 29 + + # Disable Write Gather Pipe + mfspr r3,HID2 # (HID2) + rlwinm r3, r3, 0, 2, 0 + mtspr HID2,r3 # (HID2) + isync + + # Restore the non-volatile registers to their previous values and return. + lwz r0, 28(sp) + lmw r29,12(sp) + addi sp, sp, 24 + mtlr r0 + blr + + .globl __flush_cache +__flush_cache: + lis r5,0xffff + ori r5,r5,0xfff1 + and r5,r5,r3 + subf r3,r5,r3 + add r4,r4,r3 +1: dcbst r0,r5 + sync + icbi r0,r5 + addic r5,r5,8 + subic. r4,r4,8 + bge 1b + sync + blr + + .globl __reset +__reset: + b 1f +9: mfspr r8,HID0 + ori r8,r8,0x0008 + mtspr HID0,r8 + isync + sync + nop + b 2f +1: b 3f +2: mftb r5 +4: mftb r6 + subf r7,r5,r6 + cmplwi r7,0x1124 + blt 4b + nop + b 5f +3: b 6f +5: lis r8,0xCC00 + ori r8,r8,0x3000 + li r4,3 + stw r4,36(r8) + stw r3,36(r8) + nop + b 7f +6: b 8f +7: nop + b 7b +8: b 9b + + .globl __InitBATS +__InitBATS: + mflr r31 + oris r31,r31,0x8000 + lis r3,__configBATS@h + ori r3,r3,__configBATS@l + bl __realmode + mtlr r31 + blr + + .globl SYS_SwitchFiber +SYS_SwitchFiber: + mflr r0 + mr r9,sp + stwu r9,-8(r8) + mr sp,r8 + stw r0,4(r9) + mtlr r7 + blrl + lwz r5,0(sp) + lwz r0,4(r5) + mtlr r0 + mr sp,r5 + blr + + .section .data + .balign 4 +zeroF: + .double 0.0 +zeroPS: + .float 0.0,0.0 diff --git a/wii/libogc/libogc/texconv.c b/wii/libogc/libogc/texconv.c new file mode 100644 index 0000000000..f955b351aa --- /dev/null +++ b/wii/libogc/libogc/texconv.c @@ -0,0 +1,77 @@ +/*------------------------------------------------------------- + +texconv.c - Helper functions for GX texture conversion + +Copyright (C) 2008 +softdev +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ +#include + +void MakeTexture565(const void *src,void *dst,s32 width,s32 height) +{ + register u32 tmp0=0,tmp1=0,tmp2=0,tmp3=0; + + __asm__ __volatile__ ( + " srwi %6,%6,2\n" + " srwi %7,%7,2\n" + " subi %3,%4,4\n" + " subi %4,%4,8\n" + + "2: mtctr %6\n" + " mr %0,%5\n" + // + "1: lwz %1,0(%5)\n" + " stwu %1,8(%4)\n" + " lwz %2,4(%5)\n" + " stwu %2,8(%3)\n" + + " lwz %1,1024(%5)\n" + " stwu %1,8(%4)\n" + " lwz %2,1028(%5)\n" + " stwu %2,8(%3)\n" + + " lwz %1,2048(%5)\n" + " stwu %1,8(%4)\n" + " lwz %2,2052(%5)\n" + " stwu %2,8(%3)\n" + + " lwz %1,3072(%5)\n" + " stwu %1,8(%4)\n" + " lwz %2,3076(%5)\n" + " stwu %2,8(%3)\n" + + " addi %5,%5,8\n" + " bdnz 1b\n" + + " addi %5,%0,4096\n" + " subic. %7,%7,1\n" + " bne 2b" + // 0 1 2 3 + : "=&b"(tmp0), "=&r"(tmp1), "=&r"(tmp2), "=&b"(tmp3) + // 4 5 6 7 + : "b"(dst), "b"(src), "r"(width), "r"(height) + : "memory" + ); +} diff --git a/wii/libogc/libogc/timesupp.c b/wii/libogc/libogc/timesupp.c new file mode 100644 index 0000000000..bdb8770819 --- /dev/null +++ b/wii/libogc/libogc/timesupp.c @@ -0,0 +1,323 @@ +#include <_ansi.h> +#include <_syslist.h> + +#include "asm.h" +#include "processor.h" +#include "lwp.h" +#include "lwp_threadq.h" +#include "timesupp.h" +#include "exi.h" +#include "system.h" +#include "conf.h" + +#include +#include + +/* time variables */ +static u32 exi_wait_inited = 0; +static lwpq_t time_exi_wait; + +extern u32 __SYS_GetRTC(u32 *gctime); +extern syssram* __SYS_LockSram(); +extern u32 __SYS_UnlockSram(u32 write); + + + +u32 _DEFUN(gettick,(), + _NOARGS) + +{ + u32 result; + __asm__ __volatile__ ( + "mftb %0\n" + : "=r" (result) + ); + return result; +} + + +u64 _DEFUN(gettime,(), + _NOARGS) +{ + u32 tmp; + union uulc { + u64 ull; + u32 ul[2]; + } v; + + __asm__ __volatile__( + "1: mftbu %0\n\ + mftb %1\n\ + mftbu %2\n\ + cmpw %0,%2\n\ + bne 1b\n" + : "=r" (v.ul[0]), "=r" (v.ul[1]), "=&r" (tmp) + ); + return v.ull; +} + +void _DEFUN(settime,(t), + u64 t) +{ + u32 tmp; + union uulc { + u64 ull; + u32 ul[2]; + } v; + + v.ull = t; + __asm__ __volatile__ ( + "li %0,0\n\ + mttbl %0\n\ + mttbu %1\n\ + mttbl %2\n" + : "=&r" (tmp) + : "r" (v.ul[0]), "r" (v.ul[1]) + ); +} + +u32 diff_sec(u64 start,u64 end) +{ + u64 diff; + + diff = diff_ticks(start,end); + return ticks_to_secs(diff); +} + +u32 diff_msec(u64 start,u64 end) +{ + u64 diff = diff_ticks(start,end); + return ticks_to_millisecs(diff); +} + +u32 diff_usec(u64 start,u64 end) +{ + u64 diff = diff_ticks(start,end); + return ticks_to_microsecs(diff); +} + +u32 diff_nsec(u64 start,u64 end) +{ + u64 diff = diff_ticks(start,end); + return ticks_to_nanosecs(diff); +} + +void __timesystem_init() +{ + if(!exi_wait_inited) { + exi_wait_inited = 1; + LWP_InitQueue(&time_exi_wait); + } +} + +void timespec_subtract(const struct timespec *tp_start,const struct timespec *tp_end,struct timespec *result) +{ + struct timespec start_st = *tp_start; + struct timespec *start = &start_st; + u32 nsecpersec = TB_NSPERSEC; + + if(tp_end->tv_nsectv_nsec) { + int secs = (start->tv_nsec - tp_end->tv_nsec)/nsecpersec+1; + start->tv_nsec -= nsecpersec * secs; + start->tv_sec += secs; + } + if((tp_end->tv_nsec - start->tv_nsec)>nsecpersec) { + int secs = (start->tv_nsec - tp_end->tv_nsec)/nsecpersec; + start->tv_nsec += nsecpersec * secs; + start->tv_sec -= secs; + } + + result->tv_sec = (tp_end->tv_sec - start->tv_sec); + result->tv_nsec = (tp_end->tv_nsec - start->tv_nsec); +} + +unsigned long long timespec_to_ticks(const struct timespec *tp) +{ + return __lwp_wd_calc_ticks(tp); +} + +int clock_gettime(struct timespec *tp) +{ + u32 gctime; +#if defined(HW_RVL) + u32 wii_bias = 0; +#endif + + if(!tp) return -1; + + if(!__SYS_GetRTC(&gctime)) return -1; + +#if defined(HW_DOL) + syssram* sram = __SYS_LockSram(); + gctime += sram->counter_bias; + __SYS_UnlockSram(0); +#else + if(CONF_GetCounterBias(&wii_bias)>=0) gctime += wii_bias; +#endif + gctime += 946684800; + + tp->tv_sec = gctime; + tp->tv_nsec = ticks_to_nanosecs(gettick())%1000000000; + + return 0; +} + +// this function spins till timeout is reached +void _DEFUN(udelay,(us), + unsigned us) +{ + unsigned long long start, end; + start = gettime(); + while (1) + { + end = gettime(); + if (diff_usec(start,end) >= us) + break; + } +} + +unsigned int _DEFUN(nanosleep,(tb), + struct timespec *tb) +{ + u64 timeout; + + __lwp_thread_dispatchdisable(); + + timeout = __lwp_wd_calc_ticks(tb); + __lwp_thread_setstate(_thr_executing,LWP_STATES_DELAYING|LWP_STATES_INTERRUPTIBLE_BY_SIGNAL); + __lwp_wd_initialize(&_thr_executing->timer,__lwp_thread_delayended,_thr_executing->object.id,_thr_executing); + __lwp_wd_insert_ticks(&_thr_executing->timer,timeout); + + __lwp_thread_dispatchenable(); + return TB_SUCCESSFUL; +} + +static u32 __getrtc(u32 *gctime) +{ + u32 ret; + u32 cmd; + u32 time; + + if(EXI_Select(EXI_CHANNEL_0,EXI_DEVICE_1,EXI_SPEED8MHZ)==0) { + return 0; + } + + ret = 0; + time = 0; + cmd = 0x20000000; + if(EXI_Imm(EXI_CHANNEL_0,&cmd,4,EXI_WRITE,NULL)==0) ret |= 0x01; + if(EXI_Sync(EXI_CHANNEL_0)==0) ret |= 0x02; + if(EXI_Imm(EXI_CHANNEL_0,&time,4,EXI_READ,NULL)==0) ret |= 0x04; + if(EXI_Sync(EXI_CHANNEL_0)==0) ret |= 0x08; + if(EXI_Deselect(EXI_CHANNEL_0)==0) ret |= 0x10; + + *gctime = time; + if(ret) return 0; + + return 1; +} + +static s32 __time_exi_unlock(s32 chn,s32 dev) +{ + LWP_ThreadBroadcast(time_exi_wait); + return 1; +} + +static void __time_exi_wait() +{ + u32 ret; + + do { + if((ret=EXI_Lock(EXI_CHANNEL_0,EXI_DEVICE_1,__time_exi_unlock))==1) break; + LWP_ThreadSleep(time_exi_wait); + }while(ret==0); +} + +static u32 __getRTC(u32 *gctime) +{ + u32 cnt,time1,time2; + + __time_exi_wait(); + + cnt = 0; + + while(cnt<16) { + if(__getrtc(&time1)==0 + || __getrtc(&time2)==0) { + EXI_Unlock(EXI_CHANNEL_0); + break; + } + if(time1==time2) { + *gctime = time1; + EXI_Unlock(EXI_CHANNEL_0); + return 1; + } + cnt++; + } + return 0; +} + +time_t _DEFUN(time,(timer), + time_t *timer) +{ + time_t gctime = 0; +#if defined(HW_RVL) + u32 wii_bias = 0; +#endif + + if(__getRTC((u32*)&gctime)==0) return (time_t)0; + +#if defined(HW_DOL) + syssram* sram = __SYS_LockSram(); + gctime += sram->counter_bias; + __SYS_UnlockSram(0); +#else + if(CONF_GetCounterBias(&wii_bias)>=0) gctime += wii_bias; +#endif + + gctime += 946684800; + + if(timer) *timer = gctime; + return gctime; +} + +unsigned int _DEFUN(sleep,(s), + unsigned int s) +{ + struct timespec tb; + + tb.tv_sec = s; + tb.tv_nsec = 0; + return nanosleep(&tb); +} + +unsigned int _DEFUN(usleep,(us), + unsigned int us) +{ + struct timespec tb; + u32 sec = us/TB_USPERSEC; + u32 rem = us - (sec*TB_USPERSEC); + + tb.tv_sec = sec; + tb.tv_nsec = rem*TB_NSPERUS; + return nanosleep(&tb); +} + +clock_t clock(void) { + return -1; +} + +int __libogc_gettod_r(struct _reent *ptr, struct timeval *tp, struct timezone *tz) { + + if (tp != NULL) { + tp->tv_sec = time(NULL); + tp->tv_usec = ticks_to_microsecs(gettick())%1000000; + } + if (tz != NULL) { + tz->tz_minuteswest = 0; + tz->tz_dsttime = 0; + + } + return 0; +} + diff --git a/wii/libogc/libogc/timesupp.h b/wii/libogc/libogc/timesupp.h new file mode 100644 index 0000000000..2e84d439d9 --- /dev/null +++ b/wii/libogc/libogc/timesupp.h @@ -0,0 +1,27 @@ +#ifndef __TIMESUPP_H__ +#define __TIMESUPP_H__ + +#define TB_REQ 250 +#define TB_SUCCESSFUL 0 + +#define TB_SECSPERMIN 60 +#define TB_MINSPERHR 60 +#define TB_MONSPERYR 12 +#define TB_DAYSPERYR 365 +#define TB_HRSPERDAY 24 +#define TB_SECSPERDAY (TB_SECSPERMIN*TB_MINSPERHR*TB_HRSPERDAY) +#define TB_SECSPERNYR (365*TB_SECSPERDAY) + +#define TB_MSPERSEC 1000 +#define TB_USPERSEC 1000000 +#define TB_NSPERSEC 1000000000 +#define TB_NSPERMS 1000000 +#define TB_NSPERUS 1000 +#define TB_USPERTICK 10000 + +#include + +time_t time(time_t *timer); +unsigned int nanosleep(struct timespec *tb); + +#endif diff --git a/wii/libogc/libogc/tpl.c b/wii/libogc/libogc/tpl.c new file mode 100644 index 0000000000..b6e027cdff --- /dev/null +++ b/wii/libogc/libogc/tpl.c @@ -0,0 +1,346 @@ +#include +#include +#include +#include +#include +#include +#include "tpl.h" +#include "processor.h" + +#define TPL_FILE_TYPE_DISC 0 +#define TPL_FILE_TYPE_MEM 1 + +#define TPL_HDR_VERSION_FIELD 0 +#define TPL_HDR_NTEXTURE_FIELD 4 +#define TPL_HDR_HDRSIZE_FIELD 8 +#define TPL_HDR_DESCR_FIELD 12 + +// texture header +typedef struct _tplimgheader TPLImgHeader; + +struct _tplimgheader { + u16 height; + u16 width; + u32 fmt; + void *data; + u32 wraps; + u32 wrapt; + u32 minfilter; + u32 magfilter; + f32 lodbias; + u8 edgelod; + u8 minlod; + u8 maxlod; + u8 unpacked; +} ATTRIBUTE_PACKED; + +// texture palette header +typedef struct _tplpalheader TPLPalHeader; + +struct _tplpalheader { + u16 nitems; + u8 unpacked; + u8 pad; + u32 fmt; + void *data; +} ATTRIBUTE_PACKED; + +// texture descriptor +typedef struct _tpldesc TPLDescHeader; + +struct _tpldesc { + TPLImgHeader *imghead; + TPLPalHeader *palhead; +} ATTRIBUTE_PACKED; + +static u32 TPL_GetTextureSize(u32 width,u32 height,u32 fmt) +{ + u32 size = 0; + + switch(fmt) { + case GX_TF_I4: + case GX_TF_CI4: + case GX_TF_CMPR: + size = ((width+7)>>3)*((height+7)>>3)*32; + break; + case GX_TF_I8: + case GX_TF_IA4: + case GX_TF_CI8: + size = ((width+7)>>3)*((height+7)>>2)*32; + break; + case GX_TF_IA8: + case GX_TF_CI14: + case GX_TF_RGB565: + case GX_TF_RGB5A3: + size = ((width+3)>>2)*((height+3)>>2)*32; + break; + case GX_TF_RGBA8: + size = ((width+3)>>2)*((height+3)>>2)*32*2; + break; + default: + break; + } + return size; +} + +s32 TPL_OpenTPLFromFile(TPLFile* tdf, const char* file_name) +{ + u32 c; + u32 version; + FILE *f = NULL; + TPLDescHeader *deschead = NULL; + TPLImgHeader *imghead = NULL; + TPLPalHeader *palhead = NULL; + + if(!file_name) return 0; + + f = fopen(file_name,"rb"); + if(!f) return -1; + + tdf->type = TPL_FILE_TYPE_DISC; + tdf->tpl_file = (FHANDLE)f; + + fread(&version,sizeof(u32),1,f); + fread(&tdf->ntextures,sizeof(u32),1,f); + + fseek(f,TPL_HDR_DESCR_FIELD,SEEK_SET); + + deschead = malloc(tdf->ntextures*sizeof(TPLDescHeader)); + if(deschead) { + fread(deschead,sizeof(TPLDescHeader),tdf->ntextures,f); + + for(c=0;cntextures;c++) { + imghead = deschead[c].imghead; + palhead = deschead[c].palhead; + + //now read in the image data. + fseek(f,(s32)imghead,SEEK_SET); + imghead = malloc(sizeof(TPLImgHeader)); + if(!imghead) goto error_open; + + fread(imghead,sizeof(TPLImgHeader),1,f); + deschead[c].imghead = imghead; + + if(palhead) { + fseek(f,(s32)palhead,SEEK_SET); + + palhead = malloc(sizeof(TPLPalHeader)); + if(!palhead) goto error_open; + + fread(palhead,sizeof(TPLPalHeader),1,f); + deschead[c].palhead = palhead; + } + } + tdf->texdesc = deschead; + + return 1; + } + +error_open: + if(deschead) free(deschead); + if(palhead) free(palhead); + + fclose(f); + return 0; +} + +s32 TPL_OpenTPLFromMemory(TPLFile* tdf, void *memory,u32 len) +{ + u32 c,pos; + const char *p = memory; + TPLDescHeader *deschead = NULL; + TPLImgHeader *imghead = NULL; + TPLPalHeader *palhead = NULL; + + if(!memory || !len) return -1; //TPL_ERR_INVALID + + tdf->type = TPL_FILE_TYPE_MEM; + tdf->tpl_file = (FHANDLE)NULL; + + //version = *(u32*)(p + TPL_HDR_VERSION_FIELD); + tdf->ntextures = *(u32*)(p + TPL_HDR_NTEXTURE_FIELD); + + deschead = (TPLDescHeader*)(p + TPL_HDR_DESCR_FIELD); + for(c=0;cntextures;c++) { + imghead = NULL; + palhead = NULL; + + pos = (u32)deschead[c].imghead; + imghead = (TPLImgHeader*)(p + pos); + + pos = (u32)imghead->data; + imghead->data = (char*)(p + pos); + + pos = (u32)deschead[c].palhead; + if(pos) { + palhead = (TPLPalHeader*)(p + pos); + + pos = (u32)palhead->data; + palhead->data = (char*)(p + pos); + } + deschead[c].imghead = imghead; + deschead[c].palhead = palhead; + } + tdf->texdesc = deschead; + + return 1; +} + +s32 TPL_GetTextureInfo(TPLFile *tdf,s32 id,u32 *fmt,u16 *width,u16 *height) +{ + TPLDescHeader *deschead = NULL; + TPLImgHeader *imghead = NULL; + + if(!tdf) return -1; + if(id<0 || id>=tdf->ntextures) return -1; + + deschead = (TPLDescHeader*)tdf->texdesc; + if(!deschead) return -1; + + imghead = deschead[id].imghead; + if(!imghead) return -1; + + if(fmt) *fmt = imghead->fmt; + if(width) *width = imghead->width; + if(height) *height = imghead->height; + + return 0; +} + +s32 TPL_GetTexture(TPLFile *tdf,s32 id,GXTexObj *texObj) +{ + s32 pos; + u32 size; + FILE *f = NULL; + TPLDescHeader *deschead = NULL; + TPLImgHeader *imghead = NULL; + s32 bMipMap = 0; + u8 biasclamp = GX_DISABLE; + + if(!tdf) return -1; + if(!texObj) return -1; + if(id<0 || id>=tdf->ntextures) return -1; + + deschead = (TPLDescHeader*)tdf->texdesc; + if(!deschead) return -1; + + imghead = deschead[id].imghead; + if(!imghead) return -1; + + size = TPL_GetTextureSize(imghead->width,imghead->height,imghead->fmt); + if(tdf->type==TPL_FILE_TYPE_DISC) { + f = (FILE*)tdf->tpl_file; + pos = (s32)imghead->data; + imghead->data = memalign(PPC_CACHE_ALIGNMENT,size); + if(!imghead->data) return -1; + + fseek(f,pos,SEEK_SET); + fread(imghead->data,1,size,f); + } + + if(imghead->maxlod>0) bMipMap = 1; + if(imghead->lodbias>0.0f) biasclamp = GX_ENABLE; + + DCFlushRange(imghead->data,size); + GX_InitTexObj(texObj,imghead->data,imghead->width,imghead->height,imghead->fmt,imghead->wraps,imghead->wrapt,bMipMap); + if(bMipMap) GX_InitTexObjLOD(texObj,imghead->minfilter,imghead->magfilter,imghead->minlod,imghead->maxlod, + imghead->lodbias,biasclamp,biasclamp,imghead->edgelod); + + return 0; +} + +s32 TPL_GetTextureCI(TPLFile *tdf,s32 id,GXTexObj *texObj,GXTlutObj *tlutObj,u8 tluts) +{ + s32 pos; + u32 size; + FILE *f = NULL; + TPLDescHeader *deschead = NULL; + TPLImgHeader *imghead = NULL; + TPLPalHeader *palhead = NULL; + s32 bMipMap = 0; + u8 biasclamp = GX_DISABLE; + + if(!tdf) return -1; + if(!texObj) return -1; + if(!tlutObj) return -1; + if(id<0 || id>=tdf->ntextures) return -1; + + deschead = (TPLDescHeader*)tdf->texdesc; + if(!deschead) return -1; + + imghead = deschead[id].imghead; + if(!imghead) return -1; + + palhead = deschead[id].palhead; + if(!palhead) return -1; + + size = TPL_GetTextureSize(imghead->width,imghead->height,imghead->fmt); + if(tdf->type==TPL_FILE_TYPE_DISC) { + f = (FILE*)tdf->tpl_file; + pos = (s32)imghead->data; + imghead->data = memalign(PPC_CACHE_ALIGNMENT,size); + if(!imghead->data) return -1; + + fseek(f,pos,SEEK_SET); + fread(imghead->data,1,size,f); + + pos = (s32)palhead->data; + palhead->data = memalign(PPC_CACHE_ALIGNMENT,(palhead->nitems*sizeof(u16))); + if(!palhead->data) { + free(imghead->data); + return -1; + } + + fseek(f,pos,SEEK_SET); + fread(palhead->data,1,(palhead->nitems*sizeof(u16)),f); + } + + if(imghead->maxlod>0) bMipMap = 1; + if(imghead->lodbias>0.0f) biasclamp = GX_ENABLE; + + DCFlushRange(imghead->data,size); + DCFlushRange(palhead->data,(palhead->nitems*sizeof(u16))); + GX_InitTlutObj(tlutObj,palhead->data,palhead->fmt,palhead->nitems); + GX_InitTexObjCI(texObj,imghead->data,imghead->width,imghead->height,imghead->fmt,imghead->wraps,imghead->wrapt,bMipMap,tluts); + if(bMipMap) GX_InitTexObjLOD(texObj,imghead->minfilter,imghead->magfilter,imghead->minlod,imghead->maxlod, + imghead->lodbias,biasclamp,biasclamp,imghead->edgelod); + + return 0; +} + +void TPL_CloseTPLFile(TPLFile *tdf) +{ + int i; + FILE *f; + TPLPalHeader *palhead; + TPLImgHeader *imghead; + TPLDescHeader *deschead; + + if(!tdf) return; + + if(tdf->type==TPL_FILE_TYPE_DISC) { + f = (FILE*)tdf->tpl_file; + if(f) fclose(f); + + deschead = (TPLDescHeader*)tdf->texdesc; + if(!deschead) return; + + for(i=0;intextures;i++) { + imghead = deschead[i].imghead; + palhead = deschead[i].palhead; + if(imghead) { + if(imghead->data) free(imghead->data); + free(imghead); + } + if(palhead) { + if(palhead->data) free(palhead->data); + free(palhead); + } + } + free(tdf->texdesc); + } + + tdf->ntextures = 0; + tdf->texdesc = NULL; + tdf->tpl_file = NULL; +} diff --git a/wii/libogc/libogc/usb.c b/wii/libogc/libogc/usb.c new file mode 100644 index 0000000000..f48c6ff2e4 --- /dev/null +++ b/wii/libogc/libogc/usb.c @@ -0,0 +1,1464 @@ +/*------------------------------------------------------------- + +usb.c -- USB lowlevel + +Copyright (C) 2008 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) +tueidj + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +/* Note: There are 3 types of USB interfaces here, the early ones + * (V0: /dev/usb/oh0 and /dev/usb/oh1) and two later ones (V5: /dev/usb/ven + * and /dev/usb/hid) which are similar but have some small + * differences. There is also an earlier version of /dev/usb/hid (V4) + * found in IOSes 37,61,56,etc. and /dev/usb/msc found in IOS 57. + * These interfaces aren't implemented here and you may find some + * devices don't show up if you're running under those IOSes. + */ + +#if defined(HW_RVL) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usb.h" + +#define USB_HEAPSIZE 16384 + +#define USBV0_IOCTL_CTRLMSG 0 +#define USBV0_IOCTL_BLKMSG 1 +#define USBV0_IOCTL_INTRMSG 2 +#define USBV0_IOCTL_SUSPENDDEV 5 +#define USBV0_IOCTL_RESUMEDEV 6 +#define USBV0_IOCTL_ISOMSG 9 +#define USBV0_IOCTL_GETDEVLIST 12 +#define USBV0_IOCTL_DEVREMOVALHOOK 26 +#define USBV0_IOCTL_DEVINSERTHOOK 27 +#define USBV0_IOCTL_DEVICECLASSCHANGE 28 + +#define USBV4_IOCTL_GETVERSION 6 // returns 0x40001 + +#define USBV5_IOCTL_GETVERSION 0 // should return 0x50001 +#define USBV5_IOCTL_GETDEVICECHANGE 1 +#define USBV5_IOCTL_SHUTDOWN 2 +#define USBV5_IOCTL_GETDEVPARAMS 3 +#define USBV5_IOCTL_ATTACHFINISH 6 +#define USBV5_IOCTL_SETALTERNATE 7 +#define USBV5_IOCTL_SUSPEND_RESUME 16 +#define USBV5_IOCTL_CANCELENDPOINT 17 +#define USBV5_IOCTL_CTRLMSG 18 +#define USBV5_IOCTL_INTRMSG 19 +#define USBV5_IOCTL_ISOMSG 20 +#define USBV5_IOCTL_BULKMSG 21 +#define USBV5_IOCTL_MSC_READWRITE_ASYNC 32 /* unimplemented */ +#define USBV5_IOCTL_MSC_READ_ASYNC 33 /* unimplemented */ +#define USBV5_IOCTL_MSC_WRITE_ASYNC 34 /* unimplemented */ +#define USBV5_IOCTL_MSC_READWRITE 35 /* unimplemented */ +#define USBV5_IOCTL_MSC_RESET 36 /* unimplemented */ + +#define USB_MAX_DEVICES 32 + +static s32 hId = -1; +static const char __oh0_path[] ATTRIBUTE_ALIGN(32) = "/dev/usb/oh0"; +static const char __ven_path[] ATTRIBUTE_ALIGN(32) = "/dev/usb/ven"; +static const char __hid_path[] ATTRIBUTE_ALIGN(32) = "/dev/usb/hid"; + +typedef struct _usb_cb_list { + usbcallback cb; + void *userdata; + union { + s32 device_id; + struct _usb_cb_list *next; + }; +} _usb_cb_list; + +struct _usbv5_host { + usb_device_entry attached_devices[USB_MAX_DEVICES]; + _usb_cb_list remove_cb[USB_MAX_DEVICES]; + s32 fd; + _usb_cb_list *device_change_notify; +}; + +static struct _usbv5_host* ven_host = NULL; +static struct _usbv5_host* hid_host = NULL; + +struct _usb_msg { + s32 fd; + u32 heap_buffers; + union { + struct { + u8 bmRequestType; + u8 bmRequest; + u16 wValue; + u16 wIndex; + u16 wLength; + void *rpData; + } ctrl; + + struct { + void *rpData; + u16 wLength; + u8 pad[4]; + u8 bEndpoint; + } bulk; + + struct { + void *rpData; + u16 wLength; + u8 bEndpoint; + } intr; + + struct { + void *rpData; + void *rpPacketSizes; + u8 bPackets; + u8 bEndpoint; + } iso; + + struct { + u16 pid; + u16 vid; + } notify; + + u8 class; + u32 hid_intr_dir; + + u32 align_pad[4]; // pad to 24 bytes + }; + usbcallback cb; + void *userdata; + ioctlv vec[7]; +}; + +static s32 __usbv5_devicechangeCB(s32 result, void *p); + +static s32 __usbv5_attachfinishCB(s32 result, void *p) +{ + _usb_cb_list *list; + struct _usbv5_host* host = (struct _usbv5_host*)p; + if(host==NULL) return IPC_EINVAL; + + /* the callback functions may attempt to set a new notify func, + * device_change_notify is set to NULL *before* calling it to + * avoid wiping out the new functions + */ + list = host->device_change_notify; + host->device_change_notify = NULL; + while (list) { + _usb_cb_list *next = list->next; + list->cb(result, list->userdata); + iosFree(hId, list); + list = next; + } + + if (result==0) + IOS_IoctlAsync(host->fd, USBV5_IOCTL_GETDEVICECHANGE, NULL, 0, host->attached_devices, 0x180, __usbv5_devicechangeCB, host); + + return IPC_OK; +} + +static s32 __usbv5_devicechangeCB(s32 result, void *p) +{ + int i, j; + struct _usbv5_host* host = (struct _usbv5_host*)p; + + if(host==NULL) return IPC_EINVAL; + + if (result>=0) { + // can't check the remove callbacks only if the number of devices has decreased, + // because devices may have been inserted as well as removed + for (i=0; iremove_cb[i].cb==NULL) + continue; + for (j=0; jremove_cb[i].device_id == host->attached_devices[j].device_id) + break; + } + + if (j==result) { // execute callback and remove it + host->remove_cb[i].cb(0, host->remove_cb[i].userdata); + host->remove_cb[i].cb = NULL; + } + } + // wipe unused device entries + memset(host->attached_devices+result, 0, sizeof(usb_device_entry)*(32-result)); + + IOS_IoctlAsync(host->fd, USBV5_IOCTL_ATTACHFINISH, NULL, 0, NULL, 0, __usbv5_attachfinishCB, host); + } + + return IPC_OK; +} + +static s32 add_devicechange_cb(_usb_cb_list **list, usbcallback cb, void *userdata) +{ + _usb_cb_list *new_cb = (_usb_cb_list*)iosAlloc(hId, sizeof(_usb_cb_list)); + if (new_cb==NULL) + return IPC_ENOMEM; + + new_cb->cb = cb; + new_cb->userdata = userdata; + new_cb->next = *list; + *list = new_cb; + + return IPC_OK; +} + +static s32 __usbv5_messageCB(s32 result,void *_msg) +{ + struct _usb_msg *msg = (struct _usb_msg*)_msg; + + if(msg==NULL) return IPC_EINVAL; + + if(msg->cb!=NULL) msg->cb(result, msg->userdata); + + iosFree(hId,msg); + + return IPC_OK; +} + +static s32 __usbv0_messageCB(s32 result,void *usrdata) +{ + u32 i; + struct _usb_msg *msg = (struct _usb_msg*)usrdata; + + if(msg==NULL) return IPC_EINVAL; + + if(msg->cb!=NULL) msg->cb(result, msg->userdata); + + for(i=0; iheap_buffers; i++) { + if(msg->vec[i].data!=NULL) + iosFree(hId,msg->vec[i].data); + } + + iosFree(hId,msg); + + return IPC_OK; +} + +static s32 __find_device_on_host(struct _usbv5_host *host, s32 device_id) +{ + int i; + if (host==NULL) return -1; + + for (i=0; host->attached_devices[i].device_id; i++) { + if (host->attached_devices[i].device_id == device_id) + return i; + } + + return -1; +} + +static s32 __usb_isochronous_message(s32 device_id,u8 bEndpoint,u8 bPackets,u16* rpPacketSizes,void* rpData,usbcallback cb,void *userdata) +{ + s32 ret = IPC_ENOMEM; + struct _usb_msg *msg; + u16 wLength=0; + u8 i; + + for (i=0; ifd = device_id; + msg->cb = cb; + msg->userdata = userdata; + + if (device_id>=0 && device_id<0x20) { + u8 *pEndp=NULL; + u16 *pLength=NULL; + u8 *pPackets=NULL; + + pEndp = (u8*)iosAlloc(hId,32); + if(pEndp==NULL) goto done; + *pEndp = bEndpoint; + + pLength = (u16*)iosAlloc(hId,32); + if(pLength==NULL) goto done; + // NOT byteswapped! + *pLength = wLength; + + pPackets = (u8*)iosAlloc(hId,32); + if(pPackets==NULL) goto done; + *pPackets = bPackets; + + msg->heap_buffers = 3; + + msg->vec[0].data = pEndp; + msg->vec[0].len = sizeof(u8); + msg->vec[1].data = pLength; + msg->vec[1].len = sizeof(u16); + msg->vec[2].data = pPackets; + msg->vec[2].len = sizeof(u8); + msg->vec[3].data = rpPacketSizes; + msg->vec[3].len = sizeof(u16)*bPackets; + msg->vec[4].data = rpData; + msg->vec[4].len = wLength; + + if (cb==NULL) + ret = IOS_Ioctlv(device_id, USBV0_IOCTL_ISOMSG, 3, 2, msg->vec); + else + return IOS_IoctlvAsync(device_id, USBV0_IOCTL_ISOMSG, 3, 2, msg->vec, __usbv0_messageCB, msg); + +done: + if(pEndp) iosFree(hId,pEndp); + if(pLength) iosFree(hId,pLength); + if(pPackets) iosFree(hId,pPackets); + } else { + u8 endpoint_dir = !!(bEndpoint&USB_ENDPOINT_IN); + s32 fd = (device_id<0) ? ven_host->fd : hid_host->fd; + + msg->iso.rpData = rpData; + msg->iso.rpPacketSizes = rpPacketSizes; + msg->iso.bEndpoint = bEndpoint; + msg->iso.bPackets = bPackets; + + msg->vec[0].data = msg; + msg->vec[0].len = 64; + // block counts are used for both input and output + msg->vec[1].data = msg->vec[3].data = rpPacketSizes; + msg->vec[1].len = msg->vec[3].len = sizeof(u16)*bPackets; + msg->vec[2].data = rpData; + msg->vec[2].len = wLength; + + if (cb==NULL) + ret = IOS_Ioctlv(fd, USBV5_IOCTL_ISOMSG, 2-endpoint_dir, 2+endpoint_dir, msg->vec); + else + return IOS_IoctlvAsync(fd, USBV5_IOCTL_ISOMSG, 2-endpoint_dir, 2+endpoint_dir, msg->vec, __usbv5_messageCB, msg); + } + + if (msg!=NULL) iosFree(hId,msg); + + return ret; +} + +static s32 __usb_control_message(s32 device_id,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData,usbcallback cb,void *userdata) +{ + s32 ret = IPC_ENOMEM; + struct _usb_msg *msg; + + if(((s32)rpData%32)!=0) return IPC_EINVAL; + if(wLength && !rpData) return IPC_EINVAL; + if(!wLength && rpData) return IPC_EINVAL; + + msg = (struct _usb_msg*)iosAlloc(hId,sizeof(struct _usb_msg)); + if(msg==NULL) return IPC_ENOMEM; + + memset(msg, 0, sizeof(struct _usb_msg)); + + msg->fd = device_id; + msg->cb = cb; + msg->userdata = userdata; + + if (device_id>=0 && device_id<0x20) { + u8 *pRqType = NULL,*pRq = NULL,*pNull = NULL; + u16 *pValue = NULL,*pIndex = NULL,*pLength = NULL; + + pRqType = (u8*)iosAlloc(hId,32); + if(pRqType==NULL) goto done; + *pRqType = bmRequestType; + + pRq = (u8*)iosAlloc(hId,32); + if(pRq==NULL) goto done; + *pRq = bmRequest; + + pValue = (u16*)iosAlloc(hId,32); + if(pValue==NULL) goto done; + *pValue = bswap16(wValue); + + pIndex = (u16*)iosAlloc(hId,32); + if(pIndex==NULL) goto done; + *pIndex = bswap16(wIndex); + + pLength = (u16*)iosAlloc(hId,32); + if(pLength==NULL) goto done; + *pLength = bswap16(wLength); + + pNull = (u8*)iosAlloc(hId,32); + if(pNull==NULL) goto done; + *pNull = 0; + + msg->heap_buffers = 6; + + msg->vec[0].data = pRqType; + msg->vec[0].len = sizeof(u8); + msg->vec[1].data = pRq; + msg->vec[1].len = sizeof(u8); + msg->vec[2].data = pValue; + msg->vec[2].len = sizeof(u16); + msg->vec[3].data = pIndex; + msg->vec[3].len = sizeof(u16); + msg->vec[4].data = pLength; + msg->vec[4].len = sizeof(u16); + msg->vec[5].data = pNull; + msg->vec[5].len = sizeof(u8); + msg->vec[6].data = rpData; + msg->vec[6].len = wLength; + + if (cb==NULL) + ret = IOS_Ioctlv(device_id, USBV0_IOCTL_CTRLMSG, 6, 1, msg->vec); + else + return IOS_IoctlvAsync(device_id, USBV0_IOCTL_CTRLMSG, 6, 1, msg->vec, __usbv0_messageCB, msg); + +done: + if(pRqType!=NULL) iosFree(hId,pRqType); + if(pRq!=NULL) iosFree(hId,pRq); + if(pValue!=NULL) iosFree(hId,pValue); + if(pIndex!=NULL) iosFree(hId,pIndex); + if(pLength!=NULL) iosFree(hId,pLength); + if(pNull!=NULL) iosFree(hId,pNull); + + } else { + u8 request_dir = !!(bmRequestType&USB_CTRLTYPE_DIR_DEVICE2HOST); + s32 fd = (device_id<0) ? ven_host->fd : hid_host->fd; + + msg->ctrl.bmRequestType = bmRequestType; + msg->ctrl.bmRequest = bmRequest; + msg->ctrl.wValue = wValue; + msg->ctrl.wIndex = wIndex; + msg->ctrl.wLength = wLength; + msg->ctrl.rpData = rpData; + + msg->vec[0].data = msg; + msg->vec[0].len = 64; + msg->vec[1].data = rpData; + msg->vec[1].len = wLength; + + if (cb==NULL) + ret = IOS_Ioctlv(fd, USBV5_IOCTL_CTRLMSG, 2-request_dir, request_dir, msg->vec); + else + return IOS_IoctlvAsync(fd, USBV5_IOCTL_CTRLMSG, 2-request_dir, request_dir, msg->vec, __usbv5_messageCB, msg); + } + + if(msg!=NULL) iosFree(hId,msg); + + return ret; +} + +static inline s32 __usb_interrupt_bulk_message(s32 device_id,u8 ioctl,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *userdata) +{ + s32 ret = IPC_ENOMEM; + struct _usb_msg *msg; + + if(((s32)rpData%32)!=0) return IPC_EINVAL; + if(wLength && !rpData) return IPC_EINVAL; + if(!wLength && rpData) return IPC_EINVAL; + + msg = (struct _usb_msg*)iosAlloc(hId,sizeof(struct _usb_msg)); + if(msg==NULL) return IPC_ENOMEM; + + memset(msg, 0, sizeof(struct _usb_msg)); + + msg->fd = device_id; + msg->cb = cb; + msg->userdata = userdata; + + if (device_id>=0 && device_id<0x20) { + u8 *pEndP = NULL; + u16 *pLength = NULL; + + pEndP = (u8*)iosAlloc(hId,32); + if(pEndP==NULL) goto done; + *pEndP = bEndpoint; + + pLength = (u16*)iosAlloc(hId,32); + if(pLength==NULL) goto done; + *pLength = wLength; + + msg->vec[0].data = pEndP; + msg->vec[0].len = sizeof(u8); + msg->vec[1].data = pLength; + msg->vec[1].len = sizeof(u16); + msg->vec[2].data = rpData; + msg->vec[2].len = wLength; + + msg->heap_buffers = 2; + + if (cb==NULL) + ret = IOS_Ioctlv(device_id,ioctl,2,1,msg->vec); + else + return IOS_IoctlvAsync(device_id,ioctl,2,1,msg->vec,__usbv0_messageCB,msg); + +done: + if(pEndP!=NULL) iosFree(hId,pEndP); + if(pLength!=NULL) iosFree(hId,pLength); + + } else { + u8 endpoint_dir = !!(bEndpoint&USB_ENDPOINT_IN); + s32 fd = (device_id<0) ? ven_host->fd : hid_host->fd; + + if (ioctl == USBV0_IOCTL_INTRMSG) { + // HID does this a little bit differently + if (device_id>=0) + msg->hid_intr_dir = !endpoint_dir; + else { + msg->intr.rpData = rpData; + msg->intr.wLength = wLength; + msg->intr.bEndpoint = bEndpoint; + } + ioctl = USBV5_IOCTL_INTRMSG; + } else { + msg->bulk.rpData = rpData; + msg->bulk.wLength = wLength; + msg->bulk.bEndpoint = bEndpoint; + ioctl = USBV5_IOCTL_BULKMSG; + } + + msg->vec[0].data = msg; + msg->vec[0].len = 64; + msg->vec[1].data = rpData; + msg->vec[1].len = wLength; + + if (cb==NULL) + ret = IOS_Ioctlv(fd, ioctl, 2-endpoint_dir, endpoint_dir, msg->vec); + else + return IOS_IoctlvAsync(fd, ioctl, 2-endpoint_dir, endpoint_dir, msg->vec, __usbv5_messageCB, msg); + } + + if(msg!=NULL) iosFree(hId,msg); + + return ret; +} + +static inline s32 __usb_getdesc(s32 fd, u8 *buffer, u8 valuehi, u8 valuelo, u16 index, u16 size) +{ + u8 requestType = USB_CTRLTYPE_DIR_DEVICE2HOST; + + if (valuehi==USB_DT_HID || valuehi==USB_DT_REPORT || valuehi==USB_DT_PHYSICAL) + requestType |= USB_CTRLTYPE_REC_INTERFACE; + + return __usb_control_message(fd, requestType, USB_REQ_GETDESCRIPTOR, (valuehi << 8) | valuelo, index, size, buffer, NULL, NULL); +} + +static u32 __find_next_endpoint(u8 *buffer,s32 size,u8 align) +{ + u8 *ptr = buffer; + + while(size>2 && buffer[0]) { // abort if buffer[0]==0 to avoid getting stuck + if(buffer[1]==USB_DT_ENDPOINT || buffer[1]==USB_DT_INTERFACE) + break; + + size -= (buffer[0]+align)&~align; + buffer += (buffer[0]+align)&~align; + } + + return (buffer - ptr); +} + +s32 USB_Initialize() +{ + if(hId==-1) hId = iosCreateHeap(USB_HEAPSIZE); + if(hId<0) return IPC_ENOMEM; + + if (ven_host==NULL) { + s32 ven_fd = IOS_Open(__ven_path, IPC_OPEN_NONE); + if (ven_fd>=0) { + ven_host = (struct _usbv5_host*)iosAlloc(hId, sizeof(*ven_host)); + if (ven_host==NULL) { + IOS_Close(ven_fd); + return IPC_ENOMEM; + } + memset(ven_host, 0, sizeof(*ven_host)); + ven_host->fd = ven_fd; + + u32 *ven_ver = (u32*)iosAlloc(hId, 0x20); + if (ven_ver==NULL) goto mem_error; + if (IOS_Ioctl(ven_fd, USBV5_IOCTL_GETVERSION, NULL, 0, ven_ver, 0x20)==0 && ven_ver[0]==0x50001) + IOS_IoctlAsync(ven_fd, USBV5_IOCTL_GETDEVICECHANGE, NULL, 0, ven_host->attached_devices, 0x180, __usbv5_devicechangeCB, ven_host); + else { + // wrong ven version + IOS_Close(ven_fd); + iosFree(hId, ven_host); + ven_host = NULL; + } + + iosFree(hId, ven_ver); + } + } + + if (hid_host==NULL) { + s32 hid_fd = IOS_Open(__hid_path, IPC_OPEN_NONE); + if (hid_fd>=0) { + hid_host = (struct _usbv5_host*)iosAlloc(hId, sizeof(*hid_host)); + if (hid_host==NULL) { + IOS_Close(hid_fd); + goto mem_error; + } + memset(hid_host, 0, sizeof(*hid_host)); + hid_host->fd = hid_fd; + + u32 *hid_ver = (u32*)iosAlloc(hId, 0x20); + if (hid_ver==NULL) goto mem_error; + // have to call the USB4 version first, to be safe + if (IOS_Ioctl(hid_fd, USBV4_IOCTL_GETVERSION, NULL, 0, NULL, 0)==0x40001 || \ + IOS_Ioctl(hid_fd, USBV5_IOCTL_GETVERSION, NULL, 0, hid_ver, 0x20) || hid_ver[0]!=0x50001) { + // wrong hid version + IOS_Close(hid_fd); + iosFree(hId, hid_host); + hid_host = NULL; + } else + IOS_IoctlAsync(hid_fd, USBV5_IOCTL_GETDEVICECHANGE, NULL, 0, hid_host->attached_devices, 0x180, __usbv5_devicechangeCB, hid_host); + + iosFree(hId, hid_ver); + } + } + + return IPC_OK; + +mem_error: + USB_Deinitialize(); + return IPC_ENOMEM; +} + +s32 USB_Deinitialize() +{ + if (hid_host) { + if (hid_host->fd>=0) { + IOS_Ioctl(hid_host->fd, USBV5_IOCTL_SHUTDOWN, NULL, 0, NULL, 0); + IOS_Close(hid_host->fd); + } + iosFree(hId, hid_host); + hid_host = NULL; + } + + if (ven_host) { + if (ven_host->fd>=0) { + IOS_Ioctl(ven_host->fd, USBV5_IOCTL_SHUTDOWN, NULL, 0, NULL, 0); + IOS_Close(ven_host->fd); + } + iosFree(hId, ven_host); + ven_host = NULL; + } + + return IPC_OK; +} + +s32 USB_OpenDevice(s32 device_id,u16 vid,u16 pid,s32 *fd) +{ + s32 ret = USB_OK; + char *devicepath = NULL; + *fd = -1; + + if (device_id && device_id!=USB_OH1_DEVICE_ID) { + int i; + + i = __find_device_on_host(ven_host, device_id); + if (i>=0) + USB_ResumeDevice(device_id); + else { + // HID V5 devices need their descriptors read before being used + usb_devdesc desc; + i = __find_device_on_host(hid_host, device_id); + if (i>=0) { + USB_ResumeDevice(device_id); + i = USB_GetDescriptors(device_id, &desc); + if (i>=0) + USB_FreeDescriptors(&desc); + else { + USB_SuspendDevice(device_id); + return i; + } + } + } + if (i>=0) { + *fd = device_id; + return 0; + } + } + + devicepath = iosAlloc(hId,USB_MAXPATH); + if(devicepath==NULL) return IPC_ENOMEM; + + if (device_id==USB_OH1_DEVICE_ID) + snprintf(devicepath,USB_MAXPATH,"/dev/usb/oh1/%x/%x",vid,pid); + else + snprintf(devicepath,USB_MAXPATH,"/dev/usb/oh0/%x/%x",vid,pid); + + *fd = IOS_Open(devicepath,0); + if(*fd<0) ret = *fd; + + if (devicepath!=NULL) iosFree(hId,devicepath); + return ret; +} + +s32 USBV5_CloseDevice(s32 device_id) +{ + int i; + struct _usbv5_host* host; + + if (__find_device_on_host(ven_host, device_id)>=0) + host = ven_host; + else if (__find_device_on_host(hid_host, device_id)>=0) + host = hid_host; + else + return IPC_EINVAL; + + for (i=0; i < USB_MAX_DEVICES; i++) { + if (host->remove_cb[i].cb==NULL) + continue; + + if (host->remove_cb[i].device_id==device_id) { + host->remove_cb[i].cb(0, host->remove_cb[i].userdata); + host->remove_cb[i].cb = NULL; + break; + } + } + //return USB_SuspendDevice(device_id); + return 0; +} + +s32 USB_CloseDevice(s32 *fd) +{ + s32 ret = IPC_EINVAL; + if (fd) { + ret = USBV5_CloseDevice(*fd); + if (ret==IPC_EINVAL && *fd>0) + ret = IOS_Close(*fd); + if (ret>=0) *fd = -1; + } + + return ret; +} + +s32 USB_CloseDeviceAsync(s32 *fd,usbcallback cb,void *userdata) +{ + s32 ret = IPC_EINVAL; + if(fd) { + ret = USBV5_CloseDevice(*fd); + if (ret!=IPC_EINVAL) { + if (cb) + return cb(ret, userdata); + else + return ret; + } + if (*fd>0) + return IOS_CloseAsync(*fd,cb,userdata); + } + + return ret; +} + +s32 USB_GetDeviceDescription(s32 fd,usb_devdesc *devdesc) +{ + s32 ret; + usb_devdesc *p; + + p = iosAlloc(hId,USB_DT_DEVICE_SIZE); + if(p==NULL) return IPC_ENOMEM; + + ret = __usb_control_message(fd,USB_CTRLTYPE_DIR_DEVICE2HOST,USB_REQ_GETDESCRIPTOR,(USB_DT_DEVICE<<8),0,USB_DT_DEVICE_SIZE,p,NULL,NULL); + if(ret>=0) memcpy(devdesc,p,USB_DT_DEVICE_SIZE); + devdesc->configurations = NULL; + + if(p!=NULL) iosFree(hId,p); + return ret; +} + +static s32 USBV5_GetDescriptors(s32 device_id, usb_devdesc *udd) +{ + s32 retval = IPC_ENOMEM; + u32 *io_buffer = NULL; + u8 *buffer = NULL; + usb_configurationdesc *ucd = NULL; + usb_interfacedesc *uid = NULL; + usb_endpointdesc *ued = NULL; + u32 iConf, iEndpoint; + s32 fd; + u32 desc_out_size, desc_start_offset; + + if (__find_device_on_host(ven_host, device_id)>=0) { + fd = ven_host->fd; + desc_out_size = 0xC0; + desc_start_offset = 20; + } else if (__find_device_on_host(hid_host, device_id)>=0) { + fd = hid_host->fd; + desc_out_size = 0x60; + desc_start_offset = 36; + } else + return IPC_EINVAL; + + io_buffer = (u32*)iosAlloc(hId, 0x20); + buffer = (u8*)iosAlloc(hId, desc_out_size); + if (io_buffer==NULL || buffer==NULL) goto free_bufs; + + io_buffer[0] = device_id; + io_buffer[2] = 0; + memset(buffer, 0, desc_out_size); + retval = IOS_Ioctl(fd, USBV5_IOCTL_GETDEVPARAMS, io_buffer, 0x20, buffer, desc_out_size); + if (retval==IPC_OK) { + u8 *next = buffer+desc_start_offset; + + memcpy(udd, next, sizeof(*udd)); + udd->configurations = calloc(udd->bNumConfigurations, sizeof(*udd->configurations)); + if(udd->configurations == NULL) goto free_bufs; + + next += (udd->bLength+3)&~3; + for (iConf = 0; iConf < udd->bNumConfigurations; iConf++) + { + ucd = &udd->configurations[iConf]; + memcpy(ucd, next, USB_DT_CONFIG_SIZE); + next += (USB_DT_CONFIG_SIZE+3)&~3; + + // ignore the actual value of bNumInterfaces; IOS presents each interface as a different device + // alternate settings will not show up here, if you want them you must explicitly request + // the interface descriptor + if (ucd->bNumInterfaces==0) + continue; + + ucd->bNumInterfaces = 1; + ucd->interfaces = calloc(1, sizeof(*ucd->interfaces)); + if (ucd->interfaces == NULL) goto free_bufs; + + uid = ucd->interfaces; + memcpy(uid, next, USB_DT_INTERFACE_SIZE); + next += (uid->bLength+3)&~3; + + /* This skips vendor and class specific descriptors */ + uid->extra_size = __find_next_endpoint(next, buffer+desc_out_size-next, 3); + if(uid->extra_size>0) + { + uid->extra = malloc(uid->extra_size); + if(uid->extra == NULL) + goto free_bufs; + memcpy(uid->extra, next, uid->extra_size); + // already rounded + next += uid->extra_size; + } + + if (uid->bNumEndpoints) { + uid->endpoints = calloc(uid->bNumEndpoints, sizeof(*uid->endpoints)); + if (uid->endpoints == NULL) goto free_bufs; + + for(iEndpoint = 0; iEndpoint < uid->bNumEndpoints; iEndpoint++) + { + ued = &uid->endpoints[iEndpoint]; + memcpy(ued, next, USB_DT_ENDPOINT_SIZE); + next += (ued->bLength+3)&~3; + } + } + + } + + retval = IPC_OK; + } + +free_bufs: + if (io_buffer!=NULL) + iosFree(hId, io_buffer); + if (buffer!=NULL) + iosFree(hId, buffer); + if (retval<0) + USB_FreeDescriptors(udd); + return retval; +} + +s32 USB_GetDescriptors(s32 fd, usb_devdesc *udd) +{ + u8 *buffer = NULL; + u8 *ptr = NULL; + usb_configurationdesc *ucd = NULL; + usb_interfacedesc *uid = NULL; + usb_endpointdesc *ued = NULL; + s32 retval = 0; + u32 size; + u32 iConf, iInterface, iEndpoint; + + if (udd==NULL) + return IPC_EINVAL; + memset(udd, 0, sizeof(*udd)); + + if (fd>=0x20 || fd<-1) + return USBV5_GetDescriptors(fd, udd); + + buffer = iosAlloc(hId, sizeof(*udd)); + if(buffer == NULL) + { + retval = IPC_ENOHEAP; + goto free_and_error; + } + + retval = __usb_getdesc(fd, buffer, USB_DT_DEVICE, 0, 0, USB_DT_DEVICE_SIZE); + if(retval < 0) + goto free_and_error; + memcpy(udd, buffer, USB_DT_DEVICE_SIZE); + iosFree(hId, buffer); + + udd->bcdUSB = bswap16(udd->bcdUSB); + udd->idVendor = bswap16(udd->idVendor); + udd->idProduct = bswap16(udd->idProduct); + udd->bcdDevice = bswap16(udd->bcdDevice); + + udd->configurations = calloc(udd->bNumConfigurations, sizeof(*udd->configurations)); + if(udd->configurations == NULL) + { + retval = IPC_ENOMEM; + goto free_and_error; + } + for(iConf = 0; iConf < udd->bNumConfigurations; iConf++) + { + buffer = iosAlloc(hId, USB_DT_CONFIG_SIZE); + if(buffer == NULL) + { + retval = IPC_ENOHEAP; + goto free_and_error; + } + + retval = __usb_getdesc(fd, buffer, USB_DT_CONFIG, iConf, 0, USB_DT_CONFIG_SIZE); + if (retval < 0) + goto free_and_error; + + ucd = &udd->configurations[iConf]; + memcpy(ucd, buffer, USB_DT_CONFIG_SIZE); + iosFree(hId, buffer); + + ucd->wTotalLength = bswap16(ucd->wTotalLength); + size = ucd->wTotalLength; + buffer = iosAlloc(hId, size); + if(buffer == NULL) + { + retval = IPC_ENOHEAP; + goto free_and_error; + } + + retval = __usb_getdesc(fd, buffer, USB_DT_CONFIG, iConf, 0, ucd->wTotalLength); + if(retval < 0) + goto free_and_error; + + ptr = buffer; + ptr += ucd->bLength; + size -= ucd->bLength; + + retval = IPC_ENOMEM; + + ucd->interfaces = calloc(ucd->bNumInterfaces, sizeof(*ucd->interfaces)); + if(ucd->interfaces == NULL) + goto free_and_error; + for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++) + { + retval = __find_next_endpoint(ptr, size, 0); + if (retval>0) { + // FIXME: do something with this data + } + ptr += retval; + size -= retval; + + uid = ucd->interfaces+iInterface; + memcpy(uid, ptr, USB_DT_INTERFACE_SIZE); + ptr += uid->bLength; + size -= uid->bLength; + + /* This skips vendor and class specific descriptors */ + uid->extra_size = __find_next_endpoint(ptr, size, 0); + if(uid->extra_size>0) + { + uid->extra = malloc(uid->extra_size); + if(uid->extra == NULL) + goto free_and_error; + memcpy(uid->extra, ptr, uid->extra_size); + ptr += uid->extra_size; + size -= uid->extra_size; + } + + if (uid->bNumEndpoints) { + uid->endpoints = calloc(uid->bNumEndpoints, sizeof(*uid->endpoints)); + if(uid->endpoints == NULL) + goto free_and_error; + + for(iEndpoint = 0; iEndpoint < uid->bNumEndpoints; iEndpoint++) + { + ued = &uid->endpoints[iEndpoint]; + memcpy(ued, ptr, USB_DT_ENDPOINT_SIZE); + ptr += ued->bLength; + size -= ued->bLength; + ued->wMaxPacketSize = bswap16(ued->wMaxPacketSize); + } + } + + if (iInterface==(ucd->bNumInterfaces-1) && size>2) { + // we've read all the interfaces but there's data left (probably alternate setting interfaces) + // see if we can find another interface descriptor + retval = __find_next_endpoint(ptr, size, 0); + if (size-retval >= USB_DT_INTERFACE_SIZE && ptr[retval+1] == USB_DT_INTERFACE) { + // found alternates, make room and loop + usb_interfacedesc *interfaces = realloc(ucd->interfaces, (iInterface+2)*sizeof(*interfaces)); + if (interfaces == NULL) + goto free_and_error; + interfaces[iInterface+1].endpoints = NULL; + interfaces[iInterface+1].extra = NULL; + ucd->bNumInterfaces++; + ucd->interfaces = interfaces; + } + } + } + iosFree(hId, buffer); + buffer = NULL; + } + retval = IPC_OK; + +free_and_error: + if(buffer != NULL) + iosFree(hId, buffer); + if(retval < 0) + USB_FreeDescriptors(udd); + return retval; +} + +s32 USB_GetGenericDescriptor(s32 fd,u8 type,u8 index,u8 interface,void *data,u32 size) { + u8 *buffer; + s32 retval; + + buffer = iosAlloc(hId,size); + if(buffer==NULL) { + retval = IPC_ENOMEM; + goto free_and_error; + } + + retval = __usb_getdesc(fd,buffer,type,index,interface,size); + if(retval<0) goto free_and_error; + + memcpy(data,buffer,size); + retval = IPC_OK; + +free_and_error: + if(buffer!=NULL) iosFree(hId,buffer); + return retval; +} + +s32 USB_GetHIDDescriptor(s32 fd,u8 interface,usb_hiddesc *uhd,u32 size) +{ + int i; + s32 retval; + + if (size < USB_DT_HID_SIZE) + return IPC_EINVAL; + + retval = USB_GetGenericDescriptor(fd, USB_DT_HID, 0, interface, uhd, size); + if (retval != IPC_OK) + return retval; + + uhd->bcdHID = bswap16(uhd->bcdHID); + uhd->descr[0].wDescriptorLength = bswap16(uhd->descr[0].wDescriptorLength); + size -= USB_DT_HID_SIZE; + for (i=1; ibNumDescriptors && size>=3; size -=3, i++) + uhd->descr[i].wDescriptorLength = bswap16(uhd->descr[i].wDescriptorLength); + + return retval; +} + +void USB_FreeDescriptors(usb_devdesc *udd) +{ + int iConf, iInterface; + usb_configurationdesc *ucd; + usb_interfacedesc *uid; + if(udd->configurations != NULL) + { + for(iConf = 0; iConf < udd->bNumConfigurations; iConf++) + { + ucd = &udd->configurations[iConf]; + if(ucd->interfaces != NULL) + { + for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++) + { + uid = ucd->interfaces+iInterface; + free(uid->endpoints); + free(uid->extra); + } + free(ucd->interfaces); + } + } + free(udd->configurations); + } +} + +s32 USB_GetAsciiString(s32 fd,u8 bIndex,u16 wLangID,u16 wLength,void *rpData) +{ + s32 ret; + u8 bo, ro; + u8 *buf; + u8 *rp = (u8 *)rpData; + + if(wLength > 255) + wLength = 255; + + buf = iosAlloc(hId, 255); /* 255 is the highest possible length of a descriptor */ + if(buf == NULL) + return IPC_ENOMEM; + + ret = __usb_getdesc(fd, buf, USB_DT_STRING, bIndex, wLangID, 255); + + /* index 0 gets a list of supported languages */ + if(bIndex == 0) + { + if(ret > 0) + memcpy(rpData, buf, wLength); + iosFree(hId, buf); + return ret; + } + + if(ret > 0) + { + bo = 2; + ro = 0; + while(ro < (wLength - 1) && bo < buf[0]) + { + if(buf[bo + 1]) + rp[ro++] = '?'; + else + rp[ro++] = buf[bo]; + bo += 2; + } + rp[ro] = 0; + ret = ro - 1; + } + + iosFree(hId, buf); + return ret; +} + +s32 USB_ReadIsoMsg(s32 fd,u8 bEndpoint,u8 bPackets,u16 *rpPacketSizes,void *rpData) +{ + return __usb_isochronous_message(fd,bEndpoint,bPackets,rpPacketSizes,rpData,NULL,NULL); +} + +s32 USB_ReadIsoMsgAsync(s32 fd,u8 bEndpoint,u8 bPackets,u16 *rpPacketSizes,void *rpData,usbcallback cb,void *userdata) +{ + return __usb_isochronous_message(fd,bEndpoint,bPackets,rpPacketSizes,rpData,cb,userdata); +} + +s32 USB_WriteIsoMsg(s32 fd,u8 bEndpoint,u8 bPackets,u16 *rpPacketSizes,void *rpData) +{ + return __usb_isochronous_message(fd,bEndpoint,bPackets,rpPacketSizes,rpData,NULL,NULL); +} + +s32 USB_WriteIsoMsgAsync(s32 fd,u8 bEndpoint,u8 bPackets,u16 *rpPacketSizes,void *rpData,usbcallback cb,void *userdata) +{ + return __usb_isochronous_message(fd,bEndpoint,bPackets,rpPacketSizes,rpData,cb,userdata); +} + +s32 USB_ReadIntrMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData) +{ + return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_INTRMSG,bEndpoint,wLength,rpData,NULL,NULL); +} + +s32 USB_ReadIntrMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *userdata) +{ + return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_INTRMSG,bEndpoint,wLength,rpData,cb,userdata); +} + +s32 USB_WriteIntrMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData) +{ + return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_INTRMSG,bEndpoint,wLength,rpData,NULL,NULL); +} + +s32 USB_WriteIntrMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *userdata) +{ + return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_INTRMSG,bEndpoint,wLength,rpData,cb,userdata); +} + +s32 USB_ReadBlkMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData) +{ + return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_BLKMSG,bEndpoint,wLength,rpData,NULL,NULL); +} + +s32 USB_ReadBlkMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *userdata) +{ + return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_BLKMSG,bEndpoint,wLength,rpData,cb,userdata); +} + +s32 USB_WriteBlkMsg(s32 fd,u8 bEndpoint,u16 wLength,void *rpData) +{ + return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_BLKMSG,bEndpoint,wLength,rpData,NULL,NULL); +} + +s32 USB_WriteBlkMsgAsync(s32 fd,u8 bEndpoint,u16 wLength,void *rpData,usbcallback cb,void *userdata) +{ + return __usb_interrupt_bulk_message(fd,USBV0_IOCTL_BLKMSG,bEndpoint,wLength,rpData,cb,userdata); +} + +s32 USB_ReadCtrlMsg(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData) +{ + return __usb_control_message(fd,bmRequestType,bmRequest,wValue,wIndex,wLength,rpData,NULL,NULL); +} + +s32 USB_ReadCtrlMsgAsync(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData,usbcallback cb,void *userdata) +{ + return __usb_control_message(fd,bmRequestType,bmRequest,wValue,wIndex,wLength,rpData,cb,userdata); +} + +s32 USB_WriteCtrlMsg(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData) +{ + return __usb_control_message(fd,bmRequestType,bmRequest,wValue,wIndex,wLength,rpData,NULL,NULL); +} + +s32 USB_WriteCtrlMsgAsync(s32 fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData,usbcallback cb,void *userdata) +{ + return __usb_control_message(fd,bmRequestType,bmRequest,wValue,wIndex,wLength,rpData,cb,userdata); +} + +static s32 USB5_RegisterDeviceRemoval(struct _usbv5_host *host, s32 device_id, usbcallback cb, void *userdata) +{ + int i; + + // check to make sure the device is present + if (__find_device_on_host(host, device_id)<0) + return IPC_ENOENT; + + // now make sure it's not hooked already + for (i=0; iremove_cb[i].cb && host->remove_cb[i].device_id==device_id) + return IPC_EINVAL; + } + + // find a free entry and add it + for (i=0; iremove_cb[i].cb==NULL) { + host->remove_cb[i].cb = cb; + host->remove_cb[i].userdata = userdata; + host->remove_cb[i].device_id = device_id; + return IPC_OK; + } + } + return IPC_EINVAL; +} + +s32 USB_DeviceRemovalNotifyAsync(s32 fd,usbcallback cb,void *userdata) +{ + s32 ret; + if (fd>=0 && fd<0x20) + return IOS_IoctlAsync(fd,USBV0_IOCTL_DEVREMOVALHOOK,NULL,0,NULL,0,cb,userdata); + + ret = USB5_RegisterDeviceRemoval(ven_host, fd, cb, userdata); + if (ret == IPC_ENOENT) + ret = USB5_RegisterDeviceRemoval(hid_host, fd, cb, userdata); + + return ret; +} + +static s32 USBV5_SuspendResume(s32 device_id, s32 resumed) +{ + s32 ret; + s32 fd; + + if (__find_device_on_host(ven_host, device_id)>=0) + fd = ven_host->fd; + else if (__find_device_on_host(hid_host, device_id)>=0) + fd = hid_host->fd; + else + return IPC_ENOENT; + + s32 *buf = (s32*)iosAlloc(hId, 32); + if (buf==NULL) return IPC_ENOMEM; + + buf[0] = device_id; + buf[2] = resumed; + ret = IOS_Ioctl(fd, USBV5_IOCTL_SUSPEND_RESUME, buf, 32, NULL, 0); + iosFree(hId, buf); + + return ret; +} + +s32 USB_SuspendDevice(s32 fd) +{ + if (fd>=0x20 || fd<-1) + return USBV5_SuspendResume(fd, 0); + + return IOS_Ioctl(fd,USBV0_IOCTL_SUSPENDDEV,NULL,0,NULL,0); +} + +s32 USB_ResumeDevice(s32 fd) +{ + if (fd>=0x20 || fd<-1) + return USBV5_SuspendResume(fd, 1); + + return IOS_Ioctl(fd,USBV0_IOCTL_RESUMEDEV,NULL,0,NULL,0); +} + +s32 USB_DeviceChangeNotifyAsync(u8 interface_class, usbcallback cb, void* userdata) +{ + s32 ret=IPC_ENOENT; + + if (ven_host==NULL) { + s32 fd; + struct _usb_msg *msg; + + fd = IOS_Open(__oh0_path,IPC_OPEN_NONE); + if (fd<0) return fd; + + msg = iosAlloc(hId,sizeof(*msg)); + if (msg==NULL) { + IOS_Close(fd); + return IPC_ENOMEM; + } + + msg->cb = cb; + msg->userdata = userdata; + msg->class = interface_class; + + msg->vec[0].data = &msg->class; + msg->vec[0].len = 1; + + ret = IOS_IoctlvAsync(fd,USBV0_IOCTL_DEVICECLASSCHANGE,1,0,msg->vec,__usbv5_messageCB,msg); + IOS_Close(fd); + + if (ret<0) iosFree(hId, msg); + } + else if (interface_class != USB_CLASS_HID && ven_host) + ret = add_devicechange_cb(&ven_host->device_change_notify, cb, userdata); + + else if (interface_class==USB_CLASS_HID && hid_host) + ret = add_devicechange_cb(&hid_host->device_change_notify, cb, userdata); + + + return ret; +} + +s32 USB_GetDeviceList(usb_device_entry *descr_buffer,u8 num_descr,u8 interface_class,u8 *cnt_descr) +{ + int i; + u8 cntdevs=0; + + if (ven_host==NULL) { + s32 fd; + u32 *buf = (u32*)iosAlloc(hId, num_descr<<3); + if (buf==NULL) return IPC_ENOMEM; + + fd = IOS_Open(__oh0_path,IPC_OPEN_NONE); + if (fd<0) { + iosFree(hId, buf); + return fd; + } + + cntdevs = 0; + i = IOS_IoctlvFormat(hId,fd,USBV0_IOCTL_GETDEVLIST,"bb:dd",num_descr,interface_class,&cntdevs,sizeof(cntdevs),buf,(num_descr<<3)); + if (cnt_descr) *cnt_descr = cntdevs; + + while (cntdevs--) { + descr_buffer[cntdevs].device_id = 0; + descr_buffer[cntdevs].vid = (u16)(buf[cntdevs*2+1]>>16); + descr_buffer[cntdevs].pid = (u16)buf[cntdevs*2+1]; + } + + IOS_Close(fd); + iosFree(hId, buf); + return i; + } + + // for ven_host, we can only exclude usb_hid class devices + if (interface_class != USB_CLASS_HID && ven_host) { + i=0; + while (cntdevsattached_devices[i].device_id) { + descr_buffer[cntdevs++] = ven_host->attached_devices[i++]; + if (i>=32) break; + } + } + + if ((!interface_class || interface_class==USB_CLASS_HID) && hid_host) { + i=0; + while (cntdevsattached_devices[i].device_id) { + descr_buffer[cntdevs++] = hid_host->attached_devices[i++]; + if (i>32) break; + } + } + + if (cnt_descr) *cnt_descr = cntdevs; + + return IPC_OK; +} + +s32 USB_SetConfiguration(s32 fd, u8 configuration) +{ + return __usb_control_message(fd, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_DEVICE), USB_REQ_SETCONFIG, configuration, 0, 0, NULL, NULL, NULL); +} + +s32 USB_GetConfiguration(s32 fd, u8 *configuration) +{ + u8 *_configuration; + s32 retval; + + _configuration = iosAlloc(hId, 1); + if(_configuration == NULL) + return IPC_ENOMEM; + + retval = __usb_control_message(fd, (USB_CTRLTYPE_DIR_DEVICE2HOST | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_DEVICE), USB_REQ_GETCONFIG, 0, 0, 1, _configuration, NULL, NULL); + if(retval >= 0) + *configuration = *_configuration; + iosFree(hId, _configuration); + + return retval; +} + +s32 USB_SetAlternativeInterface(s32 fd, u8 interface, u8 alternateSetting) +{ + return __usb_control_message(fd, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_INTERFACE), USB_REQ_SETINTERFACE, alternateSetting, interface, 0, NULL, NULL, NULL); +} + +static s32 USBV5_CancelEndpoint(s32 device_id, u8 endpoint) +{ + s32 ret; + s32 fd; + + if (__find_device_on_host(ven_host, device_id)>=0) + fd = ven_host->fd; + else if (__find_device_on_host(hid_host, device_id)>=0) + fd = hid_host->fd; + else + return IPC_ENOENT; + + s32 *buf = (s32*)iosAlloc(hId, 32); + if (buf==NULL) return IPC_ENOMEM; + + buf[0] = device_id; + buf[2] = endpoint; + ret = IOS_Ioctl(fd, USBV5_IOCTL_CANCELENDPOINT, buf, 32, NULL, 0); + iosFree(hId, buf); + + return ret; +} + +s32 USB_ClearHalt(s32 fd, u8 endpoint) +{ + if (fd>=0x20 || fd<-1) + return USBV5_CancelEndpoint(fd, endpoint); + return __usb_control_message(fd, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_ENDPOINT), USB_REQ_CLEARFEATURE, USB_FEATURE_ENDPOINT_HALT, endpoint, 0, NULL, NULL, NULL); +} + +#endif /* defined(HW_RVL) */ + diff --git a/wii/libogc/libogc/usbgecko.c b/wii/libogc/libogc/usbgecko.c new file mode 100644 index 0000000000..c00630a207 --- /dev/null +++ b/wii/libogc/libogc/usbgecko.c @@ -0,0 +1,374 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usbgecko.h" + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + + +static u32 usbgecko_inited = 0; +static lwpq_t wait_exi_queue[2]; + +static s32 __usbgecko_exi_unlock(s32 chan,s32 dev) +{ + LWP_ThreadBroadcast(wait_exi_queue[chan]); + return 1; +} + +static void __usbgecko_init() +{ + u32 i; + + for(i=0;i 0x7FFFF) + return 0; + + if(!EXI_Select(chn,EXI_DEVICE_0,EXI_SPEED16MHZ)) ret |= 0x01; + if(!EXI_ImmEx(chn,&val,sizeof(u32),EXI_WRITE)) ret |= 0x02; + if(!EXI_ImmEx(chn,&val,sizeof(u32),EXI_WRITE)) ret |= 0x04; + if(!EXI_Deselect(chn)) ret |= 0x08; + + if(ret) return 0; + return 1; +} + +static __inline__ int __flashreadcommand(s32 chn, u32 flashaddress, u8 *flashdata) +{ + s32 ret = 0; + u32 val = 0xF0000000|(flashaddress<<9); + + if (flashaddress > 0x7FFFF) + return 0; + + if(!EXI_Select(chn,EXI_DEVICE_0,EXI_SPEED16MHZ)) ret |= 0x01; + if(!EXI_ImmEx(chn,&val,sizeof(u32),EXI_WRITE)) ret |= 0x02; + if(!EXI_ImmEx(chn,&val,sizeof(u32),EXI_READ)) ret |= 0x04; + if(!EXI_Deselect(chn)) ret |= 0x08; + + if(ret) return 0; + *flashdata = val>>23; + return 1; +} + +static int __usb_sendbyte(s32 chn,char ch) +{ + s32 ret; + u16 val; + + val = (0xB000|_SHIFTL(ch,4,8)); + ret = __send_command(chn,&val); + if(ret==1 && !(val&0x0400)) ret = 0; + + return ret; +} + +static int __usb_recvbyte(s32 chn,char *ch) +{ + s32 ret; + u16 val; + + *ch = 0; + val = 0xA000; + ret = __send_command(chn,&val); + if(ret==1 && !(val&0x0800)) ret = 0; + else if(ret==1) *ch = (val&0xff); + + return ret; +} + +int __usb_checksend(s32 chn) +{ + s32 ret; + u16 val; + + val = 0xC000; + ret = __send_command(chn,&val); + if(ret==1 && !(val&0x0400)) ret = 0; + + return ret; +} + +int __usb_checkrecv(s32 chn) +{ + s32 ret; + u16 val; + + val = 0xD000; + ret = __send_command(chn,&val); + if(ret==1 && !(val&0x0400)) ret = 0; + + return ret; +} + +void usb_flush(s32 chn) +{ + char tmp; + + __usbgecko_exi_wait(chn); + while(__usb_recvbyte(chn,&tmp)); + EXI_Unlock(chn); +} + +int usb_isgeckoalive(s32 chn) +{ + u32 id = 0; + s32 ret; + u16 val; + + if (EXI_GetID(chn, EXI_DEVICE_0, &id) == 0) + return 0; + + if (id != 0) + return 0; + + __usbgecko_exi_wait(chn); + + val = 0x9000; + ret = __send_command(chn,&val); + if(ret==1 && !(val&0x0470)) ret = 0; + + EXI_Unlock(chn); + + return ret; +} + +int usb_recvbuffer_ex(s32 chn,void *buffer,int size, int retries) +{ + s32 ret; + s32 left = size; + char *ptr = (char*)buffer; + + __usbgecko_exi_wait(chn); + while(left>0) { + ret = __usb_recvbyte(chn,ptr); + if(ret==0) break; + + ptr++; + left--; + + if (retries >= 0) { + retries--; + if (retries == 0) + break; + } + } + EXI_Unlock(chn); + + return (size - left); +} + +int usb_recvbuffer(s32 chn,void *buffer,int size) { + return usb_recvbuffer_ex(chn, buffer, size, -1); +} + +int usb_sendbuffer_ex(s32 chn,const void *buffer,int size, int retries) +{ + s32 ret; + s32 left = size; + char *ptr = (char*)buffer; + + __usbgecko_exi_wait(chn); + while(left>0) { + ret = __usb_sendbyte(chn,*ptr); + if(ret==0) break; + + ptr++; + left--; + + if (retries >= 0) { + retries--; + if (retries == 0) + break; + } + } + EXI_Unlock(chn); + + return (size - left); +} + +int usb_sendbuffer(s32 chn,const void *buffer,int size) { + return usb_sendbuffer_ex(chn, buffer, size, -1); +} + +int usb_recvbuffer_safe_ex(s32 chn,void *buffer,int size, int retries) +{ + s32 ret; + s32 left = size; + char *ptr = (char*)buffer; + + __usbgecko_exi_wait(chn); + while(left>0) { + if(__usb_checkrecv(chn)) { + ret = __usb_recvbyte(chn,ptr); + if(ret==0) break; + + ptr++; + left--; + } + + if (retries >= 0) { + retries--; + if (retries == 0) + break; + } + } + EXI_Unlock(chn); + + return (size - left); +} + +int usb_recvbuffer_safe(s32 chn,void *buffer,int size) { + return usb_recvbuffer_safe_ex(chn, buffer, size, -1); +} + +int usb_sendbuffer_safe_ex(s32 chn,const void *buffer,int size, int retries) +{ + s32 ret; + s32 left = size; + char *ptr = (char*)buffer; + + __usbgecko_exi_wait(chn); + while(left>0) { + if(__usb_checksend(chn)) { + ret = __usb_sendbyte(chn,*ptr); + if(ret==0) break; + + ptr++; + left--; + } + + if (retries >= 0) { + retries--; + if (retries == 0) + break; + } + } + EXI_Unlock(chn); + + return (size - left); +} + +int usb_sendbuffer_safe(s32 chn,const void *buffer,int size) { + return usb_sendbuffer_safe_ex(chn, buffer, size, -1); +} + +static int __flashsoftwareid_entry(s32 chn) +{ + s32 ret=0; + + if (__flashwritecommand(chn, 0x5555, 0xAA) && __flashwritecommand(chn, 0x2AAA, 0x55) && + __flashwritecommand(chn, 0x5555, 0x90)) + ret = 1; + + return ret; +} + +static int __flashsoftwareid_exit(s32 chn) +{ + s32 ret=0; + + if (__flashwritecommand(chn, 0x5555, 0xAA) && __flashwritecommand(chn, 0x2AAA, 0x55) && + __flashwritecommand(chn, 0x5555, 0xF0)) + ret = 1; + + return ret; +} + +int usb_flashread(s32 chn, u32 offset, void *buffer, size_t length) +{ + s32 ret=1; + u8 *data = (u8*)buffer; + + __usbgecko_exi_wait(chn); + while (ret && length--) + ret = __flashreadcommand(chn, offset++, data++); + + EXI_Unlock(chn); + + return ret; +} + +int usb_flashwrite(s32 chn, u32 offset, const void *buffer, size_t length) +{ + s32 ret=1; + const u8 *data = (const u8*)buffer; + u8 verify; + + __usbgecko_exi_wait(chn); + while (ret && length--) + { + if (!__flashwritecommand(chn, 0x5555, 0xAA) || !__flashwritecommand(chn, 0x2AAA, 0x55) || + !__flashwritecommand(chn, 0x5555, 0xA0) || !__flashwritecommand(chn, offset, *data)) + ret = 0; + else + { + usleep(20); + if (!__flashreadcommand(chn, offset++, &verify) || verify != *data++) + ret = 0; + } + } + EXI_Unlock(chn); + + return ret; +} + +int usb_flashverify(s32 chn) +{ + u8 id[2]; + s32 ret=0; + + __usbgecko_exi_wait(chn); + + if (__flashsoftwareid_entry(chn) &&__flashreadcommand(chn, 0, id+0) && + __flashreadcommand(chn, 1, id+1) && id[0] == 0xBF && id[1] == 0xD7 && + __flashsoftwareid_exit(chn)) + ret = 1; + + EXI_Unlock(chn); + + return ret; +} diff --git a/wii/libogc/libogc/usbmouse.c b/wii/libogc/libogc/usbmouse.c new file mode 100644 index 0000000000..6ab1055d5c --- /dev/null +++ b/wii/libogc/libogc/usbmouse.c @@ -0,0 +1,407 @@ +/*------------------------------------------------------------- + +usbmouse.c -- USB mouse support + +Copyright (C) 2009 +Daryl Borth (Tantric) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#if defined(HW_RVL) + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define MOUSE_THREAD_STACKSIZE (1024 * 4) +#define MOUSE_THREAD_PRIO 65 + +#define MOUSE_MAX_DATA 32 + +#define HEAP_SIZE 4096 +#define DEVLIST_MAXSIZE 8 + +typedef struct { + lwp_node node; + mouse_event event; +} _node; + +struct umouse { + bool connected; + bool has_wheel; + + s32 fd; + + u8 configuration; + u32 interface; + u32 altInterface; + + u8 ep; + u32 ep_size; +}; + +static lwp_queue _queue; + +static s32 hId = -1; +static bool _mouse_is_inited = false; +static lwp_t _mouse_thread = LWP_THREAD_NULL; +static bool _mouse_thread_running = false; +static bool _mouse_thread_quit = false; +static struct umouse *_mouse = NULL; +static s8 *_mousedata = NULL; +static sem_t _mousesema = LWP_SEM_NULL; + +static u8 _mouse_stack[MOUSE_THREAD_STACKSIZE] ATTRIBUTE_ALIGN(8); + +//Add an event to the event queue +static s32 _mouse_addEvent(const mouse_event *event) { + _node *n = malloc(sizeof(_node)); + n->event = *event; + + __lwp_queue_append(&_queue, (lwp_node*) n); + + return 1; +} + +// Event callback +static s32 _mouse_event_cb(s32 result, mouse_event *event) +{ + if (result>=3) + { + event->button = _mousedata[0]; + event->rx = _mousedata[1]; + event->ry = _mousedata[2]; + // this isn't defined in the HID mouse boot protocol, but it's a fairly safe bet + if (_mouse->has_wheel) { + // if more than 3 bytes were returned and the fourth byte is 1 or -1, assume it's wheel motion + // if it's outside this range, probably not a wheel + if (result < 4 || _mousedata[3] < -1 || _mousedata[3] > 1) { + _mouse->has_wheel = false; + event->rz = 0; + } + else + event->rz = _mousedata[3]; + } else + event->rz = 0; + } + else + _mouse->connected = false; + + if (_mousesema != LWP_SEM_NULL) + LWP_SemPost(_mousesema); + + return 0; +} + +//Callback when the mouse is disconnected +static s32 _disconnect(s32 retval, void *data) +{ + _mouse->connected = false; + if (_mousesema != LWP_SEM_NULL) + LWP_SemPost(_mousesema); + return 1; +} + +//Callback when a device is connected/disconnected (for notification when we're looking for a mouse) +static s32 _device_change(s32 retval, void *data) +{ + if (_mousesema != LWP_SEM_NULL) + LWP_SemPost(_mousesema); + return 1; +} + +//init the ioheap +static s32 USBMouse_Initialize(void) +{ + if (hId > 0) + return 0; + + hId = iosCreateHeap(HEAP_SIZE); + + if (hId < 0) + return IPC_ENOHEAP; + + return IPC_OK; +} + +//Close the device +static void USBMouse_Close(void) +{ + if (_mouse && _mouse->fd != -1) { + USB_CloseDevice(&_mouse->fd); + _mouse->fd = -1; + } +} + +//Search for a mouse connected to the wii usb port +//Thanks to Sven Peter usbstorage support +static s32 USBMouse_Open() +{ + usb_device_entry *buffer; + u8 device_count, i; + u16 vid, pid; + bool found = false; + u32 iConf, iInterface, iEp; + usb_devdesc udd; + usb_configurationdesc *ucd; + usb_interfacedesc *uid; + usb_endpointdesc *ued; + + buffer = iosAlloc(hId, DEVLIST_MAXSIZE * sizeof(usb_device_entry)); + if(buffer == NULL) + return -1; + + memset(buffer, 0, DEVLIST_MAXSIZE * sizeof(usb_device_entry)); + + if (USB_GetDeviceList(buffer, DEVLIST_MAXSIZE, USB_CLASS_HID, &device_count) < 0) + { + iosFree(hId,buffer); + return -2; + } + + for (i = 0; i < device_count; i++) + { + vid = buffer[i].vid; + pid = buffer[i].pid; + + if ((vid == 0) || (pid == 0)) + continue; + + s32 fd = 0; + if (USB_OpenDevice(buffer[i].device_id, vid, pid, &fd) < 0) + continue; + + if (USB_GetDescriptors(fd, &udd) < 0) { + USB_CloseDevice(&fd); + continue; + } + + for(iConf = 0; iConf < udd.bNumConfigurations; iConf++) + { + ucd = &udd.configurations[iConf]; + + for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++) + { + uid = &ucd->interfaces[iInterface]; + + if ((uid->bInterfaceClass == USB_CLASS_HID) && + (uid->bInterfaceSubClass == USB_SUBCLASS_BOOT) && + (uid->bInterfaceProtocol== USB_PROTOCOL_MOUSE)) + { + for(iEp = 0; iEp < uid->bNumEndpoints; iEp++) + { + ued = &uid->endpoints[iEp]; + + if (ued->bmAttributes != USB_ENDPOINT_INTERRUPT) + continue; + + if (!(ued->bEndpointAddress & USB_ENDPOINT_IN)) + continue; + + if (ued->wMaxPacketSize > MOUSE_MAX_DATA) + continue; + + _mouse->fd = fd; + + _mouse->configuration = ucd->bConfigurationValue; + _mouse->interface = uid->bInterfaceNumber; + _mouse->altInterface = uid->bAlternateSetting; + + _mouse->ep = ued->bEndpointAddress; + _mouse->ep_size = ued->wMaxPacketSize; + + found = true; + + break; + } + } + + if (found) + break; + } + + if (found) + break; + } + + USB_FreeDescriptors(&udd); + + if (found) + break; + else + USB_CloseDevice(&fd); + } + + iosFree(hId,buffer); + + if (!found) + return -3; + + if (USB_DeviceRemovalNotifyAsync(_mouse->fd, &_disconnect, NULL) < 0) + { + USBMouse_Close(); + return -8; + } + + // set boot protocol + USB_WriteCtrlMsg(_mouse->fd, USB_REQTYPE_INTERFACE_SET, USB_REQ_SETPROTOCOL, 0, _mouse->interface, 0, NULL); + // assume there's a wheel until we know otherwise + _mouse->has_wheel = true; + _mouse->connected = true; + return 1; +} + +bool MOUSE_IsConnected(void) +{ + if (!_mouse) return false; + return _mouse->connected; +} + +static void * _mouse_thread_func(void *arg) +{ + mouse_event event; + memset(&event, 0, sizeof(event)); + + while (!_mouse_thread_quit) + { + // scan for new attached mice + if (!MOUSE_IsConnected()) + { + USBMouse_Close(); + if (USBMouse_Open() < 0) { + // wait for something to be inserted + USB_DeviceChangeNotifyAsync(USB_CLASS_HID, _device_change, NULL); + LWP_SemWait(_mousesema); + continue; + } + } + + if (USB_ReadIntrMsgAsync(_mouse->fd, _mouse->ep, _mouse->ep_size, _mousedata, (usbcallback)_mouse_event_cb, &event) < 0) + break; + LWP_SemWait(_mousesema); + _mouse_addEvent(&event); + memset(&event, 0, sizeof(event)); + } + return NULL; +} + +//Initialize USB and USB_MOUSE and the event queue +s32 MOUSE_Init(void) +{ + if(_mouse_is_inited) return 0; + + if (USB_Initialize() != IPC_OK) + return -1; + + if (USBMouse_Initialize() != IPC_OK) { + return -2; + } + + _mousedata = (s8*)iosAlloc(hId,MOUSE_MAX_DATA); + _mouse = (struct umouse *) malloc(sizeof(struct umouse)); + memset(_mouse, 0, sizeof(struct umouse)); + _mouse->fd = -1; + LWP_SemInit(&_mousesema, 0, 1); + + if (!_mouse_thread_running) + { + // start the mouse thread + _mouse_thread_quit = false; + memset(_mouse_stack, 0, MOUSE_THREAD_STACKSIZE); + + s32 res = LWP_CreateThread(&_mouse_thread, _mouse_thread_func, NULL, + _mouse_stack, MOUSE_THREAD_STACKSIZE, + MOUSE_THREAD_PRIO); + + if (res) + { + USBMouse_Close(); + MOUSE_FlushEvents(); + _mouse_thread_running = false; + return -6; + } + _mouse_thread_running = true; + } + + __lwp_queue_init_empty(&_queue); + _mouse_is_inited = true; + return 0; +} + +// Deinitialize USB_MOUSE and the event queue +s32 MOUSE_Deinit(void) +{ + if(!_mouse_is_inited) return 1; + + if (_mouse_thread_running) { + _mouse_thread_quit = true; + LWP_SemPost(_mousesema); + LWP_JoinThread(_mouse_thread, NULL); + _mouse_thread_running = false; + } + + USBMouse_Close(); + MOUSE_FlushEvents(); + if(_mousedata!=NULL) iosFree(hId,_mousedata); + free(_mouse); + if (_mousesema != LWP_SEM_NULL) { + LWP_SemDestroy(_mousesema); + _mousesema = LWP_SEM_NULL; + } + _mouse_is_inited = false; + return 1; +} + +//Get the first event of the event queue +s32 MOUSE_GetEvent(mouse_event *event) +{ + _node *n = (_node *) __lwp_queue_get(&_queue); + + if (!n) + return 0; + + if (event) + *event = n->event; + + free(n); + + return 1; +} + +//Flush all pending events +s32 MOUSE_FlushEvents(void) +{ + s32 res=0; + + while (MOUSE_GetEvent(NULL)) + res++; + + return res; +} + +#endif diff --git a/wii/libogc/libogc/usbstorage.c b/wii/libogc/libogc/usbstorage.c new file mode 100644 index 0000000000..d53effbcf6 --- /dev/null +++ b/wii/libogc/libogc/usbstorage.c @@ -0,0 +1,1008 @@ +/*------------------------------------------------------------- + +usbstorage.c -- Bulk-only USB mass storage support + +Copyright (C) 2008 +Sven Peter (svpe) +Copyright (C) 2009-2010 +tueidj, rodries, Tantric + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ +#if defined(HW_RVL) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asm.h" +#include "processor.h" +#include "disc_io.h" +#include "lwp_watchdog.h" + +#define ROUNDDOWN32(v) (((u32)(v)-0x1f)&~0x1f) + +#define HEAP_SIZE (18*1024) +#define TAG_START 0x0BADC0DE + +#define CBW_SIZE 31 +#define CBW_SIGNATURE 0x43425355 +#define CBW_IN (1 << 7) +#define CBW_OUT 0 + +#define CSW_SIZE 13 +#define CSW_SIGNATURE 0x53425355 + +#define SCSI_TEST_UNIT_READY 0x00 +#define SCSI_REQUEST_SENSE 0x03 +#define SCSI_INQUIRY 0x12 +#define SCSI_START_STOP 0x1B +#define SCSI_READ_CAPACITY 0x25 +#define SCSI_READ_10 0x28 +#define SCSI_WRITE_10 0x2A + +#define SCSI_SENSE_REPLY_SIZE 18 +#define SCSI_SENSE_NOT_READY 0x02 +#define SCSI_SENSE_MEDIUM_ERROR 0x03 +#define SCSI_SENSE_HARDWARE_ERROR 0x04 + +#define USB_CLASS_MASS_STORAGE 0x08 +#define MASS_STORAGE_RBC_COMMANDS 0x01 +#define MASS_STORAGE_ATA_COMMANDS 0x02 +#define MASS_STORAGE_QIC_COMMANDS 0x03 +#define MASS_STORAGE_UFI_COMMANDS 0x04 +#define MASS_STORAGE_SFF8070_COMMANDS 0x05 +#define MASS_STORAGE_SCSI_COMMANDS 0x06 +#define MASS_STORAGE_BULK_ONLY 0x50 + +#define USBSTORAGE_GET_MAX_LUN 0xFE +#define USBSTORAGE_RESET 0xFF + +#define USB_ENDPOINT_BULK 0x02 + +#define USBSTORAGE_CYCLE_RETRIES 3 +#define USBSTORAGE_TIMEOUT 2 + +#define INVALID_LUN -2 + +#define MAX_TRANSFER_SIZE_V0 4096 +#define MAX_TRANSFER_SIZE_V5 (16*1024) + +#define DEVLIST_MAXSIZE 8 + +static heap_cntrl __heap; +static bool __inited = false; +static u64 usb_last_used = 0; +static lwpq_t __usbstorage_waitq = 0; +static u32 usbtimeout = USBSTORAGE_TIMEOUT; + +/* +The following is for implementing a DISC_INTERFACE +as used by libfat +*/ + +static usbstorage_handle __usbfd; +static u8 __lun = 0; +static bool __mounted = false; +static u16 __vid = 0; +static u16 __pid = 0; +static bool usb2_mode=true; + +static s32 __usbstorage_reset(usbstorage_handle *dev); +static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun); +s32 USBStorage_Inquiry(usbstorage_handle *dev, u8 lun); + +/* XXX: this is a *really* dirty and ugly way to send a bulkmessage with a timeout + * but there's currently no other known way of doing this and it's in my humble + * opinion still better than having a function blocking forever while waiting + * for the USB data/IOS reply.. + */ + +static s32 __usb_blkmsg_cb(s32 retval, void *dummy) +{ + usbstorage_handle *dev = (usbstorage_handle *)dummy; + dev->retval = retval; + SYS_CancelAlarm(dev->alarm); + LWP_ThreadBroadcast(__usbstorage_waitq); + return 0; +} + +static s32 __usb_deviceremoved_cb(s32 retval,void *arg) +{ + __mounted = false; + return 0; +} + +static void __usb_timeouthandler(syswd_t alarm,void *cbarg) +{ + usbstorage_handle *dev = (usbstorage_handle*)cbarg; + dev->retval = USBSTORAGE_ETIMEDOUT; + LWP_ThreadBroadcast(__usbstorage_waitq); +} + +static void __usb_settimeout(usbstorage_handle *dev, u32 secs) +{ + struct timespec ts; + + ts.tv_sec = secs; + ts.tv_nsec = 0; + SYS_SetAlarm(dev->alarm,&ts,__usb_timeouthandler,dev); +} + +static s32 __USB_BlkMsgTimeout(usbstorage_handle *dev, u8 bEndpoint, u16 wLength, void *rpData, u32 timeout) +{ + s32 retval; + + dev->retval = USBSTORAGE_PROCESSING; + retval = USB_WriteBlkMsgAsync(dev->usb_fd, bEndpoint, wLength, rpData, __usb_blkmsg_cb, (void *)dev); + if(retval < 0) return retval; + + __usb_settimeout(dev, timeout); + + do { + retval = dev->retval; + if(retval!=USBSTORAGE_PROCESSING) break; + else LWP_ThreadSleep(__usbstorage_waitq); + } while(retval==USBSTORAGE_PROCESSING); + + if (retval<0) + USB_ClearHalt(dev->usb_fd, bEndpoint); + + return retval; +} + +static s32 __USB_CtrlMsgTimeout(usbstorage_handle *dev, u8 bmRequestType, u8 bmRequest, u16 wValue, u16 wIndex, u16 wLength, void *rpData) +{ + s32 retval; + + dev->retval = USBSTORAGE_PROCESSING; + retval = USB_WriteCtrlMsgAsync(dev->usb_fd, bmRequestType, bmRequest, wValue, wIndex, wLength, rpData, __usb_blkmsg_cb, (void *)dev); + if(retval < 0) return retval; + + __usb_settimeout(dev, usbtimeout); + + do { + retval = dev->retval; + if(retval!=USBSTORAGE_PROCESSING) break; + else LWP_ThreadSleep(__usbstorage_waitq); + } while(retval==USBSTORAGE_PROCESSING); + + return retval; +} + +static u8 *arena_ptr=NULL; +static u8 *cbw_buffer=NULL; + +s32 USBStorage_Initialize() +{ + u32 level; + + if(__inited) + return IPC_OK; + + _CPU_ISR_Disable(level); + LWP_InitQueue(&__usbstorage_waitq); + if(!arena_ptr) { + arena_ptr = (u8*)ROUNDDOWN32(((u32)SYS_GetArena2Hi() - HEAP_SIZE)); + if((u32)arena_ptr < (u32)SYS_GetArena2Lo()) { + _CPU_ISR_Restore(level); + return IPC_ENOMEM; + } + SYS_SetArena2Hi(arena_ptr); + } + __lwp_heap_init(&__heap, arena_ptr, HEAP_SIZE, 32); + cbw_buffer=(u8*)__lwp_heap_allocate(&__heap, 32); + __inited = true; + _CPU_ISR_Restore(level); + return IPC_OK; +} + +static s32 __send_cbw(usbstorage_handle *dev, u8 lun, u32 len, u8 flags, const u8 *cb, u8 cbLen) +{ + s32 retval = USBSTORAGE_OK; + + if(cbLen == 0 || cbLen > 16) + return IPC_EINVAL; + + memset(cbw_buffer, 0, CBW_SIZE); + + __stwbrx(cbw_buffer, 0, CBW_SIGNATURE); + __stwbrx(cbw_buffer, 4, ++dev->tag); + __stwbrx(cbw_buffer, 8, len); + cbw_buffer[12] = flags; + cbw_buffer[13] = lun; + cbw_buffer[14] = (cbLen > 6 ? 10 : 6); + + memcpy(cbw_buffer + 15, cb, cbLen); + + if(dev->suspended == 1) + { + USB_ResumeDevice(dev->usb_fd); + dev->suspended = 0; + } + + retval = __USB_BlkMsgTimeout(dev, dev->ep_out, CBW_SIZE, (void *)cbw_buffer, usbtimeout); + + if(retval == CBW_SIZE) return USBSTORAGE_OK; + else if(retval > 0) return USBSTORAGE_ESHORTWRITE; + + return retval; +} + +static s32 __read_csw(usbstorage_handle *dev, u8 *status, u32 *dataResidue, u32 timeout) +{ + s32 retval = USBSTORAGE_OK; + u32 signature, tag, _dataResidue, _status; + + memset(cbw_buffer, 0, CSW_SIZE); + + retval = __USB_BlkMsgTimeout(dev, dev->ep_in, CSW_SIZE, cbw_buffer, timeout); + if(retval > 0 && retval != CSW_SIZE) return USBSTORAGE_ESHORTREAD; + else if(retval < 0) return retval; + + signature = __lwbrx(cbw_buffer, 0); + tag = __lwbrx(cbw_buffer, 4); + _dataResidue = __lwbrx(cbw_buffer, 8); + _status = cbw_buffer[12]; + + if(signature != CSW_SIGNATURE) return USBSTORAGE_ESIGNATURE; + + if(dataResidue != NULL) + *dataResidue = _dataResidue; + if(status != NULL) + *status = _status; + + if(tag != dev->tag) return USBSTORAGE_ETAG; + + return USBSTORAGE_OK; +} + +static s32 __cycle(usbstorage_handle *dev, u8 lun, u8 *buffer, u32 len, u8 *cb, u8 cbLen, u8 write, u8 *_status, u32 *_dataResidue) +{ + s32 retval = USBSTORAGE_OK; + + u8 status=0; + u32 dataResidue = 0; + u16 max_size; + u8 ep = write ? dev->ep_out : dev->ep_in; + s8 retries = USBSTORAGE_CYCLE_RETRIES + 1; + + if(usb2_mode) + max_size=MAX_TRANSFER_SIZE_V5; + else + max_size=MAX_TRANSFER_SIZE_V0; + + LWP_MutexLock(dev->lock); + do + { + u8 *_buffer = buffer; + u32 _len = len; + retries--; + + if(retval == USBSTORAGE_ETIMEDOUT) + break; + + retval = __send_cbw(dev, lun, len, (write ? CBW_OUT:CBW_IN), cb, cbLen); + + while(_len > 0 && retval >= 0) + { + u32 thisLen = _len > max_size ? max_size : _len; + + if ((u32)_buffer&0x1F || !((u32)_buffer&0x10000000)) { + if (write) memcpy(dev->buffer, _buffer, thisLen); + retval = __USB_BlkMsgTimeout(dev, ep, thisLen, dev->buffer, usbtimeout); + if (!write && retval > 0) + memcpy(_buffer, dev->buffer, retval); + } else + retval = __USB_BlkMsgTimeout(dev, ep, thisLen, _buffer, usbtimeout); + + if (retval == thisLen) { + _len -= retval; + _buffer += retval; + } + else if (retval != USBSTORAGE_ETIMEDOUT) + retval = USBSTORAGE_EDATARESIDUE; + } + + if (retval >= 0) + retval = __read_csw(dev, &status, &dataResidue, usbtimeout); + + if (retval < 0) { + if (__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT) + retval = USBSTORAGE_ETIMEDOUT; + } + } while (retval < 0 && retries > 0); + + LWP_MutexUnlock(dev->lock); + + if(_status != NULL) + *_status = status; + if(_dataResidue != NULL) + *_dataResidue = dataResidue; + + return retval; +} + +static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun) +{ + s32 retval; + u8 cmd[6]; + u8 sense[SCSI_SENSE_REPLY_SIZE]; + u8 status = 0; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = SCSI_TEST_UNIT_READY; + + retval = __cycle(dev, lun, NULL, 0, cmd, 1, 0, &status, NULL); + if (retval < 0) return retval; + + if (status) + { + cmd[0] = SCSI_REQUEST_SENSE; + cmd[1] = lun << 5; + cmd[4] = SCSI_SENSE_REPLY_SIZE; + memset(sense, 0, SCSI_SENSE_REPLY_SIZE); + retval = __cycle(dev, lun, sense, SCSI_SENSE_REPLY_SIZE, cmd, 6, 0, NULL, NULL); + if (retval>=0) { + switch (sense[2]&0xF) { + case SCSI_SENSE_NOT_READY: + return USBSTORAGE_EINIT; + case SCSI_SENSE_MEDIUM_ERROR: + case SCSI_SENSE_HARDWARE_ERROR: + return USBSTORAGE_ESENSE; + } + } + } + + return retval; +} + +static s32 __usbstorage_reset(usbstorage_handle *dev) +{ + u32 t = usbtimeout; + usbtimeout = 1; + s32 retval = __USB_CtrlMsgTimeout(dev, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE), USBSTORAGE_RESET, 0, dev->interface, 0, NULL); + usbtimeout = t; + usleep(60*1000); + USB_ClearHalt(dev->usb_fd, dev->ep_in);usleep(10000); //from http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf + USB_ClearHalt(dev->usb_fd, dev->ep_out);usleep(10000); + return retval; +} + +s32 USBStorage_Open(usbstorage_handle *dev, s32 device_id, u16 vid, u16 pid) +{ + s32 retval = -1; + u8 conf = -1; + u8 *max_lun; + u32 iConf, iInterface, iEp; + usb_devdesc udd; + usb_configurationdesc *ucd; + usb_interfacedesc *uid; + usb_endpointdesc *ued; + + max_lun = __lwp_heap_allocate(&__heap, 1); + if (!max_lun) + return IPC_ENOMEM; + + memset(dev, 0, sizeof(*dev)); + dev->usb_fd = -1; + + dev->tag = TAG_START; + + if (LWP_MutexInit(&dev->lock, false) < 0) + goto free_and_return; + + if (SYS_CreateAlarm(&dev->alarm) < 0) + goto free_and_return; + + retval = USB_OpenDevice(device_id, vid, pid, &dev->usb_fd); + if (retval < 0) + goto free_and_return; + + retval = USB_GetDescriptors(dev->usb_fd, &udd); + if (retval < 0) + goto free_and_return; + + for (iConf = 0; iConf < udd.bNumConfigurations; iConf++) { + ucd = &udd.configurations[iConf]; + for (iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++) { + uid = &ucd->interfaces[iInterface]; + if(uid->bInterfaceClass == USB_CLASS_MASS_STORAGE && /* + (uid->bInterfaceSubClass == MASS_STORAGE_SCSI_COMMANDS + || uid->bInterfaceSubClass == MASS_STORAGE_RBC_COMMANDS + || uid->bInterfaceSubClass == MASS_STORAGE_ATA_COMMANDS + || uid->bInterfaceSubClass == MASS_STORAGE_QIC_COMMANDS + || uid->bInterfaceSubClass == MASS_STORAGE_UFI_COMMANDS + || uid->bInterfaceSubClass == MASS_STORAGE_SFF8070_COMMANDS) &&*/ + uid->bInterfaceProtocol == MASS_STORAGE_BULK_ONLY) + { + + if (uid->bNumEndpoints < 2) + continue; + + dev->ep_in = dev->ep_out = 0; + for (iEp = 0; iEp < uid->bNumEndpoints; iEp++) { + ued = &uid->endpoints[iEp]; + if (ued->bmAttributes != USB_ENDPOINT_BULK) + continue; + + if (ued->bEndpointAddress & USB_ENDPOINT_IN) { + dev->ep_in = ued->bEndpointAddress; + } + else { + dev->ep_out = ued->bEndpointAddress; + if(ued->wMaxPacketSize > 64 && (dev->usb_fd>=0x20 || dev->usb_fd<-1)) + usb2_mode=true; + else + usb2_mode=false; + } + } + + if (dev->ep_in != 0 && dev->ep_out != 0) { + dev->configuration = ucd->bConfigurationValue; + dev->interface = uid->bInterfaceNumber; + dev->altInterface = uid->bAlternateSetting; + goto found; + } + } + } + } + + USB_FreeDescriptors(&udd); + retval = USBSTORAGE_ENOINTERFACE; + goto free_and_return; + +found: + dev->bInterfaceSubClass = uid->bInterfaceSubClass; + + USB_FreeDescriptors(&udd); + + retval = USBSTORAGE_EINIT; + // some devices return an error, ignore it + USB_GetConfiguration(dev->usb_fd, &conf); + + if (conf != dev->configuration) USB_SetConfiguration(dev->usb_fd, dev->configuration); + if (dev->altInterface !=0) USB_SetAlternativeInterface(dev->usb_fd, dev->interface, dev->altInterface); + + if(!usb2_mode) + retval = USBStorage_Reset(dev); + + dev->suspended = 0; + + LWP_MutexLock(dev->lock); + retval = __USB_CtrlMsgTimeout(dev, (USB_CTRLTYPE_DIR_DEVICE2HOST | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE), USBSTORAGE_GET_MAX_LUN, 0, dev->interface, 1, max_lun); + LWP_MutexUnlock(dev->lock); + + if (retval < 0) + dev->max_lun = 1; + else + dev->max_lun = *max_lun + 1; + + if (retval == USBSTORAGE_ETIMEDOUT) + goto free_and_return; + + retval = USBSTORAGE_OK; + dev->sector_size = (u32 *) calloc(dev->max_lun, sizeof(u32)); + if(!dev->sector_size) { + retval = IPC_ENOMEM; + goto free_and_return; + } + + /* taken from linux usbstorage module (drivers/usb/storage/transport.c) + * + * Some devices (i.e. Iomega Zip100) need this -- apparently + * the bulk pipes get STALLed when the GetMaxLUN request is + * processed. This is, in theory, harmless to all other devices + * (regardless of if they stall or not). + * + * 8/9/10: If anyone wants to actually use a Zip100, they can add this back. + * But for now, it seems to be breaking things more than it is helping. + */ + //USB_ClearHalt(dev->usb_fd, dev->ep_in); + //USB_ClearHalt(dev->usb_fd, dev->ep_out); + + if(!dev->buffer) + dev->buffer = __lwp_heap_allocate(&__heap, MAX_TRANSFER_SIZE_V5); + + if(!dev->buffer) { + retval = IPC_ENOMEM; + } else { + USB_DeviceRemovalNotifyAsync(dev->usb_fd,__usb_deviceremoved_cb,dev); + retval = USBSTORAGE_OK; + } + +free_and_return: + if (max_lun) + __lwp_heap_free(&__heap, max_lun); + + if (retval < 0) { + USBStorage_Close(dev); + return retval; + } + + return 0; +} + +s32 USBStorage_Close(usbstorage_handle *dev) +{ + __mounted = false; + __lun = 0; + __vid = 0; + __pid = 0; + + if (dev->usb_fd != -1) + USB_CloseDevice(&dev->usb_fd); + + LWP_MutexDestroy(dev->lock); + SYS_RemoveAlarm(dev->alarm); + + if(dev->sector_size) + free(dev->sector_size); + + if (dev->buffer) + __lwp_heap_free(&__heap, dev->buffer); + + memset(dev, 0, sizeof(*dev)); + dev->usb_fd = -1; + return 0; +} + +s32 USBStorage_Reset(usbstorage_handle *dev) +{ + s32 retval; + + LWP_MutexLock(dev->lock); + retval = __usbstorage_reset(dev); + LWP_MutexUnlock(dev->lock); + + return retval; +} + +s32 USBStorage_GetMaxLUN(usbstorage_handle *dev) +{ + return dev->max_lun; +} + +s32 USBStorage_MountLUN(usbstorage_handle *dev, u8 lun) +{ + s32 retval; + u32 n_sectors; + + if(lun >= dev->max_lun) + return IPC_EINVAL; + + usleep(50); + retval = __usbstorage_clearerrors(dev, lun); + if (retval<0) + { + USBStorage_Reset(dev); + retval = __usbstorage_clearerrors(dev, lun); + } + + retval = USBStorage_Inquiry(dev, lun); + + retval = USBStorage_ReadCapacity(dev, lun, &dev->sector_size[lun], &n_sectors); + if(retval >= 0 && (dev->sector_size[lun]<512 || n_sectors==0)) + return INVALID_LUN; + + return retval; +} + +s32 USBStorage_Inquiry(usbstorage_handle *dev, u8 lun) +{ + int n; + s32 retval; + u8 cmd[] = {SCSI_INQUIRY, lun << 5,0,0,36,0}; + u8 response[36]; + + for(n=0;n<2;n++) + { + memset(response,0,36); + + retval = __cycle(dev, lun, response, 36, cmd, 6, 0, NULL, NULL); + if(retval>=0) break; + } + + if(retval>=0) retval=*response & 31; + /* + if(retval>=0) + { + switch(retval) + { + // info from http://en.wikipedia.org/wiki/SCSI_Peripheral_Device_Type + case 5: // CDROM + case 7: // optical memory device (e.g., some optical disks) + __dvd_mounted = 1; + break; + default: + __dvd_mounted = 0; + break; + } + } + */ + return retval; +} + +s32 USBStorage_ReadCapacity(usbstorage_handle *dev, u8 lun, u32 *sector_size, u32 *n_sectors) +{ + s32 retval; + u8 cmd[10] = {SCSI_READ_CAPACITY, lun<<5}; + u8 response[8]; + + retval = __cycle(dev, lun, response, sizeof(response), cmd, sizeof(cmd), 0, NULL, NULL); + if(retval >= 0) + { + if(n_sectors != NULL) + memcpy(n_sectors, response, 4); + if(sector_size != NULL) + memcpy(sector_size, response + 4, 4); + retval = USBSTORAGE_OK; + } + + return retval; +} + +s32 USBStorage_IsDVD() +{ + u32 sectorsize, numSectors; + + if(!__mounted || __usbfd.sector_size[__lun] != 2048) + return 0; + + if(USBStorage_ReadCapacity(&__usbfd, __lun, §orsize, &numSectors) < 0) + return 0; + + if(sectorsize == 2048) + return 1; + return 0; +} + +/* lo_ej = load/eject, controls the tray +* start = start(1) or stop(0) the motor (or eject(0), load(1)) +* imm = return before the command has completed +* it might be a good idea to call this before STM_ShutdownToStandby() so the USB HDD doesn't stay on +*/ +s32 USBStorage_StartStop(usbstorage_handle *dev, u8 lun, u8 lo_ej, u8 start, u8 imm) +{ + u8 status = 0; + s32 retval = USBSTORAGE_OK; + u8 cmd[] = { + SCSI_START_STOP, + (lun << 5) | (imm&1), + 0, + 0, + ((lo_ej&1)<<1) | (start&1), + 0 + }; + + if(lun >= dev->max_lun) + return IPC_EINVAL; + + LWP_MutexLock(dev->lock); + + retval = __send_cbw(dev, lun, 0, CBW_IN, cmd, sizeof(cmd)); + + // if imm==0, wait up to 10secs for spinup to finish + if (retval >= 0) + retval = __read_csw(dev, &status, NULL, (imm ? USBSTORAGE_TIMEOUT : 10)); + + LWP_MutexUnlock(dev->lock); + + if(retval >=0 && status != 0) + retval = USBSTORAGE_ESTATUS; + + return retval; +} + +s32 USBStorage_Read(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, u8 *buffer) +{ + u8 status = 0; + s32 retval; + u8 cmd[] = { + SCSI_READ_10, + lun << 5, + sector >> 24, + sector >> 16, + sector >> 8, + sector, + 0, + n_sectors >> 8, + n_sectors, + 0 + }; + + if(lun >= dev->max_lun || dev->sector_size[lun] == 0) + return IPC_EINVAL; + + // more than 60s since last use - make sure drive is awake + if(ticks_to_secs(gettime() - usb_last_used) > 60) + { + usbtimeout = 10; + USBStorage_MountLUN(dev, lun); + } + + retval = __cycle(dev, lun, buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 0, &status, NULL); + if(retval > 0 && status != 0) + retval = USBSTORAGE_ESTATUS; + + usb_last_used = gettime(); + usbtimeout = USBSTORAGE_TIMEOUT; + + return retval; +} + +s32 USBStorage_Write(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, const u8 *buffer) +{ + u8 status = 0; + s32 retval; + u8 cmd[] = { + SCSI_WRITE_10, + lun << 5, + sector >> 24, + sector >> 16, + sector >> 8, + sector, + 0, + n_sectors >> 8, + n_sectors, + 0 + }; + + if(lun >= dev->max_lun || dev->sector_size[lun] == 0) + return IPC_EINVAL; + + // more than 60s since last use - make sure drive is awake + if(ticks_to_secs(gettime() - usb_last_used) > 60) + { + usbtimeout = 10; + USBStorage_MountLUN(dev, lun); + } + + retval = __cycle(dev, lun, (u8 *)buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 1, &status, NULL); + if(retval > 0 && status != 0) + retval = USBSTORAGE_ESTATUS; + + usb_last_used = gettime(); + usbtimeout = USBSTORAGE_TIMEOUT; + + return retval; +} + +s32 USBStorage_Suspend(usbstorage_handle *dev) +{ + if(dev->suspended == 1) + return USBSTORAGE_OK; + + USB_SuspendDevice(dev->usb_fd); + dev->suspended = 1; + + return USBSTORAGE_OK; +} + +/* +The following is for implementing a DISC_INTERFACE +as used by libfat +*/ + +static bool __usbstorage_Startup(void) +{ + if(USB_Initialize() < 0 || USBStorage_Initialize() < 0) + return false; + + return true; +} + +static bool __usbstorage_IsInserted(void) +{ + usb_device_entry *buffer; + u8 device_count; + u8 i, j; + u16 vid, pid; + s32 maxLun; + s32 retval; + u32 sectorsize, numSectors; + + if(__mounted) + { + // device is not a USB DVD drive - always return true + if (__usbfd.sector_size[__lun] != 2048) + return true; + + // check if DVD is inserted + if (USBStorage_ReadCapacity(&__usbfd, __lun, §orsize, &numSectors) < 0) + return false; + else + return true; + } + + if(!__inited) + return false; + + buffer = (usb_device_entry*)__lwp_heap_allocate(&__heap, DEVLIST_MAXSIZE * sizeof(usb_device_entry)); + if (!buffer) + return false; + + memset(buffer, 0, DEVLIST_MAXSIZE * sizeof(usb_device_entry)); + + if (USB_GetDeviceList(buffer, DEVLIST_MAXSIZE, USB_CLASS_MASS_STORAGE, &device_count) < 0) + { + if (__vid != 0 || __pid != 0) + USBStorage_Close(&__usbfd); + + __lwp_heap_free(&__heap, buffer); + return false; + } + + usleep(100); + + if (__vid != 0 || __pid != 0) { + for(i = 0; i < device_count; i++) { + vid = buffer[i].vid; + pid = buffer[i].pid; + if(vid != 0 || pid != 0) { + if((vid == __vid) && (pid == __pid)) { + __mounted = true; + __lwp_heap_free(&__heap,buffer); + usleep(50); // I don't know why I have to wait but it's needed + return true; + } + } + } + USBStorage_Close(&__usbfd); // device changed or unplugged, return false the first time to notify to the client that he must unmount devices + __lwp_heap_free(&__heap,buffer); + return false; + } + for (i = 0; i < device_count; i++) { + vid = buffer[i].vid; + pid = buffer[i].pid; + if (vid == 0 || pid == 0) + continue; + + if (vid == 0x0b95 && pid == 0x7720) // USB LAN + continue; + + if (USBStorage_Open(&__usbfd, buffer[i].device_id, vid, pid) < 0) + continue; + + maxLun = USBStorage_GetMaxLUN(&__usbfd); + for (j = 0; j < maxLun; j++) { + retval = USBStorage_MountLUN(&__usbfd, j); + + if (retval == INVALID_LUN) + continue; + + if (retval < 0) + { + __usbstorage_reset(&__usbfd); + continue; + } + + __mounted = true; + __lun = j; + __vid = vid; + __pid = pid; + usb_last_used = gettime()-secs_to_ticks(100); + usleep(10000); + break; + } + + if (__mounted) + break; + + USBStorage_Close(&__usbfd); + } + __lwp_heap_free(&__heap, buffer); + + return __mounted; +} + +static bool __usbstorage_ReadSectors(u32 sector, u32 numSectors, void *buffer) +{ + s32 retval; + + if (!__mounted) + return false; + + retval = USBStorage_Read(&__usbfd, __lun, sector, numSectors, buffer); + + return retval >= 0; +} + +static bool __usbstorage_WriteSectors(u32 sector, u32 numSectors, const void *buffer) +{ + s32 retval; + + if (!__mounted) + return false; + + retval = USBStorage_Write(&__usbfd, __lun, sector, numSectors, buffer); + + return retval >= 0; +} + +static bool __usbstorage_ClearStatus(void) +{ + return true; +} + +static bool __usbstorage_Shutdown(void) +{ + if (__vid != 0 || __pid != 0) + USBStorage_Close(&__usbfd); + + return true; +} + +void USBStorage_Deinitialize() +{ + __usbstorage_Shutdown(); + LWP_CloseQueue(__usbstorage_waitq); + __inited = false; +} + +s32 USBStorage_ioctl(int request, ...) +{ + int retval = 0; + va_list ap; + + if(!__mounted) + return -1; + + va_start(ap, request); + + switch (request) + { + case B_RAW_DEVICE_COMMAND: + { + u8 write; + raw_device_command *rdc = va_arg(ap, raw_device_command *); + write = (rdc->flags == B_RAW_DEVICE_DATA_IN) ? 0 : 1; + retval = __cycle(&__usbfd, __lun, rdc->data, rdc->data_length, rdc->command, rdc->command_length, write, &rdc->scsi_status, NULL); + break; + } + + default: + retval = -1; + break; + } + + va_end(ap); + return retval; +} + +DISC_INTERFACE __io_usbstorage = { + DEVICE_TYPE_WII_USB, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB, + (FN_MEDIUM_STARTUP)&__usbstorage_Startup, + (FN_MEDIUM_ISINSERTED)&__usbstorage_IsInserted, + (FN_MEDIUM_READSECTORS)&__usbstorage_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&__usbstorage_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&__usbstorage_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&__usbstorage_Shutdown +}; + +#endif /* HW_RVL */ diff --git a/wii/libogc/libogc/video.c b/wii/libogc/libogc/video.c new file mode 100644 index 0000000000..9012530ea2 --- /dev/null +++ b/wii/libogc/libogc/video.c @@ -0,0 +1,2594 @@ +/*------------------------------------------------------------- + +video.c -- VIDEO subsystem + + Copyright (C) 2004 - 2008 + Michael Wiedenbauer (shagkur) + Dave Murphy (WinterMute) + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +-------------------------------------------------------------*/ + + +#include +#include +#include +#include +#include "asm.h" +#include "processor.h" +#include "ogcsys.h" +#include "irq.h" +#include "exi.h" +#include "gx.h" +#include "si.h" +#include "lwp.h" +#include "system.h" +#include "video.h" +#include "video_types.h" + +#define VIDEO_MQ 1 + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +#define VI_REGCHANGE(_reg) \ + ((u64)0x01<<(63-_reg)) + +typedef struct _horVer { + u16 dispPosX; + u16 dispPosY; + u16 dispSizeX; + u16 dispSizeY; + u16 adjustedDispPosX; + u16 adjustedDispPosY; + u16 adjustedDispSizeY; + u16 adjustedPanPosY; + u16 adjustedPanSizeY; + u16 fbSizeX; + u16 fbSizeY; + u16 panPosX; + u16 panPosY; + u16 panSizeX; + u16 panSizeY; + u32 fbMode; + u32 nonInter; + u32 tv; + u8 wordPerLine; + u8 std; + u8 wpl; + void *bufAddr; + u32 tfbb; + u32 bfbb; + u8 xof; + s32 black; + s32 threeD; + void *rbufAddr; + u32 rtfbb; + u32 rbfbb; + const struct _timing *timing; +} horVer; + +GXRModeObj TVNtsc240Ds = +{ + VI_TVMODE_NTSC_DS, // viDisplayMode + 640, // fbWidth + 240, // efbHeight + 240, // xfbHeight + (VI_MAX_WIDTH_NTSC - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_NTSC - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVNtsc240DsAa = +{ + VI_TVMODE_NTSC_DS, // viDisplayMode + 640, // fbWidth + 240, // efbHeight + 240, // xfbHeight + (VI_MAX_WIDTH_NTSC - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_NTSC - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_TRUE, // aa + + // sample points arranged in increasing Y order + { + {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {3,2},{9,6},{3,10}, // pix 1 + {9,2},{3,6},{9,10}, // pix 2 + {9,2},{3,6},{9,10} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVNtsc240Int = +{ + VI_TVMODE_NTSC_INT, // viDisplayMode + 640, // fbWidth + 240, // efbHeight + 240, // xfbHeight + (VI_MAX_WIDTH_NTSC - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_NTSC - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_TRUE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVNtsc240IntAa = +{ + VI_TVMODE_NTSC_INT, // viDisplayMode + 640, // fbWidth + 240, // efbHeight + 240, // xfbHeight + (VI_MAX_WIDTH_NTSC - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_NTSC - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_TRUE, // field_rendering + GX_TRUE, // aa + + // sample points arranged in increasing Y order + { + {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {3,2},{9,6},{3,10}, // pix 1 + {9,2},{3,6},{9,10}, // pix 2 + {9,2},{3,6},{9,10} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVNtsc480Int = +{ + VI_TVMODE_NTSC_INT, // viDisplayMode + 640, // fbWidth + 480, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_NTSC - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_NTSC - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_DF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVNtsc480IntDf = +{ + VI_TVMODE_NTSC_INT, // viDisplayMode + 640, // fbWidth + 480, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_NTSC - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_NTSC - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_DF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 8, // line n-1 + 8, // line n-1 + 10, // line n + 12, // line n + 10, // line n + 8, // line n+1 + 8 // line n+1 + } +}; + +GXRModeObj TVNtsc480IntAa = +{ + VI_TVMODE_NTSC_INT, // viDisplayMode + 640, // fbWidth + 242, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_NTSC - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_NTSC - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_DF, // xFBmode + GX_FALSE, // field_rendering + GX_TRUE, // aa + + // sample points arranged in increasing Y order + { + {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {3,2},{9,6},{3,10}, // pix 1 + {9,2},{3,6},{9,10}, // pix 2 + {9,2},{3,6},{9,10} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 4, // line n-1 + 8, // line n-1 + 12, // line n + 16, // line n + 12, // line n + 8, // line n+1 + 4 // line n+1 + } +}; + + +GXRModeObj TVNtsc480Prog = +{ + VI_TVMODE_NTSC_PROG, // viDisplayMode + 640, // fbWidth + 480, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_NTSC - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_NTSC - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVNtsc480ProgSoft = +{ + VI_TVMODE_NTSC_PROG, // viDisplayMode + 640, // fbWidth + 480, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_NTSC - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_NTSC - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 8, // line n-1 + 8, // line n-1 + 10, // line n + 12, // line n + 10, // line n + 8, // line n+1 + 8 // line n+1 + } +}; + +GXRModeObj TVNtsc480ProgAa = +{ + VI_TVMODE_NTSC_PROG, // viDisplayMode + 640, // fbWidth + 242, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_NTSC - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_NTSC - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_TRUE, // aa + + // sample points arranged in increasing Y order + { + {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {3,2},{9,6},{3,10}, // pix 1 + {9,2},{3,6},{9,10}, // pix 2 + {9,2},{3,6},{9,10} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 4, // line n-1 + 8, // line n-1 + 12, // line n + 16, // line n + 12, // line n + 8, // line n+1 + 4 // line n+1 + } +}; + +GXRModeObj TVMpal240Ds = +{ + VI_TVMODE_MPAL_DS, // viDisplayMode + 640, // fbWidth + 240, // efbHeight + 240, // xfbHeight + (VI_MAX_WIDTH_MPAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_MPAL - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVMpal240DsAa = +{ + VI_TVMODE_MPAL_DS, // viDisplayMode + 640, // fbWidth + 240, // efbHeight + 240, // xfbHeight + (VI_MAX_WIDTH_MPAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_MPAL - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_TRUE, // aa + + // sample points arranged in increasing Y order + { + {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {3,2},{9,6},{3,10}, // pix 1 + {9,2},{3,6},{9,10}, // pix 2 + {9,2},{3,6},{9,10} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVMpal480IntDf = +{ + VI_TVMODE_MPAL_INT, // viDisplayMode + 640, // fbWidth + 480, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_MPAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_MPAL - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_DF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 8, // line n-1 + 8, // line n-1 + 10, // line n + 12, // line n + 10, // line n + 8, // line n+1 + 8 // line n+1 + } +}; + +GXRModeObj TVMpal480IntAa = +{ + VI_TVMODE_MPAL_INT, // viDisplayMode + 640, // fbWidth + 242, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_MPAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_MPAL - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_DF, // xFBmode + GX_FALSE, // field_rendering + GX_TRUE, // aa + + // sample points arranged in increasing Y order + { + {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {3,2},{9,6},{3,10}, // pix 1 + {9,2},{3,6},{9,10}, // pix 2 + {9,2},{3,6},{9,10} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 4, // line n-1 + 8, // line n-1 + 12, // line n + 16, // line n + 12, // line n + 8, // line n+1 + 4 // line n+1 + } +}; + +GXRModeObj TVMpal480Prog = +{ + VI_TVMODE_MPAL_PROG, // viDisplayMode + 640, // fbWidth + 480, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_MPAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_MPAL - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVPal264Ds = +{ + VI_TVMODE_PAL_DS, // viDisplayMode + 640, // fbWidth + 264, // efbHeight + 264, // xfbHeight + (VI_MAX_WIDTH_PAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_PAL - 528)/2, // viYOrigin + 640, // viWidth + 528, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVPal264DsAa = +{ + VI_TVMODE_PAL_DS, // viDisplayMode + 640, // fbWidth + 264, // efbHeight + 264, // xfbHeight + (VI_MAX_WIDTH_PAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_PAL - 528)/2, // viYOrigin + 640, // viWidth + 528, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_TRUE, // aa + + // sample points arranged in increasing Y order + { + {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {3,2},{9,6},{3,10}, // pix 1 + {9,2},{3,6},{9,10}, // pix 2 + {9,2},{3,6},{9,10} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVPal264Int = +{ + VI_TVMODE_PAL_INT, // viDisplayMode + 640, // fbWidth + 264, // efbHeight + 264, // xfbHeight + (VI_MAX_WIDTH_PAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_PAL - 528)/2, // viYOrigin + 640, // viWidth + 528, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_TRUE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVPal264IntAa = +{ + VI_TVMODE_PAL_INT, // viDisplayMode + 640, // fbWidth + 264, // efbHeight + 264, // xfbHeight + (VI_MAX_WIDTH_PAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_PAL - 528)/2, // viYOrigin + 640, // viWidth + 528, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_TRUE, // field_rendering + GX_TRUE, // aa + + // sample points arranged in increasing Y order + { + {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {3,2},{9,6},{3,10}, // pix 1 + {9,2},{3,6},{9,10}, // pix 2 + {9,2},{3,6},{9,10} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVPal524IntAa = +{ + VI_TVMODE_PAL_INT, + 640, + 264, + 524, + (VI_MAX_WIDTH_PAL-640)/2, + (VI_MAX_HEIGHT_PAL-528)/2, + 640, + 524, + VI_XFBMODE_DF, + GX_FALSE, + GX_TRUE, + + // sample points arranged in increasing Y order + { + {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {3,2},{9,6},{3,10}, // pix 1 + {9,2},{3,6},{9,10}, // pix 2 + {9,2},{3,6},{9,10} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 4, // line n-1 + 8, // line n-1 + 12, // line n + 16, // line n + 12, // line n + 8, // line n+1 + 4 // line n+1 + } +}; + +GXRModeObj TVPal528Int = +{ + VI_TVMODE_PAL_INT, // viDisplayMode + 640, // fbWidth + 528, // efbHeight + 528, // xfbHeight + (VI_MAX_WIDTH_PAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_PAL - 528)/2, // viYOrigin + 640, // viWidth + 528, // viHeight + VI_XFBMODE_DF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVPal528IntDf = +{ + VI_TVMODE_PAL_INT, // viDisplayMode + 640, // fbWidth + 528, // efbHeight + 528, // xfbHeight + (VI_MAX_WIDTH_PAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_PAL - 528)/2, // viYOrigin + 640, // viWidth + 528, // viHeight + VI_XFBMODE_DF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + // vertical filter[7], 1/64 units, 6 bits each + { + 8, // line n-1 + 8, // line n-1 + 10, // line n + 12, // line n + 10, // line n + 8, // line n+1 + 8 // line n+1 + } +}; + +GXRModeObj TVPal576IntDfScale = +{ + VI_TVMODE_PAL_INT, // viDisplayMode + 640, // fbWidth + 480, // efbHeight + 576, // xfbHeight + (VI_MAX_WIDTH_PAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_PAL - 576)/2, // viYOrigin + 640, // viWidth + 576, // viHeight + VI_XFBMODE_DF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + // vertical filter[7], 1/64 units, 6 bits each + { + 8, // line n-1 + 8, // line n-1 + 10, // line n + 12, // line n + 10, // line n + 8, // line n+1 + 8 // line n+1 + } +}; + +GXRModeObj TVPal576ProgScale = +{ + VI_TVMODE_PAL_PROG, // viDisplayMode + 640, // fbWidth + 480, // efbHeight + 576, // xfbHeight + (VI_MAX_WIDTH_PAL - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_PAL - 576)/2, // viYOrigin + 640, // viWidth + 576, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVEurgb60Hz240Ds = +{ + VI_TVMODE_EURGB60_DS, // viDisplayMode + 640, // fbWidth + 240, // efbHeight + 240, // xfbHeight + (VI_MAX_WIDTH_EURGB60 - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_EURGB60 - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVEurgb60Hz240DsAa = +{ + VI_TVMODE_EURGB60_DS, // viDisplayMode + 640, // fbWidth + 240, // efbHeight + 240, // xfbHeight + (VI_MAX_WIDTH_EURGB60 - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_EURGB60 - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_TRUE, // aa + + // sample points arranged in increasing Y order + { + {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {3,2},{9,6},{3,10}, // pix 1 + {9,2},{3,6},{9,10}, // pix 2 + {9,2},{3,6},{9,10} // pix 3 + }, + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVEurgb60Hz240Int = +{ + VI_TVMODE_EURGB60_INT, // viDisplayMode + 640, // fbWidth + 240, // efbHeight + 240, // xfbHeight + (VI_MAX_WIDTH_EURGB60 - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_EURGB60 - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_TRUE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVEurgb60Hz240IntAa = +{ + VI_TVMODE_EURGB60_INT, // viDisplayMode + 640, // fbWidth + 240, // efbHeight + 240, // xfbHeight + (VI_MAX_WIDTH_EURGB60 - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_EURGB60 - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_TRUE, // field_rendering + GX_TRUE, // aa + + // sample points arranged in increasing Y order + { + {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {3,2},{9,6},{3,10}, // pix 1 + {9,2},{3,6},{9,10}, // pix 2 + {9,2},{3,6},{9,10} // pix 3 + }, + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVEurgb60Hz480Int = +{ + VI_TVMODE_EURGB60_INT, // viDisplayMode + 640, // fbWidth + 480, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_EURGB60 - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_EURGB60 - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_DF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVEurgb60Hz480IntDf = +{ + VI_TVMODE_EURGB60_INT, // viDisplayMode + 640, // fbWidth + 480, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_EURGB60 - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_EURGB60 - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_DF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + // vertical filter[7], 1/64 units, 6 bits each + { + 8, // line n-1 + 8, // line n-1 + 10, // line n + 12, // line n + 10, // line n + 8, // line n+1 + 8 // line n+1 + } +}; + +GXRModeObj TVEurgb60Hz480IntAa = +{ + VI_TVMODE_EURGB60_INT, // viDisplayMode + 640, // fbWidth + 242, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_EURGB60 - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_EURGB60 - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_DF, // xFBmode + GX_FALSE, // field_rendering + GX_TRUE, // aa + + // sample points arranged in increasing Y order + { + {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {3,2},{9,6},{3,10}, // pix 1 + {9,2},{3,6},{9,10}, // pix 2 + {9,2},{3,6},{9,10} // pix 3 + }, + // vertical filter[7], 1/64 units, 6 bits each + { + 4, // line n-1 + 8, // line n-1 + 12, // line n + 16, // line n + 12, // line n + 8, // line n+1 + 4 // line n+1 + } +}; + +GXRModeObj TVEurgb60Hz480Prog = +{ + VI_TVMODE_EURGB60_PROG, // viDisplayMode + 640, // fbWidth + 480, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_EURGB60 - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_EURGB60 - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + // vertical filter[7], 1/64 units, 6 bits each + { + 0, // line n-1 + 0, // line n-1 + 21, // line n + 22, // line n + 21, // line n + 0, // line n+1 + 0 // line n+1 + } +}; + +GXRModeObj TVEurgb60Hz480ProgSoft = +{ + VI_TVMODE_EURGB60_PROG, // viDisplayMode + 640, // fbWidth + 480, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_EURGB60 - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_EURGB60 - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_FALSE, // aa + + // sample points arranged in increasing Y order + { + {3,2},{9,6},{3,10}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {3,2},{9,6},{3,10}, // pix 1 + {9,2},{3,6},{9,10}, // pix 2 + {9,2},{3,6},{9,10} // pix 3 + }, + // vertical filter[7], 1/64 units, 6 bits each + { + 4, // line n-1 + 8, // line n-1 + 12, // line n + 16, // line n + 12, // line n + 8, // line n+1 + 4 // line n+1 + } +}; + +GXRModeObj TVEurgb60Hz480ProgAa = +{ + VI_TVMODE_EURGB60_PROG, // viDisplayMode + 640, // fbWidth + 242, // efbHeight + 480, // xfbHeight + (VI_MAX_WIDTH_EURGB60 - 640)/2, // viXOrigin + (VI_MAX_HEIGHT_EURGB60 - 480)/2, // viYOrigin + 640, // viWidth + 480, // viHeight + VI_XFBMODE_SF, // xFBmode + GX_FALSE, // field_rendering + GX_TRUE, // aa + + // sample points arranged in increasing Y order + { + {6,6},{6,6},{6,6}, // pix 0, 3 sample points, 1/12 units, 4 bits each + {6,6},{6,6},{6,6}, // pix 1 + {6,6},{6,6},{6,6}, // pix 2 + {6,6},{6,6},{6,6} // pix 3 + }, + // vertical filter[7], 1/64 units, 6 bits each + { + 8, // line n-1 + 8, // line n-1 + 10, // line n + 12, // line n + 10, // line n + 8, // line n+1 + 8 // line n+1 + } +}; + + +static const u16 taps[26] = { + 0x01F0,0x01DC,0x01AE,0x0174,0x0129,0x00DB, + 0x008E,0x0046,0x000C,0x00E2,0x00CB,0x00C0, + 0x00C4,0x00CF,0x00DE,0x00EC,0x00FC,0x0008, + 0x000F,0x0013,0x0013,0x000F,0x000C,0x0008, + 0x0001,0x0000 +}; + +static const struct _timing { + u8 equ; + u16 acv; + u16 prbOdd,prbEven; + u16 psbOdd,psbEven; + u8 bs1,bs2,bs3,bs4; + u16 be1,be2,be3,be4; + u16 nhlines,hlw; + u8 hsy,hcs,hce,hbe640; + u16 hbs640; +} video_timing[] = { + { + 0x06,0x00F0, + 0x0018,0x0019,0x0003,0x0002, + 0x0C,0x0D,0x0C,0x0D, + 0x0208,0x0207,0x0208,0x0207, + 0x020D,0x01AD, + 0x40,0x47,0x69,0xA2, + 0x0175 + }, + { + 0x06,0x00F0, + 0x0018,0x0018,0x0004,0x0004, + 0x0C,0x0C,0x0C,0x0C, + 0x0208,0x0208,0x0208,0x0208, + 0x020E,0x01AD, + 0x40,0x47,0x69,0xA2, + 0x0175 + }, + { + 0x05,0x0120, + 0x0021,0x0022,0x0001,0x0000, + 0x0A,0x0B,0x0A,0x0B, + 0x026D,0x026C,0x026D,0x026C, + 0x0271,0x01B0, + 0x40,0x4B,0x6A,0xAC, + 0x017C + }, + { + 0x05,0x011F, + 0x0021,0x0021,0x0002,0x0002, + 0x0D,0x0B,0x0D,0x0B, + 0x026B,0x026D,0x026B,0x026D, + 0x0270,0x01B0, + 0x40,0x4B,0x6A,0xAC, + 0x017C + }, + { + 0x06,0x00F0, + 0x0018,0x0019,0x0003,0x0002, + 0x10,0x0F,0x0E,0x0D, + 0x0206,0x0205,0x0204,0x0207, + 0x020D,0x01AD, + 0x40,0x4E,0x70,0xA2, + 0x0175 + }, + { + 0x06,0x00F0, + 0x0018,0x0018,0x0004,0x0004, + 0x10,0x0E,0x10,0x0E, + 0x0206,0x0208,0x0206,0x0208, + 0x020E,0x01AD, + 0x40,0x4E,0x70,0xA2, + 0x0175 + }, + { + 0x0C,0x01E0, + 0x0030,0x0030,0x0006,0x0006, + 0x18,0x18,0x18,0x18, + 0x040E,0x040E,0x040E,0x040E, + 0x041A,0x01AD, + 0x40,0x47,0x69,0xA2, + 0x0175 + }, + { + 0x0A,0x0240, + 0x003E,0x003E,0x0006,0x0006, + 0x14,0x14,0x14,0x14, + 0x04D8,0x04D8,0x04D8,0x04D8, + 0x04E2,0x01B0, + 0x40,0x4B,0x6A,0xAC, + 0x017C + } +}; + +#if defined(HW_RVL) +static u32 vdacFlagRegion; +static u32 i2cIdentFirst = 0; +static u32 i2cIdentFlag = 1; +static u32 oldTvStatus = 0x03e7; +static u32 oldDtvStatus = 0x03e7; +static vu32* const _i2cReg = (u32*)0xCD800000; +#endif + +static u16 regs[60]; +static u16 shdw_regs[60]; +static u32 fbSet = 0; +static s16 displayOffsetH; +static s16 displayOffsetV; +static u32 currTvMode,changeMode; +static u32 shdw_changeMode,flushFlag; +static u64 changed,shdw_changed; +static vu32 retraceCount; +static const struct _timing *currTiming; +static lwpq_t video_queue; +static horVer HorVer; +static void *currentFb = NULL; +static void *nextFb = NULL; +static VIRetraceCallback preRetraceCB = NULL; +static VIRetraceCallback postRetraceCB = NULL; +static VIPositionCallback positionCB = NULL; + +static vu16* const _viReg = (u16*)0xCC002000; + +extern syssram* __SYS_LockSram(); +extern u32 __SYS_UnlockSram(u32 write); + +extern void __VIClearFramebuffer(void*,u32,u32); + +extern void udelay(int us); + +static __inline__ u32 cntlzd(u64 bit) +{ + u32 hi = (u32)(bit>>32); + u32 lo = (u32)(bit&-1); + u32 value = cntlzw(hi); + if(value>=32) value += cntlzw(lo); + + return value; +} + +static const struct _timing* __gettiming(u32 vimode) +{ + switch(vimode) { + case VI_TVMODE_NTSC_INT: + return &video_timing[0]; + break; + case VI_TVMODE_NTSC_DS: + return &video_timing[1]; + break; + case VI_TVMODE_PAL_INT: + return &video_timing[2]; + break; + case VI_TVMODE_PAL_DS: + return &video_timing[3]; + break; + case VI_TVMODE_EURGB60_INT: + return &video_timing[0]; + break; + case VI_TVMODE_EURGB60_DS: + return &video_timing[1]; + break; + case VI_TVMODE_MPAL_INT: + return &video_timing[4]; + break; + case VI_TVMODE_MPAL_DS: + return &video_timing[5]; + break; + case VI_TVMODE_NTSC_PROG: + return &video_timing[6]; + break; + case VI_TVMODE_PAL_PROG: + return &video_timing[7]; + break; + case VI_TVMODE_EURGB60_PROG: + return &video_timing[6]; + break; + case VI_TVMODE_MPAL_PROG: + return &video_timing[6]; + break; + default: + return NULL; + } +} + +#if defined(HW_RVL) +static inline void __viOpenI2C(u32 channel) +{ + u32 val = ((_i2cReg[49]&~0x8000)|0x4000); + val |= _SHIFTL(channel,15,1); + _i2cReg[49] = val; +} + +static inline u32 __viSetSCL(u32 channel) +{ + u32 val = (_i2cReg[48]&~0x4000); + val |= _SHIFTL(channel,14,1); + _i2cReg[48] = val; + return 1; +} +static inline u32 __viSetSDA(u32 channel) +{ + u32 val = (_i2cReg[48]&~0x8000); + val |= _SHIFTL(channel,15,1); + _i2cReg[48] = val; + return 1; +} + +static inline u32 __viGetSDA() +{ + return _SHIFTR(_i2cReg[50],15,1); +} + +static inline void __viCheckI2C() +{ + __viOpenI2C(0); + udelay(4); + + i2cIdentFlag = 0; + if(__viGetSDA()!=0) i2cIdentFlag = 1; +} + +static u32 __sendSlaveAddress(u8 addr) +{ + u32 i; + + __viSetSDA(i2cIdentFlag^1); + udelay(2); + + __viSetSCL(0); + for(i=0;i<8;i++) { + if(addr&0x80) __viSetSDA(i2cIdentFlag); + else __viSetSDA(i2cIdentFlag^1); + udelay(2); + + __viSetSCL(1); + udelay(2); + + __viSetSCL(0); + addr <<= 1; + } + + __viOpenI2C(0); + udelay(2); + + __viSetSCL(1); + udelay(2); + + if(i2cIdentFlag==1 && __viGetSDA()!=0) return 0; + + __viSetSDA(i2cIdentFlag^1); + __viOpenI2C(1); + __viSetSCL(0); + + return 1; +} +#endif + +static inline void __setInterruptRegs(const struct _timing *tm) +{ + u16 hlw; + + hlw = 0; + if(tm->nhlines%2) hlw = tm->hlw; + regs[24] = 0x1000|((tm->nhlines/2)+1); + regs[25] = hlw+1; + changed |= VI_REGCHANGE(24); + changed |= VI_REGCHANGE(25); +} + +static inline void __setPicConfig(u16 fbSizeX,u32 xfbMode,u16 panPosX,u16 panSizeX,u8 *wordPerLine,u8 *std,u8 *wpl,u8 *xof) +{ + *wordPerLine = (fbSizeX+15)/16; + *std = *wordPerLine; + if(xfbMode==VI_XFBMODE_DF) *std <<= 1; + + *xof = panPosX%16; + *wpl = (*xof+(panSizeX+15))/16; + regs[36] = (*wpl<<8)|*std; + changed |= VI_REGCHANGE(36); +} + +static inline void __setBBIntervalRegs(const struct _timing *tm) +{ + regs[10] = (tm->be3<<5)|tm->bs3; + regs[11] = (tm->be1<<5)|tm->bs1; + changed |= VI_REGCHANGE(10); + changed |= VI_REGCHANGE(11); + + regs[12] = (tm->be4<<5)|tm->bs4; + regs[13] = (tm->be2<<5)|tm->bs2; + changed |= VI_REGCHANGE(12); + changed |= VI_REGCHANGE(13); +} + +static void __setScalingRegs(u16 panSizeX,u16 dispSizeX,s32 threeD) +{ + if(threeD) panSizeX = _SHIFTL(panSizeX,1,16); + if(panSizeXbufAddr,horVer->panPosX,horVer->adjustedPanPosY,horVer->wordPerLine,horVer->fbMode,horVer->adjustedDispPosY,tfbb,bfbb); + if(horVer->threeD) __calcFbbs((u32)horVer->rbufAddr,horVer->panPosX,horVer->adjustedPanPosY,horVer->wordPerLine,horVer->fbMode,horVer->adjustedDispPosY,rtfbb,rbfbb); + + flag = 1; + if((*tfbb)<0x01000000 && (*bfbb)<0x01000000 + && (*rtfbb)<0x01000000 && (*rbfbb)<0x01000000) flag = 0; + + if(flag) { + *tfbb >>= 5; + *bfbb >>= 5; + *rtfbb >>= 5; + *rbfbb >>= 5; + } + + regs[14] = _SHIFTL(flag,12,1)|_SHIFTL(horVer->xof,8,4)|_SHIFTR(*tfbb,16,8); + regs[15] = *tfbb&0xffff; + changed |= VI_REGCHANGE(14); + changed |= VI_REGCHANGE(15); + + regs[18] = _SHIFTR(*bfbb,16,8); + regs[19] = *bfbb&0xffff; + changed |= VI_REGCHANGE(18); + changed |= VI_REGCHANGE(19); + + if(horVer->threeD) { + regs[16] = _SHIFTR(*rtfbb,16,8); + regs[17] = *rtfbb&0xffff; + changed |= VI_REGCHANGE(16); + changed |= VI_REGCHANGE(17); + + regs[20] = _SHIFTR(*rbfbb,16,8); + regs[21] = *rbfbb&0xffff; + changed |= VI_REGCHANGE(20); + changed |= VI_REGCHANGE(21); + } +} + +static inline void __setHorizontalRegs(const struct _timing *tm,u16 dispPosX,u16 dispSizeX) +{ + u32 val1,val2; + + regs[2] = (tm->hcs<<8)|tm->hce; + regs[3] = tm->hlw; + changed |= VI_REGCHANGE(2); + changed |= VI_REGCHANGE(3); + + val1 = (tm->hbe640+dispPosX-40)&0x01ff; + val2 = (tm->hbs640+dispPosX+40)-(720-dispSizeX); + regs[4] = (val1>>9)|(val2<<1); + regs[5] = (val1<<7)|tm->hsy; + changed |= VI_REGCHANGE(4); + changed |= VI_REGCHANGE(5); +} + +static inline void __setVerticalRegs(u16 dispPosY,u16 dispSizeY,u8 equ,u16 acv,u16 prbOdd,u16 prbEven,u16 psbOdd,u16 psbEven,s32 black) +{ + u16 tmp; + u32 div1,div2; + u32 psb,prb; + u32 psbodd,prbodd; + u32 psbeven,prbeven; + + div1 = 2; + div2 = 1; + if(equ>=10) { + div1 = 1; + div2 = 2; + } + + prb = div2*dispPosY; + psb = div2*(((acv*div1)-dispSizeY)-dispPosY); + if(dispPosY%2) { + prbodd = prbEven+prb; + psbodd = psbEven+psb; + prbeven = prbOdd+prb; + psbeven = psbOdd+psb; + } else { + prbodd = prbOdd+prb; + psbodd = psbOdd+psb; + prbeven = prbEven+prb; + psbeven = psbEven+psb; + } + + tmp = dispSizeY/div1; + if(black) { + prbodd += ((tmp<<1)-2); + prbeven += ((tmp<<1)-2); + psbodd += 2; + psbeven += 2; + tmp = 0; + } + + regs[0] = ((tmp<<4)&~0x0f)|equ; + changed |= VI_REGCHANGE(0); + + regs[6] = psbodd; + regs[7] = prbodd; + changed |= VI_REGCHANGE(6); + changed |= VI_REGCHANGE(7); + + regs[8] = psbeven; + regs[9] = prbeven; + changed |= VI_REGCHANGE(8); + changed |= VI_REGCHANGE(9); +} + +static inline void __adjustPosition(u16 acv) +{ + u32 fact,field; + s16 dispPosX,dispPosY; + s16 dispSizeY,maxDispSizeY; + + dispPosX = (HorVer.dispPosX+displayOffsetH); + if(dispPosX<=(720-HorVer.dispSizeX)) { + if(dispPosX>=0) HorVer.adjustedDispPosX = dispPosX; + else HorVer.adjustedDispPosX = 0; + } else HorVer.adjustedDispPosX = (720-HorVer.dispSizeX); + + fact = 1; + if(HorVer.fbMode==VI_XFBMODE_SF) fact = 2; + + field = HorVer.dispPosY&0x0001; + dispPosY = HorVer.dispPosY+displayOffsetV; + if(dispPosY>field) HorVer.adjustedDispPosY = dispPosY; + else HorVer.adjustedDispPosY = field; + + dispSizeY = HorVer.dispPosY+HorVer.dispSizeY+displayOffsetV; + maxDispSizeY = ((acv<<1)-field); + if(dispSizeY>maxDispSizeY) dispSizeY -= (acv<<1)-field; + else dispSizeY = 0; + + dispPosY = HorVer.dispPosY+displayOffsetV; + if(dispPosYmaxDispSizeY) dispSizeY -= maxDispSizeY; + else dispSizeY = 0; + + dispPosY = HorVer.dispPosY+displayOffsetV; + if(dispPosYdisplay_offsetH; + __SYS_UnlockSram(0); +#else + s8 offset; + if ( CONF_GetDisplayOffsetH(&offset) == 0 ) { + displayOffsetH = offset; + } else { + displayOffsetH = 0; + } +#endif + displayOffsetV = 0; +} + +static void __VIInit(u32 vimode) +{ + u32 cnt; + u32 vi_mode,interlace,progressive; + const struct _timing *cur_timing = NULL; + + vi_mode = ((vimode>>2)&0x07); + interlace = (vimode&0x01); + progressive = (vimode&0x02); + + cur_timing = __gettiming(vimode); + + //reset the interface + cnt = 0; + _viReg[1] = 0x02; + while(cnt<1000) cnt++; + _viReg[1] = 0x00; + + // now begin to setup the interface + _viReg[2] = ((cur_timing->hcs<<8)|cur_timing->hce); //set HCS & HCE + _viReg[3] = cur_timing->hlw; //set Half Line Width + + _viReg[4] = (cur_timing->hbs640<<1); //set HBS640 + _viReg[5] = ((cur_timing->hbe640<<7)|cur_timing->hsy); //set HBE640 & HSY + + _viReg[0] = cur_timing->equ; + + _viReg[6] = (cur_timing->psbOdd+2); //set PSB odd field + _viReg[7] = (cur_timing->prbOdd+((cur_timing->acv<<1)-2)); //set PRB odd field + + _viReg[8] = (cur_timing->psbEven+2); //set PSB even field + _viReg[9] = (cur_timing->prbEven+((cur_timing->acv<<1)-2)); //set PRB even field + + _viReg[10] = ((cur_timing->be3<<5)|cur_timing->bs3); //set BE3 & BS3 + _viReg[11] = ((cur_timing->be1<<5)|cur_timing->bs1); //set BE1 & BS1 + + _viReg[12] = ((cur_timing->be4<<5)|cur_timing->bs4); //set BE4 & BS4 + _viReg[13] = ((cur_timing->be2<<5)|cur_timing->bs2); //set BE2 & BS2 + + _viReg[24] = (0x1000|((cur_timing->nhlines/2)+1)); + _viReg[25] = (cur_timing->hlw+1); + + _viReg[26] = 0x1001; //set DI1 + _viReg[27] = 0x0001; //set DI1 + _viReg[36] = 0x2828; //set HSR + + if(vi_mode=VI_DEBUG_PAL) vi_mode = VI_NTSC; + if(progressive){ + _viReg[1] = ((vi_mode<<8)|0x0005); //set MODE & INT & enable + _viReg[54] = 0x0001; + } else { + _viReg[1] = ((vi_mode<<8)|(interlace<<2)|0x0001); + _viReg[54] = 0x0000; + } +} + +#if defined(HW_RVL) +static u32 __VISendI2CData(u8 addr,void *val,u32 len) +{ + u8 c; + s32 i,j; + u32 level,ret; + + if(i2cIdentFirst==0) { + __viCheckI2C(); + i2cIdentFirst = 1; + } + + _CPU_ISR_Disable(level); + + __viOpenI2C(1); + __viSetSCL(1); + + __viSetSDA(i2cIdentFlag); + udelay(4); + + ret = __sendSlaveAddress(addr); + if(ret==0) { + _CPU_ISR_Restore(level); + return 0; + } + + __viOpenI2C(1); + for(i=0;i> 8; + buf[2] = data & 0xFF; + __VISendI2CData(0xe0,buf,3); + udelay(2); +} + +static void __VIWriteI2CRegister32(u8 reg, u32 data) +{ + u8 buf[5]; + buf[0] = reg; + buf[1] = data >> 24; + buf[2] = (data >> 16) & 0xFF; + buf[3] = (data >> 8) & 0xFF; + buf[4] = data & 0xFF; + __VISendI2CData(0xe0,buf,5); + udelay(2); +} + +static void __VIWriteI2CRegisterBuf(u8 reg, int size, u8 *data) +{ + u8 buf[0x100]; + buf[0] = reg; + memcpy(&buf[1], data, size); + __VISendI2CData(0xe0,buf,size+1); + udelay(2); +} + +static void __VISetYUVSEL(u8 dtvstatus) +{ + if(currTvMode==VI_NTSC) vdacFlagRegion = 0x0000; + else if(currTvMode==VI_PAL || currTvMode==VI_EURGB60) vdacFlagRegion = 0x0002; + else if(currTvMode==VI_MPAL) vdacFlagRegion = 0x0001; + else vdacFlagRegion = 0x0000; + + __VIWriteI2CRegister8(0x01, _SHIFTL(dtvstatus,5,3)|(vdacFlagRegion&0x1f)); +} + +static void __VISetFilterEURGB60(u8 enable) +{ + __VIWriteI2CRegister8(0x6e, enable); +} + +static void __VISetupEncoder(void) +{ + u8 macrobuf[0x1a]; + + u8 gamma[0x21] = { + 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, + 0x10, 0x00, 0x10, 0x00, 0x10, 0x20, 0x40, 0x60, + 0x80, 0xa0, 0xeb, 0x10, 0x00, 0x20, 0x00, 0x40, + 0x00, 0x60, 0x00, 0x80, 0x00, 0xa0, 0x00, 0xeb, + 0x00 + }; + + u8 dtv, tv; + + tv = VIDEO_GetCurrentTvMode(); + dtv = (_viReg[55]&0x01); + oldDtvStatus = dtv; + + // SetRevolutionModeSimple + + memset(macrobuf, 0, 0x1a); + + __VIWriteI2CRegister8(0x6a, 1); + __VIWriteI2CRegister8(0x65, 1); + __VISetYUVSEL(dtv); + __VIWriteI2CRegister8(0x00, 0); + __VIWriteI2CRegister16(0x71, 0x8e8e); + __VIWriteI2CRegister8(0x02, 7); + __VIWriteI2CRegister16(0x05, 0x0000); + __VIWriteI2CRegister16(0x08, 0x0000); + __VIWriteI2CRegister32(0x7A, 0x00000000); + + // Macrovision crap + __VIWriteI2CRegisterBuf(0x40, sizeof(macrobuf), macrobuf); + + // Sometimes 1 in RGB mode? (reg 1 == 3) + __VIWriteI2CRegister8(0x0A, 0); + + __VIWriteI2CRegister8(0x03, 1); + + __VIWriteI2CRegisterBuf(0x10, sizeof(gamma), gamma); + + __VIWriteI2CRegister8(0x04, 1); + + if(tv==VI_EURGB60) __VISetFilterEURGB60(1); + else __VISetFilterEURGB60(0); + oldTvStatus = tv; + +} +#endif + +static inline void __getCurrentDisplayPosition(u32 *px,u32 *py) +{ + u32 hpos = 0; + u32 vpos = 0; + u32 vpos_old; + + vpos = (_viReg[22]&0x7ff); + do { + vpos_old = vpos; + hpos = (_viReg[23]&0x7ff); + vpos = (_viReg[22]&0x7ff); + } while(vpos_old!=vpos); + *px = hpos; + *py = vpos; +} + +static inline u32 __getCurrentHalfLine() +{ + u32 vpos = 0; + u32 hpos = 0; + + __getCurrentDisplayPosition(&hpos,&vpos); + + hpos--; + vpos--; + vpos <<= 1; + + return vpos+(hpos/currTiming->hlw); +} + +static inline u32 __getCurrentFieldEvenOdd() +{ + u32 hline; + + hline = __getCurrentHalfLine(); + if(hlinenhlines) return 1; + + return 0; +} + +static inline u32 __VISetRegs() +{ + u32 val; + u64 mask; + + if(shdw_changeMode==1){ + if(!__getCurrentFieldEvenOdd()) return 0; + } + + while(shdw_changed) { + val = cntlzd(shdw_changed); + _viReg[val] = shdw_regs[val]; + mask = VI_REGCHANGE(val); + shdw_changed &= ~mask; + } + shdw_changeMode = 0; + currTiming = HorVer.timing; + currTvMode = HorVer.tv; + + currentFb = nextFb; + + return 1; +} + +static void __VIDisplayPositionToXY(s32 xpos,s32 ypos,s32 *px,s32 *py) +{ + u32 hpos,vpos; + u32 hline,val; + + hpos = (xpos-1); + vpos = (ypos-1); + hline = ((vpos<<1)+(hpos/currTiming->hlw)); + + *px = (s32)hpos; + if(HorVer.nonInter==0x0000) { + if(hlinenhlines) { + val = currTiming->prbOdd+(currTiming->equ*3); + if(hline>=val) { + val = (currTiming->nhlines-currTiming->psbOdd); + if(hlineequ*3))-currTiming->prbOdd)&~0x01); + } else + *py = -1; + } else + *py = -1; + } else { + hline -= currTiming->psbOdd; + val = (currTiming->prbEven+(currTiming->equ*3)); + if(hline>=val) { + val = (currTiming->nhlines-currTiming->psbEven); + if(hlineequ*3))-currTiming->prbEven)&~0x01)+1); + } else + *py = -1; + } else + *py = -1; + } + } else if(HorVer.nonInter==0x0001) { + if(hline>=currTiming->nhlines) hline -= currTiming->nhlines; + + val = (currTiming->prbOdd+(currTiming->equ*3)); + if(hline>=val) { + val = (currTiming->nhlines-currTiming->psbOdd); + if(hlineequ*3))-currTiming->prbOdd)&~0x01); + } else + *py = -1; + } else + *py = -1; + } else if(HorVer.nonInter==0x0002) { + if(hlinenhlines) { + val = currTiming->prbOdd+(currTiming->equ*3); + if(hline>=val) { + val = (currTiming->nhlines-currTiming->psbOdd); + if(hlineequ*3))-currTiming->prbOdd); + } else + *py = -1; + } else + *py = -1; + } else { + hline -= currTiming->psbOdd; + val = (currTiming->prbEven+(currTiming->equ*3)); + if(hline>=val) { + val = (currTiming->nhlines-currTiming->psbEven); + if(hlineequ*3))-currTiming->prbEven)&~0x01); + } else + *py = -1; + } else + *py = -1; + } + } +} + +static inline void __VIGetCurrentPosition(s32 *px,s32 *py) +{ + s32 xpos,ypos; + + __getCurrentDisplayPosition((u32*)&xpos,(u32*)&ypos); + __VIDisplayPositionToXY(xpos,ypos,px,py); +} + +static void __VIRetraceHandler(u32 nIrq,void *pCtx) +{ +#if defined(HW_RVL) + u8 dtv, tv; +#endif + u32 ret = 0; + u32 intr; + s32 xpos,ypos; + + intr = _viReg[24]; + if(intr&0x8000) { + _viReg[24] = intr&~0x8000; + ret |= 0x01; + } + + intr = _viReg[26]; + if(intr&0x8000) { + _viReg[26] = intr&~0x8000; + ret |= 0x02; + } + + intr = _viReg[28]; + if(intr&0x8000) { + _viReg[28] = intr&~0x8000; + ret |= 0x04; + } + + intr = _viReg[30]; + if(intr&0x8000) { + _viReg[30] = intr&~0x8000; + ret |= 0x08; + } + + intr = _viReg[30]; + if(ret&0x04 || ret&0x08) { + if(positionCB!=NULL) { + __VIGetCurrentPosition(&xpos,&ypos); + positionCB(xpos,ypos); + } + } + + retraceCount++; + if(preRetraceCB) + preRetraceCB(retraceCount); + + if(flushFlag) { + if(__VISetRegs()) { + flushFlag = 0; + SI_RefreshSamplingRate(); + } + } +#if defined(HW_RVL) + tv = VIDEO_GetCurrentTvMode(); + dtv = (_viReg[55]&0x01); + if(dtv!=oldDtvStatus || tv!=oldTvStatus) __VISetYUVSEL(dtv); + oldDtvStatus = dtv; + + if(tv!=oldTvStatus) { + if(tv==VI_EURGB60) __VISetFilterEURGB60(1); + else __VISetFilterEURGB60(0); + } + oldTvStatus = tv; +#endif + if(postRetraceCB) + postRetraceCB(retraceCount); + + LWP_ThreadBroadcast(video_queue); +} + +void* VIDEO_GetNextFramebuffer() +{ + return nextFb; +} + +void* VIDEO_GetCurrentFramebuffer() +{ + return currentFb; +} + +void VIDEO_Init() +{ + u32 level,vimode = 0; + + _CPU_ISR_Disable(level); + + if(!(_viReg[1]&0x0001)) + __VIInit(VI_TVMODE_NTSC_INT); + + retraceCount = 0; + changed = 0; + shdw_changed = 0; + shdw_changeMode = 0; + flushFlag = 0; + + _viReg[38] = ((taps[1]>>6)|(taps[2]<<4)); + _viReg[39] = (taps[0]|_SHIFTL(taps[1],10,6)); + _viReg[40] = ((taps[4]>>6)|(taps[5]<<4)); + _viReg[41] = (taps[3]|_SHIFTL(taps[4],10,6)); + _viReg[42] = ((taps[7]>>6)|(taps[8]<<4)); + _viReg[43] = (taps[6]|_SHIFTL(taps[7],10,6)); + _viReg[44] = (taps[11]|(taps[12]<<8)); + _viReg[45] = (taps[9]|(taps[10]<<8)); + _viReg[46] = (taps[15]|(taps[16]<<8)); + _viReg[47] = (taps[13]|(taps[14]<<8)); + _viReg[48] = (taps[19]|(taps[20]<<8)); + _viReg[49] = (taps[17]|(taps[18]<<8)); + _viReg[50] = (taps[23]|(taps[24]<<8)); + _viReg[51] = (taps[21]|(taps[22]<<8)); + _viReg[56] = 640; + + __importAdjustingValues(); + + HorVer.nonInter = _SHIFTR(_viReg[1],2,1); + HorVer.tv = _SHIFTR(_viReg[1],8,2); + + vimode = HorVer.nonInter; + if(HorVer.tv!=VI_DEBUG) vimode += (HorVer.tv<<2); + currTiming = __gettiming(vimode); + currTvMode = HorVer.tv; + + regs[1] = _viReg[1]; + HorVer.timing = currTiming; + HorVer.dispSizeX = 640; + HorVer.dispSizeY = currTiming->acv<<1; + HorVer.dispPosX = (VI_MAX_WIDTH_NTSC-HorVer.dispSizeX)/2; + HorVer.dispPosY = 0; + + __adjustPosition(currTiming->acv); + + HorVer.fbSizeX = 640; + HorVer.fbSizeY = currTiming->acv<<1; + HorVer.panPosX = 0; + HorVer.panPosY = 0; + HorVer.panSizeX = 640; + HorVer.panSizeY = currTiming->acv<<1; + HorVer.fbMode = VI_XFBMODE_SF; + HorVer.wordPerLine = 40; + HorVer.std = 40; + HorVer.wpl = 40; + HorVer.xof = 0; + HorVer.black = 1; + HorVer.threeD = 0; + HorVer.bfbb = 0; + HorVer.tfbb = 0; + HorVer.rbfbb = 0; + HorVer.rtfbb = 0; + + _viReg[24] &= ~0x8000; + _viReg[26] &= ~0x8000; + + preRetraceCB = NULL; + postRetraceCB = NULL; + + LWP_InitQueue(&video_queue); + + IRQ_Request(IRQ_PI_VI,__VIRetraceHandler,NULL); + __UnmaskIrq(IRQMASK(IRQ_PI_VI)); +#if defined(HW_RVL) + __VISetupEncoder(); +#endif + _CPU_ISR_Restore(level); +} + +void VIDEO_Configure(GXRModeObj *rmode) +{ + u16 dcr; + u32 nonint,vimode,level; + const struct _timing *curtiming; + _CPU_ISR_Disable(level); + nonint = (rmode->viTVMode&0x0003); + if(nonint!=HorVer.nonInter) { + changeMode = 1; + HorVer.nonInter = nonint; + } + HorVer.tv = _SHIFTR(rmode->viTVMode,2,3); + HorVer.dispPosX = rmode->viXOrigin; + HorVer.dispPosY = rmode->viYOrigin; + if(HorVer.nonInter==VI_NON_INTERLACE) HorVer.dispPosY = HorVer.dispPosY<<1; + + HorVer.dispSizeX = rmode->viWidth; + HorVer.fbSizeX = rmode->fbWidth; + HorVer.fbSizeY = rmode->xfbHeight; + HorVer.fbMode = rmode->xfbMode; + HorVer.panSizeX = HorVer.fbSizeX; + HorVer.panSizeY = HorVer.fbSizeY; + HorVer.panPosX = 0; + HorVer.panPosY = 0; + + if(HorVer.nonInter==VI_PROGRESSIVE || HorVer.nonInter==(VI_NON_INTERLACE|VI_PROGRESSIVE)) HorVer.dispSizeY = HorVer.panSizeY; + else if(HorVer.fbMode==VI_XFBMODE_SF) HorVer.dispSizeY = HorVer.panSizeY<<1; + else HorVer.dispSizeY = HorVer.panSizeY; + + if(HorVer.nonInter==(VI_NON_INTERLACE|VI_PROGRESSIVE)) HorVer.threeD = 1; + else HorVer.threeD = 0; + + vimode = VI_TVMODE(HorVer.tv,HorVer.nonInter); + curtiming = __gettiming(vimode); + HorVer.timing = curtiming; + + __adjustPosition(curtiming->acv); + __setInterruptRegs(curtiming); + + dcr = regs[1]&~0x030c; + dcr |= _SHIFTL(HorVer.threeD,3,1); + if(HorVer.nonInter==VI_PROGRESSIVE || HorVer.nonInter==(VI_NON_INTERLACE|VI_PROGRESSIVE)) dcr |= 0x0004; + else dcr |= _SHIFTL(HorVer.nonInter,2,1); + if(!(HorVer.tv==VI_EURGB60)) dcr |= _SHIFTL(HorVer.tv,8,2); + regs[1] = dcr; + changed |= VI_REGCHANGE(1); + + regs[54] &= ~0x0001; + if(HorVer.nonInter==VI_PROGRESSIVE || HorVer.nonInter==(VI_NON_INTERLACE|VI_PROGRESSIVE)) regs[54] |= 0x0001; + changed |= VI_REGCHANGE(54); + + __setScalingRegs(HorVer.panSizeX,HorVer.dispSizeX,HorVer.threeD); + __setHorizontalRegs(curtiming,HorVer.adjustedDispPosX,HorVer.dispSizeX); + __setBBIntervalRegs(curtiming); + __setPicConfig(HorVer.fbSizeX,HorVer.fbMode,HorVer.panPosX,HorVer.panSizeX,&HorVer.wordPerLine,&HorVer.std,&HorVer.wpl,&HorVer.xof); + + if(fbSet) __setFbbRegs(&HorVer,&HorVer.tfbb,&HorVer.bfbb,&HorVer.rtfbb,&HorVer.rbfbb); + + __setVerticalRegs(HorVer.adjustedDispPosY,HorVer.adjustedDispSizeY,curtiming->equ,curtiming->acv,curtiming->prbOdd,curtiming->prbEven,curtiming->psbOdd,curtiming->psbEven,HorVer.black); + _CPU_ISR_Restore(level); +} + +void VIDEO_WaitVSync(void) +{ + u32 level; + u32 retcnt; + + _CPU_ISR_Disable(level); + retcnt = retraceCount; + do { + LWP_ThreadSleep(video_queue); + } while(retraceCount==retcnt); + _CPU_ISR_Restore(level); +} + +void VIDEO_SetFramebuffer(void *fb) +{ + u32 level; + + _CPU_ISR_Disable(level); + fbSet = 1; + HorVer.bufAddr = fb; + __setFbbRegs(&HorVer,&HorVer.tfbb,&HorVer.bfbb,&HorVer.rtfbb,&HorVer.rbfbb); + _viReg[14] = regs[14]; + _viReg[15] = regs[15]; + + _viReg[18] = regs[18]; + _viReg[19] = regs[19]; + + if(HorVer.threeD) { + _viReg[16] = regs[16]; + _viReg[17] = regs[17]; + + _viReg[20] = regs[20]; + _viReg[21] = regs[21]; + } + _CPU_ISR_Restore(level); +} + +void VIDEO_SetNextFramebuffer(void *fb) +{ + u32 level; + _CPU_ISR_Disable(level); + fbSet = 1; + HorVer.bufAddr = fb; + __setFbbRegs(&HorVer,&HorVer.tfbb,&HorVer.bfbb,&HorVer.rtfbb,&HorVer.rbfbb); + _CPU_ISR_Restore(level); +} + +void VIDEO_SetNextRightFramebuffer(void *fb) +{ + u32 level; + + _CPU_ISR_Disable(level); + fbSet = 1; + HorVer.rbufAddr = fb; + __setFbbRegs(&HorVer,&HorVer.tfbb,&HorVer.bfbb,&HorVer.rtfbb,&HorVer.rbfbb); + _CPU_ISR_Restore(level); +} + +void VIDEO_Flush() +{ + u32 level; + u32 val; + u64 mask; + + _CPU_ISR_Disable(level); + shdw_changeMode |= changeMode; + changeMode = 0; + + shdw_changed |= changed; + while(changed) { + val = cntlzd(changed); + shdw_regs[val] = regs[val]; + mask = VI_REGCHANGE(val); + changed &= ~mask; + } + flushFlag = 1; + nextFb = HorVer.bufAddr; + _CPU_ISR_Restore(level); +} + +void VIDEO_SetBlack(bool black) +{ + u32 level; + const struct _timing *curtiming; + + _CPU_ISR_Disable(level); + HorVer.black = black; + curtiming = HorVer.timing; + __setVerticalRegs(HorVer.adjustedDispPosY,HorVer.dispSizeY,curtiming->equ,curtiming->acv,curtiming->prbOdd,curtiming->prbEven,curtiming->psbOdd,curtiming->psbEven,HorVer.black); + _CPU_ISR_Restore(level); +} + +u32 VIDEO_GetNextField() +{ + u32 level,nextfield; + + _CPU_ISR_Disable(level); + nextfield = __getCurrentFieldEvenOdd()^1; //we've to swap the result because it shows us only the current field,so we've the next field either even or odd + _CPU_ISR_Restore(level); + + return nextfield^(HorVer.adjustedDispPosY&0x0001); //if the YOrigin is at an odd position we've to swap it again, since the Fb registers are set swapped if this rule applies +} + +u32 VIDEO_GetCurrentTvMode() +{ + u32 mode; + u32 level; + u32 tv; + + _CPU_ISR_Disable(level); + mode = currTvMode; + + if(mode==VI_DEBUG) tv = VI_NTSC; + else if(mode==VI_EURGB60) tv = VI_EURGB60; + else if(mode==VI_MPAL) tv = VI_MPAL; + else if(mode==VI_NTSC) tv = VI_NTSC; + else tv = VI_PAL; + _CPU_ISR_Restore(level); + + return tv; +} + +GXRModeObj * VIDEO_GetPreferredMode(GXRModeObj *mode) +{ + +GXRModeObj *rmode = NULL; + +#if defined(HW_RVL) + u32 tvmode = CONF_GetVideo(); + if (CONF_GetProgressiveScan() > 0 && VIDEO_HaveComponentCable()) { + switch (tvmode) { + case CONF_VIDEO_NTSC: + rmode = &TVNtsc480Prog; + break; + case CONF_VIDEO_PAL: + if (CONF_GetEuRGB60() > 0) + rmode = &TVEurgb60Hz480Prog; + else rmode = &TVPal576ProgScale; + break; + case CONF_VIDEO_MPAL: + rmode = &TVMpal480Prog; + break; + default: + rmode = &TVNtsc480Prog; + } + } else { + switch (tvmode) { + case CONF_VIDEO_NTSC: + rmode = &TVNtsc480IntDf; + break; + case CONF_VIDEO_PAL: + if (CONF_GetEuRGB60() > 0) + rmode = &TVEurgb60Hz480IntDf; + else rmode = &TVPal576IntDfScale; + break; + case CONF_VIDEO_MPAL: + rmode = &TVMpal480IntDf; + break; + default: + rmode = &TVNtsc480IntDf; + } + } +#else + u32 tvmode = VIDEO_GetCurrentTvMode(); + if (VIDEO_HaveComponentCable()) { + switch (tvmode) { + case VI_NTSC: + rmode = &TVNtsc480Prog; + break; + case VI_PAL: + rmode = &TVPal576ProgScale; + break; + case VI_MPAL: + rmode = &TVMpal480Prog; + break; + case VI_EURGB60: + rmode = &TVEurgb60Hz480Prog; + break; + } + } else { + switch (tvmode) { + case VI_NTSC: + rmode = &TVNtsc480IntDf; + break; + case VI_PAL: + rmode = &TVPal576IntDfScale; + break; + case VI_MPAL: + rmode = &TVMpal480IntDf; + break; + case VI_EURGB60: + rmode = &TVEurgb60Hz480IntDf; + break; + } + } +#endif + + if ( NULL != mode ) { + memcpy( mode, rmode, sizeof(GXRModeObj)); + } else { + mode = rmode; + } + + return mode; + + +} + +u32 VIDEO_GetCurrentLine() +{ + u32 level,curr_hl = 0; + + _CPU_ISR_Disable(level); + curr_hl = __getCurrentHalfLine(); + _CPU_ISR_Restore(level); + + if(curr_hl>=currTiming->nhlines) curr_hl -=currTiming->nhlines; + curr_hl >>= 1; + + return curr_hl; +} + +VIRetraceCallback VIDEO_SetPreRetraceCallback(VIRetraceCallback callback) +{ + u32 level = 0; + VIRetraceCallback ret = preRetraceCB; + _CPU_ISR_Disable(level); + preRetraceCB = callback; + _CPU_ISR_Restore(level); + return ret; +} + +VIRetraceCallback VIDEO_SetPostRetraceCallback(VIRetraceCallback callback) +{ + u32 level = 0; + VIRetraceCallback ret = postRetraceCB; + _CPU_ISR_Disable(level); + postRetraceCB = callback; + _CPU_ISR_Restore(level); + return ret; +} + +u32 VIDEO_GetFrameBufferSize(GXRModeObj *rmode) { + u16 w, h; + + w = VIDEO_PadFramebufferWidth(rmode->fbWidth); + h = rmode->xfbHeight; + + if (rmode->aa) + h += 4; + + return w * h * VI_DISPLAY_PIX_SZ; +} + +void VIDEO_ClearFrameBuffer(GXRModeObj *rmode,void *fb,u32 color) +{ + __VIClearFramebuffer(fb, VIDEO_GetFrameBufferSize(rmode), color); +} + +u32 VIDEO_HaveComponentCable(void) +{ + return (_viReg[55]&0x01); +} diff --git a/wii/libogc/libogc/video_asm.S b/wii/libogc/libogc/video_asm.S new file mode 100644 index 0000000000..b8cd37329e --- /dev/null +++ b/wii/libogc/libogc/video_asm.S @@ -0,0 +1,106 @@ +/*------------------------------------------------------------- + +video_asm.S -- VIDEO subsystem assembler support + +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + + +#include + + .globl __VIClearFramebuffer + //r3 = dst, r4 = length(bytes),r5 = color +__VIClearFramebuffer: + srwi. r0,r4,8 + beq 2f + mtctr r0 + subi r3,r3,4 +1: stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + stwu r5,4(r3) + bdnz 1b +2: blr diff --git a/wii/libogc/libogc/wiilaunch.c b/wii/libogc/libogc/wiilaunch.c new file mode 100644 index 0000000000..29072cb74d --- /dev/null +++ b/wii/libogc/libogc/wiilaunch.c @@ -0,0 +1,409 @@ +/*------------------------------------------------------------- + +wiilaunch.c -- Wii NAND title launching and argument passing + +Copyright (C) 2008 +Hector Martin (marcan) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#if defined(HW_RVL) + +#include +#include +#include +#include +#include +#include "ipc.h" +#include "asm.h" +#include "processor.h" +#include "es.h" +#include "video.h" +#include "network.h" +#include "wiilaunch.h" + +static char __nandbootinfo[] ATTRIBUTE_ALIGN(32) = "/shared2/sys/NANDBOOTINFO"; +static char __stateflags[] ATTRIBUTE_ALIGN(32) = "/title/00000001/00000002/data/state.dat"; + +static int __initialized = 0; +static char args_set = 0; + +typedef struct { + u32 checksum; + u32 argsoff; + u8 unk1; + u8 unk2; + u8 apptype; + u8 titletype; + u32 launchcode; + u32 unknown[2]; + u64 launcher; + u8 argbuf[0x1000]; +} NANDBootInfo; + +typedef struct { + u32 checksum; + u8 flags; + u8 type; + u8 discstate; + u8 returnto; + u32 unknown[6]; +} StateFlags; + +#define TYPE_RETURN 3 +#define TYPE_NANDBOOT 4 +#define TYPE_SHUTDOWNSYSTEM 5 +#define RETURN_TO_MENU 0 +#define RETURN_TO_SETTINGS 1 +#define RETURN_TO_ARGS 2 + + +static NANDBootInfo nandboot ATTRIBUTE_ALIGN(32); + +static StateFlags stateflags ATTRIBUTE_ALIGN(32); + +static u32 __CalcChecksum(u32 *buf, int len) +{ + u32 sum = 0; + int i; + len = (len/4); + + for(i=1; i 0x1000) + return WII_E2BIG; + + argp = 0x1000 - argslen; + argvp = 0x1000 - buflen; + + memset(&nandboot.argbuf, 0, sizeof(nandboot.argbuf)); + + nandboot.argsoff = 0x1000 + argvp; + *((u32*)&nandboot.argbuf[argvp]) = argc; + argvp += 4; + + for(i=0; i 4) { + return WII_EINTERNAL; + } + res = ES_GetTicketViews(titleID, views, numviews); + if(res < 0) + return res; + + net_wc24cleanup(); + + if (args_set == 0) + { + memset(&nandboot,0,sizeof(NANDBootInfo)); + nandboot.apptype = 0x81; + if(titleID == 0x100000002LL) + nandboot.titletype = 4; + else + nandboot.titletype = 2; + if(ES_GetTitleID(&nandboot.launcher) < 0) + nandboot.launcher = 0x0000000100000002LL; + nandboot.checksum = __CalcChecksum((u32*)&nandboot,sizeof(NANDBootInfo)); + __WII_WriteNANDBootInfo(); + } + VIDEO_SetBlack(1); + VIDEO_Flush(); + + res = ES_LaunchTitle(titleID, &views[0]); + if(res < 0) + return res; + return WII_EINTERNAL; +} + +s32 WII_LaunchTitleWithArgs(u64 titleID, int launchcode, ...) +{ + char argv0[20]; + const char *argv[256]; + int argc = 1; + va_list args; + int ret = 0; + + if(!__initialized) + return WII_ENOTINIT; + + sprintf(argv0, "%016llx", titleID); + + argv[0] = argv0; + + va_start(args, launchcode); + + do { + argv[argc++] = va_arg(args, const char *); + } while(argv[argc - 1] != NULL); + + va_end(args); + + if(ES_GetTitleID(&nandboot.launcher) < 0) + nandboot.launcher = 0x100000002LL; + + if(titleID == 0x100000002LL) + nandboot.titletype = 4; + else + nandboot.titletype = 2; + nandboot.apptype = 0x81; + nandboot.launchcode = launchcode; + + stateflags.type = TYPE_RETURN; + stateflags.returnto = RETURN_TO_ARGS; + + __WII_SetArgs(argv); + + __WII_WriteStateFlags(); + __WII_WriteNANDBootInfo(); + + args_set = 1; + + ret = WII_LaunchTitle(titleID); + if(ret < 0) + args_set = 0; + + return ret; +} + +s32 WII_ReturnToMenu(void) +{ + if(!__initialized) + return WII_ENOTINIT; + stateflags.type = TYPE_RETURN; + stateflags.returnto = RETURN_TO_MENU; + __WII_WriteStateFlags(); + return WII_LaunchTitle(0x100000002LL); +} + +s32 WII_ReturnToSettings(void) +{ + if(!__initialized) + return WII_ENOTINIT; + stateflags.type = TYPE_RETURN; + stateflags.returnto = RETURN_TO_SETTINGS; + __WII_WriteStateFlags(); + return WII_LaunchTitle(0x100000002LL); +} + +s32 WII_ReturnToSettingsPage(const char *page) +{ + if(!__initialized) + return WII_ENOTINIT; + + return WII_LaunchTitleWithArgs(0x100000002LL, 1, page, NULL); +} + +s32 WII_OpenURL(const char *url) +{ + u32 tmdsize; + u64 tid = 0; + u64 *list; + u32 titlecount; + s32 ret; + u32 i; + + if(!__initialized) + return WII_ENOTINIT; + + ret = ES_GetNumTitles(&titlecount); + if(ret < 0) + return WII_EINTERNAL; + + list = memalign(32, titlecount * sizeof(u64) + 32); + + ret = ES_GetTitles(list, titlecount); + if(ret < 0) { + free(list); + return WII_EINTERNAL; + } + + for(i=0; i + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#if defined(HW_RVL) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define SDIO_HEAPSIZE (5*1024) + +#define PAGE_SIZE512 512 + +#define SDIOHCR_RESPONSE 0x10 +#define SDIOHCR_HOSTCONTROL 0x28 +#define SDIOHCR_POWERCONTROL 0x29 +#define SDIOHCR_CLOCKCONTROL 0x2c +#define SDIOHCR_TIMEOUTCONTROL 0x2e +#define SDIOHCR_SOFTWARERESET 0x2f + +#define SDIOHCR_HOSTCONTROL_4BIT 0x02 + +#define SDIO_DEFAULT_TIMEOUT 0xe + +#define IOCTL_SDIO_WRITEHCREG 0x01 +#define IOCTL_SDIO_READHCREG 0x02 +#define IOCTL_SDIO_READCREG 0x03 +#define IOCTL_SDIO_RESETCARD 0x04 +#define IOCTL_SDIO_WRITECREG 0x05 +#define IOCTL_SDIO_SETCLK 0x06 +#define IOCTL_SDIO_SENDCMD 0x07 +#define IOCTL_SDIO_SETBUSWIDTH 0x08 +#define IOCTL_SDIO_READMCREG 0x09 +#define IOCTL_SDIO_WRITEMCREG 0x0A +#define IOCTL_SDIO_GETSTATUS 0x0B +#define IOCTL_SDIO_GETOCR 0x0C +#define IOCTL_SDIO_READDATA 0x0D +#define IOCTL_SDIO_WRITEDATA 0x0E + +#define SDIOCMD_TYPE_BC 1 +#define SDIOCMD_TYPE_BCR 2 +#define SDIOCMD_TYPE_AC 3 +#define SDIOCMD_TYPE_ADTC 4 + +#define SDIO_RESPONSE_NONE 0 +#define SDIO_RESPONSE_R1 1 +#define SDIO_RESPONSE_R1B 2 +#define SDIO_RESPOSNE_R2 3 +#define SDIO_RESPONSE_R3 4 +#define SDIO_RESPONSE_R4 5 +#define SDIO_RESPONSE_R5 6 +#define SDIO_RESPONSE_R6 7 + +#define SDIO_CMD_GOIDLE 0x00 +#define SDIO_CMD_ALL_SENDCID 0x02 +#define SDIO_CMD_SENDRCA 0x03 +#define SDIO_CMD_SELECT 0x07 +#define SDIO_CMD_DESELECT 0x07 +#define SDIO_CMD_SENDIFCOND 0x08 +#define SDIO_CMD_SENDCSD 0x09 +#define SDIO_CMD_SENDCID 0x0A +#define SDIO_CMD_SENDSTATUS 0x0D +#define SDIO_CMD_SETBLOCKLEN 0x10 +#define SDIO_CMD_READBLOCK 0x11 +#define SDIO_CMD_READMULTIBLOCK 0x12 +#define SDIO_CMD_WRITEBLOCK 0x18 +#define SDIO_CMD_WRITEMULTIBLOCK 0x19 +#define SDIO_CMD_APPCMD 0x37 + +#define SDIO_ACMD_SETBUSWIDTH 0x06 +#define SDIO_ACMD_SENDSCR 0x33 +#define SDIO_ACMD_SENDOPCOND 0x29 + +#define SDIO_STATUS_CARD_INSERTED 0x1 +#define SDIO_STATUS_CARD_INITIALIZED 0x10000 +#define SDIO_STATUS_CARD_SDHC 0x100000 + +#define READ_BL_LEN ((u8)(__sd0_csd[5]&0x0f)) +#define WRITE_BL_LEN ((u8)(((__sd0_csd[12]&0x03)<<2)|((__sd0_csd[13]>>6)&0x03))) + +static u8 *rw_buffer = NULL; + +struct _sdiorequest +{ + u32 cmd; + u32 cmd_type; + u32 rsp_type; + u32 arg; + u32 blk_cnt; + u32 blk_size; + void *dma_addr; + u32 isdma; + u32 pad0; +}; + +struct _sdioresponse +{ + u32 rsp_fields[3]; + u32 acmd12_response; +}; + +static s32 hId = -1; + +static s32 __sd0_fd = -1; +static u16 __sd0_rca = 0; +static s32 __sd0_initialized = 0; +static s32 __sd0_sdhc = 0; +//static u8 __sd0_csd[16]; +static u8 __sd0_cid[16]; + +static s32 __sdio_initialized = 0; + +static char _sd0_fs[] ATTRIBUTE_ALIGN(32) = "/dev/sdio/slot0"; + +static s32 __sdio_sendcommand(u32 cmd,u32 cmd_type,u32 rsp_type,u32 arg,u32 blk_cnt,u32 blk_size,void *buffer,void *reply,u32 rlen) +{ + s32 ret; + STACK_ALIGN(ioctlv,iovec,3,32); + STACK_ALIGN(struct _sdiorequest,request,1,32); + STACK_ALIGN(struct _sdioresponse,response,1,32); + + request->cmd = cmd; + request->cmd_type = cmd_type; + request->rsp_type = rsp_type; + request->arg = arg; + request->blk_cnt = blk_cnt; + request->blk_size = blk_size; + request->dma_addr = buffer; + request->isdma = ((buffer!=NULL)?1:0); + request->pad0 = 0; + + if(request->isdma || __sd0_sdhc == 1) { + iovec[0].data = request; + iovec[0].len = sizeof(struct _sdiorequest); + iovec[1].data = buffer; + iovec[1].len = (blk_size*blk_cnt); + iovec[2].data = response; + iovec[2].len = sizeof(struct _sdioresponse); + ret = IOS_Ioctlv(__sd0_fd,IOCTL_SDIO_SENDCMD,2,1,iovec); + } else + ret = IOS_Ioctl(__sd0_fd,IOCTL_SDIO_SENDCMD,request,sizeof(struct _sdiorequest),response,sizeof(struct _sdioresponse)); + + if(reply && !(rlen>16)) memcpy(reply,response,rlen); + +// printf(" cmd= %08x\n", cmd); + + return ret; +} + +static s32 __sdio_setclock(u32 set) +{ + s32 ret; + STACK_ALIGN(u32,clock,1,32); + + *clock = set; + ret = IOS_Ioctl(__sd0_fd,IOCTL_SDIO_SETCLK,clock,sizeof(u32),NULL,0); + + return ret; +} +static s32 __sdio_getstatus() +{ + s32 ret; + STACK_ALIGN(u32,status,1,32); + + ret = IOS_Ioctl(__sd0_fd,IOCTL_SDIO_GETSTATUS,NULL,0,status,sizeof(u32)); + if(ret<0) return ret; + + return *status; +} + +static s32 __sdio_resetcard() +{ + s32 ret; + STACK_ALIGN(u32,status,1,32); + + __sd0_rca = 0; + ret = IOS_Ioctl(__sd0_fd,IOCTL_SDIO_RESETCARD,NULL,0,status,sizeof(u32)); + if(ret<0) return ret; + + __sd0_rca = (u16)(*status>>16); + return (*status&0xffff); +} + +static s32 __sdio_gethcr(u8 reg, u8 size, u32 *val) +{ + s32 ret; + STACK_ALIGN(u32,hcr_value,1,32); + STACK_ALIGN(u32,hcr_query,6,32); + + if(val==NULL) return IPC_EINVAL; + + *hcr_value = 0; + *val = 0; + hcr_query[0] = reg; + hcr_query[1] = 0; + hcr_query[2] = 0; + hcr_query[3] = size; + hcr_query[4] = 0; + hcr_query[5] = 0; + ret = IOS_Ioctl(__sd0_fd,IOCTL_SDIO_READHCREG,(void*)hcr_query,24,hcr_value,sizeof(u32)); + *val = *hcr_value; + + + return ret; +} + +static s32 __sdio_sethcr(u8 reg, u8 size, u32 data) +{ + s32 ret; + STACK_ALIGN(u32,hcr_query,6,32); + + hcr_query[0] = reg; + hcr_query[1] = 0; + hcr_query[2] = 0; + hcr_query[3] = size; + hcr_query[4] = data; + hcr_query[5] = 0; + ret = IOS_Ioctl(__sd0_fd,IOCTL_SDIO_WRITEHCREG,(void*)hcr_query,24,NULL,0); + + + return ret; +} + +static s32 __sdio_waithcr(u8 reg, u8 size, u8 unset, u32 mask) +{ + u32 val; + s32 ret; + s32 tries = 10; + + while(tries-- > 0) + { + ret = __sdio_gethcr(reg, size, &val); + if(ret < 0) return ret; + if((unset && !(val & mask)) || (!unset && (val & mask))) return 0; + usleep(10000); + } + + return -1; +} + +static s32 __sdio_setbuswidth(u32 bus_width) +{ + s32 ret; + u32 hc_reg = 0; + + ret = __sdio_gethcr(SDIOHCR_HOSTCONTROL, 1, &hc_reg); + if(ret<0) return ret; + + hc_reg &= 0xff; + hc_reg &= ~SDIOHCR_HOSTCONTROL_4BIT; + if(bus_width==4) hc_reg |= SDIOHCR_HOSTCONTROL_4BIT; + + return __sdio_sethcr(SDIOHCR_HOSTCONTROL, 1, hc_reg); +} + +static s32 __sd0_getrca() +{ + s32 ret; + u32 rca; + + ret = __sdio_sendcommand(SDIO_CMD_SENDRCA,0,SDIO_RESPONSE_R5,0,0,0,NULL,&rca,sizeof(rca)); + if(ret<0) return ret; + + __sd0_rca = (u16)(rca>>16); + return (rca&0xffff); +} + +static s32 __sd0_select() +{ + s32 ret; + + ret = __sdio_sendcommand(SDIO_CMD_SELECT,SDIOCMD_TYPE_AC,SDIO_RESPONSE_R1B,(__sd0_rca<<16),0,0,NULL,NULL,0); + + return ret; +} + +static s32 __sd0_deselect() +{ + s32 ret; + + ret = __sdio_sendcommand(SDIO_CMD_DESELECT,SDIOCMD_TYPE_AC,SDIO_RESPONSE_R1B,0,0,0,NULL,NULL,0); + + return ret; +} + +static s32 __sd0_setblocklength(u32 blk_len) +{ + s32 ret; + + ret = __sdio_sendcommand(SDIO_CMD_SETBLOCKLEN,SDIOCMD_TYPE_AC,SDIO_RESPONSE_R1,blk_len,0,0,NULL,NULL,0); + + return ret; +} + +static s32 __sd0_setbuswidth(u32 bus_width) +{ + u16 val; + s32 ret; + + val = 0x0000; + if(bus_width==4) val = 0x0002; + + ret = __sdio_sendcommand(SDIO_CMD_APPCMD,SDIOCMD_TYPE_AC,SDIO_RESPONSE_R1,(__sd0_rca<<16),0,0,NULL,NULL,0); + if(ret<0) return ret; + + ret = __sdio_sendcommand(SDIO_ACMD_SETBUSWIDTH,SDIOCMD_TYPE_AC,SDIO_RESPONSE_R1,val,0,0,NULL,NULL,0); + + return ret; +} + +static s32 __sd0_getcid() +{ + s32 ret; + + ret = __sdio_sendcommand(SDIO_CMD_ALL_SENDCID,0,SDIO_RESPOSNE_R2,(__sd0_rca<<16),0,0,NULL,__sd0_cid,16); + + return ret; +} + + +static bool __sd0_initio() +{ + s32 ret; + s32 tries; + u32 status; + struct _sdioresponse resp; + + __sdio_resetcard(); + status = __sdio_getstatus(); + + if(!(status & SDIO_STATUS_CARD_INSERTED)) + return false; + + if(!(status & SDIO_STATUS_CARD_INITIALIZED)) + { + // IOS doesn't like this card, so we need to convice it to accept it. + + // reopen the handle which makes IOS clean stuff up + IOS_Close(__sd0_fd); + __sd0_fd = IOS_Open(_sd0_fs,1); + + // reset the host controller + if(__sdio_sethcr(SDIOHCR_SOFTWARERESET, 1, 7) < 0) goto fail; + if(__sdio_waithcr(SDIOHCR_SOFTWARERESET, 1, 1, 7) < 0) goto fail; + + // initialize interrupts (sd_reset_card does this on success) + __sdio_sethcr(0x34, 4, 0x13f00c3); + __sdio_sethcr(0x38, 4, 0x13f00c3); + + // enable power + __sd0_sdhc = 1; + ret = __sdio_sethcr(SDIOHCR_POWERCONTROL, 1, 0xe); + if(ret < 0) goto fail; + ret = __sdio_sethcr(SDIOHCR_POWERCONTROL, 1, 0xf); + if(ret < 0) goto fail; + + // enable internal clock, wait until it gets stable and enable sd clock + ret = __sdio_sethcr(SDIOHCR_CLOCKCONTROL, 2, 0); + if(ret < 0) goto fail; + ret = __sdio_sethcr(SDIOHCR_CLOCKCONTROL, 2, 0x101); + if(ret < 0) goto fail; + ret = __sdio_waithcr(SDIOHCR_CLOCKCONTROL, 2, 0, 2); + if(ret < 0) goto fail; + ret = __sdio_sethcr(SDIOHCR_CLOCKCONTROL, 2, 0x107); + if(ret < 0) goto fail; + + // setup timeout + ret = __sdio_sethcr(SDIOHCR_TIMEOUTCONTROL, 1, SDIO_DEFAULT_TIMEOUT); + if(ret < 0) goto fail; + + // standard SDHC initialization process + ret = __sdio_sendcommand(SDIO_CMD_GOIDLE, 0, 0, 0, 0, 0, NULL, NULL, 0); + if(ret < 0) goto fail; + ret = __sdio_sendcommand(SDIO_CMD_SENDIFCOND, 0, SDIO_RESPONSE_R6, 0x1aa, 0, 0, NULL, &resp, sizeof(resp)); + if(ret < 0) goto fail; + if((resp.rsp_fields[0] & 0xff) != 0xaa) goto fail; + + tries = 10; + while(tries-- > 0) + { + ret = __sdio_sendcommand(SDIO_CMD_APPCMD, SDIOCMD_TYPE_AC,SDIO_RESPONSE_R1,0,0,0,NULL,NULL,0); + if(ret < 0) goto fail; + ret = __sdio_sendcommand(SDIO_ACMD_SENDOPCOND, 0, SDIO_RESPONSE_R3, 0x40300000, 0, 0, NULL, &resp, sizeof(resp)); + if(ret < 0) goto fail; + if(resp.rsp_fields[0] & (1 << 31)) break; + + usleep(10000); + } + if(tries < 0) goto fail; + + // FIXME: SDv2 cards which are not high-capacity won't work :/ + if(resp.rsp_fields[0] & (1 << 30)) + __sd0_sdhc = 1; + else + __sd0_sdhc = 0; + + ret = __sd0_getcid(); + if(ret < 0) goto fail; + ret = __sd0_getrca(); + if(ret < 0) goto fail; + } + else if(status&SDIO_STATUS_CARD_SDHC) + __sd0_sdhc = 1; + else + __sd0_sdhc = 0; + + ret = __sdio_setbuswidth(4); + if(ret<0) return false; + + ret = __sdio_setclock(1); + if(ret<0) return false; + + ret = __sd0_select(); + if(ret<0) return false; + + ret = __sd0_setblocklength(PAGE_SIZE512); + if(ret<0) { + ret = __sd0_deselect(); + return false; + } + + ret = __sd0_setbuswidth(4); + if(ret<0) { + ret = __sd0_deselect(); + return false; + } + __sd0_deselect(); + + __sd0_initialized = 1; + return true; + + fail: + __sdio_sethcr(SDIOHCR_SOFTWARERESET, 1, 7); + __sdio_waithcr(SDIOHCR_SOFTWARERESET, 1, 1, 7); + IOS_Close(__sd0_fd); + __sd0_fd = IOS_Open(_sd0_fs,1); + return false; +} + +bool sdio_Deinitialize() +{ + if(__sd0_fd>=0) + IOS_Close(__sd0_fd); + + __sd0_fd = -1; + __sdio_initialized = 0; + return true; +} + +bool sdio_Startup() +{ + if(__sdio_initialized==1) return true; + + if(hId<0) { + hId = iosCreateHeap(SDIO_HEAPSIZE); + if(hId<0) return false; + } + + if(rw_buffer == NULL) rw_buffer = iosAlloc(hId,(4*1024)); + if(rw_buffer == NULL) return false; + + __sd0_fd = IOS_Open(_sd0_fs,1); + + if(__sd0_fd<0) { + sdio_Deinitialize(); + return false; + } + + if(__sd0_initio()==false) { + sdio_Deinitialize(); + return false; + } + __sdio_initialized = 1; + return true; +} + + + +bool sdio_Shutdown() +{ + if(__sd0_initialized==0) return false; + + sdio_Deinitialize(); + + __sd0_initialized = 0; + return true; +} + +bool sdio_ReadSectors(sec_t sector, sec_t numSectors,void* buffer) +{ + s32 ret; + u8 *ptr; + sec_t blk_off; + + if(buffer==NULL) return false; + + ret = __sd0_select(); + if(ret<0) return false; + + if((u32)buffer & 0x1F) { + ptr = (u8*)buffer; + int secs_to_read; + while(numSectors>0) { + if(__sd0_sdhc == 0) blk_off = (sector*PAGE_SIZE512); + else blk_off = sector; + if(numSectors > 8)secs_to_read = 8; + else secs_to_read = numSectors; + ret = __sdio_sendcommand(SDIO_CMD_READMULTIBLOCK,SDIOCMD_TYPE_AC,SDIO_RESPONSE_R1,blk_off,secs_to_read,PAGE_SIZE512,rw_buffer,NULL,0); + if(ret>=0) { + memcpy(ptr,rw_buffer,PAGE_SIZE512*secs_to_read); + ptr += PAGE_SIZE512*secs_to_read; + sector+=secs_to_read; + numSectors-=secs_to_read; + } else + break; + } + } else { + if(__sd0_sdhc == 0) sector *= PAGE_SIZE512; + ret = __sdio_sendcommand(SDIO_CMD_READMULTIBLOCK,SDIOCMD_TYPE_AC,SDIO_RESPONSE_R1,sector,numSectors,PAGE_SIZE512,buffer,NULL,0); + } + + __sd0_deselect(); + + return (ret>=0); +} + +bool sdio_WriteSectors(sec_t sector, sec_t numSectors,const void* buffer) +{ + s32 ret; + u8 *ptr; + u32 blk_off; + + if(buffer==NULL) return false; + + ret = __sd0_select(); + if(ret<0) return false; + + if((u32)buffer & 0x1F) { + ptr = (u8*)buffer; + int secs_to_write; + while(numSectors>0) { + if(__sd0_sdhc == 0) blk_off = (sector*PAGE_SIZE512); + else blk_off = sector; + if(numSectors > 8)secs_to_write = 8; + else secs_to_write = numSectors; + memcpy(rw_buffer,ptr,PAGE_SIZE512*secs_to_write); + ret = __sdio_sendcommand(SDIO_CMD_WRITEMULTIBLOCK,SDIOCMD_TYPE_AC,SDIO_RESPONSE_R1,blk_off,secs_to_write,PAGE_SIZE512,rw_buffer,NULL,0); + if(ret>=0) { + ptr += PAGE_SIZE512*secs_to_write; + sector+=secs_to_write; + numSectors-=secs_to_write; + } else + break; + } + } else { + if(__sd0_sdhc == 0) sector *= PAGE_SIZE512; + ret = __sdio_sendcommand(SDIO_CMD_WRITEMULTIBLOCK,SDIOCMD_TYPE_AC,SDIO_RESPONSE_R1,sector,numSectors,PAGE_SIZE512,(char *)buffer,NULL,0); + } + + __sd0_deselect(); + + return (ret>=0); +} + +bool sdio_ClearStatus() +{ + return true; +} + +bool sdio_IsInserted() +{ + return ((__sdio_getstatus() & SDIO_STATUS_CARD_INSERTED) == + SDIO_STATUS_CARD_INSERTED); +} + +bool sdio_IsInitialized() +{ + return ((__sdio_getstatus() & SDIO_STATUS_CARD_INITIALIZED) == + SDIO_STATUS_CARD_INITIALIZED); +} + +const DISC_INTERFACE __io_wiisd = { + DEVICE_TYPE_WII_SD, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_SD, + (FN_MEDIUM_STARTUP)&sdio_Startup, + (FN_MEDIUM_ISINSERTED)&sdio_IsInserted, + (FN_MEDIUM_READSECTORS)&sdio_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&sdio_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&sdio_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&sdio_Shutdown +}; + +#endif diff --git a/wii/libogc/libogc_license.txt b/wii/libogc/libogc_license.txt new file mode 100644 index 0000000000..a3dfd0581b --- /dev/null +++ b/wii/libogc/libogc_license.txt @@ -0,0 +1,20 @@ + Copyright (C) 2004 - 2009 + Michael Wiedenbauer (shagkur) + Dave Murphy (WinterMute) + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any + damages arising from the use of this software. + + Permission is granted to anyone to use this software for any + purpose, including commercial applications, and to alter it and + redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + 3. This notice may not be removed or altered from any source + distribution. diff --git a/wii/libogc/libwiikeyboard/keyboard.c b/wii/libogc/libwiikeyboard/keyboard.c new file mode 100644 index 0000000000..b7a67f5588 --- /dev/null +++ b/wii/libogc/libwiikeyboard/keyboard.c @@ -0,0 +1,664 @@ +/*------------------------------------------------------------- + +keyboard.c -- keyboard event system + +Copyright (C) 2008, 2009 +DAVY Guillaume davyg2@gmail.com +Brian Johnson brijohn@gmail.com +dhewg + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "wsksymvar.h" + +#define KBD_THREAD_STACKSIZE (1024 * 4) +#define KBD_THREAD_PRIO 64 +#define KBD_THREAD_UDELAY (1000 * 10) +#define KBD_THREAD_KBD_SCAN_INTERVAL (3 * 100) + +static lwp_queue _queue; +static lwp_t _kbd_thread = LWP_THREAD_NULL; +static lwp_t _kbd_buf_thread = LWP_THREAD_NULL; +static bool _kbd_thread_running = false; +static bool _kbd_thread_quit = false; + +keysym_t ksym_upcase(keysym_t); + +extern const struct wscons_keydesc ukbd_keydesctab[]; + +static struct wskbd_mapdata _ukbd_keymapdata = { + ukbd_keydesctab, + KB_NONE +}; + +struct nameint { + int value; + char *name; +}; + +static struct nameint kbdenc_tab[] = { + KB_ENCTAB +}; + +static struct nameint kbdvar_tab[] = { + KB_VARTAB +}; + +static int _sc_maplen = 0; /* number of entries in sc_map */ +static struct wscons_keymap *_sc_map = 0; /* current translation map */ + +static u16 _modifiers; + +static int _composelen; /* remaining entries in _composebuf */ +static keysym_t _composebuf[2]; + +typedef struct { + u8 keycode; + u16 symbol; +} _keyheld; + +#define MAXHELD 8 +static _keyheld _held[MAXHELD]; + +typedef struct { + lwp_node node; + keyboard_event event; +} _node; + +static keyPressCallback _readKey_cb = NULL; + +static u8 *_kbd_stack[KBD_THREAD_STACKSIZE] ATTRIBUTE_ALIGN(8); +static u8 *_kbd_buf_stack[KBD_THREAD_STACKSIZE] ATTRIBUTE_ALIGN(8); + +static kbd_t _get_keymap_by_name(const char *identifier) { + char name[64]; + u8 i, j; + kbd_t encoding, variant; + + kbd_t res = KB_NONE; + + if (!identifier || (strlen(identifier) < 2)) + return res; + + i = 0; + for (i = 0; ukbd_keydesctab[i].name != 0; ++i) { + if (ukbd_keydesctab[i].name & KB_HANDLEDBYWSKBD) + continue; + + encoding = KB_ENCODING(ukbd_keydesctab[i].name); + variant = KB_VARIANT(ukbd_keydesctab[i].name); + + name[0] = 0; + for (j = 0; j < sizeof(kbdenc_tab) / sizeof(struct nameint); ++j) + if (encoding == kbdenc_tab[j].value) { + strcpy(name, kbdenc_tab[j].name); + break; + } + + if (strlen(name) < 1) + continue; + + for (j = 0; j < sizeof(kbdvar_tab) / sizeof(struct nameint); ++j) + if (variant & kbdvar_tab[j].value) { + strcat(name, "-"); + strcat(name, kbdvar_tab[j].name); + } + + if (!strcmp(identifier, name)) { + res = ukbd_keydesctab[i].name; + break; + } + } + + return res; +} + +//Add an event to the event queue +static s32 _kbd_addEvent(const keyboard_event *event) { + _node *n = malloc(sizeof(_node)); + n->event = *event; + + __lwp_queue_append(&_queue, (lwp_node*) n); + + return 1; +} + +void update_modifier(u_int type, int toggle, int mask) { + if (toggle) { + if (type == KEYBOARD_PRESSED) + _modifiers ^= mask; + } else { + if (type == KEYBOARD_RELEASED) + _modifiers &= ~mask; + else + _modifiers |= mask; + } +} + +//Event callback, gets called when an event occurs in usbkeyboard +static void _kbd_event_cb(USBKeyboard_event kevent) +{ + keyboard_event event; + struct wscons_keymap kp; + keysym_t *group; + int gindex; + keysym_t ksym; + int i; + + switch (kevent.type) { + case USBKEYBOARD_DISCONNECTED: + event.type = KEYBOARD_DISCONNECTED; + event.modifiers = 0; + event.keycode = 0; + event.symbol = 0; + + _kbd_addEvent(&event); + + return; + + case USBKEYBOARD_PRESSED: + event.type = KEYBOARD_PRESSED; + break; + + case USBKEYBOARD_RELEASED: + event.type = KEYBOARD_RELEASED; + break; + + default: + return; + } + + event.keycode = kevent.keyCode; + + wskbd_get_mapentry(&_ukbd_keymapdata, event.keycode, &kp); + + /* Now update modifiers */ + switch (kp.group1[0]) { + case KS_Shift_L: + update_modifier(event.type, 0, MOD_SHIFT_L); + break; + + case KS_Shift_R: + update_modifier(event.type, 0, MOD_SHIFT_R); + break; + + case KS_Shift_Lock: + update_modifier(event.type, 1, MOD_SHIFTLOCK); + break; + + case KS_Caps_Lock: + update_modifier(event.type, 1, MOD_CAPSLOCK); + USBKeyboard_SetLed(USBKEYBOARD_LEDCAPS, + MOD_ONESET(_modifiers, MOD_CAPSLOCK)); + break; + + case KS_Control_L: + update_modifier(event.type, 0, MOD_CONTROL_L); + break; + + case KS_Control_R: + update_modifier(event.type, 0, MOD_CONTROL_R); + break; + + case KS_Alt_L: + update_modifier(event.type, 0, MOD_META_L); + break; + + case KS_Alt_R: + update_modifier(event.type, 0, MOD_META_R); + break; + + case KS_Mode_switch: + update_modifier(event.type, 0, MOD_MODESHIFT); + break; + + case KS_Mode_Lock: + update_modifier(event.type, 1, MOD_MODELOCK); + break; + + case KS_Num_Lock: + update_modifier(event.type, 1, MOD_NUMLOCK); + USBKeyboard_SetLed(USBKEYBOARD_LEDNUM, + MOD_ONESET(_modifiers, MOD_NUMLOCK)); + break; + + case KS_Hold_Screen: + update_modifier(event.type, 1, MOD_HOLDSCREEN); + USBKeyboard_SetLed(USBKEYBOARD_LEDSCROLL, + MOD_ONESET(_modifiers, MOD_HOLDSCREEN)); + break; + } + + /* Get the keysym */ + if (_modifiers & (MOD_MODESHIFT|MOD_MODELOCK) && + !MOD_ONESET(_modifiers, MOD_ANYCONTROL)) + group = &kp.group2[0]; + else + group = &kp.group1[0]; + + if ((_modifiers & MOD_NUMLOCK) && + KS_GROUP(group[1]) == KS_GROUP_Keypad) { + gindex = !MOD_ONESET(_modifiers, MOD_ANYSHIFT); + ksym = group[gindex]; + } else { + /* CAPS alone should only affect letter keys */ + if ((_modifiers & (MOD_CAPSLOCK | MOD_ANYSHIFT)) == + MOD_CAPSLOCK) { + gindex = 0; + ksym = ksym_upcase(group[0]); + } else { + gindex = MOD_ONESET(_modifiers, MOD_ANYSHIFT); + ksym = group[gindex]; + } + } + + /* Process compose sequence and dead accents */ + switch (KS_GROUP(ksym)) { + case KS_GROUP_Mod: + if (ksym == KS_Multi_key) { + update_modifier(KEYBOARD_PRESSED, 0, MOD_COMPOSE); + _composelen = 2; + } + break; + + case KS_GROUP_Dead: + if (event.type != KEYBOARD_PRESSED) + return; + + if (_composelen == 0) { + update_modifier(KEYBOARD_PRESSED, 0, MOD_COMPOSE); + _composelen = 1; + _composebuf[0] = ksym; + + return; + } + break; + } + + if ((event.type == KEYBOARD_PRESSED) && (_composelen > 0)) { + /* + * If the compose key also serves as AltGr (i.e. set to both + * KS_Multi_key and KS_Mode_switch), and would provide a valid, + * distinct combination as AltGr, leave compose mode. + */ + if (_composelen == 2 && group == &kp.group2[0]) { + if (kp.group1[gindex] != kp.group2[gindex]) + _composelen = 0; + } + + if (_composelen != 0) { + _composebuf[2 - _composelen] = ksym; + if (--_composelen == 0) { + ksym = wskbd_compose_value(_composebuf); + update_modifier(KEYBOARD_RELEASED, 0, MOD_COMPOSE); + } else { + return; + } + } + } + + // store up to MAXHELD pressed events to match the symbol for release + switch (KS_GROUP(ksym)) { + case KS_GROUP_Ascii: + case KS_GROUP_Keypad: + case KS_GROUP_Function: + if (event.type == KEYBOARD_PRESSED) { + for (i = 0; i < MAXHELD; ++i) { + if (_held[i].keycode == 0) { + _held[i].keycode = event.keycode; + _held[i].symbol = ksym; + + break; + } + } + } else { + for (i = 0; i < MAXHELD; ++i) { + if (_held[i].keycode == event.keycode) { + ksym = _held[i].symbol; + _held[i].keycode = 0; + _held[i].symbol = 0; + + break; + } + } + } + + break; + } + + event.symbol = ksym; + event.modifiers = _modifiers; + + _kbd_addEvent(&event); + + return; +} + +//This function call usb function to check if a new keyboard is connected +static s32 _kbd_scan_for_keyboard(void) +{ + s32 ret; + keyboard_event event; + + ret = USBKeyboard_Open(&_kbd_event_cb); + + if (ret < 0) + return ret; + + _modifiers = 0; + _composelen = 0; + memset(_held, 0, sizeof(_held)); + + USBKeyboard_SetLed(USBKEYBOARD_LEDNUM, true); + USBKeyboard_SetLed(USBKEYBOARD_LEDCAPS, true); + USBKeyboard_SetLed(USBKEYBOARD_LEDSCROLL, true); + usleep(200 * 1000); + USBKeyboard_SetLed(USBKEYBOARD_LEDNUM, false); + USBKeyboard_SetLed(USBKEYBOARD_LEDCAPS, false); + USBKeyboard_SetLed(USBKEYBOARD_LEDSCROLL, false); + + event.type = KEYBOARD_CONNECTED; + event.modifiers = 0; + event.keycode = 0; + + _kbd_addEvent(&event); + + return ret; +} + +static void * _kbd_thread_func(void *arg) { + u32 turns = 0; + + while (!_kbd_thread_quit) { + // scan for new attached keyboards + if ((turns % KBD_THREAD_KBD_SCAN_INTERVAL) == 0) { + if (!USBKeyboard_IsConnected()) + _kbd_scan_for_keyboard(); + + turns = 0; + } + turns++; + + USBKeyboard_Scan(); + usleep(KBD_THREAD_UDELAY); + } + + return NULL; +} + +struct { + vu8 head; + vu8 tail; + char buf[256]; +} _keyBuffer; + +static void * _kbd_buf_thread_func(void *arg) { + keyboard_event event; + while (!_kbd_thread_quit) { + if (((_keyBuffer.tail+1)&255) != _keyBuffer.head) { + if ( KEYBOARD_GetEvent(&event)) { + if (event.type == KEYBOARD_PRESSED) { + _keyBuffer.buf[_keyBuffer.tail] = event.symbol; + _keyBuffer.tail++; + } + } + } + usleep(KBD_THREAD_UDELAY); + } + return NULL; +} + +static ssize_t _keyboardRead(struct _reent *r, void *unused, char *ptr, size_t len) +{ + ssize_t count = len; + while ( count > 0 ) { + if (_keyBuffer.head != _keyBuffer.tail) { + char key = _keyBuffer.buf[_keyBuffer.head]; + *ptr++ = key; + if (_readKey_cb != NULL) _readKey_cb(key); + _keyBuffer.head++; + count--; + } + } + return len; +} + +static const devoptab_t std_in = +{ + "stdin", + 0, + NULL, + NULL, + NULL, + _keyboardRead, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +//Initialize USB and USB_KEYBOARD and the event queue +s32 KEYBOARD_Init(keyPressCallback keypress_cb) +{ + int fd; + struct stat st; + char keymap[64]; + size_t i; + + if (USB_Initialize() != IPC_OK) + return -1; + + if (USBKeyboard_Initialize() != IPC_OK) { + return -2; + } + + if (_ukbd_keymapdata.layout == KB_NONE) { + keymap[0] = 0; + fd = open("/wiikbd.map", O_RDONLY); + + if ((fd > 0) && !fstat(fd, &st)) { + if ((st.st_size > 0) && (st.st_size < 64) && + (st.st_size == read(fd, keymap, st.st_size))) { + keymap[63] = 0; + for (i = 0; i < 64; ++i) { + if ((keymap[i] != '-') && (isalpha((int)keymap[i]) == 0)) { + keymap[i] = 0; + break; + } + } + } + + close(fd); + } + + _ukbd_keymapdata.layout = _get_keymap_by_name(keymap); + } + + if (_ukbd_keymapdata.layout == KB_NONE) { + switch (CONF_GetLanguage()) { + case CONF_LANG_GERMAN: + _ukbd_keymapdata.layout = KB_DE; + break; + + case CONF_LANG_JAPANESE: + _ukbd_keymapdata.layout = KB_JP; + break; + + case CONF_LANG_FRENCH: + _ukbd_keymapdata.layout = KB_FR; + break; + + case CONF_LANG_SPANISH: + _ukbd_keymapdata.layout = KB_ES; + break; + + case CONF_LANG_ITALIAN: + _ukbd_keymapdata.layout = KB_IT; + break; + + case CONF_LANG_DUTCH: + _ukbd_keymapdata.layout = KB_NL; + break; + + case CONF_LANG_SIMP_CHINESE: + case CONF_LANG_TRAD_CHINESE: + case CONF_LANG_KOREAN: + default: + _ukbd_keymapdata.layout = KB_US; + break; + } + } + + if (wskbd_load_keymap(&_ukbd_keymapdata, &_sc_map, &_sc_maplen) < 0) { + _ukbd_keymapdata.layout = KB_NONE; + + return -4; + } + + __lwp_queue_init_empty(&_queue); + + if (!_kbd_thread_running) { + // start the keyboard thread + _kbd_thread_quit = false; + + memset(_kbd_stack, 0, KBD_THREAD_STACKSIZE); + + s32 res = LWP_CreateThread(&_kbd_thread, _kbd_thread_func, NULL, + _kbd_stack, KBD_THREAD_STACKSIZE, + KBD_THREAD_PRIO); + + if (res) { + USBKeyboard_Close(); + + return -6; + } + + if(keypress_cb) + { + _keyBuffer.head = 0; + _keyBuffer.tail = 0; + + res = LWP_CreateThread(&_kbd_buf_thread, _kbd_buf_thread_func, NULL, + _kbd_buf_stack, KBD_THREAD_STACKSIZE, + KBD_THREAD_PRIO); + if(res) { + _kbd_thread_quit = true; + + LWP_JoinThread(_kbd_thread, NULL); + + USBKeyboard_Close(); + KEYBOARD_FlushEvents(); + USBKeyboard_Deinitialize(); + _kbd_thread_running = false; + return -6; + } + + devoptab_list[STD_IN] = &std_in; + setvbuf(stdin, NULL , _IONBF, 0); + _readKey_cb = keypress_cb; + } + _kbd_thread_running = true; + } + return 0; +} + +//Deinitialize USB and USB_KEYBOARD and the event queue +s32 KEYBOARD_Deinit(void) +{ + if (_kbd_thread_running) { + _kbd_thread_quit = true; + + if(_kbd_thread != LWP_THREAD_NULL) + LWP_JoinThread(_kbd_thread, NULL); + if(_kbd_buf_thread != LWP_THREAD_NULL) + LWP_JoinThread(_kbd_buf_thread, NULL); + + _kbd_thread_running = false; + } + + USBKeyboard_Close(); + KEYBOARD_FlushEvents(); + USBKeyboard_Deinitialize(); + + if (_sc_map) { + free(_sc_map); + _sc_map = NULL; + _sc_maplen = 0; + } + + return 1; +} + +//Get the first event of the event queue +s32 KEYBOARD_GetEvent(keyboard_event *event) +{ + _node *n = (_node *) __lwp_queue_get(&_queue); + + if (!n) + return 0; + + *event = n->event; + + free(n); + + return 1; +} + +//Flush all pending events +s32 KEYBOARD_FlushEvents(void) +{ + s32 res; + _node *n; + + res = 0; + while (true) { + n = (_node *) __lwp_queue_get(&_queue); + + if (!n) + break; + + free(n); + res++; + } + + return res; +} + diff --git a/wii/libogc/libwiikeyboard/ukbdmap.c b/wii/libogc/libwiikeyboard/ukbdmap.c new file mode 100644 index 0000000000..133a7d791e --- /dev/null +++ b/wii/libogc/libwiikeyboard/ukbdmap.c @@ -0,0 +1,1132 @@ +/* + * THIS FILE IS AUTOMAGICALLY GENERATED. DO NOT EDIT. + * + * generated by: + * OpenBSD: makemap.awk,v 1.10 2009/01/11 16:54:53 miod Exp + * generated from: + */ + +/* + * PLEASE DO NOT FORGET TO REGEN + * sys/dev/usb/ukbdmap.c + * AFTER ANY CHANGES TO THIS FILE! + */ + +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Juergen Hannken-Illjes. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "wsksymvar.h" + +#define KC(n) KS_KEYCODE(n) + +static const keysym_t ukbd_keydesc_us[] = { +/* pos command normal shifted */ + KC(4), KS_a, + KC(5), KS_b, + KC(6), KS_c, + KC(7), KS_d, + KC(8), KS_e, + KC(9), KS_f, + KC(10), KS_g, + KC(11), KS_h, + KC(12), KS_i, + KC(13), KS_j, + KC(14), KS_k, + KC(15), KS_l, + KC(16), KS_m, + KC(17), KS_n, + KC(18), KS_o, + KC(19), KS_p, + KC(20), KS_q, + KC(21), KS_r, + KC(22), KS_s, + KC(23), KS_t, + KC(24), KS_u, + KC(25), KS_v, + KC(26), KS_w, + KC(27), KS_x, + KC(28), KS_y, + KC(29), KS_z, + KC(30), KS_1, KS_exclam, + KC(31), KS_2, KS_at, + KC(32), KS_3, KS_numbersign, + KC(33), KS_4, KS_dollar, + KC(34), KS_5, KS_percent, + KC(35), KS_6, KS_asciicircum, + KC(36), KS_7, KS_ampersand, + KC(37), KS_8, KS_asterisk, + KC(38), KS_9, KS_parenleft, + KC(39), KS_0, KS_parenright, + KC(40), KS_Return, + KC(41), KS_Cmd_Debugger,KS_Escape, + KC(42), KS_Cmd_ResetEmul,KS_BackSpace, + KC(43), KS_Tab, + KC(44), KS_space, + KC(45), KS_minus, KS_underscore, + KC(46), KS_equal, KS_plus, + KC(47), KS_bracketleft, KS_braceleft, + KC(48), KS_bracketright,KS_braceright, + KC(49), KS_backslash, KS_bar, + KC(50), KS_backslash, KS_bar, + KC(51), KS_semicolon, KS_colon, + KC(52), KS_apostrophe, KS_quotedbl, + KC(53), KS_grave, KS_asciitilde, + KC(54), KS_comma, KS_less, + KC(55), KS_period, KS_greater, + KC(56), KS_slash, KS_question, + KC(57), KS_Caps_Lock, + KC(58), KS_Cmd_Screen0, KS_f1, + KC(59), KS_Cmd_Screen1, KS_f2, + KC(60), KS_Cmd_Screen2, KS_f3, + KC(61), KS_Cmd_Screen3, KS_f4, + KC(62), KS_Cmd_Screen4, KS_f5, + KC(63), KS_Cmd_Screen5, KS_f6, + KC(64), KS_Cmd_Screen6, KS_f7, + KC(65), KS_Cmd_Screen7, KS_f8, + KC(66), KS_Cmd_Screen8, KS_f9, + KC(67), KS_Cmd_Screen9, KS_f10, + KC(68), KS_Cmd_Screen10,KS_f11, + KC(69), KS_Cmd_Screen11,KS_f12, + KC(70), KS_Print_Screen, + KC(71), KS_Hold_Screen, + KC(72), KS_Pause, /*Break*/ + KC(73), KS_Insert, + KC(74), KS_Home, + KC(75), KS_Cmd_ScrollBack,KS_Prior, + KC(76), KS_Cmd_ResetEmul,KS_Delete, + KC(77), KS_End, + KC(78), KS_Cmd_ScrollFwd,KS_Next, + KC(79), KS_Right, + KC(80), KS_Left, + KC(81), KS_Down, + KC(82), KS_Up, + KC(83), KS_Num_Lock, + KC(84), KS_KP_Divide, + KC(85), KS_KP_Multiply, + KC(86), KS_KP_Subtract, + KC(87), KS_KP_Add, + KC(88), KS_KP_Enter, + KC(89), KS_KP_End, KS_KP_1, + KC(90), KS_KP_Down, KS_KP_2, + KC(91), KS_KP_Next, KS_KP_3, + KC(92), KS_KP_Left, KS_KP_4, + KC(93), KS_KP_Begin, KS_KP_5, + KC(94), KS_KP_Right, KS_KP_6, + KC(95), KS_KP_Home, KS_KP_7, + KC(96), KS_KP_Up, KS_KP_8, + KC(97), KS_KP_Prior, KS_KP_9, + KC(98), KS_KP_Insert, KS_KP_0, + KC(99), KS_Cmd_KbdReset,KS_KP_Delete, + KC(101), KS_Menu, + KC(116), KS_Open, + KC(117), KS_Help, + KC(118), KS_Props, + KC(119), KS_Front, + KC(120), KS_Cmd, + KC(121), KS_Again, + KC(122), KS_Undo, + KC(123), KS_Cut, + KC(124), KS_Copy, + KC(125), KS_Paste, + KC(126), KS_Find, + KC(127), KS_AudioMute, + KC(128), KS_AudioRaise, + KC(129), KS_AudioLower, + KC(224), KS_Cmd1, KS_Control_L, + KC(225), KS_Shift_L, + KC(226), KS_Cmd2, KS_Alt_L, + KC(227), KS_Meta_L, + KC(228), KS_Cmd1, KS_Control_R, + KC(229), KS_Shift_R, + KC(230), KS_Cmd2, KS_Alt_R, KS_Multi_key, + KC(231), KS_Meta_R, +}; + +static const keysym_t ukbd_keydesc_de[] = { +/* pos normal shifted altgr shift-altgr */ + KC(16), KS_m, KS_M, KS_mu, + KC(20), KS_q, KS_Q, KS_at, + KC(28), KS_z, + KC(29), KS_y, + KC(31), KS_2, KS_quotedbl, KS_twosuperior, + KC(32), KS_3, KS_section, KS_threesuperior, + KC(35), KS_6, KS_ampersand, + KC(36), KS_7, KS_slash, KS_braceleft, + KC(37), KS_8, KS_parenleft, KS_bracketleft, + KC(38), KS_9, KS_parenright, KS_bracketright, + KC(39), KS_0, KS_equal, KS_braceright, + KC(45), KS_ssharp, KS_question, KS_backslash, + KC(46), KS_dead_acute, KS_dead_grave, + KC(47), KS_udiaeresis, + KC(48), KS_plus, KS_asterisk, KS_dead_tilde, + KC(49), KS_numbersign, KS_apostrophe, + KC(50), KS_numbersign, KS_apostrophe, + KC(51), KS_odiaeresis, + KC(52), KS_adiaeresis, + KC(53), KS_dead_circumflex,KS_dead_abovering, + KC(54), KS_comma, KS_semicolon, + KC(55), KS_period, KS_colon, + KC(56), KS_minus, KS_underscore, + KC(100), KS_less, KS_greater, KS_bar, KS_brokenbar, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_de_nodead[] = { +/* pos normal shifted altgr shift-altgr */ + KC(46), KS_apostrophe, KS_grave, + KC(48), KS_plus, KS_asterisk, KS_asciitilde, + KC(53), KS_asciicircum, KS_degree, +}; + +static const keysym_t ukbd_keydesc_dk[] = { +/* pos normal shifted altgr shift-altgr */ + KC(31), KS_2, KS_quotedbl, KS_at, + KC(32), KS_3, KS_numbersign, KS_sterling, + KC(33), KS_4, KS_currency, KS_dollar, + KC(35), KS_6, KS_ampersand, + KC(36), KS_7, KS_slash, KS_braceleft, + KC(37), KS_8, KS_parenleft, KS_bracketleft, + KC(38), KS_9, KS_parenright, KS_bracketright, + KC(39), KS_0, KS_equal, KS_braceright, + KC(45), KS_plus, KS_question, + KC(46), KS_dead_acute, KS_dead_grave, KS_bar, + KC(47), KS_aring, + KC(48), KS_dead_diaeresis,KS_dead_circumflex,KS_dead_tilde, + KC(49), KS_apostrophe, KS_asterisk, + KC(50), KS_apostrophe, KS_asterisk, + KC(51), KS_ae, + KC(52), KS_oslash, + KC(53), KS_onehalf, KS_paragraph, + KC(54), KS_comma, KS_semicolon, + KC(55), KS_period, KS_colon, + KC(56), KS_minus, KS_underscore, + KC(100), KS_less, KS_greater, KS_backslash, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_dk_nodead[] = { +/* pos normal shifted altgr shift-altgr */ + KC(46), KS_apostrophe, KS_grave, KS_bar, + KC(48), KS_diaeresis, KS_asciicircum, KS_asciitilde, +}; + +static const keysym_t ukbd_keydesc_sv[] = { +/* pos normal shifted altgr shift-altgr */ + KC(45), KS_plus, KS_question, KS_backslash, + KC(48), KS_dead_diaeresis,KS_dead_circumflex,KS_dead_tilde, + KC(51), KS_odiaeresis, + KC(52), KS_adiaeresis, + KC(53), KS_section, KS_onehalf, + KC(100), KS_less, KS_greater, KS_bar, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_sv_nodead[] = { +/* pos normal shifted altgr shift-altgr */ + KC(46), KS_apostrophe, KS_grave, KS_bar, + KC(48), KS_diaeresis, KS_asciicircum, KS_asciitilde, +}; + +static const keysym_t ukbd_keydesc_no[] = { +/* pos normal shifted altgr shift-altgr */ + KC(46), KS_backslash, KS_dead_grave, KS_dead_acute, + KC(48), KS_dead_diaeresis,KS_dead_circumflex,KS_dead_tilde, + KC(51), KS_oslash, + KC(52), KS_ae, + KC(53), KS_bar, KS_paragraph, + KC(100), KS_less, KS_greater, +}; + +static const keysym_t ukbd_keydesc_no_nodead[] = { +/* pos normal shifted altgr shift-altgr */ + KC(46), KS_backslash, KS_grave, KS_acute, + KC(48), KS_diaeresis, KS_asciicircum, KS_asciitilde, +}; + +static const keysym_t ukbd_keydesc_fr[] = { +/* pos normal shifted altgr shift-altgr */ + KC(4), KS_q, + KC(16), KS_comma, KS_question, + KC(20), KS_a, + KC(26), KS_z, + KC(29), KS_w, + KC(30), KS_ampersand, KS_1, + KC(31), KS_eacute, KS_2, KS_asciitilde, + KC(32), KS_quotedbl, KS_3, KS_numbersign, + KC(33), KS_apostrophe, KS_4, KS_braceleft, + KC(34), KS_parenleft, KS_5, KS_bracketleft, + KC(35), KS_minus, KS_6, KS_bar, + KC(36), KS_egrave, KS_7, KS_grave, + KC(37), KS_underscore, KS_8, KS_backslash, + KC(38), KS_ccedilla, KS_9, KS_asciicircum, + KC(39), KS_agrave, KS_0, KS_at, + KC(45), KS_parenright, KS_degree, KS_bracketright, + KC(46), KS_equal, KS_plus, KS_braceright, + KC(47), KS_dead_circumflex,KS_dead_diaeresis, + KC(48), KS_dollar, KS_sterling, KS_currency, + KC(49), KS_asterisk, KS_mu, + KC(50), KS_asterisk, KS_mu, + KC(51), KS_m, + KC(52), KS_ugrave, KS_percent, + KC(53), KS_twosuperior, + KC(54), KS_semicolon, KS_period, + KC(55), KS_colon, KS_slash, + KC(56), KS_exclam, KS_section, + KC(100), KS_less, KS_greater, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_fr_apple[] = { + KC(5), KS_b, KS_B, KS_ssharp, + KC(8), KS_e, KS_E, KS_ecircumflex, KS_Ecircumflex, + KC(11), KS_h, KS_H, KS_Igrave, KS_Icircumflex, + KC(12), KS_i, KS_I, KS_icircumflex, KS_idiaeresis, + KC(13), KS_j, KS_J, KS_Idiaeresis, KS_Iacute, + KC(14), KS_k, KS_K, KS_Egrave, KS_Ediaeresis, + KC(15), KS_l, KS_L, KS_voidSymbol, KS_bar, + KC(16), KS_comma, KS_question, KS_voidSymbol, KS_questiondown, + KC(17), KS_n, KS_N, KS_asciitilde, + KC(20), KS_a, KS_A, KS_ae, KS_AE, + KC(21), KS_r, KS_R, KS_registered, KS_comma, + KC(22), KS_s, KS_S, KS_Ograve, + KC(26), KS_z, KS_Z, KS_Acircumflex, KS_Aring, + KC(28), KS_y, KS_Y, KS_Uacute, + KC(31), KS_eacute, KS_2, KS_ediaeresis, + KC(32), KS_quotedbl, KS_3, + KC(33), KS_apostrophe, KS_4, + KC(34), KS_parenleft, KS_5, KS_braceleft, KS_bracketleft, + KC(35), KS_section, KS_6, + KC(36), KS_egrave, KS_7, KS_guillemotleft, + KS_guillemotright, + KC(37), KS_exclam, KS_8, + KC(38), KS_ccedilla, KS_9, KS_Ccedilla, KS_Aacute, + KC(37), KS_exclam, KS_8, KS_exclamdown, KS_Ucircumflex, + KC(39), KS_agrave, KS_0, KS_oslash, KS_Ooblique, + KC(45), KS_parenright, KS_degree, KS_braceright, KS_bracketright, + KC(46), KS_minus, KS_underscore, + KC(47), KS_dead_circumflex, KS_dead_diaeresis, + KS_ocircumflex, KS_Ocircumflex, + KC(48), KS_dollar, KS_asterisk, KS_cent, KS_yen, + KC(50), KS_grave, KS_sterling, KS_at, KS_numbersign, + KC(51), KS_m, KS_M, KS_mu, KS_Oacute, + KC(52), KS_ugrave, KS_percent, KS_Ugrave, + KC(53), KS_at, KS_numbersign, + KC(55), KS_colon, KS_slash, KS_voidSymbol, KS_backslash, + KC(56), KS_equal, KS_plus, + KC(103), KS_KP_Equal, + KC(231), KS_Mode_switch, KS_Multi_key, +}; + +/* + * fr-dvorak-be'po layout, simplified map, per http://www.clavier-dvorak.org/ + * (the complete map is still a moving target) + */ +static const keysym_t ukbd_keydesc_fr_dvorak_bepo[] = { + /* oe ligature */ + /* euro currency */ + KC(4), KS_a, KS_A, KS_ae, KS_AE, + KC(5), KS_k, KS_K, KS_asciitilde, + KC(6), KS_x, KS_X, KS_braceright, + KC(7), KS_i, KS_I, KS_dead_diaeresis, + KC(8), KS_p, KS_P, KS_ampersand, + KC(9), KS_e, KS_E, + KC(10), KS_comma, KS_semicolon, + KC(11), KS_c, + KC(12), KS_d, + KC(13), KS_t, + KC(14), KS_s, + KC(15), KS_r, + KC(16), KS_q, KS_Q, + KC(17), KS_apostrophe, KS_question, + KC(18), KS_l, + KC(19), KS_j, + KC(20), KS_b, KS_B, KS_bar, + KC(21), KS_o, KS_O, + KC(22), KS_u, KS_U, KS_ugrave, KS_Ugrave, + KC(23), KS_egrave, KS_Egrave, KS_dead_grave, + KC(24), KS_v, + KC(25), KS_period, KS_colon, /*ellipsis*/ + KC(26), KS_eacute, KS_Eacute, KS_dead_acute, + KC(27), KS_y, KS_Y, KS_braceleft, + KC(28), KS_dead_circumflex,KS_exclam, + KC(29), KS_agrave, KS_Agrave, KS_backslash, + KC(30), KS_quotedbl, KS_1, KS_hyphen, + KC(31), KS_guillemotleft,KS_2, KS_less, + KC(32), KS_guillemotright,KS_3, KS_greater, + KC(33), KS_parenleft, KS_4, KS_bracketleft, + KC(34), KS_parenright, KS_5, KS_bracketright, + KC(35), KS_at, KS_6, + KC(36), KS_plus, KS_7, + KC(37), KS_minus, KS_8, + KC(38), KS_slash, KS_9, + KC(39), KS_asterisk, KS_0, + KC(44), KS_space, KS_nobreakspace,KS_underscore, + KC(45), KS_equal, KS_asciicircum, + KC(46), KS_percent, KS_grave, + KC(47), KS_z, + KC(48), KS_w, + KC(49), KS_ccedilla, KS_Ccedilla, + KC(50), KS_ccedilla, KS_Ccedilla, + KC(51), KS_n, + KC(52), KS_m, + KC(53), KS_dollar, KS_numbersign, + KC(54), KS_g, KS_G, KS_mu, + KC(55), KS_h, + KC(56), KS_f, + KC(100), KS_egrave, KS_Egrave, KS_slash, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_it[] = { +/* pos normal shifted altgr shift-altgr */ + KC(31), KS_2, KS_quotedbl, KS_twosuperior, + KC(32), KS_3, KS_sterling, KS_threesuperior, + KC(34), KS_5, KS_percent, + KC(35), KS_6, KS_ampersand, + KC(36), KS_7, KS_slash, + KC(37), KS_8, KS_parenleft, + KC(38), KS_9, KS_parenright, + KC(39), KS_0, KS_equal, + KC(45), KS_apostrophe, KS_question, + KC(46), KS_igrave, KS_asciicircum, + KC(47), KS_egrave, KS_eacute, KS_braceleft, KS_bracketleft, + KC(48), KS_plus, KS_asterisk, KS_braceright, KS_bracketright, + KC(49), KS_ugrave, KS_section, + KC(50), KS_ugrave, KS_section, + KC(51), KS_ograve, KS_Ccedilla, KS_at, + KC(52), KS_agrave, KS_degree, KS_numbersign, + KC(53), KS_backslash, KS_bar, + KC(54), KS_comma, KS_semicolon, + KC(55), KS_period, KS_colon, + KC(56), KS_minus, KS_underscore, + KC(100), KS_less, KS_greater, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_uk[] = { +/* pos normal shifted altgr shift-altgr */ + KC(30), KS_1, KS_exclam, KS_plusminus, KS_exclamdown, + KC(31), KS_2, KS_quotedbl, KS_twosuperior, KS_cent, + KC(32), KS_3, KS_sterling, KS_threesuperior, + KC(33), KS_4, KS_dollar, KS_acute, KS_currency, + KC(34), KS_5, KS_percent, KS_mu, KS_yen, + KC(35), KS_6, KS_asciicircum, KS_paragraph, + KC(36), KS_7, KS_ampersand, KS_periodcentered,KS_brokenbar, + KC(37), KS_8, KS_asterisk, KS_cedilla, KS_ordfeminine, + KC(38), KS_9, KS_parenleft, KS_onesuperior, KS_diaeresis, + KC(39), KS_0, KS_parenright, KS_masculine, KS_copyright, + KC(45), KS_minus, KS_underscore, KS_hyphen, KS_ssharp, + KC(46), KS_equal, KS_plus, KS_onehalf, KS_guillemotleft, + KC(49), KS_numbersign, KS_asciitilde, KS_sterling, KS_thorn, + KC(50), KS_numbersign, KS_asciitilde, KS_sterling, KS_thorn, + KC(52), KS_apostrophe, KS_at, KS_section, KS_Agrave, + KC(53), KS_grave, KS_grave, KS_agrave, KS_agrave, + KC(100), KS_backslash, KS_bar, KS_Udiaeresis, +}; + +static const keysym_t ukbd_keydesc_jp[] = { +/* pos normal shifted altgr shift-altgr */ + KC(31), KS_2, KS_quotedbl, + KC(35), KS_6, KS_ampersand, + KC(36), KS_7, KS_apostrophe, + KC(37), KS_8, KS_parenleft, + KC(38), KS_9, KS_parenright, + KC(39), KS_0, + KC(45), KS_minus, KS_equal, + KC(46), KS_asciicircum, KS_asciitilde, + KC(47), KS_at, KS_grave, + KC(48), KS_bracketleft, KS_braceleft, + KC(49), KS_bracketright,KS_braceright, + KC(50), KS_bracketright,KS_braceright, + KC(51), KS_semicolon, KS_plus, + KC(52), KS_colon, KS_asterisk, + KC(53), KS_Zenkaku_Hankaku,/*replacegrave/tilde*/ + KC(135), KS_Hiragana_Katakana, + KC(136), KS_backslash, KS_underscore, + KC(137), KS_Henkan, + KC(138), KS_Muhenkan, + KC(139), KS_backslash, KS_bar, +}; + +static const keysym_t ukbd_keydesc_es[] = { +/* pos normal shifted altgr shift-altgr */ + KC(30), KS_1, KS_exclam, KS_bar, + KC(31), KS_2, KS_quotedbl, KS_at, + KC(32), KS_3, KS_periodcentered,KS_numbersign, + KC(33), KS_4, KS_dollar, KS_asciitilde, + KC(35), KS_6, KS_ampersand, + KC(36), KS_7, KS_slash, + KC(37), KS_8, KS_parenleft, + KC(38), KS_9, KS_parenright, + KC(39), KS_0, KS_equal, + KC(45), KS_apostrophe, KS_question, + KC(46), KS_exclamdown, KS_questiondown, + KC(47), KS_dead_grave, KS_dead_circumflex,KS_bracketleft, + KC(48), KS_plus, KS_asterisk, KS_bracketright, + KC(49), KS_ccedilla, KS_Ccedilla, KS_braceright, + KC(50), KS_ccedilla, KS_Ccedilla, KS_braceright, + KC(51), KS_ntilde, + KC(52), KS_dead_acute, KS_dead_diaeresis,KS_braceleft, + KC(53), KS_degree, KS_ordfeminine, KS_backslash, + KC(54), KS_comma, KS_semicolon, + KC(55), KS_period, KS_colon, + KC(56), KS_minus, KS_underscore, + KC(100), KS_less, KS_greater, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_lt[] = { +/* pos normal shifted altgr shift-altgr */ + KC(8), KS_e, KS_E, KS_currency, + KC(9), KS_L7_scaron, KS_L7_Scaron, + KC(20), KS_L7_aogonek, KS_L7_Aogonek, + KC(26), KS_L7_zcaron, KS_L7_Zcaron, + KC(27), KS_L7_umacron, KS_L7_Umacron, + KC(30), KS_exclam, KS_1, KS_at, + KC(31), KS_minus, KS_2, KS_underscore, + KC(32), KS_slash, KS_3, KS_numbersign, + KC(33), KS_semicolon, KS_4, KS_dollar, + KC(34), KS_colon, KS_5, KS_paragraph, + KC(35), KS_comma, KS_6, KS_asciicircum, + KC(36), KS_period, KS_7, KS_ampersand, + KC(37), KS_equal, KS_8, KS_asterisk, + KC(38), KS_bracketleft, KS_9, KS_parenleft, + KC(39), KS_bracketright,KS_0, KS_parenright, + KC(44), KS_space, KS_space, KS_nobreakspace, + KC(45), KS_question, KS_plus, KS_apostrophe, + KC(46), KS_x, KS_X, KS_percent, + KC(47), KS_L7_iogonek, KS_L7_Iogonek, KS_braceleft, + KC(48), KS_w, KS_W, KS_braceright, + KC(49), KS_q, KS_Q, KS_bar, + KC(50), KS_q, KS_Q, KS_bar, + KC(51), KS_L7_uogonek, KS_L7_Uogonek, + KC(52), KS_L7_edot, KS_L7_Edot, KS_quotedbl, + KC(53), KS_grave, KS_asciitilde, + KC(54), KS_L7_ccaron, KS_L7_Ccaron, KS_L7_dbllow9quot, + KC(55), KS_f, KS_F, KS_L7_leftdblquot, + KC(56), KS_L7_eogonek, KS_L7_Eogonek, KS_backslash, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_be[] = { +/* pos normal shifted altgr shift-altgr */ + KC(4), KS_q, + KC(16), KS_comma, KS_question, + KC(20), KS_a, + KC(26), KS_z, + KC(29), KS_w, + KC(30), KS_ampersand, KS_1, KS_bar, + KC(31), KS_eacute, KS_2, KS_at, + KC(32), KS_quotedbl, KS_3, KS_numbersign, + KC(33), KS_apostrophe, KS_4, + KC(34), KS_parenleft, KS_5, + KC(35), KS_section, KS_6, KS_asciicircum, + KC(36), KS_egrave, KS_7, + KC(37), KS_exclam, KS_8, + KC(38), KS_ccedilla, KS_9, KS_braceleft, + KC(39), KS_agrave, KS_0, KS_braceright, + KC(45), KS_parenright, KS_degree, + KC(46), KS_minus, KS_underscore, + KC(47), KS_dead_circumflex,KS_dead_diaeresis,KS_bracketleft, + KC(48), KS_dollar, KS_asterisk, KS_bracketright, + KC(49), KS_mu, KS_sterling, KS_grave, + KC(50), KS_mu, KS_sterling, KS_grave, + KC(51), KS_m, + KC(52), KS_ugrave, KS_percent, KS_acute, + KC(53), KS_twosuperior, KS_threesuperior, + KC(54), KS_semicolon, KS_period, + KC(55), KS_colon, KS_slash, + KC(56), KS_equal, KS_plus, KS_asciitilde, + KC(100), KS_less, KS_greater, KS_backslash, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + + +static const keysym_t ukbd_keydesc_us_dvorak[] = { +/* pos command normal shifted */ + KC(5), KS_x, + KC(6), KS_j, + KC(7), KS_e, + KC(8), KS_period, KS_greater, + KC(9), KS_u, + KC(10), KS_i, + KC(11), KS_d, + KC(12), KS_c, + KC(13), KS_h, + KC(14), KS_t, + KC(15), KS_n, + KC(17), KS_b, + KC(18), KS_r, + KC(19), KS_l, + KC(20), KS_apostrophe, KS_quotedbl, + KC(21), KS_p, + KC(22), KS_o, + KC(23), KS_y, + KC(24), KS_g, + KC(25), KS_k, + KC(26), KS_comma, KS_less, + KC(27), KS_q, + KC(28), KS_f, + KC(29), KS_semicolon, KS_colon, + KC(45), KS_bracketleft, KS_braceleft, + KC(46), KS_bracketright,KS_braceright, + KC(47), KS_slash, KS_question, + KC(48), KS_equal, KS_plus, + KC(51), KS_s, + KC(52), KS_minus, KS_underscore, + KC(54), KS_w, + KC(55), KS_v, + KC(56), KS_z, +}; + +static const keysym_t ukbd_keydesc_swapctrlcaps[] = { +/* pos command normal shifted */ + KC(57), KS_Cmd1, KS_Control_L, + KC(224), KS_Caps_Lock, +}; + +static const keysym_t ukbd_keydesc_iopener[] = { +/* pos command normal shifted */ + KC(58), KS_Cmd_Debugger,KS_Escape, + KC(59), KS_Cmd_Screen0, KS_f1, + KC(60), KS_Cmd_Screen1, KS_f2, + KC(61), KS_Cmd_Screen2, KS_f3, + KC(62), KS_Cmd_Screen3, KS_f4, + KC(63), KS_Cmd_Screen4, KS_f5, + KC(64), KS_Cmd_Screen5, KS_f6, + KC(65), KS_Cmd_Screen6, KS_f7, + KC(66), KS_Cmd_Screen7, KS_f8, + KC(67), KS_Cmd_Screen8, KS_f9, + KC(68), KS_Cmd_Screen9, KS_f10, + KC(69), KS_f11, +}; + +static const keysym_t ukbd_keydesc_ru[] = { +/* pos normal shifted altgr shift-altgr */ + KC(4), KS_a, KS_A, KS_Cyrillic_ef, KS_Cyrillic_EF, + KC(5), KS_b, KS_B, KS_Cyrillic_i, KS_Cyrillic_I, + KC(6), KS_c, KS_C, KS_Cyrillic_es, KS_Cyrillic_ES, + KC(7), KS_d, KS_D, KS_Cyrillic_ve, KS_Cyrillic_VE, + KC(8), KS_e, KS_E, KS_Cyrillic_u, KS_Cyrillic_U, + KC(9), KS_f, KS_F, KS_Cyrillic_a, KS_Cyrillic_A, + KC(10), KS_g, KS_G, KS_Cyrillic_pe, KS_Cyrillic_PE, + KC(11), KS_h, KS_H, KS_Cyrillic_er, KS_Cyrillic_ER, + KC(12), KS_i, KS_I, KS_Cyrillic_sha,KS_Cyrillic_SHA, + KC(13), KS_j, KS_J, KS_Cyrillic_o, KS_Cyrillic_O, + KC(14), KS_k, KS_K, KS_Cyrillic_el, KS_Cyrillic_EL, + KC(15), KS_l, KS_L, KS_Cyrillic_de, KS_Cyrillic_DE, + KC(16), KS_m, KS_M, KS_Cyrillic_ssighn,KS_Cyrillic_SSIGHN, + KC(17), KS_n, KS_N, KS_Cyrillic_te, KS_Cyrillic_TE, + KC(18), KS_o, KS_O, KS_Cyrillic_scha,KS_Cyrillic_SCHA, + KC(19), KS_p, KS_P, KS_Cyrillic_ze, KS_Cyrillic_ZE, + KC(20), KS_q, KS_Q, KS_Cyrillic_ishort,KS_Cyrillic_ISHORT, + KC(21), KS_r, KS_R, KS_Cyrillic_ka, KS_Cyrillic_KA, + KC(22), KS_s, KS_S, KS_Cyrillic_yeru,KS_Cyrillic_YERU, + KC(23), KS_t, KS_T, KS_Cyrillic_ie, KS_Cyrillic_IE, + KC(24), KS_u, KS_U, KS_Cyrillic_ge, KS_Cyrillic_GE, + KC(25), KS_v, KS_V, KS_Cyrillic_em, KS_Cyrillic_EM, + KC(26), KS_w, KS_W, KS_Cyrillic_tse,KS_Cyrillic_TSE, + KC(27), KS_x, KS_X, KS_Cyrillic_che,KS_Cyrillic_CHE, + KC(28), KS_y, KS_Y, KS_Cyrillic_en, KS_Cyrillic_EN, + KC(29), KS_z, KS_Z, KS_Cyrillic_ya, KS_Cyrillic_YA, + KC(35), KS_6, KS_asciicircum, KS_6, KS_comma, + KC(36), KS_7, KS_ampersand, KS_7, KS_period, + KC(47), KS_bracketleft, KS_braceleft, KS_Cyrillic_ha, KS_Cyrillic_HA, + KC(48), KS_bracketright,KS_braceright, KS_Cyrillic_hsighn,KS_Cyrillic_HSIGHN, + KC(51), KS_semicolon, KS_colon, KS_Cyrillic_zhe,KS_Cyrillic_ZHE, + KC(52), KS_apostrophe, KS_quotedbl, KS_Cyrillic_e, KS_Cyrillic_E, + KC(54), KS_comma, KS_less, KS_Cyrillic_be, KS_Cyrillic_BE, + KC(55), KS_period, KS_greater, KS_Cyrillic_yu, KS_Cyrillic_YU, + KC(56), KS_slash, KS_question, KS_Cyrillic_yo, KS_Cyrillic_YO, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_ua[] = { +/* pos normal shifted altgr shift-altgr */ + KC(4), KS_a, KS_A, KS_Cyrillic_ef, KS_Cyrillic_EF, + KC(5), KS_b, KS_B, KS_Cyrillic_i, KS_Cyrillic_I, + KC(6), KS_c, KS_C, KS_Cyrillic_es, KS_Cyrillic_ES, + KC(7), KS_d, KS_D, KS_Cyrillic_ve, KS_Cyrillic_VE, + KC(8), KS_e, KS_E, KS_Cyrillic_u, KS_Cyrillic_U, + KC(9), KS_f, KS_F, KS_Cyrillic_a, KS_Cyrillic_A, + KC(10), KS_g, KS_G, KS_Cyrillic_pe, KS_Cyrillic_PE, + KC(11), KS_h, KS_H, KS_Cyrillic_er, KS_Cyrillic_ER, + KC(12), KS_i, KS_I, KS_Cyrillic_sha,KS_Cyrillic_SHA, + KC(13), KS_j, KS_J, KS_Cyrillic_o, KS_Cyrillic_O, + KC(14), KS_k, KS_K, KS_Cyrillic_el, KS_Cyrillic_EL, + KC(15), KS_l, KS_L, KS_Cyrillic_de, KS_Cyrillic_DE, + KC(16), KS_m, KS_M, KS_Cyrillic_ssighn,KS_Cyrillic_SSIGHN, + KC(17), KS_n, KS_N, KS_Cyrillic_te, KS_Cyrillic_TE, + KC(18), KS_o, KS_O, KS_Cyrillic_scha,KS_Cyrillic_SCHA, + KC(19), KS_p, KS_P, KS_Cyrillic_ze, KS_Cyrillic_ZE, + KC(20), KS_q, KS_Q, KS_Cyrillic_ishort,KS_Cyrillic_ISHORT, + KC(21), KS_r, KS_R, KS_Cyrillic_ka, KS_Cyrillic_KA, + KC(22), KS_s, KS_S, KS_Cyrillic_yeru,KS_Cyrillic_YERU, + KC(23), KS_t, KS_T, KS_Cyrillic_ie, KS_Cyrillic_IE, + KC(24), KS_u, KS_U, KS_Cyrillic_ge, KS_Cyrillic_GE, + KC(25), KS_v, KS_V, KS_Cyrillic_em, KS_Cyrillic_EM, + KC(26), KS_w, KS_W, KS_Cyrillic_tse,KS_Cyrillic_TSE, + KC(27), KS_x, KS_X, KS_Cyrillic_che,KS_Cyrillic_CHE, + KC(28), KS_y, KS_Y, KS_Cyrillic_en, KS_Cyrillic_EN, + KC(29), KS_z, KS_Z, KS_Cyrillic_ya, KS_Cyrillic_YA, + KC(35), KS_6, KS_asciicircum, KS_6, KS_comma, + KC(36), KS_7, KS_ampersand, KS_7, KS_period, + KC(45), KS_minus, KS_underscore, KS_Cyrillic_iukr,KS_Cyrillic_IUKR, + KC(46), KS_equal, KS_plus, KS_Cyrillic_yeukr,KS_Cyrillic_YEUKR, + KC(47), KS_bracketleft, KS_braceleft, KS_Cyrillic_ha, KS_Cyrillic_HA, + KC(48), KS_bracketright,KS_braceright, KS_Cyrillic_hsighn,KS_Cyrillic_HSIGHN, + KC(49), KS_backslash, KS_bar, KS_Cyrillic_yi, KS_Cyrillic_YI, + KC(50), KS_backslash, KS_bar, KS_Cyrillic_yi, KS_Cyrillic_YI, + KC(51), KS_semicolon, KS_colon, KS_Cyrillic_zhe,KS_Cyrillic_ZHE, + KC(52), KS_apostrophe, KS_quotedbl, KS_Cyrillic_e, KS_Cyrillic_E, + KC(53), KS_grave, KS_asciitilde, KS_Cyrillic_gheukr,KS_Cyrillic_GHEUKR, + KC(54), KS_comma, KS_less, KS_Cyrillic_be, KS_Cyrillic_BE, + KC(55), KS_period, KS_greater, KS_Cyrillic_yu, KS_Cyrillic_YU, + KC(56), KS_slash, KS_question, KS_Cyrillic_yo, KS_Cyrillic_YO, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_sg[] = { +/* pos normal shifted altgr shift-altgr */ + KC(8), KS_e, KS_E, KS_currency, + KC(28), KS_z, + KC(29), KS_y, + KC(30), KS_1, KS_plus, KS_bar, + KC(31), KS_2, KS_quotedbl, KS_at, + KC(32), KS_3, KS_asterisk, KS_numbersign, + KC(33), KS_4, KS_ccedilla, + KC(35), KS_6, KS_ampersand, KS_notsign, + KC(36), KS_7, KS_slash, KS_brokenbar, + KC(37), KS_8, KS_parenleft, KS_cent, + KC(38), KS_9, KS_parenright, + KC(39), KS_0, KS_equal, + KC(45), KS_apostrophe, KS_question, KS_dead_acute, + KC(46), KS_dead_circumflex,KS_dead_grave,KS_dead_tilde, + KC(47), KS_udiaeresis, KS_egrave, KS_bracketleft, + KC(48), KS_dead_diaeresis,KS_exclam, KS_bracketright, + KC(49), KS_dollar, KS_sterling, KS_braceright, + KC(50), KS_dollar, KS_sterling, KS_braceright, + KC(51), KS_odiaeresis, KS_eacute, + KC(52), KS_adiaeresis, KS_agrave, KS_braceleft, + KC(53), KS_section, KS_degree, KS_dead_abovering, + KC(54), KS_comma, KS_semicolon, + KC(55), KS_period, KS_colon, + KC(56), KS_minus, KS_underscore, + KC(100), KS_less, KS_greater, KS_backslash, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_sg_nodead[] = { +/* pos normal shifted altgr shift-altgr */ + KC(45), KS_apostrophe, KS_question, KS_acute, + KC(46), KS_asciicircum, KS_grave, KS_asciitilde, + KC(48), KS_diaeresis, KS_exclam, KS_bracketright, +}; + +static const keysym_t ukbd_keydesc_sf[] = { +/* pos normal shifted altgr shift-altgr */ + KC(47), KS_egrave, KS_udiaeresis, KS_bracketleft, + KC(51), KS_eacute, KS_odiaeresis, + KC(52), KS_agrave, KS_adiaeresis, KS_braceleft, +}; + +static const keysym_t ukbd_keydesc_pt[] = { +/* pos normal shifted altgr shift-altgr */ + KC(31), KS_2, KS_quotedbl, KS_at, + KC(32), KS_3, KS_numbersign, KS_sterling, + KC(35), KS_6, KS_ampersand, + KC(36), KS_7, KS_slash, KS_braceleft, + KC(37), KS_8, KS_parenleft, KS_bracketleft, + KC(38), KS_9, KS_parenright, KS_bracketright, + KC(39), KS_0, KS_equal, KS_braceright, + KC(45), KS_apostrophe, KS_question, + KC(46), KS_less, KS_greater, + KC(47), KS_plus, KS_asterisk, + KC(48), KS_dead_acute, KS_dead_grave, + KC(49), KS_dead_tilde, KS_dead_circumflex, + KC(50), KS_dead_tilde, KS_dead_circumflex, + KC(51), KS_ccedilla, KS_Ccedilla, + KC(52), KS_masculine, KS_ordfeminine, + KC(53), KS_backslash, KS_bar, + KC(54), KS_comma, KS_semicolon, + KC(55), KS_period, KS_colon, + KC(56), KS_minus, KS_underscore, + KC(100), KS_less, KS_greater, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_pt_apple[] = { +/* pos normal shifted */ + KC(46), KS_plus, KS_asterisk, + KC(47), KS_masculine, KS_ordfeminine, + KC(50), KS_backslash, KS_bar, + KC(52), KS_dead_tilde, KS_dead_circumflex +}; + +static const keysym_t ukbd_keydesc_la[] = { +/* pos normal shifted altgr shift-altgr */ + KC(20), KS_q, KS_Q, KS_at, + KC(30), KS_1, KS_exclam, + KC(31), KS_2, KS_quotedbl, + KC(32), KS_3, KS_numbersign, + KC(35), KS_6, KS_ampersand, + KC(36), KS_7, KS_slash, + KC(37), KS_8, KS_parenleft, + KC(38), KS_9, KS_parenright, + KC(39), KS_0, KS_equal, + KC(45), KS_apostrophe, KS_question, KS_backslash, + KC(46), KS_questiondown,KS_exclamdown, + KC(47), KS_dead_acute, KS_dead_diaeresis, + KC(48), KS_plus, KS_asterisk, KS_asciitilde, + KC(49), KS_braceright, KS_bracketright,KS_dead_grave, + KC(50), KS_braceright, KS_bracketright,KS_dead_grave, + KC(51), KS_ntilde, + KC(52), KS_braceleft, KS_bracketleft, KS_dead_circumflex, + KC(53), KS_bar, KS_degree, KS_notsign, + KC(54), KS_comma, KS_semicolon, + KC(55), KS_period, KS_colon, + KC(56), KS_minus, KS_underscore, + KC(100), KS_less, KS_greater, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_br[] = { +/* pos normal shifted altgr shift-altgr */ + KC(30), KS_1, KS_exclam, KS_onesuperior, + KC(31), KS_2, KS_at, KS_twosuperior, + KC(32), KS_3, KS_numbersign, KS_threesuperior, + KC(33), KS_4, KS_dollar, KS_sterling, + KC(34), KS_5, KS_percent, KS_cent, + KC(35), KS_6, KS_dead_diaeresis,KS_notsign, + KC(46), KS_equal, KS_plus, KS_section, + KC(47), KS_dead_acute, KS_dead_grave, + KC(48), KS_bracketleft, KS_braceleft, KS_ordfeminine, + KC(49), KS_bracketright,KS_braceright, KS_masculine, + KC(50), KS_bracketright,KS_braceright, KS_masculine, + KC(51), KS_ccedilla, KS_Ccedilla, + KC(52), KS_dead_tilde, KS_dead_circumflex, + KC(53), KS_apostrophe, KS_quotedbl, + KC(56), KS_semicolon, KS_colon, + KC(99), KS_KP_Delete, KS_KP_Decimal, + KC(100), KS_backslash, KS_bar, + KC(136), KS_slash, KS_question, KS_degree, +}; + +static const keysym_t ukbd_keydesc_tr[] = { +/* pos normal shifted altgr shift-altgr */ + KC(12), KS_L5_idotless, KS_I, + KC(20), KS_q, KS_Q, KS_at, + KC(31), KS_2, KS_apostrophe, KS_sterling, + KC(32), KS_3, KS_asciicircum, KS_numbersign, + KC(33), KS_4, KS_plus, KS_dollar, + KC(34), KS_5, KS_percent, KS_onehalf, + KC(35), KS_6, KS_ampersand, + KC(36), KS_7, KS_slash, KS_braceleft, + KC(37), KS_8, KS_parenleft, KS_bracketleft, + KC(38), KS_9, KS_parenright, KS_bracketright, + KC(39), KS_0, KS_equal, KS_braceright, + KC(45), KS_asterisk, KS_question, KS_backslash, + KC(46), KS_minus, KS_underscore, + KC(47), KS_L5_gbreve, KS_L5_Gbreve, KS_dead_diaeresis, + KC(48), KS_udiaeresis, KS_Udiaeresis, KS_asciitilde, + KC(49), KS_comma, KS_semicolon, KS_dead_grave, + KC(50), KS_comma, KS_semicolon, KS_dead_grave, + KC(51), KS_L5_scedilla, KS_L5_Scedilla, KS_dead_acute, + KC(52), KS_i, KS_L5_Idotabove, + KC(53), KS_quotedbl, KS_eacute, + KC(54), KS_odiaeresis, KS_Odiaeresis, + KC(55), KS_ccedilla, KS_Ccedilla, + KC(56), KS_period, KS_colon, + KC(100), KS_less, KS_greater, KS_bar, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_tr_nodead[] = { +/* pos normal shifted altgr shift-altgr */ + KC(47), KS_L5_gbreve, KS_L5_Gbreve, + KC(49), KS_comma, KS_semicolon, KS_grave, + KC(50), KS_comma, KS_semicolon, KS_grave, + KC(51), KS_L5_scedilla, KS_L5_Scedilla, KS_apostrophe, +}; + +static const keysym_t ukbd_keydesc_pl[] = { +/* pos normal shifted altgr shift-altgr */ + KC(4), KS_a, KS_A, KS_L2_aogonek, KS_L2_Aogonek, + KC(6), KS_c, KS_C, KS_L2_cacute, KS_L2_Cacute, + KC(8), KS_e, KS_E, KS_L2_eogonek, KS_L2_Eogonek, + KC(15), KS_l, KS_L, KS_L2_lstroke, KS_L2_Lstroke, + KC(17), KS_n, KS_N, KS_L2_nacute, KS_L2_Nacute, + KC(18), KS_o, KS_O, KS_oacute, KS_Oacute, + KC(22), KS_s, KS_S, KS_L2_sacute, KS_L2_Sacute, + KC(27), KS_x, KS_X, KS_L2_zacute, KS_L2_Zacute, + KC(29), KS_z, KS_Z, KS_L2_zdotabove,KS_L2_Zdotabove, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_hu[] = { +/* pos normal shifted altgr shift-altgr */ + KC(5), KS_b, KS_B, KS_braceleft, + KC(6), KS_c, KS_C, KS_ampersand, + KC(9), KS_f, KS_F, KS_bracketleft, + KC(10), KS_g, KS_G, KS_bracketright, + KC(12), KS_i, KS_I, KS_iacute, + KC(13), KS_j, KS_J, KS_iacute, + KC(17), KS_n, KS_N, KS_braceright, + KC(20), KS_q, KS_Q, KS_backslash, + KC(25), KS_v, KS_V, KS_at, + KC(26), KS_w, KS_W,KS_bar, + KC(27), KS_x, KS_X, KS_numbersign, + KC(28), KS_z, KS_Z, + KC(29), KS_y, KS_Y, KS_greater, + KC(30), KS_1, KS_apostrophe, KS_asciitilde, + KC(31), KS_2, KS_quotedbl, + KC(32), KS_3, KS_plus, KS_asciicircum, + KC(33), KS_4, KS_exclam, + KC(34), KS_5, KS_percent, + KC(35), KS_6, KS_slash, + KC(36), KS_7, KS_equal,KS_grave, + KC(37), KS_8, KS_parenleft, + KC(38), KS_9, KS_parenright, KS_acute, + KC(39), KS_odiaeresis, KS_Odiaeresis, + KC(45), KS_udiaeresis, KS_Udiaeresis, + KC(46), KS_oacute, KS_Oacute, + KC(47), KS_odoubleacute,KS_Odoubleacute,KS_division, + KC(48), KS_uacute, KS_Uacute, KS_multiply, + KC(49), KS_udoubleacute,KS_Udoubleacute,KS_currency, + KC(50), KS_udoubleacute,KS_Udoubleacute,KS_currency, + KC(51), KS_eacute, KS_Eacute, KS_dollar, + KC(52), KS_aacute, KS_Aacute, KS_ssharp, + KC(53), KS_0, KS_section, + KC(54), KS_comma, KS_question, KS_semicolon, + KC(55), KS_period, KS_colon, + KC(56), KS_minus, KS_underscore, KS_asterisk, + KC(100), KS_iacute, KS_Iacute, KS_less, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_si[]= +{ +/* pos normal shifted altgr shift-altgr */ + KC(5), KS_b, KS_B, KS_braceleft, + KC(9), KS_f, KS_F, KS_bracketleft, + KC(10), KS_g, KS_G, KS_bracketright, + KC(14), KS_k, KS_K, KS_L2_lstroke, + KC(15), KS_l, KS_L, KS_L2_Lstroke, + KC(16), KS_m, KS_M, KS_section, + KC(17), KS_n, KS_N, KS_braceright, + KC(20), KS_q, KS_Q, KS_backslash, + KC(25), KS_v, KS_V, KS_at, + KC(26), KS_w, KS_W, KS_bar, + KC(28), KS_z, KS_Z, + KC(29), KS_y, KS_Y, + KC(30), KS_1, KS_exclam, KS_asciitilde, + KC(31), KS_2, KS_quotedbl, KS_L2_caron, + KC(32), KS_3, KS_numbersign, KS_asciicircum, + KC(33), KS_4, KS_dollar, KS_L2_breve, + KC(34), KS_5, KS_percent, KS_degree, + KC(35), KS_6, KS_ampersand, KS_L2_ogonek, + KC(36), KS_7, KS_slash, KS_grave, + KC(37), KS_8, KS_parenleft, KS_L2_dotabove, + KC(38), KS_9, KS_parenright, KS_acute, + KC(39), KS_0, KS_equal, KS_L2_dblacute, + KC(45), KS_apostrophe, KS_question, KS_diaeresis, + KC(46), KS_plus, KS_asterisk, KS_cedilla, + KC(47), KS_L2_scaron, KS_L2_Scaron, KS_division, + KC(48), KS_L2_dstroke, KS_L2_Dstroke, KS_multiply, + KC(49), KS_L2_zcaron, KS_L2_Zcaron, KS_currency, + KC(50), KS_L2_zcaron, KS_L2_Zcaron, KS_currency, + KC(51), KS_L2_ccaron, KS_L2_Ccaron, + KC(52), KS_L2_cacute, KS_L2_Cacute, KS_ssharp, + KC(53), KS_cedilla, KS_diaeresis, + KC(54), KS_comma, KS_semicolon, + KC(55), KS_period, KS_colon, + KC(56), KS_minus, KS_underscore, + KC(100), KS_less, KS_greater, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_cf[] = { +/* pos normal shifted altgr shift-altgr */ + KC(16), KS_m, KS_M, KS_mu, + KC(18), KS_o, KS_O, KS_section, + KC(19), KS_p, KS_P, KS_paragraph, + KC(30), KS_1, KS_exclam, KS_plusminus, + KC(31), KS_2, KS_quotedbl, KS_at, + KC(32), KS_3, KS_slash, KS_sterling, + KC(33), KS_4, KS_dollar, KS_cent, + KC(34), KS_5, KS_percent, KS_diaeresis, + KC(35), KS_6, KS_question, KS_macron, + KC(36), KS_7, KS_ampersand, KS_brokenbar, + KC(37), KS_8, KS_asterisk, KS_twosuperior, + KC(38), KS_9, KS_parenleft, KS_threesuperior, + KC(39), KS_0, KS_parenright, KS_onequarter, + KC(45), KS_minus, KS_underscore, KS_onehalf, + KC(46), KS_equal, KS_plus, KS_threequarters, + KC(47), KS_dead_circumflex,KS_dead_circumflex,KS_bracketleft, + KC(48), KS_dead_cedilla,KS_dead_diaeresis,KS_bracketright, + KC(49), KS_less, KS_greater, KS_braceright, + KC(50), KS_less, KS_greater, KS_braceright, + KC(51), KS_semicolon, KS_colon, KS_asciitilde, + KC(52), KS_dead_grave, KS_dead_grave, KS_braceleft, + KC(53), KS_numbersign, KS_bar, KS_backslash, + KC(54), KS_comma, KS_apostrophe, KS_hyphen, + KC(55), KS_period, KS_period, + KC(56), KS_eacute, KS_Eacute, KS_dead_acute, + KC(100), KS_guillemotleft,KS_guillemotright,KS_degree, + KC(230), KS_Mode_switch,KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_cf_nodead[] = { +/* pos normal shifted altgr shift-altgr */ + KC(47), KS_asciicircum,KS_asciicircum,KS_bracketleft, + KC(48), KS_cedilla, KS_diaeresis, KS_bracketright, + KC(52), KS_grave, KS_grave, KS_braceleft, + KC(56), KS_eacute, KS_Eacute, KS_acute, +}; + +static const keysym_t ukbd_keydesc_lv[] = { +/* pos normal shifted altgr shift-altgr */ + KC(4), KS_a, KS_A, KS_L7_amacron, KS_L7_Amacron, + KC(6), KS_c, KS_C, KS_L7_ccaron, KS_L7_Ccaron, + KC(8), KS_e, KS_E, KS_L7_emacron, KS_L7_Emacron, + KC(10), KS_g, KS_G, KS_L7_gcedilla, KS_L7_Gcedilla, + KC(12), KS_i, KS_I, KS_L7_imacron, KS_L7_Imacron, + KC(14), KS_k, KS_K, KS_L7_kcedilla, KS_L7_Kcedilla, + KC(15), KS_l, KS_L, KS_L7_lcedilla, KS_L7_Lcedilla, + KC(17), KS_n, KS_N, KS_L7_ncedilla, KS_L7_Ncedilla, + KC(18), KS_o, KS_O, KS_L7_omacron, KS_L7_Omacron, + KC(22), KS_s, KS_S, KS_L7_scaron, KS_L7_Scaron, + KC(24), KS_u, KS_U, KS_L7_umacron, KS_L7_Umacron, + KC(29), KS_z, KS_Z, KS_L7_zcaron, KS_L7_Zcaron, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_nl[] = { +/* pos normal shifted altgr shift-altgr */ + KC(6), KS_c, KS_C, KS_cent, + KC(16), KS_m, KS_M, KS_mu, + KC(21), KS_r, KS_R, KS_paragraph, + KC(22), KS_s, KS_S, KS_ssharp, + KC(27), KS_x, KS_X, KS_guillemotright, + KC(29), KS_z, KS_Z, KS_guillemotleft, + KC(30), KS_1, KS_exclam, KS_onesuperior, + KC(31), KS_2, KS_quotedbl, KS_twosuperior, + KC(32), KS_3, KS_numbersign, KS_threesuperior, + KC(33), KS_4, KS_dollar, KS_onequarter, + KC(34), KS_5, KS_percent, KS_onehalf, + KC(35), KS_6, KS_ampersand, KS_threequarters, + KC(36), KS_7, KS_underscore, KS_sterling, + KC(37), KS_8, KS_parenleft, KS_braceleft, + KC(38), KS_9, KS_parenright, KS_braceright, + KC(39), KS_0, KS_apostrophe, + KC(45), KS_slash, KS_question, KS_backslash, + KC(46), KS_degree, KS_dead_tilde, KS_dead_cedilla, + KC(47), KS_dead_diaeresis,KS_dead_circumflex, + KC(48), KS_asterisk, KS_bar, + KC(49), KS_less, KS_greater, + KC(50), KS_less, KS_greater, + KC(51), KS_plus, KS_plusminus, + KC(52), KS_dead_acute, KS_dead_grave, + KC(53), KS_at, KS_section, KS_notsign, + KC(54), KS_comma, KS_semicolon, + KC(55), KS_period, KS_colon, KS_periodcentered, + KC(56), KS_minus, KS_equal, + KC(100), KS_bracketright,KS_bracketleft, KS_brokenbar, + KC(230), KS_Mode_switch, KS_Multi_key, +}; + +static const keysym_t ukbd_keydesc_nl_nodead[] = { +/* pos normal shifted altgr shift-altgr */ + KC(46), KS_degree, KS_asciitilde, KS_cedilla, + KC(47), KS_quotedbl, KS_asciicircum, + KC(52), KS_apostrophe, KS_grave, +}; + +#define KBD_MAP(name, base, map) \ + { name, base, sizeof(map)/sizeof(keysym_t), map } + +const struct wscons_keydesc ukbd_keydesctab[] = { + KBD_MAP(KB_US, 0, ukbd_keydesc_us), + KBD_MAP(KB_DE, KB_US, ukbd_keydesc_de), + KBD_MAP(KB_DE | KB_NODEAD, KB_DE, ukbd_keydesc_de_nodead), + KBD_MAP(KB_FR, KB_US, ukbd_keydesc_fr), + KBD_MAP(KB_FR | KB_APPLE, KB_FR, ukbd_keydesc_fr_apple), + KBD_MAP(KB_FR | KB_DVORAK, KB_US, ukbd_keydesc_fr_dvorak_bepo), + KBD_MAP(KB_DK, KB_US, ukbd_keydesc_dk), + KBD_MAP(KB_DK | KB_NODEAD, KB_DK, ukbd_keydesc_dk_nodead), + KBD_MAP(KB_IT, KB_US, ukbd_keydesc_it), + KBD_MAP(KB_UK, KB_US, ukbd_keydesc_uk), + KBD_MAP(KB_JP, KB_US, ukbd_keydesc_jp), + KBD_MAP(KB_SV, KB_DK, ukbd_keydesc_sv), + KBD_MAP(KB_SV | KB_NODEAD, KB_SV, ukbd_keydesc_sv_nodead), + KBD_MAP(KB_NO, KB_DK, ukbd_keydesc_no), + KBD_MAP(KB_NO | KB_NODEAD, KB_NO, ukbd_keydesc_no_nodead), + KBD_MAP(KB_US | KB_DVORAK, KB_US, ukbd_keydesc_us_dvorak), + KBD_MAP(KB_US | KB_SWAPCTRLCAPS, KB_US, ukbd_keydesc_swapctrlcaps), + KBD_MAP(KB_US | KB_IOPENER, KB_US, ukbd_keydesc_iopener), + KBD_MAP(KB_JP | KB_SWAPCTRLCAPS, KB_JP, ukbd_keydesc_swapctrlcaps), + KBD_MAP(KB_FR | KB_SWAPCTRLCAPS, KB_FR, ukbd_keydesc_swapctrlcaps), + KBD_MAP(KB_FR | KB_APPLE | KB_SWAPCTRLCAPS, KB_FR | KB_APPLE, + ukbd_keydesc_swapctrlcaps), + KBD_MAP(KB_FR | KB_DVORAK | KB_SWAPCTRLCAPS, KB_FR | KB_DVORAK, + ukbd_keydesc_swapctrlcaps), + KBD_MAP(KB_BE | KB_SWAPCTRLCAPS, KB_BE, ukbd_keydesc_swapctrlcaps), + KBD_MAP(KB_US | KB_DVORAK | KB_SWAPCTRLCAPS, KB_US | KB_DVORAK, + ukbd_keydesc_swapctrlcaps), + KBD_MAP(KB_US | KB_IOPENER | KB_SWAPCTRLCAPS, KB_US | KB_IOPENER, + ukbd_keydesc_swapctrlcaps), + KBD_MAP(KB_ES, KB_US, ukbd_keydesc_es), + KBD_MAP(KB_BE, KB_US, ukbd_keydesc_be), + KBD_MAP(KB_RU, KB_US, ukbd_keydesc_ru), + KBD_MAP(KB_UA, KB_US, ukbd_keydesc_ua), + KBD_MAP(KB_SG, KB_US, ukbd_keydesc_sg), + KBD_MAP(KB_SG | KB_NODEAD, KB_SG, ukbd_keydesc_sg_nodead), + KBD_MAP(KB_SF, KB_SG, ukbd_keydesc_sf), + KBD_MAP(KB_SF | KB_NODEAD, KB_SF, ukbd_keydesc_sg_nodead), + KBD_MAP(KB_PT, KB_US, ukbd_keydesc_pt), + KBD_MAP(KB_PT | KB_APPLE, KB_PT, ukbd_keydesc_pt_apple), + KBD_MAP(KB_LT, KB_US, ukbd_keydesc_lt), + KBD_MAP(KB_LA, KB_US, ukbd_keydesc_la), + KBD_MAP(KB_BR, KB_US, ukbd_keydesc_br), + KBD_MAP(KB_TR, KB_US, ukbd_keydesc_tr), + KBD_MAP(KB_TR | KB_NODEAD, KB_TR, ukbd_keydesc_tr_nodead), + KBD_MAP(KB_PL, KB_US, ukbd_keydesc_pl), + KBD_MAP(KB_HU, KB_US, ukbd_keydesc_hu), + KBD_MAP(KB_SI, KB_US, ukbd_keydesc_si), + KBD_MAP(KB_CF, KB_US, ukbd_keydesc_cf), + KBD_MAP(KB_CF | KB_NODEAD, KB_CF, ukbd_keydesc_cf_nodead), + KBD_MAP(KB_LV, KB_US, ukbd_keydesc_lv), + KBD_MAP(KB_NL, KB_US, ukbd_keydesc_nl), + KBD_MAP(KB_NL | KB_NODEAD, KB_NL, ukbd_keydesc_nl_nodead), + {0, 0, 0, 0} +}; + +#undef KBD_MAP +#undef KC diff --git a/wii/libogc/libwiikeyboard/usbkeyboard.c b/wii/libogc/libwiikeyboard/usbkeyboard.c new file mode 100644 index 0000000000..1b2d45a6e3 --- /dev/null +++ b/wii/libogc/libwiikeyboard/usbkeyboard.c @@ -0,0 +1,495 @@ +/*------------------------------------------------------------- + +usbkeyboard.c -- Usb keyboard support(boot protocol) + +Copyright (C) 2008, 2009 +DAVY Guillaume davyg2@gmail.com +dhewg + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#include +#include + +#include +#include + +#include + +#define HEAP_SIZE 4096 +#define DEVLIST_MAXSIZE 8 +#define KEY_ERROR 0x01 +#define MAXKEYCODE 6 + +#define USB_MOD_CTRL_L 0x01 +#define USB_MOD_SHIFT_L 0x02 +#define USB_MOD_ALT_L 0x04 +#define USB_MOD_META_L 0x08 +#define USB_MOD_CTRL_R 0x10 +#define USB_MOD_SHIFT_R 0x20 +#define USB_MOD_ALT_R 0x40 +#define USB_MOD_META_R 0x80 + +struct ukbd_data { + u16 modifiers; + u8 keycode[MAXKEYCODE]; +} ATTRIBUTE_PACKED; + +struct ukbd { + bool connected; + + s32 fd; + + struct ukbd_data sc_ndata; + struct ukbd_data sc_odata; + + u8 leds; + + eventcallback cb; + + u8 configuration; + u32 interface; + u32 altInterface; + + u8 ep; + u32 ep_size; +}; + +static s32 hId = -1; +static struct ukbd *_kbd = NULL; + +static u8 _ukbd_mod_map[][2] = { + { USB_MOD_CTRL_L, 224 }, + { USB_MOD_SHIFT_L, 225 }, + { USB_MOD_ALT_L, 226 }, + { USB_MOD_META_L, 227 }, + { USB_MOD_CTRL_R, 228 }, + { USB_MOD_SHIFT_R, 229 }, + { USB_MOD_ALT_R, 230 }, + { USB_MOD_META_R, 231 } +}; + +#define MODMAPSIZE (sizeof(_ukbd_mod_map)/sizeof(_ukbd_mod_map[0])) + +static void _submit(USBKeyboard_eventType type, u8 code) +{ + if (!_kbd->cb) + return; + + USBKeyboard_event ev; + ev.type = type; + ev.keyCode = code; + + _kbd->cb(ev); +} + +//Callback when the keyboard is disconnected +static s32 _disconnect(s32 retval, void *data) +{ + (void) data; + + _kbd->connected = false; + + _submit(USBKEYBOARD_DISCONNECTED, 0); + + return 1; +} + +//Get the protocol, 0=boot protocol and 1=report protocol +static s32 _get_protocol(void) +{ + s32 protocol; + u8 *buffer = 0; + + if(!_kbd || _kbd->fd==-1) return -1; + + buffer = iosAlloc(hId, 1); + + if (buffer == NULL) + return -1; + + USB_WriteCtrlMsg(_kbd->fd, USB_REQTYPE_INTERFACE_GET, USB_REQ_GETPROTOCOL, 0, _kbd->interface, 1, buffer); + + protocol = *buffer; + iosFree(hId, buffer); + + return protocol; +} + +//Modify the protocol, 0=boot protocol and 1=report protocol +static s32 _set_protocol(u8 protocol) +{ + if(!_kbd || _kbd->fd==-1) return -1; + return USB_WriteCtrlMsg(_kbd->fd, USB_REQTYPE_INTERFACE_SET, USB_REQ_SETPROTOCOL, protocol, _kbd->interface, 0, NULL); +} + +//Get an input report from interrupt pipe +static s32 _get_input_report(void) +{ + u8 *buffer = 0; + + if(!_kbd || _kbd->fd==-1) return -1; + buffer = iosAlloc(hId, 8); + + if (buffer == NULL) + return -1; + + s32 ret = USB_ReadIntrMsg(_kbd->fd, _kbd->ep, 8, buffer); + + memcpy(&_kbd->sc_ndata, buffer, 8); + iosFree(hId, buffer); + + _kbd->sc_ndata.modifiers = (_kbd->sc_ndata.modifiers << 8) | (_kbd->sc_ndata.modifiers >> 8); + + return ret; +} + +#if 0 +//Get an input report from control pipe +static s32 _get_output_report(u8 *leds) +{ + u8 *buffer = 0; + if(!_kbd || _kbd->fd==-1) return -1; + buffer = iosAlloc(hId, 1); + + if (buffer == NULL) + return -1; + + s32 ret = USB_WriteCtrlMsg(_kbd->fd, USB_REQTYPE_INTERFACE_GET, USB_REQ_GETREPORT, USB_REPTYPE_OUTPUT << 8, _kbd->interface, 1, buffer); + + memcpy(leds, buffer, 1); + iosFree(hId, buffer); + + return ret; +} +#endif + +//Set an input report to control pipe +static s32 _set_output_report(void) +{ + u8 *buffer = 0; + if(!_kbd || _kbd->fd==-1) return -1; + buffer = iosAlloc(hId, 1); + + if (buffer == NULL) + return -1; + + memcpy(buffer, &_kbd->leds, 1); + s32 ret = USB_WriteCtrlMsg(_kbd->fd, USB_REQTYPE_INTERFACE_SET, USB_REQ_SETREPORT, USB_REPTYPE_OUTPUT << 8, _kbd->interface, 1, buffer); + + iosFree(hId, buffer); + + return ret; +} + +//init the ioheap +s32 USBKeyboard_Initialize(void) +{ + if (hId > 0) + return 0; + + hId = iosCreateHeap(HEAP_SIZE); + + if (hId < 0) + return IPC_ENOHEAP; + + return IPC_OK; +} + +s32 USBKeyboard_Deinitialize(void) +{ + return IPC_OK; +} + +//Search for a keyboard connected to the wii usb port +//Thanks to Sven Peter usbstorage support +s32 USBKeyboard_Open(const eventcallback cb) +{ + usb_device_entry *buffer; + u8 device_count, i, conf; + u16 vid, pid; + bool found = false; + u32 iConf, iInterface, iEp; + usb_devdesc udd; + usb_configurationdesc *ucd; + usb_interfacedesc *uid; + usb_endpointdesc *ued; + + buffer = (usb_device_entry*)iosAlloc(hId, DEVLIST_MAXSIZE * sizeof(usb_device_entry)); + if(buffer == NULL) + return -1; + + memset(buffer, 0, DEVLIST_MAXSIZE * sizeof(usb_device_entry)); + + if (USB_GetDeviceList(buffer, DEVLIST_MAXSIZE, USB_CLASS_HID, &device_count) < 0) + { + iosFree(hId, buffer); + return -2; + } + + if (_kbd) { + if (_kbd->fd != -1) USB_CloseDevice(&_kbd->fd); + } else { + _kbd = (struct ukbd *) malloc(sizeof(struct ukbd)); + + if (!_kbd) + return -1; + } + + memset(_kbd, 0, sizeof(struct ukbd)); + _kbd->fd = -1; + + for (i = 0; i < device_count; i++) + { + vid = buffer[i].vid;; + pid = buffer[i].pid; + + if ((vid == 0) || (pid == 0)) + continue; + + s32 fd = 0; + if (USB_OpenDevice(buffer[i].device_id, vid, pid, &fd) < 0) + continue; + + if (USB_GetDescriptors(fd, &udd) < 0) { + USB_CloseDevice(&fd); + continue; + } + + for(iConf = 0; iConf < udd.bNumConfigurations; iConf++) + { + ucd = &udd.configurations[iConf]; + + for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++) + { + uid = &ucd->interfaces[iInterface]; + + if ((uid->bInterfaceClass == USB_CLASS_HID) && + (uid->bInterfaceSubClass == USB_SUBCLASS_BOOT) && + (uid->bInterfaceProtocol== USB_PROTOCOL_KEYBOARD)) + { + for(iEp = 0; iEp < uid->bNumEndpoints; iEp++) + { + ued = &uid->endpoints[iEp]; + + if (ued->bmAttributes != USB_ENDPOINT_INTERRUPT) + continue; + + if (!(ued->bEndpointAddress & USB_ENDPOINT_IN)) + continue; + + _kbd->fd = fd; + _kbd->cb = cb; + + _kbd->configuration = ucd->bConfigurationValue; + _kbd->interface = uid->bInterfaceNumber; + _kbd->altInterface = uid->bAlternateSetting; + + _kbd->ep = ued->bEndpointAddress; + _kbd->ep_size = ued->wMaxPacketSize; + + found = true; + + break; + } + } + + if (found) + break; + } + + if (found) + break; + } + + USB_FreeDescriptors(&udd); + + if (found) + break; + else + USB_CloseDevice(&fd); + } + + iosFree(hId, buffer); + + if (!found) + return -3; + + if (USB_GetConfiguration(_kbd->fd, &conf) < 0) + { + USBKeyboard_Close(); + return -4; + } + + if (conf != _kbd->configuration && + USB_SetConfiguration(_kbd->fd, _kbd->configuration) < 0) + { + USBKeyboard_Close(); + return -5; + } + + if (_kbd->altInterface != 0 && + USB_SetAlternativeInterface(_kbd->fd, _kbd->interface, _kbd->altInterface) < 0) + { + USBKeyboard_Close(); + return -6; + } + + if (_get_protocol() != 0) + { + if (_set_protocol(0) < 0) + { + USBKeyboard_Close(); + return -6; + } + + if (_get_protocol() == 1) + { + USBKeyboard_Close(); + return -7; + } + } + + if (USB_DeviceRemovalNotifyAsync(_kbd->fd, &_disconnect, NULL) < 0) + { + USBKeyboard_Close(); + return -8; + } + + _kbd->connected = true; + + return 1; +} + +//Close the device +void USBKeyboard_Close(void) +{ + if (!_kbd) + return; + + if(_kbd->fd != -1) + USB_CloseDevice(&_kbd->fd); + + free(_kbd); + _kbd = NULL; + + return; +} + +bool USBKeyboard_IsConnected(void) { + if (!_kbd) + return false; + + return _kbd->connected; +} + +//Scan for key presses and generate events for the callback function +s32 USBKeyboard_Scan(void) +{ + int i, j, index; + + if (!_kbd) + return -1; + + if (_get_input_report() < 0) + return -2; + + if (_kbd->sc_ndata.keycode[0] == KEY_ERROR) + return 0; + + if (_kbd->sc_ndata.modifiers != _kbd->sc_odata.modifiers) { + for (i = 0; i < MODMAPSIZE; ++i) { + if ((_kbd->sc_odata.modifiers & _ukbd_mod_map[i][0]) + && !(_kbd->sc_ndata.modifiers & _ukbd_mod_map[i][0])) + _submit(USBKEYBOARD_RELEASED, _ukbd_mod_map[i][1]); + else if ((_kbd->sc_ndata.modifiers & _ukbd_mod_map[i][0]) + && !(_kbd->sc_odata.modifiers & _ukbd_mod_map[i][0])) + _submit(USBKEYBOARD_PRESSED, _ukbd_mod_map[i][1]); + } + } + + for (i = 0; i < MAXKEYCODE; i++) { + if (_kbd->sc_odata.keycode[i] > 3) { + index = -1; + + for (j = 0; j < MAXKEYCODE; j++) { + if (_kbd->sc_odata.keycode[i] == _kbd->sc_ndata.keycode[j]) { + index = j; + break; + } + } + + if (index == -1) + _submit(USBKEYBOARD_RELEASED, _kbd->sc_odata.keycode[i]); + } + + if (_kbd->sc_ndata.keycode[i] > 3) { + index = -1; + + for (j = 0; j < MAXKEYCODE; j++) { + if (_kbd->sc_ndata.keycode[i] == _kbd->sc_odata.keycode[j]) { + index = j; + break; + } + } + + if (index == -1) + _submit(USBKEYBOARD_PRESSED, _kbd->sc_ndata.keycode[i]); + } + } + + _kbd->sc_odata = _kbd->sc_ndata; + + return 0; +} + +//Turn on/off a led +s32 USBKeyboard_SetLed(const USBKeyboard_led led, bool on) +{ + if (!_kbd) + return -1; + + if (on) + _kbd->leds = _kbd->leds | (1 << led ); + else + _kbd->leds = _kbd->leds & (255 ^ (1 << led)); + + if (_set_output_report() < 0) + return -2; + + return 1; +} + +//Toggle a led +s32 USBKeyboard_ToggleLed(const USBKeyboard_led led) +{ + if (!_kbd) + return -1; + + _kbd->leds = _kbd->leds ^ (1 << led); + + if (_set_output_report() < 0) + return -2; + + return 1; +} + diff --git a/wii/libogc/libwiikeyboard/wskbdutil.c b/wii/libogc/libwiikeyboard/wskbdutil.c new file mode 100644 index 0000000000..8ed2495afc --- /dev/null +++ b/wii/libogc/libwiikeyboard/wskbdutil.c @@ -0,0 +1,492 @@ +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Juergen Hannken-Illjes. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "wsksymvar.h" + +static struct compose_tab_s { + keysym_t elem[2]; + keysym_t result; +} compose_tab[] = { + { { KS_plus, KS_plus }, KS_numbersign }, + { { KS_a, KS_a }, KS_at }, + { { KS_parenleft, KS_parenleft }, KS_bracketleft }, + { { KS_slash, KS_slash }, KS_backslash }, + { { KS_parenright, KS_parenright }, KS_bracketright }, + { { KS_parenleft, KS_minus }, KS_braceleft }, + { { KS_slash, KS_minus }, KS_bar }, + { { KS_parenright, KS_minus }, KS_braceright }, + { { KS_exclam, KS_exclam }, KS_exclamdown }, + { { KS_c, KS_slash }, KS_cent }, + { { KS_l, KS_minus }, KS_sterling }, + { { KS_y, KS_minus }, KS_yen }, + { { KS_s, KS_o }, KS_section }, + { { KS_x, KS_o }, KS_currency }, + { { KS_c, KS_o }, KS_copyright }, + { { KS_less, KS_less }, KS_guillemotleft }, + { { KS_greater, KS_greater }, KS_guillemotright }, + { { KS_question, KS_question }, KS_questiondown }, + { { KS_dead_acute, KS_space }, KS_apostrophe }, + { { KS_dead_grave, KS_space }, KS_grave }, + { { KS_dead_tilde, KS_space }, KS_asciitilde }, + { { KS_dead_circumflex, KS_space }, KS_asciicircum }, + { { KS_dead_diaeresis, KS_space }, KS_quotedbl }, + { { KS_dead_cedilla, KS_space }, KS_comma }, + { { KS_dead_circumflex, KS_A }, KS_Acircumflex }, + { { KS_dead_diaeresis, KS_A }, KS_Adiaeresis }, + { { KS_dead_grave, KS_A }, KS_Agrave }, + { { KS_dead_abovering, KS_A }, KS_Aring }, + { { KS_dead_tilde, KS_A }, KS_Atilde }, + { { KS_dead_cedilla, KS_C }, KS_Ccedilla }, + { { KS_dead_acute, KS_E }, KS_Eacute }, + { { KS_dead_circumflex, KS_E }, KS_Ecircumflex }, + { { KS_dead_diaeresis, KS_E }, KS_Ediaeresis }, + { { KS_dead_grave, KS_E }, KS_Egrave }, + { { KS_dead_acute, KS_I }, KS_Iacute }, + { { KS_dead_circumflex, KS_I }, KS_Icircumflex }, + { { KS_dead_diaeresis, KS_I }, KS_Idiaeresis }, + { { KS_dead_grave, KS_I }, KS_Igrave }, + { { KS_dead_tilde, KS_N }, KS_Ntilde }, + { { KS_dead_acute, KS_O }, KS_Oacute }, + { { KS_dead_circumflex, KS_O }, KS_Ocircumflex }, + { { KS_dead_diaeresis, KS_O }, KS_Odiaeresis }, + { { KS_dead_grave, KS_O }, KS_Ograve }, + { { KS_dead_tilde, KS_O }, KS_Otilde }, + { { KS_dead_acute, KS_U }, KS_Uacute }, + { { KS_dead_circumflex, KS_U }, KS_Ucircumflex }, + { { KS_dead_diaeresis, KS_U }, KS_Udiaeresis }, + { { KS_dead_grave, KS_U }, KS_Ugrave }, + { { KS_dead_acute, KS_Y }, KS_Yacute }, + { { KS_dead_acute, KS_a }, KS_aacute }, + { { KS_dead_circumflex, KS_a }, KS_acircumflex }, + { { KS_dead_diaeresis, KS_a }, KS_adiaeresis }, + { { KS_dead_grave, KS_a }, KS_agrave }, + { { KS_dead_abovering, KS_a }, KS_aring }, + { { KS_dead_tilde, KS_a }, KS_atilde }, + { { KS_dead_cedilla, KS_c }, KS_ccedilla }, + { { KS_dead_acute, KS_e }, KS_eacute }, + { { KS_dead_circumflex, KS_e }, KS_ecircumflex }, + { { KS_dead_diaeresis, KS_e }, KS_ediaeresis }, + { { KS_dead_grave, KS_e }, KS_egrave }, + { { KS_dead_acute, KS_i }, KS_iacute }, + { { KS_dead_circumflex, KS_i }, KS_icircumflex }, + { { KS_dead_diaeresis, KS_i }, KS_idiaeresis }, + { { KS_dead_grave, KS_i }, KS_igrave }, + { { KS_dead_tilde, KS_n }, KS_ntilde }, + { { KS_dead_acute, KS_o }, KS_oacute }, + { { KS_dead_circumflex, KS_o }, KS_ocircumflex }, + { { KS_dead_diaeresis, KS_o }, KS_odiaeresis }, + { { KS_dead_grave, KS_o }, KS_ograve }, + { { KS_dead_tilde, KS_o }, KS_otilde }, + { { KS_dead_acute, KS_u }, KS_uacute }, + { { KS_dead_circumflex, KS_u }, KS_ucircumflex }, + { { KS_dead_diaeresis, KS_u }, KS_udiaeresis }, + { { KS_dead_grave, KS_u }, KS_ugrave }, + { { KS_dead_acute, KS_y }, KS_yacute }, + { { KS_dead_diaeresis, KS_y }, KS_ydiaeresis }, + { { KS_quotedbl, KS_A }, KS_Adiaeresis }, + { { KS_quotedbl, KS_E }, KS_Ediaeresis }, + { { KS_quotedbl, KS_I }, KS_Idiaeresis }, + { { KS_quotedbl, KS_O }, KS_Odiaeresis }, + { { KS_quotedbl, KS_U }, KS_Udiaeresis }, + { { KS_quotedbl, KS_a }, KS_adiaeresis }, + { { KS_quotedbl, KS_e }, KS_ediaeresis }, + { { KS_quotedbl, KS_i }, KS_idiaeresis }, + { { KS_quotedbl, KS_o }, KS_odiaeresis }, + { { KS_quotedbl, KS_u }, KS_udiaeresis }, + { { KS_quotedbl, KS_y }, KS_ydiaeresis }, + { { KS_acute, KS_A }, KS_Aacute }, + { { KS_asciicircum, KS_A }, KS_Acircumflex }, + { { KS_grave, KS_A }, KS_Agrave }, + { { KS_asterisk, KS_A }, KS_Aring }, + { { KS_asciitilde, KS_A }, KS_Atilde }, + { { KS_cedilla, KS_C }, KS_Ccedilla }, + { { KS_acute, KS_E }, KS_Eacute }, + { { KS_asciicircum, KS_E }, KS_Ecircumflex }, + { { KS_grave, KS_E }, KS_Egrave }, + { { KS_acute, KS_I }, KS_Iacute }, + { { KS_asciicircum, KS_I }, KS_Icircumflex }, + { { KS_grave, KS_I }, KS_Igrave }, + { { KS_asciitilde, KS_N }, KS_Ntilde }, + { { KS_acute, KS_O }, KS_Oacute }, + { { KS_asciicircum, KS_O }, KS_Ocircumflex }, + { { KS_grave, KS_O }, KS_Ograve }, + { { KS_asciitilde, KS_O }, KS_Otilde }, + { { KS_acute, KS_U }, KS_Uacute }, + { { KS_asciicircum, KS_U }, KS_Ucircumflex }, + { { KS_grave, KS_U }, KS_Ugrave }, + { { KS_acute, KS_Y }, KS_Yacute }, + { { KS_acute, KS_a }, KS_aacute }, + { { KS_asciicircum, KS_a }, KS_acircumflex }, + { { KS_grave, KS_a }, KS_agrave }, + { { KS_asterisk, KS_a }, KS_aring }, + { { KS_asciitilde, KS_a }, KS_atilde }, + { { KS_cedilla, KS_c }, KS_ccedilla }, + { { KS_acute, KS_e }, KS_eacute }, + { { KS_asciicircum, KS_e }, KS_ecircumflex }, + { { KS_grave, KS_e }, KS_egrave }, + { { KS_acute, KS_i }, KS_iacute }, + { { KS_asciicircum, KS_i }, KS_icircumflex }, + { { KS_grave, KS_i }, KS_igrave }, + { { KS_asciitilde, KS_n }, KS_ntilde }, + { { KS_acute, KS_o }, KS_oacute }, + { { KS_asciicircum, KS_o }, KS_ocircumflex }, + { { KS_grave, KS_o }, KS_ograve }, + { { KS_asciitilde, KS_o }, KS_otilde }, + { { KS_acute, KS_u }, KS_uacute }, + { { KS_asciicircum, KS_u }, KS_ucircumflex }, + { { KS_grave, KS_u }, KS_ugrave }, + { { KS_acute, KS_y }, KS_yacute } +}; + +#define COMPOSE_SIZE sizeof(compose_tab)/sizeof(compose_tab[0]) + +static int compose_tab_inorder = 0; + +inline int compose_tab_cmp(struct compose_tab_s *, + struct compose_tab_s *); +keysym_t ksym_upcase(keysym_t); +void fillmapentry(const keysym_t *, int, struct wscons_keymap *); + +inline int +compose_tab_cmp(i, j) + struct compose_tab_s *i, *j; +{ + if (i->elem[0] == j->elem[0]) + return(i->elem[1] - j->elem[1]); + else + return(i->elem[0] - j->elem[0]); +} + +keysym_t +wskbd_compose_value(compose_buf) + keysym_t *compose_buf; +{ + int i, j, r; + struct compose_tab_s v; + + if (! compose_tab_inorder) { + /* Insertion sort. */ + for (i = 1; i < COMPOSE_SIZE; i++) { + v = compose_tab[i]; + /* find correct slot, moving others up */ + for (j = i; --j >= 0 && compose_tab_cmp(& v, & compose_tab[j]) < 0; ) + compose_tab[j + 1] = compose_tab[j]; + compose_tab[j + 1] = v; + } + compose_tab_inorder = 1; + } + + for (j = 0, i = COMPOSE_SIZE; i != 0; i /= 2) { + if (compose_tab[j + i/2].elem[0] == compose_buf[0]) { + if (compose_tab[j + i/2].elem[1] == compose_buf[1]) + return(compose_tab[j + i/2].result); + r = compose_tab[j + i/2].elem[1] < compose_buf[1]; + } else + r = compose_tab[j + i/2].elem[0] < compose_buf[0]; + if (r) { + j += i/2 + 1; + i--; + } + } + + return(KS_voidSymbol); +} + +static const u_char latin1_to_upper[256] = { +/* 0 8 1 9 2 a 3 b 4 c 5 d 6 e 7 f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 2 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 2 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 3 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 3 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 4 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 4 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5 */ + 0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 6 */ + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* 6 */ + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 7 */ + 'X', 'Y', 'Z', 0x00, 0x00, 0x00, 0x00, 0x00, /* 7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 9 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 9 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d */ + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* e */ + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* e */ + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0x00, /* f */ + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x00, /* f */ +}; + +keysym_t +ksym_upcase(ksym) + keysym_t ksym; +{ + if (ksym >= KS_f1 && ksym <= KS_f20) + return(KS_F1 - KS_f1 + ksym); + + if (KS_GROUP(ksym) == KS_GROUP_Ascii && ksym <= 0xff && + latin1_to_upper[ksym] != 0x00) + return(latin1_to_upper[ksym]); + + return(ksym); +} + +void +fillmapentry(kp, len, mapentry) + const keysym_t *kp; + int len; + struct wscons_keymap *mapentry; +{ + switch (len) { + case 0: + mapentry->group1[0] = KS_voidSymbol; + mapentry->group1[1] = KS_voidSymbol; + mapentry->group2[0] = KS_voidSymbol; + mapentry->group2[1] = KS_voidSymbol; + break; + + case 1: + mapentry->group1[0] = kp[0]; + mapentry->group1[1] = ksym_upcase(kp[0]); + mapentry->group2[0] = mapentry->group1[0]; + mapentry->group2[1] = mapentry->group1[1]; + break; + + case 2: + mapentry->group1[0] = kp[0]; + mapentry->group1[1] = kp[1]; + mapentry->group2[0] = mapentry->group1[0]; + mapentry->group2[1] = mapentry->group1[1]; + break; + + case 3: + mapentry->group1[0] = kp[0]; + mapentry->group1[1] = kp[1]; + mapentry->group2[0] = kp[2]; + mapentry->group2[1] = ksym_upcase(kp[2]); + break; + + case 4: + mapentry->group1[0] = kp[0]; + mapentry->group1[1] = kp[1]; + mapentry->group2[0] = kp[2]; + mapentry->group2[1] = kp[3]; + break; + + } +} + +void +wskbd_get_mapentry(mapdata, kc, mapentry) + const struct wskbd_mapdata *mapdata; + int kc; + struct wscons_keymap *mapentry; +{ + kbd_t cur; + const keysym_t *kp; + const struct wscons_keydesc *mp; + int l; + keysym_t ksg; + + mapentry->command = KS_voidSymbol; + mapentry->group1[0] = KS_voidSymbol; + mapentry->group1[1] = KS_voidSymbol; + mapentry->group2[0] = KS_voidSymbol; + mapentry->group2[1] = KS_voidSymbol; + + for (cur = mapdata->layout & ~KB_HANDLEDBYWSKBD; cur != 0; ) { + mp = mapdata->keydesc; + while (mp->map_size > 0) { + if (mp->name == cur) + break; + mp++; + } + + /* If map not found, return */ + if (mp->map_size <= 0) + return; + + for (kp = mp->map; kp < mp->map + mp->map_size; kp++) { + ksg = KS_GROUP(*kp); + if (ksg == KS_GROUP_Keycode && + KS_VALUE(*kp) == kc) { + /* First skip keycode and possible command */ + kp++; + if (KS_GROUP(*kp) == KS_GROUP_Command || + *kp == KS_Cmd || *kp == KS_Cmd1 || *kp == KS_Cmd2) + mapentry->command = *kp++; + + for (l = 0; kp + l < mp->map + mp->map_size; + l++) { + ksg = KS_GROUP(kp[l]); + if (ksg == KS_GROUP_Keycode) + break; + } + if (l > 4) { + fprintf(stderr, "wskbd_get_mapentry: %lu(%d): bad entry", + mp->name, *kp); + return; + } + fillmapentry(kp, l, mapentry); + return; + } + } + + cur = mp->base; + } +} + +void +wskbd_init_keymap(newlen, map, maplen) + int newlen; + struct wscons_keymap **map; + int *maplen; +{ + int i; + + if (newlen != *maplen) { + if (*maplen > 0) + free(*map); + *maplen = newlen; + *map = malloc(newlen*sizeof(struct wscons_keymap)); + } + + for (i = 0; i < *maplen; i++) { + (*map)[i].command = KS_voidSymbol; + (*map)[i].group1[0] = KS_voidSymbol; + (*map)[i].group1[1] = KS_voidSymbol; + (*map)[i].group2[0] = KS_voidSymbol; + (*map)[i].group2[1] = KS_voidSymbol; + } +} + +int +wskbd_load_keymap(mapdata, map, maplen) + const struct wskbd_mapdata *mapdata; + struct wscons_keymap **map; + int *maplen; +{ + int i, s, kc, stack_ptr; + const keysym_t *kp; + const struct wscons_keydesc *mp, *stack[10]; + kbd_t cur; + keysym_t ksg; + + for (cur = mapdata->layout & ~KB_HANDLEDBYWSKBD, stack_ptr = 0; + cur != 0; stack_ptr++) { + mp = mapdata->keydesc; + while (mp->map_size > 0) { + if (cur == 0 || mp->name == cur) { + break; + } + mp++; + } + + if (stack_ptr == sizeof(stack)/sizeof(stack[0])) { + fprintf(stderr, "wskbd_load_keymap: %lu: recursion too deep", + mapdata->layout); + return(EINVAL); + } + + if (mp->map_size <= 0) + return(EINVAL); + + stack[stack_ptr] = mp; + cur = mp->base; + } + + for (i = 0, s = stack_ptr - 1; s >= 0; s--) { + mp = stack[s]; + for (kp = mp->map; kp < mp->map + mp->map_size; kp++) { + ksg = KS_GROUP(*kp); + if (ksg == KS_GROUP_Keycode && KS_VALUE(*kp) > i) + i = KS_VALUE(*kp); + } + } + + wskbd_init_keymap(i + 1, map, maplen); + + for (s = stack_ptr - 1; s >= 0; s--) { + mp = stack[s]; + for (kp = mp->map; kp < mp->map + mp->map_size; ) { + ksg = KS_GROUP(*kp); + if (ksg != KS_GROUP_Keycode) { + fprintf(stderr, "wskbd_load_keymap: %lu(%d): bad entry", + mp->name, *kp); + return(EINVAL); + } + + kc = KS_VALUE(*kp); + kp++; + + if (KS_GROUP(*kp) == KS_GROUP_Command || + *kp == KS_Cmd || *kp == KS_Cmd1 || *kp == KS_Cmd2) { + (*map)[kc].command = *kp; + kp++; + } + + for (i = 0; kp + i < mp->map + mp->map_size; i++) { + ksg = KS_GROUP(kp[i]); + if (ksg == KS_GROUP_Keycode) + break; + } + + if (i > 4) { + fprintf(stderr, "wskbd_load_keymap: %lu(%d): bad entry", + mp->name, *kp); + return(EINVAL); + } + + fillmapentry(kp, i, &(*map)[kc]); + kp += i; + } + } + + return(0); +} diff --git a/wii/libogc/libwiikeyboard/wsksymvar.h b/wii/libogc/libwiikeyboard/wsksymvar.h new file mode 100644 index 0000000000..0bf10d3466 --- /dev/null +++ b/wii/libogc/libwiikeyboard/wsksymvar.h @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Juergen Hannken-Illjes. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DEV_WSCONS_WSKSYMVAR_H_ +#define _DEV_WSCONS_WSKSYMVAR_H_ + +#include + +typedef u16 keysym_t; +typedef u32 kbd_t; + +struct wscons_keymap { + keysym_t command; + keysym_t group1[2]; + keysym_t group2[2]; +}; + +struct wscons_keydesc { + kbd_t name; /* name of this map */ + kbd_t base; /* map this one is based on */ + int map_size; /* size of map */ + const keysym_t *map; /* the map itself */ +}; + +struct wskbd_mapdata { + const struct wscons_keydesc *keydesc; + kbd_t layout; +}; + +/* layout variant bits ignored by mapping code */ +#define KB_HANDLEDBYWSKBD KB_METAESC + +/* + * Utility functions. + */ +void wskbd_get_mapentry(const struct wskbd_mapdata *, int, + struct wscons_keymap *); +void wskbd_init_keymap(int, struct wscons_keymap **, int *); +int wskbd_load_keymap(const struct wskbd_mapdata *, + struct wscons_keymap **, int *); +keysym_t wskbd_compose_value(keysym_t *); + +#endif /* !_DEV_WSCONS_WSKSYMVAR_H_ */ diff --git a/wii/libogc/lwbt/bt.h b/wii/libogc/lwbt/bt.h new file mode 100644 index 0000000000..d0ed63af8e --- /dev/null +++ b/wii/libogc/lwbt/bt.h @@ -0,0 +1,119 @@ +/** + * \addtogroup uip + * @{ + */ + +/** + * \file + * Header file for the uIP TCP/IP stack. + * \author Adam Dunkels + * + * The uIP TCP/IP stack header file contains definitions for a number + * of C macros that are used by uIP programs as well as internal uIP + * structures, TCP/IP header structures and function declarations. + * + */ + + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * + */ + +#ifndef __BT_H__ +#define __BT_H__ + +#include "btopt.h" +#include "btarch.h" + +#define ERR_OK 0 +#define ERR_MEM -1 +#define ERR_BUF -2 +#define ERR_ABRT -3 +#define ERR_RST -4 +#define ERR_CLSD -5 +#define ERR_CONN -6 +#define ERR_VAL -7 +#define ERR_ARG -8 +#define ERR_RTE -9 +#define ERR_USE -10 +#define ERR_IF -11 +#define ERR_PKTSIZE -17 + +#define PROTO_ICMP 1 +#define PROTO_TCP 6 +#define PROTO_UDP 17 + +/* Headezes. */ +#define IP_HLEN 20 /* Size of IP header */ +#define TRANSPORT_HLEN 20 + +#define UDP_HLEN 8 /* Size of UDP header */ +#define TCP_HLEN 20 /* Size of TCP header */ +#define IPUDP_HLEN 28 /* Size of IP + UDP header */ +#define IPTCP_HLEN 40 /* Size of IP + TCP header */ + +/** + * Convert 16-bit quantity from host byte order to network byte order. + * + * This macro is primarily used for converting constants from host + * byte order to network byte order. For converting variables to + * network byte order, use the htons() function instead. + * + * \hideinitializer + */ +#ifndef HTONS +# if BYTE_ORDER == BIG_ENDIAN +# define HTONS(n) (n) +# else /* BYTE_ORDER == BIG_ENDIAN */ +# define HTONS(n) ((((u16_t)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8)) +# endif /* BYTE_ORDER == BIG_ENDIAN */ +#endif /* HTONS */ + + +/** + * Convert 16-bit quantity from host byte order to network byte order. + * + * This function is primarily used for converting variables from host + * byte order to network byte order. For converting constants to + * network byte order, use the HTONS() macro instead. + */ +#ifndef htons +u16_t htons(u16_t val); +#endif /* htons */ + +/** @} */ + +#endif /* __UIP_H__ */ + + +/** @} */ + diff --git a/wii/libogc/lwbt/btarch.h b/wii/libogc/lwbt/btarch.h new file mode 100644 index 0000000000..7697f32b22 --- /dev/null +++ b/wii/libogc/lwbt/btarch.h @@ -0,0 +1,156 @@ +/** + * \defgroup uiparch Architecture specific uIP functions + * @{ + * + * The functions in the architecture specific module implement the IP + * check sum and 32-bit additions. + * + * The IP checksum calculation is the most computationally expensive + * operation in the TCP/IP stack and it therefore pays off to + * implement this in efficient assembler. The purpose of the uip-arch + * module is to let the checksum functions to be implemented in + * architecture specific assembler. + * + */ + +/** + * \file + * Declarations of architecture specific functions. + * \author Adam Dunkels + */ + +/* + * Copyright (c) 2001, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the lwBT stack. + * + * + */ + +#ifndef __BT_ARCH_H__ +#define __BT_ARCH_H__ + +#include "bt.h" +#include "asm.h" +#include "processor.h" + +#define MEM_ALIGNMENT 4 +#define MEM_ALIGN(mem) ((void*)(((u32_t)(mem)+MEM_ALIGNMENT-1)&~(u32_t)(MEM_ALIGNMENT-1))) +#define MEM_ALIGN_SIZE(size) (((size)+MEM_ALIGNMENT-1)&~(u32_t)(MEM_ALIGNMENT-1)) + + +#if BYTE_ORDER == BIG_ENDIAN + #ifndef htole16 + #define htole16 bswap16 + #endif + #ifndef htole32 + #define htole32 bswap32 + #endif + #ifndef htole64 + #define htole64 bswap64 + #endif + #ifndef le16toh + #define le16toh bswap16 + #endif + #ifndef le32toh + #define le32toh bswap32 + #endif + #ifndef le642toh + #define le64toh bswap64 + #endif + #ifndef htons + #define htons(x) (x) + #endif + #ifndef htonl + #define htonl(x) (x) + #endif + #ifndef ntohl + #define ntohl(x) (x) + #endif + #ifndef ntohs + #define ntohs(x) (x) + #endif +#else + #ifndef htole16 + #define htole16 + #endif + #ifndef htole32 + #define htole32 + #endif + #ifndef le16toh + #define le16toh + #endif + #ifndef le32toh + #define le32toh + #endif +#endif + +#if LIBC_MEMFUNCREPLACE +static __inline__ void __memcpy(void *dest,const void *src,s32_t len) +{ + u8_t *dest0 = (u8_t*)dest; + u8_t *src0 = (u8_t*)src; + + while(len--) { + *dest0++ = *src0++; + } +} + +static __inline__ void __memset(void *dest,s32_t c,s32_t len) +{ + u8_t *dest0 = (u8_t*)dest; + + while(len--) { + *dest0++ = (s8_t)c; + } +} + +#define MEMCPY __memcpy +#define MEMSET __memset +#else +#define MEMCPY memcpy +#define MEMSET memset +#endif + +#if LOGGING == 1 +#include +#define LOG(fmt, ...) fprintf(stderr, "[BTLOG] " __FILE__ ":%i: " fmt "\n", __LINE__, ##__VA_ARGS__) +#else +#define LOG(fmt, ...) +#endif /* LOGGING == 1 */ + +#if ERRORING == 1 +#include +#define ERROR(fmt,...) fprintf(stderr, "[BTERR] " __FILE__ ":%i: " fmt "\n", __LINE__, ##__VA_ARGS__) +#else +#define ERROR(fmt, ...) +#endif /* ERRORING == 1 */ + +/** @} */ + +#endif /* __UIP_ARCH_H__ */ diff --git a/wii/libogc/lwbt/bte.c b/wii/libogc/lwbt/bte.c new file mode 100644 index 0000000000..f02bee003b --- /dev/null +++ b/wii/libogc/lwbt/bte.c @@ -0,0 +1,1361 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "bt.h" +#include "bte.h" +#include "hci.h" +#include "l2cap.h" +#include "btmemb.h" +#include "physbusif.h" + + +#define STACKSIZE 32768 +#define MQ_BOX_SIZE 256 + +/* Vendor specific OGF */ +#define HCI_VENDOR_OGF 0x3f + +/* Vendor specific OCF */ +#define HCI_VENDOR_PATCH_START_OCF 0x4f +#define HCI_VENDOR_PATCH_CONT_OCF 0x4c +#define HCI_VENDOR_PATCH_END_OCF 0x4f + +enum bte_state { + STATE_NOTREADY = -1, + STATE_READY = 0, + STATE_CONNECTING, + STATE_CONNECTED, + STATE_DISCONNECTING, + STATE_DISCONNECTED, + STATE_SENDING, + STATE_SENT, + STATE_RECEIVING, + STATE_RECEIVED, + STATE_FAILED +}; + +struct bt_state +{ + err_t last_err; + + syswd_t timer_svc; + lwpq_t hci_cmdq; + u8_t hci_cmddone; + u8_t hci_inited; + + u8_t num_maxdevs; + u8_t num_founddevs; + struct inquiry_info_ex *info; + + btecallback cb; + void *usrdata; +}; + +struct ctrl_req_t +{ + u8 err; + struct pbuf *p; + struct bte_pcb *pcb; + enum bte_state state; + s32 (*sent)(void *arg,struct bte_pcb *pcb,u8 err); + + struct ctrl_req_t *next; +}; + +static struct bt_state btstate; +static u8_t bte_patch0[184] = { + 0x70,0x99,0x08,0x00,0x88,0x43,0xd1,0x07,0x09,0x0c,0x08,0x43,0xa0,0x62,0x19,0x23, + 0xdb,0x01,0x33,0x80,0x7c,0xf7,0x88,0xf8,0x28,0x76,0x80,0xf7,0x17,0xff,0x43,0x78, + 0xeb,0x70,0x19,0x23,0xdb,0x01,0x33,0x87,0x7c,0xf7,0xbc,0xfb,0x0b,0x60,0xa3,0x7b, + 0x01,0x49,0x0b,0x60,0x90,0xf7,0x96,0xfb,0xd8,0x1d,0x08,0x00,0x00,0xf0,0x04,0xf8, + 0x00,0x23,0x79,0xf7,0xe3,0xfa,0x00,0x00,0x00,0xb5,0x00,0x23,0x11,0x49,0x0b,0x60, + 0x1d,0x21,0xc9,0x03,0x0b,0x60,0x7d,0x20,0x80,0x01,0x01,0x38,0xfd,0xd1,0x0e,0x4b, + 0x0e,0x4a,0x13,0x60,0x47,0x20,0x00,0x21,0x96,0xf7,0x96,0xff,0x46,0x20,0x00,0x21, + 0x96,0xf7,0x92,0xff,0x0a,0x4a,0x13,0x68,0x0a,0x48,0x03,0x40,0x13,0x60,0x0a,0x4a, + 0x13,0x68,0x0a,0x48,0x03,0x40,0x13,0x60,0x09,0x4a,0x13,0x68,0x09,0x48,0x03,0x40, + 0x13,0x60,0x00,0xbd,0x24,0x80,0x0e,0x00,0x81,0x03,0x0f,0xfe,0x5c,0x00,0x0f,0x00, + 0x60,0xfc,0x0e,0x00,0xfe,0xff,0x00,0x00,0xfc,0xfc,0x0e,0x00,0xff,0x9f,0x00,0x00, + 0x30,0xfc,0x0e,0x00,0x7f,0xff,0x00,0x00 +}; +static u8_t bte_patch1[92] = { + 0x07,0x20,0xbc,0x65,0x01,0x00,0x84,0x42,0x09,0xd2,0x84,0x42,0x09,0xd1,0x21,0x84, + 0x5a,0x00,0x00,0x83,0xf0,0x74,0xff,0x09,0x0c,0x08,0x43,0x22,0x00,0x61,0x00,0x00, + 0x83,0xf0,0x40,0xfc,0x00,0x00,0x00,0x00,0x23,0xcc,0x9f,0x01,0x00,0x6f,0xf0,0xe4, + 0xfc,0x03,0x28,0x7d,0xd1,0x24,0x3c,0x62,0x01,0x00,0x28,0x20,0x00,0xe0,0x60,0x8d, + 0x23,0x68,0x25,0x04,0x12,0x01,0x00,0x20,0x1c,0x20,0x1c,0x24,0xe0,0xb0,0x21,0x26, + 0x74,0x2f,0x00,0x00,0x86,0xf0,0x18,0xfd,0x21,0x4f,0x3b,0x60 +}; + +static u8 ppc_stack[STACKSIZE] ATTRIBUTE_ALIGN(8); + +err_t acl_wlp_completed(void *arg,struct bd_addr *bdaddr); +err_t link_key_not(void *arg,struct bd_addr *bdaddr,u8_t *key); +err_t pin_req(void *arg,struct bd_addr *bdaddr); +err_t l2cap_connected(void *arg,struct l2cap_pcb *l2cappcb,u16_t result,u16_t status); +err_t l2cap_accepted(void *arg,struct l2cap_pcb *l2cappcb,err_t err); +err_t acl_conn_complete(void *arg,struct bd_addr *bdaddr); +err_t l2cap_disconnect_cfm(void *arg, struct l2cap_pcb *pcb); +err_t l2cap_disconnected_ind(void *arg, struct l2cap_pcb *pcb, err_t err); + +err_t bte_input(void *arg,struct l2cap_pcb *pcb,struct pbuf *p,err_t err); +err_t bte_callback(void (*f)(void*),void *ctx); +err_t bte_hci_apply_patch_complete(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result); +err_t bte_hci_patch_complete(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result); +err_t bte_hci_initcore_complete(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result); +err_t bte_hci_initsub_complete(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result); +err_t bte_inquiry_complete(void *arg,struct hci_pcb *pcb,struct hci_inq_res *ires,u16_t result); +err_t bte_read_stored_link_key_complete(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result); +err_t bte_read_bd_addr_complete(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result); + + +MEMB(bte_pcbs,sizeof(struct bte_pcb),MEMP_NUM_BTE_PCB); +MEMB(bte_ctrl_reqs,sizeof(struct ctrl_req_t),MEMP_NUM_BTE_CTRLS); + +static void bte_reset_all() +{ + btmemb_init(&bte_pcbs); + btmemb_init(&bte_ctrl_reqs); + + if(btstate.info!=NULL) free(btstate.info); + + btstate.info = NULL; + btstate.hci_inited = 0; + btstate.hci_cmddone = 0; + btstate.num_founddevs = 0; + btstate.last_err = ERR_OK; +} + +static void bt_alarmhandler(syswd_t alarm,void *cbarg) +{ + __lwp_thread_dispatchdisable(); + SYS_SwitchFiber(0,0,0,0,(u32)l2cap_tmr,(u32)(&ppc_stack[STACKSIZE])); + __lwp_thread_dispatchunnest(); +} + +static inline s32 __bte_waitcmdfinish(struct bt_state *state) +{ + u32 level; + s32 ret; + + if(!state) return ERR_VAL; + + _CPU_ISR_Disable(level); + while(!state->hci_cmddone) + LWP_ThreadSleep(state->hci_cmdq); + ret = state->last_err; + _CPU_ISR_Restore(level); + + return ret; +} + +static inline s32 __bte_cmdfinish(struct bt_state *state,err_t err) +{ + u32 level; + + if(!state) return ERR_VAL; + + _CPU_ISR_Disable(level); + state->last_err = err; + state->hci_cmddone = 1; + if(state->cb!=NULL) + state->cb(err,state->usrdata); + else + LWP_ThreadSignal(state->hci_cmdq); + _CPU_ISR_Restore(level); + + return err; +} + +static inline s32 __bte_waitrequest(struct ctrl_req_t *req) +{ + s32 err; + u32 level; + + if(!req || !req->pcb) return ERR_VAL; + + _CPU_ISR_Disable(level); + while(req->state!=STATE_SENT + && req->state!=STATE_FAILED) + { + LWP_ThreadSleep(req->pcb->cmdq); + } + err = req->err; + _CPU_ISR_Restore(level); + + return err; +} + +static inline void __bte_close_ctrl_queue(struct bte_pcb *pcb) +{ + struct ctrl_req_t *req; + + while(pcb->ctrl_req_head!=NULL) { + req = pcb->ctrl_req_head; + req->err = ERR_CLSD; + req->state = STATE_DISCONNECTED; + if(req->sent!=NULL) { + req->sent(pcb->cbarg,pcb,ERR_CLSD); + btmemb_free(&bte_ctrl_reqs,req); + } else + LWP_ThreadSignal(pcb->cmdq); + + pcb->ctrl_req_head = req->next; + } + pcb->ctrl_req_tail = NULL; +} + +static s32 __bte_send_pending_request(struct bte_pcb *pcb) +{ + s32 err; + struct ctrl_req_t *req; + + if(pcb->ctrl_req_head==NULL) return ERR_OK; + if(pcb->state==STATE_DISCONNECTING || pcb->state==STATE_DISCONNECTED) return ERR_CLSD; + + req = pcb->ctrl_req_head; + req->state = STATE_SENDING; + + err = l2ca_datawrite(pcb->ctl_pcb,req->p); + btpbuf_free(req->p); + + if(err!=ERR_OK) { + pcb->ctrl_req_head = req->next; + + req->err = err; + req->state = STATE_FAILED; + if(req->sent) { + req->sent(pcb->cbarg,pcb,err); + btmemb_free(&bte_ctrl_reqs,req); + } else + LWP_ThreadSignal(pcb->cmdq); + } + + return err; +} + +static s32 __bte_send_request(struct ctrl_req_t *req) +{ + s32 err; + u32 level; + + req->next = NULL; + req->err = ERR_VAL; + req->state = STATE_READY; + + _CPU_ISR_Disable(level); + if(req->pcb->ctrl_req_head==NULL) { + req->pcb->ctrl_req_head = req->pcb->ctrl_req_tail = req; + err = __bte_send_pending_request(req->pcb); + } else { + req->pcb->ctrl_req_tail->next = req; + req->pcb->ctrl_req_tail = req; + err = ERR_OK; + } + _CPU_ISR_Restore(level); + + return err; +} + +static err_t __bte_shutdown_finished(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result) +{ + err_t err; + struct bt_state *state = (struct bt_state*)arg; + + if(state==NULL) return ERR_OK; + + state->hci_inited = 0; + hci_cmd_complete(NULL); + if(result==HCI_SUCCESS) + err = ERR_OK; + else + err = ERR_CONN; + + physbusif_close(); + return __bte_cmdfinish(state,err); +} + +static void bte_process_handshake(struct bte_pcb *pcb,u8_t param,void *buf,u16_t len) +{ + struct ctrl_req_t *req; + + LOG("bte_process_handshake(%p)\n",pcb); + + switch(param) { + case HIDP_HSHK_SUCCESSFULL: + req = pcb->ctrl_req_head; + pcb->ctrl_req_head = req->next; + + req->err = ERR_OK; + req->state = STATE_SENT; + if(req->sent) { + req->sent(pcb->cbarg,pcb,ERR_OK); + btmemb_free(&bte_ctrl_reqs,req); + } else + LWP_ThreadSignal(pcb->cmdq); + + __bte_send_pending_request(pcb); + break; + case HIDP_HSHK_NOTREADY: + case HIDP_HSHK_INV_REPORTID: + case HIDP_HSHK_NOTSUPPORTED: + case HIDP_HSHK_IVALIDPARAM: + case HIDP_HSHK_UNKNOWNERROR: + break; + case HIDP_HSHK_FATALERROR: + break; + default: + break; + } +} + +static void bte_process_data(struct bte_pcb *pcb,u8_t param,void *buf,u16_t len) +{ + LOG("bte_process_data(%p)\n",pcb); + switch(param) { + case HIDP_DATA_RTYPE_INPUT: + if(pcb->recv!=NULL) pcb->recv(pcb->cbarg,buf,len); + break; + case HIDP_DATA_RTYPE_OTHER: + case HIDP_DATA_RTYPE_OUPUT: + case HIDP_DATA_RTYPE_FEATURE: + break; + default: + break; + } +} + +static err_t bte_process_input(void *arg,struct l2cap_pcb *pcb,struct pbuf *p,err_t err) +{ + u8 *buf; + u16 len; + u8 hdr,type,param; + struct bte_pcb *bte = (struct bte_pcb*)arg; + + LOG("bte_process_input(%p,%p)\n",bte,p); + + if(bte->state==STATE_DISCONNECTING + || bte->state==STATE_DISCONNECTED) return ERR_CLSD; + + buf = p->payload; + len = p->tot_len; + + len--; + hdr = *buf++; + type = (hdr&HIDP_HDR_TRANS_MASK); + param = (hdr&HIDP_HDR_PARAM_MASK); + switch(type) { + case HIDP_TRANS_HANDSHAKE: + bte_process_handshake(bte,param,buf,len); + break; + case HIDP_TRANS_HIDCONTROL: + break; + case HIDP_TRANS_DATA: + bte_process_data(bte,param,buf,len); + break; + default: + break; + } + return ERR_OK; +} + +void BTE_Init() +{ + u32 level; + struct timespec tb; + + LOG("BTE_Init()\n"); + + memset(&btstate,0,sizeof(struct bt_state)); + + hci_init(); + l2cap_init(); + physbusif_init(); + + LWP_InitQueue(&btstate.hci_cmdq); + SYS_CreateAlarm(&btstate.timer_svc); + + _CPU_ISR_Disable(level); + bte_reset_all(); + hci_reset_all(); + l2cap_reset_all(); + physbusif_reset_all(); + + hci_wlp_complete(acl_wlp_completed); + hci_connection_complete(acl_conn_complete); + _CPU_ISR_Restore(level); + + tb.tv_sec = 1; + tb.tv_nsec = 0; + SYS_SetPeriodicAlarm(btstate.timer_svc,&tb,&tb,bt_alarmhandler, NULL); +} + +void BTE_Shutdown() +{ + u32 level; + + if(btstate.hci_inited==0) return; + + LOG("BTE_Shutdown()\n"); + + _CPU_ISR_Disable(level); + SYS_RemoveAlarm(btstate.timer_svc); + btstate.cb = NULL; + btstate.usrdata = NULL; + btstate.hci_cmddone = 0; + hci_arg(&btstate); + hci_cmd_complete(__bte_shutdown_finished); + hci_reset(); + __bte_waitcmdfinish(&btstate); + _CPU_ISR_Restore(level); + + physbusif_shutdown(); +} + +s32 BTE_InitCore(btecallback cb) +{ + u32 level; + + _CPU_ISR_Disable(level); + btstate.cb = cb; + btstate.usrdata = NULL; + btstate.hci_cmddone = 0; + hci_arg(&btstate); + hci_cmd_complete(bte_hci_initcore_complete); + hci_reset(); + _CPU_ISR_Restore(level); + + return ERR_OK; +} + +s32 BTE_ApplyPatch(btecallback cb) +{ + u32 level; + u8 kick = 0; + + _CPU_ISR_Disable(level); + btstate.cb = cb; + btstate.usrdata = NULL; + btstate.hci_cmddone = 0; + hci_arg(&btstate); + hci_cmd_complete(bte_hci_apply_patch_complete); + hci_vendor_specific_command(HCI_VENDOR_PATCH_START_OCF,HCI_VENDOR_OGF,&kick,1); + _CPU_ISR_Restore(level); + + return ERR_OK; +} + +s32 BTE_InitSub(btecallback cb) +{ + u32 level; + + _CPU_ISR_Disable(level); + btstate.cb = cb; + btstate.usrdata = NULL; + btstate.hci_cmddone = 0; + hci_arg(&btstate); + hci_cmd_complete(bte_hci_initsub_complete); + hci_write_inquiry_mode(0x01); + _CPU_ISR_Restore(level); + + return ERR_OK; +} + +s32 BTE_ReadStoredLinkKey(struct linkkey_info *keys,u8 max_cnt,btecallback cb) +{ + u32 level; + + _CPU_ISR_Disable(level); + btstate.cb = cb; + btstate.usrdata = keys; + btstate.num_maxdevs = max_cnt; + btstate.hci_cmddone = 0; + hci_arg(&btstate); + hci_cmd_complete(bte_read_stored_link_key_complete); + hci_read_stored_link_key(); + _CPU_ISR_Restore(level); + + return ERR_OK; +} + +s32 BTE_ReadBdAddr(struct bd_addr *bdaddr, btecallback cb) +{ + u32 level; + + _CPU_ISR_Disable(level); + btstate.cb = cb; + btstate.usrdata = bdaddr; + btstate.hci_cmddone = 0; + hci_arg(&btstate); + hci_cmd_complete(bte_read_bd_addr_complete); + hci_read_bd_addr(); + _CPU_ISR_Restore(level); + + return ERR_OK; +} + +void (*BTE_SetDisconnectCallback(void (*callback)(struct bd_addr *bdaddr,u8 reason)))(struct bd_addr *bdaddr,u8 reason) +{ + return l2cap_disconnect_bb(callback); +} + +struct bte_pcb* bte_new() +{ + struct bte_pcb *pcb; + + if((pcb=btmemb_alloc(&bte_pcbs))==NULL) return NULL; + + memset(pcb,0,sizeof(struct bte_pcb)); + + pcb->state = (u32)STATE_NOTREADY; + LWP_InitQueue(&(pcb->cmdq)); + + return pcb; +} + +s32 bte_registerdeviceasync(struct bte_pcb *pcb,struct bd_addr *bdaddr,s32 (*conn_cfm)(void *arg,struct bte_pcb *pcb,u8 err)) +{ + u32 level; + s32 err = ERR_OK; + struct l2cap_pcb *l2capcb = NULL; + + //printf("bte_registerdeviceasync()\n"); + _CPU_ISR_Disable(level); + pcb->err = ERR_USE; + pcb->data_pcb = NULL; + pcb->ctl_pcb = NULL; + pcb->conn_cfm = conn_cfm; + pcb->state = (u32)STATE_CONNECTING; + + bd_addr_set(&(pcb->bdaddr),bdaddr); + if((l2capcb=l2cap_new())==NULL) { + err = ERR_MEM; + goto error; + } + l2cap_arg(l2capcb,pcb); + + err = l2cap_connect_ind(l2capcb,bdaddr,HIDP_CONTROL_CHANNEL,l2cap_accepted); + if(err!=ERR_OK) { + l2cap_close(l2capcb); + err = ERR_CONN; + goto error; + } + + if((l2capcb=l2cap_new())==NULL) { + err = ERR_MEM; + goto error; + } + l2cap_arg(l2capcb,pcb); + + err = l2cap_connect_ind(l2capcb,bdaddr,HIDP_DATA_CHANNEL,l2cap_accepted); + if(err!=ERR_OK) { + l2cap_close(l2capcb); + err = ERR_CONN; + } + +error: + _CPU_ISR_Restore(level); + //printf("bte_registerdeviceasync(%02x)\n",err); + return err; +} + +s32 bte_inquiry(struct inquiry_info *info,u8 max_cnt,u8 flush) +{ + s32_t i; + u32 level,fnd; + err_t last_err; + struct inquiry_info_ex *pinfo; + + last_err = ERR_OK; + + _CPU_ISR_Disable(level); + if(btstate.num_founddevs==0 || flush==1) { + btstate.hci_cmddone = 0; + btstate.num_maxdevs = max_cnt; + hci_inquiry(0x009E8B33,0x03,max_cnt,bte_inquiry_complete); + last_err = __bte_waitcmdfinish(&btstate); + } + fnd = btstate.num_founddevs; + pinfo = btstate.info; + _CPU_ISR_Restore(level); + + if(last_err==ERR_OK) { + for(i=0;istate = (u32)STATE_DISCONNECTING; + if(pcb->data_pcb!=NULL ) + err = l2ca_disconnect_req(pcb->data_pcb,l2cap_disconnect_cfm); + else if(pcb->ctl_pcb!=NULL) + err = l2ca_disconnect_req(pcb->ctl_pcb,l2cap_disconnect_cfm); + _CPU_ISR_Restore(level); + + return err; +} + +/* +s32 bte_connect(struct bte_pcb *pcb,struct bd_addr *bdaddr,u8 psm,s32 (*recv)(void *arg,void *buffer,u16 len)) +{ + u32 level; + err_t err = ERR_OK; + + if(pcb==NULL) return ERR_VAL; + + if((pcb->l2capcb=l2cap_new())==NULL) return ERR_MEM; + + pcb->psm = psm; + pcb->recv = recv; + bd_addr_set(&(pcb->bdaddr),bdaddr); + + _CPU_ISR_Disable(level); + pcb->err = ERR_CONN; + l2cap_arg(pcb->l2capcb,pcb); + err = l2ca_connect_req(pcb->l2capcb,bdaddr,psm,HCI_ALLOW_ROLE_SWITCH,l2cap_connected); + if(err==ERR_OK) { + LWP_ThreadSleep(pcb->cmdq); + err = pcb->err; + } + _CPU_ISR_Restore(level); + + return err; +} + +s32 bte_connect_ex(struct bte_pcb *pcb,struct inquiry_info_ex *info,u8 psm,s32 (*recv)(void *arg,void *buffer,u16 len)) +{ + err_t err; + + if((err=hci_reg_dev_info(&(info->bdaddr),info->cod,info->psrm,info->psm,info->co))!=ERR_OK) return err; + return bte_connect(pcb,&(info->bdaddr),psm,recv); +} + +s32 bte_listen(struct bte_pcb *pcb,struct bd_addr *bdaddr,u8 psm) +{ + s32 err; + u32 level; + struct l2cap_pcb *l2capcb = NULL; + + if(pcb==NULL) return ERR_VAL; + + if((l2capcb=l2cap_new())==NULL) return ERR_MEM; + pcb->l2capcb = NULL; + + pcb->psm = psm; + pcb->recv = NULL; + bd_addr_set(&(pcb->bdaddr),bdaddr); + + _CPU_ISR_Disable(level); + pcb->err = ERR_CONN; + l2cap_arg(l2capcb,pcb); + err = l2cap_connect_ind(l2capcb,psm,l2cap_accepted); + if(err!=ERR_OK) l2cap_close(l2capcb); + + _CPU_ISR_Restore(level); + return err; +} + +s32 bte_accept(struct bte_pcb *pcb,s32 (*recv)(void *arg,void *buffer,u16 len)) +{ + u32 level; + err_t err = ERR_OK; + + if(pcb==NULL) return ERR_VAL; + + _CPU_ISR_Disable(level); + pcb->recv = recv; + while(pcb->l2capcb==NULL) + LWP_ThreadSleep(pcb->cmdq); + err = pcb->err; + _CPU_ISR_Restore(level); + + return err; +} +*/ + +s32 bte_senddata(struct bte_pcb *pcb,void *message,u16 len) +{ + err_t err; + struct pbuf *p; + + if(pcb==NULL || message==NULL || len==0) return ERR_VAL; + if(pcb->state==STATE_DISCONNECTING || pcb->state==STATE_DISCONNECTED) return ERR_CLSD; + + if((p=btpbuf_alloc(PBUF_RAW,(1 + len),PBUF_RAM))==NULL) { + ERROR("bte_senddata: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + ((u8*)p->payload)[0] = (HIDP_TRANS_DATA|HIDP_DATA_RTYPE_OUPUT); + memcpy(p->payload+1,message,len); + + err = l2ca_datawrite(pcb->data_pcb,p); + btpbuf_free(p); + + return err; +} + +s32 bte_sendmessageasync(struct bte_pcb *pcb,void *message,u16 len,s32 (*sent)(void *arg,struct bte_pcb *pcb,u8 err)) +{ + struct pbuf *p; + struct ctrl_req_t *req; + + //printf("bte_sendmessageasync()\n"); + + if(pcb==NULL || message==NULL || len==0) return ERR_VAL; + if(pcb->state==STATE_DISCONNECTING || pcb->state==STATE_DISCONNECTED) return ERR_CLSD; + + if((req=btmemb_alloc(&bte_ctrl_reqs))==NULL) { + ERROR("bte_sendmessageasync: Could not allocate memory for request\n"); + return ERR_MEM; + } + + if((p=btpbuf_alloc(PBUF_RAW,(1 + len),PBUF_RAM))==NULL) { + ERROR("bte_sendmessageasync: Could not allocate memory for pbuf\n"); + btmemb_free(&bte_ctrl_reqs,req); + return ERR_MEM; + } + + ((u8*)p->payload)[0] = (HIDP_TRANS_SETREPORT|HIDP_DATA_RTYPE_OUPUT); + memcpy(p->payload+1,message,len); + + req->p = p; + req->pcb = pcb; + req->sent = sent; + return __bte_send_request(req); +} + +s32 bte_sendmessage(struct bte_pcb *pcb,void *message,u16 len) +{ + s32 err = ERR_VAL; + struct pbuf *p; + struct ctrl_req_t *req; + + //printf("bte_sendmessage()\n"); + + if(pcb==NULL || message==NULL || len==0) return ERR_VAL; + if(pcb->state==STATE_DISCONNECTING || pcb->state==STATE_DISCONNECTED) return ERR_CLSD; + + if((req=btmemb_alloc(&bte_ctrl_reqs))==NULL) { + ERROR("bte_sendmessage: Could not allocate memory for request\n"); + return ERR_MEM; + } + + if((p=btpbuf_alloc(PBUF_RAW,(1 + len),PBUF_RAM))==NULL) { + ERROR("bte_sendmessage: Could not allocate memory for pbuf\n"); + btmemb_free(&bte_ctrl_reqs,req); + return ERR_MEM; + } + + ((u8*)p->payload)[0] = (HIDP_TRANS_SETREPORT|HIDP_DATA_RTYPE_OUPUT); + memcpy(p->payload+1,message,len); + + req->p = p; + req->pcb = pcb; + req->sent = NULL; + err = __bte_send_request(req); + if(err==ERR_OK) err = __bte_waitrequest(req); + + btmemb_free(&bte_ctrl_reqs,req); + return err; +} + +void bte_arg(struct bte_pcb *pcb,void *arg) +{ + u32 level; + _CPU_ISR_Disable(level); + pcb->cbarg = arg; + _CPU_ISR_Restore(level); +} + +void bte_received(struct bte_pcb *pcb, s32 (*recv)(void *arg,void *buffer,u16 len)) +{ + u32 level; + _CPU_ISR_Disable(level); + pcb->recv = recv; + _CPU_ISR_Restore(level); +} + +void bte_disconnected(struct bte_pcb *pcb,s32 (disconn_cfm)(void *arg,struct bte_pcb *pcb,u8 err)) +{ + u32 level; + _CPU_ISR_Disable(level); + pcb->disconn_cfm = disconn_cfm; + _CPU_ISR_Restore(level); +} + +err_t acl_wlp_completed(void *arg,struct bd_addr *bdaddr) +{ + //hci_sniff_mode(bdaddr,200,100,10,10); + return ERR_OK; +} + +err_t acl_conn_complete(void *arg,struct bd_addr *bdaddr) +{ + //printf("acl_conn_complete\n"); + //memcpy(&(btstate.acl_bdaddr),bdaddr,6); + + hci_write_link_policy_settings(bdaddr,0x0005); + return ERR_OK; +} + +err_t pin_req(void *arg,struct bd_addr *bdaddr) +{ + //printf("pin_req\n"); + return ERR_OK; +} + +err_t l2cap_disconnected_ind(void *arg, struct l2cap_pcb *pcb, err_t err) +{ + struct bte_pcb *bte = (struct bte_pcb*)arg; + + if(bte==NULL) return ERR_OK; + + bte->state = (u32)STATE_DISCONNECTING; + switch(l2cap_psm(pcb)) { + case HIDP_CONTROL_CHANNEL: + l2cap_close(bte->ctl_pcb); + bte->ctl_pcb = NULL; + break; + case HIDP_DATA_CHANNEL: + l2cap_close(bte->data_pcb); + bte->data_pcb = NULL; + break; + } + if(bte->data_pcb==NULL && bte->ctl_pcb==NULL) { + bte->err = ERR_OK; + bte->state = (u32)STATE_DISCONNECTED; + __bte_close_ctrl_queue(bte); + if(bte->disconn_cfm!=NULL) bte->disconn_cfm(bte->cbarg,bte,ERR_OK); + } + return ERR_OK; +} + +err_t l2cap_disconnect_cfm(void *arg, struct l2cap_pcb *pcb) +{ + struct bte_pcb *bte = (struct bte_pcb*)arg; + + if(bte==NULL) return ERR_OK; + + switch(l2cap_psm(pcb)) { + case HIDP_CONTROL_CHANNEL: + l2cap_close(bte->ctl_pcb); + bte->ctl_pcb = NULL; + if(bte->data_pcb!=NULL) + l2ca_disconnect_req(bte->data_pcb,l2cap_disconnect_cfm); + break; + case HIDP_DATA_CHANNEL: + l2cap_close(bte->data_pcb); + bte->data_pcb = NULL; + if(bte->ctl_pcb!=NULL) + l2ca_disconnect_req(bte->ctl_pcb,l2cap_disconnect_cfm); + break; + } + if(bte->data_pcb==NULL && bte->ctl_pcb==NULL) { + bte->err = ERR_OK; + bte->state = (u32)STATE_DISCONNECTED; + __bte_close_ctrl_queue(bte); + if(bte->disconn_cfm!=NULL) bte->disconn_cfm(bte->cbarg,bte,ERR_OK); + + hci_cmd_complete(NULL); + hci_disconnect(&(bte->bdaddr),HCI_OTHER_END_TERMINATED_CONN_USER_ENDED); + } + + return ERR_OK; +} + +err_t link_key_not(void *arg,struct bd_addr *bdaddr,u8_t *key) +{ + //printf("link_key_not\n"); + return hci_write_stored_link_key(bdaddr,key); +} + +/* +err_t l2cap_connected(void *arg,struct l2cap_pcb *l2cappcb,u16_t result,u16_t status) +{ + struct bte_pcb *btepcb = (struct bte_pcb*)arg; + + printf("l2cap_connected(%02x)\n",result); + if(result==L2CAP_CONN_SUCCESS) { + l2cap_recv(l2cappcb,bte_input); + l2cap_disconnect_ind(l2cappcb,l2cap_disconnected_ind); + btepcb->err = ERR_OK; + } else { + l2cap_close(l2cappcb); + btepcb->err = ERR_CONN; + } + + if(btepcb->conn_cfm) btepcb->conn_cfm(btepcb->cbarg,btepcb,btepcb->err); + LWP_ThreadSignal(btepcb->cmdq); + return ERR_OK; +} +*/ +err_t l2cap_accepted(void *arg,struct l2cap_pcb *l2cappcb,err_t err) +{ + struct bte_pcb *btepcb = (struct bte_pcb*)arg; + + //printf("l2cap_accepted(%02x)\n",err); + if(err==ERR_OK) { + l2cap_recv(l2cappcb,bte_process_input); + l2cap_disconnect_ind(l2cappcb,l2cap_disconnected_ind); + switch(l2cap_psm(l2cappcb)) { + case HIDP_CONTROL_CHANNEL: + btepcb->ctl_pcb = l2cappcb; + break; + case HIDP_DATA_CHANNEL: + btepcb->data_pcb = l2cappcb; + break; + } + if(btepcb->data_pcb && btepcb->ctl_pcb) { + btepcb->err = ERR_OK; + btepcb->state = (u32)STATE_CONNECTED; + if(btepcb->conn_cfm) btepcb->conn_cfm(btepcb->cbarg,btepcb,ERR_OK); + } + } else { + l2cap_close(l2cappcb); + btepcb->err = ERR_CONN; + btepcb->conn_cfm(btepcb->cbarg,btepcb,ERR_CONN); + } + + return ERR_OK; +} + +err_t bte_inquiry_complete(void *arg,struct hci_pcb *pcb,struct hci_inq_res *ires,u16_t result) +{ + u8_t i; + struct hci_inq_res *p; + struct bt_state *state = (struct bt_state*)arg; + + if(result==HCI_SUCCESS) { + if(ires!=NULL) { + + if(btstate.info!=NULL) free(btstate.info); + btstate.info = NULL; + btstate.num_maxdevs = 0; + btstate.num_founddevs = 0; + + p = ires; + while(p!=NULL) { + btstate.num_founddevs++; + p = p->next; + } + + p = ires; + btstate.info = (struct inquiry_info_ex*)malloc(sizeof(struct inquiry_info_ex)*btstate.num_founddevs); + for(i=0;ibdaddr)); + memcpy(btstate.info[i].cod,p->cod,3); + btstate.info[i].psrm = p->psrm; + btstate.info[i].psm = p->psm; + btstate.info[i].co = p->co; + + printf("bdaddr: %02x:%02x:%02x:%02x:%02x:%02x\n",p->bdaddr.addr[0],p->bdaddr.addr[1],p->bdaddr.addr[2],p->bdaddr.addr[3],p->bdaddr.addr[4],p->bdaddr.addr[5]); + printf("cod: %02x%02x%02x\n",p->cod[0],p->cod[1],p->cod[2]); + printf("psrm: %02x\n",p->psrm); + printf("psm: %02x\n",p->psm); + printf("co: %04x\n",p->co); + p = p->next; + } + __bte_cmdfinish(state,ERR_OK); + } else + hci_inquiry(0x009E8B33,0x03,btstate.num_maxdevs,bte_inquiry_complete); + } + return ERR_OK; +} + +err_t bte_read_stored_link_key_complete(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result) +{ + u8_t i = 0; + struct hci_link_key *p; + struct linkkey_info *keys; + struct bt_state *state = (struct bt_state*)arg; + + if(!pcb) return ERR_CONN; + + LOG("bte_read_stored_link_key_complete(%02x,%p)\n",result,pcb->keyres); + + if(state==NULL) return ERR_VAL; + if(!(ogf==HCI_HC_BB_OGF && ocf==HCI_R_STORED_LINK_KEY_OCF)) return __bte_cmdfinish(state,ERR_CONN); + + if(result==HCI_SUCCESS) { + keys = (struct linkkey_info*)state->usrdata; + if(pcb->keyres!=NULL && keys!=NULL) { + for(i=0,p=pcb->keyres;inum_maxdevs && p!=NULL;i++) { + bd_addr_set(&(keys[i].bdaddr),&(p->bdaddr)); + memcpy(keys[i].key,p->key,16); + + p = p->next; + } + } + LOG("bte_read_stored_link_key_complete(%02x,%p,%d)\n",result,pcb->keyres,i); + __bte_cmdfinish(state,i); + return ERR_OK; + } + + return __bte_cmdfinish(state,ERR_VAL); +} + +err_t bte_read_bd_addr_complete(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result) +{ + struct bd_addr *bdaddr; + struct bt_state *state = (struct bt_state*)arg; + + if(!pcb) return ERR_CONN; + + LOG("bte_read_bd_addr_complete(%02x,%p)\n", result, &pcb->bdaddr); + + if(state==NULL) return ERR_VAL; + + if(!(ogf==HCI_INFO_PARAM_OGF && ocf==HCI_R_BD_ADDR_OCF)) return __bte_cmdfinish(state,ERR_CONN); + + if(result == HCI_SUCCESS) { + bdaddr = (struct bd_addr *)state->usrdata; + if (bdaddr != NULL) { + bdaddr->addr[0] = pcb->bdaddr.addr[5]; + bdaddr->addr[1] = pcb->bdaddr.addr[4]; + bdaddr->addr[2] = pcb->bdaddr.addr[3]; + bdaddr->addr[3] = pcb->bdaddr.addr[2]; + bdaddr->addr[4] = pcb->bdaddr.addr[1]; + bdaddr->addr[5] = pcb->bdaddr.addr[0]; + } + LOG("bte_read_bd_addr_complete(%02x,%p,%d)\n",result,bdaddr,i); + __bte_cmdfinish(state,ERR_OK); + return ERR_OK; + } + + return __bte_cmdfinish(state,ERR_VAL); +} + +/* new init with patching */ +err_t bte_hci_initcore_complete2(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result) +{ + err_t err = ERR_OK; + u8_t dev_cod[] = {0x04, 0x02,0x40}; + struct bt_state *state = (struct bt_state*)arg; + + LOG("bte_hci_initcore_complete2(%02x,%02x)\n",ogf,ocf); + switch(ogf) { + case HCI_HC_BB_OGF: + if(ocf==HCI_WRITE_INQUIRY_MODE) { + if(result==HCI_SUCCESS) { + hci_write_page_scan_type(0x01); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_PAGE_SCAN_TYPE) { + if(result==HCI_SUCCESS) { + hci_write_inquiry_scan_type(0x01); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_INQUIRY_SCAN_TYPE) { + if(result==HCI_SUCCESS) { + hci_write_cod(dev_cod); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_COD) { + if(result==HCI_SUCCESS) { + hci_write_page_timeout(0x2000); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_PAGE_TIMEOUT) { + if(result==HCI_SUCCESS) { + state->hci_inited = 1; + hci_cmd_complete(NULL); + return __bte_cmdfinish(state,ERR_OK); + } else + err = ERR_CONN; + } + break; + default: + LOG("Unknown command complete event. OGF = 0x%x OCF = 0x%x\n", ogf, ocf); + err = ERR_CONN; + break; + } + + if(err!=ERR_OK) __bte_cmdfinish(state,err); + return err; +} + +err_t bte_hci_initcore_complete(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result) +{ + err_t err = ERR_OK; + u8_t dev_cod[] = {0x00, 0x1f,0x00}; + struct bt_state *state = (struct bt_state*)arg; + + LOG("bte_hci_initcore_complete(%02x,%02x)\n",ogf,ocf); + switch(ogf) { + case HCI_INFO_PARAM: + if(ocf==HCI_READ_BUFFER_SIZE) { + if(result==HCI_SUCCESS) { + hci_write_cod(dev_cod); + } else + err = ERR_CONN; + } else if(ocf==HCI_READ_LOCAL_VERSION) { + if(result==HCI_SUCCESS) { + hci_read_bd_addr(); + } else + err = ERR_CONN; + } else if(ocf==HCI_READ_BD_ADDR) { + if(result==HCI_SUCCESS) { + hci_read_local_features(); + } else + err = ERR_CONN; + } else if(ocf==HCI_READ_LOCAL_FEATURES) { + if(result==HCI_SUCCESS) { + hci_cmd_complete(bte_hci_initcore_complete2); + hci_write_inquiry_mode(0x01); + } else + err = ERR_CONN; + } + break; + case HCI_HC_BB_OGF: + if(ocf==HCI_RESET) { + if(result==HCI_SUCCESS) { + hci_read_buffer_size(); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_COD) { + if(result==HCI_SUCCESS) { + hci_write_local_name((u8_t*)"",1); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_LOCAL_NAME) { + if(result==HCI_SUCCESS) { + hci_write_pin_type(0x00); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_PIN_TYPE) { + if(result==HCI_SUCCESS) { + hci_host_buffer_size(); + } else + err = ERR_CONN; + } else if(ocf==HCI_HOST_BUF_SIZE) { + if(result==HCI_SUCCESS) { + hci_read_local_version(); + } else + err = ERR_CONN; + } + break; + default: + LOG("Unknown command complete event. OGF = 0x%x OCF = 0x%x\n", ogf, ocf); + err = ERR_CONN; + break; + } + + if(err!=ERR_OK) __bte_cmdfinish(state,err); + return err; +} + +err_t bte_hci_apply_patch_complete(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result) +{ + err_t err = ERR_OK; + struct bt_state *state = (struct bt_state*)arg; + + LOG("bte_hci_apply_patch_complete(%02x,%02x,%02x)\n",ogf,ocf,result); + switch(ogf) { + case HCI_VENDOR_OGF: + if(ocf==HCI_VENDOR_PATCH_START_OCF) { + if(result==HCI_SUCCESS) { + err = hci_vendor_specific_command(HCI_VENDOR_PATCH_CONT_OCF,HCI_VENDOR_OGF,bte_patch0,184); + } else + err = ERR_CONN; + } else if(ocf==HCI_VENDOR_PATCH_CONT_OCF) { + if(result==HCI_SUCCESS) { + hci_cmd_complete(bte_hci_patch_complete); + err = hci_vendor_specific_command(HCI_VENDOR_PATCH_END_OCF,HCI_VENDOR_OGF,bte_patch1,92); + } else + err = ERR_CONN; + } + break; + default: + LOG("Unknown command complete event. OGF = 0x%x OCF = 0x%x\n", ogf, ocf); + err = ERR_CONN; + break; + } + + if(err!=ERR_OK) __bte_cmdfinish(state,err); + return err; +} + +err_t bte_hci_patch_complete(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result) +{ + err_t err = ERR_OK; + u8_t dev_cod[] = {0x04, 0x02,0x40}; + struct bt_state *state = (struct bt_state*)arg; + + LOG("bte_hci_patch_complete(%02x,%02x,%02x)\n",ogf,ocf,result); + switch(ogf) { + case HCI_INFO_PARAM: + if(ocf==HCI_READ_BUFFER_SIZE) { + if(result==HCI_SUCCESS) { + hci_write_cod(dev_cod); + } else + err = ERR_CONN; + } else if(ocf==HCI_READ_LOCAL_VERSION) { + if(result==HCI_SUCCESS) { + hci_read_bd_addr(); + } else + err = ERR_CONN; + } else if(ocf==HCI_READ_BD_ADDR) { + if(result==HCI_SUCCESS) { + hci_read_local_features(); + } else + err = ERR_CONN; + } else if(ocf==HCI_READ_LOCAL_FEATURES) { + if(result==HCI_SUCCESS) { + hci_cmd_complete(NULL); + return __bte_cmdfinish(state,ERR_OK); + } else + err = ERR_CONN; + } + break; + case HCI_HC_BB_OGF: + if(ocf==HCI_RESET) { + if(result==HCI_SUCCESS) { + hci_read_buffer_size(); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_COD) { + if(result==HCI_SUCCESS) { + hci_write_local_name((u8_t*)"",1); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_LOCAL_NAME) { + if(result==HCI_SUCCESS) { + hci_write_pin_type(0x00); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_PIN_TYPE) { + if(result==HCI_SUCCESS) { + hci_host_buffer_size(); + } else + err = ERR_CONN; + } else if(ocf==HCI_HOST_BUF_SIZE) { + if(result==HCI_SUCCESS) { + hci_read_local_version(); + } else + err = ERR_CONN; + } + break; + case HCI_VENDOR_OGF: + if(ocf==HCI_VENDOR_PATCH_END_OCF) { + if(result==HCI_SUCCESS) { + err = hci_reset(); + } else + err = ERR_CONN; + } + break; + default: + LOG("Unknown command complete event. OGF = 0x%x OCF = 0x%x\n", ogf, ocf); + err = ERR_CONN; + break; + } + + if(err!=ERR_OK) __bte_cmdfinish(state,err); + return err; +} + +err_t bte_hci_initsub_complete(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result) +{ + err_t err = ERR_OK; + u8_t dev_cod[] = {0x00, 0x04,0x48}; + struct bt_state *state = (struct bt_state*)arg; + + LOG("bte_hci_initsub_complete(%02x,%02x)\n",ogf,ocf); + switch(ogf) { + case HCI_HC_BB_OGF: + if(ocf==HCI_WRITE_INQUIRY_MODE) { + if(result==HCI_SUCCESS) { + hci_write_page_scan_type(0x01); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_PAGE_SCAN_TYPE) { + if(result==HCI_SUCCESS) { + hci_write_inquiry_scan_type(0x01); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_INQUIRY_SCAN_TYPE) { + if(result==HCI_SUCCESS) { + hci_write_cod(dev_cod); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_COD) { + if(result==HCI_SUCCESS) { + hci_write_page_timeout(0x8000); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_PAGE_TIMEOUT) { + if(result==HCI_SUCCESS) { + hci_write_local_name((u8_t*)"Wii",4); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_LOCAL_NAME) { + if(result==HCI_SUCCESS) { + hci_write_scan_enable(0x02); + } else + err = ERR_CONN; + } else if(ocf==HCI_WRITE_SCAN_ENABLE) { + if(result==HCI_SUCCESS) { + hci_cmd_complete(NULL); + return __bte_cmdfinish(state,ERR_OK); + } else + err = ERR_CONN; + } + break; + default: + LOG("Unknown command complete event. OGF = 0x%x OCF = 0x%x\n", ogf, ocf); + err = ERR_CONN; + break; + + } + + if(err!=ERR_OK) __bte_cmdfinish(state,err); + return err; +} + diff --git a/wii/libogc/lwbt/btmemb.c b/wii/libogc/lwbt/btmemb.c new file mode 100644 index 0000000000..899a3359e5 --- /dev/null +++ b/wii/libogc/lwbt/btmemb.c @@ -0,0 +1,70 @@ +#include +#include + +#include "asm.h" +#include "processor.h" + +#include "bt.h" +#include "btmemb.h" + +void btmemb_init(struct memb_blks *blk) +{ + MEMSET(blk->mem,0,(MEM_ALIGN_SIZE(blk->size)+sizeof(u32))*blk->num); +} + +void* btmemb_alloc(struct memb_blks *blk) +{ + s32 i; + u32 *ptr; + u32 level; + void *p; + + _CPU_ISR_Disable(level); + ptr = (u32*)blk->mem; + for(i=0;inum;i++) { + if(*ptr==0) { + ++(*ptr); + p = (ptr+1); + _CPU_ISR_Restore(level); + return p; + } + ptr = (u32*)((u8*)ptr+(MEM_ALIGN_SIZE(blk->size)+sizeof(u32))); + } + _CPU_ISR_Restore(level); + return NULL; +} + +u8 btmemb_free(struct memb_blks *blk,void *ptr) +{ + u8 ref; + s32 i; + u32 level; + u32 *ptr2,*ptr1; + + _CPU_ISR_Disable(level); + ptr1 = ptr; + ptr2 = (u32*)blk->mem; + for(i=0;inum;i++) { + if(ptr2==(ptr1 - 1)) { + ref = --(*ptr2); + _CPU_ISR_Restore(level); + return ref; + } + ptr2 = (u32*)((u8*)ptr2+(MEM_ALIGN_SIZE(blk->size)+sizeof(u32))); + } + _CPU_ISR_Restore(level); + return -1; +} + +u8 btmemb_ref(struct memb_blks *blk,void *ptr) +{ + u8 ref; + u32 *pref; + u32 level; + + _CPU_ISR_Disable(level); + pref = ptr-sizeof(u32); + ref = ++(*pref); + _CPU_ISR_Restore(level); + return ref; +} diff --git a/wii/libogc/lwbt/btmemb.h b/wii/libogc/lwbt/btmemb.h new file mode 100644 index 0000000000..863456acda --- /dev/null +++ b/wii/libogc/lwbt/btmemb.h @@ -0,0 +1,21 @@ +#ifndef __BTMEMB_H__ +#define __BTMEMB_H__ + +#include + +#define MEMB(name,size,num) \ + static u8 memb_mem_##name[(MEM_ALIGN_SIZE(size)+sizeof(u32))*num]; \ + static struct memb_blks name = {size,num,memb_mem_##name} + +struct memb_blks { + u16 size; + u16 num; + u8 *mem; +}; + +void btmemb_init(struct memb_blks *blk); +void* btmemb_alloc(struct memb_blks *blk); +u8 btmemb_free(struct memb_blks *blk,void *ptr); +u8 btmemb_ref(struct memb_blks *blk,void *ptr); + +#endif diff --git a/wii/libogc/lwbt/btmemr.c b/wii/libogc/lwbt/btmemr.c new file mode 100644 index 0000000000..cd23473596 --- /dev/null +++ b/wii/libogc/lwbt/btmemr.c @@ -0,0 +1,166 @@ +#include +#include + +#include "asm.h" +#include "processor.h" + +#include "bt.h" +#include "btmemr.h" + +#define MIN_SIZE 12 +#define SIZEOF_STRUCT_MEM (sizeof(struct mem)+(((sizeof(struct mem)%MEM_ALIGNMENT)==0)?0:(4-(sizeof(struct mem)%MEM_ALIGNMENT)))) + +struct mem { + u32 next,prev; + u32 used; +}; + +static struct mem *ram_free; +static struct mem *ram_end; +static u8 ram_block[sizeof(struct mem)+MEM_SIZE+MEM_ALIGNMENT]; + +static void plug_holes(struct mem *rmem) +{ + struct mem *nmem; + struct mem *pmem; + + nmem = (struct mem*)&ram_block[rmem->next]; + if(rmem!=nmem && nmem->used==0 && (u8_t*)nmem!=(u8_t*)ram_end) { + if(ram_free==nmem) ram_free = rmem; + + rmem->next = nmem->next; + ((struct mem*)&ram_block[nmem->next])->prev = (u8_t*)rmem - ram_block; + } + + pmem = (struct mem*)&ram_block[rmem->prev]; + if(pmem!=rmem && pmem->used==0) { + if(ram_free==rmem) ram_free = pmem; + pmem->next = rmem->next; + ((struct mem*)&ram_block[rmem->next])->prev = (u8_t*)pmem - ram_block; + } +} + +void btmemr_init() +{ + u32 level; + struct mem *rmem; + + MEMSET(ram_block,0,MEM_SIZE); + + _CPU_ISR_Disable(level); + rmem = (struct mem*)ram_block; + rmem->next = MEM_SIZE; + rmem->prev = 0; + rmem->used = 0; + + ram_end = (struct mem*)&ram_block[MEM_SIZE]; + ram_end->used = 1; + ram_end->prev = MEM_SIZE; + ram_end->next = MEM_SIZE; + + ram_free = (struct mem*)ram_block; + _CPU_ISR_Restore(level); +} + +void* btmemr_malloc(u32 size) +{ + u32 level; + u32 ptr,ptr2; + struct mem *rmem,*rmem2; + + if(size==0) return NULL; + + if(size%MEM_ALIGNMENT) size += MEM_ALIGNMENT - ((size+SIZEOF_STRUCT_MEM)%SIZEOF_STRUCT_MEM); + if(size>MEM_SIZE) return NULL; + + _CPU_ISR_Disable(level); + for(ptr = (u8_t*)ram_free - ram_block;ptrnext) { + rmem = (struct mem*)&ram_block[ptr]; + if(!rmem->used && rmem->next - (ptr + SIZEOF_STRUCT_MEM)>=size + SIZEOF_STRUCT_MEM) { + ptr2 = ptr + SIZEOF_STRUCT_MEM + size; + rmem2 = (struct mem*)&ram_block[ptr2]; + + rmem2->prev = ptr; + rmem2->next = rmem->next; + rmem->next = ptr2; + if(rmem->next!=MEM_SIZE) ((struct mem*)&ram_block[rmem2->next])->prev = ptr2; + + rmem2->used = 0; + rmem->used = 1; + + if(rmem==ram_free) { + while(ram_free->used && ram_free!=ram_end) ram_free = (struct mem*)&ram_block[ram_free->next]; + } + + _CPU_ISR_Restore(level); + return (u8_t*)rmem+SIZEOF_STRUCT_MEM; + } + } + _CPU_ISR_Restore(level); + return NULL; +} + +void btmemr_free(void *ptr) +{ + u32 level; + struct mem *rmem; + + if(ptr==NULL) return; + if((u8_t*)ptr<(u8_t*)ram_block || (u8_t*)ptr>=(u8_t*)ram_end) return; + + _CPU_ISR_Disable(level); + rmem = (struct mem*)((u8_t*)ptr - SIZEOF_STRUCT_MEM); + rmem->used = 0; + + if(rmemMEM_SIZE) return NULL; + if((u8_t*)ptr<(u8_t*)ram_block || (u8_t*)ptr>=(u8_t*)ram_end) { + ERROR("memr_realloc: illegal memory.\n"); + return ptr; + } + + _CPU_ISR_Disable(level); + rmem = (struct mem*)((u8_t*)ptr - SIZEOF_STRUCT_MEM); + ptr1 = (u8_t*)rmem - ram_block; + size = rmem->next - ptr1 - SIZEOF_STRUCT_MEM; + + if(newsize+SIZEOF_STRUCT_MEM+MIN_SIZEused = 0; + rmem2->next = rmem->next; + rmem2->prev = ptr1; + rmem->next = ptr2; + if(rmem2->next!=MEM_SIZE) ((struct mem*)&ram_block[rmem2->next])->prev = ptr2; + + plug_holes(rmem2); + } + _CPU_ISR_Restore(level); + + return ptr; +} + +void* btmemr_reallocm(void *ptr,u32 newsize) +{ + void *nmem; + + nmem = btmemr_malloc(newsize); + if(nmem==NULL) return btmemr_realloc(ptr,newsize); + + MEMCPY(nmem,ptr,newsize); + btmemr_free(ptr); + + return nmem; +} diff --git a/wii/libogc/lwbt/btmemr.h b/wii/libogc/lwbt/btmemr.h new file mode 100644 index 0000000000..f9c71523ae --- /dev/null +++ b/wii/libogc/lwbt/btmemr.h @@ -0,0 +1,12 @@ +#ifndef __BTMEMR_H__ +#define __BTMEMR_H__ + +#include + +void btmemr_init(); +void* btmemr_malloc(u32 size); +void btmemr_free(void *ptr); +void* btmemr_realloc(void *ptr,u32 newsize); +void* btmemr_reallocm(void *ptr,u32 newsize); + +#endif diff --git a/wii/libogc/lwbt/btopt.h b/wii/libogc/lwbt/btopt.h new file mode 100644 index 0000000000..28485f10b1 --- /dev/null +++ b/wii/libogc/lwbt/btopt.h @@ -0,0 +1,352 @@ +/** + * \defgroup uipopt Configuration options for uIP + * @{ + * + * uIP is configured using the per-project configuration file + * "uipopt.h". This file contains all compile-time options for uIP and + * should be tweaked to match each specific project. The uIP + * distribution contains a documented example "uipopt.h" that can be + * copied and modified for each project. + */ + +/** + * \file + * Configuration options for uIP. + * \author Adam Dunkels + * + * This file is used for tweaking various configuration options for + * uIP. You should make a copy of this file into one of your project's + * directories instead of editing this example "uipopt.h" file that + * comes with the uIP distribution. + */ + +/* + * Copyright (c) 2001-2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack. + * + * + */ + +#ifndef __BTOPT_H__ +#define __BTOPT_H__ + +#include +#include +#include + +/*------------------------------------------------------------------------------*/ +/** + * \defgroup uipopttypedef uIP type definitions + * @{ + */ + +/** + * The 8-bit unsigned data type. + * + * This may have to be tweaked for your particular compiler. "unsigned + * char" works for most compilers. + */ +typedef u8 u8_t; + +/** + * The 8-bit signed data type. + * + * This may have to be tweaked for your particular compiler. "unsigned + * char" works for most compilers. + */ +typedef s8 s8_t; + +/** + * The 16-bit unsigned data type. + * + * This may have to be tweaked for your particular compiler. "unsigned + * short" works for most compilers. + */ +typedef u16 u16_t; + +/** + * The 16-bit signed data type. + * + * This may have to be tweaked for your particular compiler. "unsigned + * short" works for most compilers. + */ +typedef s16 s16_t; + +/** + * The 32-bit signed data type. + * + * This may have to be tweaked for your particular compiler. "unsigned + * short" works for most compilers. + */ +typedef s32 s32_t; + +/** + * The 32-bit unsigned data type. + * + * This may have to be tweaked for your particular compiler. "unsigned + * short" works for most compilers. + */ +typedef u32 u32_t; + +/** + * The 64-bit unsigned data type. + * + * This may have to be tweaked for your particular compiler. "unsigned + * short" works for most compilers. + */ +typedef u64 u64_t; + +/** + * The 64-bit signed data type. + * + * This may have to be tweaked for your particular compiler. "unsigned + * short" works for most compilers. + */ +typedef s64 s64_t; + +/** + * The statistics data type. + * + * This datatype determines how high the statistics counters are able + * to count. + */ +typedef s8 err_t; + +/*------------------------------------------------------------------------------*/ + +/** + * \defgroup btopt general configuration options + * @{ + */ + +/** + * The size of the uIP packet buffer. + * + * The uIP packet buffer should not be smaller than 60 bytes, and does + * not need to be larger than 1500 bytes. Lower size results in lower + * TCP throughput, larger size results in higher TCP throughput. + * + * \hideinitializer + */ +#define MEM_SIZE (64*1024) + +#define PBUF_POOL_NUM (HCI_HOST_MAX_NUM_ACL*MAX_NUM_CLIENTS) +#define PBUF_POOL_BUFSIZE HCI_HOST_ACL_MAX_LEN + +#define PBUF_ROM_NUM 45 + + +/** + * Determines if statistics support should be compiled in. + * + * The statistics is useful for debugging and to show the user. + * + * \hideinitializer + */ +#define STATISTICS 0 + +/** + * Determines if logging of certain events should be compiled in. + * + * This is useful mostly for debugging. The function uip_log() + * must be implemented to suit the architecture of the project, if + * logging is turned on. + * + * \hideinitializer + */ +#define LOGGING 0 +#define ERRORING 0 + +/** + * Print out a uIP log message. + * + * This function must be implemented by the module that uses uIP, and + * is called by uIP whenever a log message is generated. + */ +void bt_log(const char *filename,int line_nb,char *msg); + +/** + * The link level header length. + * + * This is the offset into the uip_buf where the IP header can be + * found. For Ethernet, this should be set to 14. For SLIP, this + * should be set to 0. + * + * \hideinitializer + */ +#define LL_HLEN 16 + +#define TCPIP_HLEN 40 +/** @} */ +/*------------------------------------------------------------------------------*/ +/** + * \defgroup uipoptcpu CPU architecture configuration + * @{ + * + * The CPU architecture configuration is where the endianess of the + * CPU on which uIP is to be run is specified. Most CPUs today are + * little endian, and the most notable exception are the Motorolas + * which are big endian. The BYTE_ORDER macro should be changed to + * reflect the CPU architecture on which uIP is to be run. + */ +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 3412 +#endif /* LITTLE_ENDIAN */ +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 1234 +#endif /* BIGE_ENDIAN */ + +/** + * The byte order of the CPU architecture on which uIP is to be run. + * + * This option can be either BIG_ENDIAN (Motorola byte order) or + * LITTLE_ENDIAN (Intel byte order). + * + * \hideinitializer + */ +#ifndef BYTE_ORDER +#define BYTE_ORDER BIG_ENDIAN +#endif /* BYTE_ORDER */ + +/** @} */ +/*------------------------------------------------------------------------------*/ + +#define LIBC_MEMFUNCREPLACE 0 + +/* ---------- Memory options ---------- */ +#define MAX_NUM_CLIENTS 6 /* Maximum number of connected Bluetooth clients. No more than 6 */ +#define MAX_NUM_OPT_CLIENTS 10 /* Maximum number of possible Bluetooth clients we might listen to */ + +#define MEMB_NUM_HCI_PCB 1 /* Always set to one */ +#define MEMB_NUM_HCI_LINK MAX_NUM_CLIENTS /* One for DT + One per ACL connection */ +#define MEMB_NUM_HCI_INQ 256 /* One per max number of returned results from an inquiry */ +#define MEMB_NUM_HCI_LINK_KEY 256 /* One per max number of returned results from an read stored link key */ + +/* MEMP_NUM_L2CAP_PCB: the number of simulatenously active L2CAP + connections. */ +#define MEMB_NUM_L2CAP_PCB (2 + 2 * MAX_NUM_CLIENTS) /* One for a closing connection + one for DT + one per number of connected Bluetooth clients */ +/* MEMP_NUM_L2CAP_PCB_LISTEN: the number of listening L2CAP + connections. */ +#define MEMB_NUM_L2CAP_PCB_LISTEN (2 * MAX_NUM_OPT_CLIENTS) /* One per listening PSM */ +/* MEMP_NUM_L2CAP_SIG: the number of simultaneously unresponded + L2CAP signals */ +#define MEMB_NUM_L2CAP_SIG (2 * MAX_NUM_CLIENTS)/* Two per number of connected Bluetooth clients but min 2 */ +#define MEMB_NUM_L2CAP_SEG (2 + 2 * MAX_NUM_CLIENTS) /* One per number of L2CAP connections */ + +#define MEMB_NUM_SDP_PCB MAX_NUM_CLIENTS /* One per number of connected Bluetooth clients */ +#define MEMB_NUM_SDP_RECORD 1 /* One per registered service record */ + +#define MEMP_NUM_RFCOMM_PCB (2 + 2 * MAX_NUM_CLIENTS) /* Two for DT + Two per number of connected Bluetooth clients */ +#define MEMP_NUM_RFCOMM_PCB_LISTEN (2 * MAX_NUM_CLIENTS) /* Two per number of connected Bluetooth clients */ + +#define MEMP_NUM_HIDP_PCB (2 + 2 * MAX_NUM_CLIENTS) /* Two for DT + Two per number of connected Bluetooth clients */ +#define MEMP_NUM_HIDP_PCB_LISTEN (2 * MAX_NUM_CLIENTS) /* Two per number of connected Bluetooth clients */ + +#define MEMP_NUM_PPP_PCB (1 + MAX_NUM_CLIENTS) /* One for DT + One per number of connected Bluetooth clients */ +#define MEMP_NUM_PPP_REQ MAX_NUM_CLIENTS /* One per number of connected Bluetooth clients but min 1 */ + +#define MEMP_NUM_BTE_PCB (2 + 2 * MAX_NUM_CLIENTS) /* Two for DT + Two per number of connected Bluetooth clients */ +#define MEMP_NUM_BTE_PCB_LISTEN (2 * MAX_NUM_CLIENTS) /* Two per number of connected Bluetooth clients */ + +#define MEMP_NUM_BTE_CTRLS 256 + +/* ---------- HCI options ---------- */ +/* HCI: Defines if we have lower layers of the Bluetooth stack running on a separate host + controller */ +#define HCI 1 + +#if HCI +/* HCI_HOST_MAX_NUM_ACL: The maximum number of ACL packets that the host can buffer */ +#define HCI_HOST_MAX_NUM_ACL 20 //TODO: Should be equal to PBUF_POOL_SIZE/2??? */ +/* HCI_HOST_ACL_MAX_LEN: The maximum size of an ACL packet that the host can buffer */ +#define HCI_HOST_ACL_MAX_LEN 1691 /* Default: RFCOMM MFS + ACL header size, L2CAP header size, + RFCOMM header size and RFCOMM FCS size */ +/* HCI_PACKET_TYPE: The set of packet types which may be used on the connection. In order to + maximize packet throughput, it is recommended that RFCOMM should make use of the 3 and 5 + slot baseband packets.*/ +#define HCI_PACKET_TYPE 0xCC18 /* Default DM1, DH1, DM3, DH3, DM5, DH5 */ +/* HCI_ALLOW_ROLE_SWITCH: Tells the host controller whether to accept a Master/Slave switch + during establishment of a connection */ +#define HCI_ALLOW_ROLE_SWITCH 1 /* Default 1 */ +/* HCI_FLOW_QUEUEING: Control if a packet should be queued if the host controller is out of + bufferspace for outgoing packets. Only the first packet sent when out of credits will be + queued */ +#define HCI_FLOW_QUEUEING 0 /* Default: 0 */ + +#endif /* HCI */ + +/* ---------- L2CAP options ---------- */ +/* L2CAP_HCI: Option for including HCI to access the Bluetooth baseband capabilities */ +#define L2CAP_HCI 1 //TODO: NEEDED? +/* L2CAP_CFG_QOS: Control if a flow specification similar to RFC 1363 should be used */ +#define L2CAP_CFG_QOS 0 +/* L2CAP_MTU: Maximum transmission unit for L2CAP packet payload (min 48) */ +#define L2CAP_MTU (HIDD_N + 1)/* Default for this implementation is RFCOMM MFS + RFCOMM header size and + RFCOMM FCS size while the L2CAP default is 672 */ +/* L2CAP_OUT_FLUSHTO: For some networking protocols, such as many real-time protocols, guaranteed delivery + is undesirable. The flush time-out value SHALL be set to its default value 0xffff for a reliable L2CAP + channel, and MAY be set to other values if guaranteed delivery is not desired. (min 1) */ +#define L2CAP_OUT_FLUSHTO 0xFFFF /* Default: 0xFFFF. Infinite number of retransmissions (reliable channel) + The value of 1 implies no retransmissions at the Baseband level + should be performed since the minimum polling interval is 1.25 ms.*/ +/* L2CAP_RTX: The Responsive Timeout eXpired timer is used to terminate + the channel when the remote endpoint is unresponsive to signalling + requests (min 1s, max 60s) */ +#define L2CAP_RTX 60 +/* L2CAP_ERTX: The Extended Response Timeout eXpired timer is used in + place of the RTC timer when a L2CAP_ConnectRspPnd event is received + (min 60s, max 300s) */ +#define L2CAP_ERTX 300 +/* L2CAP_MAXRTX: Maximum number of Request retransmissions before + terminating the channel identified by the request. The decision + should be based on the flush timeout of the signalling link. If the + flush timeout is infinite, no retransmissions should be performed */ +#define L2CAP_MAXRTX 0 +/* L2CAP_CFG_TO: Amount of time spent arbitrating the channel parameters + before terminating the connection (max 120s) */ +#define L2CAP_CFG_TO 30 + +/* ---------- BTE options ---------- */ + +/* ---------- HIDD options ---------- */ +/* RFCOMM_N: Maximum frame size for RFCOMM segments (min 23, max 32767)*/ +#define HIDD_N 672 /* Default: Worst case byte stuffed PPP packet size + + non-compressed PPP header size and FCS size */ +/* RFCOMM_K: Initial amount of credits issued to the peer (min 0, max 7) */ +#define RFCOMM_K 0 +/* RFCOMM_TO: Acknowledgement timer (T1) and response timer for multiplexer control channel (T2). + T1 is the timeout for frames sent with the P/F bit set to 1 (SABM and DISC) and T2 is the timeout + for commands sent in UIH frames on DLCI 0 (min 10s, max 60s) */ +#define RFCOMM_TO 20 +/* RFCOMM_FLOW_QUEUEING: Control if a packet should be queued if a channel is out of credits for + outgoing packets. Only the first packet sent when out of credits will be queued */ +#define RFCOMM_FLOW_QUEUEING 0 /* Default: 0 */ + + +#endif /* __BTOPT_H__ */ diff --git a/wii/libogc/lwbt/btpbuf.c b/wii/libogc/lwbt/btpbuf.c new file mode 100644 index 0000000000..5afa2ce741 --- /dev/null +++ b/wii/libogc/lwbt/btpbuf.c @@ -0,0 +1,358 @@ +#include +#include + +#include "btmemb.h" +#include "btmemr.h" +#include "btpbuf.h" + +#if STATISTICS == 1 +#define STAT(s) +#else +#define STAT(s) +#endif /* STATISTICS == 1 */ + +MEMB(pool_pbufs,sizeof(struct pbuf)+PBUF_POOL_BUFSIZE,PBUF_POOL_NUM); +MEMB(rom_pbufs,sizeof(struct pbuf),PBUF_ROM_NUM); + +void btpbuf_init() +{ + btmemb_init(&pool_pbufs); + btmemb_init(&rom_pbufs); +} + +struct pbuf* btpbuf_alloc(pbuf_layer layer,u16_t len,pbuf_flag flag) +{ + u16_t offset; + s32_t rem_len; + struct pbuf *p,*q,*r; + + offset = 0; + switch(layer) { + case PBUF_TRANSPORT: + offset += TRANSPORT_HLEN; + case PBUF_LINK: + offset += LL_HLEN; + break; + case PBUF_RAW: + break; + default: + ERROR("btpbuf_alloc: bad pbuf layer.\n"); + return NULL; + } + + switch(flag) { + case PBUF_POOL: + p = btmemb_alloc(&pool_pbufs); + if(p==NULL) { + ERROR("btbtpbuf_alloc: couldn't allocate pbuf(p) from pool\n"); + return NULL; + } + + p->next = NULL; + p->payload = MEM_ALIGN((void*)((u8_t*)p+(sizeof(struct pbuf)+offset))); + p->tot_len = len; + p->len = (len>(PBUF_POOL_BUFSIZE-offset)?(PBUF_POOL_BUFSIZE-offset):len); + p->flags = PBUF_FLAG_POOL; + p->ref = 1; + + r = p; + rem_len = len - p->len; + while(rem_len>0) { + q = btmemb_alloc(&pool_pbufs); + if(q==NULL) { + ERROR("btpbuf_alloc: couldn't allocate pbuf(q) from pool\n"); + btpbuf_free(p); + return NULL; + } + + q->next = NULL; + r->next = q; + q->tot_len = rem_len; + q->len = (rem_len>PBUF_POOL_BUFSIZE?PBUF_POOL_BUFSIZE:rem_len); + q->payload = (void*)((u8_t*)q+sizeof(struct pbuf)); + q->flags = PBUF_FLAG_POOL; + q->ref = 1; + + rem_len -= q->len; + r = q; + } + break; + case PBUF_RAM: + p = btmemr_malloc(MEM_ALIGN_SIZE(sizeof(struct pbuf)+offset)+MEM_ALIGN_SIZE(len)); + if(p==NULL) { + ERROR("btpbuf_alloc: couldn't allocate pbuf from ram\n"); + return NULL; + } + p->payload = MEM_ALIGN((u8_t*)p+sizeof(struct pbuf)+offset); + p->len = p->tot_len = len; + p->next = NULL; + p->flags = PBUF_FLAG_RAM; + break; + case PBUF_ROM: + case PBUF_REF: + p = btmemb_alloc(&rom_pbufs); + if(p==NULL) { + ERROR("btpbuf_alloc: couldn't allocate pbuf from rom/ref\n"); + return NULL; + } + p->payload = NULL; + p->next = NULL; + p->len = p->tot_len = len; + p->flags = (flag==PBUF_ROM?PBUF_FLAG_ROM:PBUF_FLAG_REF); + break; + default: + ERROR("btpbuf_alloc: bad flag value.\n"); + return NULL; + } + + p->ref = 1; + return p; +} + +u8_t btpbuf_free(struct pbuf *p) +{ + u8_t cnt; + u32 level; + struct pbuf *q; + + if(p==NULL) return 0; + + cnt = 0; + + _CPU_ISR_Disable(level); + while(p!=NULL) { + p->ref--; + if(p->ref==0) { + q = p->next; + if(p->flags==PBUF_FLAG_POOL) { + btmemb_free(&pool_pbufs,p); + } else if(p->flags==PBUF_FLAG_ROM || p->flags==PBUF_FLAG_REF) { + btmemb_free(&rom_pbufs,p); + } else { + btmemr_free(p); + } + cnt++; + p = q; + } else + p = NULL; + } + _CPU_ISR_Restore(level); + + return cnt; +} + +void btpbuf_realloc(struct pbuf *p,u16_t new_len) +{ + u16_t rem_len; + s16_t grow; + struct pbuf *q; + + if(new_len>=p->tot_len) return; + + grow = new_len - p->tot_len; + rem_len = new_len; + q = p; + while(rem_len>q->len) { + rem_len -= q->len; + q->tot_len += grow; + q = q->next; + } + + if(q->flags==PBUF_FLAG_RAM && rem_len!=q->len) + btmemr_realloc(q,(u8_t*)q->payload-(u8_t*)q+rem_len); + + q->len = rem_len; + q->tot_len = q->len; + + if(q->next!=NULL) btpbuf_free(q->next); + q->next = NULL; +} + +u8_t btpbuf_header(struct pbuf *p,s16_t hdr_size_inc) +{ + void *payload; + + if(hdr_size_inc==0 || p==NULL) return 0; + + + payload = p->payload; + if(p->flags==PBUF_FLAG_POOL || p->flags==PBUF_FLAG_RAM) { + p->payload = (u8_t*)p->payload-hdr_size_inc; + if((u8_t*)p->payload<(u8_t*)p+sizeof(struct pbuf)) { + p->payload = payload; + return 1; + } + } else if(p->flags==PBUF_FLAG_ROM || p->flags==PBUF_FLAG_REF) { + if(hdr_size_inc<0 && hdr_size_inc-p->len<=0) p->payload = (u8_t*)p->payload-hdr_size_inc; + else return 1; + } + p->tot_len += hdr_size_inc; + p->len += hdr_size_inc; + + return 0; +} + +u8_t btpbuf_clen(struct pbuf *p) +{ + u8_t len; + + len = 0; + while(p!=NULL) { + len++; + p = p->next; + } + return len; +} + +void btpbuf_ref(struct pbuf *p) +{ + u32 level; + + if(p!=NULL) { + _CPU_ISR_Disable(level); + ++(p->ref); + _CPU_ISR_Restore(level); + } +} + +void btpbuf_cat(struct pbuf *h,struct pbuf *t) +{ + struct pbuf *p; + + if(h==NULL || t==NULL) return; + + for(p=h;p->next!=NULL;p=p->next) { + p->tot_len += t->tot_len; + } + p->tot_len += t->tot_len; + p->next = t; +} + +void btpbuf_queue(struct pbuf *p,struct pbuf *n) +{ + if(p==NULL || n==NULL || p==n) return; + + while(p->next!=NULL) p = p->next; + + p->next = n; + btpbuf_ref(n); +} + +struct pbuf* btpbuf_dequeue(struct pbuf *p) +{ + struct pbuf *q; + + if(p==NULL) return NULL; + + while(p->tot_len!=p->len) p = p->next; + + q = p->next; + p->next = NULL; + + return q; +} + +void btpbuf_chain(struct pbuf *h,struct pbuf *t) +{ + btpbuf_cat(h,t); + btpbuf_ref(t); +} + +struct pbuf* btpbuf_dechain(struct pbuf *p) +{ + struct pbuf *q; + u8_t tail_gone = 1; + + q = p->next; + if(q!=NULL) { + q->tot_len = p->tot_len - p->len; + p->next = NULL; + p->tot_len = p->len; + + tail_gone = btpbuf_free(q); + } + + return (tail_gone>0?NULL:q); +} + +struct pbuf* btpbuf_take(struct pbuf *p) +{ + struct pbuf *q , *prev, *head; + + prev = NULL; + head = p; + /* iterate through pbuf chain */ + do + { + /* pbuf is of type PBUF_REF? */ + if (p->flags == PBUF_FLAG_REF) { + LOG("pbuf_take: encountered PBUF_REF %p\n", (void *)p); + /* allocate a pbuf (w/ payload) fully in RAM */ + /* PBUF_POOL buffers are faster if we can use them */ + if (p->len <= PBUF_POOL_BUFSIZE) { + q = btpbuf_alloc(PBUF_RAW, p->len, PBUF_POOL); + if (q == NULL) { + LOG("pbuf_take: Could not allocate PBUF_POOL\n"); + } + } else { + /* no replacement pbuf yet */ + q = NULL; + LOG("pbuf_take: PBUF_POOL too small to replace PBUF_REF\n"); + } + /* no (large enough) PBUF_POOL was available? retry with PBUF_RAM */ + if (q == NULL) { + q = btpbuf_alloc(PBUF_RAW, p->len, PBUF_RAM); + if (q == NULL) { + LOG("pbuf_take: Could not allocate PBUF_RAM\n"); + } + } + /* replacement pbuf could be allocated? */ + if (q != NULL) + { + /* copy p to q */ + /* copy successor */ + q->next = p->next; + /* remove linkage from original pbuf */ + p->next = NULL; + /* remove linkage to original pbuf */ + if (prev != NULL) { + /* break chain and insert new pbuf instead */ + prev->next = q; + /* prev == NULL, so we replaced the head pbuf of the chain */ + } else { + head = q; + } + /* copy pbuf payload */ + memcpy(q->payload, p->payload, p->len); + q->tot_len = p->tot_len; + q->len = p->len; + /* in case p was the first pbuf, it is no longer refered to by + * our caller, as the caller MUST do p = pbuf_take(p); + * in case p was not the first pbuf, it is no longer refered to + * by prev. we can safely free the pbuf here. + * (note that we have set p->next to NULL already so that + * we will not free the rest of the chain by accident.) + */ + btpbuf_free(p); + /* do not copy ref, since someone else might be using the old buffer */ + LOG("pbuf_take: replaced PBUF_REF %p with %p\n", (void *)p, (void *)q); + p = q; + } else { + /* deallocate chain */ + btpbuf_free(head); + LOG("pbuf_take: failed to allocate replacement pbuf for %p\n", (void *)p); + return NULL; + } + /* p->flags != PBUF_FLAG_REF */ + } else { + LOG("pbuf_take: skipping pbuf not of type PBUF_REF\n"); + } + /* remember this pbuf */ + prev = p; + /* proceed to next pbuf in original chain */ + p = p->next; + } while (p); + LOG("pbuf_take: end of chain reached.\n"); + + return head; +} diff --git a/wii/libogc/lwbt/btpbuf.h b/wii/libogc/lwbt/btpbuf.h new file mode 100644 index 0000000000..924b99c0cf --- /dev/null +++ b/wii/libogc/lwbt/btpbuf.h @@ -0,0 +1,49 @@ +#ifndef __BTPBUF_H__ +#define __BTPBUF_H__ + +#include "bt.h" + +/* Definitions for the pbuf flag field. These are NOT the flags that + * are passed to pbuf_alloc(). */ +#define PBUF_FLAG_RAM 0x00U /* Flags that pbuf data is stored in RAM */ +#define PBUF_FLAG_ROM 0x01U /* Flags that pbuf data is stored in ROM */ +#define PBUF_FLAG_POOL 0x02U /* Flags that the pbuf comes from the pbuf pool */ +#define PBUF_FLAG_REF 0x04U /* Flags thet the pbuf payload refers to RAM */ + +typedef enum { + PBUF_TRANSPORT, + PBUF_LINK, + PBUF_RAW +} pbuf_layer; + +typedef enum { + PBUF_POOL, + PBUF_RAM, + PBUF_ROM, + PBUF_REF +} pbuf_flag; + +struct pbuf { + struct pbuf *next; + void *payload; + u16_t tot_len; + u16_t len; + u16_t flags; + u16_t ref; +}; + +void btpbuf_init(); +struct pbuf* btpbuf_alloc(pbuf_layer layer,u16_t len,pbuf_flag flag); +u8_t btpbuf_free(struct pbuf *p); +void btpbuf_realloc(struct pbuf *p,u16_t new_len); +u8_t btpbuf_header(struct pbuf *p,s16_t hdr_size_inc); +void btpbuf_cat(struct pbuf *h,struct pbuf *t); +u8_t btpbuf_clen(struct pbuf *p); +void btpbuf_queue(struct pbuf *p,struct pbuf *n); +void btpbuf_ref(struct pbuf *p); +void btpbuf_chain(struct pbuf *h,struct pbuf *t); +struct pbuf* btpbuf_dequeue(struct pbuf *p); +struct pbuf* btpbuf_dechain(struct pbuf *p); +struct pbuf* btpbuf_take(struct pbuf *p); + +#endif diff --git a/wii/libogc/lwbt/hci.c b/wii/libogc/lwbt/hci.c new file mode 100644 index 0000000000..fa0a1e8378 --- /dev/null +++ b/wii/libogc/lwbt/hci.c @@ -0,0 +1,1772 @@ +/* + * Copyright (c) 2003 EISLAB, Lulea University of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwBT Bluetooth stack. + * + * Author: Conny Ohult + * + */ + +/*-----------------------------------------------------------------------------------*/ +/* hci.c + * + * Implementation of the Host Controller Interface (HCI). A command interface to the + * baseband controller and link manager, and gives access to hardware status and + * control registers. + * + */ +/*-----------------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include + +#include "hci.h" +#include "l2cap.h" +#include "btmemr.h" +#include "btmemb.h" +#include "btpbuf.h" +#include "physbusif.h" + +struct hci_pcb *hci_dev = NULL; +struct hci_link *hci_active_links = NULL; +struct hci_link *hci_tmp_link = NULL; +struct hci_link_key *hci_tmp_key = NULL; + +MEMB(hci_pcbs,sizeof(struct hci_pcb),MEMB_NUM_HCI_PCB); +MEMB(hci_links,sizeof(struct hci_link),MEMB_NUM_HCI_LINK); +MEMB(hci_inq_results,sizeof(struct hci_inq_res),MEMB_NUM_HCI_INQ); +MEMB(hci_link_key_results,sizeof(struct hci_link_key),MEMB_NUM_HCI_LINK_KEY); + +err_t hci_init(void) +{ + btmemr_init(); + btpbuf_init(); + + btmemb_init(&hci_pcbs); + btmemb_init(&hci_links); + btmemb_init(&hci_inq_results); + btmemb_init(&hci_link_key_results); + + if((hci_dev=btmemb_alloc(&hci_pcbs))==NULL) { + ERROR("hci_init: Could not allocate memory for hci_dev\n"); + return ERR_MEM; + } + memset(hci_dev,0,sizeof(struct hci_pcb)); + + hci_active_links = NULL; + hci_tmp_link = NULL; + + return ERR_OK; +} + +struct hci_link* hci_new(void) +{ + struct hci_link *link; + + link = btmemb_alloc(&hci_links); + if(link==NULL) return NULL; + + memset(link,0,sizeof(struct hci_link)); + return link; +} + +struct hci_link* hci_get_link(struct bd_addr *bdaddr) +{ + struct hci_link *link; + + for(link=hci_active_links;link!=NULL;link=link->next) { + if(bd_addr_cmp(&(link->bdaddr),bdaddr)) break; + } + return link; +} + +/*-----------------------------------------------------------------------------------*/ +/* + * hci_close(): + * + * Close the link control block. + */ +/*-----------------------------------------------------------------------------------*/ +err_t hci_close(struct hci_link *link) +{ + if(link->p != NULL) { + btpbuf_free(link->p); + } + + HCI_RMV(&(hci_active_links), link); + btmemb_free(&hci_links, link); + link = NULL; + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* + * hci_reset_all(): + * + * Closes all active link control blocks. + */ +/*-----------------------------------------------------------------------------------*/ +void hci_reset_all(void) +{ + struct hci_link *link,*tlink; + struct hci_inq_res *ires,*tires; + struct hci_link_key *ikeys,*tikeys; + + for(link=hci_active_links;link!=NULL;) { + tlink = link->next; + hci_close(link); + link = tlink; + } + hci_active_links = NULL; + + for(ires=hci_dev->ires;ires!=NULL;) { + tires = ires->next; + btmemb_free(&hci_inq_results,ires); + ires = tires; + } + + for(ikeys=hci_dev->keyres;ikeys!=NULL;) { + tikeys = ikeys->next; + btmemb_free(&hci_inq_results,ikeys); + ikeys = tikeys; + } + btmemb_free(&hci_pcbs,hci_dev); + + hci_init(); +} + +void hci_arg(void *arg) +{ + hci_dev->cbarg = arg; +} + +void hci_cmd_complete(err_t (*cmd_complete)(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result)) +{ + hci_dev->cmd_complete = cmd_complete; +} + +/*-----------------------------------------------------------------------------------*/ +/* + * hci_pin_req(): + * + * Used to specify the function that should be called when HCI has received a + * PIN code request event. + */ +/*-----------------------------------------------------------------------------------*/ +void hci_pin_req(err_t (* pin_req)(void *arg, struct bd_addr *bdaddr)) +{ + hci_dev->pin_req = pin_req; +} +/*-----------------------------------------------------------------------------------*/ +/* + * hci_link_key_req(): + * + * Used to specify the function that should be called when HCI has received a + * Link Key request event. + */ +/*-----------------------------------------------------------------------------------*/ +void hci_link_key_req(err_t (* link_key_req)(void *arg, struct bd_addr *bdaddr)) +{ + hci_dev->link_key_req = link_key_req; +} +/*-----------------------------------------------------------------------------------*/ +/* + * hci_link_key_not(): + * + * Used to specify the function that should be called when HCI has received a + * link key notification event. + */ +/*-----------------------------------------------------------------------------------*/ +void hci_link_key_not(err_t (* link_key_not)(void *arg, struct bd_addr *bdaddr, u8_t *key)) +{ + hci_dev->link_key_not = link_key_not; +} + +/*-----------------------------------------------------------------------------------*/ +/* + * hci_connection_complete(): + * + * Used to specify the function that should be called when HCI has received a + * connection complete event. + */ +/*-----------------------------------------------------------------------------------*/ +void hci_connection_complete(err_t (* conn_complete)(void *arg, struct bd_addr *bdaddr)) +{ + hci_dev->conn_complete = conn_complete; +} + +/*-----------------------------------------------------------------------------------*/ +/* + * hci_wlp_complete(): + * + * Used to specify the function that should be called when HCI has received a + * successful write link policy complete event. + */ +/*-----------------------------------------------------------------------------------*/ +void hci_wlp_complete(err_t (* wlp_complete)(void *arg, struct bd_addr *bdaddr)) +{ + hci_dev->wlp_complete = wlp_complete; +} + +void hci_conn_req(err_t (*conn_req)(void *arg,struct bd_addr *bdaddr,u8_t *cod,u8_t link_type)) +{ + hci_dev->conn_req = conn_req; +} + +err_t hci_reg_dev_info(struct bd_addr *bdaddr,u8_t *cod,u8_t psrm,u8_t psm,u16_t co) +{ + struct hci_inq_res *ires; + + if(hci_dev==NULL) return ERR_VAL; + + if((ires=btmemb_alloc(&hci_inq_results))!=NULL) { + bd_addr_set(&(ires->bdaddr),bdaddr); + memcpy(ires->cod,cod,3); + ires->psrm = psrm; + ires->psm = psm; + ires->co = co; + ires->next = NULL; + + HCI_REG(&(hci_dev->ires),ires); + return ERR_OK; + } + return ERR_MEM; +} + +struct pbuf* hci_cmd_ass(struct pbuf *p,u8_t ocf,u8_t ogf,u8_t len) +{ + ((u8_t*)p->payload)[0] = HCI_COMMAND_DATA_PACKET; /* cmd packet type */ + ((u8_t*)p->payload)[1] = (ocf&0xff); /* OCF & OGF */ + ((u8_t*)p->payload)[2] = ((ocf>>8)|(ogf<<2)); + ((u8_t*)p->payload)[3] = len-HCI_CMD_HDR_LEN-1; /* Param len = plen - cmd hdr - ptype */ + + if(hci_dev->num_cmd>0) hci_dev->num_cmd--; + return p; +} + +err_t hci_reset(void) +{ + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_RESET_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_reset: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + p = hci_cmd_ass(p,HCI_RESET_OCF,HCI_HC_BB_OGF,HCI_RESET_PLEN); + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_read_buffer_size(void) +{ + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_R_BUF_SIZE_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_read_buffer_size: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + p = hci_cmd_ass(p,HCI_R_BUF_SIZE_OCF,HCI_INFO_PARAM_OGF,HCI_R_BUF_SIZE_PLEN); + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_read_bd_addr(void) +{ + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_R_BD_ADDR_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_read_bd_addr: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + p = hci_cmd_ass(p,HCI_R_BD_ADDR_OCF,HCI_INFO_PARAM_OGF,HCI_R_BD_ADDR_PLEN); + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_read_local_version(void) +{ + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_R_LOC_VERS_SIZE_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_read_local_version: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + p = hci_cmd_ass(p,HCI_R_LOC_VERSION_OCF,HCI_INFO_PARAM_OGF,HCI_R_LOC_VERS_SIZE_PLEN); + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_read_local_features(void) +{ + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_R_LOC_FEAT_SIZE_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_read_local_features: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + p = hci_cmd_ass(p,HCI_R_LOC_FEATURES_OCF,HCI_INFO_PARAM_OGF,HCI_R_LOC_FEAT_SIZE_PLEN); + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_read_stored_link_key() +{ + struct pbuf *p = NULL; + struct hci_link_key *tmpres; + + /* Free any previous link key result list */ + while(hci_dev->keyres != NULL) { + tmpres = hci_dev->keyres; + hci_dev->keyres = hci_dev->keyres->next; + btmemb_free(&hci_link_key_results,tmpres); + } + + + if((p=btpbuf_alloc(PBUF_RAW,HCI_R_STORED_LINK_KEY_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_read_stored_link_keys: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + p = hci_cmd_ass(p,HCI_R_STORED_LINK_KEY_OCF,HCI_HC_BB_OGF,HCI_R_STORED_LINK_KEY_PLEN); + + memcpy((void*)((u8_t*)p->payload + 4),hci_dev->bdaddr.addr,6); + ((u8_t*)p->payload)[10] = 1; + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_set_event_filter(u8_t filter_type,u8_t filter_cond_type,u8_t *cond) +{ + u32 cond_len = 0; + struct pbuf *p = NULL; + + switch(filter_type) { + case 0x00: + cond_len = 0x00; + break; + case 0x01: + switch(filter_cond_type) { + case 0x00: + cond_len = 0x00; + break; + case 0x01: + cond_len = 0x06; + break; + case 0x02: + cond_len = 0x06; + break; + default: + break; + } + break; + case 0x02: + switch(filter_cond_type) { + case 0x00: + cond_len = 0x01; + break; + case 0x01: + cond_len = 0x07; + break; + case 0x02: + cond_len = 0x07; + break; + default: + break; + } + break; + default: + break; + } + + if((p=btpbuf_alloc(PBUF_RAW,HCI_SET_EV_FILTER_PLEN+cond_len,PBUF_RAM))==NULL) { + ERROR("hci_set_event_filter: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + p = hci_cmd_ass(p,HCI_SET_EV_FILTER_OCF,HCI_HC_BB_OGF,HCI_SET_EV_FILTER_PLEN+cond_len); + ((u8_t*)p->payload)[4] = filter_type; + ((u8_t*)p->payload)[5] = filter_cond_type; + if(cond_len>0) memcpy(p->payload+6,cond,cond_len); + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_write_page_timeout(u16_t timeout) +{ + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_W_PAGE_TIMEOUT_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_set_write_page_timeout: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + p = hci_cmd_ass(p,HCI_W_PAGE_TIMEOUT_OCF,HCI_HC_BB_OGF,HCI_W_PAGE_TIMEOUT_PLEN); + ((u16_t*)p->payload)[2] = htole16(timeout); + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_write_scan_enable(u8_t scan_enable) +{ + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_W_SCAN_EN_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_set_write_page_timeout: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + p = hci_cmd_ass(p,HCI_W_SCAN_EN_OCF,HCI_HC_BB_OGF,HCI_W_SCAN_EN_PLEN); + ((u8_t*)p->payload)[4] = scan_enable; + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_inquiry(u32_t lap,u8_t inq_len,u8_t num_resp,err_t (*inq_complete)(void *arg,struct hci_pcb *pcb,struct hci_inq_res *ires,u16_t result)) +{ + struct pbuf *p = NULL; + struct hci_inq_res *tmpres; + + /* Free any previous inquiry result list */ + while(hci_dev->ires != NULL) { + tmpres = hci_dev->ires; + hci_dev->ires = hci_dev->ires->next; + btmemb_free(&hci_inq_results,tmpres); + } + + hci_dev->inq_complete = inq_complete; + if((p=btpbuf_alloc(PBUF_RAW,HCI_INQUIRY_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_inquiry: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + p = hci_cmd_ass(p,HCI_INQUIRY_OCF,HCI_LINK_CTRL_OGF,HCI_INQUIRY_PLEN); + ((u8_t*)p->payload)[4] = (lap&0xff); + ((u8_t*)p->payload)[5] = (lap>>8); + ((u8_t*)p->payload)[6] = (lap>>16); + + ((u8_t*)p->payload)[7] = inq_len; + ((u8_t*)p->payload)[8] = num_resp; + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_periodic_inquiry(u32_t lap,u16_t min_period,u16_t max_period,u8_t inq_len,u8_t num_resp,err_t (*inq_complete)(void *arg,struct hci_pcb *pcb,struct hci_inq_res *ires,u16_t result)) +{ + struct pbuf *p = NULL; + struct hci_inq_res *tmpres; + + /* Free any previous inquiry result list */ + while(hci_dev->ires != NULL) { + tmpres = hci_dev->ires; + hci_dev->ires = hci_dev->ires->next; + btmemb_free(&hci_inq_results,tmpres); + } + + hci_dev->inq_complete = inq_complete; + if((p=btpbuf_alloc(PBUF_RAW,HCI_PERIODIC_INQUIRY_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_periodic_inquiry: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Assembling command packet */ + p = hci_cmd_ass(p,HCI_PERIODIC_INQUIRY_OCF,HCI_LINK_CTRL_OGF,HCI_PERIODIC_INQUIRY_PLEN); + + /* Assembling cmd prameters */ + ((u16_t*)p->payload)[2] = htole16(max_period); + ((u16_t*)p->payload)[3] = htole16(min_period); + ((u8_t*)p->payload)[8] = (lap&0xff); + ((u8_t*)p->payload)[9] = (lap>>8); + ((u8_t*)p->payload)[10] = (lap>>16); + + ((u8_t*)p->payload)[11] = inq_len; + ((u8_t*)p->payload)[12] = num_resp; + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_exit_periodic_inquiry() +{ + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_EXIT_PERIODIC_INQUIRY_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_exit_periodic_inquiry: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Assembling command packet */ + p = hci_cmd_ass(p,HCI_EXIT_PERIODIC_INQUIRY_OCF,HCI_LINK_CTRL_OGF,HCI_EXIT_PERIODIC_INQUIRY_PLEN); + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_accecpt_conn_request(struct bd_addr *bdaddr,u8_t role) +{ + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_ACCEPT_CONN_REQ_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_exit_periodic_inquiry: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Assembling command packet */ + p = hci_cmd_ass(p,HCI_ACCEPT_CONN_REQ_OCF,HCI_LINK_CTRL_OGF,HCI_ACCEPT_CONN_REQ_PLEN); + + /* Assembling cmd prameters */ + memcpy((void*)(((u8_t*)p->payload)+4),bdaddr,6); + ((u8_t*)p->payload)[10] = role; + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_set_event_mask(u64_t ev_mask) +{ + u64_t mask; + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_SET_EV_MASK_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_set_event_mask: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Assembling command packet */ + p = hci_cmd_ass(p,HCI_SET_EV_MASK_OCF,HCI_HC_BB_OGF,HCI_SET_EV_MASK_PLEN); + + mask = htole64(ev_mask); + memcpy(((u8_t*)p->payload)+4,&mask,8); + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_write_local_name(u8_t *name,u8_t len) +{ + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_W_LOCAL_NAME_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_write_local_name: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Assembling command packet */ + p = hci_cmd_ass(p,HCI_W_LOCAL_NAME_OCF,HCI_HC_BB_OGF,HCI_W_LOCAL_NAME_PLEN); + /* Assembling cmd prameters */ + memcpy(((u8_t *)p->payload) + 4, name, len); + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_write_pin_type(u8_t type) +{ + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_W_PIN_TYPE_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_write_local_name: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Assembling command packet */ + p = hci_cmd_ass(p,HCI_W_PIN_TYPE_OCF,HCI_HC_BB_OGF,HCI_W_PIN_TYPE_PLEN); + /* Assembling cmd prameters */ + ((u8_t *)p->payload)[4] = type; + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_read_remote_name(struct bd_addr *bdaddr) +{ + u16_t clock_offset; + struct pbuf *p = NULL; + struct hci_inq_res *ires; + u8_t page_scan_repetition_mode, page_scan_mode; + + for(ires=hci_dev->ires;ires!=NULL;ires=ires->next) { + if(bd_addr_cmp(&(ires->bdaddr),bdaddr)) { + page_scan_repetition_mode = ires->psrm; + page_scan_mode = ires->psm; + clock_offset = ires->co; + break; + } + } + + if(ires==NULL) { + page_scan_repetition_mode = 0x01; + page_scan_mode = 0x00; + clock_offset = 0x00; + } + + if((p=btpbuf_alloc(PBUF_RAW,HCI_R_REMOTE_NAME_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_read_remote_name: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Assembling command packet */ + p = hci_cmd_ass(p,HCI_R_REMOTE_NAME_OCF,HCI_LINK_CTRL_OGF,HCI_R_REMOTE_NAME_PLEN); + /* Assembling cmd prameters */ + memcpy(((u8_t *)p->payload+4),bdaddr->addr,6); + ((u8_t*)p->payload)[10] = page_scan_repetition_mode; + ((u8_t*)p->payload)[11] = page_scan_mode; + ((u16_t*)p->payload)[6] = htole16(clock_offset); + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; + +} + +err_t hci_write_inquiry_mode(u8_t mode) +{ + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_W_INQUIRY_MODE_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_write_inquiry_mode: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Assembling command packet */ + p = hci_cmd_ass(p,HCI_W_INQUIRY_MODE_OCF,HCI_HC_BB_OGF,HCI_W_INQUIRY_MODE_PLEN); + /* Assembling cmd prameters */ + ((u8_t*)p->payload)[4] = mode; + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_write_page_scan_type(u8_t type) +{ + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_W_PAGE_SCAN_TYPE_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_write_inquiry_mode: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Assembling command packet */ + p = hci_cmd_ass(p,HCI_W_PAGE_SCAN_TYPE_OCF,HCI_HC_BB_OGF,HCI_W_PAGE_SCAN_TYPE_PLEN); + /* Assembling cmd prameters */ + ((u8_t*)p->payload)[4] = type; + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_write_inquiry_scan_type(u8_t type) +{ + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_W_INQUIRY_SCAN_TYPE_PLEN,PBUF_RAM))==NULL) { + ERROR("hci_write_inquiry_mode: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Assembling command packet */ + p = hci_cmd_ass(p,HCI_W_INQUIRY_SCAN_TYPE_OCF,HCI_HC_BB_OGF,HCI_W_INQUIRY_SCAN_TYPE_PLEN); + /* Assembling cmd prameters */ + ((u8_t*)p->payload)[4] = type; + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_vendor_specific_command(u8_t ocf,u8_t ogf,void *data,u8_t len) +{ + struct pbuf *p = NULL; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_W_VENDOR_CMD_PLEN + len,PBUF_RAM))==NULL) { + ERROR("hci_vendor_specific_patch: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Assembling command packet */ + p = hci_cmd_ass(p,ocf,ogf,HCI_W_VENDOR_CMD_PLEN + len); + /* Assembling cmd prameters */ + memcpy(((u8_t*)p->payload + 4),data,len); + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} +/*-----------------------------------------------------------------------------------*/ +/* hci_sniff_mode(): + * + * Sets an ACL connection to low power Sniff mode. + */ +/*-----------------------------------------------------------------------------------*/ +err_t hci_sniff_mode(struct bd_addr *bdaddr, u16_t max_interval, u16_t min_interval, u16_t attempt, u16_t timeout) +{ + struct pbuf *p; + struct hci_link *link; + + /* Check if an ACL connection exists */ + link = hci_get_link(bdaddr); + + if(link == NULL) { + ERROR("hci_sniff_mode: ACL connection does not exist\n"); + return ERR_CONN; + } + + if((p = btpbuf_alloc(PBUF_TRANSPORT, HCI_SNIFF_PLEN, PBUF_RAM)) == NULL) { /* Alloc len of packet */ + ERROR("hci_sniff_mode: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_SNIFF_MODE_OCF, HCI_LINK_POLICY_OGF, HCI_SNIFF_PLEN); + /* Assembling cmd prameters */ + ((u16_t *)p->payload)[2] = htole16(link->connhdl); + ((u16_t *)p->payload)[3] = htole16(max_interval); + ((u16_t *)p->payload)[4] = htole16(min_interval); + ((u16_t *)p->payload)[5] = htole16(attempt); + ((u16_t *)p->payload)[6] = htole16(timeout); + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} +/*-----------------------------------------------------------------------------------*/ +/* hci_write_link_policy_settings(): + * + * Control the modes (park, sniff, hold) that an ACL connection can take. + * + */ +/*-----------------------------------------------------------------------------------*/ +err_t hci_write_link_policy_settings(struct bd_addr *bdaddr, u16_t link_policy) +{ + struct pbuf *p; + struct hci_link *link; + + /* Check if an ACL connection exists */ + link = hci_get_link(bdaddr); + + if(link == NULL) { + ERROR("hci_write_link_policy_settings: ACL connection does not exist\n"); + return ERR_CONN; + } + + if( (p = btpbuf_alloc(PBUF_TRANSPORT, HCI_W_LINK_POLICY_PLEN, PBUF_RAM)) == NULL) { /* Alloc len of packet */ + ERROR("hci_write_link_policy_settings: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_W_LINK_POLICY_OCF, HCI_LINK_POLICY_OGF, HCI_W_LINK_POLICY_PLEN); + + /* Assembling cmd prameters */ + ((u16_t *)p->payload)[2] = htole16(link->connhdl); + ((u16_t *)p->payload)[3] = htole16(link_policy); + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* hci_pin_code_request_reply(): + * + * Used to reply to a PIN Code Request event from the Host Controller and specifies + * the PIN code to use for a connection. + */ + /*-----------------------------------------------------------------------------------*/ +err_t hci_pin_code_request_reply(struct bd_addr *bdaddr, u8_t pinlen, u8_t *pincode) +{ + struct pbuf *p; + + if((p = btpbuf_alloc(PBUF_RAW, HCI_PIN_CODE_REQ_REP_PLEN, PBUF_RAM)) == NULL) { + ERROR("hci_pin_code_request_reply: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Reset buffer content just to make sure */ + memset((u8_t *)p->payload, 0, HCI_PIN_CODE_REQ_REP_PLEN); + + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_PIN_CODE_REQ_REP, HCI_LINK_CTRL_OGF, HCI_PIN_CODE_REQ_REP_PLEN); + /* Assembling cmd prameters */ + memcpy(((u8_t *)p->payload) + 4, bdaddr->addr, 6); + ((u8_t *)p->payload)[10] = pinlen; + memcpy(((u8_t *)p->payload) + 11, pincode, pinlen); + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* hci_link_key_req_reply(): + * + * Used to reply to a Link Key Code Request event from the Host Controller and specifies + * the Link Key to use for a connection. + */ + /*-----------------------------------------------------------------------------------*/ +err_t hci_link_key_req_reply(struct bd_addr *bdaddr, unsigned char *link_key) +{ + struct pbuf *p; + if ((p = btpbuf_alloc(PBUF_RAW, HCI_LINK_KEY_REQ_REP_PLEN, PBUF_RAM)) == NULL) { + ERROR("hci_link_key_req_reply: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + p = hci_cmd_ass(p, HCI_LINK_KEY_REQ_REP, HCI_LINK_CTRL_OGF, HCI_LINK_KEY_REQ_REP_PLEN); + //copy bdaddr to offset 0x4 + memcpy(((u8_t *)p->payload)+4, bdaddr->addr, 6); + //copy Link Key (16 bytes long) to offset 10 (0xA) + memcpy(((u8_t *)p->payload)+10, link_key, 16); + //send command + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + + +/*-----------------------------------------------------------------------------------*/ +/* hci_pin_code_request_neg_reply(): + * + * Used to reply to a PIN Code Request event from the Host Controller when the Host + * cannot specify a PIN code to use for a connection. + */ + /*-----------------------------------------------------------------------------------*/ +err_t hci_pin_code_request_neg_reply(struct bd_addr *bdaddr) +{ + struct pbuf *p; + + if((p=btpbuf_alloc(PBUF_RAW,HCI_PIN_CODE_REQ_NEG_REP_PLEN,PBUF_RAM)) == NULL) { + ERROR("hci_pin_code_request_neg_reply: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + p = hci_cmd_ass(p,HCI_PIN_CODE_REQ_NEG_REP,HCI_LINK_CTRL_OGF,HCI_PIN_CODE_REQ_NEG_REP_PLEN); + memcpy(((u8_t *)p->payload)+4, bdaddr->addr, 6); + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* hci_link_key_req_neg_reply(): + * + * Used to reply to a Link Key Request event from the Host Controller when the Host + * cannot specify a Link Key to use for a connection. + */ + /*-----------------------------------------------------------------------------------*/ +err_t hci_link_key_req_neg_reply(struct bd_addr *bdaddr) +{ + struct pbuf *p; + + if ((p = btpbuf_alloc(PBUF_RAW, HCI_LINK_KEY_REQ_REP_NEG_PLEN, PBUF_RAM)) == NULL) { + ERROR("hci_link_key_req_neg_repl: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + p = hci_cmd_ass(p, HCI_LINK_KEY_REQ_REP_NEG, HCI_LINK_CTRL_OGF, HCI_LINK_KEY_REQ_REP_NEG_PLEN); + memcpy(((u8_t *)p->payload)+4, bdaddr->addr, 6); + + physbusif_output(p,p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* hci_disconnect(): + * + * Used to terminate an existing connection. + */ + /*-----------------------------------------------------------------------------------*/ +err_t hci_disconnect(struct bd_addr *bdaddr, u8_t reason) +{ + struct pbuf *p; + struct hci_link *link; + + link = hci_get_link(bdaddr); + + if(link == NULL) { + ERROR("hci_disconnect: Connection does not exist\n"); + return ERR_CONN; /* Connection does not exist */ + } + if((p = btpbuf_alloc(PBUF_RAW, HCI_DISCONN_PLEN, PBUF_RAM)) == NULL) { + ERROR("hci_disconnect: Could not allocate memory for pbuf\n"); + return ERR_MEM; /* Could not allocate memory for pbuf */ + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_DISCONN_OCF, HCI_LINK_CTRL_OGF, HCI_DISCONN_PLEN); + + /* Assembling cmd prameters */ + ((u16_t *)p->payload)[2] = htole16(link->connhdl); + ((u8_t *)p->payload)[6] = reason; + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* hci_reject_connection_request(): + * + * Used to decline a new incoming connection request. + */ + /*-----------------------------------------------------------------------------------*/ +err_t hci_reject_connection_request(struct bd_addr *bdaddr, u8_t reason) +{ + struct pbuf *p; + + if((p = btpbuf_alloc(PBUF_RAW, HCI_REJECT_CONN_REQ_PLEN, PBUF_RAM)) == NULL) { + ERROR("hci_reject_connection_request: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_REJECT_CONN_REQ_OCF, HCI_LINK_CTRL_OGF, HCI_REJECT_CONN_REQ_PLEN); + /* Assembling cmd prameters */ + memcpy(((u8_t *)p->payload) + 4, bdaddr->addr, 6); + ((u8_t *)p->payload)[10] = reason; + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* hci_write_stored_link_key(): + * + * Writes a link key to be stored in the Bluetooth host controller. + */ + /*-----------------------------------------------------------------------------------*/ +err_t hci_write_stored_link_key(struct bd_addr *bdaddr, u8_t *link) +{ + struct pbuf *p; + + if((p = btpbuf_alloc(PBUF_RAW, HCI_WRITE_STORED_LINK_KEY_PLEN, PBUF_RAM)) == NULL) { + ERROR("hci_write_stored_link_key: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_WRITE_STORED_LINK_KEY, HCI_HC_BB_OGF, HCI_WRITE_STORED_LINK_KEY_PLEN); + /* Assembling cmd prameters */ + ((u8_t *)p->payload)[4] = 0x01; + memcpy(((u8_t *)p->payload) + 5, bdaddr->addr, 6); + memcpy(((u8_t *)p->payload) + 11, link, 16); + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* hci_write_cod(): + * + * Write the value for the Class_of_Device parameter, which is used to indicate its + * capabilities to other devices. + */ + /*-----------------------------------------------------------------------------------*/ +err_t hci_write_cod(u8_t *cod) +{ + struct pbuf *p; + + if((p = btpbuf_alloc(PBUF_RAW, HCI_W_COD_PLEN, PBUF_RAM)) == NULL) { + ERROR("hci_write_cod: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_W_COD_OCF, HCI_HC_BB_OGF, HCI_W_COD_PLEN); + /* Assembling cmd prameters */ + memcpy(((u8_t *)p->payload)+4, cod, 3); + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +err_t hci_read_current_lap(void) +{ + struct pbuf *p; + + if((p = btpbuf_alloc(PBUF_RAW, HCI_R_CUR_IACLAP_PLEN, PBUF_RAM)) == NULL) { + ERROR("hci_read_current_lap: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_R_CUR_IACLAP_OCF, HCI_HC_BB_OGF, HCI_R_CUR_IACLAP_PLEN); + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* hci_set_hc_to_h_fc(): + * + * Used by the Host to turn flow control on or off in the direction from the Host + * Controller to the Host. + */ + /*-----------------------------------------------------------------------------------*/ +err_t hci_set_hc_to_h_fc(void) +{ + struct pbuf *p; + + if((p = btpbuf_alloc(PBUF_RAW, HCI_SET_HC_TO_H_FC_PLEN, PBUF_RAM)) == NULL) { + ERROR("hci_set_hc_to_h_fc: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_SET_HC_TO_H_FC_OCF, HCI_HC_BB_OGF, HCI_SET_HC_TO_H_FC_PLEN); + /* Assembling cmd prameters */ + ((u8_t *)p->payload)[4] = 0x01; /* Flow control on for HCI ACL Data Packets and off for HCI + SCO Data Packets in direction from Host Controller to + Host */ + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* hci_host_buffer_size(): + * + * Used by the Host to notify the Host Controller about the maximum size of the data + * portion of HCI ACL Data Packets sent from the Host Controller to the Host. + */ + /*-----------------------------------------------------------------------------------*/ +err_t hci_host_buffer_size(void) +{ + struct pbuf *p; + if((p = btpbuf_alloc(PBUF_RAW, HCI_H_BUF_SIZE_PLEN, PBUF_RAM)) == NULL) { + ERROR("hci_host_buffer_size: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_H_BUF_SIZE_OCF, HCI_HC_BB_OGF, HCI_H_BUF_SIZE_PLEN); + ((u16_t *)p->payload)[2] = htole16(HCI_HOST_ACL_MAX_LEN); /* Host ACL data packet maximum length */ + ((u8_t *)p->payload)[6] = 255; /* Host SCO Data Packet Length */ + *((u16_t *)(((u8_t *)p->payload)+7)) = htole16(HCI_HOST_MAX_NUM_ACL); /* Host max total num ACL data packets */ + ((u16_t *)p->payload)[4] = htole16(1); /* Host Total Num SCO Data Packets */ + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + hci_dev->host_num_acl = HCI_HOST_MAX_NUM_ACL; + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* hci_host_num_comp_packets(): + * + * Used by the Host to indicate to the Host Controller the number of HCI Data Packets + * that have been completed for each Connection Handle since the previous + * Host_Number_Of_Completed_Packets command was sent to the Host Controller. + */ + /*-----------------------------------------------------------------------------------*/ +err_t hci_host_num_comp_packets(u16_t conhdl, u16_t num_complete) +{ + struct pbuf *p; + + if((p = btpbuf_alloc(PBUF_RAW, HCI_H_NUM_COMPL_PLEN, PBUF_RAM)) == NULL) { + ERROR("hci_host_num_comp_packets: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_H_NUM_COMPL_OCF, HCI_HC_BB_OGF, HCI_H_NUM_COMPL_PLEN); + ((u8_t*)p->payload)[4] = 1; + *(u16_t*)(p->payload+5) = htole16(conhdl); + *(u16_t*)(p->payload+7) = htole16(num_complete); /* Number of completed acl packets */ + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + hci_dev->host_num_acl += num_complete; + + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* lp_pdu_maxsize(): + * + * Called by L2CAP to check the maxsize of the PDU. In this case it is the largest + * ACL packet that the Host Controller can buffer. + */ +/*-----------------------------------------------------------------------------------*/ +u16_t lp_pdu_maxsize() +{ + return hci_dev->acl_mtu; +} + +/*-----------------------------------------------------------------------------------*/ +/* lp_is_connected(): + * + * Called by L2CAP to check if an active ACL connection exists for the specified + * Bluetooth address. + */ +/*-----------------------------------------------------------------------------------*/ +u8_t lp_is_connected(struct bd_addr *bdaddr) +{ + struct hci_link *link; + + link = hci_get_link(bdaddr); + + if(link == NULL) { + return 0; + } + return 1; +} + +/*-----------------------------------------------------------------------------------*/ +/* lp_acl_write(): + * + * Called by L2CAP to send data to the Host Controller that will be transfered over + * the ACL link from there. + */ +/*-----------------------------------------------------------------------------------*/ +err_t lp_acl_write(struct bd_addr *bdaddr,struct pbuf *p,u16_t len,u8_t pb) +{ + u16_t connhdlpbbc; + struct hci_link *link; + struct hci_acl_hdr *aclhdr; + struct pbuf *q; + + link = hci_get_link(bdaddr); + if(link==NULL) { + ERROR("lp_acl_write: ACL connection does not exist\n"); + return ERR_CONN; + } + + if(hci_dev->acl_max_pkt==0) { + if(p != NULL) { + /* Packet can be queued? */ + if(link->p != NULL) { + LOG("lp_acl_write: Host buffer full. Dropped packet\n"); + return ERR_OK; /* Drop packet */ + } else { + /* Copy PBUF_REF referenced payloads into PBUF_RAM */ + p = btpbuf_take(p); + /* Remember pbuf to queue, if any */ + link->p = p; + link->len = len; + link->pb = pb; + /* Pbufs are queued, increase the reference count */ + btpbuf_ref(p); + LOG("lp_acl_write: Host queued packet %p\n", (void *)p); + } + } + } + + if((q=btpbuf_alloc(PBUF_RAW,HCI_ACL_HDR_LEN+1,PBUF_RAM))==NULL) { + ERROR("lp_acl_write: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + btpbuf_chain(q,p); + ((u8_t*)q->payload)[0] = HCI_ACL_DATA_PACKET; + + aclhdr = (void*)((u8_t*)q->payload+1); + //aclhdr->connhdl_pb_bc = CONNPBBC(link->connhdl,pb,0); + connhdlpbbc = link->connhdl; /* Received from connection complete event */ + connhdlpbbc |= (pb<<12); /* Packet boundary flag */ + connhdlpbbc &= 0x3FFF; /* Point-to-point */ + aclhdr->connhdl_pb_bc = htole16(connhdlpbbc); + aclhdr->len = htole16(len); + + physbusif_output(q,(q->len+len)); + --hci_dev->acl_max_pkt; + + p = btpbuf_dechain(q); + btpbuf_free(q); + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* lp_write_flush_timeout(): + * + * Called by L2CAP to set the flush timeout for the ACL link. + */ +/*-----------------------------------------------------------------------------------*/ +err_t lp_write_flush_timeout(struct bd_addr *bdaddr, u16_t flushto) +{ + struct hci_link *link; + struct pbuf *p; + + /* Check if an ACL connection exists */ + link = hci_get_link(bdaddr); + + if(link == NULL) { + ERROR("lp_write_flush_timeout: ACL connection does not exist\n"); + return ERR_CONN; + } + + if((p = btpbuf_alloc(PBUF_TRANSPORT, HCI_W_FLUSHTO_PLEN, PBUF_RAM)) == NULL) { /* Alloc len of packet */ + ERROR("lp_write_flush_timeout: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_W_FLUSHTO, HCI_HC_BB_OGF, HCI_W_FLUSHTO_PLEN); + /* Assembling cmd prameters */ + ((u16_t *)p->payload)[2] = htole16(link->connhdl); + ((u16_t *)p->payload)[3] = htole16(flushto); + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* lp_connect_req(): + * + * Called by L2CAP to cause the Link Manager to create a connection to the + * Bluetooth device with the BD_ADDR specified by the command parameters. + */ +/*-----------------------------------------------------------------------------------*/ +err_t lp_connect_req(struct bd_addr *bdaddr, u8_t allow_role_switch) +{ + u8_t page_scan_repetition_mode, page_scan_mode; + u16_t clock_offset; + struct pbuf *p; + struct hci_link *link = hci_new(); + struct hci_inq_res *inqres; + + if(link == NULL) { + ERROR("lp_connect_req: Could not allocate memory for link\n"); + return ERR_MEM; /* Could not allocate memory for link */ + } + + bd_addr_set(&(link->bdaddr), bdaddr); + HCI_REG(&(hci_active_links), link); + + + /* Check if module has been discovered in a recent inquiry */ + for(inqres = hci_dev->ires; inqres != NULL; inqres = inqres->next) { + if(bd_addr_cmp(&inqres->bdaddr, bdaddr)) { + page_scan_repetition_mode = inqres->psrm; + page_scan_mode = inqres->psm; + clock_offset = inqres->co; + break; + } + } + if(inqres == NULL) { + /* No information on parameters from an inquiry. Using default values */ + page_scan_repetition_mode = 0x01; /* Assuming worst case: time between + successive page scans starting + <= 2.56s */ + page_scan_mode = 0x00; /* Assumes the device uses mandatory scanning, most + devices use this. If no conn is established, try + again w this parm set to optional page scanning */ + clock_offset = 0x00; /* If the device was not found in a recent inquiry + this information is irrelevant */ + } + + if((p = btpbuf_alloc(PBUF_RAW, HCI_CREATE_CONN_PLEN, PBUF_RAM)) == NULL) { + ERROR("lp_connect_req: Could not allocate memory for pbuf\n"); + return ERR_MEM; /* Could not allocate memory for pbuf */ + } + + /* Assembling command packet */ + p = hci_cmd_ass(p, HCI_CREATE_CONN_OCF, HCI_LINK_CTRL_OGF, HCI_CREATE_CONN_PLEN); + /* Assembling cmd prameters */ + memcpy(((u8_t *)p->payload)+4, bdaddr->addr, 6); + ((u16_t *)p->payload)[5] = htole16(hci_dev->pkt_type); + ((u8_t *)p->payload)[12] = page_scan_repetition_mode; + ((u8_t *)p->payload)[13] = page_scan_mode; + ((u16_t *)p->payload)[7] = htole16(clock_offset); + ((u8_t *)p->payload)[16] = allow_role_switch; + + physbusif_output(p, p->tot_len); + btpbuf_free(p); + + return ERR_OK; +} + +static void hci_cc_info_param(u8_t ocf,struct pbuf *p) +{ + struct bd_addr *bdaddr; + + switch(ocf) { + case HCI_READ_LOCAL_VERSION: + if(((u8_t*)p->payload)[0]==HCI_SUCCESS) { + hci_dev->info.hci_version = *((u8_t*)p->payload + 1); + hci_dev->info.hci_revision = le16toh(*(u16_t*)((u8_t*)p->payload + 2)); + hci_dev->info.lmp_version = *((u8_t*)p->payload + 4); + hci_dev->info.manufacturer = le16toh(*(u16_t*)((u8_t*)p->payload + 5)); + hci_dev->info.lmp_subversion = le16toh(*(u16_t*)((u8_t*)p->payload + 7)); + LOG("hci_cc_info_param(HCI_READ_LOCAL_VERSION): hci_version = %02x, hci_revision = %04x, lmp_version = %02x, manufacturer = %04x, lmp_suversion = %04x\n",hci_dev->info.hci_version,hci_dev->info.hci_revision,hci_dev->info.lmp_version,hci_dev->info.manufacturer,hci_dev->info.lmp_subversion); + } + break; + case HCI_READ_LOCAL_FEATURES: + if(((u8_t*)p->payload)[0]==HCI_SUCCESS) { + memcpy(hci_dev->features,(void*)((u8_t*)p->payload+1),sizeof(hci_dev->features)); + + if(hci_dev->features[0]&LMP_3SLOT) + hci_dev->pkt_type |= (HCI_DM3|HCI_DH3); + if(hci_dev->features[0]&LMP_5SLOT) + hci_dev->pkt_type |= (HCI_DM5|HCI_DH5); + if(hci_dev->features[1]&LMP_HV2) + hci_dev->pkt_type |= HCI_HV2; + if(hci_dev->features[1]&LMP_HV3) + hci_dev->pkt_type |= HCI_HV3; + LOG("hci_cc_info_param(HCI_READ_LOCAL_FEATURES): %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",hci_dev->features[0],hci_dev->features[1],hci_dev->features[2],hci_dev->features[3], + hci_dev->features[4],hci_dev->features[5],hci_dev->features[6],hci_dev->features[7]); + } + break; + case HCI_READ_BUFFER_SIZE: + if(((u8_t*)p->payload)[0]==HCI_SUCCESS) { + hci_dev->acl_mtu = le16toh(*(u16_t*)(((u8_t*)p->payload)+1)); + hci_dev->sco_mtu = *((u8_t*)p->payload+3); + hci_dev->acl_max_pkt = le16toh(*(u16_t*)(((u8_t*)p->payload)+4)); + hci_dev->sco_max_pkt = le16toh(*(u16_t*)(((u8_t*)p->payload)+5)); + LOG("hci_cc_info_param(HCI_READ_BUFFER_SIZE): acl_mt = %d, sco_mt = %d, acl_max_pkt = %d, sco_max_pkt = %d\n",hci_dev->acl_mtu,hci_dev->sco_mtu,hci_dev->acl_max_pkt,hci_dev->sco_max_pkt); + } + break; + case HCI_READ_BD_ADDR: + if(((u8_t*)p->payload)[0]==HCI_SUCCESS) { + bdaddr = (void*)((u8_t*)p->payload+1); + LOG("hci_cc_info_param(HCI_READ_BD_ADDR): %02x:%02x:%02x:%02x:%02x:%02x",bdaddr->addr[0],bdaddr->addr[1],bdaddr->addr[2],bdaddr->addr[3],bdaddr->addr[4],bdaddr->addr[5]); + bd_addr_set(&(hci_dev->bdaddr),bdaddr); + } + break; + } +} + +static void hci_cc_host_ctrl(u8_t ocf,struct pbuf *p) +{ + u8_t *lap; + u8_t i,resp_off; + + //printf("hci_cc_host_ctrl(%02x)\n",ocf); + switch(ocf) { + case HCI_SET_HC_TO_H_FC: + if(((u8_t*)p->payload)[0]==HCI_SUCCESS) hci_dev->flow = 1; + break; + case HCI_READ_CUR_IACLAP: + if(((u8_t*)p->payload)[0]==HCI_SUCCESS) { + for(i=0;i<((u8_t*)p->payload)[1];i++) { + resp_off = (i*3); + lap = (void*)(((u8_t*)p->payload)+(2+resp_off)); + printf("lap = 00%02x%02x%02x\n",lap[2],lap[1],lap[0]); + } + } + break; + } +} + +static void hci_cc_link_policy(u8_t ocf,struct pbuf *p) +{ + err_t ret; + struct hci_link *link; + + (void)ret; + + switch(ocf) { + case HCI_W_LINK_POLICY: + if(((u8_t*)p->payload)[0]==HCI_SUCCESS) { + for(link=hci_active_links;link!=NULL;link=link->next) { + if(link->connhdl==le16toh(*((u16_t*)(((u8_t*)p->payload)+1)))) break; + } + if(link==NULL) { + LOG("hci_cc_link_policy: Connection does not exist\n"); + break; + } + HCI_EVENT_WLP_COMPLETE(hci_dev,&link->bdaddr,ret); + } else { + LOG("Unsuccessful HCI_W_LINK_POLICY.\n"); + } + break; + } +} + +static void hci_conn_request_evt(struct pbuf *p) +{ + u8_t *cod; + u8_t link_type; + err_t ret = ERR_OK; + struct bd_addr *bdaddr; + struct hci_link *link; + + LOG("hci_conn_request_evt()\n"); + bdaddr = (void*)((u8_t*)p->payload); + cod = (((u8_t*)p->payload)+6); + link_type = *(((u8_t*)p->payload)+9); + + HCI_EVENT_CONN_REQ(hci_dev,bdaddr,cod,link_type,ret); + if(ret==ERR_OK) { + link = hci_get_link(bdaddr); + if(link==NULL) { + if((link=hci_new())==NULL) { + ERROR("hci_conn_request_evt: Could not allocate memory for link. Disconnect\n"); + return; + } + + bd_addr_set(&(link->bdaddr),bdaddr); + HCI_REG(&(hci_active_links),link); + } + hci_accecpt_conn_request(bdaddr,0x00); + } else { + } +} + +static void hci_conn_complete_evt(struct pbuf *p) +{ + err_t ret; + struct bd_addr *bdaddr; + struct hci_link *link; + + (void)ret; + + bdaddr = (void*)(((u8_t*)p->payload)+3); + link = hci_get_link(bdaddr); + LOG("hci_conn_complete_evt(%p,%02x - %02x:%02x:%02x:%02x:%02x:%02x)\n",link,((u8_t*)p->payload)[0],bdaddr->addr[0],bdaddr->addr[1],bdaddr->addr[2],bdaddr->addr[3],bdaddr->addr[4],bdaddr->addr[5]); + switch(((u8_t*)p->payload)[0]) { + case HCI_SUCCESS: + if(link==NULL) { + if((link=hci_new())==NULL) { + ERROR("hci_conn_complete_evt: Could not allocate memory for link. Disconnect\n"); + hci_disconnect(bdaddr, HCI_OTHER_END_TERMINATED_CONN_LOW_RESOURCES); + lp_disconnect_ind(bdaddr,HCI_CONN_TERMINATED_BY_LOCAL_HOST); + break; + } + bd_addr_set(&(link->bdaddr),bdaddr); + link->connhdl = le16toh(*((u16_t*)(((u8_t*)p->payload)+1))); + HCI_REG(&(hci_active_links),link); + HCI_EVENT_CONN_COMPLETE(hci_dev,bdaddr,ret); + lp_connect_ind(&(link->bdaddr)); + } else { + link->connhdl = le16toh(*((u16_t*)(((u8_t*)p->payload)+1))); + HCI_EVENT_CONN_COMPLETE(hci_dev,bdaddr,ret); + lp_connect_cfm(&(link->bdaddr),((u8_t*)p->payload)[10],ERR_OK); + } + break; + case HCI_PAGE_TIMEOUT: + break; + default: + if(link!=NULL) { + hci_close(link); + lp_connect_cfm(bdaddr,((u8_t*)p->payload)[10],ERR_CONN); + } + break; + } +} + +static void hci_inquiry_result_evt(struct pbuf *p) +{ + u8_t num_resp; + u32_t i,resp_off; + struct bd_addr *bdaddr; + struct hci_inq_res *ires; + + num_resp = ((u8_t*)p->payload)[0]; + //printf("hci_inquriy_result_evt(%d)\n",num_resp); + for(i=0;ipayload)+(1+resp_off)); + if((ires=btmemb_alloc(&hci_inq_results))!=NULL) { + bd_addr_set(&(ires->bdaddr),bdaddr); + ires->psrm = ((u8_t*)p->payload)[7+resp_off]; + ires->psm = ((u8_t*)p->payload)[8+resp_off]; + memcpy(ires->cod,((u8_t*)p->payload)+10+resp_off,3); + ires->co = le16toh(*((u16_t*)(((u8_t*)p->payload)+13+resp_off))); + ires->next = NULL; + + HCI_REG(&(hci_dev->ires),ires); + } else + ERROR("hci_inquriy_result_evt: Could not allocate memory for inquiry result\n"); + } + +} + +static void hci_return_link_key_evt(struct pbuf *p) +{ + u8_t num_keys; + u32_t i,resp_off; + struct bd_addr *bdaddr; + struct hci_link_key *keyres; + + num_keys = ((u8_t*)p->payload)[0]; + //printf("hci_return_link_key_evt(%d)\n",num_keys); + for(i=0;ipayload)+1+resp_off); + if((keyres=btmemb_alloc(&hci_link_key_results))!=NULL) { + bd_addr_set(&(keyres->bdaddr),bdaddr); + memcpy(keyres->key,((u8_t*)p->payload)+7+resp_off,16); + keyres->next = NULL; + + //printf("link key evt: %02x:%02x:%02x:%02x:%02x:%02x\n",bdaddr->addr[0],bdaddr->addr[1],bdaddr->addr[2],bdaddr->addr[3],bdaddr->addr[4],bdaddr->addr[5]); + HCI_REG(&(hci_dev->keyres),keyres); + } else + ERROR("hci_return_link_key_evt: Could not allocate memory for link key result\n"); + } + +} + +void hci_event_handler(struct pbuf *p) +{ + err_t ret; + u8_t i,resp_off; + u16_t ogf,ocf,opc; + u16_t connhdl; + struct pbuf *q; + struct hci_link *link; + struct bd_addr *bdaddr; + struct hci_evt_hdr *evthdr; + + (void)ret; + + evthdr = p->payload; + btpbuf_header(p,-HCI_EVENT_HDR_LEN); + + switch(evthdr->code) { + case HCI_INQUIRY_COMPLETE: + //printf("HCI_INQUIRY_COMPLETE\n"); + HCI_EVENT_INQ_COMPLETE(hci_dev,((u8_t*)p->payload)[0],ret); + break; + case HCI_INQUIRY_RESULT: + hci_inquiry_result_evt(p); + break; + case HCI_CONNECTION_COMPLETE: + hci_conn_complete_evt(p); + break; + case HCI_CONNECTION_REQUEST: + hci_conn_request_evt(p); + break; + case HCI_DISCONNECTION_COMPLETE: + switch(((u8_t*)p->payload)[0]) { + case HCI_SUCCESS: + for(link=hci_active_links;link!=NULL;link=link->next) { + if(link->connhdl==le16toh(*((u16_t*)(((u8_t*)p->payload)+1)))) break; + } + if(link!=NULL) { + lp_disconnect_ind(&(link->bdaddr),((u8_t*)p->payload)[3]); + hci_close(link); + } + break; + default: + return; + } + break; + case HCI_ENCRYPTION_CHANGE: + break; + case HCI_QOS_SETUP_COMPLETE: + break; + case HCI_COMMAND_COMPLETE: + hci_dev->num_cmd += ((u8_t*)p->payload)[0]; + btpbuf_header(p,-1); + + opc = le16toh(((u16_t*)p->payload)[0]); + ocf = (opc&0x03ff); + ogf = (opc>>10); + btpbuf_header(p,-2); + + switch(ogf) { + case HCI_INFO_PARAM: + hci_cc_info_param(ocf,p); + break; + case HCI_HOST_C_N_BB: + hci_cc_host_ctrl(ocf,p); + break; + case HCI_LINK_POLICY: + hci_cc_link_policy(ocf,p); + break; + } + HCI_EVENT_CMD_COMPLETE(hci_dev,ogf,ocf,((u8_t*)p->payload)[0],ret); + break; + case HCI_COMMAND_STATUS: + if(((u8_t*)p->payload)[0]!=HCI_SUCCESS) { + btpbuf_header(p,-2); + + opc = le16toh(((u16_t*)p->payload)[0]); + ocf = (opc&0x03ff); + ogf = (opc>>10); + btpbuf_header(p,-2); + + HCI_EVENT_CMD_COMPLETE(hci_dev,ogf,ocf,((u8_t*)p->payload)[0],ret); + btpbuf_header(p,4); + } + hci_dev->num_cmd += ((u8_t*)p->payload)[1]; + break; + case HCI_HARDWARE_ERROR: + //TODO: IS THIS FATAL?? + break; + case HCI_ROLE_CHANGE: + break; + case HCI_NBR_OF_COMPLETED_PACKETS: + for(i=0;i<((u8_t *)p->payload)[0];i++) { + resp_off = i*4; + hci_dev->acl_max_pkt += le16toh(*((u16_t *)(((u8_t *)p->payload) + 3 + resp_off))); + connhdl = le16toh(*((u16_t *)(((u8_t *)p->payload) + 1 + resp_off))); + + for(link = hci_active_links; link != NULL; link = link->next) { + if(link->connhdl == connhdl) break; + } + + q = link == NULL ? NULL : link->p; + /* Queued packet present? */ + if (q != NULL) { + /* NULL attached buffer immediately */ + link->p = NULL; + /* Send the queued packet */ + lp_acl_write(&link->bdaddr, q, link->len, link->pb); + /* Free the queued packet */ + btpbuf_free(q); + } + } + break; + case HCI_MODE_CHANGE: + printf("HCI_MODE_CHANGE\n"); + break; + case HCI_DATA_BUFFER_OVERFLOW: + //TODO: IS THIS FATAL???? + break; + case HCI_MAX_SLOTS_CHANGE: + break; + case HCI_RETURN_LINK_KEYS: + hci_return_link_key_evt(p); + break; + case HCI_PIN_CODE_REQUEST: + bdaddr = (void *)((u8_t *)p->payload); /* Get the Bluetooth address */ + HCI_EVENT_PIN_REQ(hci_dev, bdaddr, ret); /* Notify application. If event is not registered, + send a negative reply */ + break; + case HCI_LINK_KEY_REQUEST: + bdaddr = (void *)((u8_t *)p->payload); /* Get the Bluetooth address */ + HCI_EVENT_LINK_KEY_REQ(hci_dev, bdaddr, ret); + break; + case HCI_LINK_KEY_NOTIFICATION: + bdaddr = (void *)((u8_t *)p->payload); /* Get the Bluetooth address */ + + HCI_EVENT_LINK_KEY_NOT(hci_dev, bdaddr, ((u8_t *)p->payload) + 6, ret); /* Notify application.*/ + break; + default: + LOG("hci_event_input: Undefined event code 0x%x\n", evthdr->code); + break; + } +} + +void hci_acldata_handler(struct pbuf *p) +{ + struct hci_acl_hdr *aclhdr; + struct hci_link *link; + u16_t conhdl; + + aclhdr = p->payload; + btpbuf_header(p, -HCI_ACL_HDR_LEN); + + conhdl = le16toh(aclhdr->connhdl_pb_bc) & 0x0FFF; /* Get the connection handle from the first + 12 bits */ + if(hci_dev->flow) { + //TODO: XXX??? DO WE SAVE NUMACL PACKETS COMPLETED IN LINKS LIST?? SHOULD WE CALL + //hci_host_num_comp_packets from the main loop when no data has been received from the + //serial port??? + --hci_dev->host_num_acl; + if(hci_dev->host_num_acl == 0) { + hci_host_num_comp_packets(conhdl, HCI_HOST_MAX_NUM_ACL); + hci_dev->host_num_acl = HCI_HOST_MAX_NUM_ACL; + } + } + + for(link = hci_active_links; link != NULL; link = link->next) { + if(link->connhdl == conhdl) { + break; + } + } + + if(link != NULL) { + if(le16toh(aclhdr->len)) { + //LOG("hci_acl_input: Forward ACL packet to higher layer p->tot_len = %d\n", p->tot_len); + l2cap_input(p, &(link->bdaddr)); + } else { + btpbuf_free(p); /* If length of ACL packet is zero, we silently discard it */ + } + } else { + btpbuf_free(p); /* If no acitve ACL link was found, we silently discard the packet */ + } +} diff --git a/wii/libogc/lwbt/hci.h b/wii/libogc/lwbt/hci.h new file mode 100644 index 0000000000..3a3a98cf61 --- /dev/null +++ b/wii/libogc/lwbt/hci.h @@ -0,0 +1,482 @@ +#ifndef __HCI_H__ +#define __HCI_H__ + +#include "bt.h" +#include "bd_addr.h" + +/* HCI packet indicators */ +#define HCI_COMMAND_DATA_PACKET 0x01 +#define HCI_ACL_DATA_PACKET 0x02 +#define HCI_SCO_DATA_PACKET 0x03 +#define HCI_EVENT_PACKET 0x04 +#define HCI_VENDOR_PACKET 0xff + +/* HCI packet types */ +#define HCI_DM1 0x0008 +#define HCI_DM3 0x0400 +#define HCI_DM5 0x4000 +#define HCI_DH1 0x0010 +#define HCI_DH3 0x0800 +#define HCI_DH5 0x8000 + +#define HCI_HV1 0x0020 +#define HCI_HV2 0x0040 +#define HCI_HV3 0x0080 + +#define SCO_PTYPE_MASK (HCI_HV1 | HCI_HV2 | HCI_HV3) +#define ACL_PTYPE_MASK (~SCO_PTYPE_MASK) + +#define HCI_EVENT_HDR_LEN 2 +#define HCI_ACL_HDR_LEN 4 +#define HCI_SCO_HDR_LEN 3 +#define HCI_CMD_HDR_LEN 3 + +/* LMP features */ +#define LMP_3SLOT 0x01 +#define LMP_5SLOT 0x02 +#define LMP_ENCRYPT 0x04 +#define LMP_SOFFSET 0x08 +#define LMP_TACCURACY 0x10 +#define LMP_RSWITCH 0x20 +#define LMP_HOLD 0x40 +#define LMP_SNIFF 0x80 + +#define LMP_PARK 0x01 +#define LMP_RSSI 0x02 +#define LMP_QUALITY 0x04 +#define LMP_SCO 0x08 +#define LMP_HV2 0x10 +#define LMP_HV3 0x20 +#define LMP_ULAW 0x40 +#define LMP_ALAW 0x80 + +#define LMP_CVSD 0x01 +#define LMP_PSCHEME 0x02 +#define LMP_PCONTROL 0x04 + +/* Opcode Group Field (OGF) values */ +#define HCI_LINK_CONTROL 0x01 /* Link Control Commands */ +#define HCI_LINK_POLICY 0x02 /* Link Policy Commands */ +#define HCI_HOST_C_N_BB 0x03 /* Host Controller & Baseband Commands */ +#define HCI_INFO_PARAM 0x04 /* Informational Parameters */ +#define HCI_STATUS_PARAM 0x05 /* Status Parameters */ +#define HCI_TESTING 0x06 /* Testing Commands */ + +/* Opcode Command Field (OCF) values */ + +/* Link control commands */ +#define HCI_INQUIRY 0x01 +#define HCI_PERIODIC_INQUIRY 0x03 +#define HCI_CREATE_CONNECTION 0x05 +#define HCI_REJECT_CONNECTION_REQUEST 0x0A +#define HCI_DISCONNECT 0x06 +#define HCI_PIN_CODE_REQ_REP 0x0D +#define HCI_PIN_CODE_REQ_NEG_REP 0x0E +#define HCI_LINK_KEY_REQ_REP 0x0B +#define HCI_LINK_KEY_REQ_REP_NEG 0x0C +#define HCI_SET_CONN_ENCRYPT 0x13 + +/* Link Policy commands */ +#define HCI_HOLD_MODE 0x01 +#define HCI_SNIFF_MODE 0x03 +#define HCI_EXIT_SNIFF_MODE 0x04 +#define HCI_PARK_MODE 0x05 +#define HCI_EXIT_PARK_MODE 0x06 +#define HCI_W_LINK_POLICY 0x0D + +/* Host-Controller and Baseband Commands */ +#define HCI_SET_EVENT_MASK 0x01 +#define HCI_RESET 0x03 +#define HCI_SET_EVENT_FILTER 0x05 +#define HCI_WRITE_STORED_LINK_KEY 0x11 +#define HCI_ROLE_CHANGE 0x12 +#define HCI_WRITE_LOCAL_NAME 0x13 + +#define HCI_WRITE_PAGE_TIMEOUT 0x18 +#define HCI_WRITE_SCAN_ENABLE 0x1A +#define HCI_WRITE_COD 0x24 +#define HCI_W_FLUSHTO 0x28 +#define HCI_SET_HC_TO_H_FC 0x31 +#define HCI_READ_CUR_IACLAP 0x39 +#define HCI_WRITE_PIN_TYPE 0x0A +#define HCI_R_STORED_LINK_KEY 0x0D +#define HCI_HOST_BUF_SIZE 0x33 +#define HCI_WRITE_INQUIRY_SCAN_TYPE 0x43 +#define HCI_WRITE_INQUIRY_MODE 0x45 +#define HCI_WRITE_PAGE_SCAN_TYPE 0x47 + +/* Informational Parameters */ +#define HCI_READ_LOCAL_VERSION 0x01 +#define HCI_READ_LOCAL_FEATURES 0x03 +#define HCI_READ_BUFFER_SIZE 0x05 +#define HCI_READ_BD_ADDR 0x09 + +/* Status Parameters */ +#define HCI_READ_FAILED_CONTACT_COUNTER 0x01 +#define HCI_RESET_FAILED_CONTACT_COUNTER 0x02 +#define HCI_GET_LINK_QUALITY 0x03 +#define HCI_READ_RSSI 0x05 + +/* Testing commands */ + +/* Possible event codes */ +#define HCI_INQUIRY_COMPLETE 0x01 +#define HCI_INQUIRY_RESULT 0x02 +#define HCI_CONNECTION_COMPLETE 0x03 +#define HCI_CONNECTION_REQUEST 0x04 +#define HCI_DISCONNECTION_COMPLETE 0x05 +#define HCI_ENCRYPTION_CHANGE 0x08 +#define HCI_QOS_SETUP_COMPLETE 0x0D +#define HCI_COMMAND_COMPLETE 0x0E +#define HCI_COMMAND_STATUS 0x0F +#define HCI_HARDWARE_ERROR 0x10 +#define HCI_ROLE_CHANGE 0x12 +#define HCI_NBR_OF_COMPLETED_PACKETS 0x13 +#define HCI_MODE_CHANGE 0x14 +#define HCI_RETURN_LINK_KEYS 0x15 +#define HCI_PIN_CODE_REQUEST 0x16 +#define HCI_LINK_KEY_REQUEST 0x17 +#define HCI_LINK_KEY_NOTIFICATION 0x18 +#define HCI_DATA_BUFFER_OVERFLOW 0x1A +#define HCI_MAX_SLOTS_CHANGE 0x1B + +/* Success code */ +#define HCI_SUCCESS 0x00 +/* Possible error codes */ +#define HCI_UNKNOWN_HCI_COMMAND 0x01 +#define HCI_NO_CONNECTION 0x02 +#define HCI_HW_FAILURE 0x03 +#define HCI_PAGE_TIMEOUT 0x04 +#define HCI_AUTHENTICATION_FAILURE 0x05 +#define HCI_KEY_MISSING 0x06 +#define HCI_MEMORY_FULL 0x07 +#define HCI_CONN_TIMEOUT 0x08 +#define HCI_MAX_NUMBER_OF_CONNECTIONS 0x09 +#define HCI_MAX_NUMBER_OF_SCO_CONNECTIONS_TO_DEVICE 0x0A +#define HCI_ACL_CONNECTION_EXISTS 0x0B +#define HCI_COMMAND_DISSALLOWED 0x0C +#define HCI_HOST_REJECTED_DUE_TO_LIMITED_RESOURCES 0x0D +#define HCI_HOST_REJECTED_DUE_TO_SECURITY_REASONS 0x0E +#define HCI_HOST_REJECTED_DUE_TO_REMOTE_DEVICE_ONLY_PERSONAL_SERVICE 0x0F +#define HCI_HOST_TIMEOUT 0x10 +#define HCI_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE 0x11 +#define HCI_INVALID_HCI_COMMAND_PARAMETERS 0x12 +#define HCI_OTHER_END_TERMINATED_CONN_USER_ENDED 0x13 +#define HCI_OTHER_END_TERMINATED_CONN_LOW_RESOURCES 0x14 +#define HCI_OTHER_END_TERMINATED_CONN_ABOUT_TO_POWER_OFF 0x15 +#define HCI_CONN_TERMINATED_BY_LOCAL_HOST 0x16 +#define HCI_REPETED_ATTEMPTS 0x17 +#define HCI_PAIRING_NOT_ALLOWED 0x18 +#define HCI_UNKNOWN_LMP_PDU 0x19 +#define HCI_UNSUPPORTED_REMOTE_FEATURE 0x1A +#define HCI_SCO_OFFSET_REJECTED 0x1B +#define HCI_SCO_INTERVAL_REJECTED 0x1C +#define HCI_SCO_AIR_MODE_REJECTED 0x1D +#define HCI_INVALID_LMP_PARAMETERS 0x1E +#define HCI_UNSPECIFIED_ERROR 0x1F +#define HCI_UNSUPPORTED_LMP_PARAMETER_VALUE 0x20 +#define HCI_ROLE_CHANGE_NOT_ALLOWED 0x21 +#define HCI_LMP_RESPONSE_TIMEOUT 0x22 +#define HCI_LMP_ERROR_TRANSACTION_COLLISION 0x23 +#define HCI_LMP_PDU_NOT_ALLOWED 0x24 +#define HCI_ENCRYPTION_MODE_NOT_ACCEPTABLE 0x25 +#define HCI_UNIT_KEY_USED 0x26 +#define HCI_QOS_NOT_SUPPORTED 0x27 +#define HCI_INSTANT_PASSED 0x28 +#define HCI_PAIRING_UNIT_KEY_NOT_SUPPORTED 0x29 + +/* Specification specific parameters */ +#define HCI_BD_ADDR_LEN 6 +#define HCI_LMP_FEATURES_LEN 8 +#define HCI_LINK_KEY_LEN 16 +#define HCI_LMP_FEAT_LEN 8 + +/* Command OGF */ +#define HCI_LINK_CTRL_OGF 0x01 /* Link ctrl cmds */ +#define HCI_LINK_POLICY_OGF 0x02 /* Link Policy Commands */ +#define HCI_HC_BB_OGF 0x03 /* Host controller and baseband commands */ +#define HCI_INFO_PARAM_OGF 0x04 /* Informal parameters */ + +/* Command OCF */ +#define HCI_INQUIRY_OCF 0x01 +#define HCI_SNIFF_MODE_OCF 0x03 +#define HCI_EXIT_SNIFF_MODE_OCF 0x04 +#define HCI_PARK_MODE_OCF 0x05 +#define HCI_EXIT_PARK_MODE_OCF 0x06 +#define HCI_PERIODIC_INQUIRY_OCF 0x03 +#define HCI_EXIT_PERIODIC_INQUIRY_OCF 0x04 +#define HCI_CREATE_CONN_OCF 0x05 +#define HCI_DISCONN_OCF 0x06 +#define HCI_W_LOCAL_NAME_OCF 0x13 +#define HCI_W_LINK_POLICY_OCF 0x0D +#define HCI_ACCEPT_CONN_REQ_OCF 0x09 +#define HCI_REJECT_CONN_REQ_OCF 0x0A +#define HCI_SET_EV_MASK_OCF 0x01 +#define HCI_RESET_OCF 0x03 +#define HCI_SET_EV_FILTER_OCF 0x05 +#define HCI_R_STORED_LINK_KEY_OCF 0x0D +#define HCI_W_PAGE_TIMEOUT_OCF 0x18 +#define HCI_W_SCAN_EN_OCF 0x1A +#define HCI_R_COD_OCF 0x23 +#define HCI_W_COD_OCF 0x24 +#define HCI_SET_HC_TO_H_FC_OCF 0x31 +#define HCI_H_BUF_SIZE_OCF 0x33 +#define HCI_H_NUM_COMPL_OCF 0x35 +#define HCI_R_CUR_IACLAP_OCF 0x39 +#define HCI_R_LOC_VERSION_OCF 0x01 +#define HCI_R_LOC_FEATURES_OCF 0x03 +#define HCI_R_BUF_SIZE_OCF 0x05 +#define HCI_R_BD_ADDR_OCF 0x09 +#define HCI_R_REMOTE_NAME_OCF 0x19 +#define HCI_W_PIN_TYPE_OCF 0x0A +#define HCI_W_INQUIRY_SCAN_TYPE_OCF 0x43 +#define HCI_W_INQUIRY_MODE_OCF 0x45 +#define HCI_W_PAGE_SCAN_TYPE_OCF 0x47 +#define HCI_W_PAGE_SCAN_TYPE_OCF 0x47 + +/* Command packet length (including ACL header)*/ +#define HCI_INQUIRY_PLEN 9 +#define HCI_PERIODIC_INQUIRY_PLEN 13 +#define HCI_EXIT_PERIODIC_INQUIRY_PLEN 4 +#define HCI_CREATE_CONN_PLEN 17 +#define HCI_DISCONN_PLEN 7 +#define HCI_REJECT_CONN_REQ_PLEN 11 +#define HCI_ACCEPT_CONN_REQ_PLEN 11 +#define HCI_PIN_CODE_REQ_REP_PLEN 27 +#define HCI_PIN_CODE_REQ_NEG_REP_PLEN 10 +#define HCI_LINK_KEY_REQ_REP_PLEN 26 +#define HCI_LINK_KEY_REQ_REP_NEG_PLEN 10 +#define HCI_SET_CONN_ENCRYPT_PLEN 7 +#define HCI_WRITE_STORED_LINK_KEY_PLEN 27 +#define HCI_SET_EV_MASK_PLEN 12 +#define HCI_SNIFF_PLEN 14 +#define HCI_W_LINK_POLICY_PLEN 8 +#define HCI_RESET_PLEN 4 +#define HCI_SET_EV_FILTER_PLEN 6 +#define HCI_W_PAGE_TIMEOUT_PLEN 6 +#define HCI_W_SCAN_EN_PLEN 5 +#define HCI_R_COD_PLEN 4 +#define HCI_W_COD_PLEN 7 +#define HCI_W_FLUSHTO_PLEN 8 +#define HCI_W_LOCAL_NAME_PLEN 252 +#define HCI_SET_HC_TO_H_FC_PLEN 5 +#define HCI_H_BUF_SIZE_PLEN 11 +#define HCI_H_NUM_COMPL_PLEN 9 +#define HCI_R_LOC_FEAT_SIZE_PLEN 4 +#define HCI_R_LOC_VERS_SIZE_PLEN 4 +#define HCI_R_BUF_SIZE_PLEN 4 +#define HCI_R_BD_ADDR_PLEN 4 +#define HCI_R_CUR_IACLAP_PLEN 4 +#define HCI_R_STORED_LINK_KEY_PLEN 11 +#define HCI_R_REMOTE_NAME_PLEN 14 +#define HCI_W_PIN_TYPE_PLEN 5 +#define HCI_W_INQUIRY_MODE_PLEN 5 +#define HCI_W_INQUIRY_SCAN_TYPE_PLEN 5 +#define HCI_W_PAGE_SCAN_TYPE_PLEN 5 +#define HCI_W_VENDOR_CMD_PLEN 4 + +struct hci_evt_hdr +{ + u8_t code; + u8_t len; +} ATTRIBUTE_PACKED; + +struct hci_acl_hdr +{ + u16_t connhdl_pb_bc; + u16_t len; +} ATTRIBUTE_PACKED; + +struct hci_inq_res +{ + struct hci_inq_res *next; + struct bd_addr bdaddr; + u8_t cod[3]; + u8_t psrm; + u8_t psm; + u16_t co; +}; + +struct hci_link_key +{ + struct bd_addr bdaddr; + u8_t key[16]; + + struct hci_link_key *next; +}; + +struct hci_link +{ + struct hci_link *next; + struct bd_addr bdaddr; + u16_t connhdl; + struct pbuf *p; + u16_t len; + u8_t pb; + u32_t link_mode; +}; + +struct hci_version_info +{ + u8_t hci_version; + u16_t hci_revision; + u8_t lmp_version; + u16_t manufacturer; + u16_t lmp_subversion; +}; + +struct hci_pcb +{ + void *cbarg; + s8_t num_cmd; + + u16_t acl_mtu; + u8_t sco_mtu; + u16_t acl_max_pkt; + u16_t sco_max_pkt; + + u32_t flags; + + u8_t flow; + u16_t host_num_acl; + + u16_t pkt_type; + u8_t features[8]; + + struct bd_addr bdaddr; + struct hci_version_info info; + + struct hci_inq_res *ires; + struct hci_link_key *keyres; + + err_t (*pin_req)(void *arg,struct bd_addr *bdaddr); + err_t (*link_key_req)(void *arg,struct bd_addr *bdaddr); + err_t (*cmd_complete)(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result); + err_t (*link_key_not)(void *arg, struct bd_addr *bdaddr, u8_t *key); + err_t (*conn_complete)(void *arg,struct bd_addr *bdaddr); + err_t (*inq_complete)(void *arg,struct hci_pcb *pcb,struct hci_inq_res *ires,u16_t result); + err_t (*wlp_complete)(void *arg, struct bd_addr *bdaddr); + err_t (*conn_req)(void *arg,struct bd_addr *bdaddr,u8_t *cod,u8_t link_type); +}; + +err_t hci_init(void); +struct hci_link* hci_new(void); +void hci_reset_all(void); +void hci_event_handler(struct pbuf *p); +void hci_acldata_handler(struct pbuf *p); + +err_t hci_reset(); +err_t hci_read_bd_addr(void); +err_t hci_set_hc_to_h_fc(void); +err_t hci_read_buffer_size(void); +err_t hci_host_buffer_size(void); +err_t hci_read_current_lap(void); +err_t hci_write_cod(u8_t *cod); +err_t hci_close(struct hci_link *link); +err_t hci_write_inquiry_mode(u8_t mode); +err_t hci_write_page_scan_type(u8_t type); +err_t hci_write_inquiry_scan_type(u8_t type); +err_t hci_disconnect(struct bd_addr *bdaddr, u8_t reason); +err_t hci_reject_connection_request(struct bd_addr *bdaddr, u8_t reason); +err_t hci_pin_code_request_reply(struct bd_addr *bdaddr, u8_t pinlen, u8_t *pincode); +err_t hci_link_key_req_reply(struct bd_addr *bdaddr, u8_t *link_key); +err_t hci_write_stored_link_key(struct bd_addr *bdaddr, u8_t *link); +err_t hci_set_event_filter(u8_t filter_type,u8_t filter_cond_type,u8_t *cond); +err_t hci_write_page_timeout(u16_t timeout); +err_t hci_inquiry(u32_t lap,u8_t inq_len,u8_t num_resp,err_t (*inq_complete)(void *arg,struct hci_pcb *pcb,struct hci_inq_res *ires,u16_t result)); +err_t hci_pin_code_request_neg_reply(struct bd_addr *bdaddr); +err_t hci_link_key_req_neg_reply(struct bd_addr *bdaddr); +err_t hci_write_scan_enable(u8_t scan_enable); +err_t hci_host_num_comp_packets(u16_t conhdl, u16_t num_complete); +err_t hci_sniff_mode(struct bd_addr *bdaddr, u16_t max_interval, u16_t min_interval, u16_t attempt, u16_t timeout); +err_t hci_write_link_policy_settings(struct bd_addr *bdaddr, u16_t link_policy); +err_t hci_periodic_inquiry(u32_t lap,u16_t min_period,u16_t max_period,u8_t inq_len,u8_t num_resp,err_t (*inq_complete)(void *arg,struct hci_pcb *pcb,struct hci_inq_res *ires,u16_t result)); +err_t hci_exit_periodic_inquiry(); +err_t hci_accecpt_conn_request(struct bd_addr *bdaddr,u8_t role); +err_t hci_set_event_mask(u64_t ev_mask); +err_t hci_read_local_version(void); +err_t hci_read_local_features(void); +err_t hci_write_local_name(u8_t *name,u8_t len); +err_t hci_write_pin_type(u8_t type); +err_t hci_read_stored_link_key(); +err_t hci_read_remote_name(struct bd_addr *bdaddr); +err_t hci_vendor_specific_command(u8_t ocf,u8_t ogf,void *data,u8_t len); + +err_t hci_reg_dev_info(struct bd_addr *bdaddr,u8_t *cod,u8_t psrm,u8_t psm,u16_t co); + +void hci_arg(void *arg); +void hci_cmd_complete(err_t (*cmd_complete)(void *arg,struct hci_pcb *pcb,u8_t ogf,u8_t ocf,u8_t result)); +void hci_connection_complete(err_t (* conn_complete)(void *arg, struct bd_addr *bdaddr)); +void hci_pin_req(err_t (* pin_req)(void *arg, struct bd_addr *bdaddr)); +void hci_link_key_req(err_t (* link_key_req)(void *arg, struct bd_addr *bdaddr)); +void hci_link_key_not(err_t (* link_key_not)(void *arg, struct bd_addr *bdaddr, u8_t *key)); +void hci_wlp_complete(err_t (* wlp_complete)(void *arg, struct bd_addr *bdaddr)); +void hci_conn_req(err_t (*conn_req)(void *arg,struct bd_addr *bdaddr,u8_t *cod,u8_t link_type)); + +u16_t lp_pdu_maxsize(); +u8_t lp_is_connected(struct bd_addr *bdaddr); +err_t lp_acl_write(struct bd_addr *bdaddr,struct pbuf *p,u16_t len,u8_t pb); +err_t lp_connect_req(struct bd_addr *bdaddr, u8_t allow_role_switch); +err_t lp_write_flush_timeout(struct bd_addr *bdaddr, u16_t flushto); + + +#define HCI_EVENT_PIN_REQ(pcb,bdaddr,ret) \ + if((pcb)->pin_req != NULL) { \ + (ret = (pcb)->pin_req((pcb)->cbarg,(bdaddr))); \ + } else { \ + ret = hci_pin_code_request_neg_reply(bdaddr); \ + } +#define HCI_EVENT_LINK_KEY_REQ(pcb,bdaddr,ret) \ + if((pcb)->link_key_req != NULL) { \ + (ret = (pcb)->link_key_req((pcb)->cbarg,(bdaddr))); \ + } else { \ + ret = hci_link_key_req_neg_reply(bdaddr); \ + } +#define HCI_EVENT_CONN_REQ(pcb,bdaddr,cod,linktype,ret) \ + if((pcb)->conn_req!=NULL) \ + (ret = (pcb)->conn_req((pcb)->cbarg,(bdaddr),(cod),(linktype))) +#define HCI_EVENT_LINK_KEY_NOT(pcb,bdaddr,key,ret) \ + if((pcb)->link_key_not != NULL) { \ + (ret = (pcb)->link_key_not((pcb)->cbarg,(bdaddr),(key))); \ + } +#define HCI_EVENT_INQ_COMPLETE(pcb,result,ret) \ + if((pcb)->inq_complete != NULL) \ + (ret = (pcb)->inq_complete((pcb)->cbarg,(pcb),(pcb)->ires,(result))) +#define HCI_EVENT_WLP_COMPLETE(pcb,bdaddr,ret) \ + if((pcb)->wlp_complete != NULL) \ + (ret = (pcb)->wlp_complete((pcb)->cbarg,(bdaddr))); +#define HCI_EVENT_CONN_COMPLETE(pcb,bdaddr,ret) \ + if((pcb)->conn_complete != NULL) \ + (ret = (pcb)->conn_complete((pcb)->cbarg,(bdaddr))); +#define HCI_EVENT_CMD_COMPLETE(pcb,ogf,ocf,result,ret) \ + if((pcb)->cmd_complete != NULL) \ + (ret = (pcb)->cmd_complete((pcb)->cbarg,(pcb),(ogf),(ocf),(result))) + +/* The HCI LINK lists. */ +extern struct hci_link *hci_active_links; /* List of all active HCI LINKs */ +extern struct hci_link *hci_tmp_link; /* Only used for temporary storage. */ +extern struct hci_link_key *hci_tmp_key; + +#define HCI_REG(links, nlink) do { \ + u32 level; \ + _CPU_ISR_Disable(level); \ + nlink->next = *links; \ + *links = nlink; \ + _CPU_ISR_Restore(level); \ + } while(0) +#define HCI_RMV(links, nlink) do { \ + u32 level; \ + _CPU_ISR_Disable(level);\ + if(*links == nlink) { \ + *links = (*links)->next; \ + } else for(hci_tmp_link = *links; hci_tmp_link != NULL; hci_tmp_link = hci_tmp_link->next) { \ + if(hci_tmp_link->next != NULL && hci_tmp_link->next == nlink) { \ + hci_tmp_link->next = nlink->next; \ + break; \ + } \ + } \ + nlink->next = NULL; \ + _CPU_ISR_Restore(level); \ + } while(0) + +#endif diff --git a/wii/libogc/lwbt/l2cap.c b/wii/libogc/lwbt/l2cap.c new file mode 100644 index 0000000000..0496834569 --- /dev/null +++ b/wii/libogc/lwbt/l2cap.c @@ -0,0 +1,1555 @@ +#include +#include +#include +#include +#include +#include + +#include "hci.h" +#include "l2cap.h" +#include "btmemb.h" +#include "btpbuf.h" + +/* Next Identifier to be sent */ +u8_t sigid_nxt; + +/* The L2CAP PCB lists. */ +struct l2cap_pcb_listen *l2cap_listen_pcbs = NULL; /* List of all L2CAP PCBs in CLOSED state + but awaiting an incoming conn req */ +struct l2cap_pcb *l2cap_active_pcbs; /* List of all L2CAP PCBs that are in a + state in which they accept or send + data */ +struct l2cap_pcb *l2cap_tmp_pcb = NULL; + +/* Temp signal */ +struct l2cap_sig *l2cap_tmp_sig = NULL; + +/* Global variable involved in input processing of l2cap data segements */ +struct l2cap_seg *l2cap_insegs = NULL; +struct l2cap_seg *l2cap_tmp_inseg = NULL; + +/* Global Baseband disconnect callback. */ +static void (*l2cap_disconnect_bb_cb)(struct bd_addr *bdaddr,u8_t reason) = NULL; + +/* Forward declarations */ +static u16_t l2cap_cid_alloc(void); + +MEMB(l2cap_pcbs,sizeof(struct l2cap_pcb),MEMB_NUM_L2CAP_PCB); +MEMB(l2cap_listenpcbs,sizeof(struct l2cap_pcb_listen),MEMB_NUM_L2CAP_PCB_LISTEN); +MEMB(l2cap_sigs,sizeof(struct l2cap_sig),MEMB_NUM_L2CAP_SIG); +MEMB(l2cap_segs,sizeof(struct l2cap_seg),MEMB_NUM_L2CAP_SEG); + +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_init(): + * + * Initializes the L2CAP layer. + */ +/*-----------------------------------------------------------------------------------*/ +void l2cap_init() +{ + btmemb_init(&l2cap_pcbs); + btmemb_init(&l2cap_listenpcbs); + btmemb_init(&l2cap_sigs); + btmemb_init(&l2cap_segs); + + /* Clear globals */ + l2cap_listen_pcbs = NULL; + l2cap_active_pcbs = NULL; + l2cap_tmp_pcb = NULL; + l2cap_tmp_sig = NULL; + l2cap_insegs = NULL; + l2cap_tmp_inseg = NULL; + l2cap_disconnect_bb_cb = NULL; + + /* Initialize the signal identifier (0x00 shall never be used) */ + sigid_nxt = 0x00; +} + +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_tmr(): + * + * Called every 1s and implements the retransmission timer that + * removes a channel if it has been waiting for a request enough + * time. It also includes a configuration timer. + */ +/*-----------------------------------------------------------------------------------*/ +void l2cap_tmr() +{ + struct l2cap_sig *sig; + struct l2cap_pcb *pcb; + err_t ret; + + (void) ret; + + /* Step through all of the active pcbs */ + for(pcb = l2cap_active_pcbs; pcb != NULL; pcb = pcb->next) { + /* Step through any unresponded signals */ + for(sig = pcb->unrsp_sigs; sig != NULL; sig = sig->next) { + /* Check if channel is not reliable */ + if(pcb->cfg.outflushto < 0xFFFF) { + /* Check if rtx is active. Otherwise ertx is active */ + if(sig->rtx > 0) { + /* Adjust rtx timer */ + --sig->rtx; + /* Check if rtx has expired */ + if(sig->rtx == 0) { + if(sig->nrtx == 0) { + /* Move pcb to closed state */ + pcb->state = L2CAP_CLOSED; + /* Indicate disconnect to upper layer */ + LOG("l2cap_tmr: Max number of retransmissions (rtx) has expired\n"); + L2CA_ACTION_DISCONN_IND(pcb,ERR_OK,ret); + } else { + --sig->nrtx; + /* Indicate timeout to upper layer */ + L2CA_ACTION_TO_IND(pcb,ERR_OK,ret); + /* Retransmitt signal w timeout doubled */ + sig->rtx += sig->rtx; + ret = l2cap_rexmit_signal(pcb, sig); + } + } /* if */ + } else { + /* Adjust ertx timer */ + --sig->ertx; + /* Check if ertx has expired */ + if(sig->ertx == 0) { + if(sig->nrtx == 0) { + /* Move pcb to closed state */ + pcb->state = L2CAP_CLOSED; + /* Indicate disconnect to upper layer */ + LOG("l2cap_tmr: Max number of retransmissions (ertx) has expired\n"); + L2CA_ACTION_DISCONN_IND(pcb,ERR_OK,ret); + } else { + --sig->nrtx; + /* Indicate timeout to upper layer */ + L2CA_ACTION_TO_IND(pcb,ERR_OK,ret); + /* Disable ertx, activate rtx and retransmitt signal */ + sig->ertx = 0; + sig->rtx = L2CAP_RTX; + ret = l2cap_rexmit_signal(pcb, sig); + } + } /* if */ + } /* else */ + } /* if */ + } /* for */ + + /* Check configuration timer */ + if(pcb->state == L2CAP_CONFIG) { + /* Check if configuration timer is active */ + if(pcb->cfg.cfgto > 0) { + --pcb->cfg.cfgto; + //LOG("l2cap_tmr: Configuration timer = %d\n", pcb->cfg.cfgto); + /* Check if config timer has expired */ + if(pcb->cfg.cfgto == 0) { + /* Connection attempt failed. Disconnect */ + l2ca_disconnect_req(pcb, NULL); + /* Notify the application that the connection attempt failed */ + if(pcb->cfg.l2capcfg & L2CAP_CFG_IR) { + L2CA_ACTION_CONN_CFM(pcb, L2CAP_CONN_CFG_TO, 0x0000, ret); + } else { + L2CA_ACTION_CONN_IND(pcb, ERR_OK, ret); + } + pcb->cfg.cfgto = L2CAP_CFG_TO; /* Reset timer */ + } + } + } + } /* for */ +} + +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_write(): + * + * Output L2CAP data to the lower layers. Segments the packet in to PDUs. + */ +/*-----------------------------------------------------------------------------------*/ +err_t l2cap_write(struct bd_addr *bdaddr, struct pbuf *p, u16_t len) +{ + u8_t pb = L2CAP_ACL_START; + u16_t maxsize; + u16_t outsize; + err_t ret = ERR_OK; + struct pbuf *q; + u16_t i = 0; + + /*u16_t i; + struct pbuf *q; + for(q = p; q != NULL; q = q->next) { + for(i = 0; i < q->len; ++i) { + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_write: 0x%x\n", ((u8_t *)q->payload)[i])); + } + LWIP_DEBUGF(L2CAP_DEBUG, ("l2cap_write: *\n")); + } + */ + + maxsize = lp_pdu_maxsize(); + q = p; + + while(len && ret == ERR_OK) { + //LOG("l2cap_write: len %d maxsize %d p->len %d\n", len, maxsize, p->len); + if(len > maxsize) { + ret = lp_acl_write(bdaddr, q, maxsize, pb); + len -= maxsize; + outsize = maxsize; + //LOG("l2cap_write: Outsize before %d\n", outsize); + while(q->len < outsize) { + outsize -= q->len; + q = q->next; + } + //LOG("l2cap_write: Outsize after %d\n", outsize); + if(outsize) { + btpbuf_header(q, -outsize); + i += outsize; + } + pb = L2CAP_ACL_CONT; + LOG("l2cap_write: FRAG\n"); + } else { + ret = lp_acl_write(bdaddr, q, len, pb); + len = 0; + } + } + btpbuf_header(q, i); + LOG("l2cap_write: DONE\n"); + return ret; +} + +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_process_sig(): + * + * Parses the received message handles it. + */ +/*-----------------------------------------------------------------------------------*/ +void l2cap_process_sig(struct pbuf *q, struct l2cap_hdr *l2caphdr, struct bd_addr *bdaddr) +{ + struct l2cap_sig_hdr *sighdr; + struct l2cap_sig *sig = NULL; + struct l2cap_pcb *pcb = NULL; + struct l2cap_pcb_listen *lpcb; + struct l2cap_cfgopt_hdr *opthdr; + u16_t result, status, flags, psm, dcid; + u16_t len; + u16_t siglen; + struct pbuf *p, *r = NULL, *s = NULL, *data; + err_t ret; + u8_t i; + u16_t rspstate = L2CAP_CFG_SUCCESS; + + (void)ret; + + if(q->len != q->tot_len) { + LOG("l2cap_process_sig: Fragmented packet received. Reassemble into one buffer\n"); + if((p = btpbuf_alloc(PBUF_RAW, q->tot_len, PBUF_RAM)) != NULL) { + i = 0; + for(r = q; r != NULL; r = r->next) { + memcpy(((u8_t *)p->payload) + i, r->payload, r->len); + i += r->len; + } + } else { + ERROR("l2cap_process_sig: Could not allocate buffer for fragmented packet\n"); + return; + } + } else { + p = q; + } + + len = l2caphdr->len; + + while(len > 0) { + /* Set up signal header */ + sighdr = p->payload; + btpbuf_header(p, -L2CAP_SIGHDR_LEN); + + /* Check if this is a response/reject signal, and if so, find the matching request */ + if(sighdr->code % 2) { /* if odd this is a resp/rej signal */ + LOG("l2cap_process_sig: Response/reject signal received id = %d code = %d\n", sighdr->id, sighdr->code); + for(pcb = l2cap_active_pcbs; pcb != NULL; pcb = pcb->next) { + for(sig = pcb->unrsp_sigs; sig != NULL; sig = sig->next) { + if(sig->sigid == sighdr->id) { + break; /* found */ + } + } + if(sig != NULL) { + break; + } + } + } else { + LOG("l2cap_process_sig: Request signal received id = %d code = %d\n", sighdr->id, sighdr->code); + } + + /* Reject packet if length exceeds MTU */ + if(l2caphdr->len > L2CAP_MTU) { + /* Alloc size of reason in cmd rej + MTU */ + if((data = btpbuf_alloc(PBUF_RAW, L2CAP_CMD_REJ_SIZE+2, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = htole16(L2CAP_MTU_EXCEEDED); + ((u16_t *)data->payload)[1] = htole16(L2CAP_MTU); + + l2cap_signal(NULL, L2CAP_CMD_REJ, sighdr->id, bdaddr, data); + } + break; + } + + switch(sighdr->code) { + case L2CAP_CMD_REJ: + /* Remove signal from unresponded list and deallocate it */ + L2CAP_SIG_RMV(&(pcb->unrsp_sigs), sig); + btpbuf_free(sig->p); + btmemb_free(&l2cap_sigs, sig); + LOG("l2cap_process_sig: Our command was rejected so we disconnect\n"); + l2ca_disconnect_req(pcb, NULL); + break; + case L2CAP_CONN_REQ: + psm = le16toh(((u16_t *)p->payload)[0]); + /* Search for a listening pcb */ + for(lpcb = l2cap_listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if(bd_addr_cmp(&(lpcb->bdaddr),bdaddr) && lpcb->psm == psm) { + /* Found a listening pcb with the correct PSM & BD Address */ + break; + } + } + + //printf("l2cap_process_sig(L2CAP_CONN_REQ): psm = %04x, lpcb = %p\n",psm,lpcb); + /* If no matching pcb was found, send a connection rsp neg (PSM) */ + if(lpcb == NULL) { + /* Alloc size of data in conn rsp signal */ + if((data = btpbuf_alloc(PBUF_RAW, L2CAP_CONN_RSP_SIZE, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = htole16(L2CAP_CONN_REF_PSM); + ((u16_t *)data->payload)[1] = 0; /* No further info available */ + ret = l2cap_signal(NULL, L2CAP_CONN_RSP, sighdr->id, bdaddr, data); + } + } else { + /* Initiate a new active pcb */ + pcb = l2cap_new(); + if(pcb == NULL) { + LOG("l2cap_process_sig: could not allocate PCB\n"); + /* Send a connection rsp neg (no resources available) and alloc size of data in conn rsp + signal */ + if((data = btpbuf_alloc(PBUF_RAW, L2CAP_CONN_RSP_SIZE, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = htole16(L2CAP_CONN_REF_RES); + ((u16_t *)data->payload)[1] = 0; /* No further info available */ + ret = l2cap_signal(NULL, L2CAP_CONN_RSP, sighdr->id, bdaddr, data); + } + } + bd_addr_set(&(pcb->remote_bdaddr),bdaddr); + + pcb->scid = l2cap_cid_alloc(); + pcb->dcid = le16toh(((u16_t *)p->payload)[1]); + pcb->psm = psm; + pcb->callback_arg = lpcb->callback_arg; + pcb->l2ca_connect_ind = lpcb->l2ca_connect_ind; + + pcb->state = L2CAP_CONFIG; + L2CAP_REG(&l2cap_active_pcbs, pcb); + + LOG("l2cap_process_sig: A connection request was received. Send a response\n"); + data = btpbuf_alloc(PBUF_RAW, L2CAP_CONN_RSP_SIZE, PBUF_RAM); + if(data == NULL) { + ERROR("l2cap_connect_rsp: Could not allocate memory for pbuf\n"); + break; + } + ((u16_t *)data->payload)[0] = htole16(pcb->scid); + ((u16_t *)data->payload)[1] = htole16(pcb->dcid); + ((u16_t *)data->payload)[2] = htole16(L2CAP_CONN_SUCCESS); + ((u16_t *)data->payload)[3] = 0x0000; /* No further information available */ + + /* Send the response */ + ret = l2cap_signal(pcb, L2CAP_CONN_RSP, sighdr->id, &(pcb->remote_bdaddr), data); + } + break; + case L2CAP_CONN_RSP: + if(pcb == NULL) { + /* A response without a matching request is silently discarded */ + break; + } + LOG("l2cap_process_sig: conn rsp, active pcb->state == W4_L2CAP_CONNECT_RSP\n"); + result = le16toh(((u16_t *)p->payload)[2]); + status = le16toh(((u16_t *)p->payload)[3]); + switch(result) { + case L2CAP_CONN_SUCCESS: + LOG("l2cap_process_sig: Conn_rsp_sucess, status %d\n", status); + LOG("l2cap_process_sig: conn rsp success, pcb->scid == %04x\n", ((u16_t *)p->payload)[1]); + + /* Set destination connection id */ + pcb->dcid = le16toh(((u16_t *)p->payload)[0]); + + /* Remove signal from unresponded list and deallocate it */ + L2CAP_SIG_RMV(&(pcb->unrsp_sigs), sig); + btpbuf_free(sig->p); + btmemb_free(&l2cap_sigs, sig); + + /* Configure connection */ + pcb->state = L2CAP_CONFIG; + + /* If initiator send a configuration request */ + if(pcb->cfg.l2capcfg & L2CAP_CFG_IR) { + l2ca_config_req(pcb); + pcb->cfg.l2capcfg |= L2CAP_CFG_OUT_REQ; + } + break; + case L2CAP_CONN_PND: + LOG("l2cap_process_sig: Conn_rsp_pnd, status %d\n", status); + + /* Disable rtx and enable ertx */ + sig->rtx = 0; + sig->ertx = L2CAP_ERTX; + break; + default: + LOG("l2cap_process_sig: Conn_rsp_neg, result %d\n", result); + /* Remove signal from unresponded list and deallocate it */ + L2CAP_SIG_RMV(&(pcb->unrsp_sigs), sig); + btpbuf_free(sig->p); + btmemb_free(&l2cap_sigs, sig); + + L2CA_ACTION_CONN_CFM(pcb,result,status,ret); + break; + } + break; + case L2CAP_CFG_REQ: + siglen = le16toh(sighdr->len); + dcid = le16toh(((u16_t *)p->payload)[0]); + flags = le16toh(((u16_t *)p->payload)[1]); + siglen -= 4; + btpbuf_header(p, -4); + + + LOG("l2cap_process_sig: Congfiguration request, flags = %d\n", flags); + + /* Find PCB with matching cid */ + for(pcb = l2cap_active_pcbs; pcb != NULL; pcb = pcb->next) { + //LOG("l2cap_process_sig: dcid = 0x%x, pcb->scid = 0x%x, pcb->dcid = 0x%x\n\n", dcid, pcb->scid, pcb->dcid); + if(pcb->scid == dcid) { + /* Matching cid found */ + break; + } + } + /* If no matching cid was found, send a cmd reject (Invalid cid) */ + if(pcb == NULL) { + LOG("l2cap_process_sig: Cfg req: no matching cid was found\n"); + /* Alloc size of reason in cmd rej + data (dcid + scid) */ + if((data = btpbuf_alloc(PBUF_RAW, L2CAP_CMD_REJ_SIZE+4, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = htole16(L2CAP_INVALID_CID); + ((u16_t *)data->payload)[1] = htole16(dcid); /* Requested local cid */ + ((u16_t *)data->payload)[2] = htole16(L2CAP_NULL_CID); /* Remote cid not known */ + + ret = l2cap_signal(NULL, L2CAP_CMD_REJ, sighdr->id, bdaddr, data); + } + } else { /* Handle config request */ + LOG("l2cap_process_sig: Handle configuration request\n"); + pcb->ursp_id = sighdr->id; /* Set id of request to respond to */ + + /* Parse options and add to pcb */ + while(siglen > 0) { + LOG("l2cap_process_sig: Siglen = %d\n", siglen); + opthdr = p->payload; + /* Check if type of action bit indicates a non-hint. Hints are ignored */ + LOG("l2cap_process_sig: Type of action bit = %d\n", L2CAP_OPTH_TOA(opthdr)); + if(L2CAP_OPTH_TOA(opthdr) == 0) { + LOG("l2cap_process_sig: Type = %d\n", L2CAP_OPTH_TYPE(opthdr)); + LOG("l2cap_process_sig: Length = %d\n", opthdr->len); + switch(L2CAP_OPTH_TYPE(opthdr)) { + case L2CAP_CFG_MTU: + LOG("l2cap_process_sig: Out MTU = %d\n", le16toh(((u16_t *)p->payload)[1])); + pcb->cfg.outmtu = le16toh(((u16_t *)p->payload)[1]); + break; + case L2CAP_FLUSHTO: + LOG("l2cap_process_sig: In flush timeout = %d\n", ((u16_t *)p->payload)[1]); + pcb->cfg.influshto = le16toh(((u16_t *)p->payload)[1]); + break; + case L2CAP_QOS: + /* If service type is Best Effort or No Traffic the remainder fields will be ignored */ + if(((u8_t *)p->payload)[3] == L2CAP_QOS_GUARANTEED) { + /*LOG("l2cap_process_sig: This implementation does not support the guaranteed QOS service type"); + if(rspstate == L2CAP_CFG_SUCCESS) { + rspstate = L2CAP_CFG_UNACCEPT; + if(pcb->cfg.opt != NULL) { + btpbuf_free(pcb->cfg.opt); + pcb->cfg.opt = NULL; + } + }*/ + s = btpbuf_alloc(PBUF_RAW, L2CAP_CFGOPTHDR_LEN + opthdr->len, PBUF_RAM); + memcpy((u8_t *)s->payload, (u8_t *)p->payload, L2CAP_CFGOPTHDR_LEN + opthdr->len); + if(pcb->cfg.opt == NULL) { + pcb->cfg.opt = s; + } else { + btpbuf_chain(pcb->cfg.opt, s); + btpbuf_free(s); + } + } + break; + default: + if(rspstate != L2CAP_CFG_REJ) { + /* Unknown option. Add to unknown option type buffer */ + if(rspstate != L2CAP_CFG_UNKNOWN) { + rspstate = L2CAP_CFG_UNKNOWN; + if(pcb->cfg.opt != NULL) { + btpbuf_free(pcb->cfg.opt); + pcb->cfg.opt = NULL; + } + } + s = btpbuf_alloc(PBUF_RAW, L2CAP_CFGOPTHDR_LEN + opthdr->len, PBUF_RAM); + memcpy((u8_t *)s->payload, (u8_t *)p->payload, L2CAP_CFGOPTHDR_LEN + opthdr->len); + if(pcb->cfg.opt == NULL) { + pcb->cfg.opt = s; + } else { + btpbuf_chain(pcb->cfg.opt, s); + btpbuf_free(s); + } + } + break; + } /* switch */ + } /* if(L2CAP_OPTH_TOA(opthdr) == 0) */ + btpbuf_header(p, -(L2CAP_CFGOPTHDR_LEN + opthdr->len)); + siglen -= L2CAP_CFGOPTHDR_LEN + opthdr->len; + } /* while */ + + /* If continuation flag is set we don't send the final response just yet */ + if((flags & 0x0001) == 1) { + /* Send success result with no options until the full request has been received */ + if((data = btpbuf_alloc(PBUF_RAW, L2CAP_CFG_RSP_SIZE, PBUF_RAM)) == NULL) { + ERROR("l2cap_process_sig: Could not allocate memory for pbuf\n"); + break; + } + ((u16_t *)data->payload)[0] = htole16(pcb->dcid); + ((u16_t *)data->payload)[1] = 0; + ((u16_t *)data->payload)[2] = htole16(L2CAP_CFG_SUCCESS); + ret = l2cap_signal(pcb, L2CAP_CFG_RSP, pcb->ursp_id, &(pcb->remote_bdaddr), data); + break; + } + + /* Send a configure request for outgoing link if it hasnt been configured */ + if(!(pcb->cfg.l2capcfg & L2CAP_CFG_IR) && !(pcb->cfg.l2capcfg & L2CAP_CFG_OUT_REQ)) { + l2ca_config_req(pcb); + pcb->cfg.l2capcfg |= L2CAP_CFG_OUT_REQ; + } + + /* Send response to configuration request */ + LOG("l2cap_process_sig: Send response to configuration request\n"); + if((data = btpbuf_alloc(PBUF_RAW, L2CAP_CFG_RSP_SIZE, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = htole16(pcb->dcid); + ((u16_t *)data->payload)[1] = 0; /* Flags (No continuation) */ + ((u16_t *)data->payload)[2] = htole16(rspstate); /* Result */ + if(pcb->cfg.opt != NULL) { + LOG("l2cap_process_sig: pcb->cfg.opt->len = %d\n", pcb->cfg.opt->len); + btpbuf_chain(data, pcb->cfg.opt); /* Add option type buffer to data buffer */ + btpbuf_free(pcb->cfg.opt); + pcb->cfg.opt = NULL; + } + ret = l2cap_signal(pcb, L2CAP_CFG_RSP, pcb->ursp_id, &(pcb->remote_bdaddr), data); + } + + if(rspstate == L2CAP_CFG_SUCCESS) { + pcb->cfg.l2capcfg |= L2CAP_CFG_OUT_SUCCESS; + /* L2CAP connection established if a successful configuration response has been sent */ + if(pcb->cfg.l2capcfg & L2CAP_CFG_IN_SUCCESS) { + /* IPCP connection established, notify upper layer that connection is open */ + pcb->state = L2CAP_OPEN; + if(pcb->cfg.l2capcfg & L2CAP_CFG_IR) { + L2CA_ACTION_CONN_CFM(pcb, L2CAP_CONN_SUCCESS, 0x0000, ret); + } else { + L2CA_ACTION_CONN_IND(pcb, ERR_OK, ret); + } + } + } + } /* else */ + break; + case L2CAP_CFG_RSP: + if(pcb == NULL) { + /* A response without a matching request is silently discarded */ + LOG("l2cap_process_sig: discarded response without matching request\n"); + break; + } + + /* Remove signal from unresponded list and deallocate it */ + L2CAP_SIG_RMV(&(pcb->unrsp_sigs), sig); + btpbuf_free(sig->p); + btmemb_free(&l2cap_sigs, sig); + + siglen = le16toh(sighdr->len); + //scid = le16toh(((u16_t *)p->payload)[0]); + flags = le16toh(((u16_t *)p->payload)[1]); + result = le16toh(((u16_t *)p->payload)[2]); + siglen -= 6; + btpbuf_header(p, -6); + + LOG("l2cap_process_sig: Outgoing configuration result == %d continuation flag == %d\n", result, flags); + + /* Handle config request */ + switch(result) { + case L2CAP_CFG_SUCCESS: + LOG("l2cap_process_sig: Successfull outgoing configuration\n"); + pcb->cfg.l2capcfg |= L2CAP_CFG_IN_SUCCESS; /* Local side of the connection + has been configured for outgoing data */ + pcb->cfg.cfgto = L2CAP_CFG_TO; /* Reset configuration timeout */ + + if(pcb->cfg.outflushto != L2CAP_CFG_DEFAULT_OUTFLUSHTO) { + lp_write_flush_timeout(&pcb->remote_bdaddr, pcb->cfg.outflushto); + } + + /* L2CAP connection established if a successful configuration response has been sent */ + if(pcb->cfg.l2capcfg & L2CAP_CFG_OUT_SUCCESS) { + pcb->state = L2CAP_OPEN; + if(pcb->cfg.l2capcfg & L2CAP_CFG_IR) { + L2CA_ACTION_CONN_CFM(pcb, L2CAP_CONN_SUCCESS, 0x0000, ret); + } else { + L2CA_ACTION_CONN_IND(pcb, ERR_OK, ret); + } + } + break; + case L2CAP_CFG_UNACCEPT: + /* Parse and add options to pcb */ + while(siglen > 0) { + opthdr = p->payload; + /* Check if type of action bit indicates a non-hint. Hints are ignored */ + if(L2CAP_OPTH_TOA(opthdr) == 0) { + switch(L2CAP_OPTH_TYPE(opthdr)) { + case L2CAP_CFG_MTU: + if(L2CAP_MTU > le16toh(((u16_t *)p->payload)[1])) { + pcb->cfg.outmtu = le16toh(((u16_t *)p->payload)[1]); + } else { + ERROR("l2cap_process_sig: Configuration of MTU failed\n"); + l2ca_disconnect_req(pcb, NULL); + return; + } + break; + case L2CAP_FLUSHTO: + pcb->cfg.influshto = le16toh(((u16_t *)p->payload)[1]); + break; + case L2CAP_QOS: + /* If service type Best Effort is not accepted we will close the connection */ + if(((u8_t *)p->payload)[3] != L2CAP_QOS_BEST_EFFORT) { + ERROR("l2cap_process_sig: Unsupported service type\n"); + l2ca_disconnect_req(pcb, NULL); + return; + } + break; + default: + /* Should not happen, skip option */ + break; + } /* switch */ + } /* if(L2CAP_OPTH_TOA(opthdr) == 0) */ + btpbuf_header(p, -(L2CAP_CFGOPTHDR_LEN + opthdr->len)); + siglen -= L2CAP_CFGOPTHDR_LEN + opthdr->len; + } /* while */ + + /* Send out a new configuration request if the continuation flag isn't set */ + if((flags & 0x0001) == 0) { + l2ca_config_req(pcb); + } + break; + case L2CAP_CFG_REJ: + /* Fallthrough */ + case L2CAP_CFG_UNKNOWN: + /* Fallthrough */ + default: + if((flags & 0x0001) == 0) { + LOG("l2cap_process_sig: Configuration failed\n"); + l2ca_disconnect_req(pcb, NULL); + return; + } + break; + } /* switch(result) */ + + /* If continuation flag is set we must send a NULL configuration request */ + if((flags & 0x0001) == 1) { + LOG("l2cap_process_sig: Continuation flag is set. Send empty (default) config request signal\n"); + if((data = btpbuf_alloc(PBUF_RAW, L2CAP_CFG_REQ_SIZE, PBUF_RAM)) == NULL) { + ERROR("l2cap_process_sig: Could not allocate memory for pbuf\n"); + return; + } + /* Assemble config request packet */ + ((u16_t *)data->payload)[0] = htole16(pcb->scid); + ((u16_t *)data->payload)[2] = 0; + l2cap_signal(pcb, L2CAP_CFG_REQ, 0, &(pcb->remote_bdaddr), data); + } + break; + case L2CAP_DISCONN_REQ: + siglen = le16toh(sighdr->len); + dcid = le16toh(((u16_t *)p->payload)[0]); + siglen = siglen - 2; + flags = le16toh(((u16_t *)p->payload)[1]); + siglen = siglen - 2; + btpbuf_header(p, -4); + + /* Find PCB with matching cid */ + for(pcb = l2cap_active_pcbs; pcb != NULL; pcb = pcb->next) { + if(pcb->scid == dcid) { + /* Matching cid found */ + break; + } + } + /* If no matching cid was found, send a cmd reject (Invalid cid) */ + if(pcb == NULL) { + /* Alloc size of reason in cmd rej + data (dcid + scid) */ + if((data = btpbuf_alloc(PBUF_RAW, L2CAP_CMD_REJ_SIZE+4, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = htole16(L2CAP_INVALID_CID); + ((u16_t *)data->payload)[1] = htole16(dcid); /* Requested local cid */ + ((u16_t *)data->payload)[2] = htole16(L2CAP_NULL_CID); /* Remote cid not known */ + + ret = l2cap_signal(NULL, L2CAP_CMD_REJ, sighdr->id, bdaddr, data); + } + } else { /* Handle disconnection request */ + if((data = btpbuf_alloc(PBUF_RAW, L2CAP_DISCONN_RSP_SIZE, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = htole16(pcb->scid); + ((u16_t *)data->payload)[1] = htole16(pcb->dcid); + ret = l2cap_signal(pcb, L2CAP_DISCONN_RSP, sighdr->id, &(pcb->remote_bdaddr), data); + + /* Give upper layer indication */ + pcb->state = L2CAP_CLOSED; + LOG("l2cap_process_sig: Disconnection request\n"); + L2CA_ACTION_DISCONN_IND(pcb,ERR_OK,ret); + } + } + break; + case L2CAP_DISCONN_RSP: + if(pcb == NULL) { + /* A response without a matching request is silently discarded */ + break; + } + /* Remove signal from unresponded list and deallocate it */ + L2CAP_SIG_RMV(&(pcb->unrsp_sigs), sig); + btpbuf_free(sig->p); + btmemb_free(&l2cap_sigs, sig); + + L2CA_ACTION_DISCONN_CFM(pcb,ret); /* NOTE: Application should + now close the connection */ + break; + case L2CAP_ECHO_REQ: + pcb->ursp_id = sighdr->id; + ret = l2cap_signal(pcb, L2CAP_ECHO_RSP, sighdr->id, &(pcb->remote_bdaddr), NULL); + break; + case L2CAP_ECHO_RSP: + if(pcb == NULL) { + /* A response without a matching request is silently discarded */ + break; + } + /* Remove signal from unresponded list and deallocate it */ + L2CAP_SIG_RMV(&(pcb->unrsp_sigs), sig); + btpbuf_free(sig->p); + btmemb_free(&l2cap_sigs, sig); + + /* Remove temporary pcb from active list */ + L2CAP_RMV(&l2cap_active_pcbs, pcb); + L2CA_ACTION_PING_CFM(pcb,L2CAP_ECHO_RCVD,ret); + break; + default: + /* Alloc size of reason in cmd rej */ + if((data = btpbuf_alloc(PBUF_RAW, L2CAP_CMD_REJ_SIZE, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = htole16(L2CAP_CMD_NOT_UNDERSTOOD); + + ret = l2cap_signal(NULL, L2CAP_CMD_REJ, sighdr->id, bdaddr, data); + } + break; + } /* switch */ + len = len - (le16toh(sighdr->len) + L2CAP_SIGHDR_LEN); + btpbuf_header(p, -(le16toh(sighdr->len))); + } /* while */ +} + +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_input(): + * + * Called by the lower layer. Reassembles the packet, parses the header and forward + * it to the upper layer or the signal handler. + */ +/*-----------------------------------------------------------------------------------*/ +void l2cap_input(struct pbuf *p, struct bd_addr *bdaddr) +{ + struct l2cap_seg *inseg; + struct hci_acl_hdr *aclhdr; + struct pbuf *data; + err_t ret; + + (void)ret; + + btpbuf_header(p, HCI_ACL_HDR_LEN); + aclhdr = p->payload; + btpbuf_header(p, -HCI_ACL_HDR_LEN); + + btpbuf_realloc(p, aclhdr->len); + + for(inseg = l2cap_insegs; inseg != NULL; inseg = inseg->next) { + if(bd_addr_cmp(bdaddr, &(inseg->bdaddr))) { + break; + } + } + + aclhdr->connhdl_pb_bc = le16toh(aclhdr->connhdl_pb_bc); + aclhdr->len = le16toh(aclhdr->len); + /* Reassembly procedures */ + /* Check if continuing fragment or start of L2CAP packet */ + if(((aclhdr->connhdl_pb_bc >> 12) & 0x03)== L2CAP_ACL_CONT) { /* Continuing fragment */ + if(inseg == NULL) { + /* Discard packet */ + LOG("l2cap_input: Continuing fragment. Discard packet\n"); + btpbuf_free(p); + return; + } else if(inseg->p->tot_len + p->tot_len > inseg->len) { /* Check if length of + segment exceeds + l2cap header length */ + /* Discard packet */ + LOG("l2cap_input: Continuing fragment. Length exceeds L2CAP hdr length. Discard packet\n"); + btpbuf_free(inseg->p); + L2CAP_SEG_RMV(&(l2cap_insegs), inseg); + btmemb_free(&l2cap_segs, inseg); + + btpbuf_free(p); + return; + } + /* Add pbuf to segement */ + btpbuf_chain(inseg->p, p); + btpbuf_free(p); + + } else if(((aclhdr->connhdl_pb_bc >> 12) & 0x03) == L2CAP_ACL_START) { /* Start of L2CAP packet */ + //LOG("l2cap_input: Start of L2CAP packet p->len = %d, p->tot_len = %d\n", p->len, p->tot_len); + if(inseg != NULL) { /* Check if there are segments missing in a previous packet */ + /* Discard previous packet */ + LOG("l2cap_input: Start of L2CAP packet. Discard previous packet\n"); + btpbuf_free(inseg->p); + } else { + inseg = btmemb_alloc(&l2cap_segs); + bd_addr_set(&(inseg->bdaddr), bdaddr); + L2CAP_SEG_REG(&(l2cap_insegs), inseg); + } + inseg->p = p; + inseg->l2caphdr = p->payload; + inseg->l2caphdr->cid = le16toh(inseg->l2caphdr->cid); + inseg->l2caphdr->len = le16toh(inseg->l2caphdr->len); + + inseg->len = inseg->l2caphdr->len + L2CAP_HDR_LEN; + for(inseg->pcb = l2cap_active_pcbs; inseg->pcb != NULL; inseg->pcb = inseg->pcb->next) { + if(inseg->pcb->scid == inseg->l2caphdr->cid) { + break; /* found */ + } + } + } else { + /* Discard packet */ + LOG("l2cap_input: Discard packet\n"); + btpbuf_free(inseg->p); + L2CAP_SEG_RMV(&(l2cap_insegs), inseg); + btmemb_free(&l2cap_segs, inseg); + + btpbuf_free(p); + return; + } + if(inseg->p->tot_len < inseg->len) { + LOG("l2cap_input: Get continuing segments\n"); + return; /* Get continuing segments */ + } + + /* Handle packet */ + switch(inseg->l2caphdr->cid) { + case L2CAP_NULL_CID: + /* Illegal */ + LOG("l2cap_input: Illegal null cid\n"); + btpbuf_free(inseg->p); + break; + case L2CAP_SIG_CID: + btpbuf_header(inseg->p, -L2CAP_HDR_LEN); + l2cap_process_sig(inseg->p, inseg->l2caphdr, bdaddr); + btpbuf_free(inseg->p); + break; + case L2CAP_CONNLESS_CID: + /* Not needed by PAN, LAN access or DUN profiles */ + btpbuf_free(inseg->p); + break; + default: + if(inseg->l2caphdr->cid < 0x0040 || inseg->pcb == NULL) { + /* Reserved for specific L2CAP functions or channel does not exist */ + /* Alloc size of reason in cmd rej */ + if((data = btpbuf_alloc(PBUF_RAW, L2CAP_CMD_REJ_SIZE+4, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = htole16(L2CAP_INVALID_CID); + ((u16_t *)data->payload)[1] = htole16(inseg->l2caphdr->cid); + ((u16_t *)data->payload)[2] = htole16(L2CAP_NULL_CID); + + ret = l2cap_signal(NULL, L2CAP_CMD_REJ, l2cap_next_sigid(), bdaddr, data); + } + btpbuf_free(inseg->p); + break; + } + + btpbuf_header(inseg->p, -L2CAP_HDR_LEN); + + /* Forward packet to higher layer */ + LOG("l2cap_input: Forward packet to higher layer\n"); + /* + LOG("l2cap_input: Remote BD address: 0x%x:0x%x:0x%x:0x%x:0x%x:0x%x\n", + inseg->pcb->remote_bdaddr.addr[5], + inseg->pcb->remote_bdaddr.addr[4], + inseg->pcb->remote_bdaddr.addr[3], + inseg->pcb->remote_bdaddr.addr[2], + inseg->pcb->remote_bdaddr.addr[1], + inseg->pcb->remote_bdaddr.addr[0])); + */ + L2CA_ACTION_RECV(inseg->pcb,inseg->p,ERR_OK,ret); + break; + } + + /* Remove input segment */ + L2CAP_SEG_RMV(&(l2cap_insegs), inseg); + btmemb_free(&l2cap_segs, inseg); +} + +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_cid_alloc(): + * + * Allocates a channel identifier (CID). They are local names representing a logical + * channel endpoint on the device. + */ +/*-----------------------------------------------------------------------------------*/ +static u16_t l2cap_cid_alloc(void) +{ + u16_t cid; + struct l2cap_pcb *pcb; + + for (cid = L2CAP_MIN_CID; cid < L2CAP_MAX_CID; ++cid) { + for(pcb = l2cap_active_pcbs; pcb != NULL; pcb = pcb->next) { + if(pcb->scid == cid) { + break; + } + } + if(pcb == NULL) { + return cid; + } + } + return 0; +} + +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_new(): + * + * Creates a new L2CAP protocol control block but doesn't place it on + * any of the L2CAP PCB lists. + */ +/*-----------------------------------------------------------------------------------*/ +struct l2cap_pcb* l2cap_new(void) +{ + struct l2cap_pcb *pcb; + + pcb = btmemb_alloc(&l2cap_pcbs); + if(pcb != NULL) { + memset(pcb, 0, sizeof(struct l2cap_pcb)); + pcb->state = L2CAP_CLOSED; + + /* Initialize configuration parameter options with default values */ + + /* Maximum Transmission Unit */ + pcb->cfg.inmtu = L2CAP_MTU; /* The MTU that this implementation support */ + pcb->cfg.outmtu = 672; /* Default MTU. Two Baseband DH5 packets minus the Baseband ACL headers and + L2CAP header. This can be set here since we will never send any signals + larger than the L2CAP sig MTU (48 bytes) before L2CAP has been configured + */ + + /* Flush Timeout */ + pcb->cfg.influshto = 0xFFFF; + pcb->cfg.outflushto = 0xFFFF; + + pcb->cfg.cfgto = L2CAP_CFG_TO; /* Maximum time before terminating a negotiation. + Cfg shall not last more than 120s */ + pcb->cfg.opt = NULL; + return pcb; + } + ERROR("l2cap_new: Could not allocate memory for pcb\n"); + return NULL; +} + +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_close(): + * + * Closes the L2CAP protocol control block. + */ +/*-----------------------------------------------------------------------------------*/ +err_t l2cap_close(struct l2cap_pcb *pcb) +{ + struct l2cap_sig *tmpsig; + + if(pcb->state == L2CAP_LISTEN) { + L2CAP_RMV((struct l2cap_pcb**)((void*)&(l2cap_listen_pcbs)), pcb); + btmemb_free(&l2cap_listenpcbs, pcb); + } else { + L2CAP_RMV(&(l2cap_active_pcbs), pcb); + /* Free any unresponded signals */ + while(pcb->unrsp_sigs != NULL) { + tmpsig = pcb->unrsp_sigs; + pcb->unrsp_sigs = pcb->unrsp_sigs->next; + btmemb_free(&l2cap_sigs, tmpsig); + } + + btmemb_free(&l2cap_pcbs, pcb); + } + pcb = NULL; + return ERR_OK; +} +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_reset_all(): + * + * Closes all active and listening L2CAP protocol control blocks. + */ +/*-----------------------------------------------------------------------------------*/ +void l2cap_reset_all(void) +{ + struct l2cap_pcb *pcb, *tpcb; + struct l2cap_pcb_listen *lpcb, *tlpcb; + struct l2cap_seg *seg, *tseg; + + for(pcb = l2cap_active_pcbs; pcb != NULL;) { + tpcb = pcb->next; + l2cap_close(pcb); + pcb = tpcb; + } + + for(lpcb = l2cap_listen_pcbs; lpcb != NULL;) { + tlpcb = lpcb->next; + l2cap_close((struct l2cap_pcb *)lpcb); + lpcb = tlpcb; + } + + for(seg = l2cap_insegs; seg != NULL;) { + tseg = seg->next; + L2CAP_SEG_RMV(&(l2cap_insegs), seg); + btmemb_free(&l2cap_segs, seg); + seg = tseg; + } + + l2cap_init(); +} + +/*-----------------------------------------------------------------------------------*/ +/* L2CAP to L2CAP signalling events + */ +/*-----------------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_signal(): + * + * Assembles the signalling packet and passes it to the lower layer. + */ +/*-----------------------------------------------------------------------------------*/ +err_t l2cap_signal(struct l2cap_pcb *pcb, u8_t code, u16_t ursp_id, struct bd_addr *remote_bdaddr, struct pbuf *data) +{ + struct l2cap_sig *sig; + struct l2cap_sig_hdr *sighdr; + struct l2cap_hdr *hdr; + err_t ret; + + /* Alloc a new signal */ + LOG("l2cap_signal: Allocate memory for l2cap_sig. Code = 0x%x\n", code); + if((sig = btmemb_alloc(&l2cap_sigs)) == NULL) { + ERROR("l2cap_signal: could not allocate memory for l2cap_sig\n"); + return ERR_MEM; + } + + /* Alloc a pbuf for signal */ + if((sig->p = btpbuf_alloc(PBUF_RAW, L2CAP_HDR_LEN+L2CAP_SIGHDR_LEN, PBUF_RAM)) == NULL) { + ERROR("l2cap_signal: could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Setup signal header and leave room for l2cap hdr */ + sighdr = (struct l2cap_sig_hdr *)(((u8_t *)sig->p->payload)+L2CAP_HDR_LEN); + + /* Chain data to signal and set length of signal data */ + if(data == NULL) { + sighdr->len = 0; + } else { + btpbuf_chain(sig->p, data); + btpbuf_free(data); + sighdr->len = htole16(data->tot_len); + } + + sighdr->code = code; + + if(sighdr->code % 2) { /* If odd this is a resp/rej signal */ + sig->sigid = ursp_id; /* Get id */ + LOG("l2cap_signal: Sending response/reject signal with id = %d code = %d\n", sig->sigid, sighdr->code); + } else { + sig->sigid = l2cap_next_sigid(); /* Alloc id */ + sig->rtx = L2CAP_RTX; /* Set Response Timeout Expired timer (in seconds) + should be at least as large as the BB flush timeout */ + sig->nrtx = L2CAP_MAXRTX; /* Set max number of retransmissions */ + LOG("l2cap_signal: Sending request signal with id = %d code = %d\n", sig->sigid, sighdr->code); + } + sighdr->id = sig->sigid; /* Set id */ + + /* Set up L2CAP hdr */ + hdr = sig->p->payload; + hdr->len = htole16((sig->p->tot_len - L2CAP_HDR_LEN)); + hdr->cid = htole16(L2CAP_SIG_CID); /* 0x0001 */ + + ret = l2cap_write(remote_bdaddr, sig->p, sig->p->tot_len); /* Send peer L2CAP signal */ + + /* Put signal on unresponded list if it's a request signal, else deallocate it */ + if(ret == ERR_OK && (sighdr->code % 2) == 0) { + LOG("l2cap_signal: Registering sent request signal with id = %d code = %d\n", sig->sigid, sighdr->code); + L2CAP_SIG_REG(&(pcb->unrsp_sigs), sig); + } else { + LOG("l2cap_signal: Deallocating sent response/reject signal with id = %d code = %d\n", sig->sigid, sighdr->code); + btpbuf_free(sig->p); + sig->p = NULL; + btmemb_free(&l2cap_sigs, sig); + } + + return ret; +} + +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_rexmit_signal(): + * + * Called by the l2cap timer. Retransmitts a signal. + */ +/*-----------------------------------------------------------------------------------*/ +err_t l2cap_rexmit_signal(struct l2cap_pcb *pcb, struct l2cap_sig *sig) +{ + err_t ret; + + /* Set up L2CAP hdr */ + ret = l2cap_write(&(pcb->remote_bdaddr), sig->p, sig->p->tot_len); /* Send peer L2CAP signal */ + + return ret; +} +/*-----------------------------------------------------------------------------------*/ +/* Upper-Layer to L2CAP signaling events + */ +/*-----------------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------------*/ +/* + * l2ca_connect_req(): + * + * Initiates the sending of a connect request message. Requests the creation of a + * channel representing a logicalconnection to a physical address. Input parameters + * are the target protocol(PSM) and remote devices 48-bit address (BD_ADDR). Also + * specify the function to be called when a confirm has been received. + */ +/*-----------------------------------------------------------------------------------*/ +err_t l2ca_connect_req(struct l2cap_pcb *pcb, struct bd_addr *bdaddr, u16_t psm, + u8_t role_switch, err_t (* l2ca_connect_cfm)(void *arg, struct l2cap_pcb *lpcb, + u16_t result, u16_t status)) +{ + err_t ret; + struct pbuf *data; + + if(bdaddr != NULL) { + bd_addr_set(&(pcb->remote_bdaddr),bdaddr); + } else { + return ERR_VAL; + } + + pcb->psm = psm; + pcb->l2ca_connect_cfm = l2ca_connect_cfm; + pcb->scid = l2cap_cid_alloc(); + + pcb->cfg.l2capcfg |= L2CAP_CFG_IR; /* We are the initiator of this connection */ + + if(!lp_is_connected(bdaddr)) { + ret = lp_connect_req(bdaddr, role_switch); /* Create ACL link w pcb state == CLOSED */ + } else { + if((data = btpbuf_alloc(PBUF_RAW, L2CAP_CONN_REQ_SIZE, PBUF_RAM)) == NULL) { + ERROR("l2cap_connect_req: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + ((u16_t *)data->payload)[0] = htole16(psm); + ((u16_t *)data->payload)[1] = htole16(pcb->scid); + ret = l2cap_signal(pcb, L2CAP_CONN_REQ, 0, &(pcb->remote_bdaddr), data); /* Send l2cap_conn_req signal */ + + pcb->state = W4_L2CAP_CONNECT_RSP; + } + + L2CAP_REG(&(l2cap_active_pcbs), pcb); + + return ret; +} + +/*-----------------------------------------------------------------------------------*/ +/* + * l2ca_config_req(): + * + * Requests the initial configuration (or reconfiguration) of a channel to a new set + * of channel parameters. Input parameters are the local CID endpoint, new incoming + * receivable MTU (InMTU), new outgoing flow specification, and flush and link + * timeouts. Also specify the function to be called when a confirm has been received. + */ +/*-----------------------------------------------------------------------------------*/ +err_t l2ca_config_req(struct l2cap_pcb *pcb) +{ + struct pbuf *p, *q; + struct l2cap_cfgopt_hdr *opthdr; + err_t ret; + + switch(pcb->state) { + case L2CAP_OPEN: + LOG("l2cap_config_req: state = L2CAP_OPEN. Suspend transmission\n"); + /* Note: Application should have suspended data transmission, otherwise outgoing data will be + dropped */ + pcb->state = L2CAP_CONFIG; + /* Fallthrough */ + case L2CAP_CONFIG: + LOG("l2cap_config_req: state = L2CAP_CONFIG\n"); + + if((p = btpbuf_alloc(PBUF_RAW, L2CAP_CFG_REQ_SIZE, PBUF_RAM)) == NULL) { + ERROR("l2cap_config_req: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + + /* Assemble config request packet. Only options that has to be changed will be + sent */ + ((u16_t *)p->payload)[0] = htole16(pcb->dcid); + /* In this implementation we do not send multiple cmds in one + signal packet. Therefore we will never send a config_req packet + that will cause the signal to be larger than the minimum L2CAP MTU + 48 bytes. Hence, this flag will always be cleared */ + ((u16_t *)p->payload)[1] = 0; + + /* Add MTU and out flush timeout to cfg packet if not default value. QoS (Best effort) is always + set to default and can be skipped */ + if(pcb->cfg.inmtu != L2CAP_CFG_DEFAULT_INMTU) { + if((q = btpbuf_alloc(PBUF_RAW, L2CAP_CFGOPTHDR_LEN + L2CAP_MTU_LEN, PBUF_RAM)) == NULL) { + ERROR("l2cap_config_req: Could not allocate memory for pbuf\n"); + btpbuf_free(p); + return ERR_MEM; + } + opthdr = q->payload; + opthdr->type = L2CAP_CFG_MTU; + opthdr->len = L2CAP_MTU_LEN; + ((u16_t *)q->payload)[1] = htole16(pcb->cfg.inmtu); + btpbuf_chain(p, q); + btpbuf_free(q); + } + + if(L2CAP_OUT_FLUSHTO != L2CAP_CFG_DEFAULT_OUTFLUSHTO) { + if((q = btpbuf_alloc(PBUF_RAW, L2CAP_CFGOPTHDR_LEN + L2CAP_FLUSHTO_LEN, PBUF_RAM)) == NULL) { + ERROR("l2cap_config_req: Could not allocate memory for pbuf\n"); + btpbuf_free(p); + return ERR_MEM; + } + opthdr = q->payload; + opthdr->type = L2CAP_FLUSHTO; + opthdr->len = L2CAP_FLUSHTO_LEN; + pcb->cfg.outflushto = L2CAP_OUT_FLUSHTO; + ((u16_t *)q->payload)[1] = htole16(pcb->cfg.outflushto); + btpbuf_chain(p, q); + btpbuf_free(q); + } + + /* Send config request signal */ + ret = l2cap_signal(pcb, L2CAP_CFG_REQ, 0, &(pcb->remote_bdaddr), p); + break; + default: + ERROR("l2cap_config_req: state = L2CAP_?. Invalid state\n"); + return ERR_CONN; /* Invalid state. Connection is not in OPEN or CONFIG state */ + } + return ret; +} +/*-----------------------------------------------------------------------------------*/ +/* + * l2ca_disconnect_req(): + * + * Requests the disconnection of the channel. Also specify the function to be called + * when a confirm is received + */ +/*-----------------------------------------------------------------------------------*/ +err_t l2ca_disconnect_req(struct l2cap_pcb *pcb, err_t (* l2ca_disconnect_cfm)(void *arg, struct l2cap_pcb *pcb)) +{ + struct pbuf *data; + err_t ret; + + if(pcb->state == L2CAP_OPEN || pcb->state == L2CAP_CONFIG) { + if((data = btpbuf_alloc(PBUF_RAW, L2CAP_DISCONN_REQ_SIZE, PBUF_RAM)) == NULL) { + ERROR("l2cap_disconnect_req: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + pcb->l2ca_disconnect_cfm = l2ca_disconnect_cfm; + + ((u16_t *)data->payload)[0] = htole16(pcb->dcid); + ((u16_t *)data->payload)[1] = htole16(pcb->scid); + + ret = l2cap_signal(pcb, L2CAP_DISCONN_REQ, 0, &(pcb->remote_bdaddr), data); + + if(ret == ERR_OK) { + pcb->state = W4_L2CAP_DISCONNECT_RSP; + } + } else { + return ERR_CONN; /* Signal not supported in this state */ + } + + return ret; +} +/*-----------------------------------------------------------------------------------*/ +/* + * l2ca_datawrite(): + * + * Transfers data across the channel. + */ +/*-----------------------------------------------------------------------------------*/ +err_t l2ca_datawrite(struct l2cap_pcb *pcb, struct pbuf *p) +{ + err_t ret; + struct l2cap_hdr *l2caphdr; + struct pbuf *q; + + if(pcb->state != L2CAP_OPEN) { + ERROR("l2cap_datawrite: State != L2CAP_OPEN. Dropping data\n"); + return ERR_CONN; + } + + /* Build L2CAP header */ + if((q = btpbuf_alloc(PBUF_RAW, L2CAP_HDR_LEN, PBUF_RAM)) == NULL) { + ERROR("l2cap_datawrite: Could not allocate memory for pbuf\n"); + return ERR_MEM; + } + btpbuf_chain(q, p); + + l2caphdr = q->payload; + l2caphdr->cid = htole16(pcb->dcid); + + /* If length of the data exceeds the OutMTU then only the first OutMTU bytes are sent */ + if(p->tot_len > pcb->cfg.outmtu) { + /* Send peer L2CAP data */ + l2caphdr->len = htole16(pcb->cfg.outmtu); + if((ret = l2cap_write(&(pcb->remote_bdaddr), q, pcb->cfg.outmtu + L2CAP_HDR_LEN)) == ERR_OK) { + //LOG("l2cap_datawrite: Length of data exceeds the OutMTU p->tot_len = %d\n", p->tot_len); + ret = ERR_BUF; /* Length of data exceeds the OutMTU */ + } + } else { + /* Send peer L2CAP data */ + l2caphdr->len = htole16(p->tot_len); + //LOG("l2cap_datawrite: q->tot_len = %d\n", q->tot_len); + ret = l2cap_write(&(pcb->remote_bdaddr), q, q->tot_len); + } + + /* Free L2CAP header. Higher layers will handle rest of packet */ + p = btpbuf_dechain(q); + btpbuf_free(q); + + return ret; +} +/*-----------------------------------------------------------------------------------*/ +/* + * l2ca_ping(): + * + * Sends an empty L2CAP echo request message. Also specify the function that should + * be called when a L2CAP echo reply has been received. + */ +/*-----------------------------------------------------------------------------------*/ +err_t l2ca_ping(struct bd_addr *bdaddr, struct l2cap_pcb *tpcb, + err_t (* l2ca_pong)(void *arg, struct l2cap_pcb *pcb, u8_t result)) +{ + err_t ret; + + if(!lp_is_connected(bdaddr)) { + return ERR_CONN; + } + + bd_addr_set(&(tpcb->remote_bdaddr), bdaddr); + tpcb->l2ca_pong = l2ca_pong; + + L2CAP_REG(&(l2cap_active_pcbs), tpcb); + + ret = l2cap_signal(tpcb, L2CAP_ECHO_REQ, 0, &(tpcb->remote_bdaddr), NULL); /* Send l2cap_echo_req signal */ + + return ret; +} +/*-----------------------------------------------------------------------------------*/ +/* Lower-Layer to L2CAP signaling events + */ +/*-----------------------------------------------------------------------------------*/ +/*-----------------------------------------------------------------------------------*/ +/* + * lp_connect_cfm(): + * + * Confirms the request to establish a lower layer (Baseband) connection. + */ +/*-----------------------------------------------------------------------------------*/ +void lp_connect_cfm(struct bd_addr *bdaddr, u8_t encrypt_mode, err_t err) +{ + struct l2cap_pcb *pcb; + struct pbuf *data; + err_t ret; + + for(pcb = l2cap_active_pcbs; pcb != NULL; pcb = pcb->next) { + if(bd_addr_cmp(&(pcb->remote_bdaddr), bdaddr)) { + break; + } + } + if(pcb == NULL) { + /* Silently discard */ + LOG("lp_connect_cfm: Silently discard\n"); + } else { + if(err == ERR_OK) { + pcb->encrypt = encrypt_mode; + /* Send l2cap_conn_req signal if no error */ + if((data = btpbuf_alloc(PBUF_RAW, L2CAP_CONN_REQ_SIZE, PBUF_RAM)) != NULL) { + ((u16_t *)data->payload)[0] = htole16(pcb->psm); + ((u16_t *)data->payload)[1] = htole16(pcb->scid); + if((ret = l2cap_signal(pcb, L2CAP_CONN_REQ, 0, &(pcb->remote_bdaddr), data)) == ERR_OK) { + pcb->state = W4_L2CAP_CONNECT_RSP; + } else { + L2CA_ACTION_CONN_CFM(pcb,L2CAP_CONN_REF_RES,0x0000,ret); /* No resources available? */ + } + //LOG("lp_connect_cfm: l2cap_conn_req signal sent. err = %d\nPSM = 0x%x\nscid = 0x%x\nencrypt mode = 0x%x\n", err, pcb->psm, pcb->scid, pcb->encrypt); + } else { + ERROR("lp_connect_cfm: No resources available\n"); + L2CA_ACTION_CONN_CFM(pcb,L2CAP_CONN_REF_RES,0x0000,ret); /* No resources available */ + } + } else { + ERROR("lp_connect_cfm: Connection falied\n"); + L2CA_ACTION_CONN_CFM(pcb,L2CAP_CONN_REF_RES,0x0000,ret); /* No resources available */ + } + } +} +/*-----------------------------------------------------------------------------------*/ +/* + * lp_connect_ind(): + * + * Indicates the lower protocol has successfully established a connection. + */ +/*-----------------------------------------------------------------------------------*/ +void lp_connect_ind(struct bd_addr *bdaddr) +{ + LOG("lp_connect_ind\n"); +} +/*-----------------------------------------------------------------------------------*/ +/* + * lp_disconnect_ind(): + * + * Indicates the lower protocol (Baseband) has been shut down by LMP commands or a + * timeout event.. + */ +/*-----------------------------------------------------------------------------------*/ +void lp_disconnect_ind(struct bd_addr *bdaddr,u8_t reason) +{ + struct l2cap_pcb *pcb, *tpcb; + err_t ret; + + (void)ret; + + for(pcb = l2cap_active_pcbs; pcb != NULL;) { + tpcb = pcb->next; + LOG("lp_disconnect_ind: Find a pcb with a matching Bluetooth address\n"); + /* All PCBs with matching Bluetooth address have been disconnected */ + if(bd_addr_cmp(&(pcb->remote_bdaddr), bdaddr)) {// && pcb->state != L2CAP_CLOSED) { + pcb->state = L2CAP_CLOSED; + LOG("lp_disconnect_ind: Notify application\n"); + L2CA_ACTION_DISCONN_IND(pcb,ERR_OK,ret); + } + pcb = tpcb; + } + if(l2cap_disconnect_bb_cb) l2cap_disconnect_bb_cb(bdaddr,reason); +} + +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_disconnect_bb(): + * + * Register a callback to obtain the disconnection reason from the baseband + */ +/*-----------------------------------------------------------------------------------*/ +void (*l2cap_disconnect_bb(void (*l2ca_disconnect_bb)(struct bd_addr *bdaddr,u8_t reason)))(struct bd_addr *bdaddr,u8_t reason) +{ + void (*oldcb)(struct bd_addr *bdaddr,u8_t reason) = NULL; + oldcb = l2cap_disconnect_bb_cb; + l2cap_disconnect_bb_cb = l2ca_disconnect_bb; + return oldcb; +} + +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_next_sigid(): + * + * Issues a signal identifier that helps matching a request with the reply. + */ +/*-----------------------------------------------------------------------------------*/ +u8_t l2cap_next_sigid(void) +{ + ++sigid_nxt; + if(sigid_nxt == 0) { + sigid_nxt = 1; + } + return sigid_nxt; +} +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_arg(): + * + * Used to specify the argument that should be passed callback functions. + */ +/*-----------------------------------------------------------------------------------*/ +void l2cap_arg(struct l2cap_pcb *pcb, void *arg) +{ + pcb->callback_arg = arg; +} + +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_connect_ind(): + * + * Set the state of the connection to be LISTEN, which means that it is able to accept + * incoming connections. The protocol control block is reallocated in order to consume + * less memory. Setting the connection to LISTEN is an irreversible process. Also + * specify the function that should be called when the channel has received a + * connection request. + */ +/*-----------------------------------------------------------------------------------*/ +err_t l2cap_connect_ind(struct l2cap_pcb *npcb, struct bd_addr *bdaddr, u16_t psm,err_t (* l2ca_connect_ind)(void *arg, struct l2cap_pcb *pcb, err_t err)) +{ + struct l2cap_pcb_listen *lpcb; + + lpcb = btmemb_alloc(&l2cap_listenpcbs); + if(lpcb == NULL) { + ERROR("l2cap_connect_ind: Could not allocate memory for lpcb\n"); + return ERR_MEM; + } + + bd_addr_set(&(lpcb->bdaddr),bdaddr); + lpcb->psm = psm; + lpcb->l2ca_connect_ind = l2ca_connect_ind; + lpcb->state = L2CAP_LISTEN; + lpcb->callback_arg = npcb->callback_arg; + btmemb_free(&l2cap_pcbs, npcb); + L2CAP_REG(&(l2cap_listen_pcbs), lpcb); + return ERR_OK; +} + +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_disconnect_ind(): + * + * Used to specify the a function to be called when a disconnection request has been + * received from a remote device or the remote device has been disconnected because it + * has failed to respond to a signalling request. + */ +/*-----------------------------------------------------------------------------------*/ +void l2cap_disconnect_ind(struct l2cap_pcb *pcb, err_t (* l2ca_disconnect_ind)(void *arg, struct l2cap_pcb *newpcb, err_t err)) +{ + pcb->l2ca_disconnect_ind = l2ca_disconnect_ind; +} +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_timeout_ind(): + * + * Used to specify the function to be called when RTX or ERTX timer has expired. + */ +/*-----------------------------------------------------------------------------------*/ +void l2cap_timeout_ind(struct l2cap_pcb *pcb,err_t (* l2ca_timeout_ind)(void *arg, struct l2cap_pcb *newpcb, err_t err)) +{ + pcb->l2ca_timeout_ind = l2ca_timeout_ind; +} +/*-----------------------------------------------------------------------------------*/ +/* + * l2cap_recv(): + * + * Used to specify the function that should be called when a L2CAP connection receives + * data. + */ +/*-----------------------------------------------------------------------------------*/ +void l2cap_recv(struct l2cap_pcb *pcb, err_t (* l2ca_recv)(void *arg, struct l2cap_pcb *pcb, struct pbuf *p, err_t err)) +{ + pcb->l2ca_recv = l2ca_recv; +} +/*-----------------------------------------------------------------------------------*/ diff --git a/wii/libogc/lwbt/l2cap.h b/wii/libogc/lwbt/l2cap.h new file mode 100644 index 0000000000..6ba450d7f1 --- /dev/null +++ b/wii/libogc/lwbt/l2cap.h @@ -0,0 +1,356 @@ +#ifndef __L2CAP_H__ +#define __L2CAP_H__ + +#include "bt.h" +#include "bd_addr.h" + +/* Protocol and service multiplexor */ +#define HIDP_PSM 0x0011 +#define INTR_PSM 0x0013 + +/* Packet header lengths */ +#define L2CAP_HDR_LEN 4 +#define L2CAP_SIGHDR_LEN 4 +#define L2CAP_CFGOPTHDR_LEN 2 + +/* Signals sizes */ +#define L2CAP_CONN_REQ_SIZE 4 +#define L2CAP_CONN_RSP_SIZE 8 +#define L2CAP_CFG_RSP_SIZE 6 +#define L2CAP_DISCONN_RSP_SIZE 4 + +#define L2CAP_CFG_REQ_SIZE 4 + +#define L2CAP_DISCONN_REQ_SIZE 4 +#define L2CAP_CMD_REJ_SIZE 2 + +/* Signal codes */ +#define L2CAP_CMD_REJ 0x01 +#define L2CAP_CONN_REQ 0x02 +#define L2CAP_CONN_RSP 0x03 +#define L2CAP_CFG_REQ 0x04 +#define L2CAP_CFG_RSP 0x05 +#define L2CAP_DISCONN_REQ 0x06 +#define L2CAP_DISCONN_RSP 0x07 +#define L2CAP_ECHO_REQ 0x08 +#define L2CAP_ECHO_RSP 0x09 +#define L2CAP_INFO_REQ 0x0A +#define L2CAP_INFO_RSP 0x0B + +/* Permanent channel identifiers */ +#define L2CAP_NULL_CID 0x0000 +#define L2CAP_SIG_CID 0x0001 +#define L2CAP_CONNLESS_CID 0x0002 + +/* Channel identifiers values */ +#define L2CAP_MIN_CID 0x0040 +#define L2CAP_MAX_CID 0xFFFF + +/* Configuration types */ +#define L2CAP_CFG_MTU 0x01 +#define L2CAP_FLUSHTO 0x02 +#define L2CAP_QOS 0x03 + +/* Configuration types length */ +#define L2CAP_MTU_LEN 2 +#define L2CAP_FLUSHTO_LEN 2 +#define L2CAP_QOS_LEN 22 + +/* Configuration response types */ +#define L2CAP_CFG_SUCCESS 0x0000 +#define L2CAP_CFG_UNACCEPT 0x0001 +#define L2CAP_CFG_REJ 0x0002 +#define L2CAP_CFG_UNKNOWN 0x0003 +#define L2CAP_CFG_TIMEOUT 0xEEEE + +/* QoS types */ +#define L2CAP_QOS_NO_TRAFFIC 0x00 +#define L2CAP_QOS_BEST_EFFORT 0x01 +#define L2CAP_QOS_GUARANTEED 0x02 + +/* Command reject reasons */ +#define L2CAP_CMD_NOT_UNDERSTOOD 0x0000 +#define L2CAP_MTU_EXCEEDED 0x0001 +#define L2CAP_INVALID_CID 0x0002 + +/* Connection response results */ +#define L2CAP_CONN_SUCCESS 0x0000 +#define L2CAP_CONN_PND 0x0001 +#define L2CAP_CONN_REF_PSM 0x0002 +#define L2CAP_CONN_REF_SEC 0x0003 +#define L2CAP_CONN_REF_RES 0x0004 +#define L2CAP_CONN_CFG_TO 0x0005 /* Implementation specific result */ + +/* Echo response results */ +#define L2CAP_ECHO_RCVD 0x00 +#define L2CAP_ECHO_TO 0x01 + +/* L2CAP segmentation */ +#define L2CAP_ACL_START 0x02 +#define L2CAP_ACL_CONT 0x01 + +/* L2CAP config default parameters */ +#define L2CAP_CFG_DEFAULT_INMTU 672 /* Two Baseband DH5 packets (2*341=682) minus the Baseband ACL + headers (2*2=4) and L2CAP header (6) */ +#define L2CAP_CFG_DEFAULT_OUTFLUSHTO 0xFFFF + +/* L2CAP configuration parameter masks */ +#define L2CAP_CFG_IR 0x01 +#define L2CAP_CFG_IN_SUCCESS 0x02 +#define L2CAP_CFG_OUT_SUCCESS 0x04 +#define L2CAP_CFG_OUT_REQ 0x08 + +enum l2cap_state { + L2CAP_CLOSED, L2CAP_LISTEN, W4_L2CAP_CONNECT_RSP, W4_L2CA_CONNECT_RSP, L2CAP_CONFIG, + L2CAP_OPEN, W4_L2CAP_DISCONNECT_RSP, W4_L2CA_DISCONNECT_RSP +}; + +struct l2cap_hdr +{ + u16_t len; + u16_t cid; +} ATTRIBUTE_PACKED; + +struct l2cap_sig_hdr +{ + u8_t code; + u8_t id; + u16_t len; +} ATTRIBUTE_PACKED; + +struct l2cap_cfgopt_hdr +{ + u8_t type; + u8_t len; +} ATTRIBUTE_PACKED; + +/* This structure is used to represent L2CAP signals. */ +struct l2cap_sig { + struct l2cap_sig *next; /* for the linked list, used when putting signals + on a queue */ + struct pbuf *p; /* buffer containing data + L2CAP header */ + u16_t sigid; /* Identification */ + u16_t ertx; /* extended response timeout expired */ + u8_t rtx; /* response timeout expired */ + u8_t nrtx; /* number of retransmissions */ +}; + +struct l2cap_cfg +{ + u16_t inmtu; + u16_t outmtu; + u16_t influshto; + u16_t outflushto; + + struct pbuf *opt; + + u8_t cfgto; + u8_t l2capcfg; +}; + +struct l2cap_seg +{ + struct l2cap_seg *next; + struct bd_addr bdaddr; + struct pbuf *p; + u16_t len; + struct l2cap_hdr *l2caphdr; + struct l2cap_pcb *pcb; /* The L2CAP Protocol Control Block */ +}; + +struct l2cap_pcb { + struct l2cap_pcb *next; /* For the linked list */ + + enum l2cap_state state; /* L2CAP state */ + + void *callback_arg; + + u16_t scid; /* Source CID */ + u16_t dcid; /* Destination CID */ + + u16_t psm; /* Protocol/Service Multiplexer */ + + u16_t ursp_id; /* Signal id to respond to */ + u8_t encrypt; /* encryption mode */ + + struct l2cap_sig *unrsp_sigs; /* List of sent but unresponded signals */ + + struct bd_addr remote_bdaddr; + + struct l2cap_cfg cfg; /* Configuration parameters */ + + /* Upper layer to L2CAP confirmation functions */ + + /* Function to be called when a connection has been set up */ + err_t (* l2ca_connect_cfm)(void *arg, struct l2cap_pcb *pcb, u16_t result, u16_t status); + /* Function to be called when a connection has been closed */ + err_t (* l2ca_disconnect_cfm)(void *arg, struct l2cap_pcb *pcb); + /* Function to be called when a echo reply has been received */ + err_t (* l2ca_pong)(void *arg, struct l2cap_pcb *pcb, u8_t result); + + /* L2CAP to upper layer indication functions */ + + /* Function to be called when a connection indication event occurs */ + err_t (* l2ca_connect_ind)(void *arg, struct l2cap_pcb *pcb, err_t err); + /* Function to be called when a disconnection indication event occurs */ + err_t (* l2ca_disconnect_ind)(void *arg, struct l2cap_pcb *pcb, err_t err); + /* Function to be called when a timeout indication event occurs */ + err_t (* l2ca_timeout_ind)(void *arg, struct l2cap_pcb *newpcb, err_t err); + /* Function to be called when a L2CAP connection receives data */ + err_t (* l2ca_recv)(void *arg, struct l2cap_pcb *pcb, struct pbuf *p, err_t err); +}; + +struct l2cap_pcb_listen { + struct l2cap_pcb_listen *next; /* for the linked list */ + + enum l2cap_state state; /* L2CAP state */ + + void *callback_arg; + + u16_t psm; /* Protocol/Service Multiplexer */ + struct bd_addr bdaddr; /* Device Address */ + + /* Function to call when a connection request has been received + from a remote device. */ + err_t (* l2ca_connect_ind)(void *arg, struct l2cap_pcb *pcb, err_t err); +}; + +#define l2cap_psm(pcb) ((pcb)->psm) + +void l2cap_init(); +struct l2cap_pcb* l2cap_new(void); + +void lp_connect_ind(struct bd_addr *bdaddr); +void lp_connect_cfm(struct bd_addr *bdaddr, u8_t encrypt_mode, err_t err); +void lp_disconnect_ind(struct bd_addr *bdaddr,u8_t reason); + +err_t l2ca_config_req(struct l2cap_pcb *pcb); +err_t l2ca_disconnect_req(struct l2cap_pcb *pcb, err_t (* l2ca_disconnect_cfm)(void *arg, struct l2cap_pcb *pcb)); +err_t l2ca_datawrite(struct l2cap_pcb *pcb, struct pbuf *p); +err_t l2ca_ping(struct bd_addr *bdaddr, struct l2cap_pcb *tpcb,err_t (* l2ca_pong)(void *arg, struct l2cap_pcb *pcb, u8_t result)); +err_t l2ca_connect_req(struct l2cap_pcb *pcb, struct bd_addr *bdaddr, u16_t psm, u8_t role_switch, err_t (* l2ca_connect_cfm)(void *arg, struct l2cap_pcb *lpcb,u16_t result, u16_t status)); + +void l2cap_tmr(); +void l2cap_input(struct pbuf *p, struct bd_addr *bdaddr); +err_t l2cap_close(struct l2cap_pcb *pcb); +void l2cap_reset_all(void); +u8_t l2cap_next_sigid(void); +err_t l2cap_write(struct bd_addr *bdaddr, struct pbuf *p, u16_t len); +void l2cap_arg(struct l2cap_pcb *pcb, void *arg); +void l2cap_disconnect_ind(struct l2cap_pcb *pcb, err_t (* l2ca_disconnect_ind)(void *arg, struct l2cap_pcb *newpcb, err_t err)); +void l2cap_timeout_ind(struct l2cap_pcb *pcb,err_t (* l2ca_timeout_ind)(void *arg, struct l2cap_pcb *newpcb, err_t err)); +void l2cap_recv(struct l2cap_pcb *pcb, err_t (* l2ca_recv)(void *arg, struct l2cap_pcb *pcb, struct pbuf *p, err_t err)); +err_t l2cap_signal(struct l2cap_pcb *pcb, u8_t code, u16_t ursp_id, struct bd_addr *remote_bdaddr, struct pbuf *data); +void l2cap_process_sig(struct pbuf *q, struct l2cap_hdr *l2caphdr, struct bd_addr *bdaddr); + +err_t l2cap_rexmit_signal(struct l2cap_pcb *pcb, struct l2cap_sig *sig); +err_t l2cap_connect_ind(struct l2cap_pcb *npcb, struct bd_addr *bdaddr, u16_t psm,err_t (* l2ca_connect_ind)(void *arg, struct l2cap_pcb *pcb, err_t err)); + +void (*l2cap_disconnect_bb(void (*l2ca_disconnect_bb)(struct bd_addr *bdaddr,u8_t reason)))(struct bd_addr *bdaddr,u8_t reason); + +/* Internal functions and global variables */ +#define L2CA_ACTION_CONN_CFM(pcb,result,status,ret) if((pcb)->l2ca_connect_cfm != NULL) (ret = (pcb)->l2ca_connect_cfm((pcb)->callback_arg,(pcb),(result),(status))) +#define L2CA_ACTION_DISCONN_CFM(pcb,ret) if((pcb)->l2ca_disconnect_cfm != NULL) (ret = (pcb)->l2ca_disconnect_cfm((pcb)->callback_arg,(pcb))) +#define L2CA_ACTION_PING_CFM(pcb,result,ret) if((pcb)->l2ca_pong != NULL) (ret = (pcb)->l2ca_pong((pcb)->callback_arg,(pcb),(result))) + +#define L2CA_ACTION_CONN_IND(pcb,err,ret) if((pcb)->l2ca_connect_ind != NULL) (ret = (pcb)->l2ca_connect_ind((pcb)->callback_arg,(pcb),(err))) +#define L2CA_ACTION_DISCONN_IND(pcb,err,ret) \ + if((pcb)->l2ca_disconnect_ind != NULL) { \ + LOG("l2cap_disconnect_ind called\n"); \ + (ret = (pcb)->l2ca_disconnect_ind((pcb)->callback_arg,(pcb),(err))); \ + } else { \ + l2cap_close(pcb); \ + } +#define L2CA_ACTION_TO_IND(pcb,err,ret) if((pcb)->l2ca_timeout_ind != NULL) (ret = (pcb)->l2ca_timeout_ind((pcb)->callback_arg,(pcb),(err))) +#define L2CA_ACTION_RECV(pcb,p,err,ret) \ + if((pcb)->l2ca_recv != NULL) { \ + (ret = (pcb)->l2ca_recv((pcb)->callback_arg,(pcb),(p),(err))); \ + } else { \ + btpbuf_free(p); \ + } + +#define L2CAP_OPTH_TYPE(hdr) (((hdr)->type) & 0x7f) +#define L2CAP_OPTH_TOA(hdr) (((hdr)->type) >> 7) + +/* The L2CAP PCB lists. */ +extern struct l2cap_pcb_listen *l2cap_listen_pcbs; /* List of all L2CAP PCBs in CLOSED state + but awaing an incoming conn req. */ +extern struct l2cap_pcb *l2cap_active_pcbs; /* List of all L2CAP PCBs that has + established or is about to establish + an ACL link */ + +extern struct l2cap_pcb *l2cap_tmp_pcb; /* Only used for temporary storage. */ + +#define L2CAP_REG(pcbs, npcb) do { \ + u32 level; \ + _CPU_ISR_Disable(level); \ + npcb->next = *pcbs; \ + *pcbs = npcb; \ + _CPU_ISR_Restore(level); \ + } while(0) +#define L2CAP_RMV(pcbs, npcb) do { \ + u32 level; \ + _CPU_ISR_Disable(level); \ + if(*pcbs == npcb) { \ + *pcbs = (*pcbs)->next; \ + } else for(l2cap_tmp_pcb = *pcbs; l2cap_tmp_pcb != NULL; l2cap_tmp_pcb = l2cap_tmp_pcb->next) { \ + if(l2cap_tmp_pcb->next != NULL && l2cap_tmp_pcb->next == npcb) { \ + l2cap_tmp_pcb->next = npcb->next; \ + break; \ + } \ + } \ + npcb->next = NULL; \ + _CPU_ISR_Restore(level); \ + } while(0) + +/* The L2CAP SIG list macros */ +extern struct l2cap_sig *l2cap_tmp_sig; /* Only used for temporary storage. */ + +#define L2CAP_SIG_REG(ursp_sigs, nsig) do { \ + u32 level; \ + _CPU_ISR_Disable(level); \ + nsig->next = *ursp_sigs; \ + *ursp_sigs = nsig; \ + _CPU_ISR_Restore(level); \ + } while(0) +#define L2CAP_SIG_RMV(ursp_sigs, nsig) do { \ + u32 level; \ + _CPU_ISR_Disable(level); \ + if(*ursp_sigs == nsig) { \ + *ursp_sigs = (*ursp_sigs)->next; \ + } else for(l2cap_tmp_sig = *ursp_sigs; l2cap_tmp_sig != NULL; l2cap_tmp_sig = l2cap_tmp_sig->next) { \ + if(l2cap_tmp_sig->next != NULL && l2cap_tmp_sig->next == nsig) { \ + l2cap_tmp_sig->next = nsig->next; \ + break; \ + } \ + } \ + nsig->next = NULL; \ + _CPU_ISR_Restore(level); \ + } while(0) + +/* The L2CAP incoming segments list macros */ +extern struct l2cap_seg *l2cap_tmp_inseg; /* Only used for temporary storage. */ + +#define L2CAP_SEG_REG(segs, nseg) do { \ + u32 level; \ + _CPU_ISR_Disable(level); \ + nseg->next = *segs; \ + *segs = nseg; \ + _CPU_ISR_Restore(level); \ + } while(0) +#define L2CAP_SEG_RMV(segs, nseg) do { \ + u32 level; \ + _CPU_ISR_Disable(level); \ + if(*segs == nseg) { \ + *segs = (*segs)->next; \ + } else for(l2cap_tmp_inseg = *segs; l2cap_tmp_inseg != NULL; l2cap_tmp_inseg = l2cap_tmp_inseg->next) { \ + if(l2cap_tmp_inseg->next != NULL && l2cap_tmp_inseg->next == nseg) { \ + l2cap_tmp_inseg->next = nseg->next; \ + break; \ + } \ + } \ + nseg->next = NULL; \ + _CPU_ISR_Restore(level); \ + } while(0) + +#endif diff --git a/wii/libogc/lwbt/physbusif.c b/wii/libogc/lwbt/physbusif.c new file mode 100644 index 0000000000..669b6a6548 --- /dev/null +++ b/wii/libogc/lwbt/physbusif.c @@ -0,0 +1,342 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "hci.h" +#include "btmemb.h" +#include "physbusif.h" + +#define STACKSIZE 32768 + +#define NUM_ACL_BUFS 30 +#define NUM_CTRL_BUFS 45 + +#define ACL_BUF_SIZE 1800 +#define CTRL_BUF_SIZE 660 + +#define ROUNDUP32(v) (((u32)(v)+0x1f)&~0x1f) +#define ROUNDDOWN32(v) (((u32)(v)-0x1f)&~0x1f) + +struct usbtxbuf +{ + u32 txsize; + void *rpData; +}; + +static u32 __ntd_ohci = 0; +static u32 __ntd_ohci_initflag = 0; +static u16 __ntd_vid = 0; +static u16 __ntd_pid = 0; +static u32 __ntd_vid_pid_specified = 0; +static s32 __ntd_usb_fd = -1; +static u32 __wait4hci = 1; +static struct _usb_p __usbdev; + +static struct memb_blks ctrlbufs; +static struct memb_blks aclbufs; + +static u8 __ppc_btstack1[STACKSIZE] ATTRIBUTE_ALIGN(8); +static u8 __ppc_btstack2[STACKSIZE] ATTRIBUTE_ALIGN(8); + +static s32 __issue_bulkread(); +static s32 __issue_intrread(); + +extern u32 __IPC_ClntInit(); + +static s32 __usb_closeCB(s32 result,void *usrdata) +{ + __usbdev.fd = -1; + return result; +} + +static s32 __writectrlmsgCB(s32 result,void *usrdata) +{ + if(usrdata!=NULL) btmemb_free(&ctrlbufs,usrdata); + return result; +} + +static s32 __writebulkmsgCB(s32 result,void *usrdata) +{ + if(usrdata!=NULL) btmemb_free(&aclbufs,usrdata); + return result; +} + +static s32 __readbulkdataCB(s32 result,void *usrdata) +{ + u8 *ptr; + u32 len; + struct pbuf *p,*q; + struct usbtxbuf *buf = (struct usbtxbuf*)usrdata; + + if(__usbdev.openstate!=0x0002) return 0; + + if(result>0) { + len = result; + p = btpbuf_alloc(PBUF_RAW,len,PBUF_POOL); + if(p!=NULL) { + ptr = buf->rpData; + for(q=p;q!=NULL && len>0;q=q->next) { + memcpy(q->payload,ptr,q->len); + ptr += q->len; + len -= q->len; + } + + SYS_SwitchFiber((u32)p,0,0,0,(u32)hci_acldata_handler,(u32)(&__ppc_btstack2[STACKSIZE])); + btpbuf_free(p); + } else + ERROR("__readbulkdataCB: Could not allocate memory for pbuf.\n"); + } + btmemb_free(&aclbufs,buf); + + return __issue_bulkread(); +} + +static s32 __readintrdataCB(s32 result,void *usrdata) +{ + u8 *ptr; + u32 len; + struct pbuf *p,*q; + struct usbtxbuf *buf = (struct usbtxbuf*)usrdata; + + if(__usbdev.openstate!=0x0002) return 0; + + if(result>0) { + len = result; + p = btpbuf_alloc(PBUF_RAW,len,PBUF_POOL); + if(p!=NULL) { + ptr = buf->rpData; + for(q=p;q!=NULL && len>0;q=q->next) { + memcpy(q->payload,ptr,q->len); + ptr += q->len; + len -= q->len; + } + + SYS_SwitchFiber((u32)p,0,0,0,(u32)hci_event_handler,(u32)(&__ppc_btstack1[STACKSIZE])); + btpbuf_free(p); + } else + ERROR("__readintrdataCB: Could not allocate memory for pbuf.\n"); + } + btmemb_free(&ctrlbufs,buf); + + return __issue_intrread(); +} + +static s32 __issue_intrread() +{ + s32 ret; + u32 len; + u8 *ptr; + struct usbtxbuf *buf; + + if(__usbdev.openstate!=0x0002) return IPC_OK; + + buf = (struct usbtxbuf*)btmemb_alloc(&ctrlbufs); + if(buf!=NULL) { + ptr = (u8*)((u32)buf + sizeof(struct usbtxbuf)); + buf->rpData = (void*)ROUNDUP32(ptr); + len = (ctrlbufs.size - ((u32)buf->rpData - (u32)buf)); + buf->txsize = ROUNDDOWN32(len); + ret = USB_ReadIntrMsgAsync(__usbdev.fd,__usbdev.hci_evt,buf->txsize,buf->rpData,__readintrdataCB,buf); + } else + ret = IPC_ENOMEM; + + return ret; +} + +static s32 __issue_bulkread() +{ + s32 ret; + u32 len; + u8 *ptr; + struct usbtxbuf *buf; + + if(__usbdev.openstate!=0x0002) return IPC_OK; + + buf = (struct usbtxbuf*)btmemb_alloc(&aclbufs); + if(buf!=NULL) { + ptr = (u8*)((u32)buf + sizeof(struct usbtxbuf)); + buf->rpData = (void*)ROUNDUP32(ptr); + len = (aclbufs.size - ((u32)buf->rpData - (u32)buf)); + buf->txsize = ROUNDDOWN32(len); + ret = USB_ReadBlkMsgAsync(__usbdev.fd,__usbdev.acl_in,buf->txsize,buf->rpData,__readbulkdataCB,buf); + } else + ret = IPC_ENOMEM; + + return ret; +} + +static s32 __initUsbIOBuffer(struct memb_blks *blk,u32 buf_size,u32 num_bufs) +{ + u32 len; + u8 *ptr = NULL; + + len = ((MEM_ALIGN_SIZE(buf_size)+sizeof(u32))*num_bufs); + ptr = (u8*)ROUNDDOWN32(((u32)SYS_GetArena2Hi() - len)); + if((u32)ptr<(u32)SYS_GetArena2Lo()) return -4; + + SYS_SetArena2Hi(ptr); + + blk->size = buf_size; + blk->num = num_bufs; + blk->mem = ptr; + + btmemb_init(blk); + return 0; +} + +static s32 __getDeviceId(u16 vid,u16 pid) +{ + s32 ret = 0; + + if(__ntd_ohci_initflag==0x0001) { + if(__ntd_ohci==0x0000) + ret = USB_OpenDevice(USB_OH0_DEVICE_ID,vid,pid,&__usbdev.fd); + else if(__ntd_ohci==0x0001) + ret = USB_OpenDevice(USB_OH1_DEVICE_ID,vid,pid,&__usbdev.fd); + } else + ret = USB_OpenDevice(USB_OH1_DEVICE_ID,vid,pid,&__usbdev.fd); + + //printf("__getDeviceId(%04x,%04x,%d)\n",vid,pid,__usbdev.fd); + if(ret==0) __ntd_usb_fd = __usbdev.fd; + return ret; +} + +static s32 __usb_register(pbcallback cb) +{ + s32 ret = 0; + + memset(&__usbdev,0,sizeof(struct _usb_p)); + __usbdev.openstate = 5; + + ret = __IPC_ClntInit(); + if(ret<0) return ret; + + ret = USB_Initialize(); + if(ret<0) return ret; + + __usbdev.fd = -1; + __usbdev.unregcb = cb; + if(__ntd_vid_pid_specified) { + __usbdev.vid = __ntd_vid; + __usbdev.pid = __ntd_pid; + } else { + __usbdev.vid = 0x057E; + __usbdev.pid = 0x0305; + } + + ret = __getDeviceId(__usbdev.vid,__usbdev.pid); + if(ret<0) return ret; + + __usbdev.acl_out = 0x02; + __usbdev.acl_in = 0x82; + __usbdev.hci_evt = 0x81; + __usbdev.hci_ctrl = 0x00; + + __initUsbIOBuffer(&ctrlbufs,CTRL_BUF_SIZE,NUM_CTRL_BUFS); + __initUsbIOBuffer(&aclbufs,ACL_BUF_SIZE,NUM_ACL_BUFS); + + __usbdev.openstate = 4; + __wait4hci = 1; + + return ret; +} + +static s32 __usb_open(pbcallback cb) +{ + if(__usbdev.openstate!=0x0004) return -1; + + __usbdev.closecb = cb; + __usbdev.openstate = 2; + + __issue_intrread(); + __issue_bulkread(); + + __wait4hci = 0; + return 0; +} + +void __ntd_set_ohci(u8 hci) +{ + if(hci==0x0000) { + __ntd_ohci = 0; + __ntd_ohci_initflag = 1; + } else if(hci==0x0001) { + __ntd_ohci = 1; + __ntd_ohci_initflag = 1; + } +} + +void __ntd_set_pid_vid(u16 vid,u16 pid) +{ + __ntd_vid = vid; + __ntd_pid = pid; + __ntd_vid_pid_specified = 1; +} + + +void physbusif_init() +{ + s32 ret; + + ret = __usb_register(NULL); + if(ret<0) return; + + __usb_open(NULL); +} + +void physbusif_close() +{ + if(__usbdev.openstate!=0x0002) return; + + __usbdev.openstate = 4; + __wait4hci = 1; +} + +void physbusif_shutdown() +{ + if(__usbdev.openstate!=0x0004) return; + USB_CloseDeviceAsync(&__usbdev.fd,__usb_closeCB,NULL); +} + +void physbusif_reset_all() +{ + return; +} + +void physbusif_output(struct pbuf *p,u16_t len) +{ + u32 pos; + u8 *ptr; + struct pbuf *q; + struct memb_blks *mblks; + struct usbtxbuf *blkbuf; + + if(__usbdev.openstate!=0x0002) return; + + if(((u8*)p->payload)[0]==HCI_COMMAND_DATA_PACKET) mblks = &ctrlbufs; + else if(((u8*)p->payload)[0]==HCI_ACL_DATA_PACKET) mblks = &aclbufs; + else return; + + blkbuf = btmemb_alloc(mblks); + if(blkbuf!=NULL) { + blkbuf->txsize = --len; + blkbuf->rpData = (void*)ROUNDUP32(((u32)blkbuf+sizeof(struct usbtxbuf))); + + ptr = blkbuf->rpData; + for(q=p,pos=1;q!=NULL && len>0;q=q->next,pos=0) { + memcpy(ptr,q->payload+pos,(q->len-pos)); + ptr += (q->len-pos); + len -= (q->len-pos); + } + + if(((u8*)p->payload)[0]==HCI_COMMAND_DATA_PACKET) { + USB_WriteCtrlMsgAsync(__usbdev.fd,0x20,0,0,0,blkbuf->txsize,blkbuf->rpData,__writectrlmsgCB,blkbuf); + } else if(((u8*)p->payload)[0]==HCI_ACL_DATA_PACKET) { + USB_WriteBlkMsgAsync(__usbdev.fd,__usbdev.acl_out,blkbuf->txsize,blkbuf->rpData,__writebulkmsgCB,blkbuf); + } + } +} diff --git a/wii/libogc/lwbt/physbusif.h b/wii/libogc/lwbt/physbusif.h new file mode 100644 index 0000000000..8565c5a690 --- /dev/null +++ b/wii/libogc/lwbt/physbusif.h @@ -0,0 +1,29 @@ +#ifndef __PHYSBUSIF_H__ +#define __PHYSBUSIF_H__ + +#include "btpbuf.h" + +typedef struct _usb_p usb_p; +typedef s32 (*pbcallback)(s32 state,s32 result,usb_p *usb); + +struct _usb_p +{ + s32 fd; + u8 acl_out; + u8 acl_in; + u8 hci_evt; + u8 hci_ctrl; + u32 vid; + u32 pid; + u8 openstate; + pbcallback closecb; + pbcallback unregcb; +}; + +void physbusif_init(); +void physbusif_close(); +void physbusif_shutdown(); +void physbusif_reset_all(); +void physbusif_output(struct pbuf *p,u16_t len); + +#endif diff --git a/wii/libogc/lwip/arch/gc/netif/gcif.c b/wii/libogc/lwip/arch/gc/netif/gcif.c new file mode 100644 index 0000000000..31cbb90257 --- /dev/null +++ b/wii/libogc/lwip/arch/gc/netif/gcif.c @@ -0,0 +1,1042 @@ +#include +#include "asm.h" +#include "processor.h" +#include "spinlock.h" +#include "irq.h" +#include "exi.h" +#include "cache.h" +#include "ogcsys.h" +#include "lwp.h" +#include "lwp_threads.h" +#include "lwp_watchdog.h" +#include "lwip/debug.h" +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "netif/etharp.h" + +#include "netif/gcif/gcif.h" + +//#define _BBA_DEBUG + +#define IFNAME0 'e' +#define IFNAME1 '0' + +#define GCIF_TX_TQ 8 +#define GCIF_EXI_TQ 9 + +#define BBA_MINPKTSIZE 60 + +#define BBA_CID 0x04020200 + +#define BBA_CMD_IRMASKALL 0x00 +#define BBA_CMD_IRMASKNONE 0xF8 + +#define BBA_NCRA 0x00 /* Network Control Register A, RW */ +#define BBA_NCRA_RESET (1<<0) /* RESET */ +#define BBA_NCRA_ST0 (1<<1) /* ST0, Start transmit command/status */ +#define BBA_NCRA_ST1 (1<<2) /* ST1, " */ +#define BBA_NCRA_SR (1<<3) /* SR, Start Receive */ + +#define BBA_NCRB 0x01 /* Network Control Register B, RW */ +#define BBA_NCRB_PR (1<<0) /* PR, Promiscuous Mode */ +#define BBA_NCRB_CA (1<<1) /* CA, Capture Effect Mode */ +#define BBA_NCRB_PM (1<<2) /* PM, Pass Multicast */ +#define BBA_NCRB_PB (1<<3) /* PB, Pass Bad Frame */ +#define BBA_NCRB_AB (1<<4) /* AB, Accept Broadcast */ +#define BBA_NCRB_HBD (1<<5) /* HBD, reserved */ +#define BBA_NCRB_RXINTC0 (1<<6) /* RXINTC, Receive Interrupt Counter */ +#define BBA_NCRB_RXINTC1 (1<<7) /* " */ +#define BBA_NCRB_1_PACKET_PER_INT (0<<6) /* 0 0 */ +#define BBA_NCRB_2_PACKETS_PER_INT (1<<6) /* 0 1 */ +#define BBA_NCRB_4_PACKETS_PER_INT (2<<6) /* 1 0 */ +#define BBA_NCRB_8_PACKETS_PER_INT (3<<6) /* 1 1 */ + +#define BBA_LTPS 0x04 /* Last Transmitted Packet Status, RO */ +#define BBA_LRPS 0x05 /* Last Received Packet Status, RO */ + +#define BBA_IMR 0x08 /* Interrupt Mask Register, RW, 00h */ +#define BBA_IMR_FRAGIM (1<<0) /* FRAGIM, Fragment Counter Int Mask */ +#define BBA_IMR_RIM (1<<1) /* RIM, Receive Interrupt Mask */ +#define BBA_IMR_TIM (1<<2) /* TIM, Transmit Interrupt Mask */ +#define BBA_IMR_REIM (1<<3) /* REIM, Receive Error Interrupt Mask */ +#define BBA_IMR_TEIM (1<<4) /* TEIM, Transmit Error Interrupt Mask */ +#define BBA_IMR_FIFOEIM (1<<5) /* FIFOEIM, FIFO Error Interrupt Mask */ +#define BBA_IMR_BUSEIM (1<<6) /* BUSEIM, BUS Error Interrupt Mask */ +#define BBA_IMR_RBFIM (1<<7) /* RBFIM, RX Buffer Full Interrupt Mask */ + +#define BBA_IR 0x09 /* Interrupt Register, RW, 00h */ +#define BBA_IR_FRAGI (1<<0) /* FRAGI, Fragment Counter Interrupt */ +#define BBA_IR_RI (1<<1) /* RI, Receive Interrupt */ +#define BBA_IR_TI (1<<2) /* TI, Transmit Interrupt */ +#define BBA_IR_REI (1<<3) /* REI, Receive Error Interrupt */ +#define BBA_IR_TEI (1<<4) /* TEI, Transmit Error Interrupt */ +#define BBA_IR_FIFOEI (1<<5) /* FIFOEI, FIFO Error Interrupt */ +#define BBA_IR_BUSEI (1<<6) /* BUSEI, BUS Error Interrupt */ +#define BBA_IR_RBFI (1<<7) /* RBFI, RX Buffer Full Interrupt */ + +#define BBA_BP 0x0a/*+0x0b*/ /* Boundary Page Pointer Register */ +#define BBA_TLBP 0x0c/*+0x0d*/ /* TX Low Boundary Page Pointer Register */ +#define BBA_TWP 0x0e/*+0x0f*/ /* Transmit Buffer Write Page Pointer Register */ +#define BBA_TRP 0x12/*+0x13*/ /* Transmit Buffer Read Page Pointer Register */ +#define BBA_RWP 0x16/*+0x17*/ /* Receive Buffer Write Page Pointer Register */ +#define BBA_RRP 0x18/*+0x19*/ /* Receive Buffer Read Page Pointer Register */ +#define BBA_RHBP 0x1a/*+0x1b*/ /* Receive High Boundary Page Pointer Register */ + +#define BBA_RXINTT 0x14/*+0x15*/ /* Receive Interrupt Timer Register */ + +#define BBA_NAFR_PAR0 0x20 /* Physical Address Register Byte 0 */ +#define BBA_NAFR_PAR1 0x21 /* Physical Address Register Byte 1 */ +#define BBA_NAFR_PAR2 0x22 /* Physical Address Register Byte 2 */ +#define BBA_NAFR_PAR3 0x23 /* Physical Address Register Byte 3 */ +#define BBA_NAFR_PAR4 0x24 /* Physical Address Register Byte 4 */ +#define BBA_NAFR_PAR5 0x25 /* Physical Address Register Byte 5 */ + +#define BBA_NWAYC 0x30 /* NWAY Configuration Register, RW, 84h */ +#define BBA_NWAYC_FD (1<<0) /* FD, Full Duplex Mode */ +#define BBA_NWAYC_PS100 (1<<1) /* PS100/10, Port Select 100/10 */ +#define BBA_NWAYC_ANE (1<<2) /* ANE, Autonegotiation Enable */ +#define BBA_NWAYC_ANS_RA (1<<3) /* ANS, Restart Autonegotiation */ +#define BBA_NWAYC_LTE (1<<7) /* LTE, Link Test Enable */ + +#define BBA_HALF_100 (BBA_NWAYC_PS100) +#define BBA_FULL_100 (BBA_NWAYC_PS100|BBA_NWAYC_FD) +#define BBA_HALF_10 (BBA_NWAYC_ANE) +#define BBA_FULL_10 (BBA_NWAYC_FD) + +#define BBA_NWAYS 0x31 +#define BBA_NWAYS_LS10 (1<<0) +#define BBA_NWAYS_LS100 (1<<1) +#define BBA_NWAYS_LPNWAY (1<<2) +#define BBA_NWAYS_ANCLPT (1<<3) +#define BBA_NWAYS_100TXF (1<<4) +#define BBA_NWAYS_100TXH (1<<5) +#define BBA_NWAYS_10TXF (1<<6) +#define BBA_NWAYS_10TXH (1<<7) + +#define BBA_GCA 0x32 /* GMAC Configuration A Register, RW, 00h */ +#define BBA_GCA_ARXERRB (1<<3) /* ARXERRB, Accept RX packet with error */ +#define BBA_GCA_TXFIFOCNTEN (1<<6) /* TX FIFO cnt enable */ + +#define BBA_MISC 0x3d /* MISC Control Register 1, RW, 3ch */ +#define BBA_MISC_BURSTDMA (1<<0) +#define BBA_MISC_DISLDMA (1<<1) + +#define BBA_TXFIFOCNT 0x3e/*0x3f*/ /* Transmit FIFO Counter Register */ +#define BBA_WRTXFIFOD 0x48/*-0x4b*/ /* Write TX FIFO Data Port Register */ + +#define BBA_MISC2 0x50 /* MISC Control Register 2, RW, 00h */ +#define BBA_MISC2_HBRLEN0 (1<<0) /* HBRLEN, Host Burst Read Length */ +#define BBA_MISC2_HBRLEN1 (1<<1) /* " */ +#define BBA_MISC2_RUNTSIZE (1<<2) /* " */ +#define BBA_MISC2_DREQBCTRL (1<<3) /* " */ +#define BBA_MISC2_RINTSEL (1<<4) /* " */ +#define BBA_MISC2_ITPSEL (3<<5) /* " */ +#define BBA_MISC2_AUTORCVR (1<<7) /* Auto RX Full Recovery */ + +#define BBA_RX_STATUS_BF (1<<0) +#define BBA_RX_STATUS_CRC (1<<1) +#define BBA_RX_STATUS_FAE (1<<2) +#define BBA_RX_STATUS_FO (1<<3) +#define BBA_RX_STATUS_RW (1<<4) +#define BBA_RX_STATUS_MF (1<<5) +#define BBA_RX_STATUS_RF (1<<6) +#define BBA_RX_STATUS_RERR (1<<7) + +#define BBA_TX_STATUS_CC0 (1<<0) +#define BBA_TX_STATUS_CC1 (1<<1) +#define BBA_TX_STATUS_CC2 (1<<2) +#define BBA_TX_STATUS_CC3 (1<<3) +#define BBA_TX_STATUS_CCMASK (0x0f) +#define BBA_TX_STATUS_CRSLOST (1<<4) +#define BBA_TX_STATUS_UF (1<<5) +#define BBA_TX_STATUS_OWC (1<<6) +#define BBA_TX_STATUS_OWN (1<<7) +#define BBA_TX_STATUS_TERR (1<<7) + +#define BBA_SI_ACTRL 0x5c +#define BBA_SI_STATUS 0x5d +#define BBA_SI_ACTRL2 0x60 + +#define BBA_INIT_TLBP 0x00 +#define BBA_INIT_BP 0x01 +#define BBA_INIT_RHBP 0x0f +#define BBA_INIT_RWP BBA_INIT_BP +#define BBA_INIT_RRP BBA_INIT_BP + +#define BBA_TX_MAX_PACKET_SIZE (1518) /* 14+1500+4 */ +#define BBA_RX_MAX_PACKET_SIZE (1536) /* 6 pages * 256 bytes */ + +#define BBA_NAPI_WEIGHT 16 + + +#define X(a,b) b,a +struct bba_descr { + u32 X(X(next_packet_ptr:12, packet_len:12), status:8); +} __attribute((packed)); + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + + +struct bba_priv { + u8 flag; + u8 revid; + u16 devid; + u8 acstart; + u8 linkstate; + lwpq_t tq_xmit; + err_t state; + struct eth_addr *ethaddr; + struct dev_stats { + u32 rx_errors,rx_overerrors,rx_crcerrors; + u32 rx_fifoerrors,rx_lengtherrors,rx_frameerrors; + u32 rx_bytes,rx_packets; + u32 tx_errors,tx_carriererrors,tx_fifoerrors; + u32 tx_windowerrors,tx_collisions; + } txrx_stats; +}; + +static lwpq_t wait_exi_queue; +static struct bba_descr cur_descr; +static struct netif *gc_netif = NULL; +static const struct eth_addr ethbroadcast = {{0xffU,0xffU,0xffU,0xffU,0xffU,0xffU}}; + +static err_t __bba_link_tx(struct netif *dev,struct pbuf *p); +static u32 __bba_rx_err(u8 status,struct bba_priv *priv); + +extern void udelay(int us); + +/* new functions */ +#define bba_select() EXI_Select(EXI_CHANNEL_0,EXI_DEVICE_2,EXI_SPEED32MHZ) +#define bba_deselect() EXI_Deselect(EXI_CHANNEL_0) +#define bba_sync() EXI_Sync(EXI_CHANNEL_0) + +#define bba_in12(reg) ((bba_in8((reg))&0xff)|((bba_in8(((reg)+1))&0x0f)<<8)) +#define bba_out12(reg,val) do { \ + bba_out8((reg),((val)&0xff)); \ + bba_out8(((reg)+1),(((val)&0x0f00)>>8)); \ + } while(0) +#define bba_in16(reg) ((bba_in8((reg))&0xff)|((bba_in8(((reg)+1))&0xff)<<8)) +#define bba_out16(reg,val) do { \ + bba_out8((reg),((val)&0xff)); \ + bba_out8(((reg)+1),(((val)&0xff00)>>8)); \ + } while(0) + +static __inline__ void bba_cmd_insnosel(u32 reg,void *val,u32 len) +{ + u16 req; + req = reg<<8; + EXI_Imm(EXI_CHANNEL_0,&req,sizeof(req),EXI_WRITE,NULL); + EXI_Sync(EXI_CHANNEL_0); + EXI_ImmEx(EXI_CHANNEL_0,val,len,EXI_READ); +} + +static __inline__ void bba_cmd_outsnosel(u32 reg,void *val,u32 len) +{ + u16 req; + req = (reg<<8)|0x4000; + EXI_Imm(EXI_CHANNEL_0,&req,sizeof(req),EXI_WRITE,NULL); + EXI_Sync(EXI_CHANNEL_0); + EXI_ImmEx(EXI_CHANNEL_0,val,len,EXI_WRITE); +} + +static __inline__ void bba_insnosel(u32 reg,void *val,u32 len) +{ + u32 req; + req = (reg<<8)|0x80000000; + EXI_Imm(EXI_CHANNEL_0,&req,sizeof(req),EXI_WRITE,NULL); + EXI_Sync(EXI_CHANNEL_0); + EXI_ImmEx(EXI_CHANNEL_0,val,len,EXI_READ); +} + +static __inline__ void bba_outsnosel(u32 reg,void *val,u32 len) +{ + u32 req; + req = (reg<<8)|0xC0000000; + EXI_Imm(EXI_CHANNEL_0,&req,sizeof(req),EXI_WRITE,NULL); + EXI_Sync(EXI_CHANNEL_0); + EXI_ImmEx(EXI_CHANNEL_0,val,len,EXI_WRITE); +} + +static __inline__ void bba_insregister(u32 reg) +{ + u32 req; + req = (reg<<8)|0x80000000; + EXI_Imm(EXI_CHANNEL_0,&req,sizeof(req),EXI_WRITE,NULL); + EXI_Sync(EXI_CHANNEL_0); +} + +static __inline__ void bba_insdata(void *val,u32 len) +{ + EXI_ImmEx(EXI_CHANNEL_0,val,len,EXI_READ); +} + +static __inline__ void bba_insdmadata(void *val,u32 len,s32 (*dmasubrcv)(s32 chn,s32 dev)) +{ + EXI_Dma(EXI_CHANNEL_0,val,len,EXI_READ,dmasubrcv); +} + +static void bba_insdata_fast(void *val,s32 len) +{ + u32 roundlen; + s32 missalign; + u8 *ptr = val; + + if(!val || len<=0) return; + + missalign = -((u32)val)&0x1f; + if((s32)(len-missalign)<32) { + bba_insdata(val,len); + return; + } + + if(missalign>0) { + bba_insdata(ptr,missalign); + len -= missalign; + ptr += missalign; + } + + roundlen = (len&~0x1f); + DCInvalidateRange(ptr,roundlen); + bba_insdmadata(ptr,roundlen,NULL); + bba_sync(); + + len -= roundlen; + ptr += roundlen; + if(len>0) bba_insdata(ptr,len); +} + +static __inline__ void bba_outsregister(u32 reg) +{ + u32 req; + req = (reg<<8)|0xC0000000; + EXI_Imm(EXI_CHANNEL_0,&req,sizeof(req),EXI_WRITE,NULL); + EXI_Sync(EXI_CHANNEL_0); +} + +static __inline__ void bba_outsdata(void *val,u32 len) +{ + EXI_ImmEx(EXI_CHANNEL_0,val,len,EXI_WRITE); +} + +static __inline__ void bba_outsdmadata(void *val,u32 len,s32 (*dmasubsnd)(s32 chn,s32 dev)) +{ + EXI_Dma(EXI_CHANNEL_0,val,len,EXI_WRITE,dmasubsnd); +} + +static void bba_outsdata_fast(void *val,s32 len) +{ + u32 roundlen; + s32 missalign; + u8 *ptr = val; + + if(!val || len<=0) return; + + missalign = -((u32)val)&0x1f; + if((s32)(len-missalign)<32) { + bba_outsdata(val,len); + return; + } + + if(missalign>0) { + bba_outsdata(ptr,missalign); + len -= missalign; + ptr += missalign; + } + + roundlen = (len&~0x1f); + DCStoreRange(ptr,roundlen); + bba_outsdmadata(ptr,roundlen,NULL); + bba_sync(); + + len -= roundlen; + ptr += roundlen; + if(len>0) bba_outsdata(ptr,len); +} + +static inline void bba_cmd_ins(u32 reg,void *val,u32 len) +{ + bba_select(); + bba_cmd_insnosel(reg,val,len); + bba_deselect(); +} + +static inline void bba_cmd_outs(u32 reg,void *val,u32 len) +{ + bba_select(); + bba_cmd_outsnosel(reg,val,len); + bba_deselect(); +} + +static inline u8 bba_cmd_in8(u32 reg) +{ + u8 val; + bba_cmd_ins(reg,&val,sizeof(val)); + return val; +} + +static inline u8 bba_cmd_in8_slow(u32 reg) +{ + u8 val; + bba_select(); + bba_cmd_insnosel(reg,&val,sizeof(val)); + udelay(200); //usleep doesn't work on this amount, decrementer is based on 10ms, wait is 200us + bba_deselect(); + return val; +} + +static inline void bba_cmd_out8(u32 reg,u8 val) +{ + bba_cmd_outs(reg,&val,sizeof(val)); +} + +static inline void bba_ins(u32 reg,void *val,u32 len) +{ + bba_select(); + bba_insnosel(reg,val,len); + bba_deselect(); +} + + +static inline void bba_outs(u32 reg,void *val,u32 len) +{ + bba_select(); + bba_outsnosel(reg,val,len); + bba_deselect(); +} + +static inline u8 bba_in8(u32 reg) +{ + u8 val; + bba_ins(reg,&val,sizeof(val)); + return val; +} + +static inline void bba_out8(u32 reg,u8 val) +{ + bba_outs(reg,&val,sizeof(val)); +} + +static s32 __bba_exi_unlockcb(s32 chn,s32 dev) +{ + LWP_ThreadBroadcast(wait_exi_queue); + return 1; +} + +static __inline__ void __bba_exi_stop(struct bba_priv *priv) +{ + u32 level; + + _CPU_ISR_Disable(level); + while(EXI_Lock(EXI_CHANNEL_0,EXI_DEVICE_2,__bba_exi_unlockcb)==0) { + LWIP_DEBUGF(NETIF_DEBUG|1,("__bba_exi_wait(exi locked)\n")); + LWP_ThreadSleep(wait_exi_queue); + } + _CPU_ISR_Restore(level); +} + +static __inline__ void __bba_exi_wake(struct bba_priv *priv) +{ + EXI_Unlock(EXI_CHANNEL_0); +} + +static __inline__ void __bba_tx_stop(struct bba_priv *priv) +{ + u32 level; + + _CPU_ISR_Disable(level); + while(priv->state==ERR_TXPENDING) { + LWIP_DEBUGF(NETIF_DEBUG,("__bba_tx_stop(pending tx)\n")); + LWP_ThreadSleep(priv->tq_xmit); + } + priv->state = ERR_TXPENDING; + _CPU_ISR_Restore(level); +} + +static __inline__ void __bba_tx_wake(struct bba_priv *priv) +{ + u32 level; + + _CPU_ISR_Disable(level); + if(priv->state==ERR_TXPENDING) { + priv->state = ERR_OK; + LWP_ThreadBroadcast(priv->tq_xmit); + } + _CPU_ISR_Restore(level); +} + +static __inline__ u8 __linkstate(struct bba_priv *priv) +{ + u8 nways = 0; + + nways = bba_in8(BBA_NWAYS); + priv->linkstate = nways; + if(nways&BBA_NWAYS_LS10 || nways&BBA_NWAYS_LS100) return nways; + return 0; +} + +static bool __bba_get_linkstateasync(struct bba_priv *priv) +{ + u32 ret,cnt,sec; + + for(cnt=0;cnt<10000;cnt++) { + udelay(500); + ret = __linkstate(priv); + + if(ret&0xf0 && ret&0x08) break; + } + + // only sleep for additional 2 seconds if linkstate is ok + if(cnt<10000) { + sec = 1; + if(!(ret&0x04)) sec = 2; + udelay(sec*TB_USPERSEC); + } + + return (cnt<10000); +} + +static u32 __bba_read_cid() +{ + u16 cmd = 0; + u32 cid = 0; + + bba_select(); + EXI_Imm(EXI_CHANNEL_0,&cmd,2,EXI_WRITE,NULL); + EXI_Sync(EXI_CHANNEL_0); + EXI_Imm(EXI_CHANNEL_0,&cid,4,EXI_READ,NULL); + EXI_Sync(EXI_CHANNEL_0); + bba_deselect(); + + return cid; +} + +static void __bba_reset() +{ + bba_out8(0x60,0x00); + udelay(10000); + bba_cmd_in8_slow(0x0F); + udelay(10000); + bba_out8(BBA_NCRA,BBA_NCRA_RESET); + udelay(100); + bba_out8(BBA_NCRA,0x00); + udelay(100); +} + +static void __bba_recv_init() +{ + bba_out8(BBA_NCRB,(BBA_NCRB_AB|BBA_NCRB_CA|BBA_NCRB_2_PACKETS_PER_INT)); + bba_out8(BBA_SI_ACTRL2,0x74); + bba_out8(BBA_RXINTT, 0x00); + bba_out8(BBA_RXINTT+1, 0x06); /* 0x0600 = 61us */ + + bba_out8(BBA_MISC2,BBA_MISC2_AUTORCVR); + + bba_out12(BBA_TLBP, BBA_INIT_TLBP); + bba_out12(BBA_BP,BBA_INIT_BP); + bba_out12(BBA_RHBP,BBA_INIT_RHBP); + bba_out12(BBA_RWP,BBA_INIT_RWP); + bba_out12(BBA_RRP,BBA_INIT_RRP); + + bba_out8(BBA_GCA,BBA_GCA_ARXERRB); + bba_out8(BBA_NCRA,BBA_NCRA_SR); +} + +static u32 __bba_tx_err(u8 status,struct bba_priv *priv) +{ + u32 last_errors = priv->txrx_stats.tx_errors; + + if(status&BBA_TX_STATUS_TERR) { + priv->txrx_stats.tx_errors++; + if(status&BBA_TX_STATUS_CCMASK) + priv->txrx_stats.tx_collisions += (status&BBA_TX_STATUS_CCMASK); + + if(status&BBA_TX_STATUS_CRSLOST) + priv->txrx_stats.tx_carriererrors++; + + if(status&BBA_TX_STATUS_UF) + priv->txrx_stats.tx_fifoerrors++; + + if(status&BBA_TX_STATUS_OWC) + priv->txrx_stats.tx_windowerrors++; + } + if(last_errors!=priv->txrx_stats.tx_errors) + LWIP_ERROR(("__bba_tx_err(%02x)\n",status)); + + return priv->txrx_stats.tx_errors; +} + +static u32 __bba_rx_err(u8 status,struct bba_priv *priv) +{ + u32 last_errors = priv->txrx_stats.tx_errors; + + if(status&0xff) { + priv->txrx_stats.rx_overerrors++; + priv->txrx_stats.rx_errors++; + } else { + if(status&BBA_RX_STATUS_RERR) { + priv->txrx_stats.rx_errors++; + if(status&BBA_RX_STATUS_BF) + priv->txrx_stats.rx_overerrors++; + + if(status&BBA_RX_STATUS_CRC) + priv->txrx_stats.rx_crcerrors++; + + if(status&BBA_RX_STATUS_FO) + priv->txrx_stats.rx_fifoerrors++; + + if(status&BBA_RX_STATUS_RW) + priv->txrx_stats.rx_lengtherrors++; + + if(status&BBA_RX_STATUS_RF) + priv->txrx_stats.rx_lengtherrors++; + + if(status&BBA_RX_STATUS_BF) + priv->txrx_stats.rx_overerrors++; + } + if(status&BBA_RX_STATUS_FAE) { + priv->txrx_stats.rx_errors++; + priv->txrx_stats.rx_frameerrors++; + } + } + if(last_errors!=priv->txrx_stats.rx_errors) + LWIP_ERROR(("__bba_rx_err(%02x)\n",status)); + + return priv->txrx_stats.rx_errors; +} + +void bba_process(struct pbuf *p,struct netif *dev) +{ + struct eth_hdr *ethhdr = NULL; + struct bba_priv *priv = (struct bba_priv*)dev->state; + const s32 hlen = sizeof(struct eth_hdr); + + if(p) { + ethhdr = p->payload; + switch(htons(ethhdr->type)) { + case ETHTYPE_IP: + LWIP_DEBUGF(NETIF_DEBUG,("bba_process: passing packet up to IP layer\n")); + + etharp_ip_input(dev,p); + pbuf_header(p,-(hlen)); + ip_input(p,dev); + break; + case ETHTYPE_ARP: + /* pass p to ARP module, get ARP reply or ARP queued packet */ + LWIP_DEBUGF(NETIF_DEBUG,("bba_process: passing packet up to ARP layer\n")); + etharp_arp_input(dev, priv->ethaddr, p); + break; + /* unsupported Ethernet packet type */ + default: + /* free pbuf */ + pbuf_free(p); + break; + } + } +} + +static err_t __bba_link_tx(struct netif *dev,struct pbuf *p) +{ + u8 tmpbuf[BBA_MINPKTSIZE]; + struct pbuf *tmp; + struct bba_priv *priv = (struct bba_priv*)dev->state; + + __bba_tx_stop(priv); + __bba_exi_stop(priv); + + if(p->tot_len>BBA_TX_MAX_PACKET_SIZE) { + LWIP_ERROR(("__bba_link_tx(%d,%p) pkt_size\n",p->tot_len,LWP_GetSelf())); + __bba_tx_wake(priv); + __bba_exi_wake(priv); + return ERR_PKTSIZE; + } + + if(!__linkstate(priv)) { + LWIP_ERROR(("__bba_link_tx(error link state)\n")); + __bba_tx_wake(priv); + __bba_exi_wake(priv); + return ERR_ABRT; + } + + LWIP_DEBUGF(NETIF_DEBUG,("__bba_link_tx(%d,%p)\n",p->tot_len,LWP_GetSelf())); + + bba_out12(BBA_TXFIFOCNT,p->tot_len); + + bba_select(); + bba_outsregister(BBA_WRTXFIFOD); + for(tmp=p;tmp!=NULL;tmp=tmp->next) { + bba_outsdata_fast(tmp->payload,tmp->len); + } + if(p->tot_lentot_len)); + bba_deselect(); + + bba_out8(BBA_NCRA,((bba_in8(BBA_NCRA)&~BBA_NCRA_ST0)|BBA_NCRA_ST1)); //&~BBA_NCRA_ST0 + __bba_exi_wake(priv); + return ERR_OK; +} + +static err_t __bba_start_tx(struct netif *dev,struct pbuf *p,struct ip_addr *ipaddr) +{ + LWIP_DEBUGF(NETIF_DEBUG,("__bba_start_tx(%p)\n",LWP_GetSelf())); + return etharp_output(dev,ipaddr,p); +} + +static err_t bba_start_rx(struct netif *dev,u32 budget) +{ + s32 size; + u16 top,pos,rrp,rwp; + u32 pkt_status,recvd; + struct pbuf *tmp,*p = NULL; + struct bba_priv *priv = (struct bba_priv*)dev->state; + + LWIP_DEBUGF(NETIF_DEBUG,("bba_start_rx()\n")); + + recvd = 0; + rwp = bba_in12(BBA_RWP); + rrp = bba_in12(BBA_RRP); + while(recvd(BBA_RX_MAX_PACKET_SIZE+4)) { + LWIP_DEBUGF(NETIF_DEBUG|2,("bba_start_rx(size>BBA_RX_MAX_PACKET_SIZE)\n")); + continue; + } + + if(pkt_status&(BBA_RX_STATUS_RERR|BBA_RX_STATUS_FAE)) { + LWIP_DEBUGF(NETIF_DEBUG|2,("bba_start_rx(pkt_status = 02x)\n",pkt_status)); + __bba_rx_err(pkt_status,priv); + rwp = bba_in12(BBA_RWP); + rrp = bba_in12(BBA_RRP); + continue; + } + + pos = ((rrp<<8)+4); + top = ((BBA_INIT_RHBP+1)<<8); + LWIP_DEBUGF(NETIF_DEBUG,("bba_start_rx(%04x,%d,%04x)\n",pos,size,top)); + + p = pbuf_alloc(PBUF_RAW,size,PBUF_POOL); + if(p) { + for(tmp=p;tmp!=NULL;tmp=tmp->next) { + size = tmp->len; + + bba_select(); + bba_insregister(pos); + if((pos+size)payload,size); + } else { + s32 chunk_size = (top-pos); + + size -= chunk_size; + pos = (BBA_INIT_RRP<<8); + bba_insdata_fast(tmp->payload,chunk_size); + bba_deselect(); + bba_select(); + bba_insregister(pos); + bba_insdata_fast(tmp->payload+chunk_size,size); + } + bba_deselect(); + pos += size; + } + dev->input(p,dev); + } else + break; + + recvd++; + bba_out12(BBA_RRP,(rrp=cur_descr.next_packet_ptr)); + rwp = bba_in12(BBA_RWP); + } + if(priv->flag&BBA_IR_RBFI) { + priv->flag &= ~BBA_IR_RBFI; + bba_out8(BBA_IMR,(bba_in8(BBA_IMR)|BBA_IMR_RBFIM)); + } + + LWIP_DEBUGF(NETIF_DEBUG,("bba_start_rx(rx interrupt close)\n")); + return ERR_OK; +} + +static inline void bba_interrupt(struct netif *dev) +{ + u8 ir,imr,status,lrps,ltps; + struct bba_priv *priv = (struct bba_priv*)dev->state; + + ir = bba_in8(BBA_IR); + imr = bba_in8(BBA_IMR); + status = ir&imr; + while(status) { + LWIP_DEBUGF(NETIF_DEBUG,("bba_interrupt(%02x)\n",status)); + bba_out8(BBA_IR,status); + if(status&BBA_IR_RBFI) { + bba_out8(BBA_IMR,(bba_in8(BBA_IMR)&~BBA_IMR_RBFIM)); + priv->flag |= BBA_IMR_RBFIM; + } + if(status&(BBA_IR_RI|BBA_IR_RBFI)) { + bba_start_rx(dev,0x20); + } + if(status&(BBA_IR_TI|BBA_IR_FIFOEI)) { + __bba_tx_wake(priv); + } + if(status&(BBA_IR_RBFI|BBA_IR_REI)) { + lrps = bba_in8(BBA_LRPS); + __bba_rx_err(lrps,priv); + } + if(status&BBA_IR_TEI) { + ltps = bba_in8(BBA_LTPS); + __bba_tx_err(ltps,priv); + __bba_tx_wake(priv); + } + if(status&BBA_IR_FRAGI) { + LWIP_DEBUGF(NETIF_DEBUG,("bba_interrupt(BBA_IR_FRAGI)\n")); + } + if(status&BBA_IR_FIFOEI) { + LWIP_DEBUGF(NETIF_DEBUG,("bba_interrupt(BBA_IR_FIFOEI)\n")); + } + if(status&BBA_IR_BUSEI) { + LWIP_DEBUGF(NETIF_DEBUG,("bba_interrupt(BBA_IR_BUSEI)\n")); + } + + ir = bba_in8(BBA_IR); + imr = bba_in8(BBA_IMR); + status = ir&imr; + } + LWIP_DEBUGF(NETIF_DEBUG,("bba_interrupt(exit)\n")); +} + +static err_t __bba_init(struct netif *dev) +{ + u8 nwayc; + struct bba_priv *priv = (struct bba_priv*)dev->state; + + if(!priv) return ERR_IF; + + LWIP_DEBUGF(NETIF_DEBUG,("initializing BBA...\n")); + bba_cmd_out8(0x02,BBA_CMD_IRMASKALL); + + __bba_reset(); + + priv->revid = bba_cmd_in8(0x01); + + bba_cmd_outs(0x04,&priv->devid,2); + bba_cmd_out8(0x05,priv->acstart); + + /* Assume you are being started by something which has fucked NWAY! + So reset to power on defaults for SIACTRL/SIACONN */ + bba_out8(0x58, 0x80); + bba_out8(0x59, 0x00); + bba_out8(0x5a, 0x03); + bba_out8(0x5b, 0x83); + bba_out8(0x5c, 0x32); + bba_out8(0x5d, 0xfe); + bba_out8(0x5e, 0x1f); + bba_out8(0x5f, 0x1f); + udelay(100); + + __bba_recv_init(); + + /* This doesn't set the speed anymore - it simple kicks off NWAY */ + nwayc = bba_in8(BBA_NWAYC)&0xc0; + bba_out8(BBA_NWAYC,nwayc); + udelay(100); + nwayc |= 0x04; + bba_out8(BBA_NWAYC,nwayc); + udelay(100); + nwayc |= 0x08; + bba_out8(BBA_NWAYC,nwayc); + udelay(100); + + bba_ins(BBA_NAFR_PAR0,priv->ethaddr->addr, 6); + LWIP_DEBUGF(NETIF_DEBUG,("MAC ADDRESS %02x:%02x:%02x:%02x:%02x:%02x\n", + priv->ethaddr->addr[0], priv->ethaddr->addr[1], priv->ethaddr->addr[2], + priv->ethaddr->addr[3], priv->ethaddr->addr[4], priv->ethaddr->addr[5])); + + bba_out8(BBA_IR,0xFF); + bba_out8(BBA_IMR,0xFF&~BBA_IMR_FIFOEIM); + + bba_cmd_out8(0x02,BBA_CMD_IRMASKNONE); + + return ERR_OK; +} + +static err_t bba_init_one(struct netif *dev) +{ + struct bba_priv *priv = (struct bba_priv*)dev->state; + + if(!priv) return ERR_IF; + + priv->revid = 0xf0; + priv->devid = 0xD107; + priv->acstart = 0x4E; + + __bba_init(dev); + + return ERR_OK; +} + +static err_t bba_probe(struct netif *dev) +{ + u32 cid; + err_t ret; + + cid = __bba_read_cid(); + if(cid!=BBA_CID) return ERR_NODEV; + + ret = bba_init_one(dev); + return ret; +} + +static u32 bba_calc_response(struct bba_priv *priv,u32 val) +{ + u8 i0,i1,i2,i3; + u8 c0,c1,c2,c3; + u8 revid_0,revid_eth_0,revid_eth_1; + + revid_0 = priv->revid; + revid_eth_0 = _SHIFTR(priv->devid,8,8); + revid_eth_1 = _SHIFTR(priv->devid,0,8); + + i0 = _SHIFTR(val,24,8); + i1 = _SHIFTR(val,16,8); + i2 = _SHIFTR(val, 8,8); + i3 = _SHIFTR(val, 0,8); + + c0 = ((i0+i1*0xc1+0x18+revid_0)^(i3*i2+0x90))&0xff; + c1 = ((i1+i2+0x90)^(c0+i0-0xc1))&0xff; + c2 = ((i2+0xc8)^(c0+((revid_eth_0+revid_0*0x23)^0x19)))&0xff; + c3 = ((i0+0xc1)^(i3+((revid_eth_1+0xc8)^0x90)))&0xff; + + return ((c0<<24)|(c1<<16)|(c2<<8)|c3); +} + +static s32 bba_event_handler(s32 nChn,s32 nDev) +{ + u8 status; + struct bba_priv *priv = (struct bba_priv*)gc_netif->state; + + if(EXI_Lock(EXI_CHANNEL_0,EXI_DEVICE_2,bba_event_handler)==0) { + LWIP_DEBUGF(NETIF_DEBUG|1,("bba_event_handler(exi locked)\n")); + return 1; + } + + status = bba_cmd_in8(0x03); + bba_cmd_out8(0x02,BBA_CMD_IRMASKALL); + + LWIP_DEBUGF(NETIF_DEBUG,("bba_event_handler(status(%02x))\n",status)); + + if(status&0x80) { + LWIP_DEBUGF(NETIF_DEBUG,("bba_event_handler(bba_interrupt(%02x))\n",status)); + bba_interrupt(gc_netif); + bba_cmd_out8(0x03,0x80); + bba_cmd_out8(0x02,BBA_CMD_IRMASKNONE); + EXI_Unlock(EXI_CHANNEL_0); + return 1; + } + if(status&0x40) { + LWIP_ERROR(("bba_event_handler(bba_reset(%02x))\n",status)); + __bba_init(gc_netif); + bba_cmd_out8(0x03, 0x40); + bba_cmd_out8(0x02, BBA_CMD_IRMASKNONE); + EXI_Unlock(EXI_CHANNEL_0); + return 1; + } + if(status&0x20) { + LWIP_DEBUGF(NETIF_DEBUG,("bba_event_handler(unknown(%02x))\n",status)); + bba_cmd_out8(0x03, 0x20); + bba_cmd_out8(0x02, BBA_CMD_IRMASKNONE); + EXI_Unlock(EXI_CHANNEL_0); + return 1; + } + if(status&0x10) { + u32 response,challange; + LWIP_DEBUGF(NETIF_DEBUG,("bba_event_handler(challange/response(%02x))\n",status)); + bba_cmd_out8(0x05,priv->acstart); + bba_cmd_ins(0x08,&challange,sizeof(challange)); + response = bba_calc_response(priv,challange); + bba_cmd_outs(0x09,&response,sizeof(response)); + + bba_cmd_out8(0x03, 0x10); + bba_cmd_out8(0x02, BBA_CMD_IRMASKNONE); + EXI_Unlock(EXI_CHANNEL_0); + return 1; + } + if(status&0x08) { + LWIP_DEBUGF(NETIF_DEBUG,("bba_event_handler(challange/response status(%02x))\n",bba_cmd_in8(0x0b))); + bba_cmd_out8(0x03, 0x08); + bba_cmd_out8(0x02, BBA_CMD_IRMASKNONE); + EXI_Unlock(EXI_CHANNEL_0); + return 1; + } + LWIP_ERROR(("GCIF - EXI - ?? %02x\n", status)); + bba_interrupt(gc_netif); + bba_cmd_out8(0x02,BBA_CMD_IRMASKNONE); + EXI_Unlock(EXI_CHANNEL_0); + return 1; +} + +err_t bba_init(struct netif *dev) +{ + err_t ret; + struct bba_priv *priv = (struct bba_priv*)dev->state; + + __bba_exi_stop(priv); + + LWIP_DEBUGF(NETIF_DEBUG, ("bba_init(call EXI_RegisterEXICallback())\n")); + EXI_RegisterEXICallback(EXI_CHANNEL_2,bba_event_handler); + + ret = bba_probe(dev); + if(ret!=ERR_OK) { + EXI_RegisterEXICallback(EXI_CHANNEL_2,NULL); + __bba_exi_wake(priv); + return ret; + } + + ret = __bba_get_linkstateasync(priv); + if(ret) { + dev->flags |= NETIF_FLAG_LINK_UP; + ret = ERR_OK; + } else { + EXI_RegisterEXICallback(EXI_CHANNEL_2,NULL); + ret = ERR_IF; + } + + __bba_exi_wake(priv); + return ret; +} + +dev_s bba_create(struct netif *dev) +{ + struct bba_priv *priv = NULL; + + LWIP_DEBUGF(NETIF_DEBUG, ("bba_create()\n")); + + priv = (struct bba_priv*)mem_malloc(sizeof(struct bba_priv)); + if(!priv) { + LWIP_ERROR(("bba_create: out of memory for bba_priv\n")); + return NULL; + } + memset(priv,0,sizeof(struct bba_priv)); + + LWP_InitQueue(&priv->tq_xmit); + LWP_InitQueue(&wait_exi_queue); + + dev->name[0] = IFNAME0; + dev->name[1] = IFNAME1; + dev->output = __bba_start_tx; + dev->linkoutput = __bba_link_tx; + dev->mtu = 1500; + dev->flags = NETIF_FLAG_BROADCAST; + dev->hwaddr_len = 6; + + priv->ethaddr = (struct eth_addr*)&(dev->hwaddr[0]); + priv->state = ERR_OK; + + gc_netif = dev; + return priv; +} diff --git a/wii/libogc/lwip/core/dhcp.c b/wii/libogc/lwip/core/dhcp.c new file mode 100644 index 0000000000..8cceeee34c --- /dev/null +++ b/wii/libogc/lwip/core/dhcp.c @@ -0,0 +1,1457 @@ +/** + * @file + * + * Dynamic Host Configuration Protocol client + */ + +/* + * + * Copyright (c) 2001-2004 Leon Woestenberg + * Copyright (c) 2001-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is a contribution to the lwIP TCP/IP stack. + * The Swedish Institute of Computer Science and Adam Dunkels + * are specifically granted permission to redistribute this + * source code. + * + * Author: Leon Woestenberg + * + * This is a DHCP client for the lwIP TCP/IP stack. It aims to conform + * with RFC 2131 and RFC 2132. + * + * TODO: + * - Proper parsing of DHCP messages exploiting file/sname field overloading. + * - Add JavaDoc style documentation (API, internals). + * - Support for interfaces other than Ethernet (SLIP, PPP, ...) + * + * Please coordinate changes and requests with Leon Woestenberg + * + * + * Integration with your code: + * + * In lwip/dhcp.h + * #define DHCP_COARSE_TIMER_SECS (recommended 60 which is a minute) + * #define DHCP_FINE_TIMER_MSECS (recommended 500 which equals TCP coarse timer) + * + * Then have your application call dhcp_coarse_tmr() and + * dhcp_fine_tmr() on the defined intervals. + * + * dhcp_start(struct netif *netif); + * starts a DHCP client instance which configures the interface by + * obtaining an IP address lease and maintaining it. + * + * Use dhcp_release(netif) to end the lease and use dhcp_stop(netif) + * to remove the DHCP client. + * + */ + +#include + +#include "lwip/stats.h" +#include "lwip/mem.h" +#include "lwip/udp.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/inet.h" +#include "netif/etharp.h" + +#include "lwip/sys.h" +#include "lwip/opt.h" +#include "lwip/dhcp.h" + +#if LWIP_DHCP /* don't build if not configured for use in lwipopt.h */ + +/** global transaction identifier, must be + * unique for each DHCP request. We simply increment, starting + * with this value (easy to match with a packet analyzer) */ +static u32_t xid = 0xABCD0000; + +/** DHCP client state machine functions */ +static void dhcp_handle_ack(struct netif *netif); +static void dhcp_handle_nak(struct netif *netif); +static void dhcp_handle_offer(struct netif *netif); + +static err_t dhcp_discover(struct netif *netif); +static err_t dhcp_select(struct netif *netif); +static void dhcp_check(struct netif *netif); +static void dhcp_bind(struct netif *netif); +static err_t dhcp_decline(struct netif *netif); +static err_t dhcp_rebind(struct netif *netif); +static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state); + +/** receive, unfold, parse and free incoming messages */ +static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port); +static err_t dhcp_unfold_reply(struct dhcp *dhcp); +static u8_t *dhcp_get_option_ptr(struct dhcp *dhcp, u8_t option_type); +static u8_t dhcp_get_option_byte(u8_t *ptr); +//static u16_t dhcp_get_option_short(u8_t *ptr); +static u32_t dhcp_get_option_long(u8_t *ptr); +static void dhcp_free_reply(struct dhcp *dhcp); + +/** set the DHCP timers */ +static void dhcp_timeout(struct netif *netif); +static void dhcp_t1_timeout(struct netif *netif); +static void dhcp_t2_timeout(struct netif *netif); + +/** build outgoing messages */ +/** create a DHCP request, fill in common headers */ +static err_t dhcp_create_request(struct netif *netif); +/** free a DHCP request */ +static void dhcp_delete_request(struct netif *netif); +/** add a DHCP option (type, then length in bytes) */ +static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len); +/** add option values */ +static void dhcp_option_byte(struct dhcp *dhcp, u8_t value); +static void dhcp_option_short(struct dhcp *dhcp, u16_t value); +static void dhcp_option_long(struct dhcp *dhcp, u32_t value); +/** always add the DHCP options trailer to end and pad */ +static void dhcp_option_trailer(struct dhcp *dhcp); + +/** + * Back-off the DHCP client (because of a received NAK response). + * + * Back-off the DHCP client because of a received NAK. Receiving a + * NAK means the client asked for something non-sensible, for + * example when it tries to renew a lease obtained on another network. + * + * We back-off and will end up restarting a fresh DHCP negotiation later. + * + * @param state pointer to DHCP state structure + */ +static void dhcp_handle_nak(struct netif *netif) { + struct dhcp *dhcp = netif->dhcp; + u16_t msecs = 10 * 1000; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_handle_nak(): set request timeout %"U16_F" msecs\n", msecs)); + dhcp_set_state(dhcp, DHCP_BACKING_OFF); +} + +/** + * Checks if the offered IP address is already in use. + * + * It does so by sending an ARP request for the offered address and + * entering CHECKING state. If no ARP reply is received within a small + * interval, the address is assumed to be free for use by us. + */ +static void dhcp_check(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0], + (s16_t)netif->name[1])); + /* create an ARP query for the offered IP address, expecting that no host + responds, as the IP address should not be in use. */ + result = etharp_query(netif, &dhcp->offered_ip_addr, NULL); + if (result != ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_check: could not perform ARP query\n")); + } + dhcp->tries++; + msecs = 500; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs)); + dhcp_set_state(dhcp, DHCP_CHECKING); +} + +/** + * Remember the configuration offered by a DHCP server. + * + * @param state pointer to DHCP state structure + */ +static void dhcp_handle_offer(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + /* obtain the server address */ + u8_t *option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_SERVER_ID); + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n", + (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + if (option_ptr != NULL) + { + dhcp->server_ip_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n", dhcp->server_ip_addr.addr)); + /* remember offered address */ + ip_addr_set(&dhcp->offered_ip_addr, (struct ip_addr *)&dhcp->msg_in->yiaddr); + LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n", dhcp->offered_ip_addr.addr)); + + dhcp_select(netif); + } +} + +/** + * Select a DHCP server offer out of all offers. + * + * Simply select the first offer received. + * + * @param netif the netif under DHCP control + * @return lwIP specific error (see error.h) + */ +static err_t dhcp_select(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u32_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) + { + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_REQUEST); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, 576); + + /* MUST request the offered IP address */ + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/); + dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK); + dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST); + dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER); + + dhcp_option_trailer(dhcp); + /* shrink the pbuf to the actual content length */ + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* TODO: we really should bind to a specific local interface here + but we cannot specify an unconfigured netif as it is addressless */ + udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + /* send broadcast to any DHCP server */ + udp_connect(dhcp->pcb, IP_ADDR_BROADCAST, DHCP_SERVER_PORT); + udp_send(dhcp->pcb, dhcp->p_out); + /* reconnect to any (or to server here?!) */ + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + dhcp_delete_request(netif); + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_select: REQUESTING\n")); + dhcp_set_state(dhcp, DHCP_REQUESTING); + } else { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_select: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 4 ? dhcp->tries * 1000 : 4 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_select(): set request timeout %"U32_F" msecs\n", msecs)); + return result; +} + +/** + * The DHCP timer that checks for lease renewal/rebind timeouts. + * + */ +void dhcp_coarse_tmr() +{ + struct netif *netif = netif_list; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_coarse_tmr()\n")); + /* iterate through all network interfaces */ + while (netif != NULL) { + /* only act on DHCP configured interfaces */ + if (netif->dhcp != NULL) { + /* timer is active (non zero), and triggers (zeroes) now? */ + if (netif->dhcp->t2_timeout-- == 1) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n")); + /* this clients' rebind timeout triggered */ + dhcp_t2_timeout(netif); + /* timer is active (non zero), and triggers (zeroes) now */ + } else if (netif->dhcp->t1_timeout-- == 1) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n")); + /* this clients' renewal timeout triggered */ + dhcp_t1_timeout(netif); + } + } + /* proceed to next netif */ + netif = netif->next; + } +} + +/** + * DHCP transaction timeout handling + * + * A DHCP server is expected to respond within a short period of time. + * This timer checks whether an outstanding DHCP request is timed out. + * + */ +void dhcp_fine_tmr() +{ + struct netif *netif = netif_list; + /* loop through netif's */ + while (netif != NULL) { + /* only act on DHCP configured interfaces */ + if (netif->dhcp != NULL) { + /* timer is active (non zero), and is about to trigger now */ + if (netif->dhcp->request_timeout-- == 1) { + /* { netif->dhcp->request_timeout == 0 } */ + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_fine_tmr(): request timeout\n")); + /* this clients' request timeout triggered */ + dhcp_timeout(netif); + } + } + /* proceed to next network interface */ + netif = netif->next; + } +} + +/** + * A DHCP negotiation transaction, or ARP request, has timed out. + * + * The timer that was started with the DHCP or ARP request has + * timed out, indicating no response was received in time. + * + * @param netif the netif under DHCP control + * + */ +static void dhcp_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_timeout()\n")); + /* back-off period has passed, or server selection timed out */ + if ((dhcp->state == DHCP_BACKING_OFF) || (dhcp->state == DHCP_SELECTING)) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_timeout(): restarting discovery\n")); + dhcp_discover(netif); + /* receiving the requested lease timed out */ + } else if (dhcp->state == DHCP_REQUESTING) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n")); + if (dhcp->tries <= 5) { + dhcp_select(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n")); + dhcp_release(netif); + dhcp_discover(netif); + } + /* received no ARP reply for the offered address (which is good) */ + } else if (dhcp->state == DHCP_CHECKING) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n")); + if (dhcp->tries <= 1) { + dhcp_check(netif); + /* no ARP replies on the offered address, + looks like the IP address is indeed free */ + } else { + /* bind the interface to the offered address */ + dhcp_bind(netif); + } + } + /* did not get response to renew request? */ + else if (dhcp->state == DHCP_RENEWING) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): RENEWING, DHCP request timed out\n")); + /* just retry renewal */ + /* note that the rebind timer will eventually time-out if renew does not work */ + dhcp_renew(netif); + /* did not get response to rebind request? */ + } else if (dhcp->state == DHCP_REBINDING) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): REBINDING, DHCP request timed out\n")); + if (dhcp->tries <= 8) { + dhcp_rebind(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_timeout(): RELEASING, DISCOVERING\n")); + dhcp_release(netif); + dhcp_discover(netif); + } + } +} + +/** + * The renewal period has timed out. + * + * @param netif the netif under DHCP control + */ +static void dhcp_t1_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_t1_timeout()\n")); + if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || (dhcp->state == DHCP_RENEWING)) { + /* just retry to renew - note that the rebind timer (t2) will + * eventually time-out if renew tries fail. */ + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_t1_timeout(): must renew\n")); + dhcp_renew(netif); + } +} + +/** + * The rebind period has timed out. + * + */ +static void dhcp_t2_timeout(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_t2_timeout()\n")); + if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) || (dhcp->state == DHCP_RENEWING)) { + /* just retry to rebind */ + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_t2_timeout(): must rebind\n")); + dhcp_rebind(netif); + } +} + +/** + * + * @param netif the netif under DHCP control + */ +static void dhcp_handle_ack(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + u8_t *option_ptr; + /* clear options we might not get from the ACK */ + dhcp->offered_sn_mask.addr = 0; + dhcp->offered_gw_addr.addr = 0; + dhcp->offered_bc_addr.addr = 0; + + /* lease time given? */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_LEASE_TIME); + if (option_ptr != NULL) { + /* remember offered lease time */ + dhcp->offered_t0_lease = dhcp_get_option_long(option_ptr + 2); + } + /* renewal period given? */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_T1); + if (option_ptr != NULL) { + /* remember given renewal period */ + dhcp->offered_t1_renew = dhcp_get_option_long(option_ptr + 2); + } else { + /* calculate safe periods for renewal */ + dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2; + } + + /* renewal period given? */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_T2); + if (option_ptr != NULL) { + /* remember given rebind period */ + dhcp->offered_t2_rebind = dhcp_get_option_long(option_ptr + 2); + } else { + /* calculate safe periods for rebinding */ + dhcp->offered_t2_rebind = dhcp->offered_t0_lease; + } + + /* (y)our internet address */ + ip_addr_set(&dhcp->offered_ip_addr, &dhcp->msg_in->yiaddr); + +/** + * Patch #1308 + * TODO: we must check if the file field is not overloaded by DHCP options! + */ +#if 0 + /* boot server address */ + ip_addr_set(&dhcp->offered_si_addr, &dhcp->msg_in->siaddr); + /* boot file name */ + if (dhcp->msg_in->file[0]) { + dhcp->boot_file_name = mem_malloc(strlen(dhcp->msg_in->file) + 1); + strcpy(dhcp->boot_file_name, dhcp->msg_in->file); + } +#endif + + /* subnet mask */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_SUBNET_MASK); + /* subnet mask given? */ + if (option_ptr != NULL) { + dhcp->offered_sn_mask.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + } + + /* gateway router */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_ROUTER); + if (option_ptr != NULL) { + dhcp->offered_gw_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + } + + /* broadcast address */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_BROADCAST); + if (option_ptr != NULL) { + dhcp->offered_bc_addr.addr = htonl(dhcp_get_option_long(&option_ptr[2])); + } + + /* DNS servers */ + option_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_DNS_SERVER); + if (option_ptr != NULL) { + u8_t n; + dhcp->dns_count = dhcp_get_option_byte(&option_ptr[1]); + /* limit to at most DHCP_MAX_DNS DNS servers */ + if (dhcp->dns_count > DHCP_MAX_DNS) dhcp->dns_count = DHCP_MAX_DNS; + for (n = 0; n < dhcp->dns_count; n++) + { + dhcp->offered_dns_addr[n].addr = htonl(dhcp_get_option_long(&option_ptr[2+(n<<2)])); + } + } +} + +/** + * Start DHCP negotiation for a network interface. + * + * If no DHCP client instance was attached to this interface, + * a new client is created first. If a DHCP client instance + * was already present, it restarts negotiation. + * + * @param netif The lwIP network interface + * @return lwIP error code + * - ERR_OK - No error + * - ERR_MEM - Out of memory + * + */ +err_t dhcp_start(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result = ERR_OK; + + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + netif->flags &= ~NETIF_FLAG_DHCP; + + /* no DHCP client attached yet? */ + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_start(): starting new DHCP client\n")); + dhcp = mem_malloc(sizeof(struct dhcp)); + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n")); + return ERR_MEM; + } + /* store this dhcp client in the netif */ + netif->dhcp = dhcp; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_start(): allocated dhcp")); + /* already has DHCP client attached */ + } else { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE | 3, ("dhcp_start(): restarting DHCP configuration\n")); + } + + /* clear data structure */ + memset(dhcp, 0, sizeof(struct dhcp)); + /* allocate UDP PCB */ + dhcp->pcb = udp_new(); + if (dhcp->pcb == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_start(): could not obtain pcb\n")); + mem_free((void *)dhcp); + netif->dhcp = dhcp = NULL; + return ERR_MEM; + } + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n")); + /* (re)start the DHCP negotiation */ + result = dhcp_discover(netif); + if (result != ERR_OK) { + /* free resources allocated above */ + dhcp_stop(netif); + return ERR_MEM; + } + netif->flags |= NETIF_FLAG_DHCP; + return result; +} + +/** + * Inform a DHCP server of our manual configuration. + * + * This informs DHCP servers of our fixed IP address configuration + * by sending an INFORM message. It does not involve DHCP address + * configuration, it is just here to be nice to the network. + * + * @param netif The lwIP network interface + * + */ +void dhcp_inform(struct netif *netif) +{ + struct dhcp *dhcp; + err_t result = ERR_OK; + dhcp = mem_malloc(sizeof(struct dhcp)); + if (dhcp == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_inform(): could not allocate dhcp\n")); + return; + } + netif->dhcp = dhcp; + memset(dhcp, 0, sizeof(struct dhcp)); + + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_inform(): allocated dhcp\n")); + dhcp->pcb = udp_new(); + if (dhcp->pcb == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_inform(): could not obtain pcb")); + mem_free((void *)dhcp); + return; + } + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_inform(): created new udp pcb\n")); + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) { + + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_INFORM); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + /* TODO: use netif->mtu ?! */ + dhcp_option_short(dhcp, 576); + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + udp_connect(dhcp->pcb, IP_ADDR_BROADCAST, DHCP_SERVER_PORT); + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_inform: INFORMING\n")); + udp_send(dhcp->pcb, dhcp->p_out); + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + dhcp_delete_request(netif); + } else { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_inform: could not allocate DHCP request\n")); + } + + if (dhcp != NULL) + { + if (dhcp->pcb != NULL) udp_remove(dhcp->pcb); + dhcp->pcb = NULL; + mem_free((void *)dhcp); + netif->dhcp = NULL; + } +} + +#if DHCP_DOES_ARP_CHECK +/** + * Match an ARP reply with the offered IP address. + * + * @param addr The IP address we received a reply from + * + */ +void dhcp_arp_reply(struct netif *netif, struct ip_addr *addr) +{ + LWIP_ASSERT("netif != NULL", netif != NULL); + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_arp_reply()\n")); + /* is a DHCP client doing an ARP check? */ + if ((netif->dhcp != NULL) && (netif->dhcp->state == DHCP_CHECKING)) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n", addr->addr)); + /* did a host respond with the address we + were offered by the DHCP server? */ + if (ip_addr_cmp(addr, &netif->dhcp->offered_ip_addr)) { + /* we will not accept the offered address */ + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE | 1, ("dhcp_arp_reply(): arp reply matched with offered address, declining\n")); + dhcp_decline(netif); + } + } +} + +/** + * Decline an offered lease. + * + * Tell the DHCP server we do not accept the offered address. + * One reason to decline the lease is when we find out the address + * is already in use by another host (through ARP). + */ +static err_t dhcp_decline(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result = ERR_OK; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_decline()\n")); + dhcp_set_state(dhcp, DHCP_BACKING_OFF); + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) + { + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_DECLINE); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, 576); + + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); + + dhcp_option_trailer(dhcp); + /* resize pbuf to reflect true size of options */ + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + /* @todo: should we really connect here? we are performing sendto() */ + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + /* per section 4.4.4, broadcast DECLINE messages */ + udp_sendto(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT); + dhcp_delete_request(netif); + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_decline: BACKING OFF\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_decline: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = 10*1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} +#endif + + +/** + * Start the DHCP process, discover a DHCP server. + * + */ +static err_t dhcp_discover(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result = ERR_OK; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_discover()\n")); + ip_addr_set(&dhcp->offered_ip_addr, IP_ADDR_ANY); + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) + { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_discover: making request\n")); + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_DISCOVER); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, 576); + + dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/); + dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK); + dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER); + dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST); + dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER); + + dhcp_option_trailer(dhcp); + + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_discover: realloc()ing\n")); + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* set receive callback function with netif as user data */ + udp_recv(dhcp->pcb, dhcp_recv, netif); + udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n")); + udp_sendto(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT); + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_discover: deleting()ing\n")); + dhcp_delete_request(netif); + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_discover: SELECTING\n")); + dhcp_set_state(dhcp, DHCP_SELECTING); + } else { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_discover: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 4 ? (dhcp->tries + 1) * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + + +/** + * Bind the interface to the offered IP address. + * + * @param netif network interface to bind to the offered address + */ +static void dhcp_bind(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + struct ip_addr sn_mask, gw_addr; + LWIP_ASSERT("dhcp_bind: netif != NULL", netif != NULL); + LWIP_ASSERT("dhcp_bind: dhcp != NULL", dhcp != NULL); + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num)); + + /* temporary DHCP lease? */ + if (dhcp->offered_t1_renew != 0xffffffffUL) { + /* set renewal period timer */ + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew)); + dhcp->t1_timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if (dhcp->t1_timeout == 0) dhcp->t1_timeout = 1; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000)); + } + /* set renewal period timer */ + if (dhcp->offered_t2_rebind != 0xffffffffUL) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind)); + dhcp->t2_timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS; + if (dhcp->t2_timeout == 0) dhcp->t2_timeout = 1; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000)); + } + /* copy offered network mask */ + ip_addr_set(&sn_mask, &dhcp->offered_sn_mask); + + /* subnet mask not given? */ + /* TODO: this is not a valid check. what if the network mask is 0? */ + if (sn_mask.addr == 0) { + /* choose a safe subnet mask given the network class */ + u8_t first_octet = ip4_addr1(&sn_mask); + if (first_octet <= 127) sn_mask.addr = htonl(0xff000000); + else if (first_octet >= 192) sn_mask.addr = htonl(0xffffff00); + else sn_mask.addr = htonl(0xffff0000); + } + + ip_addr_set(&gw_addr, &dhcp->offered_gw_addr); + /* gateway address not given? */ + if (gw_addr.addr == 0) { + /* copy network address */ + gw_addr.addr = (dhcp->offered_ip_addr.addr & sn_mask.addr); + /* use first host address on network as gateway */ + gw_addr.addr |= htonl(0x00000001); + } + + LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F"\n", dhcp->offered_ip_addr.addr)); + netif_set_ipaddr(netif, &dhcp->offered_ip_addr); + LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_bind(): SN: 0x%08"X32_F"\n", sn_mask.addr)); + netif_set_netmask(netif, &sn_mask); + LWIP_DEBUGF(DHCP_DEBUG | DBG_STATE, ("dhcp_bind(): GW: 0x%08"X32_F"\n", gw_addr.addr)); + netif_set_gw(netif, &gw_addr); + /* bring the interface up */ + netif_set_up(netif); + /* netif is now bound to DHCP leased address */ + dhcp_set_state(dhcp, DHCP_BOUND); +} + +/** + * Renew an existing DHCP lease at the involved DHCP server. + * + * @param netif network interface which must renew its lease + */ +err_t dhcp_renew(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_renew()\n")); + dhcp_set_state(dhcp, DHCP_RENEWING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) { + + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_REQUEST); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + /* TODO: use netif->mtu in some way */ + dhcp_option_short(dhcp, 576); + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); +#endif + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); +#endif + /* append DHCP message trailer */ + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + udp_connect(dhcp->pcb, &dhcp->server_ip_addr, DHCP_SERVER_PORT); + udp_send(dhcp->pcb, dhcp->p_out); + dhcp_delete_request(netif); + + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_renew: RENEWING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_renew: could not allocate DHCP request\n")); + } + dhcp->tries++; + /* back-off on retries, but to a maximum of 20 seconds */ + msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * Rebind with a DHCP server for an existing DHCP lease. + * + * @param netif network interface which must rebind with a DHCP server + */ +static err_t dhcp_rebind(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_rebind()\n")); + dhcp_set_state(dhcp, DHCP_REBINDING); + + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) + { + + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_REQUEST); + + dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN); + dhcp_option_short(dhcp, 576); + +#if 0 + dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4); + dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr)); + + dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4); + dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr)); +#endif + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + /* set remote IP association to any DHCP server */ + udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT); + /* broadcast to server */ + udp_sendto(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT); + dhcp_delete_request(netif); + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_rebind: REBINDING\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_rebind: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs)); + return result; +} + +/** + * Release a DHCP lease. + * + * @param netif network interface which must release its lease + */ +err_t dhcp_release(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + err_t result; + u16_t msecs; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_release()\n")); + + /* idle DHCP client */ + dhcp_set_state(dhcp, DHCP_OFF); + /* clean old DHCP offer */ + dhcp->server_ip_addr.addr = 0; + dhcp->offered_ip_addr.addr = dhcp->offered_sn_mask.addr = 0; + dhcp->offered_gw_addr.addr = dhcp->offered_bc_addr.addr = 0; + dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0; + dhcp->dns_count = 0; + + /* create and initialize the DHCP message header */ + result = dhcp_create_request(netif); + if (result == ERR_OK) { + dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN); + dhcp_option_byte(dhcp, DHCP_RELEASE); + + dhcp_option_trailer(dhcp); + + pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len); + + udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT); + udp_connect(dhcp->pcb, &dhcp->server_ip_addr, DHCP_SERVER_PORT); + udp_send(dhcp->pcb, dhcp->p_out); + dhcp_delete_request(netif); + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_release: RELEASED, DHCP_OFF\n")); + } else { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_release: could not allocate DHCP request\n")); + } + dhcp->tries++; + msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000; + dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | DBG_STATE, ("dhcp_release(): set request timeout %"U16_F" msecs\n", msecs)); + /* bring the interface down */ + netif_set_down(netif); + /* remove IP address from interface */ + netif_set_ipaddr(netif, IP_ADDR_ANY); + netif_set_gw(netif, IP_ADDR_ANY); + netif_set_netmask(netif, IP_ADDR_ANY); + + /* TODO: netif_down(netif); */ + return result; +} +/** + * Remove the DHCP client from the interface. + * + * @param netif The network interface to stop DHCP on + */ +void dhcp_stop(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_ASSERT("dhcp_stop: netif != NULL", netif != NULL); + + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_stop()\n")); + /* netif is DHCP configured? */ + if (dhcp != NULL) + { + if (dhcp->pcb != NULL) + { + udp_remove(dhcp->pcb); + dhcp->pcb = NULL; + } + if (dhcp->p != NULL) + { + pbuf_free(dhcp->p); + dhcp->p = NULL; + } + /* free unfolded reply */ + dhcp_free_reply(dhcp); + mem_free((void *)dhcp); + netif->dhcp = NULL; + } +} + +/* + * Set the DHCP state of a DHCP client. + * + * If the state changed, reset the number of tries. + * + * TODO: we might also want to reset the timeout here? + */ +static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state) +{ + if (new_state != dhcp->state) + { + dhcp->state = new_state; + dhcp->tries = 0; + } +} + +/* + * Concatenate an option type and length field to the outgoing + * DHCP message. + * + */ +static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len) +{ + LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = option_type; + dhcp->msg_out->options[dhcp->options_out_len++] = option_len; +} +/* + * Concatenate a single byte to the outgoing DHCP message. + * + */ +static void dhcp_option_byte(struct dhcp *dhcp, u8_t value) +{ + LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = value; +} +static void dhcp_option_short(struct dhcp *dhcp, u16_t value) +{ + LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (value & 0xff00U) >> 8; + dhcp->msg_out->options[dhcp->options_out_len++] = value & 0x00ffU; +} +static void dhcp_option_long(struct dhcp *dhcp, u32_t value) +{ + LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = (value & 0xff000000UL) >> 24; + dhcp->msg_out->options[dhcp->options_out_len++] = (value & 0x00ff0000UL) >> 16; + dhcp->msg_out->options[dhcp->options_out_len++] = (value & 0x0000ff00UL) >> 8; + dhcp->msg_out->options[dhcp->options_out_len++] = (value & 0x000000ffUL); +} + +/** + * Extract the DHCP message and the DHCP options. + * + * Extract the DHCP message and the DHCP options, each into a contiguous + * piece of memory. As a DHCP message is variable sized by its options, + * and also allows overriding some fields for options, the easy approach + * is to first unfold the options into a conitguous piece of memory, and + * use that further on. + * + */ +static err_t dhcp_unfold_reply(struct dhcp *dhcp) +{ + struct pbuf *p = dhcp->p; + u8_t *ptr; + u16_t i; + u16_t j = 0; + LWIP_ASSERT("dhcp->p != NULL", dhcp->p != NULL); + /* free any left-overs from previous unfolds */ + dhcp_free_reply(dhcp); + /* options present? */ + if (dhcp->p->tot_len > (sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN)) + { + dhcp->options_in_len = dhcp->p->tot_len - (sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); + dhcp->options_in = mem_malloc(dhcp->options_in_len); + if (dhcp->options_in == NULL) + { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_unfold_reply(): could not allocate dhcp->options\n")); + return ERR_MEM; + } + } + dhcp->msg_in = mem_malloc(sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN); + if (dhcp->msg_in == NULL) + { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_unfold_reply(): could not allocate dhcp->msg_in\n")); + mem_free((void *)dhcp->options_in); + dhcp->options_in = NULL; + return ERR_MEM; + } + + ptr = (u8_t *)dhcp->msg_in; + /* proceed through struct dhcp_msg */ + for (i = 0; i < sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN; i++) + { + *ptr++ = ((u8_t *)p->payload)[j++]; + /* reached end of pbuf? */ + if (j == p->len) + { + /* proceed to next pbuf in chain */ + p = p->next; + j = 0; + } + } + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_unfold_reply(): copied %"U16_F" bytes into dhcp->msg_in[]\n", i)); + if (dhcp->options_in != NULL) { + ptr = (u8_t *)dhcp->options_in; + /* proceed through options */ + for (i = 0; i < dhcp->options_in_len; i++) { + *ptr++ = ((u8_t *)p->payload)[j++]; + /* reached end of pbuf? */ + if (j == p->len) { + /* proceed to next pbuf in chain */ + p = p->next; + j = 0; + } + } + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("dhcp_unfold_reply(): copied %"U16_F" bytes to dhcp->options_in[]\n", i)); + } + return ERR_OK; +} + +/** + * Free the incoming DHCP message including contiguous copy of + * its DHCP options. + * + */ +static void dhcp_free_reply(struct dhcp *dhcp) +{ + if (dhcp->msg_in != NULL) { + mem_free((void *)dhcp->msg_in); + dhcp->msg_in = NULL; + } + if (dhcp->options_in) { + mem_free((void *)dhcp->options_in); + dhcp->options_in = NULL; + dhcp->options_in_len = 0; + } + LWIP_DEBUGF(DHCP_DEBUG, ("dhcp_free_reply(): free'd\n")); +} + + +/** + * If an incoming DHCP message is in response to us, then trigger the state machine + */ +static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port) +{ + struct netif *netif = (struct netif *)arg; + struct dhcp *dhcp = netif->dhcp; + struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload; + u8_t *options_ptr; + u8_t msg_type; + u8_t i; + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 3, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p, + (u16_t)(ntohl(addr->addr) >> 24 & 0xff), (u16_t)(ntohl(addr->addr) >> 16 & 0xff), + (u16_t)(ntohl(addr->addr) >> 8 & 0xff), (u16_t)(ntohl(addr->addr) & 0xff), port)); + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len)); + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len)); + /* prevent warnings about unused arguments */ + (void)pcb; (void)addr; (void)port; + dhcp->p = p; + /* TODO: check packet length before reading them */ + if (reply_msg->op != DHCP_BOOTREPLY) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op)); + pbuf_free(p); + dhcp->p = NULL; + return; + } + /* iterate through hardware address and match against DHCP message */ + for (i = 0; i < netif->hwaddr_len; i++) { + if (netif->hwaddr[i] != reply_msg->chaddr[i]) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n", + (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i])); + pbuf_free(p); + dhcp->p = NULL; + return; + } + } + /* match transaction ID against what we expected */ + if (ntohl(reply_msg->xid) != dhcp->xid) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("transaction id mismatch\n")); + pbuf_free(p); + dhcp->p = NULL; + return; + } + /* option fields could be unfold? */ + if (dhcp_unfold_reply(dhcp) != ERR_OK) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("problem unfolding DHCP message - too short on memory?\n")); + pbuf_free(p); + dhcp->p = NULL; + return; + } + + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n")); + /* obtain pointer to DHCP message type */ + options_ptr = dhcp_get_option_ptr(dhcp, DHCP_OPTION_MESSAGE_TYPE); + if (options_ptr == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("DHCP_OPTION_MESSAGE_TYPE option not found\n")); + pbuf_free(p); + dhcp->p = NULL; + return; + } + + /* read DHCP message type */ + msg_type = dhcp_get_option_byte(options_ptr + 2); + /* message type is DHCP ACK? */ + if (msg_type == DHCP_ACK) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("DHCP_ACK received\n")); + /* in requesting state? */ + if (dhcp->state == DHCP_REQUESTING) { + dhcp_handle_ack(netif); + dhcp->request_timeout = 0; +#if DHCP_DOES_ARP_CHECK + /* check if the acknowledged lease address is already in use */ + dhcp_check(netif); +#else + /* bind interface to the acknowledged lease address */ + dhcp_bind(netif); +#endif + } + /* already bound to the given lease address? */ + else if ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING)) { + dhcp->request_timeout = 0; + dhcp_bind(netif); + } + } + /* received a DHCP_NAK in appropriate state? */ + else if ((msg_type == DHCP_NAK) && + ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REQUESTING) || + (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING ))) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("DHCP_NAK received\n")); + dhcp->request_timeout = 0; + dhcp_handle_nak(netif); + } + /* received a DHCP_OFFER in DHCP_SELECTING state? */ + else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_SELECTING)) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("DHCP_OFFER received in DHCP_SELECTING state\n")); + dhcp->request_timeout = 0; + /* remember offered lease */ + dhcp_handle_offer(netif); + } + pbuf_free(p); + dhcp->p = NULL; +} + + +static err_t dhcp_create_request(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + u16_t i; + LWIP_ASSERT("dhcp_create_request: dhcp->p_out == NULL", dhcp->p_out == NULL); + LWIP_ASSERT("dhcp_create_request: dhcp->msg_out == NULL", dhcp->msg_out == NULL); + dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM); + if (dhcp->p_out == NULL) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("dhcp_create_request(): could not allocate pbuf\n")); + return ERR_MEM; + } + /* give unique transaction identifier to this request */ + dhcp->xid = xid++; + + dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload; + + dhcp->msg_out->op = DHCP_BOOTREQUEST; + /* TODO: make link layer independent */ + dhcp->msg_out->htype = DHCP_HTYPE_ETH; + /* TODO: make link layer independent */ + dhcp->msg_out->hlen = DHCP_HLEN_ETH; + dhcp->msg_out->hops = 0; + dhcp->msg_out->xid = htonl(dhcp->xid); + dhcp->msg_out->secs = 0; + dhcp->msg_out->flags = 0; + dhcp->msg_out->ciaddr.addr = netif->ip_addr.addr; + dhcp->msg_out->yiaddr.addr = 0; + dhcp->msg_out->siaddr.addr = 0; + dhcp->msg_out->giaddr.addr = 0; + for (i = 0; i < DHCP_CHADDR_LEN; i++) { + /* copy netif hardware address, pad with zeroes */ + dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len) ? netif->hwaddr[i] : 0/* pad byte*/; + } + for (i = 0; i < DHCP_SNAME_LEN; i++) dhcp->msg_out->sname[i] = 0; + for (i = 0; i < DHCP_FILE_LEN; i++) dhcp->msg_out->file[i] = 0; + dhcp->msg_out->cookie = htonl(0x63825363UL); + dhcp->options_out_len = 0; + /* fill options field with an incrementing array (for debugging purposes) */ + for (i = 0; i < DHCP_OPTIONS_LEN; i++) dhcp->msg_out->options[i] = i; + return ERR_OK; +} + +static void dhcp_delete_request(struct netif *netif) +{ + struct dhcp *dhcp = netif->dhcp; + LWIP_ASSERT("dhcp_free_msg: dhcp->p_out != NULL", dhcp->p_out != NULL); + LWIP_ASSERT("dhcp_free_msg: dhcp->msg_out != NULL", dhcp->msg_out != NULL); + pbuf_free(dhcp->p_out); + dhcp->p_out = NULL; + dhcp->msg_out = NULL; +} + +/** + * Add a DHCP message trailer + * + * Adds the END option to the DHCP message, and if + * necessary, up to three padding bytes. + */ + +static void dhcp_option_trailer(struct dhcp *dhcp) +{ + LWIP_ASSERT("dhcp_option_trailer: dhcp->msg_out != NULL\n", dhcp->msg_out != NULL); + LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN); + dhcp->msg_out->options[dhcp->options_out_len++] = DHCP_OPTION_END; + /* packet is too small, or not 4 byte aligned? */ + while ((dhcp->options_out_len < DHCP_MIN_OPTIONS_LEN) || (dhcp->options_out_len & 3)) { + /* LWIP_DEBUGF(DHCP_DEBUG,("dhcp_option_trailer:dhcp->options_out_len=%"U16_F", DHCP_OPTIONS_LEN=%"U16_F, dhcp->options_out_len, DHCP_OPTIONS_LEN)); */ + LWIP_ASSERT("dhcp_option_trailer: dhcp->options_out_len < DHCP_OPTIONS_LEN\n", dhcp->options_out_len < DHCP_OPTIONS_LEN); + /* add a fill/padding byte */ + dhcp->msg_out->options[dhcp->options_out_len++] = 0; + } +} + +/** + * Find the offset of a DHCP option inside the DHCP message. + * + * @param client DHCP client + * @param option_type + * + * @return a byte offset into the UDP message where the option was found, or + * zero if the given option was not found. + */ +static u8_t *dhcp_get_option_ptr(struct dhcp *dhcp, u8_t option_type) +{ + u8_t overload = DHCP_OVERLOAD_NONE; + + /* options available? */ + if ((dhcp->options_in != NULL) && (dhcp->options_in_len > 0)) { + /* start with options field */ + u8_t *options = (u8_t *)dhcp->options_in; + u16_t offset = 0; + /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */ + while ((offset < dhcp->options_in_len) && (options[offset] != DHCP_OPTION_END)) { + /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */ + /* are the sname and/or file field overloaded with options? */ + if (options[offset] == DHCP_OPTION_OVERLOAD) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 2, ("overloaded message detected\n")); + /* skip option type and length */ + offset += 2; + overload = options[offset++]; + } + /* requested option found */ + else if (options[offset] == option_type) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("option found at offset %"U16_F" in options\n", offset)); + return &options[offset]; + /* skip option */ + } else { + LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", options[offset])); + /* skip option type */ + offset++; + /* skip option length, and then length bytes */ + offset += 1 + options[offset]; + } + } + /* is this an overloaded message? */ + if (overload != DHCP_OVERLOAD_NONE) { + u16_t field_len; + if (overload == DHCP_OVERLOAD_FILE) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("overloaded file field\n")); + options = (u8_t *)&dhcp->msg_in->file; + field_len = DHCP_FILE_LEN; + } else if (overload == DHCP_OVERLOAD_SNAME) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("overloaded sname field\n")); + options = (u8_t *)&dhcp->msg_in->sname; + field_len = DHCP_SNAME_LEN; + /* TODO: check if else if () is necessary */ + } else { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE | 1, ("overloaded sname and file field\n")); + options = (u8_t *)&dhcp->msg_in->sname; + field_len = DHCP_FILE_LEN + DHCP_SNAME_LEN; + } + offset = 0; + + /* at least 1 byte to read and no end marker */ + while ((offset < field_len) && (options[offset] != DHCP_OPTION_END)) { + if (options[offset] == option_type) { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("option found at offset=%"U16_F"\n", offset)); + return &options[offset]; + /* skip option */ + } else { + LWIP_DEBUGF(DHCP_DEBUG | DBG_TRACE, ("skipping option %"U16_F"\n", options[offset])); + /* skip option type */ + offset++; + offset += 1 + options[offset]; + } + } + } + } + return 0; +} + +/** + * Return the byte of DHCP option data. + * + * @param client DHCP client. + * @param ptr pointer obtained by dhcp_get_option_ptr(). + * + * @return byte value at the given address. + */ +static u8_t dhcp_get_option_byte(u8_t *ptr) +{ + LWIP_DEBUGF(DHCP_DEBUG, ("option byte value=%"U16_F"\n", (u16_t)(*ptr))); + return *ptr; +} + +/** + * Return the 16-bit value of DHCP option data. + * + * @param client DHCP client. + * @param ptr pointer obtained by dhcp_get_option_ptr(). + * + * @return byte value at the given address. + */ +#if 0 +static u16_t dhcp_get_option_short(u8_t *ptr) +{ + u16_t value; + value = *ptr++ << 8; + value |= *ptr; + LWIP_DEBUGF(DHCP_DEBUG, ("option short value=%"U16_F"\n", value)); + return value; +} +#endif + +/** + * Return the 32-bit value of DHCP option data. + * + * @param client DHCP client. + * @param ptr pointer obtained by dhcp_get_option_ptr(). + * + * @return byte value at the given address. + */ +static u32_t dhcp_get_option_long(u8_t *ptr) +{ + u32_t value; + value = (u32_t)(*ptr++) << 24; + value |= (u32_t)(*ptr++) << 16; + value |= (u32_t)(*ptr++) << 8; + value |= (u32_t)(*ptr++); + LWIP_DEBUGF(DHCP_DEBUG, ("option long value=%"U32_F"\n", value)); + return value; +} + +#endif /* LWIP_DHCP */ diff --git a/wii/libogc/lwip/core/inet.c b/wii/libogc/lwip/core/inet.c new file mode 100644 index 0000000000..33a353eb02 --- /dev/null +++ b/wii/libogc/lwip/core/inet.c @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + +/* inet.c + * + * Functions common to all TCP/IP modules, such as the Internet checksum and the + * byte order functions. + * + */ + + +#include "lwip/opt.h" + +#include "lwip/arch.h" + +#include "lwip/def.h" +#include "lwip/inet.h" + +#include "lwip/sys.h" + +/* This is a reference implementation of the checksum algorithm, with the + * aim of being simple, correct and fully portable. Checksumming is the + * first thing you would want to optimize for your platform. You will + * need to port it to your architecture and in your sys_arch.h: + * + * #define LWIP_CHKSUM +*/ +#ifndef LWIP_CHKSUM +#define LWIP_CHKSUM lwip_standard_chksum + +/** + * lwip checksum + * + * @param dataptr points to start of data to be summed at any boundary + * @param len length of data to be summed + * @return host order (!) lwip checksum (non-inverted Internet sum) + * + * @note accumulator size limits summable lenght to 64k + * @note host endianess is irrelevant (p3 RFC1071) + */ +static u16_t +lwip_standard_chksum(void *dataptr, u16_t len) +{ + u32_t acc; + u16_t src; + u8_t *octetptr; + + acc = 0; + /* dataptr may be at odd or even addresses */ + octetptr = (u8_t*)dataptr; + while (len > 1) + { + /* declare first octet as most significant + thus assume network order, ignoring host order */ + src = (*octetptr) << 8; + octetptr++; + /* declare second octet as least significant */ + src |= (*octetptr); + octetptr++; + acc += src; + len -= 2; + } + if (len > 0) + { + /* accumulate remaining octet */ + src = (*octetptr) << 8; + acc += src; + } + /* add deferred carry bits */ + acc = (acc >> 16) + (acc & 0x0000ffffUL); + if ((acc & 0xffff0000) != 0) { + acc = (acc >> 16) + (acc & 0x0000ffffUL); + } + /* This maybe a little confusing: reorder sum using htons() + instead of ntohs() since it has a little less call overhead. + The caller must invert bits for Internet sum ! */ + return htons((u16_t)acc); +} + +#endif + +#if 0 +/* + * Curt McDowell + * Broadcom Corp. + * csm@broadcom.com + * + * IP checksum two bytes at a time with support for + * unaligned buffer. + * Works for len up to and including 0x20000. + * by Curt McDowell, Broadcom Corp. 12/08/2005 + */ + +static u16_t +lwip_standard_chksum2(void *dataptr, int len) +{ + u8_t *pb = dataptr; + u16_t *ps, t = 0; + u32_t sum = 0; + int odd = ((u32_t)pb & 1); + + /* Get aligned to u16_t */ + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + /* Add the bulk of the data */ + ps = (u16_t *)pb; + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* Consume left-over byte, if any */ + if (len > 0) + ((u8_t *)&t)[0] = *(u8_t *)ps;; + + /* Add end bytes */ + sum += t; + + /* Fold 32-bit sum to 16 bits */ + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + /* Swap if alignment was odd */ + if (odd) + sum = ((sum & 0xff) << 8) | ((sum & 0xff00) >> 8); + + return sum; +} + +/** + * An optimized checksum routine. Basically, it uses loop-unrolling on + * the checksum loop, treating the head and tail bytes specially, whereas + * the inner loop acts on 8 bytes at a time. + * + * @arg start of buffer to be checksummed. May be an odd byte address. + * @len number of bytes in the buffer to be checksummed. + * + * @todo First argument type conflicts with generic checksum routine. + * + * by Curt McDowell, Broadcom Corp. December 8th, 2005 + */ + +static u16_t +lwip_standard_chksum4(u8_t *pb, int len) +{ + u16_t *ps, t = 0; + u32_t *pl; + u32_t sum = 0, tmp; + /* starts at odd byte address? */ + int odd = ((u32_t)pb & 1); + + if (odd && len > 0) { + ((u8_t *)&t)[1] = *pb++; + len--; + } + + ps = (u16_t *)pb; + + if (((u32_t)ps & 3) && len > 1) { + sum += *ps++; + len -= 2; + } + + pl = (u32_t *)ps; + + while (len > 7) { + tmp = sum + *pl++; /* ping */ + if (tmp < sum) + tmp++; /* add back carry */ + + sum = tmp + *pl++; /* pong */ + if (sum < tmp) + sum++; /* add back carry */ + + len -= 8; + } + + /* make room in upper bits */ + sum = (sum >> 16) + (sum & 0xffff); + + ps = (u16_t *)pl; + + /* 16-bit aligned word remaining? */ + while (len > 1) { + sum += *ps++; + len -= 2; + } + + /* dangling tail byte remaining? */ + if (len > 0) /* include odd byte */ + ((u8_t *)&t)[0] = *(u8_t *)ps; + + sum += t; /* add end bytes */ + + while (sum >> 16) /* combine halves */ + sum = (sum >> 16) + (sum & 0xffff); + + if (odd) + sum = ((sum & 0xff) << 8) | ((sum & 0xff00) >> 8); + + return sum; +} +#endif + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + */ + +u16_t +inet_chksum_pseudo(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u16_t proto_len) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + /* iterate through all pbuf in chain */ + for(q = p; q != NULL; q = q->next) { + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n", + (void *)q, (void *)q->next)); + acc += LWIP_CHKSUM(q->payload, q->len); + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/ + while (acc >> 16) { + acc = (acc & 0xffffUL) + (acc >> 16); + } + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8); + } + /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/ + } + + if (swapped) { + acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8); + } + acc += (src->addr & 0xffffUL); + acc += ((src->addr >> 16) & 0xffffUL); + acc += (dest->addr & 0xffffUL); + acc += ((dest->addr >> 16) & 0xffffUL); + acc += (u32_t)htons((u16_t)proto); + acc += (u32_t)htons(proto_len); + + while (acc >> 16) { + acc = (acc & 0xffffUL) + (acc >> 16); + } + LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc)); + return (u16_t)~(acc & 0xffffUL); +} + +/* inet_chksum: + * + * Calculates the Internet checksum over a portion of memory. Used primarely for IP + * and ICMP. + */ + +u16_t +inet_chksum(void *dataptr, u16_t len) +{ + u32_t acc; + + acc = LWIP_CHKSUM(dataptr, len); + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + return (u16_t)~(acc & 0xffff); +} + +u16_t +inet_chksum_pbuf(struct pbuf *p) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += LWIP_CHKSUM(q->payload, q->len); + while (acc >> 16) { + acc = (acc & 0xffffUL) + (acc >> 16); + } + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = (acc & 0x00ffUL << 8) | (acc & 0xff00UL >> 8); + } + } + + if (swapped) { + acc = ((acc & 0x00ffUL) << 8) | ((acc & 0xff00UL) >> 8); + } + return (u16_t)~(acc & 0xffffUL); +} + diff --git a/wii/libogc/lwip/core/inet6.c b/wii/libogc/lwip/core/inet6.c new file mode 100644 index 0000000000..c04915b73d --- /dev/null +++ b/wii/libogc/lwip/core/inet6.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + +/* inet6.c + * + * Functions common to all TCP/IP modules, such as the Internet checksum and the + * byte order functions. + * + */ + + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/inet.h" + + + +/* chksum: + * + * Sums up all 16 bit words in a memory portion. Also includes any odd byte. + * This function is used by the other checksum functions. + * + * For now, this is not optimized. Must be optimized for the particular processor + * arcitecture on which it is to run. Preferebly coded in assembler. + */ + +static u32_t +chksum(void *dataptr, u16_t len) +{ + u16_t *sdataptr = dataptr; + u32_t acc; + + + for(acc = 0; len > 1; len -= 2) { + acc += *sdataptr++; + } + + /* add up any odd byte */ + if (len == 1) { + acc += htons((u16_t)(*(u8_t *)dataptr) << 8); + } + + return acc; + +} + +/* inet_chksum_pseudo: + * + * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain. + */ + +u16_t +inet_chksum_pseudo(struct pbuf *p, + struct ip_addr *src, struct ip_addr *dest, + u8_t proto, u32_t proto_len) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped, i; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += chksum(q->payload, q->len); + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + } + + if (swapped) { + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + + for(i = 0; i < 8; i++) { + acc += ((u16_t *)src->addr)[i] & 0xffff; + acc += ((u16_t *)dest->addr)[i] & 0xffff; + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + } + acc += (u16_t)htons((u16_t)proto); + acc += ((u16_t *)&proto_len)[0] & 0xffff; + acc += ((u16_t *)&proto_len)[1] & 0xffff; + + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + return ~(acc & 0xffff); +} + +/* inet_chksum: + * + * Calculates the Internet checksum over a portion of memory. Used primarely for IP + * and ICMP. + */ + +u16_t +inet_chksum(void *dataptr, u16_t len) +{ + u32_t acc, sum; + + acc = chksum(dataptr, len); + sum = (acc & 0xffff) + (acc >> 16); + sum += (sum >> 16); + return ~(sum & 0xffff); +} + +u16_t +inet_chksum_pbuf(struct pbuf *p) +{ + u32_t acc; + struct pbuf *q; + u8_t swapped; + + acc = 0; + swapped = 0; + for(q = p; q != NULL; q = q->next) { + acc += chksum(q->payload, q->len); + while (acc >> 16) { + acc = (acc & 0xffff) + (acc >> 16); + } + if (q->len % 2 != 0) { + swapped = 1 - swapped; + acc = (acc & 0xff << 8) | (acc & 0xff00 >> 8); + } + } + + if (swapped) { + acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8); + } + return ~(acc & 0xffff); +} + diff --git a/wii/libogc/lwip/core/ipv4/icmp.c b/wii/libogc/lwip/core/ipv4/icmp.c new file mode 100644 index 0000000000..02d467e43c --- /dev/null +++ b/wii/libogc/lwip/core/ipv4/icmp.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* Some ICMP messages should be passed to the transport protocols. This + is not implemented. */ + +#include + +#include "lwip/opt.h" +#include "lwip/icmp.h" +#include "lwip/inet.h" +#include "lwip/ip.h" +#include "lwip/def.h" +#include "lwip/stats.h" +#include "lwip/snmp.h" + +void +icmp_input(struct pbuf *p, struct netif *inp) +{ + u8_t type; + u8_t code; + struct icmp_echo_hdr *iecho; + struct ip_hdr *iphdr; + struct ip_addr tmpaddr; + u16_t hlen; + + ICMP_STATS_INC(icmp.recv); + snmp_inc_icmpinmsgs(); + + + iphdr = p->payload; + hlen = IPH_HL(iphdr) * 4; + if (pbuf_header(p, -((s16_t)hlen)) || (p->tot_len < sizeof(u16_t)*2)) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len)); + pbuf_free(p); + ICMP_STATS_INC(icmp.lenerr); + snmp_inc_icmpinerrors(); + return; + } + + type = *((u8_t *)p->payload); + code = *(((u8_t *)p->payload)+1); + (void)code; + switch (type) { + case ICMP_ECHO: + /* broadcast or multicast destination address? */ + if (ip_addr_isbroadcast(&iphdr->dest, inp) || ip_addr_ismulticast(&iphdr->dest)) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n")); + ICMP_STATS_INC(icmp.err); + pbuf_free(p); + return; + } + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n")); + if (p->tot_len < sizeof(struct icmp_echo_hdr)) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n")); + pbuf_free(p); + ICMP_STATS_INC(icmp.lenerr); + snmp_inc_icmpinerrors(); + + return; + } + iecho = p->payload; + if (inet_chksum_pbuf(p) != 0) { + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n")); + pbuf_free(p); + ICMP_STATS_INC(icmp.chkerr); + snmp_inc_icmpinerrors(); + return; + } + tmpaddr.addr = iphdr->src.addr; + iphdr->src.addr = iphdr->dest.addr; + iphdr->dest.addr = tmpaddr.addr; + ICMPH_TYPE_SET(iecho, ICMP_ER); + /* adjust the checksum */ + if (iecho->chksum >= htons(0xffff - (ICMP_ECHO << 8))) { + iecho->chksum += htons(ICMP_ECHO << 8) + 1; + } else { + iecho->chksum += htons(ICMP_ECHO << 8); + } + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of echo replies attempted to send */ + snmp_inc_icmpoutechoreps(); + + pbuf_header(p, hlen); + ip_output_if(p, &(iphdr->src), IP_HDRINCL, + IPH_TTL(iphdr), 0, IP_PROTO_ICMP, inp); + break; + default: + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n", (s16_t)type, (s16_t)code)); + ICMP_STATS_INC(icmp.proterr); + ICMP_STATS_INC(icmp.drop); + } + pbuf_free(p); +} + +void +icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + struct icmp_dur_hdr *idur; + + q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM); + /* ICMP header + IP header + 8 bytes of data */ + + iphdr = p->payload; + + idur = q->payload; + ICMPH_TYPE_SET(idur, ICMP_DUR); + ICMPH_CODE_SET(idur, t); + + memcpy((u8_t *)q->payload + 8, p->payload, IP_HLEN + 8); + + /* calculate checksum */ + idur->chksum = 0; + idur->chksum = inet_chksum(idur, q->len); + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of destination unreachable messages attempted to send */ + snmp_inc_icmpoutdestunreachs(); + + ip_output(q, NULL, &(iphdr->src), + ICMP_TTL, 0, IP_PROTO_ICMP); + pbuf_free(q); +} + +#if IP_FORWARD +void +icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t) +{ + struct pbuf *q; + struct ip_hdr *iphdr; + struct icmp_te_hdr *tehdr; + + q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM); + + iphdr = p->payload; + LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from ")); + ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src)); + LWIP_DEBUGF(ICMP_DEBUG, (" to ")); + ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest)); + LWIP_DEBUGF(ICMP_DEBUG, ("\n")); + + tehdr = q->payload; + ICMPH_TYPE_SET(tehdr, ICMP_TE); + ICMPH_CODE_SET(tehdr, t); + + /* copy fields from original packet */ + memcpy((u8_t *)q->payload + 8, (u8_t *)p->payload, IP_HLEN + 8); + + /* calculate checksum */ + tehdr->chksum = 0; + tehdr->chksum = inet_chksum(tehdr, q->len); + ICMP_STATS_INC(icmp.xmit); + /* increase number of messages attempted to send */ + snmp_inc_icmpoutmsgs(); + /* increase number of destination unreachable messages attempted to send */ + snmp_inc_icmpouttimeexcds(); + ip_output(q, NULL, &(iphdr->src), + ICMP_TTL, 0, IP_PROTO_ICMP); + pbuf_free(q); +} + +#endif /* IP_FORWARD */ + + + + + + + diff --git a/wii/libogc/lwip/core/ipv4/ip.c b/wii/libogc/lwip/core/ipv4/ip.c new file mode 100644 index 0000000000..4db68c8e86 --- /dev/null +++ b/wii/libogc/lwip/core/ipv4/ip.c @@ -0,0 +1,508 @@ +/* @file + * + * This is the IP layer implementation for incoming and outgoing IP traffic. + * + * @see ip_frag.c + * + */ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/ip.h" +#include "lwip/ip_frag.h" +#include "lwip/inet.h" +#include "lwip/netif.h" +#include "lwip/icmp.h" +#include "lwip/raw.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" + +#include "lwip/stats.h" + +#include "arch/perf.h" + +#include "lwip/snmp.h" +#if LWIP_DHCP +# include "lwip/dhcp.h" +#endif /* LWIP_DHCP */ + + +/** + * Initializes the IP layer. + */ + +void +ip_init(void) +{ + /* no initializations as of yet */ +} + +/** + * Finds the appropriate network interface for a given IP address. It + * searches the list of network interfaces linearly. A match is found + * if the masked IP address of the network interface equals the masked + * IP address given to the function. + */ + +struct netif * +ip_route(struct ip_addr *dest) +{ + struct netif *netif; + + /* iterate through netifs */ + for(netif = netif_list; netif != NULL; netif = netif->next) { + /* network mask matches? */ + if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) { + /* return netif on which to forward IP packet */ + return netif; + } + } + /* no matching netif found, use default netif */ + return netif_default; +} +#if IP_FORWARD + +/** + * Forwards an IP packet. It finds an appropriate route for the + * packet, decrements the TTL value of the packet, adjusts the + * checksum and outputs the packet on the appropriate interface. + */ + +static struct netif * +ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) +{ + struct netif *netif; + + PERF_START; + /* Find network interface where to forward this IP packet to. */ + netif = ip_route((struct ip_addr *)&(iphdr->dest)); + if (netif == NULL) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for 0x%"X32_F" found\n", + iphdr->dest.addr)); + snmp_inc_ipnoroutes(); + return (struct netif *)NULL; + } + /* Do not forward packets onto the same network interface on which + * they arrived. */ + if (netif == inp) { + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n")); + snmp_inc_ipnoroutes(); + return (struct netif *)NULL; + } + + /* decrement TTL */ + IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1); + /* send ICMP if TTL == 0 */ + if (IPH_TTL(iphdr) == 0) { + /* Don't send ICMP messages in response to ICMP messages */ + if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) { + icmp_time_exceeded(p, ICMP_TE_TTL); + snmp_inc_icmpouttimeexcds(); + } + return (struct netif *)NULL; + } + + /* Incrementally update the IP checksum. */ + if (IPH_CHKSUM(iphdr) >= htons(0xffff - 0x100)) { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100) + 1); + } else { + IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100)); + } + + LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to 0x%"X32_F"\n", + iphdr->dest.addr)); + + IP_STATS_INC(ip.fw); + IP_STATS_INC(ip.xmit); + snmp_inc_ipforwdatagrams(); + + PERF_STOP("ip_forward"); + /* transmit pbuf on chosen interface */ + netif->output(netif, p, (struct ip_addr *)&(iphdr->dest)); + return netif; +} +#endif /* IP_FORWARD */ + +/** + * This function is called by the network interface device driver when + * an IP packet is received. The function does the basic checks of the + * IP header such as packet size being at least larger than the header + * size etc. If the packet was not destined for us, the packet is + * forwarded (using ip_forward). The IP checksum is always checked. + * + * Finally, the packet is sent to the upper layer protocol input function. + * + * + * + */ + +err_t +ip_input(struct pbuf *p, struct netif *inp) { + struct ip_hdr *iphdr; + struct netif *netif; + u16_t iphdrlen; + + IP_STATS_INC(ip.recv); + snmp_inc_ipinreceives(); + + /* identify the IP header */ + iphdr = p->payload; + if (IPH_V(iphdr) != 4) { + LWIP_DEBUGF(IP_DEBUG | 1, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr))); + ip_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.err); + IP_STATS_INC(ip.drop); + snmp_inc_ipunknownprotos(); + return ERR_OK; + } + /* obtain IP header length in number of 32-bit words */ + iphdrlen = IPH_HL(iphdr); + /* calculate IP header length in bytes */ + iphdrlen *= 4; + + /* header length exceeds first pbuf length? */ + if (iphdrlen > p->len) { + LWIP_DEBUGF(IP_DEBUG | 2, ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet droppped.\n", + iphdrlen, p->len)); + /* free (drop) packet pbufs */ + pbuf_free(p); + IP_STATS_INC(ip.lenerr); + IP_STATS_INC(ip.drop); + snmp_inc_ipindiscards(); + return ERR_OK; + } + + /* verify checksum */ +#if CHECKSUM_CHECK_IP + if (inet_chksum(iphdr, iphdrlen) != 0) { + + LWIP_DEBUGF(IP_DEBUG | 2, ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdrlen))); + ip_debug_print(p); + pbuf_free(p); + IP_STATS_INC(ip.chkerr); + IP_STATS_INC(ip.drop); + snmp_inc_ipindiscards(); + return ERR_OK; + } +#endif + + /* Trim pbuf. This should have been done at the netif layer, + * but we'll do it anyway just to be sure that its done. */ + pbuf_realloc(p, ntohs(IPH_LEN(iphdr))); + + /* match packet against an interface, i.e. is this packet for us? */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + + LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n", + iphdr->dest.addr, netif->ip_addr.addr, + iphdr->dest.addr & netif->netmask.addr, + netif->ip_addr.addr & netif->netmask.addr, + iphdr->dest.addr & ~(netif->netmask.addr))); + + /* interface is up and configured? */ + if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) + { + /* unicast to this interface address? */ + if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr)) || + /* or broadcast on this interface network address? */ + ip_addr_isbroadcast(&(iphdr->dest), netif)) { + LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n", + netif->name[0], netif->name[1])); + /* break out of for loop */ + break; + } + } + } +#if LWIP_DHCP + /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed + * using link layer addressing (such as Ethernet MAC) so we must not filter on IP. + * According to RFC 1542 section 3.1.1, referred by RFC 2131). + */ + if (netif == NULL) { + /* remote port is DHCP server? */ + if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { + LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: UDP packet to DHCP client port %"U16_F"\n", + ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdrlen))->dest))); + if (ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdrlen))->dest) == DHCP_CLIENT_PORT) { + LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: DHCP packet accepted.\n")); + netif = inp; + } + } + } +#endif /* LWIP_DHCP */ + /* packet not for us? */ + if (netif == NULL) { + /* packet not for us, route or discard */ + LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: packet not for us.\n")); +#if IP_FORWARD + /* non-broadcast packet? */ + if (!ip_addr_isbroadcast(&(iphdr->dest), inp)) { + /* try to forward IP packet on (other) interfaces */ + ip_forward(p, iphdr, inp); + } + else +#endif /* IP_FORWARD */ + { + snmp_inc_ipindiscards(); + } + pbuf_free(p); + return ERR_OK; + } + /* packet consists of multiple fragments? */ + if ((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) { +#if IP_REASSEMBLY /* packet fragment reassembly code present? */ + LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n", + ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & htons(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8)); + /* reassemble the packet*/ + p = ip_reass(p); + /* packet not fully reassembled yet? */ + if (p == NULL) { + return ERR_OK; + } + iphdr = p->payload; +#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */ + pbuf_free(p); + LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n", + ntohs(IPH_OFFSET(iphdr)))); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + snmp_inc_ipunknownprotos(); + return ERR_OK; +#endif /* IP_REASSEMBLY */ + } + +#if IP_OPTIONS == 0 /* no support for IP options in the IP header? */ + if (iphdrlen > IP_HLEN) { + LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since there were IP options (while IP_OPTIONS == 0).\n")); + pbuf_free(p); + IP_STATS_INC(ip.opterr); + IP_STATS_INC(ip.drop); + snmp_inc_ipunknownprotos(); + return ERR_OK; + } +#endif /* IP_OPTIONS == 0 */ + + /* send to upper layers */ + LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n")); + ip_debug_print(p); + LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); + +#if LWIP_RAW + /* raw input did not eat the packet? */ + if (raw_input(p, inp) == 0) { +#endif /* LWIP_RAW */ + + switch (IPH_PROTO(iphdr)) { +#if LWIP_UDP + case IP_PROTO_UDP: + case IP_PROTO_UDPLITE: + snmp_inc_ipindelivers(); + udp_input(p, inp); + break; +#endif /* LWIP_UDP */ +#if LWIP_TCP + case IP_PROTO_TCP: + snmp_inc_ipindelivers(); + tcp_input(p, inp); + break; +#endif /* LWIP_TCP */ + case IP_PROTO_ICMP: + snmp_inc_ipindelivers(); + icmp_input(p, inp); + break; + default: + /* send ICMP destination protocol unreachable unless is was a broadcast */ + if (!ip_addr_isbroadcast(&(iphdr->dest), inp) && + !ip_addr_ismulticast(&(iphdr->dest))) { + p->payload = iphdr; + icmp_dest_unreach(p, ICMP_DUR_PROTO); + } + pbuf_free(p); + + LWIP_DEBUGF(IP_DEBUG | 2, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr))); + + IP_STATS_INC(ip.proterr); + IP_STATS_INC(ip.drop); + snmp_inc_ipunknownprotos(); + } +#if LWIP_RAW + } /* LWIP_RAW */ +#endif + return ERR_OK; +} + +/** + * Sends an IP packet on a network interface. This function constructs + * the IP header and calculates the IP header checksum. If the source + * IP address is NULL, the IP address of the outgoing network + * interface is filled in as source address. + */ + +err_t +ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, + u8_t proto, struct netif *netif) +{ + struct ip_hdr *iphdr; + u16_t ip_id = 0; + + snmp_inc_ipoutrequests(); + + if (dest != IP_HDRINCL) { + if (pbuf_header(p, IP_HLEN)) { + LWIP_DEBUGF(IP_DEBUG | 2, ("ip_output: not enough room for IP header in pbuf\n")); + + IP_STATS_INC(ip.err); + snmp_inc_ipoutdiscards(); + return ERR_BUF; + } + + iphdr = p->payload; + + IPH_TTL_SET(iphdr, ttl); + IPH_PROTO_SET(iphdr, proto); + + ip_addr_set(&(iphdr->dest), dest); + + IPH_VHLTOS_SET(iphdr, 4, IP_HLEN / 4, tos); + IPH_LEN_SET(iphdr, htons(p->tot_len)); + IPH_OFFSET_SET(iphdr, htons(IP_DF)); + IPH_ID_SET(iphdr, htons(ip_id)); + ++ip_id; + + if (ip_addr_isany(src)) { + ip_addr_set(&(iphdr->src), &(netif->ip_addr)); + } else { + ip_addr_set(&(iphdr->src), src); + } + + IPH_CHKSUM_SET(iphdr, 0); +#if CHECKSUM_GEN_IP + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); +#endif + } else { + iphdr = p->payload; + dest = &(iphdr->dest); + } + +#if IP_FRAG + /* don't fragment if interface has mtu set to 0 [loopif] */ + if (netif->mtu && (p->tot_len > netif->mtu)) + return ip_frag(p,netif,dest); +#endif + + IP_STATS_INC(ip.xmit); + + LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); + ip_debug_print(p); + + LWIP_DEBUGF(IP_DEBUG, ("netif->output()")); + + return netif->output(netif, p, dest); +} + +/** + * Simple interface to ip_output_if. It finds the outgoing network + * interface and calls upon ip_output_if to do the actual work. + */ + +err_t +ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, + u8_t ttl, u8_t tos, u8_t proto) +{ + struct netif *netif; + + if ((netif = ip_route(dest)) == NULL) { + LWIP_DEBUGF(IP_DEBUG | 2, ("ip_output: No route to 0x%"X32_F"\n", dest->addr)); + + IP_STATS_INC(ip.rterr); + snmp_inc_ipoutdiscards(); + return ERR_RTE; + } + + return ip_output_if(p, src, dest, ttl, tos, proto, netif); +} + +#if IP_DEBUG +void +ip_debug_print(struct pbuf *p) +{ + struct ip_hdr *iphdr = p->payload; + u8_t *payload; + + payload = (u8_t *)iphdr + IP_HLEN; + + LWIP_DEBUGF(IP_DEBUG, ("IP header:\n")); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n", + IPH_V(iphdr), + IPH_HL(iphdr), + IPH_TOS(iphdr), + ntohs(IPH_LEN(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n", + ntohs(IPH_ID(iphdr)), + ntohs(IPH_OFFSET(iphdr)) >> 15 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 14 & 1, + ntohs(IPH_OFFSET(iphdr)) >> 13 & 1, + ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n", + IPH_TTL(iphdr), + IPH_PROTO(iphdr), + ntohs(IPH_CHKSUM(iphdr)))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n", + ip4_addr1(&iphdr->src), + ip4_addr2(&iphdr->src), + ip4_addr3(&iphdr->src), + ip4_addr4(&iphdr->src))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (dest)\n", + ip4_addr1(&iphdr->dest), + ip4_addr2(&iphdr->dest), + ip4_addr3(&iphdr->dest), + ip4_addr4(&iphdr->dest))); + LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* IP_DEBUG */ + + + + + + diff --git a/wii/libogc/lwip/core/ipv4/ip_addr.c b/wii/libogc/lwip/core/ipv4/ip_addr.c new file mode 100644 index 0000000000..2af526e9f3 --- /dev/null +++ b/wii/libogc/lwip/core/ipv4/ip_addr.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/ip_addr.h" +#include "lwip/inet.h" +#include "lwip/netif.h" + +/* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */ +const struct ip_addr ip_addr_any = { 0x00000000UL }; +const struct ip_addr ip_addr_broadcast = { 0xffffffffUL }; + +/* Determine if an address is a broadcast address on a network interface + * + * @param addr address to be checked + * @param netif the network interface against which the address is checked + * @return returns non-zero if the address is a broadcast address + * + */ + +u8_t ip_addr_isbroadcast(struct ip_addr *addr, struct netif *netif) +{ + /* all ones (broadcast) or all zeroes (old skool broadcast) */ + if ((addr->addr == ip_addr_broadcast.addr) || + (addr->addr == ip_addr_any.addr)) + return 1; + /* no broadcast support on this network interface? */ + else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) + /* the given address cannot be a broadcast address + * nor can we check against any broadcast addresses */ + return 0; + /* address matches network interface address exactly? => no broadcast */ + else if (addr->addr == netif->ip_addr.addr) + return 0; + /* on the same (sub) network... */ + else if (ip_addr_netcmp(addr, &(netif->ip_addr), &(netif->netmask)) + /* ...and host identifier bits are all ones? =>... */ + && ((addr->addr & ~netif->netmask.addr) == + (ip_addr_broadcast.addr & ~netif->netmask.addr))) + /* => network broadcast address */ + return 1; + else + return 0; +} diff --git a/wii/libogc/lwip/core/ipv4/ip_frag.c b/wii/libogc/lwip/core/ipv4/ip_frag.c new file mode 100644 index 0000000000..5a57138ce3 --- /dev/null +++ b/wii/libogc/lwip/core/ipv4/ip_frag.c @@ -0,0 +1,366 @@ +/* @file + * + * This is the IP packet segmentation and reassembly implementation. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Jani Monoses + * original reassembly code by Adam Dunkels + * + */ + +#include + +#include "lwip/opt.h" +/* #include "lwip/sys.h" */ +#include "lwip/ip.h" +#include "lwip/ip_frag.h" +#include "lwip/netif.h" +#include "lwip/stats.h" + + +/* + * Copy len bytes from offset in pbuf to buffer + * + * helper used by both ip_reass and ip_frag + */ +static struct pbuf * +copy_from_pbuf(struct pbuf *p, u16_t * offset, + u8_t * buffer, u16_t len) +{ + u16_t l; + + p->payload = (u8_t *)p->payload + *offset; + p->len -= *offset; + while (len) { + l = len < p->len ? len : p->len; + memcpy(buffer, p->payload, l); + buffer += l; + len -= l; + if (len) + p = p->next; + else + *offset = l; + } + return p; +} + +#define IP_REASS_BUFSIZE 5760 +#define IP_REASS_MAXAGE 30 +#define IP_REASS_TMO 1000 + +static u8_t ip_reassbuf[IP_HLEN + IP_REASS_BUFSIZE]; +static u8_t ip_reassbitmap[IP_REASS_BUFSIZE / (8 * 8) + 1]; +static const u8_t bitmap_bits[8] = { 0xff, 0x7f, 0x3f, 0x1f, + 0x0f, 0x07, 0x03, 0x01 +}; +static u16_t ip_reasslen; +static u8_t ip_reassflags; +#define IP_REASS_FLAG_LASTFRAG 0x01 + +static u8_t ip_reasstmr; + +/** + * Reassembly timer base function + * for both NO_SYS == 0 and 1 (!). + * + * Should be called every 1000 msec. + */ +void +ip_reass_tmr(void) +{ + if (ip_reasstmr > 0) { + ip_reasstmr--; + } +} + +/** + * Reassembles incoming IP fragments into an IP datagram. + * + * @param p points to a pbuf chain of the fragment + * @return NULL if reassembly is incomplete, ? otherwise + */ +struct pbuf * +ip_reass(struct pbuf *p) +{ + struct pbuf *q; + struct ip_hdr *fraghdr, *iphdr; + u16_t offset, len; + u16_t i; + + IPFRAG_STATS_INC(ip_frag.recv); + + iphdr = (struct ip_hdr *) ip_reassbuf; + fraghdr = (struct ip_hdr *) p->payload; + /* If ip_reasstmr is zero, no packet is present in the buffer, so we + write the IP header of the fragment into the reassembly + buffer. The timer is updated with the maximum age. */ + if (ip_reasstmr == 0) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: new packet\n")); + memcpy(iphdr, fraghdr, IP_HLEN); + ip_reasstmr = IP_REASS_MAXAGE; + ip_reassflags = 0; + /* Clear the bitmap. */ + memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap)); + } + + /* Check if the incoming fragment matches the one currently present + in the reasembly buffer. If so, we proceed with copying the + fragment into the buffer. */ + if (ip_addr_cmp(&iphdr->src, &fraghdr->src) && + ip_addr_cmp(&iphdr->dest, &fraghdr->dest) && + IPH_ID(iphdr) == IPH_ID(fraghdr)) { + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n", + ntohs(IPH_ID(fraghdr)))); + IPFRAG_STATS_INC(ip_frag.cachehit); + /* Find out the offset in the reassembly buffer where we should + copy the fragment. */ + len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; + offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; + + /* If the offset or the offset + fragment length overflows the + reassembly buffer, we discard the entire packet. */ + if (offset > IP_REASS_BUFSIZE || offset + len > IP_REASS_BUFSIZE) { + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip_reass: fragment outside of buffer (%"S16_F":%"S16_F"/%"S16_F").\n", offset, + offset + len, IP_REASS_BUFSIZE)); + ip_reasstmr = 0; + goto nullreturn; + } + + /* Copy the fragment into the reassembly buffer, at the right + offset. */ + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip_reass: copying with offset %"S16_F" into %"S16_F":%"S16_F"\n", offset, + IP_HLEN + offset, IP_HLEN + offset + len)); + i = IPH_HL(fraghdr) * 4; + copy_from_pbuf(p, &i, &ip_reassbuf[IP_HLEN + offset], len); + + /* Update the bitmap. */ + if (offset / (8 * 8) == (offset + len) / (8 * 8)) { + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip_reass: updating single byte in bitmap.\n")); + /* If the two endpoints are in the same byte, we only update that byte. */ + LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)", + offset / (8 * 8) < sizeof(ip_reassbitmap)); + ip_reassbitmap[offset / (8 * 8)] |= + bitmap_bits[(offset / 8) & 7] & + ~bitmap_bits[((offset + len) / 8) & 7]; + } else { + /* If the two endpoints are in different bytes, we update the + bytes in the endpoints and fill the stuff inbetween with + 0xff. */ + LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)", + offset / (8 * 8) < sizeof(ip_reassbitmap)); + ip_reassbitmap[offset / (8 * 8)] |= bitmap_bits[(offset / 8) & 7]; + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip_reass: updating many bytes in bitmap (%"S16_F":%"S16_F").\n", + 1 + offset / (8 * 8), (offset + len) / (8 * 8))); + for (i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) { + ip_reassbitmap[i] = 0xff; + } + LWIP_ASSERT("(offset + len) / (8 * 8) < sizeof(ip_reassbitmap)", + (offset + len) / (8 * 8) < sizeof(ip_reassbitmap)); + ip_reassbitmap[(offset + len) / (8 * 8)] |= + ~bitmap_bits[((offset + len) / 8) & 7]; + } + + /* If this fragment has the More Fragments flag set to zero, we + know that this is the last fragment, so we can calculate the + size of the entire packet. We also set the + IP_REASS_FLAG_LASTFRAG flag to indicate that we have received + the final fragment. */ + + if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) { + ip_reassflags |= IP_REASS_FLAG_LASTFRAG; + ip_reasslen = offset + len; + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip_reass: last fragment seen, total len %"S16_F"\n", + ip_reasslen)); + } + + /* Finally, we check if we have a full packet in the buffer. We do + this by checking if we have the last fragment and if all bits + in the bitmap are set. */ + if (ip_reassflags & IP_REASS_FLAG_LASTFRAG) { + /* Check all bytes up to and including all but the last byte in + the bitmap. */ + LWIP_ASSERT("ip_reasslen / (8 * 8) - 1 < sizeof(ip_reassbitmap)", + ip_reasslen / (8 * 8) - 1 < sizeof(ip_reassbitmap)); + for (i = 0; i < ip_reasslen / (8 * 8) - 1; ++i) { + if (ip_reassbitmap[i] != 0xff) { + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip_reass: last fragment seen, bitmap %"S16_F"/%"S16_F" failed (%"X16_F")\n", + i, ip_reasslen / (8 * 8) - 1, ip_reassbitmap[i])); + goto nullreturn; + } + } + /* Check the last byte in the bitmap. It should contain just the + right amount of bits. */ + LWIP_ASSERT("ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap)", + ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap)); + if (ip_reassbitmap[ip_reasslen / (8 * 8)] != + (u8_t) ~ bitmap_bits[ip_reasslen / 8 & 7]) { + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip_reass: last fragment seen, bitmap %"S16_F" didn't contain %"X16_F" (%"X16_F")\n", + ip_reasslen / (8 * 8), ~bitmap_bits[ip_reasslen / 8 & 7], + ip_reassbitmap[ip_reasslen / (8 * 8)])); + goto nullreturn; + } + + /* Pretend to be a "normal" (i.e., not fragmented) IP packet + from now on. */ + ip_reasslen += IP_HLEN; + + IPH_LEN_SET(iphdr, htons(ip_reasslen)); + IPH_OFFSET_SET(iphdr, 0); + IPH_CHKSUM_SET(iphdr, 0); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + + /* If we have come this far, we have a full packet in the + buffer, so we allocate a pbuf and copy the packet into it. We + also reset the timer. */ + ip_reasstmr = 0; + pbuf_free(p); + p = pbuf_alloc(PBUF_LINK, ip_reasslen, PBUF_POOL); + if (p != NULL) { + i = 0; + for (q = p; q != NULL; q = q->next) { + /* Copy enough bytes to fill this pbuf in the chain. The + available data in the pbuf is given by the q->len variable. */ + LWIP_DEBUGF(IP_REASS_DEBUG, + ("ip_reass: memcpy from %p (%"S16_F") to %p, %"S16_F" bytes\n", + (void *)&ip_reassbuf[i], i, q->payload, + q->len > ip_reasslen - i ? ip_reasslen - i : q->len)); + memcpy(q->payload, &ip_reassbuf[i], + q->len > ip_reasslen - i ? ip_reasslen - i : q->len); + i += q->len; + } + IPFRAG_STATS_INC(ip_frag.fw); + } else { + IPFRAG_STATS_INC(ip_frag.memerr); + } + LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", (void*)p)); + return p; + } + } + +nullreturn: + IPFRAG_STATS_INC(ip_frag.drop); + pbuf_free(p); + return NULL; +} + +#define MAX_MTU 1500 +static u8_t buf[MEM_ALIGN_SIZE(MAX_MTU)]; + +/** + * Fragment an IP datagram if too large for the netif. + * + * Chop the datagram in MTU sized chunks and send them in order + * by using a fixed size static memory buffer (PBUF_ROM) + */ +err_t +ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest) +{ + struct pbuf *rambuf; + struct pbuf *header; + struct ip_hdr *iphdr; + u16_t nfb = 0; + u16_t left, cop; + u16_t mtu = netif->mtu; + u16_t ofo, omf; + u16_t last; + u16_t poff = IP_HLEN; + u16_t tmp; + + /* Get a RAM based MTU sized pbuf */ + rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF); + if (rambuf == NULL) { + return ERR_MEM; + } + rambuf->tot_len = rambuf->len = mtu; + rambuf->payload = MEM_ALIGN((void *)buf); + + /* Copy the IP header in it */ + iphdr = rambuf->payload; + memcpy(iphdr, p->payload, IP_HLEN); + + /* Save original offset */ + tmp = ntohs(IPH_OFFSET(iphdr)); + ofo = tmp & IP_OFFMASK; + omf = tmp & IP_MF; + + left = p->tot_len - IP_HLEN; + + while (left) { + last = (left <= mtu - IP_HLEN); + + /* Set new offset and MF flag */ + ofo += nfb; + tmp = omf | (IP_OFFMASK & (ofo)); + if (!last) + tmp = tmp | IP_MF; + IPH_OFFSET_SET(iphdr, htons(tmp)); + + /* Fill this fragment */ + nfb = (mtu - IP_HLEN) / 8; + cop = last ? left : nfb * 8; + + p = copy_from_pbuf(p, &poff, (u8_t *) iphdr + IP_HLEN, cop); + + /* Correct header */ + IPH_LEN_SET(iphdr, htons(cop + IP_HLEN)); + IPH_CHKSUM_SET(iphdr, 0); + IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); + + if (last) + pbuf_realloc(rambuf, left + IP_HLEN); + /* This part is ugly: we alloc a RAM based pbuf for + * the link level header for each chunk and then + * free it.A PBUF_ROM style pbuf for which pbuf_header + * worked would make things simpler. + */ + header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM); + if (header != NULL) { + pbuf_chain(header, rambuf); + netif->output(netif, header, dest); + IPFRAG_STATS_INC(ip_frag.xmit); + pbuf_free(header); + } else { + pbuf_free(rambuf); + return ERR_MEM; + } + left -= cop; + } + pbuf_free(rambuf); + return ERR_OK; +} diff --git a/wii/libogc/lwip/core/mem.c b/wii/libogc/lwip/core/mem.c new file mode 100644 index 0000000000..ffe3f57c39 --- /dev/null +++ b/wii/libogc/lwip/core/mem.c @@ -0,0 +1,310 @@ +/** @file + * + * Dynamic memory manager + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include + +#include "lwip/arch.h" +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" + +#include "lwip/sys.h" + +#include "lwip/stats.h" + +struct mem { + mem_size_t next, prev; +#if MEM_ALIGNMENT == 1 + u8_t used; +#elif MEM_ALIGNMENT == 2 + u16_t used; +#elif MEM_ALIGNMENT == 4 + u32_t used; +#elif MEM_ALIGNMENT == 8 + u64_t used; +#else +#error "unhandled MEM_ALIGNMENT size" +#endif /* MEM_ALIGNMENT */ +}; + +static struct mem *ram_end; +static u8_t ram[MEM_SIZE + sizeof(struct mem) + MEM_ALIGNMENT]; + +#define MIN_SIZE 12 +#if 0 /* this one does not align correctly for some, resulting in crashes */ +#define SIZEOF_STRUCT_MEM (unsigned int)MEM_ALIGN_SIZE(sizeof(struct mem)) +#else +#define SIZEOF_STRUCT_MEM (sizeof(struct mem) + \ + (((sizeof(struct mem) % MEM_ALIGNMENT) == 0)? 0 : \ + (4 - (sizeof(struct mem) % MEM_ALIGNMENT)))) +#endif + +static struct mem *lfree; /* pointer to the lowest free block */ + +static sys_sem mem_sem; + +static void +plug_holes(struct mem *mem) +{ + struct mem *nmem; + struct mem *pmem; + + LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram); + LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end); + LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0); + + /* plug hole forward */ + LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE", mem->next <= MEM_SIZE); + + nmem = (struct mem *)&ram[mem->next]; + if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) { + if (lfree == nmem) { + lfree = mem; + } + mem->next = nmem->next; + ((struct mem *)&ram[nmem->next])->prev = (u8_t *)mem - ram; + } + + /* plug hole backward */ + pmem = (struct mem *)&ram[mem->prev]; + if (pmem != mem && pmem->used == 0) { + if (lfree == mem) { + lfree = pmem; + } + pmem->next = mem->next; + ((struct mem *)&ram[mem->next])->prev = (u8_t *)pmem - ram; + } + +} +void +mem_init(void) +{ + struct mem *mem; + + memset(ram, 0, MEM_SIZE); + mem = (struct mem *)ram; + mem->next = MEM_SIZE; + mem->prev = 0; + mem->used = 0; + ram_end = (struct mem *)&ram[MEM_SIZE]; + ram_end->used = 1; + ram_end->next = MEM_SIZE; + ram_end->prev = MEM_SIZE; + + LWP_SemInit(&mem_sem,1,1); + + lfree = (struct mem *)ram; + +#if MEM_STATS + lwip_stats.mem.avail = MEM_SIZE; +#endif /* MEM_STATS */ +} +void +mem_free(void *rmem) +{ + struct mem *mem; + + if (rmem == NULL) { + LWIP_DEBUGF(MEM_DEBUG | DBG_TRACE | 2, ("mem_free(p == NULL) was called.\n")); + return; + } + + LWP_SemWait(mem_sem); + + LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + LWIP_DEBUGF(MEM_DEBUG | 3, ("mem_free: illegal memory\n")); +#if MEM_STATS + ++lwip_stats.mem.err; +#endif /* MEM_STATS */ + LWP_SemPost(mem_sem); + return; + } + mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + + LWIP_ASSERT("mem_free: mem->used", mem->used); + + mem->used = 0; + + if (mem < lfree) { + lfree = mem; + } + +#if MEM_STATS + lwip_stats.mem.used -= mem->next - ((u8_t *)mem - ram); + +#endif /* MEM_STATS */ + plug_holes(mem); + LWP_SemPost(mem_sem); +} +void * +mem_reallocm(void *rmem, mem_size_t newsize) +{ + void *nmem; + nmem = mem_malloc(newsize); + if (nmem == NULL) { + return mem_realloc(rmem, newsize); + } + memcpy(nmem, rmem, newsize); + mem_free(rmem); + return nmem; +} + +void * +mem_realloc(void *rmem, mem_size_t newsize) +{ + mem_size_t size; + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + if ((newsize % MEM_ALIGNMENT) != 0) { + newsize += MEM_ALIGNMENT - ((newsize + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT); + } + + if (newsize > MEM_SIZE) { + return NULL; + } + + LWP_SemWait(mem_sem); + + LWIP_ASSERT("mem_realloc: legal memory", (u8_t *)rmem >= (u8_t *)ram && + (u8_t *)rmem < (u8_t *)ram_end); + + if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) { + LWIP_DEBUGF(MEM_DEBUG | 3, ("mem_realloc: illegal memory\n")); + return rmem; + } + mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM); + + ptr = (u8_t *)mem - ram; + + size = mem->next - ptr - SIZEOF_STRUCT_MEM; +#if MEM_STATS + lwip_stats.mem.used -= (size - newsize); +#endif /* MEM_STATS */ + + if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size) { + ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; + mem2 = (struct mem *)&ram[ptr2]; + mem2->used = 0; + mem2->next = mem->next; + mem2->prev = ptr; + mem->next = ptr2; + if (mem2->next != MEM_SIZE) { + ((struct mem *)&ram[mem2->next])->prev = ptr2; + } + + plug_holes(mem2); + } + LWP_SemPost(mem_sem); + return rmem; +} +void * +mem_malloc(mem_size_t size) +{ + mem_size_t ptr, ptr2; + struct mem *mem, *mem2; + + if (size == 0) { + return NULL; + } + + /* Expand the size of the allocated memory region so that we can + adjust for alignment. */ + if ((size % MEM_ALIGNMENT) != 0) { + size += MEM_ALIGNMENT - ((size + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT); + } + + if (size > MEM_SIZE) { + return NULL; + } + + LWP_SemWait(mem_sem); + + for (ptr = (u8_t *)lfree - ram; ptr < MEM_SIZE; ptr = ((struct mem *)&ram[ptr])->next) { + mem = (struct mem *)&ram[ptr]; + if (!mem->used && + mem->next - (ptr + SIZEOF_STRUCT_MEM) >= size + SIZEOF_STRUCT_MEM) { + ptr2 = ptr + SIZEOF_STRUCT_MEM + size; + mem2 = (struct mem *)&ram[ptr2]; + + mem2->prev = ptr; + mem2->next = mem->next; + mem->next = ptr2; + if (mem2->next != MEM_SIZE) { + ((struct mem *)&ram[mem2->next])->prev = ptr2; + } + + mem2->used = 0; + mem->used = 1; +#if MEM_STATS + lwip_stats.mem.used += (size + SIZEOF_STRUCT_MEM); + /* if (lwip_stats.mem.max < lwip_stats.mem.used) { + lwip_stats.mem.max = lwip_stats.mem.used; + } */ + if (lwip_stats.mem.max < ptr2) { + lwip_stats.mem.max = ptr2; + } +#endif /* MEM_STATS */ + + if (mem == lfree) { + /* Find next free block after mem */ + while (lfree->used && lfree != ram_end) { + lfree = (struct mem *)&ram[lfree->next]; + } + LWIP_ASSERT("mem_malloc: !lfree->used", !lfree->used); + } + LWP_SemPost(mem_sem); + LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.", + (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end); + LWIP_ASSERT("mem_malloc: allocated memory properly aligned.", + (unsigned long)((u8_t *)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0); + return (u8_t *)mem + SIZEOF_STRUCT_MEM; + } + } + LWIP_DEBUGF(MEM_DEBUG | 2, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size)); +#if MEM_STATS + ++lwip_stats.mem.err; +#endif /* MEM_STATS */ + LWP_SemPost(mem_sem); + return NULL; +} diff --git a/wii/libogc/lwip/core/memp.c b/wii/libogc/lwip/core/memp.c new file mode 100644 index 0000000000..3f5ca97727 --- /dev/null +++ b/wii/libogc/lwip/core/memp.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/memp.h" + +#include "lwip/pbuf.h" +#include "lwip/udp.h" +#include "lwip/raw.h" +#include "lwip/tcp.h" +#include "lwip/api.h" +#include "lwip/api_msg.h" +#include "lwip/tcpip.h" + +#include "lwip/sys.h" +#include "lwip/stats.h" + +struct memp { + struct memp *next; +}; + + + +static struct memp *memp_tab[MEMP_MAX]; + +static const u16_t memp_sizes[MEMP_MAX] = { + sizeof(struct pbuf), + sizeof(struct raw_pcb), + sizeof(struct udp_pcb), + sizeof(struct tcp_pcb), + sizeof(struct tcp_pcb_listen), + sizeof(struct tcp_seg), + sizeof(struct netbuf), + sizeof(struct netconn), + sizeof(struct api_msg), + sizeof(struct net_msg), + sizeof(struct sys_timeout) +}; + +static const u16_t memp_num[MEMP_MAX] = { + MEMP_NUM_PBUF, + MEMP_NUM_RAW_PCB, + MEMP_NUM_UDP_PCB, + MEMP_NUM_TCP_PCB, + MEMP_NUM_TCP_PCB_LISTEN, + MEMP_NUM_TCP_SEG, + MEMP_NUM_NETBUF, + MEMP_NUM_NETCONN, + MEMP_NUM_API_MSG, + MEMP_NUM_TCPIP_MSG, + MEMP_NUM_SYS_TIMEOUT +}; + +static u8_t memp_memory[(MEMP_NUM_PBUF * + MEM_ALIGN_SIZE(sizeof(struct pbuf) + + sizeof(struct memp)) + + MEMP_NUM_RAW_PCB * + MEM_ALIGN_SIZE(sizeof(struct raw_pcb) + + sizeof(struct memp)) + + MEMP_NUM_UDP_PCB * + MEM_ALIGN_SIZE(sizeof(struct udp_pcb) + + sizeof(struct memp)) + + MEMP_NUM_TCP_PCB * + MEM_ALIGN_SIZE(sizeof(struct tcp_pcb) + + sizeof(struct memp)) + + MEMP_NUM_TCP_PCB_LISTEN * + MEM_ALIGN_SIZE(sizeof(struct tcp_pcb_listen) + + sizeof(struct memp)) + + MEMP_NUM_TCP_SEG * + MEM_ALIGN_SIZE(sizeof(struct tcp_seg) + + sizeof(struct memp)) + + MEMP_NUM_NETBUF * + MEM_ALIGN_SIZE(sizeof(struct netbuf) + + sizeof(struct memp)) + + MEMP_NUM_NETCONN * + MEM_ALIGN_SIZE(sizeof(struct netconn) + + sizeof(struct memp)) + + MEMP_NUM_API_MSG * + MEM_ALIGN_SIZE(sizeof(struct api_msg) + + sizeof(struct memp)) + + MEMP_NUM_TCPIP_MSG * + MEM_ALIGN_SIZE(sizeof(struct net_msg) + + sizeof(struct memp)) + + MEMP_NUM_SYS_TIMEOUT * + MEM_ALIGN_SIZE(sizeof(struct sys_timeout) + + sizeof(struct memp)))]; + + +#if !SYS_LIGHTWEIGHT_PROT +static sys_sem mutex; +#endif + +#if MEMP_SANITY_CHECK +static int +memp_sanity(void) +{ + s16_t i, c; + struct memp *m, *n; + + for(i = 0; i < MEMP_MAX; i++) { + for(m = memp_tab[i]; m != NULL; m = m->next) { + c = 1; + for(n = memp_tab[i]; n != NULL; n = n->next) { + if (n == m) { + --c; + } + if (c < 0) return 0; /* LW was: abort(); */ + } + } + } + return 1; +} +#endif /* MEMP_SANITY_CHECK*/ + +void +memp_init(void) +{ + struct memp *m, *memp; + u16_t i, j; + u16_t size; + +#if MEMP_STATS + for(i = 0; i < MEMP_MAX; ++i) { + lwip_stats.memp[i].used = lwip_stats.memp[i].max = + lwip_stats.memp[i].err = 0; + lwip_stats.memp[i].avail = memp_num[i]; + } +#endif /* MEMP_STATS */ + + memp = (struct memp *)&memp_memory[0]; + for(i = 0; i < MEMP_MAX; ++i) { + size = MEM_ALIGN_SIZE(memp_sizes[i] + sizeof(struct memp)); + if (memp_num[i] > 0) { + memp_tab[i] = memp; + m = memp; + + for(j = 0; j < memp_num[i]; ++j) { + m->next = (struct memp *)MEM_ALIGN((u8_t *)m + size); + memp = m; + m = m->next; + } + memp->next = NULL; + memp = m; + } else { + memp_tab[i] = NULL; + } + } + +#if !SYS_LIGHTWEIGHT_PROT + LWP_SemInit(&mutex,1,1); +#endif + + +} + +void * +memp_malloc(memp_t type) +{ + struct memp *memp; + void *mem; +#if SYS_LIGHTWEIGHT_PROT + SYS_ARCH_DECL_PROTECT(old_level); +#endif + + LWIP_ASSERT("memp_malloc: type < MEMP_MAX", type < MEMP_MAX); + +#if SYS_LIGHTWEIGHT_PROT + SYS_ARCH_PROTECT(old_level); +#else /* SYS_LIGHTWEIGHT_PROT */ + LWP_SemWait(mutex); +#endif /* SYS_LIGHTWEIGHT_PROT */ + + memp = memp_tab[type]; + if (memp != NULL) { + memp_tab[type] = memp->next; + memp->next = NULL; +#if MEMP_STATS + ++lwip_stats.memp[type].used; + if (lwip_stats.memp[type].used > lwip_stats.memp[type].max) { + lwip_stats.memp[type].max = lwip_stats.memp[type].used; + } +#endif /* MEMP_STATS */ +#if SYS_LIGHTWEIGHT_PROT + SYS_ARCH_UNPROTECT(old_level); +#else /* SYS_LIGHTWEIGHT_PROT */ + LWP_SemPost(mutex); +#endif /* SYS_LIGHTWEIGHT_PROT */ + LWIP_ASSERT("memp_malloc: memp properly aligned", + ((mem_ptr_t)MEM_ALIGN((u8_t *)memp + sizeof(struct memp)) % MEM_ALIGNMENT) == 0); + + mem = MEM_ALIGN((u8_t *)memp + sizeof(struct memp)); + return mem; + } else { + LWIP_DEBUGF(MEMP_DEBUG | 2, ("memp_malloc: out of memory in pool %"S16_F"\n", type)); +#if MEMP_STATS + ++lwip_stats.memp[type].err; +#endif /* MEMP_STATS */ +#if SYS_LIGHTWEIGHT_PROT + SYS_ARCH_UNPROTECT(old_level); +#else /* SYS_LIGHTWEIGHT_PROT */ + LWP_SemPost(mutex); +#endif /* SYS_LIGHTWEIGHT_PROT */ + return NULL; + } +} + +void +memp_free(memp_t type, void *mem) +{ + struct memp *memp; +#if SYS_LIGHTWEIGHT_PROT + SYS_ARCH_DECL_PROTECT(old_level); +#endif /* SYS_LIGHTWEIGHT_PROT */ + + if (mem == NULL) { + return; + } + memp = (struct memp *)((u8_t *)mem - sizeof(struct memp)); + +#if SYS_LIGHTWEIGHT_PROT + SYS_ARCH_PROTECT(old_level); +#else /* SYS_LIGHTWEIGHT_PROT */ + LWP_SemWait(mutex); +#endif /* SYS_LIGHTWEIGHT_PROT */ + +#if MEMP_STATS + lwip_stats.memp[type].used--; +#endif /* MEMP_STATS */ + + memp->next = memp_tab[type]; + memp_tab[type] = memp; + +#if MEMP_SANITY_CHECK + LWIP_ASSERT("memp sanity", memp_sanity()); +#endif + +#if SYS_LIGHTWEIGHT_PROT + SYS_ARCH_UNPROTECT(old_level); +#else /* SYS_LIGHTWEIGHT_PROT */ + LWP_SemPost(mutex); +#endif /* SYS_LIGHTWEIGHT_PROT */ +} + diff --git a/wii/libogc/lwip/core/netif.c b/wii/libogc/lwip/core/netif.c new file mode 100644 index 0000000000..3525089b2f --- /dev/null +++ b/wii/libogc/lwip/core/netif.c @@ -0,0 +1,288 @@ +/** + * @file + * + * lwIP network interface abstraction + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/tcp.h" + +struct netif *netif_list = NULL; +struct netif *netif_default = NULL; + +/** + * Add a network interface to the list of lwIP netifs. + * + * @param netif a pre-allocated netif structure + * @param ipaddr IP address for the new netif + * @param netmask network mask for the new netif + * @param gw default gateway IP address for the new netif + * @param state opaque data passed to the new netif + * @param init callback function that initializes the interface + * @param input callback function that is called to pass + * ingress packets up in the protocol layer stack. + * + * @return netif, or NULL if failed. + */ +struct netif * +netif_add(struct netif *netif, struct ip_addr *ipaddr, struct ip_addr *netmask, + struct ip_addr *gw, + void *state, + err_t (* init)(struct netif *netif), + err_t (* input)(struct pbuf *p, struct netif *netif)) +{ + static s16_t netifnum = 0; + +#if LWIP_DHCP + /* netif not under DHCP control by default */ + netif->dhcp = NULL; +#endif + /* remember netif specific state information data */ + netif->state = state; + netif->num = netifnum++; + netif->input = input; + + netif_set_addr(netif, ipaddr, netmask, gw); + + /* call user specified initialization function for netif */ + if (init(netif) != ERR_OK) { + return NULL; + } + + /* add this netif to the list */ + netif->next = netif_list; + netif_list = netif; + LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ", + netif->name[0], netif->name[1])); + ip_addr_debug_print(NETIF_DEBUG, ipaddr); + LWIP_DEBUGF(NETIF_DEBUG, (" netmask ")); + ip_addr_debug_print(NETIF_DEBUG, netmask); + LWIP_DEBUGF(NETIF_DEBUG, (" gw ")); + ip_addr_debug_print(NETIF_DEBUG, gw); + LWIP_DEBUGF(NETIF_DEBUG, ("\n")); + return netif; +} + +void +netif_set_addr(struct netif *netif,struct ip_addr *ipaddr, struct ip_addr *netmask, + struct ip_addr *gw) +{ + netif_set_ipaddr(netif, ipaddr); + netif_set_netmask(netif, netmask); + netif_set_gw(netif, gw); +} + +void netif_remove(struct netif * netif) +{ + if ( netif == NULL ) return; + + /* is it the first netif? */ + if (netif_list == netif) { + netif_list = netif->next; + } + else { + /* look for netif further down the list */ + struct netif * tmpNetif; + for (tmpNetif = netif_list; tmpNetif != NULL; tmpNetif = tmpNetif->next) { + if (tmpNetif->next == netif) { + tmpNetif->next = netif->next; + break; + } + } + if (tmpNetif == NULL) + return; /* we didn't find any netif today */ + } + /* this netif is default? */ + if (netif_default == netif) + /* reset default netif */ + netif_default = NULL; + LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") ); +} + +struct netif * +netif_find(char *name) +{ + struct netif *netif; + u8_t num; + + if (name == NULL) { + return NULL; + } + + num = name[2] - '0'; + + for(netif = netif_list; netif != NULL; netif = netif->next) { + if (num == netif->num && + name[0] == netif->name[0] && + name[1] == netif->name[1]) { + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1])); + return netif; + } + } + LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1])); + return NULL; +} + +void +netif_set_ipaddr(struct netif *netif, struct ip_addr *ipaddr) +{ + /* TODO: Handling of obsolete pcbs */ + /* See: http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */ +#if LWIP_TCP + struct tcp_pcb *pcb; + struct tcp_pcb_listen *lpcb; + + /* address is actually being changed? */ + if ((ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) + { + /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */ + LWIP_DEBUGF(NETIF_DEBUG | 1, ("netif_set_ipaddr: netif address being changed\n")); + pcb = tcp_active_pcbs; + while (pcb != NULL) { + /* PCB bound to current local interface address? */ + if (ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) { + /* this connection must be aborted */ + struct tcp_pcb *next = pcb->next; + LWIP_DEBUGF(NETIF_DEBUG | 1, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb)); + tcp_abort(pcb); + pcb = next; + } else { + pcb = pcb->next; + } + } + for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + /* PCB bound to current local interface address? */ + if (ip_addr_cmp(&(lpcb->local_ip), &(netif->ip_addr))) { + /* The PCB is listening to the old ipaddr and + * is set to listen to the new one instead */ + ip_addr_set(&(lpcb->local_ip), ipaddr); + } + } + } +#endif + ip_addr_set(&(netif->ip_addr), ipaddr); +#if 0 /* only allowed for Ethernet interfaces TODO: how can we check? */ + /** For Ethernet network interfaces, we would like to send a + * "gratuitous ARP"; this is an ARP packet sent by a node in order + * to spontaneously cause other nodes to update an entry in their + * ARP cache. From RFC 3220 "IP Mobility Support for IPv4" section 4.6. + */ + etharp_query(netif, ipaddr, NULL); +#endif + LWIP_DEBUGF(NETIF_DEBUG | DBG_TRACE | DBG_STATE | 3, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1(&netif->ip_addr), + ip4_addr2(&netif->ip_addr), + ip4_addr3(&netif->ip_addr), + ip4_addr4(&netif->ip_addr))); +} + +void +netif_set_gw(struct netif *netif, struct ip_addr *gw) +{ + ip_addr_set(&(netif->gw), gw); + LWIP_DEBUGF(NETIF_DEBUG | DBG_TRACE | DBG_STATE | 3, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1(&netif->gw), + ip4_addr2(&netif->gw), + ip4_addr3(&netif->gw), + ip4_addr4(&netif->gw))); +} + +void +netif_set_netmask(struct netif *netif, struct ip_addr *netmask) +{ + ip_addr_set(&(netif->netmask), netmask); + LWIP_DEBUGF(NETIF_DEBUG | DBG_TRACE | DBG_STATE | 3, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + netif->name[0], netif->name[1], + ip4_addr1(&netif->netmask), + ip4_addr2(&netif->netmask), + ip4_addr3(&netif->netmask), + ip4_addr4(&netif->netmask))); +} + +void +netif_set_default(struct netif *netif) +{ + netif_default = netif; + LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n", + netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\'')); +} + +/** + * Bring an interface up, available for processing + * traffic. + * + * @note: Enabling DHCP on a down interface will make it come + * up once configured. + * + * @see dhcp_start() + */ +void netif_set_up(struct netif *netif) +{ + netif->flags |= NETIF_FLAG_UP; +} + +/** + * Ask if an interface is up + */ +u8_t netif_is_up(struct netif *netif) +{ + return (netif->flags & NETIF_FLAG_UP)?1:0; +} + +/** + * Bring an interface down, disabling any traffic processing. + * + * @note: Enabling DHCP on a down interface will make it come + * up once configured. + * + * @see dhcp_start() + */ +void netif_set_down(struct netif *netif) +{ + netif->flags &= ~NETIF_FLAG_UP; +} + +void +netif_init(void) +{ + netif_list = netif_default = NULL; +} + diff --git a/wii/libogc/lwip/core/pbuf.c b/wii/libogc/lwip/core/pbuf.c new file mode 100644 index 0000000000..471e6efb7a --- /dev/null +++ b/wii/libogc/lwip/core/pbuf.c @@ -0,0 +1,957 @@ +/** + * @file + * Packet buffer management + * + * Packets are built from the pbuf data structure. It supports dynamic + * memory allocation for packet contents or can reference externally + * managed packet contents both in RAM and ROM. Quick allocation for + * incoming packets is provided through pools with fixed sized pbufs. + * + * A packet may span over multiple pbufs, chained as a singly linked + * list. This is called a "pbuf chain". + * + * Multiple packets may be queued, also using this singly linked list. + * This is called a "packet queue". + * + * So, a packet queue consists of one or more pbuf chains, each of + * which consist of one or more pbufs. Currently, queues are only + * supported in a limited section of lwIP, this is the etharp queueing + * code. Outside of this section no packet queues are supported yet. + * + * The differences between a pbuf chain and a packet queue are very + * precise but subtle. + * + * The last pbuf of a packet has a ->tot_len field that equals the + * ->len field. It can be found by traversing the list. If the last + * pbuf of a packet has a ->next field other than NULL, more packets + * are on the queue. + * + * Therefore, looping through a pbuf of a single packet, has an + * loop end condition (tot_len == p->len), NOT (next == NULL). + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include + +#include "lwip/opt.h" +#include "lwip/stats.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "arch/perf.h" + +static u8_t pbuf_pool_memory[MEM_ALIGNMENT - 1 + PBUF_POOL_SIZE * MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE + sizeof(struct pbuf))]; + +#if !SYS_LIGHTWEIGHT_PROT +static volatile u8_t pbuf_pool_free_lock, pbuf_pool_alloc_lock; +static sys_sem pbuf_pool_free_sem; +#endif + +static struct pbuf *pbuf_pool = NULL; + +/** + * Initializes the pbuf module. + * + * A large part of memory is allocated for holding the pool of pbufs. + * The size of the individual pbufs in the pool is given by the size + * parameter, and the number of pbufs in the pool by the num parameter. + * + * After the memory has been allocated, the pbufs are set up. The + * ->next pointer in each pbuf is set up to point to the next pbuf in + * the pool. + * + */ +void +pbuf_init(void) +{ + struct pbuf *p, *q = NULL; + u16_t i; + + pbuf_pool = (struct pbuf *)MEM_ALIGN(pbuf_pool_memory); + +#if PBUF_STATS + lwip_stats.pbuf.avail = PBUF_POOL_SIZE; +#endif /* PBUF_STATS */ + + /* Set up ->next pointers to link the pbufs of the pool together */ + p = pbuf_pool; + + for(i = 0; i < PBUF_POOL_SIZE; ++i) { + p->next = (struct pbuf *)((u8_t *)p + PBUF_POOL_BUFSIZE + sizeof(struct pbuf)); + p->len = p->tot_len = PBUF_POOL_BUFSIZE; + p->payload = MEM_ALIGN((void *)((u8_t *)p + sizeof(struct pbuf))); + p->flags = PBUF_FLAG_POOL; + q = p; + p = p->next; + } + + /* The ->next pointer of last pbuf is NULL to indicate that there + are no more pbufs in the pool */ + q->next = NULL; + +#if !SYS_LIGHTWEIGHT_PROT + pbuf_pool_alloc_lock = 0; + pbuf_pool_free_lock = 0; + LWP_SemInit(&pbuf_pool_free_sem,1,1); +#endif +} + +/** + * @internal only called from pbuf_alloc() + */ +static struct pbuf * +pbuf_pool_alloc(void) +{ + struct pbuf *p = NULL; + + SYS_ARCH_DECL_PROTECT(old_level); + SYS_ARCH_PROTECT(old_level); + +#if !SYS_LIGHTWEIGHT_PROT + /* Next, check the actual pbuf pool, but if the pool is locked, we + pretend to be out of buffers and return NULL. */ + if (pbuf_pool_free_lock) { +#if PBUF_STATS + ++lwip_stats.pbuf.alloc_locked; +#endif /* PBUF_STATS */ + return NULL; + } + pbuf_pool_alloc_lock = 1; + if (!pbuf_pool_free_lock) { +#endif /* SYS_LIGHTWEIGHT_PROT */ + p = pbuf_pool; + if (p) { + pbuf_pool = p->next; + } +#if !SYS_LIGHTWEIGHT_PROT +#if PBUF_STATS + } else { + ++lwip_stats.pbuf.alloc_locked; +#endif /* PBUF_STATS */ + } + pbuf_pool_alloc_lock = 0; +#endif /* SYS_LIGHTWEIGHT_PROT */ + +#if PBUF_STATS + if (p != NULL) { + ++lwip_stats.pbuf.used; + if (lwip_stats.pbuf.used > lwip_stats.pbuf.max) { + lwip_stats.pbuf.max = lwip_stats.pbuf.used; + } + } +#endif /* PBUF_STATS */ + + SYS_ARCH_UNPROTECT(old_level); + return p; +} + + +/** + * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). + * + * The actual memory allocated for the pbuf is determined by the + * layer at which the pbuf is allocated and the requested size + * (from the size parameter). + * + * @param flag this parameter decides how and where the pbuf + * should be allocated as follows: + * + * - PBUF_RAM: buffer memory for pbuf is allocated as one large + * chunk. This includes protocol headers as well. + * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for + * protocol headers. Additional headers must be prepended + * by allocating another pbuf and chain in to the front of + * the ROM pbuf. It is assumed that the memory used is really + * similar to ROM in that it is immutable and will not be + * changed. Memory which is dynamic should generally not + * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. + * - PBUF_REF: no buffer memory is allocated for the pbuf, even for + * protocol headers. It is assumed that the pbuf is only + * being used in a single thread. If the pbuf gets queued, + * then pbuf_take should be called to copy the buffer. + * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from + * the pbuf pool that is allocated during pbuf_init(). + * + * @return the allocated pbuf. If multiple pbufs where allocated, this + * is the first pbuf of a pbuf chain. + */ +struct pbuf * +pbuf_alloc(pbuf_layer l, u16_t length, pbuf_flag flag) +{ + struct pbuf *p, *q, *r; + u16_t offset; + s32_t rem_len; /* remaining length */ + LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_alloc(length=%"U16_F")\n", length)); + + /* determine header offset */ + offset = 0; + switch (l) { + case PBUF_TRANSPORT: + /* add room for transport (often TCP) layer header */ + offset += PBUF_TRANSPORT_HLEN; + /* FALLTHROUGH */ + case PBUF_IP: + /* add room for IP layer header */ + offset += PBUF_IP_HLEN; + /* FALLTHROUGH */ + case PBUF_LINK: + /* add room for link layer header */ + offset += PBUF_LINK_HLEN; + break; + case PBUF_RAW: + break; + default: + LWIP_ASSERT("pbuf_alloc: bad pbuf layer", 0); + return NULL; + } + + switch (flag) { + case PBUF_POOL: + /* allocate head of pbuf chain into p */ + p = pbuf_pool_alloc(); + LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_alloc: allocated pbuf %p\n", (void *)p)); + if (p == NULL) { +#if PBUF_STATS + ++lwip_stats.pbuf.err; +#endif /* PBUF_STATS */ + return NULL; + } + p->next = NULL; + + /* make the payload pointer point 'offset' bytes into pbuf data memory */ + p->payload = MEM_ALIGN((void *)((u8_t *)p + (sizeof(struct pbuf) + offset))); + LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + /* the total length of the pbuf chain is the requested size */ + p->tot_len = length; + /* set the length of the first pbuf in the chain */ + p->len = length > PBUF_POOL_BUFSIZE - offset? PBUF_POOL_BUFSIZE - offset: length; + /* set reference count (needed here in case we fail) */ + p->ref = 1; + + /* now allocate the tail of the pbuf chain */ + + /* remember first pbuf for linkage in next iteration */ + r = p; + /* remaining length to be allocated */ + rem_len = length - p->len; + /* any remaining pbufs to be allocated? */ + while (rem_len > 0) { + q = pbuf_pool_alloc(); + if (q == NULL) { + LWIP_DEBUGF(PBUF_DEBUG | 2, ("pbuf_alloc: Out of pbufs in pool.\n")); +#if PBUF_STATS + ++lwip_stats.pbuf.err; +#endif /* PBUF_STATS */ + /* free chain so far allocated */ + pbuf_free(p); + /* bail out unsuccesfully */ + return NULL; + } + q->next = NULL; + /* make previous pbuf point to this pbuf */ + r->next = q; + /* set total length of this pbuf and next in chain */ + q->tot_len = rem_len; + /* this pbuf length is pool size, unless smaller sized tail */ + q->len = rem_len > PBUF_POOL_BUFSIZE? PBUF_POOL_BUFSIZE: rem_len; + q->payload = (void *)((u8_t *)q + sizeof(struct pbuf)); + LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", + ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); + q->ref = 1; + /* calculate remaining length to be allocated */ + rem_len -= q->len; + /* remember this pbuf for linkage in next iteration */ + r = q; + } + /* end of chain */ + /*r->next = NULL;*/ + + break; + case PBUF_RAM: + /* If pbuf is to be allocated in RAM, allocate memory for it. */ + p = mem_malloc(MEM_ALIGN_SIZE(sizeof(struct pbuf) + offset) + MEM_ALIGN_SIZE(length)); + if (p == NULL) { + return NULL; + } + /* Set up internal structure of the pbuf. */ + p->payload = MEM_ALIGN((void *)((u8_t *)p + sizeof(struct pbuf) + offset)); + p->len = p->tot_len = length; + p->next = NULL; + p->flags = PBUF_FLAG_RAM; + + LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", + ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); + break; + /* pbuf references existing (non-volatile static constant) ROM payload? */ + case PBUF_ROM: + /* pbuf references existing (externally allocated) RAM payload? */ + case PBUF_REF: + /* only allocate memory for the pbuf structure */ + p = memp_malloc(MEMP_PBUF); + if (p == NULL) { + LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n", flag == PBUF_ROM?"ROM":"REF")); + return NULL; + } + /* caller must set this field properly, afterwards */ + p->payload = NULL; + p->len = p->tot_len = length; + p->next = NULL; + p->flags = (flag == PBUF_ROM? PBUF_FLAG_ROM: PBUF_FLAG_REF); + break; + default: + LWIP_ASSERT("pbuf_alloc: erroneous flag", 0); + return NULL; + } + /* set reference count */ + p->ref = 1; + LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); + return p; +} + + +#if PBUF_STATS +#define DEC_PBUF_STATS do { --lwip_stats.pbuf.used; } while (0) +#else /* PBUF_STATS */ +#define DEC_PBUF_STATS +#endif /* PBUF_STATS */ + +#define PBUF_POOL_FAST_FREE(p) do { \ + p->next = pbuf_pool; \ + pbuf_pool = p; \ + DEC_PBUF_STATS; \ + } while (0) + +#if SYS_LIGHTWEIGHT_PROT +#define PBUF_POOL_FREE(p) do { \ + SYS_ARCH_DECL_PROTECT(old_level); \ + SYS_ARCH_PROTECT(old_level); \ + PBUF_POOL_FAST_FREE(p); \ + SYS_ARCH_UNPROTECT(old_level); \ + } while (0) +#else /* SYS_LIGHTWEIGHT_PROT */ +#define PBUF_POOL_FREE(p) do { \ + LWP_SemWait(pbuf_pool_free_sem); \ + PBUF_POOL_FAST_FREE(p); \ + LWP_SemPost(pbuf_pool_free_sem); \ + } while (0) +#endif /* SYS_LIGHTWEIGHT_PROT */ + +/** + * Shrink a pbuf chain to a desired length. + * + * @param p pbuf to shrink. + * @param new_len desired new length of pbuf chain + * + * Depending on the desired length, the first few pbufs in a chain might + * be skipped and left unchanged. The new last pbuf in the chain will be + * resized, and any remaining pbufs will be freed. + * + * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted. + * @note May not be called on a packet queue. + * + * @bug Cannot grow the size of a pbuf (chain) (yet). + */ +void +pbuf_realloc(struct pbuf *p, u16_t new_len) +{ + struct pbuf *q; + u16_t rem_len; /* remaining length */ + s16_t grow; + + LWIP_ASSERT("pbuf_realloc: sane p->flags", p->flags == PBUF_FLAG_POOL || + p->flags == PBUF_FLAG_ROM || + p->flags == PBUF_FLAG_RAM || + p->flags == PBUF_FLAG_REF); + + /* desired length larger than current length? */ + if (new_len >= p->tot_len) { + /* enlarging not yet supported */ + return; + } + + /* the pbuf chain grows by (new_len - p->tot_len) bytes + * (which may be negative in case of shrinking) */ + grow = new_len - p->tot_len; + + /* first, step over any pbufs that should remain in the chain */ + rem_len = new_len; + q = p; + /* should this pbuf be kept? */ + while (rem_len > q->len) { + /* decrease remaining length by pbuf length */ + rem_len -= q->len; + /* decrease total length indicator */ + q->tot_len += grow; + /* proceed to next pbuf in chain */ + q = q->next; + } + /* we have now reached the new last pbuf (in q) */ + /* rem_len == desired length for pbuf q */ + + /* shrink allocated memory for PBUF_RAM */ + /* (other types merely adjust their length fields */ + if ((q->flags == PBUF_FLAG_RAM) && (rem_len != q->len)) { + /* reallocate and adjust the length of the pbuf that will be split */ + mem_realloc(q, (u8_t *)q->payload - (u8_t *)q + rem_len); + } + /* adjust length fields for new last pbuf */ + q->len = rem_len; + q->tot_len = q->len; + + /* any remaining pbufs in chain? */ + if (q->next != NULL) { + /* free remaining pbufs in chain */ + pbuf_free(q->next); + } + /* q is last packet in chain */ + q->next = NULL; + +} + +/** + * Adjusts the payload pointer to hide or reveal headers in the payload. + * + * Adjusts the ->payload pointer so that space for a header + * (dis)appears in the pbuf payload. + * + * The ->payload, ->tot_len and ->len fields are adjusted. + * + * @param hdr_size_inc Number of bytes to increment header size which + * increases the size of the pbuf. New space is on the front. + * (Using a negative value decreases the header size.) + * If hdr_size_inc is 0, this function does nothing and returns succesful. + * + * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so + * the call will fail. A check is made that the increase in header size does + * not move the payload pointer in front of the start of the buffer. + * @return non-zero on failure, zero on success. + * + */ +u8_t +pbuf_header(struct pbuf *p, s16_t header_size_increment) +{ + void *payload; + + LWIP_ASSERT("p != NULL", p != NULL); + if ((header_size_increment == 0) || (p == NULL)) return 0; + + /* remember current payload pointer */ + payload = p->payload; + + /* pbuf types containing payloads? */ + if (p->flags == PBUF_FLAG_RAM || p->flags == PBUF_FLAG_POOL) { + /* set new payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + /* boundary check fails? */ + if ((u8_t *)p->payload < (u8_t *)p + sizeof(struct pbuf)) { + LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_header: failed as %p < %p (not enough space for new header size)\n", + (void *)p->payload, + (void *)(p + 1)));\ + /* restore old payload pointer */ + p->payload = payload; + /* bail out unsuccesfully */ + return 1; + } + /* pbuf types refering to external payloads? */ + } else if (p->flags == PBUF_FLAG_REF || p->flags == PBUF_FLAG_ROM) { + /* hide a header in the payload? */ + if ((header_size_increment < 0) && (header_size_increment - p->len <= 0)) { + /* increase payload pointer */ + p->payload = (u8_t *)p->payload - header_size_increment; + } else { + /* cannot expand payload to front (yet!) + * bail out unsuccesfully */ + return 1; + } + } + /* modify pbuf length fields */ + p->len += header_size_increment; + p->tot_len += header_size_increment; + + LWIP_DEBUGF( PBUF_DEBUG, ("pbuf_header: old %p new %p (%"S16_F")\n", + (void *)payload, (void *)p->payload, header_size_increment)); + + return 0; +} + +/** + * Dereference a pbuf chain or queue and deallocate any no-longer-used + * pbufs at the head of this chain or queue. + * + * Decrements the pbuf reference count. If it reaches zero, the pbuf is + * deallocated. + * + * For a pbuf chain, this is repeated for each pbuf in the chain, + * up to the first pbuf which has a non-zero reference count after + * decrementing. So, when all reference counts are one, the whole + * chain is free'd. + * + * @param pbuf The pbuf (chain) to be dereferenced. + * + * @return the number of pbufs that were de-allocated + * from the head of the chain. + * + * @note MUST NOT be called on a packet queue (Not verified to work yet). + * @note the reference counter of a pbuf equals the number of pointers + * that refer to the pbuf (or into the pbuf). + * + * @internal examples: + * + * Assuming existing chains a->b->c with the following reference + * counts, calling pbuf_free(a) results in: + * + * 1->2->3 becomes ...1->3 + * 3->3->3 becomes 2->3->3 + * 1->1->2 becomes ......1 + * 2->1->1 becomes 1->1->1 + * 1->1->1 becomes ....... + * + */ +u8_t +pbuf_free(struct pbuf *p) +{ + struct pbuf *q; + u8_t count; + SYS_ARCH_DECL_PROTECT(old_level); + + LWIP_ASSERT("p != NULL", p != NULL); + /* if assertions are disabled, proceed with debug output */ + if (p == NULL) { + LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_free(p == NULL) was called.\n")); + return 0; + } + LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_free(%p)\n", (void *)p)); + + PERF_START; + + LWIP_ASSERT("pbuf_free: sane flags", + p->flags == PBUF_FLAG_RAM || p->flags == PBUF_FLAG_ROM || + p->flags == PBUF_FLAG_REF || p->flags == PBUF_FLAG_POOL); + + count = 0; + /* Since decrementing ref cannot be guaranteed to be a single machine operation + * we must protect it. Also, the later test of ref must be protected. + */ + SYS_ARCH_PROTECT(old_level); + /* de-allocate all consecutive pbufs from the head of the chain that + * obtain a zero reference count after decrementing*/ + while (p != NULL) { + /* all pbufs in a chain are referenced at least once */ + LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0); + /* decrease reference count (number of pointers to pbuf) */ + p->ref--; + /* this pbuf is no longer referenced to? */ + if (p->ref == 0) { + /* remember next pbuf in chain for next iteration */ + q = p->next; + LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_free: deallocating %p\n", (void *)p)); + /* is this a pbuf from the pool? */ + if (p->flags == PBUF_FLAG_POOL) { + p->len = p->tot_len = PBUF_POOL_BUFSIZE; + p->payload = (void *)((u8_t *)p + sizeof(struct pbuf)); + PBUF_POOL_FREE(p); + /* is this a ROM or RAM referencing pbuf? */ + } else if (p->flags == PBUF_FLAG_ROM || p->flags == PBUF_FLAG_REF) { + memp_free(MEMP_PBUF, p); + /* p->flags == PBUF_FLAG_RAM */ + } else { + mem_free(p); + } + count++; + /* proceed to next pbuf */ + p = q; + /* p->ref > 0, this pbuf is still referenced to */ + /* (and so the remaining pbufs in chain as well) */ + } else { + LWIP_DEBUGF( PBUF_DEBUG | 2, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, (u16_t)p->ref)); + /* stop walking through the chain */ + p = NULL; + } + } + SYS_ARCH_UNPROTECT(old_level); + PERF_STOP("pbuf_free"); + /* return number of de-allocated pbufs */ + return count; +} + +/** + * Count number of pbufs in a chain + * + * @param p first pbuf of chain + * @return the number of pbufs in a chain + */ + +u8_t +pbuf_clen(struct pbuf *p) +{ + u8_t len; + + len = 0; + while (p != NULL) { + ++len; + p = p->next; + } + return len; +} + +/** + * Increment the reference count of the pbuf. + * + * @param p pbuf to increase reference counter of + * + */ +void +pbuf_ref(struct pbuf *p) +{ + SYS_ARCH_DECL_PROTECT(old_level); + /* pbuf given? */ + if (p != NULL) { + SYS_ARCH_PROTECT(old_level); + ++(p->ref); + SYS_ARCH_UNPROTECT(old_level); + } +} + +/** + * Concatenate two pbufs (each may be a pbuf chain) and take over + * the caller's reference of the tail pbuf. + * + * @note The caller MAY NOT reference the tail pbuf afterwards. + * Use pbuf_chain() for that purpose. + * + * @see pbuf_chain() + */ + +void +pbuf_cat(struct pbuf *h, struct pbuf *t) +{ + struct pbuf *p; + + LWIP_ASSERT("h != NULL (programmer violates API)", h != NULL); + LWIP_ASSERT("t != NULL (programmer violates API)", t != NULL); + if ((h == NULL) || (t == NULL)) return; + + /* proceed to last pbuf of chain */ + for (p = h; p->next != NULL; p = p->next) { + /* add total length of second chain to all totals of first chain */ + p->tot_len += t->tot_len; + } + /* { p is last pbuf of first h chain, p->next == NULL } */ + LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len); + LWIP_ASSERT("p->next == NULL", p->next == NULL); + /* add total length of second chain to last pbuf total of first chain */ + p->tot_len += t->tot_len; + /* chain last pbuf of head (p) with first of tail (t) */ + p->next = t; + /* p->next now references t, but the caller will drop its reference to t, + * so netto there is no change to the reference count of t. + */ +} + +/** + * Chain two pbufs (or pbuf chains) together. + * + * The caller MUST call pbuf_free(t) once it has stopped + * using it. Use pbuf_cat() instead if you no longer use t. + * + * @param h head pbuf (chain) + * @param t tail pbuf (chain) + * @note The pbufs MUST belong to the same packet. + * @note MAY NOT be called on a packet queue. + * + * The ->tot_len fields of all pbufs of the head chain are adjusted. + * The ->next field of the last pbuf of the head chain is adjusted. + * The ->ref field of the first pbuf of the tail chain is adjusted. + * + */ +void +pbuf_chain(struct pbuf *h, struct pbuf *t) +{ + pbuf_cat(h, t); + /* t is now referenced by h */ + pbuf_ref(t); + LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t)); +} + +/* For packet queueing. Note that queued packets MUST be dequeued first + * using pbuf_dequeue() before calling other pbuf_() functions. */ +#if ARP_QUEUEING +/** + * Add a packet to the end of a queue. + * + * @param q pointer to first packet on the queue + * @param n packet to be queued + * + * Both packets MUST be given, and must be different. + */ +void +pbuf_queue(struct pbuf *p, struct pbuf *n) +{ +#if PBUF_DEBUG /* remember head of queue */ + struct pbuf *q = p; +#endif + /* programmer stupidity checks */ + LWIP_ASSERT("p == NULL in pbuf_queue: this indicates a programmer error\n", p != NULL); + LWIP_ASSERT("n == NULL in pbuf_queue: this indicates a programmer error\n", n != NULL); + LWIP_ASSERT("p == n in pbuf_queue: this indicates a programmer error\n", p != n); + if ((p == NULL) || (n == NULL) || (p == n)){ + LWIP_DEBUGF(PBUF_DEBUG | DBG_HALT | 3, ("pbuf_queue: programmer argument error\n")); + return; + } + + /* iterate through all packets on queue */ + while (p->next != NULL) { +/* be very picky about pbuf chain correctness */ +#if PBUF_DEBUG + /* iterate through all pbufs in packet */ + while (p->tot_len != p->len) { + /* make sure invariant condition holds */ + LWIP_ASSERT("p->len < p->tot_len", p->len < p->tot_len); + /* make sure each packet is complete */ + LWIP_ASSERT("p->next != NULL", p->next != NULL); + p = p->next; + /* { p->tot_len == p->len => p is last pbuf of a packet } */ + } + /* { p is last pbuf of a packet } */ + /* proceed to next packet on queue */ +#endif + /* proceed to next pbuf */ + if (p->next != NULL) p = p->next; + } + /* { p->tot_len == p->len and p->next == NULL } ==> + * { p is last pbuf of last packet on queue } */ + /* chain last pbuf of queue with n */ + p->next = n; + /* n is now referenced to by the (packet p in the) queue */ + pbuf_ref(n); +#if PBUF_DEBUG + LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, + ("pbuf_queue: newly queued packet %p sits after packet %p in queue %p\n", + (void *)n, (void *)p, (void *)q)); +#endif +} + +/** + * Remove a packet from the head of a queue. + * + * The caller MUST reference the remainder of the queue (as returned). The + * caller MUST NOT call pbuf_ref() as it implicitly takes over the reference + * from p. + * + * @param p pointer to first packet on the queue which will be dequeued. + * @return first packet on the remaining queue (NULL if no further packets). + * + */ +struct pbuf * +pbuf_dequeue(struct pbuf *p) +{ + struct pbuf *q; + LWIP_ASSERT("p != NULL", p != NULL); + + /* iterate through all pbufs in packet p */ + while (p->tot_len != p->len) { + /* make sure invariant condition holds */ + LWIP_ASSERT("p->len < p->tot_len", p->len < p->tot_len); + /* make sure each packet is complete */ + LWIP_ASSERT("p->next != NULL", p->next != NULL); + p = p->next; + } + /* { p->tot_len == p->len } => p is the last pbuf of the first packet */ + /* remember next packet on queue in q */ + q = p->next; + /* dequeue packet p from queue */ + p->next = NULL; + /* any next packet on queue? */ + if (q != NULL) { + /* although q is no longer referenced by p, it MUST be referenced by + * the caller, who is maintaining this packet queue. So, we do not call + * pbuf_free(q) here, resulting in an implicit pbuf_ref(q) for the caller. */ + LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, ("pbuf_dequeue: first remaining packet on queue is %p\n", (void *)q)); + } else { + LWIP_DEBUGF(PBUF_DEBUG | DBG_FRESH | 2, ("pbuf_dequeue: no further packets on queue\n")); + } + return q; +} +#endif + +/** + * + * Create PBUF_POOL (or PBUF_RAM) copies of PBUF_REF pbufs. + * + * Used to queue packets on behalf of the lwIP stack, such as + * ARP based queueing. + * + * Go through a pbuf chain and replace any PBUF_REF buffers + * with PBUF_POOL (or PBUF_RAM) pbufs, each taking a copy of + * the referenced data. + * + * @note You MUST explicitly use p = pbuf_take(p); + * The pbuf you give as argument, may have been replaced + * by a (differently located) copy through pbuf_take()! + * + * @note Any replaced pbufs will be freed through pbuf_free(). + * This may deallocate them if they become no longer referenced. + * + * @param p Head of pbuf chain to process + * + * @return Pointer to head of pbuf chain + */ +struct pbuf * +pbuf_take(struct pbuf *p) +{ + struct pbuf *q , *prev, *head; + LWIP_ASSERT("pbuf_take: p != NULL\n", p != NULL); + LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 3, ("pbuf_take(%p)\n", (void*)p)); + + prev = NULL; + head = p; + /* iterate through pbuf chain */ + do + { + /* pbuf is of type PBUF_REF? */ + if (p->flags == PBUF_FLAG_REF) { + LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE, ("pbuf_take: encountered PBUF_REF %p\n", (void *)p)); + /* allocate a pbuf (w/ payload) fully in RAM */ + /* PBUF_POOL buffers are faster if we can use them */ + if (p->len <= PBUF_POOL_BUFSIZE) { + q = pbuf_alloc(PBUF_RAW, p->len, PBUF_POOL); + if (q == NULL) { + LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_POOL\n")); + } + } else { + /* no replacement pbuf yet */ + q = NULL; + LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: PBUF_POOL too small to replace PBUF_REF\n")); + } + /* no (large enough) PBUF_POOL was available? retry with PBUF_RAM */ + if (q == NULL) { + q = pbuf_alloc(PBUF_RAW, p->len, PBUF_RAM); + if (q == NULL) { + LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 2, ("pbuf_take: Could not allocate PBUF_RAM\n")); + } + } + /* replacement pbuf could be allocated? */ + if (q != NULL) + { + /* copy p to q */ + /* copy successor */ + q->next = p->next; + /* remove linkage from original pbuf */ + p->next = NULL; + /* remove linkage to original pbuf */ + if (prev != NULL) { + /* prev->next == p at this point */ + LWIP_ASSERT("prev->next == p", prev->next == p); + /* break chain and insert new pbuf instead */ + prev->next = q; + /* prev == NULL, so we replaced the head pbuf of the chain */ + } else { + head = q; + } + /* copy pbuf payload */ + memcpy(q->payload, p->payload, p->len); + q->tot_len = p->tot_len; + q->len = p->len; + /* in case p was the first pbuf, it is no longer refered to by + * our caller, as the caller MUST do p = pbuf_take(p); + * in case p was not the first pbuf, it is no longer refered to + * by prev. we can safely free the pbuf here. + * (note that we have set p->next to NULL already so that + * we will not free the rest of the chain by accident.) + */ + pbuf_free(p); + /* do not copy ref, since someone else might be using the old buffer */ + LWIP_DEBUGF(PBUF_DEBUG, ("pbuf_take: replaced PBUF_REF %p with %p\n", (void *)p, (void *)q)); + p = q; + } else { + /* deallocate chain */ + pbuf_free(head); + LWIP_DEBUGF(PBUF_DEBUG | 2, ("pbuf_take: failed to allocate replacement pbuf for %p\n", (void *)p)); + return NULL; + } + /* p->flags != PBUF_FLAG_REF */ + } else { + LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_take: skipping pbuf not of type PBUF_REF\n")); + } + /* remember this pbuf */ + prev = p; + /* proceed to next pbuf in original chain */ + p = p->next; + } while (p); + LWIP_DEBUGF(PBUF_DEBUG | DBG_TRACE | 1, ("pbuf_take: end of chain reached.\n")); + + return head; +} + +/** + * Dechains the first pbuf from its succeeding pbufs in the chain. + * + * Makes p->tot_len field equal to p->len. + * @param p pbuf to dechain + * @return remainder of the pbuf chain, or NULL if it was de-allocated. + * @note May not be called on a packet queue. + */ +struct pbuf * +pbuf_dechain(struct pbuf *p) +{ + struct pbuf *q; + u8_t tail_gone = 1; + /* tail */ + q = p->next; + /* pbuf has successor in chain? */ + if (q != NULL) { + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len); + /* enforce invariant if assertion is disabled */ + q->tot_len = p->tot_len - p->len; + /* decouple pbuf from remainder */ + p->next = NULL; + /* total length of pbuf p is its own length only */ + p->tot_len = p->len; + /* q is no longer referenced by p, free it */ + LWIP_DEBUGF(PBUF_DEBUG | DBG_STATE, ("pbuf_dechain: unreferencing %p\n", (void *)q)); + tail_gone = pbuf_free(q); + if (tail_gone > 0) { + LWIP_DEBUGF(PBUF_DEBUG | DBG_STATE, + ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q)); + } + /* return remaining tail or NULL if deallocated */ + } + /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ + LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len); + return (tail_gone > 0? NULL: q); +} diff --git a/wii/libogc/lwip/core/raw.c b/wii/libogc/lwip/core/raw.c new file mode 100644 index 0000000000..30199804dc --- /dev/null +++ b/wii/libogc/lwip/core/raw.c @@ -0,0 +1,326 @@ +/** + * @file + * + * Implementation of raw protocol PCBs for low-level handling of + * different types of protocols besides (or overriding) those + * already available in lwIP. + * + */ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/inet.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/raw.h" + +#include "lwip/stats.h" + +#include "arch/perf.h" +#include "lwip/snmp.h" + +#if LWIP_RAW + +/** The list of RAW PCBs */ +static struct raw_pcb *raw_pcbs = NULL; + +void +raw_init(void) +{ + raw_pcbs = NULL; +} + +/** + * Determine if in incoming IP packet is covered by a RAW PCB + * and if so, pass it to a user-provided receive callback function. + * + * Given an incoming IP datagram (as a chain of pbufs) this function + * finds a corresponding RAW PCB and calls the corresponding receive + * callback function. + * + * @param pbuf pbuf to be demultiplexed to a RAW PCB. + * @param netif network interface on which the datagram was received. + * @Return - 1 if the packet has been eaten by a RAW PCB receive + * callback function. The caller MAY NOT not reference the + * packet any longer, and MAY NOT call pbuf_free(). + * @return - 0 if packet is not eaten (pbuf is still referenced by the + * caller). + * + */ +u8_t +raw_input(struct pbuf *p, struct netif *inp) +{ + struct raw_pcb *pcb; + struct ip_hdr *iphdr; + s16_t proto; + u8_t eaten = 0; + + iphdr = p->payload; + proto = IPH_PROTO(iphdr); + + pcb = raw_pcbs; + /* loop through all raw pcbs until the packet is eaten by one */ + /* this allows multiple pcbs to match against the packet by design */ + while ((eaten == 0) && (pcb != NULL)) { + if (pcb->protocol == proto) { + /* receive callback function available? */ + if (pcb->recv != NULL) { + /* the receive callback function did not eat the packet? */ + if (pcb->recv(pcb->recv_arg, pcb, p, &(iphdr->src)) != 0) + { + /* receive function ate the packet */ + p = NULL; + eaten = 1; + } + } + /* no receive callback function was set for this raw PCB */ + /* drop the packet */ + } + pcb = pcb->next; + } + return eaten; +} + +/** + * Bind a RAW PCB. + * + * @param pcb RAW PCB to be bound with a local address ipaddr. + * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to + * bind to all local interfaces. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_USE. The specified IP address is already bound to by + * another RAW PCB. + * + * @see raw_disconnect() + */ +err_t +raw_bind(struct raw_pcb *pcb, struct ip_addr *ipaddr) +{ + ip_addr_set(&pcb->local_ip, ipaddr); + return ERR_OK; +} + +/** + * Connect an RAW PCB. This function is required by upper layers + * of lwip. Using the raw api you could use raw_sendto() instead + * + * This will associate the RAW PCB with the remote address. + * + * @param pcb RAW PCB to be connected with remote address ipaddr and port. + * @param ipaddr remote IP address to connect with. + * + * @return lwIP error code + * + * @see raw_disconnect() and raw_sendto() + */ +err_t +raw_connect(struct raw_pcb *pcb, struct ip_addr *ipaddr) +{ + ip_addr_set(&pcb->remote_ip, ipaddr); + return ERR_OK; +} + + +/** + * Set the callback function for received packets that match the + * raw PCB's protocol and binding. + * + * The callback function MUST either + * - eat the packet by calling pbuf_free() and returning non-zero. The + * packet will not be passed to other raw PCBs or other protocol layers. + * - not free the packet, and return zero. The packet will be matched + * against further PCBs and/or forwarded to another protocol layers. + * + * @return non-zero if the packet was free()d, zero if the packet remains + * available for others. + */ +void +raw_recv(struct raw_pcb *pcb, + u8_t (* recv)(void *arg, struct raw_pcb *upcb, struct pbuf *p, + struct ip_addr *addr), + void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv = recv; + pcb->recv_arg = recv_arg; +} + +/** + * Send the raw IP packet to the given address. Note that actually you cannot + * modify the IP headers (this is inconsistent with the receive callback where + * you actually get the IP headers), you can only specify the IP payload here. + * It requires some more changes in lwIP. (there will be a raw_send() function + * then.) + * + * @param pcb the raw pcb which to send + * @param p the IP payload to send + * @param ipaddr the destination address of the IP packet + * + */ +err_t +raw_sendto(struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *ipaddr) +{ + err_t err; + struct netif *netif; + struct ip_addr *src_ip; + struct pbuf *q; /* q will be sent down the stack */ + + LWIP_DEBUGF(RAW_DEBUG | DBG_TRACE | 3, ("raw_sendto\n")); + + /* not enough space to add an IP header to first pbuf in given p chain? */ + if (pbuf_header(p, IP_HLEN)) { + /* allocate header in new pbuf */ + q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + LWIP_DEBUGF(RAW_DEBUG | DBG_TRACE | 2, ("raw_sendto: could not allocate header\n")); + return ERR_MEM; + } + /* chain header q in front of given pbuf p */ + pbuf_chain(q, p); + /* { first pbuf q points to header pbuf } */ + LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); + } else { + /* first pbuf q equals given pbuf */ + q = p; + pbuf_header(q, -IP_HLEN); + } + + if ((netif = ip_route(ipaddr)) == NULL) { + LWIP_DEBUGF(RAW_DEBUG | 1, ("raw_sendto: No route to 0x%"X32_F"\n", ipaddr->addr)); +#if RAW_STATS + /* ++lwip_stats.raw.rterr;*/ +#endif /* RAW_STATS */ + /* free any temporary header pbuf allocated by pbuf_header() */ + if (q != p) { + pbuf_free(q); + } + return ERR_RTE; + } + + if (ip_addr_isany(&pcb->local_ip)) { + /* use outgoing network interface IP address as source address */ + src_ip = &(netif->ip_addr); + } else { + /* use RAW PCB local IP address as source address */ + src_ip = &(pcb->local_ip); + } + + err = ip_output_if (q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif); + + /* did we chain a header earlier? */ + if (q != p) { + /* free the header */ + pbuf_free(q); + } + return err; +} + +/** + * Send the raw IP packet to the address given by raw_connect() + * + * @param pcb the raw pcb which to send + * @param p the IP payload to send + * @param ipaddr the destination address of the IP packet + * + */ +err_t +raw_send(struct raw_pcb *pcb, struct pbuf *p) +{ + return raw_sendto(pcb, p, &pcb->remote_ip); +} + +/** + * Remove an RAW PCB. + * + * @param pcb RAW PCB to be removed. The PCB is removed from the list of + * RAW PCB's and the data structure is freed from memory. + * + * @see raw_new() + */ +void +raw_remove(struct raw_pcb *pcb) +{ + struct raw_pcb *pcb2; + /* pcb to be removed is first in list? */ + if (raw_pcbs == pcb) { + /* make list start at 2nd pcb */ + raw_pcbs = raw_pcbs->next; + /* pcb not 1st in list */ + } else for(pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in raw_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + } + } + memp_free(MEMP_RAW_PCB, pcb); +} + +/** + * Create a RAW PCB. + * + * @return The RAW PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP) + * + * @see raw_remove() + */ +struct raw_pcb * +raw_new(u16_t proto) { + struct raw_pcb *pcb; + + LWIP_DEBUGF(RAW_DEBUG | DBG_TRACE | 3, ("raw_new\n")); + + pcb = memp_malloc(MEMP_RAW_PCB); + /* could allocate RAW PCB? */ + if (pcb != NULL) { + /* initialize PCB to all zeroes */ + memset(pcb, 0, sizeof(struct raw_pcb)); + pcb->protocol = proto; + pcb->ttl = RAW_TTL; + pcb->next = raw_pcbs; + raw_pcbs = pcb; + } + return pcb; +} + +#endif /* LWIP_RAW */ diff --git a/wii/libogc/lwip/core/stats.c b/wii/libogc/lwip/core/stats.c new file mode 100644 index 0000000000..c94623f7a8 --- /dev/null +++ b/wii/libogc/lwip/core/stats.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include + +#include "lwip/opt.h" + +#include "lwip/def.h" + +#include "lwip/stats.h" +#include "lwip/mem.h" + + +#if LWIP_STATS +struct stats_ lwip_stats; + +void +stats_init(void) +{ + memset(&lwip_stats, 0, sizeof(struct stats_)); +} +#if LWIP_STATS_DISPLAY +void +stats_display_proto(struct stats_proto *proto, char *name) +{ + LWIP_PLATFORM_DIAG(("\n%s\n\t", name)); + LWIP_PLATFORM_DIAG(("xmit: %"S16_F"\n\t", proto->xmit)); + LWIP_PLATFORM_DIAG(("rexmit: %"S16_F"\n\t", proto->rexmit)); + LWIP_PLATFORM_DIAG(("recv: %"S16_F"\n\t", proto->recv)); + LWIP_PLATFORM_DIAG(("fw: %"S16_F"\n\t", proto->fw)); + LWIP_PLATFORM_DIAG(("drop: %"S16_F"\n\t", proto->drop)); + LWIP_PLATFORM_DIAG(("chkerr: %"S16_F"\n\t", proto->chkerr)); + LWIP_PLATFORM_DIAG(("lenerr: %"S16_F"\n\t", proto->lenerr)); + LWIP_PLATFORM_DIAG(("memerr: %"S16_F"\n\t", proto->memerr)); + LWIP_PLATFORM_DIAG(("rterr: %"S16_F"\n\t", proto->rterr)); + LWIP_PLATFORM_DIAG(("proterr: %"S16_F"\n\t", proto->proterr)); + LWIP_PLATFORM_DIAG(("opterr: %"S16_F"\n\t", proto->opterr)); + LWIP_PLATFORM_DIAG(("err: %"S16_F"\n\t", proto->err)); + LWIP_PLATFORM_DIAG(("cachehit: %"S16_F"\n", proto->cachehit)); +} + +void +stats_display_pbuf(struct stats_pbuf *pbuf) +{ + LWIP_PLATFORM_DIAG(("\nPBUF\n\t")); + LWIP_PLATFORM_DIAG(("avail: %"S16_F"\n\t", pbuf->avail)); + LWIP_PLATFORM_DIAG(("used: %"S16_F"\n\t", pbuf->used)); + LWIP_PLATFORM_DIAG(("max: %"S16_F"\n\t", pbuf->max)); + LWIP_PLATFORM_DIAG(("err: %"S16_F"\n\t", pbuf->err)); + LWIP_PLATFORM_DIAG(("alloc_locked: %"S16_F"\n\t", pbuf->alloc_locked)); + LWIP_PLATFORM_DIAG(("refresh_locked: %"S16_F"\n", pbuf->refresh_locked)); +} + +void +stats_display_mem(struct stats_mem *mem, char *name) +{ + LWIP_PLATFORM_DIAG(("\n MEM %s\n\t", name)); + LWIP_PLATFORM_DIAG(("avail: %"S16_F"\n\t", mem->avail)); + LWIP_PLATFORM_DIAG(("used: %"S16_F"\n\t", mem->used)); + LWIP_PLATFORM_DIAG(("max: %"S16_F"\n\t", mem->max)); + LWIP_PLATFORM_DIAG(("err: %"S16_F"\n", mem->err)); + +} + +void +stats_display(void) +{ + s16_t i; + char * memp_names[] = {"PBUF", "RAW_PCB", "UDP_PCB", "TCP_PCB", "TCP_PCB_LISTEN", + "TCP_SEG", "NETBUF", "NETCONN", "API_MSG", "TCP_MSG", "TIMEOUT"}; + stats_display_proto(&lwip_stats.link, "LINK"); + stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG"); + stats_display_proto(&lwip_stats.ip, "IP"); + stats_display_proto(&lwip_stats.icmp, "ICMP"); + stats_display_proto(&lwip_stats.udp, "UDP"); + stats_display_proto(&lwip_stats.tcp, "TCP"); + stats_display_pbuf(&lwip_stats.pbuf); + stats_display_mem(&lwip_stats.mem, "HEAP"); + for (i = 0; i < MEMP_MAX; i++) { + stats_display_mem(&lwip_stats.memp[i], memp_names[i]); + } + +} +#endif /* LWIP_STATS_DISPLAY */ +#endif /* LWIP_STATS */ + diff --git a/wii/libogc/lwip/core/sys.c b/wii/libogc/lwip/core/sys.c new file mode 100644 index 0000000000..a7dbf34dab --- /dev/null +++ b/wii/libogc/lwip/core/sys.c @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/sys.h" +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/memp.h" + +#if (NO_SYS == 0) + +struct sswt_cb +{ + s16_t timeflag; + sys_sem_t *psem; +}; + + + +void +sys_mbox_fetch(sys_mbox_t mbox, void **msg) +{ + u32_t time; + struct sys_timeouts *timeouts; + struct sys_timeout *tmptimeout; + sys_timeout_handler h; + void *arg; + + + again: + timeouts = sys_arch_timeouts(); + + if (!timeouts || !timeouts->next) { + sys_arch_mbox_fetch(mbox, msg, 0); + } else { + if (timeouts->next->time > 0) { + time = sys_arch_mbox_fetch(mbox, msg, timeouts->next->time); + } else { + time = SYS_ARCH_TIMEOUT; + } + + if (time == SYS_ARCH_TIMEOUT) { + /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message + could be fetched. We should now call the timeout handler and + deallocate the memory allocated for the timeout. */ + tmptimeout = timeouts->next; + timeouts->next = tmptimeout->next; + h = tmptimeout->h; + arg = tmptimeout->arg; + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (h != NULL) { + LWIP_DEBUGF(SYS_DEBUG, ("smf calling h=%p(%p)\n", (void *)h, (void *)arg)); + h(arg); + } + + /* We try again to fetch a message from the mbox. */ + goto again; + } else { + /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout + occured. The time variable is set to the number of + milliseconds we waited for the message. */ + if (time <= timeouts->next->time) { + timeouts->next->time -= time; + } else { + timeouts->next->time = 0; + } + } + + } +} + +void +sys_sem_wait(sys_sem_t sem) +{ + u32_t time; + struct sys_timeouts *timeouts; + struct sys_timeout *tmptimeout; + sys_timeout_handler h; + void *arg; + + /* while (sys_arch_sem_wait(sem, 1000) == 0); + return;*/ + + again: + + timeouts = sys_arch_timeouts(); + + if (!timeouts || !timeouts->next) { + sys_arch_sem_wait(sem, 0); + } else { + if (timeouts->next->time > 0) { + time = sys_arch_sem_wait(sem, timeouts->next->time); + } else { + time = SYS_ARCH_TIMEOUT; + } + + if (time == SYS_ARCH_TIMEOUT) { + /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message + could be fetched. We should now call the timeout handler and + deallocate the memory allocated for the timeout. */ + tmptimeout = timeouts->next; + timeouts->next = tmptimeout->next; + h = tmptimeout->h; + arg = tmptimeout->arg; + memp_free(MEMP_SYS_TIMEOUT, tmptimeout); + if (h != NULL) { + LWIP_DEBUGF(SYS_DEBUG, ("ssw h=%p(%p)\n", (void *)h, (void *)arg)); + h(arg); + } + + + /* We try again to fetch a message from the mbox. */ + goto again; + } else { + /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout + occured. The time variable is set to the number of + milliseconds we waited for the message. */ + if (time <= timeouts->next->time) { + timeouts->next->time -= time; + } else { + timeouts->next->time = 0; + } + } + + } +} + +void +sys_timeout(u32_t msecs, sys_timeout_handler h, void *arg) +{ + struct sys_timeouts *timeouts; + struct sys_timeout *timeout, *t; + + timeout = memp_malloc(MEMP_SYS_TIMEOUT); + if (timeout == NULL) { + return; + } + timeout->next = NULL; + timeout->h = h; + timeout->arg = arg; + timeout->time = msecs; + + timeouts = sys_arch_timeouts(); + + LWIP_DEBUGF(SYS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" h=%p arg=%p\n", + (void *)timeout, msecs, (void *)h, (void *)arg)); + + LWIP_ASSERT("sys_timeout: timeouts != NULL", timeouts != NULL); + + if (timeouts->next == NULL) { + timeouts->next = timeout; + return; + } + + if (timeouts->next->time > msecs) { + timeouts->next->time -= msecs; + timeout->next = timeouts->next; + timeouts->next = timeout; + } else { + for(t = timeouts->next; t != NULL; t = t->next) { + timeout->time -= t->time; + if (t->next == NULL || t->next->time > timeout->time) { + if (t->next != NULL) { + t->next->time -= timeout->time; + } + timeout->next = t->next; + t->next = timeout; + break; + } + } + } + +} + +/* Go through timeout list (for this task only) and remove the first matching entry, + even though the timeout has not triggered yet. +*/ + +void +sys_untimeout(sys_timeout_handler h, void *arg) +{ + struct sys_timeouts *timeouts; + struct sys_timeout *prev_t, *t; + + timeouts = sys_arch_timeouts(); + + if (timeouts->next == NULL) + return; + + for (t = timeouts->next, prev_t = NULL; t != NULL; prev_t = t, t = t->next) + { + if ((t->h == h) && (t->arg == arg)) + { + /* We have a match */ + /* Unlink from previous in list */ + if (prev_t == NULL) + timeouts->next = t->next; + else + prev_t->next = t->next; + /* If not the last one, add time of this one back to next */ + if (t->next != NULL) + t->next->time += t->time; + memp_free(MEMP_SYS_TIMEOUT, t); + return; + } + } + return; +} + + + + + +static void +sswt_handler(void *arg) +{ + struct sswt_cb *sswt_cb = (struct sswt_cb *) arg; + + /* Timeout. Set flag to TRUE and signal semaphore */ + sswt_cb->timeflag = 1; + sys_sem_signal(*(sswt_cb->psem)); +} + +/* Wait for a semaphore with timeout (specified in ms) */ +/* timeout = 0: wait forever */ +/* Returns 0 on timeout. 1 otherwise */ + +int +sys_sem_wait_timeout(sys_sem_t sem, u32_t timeout) +{ + struct sswt_cb sswt_cb; + + sswt_cb.psem = &sem; + sswt_cb.timeflag = 0; + + /* If timeout is zero, then just wait forever */ + if (timeout > 0) + /* Create a timer and pass it the address of our flag */ + sys_timeout(timeout, sswt_handler, &sswt_cb); + sys_sem_wait(sem); + /* Was it a timeout? */ + if (sswt_cb.timeflag) + { + /* timeout */ + return 0; + } else { + /* Not a timeout. Remove timeout entry */ + sys_untimeout(sswt_handler, &sswt_cb); + return 1; + } + +} + + +void +sys_msleep(u32_t ms) +{ + sys_sem_t delaysem = sys_sem_new(0); + + sys_sem_wait_timeout(delaysem, ms); + + sys_sem_free(delaysem); +} + + +#endif /* NO_SYS */ diff --git a/wii/libogc/lwip/core/tcp.c b/wii/libogc/lwip/core/tcp.c new file mode 100644 index 0000000000..41a9edb4e5 --- /dev/null +++ b/wii/libogc/lwip/core/tcp.c @@ -0,0 +1,1171 @@ +/** + * @file + * + * Transmission Control Protocol for IP + * + * This file contains common functions for the TCP implementation, such as functinos + * for manipulating the data structures and the TCP timer functions. TCP functions + * related to input and output is found in tcp_in.c and tcp_out.c respectively. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/memp.h" + +#include "lwip/tcp.h" +#if LWIP_TCP + +/* Incremented every coarse grained timer shot + (typically every 500 ms, determined by TCP_COARSE_TIMEOUT). */ +u32_t tcp_ticks; +const u8_t tcp_backoff[13] = + { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7}; + +/* The TCP PCB lists. */ + +/** List of all TCP PCBs in LISTEN state */ +union tcp_listen_pcbs_t tcp_listen_pcbs; +/** List of all TCP PCBs that are in a state in which + * they accept or send data. */ +struct tcp_pcb *tcp_active_pcbs; +/** List of all TCP PCBs in TIME-WAIT state */ +struct tcp_pcb *tcp_tw_pcbs; + +struct tcp_pcb *tcp_tmp_pcb; + +static u8_t tcp_timer; +static u16_t tcp_new_port(void); + +/** + * Initializes the TCP layer. + */ +void +tcp_init(void) +{ + /* Clear globals. */ + tcp_listen_pcbs.listen_pcbs = NULL; + tcp_active_pcbs = NULL; + tcp_tw_pcbs = NULL; + tcp_tmp_pcb = NULL; + + /* initialize timer */ + tcp_ticks = 0; + tcp_timer = 0; + +} + +/** + * Called periodically to dispatch TCP timers. + * + */ +void +tcp_tmr(void) +{ + /* Call tcp_fasttmr() every 250 ms */ + tcp_fasttmr(); + + if (++tcp_timer & 1) { + /* Call tcp_tmr() every 500 ms, i.e., every other timer + tcp_tmr() is called. */ + tcp_slowtmr(); + } +} + +/** + * Closes the connection held by the PCB. + * + */ +err_t +tcp_close(struct tcp_pcb *pcb) +{ + err_t err; + +#if TCP_DEBUG + LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in state ")); + tcp_debug_print_state(pcb->state); + LWIP_DEBUGF(TCP_DEBUG, ("\n")); +#endif /* TCP_DEBUG */ + switch (pcb->state) { + case CLOSED: + /* Closing a pcb in the CLOSED state might seem erroneous, + * however, it is in this state once allocated and as yet unused + * and the user needs some way to free it should the need arise. + * Calling tcp_close() with a pcb that has already been closed, (i.e. twice) + * or for a pcb that has been used and then entered the CLOSED state + * is erroneous, but this should never happen as the pcb has in those cases + * been freed, and so any remaining handles are bogus. */ + err = ERR_OK; + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + break; + case LISTEN: + err = ERR_OK; + tcp_pcb_remove((struct tcp_pcb **)&tcp_listen_pcbs.pcbs, pcb); + memp_free(MEMP_TCP_PCB_LISTEN, pcb); + pcb = NULL; + break; + case SYN_SENT: + err = ERR_OK; + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + pcb = NULL; + break; + case SYN_RCVD: + case ESTABLISHED: + err = tcp_send_ctrl(pcb, TCP_FIN); + if (err == ERR_OK) { + pcb->state = FIN_WAIT_1; + } + break; + case CLOSE_WAIT: + err = tcp_send_ctrl(pcb, TCP_FIN); + if (err == ERR_OK) { + pcb->state = LAST_ACK; + } + break; + default: + /* Has already been closed, do nothing. */ + err = ERR_OK; + pcb = NULL; + break; + } + + if (pcb != NULL && err == ERR_OK) { + err = tcp_output(pcb); + } + return err; +} + +/** + * Aborts a connection by sending a RST to the remote host and deletes + * the local protocol control block. This is done when a connection is + * killed because of shortage of memory. + * + */ +void +tcp_abort(struct tcp_pcb *pcb) +{ + u32_t seqno, ackno; + u16_t remote_port, local_port; + struct ip_addr remote_ip, local_ip; +#if LWIP_CALLBACK_API + void (* errf)(void *arg, err_t err); +#endif /* LWIP_CALLBACK_API */ + void *errf_arg; + + + /* Figure out on which TCP PCB list we are, and remove us. If we + are in an active state, call the receive function associated with + the PCB with a NULL argument, and send an RST to the remote end. */ + if (pcb->state == TIME_WAIT) { + tcp_pcb_remove(&tcp_tw_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + seqno = pcb->snd_nxt; + ackno = pcb->rcv_nxt; + ip_addr_set(&local_ip, &(pcb->local_ip)); + ip_addr_set(&remote_ip, &(pcb->remote_ip)); + local_port = pcb->local_port; + remote_port = pcb->remote_port; +#if LWIP_CALLBACK_API + errf = pcb->errf; +#endif /* LWIP_CALLBACK_API */ + errf_arg = pcb->callback_arg; + tcp_pcb_remove(&tcp_active_pcbs, pcb); + if (pcb->unacked != NULL) { + tcp_segs_free(pcb->unacked); + } + if (pcb->unsent != NULL) { + tcp_segs_free(pcb->unsent); + } +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL) { + tcp_segs_free(pcb->ooseq); + } +#endif /* TCP_QUEUE_OOSEQ */ + memp_free(MEMP_TCP_PCB, pcb); + TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT); + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abort: sending RST\n")); + tcp_rst(seqno, ackno, &local_ip, &remote_ip, local_port, remote_port); + } +} + +/** + * Binds the connection to a local portnumber and IP address. If the + * IP address is not given (i.e., ipaddr == NULL), the IP address of + * the outgoing network interface is used instead. + * + */ + +err_t +tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) +{ + struct tcp_pcb *cpcb; + + if (port == 0) { + port = tcp_new_port(); + } + /* Check if the address already is in use. */ + for(cpcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; + cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { + if (ip_addr_isany(&(cpcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; + } + } + } + for(cpcb = tcp_active_pcbs; + cpcb != NULL; cpcb = cpcb->next) { + if (cpcb->local_port == port) { + if (ip_addr_isany(&(cpcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { + return ERR_USE; + } + } + } + + if (!ip_addr_isany(ipaddr)) { + pcb->local_ip = *ipaddr; + } + pcb->local_port = port; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port)); + return ERR_OK; +} +#if LWIP_CALLBACK_API +static err_t +tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err) +{ + (void)arg; + (void)pcb; + (void)err; + + return ERR_ABRT; +} +#endif /* LWIP_CALLBACK_API */ + +/** + * Set the state of the connection to be LISTEN, which means that it + * is able to accept incoming connections. The protocol control block + * is reallocated in order to consume less memory. Setting the + * connection to LISTEN is an irreversible process. + * + */ +struct tcp_pcb * +tcp_listen(struct tcp_pcb *pcb) +{ + struct tcp_pcb_listen *lpcb; + + /* already listening? */ + if (pcb->state == LISTEN) { + return pcb; + } + lpcb = memp_malloc(MEMP_TCP_PCB_LISTEN); + if (lpcb == NULL) { + return NULL; + } + lpcb->callback_arg = pcb->callback_arg; + lpcb->local_port = pcb->local_port; + lpcb->state = LISTEN; + lpcb->so_options = pcb->so_options; + lpcb->so_options |= SOF_ACCEPTCONN; + lpcb->ttl = pcb->ttl; + lpcb->tos = pcb->tos; + ip_addr_set(&lpcb->local_ip, &pcb->local_ip); + memp_free(MEMP_TCP_PCB, pcb); +#if LWIP_CALLBACK_API + lpcb->accept = tcp_accept_null; +#endif /* LWIP_CALLBACK_API */ + TCP_REG(&tcp_listen_pcbs.listen_pcbs, lpcb); + return (struct tcp_pcb *)lpcb; +} + +/** + * This function should be called by the application when it has + * processed the data. The purpose is to advertise a larger window + * when the data has been processed. + * + */ +void +tcp_recved(struct tcp_pcb *pcb, u16_t len) +{ + if ((u32_t)pcb->rcv_wnd + len > TCP_WND) { + pcb->rcv_wnd = TCP_WND; + } else { + pcb->rcv_wnd += len; + } + if (!(pcb->flags & TF_ACK_DELAY) && + !(pcb->flags & TF_ACK_NOW)) { + /* + * We send an ACK here (if one is not already pending, hence + * the above tests) as tcp_recved() implies that the application + * has processed some data, and so we can open the receiver's + * window to allow more to be transmitted. This could result in + * two ACKs being sent for each received packet in some limited cases + * (where the application is only receiving data, and is slow to + * process it) but it is necessary to guarantee that the sender can + * continue to transmit. + */ + tcp_ack(pcb); + } + else if (pcb->flags & TF_ACK_DELAY && pcb->rcv_wnd >= TCP_WND/2) { + /* If we can send a window update such that there is a full + * segment available in the window, do so now. This is sort of + * nagle-like in its goals, and tries to hit a compromise between + * sending acks each time the window is updated, and only sending + * window updates when a timer expires. The "threshold" used + * above (currently TCP_WND/2) can be tuned to be more or less + * aggressive */ + tcp_ack_now(pcb); + } + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n", + len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd)); +} + +/** + * A nastly hack featuring 'goto' statements that allocates a + * new TCP local port. + */ +static u16_t +tcp_new_port(void) +{ + struct tcp_pcb *pcb; +#ifndef TCP_LOCAL_PORT_RANGE_START +#define TCP_LOCAL_PORT_RANGE_START 4096 +#define TCP_LOCAL_PORT_RANGE_END 0x7fff +#endif + static u16_t port = TCP_LOCAL_PORT_RANGE_START; + + again: + if (++port > TCP_LOCAL_PORT_RANGE_END) { + port = TCP_LOCAL_PORT_RANGE_START; + } + + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == port) { + goto again; + } + } + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == port) { + goto again; + } + } + for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->local_port == port) { + goto again; + } + } + return port; +} + +/** + * Connects to another host. The function given as the "connected" + * argument will be called when the connection has been established. + * + */ +err_t +tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port, + err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err)) +{ + u32_t optdata; + err_t ret; + u32_t iss; + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port)); + if (ipaddr != NULL) { + pcb->remote_ip = *ipaddr; + } else { + return ERR_VAL; + } + pcb->remote_port = port; + if (pcb->local_port == 0) { + pcb->local_port = tcp_new_port(); + } + iss = tcp_next_iss(); + pcb->rcv_nxt = 0; + pcb->snd_nxt = iss; + pcb->lastack = iss - 1; + pcb->snd_lbb = iss - 1; + pcb->rcv_wnd = TCP_WND; + pcb->snd_wnd = TCP_WND; + pcb->mss = TCP_MSS; + pcb->cwnd = 1; + pcb->ssthresh = pcb->mss * 10; + pcb->state = SYN_SENT; +#if LWIP_CALLBACK_API + pcb->connected = connected; +#endif /* LWIP_CALLBACK_API */ + TCP_REG(&tcp_active_pcbs, pcb); + + /* Build an MSS option */ + optdata = htonl(((u32_t)2 << 24) | + ((u32_t)4 << 16) | + (((u32_t)pcb->mss / 256) << 8) | + (pcb->mss & 255)); + + ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, (u8_t *)&optdata, 4); + if (ret == ERR_OK) { + tcp_output(pcb); + } + return ret; +} + +/** + * Called every 500 ms and implements the retransmission timer and the timer that + * removes PCBs that have been in TIME-WAIT for enough time. It also increments + * various timers such as the inactivity timer in each PCB. + */ +void +tcp_slowtmr(void) +{ + struct tcp_pcb *pcb, *pcb2, *prev; + u32_t eff_wnd; + u8_t pcb_remove; /* flag if a PCB should be removed */ + err_t err; + + err = ERR_OK; + + ++tcp_ticks; + + /* Steps through all of the active PCBs. */ + prev = NULL; + pcb = tcp_active_pcbs; + if (pcb == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n")); + } + while (pcb != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n")); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN); + LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT); + + pcb_remove = 0; + + if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n")); + } + else if (pcb->nrtx == TCP_MAXRTX) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n")); + } else { + ++pcb->rtime; + if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) { + + /* Time for a retransmission. */ + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"U16_F" pcb->rto %"U16_F"\n", + pcb->rtime, pcb->rto)); + + /* Double retransmission time-out unless we are trying to + * connect to somebody (i.e., we are in SYN_SENT). */ + if (pcb->state != SYN_SENT) { + pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx]; + } + /* Reduce congestion window and ssthresh. */ + eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd); + pcb->ssthresh = eff_wnd >> 1; + if (pcb->ssthresh < pcb->mss) { + pcb->ssthresh = pcb->mss * 2; + } + pcb->cwnd = pcb->mss; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F" ssthresh %"U16_F"\n", + pcb->cwnd, pcb->ssthresh)); + + /* The following needs to be called AFTER cwnd is set to one mss - STJ */ + tcp_rexmit_rto(pcb); + } + } + /* Check if this PCB has stayed too long in FIN-WAIT-2 */ + if (pcb->state == FIN_WAIT_2) { + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n")); + } + } + + /* Check if KEEPALIVE should be sent */ + if((pcb->so_options & SOF_KEEPALIVE) && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) { + if((u32_t)(tcp_ticks - pcb->tmr) > (pcb->keepalive + TCP_MAXIDLE) / TCP_SLOW_INTERVAL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to %"U16_F".%"U16_F".%"U16_F".%"U16_F".\n", + ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip))); + + tcp_abort(pcb); + } + else if((u32_t)(tcp_ticks - pcb->tmr) > (pcb->keepalive + pcb->keep_cnt * TCP_KEEPINTVL) / TCP_SLOW_INTERVAL) { + tcp_keepalive(pcb); + pcb->keep_cnt++; + } + } + + /* If this PCB has queued out of sequence data, but has been + inactive for too long, will drop the data (it will eventually + be retransmitted). */ +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL && + (u32_t)tcp_ticks - pcb->tmr >= + pcb->rto * TCP_OOSEQ_TIMEOUT) { + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n")); + } +#endif /* TCP_QUEUE_OOSEQ */ + + /* Check if this PCB has stayed too long in SYN-RCVD */ + if (pcb->state == SYN_RCVD) { + if ((u32_t)(tcp_ticks - pcb->tmr) > + TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n")); + } + } + + /* Check if this PCB has stayed too long in LAST-ACK */ + if (pcb->state == LAST_ACK) { + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n")); + } + } + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_active_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb); + tcp_active_pcbs = pcb->next; + } + + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_ABRT); + + pcb2 = pcb->next; + memp_free(MEMP_TCP_PCB, pcb); + pcb = pcb2; + } else { + + /* We check if we should poll the connection. */ + ++pcb->polltmr; + if (pcb->polltmr >= pcb->pollinterval) { + pcb->polltmr = 0; + LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n")); + TCP_EVENT_POLL(pcb, err); + if (err == ERR_OK) { + tcp_output(pcb); + } + } + + prev = pcb; + pcb = pcb->next; + } + } + + + /* Steps through all of the TIME-WAIT PCBs. */ + prev = NULL; + pcb = tcp_tw_pcbs; + while (pcb != NULL) { + LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + pcb_remove = 0; + + /* Check if this PCB has stayed long enough in TIME-WAIT */ + if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) { + ++pcb_remove; + } + + + + /* If the PCB should be removed, do it. */ + if (pcb_remove) { + tcp_pcb_purge(pcb); + /* Remove PCB from tcp_tw_pcbs list. */ + if (prev != NULL) { + LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs); + prev->next = pcb->next; + } else { + /* This PCB was the first. */ + LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb); + tcp_tw_pcbs = pcb->next; + } + pcb2 = pcb->next; + memp_free(MEMP_TCP_PCB, pcb); + pcb = pcb2; + } else { + prev = pcb; + pcb = pcb->next; + } + } +} + +/** + * Is called every TCP_FAST_INTERVAL (250 ms) and sends delayed ACKs. + */ +void +tcp_fasttmr(void) +{ + struct tcp_pcb *pcb; + + /* send delayed ACKs */ + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->flags & TF_ACK_DELAY) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n")); + tcp_ack_now(pcb); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + } +} + +/** + * Deallocates a list of TCP segments (tcp_seg structures). + * + */ +u8_t +tcp_segs_free(struct tcp_seg *seg) +{ + u8_t count = 0; + struct tcp_seg *next; + while (seg != NULL) { + next = seg->next; + count += tcp_seg_free(seg); + seg = next; + } + return count; +} + +/** + * Frees a TCP segment. + * + */ +u8_t +tcp_seg_free(struct tcp_seg *seg) +{ + u8_t count = 0; + + if (seg != NULL) { + if (seg->p != NULL) { + count = pbuf_free(seg->p); +#if TCP_DEBUG + seg->p = NULL; +#endif /* TCP_DEBUG */ + } + memp_free(MEMP_TCP_SEG, seg); + } + return count; +} + +/** + * Sets the priority of a connection. + * + */ +void +tcp_setprio(struct tcp_pcb *pcb, u8_t prio) +{ + pcb->prio = prio; +} +#if TCP_QUEUE_OOSEQ + +/** + * Returns a copy of the given TCP segment. + * + */ +struct tcp_seg * +tcp_seg_copy(struct tcp_seg *seg) +{ + struct tcp_seg *cseg; + + cseg = memp_malloc(MEMP_TCP_SEG); + if (cseg == NULL) { + return NULL; + } + memcpy((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg)); + pbuf_ref(cseg->p); + return cseg; +} +#endif + +#if LWIP_CALLBACK_API +static err_t +tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) +{ + arg = arg; + if (p != NULL) { + pbuf_free(p); + } else if (err == ERR_OK) { + return tcp_close(pcb); + } + return ERR_OK; +} +#endif /* LWIP_CALLBACK_API */ + +static void +tcp_kill_prio(u8_t prio) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + u8_t mprio; + + + mprio = TCP_PRIO_MAX; + + /* We kill the oldest active connection that has lower priority than + prio. */ + inactivity = 0; + inactive = NULL; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if (pcb->prio <= prio && + pcb->prio <= mprio && + (u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + mprio = pcb->prio; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + + +static void +tcp_kill_timewait(void) +{ + struct tcp_pcb *pcb, *inactive; + u32_t inactivity; + + inactivity = 0; + inactive = NULL; + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) { + inactivity = tcp_ticks - pcb->tmr; + inactive = pcb; + } + } + if (inactive != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n", + (void *)inactive, inactivity)); + tcp_abort(inactive); + } +} + + + +struct tcp_pcb * +tcp_alloc(u8_t prio) +{ + struct tcp_pcb *pcb; + u32_t iss; + + pcb = memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + /* Try killing oldest connection in TIME-WAIT. */ + LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n")); + tcp_kill_timewait(); + pcb = memp_malloc(MEMP_TCP_PCB); + if (pcb == NULL) { + tcp_kill_prio(prio); + pcb = memp_malloc(MEMP_TCP_PCB); + } + } + if (pcb != NULL) { + memset(pcb, 0, sizeof(struct tcp_pcb)); + pcb->prio = TCP_PRIO_NORMAL; + pcb->snd_buf = TCP_SND_BUF; + pcb->snd_queuelen = 0; + pcb->rcv_wnd = TCP_WND; + pcb->tos = 0; + pcb->ttl = TCP_TTL; + pcb->mss = TCP_MSS; + pcb->rto = 3000 / TCP_SLOW_INTERVAL; + pcb->sa = 0; + pcb->sv = 3000 / TCP_SLOW_INTERVAL; + pcb->rtime = 0; + pcb->cwnd = 1; + iss = tcp_next_iss(); + pcb->snd_wl2 = iss; + pcb->snd_nxt = iss; + pcb->snd_max = iss; + pcb->lastack = iss; + pcb->snd_lbb = iss; + pcb->tmr = tcp_ticks; + + pcb->polltmr = 0; + +#if LWIP_CALLBACK_API + pcb->recv = tcp_recv_null; +#endif /* LWIP_CALLBACK_API */ + + /* Init KEEPALIVE timer */ + pcb->keepalive = TCP_KEEPDEFAULT; + pcb->keep_cnt = 0; + } + return pcb; +} + +/** + * Creates a new TCP protocol control block but doesn't place it on + * any of the TCP PCB lists. + * + * @internal: Maybe there should be a idle TCP PCB list where these + * PCBs are put on. We can then implement port reservation using + * tcp_bind(). Currently, we lack this (BSD socket type of) feature. + */ + +struct tcp_pcb * +tcp_new(void) +{ + return tcp_alloc(TCP_PRIO_NORMAL); +} + +/* + * tcp_arg(): + * + * Used to specify the argument that should be passed callback + * functions. + * + */ + +void +tcp_arg(struct tcp_pcb *pcb, void *arg) +{ + pcb->callback_arg = arg; +} +#if LWIP_CALLBACK_API + +/** + * Used to specify the function that should be called when a TCP + * connection receives data. + * + */ +void +tcp_recv(struct tcp_pcb *pcb, + err_t (* recv)(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)) +{ + pcb->recv = recv; +} + +/** + * Used to specify the function that should be called when TCP data + * has been successfully delivered to the remote host. + * + */ + +void +tcp_sent(struct tcp_pcb *pcb, + err_t (* sent)(void *arg, struct tcp_pcb *tpcb, u16_t len)) +{ + pcb->sent = sent; +} + +/** + * Used to specify the function that should be called when a fatal error + * has occured on the connection. + * + */ +void +tcp_err(struct tcp_pcb *pcb, + void (* errf)(void *arg, err_t err)) +{ + pcb->errf = errf; +} + +/** + * Used for specifying the function that should be called when a + * LISTENing connection has been connected to another host. + * + */ +void +tcp_accept(struct tcp_pcb *pcb, + err_t (* accept)(void *arg, struct tcp_pcb *newpcb, err_t err)) +{ + ((struct tcp_pcb_listen *)pcb)->accept = accept; +} +#endif /* LWIP_CALLBACK_API */ + + +/** + * Used to specify the function that should be called periodically + * from TCP. The interval is specified in terms of the TCP coarse + * timer interval, which is called twice a second. + * + */ +void +tcp_poll(struct tcp_pcb *pcb, + err_t (* poll)(void *arg, struct tcp_pcb *tpcb), u8_t interval) +{ +#if LWIP_CALLBACK_API + pcb->poll = poll; +#endif /* LWIP_CALLBACK_API */ + pcb->pollinterval = interval; +} + +/** + * Purges a TCP PCB. Removes any buffered data and frees the buffer memory. + * + */ +void +tcp_pcb_purge(struct tcp_pcb *pcb) +{ + if (pcb->state != CLOSED && + pcb->state != TIME_WAIT && + pcb->state != LISTEN) { + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n")); + + if (pcb->unsent != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n")); + } + if (pcb->unacked != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n")); + } +#if TCP_QUEUE_OOSEQ /* LW */ + if (pcb->ooseq != NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n")); + } + + tcp_segs_free(pcb->ooseq); + pcb->ooseq = NULL; +#endif /* TCP_QUEUE_OOSEQ */ + tcp_segs_free(pcb->unsent); + tcp_segs_free(pcb->unacked); + pcb->unacked = pcb->unsent = NULL; + } +} + +/** + * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first. + * + */ +void +tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb) +{ + TCP_RMV(pcblist, pcb); + + tcp_pcb_purge(pcb); + + /* if there is an outstanding delayed ACKs, send it */ + if (pcb->state != TIME_WAIT && + pcb->state != LISTEN && + pcb->flags & TF_ACK_DELAY) { + pcb->flags |= TF_ACK_NOW; + tcp_output(pcb); + } + pcb->state = CLOSED; + + LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane()); +} + +/** + * Calculates a new initial sequence number for new connections. + * + */ +u32_t +tcp_next_iss(void) +{ + static u32_t iss = 6510; + + iss += tcp_ticks; /* XXX */ + return iss; +} + +#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG +void +tcp_debug_print(struct tcp_hdr *tcphdr) +{ + LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + ntohs(tcphdr->src), ntohs(tcphdr->dest))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (seq no)\n", + ntohl(tcphdr->seqno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (ack no)\n", + ntohl(tcphdr->ackno))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" | |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"| %5"U16_F" | (hdrlen, flags (", + TCPH_HDRLEN(tcphdr), + TCPH_FLAGS(tcphdr) >> 5 & 1, + TCPH_FLAGS(tcphdr) >> 4 & 1, + TCPH_FLAGS(tcphdr) >> 3 & 1, + TCPH_FLAGS(tcphdr) >> 2 & 1, + TCPH_FLAGS(tcphdr) >> 1 & 1, + TCPH_FLAGS(tcphdr) & 1, + ntohs(tcphdr->wnd))); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_DEBUG, ("), win)\n")); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(TCP_DEBUG, ("| 0x%04"X16_F" | %5"U16_F" | (chksum, urgp)\n", + ntohs(tcphdr->chksum), ntohs(tcphdr->urgp))); + LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n")); +} + +void +tcp_debug_print_state(enum tcp_state s) +{ + LWIP_DEBUGF(TCP_DEBUG, ("State: ")); + switch (s) { + case CLOSED: + LWIP_DEBUGF(TCP_DEBUG, ("CLOSED\n")); + break; + case LISTEN: + LWIP_DEBUGF(TCP_DEBUG, ("LISTEN\n")); + break; + case SYN_SENT: + LWIP_DEBUGF(TCP_DEBUG, ("SYN_SENT\n")); + break; + case SYN_RCVD: + LWIP_DEBUGF(TCP_DEBUG, ("SYN_RCVD\n")); + break; + case ESTABLISHED: + LWIP_DEBUGF(TCP_DEBUG, ("ESTABLISHED\n")); + break; + case FIN_WAIT_1: + LWIP_DEBUGF(TCP_DEBUG, ("FIN_WAIT_1\n")); + break; + case FIN_WAIT_2: + LWIP_DEBUGF(TCP_DEBUG, ("FIN_WAIT_2\n")); + break; + case CLOSE_WAIT: + LWIP_DEBUGF(TCP_DEBUG, ("CLOSE_WAIT\n")); + break; + case CLOSING: + LWIP_DEBUGF(TCP_DEBUG, ("CLOSING\n")); + break; + case LAST_ACK: + LWIP_DEBUGF(TCP_DEBUG, ("LAST_ACK\n")); + break; + case TIME_WAIT: + LWIP_DEBUGF(TCP_DEBUG, ("TIME_WAIT\n")); + break; + } +} + +void +tcp_debug_print_flags(u8_t flags) +{ + if (flags & TCP_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("FIN ")); + } + if (flags & TCP_SYN) { + LWIP_DEBUGF(TCP_DEBUG, ("SYN ")); + } + if (flags & TCP_RST) { + LWIP_DEBUGF(TCP_DEBUG, ("RST ")); + } + if (flags & TCP_PSH) { + LWIP_DEBUGF(TCP_DEBUG, ("PSH ")); + } + if (flags & TCP_ACK) { + LWIP_DEBUGF(TCP_DEBUG, ("ACK ")); + } + if (flags & TCP_URG) { + LWIP_DEBUGF(TCP_DEBUG, ("URG ")); + } + if (flags & TCP_ECE) { + LWIP_DEBUGF(TCP_DEBUG, ("ECE ")); + } + if (flags & TCP_CWR) { + LWIP_DEBUGF(TCP_DEBUG, ("CWR ")); + } +} + +void +tcp_debug_print_pcbs(void) +{ + struct tcp_pcb *pcb; + LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n")); + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n")); + for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } + LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n")); + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ", + pcb->local_port, pcb->remote_port, + pcb->snd_nxt, pcb->rcv_nxt)); + tcp_debug_print_state(pcb->state); + } +} + +s16_t +tcp_pcbs_sane(void) +{ + struct tcp_pcb *pcb; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN); + LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + } + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + } + return 1; +} +#endif /* TCP_DEBUG */ +#endif /* LWIP_TCP */ + + + + + + + + + + diff --git a/wii/libogc/lwip/core/tcp_in.c b/wii/libogc/lwip/core/tcp_in.c new file mode 100644 index 0000000000..212f9c4db6 --- /dev/null +++ b/wii/libogc/lwip/core/tcp_in.c @@ -0,0 +1,1199 @@ +/** + * @file + * + * Transmission Control Protocol, incoming traffic + * + * The input processing functions of TCP. + * + * These functions are generally called in the order (ip_input() ->) tcp_input() -> + * tcp_process() -> tcp_receive() (-> application). + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include "lwip/def.h" +#include "lwip/opt.h" + +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/mem.h" +#include "lwip/memp.h" + +#include "lwip/inet.h" +#include "lwip/tcp.h" + +#include "lwip/stats.h" + +#include "arch/perf.h" +#if LWIP_TCP +/* These variables are global to all functions involved in the input + processing of TCP segments. They are set by the tcp_input() + function. */ +static struct tcp_seg inseg; +static struct tcp_hdr *tcphdr; +static struct ip_hdr *iphdr; +static u32_t seqno, ackno; +static u8_t flags; +static u16_t tcplen; + +static u8_t recv_flags; +static struct pbuf *recv_data; + +struct tcp_pcb *tcp_input_pcb; + +/* Forward declarations. */ +static err_t tcp_process(struct tcp_pcb *pcb); +static void tcp_receive(struct tcp_pcb *pcb); +static void tcp_parseopt(struct tcp_pcb *pcb); + +static err_t tcp_listen_input(struct tcp_pcb_listen *pcb); +static err_t tcp_timewait_input(struct tcp_pcb *pcb); + + +/* tcp_input: + * + * The initial input processing of TCP. It verifies the TCP header, demultiplexes + * the segment between the PCBs and passes it on to tcp_process(), which implements + * the TCP finite state machine. This function is called by the IP layer (in + * ip_input()). + */ + +void +tcp_input(struct pbuf *p, struct netif *inp) +{ + struct tcp_pcb *pcb, *prev; + struct tcp_pcb_listen *lpcb; + u8_t hdrlen; + err_t err; + + PERF_START; + + TCP_STATS_INC(tcp.recv); + + iphdr = p->payload; + tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); + +#if TCP_INPUT_DEBUG + tcp_debug_print(tcphdr); +#endif + + /* remove header from payload */ + if (pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) { + /* drop short packets */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len)); + TCP_STATS_INC(tcp.lenerr); + TCP_STATS_INC(tcp.drop); + pbuf_free(p); + return; + } + + /* Don't even process incoming broadcasts/multicasts. */ + if (ip_addr_isbroadcast(&(iphdr->dest), inp) || + ip_addr_ismulticast(&(iphdr->dest))) { + pbuf_free(p); + return; + } + +#if CHECKSUM_CHECK_TCP + /* Verify TCP checksum. */ + if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), + (struct ip_addr *)&(iphdr->dest), + IP_PROTO_TCP, p->tot_len) != 0) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n", + inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest), + IP_PROTO_TCP, p->tot_len))); +#if TCP_DEBUG + tcp_debug_print(tcphdr); +#endif /* TCP_DEBUG */ + TCP_STATS_INC(tcp.chkerr); + TCP_STATS_INC(tcp.drop); + + pbuf_free(p); + return; + } +#endif + + /* Move the payload pointer in the pbuf so that it points to the + TCP data instead of the TCP header. */ + hdrlen = TCPH_HDRLEN(tcphdr); + pbuf_header(p, -(hdrlen * 4)); + + /* Convert fields in TCP header to host byte order. */ + tcphdr->src = ntohs(tcphdr->src); + tcphdr->dest = ntohs(tcphdr->dest); + seqno = tcphdr->seqno = ntohl(tcphdr->seqno); + ackno = tcphdr->ackno = ntohl(tcphdr->ackno); + tcphdr->wnd = ntohs(tcphdr->wnd); + + flags = TCPH_FLAGS(tcphdr) & TCP_FLAGS; + tcplen = p->tot_len + ((flags & TCP_FIN || flags & TCP_SYN)? 1: 0); + + /* Demultiplex an incoming segment. First, we check if it is destined + for an active connection. */ + prev = NULL; + + + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); + LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); + LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && + ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { + + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); + if (prev != NULL) { + prev->next = pcb->next; + pcb->next = tcp_active_pcbs; + tcp_active_pcbs = pcb; + } + LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); + break; + } + prev = pcb; + } + + if (pcb == NULL) { + /* If it did not go to an active connection, we check the connections + in the TIME-WAIT state. */ + + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); + if (pcb->remote_port == tcphdr->src && + pcb->local_port == tcphdr->dest && + ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && + ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { + /* We don't really care enough to move this PCB to the front + of the list since we are not very likely to receive that + many segments for connections in TIME-WAIT. */ + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); + tcp_timewait_input(pcb); + pbuf_free(p); + return; + } + } + + /* Finally, if we still did not get a match, we check all PCBs that + are LISTENing for incoming connections. */ + prev = NULL; + for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { + if ((ip_addr_isany(&(lpcb->local_ip)) || + ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest))) && + lpcb->local_port == tcphdr->dest) { + /* Move this PCB to the front of the list so that subsequent + lookups will be faster (we exploit locality in TCP segment + arrivals). */ + if (prev != NULL) { + ((struct tcp_pcb_listen *)prev)->next = lpcb->next; + /* our successor is the remainder of the listening list */ + lpcb->next = tcp_listen_pcbs.listen_pcbs; + /* put this listening pcb at the head of the listening list */ + tcp_listen_pcbs.listen_pcbs = lpcb; + } + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n")); + tcp_listen_input(lpcb); + pbuf_free(p); + return; + } + prev = (struct tcp_pcb *)lpcb; + } + } + +#if TCP_INPUT_DEBUG + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags ")); + tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n")); +#endif /* TCP_INPUT_DEBUG */ + + + if (pcb != NULL) { + /* The incoming segment belongs to a connection. */ +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + + /* Set up a tcp_seg structure. */ + inseg.next = NULL; + inseg.len = p->tot_len; + inseg.dataptr = p->payload; + inseg.p = p; + inseg.tcphdr = tcphdr; + + recv_data = NULL; + recv_flags = 0; + + tcp_input_pcb = pcb; + err = tcp_process(pcb); + tcp_input_pcb = NULL; + /* A return value of ERR_ABRT means that tcp_abort() was called + and that the pcb has been freed. If so, we don't do anything. */ + if (err != ERR_ABRT) { + if (recv_flags & TF_RESET) { + /* TF_RESET means that the connection was reset by the other + end. We then call the error callback to inform the + application that the connection is dead before we + deallocate the PCB. */ + TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST); + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else if (recv_flags & TF_CLOSED) { + /* The connection has been closed and we will deallocate the + PCB. */ + tcp_pcb_remove(&tcp_active_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } else { + err = ERR_OK; + /* If the application has registered a "sent" function to be + called when new send buffer space is available, we call it + now. */ + if (pcb->acked > 0) { + TCP_EVENT_SENT(pcb, pcb->acked, err); + } + + if (recv_data != NULL) { + /* Notify application that data has been received. */ + TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); + } + + /* If a FIN segment was received, we call the callback + function with a NULL buffer to indicate EOF. */ + if (recv_flags & TF_GOT_FIN) { + TCP_EVENT_RECV(pcb, NULL, ERR_OK, err); + } + /* If there were no errors, we try to send something out. */ + if (err == ERR_OK) { + tcp_output(pcb); + } + } + } + + + /* We deallocate the incoming pbuf. If it was buffered by the + application, the application should have called pbuf_ref() to + increase the reference counter in the pbuf. If so, the buffer + isn't actually deallocated by the call to pbuf_free(), only the + reference count is decreased. */ + if (inseg.p != NULL) pbuf_free(inseg.p); +#if TCP_INPUT_DEBUG +#if TCP_DEBUG + tcp_debug_print_state(pcb->state); +#endif /* TCP_DEBUG */ +#endif /* TCP_INPUT_DEBUG */ + + } else { + + /* If no matching PCB was found, send a TCP RST (reset) to the + sender. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n")); + if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) { + TCP_STATS_INC(tcp.proterr); + TCP_STATS_INC(tcp.drop); + tcp_rst(ackno, seqno + tcplen, + &(iphdr->dest), &(iphdr->src), + tcphdr->dest, tcphdr->src); + } + pbuf_free(p); + } + + LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); + PERF_STOP("tcp_input"); +} + +/* tcp_listen_input(): + * + * Called by tcp_input() when a segment arrives for a listening + * connection. + */ + +static err_t +tcp_listen_input(struct tcp_pcb_listen *pcb) +{ + struct tcp_pcb *npcb; + u32_t optdata; + + /* In the LISTEN state, we check for incoming SYN segments, + creates a new PCB, and responds with a SYN|ACK. */ + if (flags & TCP_ACK) { + /* For incoming segments with the ACK flag set, respond with a + RST. */ + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n")); + tcp_rst(ackno + 1, seqno + tcplen, + &(iphdr->dest), &(iphdr->src), + tcphdr->dest, tcphdr->src); + } else if (flags & TCP_SYN) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest)); + npcb = tcp_alloc(pcb->prio); + /* If a new PCB could not be created (probably due to lack of memory), + we don't do anything, but rely on the sender will retransmit the + SYN at a time when we have more memory available. */ + if (npcb == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n")); + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + /* Set up the new PCB. */ + ip_addr_set(&(npcb->local_ip), &(iphdr->dest)); + npcb->local_port = pcb->local_port; + ip_addr_set(&(npcb->remote_ip), &(iphdr->src)); + npcb->remote_port = tcphdr->src; + npcb->state = SYN_RCVD; + npcb->rcv_nxt = seqno + 1; + npcb->snd_wnd = tcphdr->wnd; + npcb->ssthresh = npcb->snd_wnd; + npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */ + npcb->callback_arg = pcb->callback_arg; +#if LWIP_CALLBACK_API + npcb->accept = pcb->accept; +#endif /* LWIP_CALLBACK_API */ + /* inherit socket options */ + npcb->so_options = pcb->so_options & (SOF_DEBUG|SOF_DONTROUTE|SOF_KEEPALIVE|SOF_OOBINLINE|SOF_LINGER); + /* Register the new PCB so that we can begin receiving segments + for it. */ + TCP_REG(&tcp_active_pcbs, npcb); + + /* Parse any options in the SYN. */ + tcp_parseopt(npcb); + + /* Build an MSS option. */ + optdata = htonl(((u32_t)2 << 24) | + ((u32_t)4 << 16) | + (((u32_t)npcb->mss / 256) << 8) | + (npcb->mss & 255)); + /* Send a SYN|ACK together with the MSS option. */ + tcp_enqueue(npcb, NULL, 0, TCP_SYN | TCP_ACK, 0, (u8_t *)&optdata, 4); + return tcp_output(npcb); + } + return ERR_OK; +} + +/* tcp_timewait_input(): + * + * Called by tcp_input() when a segment arrives for a connection in + * TIME_WAIT. + */ + +static err_t +tcp_timewait_input(struct tcp_pcb *pcb) +{ + if (TCP_SEQ_GT(seqno + tcplen, pcb->rcv_nxt)) { + pcb->rcv_nxt = seqno + tcplen; + } + if (tcplen > 0) { + tcp_ack_now(pcb); + } + return tcp_output(pcb); +} + +/* tcp_process + * + * Implements the TCP state machine. Called by tcp_input. In some + * states tcp_receive() is called to receive data. The tcp_seg + * argument will be freed by the caller (tcp_input()) unless the + * recv_data pointer in the pcb is set. + */ + +static err_t +tcp_process(struct tcp_pcb *pcb) +{ + struct tcp_seg *rseg; + u8_t acceptable = 0; + err_t err; + + + err = ERR_OK; + + /* Process incoming RST segments. */ + if (flags & TCP_RST) { + /* First, determine if the reset is acceptable. */ + if (pcb->state == SYN_SENT) { + if (ackno == pcb->snd_nxt) { + acceptable = 1; + } + } else { + /*if (TCP_SEQ_GEQ(seqno, pcb->rcv_nxt) && + TCP_SEQ_LEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) { + */ + if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) { + acceptable = 1; + } + } + + if (acceptable) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n")); + LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED); + recv_flags = TF_RESET; + pcb->flags &= ~TF_ACK_DELAY; + return ERR_RST; + } else { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n", + seqno, pcb->rcv_nxt)); + return ERR_OK; + } + } + + /* Update the PCB (in)activity timer. */ + pcb->tmr = tcp_ticks; + pcb->keep_cnt = 0; + + /* Do different things depending on the TCP state. */ + switch (pcb->state) { + case SYN_SENT: + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno, + pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno))); + /* received SYN ACK with expected sequence number? */ + if ((flags & TCP_ACK) && (flags & TCP_SYN) + && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) { + pcb->snd_buf++; + pcb->rcv_nxt = seqno + 1; + pcb->lastack = ackno; + pcb->snd_wnd = tcphdr->wnd; + pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */ + pcb->state = ESTABLISHED; + pcb->cwnd = pcb->mss; + --pcb->snd_queuelen; + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + rseg = pcb->unacked; + pcb->unacked = rseg->next; + tcp_seg_free(rseg); + + /* Parse any options in the SYNACK. */ + tcp_parseopt(pcb); + + /* Call the user specified function to call when sucessfully + * connected. */ + TCP_EVENT_CONNECTED(pcb, ERR_OK, err); + tcp_ack(pcb); + } + /* received ACK? possibly a half-open connection */ + else if (flags & TCP_ACK) { + /* send a RST to bring the other side in a non-synchronized state. */ + tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src), + tcphdr->dest, tcphdr->src); + } + break; + case SYN_RCVD: + if (flags & TCP_ACK && + !(flags & TCP_RST)) { + /* expected ACK number? */ + if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) { + pcb->state = ESTABLISHED; + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); +#if LWIP_CALLBACK_API + LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL); +#endif + /* Call the accept function. */ + TCP_EVENT_ACCEPT(pcb, ERR_OK, err); + if (err != ERR_OK) { + /* If the accept function returns with an error, we abort + * the connection. */ + tcp_abort(pcb); + return ERR_ABRT; + } + /* If there was any data contained within this ACK, + * we'd better pass it on to the application as well. */ + tcp_receive(pcb); + pcb->cwnd = pcb->mss; + } + /* incorrect ACK number */ + else { + /* send RST */ + tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src), + tcphdr->dest, tcphdr->src); + } + } + break; + case CLOSE_WAIT: + /* FALLTHROUGH */ + case ESTABLISHED: + tcp_receive(pcb); + if (flags & TCP_FIN) { + tcp_ack_now(pcb); + pcb->state = CLOSE_WAIT; + } + break; + case FIN_WAIT_1: + tcp_receive(pcb); + if (flags & TCP_FIN) { + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, + ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } else { + tcp_ack_now(pcb); + pcb->state = CLOSING; + } + } else if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + pcb->state = FIN_WAIT_2; + } + break; + case FIN_WAIT_2: + tcp_receive(pcb); + if (flags & TCP_FIN) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case CLOSING: + tcp_receive(pcb); + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + tcp_ack_now(pcb); + tcp_pcb_purge(pcb); + TCP_RMV(&tcp_active_pcbs, pcb); + pcb->state = TIME_WAIT; + TCP_REG(&tcp_tw_pcbs, pcb); + } + break; + case LAST_ACK: + tcp_receive(pcb); + if (flags & TCP_ACK && ackno == pcb->snd_nxt) { + LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest)); + pcb->state = CLOSED; + recv_flags = TF_CLOSED; + } + break; + default: + break; + } + return ERR_OK; +} + +/* tcp_receive: + * + * Called by tcp_process. Checks if the given segment is an ACK for outstanding + * data, and if so frees the memory of the buffered data. Next, is places the + * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment + * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until + * i it has been removed from the buffer. + * + * If the incoming segment constitutes an ACK for a segment that was used for RTT + * estimation, the RTT is estimated here as well. + */ + +static void +tcp_receive(struct tcp_pcb *pcb) +{ + struct tcp_seg *next; +#if TCP_QUEUE_OOSEQ + struct tcp_seg *prev, *cseg; +#endif + struct pbuf *p; + s32_t off; + s16_t m; + u32_t right_wnd_edge; + u16_t new_tot_len; + + + if (flags & TCP_ACK) { + right_wnd_edge = pcb->snd_wnd + pcb->snd_wl1; + + /* Update window. */ + if (TCP_SEQ_LT(pcb->snd_wl1, seqno) || + (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) || + (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) { + pcb->snd_wnd = tcphdr->wnd; + pcb->snd_wl1 = seqno; + pcb->snd_wl2 = ackno; + LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U32_F"\n", pcb->snd_wnd)); +#if TCP_WND_DEBUG + } else { + if (pcb->snd_wnd != tcphdr->wnd) { + LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: no window update lastack %"U32_F" snd_max %"U32_F" ackno %"U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n", + pcb->lastack, pcb->snd_max, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2)); + } +#endif /* TCP_WND_DEBUG */ + } + + + if (pcb->lastack == ackno) { + pcb->acked = 0; + + if (pcb->snd_wl1 + pcb->snd_wnd == right_wnd_edge){ + ++pcb->dupacks; + if (pcb->dupacks >= 3 && pcb->unacked != NULL) { + if (!(pcb->flags & TF_INFR)) { + /* This is fast retransmit. Retransmit the first unacked segment. */ + LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: dupacks %"U16_F" (%"U32_F"), fast retransmit %"U32_F"\n", + (u16_t)pcb->dupacks, pcb->lastack, + ntohl(pcb->unacked->tcphdr->seqno))); + tcp_rexmit(pcb); + /* Set ssthresh to max (FlightSize / 2, 2*SMSS) */ + /*pcb->ssthresh = LWIP_MAX((pcb->snd_max - + pcb->lastack) / 2, + 2 * pcb->mss);*/ + /* Set ssthresh to half of the minimum of the currenct cwnd and the advertised window */ + if(pcb->cwnd > pcb->snd_wnd) + pcb->ssthresh = pcb->snd_wnd / 2; + else + pcb->ssthresh = pcb->cwnd / 2; + + pcb->cwnd = pcb->ssthresh + 3 * pcb->mss; + pcb->flags |= TF_INFR; + } else { + /* Inflate the congestion window, but not if it means that + the value overflows. */ + if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + } + } + } else { + LWIP_DEBUGF(TCP_FR_DEBUG, ("tcp_receive: dupack averted %"U32_F" %"U32_F"\n", + pcb->snd_wl1 + pcb->snd_wnd, right_wnd_edge)); + } + } else + /*if (TCP_SEQ_LT(pcb->lastack, ackno) && + TCP_SEQ_LEQ(ackno, pcb->snd_max)) { */ + if(TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_max)){ + /* We come here when the ACK acknowledges new data. */ + + /* Reset the "IN Fast Retransmit" flag, since we are no longer + in fast retransmit. Also reset the congestion window to the + slow start threshold. */ + if (pcb->flags & TF_INFR) { + pcb->flags &= ~TF_INFR; + pcb->cwnd = pcb->ssthresh; + } + + /* Reset the number of retransmissions. */ + pcb->nrtx = 0; + + /* Reset the retransmission time-out. */ + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + /* Update the send buffer space. */ + pcb->acked = ackno - pcb->lastack; + + pcb->snd_buf += pcb->acked; + + /* Reset the fast retransmit variables. */ + pcb->dupacks = 0; + pcb->lastack = ackno; + + /* Update the congestion control variables (cwnd and + ssthresh). */ + if (pcb->state >= ESTABLISHED) { + if (pcb->cwnd < pcb->ssthresh) { + if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) { + pcb->cwnd += pcb->mss; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd)); + } else { + u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd); + if (new_cwnd > pcb->cwnd) { + pcb->cwnd = new_cwnd; + } + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd)); + } + } + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n", + ackno, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno): 0, + pcb->unacked != NULL? + ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0)); + + /* Remove segment from the unacknowledged list if the incoming + ACK acknowlegdes them. */ + while (pcb->unacked != NULL && + TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked), ackno)) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n", + ntohl(pcb->unacked->tcphdr->seqno), + ntohl(pcb->unacked->tcphdr->seqno) + + TCP_TCPLEN(pcb->unacked))); + + next = pcb->unacked; + pcb->unacked = pcb->unacked->next; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + pcb->snd_queuelen -= pbuf_clen(next->p); + tcp_seg_free(next); + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (u16_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + } + pcb->polltmr = 0; + } + + /* We go through the ->unsent list to see if any of the segments + on the list are acknowledged by the ACK. This may seem + strange since an "unsent" segment shouldn't be acked. The + rationale is that lwIP puts all outstanding segments on the + ->unsent list after a retransmission, so these segments may + in fact have been sent once. */ + while (pcb->unsent != NULL && + /*TCP_SEQ_LEQ(ntohl(pcb->unsent->tcphdr->seqno) + TCP_TCPLEN(pcb->unsent), ackno) && + TCP_SEQ_LEQ(ackno, pcb->snd_max)*/ + TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) + TCP_TCPLEN(pcb->unsent), pcb->snd_max) + ) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n", + ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) + + TCP_TCPLEN(pcb->unsent))); + + next = pcb->unsent; + pcb->unsent = pcb->unsent->next; + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen)); + pcb->snd_queuelen -= pbuf_clen(next->p); + tcp_seg_free(next); + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (u16_t)pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_receive: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + if (pcb->unsent != NULL) { + pcb->snd_nxt = htonl(pcb->unsent->tcphdr->seqno); + } + } + /* End of ACK for new data processing. */ + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n", + pcb->rttest, pcb->rtseq, ackno)); + + /* RTT estimation calculations. This is done by checking if the + incoming segment acknowledges the segment we use to take a + round-trip time measurement. */ + if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) { + m = tcp_ticks - pcb->rttest; + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n", + m, m * TCP_SLOW_INTERVAL)); + + /* This is taken directly from VJs original code in his paper */ + m = m - (pcb->sa >> 3); + pcb->sa += m; + if (m < 0) { + m = -m; + } + m = m - (pcb->sv >> 2); + pcb->sv += m; + pcb->rto = (pcb->sa >> 3) + pcb->sv; + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" miliseconds)\n", + pcb->rto, pcb->rto * TCP_SLOW_INTERVAL)); + + pcb->rttest = 0; + } + } + + /* If the incoming segment contains data, we must process it + further. */ + if (tcplen > 0) { + /* This code basically does three things: + + +) If the incoming segment contains data that is the next + in-sequence data, this data is passed to the application. This + might involve trimming the first edge of the data. The rcv_nxt + variable and the advertised window are adjusted. + + +) If the incoming segment has data that is above the next + sequence number expected (->rcv_nxt), the segment is placed on + the ->ooseq queue. This is done by finding the appropriate + place in the ->ooseq queue (which is ordered by sequence + number) and trim the segment in both ends if needed. An + immediate ACK is sent to indicate that we received an + out-of-sequence segment. + + +) Finally, we check if the first segment on the ->ooseq queue + now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If + rcv_nxt > ooseq->seqno, we must trim the first edge of the + segment on ->ooseq before we adjust rcv_nxt. The data in the + segments that are now on sequence are chained onto the + incoming segment so that we only need to call the application + once. + */ + + /* First, we check if we must trim the first edge. We have to do + this if the sequence number of the incoming segment is less + than rcv_nxt, and the sequence number plus the length of the + segment is larger than rcv_nxt. */ + /* if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ + if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/ + if(TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno+1, seqno+tcplen-1)){ + /* Trimming the first edge is done by pushing the payload + pointer in the pbuf downwards. This is somewhat tricky since + we do not want to discard the full contents of the pbuf up to + the new starting point of the data since we have to keep the + TCP header which is present in the first pbuf in the chain. + + What is done is really quite a nasty hack: the first pbuf in + the pbuf chain is pointed to by inseg.p. Since we need to be + able to deallocate the whole pbuf, we cannot change this + inseg.p pointer to point to any of the later pbufs in the + chain. Instead, we point the ->payload pointer in the first + pbuf to data in one of the later pbufs. We also set the + inseg.data pointer to point to the right place. This way, the + ->p pointer will still point to the first pbuf, but the + ->p->payload pointer will point to data in another pbuf. + + After we are done with adjusting the pbuf pointers we must + adjust the ->data pointer in the seg and the segment + length.*/ + + off = pcb->rcv_nxt - seqno; + p = inseg.p; + if (inseg.p->len < off) { + new_tot_len = inseg.p->tot_len - off; + while (p->len < off) { + off -= p->len; + /* KJM following line changed (with addition of new_tot_len var) + to fix bug #9076 + inseg.p->tot_len -= p->len; */ + p->tot_len = new_tot_len; + p->len = 0; + p = p->next; + } + pbuf_header(p, -off); + } else { + pbuf_header(inseg.p, -off); + } + /* KJM following line changed to use p->payload rather than inseg->p->payload + to fix bug #9076 */ + inseg.dataptr = p->payload; + inseg.len -= pcb->rcv_nxt - seqno; + inseg.tcphdr->seqno = seqno = pcb->rcv_nxt; + } + else{ + if(TCP_SEQ_LT(seqno, pcb->rcv_nxt)){ + /* the whole segment is < rcv_nxt */ + /* must be a duplicate of a packet that has already been correctly handled */ + + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno)); + tcp_ack_now(pcb); + } + } + + /* The sequence number must be within the window (above rcv_nxt + and below rcv_nxt + rcv_wnd) in order to be further + processed. */ + /*if (TCP_SEQ_GEQ(seqno, pcb->rcv_nxt) && + TCP_SEQ_LT(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/ + if(TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd - 1)){ + if (pcb->rcv_nxt == seqno) { + /* The incoming segment is the next in sequence. We check if + we have to trim the end of the segment and update rcv_nxt + and pass the data to the application. */ +#if TCP_QUEUE_OOSEQ + if (pcb->ooseq != NULL && + TCP_SEQ_LEQ(pcb->ooseq->tcphdr->seqno, seqno + inseg.len)) { + /* We have to trim the second edge of the incoming + segment. */ + inseg.len = pcb->ooseq->tcphdr->seqno - seqno; + pbuf_realloc(inseg.p, inseg.len); + } +#endif /* TCP_QUEUE_OOSEQ */ + + tcplen = TCP_TCPLEN(&inseg); + + /* First received FIN will be ACKed +1, on any successive (duplicate) + * FINs we are already in CLOSE_WAIT and have already done +1. + */ + if (pcb->state != CLOSE_WAIT) { + pcb->rcv_nxt += tcplen; + } + + /* Update the receiver's (our) window. */ + if (pcb->rcv_wnd < tcplen) { + pcb->rcv_wnd = 0; + } else { + pcb->rcv_wnd -= tcplen; + } + + /* If there is data in the segment, we make preparations to + pass this up to the application. The ->recv_data variable + is used for holding the pbuf that goes to the + application. The code for reassembling out-of-sequence data + chains its data on this pbuf as well. + + If the segment was a FIN, we set the TF_GOT_FIN flag that will + be used to indicate to the application that the remote side has + closed its end of the connection. */ + if (inseg.p->tot_len > 0) { + recv_data = inseg.p; + /* Since this pbuf now is the responsibility of the + application, we delete our reference to it so that we won't + (mistakingly) deallocate it. */ + inseg.p = NULL; + } + if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n")); + recv_flags = TF_GOT_FIN; + } + +#if TCP_QUEUE_OOSEQ + /* We now check if we have segments on the ->ooseq queue that + is now in sequence. */ + while (pcb->ooseq != NULL && + pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) { + + cseg = pcb->ooseq; + seqno = pcb->ooseq->tcphdr->seqno; + + pcb->rcv_nxt += TCP_TCPLEN(cseg); + if (pcb->rcv_wnd < TCP_TCPLEN(cseg)) { + pcb->rcv_wnd = 0; + } else { + pcb->rcv_wnd -= TCP_TCPLEN(cseg); + } + if (cseg->p->tot_len > 0) { + /* Chain this pbuf onto the pbuf that we will pass to + the application. */ + if (recv_data) { + pbuf_cat(recv_data, cseg->p); + } else { + recv_data = cseg->p; + } + cseg->p = NULL; + } + if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) { + LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n")); + recv_flags = TF_GOT_FIN; + } + + + pcb->ooseq = cseg->next; + tcp_seg_free(cseg); + } +#endif /* TCP_QUEUE_OOSEQ */ + + + /* Acknowledge the segment(s). */ + tcp_ack(pcb); + + } else { + /* We get here if the incoming segment is out-of-sequence. */ + tcp_ack_now(pcb); +#if TCP_QUEUE_OOSEQ + /* We queue the segment on the ->ooseq queue. */ + if (pcb->ooseq == NULL) { + pcb->ooseq = tcp_seg_copy(&inseg); + } else { + /* If the queue is not empty, we walk through the queue and + try to find a place where the sequence number of the + incoming segment is between the sequence numbers of the + previous and the next segment on the ->ooseq queue. That is + the place where we put the incoming segment. If needed, we + trim the second edges of the previous and the incoming + segment so that it will fit into the sequence. + + If the incoming segment has the same sequence number as a + segment on the ->ooseq queue, we discard the segment that + contains less data. */ + + prev = NULL; + for(next = pcb->ooseq; next != NULL; next = next->next) { + if (seqno == next->tcphdr->seqno) { + /* The sequence number of the incoming segment is the + same as the sequence number of the segment on + ->ooseq. We check the lengths to see which one to + discard. */ + if (inseg.len > next->len) { + /* The incoming segment is larger than the old + segment. We replace the old segment with the new + one. */ + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + cseg->next = next->next; + if (prev != NULL) { + prev->next = cseg; + } else { + pcb->ooseq = cseg; + } + } + break; + } else { + /* Either the lenghts are the same or the incoming + segment was smaller than the old one; in either + case, we ditch the incoming segment. */ + break; + } + } else { + if (prev == NULL) { + if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) { + /* The sequence number of the incoming segment is lower + than the sequence number of the first segment on the + queue. We put the incoming segment first on the + queue. */ + + if (TCP_SEQ_GT(seqno + inseg.len, next->tcphdr->seqno)) { + /* We need to trim the incoming segment. */ + inseg.len = next->tcphdr->seqno - seqno; + pbuf_realloc(inseg.p, inseg.len); + } + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + cseg->next = next; + pcb->ooseq = cseg; + } + break; + } + } else + /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) && + TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/ + if(TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)){ + /* The sequence number of the incoming segment is in + between the sequence numbers of the previous and + the next segment on ->ooseq. We trim and insert the + incoming segment and trim the previous segment, if + needed. */ + if (TCP_SEQ_GT(seqno + inseg.len, next->tcphdr->seqno)) { + /* We need to trim the incoming segment. */ + inseg.len = next->tcphdr->seqno - seqno; + pbuf_realloc(inseg.p, inseg.len); + } + + cseg = tcp_seg_copy(&inseg); + if (cseg != NULL) { + cseg->next = next; + prev->next = cseg; + if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) { + /* We need to trim the prev segment. */ + prev->len = seqno - prev->tcphdr->seqno; + pbuf_realloc(prev->p, prev->len); + } + } + break; + } + /* If the "next" segment is the last segment on the + ooseq queue, we add the incoming segment to the end + of the list. */ + if (next->next == NULL && + TCP_SEQ_GT(seqno, next->tcphdr->seqno)) { + next->next = tcp_seg_copy(&inseg); + if (next->next != NULL) { + if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) { + /* We need to trim the last segment. */ + next->len = seqno - next->tcphdr->seqno; + pbuf_realloc(next->p, next->len); + } + } + break; + } + } + prev = next; + } + } +#endif /* TCP_QUEUE_OOSEQ */ + + } + } else { + /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) || + TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/ + if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){ + tcp_ack_now(pcb); + } + } + } else { + /* Segments with length 0 is taken care of here. Segments that + fall out of the window are ACKed. */ + /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) || + TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/ + if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){ + tcp_ack_now(pcb); + } + } +} + +/* + * tcp_parseopt: + * + * Parses the options contained in the incoming segment. (Code taken + * from uIP with only small changes.) + * + */ + +static void +tcp_parseopt(struct tcp_pcb *pcb) +{ + u8_t c; + u8_t *opts, opt; + u16_t mss; + + opts = (u8_t *)tcphdr + TCP_HLEN; + + /* Parse the TCP MSS option, if present. */ + if(TCPH_HDRLEN(tcphdr) > 0x5) { + for(c = 0; c < (TCPH_HDRLEN(tcphdr) - 5) << 2 ;) { + opt = opts[c]; + if (opt == 0x00) { + /* End of options. */ + break; + } else if (opt == 0x01) { + ++c; + /* NOP option. */ + } else if (opt == 0x02 && + opts[c + 1] == 0x04) { + /* An MSS option with the right option length. */ + mss = (opts[c + 2] << 8) | opts[c + 3]; + pcb->mss = mss > TCP_MSS? TCP_MSS: mss; + + /* And we are done processing options. */ + break; + } else { + if (opts[c + 1] == 0) { + /* If the length field is zero, the options are malformed + and we don't process them further. */ + break; + } + /* All other options have a length field, so that we easily + can skip past them. */ + c += opts[c + 1]; + } + } + } +} +#endif /* LWIP_TCP */ + + diff --git a/wii/libogc/lwip/core/tcp_out.c b/wii/libogc/lwip/core/tcp_out.c new file mode 100644 index 0000000000..edfdd8e7b4 --- /dev/null +++ b/wii/libogc/lwip/core/tcp_out.c @@ -0,0 +1,721 @@ +/** + * @file + * + * Transmission Control Protocol, outgoing traffic + * + * The output functions of TCP. + * + */ + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include + +#include "lwip/def.h" +#include "lwip/opt.h" +#include "lwip/mem.h" +#include "lwip/memp.h" +#include "lwip/sys.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/inet.h" +#include "lwip/tcp.h" +#include "lwip/stats.h" + +#if LWIP_TCP + +/* Forward declarations.*/ +static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb); + +err_t +tcp_send_ctrl(struct tcp_pcb *pcb, u8_t flags) +{ + /* no data, no length, flags, copy=1, no optdata, no optdatalen */ + return tcp_enqueue(pcb, NULL, 0, flags, 1, NULL, 0); +} + +/** + * Write data for sending (but does not send it immediately). + * + * It waits in the expectation of more data being sent soon (as + * it can send them more efficiently by combining them together). + * To prompt the system to send data now, call tcp_output() after + * calling tcp_write(). + * + * @arg pcb Protocol control block of the TCP connection to enqueue data for. + * + * @see tcp_write() + */ + +err_t +tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t copy) +{ + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, arg=%p, len=%"U16_F", copy=%"U16_F")\n", (void *)pcb, + arg, len, (u16_t)copy)); + /* connection is in valid state for data transmission? */ + if (pcb->state == ESTABLISHED || + pcb->state == CLOSE_WAIT || + pcb->state == SYN_SENT || + pcb->state == SYN_RCVD) { + if (len > 0) { + return tcp_enqueue(pcb, (void *)arg, len, 0, copy, NULL, 0); + } + return ERR_OK; + } else { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | DBG_STATE | 3, ("tcp_write() called in invalid state\n")); + return ERR_CONN; + } +} + +/** + * Enqueue either data or TCP options (but not both) for tranmission + * + * + * + * @arg pcb Protocol control block for the TCP connection to enqueue data for. + * @arg arg Pointer to the data to be enqueued for sending. + * @arg len Data length in bytes + * @arg flags + * @arg copy 1 if data must be copied, 0 if data is non-volatile and can be + * referenced. + * @arg optdata + * @arg optlen + */ +err_t +tcp_enqueue(struct tcp_pcb *pcb, void *arg, u16_t len, + u8_t flags, u8_t copy, + u8_t *optdata, u8_t optlen) +{ + struct pbuf *p; + struct tcp_seg *seg, *useg, *queue; + u32_t left, seqno; + u16_t seglen; + void *ptr; + u16_t queuelen; + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_enqueue(pcb=%p, arg=%p, len=%"U16_F", flags=%"X16_F", copy=%"U16_F")\n", + (void *)pcb, arg, len, (u16_t)flags, (u16_t)copy)); + LWIP_ASSERT("tcp_enqueue: len == 0 || optlen == 0 (programmer violates API)", + len == 0 || optlen == 0); + LWIP_ASSERT("tcp_enqueue: arg == NULL || optdata == NULL (programmer violates API)", + arg == NULL || optdata == NULL); + /* fail on too much data */ + if (len > pcb->snd_buf) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n", len, pcb->snd_buf)); + return ERR_MEM; + } + left = len; + ptr = arg; + + /* seqno will be the sequence number of the first segment enqueued + * by the call to this function. */ + seqno = pcb->snd_lbb; + + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen)); + + /* If total number of pbufs on the unsent/unacked queues exceeds the + * configured maximum, return an error */ + queuelen = pcb->snd_queuelen; + if (queuelen >= TCP_SND_QUEUELEN) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue: too long queue %"U16_F" (max %"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); + TCP_STATS_INC(tcp.memerr); + return ERR_MEM; + } + if (queuelen != 0) { + LWIP_ASSERT("tcp_enqueue: pbufs on queue => at least one queue non-empty", + pcb->unacked != NULL || pcb->unsent != NULL); + } else { + LWIP_ASSERT("tcp_enqueue: no pbufs on queue => both queues empty", + pcb->unacked == NULL && pcb->unsent == NULL); + } + + /* First, break up the data into segments and tuck them together in + * the local "queue" variable. */ + useg = queue = seg = NULL; + seglen = 0; + while (queue == NULL || left > 0) { + + /* The segment length should be the MSS if the data to be enqueued + * is larger than the MSS. */ + seglen = left > pcb->mss? pcb->mss: left; + + /* Allocate memory for tcp_seg, and fill in fields. */ + seg = memp_malloc(MEMP_TCP_SEG); + if (seg == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for tcp_seg\n")); + goto memerr; + } + seg->next = NULL; + seg->p = NULL; + + /* first segment of to-be-queued data? */ + if (queue == NULL) { + queue = seg; + } + /* subsequent segments of to-be-queued data */ + else { + /* Attach the segment to the end of the queued segments */ + LWIP_ASSERT("useg != NULL", useg != NULL); + useg->next = seg; + } + /* remember last segment of to-be-queued data for next iteration */ + useg = seg; + + /* If copy is set, memory should be allocated + * and data copied into pbuf, otherwise data comes from + * ROM or other static memory, and need not be copied. If + * optdata is != NULL, we have options instead of data. */ + + /* options? */ + if (optdata != NULL) { + if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) { + goto memerr; + } + ++queuelen; + seg->dataptr = seg->p->payload; + } + /* copy from volatile memory? */ + else if (copy) { + if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_RAM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue : could not allocate memory for pbuf copy size %"U16_F"\n", seglen)); + goto memerr; + } + ++queuelen; + if (arg != NULL) { + memcpy(seg->p->payload, ptr, seglen); + } + seg->dataptr = seg->p->payload; + } + /* do not copy data */ + else { + /* First, allocate a pbuf for holding the data. + * since the referenced data is available at least until it is sent out on the + * link (as it has to be ACKed by the remote party) we can safely use PBUF_ROM + * instead of PBUF_REF here. + */ + if ((p = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for zero-copy pbuf\n")); + goto memerr; + } + ++queuelen; + /* reference the non-volatile payload data */ + p->payload = ptr; + seg->dataptr = ptr; + + /* Second, allocate a pbuf for the headers. */ + if ((seg->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_RAM)) == NULL) { + /* If allocation fails, we have to deallocate the data pbuf as + * well. */ + pbuf_free(p); + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: could not allocate memory for header pbuf\n")); + goto memerr; + } + ++queuelen; + + /* Concatenate the headers and data pbufs together. */ + pbuf_cat(seg->p/*header*/, p/*data*/); + p = NULL; + } + + /* Now that there are more segments queued, we check again if the + length of the queue exceeds the configured maximum. */ + if (queuelen > TCP_SND_QUEUELEN) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN)); + goto memerr; + } + + seg->len = seglen; + + /* build TCP header */ + if (pbuf_header(seg->p, TCP_HLEN)) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_enqueue: no room for TCP header in pbuf.\n")); + TCP_STATS_INC(tcp.err); + goto memerr; + } + seg->tcphdr = seg->p->payload; + seg->tcphdr->src = htons(pcb->local_port); + seg->tcphdr->dest = htons(pcb->remote_port); + seg->tcphdr->seqno = htonl(seqno); + seg->tcphdr->urgp = 0; + TCPH_FLAGS_SET(seg->tcphdr, flags); + /* don't fill in tcphdr->ackno and tcphdr->wnd until later */ + + /* Copy the options into the header, if they are present. */ + if (optdata == NULL) { + TCPH_HDRLEN_SET(seg->tcphdr, 5); + } + else { + TCPH_HDRLEN_SET(seg->tcphdr, (5 + optlen / 4)); + /* Copy options into data portion of segment. + Options can thus only be sent in non data carrying + segments such as SYN|ACK. */ + memcpy(seg->dataptr, optdata, optlen); + } + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | DBG_TRACE, ("tcp_enqueue: queueing %"U32_F":%"U32_F" (0x%"X16_F")\n", + ntohl(seg->tcphdr->seqno), + ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg), + (u16_t)flags)); + + left -= seglen; + seqno += seglen; + ptr = (void *)((u8_t *)ptr + seglen); + } + + /* Now that the data to be enqueued has been broken up into TCP + segments in the queue variable, we add them to the end of the + pcb->unsent queue. */ + if (pcb->unsent == NULL) { + useg = NULL; + } + else { + for (useg = pcb->unsent; useg->next != NULL; useg = useg->next); + } + /* { useg is last segment on the unsent queue, NULL if list is empty } */ + + /* If there is room in the last pbuf on the unsent queue, + chain the first pbuf on the queue together with that. */ + if (useg != NULL && + TCP_TCPLEN(useg) != 0 && + !(TCPH_FLAGS(useg->tcphdr) & (TCP_SYN | TCP_FIN)) && + !(flags & (TCP_SYN | TCP_FIN)) && + /* fit within max seg size */ + useg->len + queue->len <= pcb->mss) { + /* Remove TCP header from first segment of our to-be-queued list */ + pbuf_header(queue->p, -TCP_HLEN); + pbuf_cat(useg->p, queue->p); + useg->len += queue->len; + useg->next = queue->next; + + LWIP_DEBUGF(TCP_OUTPUT_DEBUG | DBG_TRACE | DBG_STATE, ("tcp_enqueue: chaining segments, new len %"U16_F"\n", useg->len)); + if (seg == queue) { + seg = NULL; + } + memp_free(MEMP_TCP_SEG, queue); + } + else { + /* empty list */ + if (useg == NULL) { + /* initialize list with this segment */ + pcb->unsent = queue; + } + /* enqueue segment */ + else { + useg->next = queue; + } + } + if ((flags & TCP_SYN) || (flags & TCP_FIN)) { + ++len; + } + pcb->snd_lbb += len; + + pcb->snd_buf -= len; + + /* update number of segments on the queues */ + pcb->snd_queuelen = queuelen; + LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: %"S16_F" (after enqueued)\n", pcb->snd_queuelen)); + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_enqueue: valid queue length", + pcb->unacked != NULL || pcb->unsent != NULL); + } + + /* Set the PSH flag in the last segment that we enqueued, but only + if the segment has data (indicated by seglen > 0). */ + if (seg != NULL && seglen > 0 && seg->tcphdr != NULL) { + TCPH_SET_FLAG(seg->tcphdr, TCP_PSH); + } + + return ERR_OK; +memerr: + TCP_STATS_INC(tcp.memerr); + + if (queue != NULL) { + tcp_segs_free(queue); + } + if (pcb->snd_queuelen != 0) { + LWIP_ASSERT("tcp_enqueue: valid queue length", pcb->unacked != NULL || + pcb->unsent != NULL); + } + LWIP_DEBUGF(TCP_QLEN_DEBUG | DBG_STATE, ("tcp_enqueue: %"S16_F" (with mem err)\n", pcb->snd_queuelen)); + return ERR_MEM; +} + +/* find out what we can send and send it */ +err_t +tcp_output(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + struct tcp_seg *seg, *useg; + u32_t wnd; +#if TCP_CWND_DEBUG + s16_t i = 0; +#endif /* TCP_CWND_DEBUG */ + + /* First, check if we are invoked by the TCP input processing + code. If so, we do not output anything. Instead, we rely on the + input processing code to call us when input processing is done + with. */ + if (tcp_input_pcb == pcb) { + return ERR_OK; + } + + wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd); + + seg = pcb->unsent; + + /* useg should point to last segment on unacked queue */ + useg = pcb->unacked; + if (useg != NULL) { + for (; useg->next != NULL; useg = useg->next); + } + + /* If the TF_ACK_NOW flag is set and no data will be sent (either + * because the ->unsent queue is empty or because the window does + * not allow it), construct an empty ACK segment and send it. + * + * If data is to be sent, we will just piggyback the ACK (see below). + */ + if (pcb->flags & TF_ACK_NOW && + (seg == NULL || + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) { + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + if (p == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n")); + return ERR_BUF; + } + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt)); + /* remove ACK flags from the PCB, as we send an empty ACK now */ + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + + tcphdr = p->payload; + tcphdr->src = htons(pcb->local_port); + tcphdr->dest = htons(pcb->remote_port); + tcphdr->seqno = htonl(pcb->snd_nxt); + tcphdr->ackno = htonl(pcb->rcv_nxt); + TCPH_FLAGS_SET(tcphdr, TCP_ACK); + tcphdr->wnd = htons(pcb->rcv_wnd); + tcphdr->urgp = 0; + TCPH_HDRLEN_SET(tcphdr, 5); + + tcphdr->chksum = 0; +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, &(pcb->local_ip), &(pcb->remote_ip), + IP_PROTO_TCP, p->tot_len); +#endif + ip_output(p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + IP_PROTO_TCP); + pbuf_free(p); + + return ERR_OK; + } + +#if TCP_OUTPUT_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n", (void*)pcb->unsent)); + } +#endif /* TCP_OUTPUT_DEBUG */ +#if TCP_CWND_DEBUG + if (seg == NULL) { + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U32_F", cwnd %"U16_F", wnd %"U32_F", seg == NULL, ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + pcb->lastack)); + } else { + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U32_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len, + ntohl(seg->tcphdr->seqno), pcb->lastack)); + } +#endif /* TCP_CWND_DEBUG */ + /* data available and window allows it to be sent? */ + while (seg != NULL && + ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) { +#if TCP_CWND_DEBUG + LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U32_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n", + pcb->snd_wnd, pcb->cwnd, wnd, + ntohl(seg->tcphdr->seqno) + seg->len - + pcb->lastack, + ntohl(seg->tcphdr->seqno), pcb->lastack, i)); + ++i; +#endif /* TCP_CWND_DEBUG */ + + pcb->unsent = seg->next; + + if (pcb->state != SYN_SENT) { + TCPH_SET_FLAG(seg->tcphdr, TCP_ACK); + pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW); + } + + tcp_output_segment(seg, pcb); + pcb->snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg); + if (TCP_SEQ_LT(pcb->snd_max, pcb->snd_nxt)) { + pcb->snd_max = pcb->snd_nxt; + } + /* put segment on unacknowledged list if length > 0 */ + if (TCP_TCPLEN(seg) > 0) { + seg->next = NULL; + /* unacked list is empty? */ + if (pcb->unacked == NULL) { + pcb->unacked = seg; + useg = seg; + /* unacked list is not empty? */ + } else { + /* In the case of fast retransmit, the packet should not go to the tail + * of the unacked queue, but rather at the head. We need to check for + * this case. -STJ Jul 27, 2004 */ + if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))){ + /* add segment to head of unacked list */ + seg->next = pcb->unacked; + pcb->unacked = seg; + } else { + /* add segment to tail of unacked list */ + useg->next = seg; + useg = useg->next; + } + } + /* do not queue empty segments on the unacked list */ + } else { + tcp_seg_free(seg); + } + seg = pcb->unsent; + } + return ERR_OK; +} + +/** + * Actually send a TCP segment over IP + */ +static void +tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) +{ + u16_t len; + struct netif *netif; + + /* The TCP header has already been constructed, but the ackno and + wnd fields remain. */ + seg->tcphdr->ackno = htonl(pcb->rcv_nxt); + + /* silly window avoidance */ + if (pcb->rcv_wnd < pcb->mss) { + seg->tcphdr->wnd = 0; + } else { + /* advertise our receive window size in this TCP segment */ + seg->tcphdr->wnd = htons(pcb->rcv_wnd); + } + + /* If we don't have a local IP address, we get one by + calling ip_route(). */ + if (ip_addr_isany(&(pcb->local_ip))) { + netif = ip_route(&(pcb->remote_ip)); + if (netif == NULL) { + return; + } + ip_addr_set(&(pcb->local_ip), &(netif->ip_addr)); + } + + pcb->rtime = 0; + + if (pcb->rttest == 0) { + pcb->rttest = tcp_ticks; + pcb->rtseq = ntohl(seg->tcphdr->seqno); + + LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq)); + } + LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n", + htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) + + seg->len)); + + len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload); + + seg->p->len -= len; + seg->p->tot_len -= len; + + seg->p->payload = seg->tcphdr; + + seg->tcphdr->chksum = 0; +#if CHECKSUM_GEN_TCP + seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, + &(pcb->local_ip), + &(pcb->remote_ip), + IP_PROTO_TCP, seg->p->tot_len); +#endif + TCP_STATS_INC(tcp.xmit); + + ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, + IP_PROTO_TCP); +} + +void +tcp_rst(u32_t seqno, u32_t ackno, + struct ip_addr *local_ip, struct ip_addr *remote_ip, + u16_t local_port, u16_t remote_port) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + if (p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n")); + return; + } + + tcphdr = p->payload; + tcphdr->src = htons(local_port); + tcphdr->dest = htons(remote_port); + tcphdr->seqno = htonl(seqno); + tcphdr->ackno = htonl(ackno); + TCPH_FLAGS_SET(tcphdr, TCP_RST | TCP_ACK); + tcphdr->wnd = htons(TCP_WND); + tcphdr->urgp = 0; + TCPH_HDRLEN_SET(tcphdr, 5); + + tcphdr->chksum = 0; +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, local_ip, remote_ip, + IP_PROTO_TCP, p->tot_len); +#endif + TCP_STATS_INC(tcp.xmit); + /* Send output with hardcoded TTL since we have no access to the pcb */ + ip_output(p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP); + pbuf_free(p); + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno)); +} + +/* requeue all unacked segments for retransmission */ +void +tcp_rexmit_rto(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + + if (pcb->unacked == NULL) { + return; + } + + /* Move all unacked segments to the head of the unsent queue */ + for (seg = pcb->unacked; seg->next != NULL; seg = seg->next); + /* concatenate unsent queue after unacked queue */ + seg->next = pcb->unsent; + /* unsent queue is the concatenated queue (of unacked, unsent) */ + pcb->unsent = pcb->unacked; + /* unacked queue is now empty */ + pcb->unacked = NULL; + + pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno); + /* increment number of retransmissions */ + ++pcb->nrtx; + + /* Don't take any RTT measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission */ + tcp_output(pcb); +} + +void +tcp_rexmit(struct tcp_pcb *pcb) +{ + struct tcp_seg *seg; + + if (pcb->unacked == NULL) { + return; + } + + /* Move the first unacked segment to the unsent queue */ + seg = pcb->unacked->next; + pcb->unacked->next = pcb->unsent; + pcb->unsent = pcb->unacked; + pcb->unacked = seg; + + pcb->snd_nxt = ntohl(pcb->unsent->tcphdr->seqno); + + ++pcb->nrtx; + + /* Don't take any rtt measurements after retransmitting. */ + pcb->rttest = 0; + + /* Do the actual retransmission. */ + tcp_output(pcb); + +} + + +void +tcp_keepalive(struct tcp_pcb *pcb) +{ + struct pbuf *p; + struct tcp_hdr *tcphdr; + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", + ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip))); + + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt %"U16_F"\n", tcp_ticks, pcb->tmr, pcb->keep_cnt)); + + p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM); + + if(p == NULL) { + LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: could not allocate memory for pbuf\n")); + return; + } + + tcphdr = p->payload; + tcphdr->src = htons(pcb->local_port); + tcphdr->dest = htons(pcb->remote_port); + tcphdr->seqno = htonl(pcb->snd_nxt - 1); + tcphdr->ackno = htonl(pcb->rcv_nxt); + tcphdr->wnd = htons(pcb->rcv_wnd); + tcphdr->urgp = 0; + TCPH_HDRLEN_SET(tcphdr, 5); + + tcphdr->chksum = 0; +#if CHECKSUM_GEN_TCP + tcphdr->chksum = inet_chksum_pseudo(p, &pcb->local_ip, &pcb->remote_ip, IP_PROTO_TCP, p->tot_len); +#endif + TCP_STATS_INC(tcp.xmit); + + /* Send output to IP */ + ip_output(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP); + + pbuf_free(p); + + LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n", pcb->snd_nxt - 1, pcb->rcv_nxt)); +} + +#endif /* LWIP_TCP */ + + + + + + + + + diff --git a/wii/libogc/lwip/core/udp.c b/wii/libogc/lwip/core/udp.c new file mode 100644 index 0000000000..d1e0eacacd --- /dev/null +++ b/wii/libogc/lwip/core/udp.c @@ -0,0 +1,655 @@ +/** + * @file + * User Datagram Protocol module + * + */ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + + +/* udp.c + * + * The code for the User Datagram Protocol UDP. + * + */ + +#include + +#include "lwip/opt.h" + +#include "lwip/def.h" +#include "lwip/memp.h" +#include "lwip/inet.h" +#include "lwip/ip_addr.h" +#include "lwip/netif.h" +#include "lwip/udp.h" +#include "lwip/icmp.h" + +#include "lwip/stats.h" + +#include "arch/perf.h" +#include "lwip/snmp.h" + +/* The list of UDP PCBs */ +#if LWIP_UDP +/* was static, but we may want to access this from a socket layer */ +struct udp_pcb *udp_pcbs = NULL; + +static struct udp_pcb *pcb_cache = NULL; + +void +udp_init(void) +{ + udp_pcbs = pcb_cache = NULL; +} + +/** + * Process an incoming UDP datagram. + * + * Given an incoming UDP datagram (as a chain of pbufs) this function + * finds a corresponding UDP PCB and + * + * @param pbuf pbuf to be demultiplexed to a UDP PCB. + * @param netif network interface on which the datagram was received. + * + */ +void +udp_input(struct pbuf *p, struct netif *inp) +{ + struct udp_hdr *udphdr; + struct udp_pcb *pcb; + struct udp_pcb *uncon_pcb; + struct ip_hdr *iphdr; + u16_t src, dest; + u8_t local_match; + + PERF_START; + + UDP_STATS_INC(udp.recv); + + iphdr = p->payload; + + if (pbuf_header(p, -((s16_t)(UDP_HLEN + IPH_HL(iphdr) * 4)))) { + /* drop short packets */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len)); + UDP_STATS_INC(udp.lenerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + + udphdr = (struct udp_hdr *)((u8_t *)p->payload - UDP_HLEN); + + LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len)); + + src = ntohs(udphdr->src); + dest = ntohs(udphdr->dest); + + udp_debug_print(udphdr); + + /* print the UDP source and destination */ + LWIP_DEBUGF(UDP_DEBUG, ("udp (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") <-- (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", + ip4_addr1(&iphdr->dest), ip4_addr2(&iphdr->dest), + ip4_addr3(&iphdr->dest), ip4_addr4(&iphdr->dest), ntohs(udphdr->dest), + ip4_addr1(&iphdr->src), ip4_addr2(&iphdr->src), + ip4_addr3(&iphdr->src), ip4_addr4(&iphdr->src), ntohs(udphdr->src))); + + local_match = 0; + uncon_pcb = NULL; + /* Iterate through the UDP pcb list for a matching pcb */ + for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + /* print the PCB local and remote address */ + LWIP_DEBUGF(UDP_DEBUG, ("pcb (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") --- (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", + ip4_addr1(&pcb->local_ip), ip4_addr2(&pcb->local_ip), + ip4_addr3(&pcb->local_ip), ip4_addr4(&pcb->local_ip), pcb->local_port, + ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), + ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip), pcb->remote_port)); + + /* compare PCB local addr+port to UDP destination addr+port */ + if ((pcb->local_port == dest) && + (ip_addr_isany(&pcb->local_ip) || + ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)))) { + local_match = 1; + if ((uncon_pcb == NULL) && + ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) { + /* the first unconnected matching PCB */ + uncon_pcb = pcb; + } + } + /* compare PCB remote addr+port to UDP source addr+port */ + if ((local_match != 0) && + (pcb->remote_port == src) && + (ip_addr_isany(&pcb->remote_ip) || + ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)))) { + /* the first fully matching PCB */ + break; + } + } + /* no fully matching pcb found? then look for an unconnected pcb */ + if (pcb == NULL) { + pcb = uncon_pcb; + } + + /* Check checksum if this is a match or if it was directed at us. */ + if (pcb != NULL || ip_addr_cmp(&inp->ip_addr, &iphdr->dest)) + { + LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE, ("udp_input: calculating checksum\n")); + pbuf_header(p, UDP_HLEN); +#ifdef IPv6 + if (iphdr->nexthdr == IP_PROTO_UDPLITE) { +#else + if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) { +#endif /* IPv4 */ + /* Do the UDP Lite checksum */ +#if CHECKSUM_CHECK_UDP + if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), + (struct ip_addr *)&(iphdr->dest), + IP_PROTO_UDPLITE, ntohs(udphdr->len)) != 0) { + LWIP_DEBUGF(UDP_DEBUG | 2, ("udp_input: UDP Lite datagram discarded due to failing checksum\n")); + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } +#endif + } else { +#if CHECKSUM_CHECK_UDP + if (udphdr->chksum != 0) { + if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), + (struct ip_addr *)&(iphdr->dest), + IP_PROTO_UDP, p->tot_len) != 0) { + LWIP_DEBUGF(UDP_DEBUG | 2, ("udp_input: UDP datagram discarded due to failing checksum\n")); + + UDP_STATS_INC(udp.chkerr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpinerrors(); + pbuf_free(p); + goto end; + } + } +#endif + } + pbuf_header(p, -UDP_HLEN); + if (pcb != NULL) { + snmp_inc_udpindatagrams(); + /* callback */ + if (pcb->recv != NULL) + { + pcb->recv(pcb->recv_arg, pcb, p, &(iphdr->src), src); + } + } else { + LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE, ("udp_input: not for us.\n")); + + /* No match was found, send ICMP destination port unreachable unless + destination address was broadcast/multicast. */ + + if (!ip_addr_isbroadcast(&iphdr->dest, inp) && + !ip_addr_ismulticast(&iphdr->dest)) { + + /* adjust pbuf pointer */ + p->payload = iphdr; + icmp_dest_unreach(p, ICMP_DUR_PORT); + } + UDP_STATS_INC(udp.proterr); + UDP_STATS_INC(udp.drop); + snmp_inc_udpnoports(); + pbuf_free(p); + } + } else { + pbuf_free(p); + } + end: + + PERF_STOP("udp_input"); +} + +/** + * Send data to a specified address using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param pbuf chain of pbuf's to be sent. + * @param dst_ip Destination IP address. + * @param dst_port Destination UDP port. + * + * If the PCB already has a remote address association, it will + * be restored after the data is sent. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_MEM. Out of memory. + * - ERR_RTE. Could not find route to destination address. + * + * @see udp_disconnect() udp_send() + */ +err_t +udp_sendto(struct udp_pcb *pcb, struct pbuf *p, + struct ip_addr *dst_ip, u16_t dst_port) +{ + err_t err; + /* temporary space for current PCB remote address */ + struct ip_addr pcb_remote_ip; + u16_t pcb_remote_port; + /* remember current remote peer address of PCB */ + pcb_remote_ip.addr = pcb->remote_ip.addr; + pcb_remote_port = pcb->remote_port; + /* copy packet destination address to PCB remote peer address */ + pcb->remote_ip.addr = dst_ip->addr; + pcb->remote_port = dst_port; + /* send to the packet destination address */ + err = udp_send(pcb, p); + /* restore PCB remote peer address */ + pcb->remote_ip.addr = pcb_remote_ip.addr; + pcb->remote_port = pcb_remote_port; + return err; +} + +/** + * Send data using UDP. + * + * @param pcb UDP PCB used to send the data. + * @param pbuf chain of pbuf's to be sent. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_MEM. Out of memory. + * - ERR_RTE. Could not find route to destination address. + * + * @see udp_disconnect() udp_sendto() + */ +err_t +udp_send(struct udp_pcb *pcb, struct pbuf *p) +{ + struct udp_hdr *udphdr; + struct netif *netif; + struct ip_addr *src_ip; + err_t err; + struct pbuf *q; /* q will be sent down the stack */ + + LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 3, ("udp_send\n")); + + /* if the PCB is not yet bound to a port, bind it here */ + if (pcb->local_port == 0) { + LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 2, ("udp_send: not yet bound to a port, binding now\n")); + err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); + if (err != ERR_OK) { + LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 2, ("udp_send: forced port bind failed\n")); + return err; + } + } + /* find the outgoing network interface for this packet */ + netif = ip_route(&(pcb->remote_ip)); + /* no outgoing network interface could be found? */ + if (netif == NULL) { + LWIP_DEBUGF(UDP_DEBUG | 1, ("udp_send: No route to 0x%"X32_F"\n", pcb->remote_ip.addr)); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } + + /* not enough space to add an UDP header to first pbuf in given p chain? */ + if (pbuf_header(p, UDP_HLEN)) { + /* allocate header in a seperate new pbuf */ + q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); + /* new header pbuf could not be allocated? */ + if (q == NULL) { + LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 2, ("udp_send: could not allocate header\n")); + return ERR_MEM; + } + /* chain header q in front of given pbuf p */ + pbuf_chain(q, p); + /* { first pbuf q points to header pbuf } */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); + /* adding a header within p succeeded */ + } else { + /* first pbuf q equals given pbuf */ + q = p; + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p)); + } + /* { q now represents the packet to be sent } */ + udphdr = q->payload; + udphdr->src = htons(pcb->local_port); + udphdr->dest = htons(pcb->remote_port); + /* in UDP, 0 checksum means 'no checksum' */ + udphdr->chksum = 0x0000; + + /* PCB local address is IP_ANY_ADDR? */ + if (ip_addr_isany(&pcb->local_ip)) { + /* use outgoing network interface IP address as source address */ + src_ip = &(netif->ip_addr); + } else { + /* use UDP PCB local IP address as source address */ + src_ip = &(pcb->local_ip); + } + + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len)); + + /* UDP Lite protocol? */ + if (pcb->flags & UDP_FLAGS_UDPLITE) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len)); + /* set UDP message length in UDP header */ + udphdr->len = htons(pcb->chksum_len); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP + udphdr->chksum = inet_chksum_pseudo(q, src_ip, &(pcb->remote_ip), + IP_PROTO_UDP, pcb->chksum_len); + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udphdr->chksum == 0x0000) udphdr->chksum = 0xffff; +#else + udphdr->chksum = 0x0000; +#endif + /* output to IP */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDPLITE,)\n")); + err = ip_output_if (q, src_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif); + /* UDP */ + } else { + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len)); + udphdr->len = htons(q->tot_len); + /* calculate checksum */ +#if CHECKSUM_GEN_UDP + if ((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { + udphdr->chksum = inet_chksum_pseudo(q, src_ip, &pcb->remote_ip, IP_PROTO_UDP, q->tot_len); + /* chksum zero must become 0xffff, as zero means 'no checksum' */ + if (udphdr->chksum == 0x0000) udphdr->chksum = 0xffff; + } +#else + udphdr->chksum = 0x0000; +#endif + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum)); + LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDP,)\n")); + /* output to IP */ + err = ip_output_if(q, src_ip, &pcb->remote_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif); + } + /* TODO: must this be increased even if error occured? */ + snmp_inc_udpoutdatagrams(); + + /* did we chain a seperate header pbuf earlier? */ + if (q != p) { + /* free the header pbuf */ + pbuf_free(q); q = NULL; + /* { p is still referenced by the caller, and will live on } */ + } + + UDP_STATS_INC(udp.xmit); + return err; +} + +/** + * Bind an UDP PCB. + * + * @param pcb UDP PCB to be bound with a local address ipaddr and port. + * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to + * bind to all local interfaces. + * @param port local UDP port to bind with. + * + * @return lwIP error code. + * - ERR_OK. Successful. No error occured. + * - ERR_USE. The specified ipaddr and port are already bound to by + * another UDP PCB. + * + * @see udp_disconnect() + */ +err_t +udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + u8_t rebind; + + LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 3, ("udp_bind(ipaddr = ")); + ip_addr_debug_print(UDP_DEBUG, ipaddr); + LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | 3, (", port = %"U16_F")\n", port)); + + rebind = 0; + /* Check for double bind and rebind of the same pcb */ + for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + /* is this UDP PCB already on active list? */ + if (pcb == ipcb) { + /* pcb may occur at most once in active list */ + LWIP_ASSERT("rebind == 0", rebind == 0); + /* pcb already in list, just rebind */ + rebind = 1; + } + +/* this code does not allow upper layer to share a UDP port for + listening to broadcast or multicast traffic (See SO_REUSE_ADDR and + SO_REUSE_PORT under *BSD). TODO: See where it fits instead, OR + combine with implementation of UDP PCB flags. Leon Woestenberg. */ +#ifdef LWIP_UDP_TODO + /* port matches that of PCB in list? */ + else if ((ipcb->local_port == port) && + /* IP address matches, or one is IP_ADDR_ANY? */ + (ip_addr_isany(&(ipcb->local_ip)) || + ip_addr_isany(ipaddr) || + ip_addr_cmp(&(ipcb->local_ip), ipaddr))) { + /* other PCB already binds to this local IP and port */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: local port %"U16_F" already bound by another pcb\n", port)); + return ERR_USE; + } +#endif + + } + + ip_addr_set(&pcb->local_ip, ipaddr); + /* no port specified? */ + if (port == 0) { +#ifndef UDP_LOCAL_PORT_RANGE_START +#define UDP_LOCAL_PORT_RANGE_START 4096 +#define UDP_LOCAL_PORT_RANGE_END 0x7fff +#endif + port = UDP_LOCAL_PORT_RANGE_START; + ipcb = udp_pcbs; + while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) { + if (ipcb->local_port == port) { + port++; + ipcb = udp_pcbs; + } else + ipcb = ipcb->next; + } + if (ipcb != NULL) { + /* no more ports available in local range */ + LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n")); + return ERR_USE; + } + } + pcb->local_port = port; + /* pcb not active yet? */ + if (rebind == 0) { + /* place the PCB on the active list if not already there */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + } + LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | DBG_STATE, ("udp_bind: bound to %"U16_F".%"U16_F".%"U16_F".%"U16_F", port %"U16_F"\n", + (u16_t)(ntohl(pcb->local_ip.addr) >> 24 & 0xff), + (u16_t)(ntohl(pcb->local_ip.addr) >> 16 & 0xff), + (u16_t)(ntohl(pcb->local_ip.addr) >> 8 & 0xff), + (u16_t)(ntohl(pcb->local_ip.addr) & 0xff), pcb->local_port)); + return ERR_OK; +} +/** + * Connect an UDP PCB. + * + * This will associate the UDP PCB with the remote address. + * + * @param pcb UDP PCB to be connected with remote address ipaddr and port. + * @param ipaddr remote IP address to connect with. + * @param port remote UDP port to connect with. + * + * @return lwIP error code + * + * @see udp_disconnect() + */ +err_t +udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) +{ + struct udp_pcb *ipcb; + + if (pcb->local_port == 0) { + err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); + if (err != ERR_OK) + return err; + } + + ip_addr_set(&pcb->remote_ip, ipaddr); + pcb->remote_port = port; + pcb->flags |= UDP_FLAGS_CONNECTED; +/** TODO: this functionality belongs in upper layers */ +#ifdef LWIP_UDP_TODO + /* Nail down local IP for netconn_addr()/getsockname() */ + if (ip_addr_isany(&pcb->local_ip) && !ip_addr_isany(&pcb->remote_ip)) { + struct netif *netif; + + if ((netif = ip_route(&(pcb->remote_ip))) == NULL) { + LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", pcb->remote_ip.addr)); + UDP_STATS_INC(udp.rterr); + return ERR_RTE; + } + /** TODO: this will bind the udp pcb locally, to the interface which + is used to route output packets to the remote address. However, we + might want to accept incoming packets on any interface! */ + pcb->local_ip = netif->ip_addr; + } else if (ip_addr_isany(&pcb->remote_ip)) { + pcb->local_ip.addr = 0; + } +#endif + LWIP_DEBUGF(UDP_DEBUG | DBG_TRACE | DBG_STATE, ("udp_connect: connected to %"U16_F".%"U16_F".%"U16_F".%"U16_F",port %"U16_F"\n", + (u16_t)(ntohl(pcb->remote_ip.addr) >> 24 & 0xff), + (u16_t)(ntohl(pcb->remote_ip.addr) >> 16 & 0xff), + (u16_t)(ntohl(pcb->remote_ip.addr) >> 8 & 0xff), + (u16_t)(ntohl(pcb->remote_ip.addr) & 0xff), pcb->remote_port)); + + /* Insert UDP PCB into the list of active UDP PCBs. */ + for(ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { + if (pcb == ipcb) { + /* already on the list, just return */ + return ERR_OK; + } + } + /* PCB not yet on the list, add PCB now */ + pcb->next = udp_pcbs; + udp_pcbs = pcb; + return ERR_OK; +} + +void +udp_disconnect(struct udp_pcb *pcb) +{ + /* reset remote address association */ + ip_addr_set(&pcb->remote_ip, IP_ADDR_ANY); + pcb->remote_port = 0; + /* mark PCB as unconnected */ + pcb->flags &= ~UDP_FLAGS_CONNECTED; +} + +void +udp_recv(struct udp_pcb *pcb, + void (* recv)(void *arg, struct udp_pcb *upcb, struct pbuf *p, + struct ip_addr *addr, u16_t port), + void *recv_arg) +{ + /* remember recv() callback and user data */ + pcb->recv = recv; + pcb->recv_arg = recv_arg; +} +/** + * Remove an UDP PCB. + * + * @param pcb UDP PCB to be removed. The PCB is removed from the list of + * UDP PCB's and the data structure is freed from memory. + * + * @see udp_new() + */ +void +udp_remove(struct udp_pcb *pcb) +{ + struct udp_pcb *pcb2; + /* pcb to be removed is first in list? */ + if (udp_pcbs == pcb) { + /* make list start at 2nd pcb */ + udp_pcbs = udp_pcbs->next; + /* pcb not 1st in list */ + } else for(pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { + /* find pcb in udp_pcbs list */ + if (pcb2->next != NULL && pcb2->next == pcb) { + /* remove pcb from list */ + pcb2->next = pcb->next; + } + } + memp_free(MEMP_UDP_PCB, pcb); +} +/** + * Create a UDP PCB. + * + * @return The UDP PCB which was created. NULL if the PCB data structure + * could not be allocated. + * + * @see udp_remove() + */ +struct udp_pcb * +udp_new(void) { + struct udp_pcb *pcb; + pcb = memp_malloc(MEMP_UDP_PCB); + /* could allocate UDP PCB? */ + if (pcb != NULL) { + /* initialize PCB to all zeroes */ + memset(pcb, 0, sizeof(struct udp_pcb)); + pcb->ttl = UDP_TTL; + } + + + return pcb; +} + +#if UDP_DEBUG +void +udp_debug_print(struct udp_hdr *udphdr) +{ + LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n")); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", + ntohs(udphdr->src), ntohs(udphdr->dest))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); + LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | 0x%04"X16_F" | (len, chksum)\n", + ntohs(udphdr->len), ntohs(udphdr->chksum))); + LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); +} +#endif /* UDP_DEBUG */ + +#endif /* LWIP_UDP */ + + + + + + + + + diff --git a/wii/libogc/lwip/netif/etharp.c b/wii/libogc/lwip/netif/etharp.c new file mode 100644 index 0000000000..4d4ebdbe15 --- /dev/null +++ b/wii/libogc/lwip/netif/etharp.c @@ -0,0 +1,831 @@ +/** + * @file + * Address Resolution Protocol module for IP over Ethernet + * + * Functionally, ARP is divided into two parts. The first maps an IP address + * to a physical address when sending a packet, and the second part answers + * requests from other machines for our physical address. + * + * This implementation complies with RFC 826 (Ethernet ARP). It supports + * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6 + * if an interface calls etharp_query(our_netif, its_ip_addr, NULL) upon + * address change. + */ + +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * Copyright (c) 2003-2004 Leon Woestenberg + * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + */ + +#include "lwip/opt.h" +#include "lwip/inet.h" +#include "netif/etharp.h" +#include "lwip/ip.h" +#include "lwip/stats.h" + +/* ARP needs to inform DHCP of any ARP replies? */ +#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) +# include "lwip/dhcp.h" +#endif + +/** the time an ARP entry stays valid after its last update, + * (240 * 5) seconds = 20 minutes. + */ +#define ARP_MAXAGE 240 +/** the time an ARP entry stays pending after first request, + * (2 * 5) seconds = 10 seconds. + * + * @internal Keep this number at least 2, otherwise it might + * run out instantly if the timeout occurs directly after a request. + */ +#define ARP_MAXPENDING 2 + +#define HWTYPE_ETHERNET 1 + +/** ARP message types */ +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +#define ARPH_HWLEN(hdr) (ntohs((hdr)->_hwlen_protolen) >> 8) +#define ARPH_PROTOLEN(hdr) (ntohs((hdr)->_hwlen_protolen) & 0xff) + +#define ARPH_HWLEN_SET(hdr, len) (hdr)->_hwlen_protolen = htons(ARPH_PROTOLEN(hdr) | ((len) << 8)) +#define ARPH_PROTOLEN_SET(hdr, len) (hdr)->_hwlen_protolen = htons((len) | (ARPH_HWLEN(hdr) << 8)) + +enum etharp_state { + ETHARP_STATE_EMPTY, + ETHARP_STATE_PENDING, + ETHARP_STATE_STABLE, + /** @internal transitional state used in etharp_tmr() for convenience*/ + ETHARP_STATE_EXPIRED +}; + +struct etharp_entry { +#if ARP_QUEUEING + /** + * Pointer to queue of pending outgoing packets on this ARP entry. + */ + struct pbuf *p; +#endif + struct ip_addr ipaddr; + struct eth_addr ethaddr; + enum etharp_state state; + u8_t ctime; +}; + +static const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; +static struct etharp_entry arp_table[ARP_TABLE_SIZE]; + +/** + * Try hard to create a new entry - we want the IP address to appear in + * the cache (even if this means removing an active entry or so). */ +#define ETHARP_TRY_HARD 1 + +static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags); +static err_t update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags); +/** + * Initializes ARP module. + */ +void +etharp_init(void) +{ + u8_t i; + /* clear ARP entries */ + for(i = 0; i < ARP_TABLE_SIZE; ++i) { + arp_table[i].state = ETHARP_STATE_EMPTY; +#if ARP_QUEUEING + arp_table[i].p = NULL; +#endif + arp_table[i].ctime = 0; + } +} + +/** + * Clears expired entries in the ARP table. + * + * This function should be called every ETHARP_TMR_INTERVAL microseconds (5 seconds), + * in order to expire entries in the ARP table. + */ +void +etharp_tmr(void) +{ + u8_t i; + + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n")); + /* remove expired entries from the ARP table */ + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + arp_table[i].ctime++; + /* stable entry? */ + if ((arp_table[i].state == ETHARP_STATE_STABLE) && + /* entry has become old? */ + (arp_table[i].ctime >= ARP_MAXAGE)) { + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired stable entry %"U16_F".\n", (u16_t)i)); + arp_table[i].state = ETHARP_STATE_EXPIRED; + /* pending entry? */ + } else if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* entry unresolved/pending for too long? */ + if (arp_table[i].ctime >= ARP_MAXPENDING) { + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired pending entry %"U16_F".\n", (u16_t)i)); + arp_table[i].state = ETHARP_STATE_EXPIRED; +#if ARP_QUEUEING + } else if (arp_table[i].p != NULL) { + /* resend an ARP query here */ +#endif + } + } + /* clean up entries that have just been expired */ + if (arp_table[i].state == ETHARP_STATE_EXPIRED) { +#if ARP_QUEUEING + /* and empty packet queue */ + if (arp_table[i].p != NULL) { + /* remove all queued packets */ + LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].p))); + pbuf_free(arp_table[i].p); + arp_table[i].p = NULL; + } +#endif + /* recycle entry for re-use */ + arp_table[i].state = ETHARP_STATE_EMPTY; + } + } +} + +/** + * Search the ARP table for a matching or new entry. + * + * If an IP address is given, return a pending or stable ARP entry that matches + * the address. If no match is found, create a new entry with this address set, + * but in state ETHARP_EMPTY. The caller must check and possibly change the + * state of the returned entry. + * + * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY. + * + * In all cases, attempt to create new entries from an empty entry. If no + * empty entries are available and ETHARP_TRY_HARD flag is set, recycle + * old entries. Heuristic choose the least important entry for recycling. + * + * @param ipaddr IP address to find in ARP cache, or to add if not found. + * @param flags + * - ETHARP_TRY_HARD: Try hard to create a entry by allowing recycling of + * active (stable or pending) entries. + * + * @return The ARP entry index that matched or is created, ERR_MEM if no + * entry is found or could be recycled. + */ +static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags) +{ + s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE; + s8_t empty = ARP_TABLE_SIZE; + u8_t i = 0, age_pending = 0, age_stable = 0; +#if ARP_QUEUEING + /* oldest entry with packets on queue */ + s8_t old_queue = ARP_TABLE_SIZE; + /* its age */ + u8_t age_queue = 0; +#endif + + /** + * a) do a search through the cache, remember candidates + * b) select candidate entry + * c) create new entry + */ + + /* a) in a single search sweep, do all of this + * 1) remember the first empty entry (if any) + * 2) remember the oldest stable entry (if any) + * 3) remember the oldest pending entry without queued packets (if any) + * 4) remember the oldest pending entry with queued packets (if any) + * 5) search for a matching IP entry, either pending or stable + * until 5 matches, or all entries are searched for. + */ + + for (i = 0; i < ARP_TABLE_SIZE; ++i) { + /* no empty entry found yet and now we do find one? */ + if ((empty == ARP_TABLE_SIZE) && (arp_table[i].state == ETHARP_STATE_EMPTY)) { + LWIP_DEBUGF(ETHARP_DEBUG, ("find_entry: found empty entry %"U16_F"\n", (u16_t)i)); + /* remember first empty entry */ + empty = i; + } + /* pending entry? */ + else if (arp_table[i].state == ETHARP_STATE_PENDING) { + /* if given, does IP address match IP address in ARP entry? */ + if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: found matching pending entry %"U16_F"\n", (u16_t)i)); + /* found exact IP address match, simply bail out */ + return i; +#if ARP_QUEUEING + /* pending with queued packets? */ + } else if (arp_table[i].p != NULL) { + if (arp_table[i].ctime >= age_queue) { + old_queue = i; + age_queue = arp_table[i].ctime; + } +#endif + /* pending without queued packets? */ + } else { + if (arp_table[i].ctime >= age_pending) { + old_pending = i; + age_pending = arp_table[i].ctime; + } + } + } + /* stable entry? */ + else if (arp_table[i].state == ETHARP_STATE_STABLE) { + /* if given, does IP address match IP address in ARP entry? */ + if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: found matching stable entry %"U16_F"\n", (u16_t)i)); + /* found exact IP address match, simply bail out */ + return i; + /* remember entry with oldest stable entry in oldest, its age in maxtime */ + } else if (arp_table[i].ctime >= age_stable) { + old_stable = i; + age_stable = arp_table[i].ctime; + } + } + } + /* { we have no match } => try to create a new entry */ + + /* no empty entry found and not allowed to recycle? */ + if ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_TRY_HARD) == 0)) + { + return (s8_t)ERR_MEM; + } + + /* b) choose the least destructive entry to recycle: + * 1) empty entry + * 2) oldest stable entry + * 3) oldest pending entry without queued packets + * 4) oldest pending entry without queued packets + * + * { ETHARP_TRY_HARD is set at this point } + */ + + /* 1) empty entry available? */ + if (empty < ARP_TABLE_SIZE) { + i = empty; + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting empty entry %"U16_F"\n", (u16_t)i)); + } + /* 2) found recyclable stable entry? */ + else if (old_stable < ARP_TABLE_SIZE) { + /* recycle oldest stable*/ + i = old_stable; + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i)); +#if ARP_QUEUEING + /* no queued packets should exist on stable entries */ + LWIP_ASSERT("arp_table[i].p == NULL", arp_table[i].p == NULL); +#endif + /* 3) found recyclable pending entry without queued packets? */ + } else if (old_pending < ARP_TABLE_SIZE) { + /* recycle oldest pending */ + i = old_pending; + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i)); +#if ARP_QUEUEING + /* 4) found recyclable pending entry with queued packets? */ + } else if (old_queue < ARP_TABLE_SIZE) { + /* recycle oldest pending */ + i = old_queue; + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].p))); + pbuf_free(arp_table[i].p); + arp_table[i].p = NULL; +#endif + /* no empty or recyclable entries found */ + } else { + return (s8_t)ERR_MEM; + } + + /* { empty or recyclable entry found } */ + LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE); + + /* recycle entry (no-op for an already empty entry) */ + arp_table[i].state = ETHARP_STATE_EMPTY; + + /* IP address given? */ + if (ipaddr != NULL) { + /* set IP address */ + ip_addr_set(&arp_table[i].ipaddr, ipaddr); + } + arp_table[i].ctime = 0; + return (err_t)i; +} + +/** + * Update (or insert) a IP/MAC address pair in the ARP cache. + * + * If a pending entry is resolved, any queued packets will be sent + * at this point. + * + * @param ipaddr IP address of the inserted ARP entry. + * @param ethaddr Ethernet address of the inserted ARP entry. + * @param flags Defines behaviour: + * - ETHARP_TRY_HARD Allows ARP to insert this as a new item. If not specified, + * only existing ARP entries will be updated. + * + * @return + * - ERR_OK Succesfully updated ARP cache. + * - ERR_MEM If we could not add a new ARP entry when ETHARP_TRY_HARD was set. + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + * @see pbuf_free() + */ +static err_t +update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags) +{ + s8_t i, k; + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 3, ("update_arp_entry()\n")); + LWIP_ASSERT("netif->hwaddr_len != 0", netif->hwaddr_len != 0); + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", + ip4_addr1(ipaddr), ip4_addr2(ipaddr), ip4_addr3(ipaddr), ip4_addr4(ipaddr), + ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], + ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); + /* non-unicast address? */ + if (ip_addr_isany(ipaddr) || + ip_addr_isbroadcast(ipaddr, netif) || + ip_addr_ismulticast(ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + /* find or create ARP entry */ + i = find_entry(ipaddr, flags); + /* bail out if no entry could be found */ + if (i < 0) return (err_t)i; + + /* mark it stable */ + arp_table[i].state = ETHARP_STATE_STABLE; + + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i)); + /* update address */ + for (k = 0; k < netif->hwaddr_len; ++k) { + arp_table[i].ethaddr.addr[k] = ethaddr->addr[k]; + } + /* reset time stamp */ + arp_table[i].ctime = 0; +/* this is where we will send out queued packets! */ +#if ARP_QUEUEING + while (arp_table[i].p != NULL) { + /* get the first packet on the queue */ + struct pbuf *p = arp_table[i].p; + /* Ethernet header */ + struct eth_hdr *ethhdr = p->payload; + /* remember (and reference) remainder of queue */ + /* note: this will also terminate the p pbuf chain */ + arp_table[i].p = pbuf_dequeue(p); + /* fill-in Ethernet header */ + for (k = 0; k < netif->hwaddr_len; ++k) { + ethhdr->dest.addr[k] = ethaddr->addr[k]; + ethhdr->src.addr[k] = netif->hwaddr[k]; + } + ethhdr->type = htons(ETHTYPE_IP); + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("update_arp_entry: sending queued IP packet %p.\n", (void *)p)); + /* send the queued IP packet */ + netif->linkoutput(netif, p); + /* free the queued IP packet */ + pbuf_free(p); + } +#endif + return ERR_OK; +} + +/** + * Updates the ARP table using the given IP packet. + * + * Uses the incoming IP packet's source address to update the + * ARP cache for the local network. The function does not alter + * or free the packet. This function must be called before the + * packet p is passed to the IP layer. + * + * @param netif The lwIP network interface on which the IP packet pbuf arrived. + * @param pbuf The IP packet that arrived on netif. + * + * @return NULL + * + * @see pbuf_free() + */ +void +etharp_ip_input(struct netif *netif, struct pbuf *p) +{ + struct ethip_hdr *hdr; + LWIP_ASSERT("netif != NULL", netif != NULL); + /* Only insert an entry if the source IP address of the + incoming IP packet comes from a host on the local network. */ + hdr = p->payload; + /* source is not on the local network? */ + if (!ip_addr_netcmp(&(hdr->ip.src), &(netif->ip_addr), &(netif->netmask))) { + /* do nothing */ + return; + } + + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n")); + /* update ARP table */ + /* @todo We could use ETHARP_TRY_HARD if we think we are going to talk + * back soon (for example, if the destination IP address is ours. */ + update_arp_entry(netif, &(hdr->ip.src), &(hdr->eth.src), 0); +} + + +/** + * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache + * send out queued IP packets. Updates cache with snooped address pairs. + * + * Should be called for incoming ARP packets. The pbuf in the argument + * is freed by this function. + * + * @param netif The lwIP network interface on which the ARP packet pbuf arrived. + * @param pbuf The ARP packet that arrived on netif. Is freed by this function. + * @param ethaddr Ethernet address of netif. + * + * @return NULL + * + * @see pbuf_free() + */ +void +etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p) +{ + struct etharp_hdr *hdr; + /* these are aligned properly, whereas the ARP header fields might not be */ + struct ip_addr sipaddr, dipaddr; + u8_t i; + u8_t for_us; + + LWIP_ASSERT("netif != NULL", netif != NULL); + + /* drop short ARP packets */ + if (p->tot_len < sizeof(struct etharp_hdr)) { + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 1, ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len, sizeof(struct etharp_hdr))); + pbuf_free(p); + return; + } + + hdr = p->payload; + + /* get aligned copies of addresses */ + *(struct ip_addr2 *)((void*)&sipaddr) = hdr->sipaddr; + *(struct ip_addr2 *)((void*)&dipaddr) = hdr->dipaddr; + + /* this interface is not configured? */ + if (netif->ip_addr.addr == 0) { + for_us = 0; + } else { + /* ARP packet directed to us? */ + for_us = ip_addr_cmp(&dipaddr, &(netif->ip_addr)); + } + + /* ARP message directed to us? */ + if (for_us) { + /* add IP address in ARP cache; assume requester wants to talk to us. + * can result in directly sending the queued packets for this host. */ + update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), ETHARP_TRY_HARD); + /* ARP message not directed to us? */ + } else { + /* update the source IP address in the cache, if present */ + update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), 0); + } + + /* now act on the message itself */ + switch (htons(hdr->opcode)) { + /* ARP request? */ + case ARP_REQUEST: + /* ARP request. If it asked for our address, we send out a + * reply. In any case, we time-stamp any existing ARP entry, + * and possiby send out an IP packet that was queued on it. */ + + LWIP_DEBUGF (ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP request\n")); + /* ARP request for our address? */ + if (for_us) { + + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n")); + /* re-use pbuf to send ARP reply */ + hdr->opcode = htons(ARP_REPLY); + + hdr->dipaddr = hdr->sipaddr; + hdr->sipaddr = *(struct ip_addr2 *)((void*)&netif->ip_addr); + + for(i = 0; i < netif->hwaddr_len; ++i) { + hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i]; + hdr->shwaddr.addr[i] = ethaddr->addr[i]; + hdr->ethhdr.dest.addr[i] = hdr->dhwaddr.addr[i]; + hdr->ethhdr.src.addr[i] = ethaddr->addr[i]; + } + + hdr->hwtype = htons(HWTYPE_ETHERNET); + ARPH_HWLEN_SET(hdr, netif->hwaddr_len); + + hdr->proto = htons(ETHTYPE_IP); + ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr)); + + hdr->ethhdr.type = htons(ETHTYPE_ARP); + /* return ARP reply */ + netif->linkoutput(netif, p); + /* we are not configured? */ + } else if (netif->ip_addr.addr == 0) { + /* { for_us == 0 and netif->ip_addr.addr == 0 } */ + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n")); + /* request was not directed to us */ + } else { + /* { for_us == 0 and netif->ip_addr.addr != 0 } */ + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n")); + } + break; + case ARP_REPLY: + /* ARP reply. We already updated the ARP cache earlier. */ + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n")); +#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) + /* DHCP wants to know about ARP replies from any host with an + * IP address also offered to us by the DHCP server. We do not + * want to take a duplicate IP address on a single network. + * @todo How should we handle redundant (fail-over) interfaces? + * */ + dhcp_arp_reply(netif, &sipaddr); +#endif + break; + default: + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode))); + break; + } + /* free ARP packet */ + pbuf_free(p); +} + +/** + * Resolve and fill-in Ethernet address header for outgoing packet. + * + * For IP multicast and broadcast, corresponding Ethernet addresses + * are selected and the packet is transmitted on the link. + * + * For unicast addresses, the packet is submitted to etharp_query(). In + * case the IP address is outside the local network, the IP address of + * the gateway is used. + * + * @param netif The lwIP network interface which the IP packet will be sent on. + * @param ipaddr The IP address of the packet destination. + * @param pbuf The pbuf(s) containing the IP packet to be sent. + * + * @return + * - ERR_RTE No route to destination (no gateway to external networks), + * or the return type of either etharp_query() or netif->linkoutput(). + */ +err_t +etharp_output(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q) +{ + struct eth_addr *dest, *srcaddr, mcastaddr; + struct eth_hdr *ethhdr; + u8_t i; + + /* make room for Ethernet header - should not fail */ + if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) { + /* bail out */ + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 2, ("etharp_output: could not allocate room for header.\n")); + LINK_STATS_INC(link.lenerr); + return ERR_BUF; + } + + /* assume unresolved Ethernet address */ + dest = NULL; + /* Determine on destination hardware address. Broadcasts and multicasts + * are special, other IP addresses are looked up in the ARP table. */ + + /* broadcast destination IP address? */ + if (ip_addr_isbroadcast(ipaddr, netif)) { + /* broadcast on Ethernet also */ + dest = (struct eth_addr *)ðbroadcast; + /* multicast destination IP address? */ + } else if (ip_addr_ismulticast(ipaddr)) { + /* Hash IP multicast address to MAC address.*/ + mcastaddr.addr[0] = 0x01; + mcastaddr.addr[1] = 0x00; + mcastaddr.addr[2] = 0x5e; + mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; + mcastaddr.addr[4] = ip4_addr3(ipaddr); + mcastaddr.addr[5] = ip4_addr4(ipaddr); + /* destination Ethernet address is multicast */ + dest = &mcastaddr; + /* unicast destination IP address? */ + } else { + /* outside local network? */ + if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask))) { + /* interface has default gateway? */ + if (netif->gw.addr != 0) { + /* send to hardware address of default gateway IP address */ + ipaddr = &(netif->gw); + /* no default gateway available */ + } else { + /* no route to destination error (default gateway missing) */ + return ERR_RTE; + } + } + /* queue on destination Ethernet address belonging to ipaddr */ + return etharp_query(netif, ipaddr, q); + } + + /* continuation for multicast/broadcast destinations */ + /* obtain source Ethernet address of the given interface */ + srcaddr = (struct eth_addr *)netif->hwaddr; + ethhdr = q->payload; + for (i = 0; i < netif->hwaddr_len; i++) { + ethhdr->dest.addr[i] = dest->addr[i]; + ethhdr->src.addr[i] = srcaddr->addr[i]; + } + ethhdr->type = htons(ETHTYPE_IP); + /* send packet directly on the link */ + return netif->linkoutput(netif, q); +} + +/** + * Send an ARP request for the given IP address and/or queue a packet. + * + * If the IP address was not yet in the cache, a pending ARP cache entry + * is added and an ARP request is sent for the given address. The packet + * is queued on this entry. + * + * If the IP address was already pending in the cache, a new ARP request + * is sent for the given address. The packet is queued on this entry. + * + * If the IP address was already stable in the cache, and a packet is + * given, it is directly sent and no ARP request is sent out. + * + * If the IP address was already stable in the cache, and no packet is + * given, an ARP request is sent out. + * + * @param netif The lwIP network interface on which ipaddr + * must be queried for. + * @param ipaddr The IP address to be resolved. + * @param q If non-NULL, a pbuf that must be delivered to the IP address. + * q is not freed by this function. + * + * @return + * - ERR_BUF Could not make room for Ethernet header. + * - ERR_MEM Hardware address unknown, and no more ARP entries available + * to query for address or queue the packet. + * - ERR_MEM Could not queue packet due to memory shortage. + * - ERR_RTE No route to destination (no gateway to external networks). + * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. + * + */ +err_t etharp_query(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q) +{ + struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr; + err_t result = ERR_MEM; + s8_t i; /* ARP entry index */ + u8_t k; /* Ethernet address octet index */ + + /* non-unicast address? */ + if (ip_addr_isbroadcast(ipaddr, netif) || + ip_addr_ismulticast(ipaddr) || + ip_addr_isany(ipaddr)) { + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n")); + return ERR_ARG; + } + + /* find entry in ARP cache, ask to create entry if queueing packet */ + i = find_entry(ipaddr, ETHARP_TRY_HARD); + + /* could not find or create entry? */ + if (i < 0) + { + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: could not create ARP entry\n")); + if (q) LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: packet dropped\n")); + return (err_t)i; + } + + /* mark a fresh entry as pending (we just sent a request) */ + if (arp_table[i].state == ETHARP_STATE_EMPTY) { + arp_table[i].state = ETHARP_STATE_PENDING; + } + + /* { i is either a STABLE or (new or existing) PENDING entry } */ + LWIP_ASSERT("arp_table[i].state == PENDING or STABLE", + ((arp_table[i].state == ETHARP_STATE_PENDING) || + (arp_table[i].state == ETHARP_STATE_STABLE))); + + /* do we have a pending entry? or an implicit query request? */ + if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) { + /* try to resolve it; send out ARP request */ + result = etharp_request(netif, ipaddr); + } + + /* packet given? */ + if (q != NULL) { + /* stable entry? */ + if (arp_table[i].state == ETHARP_STATE_STABLE) { + /* we have a valid IP->Ethernet address mapping, + * fill in the Ethernet header for the outgoing packet */ + struct eth_hdr *ethhdr = q->payload; + for(k = 0; k < netif->hwaddr_len; k++) { + ethhdr->dest.addr[k] = arp_table[i].ethaddr.addr[k]; + ethhdr->src.addr[k] = srcaddr->addr[k]; + } + ethhdr->type = htons(ETHTYPE_IP); + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: sending packet %p\n", (void *)q)); + /* send the packet */ + result = netif->linkoutput(netif, q); + /* pending entry? (either just created or already pending */ + } else if (arp_table[i].state == ETHARP_STATE_PENDING) { +#if ARP_QUEUEING /* queue the given q packet */ + struct pbuf *p; + /* copy any PBUF_REF referenced payloads into PBUF_RAM */ + /* (the caller of lwIP assumes the referenced payload can be + * freed after it returns from the lwIP call that brought us here) */ + p = pbuf_take(q); + /* packet could be taken over? */ + if (p != NULL) { + /* queue packet ... */ + if (arp_table[i].p == NULL) { + /* ... in the empty queue */ + pbuf_ref(p); + arp_table[i].p = p; +#if 0 /* multi-packet-queueing disabled, see bug #11400 */ + } else { + /* ... at tail of non-empty queue */ + pbuf_queue(arp_table[i].p, p); +#endif + } + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i)); + result = ERR_OK; + } else { + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q)); + /* { result == ERR_MEM } through initialization */ + } +#else /* ARP_QUEUEING == 0 */ + /* q && state == PENDING && ARP_QUEUEING == 0 => result = ERR_MEM */ + /* { result == ERR_MEM } through initialization */ + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_query: Ethernet destination address unknown, queueing disabled, packet %p dropped\n", (void *)q)); +#endif + } + } + return result; +} + +err_t etharp_request(struct netif *netif, struct ip_addr *ipaddr) +{ + struct pbuf *p; + struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr; + err_t result = ERR_OK; + u8_t k; /* ARP entry index */ + + /* allocate a pbuf for the outgoing ARP request packet */ + p = pbuf_alloc(PBUF_LINK, sizeof(struct etharp_hdr), PBUF_RAM); + /* could allocate a pbuf for an ARP request? */ + if (p != NULL) { + struct etharp_hdr *hdr = p->payload; + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE, ("etharp_request: sending ARP request.\n")); + hdr->opcode = htons(ARP_REQUEST); + for (k = 0; k < netif->hwaddr_len; k++) + { + hdr->shwaddr.addr[k] = srcaddr->addr[k]; + /* the hardware address is what we ask for, in + * a request it is a don't-care value, we use zeroes */ + hdr->dhwaddr.addr[k] = 0x00; + } + hdr->dipaddr = *(struct ip_addr2 *)ipaddr; + hdr->sipaddr = *(struct ip_addr2 *)((void*)&netif->ip_addr); + + hdr->hwtype = htons(HWTYPE_ETHERNET); + ARPH_HWLEN_SET(hdr, netif->hwaddr_len); + + hdr->proto = htons(ETHTYPE_IP); + ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr)); + for (k = 0; k < netif->hwaddr_len; ++k) + { + /* broadcast to all network interfaces on the local network */ + hdr->ethhdr.dest.addr[k] = 0xff; + hdr->ethhdr.src.addr[k] = srcaddr->addr[k]; + } + hdr->ethhdr.type = htons(ETHTYPE_ARP); + /* send ARP query */ + result = netif->linkoutput(netif, p); + /* free ARP query packet */ + pbuf_free(p); + p = NULL; + /* could not allocate pbuf for ARP request */ + } else { + result = ERR_MEM; + LWIP_DEBUGF(ETHARP_DEBUG | DBG_TRACE | 2, ("etharp_request: could not allocate pbuf for ARP request.\n")); + } + return result; +} diff --git a/wii/libogc/lwip/netif/loopif.c b/wii/libogc/lwip/netif/loopif.c new file mode 100644 index 0000000000..d37e3e8ce9 --- /dev/null +++ b/wii/libogc/lwip/netif/loopif.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#include +#include "lwp_watchdog.h" +#include "lwip/opt.h" + +#if LWIP_HAVE_LOOPIF + +#include "netif/loopif.h" +#include "lwip/mem.h" + +#if defined(LWIP_DEBUG) && defined(LWIP_TCPDUMP) +#include "netif/tcpdump.h" +#endif /* LWIP_DEBUG && LWIP_TCPDUMP */ + +#include "lwip/tcp.h" +#include "lwip/ip.h" + +#include "lwp_watchdog.h" + +#define LOOP_TIMER_ID 0x00070045 + +static u64 loopif_ticks; +static wd_cntrl loopif_tmr_cntrl; + +extern unsigned int timespec_to_interval(const struct timespec *); + +static void +loopif_input( void * arg ) +{ + struct netif *netif = (struct netif*)(((void**)arg)[0]); + struct pbuf *r = (struct pbuf*)(((void**)arg)[1]); + + mem_free(arg); + netif->input(r,netif); +} + +static err_t +loopif_output(struct netif *netif, struct pbuf *p, + struct ip_addr *ipaddr) +{ + struct pbuf *q, *r; + char *ptr; + void **arg; + +#if defined(LWIP_DEBUG) && defined(LWIP_TCPDUMP) + tcpdump(p); +#endif /* LWIP_DEBUG && LWIP_TCPDUMP */ + + r = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); + if (r != NULL) { + ptr = r->payload; + + for(q = p; q != NULL; q = q->next) { + memcpy(ptr, q->payload, q->len); + ptr += q->len; + } + + arg = mem_malloc( sizeof( void *[2])); + if( NULL == arg ) { + return ERR_MEM; + } + + arg[0] = netif; + arg[1] = r; + /** + * workaround (patch #1779) to try to prevent bug #2595: + * When connecting to "localhost" with the loopif interface, + * tcp_output doesn't get the opportunity to finnish sending the + * segment before tcp_process gets it, resulting in tcp_process + * referencing pcb->unacked-> which still is NULL. + * + * TODO: Is there still a race condition here? Leon + */ + __lwp_wd_initialize(&loopif_tmr_cntrl,loopif_input,LOOP_TIMER_ID,arg); + __lwp_wd_insert_ticks(&loopif_tmr_cntrl,loopif_ticks); + + return ERR_OK; + } + return ERR_MEM; +} + +err_t +loopif_init(struct netif *netif) +{ + struct timespec tb; + + netif->name[0] = 'l'; + netif->name[1] = 'o'; +#if 0 /** TODO: I think this should be enabled, or not? Leon */ + netif->input = loopif_input; +#endif + netif->output = loopif_output; + + tb.tv_sec = 0; + tb.tv_nsec = 10*TB_NSPERMS; + loopif_ticks = __lwp_wd_calc_ticks(&tb); + + return ERR_OK; +} + +#endif /* LWIP_HAVE_LOOPIF */ + + + + + + + diff --git a/wii/libogc/lwip/netif/skeleton/ethernetif.c b/wii/libogc/lwip/netif/skeleton/ethernetif.c new file mode 100644 index 0000000000..7cd04ece5e --- /dev/null +++ b/wii/libogc/lwip/netif/skeleton/ethernetif.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +/* + * This file is a skeleton for developing Ethernet network interface + * drivers for lwIP. Add code to the low_level functions and do a + * search-and-replace for the word "ethernetif" to replace it with + * something that better describes your network interface. + */ + +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/mem.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" + +#include "netif/etharp.h" + +/* Define those to better describe your network interface. */ +#define IFNAME0 'e' +#define IFNAME1 'n' + +struct ethernetif { + struct eth_addr *ethaddr; + /* Add whatever per-interface state that is needed here. */ +}; + +static const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; + +/* Forward declarations. */ +static void ethernetif_input(struct netif *netif); +static err_t ethernetif_output(struct netif *netif, struct pbuf *p, + struct ip_addr *ipaddr); + + +static void +low_level_init(struct netif *netif) +{ + struct ethernetif *ethernetif; + + ethernetif = netif->state; + + /* set MAC hardware address length */ + netif->hwaddr_len = 6; + + /* set MAC hardware address */ + netif->hwaddr[0] = ; + ... + netif->hwaddr[5] = ; + + /* maximum transfer unit */ + netif->mtu = 1500; + + /* broadcast capability */ + netif->flags = NETIF_FLAG_BROADCAST; + + /* Do whatever else is needed to initialize interface. */ +} + +/* + * low_level_output(): + * + * Should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + */ + + +static err_t +low_level_output(struct ethernetif *ethernetif, struct pbuf *p) +{ + struct pbuf *q; + + initiate transfer(); + + for(q = p; q != NULL; q = q->next) { + /* Send the data from the pbuf to the interface, one pbuf at a + time. The size of the data in each pbuf is kept in the ->len + variable. */ + send data from(q->payload, q->len); + } + + signal that packet should be sent(); + +#ifdef LINK_STATS + lwip_stats.link.xmit++; +#endif /* LINK_STATS */ + + return ERR_OK; +} + +/* + * low_level_input(): + * + * Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + */ + +static struct pbuf * +low_level_input(struct ethernetif *ethernetif) +{ + struct pbuf *p, *q; + u16_t len; + + /* Obtain the size of the packet and put it into the "len" + variable. */ + len = ; + + /* We allocate a pbuf chain of pbufs from the pool. */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + + if (p != NULL) { + /* We iterate over the pbuf chain until we have read the entire + packet into the pbuf. */ + for(q = p; q != NULL; q = q->next) { + /* Read enough bytes to fill this pbuf in the chain. The + available data in the pbuf is given by the q->len + variable. */ + read data into(q->payload, q->len); + } + acknowledge that packet has been read(); +#ifdef LINK_STATS + lwip_stats.link.recv++; +#endif /* LINK_STATS */ + } else { + drop packet(); +#ifdef LINK_STATS + lwip_stats.link.memerr++; + lwip_stats.link.drop++; +#endif /* LINK_STATS */ + } + + return p; +} + +/* + * ethernetif_output(): + * + * This function is called by the TCP/IP stack when an IP packet + * should be sent. It calls the function called low_level_output() to + * do the actual transmission of the packet. + * + */ + +static err_t +ethernetif_output(struct netif *netif, struct pbuf *p, + struct ip_addr *ipaddr) +{ + struct ethernetif *ethernetif; + struct pbuf *q; + struct eth_hdr *ethhdr; + struct eth_addr *dest, mcastaddr; + struct ip_addr *queryaddr; + err_t err; + u8_t i; + + ethernetif = netif->state; + + /* resolve the link destination hardware address */ + p = etharp_output(netif, ipaddr, p); + + /* network hardware address obtained? */ + if (p == NULL) + { + /* we cannot tell if the packet was sent: the packet could */ + /* have been queued on an ARP entry that was already pending. */ + return ERR_OK; + } + + /* send out the packet */ + return low_level_output(ethernetif, p); + +} + +/* + * ethernetif_input(): + * + * This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. + * + */ + +static void +ethernetif_input(struct netif *netif) +{ + struct ethernetif *ethernetif; + struct eth_hdr *ethhdr; + struct pbuf *p, *q; + + ethernetif = netif->state; + + p = low_level_input(ethernetif); + + if (p != NULL) + return; + +#ifdef LINK_STATS + lwip_stats.link.recv++; +#endif /* LINK_STATS */ + + ethhdr = p->payload; + q = NULL; + + switch (htons(ethhdr->type)) { + case ETHTYPE_IP: + q = etharp_ip_input(netif, p); + pbuf_header(p, -14); + netif->input(p, netif); + break; + + case ETHTYPE_ARP: + q = etharp_arp_input(netif, ethernetif->ethaddr, p); + break; + default: + pbuf_free(p); + p = NULL; + break; + } + if (q != NULL) { + low_level_output(ethernetif, q); + pbuf_free(q); + q = NULL; + } +} + +static void +arp_timer(void *arg) +{ + etharp_tmr(); + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} + +/* + * ethernetif_init(): + * + * Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + */ + +void +ethernetif_init(struct netif *netif) +{ + struct ethernetif *ethernetif; + + ethernetif = mem_malloc(sizeof(struct ethernetif)); + + if (ethernetif == NULL) + { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n")); + return ERR_MEM; + } + + netif->state = ethernetif; + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + netif->output = ethernetif_output; + netif->linkoutput = low_level_output; + + ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]); + + low_level_init(netif); + + etharp_init(); + + sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL); +} + diff --git a/wii/libogc/lwip/netif/skeleton/slipif.c b/wii/libogc/lwip/netif/skeleton/slipif.c new file mode 100644 index 0000000000..776c13f7a6 --- /dev/null +++ b/wii/libogc/lwip/netif/skeleton/slipif.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is built upon the file: src/arch/rtxc/netif/sioslip.c + * + * Author: Magnus Ivarsson + */ + +/* + * This is an arch independent SLIP netif. The specific serial hooks must be provided + * by another file.They are sio_open, sio_recv and sio_send + */ + +#include "netif/slipif.h" +#include "lwip/opt.h" +#include "lwip/def.h" +#include "lwip/pbuf.h" +#include "lwip/sys.h" +#include "lwip/stats.h" +#include "lwip/sio.h" + +#define SLIP_END 0300 +#define SLIP_ESC 0333 +#define SLIP_ESC_END 0334 +#define SLIP_ESC_ESC 0335 + +#define MAX_SIZE 1500 + +/** + * Send a pbuf doing the necessary SLIP encapsulation + * + * Uses the serial layer's sio_send() + */ +err_t +slipif_output(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr) +{ + struct pbuf *q; + int i; + u8_t c; + + /* Send pbuf out on the serial I/O device. */ + sio_send(SLIP_END, netif->state); + + for(q = p; q != NULL; q = q->next) { + for(i = 0; i < q->len; i++) { + c = ((u8_t *)q->payload)[i]; + switch (c) { + case SLIP_END: + sio_send(SLIP_ESC, netif->state); + sio_send(SLIP_ESC_END, netif->state); + break; + case SLIP_ESC: + sio_send(SLIP_ESC, netif->state); + sio_send(SLIP_ESC_ESC, netif->state); + break; + default: + sio_send(c, netif->state); + break; + } + } + } + sio_send(SLIP_END, netif->state); + return 0; +} + +/** + * Handle the incoming SLIP stream character by character + * + * Poll the serial layer by calling sio_recv() + * + * @return The IP packet when SLIP_END is received + */ +static struct pbuf * +slipif_input( struct netif * netif ) +{ + u8_t c; + struct pbuf *p, *q; + int recved; + int i; + + q = p = NULL; + recved = i = 0; + c = 0; + + while (1) { + c = sio_recv(netif->state); + switch (c) { + case SLIP_END: + if (recved > 0) { + /* Received whole packet. */ + pbuf_realloc(q, recved); + + LINK_STATS_INC(link.recv); + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet\n")); + return q; + } + break; + + case SLIP_ESC: + c = sio_recv(netif->state); + switch (c) { + case SLIP_ESC_END: + c = SLIP_END; + break; + case SLIP_ESC_ESC: + c = SLIP_ESC; + break; + } + /* FALLTHROUGH */ + + default: + if (p == NULL) { + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n")); + p = pbuf_alloc(PBUF_LINK, PBUF_POOL_BUFSIZE, PBUF_POOL); + + if (p == NULL) { + LINK_STATS_INC(link.drop); + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n")); + } + + if (q != NULL) { + pbuf_cat(q, p); + } else { + q = p; + } + } + if (p != NULL && recved < MAX_SIZE) { + ((u8_t *)p->payload)[i] = c; + recved++; + i++; + if (i >= p->len) { + i = 0; + p = NULL; + } + } + break; + } + + } + return NULL; +} + +/** + * The SLIP input thread + * + * Feed the IP layer with incoming packets + */ +static void +slipif_loop(void *nf) +{ + struct pbuf *p; + struct netif *netif = (struct netif *)nf; + + while (1) { + p = slipif_input(netif); + netif->input(p, netif); + } +} + +/** + * SLIP netif initialization + * + * Call the arch specific sio_open and remember + * the opened device in the state field of the netif. + */ +err_t +slipif_init(struct netif *netif) +{ + + LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%x\n", (int)netif->num)); + + netif->name[0] = 's'; + netif->name[1] = 'l'; + netif->output = slipif_output; + netif->mtu = 1500; + netif->flags = NETIF_FLAG_POINTTOPOINT; + + netif->state = sio_open(netif->num); + if (!netif->state) + return ERR_IF; + + sys_thread_new(slipif_loop, netif, SLIPIF_THREAD_PRIO); + return ERR_OK; +} diff --git a/wii/libogc/lwip/netio.c b/wii/libogc/lwip/netio.c new file mode 100644 index 0000000000..45de58a928 --- /dev/null +++ b/wii/libogc/lwip/netio.c @@ -0,0 +1,103 @@ +#if 0 +#include +#include +#include +#include +#undef errno +extern int errno; + +#include +#include "lwip/ip_addr.h" +#include "network.h" +int netio_open(struct _reent *r, void *fileStruct, const char *path,int flags,int mode); +int netio_close(struct _reent *r,void *fd); +int netio_write(struct _reent *r,void *fd,const char *ptr,size_t len); +int netio_read(struct _reent *r,void *fd,char *ptr,size_t len); + +//--------------------------------------------------------------------------------- +const devoptab_t dotab_stdnet = { +//--------------------------------------------------------------------------------- + "stdnet", // device name + 0, // size of file structure + netio_open, // device open + netio_close, // device close + netio_write, // device write + netio_read, // device read + NULL, // device seek + NULL, // device fstat + NULL, // device stat + NULL, // device link + NULL, // device unlink + NULL, // device chdir + NULL, // device rename + NULL, // device mkdir + 0, // dirStateSize + NULL, // device diropen_r + NULL, // device dirreset_r + NULL, // device dirnext_r + NULL, // device dirclose_r + NULL // device statvfs_r +}; + +int netio_open(struct _reent *r, void *fileStruct, const char *path,int flags,int mode) +{ + char *cport = NULL; + int optval = 1,nport = -1,udp_sock = INVALID_SOCKET; + struct sockaddr_in name; + socklen_t namelen = sizeof(struct sockaddr); + + if(net_init()==SOCKET_ERROR) return -1; + + udp_sock = net_socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); + if(udp_sock==INVALID_SOCKET) return -1; + + cport = strchr(path,':'); + if(cport) { + *cport++ = '\0'; + nport = atoi(cport); + } + if(nport==-1) nport = 7; //try to connect to the well known port 7 + + name.sin_addr.s_addr = inet_addr(path); + name.sin_port = htons(nport); + name.sin_family = AF_INET; + if(net_connect(udp_sock,(struct sockaddr*)&name,namelen)==-1) { + net_close(udp_sock); + return -1; + } + net_setsockopt(udp_sock,IPPROTO_TCP,TCP_NODELAY,&optval,sizeof(optval)); + + return udp_sock; +} + +int netio_close(struct _reent *r,void *fd) +{ + if(fd<0) return -1; + + net_close(fd); + + return 0; +} + +int netio_write(struct _reent *r,void *fd,const char *ptr,size_t len) +{ + int ret; + + if(fd<0) return -1; + + ret = net_write(fd,(void*)ptr,len); + + return ret; +} + +int netio_read(struct _reent *r,void *fd,char *ptr,size_t len) +{ + int ret; + + if(fd<0) return -1; + + ret = net_read(fd,ptr,len); + + return ret; +} +#endif diff --git a/wii/libogc/lwip/network.c b/wii/libogc/lwip/network.c new file mode 100644 index 0000000000..cabc4d1720 --- /dev/null +++ b/wii/libogc/lwip/network.c @@ -0,0 +1,2248 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "network.h" + +//#define _NET_DEBUG +#define ARP_TIMER_ID 0x00070041 +#define TCP_TIMER_ID 0x00070042 +#define DHCPCOARSE_TIMER_ID 0x00070043 +#define DHCPFINE_TIMER_ID 0x00070044 + +#define STACKSIZE 32768 +#define MQBOX_SIZE 256 +#define NUM_SOCKETS MEMP_NUM_NETCONN + +struct netsocket { + struct netconn *conn; + struct netbuf *lastdata; + u16 lastoffset,rcvevt,sendevt,flags; + s32 err; +}; + +struct netselect_cb { + struct netselect_cb *next; + fd_set *readset; + fd_set *writeset; + fd_set *exceptset; + u32 signaled; + mutex_t cond_lck; + cond_t cond; +}; + +typedef void (*apimsg_decode)(struct apimsg_msg *); + +static u32 g_netinitiated = 0; +static u32 tcpiplayer_inited = 0; +static u64 net_tcp_ticks = 0; +static u64 net_dhcpcoarse_ticks = 0; +static u64 net_dhcpfine_ticks = 0; +static u64 net_arp_ticks = 0; +static wd_cntrl arp_time_cntrl; +static wd_cntrl tcp_timer_cntrl; +static wd_cntrl dhcp_coarsetimer_cntrl; +static wd_cntrl dhcp_finetimer_cntrl; + +static struct netif g_hNetIF; +static struct netif g_hLoopIF; +static struct netsocket sockets[NUM_SOCKETS]; +static struct netselect_cb *selectcb_list = NULL; + +static sys_sem netsocket_sem; +static sys_sem sockselect_sem; +static sys_mbox netthread_mbox; + +static sys_thread hnet_thread; +static u8 netthread_stack[STACKSIZE]; + +static u32 tcp_timer_active = 0; + +static struct netbuf* netbuf_new(); +static void netbuf_delete(struct netbuf *); +static void netbuf_copypartial(struct netbuf *,void *,u32,u32); +static void netbuf_ref(struct netbuf *,const void *,u32); + +static struct netconn* netconn_new_with_callback(enum netconn_type,void (*)(struct netconn *,enum netconn_evt,u32)); +static struct netconn* netconn_new_with_proto_and_callback(enum netconn_type,u16,void (*)(struct netconn *,enum netconn_evt,u32)); +static err_t netconn_delete(struct netconn *); +static struct netconn* netconn_accept(struct netconn* ); +static err_t netconn_peer(struct netconn *,struct ip_addr *,u16 *); +static err_t netconn_bind(struct netconn *,struct ip_addr *,u16); +static err_t netconn_listen(struct netconn *); +static struct netbuf* netconn_recv(struct netconn *); +static err_t netconn_send(struct netconn *,struct netbuf *); +static err_t netconn_write(struct netconn *,const void *,u32,u8); +static err_t netconn_connect(struct netconn *,struct ip_addr *,u16); +static err_t netconn_disconnect(struct netconn *); + +static void do_newconn(struct apimsg_msg *); +static void do_delconn(struct apimsg_msg *); +static void do_bind(struct apimsg_msg *); +static void do_listen(struct apimsg_msg *); +static void do_connect(struct apimsg_msg *); +static void do_disconnect(struct apimsg_msg *); +static void do_accept(struct apimsg_msg *); +static void do_send(struct apimsg_msg *); +static void do_recv(struct apimsg_msg *); +static void do_write(struct apimsg_msg *); +static void do_close(struct apimsg_msg *); + +static apimsg_decode decode[APIMSG_MAX] = { + do_newconn, + do_delconn, + do_bind, + do_connect, + do_disconnect, + do_listen, + do_accept, + do_send, + do_recv, + do_write, + do_close +}; + +static void apimsg_post(struct api_msg *); + +static err_t net_input(struct pbuf *,struct netif *); +static void net_apimsg(struct api_msg *); +static err_t net_callback(void (*)(void *),void *); +static void* net_thread(void *); + +static void tmr_callback(void *arg) +{ + void (*functor)() = (void(*)())arg; + if(functor) functor(); +} + +/* low level stuff */ +static void __dhcpcoarse_timer(void *arg) +{ + __lwp_thread_dispatchdisable(); + net_callback(tmr_callback,(void*)dhcp_coarse_tmr); + __lwp_wd_insert_ticks(&dhcp_coarsetimer_cntrl,net_dhcpcoarse_ticks); + __lwp_thread_dispatchunnest(); +} + +static void __dhcpfine_timer(void *arg) +{ + __lwp_thread_dispatchdisable(); + net_callback(tmr_callback,(void*)dhcp_fine_tmr); + __lwp_wd_insert_ticks(&dhcp_finetimer_cntrl,net_dhcpfine_ticks); + __lwp_thread_dispatchunnest(); +} + +static void __tcp_timer(void *arg) +{ +#ifdef _NET_DEBUG + printf("__tcp_timer(%d,%p,%p)\n",tcp_timer_active,tcp_active_pcbs,tcp_tw_pcbs); +#endif + __lwp_thread_dispatchdisable(); + net_callback(tmr_callback,(void*)tcp_tmr); + if (tcp_active_pcbs || tcp_tw_pcbs) { + __lwp_wd_insert_ticks(&tcp_timer_cntrl,net_tcp_ticks); + } else + tcp_timer_active = 0; + __lwp_thread_dispatchunnest(); +} + +static void __arp_timer(void *arg) +{ + __lwp_thread_dispatchdisable(); + net_callback(tmr_callback,(void*)etharp_tmr); + __lwp_wd_insert_ticks(&arp_time_cntrl,net_arp_ticks); + __lwp_thread_dispatchunnest(); +} + +void tcp_timer_needed(void) +{ +#ifdef _NET_DEBUG + printf("tcp_timer_needed()\n"); +#endif + if(!tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) { + tcp_timer_active = 1; + __lwp_wd_insert_ticks(&tcp_timer_cntrl,net_tcp_ticks); + } +} + +/* netbuf part */ +static __inline__ u16 netbuf_len(struct netbuf *buf) +{ + return ((buf && buf->p)?buf->p->tot_len:0); +} + +static __inline__ struct ip_addr* netbuf_fromaddr(struct netbuf *buf) +{ + return (buf?buf->fromaddr:NULL); +} + +static __inline__ u16 netbuf_fromport(struct netbuf *buf) +{ + return (buf?buf->fromport:0); +} + +static void netbuf_copypartial(struct netbuf *buf,void *dataptr,u32 len,u32 offset) +{ + struct pbuf *p; + u16 i,left; + + left = 0; + if(buf==NULL || dataptr==NULL) return; + + for(p=buf->p;leftnext) { + if(offset!=0 && offset>=p->len) + offset -= p->len; + else { + for(i=offset;ilen;i++) { + ((u8*)dataptr)[left] = ((u8*)p->payload)[i]; + if(++left>=len) return; + } + offset = 0; + } + } +} + +static struct netbuf* netbuf_new() +{ + struct netbuf *buf = NULL; + + buf = memp_malloc(MEMP_NETBUF); + if(buf) { + buf->p = NULL; + buf->ptr = NULL; + } + return buf; +} + +static void netbuf_delete(struct netbuf *buf) +{ + if(buf!=NULL) { + if(buf->p!=NULL) pbuf_free(buf->p); + memp_free(MEMP_NETBUF,buf); + } +} + +static void netbuf_ref(struct netbuf *buf, const void *dataptr,u32 size) +{ + if(buf->p!=NULL) pbuf_free(buf->p); + buf->p = pbuf_alloc(PBUF_TRANSPORT,0,PBUF_REF); + buf->p->payload = (void*)dataptr; + buf->p->len = buf->p->tot_len = size; + buf->ptr = buf->p; +} + +/* netconn part */ +static inline enum netconn_type netconn_type(struct netconn *conn) +{ + return conn->type; +} + +static struct netconn* netconn_new_with_callback(enum netconn_type t,void (*cb)(struct netconn*,enum netconn_evt,u32)) +{ + return netconn_new_with_proto_and_callback(t,0,cb); +} + +static struct netconn* netconn_new_with_proto_and_callback(enum netconn_type t,u16 proto,void (*cb)(struct netconn *,enum netconn_evt,u32)) +{ + u32 dummy = 0; + struct netconn *conn; + struct api_msg *msg; + + conn = memp_malloc(MEMP_NETCONN); + if(!conn) return NULL; + + conn->err = ERR_OK; + conn->type = t; + conn->pcb.tcp = NULL; + + if(MQ_Init(&conn->mbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) { + memp_free(MEMP_NETCONN,conn); + return NULL; + } + if(LWP_SemInit(&conn->sem,0,1)==-1) { + MQ_Close(conn->mbox); + memp_free(MEMP_NETCONN,conn); + return NULL; + } + + conn->acceptmbox = SYS_MBOX_NULL; + conn->recvmbox = SYS_MBOX_NULL; + conn->state = NETCONN_NONE; + conn->socket = 0; + conn->callback = cb; + conn->recvavail = 0; + + msg = memp_malloc(MEMP_API_MSG); + if(!msg) { + MQ_Close(conn->mbox); + memp_free(MEMP_NETCONN,conn); + return NULL; + } + + msg->type = APIMSG_NEWCONN; + msg->msg.msg.bc.port = proto; + msg->msg.conn = conn; + apimsg_post(msg); + MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK); + memp_free(MEMP_API_MSG,msg); + + if(conn->err!=ERR_OK) { + MQ_Close(conn->mbox); + memp_free(MEMP_NETCONN,conn); + return NULL; + } + return conn; +} + +static err_t netconn_delete(struct netconn *conn) +{ + u32 dummy = 0; + struct api_msg *msg; + sys_mbox mbox; + void *mem; + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_delete(%p)\n", conn)); + + if(!conn) return ERR_OK; + + msg = memp_malloc(MEMP_API_MSG); + if(!msg) return ERR_MEM; + + msg->type = APIMSG_DELCONN; + msg->msg.conn = conn; + apimsg_post(msg); + MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK); + memp_free(MEMP_API_MSG,msg); + + mbox = conn->recvmbox; + conn->recvmbox = SYS_MBOX_NULL; + if(mbox!=SYS_MBOX_NULL) { + while(MQ_Receive(mbox,(mqmsg_t)&mem,MQ_MSG_NOBLOCK)==TRUE) { + if(mem!=NULL) { + if(conn->type==NETCONN_TCP) + pbuf_free((struct pbuf*)mem); + else + netbuf_delete((struct netbuf*)mem); + } + } + MQ_Close(mbox); + } + + mbox = conn->acceptmbox; + conn->acceptmbox = SYS_MBOX_NULL; + if(mbox!=SYS_MBOX_NULL) { + while(MQ_Receive(mbox,(mqmsg_t)&mem,MQ_MSG_NOBLOCK)==TRUE) { + if(mem!=NULL) netconn_delete((struct netconn*)mem); + } + MQ_Close(mbox); + } + + MQ_Close(conn->mbox); + conn->mbox = SYS_MBOX_NULL; + + LWP_SemDestroy(conn->sem); + conn->sem = SYS_SEM_NULL; + + memp_free(MEMP_NETCONN,conn); + return ERR_OK; +} + +static struct netconn* netconn_accept(struct netconn* conn) +{ + struct netconn *newconn; + + if(conn==NULL) return NULL; + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_accept(%p)\n", conn)); + MQ_Receive(conn->acceptmbox,(mqmsg_t)&newconn,MQ_MSG_BLOCK); + if(conn->callback) + (*conn->callback)(conn,NETCONN_EVTRCVMINUS,0); + + return newconn; +} + +static err_t netconn_peer(struct netconn *conn,struct ip_addr *addr,u16 *port) +{ + switch(conn->type) { + case NETCONN_RAW: + return ERR_CONN; + case NETCONN_UDPLITE: + case NETCONN_UDPNOCHKSUM: + case NETCONN_UDP: + if(conn->pcb.udp==NULL || ((conn->pcb.udp->flags&UDP_FLAGS_CONNECTED)==0)) + return ERR_CONN; + *addr = conn->pcb.udp->remote_ip; + *port = conn->pcb.udp->remote_port; + break; + case NETCONN_TCP: + if(conn->pcb.tcp==NULL) + return ERR_CONN; + *addr = conn->pcb.tcp->remote_ip; + *port = conn->pcb.tcp->remote_port; + break; + } + return (conn->err = ERR_OK); +} + +static err_t netconn_bind(struct netconn *conn,struct ip_addr *addr,u16 port) +{ + u32 dummy = 0; + struct api_msg *msg; + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_bind(%p)\n", conn)); + + if(conn==NULL) return ERR_VAL; + if(conn->type!=NETCONN_TCP && conn->recvmbox==SYS_MBOX_NULL) { + if(MQ_Init(&conn->recvmbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) return ERR_MEM; + } + + if((msg=memp_malloc(MEMP_API_MSG))==NULL) + return (conn->err = ERR_MEM); + + msg->type = APIMSG_BIND; + msg->msg.conn = conn; + msg->msg.msg.bc.ipaddr = addr; + msg->msg.msg.bc.port = port; + apimsg_post(msg); + MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK); + memp_free(MEMP_API_MSG,msg); + return conn->err; +} + +static err_t netconn_listen(struct netconn *conn) +{ + u32 dummy = 0; + struct api_msg *msg; + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_listen(%p)\n", conn)); + + if(conn==NULL) return -1; + if(conn->acceptmbox==SYS_MBOX_NULL) { + if(MQ_Init(&conn->acceptmbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) return ERR_MEM; + } + + if((msg=memp_malloc(MEMP_API_MSG))==NULL) return (conn->err = ERR_MEM); + msg->type = APIMSG_LISTEN; + msg->msg.conn = conn; + apimsg_post(msg); + MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK); + memp_free(MEMP_API_MSG,msg); + return conn->err; +} + +static struct netbuf* netconn_recv(struct netconn *conn) +{ + u32 dummy = 0; + struct api_msg *msg; + struct netbuf *buf; + struct pbuf *p; + u16 len; + + if(conn==NULL) return NULL; + if(conn->recvmbox==SYS_MBOX_NULL) { + conn->err = ERR_CONN; + return NULL; + } + if(conn->err!=ERR_OK) return NULL; + + if(conn->type==NETCONN_TCP) { + if(conn->pcb.tcp->state==LISTEN) { + conn->err = ERR_CONN; + return NULL; + } + + buf = memp_malloc(MEMP_NETBUF); + if(buf==NULL) { + conn->err = ERR_MEM; + return NULL; + } + + MQ_Receive(conn->recvmbox,(mqmsg_t)&p,MQ_MSG_BLOCK); + if(p!=NULL) { + len = p->tot_len; + conn->recvavail -= len; + } else + len = 0; + + if(conn->callback) + (*conn->callback)(conn,NETCONN_EVTRCVMINUS,len); + + if(p==NULL) { + memp_free(MEMP_NETBUF,buf); + MQ_Close(conn->recvmbox); + conn->recvmbox = SYS_MBOX_NULL; + return NULL; + } + + buf->p = p; + buf->ptr = p; + buf->fromport = 0; + buf->fromaddr = NULL; + + if((msg=memp_malloc(MEMP_API_MSG))==NULL) { + conn->err = ERR_MEM; + return buf; + } + + msg->type = APIMSG_RECV; + msg->msg.conn = conn; + if(buf!=NULL) + msg->msg.msg.len = len; + else + msg->msg.msg.len = 1; + + apimsg_post(msg); + MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK); + memp_free(MEMP_API_MSG,msg); + } else { + MQ_Receive(conn->recvmbox,(mqmsg_t)&buf,MQ_MSG_BLOCK); + conn->recvavail -= buf->p->tot_len; + if(conn->callback) + (*conn->callback)(conn,NETCONN_EVTRCVMINUS,buf->p->tot_len); + } + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv: received %p (err %d)\n", (void *)buf, conn->err)); + return buf; +} + +static err_t netconn_send(struct netconn *conn,struct netbuf *buf) +{ + u32 dummy = 0; + struct api_msg *msg; + + if(conn==NULL) return ERR_VAL; + if(conn->err!=ERR_OK) return conn->err; + if((msg=memp_malloc(MEMP_API_MSG))==NULL) return (conn->err = ERR_MEM); + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %d bytes\n", buf->p->tot_len)); + msg->type = APIMSG_SEND; + msg->msg.conn = conn; + msg->msg.msg.p = buf->p; + apimsg_post(msg); + MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK); + memp_free(MEMP_API_MSG,msg); + return conn->err; +} + +static err_t netconn_write(struct netconn *conn,const void *dataptr,u32 size,u8 copy) +{ + u32 dummy = 0; + struct api_msg *msg; + u16 len,snd_buf; + + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_write(%d)\n",conn->err)); + + if(conn==NULL) return ERR_VAL; + if(conn->err!=ERR_OK) return conn->err; + + if((msg=memp_malloc(MEMP_API_MSG))==NULL) return (conn->err = ERR_MEM); + + msg->type = APIMSG_WRITE; + msg->msg.conn = conn; + conn->state = NETCONN_WRITE; + while(conn->err==ERR_OK && size>0) { + msg->msg.msg.w.dataptr = (void*)dataptr; + msg->msg.msg.w.copy = copy; + if(conn->type==NETCONN_TCP) { + while((snd_buf=tcp_sndbuf(conn->pcb.tcp))==0) { + LWIP_DEBUGF(API_LIB_DEBUG,("netconn_write: tcp_sndbuf = 0,err = %d\n", conn->err)); + LWP_SemWait(conn->sem); + if(conn->err!=ERR_OK) goto ret; + } + if(size>snd_buf) + len = snd_buf; + else + len = size; + } else + len = size; + + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_write: writing %d bytes (%d)\n", len, copy)); + msg->msg.msg.w.len = len; + apimsg_post(msg); + MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK); + if(conn->err==ERR_OK) { + LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_write: %d bytes written\n",len)); + dataptr = (void*)((char*)dataptr+len); + size -= len; + } else if(conn->err==ERR_MEM) { + LWIP_DEBUGF(API_LIB_DEBUG,("netconn_write: mem err\n")); + conn->err = ERR_OK; + LWP_SemWait(conn->sem); + } else { + LWIP_DEBUGF(API_LIB_DEBUG,("netconn_write: err = %d\n", conn->err)); + break; + } + } +ret: + memp_free(MEMP_API_MSG,msg); + conn->state = NETCONN_NONE; + + return conn->err; +} + +static err_t netconn_connect(struct netconn *conn,struct ip_addr *addr,u16 port) +{ + u32 dummy = 0; + struct api_msg *msg; + + if(conn==NULL) return -1; + if(conn->recvmbox==SYS_MBOX_NULL) { + if(MQ_Init(&conn->recvmbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) return ERR_MEM; + } + + if((msg=memp_malloc(MEMP_API_MSG))==NULL) return ERR_MEM; + + msg->type = APIMSG_CONNECT; + msg->msg.conn = conn; + msg->msg.msg.bc.ipaddr = addr; + msg->msg.msg.bc.port = port; + apimsg_post(msg); + MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK); + memp_free(MEMP_API_MSG,msg); + return conn->err; +} + +static err_t netconn_disconnect(struct netconn *conn) +{ + u32 dummy = 0; + struct api_msg *msg; + + if(conn==NULL) return ERR_VAL; + if((msg=memp_malloc(MEMP_API_MSG))==NULL) return ERR_MEM; + + msg->type = APIMSG_DISCONNECT; + msg->msg.conn = conn; + apimsg_post(msg); + MQ_Receive(conn->mbox,(mqmsg_t)&dummy,MQ_MSG_BLOCK); + memp_free(MEMP_API_MSG,msg); + return conn->err; +} + +/* api msg part */ +static u8_t recv_raw(void *arg,struct raw_pcb *pcb,struct pbuf *p,struct ip_addr *addr) +{ + struct netbuf *buf; + struct netconn *conn = (struct netconn*)arg; + + if(!conn) return 0; + + if(conn->recvmbox!=SYS_MBOX_NULL) { + if((buf=memp_malloc(MEMP_NETBUF))==NULL) return 0; + + pbuf_ref(p); + buf->p = p; + buf->ptr = p; + buf->fromaddr = addr; + buf->fromport = pcb->protocol; + + conn->recvavail += p->tot_len; + if(conn->callback) + (*conn->callback)(conn,NETCONN_EVTRCVPLUS,p->tot_len); + MQ_Send(conn->recvmbox,buf,MQ_MSG_BLOCK); + } + return 0; +} + +static void recv_udp(void *arg,struct udp_pcb *pcb,struct pbuf *p,struct ip_addr *addr,u16 port) +{ + struct netbuf *buf; + struct netconn *conn = (struct netconn*)arg; + + if(!conn) { + pbuf_free(p); + return; + } + + if(conn->recvmbox!=SYS_MBOX_NULL) { + buf = memp_malloc(MEMP_NETBUF); + if(!buf) { + pbuf_free(p); + return; + } + buf->p = p; + buf->ptr = p; + buf->fromaddr = addr; + buf->fromport = port; + + conn->recvavail += p->tot_len; + if(conn->callback) + (*conn->callback)(conn,NETCONN_EVTRCVPLUS,p->tot_len); + + MQ_Send(conn->recvmbox,buf,MQ_MSG_BLOCK); + } +} + +static err_t recv_tcp(void *arg,struct tcp_pcb *pcb,struct pbuf *p,err_t err) +{ + u16 len; + struct netconn *conn = (struct netconn*)arg; + + LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: recv_tcp(%p,%p,%p,%d)\n",arg,pcb,p,err)); + + if(conn==NULL) { + pbuf_free(p); + return ERR_VAL; + } + + if(conn->recvmbox!=SYS_MBOX_NULL) { + conn->err = err; + if(p!=NULL) { + len = p->tot_len; + conn->recvavail += len; + } else len = 0; + + if(conn->callback) + (*conn->callback)(conn,NETCONN_EVTRCVPLUS,len); + + MQ_Send(conn->recvmbox,p,MQ_MSG_BLOCK); + } + return ERR_OK; +} + +static void err_tcp(void *arg,err_t err) +{ + u32 dummy = 0; + struct netconn *conn = (struct netconn*)arg; + + LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: err_tcp: %d\n",err)); + if(conn) { + conn->err = err; + conn->pcb.tcp = NULL; + if(conn->recvmbox!=SYS_MBOX_NULL) { + if(conn->callback) (*conn->callback)(conn,NETCONN_EVTRCVPLUS,0); + MQ_Send(conn->recvmbox,&dummy,MQ_MSG_BLOCK); + } + if(conn->mbox!=SYS_MBOX_NULL) { + MQ_Send(conn->mbox,&dummy,MQ_MSG_BLOCK); + } + if(conn->acceptmbox!=SYS_MBOX_NULL) { + if(conn->callback) (*conn->callback)(conn,NETCONN_EVTRCVPLUS,0); + MQ_Send(conn->acceptmbox,&dummy,MQ_MSG_BLOCK); + } + if(conn->sem!=SYS_SEM_NULL) { + LWP_SemPost(conn->sem); + } + } +} + +static err_t poll_tcp(void *arg,struct tcp_pcb *pcb) +{ + struct netconn *conn = (struct netconn*)arg; + + LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: poll_tcp\n")); + if(conn && conn->sem!=SYS_SEM_NULL && (conn->state==NETCONN_WRITE || conn->state==NETCONN_CLOSE)) + LWP_SemPost(conn->sem); + + return ERR_OK; +} + +static err_t sent_tcp(void *arg,struct tcp_pcb *pcb,u16 len) +{ + struct netconn *conn = (struct netconn*)arg; + + LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: sent_tcp: sent %d bytes\n",len)); + if(conn && conn->sem!=SYS_SEM_NULL) + LWP_SemPost(conn->sem); + + if(conn && conn->callback) { + if(tcp_sndbuf(conn->pcb.tcp)>TCP_SNDLOWAT) + (*conn->callback)(conn,NETCONN_EVTSENDPLUS,len); + } + return ERR_OK; +} + +static void setuptcp(struct netconn *conn) +{ + struct tcp_pcb *pcb = conn->pcb.tcp; + + tcp_arg(pcb,conn); + tcp_recv(pcb,recv_tcp); + tcp_sent(pcb,sent_tcp); + tcp_poll(pcb,poll_tcp,4); + tcp_err(pcb,err_tcp); +} + +static err_t accept_func(void *arg,struct tcp_pcb *newpcb,err_t err) +{ + sys_mbox mbox; + struct netconn *newconn,*conn = (struct netconn*)arg; + + LWIP_DEBUGF(API_LIB_DEBUG, ("accept_func: %p %p %d\n",arg,newpcb,err)); + + mbox = conn->acceptmbox; + newconn = memp_malloc(MEMP_NETCONN); + if(newconn==NULL) return ERR_MEM; + + if(MQ_Init(&newconn->recvmbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) { + memp_free(MEMP_NETCONN,newconn); + return ERR_MEM; + } + + if(MQ_Init(&newconn->mbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) { + MQ_Close(newconn->recvmbox); + memp_free(MEMP_NETCONN,newconn); + return ERR_MEM; + } + + if(LWP_SemInit(&newconn->sem,0,1)==-1) { + MQ_Close(newconn->recvmbox); + MQ_Close(newconn->mbox); + memp_free(MEMP_NETCONN,newconn); + return ERR_MEM; + } + + newconn->type = NETCONN_TCP; + newconn->pcb.tcp = newpcb; + setuptcp(newconn); + + newconn->acceptmbox = SYS_MBOX_NULL; + newconn->err = err; + + if(conn->callback) { + (*conn->callback)(conn,NETCONN_EVTRCVPLUS,0); + } + newconn->callback = conn->callback; + newconn->socket = -1; + newconn->recvavail = 0; + + MQ_Send(mbox,newconn,MQ_MSG_BLOCK); + return ERR_OK; +} + +static void do_newconn(struct apimsg_msg *msg) +{ + u32 dummy = 0; + + if(msg->conn->pcb.tcp) { + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); + return; + } + + msg->conn->err = ERR_OK; + switch(msg->conn->type) { + case NETCONN_RAW: + msg->conn->pcb.raw = raw_new(msg->msg.bc.port); + raw_recv(msg->conn->pcb.raw,recv_raw,msg->conn); + break; + case NETCONN_UDPLITE: + msg->conn->pcb.udp = udp_new(); + if(!msg->conn->pcb.udp) { + msg->conn->err = ERR_MEM; + break; + } + udp_setflags(msg->conn->pcb.udp,UDP_FLAGS_UDPLITE); + udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn); + break; + case NETCONN_UDPNOCHKSUM: + msg->conn->pcb.udp = udp_new(); + if(!msg->conn->pcb.udp) { + msg->conn->err = ERR_MEM; + break; + } + udp_setflags(msg->conn->pcb.udp,UDP_FLAGS_NOCHKSUM); + udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn); + break; + case NETCONN_UDP: + msg->conn->pcb.udp = udp_new(); + if(!msg->conn->pcb.udp) { + msg->conn->err = ERR_MEM; + break; + } + udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn); + break; + case NETCONN_TCP: + msg->conn->pcb.tcp = tcp_new(); + if(!msg->conn->pcb.tcp) { + msg->conn->err = ERR_MEM; + break; + } + setuptcp(msg->conn); + break; + default: + break; + } + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); +} + +static void do_delconn(struct apimsg_msg *msg) +{ + u32 dummy = 0; + + if(msg->conn->pcb.tcp) { + switch(msg->conn->type) { + case NETCONN_RAW: + raw_remove(msg->conn->pcb.raw); + break; + case NETCONN_UDPLITE: + case NETCONN_UDPNOCHKSUM: + case NETCONN_UDP: + msg->conn->pcb.udp->recv_arg = NULL; + udp_remove(msg->conn->pcb.udp); + break; + case NETCONN_TCP: + if(msg->conn->pcb.tcp->state==LISTEN) { + tcp_arg(msg->conn->pcb.tcp,NULL); + tcp_accept(msg->conn->pcb.tcp,NULL); + tcp_close(msg->conn->pcb.tcp); + } else { + tcp_arg(msg->conn->pcb.tcp,NULL); + tcp_sent(msg->conn->pcb.tcp,NULL); + tcp_recv(msg->conn->pcb.tcp,NULL); + tcp_poll(msg->conn->pcb.tcp,NULL,0); + tcp_err(msg->conn->pcb.tcp,NULL); + if(tcp_close(msg->conn->pcb.tcp)!=ERR_OK) + tcp_abort(msg->conn->pcb.tcp); + } + break; + default: + break; + } + } + if(msg->conn->callback) { + (*msg->conn->callback)(msg->conn,NETCONN_EVTRCVPLUS,0); + (*msg->conn->callback)(msg->conn,NETCONN_EVTSENDPLUS,0); + } + if(msg->conn->mbox!=SYS_MBOX_NULL) + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); +} + +static void do_bind(struct apimsg_msg *msg) +{ + u32 dummy = 0; + + if(msg->conn->pcb.tcp==NULL) { + switch(msg->conn->type) { + case NETCONN_RAW: + msg->conn->pcb.raw = raw_new(msg->msg.bc.port); + raw_recv(msg->conn->pcb.raw,recv_raw,msg->conn); + break; + case NETCONN_UDPLITE: + msg->conn->pcb.udp = udp_new(); + udp_setflags(msg->conn->pcb.udp,UDP_FLAGS_UDPLITE); + udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn); + break; + case NETCONN_UDPNOCHKSUM: + msg->conn->pcb.udp = udp_new(); + udp_setflags(msg->conn->pcb.udp,UDP_FLAGS_NOCHKSUM); + udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn); + break; + case NETCONN_UDP: + msg->conn->pcb.udp = udp_new(); + udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn); + break; + case NETCONN_TCP: + msg->conn->pcb.tcp = tcp_new(); + setuptcp(msg->conn); + break; + default: + break; + } + } + switch(msg->conn->type) { + case NETCONN_RAW: + msg->conn->err = raw_bind(msg->conn->pcb.raw,msg->msg.bc.ipaddr); + break; + case NETCONN_UDPLITE: + case NETCONN_UDPNOCHKSUM: + case NETCONN_UDP: + msg->conn->err = udp_bind(msg->conn->pcb.udp,msg->msg.bc.ipaddr,msg->msg.bc.port); + break; + case NETCONN_TCP: + msg->conn->err = tcp_bind(msg->conn->pcb.tcp,msg->msg.bc.ipaddr,msg->msg.bc.port); + break; + default: + break; + } + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); +} + +static err_t do_connected(void *arg,struct tcp_pcb *pcb,err_t err) +{ + u32 dummy = 0; + struct netconn *conn = (struct netconn*)arg; + + if(!conn) return ERR_VAL; + + conn->err = err; + if(conn->type==NETCONN_TCP && err==ERR_OK) setuptcp(conn); + + MQ_Send(conn->mbox,&dummy,MQ_MSG_BLOCK); + return ERR_OK; +} + +static void do_connect(struct apimsg_msg *msg) +{ + u32 dummy = 0; + + if(!msg->conn->pcb.tcp) { + switch(msg->conn->type) { + case NETCONN_RAW: + msg->conn->pcb.raw = raw_new(msg->msg.bc.port); + raw_recv(msg->conn->pcb.raw,recv_raw,msg->conn); + break; + case NETCONN_UDPLITE: + msg->conn->pcb.udp = udp_new(); + if(!msg->conn->pcb.udp) { + msg->conn->err = ERR_MEM; + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); + return; + } + udp_setflags(msg->conn->pcb.udp,UDP_FLAGS_UDPLITE); + udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn); + break; + case NETCONN_UDPNOCHKSUM: + msg->conn->pcb.udp = udp_new(); + if(!msg->conn->pcb.udp) { + msg->conn->err = ERR_MEM; + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); + return; + } + udp_setflags(msg->conn->pcb.udp,UDP_FLAGS_NOCHKSUM); + udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn); + break; + case NETCONN_UDP: + msg->conn->pcb.udp = udp_new(); + if(!msg->conn->pcb.udp) { + msg->conn->err = ERR_MEM; + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); + return; + } + udp_recv(msg->conn->pcb.udp,recv_udp,msg->conn); + break; + case NETCONN_TCP: + msg->conn->pcb.tcp = tcp_new(); + if(!msg->conn->pcb.tcp) { + msg->conn->err = ERR_MEM; + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); + return; + } + break; + default: + break; + } + } + switch(msg->conn->type) { + case NETCONN_RAW: + raw_connect(msg->conn->pcb.raw,msg->msg.bc.ipaddr); + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); + break; + case NETCONN_UDPLITE: + case NETCONN_UDPNOCHKSUM: + case NETCONN_UDP: + udp_connect(msg->conn->pcb.udp,msg->msg.bc.ipaddr,msg->msg.bc.port); + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); + break; + case NETCONN_TCP: + setuptcp(msg->conn); + tcp_connect(msg->conn->pcb.tcp,msg->msg.bc.ipaddr,msg->msg.bc.port,do_connected); + break; + default: + break; + } +} + +static void do_disconnect(struct apimsg_msg *msg) +{ + u32 dummy = 0; + + switch(msg->conn->type) { + case NETCONN_RAW: + break; + case NETCONN_UDPLITE: + case NETCONN_UDPNOCHKSUM: + case NETCONN_UDP: + udp_disconnect(msg->conn->pcb.udp); + break; + case NETCONN_TCP: + break; + } + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); +} + +static void do_listen(struct apimsg_msg *msg) +{ + u32 dummy = 0; + + if(msg->conn->pcb.tcp!=NULL) { + switch(msg->conn->type) { + case NETCONN_RAW: + LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: listen RAW: cannot listen for RAW.\n")); + break; + case NETCONN_UDPLITE: + case NETCONN_UDPNOCHKSUM: + case NETCONN_UDP: + LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: listen UDP: cannot listen for UDP.\n")); + break; + case NETCONN_TCP: + msg->conn->pcb.tcp = tcp_listen(msg->conn->pcb.tcp); + if(msg->conn->pcb.tcp==NULL) + msg->conn->err = ERR_MEM; + else { + if(msg->conn->acceptmbox==SYS_MBOX_NULL) { + if(MQ_Init(&msg->conn->acceptmbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) { + msg->conn->err = ERR_MEM; + break; + } + } + tcp_arg(msg->conn->pcb.tcp,msg->conn); + tcp_accept(msg->conn->pcb.tcp,accept_func); + } + break; + default: + break; + } + } + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); +} + +static void do_accept(struct apimsg_msg *msg) +{ + if(msg->conn->pcb.tcp) { + switch(msg->conn->type) { + case NETCONN_RAW: + LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: accept RAW: cannot accept for RAW.\n")); + break; + case NETCONN_UDPLITE: + case NETCONN_UDPNOCHKSUM: + case NETCONN_UDP: + LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: accept UDP: cannot accept for UDP.\n")); + break; + case NETCONN_TCP: + break; + } + } +} + +static void do_send(struct apimsg_msg *msg) +{ + u32 dummy = 0; + + if(msg->conn->pcb.tcp) { + switch(msg->conn->type) { + case NETCONN_RAW: + raw_send(msg->conn->pcb.raw,msg->msg.p); + break; + case NETCONN_UDPLITE: + case NETCONN_UDPNOCHKSUM: + case NETCONN_UDP: + udp_send(msg->conn->pcb.udp,msg->msg.p); + break; + case NETCONN_TCP: + break; + } + } + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); +} + +static void do_recv(struct apimsg_msg *msg) +{ + u32 dummy = 0; + + if(msg->conn->pcb.tcp && msg->conn->type==NETCONN_TCP) { + tcp_recved(msg->conn->pcb.tcp,msg->msg.len); + } + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); +} + +static void do_write(struct apimsg_msg *msg) +{ + err_t err; + u32 dummy = 0; + + if(msg->conn->pcb.tcp) { + switch(msg->conn->type) { + case NETCONN_RAW: + case NETCONN_UDPLITE: + case NETCONN_UDPNOCHKSUM: + case NETCONN_UDP: + msg->conn->err = ERR_VAL; + break; + case NETCONN_TCP: + err = tcp_write(msg->conn->pcb.tcp,msg->msg.w.dataptr,msg->msg.w.len,msg->msg.w.copy); + if(err==ERR_OK && (!msg->conn->pcb.tcp->unacked || (msg->conn->pcb.tcp->flags&TF_NODELAY) + || msg->conn->pcb.tcp->snd_queuelen>1)) { + LWIP_DEBUGF(API_MSG_DEBUG, ("api_msg: TCP write: tcp_output.\n")); + tcp_output(msg->conn->pcb.tcp); + } + msg->conn->err = err; + if(msg->conn->callback) { + if(err==ERR_OK) { + if(tcp_sndbuf(msg->conn->pcb.tcp)<=TCP_SNDLOWAT) + (*msg->conn->callback)(msg->conn,NETCONN_EVTSENDMINUS,msg->msg.w.len); + } + } + break; + default: + break; + } + } + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); +} + +static void do_close(struct apimsg_msg *msg) +{ + u32 dummy = 0; + err_t err = ERR_OK; + + if(msg->conn->pcb.tcp) { + switch(msg->conn->type) { + case NETCONN_RAW: + case NETCONN_UDPLITE: + case NETCONN_UDPNOCHKSUM: + case NETCONN_UDP: + break; + case NETCONN_TCP: + if(msg->conn->pcb.tcp->state==LISTEN) + err = tcp_close(msg->conn->pcb.tcp); + else if(msg->conn->pcb.tcp->state==CLOSE_WAIT) + err = tcp_output(msg->conn->pcb.tcp); + msg->conn->err = err; + break; + default: + break; + } + } + MQ_Send(msg->conn->mbox,&dummy,MQ_MSG_BLOCK); +} + +static void apimsg_input(struct api_msg *msg) +{ + decode[msg->type](&(msg->msg)); +} + +static void apimsg_post(struct api_msg *msg) +{ + net_apimsg(msg); +} + +/* tcpip thread part */ +static err_t net_input(struct pbuf *p,struct netif *inp) +{ + struct net_msg *msg = memp_malloc(MEMP_TCPIP_MSG); + + LWIP_DEBUGF(TCPIP_DEBUG, ("net_input: %p %p\n", p,inp)); + + if(msg==NULL) { + LWIP_ERROR(("net_input: msg out of memory.\n")); + pbuf_free(p); + return ERR_MEM; + } + + msg->type = NETMSG_INPUT; + msg->msg.inp.p = p; + msg->msg.inp.net = inp; + MQ_Send(netthread_mbox,msg,MQ_MSG_BLOCK); + return ERR_OK; +} + +static void net_apimsg(struct api_msg *apimsg) +{ + struct net_msg *msg = memp_malloc(MEMP_TCPIP_MSG); + + LWIP_DEBUGF(TCPIP_DEBUG, ("net_apimsg: %p\n",apimsg)); + if(msg==NULL) { + LWIP_ERROR(("net_apimsg: msg out of memory.\n")); + memp_free(MEMP_API_MSG,apimsg); + return; + } + + msg->type = NETMSG_API; + msg->msg.apimsg = apimsg; + MQ_Send(netthread_mbox,msg,MQ_MSG_BLOCK); +} + +static err_t net_callback(void (*f)(void *),void *ctx) +{ + struct net_msg *msg = memp_malloc(MEMP_TCPIP_MSG); + + LWIP_DEBUGF(TCPIP_DEBUG, ("net_callback: %p(%p)\n", f,ctx)); + + if(msg==NULL) { + LWIP_ERROR(("net_apimsg: msg out of memory.\n")); + return ERR_MEM; + } + + msg->type = NETMSG_CALLBACK; + msg->msg.cb.f = f; + msg->msg.cb.ctx = ctx; + MQ_Send(netthread_mbox,msg,MQ_MSG_BLOCK); + return ERR_OK; +} + +static void* net_thread(void *arg) +{ + struct net_msg *msg; + struct timespec tb; + sys_sem sem = (sys_sem)arg; + + etharp_init(); + ip_init(); + udp_init(); + tcp_init(); + + + tb.tv_sec = ARP_TMR_INTERVAL/TB_MSPERSEC; + tb.tv_nsec = 0; + net_arp_ticks = __lwp_wd_calc_ticks(&tb); + __lwp_wd_initialize(&arp_time_cntrl,__arp_timer,ARP_TIMER_ID,NULL); + __lwp_wd_insert_ticks(&arp_time_cntrl,net_arp_ticks); + + tb.tv_sec = 0; + tb.tv_nsec = TCP_TMR_INTERVAL*TB_NSPERMS; + net_tcp_ticks = __lwp_wd_calc_ticks(&tb); + __lwp_wd_initialize(&tcp_timer_cntrl,__tcp_timer,TCP_TIMER_ID,NULL); + + LWP_SemPost(sem); + + LWIP_DEBUGF(TCPIP_DEBUG, ("net_thread(%p)\n",arg)); + + while(1) { + MQ_Receive(netthread_mbox,(mqmsg_t)&msg,MQ_MSG_BLOCK); + switch(msg->type) { + case NETMSG_API: + LWIP_DEBUGF(TCPIP_DEBUG, ("net_thread: API message %p\n", (void *)msg)); + apimsg_input(msg->msg.apimsg); + break; + case NETMSG_INPUT: + LWIP_DEBUGF(TCPIP_DEBUG, ("net_thread: IP packet %p\n", (void *)msg)); + bba_process(msg->msg.inp.p,msg->msg.inp.net); + break; + case NETMSG_CALLBACK: + LWIP_DEBUGF(TCPIP_DEBUG, ("net_thread: CALLBACK %p\n", (void *)msg)); + msg->msg.cb.f(msg->msg.cb.ctx); + break; + default: + break; + } + memp_free(MEMP_TCPIP_MSG,msg); + } + return NULL; +} + +/* sockets part */ +static s32 alloc_socket(struct netconn *conn) +{ + s32 i; + + LWP_SemWait(netsocket_sem); + + for(i=0;iNUM_SOCKETS) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s)); + return NULL; + } + sock = &sockets[s]; + if(!sock->conn) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): no active\n", s)); + return NULL; + } + + return sock; +} + +static void evt_callback(struct netconn *conn,enum netconn_evt evt,u32 len) +{ + s32 s; + struct netsocket *sock; + struct netselect_cb *scb; + + if(conn) { + s = conn->socket; + if(s<0) { + if(evt==NETCONN_EVTRCVPLUS) + conn->socket--; + return; + } + sock = get_socket(s); + if(!sock) return; + } else + return; + + LWP_SemWait(sockselect_sem); + switch(evt) { + case NETCONN_EVTRCVPLUS: + sock->rcvevt++; + break; + case NETCONN_EVTRCVMINUS: + sock->rcvevt--; + break; + case NETCONN_EVTSENDPLUS: + sock->sendevt = 1; + break; + case NETCONN_EVTSENDMINUS: + sock->sendevt = 0; + break; + } + LWP_SemPost(sockselect_sem); + + while(1) { + LWP_SemWait(sockselect_sem); + for(scb = selectcb_list;scb;scb = scb->next) { + if(scb->signaled==0) { + if(scb->readset && FD_ISSET(s,scb->readset)) + if(sock->rcvevt) break; + if(scb->writeset && FD_ISSET(s,scb->writeset)) + if(sock->sendevt) break; + } + } + if(scb) { + scb->signaled = 1; + LWP_SemPost(sockselect_sem); + LWP_MutexLock(scb->cond_lck); + LWP_CondSignal(scb->cond); + LWP_MutexUnlock(scb->cond_lck); + } else { + LWP_SemPost(sockselect_sem); + break; + } + } + +} + +extern const devoptab_t dotab_stdnet; + +s32 if_configex(struct in_addr *local_ip,struct in_addr *netmask,struct in_addr *gateway,bool use_dhcp, int max_retries) +{ + s32 ret = 0; + struct ip_addr loc_ip, mask, gw; + struct netif *pnet; + struct timespec tb; + dev_s hbba = NULL; + + if(g_netinitiated) return 0; + g_netinitiated = 1; + +// AddDevice(&dotab_stdnet); +#ifdef STATS + stats_init(); +#endif /* STATS */ + + sys_init(); + mem_init(); + memp_init(); + pbuf_init(); + netif_init(); + + // init tcpip thread message box + if(MQ_Init(&netthread_mbox,MQBOX_SIZE)!=MQ_ERROR_SUCCESSFUL) return -1; + + // create & setup interface + loc_ip.addr = 0; + mask.addr = 0; + gw.addr = 0; + + + if(use_dhcp==FALSE) { + if( !gateway || gateway->s_addr==0 + || !local_ip || local_ip->s_addr==0 + || !netmask || netmask->s_addr==0 ) return -EINVAL; + loc_ip.addr = local_ip->s_addr; + mask.addr = netmask->s_addr; + gw.addr = gateway->s_addr; + } + hbba = bba_create(&g_hNetIF); + pnet = netif_add(&g_hNetIF,&loc_ip, &mask, &gw, hbba, bba_init, net_input); + if(pnet) { + netif_set_up(pnet); + netif_set_default(pnet); +#if (LWIP_DHCP) + if(use_dhcp==TRUE) { + //setup coarse timer + tb.tv_sec = DHCP_COARSE_TIMER_SECS; + tb.tv_nsec = 0; + net_dhcpcoarse_ticks = __lwp_wd_calc_ticks(&tb); + __lwp_wd_initialize(&dhcp_coarsetimer_cntrl, __dhcpcoarse_timer, DHCPCOARSE_TIMER_ID, NULL); + __lwp_wd_insert_ticks(&dhcp_coarsetimer_cntrl, net_dhcpcoarse_ticks); + + //setup fine timer + tb.tv_sec = 0; + tb.tv_nsec = DHCP_FINE_TIMER_MSECS * TB_NSPERMS; + net_dhcpfine_ticks = __lwp_wd_calc_ticks(&tb); + __lwp_wd_initialize(&dhcp_finetimer_cntrl, __dhcpfine_timer, DHCPFINE_TIMER_ID, NULL); + __lwp_wd_insert_ticks(&dhcp_finetimer_cntrl, net_dhcpfine_ticks); + + //now start dhcp client + dhcp_start(pnet); + } +#endif + } else + return -ENXIO; + + // setup loopinterface + IP4_ADDR(&loc_ip, 127, 0, 0, 1); + IP4_ADDR(&mask, 255, 0, 0, 0); + IP4_ADDR(&gw, 127, 0, 0, 1); + pnet = netif_add(&g_hLoopIF, &loc_ip, &mask, &gw, NULL, loopif_init, net_input); + + //last and least start the tcpip layer + ret = net_init(); + + if ( ret == 0 && use_dhcp == TRUE ) { + + int retries = max_retries; + // wait for dhcp to bind + while ( g_hNetIF.dhcp->state != DHCP_BOUND && retries > 0 ) { + retries--; + usleep(500000); + } + + if ( retries > 0 ) { + //copy back network addresses + if ( local_ip != NULL ) local_ip->s_addr = g_hNetIF.ip_addr.addr; + if ( gateway != NULL ) gateway->s_addr = g_hNetIF.gw.addr; + if ( netmask != NULL ) netmask->s_addr = g_hNetIF.netmask.addr; + } else { + ret = -ETIMEDOUT; + } + } + + return ret; +} + +s32 if_config(char *local_ip, char *netmask, char *gateway,bool use_dhcp, int max_retries) +{ + s32 ret = 0; + struct in_addr loc_ip, mask, gw; + + loc_ip.s_addr = 0; + mask.s_addr = 0; + gw.s_addr = 0; + + if ( local_ip != NULL ) loc_ip.s_addr = inet_addr(local_ip); + if ( netmask != NULL ) mask.s_addr = inet_addr(netmask); + if ( gateway != NULL ) gw.s_addr = inet_addr(gateway); + + ret = if_configex( &loc_ip, &mask, &gw, use_dhcp, max_retries); + + if (ret<0) return ret; + + if ( use_dhcp == TRUE ) { + //copy back network addresses + if ( local_ip != NULL ) strcpy(local_ip, inet_ntoa( loc_ip )); + if ( netmask != NULL ) strcpy(netmask, inet_ntoa( mask)); + if ( gateway != NULL ) strcpy(gateway, inet_ntoa( gw )); + } + return ret; +} + + +s32 net_init() +{ + sys_sem sem; + + if(tcpiplayer_inited) return 1; + + if(LWP_SemInit(&netsocket_sem,1,1)==-1) return -1; + if(LWP_SemInit(&sockselect_sem,1,1)==-1) { + LWP_SemDestroy(netsocket_sem); + return -1; + } + if(LWP_SemInit(&sem,0,1)==-1) { + LWP_SemDestroy(netsocket_sem); + LWP_SemDestroy(sockselect_sem); + return -1; + } + + if(LWP_CreateThread(&hnet_thread,net_thread,(void*)sem,netthread_stack,STACKSIZE,220)==-1) { + LWP_SemDestroy(netsocket_sem); + LWP_SemDestroy(sockselect_sem); + LWP_SemDestroy(sem); + return -1; + } + LWP_SemWait(sem); + LWP_SemDestroy(sem); + + tcpiplayer_inited = 1; + + return 0; +} + +s32 net_shutdown(s32 s,u32 how) +{ + return -1; +} + +s32 net_fcntl(s32 s, u32 cmd, u32 flags) +{ + return -1; +} + +s32 net_socket(u32 domain,u32 type,u32 protocol) +{ + s32 i; + struct netconn *conn; + + switch(type) { + case SOCK_RAW: + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_socket(SOCK_RAW)\n")); + conn = netconn_new_with_proto_and_callback(NETCONN_RAW,protocol,evt_callback); + break; + case SOCK_DGRAM: + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_socket(SOCK_DGRAM)\n")); + conn = netconn_new_with_callback(NETCONN_UDP,evt_callback); + break; + case SOCK_STREAM: + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_socket(SOCK_STREAM)\n")); + conn = netconn_new_with_callback(NETCONN_TCP,evt_callback); + break; + default: + return -1; + } + if(!conn) return -1; + + i = alloc_socket(conn); + if(i==-1) { + netconn_delete(conn); + return -1; + } + + conn->socket = i; + return i; +} + +s32 net_accept(s32 s,struct sockaddr *addr,socklen_t *addrlen) +{ + struct netsocket *sock; + struct netconn *newconn; + struct ip_addr naddr = {0}; + u16 port = 0; + s32 newsock; + struct sockaddr_in sin; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_accept(%d)\n", s)); + + sock = get_socket(s); + if(!sock) return -ENOTSOCK; + + newconn = netconn_accept(sock->conn); + netconn_peer(newconn,&naddr,&port); + + memset(&sin,0,sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = naddr.addr; + + if(*addrlen>sizeof(sin)) + *addrlen = sizeof(sin); + memcpy(addr,&sin,*addrlen); + + newsock = alloc_socket(newconn); + if(newsock==-1) { + netconn_delete(newconn); + return -1; + } + + newconn->callback = evt_callback; + sock = get_socket(newsock); + + LWP_SemWait(netsocket_sem); + sock->rcvevt += -1 - newconn->socket; + newconn->socket = newsock; + LWP_SemPost(netsocket_sem); + + return newsock; +} + +s32 net_bind(s32 s,struct sockaddr *name,socklen_t namelen) +{ + struct netsocket *sock; + struct ip_addr loc_addr; + u16 loc_port; + err_t err; + + sock = get_socket(s); + if(!sock) return -ENOTSOCK; + + loc_addr.addr = ((struct sockaddr_in*)name)->sin_addr.s_addr; + loc_port = ((struct sockaddr_in*)name)->sin_port; + + err = netconn_bind(sock->conn,&loc_addr,ntohs(loc_port)); + if(err!=ERR_OK) return -1; + + return 0; +} + +s32 net_listen(s32 s,u32 backlog) +{ + struct netsocket *sock; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_listen(%d, backlog=%d)\n", s, backlog)); + sock = get_socket(s); + if(!sock) return -ENOTSOCK; + + err = netconn_listen(sock->conn); + if(err!=ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_listen(%d) failed, err=%d\n", s, err)); + return -1; + } + return 0; +} + +s32 net_recvfrom(s32 s,void *mem,s32 len,u32 flags,struct sockaddr *from,socklen_t *fromlen) +{ + struct netsocket *sock; + struct netbuf *buf; + u16 buflen,copylen; + struct ip_addr *addr; + u16 port; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_recvfrom(%d, %p, %d, 0x%x, ..)\n", s, mem, len, flags)); + if(mem==NULL || len<=0) return -EINVAL; + + sock = get_socket(s); + if(!sock) return -ENOTSOCK; + + if(sock->lastdata) + buf = sock->lastdata; + else { + if(((flags&MSG_DONTWAIT) || (sock->flags&O_NONBLOCK)) && !sock->rcvevt) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_recvfrom(%d): returning EWOULDBLOCK\n", s)); + return -EAGAIN; + } + buf = netconn_recv(sock->conn); + if(!buf) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_recvfrom(%d): buf == NULL!\n", s)); + return 0; + } + } + + buflen = netbuf_len(buf); + buflen -= sock->lastoffset; + if(buflen<=0) + return 0; + if(len>buflen) + copylen = buflen; + else + copylen = len; + + netbuf_copypartial(buf,mem,copylen,sock->lastoffset); + + if(from && fromlen) { + struct sockaddr_in sin; + + addr = netbuf_fromaddr(buf); + port = netbuf_fromport(buf); + + memset(&sin,0,sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = addr->addr; + + if(*fromlen>sizeof(sin)) + *fromlen = sizeof(sin); + + memcpy(from,&sin,*fromlen); + + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_recvfrom(%d): addr=", s)); + ip_addr_debug_print(SOCKETS_DEBUG, addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u len=%u\n", port, copylen)); + } + if(netconn_type(sock->conn)==NETCONN_TCP && (buflen-copylen)>0) { + sock->lastdata = buf; + sock->lastoffset += copylen; + } else { + sock->lastdata = NULL; + sock->lastoffset = 0; + netbuf_delete(buf); + } + return copylen; +} + +s32 net_read(s32 s,void *mem,s32 len) +{ + return net_recvfrom(s,mem,len,0,NULL,NULL); +} + +s32 net_recv(s32 s,void *mem,s32 len,u32 flags) +{ + return net_recvfrom(s,mem,len,flags,NULL,NULL); +} + +s32 net_sendto(s32 s,const void *data,s32 len,u32 flags,struct sockaddr *to,socklen_t tolen) +{ + struct netsocket *sock; + struct ip_addr remote_addr, addr; + u16_t remote_port, port = 0; + s32 ret,connected; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_sendto(%d, data=%p, size=%d, flags=0x%x)\n", s, data, len, flags)); + if(data==NULL || len<=0) return -EINVAL; + + sock = get_socket(s); + if (!sock) return -ENOTSOCK; + + /* get the peer if currently connected */ + connected = (netconn_peer(sock->conn, &addr, &port) == ERR_OK); + + remote_addr.addr = ((struct sockaddr_in *)to)->sin_addr.s_addr; + remote_port = ((struct sockaddr_in *)to)->sin_port; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_sendto(%d, data=%p, size=%d, flags=0x%x to=", s, data, len, flags)); + ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u\n", ntohs(remote_port))); + + netconn_connect(sock->conn, &remote_addr, ntohs(remote_port)); + + ret = net_send(s, data, len, flags); + + /* reset the remote address and port number + of the connection */ + if (connected) + netconn_connect(sock->conn, &addr, port); + else + netconn_disconnect(sock->conn); + return ret; +} + +s32 net_send(s32 s,const void *data,s32 len,u32 flags) +{ + struct netsocket *sock; + struct netbuf *buf; + err_t err; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_send(%d, data=%p, size=%d, flags=0x%x)\n", s, data, len, flags)); + if(data==NULL || len<=0) return -EINVAL; + + sock = get_socket(s); + if(!sock) return -ENOTSOCK; + + switch(netconn_type(sock->conn)) { + case NETCONN_RAW: + case NETCONN_UDP: + case NETCONN_UDPLITE: + case NETCONN_UDPNOCHKSUM: + buf = netbuf_new(); + if(!buf) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_send(%d) ENOBUFS\n", s)); + return -ENOBUFS; + } + netbuf_ref(buf,data,len); + err = netconn_send(sock->conn,buf); + netbuf_delete(buf); + break; + case NETCONN_TCP: + err = netconn_write(sock->conn,data,len,NETCONN_COPY); + break; + default: + err = ERR_ARG; + break; + } + if(err!=ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_send(%d) err=%d\n", s, err)); + return -1; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_send(%d) ok size=%d\n", s, len)); + return len; +} + +s32 net_write(s32 s,const void *data,s32 size) +{ + return net_send(s,data,size,0); +} + +s32 net_connect(s32 s,struct sockaddr *name,socklen_t namelen) +{ + struct netsocket *sock; + err_t err; + + sock = get_socket(s); + if(!sock) return -ENOTSOCK; + + if(((struct sockaddr_in*)name)->sin_family==AF_UNSPEC) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_connect(%d, AF_UNSPEC)\n", s)); + err = netconn_disconnect(sock->conn); + } else { + struct ip_addr remote_addr; + u16 remote_port; + + remote_addr.addr = ((struct sockaddr_in*)name)->sin_addr.s_addr; + remote_port = ((struct sockaddr_in*)name)->sin_port; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_connect(%d, addr=", s)); + ip_addr_debug_print(SOCKETS_DEBUG, &remote_addr); + LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%u)\n", ntohs(remote_port))); + + err = netconn_connect(sock->conn,&remote_addr,ntohs(remote_port)); + } + if(err!=ERR_OK) { + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_connect(%d) failed, err=%d\n", s, err)); + return -1; + } + + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_connect(%d) succeeded\n", s)); + return -EISCONN; +} + +s32 net_close(s32 s) +{ + struct netsocket *sock; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_close(%d)\n", s)); + + LWP_SemWait(netsocket_sem); + + sock = get_socket(s); + if(!sock) { + LWP_SemPost(netsocket_sem); + return -ENOTSOCK; + } + + netconn_delete(sock->conn); + if(sock->lastdata) netbuf_delete(sock->lastdata); + + sock->lastdata = NULL; + sock->lastoffset = 0; + sock->conn = NULL; + + LWP_SemPost(netsocket_sem); + return 0; +} + +static s32 net_selscan(s32 maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset) +{ + s32 i,nready = 0; + fd_set lreadset,lwriteset,lexceptset; + struct netsocket *sock; + + FD_ZERO(&lreadset); + FD_ZERO(&lwriteset); + FD_ZERO(&lexceptset); + + for(i=0;ilastdata || sock->rcvevt)) { + FD_SET(i,&lreadset); + nready++; + } + } + if(FD_ISSET(i,writeset)) { + sock = get_socket(i); + if(sock && sock->sendevt) { + FD_SET(i,&lwriteset); + nready++; + } + } + } + *readset = lreadset; + *writeset = lwriteset; + FD_ZERO(exceptset); + + return nready; +} + +s32 net_select(s32 maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,struct timeval *timeout) +{ + s32 i,nready; + fd_set lreadset,lwriteset,lexceptset; + struct timespec tb,*p_tb; + struct netselect_cb sel_cb; + struct netselect_cb *psel_cb; + + sel_cb.next = NULL; + sel_cb.readset = readset; + sel_cb.writeset = writeset; + sel_cb.exceptset = exceptset; + sel_cb.signaled = 0; + + LWP_SemWait(sockselect_sem); + + if(readset) + lreadset = *readset; + else + FD_ZERO(&lreadset); + + if(writeset) + lwriteset = *writeset; + else + FD_ZERO(&lwriteset); + + if(exceptset) + lexceptset = *exceptset; + else + FD_ZERO(&lexceptset); + + nready = net_selscan(maxfdp1,&lreadset,&lwriteset,&lexceptset); + if(!nready) { + if(timeout && timeout->tv_sec==0 && timeout->tv_usec==0) { + LWP_SemPost(sockselect_sem); + if(readset) + FD_ZERO(readset); + if(writeset) + FD_ZERO(writeset); + if(exceptset) + FD_ZERO(exceptset); + return 0; + } + + LWP_MutexInit(&sel_cb.cond_lck,FALSE); + LWP_CondInit(&sel_cb.cond); + sel_cb.next = selectcb_list; + selectcb_list = &sel_cb; + + LWP_SemPost(sockselect_sem); + if(timeout==NULL) + p_tb = NULL; + else { + tb.tv_sec = timeout->tv_sec; + tb.tv_nsec = (timeout->tv_usec+500)*TB_NSPERUS; + p_tb = &tb; + } + + LWP_MutexLock(sel_cb.cond_lck); + i = LWP_CondTimedWait(sel_cb.cond,sel_cb.cond_lck,p_tb); + LWP_MutexUnlock(sel_cb.cond_lck); + + LWP_SemWait(sockselect_sem); + if(selectcb_list==&sel_cb) + selectcb_list = sel_cb.next; + else { + for(psel_cb = selectcb_list;psel_cb;psel_cb = psel_cb->next) { + if(psel_cb->next==&sel_cb) { + psel_cb->next = sel_cb.next; + break; + } + } + } + LWP_CondDestroy(sel_cb.cond); + LWP_MutexDestroy(sel_cb.cond_lck); + + LWP_SemPost(sockselect_sem); + + if(i==ETIMEDOUT) { + if(readset) + FD_ZERO(readset); + if(writeset) + FD_ZERO(writeset); + if(exceptset) + FD_ZERO(exceptset); + return 0; + } + + if(readset) + lreadset = *readset; + else + FD_ZERO(&lreadset); + + if(writeset) + lwriteset = *writeset; + else + FD_ZERO(&lwriteset); + + if(exceptset) + lexceptset = *exceptset; + else + FD_ZERO(&lexceptset); + + nready = net_selscan(maxfdp1,&lreadset,&lwriteset,&lexceptset); + } else + LWP_SemPost(sockselect_sem); + + if(readset) + *readset = lreadset; + if(writeset) + *writeset = lwriteset; + if(exceptset) + *exceptset = lexceptset; + + return nready; +} + +s32 net_setsockopt(s32 s,u32 level,u32 optname,const void *optval,socklen_t optlen) +{ + s32 err = 0; + struct netsocket *sock; + + sock = get_socket(s); + if(sock==NULL) return -ENOTSOCK; + if(optval==NULL) return -EINVAL; + + switch(level) { + case SOL_SOCKET: + { + switch(optname) { + case SO_BROADCAST: + case SO_KEEPALIVE: + case SO_REUSEADDR: + case SO_REUSEPORT: + if(optlenconn->type!=NETCONN_TCP) return 0; + + switch(optname) { + case TCP_NODELAY: + case TCP_KEEPALIVE: + break; + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n", s, optname)); + err = ENOPROTOOPT; + } + } + break; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n", s, level, optname)); + err = ENOPROTOOPT; + } + if(err!=0) return -1; + + switch(level) { + case SOL_SOCKET: + { + switch(optname) { + case SO_BROADCAST: + case SO_KEEPALIVE: + case SO_REUSEADDR: + case SO_REUSEPORT: + if(*(u32*)optval) + sock->conn->pcb.tcp->so_options |= optname; + else + sock->conn->pcb.tcp->so_options &= ~optname; + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n", s, optname, (*(u32*)optval?"on":"off"))); + break; + } + } + break; + + case IPPROTO_IP: + { + switch(optname) { + case IP_TTL: + sock->conn->pcb.tcp->ttl = (u8)(*(u32*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %u\n", s, sock->conn->pcb.tcp->ttl)); + break; + case IP_TOS: + sock->conn->pcb.tcp->tos = (u8)(*(u32*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %u\n", s, sock->conn->pcb.tcp->tos)); + break; + } + } + break; + + case IPPROTO_TCP: + { + switch(optname) { + case TCP_NODELAY: + if(*(u32*)optval) + sock->conn->pcb.tcp->flags |= TF_NODELAY; + else + sock->conn->pcb.tcp->flags &= ~TF_NODELAY; + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n", s, (*(u32*)optval)?"on":"off") ); + break; + case TCP_KEEPALIVE: + sock->conn->pcb.tcp->keepalive = (u32)(*(u32*)optval); + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %u\n", s, sock->conn->pcb.tcp->keepalive)); + break; + } + } + } + return err?-1:0; +} + +s32 net_ioctl(s32 s, u32 cmd, void *argp) +{ + struct netsocket *sock = get_socket(s); + + if(!sock) return -ENOTSOCK; + + switch (cmd) { + case FIONREAD: + if(!argp) return -EINVAL; + + *((u16_t*)argp) = sock->conn->recvavail; + + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_ioctl(%d, FIONREAD, %p) = %u\n", s, argp, *((u16*)argp))); + return 0; + + case FIONBIO: + if(argp && *(u32*)argp) + sock->flags |= O_NONBLOCK; + else + sock->flags &= ~O_NONBLOCK; + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_ioctl(%d, FIONBIO, %d)\n", s, !!(sock->flags&O_NONBLOCK))); + return 0; + + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("net_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp)); + return -EINVAL; + } +} diff --git a/wii/libogc/wiiuse/classic.c b/wii/libogc/wiiuse/classic.c new file mode 100644 index 0000000000..671522c21c --- /dev/null +++ b/wii/libogc/wiiuse/classic.c @@ -0,0 +1,225 @@ +/* + * wiiuse + * + * Written By: + * Michael Laforest < para > + * Email: < thepara (--AT--) g m a i l [--DOT--] com > + * + * Copyright 2006-2007 + * + * This file is part of wiiuse. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * $Header: /lvm/shared/ds/ds/cvs/devkitpro-cvsbackup/libogc/wiiuse/classic.c,v 1.7 2008-11-14 13:34:57 shagkur Exp $ + * + */ + +/** + * @file + * @brief Classic controller expansion device. + */ + +#include +#include +#include +#include + +#ifdef WIN32 + #include +#endif + +#include "definitions.h" +#include "wiiuse_internal.h" +#include "dynamics.h" +#include "events.h" +#include "classic.h" +#include "io.h" + +static void classic_ctrl_pressed_buttons(struct classic_ctrl_t* cc, short now); + +/** + * @brief Handle the handshake data from the classic controller. + * + * @param cc A pointer to a classic_ctrl_t structure. + * @param data The data read in from the device. + * @param len The length of the data block, in bytes. + * + * @return Returns 1 if handshake was successful, 0 if not. + */ +int classic_ctrl_handshake(struct wiimote_t* wm, struct classic_ctrl_t* cc, ubyte* data, uword len) { + //int i; + int offset = 0; + + cc->btns = 0; + cc->btns_held = 0; + cc->btns_released = 0; + + /* decrypt data */ + /* + for (i = 0; i < len; ++i) + data[i] = (data[i] ^ 0x17) + 0x17; + */ + + /* is this a wiiu pro? */ + if (len > 223 && data[223] == 0x20) { + cc->ljs.max.x = cc->ljs.max.y = 0xFF; + cc->ljs.min.x = cc->ljs.min.y = 0; + cc->ljs.center.x = cc->ljs.center.y = 0x80; + + cc->rjs = cc->ljs; + + cc->type = 2; + } + else { + if (data[offset] == 0xFF) { + /* + * Sometimes the data returned here is not correct. + * This might happen because the wiimote is lagging + * behind our initialization sequence. + * To fix this just request the handshake again. + * + * Other times it's just the first 16 bytes are 0xFF, + * but since the next 16 bytes are the same, just use + * those. + */ + if (data[offset + 16] == 0xFF) { + /* get the calibration data again */ + WIIUSE_DEBUG("Classic controller handshake appears invalid, trying again."); + wiiuse_read_data(wm, data, WM_EXP_MEM_CALIBR, EXP_HANDSHAKE_LEN, wiiuse_handshake_expansion); + } else + offset += 16; + } + + if (len > 218 && data[218]) + cc->type = 1; /* classic controller pro (no analog triggers) */ + else + cc->type = 0; /* original classic controller (analog triggers) */ + + /* joystick stuff */ + cc->ljs.max.x = data[0 + offset] / 4 == 0 ? 64 : data[0 + offset] / 4; + cc->ljs.min.x = data[1 + offset] / 4; + cc->ljs.center.x = data[2 + offset] / 4 == 0 ? 32 : data[2 + offset] / 4; + cc->ljs.max.y = data[3 + offset] / 4 == 0 ? 64 : data[3 + offset] / 4; + cc->ljs.min.y = data[4 + offset] / 4; + cc->ljs.center.y = data[5 + offset] / 4 == 0 ? 32 : data[5 + offset] / 4; + + cc->rjs.max.x = data[6 + offset] / 8 == 0 ? 32 : data[6 + offset] / 8; + cc->rjs.min.x = data[7 + offset] / 8; + cc->rjs.center.x = data[8 + offset] / 8 == 0 ? 16 : data[8 + offset] / 8; + cc->rjs.max.y = data[9 + offset] / 8 == 0 ? 32 : data[9 + offset] / 8; + cc->rjs.min.y = data[10 + offset] / 8; + cc->rjs.center.y = data[11 + offset] / 8 == 0 ? 16 : data[11 + offset] / 8; + } + /* handshake done */ + wm->event = WIIUSE_CLASSIC_CTRL_INSERTED; + wm->exp.type = EXP_CLASSIC; + + #ifdef WIN32 + wm->timeout = WIIMOTE_DEFAULT_TIMEOUT; + #endif + + return 1; +} + + +/** + * @brief The classic controller disconnected. + * + * @param cc A pointer to a classic_ctrl_t structure. + */ +void classic_ctrl_disconnected(struct classic_ctrl_t* cc) +{ + memset(cc, 0, sizeof(struct classic_ctrl_t)); +} + + + +/** + * @brief Handle classic controller event. + * + * @param cc A pointer to a classic_ctrl_t structure. + * @param msg The message specified in the event packet. + */ +void classic_ctrl_event(struct classic_ctrl_t* cc, ubyte* msg) { + //int i; + + /* decrypt data */ + /* + for (i = 0; i < 6; ++i) + msg[i] = (msg[i] ^ 0x17) + 0x17; + */ + if (cc->type==2) { + classic_ctrl_pressed_buttons(cc, BIG_ENDIAN_SHORT(*(short*)(msg + 8))); + + /* 12-bit little endian values adjusted to 8-bit */ + cc->ljs.pos.x = (msg[0] >> 4) | (msg[1] << 4); + cc->rjs.pos.x = (msg[2] >> 4) | (msg[3] << 4); + cc->ljs.pos.y = (msg[4] >> 4) | (msg[5] << 4); + cc->rjs.pos.y = (msg[6] >> 4) | (msg[7] << 4); + + cc->ls_raw = cc->btns & CLASSIC_CTRL_BUTTON_FULL_L ? 0x1F : 0; + cc->rs_raw = cc->btns & CLASSIC_CTRL_BUTTON_FULL_R ? 0x1F : 0; + } + else { + classic_ctrl_pressed_buttons(cc, BIG_ENDIAN_SHORT(*(short*)(msg + 4))); + + /* left/right buttons */ + cc->ls_raw = (((msg[2] & 0x60) >> 2) | ((msg[3] & 0xE0) >> 5)); + cc->rs_raw = (msg[3] & 0x1F); + + /* + * TODO - LR range hardcoded from 0x00 to 0x1F. + * This is probably in the calibration somewhere. + */ +#ifndef GEKKO + cc->r_shoulder = ((float)r / 0x1F); + cc->l_shoulder = ((float)l / 0x1F); +#endif + /* calculate joystick orientation */ + cc->ljs.pos.x = (msg[0] & 0x3F); + cc->ljs.pos.y = (msg[1] & 0x3F); + cc->rjs.pos.x = ((msg[0] & 0xC0) >> 3) | ((msg[1] & 0xC0) >> 5) | ((msg[2] & 0x80) >> 7); + cc->rjs.pos.y = (msg[2] & 0x1F); + } + +#ifndef GEKKO + calc_joystick_state(&cc->ljs, cc->ljs.pos.x, cc->ljs.pos.y); + calc_joystick_state(&cc->rjs, cc->rjs.pos.x, cc->rjs.pos.y); +#endif +} + + +/** + * @brief Find what buttons are pressed. + * + * @param cc A pointer to a classic_ctrl_t structure. + * @param msg The message byte specified in the event packet. + */ +static void classic_ctrl_pressed_buttons(struct classic_ctrl_t* cc, short now) { + /* message is inverted (0 is active, 1 is inactive) */ + now = ~now & CLASSIC_CTRL_BUTTON_ALL; + + /* preserve old btns pressed */ + cc->btns_last = cc->btns; + + /* pressed now & were pressed, then held */ + cc->btns_held = (now & cc->btns); + + /* were pressed or were held & not pressed now, then released */ + cc->btns_released = ((cc->btns | cc->btns_held) & ~now); + + /* buttons pressed now */ + cc->btns = now; +} diff --git a/wii/libogc/wiiuse/classic.h b/wii/libogc/wiiuse/classic.h new file mode 100644 index 0000000000..2e741efca5 --- /dev/null +++ b/wii/libogc/wiiuse/classic.h @@ -0,0 +1,56 @@ +/* + * wiiuse + * + * Written By: + * Michael Laforest < para > + * Email: < thepara (--AT--) g m a i l [--DOT--] com > + * + * Copyright 2006-2007 + * + * This file is part of wiiuse. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * $Header: /lvm/shared/ds/ds/cvs/devkitpro-cvsbackup/libogc/wiiuse/classic.h,v 1.1 2008-05-08 09:42:14 shagkur Exp $ + * + */ + +/** + * @file + * @brief Classic controller expansion device. + */ + +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#ifndef CLASSIC_H_INCLUDED +#define CLASSIC_H_INCLUDED + +#include "wiiuse_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int classic_ctrl_handshake(struct wiimote_t* wm, struct classic_ctrl_t* cc, ubyte* data, uword len); + +void classic_ctrl_disconnected(struct classic_ctrl_t* cc); + +void classic_ctrl_event(struct classic_ctrl_t* cc, ubyte* msg); + +#ifdef __cplusplus +} +#endif + +#endif // CLASSIC_H_INCLUDED diff --git a/wii/libogc/wiiuse/definitions.h b/wii/libogc/wiiuse/definitions.h new file mode 100644 index 0000000000..04dc5be6d8 --- /dev/null +++ b/wii/libogc/wiiuse/definitions.h @@ -0,0 +1,55 @@ +#ifndef __DEFINITIONS_H__ +#define __DEFINITIONS_H__ + +#include "os.h" + +#define WIIMOTE_PI 3.14159265f + +//#define WITH_WIIUSE_DEBUG + +/* Error output macros */ +#define WIIUSE_ERROR(fmt, ...) fprintf(stderr, "[ERROR] " fmt "\n", ##__VA_ARGS__) + +/* Warning output macros */ +#define WIIUSE_WARNING(fmt, ...) fprintf(stderr, "[WARNING] " fmt "\n", ##__VA_ARGS__) + +/* Information output macros */ +#define WIIUSE_INFO(fmt, ...) fprintf(stderr, "[INFO] " fmt "\n", ##__VA_ARGS__) + +#ifdef WITH_WIIUSE_DEBUG + #ifdef WIN32 + #define WIIUSE_DEBUG(fmt, ...) do { \ + char* file = __FILE__; \ + int i = strlen(file) - 1; \ + for (; i && (file[i] != '\\'); --i); \ + fprintf(stderr, "[DEBUG] %s:%i: " fmt "\n", file+i+1, __LINE__, ##__VA_ARGS__); \ + } while (0) + #else + #define WIIUSE_DEBUG(fmt, ...) fprintf(stderr, "[DEBUG] " __FILE__ ":%i: " fmt "\n", __LINE__, ##__VA_ARGS__) + #endif +#else + #define WIIUSE_DEBUG(fmt, ...) +#endif + +#if 1 +#define WII_DEBUG(fmt, ...) do { \ + printf("[WDEBUG] " __FILE__ ":%i: " fmt "\n", __LINE__, ##__VA_ARGS__); \ + usleep(3000000); \ + } while (0) +#else + #define WII_DEBUG(fmt, ...) +#endif + + +/* Convert between radians and degrees */ +#define RAD_TO_DEGREE(r) ((r * 180.0f) / WIIMOTE_PI) +#define DEGREE_TO_RAD(d) (d * (WIIMOTE_PI / 180.0f)) + +/* Convert to big endian */ +#define BIG_ENDIAN_LONG(i) (htonl(i)) +#define BIG_ENDIAN_SHORT(i) (htons(i)) + +#define absf(x) ((x >= 0) ? (x) : (x * -1.0f)) +#define diff_f(x, y) ((x >= y) ? (absf(x - y)) : (absf(y - x))) + +#endif diff --git a/wii/libogc/wiiuse/dynamics.c b/wii/libogc/wiiuse/dynamics.c new file mode 100644 index 0000000000..b30539e97b --- /dev/null +++ b/wii/libogc/wiiuse/dynamics.c @@ -0,0 +1,240 @@ +/* + * wiiuse + * + * Written By: + * Michael Laforest < para > + * Email: < thepara (--AT--) g m a i l [--DOT--] com > + * + * Copyright 2006-2007 + * + * This file is part of wiiuse. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * $Header: /lvm/shared/ds/ds/cvs/devkitpro-cvsbackup/libogc/wiiuse/dynamics.c,v 1.2 2008-11-14 13:34:57 shagkur Exp $ + * + */ + +/** + * @file + * @brief Handles the dynamics of the wiimote. + * + * The file includes functions that handle the dynamics + * of the wiimote. Such dynamics include orientation and + * motion sensing. + */ + +#include +#include +#include + +#ifdef WIN32 + #include +#endif + +#include "definitions.h" +#include "wiiuse_internal.h" +#include "ir.h" +#include "dynamics.h" + +/** + * @brief Calculate the roll, pitch, yaw. + * + * @param ac An accelerometer (accel_t) structure. + * @param accel [in] Pointer to a vec3w_t structure that holds the raw acceleration data. + * @param orient [out] Pointer to a orient_t structure that will hold the orientation data. + * @param rorient [out] Pointer to a orient_t structure that will hold the non-smoothed orientation data. + * @param smooth If smoothing should be performed on the angles calculated. 1 to enable, 0 to disable. + * + * Given the raw acceleration data from the accelerometer struct, calculate + * the orientation of the device and set it in the \a orient parameter. + */ +void calculate_orientation(struct accel_t* ac, struct vec3w_t* accel, struct orient_t* orient, int smooth) { + float xg, yg, zg; + float x, y, z; + + /* + * roll - use atan(z / x) [ ranges from -180 to 180 ] + * pitch - use atan(z / y) [ ranges from -180 to 180 ] + * yaw - impossible to tell without IR + */ + + /* yaw - set to 0, IR will take care of it if it's enabled */ + orient->yaw = 0.0f; + + /* find out how much it has to move to be 1g */ + xg = (float)ac->cal_g.x; + yg = (float)ac->cal_g.y; + zg = (float)ac->cal_g.z; + + /* find out how much it actually moved and normalize to +/- 1g */ + x = ((float)accel->x - (float)ac->cal_zero.x) / xg; + y = ((float)accel->y - (float)ac->cal_zero.y) / yg; + z = ((float)accel->z - (float)ac->cal_zero.z) / zg; + + /* make sure x,y,z are between -1 and 1 for the tan functions */ + if (x < -1.0f) x = -1.0f; + else if (x > 1.0f) x = 1.0f; + if (y < -1.0f) y = -1.0f; + else if (y > 1.0f) y = 1.0f; + if (z < -1.0f) z = -1.0f; + else if (z > 1.0f) z = 1.0f; + + /* if it is over 1g then it is probably accelerating and not reliable */ + if (abs(accel->x - ac->cal_zero.x) <= (ac->cal_g.x+10)) { + /* roll */ + x = RAD_TO_DEGREE(atan2f(x, z)); + if(isfinite(x)) { + orient->roll = x; + orient->a_roll = x; + } + } + + if (abs(accel->y - ac->cal_zero.y) <= (ac->cal_g.y+10)) { + /* pitch */ + y = RAD_TO_DEGREE(atan2f(y, z)); + if(isfinite(y)) { + orient->pitch = y; + orient->a_pitch = y; + } + } + + /* smooth the angles if enabled */ + if (smooth) { + apply_smoothing(ac, orient, SMOOTH_ROLL); + apply_smoothing(ac, orient, SMOOTH_PITCH); + } +} + + +/** + * @brief Calculate the gravity forces on each axis. + * + * @param ac An accelerometer (accel_t) structure. + * @param accel [in] Pointer to a vec3w_t structure that holds the raw acceleration data. + * @param gforce [out] Pointer to a gforce_t structure that will hold the gravity force data. + */ +void calculate_gforce(struct accel_t* ac, struct vec3w_t* accel, struct gforce_t* gforce) { + float xg, yg, zg; + + /* find out how much it has to move to be 1g */ + xg = (float)ac->cal_g.x; + yg = (float)ac->cal_g.y; + zg = (float)ac->cal_g.z; + + /* find out how much it actually moved and normalize to +/- 1g */ + gforce->x = ((float)accel->x - (float)ac->cal_zero.x) / xg; + gforce->y = ((float)accel->y - (float)ac->cal_zero.y) / yg; + gforce->z = ((float)accel->z - (float)ac->cal_zero.z) / zg; +} + +static float applyCalibration(float inval,float minval, float maxval,float centerval) +{ + float ret; + /* We don't use the exact ranges but the ranges +1 in case we get bad calibration + * data - avoid div0 */ + + if (inval == centerval) + ret = 0; + else if (inval < centerval) + ret = (inval - centerval) / (centerval - minval + 1); + else + ret = (inval - centerval) / (maxval - centerval + 1); + return ret; +} + +/** + * @brief Calculate the angle and magnitude of a joystick. + * + * @param js [out] Pointer to a joystick_t structure. + * @param x The raw x-axis value. + * @param y The raw y-axis value. + */ +void calc_joystick_state(struct joystick_t* js, float x, float y) { + float rx, ry; + + /* + * Since the joystick center may not be exactly: + * (min + max) / 2 + * Then the range from the min to the center and the center to the max + * may be different. + * Because of this, depending on if the current x or y value is greater + * or less than the assoicated axis center value, it needs to be interpolated + * between the center and the minimum or maxmimum rather than between + * the minimum and maximum. + * + * So we have something like this: + * (x min) [-1] ---------*------ [0] (x center) [0] -------- [1] (x max) + * Where the * is the current x value. + * The range is therefore -1 to 1, 0 being the exact center rather than + * the middle of min and max. + */ + if (x == js->center.x) + rx = 0; + else if (x >= js->center.x) + rx = ((float)(x - js->center.x) / (float)(js->max.x - js->center.x)); + else + rx = ((float)(x - js->min.x) / (float)(js->center.x - js->min.x)) - 1.0f; + + if (y == js->center.y) + ry = 0; + else if (y >= js->center.y) + ry = ((float)(y - js->center.y) / (float)(js->max.y - js->center.y)); + else + ry = ((float)(y - js->min.y) / (float)(js->center.y - js->min.y)) - 1.0f; + + /* calculate the joystick angle and magnitude */ + js->ang = RAD_TO_DEGREE(atan2f(rx, ry)); + js->mag = hypotf(rx, ry); +} + + +void apply_smoothing(struct accel_t* ac, struct orient_t* orient, int type) { + switch (type) { + case SMOOTH_ROLL: + { + /* it's possible last iteration was nan or inf, so set it to 0 if that happened */ + if (isnan(ac->st_roll) || isinf(ac->st_roll)) + ac->st_roll = 0.0f; + + /* + * If the sign changes (which will happen if going from -180 to 180) + * or from (-1 to 1) then don't smooth, just use the new angle. + */ + if (((ac->st_roll < 0) && (orient->roll > 0)) || ((ac->st_roll > 0) && (orient->roll < 0))) { + ac->st_roll = orient->roll; + } else { + orient->roll = ac->st_roll + (ac->st_alpha * (orient->a_roll - ac->st_roll)); + ac->st_roll = orient->roll; + } + + return; + } + + case SMOOTH_PITCH: + { + if (isnan(ac->st_pitch) || isinf(ac->st_pitch)) + ac->st_pitch = 0.0f; + + if (((ac->st_pitch < 0) && (orient->pitch > 0)) || ((ac->st_pitch > 0) && (orient->pitch < 0))) { + ac->st_pitch = orient->pitch; + } else { + orient->pitch = ac->st_pitch + (ac->st_alpha * (orient->a_pitch - ac->st_pitch)); + ac->st_pitch = orient->pitch; + } + + return; + } + } +} diff --git a/wii/libogc/wiiuse/dynamics.h b/wii/libogc/wiiuse/dynamics.h new file mode 100644 index 0000000000..7d6f1ae642 --- /dev/null +++ b/wii/libogc/wiiuse/dynamics.h @@ -0,0 +1,59 @@ +/* + * wiiuse + * + * Written By: + * Michael Laforest < para > + * Email: < thepara (--AT--) g m a i l [--DOT--] com > + * + * Copyright 2006-2007 + * + * This file is part of wiiuse. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * $Header: /lvm/shared/ds/ds/cvs/devkitpro-cvsbackup/libogc/wiiuse/dynamics.h,v 1.2 2008-11-14 13:34:57 shagkur Exp $ + * + */ + +/** + * @file + * @brief Handles the dynamics of the wiimote. + * + * The file includes functions that handle the dynamics + * of the wiimote. Such dynamics include orientation and + * motion sensing. + */ + +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#ifndef DYNAMICS_H_INCLUDED +#define DYNAMICS_H_INCLUDED + +#include "wiiuse_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void calculate_orientation(struct accel_t* ac, struct vec3w_t* accel, struct orient_t* orient, int smooth); +void calculate_gforce(struct accel_t* ac, struct vec3w_t* accel, struct gforce_t* gforce); +void calc_joystick_state(struct joystick_t* js, float x, float y); +void apply_smoothing(struct accel_t* ac, struct orient_t* orient, int type); + +#ifdef __cplusplus +} +#endif + +#endif // DYNAMICS_H_INCLUDED diff --git a/wii/libogc/wiiuse/events.c b/wii/libogc/wiiuse/events.c new file mode 100644 index 0000000000..4c435af2ee --- /dev/null +++ b/wii/libogc/wiiuse/events.c @@ -0,0 +1,336 @@ +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#include + +#ifndef WIN32 + #include + #include + #include +#else + #include +#endif + +#include +#include +#include +#include + +#include "dynamics.h" +#include "definitions.h" +#include "wiiuse_internal.h" +#include "events.h" +#include "nunchuk.h" +#include "classic.h" +#include "motion_plus.h" +#include "ir.h" +#include "io.h" + + +static void event_data_read(struct wiimote_t *wm,ubyte *msg) +{ + ubyte err; + ubyte len; + uword offset; + struct op_t *op; + struct cmd_blk_t *cmd = wm->cmd_head; + + wiiuse_pressed_buttons(wm,msg); + + if(!cmd) return; + if(!(cmd->state==CMD_SENT && cmd->data[0]==WM_CMD_READ_DATA)) return; + + //printf("event_data_read(%p)\n",cmd); + + err = msg[2]&0x0f; + op = (struct op_t*)cmd->data; + if(err) { + wm->cmd_head = cmd->next; + + cmd->state = CMD_DONE; + if(cmd->cb!=NULL) cmd->cb(wm,op->buffer,(op->readdata.size - op->wait)); + + __lwp_queue_append(&wm->cmdq,&cmd->node); + wiiuse_send_next_command(wm); + return; + } + + len = ((msg[2]&0xf0)>>4)+1; + offset = BIG_ENDIAN_SHORT(*(uword*)(msg+3)); + + //printf("addr: %08x\noffset: %d\nlen: %d\n",req->addr,offset,len); + + op->readdata.addr = (op->readdata.addr&0xffff); + op->wait -= len; + if(op->wait>=op->readdata.size) op->wait = 0; + + memcpy((op->buffer+offset-op->readdata.addr),(msg+5),len); + if(!op->wait) { + wm->cmd_head = cmd->next; + + wm->event = WIIUSE_READ_DATA; + cmd->state = CMD_DONE; + if(cmd->cb!=NULL) cmd->cb(wm,op->buffer,op->readdata.size); + + __lwp_queue_append(&wm->cmdq,&cmd->node); + wiiuse_send_next_command(wm); + } +} + +static void event_ack(struct wiimote_t *wm,ubyte *msg) +{ + struct cmd_blk_t *cmd = wm->cmd_head; + + wiiuse_pressed_buttons(wm,msg); + + if(!cmd || cmd->state!=CMD_SENT || cmd->data[0]==WM_CMD_READ_DATA || cmd->data[0]==WM_CMD_CTRL_STATUS || cmd->data[0]!=msg[2] || msg[3]) { + //WIIUSE_WARNING("Unsolicited event ack: report %02x status %02x", msg[2], msg[3]); + return; + } + + //WIIUSE_DEBUG("Received ack for command %02x %02x", cmd->data[0], cmd->data[1]); + + wm->cmd_head = cmd->next; + + wm->event = WIIUSE_ACK; + cmd->state = CMD_DONE; + if(cmd->cb) cmd->cb(wm,NULL,0); + + __lwp_queue_append(&wm->cmdq,&cmd->node); + wiiuse_send_next_command(wm); +} + +static void event_status(struct wiimote_t *wm,ubyte *msg) +{ + int ir = 0; + int attachment = 0; +#ifdef HAVE_WIIUSE_SPEAKER + int speaker = 0; +#endif + //int led[4]= {0}; + struct cmd_blk_t *cmd = wm->cmd_head; + + wiiuse_pressed_buttons(wm,msg); + + wm->event = WIIUSE_STATUS; + //if(msg[2]&WM_CTRL_STATUS_BYTE1_LED_1) led[0] = 1; + //if(msg[2]&WM_CTRL_STATUS_BYTE1_LED_2) led[1] = 1; + //if(msg[2]&WM_CTRL_STATUS_BYTE1_LED_3) led[2] = 1; + //if(msg[2]&WM_CTRL_STATUS_BYTE1_LED_4) led[3] = 1; + + if((msg[2]&WM_CTRL_STATUS_BYTE1_ATTACHMENT)==WM_CTRL_STATUS_BYTE1_ATTACHMENT) attachment = 1; +#ifdef HAVE_WIIUSE_SPEAKER + if((msg[2]&WM_CTRL_STATUS_BYTE1_SPEAKER_ENABLED)==WM_CTRL_STATUS_BYTE1_SPEAKER_ENABLED) speaker = 1; +#endif + if((msg[2]&WM_CTRL_STATUS_BYTE1_IR_ENABLED)==WM_CTRL_STATUS_BYTE1_IR_ENABLED) ir = 1; + + wm->battery_level = msg[5]; + + if(!ir && WIIMOTE_IS_SET(wm,WIIMOTE_STATE_IR_INIT)) { + WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_IR_INIT); + wiiuse_set_ir(wm, 1); + goto done; + } + if(ir && !WIIMOTE_IS_SET(wm,WIIMOTE_STATE_IR)) WIIMOTE_ENABLE_STATE(wm,WIIMOTE_STATE_IR); + else if(!ir && WIIMOTE_IS_SET(wm,WIIMOTE_STATE_IR)) WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_IR); + +#ifdef HAVE_WIIUSE_SPEAKER + if(!speaker && WIIMOTE_IS_SET(wm,WIIMOTE_STATE_SPEAKER_INIT)) { + WIIMOTE_DISABLE_STATE(wm,WIIMOTE_STATE_SPEAKER_INIT); + wiiuse_set_speaker(wm,1); + goto done; + } + if(speaker && !WIIMOTE_IS_SET(wm,WIIMOTE_STATE_SPEAKER)) WIIMOTE_ENABLE_STATE(wm,WIIMOTE_STATE_SPEAKER); + else if(!speaker && WIIMOTE_IS_SET(wm,WIIMOTE_STATE_SPEAKER)) WIIMOTE_DISABLE_STATE(wm,WIIMOTE_STATE_SPEAKER); +#endif + + if(attachment) { + if(!WIIMOTE_IS_SET(wm,WIIMOTE_STATE_EXP) && !WIIMOTE_IS_SET(wm,WIIMOTE_STATE_EXP_FAILED) && !WIIMOTE_IS_SET(wm,WIIMOTE_STATE_EXP_HANDSHAKE)) { + wiiuse_handshake_expansion_start(wm); + goto done; + } + } else { + WIIMOTE_DISABLE_STATE(wm,WIIMOTE_STATE_EXP_FAILED); + if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_EXP)) { + wiiuse_disable_expansion(wm); + goto done; + } + } + wiiuse_set_report_type(wm,NULL); + +done: + if(!cmd) return; + if(!(cmd->state==CMD_SENT && cmd->data[0]==WM_CMD_CTRL_STATUS)) return; + + wm->cmd_head = cmd->next; + + cmd->state = CMD_DONE; + if(cmd->cb!=NULL) cmd->cb(wm,msg,6); + + __lwp_queue_append(&wm->cmdq,&cmd->node); + wiiuse_send_next_command(wm); +} + +static void handle_expansion(struct wiimote_t *wm,ubyte *msg) +{ + switch (wm->exp.type) { + case EXP_NUNCHUK: + nunchuk_event(&wm->exp.nunchuk, msg); + break; + case EXP_CLASSIC: + classic_ctrl_event(&wm->exp.classic, msg); + break; + case EXP_MOTION_PLUS: + motion_plus_event(&wm->exp.mp, msg); + break; + default: + break; + } +} + +/** + * @brief Called on a cycle where no significant change occurs. + * + * @param wm Pointer to a wiimote_t structure. + */ +void idle_cycle(struct wiimote_t* wm) +{ + /* + * Smooth the angles. + * + * This is done to make sure that on every cycle the orientation + * angles are smoothed. Normally when an event occurs the angles + * are updated and smoothed, but if no packet comes in then the + * angles remain the same. This means the angle wiiuse reports + * is still an old value. Smoothing needs to be applied in this + * case in order for the angle it reports to converge to the true + * angle of the device. + */ + //printf("idle_cycle()\n");/// + if (WIIUSE_USING_ACC(wm) && WIIMOTE_IS_FLAG_SET(wm, WIIUSE_SMOOTHING)) { + apply_smoothing(&wm->accel_calib, &wm->orient, SMOOTH_ROLL); + apply_smoothing(&wm->accel_calib, &wm->orient, SMOOTH_PITCH); + } +} + +void parse_event(struct wiimote_t *wm) +{ + ubyte event; + ubyte *msg; + + event = wm->event_buf[0]; + msg = wm->event_buf+1; + //printf("parse_event(%02x,%p)\n",event,msg); + switch(event) { + case WM_RPT_CTRL_STATUS: + event_status(wm,msg); + return; + case WM_RPT_READ: + event_data_read(wm,msg); + return; + case WM_RPT_ACK: + event_ack(wm,msg); + return; + case WM_RPT_BTN: + wiiuse_pressed_buttons(wm,msg); + break; + case WM_RPT_BTN_ACC: + wiiuse_pressed_buttons(wm,msg); + + wm->accel.x = (msg[2]<<2)|((msg[0]>>5)&3); + wm->accel.y = (msg[3]<<2)|((msg[1]>>4)&2); + wm->accel.z = (msg[4]<<2)|((msg[1]>>5)&2); +#ifndef GEKKO + /* calculate the remote orientation */ + calculate_orientation(&wm->accel_calib, &wm->accel, &wm->orient, WIIMOTE_IS_FLAG_SET(wm, WIIUSE_SMOOTHING)); + + /* calculate the gforces on each axis */ + calculate_gforce(&wm->accel_calib, &wm->accel, &wm->gforce); +#endif + break; + case WM_RPT_BTN_ACC_IR: + wiiuse_pressed_buttons(wm,msg); + + wm->accel.x = (msg[2]<<2)|((msg[0]>>5)&3); + wm->accel.y = (msg[3]<<2)|((msg[1]>>4)&2); + wm->accel.z = (msg[4]<<2)|((msg[1]>>5)&2); +#ifndef GEKKO + /* calculate the remote orientation */ + calculate_orientation(&wm->accel_calib, &wm->accel, &wm->orient, WIIMOTE_IS_FLAG_SET(wm, WIIUSE_SMOOTHING)); + + /* calculate the gforces on each axis */ + calculate_gforce(&wm->accel_calib, &wm->accel, &wm->gforce); +#endif + calculate_extended_ir(wm, msg+5); + break; + case WM_RPT_BTN_EXP: + wiiuse_pressed_buttons(wm,msg); + handle_expansion(wm,msg+2); + break; + case WM_RPT_BTN_ACC_EXP: + /* button - motion - expansion */ + wiiuse_pressed_buttons(wm, msg); + + wm->accel.x = (msg[2]<<2)|((msg[0]>>5)&3); + wm->accel.y = (msg[3]<<2)|((msg[1]>>4)&2); + wm->accel.z = (msg[4]<<2)|((msg[1]>>5)&2); +#ifndef GEKKO + calculate_orientation(&wm->accel_calib, &wm->accel, &wm->orient, WIIMOTE_IS_FLAG_SET(wm, WIIUSE_SMOOTHING)); + calculate_gforce(&wm->accel_calib, &wm->accel, &wm->gforce); +#endif + handle_expansion(wm, msg+5); + break; + case WM_RPT_BTN_IR_EXP: + wiiuse_pressed_buttons(wm,msg); + calculate_basic_ir(wm, msg+2); + handle_expansion(wm,msg+12); + break; + case WM_RPT_BTN_ACC_IR_EXP: + /* button - motion - ir - expansion */ + wiiuse_pressed_buttons(wm, msg); + + wm->accel.x = (msg[2]<<2)|((msg[0]>>5)&3); + wm->accel.y = (msg[3]<<2)|((msg[1]>>4)&2); + wm->accel.z = (msg[4]<<2)|((msg[1]>>5)&2); +#ifndef GEKKO + calculate_orientation(&wm->accel_calib, &wm->accel, &wm->orient, WIIMOTE_IS_FLAG_SET(wm, WIIUSE_SMOOTHING)); + calculate_gforce(&wm->accel_calib, &wm->accel, &wm->gforce); +#endif + /* ir */ + calculate_basic_ir(wm, msg+5); + + handle_expansion(wm, msg+15); + break; + default: + WIIUSE_WARNING("Unknown event, can not handle it [Code 0x%x].", event); + return; + } + + /* was there an event? */ + wm->event = WIIUSE_EVENT; +} + +/** + * @brief Find what buttons are pressed. + * + * @param wm Pointer to a wiimote_t structure. + * @param msg The message specified in the event packet. + */ +void wiiuse_pressed_buttons(struct wiimote_t* wm, ubyte* msg) { + short now; + + /* convert to big endian */ + now = BIG_ENDIAN_SHORT(*(short*)msg) & WIIMOTE_BUTTON_ALL; + + /* preserve old btns pressed */ + wm->btns_last = wm->btns; + + /* pressed now & were pressed, then held */ + wm->btns_held = (now & wm->btns); + + /* were pressed or were held & not pressed now, then released */ + wm->btns_released = ((wm->btns | wm->btns_held) & ~now); + + /* buttons pressed now */ + wm->btns = now; +} diff --git a/wii/libogc/wiiuse/events.h b/wii/libogc/wiiuse/events.h new file mode 100644 index 0000000000..a7de3380bd --- /dev/null +++ b/wii/libogc/wiiuse/events.h @@ -0,0 +1,17 @@ +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#ifndef __EVENTS_H__ +#define __EVENTS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +void wiiuse_pressed_buttons(struct wiimote_t* wm, ubyte* msg); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wii/libogc/wiiuse/io.c b/wii/libogc/wiiuse/io.c new file mode 100644 index 0000000000..c062afd14f --- /dev/null +++ b/wii/libogc/wiiuse/io.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "definitions.h" +#include "wiiuse_internal.h" +#include "nunchuk.h" +#include "classic.h" +#include "motion_plus.h" +#include "io.h" + +void wiiuse_handshake(struct wiimote_t *wm,ubyte *data,uword len) +{ + ubyte *buf = NULL; + struct accel_t *accel = &wm->accel_calib; + + //printf("wiiuse_handshake(%d,%p,%d)\n",wm->handshake_state,data,len); + + switch(wm->handshake_state) { + case 0: + wm->handshake_state++; + + wiiuse_set_leds(wm,WIIMOTE_LED_NONE,NULL); + wiiuse_status(wm,wiiuse_handshake); + return; + + case 1: + wm->handshake_state++; + buf = __lwp_wkspace_allocate(sizeof(ubyte)*8); + + if (len > 2 && data[2]&WM_CTRL_STATUS_BYTE1_ATTACHMENT) { + wiiuse_read_data(wm,buf,WM_EXP_ID,6,wiiuse_handshake); + return; + + case 2: + if (BIG_ENDIAN_LONG(*(int*)(&data[2])) == EXP_ID_CODE_CLASSIC_WIIU_PRO) { + memset(data, 0, 8); + WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_WIIU_PRO); + break; + } + buf = data; + } + + wm->handshake_state++; + wiiuse_read_data(wm,buf,WM_MEM_OFFSET_CALIBRATION,7,wiiuse_handshake); + return; + } + + accel->cal_zero.x = ((data[0]<<2)|((data[3]>>4)&3)); + accel->cal_zero.y = ((data[1]<<2)|((data[3]>>2)&3)); + accel->cal_zero.z = ((data[2]<<2)|(data[3]&3)); + + accel->cal_g.x = (((data[4]<<2)|((data[7]>>4)&3)) - accel->cal_zero.x); + accel->cal_g.y = (((data[5]<<2)|((data[7]>>2)&3)) - accel->cal_zero.y); + accel->cal_g.z = (((data[6]<<2)|(data[7]&3)) - accel->cal_zero.z); + __lwp_wkspace_free(data); + + WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE); + WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE_COMPLETE); + + wm->event = WIIUSE_CONNECT; + wiiuse_status(wm,NULL); +} + +void wiiuse_handshake_expansion_start(struct wiimote_t *wm) +{ + if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_EXP) || WIIMOTE_IS_SET(wm,WIIMOTE_STATE_EXP_FAILED) || WIIMOTE_IS_SET(wm,WIIMOTE_STATE_EXP_HANDSHAKE)) + return; + + wm->expansion_state = 0; + WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_EXP_HANDSHAKE); + wiiuse_handshake_expansion(wm, NULL, 0); +} + +void wiiuse_handshake_expansion(struct wiimote_t *wm,ubyte *data,uword len) +{ + int id; + ubyte val; + ubyte *buf = NULL; + + switch(wm->expansion_state) { + /* These two initialization writes disable the encryption */ + case 0: + wm->expansion_state = 1; + val = 0x55; + wiiuse_write_data(wm,WM_EXP_MEM_ENABLE1,&val,1,wiiuse_handshake_expansion); + break; + case 1: + wm->expansion_state = 2; + val = 0x00; + wiiuse_write_data(wm,WM_EXP_MEM_ENABLE2,&val,1,wiiuse_handshake_expansion); + break; + case 2: + wm->expansion_state = 3; + buf = __lwp_wkspace_allocate(sizeof(ubyte)*EXP_HANDSHAKE_LEN); + wiiuse_read_data(wm,buf,WM_EXP_MEM_CALIBR,EXP_HANDSHAKE_LEN,wiiuse_handshake_expansion); + break; + case 3: + if(!data || !len) return; + id = BIG_ENDIAN_LONG(*(int*)(&data[220])); + + switch(id) { + case EXP_ID_CODE_NUNCHUK: + if(!nunchuk_handshake(wm,&wm->exp.nunchuk,data,len)) return; + break; + case EXP_ID_CODE_CLASSIC_CONTROLLER: + case EXP_ID_CODE_CLASSIC_CONTROLLER_NYKOWING: + case EXP_ID_CODE_CLASSIC_CONTROLLER_NYKOWING2: + case EXP_ID_CODE_CLASSIC_CONTROLLER_NYKOWING3: + case EXP_ID_CODE_CLASSIC_CONTROLLER_GENERIC: + case EXP_ID_CODE_CLASSIC_CONTROLLER_GENERIC2: + case EXP_ID_CODE_CLASSIC_CONTROLLER_GENERIC3: + case EXP_ID_CODE_CLASSIC_CONTROLLER_GENERIC4: + case EXP_ID_CODE_CLASSIC_CONTROLLER_GENERIC5: + case EXP_ID_CODE_CLASSIC_WIIU_PRO: + if(!classic_ctrl_handshake(wm,&wm->exp.classic,data,len)) return; + break; + default: + if(!classic_ctrl_handshake(wm,&wm->exp.classic,data,len)) return; + /*WIIMOTE_DISABLE_STATE(wm,WIIMOTE_STATE_EXP_HANDSHAKE); + WIIMOTE_ENABLE_STATE(wm,WIIMOTE_STATE_EXP_FAILED); + __lwp_wkspace_free(data); + wiiuse_status(wm,NULL); + return;*/ + } + __lwp_wkspace_free(data); + + WIIMOTE_DISABLE_STATE(wm,WIIMOTE_STATE_EXP_HANDSHAKE); + WIIMOTE_ENABLE_STATE(wm,WIIMOTE_STATE_EXP); + wiiuse_set_ir_mode(wm); + wiiuse_status(wm,NULL); + break; + } +} + +void wiiuse_disable_expansion(struct wiimote_t *wm) +{ + if(!WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP)) return; + + /* tell the associated module the expansion was removed */ + switch(wm->exp.type) { + case EXP_NUNCHUK: + nunchuk_disconnected(&wm->exp.nunchuk); + wm->event = WIIUSE_NUNCHUK_REMOVED; + break; + case EXP_CLASSIC: + classic_ctrl_disconnected(&wm->exp.classic); + wm->event = WIIUSE_CLASSIC_CTRL_REMOVED; + break; + case EXP_MOTION_PLUS: + motion_plus_disconnected(&wm->exp.mp); + wm->event = WIIUSE_MOTION_PLUS_REMOVED; + break; + + default: + break; + } + + WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP); + wm->exp.type = EXP_NONE; + + wiiuse_set_ir_mode(wm); + wiiuse_status(wm,NULL); +} diff --git a/wii/libogc/wiiuse/io.h b/wii/libogc/wiiuse/io.h new file mode 100644 index 0000000000..738ceea137 --- /dev/null +++ b/wii/libogc/wiiuse/io.h @@ -0,0 +1,26 @@ +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#ifndef __IO_H__ +#define __IO_H__ + +#include "wiiuse_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void wiiuse_handshake(struct wiimote_t* wm,ubyte *data,uword len); +void wiiuse_handshake_expansion_start(struct wiimote_t *wm); +void wiiuse_handshake_expansion(struct wiimote_t *wm,ubyte *data,uword len); +void wiiuse_disable_expansion(struct wiimote_t *wm); + +int wiiuse_io_read(struct wiimote_t* wm); +int wiiuse_io_write(struct wiimote_t* wm, ubyte* buf, int len); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/wii/libogc/wiiuse/io_wii.c b/wii/libogc/wiiuse/io_wii.c new file mode 100644 index 0000000000..63e4b7b56a --- /dev/null +++ b/wii/libogc/wiiuse/io_wii.c @@ -0,0 +1,181 @@ +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#ifdef GEKKO + +#include +#include +#include +#include + +#include "definitions.h" +#include "wiiuse_internal.h" +#include "events.h" +#include "io.h" +#include "lwp_wkspace.h" + +#define MAX_COMMANDS 0x100 +#define MAX_WIIMOTES 5 + +static vu32* const _ipcReg = (u32*)0xCD000000; +static u8 *__queue_buffer[MAX_WIIMOTES] = { 0, 0, 0, 0, 0 }; + +extern void parse_event(struct wiimote_t *wm); +extern void idle_cycle(struct wiimote_t* wm); +extern void hexdump(void *d, int len); + +static __inline__ u32 ACR_ReadReg(u32 reg) +{ + return _ipcReg[reg>>2]; +} + +static __inline__ void ACR_WriteReg(u32 reg,u32 val) +{ + _ipcReg[reg>>2] = val; +} + +static s32 __wiiuse_disconnected(void *arg,struct bte_pcb *pcb,u8 err) +{ + struct wiimote_listen_t *wml = (struct wiimote_listen_t*)arg; + struct wiimote_t *wm = wml->wm; + + if(!wm) return ERR_OK; + + //printf("wiimote disconnected\n"); + WIIMOTE_DISABLE_STATE(wm, (WIIMOTE_STATE_IR|WIIMOTE_STATE_IR_INIT)); +#ifdef HAVE_WIIUSE_SPEAKER + WIIMOTE_DISABLE_STATE(wm, (WIIMOTE_STATE_SPEAKER|WIIMOTE_STATE_SPEAKER_INIT)); +#endif + WIIMOTE_DISABLE_STATE(wm, (WIIMOTE_STATE_EXP|WIIMOTE_STATE_EXP_HANDSHAKE|WIIMOTE_STATE_EXP_FAILED)); + WIIMOTE_DISABLE_STATE(wm,(WIIMOTE_STATE_CONNECTED|WIIMOTE_STATE_HANDSHAKE|WIIMOTE_STATE_HANDSHAKE_COMPLETE)); + + while(wm->cmd_head) { + __lwp_queue_append(&wm->cmdq,&wm->cmd_head->node); + wm->cmd_head = wm->cmd_head->next; + } + wm->cmd_tail = NULL; + + if(wm->event_cb) wm->event_cb(wm,WIIUSE_DISCONNECT); + + wml->wm = NULL; + return ERR_OK; +} + +static s32 __wiiuse_receive(void *arg,void *buffer,u16 len) +{ + struct wiimote_listen_t *wml = (struct wiimote_listen_t*)arg; + struct wiimote_t *wm = wml->wm; + + if(!wm || !buffer || len==0) return ERR_OK; + + //printf("__wiiuse_receive[%02x]\n",*(char*)buffer); + wm->event = WIIUSE_NONE; + + memcpy(wm->event_buf,buffer,len); + memset(&(wm->event_buf[len]),0,(MAX_PAYLOAD - len)); + parse_event(wm); + + if(wm->event!=WIIUSE_NONE) { + if(wm->event_cb) wm->event_cb(wm,wm->event); + } + + return ERR_OK; +} + +static s32 __wiiuse_connected(void *arg,struct bte_pcb *pcb,u8 err) +{ + struct wiimote_listen_t *wml = (struct wiimote_listen_t*)arg; + struct wiimote_t *wm; + + wm = wml->assign_cb(&wml->bdaddr); + + if(!wm) { + bte_disconnect(wml->sock); + return ERR_OK; + } + + wml->wm = wm; + + wm->sock = wml->sock; + wm->bdaddr = wml->bdaddr; + + //printf("__wiiuse_connected()\n"); + WIIMOTE_ENABLE_STATE(wm,(WIIMOTE_STATE_CONNECTED|WIIMOTE_STATE_HANDSHAKE)); + + wm->handshake_state = 0; + wiiuse_handshake(wm,NULL,0); + + return ERR_OK; +} + +void __wiiuse_sensorbar_enable(int enable) +{ + u32 val; + u32 level; + + level = IRQ_Disable(); + val = (ACR_ReadReg(0xc0)&~0x100); + if(enable) val |= 0x100; + ACR_WriteReg(0xc0,val); + IRQ_Restore(level); +} + +int wiiuse_register(struct wiimote_listen_t *wml, struct bd_addr *bdaddr, struct wiimote_t *(*assign_cb)(struct bd_addr *bdaddr)) +{ + s32 err; + + if(!wml || !bdaddr || !assign_cb) return 0; + + wml->wm = NULL; + wml->bdaddr = *bdaddr; + wml->sock = bte_new(); + wml->assign_cb = assign_cb; + if(wml->sock==NULL) return 0; + + bte_arg(wml->sock,wml); + bte_received(wml->sock,__wiiuse_receive); + bte_disconnected(wml->sock,__wiiuse_disconnected); + + err = bte_registerdeviceasync(wml->sock,bdaddr,__wiiuse_connected); + if(err==ERR_OK) return 1; + + return 0; +} + +void wiiuse_disconnect(struct wiimote_t *wm) +{ + if(wm==NULL || wm->sock==NULL) return; + + WIIMOTE_DISABLE_STATE(wm,WIIMOTE_STATE_CONNECTED); + bte_disconnect(wm->sock); +} + +void wiiuse_sensorbar_enable(int enable) +{ + __wiiuse_sensorbar_enable(enable); +} + + +void wiiuse_init_cmd_queue(struct wiimote_t *wm) +{ + u32 size; + + if (!__queue_buffer[wm->unid]) { + size = (MAX_COMMANDS*sizeof(struct cmd_blk_t)); + __queue_buffer[wm->unid] = __lwp_heap_allocate(&__wkspace_heap,size); + if(!__queue_buffer[wm->unid]) return; + } + + __lwp_queue_initialize(&wm->cmdq,__queue_buffer[wm->unid],MAX_COMMANDS,sizeof(struct cmd_blk_t)); +} + +int wiiuse_io_write(struct wiimote_t *wm,ubyte *buf,int len) +{ + if(wm->sock) { + return bte_senddata(wm->sock,buf,len); + } + + return ERR_CONN; +} + +#endif diff --git a/wii/libogc/wiiuse/ir.c b/wii/libogc/wiiuse/ir.c new file mode 100644 index 0000000000..f118ea6e58 --- /dev/null +++ b/wii/libogc/wiiuse/ir.c @@ -0,0 +1,837 @@ +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#include +#include +#include + +#ifndef WIN32 + #include +#endif +#ifdef GEKKO + #include +#endif +#include "definitions.h" +#include "wiiuse_internal.h" +#include "ir.h" + +static int ir_correct_for_bounds(float* x, float* y, enum aspect_t aspect, int offset_x, int offset_y); +static void ir_convert_to_vres(float* x, float* y, enum aspect_t aspect, unsigned int vx, unsigned int vy); + +/** + * @brief Get the IR sensitivity settings. + * + * @param wm Pointer to a wiimote_t structure. + * @param block1 [out] Pointer to where block1 will be set. + * @param block2 [out] Pointer to where block2 will be set. + * + * @return Returns the sensitivity level. + */ +static int get_ir_sens(struct wiimote_t* wm, char** block1, char** block2) { + if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR_SENS_LVL1)) { + *block1 = WM_IR_BLOCK1_LEVEL1; + *block2 = WM_IR_BLOCK2_LEVEL1; + return 1; + } else if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR_SENS_LVL2)) { + *block1 = WM_IR_BLOCK1_LEVEL2; + *block2 = WM_IR_BLOCK2_LEVEL2; + return 2; + } else if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR_SENS_LVL3)) { + *block1 = WM_IR_BLOCK1_LEVEL3; + *block2 = WM_IR_BLOCK2_LEVEL3; + return 3; + } else if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR_SENS_LVL4)) { + *block1 = WM_IR_BLOCK1_LEVEL4; + *block2 = WM_IR_BLOCK2_LEVEL4; + return 4; + } else if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR_SENS_LVL5)) { + *block1 = WM_IR_BLOCK1_LEVEL5; + *block2 = WM_IR_BLOCK2_LEVEL5; + return 5; + } + + *block1 = NULL; + *block2 = NULL; + return 0; +} + +static void rotate_dots(struct fdot_t* in, struct fdot_t *out, int count, float ang) { + float s, c; + int i; + + if (ang == 0) { + for (i = 0; i < count; ++i) { + out[i].x = in[i].x; + out[i].y = in[i].y; + } + return; + } + + s = sin(DEGREE_TO_RAD(ang)); + c = cos(DEGREE_TO_RAD(ang)); + + /* + * [ cos(theta) -sin(theta) ][ ir->rx ] + * [ sin(theta) cos(theta) ][ ir->ry ] + */ + + for (i = 0; i < count; ++i) { + out[i].x = (c * in[i].x) + (-s * in[i].y); + out[i].y = (s * in[i].x) + (c * in[i].y); + } +} + +/** + * @brief Correct for the IR bounding box. + * + * @param x [out] The current X, it will be updated if valid. + * @param y [out] The current Y, it will be updated if valid. + * @param aspect Aspect ratio of the screen. + * @param offset_x The X offset of the bounding box. + * @param offset_y The Y offset of the bounding box. + * + * @return Returns 1 if the point is valid and was updated. + * + * Nintendo was smart with this bit. They sacrifice a little + * precision for a big increase in usability. + */ +static int ir_correct_for_bounds(float* x, float* y, enum aspect_t aspect, int offset_x, int offset_y) { + float x0, y0; + int xs, ys; + + if (aspect == WIIUSE_ASPECT_16_9) { + xs = WM_ASPECT_16_9_X; + ys = WM_ASPECT_16_9_Y; + } else { + xs = WM_ASPECT_4_3_X; + ys = WM_ASPECT_4_3_Y; + } + + x0 = ((1024 - xs) / 2) + offset_x; + y0 = ((768 - ys) / 2) + offset_y; + + if ((*x >= x0) + && (*x <= (x0 + xs)) + && (*y >= y0) + && (*y <= (y0 + ys))) + { + *x -= offset_x; + *y -= offset_y; + + return 1; + } + + return 0; +} + + +/** + * @brief Interpolate the point to the user defined virtual screen resolution. + */ +static void ir_convert_to_vres(float* x, float* y, enum aspect_t aspect, unsigned int vx, unsigned int vy) { + int xs, ys; + + if (aspect == WIIUSE_ASPECT_16_9) { + xs = WM_ASPECT_16_9_X; + ys = WM_ASPECT_16_9_Y; + } else { + xs = WM_ASPECT_4_3_X; + ys = WM_ASPECT_4_3_Y; + } + + *x -= ((1024-xs)/2); + *y -= ((768-ys)/2); + + *x = (*x / (float)xs) * vx; + *y = (*y / (float)ys) * vy; +} + +void wiiuse_set_ir_mode(struct wiimote_t *wm) +{ + ubyte buf = 0x00; + + if(!wm) return; + if(!WIIMOTE_IS_SET(wm,WIIMOTE_STATE_IR)) return; + + if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_EXP)) buf = WM_IR_TYPE_BASIC; + else buf = WM_IR_TYPE_EXTENDED; + wiiuse_write_data(wm,WM_REG_IR_MODENUM, &buf, 1, NULL); +} + +void wiiuse_set_ir(struct wiimote_t *wm,int status) +{ + ubyte buf = 0x00; + int ir_level = 0; + char* block1 = NULL; + char* block2 = NULL; + + if(!wm) return; + + /* + * Wait for the handshake to finish first. + * When it handshake finishes and sees that + * IR is enabled, it will call this function + * again to actually enable IR. + */ + if(!WIIMOTE_IS_SET(wm,WIIMOTE_STATE_HANDSHAKE_COMPLETE)) { + WIIUSE_DEBUG("Tried to enable IR, will wait until handshake finishes.\n"); + if(status) + WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_INIT); + else + WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_IR_INIT); + return; + } + + /* + * Check to make sure a sensitivity setting is selected. + */ + ir_level = get_ir_sens(wm, &block1, &block2); + if (!ir_level) { + WIIUSE_ERROR("No IR sensitivity setting selected."); + return; + } + + if (status) { + /* if already enabled then stop */ + if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR)) { + wiiuse_status(wm,NULL); + return; + } + } else { + /* if already disabled then stop */ + if (!WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR)) { + wiiuse_status(wm,NULL); + return; + } + } + + buf = (status ? 0x04 : 0x00); + wiiuse_sendcmd(wm,WM_CMD_IR,&buf,1,NULL); + wiiuse_sendcmd(wm,WM_CMD_IR_2,&buf,1,NULL); + + if (!status) { + WIIUSE_DEBUG("Disabled IR cameras for wiimote id %i.", wm->unid); + wiiuse_status(wm,NULL); + return; + } + + /* enable IR, set sensitivity */ + buf = 0x08; + wiiuse_write_data(wm,WM_REG_IR,&buf,1,NULL); + + wiiuse_write_data(wm, WM_REG_IR_BLOCK1, (ubyte*)block1, 9, NULL); + wiiuse_write_data(wm, WM_REG_IR_BLOCK2, (ubyte*)block2, 2, NULL); + + if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_EXP)) buf = WM_IR_TYPE_BASIC; + else buf = WM_IR_TYPE_EXTENDED; + wiiuse_write_data(wm,WM_REG_IR_MODENUM, &buf, 1, NULL); + + wiiuse_status(wm,NULL); + return; +} + +/** + * @brief Set the virtual screen resolution for IR tracking. + * + * @param wm Pointer to a wiimote_t structure. + * @param status 1 to enable, 0 to disable. + */ +void wiiuse_set_ir_vres(struct wiimote_t* wm, unsigned int x, unsigned int y) { + if (!wm) return; + + wm->ir.vres[0] = (x-1); + wm->ir.vres[1] = (y-1); +} + +/** + * @brief Set the XY position for the IR cursor. + * + * @param wm Pointer to a wiimote_t structure. + */ +void wiiuse_set_ir_position(struct wiimote_t* wm, enum ir_position_t pos) { + if (!wm) return; + + wm->ir.pos = pos; + + switch (pos) { + + case WIIUSE_IR_ABOVE: + wm->ir.offset[0] = 0; + + if (wm->ir.aspect == WIIUSE_ASPECT_16_9) + wm->ir.offset[1] = WM_ASPECT_16_9_Y/2 - 70; + else if (wm->ir.aspect == WIIUSE_ASPECT_4_3) + wm->ir.offset[1] = WM_ASPECT_4_3_Y/2 - 100; + + return; + + case WIIUSE_IR_BELOW: + wm->ir.offset[0] = 0; + + if (wm->ir.aspect == WIIUSE_ASPECT_16_9) + wm->ir.offset[1] = -WM_ASPECT_16_9_Y/2 + 70; + else if (wm->ir.aspect == WIIUSE_ASPECT_4_3) + wm->ir.offset[1] = -WM_ASPECT_4_3_Y/2 + 100; + + return; + + default: + return; + }; +} + +/** + * @brief Set the aspect ratio of the TV/monitor. + * + * @param wm Pointer to a wiimote_t structure. + * @param aspect Either WIIUSE_ASPECT_16_9 or WIIUSE_ASPECT_4_3 + */ +void wiiuse_set_aspect_ratio(struct wiimote_t* wm, enum aspect_t aspect) { + if (!wm) return; + + wm->ir.aspect = aspect; + + if (aspect == WIIUSE_ASPECT_4_3) { + wm->ir.vres[0] = WM_ASPECT_4_3_X; + wm->ir.vres[1] = WM_ASPECT_4_3_Y; + } else { + wm->ir.vres[0] = WM_ASPECT_16_9_X; + wm->ir.vres[1] = WM_ASPECT_16_9_Y; + } + + /* reset the position offsets */ + wiiuse_set_ir_position(wm, wm->ir.pos); +} + + +/** + * @brief Set the IR sensitivity. + * + * @param wm Pointer to a wiimote_t structure. + * @param level 1-5, same as Wii system sensitivity setting. + * + * If the level is < 1, then level will be set to 1. + * If the level is > 5, then level will be set to 5. + */ +void wiiuse_set_ir_sensitivity(struct wiimote_t* wm, int level) { + char* block1 = NULL; + char* block2 = NULL; + + if (!wm) return; + + if (level > 5) level = 5; + if (level < 1) level = 1; + + WIIMOTE_DISABLE_STATE(wm, (WIIMOTE_STATE_IR_SENS_LVL1 | + WIIMOTE_STATE_IR_SENS_LVL2 | + WIIMOTE_STATE_IR_SENS_LVL3 | + WIIMOTE_STATE_IR_SENS_LVL4 | + WIIMOTE_STATE_IR_SENS_LVL5)); + + switch (level) { + case 1: + WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_SENS_LVL1); + break; + case 2: + WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_SENS_LVL2); + break; + case 3: + WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_SENS_LVL3); + break; + case 4: + WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_SENS_LVL4); + break; + case 5: + WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_SENS_LVL5); + break; + default: + return; + } + + if(!WIIMOTE_IS_SET(wm,WIIMOTE_STATE_IR)) return; + + /* set the new sensitivity */ + get_ir_sens(wm, &block1, &block2); + + wiiuse_write_data(wm, WM_REG_IR_BLOCK1, (ubyte*)block1, 9,NULL); + wiiuse_write_data(wm, WM_REG_IR_BLOCK2, (ubyte*)block2, 2,NULL); + + WIIUSE_DEBUG("Set IR sensitivity to level %i (unid %i)", level, wm->unid); +} + + +/** + * @brief Calculate the data from the IR spots. Basic IR mode. + * + * @param wm Pointer to a wiimote_t structure. + * @param data Data returned by the wiimote for the IR spots. + */ +void calculate_basic_ir(struct wiimote_t* wm, ubyte* data) { + struct ir_dot_t* dot = wm->ir.dot; + int i; + + dot[0].rx = 1023 - (data[0] | ((data[2] & 0x30) << 4)); + dot[0].ry = data[1] | ((data[2] & 0xC0) << 2); + + dot[1].rx = 1023 - (data[3] | ((data[2] & 0x03) << 8)); + dot[1].ry = data[4] | ((data[2] & 0x0C) << 6); + + dot[2].rx = 1023 - (data[5] | ((data[7] & 0x30) << 4)); + dot[2].ry = data[6] | ((data[7] & 0xC0) << 2); + + dot[3].rx = 1023 - (data[8] | ((data[7] & 0x03) << 8)); + dot[3].ry = data[9] | ((data[7] & 0x0C) << 6); + + /* set each IR spot to visible if spot is in range */ + for (i = 0; i < 4; ++i) { + dot[i].rx = BIG_ENDIAN_SHORT(dot[i].rx); + dot[i].ry = BIG_ENDIAN_SHORT(dot[i].ry); + + if (dot[i].ry == 1023) + dot[i].visible = 0; + else { + dot[i].visible = 1; + dot[i].size = 0; /* since we don't know the size, set it as 0 */ + } + } +#ifndef GEKKO + interpret_ir_data(&wm->ir,&wm->orient,WIIMOTE_IS_SET(wm, WIIMOTE_STATE_ACC)); +#endif +} + +/** + * @brief Calculate the data from the IR spots. Extended IR mode. + * + * @param wm Pointer to a wiimote_t structure. + * @param data Data returned by the wiimote for the IR spots. + */ +void calculate_extended_ir(struct wiimote_t* wm, ubyte* data) { + struct ir_dot_t* dot = wm->ir.dot; + int i; + + for (i = 0; i < 4; ++i) { + dot[i].rx = 1023 - (data[3*i] | ((data[(3*i)+2] & 0x30) << 4)); + dot[i].ry = data[(3*i)+1] | ((data[(3*i)+2] & 0xC0) << 2); + + dot[i].size = data[(3*i)+2]; + + dot[i].rx = BIG_ENDIAN_SHORT(dot[i].rx); + dot[i].ry = BIG_ENDIAN_SHORT(dot[i].ry); + + dot[i].size = dot[i].size&0x0f; + + /* if in range set to visible */ + if (dot[i].ry == 1023) + dot[i].visible = 0; + else + dot[i].visible = 1; + } +#ifndef GEKKO + interpret_ir_data(&wm->ir,&wm->orient,WIIMOTE_IS_SET(wm, WIIMOTE_STATE_ACC)); +#endif +} + +enum { + IR_STATE_DEAD = 0, + IR_STATE_GOOD, + IR_STATE_SINGLE, + IR_STATE_LOST, +}; + +// half-height of the IR sensor if half-width is 1 +#define HEIGHT (384.0f / 512.0f) +// maximum sensor bar slope (tan(35 degrees)) +#define MAX_SB_SLOPE 0.7f +// minimum sensor bar width in view, relative to half of the IR sensor area +#define MIN_SB_WIDTH 0.1f +// reject "sensor bars" that happen to have a dot towards the middle +#define SB_MIDDOT_REJECT 0.05f + +// physical dimensions +// cm center to center of emitters +#define SB_WIDTH 19.5f +// half-width in cm of emitters +#define SB_DOT_WIDTH 2.25f +// half-height in cm of emitters (with some tolerance) +#define SB_DOT_HEIGHT 1.0f + +#define SB_DOT_WIDTH_RATIO (SB_DOT_WIDTH / SB_WIDTH) +#define SB_DOT_HEIGHT_RATIO (SB_DOT_HEIGHT / SB_WIDTH) + +// dots further out than these coords are allowed to not be picked up +// otherwise assume something's wrong +//#define SB_OFF_SCREEN_X 0.8f +//#define SB_OFF_SCREEN_Y (0.8f * HEIGHT) + +// disable, may be doing more harm than good due to sensor pickup glitches +#define SB_OFF_SCREEN_X 0.0f +#define SB_OFF_SCREEN_Y 0.0f + +// if a point is closer than this to one of the previous SB points +// when it reappears, consider it the same instead of trying to guess +// which one of the two it is +#define SB_SINGLE_NOGUESS_DISTANCE (100.0 * 100.0) + +// width of the sensor bar in pixels at one meter from the Wiimote +#define SB_Z_COEFFICIENT 256.0f + +// distance in meters from the center of the FOV to the left or right edge, +// when the wiimote is at one meter +#define WIIMOTE_FOV_COEFFICIENT 0.39f + +#define SQUARED(x) ((x)*(x)) +#define WMAX(x,y) ((x>y)?(x):(y)) +#define WMIN(x,y) ((xroll); + + /* count visible dots and populate dots structure */ + /* dots[] is in -1..1 units for width */ + ir->num_dots = 0; + for (i = 0; i < 4; i++) { + if (ir->dot[i].visible) { + dots[ir->num_dots].x = (ir->dot[i].rx - 512.0f) / 512.0f; + dots[ir->num_dots].y = (ir->dot[i].ry - 384.0f) / 512.0f; + WIIUSE_DEBUG("IR: dot %d at (%d,%d) (%.03f,%.03f)\n",ir->num_dots,ir->dot[i].rx,ir->dot[i].ry,dots[ir->num_dots].x,dots[ir->num_dots].y); + ir->num_dots++; + } + } + + WIIUSE_DEBUG("IR: found %d dots\n",ir->num_dots); + + // nothing to track + if(ir->num_dots == 0) { + if(ir->state != IR_STATE_DEAD) + ir->state = IR_STATE_LOST; + ir->ax = 0; + ir->ay = 0; + ir->distance = 0.0f; + ir->raw_valid = 0; + return; + } + + /* ==== Find the Sensor Bar ==== */ + + // first rotate according to accelerometer orientation + rotate_dots(dots, acc_dots, ir->num_dots, orient->roll); + if(ir->num_dots > 1) { + WIIUSE_DEBUG("IR: locating sensor bar candidates\n"); + + // iterate through all dot pairs + for(first=0; first < (ir->num_dots-1); first++) { + for(second=(first+1); second < ir->num_dots; second++) { + WIIUSE_DEBUG("IR: trying dots %d and %d\n",first,second); + // order the dots leftmost first into cand + // storing both the raw dots and the accel-rotated dots + if(acc_dots[first].x > acc_dots[second].x) { + cand.dots[0] = dots[second]; + cand.dots[1] = dots[first]; + cand.acc_dots[0] = acc_dots[second]; + cand.acc_dots[1] = acc_dots[first]; + } else { + cand.dots[0] = dots[first]; + cand.dots[1] = dots[second]; + cand.acc_dots[0] = acc_dots[first]; + cand.acc_dots[1] = acc_dots[second]; + } + difference.x = cand.acc_dots[1].x - cand.acc_dots[0].x; + difference.y = cand.acc_dots[1].y - cand.acc_dots[0].y; + + // check angle + if(fabsf(difference.y / difference.x) > MAX_SB_SLOPE) + continue; + WIIUSE_DEBUG("IR: passed angle check\n"); + // rotate to the true sensor bar angle + cand.off_angle = -RAD_TO_DEGREE(atan2(difference.y, difference.x)); + cand.angle = cand.off_angle + orient->roll; + rotate_dots(cand.dots, cand.rot_dots, 2, cand.angle); + WIIUSE_DEBUG("IR: off_angle: %.02f, angle: %.02f\n", cand.off_angle, cand.angle); + // recalculate x distance - y should be zero now, so ignore it + difference.x = cand.rot_dots[1].x - cand.rot_dots[0].x; + + // check distance + if(difference.x < MIN_SB_WIDTH) + continue; + // middle dot check. If there's another source somewhere in the + // middle of this candidate, then this can't be a sensor bar + + for(i=0; inum_dots; i++) { + float wadj, hadj; + struct fdot_t tdot; + if(i==first || i==second) continue; + hadj = SB_DOT_HEIGHT_RATIO * difference.x; + wadj = SB_DOT_WIDTH_RATIO * difference.x; + rotate_dots(&dots[i], &tdot, 1, cand.angle); + if( ((cand.rot_dots[0].x + wadj) < tdot.x) && + ((cand.rot_dots[1].x - wadj) > tdot.x) && + ((cand.rot_dots[0].y + hadj) > tdot.y) && + ((cand.rot_dots[0].y - hadj) < tdot.y)) + break; + } + // failed middle dot check + if(i < ir->num_dots) continue; + WIIUSE_DEBUG("IR: passed middle dot check\n"); + + cand.score = 1 / (cand.rot_dots[1].x - cand.rot_dots[0].x); + + // we have a candidate, store it + WIIUSE_DEBUG("IR: new candidate %d\n",num_candidates); + candidates[num_candidates++] = cand; + } + } + } + + if(num_candidates == 0) { + int closest = -1; + int closest_to = 0; + float best = 999.0f; + float d; + float dx[2]; + struct sb_t sbx[2]; + // no sensor bar candidates, try to work with a lone dot + WIIUSE_DEBUG("IR: no candidates\n"); + switch(ir->state) { + case IR_STATE_DEAD: + WIIUSE_DEBUG("IR: we're dead\n"); + // we've never seen a sensor bar before, so we're screwed + ir->ax = 0.0f; + ir->ay = 0.0f; + ir->distance = 0.0f; + ir->raw_valid = 0; + return; + case IR_STATE_GOOD: + case IR_STATE_SINGLE: + case IR_STATE_LOST: + WIIUSE_DEBUG("IR: trying to keep track of single dot\n"); + // try to find the dot closest to the previous sensor bar position + for(i=0; inum_dots; i++) { + WIIUSE_DEBUG("IR: checking dot %d (%.02f, %.02f)\n",i, acc_dots[i].x,acc_dots[i].y); + for(j=0; j<2; j++) { + WIIUSE_DEBUG(" to dot %d (%.02f, %.02f)\n",j, ir->sensorbar.acc_dots[j].x,ir->sensorbar.acc_dots[j].y); + d = SQUARED(acc_dots[i].x - ir->sensorbar.acc_dots[j].x); + d += SQUARED(acc_dots[i].y - ir->sensorbar.acc_dots[j].y); + if(d < best) { + best = d; + closest_to = j; + closest = i; + } + } + } + WIIUSE_DEBUG("IR: closest dot is %d to %d\n",closest,closest_to); + if(ir->state != IR_STATE_LOST || best < SB_SINGLE_NOGUESS_DISTANCE) { + // now work out where the other dot would be, in the acc frame + sb.acc_dots[closest_to] = acc_dots[closest]; + sb.acc_dots[closest_to^1].x = ir->sensorbar.acc_dots[closest_to^1].x - ir->sensorbar.acc_dots[closest_to].x + acc_dots[closest].x; + sb.acc_dots[closest_to^1].y = ir->sensorbar.acc_dots[closest_to^1].y - ir->sensorbar.acc_dots[closest_to].y + acc_dots[closest].y; + // get the raw frame + rotate_dots(sb.acc_dots, sb.dots, 2, -orient->roll); + if((fabsf(sb.dots[closest_to^1].x) < SB_OFF_SCREEN_X) && (fabsf(sb.dots[closest_to^1].y) < SB_OFF_SCREEN_Y)) { + // this dot should be visible but isn't, since the candidate section failed. + // fall through and try to pick out the sensor bar without previous information + WIIUSE_DEBUG("IR: dot falls on screen, falling through\n"); + } else { + // calculate the rotated dots frame + // angle tends to drift, so recalculate + sb.off_angle = -RAD_TO_DEGREE(atan2(sb.acc_dots[1].y - sb.acc_dots[0].y, sb.acc_dots[1].x - sb.acc_dots[0].x)); + sb.angle = ir->sensorbar.off_angle + orient->roll; + rotate_dots(sb.acc_dots, sb.rot_dots, 2, ir->sensorbar.off_angle); + WIIUSE_DEBUG("IR: kept track of single dot\n"); + break; + } + } else { + WIIUSE_DEBUG("IR: lost the dot and new one is too far away\n"); + } + // try to find the dot closest to the sensor edge + WIIUSE_DEBUG("IR: trying to find best dot\n"); + for(i=0; inum_dots; i++) { + d = WMIN(1.0f - fabsf(dots[i].x), HEIGHT - fabsf(dots[i].y)); + if(d < best) { + best = d; + closest = i; + } + } + WIIUSE_DEBUG("IR: best dot: %d\n",closest); + // now try it as both places in the sensor bar + // and pick the one that places the other dot furthest off-screen + for(i=0; i<2; i++) { + sbx[i].acc_dots[i] = acc_dots[closest]; + sbx[i].acc_dots[i^1].x = ir->sensorbar.acc_dots[i^1].x - ir->sensorbar.acc_dots[i].x + acc_dots[closest].x; + sbx[i].acc_dots[i^1].y = ir->sensorbar.acc_dots[i^1].y - ir->sensorbar.acc_dots[i].y + acc_dots[closest].y; + rotate_dots(sbx[i].acc_dots, sbx[i].dots, 2, -orient->roll); + dx[i] = WMAX(fabsf(sbx[i].dots[i^1].x),fabsf(sbx[i].dots[i^1].y / HEIGHT)); + } + if(dx[0] > dx[1]) { + WIIUSE_DEBUG("IR: dot is LEFT: %.02f > %.02f\n",dx[0],dx[1]); + sb = sbx[0]; + } else { + WIIUSE_DEBUG("IR: dot is RIGHT: %.02f < %.02f\n",dx[0],dx[1]); + sb = sbx[1]; + } + // angle tends to drift, so recalculate + sb.off_angle = -RAD_TO_DEGREE(atan2(sb.acc_dots[1].y - sb.acc_dots[0].y, sb.acc_dots[1].x - sb.acc_dots[0].x)); + sb.angle = ir->sensorbar.off_angle + orient->roll; + rotate_dots(sb.acc_dots, sb.rot_dots, 2, ir->sensorbar.off_angle); + WIIUSE_DEBUG("IR: found new dot to track\n"); + break; + } + sb.score = 0; + ir->state = IR_STATE_SINGLE; + } else { + int bestidx = 0; + float best = 0.0f; + WIIUSE_DEBUG("IR: finding best candidate\n"); + // look for the best candidate + // for now, the formula is simple: pick the one with the smallest distance + for(i=0; i best) { + bestidx = i; + best = candidates[i].score; + } + } + WIIUSE_DEBUG("IR: best candidate: %d\n",bestidx); + sb = candidates[bestidx]; + ir->state = IR_STATE_GOOD; + } + + ir->raw_valid = 1; + ir->ax = ((sb.rot_dots[0].x + sb.rot_dots[1].x) / 2) * 512.0 + 512.0; + ir->ay = ((sb.rot_dots[0].y + sb.rot_dots[1].y) / 2) * 512.0 + 384.0; + ir->sensorbar = sb; + ir->distance = (sb.rot_dots[1].x - sb.rot_dots[0].x) * 512.0; + +} + +#define SMOOTH_IR_RADIUS 8.0f +#define SMOOTH_IR_SPEED 0.25f +#define SMOOTH_IR_DEADZONE 2.5f + +/** + * @brief Smooth the IR pointer position + * + * @param ir Pointer to an ir_t structure. + */ +void apply_ir_smoothing(struct ir_t *ir) { + f32 dx, dy, d, theta; + + WIIUSE_DEBUG("Smooth: OK (%.02f, %.02f) LAST (%.02f, %.02f) ", ir->ax, ir->ay, ir->sx, ir->sy); + dx = ir->ax - ir->sx; + dy = ir->ay - ir->sy; + d = sqrtf(dx*dx + dy*dy); + if (d > SMOOTH_IR_DEADZONE) { + if (d < SMOOTH_IR_RADIUS) { + WIIUSE_DEBUG("INSIDE\n"); + ir->sx += dx * SMOOTH_IR_SPEED; + ir->sy += dy * SMOOTH_IR_SPEED; + } else { + WIIUSE_DEBUG("OUTSIDE\n"); + theta = atan2f(dy, dx); + ir->sx = ir->ax - cosf(theta) * SMOOTH_IR_RADIUS; + ir->sy = ir->ay - sinf(theta) * SMOOTH_IR_RADIUS; + } + } else { + WIIUSE_DEBUG("DEADZONE\n"); + } +} + +// max number of errors before cooked data drops out +#define ERROR_MAX_COUNT 8 +// max number of glitches before cooked data updates +#define GLITCH_MAX_COUNT 5 +// squared delta over which we consider something a glitch +#define GLITCH_DIST (150.0f * 150.0f) + +/** + * @brief Interpret IR data into more user friendly variables. + * + * @param ir Pointer to an ir_t structure. + * @param orient Pointer to an orient_t structure. + */ +void interpret_ir_data(struct ir_t* ir, struct orient_t *orient) { + + float x,y; + float d; + + find_sensorbar(ir, orient); + + if(ir->raw_valid) { + ir->angle = ir->sensorbar.angle; + ir->z = SB_Z_COEFFICIENT / ir->distance; + orient->yaw = calc_yaw(ir); + if(ir->error_cnt >= ERROR_MAX_COUNT) { + ir->sx = ir->ax; + ir->sy = ir->ay; + ir->glitch_cnt = 0; + } else { + d = SQUARED(ir->ax - ir->sx) + SQUARED(ir->ay - ir->sy); + if(d > GLITCH_DIST) { + if(ir->glitch_cnt > GLITCH_MAX_COUNT) { + apply_ir_smoothing(ir); + ir->glitch_cnt = 0; + } else { + ir->glitch_cnt++; + } + } else { + ir->glitch_cnt = 0; + apply_ir_smoothing(ir); + } + } + ir->smooth_valid = 1; + ir->error_cnt = 0; + } else { + if(ir->error_cnt >= ERROR_MAX_COUNT) { + ir->smooth_valid = 0; + } else { + ir->smooth_valid = 1; + ir->error_cnt++; + } + } + if(ir->smooth_valid) { + x = ir->sx; + y = ir->sy; + if (ir_correct_for_bounds(&x, &y, ir->aspect, ir->offset[0], ir->offset[1])) { + ir_convert_to_vres(&x, &y, ir->aspect, ir->vres[0], ir->vres[1]); + ir->x = x; + ir->y = y; + ir->valid = 1; + } else { + ir->valid = 0; + } + } else { + ir->valid = 0; + } +} + +/** + * @brief Calculate yaw given the IR data. + * + * @param ir IR data structure. + */ +float calc_yaw(struct ir_t* ir) { + float x; + + x = ir->ax - 512; + x *= WIIMOTE_FOV_COEFFICIENT / 512.0; + + return RAD_TO_DEGREE( atanf(x) ); +} + diff --git a/wii/libogc/wiiuse/ir.h b/wii/libogc/wiiuse/ir.h new file mode 100644 index 0000000000..0ac683b8d1 --- /dev/null +++ b/wii/libogc/wiiuse/ir.h @@ -0,0 +1,25 @@ +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#ifndef __IR_H__ +#define __IR_H__ + +#include "wiiuse_internal.h" + +#define WII_VRES_X 560 +#define WII_VRES_Y 340 + +#ifdef __cplusplus +extern "C" { +#endif + +void calculate_basic_ir(struct wiimote_t* wm, ubyte* data); +void calculate_extended_ir(struct wiimote_t* wm, ubyte* data); +float calc_yaw(struct ir_t* ir); +void interpret_ir_data(struct ir_t* ir, struct orient_t *orient); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wii/libogc/wiiuse/license_libogc.txt b/wii/libogc/wiiuse/license_libogc.txt new file mode 100644 index 0000000000..a149fce1ce --- /dev/null +++ b/wii/libogc/wiiuse/license_libogc.txt @@ -0,0 +1,641 @@ +This license is valid ONLY for libogc and what this license +defines as a "covered work" or a "combined work" with libogc. + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an officialder +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + 18. Exceptions + + Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole combination. + + As a special exception, the copyright holders of this library give +you permission to link this library with independent modules to produce +an executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from or +based on this library. If you modify this library, you may extend this +exception to your version of the library, but you are not obligated to +do so. If you do not wish to do so, delete this exception statement from +your version. + diff --git a/wii/libogc/wiiuse/motion_plus.c b/wii/libogc/wiiuse/motion_plus.c new file mode 100644 index 0000000000..8d37d7063f --- /dev/null +++ b/wii/libogc/wiiuse/motion_plus.c @@ -0,0 +1,95 @@ +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#include +#include +#include +#include + +#ifdef WIN32 + #include +#endif + +#include "definitions.h" +#include "wiiuse_internal.h" +#include "dynamics.h" +#include "events.h" +#include "io.h" + +void wiiuse_motion_plus_check(struct wiimote_t *wm,ubyte *data,uword len) +{ + u32 val; + if(data == NULL) + { + wiiuse_read_data(wm, wm->motion_plus_id, WM_EXP_ID, 6, wiiuse_motion_plus_check); + } + else + { + WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP); + WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP_FAILED); + WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP_HANDSHAKE); + val = (data[3] << 16) | (data[2] << 24) | (data[4] << 8) | data[5]; + if(val == EXP_ID_CODE_MOTION_PLUS) + { + /* handshake done */ + wm->event = WIIUSE_MOTION_PLUS_ACTIVATED; + wm->exp.type = EXP_MOTION_PLUS; + + WIIMOTE_ENABLE_STATE(wm,WIIMOTE_STATE_EXP); + wiiuse_set_ir_mode(wm); + } + } +} + +static void wiiuse_set_motion_plus_clear2(struct wiimote_t *wm,ubyte *data,uword len) +{ + WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP); + WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP_FAILED); + WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP_HANDSHAKE); + wiiuse_set_ir_mode(wm); + wiiuse_status(wm,NULL); +} + +static void wiiuse_set_motion_plus_clear1(struct wiimote_t *wm,ubyte *data,uword len) +{ + ubyte val = 0x00; + wiiuse_write_data(wm,WM_EXP_MEM_ENABLE1,&val,1,wiiuse_set_motion_plus_clear2); +} + + +void wiiuse_set_motion_plus(struct wiimote_t *wm, int status) +{ + ubyte val; + + if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_EXP_HANDSHAKE)) + return; + + WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_EXP_HANDSHAKE); + if(status) + { + val = 0x04; + wiiuse_write_data(wm,WM_EXP_MOTION_PLUS_ENABLE,&val,1,wiiuse_motion_plus_check); + } + else + { + wiiuse_disable_expansion(wm); + val = 0x55; + wiiuse_write_data(wm,WM_EXP_MEM_ENABLE1,&val,1,wiiuse_set_motion_plus_clear1); + } +} + +void motion_plus_disconnected(struct motion_plus_t* mp) +{ + WIIUSE_DEBUG("Motion plus disconnected"); + memset(mp, 0, sizeof(struct motion_plus_t)); +} + +void motion_plus_event(struct motion_plus_t* mp, ubyte* msg) +{ + mp->rx = ((msg[5] & 0xFC) << 6) | msg[2]; // Pitch + mp->ry = ((msg[4] & 0xFC) << 6) | msg[1]; // Roll + mp->rz = ((msg[3] & 0xFC) << 6) | msg[0]; // Yaw + + mp->ext = msg[4] & 0x1; + mp->status = (msg[3] & 0x3) | ((msg[4] & 0x2) << 1); // roll, yaw, pitch +} diff --git a/wii/libogc/wiiuse/motion_plus.h b/wii/libogc/wiiuse/motion_plus.h new file mode 100644 index 0000000000..628d9c6492 --- /dev/null +++ b/wii/libogc/wiiuse/motion_plus.h @@ -0,0 +1,26 @@ +/** + * @file + * @brief Motion plus extension + */ + +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#ifndef MOTION_PLUS_H_INCLUDED +#define MOTION_PLUS_H_INCLUDED + +#include "wiiuse_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void motion_plus_disconnected(struct motion_plus_t* mp); + +void motion_plus_event(struct motion_plus_t* mp, ubyte* msg); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wii/libogc/wiiuse/nunchuk.c b/wii/libogc/wiiuse/nunchuk.c new file mode 100644 index 0000000000..468db73a31 --- /dev/null +++ b/wii/libogc/wiiuse/nunchuk.c @@ -0,0 +1,157 @@ +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#include +#include +#include +#include + +#include "dynamics.h" +#include "definitions.h" +#include "wiiuse_internal.h" +#include "nunchuk.h" +#include "io.h" + +/** + * @brief Find what buttons are pressed. + * + * @param nc Pointer to a nunchuk_t structure. + * @param msg The message byte specified in the event packet. + */ +static void nunchuk_pressed_buttons(struct nunchuk_t* nc, ubyte now) { + /* message is inverted (0 is active, 1 is inactive) */ + now = ~now & NUNCHUK_BUTTON_ALL; + + /* preserve old btns pressed */ + nc->btns_last = nc->btns; + + /* pressed now & were pressed, then held */ + nc->btns_held = (now & nc->btns); + + /* were pressed or were held & not pressed now, then released */ + nc->btns_released = ((nc->btns | nc->btns_held) & ~now); + + /* buttons pressed now */ + nc->btns = now; +} + +int nunchuk_handshake(struct wiimote_t *wm,struct nunchuk_t *nc,ubyte *data,uword len) +{ + //int i; + int offset = 0; + + nc->btns = 0; + nc->btns_held = 0; + nc->btns_released = 0; + nc->flags = &wm->flags; + nc->accel_calib = wm->accel_calib; + + //for(i=0;iaccel_calib.cal_zero.x = (data[offset + 0]<<2)|((data[offset + 3]>>4)&3); + nc->accel_calib.cal_zero.y = (data[offset + 1]<<2)|((data[offset + 3]>>2)&3); + nc->accel_calib.cal_zero.z = (data[offset + 2]<<2)|(data[offset + 3]&3); + nc->accel_calib.cal_g.x = (data[offset + 4]<<2)|((data[offset + 7]>>4)&3); + nc->accel_calib.cal_g.y = (data[offset + 5]<<2)|((data[offset + 7]>>2)&3); + nc->accel_calib.cal_g.z = (data[offset + 6]<<2)|(data[offset + 7]&3); + nc->js.max.x = data[offset + 8]; + nc->js.min.x = data[offset + 9]; + nc->js.center.x = data[offset + 10]; + nc->js.max.y = data[offset + 11]; + nc->js.min.y = data[offset + 12]; + nc->js.center.y = data[offset + 13]; + + // set to defaults (averages from 5 nunchuks) if calibration data is invalid + if(nc->accel_calib.cal_zero.x == 0) + nc->accel_calib.cal_zero.x = 499; + if(nc->accel_calib.cal_zero.y == 0) + nc->accel_calib.cal_zero.y = 509; + if(nc->accel_calib.cal_zero.z == 0) + nc->accel_calib.cal_zero.z = 507; + if(nc->accel_calib.cal_g.x == 0) + nc->accel_calib.cal_g.x = 703; + if(nc->accel_calib.cal_g.y == 0) + nc->accel_calib.cal_g.y = 709; + if(nc->accel_calib.cal_g.z == 0) + nc->accel_calib.cal_g.z = 709; + if(nc->js.max.x == 0) + nc->js.max.x = 223; + if(nc->js.min.x == 0) + nc->js.min.x = 27; + if(nc->js.center.x == 0) + nc->js.center.x = 126; + if(nc->js.max.y == 0) + nc->js.max.y = 222; + if(nc->js.min.y == 0) + nc->js.min.y = 30; + if(nc->js.center.y == 0) + nc->js.center.y = 131; + + wm->event = WIIUSE_NUNCHUK_INSERTED; + wm->exp.type = EXP_NUNCHUK; + + return 1; +} + +/** + * @brief The nunchuk disconnected. + * + * @param nc A pointer to a nunchuk_t structure. + */ +void nunchuk_disconnected(struct nunchuk_t* nc) +{ + //printf("nunchuk_disconnected()\n"); + memset(nc, 0, sizeof(struct nunchuk_t)); +} + +/** + * @brief Handle nunchuk event. + * + * @param nc A pointer to a nunchuk_t structure. + * @param msg The message specified in the event packet. + */ +void nunchuk_event(struct nunchuk_t* nc, ubyte* msg) { + //int i; + + /* decrypt data */ + /* + for (i = 0; i < 6; ++i) + msg[i] = (msg[i] ^ 0x17) + 0x17; + */ + /* get button states */ + nunchuk_pressed_buttons(nc, msg[5]); + + nc->js.pos.x = msg[0]; + nc->js.pos.y = msg[1]; + + /* extend min and max values to physical range of motion */ + if (nc->js.center.x) { + if (nc->js.min.x > nc->js.pos.x) nc->js.min.x = nc->js.pos.x; + if (nc->js.max.x < nc->js.pos.x) nc->js.max.x = nc->js.pos.x; + } + if (nc->js.center.y) { + if (nc->js.min.y > nc->js.pos.y) nc->js.min.y = nc->js.pos.y; + if (nc->js.max.y < nc->js.pos.y) nc->js.max.y = nc->js.pos.y; + } + +#ifndef GEKKO + /* calculate joystick state */ + calc_joystick_state(&nc->js, nc->js.pos.x, nc->js.pos.y); +#endif + /* calculate orientation */ + nc->accel.x = (msg[2]<<2) + ((msg[5]>>2)&3); + nc->accel.y = (msg[3]<<2) + ((msg[5]>>4)&3); + nc->accel.z = (msg[4]<<2) + ((msg[5]>>6)&3); +#ifndef GEKKO + calculate_orientation(&nc->accel_calib, &nc->accel, &nc->orient, NUNCHUK_IS_FLAG_SET(nc, WIIUSE_SMOOTHING)); + calculate_gforce(&nc->accel_calib, &nc->accel, &nc->gforce); +#endif +} + diff --git a/wii/libogc/wiiuse/nunchuk.h b/wii/libogc/wiiuse/nunchuk.h new file mode 100644 index 0000000000..7545aa7b6e --- /dev/null +++ b/wii/libogc/wiiuse/nunchuk.h @@ -0,0 +1,21 @@ +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#ifndef __NUNCHUK_H__ +#define __NUNCHUK_H__ + +#include "wiiuse_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int nunchuk_handshake(struct wiimote_t* wm, struct nunchuk_t* nc, ubyte* data, uword len); +void nunchuk_disconnected(struct nunchuk_t* nc); +void nunchuk_event(struct nunchuk_t* nc, ubyte* msg); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wii/libogc/wiiuse/os.h b/wii/libogc/wiiuse/os.h new file mode 100644 index 0000000000..16571047cd --- /dev/null +++ b/wii/libogc/wiiuse/os.h @@ -0,0 +1,31 @@ +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#ifndef __OS_H__ +#define __OS_H__ + +#ifdef WIN32 + /* windows */ + #define isnan(x) _isnan(x) + #define isinf(x) !_finite(x) + + /* disable warnings I don't care about */ + #pragma warning(disable:4244) /* possible loss of data conversion */ + #pragma warning(disable:4273) /* inconsistent dll linkage */ + #pragma warning(disable:4217) +#else + /* nix/gekko */ + #ifdef GEKKO + #include + #include + #include + #include "network.h" + #include + #include + #include + #include + #else + #endif +#endif + +#endif diff --git a/wii/libogc/wiiuse/speaker.c b/wii/libogc/wiiuse/speaker.c new file mode 100644 index 0000000000..43ca994275 --- /dev/null +++ b/wii/libogc/wiiuse/speaker.c @@ -0,0 +1,145 @@ +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif +#ifdef GEKKO +#include +#endif +#include "definitions.h" +#include "wiiuse_internal.h" +#include "speaker.h" + +#define WENCMIN(a,b) ((a)>(b)?(b):(a)) +#define ABS(x) ((s32)(x)>0?(s32)(x):-((s32)(x))) + +static const int yamaha_indexscale[] = { + 230, 230, 230, 230, 307, 409, 512, 614, + 230, 230, 230, 230, 307, 409, 512, 614 +}; + +static const int yamaha_difflookup[] = { + 1, 3, 5, 7, 9, 11, 13, 15, + -1, -3, -5, -7, -9, -11, -13, -15 +}; + +static ubyte __wiiuse_speaker_vol = 0x40; +static ubyte __wiiuse_speaker_defconf[7] = { 0x00,0x00,0xD0,0x07,0x40,0x0C,0x0E }; + +static __inline__ short wenc_clip_short(int a) +{ + if((a+32768)&~65535) return (a>>31)^32767; + else return a; +} + +static __inline__ int wenc_clip(int a,int amin,int amax) +{ + if(aamax) return amax; + else return a; +} + +ubyte wencdata(WENCStatus *info,short sample) +{ + int nibble,delta; + + if(!info->step) { + info->predictor = 0; + info->step = 127; + } + + delta = sample - info->predictor; + nibble = WENCMIN(7,(ABS(delta)*4)/info->step) + ((delta<0)*8); + + info->predictor += ((info->step*yamaha_difflookup[nibble])/8); + info->predictor = wenc_clip_short(info->predictor); + info->step = (info->step*yamaha_indexscale[nibble])>>8; + info->step = wenc_clip(info->step,127,24576); + + return nibble; +} + +void wiiuse_set_speaker(struct wiimote_t *wm,int status) +{ + ubyte conf[7]; + ubyte buf = 0x00; + + if(!wm) return; + + if(!WIIMOTE_IS_SET(wm,WIIMOTE_STATE_HANDSHAKE_COMPLETE)) { + WIIUSE_DEBUG("Tried to enable speaker, will wait until handshake finishes.\n"); + if(status) + WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_SPEAKER_INIT); + else + WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_SPEAKER_INIT); + return; + } + + if(status) { + if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_SPEAKER)) { + wiiuse_status(wm,NULL); + return; + } + } else { + if(!WIIMOTE_IS_SET(wm,WIIMOTE_STATE_SPEAKER)) { + wiiuse_status(wm,NULL); + return; + } + } + + + buf = 0x04; + wiiuse_sendcmd(wm,WM_CMD_SPEAKER_MUTE,&buf,1,NULL); + + if (!status) { + WIIUSE_DEBUG("Disabled speaker for wiimote id %i.", wm->unid); + + buf = 0x01; + wiiuse_write_data(wm,WM_REG_SPEAKER_REG1,&buf,1,NULL); + + buf = 0x00; + wiiuse_write_data(wm,WM_REG_SPEAKER_REG3,&buf,1,NULL); + + buf = 0x00; + wiiuse_sendcmd(wm,WM_CMD_SPEAKER_ENABLE,&buf,1,NULL); + + wiiuse_status(wm,NULL); + return; + } + + memcpy(conf,__wiiuse_speaker_defconf,7); + + buf = 0x04; + wiiuse_sendcmd(wm,WM_CMD_SPEAKER_ENABLE,&buf,1,NULL); + + buf = 0x01; + wiiuse_write_data(wm,WM_REG_SPEAKER_REG3,&buf,1,NULL); + + buf = 0x08; + wiiuse_write_data(wm,WM_REG_SPEAKER_REG1,&buf,1,NULL); + + conf[2] = 0xd0; + conf[3] = 0x07; + conf[4] = __wiiuse_speaker_vol; + wiiuse_write_data(wm,WM_REG_SPEAKER_BLOCK,conf,7,NULL); + + buf = 0x01; + wiiuse_write_data(wm,WM_REG_SPEAKER_REG2,&buf,1,NULL); + + buf = 0x00; + wiiuse_sendcmd(wm,WM_CMD_SPEAKER_MUTE,&buf,1,NULL); + + wiiuse_status(wm,NULL); + return; +} + +void set_speakervol(struct wiimote_t *wm,ubyte vol) +{ + __wiiuse_speaker_vol = vol; +} diff --git a/wii/libogc/wiiuse/speaker.h b/wii/libogc/wiiuse/speaker.h new file mode 100644 index 0000000000..b7b9229126 --- /dev/null +++ b/wii/libogc/wiiuse/speaker.h @@ -0,0 +1,33 @@ +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#ifndef __SPEAKER_H__ +#define __SPEAKER_H__ + +#include "wiiuse_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _wencstatus +{ + s32 predictor; + s16 step_index; + s32 step; + s32 prev_sample; + s16 sample1; + s16 sample2; + s32 coeff1; + s32 coeff2; + s32 idelta; +} WENCStatus; + +u8 wencdata(WENCStatus *info,s16 sample); +void set_speakervol(struct wiimote_t *wm,ubyte vol); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wii/libogc/wiiuse/wiiuse.c b/wii/libogc/wiiuse/wiiuse.c new file mode 100644 index 0000000000..73e813063a --- /dev/null +++ b/wii/libogc/wiiuse/wiiuse.c @@ -0,0 +1,337 @@ +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#include +#include +#include + +#ifndef WIN32 + #include +#else + #include +#endif + +#include "definitions.h" +#include "wiiuse_internal.h" +#include "io.h" + +static struct wiimote_t** __wm = NULL; + +void wiiuse_send_next_command(struct wiimote_t *wm) +{ + struct cmd_blk_t *cmd = wm->cmd_head; + + if(!wm || !WIIMOTE_IS_CONNECTED(wm)) return; + + if(!cmd) return; + if(cmd->state!=CMD_READY) return; + + cmd->state = CMD_SENT; +#ifdef HAVE_WIIUSE_RUMBLE + if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_RUMBLE)) cmd->data[1] |= 0x01; +#endif + + //WIIUSE_DEBUG("Sending command: %02x %02x", cmd->data[0], cmd->data[1]); + wiiuse_io_write(wm,cmd->data,cmd->len); +} + +static inline void __wiiuse_push_command(struct wiimote_t *wm,struct cmd_blk_t *cmd) +{ + uint level; + + if(!wm || !cmd) return; + + cmd->next = NULL; + cmd->state = CMD_READY; + + _CPU_ISR_Disable(level); + if(wm->cmd_head==NULL) { + wm->cmd_head = wm->cmd_tail = cmd; + wiiuse_send_next_command(wm); + } else { + wm->cmd_tail->next = cmd; + wm->cmd_tail = cmd; + } + _CPU_ISR_Restore(level); +} + +#ifndef GEKKO +struct wiimote_t** wiiuse_init(int wiimotes) { +#else +extern void __wiiuse_sensorbar_enable(int enable); +struct wiimote_t** wiiuse_init(int wiimotes, wii_event_cb event_cb) { +#endif + int i = 0; + + if (!wiimotes) + return NULL; + + if (!__wm) { + __wm = __lwp_heap_allocate(&__wkspace_heap, sizeof(struct wiimote_t*) * wiimotes); + if(!__wm) return NULL; + memset(__wm, 0, sizeof(struct wiimote_t*) * wiimotes); + } + + for (i = 0; i < wiimotes; ++i) { + if(!__wm[i]) + __wm[i] = __lwp_heap_allocate(&__wkspace_heap, sizeof(struct wiimote_t)); + + memset(__wm[i], 0, sizeof(struct wiimote_t)); + __wm[i]->unid = i; + + #if defined(WIN32) + __wm[i]->dev_handle = 0; + __wm[i]->stack = WIIUSE_STACK_UNKNOWN; + __wm[i]->normal_timeout = WIIMOTE_DEFAULT_TIMEOUT; + __wm[i]->exp_timeout = WIIMOTE_EXP_TIMEOUT; + __wm[i]->timeout = __wm[i]->normal_timeout; + #elif defined(GEKKO) + __wm[i]->sock = NULL; + __wm[i]->bdaddr = *BD_ADDR_ANY; + __wm[i]->event_cb = event_cb; + wiiuse_init_cmd_queue(__wm[i]); + #elif defined(unix) + __wm[i]->bdaddr = *BDADDR_ANY; + __wm[i]->out_sock = -1; + __wm[i]->in_sock = -1; + #endif + + __wm[i]->state = WIIMOTE_INIT_STATES; + __wm[i]->flags = WIIUSE_INIT_FLAGS; + + __wm[i]->event = WIIUSE_NONE; + + __wm[i]->exp.type = EXP_NONE; + + wiiuse_set_aspect_ratio(__wm[i], WIIUSE_ASPECT_4_3); + wiiuse_set_ir_position(__wm[i], WIIUSE_IR_ABOVE); + + __wm[i]->accel_calib.st_alpha = WIIUSE_DEFAULT_SMOOTH_ALPHA; + } + + return __wm; +} + +/** + * @brief Set flags for the specified wiimote. + * + * @param wm Pointer to a wiimote_t structure. + * @param enable Flags to enable. + * @param disable Flags to disable. + * + * @return The flags set after 'enable' and 'disable' have been applied. + * + * The values 'enable' and 'disable' may be any flags OR'ed together. + * Flags are defined in wiiuse.h. + */ +int wiiuse_set_flags(struct wiimote_t* wm, int enable, int disable) { + if (!wm) return 0; + + /* remove mutually exclusive flags */ + enable &= ~disable; + disable &= ~enable; + + wm->flags |= enable; + wm->flags &= ~disable; + + return wm->flags; +} + +/** + * @brief Set if the wiimote should report motion sensing. + * + * @param wm Pointer to a wiimote_t structure. + * @param status 1 to enable, 0 to disable. + * + * Since reporting motion sensing sends a lot of data, + * the wiimote saves power by not transmitting it + * by default. + */ +void wiiuse_motion_sensing(struct wiimote_t* wm, int status) +{ + if (status) { + if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_ACC)) return; + WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_ACC); + } else { + if(!WIIMOTE_IS_SET(wm,WIIMOTE_STATE_ACC)) return; + WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_ACC); + } + + if(!WIIMOTE_IS_SET(wm,WIIMOTE_STATE_HANDSHAKE_COMPLETE)) return; + + wiiuse_status(wm,NULL); +} + +/** + * @brief Toggle the state of the rumble. + * + * @param wm Pointer to a wiimote_t structure. + */ +#ifdef HAVE_WIIUSE_RUMBLE +void wiiuse_toggle_rumble(struct wiimote_t* wm) +{ + if (!wm) return; + + WIIMOTE_TOGGLE_STATE(wm, WIIMOTE_STATE_RUMBLE); + if(!WIIMOTE_IS_SET(wm,WIIMOTE_STATE_HANDSHAKE_COMPLETE)) return; + + wiiuse_set_leds(wm,wm->leds,NULL); +} + +/** + * @brief Enable or disable the rumble. + * + * @param wm Pointer to a wiimote_t structure. + * @param status 1 to enable, 0 to disable. + */ +void wiiuse_rumble(struct wiimote_t* wm, int status) +{ + if (status && WIIMOTE_IS_SET(wm,WIIMOTE_STATE_RUMBLE)) return; + else if(!status && !WIIMOTE_IS_SET(wm,WIIMOTE_STATE_RUMBLE)) return; + wiiuse_toggle_rumble(wm); +} +#endif + +void wiiuse_set_leds(struct wiimote_t *wm,int leds,cmd_blk_cb cb) +{ + ubyte buf; + + if(!wm || !WIIMOTE_IS_CONNECTED(wm)) return; + + wm->leds = (leds&0xf0); + + buf = wm->leds; + wiiuse_sendcmd(wm,WM_CMD_LED,&buf,1,cb); +} + +int wiiuse_set_report_type(struct wiimote_t *wm,cmd_blk_cb cb) +{ + ubyte buf[2]; + int motion,ir,exp; + + if(!wm || !WIIMOTE_IS_CONNECTED(wm)) return 0; + + buf[0] = (WIIMOTE_IS_FLAG_SET(wm, WIIUSE_CONTINUOUS) ? 0x04 : 0x00); /* set to 0x04 for continuous reporting */ + buf[1] = 0x00; + + motion = WIIMOTE_IS_SET(wm, WIIMOTE_STATE_ACC) || WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR); + exp = WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP); + ir = WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR); + + if (motion && ir && exp) buf[1] = WM_RPT_BTN_ACC_IR_EXP; + else if (motion && exp) buf[1] = WM_RPT_BTN_ACC_EXP; + else if (motion && ir) buf[1] = WM_RPT_BTN_ACC_IR; + else if (ir && exp) buf[1] = WM_RPT_BTN_IR_EXP; + else if (ir) buf[1] = WM_RPT_BTN_ACC_IR; + else if (exp) buf[1] = WM_RPT_BTN_EXP; + else if (motion) buf[1] = WM_RPT_BTN_ACC; + else buf[1] = WM_RPT_BTN; + + //WIIUSE_DEBUG("Setting report type: 0x%x", buf[1]); + + wiiuse_sendcmd(wm,WM_CMD_REPORT_TYPE,buf,2,cb); + return buf[1]; +} + +void wiiuse_status(struct wiimote_t *wm,cmd_blk_cb cb) +{ + ubyte buf; + + if(!wm || !WIIMOTE_IS_CONNECTED(wm)) return; + + buf = 0x00; + wiiuse_sendcmd(wm,WM_CMD_CTRL_STATUS,&buf,1,cb); +} + +int wiiuse_read_data(struct wiimote_t *wm,ubyte *buffer,uint addr,uword len,cmd_blk_cb cb) +{ + struct op_t *op; + struct cmd_blk_t *cmd; + + if(!wm || !WIIMOTE_IS_CONNECTED(wm)) return 0; + if(!buffer || !len) return 0; + + cmd = (struct cmd_blk_t*)__lwp_queue_get(&wm->cmdq); + if(!cmd) return 0; + + cmd->cb = cb; + cmd->len = 7; + + op = (struct op_t*)cmd->data; + op->cmd = WM_CMD_READ_DATA; + op->buffer = buffer; + op->wait = len; + op->readdata.addr = BIG_ENDIAN_LONG(addr); + op->readdata.size = BIG_ENDIAN_SHORT(len); + __wiiuse_push_command(wm,cmd); + + return 1; +} + +int wiiuse_write_data(struct wiimote_t *wm,uint addr,ubyte *data,ubyte len,cmd_blk_cb cb) +{ + struct op_t *op; + struct cmd_blk_t *cmd; + + if(!wm || !WIIMOTE_IS_CONNECTED(wm)) return 0; + if(!data || !len) return 0; + + cmd = (struct cmd_blk_t*)__lwp_queue_get(&wm->cmdq); + if(!cmd) return 0; + + cmd->cb = cb; + cmd->len = 22; + + op = (struct op_t*)cmd->data; + op->cmd = WM_CMD_WRITE_DATA; + op->buffer = NULL; + op->wait = 0; + op->writedata.addr = BIG_ENDIAN_LONG(addr); + op->writedata.size = (len&0x0f); + memcpy(op->writedata.data,data,len); + memset(op->writedata.data+len,0,(16 - len)); + __wiiuse_push_command(wm,cmd); + + return 1; +} + +int wiiuse_write_streamdata(struct wiimote_t *wm,ubyte *data,ubyte len,cmd_blk_cb cb) +{ + struct cmd_blk_t *cmd; + + if(!wm || !WIIMOTE_IS_CONNECTED(wm)) return 0; + if(!data || !len || len>20) return 0; + + cmd = (struct cmd_blk_t*)__lwp_queue_get(&wm->cmdq); + if(!cmd) return 0; + + cmd->cb = cb; + cmd->len = 22; + cmd->data[0] = WM_CMD_STREAM_DATA; + cmd->data[1] = (len<<3); + memcpy(cmd->data+2,data,len); + __wiiuse_push_command(wm,cmd); + + return 1; +} + +int wiiuse_sendcmd(struct wiimote_t *wm,ubyte report_type,ubyte *msg,int len,cmd_blk_cb cb) +{ + struct cmd_blk_t *cmd; + + cmd = (struct cmd_blk_t*)__lwp_queue_get(&wm->cmdq); + if(!cmd) return 0; + + cmd->cb = cb; + cmd->len = (1+len); + + cmd->data[0] = report_type; + memcpy(cmd->data+1,msg,len); + if(report_type!=WM_CMD_READ_DATA && report_type!=WM_CMD_CTRL_STATUS) + cmd->data[1] |= 0x02; + + //WIIUSE_DEBUG("Pushing command: %02x %02x", cmd->data[0], cmd->data[1]); + __wiiuse_push_command(wm,cmd); + + return 1; +} diff --git a/wii/libogc/wiiuse/wiiuse_internal.h b/wii/libogc/wiiuse/wiiuse_internal.h new file mode 100644 index 0000000000..c95171e9de --- /dev/null +++ b/wii/libogc/wiiuse/wiiuse_internal.h @@ -0,0 +1,280 @@ +/* + * wiiuse + * + * Written By: + * Michael Laforest < para > + * Email: < thepara (--AT--) g m a i l [--DOT--] com > + * + * Copyright 2006-2007 + * + * This file is part of wiiuse. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * $Header: /lvm/shared/ds/ds/cvs/devkitpro-cvsbackup/libogc/wiiuse/wiiuse_internal.h,v 1.8 2008-12-10 16:16:40 shagkur Exp $ + * + */ + +/** + * @file + * @brief General internal wiiuse stuff. + * + * Since Wiiuse is a library, wiiuse.h is a duplicate + * of the API header. + * + * The code that would normally go in that file, but + * which is not needed by third party developers, + * is put here. + * + * So wiiuse_internal.h is included by other files + * internally, wiiuse.h is included only here. + */ + +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#ifndef WIIUSE_INTERNAL_H_INCLUDED +#define WIIUSE_INTERNAL_H_INCLUDED + +#if defined(__linux__) + #include /* htons() */ + #include +#endif + +#include "definitions.h" + +/* wiiuse version */ +#define WIIUSE_VERSION "0.12" + +/******************** + * + * Wiimote internal codes + * + ********************/ + +/* Communication channels */ +#define WM_OUTPUT_CHANNEL 0x11 +#define WM_INPUT_CHANNEL 0x13 + +#define WM_SET_REPORT 0x50 +#define WM_DATA 0xA0 + +/* commands */ +#define WM_CMD_RUMBLE 0x10 +#define WM_CMD_LED 0x11 +#define WM_CMD_REPORT_TYPE 0x12 +#define WM_CMD_IR 0x13 +#define WM_CMD_SPEAKER_ENABLE 0x14 +#define WM_CMD_CTRL_STATUS 0x15 +#define WM_CMD_WRITE_DATA 0x16 +#define WM_CMD_READ_DATA 0x17 +#define WM_CMD_STREAM_DATA 0x18 +#define WM_CMD_SPEAKER_MUTE 0x19 +#define WM_CMD_IR_2 0x1A + +/* input report ids */ +#define WM_RPT_CTRL_STATUS 0x20 +#define WM_RPT_READ 0x21 +#define WM_RPT_ACK 0x22 +#define WM_RPT_BTN 0x30 +#define WM_RPT_BTN_ACC 0x31 +#define WM_RPT_BTN_ACC_IR 0x33 +#define WM_RPT_BTN_EXP 0x34 +#define WM_RPT_BTN_ACC_EXP 0x35 +#define WM_RPT_BTN_IR_EXP 0x36 +#define WM_RPT_BTN_ACC_IR_EXP 0x37 + +#define WM_BT_INPUT 0x01 +#define WM_BT_OUTPUT 0x02 + +/* Identify the wiimote device by its class */ +#define WM_DEV_CLASS_0 0x04 +#define WM_DEV_CLASS_1 0x25 +#define WM_DEV_CLASS_2 0x00 +#define WM_VENDOR_ID 0x057E +#define WM_PRODUCT_ID 0x0306 + +/* controller status stuff */ +#define WM_MAX_BATTERY_CODE 0xC8 + +/* offsets in wiimote memory */ +#define WM_MEM_OFFSET_CALIBRATION 0x16 +#define WM_EXP_MEM_BASE 0x04A40000 +#define WM_EXP_MEM_ENABLE1 0x04A400F0 +#define WM_EXP_MEM_ENABLE2 0x04A400FB +#define WM_EXP_MEM_KEY 0x04A40040 +#define WM_EXP_MEM_CALIBR 0x04A40020 +#define WM_EXP_MOTION_PLUS_ENABLE 0x04A600FE +#define WM_EXP_ID 0x04A400FA + +#define WM_REG_IR 0x04B00030 +#define WM_REG_IR_BLOCK1 0x04B00000 +#define WM_REG_IR_BLOCK2 0x04B0001A +#define WM_REG_IR_MODENUM 0x04B00033 + +#define WM_REG_SPEAKER_REG1 0x04A20001 +#define WM_REG_SPEAKER_REG2 0x04A20008 +#define WM_REG_SPEAKER_REG3 0x04A20009 +#define WM_REG_SPEAKER_BLOCK 0x04A20001 + +/* ir block data */ +#define WM_IR_BLOCK1_LEVEL1 "\x02\x00\x00\x71\x01\x00\x64\x00\xfe" +#define WM_IR_BLOCK2_LEVEL1 "\xfd\x05" +#define WM_IR_BLOCK1_LEVEL2 "\x02\x00\x00\x71\x01\x00\x96\x00\xb4" +#define WM_IR_BLOCK2_LEVEL2 "\xb3\x04" +#define WM_IR_BLOCK1_LEVEL3 "\x02\x00\x00\x71\x01\x00\xaa\x00\x64" +#define WM_IR_BLOCK2_LEVEL3 "\x63\x03" +#define WM_IR_BLOCK1_LEVEL4 "\x02\x00\x00\x71\x01\x00\xc8\x00\x36" +#define WM_IR_BLOCK2_LEVEL4 "\x35\x03" +#define WM_IR_BLOCK1_LEVEL5 "\x07\x00\x00\x71\x01\x00\x72\x00\x20" +#define WM_IR_BLOCK2_LEVEL5 "\x1f\x03" + +#define WM_IR_TYPE_BASIC 0x01 +#define WM_IR_TYPE_EXTENDED 0x03 +#define WM_IR_TYPE_FULL 0x05 + +/* controller status flags for the first message byte */ +/* bit 1 is unknown */ +#define WM_CTRL_STATUS_BYTE1_ATTACHMENT 0x02 +#define WM_CTRL_STATUS_BYTE1_SPEAKER_ENABLED 0x04 +#define WM_CTRL_STATUS_BYTE1_IR_ENABLED 0x08 +#define WM_CTRL_STATUS_BYTE1_LED_1 0x10 +#define WM_CTRL_STATUS_BYTE1_LED_2 0x20 +#define WM_CTRL_STATUS_BYTE1_LED_3 0x40 +#define WM_CTRL_STATUS_BYTE1_LED_4 0x80 + +/* aspect ratio */ +#define WM_ASPECT_16_9_X 660 +#define WM_ASPECT_16_9_Y 370 +#define WM_ASPECT_4_3_X 560 +#define WM_ASPECT_4_3_Y 420 + + +/** + * Expansion stuff + */ + +/* encrypted expansion id codes (located at 0x04A400FC) */ +#define EXP_ID_CODE_NUNCHUK 0xa4200000 +#define EXP_ID_CODE_CLASSIC_CONTROLLER 0xa4200101 +#define EXP_ID_CODE_CLASSIC_CONTROLLER_NYKOWING 0x90908f00 +#define EXP_ID_CODE_CLASSIC_CONTROLLER_NYKOWING2 0x9e9f9c00 +#define EXP_ID_CODE_CLASSIC_CONTROLLER_NYKOWING3 0x908f8f00 +#define EXP_ID_CODE_CLASSIC_CONTROLLER_GENERIC 0xa5a2a300 +#define EXP_ID_CODE_CLASSIC_CONTROLLER_GENERIC2 0x98999900 +#define EXP_ID_CODE_CLASSIC_CONTROLLER_GENERIC3 0xa0a1a000 +#define EXP_ID_CODE_CLASSIC_CONTROLLER_GENERIC4 0x8d8d8e00 +#define EXP_ID_CODE_CLASSIC_CONTROLLER_GENERIC5 0x93949400 +#define EXP_ID_CODE_CLASSIC_WIIU_PRO 0xa4200120 +//#define EXP_ID_CODE_GUITAR 0xa4200103 +//#define EXP_ID_CODE_WIIBOARD 0xa4200402 +#define EXP_ID_CODE_MOTION_PLUS 0xa4200405 + +#define EXP_HANDSHAKE_LEN 224 + +/******************** + * + * End Wiimote internal codes + * + ********************/ + +/* wiimote state flags - (some duplicated in wiiuse.h)*/ +#define WIIMOTE_STATE_DEV_FOUND 0x00001 +//#define WIIMOTE_STATE_DEV_REGISTER 0x00002 +#define WIIMOTE_STATE_HANDSHAKE 0x00004 /* actual connection exists but no handshake yet */ +#define WIIMOTE_STATE_HANDSHAKE_COMPLETE 0x00008 /* actual connection exists but no handshake yet */ +#define WIIMOTE_STATE_CONNECTED 0x00010 +#define WIIMOTE_STATE_EXP_HANDSHAKE 0x00020 /* actual connection exists but no handshake yet */ +#define WIIMOTE_STATE_EXP_FAILED 0x00040 /* actual connection exists but no handshake yet */ +#define WIIMOTE_STATE_RUMBLE 0x00080 +#define WIIMOTE_STATE_ACC 0x00100 +#define WIIMOTE_STATE_EXP 0x00200 +#define WIIMOTE_STATE_IR 0x00400 +#define WIIMOTE_STATE_SPEAKER 0x00800 +#define WIIMOTE_STATE_IR_SENS_LVL1 0x01000 +#define WIIMOTE_STATE_IR_SENS_LVL2 0x02000 +#define WIIMOTE_STATE_IR_SENS_LVL3 0x04000 +#define WIIMOTE_STATE_IR_SENS_LVL4 0x08000 +#define WIIMOTE_STATE_IR_SENS_LVL5 0x10000 +#define WIIMOTE_STATE_IR_INIT 0x20000 +#define WIIMOTE_STATE_SPEAKER_INIT 0x40000 +#define WIIMOTE_STATE_WIIU_PRO 0x80000 + +#define WIIMOTE_INIT_STATES (WIIMOTE_STATE_IR_SENS_LVL3) + +/* macro to manage states */ +#define WIIMOTE_IS_SET(wm, s) ((wm->state & (s)) == (s)) +#define WIIMOTE_ENABLE_STATE(wm, s) (wm->state |= (s)) +#define WIIMOTE_DISABLE_STATE(wm, s) (wm->state &= ~(s)) +#define WIIMOTE_TOGGLE_STATE(wm, s) ((wm->state & (s)) ? WIIMOTE_DISABLE_STATE(wm, s) : WIIMOTE_ENABLE_STATE(wm, s)) + +#define WIIMOTE_IS_FLAG_SET(wm, s) ((wm->flags & (s)) == (s)) +#define WIIMOTE_ENABLE_FLAG(wm, s) (wm->flags |= (s)) +#define WIIMOTE_DISABLE_FLAG(wm, s) (wm->flags &= ~(s)) +#define WIIMOTE_TOGGLE_FLAG(wm, s) ((wm->flags & (s)) ? WIIMOTE_DISABLE_FLAG(wm, s) : WIIMOTE_ENABLE_FLAG(wm, s)) + +#define NUNCHUK_IS_FLAG_SET(wm, s) ((*(wm->flags) & (s)) == (s)) + +/* misc macros */ +#define WIIMOTE_ID(wm) (wm->unid) +#define WIIMOTE_IS_CONNECTED(wm) (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_CONNECTED)) + +/* + * Smooth tilt calculations are computed with the + * exponential moving average formula: + * St = St_last + (alpha * (tilt - St_last)) + * alpha is between 0 and 1 + */ +#define WIIUSE_DEFAULT_SMOOTH_ALPHA 0.3f + +#define SMOOTH_ROLL 0x01 +#define SMOOTH_PITCH 0x02 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct op_t +{ + ubyte cmd; + union { + struct { + uint addr; + uword size; + } readdata; + struct { + uint addr; + ubyte size; + ubyte data[16]; + } writedata; + ubyte __data[MAX_PAYLOAD]; + }; + + void *buffer; + int wait; +} __attribute__((packed)); + +/* not part of the api */ +void wiiuse_init_cmd_queue(struct wiimote_t *wm); +void wiiuse_send_next_command(struct wiimote_t *wm); +int wiiuse_set_report_type(struct wiimote_t* wm,cmd_blk_cb cb); +int wiiuse_sendcmd(struct wiimote_t *wm,ubyte report_type,ubyte *msg,int len,cmd_blk_cb cb); + +#ifdef __cplusplus +} +#endif + +#endif /* WIIUSE_INTERNAL_H_INCLUDED */ diff --git a/wii/libogc/wiiuse/wpad.c b/wii/libogc/wiiuse/wpad.c new file mode 100644 index 0000000000..8e80da0de3 --- /dev/null +++ b/wii/libogc/wiiuse/wpad.c @@ -0,0 +1,1106 @@ +/*------------------------------------------------------------- + +wpad.c -- Wiimote Application Programmers Interface + +Copyright (C) 2008 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) +Hector Martin (marcan) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +/* This source as presented is a modified version of original wiiuse for use + * with RetroArch, and must not be confused with the original software. */ + +#include +#include +#include +#include +#include + +#include "os.h" +#include "conf.h" +#include "ir.h" + +#ifdef HAVE_WIIUSE_SPEAKER +#include "speaker.h" +#endif + +#include "dynamics.h" +#include "wiiuse_internal.h" +#include "wiiuse/wpad.h" +#include "lwp_threads.h" +#include "ogcsys.h" + +#define MAX_STREAMDATA_LEN 20 +#define EVENTQUEUE_LENGTH 16 + +#define DISCONNECT_BATTERY_DIED 0x14 +#define DISCONNECT_POWER_OFF 0x15 + +struct _wpad_thresh{ + s32 btns; + s32 ir; + s32 js; + s32 acc; + s32 mp; +}; + +struct _wpad_cb { + wiimote *wm; + s32 data_fmt; + s32 queue_head; + s32 queue_tail; + s32 queue_full; + u32 queue_length; + u32 dropped_events; + s32 idle_time; + s32 speaker_enabled; + struct _wpad_thresh thresh; + +#ifdef HAVE_WIIUSE_SPEAKER + void *sound_data; + u32 sound_len; + u32 sound_off; + syswd_t sound_alarm; +#endif + + WPADData lstate; +#ifdef HAVE_WIIUSE_QUEUE_EXT + WPADData *queue_ext; +#endif + WPADData queue_int[EVENTQUEUE_LENGTH]; +}; + +static syswd_t __wpad_timer; +static vu32 __wpads_inited = 0; +static vs32 __wpads_ponded = 0; +static u32 __wpad_idletimeout = 300; +static vu32 __wpads_active = 0; +static vu32 __wpads_used = 0; +static wiimote **__wpads = NULL; +static wiimote_listen __wpads_listen[CONF_PAD_MAX_REGISTERED]; +static WPADData wpaddata[WPAD_MAX_WIIMOTES]; +static struct _wpad_cb __wpdcb[WPAD_MAX_WIIMOTES]; +static conf_pads __wpad_devs; +static struct linkkey_info __wpad_keys[WPAD_MAX_WIIMOTES]; + +static s32 __wpad_onreset(s32 final); +static s32 __wpad_disconnect(struct _wpad_cb *wpdcb); +static void __wpad_eventCB(struct wiimote_t *wm,s32 event); + +static void __wpad_def_powcb(s32 chan); +static WPADShutdownCallback __wpad_batcb = NULL; +static WPADShutdownCallback __wpad_powcb = __wpad_def_powcb; + +extern void __wiiuse_sensorbar_enable(int enable); +extern void __SYS_DoPowerCB(void); + +static sys_resetinfo __wpad_resetinfo = { + {}, + __wpad_onreset, + 127 +}; + +static s32 __wpad_onreset(s32 final) +{ + //printf("__wpad_onreset(%d)\n",final); + if(final==FALSE) { + WPAD_Shutdown(); + } + return 1; +} + +static void __wpad_def_powcb(s32 chan) +{ + __SYS_DoPowerCB(); +} + +static void __wpad_timeouthandler(syswd_t alarm,void *cbarg) +{ + s32 i; + struct wiimote_t *wm = NULL; + struct _wpad_cb *wpdcb = NULL; + + if(!__wpads_active) return; + + ++_thread_dispatch_disable_level; + for(i=0;iwm; + if(wm && WIIMOTE_IS_SET(wm,WIIMOTE_STATE_CONNECTED)) { + wpdcb->idle_time++; + if(wpdcb->idle_time>=__wpad_idletimeout) { + wpdcb->idle_time = 0; + wiiuse_disconnect(wm); + } + } + } + --_thread_dispatch_disable_level; +} + +#ifdef HAVE_WIIUSE_SPEAKER +static void __wpad_sounddata_alarmhandler(syswd_t alarm,void *cbarg) +{ + u8 *snd_data; + u32 snd_off; + struct wiimote_t *wm; + struct _wpad_cb *wpdcb = (struct _wpad_cb*)cbarg; + + if(!wpdcb) return; + + if(wpdcb->sound_off>=wpdcb->sound_len) { + wpdcb->sound_data = NULL; + wpdcb->sound_len = 0; + wpdcb->sound_off = 0; + SYS_CancelAlarm(wpdcb->sound_alarm); + return; + } + + wm = wpdcb->wm; + snd_data = wpdcb->sound_data; + snd_off = wpdcb->sound_off; + wpdcb->sound_off += MAX_STREAMDATA_LEN; + wiiuse_write_streamdata(wm,(snd_data+snd_off),MAX_STREAMDATA_LEN,NULL); +} +#endif + +static void __wpad_setfmt(s32 chan) +{ + switch(__wpdcb[chan].data_fmt) { + case WPAD_FMT_BTNS: + wiiuse_set_flags(__wpads[chan], 0, WIIUSE_CONTINUOUS); + wiiuse_motion_sensing(__wpads[chan],0); + wiiuse_set_ir(__wpads[chan],0); + break; + case WPAD_FMT_BTNS_ACC: + wiiuse_set_flags(__wpads[chan], WIIUSE_CONTINUOUS, 0); + wiiuse_motion_sensing(__wpads[chan],1); + wiiuse_set_ir(__wpads[chan],0); + break; + case WPAD_FMT_BTNS_ACC_IR: + wiiuse_set_flags(__wpads[chan], WIIUSE_CONTINUOUS, 0); + wiiuse_motion_sensing(__wpads[chan],1); + wiiuse_set_ir(__wpads[chan],1); + break; + default: + break; + } +} + +wiimote *__wpad_assign_slot(struct bd_addr *pad_addr) +{ + u32 i, level; + struct bd_addr bdaddr; + //printf("WPAD Assigning slot (active: 0x%02x)\n", __wpads_used); + _CPU_ISR_Disable(level); + + // Try preassigned slots + for(i=0; iwm; + if(wm && WIIMOTE_IS_SET(wm,WIIMOTE_STATE_CONNECTED)) { + wiiuse_disconnect(wm); + } + + return 0; +} + +static void __wpad_calc_data(WPADData *data,WPADData *lstate,struct accel_t *accel_calib,u32 smoothed) +{ + if(data->err!=WPAD_ERR_NONE) return; + + data->orient = lstate->orient; + + data->ir.state = lstate->ir.state; + data->ir.sensorbar = lstate->ir.sensorbar; + data->ir.x = lstate->ir.x; + data->ir.y = lstate->ir.y; + data->ir.sx = lstate->ir.sx; + data->ir.sy = lstate->ir.sy; + data->ir.ax = lstate->ir.ax; + data->ir.ay = lstate->ir.ay; + data->ir.distance = lstate->ir.distance; + data->ir.z = lstate->ir.z; + data->ir.angle = lstate->ir.angle; + data->ir.error_cnt = lstate->ir.error_cnt; + data->ir.glitch_cnt = lstate->ir.glitch_cnt; + + if(data->data_present & WPAD_DATA_ACCEL) { + calculate_orientation(accel_calib, &data->accel, &data->orient, smoothed); + calculate_gforce(accel_calib, &data->accel, &data->gforce); + } + if(data->data_present & WPAD_DATA_IR) { + interpret_ir_data(&data->ir,&data->orient); + } + if(data->data_present & WPAD_DATA_EXPANSION) { + switch(data->exp.type) { + case EXP_NUNCHUK: + { + struct nunchuk_t *nc = &data->exp.nunchuk; + + nc->orient = lstate->exp.nunchuk.orient; + calc_joystick_state(&nc->js,nc->js.pos.x,nc->js.pos.y); + calculate_orientation(&nc->accel_calib,&nc->accel,&nc->orient,smoothed); + calculate_gforce(&nc->accel_calib,&nc->accel,&nc->gforce); + data->btns_h |= (data->exp.nunchuk.btns<<16); + } + break; + + case EXP_CLASSIC: + { + struct classic_ctrl_t *cc = &data->exp.classic; + + cc->r_shoulder = ((f32)cc->rs_raw/0x1F); + cc->l_shoulder = ((f32)cc->ls_raw/0x1F); + calc_joystick_state(&cc->ljs, cc->ljs.pos.x, cc->ljs.pos.y); + calc_joystick_state(&cc->rjs, cc->rjs.pos.x, cc->rjs.pos.y); + data->btns_h |= (data->exp.classic.btns<<16); + } + break; + default: + break; + } + } + *lstate = *data; +} + +static void __save_state(struct wiimote_t* wm) { + /* wiimote */ + wm->lstate.btns = wm->btns; + wm->lstate.accel = wm->accel; + + /* ir */ + wm->lstate.ir = wm->ir; + + /* expansion */ + switch (wm->exp.type) { + case EXP_NUNCHUK: + wm->lstate.exp.nunchuk = wm->exp.nunchuk; + break; + case EXP_CLASSIC: + wm->lstate.exp.classic = wm->exp.classic; + break; + case EXP_MOTION_PLUS: + wm->lstate.exp.mp = wm->exp.mp; + break; + } +} + +#define ABS(x) ((s32)(x)>0?(s32)(x):-((s32)(x))) + +#define STATE_CHECK(thresh, a, b) \ + if(((thresh) > WPAD_THRESH_IGNORE) && (ABS((a)-(b)) > (thresh))) \ + state_changed = 1; + +#define STATE_CHECK_SIMPLE(thresh, a, b) \ + if(((thresh) > WPAD_THRESH_IGNORE) && ((a) != (b))) \ + state_changed = 1; + +static u32 __wpad_read_expansion(struct wiimote_t *wm,WPADData *data, struct _wpad_thresh *thresh) +{ + int state_changed = 0; + switch(data->exp.type) { + case EXP_NUNCHUK: + data->exp.nunchuk = wm->exp.nunchuk; + STATE_CHECK_SIMPLE(thresh->btns, wm->exp.nunchuk.btns, wm->lstate.exp.nunchuk.btns); + STATE_CHECK(thresh->acc, wm->exp.nunchuk.accel.x, wm->lstate.exp.nunchuk.accel.x); + STATE_CHECK(thresh->acc, wm->exp.nunchuk.accel.y, wm->lstate.exp.nunchuk.accel.y); + STATE_CHECK(thresh->acc, wm->exp.nunchuk.accel.z, wm->lstate.exp.nunchuk.accel.z); + STATE_CHECK(thresh->js, wm->exp.nunchuk.js.pos.x, wm->lstate.exp.nunchuk.js.pos.x); + STATE_CHECK(thresh->js, wm->exp.nunchuk.js.pos.y, wm->lstate.exp.nunchuk.js.pos.y); + break; + case EXP_CLASSIC: + data->exp.classic = wm->exp.classic; + STATE_CHECK_SIMPLE(thresh->btns, wm->exp.classic.btns, wm->lstate.exp.classic.btns); + STATE_CHECK(thresh->js, wm->exp.classic.rs_raw, wm->lstate.exp.classic.rs_raw); + STATE_CHECK(thresh->js, wm->exp.classic.ls_raw, wm->lstate.exp.classic.ls_raw); + STATE_CHECK(thresh->js, wm->exp.classic.ljs.pos.x, wm->lstate.exp.classic.ljs.pos.x); + STATE_CHECK(thresh->js, wm->exp.classic.ljs.pos.y, wm->lstate.exp.classic.ljs.pos.y); + STATE_CHECK(thresh->js, wm->exp.classic.rjs.pos.x, wm->lstate.exp.classic.rjs.pos.x); + STATE_CHECK(thresh->js, wm->exp.classic.rjs.pos.y, wm->lstate.exp.classic.rjs.pos.y); + break; + case EXP_MOTION_PLUS: + data->exp.mp = wm->exp.mp; + STATE_CHECK(thresh->mp,wm->exp.mp.rx,wm->lstate.exp.mp.rx); + STATE_CHECK(thresh->mp,wm->exp.mp.ry,wm->lstate.exp.mp.ry); + STATE_CHECK(thresh->mp,wm->exp.mp.rz,wm->lstate.exp.mp.rz); + break; + } + return state_changed; +} + +static void __wpad_read_wiimote(struct wiimote_t *wm, WPADData *data, s32 *idle_time, struct _wpad_thresh *thresh) +{ + int i; + int state_changed = 0; + data->err = WPAD_ERR_TRANSFER; + data->data_present = 0; + data->battery_level = wm->battery_level; + data->exp.type = wm->exp.type; + if(wm && WIIMOTE_IS_SET(wm,WIIMOTE_STATE_CONNECTED)) { + if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_HANDSHAKE_COMPLETE)) { + switch(wm->event_buf[0]) { + case WM_RPT_BTN: + case WM_RPT_BTN_ACC: + case WM_RPT_BTN_ACC_IR: + case WM_RPT_BTN_EXP: + case WM_RPT_BTN_ACC_EXP: + case WM_RPT_BTN_IR_EXP: + case WM_RPT_BTN_ACC_IR_EXP: + data->btns_h = (wm->btns&0xffff); + data->data_present |= WPAD_DATA_BUTTONS; + STATE_CHECK_SIMPLE(thresh->btns, wm->btns, wm->lstate.btns); + } + switch(wm->event_buf[0]) { + case WM_RPT_BTN_ACC: + case WM_RPT_BTN_ACC_IR: + case WM_RPT_BTN_ACC_EXP: + case WM_RPT_BTN_ACC_IR_EXP: + data->accel = wm->accel; + data->data_present |= WPAD_DATA_ACCEL; + STATE_CHECK(thresh->acc, wm->accel.x, wm->lstate.accel.x); + STATE_CHECK(thresh->acc, wm->accel.y, wm->lstate.accel.y); + STATE_CHECK(thresh->acc, wm->accel.z, wm->lstate.accel.z); + } + switch(wm->event_buf[0]) { + //IR requires acceleration + //case WM_RPT_BTN_IR_EXP: + case WM_RPT_BTN_ACC_IR: + case WM_RPT_BTN_ACC_IR_EXP: + data->ir = wm->ir; + data->data_present |= WPAD_DATA_IR; + for(i=0; iir, wm->ir.dot[i].visible, wm->lstate.ir.dot[i].visible); + STATE_CHECK(thresh->ir, wm->ir.dot[i].rx, wm->lstate.ir.dot[i].rx); + STATE_CHECK(thresh->ir, wm->ir.dot[i].ry, wm->lstate.ir.dot[i].ry); + } + } + switch(wm->event_buf[0]) { + case WM_RPT_BTN_EXP: + case WM_RPT_BTN_ACC_EXP: + case WM_RPT_BTN_IR_EXP: + case WM_RPT_BTN_ACC_IR_EXP: + state_changed |= __wpad_read_expansion(wm,data,thresh); + data->data_present |= WPAD_DATA_EXPANSION; + } + data->err = WPAD_ERR_NONE; + if(state_changed) { + *idle_time = 0; + __save_state(wm); + } + } else + data->err = WPAD_ERR_NOT_READY; + } else + data->err = WPAD_ERR_NO_CONTROLLER; +} + +static void __wpad_eventCB(struct wiimote_t *wm,s32 event) +{ + s32 chan; + u32 maxbufs; + WPADData *wpadd = NULL; + struct _wpad_cb *wpdcb = NULL; + + switch(event) { + case WIIUSE_EVENT: + chan = wm->unid; + wpdcb = &__wpdcb[chan]; + +#ifdef HAVE_WIIUSE_QUEUE_EXT + if(wpdcb->queue_ext!=NULL) { + maxbufs = wpdcb->queue_length; + wpadd = &(wpdcb->queue_ext[wpdcb->queue_tail]); + } + else +#endif + { + maxbufs = EVENTQUEUE_LENGTH; + wpadd = &(wpdcb->queue_int[wpdcb->queue_tail]); + } + if(wpdcb->queue_full == maxbufs) { + wpdcb->queue_head++; + wpdcb->queue_head %= maxbufs; + wpdcb->dropped_events++; + } else { + wpdcb->queue_full++; + } + + __wpad_read_wiimote(wm, wpadd, &wpdcb->idle_time, &wpdcb->thresh); + + wpdcb->queue_tail++; + wpdcb->queue_tail %= maxbufs; + + break; + case WIIUSE_STATUS: + break; + case WIIUSE_CONNECT: + chan = wm->unid; + wpdcb = &__wpdcb[chan]; + wpdcb->wm = wm; + wpdcb->queue_head = 0; + wpdcb->queue_tail = 0; + wpdcb->queue_full = 0; + wpdcb->idle_time = 0; + memset(&wpdcb->lstate,0,sizeof(WPADData)); + memset(&wpaddata[chan],0,sizeof(WPADData)); + memset(wpdcb->queue_int,0,(sizeof(WPADData)*EVENTQUEUE_LENGTH)); + wiiuse_set_ir_position(wm,(CONF_GetSensorBarPosition()^1)); + wiiuse_set_ir_sensitivity(wm,CONF_GetIRSensitivity()); + wiiuse_set_leds(wm,(WIIMOTE_LED_1<<(chan%WPAD_BALANCE_BOARD)),NULL); +#ifdef HAVE_WIIUSE_SPEAKER + wiiuse_set_speaker(wm,wpdcb->speaker_enabled); +#endif + __wpad_setfmt(chan); + __wpads_active |= (0x01<unid; + wpdcb = &__wpdcb[chan]; + wpdcb->wm = wm; + wpdcb->queue_head = 0; + wpdcb->queue_tail = 0; + wpdcb->queue_full = 0; + wpdcb->queue_length = 0; +#ifdef HAVE_WIIUSE_QUEUE_EXT + wpdcb->queue_ext = NULL; +#endif + wpdcb->idle_time = -1; + memset(&wpdcb->lstate,0,sizeof(WPADData)); + memset(&wpaddata[chan],0,sizeof(WPADData)); + memset(wpdcb->queue_int,0,(sizeof(WPADData)*EVENTQUEUE_LENGTH)); + __wpads_active &= ~(0x01< CONF_PAD_MAX_REGISTERED) { + WPAD_Shutdown(); + _CPU_ISR_Restore(level); + return WPAD_ERR_BADCONF; + } + + __wpads = wiiuse_init(WPAD_MAX_WIIMOTES,__wpad_eventCB); + if(__wpads==NULL) { + WPAD_Shutdown(); + _CPU_ISR_Restore(level); + return WPAD_ERR_UNKNOWN; + } + + __wiiuse_sensorbar_enable(1); + + BTE_Init(); + BTE_SetDisconnectCallback(__wpad_disconnectCB); + BTE_InitCore(__initcore_finished); + + if (SYS_CreateAlarm(&__wpad_timer) < 0) + { + WPAD_Shutdown(); + _CPU_ISR_Restore(level); + return WPAD_ERR_UNKNOWN; + } + + SYS_RegisterResetFunc(&__wpad_resetinfo); + + tb.tv_sec = 1; + tb.tv_nsec = 0; + SYS_SetPeriodicAlarm(__wpad_timer,&tb,&tb,__wpad_timeouthandler,NULL); + __wpads_inited = WPAD_STATE_ENABLING; + } + _CPU_ISR_Restore(level); + return WPAD_ERR_NONE; +} + +s32 WPAD_ReadEvent(s32 chan, WPADData *data) +{ + u32 level; + u32 maxbufs,smoothed = 0; + struct accel_t *accel_calib = NULL; + struct _wpad_cb *wpdcb = NULL; + WPADData *lstate = NULL,*wpadd = NULL; + + //if(chan=WPAD_MAX_WIIMOTES) return WPAD_ERR_BAD_CHANNEL; + + _CPU_ISR_Disable(level); + if(__wpads_inited==WPAD_STATE_DISABLED) { + _CPU_ISR_Restore(level); + return WPAD_ERR_NOT_READY; + } + + if(__wpads[chan] && WIIMOTE_IS_SET(__wpads[chan],WIIMOTE_STATE_CONNECTED)) { + if(WIIMOTE_IS_SET(__wpads[chan],WIIMOTE_STATE_HANDSHAKE_COMPLETE)) { + wpdcb = &__wpdcb[chan]; +#ifdef HAVE_WIIUSE_QUEUE_EXT + if(wpdcb->queue_ext!=NULL) { + maxbufs = wpdcb->queue_length; + wpadd = wpdcb->queue_ext; + } + else +#endif + { + maxbufs = EVENTQUEUE_LENGTH; + wpadd = wpdcb->queue_int; + } + if(wpdcb->queue_full == 0) { + _CPU_ISR_Restore(level); + return WPAD_ERR_QUEUE_EMPTY; + } + if(data) + *data = wpadd[wpdcb->queue_head]; + wpdcb->queue_head++; + wpdcb->queue_head %= maxbufs; + wpdcb->queue_full--; + lstate = &wpdcb->lstate; + accel_calib = &__wpads[chan]->accel_calib; + smoothed = WIIMOTE_IS_FLAG_SET(__wpads[chan], WIIUSE_SMOOTHING); + } else { + _CPU_ISR_Restore(level); + return WPAD_ERR_NOT_READY; + } + } else { + _CPU_ISR_Restore(level); + return WPAD_ERR_NO_CONTROLLER; + } + + _CPU_ISR_Restore(level); + if(data) + __wpad_calc_data(data,lstate,accel_calib,smoothed); + return 0; +} + +static s32 pad_readpending_temp(s32 chan) +{ + s32 count = 0; + s32 ret; + + while(1) + { + ret = WPAD_ReadEvent(chan, &wpaddata[chan]); + if(ret < WPAD_ERR_NONE) + break; + count++; + } + if(ret == WPAD_ERR_QUEUE_EMPTY) return count; + return ret; +} + +s32 WPAD_ReadPending(s32 chan, WPADDataCallback datacb) +{ + u32 i; + s32 count = 0; + s32 ret; + + for(i= WPAD_CHAN_0; i < WPAD_MAX_WIIMOTES; i++) + if((ret = pad_readpending_temp(i)) >= WPAD_ERR_NONE) + count += ret; + return count; +} + +s32 WPAD_SetMotionPlus(s32 chan, u8 enable) +{ + u32 level; + s32 ret; + int i; + + if(chan == WPAD_CHAN_ALL) { + for(i=WPAD_CHAN_0; i=WPAD_MAX_WIIMOTES) return WPAD_ERR_BAD_CHANNEL; + + _CPU_ISR_Disable(level); + if(__wpads_inited==WPAD_STATE_DISABLED) { + _CPU_ISR_Restore(level); + return WPAD_ERR_NOT_READY; + } + + if(__wpads[chan]!=NULL) { + wiiuse_set_motion_plus(__wpads[chan], enable); + } + _CPU_ISR_Restore(level); + return WPAD_ERR_NONE; +} + +s32 WPAD_SetVRes(s32 chan,u32 xres,u32 yres) +{ + u32 level; + s32 ret; + int i; + + if(chan == WPAD_CHAN_ALL) { + for(i=WPAD_CHAN_0; i=WPAD_MAX_WIIMOTES) return WPAD_ERR_BAD_CHANNEL; + + _CPU_ISR_Disable(level); + if(__wpads_inited==WPAD_STATE_DISABLED) { + _CPU_ISR_Restore(level); + return WPAD_ERR_NOT_READY; + } + + if(__wpads[chan]!=NULL) + wiiuse_set_ir_vres(__wpads[chan],xres,yres); + + _CPU_ISR_Restore(level); + return WPAD_ERR_NONE; +} + +s32 WPAD_GetStatus() +{ + s32 ret; + u32 level; + + _CPU_ISR_Disable(level); + ret = __wpads_inited; + _CPU_ISR_Restore(level); + + return ret; +} + +s32 WPAD_Probe(s32 chan,u32 *type) +{ + s32 ret; + u32 level,dev; + wiimote *wm = NULL; + + //if(chan=WPAD_MAX_WIIMOTES) return WPAD_ERR_BAD_CHANNEL; + + _CPU_ISR_Disable(level); + if(__wpads_inited==WPAD_STATE_DISABLED) { + _CPU_ISR_Restore(level); + return WPAD_ERR_NOT_READY; + } + + wm = __wpads[chan]; + if(wm && WIIMOTE_IS_SET(wm,WIIMOTE_STATE_CONNECTED)) { + if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_HANDSHAKE_COMPLETE)) { + dev = WPAD_EXP_NONE; + if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_EXP)) { + switch(wm->exp.type) { + case WPAD_EXP_NUNCHUK: + case WPAD_EXP_CLASSIC: + dev = wm->exp.type; + break; + } + } + if(type!=NULL) *type = dev; + ret = WPAD_ERR_NONE; + } else + ret = WPAD_ERR_NOT_READY; + } else + ret = WPAD_ERR_NO_CONTROLLER; + _CPU_ISR_Restore(level); + + return ret; +} + +#ifdef HAVE_WIIUSE_QUEUE_EXT +s32 WPAD_SetEventBufs(s32 chan, WPADData *bufs, u32 cnt) +{ + u32 level; + struct _wpad_cb *wpdcb = NULL; + + if(chan=WPAD_MAX_WIIMOTES) return WPAD_ERR_BAD_CHANNEL; + + _CPU_ISR_Disable(level); + wpdcb = &__wpdcb[chan]; + wpdcb->queue_head = 0; + wpdcb->queue_tail = 0; + wpdcb->queue_full = 0; + wpdcb->queue_length = cnt; + wpdcb->queue_ext = bufs; + _CPU_ISR_Restore(level); + return WPAD_ERR_NONE; +} +#endif + +void WPAD_SetPowerButtonCallback(WPADShutdownCallback cb) +{ + u32 level; + + _CPU_ISR_Disable(level); + if(cb) + __wpad_powcb = cb; + else + __wpad_powcb = __wpad_def_powcb; + _CPU_ISR_Restore(level); +} + +void WPAD_SetBatteryDeadCallback(WPADShutdownCallback cb) +{ + u32 level; + + _CPU_ISR_Disable(level); + __wpad_batcb = cb; + _CPU_ISR_Restore(level); +} + +s32 WPAD_Disconnect(s32 chan) +{ + u32 level, cnt = 0; + struct _wpad_cb *wpdcb = NULL; + + if(chan=WPAD_MAX_WIIMOTES) return WPAD_ERR_BAD_CHANNEL; + + _CPU_ISR_Disable(level); + if(__wpads_inited==WPAD_STATE_DISABLED) { + _CPU_ISR_Restore(level); + return WPAD_ERR_NOT_READY; + } + + wpdcb = &__wpdcb[chan]; + __wpad_disconnect(wpdcb); + + _CPU_ISR_Restore(level); + + while(__wpads_active&(0x01< 3000) break; + } + + return WPAD_ERR_NONE; +} + +void WPAD_Shutdown() +{ + s32 i; + u32 level; + u32 cnt = 0; + struct _wpad_cb *wpdcb = NULL; + + _CPU_ISR_Disable(level); + + __wpads_inited = WPAD_STATE_DISABLED; + SYS_RemoveAlarm(__wpad_timer); + for(i=0;isound_alarm); +#endif + __wpad_disconnect(wpdcb); + } + + __wiiuse_sensorbar_enable(0); + _CPU_ISR_Restore(level); + + while(__wpads_active) { + usleep(50); + if(++cnt > 3000) break; + } + + BTE_Shutdown(); +} + +void WPAD_SetIdleTimeout(u32 seconds) +{ + u32 level; + + _CPU_ISR_Disable(level); + __wpad_idletimeout = seconds; + _CPU_ISR_Restore(level); +} + +#ifdef HAVE_WIIUSE_RUMBLE +s32 WPAD_Rumble(s32 chan, int status) +{ + int i; + s32 ret; + u32 level; + + if(chan == WPAD_CHAN_ALL) { + for(i=WPAD_CHAN_0; i=WPAD_MAX_WIIMOTES) return WPAD_ERR_BAD_CHANNEL; + + _CPU_ISR_Disable(level); + if(__wpads_inited==WPAD_STATE_DISABLED) { + _CPU_ISR_Restore(level); + return WPAD_ERR_NOT_READY; + } + + if(__wpads[chan]!=NULL) + wiiuse_rumble(__wpads[chan],status); + + _CPU_ISR_Restore(level); + return WPAD_ERR_NONE; +} +#endif + +#ifdef HAVE_WIIUSE_SPEAKER +s32 WPAD_ControlSpeaker(s32 chan,s32 enable) +{ + int i; + s32 ret; + u32 level; + + if(chan == WPAD_CHAN_ALL) { + for(i=WPAD_CHAN_0; i=WPAD_MAX_WIIMOTES) return WPAD_ERR_BAD_CHANNEL; + + _CPU_ISR_Disable(level); + if(__wpads_inited==WPAD_STATE_DISABLED) { + _CPU_ISR_Restore(level); + return WPAD_ERR_NOT_READY; + } + + if(__wpads[chan]!=NULL) { + __wpdcb[chan].speaker_enabled = enable; + wiiuse_set_speaker(__wpads[chan],enable); + } + + _CPU_ISR_Restore(level); + return WPAD_ERR_NONE; +} + +s32 WPAD_IsSpeakerEnabled(s32 chan) +{ + s32 ret; + u32 level; + wiimote *wm = NULL; + + if(chan=WPAD_MAX_WIIMOTES) return WPAD_ERR_BAD_CHANNEL; + + _CPU_ISR_Disable(level); + if(__wpads_inited==WPAD_STATE_DISABLED) { + _CPU_ISR_Restore(level); + return WPAD_ERR_NOT_READY; + } + + wm = __wpads[chan]; + ret = WPAD_ERR_NOT_READY; + if(wm && WIIMOTE_IS_SET(wm,WIIMOTE_STATE_CONNECTED)) { + if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_HANDSHAKE_COMPLETE) + && WIIMOTE_IS_SET(wm,WIIMOTE_STATE_SPEAKER)) ret = WPAD_ERR_NONE; + } + + _CPU_ISR_Restore(level); + return ret; +} + +void WPAD_EncodeData(WPADEncStatus *info,u32 flag,const s16 *pcmSamples,s32 numSamples,u8 *encData) +{ + int n; + short *samples = (short*)pcmSamples; + WENCStatus *status = (WENCStatus*)info; + + if(!(flag&WPAD_ENC_CONT)) status->step = 0; + + n = (numSamples+1)/2; + for(;n>0;n--) { + int nibble; + nibble = (wencdata(status,samples[0]))<<4; + nibble |= (wencdata(status,samples[1])); + *encData++ = nibble; + samples += 2; + } +} +#endif + +WPADData *WPAD_Data(int chan) +{ + //if(chan<0 || chan>=WPAD_MAX_WIIMOTES) return NULL; + return &wpaddata[chan]; +} + +u8 WPAD_BatteryLevel(int chan) +{ + //if(chan<0 || chan>=WPAD_MAX_WIIMOTES) return 0; + return wpaddata[chan].battery_level; +} + +void WPAD_IR(int chan, struct ir_t *ir) +{ + //if(chan<0 || chan>=WPAD_MAX_WIIMOTES || ir==NULL ) return; + *ir = wpaddata[chan].ir; +} + +void WPAD_Orientation(int chan, struct orient_t *orient) +{ + //if(chan<0 || chan>=WPAD_MAX_WIIMOTES || orient==NULL ) return; + *orient = wpaddata[chan].orient; +} + +void WPAD_GForce(int chan, struct gforce_t *gforce) +{ + //if(chan<0 || chan>=WPAD_MAX_WIIMOTES || gforce==NULL ) return; + *gforce = wpaddata[chan].gforce; +} + +void WPAD_Accel(int chan, struct vec3w_t *accel) +{ + //if(chan<0 || chan>=WPAD_MAX_WIIMOTES || accel==NULL ) return; + *accel = wpaddata[chan].accel; +}