Merge branch 'genesys-duplication' into 'master'

genesys: Reduce code duplication

See merge request sane-project/backends!357
pixma-axis-driver
Povilas Kanapickas 2020-03-15 16:13:53 +00:00
commit a1888ae3bd
30 zmienionych plików z 1508 dodań i 3052 usunięć

Wyświetl plik

@ -546,6 +546,7 @@ libgenesys_la_SOURCES = genesys/genesys.cpp genesys/genesys.h \
genesys/status.h genesys/status.cpp \
genesys/tables_frontend.cpp \
genesys/tables_gpo.cpp \
genesys/tables_memory_layout.cpp \
genesys/tables_model.cpp \
genesys/tables_motor.cpp \
genesys/tables_sensor.cpp \
@ -554,6 +555,7 @@ libgenesys_la_SOURCES = genesys/genesys.cpp genesys/genesys.h \
genesys/test_usb_device.h genesys/test_usb_device.cpp \
genesys/usb_device.h genesys/usb_device.cpp \
genesys/low.cpp genesys/low.h \
genesys/value_filter.h \
genesys/utilities.h
libgenesys_la_CPPFLAGS = $(AM_CPPFLAGS) -DBACKEND_NAME=genesys

Wyświetl plik

@ -24,6 +24,7 @@
#include "command_set_common.h"
#include "low.h"
#include "value_filter.h"
namespace genesys {
@ -150,7 +151,7 @@ void CommandSetCommon::set_motor_mode(Genesys_Device& dev, Genesys_Register_Set&
struct MotorSettings {
ModelId model_id;
ResolutionFilter resolutions;
ValueFilterAny<unsigned> resolutions;
GenesysRegisterSettingSet regs_primary_and_secondary;
GenesysRegisterSettingSet regs_primary;
GenesysRegisterSettingSet regs_secondary;
@ -187,7 +188,7 @@ void CommandSetCommon::set_motor_mode(Genesys_Device& dev, Genesys_Register_Set&
{ 0xa6, 0x01, 0x41 },
}
},
{ ModelId::HP_SCANJET_G4050, ResolutionFilter::ANY, {
{ ModelId::HP_SCANJET_G4050, VALUE_FILTER_ANY, {
{ 0x6b, 0x81, 0x81 }, // set MULTFILM and GPOADF
{ 0x6c, 0x00, 0x40 }, // note that reverse change is not applied on off
// 0xa6 register 0x08 bit likely sets motor power. No move at all without that one
@ -200,9 +201,9 @@ void CommandSetCommon::set_motor_mode(Genesys_Device& dev, Genesys_Register_Set&
{ 0xa9, 0x00, 0x10 }, // note that 0x20 bit is not reset
}, {}
},
{ ModelId::PLUSTEK_OPTICFILM_7200I, ResolutionFilter::ANY, {}, {}, {} },
{ ModelId::PLUSTEK_OPTICFILM_7300, ResolutionFilter::ANY, {}, {}, {} },
{ ModelId::PLUSTEK_OPTICFILM_7500I, ResolutionFilter::ANY, {}, {}, {} },
{ ModelId::PLUSTEK_OPTICFILM_7200I, VALUE_FILTER_ANY, {}, {}, {} },
{ ModelId::PLUSTEK_OPTICFILM_7300, VALUE_FILTER_ANY, {}, {}, {} },
{ ModelId::PLUSTEK_OPTICFILM_7500I, VALUE_FILTER_ANY, {}, {}, {} },
};
for (const auto& setting : settings) {

Wyświetl plik

@ -227,8 +227,12 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev)
<< " initial_regs: " << format_indent_braced_list(4, dev.initial_regs) << '\n'
<< " settings: " << format_indent_braced_list(4, dev.settings) << '\n'
<< " frontend: " << format_indent_braced_list(4, dev.frontend) << '\n'
<< " frontend_initial: " << format_indent_braced_list(4, dev.frontend_initial) << '\n'
<< " gpo.regs: " << format_indent_braced_list(4, dev.gpo.regs) << '\n'
<< " frontend_initial: " << format_indent_braced_list(4, dev.frontend_initial) << '\n';
if (!dev.memory_layout.regs.empty()) {
out << " memory_layout.regs: "
<< format_indent_braced_list(4, dev.memory_layout.regs) << '\n';
}
out << " gpo.regs: " << format_indent_braced_list(4, dev.gpo.regs) << '\n'
<< " motor: " << format_indent_braced_list(4, dev.motor) << '\n'
<< " control[0..6]: " << std::hex
<< static_cast<unsigned>(dev.control[0]) << ' '
@ -272,6 +276,15 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev)
return out;
}
void apply_reg_settings_to_device_write_only(Genesys_Device& dev,
const GenesysRegisterSettingSet& regs)
{
GenesysRegisterSettingSet backup;
for (const auto& reg : regs) {
dev.interface->write_register(reg.address, reg.value);
}
}
void apply_reg_settings_to_device(Genesys_Device& dev, const GenesysRegisterSettingSet& regs)
{
apply_reg_settings_to_device_with_backup(dev, regs);

Wyświetl plik

@ -78,6 +78,17 @@ struct Genesys_Gpo
GenesysRegisterSettingSet regs;
};
struct MemoryLayout
{
// This is used on GL845, GL846, GL847 and GL124 which have special registers to define the
// memory layout
MemoryLayout() = default;
ValueFilter<ModelId> models;
GenesysRegisterSettingSet regs;
};
struct MethodResolutions
{
std::vector<ScanMethod> methods;
@ -272,6 +283,7 @@ struct Genesys_Device
Genesys_Settings settings;
Genesys_Frontend frontend, frontend_initial;
Genesys_Gpo gpo;
MemoryLayout memory_layout;
Genesys_Motor motor;
std::uint8_t control[6] = {};
@ -365,6 +377,8 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev);
void apply_reg_settings_to_device(Genesys_Device& dev, const GenesysRegisterSettingSet& regs);
void apply_reg_settings_to_device_write_only(Genesys_Device& dev,
const GenesysRegisterSettingSet& regs);
GenesysRegisterSettingSet
apply_reg_settings_to_device_with_backup(Genesys_Device& dev,
const GenesysRegisterSettingSet& regs);

Wyświetl plik

@ -109,6 +109,63 @@ std::ostream& operator<<(std::ostream& out, ColorFilter mode)
return out;
}
std::ostream& operator<<(std::ostream& out, ModelId id)
{
switch (id) {
case ModelId::UNKNOWN: out << "UNKNOWN"; break;
case ModelId::CANON_4400F: out << "CANON_4400F"; break;
case ModelId::CANON_5600F: out << "CANON_5600F"; break;
case ModelId::CANON_8400F: out << "CANON_8400F"; break;
case ModelId::CANON_8600F: out << "CANON_8600F"; break;
case ModelId::CANON_IMAGE_FORMULA_101: out << "CANON_IMAGE_FORMULA_101"; break;
case ModelId::CANON_LIDE_50: out << "CANON_LIDE_50"; break;
case ModelId::CANON_LIDE_60: out << "CANON_LIDE_60"; break;
case ModelId::CANON_LIDE_80: out << "CANON_LIDE_80"; break;
case ModelId::CANON_LIDE_100: out << "CANON_LIDE_100"; break;
case ModelId::CANON_LIDE_110: out << "CANON_LIDE_110"; break;
case ModelId::CANON_LIDE_120: out << "CANON_LIDE_120"; break;
case ModelId::CANON_LIDE_200: out << "CANON_LIDE_200"; break;
case ModelId::CANON_LIDE_210: out << "CANON_LIDE_210"; break;
case ModelId::CANON_LIDE_220: out << "CANON_LIDE_220"; break;
case ModelId::CANON_LIDE_700F: out << "CANON_LIDE_700F"; break;
case ModelId::DCT_DOCKETPORT_487: out << "DCT_DOCKETPORT_487"; break;
case ModelId::HP_SCANJET_2300C: out << "HP_SCANJET_2300C"; break;
case ModelId::HP_SCANJET_2400C: out << "HP_SCANJET_2400C"; break;
case ModelId::HP_SCANJET_3670: out << "HP_SCANJET_3670"; break;
case ModelId::HP_SCANJET_4850C: out << "HP_SCANJET_4850C"; break;
case ModelId::HP_SCANJET_G4010: out << "HP_SCANJET_G4010"; break;
case ModelId::HP_SCANJET_G4050: out << "HP_SCANJET_G4050"; break;
case ModelId::HP_SCANJET_N6310: out << "HP_SCANJET_N6310"; break;
case ModelId::MEDION_MD5345: out << "MEDION_MD5345"; break;
case ModelId::PANASONIC_KV_SS080: out << "PANASONIC_KV_SS080"; break;
case ModelId::PENTAX_DSMOBILE_600: out << "PENTAX_DSMOBILE_600"; break;
case ModelId::PLUSTEK_OPTICBOOK_3800: out << "PLUSTEK_OPTICBOOK_3800"; break;
case ModelId::PLUSTEK_OPTICFILM_7200I: out << "PLUSTEK_OPTICFILM_7200I"; break;
case ModelId::PLUSTEK_OPTICFILM_7300: out << "PLUSTEK_OPTICFILM_7300"; break;
case ModelId::PLUSTEK_OPTICFILM_7500I: out << "PLUSTEK_OPTICFILM_7500I"; break;
case ModelId::PLUSTEK_OPTICPRO_3600: out << "PLUSTEK_OPTICPRO_3600"; break;
case ModelId::PLUSTEK_OPTICPRO_ST12: out << "PLUSTEK_OPTICPRO_ST12"; break;
case ModelId::PLUSTEK_OPTICPRO_ST24: out << "PLUSTEK_OPTICPRO_ST24"; break;
case ModelId::SYSCAN_DOCKETPORT_465: out << "SYSCAN_DOCKETPORT_465"; break;
case ModelId::SYSCAN_DOCKETPORT_467: out << "SYSCAN_DOCKETPORT_467"; break;
case ModelId::SYSCAN_DOCKETPORT_485: out << "SYSCAN_DOCKETPORT_485"; break;
case ModelId::SYSCAN_DOCKETPORT_665: out << "SYSCAN_DOCKETPORT_665"; break;
case ModelId::SYSCAN_DOCKETPORT_685: out << "SYSCAN_DOCKETPORT_685"; break;
case ModelId::UMAX_ASTRA_4500: out << "UMAX_ASTRA_4500"; break;
case ModelId::VISIONEER_7100: out << "VISIONEER_7100"; break;
case ModelId::VISIONEER_ROADWARRIOR: out << "VISIONEER_ROADWARRIOR"; break;
case ModelId::VISIONEER_STROBE_XP100_REVISION3:
out << "VISIONEER_STROBE_XP100_REVISION3"; break;
case ModelId::VISIONEER_STROBE_XP200: out << "VISIONEER_STROBE_XP200"; break;
case ModelId::VISIONEER_STROBE_XP300: out << "VISIONEER_STROBE_XP300"; break;
case ModelId::XEROX_2400: out << "XEROX_2400"; break;
case ModelId::XEROX_TRAVELSCANNER_100: out << "XEROX_TRAVELSCANNER_100"; break;
default:
out << static_cast<unsigned>(id); break;
}
return out;
}
std::ostream& operator<<(std::ostream& out, StepType type)
{
switch (type) {

Wyświetl plik

@ -222,6 +222,21 @@ enum class ModelId : unsigned
XEROX_TRAVELSCANNER_100,
};
inline void serialize(std::istream& str, ModelId& x)
{
unsigned value;
serialize(str, value);
x = static_cast<ModelId>(value);
}
inline void serialize(std::ostream& str, ModelId& x)
{
unsigned value = static_cast<unsigned>(x);
serialize(str, value);
}
std::ostream& operator<<(std::ostream& out, ModelId id);
enum class SensorId : unsigned
{
UNKNOWN = 0,

Wyświetl plik

@ -111,8 +111,6 @@ class ScannerInterfaceUsb;
class TestScannerInterface;
// sensor.h
class ScanMethodFilter;
class ResolutionFilter;
struct GenesysFrontendLayout;
struct Genesys_Frontend;
struct SensorExposure;
@ -123,6 +121,10 @@ struct Genesys_Settings;
struct SetupParams;
struct ScanSession;
// value_filter.h
template<class T> class ValueFilter;
template<class T> class ValueFilterAny;
// test_usb_device.h
class TestUsbDevice;

Wyświetl plik

@ -308,6 +308,24 @@ void sanei_genesys_init_structs (Genesys_Device * dev)
}
}
if (dev->model->asic_type == AsicType::GL845 ||
dev->model->asic_type == AsicType::GL846 ||
dev->model->asic_type == AsicType::GL847 ||
dev->model->asic_type == AsicType::GL124)
{
bool memory_layout_found = false;
for (const auto& memory_layout : *s_memory_layout) {
if (memory_layout.models.matches(dev->model->model_id)) {
dev->memory_layout = memory_layout;
memory_layout_found = true;
break;
}
}
if (!memory_layout_found) {
throw SaneException("Could not find memory layout");
}
}
if (!motor_ok || !gpo_ok || !fe_ok) {
throw SaneException("bad description(s) for fe/gpo/motor=%d/%d/%d\n",
static_cast<unsigned>(dev->model->sensor_id),
@ -1272,6 +1290,577 @@ void scanner_search_strip(Genesys_Device& dev, bool forward, bool black)
}
}
static int dark_average_channel(const Image& image, unsigned black, unsigned channel)
{
auto channels = get_pixel_channels(image.get_format());
unsigned avg[3];
// computes average values on black margin
for (unsigned ch = 0; ch < channels; ch++) {
avg[ch] = 0;
unsigned count = 0;
// FIXME: start with the second line because the black pixels often have noise on the first
// line; the cause is probably incorrectly cleaned up previous scan
for (std::size_t y = 1; y < image.get_height(); y++) {
for (unsigned j = 0; j < black; j++) {
avg[ch] += image.get_raw_channel(j, y, ch);
count++;
}
}
if (count > 0) {
avg[ch] /= count;
}
DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, ch, avg[ch]);
}
DBG(DBG_info, "%s: average = %d\n", __func__, avg[channel]);
return avg[channel];
}
bool should_calibrate_only_active_area(const Genesys_Device& dev,
const Genesys_Settings& settings)
{
if (settings.scan_method == ScanMethod::TRANSPARENCY ||
settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
{
if (dev.model->model_id == ModelId::CANON_4400F && settings.xres >= 4800) {
return true;
}
if (dev.model->model_id == ModelId::CANON_8600F && settings.xres == 4800) {
return true;
}
}
return false;
}
float get_model_x_offset_ta(const Genesys_Device& dev, const Genesys_Settings& settings)
{
if (dev.model->model_id == ModelId::CANON_8600F && settings.xres == 4800) {
return 85.0f;
}
if (dev.model->model_id == ModelId::CANON_4400F && settings.xres == 4800) {
return dev.model->x_offset_ta - 10.0;
}
return dev.model->x_offset_ta;
}
void scanner_offset_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
Genesys_Register_Set& regs)
{
DBG_HELPER(dbg);
if (dev.model->asic_type == AsicType::GL843 &&
dev.frontend.layout.type != FrontendType::WOLFSON)
{
return;
}
if (dev.model->asic_type == AsicType::GL845 ||
dev.model->asic_type == AsicType::GL846)
{
// no gain nor offset for AKM AFE
std::uint8_t reg04 = dev.interface->read_register(gl846::REG_0x04);
if ((reg04 & gl846::REG_0x04_FESET) == 0x02) {
return;
}
}
if (dev.model->asic_type == AsicType::GL847) {
// no gain nor offset for AKM AFE
std::uint8_t reg04 = dev.interface->read_register(gl847::REG_0x04);
if ((reg04 & gl847::REG_0x04_FESET) == 0x02) {
return;
}
}
if (dev.model->asic_type == AsicType::GL124) {
std::uint8_t reg0a = dev.interface->read_register(gl124::REG_0x0A);
if (((reg0a & gl124::REG_0x0A_SIFSEL) >> gl124::REG_0x0AS_SIFSEL) == 3) {
return;
}
}
unsigned target_pixels = dev.model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
unsigned start_pixel = 0;
unsigned black_pixels = (sensor.black_pixels * sensor.optical_res) / sensor.optical_res;
DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
unsigned channels = 3;
unsigned lines = 1;
unsigned resolution = sensor.optical_res;
const Genesys_Sensor* calib_sensor = &sensor;
if (dev.model->asic_type == AsicType::GL843) {
lines = 8;
// compute divider factor to compute final pixels number
resolution = sensor.get_register_hwdpi(dev.settings.xres);
unsigned factor = sensor.optical_res / resolution;
calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels,
dev.settings.scan_method);
target_pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH;
black_pixels = calib_sensor->black_pixels / factor;
if (should_calibrate_only_active_area(dev, dev.settings)) {
float offset = get_model_x_offset_ta(dev, dev.settings);
offset /= calib_sensor->get_ccd_size_divisor_for_dpi(resolution);
start_pixel = static_cast<int>((offset * resolution) / MM_PER_INCH);
float size = dev.model->x_size_ta;
size /= calib_sensor->get_ccd_size_divisor_for_dpi(resolution);
target_pixels = static_cast<int>((size * resolution) / MM_PER_INCH);
}
if (dev.model->model_id == ModelId::CANON_4400F &&
dev.settings.scan_method == ScanMethod::FLATBED)
{
return;
}
}
ScanFlag flags = ScanFlag::DISABLE_SHADING |
ScanFlag::DISABLE_GAMMA |
ScanFlag::SINGLE_LINE |
ScanFlag::IGNORE_STAGGER_OFFSET |
ScanFlag::IGNORE_COLOR_OFFSET;
if (dev.settings.scan_method == ScanMethod::TRANSPARENCY ||
dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
{
flags |= ScanFlag::USE_XPA;
}
ScanSession session;
session.params.xres = resolution;
session.params.yres = resolution;;
session.params.startx = start_pixel;
session.params.starty = 0;
session.params.pixels = target_pixels;
session.params.lines = lines;
session.params.depth = 8;
session.params.channels = channels;
session.params.scan_method = dev.settings.scan_method;
session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
session.params.color_filter = dev.model->asic_type == AsicType::GL843 ? ColorFilter::RED
: dev.settings.color_filter;
session.params.flags = flags;
compute_session(&dev, session, *calib_sensor);
dev.cmd_set->init_regs_for_scan_session(&dev, *calib_sensor, &regs, session);
unsigned output_pixels = session.output_pixels;
sanei_genesys_set_motor_power(regs, false);
int top[3], bottom[3];
int topavg[3], bottomavg[3], avg[3];
// init gain and offset
for (unsigned ch = 0; ch < 3; ch++)
{
bottom[ch] = 10;
dev.frontend.set_offset(ch, bottom[ch]);
dev.frontend.set_gain(ch, 0);
}
dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
// scan with bottom AFE settings
dev.interface->write_registers(regs);
DBG(DBG_info, "%s: starting first line reading\n", __func__);
dev.cmd_set->begin_scan(&dev, *calib_sensor, &regs, true);
if (is_testing_mode()) {
dev.interface->test_checkpoint("offset_calibration");
if (dev.model->asic_type == AsicType::GL843) {
scanner_stop_action_no_move(dev, regs);
}
return;
}
Image first_line;
if (dev.model->asic_type == AsicType::GL843) {
first_line = read_unshuffled_image_from_scanner(&dev, session,
session.output_total_bytes_raw);
scanner_stop_action_no_move(dev, regs);
} else {
first_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
}
if (DBG_LEVEL >= DBG_data) {
char fn[40];
std::snprintf(fn, 40, "gl843_bottom_offset_%03d_%03d_%03d.pnm",
bottom[0], bottom[1], bottom[2]);
sanei_genesys_write_pnm_file(fn, first_line);
}
for (unsigned ch = 0; ch < 3; ch++) {
bottomavg[ch] = dark_average_channel(first_line, black_pixels, ch);
DBG(DBG_io2, "%s: bottom avg %d=%d\n", __func__, ch, bottomavg[ch]);
}
// now top value
for (unsigned ch = 0; ch < 3; ch++) {
top[ch] = 255;
dev.frontend.set_offset(ch, top[ch]);
}
dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
// scan with top AFE values
dev.interface->write_registers(regs);
DBG(DBG_info, "%s: starting second line reading\n", __func__);
dev.cmd_set->begin_scan(&dev, *calib_sensor, &regs, true);
Image second_line;
if (dev.model->asic_type == AsicType::GL843) {
second_line = read_unshuffled_image_from_scanner(&dev, session,
session.output_total_bytes_raw);
scanner_stop_action_no_move(dev, regs);
} else {
second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
}
for (unsigned ch = 0; ch < 3; ch++){
topavg[ch] = dark_average_channel(second_line, black_pixels, ch);
DBG(DBG_io2, "%s: top avg %d=%d\n", __func__, ch, topavg[ch]);
}
unsigned pass = 0;
std::vector<std::uint8_t> debug_image;
std::size_t debug_image_lines = 0;
std::string debug_image_info;
// loop until acceptable level
while ((pass < 32) && ((top[0] - bottom[0] > 1) ||
(top[1] - bottom[1] > 1) ||
(top[2] - bottom[2] > 1)))
{
pass++;
for (unsigned ch = 0; ch < 3; ch++) {
if (top[ch] - bottom[ch] > 1) {
dev.frontend.set_offset(ch, (top[ch] + bottom[ch]) / 2);
}
}
dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
// scan with no move
dev.interface->write_registers(regs);
DBG(DBG_info, "%s: starting second line reading\n", __func__);
dev.cmd_set->begin_scan(&dev, *calib_sensor, &regs, true);
if (dev.model->asic_type == AsicType::GL843) {
second_line = read_unshuffled_image_from_scanner(&dev, session,
session.output_total_bytes_raw);
scanner_stop_action_no_move(dev, regs);
} else {
second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
}
if (DBG_LEVEL >= DBG_data) {
char title[100];
std::snprintf(title, 100, "lines: %d pixels_per_line: %d offsets[0..2]: %d %d %d\n",
lines, output_pixels,
dev.frontend.get_offset(0),
dev.frontend.get_offset(1),
dev.frontend.get_offset(2));
debug_image_info += title;
std::copy(second_line.get_row_ptr(0),
second_line.get_row_ptr(0) + second_line.get_row_bytes() * second_line.get_height(),
std::back_inserter(debug_image));
debug_image_lines += lines;
}
for (unsigned ch = 0; ch < 3; ch++) {
avg[ch] = dark_average_channel(second_line, black_pixels, ch);
DBG(DBG_info, "%s: avg[%d]=%d offset=%d\n", __func__, ch, avg[ch],
dev.frontend.get_offset(ch));
}
// compute new boundaries
for (unsigned ch = 0; ch < 3; ch++) {
if (topavg[ch] >= avg[ch]) {
topavg[ch] = avg[ch];
top[ch] = dev.frontend.get_offset(ch);
} else {
bottomavg[ch] = avg[ch];
bottom[ch] = dev.frontend.get_offset(ch);
}
}
}
if (DBG_LEVEL >= DBG_data) {
sanei_genesys_write_file("gl_offset_all_desc.txt",
reinterpret_cast<const std::uint8_t*>(debug_image_info.data()),
debug_image_info.size());
sanei_genesys_write_pnm_file("gl_offset_all.pnm",
debug_image.data(), session.params.depth, channels, output_pixels,
debug_image_lines);
}
DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
dev.frontend.get_offset(0),
dev.frontend.get_offset(1),
dev.frontend.get_offset(2));
}
/* With offset and coarse calibration we only want to get our input range into
a reasonable shape. the fine calibration of the upper and lower bounds will
be done with shading.
*/
void scanner_coarse_gain_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
Genesys_Register_Set& regs, unsigned dpi)
{
DBG_HELPER_ARGS(dbg, "dpi = %d", dpi);
if (dev.model->asic_type == AsicType::GL843 &&
dev.frontend.layout.type != FrontendType::WOLFSON)
{
return;
}
if (dev.model->asic_type == AsicType::GL845 ||
dev.model->asic_type == AsicType::GL846)
{
// no gain nor offset for AKM AFE
std::uint8_t reg04 = dev.interface->read_register(gl846::REG_0x04);
if ((reg04 & gl846::REG_0x04_FESET) == 0x02) {
return;
}
}
if (dev.model->asic_type == AsicType::GL847) {
// no gain nor offset for AKM AFE
std::uint8_t reg04 = dev.interface->read_register(gl847::REG_0x04);
if ((reg04 & gl847::REG_0x04_FESET) == 0x02) {
return;
}
}
if (dev.model->asic_type == AsicType::GL124) {
// no gain nor offset for TI AFE
std::uint8_t reg0a = dev.interface->read_register(gl124::REG_0x0A);
if (((reg0a & gl124::REG_0x0A_SIFSEL) >> gl124::REG_0x0AS_SIFSEL) == 3) {
return;
}
}
if (dev.model->asic_type == AsicType::GL841) {
// feed to white strip if needed
if (dev.model->y_offset_calib_white > 0) {
unsigned move = static_cast<unsigned>(
(dev.model->y_offset_calib_white * (dev.motor.base_ydpi)) / MM_PER_INCH);
scanner_move(dev, dev.model->default_method, move, Direction::FORWARD);
}
}
unsigned resolution = sensor.optical_res;
if (dev.model->asic_type == AsicType::GL841) {
resolution = sensor.get_register_hwdpi(dev.settings.xres);
}
if (dev.model->asic_type == AsicType::GL843) {
resolution = sensor.get_register_hwdpi(dpi);
}
// coarse gain calibration is always done in color mode
unsigned channels = 3;
float coeff = 1;
// Follow CKSEL
if (dev.model->sensor_id == SensorId::CCD_KVSS080 ||
dev.model->asic_type == AsicType::GL845 ||
dev.model->asic_type == AsicType::GL846 ||
dev.model->asic_type == AsicType::GL847 ||
dev.model->asic_type == AsicType::GL124)
{
if (dev.settings.xres < sensor.optical_res) {
coeff = 0.9f;
}
}
unsigned lines = 10;
if (dev.model->asic_type == AsicType::GL841) {
lines = 1;
}
const Genesys_Sensor* calib_sensor = &sensor;
if (dev.model->asic_type == AsicType::GL841 ||
dev.model->asic_type == AsicType::GL843)
{
calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels,
dev.settings.scan_method);
}
ScanFlag flags = ScanFlag::DISABLE_SHADING |
ScanFlag::DISABLE_GAMMA |
ScanFlag::SINGLE_LINE |
ScanFlag::IGNORE_STAGGER_OFFSET |
ScanFlag::IGNORE_COLOR_OFFSET;
if (dev.settings.scan_method == ScanMethod::TRANSPARENCY ||
dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
{
flags |= ScanFlag::USE_XPA;
}
ScanSession session;
session.params.xres = resolution;
session.params.yres = dev.model->asic_type == AsicType::GL841 ? dev.settings.yres : resolution;
session.params.startx = 0;
session.params.starty = 0;
session.params.pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH;
session.params.lines = lines;
session.params.depth = dev.model->asic_type == AsicType::GL841 ? 16 : 8;
session.params.channels = channels;
session.params.scan_method = dev.settings.scan_method;
session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
session.params.color_filter = dev.settings.color_filter;
session.params.flags = flags;
compute_session(&dev, session, *calib_sensor);
std::size_t pixels = session.output_pixels;
try {
dev.cmd_set->init_regs_for_scan_session(&dev, *calib_sensor, &regs, session);
} catch (...) {
if (dev.model->asic_type != AsicType::GL841) {
catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
}
throw;
}
if (dev.model->asic_type != AsicType::GL841) {
sanei_genesys_set_motor_power(regs, false);
}
dev.interface->write_registers(regs);
if (dev.model->asic_type != AsicType::GL841) {
dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
}
dev.cmd_set->begin_scan(&dev, *calib_sensor, &regs, true);
if (is_testing_mode()) {
dev.interface->test_checkpoint("coarse_gain_calibration");
if (dev.model->asic_type == AsicType::GL841) {
gl841::gl841_stop_action(&dev);
} else {
scanner_stop_action(dev);
}
dev.cmd_set->move_back_home(&dev, true);
return;
}
Image image;
if (dev.model->asic_type == AsicType::GL843) {
image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw);
} else if (dev.model->asic_type == AsicType::GL124) {
// BUG: we probably want to read whole image, not just first line
image = read_unshuffled_image_from_scanner(&dev, session, session.output_line_bytes);
} else {
image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
}
if (dev.model->asic_type == AsicType::GL843) {
scanner_stop_action_no_move(dev, regs);
}
if (DBG_LEVEL >= DBG_data) {
sanei_genesys_write_pnm_file("gl_coarse_gain.pnm", image);
}
for (unsigned ch = 0; ch < channels; ch++) {
float curr_output = 0;
float target_value = 0;
if (dev.model->asic_type == AsicType::GL843) {
std::vector<uint16_t> values;
// FIXME: start from the second line because the first line often has artifacts. Probably
// caused by unclean cleanup of previous scan
for (std::size_t x = pixels / 4; x < (pixels * 3 / 4); x++) {
values.push_back(image.get_raw_channel(x, 1, ch));
}
// pick target value at 95th percentile of all values. There may be a lot of black values
// in transparency scans for example
std::sort(values.begin(), values.end());
curr_output = static_cast<float>(values[unsigned((values.size() - 1) * 0.95)]);
target_value = calib_sensor->gain_white_ref * coeff;
} else if (dev.model->asic_type == AsicType::GL841) {
// FIXME: use the GL843 approach
unsigned max = 0;
for (std::size_t x = 0; x < image.get_width(); x++) {
auto value = image.get_raw_channel(x, 0, ch);
if (value > max) {
max = value;
}
}
curr_output = max;
target_value = 65535.0f;
} else {
// FIXME: use the GL843 approach
auto width = image.get_width();
std::uint64_t total = 0;
for (std::size_t x = width / 4; x < (width * 3 / 4); x++) {
total += image.get_raw_channel(x, 0, ch);
}
curr_output = total / (width / 2);
target_value = calib_sensor->gain_white_ref * coeff;
}
std::uint8_t out_gain = compute_frontend_gain(curr_output, target_value,
dev.frontend.layout.type);
dev.frontend.set_gain(ch, out_gain);
DBG(DBG_proc, "%s: channel %d, curr=%f, target=%f, out_gain:%d\n", __func__, ch,
curr_output, target_value, out_gain);
if (dev.model->asic_type == AsicType::GL841 &&
target_value / curr_output > 30)
{
DBG(DBG_error0, "****************************************\n");
DBG(DBG_error0, "* *\n");
DBG(DBG_error0, "* Extremely low Brightness detected. *\n");
DBG(DBG_error0, "* Check the scanning head is *\n");
DBG(DBG_error0, "* unlocked and moving. *\n");
DBG(DBG_error0, "* *\n");
DBG(DBG_error0, "****************************************\n");
throw SaneException(SANE_STATUS_JAMMED, "scanning head is locked");
}
}
if (dev.model->is_cis) {
std::uint8_t min_gain = std::min({dev.frontend.get_gain(0),
dev.frontend.get_gain(1),
dev.frontend.get_gain(2)});
dev.frontend.set_gain(0, min_gain);
dev.frontend.set_gain(1, min_gain);
dev.frontend.set_gain(2, min_gain);
}
DBG(DBG_info, "%s: gain=(%d,%d,%d)\n", __func__,
dev.frontend.get_gain(0),
dev.frontend.get_gain(1),
dev.frontend.get_gain(2));
if (dev.model->asic_type == AsicType::GL841) {
gl841::gl841_stop_action(&dev);
} else {
scanner_stop_action(dev);
}
dev.cmd_set->move_back_home(&dev, true);
}
void sanei_genesys_calculate_zmod(bool two_table,
uint32_t exposure_time,
@ -4534,6 +5123,7 @@ void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize)
genesys_init_sensor_tables();
genesys_init_frontend_tables();
genesys_init_gpo_tables();
genesys_init_memory_layout_tables();
genesys_init_motor_tables();
genesys_init_usb_device_tables();

Wyświetl plik

@ -1460,309 +1460,16 @@ SensorExposure CommandSetGl124::led_calibration(Genesys_Device* dev, const Genes
return { exp[0], exp[1], exp[2] };
}
/**
* average dark pixels of a 8 bits scan
*/
static int
dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
unsigned int channels, unsigned int black)
{
unsigned int i, j, k, average, count;
unsigned int avg[3];
uint8_t val;
/* computes average value on black margin */
for (k = 0; k < channels; k++)
{
avg[k] = 0;
count = 0;
for (i = 0; i < lines; i++)
{
for (j = 0; j < black; j++)
{
val = data[i * channels * pixels + j + k];
avg[k] += val;
count++;
}
}
if (count)
avg[k] /= count;
DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
}
average = 0;
for (i = 0; i < channels; i++)
average += avg[i];
average /= channels;
DBG(DBG_info, "%s: average = %d\n", __func__, average);
return average;
}
void CommandSetGl124::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
Genesys_Register_Set& regs) const
{
DBG_HELPER(dbg);
unsigned channels;
int pass = 0, avg;
int topavg, bottomavg, lines;
int top, bottom, black_pixels;
// no gain nor offset for TI AFE
uint8_t reg0a = dev->interface->read_register(REG_0x0A);
if (((reg0a & REG_0x0A_SIFSEL) >> REG_0x0AS_SIFSEL) == 3) {
return;
}
/* offset calibration is always done in color mode */
channels = 3;
lines=1;
unsigned pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
black_pixels = (sensor.black_pixels * sensor.optical_res) / sensor.optical_res;
DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
ScanSession session;
session.params.xres = sensor.optical_res;
session.params.yres = sensor.optical_res;
session.params.startx = 0;
session.params.starty = 0;
session.params.pixels = pixels;
session.params.lines = lines;
session.params.depth = 8;
session.params.channels = channels;
session.params.scan_method = dev->settings.scan_method;
session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
session.params.color_filter = dev->settings.color_filter;
session.params.flags = ScanFlag::DISABLE_SHADING |
ScanFlag::DISABLE_GAMMA |
ScanFlag::SINGLE_LINE |
ScanFlag::IGNORE_STAGGER_OFFSET |
ScanFlag::IGNORE_COLOR_OFFSET;
compute_session(dev, session, sensor);
init_regs_for_scan_session(dev, sensor, &regs, session);
sanei_genesys_set_motor_power(regs, false);
/* init gain */
dev->frontend.set_gain(0, 0);
dev->frontend.set_gain(1, 0);
dev->frontend.set_gain(2, 0);
/* scan with no move */
bottom = 10;
dev->frontend.set_offset(0, bottom);
dev->frontend.set_offset(1, bottom);
dev->frontend.set_offset(2, bottom);
set_fe(dev, sensor, AFE_SET);
dev->interface->write_registers(regs);
DBG(DBG_info, "%s: starting first line reading\n", __func__);
begin_scan(dev, sensor, &regs, true);
if (is_testing_mode()) {
dev->interface->test_checkpoint("offset_calibration");
return;
}
auto first_line = read_unshuffled_image_from_scanner(dev, session, session.output_line_bytes);
if (DBG_LEVEL >= DBG_data)
{
char title[30];
std::snprintf(title, 30, "gl124_offset%03d.pnm", bottom);
sanei_genesys_write_pnm_file(title, first_line);
}
bottomavg = dark_average(first_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg);
/* now top value */
top = 255;
dev->frontend.set_offset(0, top);
dev->frontend.set_offset(1, top);
dev->frontend.set_offset(2, top);
set_fe(dev, sensor, AFE_SET);
dev->interface->write_registers(regs);
DBG(DBG_info, "%s: starting second line reading\n", __func__);
begin_scan(dev, sensor, &regs, true);
auto second_line = read_unshuffled_image_from_scanner(dev, session, session.output_line_bytes);
topavg = dark_average(second_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg);
/* loop until acceptable level */
while ((pass < 32) && (top - bottom > 1))
{
pass++;
/* settings for new scan */
dev->frontend.set_offset(0, (top + bottom) / 2);
dev->frontend.set_offset(1, (top + bottom) / 2);
dev->frontend.set_offset(2, (top + bottom) / 2);
// scan with no move
set_fe(dev, sensor, AFE_SET);
dev->interface->write_registers(regs);
DBG(DBG_info, "%s: starting second line reading\n", __func__);
begin_scan(dev, sensor, &regs, true);
second_line = read_unshuffled_image_from_scanner(dev, session, session.output_line_bytes);
if (DBG_LEVEL >= DBG_data) {
char title[30];
std::snprintf(title, 30, "gl124_offset%03d.pnm", dev->frontend.get_offset(1));
sanei_genesys_write_pnm_file(title, second_line);
}
avg = dark_average(second_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
/* compute new boundaries */
if (topavg == avg)
{
topavg = avg;
top = dev->frontend.get_offset(1);
}
else
{
bottomavg = avg;
bottom = dev->frontend.get_offset(1);
}
}
DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
dev->frontend.get_offset(0),
dev->frontend.get_offset(1),
dev->frontend.get_offset(2));
scanner_offset_calibration(*dev, sensor, regs);
}
/* alternative coarse gain calibration
this on uses the settings from offset_calibration and
uses only one scanline
*/
/*
with offset and coarse calibration we only want to get our input range into
a reasonable shape. the fine calibration of the upper and lower bounds will
be done with shading.
*/
void CommandSetGl124::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
Genesys_Register_Set& regs, int dpi) const
{
DBG_HELPER_ARGS(dbg, "dpi = %d", dpi);
float gain[3],coeff;
int code, lines;
// no gain nor offset for TI AFE
uint8_t reg0a = dev->interface->read_register(REG_0x0A);
if (((reg0a & REG_0x0A_SIFSEL) >> REG_0x0AS_SIFSEL) == 3) {
return;
}
/* coarse gain calibration is always done in color mode */
unsigned channels = 3;
if(dev->settings.xres<sensor.optical_res)
{
coeff = 0.9f;
} else {
coeff = 1.0f;
}
lines=10;
ScanSession session;
session.params.xres = sensor.optical_res;
session.params.yres = sensor.optical_res;
session.params.startx = 0;
session.params.starty = 0;
session.params.pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;;
session.params.lines = lines;
session.params.depth = 8;
session.params.channels = channels;
session.params.scan_method = dev->settings.scan_method;
session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
session.params.color_filter = dev->settings.color_filter;
session.params.flags = ScanFlag::DISABLE_SHADING |
ScanFlag::DISABLE_GAMMA |
ScanFlag::SINGLE_LINE |
ScanFlag::IGNORE_STAGGER_OFFSET |
ScanFlag::IGNORE_COLOR_OFFSET;
compute_session(dev, session, sensor);
try {
init_regs_for_scan_session(dev, sensor, &regs, session);
} catch (...) {
catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
throw;
}
sanei_genesys_set_motor_power(regs, false);
dev->interface->write_registers(regs);
std::vector<uint8_t> line(session.output_line_bytes);
set_fe(dev, sensor, AFE_SET);
begin_scan(dev, sensor, &regs, true);
if (is_testing_mode()) {
dev->interface->test_checkpoint("coarse_gain_calibration");
scanner_stop_action(*dev);
move_back_home(dev, true);
return;
}
// BUG: we probably want to read whole image, not just first line
auto image = read_unshuffled_image_from_scanner(dev, session, session.output_line_bytes);
if (DBG_LEVEL >= DBG_data) {
sanei_genesys_write_pnm_file("gl124_gain.pnm", image);
}
/* average value on each channel */
for (unsigned ch = 0; ch < channels; ch++) {
auto width = image.get_width();
std::uint64_t total = 0;
for (std::size_t x = width / 4; x < (width * 3 / 4); x++) {
total += image.get_raw_channel(x, 0, ch);
}
total /= width / 2;
gain[ch] = (static_cast<float>(sensor.gain_white_ref) * coeff) / total;
/* turn logical gain value into gain code, checking for overflow */
code = static_cast<int>(283 - 208 / gain[ch]);
code = clamp(code, 0, 255);
dev->frontend.set_gain(ch, code);
DBG(DBG_proc, "%s: channel %d, total=%d, gain = %f, setting:%d\n", __func__, ch,
static_cast<unsigned>(total),
gain[ch], dev->frontend.get_gain(ch));
}
if (dev->model->is_cis) {
uint8_t gain0 = dev->frontend.get_gain(0);
if (gain0 > dev->frontend.get_gain(1)) {
gain0 = dev->frontend.get_gain(1);
}
if (gain0 > dev->frontend.get_gain(2)) {
gain0 = dev->frontend.get_gain(2);
}
dev->frontend.set_gain(0, gain0);
dev->frontend.set_gain(1, gain0);
dev->frontend.set_gain(2, gain0);
}
if (channels == 1) {
dev->frontend.set_gain(0, dev->frontend.get_gain(1));
dev->frontend.set_gain(2, dev->frontend.get_gain(1));
}
scanner_stop_action(*dev);
move_back_home(dev, true);
scanner_coarse_gain_calibration(*dev, sensor, regs, dpi);
}
// wait for lamp warmup by scanning the same line until difference
@ -1841,64 +1548,8 @@ static void gl124_init_gpio(Genesys_Device* dev)
static void gl124_init_memory_layout(Genesys_Device* dev)
{
DBG_HELPER(dbg);
int idx = 0;
/* point to per model memory layout */
if (dev->model->model_id == ModelId::CANON_LIDE_110 ||
dev->model->model_id == ModelId::CANON_LIDE_120)
{
idx = 0;
}
else
{ /* canon LiDE 210 and 220 case */
idx = 1;
}
/* setup base address for shading data. */
/* values must be multiplied by 8192=0x4000 to give address on AHB */
/* R-Channel shading bank0 address setting for CIS */
dev->interface->write_register(0xd0, layouts[idx].rd0);
/* G-Channel shading bank0 address setting for CIS */
dev->interface->write_register(0xd1, layouts[idx].rd1);
/* B-Channel shading bank0 address setting for CIS */
dev->interface->write_register(0xd2, layouts[idx].rd2);
/* setup base address for scanned data. */
/* values must be multiplied by 1024*2=0x0800 to give address on AHB */
/* R-Channel ODD image buffer 0x0124->0x92000 */
/* size for each buffer is 0x16d*1k word */
dev->interface->write_register(0xe0, layouts[idx].re0);
dev->interface->write_register(0xe1, layouts[idx].re1);
/* R-Channel ODD image buffer end-address 0x0291->0x148800 => size=0xB6800*/
dev->interface->write_register(0xe2, layouts[idx].re2);
dev->interface->write_register(0xe3, layouts[idx].re3);
/* R-Channel EVEN image buffer 0x0292 */
dev->interface->write_register(0xe4, layouts[idx].re4);
dev->interface->write_register(0xe5, layouts[idx].re5);
/* R-Channel EVEN image buffer end-address 0x03ff*/
dev->interface->write_register(0xe6, layouts[idx].re6);
dev->interface->write_register(0xe7, layouts[idx].re7);
/* same for green, since CIS, same addresses */
dev->interface->write_register(0xe8, layouts[idx].re0);
dev->interface->write_register(0xe9, layouts[idx].re1);
dev->interface->write_register(0xea, layouts[idx].re2);
dev->interface->write_register(0xeb, layouts[idx].re3);
dev->interface->write_register(0xec, layouts[idx].re4);
dev->interface->write_register(0xed, layouts[idx].re5);
dev->interface->write_register(0xee, layouts[idx].re6);
dev->interface->write_register(0xef, layouts[idx].re7);
/* same for blue, since CIS, same addresses */
dev->interface->write_register(0xf0, layouts[idx].re0);
dev->interface->write_register(0xf1, layouts[idx].re1);
dev->interface->write_register(0xf2, layouts[idx].re2);
dev->interface->write_register(0xf3, layouts[idx].re3);
dev->interface->write_register(0xf4, layouts[idx].re4);
dev->interface->write_register(0xf5, layouts[idx].re5);
dev->interface->write_register(0xf6, layouts[idx].re6);
dev->interface->write_register(0xf7, layouts[idx].re7);
apply_reg_settings_to_device_write_only(*dev, dev->memory_layout.regs);
}
/**

Wyświetl plik

@ -80,35 +80,6 @@ static Gpio_layout gpios[]={
},
};
typedef struct
{
uint8_t rd0;
uint8_t rd1;
uint8_t rd2;
uint8_t re0;
uint8_t re1;
uint8_t re2;
uint8_t re3;
uint8_t re4;
uint8_t re5;
uint8_t re6;
uint8_t re7;
} Memory_layout;
static Memory_layout layouts[]={
/* LIDE 110, 120 */
{ /* 0xd0 0xd1 0xd2 */
0x0a, 0x15, 0x20,
/* 0xe0 0xe1 0xe2 0xe3 0xe4 0xe5 0xe6 0xe7 */
0x00, 0xac, 0x08, 0x55, 0x08, 0x56, 0x0f, 0xff
},
/* LIDE 210, 220 */
{
0x0a, 0x1f, 0x34,
0x01, 0x24, 0x08, 0x91, 0x08, 0x92, 0x0f, 0xff
}
};
static void gl124_send_slope_table(Genesys_Device* dev, int table_nr,
const std::vector<uint16_t>& slope_table, int steps);

Wyświetl plik

@ -104,44 +104,6 @@ static void gl646_stop_motor(Genesys_Device* dev)
dev->interface->write_register(0x0f, 0x00);
}
/**
* find the closest match in mode tables for the given resolution and scan mode.
* @param sensor id of the sensor
* @param required required resolution
* @param color true is color mode
* @return the closest resolution for the sensor and mode
*/
static unsigned get_closest_resolution(SensorId sensor_id, int required, unsigned channels)
{
unsigned best_res = 0;
unsigned best_diff = 9600;
for (const auto& sensor : *s_sensors) {
if (sensor_id != sensor.sensor_id)
continue;
// exit on perfect match
if (sensor.resolutions.matches(required) && sensor.matches_channel_count(channels)) {
DBG(DBG_info, "%s: match found for %d\n", __func__, required);
return required;
}
// computes distance and keep mode if it is closer than previous
if (sensor.matches_channel_count(channels)) {
for (auto res : sensor.resolutions.resolutions()) {
unsigned curr_diff = std::abs(static_cast<int>(res) - static_cast<int>(required));
if (curr_diff < best_diff) {
best_res = res;
best_diff = curr_diff;
}
}
}
}
DBG(DBG_info, "%s: closest match for %d is %d\n", __func__, required, best_res);
return best_res;
}
/**
* Returns the cksel values used by the required scan mode.
* @param sensor id of the sensor
@ -1820,7 +1782,6 @@ SensorExposure CommandSetGl646::led_calibration(Genesys_Device* dev, const Genes
int turn;
uint16_t expr, expg, expb;
Genesys_Settings settings;
SANE_Int resolution;
unsigned channels = dev->settings.get_channels();
@ -1833,15 +1794,14 @@ SensorExposure CommandSetGl646::led_calibration(Genesys_Device* dev, const Genes
{
settings.scan_mode = ScanColorMode::GRAY;
}
resolution = get_closest_resolution(dev->model->sensor_id, sensor.optical_res, channels);
/* offset calibration is always done in color mode */
// offset calibration is always done in color mode
settings.scan_method = dev->model->default_method;
settings.xres = resolution;
settings.yres = resolution;
settings.xres = sensor.optical_res;
settings.yres = sensor.optical_res;
settings.tl_x = 0;
settings.tl_y = 0;
settings.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
settings.pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
settings.requested_pixels = settings.pixels;
settings.lines = 1;
settings.depth = 16;
@ -1998,24 +1958,25 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor&
unsigned int channels;
int pass = 0;
SANE_Int resolution;
Genesys_Settings settings;
unsigned int x, y, adr, min;
unsigned int bottom, black_pixels;
channels = 3;
resolution = get_closest_resolution(dev->model->sensor_id, sensor.optical_res, channels);
const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3, ScanMethod::FLATBED);
black_pixels = (calib_sensor.black_pixels * resolution) / calib_sensor.optical_res;
// FIXME: maybe reuse `sensor`
const auto& calib_sensor = sanei_genesys_find_sensor(dev, sensor.optical_res, 3,
ScanMethod::FLATBED);
black_pixels = (calib_sensor.black_pixels * sensor.optical_res) / calib_sensor.optical_res;
DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
settings.scan_method = dev->model->default_method;
settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
settings.xres = resolution;
settings.yres = resolution;
settings.xres = sensor.optical_res;
settings.yres = sensor.optical_res;
settings.tl_x = 0;
settings.tl_y = 0;
settings.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
settings.pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
settings.requested_pixels = settings.pixels;
settings.lines = CALIBRATION_LINES;
settings.depth = 8;
@ -2097,7 +2058,6 @@ void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sens
DBG_HELPER(dbg);
(void) regs;
unsigned int channels;
int pass = 0, avg;
Genesys_Settings settings;
int topavg, bottomavg;
@ -2112,10 +2072,11 @@ void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sens
/* setup for a RGB scan, one full sensor's width line */
/* resolution is the one from the final scan */
channels = 3;
int resolution = get_closest_resolution(dev->model->sensor_id, dev->settings.xres, channels);
unsigned resolution = dev->settings.xres;
unsigned channels = 3;
const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3, ScanMethod::FLATBED);
const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
ScanMethod::FLATBED);
black_pixels = (calib_sensor.black_pixels * resolution) / calib_sensor.optical_res;
DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
@ -2243,7 +2204,7 @@ static void ad_fe_coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sen
(void) regs;
unsigned int i, channels, val;
unsigned int size, count, resolution, pass;
unsigned int size, count, pass;
float average;
Genesys_Settings settings;
char title[32];
@ -2251,18 +2212,16 @@ static void ad_fe_coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sen
/* setup for a RGB scan, one full sensor's width line */
/* resolution is the one from the final scan */
channels = 3;
resolution = get_closest_resolution(dev->model->sensor_id, dpi, channels);
const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3, ScanMethod::FLATBED);
const auto& calib_sensor = sanei_genesys_find_sensor(dev, dpi, 3, ScanMethod::FLATBED);
settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
settings.scan_method = dev->model->default_method;
settings.xres = resolution;
settings.yres = resolution;
settings.xres = dpi;
settings.yres = dpi;
settings.tl_x = 0;
settings.tl_y = 0;
settings.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
settings.pixels = dev->model->x_size_calib_mm * dpi / MM_PER_INCH;
settings.requested_pixels = settings.pixels;
settings.lines = CALIBRATION_LINES;
settings.depth = 8;
@ -2342,7 +2301,7 @@ void CommandSetGl646::coarse_gain_calibration(Genesys_Device* dev, const Genesys
(void) dpi;
unsigned int i, j, k, channels, val, maximum, idx;
unsigned int count, resolution, pass;
unsigned count, pass;
float average[3];
Genesys_Settings settings;
char title[32];
@ -2355,26 +2314,26 @@ void CommandSetGl646::coarse_gain_calibration(Genesys_Device* dev, const Genesys
/* resolution is the one from the final scan */
channels = 3;
/* we are searching a sensor resolution */
resolution = get_closest_resolution(dev->model->sensor_id, dev->settings.xres, channels);
const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
// BUG: the following comment is incorrect
// we are searching a sensor resolution */
const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->settings.xres, channels,
ScanMethod::FLATBED);
settings.scan_method = dev->settings.scan_method;
settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
settings.xres = resolution;
settings.yres = resolution;
settings.xres = dev->settings.xres;
settings.yres = dev->settings.xres;
settings.tl_y = 0;
if (settings.scan_method == ScanMethod::FLATBED)
{
settings.tl_x = 0;
settings.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
settings.pixels = dev->model->x_size_calib_mm * dev->settings.xres / MM_PER_INCH;
}
else
{
settings.tl_x = dev->model->x_offset_ta;
settings.pixels = static_cast<unsigned>((dev->model->x_size_ta * resolution) / MM_PER_INCH);
settings.pixels = static_cast<unsigned>(
(dev->model->x_size_ta * dev->settings.xres) / MM_PER_INCH);
}
settings.requested_pixels = settings.pixels;
settings.lines = CALIBRATION_LINES;
@ -2502,12 +2461,11 @@ void CommandSetGl646::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Se
(void) sensor;
Genesys_Settings settings;
int resolution, lines;
int lines;
dev->frontend = dev->frontend_initial;
resolution = get_closest_resolution(dev->model->sensor_id, 300, 1);
unsigned resolution = 300;
const auto& local_sensor = sanei_genesys_find_sensor(dev, resolution, 1,
dev->settings.scan_method);
@ -2565,7 +2523,8 @@ static void gl646_repark_head(Genesys_Device* dev)
settings.scan_method = dev->model->default_method;
settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
settings.xres = get_closest_resolution(dev->model->sensor_id, 75, 1);
settings.xres = dev->model->get_resolution_settings(dev->model->default_method)
.get_min_resolution_y();
settings.yres = settings.xres;
settings.tl_x = 0;
settings.tl_y = 5;

Wyświetl plik

@ -1821,6 +1821,8 @@ void CommandSetGl841::set_powersaving(Genesys_Device* dev, int delay /* in minut
void gl841_stop_action(Genesys_Device* dev)
{
// FIXME: figure out a way to merge this to scanner_stop_action()
DBG_HELPER(dbg);
Genesys_Register_Set local_reg;
unsigned int loop;
@ -2949,146 +2951,7 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens
void CommandSetGl841::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
Genesys_Register_Set& regs, int dpi) const
{
DBG_HELPER_ARGS(dbg, "dpi=%d", dpi);
float gain[3];
int lines=1;
// feed to white strip if needed
if (dev->model->y_offset_calib_white > 0) {
unsigned move = static_cast<unsigned>(
(dev->model->y_offset_calib_white * (dev->motor.base_ydpi)) / MM_PER_INCH);
scanner_move(*dev, dev->model->default_method, move, Direction::FORWARD);
}
/* coarse gain calibration is allways done in color mode */
unsigned channels = 3;
unsigned resolution = sensor.get_register_hwdpi(dev->settings.xres);
const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
dev->settings.scan_method);
unsigned num_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
ScanSession session;
session.params.xres = resolution;
session.params.yres = dev->settings.yres;
session.params.startx = 0;
session.params.starty = 0;
session.params.pixels = num_pixels;
session.params.lines = lines;
session.params.depth = 16;
session.params.channels = channels;
session.params.scan_method = dev->settings.scan_method;
session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
session.params.color_filter = dev->settings.color_filter;
session.params.flags = ScanFlag::DISABLE_SHADING |
ScanFlag::DISABLE_GAMMA |
ScanFlag::SINGLE_LINE |
ScanFlag::IGNORE_STAGGER_OFFSET |
ScanFlag::IGNORE_COLOR_OFFSET;
compute_session(dev, session, calib_sensor);
init_regs_for_scan_session(dev, calib_sensor, &regs, session);
dev->interface->write_registers(regs);
std::vector<uint8_t> line(session.output_total_bytes);
dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
if (is_testing_mode()) {
dev->interface->test_checkpoint("coarse_gain_calibration");
gl841_stop_action(dev);
move_back_home(dev, true);
return;
}
auto image = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
if (DBG_LEVEL >= DBG_data)
sanei_genesys_write_pnm_file("gl841_gain.pnm", line.data(), 16, channels, num_pixels, lines);
/* average high level for each channel and compute gain
to reach the target code
we only use the central half of the CCD data */
for (unsigned ch = 0; ch < channels; ch++) {
unsigned max = 0;
for (std::size_t x = 0; x < image.get_width(); x++) {
auto value = image.get_raw_channel(x, 0, ch);
if (value > max) {
max = value;
}
}
gain[ch] = 65535.0f / max;
uint8_t out_gain = 0;
if (dev->model->adc_id == AdcId::CANON_LIDE_35 ||
dev->model->adc_id == AdcId::WOLFSON_XP300 ||
dev->model->adc_id == AdcId::WOLFSON_DSM600)
{
gain[ch] *= 0.69f; // seems we don't get the real maximum. empirically derived
if (283 - 208 / gain[ch] > 255) {
out_gain = 255;
} else if (283 - 208 / gain[ch] < 0) {
out_gain = 0;
} else {
out_gain = static_cast<std::uint8_t>(283 - 208 / gain[ch]);
}
} else if (dev->model->adc_id == AdcId::CANON_LIDE_80) {
out_gain = static_cast<std::uint8_t>(gain[ch] * 12);
}
dev->frontend.set_gain(ch, out_gain);
DBG(DBG_proc, "%s: channel %d, max=%d, gain = %f, setting:%d\n", __func__, ch, max,
gain[ch], out_gain);
}
for (unsigned j = 0; j < channels; j++) {
if (gain[j] > 30) {
DBG (DBG_error0, "**********************************************\n");
DBG (DBG_error0, "**********************************************\n");
DBG (DBG_error0, "**** ****\n");
DBG (DBG_error0, "**** Extremely low Brightness detected. ****\n");
DBG (DBG_error0, "**** Check the scanning head is ****\n");
DBG (DBG_error0, "**** unlocked and moving. ****\n");
DBG (DBG_error0, "**** ****\n");
DBG (DBG_error0, "**********************************************\n");
DBG (DBG_error0, "**********************************************\n");
throw SaneException(SANE_STATUS_JAMMED, "scanning head is locked");
}
}
if (dev->model->is_cis) {
uint8_t gain0 = dev->frontend.get_gain(0);
if (gain0 > dev->frontend.get_gain(1)) {
gain0 = dev->frontend.get_gain(1);
}
if (gain0 > dev->frontend.get_gain(2)) {
gain0 = dev->frontend.get_gain(2);
}
dev->frontend.set_gain(0, gain0);
dev->frontend.set_gain(1, gain0);
dev->frontend.set_gain(2, gain0);
}
if (channels == 1) {
dev->frontend.set_gain(0, dev->frontend.get_gain(1));
dev->frontend.set_gain(2, dev->frontend.get_gain(1));
}
DBG(DBG_info, "%s: gain=(%d,%d,%d)\n", __func__,
dev->frontend.get_gain(0),
dev->frontend.get_gain(1),
dev->frontend.get_gain(2));
gl841_stop_action(dev);
dev->cmd_set->move_back_home(dev, true);
scanner_coarse_gain_calibration(*dev, sensor, regs, dpi);
}
// wait for lamp warmup by scanning the same line until difference

Wyświetl plik

@ -755,39 +755,33 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev,
exposure, scan_yres, static_cast<unsigned>(motor_profile.step_type),
scan_lines, scan_dummy, feed_steps, static_cast<unsigned>(flags));
int use_fast_fed, coeff;
unsigned int lincnt;
unsigned feedl, dist;
GenesysRegister *r;
uint32_t z1, z2;
/* get step multiplier */
unsigned step_multiplier = gl843_get_step_multiplier (reg);
use_fast_fed = 0;
bool use_fast_fed = false;
if ((scan_yres >= 300 && feed_steps > 900) || (has_flag(flags, MotorFlag::FEED))) {
use_fast_fed = 1;
use_fast_fed = true;
}
lincnt=scan_lines;
reg->set24(REG_LINCNT, lincnt);
DBG(DBG_io, "%s: lincnt=%d\n", __func__, lincnt);
reg->set24(REG_LINCNT, scan_lines);
DBG(DBG_io, "%s: lincnt=%d\n", __func__, scan_lines);
/* compute register 02 value */
r = sanei_genesys_get_address(reg, REG_0x02);
r->value = 0x00;
sanei_genesys_set_motor_power(*reg, true);
reg->set8(REG_0x02, 0);
sanei_genesys_set_motor_power(*reg, true);
std::uint8_t reg02 = reg->get8(REG_0x02);
if (use_fast_fed) {
r->value |= REG_0x02_FASTFED;
reg02 |= REG_0x02_FASTFED;
} else {
r->value &= ~REG_0x02_FASTFED;
reg02 &= ~REG_0x02_FASTFED;
}
/* in case of automatic go home, move until home sensor */
// in case of automatic go home, move until home sensor
if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) {
r->value |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
}
/* disable backtracking */
@ -795,16 +789,15 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev,
||(scan_yres>=2400 && dev->model->model_id != ModelId::CANON_4400F)
||(scan_yres>=sensor.optical_res))
{
r->value |= REG_0x02_ACDCDIS;
reg02 |= REG_0x02_ACDCDIS;
}
if (has_flag(flags, MotorFlag::REVERSE)) {
r->value |= REG_0x02_MTRREV;
reg02 |= REG_0x02_MTRREV;
} else {
r->value &= ~REG_0x02_MTRREV;
reg02 &= ~REG_0x02_MTRREV;
}
reg->set8(REG_0x02, reg02);
// scan and backtracking slope table
auto scan_table = sanei_genesys_slope_table(dev->model->asic_type, scan_yres, exposure,
@ -847,11 +840,11 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev,
feedl <<= static_cast<unsigned>(motor_profile.step_type);
dist = scan_table.steps_count / step_multiplier;
if (use_fast_fed)
{
if (use_fast_fed) {
dist += (fast_table.steps_count / step_multiplier) * 2;
}
DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
/* get sure when don't insane value : XXX STEF XXX in this case we should
* fall back to single table move */
@ -864,12 +857,13 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev,
reg->set24(REG_FEEDL, feedl);
DBG(DBG_io, "%s: feedl=%d\n", __func__, feedl);
/* doesn't seem to matter that much */
// doesn't seem to matter that much
std::uint32_t z1, z2;
sanei_genesys_calculate_zmod(use_fast_fed,
exposure,
exposure,
scan_table.table,
scan_table.steps_count / step_multiplier,
feedl,
feedl,
scan_table.steps_count / step_multiplier,
&z1,
&z2);
@ -885,9 +879,7 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev,
reg->set24(REG_Z2MOD, z2);
DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
r = sanei_genesys_get_address(reg, REG_0x1E);
r->value &= 0xf0; /* 0 dummy lines */
r->value |= scan_dummy; /* dummy lines */
reg->set8_mask(REG_0x1E, scan_dummy, 0x0f);
reg->set8_mask(REG_0x67, static_cast<unsigned>(motor_profile.step_type) << REG_0x67S_STEPSEL, 0xc0);
reg->set8_mask(REG_0x68, static_cast<unsigned>(fast_profile->step_type) << REG_0x68S_FSTPSEL, 0xc0);
@ -895,32 +887,29 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev,
// steps for STOP table
reg->set8(REG_FMOVDEC, fast_table.steps_count / step_multiplier);
/* Vref XXX STEF XXX : optical divider or step type ? */
r = sanei_genesys_get_address (reg, 0x80);
if (!has_flag(dev->model->flags, ModelFlag::FULL_HWDPI_MODE))
{
r->value = 0x50;
coeff = sensor.get_hwdpi_divisor_for_dpi(scan_yres);
if (!has_flag(dev->model->flags, ModelFlag::FULL_HWDPI_MODE)) {
// FIXME: take this information from motor struct
std::uint8_t reg_vref = reg->get8(0x80);
reg_vref = 0x50;
unsigned coeff = sensor.get_hwdpi_divisor_for_dpi(scan_yres);
if (dev->model->motor_id == MotorId::KVSS080) {
if(coeff>=1)
{
r->value |= 0x05;
if (coeff >= 1) {
reg_vref |= 0x05;
}
} else {
switch (coeff) {
case 4:
reg_vref |= 0x0a;
break;
case 2:
reg_vref |= 0x0f;
break;
case 1:
reg_vref |= 0x0f;
break;
}
}
else {
switch(coeff)
{
case 4:
r->value |= 0x0a;
break;
case 2:
r->value |= 0x0f;
break;
case 1:
r->value |= 0x0f;
break;
}
}
reg->set8(REG_0x80, reg_vref);
}
}
@ -1170,18 +1159,6 @@ void CommandSetGl843::init_regs_for_scan_session(Genesys_Device* dev, const Gene
DBG(DBG_info, "%s: total bytes to send = %zu\n", __func__, dev->total_bytes_to_read);
}
static float get_model_x_offset_ta(const Genesys_Device& dev,
const Genesys_Settings& settings)
{
if (dev.model->model_id == ModelId::CANON_8600F && settings.xres == 4800) {
return 85.0f;
}
if (dev.model->model_id == ModelId::CANON_4400F && settings.xres == 4800) {
return dev.model->x_offset_ta - 10.0;
}
return dev.model->x_offset_ta;
}
ScanSession CommandSetGl843::calculate_scan_session(const Genesys_Device* dev,
const Genesys_Sensor& sensor,
const Genesys_Settings& settings) const
@ -1482,22 +1459,6 @@ void CommandSetGl843::move_back_home(Genesys_Device* dev, bool wait_until_home)
scanner_move_back_home(*dev, wait_until_home);
}
static bool should_calibrate_only_active_area(const Genesys_Device& dev,
const Genesys_Settings& settings)
{
if (settings.scan_method == ScanMethod::TRANSPARENCY ||
settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
{
if (dev.model->model_id == ModelId::CANON_4400F && settings.xres >= 4800) {
return true;
}
if (dev.model->model_id == ModelId::CANON_8600F && settings.xres == 4800) {
return true;
}
}
return false;
}
// init registers for shading calibration shading calibration is done at dpihw
void CommandSetGl843::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,
Genesys_Register_Set& regs) const
@ -1774,410 +1735,16 @@ SensorExposure CommandSetGl843::led_calibration(Genesys_Device* dev, const Genes
return calib_sensor.exposure;
}
/**
* average dark pixels of a 8 bits scan of a given channel
*/
static int dark_average_channel(const Image& image, unsigned black, unsigned channel)
{
auto channels = get_pixel_channels(image.get_format());
unsigned avg[3];
// computes average values on black margin
for (unsigned ch = 0; ch < channels; ch++) {
avg[ch] = 0;
unsigned count = 0;
// FIXME: start with the second line because the black pixels often have noise on the first
// line; the cause is probably incorrectly cleaned up previous scan
for (std::size_t y = 1; y < image.get_height(); y++) {
for (unsigned j = 0; j < black; j++) {
avg[ch] += image.get_raw_channel(j, y, ch);
count++;
}
}
if (count > 0) {
avg[ch] /= count;
}
DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, ch, avg[ch]);
}
DBG(DBG_info, "%s: average = %d\n", __func__, avg[channel]);
return avg[channel];
}
/** @brief calibrate AFE offset
* Iterate doing scans at target dpi until AFE offset if correct. One
* color line is scanned at a time. Scanning head doesn't move.
* @param dev device to calibrate
*/
void CommandSetGl843::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
Genesys_Register_Set& regs) const
{
DBG_HELPER(dbg);
if (dev->frontend.layout.type != FrontendType::WOLFSON)
return;
unsigned channels;
int pass, resolution, lines;
int topavg[3], bottomavg[3], avg[3];
int top[3], bottom[3], black_pixels, pixels, factor, dpihw;
/* offset calibration is always done in color mode */
channels = 3;
lines = 8;
// compute divider factor to compute final pixels number
dpihw = sensor.get_register_hwdpi(dev->settings.xres);
factor = sensor.optical_res / dpihw;
resolution = dpihw;
const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
dev->settings.scan_method);
int target_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
int start_pixel = 0;
black_pixels = calib_sensor.black_pixels / factor;
if (should_calibrate_only_active_area(*dev, dev->settings)) {
float offset = get_model_x_offset_ta(*dev, dev->settings);
offset /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution);
start_pixel = static_cast<int>((offset * resolution) / MM_PER_INCH);
float size = dev->model->x_size_ta;
size /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution);
target_pixels = static_cast<int>((size * resolution) / MM_PER_INCH);
}
if (dev->model->model_id == ModelId::CANON_4400F &&
dev->settings.scan_method == ScanMethod::FLATBED)
{
return;
}
ScanFlag flags = ScanFlag::DISABLE_SHADING |
ScanFlag::DISABLE_GAMMA |
ScanFlag::SINGLE_LINE |
ScanFlag::IGNORE_STAGGER_OFFSET |
ScanFlag::IGNORE_COLOR_OFFSET;
if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
{
flags |= ScanFlag::USE_XPA;
}
ScanSession session;
session.params.xres = resolution;
session.params.yres = resolution;
session.params.startx = start_pixel;
session.params.starty = 0;
session.params.pixels = target_pixels;
session.params.lines = lines;
session.params.depth = 8;
session.params.channels = channels;
session.params.scan_method = dev->settings.scan_method;
session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
session.params.color_filter = ColorFilter::RED;
session.params.flags = flags;
compute_session(dev, session, calib_sensor);
pixels = session.output_pixels;
DBG(DBG_io, "%s: dpihw =%d\n", __func__, dpihw);
DBG(DBG_io, "%s: factor =%d\n", __func__, factor);
DBG(DBG_io, "%s: resolution =%d\n", __func__, resolution);
DBG(DBG_io, "%s: pixels =%d\n", __func__, pixels);
DBG(DBG_io, "%s: black_pixels=%d\n", __func__, black_pixels);
init_regs_for_scan_session(dev, calib_sensor, &regs, session);
sanei_genesys_set_motor_power(regs, false);
// init gain and offset
for (unsigned ch = 0; ch < 3; ch++)
{
bottom[ch] = 10;
dev->frontend.set_offset(ch, bottom[ch]);
dev->frontend.set_gain(ch, 0);
}
dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);
// scan with bottom AFE settings
dev->interface->write_registers(regs);
DBG(DBG_info, "%s: starting first line reading\n", __func__);
dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
if (is_testing_mode()) {
dev->interface->test_checkpoint("offset_calibration");
scanner_stop_action_no_move(*dev, regs);
return;
}
auto first_line = read_unshuffled_image_from_scanner(dev, session,
session.output_total_bytes_raw);
scanner_stop_action_no_move(*dev, regs);
if (DBG_LEVEL >= DBG_data)
{
char fn[40];
std::snprintf(fn, 40, "gl843_bottom_offset_%03d_%03d_%03d.pnm",
bottom[0], bottom[1], bottom[2]);
sanei_genesys_write_pnm_file(fn, first_line);
}
for (unsigned ch = 0; ch < 3; ch++) {
bottomavg[ch] = dark_average_channel(first_line, black_pixels, ch);
DBG(DBG_io2, "%s: bottom avg %d=%d\n", __func__, ch, bottomavg[ch]);
}
// now top value
for (unsigned ch = 0; ch < 3; ch++) {
top[ch] = 255;
dev->frontend.set_offset(ch, top[ch]);
}
dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);
// scan with top AFE values
dev->interface->write_registers(regs);
DBG(DBG_info, "%s: starting second line reading\n", __func__);
dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
auto second_line = read_unshuffled_image_from_scanner(dev, session,
session.output_total_bytes_raw);
scanner_stop_action_no_move(*dev, regs);
for (unsigned ch = 0; ch < 3; ch++){
topavg[ch] = dark_average_channel(second_line, black_pixels, ch);
DBG(DBG_io2, "%s: top avg %d=%d\n", __func__, ch, topavg[ch]);
}
pass = 0;
std::vector<uint8_t> debug_image;
size_t debug_image_lines = 0;
std::string debug_image_info;
/* loop until acceptable level */
while ((pass < 32)
&& ((top[0] - bottom[0] > 1)
|| (top[1] - bottom[1] > 1) || (top[2] - bottom[2] > 1)))
{
pass++;
// settings for new scan
for (unsigned ch = 0; ch < 3; ch++) {
if (top[ch] - bottom[ch] > 1) {
dev->frontend.set_offset(ch, (top[ch] + bottom[ch]) / 2);
}
}
dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);
// scan with no move
dev->interface->write_registers(regs);
DBG(DBG_info, "%s: starting second line reading\n", __func__);
dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
second_line = read_unshuffled_image_from_scanner(dev, session,
session.output_total_bytes_raw);
scanner_stop_action_no_move(*dev, regs);
if (DBG_LEVEL >= DBG_data)
{
char title[100];
std::snprintf(title, 100, "lines: %d pixels_per_line: %d offsets[0..2]: %d %d %d\n",
lines, pixels,
dev->frontend.get_offset(0),
dev->frontend.get_offset(1),
dev->frontend.get_offset(2));
debug_image_info += title;
std::copy(second_line.get_row_ptr(0),
second_line.get_row_ptr(0) + second_line.get_row_bytes() * second_line.get_height(),
std::back_inserter(debug_image));
debug_image_lines += lines;
}
for (unsigned ch = 0; ch < 3; ch++) {
avg[ch] = dark_average_channel(second_line, black_pixels, ch);
DBG(DBG_info, "%s: avg[%d]=%d offset=%d\n", __func__, ch, avg[ch],
dev->frontend.get_offset(ch));
}
// compute new boundaries
for (unsigned ch = 0; ch < 3; ch++) {
if (topavg[ch] >= avg[ch]) {
topavg[ch] = avg[ch];
top[ch] = dev->frontend.get_offset(ch);
} else {
bottomavg[ch] = avg[ch];
bottom[ch] = dev->frontend.get_offset(ch);
}
}
}
if (DBG_LEVEL >= DBG_data)
{
sanei_genesys_write_file("gl843_offset_all_desc.txt",
reinterpret_cast<const std::uint8_t*>(debug_image_info.data()),
debug_image_info.size());
sanei_genesys_write_pnm_file("gl843_offset_all.pnm",
debug_image.data(), session.params.depth, channels, pixels,
debug_image_lines);
}
DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
dev->frontend.get_offset(0),
dev->frontend.get_offset(1),
dev->frontend.get_offset(2));
scanner_offset_calibration(*dev, sensor, regs);
}
/* alternative coarse gain calibration
this on uses the settings from offset_calibration and
uses only one scanline
*/
/*
with offset and coarse calibration we only want to get our input range into
a reasonable shape. the fine calibration of the upper and lower bounds will
be done with shading.
*/
void CommandSetGl843::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
Genesys_Register_Set& regs, int dpi) const
{
DBG_HELPER_ARGS(dbg, "dpi = %d", dpi);
int dpihw;
float coeff;
int lines;
int resolution;
if (dev->frontend.layout.type != FrontendType::WOLFSON)
return;
dpihw = sensor.get_register_hwdpi(dpi);
// coarse gain calibration is always done in color mode
unsigned channels = 3;
/* follow CKSEL */
if (dev->model->sensor_id == SensorId::CCD_KVSS080) {
if(dev->settings.xres<sensor.optical_res)
{
coeff = 0.9f;
}
else
{
coeff=1.0;
}
}
else
{
coeff=1.0;
}
resolution=dpihw;
lines=10;
ScanFlag flags = ScanFlag::DISABLE_SHADING |
ScanFlag::DISABLE_GAMMA |
ScanFlag::SINGLE_LINE |
ScanFlag::IGNORE_STAGGER_OFFSET |
ScanFlag::IGNORE_COLOR_OFFSET;
if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
{
flags |= ScanFlag::USE_XPA;
}
const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,
dev->settings.scan_method);
ScanSession session;
session.params.xres = resolution;
session.params.yres = resolution;
session.params.startx = 0;
session.params.starty = 0;
session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;
session.params.lines = lines;
session.params.depth = 8;
session.params.channels = channels;
session.params.scan_method = dev->settings.scan_method;
session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
session.params.color_filter = dev->settings.color_filter;
session.params.flags = flags;
compute_session(dev, session, calib_sensor);
std::size_t pixels = session.output_pixels;
try {
init_regs_for_scan_session(dev, calib_sensor, &regs, session);
} catch (...) {
catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
throw;
}
sanei_genesys_set_motor_power(regs, false);
dev->interface->write_registers(regs);
dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);
dev->cmd_set->begin_scan(dev, calib_sensor, &regs, true);
if (is_testing_mode()) {
dev->interface->test_checkpoint("coarse_gain_calibration");
scanner_stop_action(*dev);
move_back_home(dev, true);
return;
}
auto line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes_raw);
scanner_stop_action_no_move(*dev, regs);
if (DBG_LEVEL >= DBG_data) {
sanei_genesys_write_pnm_file("gl843_gain.pnm", line);
}
// average value on each channel
for (unsigned ch = 0; ch < channels; ch++) {
std::vector<uint16_t> values;
// FIXME: start from the second line because the first line often has artifacts. Probably
// caused by unclean cleanup of previous scan
for (std::size_t x = pixels / 4; x < (pixels * 3 / 4); x++) {
values.push_back(line.get_raw_channel(x, 1, ch));
}
// pick target value at 95th percentile of all values. There may be a lot of black values
// in transparency scans for example
std::sort(values.begin(), values.end());
uint16_t curr_output = values[unsigned((values.size() - 1) * 0.95)];
float target_value = calib_sensor.gain_white_ref * coeff;
int code = compute_frontend_gain(curr_output, target_value, dev->frontend.layout.type);
dev->frontend.set_gain(ch, code);
DBG(DBG_proc, "%s: channel %d, max=%d, target=%d, setting:%d\n", __func__, ch, curr_output,
static_cast<int>(target_value), code);
}
if (dev->model->is_cis) {
uint8_t gain0 = dev->frontend.get_gain(0);
if (gain0 > dev->frontend.get_gain(1)) {
gain0 = dev->frontend.get_gain(1);
}
if (gain0 > dev->frontend.get_gain(2)) {
gain0 = dev->frontend.get_gain(2);
}
dev->frontend.set_gain(0, gain0);
dev->frontend.set_gain(1, gain0);
dev->frontend.set_gain(2, gain0);
}
if (channels == 1) {
dev->frontend.set_gain(0, dev->frontend.get_gain(1));
dev->frontend.set_gain(2, dev->frontend.get_gain(1));
}
scanner_stop_action(*dev);
move_back_home(dev, true);
scanner_coarse_gain_calibration(*dev, sensor, regs, dpi);
}
// wait for lamp warmup by scanning the same line until difference

Wyświetl plik

@ -117,6 +117,9 @@ gl846_init_registers (Genesys_Device * dev)
dev->reg.init_reg(0x09, 0x00);
dev->reg.init_reg(0x0a, 0x00);
dev->reg.init_reg(0x0b, 0x8b);
if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) {
dev->reg.init_reg(0x0b, 0x2a);
}
dev->reg.init_reg(0x0c, 0x00);
dev->reg.init_reg(0x0d, 0x00);
dev->reg.init_reg(0x10, 0x00);
@ -325,51 +328,42 @@ static void gl846_init_motor_regs_scan(Genesys_Device* dev,
"scan_dummy=%d, feed_steps=%d, flags=%x",
scan_exposure_time, scan_yres, static_cast<unsigned>(motor_profile.step_type),
scan_lines, scan_dummy, feed_steps, static_cast<unsigned>(flags));
int use_fast_fed;
unsigned int fast_dpi;
unsigned int feedl, dist;
GenesysRegister *r;
uint32_t z1, z2;
unsigned int min_restep = 0x20;
uint8_t val;
unsigned int ccdlmt,tgtime;
unsigned step_multiplier = gl846_get_step_multiplier(reg);
use_fast_fed=0;
/* no fast fed since feed works well */
bool use_fast_fed = false;
if (dev->settings.yres == 4444 && feed_steps > 100 && !has_flag(flags, MotorFlag::FEED)) {
use_fast_fed = 1;
use_fast_fed = true;
}
DBG (DBG_io, "%s: use_fast_fed=%d\n", __func__, use_fast_fed);
reg->set24(REG_LINCNT, scan_lines);
DBG (DBG_io, "%s: lincnt=%d\n", __func__, scan_lines);
DBG(DBG_io, "%s: lincnt=%d\n", __func__, scan_lines);
/* compute register 02 value */
r = sanei_genesys_get_address(reg, REG_0x02);
r->value = 0x00;
sanei_genesys_set_motor_power(*reg, true);
reg->set8(REG_0x02, 0);
sanei_genesys_set_motor_power(*reg, true);
if (use_fast_fed)
r->value |= REG_0x02_FASTFED;
else
r->value &= ~REG_0x02_FASTFED;
std::uint8_t reg02 = reg->get8(REG_0x02);
if (use_fast_fed) {
reg02 |= REG_0x02_FASTFED;
} else {
reg02 &= ~REG_0x02_FASTFED;
}
if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) {
r->value |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
}
if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE) ||(scan_yres>=sensor.optical_res)) {
r->value |= REG_0x02_ACDCDIS;
reg02 |= REG_0x02_ACDCDIS;
}
if (has_flag(flags, MotorFlag::REVERSE)) {
r->value |= REG_0x02_MTRREV;
reg02 |= REG_0x02_MTRREV;
} else {
r->value &= ~REG_0x02_MTRREV;
reg02 &= ~REG_0x02_MTRREV;
}
reg->set8(REG_0x02, reg02);
/* scan and backtracking slope table */
// scan and backtracking slope table
auto scan_table = sanei_genesys_slope_table(dev->model->asic_type, scan_yres,
scan_exposure_time, dev->motor.base_ydpi,
step_multiplier, motor_profile);
@ -377,8 +371,8 @@ static void gl846_init_motor_regs_scan(Genesys_Device* dev,
gl846_send_slope_table(dev, SCAN_TABLE, scan_table.table, scan_table.steps_count);
gl846_send_slope_table(dev, BACKTRACK_TABLE, scan_table.table, scan_table.steps_count);
/* fast table */
fast_dpi=sanei_genesys_get_lowest_ydpi(dev);
// fast table
unsigned fast_dpi = sanei_genesys_get_lowest_ydpi(dev);
// BUG: looks like for fast moves we use inconsistent step type
StepType fast_step_type = motor_profile.step_type;
@ -397,30 +391,25 @@ static void gl846_init_motor_regs_scan(Genesys_Device* dev,
gl846_send_slope_table(dev, FAST_TABLE, fast_table.table, fast_table.steps_count);
gl846_send_slope_table(dev, HOME_TABLE, fast_table.table, fast_table.steps_count);
/* correct move distance by acceleration and deceleration amounts */
feedl=feed_steps;
if (use_fast_fed)
{
// correct move distance by acceleration and deceleration amounts
unsigned feedl = feed_steps;
unsigned dist = 0;
if (use_fast_fed) {
feedl <<= static_cast<unsigned>(fast_step_type);
dist = (scan_table.steps_count + 2 * fast_table.steps_count);
/* TODO read and decode REG_0xAB */
r = sanei_genesys_get_address (reg, 0x5e);
dist += (r->value & 31);
/* FEDCNT */
r = sanei_genesys_get_address(reg, REG_FEDCNT);
dist += r->value;
}
else
{
// TODO read and decode REG_0xAB
dist += (reg->get8(0x5e) & 31);
dist += reg->get8(REG_FEDCNT);
} else {
feedl <<= static_cast<unsigned>(motor_profile.step_type);
dist = scan_table.steps_count;
if (has_flag(flags, MotorFlag::FEED)) {
dist *= 2;
}
}
DBG (DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
/* check for overflow */
// check for overflow
if (dist < feedl) {
feedl -= dist;
} else {
@ -428,13 +417,10 @@ static void gl846_init_motor_regs_scan(Genesys_Device* dev,
}
reg->set24(REG_FEEDL, feedl);
DBG (DBG_io ,"%s: feedl=%d\n",__func__,feedl);
DBG(DBG_io, "%s: feedl=%d\n", __func__, feedl);
r = sanei_genesys_get_address(reg, REG_0x0C);
ccdlmt = (r->value & REG_0x0C_CCDLMT) + 1;
r = sanei_genesys_get_address(reg, REG_0x1C);
tgtime = 1 << (r->value & REG_0x1C_TGTIME);
unsigned ccdlmt = (reg->get8(REG_0x0C) & REG_0x0C_CCDLMT) + 1;
unsigned tgtime = 1 << (reg->get8(REG_0x1C) & REG_0x1C_TGTIME);
/* hi res motor speed GPIO */
/*
@ -468,28 +454,25 @@ static void gl846_init_motor_regs_scan(Genesys_Device* dev,
dev->interface->write_register(REG_0x6C, val);
*/
if(dev->model->gpio_id == GpioId::IMG101) {
if (dev->model->gpio_id == GpioId::IMG101) {
if (scan_yres == sensor.get_register_hwdpi(scan_yres)) {
val=1;
dev->interface->write_register(REG_0x7E, 1);
} else {
dev->interface->write_register(REG_0x7E, 0);
}
else
{
val=0;
}
dev->interface->write_register(REG_0x7E, val);
}
min_restep = (scan_table.steps_count / step_multiplier) / 2 - 1;
unsigned min_restep = (scan_table.steps_count / step_multiplier) / 2 - 1;
if (min_restep < 1) {
min_restep = 1;
}
r = sanei_genesys_get_address(reg, REG_FWDSTEP);
r->value = min_restep;
r = sanei_genesys_get_address(reg, REG_BWDSTEP);
r->value = min_restep;
reg->set8(REG_FWDSTEP, min_restep);
reg->set8(REG_BWDSTEP, min_restep);
std::uint32_t z1, z2;
sanei_genesys_calculate_zmod(use_fast_fed,
scan_exposure_time*ccdlmt*tgtime,
scan_exposure_time * ccdlmt * tgtime,
scan_table.table,
scan_table.steps_count,
feedl,
@ -497,21 +480,16 @@ static void gl846_init_motor_regs_scan(Genesys_Device* dev,
&z1,
&z2);
DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
reg->set24(REG_0x60, z1 | (static_cast<unsigned>(motor_profile.step_type) << (16 + REG_0x60S_STEPSEL)));
DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
reg->set24(REG_0x63, z2 | (static_cast<unsigned>(motor_profile.step_type) << (16 + REG_0x63S_FSTPSEL)));
r = sanei_genesys_get_address (reg, 0x1e);
r->value &= 0xf0; /* 0 dummy lines */
r->value |= scan_dummy; /* dummy lines */
reg->set8_mask(REG_0x1E, scan_dummy, 0x0f);
r = sanei_genesys_get_address(reg, REG_0x67);
r->value = 0x7f;
r = sanei_genesys_get_address(reg, REG_0x68);
r->value = 0x7f;
reg->set8(REG_0x67, 0x7f);
reg->set8(REG_0x68, 0x7f);
reg->set8(REG_STEPNO, scan_table.steps_count / step_multiplier);
reg->set8(REG_FASTNO, scan_table.steps_count / step_multiplier);
@ -1153,29 +1131,10 @@ SensorExposure CommandSetGl846::led_calibration(Genesys_Device* dev, const Genes
static void gl846_init_gpio(Genesys_Device* dev)
{
DBG_HELPER(dbg);
int idx=0;
/* search GPIO profile */
while (gpios[idx].gpio_id != GpioId::UNKNOWN && dev->model->gpio_id != gpios[idx].gpio_id) {
idx++;
}
if (gpios[idx].gpio_id == GpioId::UNKNOWN)
apply_registers_ordered(dev->gpo.regs, { 0x6e, 0x6f }, [&](const GenesysRegisterSetting& reg)
{
throw SaneException("failed to find GPIO profile for sensor_id=%d",
static_cast<unsigned>(dev->model->sensor_id));
}
dev->interface->write_register(REG_0xA7, gpios[idx].ra7);
dev->interface->write_register(REG_0xA6, gpios[idx].ra6);
dev->interface->write_register(REG_0x6B, gpios[idx].r6b);
dev->interface->write_register(REG_0x6C, gpios[idx].r6c);
dev->interface->write_register(REG_0x6D, gpios[idx].r6d);
dev->interface->write_register(REG_0x6E, gpios[idx].r6e);
dev->interface->write_register(REG_0x6F, gpios[idx].r6f);
dev->interface->write_register(REG_0xA8, gpios[idx].ra8);
dev->interface->write_register(REG_0xA9, gpios[idx].ra9);
dev->interface->write_register(reg.address, reg.value);
});
}
/**
@ -1184,32 +1143,11 @@ static void gl846_init_gpio(Genesys_Device* dev)
static void gl846_init_memory_layout(Genesys_Device* dev)
{
DBG_HELPER(dbg);
int idx = 0, i;
uint8_t val;
/* point to per model memory layout */
idx = 0;
while (layouts[idx].model != nullptr && strcmp(dev->model->name,layouts[idx].model)!=0) {
if(strcmp(dev->model->name,layouts[idx].model)!=0)
idx++;
}
if (layouts[idx].model == nullptr) {
throw SaneException("failed to find memory layout for model %s", dev->model->name);
}
// prevent further writings by bulk write register
dev->reg.remove_reg(0x0b);
/* CLKSET and DRAMSEL */
val = layouts[idx].dramsel;
dev->interface->write_register(REG_0x0B, val);
dev->reg.find_reg(0x0b).value = val;
/* prevent further writings by bulk write register */
dev->reg.remove_reg(0x0b);
/* setup base address for shading and scanned data. */
for(i=0;i<10;i++)
{
dev->interface->write_register(0xe0+i, layouts[idx].rx[i]);
}
apply_reg_settings_to_device_write_only(*dev, dev->memory_layout.regs);
}
/* *
@ -1243,18 +1181,11 @@ void CommandSetGl846::asic_boot(Genesys_Device* dev, bool cold) const
DBG(DBG_info, "%s: reported version for genesys chip is 0x%02x\n", __func__, val);
}
/* Set default values for registers */
gl846_init_registers (dev);
gl846_init_registers (dev);
// Write initial registers
dev->interface->write_registers(dev->reg);
/* Enable DRAM by setting a rising edge on bit 3 of reg 0x0b */
val = dev->reg.find_reg(0x0b).value & REG_0x0B_DRAMSEL;
val = (val | REG_0x0B_ENBDRAM);
dev->interface->write_register(REG_0x0B, val);
dev->reg.find_reg(0x0b).value = val;
/* CIS_LINE */
if (dev->model->is_cis)
{
@ -1322,292 +1253,16 @@ void CommandSetGl846::update_home_sensor_gpio(Genesys_Device& dev) const
dev.interface->write_register(REG_0x6C, val);
}
/**
* average dark pixels of a 8 bits scan
*/
static int
dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
unsigned int channels, unsigned int black)
{
unsigned int i, j, k, average, count;
unsigned int avg[3];
uint8_t val;
/* computes average value on black margin */
for (k = 0; k < channels; k++)
{
avg[k] = 0;
count = 0;
for (i = 0; i < lines; i++)
{
for (j = 0; j < black; j++)
{
val = data[i * channels * pixels + j + k];
avg[k] += val;
count++;
}
}
if (count)
avg[k] /= count;
DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
}
average = 0;
for (i = 0; i < channels; i++)
average += avg[i];
average /= channels;
DBG(DBG_info, "%s: average = %d\n", __func__, average);
return average;
}
void CommandSetGl846::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
Genesys_Register_Set& regs) const
{
DBG_HELPER(dbg);
unsigned channels;
int pass = 0, avg;
int topavg, bottomavg, lines;
int top, bottom, black_pixels;
// no gain nor offset for AKM AFE
uint8_t reg04 = dev->interface->read_register(REG_0x04);
if ((reg04 & REG_0x04_FESET) == 0x02) {
return;
}
/* offset calibration is always done in color mode */
channels = 3;
lines=1;
unsigned pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
black_pixels = (sensor.black_pixels * sensor.optical_res) / sensor.optical_res;
DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
ScanSession session;
session.params.xres = sensor.optical_res;
session.params.yres = sensor.optical_res;
session.params.startx = 0;
session.params.starty = 0;
session.params.pixels = pixels;
session.params.lines = lines;
session.params.depth = 8;
session.params.channels = channels;
session.params.scan_method = dev->settings.scan_method;
session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
session.params.color_filter = dev->settings.color_filter;
session.params.flags = ScanFlag::DISABLE_SHADING |
ScanFlag::DISABLE_GAMMA |
ScanFlag::SINGLE_LINE |
ScanFlag::IGNORE_STAGGER_OFFSET |
ScanFlag::IGNORE_COLOR_OFFSET;
compute_session(dev, session, sensor);
init_regs_for_scan_session(dev, sensor, &regs, session);
sanei_genesys_set_motor_power(regs, false);
/* init gain */
dev->frontend.set_gain(0, 0);
dev->frontend.set_gain(1, 0);
dev->frontend.set_gain(2, 0);
/* scan with no move */
bottom = 10;
dev->frontend.set_offset(0, bottom);
dev->frontend.set_offset(1, bottom);
dev->frontend.set_offset(2, bottom);
set_fe(dev, sensor, AFE_SET);
dev->interface->write_registers(regs);
DBG(DBG_info, "%s: starting first line reading\n", __func__);
begin_scan(dev, sensor, &regs, true);
if (is_testing_mode()) {
dev->interface->test_checkpoint("offset_calibration");
return;
}
auto first_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
if (DBG_LEVEL >= DBG_data)
{
char fn[30];
std::snprintf(fn, 30, "gl846_offset%03d.pnm", bottom);
sanei_genesys_write_pnm_file(fn, first_line);
}
bottomavg = dark_average(first_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg);
/* now top value */
top = 255;
dev->frontend.set_offset(0, top);
dev->frontend.set_offset(1, top);
dev->frontend.set_offset(2, top);
set_fe(dev, sensor, AFE_SET);
dev->interface->write_registers(regs);
DBG(DBG_info, "%s: starting second line reading\n", __func__);
begin_scan(dev, sensor, &regs, true);
auto second_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
topavg = dark_average(second_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg);
/* loop until acceptable level */
while ((pass < 32) && (top - bottom > 1))
{
pass++;
/* settings for new scan */
dev->frontend.set_offset(0, (top + bottom) / 2);
dev->frontend.set_offset(1, (top + bottom) / 2);
dev->frontend.set_offset(2, (top + bottom) / 2);
// scan with no move
set_fe(dev, sensor, AFE_SET);
dev->interface->write_registers(regs);
DBG(DBG_info, "%s: starting second line reading\n", __func__);
begin_scan(dev, sensor, &regs, true);
second_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
if (DBG_LEVEL >= DBG_data) {
char fn[30];
std::snprintf(fn, 30, "gl846_offset%03d.pnm", dev->frontend.get_offset(1));
sanei_genesys_write_pnm_file(fn, second_line);
}
avg = dark_average(second_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
/* compute new boundaries */
if (topavg == avg)
{
topavg = avg;
top = dev->frontend.get_offset(1);
}
else
{
bottomavg = avg;
bottom = dev->frontend.get_offset(1);
}
}
DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
dev->frontend.get_offset(0),
dev->frontend.get_offset(1),
dev->frontend.get_offset(2));
scanner_offset_calibration(*dev, sensor, regs);
}
void CommandSetGl846::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
Genesys_Register_Set& regs, int dpi) const
{
DBG_HELPER(dbg);
float gain[3],coeff;
int code, lines;
DBG(DBG_proc, "%s: dpi = %d\n", __func__, dpi);
// no gain nor offset for AKM AFE
uint8_t reg04 = dev->interface->read_register(REG_0x04);
if ((reg04 & REG_0x04_FESET) == 0x02) {
return;
}
/* coarse gain calibration is always done in color mode */
unsigned channels = 3;
/* follow CKSEL */
if(dev->settings.xres<sensor.optical_res)
{
coeff = 0.9f;
}
else
{
coeff=1.0;
}
lines=10;
ScanSession session;
session.params.xres = sensor.optical_res;
session.params.yres = sensor.optical_res;
session.params.startx = 0;
session.params.starty = 0;
session.params.pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
session.params.lines = lines;
session.params.depth = 8;
session.params.channels = channels;
session.params.scan_method = dev->settings.scan_method;
session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
session.params.color_filter = dev->settings.color_filter;
session.params.flags = ScanFlag::DISABLE_SHADING |
ScanFlag::DISABLE_GAMMA |
ScanFlag::SINGLE_LINE |
ScanFlag::IGNORE_STAGGER_OFFSET |
ScanFlag::IGNORE_COLOR_OFFSET;
compute_session(dev, session, sensor);
try {
init_regs_for_scan_session(dev, sensor, &regs, session);
} catch (...) {
catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
throw;
}
sanei_genesys_set_motor_power(regs, false);
dev->interface->write_registers(regs);
set_fe(dev, sensor, AFE_SET);
begin_scan(dev, sensor, &regs, true);
if (is_testing_mode()) {
dev->interface->test_checkpoint("coarse_gain_calibration");
scanner_stop_action(*dev);
move_back_home(dev, true);
return;
}
auto image = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
if (DBG_LEVEL >= DBG_data) {
sanei_genesys_write_pnm_file("gl846_gain.pnm", image);
}
/* average value on each channel */
for (unsigned ch = 0; ch < channels; ch++) {
auto width = image.get_width();
std::uint64_t total = 0;
for (std::size_t x = width / 4; x < (width * 3 / 4); x++) {
total += image.get_raw_channel(x, 0, ch);
}
total /= width / 2;
gain[ch] = (static_cast<float>(sensor.gain_white_ref) * coeff) / total;
/* turn logical gain value into gain code, checking for overflow */
code = static_cast<int>(283 - 208 / gain[ch]);
code = clamp(code, 0, 255);
dev->frontend.set_gain(ch, code);
DBG(DBG_proc, "%s: channel %d, total=%d, gain = %f, setting:%d\n", __func__, ch,
static_cast<unsigned>(total), gain[ch], dev->frontend.get_gain(ch));
}
if (dev->model->is_cis) {
uint8_t gain0 = dev->frontend.get_gain(0);
if (gain0 > dev->frontend.get_gain(1)) {
gain0 = dev->frontend.get_gain(1);
}
if (gain0 > dev->frontend.get_gain(2)) {
gain0 = dev->frontend.get_gain(2);
}
dev->frontend.set_gain(0, gain0);
dev->frontend.set_gain(1, gain0);
dev->frontend.set_gain(2, gain0);
}
scanner_stop_action(*dev);
move_back_home(dev, true);
scanner_coarse_gain_calibration(*dev, sensor, regs, dpi);
}
bool CommandSetGl846::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const

Wyświetl plik

@ -50,81 +50,6 @@
namespace genesys {
namespace gl846 {
typedef struct
{
GpioId gpio_id;
uint8_t r6b;
uint8_t r6c;
uint8_t r6d;
uint8_t r6e;
uint8_t r6f;
uint8_t ra6;
uint8_t ra7;
uint8_t ra8;
uint8_t ra9;
} Gpio_Profile;
static Gpio_Profile gpios[]={
{ GpioId::IMG101, 0x72, 0x1f, 0xa4, 0x13, 0xa7, 0x11, 0xff, 0x19, 0x05},
{ GpioId::PLUSTEK_OPTICBOOK_3800, 0x30, 0x01, 0x80, 0x2d, 0x80, 0x0c, 0x8f, 0x08, 0x04},
{ GpioId::UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
};
typedef struct
{
const char *model;
uint8_t dramsel;
/* shading data address */
uint8_t rd0;
uint8_t rd1;
uint8_t rd2;
/* scanned data address */
uint8_t rx[24];
} Memory_layout;
static Memory_layout layouts[]={
/* Image formula 101 */
{
"canon-image-formula-101",
0x8b,
0x0a, 0x1b, 0x00,
{ /* RED ODD START / RED ODD END */
0x00, 0xb0, 0x05, 0xe7, /* [0x00b0, 0x05e7] 1336*4000w */
/* RED EVEN START / RED EVEN END */
0x05, 0xe8, 0x0b, 0x1f, /* [0x05e8, 0x0b1f] */
/* GREEN ODD START / GREEN ODD END */
0x0b, 0x20, 0x10, 0x57, /* [0x0b20, 0x1057] */
/* GREEN EVEN START / GREEN EVEN END */
0x10, 0x58, 0x15, 0x8f, /* [0x1058, 0x158f] */
/* BLUE ODD START / BLUE ODD END */
0x15, 0x90, 0x1a, 0xc7, /* [0x1590,0x1ac7] */
/* BLUE EVEN START / BLUE EVEN END */
0x1a, 0xc8, 0x1f, 0xff /* [0x1ac8,0x1fff] */
}
},
/* OpticBook 3800 */
{
"plustek-opticbook-3800",
0x2a,
0x0a, 0x0a, 0x0a,
{ /* RED ODD START / RED ODD END */
0x00, 0x68, 0x03, 0x00,
/* RED EVEN START / RED EVEN END */
0x03, 0x01, 0x05, 0x99,
/* GREEN ODD START / GREEN ODD END */
0x05, 0x9a, 0x08, 0x32,
/* GREEN EVEN START / GREEN EVEN END */
0x08, 0x33, 0x0a, 0xcb,
/* BLUE ODD START / BLUE ODD END */
0x0a, 0xcc, 0x0d, 0x64,
/* BLUE EVEN START / BLUE EVEN END */
0x0d, 0x65, 0x0f, 0xfd
}
},
/* list terminating entry */
{ nullptr, 0, 0, 0, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} }
};
class CommandSetGl846 : public CommandSetCommon
{
public:

Wyświetl plik

@ -194,7 +194,7 @@ static constexpr RegMask REG_0x1D_CK1LOW = 0x20;
static constexpr RegMask REG_0x1D_TGSHLD = 0x1f;
static constexpr RegShift REG_0x1DS_TGSHLD = 0;
static constexpr RegAddr REG_0x1E = 0x1e;
static constexpr RegMask REG_0x1E_WDTIME = 0xf0;
static constexpr RegShift REG_0x1ES_WDTIME = 4;
static constexpr RegMask REG_0x1E_LINESEL = 0x0f;

Wyświetl plik

@ -352,64 +352,52 @@ static void gl847_init_motor_regs_scan(Genesys_Device* dev,
"scan_dummy=%d, feed_steps=%d, flags=%x",
scan_exposure_time, scan_yres, static_cast<unsigned>(motor_profile.step_type),
scan_lines, scan_dummy, feed_steps, static_cast<unsigned>(flags));
int use_fast_fed;
unsigned int fast_dpi;
unsigned int feedl, dist;
GenesysRegister *r;
uint32_t z1, z2;
unsigned int min_restep = 0x20;
uint8_t val;
unsigned int ccdlmt,tgtime;
unsigned step_multiplier = gl847_get_step_multiplier (reg);
use_fast_fed=0;
/* no fast fed since feed works well */
if (dev->settings.yres==4444 && feed_steps > 100 && (!has_flag(flags, MotorFlag::FEED)))
{
use_fast_fed=1;
bool use_fast_fed = false;
if (dev->settings.yres == 4444 && feed_steps > 100 && !has_flag(flags, MotorFlag::FEED)) {
use_fast_fed = true;
}
DBG(DBG_io, "%s: use_fast_fed=%d\n", __func__, use_fast_fed);
reg->set24(REG_LINCNT, scan_lines);
DBG(DBG_io, "%s: lincnt=%d\n", __func__, scan_lines);
DBG(DBG_io, "%s: lincnt=%d\n", __func__, scan_lines);
/* compute register 02 value */
r = sanei_genesys_get_address(reg, REG_0x02);
r->value = 0x00;
sanei_genesys_set_motor_power(*reg, true);
reg->set8(REG_0x02, 0);
sanei_genesys_set_motor_power(*reg, true);
std::uint8_t reg02 = reg->get8(REG_0x02);
if (use_fast_fed) {
r->value |= REG_0x02_FASTFED;
reg02 |= REG_0x02_FASTFED;
} else {
r->value &= ~REG_0x02_FASTFED;
reg02 &= ~REG_0x02_FASTFED;
}
if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) {
r->value |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;
}
if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE)
||(scan_yres>=sensor.optical_res))
{
r->value |= REG_0x02_ACDCDIS;
if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE) ||(scan_yres>=sensor.optical_res)) {
reg02 |= REG_0x02_ACDCDIS;
}
if (has_flag(flags, MotorFlag::REVERSE)) {
r->value |= REG_0x02_MTRREV;
reg02 |= REG_0x02_MTRREV;
} else {
r->value &= ~REG_0x02_MTRREV;
reg02 &= ~REG_0x02_MTRREV;
}
reg->set8(REG_0x02, reg02);
/* scan and backtracking slope table */
// scan and backtracking slope table
auto scan_table = sanei_genesys_slope_table(dev->model->asic_type, scan_yres,
scan_exposure_time, dev->motor.base_ydpi,
step_multiplier, motor_profile);
gl847_send_slope_table(dev, SCAN_TABLE, scan_table.table, scan_table.steps_count);
gl847_send_slope_table(dev, BACKTRACK_TABLE, scan_table.table, scan_table.steps_count);
/* fast table */
fast_dpi=sanei_genesys_get_lowest_ydpi(dev);
// fast table
unsigned fast_dpi = sanei_genesys_get_lowest_ydpi(dev);
// BUG: looks like for fast moves we use inconsistent step type
StepType fast_step_type = motor_profile.step_type;
if (static_cast<unsigned>(motor_profile.step_type) >= static_cast<unsigned>(StepType::QUARTER)) {
fast_step_type = StepType::QUARTER;
@ -426,30 +414,26 @@ static void gl847_init_motor_regs_scan(Genesys_Device* dev,
gl847_send_slope_table(dev, FAST_TABLE, fast_table.table, fast_table.steps_count);
gl847_send_slope_table(dev, HOME_TABLE, fast_table.table, fast_table.steps_count);
/* correct move distance by acceleration and deceleration amounts */
feedl=feed_steps;
if (use_fast_fed)
// correct move distance by acceleration and deceleration amounts
unsigned feedl = feed_steps;
unsigned dist = 0;
if (use_fast_fed)
{
feedl <<= static_cast<unsigned>(fast_step_type);
dist = (scan_table.steps_count + 2 * fast_table.steps_count);
/* TODO read and decode REG_0xAB */
r = sanei_genesys_get_address (reg, 0x5e);
dist += (r->value & 31);
/* FEDCNT */
r = sanei_genesys_get_address (reg, REG_FEDCNT);
dist += r->value;
}
else
{
// TODO read and decode REG_0xAB
dist += (reg->get8(0x5e) & 31);
dist += reg->get8(REG_FEDCNT);
} else {
feedl <<= static_cast<unsigned>(motor_profile.step_type);
dist = scan_table.steps_count;
if (has_flag(flags, MotorFlag::FEED)) {
dist *= 2;
}
}
DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);
/* check for overflow */
// check for overflow
if (dist < feedl) {
feedl -= dist;
} else {
@ -457,25 +441,21 @@ static void gl847_init_motor_regs_scan(Genesys_Device* dev,
}
reg->set24(REG_FEEDL, feedl);
DBG(DBG_io ,"%s: feedl=%d\n", __func__, feedl);
DBG(DBG_io ,"%s: feedl=%d\n", __func__, feedl);
r = sanei_genesys_get_address(reg, REG_0x0C);
ccdlmt = (r->value & REG_0x0C_CCDLMT) + 1;
r = sanei_genesys_get_address(reg, REG_0x1C);
tgtime = 1<<(r->value & REG_0x1C_TGTIME);
unsigned ccdlmt = (reg->get8(REG_0x0C) & REG_0x0C_CCDLMT) + 1;
unsigned tgtime = 1 << (reg->get8(REG_0x1C) & REG_0x1C_TGTIME);
// hi res motor speed GPIO
uint8_t effective = dev->interface->read_register(REG_0x6C);
// if quarter step, bipolar Vref2
std::uint8_t val = effective;
if (motor_profile.step_type == StepType::QUARTER) {
val = effective & ~REG_0x6C_GPIO13;
} else if (static_cast<unsigned>(motor_profile.step_type) > static_cast<unsigned>(StepType::QUARTER)) {
val = effective | REG_0x6C_GPIO13;
} else {
val = effective;
}
dev->interface->write_register(REG_0x6C, val);
@ -484,39 +464,34 @@ static void gl847_init_motor_regs_scan(Genesys_Device* dev,
val = effective | REG_0x6C_GPIO10;
dev->interface->write_register(REG_0x6C, val);
min_restep = scan_table.steps_count / (2 * step_multiplier) - 1;
unsigned min_restep = scan_table.steps_count / (2 * step_multiplier) - 1;
if (min_restep < 1) {
min_restep = 1;
}
r = sanei_genesys_get_address(reg, REG_FWDSTEP);
r->value = min_restep;
r = sanei_genesys_get_address(reg, REG_BWDSTEP);
r->value = min_restep;
reg->set8(REG_FWDSTEP, min_restep);
reg->set8(REG_BWDSTEP, min_restep);
std::uint32_t z1, z2;
sanei_genesys_calculate_zmod(use_fast_fed,
scan_exposure_time*ccdlmt*tgtime,
scan_exposure_time * ccdlmt * tgtime,
scan_table.table,
scan_table.steps_count,
feedl,
feedl,
min_restep * step_multiplier,
&z1,
&z2);
DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
DBG(DBG_info, "%s: z1 = %d\n", __func__, z1);
reg->set24(REG_0x60, z1 | (static_cast<unsigned>(motor_profile.step_type) << (16+REG_0x60S_STEPSEL)));
DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);
reg->set24(REG_0x63, z2 | (static_cast<unsigned>(motor_profile.step_type) << (16+REG_0x63S_FSTPSEL)));
r = sanei_genesys_get_address (reg, 0x1e);
r->value &= 0xf0; /* 0 dummy lines */
r->value |= scan_dummy; /* dummy lines */
reg->set8_mask(REG_0x1E, scan_dummy, 0x0f);
r = sanei_genesys_get_address(reg, REG_0x67);
r->value = REG_0x67_MTRPWM;
r = sanei_genesys_get_address(reg, REG_0x68);
r->value = REG_0x68_FASTPWM;
reg->set8(REG_0x67, REG_0x67_MTRPWM);
reg->set8(REG_0x68, REG_0x68_FASTPWM);
reg->set8(REG_STEPNO, scan_table.steps_count / step_multiplier);
reg->set8(REG_FASTNO, scan_table.steps_count / step_multiplier);
@ -1184,77 +1159,25 @@ static void gl847_init_gpio(Genesys_Device* dev)
static void gl847_init_memory_layout(Genesys_Device* dev)
{
DBG_HELPER(dbg);
int idx = 0;
uint8_t val;
/* point to per model memory layout */
idx = 0;
if (dev->model->model_id == ModelId::CANON_LIDE_100) {
idx = 0;
}
if (dev->model->model_id == ModelId::CANON_LIDE_200) {
idx = 1;
}
if (dev->model->model_id == ModelId::CANON_5600F) {
idx = 2;
}
if (dev->model->model_id == ModelId::CANON_LIDE_700F) {
idx = 3;
// TODO: move to initial register list
switch (dev->model->model_id) {
case ModelId::CANON_LIDE_100:
case ModelId::CANON_LIDE_200:
case ModelId::CANON_5600F:
dev->interface->write_register(REG_0x0B, 0x29);
break;
case ModelId::CANON_LIDE_700F:
dev->interface->write_register(REG_0x0B, 0x2a);
break;
default:
throw SaneException("Unknown device");
}
/* CLKSET nd DRAMSEL */
val = layouts[idx].dramsel;
dev->interface->write_register(REG_0x0B, val);
dev->reg.find_reg(0x0b).value = val;
// prevent further writings by bulk write register
dev->reg.remove_reg(0x0b);
/* prevent further writings by bulk write register */
dev->reg.remove_reg(0x0b);
/* setup base address for shading data. */
/* values must be multiplied by 8192=0x4000 to give address on AHB */
/* R-Channel shading bank0 address setting for CIS */
dev->interface->write_register(0xd0, layouts[idx].rd0);
/* G-Channel shading bank0 address setting for CIS */
dev->interface->write_register(0xd1, layouts[idx].rd1);
/* B-Channel shading bank0 address setting for CIS */
dev->interface->write_register(0xd2, layouts[idx].rd2);
/* setup base address for scanned data. */
/* values must be multiplied by 1024*2=0x0800 to give address on AHB */
/* R-Channel ODD image buffer 0x0124->0x92000 */
/* size for each buffer is 0x16d*1k word */
dev->interface->write_register(0xe0, layouts[idx].re0);
dev->interface->write_register(0xe1, layouts[idx].re1);
/* R-Channel ODD image buffer end-address 0x0291->0x148800 => size=0xB6800*/
dev->interface->write_register(0xe2, layouts[idx].re2);
dev->interface->write_register(0xe3, layouts[idx].re3);
/* R-Channel EVEN image buffer 0x0292 */
dev->interface->write_register(0xe4, layouts[idx].re4);
dev->interface->write_register(0xe5, layouts[idx].re5);
/* R-Channel EVEN image buffer end-address 0x03ff*/
dev->interface->write_register(0xe6, layouts[idx].re6);
dev->interface->write_register(0xe7, layouts[idx].re7);
/* same for green, since CIS, same addresses */
dev->interface->write_register(0xe8, layouts[idx].re0);
dev->interface->write_register(0xe9, layouts[idx].re1);
dev->interface->write_register(0xea, layouts[idx].re2);
dev->interface->write_register(0xeb, layouts[idx].re3);
dev->interface->write_register(0xec, layouts[idx].re4);
dev->interface->write_register(0xed, layouts[idx].re5);
dev->interface->write_register(0xee, layouts[idx].re6);
dev->interface->write_register(0xef, layouts[idx].re7);
/* same for blue, since CIS, same addresses */
dev->interface->write_register(0xf0, layouts[idx].re0);
dev->interface->write_register(0xf1, layouts[idx].re1);
dev->interface->write_register(0xf2, layouts[idx].re2);
dev->interface->write_register(0xf3, layouts[idx].re3);
dev->interface->write_register(0xf4, layouts[idx].re4);
dev->interface->write_register(0xf5, layouts[idx].re5);
dev->interface->write_register(0xf6, layouts[idx].re6);
dev->interface->write_register(0xf7, layouts[idx].re7);
apply_reg_settings_to_device_write_only(*dev, dev->memory_layout.regs);
}
/* *
@ -1363,294 +1286,16 @@ void CommandSetGl847::update_home_sensor_gpio(Genesys_Device& dev) const
}
}
/**
* average dark pixels of a 8 bits scan
*/
static int
dark_average (uint8_t * data, unsigned int pixels, unsigned int lines,
unsigned int channels, unsigned int black)
{
unsigned int i, j, k, average, count;
unsigned int avg[3];
uint8_t val;
/* computes average value on black margin */
for (k = 0; k < channels; k++)
{
avg[k] = 0;
count = 0;
for (i = 0; i < lines; i++)
{
for (j = 0; j < black; j++)
{
val = data[i * channels * pixels + j + k];
avg[k] += val;
count++;
}
}
if (count)
avg[k] /= count;
DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, k, avg[k]);
}
average = 0;
for (i = 0; i < channels; i++)
average += avg[i];
average /= channels;
DBG(DBG_info, "%s: average = %d\n", __func__, average);
return average;
}
void CommandSetGl847::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
Genesys_Register_Set& regs) const
{
DBG_HELPER(dbg);
unsigned channels;
int pass = 0, avg;
int topavg, bottomavg, lines;
int top, bottom, black_pixels;
// no gain nor offset for AKM AFE
uint8_t reg04 = dev->interface->read_register(REG_0x04);
if ((reg04 & REG_0x04_FESET) == 0x02) {
return;
}
/* offset calibration is always done in color mode */
channels = 3;
lines=1;
unsigned pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
black_pixels = (sensor.black_pixels * sensor.optical_res) / sensor.optical_res;
DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels);
ScanSession session;
session.params.xres = sensor.optical_res;
session.params.yres = sensor.optical_res;
session.params.startx = 0;
session.params.starty = 0;
session.params.pixels = pixels;
session.params.lines = lines;
session.params.depth = 8;
session.params.channels = channels;
session.params.scan_method = dev->settings.scan_method;
session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
session.params.color_filter = dev->settings.color_filter;
session.params.flags = ScanFlag::DISABLE_SHADING |
ScanFlag::DISABLE_GAMMA |
ScanFlag::SINGLE_LINE |
ScanFlag::IGNORE_STAGGER_OFFSET |
ScanFlag::IGNORE_COLOR_OFFSET;
compute_session(dev, session, sensor);
init_regs_for_scan_session(dev, sensor, &regs, session);
sanei_genesys_set_motor_power(regs, false);
/* init gain */
dev->frontend.set_gain(0, 0);
dev->frontend.set_gain(1, 0);
dev->frontend.set_gain(2, 0);
/* scan with no move */
bottom = 10;
dev->frontend.set_offset(0, bottom);
dev->frontend.set_offset(1, bottom);
dev->frontend.set_offset(2, bottom);
set_fe(dev, sensor, AFE_SET);
dev->interface->write_registers(regs);
DBG(DBG_info, "%s: starting first line reading\n", __func__);
begin_scan(dev, sensor, &regs, true);
if (is_testing_mode()) {
dev->interface->test_checkpoint("offset_calibration");
return;
}
auto first_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
if (DBG_LEVEL >= DBG_data) {
char fn[30];
std::snprintf(fn, 30, "gl847_offset%03d.pnm", bottom);
sanei_genesys_write_pnm_file(fn, first_line);
}
bottomavg = dark_average(first_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg);
/* now top value */
top = 255;
dev->frontend.set_offset(0, top);
dev->frontend.set_offset(1, top);
dev->frontend.set_offset(2, top);
set_fe(dev, sensor, AFE_SET);
dev->interface->write_registers(regs);
DBG(DBG_info, "%s: starting second line reading\n", __func__);
begin_scan(dev, sensor, &regs, true);
auto second_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
topavg = dark_average(second_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg);
/* loop until acceptable level */
while ((pass < 32) && (top - bottom > 1))
{
pass++;
/* settings for new scan */
dev->frontend.set_offset(0, (top + bottom) / 2);
dev->frontend.set_offset(1, (top + bottom) / 2);
dev->frontend.set_offset(2, (top + bottom) / 2);
// scan with no move
set_fe(dev, sensor, AFE_SET);
dev->interface->write_registers(regs);
DBG(DBG_info, "%s: starting second line reading\n", __func__);
begin_scan(dev, sensor, &regs, true);
second_line = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
if (DBG_LEVEL >= DBG_data) {
char fn[30];
std::snprintf(fn, 30, "gl847_offset%03d.pnm", dev->frontend.get_offset(1));
sanei_genesys_write_pnm_file(fn, second_line);
}
avg = dark_average(second_line.get_row_ptr(0), pixels, lines, channels, black_pixels);
DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));
/* compute new boundaries */
if (topavg == avg)
{
topavg = avg;
top = dev->frontend.get_offset(1);
}
else
{
bottomavg = avg;
bottom = dev->frontend.get_offset(1);
}
}
DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
dev->frontend.get_offset(0),
dev->frontend.get_offset(1),
dev->frontend.get_offset(2));
scanner_offset_calibration(*dev, sensor, regs);
}
void CommandSetGl847::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
Genesys_Register_Set& regs, int dpi) const
{
DBG_HELPER_ARGS(dbg, "dpi = %d", dpi);
float gain[3],coeff;
int code, lines;
// no gain nor offset for AKM AFE
uint8_t reg04 = dev->interface->read_register(REG_0x04);
if ((reg04 & REG_0x04_FESET) == 0x02) {
return;
}
/* coarse gain calibration is always done in color mode */
unsigned channels = 3;
/* follow CKSEL */
if(dev->settings.xres<sensor.optical_res)
{
coeff = 0.9f;
}
else
{
coeff=1.0;
}
lines=10;
ScanSession session;
session.params.xres = sensor.optical_res;
session.params.yres = sensor.optical_res;
session.params.startx = 0;
session.params.starty = 0;
session.params.pixels = dev->model->x_size_calib_mm * sensor.optical_res / MM_PER_INCH;
session.params.lines = lines;
session.params.depth = 8;
session.params.channels = channels;
session.params.scan_method = dev->settings.scan_method;
session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
session.params.color_filter = dev->settings.color_filter;
session.params.flags = ScanFlag::DISABLE_SHADING |
ScanFlag::DISABLE_GAMMA |
ScanFlag::SINGLE_LINE |
ScanFlag::IGNORE_STAGGER_OFFSET |
ScanFlag::IGNORE_COLOR_OFFSET;
compute_session(dev, session, sensor);
try {
init_regs_for_scan_session(dev, sensor, &regs, session);
} catch (...) {
catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
throw;
}
sanei_genesys_set_motor_power(regs, false);
dev->interface->write_registers(regs);
set_fe(dev, sensor, AFE_SET);
begin_scan(dev, sensor, &regs, true);
if (is_testing_mode()) {
dev->interface->test_checkpoint("coarse_gain_calibration");
scanner_stop_action(*dev);
move_back_home(dev, true);
return;
}
auto image = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes);
if (DBG_LEVEL >= DBG_data) {
sanei_genesys_write_pnm_file("gl847_gain.pnm", image);
}
/* average value on each channel */
for (unsigned ch = 0; ch < channels; ch++) {
auto width = image.get_width();
std::uint64_t total = 0;
for (std::size_t x = width / 4; x < (width * 3 / 4); x++) {
total += image.get_raw_channel(x, 0, ch);
}
total /= width / 2;
gain[ch] = (static_cast<float>(sensor.gain_white_ref) * coeff) / total;
/* turn logical gain value into gain code, checking for overflow */
code = static_cast<int>(283 - 208 / gain[ch]);
code = clamp(code, 0, 255);
dev->frontend.set_gain(ch, code);
DBG(DBG_proc, "%s: channel %d, total=%d, gain = %f, setting:%d\n", __func__, ch,
static_cast<unsigned>(total), gain[ch], dev->frontend.get_gain(ch));
}
if (dev->model->is_cis) {
uint8_t gain0 = dev->frontend.get_gain(0);
if (gain0 > dev->frontend.get_gain(1)) {
gain0 = dev->frontend.get_gain(1);
}
if (gain0 > dev->frontend.get_gain(2)) {
gain0 = dev->frontend.get_gain(2);
}
dev->frontend.set_gain(0, gain0);
dev->frontend.set_gain(1, gain0);
dev->frontend.set_gain(2, gain0);
}
if (channels == 1) {
dev->frontend.set_gain(0, dev->frontend.get_gain(1));
dev->frontend.set_gain(2, dev->frontend.get_gain(1));
}
scanner_stop_action(*dev);
move_back_home(dev, true);
scanner_coarse_gain_calibration(*dev, sensor, regs, dpi);
}
bool CommandSetGl847::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const

Wyświetl plik

@ -70,49 +70,6 @@ static Gpio_Profile gpios[]={
{ GpioId::UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
};
typedef struct
{
uint8_t dramsel;
uint8_t rd0;
uint8_t rd1;
uint8_t rd2;
uint8_t re0;
uint8_t re1;
uint8_t re2;
uint8_t re3;
uint8_t re4;
uint8_t re5;
uint8_t re6;
uint8_t re7;
} Memory_layout;
static Memory_layout layouts[]={
/* LIDE 100 */
{
0x29,
0x0a, 0x15, 0x20,
0x00, 0xac, 0x02, 0x55, 0x02, 0x56, 0x03, 0xff
},
/* LIDE 200 */
{
0x29,
0x0a, 0x1f, 0x34,
0x01, 0x24, 0x02, 0x91, 0x02, 0x92, 0x03, 0xff
},
/* 5600F */
{
0x29,
0x0a, 0x1f, 0x34,
0x01, 0x24, 0x02, 0x91, 0x02, 0x92, 0x03, 0xff
},
/* LIDE 700F */
{
0x2a,
0x0a, 0x33, 0x5c,
0x02, 0x14, 0x09, 0x09, 0x09, 0x0a, 0x0f, 0xff
}
};
class CommandSetGl847 : public CommandSetCommon
{
public:

Wyświetl plik

@ -190,6 +190,7 @@ static constexpr RegMask REG_0x1D_CK1LOW = 0x20;
static constexpr RegMask REG_0x1D_TGSHLD = 0x1f;
static constexpr RegMask REG_0x1DS_TGSHLD = 0;
static constexpr RegAddr REG_0x1E = 0x1e;
static constexpr RegMask REG_0x1E_WDTIME = 0xf0;
static constexpr RegMask REG_0x1ES_WDTIME = 4;
static constexpr RegMask REG_0x1E_LINESEL = 0x0f;

Wyświetl plik

@ -1335,6 +1335,32 @@ std::uint8_t compute_frontend_gain_wolfson(float value, float target_value)
return clamp(code, 0, 255);
}
std::uint8_t compute_frontend_gain_lide_80(float value, float target_value)
{
int code = static_cast<int>((target_value / value) * 12);
return clamp(code, 0, 255);
}
std::uint8_t compute_frontend_gain_wolfson_gl841(float value, float target_value)
{
// this code path is similar to what generic wolfson code path uses and uses similar constants,
// but is likely incorrect.
float inv_gain = target_value / value;
inv_gain *= 0.69f;
int code = static_cast<int>(283 - 208 / inv_gain);
return clamp(code, 0, 255);
}
std::uint8_t compute_frontend_gain_wolfson_gl846_gl847_gl124(float value, float target_value)
{
// this code path is similar to what generic wolfson code path uses and uses similar constants,
// but is likely incorrect.
float inv_gain = target_value / value;
int code = static_cast<int>(283 - 208 / inv_gain);
return clamp(code, 0, 255);
}
std::uint8_t compute_frontend_gain_analog_devices(float value, float target_value)
{
/* The flow of data through the frontend ADC is as follows (see e.g. AD9826 datasheet)
@ -1359,13 +1385,22 @@ std::uint8_t compute_frontend_gain_analog_devices(float value, float target_valu
std::uint8_t compute_frontend_gain(float value, float target_value,
FrontendType frontend_type)
{
if (frontend_type == FrontendType::WOLFSON) {
return compute_frontend_gain_wolfson(value, target_value);
switch (frontend_type) {
case FrontendType::WOLFSON:
return compute_frontend_gain_wolfson(value, target_value);
case FrontendType::ANALOG_DEVICES:
return compute_frontend_gain_analog_devices(value, target_value);
case FrontendType::CANON_LIDE_80:
return compute_frontend_gain_lide_80(value, target_value);
case FrontendType::WOLFSON_GL841:
return compute_frontend_gain_wolfson_gl841(value, target_value);
case FrontendType::WOLFSON_GL846:
case FrontendType::WOLFSON_GL847:
case FrontendType::WOLFSON_GL124:
return compute_frontend_gain_wolfson_gl846_gl847_gl124(value, target_value);
default:
throw SaneException("Unknown frontend to compute gain for");
}
if (frontend_type == FrontendType::ANALOG_DEVICES) {
return compute_frontend_gain_analog_devices(value, target_value);
}
throw SaneException("Unknown frontend to compute gain for");
}
/** @brief initialize device

Wyświetl plik

@ -303,6 +303,17 @@ void scanner_move_back_home_ta(Genesys_Device& dev);
*/
void scanner_search_strip(Genesys_Device& dev, bool forward, bool black);
bool should_calibrate_only_active_area(const Genesys_Device& dev,
const Genesys_Settings& settings);
float get_model_x_offset_ta(const Genesys_Device& dev, const Genesys_Settings& settings);
void scanner_offset_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
Genesys_Register_Set& regs);
void scanner_coarse_gain_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
Genesys_Register_Set& regs, unsigned dpi);
void scanner_clear_scan_and_feed_counts(Genesys_Device& dev);
extern void sanei_genesys_write_file(const char* filename, const std::uint8_t* data,
@ -455,12 +466,14 @@ inline T clamp(const T& value, const T& lo, const T& hi)
extern StaticInit<std::vector<Genesys_Sensor>> s_sensors;
extern StaticInit<std::vector<Genesys_Frontend>> s_frontends;
extern StaticInit<std::vector<Genesys_Gpo>> s_gpo;
extern StaticInit<std::vector<MemoryLayout>> s_memory_layout;
extern StaticInit<std::vector<Genesys_Motor>> s_motors;
extern StaticInit<std::vector<Genesys_USB_Device_Entry>> s_usb_devices;
void genesys_init_sensor_tables();
void genesys_init_frontend_tables();
void genesys_init_gpo_tables();
void genesys_init_memory_layout_tables();
void genesys_init_motor_tables();
void genesys_init_usb_device_tables();
void verify_usb_device_tables();

Wyświetl plik

@ -49,6 +49,7 @@
#include <vector>
#include "enums.h"
#include "sensor.h"
#include "value_filter.h"
namespace genesys {
@ -151,9 +152,9 @@ struct MotorProfile
int motor_vref = -1;
// the resolutions this profile is good for
ResolutionFilter resolutions = ResolutionFilter::ANY;
ValueFilterAny<unsigned> resolutions = VALUE_FILTER_ANY;
// the scan method this profile is good for. If the list is empty, good for any method.
ScanMethodFilter scan_methods = ScanMethodFilter::ANY;
ValueFilterAny<ScanMethod> scan_methods = VALUE_FILTER_ANY;
unsigned max_exposure = 0; // 0 - any exposure
};

Wyświetl plik

@ -64,6 +64,11 @@ std::ostream& operator<<(std::ostream& out, const FrontendType& type)
case FrontendType::UNKNOWN: out << "UNKNOWN"; break;
case FrontendType::WOLFSON: out << "WOLFSON"; break;
case FrontendType::ANALOG_DEVICES: out << "ANALOG_DEVICES"; break;
case FrontendType::CANON_LIDE_80: out << "CANON_LIDE_80"; break;
case FrontendType::WOLFSON_GL841: out << "WOLFSON_GL841"; break;
case FrontendType::WOLFSON_GL846: out << "WOLFSON_GL846"; break;
case FrontendType::WOLFSON_GL847: out << "WOLFSON_GL847"; break;
case FrontendType::WOLFSON_GL124: out << "WOLFSON_GL124"; break;
default: out << "(unknown value)";
}
return out;
@ -112,26 +117,6 @@ std::ostream& operator<<(std::ostream& out, const SensorExposure& exposure)
return out;
}
std::ostream& operator<<(std::ostream& out, const ResolutionFilter& resolutions)
{
if (resolutions.matches_any()) {
out << "ANY";
return out;
}
out << format_vector_unsigned(4, resolutions.resolutions());
return out;
}
std::ostream& operator<<(std::ostream& out, const ScanMethodFilter& methods)
{
if (methods.matches_any()) {
out << "ANY";
return out;
}
out << format_vector_unsigned(4, methods.methods());
return out;
}
std::ostream& operator<<(std::ostream& out, const Genesys_Sensor& sensor)
{
out << "Genesys_Sensor{\n"

Wyświetl plik

@ -47,6 +47,7 @@
#include "enums.h"
#include "register.h"
#include "serialize.h"
#include "value_filter.h"
#include <array>
#include <functional>
@ -114,9 +115,14 @@ std::ostream& operator<<(std::ostream& out, const StaggerConfig& config);
enum class FrontendType : unsigned
{
UNKNOWN,
UNKNOWN = 0,
WOLFSON,
ANALOG_DEVICES
ANALOG_DEVICES,
CANON_LIDE_80,
WOLFSON_GL841, // old code path, likely wrong calculation
WOLFSON_GL846, // old code path, likely wrong calculation
WOLFSON_GL847, // old code path, likely wrong calculation
WOLFSON_GL124, // old code path, likely wrong calculation
};
inline void serialize(std::istream& str, FrontendType& x)
@ -242,94 +248,6 @@ struct SensorExposure {
std::ostream& operator<<(std::ostream& out, const SensorExposure& exposure);
class ResolutionFilter
{
public:
struct Any {};
static constexpr Any ANY{};
ResolutionFilter() : matches_any_{false} {}
ResolutionFilter(Any) : matches_any_{true} {}
ResolutionFilter(std::initializer_list<unsigned> resolutions) :
matches_any_{false},
resolutions_{resolutions}
{}
bool matches(unsigned resolution) const
{
if (matches_any_)
return true;
auto it = std::find(resolutions_.begin(), resolutions_.end(), resolution);
return it != resolutions_.end();
}
bool operator==(const ResolutionFilter& other) const
{
return matches_any_ == other.matches_any_ && resolutions_ == other.resolutions_;
}
bool matches_any() const { return matches_any_; }
const std::vector<unsigned>& resolutions() const { return resolutions_; }
private:
bool matches_any_ = false;
std::vector<unsigned> resolutions_;
template<class Stream>
friend void serialize(Stream& str, ResolutionFilter& x);
};
std::ostream& operator<<(std::ostream& out, const ResolutionFilter& resolutions);
template<class Stream>
void serialize(Stream& str, ResolutionFilter& x)
{
serialize(str, x.matches_any_);
serialize_newline(str);
serialize(str, x.resolutions_);
}
class ScanMethodFilter
{
public:
struct Any {};
static constexpr Any ANY{};
ScanMethodFilter() : matches_any_{false} {}
ScanMethodFilter(Any) : matches_any_{true} {}
ScanMethodFilter(std::initializer_list<ScanMethod> methods) :
matches_any_{false},
methods_{methods}
{}
bool matches(ScanMethod method) const
{
if (matches_any_)
return true;
auto it = std::find(methods_.begin(), methods_.end(), method);
return it != methods_.end();
}
bool operator==(const ScanMethodFilter& other) const
{
return matches_any_ == other.matches_any_ && methods_ == other.methods_;
}
bool matches_any() const { return matches_any_; }
const std::vector<ScanMethod>& methods() const { return methods_; }
private:
bool matches_any_ = false;
std::vector<ScanMethod> methods_;
template<class Stream>
friend void serialize(Stream& str, ResolutionFilter& x);
};
std::ostream& operator<<(std::ostream& out, const ScanMethodFilter& methods);
struct Genesys_Sensor {
Genesys_Sensor() = default;
@ -343,7 +261,7 @@ struct Genesys_Sensor {
unsigned optical_res = 0;
// the resolution list that the sensor is usable at.
ResolutionFilter resolutions = ResolutionFilter::ANY;
ValueFilterAny<unsigned> resolutions = VALUE_FILTER_ANY;
// the channel list that the sensor is usable at
std::vector<unsigned> channels = { 1, 3 };

Wyświetl plik

@ -198,6 +198,7 @@ void genesys_init_frontend_tables()
fe = Genesys_Frontend();
fe.id = AdcId::CANON_LIDE_35;
fe.layout = wolfson_layout;
fe.layout.type = FrontendType::WOLFSON_GL841;
fe.regs = {
{ 0x00, 0x00 },
{ 0x01, 0x3d },
@ -242,6 +243,7 @@ void genesys_init_frontend_tables()
fe = Genesys_Frontend();
fe.id = AdcId::WOLFSON_XP300;
fe.layout = wolfson_layout;
fe.layout.type = FrontendType::WOLFSON_GL841;
fe.regs = {
{ 0x00, 0x00 },
{ 0x01, 0x35 },
@ -286,6 +288,7 @@ void genesys_init_frontend_tables()
fe = Genesys_Frontend();
fe.id = AdcId::WOLFSON_DSM600;
fe.layout = wolfson_layout;
fe.layout.type = FrontendType::WOLFSON_GL841;
fe.regs = {
{ 0x00, 0x00 },
{ 0x01, 0x35 },
@ -308,6 +311,7 @@ void genesys_init_frontend_tables()
fe = Genesys_Frontend();
fe.id = AdcId::CANON_LIDE_200;
fe.layout = wolfson_layout;
fe.layout.type = FrontendType::WOLFSON_GL847;
fe.regs = {
{ 0x00, 0x9d },
{ 0x01, 0x91 },
@ -330,6 +334,7 @@ void genesys_init_frontend_tables()
fe = Genesys_Frontend();
fe.id = AdcId::CANON_LIDE_700F;
fe.layout = wolfson_layout;
fe.layout.type = FrontendType::WOLFSON_GL847;
fe.regs = {
{ 0x00, 0x9d },
{ 0x01, 0x9e },
@ -396,6 +401,7 @@ void genesys_init_frontend_tables()
fe = Genesys_Frontend();
fe.id = AdcId::CANON_LIDE_110;
fe.layout = wolfson_layout;
fe.layout.type = FrontendType::WOLFSON_GL124;
fe.regs = {
{ 0x00, 0x80 },
{ 0x01, 0x8a },
@ -422,6 +428,7 @@ void genesys_init_frontend_tables()
fe = Genesys_Frontend();
fe.id = AdcId::CANON_LIDE_120;
fe.layout = wolfson_layout;
fe.layout.type = FrontendType::WOLFSON_GL124;
fe.regs = {
{ 0x00, 0x80 },
{ 0x01, 0xa3 },
@ -583,6 +590,7 @@ void genesys_init_frontend_tables()
fe = Genesys_Frontend();
fe.id = AdcId::IMG101;
fe.layout = wolfson_layout;
fe.layout.type = FrontendType::WOLFSON_GL846;
fe.regs = {
{ 0x00, 0x78 },
{ 0x01, 0xf0 },
@ -605,6 +613,7 @@ void genesys_init_frontend_tables()
fe = Genesys_Frontend();
fe.id = AdcId::PLUSTEK_OPTICBOOK_3800;
fe.layout = wolfson_layout;
fe.layout.type = FrontendType::WOLFSON_GL846;
fe.regs = {
{ 0x00, 0x78 },
{ 0x01, 0xf0 },
@ -631,6 +640,7 @@ void genesys_init_frontend_tables()
fe = Genesys_Frontend();
fe.id = AdcId::CANON_LIDE_80;
fe.layout = wolfson_layout;
fe.layout.type = FrontendType::CANON_LIDE_80;
fe.regs = {
{ 0x00, 0x70 },
{ 0x01, 0x16 },

Wyświetl plik

@ -382,10 +382,8 @@ void genesys_init_gpo_tables()
gpo = Genesys_Gpo();
gpo.id = GpioId::IMG101;
gpo.regs = {
{ 0x6c, 0x41 },
{ 0x6d, 0xa4 },
{ 0x6e, 0x13 },
{ 0x6f, 0xa7 },
{ 0x6b, 0x72 }, { 0x6c, 0x1f }, { 0x6d, 0xa4 }, { 0x6e, 0x13 }, { 0x6f, 0xa7 },
{ 0xa6, 0x11 }, { 0xa7, 0xff }, { 0xa8, 0x19 }, { 0xa9, 0x05 },
};
s_gpo->push_back(gpo);
@ -393,10 +391,8 @@ void genesys_init_gpo_tables()
gpo = Genesys_Gpo();
gpo.id = GpioId::PLUSTEK_OPTICBOOK_3800;
gpo.regs = {
{ 0x6c, 0x41 },
{ 0x6d, 0xa4 },
{ 0x6e, 0x13 },
{ 0x6f, 0xa7 },
{ 0x6b, 0x30 }, { 0x6c, 0x01 }, { 0x6d, 0x80 }, { 0x6e, 0x2d }, { 0x6f, 0x80 },
{ 0xa6, 0x0c }, { 0xa7, 0x8f }, { 0xa8, 0x08 }, { 0xa9, 0x04 },
};
s_gpo->push_back(gpo);

Wyświetl plik

@ -0,0 +1,135 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 2020 Povilas Kanapickas <povilas@radix.lt>
This file is part of the SANE package.
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA.
*/
#define DEBUG_DECLARE_ONLY
#include "low.h"
namespace genesys {
StaticInit<std::vector<MemoryLayout>> s_memory_layout;
void genesys_init_memory_layout_tables()
{
s_memory_layout.init();
MemoryLayout ml;
ml.models = { ModelId::CANON_IMAGE_FORMULA_101 };
// FIXME: this scanner does not set all required registers
ml.regs = {
{ 0xe0, 0x00 }, { 0xe1, 0xb0 }, { 0xe2, 0x05 }, { 0xe3, 0xe7 },
{ 0xe4, 0x05 }, { 0xe5, 0xe8 }, { 0xe6, 0x0b }, { 0xe7, 0x1f },
{ 0xe8, 0x0b }, { 0xe9, 0x20 },
};
s_memory_layout->push_back(ml);
ml = MemoryLayout();
ml.models = { ModelId::PLUSTEK_OPTICBOOK_3800 };
// FIXME: this scanner does not set all required registers
ml.regs = {
{ 0xe0, 0x00 }, { 0xe1, 0x68 }, { 0xe2, 0x03 }, { 0xe3, 0x00 },
{ 0xe4, 0x03 }, { 0xe5, 0x01 }, { 0xe6, 0x05 }, { 0xe7, 0x99 },
{ 0xe8, 0x05 }, { 0xe9, 0x9a },
};
s_memory_layout->push_back(ml);
/* On GL847 and GL124, the values of the base address for shading data must be multiplied by
8192=0x4000 to give address on AHB
On GL847 and GL124, the values of the base address for scanned data must be multiplied by
1024*2=0x0800 to give address on AHB
*/
ml = MemoryLayout();
ml.models = { ModelId::CANON_LIDE_100 };
ml.regs = {
{ 0xd0, 0x0a }, { 0xd1, 0x15 }, { 0xd2, 0x20 },
{ 0xe0, 0x00 }, { 0xe1, 0xac }, { 0xe2, 0x02 }, { 0xe3, 0x55 },
{ 0xe4, 0x02 }, { 0xe5, 0x56 }, { 0xe6, 0x03 }, { 0xe7, 0xff },
{ 0xe8, 0x00 }, { 0xe9, 0xac }, { 0xea, 0x02 }, { 0xeb, 0x55 },
{ 0xec, 0x02 }, { 0xed, 0x56 }, { 0xee, 0x03 }, { 0xef, 0xff },
{ 0xf0, 0x00 }, { 0xf1, 0xac }, { 0xf2, 0x02 }, { 0xf3, 0x55 },
{ 0xf4, 0x02 }, { 0xf5, 0x56 }, { 0xf6, 0x03 }, { 0xf7, 0xff },
};
s_memory_layout->push_back(ml);
ml = MemoryLayout();
// BUG: we shouldn't use LIDE_200 data for 5600F
ml.models = { ModelId::CANON_LIDE_200, ModelId::CANON_5600F };
ml.regs = {
{ 0xd0, 0x0a }, { 0xd1, 0x1f }, { 0xd2, 0x34 },
{ 0xe0, 0x01 }, { 0xe1, 0x24 }, { 0xe2, 0x02 }, { 0xe3, 0x91 },
{ 0xe4, 0x02 }, { 0xe5, 0x92 }, { 0xe6, 0x03 }, { 0xe7, 0xff },
{ 0xe8, 0x01 }, { 0xe9, 0x24 }, { 0xea, 0x02 }, { 0xeb, 0x91 },
{ 0xec, 0x02 }, { 0xed, 0x92 }, { 0xee, 0x03 }, { 0xef, 0xff },
{ 0xf0, 0x01 }, { 0xf1, 0x24 }, { 0xf2, 0x02 }, { 0xf3, 0x91 },
{ 0xf4, 0x02 }, { 0xf5, 0x92 }, { 0xf6, 0x03 }, { 0xf7, 0xff },
};
s_memory_layout->push_back(ml);
ml = MemoryLayout();
ml.models = { ModelId::CANON_LIDE_700F };
ml.regs = {
{ 0xd0, 0x0a }, { 0xd1, 0x33 }, { 0xd2, 0x5c },
{ 0xe0, 0x02 }, { 0xe1, 0x14 }, { 0xe2, 0x09 }, { 0xe3, 0x09 },
{ 0xe4, 0x09 }, { 0xe5, 0x0a }, { 0xe6, 0x0f }, { 0xe7, 0xff },
{ 0xe8, 0x02 }, { 0xe9, 0x14 }, { 0xea, 0x09 }, { 0xeb, 0x09 },
{ 0xec, 0x09 }, { 0xed, 0x0a }, { 0xee, 0x0f }, { 0xef, 0xff },
{ 0xf0, 0x02 }, { 0xf1, 0x14 }, { 0xf2, 0x09 }, { 0xf3, 0x09 },
{ 0xf4, 0x09 }, { 0xf5, 0x0a }, { 0xf6, 0x0f }, { 0xf7, 0xff },
};
s_memory_layout->push_back(ml);
ml = MemoryLayout();
ml.models = { ModelId::CANON_LIDE_110, ModelId::CANON_LIDE_120 };
ml.regs = {
{ 0xd0, 0x0a }, { 0xd1, 0x15 }, { 0xd2, 0x20 },
{ 0xe0, 0x00 }, { 0xe1, 0xac }, { 0xe2, 0x08 }, { 0xe3, 0x55 },
{ 0xe4, 0x08 }, { 0xe5, 0x56 }, { 0xe6, 0x0f }, { 0xe7, 0xff },
{ 0xe8, 0x00 }, { 0xe9, 0xac }, { 0xea, 0x08 }, { 0xeb, 0x55 },
{ 0xec, 0x08 }, { 0xed, 0x56 }, { 0xee, 0x0f }, { 0xef, 0xff },
{ 0xf0, 0x00 }, { 0xf1, 0xac }, { 0xf2, 0x08 }, { 0xf3, 0x55 },
{ 0xf4, 0x08 }, { 0xf5, 0x56 }, { 0xf6, 0x0f }, { 0xf7, 0xff },
};
s_memory_layout->push_back(ml);
ml = MemoryLayout();
ml.models = { ModelId::CANON_LIDE_210, ModelId::CANON_LIDE_220 };
ml.regs = {
{ 0xd0, 0x0a }, { 0xd1, 0x1f }, { 0xd2, 0x34 },
{ 0xe0, 0x01 }, { 0xe1, 0x24 }, { 0xe2, 0x08 }, { 0xe3, 0x91 },
{ 0xe4, 0x08 }, { 0xe5, 0x92 }, { 0xe6, 0x0f }, { 0xe7, 0xff },
{ 0xe8, 0x01 }, { 0xe9, 0x24 }, { 0xea, 0x08 }, { 0xeb, 0x91 },
{ 0xec, 0x08 }, { 0xed, 0x92 }, { 0xee, 0x0f }, { 0xef, 0xff },
{ 0xf0, 0x01 }, { 0xf1, 0x24 }, { 0xf2, 0x08 }, { 0xf3, 0x91 },
{ 0xf4, 0x08 }, { 0xf5, 0x92 }, { 0xf6, 0x0f }, { 0xf7, 0xff },
};
s_memory_layout->push_back(ml);
}
} // namespace genesys

Wyświetl plik

@ -270,7 +270,7 @@ void genesys_init_motor_tables()
profile.slope = MotorSlope::create_from_steps(20202 * 4, 333 * 4, 100);
profile.step_type = StepType::QUARTER;
profile.motor_vref = 0;
profile.resolutions = ResolutionFilter::ANY;
profile.resolutions = VALUE_FILTER_ANY;
profile.scan_methods = { ScanMethod::FLATBED };
motor.profiles.push_back(std::move(profile));
@ -278,7 +278,7 @@ void genesys_init_motor_tables()
profile.slope = MotorSlope::create_from_steps(65535 * 4, 333 * 4, 100);
profile.step_type = StepType::QUARTER;
profile.motor_vref = 2;
profile.resolutions = ResolutionFilter::ANY;
profile.resolutions = VALUE_FILTER_ANY;
profile.scan_methods = { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED };
motor.profiles.push_back(std::move(profile));
@ -286,8 +286,8 @@ void genesys_init_motor_tables()
profile.slope = MotorSlope::create_from_steps(65535 * 4, 333 * 4, 200);
profile.step_type = StepType::QUARTER;
profile.motor_vref = 2;
profile.resolutions = ResolutionFilter::ANY;
profile.scan_methods = ScanMethodFilter::ANY;
profile.resolutions = VALUE_FILTER_ANY;
profile.scan_methods = VALUE_FILTER_ANY;
motor.fast_profiles.push_back(std::move(profile));
s_motors->push_back(std::move(motor));

Wyświetl plik

@ -0,0 +1,140 @@
/* sane - Scanner Access Now Easy.
Copyright (C) 2020 Povilas Kanapickas <povilas@radix.lt>
This file is part of the SANE package.
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA.
*/
#ifndef BACKEND_GENESYS_VALUE_FILTER_H
#define BACKEND_GENESYS_VALUE_FILTER_H
#include <algorithm>
#include <initializer_list>
#include <iostream>
#include <vector>
namespace genesys {
struct AnyTag {};
constexpr AnyTag VALUE_FILTER_ANY{};
template<class T>
class ValueFilterAny
{
public:
ValueFilterAny() : matches_any_{false} {}
ValueFilterAny(AnyTag) : matches_any_{true} {}
ValueFilterAny(std::initializer_list<T> values) :
matches_any_{false},
values_{values}
{}
bool matches(T value) const
{
if (matches_any_)
return true;
auto it = std::find(values_.begin(), values_.end(), value);
return it != values_.end();
}
bool operator==(const ValueFilterAny& other) const
{
return matches_any_ == other.matches_any_ && values_ == other.values_;
}
bool matches_any() const { return matches_any_; }
const std::vector<T>& values() const { return values_; }
private:
bool matches_any_ = false;
std::vector<T> values_;
template<class Stream, class U>
friend void serialize(Stream& str, ValueFilterAny<U>& x);
};
template<class T>
std::ostream& operator<<(std::ostream& out, const ValueFilterAny<T>& values)
{
if (values.matches_any()) {
out << "ANY";
return out;
}
out << format_vector_indent_braced(4, "", values.values());
return out;
}
template<class Stream, class T>
void serialize(Stream& str, ValueFilterAny<T>& x)
{
serialize(str, x.matches_any_);
serialize_newline(str);
serialize(str, x.values_);
}
template<class T>
class ValueFilter
{
public:
ValueFilter() = default;
ValueFilter(std::initializer_list<T> values) :
values_{values}
{}
bool matches(T value) const
{
auto it = std::find(values_.begin(), values_.end(), value);
return it != values_.end();
}
bool operator==(const ValueFilter& other) const
{
return values_ == other.values_;
}
const std::vector<T>& values() const { return values_; }
private:
std::vector<T> values_;
template<class Stream, class U>
friend void serialize(Stream& str, ValueFilter<U>& x);
};
template<class T>
std::ostream& operator<<(std::ostream& out, const ValueFilter<T>& values)
{
if (values.values().empty()) {
out << "(none)";
return out;
}
out << format_vector_indent_braced(4, "", values.values());
return out;
}
template<class Stream, class T>
void serialize(Stream& str, ValueFilter<T>& x)
{
serialize_newline(str);
serialize(str, x.values_);
}
} // namespace genesys
#endif // BACKEND_GENESYS_VALUE_FILTER_H