summaryrefslogtreecommitdiff
path: root/camlibs/ax203
diff options
context:
space:
mode:
Diffstat (limited to 'camlibs/ax203')
-rw-r--r--camlibs/ax203/ax203.c319
-rw-r--r--camlibs/ax203/ax203.h19
-rw-r--r--camlibs/ax203/library.c1
3 files changed, 320 insertions, 19 deletions
diff --git a/camlibs/ax203/ax203.c b/camlibs/ax203/ax203.c
index 08aaf754a..0d15c3fdd 100644
--- a/camlibs/ax203/ax203.c
+++ b/camlibs/ax203/ax203.c
@@ -34,6 +34,7 @@
#include <gphoto2/gphoto2-result.h>
#include "ax203.h"
+#include "jpeg_memsrcdest.h"
static const struct eeprom_info {
const char *name;
@@ -177,6 +178,44 @@ ax203_get_lcd_size(Camera *camera)
}
#endif
+static int
+ax3003_get_frame_id(Camera *camera)
+{
+ int ret;
+ char cmd_buffer[16];
+ uint8_t id;
+
+ memset (cmd_buffer, 0, sizeof (cmd_buffer));
+
+ cmd_buffer[0] = AX3003_FRAME_CMD;
+ cmd_buffer[1] = AX3003_GET_FRAME_ID;
+
+ ret = ax203_send_cmd (camera, 0, cmd_buffer, sizeof(cmd_buffer),
+ (char *)&id, 1);
+ if (ret < 0) return ret;
+
+ return id;
+}
+
+static int
+ax3003_get_abfs_start(Camera *camera)
+{
+ int ret;
+ char cmd_buffer[16];
+ uint8_t buf[2];
+
+ memset (cmd_buffer, 0, sizeof (cmd_buffer));
+
+ cmd_buffer[0] = AX3003_FRAME_CMD;
+ cmd_buffer[1] = AX3003_GET_ABFS_START;
+
+ ret = ax203_send_cmd (camera, 0, cmd_buffer, sizeof(cmd_buffer),
+ (char *)buf, 2);
+ if (ret < 0) return ret;
+
+ return be16atoh(buf) * 0x100;
+}
+
int
ax203_set_time_and_date(Camera *camera, struct tm *t)
{
@@ -189,6 +228,12 @@ ax203_set_time_and_date(Camera *camera, struct tm *t)
cmd_buffer[5] = t->tm_year % 100;
switch (camera->pl->frame_version) {
+ case AX3003_FIRMWARE_3_5_x:
+ /* Note this is what the windows software does, with the
+ one AX3003 frame I have this does not do anything */
+ cmd_buffer[0] = AX3003_FRAME_CMD;
+ cmd_buffer[1] = AX3003_SET_TIME;
+ /* fall through */
case AX203_FIRMWARE_3_3_x:
case AX203_FIRMWARE_3_4_x:
cmd_buffer[6] = t->tm_mon + 1;
@@ -261,6 +306,17 @@ ax203_eeprom_write_enable(Camera *camera)
}
static int
+ax203_eeprom_clear_block_protection(Camera *camera)
+{
+ char cmd[2];
+
+ cmd[0] = SPI_EEPROM_WRSR;
+ cmd[1] = 0;
+
+ return ax203_send_eeprom_cmd (camera, 1, cmd, sizeof(cmd), NULL, 0);
+}
+
+static int
ax203_eeprom_erase_4k_sector(Camera *camera, int address)
{
char cmd[4];
@@ -290,12 +346,28 @@ static int
ax203_eeprom_wait_ready(Camera *camera)
{
char cmd = SPI_EEPROM_RDSR; /* Read status */
- char buf[64]; /* Do as windows does, read the status word 64 times,
- then check */
+ char buf[64];
+ int count = 0;
+
+ switch (camera->pl->frame_version) {
+ case AX203_FIRMWARE_3_3_x:
+ case AX203_FIRMWARE_3_4_x:
+ case AX206_FIRMWARE_3_5_x:
+ /* Do as windows does, read the status word 64 times,
+ then check */
+ count = 64;
+ break;
+ case AX3003_FIRMWARE_3_5_x:
+ /* On the ax3003 contineously reading the status word
+ does not work. */
+ count = 1;
+ break;
+ }
+
while (1) {
- CHECK (ax203_send_eeprom_cmd (camera, 0, &cmd, 1, buf, 64))
+ CHECK (ax203_send_eeprom_cmd (camera, 0, &cmd, 1, buf, count))
/* We only need to check the last read */
- if (!(buf[63] & 0x01)) /* Check write in progress bit */
+ if (!(buf[count - 1] & 0x01)) /* Check write in progress bit */
break; /* No write in progress, done waiting */
}
return GP_OK;
@@ -473,8 +545,8 @@ ax203_write_mem(Camera *camera, int offset,
static int ax203_read_parameter_block(Camera *camera)
{
uint8_t buf[32], expect[32];
- int i, param_offset, resolution_offset, compression_offset = -1;
- int abfs_start_offset, expect_size;
+ int i, param_offset = 0, resolution_offset = 0;
+ int compression_offset = -1, abfs_start_offset = 0, expect_size = 0;
const int valid_resolutions[][2] = {
{ 120, 160 },
{ 128, 128 },
@@ -522,6 +594,27 @@ static int ax203_read_parameter_block(Camera *camera)
/* ax206 + 3.5.x firmware has a fixed compression type */
camera->pl->compression_version = AX206_COMPRESSION_JPEG;
break;
+ case AX3003_FIRMWARE_3_5_x:
+ i = ax3003_get_frame_id (camera);
+ if (i < 0) return i;
+ switch (i) {
+ case 0:
+ camera->pl->width = 320;
+ camera->pl->height = 240;
+ break;
+ default:
+ gp_log (GP_LOG_ERROR, "ax203",
+ "unknown ax3003 frame id: %d", i);
+ return GP_ERROR_MODEL_NOT_FOUND;
+ }
+
+ i = ax3003_get_abfs_start (camera);
+ if (i < 0) return i;
+ camera->pl->fs_start = i;
+
+ camera->pl->compression_version = AX3003_COMPRESSION_JPEG;
+
+ goto verify_parameters;
}
CHECK (ax203_read_mem (camera, param_offset, buf, sizeof(buf)))
@@ -542,6 +635,9 @@ static int ax203_read_parameter_block(Camera *camera)
htole16a(expect + resolution_offset , camera->pl->width);
htole16a(expect + resolution_offset + 2, camera->pl->height);
break;
+ case AX3003_FIRMWARE_3_5_x:
+ /* Never reached, here to silence a compiler warning */
+ break;
}
if (compression_offset != -1) {
@@ -573,6 +669,7 @@ static int ax203_read_parameter_block(Camera *camera)
return GP_ERROR_MODEL_NOT_FOUND;
}
+verify_parameters:
for (i = 0; valid_resolutions[i][0]; i++)
if (valid_resolutions[i][0] == camera->pl->width &&
valid_resolutions[i][1] == camera->pl->height)
@@ -596,7 +693,8 @@ static int ax203_read_parameter_block(Camera *camera)
camera->pl->compression_version, camera->pl->fs_start);
/* Set JPEG compression parameters based on the found resolution */
- if (camera->pl->width % 16 || camera->pl->height % 16) {
+ if (camera->pl->compression_version == AX206_COMPRESSION_JPEG &&
+ (camera->pl->width % 16 || camera->pl->height % 16)) {
gp_log (GP_LOG_DEBUG, "ax203", "height or width not a "
"multiple of 16, forcing 1x subsampling");
camera->pl->jpeg_uv_subsample = 1;
@@ -637,6 +735,7 @@ ax203_filesize(Camera *camera)
case AX203_COMPRESSION_YUV_DELTA:
return camera->pl->width * camera->pl->height * 3 / 4;
case AX206_COMPRESSION_JPEG:
+ case AX3003_COMPRESSION_JPEG:
/* Variable size */
return 0;
}
@@ -653,7 +752,10 @@ ax203_max_filecount(Camera *camera)
return (AX203_ABFS_SIZE - AX203_ABFS_FILE_OFFSET (0)) / 2;
case AX206_FIRMWARE_3_5_x:
return (AX203_ABFS_SIZE - AX206_ABFS_FILE_OFFSET (0)) /
- sizeof(struct ax203_v3_5_x_raw_fileinfo);
+ sizeof(struct ax206_v3_5_x_raw_fileinfo);
+ case AX3003_FIRMWARE_3_5_x:
+ return (AX203_ABFS_SIZE - AX3003_ABFS_FILE_OFFSET (0)) /
+ sizeof(struct ax3003_v3_5_x_raw_fileinfo);
}
/* Never reached */
return GP_ERROR_NOT_SUPPORTED;
@@ -744,10 +846,10 @@ int ax203_write_v3_3_x_v3_4_x_fileinfo(Camera *camera, int idx,
}
static
-int ax203_read_v3_5_x_fileinfo(Camera *camera, int idx,
+int ax206_read_v3_5_x_fileinfo(Camera *camera, int idx,
struct ax203_fileinfo *fileinfo)
{
- struct ax203_v3_5_x_raw_fileinfo raw;
+ struct ax206_v3_5_x_raw_fileinfo raw;
CHECK (ax203_read_mem (camera,
camera->pl->fs_start +
@@ -762,10 +864,10 @@ int ax203_read_v3_5_x_fileinfo(Camera *camera, int idx,
}
static
-int ax203_write_v3_5_x_fileinfo(Camera *camera, int idx,
+int ax206_write_v3_5_x_fileinfo(Camera *camera, int idx,
struct ax203_fileinfo *fileinfo)
{
- struct ax203_v3_5_x_raw_fileinfo raw;
+ struct ax206_v3_5_x_raw_fileinfo raw;
raw.present = fileinfo->present;
raw.address = htole32(fileinfo->address);
@@ -779,6 +881,56 @@ int ax203_write_v3_5_x_fileinfo(Camera *camera, int idx,
return GP_OK;
}
+static
+int ax3003_read_v3_5_x_fileinfo(Camera *camera, int idx,
+ struct ax203_fileinfo *fileinfo)
+{
+ struct ax3003_v3_5_x_raw_fileinfo raw;
+
+ CHECK (ax203_read_mem (camera,
+ camera->pl->fs_start +
+ AX3003_ABFS_FILE_OFFSET (idx),
+ &raw, sizeof(raw)))
+
+ fileinfo->present = raw.address && raw.size;
+ fileinfo->address = be16toh (raw.address) * 0x100;
+ fileinfo->size = be16toh (raw.size) * 0x100;
+
+ return GP_OK;
+}
+
+static
+int ax3003_write_v3_5_x_fileinfo(Camera *camera, int idx,
+ struct ax203_fileinfo *fileinfo)
+{
+ struct ax3003_v3_5_x_raw_fileinfo raw;
+
+ if (fileinfo->address & 0xff) {
+ gp_log (GP_LOG_ERROR, "ax203", "LSB of address is not 0");
+ return GP_ERROR_BAD_PARAMETERS;
+ }
+
+ if (fileinfo->size & 0xff) {
+ gp_log (GP_LOG_ERROR, "ax203", "LSB of size is not 0");
+ return GP_ERROR_BAD_PARAMETERS;
+ }
+
+ if (fileinfo->present) {
+ raw.address = htobe16(fileinfo->address / 0x100);
+ raw.size = htobe16(fileinfo->size / 0x100);
+ } else {
+ raw.address = 0;
+ raw.size = 0;
+ }
+
+ CHECK (ax203_write_mem (camera,
+ camera->pl->fs_start +
+ AX3003_ABFS_FILE_OFFSET (idx),
+ &raw, sizeof(raw)))
+
+ return GP_OK;
+}
+
/***************** Begin "public" functions *****************/
int
@@ -789,6 +941,7 @@ ax203_read_filecount(Camera *camera)
case AX203_FIRMWARE_3_4_x:
return ax203_read_v3_3_x_v3_4_x_filecount (camera);
case AX206_FIRMWARE_3_5_x:
+ case AX3003_FIRMWARE_3_5_x:
return ax203_read_v3_5_x_filecount (camera);;
}
/* Never reached */
@@ -812,6 +965,7 @@ ax203_update_filecount(Camera *camera)
case AX203_FIRMWARE_3_4_x:
return ax203_write_v3_3_x_v3_4_x_filecount (camera, count);
case AX206_FIRMWARE_3_5_x:
+ case AX3003_FIRMWARE_3_5_x:
return ax203_write_v3_5_x_filecount (camera, count);
}
/* Never reached */
@@ -830,7 +984,9 @@ int ax203_read_fileinfo(Camera *camera, int idx,
return ax203_read_v3_3_x_v3_4_x_fileinfo (camera, idx,
fileinfo);
case AX206_FIRMWARE_3_5_x:
- return ax203_read_v3_5_x_fileinfo (camera, idx, fileinfo);
+ return ax206_read_v3_5_x_fileinfo (camera, idx, fileinfo);
+ case AX3003_FIRMWARE_3_5_x:
+ return ax3003_read_v3_5_x_fileinfo (camera, idx, fileinfo);
}
/* Never reached */
return GP_ERROR_NOT_SUPPORTED;
@@ -846,7 +1002,9 @@ int ax203_write_fileinfo(Camera *camera, int idx,
return ax203_write_v3_3_x_v3_4_x_fileinfo (camera, idx,
fileinfo);
case AX206_FIRMWARE_3_5_x:
- return ax203_write_v3_5_x_fileinfo (camera, idx, fileinfo);
+ return ax206_write_v3_5_x_fileinfo (camera, idx, fileinfo);
+ case AX3003_FIRMWARE_3_5_x:
+ return ax3003_write_v3_5_x_fileinfo (camera, idx, fileinfo);
}
/* Never reached */
return GP_ERROR_NOT_SUPPORTED;
@@ -869,6 +1027,10 @@ ax203_decode_image(Camera *camera, char *src, int src_size, int **dest)
int ret;
unsigned int x, y, width, height;
unsigned char *components[3];
+ struct jpeg_decompress_struct dinfo;
+ struct jpeg_error_mgr jderr;
+ JSAMPLE row[camera->pl->width * 3];
+ JSAMPROW row_pointer[1] = { row };
switch (camera->pl->compression_version) {
case AX203_COMPRESSION_YUV:
@@ -922,6 +1084,36 @@ ax203_decode_image(Camera *camera, char *src, int src_size, int **dest)
}
}
return GP_OK;
+ case AX3003_COMPRESSION_JPEG:
+ dinfo.err = jpeg_std_error (&jderr);
+ jpeg_create_decompress (&dinfo);
+ jpeg_mem_src (&dinfo, (unsigned char *)src, src_size);
+ jpeg_read_header (&dinfo, TRUE);
+ jpeg_start_decompress (&dinfo);
+ if (dinfo.output_width != camera->pl->width ||
+ dinfo.output_height != camera->pl->height ||
+ dinfo.output_components != 3 ||
+ dinfo.out_color_space != JCS_RGB) {
+ gp_log (GP_LOG_ERROR, "ax203",
+ "Wrong JPEG header parameters: %dx%d, "
+ "%d components, colorspace: %d",
+ dinfo.output_width, dinfo.output_height,
+ dinfo.output_components,
+ dinfo.out_color_space);
+ return GP_ERROR_CORRUPTED_DATA;
+ }
+
+ for (y = 0; y < dinfo.output_height; y++) {
+ jpeg_read_scanlines (&dinfo, row_pointer, 1);
+ for (x = 0; x < dinfo.output_width; x++) {
+ dest[y][x] = gdTrueColor (row[x * 3 + 0],
+ row[x * 3 + 1],
+ row[x * 3 + 2]);
+ }
+ }
+ jpeg_finish_decompress (&dinfo);
+ jpeg_destroy_decompress (&dinfo);
+ return GP_OK;
}
#endif
/* Never reached */
@@ -933,10 +1125,16 @@ static int
ax203_encode_image(Camera *camera, int **src, char *dest, int dest_size)
{
#ifdef HAVE_GD
- int size = ax203_filesize (camera);
+ int x, y, size = ax203_filesize (camera);
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jcerr;
+ JOCTET *jpeg_dest = NULL;
+ unsigned long jpeg_size = 0;
+ JSAMPLE row[camera->pl->width * 3];
+ JSAMPROW row_pointer[1] = { row };
if (dest_size < size)
- return GP_ERROR_BAD_PARAMETERS;
+ return GP_ERROR_FIXED_LIMIT_EXCEEDED;
switch (camera->pl->compression_version) {
case AX203_COMPRESSION_YUV:
@@ -952,6 +1150,39 @@ ax203_encode_image(Camera *camera, int **src, char *dest, int dest_size)
(uint8_t *)dest, dest_size,
camera->pl->width,
camera->pl->height);
+ case AX3003_COMPRESSION_JPEG:
+ cinfo.err = jpeg_std_error (&jcerr);
+ jpeg_create_compress (&cinfo);
+ jpeg_mem_dest (&cinfo, &jpeg_dest, &jpeg_size);
+ cinfo.image_width = camera->pl->width;
+ cinfo.image_height = camera->pl->height;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+ jpeg_set_defaults (&cinfo);
+ jpeg_start_compress (&cinfo, TRUE);
+ for (y = 0; y < cinfo.image_height; y++) {
+ for (x = 0; x < cinfo.image_width; x++) {
+ int p = src[y][x];
+ row[x * 3 + 0] = gdTrueColorGetRed(p);
+ row[x * 3 + 1] = gdTrueColorGetGreen(p);
+ row[x * 3 + 2] = gdTrueColorGetBlue(p);
+ }
+ jpeg_write_scanlines (&cinfo, row_pointer, 1);
+ }
+ jpeg_finish_compress (&cinfo);
+ jpeg_destroy_compress (&cinfo);
+
+ if (jpeg_size > dest_size) {
+ free (jpeg_dest);
+ gp_log (GP_LOG_ERROR, "ax203",
+ "JPEG is bigger then buffer");
+ return GP_ERROR_FIXED_LIMIT_EXCEEDED;
+ }
+ memcpy (dest, jpeg_dest, jpeg_size);
+ free (jpeg_dest);
+ /* Round size up to a multiple of 256 because of ax3003
+ abfs size granularity. */
+ return (jpeg_size + 0xff) & ~0xff;
}
/* Never reached */
#endif
@@ -1097,6 +1328,9 @@ ax203_build_used_mem_table(Camera *camera, struct ax203_fileinfo *table)
case AX206_FIRMWARE_3_5_x:
fileinfo.size = camera->pl->fs_start + AX206_PICTURE_OFFSET;
break;
+ case AX3003_FIRMWARE_3_5_x:
+ fileinfo.size = camera->pl->fs_start + AX3003_PICTURE_OFFSET;
+ break;
}
fileinfo.present = 1;
table[found++] = fileinfo;
@@ -1112,7 +1346,17 @@ ax203_build_used_mem_table(Camera *camera, struct ax203_fileinfo *table)
/* Add a last memory used region which starts at the end of memory
the size does not matter as this is the last entry. */
- fileinfo.address = camera->pl->mem_size;
+ switch (camera->pl->frame_version) {
+ case AX203_FIRMWARE_3_3_x:
+ case AX203_FIRMWARE_3_4_x:
+ case AX206_FIRMWARE_3_5_x:
+ fileinfo.address = camera->pl->mem_size;
+ break;
+ case AX3003_FIRMWARE_3_5_x:
+ fileinfo.address = camera->pl->mem_size - AX3003_BL_SIZE;
+ break;
+ }
+
fileinfo.size = 0;
fileinfo.present = 1;
table[found++] = fileinfo;
@@ -1234,6 +1478,9 @@ ax203_delete_all(Camera *camera)
case AX206_FIRMWARE_3_5_x:
file0_offset = AX206_ABFS_FILE_OFFSET (0);
break;
+ case AX3003_FIRMWARE_3_5_x:
+ file0_offset = AX3003_ABFS_FILE_OFFSET (0);
+ break;
}
size = AX203_ABFS_SIZE - file0_offset;
@@ -1290,6 +1537,40 @@ ax203_commit_block_64k(Camera *camera, int bss)
return GP_OK;
}
+static int
+ax203_commit_block_ax3003(Camera *camera, int bss)
+{
+ int block_sector_size = SPI_EEPROM_BLOCK_SIZE / SPI_EEPROM_SECTOR_SIZE;
+ int i, address = bss * SPI_EEPROM_SECTOR_SIZE;
+
+ /* Make sure we have read the entire block before erasing it !! */
+ for (i = 0; i < block_sector_size; i++)
+ CHECK (ax203_check_sector_present (camera, bss + i))
+
+ if (!camera->pl->block_protection_removed) {
+ CHECK (ax203_eeprom_write_enable (camera))
+ CHECK (ax203_eeprom_clear_block_protection (camera))
+ CHECK (ax203_eeprom_wait_ready (camera))
+ camera->pl->block_protection_removed = 1;
+ }
+
+ /* Erase the block */
+ CHECK (ax203_erase64k_sector (camera, bss))
+
+ /* And program the block in one large 64k page write, the ax3003
+ probably emulates this in firmware, to avoid usb bus overhead. */
+ CHECK (ax203_eeprom_write_enable (camera))
+ CHECK (ax203_eeprom_program_page (camera, address,
+ camera->pl->mem + address,
+ SPI_EEPROM_BLOCK_SIZE))
+ CHECK (ax203_eeprom_wait_ready (camera))
+
+ for (i = 0; i < block_sector_size; i++)
+ camera->pl->sector_dirty[bss + i] = 0;
+
+ return GP_OK;
+}
+
int
ax203_commit(Camera *camera)
{
@@ -1312,10 +1593,12 @@ ax203_commit(Camera *camera)
if (!dirty_sectors)
continue;
+ if (camera->pl->frame_version == AX3003_FIRMWARE_3_5_x)
+ CHECK (ax203_commit_block_ax3003 (camera, i))
/* There are 16 4k sectors per 64k block, when we need to
program 12 or more sectors, programming the entire block
becomes faster */
- if (dirty_sectors < 12 && camera->pl->has_4k_sectors)
+ else if (dirty_sectors < 12 && camera->pl->has_4k_sectors)
CHECK (ax203_commit_block_4k (camera, i))
else
CHECK (ax203_commit_block_64k (camera, i))
diff --git a/camlibs/ax203/ax203.h b/camlibs/ax203/ax203.h
index f52bdb1f7..282ab2dbd 100644
--- a/camlibs/ax203/ax203.h
+++ b/camlibs/ax203/ax203.h
@@ -38,6 +38,11 @@
#define AX206_ABFS_FILE_OFFSET(idx) (0x10 + 8 * (idx))
#define AX206_PICTURE_OFFSET 0x1000 /* offset from ABFS start */
+#define AX3003_ABFS_FILE_OFFSET(idx) (0x20 + 4 * (idx))
+#define AX3003_PICTURE_OFFSET 0x1000 /* offset from ABFS start */
+#define AX3003_BL_SIZE 0x10000 /* Space used by the bootloader
+ at the end of the memory */
+
#define AX203_SET_TIME 0xCA
#define AX203_TO_DEV 0xCB
#define AX203_FROM_DEV 0xCD
@@ -45,10 +50,16 @@
#define AX203_GET_VERSION 0x01
#define AX203_GET_LCD_SIZE 0x02
+#define AX3003_FRAME_CMD 0xCA
+#define AX3003_SET_TIME 0x01
+#define AX3003_GET_FRAME_ID 0x02
+#define AX3003_GET_ABFS_START 0x03
+
/* Note not all SPI EEPROM's actually have 4k sectors, some have
64k sectors, ax203_commit() takes care if this. */
#define SPI_EEPROM_SECTOR_SIZE 4096
#define SPI_EEPROM_BLOCK_SIZE 65536
+#define SPI_EEPROM_WRSR 0x01 /* WRite Status Register */
#define SPI_EEPROM_PP 0x02
#define SPI_EEPROM_READ 0x03
#define SPI_EEPROM_RDSR 0x05 /* ReaD Status Register */
@@ -90,6 +101,7 @@ struct _CameraPrivateLibrary {
/* EEPROM attributes */
int mem_size;
int has_4k_sectors;
+ int block_protection_removed;
/* Compression configuration settings */
int jpeg_uv_subsample;
/* Driver configuration settings */
@@ -108,13 +120,18 @@ struct ax203_fileinfo {
int size;
};
-struct ax203_v3_5_x_raw_fileinfo {
+struct ax206_v3_5_x_raw_fileinfo {
uint8_t present;
uint32_t address;
uint16_t size;
uint8_t pad;
} __attribute__((packed));
+struct ax3003_v3_5_x_raw_fileinfo {
+ uint16_t address; /* In blocks of 256 bytes */
+ uint16_t size; /* In blocks of 256 bytes */
+} __attribute__((packed));
+
/* functions in ax203.c */
int
ax203_open_device(Camera *camera);
diff --git a/camlibs/ax203/library.c b/camlibs/ax203/library.c
index 7140bc0e8..c5ad4b21b 100644
--- a/camlibs/ax203/library.c
+++ b/camlibs/ax203/library.c
@@ -48,6 +48,7 @@ static const struct ax203_devinfo ax203_devinfo[] = {
{ 0x1908, 0x1315, AX203_FIRMWARE_3_3_x },
{ 0x1908, 0x1320, AX203_FIRMWARE_3_4_x },
{ 0x1908, 0x0102, AX206_FIRMWARE_3_5_x },
+ { 0x1908, 0x3335, AX3003_FIRMWARE_3_5_x },
{}
};