Skip to content

Commit

Permalink
Firmware update detection on gimx-launcher start #657
Browse files Browse the repository at this point in the history
  • Loading branch information
matlo committed Apr 29, 2020
1 parent 01d5102 commit 6dbb7d1
Show file tree
Hide file tree
Showing 11 changed files with 322 additions and 14 deletions.
2 changes: 2 additions & 0 deletions info.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
239 changes: 239 additions & 0 deletions launcher/gimx-launcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@
#include <tlhelp32.h>
#endif

#include <gimxserial/include/gserial.h>
#include <gimx-adapter-protocol/include/protocol.h>

#define BAUDRATE 500000 //bps
#define ADAPTER_TIMEOUT 1000 //millisecond

#ifdef WIN32
#define REGISTER_FUNCTION gpoll_register_handle
#define REMOVE_FUNCTION gpoll_remove_handle
Expand Down Expand Up @@ -1211,6 +1217,8 @@ launcherFrame::launcherFrame(wxWindow* parent,wxWindowID id __attribute__((unuse
refreshGui();

openLog = false;

checkFirmware();
}

launcherFrame::~launcherFrame()
Expand Down Expand Up @@ -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<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while (getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}

static std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> 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<std::string> 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);
}
}
2 changes: 2 additions & 0 deletions launcher/gimx-launcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion launcher/launcherApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ class launcherApp : public wxApp
virtual int OnExit();
};

#endif // SIXEMUGUIAPP_H
#endif // LAUNCHERAPP_H
4 changes: 4 additions & 0 deletions loader/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
39 changes: 33 additions & 6 deletions loader/gimx-loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
#include <stdlib.h>
#endif

#include <gimxtime/include/gtime.h>

using namespace std;

#define MAX_PORT_NB 257 // 0 to 256
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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
Expand All @@ -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;
}

Expand All @@ -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);
}
}
}

Expand All @@ -281,5 +303,10 @@ void loaderFrame::OnProcessTerminated(wxProcess *process __attribute__((unused))
}

SetFocus();

if (!selected.IsEmpty()) {
wxCommandEvent event;
OnQuit(event);
}
}

Loading

0 comments on commit 6dbb7d1

Please sign in to comment.