New arhitecture

This commit is contained in:
gb 2023-12-01 17:17:09 +08:00
commit 4d0f93932d
42 changed files with 14339 additions and 0 deletions

375
scanner/async_scanner.cpp Normal file
View File

@ -0,0 +1,375 @@
#include "async_scanner.h"
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <iostream>
#include <fstream>
#include <usb_io.h>
#include <sane_opt_json/device_opt.h>
#include <sane/sane_ex.h>
#include <huagao/hgscanner_error.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
async_scanner::async_scanner() : usb_(nullptr), cfg_mgr_(nullptr), scan_id_(0)
{
utils::init_log(LOG_TYPE_FILE);
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 = [&](bool connected) -> void
{
utils::to_log(LOG_LEVEL_ALL, "USB %s\n", connected ? "connected" : "dis-connected");
// if(!connected)
// stop_scan();
};
auto user = [&](int priv) -> bool
{
return true;
};
auto on_log = [&](const char* msg) -> void
{
utils::log_info(msg, LOG_LEVEL_DEBUG);
};
cfg_mgr_ = new device_option(user, on_log);
init();
usb_ = new async_usb_gadget(bulk_handle, on_connect);
}
async_scanner::~async_scanner()
{
if(usb_)
{
usb_->stop();
usb_->release();
}
if(cfg_mgr_)
{
cfg_mgr_->clear();
delete cfg_mgr_;
}
}
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_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)
{
}
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);
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);
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, "receive file (%u bytes): %s = %d\n", pfi->size, path.c_str(), err);
*required = dynamic_cast<packet_data_base_ptr>(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());
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);
*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
{
((LPPACK_BASE)reply->ptr())->payload_len = sizeof(TXFILE);
((LPTXFILE)((LPPACK_BASE)reply->ptr())->payload)->size = reader->get_rest();
reader->set_packet_param(pack->cmd, pack->pack_id);
}
*required = dynamic_cast<packet_data_base_ptr>(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;
*used = base_head_size;
reply->set_len(base_head_size);
// scan_err_ = capture_->start();
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 = capture_->stop();
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();
}
}

51
scanner/async_scanner.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include <stdint.h>
#include <thread>
#include <functional>
#include <algorithm>
#include <memory>
#include <vector>
#include <base/utils.h>
#include <base/data.h>
class async_usb_gadget;
class device_option;
class image_capture;
class img_processor;
class gb_json;
class async_scanner : public refer
{
async_usb_gadget *usb_;
device_option *cfg_mgr_;
MUTEX locker_;
uint32_t img_cnt_;
uint32_t scan_id_;
uint32_t scan_err_;
volatile bool reply_start_;
dyn_mem_ptr handle_bulk_cmd(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required);
void init(void);
dyn_mem_ptr handle_simple_roger(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required);
dyn_mem_ptr handle_get_opt_value(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required);
dyn_mem_ptr handle_get_opt_all(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required);
dyn_mem_ptr handle_set_opt(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required);
dyn_mem_ptr handle_file_receive(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required);
dyn_mem_ptr handle_file_send(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required);
dyn_mem_ptr handle_scan_start(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required);
dyn_mem_ptr handle_scan_stop(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required);
dyn_mem_ptr handle_process_cmd(LPPACK_BASE pack, uint32_t* used, packet_data_base_ptr* required);
public:
async_scanner();
protected:
~async_scanner();
public:
uint32_t stop(void);
};

45
scanner/main.cpp Normal file
View File

@ -0,0 +1,45 @@
#include <iostream>
#include <iomanip>
#include <memory>
#include <thread>
#include <chrono>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <sys/file.h>
#include <functional>
#include "async_scanner.h"
#define BUF_LEN_FOR_PID 64
static void sigHandler(int sig)
{
// if (sig == SIGINT || sig == SIGTERM)
// remove(MY_PID_FILE);
printf("exit now for signal: %d\n", sig);
_exit(0);
}
int main()
{
/* Ctrl + C */
if (signal(SIGINT, sigHandler) == SIG_ERR)
{
exit(-1);
}
/* kill pid / killall name */
if (signal(SIGTERM, sigHandler) == SIG_ERR)
{
exit(-1);
}
async_scanner *scanner = new async_scanner();
while(1) {std::this_thread::sleep_for(std::chrono::milliseconds(2));}
return 0;
}

12
scanner/xmake.lua Normal file
View File

@ -0,0 +1,12 @@
add_rules("mode.debug", "mode.release")
target("hgscanner")
set_kind("binary")
add_syslinks("pthread", "dl")
add_includedirs("../sdk", "../usb")
add_files("*.cpp", "../sdk/base/*.c*", "../sdk/imgprc/*.c*", "../sdk/json/*.c*", "../sdk/sane_opt_json/*.c*")
--add_deps("gusb", "applog")
--add_rules("utils.bin2c",{linewidth = 32,extension = {".bin"}})
--add_files("table.bin")
add_packages("sdk")
add_deps("usb")

473
sdk/base/data.cpp Normal file
View File

@ -0,0 +1,473 @@
#include "data.h"
#include <string.h>
#if defined(WIN32) || defined(_WIN64)
#else
#include <sys/fcntl.h>
#include <unistd.h>
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
packet_data_base::packet_data_base() : pack_cmd_(0), pack_id_(0)
, progress_notify_(PROGRESS_NOTIFYER()), user_data_(nullptr)
{}
packet_data_base::~packet_data_base()
{}
int packet_data_base::notify_progress(uint64_t total, uint64_t cur_size, uint32_t err)
{
if (progress_notify_)
progress_notify_(total, cur_size, err, user_data_);
else
return ENOENT;
}
void packet_data_base::set_packet_param(uint32_t cmd, uint32_t id)
{
pack_cmd_ = cmd;
pack_id_ = id;
}
int packet_data_base::get_packet_command(void)
{
return pack_cmd_;
}
int packet_data_base::get_packet_id(void)
{
return pack_id_;
}
void packet_data_base::set_progress_notify(PROGRESS_NOTIFYER notify, void* param)
{
progress_notify_ = notify;
user_data_ = param;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
data_holder::data_holder()
{}
data_holder::~data_holder()
{}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
mem_holder::mem_holder(size_t size) : space_(size)
{
buf_ = (uint8_t*)malloc(size);
if (buf_)
memset(buf_, 0, size);
}
mem_holder::~mem_holder()
{
if (buf_)
free(buf_);
buf_ = nullptr;
}
int mem_holder::put_data(const void* data, uint32_t* size)
{
if (*size > space_ - wpos_)
*size = space_ - wpos_;
memcpy(buf_ + wpos_, data, *size);
wpos_ += *size;
return 0;
}
bool mem_holder::is_complete(void)
{
return wpos_ >= space_;
}
uint32_t mem_holder::get_required(void)
{
if (wpos_ >= space_)
return 0;
else
return space_ - wpos_;
}
size_t mem_holder::data_length(void)
{
return wpos_;
}
uint8_t* mem_holder::data(void)
{
return buf_;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
empty_holer::empty_holer(uint64_t size) : size_(size), put_(0)
{}
empty_holer::~empty_holer()
{}
int empty_holer::put_data(const void* data, uint32_t* size)
{
if (*size >= size_ - put_)
{
*size -= size_ - put_;
put_ = size_;
}
else
{
put_ += *size;
}
notify_progress(size_, put_, 0);
return 0;
}
bool empty_holer::is_complete(void)
{
return size_ == put_;
}
uint32_t empty_holer::get_required(void)
{
return size_ - put_;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
file_saver::file_saver(void) : size_(0), wrote_(0), path_(""), check_(""), dst_(nullptr), pack_cmd_(0), pack_id_(0)
{}
file_saver::~file_saver()
{
close();
}
void file_saver::close(void)
{
if(dst_)
fclose(dst_);
dst_ = nullptr;
size_ = wrote_ = pack_cmd_ = pack_id_ = 0;
path_ = check_ = "";
}
int file_saver::open(const char* path, uint64_t size, const char* check)
{
int err = 0;
close();
dst_ = fopen(path, "wb");
if(dst_)
{
unsigned long long space = 0;
err = utils::get_disk_space(path, nullptr, &space, nullptr);
if (err || space < size * 1.5)
{
fclose(dst_);
dst_ = nullptr;
remove(path);
if (err == 0)
err = ENOSPC;
}
else
{
path_ = path;
size_ = size;
check_ = check ? check : "";
}
}
else
{
err = errno;
}
return err;
}
int file_saver::put_data(const void* data, uint32_t* size/*[in] - total bytes of data; [out] - used bytes*/)
{
if(!dst_)
return ENOENT;
int w = *size > size_ - wrote_ ? size_ - wrote_ : *size,
real_w = fwrite(data, 1, w, dst_), // should handle error here !
err = 0;
*size = real_w;
wrote_ += real_w;
if(wrote_ >= size_)
{
fclose(dst_);
dst_ = nullptr;
}
else if (real_w < w) // what happens ?
{
err = ferror(dst_);
}
notify_progress(size_, wrote_, err);
return 0;
}
bool file_saver::is_complete(void)
{
return wrote_ >= size_;
}
uint32_t file_saver::get_required(void)
{
return size_ - wrote_;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
data_source::data_source()
{}
data_source::~data_source()
{}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// dyn_mem
uint64_t dyn_mem::mem_used_bytes_ = 0;
MUTEX dyn_mem::mem_lock_;
dyn_mem::dyn_mem(size_t size) : buf_(nullptr), len_(0), space_(ALIGN_TO(size, 16))
{
buf_ = (uint8_t*)malloc(space_);
if (buf_)
{
SIMPLE_LOCK(dyn_mem::mem_lock_);
dyn_mem::mem_used_bytes_ += space_;
memset(buf_, 0, space_);
}
}
dyn_mem::dyn_mem(void* buf, size_t size) : buf_((uint8_t*)buf), space_(size), len_(size)
{
}
dyn_mem::~dyn_mem()
{
if (buf_)
{
free(buf_);
{
SIMPLE_LOCK(dyn_mem::mem_lock_);
dyn_mem::mem_used_bytes_ -= space_;
}
}
}
uint64_t dyn_mem::mem_used(void)
{
return dyn_mem::mem_used_bytes_;
}
dyn_mem_ptr dyn_mem::memory(size_t size)
{
return new dyn_mem(size);
}
uint32_t dyn_mem::space(void)
{
return space_;
}
bool dyn_mem::set_len(size_t len)
{
if (len > space_)
return false;
len_ = len;
return true;
}
int dyn_mem::put(const void* data, int len)
{
if (len + len_ > space_)
len = space_ - len;
if (len > 0)
{
memcpy(buf_ + len_, data, len);
len_ += len;
}
else
len = 0;
return len;
}
void* dyn_mem::detach(size_t* size)
{
void* buf = buf_;
if (size)
*size = space_;
space_ = len_ = 0;
buf_ = nullptr;
return buf;
}
size_t dyn_mem::used(size_t len)
{
if (len >= len_)
{
len_ = 0;
}
else if (len)
{
memcpy(buf_, buf_ + len, len_ - len);
len_ -= len;
}
return len_;
}
dyn_mem& dyn_mem::operator+=(dyn_mem& r)
{
if (len_ + r.get_rest() > space_)
{
size_t size = ALIGN_TO(len_ + r.get_rest(), 16);
uint8_t* buf = (uint8_t*)malloc(size);
memcpy(buf, buf_, len_);
free(buf_);
buf_ = buf;
{
SIMPLE_LOCK(dyn_mem::mem_lock_);
dyn_mem::mem_used_bytes_ += size - space_;
}
space_ = size;
}
memcpy(buf_ + len_, r.buf_, r.get_rest());
len_ += r.get_rest();
return *this;
}
bool dyn_mem::is_memory_block(void)
{
return true;
}
uint32_t dyn_mem::get_rest(void)
{
return len_;
}
// following API valid when is_memory_block() return true
uint8_t* dyn_mem::ptr(void)
{
return buf_;
}
// following API valid when is_memory_block() return false
int dyn_mem::fetch_data(void* buf, uint32_t* size)
{
if (*size >= len_)
{
memcpy(buf, buf_, len_);
*size = len_;
len_ = 0;
}
else
{
memcpy(buf, buf_, *size);
used(*size);
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
file_reader::file_reader() : len_(0), src_(nullptr), path_(""), consume_(0)
{}
file_reader::~file_reader()
{
if(src_)
fclose(src_);
}
int file_reader::open(const char* file)
{
if(src_)
fclose(src_);
src_ = fopen(file, "rb");
if(!src_)
return errno;
FSEEK(src_, 0, SEEK_END);
len_ = FTELL(src_);
FSEEK(src_, 0, SEEK_SET);
path_ = file;
consume_ = 0;
return 0;
}
int file_reader::attach(FILE* f)
{
if (src_)
{
fclose(src_);
src_ = nullptr;
}
uint64_t cur = FTELL(f);
FSEEK(f, 0, SEEK_END);
len_ = FTELL(f);
FSEEK(f, cur, SEEK_SET);
if (len_ <= cur)
return EINVAL;
src_ = f;
len_ -= cur;
consume_ = 0;
return 0;
}
FILE* file_reader::detach(void)
{
FILE* ret = src_;
src_ = nullptr;
len_ = 0;
path_ = "";
return ret;
}
bool file_reader::is_memory_block(void)
{
return false;
}
uint32_t file_reader::get_rest(void)
{
return len_ - consume_;
}
// following API valid when is_memory_block() return true
uint8_t* file_reader::ptr(void)
{
return nullptr;
}
// following API valid when is_memory_block() return false
int file_reader::fetch_data(void* buf, uint32_t* size)
{
if (!src_)
return ENODATA;
size_t r = fread(buf, 1, *size, src_); // fix me if ERROR occurs !!!
consume_ += r;
*size = r;
if (consume_ >= len_)
{
fclose(src_);
src_ = nullptr;
}
notify_progress(len_, consume_, 0);
return 0;
}

260
sdk/base/data.h Normal file
View File

@ -0,0 +1,260 @@
#pragma once
// Objects IO
//
// created on 2023-03-10
#include "utils.h"
#include "packet.h"
#include <string>
#include <functional>
#define CLS_PTR(cls) typedef cls* cls##_ptr;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
/* packet parameter keeper, parameter of corresponding packet
*/
#define PROGRESS_NOTIFYER std::function<int(uint64_t/*total*/, uint64_t/*cur-size*/, uint32_t/*err*/, void* /*user data*/)>
class packet_data_base : public refer
{
PROGRESS_NOTIFYER progress_notify_;
void* user_data_;
protected:
uint32_t pack_cmd_;
uint32_t pack_id_;
public:
packet_data_base();
protected:
virtual ~packet_data_base();
int notify_progress(uint64_t total, uint64_t cur_size, uint32_t err);
public:
void set_packet_param(uint32_t cmd, uint32_t id);
int get_packet_command(void);
int get_packet_id(void);
void set_progress_notify(PROGRESS_NOTIFYER notify = PROGRESS_NOTIFYER(), void* param = nullptr);
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
/* data_holder, used when data is also required for a certain packet
*/
class data_holder : public packet_data_base
{
public:
data_holder();
protected:
virtual ~data_holder();
public:
virtual int put_data(const void* data, uint32_t* size/*[in] - total bytes of data; [out] - used bytes*/) = 0; // return error code
virtual bool is_complete(void) = 0;
virtual uint32_t get_required(void) = 0;
};
class mem_holder : public data_holder
{
uint8_t* buf_ = nullptr;
size_t space_ = 0;
size_t wpos_ = 0;
public:
mem_holder(size_t size);
protected:
~mem_holder();
// data_holder
public:
virtual int put_data(const void* data, uint32_t* size/*[in] - total bytes of data; [out] - used bytes*/) override; // return error code
virtual bool is_complete(void) override;
virtual uint32_t get_required(void) override;
public:
size_t data_length(void);
uint8_t* data(void);
};
class empty_holer : public data_holder
{
uint64_t size_;
uint64_t put_;
public:
empty_holer(uint64_t size);
protected:
~empty_holer();
public:
virtual int put_data(const void* data, uint32_t* size/*[in] - total bytes of data; [out] - used bytes*/) override; // return error code
virtual bool is_complete(void) override;
virtual uint32_t get_required(void) override;
};
class file_saver : public data_holder
{
uint64_t size_;
uint64_t wrote_;
std::string path_;
std::string check_;
FILE *dst_;
uint32_t pack_cmd_;
uint32_t pack_id_;
void close(void);
public:
file_saver(void);
protected:
~file_saver();
public:
int open(const char* path, uint64_t size, const char* check = nullptr);
public:
virtual int put_data(const void* data, uint32_t* size/*[in] - total bytes of data; [out] - used bytes*/) override;
virtual bool is_complete(void) override;
virtual uint32_t get_required(void) override;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
/* data_source, can be a memory block or STREAM object
*/
class data_source : public packet_data_base
{
uint32_t pack_cmd_;
uint32_t pack_id_;
public:
data_source();
protected:
virtual ~data_source();
public:
virtual bool is_memory_block(void) = 0;
virtual uint32_t get_rest(void) = 0;
// following API valid when is_memory_block() return true
virtual uint8_t* ptr(void) = 0;
// following API valid when is_memory_block() return false. return error code
virtual int fetch_data(void* buf, uint32_t* size) = 0;
};
class dyn_mem : public data_source
{
uint8_t* buf_; // data buf
size_t space_; // occupy space in bytes
size_t len_; // data length in bytes
static MUTEX mem_lock_;
static uint64_t mem_used_bytes_;
public:
dyn_mem(size_t size);
dyn_mem(void* buf, size_t size);
static uint64_t mem_used(void);
static dyn_mem* memory(size_t size);
protected:
~dyn_mem();
public:
uint32_t space(void);
bool set_len(size_t len);
int put(const void* data, int len);
void* detach(size_t* size); // for constructed from dyn_mem(void* buf, size_t size)
size_t used(size_t len); // used len bytes content, move following data to head and set data length, return rest data length
dyn_mem& operator+=(dyn_mem& r);
// data_source
public:
virtual bool is_memory_block(void) override;
virtual uint32_t get_rest(void) override;
// following API valid when is_memory_block() return true
virtual uint8_t* ptr(void) override;
// following API valid when is_memory_block() return false
virtual int fetch_data(void* buf, uint32_t* size) override;
};
class file_reader : public data_source
{
size_t len_;
size_t consume_;
FILE *src_;
std::string path_;
public:
file_reader();
protected:
~file_reader();
public:
int open(const char* file);
int attach(FILE* f);
FILE* detach(void);
public:
virtual bool is_memory_block(void) override;
virtual uint32_t get_rest(void) override;
// following API valid when is_memory_block() return true
virtual uint8_t* ptr(void) override;
// following API valid when is_memory_block() return false
virtual int fetch_data(void* buf, uint32_t* size) override;
};
CLS_PTR(packet_data_base);
CLS_PTR(data_holder);
CLS_PTR(mem_holder);
CLS_PTR(data_source);
CLS_PTR(dyn_mem);
CLS_PTR(file_reader);
// callback proto
//
// parameters: usb_functionfs_event* - the function event ptr
//
// dyn_mem_ptr - the packet buffer, read-only
//
// uint32_t* - to return how many data in bytes the handler consumed, the most high bit is to indicate whether should notify the returned packet has sent
//
// normally, the value should be sizeof(PACK_BASE) + PACK_BASE::payload_len, i.e. the handler consume all data of an entire packet
//
// when invalid packet, suggest use the entire data
//
// packet_data_base_ptr* - return data_holder or data_source or nullptr £¨The number of bytes required for this packet, 0 is over for this packet£©
//
// data_holder: the packet/command need more data than dyn_mem_ptr provides to complete the business. such as 'write a large file'
//
// data_source: the reply content may be a large data (a large file content)
//
// return value of all routines is the reply packet, nullptr if the packet need not reply
//
// NOTE: when parameter uint32_t* and packet_data_base_ptr* both are nullptr, it is notifying the command reply packet has sent, callback should return nullptr only
//
#define FUNCTION_PROTO_PARAMETERS dyn_mem_ptr, uint32_t*, packet_data_base_ptr*
#define FUNCTION_PROTO_COMMAND_HANDLE dyn_mem_ptr(FUNCTION_PROTO_PARAMETERS)

133
sdk/base/encrypt.cpp Normal file
View File

@ -0,0 +1,133 @@
#include "encrypt.h"
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// exporting api:
dyn_mem_ptr packet_encrypt(dyn_mem_ptr packet, uint32_t cmd_type, uint32_t type, uint8_t enc_data)
{
dyn_mem_ptr ret = packet;
LPPACK_BASE pack = (LPPACK_BASE)packet->ptr();
ret->add_ref();
if (cmd_type == ENCRYPT_CMD_XOR_PID)
pack->cmd ^= pack->pack_id;
else if (cmd_type == ENCRYPT_CMD_ADD_PID)
pack->cmd += pack->pack_id;
else if (cmd_type == ENCRYPT_CMD_SUB_PID)
pack->cmd -= pack->pack_id;
pack->enc_cmd = cmd_type;
if (type != ENCRYPT_NONE)
{
std::string cont("");
if (type == ENCRYPT_BASE64)
{
}
else if (type == ENCRYPT_AES)
{
}
else if (type == ENCRYPT_ZIP)
{
}
if (cont.length())
{
// space is enough ?
if (cont.length() + sizeof(PACK_BASE) > packet->space())
{
ret->release();
ret = dyn_mem::memory(sizeof(PACK_BASE) + cont.length());
memcpy(ret->ptr(), packet->ptr(), sizeof(PACK_BASE));
pack = (LPPACK_BASE)ret->ptr();
}
// copy cipher text and set encrypt type ...
memcpy(pack->payload, cont.c_str(), cont.length());
ret->set_len(sizeof(PACK_BASE) + cont.length());
pack->payload_len = cont.length();
pack->encrypt = type;
pack->enc_data = enc_data;
}
}
return ret;
}
dyn_mem_ptr packet_decrypt(dyn_mem_ptr packet)
{
dyn_mem_ptr ret = packet;
LPPACK_BASE pack = (LPPACK_BASE)packet->ptr();
ret->add_ref();
if (pack->enc_cmd == ENCRYPT_CMD_XOR_PID)
pack->cmd ^= pack->pack_id;
else if (pack->enc_cmd == ENCRYPT_CMD_ADD_PID)
pack->cmd -= pack->pack_id;
else if (pack->enc_cmd == ENCRYPT_CMD_SUB_PID)
pack->cmd += pack->pack_id;
if (pack->encrypt != ENCRYPT_NONE && pack->payload_len && packet->get_rest() >= sizeof(PACK_BASE) + pack->payload_len)
{
std::string cont("");
if (pack->encrypt == ENCRYPT_BASE64)
{
}
else if (pack->encrypt == ENCRYPT_AES)
{
}
else if (pack->encrypt == ENCRYPT_ZIP)
{
}
if (cont.length())
{
// have out-packet data ?
if (packet->get_rest() > sizeof(PACK_BASE) + pack->payload_len)
cont += std::string(pack->payload + pack->payload_len, packet->get_rest() - (sizeof(PACK_BASE) + pack->payload_len));
// space is enough ?
if (cont.length() + sizeof(PACK_BASE) > packet->space())
{
ret->release();
ret = dyn_mem::memory(sizeof(PACK_BASE) + cont.length());
memcpy(ret->ptr(), packet->ptr(), sizeof(PACK_BASE));
pack = (LPPACK_BASE)ret->ptr();
}
// copy plain text and set encrypt type to none ...
memcpy(pack->payload, cont.c_str(), cont.length());
ret->set_len(sizeof(PACK_BASE) + cont.length());
pack->encrypt = ENCRYPT_NONE;
pack->payload_len = cont.length();
}
}
return ret;
}

45
sdk/base/encrypt.h Normal file
View File

@ -0,0 +1,45 @@
#pragma once
// Objects for encrypting/decrypting
//
// created on 2023-04-04
#include "data.h"
enum encryptor
{
ENCRYPT_NONE = 0,
ENCRYPT_BASE64,
ENCRYPT_AES,
ENCRYPT_ZIP,
};
enum encrypt_cmd
{
ENCRYPT_CMD_NONE = 0,
ENCRYPT_CMD_XOR_PID, // cmd ^= pack_id
ENCRYPT_CMD_ADD_PID, // cmd += pack_id
ENCRYPT_CMD_SUB_PID, // cmd -= pack_id
};
// Function: encrypting & decrypting packet
//
// Parameters: packet - pointer to the base packet
//
// data - cipher/plain data
//
// size - [in]: bytes of source 'data';
//
// cmd_type - member 'cmd' encrypting method
//
// type - payload encrypting method
//
// enc_data - encrypting data for payload encrypting method
//
// Return: 'cmd' returned on origin packet, and returning value is for payload only. nullptr on failure
//
// NOTE: nullptr also returned if data was nullptr or size was ZERO
//
dyn_mem_ptr packet_encrypt(dyn_mem_ptr packet, uint32_t cmd_type = ENCRYPT_CMD_NONE, uint32_t type = ENCRYPT_NONE, uint8_t enc_data = 0);
dyn_mem_ptr packet_decrypt(dyn_mem_ptr packet);

243
sdk/base/ini_file.cpp Normal file
View File

@ -0,0 +1,243 @@
#include "ini_file.h"
#include <stdlib.h>
#include <string.h>
#if defined(WIN32) || defined(_WIN64)
#define bzero(b, s) memset(b, 0, s)
#endif
simple_ini::simple_ini()
{}
simple_ini::~simple_ini()
{}
std::string simple_ini::temporary_path(void)
{
#if defined(WIN32) || defined(_WIN64)
char path[MAX_PATH] = { 0 };
if (GetTempPathA(_countof(path) - 1, path))
{
if (path[lstrlenA(path) - 1] == '\\')
path[lstrlenA(path) - 1] = 0;
}
return path;
#else
return "/tmp";
#endif
}
bool simple_ini::skip_empty(char*& ptr)
{
char* p = ptr;
while(*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n')
ptr++;
return ptr > p;
}
void simple_ini::trime(char*& ptr)
{
skip_empty(ptr);
char* tail = ptr + strlen(ptr) - 1;
while (tail >= ptr)
{
if (*tail != ' ' && *tail != '\t' && *tail != '\r' && *tail != '\n')
break;
tail--;
}
tail[1] = 0;
}
int simple_ini::load(const char* local_file)
{
int ret = 0;
#if defined(WIN32) || defined(_WIN64)
file_ = local_file;
#else
values_.clear();
FILE* src = fopen(local_file, "rb");
if (src)
{
char line[256];
char* oper = NULL;
SECKEY sec;
bzero(line, sizeof(line));
while (fgets(line, sizeof(line) - 1, src))
{
oper = line;
simple_ini::skip_empty(oper);
if (strstr(oper, "//") == oper ||
strstr(oper, "#") == oper ||
*oper == 0)
{
bzero(line, sizeof(line));
continue;
}
if (*oper == '[')
{
oper++;
if (strstr(oper, (char*)"]"))
strstr(oper, (char*)"]")[0] = 0;
if (sec.sec != oper)
{
if (sec.vals.size())
values_.push_back(sec);
sec.vals.clear();
sec.sec = oper;
std::vector<SECKEY>::iterator it = std::find(values_.begin(), values_.end(), oper);
if (it != values_.end())
{
sec = *it;
values_.erase(it);
}
}
}
else
{
char* v = strstr(oper, "=");
if (v)
{
*v++ = 0;
trime(oper);
if (*oper)
{
trime(v);
KEYVAL kv;
kv.key = oper;
kv.val = v;
sec.vals.push_back(kv);
std::sort(sec.vals.begin(), sec.vals.end());
}
}
}
bzero(line, sizeof(line));
}
fclose(src);
if (sec.vals.size())
values_.push_back(sec);
std::sort(values_.begin(), values_.end());
}
else
ret = errno;
#endif
return ret;
}
#if !defined(WIN32) && !defined(_WIN64)
int simple_ini::save(const char* local_file)
{
FILE* dst = fopen(local_file, "wb");
if (dst)
{
for (size_t i = 0; i < values_.size(); ++i)
{
std::string val(values_[i].sec);
val.insert(0, "[");
val += "]\r\n";
fwrite(val.c_str(), 1, val.length(), dst);
for (size_t j = 0; j < values_[i].vals.size(); ++j)
{
val = values_[i].vals[j].key + "=" + values_[i].vals[j].val + "\r\n";
fwrite(val.c_str(), 1, val.length(), dst);
}
val = "\r\n";
fwrite(val.c_str(), 1, val.length(), dst);
}
fclose(dst);
}
else
return errno;
}
#endif
std::string simple_ini::get(const char* sec, const char* key, const char* default_val)
{
#if defined(WIN32) || defined(_WIN64)
char str[MAX_PATH] = { 0 };
GetPrivateProfileStringA(sec, key, default_val, str, _countof(str), file_.c_str());
return str;
#else
std::vector<SECKEY>::iterator it = std::find(values_.begin(), values_.end(), sec);
if (it == values_.end())
return default_val;
std::vector<KEYVAL>::iterator it1 = std::find(it->vals.begin(), it->vals.end(), key);
if (it1 == it->vals.end())
return default_val;
else
return it1->val;
#endif
}
void simple_ini::set(const char* sec, const char* key, const char* val)
{
#if defined(WIN32) || defined(_WIN64)
WritePrivateProfileStringA(sec, key, val, file_.c_str());
#else
std::vector<SECKEY>::iterator it = std::find(values_.begin(), values_.end(), sec);
if (it == values_.end())
{
KEYVAL kv;
SECKEY sk;
kv.key = key;
kv.val = val;
sk.sec = sec;
sk.vals.push_back(kv);
values_.push_back(sk);
std::sort(values_.begin(), values_.end());
return;
}
std::vector<KEYVAL>::iterator it1 = std::find(it->vals.begin(), it->vals.end(), key);
if (it1 == it->vals.end())
{
KEYVAL kv;
kv.key = key;
kv.val = val;
it->vals.push_back(kv);
std::sort(it->vals.begin(), it->vals.end());
}
else
it1->val = val;
#endif
}
void simple_ini::remove(const char* sec, const char* key)
{
#if defined(WIN32) || defined(_WIN64)
#else
std::vector<SECKEY>::iterator it = std::find(values_.begin(), values_.end(), sec);
if (it == values_.end())
return;
std::vector<KEYVAL>::iterator it1 = std::find(it->vals.begin(), it->vals.end(), key);
if (it1 != it->vals.end())
it->vals.erase(it1);
#endif
}
void simple_ini::remove(const char* sec)
{
#if defined(WIN32) || defined(_WIN64)
#else
std::vector<SECKEY>::iterator it = std::find(values_.begin(), values_.end(), sec);
if (it != values_.end())
values_.erase(it);
#endif
}
void simple_ini::clear(void)
{
values_.clear();
}

68
sdk/base/ini_file.h Normal file
View File

@ -0,0 +1,68 @@
#pragma once
#if defined(WIN32) || defined(_WIN64)
#include <Windows.h>
#else
#include <string.h>
#define stricmp strcasecmp
#endif
#include <vector>
#include <string>
#include <algorithm>
class simple_ini
{
typedef struct _key_val
{
std::string key;
std::string val;
bool operator==(const char* k)
{
return key == k;
}
bool operator<(const struct _key_val& r)
{
return key.compare(r.key) < 0;
}
}KEYVAL;
typedef struct _sec_key
{
std::string sec;
std::vector<KEYVAL> vals;
bool operator==(const char* s)
{
return sec == s;
}
bool operator<(const struct _sec_key& r)
{
return sec.compare(r.sec) < 0;
}
}SECKEY;
std::vector<SECKEY> values_;
#if defined(WIN32) || defined(_WIN64)
std::string file_;
#endif
public:
simple_ini();
~simple_ini();
static std::string temporary_path(void);
static bool skip_empty(char*& ptr);
static void trime(char*& ptr);
public:
int load(const char* local_file);
#if !defined(WIN32) && !defined(_WIN64)
int save(const char* local_file);
#endif
std::string get(const char* sec, const char* key, const char* default_val = "");
void set(const char* sec, const char* key, const char* val);
void remove(const char* sec, const char* key);
void remove(const char* sec);
void clear(void);
};

379
sdk/base/packet.h Normal file
View File

@ -0,0 +1,379 @@
#pragma once
// packet structures and command
//
// created on 2022-12-06
//
#if !defined(WIN32)
#include <bits/stdint-uintn.h>
#endif
#include <string.h>
#define TEMPORARY_API
///////////////////////////////////////////////////////////////////////////////
// definitions ...
#define CONFIG_NAME_MAX_LEN 32 // max bytes of configuration name
#define FLOAT_PRECISION .000001f
#define IS_FLOAT_EQUAL(x, y) (-FLOAT_PRECISION <= (x) - (y) && (x) - (y) <= FLOAT_PRECISION)
#define MAKE_WORD(b0, b1) (((b0) & 0xff) | (((b1) << 8) & 0x0ff00))
#define MAKE_STR(str) #str
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define ROGER(cmd) cmd##_ROGER
#define PAIR_COMMAND(cmd) \
cmd, \
cmd##_ROGER
#define RETURN_STR_ENUM(v, e) \
if(v == e) \
return #e;
#define STRUCT_CONSTRUCTOR(st_name) \
st_name() \
{ \
memset(this, 0, sizeof(st_name));\
}
// protocol version, The first thing to do after connecting is to check whether the field is compatible !!!
#define PROTOCOL_VER MAKE_WORD(0, 1)
// NOTE: All text transmitted by pack cmd is in UTF-8 format !!!
enum ep0_req
{
USB_REQ_EP0_GET_PROTO_VER = 100, // get protocol version (PROTOCOL_VER), req = me, ind = 0, val = 0, len = 2
USB_REQ_EP0_GET_STATUS, // 获取各工作线程状态, return EP0REPLYSTATUS. req = me, ind = 0, val = 0, len = sizeof(EP0REPLYSTATUS)
USB_REQ_EP0_RESET_BULK, // 关闭并重新打开BULK端点, return error number (uint32_t). req = me, ind = 0, val = 0, len = sizeof(uint32_t)
USB_REQ_EP0_CANCEL_CMD, // 取消当前指令的继续执行(一般用于中止大数据的传输). req = me, ind = 0, val = 0, len = sizeof(uint32_t) * 2 [(uint32_t)cmd + (uint32_t)pack-id]
USB_REQ_EP0_SET_ENCRYPT, // 设置加密方式, req = me, ind = 0, val = 0, len = sizeof(PACK_BASE)
USB_REQ_EP0_SET_BULK_BUFFER, // 设置bulk缓冲区大小系数 req = me, ind = coef, val = 0, len = 0
};
enum bulk_status
{
BULK_STATUS_NOT_START = 0, // has not initialized
BULK_STATUS_IDLE, // wait IO
BULK_STATUS_IO, // in reading or writing
BULK_STATUS_ERROR, // error occurs
BULK_STATUS_RESET, // in reset(close and reopen) process
};
enum packet_cmd
{
PACK_CMD_NULL,
PAIR_COMMAND(PACK_CMD_HEART_BEAT), // notify peers you are still alive, receiver should reply the same pack
PAIR_COMMAND(PACK_CMD_INVALID), // reply when received an invalid packet
PAIR_COMMAND(PACK_CMD_SYNC),
// attributes get/set, all content in PACK_BASE::payload should be in JSON style - all readonly attributes move to readonly SANE-options on 2023-03-20
//PACK_CMD_ATTR_SYS_VER_GET = 10, // get system version on device, [in]: PACK_BASE, [out] PACK_BASE::payload - {"os":"linux", "ver":"4.4.194", ...}
//PACK_CMD_ATTR_FIRMWARE_VER_GET, // get firmware version, [in]: PACK_BASE, [out] PACK_BASE::payload - {"firmware":"G2393A1234", "CIS":"CIS-123", ...}
//PACK_CMD_ATTR_SERIAL_NUM_GET, // get device serial num, [in]: PACK_BASE, [out] PACK_BASE::payload - {"serial":"20221206001"}
//PACK_CMD_ATTR_SERIAL_NUM_SET, // set device serial num, [in]: PACK_BASE::payload - {"serial":"20221206001"}, [out] PACK_BASE
//PACK_CMD_ATTR_MAC_GET, // get mac address, [in]: PACK_BASE, [out] PACK_BASE::payload - {"mac":"12:34:56:78:9a:bc"}
//PACK_CMD_ATTR_IP_GET, // get ip address, [in]: PACK_BASE, [out] PACK_BASE::payload - {"ipv4":"192.168.1.123", "ipv6":"::1"}
//PACK_CMD_ATTR_HARDWARE_INFO_GET, // get hardwares information on device, [in]: PACK_BASE, [out] PACK_BASE::payload - {"CPU":"ARM x86", "mem":"16GB", ...}
//PACK_CMD_ATTR_HISTORY_COUNT_GET, // get history count, [in]: PACK_BASE, [out] PACK_BASE::payload - {"history-count":12345, ...}
//PACK_CMD_ATTR_ROLLER_COUNT_GET, // get roller count, [in]: PACK_BASE, [out] PACK_BASE::payload - {"roller-count":2345}
//PACK_CMD_ATTR_ROLLER_COUNT_SET, // set roller count, [in]: PACK_BASE::payload - {"roller-count":2345}, [out] PACK_BASE
// configuration get/set
PACK_CMD_STATUS_ROGER = 100, // device -> host. PACK_BASE::result -> status
PAIR_COMMAND(PACK_CMD_SETTING_GET), // get all settings supported by the device, [in]: PACK_BASE, [out]: PACK_BASE::payload - configuration JSON, see SANE-configuration format
PAIR_COMMAND(PACK_CMD_SETTING_GET_CUR), // get current value of given setting, [in]: PACK_BASE::payload - (char*)name, [out]: PACK_BASE::payload - LPCFGVAL
PAIR_COMMAND(PACK_CMD_SETTING_SET), // set value of given setting, [in]: PACK_BASE::payload - LPCFGVAL, [out]: PACK_BASE::payload - LPCFGVAL
PAIR_COMMAND(PACK_CMD_SETTING_RESTORE), // restore given settings, [in]: PACK_BASE::payload - LPCFGVAL, [out]: PACK_BASE::payload - LPCFGVAL
// scan command
PACK_CMD_SCAN_BASE = 200,
PAIR_COMMAND(PACK_CMD_SCAN_START), // start scanning, [in]: PACK_BASE, [out]: PACK_BASE
PAIR_COMMAND(PACK_CMD_SCAN_IMG), // device -> host, PACK_BASE::payload - LPPACKIMAGE
PAIR_COMMAND(PACK_CMD_SCAN_PAPER), // device -> host, ONE paper has passed through the CIS. PACK_BASE::data - index of this paper
PACK_CMD_SCAN_FINISHED_ROGER, // device -> host, PACK_BASE::data is scanner_status
PAIR_COMMAND(PACK_CMD_SCAN_STOP), // stop scanning, [in]: PACK_BASE, [out]: PACK_BASE
//PAIR_COMMAND(PACK_CMD_SCAN_IMAGE_REQ), // get image request, [in]: PACK_BASE, [out] PACK_BASE on error, or PACK_BASE::payload - LPPACKIMAGE
//PAIR_COMMAND(PACK_CMD_SCAN_STATUS), // get scanner status, [in]: PACK_BASE, [out] PACK_BASE::result is status code
// file operation
PACK_CMD_FILE_BASE = 300,
//PAIR_COMMAND(PACK_CMD_FILE_QUERY), // query file information, [in]: PACK_BASE::payload - (char*)file-path, [out] PACK_BASE::payload - LPFILEINFO
PAIR_COMMAND(PACK_CMD_FILE_READ_REQ), // read file content, [in]: PACK_BASE::payload - LPTXFILE, [out] PACK_BASE::payload - LPTXFILE
PAIR_COMMAND(PACK_CMD_FILE_WRITE_REQ), // write a file, [in]: PACK_BASE::payload - LPTXFILE, [out] PACK_BASE
PAIR_COMMAND(PACK_CMD_FILE_MOVE), // move/rename a file, [in]: PACK_BASE::payload - src\0dst\0\0, [out] PACK_BASE
PAIR_COMMAND(PACK_CMD_FILE_REMOVE), // delete a file, [in]: PACK_BASE::payload - (char*)file-path, [out] PACK_BASE
// process operation
PACK_CMD_PROCESS_BASE = 400,
PAIR_COMMAND(PACK_CMD_PROCESS_START), // start a program [in]: PACK_BASE::payload - (char*)pe\0param\0\0, [out]: PACK_BASE::payload - (uint64_t)process-id on success or PACK_BASE on failure
PAIR_COMMAND(PACK_CMD_PROCESS_STOP), // kill a process [in]: PACK_BASE::payload - (char*)process-id, [out]: PACK_BASE
PAIR_COMMAND(PACK_CMD_PROCESS_REBOOT), // reboot system, [in]: PACK_BASE, [out]: PACK_BASE
//PAIR_COMMAND(PACK_CMD_PROCESS_EXEC_RESULT), // get result of a command, [in]: PACK_BASE::payload - (char*)command string, [out]: PACK_BASE::payload - (char*)execute result. popen(), fgets ...
//PAIR_COMMAND(PACK_CMD_PROCESS_QUERY), // query process information [in]: PACK_BASE::payload - (char*)process-id(-1 for all), [out]: LPPROCINFO
PACK_CMD_TOKEN_GET = 900, // Obtain the token of the required command, [in] PACK_BASE, [out] - PACK_BASE::payload - LPOPERTOKEN
};
enum img_cb_type
{
IMG_CB_IMAGE = 0,
IMG_CB_STATUS,
IMG_CB_STOPPED,
};
enum scanner_status
{
SCANNER_STATUS_READY = 0x10000, // status beginning, avoiding conficts with standards/system error code
SCANNER_STATUS_NOT_OPEN,
SCANNER_STATUS_LOST_CONNECT,
SCANNER_STATUS_RESET_BULK,
SCANNER_STATUS_START_SCANNING, // start ok, but scanning-thread not working
SCANNER_STATUS_SCANNING, // start ok, and scanning-thread is working
SCANNER_STATUS_SCAN_FINISHED, // not a persistance status
SCANNER_STATUS_BUSY, // doing task exclude scanning
SCANNER_STATUS_COVER_OPENNED,
SCANNER_STATUS_COVER_CLOSED,
SCANNER_STATUS_SLEEPING,
SCANNER_STATUS_WAKED_UP,
SCANNER_STATUS_COUNT_MODE,
SCANNER_STATUS_DOUBLE_FEEDED,
SCANNER_STATUS_PAPER_JAMMED,
SCANNER_STATUS_PAPER_ASKEW,
SCANNER_STATUS_FEED_FAILED,
SCANNER_STATUS_NO_PAPER,
SCANNER_STATUS_PAPER_ON,
SCANNER_STATUS_STAPLE_ON,
SCANNER_STATUS_SIZE_ERR,
SCANNER_STATUS_DOGEAR,
SCANNER_STATUS_CFG_CHANGED, // PACK_BASE::payload - LPCFGVAL
};
// option affection if value changed, see SANE_INFO_xxx
enum opt_affect
{
OPT_AFFECT_NONE = 0,
OPT_AFFECT_INEXACT = 1,
OPT_AFFECT_OTHERS = 2,
OPT_AFFECT_IMG_PARAM = 4,
};
enum img_format
{
IMG_FMT_UNKNOWN = 0, // unknown format
IMG_FMT_TIFF,
IMG_FMT_BMP,
IMG_FMT_JPEG,
IMG_FMT_PNG,
IMG_FMT_SVG,
IMG_FMT_WEBP,
IMG_FMT_GIF,
};
enum img_compression
{
IMG_COMPRESSION_NONE = 0,
IMG_COMPRESSION_GROUP4,
IMG_COMPRESSION_RLE4,
IMG_COMPRESSION_RLE8,
IMG_COMPRESSION_LZW,
IMG_COMPRESSION_ZIP,
};
enum img_status
{
IMG_STATUS_OK = 0, // normal
IMG_STATUS_DOUBLE = 1 << 0, // double-feeded paper
IMG_STATUS_JAM = 1 << 1, // jammed paper
IMG_STATUS_STAPLE = 1 << 2, // staples on the paper
IMG_STATUS_SIZE_ERR = 1 << 3, // size check failed
IMG_STATUS_DOGEAR = 1 << 4, // paper has dogear - common
IMG_STATUS_DOGEAR_PARTIAL = 1 << 5, // dogear - scanned partial
IMG_STATUS_BLANK = 1 << 6, // blank image
};
enum data_type
{
DATA_TYPE_BOOL = 0, // (bool*)
DATA_TYPE_INT4, // (uint32_t*)
DATA_TYPE_FLOAT, // (double*)
DATA_TYPE_STRING, // (char*) with max_len space. befor and include me, keep same with SANE_TYPE_BOOL, SANE_TYPE_xxx ...
DATA_TYPE_INT1, // (uint8_t*)
DATA_TYPE_INT2, // (uint16_t*)
DATA_TYPE_INT8, // (uint64_t*)
DATA_TYPE_CUSTOM,
};
enum paper_side
{
PAPER_SIDE_FRONT = 0, // single side, this is front
PAPER_SIDE_BACK, // single side, this is back
PAPER_SIDE_TOP, // VERT-compound sides, and front side is at top
PAPER_SIDE_BOTTOM, // VERT-compound sides, and front side is at bottom
PAPER_SIDE_LEFT, // HORZ-compound sides, and front side is at left
PAPER_SIDE_RIGHT, // HORZ-compound sides, and front side is at right
PAPER_SIDE_DSP, // a special type
};
enum rot_angle
{
ROT_ANGLE_0 = 0,
ROT_ANGLE_90,
ROT_ANGLE_180,
ROT_ANGLE_270,
};
enum clr_channel
{
COLOR_CHANNEL_RGB = 0,
COLOR_CHANNEL_RGBA,
COLOR_CHANNEL_GRAY,
COLOR_CHANNEL_RED,
COLOR_CHANNEL_GREEN,
COLOR_CHANNEL_BLUE,
COLOR_CHANNEL_ALPHA,
};
enum color_mode
{
COLOR_MODE_BW = 0,
COLOR_MODE_GRAY,
COLOR_MODE_RGB,
};
#pragma pack(push)
#pragma pack(1)
typedef struct _ep0_reply
{
uint8_t in_status; // BULK-IN status, enum bulk_statu
uint8_t out_status; // BULK-OUT status, enum bulk_statu
uint16_t in_err; // valid if in_statu == BULK_STATU_ERROR
uint16_t out_err; // valid if out_statu == BULK_STATU_ERROR
uint16_t task_cnt; // tasks in command queue
uint32_t task_cmd; // the cmd of the task thread is doing
uint32_t task_pack_id; // packet id of the cmd
uint32_t task_required_bytes; // required byte of this packet
uint32_t packets_to_sent; // how many packets in sent queue
uint32_t bytes_to_sent; // how many bytes data is waiting for be sent in one replying packet
}EP0REPLYSTATUS, *LPEP0REPLYSTATUS;
typedef struct _pack_base // A piece of data has only one header
{
uint32_t enc_cmd : 2; // encrypting type, for 'cmd'
uint32_t encrypt : 3; // encrypting type, for payload content. the payload must cotains self-check if was encrypted packet
uint32_t enc_data : 5; // data for encrypt
uint32_t size : 6; // bytes of this structure
uint32_t cmd : 16; // packet command
uint32_t data; // simple data in command packet depends 'cmd', or error code in reply packet
uint32_t pack_id; // maintain by the initiator, the reply packet use the same id
uint32_t payload_len; // total bytes of payload of this command packet (the data in the range will sent in ONE 'write'),
// big data can be described in payload and independent communication, and this field should not include them.
// if encrypted packet, this field is the length after encrypting
char payload[0]; // payloads, according to 'cmd'
STRUCT_CONSTRUCTOR(_pack_base)
}PACK_BASE, * LPPACK_BASE;
#define BASE_PACKET_REPLY(reply, command, id, err) \
(reply).encrypt = 0; \
(reply).enc_data = 0; \
(reply).size = sizeof(reply); \
(reply).data = err; \
(reply).cmd = command; \
(reply).pack_id = id; \
(reply).payload_len = 0;
typedef struct _config_val
{
uint8_t type; // same as SANE_Value_Type
uint8_t name_off; // name offset of the option in data, end with '\0'
uint8_t val_off; // option value offset in data
uint8_t after_do; // see SANE_INFO_xxx in sane.h
uint16_t val_size; // real size of value
uint16_t max_size; // max size of this option, this value has given in gb_json::size
char data[0]; // contains value and name. fetch them according name_off and val_off members.
}CFGVAL, *LPCFGVAL;
typedef struct _img_pos
{
uint64_t paper_ind : 32; // paper index in this turn/start, based ZERO. (image-collector set)
uint64_t new_img : 1; // 0 - partial data; 1 - new image data. (image-collector set)
uint64_t img_over : 1; // 0 - has data yet; 1 - END for the image. (image-collector set)
uint64_t paper_side : 3; // enum paper_side. front of paper(When scanning multiple sheets, the paper feeding side is the front side). (image-collector set)
uint64_t back_rot : 2; // back rotation angle, enum rot_angle. (image-collector set)
uint64_t channel_ind : 4; // index of color channel, enum clr_channel. (image-collector set)
uint64_t status : 7; // img_status. (image-collector set)
uint64_t split_ind : 7; // splitting order, from left to right and then top to bottom, based ZERO
uint64_t multiout_ind : 4; // index of multi-out
uint64_t reserved : 3; // reserved
STRUCT_CONSTRUCTOR(_img_pos)
}IMGPOS, * LPIMGPOS;
typedef struct _pack_img
{
IMGPOS pos; // image pos info ...
uint32_t width; // image width in pixel. (image-collector set)
uint32_t height; // image height in pixel. (image-collector set)
uint32_t resolution_x; // image horizontal reolution. (image-collector set)
uint32_t resolution_y; // image vertical reolution. (image-collector set)
uint32_t channels : 6; // image channels per pixel. (image-collector set)
uint32_t format : 6; // image format, see 'img_format'. (image-collector set)
uint32_t bpp : 6; // bits per pixel. (image-collector set)
uint32_t bppc : 6; // bits per pixel in this channel, equal to 'bpp' if pos.channel_ind == 0x0f. (image-collector set)
uint32_t compression : 6; // image data compression, see 'img_compression'. (image-collector set)
uint32_t reserve : 2; // unused now
uint32_t info_size; // image information size in bytes, information part is used for quality of JPEG, pallete of BMP .... (image-collector set)
uint64_t data_size; // image data size in 'data' with bytes. (image-collector set)
// char data[0]; // two parts: image info (info_size) + image data (data_size)
STRUCT_CONSTRUCTOR(_pack_img)
}PACKIMAGE, * LPPACKIMAGE;
typedef struct _oper_token
{
uint32_t type; // token type
char data[128]; // token data
}OPERTOKEN, * LPOPERTOKEN;
typedef struct _tx_file
{
uint64_t size; // total size
uint64_t offset; // offset in the file
char path[2]; // file full path-name
}TXFILE, *LPTXFILE;
typedef struct _file_info
{
OPERTOKEN token; // operation token, returned by command PACK_CMD_TOKEN_GET
uint64_t size; // file size
uint16_t name_len; // bytes of file name string
uint16_t create_time_len; // bytes of create time string: '2022-12-07 12:34:56.789', or target file path in command PACK_CMD_FILE_MOVE
uint16_t modify_time_len;
uint16_t version_len; // bytes of version string
char data[0]; // 4 parts: path-file(name_len) + create-time(create_time_len) + modify-time(modify_time_len) + version(version_len)
// or 5 parts in command PACK_CMD_FILE_WRITE, add content at the last part of bytes 'size'
STRUCT_CONSTRUCTOR(_file_info)
}FILEINFO, * LPFILEINFO;
typedef struct _proc_info
{
OPERTOKEN token; // operation token, returned by command PACK_CMD_TOKEN_GET
uint32_t count; // number of elements in array proc
struct _info
{
uint16_t len; // bytes of this element, include this head
uint64_t pid; // process id
uint64_t ppid; // parent process id
uint64_t start; // started time in ns from 1970-01-01 00:00:00
uint64_t mem; // memory usage, in bytes
uint64_t cpu_clk; // cpu clock
char path_name[4];
}proc[1];
STRUCT_CONSTRUCTOR(_proc_info)
}PROCINFO, * LPPROCINFO;
#pragma pack(pop)
////////////////////////////////////////////////////////////////////////////////////////////////
// configurations ...
//
// 1 - App has whole set, group definitions
//
// 2 - device provides sub-set, or a customizing item
//

125
sdk/base/plat_types.h Normal file
View File

@ -0,0 +1,125 @@
#pragma once
#if defined(WIN32) || defined(_WIN64)
#define OS_WIN 1
#else
#define OS_WIN 0
#endif
#define SIZE_KB(n) ((n) * 1024)
#define SIZE_MB(n) SIZE_KB(n * 1024)
#define SIZE_GB(n) SIZE_MB(n * 1024)
#define SEC_2_MS(s) ((s) * 1000)
#define MSEC_2_US(ms) ((ms) * 1000)
#define SEC_2_US(s) MSEC_2_US(SEC_2_MS(s))
#define ALIGN_TO(v, align) (((v) + (align) - 1) / (align) * (align))
#define ALIGN_INT(v) ALIGN_TO(v, sizeof(int))
#define RETURN_ENUM_STR(v, e) \
if(v == e) \
return #e;
#if !OS_WIN // migrate codes from windows to linux ...
#include <unistd.h>
#include <sys/types.h>
#include <dlfcn.h>
#include <semaphore.h>
#pragma pack(push)
#pragma pack(1)
typedef struct BITMAPFILEHEADER
{
u_int16_t bfType;
u_int32_t bfSize;
u_int16_t bfReserved1;
u_int16_t bfReserved2;
u_int32_t bfOffBits;
}BITMAPFILEHEADER;
typedef struct BITMAPINFOHEADER
{
u_int32_t biSize;
u_int32_t biWidth;
u_int32_t biHeight;
u_int16_t biPlanes;
u_int16_t biBitCount;
u_int32_t biCompression;
u_int32_t biSizeImage;
u_int32_t biXPelsPerMeter;
u_int32_t biYPelsPerMeter;
u_int32_t biClrUsed;
u_int32_t biClrImportant;
}BITMAPINFODEADER;
#pragma pack(pop)
#define BI_RGB 0
#define MAKEWORD(a, b) (((a) & 0x0ff) | (((b) & 0x0ff) << 8))
#define MAKELONG(a, b) (((a) & 0x0ffff) | (((b) & 0x0ffff) << 16))
#define _countof(a) (sizeof(a) / sizeof((a)[0]))
typedef long LONG;
typedef void* HANDLE;
typedef void* HWND;
typedef void* HMODULE;
typedef void* LPVOID;
typedef void* FARPROC;
typedef unsigned int DWORD;
typedef unsigned short WORD;
typedef unsigned short UINT16;
typedef unsigned char BYTE;
typedef int BOOL;
#define TRUE 1
#define FALSE 0
#define MAX_PATH 256
#define huge
#define FAR
#define NEAR
#define LOWORD(v) ((v) & 0x0ffff)
#define HIWORD(v) (((v) >> 16) & 0x0ffff)
#define PASCAL __attribute__((stdcall))
#define _countof(a) (sizeof(a) / sizeof(a[0]))
#define LOAD_WITH_ALTERED_SEARCH_PATH RTLD_NOW
#define FreeLibrary dlclose
#define GetProcAddress dlsym
#define GetPrivateProfileIntW GetPrivateProfileIntA
#define lstrlenA strlen
#define lstrlenW strlen
#define DLL_EXTESION "so"
#define PATH_SEPARATOR "/"
#define STRICMP strcasecmp
#define MKDIR(a, b) mkdir(a, b)
#define STDCALL
#define ERROR_CANCELLED 1223
#define USB_TIMEOUT_INFINITE 0
#define FSEEK fseek
#define FTELL ftell
extern DWORD GetLastError(void);
extern DWORD GetPrivateProfileIntA(const char* app, const char* key, DWORD def, const char* file);
extern DWORD GetPrivateProfileStringA(const char* app, const char* key, const char* init, char* buf, size_t len, const char* file);
extern void Sleep(DWORD milliseconds);
extern int GetModuleFileNameA(HMODULE module, char* buf, size_t len); // NOTE: parameter 'module' is consinder as a part of the module file name
extern int GetCurrentProcessId(void);
extern int GetCurrentThreadId(void);
#else
#include <Windows.h>
#define bzero(a, l) memset(a, 0, l)
#define DLL_EXTESION "dll"
#define PATH_SEPARATOR "\\"
#define STRICMP stricmp
#define MKDIR(a, b) mkdir(a)
#define STDCALL __stdcall
#define sem_t HANDLE
#define USB_TIMEOUT_INFINITE -1
#define FSEEK _fseeki64
#define FTELL _ftelli64
#define pid_t int
#endif

1942
sdk/base/utils.cpp Normal file

File diff suppressed because it is too large Load Diff

368
sdk/base/utils.h Normal file
View File

@ -0,0 +1,368 @@
// utilities for platform ...
//
// Date: 2023-06-30
//
#pragma once
#include "plat_types.h"
#include <string>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>
#include <algorithm>
#include <deque>
#include <functional>
#define USE_SAFE_THREAD
enum log_type
{
LOG_TYPE_NONE = 0, // no logging
LOG_TYPE_CONSOLE, // print to console
LOG_TYPE_FILE, // write log into file
};
enum log_level
{
LOG_LEVEL_ALL = 0,
LOG_LEVEL_DEBUG,
LOG_LEVEL_WARNING,
LOG_LEVEL_FATAL,
};
namespace utils
{
std::string utf82ansi(const char* utf8);
std::string ansi2utf8(const char* ansi);
std::string get_command_result(const char* cmd, int len = -1, int *err = nullptr);
std::string get_local_data_path(void);
std::string temporary_path(void);
std::string format_current_time(void);
std::string get_module_full_path(const char* part_name = nullptr/*nullptr to get main-pe/first module's full path*/);
std::string find_file(const char* root_dir, const char* part_name, bool recursive = false);
std::string target_file_from_link(const char* lnk_file);
std::string get_ini_value(const char* seg, const char* key, const char* cfg_file); // return "" if not found
std::string load_mini_file(const char* file, int* err); // <= 1MB
int save_2_file(void* data, size_t len, const char* file, bool append = false/*append or new*/, size_t max_size = -1/*in append mode, truncate file if size is exceeded this value if was not -1*/);
bool is_match_pattern(const char* text, const char* pattern);
const char* to_lower(std::string& str); // return str.c_str()
const char* trim(std::string& str, const char* sp = "\r\n\t "); // return str.c_str()
HMODULE load_dll(const char* path_file, int flag);
bool create_folder(const char* folder);
void set_ini_value(const char* seg, const char* key, const char* val, const char* cfg_file);
int enum_file(const char* folder, bool recursive, bool/*return false to stop enumeration*/(STDCALL* found)(const char* path_name, bool dir, void* param), void* param);
int move_file(const char* from, const char* to);
int get_disk_space(const char* path, unsigned long long* total, unsigned long long* avail, unsigned long long* block);
unsigned int get_page_size(unsigned int* map_unit = nullptr);
void init_log(log_type type, log_level level = LOG_LEVEL_ALL, const char* fn_appendix = nullptr/*appendix to default log-file-name*/);
void uninit(void);
void log_info(const char* info, int level = LOG_LEVEL_ALL);
void log_mem_info(const char* desc, const void* data, size_t bytes, int level = LOG_LEVEL_ALL); // log as 0x12345678 00 01 02 ...
int get_log_type(void);
int get_log_level(void);
int copy_log_file_to(const char* dst);
int clear_log_file(void);
#if OS_WIN
// Function: pump message recycle (GetMessageW)
//
// Parameters: hwnd - target window
//
// filter_min - the minimal message
//
// filter_max - the maximal message
//
// msghandler - custom message processor
// MSG*, received message by GetMessage
// bool*, return whether user handled the message, no TranslateMessage and DispatchMessage if this was true
//
// Return: whether quit the message recycle
bool run_get_message(HWND hwnd, UINT filter_min = 0, UINT filter_max = 0, std::function<bool(MSG*, bool*)> msghandler = std::function<bool(MSG*, bool*)>());
#endif
template<typename ... Args>
void to_log(int level, const char* fmt, Args ... args)
{
if (get_log_type() != LOG_TYPE_NONE && get_log_level() <= level)
{
size_t size = snprintf(nullptr, 0, fmt, args ...) + 2;
std::unique_ptr<char[]> buf(new char[size]);
snprintf(buf.get(), size, fmt, args ...);
log_info(buf.get(), (log_level)level);
}
}
template<typename ... Args>
void to_log_with_api(bool(*is_enable)(int), void(*log_api)(const char*, int), int level, const char* fmt, Args ... args)
{
if (is_enable(level))
{
size_t size = snprintf(nullptr, 0, fmt, args ...) + 2;
std::unique_ptr<char[]> buf(new char[size]);
snprintf(buf.get(), size, fmt, args ...);
log_api(buf.get(), (log_level)level);
}
}
template<class T>
T swap_half(T v)
{
T mask = (1 << (sizeof(T) * 4)) - 1,
h = v & mask;
v >>= sizeof(T) * 4;
v &= mask;
h <<= sizeof(T) * 4;
v |= h;
return v;
}
class base64
{
char base64_ind_[128];
char base64_char_[80];
char padding_char_;
bool is_valid_base64_table(const char* table);
bool initialize_base64_table(const char* table);
public:
base64();
~base64();
public:
bool set_base64_table(const char* table = nullptr);
std::string encode(const char* data, size_t bytes, unsigned int line_bytes = -1, bool need_padding = true);
std::string decode(const char* data, size_t bytes);
};
};
#if OS_WIN
struct _time_val
{
time_t tv_sec; /* Seconds. */
time_t tv_usec; /* Microseconds. */
};
typedef struct _time_val TIMEV;
struct timezone
{
int tz_minuteswest; /* Minutes west of GMT. */
int tz_dsttime; /* Nonzero if DST is ever in effect. */
};
int gettimeofday(TIMEV* tv, struct timezone* tz);
#else
#include <sys/time.h>
typedef struct timeval TIMEV;
#endif
// object life referer
//
// derived from 'refer' if your class used in multi-threads
//
#define MUTEX std::mutex
#define LOCK_WITH_NAME(n, v) std::lock_guard<MUTEX> n(v)
#define SIMPLE_LOCK(v) LOCK_WITH_NAME(locker, v)
class refer
{
volatile int32_t ref_;
MUTEX mutex_;
protected:
refer();
virtual ~refer();
virtual void on_born(void);
virtual void on_dead(void);
public:
virtual int32_t add_ref(void);
virtual int32_t release(void);
};
// time utility
class chronograph
{
TIMEV bgn_;
public:
chronograph();
~chronograph();
static bool now(TIMEV* tv);
static bool now(uint64_t* seconds, uint64_t* u_seconds);
static std::string now(bool with_ms = true/*whether with milliseconds*/); // return '2022-11-30 10:38:42.123', no '.123' if with_ms was false
public:
uint64_t elapse_s(void);
uint64_t elapse_ms(void);
uint64_t elapse_us(void);
void reset(void);
};
// event
class platform_event : public refer
{
sem_t sem_;
volatile bool waiting_;
std::string dbg_info_;
bool log_ = true;
public:
platform_event(const char* info = "");
~platform_event();
public:
bool try_wait(void);
bool wait(unsigned timeout = USB_TIMEOUT_INFINITE/*ms*/); // USB_TIMEOUT_INFINITE is waiting unfinite, true when watied and false for wait timeout
void trigger(void);
bool is_waiting(void);
void enable_log(bool enable);
void set_debug_info(const char* info);
};
// share memory
class shared_memory : public refer
{
unsigned long long key_;
void* obj_;
bool first_;
size_t bytes_;
size_t len_;
void init(void);
void clear(void);
char* get_buf(void);
void release_buf(void* buf);
#if !defined(WIN32) && !defined(_WIN64)
static std::string get_proc_name_by_pid(pid_t pid);
#endif
public:
shared_memory(unsigned long long key, size_t size = 1024);
protected:
~shared_memory();
public:
bool is_ok(void);
bool is_first(void);
std::string read(void);
int write(const char* data, size_t len);
};
template<class T>
class safe_fifo
{
MUTEX lock_;
std::deque<T> que_;
platform_event* wait_;
public:
safe_fifo(const char* who) : wait_(new platform_event(who && *who ? who : "fifo"))
{}
~safe_fifo()
{
wait_->release();
}
public:
size_t save(const T& t, bool notify = false)
{
SIMPLE_LOCK(lock_);
size_t cnt = que_.size();
que_.push_back(std::move(t));
if (notify)
wait_->trigger();
return cnt + 1;
}
bool take(T& t, bool wait = false)
{
if (wait && size() == 0)
{
wait_->wait();
}
{
SIMPLE_LOCK(lock_);
if (que_.size())
{
t = std::move(que_.front());
que_.pop_front();
return true;
}
else
{
return false;
}
}
}
size_t size(void)
{
SIMPLE_LOCK(lock_);
return que_.size();
}
void clear(void)
{
SIMPLE_LOCK(lock_);
que_.clear();
}
void trigger(void)
{
wait_->trigger();
}
void enable_wait_log(bool enable)
{
wait_->enable_log(enable);
}
};
class safe_thread
{
typedef struct _safe_thrd
{
std::string name;
std::shared_ptr<std::thread> thread;
}SAFETHRD;
volatile bool run_ = true;
MUTEX lock_;
std::unique_ptr<std::thread> notify_thread_;
std::vector<SAFETHRD> threads_;
safe_fifo<std::string> excep_que_;
std::function<void(const char*)> excep_handler_ = std::function<void(const char*)>();
void thread_worker(std::function<void(void)> func, std::string name);
void thread_notify_exception(void);
public:
safe_thread(void);
~safe_thread();
public:
void set_exception_handler(std::function<void(const char*)> on_exception = std::function<void(const char*)>());
int start(std::function<void(void)> f, const char* thread_name);
int stop(const char* thread_name);
};

View File

@ -0,0 +1,380 @@
// this file is include huagao scanner error definitions
//
// created: 2022-02-07
//
#pragma once
#define RETURN_IF(var, enum_val) \
if(var == enum_val) \
return #enum_val;
#define RETURN_DESC_IF(var, hgerr) \
if(var == hgerr) \
return from_default_language(STATU_DESC_##hgerr);
enum scanner_err
{
SCANNER_ERR_OK = 0, // 成功,正常状态
// 1软件逻辑错误
SCANNER_ERR_INVALID_PARAMETER = 0x100, // 非法的参数调用
SCANNER_ERR_USER_CANCELED, // 用户取消了操作
SCANNER_ERR_INSUFFICIENT_MEMORY, // 分配的内存不足
SCANNER_ERR_ACCESS_DENIED, // 访问被拒绝
SCANNER_ERR_IO_PENDING, // 异步访问,数据稍后返回
SCANNER_ERR_NOT_EXACT, // 数据不精确,精确的数据已经在同一缓存中返回
SCANNER_ERR_CONFIGURATION_CHANGED, // 设备的配置项发生改变,需要重新加载显示
SCANNER_ERR_NOT_OPEN, // 设备未打开
SCANNER_ERR_NOT_START, // 设备没有启动
SCANNER_ERR_NOT_ANY_MORE, // 用于回调返回,在本次扫描中,对相同操作不再回调
SCANNER_ERR_NO_DATA, // 没有数据
SCANNER_ERR_HAS_DATA_YET, // 有数据未被读取(异步操作中)
SCANNER_ERR_OUT_OF_RANGE, // 相关操作超出范围
SCANNER_ERR_IO, // IO错误
SCANNER_ERR_TIMEOUT, // 超时错误
SCANNER_ERR_OPEN_FILE_FAILED, // 打开本地文件失败
SCANNER_ERR_CREATE_FILE_FAILED, // 创建本地文件失败
SCANNER_ERR_WRITE_FILE_FAILED, // 写本地文件失败
SCANNER_ERR_DATA_DAMAGED, // 数据损坏(内置资源数据损坏)
SCANNER_ERR_OPENED_BY_OTHER_PROCESS, // 设备已经被其它进程打开占用
SCANNER_ERR_LANG_PAK_LOST, // 语言包丢失
SCANNER_ERR_RELOAD_IMAGE_PARAM, // 配置成功,会影响图像参数,应用需要重新加载图像参数 - added on 2023-02-18 for XSANE修改影响图像参数的属性后扫描崩溃的问题
SCANNER_ERR_RELOAD_OPT_PARAM, // SCANNER_ERR_CONFIGURATION_CHANGED + SCANNER_ERR_RELOAD_IMAGE_PARAM - added on 2023-02-18 for XSANE修改影响图像参数的属性后扫描崩溃的问题
SCANNER_ERR_THROW_EXCEPTION, // catched exception
// 2USB错误
SCANNER_ERR_USB_INIT_FAILED = 0x5b00, // libusb_init 失败
SCANNER_ERR_USB_REGISTER_PNP_FAILED, // 注册USB监听事件失败
SCANNER_ERR_USB_CLAIM_INTERFACE_FAILED, // failed in calling libusb_claim_interface
// 3硬件错误
SCANNER_ERR_DEVICE_NOT_FOUND = 0x0de00, // 设备未找到
SCANNER_ERR_DEVICE_NOT_SUPPORT, // 设备不支持该操作
SCANNER_ERR_DEVICE_BUSY, // 设备正忙,不能响应该操作
SCANNER_ERR_DEVICE_SLEEPING, // 设备处于睡眠状态
SCANNER_ERR_DEVICE_COUNT_MODE, // 设备处于计数扫描状态?
SCANNER_ERR_DEVICE_STOPPED, // 扫描停止
SCANNER_ERR_DEVICE_COVER_OPENNED, // 扫描仪盖板呈打开状态
SCANNER_ERR_DEVICE_NO_PAPER, // 没有纸张输入
SCANNER_ERR_DEVICE_FEEDING_PAPER, // 搓纸失败
SCANNER_ERR_DEVICE_DOUBLE_FEEDING, // 双张检测
SCANNER_ERR_DEVICE_PAPER_JAMMED, // 卡纸
SCANNER_ERR_DEVICE_STAPLE_ON, // 有钉书钉
SCANNER_ERR_DEVICE_PAPER_SKEW, // 纸张倾斜
SCANNER_ERR_DEVICE_SIZE_CHECK, // 尺寸检测错误
SCANNER_ERR_DEVICE_DOGEAR, // 纸张有折角
SCANNER_ERR_DEVICE_NO_IMAGE, // 设备没取到图
SCANNER_ERR_DEVICE_SCANN_ERROR, // 设备扫图失败
SCANNER_ERR_DEVICE_PC_BUSY, // PC繁忙或出错
SCANNER_ERR_DEVICE_ISLOCK, // 设备被锁定
SCANNER_ERR_DEVICE_UPGRADE_SUCCESSFUL, // 固件升级成功
SCANNER_ERR_DEVICE_UPGRADE_FAIL, // 固件升级失败+
SCANNER_ERR_DEVICE_AUTO_FAIL_OVER, // 设备平场自动校正结束
SCANNER_ERR_DEVICE_AUTO_FAIL_INFO, // 设备平场自动校正信息传输
SCANNER_ERR_DEVICE_DISTORTION, // 畸变修正失败
SCANNER_ERR_DEVICE_MAYBE_IS_HOLE, // 纸张有孔洞
SCANNER_ERR_DEVICE_DEVS_BOOTING, // 请稍等 - 设备正在准备
SCANNER_ERR_DEVICE_UNKNOWN_STATUS, // 设备处于未知状态
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 状态信息描述符
//
// #define STATU_DESC_PREPARE_START "准备启动……"
#define STATU_DESC_PREPARE_START "\345\207\206\345\244\207\345\220\257\345\212\250\342\200\246\342\200\246"
#define ID_STATU_DESC_PREPARE_START 62135
// #define STATU_DESC_REWRITE_CONFIGURATION "写入配置……"
#define STATU_DESC_REWRITE_CONFIGURATION "\345\206\231\345\205\245\351\205\215\347\275\256\342\200\246\342\200\246"
#define ID_STATU_DESC_REWRITE_CONFIGURATION 16489
// #define STATU_DESC_CLEAR_CACHE "清理缓存……"
#define STATU_DESC_CLEAR_CACHE "\346\270\205\347\220\206\347\274\223\345\255\230\342\200\246\342\200\246"
#define ID_STATU_DESC_CLEAR_CACHE 6523
// #define STATU_DESC_START_SUCCESS "启动成功"
#define STATU_DESC_START_SUCCESS "\345\220\257\345\212\250\346\210\220\345\212\237"
#define ID_STATU_DESC_START_SUCCESS 33731
// #define STATU_DESC_START_FAIL "启动失败"
#define STATU_DESC_START_FAIL "\345\220\257\345\212\250\345\244\261\350\264\245"
#define ID_STATU_DESC_START_FAIL 51358
// #define STATU_DESC_SCAN_WORKING "正在扫描……"
#define STATU_DESC_SCAN_WORKING "\346\255\243\345\234\250\346\211\253\346\217\217\342\200\246\342\200\246"
#define ID_STATU_DESC_SCAN_WORKING 21315
// #define STATU_DESC_SCAN_STOPPED "扫描完成"
#define STATU_DESC_SCAN_STOPPED "\346\211\253\346\217\217\345\256\214\346\210\220"
#define ID_STATU_DESC_SCAN_STOPPED 17731
// #define STATU_DESC_SCAN_CANCELED "扫描已取消"
#define STATU_DESC_SCAN_CANCELED "\346\211\253\346\217\217\345\267\262\345\217\226\346\266\210"
#define ID_STATU_DESC_SCAN_CANCELED 63314
// #define STATU_DESC_WAIT_FOR_MEM "内存不足,等待内存释放……"
#define STATU_DESC_WAIT_FOR_MEM "\345\206\205\345\255\230\344\270\215\350\266\263\357\274\214\347\255\211\345\276\205\345\206\205\345\255\230\351\207\212\346\224\276\342\200\246\342\200\246"
#define ID_STATU_DESC_WAIT_FOR_MEM 7697
// #define STATU_DESC_DEVICE_RESET "正在重置,如果设备迟迟没有动作,请重新扫描……"
#define STATU_DESC_DEVICE_RESET "\346\255\243\345\234\250\351\207\215\347\275\256\357\274\214\345\246\202\346\236\234\350\256\276\345\244\207\350\277\237\350\277\237\346\262\241\346\234\211\345\212\250\344\275\234\357\274\214\350\257\267\351\207\215\346\226\260\346\211\253\346\217\217\342\200\246\342\200\246"
#define ID_STATU_DESC_DEVICE_RESET 47482
// #define STATU_DESC_SCANNER_ERR_OK "操作成功"
#define STATU_DESC_SCANNER_ERR_OK "\346\223\215\344\275\234\346\210\220\345\212\237"
//#define ID_STATU_DESC_SCANNER_ERR_OK 3249
// #define STATU_DESC_SCANNER_ERR_USER_CANCELED "操作被取消"
#define STATU_DESC_SCANNER_ERR_USER_CANCELED "\346\223\215\344\275\234\350\242\253\345\217\226\346\266\210"
#define ID_STATU_DESC_SCANNER_ERR_USER_CANCELED 48546
// #define STATU_DESC_SCANNER_ERR_DATA_DAMAGED "数据损坏"
#define STATU_DESC_SCANNER_ERR_DATA_DAMAGED "\346\225\260\346\215\256\346\215\237\345\235\217"
#define ID_STATU_DESC_SCANNER_ERR_DATA_DAMAGED 45852
// #define STATU_DESC_SCANNER_ERR_INVALID_PARAMETER "非法的参数调用"
#define STATU_DESC_SCANNER_ERR_INVALID_PARAMETER "\351\235\236\346\263\225\347\232\204\345\217\202\346\225\260\350\260\203\347\224\250"
#define ID_STATU_DESC_SCANNER_ERR_INVALID_PARAMETER 6119
// #define STATU_DESC_SCANNER_ERR_INSUFFICIENT_MEMORY "内存不足或内存分配失败"
#define STATU_DESC_SCANNER_ERR_INSUFFICIENT_MEMORY "\345\206\205\345\255\230\344\270\215\350\266\263\346\210\226\345\206\205\345\255\230\345\210\206\351\205\215\345\244\261\350\264\245"
#define ID_STATU_DESC_SCANNER_ERR_INSUFFICIENT_MEMORY 56958
// #define STATU_DESC_SCANNER_ERR_ACCESS_DENIED "访问被拒绝"
#define STATU_DESC_SCANNER_ERR_ACCESS_DENIED "\350\256\277\351\227\256\350\242\253\346\213\222\347\273\235"
#define ID_STATU_DESC_SCANNER_ERR_ACCESS_DENIED 56275
// #define STATU_DESC_SCANNER_ERR_IO_PENDING "异步访问,数据稍后返回"
#define STATU_DESC_SCANNER_ERR_IO_PENDING "\345\274\202\346\255\245\350\256\277\351\227\256\357\274\214\346\225\260\346\215\256\347\250\215\345\220\216\350\277\224\345\233\236"
#define ID_STATU_DESC_SCANNER_ERR_IO_PENDING 25758
// #define STATU_DESC_SCANNER_ERR_NOT_EXACT "数据不精确,精确的数据已经在同一缓存中返回"
#define STATU_DESC_SCANNER_ERR_NOT_EXACT "\346\225\260\346\215\256\344\270\215\347\262\276\347\241\256\357\274\214\347\262\276\347\241\256\347\232\204\346\225\260\346\215\256\345\267\262\347\273\217\345\234\250\345\220\214\344\270\200\347\274\223\345\255\230\344\270\255\350\277\224\345\233\236"
#define ID_STATU_DESC_SCANNER_ERR_NOT_EXACT 64193
// #define STATU_DESC_SCANNER_ERR_CONFIGURATION_CHANGED "设备的配置项发生改变,需要重新加载显示"
#define STATU_DESC_SCANNER_ERR_CONFIGURATION_CHANGED "\350\256\276\345\244\207\347\232\204\351\205\215\347\275\256\351\241\271\345\217\221\347\224\237\346\224\271\345\217\230\357\274\214\351\234\200\350\246\201\351\207\215\346\226\260\345\212\240\350\275\275\346\230\276\347\244\272"
#define ID_STATU_DESC_SCANNER_ERR_CONFIGURATION_CHANGED 26730
// #define STATU_DESC_SCANNER_ERR_RELOAD_IMAGE_PARAM "图像参数已经改变,请重新加载"
#define STATU_DESC_SCANNER_ERR_RELOAD_IMAGE_PARAM "\345\233\276\345\203\217\345\217\202\346\225\260\345\267\262\347\273\217\346\224\271\345\217\230\357\274\214\350\257\267\351\207\215\346\226\260\345\212\240\350\275\275"
#define ID_STATU_DESC_SCANNER_ERR_RELOAD_IMAGE_PARAM 54637
// #define STATU_DESC_SCANNER_ERR_RELOAD_OPT_PARAM "关联属性状态及图像参数已经改变,请重新加载"
#define STATU_DESC_SCANNER_ERR_RELOAD_OPT_PARAM "\345\205\263\350\201\224\345\261\236\346\200\247\347\212\266\346\200\201\345\217\212\345\233\276\345\203\217\345\217\202\346\225\260\345\267\262\347\273\217\346\224\271\345\217\230\357\274\214\350\257\267\351\207\215\346\226\260\345\212\240\350\275\275"
#define ID_STATU_DESC_SCANNER_ERR_RELOAD_OPT_PARAM 4762
// #define STATU_DESC_SCANNER_ERR_NOT_OPEN "设备未打开"
#define STATU_DESC_SCANNER_ERR_NOT_OPEN "\350\256\276\345\244\207\346\234\252\346\211\223\345\274\200"
#define ID_STATU_DESC_SCANNER_ERR_NOT_OPEN 38521
// #define STATU_DESC_SCANNER_ERR_NOT_START "设备没有启动"
#define STATU_DESC_SCANNER_ERR_NOT_START "\350\256\276\345\244\207\346\262\241\346\234\211\345\220\257\345\212\250"
#define ID_STATU_DESC_SCANNER_ERR_NOT_START 5681
// #define STATU_DESC_SCANNER_ERR_NOT_ANY_MORE "在本次扫描中,对相同操作不再询问"
#define STATU_DESC_SCANNER_ERR_NOT_ANY_MORE "\345\234\250\346\234\254\346\254\241\346\211\253\346\217\217\344\270\255\357\274\214\345\257\271\347\233\270\345\220\214\346\223\215\344\275\234\344\270\215\345\206\215\350\257\242\351\227\256"
#define ID_STATU_DESC_SCANNER_ERR_NOT_ANY_MORE 16267
// #define STATU_DESC_SCANNER_ERR_NO_DATA "没有数据"
#define STATU_DESC_SCANNER_ERR_NO_DATA "\346\262\241\346\234\211\346\225\260\346\215\256"
#define ID_STATU_DESC_SCANNER_ERR_NO_DATA 15331
// #define STATU_DESC_SCANNER_ERR_HAS_DATA_YET "有数据未被读取"
#define STATU_DESC_SCANNER_ERR_HAS_DATA_YET "\346\234\211\346\225\260\346\215\256\346\234\252\350\242\253\350\257\273\345\217\226"
#define ID_STATU_DESC_SCANNER_ERR_HAS_DATA_YET 31030
// #define STATU_DESC_SCANNER_ERR_OUT_OF_RANGE "操作超出范围"
#define STATU_DESC_SCANNER_ERR_OUT_OF_RANGE "\346\223\215\344\275\234\350\266\205\345\207\272\350\214\203\345\233\264"
#define ID_STATU_DESC_SCANNER_ERR_OUT_OF_RANGE 22268
// #define STATU_DESC_SCANNER_ERR_IO "IO错误请重启设备或拔插USB"
#define STATU_DESC_SCANNER_ERR_IO "IO\351\224\231\350\257\257\357\274\214\350\257\267\351\207\215\345\220\257\350\256\276\345\244\207\346\210\226\346\213\224\346\217\222USB"
#define ID_STATU_DESC_SCANNER_ERR_IO 27027
// #define STATU_DESC_SCANNER_ERR_TIMEOUT "操作超时"
#define STATU_DESC_SCANNER_ERR_TIMEOUT "\346\223\215\344\275\234\350\266\205\346\227\266"
#define ID_STATU_DESC_SCANNER_ERR_TIMEOUT 65371
// #define STATU_DESC_SCANNER_ERR_OPEN_FILE_FAILED "打开本地文件失败"
#define STATU_DESC_SCANNER_ERR_OPEN_FILE_FAILED "\346\211\223\345\274\200\346\234\254\345\234\260\346\226\207\344\273\266\345\244\261\350\264\245"
#define ID_STATU_DESC_SCANNER_ERR_OPEN_FILE_FAILED 52380
// #define STATU_DESC_SCANNER_ERR_CREATE_FILE_FAILED "创建本地文件失败"
#define STATU_DESC_SCANNER_ERR_CREATE_FILE_FAILED "\345\210\233\345\273\272\346\234\254\345\234\260\346\226\207\344\273\266\345\244\261\350\264\245"
#define ID_STATU_DESC_SCANNER_ERR_CREATE_FILE_FAILED 23361
// #define STATU_DESC_SCANNER_ERR_WRITE_FILE_FAILED "写本地文件失败"
#define STATU_DESC_SCANNER_ERR_WRITE_FILE_FAILED "\345\206\231\346\234\254\345\234\260\346\226\207\344\273\266\345\244\261\350\264\245"
#define ID_STATU_DESC_SCANNER_ERR_WRITE_FILE_FAILED 22164
// #define STATU_DESC_SCANNER_ERR_OPENED_BY_OTHER_PROCESS "设备已经被其它进程占用"
#define STATU_DESC_SCANNER_ERR_OPENED_BY_OTHER_PROCESS "\350\256\276\345\244\207\345\267\262\347\273\217\350\242\253\345\205\266\345\256\203\350\277\233\347\250\213\345\215\240\347\224\250"
#define ID_STATU_DESC_SCANNER_ERR_OPENED_BY_OTHER_PROCESS 62384
// #define STATU_DESC_SCANNER_ERR_USB_INIT_FAILED "USB通信初始化失败"
#define STATU_DESC_SCANNER_ERR_USB_INIT_FAILED "USB\351\200\232\344\277\241\345\210\235\345\247\213\345\214\226\345\244\261\350\264\245"
#define ID_STATU_DESC_SCANNER_ERR_USB_INIT_FAILED 49159
// #define STATU_DESC_SCANNER_ERR_USB_REGISTER_PNP_FAILED "注册USB监听事件失败"
#define STATU_DESC_SCANNER_ERR_USB_REGISTER_PNP_FAILED "\346\263\250\345\206\214USB\347\233\221\345\220\254\344\272\213\344\273\266\345\244\261\350\264\245"
#define ID_STATU_DESC_SCANNER_ERR_USB_REGISTER_PNP_FAILED 5685
// #define STATU_DESC_SCANNER_ERR_USB_CLAIM_INTERFACE_FAILED "声明USB接口失败"
#define STATU_DESC_SCANNER_ERR_USB_CLAIM_INTERFACE_FAILED "\345\243\260\346\230\216USB\346\216\245\345\217\243\345\244\261\350\264\245"
#define ID_STATU_DESC_SCANNER_ERR_USB_CLAIM_INTERFACE_FAILED 21918
// #define STATU_DESC_SCANNER_ERR_DEVICE_NOT_FOUND "设备未找到"
#define STATU_DESC_SCANNER_ERR_DEVICE_NOT_FOUND "\350\256\276\345\244\207\346\234\252\346\211\276\345\210\260"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_NOT_FOUND 43988
// #define STATU_DESC_SCANNER_ERR_DEVICE_NOT_SUPPORT "设备不支持该操作"
#define STATU_DESC_SCANNER_ERR_DEVICE_NOT_SUPPORT "\350\256\276\345\244\207\344\270\215\346\224\257\346\214\201\350\257\245\346\223\215\344\275\234"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_NOT_SUPPORT 15726
// #define STATU_DESC_SCANNER_ERR_DEVICE_BUSY "设备正忙,不能响应该操作"
#define STATU_DESC_SCANNER_ERR_DEVICE_BUSY "\350\256\276\345\244\207\346\255\243\345\277\231\357\274\214\344\270\215\350\203\275\345\223\215\345\272\224\350\257\245\346\223\215\344\275\234"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_BUSY 29315
// #define STATU_DESC_SCANNER_ERR_DEVICE_SLEEPING "设备处于睡眠状态"
#define STATU_DESC_SCANNER_ERR_DEVICE_SLEEPING "\350\256\276\345\244\207\345\244\204\344\272\216\347\235\241\347\234\240\347\212\266\346\200\201"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_SLEEPING 26372
//#define STATU_DESC_SCANNER_ERR_DEVICE_NOTIFY_SLEEP 正在唤醒...请等待十秒再次扫描
#define STATU_DESC_SCANNER_ERR_DEVICE_NOTIFY_SLEEP "\346\255\243\345\234\250\345\224\244\351\206\222...\350\257\267\347\255\211\345\276\205\345\215\201\347\247\222\345\206\215\346\254\241\346\211\253\346\217\217"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_NOTIFY_SLEEP 64756
// #define STATU_DESC_SCANNER_ERR_DEVICE_COUNT_MODE "设备处于计数模式扫描状态"
#define STATU_DESC_SCANNER_ERR_DEVICE_COUNT_MODE "\350\256\276\345\244\207\345\244\204\344\272\216\350\256\241\346\225\260\346\250\241\345\274\217\346\211\253\346\217\217\347\212\266\346\200\201"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_COUNT_MODE 602
// #define STATU_DESC_SCANNER_ERR_DEVICE_STOPPED "扫描停止"
#define STATU_DESC_SCANNER_ERR_DEVICE_STOPPED "\346\211\253\346\217\217\345\201\234\346\255\242"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_STOPPED 45291
// #define STATU_DESC_SCANNER_ERR_DEVICE_COVER_OPENNED "请关闭扫描仪盖板"
#define STATU_DESC_SCANNER_ERR_DEVICE_COVER_OPENNED "\350\257\267\345\205\263\351\227\255\346\211\253\346\217\217\344\273\252\347\233\226\346\235\277"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_COVER_OPENNED 29725
// #define STATU_DESC_SCANNER_ERR_DEVICE_NO_PAPER "无纸"
#define STATU_DESC_SCANNER_ERR_DEVICE_NO_PAPER "\346\227\240\347\272\270"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_NO_PAPER 61284
// #define STATU_DESC_SCANNER_ERR_DEVICE_FEEDING_PAPER "搓纸失败"
#define STATU_DESC_SCANNER_ERR_DEVICE_FEEDING_PAPER "\346\220\223\347\272\270\345\244\261\350\264\245"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_FEEDING_PAPER 60256
// #define STATU_DESC_SCANNER_ERR_DEVICE_DOUBLE_FEEDING "有多张纸被同时搓进扫描仪"
#define STATU_DESC_SCANNER_ERR_DEVICE_DOUBLE_FEEDING "\346\234\211\345\244\232\345\274\240\347\272\270\350\242\253\345\220\214\346\227\266\346\220\223\350\277\233\346\211\253\346\217\217\344\273\252"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_DOUBLE_FEEDING 58398
// #define STATU_DESC_SCANNER_ERR_DEVICE_PAPER_JAMMED "扫描仪卡纸"
#define STATU_DESC_SCANNER_ERR_DEVICE_PAPER_JAMMED "\346\211\253\346\217\217\344\273\252\345\215\241\347\272\270"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_PAPER_JAMMED 39928
// #define STATU_DESC_SCANNER_ERR_DEVICE_STAPLE_ON "纸张上检测到有钉书钉"
#define STATU_DESC_SCANNER_ERR_DEVICE_STAPLE_ON "\347\272\270\345\274\240\344\270\212\346\243\200\346\265\213\345\210\260\346\234\211\351\222\211\344\271\246\351\222\211"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_STAPLE_ON 3126
// #define STATU_DESC_SCANNER_ERR_DEVICE_PAPER_SKEW "纸张倾斜"
#define STATU_DESC_SCANNER_ERR_DEVICE_PAPER_SKEW "\347\272\270\345\274\240\345\200\276\346\226\234"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_PAPER_SKEW 5570
// #define STATU_DESC_SCANNER_ERR_DEVICE_SIZE_CHECK "纸张尺寸检测错误"
#define STATU_DESC_SCANNER_ERR_DEVICE_SIZE_CHECK "\347\272\270\345\274\240\345\260\272\345\257\270\346\243\200\346\265\213\351\224\231\350\257\257"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_SIZE_CHECK 32107
// #define STATU_DESC_SCANNER_ERR_DEVICE_DOGEAR "纸张有折角"
#define STATU_DESC_SCANNER_ERR_DEVICE_DOGEAR "\347\272\270\345\274\240\346\234\211\346\212\230\350\247\222"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_DOGEAR 61565
// #define STATU_DESC_SCANNER_ERR_DEVICE_NO_IMAGE "设备没取到图"
#define STATU_DESC_SCANNER_ERR_DEVICE_NO_IMAGE "\350\256\276\345\244\207\346\262\241\345\217\226\345\210\260\345\233\276"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_NO_IMAGE 41789
// #define STATU_DESC_SCANNER_ERR_DEVICE_SCANN_ERROR "扫描失败"
#define STATU_DESC_SCANNER_ERR_DEVICE_SCANN_ERROR "\346\211\253\346\217\217\345\244\261\350\264\245"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_SCANN_ERROR 14901
// #define STATU_DESC_SCANNER_ERR_DEVICE_PC_BUSY "PC繁忙或出错"
#define STATU_DESC_SCANNER_ERR_DEVICE_PC_BUSY "PC\347\271\201\345\277\231\346\210\226\345\207\272\351\224\231"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_PC_BUSY 61142
// #define STATU_DESC_SCANNER_ERR_DEVICE_ISLOCK "设备被锁定"
#define STATU_DESC_SCANNER_ERR_DEVICE_ISLOCK "\350\256\276\345\244\207\350\242\253\351\224\201\345\256\232"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_ISLOCK 1535
// #define STATU_DESC_SCANNER_ERR_DEVICE_ISLOCK "此固件不支持待纸扫描"
#define STATU_DESC_SCANNER_ERR_DEVICE_NOT_SUPPORTED "\346\255\244\345\233\272\344\273\266\344\270\215\346\224\257\346\214\201\345\276\205\347\272\270\346\211\253\346\217\217"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_NOT_SUPPORTED 9610
//#define STATU_DESC_SCANNER_ERR_DEVICE_AUTO_FAIL_OVER "自动平场校正结束"
#define STATU_DESC_SCANNER_ERR_DEVICE_AUTO_FAIL_OVER "\350\207\252\345\212\250\345\271\263\345\234\272\346\240\241\346\255\243\347\273\223\346\235\237"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_AUTO_FAIL_OVER 38824
//#define STATU_DESC_SCANNER_ERR_DEVICE_DISTORTION "疑是非专用畸变修正纸"
#define STATU_DESC_SCANNER_ERR_DEVICE_DISTORTION "\347\226\221\346\230\257\351\235\236\344\270\223\347\224\250\347\225\270\345\217\230\344\277\256\346\255\243\347\272\270"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_DISTORTION 32402
//#define STATU_DESC_SCANNER_ERR_DEVICE_GET_IMAGE_OUTTIME "取图通信超时"
#define STATU_DESC_SCANNER_ERR_DEVICE_GET_IMAGE_OUTTIME "\345\217\226\345\233\276\351\200\232\344\277\241\350\266\205\346\227\266"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_GET_IMAGE_OUTTIME 10438
//#define STATU_DESC_SCANNER_ERR_DEVICE_GET_USER_CANCEL_SCAN "用户取消扫描"
#define STATU_DESC_SCANNER_ERR_DEVICE_GET_USER_CANCEL_SCAN "\347\224\250\346\210\267\345\217\226\346\266\210\346\211\253\346\217\217"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_GET_USER_CANCEL_SCAN 39020
//#define STATU_DESC_SCANNER_ERR_DEVICE_FAIL_OUTTIME "设备校正超时"
#define STATU_DESC_SCANNER_ERR_DEVICE_FAIL_OUTTIME "\350\256\276\345\244\207\346\240\241\346\255\243\350\266\205\346\227\266"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_FAIL_OUTTIME 21710
//#define STATU_DESC_SCANNER_ERR_DEVICE_ROUND_SACN_OVER "此轮扫描完成"
#define STATU_DESC_SCANNER_ERR_DEVICE_ROUND_SACN_OVER "\346\255\244\350\275\256\346\211\253\346\217\217\345\256\214\346\210\220"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_ROUND_SACN_OVER 57619
//#define STATU_DESC_SCANNER_ERR_DEVICE_DEVS "设备"
#define STATU_DESC_SCANNER_ERR_DEVICE_DEVS "\350\256\276\345\244\207\342\200\234"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_DEVS 603
//#define STATU_DESC_SCANNER_ERR_DEVICE_IS_CLOSE "已经关闭"
#define STATU_DESC_SCANNER_ERR_DEVICE_IS_CLOSE "\342\200\235\345\267\262\347\273\217\345\205\263\351\227\255\343\200\202"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_IS_CLOSE 35078
//#define STATU_DESC_SCANNER_ERR_DEVICE_UNKNOWN_ERROR "未知错误,请重启设备,等待设备复位成功在启动软件。"
#define STATU_DESC_SCANNER_ERR_DEVICE_UNKNOWN_ERROR "\346\234\252\347\237\245\351\224\231\350\257\257\357\274\214\350\257\267\351\207\215\345\220\257\350\256\276\345\244\207\357\274\214\347\255\211\345\276\205\350\256\276\345\244\207\345\244\215\344\275\215\346\210\220\345\212\237\345\234\250\345\220\257\345\212\250\350\275\257\344\273\266"
#define ID_STATU_DESC_SCANNER_ERR_DEVICE_UNKNOWN_ERROR 22744
//#define STATU_DESC_SCANNER_ERR_DEVICE_MAYBE_IS_HOLE "疑是纸张有较大孔洞"
#define STATU_DESC_SCANNER_ERR_DEVICE_MAYBE_IS_HOLE "\347\226\221\346\230\257\347\272\270\345\274\240\346\234\211\350\276\203\345\244\247\345\255\224\346\264\236"
//#define STATU_DESC_SCANNER_ERR_DEVICE_EXIT_WAIT_SCAN "退出待纸扫描"
#define STATU_DESC_SCANNER_ERR_DEVICE_EXIT_WAIT_SCAN "\351\200\200\345\207\272\345\276\205\347\272\270\346\211\253\346\217\217"
//#define STATU_DESC_SCANNER_ERR_DEVICE_GET_IMAGE_ERR "获取图像异常"
#define STATU_DESC_SCANNER_ERR_DEVICE_GET_IMAGE_ERR "\350\216\267\345\217\226\345\233\276\345\203\217\345\274\202\345\270\270"
//#define STATU_DESC_SCANNER_ERR_DEVICE_DEVS_BOOTING "请稍等-设备正在准备中"
#define STATU_DESC_SCANNER_ERR_DEVICE_DEVS_BOOTING "\350\257\267\347\250\215\347\255\211-\350\256\276\345\244\207\346\255\243\345\234\250\345\207\206\345\244\207\344\270\255"
//#define STATU_DESC_SCANNER_ERR_DEVICE_DEVS_BOOTING "设备启动成功-可以开始扫描"
#define STATU_DESC_SCANNER_ERR_DEVICE_DEVS_START_SUCCES "\350\256\276\345\244\207\345\220\257\345\212\250\346\210\220\345\212\237-\345\217\257\344\273\245\345\274\200\345\247\213\346\211\253\346\217\217"
//#define STATU_DESC_SCANNER_ERR_LANG_PAK_LOST 语言包缺失词条
#define STATU_DESC_SCANNER_ERR_LANG_PAK_LOST "\350\257\255\350\250\200\345\214\205\347\274\272\345\244\261\350\257\215\346\235\241"
//#define STATU_DESC_SCANNER_ERR_DEVICE_UPGRADE_SUCCESSFUL 设备升级成功
#define STATU_DESC_SCANNER_ERR_DEVICE_UPGRADE_SUCCESSFUL "\350\256\276\345\244\207\345\215\207\347\272\247\346\210\220\345\212\237"
//#define STATU_DESC_SCANNER_ERR_DEVICE_UPGRADE_FAIL 设备升级失败
#define STATU_DESC_SCANNER_ERR_DEVICE_UPGRADE_FAIL "\350\256\276\345\244\207\345\215\207\347\272\247\345\244\261\350\264\245"
//#define STATU_DESC_SCANNER_ERR_DEVICE_AUTO_FAIL_INFO 自动平场校正信息
#define STATU_DESC_SCANNER_ERR_DEVICE_AUTO_FAIL_INFO "\350\207\252\345\212\250\345\271\263\345\234\272\346\240\241\346\255\243\344\277\241\346\201\257"
//#define STATU_DESC_SCANNER_ERR_DEVICE_UNKNOWN_STATUS 设备处于未知状态
#define STATU_DESC_SCANNER_ERR_DEVICE_UNKNOWN_STATUS "\350\256\276\345\244\207\345\244\204\344\272\216\346\234\252\347\237\245\347\212\266\346\200\201"
// 状态信息描述符 - OVER
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

794
sdk/json/cJSON.c Normal file
View File

@ -0,0 +1,794 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/* cJSON */
/* JSON parser in C. */
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <float.h>
#include <limits.h>
#include <ctype.h>
#include "cJSON.h"
static const char *ep;
const char *cJSON_GetErrorPtr(void) {return ep;}
static int cJSON_strcasecmp(const char *s1,const char *s2)
{
if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0;
return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
}
static void* ask_memory(size_t bytes)
{
// printf("allocate %u bytes memory in cJSON\n", bytes);
return malloc(bytes);
}
static void *(*cJSON_malloc)(size_t sz) = ask_memory;
static void (*cJSON_free)(void *ptr) = free;
static char* cJSON_strdup(const char* str)
{
size_t len;
char* copy;
len = strlen(str) + 1;
if (!(copy = (char*)cJSON_malloc(len))) return 0;
memcpy(copy,str,len);
return copy;
}
void cJSON_InitHooks(cJSON_Hooks* hooks)
{
if (!hooks) { /* Reset hooks */
cJSON_malloc = ask_memory;
cJSON_free = free;
return;
}
cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:ask_memory;
cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
}
/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
if (node) memset(node,0,sizeof(cJSON));
return node;
}
/* Delete a cJSON structure. */
void cJSON_Delete(cJSON *c)
{
cJSON *next;
while (c)
{
next=c->next;
if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string);
cJSON_free(c);
c=next;
}
}
/* Parse the input text to generate a number, and populate the result into item. */
static const char *parse_number(cJSON *item,const char *num)
{
double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
if (*num=='-') sign=-1,num++; /* Has sign? */
if (*num=='0') num++; /* is zero */
if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */
if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */
if (*num=='e' || *num=='E') /* Exponent? */
{ num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */
while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */
}
n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */
item->valuedouble=n;
item->valueint=(int)n;
item->type=cJSON_Number;
return num;
}
static int pow2gt (int x) { --x; x|=x>>1; x|=x>>2; x|=x>>4; x|=x>>8; x|=x>>16; return x+1; }
typedef struct {char *buffer; int length; int offset; } printbuffer;
static char* ensure(printbuffer *p,int needed)
{
char *newbuffer;int newsize;
if (!p || !p->buffer) return 0;
needed+=p->offset;
if (needed<=p->length) return p->buffer+p->offset;
newsize=pow2gt(needed);
newbuffer=(char*)cJSON_malloc(newsize);
if (!newbuffer) {cJSON_free(p->buffer);p->length=0,p->buffer=0;return 0;}
if (newbuffer) memcpy(newbuffer,p->buffer,p->length);
cJSON_free(p->buffer);
p->length=newsize;
p->buffer=newbuffer;
return newbuffer+p->offset;
}
static int update(printbuffer *p)
{
char *str;
if (!p || !p->buffer) return 0;
str=p->buffer+p->offset;
return p->offset+strlen(str);
}
/* Render the number nicely from the given item into a string. */
static char *print_number(cJSON *item,printbuffer *p)
{
char *str=0;
double d=item->valuedouble;
if (d==0)
{
if (p) str=ensure(p,2);
else str=(char*)cJSON_malloc(2); /* special case for 0. */
if (str) strcpy(str,"0");
}
else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
{
if (p) str=ensure(p,21);
else str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
if (str) sprintf(str,"%d",item->valueint);
}
else
{
if (p) str=ensure(p,64);
else str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */
if (str)
{
if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d);
else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
else sprintf(str,"%f",d);
}
}
return str;
}
static unsigned parse_hex4(const char *str)
{
unsigned h=0;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
h=h<<4;str++;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
h=h<<4;str++;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
h=h<<4;str++;
if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
return h;
}
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str)
{
const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
if (*str!='\"') {ep=str;return 0;} /* not a string! */
while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
if (!out) return 0;
ptr=str+1;ptr2=out;
while (*ptr!='\"' && *ptr)
{
if (*ptr!='\\') *ptr2++=*ptr++;
else
{
ptr++;
switch (*ptr)
{
case 'b': *ptr2++='\b'; break;
case 'f': *ptr2++='\f'; break;
case 'n': *ptr2++='\n'; break;
case 'r': *ptr2++='\r'; break;
case 't': *ptr2++='\t'; break;
case 'u': /* transcode utf16 to utf8. */
uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */
if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */
{
if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */
uc2=parse_hex4(ptr+3);ptr+=6;
if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */
uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
}
len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
switch (len) {
case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
case 1: *--ptr2 =(uc | firstByteMark[len]);
}
ptr2+=len;
break;
default: *ptr2++=*ptr; break;
}
ptr++;
}
}
*ptr2=0;
if (*ptr=='\"') ptr++;
item->valuestring=out;
item->type=cJSON_String;
return ptr;
}
char* cJSON_utf8_2_unic(const char* utf8)
{
char* unic = (char*)malloc(strlen(utf8) * 3 + 8);
unsigned char * cur = unic;
while (*cur = *utf8++)
{
if ((*cur & 0x0f0) == 0x0e0)
{
if (((unsigned char)utf8[0] & 0x0c0) == 0x80 &&
((unsigned char)utf8[1] & 0x0c0) == 0x80)
{
char* hex = "0123456789ABCDEF";
unsigned short us = *cur & 0x0f;
us <<= 6;
us += utf8[0] & 0x3f;
us <<= 6;
us += utf8[1] & 0x3f;
*cur++ = '\\';
*cur++ = 'u';
cur[3] = hex[us & 0x0f];
us >>= 4;
cur[2] = hex[us & 0x0f];
us >>= 4;
cur[1] = hex[us & 0x0f];
us >>= 4;
cur[0] = hex[us & 0x0f];
cur += 3;
utf8 += 2;
}
}
cur++;
}
*cur++ = 0;
return unic;
}
/* Render the cstring provided to an escaped version that can be printed. */
static char *print_string_ptr(const char *str,printbuffer *p)
{
const char *ptr;char *ptr2,*out;int len=0,flag=0;unsigned char token;
for (ptr=str;*ptr;ptr++) flag|=((*ptr>0 && *ptr<32)||(*ptr=='\"')||(*ptr=='\\'))?1:0;
if (!flag)
{
len=ptr-str;
if (p) out=ensure(p,len+3);
else out=(char*)cJSON_malloc(len+3);
if (!out) return 0;
ptr2=out;*ptr2++='\"';
strcpy(ptr2,str);
ptr2[len]='\"';
ptr2[len+1]=0;
return out;
}
if (!str)
{
if (p) out=ensure(p,3);
else out=(char*)cJSON_malloc(3);
if (!out) return 0;
strcpy(out,"\"\"");
return out;
}
ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
if (p) out=ensure(p,len+3);
else out=(char*)cJSON_malloc(len+3);
if (!out) return 0;
ptr2=out;ptr=str;
*ptr2++='\"';
while (*ptr)
{
if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
else
{
*ptr2++='\\';
switch (token=*ptr++)
{
case '\\': *ptr2++='\\'; break;
case '\"': *ptr2++='\"'; break;
case '\b': *ptr2++='b'; break;
case '\f': *ptr2++='f'; break;
case '\n': *ptr2++='n'; break;
case '\r': *ptr2++='r'; break;
case '\t': *ptr2++='t'; break;
default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */
}
}
}
*ptr2++='\"';*ptr2++=0;
return out;
}
/* Invote print_string_ptr (which is useful) on an item. */
static char *print_string(cJSON *item,printbuffer *p) {return print_string_ptr(item->valuestring,p);}
/* Predeclare these prototypes. */
static const char *parse_value(cJSON *item,const char *value);
static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p);
static const char *parse_array(cJSON *item,const char *value);
static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p);
static const char *parse_object(cJSON *item,const char *value);
static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p);
/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
/* Parse an object - create a new root, and populate. */
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
{
const char *end=0;
cJSON *c=cJSON_New_Item();
ep=0;
if (!c) return 0; /* memory fail */
end=parse_value(c,skip(value));
if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */
/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}
if (return_parse_end) *return_parse_end=end;
return c;
}
/* Default options for cJSON_Parse */
cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
/* Render a cJSON item/entity/structure to text. */
char *cJSON_Print(cJSON *item) {return print_value(item,0,1,0);}
char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0,0);}
char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt)
{
printbuffer p;
p.buffer=(char*)cJSON_malloc(prebuffer);
p.length=prebuffer;
p.offset=0;
return print_value(item,0,fmt,&p);
return p.buffer;
}
/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(cJSON *item,const char *value)
{
if (!value) return 0; /* Fail on null. */
if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
if (*value=='\"') { return parse_string(item,value); }
if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
if (*value=='[') { return parse_array(item,value); }
if (*value=='{') { return parse_object(item,value); }
ep=value;return 0; /* failure. */
}
/* Render a value to text. */
static char *print_value(cJSON *item,int depth,int fmt,printbuffer *p)
{
char *out=0;
if (!item) return 0;
if (p)
{
switch ((item->type)&255)
{
case cJSON_NULL: {out=ensure(p,5); if (out) strcpy(out,"null"); break;}
case cJSON_False: {out=ensure(p,6); if (out) strcpy(out,"false"); break;}
case cJSON_True: {out=ensure(p,5); if (out) strcpy(out,"true"); break;}
case cJSON_Number: out=print_number(item,p);break;
case cJSON_String: out=print_string(item,p);break;
case cJSON_Array: out=print_array(item,depth,fmt,p);break;
case cJSON_Object: out=print_object(item,depth,fmt,p);break;
}
}
else
{
switch ((item->type)&255)
{
case cJSON_NULL: out=cJSON_strdup("null"); break;
case cJSON_False: out=cJSON_strdup("false");break;
case cJSON_True: out=cJSON_strdup("true"); break;
case cJSON_Number: out=print_number(item,0);break;
case cJSON_String: out=print_string(item,0);break;
case cJSON_Array: out=print_array(item,depth,fmt,0);break;
case cJSON_Object: out=print_object(item,depth,fmt,0);break;
}
}
return out;
}
/* Build an array from input text. */
static const char *parse_array(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='[') {ep=value;return 0;} /* not an array! */
item->type=cJSON_Array;
value=skip(value+1);
if (*value==']') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0; /* memory fail */
value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_value(child,skip(value+1)));
if (!value) return 0; /* memory fail */
}
if (*value==']') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an array to text */
static char *print_array(cJSON *item,int depth,int fmt,printbuffer *p)
{
char **entries;
char *out=0,*ptr,*ret;int len=5;
cJSON *child=item->child;
int numentries=0,i=0,fail=0;
size_t tmplen=0;
/* How many entries in the array? */
while (child) numentries++,child=child->next;
/* Explicitly handle numentries==0 */
if (!numentries)
{
if (p) out=ensure(p,3);
else out=(char*)cJSON_malloc(3);
if (out) strcpy(out,"[]");
return out;
}
if (p)
{
/* Compose the output array. */
i=p->offset;
ptr=ensure(p,1);if (!ptr) return 0; *ptr='['; p->offset++;
child=item->child;
while (child && !fail)
{
print_value(child,depth+1,fmt,p);
p->offset=update(p);
if (child->next) {len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;p->offset+=len;}
child=child->next;
}
ptr=ensure(p,2);if (!ptr) return 0; *ptr++=']';*ptr=0;
out=(p->buffer)+i;
}
else
{
/* Allocate an array to hold the values for each */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
memset(entries,0,numentries*sizeof(char*));
/* Retrieve all the results: */
child=item->child;
while (child && !fail)
{
ret=print_value(child,depth+1,fmt,0);
entries[i++]=ret;
if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
child=child->next;
}
/* If we didn't fail, try to ask_memory the output string */
if (!fail) out=(char*)cJSON_malloc(len);
/* If that fails, we fail. */
if (!out) fail=1;
/* Handle failure. */
if (fail)
{
for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
cJSON_free(entries);
return 0;
}
/* Compose the output array. */
*out='[';
ptr=out+1;*ptr=0;
for (i=0;i<numentries;i++)
{
tmplen=strlen(entries[i]);memcpy(ptr,entries[i],tmplen);ptr+=tmplen;
if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
cJSON_free(entries[i]);
}
cJSON_free(entries);
*ptr++=']';*ptr++=0;
}
return out;
}
/* Build an object from the text. */
static const char *parse_object(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='{') {ep=value;return 0;} /* not an object! */
item->type=cJSON_Object;
value=skip(value+1);
if (*value=='}') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0;
value=skip(parse_string(child,skip(value)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_string(child,skip(value+1)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
}
if (*value=='}') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
/* Render an object to text. */
static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p)
{
char **entries=0,**names=0;
char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
cJSON *child=item->child;
int numentries=0,fail=0;
size_t tmplen=0;
/* Count the number of entries. */
while (child) numentries++,child=child->next;
/* Explicitly handle empty object case */
if (!numentries)
{
if (p) out=ensure(p,fmt?depth+4:3);
else out=(char*)cJSON_malloc(fmt?depth+4:3);
if (!out) return 0;
ptr=out;*ptr++='{';
if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}
*ptr++='}';*ptr++=0;
return out;
}
if (p)
{
/* Compose the output: */
i=p->offset;
len=fmt?2:1; ptr=ensure(p,len+1); if (!ptr) return 0;
*ptr++='{'; if (fmt) *ptr++='\n'; *ptr=0; p->offset+=len;
child=item->child;depth++;
while (child)
{
if (fmt)
{
ptr=ensure(p,depth); if (!ptr) return 0;
for (j=0;j<depth;j++) *ptr++='\t';
p->offset+=depth;
}
print_string_ptr(child->string,p);
p->offset=update(p);
len=fmt?2:1;
ptr=ensure(p,len); if (!ptr) return 0;
*ptr++=':';if (fmt) *ptr++='\t';
p->offset+=len;
print_value(child,depth,fmt,p);
p->offset=update(p);
len=(fmt?1:0)+(child->next?1:0);
ptr=ensure(p,len+1); if (!ptr) return 0;
if (child->next) *ptr++=',';
if (fmt) *ptr++='\n';*ptr=0;
p->offset+=len;
child=child->next;
}
ptr=ensure(p,fmt?(depth+1):2); if (!ptr) return 0;
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
*ptr++='}';*ptr=0;
out=(p->buffer)+i;
}
else
{
/* Allocate space for the names and the objects */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
names=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!names) {cJSON_free(entries);return 0;}
memset(entries,0,sizeof(char*)*numentries);
memset(names,0,sizeof(char*)*numentries);
/* Collect all the results into our arrays: */
child=item->child;depth++;if (fmt) len+=depth;
while (child)
{
names[i]=str=print_string_ptr(child->string,0);
entries[i++]=ret=print_value(child,depth,fmt,0);
if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
child=child->next;
}
/* Try to allocate the output string */
if (!fail) out=(char*)cJSON_malloc(len);
if (!out) fail=1;
/* Handle failure */
if (fail)
{
for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
cJSON_free(names);cJSON_free(entries);
return 0;
}
/* Compose the output: */
*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
for (i=0;i<numentries;i++)
{
if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
tmplen=strlen(names[i]);memcpy(ptr,names[i],tmplen);ptr+=tmplen;
*ptr++=':';if (fmt) *ptr++='\t';
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
if (i!=numentries-1) *ptr++=',';
if (fmt) *ptr++='\n';*ptr=0;
cJSON_free(names[i]);cJSON_free(entries[i]);
}
cJSON_free(names);cJSON_free(entries);
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
*ptr++='}';*ptr++=0;
}
return out;
}
/* Get Array size/item / object item. */
int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;}
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
/* Utility for array list handling. */
static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
/* Utility for handling references. */
static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
/* Add item to array/object. */
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (!(item->type&cJSON_StringIsConst) && item->string) cJSON_free(item->string);item->string=(char*)string;item->type|=cJSON_StringIsConst;cJSON_AddItemToArray(object,item);}
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));}
void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));}
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
/* Replace array/object items with new ones. */
void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) {cJSON_AddItemToArray(array,newitem);return;}
newitem->next=c;newitem->prev=c->prev;c->prev=newitem;if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;}
void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
/* Create basic types: */
cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
/* Create Arrays: */
cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
/* Duplication */
cJSON *cJSON_Duplicate(cJSON *item,int recurse)
{
cJSON *newitem,*cptr,*nptr=0,*newchild;
/* Bail on bad ptr */
if (!item) return 0;
/* Create new item */
newitem=cJSON_New_Item();
if (!newitem) return 0;
/* Copy over all vars */
newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble;
if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}}
if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}}
/* If non-recursive, then we're done! */
if (!recurse) return newitem;
/* Walk the ->next chain for the child. */
cptr=item->child;
while (cptr)
{
newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */
if (!newchild) {cJSON_Delete(newitem);return 0;}
if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */
else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */
cptr=cptr->next;
}
return newitem;
}
void cJSON_Minify(char *gb_json)
{
char *into=gb_json;
while (*gb_json)
{
if (*gb_json==' ') gb_json++;
else if (*gb_json=='\t') gb_json++; /* Whitespace characters. */
else if (*gb_json=='\r') gb_json++;
else if (*gb_json=='\n') gb_json++;
else if (*gb_json=='/' && gb_json[1]=='/') while (*gb_json && *gb_json!='\n') gb_json++; /* double-slash comments, to end of line. */
else if (*gb_json=='/' && gb_json[1]=='*') {while (*gb_json && !(*gb_json=='*' && gb_json[1]=='/')) gb_json++;gb_json+=2;} /* multiline comments. */
else if (*gb_json=='\"'){*into++=*gb_json++;while (*gb_json && *gb_json!='\"'){if (*gb_json=='\\') *into++=*gb_json++;*into++=*gb_json++;}*into++=*gb_json++;} /* string literals, which are \" sensitive. */
else *into++=*gb_json++; /* All other characters. */
}
*into=0; /* and null-terminate. */
}

157
sdk/json/cJSON.h Normal file
View File

@ -0,0 +1,157 @@
/*
Copyright (c) 2009 Dave Gamble
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#ifndef cJSON__h
#define cJSON__h
#include <stdlib.h>
#ifdef __cplusplus
extern "C"
{
#endif
/* cJSON Types: */
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
typedef void *(*malloc_fnxx)(size_t sz);
typedef void (*free_fnxx)(void *ptr);
/* The cJSON structure: */
typedef struct cJSON {
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
int type; /* The type of the item, as above. */
char *valuestring; /* The item's string, if type==cJSON_String */
int valueint; /* The item's number, if type==cJSON_Number */
double valuedouble; /* The item's number, if type==cJSON_Number */
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;
typedef struct cJSON_Hooks {
malloc_fnxx malloc_fn;
free_fnxx free_fn;
} cJSON_Hooks;
/* Supply malloc, realloc and free functions to cJSON */
extern void cJSON_InitHooks(cJSON_Hooks* hooks);
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
extern cJSON *cJSON_Parse(const char *value);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
extern char *cJSON_Print(cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
extern char *cJSON_PrintUnformatted(cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);
/* Delete a cJSON entity and all subentities. */
extern void cJSON_Delete(cJSON *c);
/* Returns the number of items in an array (or object). */
extern int cJSON_GetArraySize(cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
/* Get item "string" from object. Case insensitive. */
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
extern const char *cJSON_GetErrorPtr(void);
/* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
extern cJSON *cJSON_CreateArray(void);
extern cJSON *cJSON_CreateObject(void);
/* These utilities create an Array of count items. */
extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
/* Append item to the specified array/object. */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item); /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
/* Remove/Detatch items from Arrays/Objects. */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
/* Update array items. */
extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* Shifts pre-existing items to the right. */
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
need to be released. With recurse!=0, it will duplicate any children connected to the item.
The item->next and ->prev pointers are always zero on return from Duplicate. */
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
extern void cJSON_Minify(char *gb_json);
// convert e681a2 to \u6062, call 'free' to free the returned value
extern char* cJSON_utf8_2_unic(const char* utf8);
/* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#ifdef __cplusplus
}
#endif
#endif

962
sdk/json/gb_json.cpp Normal file
View File

@ -0,0 +1,962 @@
#include "gb_json.h"
#include "cJSON.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
namespace special_char_trans
{
struct
{
const char* writedown_text;
char readable_char;
}transferred_chars[] = { { "\\\"", '\"' }
, { "\\'", '\'' }
, { "\\a", '\a' }
, { "\\b", '\b' }
, { "\\f", '\f' }
, { "\\n", '\n' }
, { "\\r", '\r' }
, { "\\t", '\t' }
, { "\\v", '\v' }
// , { "\\?", '\?' }
, { "\\\\", '\\' }
, { "\\/", '/' }
// , { "\\0", '\0' }
};
void to_writedown(std::string& str)
{
std::string trans(str);
const char* ptr = trans.c_str();
str.clear();
while (*ptr)
{
bool rep = false;
if (*ptr == '\\')
{
if (ptr[1] == '\\')
{
str += "\\\\";
ptr++;
rep = true;
}
else if( ptr[1] == '/' ||
ptr[1] == 'a' ||
ptr[1] == 'b' ||
ptr[1] == 'f' ||
ptr[1] == 'n' ||
ptr[1] == 'r' ||
ptr[1] == 't' ||
ptr[1] == 'u' ||
ptr[1] == 'v')
{
str += "\\";
ptr++;
}
else
{
str += "\\\\";
rep = true;
}
}
else
{
for (size_t i = 0; i < sizeof(transferred_chars) / sizeof(transferred_chars[0]); ++i)
{
if (*ptr == transferred_chars[i].readable_char)
{
str += transferred_chars[i].writedown_text;
rep = true;
break;
}
}
}
if (!rep)
str.append(1, *ptr);
ptr++;
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// life callback ...
#ifdef DUMP_JSON_OBJECT_LIFE
static void record_life_empty(gb_json*, bool, void*)
{}
static void(*g_life)(gb_json*, bool, void*) = &record_life_empty;
static void* g_life_param = nullptr;
void set_gbjson_life_callback(void(*life)(gb_json*, bool, void*), void* param)
{
g_life_param = param;
g_life = life ? life : &record_life_empty;
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// gb_json ...
gb_json::gb_json(char* json_txt) : ref_(1), type_(VAL_TYPE_OBJECT), key_(""), strval_(""), cur_child_(-1)
{
#ifdef DUMP_JSON_OBJECT_LIFE
g_life(this, true, g_life_param);
#endif
simple_val_.dval = .0f;
if(json_txt)
attach_text(json_txt);
}
gb_json::gb_json(const char* key, bool val) : ref_(1), type_(VAL_TYPE_BOOL), key_(key ? key : ""), strval_(""), cur_child_(-1)
{
#ifdef DUMP_JSON_OBJECT_LIFE
g_life(this, true, g_life_param);
#endif
simple_val_.bval = val;
}
gb_json::gb_json(const char* key, int val) : ref_(1), type_(VAL_TYPE_INT), key_(key ? key : ""), strval_(""), cur_child_(-1)
{
#ifdef DUMP_JSON_OBJECT_LIFE
g_life(this, true, g_life_param);
#endif
simple_val_.nval = val;
}
gb_json::gb_json(const char* key, double val) : ref_(1), type_(VAL_TYPE_FLOAT), key_(key ? key : ""), strval_(""), cur_child_(-1)
{
#ifdef DUMP_JSON_OBJECT_LIFE
g_life(this, true, g_life_param);
#endif
simple_val_.dval = val;
}
gb_json::gb_json(const char* key, const char* val) : ref_(1), type_(VAL_TYPE_STRING), key_(key ? key : ""), strval_(val ? val : ""), cur_child_(-1)
{
#ifdef DUMP_JSON_OBJECT_LIFE
g_life(this, true, g_life_param);
#endif
}
gb_json::gb_json(const char* key, const std::string& val) : ref_(1), type_(VAL_TYPE_STRING), key_(key ? key : ""), strval_(val), cur_child_(-1)
{
#ifdef DUMP_JSON_OBJECT_LIFE
g_life(this, true, g_life_param);
#endif
}
gb_json::~gb_json()
{
clear();
#ifdef DUMP_JSON_OBJECT_LIFE
g_life(this, false, g_life_param);
#endif
}
std::string gb_json::object_key(gb_json* jsn)
{
return "\"" + jsn->key() + "\":";
}
std::string gb_json::array_key(gb_json* jsn)
{
return "";
}
void gb_json::from_cjson(cJSON* cj)
{
key_ = cj && cj->string ? cj->string : "";
while (cj)
{
gb_json* child = nullptr;
if (cj->type == cJSON_True)
{
child = new gb_json(cj->string, true);
}
else if(cj->type == cJSON_False)
{
child = new gb_json(cj->string, false);
}
else if (cj->type == cJSON_Number)
{
if (cj->valuedouble - (int)cj->valuedouble < .00001)
{
child = new gb_json(cj->string, cj->valueint);
}
else
{
child = new gb_json(cj->string, cj->valuedouble);
}
}
else if (cj->type == cJSON_String)
{
child = new gb_json(cj->string, cj->valuestring);
}
else if (cj->type == cJSON_Object || cj->type == cJSON_Array)
{
child = new gb_json();
child->from_cjson(cj->child);
child->key_ = cj->string ? cj->string : "";
if (child->key_.empty())
child->type_ = VAL_TYPE_OBJECT;
}
arr_val_.push_back(child);
cj = cj->next;
}
//if (arr_val_.size() == 1 && arr_val_[0]->arr_val_.size() == 0)
//{
// gb_json* child = arr_val_[0];
//
// if (!child->key_.empty()) // array
// {
// arr_val_.clear();
// type_ = child->type_;
// key_ = child->key_;
// simple_val_.dval = child->simple_val_.dval;
// strval_ = child->strval_;
// for (auto& v : child->arr_val_)
// arr_val_.push_back(v);
// child->arr_val_.clear();
// child->release();
// }
//}
if (arr_val_.size())
{
type_ = arr_val_[0]->key().empty() ? VAL_TYPE_ARRAY : VAL_TYPE_OBJECT;
}
}
gb_json* gb_json::find_child(const char* key, bool remove)
{
gb_json* ret = nullptr;
if (type_ == VAL_TYPE_OBJECT)
{
for (size_t i = 0; i < arr_val_.size(); ++i)
{
if (arr_val_[i]->key() == key)
{
ret = arr_val_[i];
if (remove)
arr_val_.erase(arr_val_.begin() + i);
else
ret->add_ref();
break;
}
}
}
return ret;
}
int32_t gb_json::add_ref()
{
std::lock_guard<std::mutex> lock(ref_mutex_);
int32_t ref = ++ref_;
return ref;
}
int32_t gb_json::release()
{
int32_t ref = 0;
{
std::lock_guard<std::mutex> lock(ref_mutex_);
ref = --ref_;
}
if (ref == 0)
delete this;
return ref;
}
static void check_cJSON(cJSON* root, std::vector<cJSON*>* existing)
{
if (!root)
return;
for (auto& v : *existing)
{
if (v == root)
{
printf("cJSON* 0x%08x is repeat!\n", v);
break;
}
}
existing->push_back(root);
check_cJSON(root->child, existing);
cJSON* next = root->next;
while (next)
{
check_cJSON(next, existing);
next = next->next;
}
}
bool gb_json::attach_text(char* json_txt)
{
clear();
cJSON* jsn = cJSON_Parse(json_txt);
if (jsn)
{
char *text = cJSON_Print(jsn);
if (text)
{
if (0)
{
FILE* dst = fopen("e:\\test-json.txt", "wb");
fwrite(text, 1, strlen(text), dst);
fclose(dst);
}
free(text);
}
std::vector<cJSON*> repeat;
if(0)
check_cJSON(jsn, &repeat);
from_cjson(jsn->child);
cJSON_Delete(jsn);
return true;
}
return false;
}
void gb_json::clear(bool as_array)
{
if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT)
{
for (auto& v : arr_val_)
v->release();
}
type_ = as_array ? VAL_TYPE_ARRAY : VAL_TYPE_OBJECT;
simple_val_.dval = .0f;
key_ = "";
strval_ = "";
arr_val_.clear();
cur_child_ = -1;
}
std::string gb_json::to_string(void)
{
if (type_ == VAL_TYPE_NULL)
return "";
if (type_ == VAL_TYPE_BOOL)
return (simple_val_.bval ? "true" : "false");
if (type_ == VAL_TYPE_INT)
return std::to_string(simple_val_.nval);
if (type_ == VAL_TYPE_FLOAT)
return std::to_string(simple_val_.dval);
if (type_ == VAL_TYPE_STRING)
{
char* u = cJSON_utf8_2_unic(strval_.c_str());
std::string r(u);
free(u);
special_char_trans::to_writedown(r);
return "\"" + r + "\"";
}
std::string(*k)(gb_json*) = type_ == VAL_TYPE_OBJECT ? gb_json::object_key : gb_json::array_key;
std::string str(type_ == VAL_TYPE_OBJECT ? "{" : "[");
if (arr_val_.size())
{
str += k(arr_val_[0]) + arr_val_[0]->to_string();
for(size_t i = 1; i < arr_val_.size(); ++i)
str += "," + k(arr_val_[i]) + arr_val_[i]->to_string();
}
str += type_ == VAL_TYPE_OBJECT ? "}" : "]";
return str;
}
std::string& gb_json::key(void)
{
return key_;
}
bool gb_json::is_array(void)
{
return type_ == VAL_TYPE_ARRAY;
}
bool gb_json::is_leaf_node(void)
{
return type_ == VAL_TYPE_BOOL ||
type_ == VAL_TYPE_INT ||
type_ == VAL_TYPE_FLOAT ||
type_ == VAL_TYPE_STRING;
}
bool gb_json::get_value(const char* key, bool& val)
{
bool ret = false;
gb_json* child = find_child(key);
if (child)
{
if (child->type_ == VAL_TYPE_BOOL)
{
val = child->simple_val_.bval;
ret = true;
}
child->release();
}
else if (type_ == VAL_TYPE_BOOL && key_ == key)
{
val = simple_val_.bval;
ret = true;
}
return ret;
}
bool gb_json::get_value(const char* key, int& val)
{
bool ret = false;
gb_json* child = find_child(key);
if (child)
{
if (child->type_ == VAL_TYPE_INT)
{
val = child->simple_val_.nval;
ret = true;
}
child->release();
}
else if (type_ == VAL_TYPE_INT && key_ == key)
{
val = simple_val_.nval;
ret = true;
}
return ret;
}
bool gb_json::get_value(const char* key, double& val)
{
bool ret = false;
gb_json* child = find_child(key);
if (child)
{
if (child->type_ == VAL_TYPE_FLOAT)
{
val = child->simple_val_.dval;
ret = true;
}
child->release();
}
else if (type_ == VAL_TYPE_FLOAT && key_ == key)
{
val = simple_val_.dval;
ret = true;
}
// added on 2023-04-27: for cJSON consider both int and float as CJSON_Number, we consider int if the value is just an integer
if(!ret)
{
int v = 0;
ret = get_value(key, v);
if(ret)
val = v;
}
return ret;
}
bool gb_json::get_value(const char* key, std::string& val)
{
bool ret = false;
gb_json* child = find_child(key);
if (child)
{
if (child->type_ == VAL_TYPE_STRING)
{
val = child->strval_;
ret = true;
}
child->release();
}
else if (type_ == VAL_TYPE_STRING && key_ == key)
{
val = strval_;
ret = true;
}
return ret;
}
bool gb_json::get_value(const char* key, gb_json*& val)
{
bool ret = false;
gb_json *child = find_child(key);
if (child)
{
if (child->type_ == VAL_TYPE_OBJECT || child->type_ == VAL_TYPE_ARRAY)
{
val = child;
ret = true;
}
else
{
child->release();
}
}
return ret;
}
size_t gb_json::children(void)
{
if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT)
return arr_val_.size();
else
return -1;
}
gb_json* gb_json::child(size_t ind)
{
if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT)
{
if (ind >= 0 && ind < arr_val_.size())
{
arr_val_[ind]->add_ref();
return arr_val_[ind];
}
}
return nullptr;
}
gb_json* gb_json::first_child(void)
{
if (type_ == VAL_TYPE_OBJECT || type_ == VAL_TYPE_ARRAY)
{
cur_child_ = 0;
if (arr_val_.size())
{
arr_val_[0]->add_ref();
return arr_val_[0];
}
}
return nullptr;
}
gb_json* gb_json::next_child(void)
{
if (type_ == VAL_TYPE_OBJECT || type_ == VAL_TYPE_ARRAY)
{
if (++cur_child_ < arr_val_.size())
{
arr_val_[cur_child_]->add_ref();
return arr_val_[cur_child_];
}
}
return nullptr;
}
bool gb_json::set_value(const char* key, bool val)
{
if (type_ != VAL_TYPE_OBJECT)
return false;
gb_json* child = find_child(key);
if (child)
{
child->clear();
child->type_ = VAL_TYPE_BOOL;
child->key() = key ? key : "";
child->simple_val_.bval = val;
child->release();
}
else
{
child = new gb_json(key, val);
arr_val_.push_back(child);
}
return true;
}
bool gb_json::set_value(const char* key, int val)
{
if (type_ != VAL_TYPE_OBJECT)
return false;
gb_json* child = find_child(key);
if (child)
{
child->clear();
child->type_ = VAL_TYPE_INT;
child->key() = key ? key : "";
child->simple_val_.nval = val;
child->release();
}
else
{
child = new gb_json(key, val);
arr_val_.push_back(child);
}
return true;
}
bool gb_json::set_value(const char* key, double val)
{
if (type_ != VAL_TYPE_OBJECT)
return false;
gb_json* child = find_child(key);
if (child)
{
child->clear();
child->type_ = VAL_TYPE_FLOAT;
child->key() = key ? key : "";
child->simple_val_.dval = val;
child->release();
}
else
{
child = new gb_json(key, val);
arr_val_.push_back(child);
}
return true;
}
bool gb_json::set_value(const char* key, const char* val)
{
if (type_ != VAL_TYPE_OBJECT)
return false;
gb_json* child = find_child(key);
if (child)
{
child->clear();
child->type_ = VAL_TYPE_STRING;
child->key() = key ? key : "";
child->strval_ = val ? val : "";
child->release();
}
else
{
child = new gb_json(key, val);
arr_val_.push_back(child);
}
return true;
}
bool gb_json::set_value(const char* key, gb_json* val)
{
if (type_ != VAL_TYPE_OBJECT)
return false;
for (size_t i = 0; i < arr_val_.size(); ++i)
{
if (arr_val_[i]->key() == key)
{
arr_val_[i]->release();
arr_val_[i] = val;
val->add_ref();
return true;
}
}
arr_val_.push_back(val);
val->key() = key;
val->add_ref();
return true;
}
gb_json& gb_json::operator+=(bool val)
{
if (type_ == VAL_TYPE_ARRAY)
{
gb_json* child = new gb_json(nullptr, val);
arr_val_.push_back(child);
}
return *this;
}
gb_json& gb_json::operator+=(int val)
{
if (type_ == VAL_TYPE_ARRAY)
{
gb_json* child = new gb_json(nullptr, val);
arr_val_.push_back(child);
}
return *this;
}
gb_json& gb_json::operator+=(double val)
{
if (type_ == VAL_TYPE_ARRAY)
{
gb_json* child = new gb_json(nullptr, val);
arr_val_.push_back(child);
}
return *this;
}
gb_json& gb_json::operator+=(const char* val)
{
if (type_ == VAL_TYPE_ARRAY)
{
gb_json* child = new gb_json(nullptr, val);
arr_val_.push_back(child);
}
return *this;
}
gb_json& gb_json::operator+=(gb_json* val)
{
if (type_ == VAL_TYPE_ARRAY)
{
val->add_ref();
arr_val_.push_back(val);
}
return *this;
}
gb_json& gb_json::operator-=(int ind)
{
remove(ind);
return *this;
}
bool gb_json::remove(const char* key)
{
gb_json* child = find_child(key, true);
if (child)
{
child->release();
return true;
}
else
{
return false;
}
}
bool gb_json::remove(gb_json* child)
{
if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT)
{
for (size_t i = 0; i < arr_val_.size(); ++i)
{
if (arr_val_[i] == child)
{
arr_val_[i]->release();
arr_val_.erase(arr_val_.begin() + i);
return true;
}
}
}
return false;
}
bool gb_json::remove(int ind)
{
bool ret = false;
if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT)
{
if (ind >= 0 && ind < arr_val_.size())
{
arr_val_[ind]->release();
arr_val_.erase(arr_val_.begin() + ind);
ret = true;
}
}
return ret;
}
int gb_json::index(gb_json* child)
{
if (type_ == VAL_TYPE_ARRAY || type_ == VAL_TYPE_OBJECT)
{
for (int i = 0; i < arr_val_.size(); ++i)
{
if (arr_val_[i] == child)
return i;
}
}
return -1;
}
int gb_json::index_move_to(gb_json* child, int ind)
{
int i = index(child);
if (i == -1)
return -1;
arr_val_.erase(arr_val_.begin() + i);
if (ind < 0)
ind = 0;
if (ind > arr_val_.size())
ind = arr_val_.size();
arr_val_.insert(arr_val_.begin() + ind, child);
return ind;
}
int gb_json::insert(int ind, const char* key, gb_json* child)
{
int i = index(child);
if (i == -1)
{
if (ind < 0)
ind = 0;
else if (ind > arr_val_.size())
ind = arr_val_.size();
child->key() = key ? key : "";
arr_val_.insert(arr_val_.begin() + ind, child);
child->add_ref();
}
else if(i != ind)
{
arr_val_.erase(arr_val_.begin() + i);
if (ind < 0)
ind = 0;
if (ind > arr_val_.size())
ind = arr_val_.size();
child->key() = key ? key : "";
arr_val_.insert(arr_val_.begin() + ind, child);
}
return ind;
}
bool gb_json::value(bool& val)
{
bool ret = false;
if (is_leaf_node() && type_ == VAL_TYPE_BOOL)
{
val = simple_val_.bval;
ret = true;
}
return ret;
}
bool gb_json::value(int& val)
{
bool ret = false;
if (is_leaf_node() && type_ == VAL_TYPE_INT)
{
val = simple_val_.nval;
ret = true;
}
return ret;
}
bool gb_json::value(double& val)
{
bool ret = false;
if (is_leaf_node() && type_ == VAL_TYPE_FLOAT)
{
val = simple_val_.dval;
ret = true;
}
return ret;
}
bool gb_json::value(std::string& val)
{
bool ret = false;
if (is_leaf_node() && type_ == VAL_TYPE_STRING)
{
val = strval_;
ret = true;
}
return ret;
}
gb_json& gb_json::operator=(bool val)
{
if (is_leaf_node())
{
simple_val_.bval = val;
type_ = VAL_TYPE_BOOL;
}
return *this;
}
gb_json& gb_json::operator=(int val)
{
if (is_leaf_node())
{
simple_val_.nval = val;
type_ = VAL_TYPE_INT;
}
return *this;
}
gb_json& gb_json::operator=(double val)
{
if (is_leaf_node())
{
simple_val_.dval = val;
type_ = VAL_TYPE_FLOAT;
}
return *this;
}
gb_json& gb_json::operator=(const char* val)
{
if (is_leaf_node())
{
strval_ = val ? val : "";
type_ = VAL_TYPE_STRING;
}
return *this;
}
bool gb_json::operator==(const gb_json& r)
{
if (type_ != r.type_)
return false;
if (type_ == VAL_TYPE_BOOL)
return simple_val_.bval == r.simple_val_.bval;
if (type_ == VAL_TYPE_INT)
return simple_val_.nval == r.simple_val_.nval;
if (type_ == VAL_TYPE_FLOAT)
return fabs(simple_val_.dval - r.simple_val_.dval) < .00001;
if (type_ == VAL_TYPE_STRING)
return strval_ == r.strval_;
if (arr_val_.size() != r.arr_val_.size())
return false;
for (int i = 0; i < arr_val_.size(); ++i)
{
if (!(*arr_val_[i] == *r.arr_val_[i]))
return false;
}
return true;
}
bool gb_json::operator!=(const gb_json& r)
{
return !(*this == r);
}

122
sdk/json/gb_json.h Normal file
View File

@ -0,0 +1,122 @@
#pragma once
#include <vector>
#include <string>
#include <mutex>
struct cJSON;
class gb_json
{
volatile int32_t ref_;
std::mutex ref_mutex_;
enum val_type
{
VAL_TYPE_NULL = 0,
VAL_TYPE_BOOL,
VAL_TYPE_INT,
VAL_TYPE_FLOAT,
VAL_TYPE_STRING,
VAL_TYPE_OBJECT,
VAL_TYPE_ARRAY,
};
val_type type_;
std::string key_;
union
{
bool bval;
int nval;
double dval;
}simple_val_;
std::string strval_;
std::vector<gb_json*> arr_val_;
size_t cur_child_;
static std::string object_key(gb_json* jsn);
static std::string array_key(gb_json* jsn);
void from_cjson(cJSON* cj);
gb_json* find_child(const char* key, bool remove = false);
public:
gb_json(char* json_txt = 0);
gb_json(const char* key, bool val);
gb_json(const char* key, int val);
gb_json(const char* key, double val);
gb_json(const char* key, const char* val);
gb_json(const char* key, const std::string& val);
protected:
~gb_json();
public:
int32_t add_ref();
int32_t release();
public:
// parse/un-parse ...
bool attach_text(char* json_txt);
void clear(bool as_array = false);
std::string to_string(void);
// attributes ...
std::string& key(void);
bool is_array(void);
bool is_leaf_node(void); // whether this object is a leaf node contains final value
// value access ...
bool get_value(const char* key, bool& val);
bool get_value(const char* key, int& val);
bool get_value(const char* key, double& val);
bool get_value(const char* key, std::string& val);
bool get_value(const char* key, gb_json*& val);
// enumeration ...
size_t children(void); // return children count if was object or array, or else -1 returned
gb_json* child(size_t ind);
gb_json* first_child(void);
gb_json* next_child(void);
// change the item matching 'key', otherwise add a new item
bool set_value(const char* key, bool val);
bool set_value(const char* key, int val);
bool set_value(const char* key, double val);
bool set_value(const char* key, const char* val);
bool set_value(const char* key, gb_json* val);
// operator+= only for array
gb_json& operator+=(bool val);
gb_json& operator+=(int val);
gb_json& operator+=(double val);
gb_json& operator+=(const char* val);
gb_json& operator+=(gb_json* val);
// remove item
gb_json& operator-=(int ind);
bool remove(const char* key);
bool remove(gb_json* child);
bool remove(int ind);
// position management, return index
int index(gb_json* child);
int index_move_to(gb_json* child, int ind);
int insert(int ind, const char* key, gb_json* child);
// leaf node value ...
bool value(bool& val);
bool value(int& val);
bool value(double& val);
bool value(std::string& val);
gb_json& operator=(bool val);
gb_json& operator=(int val);
gb_json& operator=(double val);
gb_json& operator=(const char* val);
bool operator==(const gb_json& r);
bool operator!=(const gb_json& r);
};
#ifdef DUMP_JSON_OBJECT_LIFE
void set_gbjson_life_callback(void(*life)(gb_json*, bool, void*), void* param);
#endif

252
sdk/sane/sane.h Normal file
View File

@ -0,0 +1,252 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 1997-1999 David Mosberger-Tang and Andreas Beck
This file is part of the SANE package.
This file is in the public domain. You may use and modify it as
you see fit, as long as this copyright message is included and
that there is an indication as to what modifications have been
made (if any).
SANE is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.
This file declares SANE application interface. See the SANE
standard for a detailed explanation of the interface. */
#ifndef sane_h
#define sane_h
#ifndef BACKEND_NAME
#define BACKEND_NAME hgsane
#endif
#ifdef __cplusplus
extern "C" {
#endif
/*
* SANE types and defines
*/
#define SANE_CURRENT_MAJOR 1
#define SANE_CURRENT_MINOR 0
#define SANE_VERSION_CODE(major, minor, build) \
( (((SANE_Word) (major) & 0xff) << 24) \
| (((SANE_Word) (minor) & 0xff) << 16) \
| (((SANE_Word) (build) & 0xffff) << 0))
#define SANE_VERSION_MAJOR(code) ((((SANE_Word)(code)) >> 24) & 0xff)
#define SANE_VERSION_MINOR(code) ((((SANE_Word)(code)) >> 16) & 0xff)
#define SANE_VERSION_BUILD(code) ((((SANE_Word)(code)) >> 0) & 0xffff)
#define SANE_FALSE 0
#define SANE_TRUE 1
typedef unsigned char SANE_Byte;
typedef int SANE_Word;
typedef SANE_Word SANE_Bool;
typedef SANE_Word SANE_Int;
typedef char SANE_Char;
typedef SANE_Char *SANE_String;
typedef const SANE_Char *SANE_String_Const;
typedef void *SANE_Handle;
typedef SANE_Word SANE_Fixed;
#define SANE_FIXED_SCALE_SHIFT 16
#define SANE_FIX(v) ((SANE_Word) ((v) * (1 << SANE_FIXED_SCALE_SHIFT)))
#define SANE_UNFIX(v) ((double)(v) / (1 << SANE_FIXED_SCALE_SHIFT))
typedef enum
{
SANE_STATUS_GOOD = 0, /* everything A-OK */
SANE_STATUS_UNSUPPORTED, /* operation is not supported */
SANE_STATUS_CANCELLED, /* operation was cancelled */
SANE_STATUS_DEVICE_BUSY, /* device is busy; try again later */
SANE_STATUS_INVAL, /* data is invalid (includes no dev at open) */
SANE_STATUS_EOF, /* no more data available (end-of-file) */
SANE_STATUS_JAMMED, /* document feeder jammed */
SANE_STATUS_NO_DOCS, /* document feeder out of documents */
SANE_STATUS_COVER_OPEN, /* scanner cover is open */
SANE_STATUS_IO_ERROR, /* error during device I/O */
SANE_STATUS_NO_MEM, /* out of memory */
SANE_STATUS_ACCESS_DENIED /* access to resource has been denied */
}
SANE_Status;
/* following are for later sane version, older frontends wont support */
#if 0
#define SANE_STATUS_WARMING_UP 12 /* lamp not ready, please retry */
#define SANE_STATUS_HW_LOCKED 13 /* scanner mechanism locked for transport */
#endif
typedef enum
{
SANE_TYPE_BOOL = 0,
SANE_TYPE_INT,
SANE_TYPE_FIXED,
SANE_TYPE_STRING,
SANE_TYPE_BUTTON,
SANE_TYPE_GROUP
}
SANE_Value_Type;
typedef enum
{
SANE_UNIT_NONE = 0, /* the value is unit-less (e.g., # of scans) */
SANE_UNIT_PIXEL, /* value is number of pixels */
SANE_UNIT_BIT, /* value is number of bits */
SANE_UNIT_MM, /* value is millimeters */
SANE_UNIT_DPI, /* value is resolution in dots/inch */
SANE_UNIT_PERCENT, /* value is a percentage */
SANE_UNIT_MICROSECOND /* value is micro seconds */
}
SANE_Unit;
typedef struct
{
SANE_String_Const name; /* unique device name */
SANE_String_Const vendor; /* device vendor string */
SANE_String_Const model; /* device model name */
SANE_String_Const type; /* device type (e.g., "flatbed scanner") */
}
SANE_Device;
#define SANE_CAP_SOFT_SELECT (1 << 0)
#define SANE_CAP_HARD_SELECT (1 << 1)
#define SANE_CAP_SOFT_DETECT (1 << 2)
#define SANE_CAP_EMULATED (1 << 3)
#define SANE_CAP_AUTOMATIC (1 << 4)
#define SANE_CAP_INACTIVE (1 << 5)
#define SANE_CAP_ADVANCED (1 << 6)
#define SANE_OPTION_IS_ACTIVE(cap) (((cap) & SANE_CAP_INACTIVE) == 0)
#define SANE_OPTION_IS_SETTABLE(cap) (((cap) & SANE_CAP_SOFT_SELECT) != 0)
#define SANE_INFO_INEXACT (1 << 0)
#define SANE_INFO_RELOAD_OPTIONS (1 << 1)
#define SANE_INFO_RELOAD_PARAMS (1 << 2)
typedef enum
{
SANE_CONSTRAINT_NONE = 0,
SANE_CONSTRAINT_RANGE,
SANE_CONSTRAINT_WORD_LIST,
SANE_CONSTRAINT_STRING_LIST
}
SANE_Constraint_Type;
typedef struct
{
SANE_Word min; /* minimum (element) value */
SANE_Word max; /* maximum (element) value */
SANE_Word quant; /* quantization value (0 if none) */
}
SANE_Range;
typedef struct
{
SANE_String_Const name; /* name of this option (command-line name) */
SANE_String_Const title; /* title of this option (single-line) */
SANE_String_Const desc; /* description of this option (multi-line) */
SANE_Value_Type type; /* how are values interpreted? */
SANE_Unit unit; /* what is the (physical) unit? */
SANE_Int size;
SANE_Int cap; /* capabilities */
SANE_Constraint_Type constraint_type;
union
{
const SANE_String_Const *string_list; /* NULL-terminated list */
const SANE_Word *word_list; /* first element is list-length */
const SANE_Range *range;
}
constraint;
}
SANE_Option_Descriptor;
typedef enum
{
SANE_ACTION_GET_VALUE = 0,
SANE_ACTION_SET_VALUE,
SANE_ACTION_SET_AUTO
}
SANE_Action;
typedef enum
{
SANE_FRAME_GRAY, /* band covering human visual range */
SANE_FRAME_RGB, /* pixel-interleaved red/green/blue bands */
SANE_FRAME_RED, /* red band only */
SANE_FRAME_GREEN, /* green band only */
SANE_FRAME_BLUE /* blue band only */
}
SANE_Frame;
/* push remaining types down to match existing backends */
/* these are to be exposed in a later version of SANE */
/* most front-ends will require updates to understand them */
#if 0
#define SANE_FRAME_TEXT 0x0A /* backend specific textual data */
#define SANE_FRAME_JPEG 0x0B /* complete baseline JPEG file */
#define SANE_FRAME_G31D 0x0C /* CCITT Group 3 1-D Compressed (MH) */
#define SANE_FRAME_G32D 0x0D /* CCITT Group 3 2-D Compressed (MR) */
#define SANE_FRAME_G42D 0x0E /* CCITT Group 4 2-D Compressed (MMR) */
#define SANE_FRAME_IR 0x0F /* bare infrared channel */
#define SANE_FRAME_RGBI 0x10 /* red+green+blue+infrared */
#define SANE_FRAME_GRAYI 0x11 /* gray+infrared */
#define SANE_FRAME_XML 0x12 /* undefined schema */
#endif
typedef struct
{
SANE_Frame format;
SANE_Bool last_frame;
SANE_Int bytes_per_line;
SANE_Int pixels_per_line;
SANE_Int lines;
SANE_Int depth;
}
SANE_Parameters;
struct SANE_Auth_Data;
#define SANE_MAX_USERNAME_LEN 128
#define SANE_MAX_PASSWORD_LEN 128
typedef void (*SANE_Auth_Callback) (SANE_String_Const resource,
SANE_Char *username,
SANE_Char *password);
extern SANE_Status sane_init (SANE_Int * version_code,
SANE_Auth_Callback authorize);
extern void sane_exit (void);
extern SANE_Status sane_get_devices (const SANE_Device *** device_list,
SANE_Bool local_only);
extern SANE_Status sane_open (SANE_String_Const devicename,
SANE_Handle * handle);
extern void sane_close (SANE_Handle handle);
extern const SANE_Option_Descriptor *
sane_get_option_descriptor (SANE_Handle handle, SANE_Int option);
extern SANE_Status sane_control_option (SANE_Handle handle, SANE_Int option,
SANE_Action action, void *value,
SANE_Int * info);
extern SANE_Status sane_get_parameters (SANE_Handle handle,
SANE_Parameters * params);
extern SANE_Status sane_start (SANE_Handle handle);
extern SANE_Status sane_read (SANE_Handle handle, SANE_Byte * data,
SANE_Int max_length, SANE_Int * length);
extern void sane_cancel (SANE_Handle handle);
extern SANE_Status sane_set_io_mode (SANE_Handle handle,
SANE_Bool non_blocking);
extern SANE_Status sane_get_select_fd (SANE_Handle handle,
SANE_Int * fd);
extern SANE_String_Const sane_strstatus (SANE_Status status);
#ifdef __cplusplus
}
#endif
#endif /* sane_h */

897
sdk/sane/sane_ex.h Normal file
View File

@ -0,0 +1,897 @@
/* sane - Scanner Access Now Easy.
sane标准头文件的扩展
Author: Gongbing
Date: 2022-01-14
Path: SANE_CONFIG_DIR
This environment variable specifies the list of directories that may contain theconfiguration file. Under UNIX,
the directories are separated by a colon (:'),under os/2,they are separated by a semi-colon (;'). If this variable
is not set,the configuration file is searched in two default directories: first, the currentworking directory(".")
and then in letc/sane.d. If the value of the environmentvariable ends with the directory separator characterthen
the default directoriesare searched after the explicitly specified directories. For examplesetting SANE_CONFIG_DIR
to" /tmp/config :" would result in directories tmp/config, ., andletclsane.d being searched (in this order).
*/
#ifndef sane_ex_h
#define sane_ex_h
#define SIZE_KB(n) ((n) * 1024)
#define SIZE_MB(n) SIZE_KB(n * 1024)
#define SIZE_GB(n) SIZE_MB(n * 1024)
#define SEC_2_MS(s) ((s) * 1000)
#define MSEC_2_US(ms) ((ms) * 1000)
#define SEC_2_US(s) MSEC_2_US(SEC_2_MS(s))
#define ALIGN_TO(v, align) (((v) + (align) - 1) / (align) * (align))
#define ALIGN_INT(v) ALIGN_TO(v, sizeof(int))
#include <base/plat_types.h>
#ifndef EXPORT_SANE_API
#ifdef OEM_HANWANG
#define ENTIRE_API(pre, tail) pre##_hwsane_##tail
#elif defined(OEM_LISICHENG)
#define ENTIRE_API(pre, tail) pre##_lscsane_##tail
#elif defined(OEM_CANGTIAN)
#define ENTIRE_API(pre, tail) pre##_ctssane_##tail
#elif defined(OEM_ZHONGJING)
#define ENTIRE_API(pre, tail) pre##_zjsane_##tail
#elif defined(OEM_ZIGUANG)
#define ENTIRE_API(pre, tail) pre##_zgsane_##tail
#elif defined(OEM_DELI)
#define ENTIRE_API(pre, tail) pre##_dlsane_##tail
#elif defined(OEM_NEUTRAL)
#define ENTIRE_API(pre, tail) pre##_neusane_##tail
#else
#define ENTIRE_API(pre, tail) pre##_hgsane_##tail
#endif
#define sane_init ENTIRE_API(sane, init)
#define sane_init_ex ENTIRE_API(sane, init_ex)
#define sane_exit ENTIRE_API(sane, exit)
#define sane_get_devices ENTIRE_API(sane, get_devices)
#define sane_open ENTIRE_API(sane, open)
#define sane_close ENTIRE_API(sane, close)
#define sane_get_option_descriptor ENTIRE_API(sane, get_option_descriptor)
#define sane_control_option ENTIRE_API(sane, control_option)
#define sane_get_parameters ENTIRE_API(sane, get_parameters)
#define sane_start ENTIRE_API(sane, start)
#define sane_read ENTIRE_API(sane, read)
#define sane_cancel ENTIRE_API(sane, cancel)
#define sane_set_io_mode ENTIRE_API(sane, set_io_mode)
#define sane_get_select_fd ENTIRE_API(sane, get_select_fd)
#define sane_strstatus ENTIRE_API(sane, strstatus)
#define sane_io_control ENTIRE_API(sane, io_control)
#define sane_err_desc ENTIRE_API(sane, err_desc)
#define sane_get_option_descriptor_ex ENTIRE_API(get_option_descriptor_ex)
#define sane_control_option_ex ENTIRE_API(control_option_ex)
#define sane_read_ext_info ENTIRE_API(sane, read_ext_info)
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define SANE_OPT_NAME(name) SANE_STD_OPT_NAME_##name
#define SANE_OPT_FIXED_ID(id) SANE_OPT_ID_##id
#define IS_SANE_OPT(v, sd) strcmp(v, SANE_OPT_NAME(sd)) == 0
#define MAX_OPT_NAME_LEN 64
#define JSON_SANE_TYPE_BOOL "bool"
#define JSON_SANE_TYPE_INT "int"
#define JSON_SANE_TYPE_FIXED "float"
#define JSON_SANE_TYPE_STRING "string"
#define JSON_SANE_TYPE_GROUP "group"
#define JSON_SANE_TYPE_BUTTON "button"
// 用户权限
enum
{
USER_PRIVILEGE_COMMON = 0, // 普通用户权限
USER_PRIVILEGE_LOCAL_MGR = 10, // 本地管理员权限
USER_PRIVILEGE_TECH_SUPPORTING = 20, // 技术支持权限
USER_PRIVILEGE_DEVLOPER = 100, // 开发者权限(查看所有属性)
};
// 属性可见
enum opt_visible_level // "visible" field
{
OPT_VISIBLE_ALL = 0, // visible on ui and accessible
OPT_VISIBLE_ACCESS, // accessible only
OPT_VISIBLE_HIDE, // invisible and inaccessible unless user has DEVELOPE privilege
OPT_VISIBLE_NOT_SUPPORT, // device does not support this option
};
// sane-standard-option-name defined by third-app
//
// PART I: 参数类型与华高一致可直接通过改name字段为标准值实现
#define SANE_STD_OPT_NAME_RESTORE "restore" // OPTION_TITLE_HFMRSZ
#define SANE_STD_OPT_NAME_HELP "help" // OPTION_TITLE_BZ
#define SANE_STD_OPT_NAME_IS_MULTI_OUT "is-multiout" // OPTION_TITLE_DLSC
#define SANE_STD_OPT_NAME_MULTI_OUT_TYPE "multiout-type" // OPTION_TITLE_DLSCLX
#define SANE_STD_OPT_NAME_COLOR_MODE "mode" // OPTION_TITLE_YSMS
#define SANE_STD_OPT_NAME_BINARY_THRESHOLD "binary-threshold" // OPTION_TITLE_HBTXYZ
#define SANE_STD_OPT_NAME_REVERSE_01 "reverse-bw" // OPTION_TITLE_HBTXFSSC
#define SANE_STD_OPT_NAME_FILTER "filter" // OPTION_TITLE_HDHHBTX_CSYZQ
#define SANE_STD_OPT_NAME_RID_MULTIOUT_RED "is-rid-multiout-red" // OPTION_TITLE_24WCSTX_DLSCCH
#define SANE_STD_OPT_NAME_RID_ANSWER_SHEET_RED "is-rid-answer-sheet-red" // OPTION_TITLE_24WCSTX_DTKCH
#define SANE_STD_OPT_NAME_ERASE_BACKGROUND "is-erase-bkg" // OPTION_TITLE_BJYC
#define SANE_STD_OPT_NAME_BKG_COLOR_RANGE "bkg-color-range" // OPTION_TITLE_BJSCFDFW
#define SANE_STD_OPT_NAME_SHARPEN "sharpen" // OPTION_TITLE_RHYMH
#define SANE_STD_OPT_NAME_RID_MORR "is-rid-morr" // OPTION_TITLE_QCMW
#define SANE_STD_OPT_NAME_RID_GRID "is-rid-grid" // OPTION_TITLE_CWW
#define SANE_STD_OPT_NAME_ERROR_EXTENSION "is-err-extension" // OPTION_TITLE_CWKS
#define SANE_STD_OPT_NAME_NOISE_OPTIMIZE "is-noise-optimize" // OPTION_TITLE_HBTXZDYH
#define SANE_STD_OPT_NAME_NOISE_SIZE "noise-size" // OPTION_TITLE_ZDYHCC
#define SANE_STD_OPT_NAME_PAPER "paper" // OPTION_TITLE_ZZCC
#define SANE_STD_OPT_NAME_PAPER_W "paper-w" // 纸张宽度
#define SANE_STD_OPT_NAME_PAPER_H "paper-h" // 纸张高度
#define SANE_STD_OPT_NAME_LATERAL "lateral" // 纸张横向标志
#define SANE_STD_OPT_NAME_CUSTOM_AREA "is-custom-area" // OPTION_TITLE_ZDYSMQY
#define SANE_STD_OPT_NAME_CUSTOM_AREA_LEFT "tl-x" // OPTION_TITLE_SMQYZCmm
#define SANE_STD_OPT_NAME_CUSTOM_AREA_RIGHT "br-x" // OPTION_TITLE_SMQYYCmm
#define SANE_STD_OPT_NAME_CUSTOM_AREA_TOP "tl-y" // OPTION_TITLE_SMQYSCmm
#define SANE_STD_OPT_NAME_CUSTOM_AREA_BOTTOM "br-y" // OPTION_TITLE_SMQYXCmm
#define SANE_STD_OPT_NAME_SIZE_CHECK "is-size-check" // OPTION_TITLE_CCJC
#define SANE_STD_OPT_NAME_PAGE "page" // OPTION_TITLE_SMYM
#define SANE_STD_OPT_NAME_DISCARD_BLANK_SENS "blank-sensitivity" // OPTION_TITLE_TGKBYLMD
#define SANE_STD_OPT_NAME_RESOLUTION "resolution" // OPTION_TITLE_FBL
#define SANE_STD_OPT_NAME_TIME_TO_SLEEP "time-to-sleep" // OPTION_TITLE_XMSJ
#define SANE_STD_OPT_NAME_IMAGE_QUALITY "image-quality" // OPTION_TITLE_HZ
#define SANE_STD_OPT_NAME_EXCHANGE "is-exchange" // OPTION_TITLE_JHZFM
#define SANE_STD_OPT_NAME_SPLIT "is-split" // OPTION_TITLE_TXCF
#define SANE_STD_OPT_NAME_ANTI_SKEW "is-anti-skew" // OPTION_TITLE_ZDJP
#define SANE_STD_OPT_NAME_IS_CUSTOM_GAMMA "is-custom-gamma" // OPTION_TITLE_QYSDQX
#define SANE_STD_OPT_NAME_GAMMA "gamma" // OPTION_TITLE_JMZ
#define SANE_STD_OPT_NAME_CUSTOM_GAMMA "custom-gamma" // OPTION_TITLE_CUSTOM_JMZ; data - SANE_Gamma*
#define SANE_STD_OPT_NAME_GRAY_GAMMA "gray-gamma-table"
#define SANE_STD_OPT_NAME_COLOR_GAMMA "color-gamma-table"
#define SANE_STD_OPT_NAME_RED_GAMMA "red-gamma-table"
#define SANE_STD_OPT_NAME_GREEN_GAMMA "green-gamma-table"
#define SANE_STD_OPT_NAME_BLUE_GAMMA "blue-gamma-table"
#define SANE_STD_OPT_NAME_BRIGHTNESS "brightness" // OPTION_TITLE_LDZ
#define SANE_STD_OPT_NAME_CONTRAST "contrast" // OPTION_TITLE_DBD
#define SANE_STD_OPT_NAME_IS_PHOTO_MODE "is-photo-mode" // OPTION_TITLE_ZPMS
#define SANE_STD_OPT_NAME_ERASE_BLACK_FRAME "is-erase-black-frame" // OPTION_TITLE_XCHK
#define SANE_STD_OPT_NAME_DARK_SAMPLE "is-dark-sample" // OPTION_TITLE_SSYZ
#define SANE_STD_OPT_NAME_THRESHOLD "threshold" // OPTION_TITLE_YZ
#define SANE_STD_OPT_NAME_ANTI_NOISE_LEVEL "anti-noise-level" // OPTION_TITLE_BJKZDJ
#define SANE_STD_OPT_NAME_MARGIN "margin" // OPTION_TITLE_BYSJ
#define SANE_STD_OPT_NAME_FILL_BKG_MODE "bkg-fill-mode" // OPTION_TITLE_BJTCFS
#define SANE_STD_OPT_NAME_IS_ANTI_PERMEATE "is-anti-permeate" // OPTION_TITLE_FZST
#define SANE_STD_OPT_NAME_ANTI_PERMEATE_LEVEL "permeate-level" // OPTION_TITLE_FZSTDJ
#define SANE_STD_OPT_NAME_RID_HOLE "is-rid-hole" // OPTION_TITLE_CKYC
#define SANE_STD_OPT_NAME_HOLE_THRESHOLD "hole-threshold" //
#define SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE "search-hole-range" // OPTION_TITLE_CKSSFWZFMBL
#define SANE_STD_OPT_NAME_RID_HOLE_L "is-rid-hole-l" // OPTION_TITLE_CKYCZC
#define SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_L "search-hole-range-l" // OPTION_TITLE_ZCCKSSFWZFMBL
#define SANE_STD_OPT_NAME_RID_HOLE_R "is-rid-hole-r" // OPTION_TITLE_CKYCYC
#define SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_R "search-hole-range-r" // OPTION_TITLE_YCCKSSFWZFMBL
#define SANE_STD_OPT_NAME_RID_HOLE_T "is-rid-hole-t" // OPTION_TITLE_CKYCSC
#define SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_T "search-hole-range-t" // OPTION_TITLE_SCCKSSFWZFMBL
#define SANE_STD_OPT_NAME_RID_HOLE_B "is-rid-hole-b" // OPTION_TITLE_CKYCXC
#define SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_B "search-hole-range-b" // OPTION_TITLE_XCCKSSFWZFMBL
#define SANE_STD_OPT_NAME_IS_FILL_COLOR "is-fill-color" // OPTION_TITLE_SCTC
#define SANE_STD_OPT_NAME_IS_ULTROSONIC_CHECK "is-ultrosonic" // OPTION_TITLE_CSBJC
#define SANE_STD_OPT_NAME_DOUBLE_FEED_HANDLE "double-feed" // OPTION_TITLE_SZTPCL
#define SANE_STD_OPT_NAME_IS_CHECK_STAPLE "is-staple" // OPTION_TITLE_ZDJC
#define SANE_STD_OPT_NAME_SCAN_MODE "scan-mode" // OPTION_TITLE_SMZS
#define SANE_STD_OPT_NAME_SCAN_COUNT "scan-count" // OPTION_TITLE_SMSL
#define SANE_STD_OPT_NAME_TEXT_DIRECTION "direction" // OPTION_TITLE_WGFX
#define SANE_STD_OPT_NAME_IS_ROTATE_BKG_180 "is-rotate-bkg-180" // OPTION_TITLE_BMXZ180
#define SANE_STD_OPT_NAME_IS_CHECK_DOG_EAR "is-check-dog-ear" // OPTION_TITLE_ZJJC
#define SANE_STD_OPT_NAME_DOG_EAR_SIZE "dog-ear-size" // OPTION_TITLE_ZJDX
#define SANE_STD_OPT_NAME_IS_CHECK_ASKEW "is-check-askew" // OPTION_TITLE_WXJC
#define SANE_STD_OPT_NAME_ASKEW_RANGE "askew-range" // OPTION_TITLE_WXRRD
#define SANE_STD_OPT_NAME_FEED_STRENGTH "feed-strength" // OPTION_TITLE_FZQD
#define SANE_STD_OPT_NAME_IS_AUTO_FEED_STRENGTH "is-auto-strength" // OPTION_TITLE_ZDFZQD
#define SANE_STD_OPT_NAME_FEED_STRENGTH_VALUE "feed-strength-value" // OPTION_TITLE_JZSBL
#define SANE_STD_OPT_NAME_WAIT_TO_SCAN "is-wait-scan" // OPTION_TITLE_DZSM
#define SANE_STD_OPT_NAME_FOLD_TYPE "fold-type" // OPTION_TITLE_DZMS
#define SANE_STD_OPT_NAME_COLOR_CORRECTION "color-correction" // OPTION_TITLE_SPJZ
#define SANE_STD_OPT_NAME_WAIT_SCAN_EXIT "wait-scan-exit" // OPTION_TITLE_DZSMTCSJ
#define SANE_STD_OPT_NAME_DISCARDBLANK "discardblank" // OPTION_TITLE_TGKBY
#define SANE_STD_OPT_NAME_DEVICE_NAME "dev-name" // 设备名称
#define SANE_STD_OPT_NAME_DEVICE_VID "dev-vid" // 设备VID
#define SANE_STD_OPT_NAME_DEVICE_PID "dev-pid" // 设备PID
#define SANE_STD_OPT_NAME_DEVICE_MODEL "dev-model" // 设备系列
#define SANE_STD_OPT_NAME_DEV_NAME SANE_STD_OPT_NAME_DEVICE_NAME
#define SANE_STD_OPT_NAME_VID SANE_STD_OPT_NAME_DEVICE_VID
#define SANE_STD_OPT_NAME_PID SANE_STD_OPT_NAME_DEVICE_PID
#define SANE_STD_OPT_NAME_DEV_FAMILY SANE_STD_OPT_NAME_DEVICE_MODEL
#define SANE_STD_OPT_NAME_DEVICE_TYPE "dev-type" // 设备型号
#define SANE_STD_OPT_NAME_DEVICE_SERIAL_NO "dev-sn" // 设备序列号
#define SANE_STD_OPT_NAME_FIRMWARE_VERSION "fmw-ver" // 固件版本
#define SANE_STD_OPT_NAME_DEVICE_IP_ADDR "ip-addr" // 设备IP地址
#define SANE_STD_OPT_NAME_DEVICE_MAC_ADDR "mac-addr" // 设备MAC地址
#define SANE_STD_OPT_NAME_ROLLER_COUNT "roll-cnt" // 滚轴张数
#define SANE_STD_OPT_NAME_TOTAL_COUNT "total-cnt" // 历史总张数
#define SANE_STD_OPT_NAME_HISTORY_COUNT SANE_STD_OPT_NAME_TOTAL_COUNT
#define SANE_STD_OPT_NAME_GET_DEVS_L0G "devs-log" // 获取设备日志
#define SANE_STD_OPT_NAME_DRIVER_VERSION "drv-ver" // 设备VID
#define SANE_STD_OPT_NAME_MANUFACTURER "company" // 公司名称
#define SANE_STD_OPT_NAME_COPYRIGHT "copyright" // 版权
#define SANE_STD_OPT_NAME_CO_URL "co-url" // 公司网址
#define SANE_STD_OPT_NAME_CO_TEL "co-tel" // 公司电话
#define SANE_STD_OPT_NAME_CO_ADDR "co-addr" // 公司地址
#define SANE_STD_OPT_NAME_CO_GPS "co-gps" // 公司地图定位
#define SANE_STD_OPT_NAME_LOGIN "login" // 登录
#define SANE_STD_OPT_NAME_LOGOUT "logout" // 登出
#define SANE_STD_OPT_NAME_DRIVER_LOG "drv-log" // 驱动日志
#define SANE_STD_OPT_NAME_DEVICE_LOG SANE_STD_OPT_NAME_GET_DEVS_L0G
#define SANE_STD_OPT_NAME_ROLLER_LIFE "roller-life" // 滚轴最大寿命(过纸张数)
#define SANE_STD_OPT_NAME_LANGUAGE "language" // 语言
#define SANE_STD_OPT_NAME_MOTOR_VER "motor-ver" // 电机固件版本, data = char*
#define SANE_STD_OPT_NAME_TRANSFORM_IMAGE_FORMAT "trans-img-fmt" // 图像格式转换, data - SANE_ImageFormatConvert*, dst.data 调用SANE_STD_OPT_NAME_FREE_BUFFER释放
#define SANE_STD_OPT_NAME_FREE_BUFFER "free-buf" // 释放由驱动返回的内存, data - (void**)&buf
#define SANE_STD_OPT_NAME_PAPER_ON "paper-on" // check whether paper is on
#define SANE_STD_OPT_NAME_INITIAL_BOOT_TIME "initial-boot-time" // 设备的初始开机时间
#define SANE_STD_OPT_NAME_DUMP_IMG "dumpimg" // 是否输出算法各阶段中间图像
#define SANE_STD_OPT_NAME_DUMP_IMG_PATH "dump-path" // 中间图像输出路径
// PART II: 参数类型与华高不一致需要通过“hgsane”组件在中间转换
#define SANE_STD_OPT_NAME_PAGE_W "page-width" // OPTION_TITLE_ZZCC
#define SANE_STD_OPT_NAME_PAGE_H "page-height" // OPTION_TITLE_ZZCC
#define SANE_STD_OPT_NAME_DUPLEX "duplex" // OPTION_TITLE_SMYM
// END for sane-standard-option-name defined by third-app
//
//
//
// 借鉴TWAIN协议固定ID的做法定义SANE属性的固定ID - 这里与TWAIN中的ID保持一致以方便TWAIN的调用
enum sane_option_id
{
SANE_OPT_ID_BASE = 0x8800,
SANE_OPT_ID_IS_MULTI_OUT = 0x8801,
SANE_OPT_ID_MULTI_OUT_TYPE = 0x8802,
SANE_OPT_ID_COLOR_MODE = 0x8803,
SANE_OPT_ID_FILTER = 0x8804,
SANE_OPT_ID_RID_MULTIOUT_RED = 0x8805,
SANE_OPT_ID_RID_ANSWER_SHEET_RED = 0x8806,
SANE_OPT_ID_ERASE_BACKGROUND = 0x8807,
SANE_OPT_ID_BKG_COLOR_RANGE = 0x8808,
SANE_OPT_ID_SHARPEN = 0x8809,
SANE_OPT_ID_RID_MORR = 0x880A,
SANE_OPT_ID_RID_GRID = 0x880B,
SANE_OPT_ID_ERROR_EXTENSION = 0x880C,
SANE_OPT_ID_NOISE_OPTIMIZE = 0x880D,
SANE_OPT_ID_NOISE_SIZE = 0x880E,
SANE_OPT_ID_PAPER = 0x880F,
SANE_OPT_ID_CUSTOM_AREA = 0x8810,
SANE_OPT_ID_CUSTOM_AREA_LEFT = 0x8811,
SANE_OPT_ID_CUSTOM_AREA_RIGHT = 0x8812,
SANE_OPT_ID_CUSTOM_AREA_TOP = 0x8813,
SANE_OPT_ID_CUSTOM_AREA_BOTTOM = 0x8814,
SANE_OPT_ID_SIZE_CHECK = 0x8815,
SANE_OPT_ID_PAGE = 0x8816,
SANE_OPT_ID_DISCARD_BLANK_SENS = 0x8817,
SANE_OPT_ID_RESOLUTION = 0x8818,
SANE_OPT_ID_IMAGE_QUALITY = 0x8819,
SANE_OPT_ID_EXCHANGE = 0x881A,
SANE_OPT_ID_SPLIT = 0x881B,
SANE_OPT_ID_ANTI_SKEW = 0x881C,
SANE_OPT_ID_IS_CUSTOM_GAMMA = 0x881D,
SANE_OPT_ID_BRIGHTNESS = 0x881E,
SANE_OPT_ID_CONTRAST = 0x881F,
SANE_OPT_ID_GAMMA = 0x8820,
SANE_OPT_ID_ERASE_BLACK_FRAME = 0x8821,
SANE_OPT_ID_DARK_SAMPLE = 0x8822,
SANE_OPT_ID_THRESHOLD = 0x8823,
SANE_OPT_ID_ANTI_NOISE_LEVEL = 0x8824,
SANE_OPT_ID_MARGIN = 0x8825,
SANE_OPT_ID_FILL_BKG_MODE = 0x8826,
SANE_OPT_ID_IS_ANTI_PERMEATE = 0x8827,
SANE_OPT_ID_ANTI_PERMEATE_LEVEL = 0x8828,
SANE_OPT_ID_RID_HOLE = 0x8829,
SANE_OPT_ID_SEARCH_HOLE_RANGE = 0x882A,
SANE_OPT_ID_IS_FILL_COLOR = 0x882B,
SANE_OPT_ID_IS_ULTROSONIC_CHECK = 0x882C,
SANE_OPT_ID_IS_CHECK_STAPLE = 0x882D,
SANE_OPT_ID_SCAN_MODE = 0x882E,
SANE_OPT_ID_SCAN_COUNT = 0x882F,
SANE_OPT_ID_TEXT_DIRECTION = 0x8830,
SANE_OPT_ID_IS_ROTATE_BKG_180 = 0x8831,
SANE_OPT_ID_IS_CHECK_DOG_EAR = 0x8832,
SANE_OPT_ID_DOG_EAR_SIZE = 0x8833,
SANE_OPT_ID_IS_CHECK_ASKEW = 0x8834,
SANE_OPT_ID_ASKEW_RANGE = 0x8835,
SANE_OPT_ID_BINARY_THRESHOLD = 0x8836,
SANE_OPT_ID_IS_PHOTO_MODE = 0x8837,
SANE_OPT_ID_DOUBLE_FEED_HANDLE = 0x8838,
SANE_OPT_ID_WAIT_TO_SCAN = 0x8839,
SANE_OPT_ID_FEED_STRENGTH = 0x883A,
SANE_OPT_ID_TIME_TO_SLEEP = 0x883B,
SANE_OPT_ID_IS_AUTO_FEED_STRENGTH = 0x883C,
SANE_OPT_ID_FEED_STRENGTH_VALUE = 0x883D,
SANE_OPT_ID_REVERSE_01 = 0x883E,
SANE_OPT_ID_RID_HOLE_L = 0x883F,
SANE_OPT_ID_SEARCH_HOLE_RANGE_L = 0x8840,
SANE_OPT_ID_RID_HOLE_R = 0x8841,
SANE_OPT_ID_SEARCH_HOLE_RANGE_R = 0x8842,
SANE_OPT_ID_RID_HOLE_T = 0x8843,
SANE_OPT_ID_SEARCH_HOLE_RANGE_T = 0x8844,
SANE_OPT_ID_RID_HOLE_B = 0x8845,
SANE_OPT_ID_SEARCH_HOLE_RANGE_B = 0x8846,
SANE_OPT_ID_FOLD_TYPE = 0x8847,
SANE_OPT_ID_COLOR_CORRECTION = 0x8848,
SANE_OPT_ID_HISTORY_COUNT = 0x8849,
SANE_OPT_ID_DRIVER_VERSION = 0x884A,
SANE_OPT_ID_MANUFACTURER = 0x884B,
SANE_OPT_ID_COPYRIGHT = 0x884C,
SANE_OPT_ID_CO_URL = 0x884D,
SANE_OPT_ID_CO_TEL = 0x884E,
SANE_OPT_ID_CO_ADDR = 0x884F,
SANE_OPT_ID_CO_GPS = 0x8850,
SANE_OPT_ID_HELP = 0x8851,
SANE_OPT_ID_VID = 0x8852,
SANE_OPT_ID_PID = 0x8853,
SANE_OPT_ID_DEV_NAME = 0x8854,
SANE_OPT_ID_DEV_FAMILY = 0x8855,
SANE_OPT_ID_DEVICE_SERIAL_NO = 0x8856,
SANE_OPT_ID_FIRMWARE_VERSION = 0x8857,
SANE_OPT_ID_DEVICE_IP_ADDR = 0x8858,
SANE_OPT_ID_DEVICE_MAC_ADDR = 0x8859,
SANE_OPT_ID_CUSTOM_GAMMA = 0x885A, // data: SANE_Gamma*
SANE_OPT_ID_ROLLER_LIFE = 0x885B, // data: uint32_t*
SANE_OPT_ID_LANGUAGE = 0x885C, // data: char*
SANE_OPT_ID_MOTOR_VER = 0x885D, // 电机固件版本, data - char*
SANE_OPT_ID_TRANSFORM_IMAGE_FORMAT = 0x885E,// 图像格式转换, data - SANE_ImageFormatConvert*, dst.data 调用SANE_STD_OPT_NAME_FREE_BUFFER释放
SANE_OPT_ID_FREE_BUFFER = 0x885F, // 释放由驱动返回的内存, data - (void**)&buf
SANE_OPT_ID_PAPER_ON = 0x8860, // 是否有纸张, data - SANE_Bool*
SANE_OPT_ID_GRAY_GAMMA = 0x8861, // gray-gamma-table
SANE_OPT_ID_COLOR_GAMMA = 0x8862, // color-gamma-table
SANE_OPT_ID_RED_GAMMA = 0x8863, // red-gamma-table
SANE_OPT_ID_GREEN_GAMMA = 0x8864, // green-gamma-table
SANE_OPT_ID_BLUE_GAMMA = 0x8865, // blue-gamma-table
SANE_OPT_ID_INITIAL_BOOT_TIME = 0x8866,
SANE_OPT_ID_DISCARDBLANK = 0x8867,
SANE_OPT_ID_WAIT_SCAN_EXIT = 0x8868,
SANE_OPT_ID_RESTORE = 0x8869,
SANE_OPT_ID_PAPER_W = 0x886A, // 纸张宽度单位为mm
SANE_OPT_ID_PAPER_H = 0x886B, // 纸张高度单位为mm
SANE_OPT_ID_LATERAL = 0x886C, // 纸张横向标志bool
SANE_OPT_ID_LOGIN = 0x9900,
SANE_OPT_ID_LOGOUT = 0x9901,
SANE_OPT_ID_ROLLER_COUNT = 0x9902,
SANE_OPT_ID_DRIVER_LOG = 0x9903,
SANE_OPT_ID_DEVICE_LOG = 0x9904,
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "sane.h"
#include <math.h>
#define MAX_STRING_LEN 256
#define IS_DOUBLE_EQUAL(a, b) fabs((a) - (b)) < .000001
#define IS_PTR_NUMBER(ptr) (((unsigned long long)(ptr)) < 0x10000)
#define CAPABILITY_ORDINARY (SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT)
#define CAPABILITY_READONLY SANE_CAP_SOFT_DETECT
// 设置活动状态
#define SET_CAP_ACTIVE(cap, act) \
{ \
if(act) \
cap &= ~SANE_CAP_INACTIVE; \
else \
cap |= SANE_CAP_INACTIVE; \
}
// 设置为只读属性
#define SET_CAP_READONLY(cap) \
{ \
cap &= ~(CAPABILITY_ORDINARY | SANE_CAP_HARD_SELECT); \
cap |= CAPABILITY_READONLY; \
}
#define IS_CAP_READONLY(cap) (((cap) & (CAPABILITY_ORDINARY | SANE_CAP_HARD_SELECT)) == CAPABILITY_READONLY)
// 设置为软件可设置/读取
#define SET_CAP_SOFT_SETTABLE(cap) \
{ \
cap &= ~(CAPABILITY_ORDINARY | SANE_CAP_HARD_SELECT); \
cap |= SANE_CAP_HARD_SELECT; \
}
// 设置为由设备上的按键操作属性
#define SET_CAP_DEVICE_SETTABLE(cap, can_read) \
{ \
cap &= ~(CAPABILITY_ORDINARY | SANE_CAP_HARD_SELECT); \
cap |= can_read ? SANE_CAP_SOFT_DETECT | CAPABILITY_ORDINARY : CAPABILITY_ORDINARY;\
}
#ifdef __cplusplus
extern "C" {
#endif
#pragma pack(push)
#pragma pack(1)
#define ZERO_STRUCT(struct_ptr) memset(struct_ptr, 0, sizeof(*(struct_ptr)))
////////////////////////////////////////////////////////////////////////////////
// extension for standard SANE ...
enum SANE_Action_Ex // extension for SANE_Action
{
SANE_ACTION_GET_DEFAULT_VALUE = 100, // 获取设置项默认值参数同SANE_ACTION_GET_VALUE
SANE_ACTION_GET_FIX_ID, // 获取属性的固定IDvoid* = SANE_Int*
SANE_ACTION_GET_ENTIRE_JSON, // 获取该属性JSON文本
// Function: 枚举对SANE协议不可见的固定ID属性以使TWAIN可访问
//
// Parameter: void* = FIXDCB*
SANE_ACTION_ENUM_INVISIBLE_FIX_ID = 0x0FEC7,
};
enum SANE_Frame_Ex // after SANE_Frame
{
SANE_FRAME_RAW = 5,
SANE_FRAME_MIME, // MIME descriptor
};
// SANE_ACTION_ENUM_INVISIBLE_FIX_ID
struct _fix_id_cb
{
SANE_Bool (*cb)(int, void*);
void* param;
};
typedef struct _sane_stream
{
SANE_Int bytes;
SANE_Byte *data;
}SANE_Stream;
typedef struct _sane_dev_ex
{
SANE_String_Const name; /* unique device name */
SANE_String_Const vendor; /* device vendor string */
SANE_String_Const model; /* device model name */
SANE_String_Const family; /* device type (e.g., "flatbed scanner") */
SANE_Bool openned; // whether openned
}SANE_Device_Ex;
typedef struct _sane_auth // for SANE_EVENT_NEED_AUTH
{
SANE_String_Const resource;
SANE_Char name[MAX_STRING_LEN];
SANE_Char pwd[MAX_STRING_LEN];
SANE_Char method[MAX_STRING_LEN];
}SANEAUTH;
typedef enum
{
SANE_TYPE_EX_BOOL = 0,
SANE_TYPE_EX_INT,
SANE_TYPE_EX_FIXED, // same as SANE_TYPE_FIXED
SANE_TYPE_EX_STRING, // sane as SANE_TYPE_STRING
SANE_TYPE_EX_POS, // position in origin image, SANE_Rect*
SANE_TYPE_EX_IMAGE, // SANE_Image*, src_id is meanless
}SANE_Value_Ext_Type;
typedef struct
{
SANE_Int left;
SANE_Int top;
SANE_Int right;
SANE_Int bottom;
}SANE_Rect;
typedef struct
{
SANE_Int src_id; // unique(in this running) image ID from SANE
SANE_Int count; // length of array 'info'
struct
{
SANE_Int ext_inf_id; // Extended information ID
SANE_Value_Ext_Type type; // value type
SANE_Int inf_bytes; // length of information
union
{
SANE_Bool bval; // bool
SANE_Int ival; // integer
SANE_Fixed fval; // float value
SANE_Char *pval; // string, byte-stream or other structure
};
}info[1];
}SANE_Img_Ext_Info; // added on 2023-01-13
typedef struct
{
SANE_Int src_id; // unique(in this running) image ID from SANE
SANE_Parameters header;
struct
{
unsigned int statu : 4; // SANE_Image_Statu
unsigned int dpi : 12; // resolution, dots per inch
unsigned int reserve : 16;
}flag;
unsigned long bytes;
unsigned char *data;
}SANE_Image;
typedef struct _about_info
{
SANE_String_Const title; // APP名称
SANE_String_Const version; // 版本号
SANE_String_Const copyright; // 版权信息
unsigned int logo_bytes; // LOGO 数据长度
void* logo; // LOGO 数据
struct
{
SANE_String_Const key; // 附加信息名称该数据为NULL时appendix数组结束
SANE_String_Const content;// 附加信息内容
SANE_String_Const url; // 附加信息链接, NULL则忽略
}appendix[1];
}SANE_About;
typedef enum
{
SANE_COMPRESSION_FIRST = 0,
SANE_COMPRESSION_NONE = 0, // default value
//SANE_COMPRESSION_PACBITS,
//SANE_COMPRESSION_GROUP31D,
//SANE_COMPRESSION_GROUP31DEOL,
//SANE_COMPRESSION_GROUP32D,
SANE_COMPRESSION_GROUP4 = 5, // support now ! detail is threshold converting color to BlackWhite, e.g. (void*)128
//SANE_COMPRESSION_JPEG,
//SANE_COMPRESSION_LZW,
//SANE_COMPRESSION_JBIG,
//SANE_COMPRESSION_PNG,
//SANE_COMPRESSION_RLE4,
//SANE_COMPRESSION_RLE8,
//SANE_COMPRESSION_BITFIELDS,
//SANE_COMPRESSION_ZIZ,
//SANE_COMPRESSION_JPEG2000,
SANE_COMPRESSION_LAST,
SANE_COMPRESSION_DONTCARE = 0xFFFF,
}SANE_CompressionType;
typedef struct _img_compression
{
SANE_CompressionType compression;
void* detail; // see SANE_CompressionType (该参数在当前版本不考虑,暂使用压缩类型的默认值)
}SANE_Compression;
typedef enum // 与Twpp::ImageFileFormat 保持一致
{
SANE_IMAGE_TYPE_FIRST = 0,
SANE_IMAGE_TYPE_TIFF = 0,
SANE_IMAGE_TYPE_BMP = 2, // (BITMAPINFOHEADER*)detail
SANE_IMAGE_TYPE_PNG = 7,
SANE_IMAGE_TYPE_JPG = 13,
SANE_IMAGE_TYPE_JFIF = 4,
SANE_IMAGE_TYPE_PDF = 10,
// 以下为Twpp::ImageFileFormat不存在的值
SANE_IMAGE_TYPE_GIF = 100,
SANE_IMAGE_TYPE_WEBP,
SANE_IMAGE_TYPE_SVG,
SANE_IMAGE_TYPE_LAST,
}SANE_ImageType;
typedef struct _img_final_fmt
{
SANE_ImageType img_format;
void* detail; // see SANE_ImageType (该参数在当前版本不考虑,暂使用该图像类型的默认值)
SANE_Compression compress;
}SANE_FinalImgFormat;
typedef struct _img_format_convert
{
struct
{
SANE_FinalImgFormat fmt; // format of the data
SANE_Bool is_file; // data is a 'path-file' if it was true, or else is raw file data
SANE_String_Const data; // represents a local file or a memory data. call 'IO_CTRL_CODE_FREE_MEMORY' to free the dst.data memory if 'is_file' was true !!!
unsigned int data_len; // bytes of 'data'
}src, dst;
}SANE_ImageFormatConvert;
typedef struct _sane_point
{
SANE_Byte x;
SANE_Byte y;
}SANE_Gamma_Point;
enum gamma_index
{
GAMMA_INDEX_GRAY = 0,
GAMMA_INDEX_COLOR,
GAMMA_INDEX_RED,
GAMMA_INDEX_GREEN,
GAMMA_INDEX_BLUE,
};
typedef struct _user_gamma // NOTE: exceeds 4KB !!!
{
SANE_Int apply_to_back : 8; // SANE_TRUE: 应用到背面; SANE_FALSE: 应用到正面
SANE_Int app_data : 24; // APP 自定义数据
SANE_Byte count[8]; // 控制点个数,顺序为(gamma_index) Gray + Color + Red + Green + Blue
SANE_Byte pt_gray[4]; // 灰度控制点 - 只记录横坐标纵坐标请在table表中查得
SANE_Byte pt_color[4]; // 彩色控制点 - 只记录横坐标纵坐标请在table表中查得
SANE_Byte pt_red[4]; // 红色控制点 - 只记录横坐标纵坐标请在table表中查得
SANE_Byte pt_green[4]; // 绿色控制点 - 只记录横坐标纵坐标请在table表中查得
SANE_Byte pt_blue[4]; // 蓝色控制点 - 只记录横坐标纵坐标请在table表中查得
SANE_Byte table[5 * 256]; // gamma变换阵列顺序为(gamma_index)Gray[256] + Color[256] + R[256] + G[256] + B[256]
}SANE_Gamma;
typedef struct _user_gamma_ex // from draft-2.0
{
SANE_Int size : 16; // size of this structure
SANE_Int ver : 8; // version
SANE_Int count : 8; // 控制点(point数组中有效元素)个数
SANE_Int side : 4; // 0 - 应用到正反面1 - 应用到正面2 - 应用到反面
SANE_Int channel : 4; // 0 - 灰度曲线1 - 彩色曲线2 - Red通道曲线3 - Green通道曲线4 - Blue通道曲线
SANE_Int app_data : 24; // APP 自定义数据
SANE_Byte point[4]; // 控制点 - 只记录横坐标纵坐标请在table表中查得
SANE_Byte table[256]; // 变换表
}SANE_Gamma_Ex;
typedef struct CISTestResult
{
double w; //圆横轴
double h; //圆纵轴
double scaleXY; //圆3横纵比
SANE_Byte colorBlock1[3]; //彩色色块1RGB //余下保留
SANE_Byte colorBlock2[3]; //彩色色块2RGB
SANE_Byte colorBlock3[3]; //彩色色块3RGB
SANE_Byte colorBlock4[3]; //彩色色块4RGB
SANE_Byte colorBlock5[3]; //彩色色块5RGB
SANE_Byte colorBlock6[3]; //彩色色块6RGB
SANE_Byte colorBlock7[3]; //彩色色块7RGB
SANE_Byte colorBlock8[3]; //彩色色块8RGB
SANE_Byte colorBlock9[3]; //彩色色块9RGB
SANE_Byte grayBlock1; //灰度色块1灰度值
SANE_Byte grayBlock2; //灰度色块2灰度值
SANE_Byte grayBlock3; //灰度色块3灰度值
SANE_Byte grayBlock4; //灰度色块4灰度值
SANE_Byte grayBlock5; //灰度色块5灰度值
SANE_Byte grayBlock6; //灰度色块6灰度值
SANE_Byte grayBlock7; //灰度色块7灰度值
SANE_Byte grayBlock8; //灰度色块8灰度值
SANE_Byte grayBlock9; //灰度色块9灰度值
}SANE_DISTORTION_VAL;//获取畸变修正值
typedef enum
{
SANE_FUNCTION_PARAMETER_TYPE_STRING = 0, // SANE_String_List
SANE_FUNCTION_PARAMETER_TYPE_STREAM, // SANE_Stream
SANE_FUNCTION_PARAMETER_TYPE_USER_INPUT, // SANE_String
}SANE_Function_Parameter_Type;
typedef struct _sane_function_parameter
{
SANE_Function_Parameter_Type type;
}SANE_Function_Parameter;
typedef struct _sane_function
{
SANE_Int func_id;
SANE_Int parameter_count;
SANE_Function_Parameter parameter[1];
}SANE_Function;
typedef struct _sane_img_kits
{
SANE_Int kit_id;
SANE_String name;
SANE_String title;
SANE_String desc;
SANE_Stream icon;
SANE_Int function_count;
SANE_Function function[1];
}SANE_Image_Kits;
typedef enum
{
AUTO_COLOR_BLACKWHITE = 0,
AUTO_COLOR_GRAY,
}SANE_AutoColorType;
typedef enum
{
LOG_FILE_DEVICE = 0, // 设备端日志文件
LOG_FILE_DRIVER, // 驱动层日志文件
}SANE_LogFileType;
typedef enum // IO_CTRL_CODE_SET_POWER_LEVEL
{
SANE_POWER_FIRST = 0,
SANE_POWER_NONE = SANE_POWER_FIRST,
SANE_POWER_MINUTES_5,
SANE_POWER_MINUTES_10,
SANE_POWER_MINUTES_20,
SANE_POWER_MINUTES_30,
SANE_POWER_MINUTES_60,
SANE_POWER_MINUTES_120,
SANE_POWER_MINUTES_240,
SANE_POWER_LAST,
SANE_POWER_SHUTDOWN, // 关闭设备 239 设备重启和关闭设备不会触发热拔插,所以使用此命令时需要手动关闭设备和打开设备 1:rebootloader ,0:reboot
SANE_POWER_RESTART, // 重启设备
}SANE_Power;
typedef enum
{
SANE_EVENT_NONE = 0, // 无意义的回调, both data & len are NULL
SANE_EVENT_SUPPORT_ASYNC_IO, // 是否支持异步IO返回“0”支持其它不支持。data and len unused
SANE_EVENT_IS_MEMORY_ENOUGH, // 当前内存是否足够读取图片, data - unused*len - needed size. return SCANNER_ERR_OK or SCANNER_ERR_INSUFFICIENT_MEM
SANE_EVENT_NEED_AUTH, // data - (SANEAUTH*); len - unused; return none-zero to giveup
SANE_EVENT_DEVICE_ARRIVED, // data - SANE_Device_Ex*; *len - sizeof(SANE_Device_Ex)
SANE_EVENT_DEVICE_LEFT, // data - SANE_Device*; *len - sizeof(SANE_Device)
SANE_EVENT_STATUS, // normal status description. data - (utf8*), len - unused, be NULL
SANE_EVENT_ERROR, // error happens, should stop operations. data - (utf8*)description, *len - error code
SANE_EVENT_WORKING, // 扫描仪正在扫描, data - (char*)description, len - unused
SANE_EVENT_USB_DATA_RECEIVED, // 从USB读取到一个图片数据包当前只用作纸张统计。data - NULL, *len - bytes
SANE_EVENT_IMAGE_OK, // new image data is ready for UI. data - (SANE_Image*), *len - index of the image (ZERO base)
SANE_EVENT_IMAGE_EXT_INFO_OK, // new image data is ready for UI. data - (SANE_Img_Ext_Info*), *len - index of the image (ZERO base)
SANE_EVENT_SCAN_FINISHED, // 扫描仪完成扫描, data - (char*)description, *len - error code
SANE_EVENT_ABOUT_INFORMATION, // APP关于信息, data - (SANE_About*), len - unused, be NULL
SANE_EVENT_SCANNER_CLOSED, // 扫描仪已经关闭data & len 同 SANE_EVENT_STATUS
SANE_EVENT_IMG_UPLOADED, // APP取走一张图片, data - unused, len - count
// SANE_EVENT_WIN_DEBUG_INFO, // writedown debug info on windows platform ... data - (utf8*), *len - HG_LOG_LEVEL, param - NULL !!!
// ui event ...
SANE_EVENT_UI_CLOSE_CANCEL = 0x1000,
SANE_EVENT_UI_CLOSE_NORMAL,
SANE_EVENT_UI_SCAN_COMMAND, // setting ui notify button 'scan' clicked
SANE_EVENT_UI_CLOSE_SETTING,
SANE_EVENT_TWAIN_XFER_READY,
}SANE_Event;
#pragma pack(pop)
// 'start' and 'stop' will be blocked until image arrived or worker-threads stopped - 2023-11-01
// callback may return before these functions !!!
// the calling program should pay more attention to this situation !!!
typedef int(*sane_callback)( // 注册回调的对象,需要保证该回调是多线程安全的
SANE_Handle hdev // 产生事件的设备句柄
, int code // 回调事件代码
, void* data // 回调事件数据,根据事件代码有所不同,参照具体事件定义
, unsigned int* len // 数据长度字节或者event_data的缓冲区长度详细请看相应的事件代码
, void* param // 用户自定义数据与调用sane_init_ex传入时的保持一致
); // 返回值依不同的事件代码而定通常为“0”
extern SANE_Status sane_init_ex(SANE_Int* version_code, sane_callback cb, void* param);
enum io_code
{
IO_CTRL_CODE_BASE = 0x00C0DE111,
IO_CTRL_CODE_TEST_SINGLE = IO_CTRL_CODE_BASE,// 单张测试扫描 data - NULL, len - NULL
IO_CTRL_CODE_ABOUT_INFO, // 获取软件关于信息。data - (SANE_About*), 如果data为空或者由len指示的内存大小不足
// 则会在len中返回所需要的最小内存长度并返回 SANE_STATUS_NO_MEM 错误
IO_CTRL_CODE_RESTORE_SETTINGS, // 恢复默认设置 data - NULL, len - NULL
IO_CTRL_CODE_GET_DEFAULT_VALUE, // 获取设置项默认值 data - 同sane_control_option - SANE_ACTION_GET_VALUE时的定义 *len - [in] 设置项序号同sane_control_option中option的值
IO_CTRL_CODE_GET_FINAL_IMAGE_FORMAT, // 获取图像处理最终输出final())的图像数据格式 data - (SANE_FinalImgFormat*), len - bytes of data
IO_CTRL_CODE_SET_FINAL_IMAGE_FORMAT, // 设置图像处理最终输出final())的图像数据格式 data - (SANE_FinalImgFormat*), len - bytes of data
IO_CTRL_CODE_GET_FINAL_COMPRESSION, // 获取支持的压缩格式data - (SANE_Int*), to receive supported SANE_CompressionType, data[0]: supported counts, data[1]: default value, data[2]: current value; data[3...]: all supported values
// *len - array length of data, or need length if it was too small
IO_CTRL_CODE_SET_FINAL_COMPRESSION, // 设置图像数据最终输出的压缩格式data - (SANE_Compression*), len - bytes of data
IO_CTRL_CODE_SET_AUTO_COLOR_TYPE, // 设置自动匹配颜色模式, data - (SANE_AutoColorType*), *len - sizeof(data)
IO_CTRL_CODE_SET_CLEAR_ROLLER_COUNT, // 清除滚轴计数 data - NULL, len - to receive current roller count, can be NULL
IO_CTRL_CODE_SET_CLEAR_HISTORY_COUNT, // 清除历史计数 data - NULL, len - to receive current history count, can be NULL
IO_CTRL_CODE_GET_DEVICE_CODE, // 获取设备编码, data - (char*), *len - bytes of data
IO_CTRL_CODE_GET_PAPER_ON, // 获取进纸盘上是否有纸, data - (SANE_Bool*), *len - sizeof(SANE_Bool) datafalse 无纸 true有纸
IO_CTRL_CODE_SET_POWER_LEVEL, // 设置功耗模式(休眠), data - (SANE_Power*), *len - sizeof(SANE_Power) (包括重启设备 关闭断电)
IO_CTRL_CODE_GET_POWER_LEVEL, // 获取功耗模式(休眠), data - (SANE_Power*), *len - sizeof(SANE_Power)
IO_CTRL_CODE_GET_CUSTOM_GAMMA, // 用于获取用户自定义gamma数值data - SANE_Gamma*, *len - to receive current color mode
IO_CTRL_CODE_SET_CUSTOM_GAMMA, // 用于设置用户自定义gamma数值data - SANE_Gamma*, len - unused
IO_CTRL_CODE_DISPLAY_APP_HELP, // 显示APP帮助文档 data - NULL, len - NULL
IO_CTRL_CODE_GET_PAPER_SIZE, // 获取纸张尺寸mm。 data - (utf8*)paper name, *len - MAKELPARAM(w, h)
IO_CTRL_CODE_GET_IMAGE_QUEUE_COUNT, // 获取图像队列的长度/数量, data - NULL, len - to receive the count
IO_CTRL_CODE_CONVERT_IMAGE_FORMAT, // 图像格式转换(文件方式), data - SANE_ImageFormatConvert*, len - unused, be NULL
IO_CTRL_CODE_FREE_MEMORY, // 释放由该模块分配的内存, data - 内存块,*len - 内存块长度
IO_CTRL_CODE_GET_LOG_FILE, // 获取日志文件data - char[260],用于接收日志文件路径,返回空则获取失败;*len - 日志文件类型 SANE_LogFileType.
// 返回的日志文件,如果不再使用,调用者负责删除。
IO_CTRL_CODE_GET_SCAN_ISLOCK, // 获取设备是否支持锁定设备功能data - (SANE_Bool*), *len - sizeof(SANE_Bool)
IO_CTRL_CODE_SET_SCAN_LOCK, // 设置设备锁定, data - (SANE_Bool), len - unused
IO_CTRL_CODE_SET_SCAN_LOCK_CHECK_VAL, // 设置校验码 data - (char*), *len - bytes of data
IO_CTRL_CODE_SET_FIRMWARE_UPGRADE, // 设置固件升级, data - (char*), *len - bytes of data
IO_CTRL_CODE_GET_HISTORY_SCAN_NUM, // 获取历史张数 data - (SANE_Int*), *len - sizeof(SANE_Int)
IO_CTRL_CODE_GET_CLEAN_PAPER_ROAD, // 清除纸道 data - (SANE_Bool*), *len - sizeof(SANE_Bool)
IO_CTRL_CODE_GET_ROLLER_NUM, // 获取滚轴张数 data - (SANE_Int*), *len - sizeof(SANE_Int)
IO_CTRL_CODE_SET_FEEDMODE, // 设置分纸强度 data - (SANE_Int*) ,*len - sizeof(SANE_Int) data: 0 ,1 ,2)
IO_CTRL_CODE_SET_SKEW_CHECK, // 设置歪斜检测 data - (SANE_Bool*), *len - sizeof(SANE_Bool)
IO_CTRL_CODE_SET_SKEW_CHECK_VAL, // 设置歪斜检测值 data - (SANE_Int*), *len - sizeof(SANE_Int)
IO_CTRL_CODE_GET_IS_MODE, // 获取是否计数模式 data - (SANE_Bool*), *len - sizeof(SANE_Bool)
IO_CTRL_CODE_SET_ULTRASONIC_MODULE, // 设置超声波检测 data - (SANE_Bool*), *len - sizeof(SANE_Bool)
IO_CTRL_CODE_SET_SPEED_MODE, // 设置速度模式 data - (SANE_Int*) ,*len - sizeof(SANE_Int) data = G100-G200:100,110,120 G300:40,50,60,70 G400:40,50,60,70,80
IO_CTRL_CODE_GET_SPEED_MODE, // 获取速度模式 data - (SANE_Int*) ,*len - sizeof(SANE_Int)
IO_CTRL_CODE_SET_CIS_IMAGE, // 获取CIS原图 data - (SANE_Bool*), *len - sizeof(SANE_Bool)
IO_CTRL_CODE_GET_IMAGE, // 批量扫描 data - NULL,*len-NULL
IO_CTRL_CODE_SET_DISTORTION_IMAGE, // 设置畸变校正图 data - NULL,*len-NULL
IO_CTRL_CODE_GET_PC_DISTORTION_CHECK_VAL, // 获取PC计算畸变校正值 data - (SANE_DISTORTION_VAL*) ,*len - sizeof(SANE_DISTORTION_VAL) //通过PC算法计算获取的畸变值
IO_CTRL_CODE_SET_DISTORTION_DEVS_CHECK_VAL, // 设置设备畸变值 data - (float*) ,*len - sizeof(float)
IO_CTRL_CODE_GET_DISTORTION_DEVS_CHECK_VAL, // 获取设备畸变值 data - (float*) ,*len - sizeof(float)
IO_CTRL_CODE_SET_DEVS_REBOOT, // 设置设备重启 data - (SANE_Int*) ,*len - sizeof(SANE_Int) data 1:loader,data 0:普通重启
IO_CTRL_CODE_SET_AUTO_FALT, // 设置自动平场校正 data - (int *),*len - sizeof(SANE_Int)
// data:0(ALL) 1(200dpi、gray) 2(200dpi、color) 3(300dpi、gray) 4(300dpi、color) 5(600dpi、gray) 6(600dpi、color)
IO_CTRL_CODE_SET_COLOR, // 设置颜色 data - (char*), *len - bytes of data
IO_CTRL_CODE_SET_DPI, // 设置DPI data - (int *) *len - sizeof(int)
///////////////////////固定的硬件信息//////////////
IO_CTRL_CODE_SET_VIDPID, // 设置VID PID data - (int *) *len - sizeof(int)
IO_CTRL_CODE_GET_VIDPID, // 获取VID PID data - (int *) *len - sizeof(int)
IO_CTRL_CODE_SET_SERIAL, // 设置设备序列号 data - (char*), *len - bytes of data
IO_CTRL_CODE_GET_SERIAL, // 获取设备序列号, data - (char*), *len - bytes of data
IO_CTRL_CODE_GET_HARDWARE_VERSION, // 获取硬件版本号, data - (char*), *len - bytes of data
IO_CTRL_CODE_GET_IP, // 获取设备IP data - (char*), *len - bytes of data
IO_CTRL_CODE_SET_DEVS_MODEL, // 获取设备型号, data - (char*), *len - bytes of data
IO_CTRL_CODE_GET_DEVS_MODEL, // 设置设备型号, data - (char*), *len - bytes of data
///////////////////////未使用//////////////暂时保留
IO_CTRL_CODE_GET_SCAN_WHEN_PAPER_ON, //获取是否带纸扫描
IO_CTRL_CODE_SET_SCAN_WHEN_PAPER_ON, //设置带纸扫描
IO_CTRL_CODE_GET_SCAN_WITH_HOLE, //获取是否穿孔移除
IO_CTRL_CODE_SET_SCAN_WITH_HOLE, //设置穿孔移除
// all control code must be less than the following value
IO_CTRL_CODE_LAST = 0x10000000,
};
// Function: 直接访问控制
//
// Parameter: h - hg_open_scanner打开的设备句柄
//
// code - 控制码
//
// data - 用户分配的内存,对应于控制码的原始数据
//
// len - data中数据长度。如果是获取操作时长度小于所需要的长度则返回需要的长度且返回 E_INSUFFICIENTMEM 的错误
//
// Return: 错误码
extern SANE_Status sane_io_control(SANE_Handle h, unsigned long code, void* data, unsigned* len);
// Function: 获取错误描述
//
// Parameters: err - 其它SANE函数返回的错误代码
//
// Return: 错误描述信息(UTF-8),调用者应立即使用,无须释放
extern const char* sane_err_desc(SANE_Status err);
// Function: 读取刚刚(调用sane_get_parameters后)获取的图片的扩展信息
//
// Parameter: ext_info - 用户分配的用户获取扩展信息的内存地址
//
// len - [in]: ext_info 空间大小; [out] - ext_info中实际存储的字节数或者空间不足时需要的最小字节数
//
// Return: 错误码SANE_STATUS_GOOD - 成功获取到图像扩展信息
// SANE_STATUS_NO_DOCS - 该图片没有扩展信息
// SANE_STATUS_NO_MEM - 空间不足最小空间已经存储在参数len中
// SANE_STATUS_UNSUPPORTED - 当前版本不支持该项操作
extern SANE_Status sane_read_ext_info(SANE_Img_Ext_Info* ext_info, SANE_Int* len);
// for ui interface
typedef struct _sane_api
{
SANE_Status (*sane_get_devices_api)(const SANE_Device*** device_list, SANE_Bool local_only);
SANE_Status (*sane_open_api)(SANE_String_Const devicename, SANE_Handle* handle);
void (* sane_close_api)(SANE_Handle handle);
const SANE_Option_Descriptor* (*sane_get_option_descriptor_api)(SANE_Handle handle, SANE_Int option);
SANE_Status (*sane_control_option_api)(SANE_Handle handle, SANE_Int option, SANE_Action action, void* value, SANE_Int* info);
SANE_Status (*sane_get_parameters_api)(SANE_Handle handle, SANE_Parameters* params);
SANE_Status (*sane_start_api)(SANE_Handle handle);
SANE_Status (*sane_read_api)(SANE_Handle handle, SANE_Byte* data, SANE_Int max_length, SANE_Int* length);
void (*sane_cancel_api)(SANE_Handle handle);
SANE_Status (*sane_set_io_mode_api)(SANE_Handle handle, SANE_Bool non_blocking);
SANE_Status (*sane_get_select_fd_api)(SANE_Handle handle, SANE_Int* fd);
SANE_String_Const (*sane_strstatus_api)(SANE_Status status);
SANE_Status (*sane_io_control_api)(SANE_Handle h, unsigned long code, void* data, unsigned* len);
SANE_Status (*sane_init_api)(SANE_Int* version_code, SANE_Auth_Callback authorize);
void (*sane_exit_api)(void);
}SANEAPI, *LPSANEAPI;
#ifdef __cplusplus
}
#endif
//#include <string>
//
//template<typename ... Args>
//std::string format_string(int level, const char* fmt, Args ... args)
//{
// size_t size = snprintf(nullptr, 0, fmt, args ...) + 2;
// std::string str(size);
//
// snprintf(&str[0], size, fmt, args ...);
//
// return std::move(str);
//}
#endif /* sane_ex_h */

View File

@ -0,0 +1,508 @@
//
// definitions for option titles and values
//
// all multi-bytes letter are in UTF-8 format
//
// Date: 2022-09-30 14:23:02
//
#pragma once
// #define OPTION_TITLE_HFMRSZ "恢复默认设置"
#define OPTION_TITLE_HFMRSZ "\346\201\242\345\244\215\351\273\230\350\256\244\350\256\276\347\275\256"
// #define OPTION_TITLE_BZ "帮助"
#define OPTION_TITLE_BZ "\345\270\256\345\212\251"
// #define OPTION_TITLE_DLSC "多流输出"
#define OPTION_TITLE_DLSC "\345\244\232\346\265\201\350\276\223\345\207\272"
// #define OPTION_TITLE_DLSCLX "多流输出类型"
#define OPTION_TITLE_DLSCLX "\345\244\232\346\265\201\350\276\223\345\207\272\347\261\273\345\236\213"
// #define OPTION_VALUE_DLSCLX_W "无"
#define OPTION_VALUE_DLSCLX_W "\346\227\240"
// #define OPTION_VALUE_DLSCLX_CS_HD_HB "彩色+灰度+黑白"
#define OPTION_VALUE_DLSCLX_CS_HD_HB "\345\275\251\350\211\262+\347\201\260\345\272\246+\351\273\221\347\231\275"
// #define OPTION_VALUE_DLSCLX_CS_HD "彩色+灰度"
#define OPTION_VALUE_DLSCLX_CS_HD "\345\275\251\350\211\262+\347\201\260\345\272\246"
// #define OPTION_VALUE_DLSCLX_CS_HB "彩色+黑白"
#define OPTION_VALUE_DLSCLX_CS_HB "\345\275\251\350\211\262+\351\273\221\347\231\275"
// #define OPTION_VALUE_DLSCLX_HD_HB "灰度+黑白"
#define OPTION_VALUE_DLSCLX_HD_HB "\347\201\260\345\272\246+\351\273\221\347\231\275"
// #define OPTION_TITLE_YSMS "颜色模式"
#define OPTION_TITLE_YSMS "\351\242\234\350\211\262\346\250\241\345\274\217"
// #define OPTION_VALUE_YSMS_24WCS "24位彩色"
#define OPTION_VALUE_YSMS_24WCS "24\344\275\215\345\275\251\350\211\262"
// #define OPTION_VALUE_YSMS_256JHD "256级灰度"
#define OPTION_VALUE_YSMS_256JHD "256\347\272\247\347\201\260\345\272\246"
// #define OPTION_VALUE_YSMS_HB "黑白"
#define OPTION_VALUE_YSMS_HB "\351\273\221\347\231\275"
// #define OPTION_VALUE_YSMS_YSZDSB "颜色自动识别"
#define OPTION_VALUE_YSMS_YSZDSB "\351\242\234\350\211\262\350\207\252\345\212\250\350\257\206\345\210\253"
// #define OPTION_TITLE_HBTXYZ "黑白图像阈值"
#define OPTION_TITLE_HBTXYZ "\351\273\221\347\231\275\345\233\276\345\203\217\351\230\210\345\200\274"
// #define OPTION_TITLE_HBTXFSSC "黑白图像反色输出"
#define OPTION_TITLE_HBTXFSSC "\351\273\221\347\231\275\345\233\276\345\203\217\345\217\215\350\211\262\350\276\223\345\207\272"
// #define OPTION_TITLE_HDHHBTX_CSYZQ "灰度或黑白图像 - 除色与增强"
#define OPTION_TITLE_HDHHBTX_CSYZQ "\347\201\260\345\272\246\346\210\226\351\273\221\347\231\275\345\233\276\345\203\217 - \351\231\244\350\211\262\344\270\216\345\242\236\345\274\272"
// #define OPTION_VALUE_HDHHBTX_CSYZQ_BCS "不除色"
#define OPTION_VALUE_HDHHBTX_CSYZQ_BCS "\344\270\215\351\231\244\350\211\262"
// #define OPTION_VALUE_HDHHBTX_CSYZQ_CHS "除红色"
#define OPTION_VALUE_HDHHBTX_CSYZQ_CHS "\351\231\244\347\272\242\350\211\262"
// #define OPTION_VALUE_HDHHBTX_CSYZQ_CLS "除绿色"
#define OPTION_VALUE_HDHHBTX_CSYZQ_CLS "\351\231\244\347\273\277\350\211\262"
// #define OPTION_VALUE_HDHHBTX_CSYZQ_CHULANSE "除蓝色"
#define OPTION_VALUE_HDHHBTX_CSYZQ_CHULANSE "\351\231\244\350\223\235\350\211\262"
// #define OPTION_VALUE_HDHHBTX_CSYZQ_HSZQ "红色增强"
#define OPTION_VALUE_HDHHBTX_CSYZQ_HSZQ "\347\272\242\350\211\262\345\242\236\345\274\272"
// #define OPTION_VALUE_HDHHBTX_CSYZQ_LSZQ "绿色增强"
#define OPTION_VALUE_HDHHBTX_CSYZQ_LSZQ "\347\273\277\350\211\262\345\242\236\345\274\272"
// #define OPTION_VALUE_HDHHBTX_CSYZQ_LANSEZENGQIANG "蓝色增强"
#define OPTION_VALUE_HDHHBTX_CSYZQ_LANSEZENGQIANG "\350\223\235\350\211\262\345\242\236\345\274\272"
// #define OPTION_TITLE_24WCSTX_DLSCCH "24位彩色图像 - 多流输出除红"
#define OPTION_TITLE_24WCSTX_DLSCCH "24\344\275\215\345\275\251\350\211\262\345\233\276\345\203\217 - \345\244\232\346\265\201\350\276\223\345\207\272\351\231\244\347\272\242"
// #define OPTION_TITLE_24WCSTX_DTKCH "24位彩色图像 - 答题卡除红"
#define OPTION_TITLE_24WCSTX_DTKCH "24\344\275\215\345\275\251\350\211\262\345\233\276\345\203\217 - \347\255\224\351\242\230\345\215\241\351\231\244\347\272\242"
// #define OPTION_TITLE_BJYC "背景移除"
#define OPTION_TITLE_BJYC "\350\203\214\346\231\257\347\247\273\351\231\244"
// #define OPTION_TITLE_BJSCFDFW "背景色彩浮动范围"
#define OPTION_TITLE_BJSCFDFW "\350\203\214\346\231\257\350\211\262\345\275\251\346\265\256\345\212\250\350\214\203\345\233\264"
// #define OPTION_TITLE_RHYMH "锐化与模糊"
#define OPTION_TITLE_RHYMH "\351\224\220\345\214\226\344\270\216\346\250\241\347\263\212"
// #define OPTION_VALUE_RHYMH_W "无"
#define OPTION_VALUE_RHYMH_W "\346\227\240"
// #define OPTION_VALUE_RHYMH_RH "锐化"
#define OPTION_VALUE_RHYMH_RH "\351\224\220\345\214\226"
// #define OPTION_VALUE_RHYMH_JYBRH "进一步锐化"
#define OPTION_VALUE_RHYMH_JYBRH "\350\277\233\344\270\200\346\255\245\351\224\220\345\214\226"
// #define OPTION_VALUE_RHYMH_MH "模糊"
#define OPTION_VALUE_RHYMH_MH "\346\250\241\347\263\212"
// #define OPTION_VALUE_RHYMH_JYBMH "进一步模糊"
#define OPTION_VALUE_RHYMH_JYBMH "\350\277\233\344\270\200\346\255\245\346\250\241\347\263\212"
// #define OPTION_TITLE_QCMW "去除摩尔纹"
#define OPTION_TITLE_QCMW "\345\216\273\351\231\244\346\221\251\345\260\224\347\272\271"
// #define OPTION_TITLE_CWW "除网纹"
#define OPTION_TITLE_CWW "\351\231\244\347\275\221\347\272\271"
// #define OPTION_TITLE_CWKS "错误扩散"
#define OPTION_TITLE_CWKS "\351\224\231\350\257\257\346\211\251\346\225\243"
// #define OPTION_TITLE_HBTXZDYH "黑白图像噪点优化"
#define OPTION_TITLE_HBTXZDYH "\351\273\221\347\231\275\345\233\276\345\203\217\345\231\252\347\202\271\344\274\230\345\214\226"
// #define OPTION_TITLE_ZDYHCC "噪点优化尺寸"
#define OPTION_TITLE_ZDYHCC "\345\231\252\347\202\271\344\274\230\345\214\226\345\260\272\345\257\270"
// #define OPTION_TITLE_ZZCC "纸张尺寸"
#define OPTION_TITLE_ZZCC "\347\272\270\345\274\240\345\260\272\345\257\270"
// #define OPTION_VALUE_ZZCC_A3 "A3"
#define OPTION_VALUE_ZZCC_A3 "A3"
// #define OPTION_VALUE_ZZCC_8K "8开"
#define OPTION_VALUE_ZZCC_8K "8\345\274\200"
// #define OPTION_VALUE_ZZCC_A4 "A4"
#define OPTION_VALUE_ZZCC_A4 "A4"
// #define OPTION_VALUE_ZZCC_A4HX "A4横向"
#define OPTION_VALUE_ZZCC_A4HX "A4\346\250\252\345\220\221"
// #define OPTION_VALUE_ZZCC_16K "16开"
#define OPTION_VALUE_ZZCC_16K "16\345\274\200"
// #define OPTION_VALUE_ZZCC_16KHX "16开横向"
#define OPTION_VALUE_ZZCC_16KHX "16\345\274\200\346\250\252\345\220\221"
// #define OPTION_VALUE_ZZCC_A5 "A5"
#define OPTION_VALUE_ZZCC_A5 "A5"
// #define OPTION_VALUE_ZZCC_A5HX "A5横向"
#define OPTION_VALUE_ZZCC_A5HX "A5\346\250\252\345\220\221"
// #define OPTION_VALUE_ZZCC_A6 "A6"
#define OPTION_VALUE_ZZCC_A6 "A6"
// #define OPTION_VALUE_ZZCC_A6HX "A6横向"
#define OPTION_VALUE_ZZCC_A6HX "A6\346\250\252\345\220\221"
// #define OPTION_VALUE_ZZCC_B4 "B4"
#define OPTION_VALUE_ZZCC_B4 "B4"
// #define OPTION_VALUE_ZZCC_B5 "B5"
#define OPTION_VALUE_ZZCC_B5 "B5"
// #define OPTION_VALUE_ZZCC_B5HX "B5横向"
#define OPTION_VALUE_ZZCC_B5HX "B5\346\250\252\345\220\221"
// #define OPTION_VALUE_ZZCC_B6 "B6"
#define OPTION_VALUE_ZZCC_B6 "B6"
// #define OPTION_VALUE_ZZCC_B6HX "B6横向"
#define OPTION_VALUE_ZZCC_B6HX "B6\346\250\252\345\220\221"
// #define OPTION_VALUE_ZZCC_Letter "Letter"
#define OPTION_VALUE_ZZCC_Letter "Letter"
// #define OPTION_VALUE_ZZCC_LetterHX "Letter横向"
#define OPTION_VALUE_ZZCC_LetterHX "Letter\346\250\252\345\220\221"
// #define OPTION_VALUE_ZZCC_DoubleLetter "Double Letter"
#define OPTION_VALUE_ZZCC_DoubleLetter "Double Letter"
// #define OPTION_VALUE_ZZCC_LEGAL "LEGAL"
#define OPTION_VALUE_ZZCC_LEGAL "LEGAL"
// #define OPTION_VALUE_ZZCC_PPYSCC "匹配原始尺寸"
#define OPTION_VALUE_ZZCC_PPYSCC "\345\214\271\351\205\215\345\216\237\345\247\213\345\260\272\345\257\270"
// #define OPTION_VALUE_ZZCC_ZDSMCCZDCQ "最大扫描尺寸自动裁切"
#define OPTION_VALUE_ZZCC_ZDSMCCZDCQ "\346\234\200\345\244\247\346\211\253\346\217\217\345\260\272\345\257\270\350\207\252\345\212\250\350\243\201\345\210\207"
// #define OPTION_VALUE_ZZCC_ZDSMCC "最大扫描尺寸"
#define OPTION_VALUE_ZZCC_ZDSMCC "\346\234\200\345\244\247\346\211\253\346\217\217\345\260\272\345\257\270"
// #define OPTION_VALUE_ZZCC_SLSJ "三联试卷"
#define OPTION_VALUE_ZZCC_SLSJ "\344\270\211\350\201\224\350\257\225\345\215\267"
// #define OPTION_TITLE_ZDYSMQY "自定义扫描区域"
#define OPTION_TITLE_ZDYSMQY "\350\207\252\345\256\232\344\271\211\346\211\253\346\217\217\345\214\272\345\237\237"
// #define OPTION_TITLE_SMQYZCmm "扫描区域左侧mm"
#define OPTION_TITLE_SMQYZCmm "\346\211\253\346\217\217\345\214\272\345\237\237\345\267\246\344\276\247\357\274\210mm\357\274\211"
// #define OPTION_TITLE_SMQYYCmm "扫描区域右侧mm"
#define OPTION_TITLE_SMQYYCmm "\346\211\253\346\217\217\345\214\272\345\237\237\345\217\263\344\276\247\357\274\210mm\357\274\211"
// #define OPTION_TITLE_SMQYSCmm "扫描区域上侧mm"
#define OPTION_TITLE_SMQYSCmm "\346\211\253\346\217\217\345\214\272\345\237\237\344\270\212\344\276\247\357\274\210mm\357\274\211"
// #define OPTION_TITLE_SMQYXCmm "扫描区域下侧mm"
#define OPTION_TITLE_SMQYXCmm "\346\211\253\346\217\217\345\214\272\345\237\237\344\270\213\344\276\247\357\274\210mm\357\274\211"
// #define OPTION_TITLE_CCJC "尺寸检测"
#define OPTION_TITLE_CCJC "\345\260\272\345\257\270\346\243\200\346\265\213"
// #define OPTION_TITLE_SMYM "扫描页面"
#define OPTION_TITLE_SMYM "\346\211\253\346\217\217\351\241\265\351\235\242"
// #define OPTION_VALUE_SMYM_DM "单面"
#define OPTION_VALUE_SMYM_DM "\345\215\225\351\235\242"
// #define OPTION_VALUE_SMYM_SM "双面"
#define OPTION_VALUE_SMYM_SM "\345\217\214\351\235\242"
// #define OPTION_VALUE_SMYM_TGKBYTY "跳过空白页(通用)"
#define OPTION_VALUE_SMYM_TGKBYTY "\350\267\263\350\277\207\347\251\272\347\231\275\351\241\265\357\274\210\351\200\232\347\224\250\357\274\211"
// #define OPTION_VALUE_SMYM_TGKBYFPZ "跳过空白页(发票纸)"
#define OPTION_VALUE_SMYM_TGKBYFPZ "\350\267\263\350\277\207\347\251\272\347\231\275\351\241\265\357\274\210\345\217\221\347\245\250\347\272\270\357\274\211"
// #define OPTION_VALUE_SMYM_DZ "对折"
#define OPTION_VALUE_SMYM_DZ "\345\257\271\346\212\230"
// #define OPTION_TITLE_TGKBYLMD "跳过空白页灵敏度"
#define OPTION_TITLE_TGKBYLMD "\350\267\263\350\277\207\347\251\272\347\231\275\351\241\265\347\201\265\346\225\217\345\272\246"
// #define OPTION_TITLE_FBL "分辨率"
#define OPTION_TITLE_FBL "\345\210\206\350\276\250\347\216\207"
// #define OPTION_TITLE_HZ "画质"
#define OPTION_TITLE_HZ "\347\224\273\350\264\250"
// #define OPTION_VALUE_HZ_W "无"
#define OPTION_VALUE_HZ_W "\346\227\240"
// #define OPTION_VALUE_HZ_SDYX "速度优先"
#define OPTION_VALUE_HZ_SDYX "\351\200\237\345\272\246\344\274\230\345\205\210"
// #define OPTION_VALUE_HZ_HZYX "画质优先"
#define OPTION_VALUE_HZ_HZYX "\347\224\273\350\264\250\344\274\230\345\205\210"
// #define OPTION_TITLE_JHZFM "交换正反面"
#define OPTION_TITLE_JHZFM "\344\272\244\346\215\242\346\255\243\345\217\215\351\235\242"
// #define OPTION_TITLE_QYSDQX "启用色调曲线"
#define OPTION_TITLE_QYSDQX "\345\220\257\347\224\250\350\211\262\350\260\203\346\233\262\347\272\277"
// #define OPTION_TITLE_LDZ "亮度值"
#define OPTION_TITLE_LDZ "\344\272\256\345\272\246\345\200\274"
// #define OPTION_TITLE_DBD "对比度"
#define OPTION_TITLE_DBD "\345\257\271\346\257\224\345\272\246"
// #define OPTION_TITLE_JMZ "伽马值"
#define OPTION_TITLE_JMZ "\344\274\275\351\251\254\345\200\274"
// #define OPTION_TITLE_ZDJP "自动纠偏"
#define OPTION_TITLE_ZDJP "\350\207\252\345\212\250\347\272\240\345\201\217"
// #define OPTION_TITLE_TXCF "图像拆分"
#define OPTION_TITLE_TXCF "\345\233\276\345\203\217\346\213\206\345\210\206"
// #define OPTION_TITLE_ZPMS "照片模式"
#define OPTION_TITLE_ZPMS "\347\205\247\347\211\207\346\250\241\345\274\217"
//#define OPTION_TITLE_XCHK "消除黑框"
#define OPTION_TITLE_XCHK "\346\266\210\351\231\244\351\273\221\346\241\206"
// #define OPTION_TITLE_BJTCFS "背景填充方式"
#define OPTION_TITLE_BJTCFS "\350\203\214\346\231\257\345\241\253\345\205\205\346\226\271\345\274\217"
// #define OPTION_VALUE_BJTCFS_TDBX "凸多边形"
#define OPTION_VALUE_BJTCFS_TDBX "\345\207\270\345\244\232\350\276\271\345\275\242"
// #define OPTION_VALUE_BJTCFS_ADBX "凹多边形"
#define OPTION_VALUE_BJTCFS_ADBX "\345\207\271\345\244\232\350\276\271\345\275\242"
// #define OPTION_TITLE_SCTC "色彩填充"
#define OPTION_TITLE_SCTC "\350\211\262\345\275\251\345\241\253\345\205\205"
// #define OPTION_TITLE_YZ "阈值"
#define OPTION_TITLE_YZ "\351\230\210\345\200\274"
// #define OPTION_TITLE_BJKZDJ "背景抗噪等级"
#define OPTION_TITLE_BJKZDJ "\350\203\214\346\231\257\346\212\227\345\231\252\347\255\211\347\272\247"
// #define OPTION_TITLE_BYSJ "边缘缩进"
#define OPTION_TITLE_BYSJ "\350\276\271\347\274\230\347\274\251\350\277\233"
// #define OPTION_TITLE_SSYZ "深色样张"
#define OPTION_TITLE_SSYZ "\346\267\261\350\211\262\346\240\267\345\274\240"
// #define OPTION_TITLE_FZST "防止渗透"
#define OPTION_TITLE_FZST "\351\230\262\346\255\242\346\270\227\351\200\217"
// #define OPTION_TITLE_FZSTDJ "防止渗透等级"
#define OPTION_TITLE_FZSTDJ "\351\230\262\346\255\242\346\270\227\351\200\217\347\255\211\347\272\247"
// #define OPTION_VALUE_FZSTDJ_R "弱"
#define OPTION_VALUE_FZSTDJ_R "\345\274\261"
// #define OPTION_VALUE_FZSTDJ_JR "较弱"
#define OPTION_VALUE_FZSTDJ_JR "\350\276\203\345\274\261"
// #define OPTION_VALUE_FZSTDJ_YB "一般"
#define OPTION_VALUE_FZSTDJ_YB "\344\270\200\350\210\254"
// #define OPTION_VALUE_FZSTDJ_JQ "较强"
#define OPTION_VALUE_FZSTDJ_JQ "\350\276\203\345\274\272"
// #define OPTION_VALUE_FZSTDJ_Q "强"
#define OPTION_VALUE_FZSTDJ_Q "\345\274\272"
// #define OPTION_TITLE_CKYC "穿孔移除"
#define OPTION_TITLE_CKYC "\347\251\277\345\255\224\347\247\273\351\231\244"
// #define OPTION_TITLE_CKSSFWZFMBL "穿孔搜索范围占幅面比例"
#define OPTION_TITLE_CKSSFWZFMBL "\347\251\277\345\255\224\346\220\234\347\264\242\350\214\203\345\233\264\345\215\240\345\271\205\351\235\242\346\257\224\344\276\213"
// #define OPTION_TITLE_CKYCZC "穿孔移除—左侧"
#define OPTION_TITLE_CKYCZC "\347\251\277\345\255\224\347\247\273\351\231\244\342\200\224\345\267\246\344\276\247"
// #define OPTION_TITLE_ZCCKSSFWZFMBL "左侧穿孔搜索范围占幅面比例"
#define OPTION_TITLE_ZCCKSSFWZFMBL "\345\267\246\344\276\247\347\251\277\345\255\224\346\220\234\347\264\242\350\214\203\345\233\264\345\215\240\345\271\205\351\235\242\346\257\224\344\276\213"
// #define OPTION_TITLE_CKYCYC "穿孔移除—右侧"
#define OPTION_TITLE_CKYCYC "\347\251\277\345\255\224\347\247\273\351\231\244\342\200\224\345\217\263\344\276\247"
// #define OPTION_TITLE_YCCKSSFWZFMBL "右侧穿孔搜索范围占幅面比例"
#define OPTION_TITLE_YCCKSSFWZFMBL "\345\217\263\344\276\247\347\251\277\345\255\224\346\220\234\347\264\242\350\214\203\345\233\264\345\215\240\345\271\205\351\235\242\346\257\224\344\276\213"
// #define OPTION_TITLE_CKYCSC "穿孔移除—上侧"
#define OPTION_TITLE_CKYCSC "\347\251\277\345\255\224\347\247\273\351\231\244\342\200\224\344\270\212\344\276\247"
// #define OPTION_TITLE_SCCKSSFWZFMBL "上侧穿孔搜索范围占幅面比例"
#define OPTION_TITLE_SCCKSSFWZFMBL "\344\270\212\344\276\247\347\251\277\345\255\224\346\220\234\347\264\242\350\214\203\345\233\264\345\215\240\345\271\205\351\235\242\346\257\224\344\276\213"
// #define OPTION_TITLE_CKYCXC "穿孔移除—下侧"
#define OPTION_TITLE_CKYCXC "\347\251\277\345\255\224\347\247\273\351\231\244\342\200\224\344\270\213\344\276\247"
// #define OPTION_TITLE_XCCKSSFWZFMBL "下侧穿孔搜索范围占幅面比例"
#define OPTION_TITLE_XCCKSSFWZFMBL "\344\270\213\344\276\247\347\251\277\345\255\224\346\220\234\347\264\242\350\214\203\345\233\264\345\215\240\345\271\205\351\235\242\346\257\224\344\276\213"
// #define OPTION_TITLE_DZSM "待纸扫描"
#define OPTION_TITLE_DZSM "\345\276\205\347\272\270\346\211\253\346\217\217"
// #define OPTION_TITLE_SMZS "扫描张数"
#define OPTION_TITLE_SMZS "\346\211\253\346\217\217\345\274\240\346\225\260"
// #define OPTION_VALUE_SMZS_LXSM "连续扫描"
#define OPTION_VALUE_SMZS_LXSM "\350\277\236\347\273\255\346\211\253\346\217\217"
// #define OPTION_VALUE_SMZS_SMZDZS "扫描指定张数"
#define OPTION_VALUE_SMZS_SMZDZS "\346\211\253\346\217\217\346\214\207\345\256\232\345\274\240\346\225\260"
// #define OPTION_TITLE_SMSL "扫描数量"
#define OPTION_TITLE_SMSL "\346\211\253\346\217\217\346\225\260\351\207\217"
// #define OPTION_TITLE_WGFX "文稿方向"
#define OPTION_TITLE_WGFX "\346\226\207\347\250\277\346\226\271\345\220\221"
// #define OPTION_VALUE_WGFX_0 "0°"
#define OPTION_VALUE_WGFX_0 "0\302\260"
// #define OPTION_VALUE_WGFX_90 "90°"
#define OPTION_VALUE_WGFX_90 "90\302\260"
// #define OPTION_VALUE_WGFX_180 "180°"
#define OPTION_VALUE_WGFX_180 "180\302\260"
// #define OPTION_VALUE_WGFX__90 "-90°"
#define OPTION_VALUE_WGFX__90 "-90\302\260"
// #define OPTION_VALUE_WGFX_ZDWBFXSB "自动文本方向识别°"
#define OPTION_VALUE_WGFX_ZDWBFXSB "\350\207\252\345\212\250\346\226\207\346\234\254\346\226\271\345\220\221\350\257\206\345\210\253\302\260"
// #define OPTION_TITLE_BMXZ180 "背面旋转180°"
#define OPTION_TITLE_BMXZ180 "\350\203\214\351\235\242\346\227\213\350\275\254180\302\260"
// #define OPTION_TITLE_CSBJC "超声波检测"
#define OPTION_TITLE_CSBJC "\350\266\205\345\243\260\346\263\242\346\243\200\346\265\213"
// #define OPTION_TITLE_SZTPCL "双张图片处理"
#define OPTION_TITLE_SZTPCL "\345\217\214\345\274\240\345\233\276\347\211\207\345\244\204\347\220\206"
// #define OPTION_VALUE_SZTPCL_DQTXBTZSM "丢弃图像并停止扫描"
#define OPTION_VALUE_SZTPCL_DQTXBTZSM "\344\270\242\345\274\203\345\233\276\345\203\217\345\271\266\345\201\234\346\255\242\346\211\253\346\217\217"
// #define OPTION_VALUE_SZTPCL_DQTXBJXSM "丢弃图像并继续扫描"
#define OPTION_VALUE_SZTPCL_DQTXBJXSM "\344\270\242\345\274\203\345\233\276\345\203\217\345\271\266\347\273\247\347\273\255\346\211\253\346\217\217"
// #define OPTION_VALUE_SZTPCL_SCTXBTZSM "上传图像并停止扫描"
#define OPTION_VALUE_SZTPCL_SCTXBTZSM "\344\270\212\344\274\240\345\233\276\345\203\217\345\271\266\345\201\234\346\255\242\346\211\253\346\217\217"
// #define OPTION_VALUE_SZTPCL_SCTXBJXSM "上传图像并继续扫描"
#define OPTION_VALUE_SZTPCL_SCTXBJXSM "\344\270\212\344\274\240\345\233\276\345\203\217\345\271\266\347\273\247\347\273\255\346\211\253\346\217\217"
// #define OPTION_TITLE_ZDJC "装订检测"
#define OPTION_TITLE_ZDJC "\350\243\205\350\256\242\346\243\200\346\265\213"
// #define OPTION_TITLE_WXJC "歪斜检测"
#define OPTION_TITLE_WXJC "\346\255\252\346\226\234\346\243\200\346\265\213"
// #define OPTION_TITLE_WXRRD "歪斜容忍度"
#define OPTION_TITLE_WXRRD "\346\255\252\346\226\234\345\256\271\345\277\215\345\272\246"
// #define OPTION_TITLE_ZJJC "折角检测"
#define OPTION_TITLE_ZJJC "\346\212\230\350\247\222\346\243\200\346\265\213"
// #define OPTION_TITLE_ZJDX "折角大小"
#define OPTION_TITLE_ZJDX "\346\212\230\350\247\222\345\244\247\345\260\217"
// #define OPTION_TITLE_FZQD "分纸强度"
#define OPTION_TITLE_FZQD "\345\210\206\347\272\270\345\274\272\345\272\246"
// #define OPTION_VALUE_FZQD_R "弱"
#define OPTION_VALUE_FZQD_R "\345\274\261"
// #define OPTION_VALUE_FZQD_YB "一般"
#define OPTION_VALUE_FZQD_YB "\344\270\200\350\210\254"
// #define OPTION_VALUE_FZQD_Q "强"
#define OPTION_VALUE_FZQD_Q "\345\274\272"
// #define OPTION_TITLE_XMSJ "休眠时间"
#define OPTION_TITLE_XMSJ "\344\274\221\347\234\240\346\227\266\351\227\264"
// #define OPTION_VALUE_XMSJ_BXM "不休眠"
#define OPTION_VALUE_XMSJ_BXM "\344\270\215\344\274\221\347\234\240"
// #define OPTION_VALUE_XMSJ_WFZ "五分钟"
#define OPTION_VALUE_XMSJ_WFZ "\344\272\224\345\210\206\351\222\237"
// #define OPTION_VALUE_XMSJ_SFZ "十分钟"
#define OPTION_VALUE_XMSJ_SFZ "\345\215\201\345\210\206\351\222\237"
// #define OPTION_VALUE_XMSJ_BXS "半小时"
#define OPTION_VALUE_XMSJ_BXS "\345\215\212\345\260\217\346\227\266"
// #define OPTION_VALUE_XMSJ_YXS "一小时"
#define OPTION_VALUE_XMSJ_YXS "\344\270\200\345\260\217\346\227\266"
// #define OPTION_VALUE_XMSJ_LXS "两小时"
#define OPTION_VALUE_XMSJ_LXS "\344\270\244\345\260\217\346\227\266"
// #define OPTION_VALUE_XMSJ_SXS "四小时"
#define OPTION_VALUE_XMSJ_SXS "\345\233\233\345\260\217\346\227\266"
// #define OPTION_TITLE_ZDFZQD "自动分纸强度"
#define OPTION_TITLE_ZDFZQD "\350\207\252\345\212\250\345\210\206\347\272\270\345\274\272\345\272\246"
// #define OPTION_TITLE_JZSBL "进纸失败率"
#define OPTION_TITLE_JZSBL "\350\277\233\347\272\270\345\244\261\350\264\245\347\216\207"
//#define OPTION_TITLE_DZMS "对折模式"
#define OPTION_TITLE_DZMS "\345\257\271\346\212\230\346\250\241\345\274\217"
//#define OPTION_VALUE_ZYDZ "左右对折"
#define OPTION_VALUE_ZYDZ "\345\267\246\345\217\263\345\257\271\346\212\230"
//#define OPTION_VALUE_SXDZ "上下对折"
#define OPTION_VALUE_SXDZ "\344\270\212\344\270\213\345\257\271\346\212\230"
//#define OPTION_VALUE_SXDZ "自动对折"
#define OPTION_VALUE_ZDDZ "\350\207\252\345\212\250\345\257\271\346\212\230"
//#define OPTION_TITLE_SPJZ "色偏校正"
#define OPTION_TITLE_SPJZ "\350\211\262\345\201\217\346\240\241\346\255\243"
//#define OPTION_TITLE_DZSMTCSJ "待纸扫描退出时间"
#define OPTION_TITLE_DZSMTCSJ "\345\276\205\347\272\270\346\211\253\346\217\217\351\200\200\345\207\272\346\227\266\351\227\264"
//#define OPTION_TITLE_TGKBY "跳过空白页"
#define OPTION_TITLE_TGKBY "\350\267\263\350\277\207\347\251\272\347\231\275\351\241\265"
////////////////////////////////////////////////////////////////
// reserved ...
////////////////////////////////////////////////////////////////
#define OPTION_TITLE_GMZ OPTION_TITLE_JMZ
////////////////////////////////////////////////////////////////
// string compare ...
////////////////////////////////////////////////////////////////
#include <string.h>
#define IMPLEMENT_OPTION_STRING_COMPARE(func_name) \
bool func_name(const char* opt_define, const char* value) \
{ \
while(*value++ == L' '); \
value--; \
return strcmp(opt_define, value) == 0; \
}

160
sdk/sane/sanei.h Normal file
View File

@ -0,0 +1,160 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 1996 David Mosberger-Tang and Andreas Beck
Copyright (C) 2002, 2003 Henning Meier-Geinitz
This file is part of the SANE package.
SANE is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
SANE is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with sane; see the file COPYING. If not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
As a special exception, the authors of SANE give permission for
additional uses of the libraries contained in this release of SANE.
The exception is that, if you link a SANE library with other files
to produce an executable, this does not by itself cause the
resulting executable to be covered by the GNU General Public
License. Your use of that executable is in no way restricted on
account of linking the SANE library code into it.
This exception does not, however, invalidate any other reasons why
the executable file might be covered by the GNU General Public
License.
If you submit changes to SANE to the maintainers to be included in
a subsequent release, you agree by submitting the changes that
those changes may be distributed with this exception intact.
If you write modifications of your own for SANE, it is your choice
whether to permit this exception to apply to your modifications.
If you do not wish that, delete this exception notice.
*/
/** @file sanei.h
* Convenience macros and function declarations for backends
* @sa sanei_backend.h sanei_thread.h
*/
/* Doxygen documentation */
/** @mainpage SANEI (SANE internal routines) documentation
*
* @image html sane-logo2.jpg
* @section intro Introduction
*
* The header files in the include/sane/ directory named sanei_*.h provide
* function declarations and macros that can be used by every SANE backend.
* Their implementations can be found in the sanei/ directory. The code aims
* to be platform-independent to avoid lots of \#ifdef code in the backends.
* Please use the SANEI functions wherever possible.
*
* This documentation was created by the use of doxygen, the
* doc/doxygen-sanei.conf configuration file and documentation in the sanei_*.h
* files.
*
* This documentation is far from complete. Any help is appreciated.
*
* @section additional Additional documentation
* - The SANE standard can be found at <a
* href="http://www.sane-project.org/html/">the SANE webserver</a>,
* though the PostScript version produced from the source may be more recent.
* - Information on how to write a backend: <a
* href="../backend-writing.txt">backend-writing.txt</a>.
* - General SANE documentation is on <a
* href="http://www.sane-project.org/docs.html">the SANE documentation
* page</a>.
*
* @section contact Contact
*
* The common way to contact the developers of SANE is the sane-devel
* mailing list. See the <a
* href="http://www.sane-project.org/mailing-lists.html">mailing list webpage</a>
* for details. That's the place to ask questions, report bugs, or announce
* a new backend.
*
*/
#ifndef sanei_h
#define sanei_h
#include <sane/sane.h>
/** @name Public macros and functions
* @{
*/
/** @def STRINGIFY(x)
* Turn parameter into string.
*/
/** @def PASTE(x,y)
* Concatenate parameters.
*
*/
/** @def NELEMS(a)
* Return number of elements of an array.
*
*/
/** @fn extern SANE_Status sanei_check_value (const SANE_Option_Descriptor * opt, void * value);
* Check the constraints of a SANE option.
*
* @param opt option to check
* @param value value of the option
*
* @return
* - SANE_STATUS_GOOD - on success
* - SANE_STATUS_INVAL - if the value doesn't fit inside the constraint
* or any other error occured
* @sa sanei_constrain_value()
*/
/** @fn extern SANE_Status sanei_constrain_value (const SANE_Option_Descriptor * opt, void * value, SANE_Word * info);
* Check the constraints of a SANE option and adjust its value if necessary.
*
* Depending on the type of the option and constraint, value is modified
* to fit inside constraint.
*
* @param opt option to check
* @param value value of the option
* @param info info is set to SANE_INFO_INEXACT if value was changed
*
* @return
* - SANE_STATUS_GOOD - on success
* - SANE_STATUS_INVAL - if the function wasn't able to fit value into the
* constraint or any other error occured
* @sa sanei_check_value()
*/
/* @} */
/* A few convenience macros: */
/** @hideinitializer */
#define NELEMS(a) ((int)(sizeof (a) / sizeof (a[0])))
/** @hideinitializer */
#define STRINGIFY1(x) #x
/** @hideinitializer */
#define STRINGIFY(x) STRINGIFY1(x)
/** @hideinitializer */
#define PASTE1(x,y) x##y
/** @hideinitializer */
#define PASTE(x,y) PASTE1(x,y)
extern SANE_Status sanei_check_value (const SANE_Option_Descriptor * opt,
void * value);
extern SANE_Status sanei_constrain_value (const SANE_Option_Descriptor * opt,
void * value, SANE_Word * info);
#endif /* sanei_h */

192
sdk/sane/sanei_backend.h Normal file
View File

@ -0,0 +1,192 @@
/** @file sanei_backend.h
* Compatibility header file for backends
*
* This file provides some defines for macros missing on some platforms.
* It also has the SANE API entry points. sanei_backend.h must be included
* by every backend.
*
* @sa sanei.h sanei_thread.h
*/
/*
* Compiler related options
*/
/** Mark unused variables/parameters
*
* Tells the compiler a variable is unused, so the compiler doesn't spit a warning.
*/
#ifdef __GNUC__
#define __sane_unused__ __attribute__((unused))
#else
#define __sane_unused__
#endif
/** @name Compatibility macros
* @{
*/
#include "sanei_debug.h"
#ifdef HAVE_SYS_HW_H
/* OS/2 i/o-port access compatibility macros: */
# define inb(p) _inp8 (p)
# define outb(v,p) _outp8 ((p),(v))
# define ioperm(b,l,o) _portaccess ((b),(b)+(l)-1)
# define HAVE_IOPERM 1
#endif
#ifndef HAVE_OS2_H
#include <fcntl.h>
#ifndef O_NONBLOCK
# ifdef O_NDELAY
# define O_NONBLOCK O_NDELAY
# else
# ifdef FNDELAY
# define O_NONBLOCK FNDELAY /* last resort */
# endif
# endif
#endif
#endif /* HAVE_OS2_H */
#include <limits.h>
#ifndef PATH_MAX
# define PATH_MAX 1024
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#ifndef MM_PER_INCH
#define MM_PER_INCH 25.4
#endif
#ifdef HAVE_SIGPROCMASK
# define SIGACTION sigaction
#else
/* Just enough backwards compatibility that we get by in the backends
without making handstands. */
# ifdef sigset_t
# undef sigset_t
# define sigset_t int
# endif
# ifdef sigemptyset
# undef sigemptyset
# endif
# ifdef sigfillset
# undef sigfillset
# endif
# ifdef sigaddset
# undef sigaddset
# endif
# ifdef sigdelset
# undef sigdelset
# endif
# ifdef sigprocmask
# undef sigprocmask
# endif
# ifdef SIG_BLOCK
# undef SIG_BLOCK
# endif
# ifdef SIG_UNBLOCK
# undef SIG_UNBLOCK
# endif
# ifdef SIG_SETMASK
# undef SIG_SETMASK
# endif
# define sigemptyset(set) do { *(set) = 0; } while (0)
# define sigfillset(set) do { *(set) = ~0; } while (0)
# define sigaddset(set,signal) do { *(set) |= sigmask (signal); } while (0)
# define sigdelset(set,signal) do { *(set) &= ~sigmask (signal); } while (0)
# define sigaction(sig,new,old) sigvec (sig,new,old)
/* Note: it's not safe to just declare our own "struct sigaction" since
some systems (e.g., some versions of OpenStep) declare that structure,
but do not implement sigprocmask(). Hard to believe, aint it? */
# define SIGACTION sigvec
# define SIG_BLOCK 1
# define SIG_UNBLOCK 2
# define SIG_SETMASK 3
#endif /* !HAVE_SIGPROCMASK */
/* @} */
/** @name Declaration of entry points:
* @{
*/
extern SANE_Status ENTRY(init) (SANE_Int *, SANE_Auth_Callback);
extern SANE_Status ENTRY(get_devices) (const SANE_Device ***, SANE_Bool);
extern SANE_Status ENTRY(open) (SANE_String_Const, SANE_Handle *);
extern const SANE_Option_Descriptor *
ENTRY(get_option_descriptor) (SANE_Handle, SANE_Int);
extern SANE_Status ENTRY(control_option) (SANE_Handle, SANE_Int, SANE_Action,
void *, SANE_Word *);
extern SANE_Status ENTRY(get_parameters) (SANE_Handle, SANE_Parameters *);
extern SANE_Status ENTRY(start) (SANE_Handle);
extern SANE_Status ENTRY(read) (SANE_Handle, SANE_Byte *, SANE_Int,
SANE_Int *);
extern SANE_Status ENTRY(set_io_mode) (SANE_Handle, SANE_Bool);
extern SANE_Status ENTRY(get_select_fd) (SANE_Handle, SANE_Int *);
extern void ENTRY(cancel) (SANE_Handle);
extern void ENTRY(close) (SANE_Handle);
extern void ENTRY(exit) (void);
extern void sanei_debug_msg(int level, int max_level, const char* be, const char* fmt, va_list ap);
#ifndef STUBS
/* Now redirect sane_* calls to backend's functions: */
#define sane_init(a,b) ENTRY(init) (a,b)
#define sane_get_devices(a,b) ENTRY(get_devices) (a,b)
#define sane_open(a,b) ENTRY(open) (a,b)
#define sane_get_option_descriptor(a,b) ENTRY(get_option_descriptor) (a,b)
#define sane_control_option(a,b,c,d,e) ENTRY(control_option) (a,b,c,d,e)
#define sane_get_parameters(a,b) ENTRY(get_parameters) (a,b)
#define sane_start(a) ENTRY(start) (a)
#define sane_read(a,b,c,d) ENTRY(read) (a,b,c,d)
#define sane_set_io_mode(a,b) ENTRY(set_io_mode) (a,b)
#define sane_get_select_fd(a,b) ENTRY(get_select_fd) (a,b)
#define sane_cancel(a) ENTRY(cancel) (a)
#define sane_close(a) ENTRY(close) (a)
#define sane_exit(a) ENTRY(exit) (a)
#define sane_strstatus(a) ENTRY(strstatus)(a)
////////////////////////////////////////////////////////////////////////////////
// extension for standard SANE ...
#define sane_init_ex(a, b, c) ENTRY(init_ex) (a,b,c)
#define sane_io_control(a,b,c,d) ENTRY(io_control)(a,b,c,d)
#define sane_err_desc(a) ENTRY(err_desc)(a)
#define sane_get_option_descriptor_ex(a,b) ENTRY(get_option_descriptor_ex) (a,b) // added on 2023-01-06, option can be 'key'
#define sane_control_option_ex(a,b,c,d,e) ENTRY(control_option_ex) (a,b,c,d,e) // added on 2023-01-06, option can be 'key'
#define sane_read_ext_info(a, b) ENTRY(read_ext_info)(a, b) // added on 2023-01-13
#endif /* STUBS */
/* @} */
/** Internationalization for SANE backends
*
* Add SANE_I18N() to all texts that can be translated.
* E.g. out_txt = SANE_I18N("Hello");
*/
#ifndef SANE_I18N
#define SANE_I18N(text) text
#endif
/** Option_Value union
*
* Convenience union to access option values given to the backend
*/
#ifndef SANE_OPTION
typedef union
{
SANE_Bool b; /**< bool */
SANE_Word w; /**< word */
SANE_Word *wa; /**< word array */
SANE_String s; /**< string */
}
Option_Value;
#define SANE_OPTION 1
#endif

153
sdk/sane/sanei_debug.h Normal file
View File

@ -0,0 +1,153 @@
/** @file sanei_debug.h
* Support for printing debug messages.
*
* Use the functions of this header file to print debug or warning messages.
*/
#ifndef _SANEI_DEBUG_H
#define _SANEI_DEBUG_H
#include <sane/sanei.h>
/** @name Public macros
* These macros can be used in backends and other SANE-related
* code.
*
* Before including sanei_debug.h, the following macros must be set:
*
* - BACKEND_NAME - The name of your backend without double-quotes (must be set in any case)
* - STUBS - If this is defined, no macros will be included. Used in
* backends consisting of more than one .c file.
* - DEBUG_DECLARE_ONLY - Generates prototypes instead of functions. Used in
* backends consisting of more than one .c file.
* - DEBUG_NOT_STATIC - Doesn't generate static functions. Used in header files if
* they are include in more than one .c file.
*
* @{
*/
/** @def DBG_INIT()
* Initialize sanei_debug.
*
* Call this function before you use any DBG function.
*/
/** @def DBG(level, fmt, ...)
* Print a message at debug level `level' or higher using a printf-like
* function. Example: DBG(1, "sane_open: opening fd \%d\\n", fd).
*
* @param level debug level
* @param fmt format (see man 3 printf for details)
* @param ... additional arguments
*/
/** @def IF_DBG(x)
* Compile code only if debugging is enabled.
*
* Expands to x if debug support is enabled at compile-time. If NDEBUG is
* defined at compile-time this macro expands to nothing.
*
* @param x code to expand when debugging is enabled
*/
/**
* @def DBG_LEVEL
* Current debug level.
*
* You can only read this "variable".
*/
/** @def ENTRY(name)
* Expands to sane_BACKEND_NAME_name.
*
* Example: ENTRY(init) in mustek.c will expand to sane_mustek_init.
*/
/* @} */
/** @hideinitializer*/
#define ENTRY(name) PASTE(PASTE(PASTE(sane_,BACKEND_NAME),_),name)
#ifdef NDEBUG
extern void sanei_debug_ndebug (int level, const char *msg, ...);
# define DBG_LEVEL (0)
# define DBG_INIT()
# define DBG sanei_debug_ndebug
# define IF_DBG(x)
#else /* !NDEBUG */
/** @hideinitializer*/
# define DBG_LEVEL PASTE(sanei_debug_,BACKEND_NAME)
# if defined(BACKEND_NAME) && !defined(STUBS)
# ifdef DEBUG_DECLARE_ONLY
extern int DBG_LEVEL;
# else /* !DEBUG_DECLARE_ONLY */
int DBG_LEVEL = 0;
# endif /* DEBUG_DECLARE_ONLY */
# endif /* BACKEND_NAME && !STUBS */
/** @hideinitializer*/
# define DBG_INIT() \
sanei_init_debug (STRINGIFY(BACKEND_NAME), &DBG_LEVEL)
/** @hideinitializer*/
# define DBG_LOCAL PASTE(DBG_LEVEL,_call)
# ifndef STUBS
# ifdef DEBUG_DECLARE_ONLY
extern void DBG_LOCAL (int level, const char *msg, ...)
#ifdef __GNUC__
__attribute__ ((format (printf, 2, 3)))
#endif
;
# else /* !DEBUG_DECLARE_ONLY */
# include <stdarg.h>
extern void sanei_debug_msg
(int level, int max_level, const char *be, const char *fmt, va_list ap);
#ifdef __GNUC__
# ifndef DEBUG_NOT_STATIC
static
# endif /* !DEBUG_NOT_STATIC */
void DBG_LOCAL (int level, const char *msg, ...) __attribute__ ((format (printf, 2, 3)));
#endif /* __GNUC__ */
# ifndef DEBUG_NOT_STATIC
static
# endif /* !DEBUG_NOT_STATIC */
void
DBG_LOCAL (int level, const char *msg, ...)
{
va_list ap;
va_start (ap, msg);
sanei_debug_msg (level, DBG_LEVEL, STRINGIFY(BACKEND_NAME), msg, ap);
va_end (ap);
}
# endif /* DEBUG_DECLARE_ONLY */
# endif /* !STUBS */
/** @hideinitializer*/
# define DBG DBG_LOCAL
extern void sanei_init_debug (const char * backend, int * debug_level_var);
/** @hideinitializer*/
# define IF_DBG(x) x
#endif /* NDEBUG */
#endif /* _SANEI_DEBUG_H */

View File

@ -0,0 +1,97 @@
#include "base_opt.h"
#include <json/gb_json.h>
#include <huagao/hgscanner_error.h>
sane_opt_provider::sane_opt_provider()
{
set_where(nullptr);
}
sane_opt_provider::~sane_opt_provider()
{
for (auto& v : following_)
v.second->release();
following_.clear();
}
bool sane_opt_provider::set_opt_json_text(char* txt)
{
gb_json* jsn = new gb_json();
bool ret = jsn->attach_text(txt);
jsn->release();
if (ret)
opt_jsn_txt_ = txt;
else
opt_jsn_txt_ = "";
return ret;
}
void sane_opt_provider::set_where(const char* where)
{
if (where && *where)
{
where_ = where;
}
else
{
char buf[20] = { 0 };
sprintf(buf, "%p", this);
where_ = buf;
}
}
const char* sane_opt_provider::get_opt_json(void)
{
return opt_jsn_txt_.c_str();
}
const char* sane_opt_provider::from(void)
{
return where_.c_str();
}
void sane_opt_provider::set_following_provider(const char* name, sane_opt_provider* following)
{
if (following_.count(name))
{
following_[name]->release();
following_.erase(name);
}
if (following)
{
following_[name] = following;
following->add_ref();
}
}
sane_opt_provider* sane_opt_provider::get_following(const char* name)
{
sane_opt_provider* prvd = nullptr;
if (following_.count(name))
{
prvd = following_[name];
if (prvd)
prvd->add_ref();
}
return prvd;
}
char* sane_opt_provider::get_value(const char* name, void* value, size_t* size, int* err)
{
if (err)
*err = SCANNER_ERR_DEVICE_NOT_SUPPORT;
if (size)
*size = 0;
return nullptr;
}
int sane_opt_provider::set_value(const char* name, void* val)
{
return SCANNER_ERR_DEVICE_NOT_SUPPORT;
}
void sane_opt_provider::enable(const char* name, bool able)
{}

View File

@ -0,0 +1,44 @@
#pragma once
// SANE-Option
//
// created on 2022-10-24
//
#include <string>
#include <map>
#include <base/utils.h> // for refer
class sane_opt_provider : public refer
{
bool is_in_another_module_;
std::string opt_jsn_txt_;
std::string where_;
protected:
std::map<std::string, sane_opt_provider*> following_;
public:
sane_opt_provider();
protected:
virtual ~sane_opt_provider();
bool set_opt_json_text(char* txt);
void set_where(const char* where);
public:
const char* get_opt_json(void); // if no content, return "" plz.
const char* from(void); // if no content, return "" plz.
bool is_in_another_module(void) { return is_in_another_module_; }
void set_following_provider(const char* name, sane_opt_provider* following); // when option has provided by more than one
sane_opt_provider* get_following(const char* name); // caller should ->release returned value
public:
// return malloc(), real data size stored in parameter 'size'. invoker should free() the returned value
virtual char* get_value(const char* name, void* value, size_t* size, int* err = nullptr);
virtual int set_value(const char* name, void* val);
virtual void enable(const char* name, bool able);
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,412 @@
//
// device_opt: option manager of device
//
// Created: 2023-09-07
//
#pragma once
#include <string>
#include <vector>
#include <map>
#include <functional>
#include <algorithm>
#include <memory>
#include "simple_logic.h"
#include <json/gb_json.h>
#include <base/utils.h>
#include <sane/sane_ex.h>
class sane_opt_provider;
class device_option : public refer
{
gb_json* origin_;
gb_json* now_;
std::map<std::string, sane_opt_provider*> src_;
std::vector<std::string> master_opts_; // options that value changed will affect others
std::map<std::string, simple_logic*> slaver_;
std::function<bool(int)> user_;
std::function<void(const char*)> log_;
typedef struct _expr_calc
{
std::string name;
std::string val1;
std::string val2;
bool not_op;
bool(*compare)(gb_json*, void* val, void* v1, void* v2);
}EXPRCALC;
std::map<std::string, EXPRCALC> compare_; // simple condition compare
class condition_value
{
typedef struct _cond_val
{
simple_logic *logic;
std::string value;
}CONDVAL;
std::vector<CONDVAL> vals_;
device_option* parent_; // if this value was valid, the condition value is a consistant value with vals_[0].value
void clear(void)
{
for (auto& v : vals_)
{
if (v.logic)
delete v.logic;
}
vals_.clear();
parent_ = nullptr;
}
public:
condition_value() : parent_(nullptr)
{}
~condition_value()
{
clear();
}
public:
bool set_value(gb_json* jsn, const char* type, device_option* parent); // jsn contains only ONE value or its object, or nullptr for a consistant value
std::string value(bool(*compare)(const char*, void*), void* param);
};
class range_value
{
bool is_range_; // true - range; false - list
int val_ind_;
std::vector<condition_value*> vals_;
void clear(void)
{
for (auto& v : vals_)
delete v;
vals_.clear();
}
public:
range_value() : is_range_(false), val_ind_(0)
{}
~range_value()
{
clear();
}
public:
bool set_value(gb_json* jsn, const char* type, device_option *parent); // jsn contains all range object
int count(void)
{
return vals_.size();
}
bool is_range(void)
{
return is_range_;
}
// return first element in list-value or min-value of range
std::string first_value(bool(*compare)(const char*, void*), void* param)
{
val_ind_ = 0;
if (val_ind_ < count())
return vals_[val_ind_]->value(compare, param);
else
return "";
}
// return next element in list-value or max-value of range
std::string next_value(bool(*compare)(const char*, void*), void* param)
{
if (++val_ind_ < count())
return vals_[val_ind_]->value(compare, param);
else
return "";
}
};
std::map<std::string, range_value*> range_value_;
std::map<std::string, condition_value*> init_value_;
std::map<std::string, condition_value*> support_value_;
std::map<std::string, std::vector<std::string> > depend_opts_; // values that depend on other option's current value <master option name, [slaver-option-nam.field ...]>
static bool is_equal_b(gb_json* opt, void* val, void* v1, void* v2);
static bool is_equal_i(gb_json* opt, void* val, void* v1, void* v2);
static bool is_equal_f(gb_json* opt, void* val, void* v1, void* v2);
static bool is_equal_s(gb_json* opt, void* val, void* v1, void* v2);
static bool is_less_b(gb_json* opt, void* val, void* v1, void* v2);
static bool is_less_i(gb_json* opt, void* val, void* v1, void* v2);
static bool is_less_f(gb_json* opt, void* val, void* v1, void* v2);
static bool is_less_s(gb_json* opt, void* val, void* v1, void* v2);
static bool is_great_b(gb_json* opt, void* val, void* v1, void* v2);
static bool is_great_i(gb_json* opt, void* val, void* v1, void* v2);
static bool is_great_f(gb_json* opt, void* val, void* v1, void* v2);
static bool is_great_s(gb_json* opt, void* val, void* v1, void* v2);
static bool is_between_b(gb_json* opt, void* val, void* v1, void* v2);
static bool is_between_i(gb_json* opt, void* val, void* v1, void* v2);
static bool is_between_f(gb_json* opt, void* val, void* v1, void* v2);
static bool is_between_s(gb_json* opt, void* val, void* v1, void* v2);
static bool is_opt_enabled(gb_json* opt, void* val, void* v1, void* v2);
static bool get_equal(const char* type, bool(**f)(gb_json*, void*, void*, void*));
static bool get_less(const char* type, bool(**f)(gb_json*, void*, void*, void*));
static bool get_great(const char* type, bool(**f)(gb_json*, void*, void*, void*));
static bool get_between(const char* type, bool(**f)(gb_json*, void*, void*, void*));
// Function: parse string function - .left(cnt), .right(cnt), .mid(start, cnt)
//
// Parameter: expr - expression of string function, e.g. mode.left(2)
//
// name - to receive the final option name, e.g. mode
//
// start - to receive the starting position of the sub-string, negative is for right()
//
// cnt - to receive the length of the sub string, -1 is to end
//
// Return: true if was string function
static bool is_string_function(const char* expr, std::string& name, int& start, int& cnt);
static std::string from_text_value(const char* type, const char* text_val);
static bool parse_simple_logic_expression(gb_json* root, const char* expr, std::string* name, EXPRCALC& calc);
static void init_condition(const char* expr, void* param);
static bool calc_simple_logic_expression(const char* expr, void* param);
void clear_for_reconstruct(void);
gb_json* group_opt(const char* title);
int next_group(int start); // return index of the next group
int insert_group(const char* name, const char* title); // return index of the group
void insert_option(gb_json* opt, sane_opt_provider* from, const char* group = nullptr);
bool arrange_raw_json(sane_opt_provider* sop); // create origin_ and re-arrange groups
void init_depends(gb_json* opt);
gb_json* copy_opt(gb_json* from);
int visibility(gb_json* jsn);
bool to_now(bool init, bool* changed);
protected:
static std::string option_value(gb_json* jsn, bool def_val);
template<class T>
static condition_value* to_condition_value(gb_json* jsn, const char* key, const char* type, device_option* parent)
{
condition_value* ret = nullptr;
gb_json* child = nullptr;
if (!jsn->get_value(key, child))
{
T v;
if(jsn->get_value(key, v))
child = new gb_json("", v);
else
{
std::string sv("");
if (jsn->get_value(key, sv))
{
// consistant with another option ...
ret = new condition_value();
ret->set_value(nullptr, sv.c_str(), parent);
if (std::find(parent->master_opts_.begin(), parent->master_opts_.end(), sv)
== parent->master_opts_.end())
parent->master_opts_.push_back(sv);
}
}
}
if (child)
{
ret = new condition_value();
if (!ret->set_value(child, type, parent))
{
delete ret;
ret = nullptr;
}
child->release();
}
return ret;
}
template<class T>
bool get_range(gb_json* jsn, const char* key, T& val)
{
if (jsn->get_value(key, val))
return true;
std::string optn("");
if (!jsn->get_value(key, optn))
return false;
gb_json* opt = nullptr;
if (now_)
now_->get_value(optn.c_str(), opt);
if (!opt && origin_)
origin_->get_value(optn.c_str(), opt);
if (!opt)
return false;
bool ret = opt->get_value("cur", val);
opt->release();
return ret;
}
template<class T>
bool refine_data_to_range(gb_json* jsn, void* value)
{
bool refined = false;
gb_json* range = nullptr;
jsn->get_value("range", range);
if (range)
{
T vl, vu, s;
if (get_range<T>(range, "min", vl))
{
if (*(T*)value < vl)
{
*(T*)value = vl;
refined = true;
}
else if (get_range<T>(range, "max", vu))
{
if (*(T*)value > vu)
{
*(T*)value = vu;
refined = true;
}
else if (get_range<T>(range, "step", s))
{
// step check, FIXED me ...
T cur(*(T*)value);
cur -= vl;
cur /= s;
if (!IS_DOUBLE_EQUAL(cur, (int)cur))
{
cur *= s;
cur += vl;
if (cur > vu)
cur = vu;
refined = !IS_DOUBLE_EQUAL(cur, *(T*)value);
*(T*)value = cur;
}
}
}
}
else
{
gb_json* val = range->first_child();
bool found = false;
while (val)
{
if (val->value(vl))
{
if (*(T*)value == vl)
{
found = true;
val->release();
break;
}
}
val->release();
val = range->next_child();
}
if (!found)
{
if (jsn->get_value("default", vl))
{
refined = true;
*(T*)value = vl;
}
}
}
range->release();
}
return refined;
}
template<typename ... Args>
void write_log(const char* fmt, Args ... args)
{
if (log_)
{
size_t size = snprintf(nullptr, 0, fmt, args ...) + 2;
std::unique_ptr<char[]> buf(new char[size]);
snprintf(buf.get(), size, fmt, args ...);
log_(buf.get());
}
}
public:
device_option(std::function<bool(int)> user_priv = std::function<bool(int)>()
, std::function<void(const char*)> log = std::function<void(const char*)>());
~device_option();
static std::string trans_group(const char* utf8, bool to_title);
static std::string get_group(int ind, bool title);
static void set_from_default_language_api(const char*(*fdl)(const char*));
public:
void clear(void);
bool add(sane_opt_provider* sop);
bool refine_data(const char* name, void* value); // return true if the 'value' is out of range and refined it in the range
int update_data(const char* name, void* value, bool reorder_if_need = true); // return scanner_err. name and value would be null if invoked for language changed
int restore(sane_opt_provider* holder); //
int count(void); // return option count
bool is_auto_restore_default(const char* name);
std::string get_name_by_sane_id(int sane_ind);
std::string get_option_value_type(const char* name, size_t* size = nullptr);
std::string get_option_value_type(int sane_ind, size_t* size = nullptr);
std::string get_option_field_string(const char* name, const char* key);
std::string get_option_value(const char* name, int type/*OPT_VAL_xxx*/, int* size = nullptr, void* in_data = nullptr); // return whole json-text if name was null
std::string get_option_value(int sane_ind, int type/*OPT_VAL_xxx*/, int* size = nullptr, void* in_data = nullptr); // return whole json-text if name was null
};
//{
// "resolution": {
// "cat": "base",
// "group" : "base",
// "title" : "<22>ֱ<EFBFBD><D6B1><EFBFBD>",
// "desc" : "<22><><EFBFBD><EFBFBD>ɨ<EFBFBD><C9A8>ͼ<EFBFBD><CDBC>ķֱ<C4B7><D6B1><EFBFBD>",
// "type" : "int",
// "fix-id" : 34840,
// "size" : 4,
// "cur" : 200,
// "default" : 200,
// "range" : {
// "min": 100,
// "max" : {
// "default": 600,
// "paper==<3D><><EFBFBD>ɨ<EFBFBD><C9A8>ߴ<EFBFBD><DFB4>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD> || paper==<3D><><EFBFBD>ɨ<EFBFBD><C9A8>ߴ<EFBFBD> || paper==<3D><><EFBFBD><EFBFBD><EFBFBD>Ծ<EFBFBD>" : 500
// },
// "step" : 1
// }
// },
//
// "paper": {
// "cat": "base",
// "group" : "base",
// "title" : "ֽ<>ųߴ<C5B3>",
// "desc" : "<22><><EFBFBD>ó<EFBFBD>ͼ<EFBFBD><CDBC>С",
// "type" : "string",
// "fix-id" : 34831,
// "size" : 44,
// "cur" : "ƥ<><C6A5>ԭʼ<D4AD>ߴ<EFBFBD>",
// "default" : "ƥ<><C6A5>ԭʼ<D4AD>ߴ<EFBFBD>",
// "range" : ["A3", "8<><38>", "A4", "16<31><36>", "A5", "A6", "B4", "B5", "B6", "Letter", "Double Letter", "LEGAL", "ƥ<><C6A5>ԭʼ<D4AD>ߴ<EFBFBD>", {
// "resolution<500": "<22><><EFBFBD>ɨ<EFBFBD><C9A8>ߴ<EFBFBD><DFB4>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD>"
// }, {
// "resolution<500": "<22><><EFBFBD>ɨ<EFBFBD><C9A8>ߴ<EFBFBD>"
// }, {
// "resolution<500": "<22><><EFBFBD><EFBFBD><EFBFBD>Ծ<EFBFBD>"
// }]
// }
//}

View File

@ -0,0 +1,594 @@
#include "simple_logic.h"
#include <string.h>
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// logic_expression
namespace string_util
{
// Function: find the ending position in str
//
// Parameter: str - the string beginning after the first letter 'head'
//
// head - the leading letter, can be emblaced
//
// tail - the ending letter
//
// Return: position at the ending letter, or '\0' in the string
int find_end_of_pair(const char* str, char head, char tail)
{
int end = 0, banlance = 1;
while (str[end])
{
if (str[end] == '\\')
{
end++;
if (!str[end])
break;
// skip this translating-letter
end++;
continue;
}
if (str[end] == head)
{
banlance++;
}
else if (str[end] == tail)
{
if (--banlance == 0)
break;
}
end++;
}
return end;
}
void skip_space(const char*& ptr, const char* space)
{
char mark[2] = { 0 };
while (*ptr)
{
mark[0] = *ptr;
if (!strstr(space, mark))
break;
ptr++;
}
}
void trim(std::string& str, int type)
{
int pos = 0;
if (type & TRIM_LEFT)
{
for (; pos < str.length(); ++pos)
{
if (str[pos] != ' ')
break;
}
if (pos)
str.erase(0, pos);
}
if (type & TRIM_RIGHT)
{
pos = str.length() - 1;
for (; pos >= 0; --pos)
{
if (str[pos] != ' ')
break;
}
str.erase(pos + 1);
}
}
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// simple_logic
simple_logic::simple_logic() : l_(nullptr), r_(nullptr), oper_(LOGIC_OPER_NONE), expr_(""), not_(false)
{}
simple_logic::simple_logic(const simple_logic& r) : l_(nullptr), r_(nullptr)
{
copy(r);
}
simple_logic::~simple_logic()
{
clear();
}
void simple_logic::clear(void)
{
if (l_)
delete l_;
if (r_)
delete r_;
l_ = r_ = nullptr;
oper_ = LOGIC_OPER_NONE;
expr_ = "";
not_ = false;
}
void simple_logic::copy(const simple_logic& r)
{
clear();
oper_ = r.oper_;
not_ = r.not_;
expr_ = r.expr_;
if (r.l_)
{
l_ = new simple_logic();
l_->copy(*r.l_);
}
if (r.r_)
{
r_ = new simple_logic();
r_->copy(*r.r_);
}
}
void simple_logic::set_not(bool notv)
{
not_ = notv;
}
bool simple_logic::parse_internal(const char* expr, int* end, void(*leaf)(const char*, void*), void* leaf_param)
{
const char* ptr = expr, * first = nullptr;
std::vector<simple_logic*> ele;
std::vector<int> oper;
bool need_oper = false, good = true;
string_util::skip_space(ptr);
if (*ptr == 0)
return false;
while (*ptr)
{
if (*ptr == '(')
{
if (need_oper)
{
good = false;
*end = ptr - expr;
break;
}
int len = string_util::find_end_of_pair(ptr + 1, '(', ')');
if (ptr[++len] != ')')
{
*end = ptr - expr + len;
good = false;
break;
}
std::string sub(ptr + 1, len - 1);
if (sub.find("||") == std::string::npos &&
sub.find("&&") == std::string::npos &&
sub.find("^") == std::string::npos)
{
// count as function ...
ptr += len;
}
else
{
simple_logic* e = new simple_logic();
int over = 0;
bool not_v = false;
if (first)
{
while (first < ptr)
{
if (*first == '!')
not_v ^= true;
else
break;
first++;
}
if (first < ptr)
{
*end = first - expr;
good = false;
delete e;
break;
}
first = nullptr;
}
if (e->parse(sub.c_str(), &over, leaf, leaf_param))
{
e->set_not(not_v);
ele.push_back(e);
ptr += len;
need_oper = true;
}
else
{
*end = ptr - expr + 1 + over;
good = false;
delete e;
break;
}
}
}
else if (*ptr == '|')
{
if (*(ptr + 1) == '|')
{
if (need_oper || first)
{
if (first)
{
simple_logic* e = new simple_logic();
e->expr_ = std::string(first, ptr - first);
string_util::trim(e->expr_);
e->oper_ = LOGIC_OPER_LEAF;
ele.push_back(e);
first = nullptr;
if (leaf)
leaf(e->expr_.c_str(), leaf_param);
}
ptr++;
oper.push_back(LOGIC_OPER_OR);
need_oper = false;
}
else
{
good = false;
*end = ptr - expr;
break;
}
}
else
{
good = false;
*end = ptr - expr + 1;
break;
}
}
else if (*ptr == '&')
{
if (*(ptr + 1) == '&')
{
if (need_oper || first)
{
if (first)
{
simple_logic* e = new simple_logic();
e->expr_ = std::string(first, ptr - first);
string_util::trim(e->expr_);
e->oper_ = LOGIC_OPER_LEAF;
ele.push_back(e);
first = nullptr;
if (leaf)
leaf(e->expr_.c_str(), leaf_param);
}
ptr++;
oper.push_back(LOGIC_OPER_AND);
need_oper = false;
}
else
{
good = false;
*end = ptr - expr;
break;
}
}
else
{
good = false;
*end = ptr - expr + 1;
break;
}
}
else if (*ptr == '^')
{
if (need_oper || first)
{
if (first)
{
simple_logic* e = new simple_logic();
e->expr_ = std::string(first, ptr - first);
string_util::trim(e->expr_);
e->oper_ = LOGIC_OPER_LEAF;
ele.push_back(e);
first = nullptr;
if (leaf)
leaf(e->expr_.c_str(), leaf_param);
}
oper.push_back(LOGIC_OPER_XOR);
need_oper = false;
}
else
{
good = false;
*end = ptr - expr;
break;
}
}
else
{
// expression ...
if (need_oper)
{
good = false;
*end = ptr - expr;
break;
}
if (!first)
first = ptr;
}
ptr++;
string_util::skip_space(ptr);
}
if (good && first)
{
if (need_oper)
{
*end = first - expr;
good = false;
}
else
{
simple_logic* e = new simple_logic();
int over = 0;
if (e->parse(first, &over))
{
ele.push_back(e);
if (e->oper_ == LOGIC_OPER_LEAF && leaf)
leaf(e->expr_.c_str(), leaf_param);
}
else
{
good = false;
*end = first - expr + over;
delete e;
}
}
}
if (good && oper.size() == ele.size() - 1)
{
simple_logic* root = make_binary(ele, oper);
l_ = root->l_;
r_ = root->r_;
oper_ = root->oper_;
expr_ = root->expr_;
root->l_ = root->r_ = nullptr;
delete root;
*end = ptr - expr;
}
else
{
for (auto& v : ele)
delete v;
}
return good;
}
simple_logic* simple_logic::make_binary(const std::vector<simple_logic*>& eles, const std::vector<int>& opers)
{
if (eles.size() == 0 && opers.size() == 0)
return nullptr;
else if (eles.size() == 1)
return eles[0];
int or_pos = -1;
for (int i = 0; i < opers.size(); ++i)
{
if (opers[i] == LOGIC_OPER_OR)
{
or_pos = i;
break;
}
}
simple_logic* node = new simple_logic();
if (or_pos == -1)
{
//node->l_ = eles[0];
//node->oper_ = opers[0];
//
//std::vector<simple_logic*> re;
//std::vector<int> ro;
//for (int i = 1; i < eles.size(); ++i)
// re.push_back(eles[i]);
//for (int i = 1; i < opers.size(); ++i)
// ro.push_back(opers[i]);
//node->r_ = make_binary(re, ro);
node->r_ = eles[eles.size() - 1];
node->l_ = eles[eles.size() - 2];
node->oper_ = opers[opers.size() - 1];
simple_logic** left = &node->l_;
int cnt = 2;
while (++cnt <= eles.size())
{
simple_logic* n = new simple_logic();
n->oper_ = opers[opers.size() - cnt + 1];
n->l_ = eles[eles.size() - cnt];
n->r_ = *left;
*left = n;
left = &n->l_;
}
}
else
{
std::vector<simple_logic*> re;
std::vector<int> ro;
node->oper_ = LOGIC_OPER_OR;
for (int i = 0; i < or_pos + 1; ++i)
re.push_back(eles[i]);
for (int i = 0; i < or_pos; ++i)
ro.push_back(opers[i]);
node->l_ = make_binary(re, ro);
re.clear();
ro.clear();
for (int i = or_pos + 1; i < eles.size(); ++i)
re.push_back(eles[i]);
for (int i = or_pos + 1; i < opers.size(); ++i)
ro.push_back(opers[i]);
node->r_ = make_binary(re, ro);
}
return node;
}
std::string simple_logic::to_string_internal(bool& single)
{
single = true;
if (oper_ == LOGIC_OPER_LEAF)
return expr_;
else if (oper_ == LOGIC_OPER_AND || oper_ == LOGIC_OPER_OR || oper_ == LOGIC_OPER_XOR)
{
std::string exp("");
if (l_)
{
exp = l_->to_string_internal(single);
if (!single)
{
exp += ")";
exp.insert(0, "(");
}
}
if (oper_ == LOGIC_OPER_AND)
exp += " && ";
else if (oper_ == LOGIC_OPER_OR)
exp += " || ";
else
exp += " ^ ";
if (r_)
{
bool s = false;
std::string r(r_->to_string_internal(s));
if (!s)
{
r.insert(0, "(");
r += ")";
}
exp += r;
}
if (not_)
{
exp.insert(0, "!(");
exp += ")";
single = true;
}
else
single = false;
return std::move(exp);
}
else
return "";
}
simple_logic& simple_logic::operator=(const simple_logic& r)
{
copy(r);
return *this;
}
bool simple_logic::parse(const char* expr, int* end_pos, void(*leaf)(const char*, void*), void* leaf_param)
{
bool ret = false;
clear();
if (strstr(expr, "||") == nullptr &&
strstr(expr, "&&") == nullptr &&
strstr(expr, "^") == nullptr)
{
oper_ = LOGIC_OPER_LEAF;
expr_ = expr;
ret = true;
if (leaf)
leaf(expr_.c_str(), leaf_param);
}
else
{
int end = 0;
ret = parse_internal(expr, &end, leaf, leaf_param);
if (end_pos)
*end_pos = end;
}
return ret;
}
bool simple_logic::value(bool(*simple_expr_value)(const char*, void*), void* param)
{
if (oper_ == LOGIC_OPER_LEAF)
return simple_expr_value(expr_.c_str(), param);
else if (oper_ == LOGIC_OPER_AND)
{
bool ret = true;
if (l_)
ret = l_->value(simple_expr_value, param);
if (ret && r_)
ret &= r_->value(simple_expr_value, param);
return ret ^ not_;
}
else if (oper_ == LOGIC_OPER_OR)
{
bool ret = false;
if (l_)
ret = l_->value(simple_expr_value, param);
if (!ret && r_)
ret = r_->value(simple_expr_value, param);
return ret ^ not_;
}
else if (oper_ == LOGIC_OPER_XOR)
{
bool ret = false;
if (l_)
ret = l_->value(simple_expr_value, param);
if (r_)
ret ^= r_->value(simple_expr_value, param);
return ret ^ not_;
}
else
{
return false;
}
}
std::string simple_logic::to_expression(void)
{
bool single = false;
return std::move(to_string_internal(single));
}

View File

@ -0,0 +1,79 @@
// simple_logic.h : include file for simple logical operation
//
// Purpose: to parse and execute a logical expression like 'b1 && !(b2 || b3) ^ b4 && b5'
//
// Date: 2023-09-03
//
// Author: gb
//
// Supporting opers: &&, ||, ^
//
// Priority: (&&, ^, ), (||)
#pragma once
#include <string>
#include <vector>
namespace string_util
{
// Function: find the ending position in str
//
// Parameter: str - the string beginning after the first letter 'head'
//
// head - the leading letter, can be emblaced
//
// tail - the ending letter
//
// Return: position at the ending letter, or '\0' in the string
int find_end_of_pair(const char* str, char head, char tail);
void skip_space(const char*& ptr, const char* space = " \t");
enum
{
TRIM_LEFT = 1,
TRIM_RIGHT,
TRIM_BOTH,
};
void trim(std::string& str, int type = TRIM_BOTH);
};
class simple_logic
{
int oper_;
bool not_;
std::string expr_;
simple_logic* l_;
simple_logic* r_;
void clear(void);
void copy(const simple_logic& r);
void set_not(bool notv);
bool parse_internal(const char* expr, int* end, void(*leaf)(const char*, void*), void* leaf_param);
simple_logic* make_binary(const std::vector<simple_logic*>& eles, const std::vector<int>& opers);
std::string to_string_internal(bool& single);
public:
simple_logic();
simple_logic(const simple_logic& r);
~simple_logic();
// && == ^ > ||
enum
{
LOGIC_OPER_NONE = 0,
LOGIC_OPER_LEAF,
LOGIC_OPER_OR,
LOGIC_OPER_AND,
LOGIC_OPER_XOR,
};
simple_logic& operator=(const simple_logic& r);
public:
bool parse(const char* expr, int *end_pos = nullptr, void(*leaf)(const char*, void*) = nullptr, void* leaf_param = nullptr); // leaf: callback for leaf notifying
bool value(bool(*simple_expr_value)(const char*, void*), void* param);
std::string to_expression(void);
};

12
usb/buildconf.h Normal file
View File

@ -0,0 +1,12 @@
#define _LARGEFILE64_SOURCE
#define _FILE_OFFSET_BITS 64
//#define CONFIG_USB_NON_BLOCKING_WRITE 1
#define CONFIG_USB_FS_SUPPORT 1 // USB 1.1 Full speed
#define CONFIG_USB_HS_SUPPORT 1 // USB 2.0 High speed
#define CONFIG_USB_SS_SUPPORT 1 // USB 3.0 SuperSpeed
#define CONFIG_READ_FILE_BUFFER_SIZE (1024*1024) // Must be a 2^x value.
#define CONFIG_MAX_TX_USB_BUFFER_SIZE (16*512) // Must be a multiple of 512 and be less than CONFIG_READ_FILE_BUFFER_SIZE
#define CONFIG_MAX_RX_USB_BUFFER_SIZE (16*512) // Must be a multiple of 512

82
usb/camtp.h Normal file
View File

@ -0,0 +1,82 @@
/*
* CAMTP Responder
* Copyright (c) 2020 Holdtecs Technologies
*
* CAMTP Responder is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* CAMTP Responder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 3 for more details.
*
* You should have received a copy of the GNU General Public License
* along with CAMTP Responder; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file camtp.h
* @brief Main CAMMTP protocol functions.
* @author Barry Ruan <cubex@foxmail.com>
*/
#ifndef _INC_CAMTP_H_
#define _INC_CAMTP_H_
#define MAX_STORAGE_NB 16
#define MAX_CFG_STRING_SIZE 512
#include <stdint.h>
#pragma pack()
typedef struct mtp_usb_cfg_
{
uint16_t usb_vendor_id;
uint16_t usb_product_id;
uint8_t usb_class;
uint8_t usb_subclass;
uint8_t usb_protocol;
uint16_t usb_dev_version;
uint16_t usb_max_packet_size;
uint8_t usb_functionfs_mode;
char usb_device_path[MAX_CFG_STRING_SIZE + 1];
char usb_endpoint_in[MAX_CFG_STRING_SIZE + 1];
char usb_endpoint_out[MAX_CFG_STRING_SIZE + 1];
char usb_endpoint_intin[MAX_CFG_STRING_SIZE + 1];
char usb_string_manufacturer[MAX_CFG_STRING_SIZE + 1];
char usb_string_product[MAX_CFG_STRING_SIZE + 1];
char usb_string_serial[MAX_CFG_STRING_SIZE + 1];
char usb_string_version[MAX_CFG_STRING_SIZE + 1];
char usb_string_interface[MAX_CFG_STRING_SIZE + 1];
int wait_connection;
int loop_on_disconnect;
int show_hidden_files;
int val_umask;
}camtp_usb_cfg;
typedef struct camtp_ctx_
{
uint32_t session_id;
camtp_usb_cfg usb_cfg;
void * usb_ctx;
volatile int cancel_req;
}camtp_ctx;
int camtp_load_config_file(camtp_ctx * context, const char * conffile);
#endif

64
usb/default_cfg.h Normal file
View File

@ -0,0 +1,64 @@
/*
* CAMTP Responder
* Copyright (c) 2020 Holdtecs Technologies
*
* CAMTP Responder is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* CAMTP Responder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 3 for more details.
*
* You should have received a copy of the GNU General Public License
* along with CAMTP Responder; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file default.h
* @brief Main CAMMTP protocol functions.
* @author Barry Ruan <cubex@foxmail.com>
*/
#ifndef _INC_DEFAULT_CFG_H_
#define _INC_DEFAULT_CFG_H_
#ifndef CAMTPR_CONF_FILE
#define CAMTPR_CONF_FILE "/etc/camtprd/camtprd.conf"
#endif
#define MAX_PACKET_SIZE 1024
#define USB_DEV_VENDOR_ID 0x04b4
//0x04B4 // Linux Foundation
#define USB_DEV_PRODUCT_ID 0x8613 // PTP Gadget
#define USB_DEV_CLASS USB_CLASS_STILL_IMAGE // Still Imaging device
#define USB_DEV_SUBCLASS 0x0 //
#define USB_DEV_PROTOCOL 0x0 //
#define USB_DEV_VERSION 0x3008
#define USB_FFS_MODE 1
#define USB_DEV "/dev/ffs-camtp/ep0"
#define USB_EP_BULK_IN "/dev/ffs-camtp/ep1"
#define USB_EP_BULK_OUT "/dev/ffs-camtp/ep2"
#define USB_EP_INT_IN "/dev/ffs-camtp/ep3"
enum
{
EP_IND_BULK_IN = 0,
EP_IND_BULK_OUT,
EP_IND_INT_IN,
};
#define MANUFACTURER "HUAGAO Technologies"
#define PRODUCT "HUAGAO"
#define SERIALNUMBER "01234567ABCDEFG"
#define USB_INTERFACE "camtp"
#endif

716
usb/usb_dev.cpp Normal file
View File

@ -0,0 +1,716 @@
#include "usb_dev.h"
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "usbstring.h"
#include "default_cfg.h"
#include <errno.h>
#include <iostream>
#include <fstream>
#define CONFIG_VALUE 1
#define USB_MAX_PACKAGE_LENGTH_FILE "/opt/usbpkgconfig"
static struct usb_gadget_strings strings = {
.language = 0x0409, /* en-us */
.strings = 0,
};
typedef struct camtp_device_status_ {
uint16_t wLength;
uint16_t wCode;
}camtp_device_status;
static const int cacheSize = SIZE_KB(64);
usb_device::usb_device(const char* dwc3, const char* udc, const char* pwd)
: dwc3_(dwc3), udc_(udc), pwd_(pwd)
{
memset(&gadget_, 0, sizeof(gadget_));
gadget_.usb_device = -1;
for(int i = 0; i < _countof(gadget_.ep_handles); ++i)
gadget_.ep_handles[i] = -1;
memset( &ffs_strs_, 0, sizeof(ffs_strings));
ffs_strs_.header.magic = htole32(FUNCTIONFS_STRINGS_MAGIC);
ffs_strs_.header.length = htole32(sizeof(struct usb_functionfs_strings_head) + sizeof(uint16_t) + strlen(USB_INTERFACE) + 1);
ffs_strs_.header.str_count = htole32(1);
ffs_strs_.header.lang_count = htole32(1);
ffs_strs_.code = htole16(0x0409); // en-us
strcpy(ffs_strs_.string_data, USB_INTERFACE);
}
usb_device::~usb_device()
{
close_device();
deinit_usb_camtp_gadget();
}
std::string usb_device::endpoint_index_str(int epind)
{
RETURN_ENUM_STR(epind, EP_IND_BULK_IN);
RETURN_ENUM_STR(epind, EP_IND_BULK_OUT);
RETURN_ENUM_STR(epind, EP_IND_INT_IN);
return std::to_string(epind);
}
void usb_device::fill_if_descriptor(bool ffs_mode, usb_gadget * usbctx, struct usb_interface_descriptor * desc)
{
memset(desc,0,sizeof(struct usb_interface_descriptor));
desc->bLength = sizeof(struct usb_interface_descriptor);
desc->bDescriptorType = USB_DT_INTERFACE; //!< nick
desc->bInterfaceNumber = 0;
desc->iInterface = 1;
desc->bAlternateSetting = 0;
desc->bNumEndpoints = _countof(usbctx->ep_path);
desc->bInterfaceClass = USB_DEV_CLASS;
desc->bInterfaceSubClass = USB_DEV_SUBCLASS;
desc->bInterfaceProtocol = USB_DEV_PROTOCOL;
if( ffs_mode )
{
desc->iInterface = STRINGID_MANUFACTURER;
}
else
{
desc->iInterface = STRINGID_INTERFACE;
}
}
void usb_device::fill_ep_descriptor(unsigned short max_packet, usb_gadget * usbctx,struct usb_endpoint_descriptor_no_audio * desc,int index,unsigned int flags)
{
memset(desc,0,sizeof(struct usb_endpoint_descriptor_no_audio));
desc->bLength = USB_DT_ENDPOINT_SIZE;
desc->bDescriptorType = USB_DT_ENDPOINT;
if(flags & EP_OUT_DIR)
desc->bEndpointAddress = USB_DIR_OUT | (index);
else
desc->bEndpointAddress = USB_DIR_IN | (index);
if(flags & EP_BULK_MODE)
{
desc->bmAttributes = USB_ENDPOINT_XFER_BULK;
desc->wMaxPacketSize = max_packet;
//printf("desc->wMaxPacketSize = %d \n",desc->wMaxPacketSize);
}
else
{
desc->bmAttributes = USB_ENDPOINT_XFER_INT;
desc->wMaxPacketSize = 64; // HS size 64
desc->bInterval = 6;
}
#if defined(CONFIG_USB_SS_SUPPORT)
if(flags & EP_SS_MODE)
{
ep_cfg_descriptor * ss_descriptor;
ss_descriptor = (ep_cfg_descriptor *)desc;
ss_descriptor->ep_desc_comp.bLength = sizeof(struct usb_ss_ep_comp_descriptor);
ss_descriptor->ep_desc_comp.bDescriptorType = USB_DT_SS_ENDPOINT_COMP;
if(flags & EP_BULK_MODE){
ss_descriptor->ep_desc_comp.bMaxBurst = 15;
ss_descriptor->ep_desc_comp.wBytesPerInterval = 0x00;
}else{
ss_descriptor->ep_desc_comp.bMaxBurst = 0;
ss_descriptor->ep_desc_comp.wBytesPerInterval = 64;//0x1c
}
}
#endif
}
int usb_device::add_usb_string(usb_gadget * usbctx, int id, char * string)
{
int i;
i = 0;
while( i < MAX_USB_STRING )
{
if( !usbctx->stringtab[i].id )
{
usbctx->stringtab[i].id = id;
if(string)
{
usbctx->stringtab[i].str = (char*)malloc(strlen(string) + 1);
if(usbctx->stringtab[i].str)
{
memset(usbctx->stringtab[i].str,0,strlen(string) + 1);
strcpy(usbctx->stringtab[i].str,string);
return i;
}
else
{
usbctx->stringtab[i].id = 0;
return -2;
}
}
else
{
return i;
}
}
i++;
}
return -1;
}
void usb_device::fill_config_descriptor(usb_gadget * usbctx,struct usb_config_descriptor * desc,int total_size, int hs)
{
memset(desc,0,sizeof(struct usb_config_descriptor));
desc->bLength = sizeof(struct usb_config_descriptor);
desc->bDescriptorType = USB_DT_CONFIG;
desc->wTotalLength = desc->bLength + total_size;
desc->bNumInterfaces = 1;
desc->bConfigurationValue = CONFIG_VALUE;
if(hs)
desc->iConfiguration = STRINGID_CONFIG_HS;
else
desc->iConfiguration = STRINGID_CONFIG_LS;
desc->bmAttributes = USB_CONFIG_ATT_ONE;
desc->bMaxPower = 1;
}
void usb_device::fill_dev_descriptor(usb_gadget * usbctx,struct usb_device_descriptor * desc)
{
memset(desc,0,sizeof(struct usb_device_descriptor));
desc->bLength = USB_DT_DEVICE_SIZE;
desc->bDescriptorType = USB_DT_DEVICE;
desc->bDeviceClass = USB_DEV_CLASS;
desc->bDeviceSubClass = USB_DEV_SUBCLASS;
desc->bDeviceProtocol = USB_DEV_PROTOCOL;
desc->idVendor = USB_DEV_VENDOR_ID;
desc->idProduct = USB_DEV_PRODUCT_ID;
desc->bcdDevice = USB_DEV_VERSION; // Version
// Strings
desc->iManufacturer = STRINGID_MANUFACTURER;
desc->iProduct = STRINGID_PRODUCT;
desc->iSerialNumber = STRINGID_SERIAL;
desc->bNumConfigurations= 1; // Only one configuration
return;
}
void usb_device::init_usb_camtp_gadget(bool ffs_mode)
{
usb_gadget *usbctx = &gadget_; //(usb_gadget *)malloc(sizeof(usb_gadget));
int cfg_size;
if(usbctx)
{
std::ifstream fs(USB_MAX_PACKAGE_LENGTH_FILE);
int maxpackagesize = 0;
fs >> maxpackagesize;
if(maxpackagesize <= 0)
maxpackagesize = 512;
max_packet_ = maxpackagesize;
memset(usbctx, 0, sizeof(usb_gadget));
usbctx->usb_device = -1;
for(int i = 0; i < _countof(usbctx->ep_handles); ++i)
usbctx->ep_handles[i] = -1;
add_usb_string(usbctx, STRINGID_MANUFACTURER, MANUFACTURER);
add_usb_string(usbctx, STRINGID_PRODUCT, PRODUCT);
add_usb_string(usbctx, STRINGID_SERIAL, SERIALNUMBER);
add_usb_string(usbctx, STRINGID_CONFIG_HS, (char*)"High speed configuration");
add_usb_string(usbctx, STRINGID_CONFIG_LS, (char*)"Low speed configuration");
add_usb_string(usbctx, STRINGID_INTERFACE, USB_INTERFACE);
add_usb_string(usbctx, STRINGID_MAX, NULL);
strings.strings = usbctx->stringtab;
for(int i = 0; i < _countof(usbctx->ep_config); ++i)
{
usbctx->ep_config[i] = (ep_cfg*)malloc(sizeof(ep_cfg));
if(!usbctx->ep_config[i])
goto init_error;
memset(usbctx->ep_config[i], 0, sizeof(ep_cfg));
}
usbctx->ep_path[EP_IND_BULK_IN] = &ep_path_[EP_IND_BULK_IN][0];
usbctx->ep_path[EP_IND_BULK_OUT] = &ep_path_[EP_IND_BULK_OUT][0];
usbctx->ep_path[EP_IND_INT_IN] = &ep_path_[EP_IND_INT_IN][0];
cfg_size = sizeof(struct usb_interface_descriptor) + (sizeof(struct usb_endpoint_descriptor_no_audio) * _countof(usbctx->ep_config));
if( ffs_mode )
{
// FunctionFS mode
usbctx->usb_ffs_config = (usb_ffs_cfg *)malloc(sizeof(usb_ffs_cfg));
if(!usbctx->usb_ffs_config)
goto init_error;
memset(usbctx->usb_ffs_config, 0, sizeof(usb_ffs_cfg));
#ifdef OLD_FUNCTIONFS_DESCRIPTORS // Kernel < v3.15
usbctx->usb_ffs_config->magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC);
#else
usbctx->usb_ffs_config->magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
usbctx->usb_ffs_config->flags = htole32(0);
usbctx->usb_ffs_config->flags |= htole32(FUNCTIONFS_ALL_CTRL_RECIP);
#ifdef CONFIG_USB_FS_SUPPORT
usbctx->usb_ffs_config->flags |= htole32(FUNCTIONFS_HAS_FS_DESC);
#endif
#ifdef CONFIG_USB_HS_SUPPORT
usbctx->usb_ffs_config->flags |= htole32(FUNCTIONFS_HAS_HS_DESC);
#endif
#ifdef CONFIG_USB_SS_SUPPORT
usbctx->usb_ffs_config->flags |= htole32(FUNCTIONFS_HAS_SS_DESC);
#endif
#endif
usbctx->usb_ffs_config->length = htole32(sizeof(usb_ffs_cfg));
#ifdef CONFIG_USB_FS_SUPPORT
usbctx->usb_ffs_config->fs_count = htole32(1 + _countof(usbctx->ep_config));
fill_if_descriptor(ffs_mode, usbctx, &usbctx->usb_ffs_config->ep_desc_fs.if_desc);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_fs.ep_desc_in, EP_IND_BULK_IN + 1, EP_BULK_MODE | EP_IN_DIR);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_fs.ep_desc_out, EP_IND_BULK_OUT + 1, EP_BULK_MODE | EP_OUT_DIR);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_fs.ep_desc_int_in, EP_IND_INT_IN + 1, EP_INT_MODE | EP_IN_DIR);
#endif
#ifdef CONFIG_USB_HS_SUPPORT
usbctx->usb_ffs_config->hs_count = htole32(1 + _countof(usbctx->ep_config));
fill_if_descriptor(ffs_mode, usbctx, &usbctx->usb_ffs_config->ep_desc_hs.if_desc);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_hs.ep_desc_in, EP_IND_BULK_IN + 1, EP_BULK_MODE | EP_IN_DIR | EP_HS_MODE);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_hs.ep_desc_out, EP_IND_BULK_OUT + 1, EP_BULK_MODE | EP_OUT_DIR | EP_HS_MODE);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_hs.ep_desc_int_in, EP_IND_INT_IN + 1, EP_INT_MODE | EP_IN_DIR | EP_HS_MODE);
#endif
#ifdef CONFIG_USB_SS_SUPPORT
usbctx->usb_ffs_config->ss_count = htole32(1 + (_countof(usbctx->ep_config) * 2));
fill_if_descriptor(ffs_mode, usbctx, &usbctx->usb_ffs_config->ep_desc_ss.if_desc);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_ss.ep_desc_in, EP_IND_BULK_IN + 1, EP_BULK_MODE | EP_IN_DIR | EP_SS_MODE);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_ss.ep_desc_out, EP_IND_BULK_OUT + 1, EP_BULK_MODE | EP_OUT_DIR | EP_SS_MODE);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_ffs_config->ep_desc_ss.ep_desc_int_in, EP_IND_INT_IN + 1, EP_INT_MODE | EP_IN_DIR | EP_SS_MODE);
#endif
}
else
{
usbctx->usb_config = (usb_cfg *)malloc(sizeof(usb_cfg));
if(!usbctx->usb_config)
goto init_error;
memset(usbctx->usb_config,0,sizeof(usb_cfg));
usbctx->usb_config->head = 0x00000000;
#ifdef CONFIG_USB_FS_SUPPORT
fill_config_descriptor(usbctx, &usbctx->usb_config->cfg_fs, cfg_size, 0);
fill_if_descriptor(ffs_mode, usbctx, &usbctx->usb_config->ep_desc_fs.if_desc);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_fs.ep_desc_in, EP_IND_BULK_IN + 1, EP_BULK_MODE | EP_IN_DIR);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_fs.ep_desc_out, EP_IND_BULK_OUT + 1, EP_BULK_MODE | EP_OUT_DIR);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_fs.ep_desc_int_in, EP_IND_INT_IN + 1, EP_INT_MODE | EP_IN_DIR);
#endif
#ifdef CONFIG_USB_HS_SUPPORT
fill_config_descriptor(usbctx, &usbctx->usb_config->cfg_hs, cfg_size, 1);
fill_if_descriptor(ffs_mode, usbctx, &usbctx->usb_config->ep_desc_hs.if_desc);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_hs.ep_desc_in, EP_IND_BULK_IN + 1, EP_BULK_MODE | EP_IN_DIR | EP_HS_MODE);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_hs.ep_desc_out, EP_IND_BULK_OUT + 1, EP_BULK_MODE | EP_OUT_DIR | EP_HS_MODE);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_hs.ep_desc_int_in, EP_IND_INT_IN + 1, EP_INT_MODE | EP_IN_DIR | EP_HS_MODE);
#endif
#ifdef CONFIG_USB_SS_SUPPORT
fill_config_descriptor(usbctx, &usbctx->usb_config->cfg_ss, cfg_size, 1);
fill_if_descriptor(ffs_mode, usbctx, &usbctx->usb_config->ep_desc_ss.if_desc);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_ss.ep_desc_in, EP_IND_BULK_IN + 1, EP_BULK_MODE | EP_IN_DIR | EP_SS_MODE);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_ss.ep_desc_out, EP_IND_BULK_OUT + 1, EP_BULK_MODE | EP_OUT_DIR | EP_SS_MODE);
fill_ep_descriptor(maxpackagesize, usbctx, &usbctx->usb_config->ep_desc_ss.ep_desc_int_in, EP_IND_INT_IN + 1, EP_INT_MODE | EP_IN_DIR | EP_SS_MODE);
#endif
fill_dev_descriptor(usbctx, &usbctx->usb_config->dev_desc);
}
return;
}
init_error:
deinit_usb_camtp_gadget();
}
void usb_device::deinit_usb_camtp_gadget()
{
usb_gadget * usbctx = &gadget_;
if( usbctx )
{
if(usbctx->usb_config)
{
free(usbctx->usb_config);
usbctx->usb_config = 0;
}
if(usbctx->usb_ffs_config)
{
free(usbctx->usb_ffs_config);
usbctx->usb_ffs_config = 0;
}
for(int i = 0; i < _countof(usbctx->ep_config); ++i)
{
if( usbctx->ep_config[i] )
free( usbctx->ep_config[i] );
usbctx->ep_config[i] = nullptr;
}
for(int i = 0; i < _countof(usbctx->stringtab); ++i)
{
if( usbctx->stringtab[i].str )
free ( usbctx->stringtab[i].str );
usbctx->stringtab[i].str = nullptr;
}
}
}
int usb_device::config_device(bool ffs_mode)
{
void *data = ffs_mode ? (void*)gadget_.usb_ffs_config : (void*)gadget_.usb_config;
int size = ffs_mode ? sizeof(*gadget_.usb_ffs_config) : sizeof(*gadget_.usb_config),
ret = write(gadget_.usb_device, data, size);
if(ret == size)
{
if(ffs_mode)
{
ret = write(gadget_.usb_device, &ffs_strs_, sizeof(ffs_strs_));
if(ret)
{
ret = errno;
utils::to_log(LOG_LEVEL_FATAL, "config device with ffs_strings failed: %d - %s\n", errno, strerror(errno));
}
}
}
else
{
ret = errno;
utils::to_log(LOG_LEVEL_FATAL, "config device with '%s' failed: %d - %s\n", ffs_mode ? "ffs_config" : "usb_config", errno, strerror(errno));
}
return ret;
}
int usb_device::config_endpoint(int ep_ind)
{
int ret = 0;
void * descriptor_ptr;
int descriptor_size;
int index = ep_ind;
usb_gadget* ctx = &gadget_;
ctx->ep_config[index]->head = 1;
descriptor_size = 0;
if( ctx->usb_ffs_config )
{
#if defined(CONFIG_USB_SS_SUPPORT)
descriptor_ptr = (void *)&ctx->usb_ffs_config->ep_desc_ss;
descriptor_size = sizeof(ep_cfg_descriptor);
#elif defined(CONFIG_USB_HS_SUPPORT)
descriptor_ptr = (void *)&ctx->usb_ffs_config->ep_desc_hs;
descriptor_size = sizeof(struct usb_endpoint_descriptor_no_audio);
#elif defined(CONFIG_USB_FS_SUPPORT)
descriptor_ptr = (void *)&ctx->usb_ffs_config->ep_desc_fs;
descriptor_size = sizeof(struct usb_endpoint_descriptor_no_audio);
#else
#error Configuration Error ! At least one USB mode support must be enabled ! (CONFIG_USB_FS_SUPPORT/CONFIG_USB_HS_SUPPORT/CONFIG_USB_SS_SUPPORT)
#endif
}
else
{
#if defined(CONFIG_USB_SS_SUPPORT)
descriptor_ptr = (void *)&ctx->usb_config->ep_desc_ss;
descriptor_size = sizeof(ep_cfg_descriptor);
#elif defined(CONFIG_USB_HS_SUPPORT)
descriptor_ptr = (void *)&ctx->usb_config->ep_desc_hs;
descriptor_size = sizeof(struct usb_endpoint_descriptor_no_audio);
#elif defined(CONFIG_USB_FS_SUPPORT)
descriptor_ptr = (void *)&ctx->usb_config->ep_desc_fs;
descriptor_size = sizeof(struct usb_endpoint_descriptor_no_audio);
#else
#error Configuration Error ! At least one USB mode support must be enabled ! (CONFIG_USB_FS_SUPPORT/CONFIG_USB_HS_SUPPORT/CONFIG_USB_SS_SUPPORT)
#endif
}
#if defined(CONFIG_USB_SS_SUPPORT)
switch(index)
{
case EP_DESCRIPTOR_IN:
memcpy(&ctx->ep_config[index]->ep_desc[0], &((SSEndPointsDesc*)descriptor_ptr)->ep_desc_in,descriptor_size);
memcpy(&ctx->ep_config[index]->ep_desc[1], &((SSEndPointsDesc*)descriptor_ptr)->ep_desc_in,descriptor_size);
break;
case EP_DESCRIPTOR_OUT:
memcpy(&ctx->ep_config[index]->ep_desc[0], &((SSEndPointsDesc*)descriptor_ptr)->ep_desc_out,descriptor_size);
memcpy(&ctx->ep_config[index]->ep_desc[1], &((SSEndPointsDesc*)descriptor_ptr)->ep_desc_out,descriptor_size);
break;
case EP_DESCRIPTOR_INT_IN:
memcpy(&ctx->ep_config[index]->ep_desc[0], &((SSEndPointsDesc*)descriptor_ptr)->ep_desc_int_in,descriptor_size);
memcpy(&ctx->ep_config[index]->ep_desc[1], &((SSEndPointsDesc*)descriptor_ptr)->ep_desc_int_in,descriptor_size);
break;
}
#else
switch(index)
{
case EP_DESCRIPTOR_IN:
memcpy(&ctx->ep_config[index]->ep_desc[0], &((EndPointsDesc*)descriptor_ptr)->ep_desc_in,descriptor_size);
memcpy(&ctx->ep_config[index]->ep_desc[1], &((EndPointsDesc*)descriptor_ptr)->ep_desc_in,descriptor_size);
break;
case EP_DESCRIPTOR_OUT:
memcpy(&ctx->ep_config[index]->ep_desc[0], &((EndPointsDesc*)descriptor_ptr)->ep_desc_out,descriptor_size);
memcpy(&ctx->ep_config[index]->ep_desc[1], &((EndPointsDesc*)descriptor_ptr)->ep_desc_out,descriptor_size);
break;
case EP_DESCRIPTOR_INT_IN:
memcpy(&ctx->ep_config[index]->ep_desc[0], &((EndPointsDesc*)descriptor_ptr)->ep_desc_int_in,descriptor_size);
memcpy(&ctx->ep_config[index]->ep_desc[1], &((EndPointsDesc*)descriptor_ptr)->ep_desc_int_in,descriptor_size);
break;
}
#endif
if(ffs_mode_)
{
}
else
{
ret = write(ctx->ep_handles[index], ctx->ep_config[index], sizeof(ep_cfg));
if (ret != sizeof(ep_cfg))
{
ret = errno;
utils::to_log(LOG_LEVEL_FATAL, "config_endpoint '%s' failed: %d - %s", usb_device::endpoint_index_str(index).c_str(), errno, strerror(errno));
}
}
return ret;
}
int usb_device::add_endpoint(const char* path, bool bulk/*true - bulk, false - int*/, bool in)
{
int ret = 0, ind = -1;
if(bulk)
{
if(in)
ind = EP_IND_BULK_IN;
else
ind = EP_IND_BULK_OUT;
}
else
{
if(in)
ind = EP_IND_INT_IN;
}
if(ind == -1)
ret = EINVAL;
else
ep_path_[ind] = path ? path : "";
return ret;
}
int usb_device::open_device(const char* dev, bool ffs_mode, int* fd)
{
int ret = close_device();
if(fd)
*fd = -1;
if(ret == 0)
{
init_usb_camtp_gadget(ffs_mode);
gadget_.usb_device = open(dev, O_RDWR | O_SYNC);
if(gadget_.usb_device == -1)
{
ret = errno;
utils::to_log(LOG_LEVEL_FATAL, "Open usb device(%s) failed: %d - %s\n", dev, ret, strerror(ret));
}
else
{
ret = config_device(ffs_mode);
if(ret)
{
close_device();
}
else
{
utils::to_log(LOG_LEVEL_DEBUG, "Open usb device(%s) success.\n", dev);
ffs_mode_ = ffs_mode;
// get_system_output(R"(echo linaro | sudo -S sh -c "chmod 777 /dev/ffs-camtp -R")");
std::string cmd(dev);
size_t pos = cmd.rfind('/');
if(pos != std::string::npos)
cmd.erase(pos);
cmd += " -R\"";
cmd.insert(0, "echo " + pwd_ + " | sudo -S sh -c \"chmod 777 ");
utils::get_command_result(cmd.c_str());
}
if(fd)
*fd = gadget_.usb_device;
}
}
return ret;
}
int usb_device::open_endpoint(int ep_ind, int* fd)
{
int ret = ENOTCONN;
if(fd)
*fd = -1;
if(gadget_.usb_device >= 0)
{
if(ep_ind >= 0 && ep_ind < _countof(gadget_.ep_handles))
{
if(gadget_.ep_handles[ep_ind] >= 0)
{
ret = EALREADY;
}
else
{
gadget_.ep_handles[ep_ind] = open(gadget_.ep_path[ep_ind], O_RDWR);
if(gadget_.ep_handles[ep_ind] == -1)
{
utils::to_log(LOG_LEVEL_FATAL, "open endpoint(%s - %s) failed: %d - %s\n", usb_device::endpoint_index_str(ep_ind).c_str(), gadget_.ep_path[ep_ind], errno, strerror(errno));
ret = errno;
}
else
{
ret = config_endpoint(ep_ind);
if(ret)
{
close(gadget_.ep_handles[ep_ind]);
gadget_.ep_handles[ep_ind] = -1;
}
else
{
utils::to_log(LOG_LEVEL_DEBUG, "open endpoint(%s - %s) ok.\n", usb_device::endpoint_index_str(ep_ind).c_str(), gadget_.ep_path[ep_ind]);
}
}
}
if(fd)
*fd = gadget_.ep_handles[ep_ind];
}
else
{
ret = EINVAL;
}
}
return ret;
}
int usb_device::close_endpoint(int ep_ind)
{
int ret = 0;
if(ep_ind == -1)
{
for(int i = 0; i < _countof(gadget_.ep_handles); ++i)
{
if(gadget_.ep_handles[i] != -1)
{
ret = close(gadget_.ep_handles[i]);
if(ret)
{
utils::to_log(LOG_LEVEL_FATAL, "close endpoint(%s) failed: %d(%s)\n", usb_device::endpoint_index_str(i).c_str(), errno, strerror(errno));
break;
}
gadget_.ep_handles[i] = -1;
}
}
utils::to_log(LOG_LEVEL_DEBUG, "close all endpoints = %d\n", ret);
}
else if(ep_ind >= 0 && ep_ind < _countof(gadget_.ep_handles))
{
if(gadget_.ep_handles[ep_ind] != -1)
{
ret = close(gadget_.ep_handles[ep_ind]);
if(ret)
utils::to_log(LOG_LEVEL_FATAL, "close endpoint(%s) failed: %d(%s)\n", usb_device::endpoint_index_str(ep_ind).c_str(), errno, strerror(errno));
else
{
gadget_.ep_handles[ep_ind] = -1;
utils::to_log(LOG_LEVEL_DEBUG, "close endpoint(%s) ok.\n", usb_device::endpoint_index_str(ep_ind).c_str());
}
}
}
else
{
ret = EINVAL;
}
return ret;
}
int usb_device::close_device(void)
{
int ret = pull_down();
if(ret == 0)
{
ret = close_endpoint(-1);
if(ret == 0)
{
deinit_usb_camtp_gadget();
if(gadget_.usb_device >= 0)
{
ret = close(gadget_.usb_device);
if(ret == 0)
gadget_.usb_device = -1;
}
}
}
return ret;
}
int usb_device::pull_up(std::string* msg)
{
std::string cmd("echo " + pwd_ + " | sudo -S sh -c \"echo " + dwc3_ + " > " + udc_ + "\"");
int err = 0;
std::string info(utils::get_command_result(cmd.c_str(), -1, &err));
if(msg)
*msg = std::move(info);
return err;
}
int usb_device::pull_down(void)
{
std::string cmd("echo " + pwd_ + " | sudo -S sh -c \"echo " + "''" + " > " + udc_ + "\"");
int err = 0;
utils::get_command_result(cmd.c_str(), -1, &err);
return err;
}
int usb_device::get_device_fd(void)
{
return gadget_.usb_device;
}
int usb_device::get_endpoint_fd(int ep_ind)
{
if(ep_ind >= 0 && ep_ind < _countof(gadget_.ep_handles))
return gadget_.ep_handles[ep_ind];
else
return -1;
}
size_t usb_device::get_max_packet(void)
{
return max_packet_;
}

56
usb/usb_dev.h Normal file
View File

@ -0,0 +1,56 @@
// USB device configuration and open
//
// Date: 2023-11-28
//
#pragma once
#include "buildconf.h"
#include "camtp.h"
#include "usb_gadget.h"
#include "../sdk/base/utils.h"
class usb_device : public refer
{
usb_gadget gadget_;
std::string dwc3_; // fe900000.dwc3
std::string udc_; // /opt/cfg/usb_gadget/g1/UDC
std::string pwd_; // password of login user
std::string ep_path_[EP_NB_OF_DESCRIPTORS];
ffs_strings ffs_strs_;
bool ffs_mode_ = true;
size_t max_packet_ = 512;
void fill_if_descriptor(bool ffs_mode, usb_gadget * usbctx, struct usb_interface_descriptor * desc);
void fill_ep_descriptor(unsigned short max_packet, usb_gadget * usbctx, struct usb_endpoint_descriptor_no_audio * desc, int index, unsigned int flags);
int add_usb_string(usb_gadget * usbctx, int id, char * string);
void fill_config_descriptor(usb_gadget * usbctx,struct usb_config_descriptor * desc, int total_size, int hs);
void fill_dev_descriptor(usb_gadget * usbctx, struct usb_device_descriptor * desc);
void init_usb_camtp_gadget(bool ffs_mode);
void deinit_usb_camtp_gadget(void);
int config_device(bool ffs_mode);
int config_endpoint(int ep_ind);
public:
usb_device(const char* dwc3, const char* udc, const char* pwd);
static std::string endpoint_index_str(int epind);
protected:
virtual ~usb_device();
public:
int add_endpoint(const char* path, bool bulk/*true - bulk, false - int*/, bool in);
int open_device(const char* dev, bool ffs_mode, int* fd = nullptr);
int open_endpoint(int ep_ind, int* fd = nullptr);
int close_endpoint(int ep_ind);
int close_device(void);
int pull_up(std::string* msg = nullptr); // turn USB-pin on, so that the host can detect me
int pull_down(void);
int get_device_fd(void);
int get_endpoint_fd(int ep_ind);
size_t get_max_packet(void);
};

176
usb/usb_gadget.h Normal file
View File

@ -0,0 +1,176 @@
/*
* CAMTP Responder
* Copyright (c) 2020 Holdtecs Technologies
*
* CAMTP Responder is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* CAMTP Responder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 3 for more details.
*
* You should have received a copy of the GNU General Public License
* along with CAMTP Responder; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file usb_gadget.c
* @brief USB gadget layer
* @author ***
*/
#ifndef _INC_USB_GADGET_H_
#define _INC_USB_GADGET_H_
#include <linux/usb/ch9.h>
#include <linux/usb/gadgetfs.h>
#include <linux/usb/functionfs.h>
#include "usbstring.h"
enum
{
EP_DESCRIPTOR_IN = 0,
EP_DESCRIPTOR_OUT,
EP_DESCRIPTOR_INT_IN,
EP_NB_OF_DESCRIPTORS
};
#define EP_INT_MODE 0x00000000
#define EP_BULK_MODE 0x00000001
#define EP_IN_DIR 0x00000000
#define EP_OUT_DIR 0x00000002
#define EP_HS_MODE 0x00000004
#define EP_SS_MODE 0x00000008
typedef struct _EndPointsDesc {
struct usb_interface_descriptor if_desc;
struct usb_endpoint_descriptor_no_audio ep_desc_in;
struct usb_endpoint_descriptor_no_audio ep_desc_out;
struct usb_endpoint_descriptor_no_audio ep_desc_int_in;
} __attribute__((packed)) EndPointsDesc;
typedef struct _SSEndPointsDesc {
struct usb_interface_descriptor if_desc;
struct usb_endpoint_descriptor_no_audio ep_desc_in;
struct usb_ss_ep_comp_descriptor ep_desc_in_comp;
struct usb_endpoint_descriptor_no_audio ep_desc_out;
struct usb_ss_ep_comp_descriptor ep_desc_out_comp;
struct usb_endpoint_descriptor_no_audio ep_desc_int_in;
struct usb_ss_ep_comp_descriptor ep_desc_int_in_comp;
} __attribute__((packed)) SSEndPointsDesc;
typedef struct ep_cfg_descriptor {
struct usb_endpoint_descriptor_no_audio ep_desc;
#ifdef CONFIG_USB_SS_SUPPORT
struct usb_ss_ep_comp_descriptor ep_desc_comp;
#endif
} __attribute__((packed)) ep_cfg_descriptor;
// Direct GadgetFS mode
typedef struct _usb_cfg
{
uint32_t head;
#if CONFIG_USB_FS_SUPPORT
struct usb_config_descriptor cfg_fs;
EndPointsDesc ep_desc_fs;
#endif
#if CONFIG_USB_HS_SUPPORT
struct usb_config_descriptor cfg_hs;
EndPointsDesc ep_desc_hs;
#endif
#if CONFIG_USB_SS_SUPPORT
struct usb_config_descriptor cfg_ss;
SSEndPointsDesc ep_desc_ss;
#endif
struct usb_device_descriptor dev_desc;
} __attribute__ ((packed)) usb_cfg;
// FunctionFS mode
typedef struct _usb_ffs_cfg
{
uint32_t magic;
uint32_t length;
#ifndef OLD_FUNCTIONFS_DESCRIPTORS // Kernel > v3.14
uint32_t flags;
#endif
#ifdef CONFIG_USB_FS_SUPPORT
uint32_t fs_count;
#endif
#ifdef CONFIG_USB_HS_SUPPORT
uint32_t hs_count;
#endif
#if defined(CONFIG_USB_SS_SUPPORT) && !defined(OLD_FUNCTIONFS_DESCRIPTORS)
uint32_t ss_count;
#endif
#ifdef CONFIG_USB_FS_SUPPORT
EndPointsDesc ep_desc_fs;
#endif
#ifdef CONFIG_USB_HS_SUPPORT
EndPointsDesc ep_desc_hs;
#endif
#if defined(CONFIG_USB_SS_SUPPORT) && !defined(OLD_FUNCTIONFS_DESCRIPTORS)
SSEndPointsDesc ep_desc_ss;
#endif
} __attribute__ ((packed)) usb_ffs_cfg;
typedef struct _ffs_strings
{
struct usb_functionfs_strings_head header;
uint16_t code;
char string_data[128]; // string data.
} __attribute__((packed)) ffs_strings;
typedef struct _ep_cfg
{
uint32_t head;
ep_cfg_descriptor ep_desc[2];
} __attribute__ ((packed)) ep_cfg;
enum {
STRINGID_MANUFACTURER = 1,
STRINGID_PRODUCT,
STRINGID_SERIAL,
STRINGID_CONFIG_HS,
STRINGID_CONFIG_LS,
STRINGID_INTERFACE,
STRINGID_MAX
};
#define MAX_USB_STRING 16
typedef struct _usb_gadget
{
int usb_device;
int ep_handles[EP_NB_OF_DESCRIPTORS];
usb_cfg *usb_config;
usb_ffs_cfg *usb_ffs_config;
ep_cfg *ep_config[EP_NB_OF_DESCRIPTORS];
char *ep_path[EP_NB_OF_DESCRIPTORS];
struct usb_string stringtab[MAX_USB_STRING];
}usb_gadget;
#endif

636
usb/usb_io.cpp Normal file
View File

@ -0,0 +1,636 @@
#include "usb_io.h"
#include <sys/fcntl.h>
#include <unistd.h>
#include <linux/usb/functionfs.h>
#include <thread>
#include <base/encrypt.h>
#include "usb_dev.h"
#include "default_cfg.h"
// #define TEST
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// async_usb_gadget
async_usb_gadget::async_usb_gadget(std::function<FUNCTION_PROTO_COMMAND_HANDLE> cmd_handler
, std::function<void(bool)> dev_conn)
: handle_cmd_(cmd_handler), dev_connect_(dev_conn)
, enc_head_(ENCRYPT_CMD_NONE), enc_payload_(ENCRYPT_NONE), enc_data_(0)
, cmd_que_("in-queue"), sent_que_("out-queue")
{
memset((void*)&status_, 0, sizeof(status_));
dev_ = new usb_device("fe900000.dwc3", "/opt/cfg/usb_gadget/g1/UDC", "linaro");
dev_->add_endpoint(USB_EP_BULK_IN, true, true);
dev_->add_endpoint(USB_EP_BULK_OUT, true, false);
if(dev_->open_device(USB_DEV, true))
{
dev_->release();
dev_ = nullptr;
run_ = false;
return;
}
auto ep0 = [this](void) -> void
{
thread_read_ep0();
};
auto task = [this](void) -> void
{
thread_pump_task();
};
auto bulkw = [this](void) -> void
{
int fd = -1,
err = dev_->open_endpoint(EP_IND_BULK_IN, &fd);
if(err == 0)
{
thread_read_bulk(fd);
dev_->close_endpoint(EP_IND_BULK_IN);
}
};
auto bulkr = [this](void) -> void
{
int fd = -1,
err = dev_->open_endpoint(EP_IND_BULK_OUT, &fd);
if(err == 0)
{
thread_write_bulk(fd);
dev_->close_endpoint(EP_IND_BULK_OUT);
}
};
auto excep = [&](const char* thread_name) -> void
{
threads_.stop(thread_name);
// threads_.start(task, "thread_pump_task");
};
threads_.set_exception_handler(excep);
unit_in_ = unit_out_ = dev_->get_max_packet();
threads_.start(task, "thread_pump_task");
threads_.start(bulkw, "thread_read_bulk");
threads_.start(bulkr, "thread_write_bulk");
threads_.start(ep0, "thread_read_ep0");
}
async_usb_gadget::~async_usb_gadget()
{
stop();
}
const char* async_usb_gadget::ep0_status_desc(int ep0_status, char* unk_buf/*>= 20 bytes*/)
{
RETURN_ENUM_STR(ep0_status, EP0_STATUS_IDLE);
RETURN_ENUM_STR(ep0_status, EP0_STATUS_READ_DATA);
RETURN_ENUM_STR(ep0_status, EP0_STATUS_READ_INVAL_DATA);
RETURN_ENUM_STR(ep0_status, EP0_STATUS_HANDLE_CMD);
sprintf(unk_buf, "Unknown status (%d)", ep0_status);
return unk_buf;
}
int async_usb_gadget::wait_fd_event(int fd, int to_ms)
{
struct timeval timeout, *pto = NULL;
fd_set read_set;
int ret = 0;
FD_ZERO(&read_set);
FD_SET(fd, &read_set);
if (to_ms != -1)
{
timeout.tv_sec = to_ms / 1000;
timeout.tv_usec = (to_ms % 1000) * 1000;
pto = &timeout;
}
ret = select(fd + 1, &read_set, NULL, NULL, pto);
return ret;
}
uint16_t async_usb_gadget::get_buf_coefficient(void)
{
SIMPLE_LOCK(buf_coef_lock_);
return buf_coef_;
}
void async_usb_gadget::set_buf_coefficient(int coef)
{
SIMPLE_LOCK(buf_coef_lock_);
buf_coef_ = (coef <= 0 || coef > 10000) ? 1 : coef;
}
dyn_mem_ptr async_usb_gadget::handle_ctrl_message(dyn_mem_ptr data)
{
struct usb_functionfs_event* pev = (struct usb_functionfs_event*)data->ptr();
dyn_mem_ptr reply = nullptr;
switch (pev->type)
{
case FUNCTIONFS_ENABLE:
utils::to_log(LOG_LEVEL_ALL, "EP0 FFS ENABLE\n");
if(dev_connect_)
dev_connect_(true);
break;
case FUNCTIONFS_DISABLE:
utils::to_log(LOG_LEVEL_ALL, "EP0 FFS DISABLE\n");
if(dev_connect_)
dev_connect_(false);
break;
case FUNCTIONFS_SETUP:
reply = handle_ctrl_setup(data);
break;
case FUNCTIONFS_BIND:
utils::to_log(LOG_LEVEL_ALL, "EP0 FFS BIND\n");
break;
case FUNCTIONFS_UNBIND:
utils::to_log(LOG_LEVEL_ALL, "EP0 FFS UNBIND\n");
break;
case FUNCTIONFS_SUSPEND:
utils::to_log(LOG_LEVEL_ALL, "EP0 FFS SUSPEND\n");
break;
case FUNCTIONFS_RESUME:
utils::to_log(LOG_LEVEL_ALL, "EP0 FFS RESUME\n");
break;
}
if (pev->u.setup.bRequestType & USB_DIR_IN)
{
// if host need data back, and reply is null, we send nonsense data back ...
if (!reply)
{
reply = dyn_mem::memory(pev->u.setup.wLength + sizeof(uint16_t));
reply->set_len(pev->u.setup.wLength);
for (int i = 0; i <= pev->u.setup.wLength / sizeof(uint16_t); ++i)
((uint16_t*)reply->ptr())[i] = 0x0baad;
utils::to_log(LOG_LEVEL_DEBUG, "fake control message reply(%u bytes) while request with USB_DIR_IN flag but no reply returned.\n", reply->get_rest());
}
}
else if(reply)
{
utils::to_log(LOG_LEVEL_DEBUG, "discard control message reply(%u bytes) while request without USB_DIR_IN flag.\n", reply->get_rest());
reply->release();
reply = nullptr;
}
return reply;
}
dyn_mem_ptr async_usb_gadget::handle_ctrl_setup(dyn_mem_ptr data)
{
struct usb_functionfs_event* pev = (struct usb_functionfs_event*)data->ptr();
bool handled = (pev->u.setup.bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR;
dyn_mem_ptr reply = nullptr;
uint32_t err = 0;
if(handled)
{
switch (pev->u.setup.bRequest)
{
case USB_REQ_EP0_GET_PROTO_VER:
reply = dyn_mem::memory(sizeof(short));
*(short*)reply->ptr() = PROTOCOL_VER;
reply->set_len(sizeof(short));
break;
case USB_REQ_EP0_GET_STATUS:
reply = dyn_mem::memory(sizeof(struct _ep0_reply));
{
reply->put((void*)&status_, sizeof(status_));
}
break;
case USB_REQ_EP0_RESET_BULK:
reply = dyn_mem::memory(sizeof(uint32_t));
reply->put(&err, sizeof(err));
break;
case USB_REQ_EP0_SET_ENCRYPT:
if (pev->u.setup.wLength == sizeof(PACK_BASE))
{
LPPACK_BASE pack = (LPPACK_BASE)&pev[1];
enc_head_ = pack->enc_cmd;
enc_payload_ = pack->encrypt;
enc_data_ = pack->enc_data;
utils::to_log(LOG_LEVEL_DEBUG, "Set encrypting method: command - %d; payload - %d\n", enc_head_, enc_payload_);
}
break;
case USB_REQ_EP0_SET_BULK_BUFFER:
// if(pev->u.setup.wLength == sizeof(short))
{
uint16_t pre = buf_coef_;
set_buf_coefficient(pev->u.setup.wIndex);
utils::to_log(LOG_LEVEL_DEBUG, "Set bulk buffer size coefficient from %d to %d\n", pre, buf_coef_);
}
break;
default:
handled = false;
}
}
if(!handled)
{
utils::to_log(LOG_LEVEL_ALL, "EP0 event(0x%x, 0x%x, 0x%x, 0x%x, 0x%x) has not handled by user business.\n"
, pev->u.setup.bRequestType, pev->u.setup.bRequest, pev->u.setup.wValue, pev->u.setup.wIndex, pev->u.setup.wLength);
}
return reply;
}
int async_usb_gadget::inner_write_bulk(int fd, data_source_ptr data, int* err)
{
unsigned char* ptr = data->ptr();
size_t bulk_size = unit_in_ * get_buf_coefficient(),
total = data->get_rest(), off = 0, size = total > bulk_size ? bulk_size : total;
int w = 0;
if(data->is_memory_block())
{
while((w = write(fd, ptr + off, size)) > 0)
{
off += w;
status_.bytes_to_sent -= w;
if(off >= total)
break;
if(off + bulk_size < total)
size = bulk_size;
else
size = total - off;
}
if(w <= 0 && err)
*err = errno;
}
else
{
dyn_mem_ptr buf = dyn_mem::memory(bulk_size);
uint32_t len = bulk_size;
if(err)
*err = 0;
total = 0;
while((w = data->fetch_data(buf->ptr(), &len)) == 0)
{
ptr = buf->ptr();
off = 0;
w = write(fd, ptr + off, len);
while(w > 0 && w + off < len)
{
status_.bytes_to_sent -= w;
total += w;
off += w;
w = write(fd, ptr + off, len - off);
}
if(w <= 0)
{
if(err)
*err = errno;
utils::to_log(LOG_LEVEL_ALL, "inner_write_bulk error(%d/%d): %d (%s)\n", off, len, errno, strerror(errno));
break;
}
else
{
status_.bytes_to_sent -= w;
total += w;
}
if(data->get_rest() == 0)
break;
len = bulk_size;
}
buf->release();
off = total;
}
return off;
}
dyn_mem_ptr async_usb_gadget::handle_bulk_command(dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* pkd)
{
dyn_mem_ptr decrypt = packet_decrypt(data);
if (decrypt)
{
data->release();
data = decrypt;
return handle_cmd_(data, used, pkd);
}
else
{
*used = data->get_rest();
*pkd = nullptr;
return nullptr;
}
}
void async_usb_gadget::thread_read_ep0(void)
{
struct timeval timeout;
int headl = sizeof(struct usb_functionfs_event),
datal = 128, // gadget_->usb_config->dev_desc.bMaxPacketSize0,
ret = 0,
fd = dev_->get_device_fd();
dyn_mem_ptr mem = dyn_mem::memory(headl + datal);
uint8_t* ptr = mem->ptr();
uint32_t recycles = 0;
std::string msg("");
struct usb_functionfs_event* pe = (struct usb_functionfs_event*)ptr;
utils::to_log(LOG_LEVEL_ALL, "EP0 monitoring thread(%p of id %lx) started ...\n", &async_usb_gadget::thread_read_ep0, getpid());
ret = dev_->pull_up(&msg);
while(run_)
{
ret = wait_fd_event(fd);
if( ret <= 0 )
{
utils::to_log(LOG_LEVEL_ALL, "select EP0(%d) failed: %d(%s)\n", fd, errno, strerror(errno));
break;
}
// timeout.tv_sec = 0;
ret = read(fd, ptr, headl);
if (ret < 0)
{
utils::to_log(LOG_LEVEL_ALL, "read EP0 failed: %d(%s)\n", errno, strerror(errno));
break;
}
bool good = true;
if(pe->u.setup.bRequestType & USB_DIR_IN)
mem->set_len(headl);
else
{
if (pe->u.setup.wLength > datal)
{
good = false;
utils::to_log(LOG_LEVEL_ALL, "EP0 data too long(%d > %d), we will discard this packet(0x%x, 0x%x, 0x%x, 0x%x)!\n", pe->u.setup.wLength, datal,
pe->u.setup.bRequestType, pe->u.setup.bRequest, pe->u.setup.wValue, pe->u.setup.wIndex);
// we read-out whole data here ...
int rest = pe->u.setup.wLength;
while (rest > 0)
{
read(fd, ptr + headl, rest > datal ? datal : rest);
rest -= datal;
}
}
else
{
read(fd, ptr + headl, pe->u.setup.wLength);
mem->set_len(headl + pe->u.setup.wLength);
}
}
if (good)
{
dyn_mem_ptr reply = handle_ctrl_message(mem);
if(reply)
{
write(fd, reply->ptr(), reply->get_rest());
reply->release();
}
}
}
mem->release();
}
void async_usb_gadget::thread_read_bulk(int fd)
{
size_t bulk_size = unit_out_ * get_buf_coefficient();
uint32_t cnt_0 = 0;
while(run_)
{
dyn_mem_ptr buf(dyn_mem::memory(bulk_size));
int l = 0;
status_.out_status = BULK_STATUS_IO;
l = read(fd, buf->ptr(), bulk_size);
if(l <= 0)
{
buf->release();
if(errno)
{
utils::to_log(LOG_LEVEL_ALL, "read bulk failed: %d(%s)\n", errno, strerror(errno));
status_.out_status = BULK_STATUS_ERROR;
status_.out_err = errno;
break;
}
else
{
cnt_0++;
}
}
else if (!run_)
{
utils::to_log(LOG_LEVEL_ALL, "thread_read_bulk do reset-bulk ...\n");
buf->release();
if(!run_)
break;
}
else
{
status_.out_status = BULK_STATUS_IDLE;
buf->set_len(l);
cmd_que_.save(buf, true);
}
}
if(status_.out_status == BULK_STATUS_IDLE)
status_.out_status = BULK_STATUS_NOT_START;
}
void async_usb_gadget::thread_write_bulk(int fd)
{
status_.in_status = BULK_STATUS_IDLE;
while(run_)
{
data_source_ptr data;
int err = 0;
if(sent_que_.take(data, true))
{
status_.packets_to_sent = sent_que_.size();
status_.bytes_to_sent = data->get_rest();
status_.in_status = BULK_STATUS_IO;
inner_write_bulk(fd, data, &err);
status_.in_status = BULK_STATUS_IDLE;
status_.bytes_to_sent = 0;
data->release();
if(err)
{
utils::to_log(LOG_LEVEL_ALL, "write bulk failed: %d(%s)\n", errno, strerror(errno));
status_.in_status = BULK_STATUS_ERROR;
status_.in_err = err;
break;
}
}
}
if(status_.in_status == BULK_STATUS_IDLE)
status_.in_status = BULK_STATUS_NOT_START;
}
void async_usb_gadget::thread_pump_task(void)
{
dyn_mem_ptr prev = nullptr, data = nullptr, reply = nullptr;
bool in_err_statu = false;
uint32_t required = 0, used = 0, restore_err_cnt = 0, max_que = 0;
data_holder *dh = nullptr;
LPPACK_BASE pack = nullptr;
uint64_t total_size = 0;
while(run_)
{
data = nullptr;
if(cmd_que_.take(data, true) && data)
{
status_.task_cnt = cmd_que_.size();
if(max_que < status_.task_cnt)
max_que = status_.task_cnt;
if (in_err_statu)
{
}
if(prev)
{
utils::to_log(LOG_LEVEL_ALL, "Combine partial packet with length %u and %u ...\n", prev->get_rest(), data->get_rest());
*prev += *data;
data->release();
data = prev;
prev = nullptr;
}
do
{
packet_data_base_ptr pack_data = nullptr;
data_source_ptr ds = nullptr;
bool notify_reply_ok = false;
if(dh == nullptr)
{
if (data->get_rest() < sizeof(PACK_BASE))
break;
else
{
pack = (LPPACK_BASE)data->ptr();
status_.task_cmd = pack->cmd;
status_.task_pack_id = pack->pack_id;
used = data->get_rest();
reply = handle_bulk_command(data, &used, &pack_data);
notify_reply_ok = used & (INT32_MAX + 1);
used &= INT32_MAX;
if(pack_data)
{
dh = dynamic_cast<data_holder_ptr>(pack_data);
if(!dh)
{
ds = dynamic_cast<data_source_ptr>(pack_data);
if(!ds)
pack_data->release();
}
else
total_size = 0;
}
}
}
else
{
uint32_t len = data->get_rest();
int err = dh->put_data(data->ptr(), &len);
if(len < data->get_rest())
{
utils::to_log(LOG_LEVEL_WARNING, "Put partial data %u/%u! at +%08X with error %d\n", len, data->get_rest(), total_size, err);
}
total_size += len;
if(dh->is_complete() || err)
{
reply = dyn_mem::memory(sizeof(PACK_BASE));
pack = (LPPACK_BASE)reply->ptr();
BASE_PACKET_REPLY(*pack, dh->get_packet_command() + 1, dh->get_packet_id(), err);
reply->set_len(sizeof(PACK_BASE));
utils::to_log(LOG_LEVEL_ALL, "Finished receiving file with error(Max queue size is %u): %d, total size = 0x%x\n", max_que, err, total_size);
dh->release();
dh = nullptr;
}
used = len;
if (err)
{
in_err_statu = true;
restore_err_cnt = 0;
}
}
// first reply the packet ...
if(reply)
{
// encrypt ...
dyn_mem_ptr enc = packet_encrypt(reply, enc_head_, enc_payload_, enc_data_);
if (enc)
{
reply->release();
reply = enc;
}
write_bulk(dynamic_cast<data_source_ptr>(reply));
if(notify_reply_ok)
handle_bulk_command(data, nullptr, nullptr);
reply->release();
reply = nullptr;
}
// second send appendix data ...
if (ds)
{
write_bulk(ds);
ds->release();
ds = nullptr;
}
status_.task_required_bytes = dh ? dh->get_required() : 0;
if(status_.task_required_bytes == 0)
status_.task_cmd = status_.task_pack_id = 0;
data->used(used);
}while(used && data->get_rest());
if(data->get_rest())
prev = data;
else
data->release();
}
}
if(prev)
prev->release();
if (data)
data->release();
if (reply)
reply->release();
if (dh)
dh->release();
}
int async_usb_gadget::stop(void)
{
run_ = false;
if(dev_)
{
dev_->pull_down();
dev_->close_device();
dev_->release();
dev_ = nullptr;
}
return 0;
}
int async_usb_gadget::write_bulk(data_source_ptr data)
{
int quel = 0;
data->add_ref();
quel = sent_que_.save(data, true);
return quel;
}

98
usb/usb_io.h Normal file
View File

@ -0,0 +1,98 @@
#pragma once
// Objects IO
//
// created on 2023-03-10
#include <base/utils.h>
#include <base/packet.h>
#include <base/data.h>
#include <functional>
#include <memory>
#include <vector>
#include <thread>
// callback proto
//
// parameters: usb_functionfs_event* - the function event ptr
//
// dyn_mem_ptr - the packet buffer, read-only
//
// uint32_t* - to return how many data in bytes the handler consumed
//
// normally, the value should be sizeof(PACK_BASE) + PACK_BASE::payload_len, i.e. the handler consume all data of an entire packet
//
// when invalid packet, suggest use the entire data
//
// packet_data_base_ptr* - return data_holder or data_source or nullptr
//
// data_holder: the packet/command need more data than dyn_mem_ptr provides to complete the business. such as 'write a large file'
//
// data_source: the reply content may be a large data (a large file content)
//
// return value of all routines is the reply packet, nullptr if the packet need not reply
//
#define FUNCTION_PROTO_UNHANDLED_EP0 dyn_mem_ptr(struct usb_functionfs_event*)
class usb_device;
class async_usb_gadget : public refer
{
volatile bool run_ = true;
usb_device *dev_ = nullptr;
safe_thread threads_;
size_t unit_in_ = 0x200;
size_t unit_out_ = 0x200;
uint16_t buf_coef_ = 1;
MUTEX buf_coef_lock_;
uint32_t enc_head_;
uint32_t enc_payload_;
uint8_t enc_data_;
volatile EP0REPLYSTATUS status_;
safe_fifo<dyn_mem_ptr> cmd_que_;
safe_fifo<data_source_ptr> sent_que_;
enum
{
EP0_STATUS_IDLE = 0,
EP0_STATUS_READ_DATA, // ep0_statu::data is rest data len
EP0_STATUS_READ_INVAL_DATA, // ep0_statu::data is rest data len
EP0_STATUS_HANDLE_CMD,
};
std::function<FUNCTION_PROTO_COMMAND_HANDLE> handle_cmd_;
std::function<void(bool)> dev_connect_;
const char* ep0_status_desc(int ep0_status, char* unk_buf/*>= 40 bytes*/);
int wait_fd_event(int fd, int to_ms = -1);
uint16_t get_buf_coefficient(void);
void set_buf_coefficient(int coef);
dyn_mem_ptr handle_ctrl_message(dyn_mem_ptr data);
dyn_mem_ptr handle_ctrl_setup(dyn_mem_ptr data); // user command ...
int inner_write_bulk(int fd, data_source_ptr data, int* err = nullptr);
dyn_mem_ptr handle_bulk_command(dyn_mem_ptr data, uint32_t* used, packet_data_base_ptr* pkd);
void thread_read_ep0(void);
void thread_read_bulk(int fd);
void thread_write_bulk(int fd);
void thread_pump_task(void);
public:
async_usb_gadget(std::function<FUNCTION_PROTO_COMMAND_HANDLE> cmd_handler = std::function<FUNCTION_PROTO_COMMAND_HANDLE>()
, std::function<void(bool)> dev_conn = std::function<void(bool)>());
protected:
~async_usb_gadget();
public:
int stop(void);
int write_bulk(data_source_ptr data); // return sent-que length
};

42
usb/usbstring.h Normal file
View File

@ -0,0 +1,42 @@
/*
* CAMTP Responder
* Copyright (c) 2020 Holdtecs Technologies
*
* CAMTP Responder is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* CAMTP Responder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 3 for more details.
*
* You should have received a copy of the GNU General Public License
* along with CAMTP Responder; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file usbstring.h
* @brief USB strings - Function & structures declarations.
* @author ***
*/
#ifndef _INC_USBSTRING_H_
#define _INC_USBSTRING_H_
struct usb_string {
uint8_t id;
char *str;
};
struct usb_gadget_strings {
uint16_t language; /* 0x0409 for en-us */
struct usb_string *strings;
};
int usb_gadget_get_string (struct usb_gadget_strings *table, int id, uint8_t *buf);
int unicode2charstring(char * str, uint16_t * unicodestr, int maxstrsize);
int char2unicodestring(char * unicodestr, int index, int maxsize, char * str, int unicodestrsize);
#endif

10
usb/xmake.lua Normal file
View File

@ -0,0 +1,10 @@
add_rules("mode.debug", "mode.release")
target("usb")
set_kind("static")
add_syslinks("pthread", "dl")
add_files("*.cpp", "../sdk/base/*.c*", "../sdk/imgprc/*.c*", "../sdk/json/*.c*", "../sdk/sane_opt_json/*.c*")
add_includedirs("../sdk")
add_includedirs(".", { public = true})
add_packages("sdk")
-- add_deps("regs", "gimgproc", "applog", "capimage")

73
xmake.lua Normal file
View File

@ -0,0 +1,73 @@
set_project("GXX")
-- option("device")
-- set_showmenu(true)
-- set_default("g100")
-- set_values("g100", "g200", "g300", "g400")
-- set_category("device")
-- option_end()
-- option("chip")
-- set_showmenu(true)
-- set_default("rk3399")
-- set_values("rk3288", "rk3399")
-- set_category("chip")
-- option_end()
-- option("isp1")
-- set_showmenu(true)
-- set_default(true)
-- set_category("isp")
-- option_end()
-- option("testdemo")
-- set_showmenu(true)
-- set_default(true)
-- set_category("testdemo")
-- option_end()
-- option("testfpga")
-- set_showmenu(true)
-- set_default(true)
-- set_category("regs test")
-- set_configvar("TEST_FPGA", "ON")
-- option_end()
-- option("loopdebug")
-- set_showmenu(true)
-- set_default(false)
-- set_category("regs test")
-- set_configvar("LOOP_DEBUG", "ON")
-- option_end()
-- option("unfixed")
-- set_showmenu(true)
-- set_default(false )
-- set_configvar("CAP_UNFIXED", "ON")
-- option_end()
add_packagedirs("sdk")
-- -- if is_mode("debug") then
-- -- add_defines("DEBUG")
-- -- end
-- set_configvar("MOTOR_UART", get_config("chip") == "rk3288" and "/dev/ttyS3" or "/dev/ttyS4")
-- set_configvar("FPGA_UART", "/dev/ttyUSB0")
-- -- set_configvar("FPGA_UART", get_config("chip") == "rk3288" and "/dev/ttyS3" or "/dev/ttyS4")
-- -- set_configvar("MOTOR_UART", "/dev/ttyUSB0")
-- set_configvar("VIDEO_CLASS", has_config("isp1") and "GVideoISP1" or "gVideo")
target("conf")
set_kind("phony")
add_options("usb_isp1", "chip_type", "unfixed", "testfpga", "loopdebug")
set_configdir("$(buildir)/config")
add_configfiles("config.h.in", {prefixdir = "header"})
add_includedirs("$(buildir)/config/header", { public = true })
includes("usb", "scanner")
-- includes("regs", "deviceio", "motorboard", "capimage", "usb", "service", "scanner", "imgproc", "applog","scanservice","fpgaupdate","motorcontroller","display","testlcd","keymonitor","testkeymonitor","testdisplay","testwakeup")
-- if has_config("testdemo") then
-- includes("testmotorboard", "testcapimage", "testusb", "testscanner", "testimgproc", "testregs","testwakeup")
-- end