In this page I'm trying to understand the Magic Lantern structure and document its API.

Related: Extending Magic Lantern

API docs generated by Doxygen:



It seems that 5d-hack.c also works on the 550D, so renaming it might remove a tiny amount of confusion.

audio.h and audio.cEdit

int16_t audio_read_level(int channel)Edit

Read the raw level from the audio device. Expected values are signed 16-bit?

  • channel: 0 (left) or 1 (right)

How it works: reads the value from 0xC0920000 + 0x110.

uint8_t audio_ic_read(unsigned cmd)Edit

void audio_ic_write(unsigned cmd)Edit

Read and write commands to the AK4646. These two functions are wrappers to DryOS APIs _audio_ic_read and _audio_ic_write.

Datasheet for the AK4646 audio chip is here: Datasheets

According to this post, both 550D and 5D use the AK4646 chip, or a compatible one.

I believe the first two bytes (MSB) of cmd are the address of the audio registers, and the last two bytes are the value written there.

Audio registers:

#define AUDIO_IC_PM1	0x2000
#define AUDIO_IC_PM2	0x2100
#define AUDIO_IC_SIG1	0x2200
#define AUDIO_IC_SIG2	0x2300
#define AUDIO_IC_ALC1	0x2700
#define AUDIO_IC_ALC2	0x2800
#define AUDIO_IC_IVL	0x2900
#define AUDIO_IC_IVR	0x2C00
#define AUDIO_IC_OVL	0x2A00
#define AUDIO_IC_OVR	0x3500
#define AUDIO_IC_ALCVOL	0x2D00
#define AUDIO_IC_MODE3	0x2E00
#define AUDIO_IC_MODE4	0x2F00
#define AUDIO_IC_PM3	0x3000
#define AUDIO_IC_FIL1	0x3100
#define AUDIO_IC_HPF0	0x3C00
#define AUDIO_IC_HPF1	0x3D00
#define AUDIO_IC_HPF2	0x3E00
#define AUDIO_IC_HPF3	0x3F00
#define AUDIO_IC_LPF0	0x6C00
#define AUDIO_IC_LPF1	0x6D00
#define AUDIO_IC_LPF2	0x6E00
#define AUDIO_IC_LPF3	0x6F00

Sample code from audio.c:

./audio.c:515:	audio_ic_write( AUDIO_IC_PM1 | 0x6D ); // power up ADC and DAC
./audio.c:527:	audio_ic_write( AUDIO_IC_PM3 | 0x00 ); // internal mic
./audio.c:529:	audio_ic_write( AUDIO_IC_PM3 | 0x07 ); // external input
./audio.c:532:	audio_ic_write( AUDIO_IC_ALC1 | gain.alc1 ); // disable all ALC

void audio_ic_set_input_volume(int channel, int gain)Edit

  • channel: 0 or 1
  • gain: in DB.
./audio.c:537:	audio_ic_set_input_volume( 0, dgain_r );
./audio.c:538:	audio_ic_set_input_volume( 1, dgain_l );

dgain_r and dgain_l are audio.dgain.r and audio.dgain.l from magic.cfg (0 by default).

This is defined in audio.c. To use it, you have to declare it in audio.h.

void audio_ic_set_mgain(unsigned bits)Edit

/** Write the MGAIN2-0 bits.
 * Table 19 for the gain values:
 *       0 == +0 dB
 *       1 == +20 dB
 *       2 == +26 dB
 *       3 == +32 dB
 *       4 == +10 dB
 *       5 == +17 dB
 *       6 == +23 dB
 *       7 == +29 dB
 * Why is it split between two registers?  I don't know.

mgain is audio.mgain from magic.cfg, and the default value is 4 (i.e. 10 dB).

This is defined in audio.c. To use it, you have to declare it in audio.h.

What is mgain? Some kind of master gain, for both channels?

bmp.h and bmp.cEdit

void bmp_printf(fontspec, x, y, const char * fmt, ...)Edit

This is printf at (x,y) with a given font.

  • fontspec: FONT_HUGE, FONT_LARGE, FONT_MED or FONT_SMALL. You can include color info, too.
  • x and y: coordinates of the text, in pixels, unsigned. (0,0) is on the upper left corner.
  • fmt: the same like printf. Handles newlines.

To print in colors, use the FONT macro, defined in bmp.h:


Color example from AJ:

bmp_printf( FONT(FONT_SMALL,COLOR_RED,COLOR_BG), 000, g_bmp_height-16,
                     "AJ: XYC range. x=%d y=%d c=%d ",x,y,colour);

List of colors (bmp.h):

/** Some selected colors */
#define COLOR_EMPTY		0x00 // total transparent
#define COLOR_BG		0x14 // transparent gray
#define COLOR_BG_DARK		0x03 // transparent black
#define COLOR_WHITE		0x01 // Normal white
#define COLOR_BLUE		0x0B // normal blue
#define COLOR_RED		0x08 // normal red
#define COLOR_YELLOW		0x0F // normal yellow

uint8_t * bmp_vram(void)Edit

Returns Bitmap overlay VRAM address (bvram).

  • Pixel format: 8-bit fixed palette (see VRAM and Cropmarks).
  • Size: bmp_width() x bmp_height() pixels (720x480).
  • Pitch (size of a scanline in memory): pitch = bmp_pitch() => 960
  • Addressing mode (how to get the address of a pixel):
first_row = bvram + y * pitch + x;

void bmp_fill(color, x, y, w, h);Edit

  • color: uint8_t
  • x,y,w,h: uint32_t (these numbers describe a rectangle)
  • Fill a section of bitmap memory with solid color. Only has a four-pixel resolution in X.
  • Is there a color palette? Yes! (see the VRAM and Cropmarks pages)

struct bmp_file_t* bmp_load(const char * name);Edit

Load a BMP file into memory so that it can be drawn on screen. Used for cropmarks.

  • How to draw it on screen? Pixel by pixel, on the BMP overlay. The code is in zebra.c.

property.h and property.cEdit

void prop_request_change(unsigned property, void* addr, size_t len)Edit

Change a property. This is a DryOS call.

  • property: an ID from the List of Properties (they are declared in property.h)
  • addr: pointer to the new property value
  • len: size of the value located at addr
  • What data type do properties have? I believe most of them are integers or strings. The list of properties do not specify the data type.

Example code from lens.h:

static inline void lens_set_aperture(unsigned aperture) 
    prop_request_change( PROP_APERTURE, &aperture, sizeof(aperture) ); 

#define PROP_HANDLER(id)Edit

Use this to get notified when a property changes. This macro allows you to declare a function which is called automatically (something like interrupts).

Example code from lens.c:

        const uint32_t raw = *(uint32_t *) buf;
        lens_info.raw_iso = raw;
        lens_info.iso = raw/2 < COUNT(iso_values)
                ? iso_values[ raw / 2 ]
                : 0;
        return prop_cleanup( token, property );

void prop_get_value(unsigned property, void * addr, size_t * len);Edit

Parameters: same as prop_request_change. This is a DryOS call, only identified in 5D2/1.1.0. Not sure if it works in 550D.

todo: Does initial value of len matter?

lens.c and lens.hEdit

Control ISO, shutter, aperture and stuff like that.


lens_sem = create_named_semaphore( "lens_info", 1 ); 
focus_done_sem = create_named_semaphore( "focus_sem", 1 ); 
job_sem = create_named_semaphore( "job", 1 );


void lens_set_aperture(unsigned aperture)Edit

  • aperture: see lens.h for possible values. There are macros like APERTURE_1_8, APERTURE_4_0 and so on.

void lens_set_iso(unsigned iso)Edit

  • iso: see lens.h for possible values. There are macros from ISO_100 to ISO_12500.

void lens_set_shutter(unsigned shutter)Edit

  • shutter: see lens.h for possible values. There are macros from SHUTTER_30 to SHUTTER_4000.

void lens_set_ae(int cmd)Edit

Exposure compensation?

  • cmd: it seems to be the desired exposure, in EV, or 1/3 EV, or something like this. Not sure.

int lens_get_ae(void)Edit

// This is currently broken. Returns 0.

int lens_take_picture(int wait)Edit

Takes a picture.

  • wait: number of milliseconds to wait (this is a timeout)

Internally, this function calls:

call( "Release" );

and does a bit of black magic with some semaphores.

uint16_t bswap16(uint16_t val)Edit

bswap16(0xABCD) => 0xCDAB

menu.h and menu.cEdit

See How to create a menu

ptp.h and ptp.cEdit

These files contain macros and functions for extending PTP capabilities of the camera.

Until I understand how this works, see the PTP page.

It would be nice to create some Python wrappers for these capabilities. See Remote_control_with_PTP_and_Python. I'll try to create some Python bindings for Mweerden's patched libptp/ptpcam. Also, I'd like to be able to upload files on the camera via PTP and to poke/peek memory locations. CHDK does this afaik.



Define an initialization function, which is executed at firmware startup (I think so).

  • NAME: a string, used in debug messages
  • ENTRY: pointer to a function with this signature: static void functionname(void)

These init functions are called here:

  • [5d-hack.c] my_init_task -> menu_init -> call_init_funcs


Creates a task which runs in parallel with the Canon firmware. Each feature in ML seems to have its own task: focus_task, bracket_task, dump_task, zebra_task, menu_task and so on.

  • NAME: a string, used in debug messages.
  • ENTRY: pointer to a function with this signature: static void functionname(void)
  • ARG: it seems unused, and always 0.
  • PRIORITY: Task priority. Values from ML code: 0x10, 0x18, 0x1e, 0x1f. I don't know whether higher values mean higher priority or viceversa. [AJ: 0x1F = 31 is the lowest prioriity. Example task priorities ]
  • FLAGS: 0x1000 (I don't know what that means)

The tasks are started in 5d-hack.c, in my_init_task, AFTER call_init_funcs, with several calls to task_create, which is a function from the Canon code.

#define TASK_OVERRIDE(orig_func, replace_func)Edit

This REPLACES a Canon task with some custom function from Magic Lantern. I don't understand how it does that, but it works!!!

$ 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 );
Community content is available under CC-BY-SA unless otherwise noted.