#include "async_scanner.h" #include #include #include #include #include #include #include #include #include #include #include #include "scanner_const_opts.h" #include #include #include //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 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.\n" , global_info::page_size, global_info::page_map_size, global_info::cluster_size); 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); }; 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_FULL_NAME(DEVICE_MODEL), &cfg_mgr_->get_option_value(SANE_FULL_NAME(DEVICE_MODEL), SANE_ACTION_GET_VALUE)[0]); cfg_mgr_->add(cis_); cfg_mgr_->add(user_); 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(); } async_scanner::~async_scanner() { if(cis_) { cis_->stop_scan(); cis_->close(); cis_->release(); cis_ = nullptr; } if(usb_) { usb_->stop(); usb_->release(); } 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(); } dyn_mem_ptr async_scanner::handle_bulk_cmd(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required) { 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); break; case PACK_CMD_SETTING_GET_CUR: reply = handle_get_opt_value(pack, used, required); break; case PACK_CMD_SETTING_GET: reply = handle_get_opt_all(pack, used, required); break; case PACK_CMD_SETTING_SET: reply = handle_set_opt(pack, used, required); break; case PACK_CMD_FILE_WRITE_REQ: reply = handle_file_receive(pack, used, required); break; case PACK_CMD_FILE_READ_REQ: reply = handle_file_send(pack, used, required); break; case PACK_CMD_FILE_READ_REQ_ROGER: reply = handle_file_send_roger(pack, used, required); break; case PACK_CMD_SCAN_START: reply = handle_scan_start(pack, used, required); break; case PACK_CMD_SCAN_STOP: reply = handle_scan_stop(pack, used, required); 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](int ev_type) -> std::string { std::string ret(""); return ret; }; devui::init_ui(uicb); } 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 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 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 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 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 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 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 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) { if(!used) { reply_start_ = true; return nullptr; } uint32_t base_head_size = sizeof(PACK_BASE); dyn_mem_ptr reply = dyn_mem::memory(base_head_size); img_cnt_ = 0; scan_id_ = pack->pack_id; scan_err_ = 0; reply_start_ = false; auto receiver = [this](dyn_mem_ptr data, bool img, LPPACKIMAGE lpinfo) -> void { if(img) { FILE* dst = fopen(("/tmp/scan_" + std::to_string(lpinfo->pos.paper_ind) + ".bmp").c_str(), "wb"); if(dst) { std::string bih(utils::bitmap_info_header(lpinfo->width, lpinfo->height, lpinfo->bpp * lpinfo->channels, lpinfo->resolution_x, lpinfo->resolution_y)), bfh(utils::bitmap_file_header((BITMAPINFOHEADER*)&bih[0])); fwrite(bfh.c_str(), 1, bfh.length(), dst); fwrite(bih.c_str(), 1, bih.length(), dst); fwrite(data->ptr(), 1, data->get_rest(), dst); fclose(dst); } data->release(); } }; *used = base_head_size; reply->set_len(base_head_size); scan_err_ = cis_->open(receiver); if(scan_err_ == 0) scan_err_ = cis_->start_scan(); if(scan_err_) { cis_->stop_scan(); } BASE_PACKET_REPLY(*((LPPACK_BASE)reply->ptr()), pack->cmd + 1, pack->pack_id, scan_err_); *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 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 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(usb_) { usb_->stop(); } return 0; } int async_scanner::last_error(void) { return last_err_; }