#include "scanner_handler.h" #include "./usb/async_usb_host.h" #include #include ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cmd_result MUTEX cmd_result::lock_; uint32_t cmd_result::pack_id_ = 1; cmd_result::cmd_result(std::function call, std::function clean, std::function roger, void* param, uint32_t id) : wait_(new linux_event("wait_reply")), id_(id == 0 ? cmd_result::gen_pack_id() : id) , call_(call), clean_(clean), roger_(roger), param_(param), err_(0), to_(1000), over_(false) { } cmd_result::~cmd_result() { wait_->release(); } uint32_t cmd_result::gen_pack_id(void) { LOCKER lock(cmd_result::lock_); return cmd_result::pack_id_++; } uint32_t cmd_result::get_id(void) { return id_; } uint32_t cmd_result::set_timeout(uint32_t timeout) { uint32_t prev = to_; to_ = timeout; return prev; } void* cmd_result::get_param(void) { return param_; } void* cmd_result::set_param(void* param) { void* r = param_; param_ = param; return r; } bool cmd_result::wait(void) { return wait_->wait(to_); } void cmd_result::trigger(void) { over_ = true; wait_->trigger(); } bool cmd_result::is_over(void) { return over_; } int cmd_result::get_error_code(void) { return err_; } void cmd_result::set_error_code(int err) { err_ = err; } int cmd_result::call(void) { int ret = call_(this); if (ret == 0) { if (wait()) { ret = err_; } else { ret = ETIMEDOUT; } } return ret; } dyn_mem_ptr cmd_result::roger(dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* more) { LOCKER lock(locker_); dyn_mem_ptr ret = nullptr; LPPACK_BASE pack = (LPPACK_BASE)data->ptr(); err_ = pack->data; ret = roger_(data, used, more, this); // trigger(); return ret; } int cmd_result::clean(void) { LOCKER lock(locker_); over_ = true; return clean_(this); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // though all command transferred by BULK are asychronous in transffer level, // make BLOCKED in this layer #define WAIT_COMMAND(call, clean, roger, param) \ cmd_result * r = gen_reply(call, clean, roger, param); \ int ret = r->call(); \ r->clean(); \ r->release(); \ return ret; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // scanner_handler scanner_handler::scanner_handler(void) : usb_(nullptr), status_(SCANNER_STATUS_NOT_OPEN) , img_receiver_(std::function()) , status_notify_(std::function()) { auto on_reply = [&](dyn_mem_ptr reply, uint32_t* used, packet_data_base_ptr* more) ->dyn_mem_ptr { LPPACK_BASE pack = (LPPACK_BASE)reply->ptr(); cmd_result* cmd = take_reply(pack->pack_id); dyn_mem_ptr ret = nullptr; *used = reply->get_rest(); *more = nullptr; if (cmd) { ret = cmd->roger(reply, used, more); if (cmd->is_over()) cmd->release(); else if (status_ != SCANNER_STATUS_RESET_BULK) { LOCKER lock(lock_reply_); reply_.push_back(cmd); } } else if (status_ == SCANNER_STATUS_RESET_BULK) { if (pack->size == sizeof(PACK_BASE) && pack->cmd == PACK_CMD_SYNC_ROGER && pack->payload_len == 0) status_ = SCANNER_STATUS_READY; } else if (pack->size == sizeof(PACK_BASE)) { if (pack->cmd == PACK_CMD_SCAN_IMG_ROGER) { uint32_t off = sizeof(PACK_BASE) + sizeof(PACKIMAGE); if (*used < off) { *used = 0; } else { LPPACKIMAGE pimg = (LPPACKIMAGE)pack->payload; data_holder_ptr receiver = nullptr; if(img_receiver_) receiver = img_receiver_(pimg, pimg->info_size + pimg->data_size); if (!receiver) { // usb_->cancel_command(pack->cmd, pack->pack_id); receiver = dynamic_cast(new empty_holer(pimg->info_size + pimg->data_size)); } *used = off; *more = dynamic_cast(receiver); } } else if (pack->cmd == PACK_CMD_SCAN_FINISHED_ROGER) { if (img_receiver_) { data_holder_ptr receiver = img_receiver_(NULL, pack->data); if (receiver) receiver->release(); } *used = sizeof(PACK_BASE); status_ = pack->data; } else if (pack->cmd == PACK_CMD_STATUS_ROGER) { *used = sizeof(PACK_BASE); status_ = pack->data; if (status_notify_) status_notify_(status_); } else if (pack->cmd == PACK_CMD_FILE_WRITE_REQ_ROGER) { *used = sizeof(PACK_BASE); log_cls::log(LOG_LEVEL_DEBUG, "Send file finished with error: %d\r\n", pack->data); } else { // unknown packet ... uint8_t* ptr = reply->ptr(); log_cls::log(LOG_LEVEL_DEBUG, "Unhandled packet: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X ...\r\n" , ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7] , ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]); } } else { // unknown packet ... uint8_t* ptr = reply->ptr(); log_cls::log(LOG_LEVEL_DEBUG, "Unknown packet received: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X ...\r\n" , ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7] , ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15]); } return ret; }; usb_ = new async_usb_host(on_reply); } scanner_handler::~scanner_handler() { for (auto& v : reply_) v->release(); usb_->release(); } bool scanner_handler::reorder_device_config_json(std::string& cont, const std::map* groups) { typedef struct _grp_item { std::string name; std::vector items; bool operator==(const char* gn) { return name == gn; } }GRPITEM; std::vector items; std::vector nogrp; json* jsn = new json(), * child = nullptr; bool ret = jsn->attach_text(&cont[0]); int cnt = 0; if (ret) { std::string grp(""); child = jsn->first_child(); while (child) { cnt++; if (child->get_value("group", grp) && !grp.empty()) { std::vector::iterator it = std::find(items.begin(), items.end(), grp.c_str()); if (it == items.end()) { GRPITEM item; item.name = grp; item.items.push_back(child); items.push_back(item); } else { it->items.push_back(child); } } else { nogrp.push_back(child); } child = jsn->next_child(); } jsn->release(); child = new json(); child->set_value("option_count", cnt); jsn = new json(); jsn->set_value("global", child); child->release(); for (auto& v : nogrp) { jsn->set_value(v->key().c_str(), v); v->release(); } nogrp.clear(); cnt = 1; if (groups) { for (auto& v : *groups) { std::vector::iterator it = std::find(items.begin(), items.end(), v.first.c_str()); if (it == items.end()) continue; child = new json(); child->set_value("category", "base"); child->set_value("type", "group"); child->set_value("title", v.second.c_str()); jsn->set_value(("grp-" + std::to_string(cnt++)).c_str(), child); child->release(); for (auto& c : it->items) { jsn->set_value(c->key().c_str(), c); c->release(); } items.erase(it); } } for (auto& v : items) { child = new json(); child->set_value("category", "base"); child->set_value("type", "group"); child->set_value("title", v.name.c_str()); jsn->set_value(("grp-" + std::to_string(cnt++)).c_str(), child); child->release(); for (auto& c : v.items) { jsn->set_value(c->key().c_str(), c); c->release(); } } items.clear(); cont = std::move(jsn->to_string()); jsn->release(); } return ret; } cmd_result* scanner_handler::gen_reply(std::function call, std::function clean, std::function roger, void* param, uint32_t id) { cmd_result* reply = new cmd_result(call, clean, roger, param, id); { LOCKER lock(lock_reply_); reply_.push_back(reply); } reply->add_ref(); // for reply_ queue storing return reply; } cmd_result* scanner_handler::take_reply(uint32_t id) { cmd_result* reply = nullptr; LOCKER lock(lock_reply_); for (size_t i = 0; i < reply_.size(); ++i) { if (reply_[i]->get_id() == id) { reply = reply_[i]; reply_.erase(reply_.begin() + i); break; } } return reply; } int scanner_handler::wait_result(cmd_result* reply) { if (reply->wait()) { return reply->get_error_code(); } else { cmd_result* q = take_reply(reply->get_id()); if (q) { q->release(); } else { // take out by reply just now ? try again ... reply->set_timeout(500); if (reply->wait()) return reply->get_error_code(); } return ETIMEDOUT; } } int scanner_handler::get_protocol_version(uint8_t* h, uint8_t* l) { if (!is_scanner_available()) return ENODEV; return usb_->get_peer_protocol_version(h, l); } int scanner_handler::get_scanner_status(LPEP0REPLYSTATUS status) { if (!is_scanner_available()) return ENODEV; return usb_->get_peer_status(status); } int scanner_handler::restart_peer_bulk(uint32_t timeout) { if (!is_scanner_available()) return ENODEV; return usb_->restart_peer_bulk(timeout); } int scanner_handler::option_get_all(std::string& json_opts) { auto call = [&](cmd_result* cmd) -> int { return usb_->get_settings(cmd->get_id()); }; auto clean = [&](cmd_result* cmd) -> int { return 0; }; auto roger = [&](dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* more, cmd_result* cmd) ->dyn_mem_ptr { std::string *ret = (std::string*)cmd->get_param(); LPPACK_BASE pack = (LPPACK_BASE)data->ptr(); if (data->get_rest() < sizeof(PACK_BASE) + pack->payload_len) *used = 0; else { if(!cmd->is_over()) *(std::string*)cmd->get_param() = std::string(pack->payload, pack->payload_len); *used = sizeof(*pack) + pack->payload_len; cmd->trigger(); } *more = nullptr; return nullptr; }; if (!is_scanner_available()) return ENODEV; WAIT_COMMAND(call, clean, roger, &json_opts); } int scanner_handler::option_value_get(const char* name, void* buf, uint32_t size) { auto call = [&](cmd_result* cmd) -> int { return usb_->get_setting_val(cmd->get_id(), name); }; auto clean = [&](cmd_result* cmd) -> int { return 0; }; auto roger = [&](dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* more, cmd_result* cmd) ->dyn_mem_ptr { LPPACK_BASE pack = (LPPACK_BASE)data->ptr(); LPCFGVAL cfg = (LPCFGVAL)pack->payload; if (data->get_rest() < sizeof(PACK_BASE) + pack->payload_len) *used = 0; else { if (!cmd->is_over()) memcpy(cmd->get_param(), cfg->data + cfg->val_off, cfg->val_size); *used = sizeof(*pack) + pack->payload_len; cmd->trigger(); } *more = nullptr; return nullptr; }; if (!is_scanner_available()) return ENODEV; WAIT_COMMAND(call, clean, roger, buf); } int scanner_handler::option_value_set(const char* name, uint32_t type, void* buf, uint32_t size, uint32_t val_size, uint8_t* after) { struct { void* buf; uint8_t* after; }param; auto call = [&](cmd_result* cmd) -> int { memcpy(¶m, cmd->get_param(), sizeof(param)); return usb_->set_setting(cmd->get_id(), name, type, param.buf, val_size, size); }; auto clean = [&](cmd_result* cmd) -> int { return 0; }; auto roger = [&](dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* more, cmd_result* cmd) ->dyn_mem_ptr { struct { void* buf; uint8_t* after; }*result = nullptr; LPPACK_BASE pack = (LPPACK_BASE)data->ptr(); LPCFGVAL cfg = (LPCFGVAL)pack->payload; if (data->get_rest() < sizeof(PACK_BASE) + pack->payload_len) *used = 0; else { if (!cmd->is_over()) { *(void**)&result = cmd->get_param(); memcpy(result->buf, cfg->data + cfg->val_off, cfg->val_size); *result->after = cfg->after_do; } *used = sizeof(*pack) + pack->payload_len; cmd->trigger(); } *more = nullptr; return nullptr; }; if (!is_scanner_available()) return ENODEV; param.buf = buf; param.after = after; WAIT_COMMAND(call, clean, roger, ¶m); } int scanner_handler::status_get(void) { return status_; } void scanner_handler::set_image_receiver(std::function img) { img_receiver_ = img; } void scanner_handler::set_status_notifyer(std::function stntf) { status_notify_ = stntf; } int scanner_handler::scan_start(void) { auto call = [&](cmd_result* cmd) -> int { cmd->set_timeout(4000); return usb_->scan_start(cmd->get_id()); }; auto clean = [&](cmd_result* cmd) -> int { return 0; }; auto roger = [&](dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* more, cmd_result* cmd) ->dyn_mem_ptr { LPPACK_BASE pack = (LPPACK_BASE)data->ptr(); *used = sizeof(PACK_BASE); *more = nullptr; status_ = SCANNER_STATUS_START_SCANNING; cmd->trigger(); return nullptr; }; if (!is_scanner_available()) return ENODEV; WAIT_COMMAND(call, clean, roger, nullptr); } int scanner_handler::scan_stop(void) { auto call = [&](cmd_result* cmd) -> int { return usb_->scan_stop(cmd->get_id()); }; auto clean = [&](cmd_result* cmd) -> int { return 0; }; auto roger = [&](dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* more, cmd_result* cmd) ->dyn_mem_ptr { *used = sizeof(PACK_BASE); *more = nullptr; cmd->trigger(); return nullptr; }; if (!is_scanner_available()) return ENODEV; WAIT_COMMAND(call, clean, roger, nullptr); } int scanner_handler::file_transfer(const char* local_path, const char* remote_path, bool to_device, PROGRESS_NOTIFYER progress, uint64_t local_off, uint64_t remote_off) { if (!is_scanner_available()) return ENODEV; if (to_device) { uint64_t size = 0; FILE *src = fopen(local_path, "rb"); if (!src) return errno; FSEEK(src, 0, SEEK_END); size = FTELL(src); FSEEK(src, local_off, SEEK_SET); if (size <= local_off) { fclose(src); return EOVERFLOW; } size -= local_off; log_cls::log(LOG_LEVEL_DEBUG, "Send '%s' to '%s' ...\r\n", local_path, remote_path); auto call = [&](cmd_result* cmd) -> int { return usb_->file_send(cmd->get_id(), remote_path, size, remote_off); }; auto clean = [&](cmd_result* cmd) -> int { FILE* src = (FILE*)cmd->get_param(); if (src) fclose(src); return 0; }; auto roger = [&](dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* more, cmd_result* cmd) ->dyn_mem_ptr { LPPACK_BASE pack = (LPPACK_BASE)data->ptr(); dyn_mem_ptr ret = nullptr; *used = sizeof(PACK_BASE); *more = nullptr; log_cls::log(LOG_LEVEL_DEBUG, "Send file - Roger of send file result: %d\r\n", pack->data); if (pack->data == 0) { if (cmd->is_over()) { reset_message_que(); } else { file_reader_ptr freader = new file_reader(); int err = freader->attach((FILE*)cmd->set_param(nullptr)); freader->set_progress_notify(progress); if (err) { // cancel tx-file ... cmd->set_error_code(err); freader->release(); reset_message_que(); log_cls::log(LOG_LEVEL_DEBUG, "Send file - Attach to source file failed: %d\r\n", err); } else { *more = dynamic_cast(freader); status_ = SCANNER_STATUS_BUSY; log_cls::log(LOG_LEVEL_DEBUG, "Send file - beginning ...\r\n"); } } } cmd->trigger(); return ret; }; WAIT_COMMAND(call, clean, roger, src); } else { auto call = [&](cmd_result* cmd) -> int { return usb_->file_get(cmd->get_id(), remote_path, remote_off); }; auto clean = [&](cmd_result* cmd) -> int { return 0; }; auto roger = [&](dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* more, cmd_result* cmd) ->dyn_mem_ptr { LPPACK_BASE pack = (LPPACK_BASE)data->ptr(); LPTXFILE pfi = (LPTXFILE)pack->payload; *used = sizeof(PACK_BASE) + pack->payload_len; *more = nullptr; log_cls::log(LOG_LEVEL_DEBUG, "Receive file - Roger result: %d\r\n", pack->data); if (pack->data == 0) { if (cmd->is_over()) { log_cls::log(LOG_LEVEL_DEBUG, "Receive file - timeout, user cancelled\r\n"); reset_message_que(); } else { file_saver* fsaver = new file_saver(); int err = fsaver->open(local_path, pfi->size); fsaver->set_progress_notify(progress); if (err) { // cancel tx-file ... cmd->set_error_code(err); fsaver->release(); reset_message_que(); log_cls::log(LOG_LEVEL_DEBUG, "Receive file - open local file failed: %d\r\n", err); } else { *more = dynamic_cast(fsaver); status_ = SCANNER_STATUS_BUSY; log_cls::log(LOG_LEVEL_DEBUG, "Receive file - beginning ...\r\n"); } } } cmd->trigger(); return nullptr; }; log_cls::log(LOG_LEVEL_DEBUG, "Receive '%s' to '%s' ...\r\n", remote_path, local_path); WAIT_COMMAND(call, clean, roger, (void*)local_path); } } int scanner_handler::file_move(const char* rfrom, const char* rto) { auto call = [&](cmd_result* cmd) -> int { return usb_->file_move(cmd->get_id(), rfrom, rto); }; auto clean = [&](cmd_result* cmd) -> int { return 0; }; auto roger = [&](dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* more, cmd_result* cmd) ->dyn_mem_ptr { *used = sizeof(PACK_BASE); *more = nullptr; cmd->trigger(); return nullptr; }; if (!is_scanner_available()) return ENODEV; WAIT_COMMAND(call, clean, roger, nullptr); } int scanner_handler::file_delete(const char* remote) { auto call = [&](cmd_result* cmd) -> int { return usb_->file_remove(cmd->get_id(), remote); }; auto clean = [&](cmd_result* cmd) -> int { return 0; }; auto roger = [&](dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* more, cmd_result* cmd) ->dyn_mem_ptr { *used = sizeof(PACK_BASE); *more = nullptr; cmd->trigger(); return nullptr; }; if (!is_scanner_available()) return ENODEV; WAIT_COMMAND(call, clean, roger, nullptr); } int scanner_handler::program_start(const char* remote_pe, const char* param, uint64_t* pid) { auto call = [&](cmd_result* cmd) -> int { return usb_->program_start(cmd->get_id(), remote_pe, param); }; auto clean = [&](cmd_result* cmd) -> int { return 0; }; auto roger = [&](dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* more, cmd_result* cmd) ->dyn_mem_ptr { LPPACK_BASE pack = (LPPACK_BASE)data->ptr(); if (pack->data == 0 && !cmd->is_over()) *(uint64_t*)cmd->get_param() = *(uint64_t*)pack->payload; *used = sizeof(PACK_BASE) + pack->payload_len; *more = nullptr; cmd->trigger(); return nullptr; }; if (!is_scanner_available()) return ENODEV; WAIT_COMMAND(call, clean, roger, pid); } int scanner_handler::program_stop(uint64_t pid) { auto call = [&](cmd_result* cmd) -> int { return usb_->program_stop(cmd->get_id(), pid); }; auto clean = [&](cmd_result* cmd) -> int { return 0; }; auto roger = [&](dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* more, cmd_result* cmd) ->dyn_mem_ptr { *used = sizeof(PACK_BASE); *more = nullptr; cmd->trigger(); return nullptr; }; if (!is_scanner_available()) return ENODEV; WAIT_COMMAND(call, clean, roger, nullptr); } int scanner_handler::open_usb_scanner(libusb_device* dev) { int ret = close(); if (ret == 0) { ret = usb_->start(dev); if(ret == 0) status_ = SCANNER_STATUS_READY; //usb_->set_gadget_encrypting_method(ENCRYPT_CMD_XOR_PID); } return ret; } int scanner_handler::close(void) { int ret = usb_->stop(); { LOCKER lock(lock_reply_); for (auto& v : reply_) v->release(); reply_.clear(); } if(ret == 0) status_ = SCANNER_STATUS_NOT_OPEN; return ret; } int scanner_handler::reset_message_que(void) { int err = usb_->cancel_write(); status_ = SCANNER_STATUS_RESET_BULK; log_cls::log(LOG_LEVEL_DEBUG, "reset_message_que - send reset command ...\r\n"); err = usb_->restart_peer_bulk(); log_cls::log(LOG_LEVEL_DEBUG, "reset_message_que - send reset command = %d\r\n", err); if (err == 0) { EP0REPLYSTATUS s; dyn_mem_ptr raw(dyn_mem::memory(sizeof(PACK_BASE))); LPPACK_BASE pack = (LPPACK_BASE)raw->ptr(); BASE_PACKET_REPLY(*pack, PACK_CMD_SYNC, 0, 0); raw->set_len(sizeof(PACK_BASE)); log_cls::log(LOG_LEVEL_DEBUG, "reset_message_que - wait 100ms ...\r\n"); std::this_thread::sleep_for(std::chrono::milliseconds(100)); log_cls::log(LOG_LEVEL_DEBUG, "reset_message_que - send SYNC packet ...\r\n"); usb_->send_bulk_raw_data(raw); raw->release(); std::this_thread::sleep_for(std::chrono::milliseconds(50)); if (status_ == SCANNER_STATUS_RESET_BULK) err = EFAULT; log_cls::log(LOG_LEVEL_DEBUG, "reset_message_que - final status = %d\r\n", status_); } return err; } bool scanner_handler::is_scanner_available(void) { return status_ != SCANNER_STATUS_NOT_OPEN && status_ != SCANNER_STATUS_LOST_CONNECT && status_ != SCANNER_STATUS_RESET_BULK; }