diff --git a/rpcs3/Emu/Cell/Modules/cellCamera.cpp b/rpcs3/Emu/Cell/Modules/cellCamera.cpp index 73cb4e558c..ee0b66fa48 100644 --- a/rpcs3/Emu/Cell/Modules/cellCamera.cpp +++ b/rpcs3/Emu/Cell/Modules/cellCamera.cpp @@ -16,6 +16,7 @@ logs::channel cellCamera("cellCamera"); // ************** s32 cellCameraSetAttribute(s32 dev_num, s32 attrib, u32 arg1, u32 arg2); +s32 cellCameraReadEx(s32 dev_num, vm::ptr read); // ************************ // * HLE helper functions * @@ -116,6 +117,136 @@ static bool check_dev_num(s32 dev_num) return dev_num == 0; } +static s32 check_camera_info(const CellCameraInfoEx& info) +{ + // TODO: I managed to get 0x80990004 once. :thonkang: + + if (info.format == CELL_CAMERA_FORMAT_UNKNOWN) + { + return CELL_CAMERA_ERROR_FORMAT_UNKNOWN; + } + + if (info.resolution == CELL_CAMERA_RESOLUTION_UNKNOWN) + { + return CELL_CAMERA_ERROR_RESOLUTION_UNKNOWN; + } + + if (info.framerate <= 24) + { + return CELL_CAMERA_ERROR_BAD_FRAMERATE; + } + + auto check_fps = [fps = info.framerate](const std::vector& range) + { + return std::find(range.begin(), range.end(), fps) != range.end(); + }; + + switch (g_cfg.io.camera_type) + { + case fake_camera_type::eyetoy: + switch (info.format) + { + case CELL_CAMERA_JPG: + switch (info.resolution) + { + case CELL_CAMERA_VGA: + case CELL_CAMERA_WGA: + if (!check_fps({ 25, 30 })) + return CELL_CAMERA_ERROR_FORMAT_UNKNOWN; + break; + case CELL_CAMERA_QVGA: + if (!check_fps({ 25, 30, 50, 60 })) + return CELL_CAMERA_ERROR_FORMAT_UNKNOWN; + break; + case CELL_CAMERA_SPECIFIED_WIDTH_HEIGHT: + default: + // TODO + return CELL_CAMERA_ERROR_FORMAT_UNKNOWN; + } + break; + case CELL_CAMERA_RAW8: + case CELL_CAMERA_YUV422: + case CELL_CAMERA_RAW10: + case CELL_CAMERA_RGBA: + case CELL_CAMERA_YUV420: + case CELL_CAMERA_V_Y1_U_Y0: + default: + // TODO (also check those other formats) + return CELL_CAMERA_ERROR_FORMAT_UNKNOWN; + } + break; + case fake_camera_type::eyetoy2: + switch (info.format) + { + case CELL_CAMERA_JPG: + case CELL_CAMERA_RAW10: + return CELL_CAMERA_ERROR_FORMAT_UNKNOWN; + case CELL_CAMERA_RAW8: + switch (info.resolution) + { + case CELL_CAMERA_VGA: + case CELL_CAMERA_WGA: + if (!check_fps({ 25, 30, 50, 60 })) + return CELL_CAMERA_ERROR_FORMAT_UNKNOWN; + break; + case CELL_CAMERA_QVGA: + if (!check_fps({ 25, 30, 50, 60, 100, 120 })) + return CELL_CAMERA_ERROR_FORMAT_UNKNOWN; + break; + case CELL_CAMERA_SPECIFIED_WIDTH_HEIGHT: + default: + // TODO + return CELL_CAMERA_ERROR_FORMAT_UNKNOWN; + } + break; + case CELL_CAMERA_YUV422: + case CELL_CAMERA_RGBA: // < TODO check: they all seem to pass the same resolutions as in CELL_CAMERA_YUV422 + case CELL_CAMERA_YUV420: // < + case CELL_CAMERA_V_Y1_U_Y0: // < + default: // < + switch (info.resolution) + { + case CELL_CAMERA_VGA: + case CELL_CAMERA_WGA: + if (!check_fps({ 25, 30 })) + return CELL_CAMERA_ERROR_FORMAT_UNKNOWN; + break; + case CELL_CAMERA_QVGA: + if (!check_fps({ 25, 30, 50, 60 })) + return CELL_CAMERA_ERROR_FORMAT_UNKNOWN; + break; + case CELL_CAMERA_SPECIFIED_WIDTH_HEIGHT: + default: + // TODO + return CELL_CAMERA_ERROR_FORMAT_UNKNOWN; + } + break; + } + break; + case fake_camera_type::uvc1_1: + switch (info.format) + { + case CELL_CAMERA_JPG: + case CELL_CAMERA_YUV422: + break; + case CELL_CAMERA_RAW8: + case CELL_CAMERA_RAW10: + case CELL_CAMERA_RGBA: + case CELL_CAMERA_YUV420: + case CELL_CAMERA_V_Y1_U_Y0: + default: + // TODO + return CELL_CAMERA_ERROR_FORMAT_UNKNOWN; + } + break; + case fake_camera_type::unknown: + default: + // TODO + break; + } + return CELL_OK; +}; + /** * \brief Sets up notify event queue supplied and immediately sends an ATTACH event to it * \param key Event queue key to add @@ -169,33 +300,15 @@ bool remove_queue(u64 key) /** * \brief Sets read mode attribute (used for deciding how image data is passed to games) - * Also sends it to the camera thread - * NOTE: thread-safe (uses camera_thread::mutex) + * Also sends it to the camera thread + * NOTE: thread-safe (uses camera_thread::mutex) * \param dev_num Device number (always 0) - * \param read_mode Either CELL_CAMERA_READ_FUNCCALL or CELL_CAMERA_READ_DIRECT + * \param read_mode is CELL_CAMERA_READ_DIRECT or else CELL_CAMERA_READ_FUNCCALL * \return CELL error code or CELL_OK */ -u32 set_and_send_read_mode(s32 dev_num, const s32 read_mode) +s32 set_and_send_read_mode(s32 dev_num, const s32 read_mode) { - if (read_mode == CELL_CAMERA_READ_FUNCCALL || - read_mode == CELL_CAMERA_READ_DIRECT) - { - if (const auto status = cellCameraSetAttribute(dev_num, CELL_CAMERA_READMODE, read_mode, 0)) - { - return status; - } - } - else - { - cellCamera.error("Unknown read mode set: %d", read_mode); - } - - // Send read mode to camera thread - const auto g_camera = fxm::get(); - - g_camera->read_mode.exchange(read_mode); - - return CELL_OK; + return cellCameraSetAttribute(dev_num, CELL_CAMERA_READMODE, read_mode, 0); } std::pair get_video_resolution(const CellCameraInfoEx& info) @@ -302,6 +415,13 @@ s32 cellCameraEnd() return CELL_OK; } + // TODO: My tests hinted to this behavior, but I'm not sure, so I'll leave this commented + //s32 res = cellCameraClose(0); + //if (res != CELL_OK) + //{ + // return res; + //} + if (!fxm::remove()) { return CELL_CAMERA_ERROR_NOT_INIT; @@ -320,6 +440,13 @@ s32 cellCameraOpenEx(s32 dev_num, vm::ptr info) { cellCamera.todo("cellCameraOpenEx(dev_num=%d, type=*0x%x)", dev_num, info); + // This function has a very weird order of checking for errors + + if (!info) + { + return CELL_CAMERA_ERROR_PARAM; + } + if (g_cfg.io.camera == camera_handler::null) { return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND; @@ -332,20 +459,38 @@ s32 cellCameraOpenEx(s32 dev_num, vm::ptr info) return CELL_CAMERA_ERROR_NOT_INIT; } - s32 read_mode = info->read_mode; - - u32 status = set_and_send_read_mode(dev_num, read_mode); + s32 status = set_and_send_read_mode(dev_num, info->read_mode); if (status != CELL_OK) { return status; } - status = cellCameraSetAttribute(dev_num, CELL_CAMERA_GAMEPID, status, 0); // yup, that's what libGem does + status = cellCameraSetAttribute(dev_num, CELL_CAMERA_GAMEPID, 0, 0); // yup, that's what libGem does if (status != CELL_OK) { return status; } + if (!check_dev_num) + { + return CELL_CAMERA_ERROR_PARAM; + } + + semaphore_lock lock(g_camera->mutex); + + if (g_camera->is_open) + { + return CELL_CAMERA_ERROR_ALREADY_OPEN; + } + + status = check_camera_info(*info); + if (status < 0) + { + return status; + } + + // calls cellCameraGetAttribute(dev_num, CELL_CAMERA_PBUFFER) at some point + const auto vbuf_size = get_video_buffer_size(*info); if (info->read_mode == CELL_CAMERA_READ_FUNCCALL && !info->buffer) @@ -356,8 +501,6 @@ s32 cellCameraOpenEx(s32 dev_num, vm::ptr info) std::tie(info->width, info->height) = get_video_resolution(*info); - semaphore_lock lock(g_camera->mutex); - g_camera->is_open = true; g_camera->info = *info; @@ -368,6 +511,11 @@ s32 cellCameraClose(s32 dev_num) { cellCamera.todo("cellCameraClose(dev_num=%d)", dev_num); + if (!check_dev_num(dev_num)) + { + return CELL_CAMERA_ERROR_PARAM; + } + if (g_cfg.io.camera == camera_handler::null) { return CELL_CAMERA_ERROR_NOT_OPEN; @@ -382,6 +530,11 @@ s32 cellCameraClose(s32 dev_num) semaphore_lock lock(g_camera->mutex); + if (!g_camera->is_open) + { + return CELL_CAMERA_ERROR_NOT_OPEN; + } + vm::dealloc(g_camera->info.buffer.addr(), vm::memory_location_t::main); g_camera->is_open = false; @@ -390,7 +543,19 @@ s32 cellCameraClose(s32 dev_num) s32 cellCameraGetDeviceGUID(s32 dev_num, vm::ptr guid) { - UNIMPLEMENTED_FUNC(cellCamera); + cellCamera.todo("cellCameraGetDeviceGUID(dev_num=%d, guid=*0x%x)", dev_num, guid); + + const auto g_camera = fxm::get(); + + if (!g_camera) + { + return CELL_CAMERA_ERROR_NOT_INIT; + } + + // Does not check params or is_open (maybe attached?) + + *guid = 0; // apparently always 0 + return CELL_OK; } @@ -415,6 +580,11 @@ s32 cellCameraGetType(s32 dev_num, vm::ptr type) return CELL_CAMERA_ERROR_PARAM; } + if (!g_camera->is_attached) + { + return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND; + } + switch (g_cfg.io.camera_type) { case fake_camera_type::unknown: *type = CELL_CAMERA_TYPE_UNKNOWN; break; @@ -456,7 +626,7 @@ s32 cellCameraIsAttached(s32 dev_num) const auto g_camera = fxm::get(); - if (!g_camera) + if (!g_camera || !check_dev_num(dev_num)) { return false; } @@ -487,6 +657,11 @@ s32 cellCameraIsOpen(s32 dev_num) const auto g_camera = fxm::get(); + if (!g_camera || !check_dev_num(dev_num)) + { + return false; + } + semaphore_lock lock(g_camera->mutex); return g_camera->is_open; @@ -503,13 +678,20 @@ s32 cellCameraIsStarted(s32 dev_num) const auto g_camera = fxm::get(); + if (!g_camera || !check_dev_num(dev_num)) + { + return false; + } + + semaphore_lock lock(g_camera->mutex); + return g_camera->is_streaming; } s32 cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr arg1, vm::ptr arg2) { const auto attr_name = get_camera_attr_name(attrib); - cellCamera.todo("cellCameraGetAttribute: get attrib %s to: 0x%x - 0x%x)", attr_name ? attr_name : "(invalid)", arg1, arg2); + cellCamera.todo("cellCameraGetAttribute(dev_num=%d, attrib=%d=%s, arg1=*0x%x, arg2=*0x%x)", dev_num, attrib, attr_name, arg1, arg2); if (g_cfg.io.camera == camera_handler::null) { @@ -523,15 +705,18 @@ s32 cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr arg1, vm::ptrmutex); + if (!g_camera->is_attached) + { + return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND; + } + if (arg1) { *arg1 = g_camera->attr[attrib].v1; @@ -547,7 +732,7 @@ s32 cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr arg1, vm::ptrmutex); + + if (!g_camera->is_open) + { + return CELL_CAMERA_ERROR_NOT_OPEN; + } + + if (attrib == CELL_CAMERA_READMODE) + { + if (arg1 != CELL_CAMERA_READ_FUNCCALL && arg1 != CELL_CAMERA_READ_DIRECT) + { + LOG_WARNING(HLE, "Unknown read mode set: %d", arg1); + arg1 = CELL_CAMERA_READ_FUNCCALL; + } + g_camera->read_mode.exchange(arg1); + } + g_camera->attr[attrib] = { arg1, arg2 }; return CELL_OK; @@ -588,15 +789,35 @@ s32 cellCameraGetBufferSize(s32 dev_num, vm::ptr info) return CELL_CAMERA_ERROR_NOT_INIT; } - // TODO: a bunch of arg checks. here's one + // the next few checks have a strange order, if I can trust the tests + + if (!check_dev_num(dev_num)) + { + return CELL_CAMERA_ERROR_PARAM; + } + + if (g_camera->is_open) + { + return CELL_CAMERA_ERROR_ALREADY_OPEN; + } + + if (!info) + { + return CELL_CAMERA_ERROR_PARAM; + } + + s32 res = check_camera_info(*info); + if (res < 0) + { + return res; + } + if (!cellCameraIsAttached(dev_num)) { return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND; } - const auto read_mode = info->read_mode; - - u32 status = set_and_send_read_mode(dev_num, read_mode); + u32 status = set_and_send_read_mode(dev_num, info->read_mode); if (status != CELL_OK) { return status; @@ -614,6 +835,9 @@ s32 cellCameraGetBufferSize(s32 dev_num, vm::ptr info) s32 cellCameraGetBufferInfo() { UNIMPLEMENTED_FUNC(cellCamera); + + // called by cellCameraGetBufferInfoEx + return CELL_OK; } @@ -621,6 +845,8 @@ s32 cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr info) { cellCamera.todo("cellCameraGetBufferInfoEx(dev_num=%d, read=0x%x)", dev_num, info); + // the following should be moved to cellCameraGetBufferInfo + if (g_cfg.io.camera == camera_handler::null) { return CELL_CAMERA_ERROR_NOT_OPEN; @@ -633,13 +859,23 @@ s32 cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr info) return CELL_CAMERA_ERROR_NOT_INIT; } - if (!info) + if (!check_dev_num(dev_num)) { return CELL_CAMERA_ERROR_PARAM; } semaphore_lock lock(g_camera->mutex); + if (!g_camera->is_open) + { + return CELL_CAMERA_ERROR_NOT_OPEN; + } + + if (!info) + { + return CELL_CAMERA_ERROR_PARAM; + } + *info = g_camera->info; return CELL_OK; @@ -659,32 +895,27 @@ s32 cellCameraCtrlExtensionUnit(s32 dev_num, u8 request, u16 value, u16 length, s32 cellCameraGetExtensionUnit(s32 dev_num, u16 value, u16 length, vm::ptr data) { - UNIMPLEMENTED_FUNC(cellCamera); - return CELL_OK; + cellCamera.todo("cellCameraGetExtensionUnit(dev_num=%d, value=%d, length=%d, data=*0x%x)", dev_num, value, length, data); + + return cellCameraCtrlExtensionUnit(dev_num, GET_CUR, value, length, data); } s32 cellCameraSetExtensionUnit(s32 dev_num, u16 value, u16 length, vm::ptr data) { - UNIMPLEMENTED_FUNC(cellCamera); - return CELL_OK; + cellCamera.todo("cellCameraSetExtensionUnit(dev_num=%d, value=%d, length=%d, data=*0x%x)", dev_num, value, length, data); + + return cellCameraCtrlExtensionUnit(dev_num, SET_CUR, value, length, data); } s32 cellCameraReset(s32 dev_num) { cellCamera.todo("cellCameraReset(dev_num=%d)", dev_num); - if (g_cfg.io.camera == camera_handler::null) + if (!check_dev_num(dev_num)) { - return CELL_CAMERA_ERROR_NOT_OPEN; + return CELL_CAMERA_ERROR_PARAM; } - return CELL_OK; -} - -s32 cellCameraStart(s32 dev_num) -{ - cellCamera.todo("cellCameraStart(dev_num=%d)", dev_num); - if (g_cfg.io.camera == camera_handler::null) { return CELL_CAMERA_ERROR_NOT_OPEN; @@ -699,6 +930,54 @@ s32 cellCameraStart(s32 dev_num) semaphore_lock lock(g_camera->mutex); + if (!g_camera->is_open) + { + return CELL_CAMERA_ERROR_NOT_OPEN; + } + + if (!g_camera->is_attached) + { + return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND; + } + + // TODO reset camera + + return CELL_OK; +} + +s32 cellCameraStart(s32 dev_num) +{ + cellCamera.todo("cellCameraStart(dev_num=%d)", dev_num); + + if (!check_dev_num(dev_num)) + { + return CELL_CAMERA_ERROR_PARAM; + } + + if (g_cfg.io.camera == camera_handler::null) + { + return CELL_CAMERA_ERROR_NOT_OPEN; + } + + const auto g_camera = fxm::get(); + + if (!g_camera) + { + return CELL_CAMERA_ERROR_NOT_INIT; + } + + semaphore_lock lock(g_camera->mutex); + + if (!g_camera->is_open) + { + return CELL_CAMERA_ERROR_NOT_OPEN; + } + + if (!g_camera->is_attached) + { + return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND; + } + g_camera->timer.Start(); g_camera->is_streaming = true; @@ -709,9 +988,23 @@ s32 cellCameraRead(s32 dev_num, vm::ptr frame_num, vm::ptr bytes_read) { cellCamera.todo("cellCameraRead(dev_num=%d, frame_num=*0x%x, bytes_read=*0x%x)", dev_num, frame_num, bytes_read); - if (g_cfg.io.camera == camera_handler::null) + vm::ptr read_ex = vm::make_var({}); + + s32 res = cellCameraReadEx(dev_num, read_ex); + + if (res != CELL_OK) { - return CELL_CAMERA_ERROR_NOT_OPEN; + return res; + } + + if (frame_num) + { + *frame_num = read_ex->frame; + } + + if (bytes_read) + { + *bytes_read = read_ex->bytesread; } return CELL_OK; @@ -733,16 +1026,40 @@ s32 cellCameraReadEx(s32 dev_num, vm::ptr read) return CELL_CAMERA_ERROR_NOT_INIT; } + if (!check_dev_num(dev_num)) + { + return CELL_CAMERA_ERROR_PARAM; + } + semaphore_lock lock(g_camera->mutex); - read->timestamp = g_camera->timer.GetElapsedTimeInMicroSec(); - read->frame = g_camera->frame_num; - read->bytesread = g_camera->is_streaming ? - get_video_buffer_size(g_camera->info) : 0; + if (!g_camera->is_open) + { + return CELL_CAMERA_ERROR_NOT_OPEN; + } - auto shared_data = fxm::get_always(); + if (!g_camera->is_attached) + { + return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND; + } - shared_data->frame_timestamp.exchange(read->timestamp); + if (!g_camera->is_streaming) + { + return CELL_CAMERA_ERROR_NOT_STARTED; + } + + // can call cellCameraReset() and cellCameraStop() in some cases + + if (read) // NULL returns CELL_OK + { + read->timestamp = g_camera->timer.GetElapsedTimeInMicroSec(); + read->frame = g_camera->frame_num; + read->bytesread = g_camera->is_streaming ? get_video_buffer_size(g_camera->info) : 0; + + auto shared_data = fxm::get_always(); + + shared_data->frame_timestamp.exchange(read->timestamp); + } return CELL_OK; } @@ -751,13 +1068,6 @@ s32 cellCameraReadComplete(s32 dev_num, u32 bufnum, u32 arg2) { cellCamera.todo("cellCameraReadComplete(dev_num=%d, bufnum=%d, arg2=%d)", dev_num, bufnum, arg2); - const auto g_camera = fxm::get(); - - if (!g_camera) - { - return CELL_CAMERA_ERROR_NOT_INIT; - } - return cellCameraSetAttribute(dev_num, CELL_CAMERA_READFINISH, bufnum, arg2); } @@ -765,6 +1075,11 @@ s32 cellCameraStop(s32 dev_num) { cellCamera.todo("cellCameraStop(dev_num=%d)", dev_num); + if (!check_dev_num(dev_num)) + { + return CELL_CAMERA_ERROR_PARAM; + } + if (g_cfg.io.camera == camera_handler::null) { return CELL_CAMERA_ERROR_NOT_OPEN; @@ -779,6 +1094,21 @@ s32 cellCameraStop(s32 dev_num) semaphore_lock lock(g_camera->mutex); + if (!g_camera->is_open) + { + return CELL_CAMERA_ERROR_NOT_OPEN; + } + + if (!g_camera->is_attached) + { + return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND; + } + + if (!g_camera->is_streaming) + { + return CELL_CAMERA_ERROR_NOT_STARTED; + } + g_camera->is_streaming = false; g_camera->timer.Stop(); @@ -789,6 +1119,11 @@ s32 cellCameraSetNotifyEventQueue(u64 key) { cellCamera.todo("cellCameraSetNotifyEventQueue(key=0x%x)", key); + if (g_cfg.io.camera == camera_handler::null) + { + return CELL_OK; + } + if (!add_queue_and_send_attach(key, 0, 0)) { return CELL_CAMERA_ERROR_NOT_INIT; @@ -801,6 +1136,11 @@ s32 cellCameraRemoveNotifyEventQueue(u64 key) { cellCamera.todo("cellCameraRemoveNotifyEventQueue(key=0x%x)", key); + if (g_cfg.io.camera == camera_handler::null) + { + return CELL_OK; + } + if (!remove_queue(key)) { return CELL_CAMERA_ERROR_NOT_INIT; @@ -830,17 +1170,7 @@ s32 cellCameraRemoveNotifyEventQueue2(u64 key) { cellCamera.todo("cellCameraRemoveNotifyEventQueue2(key=0x%x)", key); - if (g_cfg.io.camera == camera_handler::null) - { - return CELL_OK; - } - - if (!remove_queue(key)) - { - return CELL_CAMERA_ERROR_NOT_INIT; - } - - return CELL_OK; + return cellCameraRemoveNotifyEventQueue(key); } DECLARE(ppu_module_manager::cellCamera)("cellCamera", []() @@ -910,15 +1240,7 @@ void camera_thread::on_task() u64 data2{ 0 }; u64 data3{ 0 }; - switch (read_mode.load()) - { - case CELL_CAMERA_READ_FUNCCALL: - { - data2 = 0; // device id (always 0) - data3 = 0; // unused - break; - } - case CELL_CAMERA_READ_DIRECT: + if (read_mode.load() == CELL_CAMERA_READ_DIRECT) { const u64 image_data_size = static_cast(info.bytesize); const u64 buffer_number = 0; @@ -926,13 +1248,11 @@ void camera_thread::on_task() data2 = image_data_size << 32 | buffer_number << 16 | camera_id; data3 = timer.GetElapsedTimeInMicroSec(); // timestamp - break; } - default: + else // CELL_CAMERA_READ_FUNCCALL, also default { - cellCamera.error("Unknown read mode set: %d. This should never happen.", read_mode.load()); - return; - } + data2 = 0; // device id (always 0) + data3 = 0; // unused } const auto send_status = queue->send(evt_data.source, CELL_CAMERA_FRAME_UPDATE, data2, data3); diff --git a/rpcs3/Emu/Cell/Modules/cellCamera.h b/rpcs3/Emu/Cell/Modules/cellCamera.h index 4bed0f5efb..a029d25739 100644 --- a/rpcs3/Emu/Cell/Modules/cellCamera.h +++ b/rpcs3/Emu/Cell/Modules/cellCamera.h @@ -310,6 +310,19 @@ enum CellCameraAttribute : s32 CELL_CAMERA_ATTRIBUTE_UNKNOWN = 500, }; +// Request codes +enum +{ + SET_CUR = 0x01, + GET_CUR = 0x81, + GET_MIN = 0x82, + GET_MAX = 0x83, + GET_RES = 0x84, + GET_LEN = 0x85, + GET_INFO = 0x86, + GET_DEF = 0x87, +}; + struct CellCameraInfoEx { be_t format; // CellCameraFormat