#include "async_scanner.h" #include #include #include #include #include #include #include #include #include #include #include #include "scanner_const_opts.h" #include #include #include #include #include "res_mgr.h" //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // #include static void *(*old_malloc_hook)(size_t, const void *) = nullptr; static void (*old_free_hook)(void *ptr, const void *caller) = nullptr; static FILE* g_mem_file = nullptr; static uint64_t total_size = 0; static bool record_ = false; static void * my_malloc_hook(size_t size, const void *caller) { void *result; /* Restore all old hooks */ __malloc_hook = old_malloc_hook; if(__malloc_hook == my_malloc_hook) __malloc_hook = nullptr; /* Call recursively */ result = malloc(size); /* Save underlying hooks */ old_malloc_hook = __malloc_hook; /* printf() might call malloc(), so protect it too. */ if(g_mem_file) { char buf[128] = {0}; sprintf(buf, "+%p from %p (size = %u)\n", result, caller, size); fwrite(buf, 1, strlen(buf), g_mem_file); } else printf("+%p from %p (size = %u)\n", result, caller, size); /* Restore our own hooks */ if(record_) __malloc_hook = my_malloc_hook; return result; } static void my_free_hook(void *ptr, const void *caller) { __free_hook = old_free_hook; if(__free_hook == my_free_hook) __free_hook = nullptr; if(ptr) free(ptr); if(g_mem_file) { char buf[128] = {0}; sprintf(buf, "-%p from %p\n", ptr, caller); fwrite(buf, 1, strlen(buf), g_mem_file); } else printf("-%p from %p\n", ptr, caller); old_free_hook = __free_hook; if(record_) __free_hook = my_free_hook; } void record_malloc(bool enable) { if(enable) { printf("my_malloc_hook = %p, my_free_hook = %p\n", (void*)my_malloc_hook, (void*)my_free_hook); if(g_mem_file) fclose(g_mem_file); g_mem_file = fopen("/tmp/memlog.txt", "wb"); std::string log(utils::format_current_time() + "\n"); fwrite(log.c_str(), 1, log.length(), g_mem_file); record_ = true; old_free_hook = __free_hook; __free_hook = my_free_hook; old_malloc_hook = __malloc_hook; __malloc_hook = my_malloc_hook; } else // if(old_malloc_hook) { printf("cancel log malloc.\n"); record_ = false; // __malloc_hook = old_malloc_hook; // old_malloc_hook = nullptr; // __free_hook = old_free_hook; // old_free_hook = nullptr; if(g_mem_file) { // std::string log(utils::format_current_time() + "\n"); // fwrite(log.c_str(), 1, log.length(), g_mem_file); fclose(g_mem_file); } g_mem_file = nullptr; } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // async_scanner::async_scanner() : usb_(nullptr), cfg_mgr_(nullptr), scan_id_(0) { utils::init_log(LOG_TYPE_FILE); utils::to_log(LOG_LEVEL_DEBUG, "System info: page-size = %u, mapping-page-size = %u, disk-cluster-size = %u, default-stack-size = %u.\n" , global_info::page_size, global_info::page_map_size, global_info::cluster_size, global_info::stack_size); res_mgr_.reset(new resource_mgr()); init(); auto bulk_handle = [&](dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* required) -> dyn_mem_ptr { LPPACK_BASE pack = (LPPACK_BASE)data->ptr(); if(used) *used = data->get_rest(); return handle_bulk_cmd(pack, used, required, data->get_session_id()); }; auto on_connect = [this](bool connected) -> void { utils::to_log(LOG_LEVEL_ALL, "USB %s\n", connected ? "connected" : "dis-connected"); connected_ = connected; if(!connected) cis_->stop_scan(); }; auto user = [&](int priv) -> bool { return user_->has_privilege(priv); }; auto on_log = [&](const char* msg) -> void { utils::log_info(msg, LOG_LEVEL_DEBUG); }; cfg_mgr_ = new device_option(true, user, on_log); utils::to_log(LOG_LEVEL_DEBUG, "OPT - initializing ...\n"); const_opts_ = new scanner_const_opts(); user_ = new user_priv(); cfg_mgr_->add(const_opts_); cis_->set_value(SANE_OPT_NAME(DEVICE_MODEL), &cfg_mgr_->get_option_value(SANE_OPT_NAME(DEVICE_MODEL), SANE_ACTION_GET_VALUE)[0]); cfg_mgr_->add(cis_); cfg_mgr_->add(user_); img_prcr_ = new imgproc_mgr(cfg_mgr_, &async_scanner::image_sender, (void*)this, &async_scanner::resource_check, (void*)res_mgr_.get()); cfg_mgr_->add(img_prcr_); utils::to_log(LOG_LEVEL_DEBUG, "OPT - initialized %u options.\n", cfg_mgr_->count()); usb_ = new async_usb_gadget(bulk_handle, on_connect); last_err_ = usb_->last_error(); devui::send_status_message(ID_WORDS_STATUS_SCANNER_CONN + 1); } async_scanner::~async_scanner() { res_mgr_->stop(); res_mgr_.reset(nullptr); if(cis_) { cis_->stop_scan(); cis_->close(); cis_->release(); cis_ = nullptr; } if(usb_) { usb_->stop(); usb_->release(); usb_ = nullptr; } if(img_prcr_) { img_prcr_->release(); img_prcr_ = nullptr; } if(cfg_mgr_) { cfg_mgr_->clear(); cfg_mgr_->release(); cfg_mgr_ = nullptr; } for(auto& v: send_files_) v->release(); send_files_.clear(); const_opts_->release(); user_->release(); utils::uninit(); devui::uninit_ui(); } bool async_scanner::resource_check(RES_CHK_PROTO) { return ((resource_mgr*)param)->is_resource_enable((enum _task)type, wait, to_ms); } void async_scanner::image_sender(SENDER_PROTO) { async_scanner* obj = (async_scanner*)param; if(ptr->ptr() && ((LPPACK_BASE)ptr->ptr())->cmd == PACK_CMD_SCAN_FINISHED_ROGER) { obj->cis_->close(); obj->res_mgr_->stop(); utils::print_memory_usage("Memory usage when finished scanning", true); system("sudo cpufreq-set -g ondemand"); } obj->usb_->write_bulk(ptr); } dyn_mem_ptr async_scanner::handle_bulk_cmd(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required, uint32_t session) { dyn_mem_ptr reply = nullptr; LPPACK_BASE pk = nullptr; size_t base_head_size = sizeof(PACK_BASE); if(pack->size != base_head_size) { if(used) { utils::log_mem_info("Invalid packet", (uint8_t*)pack, *used); } reply = dyn_mem::memory(base_head_size); LPPACK_BASE p = (LPPACK_BASE)reply->ptr(); BASE_PACKET_REPLY(*p, PACK_CMD_INVALID, pack->pack_id, pack->cmd); reply->set_len(base_head_size); return reply; } switch(pack->cmd) { case PACK_CMD_SYNC: reply = handle_simple_roger(pack, used, required, session); break; case PACK_CMD_SETTING_GET_CUR: reply = handle_get_opt_value(pack, used, required, session); break; case PACK_CMD_SETTING_GET: reply = handle_get_opt_all(pack, used, required, session); break; case PACK_CMD_SETTING_SET: reply = handle_set_opt(pack, used, required, session); break; case PACK_CMD_FILE_WRITE_REQ: reply = handle_file_receive(pack, used, required, session); break; case PACK_CMD_FILE_READ_REQ: reply = handle_file_send(pack, used, required, session); break; case PACK_CMD_FILE_READ_REQ_ROGER: reply = handle_file_send_roger(pack, used, required, session); break; case PACK_CMD_SCAN_START: reply = handle_scan_start(pack, used, required, session); break; case PACK_CMD_SCAN_STOP: reply = handle_scan_stop(pack, used, required, session); break; default: if(used) { utils::log_mem_info("Unsupported command packet", (uint8_t*)pack, *used); } reply = dyn_mem::memory(base_head_size); LPPACK_BASE p = (LPPACK_BASE)reply->ptr(); BASE_PACKET_REPLY(*p, pack->cmd + 1, pack->pack_id, EINVAL); reply->set_len(base_head_size); break; } return reply; } void async_scanner::init(void) { cis_ = new scanner_hw(); auto uicb = [this](devui::LPMSGSTREAM pack) -> void { if(devui::UI_CMD_COUNT_PAPER == pack->msg) { auto receiver = [this](dyn_mem_ptr data, bool img, LPPACKIMAGE lpinfo) -> void { img_prcr_->process(lpinfo, data, img); }; bool auto_scan = true; std::string prev(cfg_mgr_->get_option_value(SANE_OPT_NAME(WAIT_TO_SCAN), SANE_ACTION_GET_VALUE)); cis_->set_value(SANE_OPT_NAME(WAIT_TO_SCAN), &auto_scan); cis_->open(receiver, CHK_RES_FUNC(), nullptr, true); cis_->start_scan(); } else if(devui::UI_CMD_STOP_SCAN == pack->msg) { cis_->stop_scan(true); bool auto_scan = false; cis_->set_value(SANE_OPT_NAME(WAIT_TO_SCAN), &auto_scan); } else if(devui::UI_CMD_CLEAN_PASSWAY == pack->msg) { cis_->clean_paper_passway(); } }; devui::init_ui(uicb, false); } bool async_scanner::on_energy_conservation(bool normal) { bool enable = true; if(normal) { } else { if(cis_->is_scanning()) enable = false; } return enable; } dyn_mem_ptr async_scanner::handle_simple_roger(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required, uint32_t session) { uint32_t base_head_size = sizeof(PACK_BASE); dyn_mem_ptr reply = dyn_mem::memory(base_head_size); LPPACK_BASE pk = (LPPACK_BASE)reply->ptr(); *used = base_head_size; BASE_PACKET_REPLY(*pk, pack->cmd + 1, pack->pack_id, 0); reply->set_len(base_head_size); return reply; } dyn_mem_ptr async_scanner::handle_get_opt_value(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required, uint32_t session) { uint32_t base_head_size = sizeof(PACK_BASE); dyn_mem_ptr reply = nullptr; LPPACK_BASE pk = nullptr; if(*used < base_head_size + pack->payload_len) { *used = 0; } else { std::string val(cfg_mgr_->get_option_value(pack->payload, SANE_ACTION_GET_VALUE)); uint32_t err = val.empty() ? SCANNER_ERR_NO_DATA : SCANNER_ERR_OK; LPCFGVAL cfg = nullptr; reply = dyn_mem::memory(base_head_size + sizeof(CFGVAL) + strlen(pack->payload) + 1 + val.length() + 1); pk = (LPPACK_BASE)reply->ptr(); BASE_PACKET_REPLY(*pk, pack->cmd + 1, pack->pack_id, err); cfg = (LPCFGVAL)pk->payload; cfg->val_size = val.length(); cfg->val_off = 0; cfg->name_off = val.length() + 1; pk->payload_len = sizeof(CFGVAL) + strlen(pack->payload) + 1 + val.length() + 1; memcpy(cfg->data, val.c_str(), val.length()); strcpy(cfg->data + cfg->name_off, pack->payload); reply->set_len(base_head_size + pk->payload_len); *used = base_head_size + pack->payload_len; } return reply; } dyn_mem_ptr async_scanner::handle_get_opt_all(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required, uint32_t session) { uint32_t base_head_size = sizeof(PACK_BASE); dyn_mem_ptr reply = nullptr; LPPACK_BASE pk = nullptr; *used = base_head_size; { std::string val(cfg_mgr_->get_option_value(nullptr, SANE_ACTION_GET_ENTIRE_JSON)); uint32_t err = 0; reply = dyn_mem::memory(base_head_size + val.length() + 1); pk = (LPPACK_BASE)reply->ptr(); BASE_PACKET_REPLY(*pk, pack->cmd + 1, pack->pack_id, err); strcpy(pk->payload, val.c_str()); pk->payload_len = val.length() + 1; reply->set_len(base_head_size + val.length() + 1); } return reply; } dyn_mem_ptr async_scanner::handle_set_opt(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required, uint32_t session) { uint32_t base_head_size = sizeof(PACK_BASE); dyn_mem_ptr reply = nullptr; LPPACK_BASE pk = nullptr; if (*used < base_head_size + pack->payload_len) *used = 0; else { LPCFGVAL cfg = (LPCFGVAL)pack->payload, cfg_ret = nullptr; std::string name(cfg->data + cfg->name_off); size_t l = base_head_size + sizeof(CFGVAL) + name.length() + 1 + cfg->max_size + 1, val_size = cfg->val_size; int32_t err = 0; uint32_t after = 0; reply = dyn_mem::memory(l); pk = (LPPACK_BASE)reply->ptr(); cfg_ret = (LPCFGVAL)pk->payload; cfg_ret->name_off = 0; strcpy(cfg_ret->data + cfg_ret->name_off, name.c_str()); cfg_ret->val_off = name.length() + 1; memcpy(cfg_ret->data + cfg_ret->val_off, cfg->data + cfg->val_off, cfg->val_size); cfg_ret->val_size = cfg->val_size; cfg_ret->max_size = cfg->max_size; cfg_ret->type = cfg->type; err = cfg_mgr_->update_data(cfg->data + cfg->name_off, cfg_ret->data + cfg_ret->val_off); if(err == SCANNER_ERR_RELOAD_OPT_PARAM || err == SCANNER_ERR_RELOAD_IMAGE_PARAM || err == SCANNER_ERR_CONFIGURATION_CHANGED) { after = err; err = SCANNER_ERR_OK; } cfg_ret->after_do = after; cfg_ret->val_size = val_size; BASE_PACKET_REPLY(*pk, pack->cmd + 1, pack->pack_id, err); pk->payload_len = sizeof(CFGVAL) + name.length() + 1 + cfg->max_size + 1; reply->set_len(l); } return reply; } dyn_mem_ptr async_scanner::handle_file_receive(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required, uint32_t session) { uint32_t base_head_size = sizeof(PACK_BASE); dyn_mem_ptr reply = nullptr; LPPACK_BASE pk = nullptr; if(*used < pack->payload_len + pack->size) { *used = 0; } else { LPTXFILE pfi = (LPTXFILE)pack->payload; std::string path(pfi->path); int err = 0; file_saver *saver = new file_saver(); err = saver->open(path.c_str(), pfi->size, true, pfi->offset); reply = dyn_mem::memory(base_head_size); reply->set_len(base_head_size); BASE_PACKET_REPLY(*((LPPACK_BASE)reply->ptr()), pack->cmd + 1, pack->pack_id, err); *used = base_head_size + pack->payload_len; if(err) { saver->release(); saver = nullptr; } else { saver->set_packet_param(pack->cmd, pack->pack_id); } utils::to_log(LOG_LEVEL_DEBUG, "Receiving file(%p bytes): %s = %d\n", pfi->size, path.c_str(), err); *required = dynamic_cast(saver); } return reply; } dyn_mem_ptr async_scanner::handle_file_send(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required, uint32_t session) { uint32_t base_head_size = sizeof(PACK_BASE); dyn_mem_ptr reply = dyn_mem::memory(base_head_size); LPPACK_BASE pk = (LPPACK_BASE)reply->ptr(); if(*used < pack->payload_len + pack->size) { *used = 0; } else { LPTXFILE pfi = (LPTXFILE)pack->payload; std::string path(pfi->path); int err = 0; file_reader *reader = new file_reader(); err = reader->open(path.c_str(), true, pfi->offset); reply = dyn_mem::memory(base_head_size + sizeof(TXFILE)); reply->set_len(base_head_size + sizeof(TXFILE)); BASE_PACKET_REPLY(*((LPPACK_BASE)reply->ptr()), pack->cmd + 1, pack->pack_id, err); ((LPPACK_BASE)reply->ptr())->payload_len = sizeof(TXFILE); *used = base_head_size + pack->payload_len; utils::to_log(LOG_LEVEL_DEBUG, "To send file '%s' with %u bytes = %d\n", path.c_str(), reader->get_rest(), err); if(err) { reader->release(); reader = nullptr; } else { ((LPTXFILE)((LPPACK_BASE)reply->ptr())->payload)->size = reader->get_rest(); reader->set_packet_param(pack->cmd, pack->pack_id); { // move to PACK_CMD_FILE_READ_REQ_ROGER SIMPLE_LOCK(fsender_); send_files_.push_back(reader); reader = nullptr; } } *required = dynamic_cast(reader); } return reply; } dyn_mem_ptr async_scanner::handle_file_send_roger(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required, uint32_t session) { uint32_t base_head_size = sizeof(PACK_BASE); dyn_mem_ptr reply = nullptr; if(*used < base_head_size) { *used = 0; } else { file_reader* reader = nullptr; { SIMPLE_LOCK(fsender_); for(size_t i = 0; i < send_files_.size(); ++i) { if(send_files_[i]->get_packet_id() == pack->pack_id) { reader = send_files_[i]; send_files_.erase(send_files_.begin() + i); break; } } } if(!reader) { utils::log_mem_info("FATAL: Sending file lost source object !!!", pack, *used, LOG_LEVEL_FATAL); } else if(pack->data) { utils::to_log(LOG_LEVEL_DEBUG, "Sending file '%s' (%p) cancelled with error %d.\n", reader->path_file(), reader, pack->data); reader->release(); reader = nullptr; } else { // if reader was nullptr, notify failed by INT or control ? utils::to_log(LOG_LEVEL_DEBUG, "Sending file '%s' (%p) ...\n", reader->path_file(), reader); } *used = base_head_size; *required = dynamic_cast(reader); } return reply; } dyn_mem_ptr async_scanner::handle_scan_start(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required, uint32_t session) { if(!used) { reply_start_ = true; return nullptr; } uint32_t base_head_size = sizeof(PACK_BASE); dyn_mem_ptr reply = nullptr; std::string config(""); img_cnt_ = 0; scan_id_ = pack->pack_id; scan_err_ = 0; reply_start_ = false; res_mgr_->start(); auto receiver = [this](dyn_mem_ptr data, bool img, LPPACKIMAGE lpinfo) -> void { img_prcr_->process(lpinfo, data, img); }; DECL_CHK_RES_FUNC(this, res) { return res_mgr_->is_resource_enable((enum _task)task, wait, to_ms); }; utils::print_memory_usage("Memory usage before scanning", true); *used = base_head_size; img_prcr_->start_new_turn(scan_id_, session); scan_err_ = cis_->open(receiver, res, &config); reply = dyn_mem::memory(base_head_size + config.length() + 2); reply->set_len(base_head_size + config.length() + 2); if(scan_err_ == 0) scan_err_ = cis_->start_scan(); if(scan_err_) { cis_->stop_scan(); cis_->close(); } else system("echo performance > /sys/devices/system/cpu/cpu5/cpufreq/scaling_governor"); BASE_PACKET_REPLY(*((LPPACK_BASE)reply->ptr()), pack->cmd + 1, pack->pack_id, scan_err_); ((LPPACK_BASE)reply->ptr())->payload_len = config.length() + 2; strcpy(((LPPACK_BASE)reply->ptr())->payload, config.c_str()); *used |= INT32_MAX + 1; return reply; } dyn_mem_ptr async_scanner::handle_scan_stop(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required, uint32_t session) { uint32_t base_head_size = sizeof(PACK_BASE); dyn_mem_ptr reply = dyn_mem::memory(base_head_size); int err = 0; utils::to_log(LOG_LEVEL_DEBUG, "Received command Stop-Scan.\n"); err = cis_->stop_scan(); BASE_PACKET_REPLY(*((LPPACK_BASE)reply->ptr()), pack->cmd + 1, pack->pack_id, err); reply->set_len(base_head_size); *used = base_head_size; return reply; } dyn_mem_ptr async_scanner::handle_process_cmd(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required, uint32_t session) { uint32_t base_head_size = sizeof(PACK_BASE); dyn_mem_ptr reply = dyn_mem::memory(base_head_size); LPPACK_BASE pk = (LPPACK_BASE)reply->ptr(); *used = base_head_size + pack->payload_len; BASE_PACKET_REPLY(*pk, pack->cmd + 1, pack->pack_id, EINVAL); reply->set_len(base_head_size); return reply; } uint32_t async_scanner::stop(void) { if(img_prcr_) img_prcr_->stop(); if(usb_) { usb_->stop(); } return 0; } int async_scanner::last_error(void) { return last_err_; }