Some progress understanding MPU messages:
Button codesThey are encoded like this:
0x06, 0x05, 0x06, 0x00, btn_code, btn_code_arg
The button codes can be found from bindReceiveSwitch - this translates the MPU button codes (btn_code, btn_code_arg) into GUI button codes as used by GuiMainTask (the BGMT constants from gui.h). When the GUI button codes are sent to GuiMainTask, they appear in the debug log as "GUI_Control:%d 0x%x". When they are actually processed, they appear as "GUI_CONTROL:%d".
Since finding these button codes for each camera would be incredibly boring, I wrote
a Python script to get them automatically from the ROM, by directly emulating bindReceiveSwitch in
unicorn, trying all usual input values and checking debug messages.
btn_code_arg meaning can be: press/unpress (1,0), scrollwheel direction (1, -1) and number of steps for very fast turns (2, -3 etc), or some buttons can be grouped under a single btn_code (for example, the direction pad).
Many button codes are common across all cameras (DIGIC 4 and 5): MENU (0,1), INFO (1,1), PLAY (3,1), DELETE (4,1), SET (12,1/0), scrollwheels (13 and 14), others are not.
btn_code = 30 is ServiceMenu on all cameras. Interesting string on 1200D: "Enter Secret mode Electric Shutter!!!".
Some cameras also use a generic event, GUICMD_PRESS_BUTTON_SOMETHING, which I don't know how to interpret (other than some button was pressed).
Side note: this research uncovered a few subtle bugs regarding button codes on 600D, 100D and EOS M, and not-so-subtle on 1100D (see
this PR).
GUI modesOn 600D, menu navigation looks somewhat like this ("spell" being data sent from ICU to MPU, and "reply" being the response from MPU):
{ 0x06, 0x05, 0x03, 0x19, 0x00, 0x00 }, { /* spell #44 */
{ 0x06, 0x05, 0x03, 0x17, 0x9a, 0x00 }, /* reply #44.1 */
{ 0x06, 0x05, 0x06, 0x26, 0x01, 0x00 }, /* reply #44.2, GUI_Control:76, bindReceiveSwitch(38, 1) */
{ 0x06, 0x05, 0x06, 0x00, 0x01, 0x00 }, /* reply #44.3, BGMT_MENU, GUI_Control:6, bindReceiveSwitch(0, 1) */
{ 0x06, 0x05, 0x04, 0x0d, 0x00, 0x00 }, /* reply #44.4 */
{ 0 } } }, {
{ 0x06, 0x05, 0x03, 0x19, 0x00, 0x00 }, { /* spell #45 */
{ 0x06, 0x05, 0x06, 0x26, 0x01, 0x00 }, /* reply #45.1, GUI_Control:76, bindReceiveSwitch(38, 1) */
{ 0x06, 0x05, 0x06, 0x00, 0x01, 0x00 }, /* reply #45.2, BGMT_MENU, GUI_Control:6, bindReceiveSwitch(0, 1) */
{ 0 } } }, {
{ 0x06, 0x05, 0x04, 0x00, 0x01, 0x00 }, { /* spell #46, NotifyGUIEvent(1) */
{ 0x06, 0x05, 0x06, 0x0a, 0x00, 0x00 }, /* reply #46.1, BGMT_UNPRESS_ZOOMOUT_MAYBE, GUI_Control:17, bindReceiveSwitch(10, 0) */
{ 0x06, 0x05, 0x06, 0x09, 0x00, 0x00 }, /* reply #46.2, BGMT_UNPRESS_ZOOMIN_MAYBE, GUI_Control:15, bindReceiveSwitch(9, 0) */
{ 0x06, 0x05, 0x04, 0x00, 0x01, 0x01 }, /* reply #46.3 */
{ 0x0e, 0x0c, 0x0a, 0x08, 0x11, 0x00, 0x15, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00 },/* reply #46.4 */
{ 0 } } }, {
{ 0x08, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00 }, { /* spell #47, Complete WaitID = 0x80020000 */
{ 0 } } }, {
{ 0x06, 0x05, 0x03, 0x34, 0x00, 0x00 }, { /* spell #48 */
{ 0 } } }, {
{ 0x06, 0x05, 0x03, 0x19, 0x00, 0x00 }, { /* spell #49 */
{ 0x06, 0x05, 0x06, 0x26, 0x01, 0x00 }, /* reply #49.1, GUI_Control:76, bindReceiveSwitch(38, 1) */
{ 0x06, 0x05, 0x06, 0x1a, 0x01, 0x00 }, /* reply #49.2, BGMT_PRESS_RIGHT, GUI_Control:35, bindReceiveSwitch(26, 1) */
{ 0x06, 0x05, 0x06, 0x1a, 0x00, 0x00 }, /* reply #49.3, BGMT_UNPRESS_RIGHT, GUI_Control:36, bindReceiveSwitch(26, 0) */
{ 0x06, 0x05, 0x06, 0x26, 0x01, 0x00 }, /* reply #49.4, GUI_Control:76, bindReceiveSwitch(38, 1) */
{ 0x06, 0x05, 0x06, 0x1a, 0x01, 0x00 }, /* reply #49.5, BGMT_PRESS_RIGHT, GUI_Control:35, bindReceiveSwitch(26, 1) */
{ 0x06, 0x05, 0x06, 0x1a, 0x00, 0x00 }, /* reply #49.6, BGMT_UNPRESS_RIGHT, GUI_Control:36, bindReceiveSwitch(26, 0) */
NotifyGUIEvent (called by SetGUIRequestMode) sends a message like this:
0x06, 0x05, 0x04, 0x00, event_code, 0x00
The MPU is supposed to reply something, probably this:
0x06, 0x05, 0x04, 0x00, event_code, 0x01
Now the interesting part: if I enable the NotifyGUIEvent reply on 60D, and the other cameras that accept the same MPU spell set, they no longer boot the GUI: instead, they go to the date/time dialog. You can adjust the date/time in QEMU using the arrow keys, scrollwheels, and the spacebar (SET), but when pressing OK, the GUI freezes.
Obviously, with NotifyGUIEvent disabled, the GUI mode can't be changed (so you can't enter Canon menu, or playback mode, or whatever).
The question is: how to make the GUI mode switches work, so one can navigate the menu?
Note: with current implementation, you can already navigate ML menu
without CONFIG_QEMU=y... if you define GUIMODE_ML_MENU = 0 (so it won't try to change the GUI mode, because that part doesn't work). One big step closer towards running unmodified ML in QEMU
