diff --git a/info.h b/info.h index 74d54f55..84797954 100644 --- a/info.h +++ b/info.h @@ -7,6 +7,8 @@ #define INFO_WEB "http://gimx.fr" #define INFO_LICENCE "GNU GPL v3" +#define INFO_FW_VERSION "8.0" + #ifdef __x86_64__ #define INFO_ARCH "x86_64" #endif diff --git a/launcher/gimx-launcher.cpp b/launcher/gimx-launcher.cpp index bd368eff..b4617b9f 100644 --- a/launcher/gimx-launcher.cpp +++ b/launcher/gimx-launcher.cpp @@ -53,6 +53,12 @@ #include #endif +#include +#include + +#define BAUDRATE 500000 //bps +#define ADAPTER_TIMEOUT 1000 //millisecond + #ifdef WIN32 #define REGISTER_FUNCTION gpoll_register_handle #define REMOVE_FUNCTION gpoll_remove_handle @@ -1211,6 +1217,8 @@ launcherFrame::launcherFrame(wxWindow* parent,wxWindowID id __attribute__((unuse refreshGui(); openLog = false; + + checkFirmware(); } launcherFrame::~launcherFrame() @@ -2909,3 +2917,234 @@ void launcherFrame::OnMenuUpdateFirmware(wxCommandEvent& event __attribute__((un wxMessageBox(_("Failed to execute gimx-loader."), _("Error"), wxICON_ERROR); } } + +int adapter_read_reply(gserial_device * device, s_packet * packet, int permissive) +{ + uint8_t type = packet->header.type; + + /* + * The adapter may send a packet before it processes the command, + * so it is possible to receive a packet that is not the command response. + */ + while(1) + { + int ret = gserial_read_timeout(device, (void *) &packet->header, sizeof(packet->header), ADAPTER_TIMEOUT); + if(ret < 0 || (size_t)ret < sizeof(packet->header)) + { + if (!permissive) + { + std::cerr << "failed to read packet header from the GIMX adapter" << std::endl; + } + return -1; + } + + if (ret == sizeof(packet->header)) + { + ret = gserial_read_timeout(device, (void *) &packet->value, packet->header.length, ADAPTER_TIMEOUT); + if(ret < 0 || (size_t)ret < packet->header.length) + { + if (!permissive) + { + std::cerr << "failed to read packet data from the GIMX adapter" << std::endl; + } + return -1; + } + } + + //Check this packet is the command response. + if(packet->header.type == type) + { + return 0; + } + } + + return -1; +} + +/* + * This function should only be used in the initialization stages, i.e. before the mainloop. + */ +static int adapter_send_short_command(gserial_device * device, unsigned char type) +{ + s_packet packet = + { + .header = + { + .type = type, + .length = BYTE_LEN_0_BYTE + }, + .value = {} + }; + + int ret = gserial_write_timeout(device, &packet, sizeof(packet.header) + packet.header.length, ADAPTER_TIMEOUT); + if(ret < 0 || (size_t)ret < sizeof(packet.header)) + { + std::cerr << "failed to send data to the GIMX adapter" << std::endl; + return -1; + } + + ret = adapter_read_reply(device, &packet, 0); + + if(ret == 0) + { + if(packet.header.length != BYTE_LEN_1_BYTE) + { + std::cerr << "bad response from the GIMX adapter (invalid length)" << std::endl; + return -1; + } + return packet.value[0]; + } + + return -1; +} + +static int adapter_get_version(gserial_device * device, int *major, int *minor) +{ + s_packet packet = { .header = { .type = BYTE_VERSION, .length = 0 }, .value = {} }; + + int ret = gserial_write_timeout(device, &packet, sizeof(packet.header), ADAPTER_TIMEOUT); + if (ret < 0 || (size_t)ret != sizeof(packet.header)) + { + std::cerr << "failed to send data to the GIMX adapter" << std::endl; + return -1; + } + + ret = adapter_read_reply(device, &packet, 1); + + if(ret == 0 && packet.header.length == 2) + { + *major = packet.value[0]; + *minor = packet.value[1]; + } + else + { + *major = 5; + *minor = 8; + } + + return 0; +} + +static std::vector &split(const std::string &s, char delim, std::vector &elems) { + std::stringstream ss(s); + std::string item; + while (getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + +static std::vector split(const std::string &s, char delim) { + std::vector elems; + return split(s, delim, elems); +} + +void launcherFrame::checkFirmware() +{ + if (Output->GetStringSelection() != _("GIMX adapter")) + { + return; + } + + wxString outputSelection = OutputChoice->GetStringSelection(); + + string filename = string(launcherDir.mb_str(wxConvUTF8)); + filename.append(OUTPUT_CHOICE_FILE); + + if(!::wxFileExists(wxString(filename.c_str(), wxConvUTF8))) + { + return; + } + + string line; + getfileline(filename, line); + + if (TO_WXSTRING(line) != outputSelection) + { + return; + } + + wxString port; +#ifndef WIN32 + port.Append(wxT("/dev/")); +#endif + port.Append(outputSelection); + + gserial_device * device = gserial_open(TO_STRING(port).c_str(), BAUDRATE); + + if (device == NULL) + { + // adapter is probably not plugged, return silently + return; + } + + int rtype = adapter_send_short_command(device, BYTE_TYPE); + + if(rtype < 0 || rtype >= C_TYPE_NONE) + { + wxMessageBox(_("Failed to read firmware type."), _("Error"), wxICON_ERROR); + gserial_close(device); + return; + } + + const string firmwares[C_TYPE_MAX] = + { + [C_TYPE_JOYSTICK] = "EMUJOYSTICK.hex", + [C_TYPE_360_PAD] = "EMU360.hex", + [C_TYPE_SIXAXIS] = "EMUPS3.hex", + [C_TYPE_PS2_PAD] = "", // broken + [C_TYPE_XBOX_PAD] = "EMUXBOX.hex", + [C_TYPE_DS4] = "EMUPS4.hex", + [C_TYPE_XONE_PAD] = "EMUXONE.hex", + [C_TYPE_T300RS_PS4] = "", // unfinished + [C_TYPE_G27_PS3] = "EMUG27.hex", + [C_TYPE_G29_PS4] = "EMUG29PS4.hex", + [C_TYPE_DF_PS2] = "EMUDF.hex", + [C_TYPE_DFP_PS2] = "EMUDFP.hex", + [C_TYPE_GTF_PS2] = "EMUGTF.hex", + }; + + if (firmwares[rtype][0] == '\0') + { + // no update for this type, return silently + gserial_close(device); + return; + } + + int major, minor; + if (adapter_get_version(device, &major, &minor) < 0) + { + wxMessageBox(_("Failed to read firmware version."), _("Error"), wxICON_ERROR); + gserial_close(device); + return; + } + + gserial_close(device); + + std::vector elems = split(INFO_FW_VERSION, '.'); + if (elems.size() != 2) + { + wxMessageBox(_("Failed to parse firmware version."), _("Error"), wxICON_ERROR); + return; + } + + int sw_major = atoi(elems[0].c_str()); + int sw_minor = atoi(elems[1].c_str()); + + if (sw_major < major || (sw_major == major && sw_minor <= minor)) + { + // firmware is up to date + return; + } + + int answer = wxMessageBox(_("A firmware update is available.\nStart firmware update?"), _("Confirm"), wxYES_NO); + if (answer != wxYES) + { + return; + } + + if (wxExecute(wxT("gimx-loader -f ") + TO_WXSTRING(firmwares[rtype]), wxEXEC_SYNC)) + { + wxMessageBox(_("Failed to execute gimx-loader."), _("Error"), wxICON_ERROR); + } +} diff --git a/launcher/gimx-launcher.h b/launcher/gimx-launcher.h index b9e0850c..f37d52e3 100644 --- a/launcher/gimx-launcher.h +++ b/launcher/gimx-launcher.h @@ -128,6 +128,8 @@ class launcherFrame: public wxFrame void initDownload(wxProgressDialog * dlg); void cleanDownload(); + void checkFirmware(); + //(*Identifiers(launcherFrame) static const long ID_STATICTEXT4; static const long ID_CHOICE1; diff --git a/launcher/launcherApp.h b/launcher/launcherApp.h index 8147b271..20e18aa4 100644 --- a/launcher/launcherApp.h +++ b/launcher/launcherApp.h @@ -15,4 +15,4 @@ class launcherApp : public wxApp virtual int OnExit(); }; -#endif // SIXEMUGUIAPP_H +#endif // LAUNCHERAPP_H diff --git a/loader/Makefile b/loader/Makefile index facc5f73..00affcf8 100644 --- a/loader/Makefile +++ b/loader/Makefile @@ -11,6 +11,10 @@ NAME=$(shell basename "$(shell pwd)") CPPFLAGS += `wx-config --cflags` -Winvalid-pch -include wx_pch.h -DWX_PRECOMP -Wno-cast-function-type -Wno-deprecated-copy -Wno-ignored-qualifiers LDLIBS += `wx-config --libs` -lstdc++ +LDLIBS += $(GIMXTIME_LDLIBS) + +LDFLAGS += $(GIMXTIME_LDFLAGS) + OBJECTS := $(patsubst %.cpp,%.o,$(wildcard *.cpp)) OUT = gimx-$(NAME) diff --git a/loader/gimx-loader.cpp b/loader/gimx-loader.cpp index 029d8c44..9cc33c4c 100644 --- a/loader/gimx-loader.cpp +++ b/loader/gimx-loader.cpp @@ -35,6 +35,8 @@ #include #endif +#include + using namespace std; #define MAX_PORT_NB 257 // 0 to 256 @@ -87,7 +89,7 @@ BEGIN_EVENT_TABLE(loaderFrame,wxFrame) //*) END_EVENT_TABLE() -loaderFrame::loaderFrame(wxWindow* parent,wxWindowID id __attribute__((unused))) +loaderFrame::loaderFrame(wxString firmware,wxWindow* parent,wxWindowID id __attribute__((unused))) { locale = new wxLocale(wxLANGUAGE_DEFAULT); #ifdef WIN32 @@ -153,6 +155,14 @@ loaderFrame::loaderFrame(wxWindow* parent,wxWindowID id __attribute__((unused))) Panel1->Fit(); Fit(); Refresh(); + + if (!firmware.IsEmpty()) + { + selected = firmware; + ChoiceFirmware->SetSelection(ChoiceFirmware->FindString(firmware)); + wxCommandEvent event; + OnButtonLoadClick(event); + } } loaderFrame::~loaderFrame() { @@ -215,15 +225,18 @@ void loaderFrame::OnButtonLoadClick(wxCommandEvent& event __attribute__((unused) list_ports(ports); int i = -1; - int count; + int found = -1; { wxWindowDisabler disableAll; wxBusyInfo wait(_("Unplug/replug the USB cable from/to computer USB port.")); - for (count = 0; count < 1000; ++count) { + // We don't compute timeout based on check_port call count. + // On Windows check_port takes some time (100ms on my desktop). + gtime start = gtime_gettime(); + do { for (i = 0; i < MAX_PORT_NB; ++i) { - int found = check_port(i); + found = check_port(i); if (found != -1) { if (ports[i] == 0) { break; // found @@ -238,12 +251,17 @@ void loaderFrame::OnButtonLoadClick(wxCommandEvent& event __attribute__((unused) usleep(10000); // we need to be aggressive to detect removal wxTheApp->Yield(); - } + + } while (gtime_gettime() - start < 10000000000ULL); } - if (count == 1000) { + if (found == -1) { wxMessageBox(_("No new device found within 10 seconds."), _("Error"), wxICON_ERROR); ButtonLoad->Enable(true); + if (!selected.IsEmpty()) { + wxCommandEvent event; + OnQuit(event); + } return; } @@ -267,6 +285,10 @@ void loaderFrame::OnButtonLoadClick(wxCommandEvent& event __attribute__((unused) if (!wxExecute(command, wxEXEC_ASYNC | wxEXEC_NOHIDE, process)) { wxMessageBox(_("failed to load firmware"), _("Error"), wxICON_ERROR); ButtonLoad->Enable(true); + if (!selected.IsEmpty()) { + wxCommandEvent event; + OnQuit(event); + } } } @@ -281,5 +303,10 @@ void loaderFrame::OnProcessTerminated(wxProcess *process __attribute__((unused)) } SetFocus(); + + if (!selected.IsEmpty()) { + wxCommandEvent event; + OnQuit(event); + } } diff --git a/loader/gimx-loader.h b/loader/gimx-loader.h index ee4796bd..dc098a4c 100644 --- a/loader/gimx-loader.h +++ b/loader/gimx-loader.h @@ -21,7 +21,7 @@ class loaderFrame: public wxFrame { public: - loaderFrame(wxWindow* parent,wxWindowID id = -1); + loaderFrame(wxString firmware,wxWindow* parent,wxWindowID id = -1); virtual ~loaderFrame(); void OnProcessTerminated(wxProcess *process, int status); @@ -53,6 +53,8 @@ class loaderFrame: public wxFrame wxLocale* locale; + wxString selected; + DECLARE_EVENT_TABLE() }; diff --git a/loader/loaderApp.cpp b/loader/loaderApp.cpp index 6b5a288e..b9a7e8d3 100644 --- a/loader/loaderApp.cpp +++ b/loader/loaderApp.cpp @@ -15,12 +15,16 @@ IMPLEMENT_APP(loaderApp); bool loaderApp::OnInit() { + // call default behaviour (mandatory) + if (!wxApp::OnInit()) + return false; + //(*AppInitialize bool wxsOK = true; wxInitAllImageHandlers(); if ( wxsOK ) { - loaderFrame* Frame = new loaderFrame(0); + loaderFrame* Frame = new loaderFrame(firmware, 0); Frame->Show(); SetTopWindow(Frame); } @@ -33,3 +37,17 @@ int loaderApp::OnExit() { return 0; } + +void loaderApp::OnInitCmdLine(wxCmdLineParser& parser) +{ + parser.SetDesc (g_cmdLineDesc); + // must refuse '/' as parameter starter or cannot use "/path" style paths + parser.SetSwitchChars (wxT("-")); +} + +bool loaderApp::OnCmdLineParsed(wxCmdLineParser& parser) +{ + parser.Found(wxT("f"), &firmware); + + return true; +} diff --git a/loader/loaderApp.h b/loader/loaderApp.h index e0038709..b6d6a340 100644 --- a/loader/loaderApp.h +++ b/loader/loaderApp.h @@ -3,16 +3,30 @@ License: GPLv3 */ -#ifndef LAUNCHERAPP_H -#define LAUNCHERAPP_H +#ifndef LOADERAPP_H +#define LOADERAPP_H #include +#include class loaderApp : public wxApp { public: virtual bool OnInit(); virtual int OnExit(); + virtual void OnInitCmdLine(wxCmdLineParser& parser); + virtual bool OnCmdLineParsed(wxCmdLineParser& parser); + + private: + wxString firmware; +}; + +static const wxCmdLineEntryDesc g_cmdLineDesc [] = +{ + { wxCMD_LINE_OPTION, wxT_2("f"), _("firmware"), _("specifies a firmware to load"), + wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL }, + + { wxCMD_LINE_NONE, NULL, NULL, NULL, wxCMD_LINE_VAL_NONE, 0 } }; -#endif // SIXEMUGUIAPP_H +#endif // LOADERAPP_H diff --git a/shared/gimx-adapter-protocol b/shared/gimx-adapter-protocol index f087e131..a86ed89f 160000 --- a/shared/gimx-adapter-protocol +++ b/shared/gimx-adapter-protocol @@ -1 +1 @@ -Subproject commit f087e1318c6732422c55297f9c7e1d9cbbb47f18 +Subproject commit a86ed89f43700cfb909bc92e3c8263cec335c2f9 diff --git a/shared/gimxtime b/shared/gimxtime index f4401d91..ddc7fcde 160000 --- a/shared/gimxtime +++ b/shared/gimxtime @@ -1 +1 @@ -Subproject commit f4401d919205004c4585683b27fe1bdea1ebc512 +Subproject commit ddc7fcdee61962e81cb870dd240a1d4efa441b0e