Magic Lantern Firmware Wiki
Register
Advertisement

This page will describe how to extend the Magic Lantern code in order to implement new features.

See also Magic Lantern API, DryOS API, Debugging Magic Lantern ...

HOWTO's[]

Note: these instructions reflect my incomplete understanding of Magic Lantern code, and may not work.

How to debug Magic Lantern code?[]

See Debugging Magic Lantern.

How to detect when the user presses a button?[]

There are two sets of button codes:

  • One set of codes is received by gui_main_task (which is overriden by ML). These codes are defined as constants, like BGMT_PRESS_LEFT, BGMT_UNPRESS_RIGHT etc, in consts-*.h. They are camera dependent.
  • Another set of codes is received by menu_handler. Those codes are defined in gui.h and seem to be (more or less) the same across cameras. Currently, this happens only when ML menu is active.

A few notes:

  • Some buttons send an event during both press and unpress, others only send an event when pressed. For these buttons, it's also possible to detect short and long presses.
  • Some buttons can only be detected by the first set of codes, others only by the second set of codes.
  • Some buttons send the same events and it's difficult to differentiate between them.
  • Some buttons send different codes in different camera states (e.g. you can't detect arrow buttons when ISO adjustment dialog/mode is active).

Todo:

  • How to detect wheel events?
  • Which is the hardware address of the buttons? (equivalent of PhySw from CHDK)
  • How to detect certain keys, like DOF button in movie mode, or ISO button in Play mode?

Low-level details: GUI Events/550D

How to simulate button presses?[]

http://magiclantern.wikia.com/wiki/DevFAQ#How_to_fake_button_presses.3F

How to get info about the camera state?[]

Play mode or not?[]

Property PROP_GUI_STATE takes those values:

0 = IDLE
1 = PLAYMENU
3 = START_QR_MODE

Useful macros:

PLAY_MODE
MENU_MODE

LCD sensor[]

LCD sensor is infrared (see [1]). It could be used as a remote control when camera is sitting the tripod. Just put your hand near the screen to take a picture.

See GUI Events/550D. Function menu_handler receives it as event 100000a6, with *(arg3) = 80020010.

This sensor is called DisplaySensor and changes the property 80020010 (not tested). It is active only outside LiveView.

Others[]

  • read the current shooting mode (mode dial position) => PROP_SHOOTING_MODE from property.h.
  • Rotation sensor: is it digital or analog? If it's analog, is accurate enough to be used as a digital levelling indicator? (how do you call that in English?: maybe a Accelerometer/G-sensor?)
  • Focus confirmation sensor: for implementing trap focus shooting mode

LCD sensor and focus sensor seem to send some GUI events => need to decode them.

How to turn on the camera LEDs[]

- card activity LED: [2]

- self-timer led (which is bright enough to be used as focus assist light!)

- LEDs in the viewfinder (focus confirmation and settings display)

- display brightness: change PROP_BACKLIGHT_LEVEL, from 1 to 7

How to change exposure time / aperture / ISO?[]

lens_set_shutter(SHUTTER_250);
lens_set_ae(APERTURE_2_8);
lens_set_iso(ISO_1600);

See lens.h and lens.c for low level implementation.

See also: Properties (there are lots of camera settings there).

How to create a menu?[]

Menu hierarchy[]

  • Top level menus
  • A top level menu contains some menu entries

todo: put a screenshot

You register a top level menu with menu_add. Before, you have to define its entries.

Menu entries[]

There are three types of menu entries:

  • simple menu items, which display a fixed text and execute a function when selected;
  • binary (ON/OFF) entries, for which you should provide a function for displaying the menu text;
  • menu items with user-defined text and action; you provide a function for displaying the menu text and another one for doing the action.

So, let's take a look at focus.c:

static struct menu_entry focus_menu[] = {
	// ...
	{
		.display	= focus_show_a,
		.select		= focus_reset_a,
	},
	// ...
	{
		.priv		= "Rack focus",
		.display	= menu_print,
		.select		= focus_toggle,
	},
	// ...
};

Here you define the menu entries. This is the simplest menu entry, which displays "Rack focus" and calls focus_toggle when selected:

 	{
 		.priv		= "Rack focus",
 		.display	= menu_print,
		.select		= focus_toggle,
	},

In this snippet, menu_print is a function from menu.c.

The second entry is more complex, it calls focus_show_a when the menu should be displayed, and calls focus_reset_a when selected:

	{
		.display	= focus_show_a,
		.select		= focus_reset_a,
	},

You should use menu_print function as a template for the .display function.

For binary (ON/OFF) settings, use menu_binary_toggle:

	{
		.priv		= &zebra_draw,
		.select		= menu_binary_toggle,
		.display	= zebra_draw_display,
	},
	{
		.priv		= &intervalometer_running,
		.select		= menu_binary_toggle_and_close,         // this also closes the menu when selecting the option
		.display	= intervalometer_display,               // (available only in my builds until ported)
	},

Registering the menu[]

After filling the structure which describes all the menu entries, you have to call menu_add in an initialization function:

static void
focus_init( void )
{
	// ...
	menu_add( "Focus", focus_menu, COUNT(focus_menu) );
}
INIT_FUNC( __FILE__, focus_init );

As an alternative, you can also call menu_add from the beginning of some task:

static void
shoot_task( void )
{
    menu_add( "Shoot", shoot_menus, COUNT(shoot_menus) );
    while(1)
    {
        // main task loop
    }
}
TASK_CREATE( "shoot_task", shoot_task, 0, 0x18, 0x1000 );

To set the order in which all the menus appear, go to menu.c, function menu_init:

 menu_find_by_name( "Audio" );
 menu_find_by_name( "Video" );
 menu_find_by_name( "Shoot" );
 menu_find_by_name( "Brack" );
 menu_find_by_name( "Focus" );
 ...

Now try to write your own menu, as an exercise.

How to call Canon API functions by name (event procedures)[]

There are a few places in Magic Lantern code which calls functions by name:

$ grep -nr "\<call(" ./
./debug.c:66:	  //call( "mvrSetBitRate", &bitrate );
./debug.c:103:	  call( "dispcheck" );
./debug.c:469:	  call( "MovieStart" );
./bootflags.c:59: call( "DisableBootDisk" );
./bootflags.c:61: call( "EnableBootDisk" );
./zebra.c:724:	  call( "FA_StartLiveView" );
./lens.c:350:	  call( "Release" );
./ptp.c:28:	  //call( "FA_StartLiveView" );
./ptp.c:87:	  call( "MovieStart" );

Is there a list with the functions which can be called by name?

See also: http://chdk.wikia.com/index.php?title=Event_Procedure

How to create a task?[]

Use this macro:

TASK_CREATE(NAME, ENTRY, ARG, PRIORITY, FLAGS)

And some examples from the sources:

$ grep -nr "TASK_CREATE" ./
./bracket.c:132:TASK_CREATE( "bracket_task", bracket_task, 0, 0x10, 0x1000 ); 
./debug.c:437:TASK_CREATE( "dump_task", dump_task, 0, 0x1f, 0x1000 );
./debug.c:476:TASK_CREATE( "movie_start", movie_start, 0, 0x1f, 0x1000 );
./menu.c~:746:TASK_CREATE( "menu_task", menu_task, 0, 0x1e, 0x1000 );
./spotmeter.c:141:TASK_CREATE( __FILE__, spotmeter_task, 0, 0x1f, 0x1000 );
./liveview.c:126:TASK_CREATE( __FILE__, reloc_dlgliveviewapp, 0, 0x1f, 0x1000 );
./timecode.c:349:TASK_CREATE( __FILE__, tc_task, 0, 0x18, 0x1000 );
./zebra.c:748:TASK_CREATE( "zebra_task", zebra_task, 0, 0x18, 0x1000 );
./lens.c:606:TASK_CREATE( "dof_task", lens_task, 0, 0x1f, 0x1000 );
./focus.c:123:TASK_CREATE( "focus_stack_task", focus_stack_task, 0, 0x1f, 0x1000 );
./focus.c:308:TASK_CREATE( "focus_task", focus_task, 0, 0x10, 0x1000 );
./menu.c:749:TASK_CREATE( "menu_task", menu_task, 0, 0x1e, 0x1000 );
./audio.c:309:TASK_CREATE( "meter_task", meter_task, 0, 0x18, 0x1000 );
./audio.c:328:TASK_CREATE( "audio_level_task", compute_audio_level_task, 0, 0x1e, 0x1000 );

So... flags is always 0x1000?


The function which runs in the newly created task takes one argument (void*):

static void lens_task(void * priv)

See also the TASK_OVERRIDE macro:

$ grep -nr "TASK_OVERRIDE" ./
./tasks.h:107:#define TASK_OVERRIDE( orig_func, replace_func ) \
./hotplug.c:68:TASK_OVERRIDE( hotplug_task, my_hotplug_task );
./gui.c:221:TASK_OVERRIDE( gui_main_task, my_gui_main_task );
./audio.c:876:TASK_OVERRIDE( sounddev_task, my_sounddev_task );
./audio.c:977:TASK_OVERRIDE( audio_level_task, my_audio_level_task );

The function prototype is: void replace_func(void).

How to do file I/O[]

See dryos.h for prototypes.

FILE* FIO_Open(const char * name, unsigned flags);
FILE* FIO_CreateFile(const char * name);
FILE* FIO_CloseFile(FILE* file);
int FIO_GetFileSize(const char * filename, unsigned * size); // returns 0 for success
int FIO_WriteFile(FILE* file, const void * buf, size_t len_in_bytes);
int FIO_ReadFile(FILE* file, void * buf, size_t len_in_bytes);
void FIO_RemoveFile(const char * filename);

Very important: allocate buffers with alloc_dma_memory / free_dma_memory in order to avoid getting corrupt data: http://chdk.setepontos.com/index.php?topic=6510.0


To read a file, you can use read_entire_file:

int size;
void* buf = read_entire_file(filename, &size); // buf will be allocated with alloc_dma_memory; size will contain the file size.

// do something with file contents (buf);

free_dma_memory(buf);

To create a new file, first try to remove it (otherwise you will get duplicate files with the same name):

FIO_RemoveFile("B:/myfile.dat");
FILE* f = FIO_CreateFile("B:/myfile.dat");
FIO_WriteFile( f, (const void *) buffer, buffer_size );
FIO_CloseFile(f);

To write to text files, use my_fprintf(FILE* f, const char * fmt, ...).

For fast file I/O, try to read/write the entire file at once.

How to search inside all source files[]

grep -nr "blabla" ./

or, if you are searching in some SVN sources [3]:

grep -Ir --exclude="*\.svn*" "pattern" *

Original instructions, as of June 12 2009[]

Magic Lantern is an open platform for digital 35mm cinema. We've tried to make it easy for new developers to add features to the system and extend the functionality that we've written.

  • To add a file to the build list, just add the name of the .o to the dep list for the magiclantern target.
  • If you want to create a task, add the TASK_CREATE() macro in your code. Look at zebra.c for an example of how to create a new task automatically. There are no dependencies to zebra.o elsewhere in the code, so if it is not linked in there will be no compilation failures.
  • The DryOS RTOS is a pre-emptive scheduled multithreaded operating system. There is no memory protection (beyond the Canon ROM image), so it is possible to scribble over other threads' data, either intentionally or accidentally. Since the Magic Lantern firmware is running out of RAM, it is even possible to write self-modifying code (and both reboot.c and 5d-hack.c code make use of dynamic code generation to take control of the boot process). Typically, however, you do not want to write into your text segment.
  • To sleep when your task is not doing anything, call msleep() with an integral number of microseconds to pause. Depending on the task priority, the sleep might be a bit longer than requested.
  • To be notified via a callback when another task modifies a property, use prop_register_slave(). Look at lens.c to see how to hook the lens change events for examples.
  • To modify a property, use prop_request_change(). Again, look at lens.h for how it adjusts the aperture and shutter speeds.
  • To add a menu, call menu_add(). The exact syntax may change between released until the Magic Lantern API settles down.
  • To draw text to the screen, use bmp_printf(). It is similar to the normal printf, but takes a fontspec which encodes the size, fg color and bg color, and an x/y bitmap vram coordinate. Embedded newlines ('\n') are correctly handled.
  • To draw rectangles to the screen, you can use bmp_fill().
  • Hooking keys is complicated. It requires creating a fake Canon dialog, which isn't fully understood yet.
  • There are not any more complicated graphics drawing primitives yet. We need to either figure out how to use the Canon dialog API or create a full-featured graphics API.
Advertisement