diff --git a/Makefile.wii b/Makefile.wii index 8d2788a23a..7eaf7ec46b 100644 --- a/Makefile.wii +++ b/Makefile.wii @@ -30,13 +30,13 @@ ELF2DOL = $(DEVKITPPC)/bin/elf2dol$(EXE_EXT) DOL_TARGET := retroarch_wii.dol ELF_TARGET := retroarch_wii.elf -INCLUDE := -I. -I$(DEVKITPRO)/libogc/include +INCLUDE := -I. -I$(DEVKITPRO)/libogc/include -I$(DEVKITPRO)/libogc/include/ogc LIBDIRS := -L$(DEVKITPRO)/libogc/lib/wii -L. MACHDEP := -DGEKKO -DHW_RVL -mrvl -mcpu=750 -meabi -mhard-float CFLAGS += -Wall -std=gnu99 $(MACHDEP) $(INCLUDE) LDFLAGS := $(MACHDEP) -Wl,-Map,$(notdir $(ELF_TARGET)).map,-wrap,malloc,-wrap,free,-wrap,memalign,-wrap,calloc,-wrap,realloc,-wrap,strdup,-wrap,strndup,-wrap,malloc_usable_size -T gx/ld/rvl.ld -LIBS := -lfat -lretro_wii -lwiiuse -logc -lbte +LIBS := -lfat -lretro_wii -logc -lbte APP_BOOTER_DIR = wii/app_booter @@ -57,7 +57,7 @@ CFLAGS += -DHAVE_FILE_LOGGER CFLAGS += -Iconsole/logger endif -CFLAGS += -std=gnu99 -DHAVE_DEFAULT_RETROPAD_INPUT -DHAVE_RGUI -DHAVE_THREAD -DRARCH_CONSOLE -DHAVE_LIBRETRO_MANAGEMENT -DHAVE_RARCH_EXEC -DHAVE_RMENU -DGEKKO -DHAVE_ZLIB -DWANT_RZLIB -DHAVE_RARCH_MAIN_WRAP -DHAVE_GRIFFIN=1 -DHAVE_SCREENSHOTS -DPACKAGE_VERSION=\"0.9.8-beta3\" -Dmain=rarch_main -Wno-char-subscripts +CFLAGS += -std=gnu99 -DHAVE_DEFAULT_RETROPAD_INPUT -DHAVE_WIIUSE -DHAVE_RGUI -DHAVE_THREAD -DRARCH_CONSOLE -DHAVE_LIBRETRO_MANAGEMENT -DHAVE_RARCH_EXEC -DHAVE_RMENU -DGEKKO -DHAVE_ZLIB -DWANT_RZLIB -DHAVE_RARCH_MAIN_WRAP -DHAVE_GRIFFIN=1 -DHAVE_SCREENSHOTS -DPACKAGE_VERSION=\"0.9.8-beta3\" -Dmain=rarch_main -Wno-char-subscripts ifeq ($(DEBUG), 1) CFLAGS += -O0 -g -DDEBUG diff --git a/console/griffin/griffin.c b/console/griffin/griffin.c index 590b5a8287..17ad14123f 100644 --- a/console/griffin/griffin.c +++ b/console/griffin/griffin.c @@ -227,6 +227,20 @@ INPUT #include "../../input/overlay.c" #endif +#ifdef HAVE_WIIUSE +#include "../../wii/wiiuse/classic.c" +#include "../../wii/wiiuse/dynamics.c" +#include "../../wii/wiiuse/events.c" +#include "../../wii/wiiuse/io.c" +#include "../../wii/wiiuse/io_wii.c" +#include "../../wii/wiiuse/ir.c" +#include "../../wii/wiiuse/motion_plus.c" +#include "../../wii/wiiuse/nunchuk.c" +#include "../../wii/wiiuse/speaker.c" +#include "../../wii/wiiuse/wiiuse.c" +#include "../../wii/wiiuse/wpad.c" +#endif + #if defined(__CELLOS_LV2__) #include "../../ps3/ps3_input.c" #elif defined(SN_TARGET_PSP2) || defined(PSP) diff --git a/wii/wiiuse/classic.c b/wii/wiiuse/classic.c new file mode 100644 index 0000000000..c37e94f0a9 --- /dev/null +++ b/wii/wiiuse/classic.c @@ -0,0 +1,194 @@ +/* + * 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; + */ + 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; + } + + + /* 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; + */ + 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/wiiuse/classic.h b/wii/wiiuse/classic.h new file mode 100644 index 0000000000..8ae62583f4 --- /dev/null +++ b/wii/wiiuse/classic.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#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/wiiuse/definitions.h b/wii/wiiuse/definitions.h new file mode 100644 index 0000000000..04dc5be6d8 --- /dev/null +++ b/wii/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/wiiuse/dynamics.c b/wii/wiiuse/dynamics.c new file mode 100644 index 0000000000..805e3ba5e6 --- /dev/null +++ b/wii/wiiuse/dynamics.c @@ -0,0 +1,230 @@ +/* + * 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; +} + + +/** + * @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, ang; + + /* + * 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 */ + ang = RAD_TO_DEGREE(atanf(ry / rx)); + ang -= 90.0f; + if (rx < 0.0f) + ang -= 180.0f; + js->ang = absf(ang); + js->mag = (float) sqrt((rx * rx) + (ry * 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/wiiuse/dynamics.h b/wii/wiiuse/dynamics.h new file mode 100644 index 0000000000..4daec29f01 --- /dev/null +++ b/wii/wiiuse/dynamics.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/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. + */ + +#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/wiiuse/events.c b/wii/wiiuse/events.c new file mode 100644 index 0000000000..85b1768040 --- /dev/null +++ b/wii/wiiuse/events.c @@ -0,0 +1,331 @@ +#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]) { + //WIIUSE_WARNING("Unsolicited event ack: report %02x status %02x", msg[2], msg[3]); + return; + } + if(msg[3]) { + //WIIUSE_WARNING("Command %02x %02x failed: status %02x", cmd->data[0], cmd->data[1], 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; + int speaker = 0; + //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; + if((msg[2]&WM_CTRL_STATUS_BYTE1_SPEAKER_ENABLED)==WM_CTRL_STATUS_BYTE1_SPEAKER_ENABLED) speaker = 1; + 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); + + 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); + + 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/wiiuse/events.h b/wii/wiiuse/events.h new file mode 100644 index 0000000000..b6f0c75542 --- /dev/null +++ b/wii/wiiuse/events.h @@ -0,0 +1,14 @@ +#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/wiiuse/io.c b/wii/wiiuse/io.c new file mode 100644 index 0000000000..4266319ab3 --- /dev/null +++ b/wii/wiiuse/io.c @@ -0,0 +1,150 @@ +#include +#include + +#include "definitions.h" +#include "wiiuse_internal.h" +#include "nunchuk.h" +#include "classic.h" +#include "motion_plus.h" +#include "io.h" +#include "lwp_wkspace.inl" + +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); + + buf = __lwp_wkspace_allocate(sizeof(ubyte)*8); + wiiuse_read_data(wm,buf,WM_MEM_OFFSET_CALIBRATION,7,wiiuse_handshake); + break; + case 1: + wm->handshake_state++; + + 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); + break; + default: + break; + + } +} + +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: + 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/wiiuse/io.h b/wii/wiiuse/io.h new file mode 100644 index 0000000000..d7a5326a82 --- /dev/null +++ b/wii/wiiuse/io.h @@ -0,0 +1,23 @@ +#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/wiiuse/io_wii.c b/wii/wiiuse/io_wii.c new file mode 100644 index 0000000000..63a693395f --- /dev/null +++ b/wii/wiiuse/io_wii.c @@ -0,0 +1,176 @@ +#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)); + WIIMOTE_DISABLE_STATE(wm, (WIIMOTE_STATE_SPEAKER|WIIMOTE_STATE_SPEAKER_INIT)); + 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_wkspace_allocate(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/wiiuse/ir.c b/wii/wiiuse/ir.c new file mode 100644 index 0000000000..c369778fd7 --- /dev/null +++ b/wii/wiiuse/ir.c @@ -0,0 +1,834 @@ +#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/wiiuse/ir.h b/wii/wiiuse/ir.h new file mode 100644 index 0000000000..f90cfc89c5 --- /dev/null +++ b/wii/wiiuse/ir.h @@ -0,0 +1,22 @@ +#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/wiiuse/license_libogc.txt b/wii/wiiuse/license_libogc.txt new file mode 100644 index 0000000000..a149fce1ce --- /dev/null +++ b/wii/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/wiiuse/lwp_priority.inl b/wii/wiiuse/lwp_priority.inl new file mode 100644 index 0000000000..75c6438386 --- /dev/null +++ b/wii/wiiuse/lwp_priority.inl @@ -0,0 +1,49 @@ +#ifndef __LWP_PRIORITY_INL__ +#define __LWP_PRIORITY_INL__ + +static __inline__ void __lwp_priomap_init(prio_cntrl *theprio,u32 prio) +{ + u32 major,minor,mask; + + major = prio/16; + 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; +#ifdef _LWPPRIO_DEBUG + printf("__lwp_priomap_init(%p,%d,%p,%d,%d,%d,%d)\n",theprio,prio,theprio->minor,theprio->ready_major,theprio->ready_minor,theprio->block_major,theprio->block_minor); +#endif +} + +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,minor; + major = cntlzw(_prio_major_bitmap); + minor = cntlzw(_prio_bitmap[major]); +#ifdef _LWPPRIO_DEBUG + printf("__lwp_priomap_highest(%d)\n",((major<<4)+minor)); +#endif + return ((major<<4)+minor); +} + +#endif diff --git a/wii/wiiuse/lwp_threads.inl b/wii/wiiuse/lwp_threads.inl new file mode 100644 index 0000000000..a3b960ff82 --- /dev/null +++ b/wii/wiiuse/lwp_threads.inl @@ -0,0 +1,93 @@ +#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; +#ifdef _LWPTHREADS_DEBUG + printf("__lwp_thread_calcheir(%p)\n",_thr_heir); +#endif +} + +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/wiiuse/lwp_watchdog.inl b/wii/wiiuse/lwp_watchdog.inl new file mode 100644 index 0000000000..5216e10996 --- /dev/null +++ b/wii/wiiuse/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/wiiuse/lwp_wkspace.inl b/wii/wiiuse/lwp_wkspace.inl new file mode 100644 index 0000000000..4aa8f0faf0 --- /dev/null +++ b/wii/wiiuse/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/wiiuse/motion_plus.c b/wii/wiiuse/motion_plus.c new file mode 100644 index 0000000000..74ad17c98a --- /dev/null +++ b/wii/wiiuse/motion_plus.c @@ -0,0 +1,92 @@ +#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/wiiuse/motion_plus.h b/wii/wiiuse/motion_plus.h new file mode 100644 index 0000000000..9f587cacf5 --- /dev/null +++ b/wii/wiiuse/motion_plus.h @@ -0,0 +1,23 @@ +/** + * @file + * @brief Motion plus extension + */ + +#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/wiiuse/nunchuk.c b/wii/wiiuse/nunchuk.c new file mode 100644 index 0000000000..950c0eeb57 --- /dev/null +++ b/wii/wiiuse/nunchuk.c @@ -0,0 +1,154 @@ +#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/wiiuse/nunchuk.h b/wii/wiiuse/nunchuk.h new file mode 100644 index 0000000000..973412edda --- /dev/null +++ b/wii/wiiuse/nunchuk.h @@ -0,0 +1,18 @@ +#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/wiiuse/os.h b/wii/wiiuse/os.h new file mode 100644 index 0000000000..aa5bea37c5 --- /dev/null +++ b/wii/wiiuse/os.h @@ -0,0 +1,28 @@ +#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/wiiuse/speaker.c b/wii/wiiuse/speaker.c new file mode 100644 index 0000000000..4c35197b79 --- /dev/null +++ b/wii/wiiuse/speaker.c @@ -0,0 +1,142 @@ +#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/wiiuse/speaker.h b/wii/wiiuse/speaker.h new file mode 100644 index 0000000000..d7c61cca0b --- /dev/null +++ b/wii/wiiuse/speaker.h @@ -0,0 +1,30 @@ +#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/wiiuse/wiiuse.c b/wii/wiiuse/wiiuse.c new file mode 100644 index 0000000000..43d43f0118 --- /dev/null +++ b/wii/wiiuse/wiiuse.c @@ -0,0 +1,330 @@ +#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; + if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_RUMBLE)) cmd->data[1] |= 0x01; + + 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_wkspace_allocate(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_wkspace_allocate(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. + */ +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); +} + +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/wiiuse/wiiuse_internal.h b/wii/wiiuse/wiiuse_internal.h new file mode 100644 index 0000000000..149695259d --- /dev/null +++ b/wii/wiiuse/wiiuse_internal.h @@ -0,0 +1,275 @@ +/* + * 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. + */ + +#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_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_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/wiiuse/wpad.c b/wii/wiiuse/wpad.c new file mode 100644 index 0000000000..1f1c067114 --- /dev/null +++ b/wii/wiiuse/wpad.c @@ -0,0 +1,1242 @@ +/*------------------------------------------------------------- + +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. + +-------------------------------------------------------------*/ + +#include +#include +#include +#include +#include + +#include "os.h" +#include "lwp_wkspace.inl" +#include "lwp_priority.inl" +#include "lwp_watchdog.inl" +#include "lwp_threads.inl" +#include "conf.h" +#include "ir.h" +#include "speaker.h" +#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; + + void *sound_data; + u32 sound_len; + u32 sound_off; + syswd_t sound_alarm; + + WPADData lstate; + WPADData *queue_ext; + 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; + + __lwp_thread_dispatchdisable(); + 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); + } + } + } + __lwp_thread_dispatchunnest(); +} + +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); +} + +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]; + + if(wpdcb->queue_ext!=NULL) { + maxbufs = wpdcb->queue_length; + wpadd = &(wpdcb->queue_ext[wpdcb->queue_tail]); + } else { + 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); + wiiuse_set_speaker(wm,wpdcb->speaker_enabled); + __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; + wpdcb->queue_ext = NULL; + 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]; + if(wpdcb->queue_ext!=NULL) { + maxbufs = wpdcb->queue_length; + wpadd = wpdcb->queue_ext; + } else { + 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; +} + +s32 WPAD_DroppedEvents(s32 chan) +{ + u32 level; + s32 ret; + int i; + int dropped = 0; + + 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) { + dropped = __wpdcb[chan].dropped_events; + __wpdcb[chan].dropped_events = 0; + } + _CPU_ISR_Restore(level); + return dropped; +} + +s32 WPAD_Flush(s32 chan) +{ + s32 ret; + int i; + int count = 0; + if(chan == WPAD_CHAN_ALL) { + for(i=WPAD_CHAN_0; i= 0) + 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; + + if(chan == WPAD_CHAN_ALL) { + for(i=WPAD_CHAN_0; i= WPAD_ERR_NONE) + count += ret; + return count; + } + + 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_SetDataFormat(s32 chan, s32 fmt) +{ + 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) { + switch(fmt) { + case WPAD_FMT_BTNS: + case WPAD_FMT_BTNS_ACC: + case WPAD_FMT_BTNS_ACC_IR: + __wpdcb[chan].data_fmt = fmt; + __wpad_setfmt(chan); + break; + default: + _CPU_ISR_Restore(level); + return WPAD_ERR_BADVALUE; + } + } + _CPU_ISR_Restore(level); + return WPAD_ERR_NONE; +} + +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; +} + +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; +} + +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); + __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); +} + +s32 WPAD_ScanPads() +{ + return WPAD_ReadPending(WPAD_CHAN_ALL, NULL); +} + +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; +} + +s32 WPAD_SetIdleThresholds(s32 chan, s32 btns, s32 ir, s32 accel, s32 js, s32 wb, s32 mp) +{ + 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; + } + + __wpdcb[chan].thresh.btns = (btns<0) ? -1 : 0; + __wpdcb[chan].thresh.ir = ir; + __wpdcb[chan].thresh.acc = accel; + __wpdcb[chan].thresh.js = js; + __wpdcb[chan].thresh.mp = mp; + + + _CPU_ISR_Restore(level); + return WPAD_ERR_NONE; +} + +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; +} + +s32 WPAD_SendStreamData(s32 chan,void *buf,u32 len) +{ + u32 level; + struct timespec tb; + 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!=NULL && WIIMOTE_IS_SET(wm,WIIMOTE_STATE_CONNECTED)) { + if(WIIMOTE_IS_SET(wm,WIIMOTE_STATE_HANDSHAKE_COMPLETE) + && WIIMOTE_IS_SET(wm,WIIMOTE_STATE_SPEAKER)) { + __wpdcb[chan].sound_data = buf; + __wpdcb[chan].sound_len = len; + __wpdcb[chan].sound_off = 0; + + tb.tv_sec = 0; + tb.tv_nsec = 6666667; + SYS_SetPeriodicAlarm(__wpdcb[chan].sound_alarm,&tb,&tb,__wpad_sounddata_alarmhandler, &__wpdcb[chan]); + } + } + + _CPU_ISR_Restore(level); + return WPAD_ERR_NONE; +} + +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; + } +} + +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; +} + +u32 WPAD_ButtonsHeld(int chan) +{ + //if(chan<0 || chan>=WPAD_MAX_WIIMOTES) return 0; + return wpaddata[chan].btns_h; +} + +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; +} + +void WPAD_Expansion(int chan, struct expansion_t *exp) +{ + //if(chan<0 || chan>=WPAD_MAX_WIIMOTES || exp==NULL ) return; + *exp = wpaddata[chan].exp; +}