code_device/hgsane/sane_hg_mdw.cpp

864 lines
25 KiB
C++

#include "sane_hg_mdw.h"
#include "sane_opt/sane_opts.h"
#include <stdarg.h>
#include <time.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mutex>
#include <thread>
#if defined(WIN32) || defined(_WIN64)
#include <windows.h>
#include <direct.h>
#define pthread_t DWORD
#define pthread_self GetCurrentThreadId
#define MKDIR(a, b) mkdir(a)
#else
#define MKDIR(a, b) mkdir(a, b)
#include <pthread.h>
#include <signal.h>
#endif
#include <sane/sane_option_definitions.h>
#include <lang/app_language.h>
#include <base/utils.h>
#include <huagao/brand.h>
#include <sane_opt_json/device_opt.h> // for readable_text(SANE_Value_Type type, void* value)
#include <huagao/hgscanner_error.h>
#ifndef SIGUSR1
#define SIGUSR1 10
#endif
// copy to /usr/lib/x86_64-linux-gnu/sane
// export SANE_DEBUG_DLL=5
#ifndef iconv_t
#define iconv_t void*
#endif
namespace local_utility
{
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define RETURN_MATCH_ERROR(err, from, to) \
if(err == from) \
return to;
SANE_Status scanner_err_2_sane_statu(int hgerr)
{
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_OK, SANE_STATUS_GOOD);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_INVALID_PARAMETER, SANE_STATUS_INVAL);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_INSUFFICIENT_MEMORY, SANE_STATUS_NO_MEM);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_ACCESS_DENIED, SANE_STATUS_ACCESS_DENIED);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_IO_PENDING, SANE_STATUS_GOOD);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_NOT_EXACT, SANE_STATUS_GOOD);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_CONFIGURATION_CHANGED, SANE_STATUS_GOOD);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_RELOAD_IMAGE_PARAM, SANE_STATUS_GOOD);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_RELOAD_OPT_PARAM, SANE_STATUS_GOOD);
//RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_NOT_OPEN, SANE_STATUS_NO_DOCS);
//RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_NOT_START, SANE_STATUS_NO_DOCS);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_NO_DATA, SANE_STATUS_EOF);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_IO, SANE_STATUS_IO_ERROR);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_OUT_OF_RANGE, SANE_STATUS_INVAL);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_IO, SANE_STATUS_IO_ERROR);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_TIMEOUT, SANE_STATUS_IO_ERROR);
// RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_DEVICE_NOT_FOUND, SANE_STATUS_NO_DOCS);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_DEVICE_NOT_SUPPORT, SANE_STATUS_UNSUPPORTED);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_DEVICE_BUSY, SANE_STATUS_DEVICE_BUSY);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_DEVICE_COVER_OPENNED, SANE_STATUS_COVER_OPEN);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_DEVICE_NO_PAPER, SANE_STATUS_NO_DOCS);
RETURN_MATCH_ERROR(hgerr, SCANNER_ERR_DEVICE_PAPER_JAMMED, SANE_STATUS_JAMMED);
return (SANE_Status)hgerr;
}
int sane_statu_2_scanner_err(int statu)
{
RETURN_MATCH_ERROR(statu, SANE_STATUS_GOOD, SCANNER_ERR_OK);
RETURN_MATCH_ERROR(statu, SANE_STATUS_INVAL, SCANNER_ERR_INVALID_PARAMETER);
RETURN_MATCH_ERROR(statu, SANE_STATUS_NO_MEM, SCANNER_ERR_INSUFFICIENT_MEMORY);
RETURN_MATCH_ERROR(statu, SANE_STATUS_ACCESS_DENIED, SCANNER_ERR_ACCESS_DENIED);
RETURN_MATCH_ERROR(statu, SANE_STATUS_GOOD, SCANNER_ERR_IO_PENDING);
RETURN_MATCH_ERROR(statu, SANE_STATUS_GOOD, SCANNER_ERR_NOT_EXACT);
RETURN_MATCH_ERROR(statu, SANE_STATUS_GOOD, SCANNER_ERR_CONFIGURATION_CHANGED);
RETURN_MATCH_ERROR(statu, SANE_STATUS_GOOD, SCANNER_ERR_RELOAD_IMAGE_PARAM);
RETURN_MATCH_ERROR(statu, SANE_STATUS_GOOD, SCANNER_ERR_RELOAD_OPT_PARAM);
//RETURN_MATCH_ERROR(statu, SANE_STATUS_NO_DOCS, SCANNER_ERR_NOT_OPEN);
//RETURN_MATCH_ERROR(statu, SANE_STATUS_NO_DOCS, SCANNER_ERR_NOT_START);
RETURN_MATCH_ERROR(statu, SANE_STATUS_EOF, SCANNER_ERR_NO_DATA);
RETURN_MATCH_ERROR(statu, SANE_STATUS_IO_ERROR, SCANNER_ERR_IO);
RETURN_MATCH_ERROR(statu, SANE_STATUS_INVAL, SCANNER_ERR_OUT_OF_RANGE);
RETURN_MATCH_ERROR(statu, SANE_STATUS_IO_ERROR, SCANNER_ERR_IO);
RETURN_MATCH_ERROR(statu, SANE_STATUS_IO_ERROR, SCANNER_ERR_TIMEOUT);
//RETURN_MATCH_ERROR(statu, SANE_STATUS_NO_DOCS, SCANNER_ERR_DEVICE_NOT_FOUND);
RETURN_MATCH_ERROR(statu, SANE_STATUS_UNSUPPORTED, SCANNER_ERR_DEVICE_NOT_SUPPORT);
RETURN_MATCH_ERROR(statu, SANE_STATUS_DEVICE_BUSY, SCANNER_ERR_DEVICE_BUSY);
RETURN_MATCH_ERROR(statu, SANE_STATUS_COVER_OPEN, SCANNER_ERR_DEVICE_COVER_OPENNED);
RETURN_MATCH_ERROR(statu, SANE_STATUS_NO_DOCS, SCANNER_ERR_DEVICE_NO_PAPER);
RETURN_MATCH_ERROR(statu, SANE_STATUS_JAMMED, SCANNER_ERR_DEVICE_PAPER_JAMMED);
return statu;
}
void* acquire_memory(size_t bytes, const char* info)
{
//if (!info)
// info = "";
//hg_sane_middleware::log(strlen(info) + 80, "allocate memory with %u bytes from %s\n", bytes, info);
void* buf = malloc(bytes);
if (buf)
memset(buf, 0, bytes);
return buf;
}
void free_memory(void* m)
{
if (m)
free(m);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static sane_callback cb_ui_ = nullptr;
static void* cb_ui_parm_ = nullptr;
static SANE_Auth_Callback cb_auth_ = nullptr;
static std::mutex cb_lock_;
static std::string sane_event(SANE_Event ev)
{
RETURN_IF(ev, SANE_EVENT_NONE);
RETURN_IF(ev, SANE_EVENT_SUPPORT_ASYNC_IO);
RETURN_IF(ev, SANE_EVENT_IS_MEMORY_ENOUGH);
RETURN_IF(ev, SANE_EVENT_NEED_AUTH);
RETURN_IF(ev, SANE_EVENT_DEVICE_ARRIVED);
RETURN_IF(ev, SANE_EVENT_DEVICE_LEFT);
RETURN_IF(ev, SANE_EVENT_STATUS);
RETURN_IF(ev, SANE_EVENT_ERROR);
RETURN_IF(ev, SANE_EVENT_WORKING);
RETURN_IF(ev, SANE_EVENT_IMAGE_OK);
RETURN_IF(ev, SANE_EVENT_SCAN_FINISHED);
RETURN_IF(ev, SANE_EVENT_ABOUT_INFORMATION);
RETURN_IF(ev, SANE_EVENT_SCANNER_CLOSED);
char unk[20];
sprintf(unk, "%d", ev);
return unk;
}
int ui_cb(scanner_handle dev, int code, void* data, unsigned int* len, void* unused)
{
sane_callback cb_ui = nullptr;
void* cb_ui_parm = nullptr;
SANE_Auth_Callback cb_auth = nullptr;
{
std::lock_guard<std::mutex> lck(cb_lock_);
cb_ui = cb_ui_;
cb_ui_parm = cb_ui_parm_;
cb_auth_ = cb_auth;
}
if (SANE_EVENT_SUPPORT_ASYNC_IO == code)
return cb_ui_ ? SCANNER_ERR_OK : SCANNER_ERR_DEVICE_NOT_SUPPORT;
SANE_Handle h = hg_sane_middleware::scanner_handle_to_sane(dev);
// utils::to_log_with_api(hg_scanner_log_is_enable, hg_scanner_log, LOG_LEVEL_ALL, "sane callback invoked of event %s\n", sane_event((SANE_Event)code).c_str());
if (cb_ui)
{
return cb_ui(h, code, data, len, cb_ui_parm);
}
else if (cb_auth && code == SANE_EVENT_NEED_AUTH)
{
SANEAUTH* auth = (SANEAUTH*)data;
cb_auth(auth->resource, auth->name, auth->pwd);
}
return 0;
}
void get_version(SANE_Int* version_code)
{
if (version_code)
*version_code = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, VERSION_BUILD); // leading-char '1' is used for avoid compiler considering '0118' as an octal number :)
}
void set_callback(sane_callback cb, void* param)
{
std::lock_guard<std::mutex> lck(cb_lock_);
cb_ui_ = cb;
cb_ui_parm_ = param;
}
void stop_work(void)
{
std::lock_guard<std::mutex> lck(cb_lock_);
cb_ui_ = nullptr;
cb_ui_parm_ = nullptr;
cb_auth_ = nullptr;
}
static void dump_msg(const char* msg)
{
utils::to_log_with_api(hg_scanner_log_is_enable, hg_scanner_log, LOG_LEVEL_DEBUG, msg);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
hg_sane_middleware* hg_sane_middleware::inst_ = nullptr;
const SANE_Device** hg_sane_middleware::dev_list_ = nullptr;
hg_sane_middleware::hg_sane_middleware(void) : init_ok_(false), offline_(nullptr)
{
char sane_ver[40] = { 0 };
init_ok_ = true;
sprintf(sane_ver, "%u.%u.%u", SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, VERSION_BUILD);
#if !defined(WIN32)
signal(SIGUSR1, &hg_sane_middleware::device_pnp);
#endif
hg_scanner_set_sane_info(MODULE_NAME_SANE, sane_ver);
hg_scanner_initialize(local_utility::ui_cb, nullptr);
// initialize offline options with scanner_handle nullptr:
{
offline_ = new DEVINST;
offline_->opts = new sane_options();
reload_options(offline_, RELOAD_FOR_DEVICE_OPEN);
}
std::this_thread::sleep_for(std::chrono::milliseconds(500)); // wait for device OK
}
hg_sane_middleware::~hg_sane_middleware()
{
for (auto& v : openning_)
{
hg_sane_middleware::free_device_instance(v);
}
hg_sane_middleware::free_device_instance(offline_);
offline_ = nullptr;
openning_.clear();
hg_scanner_uninitialize();
if (hg_sane_middleware::dev_list_)
local_utility::free_memory(hg_sane_middleware::dev_list_);
}
const SANE_Device** hg_sane_middleware::to_sane_device(ScannerInfo* hgscanner, int count)
{
// 将多级指针安排在一个连续的内存空间存放
SANE_Device** ret = nullptr, * dev = nullptr;
SANE_String val = nullptr;
unsigned long bytes = (count + 1) * (sizeof(SANE_Device) + sizeof(SANE_Device*)), total = 0;
// calculate space ...
for (int i = 0; i < count; ++i)
{
bytes += ALIGN_INT(strlen(hgscanner[i].name) + 1);
bytes += ALIGN_INT(strlen(hgscanner[i].vendor) + 1);
bytes += ALIGN_INT(strlen(hgscanner[i].model) + 1);
}
bytes = ALIGN_INT(bytes + 16);
dev = (SANE_Device*)local_utility::acquire_memory(bytes, "hg_sane_middleware::to_sane_device");
total = bytes;
if (!dev)
return nullptr;
memset(dev, 0, bytes);
ret = (SANE_Device**)dev;
dev = (SANE_Device*)((SANE_Device**)dev + count + 1);
val = (SANE_String)(dev + count);
#define COPY_DEVICE_MEMBER(m) \
dev->m = val; \
strcpy(val, hgscanner[i].m); \
bytes = ALIGN_INT(strlen(val) + 1); \
val += bytes;
for (int i = 0; i < count; ++i, ++dev)
{
ret[i] = dev;
COPY_DEVICE_MEMBER(name);
COPY_DEVICE_MEMBER(vendor);
COPY_DEVICE_MEMBER(model);
}
//utils::to_log_with_api(hg_scanner_log_is_enable, hg_scanner_log, LOG_LEVEL_ALL, "Memory usage: %u / %u\n", val - (char*)ret, total);
return (const SANE_Device**)ret;
}
void hg_sane_middleware::free_sane_device(SANE_Device** dev)
{
char* mem = (char*)dev;
if (mem)
{
free(mem);
}
}
void hg_sane_middleware::device_pnp(int sig)
{
utils::to_log_with_api(hg_scanner_log_is_enable, hg_scanner_log, LOG_LEVEL_DEBUG, "Device list changed (%d)...", sig);
}
SANE_Fixed hg_sane_middleware::double_2_sane_fixed(double v)
{
return SANE_FIX(v);
}
double hg_sane_middleware::sane_fixed_2_double(SANE_Fixed v)
{
return SANE_UNFIX(v);
}
scanner_handle hg_sane_middleware::sane_handle_to_scanner(SANE_Handle h)
{
if (!h)
return nullptr;
int bits = sizeof(h) / 2 * 8;
uint64_t v = (uint64_t)h;
v ^= v >> bits;
return (scanner_handle)(v);
}
SANE_Handle hg_sane_middleware::scanner_handle_to_sane(scanner_handle h)
{
if (!h)
return nullptr;
int bits = sizeof(h) / 2 * 8;
uint64_t v = (uint64_t)h;
v ^= v >> bits;
return (SANE_Handle)(v);
}
void hg_sane_middleware::free_device_instance(LPDEVINST inst)
{
if (inst)
{
if (inst->dev)
hg_scanner_close(inst->dev, true);
if (inst->opts)
delete inst->opts;
delete inst;
}
}
hg_sane_middleware::LPDEVINST hg_sane_middleware::find_openning_device(SANE_Handle h, bool rmv)
{
LPDEVINST ret = nullptr;
scanner_handle handle = hg_sane_middleware::sane_handle_to_scanner(h);
for (int i = 0; i < openning_.size(); ++i)
{
if (openning_[i]->dev == handle)
{
ret = openning_[i];
if (rmv)
openning_.erase(openning_.begin() + i);
break;
}
}
return ret;
}
hg_sane_middleware::LPDEVINST hg_sane_middleware::find_openning_device(const char* name, bool rmv)
{
LPDEVINST ret = nullptr;
for(int i = 0; i < openning_.size(); ++i)
{
if (openning_[i]->name == name)
{
ret = openning_[i];
if (rmv)
openning_.erase(openning_.begin() + i);
break;
}
}
return ret;
}
bool hg_sane_middleware::reload_options(LPDEVINST inst, int reason)
{
long len = 0;
char *buf = nullptr;
scanner_err err = hg_scanner_get_option(inst->dev, PARAM_ALL, buf, &len, SANE_ACTION_GET_ENTIRE_JSON);
if (err == SCANNER_ERR_INSUFFICIENT_MEMORY)
{
buf = new char[len + 4];
err = hg_scanner_get_option(inst->dev, PARAM_ALL, buf, &len, SANE_ACTION_GET_ENTIRE_JSON);
}
if (err == SCANNER_ERR_OK && len)
{
if (!inst->opts->init_from(buf, local_utility::dump_msg, reason == RELOAD_FOR_DEVICE_OPEN))
err = SCANNER_ERR_DATA_DAMAGED;
}
if (buf)
delete[] buf;
return err == SCANNER_ERR_OK;
}
scanner_err hg_sane_middleware::read_value(scanner_handle h, const char* name, SANE_Value_Type type, size_t len, void* value, bool to_default)
{
scanner_err err = SCANNER_ERR_INVALID_PARAMETER;
if (type == SANE_TYPE_BOOL)
{
bool v = false;
long len = sizeof(v);
err = hg_scanner_get_option(h, name, (char*)&v, &len, to_default ? SANE_ACTION_GET_DEFAULT_VALUE : SANE_ACTION_GET_VALUE);
if (value)
*(SANE_Bool*)value = v ? SANE_TRUE : SANE_FALSE;
}
else if (type == SANE_TYPE_INT)
{
int v = 0;
long len = sizeof(v);
err = hg_scanner_get_option(h, name, (char*)&v, &len, to_default ? SANE_ACTION_GET_DEFAULT_VALUE : SANE_ACTION_GET_VALUE);
if (value)
*(SANE_Int*)value = v;
}
else if (type == SANE_TYPE_FIXED)
{
double v = .0f;
long len = sizeof(v);
err = hg_scanner_get_option(h, name, (char*)&v, &len, to_default ? SANE_ACTION_GET_DEFAULT_VALUE : SANE_ACTION_GET_VALUE);
if (value)
*(SANE_Fixed*)value = hg_sane_middleware::double_2_sane_fixed(v);
}
else
{
long l = len;
err = hg_scanner_get_option(h, name, (char*)value, &l, to_default ? SANE_ACTION_GET_DEFAULT_VALUE : SANE_ACTION_GET_VALUE);
}
return err;
}
scanner_err hg_sane_middleware::write_value(scanner_handle h, const char* name, SANE_Value_Type type, void* value, bool to_default, LPDEVINST optinst, SANE_Int* affect)
{
scanner_err err = SCANNER_ERR_INVALID_PARAMETER;
if (type == SANE_TYPE_BOOL)
{
bool v = value ? *(SANE_Bool*)value == SANE_TRUE : false;
err = hg_scanner_set_option(h, name, &v, to_default);
if (value)
*(SANE_Bool*)value = v ? SANE_TRUE : SANE_FALSE;
}
else if (type == SANE_TYPE_INT)
{
int v = value ? *(SANE_Int*)value : 0;
err = hg_scanner_set_option(h, name, &v, to_default);
if (value)
*(SANE_Int*)value = v;
}
else if (type == SANE_TYPE_FIXED)
{
double v = value ? hg_sane_middleware::sane_fixed_2_double(*(SANE_Fixed*)value) : .0f;
err = hg_scanner_set_option(h, name, &v, to_default);
if (value)
*(SANE_Fixed*)value = hg_sane_middleware::double_2_sane_fixed(v);
}
else
{
err = hg_scanner_set_option(h, name, value, to_default);
}
if (affect)
*affect = 0;
if (err == SCANNER_ERR_RELOAD_OPT_PARAM || err == SCANNER_ERR_CONFIGURATION_CHANGED)
{
if(optinst)
reload_options(optinst, RELOAD_FOR_OPT_CHANGED);
if (affect)
*affect = SANE_INFO_RELOAD_OPTIONS | SANE_INFO_RELOAD_PARAMS;
err = SCANNER_ERR_OK;
}
else if (err == SCANNER_ERR_RELOAD_IMAGE_PARAM)
{
err = SCANNER_ERR_OK;
if (affect)
*affect = SANE_INFO_RELOAD_PARAMS;
}
return err;
}
SANE_Status hg_sane_middleware::get_current_value(LPDEVINST inst, const void* opt, void* value, bool default_val)
{
std::string val("");
SANE_Option_Descriptor* desc = inst->opts->get_opt_descriptor(opt);
/*if (!desc && inst != offline_)
{
desc = offline_->opts->get_opt_descriptor(opt, nullptr, inst->opts->get_option_count());
if (desc)
inst = offline_;
else
return SANE_STATUS_INVAL;
}
else*/ if (!desc)
{
return SANE_STATUS_INVAL;
}
if (desc)
{
scanner_err err = read_value(inst->dev, desc->name, desc->type, desc->size, value, default_val);
return local_utility::scanner_err_2_sane_statu(err);
}
else
{
return SANE_STATUS_INVAL;
}
}
SANE_Status hg_sane_middleware::set_value(LPDEVINST inst, const void* opt, void* value, SANE_Int* affect, bool to_default, std::string* title, std::string* val_text_before, std::string* val_text_after)
{
bool empty_value = false;
SANE_Option_Descriptor* desc = inst->opts->get_opt_descriptor(opt);
/*if (!desc && inst != offline_)
{
desc = offline_->opts->get_opt_descriptor(opt, nullptr, inst->opts->get_option_count());
if (desc)
inst = offline_;
else
return SANE_STATUS_INVAL;
}
else*/ if (!desc)
{
return SANE_STATUS_INVAL;
}
SANE_Value_Type type = desc->type;
if (val_text_before && value)
*val_text_before = sane_opt::readable_text(desc->type, value);
if (title)
*title = desc->title;
if (IS_CAP_READONLY(desc->cap))
return SANE_STATUS_ACCESS_DENIED;
if (to_default && (desc->cap & SANE_CAP_AUTOMATIC) == 0)
return SANE_STATUS_UNSUPPORTED;
scanner_err err = write_value(inst->dev, desc->name, desc->type, value, to_default, inst, affect);
if (val_text_after && value)
*val_text_after = sane_opt::readable_text(type, value);
return local_utility::scanner_err_2_sane_statu(err);
}
SANE_Status hg_sane_middleware::get_option_fixed_id(LPDEVINST inst, const void* opt, void* value)
{
int fix_id = 0;
inst->opts->get_opt_descriptor(opt, &fix_id);
//if (!inst->opts->get_opt_descriptor(opt, &fix_id) && inst != offline_)
//{
// offline_->opts->get_opt_descriptor(opt, &fix_id, inst->opts->get_option_count());
//}
*(SANE_Int*)value = fix_id;
return fix_id <= 0 ? SANE_STATUS_INVAL : SANE_STATUS_GOOD;
}
void hg_sane_middleware::set_app_callback(void* cb, void* param, int type)
{
if (type == APP_CALLBACK_EX)
local_utility::set_callback((sane_callback)cb, param);
else
local_utility::cb_auth_ = (SANE_Auth_Callback)cb;
}
void hg_sane_middleware::get_version(SANE_Int* ver)
{
if (ver)
*ver = SANE_VERSION_CODE(SANE_CURRENT_MAJOR, SANE_CURRENT_MINOR, VERSION_BUILD); // leading-char '1' is used for avoid compiler considering '0118' as an octal number :)
}
hg_sane_middleware* hg_sane_middleware::instance(void)
{
if (!hg_sane_middleware::inst_)
hg_sane_middleware::inst_ = new hg_sane_middleware();
return hg_sane_middleware::inst_;
}
void hg_sane_middleware::clear(void)
{
local_utility::stop_work();
if (hg_sane_middleware::inst_)
{
delete hg_sane_middleware::inst_;
hg_sane_middleware::inst_ = nullptr;
}
}
SANE_Status hg_sane_middleware::get_devices(const SANE_Device*** device_list, SANE_Bool local_only)
{
if (!device_list)
return SANE_STATUS_INVAL;
ScannerInfo * dev = nullptr;
long count = 0;
scanner_err hgerr = hg_scanner_enum(dev, &count, local_only);
SANE_Status ret = SANE_STATUS_GOOD;
if (hgerr == SCANNER_ERR_INSUFFICIENT_MEMORY)
{
count += 4;
dev = (ScannerInfo*)local_utility::acquire_memory(sizeof(ScannerInfo) * count, "hg_sane_middleware::get_devices");
hgerr = hg_scanner_enum(dev, &count, local_only);
}
if (hgerr == SCANNER_ERR_OK)
*device_list = hg_sane_middleware::to_sane_device(dev, count);
else
ret = local_utility::scanner_err_2_sane_statu(hgerr);
if (dev)
local_utility::free_memory(dev);
if (hg_sane_middleware::dev_list_)
local_utility::free_memory(hg_sane_middleware::dev_list_);
hg_sane_middleware::dev_list_ = *device_list;
return ret;
}
SANE_Status hg_sane_middleware::open_device(SANE_String_Const devicename, SANE_Handle* handle)
{
if (!handle)
return SANE_STATUS_INVAL;
char rsc[128] = { 0 };
scanner_handle h = nullptr;
scanner_err se = hg_scanner_open(&h, devicename, false, nullptr, nullptr, nullptr, rsc);
if (se == SCANNER_ERR_ACCESS_DENIED && rsc[0])
{
SANEAUTH auth;
memset(&auth, 0, sizeof(auth));
auth.resource = rsc;
if (local_utility::ui_cb(nullptr, SANE_EVENT_NEED_AUTH, (void*)&auth, nullptr, nullptr))
{
return SANE_STATUS_CANCELLED;
}
se = hg_scanner_open(&h, devicename, false, auth.name, auth.pwd, auth.method, rsc);
}
if (se == SCANNER_ERR_OK)
{
// for depend item maybe later, we move the depend-logic-operation out of 'from_json'. to ensure the status, we get all options actively and do depend-logic operation after all options got - 2023-06-21
LPDEVINST inst = new DEVINST;
inst->dev = h;
inst->name = devicename;
inst->opts = new sane_options();
if(reload_options(inst, RELOAD_FOR_DEVICE_OPEN))
{
openning_.push_back(inst);
*handle = hg_sane_middleware::scanner_handle_to_sane(h);
}
else
{
hg_sane_middleware::free_device_instance(inst);
*handle = nullptr;
se = SCANNER_ERR_DATA_DAMAGED;
}
}
return local_utility::scanner_err_2_sane_statu(se);
}
SANE_Status hg_sane_middleware::close_device(SANE_Handle h)
{
LPDEVINST inst = find_openning_device(h, true);
SANE_Status err = SANE_STATUS_GOOD;
if (inst)
{
if(inst->dev)
err = local_utility::scanner_err_2_sane_statu(hg_scanner_close(inst->dev, true));
inst->dev = nullptr;
hg_sane_middleware::free_device_instance(inst);
}
return err;
}
SANE_Status hg_sane_middleware::get_image_parameters(SANE_Handle handle, SANE_Parameters* params)
{
if (!params)
return SANE_STATUS_INVAL;
LPDEVINST inst = find_openning_device(handle);
scanner_err err = SCANNER_ERR_NOT_START;
if(inst && inst->dev)
err = hg_scanner_get_img_info(inst->dev, params, sizeof(*params));
return local_utility::scanner_err_2_sane_statu(err);
}
SANE_Status hg_sane_middleware::start(SANE_Handle h, void* async_event)
{
LPDEVINST dev = find_openning_device(h);
scanner_err err = SCANNER_ERR_INVALID_PARAMETER;
if(dev && dev->dev)
err = hg_scanner_start(dev->dev, async_event, -1);
return local_utility::scanner_err_2_sane_statu(err);
}
SANE_Status hg_sane_middleware::read(SANE_Handle h, void* buf, int* bytes)
{
LPDEVINST dev = find_openning_device(h);
scanner_err err = SCANNER_ERR_INVALID_PARAMETER;
long r = bytes ? *bytes : 0;
if (bytes)
{
if (dev && dev->dev)
{
err = hg_scanner_read_img_data(dev->dev, (unsigned char*)buf, &r);
*bytes = r;
}
else
err = SCANNER_ERR_NOT_OPEN;
}
return local_utility::scanner_err_2_sane_statu(err);
}
SANE_Status hg_sane_middleware::stop(SANE_Handle h)
{
LPDEVINST dev = find_openning_device(h);
scanner_err err = SCANNER_ERR_OK;
if (dev && dev->dev)
err = hg_scanner_stop(dev->dev);
return local_utility::scanner_err_2_sane_statu(err);
}
SANE_Option_Descriptor* hg_sane_middleware::get_option_descriptor(SANE_Handle h, const void* option)
{
SANE_Option_Descriptor* ret = nullptr;
LPDEVINST dev = find_openning_device(h);
int base_ind = 0;
if (dev)
{
base_ind = dev->opts->get_option_count();
ret = dev->opts->get_opt_descriptor(option);
}
else
//if (!ret)
ret = offline_->opts->get_opt_descriptor(option, nullptr, base_ind);
return ret;
}
SANE_Status hg_sane_middleware::control_option(SANE_Handle h, const void* option, SANE_Action action, void* value, SANE_Int* after_do)
{
SANE_Status err = SANE_STATUS_INVAL;
LPDEVINST inst = find_openning_device(h);
if (!inst)
{
if(h)
return local_utility::scanner_err_2_sane_statu(SCANNER_ERR_NOT_OPEN);
inst = offline_;
}
if (action == SANE_ACTION_GET_VALUE)
{
if (!option)
{
*(SANE_Int*)value = inst->opts->get_option_count(); // +offline_->opts->get_option_count();
err = SANE_STATUS_GOOD;
}
else
{
err = get_current_value(inst, option, value, false);
}
}
else if (action == SANE_ACTION_SET_VALUE || action == SANE_ACTION_SET_AUTO)
{
std::string title(""), before(""), after("");
err = set_value(inst, option, value, after_do, action == SANE_ACTION_SET_AUTO, &title, &before, &after);
if (action == SANE_ACTION_SET_AUTO)
{
if(after.empty())
utils::to_log_with_api(hg_scanner_log_is_enable, hg_scanner_log, LOG_LEVEL_DEBUG, "Restore %d - '%s' = %s\n"
, (int)(long)option, title.c_str(), hg_scanner_err_description(local_utility::sane_statu_2_scanner_err(err)));
else
utils::to_log_with_api(hg_scanner_log_is_enable, hg_scanner_log, LOG_LEVEL_DEBUG, "Restore %d - '%s' to '%s' = %s\n"
, (int)(long)option, title.c_str(), after.c_str(), hg_scanner_err_description(local_utility::sane_statu_2_scanner_err(err)));
}
else if(before != after)
utils::to_log_with_api(hg_scanner_log_is_enable, hg_scanner_log, LOG_LEVEL_DEBUG, "set %d - '%s' to '%s'(APPLIED: %s) = %s\n"
, (int)(long)option, title.c_str(), before.c_str(), after.c_str(), hg_scanner_err_description(local_utility::sane_statu_2_scanner_err(err)));
else
utils::to_log_with_api(hg_scanner_log_is_enable, hg_scanner_log, LOG_LEVEL_DEBUG, "set %d - '%s' to '%s' = %s\n"
, (int)(long)option, title.c_str(), before.c_str(), hg_scanner_err_description(local_utility::sane_statu_2_scanner_err(err)));
}
// extension ...
else if (action == SANE_ACTION_GET_DEFAULT_VALUE)
{
err = get_current_value(inst, option, value, true);
}
else if (action == SANE_ACTION_GET_FIX_ID)
{
err = get_option_fixed_id(inst, option, value);
}
else if (action == SANE_ACTION_ENUM_INVISIBLE_FIX_ID)
{
struct _fix_id_cb* fcb = (struct _fix_id_cb*)value;
inst->opts->enum_invisible_fix_ids(fcb);
//if (!inst->opts->enum_invisible_fix_ids(fcb) && inst != offline_)
// offline_->opts->enum_invisible_fix_ids(fcb);
err = SANE_STATUS_GOOD;
}
return err;
}
SANE_Status hg_sane_middleware::ex_io_control(SANE_Handle h, unsigned long code, void* data, unsigned* len)
{
LPDEVINST dev = find_openning_device(h);
int ret = SANE_STATUS_GOOD;
// commented at 2022-03-23 for getting app about info before open any device
//
//if (!handle)
// return SANE_STATUS_INVAL;
ret = hg_scanner_control(dev->dev, code, data, len);
if (ret == SCANNER_ERR_CONFIGURATION_CHANGED)
{
int nc = code;
utils::to_log_with_api(hg_scanner_log_is_enable, hg_scanner_log, LOG_LEVEL_DEBUG, "the setting '0x%08x' affects other options value, RELOAD ...\n", nc);
reload_options(dev, RELOAD_FOR_OPT_CHANGED);
}
return local_utility::scanner_err_2_sane_statu(ret);
}
bool hg_sane_middleware::is_ready(void)
{
return init_ok_;
}