diff options
| author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2020-08-24 18:44:51 +0200 | 
|---|---|---|
| committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2020-08-24 18:44:51 +0200 | 
| commit | ad38bc6ecb80ddeb562841b33258dd53659b1da6 (patch) | |
| tree | e02e9c3ff760554fd87f70df0e18b88594091a48 /backend/genesys | |
| parent | 9c23ed018d72eed2554f4f9cff1ae6e6bb0cd479 (diff) | |
New upstream version 1.0.31upstream/1.0.31
Diffstat (limited to 'backend/genesys')
73 files changed, 13089 insertions, 17033 deletions
diff --git a/backend/genesys/buffer.cpp b/backend/genesys/buffer.cpp deleted file mode 100644 index f17e361..0000000 --- a/backend/genesys/buffer.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* sane - Scanner Access Now Easy. - -   Copyright (C) 2019 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. - -   As a special exception, the authors of SANE give permission for -   additional uses of the libraries contained in this release of SANE. - -   The exception is that, if you link a SANE library with other files -   to produce an executable, this does not by itself cause the -   resulting executable to be covered by the GNU General Public -   License.  Your use of that executable is in no way restricted on -   account of linking the SANE library code into it. - -   This exception does not, however, invalidate any other reasons why -   the executable file might be covered by the GNU General Public -   License. - -   If you submit changes to SANE to the maintainers to be included in -   a subsequent release, you agree by submitting the changes that -   those changes may be distributed with this exception intact. - -   If you write modifications of your own for SANE, it is your choice -   whether to permit this exception to apply to your modifications. -   If you do not wish that, delete this exception notice. -*/ - -#include "buffer.h" -#include <cstring> -#include <stdexcept> - -namespace genesys { - -void Genesys_Buffer::alloc(std::size_t size) -{ -    buffer_.resize(size); -    avail_ = 0; -    pos_ = 0; -} - -void Genesys_Buffer::clear() -{ -    buffer_.clear(); -    avail_ = 0; -    pos_ = 0; -} - -void Genesys_Buffer::reset() -{ -    avail_ = 0; -    pos_ = 0; -} - -std::uint8_t* Genesys_Buffer::get_write_pos(std::size_t size) -{ -    if (avail_ + size > buffer_.size()) -        return nullptr; -    if (pos_ + avail_ + size > buffer_.size()) -    { -        std::memmove(buffer_.data(), buffer_.data() + pos_, avail_); -        pos_ = 0; -    } -    return buffer_.data() + pos_ + avail_; -} - -std::uint8_t* Genesys_Buffer::get_read_pos() -{ -    return buffer_.data() + pos_; -} - -void Genesys_Buffer::produce(std::size_t size) -{ -    if (size > buffer_.size() - avail_) -        throw std::runtime_error("buffer size exceeded"); -    avail_ += size; -} - -void Genesys_Buffer::consume(std::size_t size) -{ -    if (size > avail_) -        throw std::runtime_error("no more data in buffer"); -    avail_ -= size; -    pos_ += size; -} - -} // namespace genesys diff --git a/backend/genesys/buffer.h b/backend/genesys/buffer.h deleted file mode 100644 index e9c889b..0000000 --- a/backend/genesys/buffer.h +++ /dev/null @@ -1,89 +0,0 @@ -/* sane - Scanner Access Now Easy. - -   Copyright (C) 2019 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. - -   As a special exception, the authors of SANE give permission for -   additional uses of the libraries contained in this release of SANE. - -   The exception is that, if you link a SANE library with other files -   to produce an executable, this does not by itself cause the -   resulting executable to be covered by the GNU General Public -   License.  Your use of that executable is in no way restricted on -   account of linking the SANE library code into it. - -   This exception does not, however, invalidate any other reasons why -   the executable file might be covered by the GNU General Public -   License. - -   If you submit changes to SANE to the maintainers to be included in -   a subsequent release, you agree by submitting the changes that -   those changes may be distributed with this exception intact. - -   If you write modifications of your own for SANE, it is your choice -   whether to permit this exception to apply to your modifications. -   If you do not wish that, delete this exception notice. -*/ - -#ifndef BACKEND_GENESYS_BUFFER_H -#define BACKEND_GENESYS_BUFFER_H - -#include <vector> -#include <cstddef> -#include <cstdint> - -namespace genesys { - -/*  A FIFO buffer. Note, that this is _not_ a ringbuffer. -    if we need a block which does not fit at the end of our available data, -    we move the available data to the beginning. -*/ -struct Genesys_Buffer -{ -    Genesys_Buffer() = default; - -    std::size_t size() const { return buffer_.size(); } -    std::size_t avail() const { return avail_; } -    std::size_t pos() const { return pos_; } - -    // TODO: refactor code that uses this function to no longer use it -    void set_pos(std::size_t pos) { pos_ = pos; } - -    void alloc(std::size_t size); -    void clear(); - -    void reset(); - -    std::uint8_t* get_write_pos(std::size_t size); -    std::uint8_t* get_read_pos(); // TODO: mark as const - -    void produce(std::size_t size); -    void consume(std::size_t size); - -private: -    std::vector<std::uint8_t> buffer_; -    // current position in read buffer -    std::size_t pos_ = 0; -    // data bytes currently in buffer -    std::size_t avail_ = 0; -}; - -} // namespace genesys - -#endif // BACKEND_GENESYS_BUFFER_H diff --git a/backend/genesys/calibration.h b/backend/genesys/calibration.h index f14aaa3..81d94ea 100644 --- a/backend/genesys/calibration.h +++ b/backend/genesys/calibration.h @@ -63,8 +63,7 @@ struct Genesys_Calibration_Cache      Genesys_Frontend frontend;      Genesys_Sensor sensor; -    size_t calib_pixels = 0; -    size_t calib_channels = 0; +    ScanSession session;      size_t average_size = 0;      std::vector<std::uint16_t> white_average_data;      std::vector<std::uint16_t> dark_average_data; @@ -75,8 +74,7 @@ struct Genesys_Calibration_Cache              last_calibration == other.last_calibration &&              frontend == other.frontend &&              sensor == other.sensor && -            calib_pixels == other.calib_pixels && -            calib_channels == other.calib_channels && +            session == other.session &&              average_size == other.average_size &&              white_average_data == other.white_average_data &&              dark_average_data == other.dark_average_data; @@ -94,8 +92,7 @@ void serialize(Stream& str, Genesys_Calibration_Cache& x)      serialize_newline(str);      serialize(str, x.sensor);      serialize_newline(str); -    serialize(str, x.calib_pixels); -    serialize(str, x.calib_channels); +    serialize(str, x.session);      serialize(str, x.average_size);      serialize_newline(str);      serialize(str, x.white_average_data); diff --git a/backend/genesys/command_set.h b/backend/genesys/command_set.h index ab3a4b6..056cba8 100644 --- a/backend/genesys/command_set.h +++ b/backend/genesys/command_set.h @@ -67,14 +67,10 @@ public:      virtual void init(Genesys_Device* dev) const = 0;      virtual void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                      Genesys_Register_Set* regs, int* channels, -                                      int* total_size) const = 0; +                                      Genesys_Register_Set* regs) const = 0; -    virtual void init_regs_for_coarse_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                                  Genesys_Register_Set& regs) const = 0;      virtual void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,                                         Genesys_Register_Set& regs) const = 0; -    virtual void init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const = 0;      /** Set up registers for a scan. Similar to init_regs_for_scan except that the session is          already computed from the session @@ -98,7 +94,6 @@ public:       */      virtual void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const = 0; -    virtual void search_start_position(Genesys_Device* dev) const = 0;      virtual void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,                                      Genesys_Register_Set& regs) const = 0;      virtual void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, @@ -112,15 +107,10 @@ public:      // Updates hardware sensor information in Genesys_Scanner.val[].      virtual void update_hardware_sensors(struct Genesys_Scanner* s) const = 0; -    /** Whether the scanner needs to call update_home_sensor_gpio before reading the status of the -        home sensor. On some chipsets this is unreliable until update_home_sensor_gpio() is called. +    /** Needed on some chipsets before reading the status of the home sensor as the sensor may be +        controlled by additional GPIO registers.      */ -    virtual bool needs_update_home_sensor_gpio() const { return false; } - -    /** Needed on some chipsets before reading the status of the home sensor to make this operation -        reliable. -    */ -    virtual void update_home_sensor_gpio(Genesys_Device& dev) const { (void) dev; } +    virtual void update_home_sensor_gpio(Genesys_Device& dev) const = 0;      // functions for sheetfed scanners @@ -134,14 +124,6 @@ public:      /// eject document from scanner      virtual void eject_document(Genesys_Device* dev) const = 0; -    /** -     * search for an black or white area in forward or reverse -     * direction */ -    virtual void search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, -                              bool forward, bool black) const = 0; - -    /// move scanning head to transparency adapter -    virtual void move_to_ta(Genesys_Device* dev) const = 0;      /// write shading data calibration to ASIC      virtual void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, @@ -159,6 +141,16 @@ public:      /// cold boot init function      virtual void asic_boot(Genesys_Device* dev, bool cold) const = 0; + +    /// checks if specific scan head is at home position +    virtual bool is_head_home(Genesys_Device& dev, ScanHeadId scan_head) const = 0; + +    /// enables or disables XPA slider motor +    virtual void set_xpa_lamp_power(Genesys_Device& dev, bool set) const = 0; + +    /// enables or disables XPA slider motor +    virtual void set_motor_mode(Genesys_Device& dev, Genesys_Register_Set& regs, +                                MotorMode mode) const = 0;  };  } // namespace genesys diff --git a/backend/genesys/command_set_common.cpp b/backend/genesys/command_set_common.cpp new file mode 100644 index 0000000..381404e --- /dev/null +++ b/backend/genesys/command_set_common.cpp @@ -0,0 +1,248 @@ +/* sane - Scanner Access Now Easy. + +   Copyright (C) 2019 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 "command_set_common.h" +#include "low.h" +#include "value_filter.h" + +namespace genesys { + +CommandSetCommon::~CommandSetCommon() = default; + +bool CommandSetCommon::is_head_home(Genesys_Device& dev, ScanHeadId scan_head) const +{ +    struct HeadSettings { +        ModelId model_id; +        ScanHeadId scan_head; +        GenesysRegisterSettingSet regs; +    }; + +    HeadSettings settings[] = { +        {   ModelId::CANON_8600F, +            ScanHeadId::PRIMARY, { +                { 0x6c, 0x20, 0x60 }, +                { 0xa6, 0x00, 0x01 }, +            } +        }, +        {   ModelId::CANON_8600F, +            ScanHeadId::SECONDARY, { +                { 0x6c, 0x00, 0x60 }, +                { 0xa6, 0x01, 0x01 }, +            } +        }, +    }; + +    for (const auto& setting : settings) { +        if (setting.model_id == dev.model->model_id && +            setting.scan_head == scan_head) +        { +            auto reg_backup = apply_reg_settings_to_device_with_backup(dev, setting.regs); +            auto status = scanner_read_status(dev); +            apply_reg_settings_to_device(dev, reg_backup); +            return status.is_at_home; +        } +    } + +    auto status = scanner_read_status(dev); +    return status.is_at_home; +} + +void CommandSetCommon::set_xpa_lamp_power(Genesys_Device& dev, bool set) const + +{ +    DBG_HELPER(dbg); + +    struct LampSettings { +        ModelId model_id; +        ScanMethod scan_method; +        GenesysRegisterSettingSet regs_on; +        GenesysRegisterSettingSet regs_off; +    }; + +    // FIXME: BUG: we're not clearing the registers to the previous state when returning back when +    // turning off the lamp +    LampSettings settings[] = { +        {   ModelId::CANON_4400F, ScanMethod::TRANSPARENCY, {}, {} }, +        {   ModelId::CANON_5600F, ScanMethod::TRANSPARENCY, {}, {} }, +        {   ModelId::CANON_8400F, ScanMethod::TRANSPARENCY, { +                { 0xa6, 0x34, 0xf4 }, +            }, { +                { 0xa6, 0x40, 0x70 }, +            } +        }, +        {   ModelId::CANON_8400F, ScanMethod::TRANSPARENCY_INFRARED, { +                { 0x6c, 0x40, 0x40 }, +                { 0xa6, 0x01, 0xff }, +            }, { +                { 0x6c, 0x00, 0x40 }, +                { 0xa6, 0x00, 0xff }, +            } +        }, +        {   ModelId::CANON_8600F, ScanMethod::TRANSPARENCY, { +                { 0xa6, 0x34, 0xf4 }, +                { 0xa7, 0xe0, 0xe0 }, +            }, { +                { 0xa6, 0x40, 0x70 }, +            } +        }, +        {   ModelId::CANON_8600F, ScanMethod::TRANSPARENCY_INFRARED, { +                { 0xa6, 0x00, 0xc0 }, +                { 0xa7, 0xe0, 0xe0 }, +                { 0x6c, 0x80, 0x80 }, +            }, { +                { 0xa6, 0x00, 0xc0 }, +                { 0x6c, 0x00, 0x80 }, +            } +        }, +        {   ModelId::PLUSTEK_OPTICFILM_7200, ScanMethod::TRANSPARENCY, {}, {} }, +        {   ModelId::PLUSTEK_OPTICFILM_7200I, ScanMethod::TRANSPARENCY, {}, {} }, +        {   ModelId::PLUSTEK_OPTICFILM_7200I, ScanMethod::TRANSPARENCY_INFRARED, { +                { 0xa8, 0x07, 0x07 }, +            }, { +                { 0xa8, 0x00, 0x07 }, +            } +        }, +        {   ModelId::PLUSTEK_OPTICFILM_7300, ScanMethod::TRANSPARENCY, {}, {} }, +        {   ModelId::PLUSTEK_OPTICFILM_7400, ScanMethod::TRANSPARENCY, {}, {} }, +        {   ModelId::PLUSTEK_OPTICFILM_7500I, ScanMethod::TRANSPARENCY, {}, {} }, +        {   ModelId::PLUSTEK_OPTICFILM_7500I, ScanMethod::TRANSPARENCY_INFRARED, { +                { 0xa8, 0x07, 0x07 }, +            }, { +                { 0xa8, 0x00, 0x07 }, +            } +        }, +        {   ModelId::PLUSTEK_OPTICFILM_8200I, ScanMethod::TRANSPARENCY, {}, {} }, +        {   ModelId::PLUSTEK_OPTICFILM_8200I, ScanMethod::TRANSPARENCY_INFRARED, { +                { 0xa8, 0x04, 0x04 }, +            }, { +                { 0xa8, 0x00, 0x04 }, +            } +        }, +    }; + +    for (const auto& setting : settings) { +        if (setting.model_id == dev.model->model_id && +            setting.scan_method == dev.settings.scan_method) +        { +            apply_reg_settings_to_device(dev, set ? setting.regs_on : setting.regs_off); +            return; +        } +    } + +    throw SaneException("Could not find XPA lamp settings"); +} + + +void CommandSetCommon::set_motor_mode(Genesys_Device& dev, Genesys_Register_Set& regs, +                                      MotorMode mode) const +{ +    DBG_HELPER(dbg); + +    struct MotorSettings { +        ModelId model_id; +        ValueFilterAny<unsigned> resolutions; +        GenesysRegisterSettingSet regs_primary_and_secondary; +        GenesysRegisterSettingSet regs_primary; +        GenesysRegisterSettingSet regs_secondary; +    }; + +    MotorSettings settings[] = { +        {   ModelId::CANON_8400F, { 400, 800, 1600, 3200 }, { +                { 0x6c, 0x00, 0x90 }, +                { 0xa9, 0x04, 0x06 }, +            }, { +                { 0x6c, 0x90, 0x90 }, +                { 0xa9, 0x02, 0x06 }, +            }, {} +        }, +        {   ModelId::CANON_8600F, { 300, 600, 1200 }, { +                { 0x6c, 0x00, 0x60 }, +                { 0xa6, 0x01, 0x41 }, +            }, { +                { 0x6c, 0x20, 0x62 }, +                { 0xa6, 0x00, 0x41 }, +            }, { +                { 0x6c, 0x40, 0x62 }, +                { 0xa6, 0x01, 0x41 }, +            } +        }, +        {   ModelId::CANON_8600F, { 2400, 4800 }, { +                { 0x6c, 0x02, 0x62 }, +                { 0xa6, 0x01, 0x41 }, +            }, { +                { 0x6c, 0x20, 0x62 }, +                { 0xa6, 0x00, 0x41 }, +            }, { +                { 0x6c, 0x40, 0x62 }, +                { 0xa6, 0x01, 0x41 }, +            } +        }, +        {   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 +                { 0xa6, 0x08, 0x08 }, // note that reverse change is not applied on off +                { 0xa8, 0x00, 0x04 }, +                { 0xa9, 0x30, 0x30 }, +            }, { +                { 0x6b, 0x00, 0x01 }, // BUG: note that only ADF is unset +                { 0xa8, 0x04, 0x04 }, +                { 0xa9, 0x00, 0x10 }, // note that 0x20 bit is not reset +            }, {} +        }, +        {   ModelId::PLUSTEK_OPTICFILM_7200, VALUE_FILTER_ANY, {}, {}, {} }, +        {   ModelId::PLUSTEK_OPTICFILM_7200I, VALUE_FILTER_ANY, {}, {}, {} }, +        {   ModelId::PLUSTEK_OPTICFILM_7300, VALUE_FILTER_ANY, {}, {}, {} }, +        {   ModelId::PLUSTEK_OPTICFILM_7400, VALUE_FILTER_ANY, {}, {}, {} }, +        {   ModelId::PLUSTEK_OPTICFILM_7500I, VALUE_FILTER_ANY, {}, {}, {} }, +        {   ModelId::PLUSTEK_OPTICFILM_8200I, VALUE_FILTER_ANY, {}, {}, {} }, +    }; + +    for (const auto& setting : settings) { +        if (setting.model_id == dev.model->model_id && +            setting.resolutions.matches(dev.session.output_resolution)) +        { +            switch (mode) { +                case MotorMode::PRIMARY: { +                    apply_reg_settings_to_device(dev, setting.regs_primary); +                    break; +                } +                case MotorMode::PRIMARY_AND_SECONDARY: { +                    apply_reg_settings_to_device(dev, setting.regs_primary_and_secondary); +                    break; +                } +                case MotorMode::SECONDARY: { +                    apply_reg_settings_to_device(dev, setting.regs_secondary); +                    break; +                } +            } +            regs.state.motor_mode = mode; +            return; +        } +    } + +    throw SaneException("Motor settings have not been found"); +} + +} // namespace genesys diff --git a/backend/genesys/command_set_common.h b/backend/genesys/command_set_common.h new file mode 100644 index 0000000..784fcd7 --- /dev/null +++ b/backend/genesys/command_set_common.h @@ -0,0 +1,48 @@ +/* sane - Scanner Access Now Easy. + +   Copyright (C) 2019 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_COMMAND_SET_COMMON_H +#define BACKEND_GENESYS_COMMAND_SET_COMMON_H + +#include "command_set.h" + +namespace genesys { + + +/** Common command set functionality + */ +class CommandSetCommon : public CommandSet +{ +public: +    ~CommandSetCommon() override; + +    bool is_head_home(Genesys_Device& dev, ScanHeadId scan_head) const override; + +    void set_xpa_lamp_power(Genesys_Device& dev, bool set) const override; + +    void set_motor_mode(Genesys_Device& dev, Genesys_Register_Set& regs, +                        MotorMode mode) const override; +}; + +} // namespace genesys + +#endif // BACKEND_GENESYS_COMMAND_SET_COMMON_H diff --git a/backend/genesys/conv.cpp b/backend/genesys/conv.cpp deleted file mode 100644 index a87c463..0000000 --- a/backend/genesys/conv.cpp +++ /dev/null @@ -1,238 +0,0 @@ -/* sane - Scanner Access Now Easy. - -   Copyright (C) 2005, 2006 Pierre Willenbrock <pierre@pirsoft.dnsalias.org> -   Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr> - -   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. - -   As a special exception, the authors of SANE give permission for -   additional uses of the libraries contained in this release of SANE. - -   The exception is that, if you link a SANE library with other files -   to produce an executable, this does not by itself cause the -   resulting executable to be covered by the GNU General Public -   License.  Your use of that executable is in no way restricted on -   account of linking the SANE library code into it. - -   This exception does not, however, invalidate any other reasons why -   the executable file might be covered by the GNU General Public -   License. - -   If you submit changes to SANE to the maintainers to be included in -   a subsequent release, you agree by submitting the changes that -   those changes may be distributed with this exception intact. - -   If you write modifications of your own for SANE, it is your choice -   whether to permit this exception to apply to your modifications. -   If you do not wish that, delete this exception notice. -*/ - -#define DEBUG_DECLARE_ONLY - -#include "conv.h" -#include "sane/sanei_magic.h" - -namespace genesys { - -/** - * uses the threshold/threshold_curve to control software binarization - * This code was taken from the epjistsu backend by m. allan noah - * @param dev device set up for the scan - * @param src pointer to raw data - * @param dst pointer where to store result - * @param width width of the processed line - * */ -void binarize_line(Genesys_Device* dev, std::uint8_t* src, std::uint8_t* dst, int width) -{ -    DBG_HELPER(dbg); -  int j, windowX, sum = 0; -  int thresh; -  int offset, addCol, dropCol; -  unsigned char mask; - -  int x; -    std::uint8_t min, max; - -  /* normalize line */ -  min = 255; -  max = 0; -    for (x = 0; x < width; x++) -      { -	if (src[x] > max) -	  { -	    max = src[x]; -	  } -	if (src[x] < min) -	  { -	    min = src[x]; -	  } -      } - -    /* safeguard against dark or white areas */ -    if(min>80) -	    min=0; -    if(max<80) -	    max=255; -    for (x = 0; x < width; x++) -      { -	src[x] = ((src[x] - min) * 255) / (max - min); -      } - -  /* ~1mm works best, but the window needs to have odd # of pixels */ -  windowX = (6 * dev->settings.xres) / 150; -  if (!(windowX % 2)) -    windowX++; - -  /* second, prefill the sliding sum */ -  for (j = 0; j < windowX; j++) -    sum += src[j]; - -  /* third, walk the input buffer, update the sliding sum, */ -  /* determine threshold, output bits */ -  for (j = 0; j < width; j++) -    { -      /* output image location */ -      offset = j % 8; -      mask = 0x80 >> offset; -      thresh = dev->settings.threshold; - -      /* move sum/update threshold only if there is a curve */ -      if (dev->settings.threshold_curve) -	{ -	  addCol = j + windowX / 2; -	  dropCol = addCol - windowX; - -	  if (dropCol >= 0 && addCol < width) -	    { -	      sum -= src[dropCol]; -	      sum += src[addCol]; -	    } -	  thresh = dev->lineart_lut[sum / windowX]; -	} - -      /* use average to lookup threshold */ -      if (src[j] > thresh) -	*dst &= ~mask;		/* white */ -      else -	*dst |= mask;		/* black */ - -      if (offset == 7) -	dst++; -    } -} - -/** - * software lineart using data from a 8 bit gray scan. We assume true gray - * or monochrome scan as input. - */ -void genesys_gray_lineart(Genesys_Device* dev, -                          std::uint8_t* src_data, std::uint8_t* dst_data, -                          std::size_t pixels, std::size_t lines, std::uint8_t threshold) -{ -    DBG_HELPER(dbg); -    std::size_t y; - -    DBG(DBG_io2, "%s: converting %zu lines of %zu pixels\n", __func__, lines, pixels); -  DBG(DBG_io2, "%s: threshold=%d\n", __func__, threshold); - -  for (y = 0; y < lines; y++) -    { -      binarize_line (dev, src_data + y * pixels, dst_data, pixels); -      dst_data += pixels / 8; -    } -} - -/** Look in image for likely left/right/bottom paper edges, then crop image. - */ -void genesys_crop(Genesys_Scanner* s) -{ -    DBG_HELPER(dbg); -  Genesys_Device *dev = s->dev; -  int top = 0; -  int bottom = 0; -  int left = 0; -  int right = 0; - -    // first find edges if any -    TIE(sanei_magic_findEdges(&s->params, dev->img_buffer.data(), -                              dev->settings.xres, dev->settings.yres, -                              &top, &bottom, &left, &right)); - -  DBG (DBG_io, "%s: t:%d b:%d l:%d r:%d\n", __func__, top, bottom, left, -       right); - -    // now crop the image -    TIE(sanei_magic_crop (&(s->params), dev->img_buffer.data(), top, bottom, left, right)); - -  /* update counters to new image size */ -  dev->total_bytes_to_read = s->params.bytes_per_line * s->params.lines; -} - -/** Look in image for likely upper and left paper edges, then rotate - * image so that upper left corner of paper is upper left of image. - */ -void genesys_deskew(Genesys_Scanner *s, const Genesys_Sensor& sensor) -{ -    DBG_HELPER(dbg); -  Genesys_Device *dev = s->dev; - -  int x = 0, y = 0, bg; -  double slope = 0; - -  bg=0; -  if(s->params.format==SANE_FRAME_GRAY && s->params.depth == 1) -    { -      bg=0xff; -    } -    TIE(sanei_magic_findSkew(&s->params, dev->img_buffer.data(), -                             sensor.optical_res, sensor.optical_res, -                             &x, &y, &slope)); - -    DBG(DBG_info, "%s: slope=%f => %f\n", __func__, slope, slope * 180 / M_PI); - -    // rotate image slope is in [-PI/2,PI/2]. Positive values rotate trigonometric direction wise -    TIE(sanei_magic_rotate(&s->params, dev->img_buffer.data(), -                           x, y, slope, bg)); -} - -/** remove lone dots - */ -void genesys_despeck(Genesys_Scanner* s) -{ -    DBG_HELPER(dbg); -    TIE(sanei_magic_despeck(&s->params, s->dev->img_buffer.data(), s->despeck)); -} - -/** Look if image needs rotation and apply it - * */ -void genesys_derotate(Genesys_Scanner* s) -{ -    DBG_HELPER(dbg); -  int angle = 0; - -    TIE(sanei_magic_findTurn(&s->params, s->dev->img_buffer.data(), -                             s->resolution, s->resolution, &angle)); - -    // apply rotation angle found -    TIE(sanei_magic_turn(&s->params, s->dev->img_buffer.data(), angle)); - -    // update counters to new image size -    s->dev->total_bytes_to_read = s->params.bytes_per_line * s->params.lines; -} - -} // namespace genesys diff --git a/backend/genesys/conv.h b/backend/genesys/conv.h deleted file mode 100644 index 446a80d..0000000 --- a/backend/genesys/conv.h +++ /dev/null @@ -1,69 +0,0 @@ -/* sane - Scanner Access Now Easy. - -   Copyright (C) 2019 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. - -   As a special exception, the authors of SANE give permission for -   additional uses of the libraries contained in this release of SANE. - -   The exception is that, if you link a SANE library with other files -   to produce an executable, this does not by itself cause the -   resulting executable to be covered by the GNU General Public -   License.  Your use of that executable is in no way restricted on -   account of linking the SANE library code into it. - -   This exception does not, however, invalidate any other reasons why -   the executable file might be covered by the GNU General Public -   License. - -   If you submit changes to SANE to the maintainers to be included in -   a subsequent release, you agree by submitting the changes that -   those changes may be distributed with this exception intact. - -   If you write modifications of your own for SANE, it is your choice -   whether to permit this exception to apply to your modifications. -   If you do not wish that, delete this exception notice. -*/ - -#ifndef BACKEND_GENESYS_CONV_H -#define BACKEND_GENESYS_CONV_H - -#include "device.h" -#include "sensor.h" -#include "genesys.h" - -namespace genesys { - -void binarize_line(Genesys_Device* dev, std::uint8_t* src, std::uint8_t* dst, int width); - -void genesys_gray_lineart(Genesys_Device* dev, -                          std::uint8_t* src_data, std::uint8_t* dst_data, -                          std::size_t pixels, size_t lines, std::uint8_t threshold); - -void genesys_crop(Genesys_Scanner* s); - -void genesys_deskew(Genesys_Scanner *s, const Genesys_Sensor& sensor); - -void genesys_despeck(Genesys_Scanner* s); - -void genesys_derotate(Genesys_Scanner* s); - -} // namespace genesys - -#endif // BACKEND_GENESYS_CONV_H diff --git a/backend/genesys/device.cpp b/backend/genesys/device.cpp index ba035fd..95bede8 100644 --- a/backend/genesys/device.cpp +++ b/backend/genesys/device.cpp @@ -62,15 +62,24 @@ std::vector<unsigned> MethodResolutions::get_resolutions() const      return ret;  } -const MethodResolutions& Genesys_Model::get_resolution_settings(ScanMethod method) const +const MethodResolutions* Genesys_Model::get_resolution_settings_ptr(ScanMethod method) const  {      for (const auto& res_for_method : resolutions) {          for (auto res_method : res_for_method.methods) {              if (res_method == method) { -                return res_for_method; +                return &res_for_method;              }          }      } +    return nullptr; + +} +const MethodResolutions& Genesys_Model::get_resolution_settings(ScanMethod method) const +{ +    const auto* ptr = get_resolution_settings_ptr(method); +    if (ptr) +        return *ptr; +      throw SaneException("Could not find resolution settings for method %d",                          static_cast<unsigned>(method));  } @@ -80,6 +89,12 @@ std::vector<unsigned> Genesys_Model::get_resolutions(ScanMethod method) const      return get_resolution_settings(method).get_resolutions();  } +bool Genesys_Model::has_method(ScanMethod method) const +{ +    return get_resolution_settings_ptr(method) != nullptr; +} + +  Genesys_Device::~Genesys_Device()  {      clear(); @@ -87,10 +102,6 @@ Genesys_Device::~Genesys_Device()  void Genesys_Device::clear()  { -    read_buffer.clear(); -    binarize_buffer.clear(); -    local_buffer.clear(); -      calib_file.clear();      calibration_cache.clear(); @@ -99,9 +110,9 @@ void Genesys_Device::clear()      dark_average_data.clear();  } -ImagePipelineNodeBytesSource& Genesys_Device::get_pipeline_source() +ImagePipelineNodeBufferedCallableSource& Genesys_Device::get_pipeline_source()  { -    return static_cast<ImagePipelineNodeBytesSource&>(pipeline.front()); +    return static_cast<ImagePipelineNodeBufferedCallableSource&>(pipeline.front());  }  bool Genesys_Device::is_head_pos_known(ScanHeadId scan_head) const @@ -124,10 +135,14 @@ unsigned Genesys_Device::head_pos(ScanHeadId scan_head) const      }  } -void Genesys_Device::set_head_pos_unknown() +void Genesys_Device::set_head_pos_unknown(ScanHeadId scan_head)  { -    is_head_pos_primary_known_ = false; -    is_head_pos_secondary_known_ = false; +    if ((scan_head & ScanHeadId::PRIMARY) != ScanHeadId::NONE) { +        is_head_pos_primary_known_ = false; +    } +    if ((scan_head & ScanHeadId::SECONDARY) != ScanHeadId::NONE) { +        is_head_pos_secondary_known_ = false; +    }  }  void Genesys_Device::set_head_pos_zero(ScanHeadId scan_head) @@ -205,12 +220,15 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev)          << "    ignore_offsets: " << dev.ignore_offsets << '\n'          << "    model: (not printed)\n"          << "    reg: " << format_indent_braced_list(4, dev.reg) << '\n' -        << "    calib_reg: " << format_indent_braced_list(4, dev.calib_reg) << '\n' +        << "    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' -        << "    frontend_is_init: " << dev.frontend_is_init << '\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]) << ' ' @@ -220,13 +238,7 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev)          << static_cast<unsigned>(dev.control[4]) << ' '          << static_cast<unsigned>(dev.control[5]) << '\n' << std::dec          << "    average_size: " << dev.average_size << '\n' -        << "    calib_pixels: " << dev.calib_pixels << '\n' -        << "    calib_lines: " << dev.calib_lines << '\n' -        << "    calib_channels: " << dev.calib_channels << '\n' -        << "    calib_resolution: " << dev.calib_resolution << '\n' -        << "    calib_total_bytes_to_read: " << dev.calib_total_bytes_to_read << '\n'          << "    calib_session: " << format_indent_braced_list(4, dev.calib_session) << '\n' -        << "    calib_pixels_offset: " << dev.calib_pixels_offset << '\n'          << "    gamma_override_tables[0].size(): " << dev.gamma_override_tables[0].size() << '\n'          << "    gamma_override_tables[1].size(): " << dev.gamma_override_tables[1].size() << '\n'          << "    gamma_override_tables[2].size(): " << dev.gamma_override_tables[2].size() << '\n' @@ -242,31 +254,47 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Device& dev)          << "    read_active: " << dev.read_active << '\n'          << "    parking: " << dev.parking << '\n'          << "    document: " << dev.document << '\n' -        << "    read_buffer.size(): " << dev.read_buffer.size() << '\n' -        << "    binarize_buffer.size(): " << dev.binarize_buffer.size() << '\n' -        << "    local_buffer.size(): " << dev.local_buffer.size() << '\n' -        << "    oe_buffer.size(): " << dev.oe_buffer.size() << '\n'          << "    total_bytes_read: " << dev.total_bytes_read << '\n'          << "    total_bytes_to_read: " << dev.total_bytes_to_read << '\n'          << "    session: " << format_indent_braced_list(4, dev.session) << '\n' -        << "    lineart_lut: (not printed)\n"          << "    calibration_cache: (not printed)\n"          << "    line_count: " << dev.line_count << '\n'          << "    segment_order: "          << format_indent_braced_list(4, format_vector_unsigned(4, dev.segment_order)) << '\n' -        << "    buffer_image: " << dev.buffer_image << '\n' -        << "    img_buffer.size(): " << dev.img_buffer.size() << '\n'          << '}';      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); +} + +GenesysRegisterSettingSet +    apply_reg_settings_to_device_with_backup(Genesys_Device& dev, +                                             const GenesysRegisterSettingSet& regs) +{ +    GenesysRegisterSettingSet backup;      for (const auto& reg : regs) { -        uint8_t val = dev.interface->read_register(reg.address); -        val = (val & ~reg.mask) | (reg.value & reg.mask); -        dev.interface->write_register(reg.address, val); +        std::uint8_t old_val = dev.interface->read_register(reg.address); +        std::uint8_t new_val = (old_val & ~reg.mask) | (reg.value & reg.mask); +        dev.interface->write_register(reg.address, new_val); + +        using SettingType = GenesysRegisterSettingSet::SettingType; +        backup.push_back(SettingType{reg.address, +                                     static_cast<std::uint8_t>(old_val & reg.mask), +                                     reg.mask});      } +    return backup;  }  } // namespace genesys diff --git a/backend/genesys/device.h b/backend/genesys/device.h index 6c744c9..ded6a48 100644 --- a/backend/genesys/device.h +++ b/backend/genesys/device.h @@ -46,7 +46,6 @@  #include "calibration.h"  #include "command_set.h" -#include "buffer.h"  #include "enums.h"  #include "image_pipeline.h"  #include "motor.h" @@ -55,6 +54,7 @@  #include "register.h"  #include "usb_device.h"  #include "scanner_interface.h" +#include "utilities.h"  #include <vector>  namespace genesys { @@ -77,22 +77,15 @@ struct Genesys_Gpo      GenesysRegisterSettingSet regs;  }; -/// Stores a SANE_Fixed value which is automatically converted from and to floating-point values -class FixedFloat +struct MemoryLayout  { -public: -    FixedFloat() = default; -    FixedFloat(const FixedFloat&) = default; -    FixedFloat(double number) : value_{SANE_FIX(number)} {} -    FixedFloat& operator=(const FixedFloat&) = default; -    FixedFloat& operator=(double number) { value_ = SANE_FIX(number); return *this; } +    // This is used on GL845, GL846, GL847 and GL124 which have special registers to define the +    // memory layout +    MemoryLayout() = default; -    operator double() const { return value(); } +    ValueFilter<ModelId> models; -    double value() const { return SANE_UNFIX(value_); } - -private: -    SANE_Fixed value_ = 0; +    GenesysRegisterSettingSet regs;  };  struct MethodResolutions @@ -106,6 +99,16 @@ struct MethodResolutions          return *std::min_element(resolutions_x.begin(), resolutions_x.end());      } +    unsigned get_nearest_resolution_x(unsigned resolution) const +    { +        return *std::min_element(resolutions_x.begin(), resolutions_x.end(), +                                 [&](unsigned lhs, unsigned rhs) +        { +            return std::abs(static_cast<int>(lhs) - static_cast<int>(resolution)) < +                     std::abs(static_cast<int>(rhs) - static_cast<int>(resolution)); +        }); +    } +      unsigned get_min_resolution_y() const      {          return *std::min_element(resolutions_y.begin(), resolutions_y.end()); @@ -143,51 +146,67 @@ struct Genesys_Model      // All offsets below are with respect to the sensor home position      // Start of scan area in mm -    FixedFloat x_offset = 0; +    float x_offset = 0;      // Start of scan area in mm (Amount of feeding needed to get to the medium) -    FixedFloat y_offset = 0; +    float y_offset = 0;      // Size of scan area in mm -    FixedFloat x_size = 0; +    float x_size = 0;      // Size of scan area in mm -    FixedFloat y_size = 0; +    float y_size = 0; -    // Start of white strip in mm -    FixedFloat y_offset_calib_white = 0; +    // Start of white strip in mm for scanners that use separate dark and white shading calibration. +    float y_offset_calib_white = 0; + +    // The size of the scan area that is used to acquire shading data in mm +    float y_size_calib_mm = 0; + +    // Start of the black/white strip in mm for scanners that use unified dark and white shading +    // calibration. +    float y_offset_calib_dark_white_mm = 0; + +    // The size of the scan area that is used to acquire dark/white shading data in mm +    float y_size_calib_dark_white_mm = 0; + +    // The width of the scan area that is used to acquire shading data +    float x_size_calib_mm = 0;      // Start of black mark in mm -    FixedFloat x_offset_calib_black = 0; +    float x_offset_calib_black = 0;      // Start of scan area in transparency mode in mm -    FixedFloat x_offset_ta = 0; +    float x_offset_ta = 0;      // Start of scan area in transparency mode in mm -    FixedFloat y_offset_ta = 0; +    float y_offset_ta = 0;      // Size of scan area in transparency mode in mm -    FixedFloat x_size_ta = 0; +    float x_size_ta = 0;      // Size of scan area in transparency mode in mm -    FixedFloat y_size_ta = 0; +    float y_size_ta = 0;      // The position of the sensor when it's aligned with the lamp for transparency scanning -    FixedFloat y_offset_sensor_to_ta = 0; +    float y_offset_sensor_to_ta = 0;      // Start of white strip in transparency mode in mm -    FixedFloat y_offset_calib_white_ta = 0; +    float y_offset_calib_white_ta = 0;      // Start of black strip in transparency mode in mm -    FixedFloat y_offset_calib_black_ta = 0; +    float y_offset_calib_black_ta = 0; + +    // The size of the scan area that is used to acquire shading data in transparency mode in mm +    float y_size_calib_ta_mm = 0;      // Size of scan area after paper sensor stop sensing document in mm -    FixedFloat post_scan = 0; +    float post_scan = 0;      // Amount of feeding needed to eject document after finishing scanning in mm -    FixedFloat eject_feed = 0; +    float eject_feed = 0; -    // Line-distance correction (in pixel at optical_ydpi) for CCD scanners +    // Line-distance correction (in pixel at motor base_ydpi) for CCD scanners      SANE_Int ld_shift_r = 0;      SANE_Int ld_shift_g = 0;      SANE_Int ld_shift_b = 0; @@ -210,22 +229,24 @@ struct Genesys_Model      // stepper motor type      MotorId motor_id = MotorId::UNKNOWN; -    // Which hacks are needed for this scanner? -    SANE_Word flags = 0; +    // Which customizations are needed for this scanner? +    ModelFlag flags = ModelFlag::NONE;      // Button flags, described existing buttons for the model      SANE_Word buttons = 0; -    // how many lines are used for shading calibration -    SANE_Int shading_lines = 0; -    // how many lines are used for shading calibration in TA mode -    SANE_Int shading_ta_lines = 0;      // how many lines are used to search start position      SANE_Int search_lines = 0; +    // returns nullptr if method is not supported +    const MethodResolutions* get_resolution_settings_ptr(ScanMethod method) const; + +    // throws if method is not supported      const MethodResolutions& get_resolution_settings(ScanMethod method) const;      std::vector<unsigned> get_resolutions(ScanMethod method) const; + +    bool has_method(ScanMethod method) const;  };  /** @@ -243,8 +264,8 @@ struct Genesys_Device      // frees commonly used data      void clear(); -    SANE_Word vendorId = 0;			/**< USB vendor identifier */ -    SANE_Word productId = 0;			/**< USB product identifier */ +    std::uint16_t vendorId = 0; // USB vendor identifier +    std::uint16_t productId = 0; // USB product identifier      // USB mode:      // 0: not set @@ -261,42 +282,25 @@ struct Genesys_Device      // acquiring the positions of the black and white strips and the actual scan area      bool ignore_offsets = false; -    Genesys_Model *model = nullptr; +    const Genesys_Model* model = nullptr;      // pointers to low level functions      std::unique_ptr<CommandSet> cmd_set;      Genesys_Register_Set reg; -    Genesys_Register_Set calib_reg; +    Genesys_Register_Set initial_regs;      Genesys_Settings settings;      Genesys_Frontend frontend, frontend_initial; - -    // whether the frontend is initialized. This is currently used just to preserve historical -    // behavior -    bool frontend_is_init = false; -      Genesys_Gpo gpo; +    MemoryLayout memory_layout;      Genesys_Motor motor;      std::uint8_t control[6] = {};      size_t average_size = 0; -    // number of pixels used during shading calibration -    size_t calib_pixels = 0; -    // number of lines used during shading calibration -    size_t calib_lines = 0; -    size_t calib_channels = 0; -    size_t calib_resolution = 0; -     // bytes to read from USB when calibrating. If 0, this is not set -    size_t calib_total_bytes_to_read = 0;      // the session that was configured for calibration      ScanSession calib_session; -    // certain scanners support much higher resolution when scanning transparency, but we can't -    // read whole width of the scanner as a single line at that resolution. Thus for stuff like -    // calibration we want to read only the possible calibration area. -    size_t calib_pixels_offset = 0; -      // gamma overrides. If a respective array is not empty then it means that the gamma for that      // color is overridden.      std::vector<std::uint16_t> gamma_override_tables[3]; @@ -313,13 +317,6 @@ struct Genesys_Device      // for sheetfed scanner's, is TRUE when there is a document in the scanner      bool document = false; -    Genesys_Buffer read_buffer; - -    // buffer for digital lineart from gray data -    Genesys_Buffer binarize_buffer; -    // local buffer for gray data during dynamix lineart -    Genesys_Buffer local_buffer; -      // total bytes read sent to frontend      size_t total_bytes_read = 0;      // total bytes read to be sent to frontend @@ -328,9 +325,6 @@ struct Genesys_Device      // contains computed data for the current setup      ScanSession session; -    // look up table used in dynamic rasterization -    unsigned char lineart_lut[256] = {}; -      Calibration calibration_cache;      // number of scan lines used during scan @@ -339,28 +333,19 @@ struct Genesys_Device      // array describing the order of the sub-segments of the sensor      std::vector<unsigned> segment_order; -    // buffer to handle even/odd data -    Genesys_Buffer oe_buffer = {}; -      // stores information about how the input image should be processed      ImagePipelineStack pipeline;      // an buffer that allows reading from `pipeline` in chunks of any size      ImageBuffer pipeline_buffer; -    // when true the scanned picture is first buffered to allow software image enhancements -    bool buffer_image = false; - -    // image buffer where the scanned picture is stored -    std::vector<std::uint8_t> img_buffer; - -    ImagePipelineNodeBytesSource& get_pipeline_source(); +    ImagePipelineNodeBufferedCallableSource& get_pipeline_source();      std::unique_ptr<ScannerInterface> interface;      bool is_head_pos_known(ScanHeadId scan_head) const;      unsigned head_pos(ScanHeadId scan_head) const; -    void set_head_pos_unknown(); +    void set_head_pos_unknown(ScanHeadId scan_head);      void set_head_pos_zero(ScanHeadId scan_head);      void advance_head_pos_by_session(ScanHeadId scan_head);      void advance_head_pos_by_steps(ScanHeadId scan_head, Direction direction, unsigned steps); @@ -382,6 +367,12 @@ 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); +  } // namespace genesys  #endif diff --git a/backend/genesys/enums.cpp b/backend/genesys/enums.cpp index f515cfd..cd4be7d 100644 --- a/backend/genesys/enums.cpp +++ b/backend/genesys/enums.cpp @@ -109,6 +109,248 @@ 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_90: out << "CANON_LIDE_90"; 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_7200: out << "PLUSTEK_OPTICFILM_7200"; 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_7400: out << "PLUSTEK_OPTICFILM_7400"; break; +        case ModelId::PLUSTEK_OPTICFILM_7500I: out << "PLUSTEK_OPTICFILM_7500I"; break; +        case ModelId::PLUSTEK_OPTICFILM_8200I: out << "PLUSTEK_OPTICFILM_8200I"; 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, SensorId id) +{ +    switch (id) { +        case SensorId::CCD_5345: out << "CCD_5345"; break; +        case SensorId::CCD_CANON_4400F: out << "CCD_CANON_4400F"; break; +        case SensorId::CCD_CANON_5600F: out << "CCD_CANON_5600F"; break; +        case SensorId::CCD_CANON_8400F: out << "CCD_CANON_8400F"; break; +        case SensorId::CCD_CANON_8600F: out << "CCD_CANON_8600F"; break; +        case SensorId::CCD_DP665: out << "CCD_DP665"; break; +        case SensorId::CCD_DP685: out << "CCD_DP685"; break; +        case SensorId::CCD_DSMOBILE600: out << "CCD_DSMOBILE600"; break; +        case SensorId::CCD_DOCKETPORT_487: out << "CCD_DOCKETPORT_487"; break; +        case SensorId::CCD_G4050: out << "CCD_G4050"; break; +        case SensorId::CCD_HP2300: out << "CCD_HP2300"; break; +        case SensorId::CCD_HP2400: out << "CCD_HP2400"; break; +        case SensorId::CCD_HP3670: out << "CCD_HP3670"; break; +        case SensorId::CCD_HP_N6310: out << "CCD_HP_N6310"; break; +        case SensorId::CCD_HP_4850C: out << "CCD_HP_4850C"; break; +        case SensorId::CCD_IMG101: out << "CCD_IMG101"; break; +        case SensorId::CCD_KVSS080: out << "CCD_KVSS080"; break; +        case SensorId::CCD_PLUSTEK_OPTICBOOK_3800: out << "CCD_PLUSTEK_OPTICBOOK_3800"; break; +        case SensorId::CCD_PLUSTEK_OPTICFILM_7200: out << "CCD_PLUSTEK_OPTICFILM_7200"; break; +        case SensorId::CCD_PLUSTEK_OPTICFILM_7200I: out << "CCD_PLUSTEK_OPTICFILM_7200I"; break; +        case SensorId::CCD_PLUSTEK_OPTICFILM_7300: out << "CCD_PLUSTEK_OPTICFILM_7300"; break; +        case SensorId::CCD_PLUSTEK_OPTICFILM_7400: out << "CCD_PLUSTEK_OPTICFILM_7400"; break; +        case SensorId::CCD_PLUSTEK_OPTICFILM_7500I: out << "CCD_PLUSTEK_OPTICFILM_7500I"; break; +        case SensorId::CCD_PLUSTEK_OPTICFILM_8200I: out << "CCD_PLUSTEK_OPTICFILM_8200I"; break; +        case SensorId::CCD_PLUSTEK_OPTICPRO_3600: out << "CCD_PLUSTEK_OPTICPRO_3600"; break; +        case SensorId::CCD_ROADWARRIOR: out << "CCD_ROADWARRIOR"; break; +        case SensorId::CCD_ST12: out << "CCD_ST12"; break; +        case SensorId::CCD_ST24: out << "CCD_ST24"; break; +        case SensorId::CCD_UMAX: out << "CCD_UMAX"; break; +        case SensorId::CCD_XP300: out << "CCD_XP300"; break; +        case SensorId::CIS_CANON_LIDE_35: out << "CIS_CANON_LIDE_35"; break; +        case SensorId::CIS_CANON_LIDE_60: out << "CIS_CANON_LIDE_60"; break; +        case SensorId::CIS_CANON_LIDE_80: out << "CIS_CANON_LIDE_80"; break; +        case SensorId::CIS_CANON_LIDE_90: out << "CIS_CANON_LIDE_90"; break; +        case SensorId::CIS_CANON_LIDE_100: out << "CIS_CANON_LIDE_100"; break; +        case SensorId::CIS_CANON_LIDE_110: out << "CIS_CANON_LIDE_110"; break; +        case SensorId::CIS_CANON_LIDE_120: out << "CIS_CANON_LIDE_120"; break; +        case SensorId::CIS_CANON_LIDE_200: out << "CIS_CANON_LIDE_200"; break; +        case SensorId::CIS_CANON_LIDE_210: out << "CIS_CANON_LIDE_210"; break; +        case SensorId::CIS_CANON_LIDE_220: out << "CIS_CANON_LIDE_220"; break; +        case SensorId::CIS_CANON_LIDE_700F: out << "CIS_CANON_LIDE_700F"; break; +        case SensorId::CIS_XP200: out << "CIS_XP200"; break; +        default: +            out << static_cast<unsigned>(id); break; +    } +    return out; +} + +std::ostream& operator<<(std::ostream& out, AdcId id) +{ +    switch (id) { +        case AdcId::UNKNOWN: out << "UNKNOWN"; break; +        case AdcId::AD_XP200: out << "AD_XP200"; break; +        case AdcId::CANON_LIDE_35: out << "CANON_LIDE_35"; break; +        case AdcId::CANON_LIDE_80: out << "CANON_LIDE_80"; break; +        case AdcId::CANON_LIDE_90: out << "CANON_LIDE_90"; break; +        case AdcId::CANON_LIDE_110: out << "CANON_LIDE_110"; break; +        case AdcId::CANON_LIDE_120: out << "CANON_LIDE_120"; break; +        case AdcId::CANON_LIDE_200: out << "CANON_LIDE_200"; break; +        case AdcId::CANON_LIDE_700F: out << "CANON_LIDE_700F"; break; +        case AdcId::CANON_4400F: out << "CANON_4400F"; break; +        case AdcId::CANON_5600F: out << "CANON_5600F"; break; +        case AdcId::CANON_8400F: out << "CANON_8400F"; break; +        case AdcId::CANON_8600F: out << "CANON_8600F"; break; +        case AdcId::G4050: out << "G4050"; break; +        case AdcId::IMG101: out << "IMG101"; break; +        case AdcId::KVSS080: out << "KVSS080"; break; +        case AdcId::PLUSTEK_OPTICBOOK_3800: out << "PLUSTEK_OPTICBOOK_3800"; break; +        case AdcId::PLUSTEK_OPTICFILM_7200: out << "PLUSTEK_OPTICFILM_7200"; break; +        case AdcId::PLUSTEK_OPTICFILM_7200I: out << "PLUSTEK_OPTICFILM_7200I"; break; +        case AdcId::PLUSTEK_OPTICFILM_7300: out << "PLUSTEK_OPTICFILM_7300"; break; +        case AdcId::PLUSTEK_OPTICFILM_7400: out << "PLUSTEK_OPTICFILM_7400"; break; +        case AdcId::PLUSTEK_OPTICFILM_7500I: out << "PLUSTEK_OPTICFILM_7500I"; break; +        case AdcId::PLUSTEK_OPTICFILM_8200I: out << "PLUSTEK_OPTICFILM_8200I"; break; +        case AdcId::PLUSTEK_OPTICPRO_3600: out << "PLUSTEK_OPTICPRO_3600"; break; +        case AdcId::WOLFSON_5345: out << "WOLFSON_5345"; break; +        case AdcId::WOLFSON_DSM600: out << "WOLFSON_DSM600"; break; +        case AdcId::WOLFSON_HP2300: out << "WOLFSON_HP2300"; break; +        case AdcId::WOLFSON_HP2400: out << "WOLFSON_HP2400"; break; +        case AdcId::WOLFSON_HP3670: out << "WOLFSON_HP3670"; break; +        case AdcId::WOLFSON_ST12: out << "WOLFSON_ST12"; break; +        case AdcId::WOLFSON_ST24: out << "WOLFSON_ST24"; break; +        case AdcId::WOLFSON_UMAX: out << "WOLFSON_UMAX"; break; +        case AdcId::WOLFSON_XP300: out << "WOLFSON_XP300"; break; +        default: +            out << static_cast<unsigned>(id); break; +    } +    return out; +} + +std::ostream& operator<<(std::ostream& out, GpioId id) +{ +    switch (id) { +        case GpioId::UNKNOWN: out << "UNKNOWN"; break; +        case GpioId::CANON_LIDE_35: out << "CANON_LIDE_35"; break; +        case GpioId::CANON_LIDE_80: out << "CANON_LIDE_80"; break; +        case GpioId::CANON_LIDE_90: out << "CANON_LIDE_90"; break; +        case GpioId::CANON_LIDE_110: out << "CANON_LIDE_110"; break; +        case GpioId::CANON_LIDE_120: out << "CANON_LIDE_120"; break; +        case GpioId::CANON_LIDE_200: out << "CANON_LIDE_200"; break; +        case GpioId::CANON_LIDE_210: out << "CANON_LIDE_210"; break; +        case GpioId::CANON_LIDE_700F: out << "CANON_LIDE_700F"; break; +        case GpioId::CANON_4400F: out << "CANON_4400F"; break; +        case GpioId::CANON_5600F: out << "CANON_5600F"; break; +        case GpioId::CANON_8400F: out << "CANON_8400F"; break; +        case GpioId::CANON_8600F: out << "CANON_8600F"; break; +        case GpioId::DP665: out << "DP665"; break; +        case GpioId::DP685: out << "DP685"; break; +        case GpioId::G4050: out << "G4050"; break; +        case GpioId::HP2300: out << "HP2300"; break; +        case GpioId::HP2400: out << "HP2400"; break; +        case GpioId::HP3670: out << "HP3670"; break; +        case GpioId::HP_N6310: out << "HP_N6310"; break; +        case GpioId::IMG101: out << "IMG101"; break; +        case GpioId::KVSS080: out << "KVSS080"; break; +        case GpioId::MD_5345: out << "MD_5345"; break; +        case GpioId::PLUSTEK_OPTICBOOK_3800: out << "PLUSTEK_OPTICBOOK_3800"; break; +        case GpioId::PLUSTEK_OPTICFILM_7200I: out << "PLUSTEK_OPTICFILM_7200I"; break; +        case GpioId::PLUSTEK_OPTICFILM_7300: out << "PLUSTEK_OPTICFILM_7300"; break; +        case GpioId::PLUSTEK_OPTICFILM_7400: out << "PLUSTEK_OPTICFILM_7400"; break; +        case GpioId::PLUSTEK_OPTICFILM_7500I: out << "PLUSTEK_OPTICFILM_7500I"; break; +        case GpioId::PLUSTEK_OPTICFILM_8200I: out << "PLUSTEK_OPTICFILM_8200I"; break; +        case GpioId::PLUSTEK_OPTICPRO_3600: out << "PLUSTEK_OPTICPRO_3600"; break; +        case GpioId::ST12: out << "ST12"; break; +        case GpioId::ST24: out << "ST24"; break; +        case GpioId::UMAX: out << "UMAX"; break; +        case GpioId::XP200: out << "XP200"; break; +        case GpioId::XP300: out << "XP300"; break; +        default: out << static_cast<unsigned>(id); break; +    } +    return out; +} + +std::ostream& operator<<(std::ostream& out, MotorId id) +{ +    switch (id) { +        case MotorId::UNKNOWN: out << "UNKNOWN"; break; +        case MotorId::CANON_LIDE_90: out << "CANON_LIDE_90"; break; +        case MotorId::CANON_LIDE_100: out << "CANON_LIDE_100"; break; +        case MotorId::CANON_LIDE_110: out << "CANON_LIDE_110"; break; +        case MotorId::CANON_LIDE_120: out << "CANON_LIDE_120"; break; +        case MotorId::CANON_LIDE_200: out << "CANON_LIDE_200"; break; +        case MotorId::CANON_LIDE_210: out << "CANON_LIDE_210"; break; +        case MotorId::CANON_LIDE_35: out << "CANON_LIDE_35"; break; +        case MotorId::CANON_LIDE_60: out << "CANON_LIDE_60"; break; +        case MotorId::CANON_LIDE_700: out << "CANON_LIDE_700"; break; +        case MotorId::CANON_LIDE_80: out << "CANON_LIDE_80"; break; +        case MotorId::CANON_4400F: out << "CANON_4400F"; break; +        case MotorId::CANON_5600F: out << "CANON_5600F"; break; +        case MotorId::CANON_8400F: out << "CANON_8400F"; break; +        case MotorId::CANON_8600F: out << "CANON_8600F"; break; +        case MotorId::DP665: out << "DP665"; break; +        case MotorId::DSMOBILE_600: out << "DSMOBILE_600"; break; +        case MotorId::G4050: out << "G4050"; break; +        case MotorId::HP2300: out << "HP2300"; break; +        case MotorId::HP2400: out << "HP2400"; break; +        case MotorId::HP3670: out << "HP3670"; break; +        case MotorId::IMG101: out << "IMG101"; break; +        case MotorId::KVSS080: out << "KVSS080"; break; +        case MotorId::MD_5345: out << "MD_5345"; break; +        case MotorId::PLUSTEK_OPTICBOOK_3800: out << "PLUSTEK_OPTICBOOK_3800"; break; +        case MotorId::PLUSTEK_OPTICFILM_7200: out << "PLUSTEK_OPTICFILM_7200"; break; +        case MotorId::PLUSTEK_OPTICFILM_7200I: out << "PLUSTEK_OPTICFILM_7200I"; break; +        case MotorId::PLUSTEK_OPTICFILM_7300: out << "PLUSTEK_OPTICFILM_7300"; break; +        case MotorId::PLUSTEK_OPTICFILM_7400: out << "PLUSTEK_OPTICFILM_7400"; break; +        case MotorId::PLUSTEK_OPTICFILM_7500I: out << "PLUSTEK_OPTICFILM_7500I"; break; +        case MotorId::PLUSTEK_OPTICFILM_8200I: out << "PLUSTEK_OPTICFILM_8200I"; break; +        case MotorId::PLUSTEK_OPTICPRO_3600: out << "PLUSTEK_OPTICPRO_3600"; break; +        case MotorId::ROADWARRIOR: out << "ROADWARRIOR"; break; +        case MotorId::ST24: out << "ST24"; break; +        case MotorId::UMAX: out << "UMAX"; break; +        case MotorId::XP200: out << "XP200"; break; +        case MotorId::XP300: out << "XP300"; break; +        default: out << static_cast<unsigned>(id); break; +    } +    return out; +} +  std::ostream& operator<<(std::ostream& out, StepType type)  {      switch (type) { diff --git a/backend/genesys/enums.h b/backend/genesys/enums.h index 810c4ca..0e16ba4 100644 --- a/backend/genesys/enums.h +++ b/backend/genesys/enums.h @@ -182,6 +182,7 @@ enum class ModelId : unsigned      CANON_LIDE_50,      CANON_LIDE_60,      CANON_LIDE_80, +    CANON_LIDE_90,      CANON_LIDE_100,      CANON_LIDE_110,      CANON_LIDE_120, @@ -201,9 +202,12 @@ enum class ModelId : unsigned      PANASONIC_KV_SS080,      PENTAX_DSMOBILE_600,      PLUSTEK_OPTICBOOK_3800, +    PLUSTEK_OPTICFILM_7200,      PLUSTEK_OPTICFILM_7200I,      PLUSTEK_OPTICFILM_7300, +    PLUSTEK_OPTICFILM_7400,      PLUSTEK_OPTICFILM_7500I, +    PLUSTEK_OPTICFILM_8200I,      PLUSTEK_OPTICPRO_3600,      PLUSTEK_OPTICPRO_ST12,      PLUSTEK_OPTICPRO_ST24, @@ -222,16 +226,33 @@ 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,      CCD_5345,      CCD_CANON_4400F, +    CCD_CANON_5600F,      CCD_CANON_8400F,      CCD_CANON_8600F,      CCD_DP665,      CCD_DP685,      CCD_DSMOBILE600, +    CCD_DOCKETPORT_487,      CCD_G4050,      CCD_HP2300,      CCD_HP2400, @@ -241,9 +262,12 @@ enum class SensorId : unsigned      CCD_IMG101,      CCD_KVSS080,      CCD_PLUSTEK_OPTICBOOK_3800, +    CCD_PLUSTEK_OPTICFILM_7200,      CCD_PLUSTEK_OPTICFILM_7200I,      CCD_PLUSTEK_OPTICFILM_7300, +    CCD_PLUSTEK_OPTICFILM_7400,      CCD_PLUSTEK_OPTICFILM_7500I, +    CCD_PLUSTEK_OPTICFILM_8200I,      CCD_PLUSTEK_OPTICPRO_3600,      CCD_ROADWARRIOR,      CCD_ST12,         // SONY ILX548: 5340 Pixel  ??? @@ -251,7 +275,9 @@ enum class SensorId : unsigned      CCD_UMAX,      CCD_XP300,      CIS_CANON_LIDE_35, +    CIS_CANON_LIDE_60,      CIS_CANON_LIDE_80, +    CIS_CANON_LIDE_90,      CIS_CANON_LIDE_100,      CIS_CANON_LIDE_110,      CIS_CANON_LIDE_120, @@ -275,6 +301,8 @@ inline void serialize(std::ostream& str, SensorId& x)      serialize(str, value);  } +std::ostream& operator<<(std::ostream& out, SensorId id); +  enum class AdcId : unsigned  { @@ -282,20 +310,25 @@ enum class AdcId : unsigned      AD_XP200,      CANON_LIDE_35,      CANON_LIDE_80, +    CANON_LIDE_90,      CANON_LIDE_110,      CANON_LIDE_120,      CANON_LIDE_200,      CANON_LIDE_700F,      CANON_4400F, +    CANON_5600F,      CANON_8400F,      CANON_8600F,      G4050,      IMG101,      KVSS080,      PLUSTEK_OPTICBOOK_3800, +    PLUSTEK_OPTICFILM_7200,      PLUSTEK_OPTICFILM_7200I,      PLUSTEK_OPTICFILM_7300, +    PLUSTEK_OPTICFILM_7400,      PLUSTEK_OPTICFILM_7500I, +    PLUSTEK_OPTICFILM_8200I,      PLUSTEK_OPTICPRO_3600,      WOLFSON_5345,      WOLFSON_DSM600, @@ -321,17 +354,21 @@ inline void serialize(std::ostream& str, AdcId& x)      serialize(str, value);  } +std::ostream& operator<<(std::ostream& out, AdcId id); +  enum class GpioId : unsigned  {      UNKNOWN = 0,      CANON_LIDE_35,      CANON_LIDE_80, +    CANON_LIDE_90,      CANON_LIDE_110,      CANON_LIDE_120,      CANON_LIDE_200,      CANON_LIDE_210,      CANON_LIDE_700F,      CANON_4400F, +    CANON_5600F,      CANON_8400F,      CANON_8600F,      DP665, @@ -345,9 +382,12 @@ enum class GpioId : unsigned      KVSS080,      MD_5345,      PLUSTEK_OPTICBOOK_3800, +    PLUSTEK_OPTICFILM_7200,      PLUSTEK_OPTICFILM_7200I,      PLUSTEK_OPTICFILM_7300, +    PLUSTEK_OPTICFILM_7400,      PLUSTEK_OPTICFILM_7500I, +    PLUSTEK_OPTICFILM_8200I,      PLUSTEK_OPTICPRO_3600,      ST12,      ST24, @@ -356,6 +396,8 @@ enum class GpioId : unsigned      XP300,  }; +std::ostream& operator<<(std::ostream& out, GpioId id); +  enum class MotorId : unsigned  {      UNKNOWN = 0, @@ -365,9 +407,12 @@ enum class MotorId : unsigned      CANON_LIDE_200,      CANON_LIDE_210,      CANON_LIDE_35, +    CANON_LIDE_60,      CANON_LIDE_700,      CANON_LIDE_80, +    CANON_LIDE_90,      CANON_4400F, +    CANON_5600F,      CANON_8400F,      CANON_8600F,      DP665, @@ -380,9 +425,12 @@ enum class MotorId : unsigned      KVSS080,      MD_5345,      PLUSTEK_OPTICBOOK_3800, +    PLUSTEK_OPTICFILM_7200,      PLUSTEK_OPTICFILM_7200I,      PLUSTEK_OPTICFILM_7300, +    PLUSTEK_OPTICFILM_7400,      PLUSTEK_OPTICFILM_7500I, +    PLUSTEK_OPTICFILM_8200I,      PLUSTEK_OPTICPRO_3600,      ROADWARRIOR,      ST24, @@ -391,6 +439,8 @@ enum class MotorId : unsigned      XP300,  }; +std::ostream& operator<<(std::ostream& out, MotorId id); +  enum class StepType : unsigned  {      FULL = 0, @@ -423,6 +473,7 @@ enum class AsicType : unsigned      UNKNOWN = 0,      GL646,      GL841, +    GL842,      GL843,      GL845,      GL846, @@ -431,6 +482,92 @@ enum class AsicType : unsigned  }; +enum class ModelFlag : unsigned +{ +    // no flags +    NONE = 0, + +    // scanner is not tested, print a warning as it's likely it won't work +    UNTESTED = 1 << 0, + +    // use 14-bit gamma table instead of 12-bit +    GAMMA_14BIT = 1 << 1, + +    // perform lamp warmup +    WARMUP = 1 << 4, + +    // whether to disable offset and gain calibration +    DISABLE_ADC_CALIBRATION = 1 << 5, + +    // whether to disable exposure calibration (this currently is only done on CIS +    // scanners) +    DISABLE_EXPOSURE_CALIBRATION = 1 << 6, + +    // whether to disable shading calibration completely +    DISABLE_SHADING_CALIBRATION = 1 << 7, + +    // do dark calibration +    DARK_CALIBRATION = 1 << 8, + +    // host-side calibration uses a complete scan +    HOST_SIDE_CALIBRATION_COMPLETE_SCAN = 1 << 9, + +    // whether scanner must wait for the head while parking +    MUST_WAIT = 1 << 10, + +    // use zeroes for dark calibration +    USE_CONSTANT_FOR_DARK_CALIBRATION = 1 << 11, + +    // do dark and white calibration in one run +    DARK_WHITE_CALIBRATION = 1 << 12, + +    // allow custom gamma tables +    CUSTOM_GAMMA = 1 << 13, + +    // disable fast feeding mode on this scanner +    DISABLE_FAST_FEEDING = 1 << 14, + +    // the scanner uses multi-segment sensors that must be handled during calibration +    SIS_SENSOR = 1 << 16, + +    // the head must be reparked between shading scans +    SHADING_REPARK = 1 << 18, + +    // the scanner outputs inverted pixel data +    INVERT_PIXEL_DATA = 1 << 19, + +    // the scanner outputs 16-bit data that is byte-inverted +    SWAP_16BIT_DATA = 1 << 20, + +    // the scanner has transparency, but it's implemented using only one motor +    UTA_NO_SECONDARY_MOTOR = 1 << 21, + +    // the scanner has transparency, but it's implemented using only one lamp +    TA_NO_SECONDARY_LAMP = 1 << 22, +}; + +inline ModelFlag operator|(ModelFlag left, ModelFlag right) +{ +    return static_cast<ModelFlag>(static_cast<unsigned>(left) | static_cast<unsigned>(right)); +} + +inline ModelFlag& operator|=(ModelFlag& left, ModelFlag right) +{ +    left = left | right; +    return left; +} + +inline ModelFlag operator&(ModelFlag left, ModelFlag right) +{ +    return static_cast<ModelFlag>(static_cast<unsigned>(left) & static_cast<unsigned>(right)); +} + +inline bool has_flag(ModelFlag flags, ModelFlag which) +{ +    return (flags & which) == which; +} + +  enum class ScanFlag : unsigned  {      NONE = 0, @@ -438,14 +575,24 @@ enum class ScanFlag : unsigned      DISABLE_SHADING = 1 << 1,      DISABLE_GAMMA = 1 << 2,      DISABLE_BUFFER_FULL_MOVE = 1 << 3, -    IGNORE_LINE_DISTANCE = 1 << 4, -    DISABLE_LAMP = 1 << 5, -    CALIBRATION = 1 << 6, -    FEEDING = 1 << 7, -    USE_XPA = 1 << 8, -    ENABLE_LEDADD = 1 << 9, -    USE_XCORRECTION = 1 << 10, -    REVERSE = 1 << 11, + +    // if this flag is set the sensor will always be handled ignoring staggering of multiple +    // sensors to achieve high resolution. +    IGNORE_STAGGER_OFFSET = 1 << 4, + +    // if this flag is set the sensor will always be handled as if the components that scan +    // different colors are at the same position. +    IGNORE_COLOR_OFFSET = 1 << 5, + +    DISABLE_LAMP = 1 << 6, +    CALIBRATION = 1 << 7, +    FEEDING = 1 << 8, +    USE_XPA = 1 << 9, +    ENABLE_LEDADD = 1 << 10, +    REVERSE = 1 << 12, + +    // the scanner should return head to home position automatically after scan. +    AUTO_GO_HOME = 1 << 13,  };  inline ScanFlag operator|(ScanFlag left, ScanFlag right) @@ -485,45 +632,18 @@ inline void serialize(std::ostream& str, ScanFlag& x)  std::ostream& operator<<(std::ostream& out, ScanFlag flags); - -enum class MotorFlag : unsigned -{ -    NONE = 0, -    AUTO_GO_HOME = 1 << 0, -    DISABLE_BUFFER_FULL_MOVE = 1 << 2, -    FEED = 1 << 3, -    USE_XPA = 1 << 4, -    REVERSE = 1 << 5, -}; - -inline MotorFlag operator|(MotorFlag left, MotorFlag right) -{ -    return static_cast<MotorFlag>(static_cast<unsigned>(left) | static_cast<unsigned>(right)); -} - -inline MotorFlag& operator|=(MotorFlag& left, MotorFlag right) -{ -    left = left | right; -    return left; -} - -inline MotorFlag operator&(MotorFlag left, MotorFlag right) -{ -    return static_cast<MotorFlag>(static_cast<unsigned>(left) & static_cast<unsigned>(right)); -} - -inline bool has_flag(MotorFlag flags, MotorFlag which) -{ -    return (flags & which) == which; -} - -  enum class Direction : unsigned  {      FORWARD = 0,      BACKWARD = 1  }; +enum class MotorMode : unsigned +{ +    PRIMARY = 0, +    PRIMARY_AND_SECONDARY, +    SECONDARY, +};  } // namespace genesys diff --git a/backend/genesys/error.cpp b/backend/genesys/error.cpp index 6c921c1..46d79c9 100644 --- a/backend/genesys/error.cpp +++ b/backend/genesys/error.cpp @@ -45,6 +45,7 @@  #include "error.h"  #include <cstdarg> +#include <cstdlib>  namespace genesys { @@ -212,4 +213,32 @@ void DebugMessageHelper::vlog(unsigned level, const char* format, ...)      DBG(level, "%s: %s\n", func_, msg.c_str());  } +enum class LogImageDataStatus +{ +    NOT_SET, +    ENABLED, +    DISABLED +}; + +static LogImageDataStatus s_log_image_data_setting = LogImageDataStatus::NOT_SET; + +LogImageDataStatus dbg_read_log_image_data_setting() +{ +    auto* setting = std::getenv("SANE_DEBUG_GENESYS_IMAGE"); +    if (!setting) +        return LogImageDataStatus::DISABLED; +    auto setting_int = std::strtol(setting, nullptr, 10); +    if (setting_int == 0) +        return LogImageDataStatus::DISABLED; +    return LogImageDataStatus::ENABLED; +} + +bool dbg_log_image_data() +{ +    if (s_log_image_data_setting == LogImageDataStatus::NOT_SET) { +        s_log_image_data_setting = dbg_read_log_image_data_setting(); +    } +    return s_log_image_data_setting == LogImageDataStatus::ENABLED; +} +  } // namespace genesys diff --git a/backend/genesys/error.h b/backend/genesys/error.h index 5aba8cf..26235dd 100644 --- a/backend/genesys/error.h +++ b/backend/genesys/error.h @@ -137,7 +137,6 @@ private:      unsigned num_exceptions_on_enter_ = 0;  }; -  #if defined(__GNUC__) || defined(__clang__)  #define GENESYS_CURRENT_FUNCTION __PRETTY_FUNCTION__  #elif defined(__FUNCSIG__) @@ -149,6 +148,8 @@ private:  #define DBG_HELPER(var) DebugMessageHelper var(GENESYS_CURRENT_FUNCTION)  #define DBG_HELPER_ARGS(var, ...) DebugMessageHelper var(GENESYS_CURRENT_FUNCTION, __VA_ARGS__) +bool dbg_log_image_data(); +  template<class F>  SANE_Status wrap_exceptions_to_status_code(const char* func, F&& function)  { @@ -172,6 +173,27 @@ SANE_Status wrap_exceptions_to_status_code(const char* func, F&& function)  }  template<class F> +SANE_Status wrap_exceptions_to_status_code_return(const char* func, F&& function) +{ +    try { +        return function(); +    } catch (const SaneException& exc) { +        DBG(DBG_error, "%s: got error: %s\n", func, exc.what()); +        return exc.status(); +    } catch (const std::bad_alloc& exc) { +        (void) exc; +        DBG(DBG_error, "%s: failed to allocate memory\n", func); +        return SANE_STATUS_NO_MEM; +    } catch (const std::exception& exc) { +        DBG(DBG_error, "%s: got uncaught exception: %s\n", func, exc.what()); +        return SANE_STATUS_INVAL; +    } catch (...) { +        DBG(DBG_error, "%s: got unknown uncaught exception\n", func); +        return SANE_STATUS_INVAL; +    } +} + +template<class F>  void catch_all_exceptions(const char* func, F&& function)  {      try { diff --git a/backend/genesys/fwd.h b/backend/genesys/fwd.h index 2d55f98..ea335f7 100644 --- a/backend/genesys/fwd.h +++ b/backend/genesys/fwd.h @@ -46,9 +46,6 @@  namespace genesys { -// buffer.h -struct Genesys_Buffer; -  // calibration.h  struct Genesys_Calibration_Cache; @@ -56,7 +53,6 @@ struct Genesys_Calibration_Cache;  class CommandSet;  // device.h -class FixedFloat;  struct Genesys_Gpo;  struct MethodResolutions;  struct Genesys_Model; @@ -75,8 +71,6 @@ class Image;  // image_buffer.h  class ImageBuffer; -class FakeBufferModel; -class ImageBufferGenesysUsb;  // image_pipeline.h  class ImagePipelineNode; @@ -88,12 +82,12 @@ struct Pixel;  struct RawPixel;  // low.h -struct Genesys_USB_Device_Entry; -struct Motor_Profile; +struct UsbDeviceEntry;  // motor.h  struct Genesys_Motor;  struct MotorSlope; +struct MotorProfile;  struct MotorSlopeTable;  // register.h @@ -113,7 +107,6 @@ class ScannerInterfaceUsb;  class TestScannerInterface;  // sensor.h -class ResolutionFilter;  struct GenesysFrontendLayout;  struct Genesys_Frontend;  struct SensorExposure; @@ -124,6 +117,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; diff --git a/backend/genesys/genesys.cpp b/backend/genesys/genesys.cpp index 7c25168..9d80cfa 100644 --- a/backend/genesys/genesys.cpp +++ b/backend/genesys/genesys.cpp @@ -61,9 +61,9 @@  #define DEBUG_NOT_STATIC  #include "genesys.h" -#include "conv.h"  #include "gl124_registers.h"  #include "gl841_registers.h" +#include "gl842_registers.h"  #include "gl843_registers.h"  #include "gl846_registers.h"  #include "gl847_registers.h" @@ -73,7 +73,6 @@  #include "test_scanner_interface.h"  #include "test_settings.h"  #include "../include/sane/sanei_config.h" -#include "../include/sane/sanei_magic.h"  #include <array>  #include <cmath> @@ -111,8 +110,8 @@ namespace {  static SANE_String_Const mode_list[] = {    SANE_VALUE_SCAN_MODE_COLOR,    SANE_VALUE_SCAN_MODE_GRAY, -  /* SANE_TITLE_HALFTONE,  currently unused */ -  SANE_VALUE_SCAN_MODE_LINEART, +    // SANE_TITLE_HALFTONE, not used +    // SANE_VALUE_SCAN_MODE_LINEART, not used      nullptr  }; @@ -131,12 +130,6 @@ static SANE_String_Const cis_color_filter_list[] = {      nullptr  }; -static SANE_Range swdespeck_range = { -  1, -  9, -  1 -}; -  static SANE_Range time_range = {    0,				/* minimum */    60,				/* maximum */ @@ -162,15 +155,9 @@ static const SANE_Range u16_range = {  };  static const SANE_Range percentage_range = { -  SANE_FIX (0),			/* minimum */ -  SANE_FIX (100),		/* maximum */ -  SANE_FIX (1)			/* quantization */ -}; - -static const SANE_Range threshold_curve_range = { -  0,			/* minimum */ -  127,		        /* maximum */ -  1			/* quantization */ +    float_to_fixed(0),     // minimum +    float_to_fixed(100),   // maximum +    float_to_fixed(1)      // quantization  };  /** @@ -191,7 +178,7 @@ static const SANE_Range expiration_range = {    1		/* quantization */  }; -const Genesys_Sensor& sanei_genesys_find_sensor_any(Genesys_Device* dev) +const Genesys_Sensor& sanei_genesys_find_sensor_any(const Genesys_Device* dev)  {      DBG_HELPER(dbg);      for (const auto& sensor : *s_sensors) { @@ -202,7 +189,7 @@ const Genesys_Sensor& sanei_genesys_find_sensor_any(Genesys_Device* dev)      throw std::runtime_error("Given device does not have sensor defined");  } -Genesys_Sensor* find_sensor_impl(Genesys_Device* dev, unsigned dpi, unsigned channels, +Genesys_Sensor* find_sensor_impl(const Genesys_Device* dev, unsigned dpi, unsigned channels,                                   ScanMethod scan_method)  {      DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels, @@ -217,7 +204,7 @@ Genesys_Sensor* find_sensor_impl(Genesys_Device* dev, unsigned dpi, unsigned cha      return nullptr;  } -bool sanei_genesys_has_sensor(Genesys_Device* dev, unsigned dpi, unsigned channels, +bool sanei_genesys_has_sensor(const Genesys_Device* dev, unsigned dpi, unsigned channels,                                ScanMethod scan_method)  {      DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels, @@ -225,8 +212,8 @@ bool sanei_genesys_has_sensor(Genesys_Device* dev, unsigned dpi, unsigned channe      return find_sensor_impl(dev, dpi, channels, scan_method) != nullptr;  } -const Genesys_Sensor& sanei_genesys_find_sensor(Genesys_Device* dev, unsigned dpi, unsigned channels, -                                                ScanMethod scan_method) +const Genesys_Sensor& sanei_genesys_find_sensor(const Genesys_Device* dev, unsigned dpi, +                                                unsigned channels, ScanMethod scan_method)  {      DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,                      static_cast<unsigned>(scan_method)); @@ -250,12 +237,14 @@ Genesys_Sensor& sanei_genesys_find_sensor_for_write(Genesys_Device* dev, unsigne  std::vector<std::reference_wrapper<const Genesys_Sensor>> -    sanei_genesys_find_sensors_all(Genesys_Device* dev, ScanMethod scan_method) +    sanei_genesys_find_sensors_all(const Genesys_Device* dev, ScanMethod scan_method)  {      DBG_HELPER_ARGS(dbg, "scan_method: %d", static_cast<unsigned>(scan_method));      std::vector<std::reference_wrapper<const Genesys_Sensor>> ret; -    for (const Genesys_Sensor& sensor : sanei_genesys_find_sensors_all_for_write(dev, scan_method)) { -        ret.push_back(sensor); +    for (auto& sensor : *s_sensors) { +        if (dev->model->sensor_id == sensor.sensor_id && sensor.method == scan_method) { +            ret.push_back(sensor); +        }      }      return ret;  } @@ -308,6 +297,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), @@ -316,33 +323,6 @@ void sanei_genesys_init_structs (Genesys_Device * dev)      }  } -/* Generate slope table for motor movement */ -/** - * This function generates a slope table using the slope from the motor struct - * truncated at the given exposure time or step count, whichever comes first. - * The summed time of the acceleration steps is returned, and the - * number of accerelation steps is put into used_steps. - * - * @param dev            Device struct - * @param slope_table    Table to write to - * @param step_type      Generate table for this step_type. 0=>full, 1=>half, - *                       2=>quarter - * @param exposure_time  Minimum exposure time of a scan line - * @param yres           Resolution of a scan line - * @param used_steps     Final number of steps is stored here - * @return               Motor slope table - * @note  all times in pixel time - */ -MotorSlopeTable sanei_genesys_create_slope_table3(AsicType asic_type, const Genesys_Motor& motor, -                                                  StepType step_type, int exposure_time, -                                                  unsigned yres) -{ -    unsigned target_speed_w = (exposure_time * yres) / motor.base_ydpi; - -    return create_slope_table(motor.get_slope(step_type), target_speed_w, step_type, 1, 1, -                              get_slope_table_max_size(asic_type)); -} -  /** @brief computes gamma table   * Generates a gamma table of the given length within 0 and the given   * maximum value @@ -382,7 +362,7 @@ void sanei_genesys_create_default_gamma_table(Genesys_Device* dev,      int size = 0;      int max = 0;      if (dev->model->asic_type == AsicType::GL646) { -        if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) { +        if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) {              size = 16384;          } else {              size = 4096; @@ -411,34 +391,30 @@ void sanei_genesys_create_default_gamma_table(Genesys_Device* dev,      Note: The enhance option of the scanners does _not_ help. It only halves            the amount of pixels transfered.   */ -SANE_Int sanei_genesys_exposure_time2(Genesys_Device * dev, float ydpi, -                                      StepType step_type, int endpixel, int exposure_by_led) +SANE_Int sanei_genesys_exposure_time2(Genesys_Device * dev, const MotorProfile& profile, float ydpi, +                                      int endpixel, int exposure_by_led)  {    int exposure_by_ccd = endpixel + 32; -    unsigned max_speed_motor_w = dev->motor.get_slope(step_type).max_speed_w; +    unsigned max_speed_motor_w = profile.slope.max_speed_w;      int exposure_by_motor = static_cast<int>((max_speed_motor_w * dev->motor.base_ydpi) / ydpi);    int exposure = exposure_by_ccd; -  if (exposure < exposure_by_motor) -    exposure = exposure_by_motor; +    if (exposure < exposure_by_motor) { +        exposure = exposure_by_motor; +    } -  if (exposure < exposure_by_led && dev->model->is_cis) -    exposure = exposure_by_led; +    if (exposure < exposure_by_led && dev->model->is_cis) { +        exposure = exposure_by_led; +    } -    DBG(DBG_info, "%s: ydpi=%d, step=%d, endpixel=%d led=%d => exposure=%d\n", __func__, -        static_cast<int>(ydpi), static_cast<unsigned>(step_type), endpixel, -        exposure_by_led, exposure); -  return exposure; +    return exposure;  }  /* Sends a block of shading information to the scanner.     The data is placed at address 0x0000 for color mode, gray mode and     unconditionally for the following CCD chips: HP2300, HP2400 and HP5345 -   In the other cases (lineart, halftone on ccd chips not mentioned) the -   addresses are 0x2a00 for dpihw==0, 0x5500 for dpihw==1 and 0xa800 for -   dpihw==2. //Note: why this?     The data needs to be of size "size", and in little endian byte order.   */ @@ -446,7 +422,6 @@ static void genesys_send_offset_and_shading(Genesys_Device* dev, const Genesys_S                                              uint8_t* data, int size)  {      DBG_HELPER_ARGS(dbg, "(size = %d)", size); -  int dpihw;    int start_address;    /* ASIC higher than gl843 doesn't have register 2A/2B, so we route to @@ -457,84 +432,30 @@ static void genesys_send_offset_and_shading(Genesys_Device* dev, const Genesys_S          return;      } -  /* gl646, gl84[123] case */ -  dpihw = dev->reg.get8(0x05) >> 6; - -  /* TODO invert the test so only the 2 models behaving like that are -   * tested instead of adding all the others */ -  /* many scanners send coefficient for lineart/gray like in color mode */ -  if ((dev->settings.scan_mode == ScanColorMode::LINEART || -       dev->settings.scan_mode == ScanColorMode::HALFTONE) -        && dev->model->sensor_id != SensorId::CCD_PLUSTEK_OPTICBOOK_3800 -        && dev->model->sensor_id != SensorId::CCD_KVSS080 -        && dev->model->sensor_id != SensorId::CCD_G4050 -        && dev->model->sensor_id != SensorId::CCD_HP_4850C -        && dev->model->sensor_id != SensorId::CCD_CANON_4400F -        && dev->model->sensor_id != SensorId::CCD_CANON_8400F -        && dev->model->sensor_id != SensorId::CCD_CANON_8600F -        && dev->model->sensor_id != SensorId::CCD_DSMOBILE600 -        && dev->model->sensor_id != SensorId::CCD_XP300 -        && dev->model->sensor_id != SensorId::CCD_DP665 -        && dev->model->sensor_id != SensorId::CCD_DP685 -        && dev->model->sensor_id != SensorId::CIS_CANON_LIDE_80 -        && dev->model->sensor_id != SensorId::CCD_ROADWARRIOR -        && dev->model->sensor_id != SensorId::CCD_HP2300 -        && dev->model->sensor_id != SensorId::CCD_HP2400 -        && dev->model->sensor_id != SensorId::CCD_HP3670 -        && dev->model->sensor_id != SensorId::CCD_5345)	/* lineart, halftone */ -    { -        if (dpihw == 0) {		/* 600 dpi */ -            start_address = 0x02a00; -        } else if (dpihw == 1) {	/* 1200 dpi */ -            start_address = 0x05500; -        } else if (dpihw == 2) {	/* 2400 dpi */ -            start_address = 0x0a800; -        } else {			/* reserved */ -            throw SaneException("unknown dpihw"); -        } -    } -    else { // color -        start_address = 0x00; -    } +    start_address = 0x00;      dev->interface->write_buffer(0x3c, start_address, data, size);  } -// ?  void sanei_genesys_init_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,                                       int pixels_per_line)  {      DBG_HELPER_ARGS(dbg, "pixels_per_line: %d", pixels_per_line); -    if (dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE) { -        return; -    } - -  int channels; -  int i; -      if (dev->cmd_set->has_send_shading_data()) {          return;      }    DBG(DBG_proc, "%s (pixels_per_line = %d)\n", __func__, pixels_per_line); -    // BUG: GRAY shouldn't probably be in the if condition below. Discovered when refactoring -    if (dev->settings.scan_mode == ScanColorMode::GRAY || -        dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) -    { -        channels = 3; -    } else { -        channels = 1; -    } +    unsigned channels = dev->settings.get_channels();    // 16 bit black, 16 bit white    std::vector<uint8_t> shading_data(pixels_per_line * 4 * channels, 0);    uint8_t* shading_data_ptr = shading_data.data(); -  for (i = 0; i < pixels_per_line * channels; i++) -    { +    for (unsigned i = 0; i < pixels_per_line * channels; i++) {        *shading_data_ptr++ = 0x00;	/* dark lo */        *shading_data_ptr++ = 0x00;	/* dark hi */        *shading_data_ptr++ = 0x00;	/* white lo */ @@ -545,184 +466,6 @@ void sanei_genesys_init_shading_data(Genesys_Device* dev, const Genesys_Sensor&                                      pixels_per_line * 4 * channels);  } - -// Find the position of the reference point: takes gray level 8 bits data and find -// first CCD usable pixel and top of scanning area -void sanei_genesys_search_reference_point(Genesys_Device* dev, Genesys_Sensor& sensor, -                                          const uint8_t* src_data, int start_pixel, int dpi, -                                          int width, int height) -{ -    DBG_HELPER(dbg); -  int x, y; -  int current, left, top = 0; -  int size, count; -  int level = 80;		/* edge threshold level */ - -    // sanity check -    if ((width < 3) || (height < 3)) { -        throw SaneException("invalid width or height"); -    } - -  /* transformed image data */ -  size = width * height; -  std::vector<uint8_t> image2(size, 0); -  std::vector<uint8_t> image(size, 0); - -  /* laplace filter to denoise picture */ -    std::memcpy(image2.data(), src_data, size); -    std::memcpy(image.data(), src_data, size);	// to initialize unprocessed part of the image buffer - -    for (y = 1; y < height - 1; y++) { -        for (x = 1; x < width - 1; x++) { -            image[y * width + x] = -                (image2[(y - 1) * width + x + 1] + 2 * image2[(y - 1) * width + x] + -                image2[(y - 1) * width + x - 1] + 2 * image2[y * width + x + 1] + -                4 * image2[y * width + x] + 2 * image2[y * width + x - 1] + -                image2[(y + 1) * width + x + 1] + 2 * image2[(y + 1) * width + x] + -                image2[(y + 1) * width + x - 1]) / 16; -        } -    } - -    image2 = image; -  if (DBG_LEVEL >= DBG_data) -    sanei_genesys_write_pnm_file("gl_laplace.pnm", image.data(), 8, 1, width, height); - -  /* apply X direction sobel filter -     -1  0  1 -     -2  0  2 -     -1  0  1 -     and finds threshold level -   */ -  level = 0; -    for (y = 2; y < height - 2; y++) { -        for (x = 2; x < width - 2; x++) { -            current = image2[(y - 1) * width + x + 1] - image2[(y - 1) * width + x - 1] + -                      2 * image2[y * width + x + 1] - 2 * image2[y * width + x - 1] + -                      image2[(y + 1) * width + x + 1] - image2[(y + 1) * width + x - 1]; -	if (current < 0) -	  current = -current; -	if (current > 255) -	  current = 255; -	image[y * width + x] = current; -	if (current > level) -	  level = current; -        } -    } -  if (DBG_LEVEL >= DBG_data) -    sanei_genesys_write_pnm_file("gl_xsobel.pnm", image.data(), 8, 1, width, height); - -  /* set up detection level */ -  level = level / 3; - -  /* find left black margin first -     todo: search top before left -     we average the result of N searches */ -  left = 0; -  count = 0; -  for (y = 2; y < 11; y++) -    { -      x = 8; -      while ((x < width / 2) && (image[y * width + x] < level)) -	{ -	  image[y * width + x] = 255; -	  x++; -	} -      count++; -      left += x; -    } -  if (DBG_LEVEL >= DBG_data) -    sanei_genesys_write_pnm_file("gl_detected-xsobel.pnm", image.data(), 8, 1, width, height); -  left = left / count; - -    // turn it in CCD pixel at full sensor optical resolution -    sensor.ccd_start_xoffset = start_pixel + (left * sensor.optical_res) / dpi; - -  /* find top edge by detecting black strip */ -  /* apply Y direction sobel filter -     -1 -2 -1 -     0  0  0 -     1  2  1 -   */ -  level = 0; -    for (y = 2; y < height - 2; y++) { -        for (x = 2; x < width - 2; x++) { -            current = -image2[(y - 1) * width + x + 1] - 2 * image2[(y - 1) * width + x] - -                      image2[(y - 1) * width + x - 1] + image2[(y + 1) * width + x + 1] + -                      2 * image2[(y + 1) * width + x] + image2[(y + 1) * width + x - 1]; -	if (current < 0) -	  current = -current; -	if (current > 255) -	  current = 255; -	image[y * width + x] = current; -	if (current > level) -	  level = current; -      } -    } -  if (DBG_LEVEL >= DBG_data) -    sanei_genesys_write_pnm_file("gl_ysobel.pnm", image.data(), 8, 1, width, height); - -  /* set up detection level */ -  level = level / 3; - -  /* search top of horizontal black stripe : TODO yet another flag */ -    if (dev->model->sensor_id == SensorId::CCD_5345 -        && dev->model->motor_id == MotorId::MD_5345) -    { -      top = 0; -      count = 0; -      for (x = width / 2; x < width - 1; x++) -	{ -	  y = 2; -	  while ((y < height) && (image[x + y * width] < level)) -	    { -	      image[y * width + x] = 255; -	      y++; -	    } -	  count++; -	  top += y; -	} -      if (DBG_LEVEL >= DBG_data) -        sanei_genesys_write_pnm_file("gl_detected-ysobel.pnm", image.data(), 8, 1, width, height); -      top = top / count; - -      /* bottom of black stripe is of fixed witdh, this hardcoded value -       * will be moved into device struct if more such values are needed */ -      top += 10; -        dev->model->y_offset_calib_white = (top * MM_PER_INCH) / dpi; -        DBG(DBG_info, "%s: black stripe y_offset = %f mm \n", __func__, -            dev->model->y_offset_calib_white.value()); -    } - -  /* find white corner in dark area : TODO yet another flag */ -    if ((dev->model->sensor_id == SensorId::CCD_HP2300 && dev->model->motor_id == MotorId::HP2300) || -        (dev->model->sensor_id == SensorId::CCD_HP2400 && dev->model->motor_id == MotorId::HP2400) || -        (dev->model->sensor_id == SensorId::CCD_HP3670 && dev->model->motor_id == MotorId::HP3670)) -    { -      top = 0; -      count = 0; -      for (x = 10; x < 60; x++) -	{ -	  y = 2; -	  while ((y < height) && (image[x + y * width] < level)) -	    y++; -	  top += y; -	  count++; -	} -      top = top / count; -        dev->model->y_offset_calib_white = (top * MM_PER_INCH) / dpi; -        DBG(DBG_info, "%s: white corner y_offset = %f mm\n", __func__, -            dev->model->y_offset_calib_white.value()); -    } - -    DBG(DBG_proc, "%s: ccd_start_xoffset = %d, left = %d, top = %d\n", __func__, -        sensor.ccd_start_xoffset, left, top); -} - -namespace gl843 { -    void gl843_park_xpa_lamp(Genesys_Device* dev); -    void gl843_set_xpa_motor_power(Genesys_Device* dev, Genesys_Register_Set& regs, bool set); -} // namespace gl843 -  namespace gl124 {      void gl124_setup_scan_gpio(Genesys_Device* dev, int resolution);  } // namespace gl124 @@ -730,6 +473,16 @@ namespace gl124 {  void scanner_clear_scan_and_feed_counts(Genesys_Device& dev)  {      switch (dev.model->asic_type) { +        case AsicType::GL841: { +            dev.interface->write_register(gl841::REG_0x0D, +                                          gl841::REG_0x0D_CLRLNCNT); +            break; +        } +        case AsicType::GL842: { +            dev.interface->write_register(gl842::REG_0x0D, +                                          gl842::REG_0x0D_CLRLNCNT); +            break; +        }          case AsicType::GL843: {              dev.interface->write_register(gl843::REG_0x0D,                                            gl843::REG_0x0D_CLRLNCNT | gl843::REG_0x0D_CLRMCNT); @@ -756,34 +509,107 @@ void scanner_clear_scan_and_feed_counts(Genesys_Device& dev)      }  } -void scanner_clear_scan_and_feed_counts2(Genesys_Device& dev) +void scanner_send_slope_table(Genesys_Device* dev, const Genesys_Sensor& sensor, unsigned table_nr, +                              const std::vector<uint16_t>& slope_table)  { -    // FIXME: switch to scanner_clear_scan_and_feed_counts when updating tests -    switch (dev.model->asic_type) { -        case AsicType::GL843: { -            dev.interface->write_register(gl843::REG_0x0D, gl843::REG_0x0D_CLRLNCNT); -            dev.interface->write_register(gl843::REG_0x0D, gl843::REG_0x0D_CLRMCNT); +    DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %zu", table_nr, slope_table.size()); + +    unsigned max_table_nr = 0; +    switch (dev->model->asic_type) { +        case AsicType::GL646: { +            max_table_nr = 2;              break;          } +        case AsicType::GL841: +        case AsicType::GL842: +        case AsicType::GL843:          case AsicType::GL845: -        case AsicType::GL846: { -            dev.interface->write_register(gl846::REG_0x0D, gl846::REG_0x0D_CLRLNCNT); -            dev.interface->write_register(gl846::REG_0x0D, gl846::REG_0x0D_CLRMCNT); +        case AsicType::GL846: +        case AsicType::GL847: +        case AsicType::GL124: { +            max_table_nr = 4;              break;          } -        case AsicType::GL847: { -            dev.interface->write_register(gl847::REG_0x0D, gl847::REG_0x0D_CLRLNCNT); -            dev.interface->write_register(gl847::REG_0x0D, gl847::REG_0x0D_CLRMCNT); +        default: +            throw SaneException("Unsupported ASIC type"); +    } + +    if (table_nr > max_table_nr) { +        throw SaneException("invalid table number %d", table_nr); +    } + +    std::vector<uint8_t> table; +    table.reserve(slope_table.size() * 2); +    for (std::size_t i = 0; i < slope_table.size(); i++) { +        table.push_back(slope_table[i] & 0xff); +        table.push_back(slope_table[i] >> 8); +    } +    if (dev->model->asic_type == AsicType::GL841 || +        dev->model->model_id == ModelId::CANON_LIDE_90) +    { +        // BUG: do this on all gl842 scanners +        auto max_table_size = get_slope_table_max_size(dev->model->asic_type); +        table.reserve(max_table_size * 2); +        while (table.size() < max_table_size * 2) { +            table.push_back(slope_table.back() & 0xff); +            table.push_back(slope_table.back() >> 8); +        } +    } + +    if (dev->interface->is_mock()) { +        dev->interface->record_slope_table(table_nr, slope_table); +    } + +    switch (dev->model->asic_type) { +        case AsicType::GL646: { +            unsigned dpihw = dev->reg.find_reg(0x05).value >> 6; +            unsigned start_address = 0; +            if (dpihw == 0) { // 600 dpi +                start_address = 0x08000; +            } else if (dpihw == 1) { // 1200 dpi +                start_address = 0x10000; +            } else if (dpihw == 2) { // 2400 dpi +                start_address = 0x1f800; +            } else { +                throw SaneException("Unexpected dpihw"); +            } +            dev->interface->write_buffer(0x3c, start_address + table_nr * 0x100, table.data(), +                                         table.size());              break;          } +        case AsicType::GL841: +        case AsicType::GL842: { +            unsigned start_address = 0; +            switch (sensor.register_dpihw) { +                case 600: start_address = 0x08000; break; +                case 1200: start_address = 0x10000; break; +                case 2400: start_address = 0x20000; break; +                default: throw SaneException("Unexpected dpihw"); +            } +            dev->interface->write_buffer(0x3c, start_address + table_nr * 0x200, table.data(), +                                         table.size()); +            break; +        } +        case AsicType::GL843: { +            // slope table addresses are fixed : 0x40000,  0x48000,  0x50000,  0x58000,  0x60000 +            // XXX STEF XXX USB 1.1 ? sanei_genesys_write_0x8c (dev, 0x0f, 0x14); +            dev->interface->write_gamma(0x28,  0x40000 + 0x8000 * table_nr, table.data(), +                                        table.size()); +            break; +        } +        case AsicType::GL845: +        case AsicType::GL846: +        case AsicType::GL847:          case AsicType::GL124: { -            dev.interface->write_register(gl124::REG_0x0D, gl124::REG_0x0D_CLRLNCNT); -            dev.interface->write_register(gl124::REG_0x0D, gl124::REG_0x0D_CLRMCNT); +            // slope table addresses are fixed +            dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, table.size(), +                                      table.data());              break;          }          default: -            throw SaneException("Unsupported asic type"); +            throw SaneException("Unsupported ASIC type");      } +  }  bool scanner_is_motor_stopped(Genesys_Device& dev) @@ -794,9 +620,18 @@ bool scanner_is_motor_stopped(Genesys_Device& dev)              return !status.is_motor_enabled && status.is_feeding_finished;          }          case AsicType::GL841: { +            auto status = scanner_read_status(dev);              auto reg = dev.interface->read_register(gl841::REG_0x40); -            return (!(reg & gl841::REG_0x40_DATAENB) && !(reg & gl841::REG_0x40_MOTMFLG)); +            return (!(reg & gl841::REG_0x40_DATAENB) && !(reg & gl841::REG_0x40_MOTMFLG) && +                    !status.is_motor_enabled); +        } +        case AsicType::GL842: { +            auto status = scanner_read_status(dev); +            auto reg = dev.interface->read_register(gl842::REG_0x40); + +            return (!(reg & gl842::REG_0x40_DATAENB) && !(reg & gl842::REG_0x40_MOTMFLG) && +                    !status.is_motor_enabled);          }          case AsicType::GL843: {              auto status = scanner_read_status(dev); @@ -832,11 +667,31 @@ bool scanner_is_motor_stopped(Genesys_Device& dev)      }  } +void scanner_setup_sensor(Genesys_Device& dev, const Genesys_Sensor& sensor, +                          Genesys_Register_Set& regs) +{ +    DBG_HELPER(dbg); + +    for (const auto& custom_reg : sensor.custom_regs) { +        regs.set8(custom_reg.address, custom_reg.value); +    } + +    if (dev.model->asic_type != AsicType::GL841 && +        dev.model->asic_type != AsicType::GL843) +    { +        regs_set_exposure(dev.model->asic_type, regs, sensor.exposure); +    } + +    dev.segment_order = sensor.segment_order; +} +  void scanner_stop_action(Genesys_Device& dev)  {      DBG_HELPER(dbg);      switch (dev.model->asic_type) { +        case AsicType::GL841: +        case AsicType::GL842:          case AsicType::GL843:          case AsicType::GL845:          case AsicType::GL846: @@ -847,9 +702,7 @@ void scanner_stop_action(Genesys_Device& dev)              throw SaneException("Unsupported asic type");      } -    if (dev.cmd_set->needs_update_home_sensor_gpio()) { -        dev.cmd_set->update_home_sensor_gpio(dev); -    } +    dev.cmd_set->update_home_sensor_gpio(dev);      if (scanner_is_motor_stopped(dev)) {          DBG(DBG_info, "%s: already stopped\n", __func__); @@ -878,6 +731,7 @@ void scanner_stop_action_no_move(Genesys_Device& dev, genesys::Genesys_Register_      switch (dev.model->asic_type) {          case AsicType::GL646:          case AsicType::GL841: +        case AsicType::GL842:          case AsicType::GL843:          case AsicType::GL845:          case AsicType::GL846: @@ -908,7 +762,9 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D      const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 3, scan_method);      bool uses_secondary_head = (scan_method == ScanMethod::TRANSPARENCY || -                                scan_method == ScanMethod::TRANSPARENCY_INFRARED); +                                scan_method == ScanMethod::TRANSPARENCY_INFRARED) && +                               (!has_flag(dev.model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR)); +      bool uses_secondary_pos = uses_secondary_head &&                                dev.model->default_method == ScanMethod::FLATBED; @@ -934,21 +790,19 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D      session.params.yres = resolution;      session.params.startx = 0;      session.params.starty = steps; -    session.params.pixels = 100; +    session.params.pixels = 50;      session.params.lines = 3;      session.params.depth = 8; -    session.params.channels = 3; +    session.params.channels = 1;      session.params.scan_method = scan_method; -    session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; -    if (dev.model->asic_type == AsicType::GL843) { -        session.params.color_filter = ColorFilter::RED; -    } else { -        session.params.color_filter = dev.settings.color_filter; -    } +    session.params.scan_mode = ScanColorMode::GRAY; +    session.params.color_filter = ColorFilter::GREEN; +      session.params.flags = ScanFlag::DISABLE_SHADING |                             ScanFlag::DISABLE_GAMMA |                             ScanFlag::FEEDING | -                           ScanFlag::IGNORE_LINE_DISTANCE; +                           ScanFlag::IGNORE_STAGGER_OFFSET | +                           ScanFlag::IGNORE_COLOR_OFFSET;      if (dev.model->asic_type == AsicType::GL124) {          session.params.flags |= ScanFlag::DISABLE_BUFFER_FULL_MOVE; @@ -963,20 +817,21 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D      dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session);      if (dev.model->asic_type != AsicType::GL843) { -        regs_set_exposure(dev.model->asic_type, local_reg, {0, 0, 0}); +        regs_set_exposure(dev.model->asic_type, local_reg, +                          sanei_genesys_fixup_exposure({0, 0, 0}));      } -    scanner_clear_scan_and_feed_counts2(dev); +    scanner_clear_scan_and_feed_counts(dev);      dev.interface->write_registers(local_reg);      if (uses_secondary_head) { -        gl843::gl843_set_xpa_motor_power(&dev, local_reg, true); +        dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY_AND_SECONDARY);      }      try {          scanner_start_action(dev, true);      } catch (...) {          catch_all_exceptions(__func__, [&]() { -            gl843::gl843_set_xpa_motor_power(&dev, local_reg, false); +            dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);          });          catch_all_exceptions(__func__, [&]() { scanner_stop_action(dev); });          // restore original registers @@ -992,17 +847,18 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D              dev.advance_head_pos_by_steps(ScanHeadId::SECONDARY, direction, steps);          } -        // FIXME: why don't we stop the scanner like on other ASICs -        if (dev.model->asic_type != AsicType::GL843) { -            scanner_stop_action(dev); -        } +        scanner_stop_action(dev);          if (uses_secondary_head) { -            gl843::gl843_set_xpa_motor_power(&dev, local_reg, false); +            dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);          }          return;      }      // wait until feed count reaches the required value +    if (dev.model->model_id == ModelId::CANON_LIDE_700F) { +        dev.cmd_set->update_home_sensor_gpio(dev); +    } +      // FIXME: should porbably wait for some timeout      Status status;      for (unsigned i = 0;; ++i) { @@ -1015,12 +871,9 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D          dev.interface->sleep_ms(10);      } -    // FIXME: why don't we stop the scanner like on other ASICs -    if (dev.model->asic_type != AsicType::GL843) { -        scanner_stop_action(dev); -    } +    scanner_stop_action(dev);      if (uses_secondary_head) { -        gl843::gl843_set_xpa_motor_power(&dev, local_reg, false); +        dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);      }      dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, direction, steps); @@ -1032,11 +885,22 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D      dev.interface->sleep_ms(100);  } +void scanner_move_to_ta(Genesys_Device& dev) +{ +    DBG_HELPER(dbg); + +    unsigned feed = static_cast<unsigned>((dev.model->y_offset_sensor_to_ta * dev.motor.base_ydpi) / +                                           MM_PER_INCH); +    scanner_move(dev, dev.model->default_method, feed, Direction::FORWARD); +} +  void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)  {      DBG_HELPER_ARGS(dbg, "wait_until_home = %d", wait_until_home);      switch (dev.model->asic_type) { +        case AsicType::GL841: +        case AsicType::GL842:          case AsicType::GL843:          case AsicType::GL845:          case AsicType::GL846: @@ -1047,11 +911,17 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)              throw SaneException("Unsupported asic type");      } +    if (dev.model->is_sheetfed) { +        dbg.vlog(DBG_proc, "sheetfed scanner, skipping going back home"); +        return; +    } +      // FIXME: also check whether the scanner actually has a secondary head -    if (!dev.is_head_pos_known(ScanHeadId::SECONDARY) || +    if ((!dev.is_head_pos_known(ScanHeadId::SECONDARY) ||          dev.head_pos(ScanHeadId::SECONDARY) > 0 ||          dev.settings.scan_method == ScanMethod::TRANSPARENCY || -        dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +        dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) && +            (!has_flag(dev.model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR)))      {          scanner_move_back_home_ta(dev);      } @@ -1064,9 +934,7 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)                       Direction::BACKWARD);      } -    if (dev.cmd_set->needs_update_home_sensor_gpio()) { -        dev.cmd_set->update_home_sensor_gpio(dev); -    } +    dev.cmd_set->update_home_sensor_gpio(dev);      auto status = scanner_read_reliable_status(dev); @@ -1076,15 +944,6 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)          return;      } -    if (dev.model->model_id == ModelId::CANON_LIDE_210) { -        // move the head back a little first -        if (dev.is_head_pos_known(ScanHeadId::PRIMARY) && -            dev.head_pos(ScanHeadId::PRIMARY) > 30) -        { -            scanner_move(dev, dev.model->default_method, 20, Direction::BACKWARD); -        } -    } -      Genesys_Register_Set local_reg = dev.reg;      unsigned resolution = sanei_genesys_get_lowest_ydpi(&dev); @@ -1093,28 +952,22 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)      ScanSession session;      session.params.xres = resolution;      session.params.yres = resolution; -    session.params.startx = 100; -    if (dev.model->asic_type == AsicType::GL843) { -        session.params.starty = 40000; -    } else { -        session.params.starty = 30000; -    } -    session.params.pixels = 100; -    session.params.lines = 100; +    session.params.startx = 0; +    session.params.starty = 40000; +    session.params.pixels = 50; +    session.params.lines = 3;      session.params.depth = 8;      session.params.channels = 1;      session.params.scan_method = dev.settings.scan_method; -    if (dev.model->asic_type == AsicType::GL843) { -        session.params.scan_mode = ScanColorMode::LINEART; -        session.params.color_filter = dev.settings.color_filter; -    } else { -        session.params.scan_mode = ScanColorMode::GRAY; -        session.params.color_filter = ColorFilter::RED; -    } +    session.params.scan_mode = ScanColorMode::GRAY; +    session.params.color_filter = ColorFilter::GREEN; +      session.params.flags =  ScanFlag::DISABLE_SHADING |                              ScanFlag::DISABLE_GAMMA | -                            ScanFlag::IGNORE_LINE_DISTANCE | +                            ScanFlag::IGNORE_STAGGER_OFFSET | +                            ScanFlag::IGNORE_COLOR_OFFSET |                              ScanFlag::REVERSE; +      if (dev.model->asic_type == AsicType::GL843) {          session.params.flags |= ScanFlag::DISABLE_BUFFER_FULL_MOVE;      } @@ -1143,9 +996,7 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)          throw;      } -    if (dev.cmd_set->needs_update_home_sensor_gpio()) { -        dev.cmd_set->update_home_sensor_gpio(dev); -    } +    dev.cmd_set->update_home_sensor_gpio(dev);      if (is_testing_mode()) {          dev.interface->test_checkpoint("move_back_home"); @@ -1174,18 +1025,49 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)          // when we come here then the scanner needed too much time for this, so we better stop          // the motor          catch_all_exceptions(__func__, [&](){ scanner_stop_action(dev); }); -        dev.set_head_pos_unknown(); +        dev.set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY);          throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home");      }      dbg.log(DBG_info, "scanhead is still moving");  } +namespace { +    bool should_use_secondary_motor_mode(Genesys_Device& dev) +    { +        bool should_use = !dev.is_head_pos_known(ScanHeadId::SECONDARY) || +                          !dev.is_head_pos_known(ScanHeadId::PRIMARY) || +                          dev.head_pos(ScanHeadId::SECONDARY) > dev.head_pos(ScanHeadId::PRIMARY); +        bool supports = dev.model->model_id == ModelId::CANON_8600F; +        return should_use && supports; +    } + +    void handle_motor_position_after_move_back_home_ta(Genesys_Device& dev, MotorMode motor_mode) +    { +        if (motor_mode == MotorMode::SECONDARY) { +            dev.set_head_pos_zero(ScanHeadId::SECONDARY); +            return; +        } + +        if (dev.is_head_pos_known(ScanHeadId::PRIMARY)) { +            if (dev.head_pos(ScanHeadId::PRIMARY) > dev.head_pos(ScanHeadId::SECONDARY)) { +                dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, Direction::BACKWARD, +                                              dev.head_pos(ScanHeadId::SECONDARY)); +            } else { +                dev.set_head_pos_zero(ScanHeadId::PRIMARY); +            } +            dev.set_head_pos_zero(ScanHeadId::SECONDARY); +        } +    } +} // namespace +  void scanner_move_back_home_ta(Genesys_Device& dev)  {      DBG_HELPER(dbg);      switch (dev.model->asic_type) { +        case AsicType::GL842:          case AsicType::GL843: +        case AsicType::GL845:              break;          default:              throw SaneException("Unsupported asic type"); @@ -1199,7 +1081,9 @@ void scanner_move_back_home_ta(Genesys_Device& dev)      const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 1, scan_method);      if (dev.is_head_pos_known(ScanHeadId::SECONDARY) && -        dev.head_pos(ScanHeadId::SECONDARY) > 1000) +        dev.is_head_pos_known(ScanHeadId::PRIMARY) && +        dev.head_pos(ScanHeadId::SECONDARY) > 1000 && +        dev.head_pos(ScanHeadId::SECONDARY) <= dev.head_pos(ScanHeadId::PRIMARY))      {          // leave 500 steps for regular slow back home          scanner_move(dev, scan_method, dev.head_pos(ScanHeadId::SECONDARY) - 500, @@ -1209,18 +1093,20 @@ void scanner_move_back_home_ta(Genesys_Device& dev)      ScanSession session;      session.params.xres = resolution;      session.params.yres = resolution; -    session.params.startx = 100; -    session.params.starty = 30000; -    session.params.pixels = 100; -    session.params.lines = 100; +    session.params.startx = 0; +    session.params.starty = 40000; +    session.params.pixels = 50; +    session.params.lines = 3;      session.params.depth = 8;      session.params.channels = 1;      session.params.scan_method = scan_method;      session.params.scan_mode = ScanColorMode::GRAY; -    session.params.color_filter = ColorFilter::RED; +    session.params.color_filter = ColorFilter::GREEN; +      session.params.flags =  ScanFlag::DISABLE_SHADING |                              ScanFlag::DISABLE_GAMMA | -                            ScanFlag::IGNORE_LINE_DISTANCE | +                            ScanFlag::IGNORE_STAGGER_OFFSET | +                            ScanFlag::IGNORE_COLOR_OFFSET |                              ScanFlag::REVERSE;      compute_session(&dev, session, sensor); @@ -1230,7 +1116,11 @@ void scanner_move_back_home_ta(Genesys_Device& dev)      scanner_clear_scan_and_feed_counts(dev);      dev.interface->write_registers(local_reg); -    gl843::gl843_set_xpa_motor_power(&dev, local_reg, true); + +    auto motor_mode = should_use_secondary_motor_mode(dev) ? MotorMode::SECONDARY +                                                           : MotorMode::PRIMARY_AND_SECONDARY; + +    dev.cmd_set->set_motor_mode(dev, local_reg, motor_mode);      try {          scanner_start_action(dev, true); @@ -1244,18 +1134,10 @@ void scanner_move_back_home_ta(Genesys_Device& dev)      if (is_testing_mode()) {          dev.interface->test_checkpoint("move_back_home_ta"); -        if (dev.is_head_pos_known(ScanHeadId::PRIMARY)) { -            if (dev.head_pos(ScanHeadId::PRIMARY) > dev.head_pos(ScanHeadId::SECONDARY)) { -                dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, Direction::BACKWARD, -                                              dev.head_pos(ScanHeadId::SECONDARY)); -            } else { -                dev.set_head_pos_zero(ScanHeadId::PRIMARY); -            } -            dev.set_head_pos_zero(ScanHeadId::SECONDARY); -        } +        handle_motor_position_after_move_back_home_ta(dev, motor_mode);          scanner_stop_action(dev); -        gl843::gl843_set_xpa_motor_power(&dev, local_reg, false); +        dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);          return;      } @@ -1266,18 +1148,10 @@ void scanner_move_back_home_ta(Genesys_Device& dev)          if (status.is_at_home) {              dbg.log(DBG_info, "TA reached home position"); -            if (dev.is_head_pos_known(ScanHeadId::PRIMARY)) { -                if (dev.head_pos(ScanHeadId::PRIMARY) > dev.head_pos(ScanHeadId::SECONDARY)) { -                    dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, Direction::BACKWARD, -                                                  dev.head_pos(ScanHeadId::SECONDARY)); -                } else { -                    dev.set_head_pos_zero(ScanHeadId::PRIMARY); -                } -                dev.set_head_pos_zero(ScanHeadId::SECONDARY); -            } +            handle_motor_position_after_move_back_home_ta(dev, motor_mode);              scanner_stop_action(dev); -            gl843::gl843_set_xpa_motor_power(&dev, local_reg, false); +            dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);              return;          } @@ -1287,325 +1161,1148 @@ void scanner_move_back_home_ta(Genesys_Device& dev)      throw SaneException("Timeout waiting for XPA lamp to park");  } -void sanei_genesys_calculate_zmod(bool two_table, -                                  uint32_t exposure_time, -                                  const std::vector<uint16_t>& slope_table, -                                  unsigned acceleration_steps, -                                  unsigned move_steps, -                                  unsigned buffer_acceleration_steps, -                                  uint32_t* out_z1, uint32_t* out_z2) +void scanner_search_strip(Genesys_Device& dev, bool forward, bool black)  { -    DBG(DBG_info, "%s: two_table=%d\n", __func__, two_table); +    DBG_HELPER_ARGS(dbg, "%s %s", black ? "black" : "white", forward ? "forward" : "reverse"); -    // acceleration total time -    unsigned sum = std::accumulate(slope_table.begin(), slope_table.begin() + acceleration_steps, -                                   0, std::plus<unsigned>()); +    if (dev.model->asic_type == AsicType::GL841 && !black && forward) { +        dev.frontend.set_gain(0, 0xff); +        dev.frontend.set_gain(1, 0xff); +        dev.frontend.set_gain(2, 0xff); +    } -    /* Z1MOD: -        c = sum(slope_table; reg_stepno) -        d = reg_fwdstep * <cruising speed> -        Z1MOD = (c+d) % exposure_time -    */ -    *out_z1 = (sum + buffer_acceleration_steps * slope_table[acceleration_steps - 1]) % exposure_time; +    // set up for a gray scan at lowest dpi +    const auto& resolution_settings = dev.model->get_resolution_settings(dev.settings.scan_method); +    unsigned dpi = resolution_settings.get_min_resolution_x(); +    unsigned channels = 1; -    /* Z2MOD: -        a = sum(slope_table; reg_stepno) -        b = move_steps or 1 if 2 tables -        Z1MOD = (a+b) % exposure_time -    */ -    if (!two_table) { -        sum = sum + (move_steps * slope_table[acceleration_steps - 1]); +    auto& sensor = sanei_genesys_find_sensor(&dev, dpi, channels, dev.settings.scan_method); +    dev.cmd_set->set_fe(&dev, sensor, AFE_SET); +    scanner_stop_action(dev); + + +    // shading calibration is done with dev.motor.base_ydpi +    unsigned lines = static_cast<unsigned>(dev.model->y_size_calib_mm * dpi / MM_PER_INCH); +    if (dev.model->asic_type == AsicType::GL841) { +        lines = 10; // TODO: use dev.model->search_lines +        lines = static_cast<unsigned>((lines * dpi) / MM_PER_INCH); +    } + +    unsigned pixels = dev.model->x_size_calib_mm * dpi / MM_PER_INCH; + +    dev.set_head_pos_zero(ScanHeadId::PRIMARY); + +    unsigned length = 20; +    if (dev.model->asic_type == AsicType::GL841) { +        // 20 cm max length for calibration sheet +        length = static_cast<unsigned>(((200 * dpi) / MM_PER_INCH) / lines); +    } + +    auto local_reg = dev.reg; + +    ScanSession session; +    session.params.xres = dpi; +    session.params.yres = dpi; +    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::GRAY; +    session.params.color_filter = ColorFilter::RED; +    session.params.flags = ScanFlag::DISABLE_SHADING | +                           ScanFlag::DISABLE_GAMMA; +    if (dev.model->asic_type != AsicType::GL841 && !forward) { +        session.params.flags |= ScanFlag::REVERSE; +    } +    compute_session(&dev, session, sensor); + +    dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session); + +    dev.interface->write_registers(local_reg); + +    dev.cmd_set->begin_scan(&dev, sensor, &local_reg, true); + +    if (is_testing_mode()) { +        dev.interface->test_checkpoint("search_strip"); +        scanner_stop_action(dev); +        return; +    } + +    wait_until_buffer_non_empty(&dev); + +    // now we're on target, we can read data +    auto image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); + +    scanner_stop_action(dev); + +    unsigned pass = 0; +    if (dbg_log_image_data()) { +        char title[80]; +        std::sprintf(title, "gl_search_strip_%s_%s%02d.tiff", +                     black ? "black" : "white", forward ? "fwd" : "bwd", pass); +        write_tiff_file(title, image); +    } + +    // loop until strip is found or maximum pass number done +    bool found = false; +    while (pass < length && !found) { +        dev.interface->write_registers(local_reg); + +        // now start scan +        dev.cmd_set->begin_scan(&dev, sensor, &local_reg, true); + +        wait_until_buffer_non_empty(&dev); + +        // now we're on target, we can read data +        image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); + +        scanner_stop_action(dev); + +        if (dbg_log_image_data()) { +            char title[80]; +            std::sprintf(title, "gl_search_strip_%s_%s%02d.tiff", +                         black ? "black" : "white", +                         forward ? "fwd" : "bwd", static_cast<int>(pass)); +            write_tiff_file(title, image); +        } + +        unsigned white_level = 90; +        unsigned black_level = 60; + +        std::size_t count = 0; +        // Search data to find black strip +        // When searching forward, we only need one line of the searched color since we +        // will scan forward. But when doing backward search, we need all the area of the ame color +        if (forward) { + +            for (std::size_t y = 0; y < image.get_height() && !found; y++) { +                count = 0; + +                // count of white/black pixels depending on the color searched +                for (std::size_t x = 0; x < image.get_width(); x++) { + +                    // when searching for black, detect white pixels +                    if (black && image.get_raw_channel(x, y, 0) > white_level) { +                        count++; +                    } + +                    // when searching for white, detect black pixels +                    if (!black && image.get_raw_channel(x, y, 0) < black_level) { +                        count++; +                    } +                } + +                // at end of line, if count >= 3%, line is not fully of the desired color +                // so we must go to next line of the buffer */ +                // count*100/pixels < 3 + +                auto found_percentage = (count * 100 / image.get_width()); +                if (found_percentage < 3) { +                    found = 1; +                    DBG(DBG_data, "%s: strip found forward during pass %d at line %zu\n", __func__, +                        pass, y); +                } else { +                    DBG(DBG_data, "%s: pixels=%zu, count=%zu (%zu%%)\n", __func__, +                        image.get_width(), count, found_percentage); +                } +            } +        } else { +            /*  since calibration scans are done forward, we need the whole area +                to be of the required color when searching backward +            */ +            count = 0; +            for (std::size_t y = 0; y < image.get_height(); y++) { +                // count of white/black pixels depending on the color searched +                for (std::size_t x = 0; x < image.get_width(); x++) { +                    // when searching for black, detect white pixels +                    if (black && image.get_raw_channel(x, y, 0) > white_level) { +                        count++; +                    } +                    // when searching for white, detect black pixels +                    if (!black && image.get_raw_channel(x, y, 0) < black_level) { +                        count++; +                    } +                } +            } + +            // at end of area, if count >= 3%, area is not fully of the desired color +            // so we must go to next buffer +            auto found_percentage = count * 100 / (image.get_width() * image.get_height()); +            if (found_percentage < 3) { +                found = 1; +                DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass); +            } else { +                DBG(DBG_data, "%s: pixels=%zu, count=%zu (%zu%%)\n", __func__, image.get_width(), +                    count, found_percentage); +            } +        } +        pass++; +    } + +    if (found) { +        DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white");      } else { -        sum = sum + slope_table[acceleration_steps - 1]; +        throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found", +                            black ? "black" : "white");      } -    *out_z2 = sum % exposure_time;  } -static uint8_t genesys_adjust_gain(double* applied_multi, double multi, uint8_t gain) +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)  { -  double voltage, original_voltage; -  uint8_t new_gain = 0; +    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; +} + +void scanner_offset_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, +                                Genesys_Register_Set& regs) +{ +    DBG_HELPER(dbg); + +    if (dev.model->asic_type == AsicType::GL842 && +        dev.frontend.layout.type != FrontendType::WOLFSON) +    { +        return; +    } + +    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; +        } +    } -  DBG(DBG_proc, "%s: multi=%f, gain=%d\n", __func__, multi, gain); +    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; +        } +    } -  voltage = 0.5 + gain * 0.25; -  original_voltage = voltage; +    unsigned target_pixels = dev.model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH; +    unsigned start_pixel = 0; +    unsigned black_pixels = (sensor.black_pixels * sensor.full_resolution) / sensor.full_resolution; -  voltage *= multi; +    unsigned channels = 3; +    unsigned lines = 1; +    unsigned resolution = sensor.full_resolution; -    new_gain = static_cast<std::uint8_t>((voltage - 0.5) * 4); -  if (new_gain > 0x0e) -    new_gain = 0x0e; +    const Genesys_Sensor* calib_sensor = &sensor; +    if (dev.model->asic_type == AsicType::GL843) { +        lines = 8; -  voltage = 0.5 + (new_gain) * 0.25; +        // compute divider factor to compute final pixels number +        const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dev.settings.xres, channels, +                                                             dev.settings.scan_method); +        resolution = dpihw_sensor.shading_resolution; +        unsigned factor = sensor.full_resolution / resolution; -  *applied_multi = voltage / original_voltage; +        calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels, +                                                  dev.settings.scan_method); -  DBG(DBG_proc, "%s: orig voltage=%.2f, new voltage=%.2f, *applied_multi=%f, new_gain=%d\n", -      __func__, original_voltage, voltage, *applied_multi, new_gain); +        target_pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH; +        black_pixels = calib_sensor->black_pixels / factor; -  return new_gain; -} +        if (should_calibrate_only_active_area(dev, dev.settings)) { +            float offset = dev.model->x_offset_ta; +            start_pixel = static_cast<int>((offset * calib_sensor->get_optical_resolution()) / MM_PER_INCH); +            float size = dev.model->x_size_ta; +            target_pixels = static_cast<int>((size * calib_sensor->get_optical_resolution()) / MM_PER_INCH); +        } -// todo: is return status necessary (unchecked?) -static void genesys_average_white(Genesys_Device* dev, Genesys_Sensor& sensor, int channels, -                                  int channel, uint8_t* data, int size, int *max_average) -{ +        if (dev.model->model_id == ModelId::CANON_4400F && +            dev.settings.scan_method == ScanMethod::FLATBED) +        { +            return; +        } +    } -    DBG_HELPER_ARGS(dbg, "channels=%d, channel=%d, size=%d", channels, channel, size); -  int gain_white_ref, sum, range; -  int average; -  int i; +    if (dev.model->model_id == ModelId::CANON_5600F) { +        // FIXME: use same approach as for GL843 scanners +        lines = 8; +    } -  range = size / 50; +    if (dev.model->asic_type == AsicType::GL847) { +        calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels, +                                                  dev.settings.scan_method); +    } -    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || -        dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +    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)      { -        gain_white_ref = sensor.fau_gain_white_ref * 256; +        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, ®s, 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, ®s, true); + +    if (is_testing_mode()) { +        dev.interface->test_checkpoint("offset_calibration"); +        if (dev.model->asic_type == AsicType::GL842 || +            dev.model->asic_type == AsicType::GL843) +        { +            scanner_stop_action_no_move(dev, regs); +        } +        return; +    } + +    Image first_line; +    if (dev.model->asic_type == AsicType::GL842 || +        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 { -        gain_white_ref = sensor.gain_white_ref * 256; +        first_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes); + +        if (dev.model->model_id == ModelId::CANON_5600F) { +            scanner_stop_action_no_move(dev, regs); +        } +    } + +    if (dbg_log_image_data()) { +        char fn[40]; +        std::snprintf(fn, 40, "gl843_bottom_offset_%03d_%03d_%03d.tiff", +                      bottom[0], bottom[1], bottom[2]); +        write_tiff_file(fn, first_line);      } -  if (range < 1) -    range = 1; +    for (unsigned ch = 0; ch < 3; ch++) { +        bottomavg[ch] = dark_average_channel(first_line, black_pixels, ch); +        DBG(DBG_info, "%s: bottom avg %d=%d\n", __func__, ch, bottomavg[ch]); +    } -  size = size / (2 * range * channels); +    // 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); -  data += (channel * 2); +    // scan with top AFE values +    dev.interface->write_registers(regs); +    DBG(DBG_info, "%s: starting second line reading\n", __func__); -  *max_average = 0; +    dev.cmd_set->begin_scan(&dev, *calib_sensor, ®s, true); -  while (size--) +    Image second_line; +    if (dev.model->asic_type == AsicType::GL842 || +        dev.model->asic_type == AsicType::GL843)      { -      sum = 0; -      for (i = 0; i < range; i++) -	{ -	  sum += (*data); -	  sum += *(data + 1) * 256; -	  data += (2 * channels);	/* byte based */ -	} +        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); -      average = (sum / range); -      if (average > *max_average) -	*max_average = average; +        if (dev.model->model_id == ModelId::CANON_5600F) { +            scanner_stop_action_no_move(dev, regs); +        }      } -  DBG(DBG_proc, "%s: max_average=%d, gain_white_ref = %d, finished\n", __func__, *max_average, -      gain_white_ref); +    for (unsigned ch = 0; ch < 3; ch++){ +        topavg[ch] = dark_average_channel(second_line, black_pixels, ch); +        DBG(DBG_info, "%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; -  if (*max_average >= gain_white_ref) -        throw SaneException(SANE_STATUS_INVAL); +    // 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, ®s, true); + +        if (dev.model->asic_type == AsicType::GL842 || +            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 (dev.model->model_id == ModelId::CANON_5600F) { +                scanner_stop_action_no_move(dev, regs); +            } +        } + +        if (dbg_log_image_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_log_image_data()) { +        sanei_genesys_write_file("gl_offset_all_desc.txt", +                                 reinterpret_cast<const std::uint8_t*>(debug_image_info.data()), +                                 debug_image_info.size()); +        write_tiff_file("gl_offset_all.tiff", 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));  } -/* todo: understand, values are too high */ -static int -genesys_average_black (Genesys_Device * dev, int channel, -		       uint8_t * data, int pixels) +/*  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)  { -  int i; -  int sum; -  int pixel_step; +    DBG_HELPER_ARGS(dbg, "dpi = %d", dpi); -  DBG(DBG_proc, "%s: channel=%d, pixels=%d\n", __func__, channel, pixels); +    if (dev.model->asic_type == AsicType::GL842 && +        dev.frontend.layout.type != FrontendType::WOLFSON) +    { +        return; +    } -  sum = 0; +    if (dev.model->asic_type == AsicType::GL843 && +        dev.frontend.layout.type != FrontendType::WOLFSON) +    { +        return; +    } -  if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) +    if (dev.model->asic_type == AsicType::GL845 || +        dev.model->asic_type == AsicType::GL846)      { -      data += (channel * 2); -      pixel_step = 3 * 2; +        // 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; +        }      } -  else + +    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); +        } +    } + +    // coarse gain calibration is always done in color mode +    unsigned channels = 3; + +    unsigned resolution = sensor.full_resolution; +    if (dev.model->asic_type == AsicType::GL841) { +        const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dev.settings.xres, channels, +                                                             dev.settings.scan_method); +        resolution = dpihw_sensor.shading_resolution; +    } + +    if (dev.model->asic_type == AsicType::GL842 || +        dev.model->asic_type == AsicType::GL843)      { -      pixel_step = 2; +        const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dpi, channels, +                                                             dev.settings.scan_method); +        resolution = dpihw_sensor.shading_resolution;      } -  for (i = 0; i < pixels; i++) +    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)      { -      sum += *data; -      sum += *(data + 1) * 256; +        if (dev.settings.xres < sensor.full_resolution) { +            coeff = 0.9f; +        } +    } -      data += pixel_step; +    unsigned lines = 10; +    if (dev.model->asic_type == AsicType::GL841) { +        lines = 1;      } -  DBG(DBG_proc, "%s = %d\n", __func__, sum / pixels); +    const Genesys_Sensor* calib_sensor = &sensor; +    if (dev.model->asic_type == AsicType::GL841 || +        dev.model->asic_type == AsicType::GL842 || +        dev.model->asic_type == AsicType::GL843 || +        dev.model->asic_type == AsicType::GL847) +    { +        calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels, +                                                  dev.settings.scan_method); +    } -    return sum / pixels; +    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, ®s, 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, ®s, true); + +    if (is_testing_mode()) { +        dev.interface->test_checkpoint("coarse_gain_calibration"); +        scanner_stop_action(dev); +        dev.cmd_set->move_back_home(&dev, true); +        return; +    } + +    Image image; +    if (dev.model->asic_type == AsicType::GL842 || +        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::GL842 || +        dev.model->asic_type == AsicType::GL843) +    { +        scanner_stop_action_no_move(dev, regs); +    } + +    if (dbg_log_image_data()) { +        write_tiff_file("gl_coarse_gain.tiff", image); +    } + +    for (unsigned ch = 0; ch < channels; ch++) { +        float curr_output = 0; +        float target_value = 0; + +        if (dev.model->asic_type == AsicType::GL842 || +            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"); +        } + +        dbg.vlog(DBG_info, "gain=(%d, %d, %d)", dev.frontend.get_gain(0), dev.frontend.get_gain(1), +                 dev.frontend.get_gain(2)); +    } + +    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.vlog(DBG_info, "final gain=(%d, %d, %d)", dev.frontend.get_gain(0), +             dev.frontend.get_gain(1), dev.frontend.get_gain(2)); + +    scanner_stop_action(dev); + +    dev.cmd_set->move_back_home(&dev, true);  } +namespace gl124 { +    void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                  Genesys_Register_Set& regs); +} // namespace gl124 -// todo: check; it works but the lines 1, 2, and 3 are too dark even with the -// same offset and gain settings? -static void genesys_coarse_calibration(Genesys_Device* dev, Genesys_Sensor& sensor) +SensorExposure scanner_led_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, +                                       Genesys_Register_Set& regs)  { -    DBG_HELPER_ARGS(dbg, "scan_mode = %d", static_cast<unsigned>(dev->settings.scan_mode)); -  int black_pixels; -  int white_average; -  uint8_t offset[4] = { 0xa0, 0x00, 0xa0, 0x40 };	/* first value isn't used */ -  uint16_t white[12], dark[12]; -  int i, j; +    DBG_HELPER(dbg); -  black_pixels = sensor.black_pixels -    * dev->settings.xres / sensor.optical_res; +    float move = 0; -    unsigned channels = dev->settings.get_channels(); +    if (dev.model->asic_type == AsicType::GL841) { +        if (dev.model->y_offset_calib_white > 0) { +            move = (dev.model->y_offset_calib_white * (dev.motor.base_ydpi)) / MM_PER_INCH; +            scanner_move(dev, dev.model->default_method, static_cast<unsigned>(move), +                         Direction::FORWARD); +        } +    } else if (dev.model->asic_type == AsicType::GL842 || +               dev.model->asic_type == AsicType::GL843) +    { +        // do nothing +    } else if (dev.model->asic_type == AsicType::GL845 || +               dev.model->asic_type == AsicType::GL846 || +               dev.model->asic_type == AsicType::GL847) +    { +        move = dev.model->y_offset_calib_white; +        move = static_cast<float>((move * (dev.motor.base_ydpi / 4)) / MM_PER_INCH); +        if (move > 20) { +            scanner_move(dev, dev.model->default_method, static_cast<unsigned>(move), +                         Direction::FORWARD); +        } +    } else if (dev.model->asic_type == AsicType::GL124) { +        gl124::move_to_calibration_area(&dev, sensor, regs); +    } -  DBG(DBG_info, "channels %d y_size %f xres %d\n", channels, dev->model->y_size.value(), -      dev->settings.xres); -    unsigned size = static_cast<unsigned>(channels * 2 * dev->model->y_size * dev->settings.xres / -                                          MM_PER_INCH); -  /*       1        1               mm                      1/inch        inch/mm */ -  std::vector<uint8_t> calibration_data(size); -  std::vector<uint8_t> all_data(size * 4, 1); +    unsigned channels = 3; +    unsigned resolution = sensor.shading_resolution; +    const auto& calib_sensor = sanei_genesys_find_sensor(&dev, resolution, channels, +                                                         dev.settings.scan_method); + +    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) +    { +        regs = dev.reg; // FIXME: apply this to all ASICs +    } + +    unsigned yres = resolution; +    if (dev.model->asic_type == AsicType::GL841) { +        yres = dev.settings.yres; // FIXME: remove this +    } + +    ScanSession session; +    session.params.xres = resolution; +    session.params.yres = yres; +    session.params.startx = 0; +    session.params.starty = 0; +    session.params.pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH; +    session.params.lines = 1; +    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); + +    dev.cmd_set->init_regs_for_scan_session(&dev, calib_sensor, ®s, session); + +    if (dev.model->asic_type == AsicType::GL841) { +        dev.interface->write_registers(regs); // FIXME: remove this +    } + +    std::uint16_t exp[3]; -    dev->cmd_set->set_fe(dev, sensor, AFE_INIT); +    if (dev.model->asic_type == AsicType::GL841) { +        exp[0] = sensor.exposure.red; +        exp[1] = sensor.exposure.green; +        exp[2] = sensor.exposure.blue; +    } else { +        exp[0] = calib_sensor.exposure.red; +        exp[1] = calib_sensor.exposure.green; +        exp[2] = calib_sensor.exposure.blue; +    } + +    std::uint16_t target = sensor.gain_white_ref * 256; -  dev->frontend.set_gain(0, 2); -  dev->frontend.set_gain(1, 2); -  dev->frontend.set_gain(2, 2); // TODO: ?  was 2 -  dev->frontend.set_offset(0, offset[0]); -  dev->frontend.set_offset(1, offset[0]); -  dev->frontend.set_offset(2, offset[0]); +    std::uint16_t min_exposure = 500; // only gl841 +    std::uint16_t max_exposure = ((exp[0] + exp[1] + exp[2]) / 3) * 2; // only gl841 -  for (i = 0; i < 4; i++)	/* read 4 lines */ +    std::uint16_t top[3] = {}; +    std::uint16_t bottom[3] = {}; + +    if (dev.model->asic_type == AsicType::GL845 || +        dev.model->asic_type == AsicType::GL846)      { -      if (i < 3)		/* first 3 lines */ -        { -          dev->frontend.set_offset(0, offset[i]); -          dev->frontend.set_offset(1, offset[i]); -          dev->frontend.set_offset(2, offset[i]); +        bottom[0] = 29000; +        bottom[1] = 29000; +        bottom[2] = 29000; + +        top[0] = 41000; +        top[1] = 51000; +        top[2] = 51000; +    } else if (dev.model->asic_type == AsicType::GL847) { +        bottom[0] = 28000; +        bottom[1] = 28000; +        bottom[2] = 28000; + +        top[0] = 32000; +        top[1] = 32000; +        top[2] = 32000; +    } + +    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) +    { +        sanei_genesys_set_motor_power(regs, false); +    } + +    bool acceptable = false; +    for (unsigned i_test = 0; i_test < 100 && !acceptable; ++i_test) { +        regs_set_exposure(dev.model->asic_type, regs, { exp[0], exp[1], exp[2] }); + +        if (dev.model->asic_type == AsicType::GL841) { +            // FIXME: remove +            dev.interface->write_register(0x10, (exp[0] >> 8) & 0xff); +            dev.interface->write_register(0x11, exp[0] & 0xff); +            dev.interface->write_register(0x12, (exp[1] >> 8) & 0xff); +            dev.interface->write_register(0x13, exp[1] & 0xff); +            dev.interface->write_register(0x14, (exp[2] >> 8) & 0xff); +            dev.interface->write_register(0x15, exp[2] & 0xff);          } -      if (i == 1)		/* second line */ -	{ -	  double applied_multi; -	  double gain_white_ref; +        dev.interface->write_registers(regs); -            if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || -                dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) -            { -                gain_white_ref = sensor.fau_gain_white_ref * 256; +        dbg.log(DBG_info, "starting line reading"); +        dev.cmd_set->begin_scan(&dev, calib_sensor, ®s, true); + +        if (is_testing_mode()) { +            dev.interface->test_checkpoint("led_calibration"); +            if (dev.model->asic_type == AsicType::GL841) { +                scanner_stop_action(dev); +                dev.cmd_set->move_back_home(&dev, true); +                return { exp[0], exp[1], exp[2] }; +            } else if (dev.model->asic_type == AsicType::GL124) { +                scanner_stop_action(dev); +                return calib_sensor.exposure;              } else { -                gain_white_ref = sensor.gain_white_ref * 256; +                scanner_stop_action(dev); +                dev.cmd_set->move_back_home(&dev, true); +                return calib_sensor.exposure;              } +        } -            // white and black are defined downwards - -            uint8_t gain0 = genesys_adjust_gain(&applied_multi, -                                                gain_white_ref / (white[0] - dark[0]), -                                                dev->frontend.get_gain(0)); -            uint8_t gain1 = genesys_adjust_gain(&applied_multi, -                                                gain_white_ref / (white[1] - dark[1]), -                                                dev->frontend.get_gain(1)); -            uint8_t gain2 = genesys_adjust_gain(&applied_multi, -                                                gain_white_ref / (white[2] - dark[2]), -                                                dev->frontend.get_gain(2)); -            // FIXME: looks like overwritten data. Are the above calculations doing -            // anything at all? -            dev->frontend.set_gain(0, gain0); -            dev->frontend.set_gain(1, gain1); -            dev->frontend.set_gain(2, gain2); -            dev->frontend.set_gain(0, 2); -            dev->frontend.set_gain(1, 2); -            dev->frontend.set_gain(2, 2); - -            dev->interface->write_fe_register(0x28, dev->frontend.get_gain(0)); -            dev->interface->write_fe_register(0x29, dev->frontend.get_gain(1)); -            dev->interface->write_fe_register(0x2a, dev->frontend.get_gain(2)); -	} +        auto image = read_unshuffled_image_from_scanner(&dev, session, session.output_line_bytes); -      if (i == 3)		/* last line */ -	{ -	  double x, y, rate; +        scanner_stop_action(dev); -	  for (j = 0; j < 3; j++) -	    { +        if (dbg_log_image_data()) { +            char fn[30]; +            std::snprintf(fn, 30, "gl_led_%02d.tiff", i_test); +            write_tiff_file(fn, image); +        } -                x = static_cast<double>(dark[(i - 2) * 3 + j] - -			  dark[(i - 1) * 3 + j]) * 254 / (offset[i - 1] / 2 - -							  offset[i - 2] / 2); -	      y = x - x * (offset[i - 1] / 2) / 254 - dark[(i - 1) * 3 + j]; -	      rate = (x - DARK_VALUE - y) * 254 / x + 0.5; +        int avg[3]; +        for (unsigned ch = 0; ch < channels; ch++) { +            avg[ch] = 0; +            for (std::size_t x = 0; x < image.get_width(); x++) { +                avg[ch] += image.get_raw_channel(x, 0, ch); +            } +            avg[ch] /= image.get_width(); +        } -                uint8_t curr_offset = static_cast<uint8_t>(rate); +        dbg.vlog(DBG_info, "average: %d, %d, %d", avg[0], avg[1], avg[2]); -                if (curr_offset > 0x7f) { -                    curr_offset = 0x7f; -                } -                curr_offset <<= 1; -                dev->frontend.set_offset(j, curr_offset); -	    } -	} -        dev->interface->write_fe_register(0x20, dev->frontend.get_offset(0)); -        dev->interface->write_fe_register(0x21, dev->frontend.get_offset(1)); -        dev->interface->write_fe_register(0x22, dev->frontend.get_offset(2)); +        acceptable = true; -      DBG(DBG_info, -          "%s: doing scan: gain: %d/%d/%d, offset: %d/%d/%d\n", __func__, -          dev->frontend.get_gain(0), -          dev->frontend.get_gain(1), -          dev->frontend.get_gain(2), -          dev->frontend.get_offset(0), -          dev->frontend.get_offset(1), -          dev->frontend.get_offset(2)); +        if (dev.model->asic_type == AsicType::GL841) { +            if (avg[0] < avg[1] * 0.95 || avg[1] < avg[0] * 0.95 || +                avg[0] < avg[2] * 0.95 || avg[2] < avg[0] * 0.95 || +                avg[1] < avg[2] * 0.95 || avg[2] < avg[1] * 0.95) +            { +                acceptable = false; +            } +            // led exposure is not acceptable if white level is too low. +            // ~80 hardcoded value for white level +            if (avg[0] < 20000 || avg[1] < 20000 || avg[2] < 20000) { +                acceptable = false; +            } -        dev->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, false); +            // for scanners using target value +            if (target > 0) { +                acceptable = true; +                for (unsigned i = 0; i < 3; i++) { +                    // we accept +- 2% delta from target +                    if (std::abs(avg[i] - target) > target / 50) { +                        exp[i] = (exp[i] * target) / avg[i]; +                        acceptable = false; +                    } +                } +            } else { +                if (!acceptable) { +                    unsigned avga = (avg[0] + avg[1] + avg[2]) / 3; +                    exp[0] = (exp[0] * avga) / avg[0]; +                    exp[1] = (exp[1] * avga) / avg[1]; +                    exp[2] = (exp[2] * avga) / avg[2]; +                    /*  Keep the resulting exposures below this value. Too long exposure drives +                        the ccd into saturation. We may fix this by relying on the fact that +                        we get a striped scan without shading, by means of statistical calculation +                    */ +                    unsigned avge = (exp[0] + exp[1] + exp[2]) / 3; + +                    if (avge > max_exposure) { +                        exp[0] = (exp[0] * max_exposure) / avge; +                        exp[1] = (exp[1] * max_exposure) / avge; +                        exp[2] = (exp[2] * max_exposure) / avge; +                    } +                    if (avge < min_exposure) { +                        exp[0] = (exp[0] * min_exposure) / avge; +                        exp[1] = (exp[1] * min_exposure) / avge; +                        exp[2] = (exp[2] * min_exposure) / avge; +                    } -        if (is_testing_mode()) { -            dev->interface->test_checkpoint("coarse_calibration"); -            dev->cmd_set->end_scan(dev, &dev->calib_reg, true); -            return; +                } +            } +        } else if (dev.model->asic_type == AsicType::GL845 || +                   dev.model->asic_type == AsicType::GL846) +        { +            for (unsigned i = 0; i < 3; i++) { +                if (avg[i] < bottom[i]) { +                    if (avg[i] != 0) { +                        exp[i] = (exp[i] * bottom[i]) / avg[i]; +                    } else { +                        exp[i] *= 10; +                    } +                    acceptable = false; +                } +                if (avg[i] > top[i]) { +                    if (avg[i] != 0) { +                        exp[i] = (exp[i] * top[i]) / avg[i]; +                    } else { +                        exp[i] *= 10; +                    } +                    acceptable = false; +                } +            } +        } else if (dev.model->asic_type == AsicType::GL847) { +            for (unsigned i = 0; i < 3; i++) { +                if (avg[i] < bottom[i] || avg[i] > top[i]) { +                    auto target = (bottom[i] + top[i]) / 2; +                    if (avg[i] != 0) { +                        exp[i] = (exp[i] * target) / avg[i]; +                    } else { +                        exp[i] *= 10; +                    } + +                    acceptable = false; +                } +            } +        } else if (dev.model->asic_type == AsicType::GL124) { +            for (unsigned i = 0; i < 3; i++) { +                // we accept +- 2% delta from target +                if (std::abs(avg[i] - target) > target / 50) { +                    float prev_weight = 0.5; +                    if (avg[i] != 0) { +                        exp[i] = exp[i] * prev_weight + ((exp[i] * target) / avg[i]) * (1 - prev_weight); +                    } else { +                        exp[i] = exp[i] * prev_weight + (exp[i] * 10) * (1 - prev_weight); +                    } +                    acceptable = false; +                } +            }          } +    } -      sanei_genesys_read_data_from_scanner(dev, calibration_data.data(), size); -      std::memcpy(all_data.data() + i * size, calibration_data.data(), size); -      if (i == 3)		/* last line */ -	{ -          std::vector<uint8_t> all_data_8(size * 4 / 2); -	  unsigned int count; +    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) +    { +        // set these values as final ones for scan +        regs_set_exposure(dev.model->asic_type, dev.reg, { exp[0], exp[1], exp[2] }); +    } -        for (count = 0; count < static_cast<unsigned>(size * 4 / 2); count++) { -            all_data_8[count] = all_data[count * 2 + 1]; +    if (dev.model->asic_type == AsicType::GL841 || +        dev.model->asic_type == AsicType::GL842 || +        dev.model->asic_type == AsicType::GL843) +    { +        dev.cmd_set->move_back_home(&dev, true); +    } + +    if (dev.model->asic_type == AsicType::GL845 || +        dev.model->asic_type == AsicType::GL846 || +        dev.model->asic_type == AsicType::GL847) +    { +        if (move > 20) { +            dev.cmd_set->move_back_home(&dev, true);          } -        sanei_genesys_write_pnm_file("gl_coarse.pnm", all_data_8.data(), 8, channels, size / 6, 4); -	} +    } -        dev->cmd_set->end_scan(dev, &dev->calib_reg, true); +    dbg.vlog(DBG_info,"acceptable exposure: %d, %d, %d\n", exp[0], exp[1], exp[2]); -      if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) -	{ -	  for (j = 0; j < 3; j++) -	    { -            genesys_average_white(dev, sensor, 3, j, calibration_data.data(), size, &white_average); -	      white[i * 3 + j] = white_average; -	      dark[i * 3 + j] = -        genesys_average_black (dev, j, calibration_data.data(), -				       black_pixels); -              DBG(DBG_info, "%s: white[%d]=%d, black[%d]=%d\n", __func__, -                  i * 3 + j, white[i * 3 + j], i * 3 + j, dark[i * 3 + j]); -	    } -	} -      else			/* one color-component modes */ -	{ -        genesys_average_white(dev, sensor, 1, 0, calibration_data.data(), size, &white_average); -	  white[i * 3 + 0] = white[i * 3 + 1] = white[i * 3 + 2] = -	    white_average; -	  dark[i * 3 + 0] = dark[i * 3 + 1] = dark[i * 3 + 2] = -        genesys_average_black (dev, 0, calibration_data.data(), black_pixels); -	} -    }				/* for (i = 0; i < 4; i++) */ - -  DBG(DBG_info, "%s: final: gain: %d/%d/%d, offset: %d/%d/%d\n", __func__, -      dev->frontend.get_gain(0), -      dev->frontend.get_gain(1), -      dev->frontend.get_gain(2), -      dev->frontend.get_offset(0), -      dev->frontend.get_offset(1), -      dev->frontend.get_offset(2)); +    return { exp[0], exp[1], exp[2] }; +} + +void sanei_genesys_calculate_zmod(bool two_table, +                                  uint32_t exposure_time, +                                  const std::vector<uint16_t>& slope_table, +                                  unsigned acceleration_steps, +                                  unsigned move_steps, +                                  unsigned buffer_acceleration_steps, +                                  uint32_t* out_z1, uint32_t* out_z2) +{ +    // acceleration total time +    unsigned sum = std::accumulate(slope_table.begin(), slope_table.begin() + acceleration_steps, +                                   0, std::plus<unsigned>()); + +    /* Z1MOD: +        c = sum(slope_table; reg_stepno) +        d = reg_fwdstep * <cruising speed> +        Z1MOD = (c+d) % exposure_time +    */ +    *out_z1 = (sum + buffer_acceleration_steps * slope_table[acceleration_steps - 1]) % exposure_time; + +    /* Z2MOD: +        a = sum(slope_table; reg_stepno) +        b = move_steps or 1 if 2 tables +        Z1MOD = (a+b) % exposure_time +    */ +    if (!two_table) { +        sum = sum + (move_steps * slope_table[acceleration_steps - 1]); +    } else { +        sum = sum + slope_table[acceleration_steps - 1]; +    } +    *out_z2 = sum % exposure_time;  }  /** @@ -1614,22 +2311,41 @@ static void genesys_coarse_calibration(Genesys_Device* dev, Genesys_Sensor& sens   * @param dev scanner's device   */  static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                             Genesys_Register_Set& local_reg,                                               std::vector<std::uint16_t>& out_average_data,                                               bool is_dark, const std::string& log_filename_prefix)  {      DBG_HELPER(dbg); +    if (dev->model->asic_type == AsicType::GL646) { +        dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg); +        local_reg = dev->reg; +    } else { +        local_reg = dev->reg; +        dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg); +        dev->interface->write_registers(local_reg); +    } +      debug_dump(DBG_info, dev->calib_session);    size_t size;    uint32_t pixels_per_line; -  uint8_t channels; -  /* end pixel - start pixel */ -  pixels_per_line = dev->calib_pixels; -  channels = dev->calib_channels; +    if (dev->model->asic_type == AsicType::GL842 || +        dev->model->asic_type == AsicType::GL843 || +        dev->model->model_id == ModelId::CANON_5600F) +    { +        pixels_per_line = dev->calib_session.output_pixels; +    } else { +        // BUG: this selects incorrect pixel number +        pixels_per_line = dev->calib_session.params.pixels; +    } +    unsigned channels = dev->calib_session.params.channels; -  uint32_t out_pixels_per_line = pixels_per_line + dev->calib_pixels_offset; +    // BUG: we are using wrong pixel number here +    unsigned start_offset = +            dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres; +    unsigned out_pixels_per_line = pixels_per_line + start_offset;      // FIXME: we set this during both dark and white calibration. A cleaner approach should      // probably be used @@ -1644,61 +2360,55 @@ static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_      }      // FIXME: the current calculation is likely incorrect on non-GL843 implementations, -    // but this needs checking -    if (dev->calib_total_bytes_to_read > 0) { -        size = dev->calib_total_bytes_to_read; -    } else if (dev->model->asic_type == AsicType::GL843) { -        size = channels * 2 * pixels_per_line * dev->calib_lines; +    // but this needs checking. Note the extra line when computing size. +    if (dev->model->asic_type == AsicType::GL842 || +        dev->model->asic_type == AsicType::GL843 || +        dev->model->model_id == ModelId::CANON_5600F) +    { +        size = dev->calib_session.output_total_bytes_raw;      } else { -        size = channels * 2 * pixels_per_line * (dev->calib_lines + 1); +        size = channels * 2 * pixels_per_line * (dev->calib_session.params.lines + 1);      }    std::vector<uint16_t> calibration_data(size / 2); -    bool motor = true; -  if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE) -    { -        motor = false; -    } -      // turn off motor and lamp power for flatbed scanners, but not for sheetfed scanners      // because they have a calibration sheet with a sufficient black strip      if (is_dark && !dev->model->is_sheetfed) { -        sanei_genesys_set_lamp_power(dev, sensor, dev->calib_reg, false); -        sanei_genesys_set_motor_power(dev->calib_reg, motor); +        sanei_genesys_set_lamp_power(dev, sensor, local_reg, false);      } else { -        sanei_genesys_set_lamp_power(dev, sensor, dev->calib_reg, true); -        sanei_genesys_set_motor_power(dev->calib_reg, motor); +        sanei_genesys_set_lamp_power(dev, sensor, local_reg, true);      } +    sanei_genesys_set_motor_power(local_reg, true); -    dev->interface->write_registers(dev->calib_reg); +    dev->interface->write_registers(local_reg);      if (is_dark) {          // wait some time to let lamp to get dark          dev->interface->sleep_ms(200); -    } else if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION) { +    } else if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {          // make sure lamp is bright again          // FIXME: what about scanners that take a long time to warm the lamp?          dev->interface->sleep_ms(500);      }      bool start_motor = !is_dark; -    dev->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, start_motor); +    dev->cmd_set->begin_scan(dev, sensor, &local_reg, start_motor);      if (is_testing_mode()) {          dev->interface->test_checkpoint(is_dark ? "dark_shading_calibration"                                                  : "white_shading_calibration"); -        dev->cmd_set->end_scan(dev, &dev->calib_reg, true); +        dev->cmd_set->end_scan(dev, &local_reg, true);          return;      }      sanei_genesys_read_data_from_scanner(dev, reinterpret_cast<std::uint8_t*>(calibration_data.data()),                                           size); -    dev->cmd_set->end_scan(dev, &dev->calib_reg, true); +    dev->cmd_set->end_scan(dev, &local_reg, true); -    if (dev->model->flags & GENESYS_FLAG_16BIT_DATA_INVERTED) { +    if (has_flag(dev->model->flags, ModelFlag::SWAP_16BIT_DATA)) {          for (std::size_t i = 0; i < size / 2; ++i) {              auto value = calibration_data[i];              value = ((value >> 8) & 0xff) | ((value << 8) & 0xff00); @@ -1706,30 +2416,29 @@ static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_          }      } +    if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) { +        for (std::size_t i = 0; i < size / 2; ++i) { +            calibration_data[i] = 0xffff - calibration_data[i]; +        } +    } +      std::fill(out_average_data.begin(), -              out_average_data.begin() + dev->calib_pixels_offset * channels, 0); +              out_average_data.begin() + start_offset * channels, 0); -    compute_array_percentile_approx(out_average_data.data() + dev->calib_pixels_offset * channels, +    compute_array_percentile_approx(out_average_data.data() + +                                        start_offset * channels,                                      calibration_data.data(), -                                    dev->calib_lines, pixels_per_line * channels, +                                    dev->calib_session.params.lines, pixels_per_line * channels,                                      0.5f); -    if (DBG_LEVEL >= DBG_data) { -        sanei_genesys_write_pnm_file16((log_filename_prefix + "_shading.pnm").c_str(), -                                       calibration_data.data(), -                                       channels, pixels_per_line, dev->calib_lines); -        sanei_genesys_write_pnm_file16((log_filename_prefix + "_average.pnm").c_str(), -                                       out_average_data.data(), -                                       channels, out_pixels_per_line, 1); +    if (dbg_log_image_data()) { +        write_tiff_file(log_filename_prefix + "_shading.tiff", calibration_data.data(), 16, +                        channels, pixels_per_line, dev->calib_session.params.lines); +        write_tiff_file(log_filename_prefix + "_average.tiff", out_average_data.data(), 16, +                        channels, out_pixels_per_line, 1);      }  } - -static void genesys_dark_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor) -{ -    DBG_HELPER(dbg); -    genesys_shading_calibration_impl(dev, sensor, dev->dark_average_data, true, "gl_black_"); -}  /*   * this function builds dummy dark calibration data so that we can   * compute shading coefficient in a clean way @@ -1737,18 +2446,28 @@ static void genesys_dark_shading_calibration(Genesys_Device* dev, const Genesys_   * can be computed from previous calibration data (when doing offset   * calibration ?)   */ -static void genesys_dummy_dark_shading(Genesys_Device* dev, const Genesys_Sensor& sensor) +static void genesys_dark_shading_by_dummy_pixel(Genesys_Device* dev, const Genesys_Sensor& sensor)  {      DBG_HELPER(dbg);    uint32_t pixels_per_line; -  uint8_t channels;    uint32_t skip, xend;    int dummy1, dummy2, dummy3;	/* dummy black average per channel */ -  pixels_per_line = dev->calib_pixels; -  channels = dev->calib_channels; +    if (dev->model->asic_type == AsicType::GL842 || +        dev->model->asic_type == AsicType::GL843) +    { +        pixels_per_line = dev->calib_session.output_pixels; +    } else { +        pixels_per_line = dev->calib_session.params.pixels; +    } + +    unsigned channels = dev->calib_session.params.channels; + +    // BUG: we are using wrong pixel number here +    unsigned start_offset = +            dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres; -  uint32_t out_pixels_per_line = pixels_per_line + dev->calib_pixels_offset; +    unsigned out_pixels_per_line = pixels_per_line + start_offset;      dev->average_size = channels * out_pixels_per_line;    dev->dark_average_data.clear(); @@ -1756,8 +2475,7 @@ static void genesys_dummy_dark_shading(Genesys_Device* dev, const Genesys_Sensor    /* we average values on 'the left' where CCD pixels are under casing and       give darkest values. We then use these as dummy dark calibration */ -  if (dev->settings.xres <= sensor.optical_res / 2) -    { +    if (dev->settings.xres <= sensor.full_resolution / 2) {        skip = 4;        xend = 36;      } @@ -1807,17 +2525,22 @@ static void genesys_dummy_dark_shading(Genesys_Device* dev, const Genesys_Sensor      }  } +static void genesys_dark_shading_by_constant(Genesys_Device& dev) +{ +    dev.dark_average_data.clear(); +    dev.dark_average_data.resize(dev.average_size, 0x0101); +}  static void genesys_repark_sensor_before_shading(Genesys_Device* dev)  {      DBG_HELPER(dbg); -    if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK) { +    if (has_flag(dev->model->flags, ModelFlag::SHADING_REPARK)) {          dev->cmd_set->move_back_home(dev, true);          if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||              dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)          { -            dev->cmd_set->move_to_ta(dev); +            scanner_move_to_ta(*dev);          }      }  } @@ -1825,34 +2548,153 @@ static void genesys_repark_sensor_before_shading(Genesys_Device* dev)  static void genesys_repark_sensor_after_white_shading(Genesys_Device* dev)  {      DBG_HELPER(dbg); -    if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK) { +    if (has_flag(dev->model->flags, ModelFlag::SHADING_REPARK)) {          dev->cmd_set->move_back_home(dev, true);      }  } -static void genesys_white_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor) +static void genesys_host_shading_calibration_impl(Genesys_Device& dev, const Genesys_Sensor& sensor, +                                                  std::vector<std::uint16_t>& out_average_data, +                                                  bool is_dark, +                                                  const std::string& log_filename_prefix)  {      DBG_HELPER(dbg); -    genesys_shading_calibration_impl(dev, sensor, dev->white_average_data, false, "gl_white_"); + +    if (is_dark && dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { +        // FIXME: dark shading currently not supported on infrared transparency scans +        return; +    } + +    auto local_reg = dev.reg; +    dev.cmd_set->init_regs_for_shading(&dev, sensor, local_reg); + +    auto& session = dev.calib_session; +    debug_dump(DBG_info, session); + +    // turn off motor and lamp power for flatbed scanners, but not for sheetfed scanners +    // because they have a calibration sheet with a sufficient black strip +    if (is_dark && !dev.model->is_sheetfed) { +        sanei_genesys_set_lamp_power(&dev, sensor, local_reg, false); +    } else { +        sanei_genesys_set_lamp_power(&dev, sensor, local_reg, true); +    } +    sanei_genesys_set_motor_power(local_reg, true); + +    dev.interface->write_registers(local_reg); + +    if (is_dark) { +        // wait some time to let lamp to get dark +        dev.interface->sleep_ms(200); +    } else if (has_flag(dev.model->flags, ModelFlag::DARK_CALIBRATION)) { +        // make sure lamp is bright again +        // FIXME: what about scanners that take a long time to warm the lamp? +        dev.interface->sleep_ms(500); +    } + +    bool start_motor = !is_dark; +    dev.cmd_set->begin_scan(&dev, sensor, &local_reg, start_motor); + +    if (is_testing_mode()) { +        dev.interface->test_checkpoint(is_dark ? "host_dark_shading_calibration" +                                               : "host_white_shading_calibration"); +        dev.cmd_set->end_scan(&dev, &local_reg, true); +        return; +    } + +    Image image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw); +    scanner_stop_action(dev); + +    auto start_offset = session.params.startx; +    auto out_pixels_per_line = start_offset + session.output_pixels; + +    // FIXME: we set this during both dark and white calibration. A cleaner approach should +    // probably be used +    dev.average_size = session.params.channels * out_pixels_per_line; + +    out_average_data.clear(); +    out_average_data.resize(dev.average_size); + +    std::fill(out_average_data.begin(), +              out_average_data.begin() + start_offset * session.params.channels, 0); + +    compute_array_percentile_approx(out_average_data.data() + +                                        start_offset * session.params.channels, +                                    reinterpret_cast<std::uint16_t*>(image.get_row_ptr(0)), +                                    session.params.lines, +                                    session.output_pixels * session.params.channels, +                                    0.5f); + +    if (dbg_log_image_data()) { +        write_tiff_file(log_filename_prefix + "_host_shading.tiff", image); +        write_tiff_file(log_filename_prefix + "_host_average.tiff", out_average_data.data(), 16, +                        session.params.channels, out_pixels_per_line, 1); +    } +} + +static void genesys_dark_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                             Genesys_Register_Set& local_reg) +{ +    DBG_HELPER(dbg); +    if (has_flag(dev->model->flags, ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN)) { +        genesys_host_shading_calibration_impl(*dev, sensor, dev->dark_average_data, true, +                                              "gl_black"); +    } else { +        genesys_shading_calibration_impl(dev, sensor, local_reg, dev->dark_average_data, true, +                                         "gl_black"); +    } +} + +static void genesys_white_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                              Genesys_Register_Set& local_reg) +{ +    DBG_HELPER(dbg); +    if (has_flag(dev->model->flags, ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN)) { +        genesys_host_shading_calibration_impl(*dev, sensor, dev->white_average_data, false, +                                              "gl_white"); +    } else { +        genesys_shading_calibration_impl(dev, sensor, local_reg, dev->white_average_data, false, +                                         "gl_white"); +    }  }  // This calibration uses a scan over the calibration target, comprising a black and a white strip.  // (So the motor must be on.)  static void genesys_dark_white_shading_calibration(Genesys_Device* dev, -                                                   const Genesys_Sensor& sensor) +                                                   const Genesys_Sensor& sensor, +                                                   Genesys_Register_Set& local_reg)  { -    DBG_HELPER_ARGS(dbg, "lines = %zu", dev->calib_lines); +    DBG_HELPER(dbg); + +    if (dev->model->asic_type == AsicType::GL646) { +        dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg); +        local_reg = dev->reg; +    } else { +        local_reg = dev->reg; +        dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg); +        dev->interface->write_registers(local_reg); +    } +    size_t size;    uint32_t pixels_per_line; -  uint8_t channels;    unsigned int x;    uint32_t dark, white, dark_sum, white_sum, dark_count, white_count, col,      dif; -  pixels_per_line = dev->calib_pixels; -  channels = dev->calib_channels; +    if (dev->model->asic_type == AsicType::GL842 || +        dev->model->asic_type == AsicType::GL843) +    { +        pixels_per_line = dev->calib_session.output_pixels; +    } else { +        pixels_per_line = dev->calib_session.params.pixels; +    } -  uint32_t out_pixels_per_line = pixels_per_line + dev->calib_pixels_offset; +    unsigned channels = dev->calib_session.params.channels; + +    // BUG: we are using wrong pixel number here +    unsigned start_offset = +            dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres; + +    unsigned out_pixels_per_line = pixels_per_line + start_offset;      dev->average_size = channels * out_pixels_per_line; @@ -1862,68 +2704,65 @@ static void genesys_dark_white_shading_calibration(Genesys_Device* dev,    dev->dark_average_data.clear();    dev->dark_average_data.resize(dev->average_size); -  if (dev->calib_total_bytes_to_read > 0) -    size = dev->calib_total_bytes_to_read; -  else -    size = channels * 2 * pixels_per_line * dev->calib_lines; - -  std::vector<uint8_t> calibration_data(size); - -    bool motor = true; -  if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE) +    if (dev->model->asic_type == AsicType::GL842 || +        dev->model->asic_type == AsicType::GL843)      { -        motor = false; +        size = dev->calib_session.output_total_bytes_raw; +    } else { +        // FIXME: on GL841 this is different than dev->calib_session.output_total_bytes_raw, +        // needs checking +        size = channels * 2 * pixels_per_line * dev->calib_session.params.lines;      } +  std::vector<uint8_t> calibration_data(size); +      // turn on motor and lamp power -    sanei_genesys_set_lamp_power(dev, sensor, dev->calib_reg, true); -    sanei_genesys_set_motor_power(dev->calib_reg, motor); +    sanei_genesys_set_lamp_power(dev, sensor, local_reg, true); +    sanei_genesys_set_motor_power(local_reg, true); -    dev->interface->write_registers(dev->calib_reg); +    dev->interface->write_registers(local_reg); -    dev->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, false); +    dev->cmd_set->begin_scan(dev, sensor, &local_reg, false);      if (is_testing_mode()) {          dev->interface->test_checkpoint("dark_white_shading_calibration"); -        dev->cmd_set->end_scan(dev, &dev->calib_reg, true); +        dev->cmd_set->end_scan(dev, &local_reg, true);          return;      }      sanei_genesys_read_data_from_scanner(dev, calibration_data.data(), size); -    dev->cmd_set->end_scan(dev, &dev->calib_reg, true); +    dev->cmd_set->end_scan(dev, &local_reg, true); -  if (DBG_LEVEL >= DBG_data) -    { -      if (dev->model->is_cis) -        { -          sanei_genesys_write_pnm_file("gl_black_white_shading.pnm", calibration_data.data(), -                                       16, 1, pixels_per_line*channels, -                                       dev->calib_lines); -        } -      else -        { -          sanei_genesys_write_pnm_file("gl_black_white_shading.pnm", calibration_data.data(), -                                       16, channels, pixels_per_line, -                                       dev->calib_lines); +    if (dbg_log_image_data()) { +        if (dev->model->is_cis) { +            write_tiff_file("gl_black_white_shading.tiff", calibration_data.data(), +                            16, 1, pixels_per_line*channels, +                            dev->calib_session.params.lines); +        } else { +            write_tiff_file("gl_black_white_shading.tiff", calibration_data.data(), +                            16, channels, pixels_per_line, +                            dev->calib_session.params.lines);          }      }      std::fill(dev->dark_average_data.begin(), -              dev->dark_average_data.begin() + dev->calib_pixels_offset * channels, 0); +              dev->dark_average_data.begin() + start_offset * channels, 0);      std::fill(dev->white_average_data.begin(), -              dev->white_average_data.begin() + dev->calib_pixels_offset * channels, 0); +              dev->white_average_data.begin() + start_offset * channels, 0); -    uint16_t* average_white = dev->white_average_data.data() + dev->calib_pixels_offset * channels; -    uint16_t* average_dark = dev->dark_average_data.data() + dev->calib_pixels_offset * channels; +    uint16_t* average_white = dev->white_average_data.data() + +                              start_offset * channels; +    uint16_t* average_dark = dev->dark_average_data.data() + +                             start_offset * channels;    for (x = 0; x < pixels_per_line * channels; x++)      {        dark = 0xffff;        white = 0; -            for (std::size_t y = 0; y < dev->calib_lines; y++) +            for (std::size_t y = 0; y < dev->calib_session.params.lines; y++)  	{  	  col = calibration_data[(x + y * pixels_per_line * channels) * 2];  	  col |= @@ -1947,7 +2786,7 @@ static void genesys_dark_white_shading_calibration(Genesys_Device* dev,        white_count = 0;        white_sum = 0; -            for (std::size_t y = 0; y < dev->calib_lines; y++) +            for (std::size_t y = 0; y < dev->calib_session.params.lines; y++)  	{  	  col = calibration_data[(x + y * pixels_per_line * channels) * 2];  	  col |= @@ -1974,11 +2813,11 @@ static void genesys_dark_white_shading_calibration(Genesys_Device* dev,          *average_white++ = white_sum;      } -    if (DBG_LEVEL >= DBG_data) { -        sanei_genesys_write_pnm_file16("gl_white_average.pnm", dev->white_average_data.data(), -                                       channels, out_pixels_per_line, 1); -        sanei_genesys_write_pnm_file16("gl_dark_average.pnm", dev->dark_average_data.data(), -                                       channels, out_pixels_per_line, 1); +    if (dbg_log_image_data()) { +        write_tiff_file("gl_white_average.tiff", dev->white_average_data.data(), 16, channels, +                        out_pixels_per_line, 1); +        write_tiff_file("gl_dark_average.tiff", dev->dark_average_data.data(), 16, channels, +                        out_pixels_per_line, 1);      }  } @@ -2085,13 +2924,12 @@ compute_averaged_planar (Genesys_Device * dev, const Genesys_Sensor& sensor,   */    res = dev->settings.xres; -    if (sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres) > 1) -    { +    if (sensor.full_resolution > sensor.get_optical_resolution()) {          res *= 2;      } -  /* this should be evenly dividable */ -  basepixels = sensor.optical_res / res; +    // this should be evenly dividable +    basepixels = sensor.full_resolution / res;    /* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */    if (basepixels < 1) @@ -2376,9 +3214,10 @@ compute_shifted_coefficients (Genesys_Device * dev,      auto cmat = color_order_to_cmat(color_order);    x = dev->settings.xres; -  if (sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres) > 1) -    x *= 2;							/* scanner is using half-ccd mode */ -  basepixels = sensor.optical_res / x;			/*this should be evenly dividable */ +    if (sensor.full_resolution > sensor.get_optical_resolution()) { +        x *= 2;	// scanner is using half-ccd mode +    } +    basepixels = sensor.full_resolution / x; // this should be evenly dividable        /* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */        if (basepixels < 1) @@ -2451,19 +3290,30 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_  {      DBG_HELPER(dbg); -    if (dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE) { +    if (sensor.use_host_side_calib) {          return;      }    uint32_t pixels_per_line; -  uint8_t channels;    int o;    unsigned int length;		/**> number of shading calibration data words */    unsigned int factor;    unsigned int coeff, target_code, words_per_color = 0; -  pixels_per_line = dev->calib_pixels + dev->calib_pixels_offset; -  channels = dev->calib_channels; + +    // BUG: we are using wrong pixel number here +    unsigned start_offset = +            dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres; + +    if (dev->model->asic_type == AsicType::GL842 || +        dev->model->asic_type == AsicType::GL843) +    { +        pixels_per_line = dev->calib_session.output_pixels + start_offset; +    } else { +        pixels_per_line = dev->calib_session.params.pixels + start_offset; +    } + +    unsigned channels = dev->calib_session.params.channels;    /* we always build data for three channels, even for gray     * we make the shading data such that each color channel data line is contiguous @@ -2504,25 +3354,27 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_    // contains 16bit words in little endian    std::vector<uint8_t> shading_data(length, 0); +    if (!dev->calib_session.computed) { +        genesys_send_offset_and_shading(dev, sensor, shading_data.data(), length); +        return; +    } +    /* TARGET/(Wn-Dn) = white gain -> ~1.xxx then it is multiplied by 0x2000       or 0x4000 to give an integer       Wn = white average for column n       Dn = dark average for column n     */ -    if (get_registers_gain4_bit(dev->model->asic_type, dev->calib_reg)) { +    if (get_registers_gain4_bit(dev->model->asic_type, dev->reg)) {          coeff = 0x4000;      } else {          coeff = 0x2000;      }    /* compute avg factor */ -  if(dev->settings.xres>sensor.optical_res) -    { -      factor=1; -    } -  else -    { -      factor=sensor.optical_res/dev->settings.xres; +    if (dev->settings.xres > sensor.full_resolution) { +        factor = 1; +    } else { +        factor = sensor.full_resolution / dev->settings.xres;      }    /* for GL646, shading data is planar if REG_0x01_FASTMOD is set and @@ -2536,6 +3388,7 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_    switch (dev->model->sensor_id)      {      case SensorId::CCD_XP300: +        case SensorId::CCD_DOCKETPORT_487:      case SensorId::CCD_ROADWARRIOR:      case SensorId::CCD_DP665:      case SensorId::CCD_DP685: @@ -2570,10 +3423,9 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_      case SensorId::CCD_HP2300:        target_code = 0xdc00;        o = 2; -      if(dev->settings.xres<=sensor.optical_res/2) -       { -          o = o - sensor.dummy_pixel / 2; -       } +            if (dev->settings.xres <= sensor.full_resolution / 2) { +                o = o - sensor.dummy_pixel / 2; +            }        compute_coefficients (dev,                  shading_data.data(),  			    pixels_per_line, @@ -2586,7 +3438,7 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_      case SensorId::CCD_5345:        target_code = 0xe000;        o = 4; -      if(dev->settings.xres<=sensor.optical_res/2) +      if(dev->settings.xres<=sensor.full_resolution/2)         {            o = o - sensor.dummy_pixel;         } @@ -2633,9 +3485,12 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_      case SensorId::CCD_CANON_4400F:      case SensorId::CCD_CANON_8400F:      case SensorId::CCD_CANON_8600F: +        case SensorId::CCD_PLUSTEK_OPTICFILM_7200:      case SensorId::CCD_PLUSTEK_OPTICFILM_7200I:      case SensorId::CCD_PLUSTEK_OPTICFILM_7300: +        case SensorId::CCD_PLUSTEK_OPTICFILM_7400:      case SensorId::CCD_PLUSTEK_OPTICFILM_7500I: +        case SensorId::CCD_PLUSTEK_OPTICFILM_8200I:        target_code = 0xe000;        o = 0;        compute_coefficients (dev, @@ -2654,6 +3509,7 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_      case SensorId::CIS_CANON_LIDE_120:      case SensorId::CIS_CANON_LIDE_210:      case SensorId::CIS_CANON_LIDE_220: +        case SensorId::CCD_CANON_5600F:          /* TODO store this in a data struct so we avoid           * growing this switch */          switch(dev->model->sensor_id) @@ -2684,6 +3540,8 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_                                       target_code);        break;      case SensorId::CIS_CANON_LIDE_35: +        case SensorId::CIS_CANON_LIDE_60: +            case SensorId::CIS_CANON_LIDE_90:        compute_averaged_planar (dev, sensor,                                 shading_data.data(),                                 pixels_per_line, @@ -2756,9 +3614,8 @@ genesys_restore_calibration(Genesys_Device * dev, Genesys_Sensor& sensor)            /* we don't restore the gamma fields */            sensor.exposure = cache.sensor.exposure; +            dev->calib_session = cache.session;            dev->average_size = cache.average_size; -          dev->calib_pixels = cache.calib_pixels; -          dev->calib_channels = cache.calib_channels;            dev->dark_average_data = cache.dark_average_data;            dev->white_average_data = cache.white_average_data; @@ -2812,8 +3669,7 @@ static void genesys_save_calibration(Genesys_Device* dev, const Genesys_Sensor&    found_cache_it->frontend = dev->frontend;    found_cache_it->sensor = sensor; -  found_cache_it->calib_pixels = dev->calib_pixels; -  found_cache_it->calib_channels = dev->calib_channels; +    found_cache_it->session = dev->calib_session;  #ifdef HAVE_SYS_TIME_H      gettimeofday(&time, nullptr); @@ -2821,20 +3677,13 @@ static void genesys_save_calibration(Genesys_Device* dev, const Genesys_Sensor&  #endif  } -/** - * does the calibration process for a flatbed scanner - * - offset calibration - * - gain calibration - * - shading calibration - * @param dev device to calibrate - */  static void genesys_flatbed_calibration(Genesys_Device* dev, Genesys_Sensor& sensor)  {      DBG_HELPER(dbg); -  uint32_t pixels_per_line; +    uint32_t pixels_per_line; -    unsigned coarse_res = sensor.optical_res; -    if (dev->settings.yres <= sensor.optical_res / 2) { +    unsigned coarse_res = sensor.full_resolution; +    if (dev->settings.yres <= sensor.full_resolution / 2) {          coarse_res /= 2;      } @@ -2848,35 +3697,29 @@ static void genesys_flatbed_calibration(Genesys_Device* dev, Genesys_Sensor& sen          coarse_res = 1200;      } -  /* do offset calibration if needed */ -  if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION) -    { +    auto local_reg = dev->initial_regs; + +    if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) { +        // do ADC calibration first.          dev->interface->record_progress_message("offset_calibration"); -        dev->cmd_set->offset_calibration(dev, sensor, dev->calib_reg); +        dev->cmd_set->offset_calibration(dev, sensor, local_reg); -      /* since all the registers are set up correctly, just use them */          dev->interface->record_progress_message("coarse_gain_calibration"); -        dev->cmd_set->coarse_gain_calibration(dev, sensor, dev->calib_reg, coarse_res); -    } else { -    /* since we have 2 gain calibration proc, skip second if first one was -       used. */ -        dev->interface->record_progress_message("init_regs_for_coarse_calibration"); -        dev->cmd_set->init_regs_for_coarse_calibration(dev, sensor, dev->calib_reg); - -        dev->interface->record_progress_message("genesys_coarse_calibration"); -        genesys_coarse_calibration(dev, sensor); +        dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res);      } -  if (dev->model->is_cis) +    if (dev->model->is_cis && +        !has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION))      { -      /* the afe now sends valid data for doing led calibration */ +        // ADC now sends correct data, we can configure the exposure for the LEDs          dev->interface->record_progress_message("led_calibration");          switch (dev->model->asic_type) {              case AsicType::GL124: +            case AsicType::GL841:              case AsicType::GL845:              case AsicType::GL846:              case AsicType::GL847: { -                auto calib_exposure = dev->cmd_set->led_calibration(dev, sensor, dev->calib_reg); +                auto calib_exposure = dev->cmd_set->led_calibration(dev, sensor, local_reg);                  for (auto& sensor_update :                          sanei_genesys_find_sensors_all_for_write(dev, sensor.method)) {                      sensor_update.get().exposure = calib_exposure; @@ -2885,80 +3728,66 @@ static void genesys_flatbed_calibration(Genesys_Device* dev, Genesys_Sensor& sen                  break;              }              default: { -                sensor.exposure = dev->cmd_set->led_calibration(dev, sensor, dev->calib_reg); +                sensor.exposure = dev->cmd_set->led_calibration(dev, sensor, local_reg);              }          } - -      /* calibrate afe again to match new exposure */ -      if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION) { +        if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) { +            // recalibrate ADC again for the new LED exposure              dev->interface->record_progress_message("offset_calibration"); -            dev->cmd_set->offset_calibration(dev, sensor, dev->calib_reg); - -            // since all the registers are set up correctly, just use them +            dev->cmd_set->offset_calibration(dev, sensor, local_reg);              dev->interface->record_progress_message("coarse_gain_calibration"); -            dev->cmd_set->coarse_gain_calibration(dev, sensor, dev->calib_reg, coarse_res); -        } else { -            // since we have 2 gain calibration proc, skip second if first one was used -            dev->interface->record_progress_message("init_regs_for_coarse_calibration"); -            dev->cmd_set->init_regs_for_coarse_calibration(dev, sensor, dev->calib_reg); - -            dev->interface->record_progress_message("genesys_coarse_calibration"); -            genesys_coarse_calibration(dev, sensor); +            dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res);          }      }    /* we always use sensor pixel number when the ASIC can't handle multi-segments sensor */ -  if (!(dev->model->flags & GENESYS_FLAG_SIS_SENSOR)) -    { +    if (!has_flag(dev->model->flags, ModelFlag::SIS_SENSOR)) {          pixels_per_line = static_cast<std::uint32_t>((dev->model->x_size * dev->settings.xres) /                                                       MM_PER_INCH); -    } -  else -    { -      pixels_per_line = sensor.sensor_pixels; +    } else { +        pixels_per_line = static_cast<std::uint32_t>((dev->model->x_size_calib_mm * dev->settings.xres) +                                                      / MM_PER_INCH);      }      // send default shading data      dev->interface->record_progress_message("sanei_genesys_init_shading_data");      sanei_genesys_init_shading_data(dev, sensor, pixels_per_line); -  if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || -      dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) -  { -        dev->cmd_set->move_to_ta(dev); -  } +    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || +        dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +    { +        scanner_move_to_ta(*dev); +    }      // shading calibration -    if (dev->model->flags & GENESYS_FLAG_DARK_WHITE_CALIBRATION) { -        dev->interface->record_progress_message("init_regs_for_shading"); -        dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg); - -        dev->interface->record_progress_message("genesys_dark_white_shading_calibration"); -        genesys_dark_white_shading_calibration(dev, sensor); -    } else { -        DBG(DBG_proc, "%s : genesys_dark_shading_calibration dev->calib_reg ", __func__); -        debug_dump(DBG_proc, dev->calib_reg); - -        if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION) { -            dev->interface->record_progress_message("init_regs_for_shading"); -            dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg); +    if (!has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION)) { +        if (has_flag(dev->model->flags, ModelFlag::DARK_WHITE_CALIBRATION)) { +            dev->interface->record_progress_message("genesys_dark_white_shading_calibration"); +            genesys_dark_white_shading_calibration(dev, sensor, local_reg); +        } else { +            DBG(DBG_proc, "%s : genesys_dark_shading_calibration local_reg ", __func__); +            debug_dump(DBG_proc, local_reg); -            dev->interface->record_progress_message("genesys_dark_shading_calibration"); -            genesys_dark_shading_calibration(dev, sensor); -            genesys_repark_sensor_before_shading(dev); -        } +            if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) { +                dev->interface->record_progress_message("genesys_dark_shading_calibration"); +                genesys_dark_shading_calibration(dev, sensor, local_reg); +                genesys_repark_sensor_before_shading(dev); +            } -        dev->interface->record_progress_message("init_regs_for_shading2"); -        dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg); +            dev->interface->record_progress_message("genesys_white_shading_calibration"); +            genesys_white_shading_calibration(dev, sensor, local_reg); -        dev->interface->record_progress_message("genesys_white_shading_calibration"); -        genesys_white_shading_calibration(dev, sensor); -        genesys_repark_sensor_after_white_shading(dev); +            genesys_repark_sensor_after_white_shading(dev); -        if (!(dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)) { -            genesys_dummy_dark_shading(dev, sensor); +            if (!has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) { +                if (has_flag(dev->model->flags, ModelFlag::USE_CONSTANT_FOR_DARK_CALIBRATION)) { +                    genesys_dark_shading_by_constant(*dev); +                } else { +                    genesys_dark_shading_by_dummy_pixel(dev, sensor); +                } +            }          }      } @@ -2982,68 +3811,62 @@ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& se      DBG_HELPER(dbg);      bool forward = true; +    auto local_reg = dev->initial_regs; +      // first step, load document      dev->cmd_set->load_document(dev); -  /* led, offset and gain calibration are influenced by scan -   * settings. So we set it to sensor resolution */ -  dev->settings.xres = sensor.optical_res; -  /* XP200 needs to calibrate a full and half sensor's resolution */ -    if (dev->model->sensor_id == SensorId::CIS_XP200 && -        dev->settings.xres <= sensor.optical_res / 2) -    { -        dev->settings.xres /= 2; -    } +    unsigned coarse_res = sensor.full_resolution;    /* the afe needs to sends valid data even before calibration */    /* go to a white area */      try { -        dev->cmd_set->search_strip(dev, sensor, forward, false); +        scanner_search_strip(*dev, forward, false);      } catch (...) {          catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });          throw;      } -  if (dev->model->is_cis) -    { -        dev->cmd_set->led_calibration(dev, sensor, dev->calib_reg); +    if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) { +        // do ADC calibration first. +        dev->interface->record_progress_message("offset_calibration"); +        dev->cmd_set->offset_calibration(dev, sensor, local_reg); + +        dev->interface->record_progress_message("coarse_gain_calibration"); +        dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res);      } -  /* calibrate afe */ -  if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION) +    if (dev->model->is_cis && +        !has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION))      { -        dev->cmd_set->offset_calibration(dev, sensor, dev->calib_reg); - -      /* since all the registers are set up correctly, just use them */ +        // ADC now sends correct data, we can configure the exposure for the LEDs +        dev->interface->record_progress_message("led_calibration"); +        dev->cmd_set->led_calibration(dev, sensor, local_reg); -        dev->cmd_set->coarse_gain_calibration(dev, sensor, dev->calib_reg, sensor.optical_res); -    } -  else -    /* since we have 2 gain calibration proc, skip second if first one was -       used. */ -    { -        dev->cmd_set->init_regs_for_coarse_calibration(dev, sensor, dev->calib_reg); +        if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) { +            // recalibrate ADC again for the new LED exposure +            dev->interface->record_progress_message("offset_calibration"); +            dev->cmd_set->offset_calibration(dev, sensor, local_reg); -        genesys_coarse_calibration(dev, sensor); +            dev->interface->record_progress_message("coarse_gain_calibration"); +            dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res); +        }      }    /* search for a full width black strip and then do a 16 bit scan to     * gather black shading data */ -  if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION) -    { -      /* seek black/white reverse/forward */ +    if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) { +        // seek black/white reverse/forward          try { -            dev->cmd_set->search_strip(dev, sensor, forward, true); +            scanner_search_strip(*dev, forward, true);          } catch (...) {              catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });              throw;          } -        dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg); -          try { -            genesys_dark_shading_calibration(dev, sensor); +            genesys_dark_shading_calibration(dev, sensor, local_reg);          } catch (...) {              catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });              throw; @@ -3054,7 +3877,7 @@ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& se    /* go to a white area */      try { -        dev->cmd_set->search_strip(dev, sensor, forward, false); +        scanner_search_strip(*dev, forward, false);      } catch (...) {          catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });          throw; @@ -3062,10 +3885,8 @@ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& se    genesys_repark_sensor_before_shading(dev); -    dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg); -      try { -        genesys_white_shading_calibration(dev, sensor); +        genesys_white_shading_calibration(dev, sensor, local_reg);          genesys_repark_sensor_after_white_shading(dev);      } catch (...) {          catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); }); @@ -3073,17 +3894,9 @@ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& se      }      // in case we haven't black shading data, build it from black pixels of white calibration -    // FIXME: shouldn't we use genesys_dummy_dark_shading() ? -    if (!(dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)) { -      dev->dark_average_data.clear(); -        dev->dark_average_data.resize(dev->average_size, 0x0f0f); -      /* XXX STEF XXX -       * with black point in white shading, build an average black -       * pixel and use it to fill the dark_average -       * dev->calib_pixels -       (sensor.sensor_pixels * dev->settings.xres) / sensor.optical_res, -       dev->calib_lines, -       */ +    // FIXME: shouldn't we use genesys_dark_shading_by_dummy_pixel() ? +    if (!has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) { +        genesys_dark_shading_by_constant(*dev);      }    /* send the shading coefficient when doing whole line shading @@ -3099,7 +3912,7 @@ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& se      dev->cmd_set->eject_document(dev);      // restore settings -    dev->settings.xres = sensor.optical_res; +    dev->settings.xres = sensor.full_resolution;  }  /** @@ -3129,22 +3942,23 @@ static void genesys_warmup_lamp(Genesys_Device* dev)  {      DBG_HELPER(dbg);      unsigned seconds = 0; -  int pixel; -  int channels, total_size; -  double first_average = 0; -  double second_average = 0; -  int difference = 255; -    int lines = 3;    const auto& sensor = sanei_genesys_find_sensor_any(dev); -    dev->cmd_set->init_regs_for_warmup(dev, sensor, &dev->reg, &channels, &total_size); +    dev->cmd_set->init_regs_for_warmup(dev, sensor, &dev->reg); +    dev->interface->write_registers(dev->reg); + +    auto total_pixels =  dev->session.output_pixels; +    auto total_size = dev->session.output_line_bytes; +    auto channels = dev->session.params.channels; +    auto lines = dev->session.output_line_count; +    std::vector<uint8_t> first_line(total_size);    std::vector<uint8_t> second_line(total_size); -  do -    { -      DBG(DBG_info, "%s: one more loop\n", __func__); +    do { +        first_line = second_line; +          dev->cmd_set->begin_scan(dev, sensor, &dev->reg, false);          if (is_testing_mode()) { @@ -3155,72 +3969,44 @@ static void genesys_warmup_lamp(Genesys_Device* dev)          wait_until_buffer_non_empty(dev); -        try { -            sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size); -        } catch (...) { -            // FIXME: document why this retry is here -            sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size); -        } - +        sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size);          dev->cmd_set->end_scan(dev, &dev->reg, true); -        dev->interface->sleep_ms(1000); -      seconds++; +        // compute difference between the two scans +        double first_average = 0; +        double second_average = 0; +        for (unsigned pixel = 0; pixel < total_size; pixel++) { +            // 16 bit data +            if (dev->session.params.depth == 16) { +                first_average += (first_line[pixel] + first_line[pixel + 1] * 256); +                second_average += (second_line[pixel] + second_line[pixel + 1] * 256); +                pixel++; +            } else { +                first_average += first_line[pixel]; +                second_average += second_line[pixel]; +            } +        } -        dev->cmd_set->begin_scan(dev, sensor, &dev->reg, false); +        first_average /= total_pixels; +        second_average /= total_pixels; -        wait_until_buffer_non_empty(dev); +        if (dbg_log_image_data()) { +            write_tiff_file("gl_warmup1.tiff", first_line.data(), dev->session.params.depth, +                            channels, total_size / (lines * channels), lines); +            write_tiff_file("gl_warmup2.tiff", second_line.data(), dev->session.params.depth, +                            channels, total_size / (lines * channels), lines); +        } -        sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size); -        dev->cmd_set->end_scan(dev, &dev->reg, true); +        DBG(DBG_info, "%s: average 1 = %.2f, average 2 = %.2f\n", __func__, first_average, +            second_average); -      /* compute difference between the two scans */ -      for (pixel = 0; pixel < total_size; pixel++) -	{ -            // 16 bit data -            if (dev->session.params.depth == 16) { -	      first_average += (first_line[pixel] + first_line[pixel + 1] * 256); -	      second_average += (second_line[pixel] + second_line[pixel + 1] * 256); -	      pixel++; -	    } -	  else -	    { -	      first_average += first_line[pixel]; -	      second_average += second_line[pixel]; -	    } -	} -        if (dev->session.params.depth == 16) { -	  first_average /= pixel; -	  second_average /= pixel; -            difference = static_cast<int>(std::fabs(first_average - second_average)); -	  DBG(DBG_info, "%s: average = %.2f, diff = %.3f\n", __func__, -	      100 * ((second_average) / (256 * 256)), -	      100 * (difference / second_average)); - -	  if (second_average > (100 * 256) -	      && (difference / second_average) < 0.002) -	    break; -	} -      else -	{ -	  first_average /= pixel; -	  second_average /= pixel; -	  if (DBG_LEVEL >= DBG_data) -	    { -              sanei_genesys_write_pnm_file("gl_warmup1.pnm", first_line.data(), 8, channels, -                                           total_size / (lines * channels), lines); -              sanei_genesys_write_pnm_file("gl_warmup2.pnm", second_line.data(), 8, channels, -                                           total_size / (lines * channels), lines); -	    } -	  DBG(DBG_info, "%s: average 1 = %.2f, average 2 = %.2f\n", __func__, first_average, -	      second_average); -          /* if delta below 15/255 ~= 5.8%, lamp is considred warm enough */ -	  if (fabs (first_average - second_average) < 15 -	      && second_average > 55) -	    break; -	} +        float average_difference = std::fabs(first_average - second_average) / second_average; +        if (second_average > 0 && average_difference < 0.005) +        { +            dbg.vlog(DBG_info, "difference: %f, exiting", average_difference); +            break; +        } -      /* sleep another second before next loop */          dev->interface->sleep_ms(1000);          seconds++;      } while (seconds < WARMUP_TIME); @@ -3236,6 +4022,37 @@ static void genesys_warmup_lamp(Genesys_Device* dev)      }  } +static void init_regs_for_scan(Genesys_Device& dev, const Genesys_Sensor& sensor, +                               Genesys_Register_Set& regs) +{ +    DBG_HELPER(dbg); +    debug_dump(DBG_info, dev.settings); + +    auto session = dev.cmd_set->calculate_scan_session(&dev, sensor, dev.settings); + +    if (dev.model->asic_type == AsicType::GL124 || +        dev.model->asic_type == AsicType::GL845 || +        dev.model->asic_type == AsicType::GL846 || +        dev.model->asic_type == AsicType::GL847) +    { +        /*  Fast move to scan area: + +            We don't move fast the whole distance since it would involve computing +            acceleration/deceleration distance for scan resolution. So leave a remainder for it so +            scan makes the final move tuning +        */ + +        if (dev.settings.get_channels() * dev.settings.yres >= 600 && session.params.starty > 700) { +            scanner_move(dev, dev.model->default_method, +                         static_cast<unsigned>(session.params.starty - 500), +                         Direction::FORWARD); +            session.params.starty = 500; +        } +        compute_session(&dev, session, sensor); +    } + +    dev.cmd_set->init_regs_for_scan_session(&dev, sensor, ®s, session); +}  // High-level start of scanning  static void genesys_start_scan(Genesys_Device* dev, bool lamp_off) @@ -3243,6 +4060,7 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)      DBG_HELPER(dbg);    unsigned int steps, expected; +    /* since not all scanners are set ot wait for head to park     * we check we are not still parking before starting a new scan */      if (dev->parking) { @@ -3254,38 +4072,30 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)    /* wait for lamp warmup : until a warmup for TRANSPARENCY is designed, skip     * it when scanning from XPA. */ -  if (!(dev->model->flags & GENESYS_FLAG_SKIP_WARMUP) -    && (dev->settings.scan_method == ScanMethod::FLATBED)) +    if (has_flag(dev->model->flags, ModelFlag::WARMUP) && +        (dev->settings.scan_method != ScanMethod::TRANSPARENCY_INFRARED))      { +        if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || +            dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +        { +            scanner_move_to_ta(*dev); +        } +          genesys_warmup_lamp(dev);      }    /* set top left x and y values by scanning the internals if flatbed scanners */      if (!dev->model->is_sheetfed) { -      /* do the geometry detection only once */ -      if ((dev->model->flags & GENESYS_FLAG_SEARCH_START) -      && (dev->model->y_offset_calib_white == 0)) -	{ -        dev->cmd_set->search_start_position (dev); - -            dev->parking = false; -            dev->cmd_set->move_back_home(dev, true); -	} -      else -	{ -	  /* Go home */ -	  /* TODO: check we can drop this since we cannot have the -	     scanner's head wandering here */ -            dev->parking = false; -            dev->cmd_set->move_back_home(dev, true); -	} +        // TODO: check we can drop this since we cannot have the scanner's head wandering here +        dev->parking = false; +        dev->cmd_set->move_back_home(dev, true);      }    /* move to calibration area for transparency adapter */      if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||          dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)      { -        dev->cmd_set->move_to_ta(dev); +        scanner_move_to_ta(*dev);      }    /* load document if needed (for sheetfed scanner for instance) */ @@ -3304,22 +4114,18 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)    /* try to use cached calibration first */    if (!genesys_restore_calibration (dev, sensor))      { -       /* calibration : sheetfed scanners can't calibrate before each scan */ -       /* and also those who have the NO_CALIBRATION flag                  */ -        if (!(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION) && !dev->model->is_sheetfed) { +        // calibration : sheetfed scanners can't calibrate before each scan. +        // also don't run calibration for those scanners where all passes are disabled +        bool shading_disabled = +                has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION) && +                has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION) && +                has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION); +        if (!shading_disabled && !dev->model->is_sheetfed) {              genesys_scanner_calibration(dev, sensor); -          genesys_save_calibration (dev, sensor); -	} -      else -	{ +            genesys_save_calibration(dev, sensor); +        } else {            DBG(DBG_warn, "%s: no calibration done\n", __func__); -	} -    } - -  /* build look up table for dynamic lineart */ -    if (dev->settings.scan_mode == ScanColorMode::LINEART) { -        sanei_genesys_load_lut(dev->lineart_lut, 8, 8, 50, 205, dev->settings.threshold_curve, -                               dev->settings.threshold-127); +        }      }      dev->cmd_set->wait_for_motor_stop(dev); @@ -3331,10 +4137,10 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)      if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||          dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)      { -        dev->cmd_set->move_to_ta(dev); +        scanner_move_to_ta(*dev);      } -    dev->cmd_set->init_regs_for_scan(dev, sensor); +    init_regs_for_scan(*dev, sensor, dev->reg);    /* no lamp during scan */      if (lamp_off) { @@ -3344,7 +4150,7 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)    /* GL124 is using SHDAREA, so we have to wait for scan to be set up before     * sending shading data */      if (dev->cmd_set->has_send_shading_data() && -        !(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)) +        !has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION))      {          genesys_send_shading_coefficient(dev, sensor);      } @@ -3386,33 +4192,6 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)      }  } -static void genesys_fill_read_buffer(Genesys_Device* dev) -{ -    DBG_HELPER(dbg); - -  /* for sheetfed scanner, we must check is document is shorter than -   * the requested scan */ -    if (dev->model->is_sheetfed) { -        dev->cmd_set->detect_document_end(dev); -    } - -    std::size_t size = dev->read_buffer.size() - dev->read_buffer.avail(); - -  /* due to sensors and motors, not all data can be directly used. It -   * may have to be read from another intermediate buffer and then processed. -   * There are currently 3 intermediate stages: -   * - handling of odd/even sensors -   * - handling of line interpolation for motors that can't have low -   *   enough dpi -   * - handling of multi-segments sensors -   * -   * This is also the place where full duplex data will be handled. -   */ -    dev->pipeline_buffer.get_data(size, dev->read_buffer.get_write_pos(size)); - -    dev->read_buffer.produce(size); -} -  /* this function does the effective data read in a manner that suits     the scanner. It does data reordering and resizing if need.     It also manages EOF and I/O errors, and line distance correction. @@ -3422,8 +4201,6 @@ static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destinatio  {      DBG_HELPER(dbg);      size_t bytes = 0; -  uint8_t *work_buffer_src; -  Genesys_Buffer *src_buffer;      if (!dev->read_active) {        *len = 0; @@ -3439,7 +4216,7 @@ static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destinatio      {        /* issue park command immediatly in case scanner can handle it         * so we save time */ -        if (!dev->model->is_sheetfed && !(dev->model->flags & GENESYS_FLAG_MUST_WAIT) && +        if (!dev->model->is_sheetfed && !has_flag(dev->model->flags, ModelFlag::MUST_WAIT) &&              !dev->parking)          {              dev->cmd_set->move_back_home(dev, false); @@ -3448,61 +4225,22 @@ static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destinatio          throw SaneException(SANE_STATUS_EOF, "nothing more to scan: EOF");      } -/* convert data */ -/* -  0. fill_read_buffer --------------- read_buffer ---------------------- -  1a). (opt)uncis                    (assumes color components to be laid out -                                    planar) -  1b). (opt)reverse_RGB              (assumes pixels to be BGR or BBGGRR)) --------------- lines_buffer ---------------------- -  2a). (opt)line_distance_correction (assumes RGB or RRGGBB) -  2b). (opt)unstagger                (assumes pixels to be depth*channels/8 -                                      bytes long, unshrinked) -------------- shrink_buffer --------------------- -  3. (opt)shrink_lines             (assumes component separation in pixels) --------------- out_buffer ----------------------- -  4. memcpy to destination (for lineart with bit reversal) -*/ -/*FIXME: for lineart we need sub byte addressing in buffers, or conversion to -  bytes at 0. and back to bits at 4. -Problems with the first approach: -  - its not clear how to check if we need to output an incomplete byte -    because it is the last one. - */ -/*FIXME: add lineart support for gl646. in the meantime add logic to convert -  from gray to lineart at the end? would suffer the above problem, -  total_bytes_to_read and total_bytes_read help in that case. - */ -      if (is_testing_mode()) {          if (dev->total_bytes_read + *len > dev->total_bytes_to_read) {              *len = dev->total_bytes_to_read - dev->total_bytes_read;          }          dev->total_bytes_read += *len;      } else { -        genesys_fill_read_buffer(dev); - -        src_buffer = &(dev->read_buffer); - -        /* move data to destination */ -        bytes = std::min(src_buffer->avail(), *len); - -        work_buffer_src = src_buffer->get_read_pos(); - -        std::memcpy(destination, work_buffer_src, bytes); -        *len = bytes; +        if (dev->model->is_sheetfed) { +            dev->cmd_set->detect_document_end(dev); +        } -        /* avoid signaling some extra data because we have treated a full block -        * on the last block */          if (dev->total_bytes_read + *len > dev->total_bytes_to_read) {              *len = dev->total_bytes_to_read - dev->total_bytes_read;          } -        /* count bytes sent to frontend */ +        dev->pipeline_buffer.get_data(*len, destination);          dev->total_bytes_read += *len; - -        src_buffer->consume(bytes);      }    /* end scan if all needed data have been read */ @@ -3576,181 +4314,113 @@ static unsigned pick_resolution(const std::vector<unsigned>& resolutions, unsign      return best_res;  } -static void calc_parameters(Genesys_Scanner* s) +static Genesys_Settings calculate_scan_settings(Genesys_Scanner* s)  {      DBG_HELPER(dbg); -  double tl_x = 0, tl_y = 0, br_x = 0, br_y = 0; -    tl_x = SANE_UNFIX(s->pos_top_left_x); -    tl_y = SANE_UNFIX(s->pos_top_left_y); -    br_x = SANE_UNFIX(s->pos_bottom_right_x); -    br_y = SANE_UNFIX(s->pos_bottom_right_y); +    const auto* dev = s->dev; +    Genesys_Settings settings; +    settings.scan_method = s->scan_method; +    settings.scan_mode = option_string_to_scan_color_mode(s->mode); -    s->params.last_frame = true;	/* only single pass scanning supported */ +    settings.depth = s->bit_depth; -    if (s->mode == SANE_VALUE_SCAN_MODE_GRAY || s->mode == SANE_VALUE_SCAN_MODE_LINEART) { -        s->params.format = SANE_FRAME_GRAY; -    } else { -        s->params.format = SANE_FRAME_RGB; +    if (settings.depth > 8) { +        settings.depth = 16; +    } else if (settings.depth < 8) { +        settings.depth = 1;      } -    if (s->mode == SANE_VALUE_SCAN_MODE_LINEART) { -        s->params.depth = 1; -    } else { -        s->params.depth = s->bit_depth; -    } - -    s->dev->settings.scan_method = s->scan_method; -    const auto& resolutions = s->dev->model->get_resolution_settings(s->dev->settings.scan_method); - -    s->dev->settings.depth = s->bit_depth; +    const auto& resolutions = dev->model->get_resolution_settings(settings.scan_method); -  /* interpolation */ -  s->dev->settings.disable_interpolation = s->disable_interpolation; +    settings.xres = pick_resolution(resolutions.resolutions_x, s->resolution, "X"); +    settings.yres = pick_resolution(resolutions.resolutions_y, s->resolution, "Y"); -  // FIXME: use correct sensor -  const auto& sensor = sanei_genesys_find_sensor_any(s->dev); - -    // hardware settings -    if (static_cast<unsigned>(s->resolution) > sensor.optical_res && -        s->dev->settings.disable_interpolation) -    { -        s->dev->settings.xres = sensor.optical_res; -    } else { -        s->dev->settings.xres = s->resolution; -    } -    s->dev->settings.yres = s->resolution; +    settings.tl_x = fixed_to_float(s->pos_top_left_x); +    settings.tl_y = fixed_to_float(s->pos_top_left_y); +    float br_x = fixed_to_float(s->pos_bottom_right_x); +    float br_y = fixed_to_float(s->pos_bottom_right_y); -    s->dev->settings.xres = pick_resolution(resolutions.resolutions_x, s->dev->settings.xres, "X"); -    s->dev->settings.yres = pick_resolution(resolutions.resolutions_y, s->dev->settings.yres, "Y"); - -    s->params.lines = static_cast<unsigned>(((br_y - tl_y) * s->dev->settings.yres) / +    settings.lines = static_cast<unsigned>(((br_y - settings.tl_y) * settings.yres) /                                              MM_PER_INCH); -    unsigned pixels_per_line = static_cast<unsigned>(((br_x - tl_x) * s->dev->settings.xres) / -                                                     MM_PER_INCH); - -  /* we need an even pixels number -   * TODO invert test logic or generalize behaviour across all ASICs */ -    if ((s->dev->model->flags & GENESYS_FLAG_SIS_SENSOR) || -        s->dev->model->asic_type == AsicType::GL847 || -        s->dev->model->asic_type == AsicType::GL124 || -        s->dev->model->asic_type == AsicType::GL845 || -        s->dev->model->asic_type == AsicType::GL846 || -        s->dev->model->asic_type == AsicType::GL843) -    { -        if (s->dev->settings.xres <= 1200) { -            pixels_per_line = (pixels_per_line / 4) * 4; -        } else if (s->dev->settings.xres < s->dev->settings.yres) { -            // BUG: this is an artifact of the fact that the resolution was twice as large than -            // the actual resolution when scanning above the supported scanner X resolution -            pixels_per_line = (pixels_per_line / 8) * 8; -        } else { -            pixels_per_line = (pixels_per_line / 16) * 16; -        } -    } - -  /* corner case for true lineart for sensor with several segments -   * or when xres is doubled to match yres */ -    if (s->dev->settings.xres >= 1200 && ( -                s->dev->model->asic_type == AsicType::GL124 || -                s->dev->model->asic_type == AsicType::GL847 || -                s->dev->session.params.xres < s->dev->session.params.yres)) -    { -        if (s->dev->settings.xres < s->dev->settings.yres) { -            // FIXME: this is an artifact of the fact that the resolution was twice as large than -            // the actual resolution when scanning above the supported scanner X resolution -            pixels_per_line = (pixels_per_line / 8) * 8; -        } else { -            pixels_per_line = (pixels_per_line / 16) * 16; -        } -    } - -    unsigned xres_factor = s->resolution / s->dev->settings.xres; -    unsigned bytes_per_line = 0; - -  if (s->params.depth > 8) -    { -      s->params.depth = 16; -        bytes_per_line = 2 * pixels_per_line; -    } -  else if (s->params.depth == 1) -    { -        // round down pixel number. This will is lossy operation, at most 7 pixels will be lost -        pixels_per_line = (pixels_per_line / 8) * 8; -        bytes_per_line = pixels_per_line / 8; -    } else { -        bytes_per_line = pixels_per_line; -    } -    if (s->params.format == SANE_FRAME_RGB) { -        bytes_per_line *= 3; -    } +    unsigned pixels_per_line = static_cast<unsigned>(((br_x - settings.tl_x) * settings.xres) / +                                                     MM_PER_INCH); -    s->dev->settings.scan_mode = option_string_to_scan_color_mode(s->mode); +    const auto& sensor = sanei_genesys_find_sensor(dev, settings.xres, settings.get_channels(), +                                                   settings.scan_method); -  s->dev->settings.lines = s->params.lines; -    s->dev->settings.pixels = pixels_per_line; -    s->dev->settings.requested_pixels = pixels_per_line * xres_factor; -    s->params.pixels_per_line = pixels_per_line * xres_factor; -    s->params.bytes_per_line = bytes_per_line * xres_factor; -  s->dev->settings.tl_x = tl_x; -  s->dev->settings.tl_y = tl_y; +    pixels_per_line = session_adjust_output_pixels(pixels_per_line, *dev, sensor, +                                                   settings.xres, settings.yres, true); -    // threshold setting -    s->dev->settings.threshold = static_cast<int>(2.55 * (SANE_UNFIX(s->threshold))); +    unsigned xres_factor = s->resolution / settings.xres; +    settings.pixels = pixels_per_line; +    settings.requested_pixels = pixels_per_line * xres_factor; -    // color filter      if (s->color_filter == "Red") { -        s->dev->settings.color_filter = ColorFilter::RED; +        settings.color_filter = ColorFilter::RED;      } else if (s->color_filter == "Green") { -        s->dev->settings.color_filter = ColorFilter::GREEN; +        settings.color_filter = ColorFilter::GREEN;      } else if (s->color_filter == "Blue") { -        s->dev->settings.color_filter = ColorFilter::BLUE; +        settings.color_filter = ColorFilter::BLUE;      } else { -        s->dev->settings.color_filter = ColorFilter::NONE; +        settings.color_filter = ColorFilter::NONE;      } -    // true gray      if (s->color_filter == "None") { -        s->dev->settings.true_gray = 1; +        settings.true_gray = 1;      } else { -        s->dev->settings.true_gray = 0; +        settings.true_gray = 0;      } -    // threshold curve for dynamic rasterization -    s->dev->settings.threshold_curve = s->threshold_curve; - -  /* some digital processing requires the whole picture to be buffered */ -  /* no digital processing takes place when doing preview, or when bit depth is -   * higher than 8 bits */ -  if ((s->swdespeck || s->swcrop || s->swdeskew || s->swderotate ||(SANE_UNFIX(s->swskip)>0)) -    && (!s->preview) -    && (s->bit_depth <= 8)) -    { -        s->dev->buffer_image = true; -    } -  else -    { -        s->dev->buffer_image = false; +    // brigthness and contrast only for for 8 bit scans +    if (s->bit_depth == 8) { +        settings.contrast = (s->contrast * 127) / 100; +        settings.brightness = (s->brightness * 127) / 100; +    } else { +        settings.contrast = 0; +        settings.brightness = 0;      } -  /* brigthness and contrast only for for 8 bit scans */ -  if(s->bit_depth <= 8) -    { -      s->dev->settings.contrast = (s->contrast * 127) / 100; -      s->dev->settings.brightness = (s->brightness * 127) / 100; -    } -  else -    { -      s->dev->settings.contrast=0; -      s->dev->settings.brightness=0; +    settings.expiration_time = s->expiration_time; + +    return settings; +} + +static SANE_Parameters calculate_scan_parameters(const Genesys_Device& dev, +                                                 const Genesys_Settings& settings) +{ +    DBG_HELPER(dbg); + +    auto sensor = sanei_genesys_find_sensor(&dev, settings.xres, settings.get_channels(), +                                            settings.scan_method); +    auto session = dev.cmd_set->calculate_scan_session(&dev, sensor, settings); +    auto pipeline = build_image_pipeline(dev, session, 0, false); + +    SANE_Parameters params; +    if (settings.scan_mode == ScanColorMode::GRAY) { +        params.format = SANE_FRAME_GRAY; +    } else { +        params.format = SANE_FRAME_RGB;      } +    // only single-pass scanning supported +    params.last_frame = true; +    params.depth = settings.depth; +    params.lines = pipeline.get_output_height(); +    params.pixels_per_line = pipeline.get_output_width(); +    params.bytes_per_line = pipeline.get_output_row_bytes(); -  /* cache expiration time */ -   s->dev->settings.expiration_time = s->expiration_time; +    return params;  } +static void calc_parameters(Genesys_Scanner* s) +{ +    DBG_HELPER(dbg); + +    s->dev->settings = calculate_scan_settings(s); +    s->params = calculate_scan_parameters(*s->dev, s->dev->settings); +}  static void create_bpp_list (Genesys_Scanner * s, const std::vector<unsigned>& bpp)  { @@ -3760,7 +4430,7 @@ static void create_bpp_list (Genesys_Scanner * s, const std::vector<unsigned>& b  /** @brief this function initialize a gamma vector based on the ASIC:   * Set up a default gamma table vector based on device description - * gl646: 12 or 14 bits gamma table depending on GENESYS_FLAG_14BIT_GAMMA + * gl646: 12 or 14 bits gamma table depending on ModelFlag::GAMMA_14BIT   * gl84x: 16 bits   * gl12x: 16 bits   * @param scanner pointer to scanner session to get options @@ -3776,8 +4446,7 @@ init_gamma_vector_option (Genesys_Scanner * scanner, int option)    scanner->opt[option].unit = SANE_UNIT_NONE;    scanner->opt[option].constraint_type = SANE_CONSTRAINT_RANGE;      if (scanner->dev->model->asic_type == AsicType::GL646) { -      if ((scanner->dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) != 0) -	{ +        if (has_flag(scanner->dev->model->flags, ModelFlag::GAMMA_14BIT)) {  	  scanner->opt[option].size = 16384 * sizeof (SANE_Word);  	  scanner->opt[option].constraint.range = &u14_range;  	} @@ -3802,9 +4471,9 @@ init_gamma_vector_option (Genesys_Scanner * scanner, int option)  static SANE_Range create_range(float size)  {      SANE_Range range; -    range.min = SANE_FIX(0.0); -    range.max = SANE_FIX(size); -    range.quant = SANE_FIX(0.0); +    range.min = float_to_fixed(0.0); +    range.max = float_to_fixed(size); +    range.quant = float_to_fixed(0.0);      return range;  } @@ -3855,7 +4524,7 @@ static std::string calibration_filename(Genesys_Device *currdev)    /* count models of the same names if several scanners attached */      if(s_devices->size() > 1) {          for (const auto& dev : *s_devices) { -            if (dev.model->model_id == currdev->model->model_id) { +            if (dev.vendorId == currdev->vendorId && dev.productId == currdev->productId) {                  count++;              }          } @@ -3921,13 +4590,13 @@ static void set_xy_range_option_values(Genesys_Scanner& s)  {      if (s.scan_method == ScanMethod::FLATBED)      { -        s.opt_x_range = create_range(static_cast<float>(s.dev->model->x_size)); -        s.opt_y_range = create_range(static_cast<float>(s.dev->model->y_size)); +        s.opt_x_range = create_range(s.dev->model->x_size); +        s.opt_y_range = create_range(s.dev->model->y_size);      }    else      { -        s.opt_x_range = create_range(static_cast<float>(s.dev->model->x_size_ta)); -        s.opt_y_range = create_range(static_cast<float>(s.dev->model->y_size_ta)); +        s.opt_x_range = create_range(s.dev->model->x_size_ta); +        s.opt_y_range = create_range(s.dev->model->y_size_ta);      }      s.opt[OPT_TL_X].constraint.range = &s.opt_x_range; @@ -3945,7 +4614,7 @@ static void init_options(Genesys_Scanner* s)  {      DBG_HELPER(dbg);    SANE_Int option; -  Genesys_Model *model = s->dev->model; +    const Genesys_Model* model = s->dev->model;    memset (s->opt, 0, sizeof (s->opt)); @@ -4038,8 +4707,8 @@ static void init_options(Genesys_Scanner* s)    s->opt[OPT_GEOMETRY_GROUP].size = 0;    s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; -    s->opt_x_range = create_range(static_cast<float>(model->x_size)); -    s->opt_y_range = create_range(static_cast<float>(model->y_size)); +    s->opt_x_range = create_range(model->x_size); +    s->opt_y_range = create_range(model->y_size);      // scan area    s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; @@ -4116,8 +4785,7 @@ static void init_options(Genesys_Scanner* s)    /* currently, there are only gamma table options in this group,     * so if the scanner doesn't support gamma table, disable the     * whole group */ -  if (!(model->flags & GENESYS_FLAG_CUSTOM_GAMMA)) -    { +    if (!has_flag(model->flags, ModelFlag::CUSTOM_GAMMA)) {        s->opt[OPT_ENHANCEMENT_GROUP].cap |= SANE_CAP_INACTIVE;        s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;        DBG(DBG_info, "%s: custom gamma disabled\n", __func__); @@ -4127,61 +4795,6 @@ static void init_options(Genesys_Scanner* s)     * memory than used by the full scanned image and may fail at high     * resolution     */ -  /* software deskew */ -  s->opt[OPT_SWDESKEW].name = "swdeskew"; -  s->opt[OPT_SWDESKEW].title = "Software deskew"; -  s->opt[OPT_SWDESKEW].desc = "Request backend to rotate skewed pages digitally"; -  s->opt[OPT_SWDESKEW].type = SANE_TYPE_BOOL; -  s->opt[OPT_SWDESKEW].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; -  s->swdeskew = false; - -  /* software deskew */ -  s->opt[OPT_SWDESPECK].name = "swdespeck"; -  s->opt[OPT_SWDESPECK].title = "Software despeck"; -  s->opt[OPT_SWDESPECK].desc = "Request backend to remove lone dots digitally"; -  s->opt[OPT_SWDESPECK].type = SANE_TYPE_BOOL; -  s->opt[OPT_SWDESPECK].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; -  s->swdespeck = false; - -  /* software despeckle radius */ -  s->opt[OPT_DESPECK].name = "despeck"; -  s->opt[OPT_DESPECK].title = "Software despeckle diameter"; -  s->opt[OPT_DESPECK].desc = "Maximum diameter of lone dots to remove from scan"; -  s->opt[OPT_DESPECK].type = SANE_TYPE_INT; -  s->opt[OPT_DESPECK].unit = SANE_UNIT_NONE; -  s->opt[OPT_DESPECK].constraint_type = SANE_CONSTRAINT_RANGE; -  s->opt[OPT_DESPECK].constraint.range = &swdespeck_range; -  s->opt[OPT_DESPECK].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED | SANE_CAP_INACTIVE; -  s->despeck = 1; - -  /* crop by software */ -  s->opt[OPT_SWCROP].name = "swcrop"; -  s->opt[OPT_SWCROP].title = SANE_I18N ("Software crop"); -  s->opt[OPT_SWCROP].desc = SANE_I18N ("Request backend to remove border from pages digitally"); -  s->opt[OPT_SWCROP].type = SANE_TYPE_BOOL; -  s->opt[OPT_SWCROP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; -  s->opt[OPT_SWCROP].unit = SANE_UNIT_NONE; -  s->swcrop = false; - -  /* Software blank page skip */ -  s->opt[OPT_SWSKIP].name = "swskip"; -  s->opt[OPT_SWSKIP].title = SANE_I18N ("Software blank skip percentage"); -  s->opt[OPT_SWSKIP].desc = SANE_I18N("Request driver to discard pages with low numbers of dark pixels"); -  s->opt[OPT_SWSKIP].type = SANE_TYPE_FIXED; -  s->opt[OPT_SWSKIP].unit = SANE_UNIT_PERCENT; -  s->opt[OPT_SWSKIP].constraint_type = SANE_CONSTRAINT_RANGE; -  s->opt[OPT_SWSKIP].constraint.range = &(percentage_range); -  s->opt[OPT_SWSKIP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; -  s->swskip = 0;    // disable by default - -  /* Software Derotate */ -  s->opt[OPT_SWDEROTATE].name = "swderotate"; -  s->opt[OPT_SWDEROTATE].title = SANE_I18N ("Software derotate"); -  s->opt[OPT_SWDEROTATE].desc = SANE_I18N("Request driver to detect and correct 90 degree image rotation"); -  s->opt[OPT_SWDEROTATE].type = SANE_TYPE_BOOL; -  s->opt[OPT_SWDEROTATE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED; -  s->opt[OPT_SWDEROTATE].unit = SANE_UNIT_NONE; -  s->swderotate = false;    /* Software brightness */    s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; @@ -4214,39 +4827,6 @@ static void init_options(Genesys_Scanner* s)    s->opt[OPT_EXTRAS_GROUP].size = 0;    s->opt[OPT_EXTRAS_GROUP].constraint_type = SANE_CONSTRAINT_NONE; -  /* BW threshold */ -  s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD; -  s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD; -  s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD; -  s->opt[OPT_THRESHOLD].type = SANE_TYPE_FIXED; -  s->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT; -  s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE; -  s->opt[OPT_THRESHOLD].constraint.range = &percentage_range; -  s->threshold = SANE_FIX(50); - -  /* BW threshold curve */ -  s->opt[OPT_THRESHOLD_CURVE].name = "threshold-curve"; -  s->opt[OPT_THRESHOLD_CURVE].title = SANE_I18N ("Threshold curve"); -  s->opt[OPT_THRESHOLD_CURVE].desc = SANE_I18N ("Dynamic threshold curve, from light to dark, normally 50-65"); -  s->opt[OPT_THRESHOLD_CURVE].type = SANE_TYPE_INT; -  s->opt[OPT_THRESHOLD_CURVE].unit = SANE_UNIT_NONE; -  s->opt[OPT_THRESHOLD_CURVE].constraint_type = SANE_CONSTRAINT_RANGE; -  s->opt[OPT_THRESHOLD_CURVE].constraint.range = &threshold_curve_range; -  s->threshold_curve = 50; - -  /* disable_interpolation */ -  s->opt[OPT_DISABLE_INTERPOLATION].name = "disable-interpolation"; -  s->opt[OPT_DISABLE_INTERPOLATION].title = -    SANE_I18N ("Disable interpolation"); -  s->opt[OPT_DISABLE_INTERPOLATION].desc = -    SANE_I18N -    ("When using high resolutions where the horizontal resolution is smaller " -     "than the vertical resolution this disables horizontal interpolation."); -  s->opt[OPT_DISABLE_INTERPOLATION].type = SANE_TYPE_BOOL; -  s->opt[OPT_DISABLE_INTERPOLATION].unit = SANE_UNIT_NONE; -  s->opt[OPT_DISABLE_INTERPOLATION].constraint_type = SANE_CONSTRAINT_NONE; -  s->disable_interpolation = false; -    /* color filter */    s->opt[OPT_COLOR_FILTER].name = "color-filter";    s->opt[OPT_COLOR_FILTER].title = SANE_I18N ("Color filter"); @@ -4508,36 +5088,39 @@ check_present (SANE_String_Const devname) noexcept    return SANE_STATUS_GOOD;  } -static Genesys_Device* attach_usb_device(const char* devname, -                                         std::uint16_t vendor_id, std::uint16_t product_id) +const UsbDeviceEntry& get_matching_usb_dev(std::uint16_t vendor_id, std::uint16_t product_id, +                                           std::uint16_t bcd_device)  { -    Genesys_USB_Device_Entry* found_usb_dev = nullptr;      for (auto& usb_dev : *s_usb_devices) { -        if (usb_dev.vendor == vendor_id && -            usb_dev.product == product_id) -        { -            found_usb_dev = &usb_dev; -            break; +        if (usb_dev.matches(vendor_id, product_id, bcd_device)) { +            return usb_dev;          }      } -    if (found_usb_dev == nullptr) { -        throw SaneException("vendor 0x%xd product 0x%xd is not supported by this backend", -                            vendor_id, product_id); -    } +    throw SaneException("vendor 0x%x product 0x%x (bcdDevice 0x%x) " +                        "is not supported by this backend", +                        vendor_id, product_id, bcd_device); +} + +static Genesys_Device* attach_usb_device(const char* devname, +                                         std::uint16_t vendor_id, std::uint16_t product_id, +                                         std::uint16_t bcd_device) +{ +    const auto& usb_dev = get_matching_usb_dev(vendor_id, product_id, bcd_device);      s_devices->emplace_back();      Genesys_Device* dev = &s_devices->back();      dev->file_name = devname; - -    dev->model = &found_usb_dev->model; -    dev->vendorId = found_usb_dev->vendor; -    dev->productId = found_usb_dev->product; +    dev->vendorId = vendor_id; +    dev->productId = product_id; +    dev->model = &usb_dev.model();      dev->usb_mode = 0; // i.e. unset      dev->already_initialized = false;      return dev;  } +static bool s_attach_device_by_name_evaluate_bcd_device = false; +  static Genesys_Device* attach_device_by_name(SANE_String_Const devname, bool may_wait)  {      DBG_HELPER_ARGS(dbg, " devname: %s, may_wait = %d", devname, may_wait); @@ -4560,26 +5143,31 @@ static Genesys_Device* attach_device_by_name(SANE_String_Const devname, bool may      usb_dev.open(devname);      DBG(DBG_info, "%s: device `%s' successfully opened\n", __func__, devname); -    int vendor, product; -    usb_dev.get_vendor_product(vendor, product); +    auto vendor_id = usb_dev.get_vendor_id(); +    auto product_id = usb_dev.get_product_id(); +    auto bcd_device = UsbDeviceEntry::BCD_DEVICE_NOT_SET; +    if (s_attach_device_by_name_evaluate_bcd_device) { +        // when the device is already known before scanning, we don't want to call get_bcd_device() +        // when iterating devices, as that will interfere with record/replay during testing. +        bcd_device = usb_dev.get_bcd_device(); +    }      usb_dev.close();    /* KV-SS080 is an auxiliary device which requires a master device to be here */ -  if(vendor == 0x04da && product == 0x100f) -    { +    if (vendor_id == 0x04da && product_id == 0x100f) {          present = false; -      sanei_usb_find_devices (vendor, 0x1006, check_present); -      sanei_usb_find_devices (vendor, 0x1007, check_present); -      sanei_usb_find_devices (vendor, 0x1010, check_present); +        sanei_usb_find_devices(vendor_id, 0x1006, check_present); +        sanei_usb_find_devices(vendor_id, 0x1007, check_present); +        sanei_usb_find_devices(vendor_id, 0x1010, check_present);          if (present == false) {              throw SaneException("master device not present");          }      } -    Genesys_Device* dev = attach_usb_device(devname, vendor, product); +    Genesys_Device* dev = attach_usb_device(devname, vendor_id, product_id, bcd_device); -    DBG(DBG_info, "%s: found %s flatbed scanner %s at %s\n", __func__, dev->model->vendor, -        dev->model->model, dev->file_name.c_str()); +    DBG(DBG_info, "%s: found %u flatbed scanner %u at %s\n", __func__, vendor_id, product_id, +        dev->file_name.c_str());      return dev;  } @@ -4614,7 +5202,8 @@ static void probe_genesys_devices()      DBG_HELPER(dbg);      if (is_testing_mode()) {          attach_usb_device(get_testing_device_name().c_str(), -                          get_testing_vendor_id(), get_testing_product_id()); +                          get_testing_vendor_id(), get_testing_product_id(), +                          get_testing_bcd_device());          return;      } @@ -4625,7 +5214,12 @@ static void probe_genesys_devices()      config.values = nullptr;    config.count = 0; -    TIE(sanei_configure_attach(GENESYS_CONFIG_FILE, &config, config_attach_genesys)); +    auto status = sanei_configure_attach(GENESYS_CONFIG_FILE, &config, config_attach_genesys); +    if (status == SANE_STATUS_ACCESS_DENIED) { +        dbg.vlog(DBG_error0, "Critical error: Couldn't access configuration file '%s'", +                 GENESYS_CONFIG_FILE); +    } +    TIE(status);      DBG(DBG_info, "%s: %zu devices currently attached\n", __func__, s_devices->size());  } @@ -4637,7 +5231,7 @@ static void probe_genesys_devices()     of Genesys_Calibration_Cache as is.  */  static const char* CALIBRATION_IDENT = "sane_genesys"; -static const int CALIBRATION_VERSION = 21; +static const int CALIBRATION_VERSION = 31;  bool read_calibration(std::istream& str, Genesys_Device::Calibration& calibration,                        const std::string& path) @@ -4706,114 +5300,6 @@ static void write_calibration(Genesys_Device::Calibration& calibration, const st      write_calibration(str, calibration);  } -/** @brief buffer scanned picture - * In order to allow digital processing, we must be able to put all the - * scanned picture in a buffer. - */ -static void genesys_buffer_image(Genesys_Scanner *s) -{ -    DBG_HELPER(dbg); -  size_t maximum;     /**> maximum bytes size of the scan */ -  size_t len;	      /**> length of scanned data read */ -  size_t total;	      /**> total of butes read */ -  size_t size;	      /**> size of image buffer */ -  size_t read_size;   /**> size of reads */ -  int lines;	      /** number of lines of the scan */ -  Genesys_Device *dev = s->dev; - -  /* compute maximum number of lines for the scan */ -  if (s->params.lines > 0) -    { -      lines = s->params.lines; -    } -  else -    { -        lines = static_cast<int>((dev->model->y_size * dev->settings.yres) / MM_PER_INCH); -    } -  DBG(DBG_info, "%s: buffering %d lines of %d bytes\n", __func__, lines, -      s->params.bytes_per_line); - -  /* maximum bytes to read */ -  maximum = s->params.bytes_per_line * lines; -    if (s->dev->settings.scan_mode == ScanColorMode::LINEART) { -      maximum *= 8; -    } - -  /* initial size of the read buffer */ -  size = -    ((2048 * 2048) / s->params.bytes_per_line) * s->params.bytes_per_line; - -  /* read size */ -  read_size = size / 2; - -  dev->img_buffer.resize(size); - -  /* loop reading data until we reach maximum or EOF */ -    total = 0; -    while (total < maximum) { -      len = size - maximum; -      if (len > read_size) -	{ -	  len = read_size; -	} - -        try { -            genesys_read_ordered_data(dev, dev->img_buffer.data() + total, &len); -        } catch (const SaneException& e) { -            if (e.status() == SANE_STATUS_EOF) { -                // ideally we shouldn't end up here, but because computations are duplicated and -                // slightly different everywhere in the genesys backend, we have no other choice -                break; -            } -            throw; -        } -      total += len; - -        // do we need to enlarge read buffer ? -        if (total + read_size > size) { -            size += read_size; -            dev->img_buffer.resize(size); -        } -    } - -  /* since digital processing is going to take place, -   * issue head parking command so that the head move while -   * computing so we can save time -   */ -    if (!dev->model->is_sheetfed && !dev->parking) { -        dev->cmd_set->move_back_home(dev, dev->model->flags & GENESYS_FLAG_MUST_WAIT); -      dev->parking = !(s->dev->model->flags & GENESYS_FLAG_MUST_WAIT); -    } - -  /* in case of dynamic lineart, we have buffered gray data which -   * must be converted to lineart first */ -    if (s->dev->settings.scan_mode == ScanColorMode::LINEART) { -      total/=8; -      std::vector<uint8_t> lineart(total); - -      genesys_gray_lineart (dev, -                            dev->img_buffer.data(), -                            lineart.data(), -                            dev->settings.pixels, -                            (total*8)/dev->settings.pixels, -                            dev->settings.threshold); -      dev->img_buffer = lineart; -    } - -  /* update counters */ -  dev->total_bytes_to_read = total; -  dev->total_bytes_read = 0; - -  /* update params */ -  s->params.lines = total / s->params.bytes_per_line; -  if (DBG_LEVEL >= DBG_io2) -    { -      sanei_genesys_write_pnm_file("gl_unprocessed.pnm", dev->img_buffer.data(), s->params.depth, -                                   s->params.format==SANE_FRAME_RGB ? 3 : 1, -                                   s->params.pixels_per_line, s->params.lines); -    } -} -  /* -------------------------- SANE API functions ------------------------- */  void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize) @@ -4839,9 +5325,6 @@ void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize)          sanei_usb_init();      } -  /* init sanei_magic */ -  sanei_magic_init(); -    s_scanners.init();    s_devices.init();    s_sane_devices.init(); @@ -4850,8 +5333,8 @@ 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_motor_profile_tables();      genesys_init_usb_device_tables(); @@ -4864,6 +5347,7 @@ void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize)      );      // cold-plug case :detection of allready connected scanners +    s_attach_device_by_name_evaluate_bcd_device = false;      probe_genesys_devices();  } @@ -4903,6 +5387,7 @@ void sane_get_devices_impl(const SANE_Device *** device_list, SANE_Bool local_on          // hot-plug case : detection of newly connected scanners */          sanei_usb_scan_devices();      } +    s_attach_device_by_name_evaluate_bcd_device = true;      probe_genesys_devices();      s_sane_devices->clear(); @@ -4969,7 +5454,7 @@ static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle)          }          if (dev) { -            DBG(DBG_info, "%s: found `%s' in devlist\n", __func__, dev->model->name); +            DBG(DBG_info, "%s: found `%s' in devlist\n", __func__, dev->file_name.c_str());          } else if (is_testing_mode()) {              DBG(DBG_info, "%s: couldn't find `%s' in devlist, not attaching", __func__, devicename);          } else { @@ -4991,37 +5476,53 @@ static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle)          throw SaneException("could not find the device to open: %s", devicename);      } -  if (dev->model->flags & GENESYS_FLAG_UNTESTED) -    { -      DBG(DBG_error0, "WARNING: Your scanner is not fully supported or at least \n"); -      DBG(DBG_error0, "         had only limited testing. Please be careful and \n"); -      DBG(DBG_error0, "         report any failure/success to \n"); -      DBG(DBG_error0, "         sane-devel@alioth-lists.debian.net. Please provide as many\n"); -      DBG(DBG_error0, "         details as possible, e.g. the exact name of your\n"); -      DBG(DBG_error0, "         scanner and what does (not) work.\n"); -    } +    if (is_testing_mode()) { +        // during testing we need to initialize dev->model before test scanner interface is created +        // as that it needs to know what type of chip it needs to mimic. +        auto vendor_id = get_testing_vendor_id(); +        auto product_id = get_testing_product_id(); +        auto bcd_device = get_testing_bcd_device(); -    dbg.vstatus("open device '%s'", dev->file_name.c_str()); +        dev->model = &get_matching_usb_dev(vendor_id, product_id, bcd_device).model(); -    if (is_testing_mode()) { -        auto interface = std::unique_ptr<TestScannerInterface>{new TestScannerInterface{dev}}; +        auto interface = std::unique_ptr<TestScannerInterface>{ +                new TestScannerInterface{dev, vendor_id, product_id, bcd_device}};          interface->set_checkpoint_callback(get_testing_checkpoint_callback());          dev->interface = std::move(interface); + +        dev->interface->get_usb_device().open(dev->file_name.c_str());      } else {          dev->interface = std::unique_ptr<ScannerInterfaceUsb>{new ScannerInterfaceUsb{dev}}; + +        dbg.vstatus("open device '%s'", dev->file_name.c_str()); +        dev->interface->get_usb_device().open(dev->file_name.c_str()); +        dbg.clear(); + +        auto bcd_device = dev->interface->get_usb_device().get_bcd_device(); + +        dev->model = &get_matching_usb_dev(dev->vendorId, dev->productId, bcd_device).model(); +    } + +    dbg.vlog(DBG_info, "Opened device %s", dev->model->name); + +    if (has_flag(dev->model->flags, ModelFlag::UNTESTED)) { +        DBG(DBG_error0, "WARNING: Your scanner is not fully supported or at least \n"); +        DBG(DBG_error0, "         had only limited testing. Please be careful and \n"); +        DBG(DBG_error0, "         report any failure/success to \n"); +        DBG(DBG_error0, "         sane-devel@alioth-lists.debian.net. Please provide as many\n"); +        DBG(DBG_error0, "         details as possible, e.g. the exact name of your\n"); +        DBG(DBG_error0, "         scanner and what does (not) work.\n");      } -    dev->interface->get_usb_device().open(dev->file_name.c_str()); -    dbg.clear();    s_scanners->push_back(Genesys_Scanner());    auto* s = &s_scanners->back(); -  s->dev = dev; +    s->dev = dev;      s->scanning = false; -    s->dev->parking = false; -    s->dev->read_active = false; -  s->dev->force_calibration = 0; -  s->dev->line_count = 0; +    dev->parking = false; +    dev->read_active = false; +    dev->force_calibration = 0; +    dev->line_count = 0;    *handle = s; @@ -5029,31 +5530,18 @@ static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle)          sanei_genesys_init_structs (dev);      } +    dev->cmd_set = create_cmd_set(dev->model->asic_type); +      init_options(s); -    sanei_genesys_init_cmd_set(s->dev); +    DBG_INIT();      // FIXME: we create sensor tables for the sensor, this should happen when we know which sensor      // we will select      dev->cmd_set->init(dev);      // some hardware capabilities are detected through sensors -    s->dev->cmd_set->update_hardware_sensors (s); - -  /* here is the place to fetch a stored calibration cache */ -  if (s->dev->force_calibration == 0) -    { -        auto path = calibration_filename(s->dev); -        s->calibration_file = path; -        s->dev->calib_file = path; -      DBG(DBG_info, "%s: Calibration filename set to:\n", __func__); -      DBG(DBG_info, "%s: >%s<\n", __func__, s->dev->calib_file.c_str()); - -        catch_all_exceptions(__func__, [&]() -        { -            sanei_genesys_read_calibration(s->dev->calibration_cache, s->dev->calib_file); -        }); -    } +    dev->cmd_set->update_hardware_sensors (s);  }  SANE_GENESYS_API_LINKAGE @@ -5085,46 +5573,42 @@ sane_close_impl(SANE_Handle handle)        return;			/* oops, not a handle we know about */      } -  Genesys_Scanner* s = &*it; +    auto* dev = it->dev; -  /* eject document for sheetfed scanners */ -    if (s->dev->model->is_sheetfed) { -        catch_all_exceptions(__func__, [&](){ s->dev->cmd_set->eject_document(s->dev); }); -    } -  else -    { -      /* in case scanner is parking, wait for the head -       * to reach home position */ -        if (s->dev->parking) { -            sanei_genesys_wait_for_home(s->dev); +    // eject document for sheetfed scanners +    if (dev->model->is_sheetfed) { +        catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); }); +    } else { +        // in case scanner is parking, wait for the head to reach home position +        if (dev->parking) { +            sanei_genesys_wait_for_home(dev);          }      }      // enable power saving before leaving -    s->dev->cmd_set->save_power(s->dev, true); +    dev->cmd_set->save_power(dev, true);      // here is the place to store calibration cache -    if (s->dev->force_calibration == 0 && !is_testing_mode()) { -        catch_all_exceptions(__func__, [&](){ write_calibration(s->dev->calibration_cache, -                                                                s->dev->calib_file); }); +    if (dev->force_calibration == 0 && !is_testing_mode()) { +        catch_all_exceptions(__func__, [&](){ write_calibration(dev->calibration_cache, +                                                                dev->calib_file); });      } -    s->dev->already_initialized = false; - -  s->dev->clear(); +    dev->already_initialized = false; +    dev->clear();      // LAMP OFF : same register across all the ASICs */ -    s->dev->interface->write_register(0x03, 0x00); +    dev->interface->write_register(0x03, 0x00); -    catch_all_exceptions(__func__, [&](){ s->dev->interface->get_usb_device().clear_halt(); }); +    catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().clear_halt(); });      // we need this to avoid these ASIC getting stuck in bulk writes -    catch_all_exceptions(__func__, [&](){ s->dev->interface->get_usb_device().reset(); }); +    catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().reset(); }); -    // not freeing s->dev because it's in the dev list -    catch_all_exceptions(__func__, [&](){ s->dev->interface->get_usb_device().close(); }); +    // not freeing dev because it's in the dev list +    catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().close(); }); -  s_scanners->erase(it); +    s_scanners->erase(it);  }  SANE_GENESYS_API_LINKAGE @@ -5174,7 +5658,7 @@ static void print_option(DebugMessageHelper& dbg, const Genesys_Scanner& s, int              return;          }          case SANE_TYPE_FIXED: { -            dbg.vlog(DBG_proc, "value: %f", SANE_UNFIX(*reinterpret_cast<SANE_Word*>(val))); +            dbg.vlog(DBG_proc, "value: %f", fixed_to_float(*reinterpret_cast<SANE_Word*>(val)));              return;          }          case SANE_TYPE_STRING: { @@ -5189,18 +5673,19 @@ static void print_option(DebugMessageHelper& dbg, const Genesys_Scanner& s, int  static void get_option_value(Genesys_Scanner* s, int option, void* val)  {      DBG_HELPER_ARGS(dbg, "option: %s (%d)", s->opt[option].name, option); +    auto* dev = s->dev;    unsigned int i;      SANE_Word* table = nullptr;    std::vector<uint16_t> gamma_table;    unsigned option_size = 0;      const Genesys_Sensor* sensor = nullptr; -    if (sanei_genesys_has_sensor(s->dev, s->dev->settings.xres, s->dev->settings.get_channels(), -                                 s->dev->settings.scan_method)) +    if (sanei_genesys_has_sensor(dev, dev->settings.xres, dev->settings.get_channels(), +                                 dev->settings.scan_method))      { -        sensor = &sanei_genesys_find_sensor(s->dev, s->dev->settings.xres, -                                            s->dev->settings.get_channels(), -                                            s->dev->settings.scan_method); +        sensor = &sanei_genesys_find_sensor(dev, dev->settings.xres, +                                            dev->settings.get_channels(), +                                            dev->settings.scan_method);      }    switch (option) @@ -5231,39 +5716,12 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val)      case OPT_PREVIEW:          *reinterpret_cast<SANE_Word*>(val) = s->preview;          break; -    case OPT_THRESHOLD: -        *reinterpret_cast<SANE_Word*>(val) = s->threshold; -        break; -    case OPT_THRESHOLD_CURVE: -        *reinterpret_cast<SANE_Word*>(val) = s->threshold_curve; -        break; -    case OPT_DISABLE_INTERPOLATION: -        *reinterpret_cast<SANE_Word*>(val) = s->disable_interpolation; -        break;      case OPT_LAMP_OFF:          *reinterpret_cast<SANE_Word*>(val) = s->lamp_off;          break;      case OPT_LAMP_OFF_TIME:          *reinterpret_cast<SANE_Word*>(val) = s->lamp_off_time;          break; -    case OPT_SWDESKEW: -        *reinterpret_cast<SANE_Word*>(val) = s->swdeskew; -        break; -    case OPT_SWCROP: -        *reinterpret_cast<SANE_Word*>(val) = s->swcrop; -        break; -    case OPT_SWDESPECK: -        *reinterpret_cast<SANE_Word*>(val) = s->swdespeck; -        break; -    case OPT_SWDEROTATE: -        *reinterpret_cast<SANE_Word*>(val) = s->swderotate; -        break; -    case OPT_SWSKIP: -        *reinterpret_cast<SANE_Word*>(val) = s->swskip; -        break; -    case OPT_DESPECK: -        *reinterpret_cast<SANE_Word*>(val) = s->despeck; -        break;      case OPT_CONTRAST:          *reinterpret_cast<SANE_Word*>(val) = s->contrast;          break; @@ -5297,13 +5755,13 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val)              throw SaneException("Unsupported scanner mode selected");          table = reinterpret_cast<SANE_Word*>(val); -        if (s->color_filter == "Red") { -            gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_RED); -        } else if (s->color_filter == "Blue") { -            gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_BLUE); -        } else { -            gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_GREEN); -        } +            if (s->color_filter == "Red") { +                gamma_table = get_gamma_table(dev, *sensor, GENESYS_RED); +            } else if (s->color_filter == "Blue") { +                gamma_table = get_gamma_table(dev, *sensor, GENESYS_BLUE); +            } else { +                gamma_table = get_gamma_table(dev, *sensor, GENESYS_GREEN); +            }          option_size = s->opt[option].size / sizeof (SANE_Word);          if (gamma_table.size() != option_size) {              throw std::runtime_error("The size of the gamma tables does not match"); @@ -5317,7 +5775,7 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val)              throw SaneException("Unsupported scanner mode selected");          table = reinterpret_cast<SANE_Word*>(val); -        gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_RED); +            gamma_table = get_gamma_table(dev, *sensor, GENESYS_RED);          option_size = s->opt[option].size / sizeof (SANE_Word);          if (gamma_table.size() != option_size) {              throw std::runtime_error("The size of the gamma tables does not match"); @@ -5331,7 +5789,7 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val)              throw SaneException("Unsupported scanner mode selected");          table = reinterpret_cast<SANE_Word*>(val); -        gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_GREEN); +            gamma_table = get_gamma_table(dev, *sensor, GENESYS_GREEN);          option_size = s->opt[option].size / sizeof (SANE_Word);          if (gamma_table.size() != option_size) {              throw std::runtime_error("The size of the gamma tables does not match"); @@ -5345,7 +5803,7 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val)              throw SaneException("Unsupported scanner mode selected");          table = reinterpret_cast<SANE_Word*>(val); -        gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_BLUE); +            gamma_table = get_gamma_table(dev, *sensor, GENESYS_BLUE);          option_size = s->opt[option].size / sizeof (SANE_Word);          if (gamma_table.size() != option_size) {              throw std::runtime_error("The size of the gamma tables does not match"); @@ -5377,11 +5835,10 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val)              bool result = true; -            auto session = s->dev->cmd_set->calculate_scan_session(s->dev, *sensor, -                                                                   s->dev->settings); +            auto session = dev->cmd_set->calculate_scan_session(dev, *sensor, dev->settings); -            for (auto& cache : s->dev->calibration_cache) { -                if (sanei_genesys_is_compatible_calibration(s->dev, session, &cache, false)) { +            for (auto& cache : dev->calibration_cache) { +                if (sanei_genesys_is_compatible_calibration(dev, session, &cache, false)) {                      *reinterpret_cast<SANE_Bool*>(val) = SANE_FALSE;                  }              } @@ -5400,6 +5857,7 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val)  static void set_calibration_value(Genesys_Scanner* s, const char* val)  {      DBG_HELPER(dbg); +    auto dev = s->dev;      std::string new_calib_path = val;      Genesys_Device::Calibration new_calibration; @@ -5414,8 +5872,8 @@ static void set_calibration_value(Genesys_Scanner* s, const char* val)          return;      } -    s->dev->calibration_cache = std::move(new_calibration); -    s->dev->calib_file = new_calib_path; +    dev->calibration_cache = std::move(new_calibration); +    dev->calib_file = new_calib_path;      s->calibration_file = new_calib_path;      DBG(DBG_info, "%s: Calibration filename set to '%s':\n", __func__, new_calib_path.c_str());  } @@ -5426,12 +5884,13 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int      DBG_HELPER_ARGS(dbg, "option: %s (%d)", s->opt[option].name, option);      print_option(dbg, *s, option, val); +    auto* dev = s->dev; +    SANE_Word *table;    unsigned int i;    unsigned option_size = 0; -  switch (option) -    { +    switch (option) {      case OPT_TL_X:          s->pos_top_left_x = *reinterpret_cast<SANE_Word*>(val);          calc_parameters(s); @@ -5457,46 +5916,6 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int          calc_parameters(s);          *myinfo |= SANE_INFO_RELOAD_PARAMS;          break; -    case OPT_THRESHOLD: -        s->threshold = *reinterpret_cast<SANE_Word*>(val); -        calc_parameters(s); -        *myinfo |= SANE_INFO_RELOAD_PARAMS; -        break; -    case OPT_THRESHOLD_CURVE: -        s->threshold_curve = *reinterpret_cast<SANE_Word*>(val); -        calc_parameters(s); -        *myinfo |= SANE_INFO_RELOAD_PARAMS; -        break; -    case OPT_SWCROP: -        s->swcrop = *reinterpret_cast<SANE_Word*>(val); -        calc_parameters(s); -        *myinfo |= SANE_INFO_RELOAD_PARAMS; -        break; -    case OPT_SWDESKEW: -        s->swdeskew = *reinterpret_cast<SANE_Word*>(val); -        calc_parameters(s); -        *myinfo |= SANE_INFO_RELOAD_PARAMS; -        break; -    case OPT_DESPECK: -        s->despeck = *reinterpret_cast<SANE_Word*>(val); -        calc_parameters(s); -        *myinfo |= SANE_INFO_RELOAD_PARAMS; -        break; -    case OPT_SWDEROTATE: -        s->swderotate = *reinterpret_cast<SANE_Word*>(val); -        calc_parameters(s); -        *myinfo |= SANE_INFO_RELOAD_PARAMS; -        break; -    case OPT_SWSKIP: -        s->swskip = *reinterpret_cast<SANE_Word*>(val); -        calc_parameters(s); -        *myinfo |= SANE_INFO_RELOAD_PARAMS; -        break; -    case OPT_DISABLE_INTERPOLATION: -        s->disable_interpolation = *reinterpret_cast<SANE_Word*>(val); -        calc_parameters(s); -        *myinfo |= SANE_INFO_RELOAD_PARAMS; -        break;      case OPT_LAMP_OFF:          s->lamp_off = *reinterpret_cast<SANE_Word*>(val);          calc_parameters(s); @@ -5517,38 +5936,14 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int          calc_parameters(s);          *myinfo |= SANE_INFO_RELOAD_PARAMS;          break; -    case OPT_SWDESPECK: -        s->swdespeck = *reinterpret_cast<SANE_Word*>(val); -        if (s->swdespeck) { -            ENABLE(OPT_DESPECK); -        } else { -            DISABLE(OPT_DESPECK); -        } -        calc_parameters(s); -      *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; -      break;      /* software enhancement functions only apply to 8 or 1 bits data */      case OPT_BIT_DEPTH:          s->bit_depth = *reinterpret_cast<SANE_Word*>(val);          if(s->bit_depth>8)          { -          DISABLE(OPT_SWDESKEW); -          DISABLE(OPT_SWDESPECK); -          DISABLE(OPT_SWCROP); -          DISABLE(OPT_DESPECK); -          DISABLE(OPT_SWDEROTATE); -          DISABLE(OPT_SWSKIP);            DISABLE(OPT_CONTRAST);            DISABLE(OPT_BRIGHTNESS); -        } -      else -        { -          ENABLE(OPT_SWDESKEW); -          ENABLE(OPT_SWDESPECK); -          ENABLE(OPT_SWCROP); -          ENABLE(OPT_DESPECK); -          ENABLE(OPT_SWDEROTATE); -          ENABLE(OPT_SWSKIP); +            } else {            ENABLE(OPT_CONTRAST);            ENABLE(OPT_BRIGHTNESS);          } @@ -5567,38 +5962,22 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int          }          break;      } -    case OPT_MODE: -      s->mode = reinterpret_cast<const char*>(val); +        case OPT_MODE: { +            s->mode = reinterpret_cast<const char*>(val); -      if (s->mode == SANE_VALUE_SCAN_MODE_LINEART) -	{ -	  ENABLE (OPT_THRESHOLD); -	  ENABLE (OPT_THRESHOLD_CURVE); -	  DISABLE (OPT_BIT_DEPTH); -                if (s->dev->model->asic_type != AsicType::GL646 || !s->dev->model->is_cis) { +            if (s->mode == SANE_VALUE_SCAN_MODE_GRAY) { +                if (dev->model->asic_type != AsicType::GL646 || !dev->model->is_cis) {                      ENABLE(OPT_COLOR_FILTER);                  } -	} -      else -	{ -	  DISABLE (OPT_THRESHOLD); -	  DISABLE (OPT_THRESHOLD_CURVE); -          if (s->mode == SANE_VALUE_SCAN_MODE_GRAY) -	    { -                    if (s->dev->model->asic_type != AsicType::GL646 || !s->dev->model->is_cis) { -                        ENABLE(OPT_COLOR_FILTER); -                    } -	      create_bpp_list (s, s->dev->model->bpp_gray_values); -                    s->bit_depth = s->dev->model->bpp_gray_values[0]; -	    } -	  else -	    { -	      DISABLE (OPT_COLOR_FILTER); -	      create_bpp_list (s, s->dev->model->bpp_color_values); -                    s->bit_depth = s->dev->model->bpp_color_values[0]; -	    } -	} -        calc_parameters(s); +                create_bpp_list(s, dev->model->bpp_gray_values); +                s->bit_depth = dev->model->bpp_gray_values[0]; +            } else { +                DISABLE(OPT_COLOR_FILTER); +                create_bpp_list(s, dev->model->bpp_color_values); +                s->bit_depth = dev->model->bpp_color_values[0]; +            } + +            calc_parameters(s);        /* if custom gamma, toggle gamma table options according to the mode */        if (s->custom_gamma) @@ -5621,30 +6000,31 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int        *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;        break; +        }      case OPT_COLOR_FILTER:        s->color_filter = reinterpret_cast<const char*>(val);          calc_parameters(s);        break; -    case OPT_CALIBRATION_FILE: -            if (s->dev->force_calibration == 0) { +        case OPT_CALIBRATION_FILE: { +            if (dev->force_calibration == 0) {                  set_calibration_value(s, reinterpret_cast<const char*>(val));              }              break; -    case OPT_LAMP_OFF_TIME: -        if (*reinterpret_cast<SANE_Word*>(val) != s->lamp_off_time) { -            s->lamp_off_time = *reinterpret_cast<SANE_Word*>(val); -                s->dev->cmd_set->set_powersaving(s->dev, s->lamp_off_time);          } -        break; -    case OPT_EXPIRATION_TIME: -        if (*reinterpret_cast<SANE_Word*>(val) != s->expiration_time) { -            s->expiration_time = *reinterpret_cast<SANE_Word*>(val); -            // BUG: this is most likely not intended behavior, found out during refactor -                s->dev->cmd_set->set_powersaving(s->dev, s->expiration_time); -	} -        break; - -    case OPT_CUSTOM_GAMMA: +        case OPT_LAMP_OFF_TIME: { +            if (*reinterpret_cast<SANE_Word*>(val) != s->lamp_off_time) { +                s->lamp_off_time = *reinterpret_cast<SANE_Word*>(val); +                    dev->cmd_set->set_powersaving(dev, s->lamp_off_time); +            } +            break; +        } +        case OPT_EXPIRATION_TIME: { +            if (*reinterpret_cast<SANE_Word*>(val) != s->expiration_time) { +                s->expiration_time = *reinterpret_cast<SANE_Word*>(val); +            } +            break; +        } +        case OPT_CUSTOM_GAMMA: {        *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;          s->custom_gamma = *reinterpret_cast<SANE_Bool*>(val); @@ -5670,88 +6050,96 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int  	  DISABLE (OPT_GAMMA_VECTOR_R);  	  DISABLE (OPT_GAMMA_VECTOR_G);  	  DISABLE (OPT_GAMMA_VECTOR_B); -            for (auto& table : s->dev->gamma_override_tables) { -                table.clear(); +                for (auto& table : dev->gamma_override_tables) { +                    table.clear(); +                }              } -	} -      break; - -    case OPT_GAMMA_VECTOR: -        table = reinterpret_cast<SANE_Word*>(val); -        option_size = s->opt[option].size / sizeof (SANE_Word); +            break; +        } -        s->dev->gamma_override_tables[GENESYS_RED].resize(option_size); -        s->dev->gamma_override_tables[GENESYS_GREEN].resize(option_size); -        s->dev->gamma_override_tables[GENESYS_BLUE].resize(option_size); -        for (i = 0; i < option_size; i++) { -            s->dev->gamma_override_tables[GENESYS_RED][i] = table[i]; -            s->dev->gamma_override_tables[GENESYS_GREEN][i] = table[i]; -            s->dev->gamma_override_tables[GENESYS_BLUE][i] = table[i]; +        case OPT_GAMMA_VECTOR: { +            table = reinterpret_cast<SANE_Word*>(val); +            option_size = s->opt[option].size / sizeof (SANE_Word); + +            dev->gamma_override_tables[GENESYS_RED].resize(option_size); +            dev->gamma_override_tables[GENESYS_GREEN].resize(option_size); +            dev->gamma_override_tables[GENESYS_BLUE].resize(option_size); +            for (i = 0; i < option_size; i++) { +                dev->gamma_override_tables[GENESYS_RED][i] = table[i]; +                dev->gamma_override_tables[GENESYS_GREEN][i] = table[i]; +                dev->gamma_override_tables[GENESYS_BLUE][i] = table[i]; +            } +            break;          } -      break; -    case OPT_GAMMA_VECTOR_R: -        table = reinterpret_cast<SANE_Word*>(val); -        option_size = s->opt[option].size / sizeof (SANE_Word); -        s->dev->gamma_override_tables[GENESYS_RED].resize(option_size); -        for (i = 0; i < option_size; i++) { -            s->dev->gamma_override_tables[GENESYS_RED][i] = table[i]; +        case OPT_GAMMA_VECTOR_R: { +            table = reinterpret_cast<SANE_Word*>(val); +            option_size = s->opt[option].size / sizeof (SANE_Word); +            dev->gamma_override_tables[GENESYS_RED].resize(option_size); +            for (i = 0; i < option_size; i++) { +                dev->gamma_override_tables[GENESYS_RED][i] = table[i]; +            } +            break;          } -      break; -    case OPT_GAMMA_VECTOR_G: -        table = reinterpret_cast<SANE_Word*>(val); -        option_size = s->opt[option].size / sizeof (SANE_Word); -        s->dev->gamma_override_tables[GENESYS_GREEN].resize(option_size); -        for (i = 0; i < option_size; i++) { -            s->dev->gamma_override_tables[GENESYS_GREEN][i] = table[i]; +        case OPT_GAMMA_VECTOR_G: { +            table = reinterpret_cast<SANE_Word*>(val); +            option_size = s->opt[option].size / sizeof (SANE_Word); +            dev->gamma_override_tables[GENESYS_GREEN].resize(option_size); +            for (i = 0; i < option_size; i++) { +                dev->gamma_override_tables[GENESYS_GREEN][i] = table[i]; +            } +            break;          } -      break; -    case OPT_GAMMA_VECTOR_B: -        table = reinterpret_cast<SANE_Word*>(val); -        option_size = s->opt[option].size / sizeof (SANE_Word); -        s->dev->gamma_override_tables[GENESYS_BLUE].resize(option_size); -        for (i = 0; i < option_size; i++) { -            s->dev->gamma_override_tables[GENESYS_BLUE][i] = table[i]; +        case OPT_GAMMA_VECTOR_B: { +            table = reinterpret_cast<SANE_Word*>(val); +            option_size = s->opt[option].size / sizeof (SANE_Word); +            dev->gamma_override_tables[GENESYS_BLUE].resize(option_size); +            for (i = 0; i < option_size; i++) { +                dev->gamma_override_tables[GENESYS_BLUE][i] = table[i]; +            } +            break;          } -      break;          case OPT_CALIBRATE: { -            auto& sensor = sanei_genesys_find_sensor_for_write(s->dev, s->dev->settings.xres, -                                                               s->dev->settings.get_channels(), -                                                               s->dev->settings.scan_method); +            auto& sensor = sanei_genesys_find_sensor_for_write(dev, dev->settings.xres, +                                                               dev->settings.get_channels(), +                                                               dev->settings.scan_method);              catch_all_exceptions(__func__, [&]()              { -                s->dev->cmd_set->save_power(s->dev, false); -                genesys_scanner_calibration(s->dev, sensor); +            dev->cmd_set->save_power(dev, false); +            genesys_scanner_calibration(dev, sensor);              });              catch_all_exceptions(__func__, [&]()              { -                s->dev->cmd_set->save_power(s->dev, true); +            dev->cmd_set->save_power(dev, true);              });              *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;              break;          } -    case OPT_CLEAR_CALIBRATION: -      s->dev->calibration_cache.clear(); +        case OPT_CLEAR_CALIBRATION: { +            dev->calibration_cache.clear(); -      /* remove file */ -      unlink(s->dev->calib_file.c_str()); -      /* signals that sensors will have to be read again */ -      *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; -      break; -    case OPT_FORCE_CALIBRATION: -      s->dev->force_calibration = 1; -      s->dev->calibration_cache.clear(); -      s->dev->calib_file.clear(); +            // remove file +            unlink(dev->calib_file.c_str()); +            // signals that sensors will have to be read again +            *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; +            break; +        } +        case OPT_FORCE_CALIBRATION: { +            dev->force_calibration = 1; +            dev->calibration_cache.clear(); +            dev->calib_file.clear(); -      /* signals that sensors will have to be read again */ -      *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; -      break; +            // signals that sensors will have to be read again +            *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; +            break; +        }          case OPT_IGNORE_OFFSETS: { -            s->dev->ignore_offsets = true; +            dev->ignore_offsets = true;              break;          } -    default: -      DBG(DBG_warn, "%s: can't set unknown option %d\n", __func__, option); +        default: { +            DBG(DBG_warn, "%s: can't set unknown option %d\n", __func__, option); +        }      }  } @@ -5829,13 +6217,13 @@ void sane_get_parameters_impl(SANE_Handle handle, SANE_Parameters* params)  {      DBG_HELPER(dbg);      Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle); +    auto* dev = s->dev;    /* don't recompute parameters once data reading is active, ie during scan */ -    if (!s->dev->read_active) { +    if (!dev->read_active) {          calc_parameters(s);      } -  if (params) -    { +    if (params) {        *params = s->params;        /* in the case of a sheetfed scanner, when full height is specified @@ -5843,11 +6231,11 @@ void sane_get_parameters_impl(SANE_Handle handle, SANE_Parameters* params)         * don't know the real document height.         * We don't do that doing buffering image for digital processing         */ -        if (s->dev->model->is_sheetfed && !s->dev->buffer_image && +        if (dev->model->is_sheetfed &&              s->pos_bottom_right_y == s->opt[OPT_BR_Y].constraint.range->max)          { -	  params->lines = -1; -	} +            params->lines = -1; +        }      }      debug_dump(DBG_proc, *params);  } @@ -5865,6 +6253,7 @@ void sane_start_impl(SANE_Handle handle)  {      DBG_HELPER(dbg);      Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle); +    auto* dev = s->dev;      if (s->pos_top_left_x >= s->pos_bottom_right_x) {          throw SaneException("top left x >= bottom right x"); @@ -5873,67 +6262,27 @@ void sane_start_impl(SANE_Handle handle)          throw SaneException("top left y >= bottom right y");      } -  /* First make sure we have a current parameter set.  Some of the -     parameters will be overwritten below, but that's OK.  */ - -    calc_parameters(s); -    genesys_start_scan(s->dev, s->lamp_off); - -    s->scanning = true; +    // fetch stored calibration +    if (dev->force_calibration == 0) { +        auto path = calibration_filename(dev); +        s->calibration_file = path; +        dev->calib_file = path; +        DBG(DBG_info, "%s: Calibration filename set to:\n", __func__); +        DBG(DBG_info, "%s: >%s<\n", __func__, dev->calib_file.c_str()); -  /* allocate intermediate buffer when doing dynamic lineart */ -    if (s->dev->settings.scan_mode == ScanColorMode::LINEART) { -        s->dev->binarize_buffer.clear(); -        s->dev->binarize_buffer.alloc(s->dev->settings.pixels); -        s->dev->local_buffer.clear(); -        s->dev->local_buffer.alloc(s->dev->binarize_buffer.size() * 8); +        catch_all_exceptions(__func__, [&]() +        { +            sanei_genesys_read_calibration(dev->calibration_cache, dev->calib_file); +        });      } -  /* if one of the software enhancement option is selected, -   * we do the scan internally, process picture then put it an internal -   * buffer. Since cropping may change scan parameters, we recompute them -   * at the end */ -  if (s->dev->buffer_image) -    { -        genesys_buffer_image(s); - -      /* check if we need to skip this page, sheetfed scanners -       * can go to next doc while flatbed ones can't */ -        if (s->swskip > 0 && IS_ACTIVE(OPT_SWSKIP)) { -            auto status = sanei_magic_isBlank(&s->params, -                                              s->dev->img_buffer.data(), -                                              SANE_UNFIX(s->swskip)); - -            if (status == SANE_STATUS_NO_DOCS && s->dev->model->is_sheetfed) { -                DBG(DBG_info, "%s: blank page, recurse\n", __func__); -                sane_start(handle); -                return; -            } +    // First make sure we have a current parameter set.  Some of the +    // parameters will be overwritten below, but that's OK. -            if (status != SANE_STATUS_GOOD) { -                throw SaneException(status); -            } -        } - -        if (s->swdeskew) { -            const auto& sensor = sanei_genesys_find_sensor(s->dev, s->dev->settings.xres, -                                                           s->dev->settings.get_channels(), -                                                           s->dev->settings.scan_method); -            catch_all_exceptions(__func__, [&](){ genesys_deskew(s, sensor); }); -        } - -        if (s->swdespeck) { -            catch_all_exceptions(__func__, [&](){ genesys_despeck(s); }); -        } - -        if(s->swcrop) { -            catch_all_exceptions(__func__, [&](){ genesys_crop(s); }); -        } +    calc_parameters(s); +    genesys_start_scan(dev, s->lamp_off); -        if(s->swderotate) { -            catch_all_exceptions(__func__, [&](){ genesys_derotate(s); }); -        } -    } +    s->scanning = true;  }  SANE_GENESYS_API_LINKAGE @@ -5945,18 +6294,18 @@ SANE_Status sane_start(SANE_Handle handle)      });  } -void sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len) +// returns SANE_STATUS_GOOD if there are more data, SANE_STATUS_EOF otherwise +SANE_Status sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len)  {      DBG_HELPER(dbg);      Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle); -  Genesys_Device *dev;    size_t local_len;      if (!s) {          throw SaneException("handle is nullptr");      } -  dev=s->dev; +    auto* dev = s->dev;      if (!dev) {          throw SaneException("dev is nullptr");      } @@ -5986,86 +6335,33 @@ void sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_        /* issue park command immediatly in case scanner can handle it         * so we save time */ -        if (!dev->model->is_sheetfed && !(dev->model->flags & GENESYS_FLAG_MUST_WAIT) && +        if (!dev->model->is_sheetfed && !has_flag(dev->model->flags, ModelFlag::MUST_WAIT) &&              !dev->parking)          {              dev->cmd_set->move_back_home(dev, false);              dev->parking = true;          } -        throw SaneException(SANE_STATUS_EOF); +        return SANE_STATUS_EOF;      }    local_len = max_len; -  /* in case of image processing, all data has been stored in -   * buffer_image. So read data from it if it exists, else from scanner */ -  if(!dev->buffer_image) -    { -      /* dynamic lineart is another kind of digital processing that needs -       * another layer of buffering on top of genesys_read_ordered_data */ -        if (dev->settings.scan_mode == ScanColorMode::LINEART) { -          /* if buffer is empty, fill it with genesys_read_ordered_data */ -          if(dev->binarize_buffer.avail() == 0) -            { -              /* store gray data */ -              local_len=dev->local_buffer.size(); -              dev->local_buffer.reset(); -                genesys_read_ordered_data(dev, dev->local_buffer.get_write_pos(local_len), -                                          &local_len); -              dev->local_buffer.produce(local_len); - -                dev->binarize_buffer.reset(); -                if (!is_testing_mode()) { -                    genesys_gray_lineart(dev, dev->local_buffer.get_read_pos(), -                                         dev->binarize_buffer.get_write_pos(local_len / 8), -                                         dev->settings.pixels, -                                         local_len / dev->settings.pixels, -                                         dev->settings.threshold); -                } -                dev->binarize_buffer.produce(local_len / 8); -            } - -          /* return data from lineart buffer if any, up to the available amount */ -          local_len = max_len; -          if (static_cast<std::size_t>(max_len) > dev->binarize_buffer.avail()) -            { -              local_len=dev->binarize_buffer.avail(); -            } -          if(local_len) -            { -              memcpy(buf, dev->binarize_buffer.get_read_pos(), local_len); -              dev->binarize_buffer.consume(local_len); -            } -        } -      else -        { -            // most usual case, direct read of data from scanner */ -            genesys_read_ordered_data(dev, buf, &local_len); -        } -    } -  else /* read data from buffer */ -    { -      if(dev->total_bytes_read+local_len>dev->total_bytes_to_read) -        { -          local_len=dev->total_bytes_to_read-dev->total_bytes_read; -        } -      memcpy(buf, dev->img_buffer.data() + dev->total_bytes_read, local_len); -      dev->total_bytes_read+=local_len; -    } +    genesys_read_ordered_data(dev, buf, &local_len);    *len = local_len;      if (local_len > static_cast<std::size_t>(max_len)) { -      fprintf (stderr, "[genesys] sane_read: returning incorrect length!!\n"); +        dbg.log(DBG_error, "error: returning incorrect length");      }    DBG(DBG_proc, "%s: %d bytes returned\n", __func__, *len); +    return SANE_STATUS_GOOD;  }  SANE_GENESYS_API_LINKAGE  SANE_Status sane_read(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len)  { -    return wrap_exceptions_to_status_code(__func__, [=]() +    return wrap_exceptions_to_status_code_return(__func__, [=]()      { -        sane_read_impl(handle, buf, max_len, len); +        return sane_read_impl(handle, buf, max_len, len);      });  } @@ -6073,36 +6369,31 @@ void sane_cancel_impl(SANE_Handle handle)  {      DBG_HELPER(dbg);      Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle); +    auto* dev = s->dev;      s->scanning = false; -    s->dev->read_active = false; -  s->dev->img_buffer.clear(); +    dev->read_active = false; -  /* no need to end scan if we are parking the head */ -    if (!s->dev->parking) { -        s->dev->cmd_set->end_scan(s->dev, &s->dev->reg, true); +    // no need to end scan if we are parking the head +    if (!dev->parking) { +        dev->cmd_set->end_scan(dev, &dev->reg, true);      } -  /* park head if flatbed scanner */ -    if (!s->dev->model->is_sheetfed) { -        if (!s->dev->parking) { -            s->dev->cmd_set->move_back_home (s->dev, s->dev->model->flags & -                                                    GENESYS_FLAG_MUST_WAIT); - -          s->dev->parking = !(s->dev->model->flags & GENESYS_FLAG_MUST_WAIT); +    // park head if flatbed scanner +    if (!dev->model->is_sheetfed) { +        if (!dev->parking) { +            dev->cmd_set->move_back_home(dev, has_flag(dev->model->flags, ModelFlag::MUST_WAIT)); +            dev->parking = !has_flag(dev->model->flags, ModelFlag::MUST_WAIT);          } -    } -  else -    {				/* in case of sheetfed scanners, we have to eject the document if still present */ -        s->dev->cmd_set->eject_document(s->dev); +    } else { +        // in case of sheetfed scanners, we have to eject the document if still present +        dev->cmd_set->eject_document(dev);      } -  /* enable power saving mode unless we are parking .... */ -    if (!s->dev->parking) { -        s->dev->cmd_set->save_power(s->dev, true); +    // enable power saving mode unless we are parking .... +    if (!dev->parking) { +        dev->cmd_set->save_power(dev, true);      } - -  return;  }  SANE_GENESYS_API_LINKAGE diff --git a/backend/genesys/genesys.h b/backend/genesys/genesys.h index 255bf76..9b1a087 100644 --- a/backend/genesys/genesys.h +++ b/backend/genesys/genesys.h @@ -107,21 +107,12 @@ enum Genesys_Option    OPT_GAMMA_VECTOR_R,    OPT_GAMMA_VECTOR_G,    OPT_GAMMA_VECTOR_B, -  OPT_SWDESKEW, -  OPT_SWCROP, -  OPT_SWDESPECK, -  OPT_DESPECK, -  OPT_SWSKIP, -  OPT_SWDEROTATE,    OPT_BRIGHTNESS,    OPT_CONTRAST,    OPT_EXTRAS_GROUP,    OPT_LAMP_OFF_TIME,    OPT_LAMP_OFF, -  OPT_THRESHOLD, -  OPT_THRESHOLD_CURVE, -  OPT_DISABLE_INTERPOLATION,    OPT_COLOR_FILTER,    OPT_CALIBRATION_FILE,    OPT_EXPIRATION_TIME, @@ -213,18 +204,9 @@ struct Genesys_Scanner      // Option values      SANE_Word bit_depth = 0;      SANE_Word resolution = 0; -    bool preview = false; -    SANE_Word threshold = 0; -    SANE_Word threshold_curve = 0; -    bool disable_interpolation = false; +    bool preview = false; // TODO: currently not used      bool lamp_off = false;      SANE_Word lamp_off_time = 0; -    bool swdeskew = false; -    bool swcrop = false; -    bool swdespeck = false; -    bool swderotate = false; -    SANE_Word swskip = 0; -    SANE_Word despeck = 0;      SANE_Word contrast = 0;      SANE_Word brightness = 0;      SANE_Word expiration_time = 0; diff --git a/backend/genesys/gl124.cpp b/backend/genesys/gl124.cpp index 054f1ef..d3fc1bc 100644 --- a/backend/genesys/gl124.cpp +++ b/backend/genesys/gl124.cpp @@ -53,6 +53,37 @@  namespace genesys {  namespace gl124 { +struct Gpio_layout +{ +    std::uint8_t r31; +    std::uint8_t r32; +    std::uint8_t r33; +    std::uint8_t r34; +    std::uint8_t r35; +    std::uint8_t r36; +    std::uint8_t r38; +}; + +/** @brief gpio layout + * describes initial gpio settings for a given model + * registers 0x31 to 0x38 + */ +static Gpio_layout gpios[] = { +    /* LiDE 110 */ +    { /*    0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38 */ +        0x9f, 0x59, 0x01, 0x80, 0x5f, 0x01, 0x00 +    }, +    /* LiDE 210 */ +    { +        0x9f, 0x59, 0x01, 0x80, 0x5f, 0x01, 0x00 +    }, +    /* LiDE 120 */ +    { +        0x9f, 0x53, 0x01, 0x80, 0x5f, 0x01, 0x00 +    }, +}; + +  /** @brief set all registers to default values .   * This function is called only once at the beginning and   * fills register startup values for registers reused across scans. @@ -336,54 +367,9 @@ gl124_init_registers (Genesys_Device * dev)      // fine tune upon device description      const auto& sensor = sanei_genesys_find_sensor_any(dev); -    sanei_genesys_set_dpihw(dev->reg, sensor, sensor.optical_res); - -  dev->calib_reg = dev->reg; -} - -/**@brief send slope table for motor movement - * Send slope_table in machine byte order - * @param dev device to send slope table - * @param table_nr index of the slope table in ASIC memory - * Must be in the [0-4] range. - * @param slope_table pointer to 16 bit values array of the slope table - * @param steps number of elemnts in the slope table - */ -static void gl124_send_slope_table(Genesys_Device* dev, int table_nr, -                                   const std::vector<uint16_t>& slope_table, -                                   int steps) -{ -    DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %d", table_nr, steps); -  int i; -  char msg[10000]; - -  /* sanity check */ -  if(table_nr<0 || table_nr>4) -    { -        throw SaneException("invalid table number"); -    } - -  std::vector<uint8_t> table(steps * 2); -  for (i = 0; i < steps; i++) -    { -      table[i * 2] = slope_table[i] & 0xff; -      table[i * 2 + 1] = slope_table[i] >> 8; -    } - -  if (DBG_LEVEL >= DBG_io) -    { -        std::sprintf(msg, "write slope %d (%d)=", table_nr, steps); -        for (i = 0; i < steps; i++) { -            std::sprintf(msg + std::strlen(msg), ",%d", slope_table[i]); -        } -      DBG (DBG_io, "%s: %s\n", __func__, msg); -    } - -    if (dev->interface->is_mock()) { -        dev->interface->record_slope_table(table_nr, slope_table); -    } -    // slope table addresses are fixed -    dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, steps * 2, table.data()); +    const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, +                                                         3, ScanMethod::FLATBED); +    sanei_genesys_set_dpihw(dev->reg, dpihw_sensor.register_dpihw);  }  /** @brief * Set register values of 'special' ti type frontend @@ -397,12 +383,8 @@ static void gl124_set_ti_fe(Genesys_Device* dev, uint8_t set)      DBG_HELPER(dbg);    int i; -  if (set == AFE_INIT) -    { -        DBG(DBG_proc, "%s: setting DAC %u\n", __func__, -            static_cast<unsigned>(dev->model->adc_id)); - -      dev->frontend = dev->frontend_initial; +    if (set == AFE_INIT) { +        dev->frontend = dev->frontend_initial;      }      // start writing to DAC @@ -441,11 +423,8 @@ void CommandSetGl124::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor,      (void) sensor;    uint8_t val; -  if (set == AFE_INIT) -    { -        DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, -            static_cast<unsigned>(dev->model->adc_id)); -      dev->frontend = dev->frontend_initial; +    if (set == AFE_INIT) { +        dev->frontend = dev->frontend_initial;      }      val = dev->interface->read_register(REG_0x0A); @@ -466,14 +445,14 @@ void CommandSetGl124::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor,  static void gl124_init_motor_regs_scan(Genesys_Device* dev,                                         const Genesys_Sensor& sensor,                                         Genesys_Register_Set* reg, -                                       const Motor_Profile& motor_profile, +                                       const MotorProfile& motor_profile,                                         unsigned int scan_exposure_time,                                         unsigned scan_yres,                                         unsigned int scan_lines,                                         unsigned int scan_dummy,                                         unsigned int feed_steps,                                         ScanColorMode scan_mode, -                                       MotorFlag flags) +                                       ScanFlag flags)  {      DBG_HELPER(dbg);    int use_fast_fed; @@ -533,11 +512,8 @@ static void gl124_init_motor_regs_scan(Genesys_Device* dev,        linesel=0;      } -    DBG(DBG_io2, "%s: final yres=%d, linesel=%d\n", __func__, yres, linesel); -    lincnt=scan_lines*(linesel+1);      reg->set24(REG_LINCNT, lincnt); -  DBG (DBG_io, "%s: lincnt=%d\n", __func__, lincnt);    /* compute register 02 value */      uint8_t r02 = REG_0x02_NOTHOME; @@ -548,15 +524,15 @@ static void gl124_init_motor_regs_scan(Genesys_Device* dev,          r02 &= ~REG_0x02_FASTFED;      } -    if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) { +    if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) {          r02 |= REG_0x02_AGOHOME;      } -    if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE) || (yres >= sensor.optical_res)) +    if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || (yres >= sensor.full_resolution))      {          r02 |= REG_0x02_ACDCDIS;      } -    if (has_flag(flags, MotorFlag::REVERSE)) { +    if (has_flag(flags, ScanFlag::REVERSE)) {          r02 |= REG_0x02_MTRREV;      } @@ -566,13 +542,12 @@ static void gl124_init_motor_regs_scan(Genesys_Device* dev,      reg->set16(REG_SCANFED, 4);    /* scan and backtracking slope table */ -    auto scan_table = sanei_genesys_slope_table(dev->model->asic_type, yres, scan_exposure_time, -                                                dev->motor.base_ydpi, 1, -                                                motor_profile); -    gl124_send_slope_table(dev, SCAN_TABLE, scan_table.table, scan_table.steps_count); -    gl124_send_slope_table(dev, BACKTRACK_TABLE, scan_table.table, scan_table.steps_count); +    auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, yres, +                                         scan_exposure_time, 1, motor_profile); +    scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); +    scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); -    reg->set16(REG_STEPNO, scan_table.steps_count); +    reg->set16(REG_STEPNO, scan_table.table.size());    /* fast table */    fast_dpi=yres; @@ -583,28 +558,26 @@ static void gl124_init_motor_regs_scan(Genesys_Device* dev,        fast_dpi*=3;      }      */ -    auto fast_table = sanei_genesys_slope_table(dev->model->asic_type, fast_dpi, -                                                scan_exposure_time, dev->motor.base_ydpi, -                                                1, motor_profile); -    gl124_send_slope_table(dev, STOP_TABLE, fast_table.table, fast_table.steps_count); -    gl124_send_slope_table(dev, FAST_TABLE, fast_table.table, fast_table.steps_count); +    auto fast_table = create_slope_table(dev->model->asic_type, dev->motor, fast_dpi, +                                         scan_exposure_time, 1, motor_profile); +    scanner_send_slope_table(dev, sensor, STOP_TABLE, fast_table.table); +    scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); -    reg->set16(REG_FASTNO, fast_table.steps_count); -    reg->set16(REG_FSHDEC, fast_table.steps_count); -    reg->set16(REG_FMOVNO, fast_table.steps_count); +    reg->set16(REG_FASTNO, fast_table.table.size()); +    reg->set16(REG_FSHDEC, fast_table.table.size()); +    reg->set16(REG_FMOVNO, fast_table.table.size());    /* substract acceleration distance from feedl */    feedl=feed_steps;      feedl <<= static_cast<unsigned>(motor_profile.step_type); -    dist = scan_table.steps_count; -    if (has_flag(flags, MotorFlag::FEED)) { +    dist = scan_table.table.size(); +    if (has_flag(flags, ScanFlag::FEEDING)) {          dist *= 2;      }      if (use_fast_fed) { -        dist += fast_table.steps_count * 2; +        dist += fast_table.table.size() * 2;      } -  DBG (DBG_io2, "%s: acceleration distance=%d\n", __func__, dist);    /* get sure we don't use insane value */      if (dist < feedl) { @@ -614,160 +587,97 @@ static void gl124_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 */      sanei_genesys_calculate_zmod(use_fast_fed,  				  scan_exposure_time,                                   scan_table.table, -                                 scan_table.steps_count, +                                 scan_table.table.size(),  				  feedl, -                                 scan_table.steps_count, +                                 scan_table.table.size(),                                    &z1,                                    &z2);      reg->set24(REG_Z1MOD, z1); -  DBG(DBG_info, "%s: z1 = %d\n", __func__, z1); -      reg->set24(REG_Z2MOD, z2); -  DBG(DBG_info, "%s: z2 = %d\n", __func__, z2);    /* LINESEL */      reg->set8_mask(REG_0x1D, linesel, REG_0x1D_LINESEL);      reg->set8(REG_0xA0, (static_cast<unsigned>(motor_profile.step_type) << REG_0xA0S_STEPSEL) |                          (static_cast<unsigned>(motor_profile.step_type) << REG_0xA0S_FSTPSEL)); -    reg->set16(REG_FMOVDEC, fast_table.steps_count); +    reg->set16(REG_FMOVDEC, fast_table.table.size());  } - -/** @brief copy sensor specific settings - * Set up register set for the given sensor resolution. Values are from the device table - * in genesys_devices.c for registers: - *       [0x16 ... 0x1d] - *       [0x52 ... 0x5e] - * Other come from the specific device sensor table in genesys_gl124.h: - *      0x18, 0x20, 0x61, 0x98 and - * @param dev device to set up - * @param regs register set to modify - * @param dpi resolution of the sensor during scan - * @param ccd_size_divisor flag for half ccd mode - * */ -static void gl124_setup_sensor(Genesys_Device* dev, const Genesys_Sensor& sensor, -                               Genesys_Register_Set* regs) -{ -    DBG_HELPER(dbg); - -    for (const auto& reg : sensor.custom_regs) { -        regs->set8(reg.address, reg.value); -    } - -    regs->set24(REG_EXPR, sensor.exposure.red); -    regs->set24(REG_EXPG, sensor.exposure.green); -    regs->set24(REG_EXPB, sensor.exposure.blue); - -    dev->segment_order = sensor.segment_order; -} - -/** @brief setup optical related registers - * start and pixels are expressed in optical sensor resolution coordinate - * space. - * @param dev scanner device to use - * @param reg registers to set up - * @param exposure_time exposure time to use - * @param used_res scanning resolution used, may differ from - *        scan's one - * @param start logical start pixel coordinate - * @param pixels logical number of pixels to use - * @param channels number of color channels (currently 1 or 3) - * @param depth bit depth of the scan (1, 8 or 16) - * @param ccd_size_divisor whether sensor's timings are such that x coordinates must be halved - * @param color_filter color channel to use as gray data - * @param flags optical flags (@see ) - */  static void gl124_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,                                           Genesys_Register_Set* reg, unsigned int exposure_time,                                           const ScanSession& session)  {      DBG_HELPER_ARGS(dbg, "exposure_time=%d", exposure_time); -    unsigned int dpihw; -  GenesysRegister *r;    uint32_t expmax; -    // resolution is divided according to ccd_pixels_per_system_pixel -    unsigned ccd_pixels_per_system_pixel = sensor.ccd_pixels_per_system_pixel(); -    DBG(DBG_io2, "%s: ccd_pixels_per_system_pixel=%d\n", __func__, ccd_pixels_per_system_pixel); - -    // to manage high resolution device while keeping good low resolution scanning speed, we -    // make hardware dpi vary -    dpihw = sensor.get_register_hwdpi(session.output_resolution * ccd_pixels_per_system_pixel); -    DBG(DBG_io2, "%s: dpihw=%d\n", __func__, dpihw); - -    gl124_setup_sensor(dev, sensor, reg); +    scanner_setup_sensor(*dev, sensor, *reg);      dev->cmd_set->set_fe(dev, sensor, AFE_SET);    /* enable shading */      regs_set_optical_off(dev->model->asic_type, *reg); -    r = sanei_genesys_get_address (reg, REG_0x01);      if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || -        (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)) +        has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION))      { -        r->value &= ~REG_0x01_DVDSET; +        reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET;      } else { -        r->value |= REG_0x01_DVDSET; +        reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET;      } -    r = sanei_genesys_get_address(reg, REG_0x03);      if ((dev->model->sensor_id != SensorId::CIS_CANON_LIDE_120) && (session.params.xres>=600)) { -        r->value &= ~REG_0x03_AVEENB; -      DBG (DBG_io, "%s: disabling AVEENB\n", __func__); -    } -  else -    { -        r->value |= ~REG_0x03_AVEENB; -      DBG (DBG_io, "%s: enabling AVEENB\n", __func__); +        reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB; +    } else { +        // BUG: the following is likely incorrect +        reg->find_reg(REG_0x03).value |= ~REG_0x03_AVEENB;      }      sanei_genesys_set_lamp_power(dev, sensor, *reg,                                   !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP));      // BW threshold -    dev->interface->write_register(REG_0x114, dev->settings.threshold); -    dev->interface->write_register(REG_0x115, dev->settings.threshold); +    dev->interface->write_register(REG_0x114, 0x7f); +    dev->interface->write_register(REG_0x115, 0x7f);    /* monochrome / color scan */ -    r = sanei_genesys_get_address (reg, REG_0x04);      switch (session.params.depth) {      case 8: -            r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); +            reg->find_reg(REG_0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET);              break;      case 16: -            r->value &= ~REG_0x04_LINEART; -            r->value |= REG_0x04_BITSET; +            reg->find_reg(REG_0x04).value &= ~REG_0x04_LINEART; +            reg->find_reg(REG_0x04).value |= REG_0x04_BITSET;              break;      } -    r->value &= ~REG_0x04_FILTER; +    reg->find_reg(REG_0x04).value &= ~REG_0x04_FILTER;    if (session.params.channels == 1)      {        switch (session.params.color_filter)  	{              case ColorFilter::RED: -                r->value |= 0x10; +                reg->find_reg(REG_0x04).value |= 0x10;                  break;              case ColorFilter::BLUE: -                r->value |= 0x30; +                reg->find_reg(REG_0x04).value |= 0x30;                  break;              case ColorFilter::GREEN: -                r->value |= 0x20; +                reg->find_reg(REG_0x04).value |= 0x20;                  break;              default:                  break; // should not happen  	}      } -    sanei_genesys_set_dpihw(*reg, sensor, dpihw); +    const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, +                                                         session.params.channels, +                                                         session.params.scan_method); +    sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw);      if (should_enable_gamma(session, sensor)) {          reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; @@ -775,25 +685,16 @@ static void gl124_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens          reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB;      } -    unsigned dpiset_reg = session.output_resolution * ccd_pixels_per_system_pixel * -            session.ccd_size_divisor; -    if (sensor.dpiset_override != 0) { -        dpiset_reg = sensor.dpiset_override; -    } - -    reg->set16(REG_DPISET, dpiset_reg); -    DBG (DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset_reg); +    reg->set16(REG_DPISET, sensor.register_dpiset); -    r = sanei_genesys_get_address(reg, REG_0x06); -    r->value |= REG_0x06_GAIN4; +    reg->find_reg(REG_0x06).value |= REG_0x06_GAIN4;    /* CIS scanners can do true gray by setting LEDADD */    /* we set up LEDADD only when asked */      if (dev->model->is_cis) { -        r = sanei_genesys_get_address (reg, REG_0x60); -        r->value &= ~REG_0x60_LEDADD; +        reg->find_reg(REG_0x60).value &= ~REG_0x60_LEDADD;          if (session.enable_ledadd) { -            r->value |= REG_0x60_LEDADD; +            reg->find_reg(REG_0x60).value |= REG_0x60_LEDADD;              expmax = reg->get24(REG_EXPR);              expmax = std::max(expmax, reg->get24(REG_EXPG));              expmax = std::max(expmax, reg->get24(REG_EXPB)); @@ -803,31 +704,32 @@ static void gl124_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens              dev->reg.set24(REG_EXPB, expmax);          }        /* RGB weighting, REG_TRUER,G and B are to be set  */ -        r = sanei_genesys_get_address (reg, 0x01); -        r->value &= ~REG_0x01_TRUEGRAY; +        reg->find_reg(0x01).value &= ~REG_0x01_TRUEGRAY;          if (session.enable_ledadd) { -            r->value |= REG_0x01_TRUEGRAY; +            reg->find_reg(0x01).value |= REG_0x01_TRUEGRAY;              dev->interface->write_register(REG_TRUER, 0x80);              dev->interface->write_register(REG_TRUEG, 0x80);              dev->interface->write_register(REG_TRUEB, 0x80);          }      } +    std::uint32_t pixel_endx = session.pixel_endx; +    if (pixel_endx == reg->get24(REG_SEGCNT)) { +        pixel_endx = 0; +    }      reg->set24(REG_STRPIXEL, session.pixel_startx); -    reg->set24(REG_ENDPIXEL, session.pixel_endx); +    reg->set24(REG_ENDPIXEL, pixel_endx);    dev->line_count = 0; -    build_image_pipeline(dev, session); +    setup_image_pipeline(*dev, session);      // MAXWD is expressed in 2 words unit      // BUG: we shouldn't multiply by channels here -    reg->set24(REG_MAXWD, session.output_line_bytes_raw / session.ccd_size_divisor * session.params.channels); - +    reg->set24(REG_MAXWD, session.output_line_bytes_raw * session.params.channels * +                              session.optical_resolution / session.full_resolution);      reg->set24(REG_LPERIOD, exposure_time); -  DBG (DBG_io2, "%s: exposure_time used=%d\n", __func__, exposure_time); -      reg->set16(REG_DUMMY, sensor.dummy_pixel);  } @@ -838,7 +740,6 @@ void CommandSetGl124::init_regs_for_scan_session(Genesys_Device* dev, const Gene      DBG_HELPER(dbg);      session.assert_computed(); -  int move;    int exposure_time;    int dummy = 0; @@ -856,9 +757,7 @@ void CommandSetGl124::init_regs_for_scan_session(Genesys_Device* dev, const Gene      } else {          exposure_time = sensor.exposure_lperiod;      } -    const auto& motor_profile = sanei_genesys_get_motor_profile(*gl124_motor_profiles, -                                                                dev->model->motor_id, -                                                                exposure_time); +    const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure_time, session);    DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time);    DBG(DBG_info, "%s : scan_step_type=%d\n", __func__, static_cast<unsigned>(motor_profile.step_type)); @@ -870,30 +769,13 @@ void CommandSetGl124::init_regs_for_scan_session(Genesys_Device* dev, const Gene      // now _LOGICAL_ optical values used are known, setup registers      gl124_init_optical_regs_scan(dev, sensor, reg, exposure_time, session); -  /* add tl_y to base movement */ -    move = session.params.starty; -  DBG(DBG_info, "%s: move=%d steps\n", __func__, move); - -    MotorFlag mflags = MotorFlag::NONE; -    if (has_flag(session.params.flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE)) { -        mflags |= MotorFlag::DISABLE_BUFFER_FULL_MOVE; -    } -    if (has_flag(session.params.flags, ScanFlag::FEEDING)) { -        mflags |= MotorFlag::FEED; -    } -    if (has_flag(session.params.flags, ScanFlag::REVERSE)) { -        mflags |= MotorFlag::REVERSE; -    }      gl124_init_motor_regs_scan(dev, sensor, reg, motor_profile, exposure_time, slope_dpi, -                               dev->model->is_cis ? session.output_line_count * session.params.channels : -                                                    session.output_line_count, -                               dummy, move, session.params.scan_mode, mflags); +                               session.optical_line_count, +                               dummy, session.params.starty, session.params.scan_mode, +                               session.params.flags);    /*** prepares data reordering ***/ -    dev->read_buffer.clear(); -    dev->read_buffer.alloc(session.buffer_size_read); -      dev->read_active = true;      dev->session = session; @@ -909,21 +791,24 @@ ScanSession CommandSetGl124::calculate_scan_session(const Genesys_Device* dev,                                                      const Genesys_Sensor& sensor,                                                      const Genesys_Settings& settings) const  { -  int start; -      DBG(DBG_info, "%s ", __func__);      debug_dump(DBG_info, settings); -  /* start */ -    start = static_cast<int>(dev->model->x_offset); -    start += static_cast<int>(settings.tl_x); -    start = static_cast<int>((start * sensor.optical_res) / MM_PER_INCH); +    unsigned move_dpi = dev->motor.base_ydpi / 4; +    float move = dev->model->y_offset; +    move += dev->settings.tl_y; +    move = static_cast<float>((move * move_dpi) / MM_PER_INCH); + +    float start = dev->model->x_offset; +    start += settings.tl_x; +    start /= sensor.full_resolution / sensor.get_optical_resolution(); +    start = static_cast<float>((start * settings.xres) / MM_PER_INCH);      ScanSession session;      session.params.xres = settings.xres;      session.params.yres = settings.yres; -    session.params.startx = start; -    session.params.starty = 0; // not used +    session.params.startx = static_cast<unsigned>(start); +    session.params.starty = static_cast<unsigned>(move);      session.params.pixels = settings.pixels;      session.params.requested_pixels = settings.requested_pixels;      session.params.lines = settings.lines; @@ -953,17 +838,15 @@ void CommandSetGl124::save_power(Genesys_Device* dev, bool enable) const  void CommandSetGl124::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const  {      DBG_HELPER_ARGS(dbg,  "delay = %d",  delay); -  GenesysRegister *r; -    r = sanei_genesys_get_address(&dev->reg, REG_0x03); -  r->value &= ~0xf0; +    dev->reg.find_reg(REG_0x03).value &= ~0xf0;    if(delay<15)      { -      r->value |= delay; +        dev->reg.find_reg(REG_0x03).value |= delay;      }    else      { -      r->value |= 0x0f; +        dev->reg.find_reg(REG_0x03).value |= 0x0f;      }  } @@ -1031,8 +914,7 @@ void CommandSetGl124::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sens      // set up GPIO for scan      gl124_setup_scan_gpio(dev,dev->settings.yres); -    // clear scan and feed count -    dev->interface->write_register(REG_0x0D, REG_0x0D_CLRLNCNT | REG_0x0D_CLRMCNT); +    scanner_clear_scan_and_feed_counts(*dev);      // enable scan and motor      uint8_t val = dev->interface->read_register(REG_0x01); @@ -1069,177 +951,43 @@ void CommandSetGl124::move_back_home(Genesys_Device* dev, bool wait_until_home)      scanner_move_back_home(*dev, wait_until_home);  } -// Automatically set top-left edge of the scan area by scanning a 200x200 pixels area at 600 dpi -// from very top of scanner -void CommandSetGl124::search_start_position(Genesys_Device* dev) const -{ -    DBG_HELPER(dbg); -  int size; -  Genesys_Register_Set local_reg = dev->reg; - -  int pixels = 600; -  int dpi = 300; - -  /* sets for a 200 lines * 600 pixels */ -  /* normal scan with no shading */ - -    // FIXME: the current approach of doing search only for one resolution does not work on scanners -    // whith employ different sensors with potentially different settings. -    const auto& sensor = sanei_genesys_find_sensor(dev, dpi, 1, ScanMethod::FLATBED); - -    ScanSession session; -    session.params.xres = dpi; -    session.params.yres = dpi; -    session.params.startx = 0; -    session.params.starty = 0;        /*we should give a small offset here~60 steps */ -    session.params.pixels = 600; -    session.params.lines = dev->model->search_lines; -    session.params.depth = 8; -    session.params.channels = 1; -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = ScanColorMode::GRAY; -    session.params.color_filter = ColorFilter::GREEN; -    session.params.flags = ScanFlag::DISABLE_SHADING | -                           ScanFlag::DISABLE_GAMMA | -                           ScanFlag::IGNORE_LINE_DISTANCE | -                           ScanFlag::DISABLE_BUFFER_FULL_MOVE; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, &local_reg, session); - -    // send to scanner -    dev->interface->write_registers(local_reg); - -  size = pixels * dev->model->search_lines; - -  std::vector<uint8_t> data(size); - -    begin_scan(dev, sensor, &local_reg, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("search_start_position"); -        end_scan(dev, &local_reg, true); -        dev->reg = local_reg; -        return; -    } - -    wait_until_buffer_non_empty(dev); - -    // now we're on target, we can read data -    sanei_genesys_read_data_from_scanner(dev, data.data(), size); - -    if (DBG_LEVEL >= DBG_data) { -        sanei_genesys_write_pnm_file("gl124_search_position.pnm", data.data(), 8, 1, pixels, -                                     dev->model->search_lines); -    } - -    end_scan(dev, &local_reg, true); - -  /* update regs to copy ASIC internal state */ -  dev->reg = local_reg; - -    for (auto& sensor_update : -             sanei_genesys_find_sensors_all_for_write(dev, dev->model->default_method)) -    { -        sanei_genesys_search_reference_point(dev, sensor_update, data.data(), 0, dpi, pixels, -                                             dev->model->search_lines); -    } -} - -// sets up register for coarse gain calibration -// todo: check it for scanners using it -void CommandSetGl124::init_regs_for_coarse_calibration(Genesys_Device* dev, -                                                       const Genesys_Sensor& sensor, -                                                       Genesys_Register_Set& regs) const -{ -    DBG_HELPER(dbg); - -    ScanSession session; -    session.params.xres = dev->settings.xres; -    session.params.yres = dev->settings.yres; -    session.params.startx = 0; -    session.params.starty = 0; -    session.params.pixels = sensor.optical_res / sensor.ccd_pixels_per_system_pixel(); -    session.params.lines = 20; -    session.params.depth = 16; -    session.params.channels = dev->settings.get_channels(); -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = dev->settings.scan_mode; -    session.params.color_filter = dev->settings.color_filter; -    session.params.flags = ScanFlag::DISABLE_SHADING | -                           ScanFlag::DISABLE_GAMMA | -                           ScanFlag::SINGLE_LINE | -                           ScanFlag::FEEDING | -                           ScanFlag::IGNORE_LINE_DISTANCE; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, ®s, session); - -  sanei_genesys_set_motor_power(regs, false); - -  DBG(DBG_info, "%s: optical sensor res: %d dpi, actual res: %d\n", __func__, -      sensor.optical_res / sensor.ccd_pixels_per_system_pixel(), dev->settings.xres); - -    dev->interface->write_registers(regs); -} - -  // init registers for shading calibration shading calibration is done at dpihw  void CommandSetGl124::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,                                              Genesys_Register_Set& regs) const  {      DBG_HELPER(dbg); -  int move, resolution, dpihw, factor; - -  /* initial calibration reg values */ -  regs = dev->reg; - -  dev->calib_channels = 3; -  dev->calib_lines = dev->model->shading_lines; -    dpihw = sensor.get_register_hwdpi(dev->settings.xres); -  if(dpihw>=2400) -    { -      dev->calib_lines *= 2; -    } -  resolution=dpihw; -    unsigned ccd_size_divisor = sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres); +    unsigned channels = 3; +    unsigned resolution = sensor.shading_resolution; -    resolution /= ccd_size_divisor; -    dev->calib_lines /= ccd_size_divisor; // reducing just because we reduced the resolution +    unsigned calib_lines = +            static_cast<unsigned>(dev->model->y_size_calib_mm * resolution / MM_PER_INCH); -    const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, -                                                         dev->calib_channels, +    const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,                                                           dev->settings.scan_method); -  dev->calib_resolution = resolution; -  dev->calib_total_bytes_to_read = 0; -    factor = calib_sensor.optical_res / resolution; -    dev->calib_pixels = calib_sensor.sensor_pixels / factor;    /* distance to move to reach white target at high resolution */ -  move=0; +    unsigned move=0;      if (dev->settings.yres >= 1200) {          move = static_cast<int>(dev->model->y_offset_calib_white);          move = static_cast<int>((move * (dev->motor.base_ydpi/4)) / MM_PER_INCH);      } -  DBG (DBG_io, "%s: move=%d steps\n", __func__, move);      ScanSession session;      session.params.xres = resolution;      session.params.yres = resolution;      session.params.startx = 0;      session.params.starty = move; -    session.params.pixels = dev->calib_pixels; -    session.params.lines = dev->calib_lines; +    session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; +    session.params.lines = calib_lines;      session.params.depth = 16; -    session.params.channels = dev->calib_channels; +    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 = ScanFlag::DISABLE_SHADING |                             ScanFlag::DISABLE_GAMMA | -                           ScanFlag::DISABLE_BUFFER_FULL_MOVE | -                           ScanFlag::IGNORE_LINE_DISTANCE; +                           ScanFlag::DISABLE_BUFFER_FULL_MOVE;      compute_session(dev, session, calib_sensor);      try { @@ -1250,7 +998,7 @@ void CommandSetGl124::init_regs_for_shading(Genesys_Device* dev, const Genesys_S      }      sanei_genesys_set_motor_power(regs, false); -    dev->interface->write_registers(regs); +    dev->calib_session = session;  }  void CommandSetGl124::wait_for_motor_stop(Genesys_Device* dev) const @@ -1272,56 +1020,6 @@ void CommandSetGl124::wait_for_motor_stop(Genesys_Device* dev) const      dev->interface->sleep_ms(50);  } -/** @brief set up registers for the actual scan - */ -void CommandSetGl124::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const -{ -    DBG_HELPER(dbg); -  float move; -  int move_dpi; -  float start; - -    debug_dump(DBG_info, dev->settings); - -  /* y (motor) distance to move to reach scanned area */ -  move_dpi = dev->motor.base_ydpi/4; -    move = static_cast<float>(dev->model->y_offset); -    move += static_cast<float>(dev->settings.tl_y); -    move = static_cast<float>((move * move_dpi) / MM_PER_INCH); -  DBG (DBG_info, "%s: move=%f steps\n", __func__, move); - -    if (dev->settings.get_channels() * dev->settings.yres >= 600 && move > 700) { -        scanner_move(*dev, dev->model->default_method, static_cast<unsigned>(move - 500), -                     Direction::FORWARD); -      move=500; -    } -  DBG(DBG_info, "%s: move=%f steps\n", __func__, move); - -  /* start */ -    start = static_cast<float>(dev->model->x_offset); -    start += static_cast<float>(dev->settings.tl_x); -    start /= sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres); -    start = static_cast<float>((start * sensor.optical_res) / MM_PER_INCH); - -    ScanSession session; -    session.params.xres = dev->settings.xres; -    session.params.yres = dev->settings.yres; -    session.params.startx = static_cast<unsigned>(start); -    session.params.starty = static_cast<unsigned>(move); -    session.params.pixels = dev->settings.pixels; -    session.params.requested_pixels = dev->settings.requested_pixels; -    session.params.lines = dev->settings.lines; -    session.params.depth = dev->settings.depth; -    session.params.channels = dev->settings.get_channels(); -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = dev->settings.scan_mode; -    session.params.color_filter = dev->settings.color_filter; -    session.params.flags = ScanFlag::NONE; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, &dev->reg, session); -} -  /**   * Send shading calibration data. The buffer is considered to always hold values   * for all the channels. @@ -1330,8 +1028,7 @@ void CommandSetGl124::send_shading_data(Genesys_Device* dev, const Genesys_Senso                                          std::uint8_t* data, int size) const  {      DBG_HELPER_ARGS(dbg, "writing %d bytes of shading data", size); -    uint32_t addr, length, x, factor, segcnt, pixels, i; -  uint16_t dpiset,dpihw; +    std::uint32_t addr, length, segcnt, pixels, i;      uint8_t *ptr, *src;    /* logical size of a color as seen by generic code of the frontend */ @@ -1339,16 +1036,6 @@ void CommandSetGl124::send_shading_data(Genesys_Device* dev, const Genesys_Senso      std::uint32_t strpixel = dev->session.pixel_startx;      std::uint32_t endpixel = dev->session.pixel_endx;      segcnt = dev->reg.get24(REG_SEGCNT); -  if(endpixel==0) -    { -      endpixel=segcnt; -    } - -  /* compute deletion factor */ -    dpiset = dev->reg.get16(REG_DPISET); -    dpihw = sensor.get_register_hwdpi(dpiset); -  factor=dpihw/dpiset; -  DBG( DBG_io2, "%s: factor=%d\n",__func__,factor);    /* turn pixel value into bytes 2x16 bits words */    strpixel*=2*2; /* 2 words of 2 bytes */ @@ -1359,7 +1046,7 @@ void CommandSetGl124::send_shading_data(Genesys_Device* dev, const Genesys_Senso      dev->interface->record_key_value("shading_start_pixel", std::to_string(strpixel));      dev->interface->record_key_value("shading_pixels", std::to_string(pixels));      dev->interface->record_key_value("shading_length", std::to_string(length)); -    dev->interface->record_key_value("shading_factor", std::to_string(factor)); +    dev->interface->record_key_value("shading_factor", std::to_string(sensor.shading_factor));      dev->interface->record_key_value("shading_segcnt", std::to_string(segcnt));      dev->interface->record_key_value("shading_segment_count",                                       std::to_string(dev->session.segment_count)); @@ -1375,47 +1062,18 @@ void CommandSetGl124::send_shading_data(Genesys_Device* dev, const Genesys_Senso        ptr = buffer.data();        /* iterate on both sensor segment */ -      for(x=0;x<pixels;x+=4*factor) -        { +        for (unsigned x = 0; x < pixels; x += 4 * sensor.shading_factor) {            /* coefficient source */            src=data+x+strpixel+i*length;            /* iterate over all the segments */ -            switch (dev->session.segment_count) { -            case 1: -              ptr[0+pixels*0]=src[0+segcnt*0]; -              ptr[1+pixels*0]=src[1+segcnt*0]; -              ptr[2+pixels*0]=src[2+segcnt*0]; -              ptr[3+pixels*0]=src[3+segcnt*0]; -              break; -            case 2: -              ptr[0+pixels*0]=src[0+segcnt*0]; -              ptr[1+pixels*0]=src[1+segcnt*0]; -              ptr[2+pixels*0]=src[2+segcnt*0]; -              ptr[3+pixels*0]=src[3+segcnt*0]; -              ptr[0+pixels*1]=src[0+segcnt*1]; -              ptr[1+pixels*1]=src[1+segcnt*1]; -              ptr[2+pixels*1]=src[2+segcnt*1]; -              ptr[3+pixels*1]=src[3+segcnt*1]; -              break; -            case 4: -              ptr[0+pixels*0]=src[0+segcnt*0]; -              ptr[1+pixels*0]=src[1+segcnt*0]; -              ptr[2+pixels*0]=src[2+segcnt*0]; -              ptr[3+pixels*0]=src[3+segcnt*0]; -              ptr[0+pixels*1]=src[0+segcnt*2]; -              ptr[1+pixels*1]=src[1+segcnt*2]; -              ptr[2+pixels*1]=src[2+segcnt*2]; -              ptr[3+pixels*1]=src[3+segcnt*2]; -              ptr[0+pixels*2]=src[0+segcnt*1]; -              ptr[1+pixels*2]=src[1+segcnt*1]; -              ptr[2+pixels*2]=src[2+segcnt*1]; -              ptr[3+pixels*2]=src[3+segcnt*1]; -              ptr[0+pixels*3]=src[0+segcnt*3]; -              ptr[1+pixels*3]=src[1+segcnt*3]; -              ptr[2+pixels*3]=src[2+segcnt*3]; -              ptr[3+pixels*3]=src[3+segcnt*3]; -              break; +          for (unsigned s = 0; s < dev->session.segment_count; s++) +            { +              unsigned segnum = dev->session.segment_count > 1 ? sensor.segment_order[s] : 0; +              ptr[0+pixels*s]=src[0+segcnt*segnum]; +              ptr[1+pixels*s]=src[1+segcnt*segnum]; +              ptr[2+pixels*s]=src[2+segcnt*segnum]; +              ptr[3+pixels*s]=src[3+segcnt*segnum];              }            /* next shading coefficient */ @@ -1433,20 +1091,17 @@ void CommandSetGl124::send_shading_data(Genesys_Device* dev, const Genesys_Senso   * by doing a 600 dpi scan   * @param dev scanner device   */ -static void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                     Genesys_Register_Set& regs) +void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor& sensor, +                              Genesys_Register_Set& regs)  {      (void) sensor;      DBG_HELPER(dbg); -  int pixels; -  int size;      unsigned resolution = 600;      unsigned channels = 3;      const auto& move_sensor = sanei_genesys_find_sensor(dev, resolution, channels,                                                           dev->settings.scan_method); -    pixels = (move_sensor.sensor_pixels * 600) / move_sensor.optical_res;    /* initial calibration reg values */    regs = dev->reg; @@ -1456,7 +1111,7 @@ static void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor&      session.params.yres = resolution;      session.params.startx = 0;      session.params.starty = 0; -    session.params.pixels = pixels; +    session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;      session.params.lines = 1;      session.params.depth = 8;      session.params.channels = channels; @@ -1466,14 +1121,12 @@ static void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor&      session.params.flags = ScanFlag::DISABLE_SHADING |                             ScanFlag::DISABLE_GAMMA |                             ScanFlag::SINGLE_LINE | -                           ScanFlag::IGNORE_LINE_DISTANCE; +                           ScanFlag::IGNORE_STAGGER_OFFSET | +                           ScanFlag::IGNORE_COLOR_OFFSET;      compute_session(dev, session, move_sensor);      dev->cmd_set->init_regs_for_scan_session(dev, move_sensor, ®s, session); -  size = pixels * 3; -  std::vector<uint8_t> line(size); -      // write registers and scan data      dev->interface->write_registers(regs); @@ -1486,14 +1139,13 @@ static void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor&          return;      } -    sanei_genesys_read_data_from_scanner(dev, line.data(), size); +    auto image = read_unshuffled_image_from_scanner(dev, session, session.output_line_bytes);      // stop scanning      scanner_stop_action(*dev); -  if (DBG_LEVEL >= DBG_data) -    { -      sanei_genesys_write_pnm_file("gl124_movetocalarea.pnm", line.data(), 8, 3, pixels, 1); +    if (dbg_log_image_data()) { +        write_tiff_file("gl124_movetocalarea.tiff", image);      }  } @@ -1505,513 +1157,60 @@ static void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor&  SensorExposure CommandSetGl124::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,                                                  Genesys_Register_Set& regs) const  { -    DBG_HELPER(dbg); -  int num_pixels; -  int total_size; -  int resolution; -  int dpihw; -  int i, j; -  int val; -  int channels; -  int avg[3]; -  int turn; -  uint16_t exp[3],target; - -  /* move to calibration area */ -  move_to_calibration_area(dev, sensor, regs); - -  /* offset calibration is always done in 16 bit depth color mode */ -  channels = 3; -    dpihw = sensor.get_register_hwdpi(dev->settings.xres); -    resolution = dpihw; -    unsigned ccd_size_divisor = sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres); -    resolution /= ccd_size_divisor; - -    const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, -                                                         dev->settings.scan_method); -    num_pixels = (calib_sensor.sensor_pixels * resolution) / calib_sensor.optical_res; - -  /* initial calibration reg values */ -  regs = dev->reg; - -    ScanSession session; -    session.params.xres = resolution; -    session.params.yres = resolution; -    session.params.startx = 0; -    session.params.starty = 0; -    session.params.pixels = num_pixels; -    session.params.lines = 1; -    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_LINE_DISTANCE; -    compute_session(dev, session, calib_sensor); - -    init_regs_for_scan_session(dev, calib_sensor, ®s, session); - -    total_size = num_pixels * channels * (session.params.depth / 8) * 1; -  std::vector<uint8_t> line(total_size); - -    // initial loop values and boundaries -    exp[0] = calib_sensor.exposure.red; -    exp[1] = calib_sensor.exposure.green; -    exp[2] = calib_sensor.exposure.blue; -  target=sensor.gain_white_ref*256; - -  turn = 0; - -  /* no move during led calibration */ -  sanei_genesys_set_motor_power(regs, false); -    bool acceptable = false; -  do -    { -        // set up exposure -        regs.set24(REG_EXPR, exp[0]); -        regs.set24(REG_EXPG, exp[1]); -        regs.set24(REG_EXPB, exp[2]); - -        // write registers and scan data -        dev->interface->write_registers(regs); - -      DBG(DBG_info, "%s: starting line reading\n", __func__); -        begin_scan(dev, calib_sensor, ®s, true); - -        if (is_testing_mode()) { -            dev->interface->test_checkpoint("led_calibration"); -            scanner_stop_action(*dev); -            return calib_sensor.exposure; -        } - -        sanei_genesys_read_data_from_scanner(dev, line.data(), total_size); - -        // stop scanning -        scanner_stop_action(*dev); - -      if (DBG_LEVEL >= DBG_data) -	{ -          char fn[30]; -          std::snprintf(fn, 30, "gl124_led_%02d.pnm", turn); -          sanei_genesys_write_pnm_file(fn, line.data(), session.params.depth, channels, num_pixels, -                                       1); -	} - -      /* compute average */ -      for (j = 0; j < channels; j++) -	{ -	  avg[j] = 0; -	  for (i = 0; i < num_pixels; i++) -	    { -	      if (dev->model->is_cis) -		val = -		  line[i * 2 + j * 2 * num_pixels + 1] * 256 + -		  line[i * 2 + j * 2 * num_pixels]; -	      else -		val = -		  line[i * 2 * channels + 2 * j + 1] * 256 + -		  line[i * 2 * channels + 2 * j]; -	      avg[j] += val; -	    } - -	  avg[j] /= num_pixels; -	} - -      DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]); - -      /* check if exposure gives average within the boundaries */ -        acceptable = true; -      for(i=0;i<3;i++) -        { -          /* we accept +- 2% delta from target */ -          if(abs(avg[i]-target)>target/50) -            { -                float prev_weight = 0.5; -                exp[i] = exp[i] * prev_weight + ((exp[i] * target) / avg[i]) * (1 - prev_weight); -                acceptable = false; -            } -        } - -      turn++; -    } -  while (!acceptable && turn < 100); - -  DBG(DBG_info, "%s: acceptable exposure: %d,%d,%d\n", __func__, exp[0], exp[1], exp[2]); - -    // set these values as final ones for scan -    dev->reg.set24(REG_EXPR, exp[0]); -    dev->reg.set24(REG_EXPG, exp[1]); -    dev->reg.set24(REG_EXPB, exp[2]); - -    return { exp[0], exp[1], exp[2] }; +    return scanner_led_calibration(*dev, sensor, regs);  } -/** - * 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, total_size; -    int topavg, bottomavg, lines; -  int top, bottom, black_pixels, 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; -  dev->calib_pixels = sensor.sensor_pixels; -  lines=1; -    pixels = (sensor.sensor_pixels * sensor.optical_res) / sensor.optical_res; -    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_LINE_DISTANCE; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, ®s, session); - -  sanei_genesys_set_motor_power(regs, false); - -  /* allocate memory for scans */ -    total_size = pixels * channels * lines * (session.params.depth / 8); - -  std::vector<uint8_t> first_line(total_size); -  std::vector<uint8_t> second_line(total_size); - -  /* 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, ®s, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("offset_calibration"); -        return; -    } - -    sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size); -  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.data(), session.params.depth, -                                     channels, pixels, lines); -   } - -  bottomavg = dark_average(first_line.data(), 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, ®s, true); -    sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size); - -  topavg = dark_average(second_line.data(), 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, ®s, true); -        sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size); - -      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.data(), session.params.depth, -                                       channels, pixels, lines); -	} - -      avg = dark_average(second_line.data(), 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); -  int pixels; -  int total_size; -  int i, j, channels; -  int max[3]; -  float gain[3],coeff; -  int val, 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 */ -  channels = 3; - -  if(dev->settings.xres<sensor.optical_res) -    { -        coeff = 0.9f; -    } else { -        coeff = 1.0f; -    } -  lines=10; -     pixels = (sensor.sensor_pixels * sensor.optical_res) / sensor.optical_res; - -    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_LINE_DISTANCE; -    compute_session(dev, session, sensor); - -    try { -        init_regs_for_scan_session(dev, sensor, ®s, 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); - -    total_size = pixels * channels * (16 / session.params.depth) * lines; - -  std::vector<uint8_t> line(total_size); - -    set_fe(dev, sensor, AFE_SET); -    begin_scan(dev, sensor, ®s, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("coarse_gain_calibration"); -        scanner_stop_action(*dev); -        move_back_home(dev, true); -        return; -    } - -    sanei_genesys_read_data_from_scanner(dev, line.data(), total_size); - -    if (DBG_LEVEL >= DBG_data) { -        sanei_genesys_write_pnm_file("gl124_gain.pnm", line.data(), session.params.depth, -                                     channels, pixels, lines); -    } - -  /* average value on each channel */ -  for (j = 0; j < channels; j++) -    { -      max[j] = 0; -      for (i = pixels/4; i < (pixels*3/4); i++) -	{ -            if (dev->model->is_cis) { -                val = line[i + j * pixels]; -            } else { -                val = line[i * channels + j]; -            } - -	    max[j] += val; -	} -      max[j] = max[j] / (pixels/2); - -      gain[j] = (static_cast<float>(sensor.gain_white_ref) * coeff) / max[j]; - -      /* turn logical gain value into gain code, checking for overflow */ -        code = static_cast<int>(283 - 208 / gain[j]); -      if (code > 255) -	code = 255; -      else if (code < 0) -	code = 0; -      dev->frontend.set_gain(j, code); - -      DBG(DBG_proc, "%s: channel %d, max=%d, gain = %f, setting:%d\n", __func__, j, max[j], -          gain[j], dev->frontend.get_gain(j)); -    } - -    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  // between 2 scans is below a threshold  void CommandSetGl124::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                           Genesys_Register_Set* reg, int* channels, -                                           int* total_size) const +                                           Genesys_Register_Set* reg) const  {      DBG_HELPER(dbg); -  int num_pixels; - -  *channels=3;    *reg = dev->reg; +    auto 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 = sensor.optical_res; +    session.params.xres = sensor.full_resolution;      session.params.yres = dev->motor.base_ydpi; -    session.params.startx = sensor.sensor_pixels / 4; +    session.params.startx = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH / 4;      session.params.starty = 0; -    session.params.pixels = sensor.sensor_pixels / 2; +    session.params.pixels = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH / 2;      session.params.lines = 1; -    session.params.depth = 8; -    session.params.channels = *channels; +    session.params.depth = dev->model->bpp_color_values.front(); +    session.params.channels = 3;      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_LINE_DISTANCE; +    session.params.flags = flags; +      compute_session(dev, session, sensor);      init_regs_for_scan_session(dev, sensor, reg, session); -    num_pixels = session.output_pixels; - -  *total_size = num_pixels * 3 * 1;        /* colors * bytes_per_color * scan lines */ -    sanei_genesys_set_motor_power(*reg, false); -    dev->interface->write_registers(*reg);  }  /** @brief default GPIO values @@ -2049,64 +1248,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);  }  /** @@ -2118,7 +1261,7 @@ void CommandSetGl124::init(Genesys_Device* dev) const    DBG_INIT ();      DBG_HELPER(dbg); -    sanei_genesys_asic_init(dev, 0); +    sanei_genesys_asic_init(dev);  } @@ -2244,26 +1387,5 @@ void CommandSetGl124::eject_document(Genesys_Device* dev) const      throw SaneException("not implemented");  } -void CommandSetGl124::search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                   bool forward, bool black) const -{ -    (void) dev; -    (void) sensor; -    (void) forward; -    (void) black; -    throw SaneException("not implemented"); -} - -void CommandSetGl124::move_to_ta(Genesys_Device* dev) const -{ -    (void) dev; -    throw SaneException("not implemented"); -} - -std::unique_ptr<CommandSet> create_gl124_cmd_set() -{ -    return std::unique_ptr<CommandSet>(new CommandSetGl124{}); -} -  } // namespace gl124  } // namespace genesys diff --git a/backend/genesys/gl124.h b/backend/genesys/gl124.h index cdf8faf..ea7041e 100644 --- a/backend/genesys/gl124.h +++ b/backend/genesys/gl124.h @@ -45,74 +45,12 @@  #define BACKEND_GENESYS_GL124_H  #include "genesys.h" -#include "command_set.h" +#include "command_set_common.h"  namespace genesys {  namespace gl124 { -typedef struct -{ -  uint8_t r31; -  uint8_t r32; -  uint8_t r33; -  uint8_t r34; -  uint8_t r35; -  uint8_t r36; -  uint8_t r38; -} Gpio_layout; - -/** @brief gpio layout - * describes initial gpio settings for a given model - * registers 0x31 to 0x38 - */ -static Gpio_layout gpios[]={ -	/* LiDE 110 */ -	{ /*    0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x38 */ -		0x9f, 0x59, 0x01, 0x80, 0x5f, 0x01, 0x00 -	}, -	/* LiDE 210 */ -	{ -		0x9f, 0x59, 0x01, 0x80, 0x5f, 0x01, 0x00 -	}, -	/* LiDE 120 */ -	{ -		0x9f, 0x53, 0x01, 0x80, 0x5f, 0x01, 0x00 -	}, -}; - -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); - -class CommandSetGl124 : public CommandSet +class CommandSetGl124 : public CommandSetCommon  {  public:      ~CommandSetGl124() override = default; @@ -122,17 +60,11 @@ public:      void init(Genesys_Device* dev) const override;      void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, -                              Genesys_Register_Set* regs, int* channels, -                              int* total_size) const override; - -    void init_regs_for_coarse_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                          Genesys_Register_Set& regs) const override; +                              Genesys_Register_Set* regs) const override;      void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,                                 Genesys_Register_Set& regs) const override; -    void init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; -      void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,                                      Genesys_Register_Set* reg,                                      const ScanSession& session) const override; @@ -148,8 +80,6 @@ public:      void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; -    void search_start_position(Genesys_Device* dev) const override; -      void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,                              Genesys_Register_Set& regs) const override; @@ -165,8 +95,6 @@ public:      void update_hardware_sensors(struct Genesys_Scanner* s) const override; -    bool needs_update_home_sensor_gpio() const override { return true; } -      void update_home_sensor_gpio(Genesys_Device& dev) const override;      void load_document(Genesys_Device* dev) const override; @@ -175,11 +103,6 @@ public:      void eject_document(Genesys_Device* dev) const override; -    void search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, -                      bool forward, bool black) const override; - -    void move_to_ta(Genesys_Device* dev) const override; -      void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t* data,                             int size) const override; diff --git a/backend/genesys/gl646.cpp b/backend/genesys/gl646.cpp index 04ee85e..61fa1e0 100644 --- a/backend/genesys/gl646.cpp +++ b/backend/genesys/gl646.cpp @@ -63,9 +63,336 @@ namespace {  constexpr unsigned CALIBRATION_LINES = 10;  } // namespace -static void gl646_send_slope_table(Genesys_Device* dev, int table_nr, -                                   const std::vector<uint16_t>& slope_table, -                                   int steps); +static void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int resolution); + + +static void gl646_set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set, int dpi); + +static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, +                        const ScanSession& session, bool move, +                        std::vector<uint8_t>& data, const char* test_identifier); +/** + * Send the stop scan command + * */ +static void end_scan_impl(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop, +                          bool eject); + +/** + * master motor settings table entry + */ +struct Motor_Master +{ +    MotorId motor_id; +    unsigned dpi; +    unsigned channels; + +    // settings +    StepType steptype; +    bool fastmod; // fast scanning +    bool fastfed; // fast fed slope tables +    SANE_Int mtrpwm; +    MotorSlope slope1; +    MotorSlope slope2; +    SANE_Int fwdbwd; // forward/backward steps +}; + +/** + * master motor settings, for a given motor and dpi, + * it gives steps and speed informations + */ +static Motor_Master motor_master[] = { +    /* HP3670 motor settings */ +    {MotorId::HP3670, 50, 3, StepType::HALF, false, true, 1, +     MotorSlope::create_from_steps(2329, 120, 229), +     MotorSlope::create_from_steps(3399, 337, 192), 192}, + +    {MotorId::HP3670, 75, 3, StepType::FULL, false, true, 1, +     MotorSlope::create_from_steps(3429, 305, 200), +     MotorSlope::create_from_steps(3399, 337, 192), 192}, + +    {MotorId::HP3670, 100, 3, StepType::HALF, false, true, 1, +     MotorSlope::create_from_steps(2905, 187, 143), +     MotorSlope::create_from_steps(3399, 337, 192), 192}, + +    {MotorId::HP3670, 150, 3, StepType::HALF, false, true, 1, +     MotorSlope::create_from_steps(3429, 305, 73), +     MotorSlope::create_from_steps(3399, 337, 192), 192}, + +    {MotorId::HP3670, 300, 3, StepType::HALF, false, true, 1, +     MotorSlope::create_from_steps(1055, 563, 11), +     MotorSlope::create_from_steps(3399, 337, 192), 192}, + +    {MotorId::HP3670, 600, 3, StepType::FULL, false, true, 0, +     MotorSlope::create_from_steps(10687, 5126, 3), +     MotorSlope::create_from_steps(3399, 337, 192), 192}, + +    {MotorId::HP3670,1200, 3, StepType::HALF, false, true, 0, +     MotorSlope::create_from_steps(15937, 6375, 3), +     MotorSlope::create_from_steps(3399, 337, 192), 192}, + +    {MotorId::HP3670, 50, 1, StepType::HALF, false, true, 1, +     MotorSlope::create_from_steps(2329, 120, 229), +     MotorSlope::create_from_steps(3399, 337, 192), 192}, + +    {MotorId::HP3670, 75, 1, StepType::FULL, false, true, 1, +     MotorSlope::create_from_steps(3429, 305, 200), +     MotorSlope::create_from_steps(3399, 337, 192), 192}, + +    {MotorId::HP3670, 100, 1, StepType::HALF, false, true, 1, +     MotorSlope::create_from_steps(2905, 187, 143), +     MotorSlope::create_from_steps(3399, 337, 192), 192}, + +    {MotorId::HP3670, 150, 1, StepType::HALF, false, true, 1, +     MotorSlope::create_from_steps(3429, 305, 73), +     MotorSlope::create_from_steps(3399, 337, 192), 192}, + +    {MotorId::HP3670, 300, 1, StepType::HALF, false, true, 1, +     MotorSlope::create_from_steps(1055, 563, 11), +     MotorSlope::create_from_steps(3399, 337, 192), 192}, + +    {MotorId::HP3670, 600, 1, StepType::FULL, false, true, 0, +     MotorSlope::create_from_steps(10687, 5126, 3), +     MotorSlope::create_from_steps(3399, 337, 192), 192}, + +    {MotorId::HP3670,1200, 1, StepType::HALF, false, true, 0, +     MotorSlope::create_from_steps(15937, 6375, 3), +     MotorSlope::create_from_steps(3399, 337, 192), 192}, + +    /* HP2400/G2410 motor settings base motor dpi = 600 */ +    {MotorId::HP2400, 50, 3, StepType::FULL, false, true, 63, +     MotorSlope::create_from_steps(8736, 601, 120), +     MotorSlope::create_from_steps(4905, 337, 192), 192}, + +    {MotorId::HP2400, 100, 3, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(8736, 601, 120), +     MotorSlope::create_from_steps(4905, 337, 192), 192}, + +    {MotorId::HP2400, 150, 3, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(15902, 902, 67), +     MotorSlope::create_from_steps(4905, 337, 192), 192}, + +    {MotorId::HP2400, 300, 3, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(16703, 2188, 32), +     MotorSlope::create_from_steps(4905, 337, 192), 192}, + +    {MotorId::HP2400, 600, 3, StepType::FULL, false, true, 63, +     MotorSlope::create_from_steps(18761, 18761, 3), +     MotorSlope::create_from_steps(4905, 627, 192), 192}, + +    {MotorId::HP2400,1200, 3, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(43501, 43501, 3), +     MotorSlope::create_from_steps(4905, 627, 192), 192}, + +    {MotorId::HP2400, 50, 1, StepType::FULL, false, true, 63, +     MotorSlope::create_from_steps(8736, 601, 120), +     MotorSlope::create_from_steps(4905, 337, 192), 192}, + +    {MotorId::HP2400, 100, 1, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(8736, 601, 120), +     MotorSlope::create_from_steps(4905, 337, 192), 192}, + +    {MotorId::HP2400, 150, 1, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(15902, 902, 67), +     MotorSlope::create_from_steps(4905, 337, 192), 192}, + +    {MotorId::HP2400, 300, 1, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(16703, 2188, 32), +     MotorSlope::create_from_steps(4905, 337, 192), 192}, + +    {MotorId::HP2400, 600, 1, StepType::FULL, false, true, 63, +     MotorSlope::create_from_steps(18761, 18761, 3), +     MotorSlope::create_from_steps(4905, 337, 192), 192}, + +    {MotorId::HP2400,1200, 1, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(43501, 43501, 3), +     MotorSlope::create_from_steps(4905, 337, 192), 192}, + +    /* XP 200 motor settings */ +    {MotorId::XP200, 75, 3, StepType::HALF, true, false, 0, +     MotorSlope::create_from_steps(6000, 2136, 4), +     MotorSlope::create_from_steps(12000, 1200, 8), 1}, + +    {MotorId::XP200, 100, 3, StepType::HALF, true, false, 0, +     MotorSlope::create_from_steps(6000, 2850, 4), +     MotorSlope::create_from_steps(12000, 1200, 8), 1}, + +    {MotorId::XP200, 200, 3, StepType::HALF, true, false, 0, +     MotorSlope::create_from_steps(6999, 5700, 4), +     MotorSlope::create_from_steps(12000, 1200, 8), 1}, + +    {MotorId::XP200, 250, 3, StepType::HALF, true, false, 0, +     MotorSlope::create_from_steps(6999, 6999, 4), +     MotorSlope::create_from_steps(12000, 1200, 8), 1}, + +    {MotorId::XP200, 300, 3, StepType::HALF, true, false, 0, +     MotorSlope::create_from_steps(13500, 13500, 4), +     MotorSlope::create_from_steps(12000, 1200, 8), 1}, + +    {MotorId::XP200, 600, 3, StepType::HALF, true, true, 0, +     MotorSlope::create_from_steps(31998, 31998, 4), +     MotorSlope::create_from_steps(12000, 1200, 2), 1}, + +    {MotorId::XP200, 75, 1, StepType::HALF, true, false, 0, +     MotorSlope::create_from_steps(6000, 2000, 4), +     MotorSlope::create_from_steps(12000, 1200, 8), 1}, + +    {MotorId::XP200, 100, 1, StepType::HALF, true, false, 0, +     MotorSlope::create_from_steps(6000, 1300, 4), +     MotorSlope::create_from_steps(12000, 1200, 8), 1}, + +    {MotorId::XP200, 200, 1, StepType::HALF, true, true, 0, +     MotorSlope::create_from_steps(6000, 3666, 4), +     MotorSlope::create_from_steps(12000, 1200, 8), 1}, + +    {MotorId::XP200, 300, 1, StepType::HALF, true, false, 0, +     MotorSlope::create_from_steps(6500, 6500, 4), +     MotorSlope::create_from_steps(12000, 1200, 8), 1}, + +    {MotorId::XP200, 600, 1, StepType::HALF, true, true, 0, +     MotorSlope::create_from_steps(24000, 24000, 4), +     MotorSlope::create_from_steps(12000, 1200, 2), 1}, + +    /* HP scanjet 2300c */ +    {MotorId::HP2300, 75, 3, StepType::FULL, false, true, 63, +     MotorSlope::create_from_steps(8139, 560, 120), +     MotorSlope::create_from_steps(4905, 337, 120), 16}, + +    {MotorId::HP2300, 150, 3, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(7903, 543, 67), +     MotorSlope::create_from_steps(4905, 337, 120), 16}, + +    {MotorId::HP2300, 300, 3, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(2175, 1087, 3), +     MotorSlope::create_from_steps(4905, 337, 120), 16}, + +    {MotorId::HP2300, 600, 3, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(8700, 4350, 3), +     MotorSlope::create_from_steps(4905, 337, 120), 16}, + +    {MotorId::HP2300,1200, 3, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(17400, 8700, 3), +     MotorSlope::create_from_steps(4905, 337, 120), 16}, + +    {MotorId::HP2300, 75, 1, StepType::FULL, false, true, 63, +     MotorSlope::create_from_steps(8139, 560, 120), +     MotorSlope::create_from_steps(4905, 337, 120), 16}, + +    {MotorId::HP2300, 150, 1, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(7903, 543, 67), +     MotorSlope::create_from_steps(4905, 337, 120), 16}, + +    {MotorId::HP2300, 300, 1, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(2175, 1087, 3), +     MotorSlope::create_from_steps(4905, 337, 120), 16}, + +    {MotorId::HP2300, 600, 1, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(8700, 4350, 3), +     MotorSlope::create_from_steps(4905, 337, 120), 16}, + +    {MotorId::HP2300,1200, 1, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(17400, 8700, 3), +     MotorSlope::create_from_steps(4905, 337, 120), 16}, + +    /* non half ccd settings for 300 dpi +    {MotorId::HP2300, 300, 3, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(5386, 2175, 44), +     MotorSlope::create_from_steps(4905, 337, 120), 16}, + +    {MotorId::HP2300, 300, 1, StepType::HALF, false, true, 63, +     MotorSlope::create_from_steps(5386, 2175, 44), +     MotorSlope::create_from_steps(4905, 337, 120), 16}, +    */ + +    /* MD5345/6471 motor settings */ +    /* vfinal=(exposure/(1200/dpi))/step_type */ +    {MotorId::MD_5345, 50, 3, StepType::HALF, false, true, 2, +     MotorSlope::create_from_steps(2500, 250, 255), +     MotorSlope::create_from_steps(2000, 300, 255), 64}, + +    {MotorId::MD_5345, 75, 3, StepType::HALF, false, true, 2, +     MotorSlope::create_from_steps(2500, 343, 255), +     MotorSlope::create_from_steps(2000, 300, 255), 64}, + +    {MotorId::MD_5345, 100, 3, StepType::HALF, false, true, 2, +     MotorSlope::create_from_steps(2500, 458, 255), +     MotorSlope::create_from_steps(2000, 300, 255), 64}, + +    {MotorId::MD_5345, 150, 3, StepType::HALF, false, true, 2, +     MotorSlope::create_from_steps(2500, 687, 255), +     MotorSlope::create_from_steps(2000, 300, 255), 64}, + +    {MotorId::MD_5345, 200, 3, StepType::HALF, false, true, 2, +     MotorSlope::create_from_steps(2500, 916, 255), +     MotorSlope::create_from_steps(2000, 300, 255), 64}, + +    {MotorId::MD_5345, 300, 3, StepType::HALF, false, true, 2, +     MotorSlope::create_from_steps(2500, 1375, 255), +     MotorSlope::create_from_steps(2000, 300, 255), 64}, + +    {MotorId::MD_5345, 400, 3, StepType::HALF, false, true, 0, +     MotorSlope::create_from_steps(2000, 1833, 32), +     MotorSlope::create_from_steps(2000, 300, 255), 32}, + +    {MotorId::MD_5345, 500, 3, StepType::HALF, false, true, 0, +     MotorSlope::create_from_steps(2291, 2291, 32), +     MotorSlope::create_from_steps(2000, 300, 255), 32}, + +    {MotorId::MD_5345, 600, 3, StepType::HALF, false, true, 0, +     MotorSlope::create_from_steps(2750, 2750, 32), +     MotorSlope::create_from_steps(2000, 300, 255), 32}, + +    {MotorId::MD_5345, 1200, 3, StepType::QUARTER, false, true, 0, +     MotorSlope::create_from_steps(2750, 2750, 16), +     MotorSlope::create_from_steps(2000, 300, 255), 146}, + +    {MotorId::MD_5345, 2400, 3, StepType::QUARTER, false, true, 0, +     MotorSlope::create_from_steps(5500, 5500, 16), +     MotorSlope::create_from_steps(2000, 300, 255), 146}, + +    {MotorId::MD_5345, 50, 1, StepType::HALF, false, true, 2, +     MotorSlope::create_from_steps(2500, 250, 255), +     MotorSlope::create_from_steps(2000, 300, 255), 64}, + +    {MotorId::MD_5345, 75, 1, StepType::HALF, false, true, 2, +     MotorSlope::create_from_steps(2500, 343, 255), +     MotorSlope::create_from_steps(2000, 300, 255), 64}, + +    {MotorId::MD_5345, 100, 1, StepType::HALF, false, true, 2, +     MotorSlope::create_from_steps(2500, 458, 255), +     MotorSlope::create_from_steps(2000, 300, 255), 64}, + +    {MotorId::MD_5345, 150, 1, StepType::HALF, false, true, 2, +     MotorSlope::create_from_steps(2500, 687, 255), +     MotorSlope::create_from_steps(2000, 300, 255), 64}, + +    {MotorId::MD_5345, 200, 1, StepType::HALF, false, true, 2, +     MotorSlope::create_from_steps(2500, 916, 255), +     MotorSlope::create_from_steps(2000, 300, 255), 64}, + +    {MotorId::MD_5345, 300, 1, StepType::HALF, false, true, 2, +     MotorSlope::create_from_steps(2500, 1375, 255), +     MotorSlope::create_from_steps(2000, 300, 255), 64}, + +    {MotorId::MD_5345, 400, 1, StepType::HALF, false, true, 0, +     MotorSlope::create_from_steps(2000, 1833, 32), +     MotorSlope::create_from_steps(2000, 300, 255), 32}, + +    {MotorId::MD_5345, 500, 1, StepType::HALF, false, true, 0, +     MotorSlope::create_from_steps(2291, 2291, 32), +     MotorSlope::create_from_steps(2000, 300, 255), 32}, + +    {MotorId::MD_5345, 600, 1, StepType::HALF, false, true, 0, +     MotorSlope::create_from_steps(2750, 2750, 32), +     MotorSlope::create_from_steps(2000, 300, 255), 32}, + +    {MotorId::MD_5345, 1200, 1, StepType::QUARTER, false, true, 0, +     MotorSlope::create_from_steps(2750, 2750, 16), +     MotorSlope::create_from_steps(2000, 300, 255), 146}, + +    {MotorId::MD_5345, 2400, 1, StepType::QUARTER, false, true, 0, +     MotorSlope::create_from_steps(5500, 5500, 16), +     MotorSlope::create_from_steps(2000, 300, 255), 146}, /* 5500 guessed */ +};  /**   * reads value from gpio endpoint @@ -105,44 +432,6 @@ static void gl646_stop_motor(Genesys_Device* dev)  }  /** - * 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   * @param required required resolution @@ -157,7 +446,6 @@ static int get_cksel(SensorId sensor_id, int required, unsigned channels)              sensor.matches_channel_count(channels))          {              unsigned cksel = sensor.ccd_pixels_per_system_pixel(); -            DBG(DBG_io, "%s: match found for %d (cksel=%d)\n", __func__, required, cksel);              return cksel;          }      } @@ -177,7 +465,6 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene      uint32_t move = session.params.starty; -  int i, nb;    Motor_Master *motor = nullptr;    uint32_t z1, z2;    int feedl; @@ -185,57 +472,47 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene    /* for the given resolution, search for master     * motor mode setting */ -  i = 0; -  nb = sizeof (motor_master) / sizeof (Motor_Master); -  while (i < nb) -    { -      if (dev->model->motor_id == motor_master[i].motor_id -          && motor_master[i].dpi == session.params.yres -          && motor_master[i].channels == session.params.channels) -	{ -	  motor = &motor_master[i]; -	} -      i++; +    for (unsigned i = 0; i < sizeof (motor_master) / sizeof (Motor_Master); ++i) { +        if (dev->model->motor_id == motor_master[i].motor_id && +            motor_master[i].dpi == session.params.yres && +            motor_master[i].channels == session.params.channels) +        { +            motor = &motor_master[i]; +        }      } -  if (motor == nullptr) -    { +    if (motor == nullptr) {          throw SaneException("unable to find settings for motor %d at %d dpi, color=%d",                              static_cast<unsigned>(dev->model->motor_id),                              session.params.yres, session.params.channels);      } -  /* now we can search for the specific sensor settings */ -  i = 0; - -    // now apply values from settings to registers -    regs->set16(REG_EXPR, sensor.exposure.red); -    regs->set16(REG_EXPG, sensor.exposure.green); -    regs->set16(REG_EXPB, sensor.exposure.blue); - -    for (const auto& reg : sensor.custom_regs) { -        regs->set8(reg.address, reg.value); -    } +    scanner_setup_sensor(*dev, sensor, *regs);    /* now generate slope tables : we are not using generate_slope_table3 yet */ -    auto slope_table1 = create_slope_table(motor->slope1, motor->slope1.max_speed_w, StepType::FULL, -                                           1, 4, get_slope_table_max_size(AsicType::GL646)); -    auto slope_table2 = create_slope_table(motor->slope2, motor->slope2.max_speed_w, StepType::FULL, -                                           1, 4, get_slope_table_max_size(AsicType::GL646)); +    auto slope_table1 = create_slope_table_for_speed(motor->slope1, motor->slope1.max_speed_w, +                                                     StepType::FULL, 1, 4, +                                                     get_slope_table_max_size(AsicType::GL646)); +    auto slope_table2 = create_slope_table_for_speed(motor->slope2, motor->slope2.max_speed_w, +                                                     StepType::FULL, 1, 4, +                                                     get_slope_table_max_size(AsicType::GL646));    /* R01 */    /* now setup other registers for final scan (ie with shading enabled) */    /* watch dog + shading + scan enable */ -    regs->find_reg(0x01).value |= REG_0x01_DOGENB | REG_0x01_DVDSET | REG_0x01_SCAN; +    regs->find_reg(0x01).value |= REG_0x01_DOGENB | REG_0x01_SCAN;      if (dev->model->is_cis) {          regs->find_reg(0x01).value |= REG_0x01_CISSET;      } else {          regs->find_reg(0x01).value &= ~REG_0x01_CISSET;      } -  /* if device has no calibration, don't enable shading correction */ -  if (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION) +    // if device has no calibration, don't enable shading correction +    if (has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || +        has_flag(session.params.flags, ScanFlag::DISABLE_SHADING))      {          regs->find_reg(0x01).value &= ~REG_0x01_DVDSET; +    } else { +        regs->find_reg(0x01).value |= REG_0x01_DVDSET;      }      regs->find_reg(0x01).value &= ~REG_0x01_FASTMOD; @@ -284,7 +561,7 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene        break;      } -    if (dev->model->is_sheetfed) { +    if (dev->model->is_sheetfed || !has_flag(session.params.flags, ScanFlag::AUTO_GO_HOME)) {          regs->find_reg(0x02).value &= ~REG_0x02_AGOHOME;      } else {          regs->find_reg(0x02).value |= REG_0x02_AGOHOME; @@ -314,14 +591,20 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene              break;      } -    sanei_genesys_set_dpihw(*regs, sensor, sensor.optical_res); +    sanei_genesys_set_dpihw(*regs, sensor.full_resolution);    /* gamma enable for scans */ -    if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) { +    if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) {          regs->find_reg(0x05).value |= REG_0x05_GMM14BIT;      } -    regs->find_reg(0x05).value &= ~REG_0x05_GMMENB; +    if (!has_flag(session.params.flags, ScanFlag::DISABLE_GAMMA) && +        session.params.depth < 16) +    { +        regs->find_reg(REG_0x05).value |= REG_0x05_GMMENB; +    } else { +        regs->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB; +    }    /* true CIS gray if needed */      if (dev->model->is_cis && session.params.channels == 1 && dev->settings.true_gray) { @@ -356,17 +639,17 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene      // the steps count must be different by at most 128, otherwise it's impossible to construct      // a proper backtracking curve. We're using slightly lower limit to allow at least a minimum      // distance between accelerations (forward_steps, backward_steps) -    if (slope_table1.steps_count > slope_table2.steps_count + 100) { -        slope_table2.steps_count += slope_table1.steps_count - 100; +    if (slope_table1.table.size() > slope_table2.table.size() + 100) { +        slope_table2.expand_table(slope_table1.table.size() - 100, 1);      } -    if (slope_table2.steps_count > slope_table1.steps_count + 100) { -        slope_table1.steps_count += slope_table2.steps_count - 100; +    if (slope_table2.table.size() > slope_table1.table.size() + 100) { +        slope_table1.expand_table(slope_table2.table.size() - 100, 1);      } -    if (slope_table1.steps_count >= slope_table2.steps_count) { -        backward_steps += (slope_table1.steps_count - slope_table2.steps_count) * 2; +    if (slope_table1.table.size() >= slope_table2.table.size()) { +        backward_steps += (slope_table1.table.size() - slope_table2.table.size()) * 2;      } else { -        forward_steps += (slope_table2.steps_count - slope_table1.steps_count) * 2; +        forward_steps += (slope_table2.table.size() - slope_table1.table.size()) * 2;      }      if (forward_steps > 255) { @@ -382,8 +665,8 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene          forward_steps -= backward_steps - 255;      } -    regs->find_reg(0x21).value = slope_table1.steps_count; -    regs->find_reg(0x24).value = slope_table2.steps_count; +    regs->find_reg(0x21).value = slope_table1.table.size(); +    regs->find_reg(0x24).value = slope_table2.table.size();      regs->find_reg(0x22).value = forward_steps;      regs->find_reg(0x23).value = backward_steps; @@ -401,8 +684,11 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene      regs->set24(REG_MAXWD, session.output_line_bytes); -    regs->set16(REG_DPISET, session.output_resolution * session.ccd_size_divisor * -                            sensor.ccd_pixels_per_system_pixel()); +    // FIXME: the incoming sensor is selected for incorrect resolution +    const auto& dpiset_sensor = sanei_genesys_find_sensor(dev, session.params.xres, +                                                          session.params.channels, +                                                          session.params.scan_method); +    regs->set16(REG_DPISET, dpiset_sensor.register_dpiset);      regs->set16(REG_LPERIOD, sensor.exposure_lperiod);    /* move distance must be adjusted to take into account the extra lines @@ -410,8 +696,8 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene    feedl = move;      if (session.num_staggered_lines + session.max_color_shift_lines > 0 && feedl != 0) { -        int feed_offset = ((session.max_color_shift_lines + session.num_staggered_lines) * dev->motor.optical_ydpi) / -                motor->dpi; +        unsigned total_lines = session.max_color_shift_lines + session.num_staggered_lines; +        int feed_offset = (total_lines * dev->motor.base_ydpi) / motor->dpi;          if (feedl > feed_offset) {              feedl = feedl - feed_offset;          } @@ -424,8 +710,6 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene    /* but head has moved due to shading calibration => dev->scanhead_position_primary */    if (feedl > 0)      { -      DBG(DBG_info, "%s: initial move=%d\n", __func__, feedl); -        /* TODO clean up this when I'll fully understand.         * for now, special casing each motor */          switch (dev->model->motor_id) { @@ -505,12 +789,12 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene  	  if (motor->fastfed)          { -                feedl = feedl - 2 * slope_table2.steps_count - -                        (slope_table1.steps_count >> step_shift); +                feedl = feedl - 2 * slope_table2.table.size() - +                        (slope_table1.table.size() >> step_shift);  	    }  	  else  	    { -                feedl = feedl - (slope_table1.steps_count >> step_shift); +                feedl = feedl - (slope_table1.table.size() >> step_shift);  	    }  	  break;          } @@ -520,7 +804,6 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene  	feedl = 0;      } -  DBG(DBG_info, "%s: final move=%d\n", __func__, feedl);      regs->set24(REG_FEEDL, feedl);    regs->find_reg(0x65).value = motor->mtrpwm; @@ -528,7 +811,7 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene      sanei_genesys_calculate_zmod(regs->find_reg(0x02).value & REG_0x02_FASTFED,                                   sensor.exposure_lperiod,                                   slope_table1.table, -                                 slope_table1.steps_count, +                                 slope_table1.table.size(),                                    move, motor->fwdbwd, &z1, &z2);    /* no z1/z2 for sheetfed scanners */ @@ -538,7 +821,7 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene      }      regs->set16(REG_Z1MOD, z1);      regs->set16(REG_Z2MOD, z2); -    regs->find_reg(0x6b).value = slope_table2.steps_count; +    regs->find_reg(0x6b).value = slope_table2.table.size();    regs->find_reg(0x6c).value =      (regs->find_reg(0x6c).value & REG_0x6C_TGTIME) | ((z1 >> 13) & 0x38) | ((z2 >> 16)  								   & 0x07); @@ -548,10 +831,7 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene      // setup analog frontend      gl646_set_fe(dev, sensor, AFE_SET, session.output_resolution); -    dev->read_buffer.clear(); -    dev->read_buffer.alloc(session.buffer_size_read); - -    build_image_pipeline(dev, session); +    setup_image_pipeline(*dev, session);      dev->read_active = true; @@ -578,32 +858,8 @@ void CommandSetGl646::init_regs_for_scan_session(Genesys_Device* dev, const Gene          }      } -    gl646_send_slope_table(dev, 0, slope_table1.table, regs->get8(0x21)); -    gl646_send_slope_table(dev, 1, slope_table2.table, regs->get8(0x6b)); -} - - -/** copy sensor specific settings */ -/* *dev  : device infos -   *regs : regiters to be set -   extended : do extended set up -   ccd_size_divisor: set up for half ccd resolution -   all registers 08-0B, 10-1D, 52-5E are set up. They shouldn't -   appear anywhere else but in register init -*/ -static void -gl646_setup_sensor (Genesys_Device * dev, const Genesys_Sensor& sensor, Genesys_Register_Set * regs) -{ -    (void) dev; -    DBG(DBG_proc, "%s: start\n", __func__); - -    for (const auto& reg_setting : sensor.custom_base_regs) { -        regs->set8(reg_setting.address, reg_setting.value); -    } -    // FIXME: all other drivers don't set exposure here -    regs_set_exposure(AsicType::GL646, *regs, sensor.exposure); - -    DBG(DBG_proc, "%s: end\n", __func__); +    scanner_send_slope_table(dev, sensor, 0, slope_table1.table); +    scanner_send_slope_table(dev, sensor, 1, slope_table2.table);  }  /** @@ -632,8 +888,8 @@ gl646_init_regs (Genesys_Device * dev)      for (addr = 0x60; addr <= 0x6d; addr++)          dev->reg.init_reg(addr, 0); -  dev->reg.find_reg(0x01).value = 0x20 /*0x22 */ ;	/* enable shading, CCD, color, 1M */ -  dev->reg.find_reg(0x02).value = 0x30 /*0x38 */ ;	/* auto home, one-table-move, full step */ +    dev->reg.find_reg(0x01).value = 0x20 /*0x22 */ ;	/* enable shading, CCD, color, 1M */ +    dev->reg.find_reg(0x02).value = 0x30 /*0x38 */ ;	/* auto home, one-table-move, full step */      if (dev->model->motor_id == MotorId::MD_5345) {          dev->reg.find_reg(0x02).value |= 0x01; // half-step      } @@ -648,8 +904,8 @@ gl646_init_regs (Genesys_Device * dev)          default:        break;      } -  dev->reg.find_reg(0x03).value = 0x1f /*0x17 */ ;	/* lamp on */ -  dev->reg.find_reg(0x04).value = 0x13 /*0x03 */ ;	/* 8 bits data, 16 bits A/D, color, Wolfson fe *//* todo: according to spec, 0x0 is reserved? */ +    dev->reg.find_reg(0x03).value = 0x1f /*0x17 */ ;	/* lamp on */ +    dev->reg.find_reg(0x04).value = 0x13 /*0x03 */ ;	/* 8 bits data, 16 bits A/D, color, Wolfson fe *//* todo: according to spec, 0x0 is reserved? */    switch (dev->model->adc_id)      {      case AdcId::AD_XP200: @@ -664,9 +920,9 @@ gl646_init_regs (Genesys_Device * dev)    const auto& sensor = sanei_genesys_find_sensor_any(dev);    dev->reg.find_reg(0x05).value = 0x00;	/* 12 bits gamma, disable gamma, 24 clocks/pixel */ -    sanei_genesys_set_dpihw(dev->reg, sensor, sensor.optical_res); +    sanei_genesys_set_dpihw(dev->reg, sensor.full_resolution); -    if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) { +    if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) {          dev->reg.find_reg(0x05).value |= REG_0x05_GMM14BIT;      }      if (dev->model->adc_id == AdcId::AD_XP200) { @@ -679,8 +935,7 @@ gl646_init_regs (Genesys_Device * dev)          dev->reg.find_reg(0x06).value = 0x18; // PWRBIT on, shading gain=8, normal AFE image capture      } - -  gl646_setup_sensor(dev, sensor, &dev->reg); +    scanner_setup_sensor(*dev, sensor, dev->reg);    dev->reg.find_reg(0x1e).value = 0xf0;	/* watch-dog time */ @@ -788,54 +1043,15 @@ gl646_init_regs (Genesys_Device * dev)    dev->reg.find_reg(0x6c).value = 0x00;	/* peroid times for LPeriod, expR,expG,expB, Z1MODE, Z2MODE (one period time) */  } - -// Send slope table for motor movement slope_table in machine byte order -static void gl646_send_slope_table(Genesys_Device* dev, int table_nr, -                                   const std::vector<uint16_t>& slope_table, -                                   int steps) -{ -    DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %d)=%d .. %d", table_nr, steps, slope_table[0], -                    slope_table[steps - 1]); -  int dpihw; -  int start_address; - -  dpihw = dev->reg.find_reg(0x05).value >> 6; - -  if (dpihw == 0)		/* 600 dpi */ -    start_address = 0x08000; -  else if (dpihw == 1)		/* 1200 dpi */ -    start_address = 0x10000; -  else if (dpihw == 2)		/* 2400 dpi */ -    start_address = 0x1f800; -    else { -        throw SaneException("Unexpected dpihw"); -    } - -  std::vector<uint8_t> table(steps * 2); -  for (int i = 0; i < steps; i++) -    { -      table[i * 2] = slope_table[i] & 0xff; -      table[i * 2 + 1] = slope_table[i] >> 8; -    } - -    if (dev->interface->is_mock()) { -        dev->interface->record_slope_table(table_nr, slope_table); -    } -    dev->interface->write_buffer(0x3c, start_address + table_nr * 0x100, table.data(), steps * 2); -} -  // Set values of Analog Device type frontend  static void gl646_set_ad_fe(Genesys_Device* dev, uint8_t set)  {      DBG_HELPER(dbg);    int i; -  if (set == AFE_INIT) -    { -        DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, -            static_cast<unsigned>(dev->model->adc_id)); +    if (set == AFE_INIT) { -      dev->frontend = dev->frontend_initial; +        dev->frontend = dev->frontend_initial;          // write them to analog frontend          dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00)); @@ -888,8 +1104,7 @@ static void gl646_wm_hp3670(Genesys_Device* dev, const Genesys_Sensor& sensor, u      default:			/* AFE_SET */        /* mode setup */        i = dev->frontend.regs.get_value(0x03); -      if (dpi > sensor.optical_res / 2) -	{ +            if (dpi > sensor.full_resolution / 2) {        /* fe_reg_0x03 must be 0x12 for 1200 dpi in WOLFSON_HP3670.         * WOLFSON_HP2400 in 1200 dpi mode works well with  	   * fe_reg_0x03 set to 0x32 or 0x12 but not to 0x02 */ @@ -947,11 +1162,8 @@ static void gl646_set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint      }    /* initialize analog frontend */ -  if (set == AFE_INIT) -    { -        DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, -            static_cast<unsigned>(dev->model->adc_id)); -      dev->frontend = dev->frontend_initial; +    if (set == AFE_INIT) { +        dev->frontend = dev->frontend_initial;          // reset only done on init          dev->interface->write_fe_register(0x04, 0x80); @@ -1174,14 +1386,15 @@ void CommandSetGl646::load_document(Genesys_Device* dev) const    regs.init_reg(0x24, 4);    /* generate slope table 2 */ -    auto slope_table = create_slope_table(MotorSlope::create_from_steps(6000, 2400, 50), 2400, -                                          StepType::FULL, 1, 4, -                                          get_slope_table_max_size(AsicType::GL646)); +    auto slope_table = create_slope_table_for_speed(MotorSlope::create_from_steps(6000, 2400, 50), +                                                    2400, StepType::FULL, 1, 4, +                                                    get_slope_table_max_size(AsicType::GL646));      // document loading:      // send regs      // start motor      // wait e1 status to become e0 -    gl646_send_slope_table(dev, 1, slope_table.table, slope_table.steps_count); +    const auto& sensor = sanei_genesys_find_sensor_any(dev); +    scanner_send_slope_table(dev, sensor, 1, slope_table.table);      dev->interface->write_registers(regs); @@ -1292,9 +1505,8 @@ void CommandSetGl646::eject_document(Genesys_Device* dev) const      // home sensor is set when document is inserted      if (status.is_at_home) {          dev->document = false; -      DBG(DBG_info, "%s: no more document to eject\n", __func__); -      DBG(DBG_proc, "%s: end\n", __func__); -      return; +        DBG(DBG_info, "%s: no more document to eject\n", __func__); +        return;      }      // there is a document inserted, eject it @@ -1331,14 +1543,16 @@ void CommandSetGl646::eject_document(Genesys_Device* dev) const    regs.init_reg(0x24, 4);    /* generate slope table 2 */ -    auto slope_table = create_slope_table(MotorSlope::create_from_steps(10000, 1600, 60), 1600, -                                          StepType::FULL, 1, 4, -                                          get_slope_table_max_size(AsicType::GL646)); +    auto slope_table = create_slope_table_for_speed(MotorSlope::create_from_steps(10000, 1600, 60), +                                                    1600, StepType::FULL, 1, 4, +                                                    get_slope_table_max_size(AsicType::GL646));      // document eject:      // send regs      // start motor      // wait c1 status to become c8 : HOMESNR and ~MOTFLAG -    gl646_send_slope_table(dev, 1, slope_table.table, slope_table.steps_count); +    // FIXME: sensor is not used. +    const auto& sensor = sanei_genesys_find_sensor_any(dev); +    scanner_send_slope_table(dev, sensor, 1, slope_table.table);      dev->interface->write_registers(regs); @@ -1473,7 +1687,7 @@ void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home)    if (!i)			/* the loop counted down to 0, scanner still is busy */      { -        dev->set_head_pos_unknown(); +        dev->set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY);          throw SaneException(SANE_STATUS_DEVICE_BUSY, "motor is still on: device busy");      } @@ -1489,15 +1703,15 @@ void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home)      session.params.startx = 0;      session.params.starty = 65535;      session.params.pixels = 600; -    session.params.requested_pixels = 600;      session.params.lines = 1;      session.params.depth = 8;      session.params.channels = 3;      session.params.scan_method = dev->model->default_method;      session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;      session.params.color_filter = ColorFilter::RED; -    session.params.flags = ScanFlag::USE_XCORRECTION | -                            ScanFlag::REVERSE; +    session.params.flags = ScanFlag::REVERSE | +                           ScanFlag::AUTO_GO_HOME | +                           ScanFlag::DISABLE_GAMMA;      if (dev->model->default_method == ScanMethod::TRANSPARENCY) {          session.params.flags |= ScanFlag::USE_XPA;      } @@ -1520,8 +1734,7 @@ void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home)    /* registers are restored to an iddl state, give up if no head to park */      if (dev->model->is_sheetfed) { -      DBG(DBG_proc, "%s: end \n", __func__); -      return; +        return;      }      // starts scan @@ -1554,7 +1767,6 @@ void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home)              if (status.is_at_home) {  	      DBG(DBG_info, "%s: reached home position\n", __func__); -	      DBG(DBG_proc, "%s: end\n", __func__);                  dev->interface->sleep_ms(500);                  dev->set_head_pos_zero(ScanHeadId::PRIMARY);                  return; @@ -1567,7 +1779,7 @@ void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home)          // stop the motor          catch_all_exceptions(__func__, [&](){ gl646_stop_motor (dev); });          catch_all_exceptions(__func__, [&](){ end_scan_impl(dev, &dev->reg, true, false); }); -        dev->set_head_pos_unknown(); +        dev->set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY);          throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home");      } @@ -1576,165 +1788,60 @@ void CommandSetGl646::move_back_home(Genesys_Device* dev, bool wait_until_home)  }  /** - * Automatically set top-left edge of the scan area by scanning an - * area at 300 dpi from very top of scanner - * @param dev  device stucture describing the scanner - */ -void CommandSetGl646::search_start_position(Genesys_Device* dev) const -{ -    DBG_HELPER(dbg); -  Genesys_Settings settings; -  unsigned int resolution, x, y; - -  /* we scan at 300 dpi */ -  resolution = get_closest_resolution(dev->model->sensor_id, 300, 1); - -    // FIXME: the current approach of doing search only for one resolution does not work on scanners -    // whith employ different sensors with potentially different settings. -    const auto& sensor = sanei_genesys_find_sensor(dev, resolution, 1, -                                                   dev->model->default_method); - -  /* fill settings for a gray level scan */ -  settings.scan_method = dev->model->default_method; -  settings.scan_mode = ScanColorMode::GRAY; -  settings.xres = resolution; -  settings.yres = resolution; -  settings.tl_x = 0; -  settings.tl_y = 0; -  settings.pixels = 600; -    settings.requested_pixels = settings.pixels; -  settings.lines = dev->model->search_lines; -  settings.depth = 8; -  settings.color_filter = ColorFilter::RED; - -  settings.disable_interpolation = 0; -  settings.threshold = 0; - -    // scan the desired area -    std::vector<uint8_t> data; -    simple_scan(dev, sensor, settings, true, true, false, data, "search_start_position"); - -    // handle stagger case : reorder gray data and thus loose some lines -    auto staggered_lines = dev->session.num_staggered_lines; -    if (staggered_lines > 0) { -        DBG(DBG_proc, "%s: 'un-staggering'\n", __func__); -        for (y = 0; y < settings.lines - staggered_lines; y++) { -            /* one point out of 2 is 'unaligned' */ -            for (x = 0; x < settings.pixels; x += 2) -        { -                data[y * settings.pixels + x] = data[(y + staggered_lines) * settings.pixels + x]; -        } -          } -        /* correct line number */ -        settings.lines -= staggered_lines; -    } - -    if (DBG_LEVEL >= DBG_data) -      { -        sanei_genesys_write_pnm_file("gl646_search_position.pnm", data.data(), settings.depth, 1, -                                     settings.pixels, settings.lines); -      } - -    // now search reference points on the data -    for (auto& sensor_update : -            sanei_genesys_find_sensors_all_for_write(dev, dev->model->default_method)) -    { -        sanei_genesys_search_reference_point(dev, sensor_update, data.data(), 0, -                                             resolution, settings.pixels, settings.lines); -    } -} - -/** - * internally overriden during effective calibration - * sets up register for coarse gain calibration - */ -void CommandSetGl646::init_regs_for_coarse_calibration(Genesys_Device* dev, -                                                       const Genesys_Sensor& sensor, -                                                       Genesys_Register_Set& regs) const -{ -    DBG_HELPER(dbg); -    (void) dev; -    (void) sensor; -    (void) regs; -} - - -/**   * init registers for shading calibration   * we assume that scanner's head is on an area suiting shading calibration.   * We scan a full scan width area by the shading line number for the device - * at either at full sensor's resolution or half depending upon ccd_size_divisor - * @param dev scanner's device   */  void CommandSetGl646::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,                                              Genesys_Register_Set& regs) const  {      DBG_HELPER(dbg);      (void) regs; -  Genesys_Settings settings; -  int cksel = 1;    /* fill settings for scan : always a color scan */    int channels = 3; +    unsigned cksel = get_cksel(dev->model->sensor_id, dev->settings.xres, channels); + +    unsigned resolution = sensor.get_optical_resolution() / cksel; +    // FIXME: we select wrong calibration sensor      const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->settings.xres, channels,                                                           dev->settings.scan_method); -    unsigned ccd_size_divisor = calib_sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres); +    auto pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; -  settings.scan_method = dev->settings.scan_method; -  settings.scan_mode = dev->settings.scan_mode; -    if (!dev->model->is_cis) { -      // FIXME: always a color scan, but why don't we set scan_mode to COLOR_SINGLE_PASS always? -      settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; -    } -  settings.xres = sensor.optical_res / ccd_size_divisor; -  cksel = get_cksel(dev->model->sensor_id, dev->settings.xres, channels); -  settings.xres = settings.xres / cksel; -  settings.yres = settings.xres; -  settings.tl_x = 0; -  settings.tl_y = 0; -    settings.pixels = (calib_sensor.sensor_pixels * settings.xres) / calib_sensor.optical_res; -    settings.requested_pixels = settings.pixels; -  dev->calib_lines = dev->model->shading_lines; -  settings.lines = dev->calib_lines * (3 - ccd_size_divisor); -  settings.depth = 16; -  settings.color_filter = dev->settings.color_filter; - -  settings.disable_interpolation = dev->settings.disable_interpolation; -  settings.threshold = dev->settings.threshold; - -    // we don't want top offset, but we need right margin to be the same than the one for the final -    // scan -    setup_for_scan(dev, calib_sensor, &dev->reg, settings, true, false, false, false); - -  /* used when sending shading calibration data */ -  dev->calib_pixels = settings.pixels; -    dev->calib_channels = dev->session.params.channels; -    if (!dev->model->is_cis) { -      dev->calib_channels = 3; +    unsigned calib_lines = +            static_cast<unsigned>(dev->model->y_size_calib_mm * resolution / MM_PER_INCH); + +    ScanSession session; +    session.params.xres = resolution; +    session.params.yres = resolution; +    session.params.startx = 0; +    session.params.starty = 0; +    session.params.pixels = pixels; +    session.params.lines = calib_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::IGNORE_COLOR_OFFSET | +                           ScanFlag::IGNORE_STAGGER_OFFSET; +    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { +        session.params.flags |= ScanFlag::USE_XPA;      } +    compute_session(dev, session, calib_sensor); + +    dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); + +    dev->calib_session = session;    /* no shading */ -    dev->reg.find_reg(0x01).value &= ~REG_0x01_DVDSET;      dev->reg.find_reg(0x02).value |= REG_0x02_ACDCDIS;	/* ease backtracking */ -    dev->reg.find_reg(0x02).value &= ~(REG_0x02_FASTFED | REG_0x02_AGOHOME); -    dev->reg.find_reg(0x05).value &= ~REG_0x05_GMMENB; +    dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED;    sanei_genesys_set_motor_power(dev->reg, false); - -  /* TODO another flag to setup regs ? */ -  /* enforce needed LINCNT, getting rid of extra lines for color reordering */ -    if (!dev->model->is_cis) { -        dev->reg.set24(REG_LINCNT, dev->calib_lines); -    } else { -        dev->reg.set24(REG_LINCNT, dev->calib_lines * 3); -    } - -  /* copy reg to calib_reg */ -  dev->calib_reg = dev->reg; - -  DBG(DBG_info, "%s:\n\tdev->settings.xres=%d\n\tdev->settings.yres=%d\n", __func__, -      dev->settings.xres, dev->settings.yres);  }  bool CommandSetGl646::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const @@ -1745,109 +1852,6 @@ bool CommandSetGl646::needs_home_before_init_regs_for_scan(Genesys_Device* dev)  }  /** - * set up registers for the actual scan. The scan's parameters are given - * through the device settings. It allocates the scan buffers. - */ -void CommandSetGl646::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const -{ -    DBG_HELPER(dbg); - -    debug_dump(DBG_info, dev->settings); - -    ScanSession session = calculate_scan_session(dev, sensor, dev->settings); - -    init_regs_for_scan_session(dev, sensor, &dev->reg, session); - -  /* gamma is only enabled at final scan time */ -    if (dev->settings.depth < 16) { -        dev->reg.find_reg(0x05).value |= REG_0x05_GMMENB; -    } -} - -/** - * set up registers for the actual scan. The scan's parameters are given - * through the device settings. It allocates the scan buffers. - * @param dev scanner's device - * @param regs     registers to set up - * @param settings settings of scan - * @param split true if move to scan area is split from scan, false is - *              scan first moves to area - * @param xcorrection take x geometry correction into account (fixed and detected offsets) - * @param ycorrection take y geometry correction into account - */ -static void setup_for_scan(Genesys_Device* dev, -                           const Genesys_Sensor& sensor, -                           Genesys_Register_Set*regs, -                           Genesys_Settings settings, -                           bool split, -                           bool xcorrection, -                           bool ycorrection, -                           bool reverse) -{ -    DBG_HELPER(dbg); - -    debug_dump(DBG_info, dev->settings); - -    // compute distance to move -    float move = 0; -    // XXX STEF XXX MD5345 -> optical_ydpi, other base_ydpi => half/full step ? */ -    if (!split) { -        if (!dev->model->is_sheetfed) { -            if (ycorrection) { -                move = static_cast<float>(dev->model->y_offset); -            } - -            // add tl_y to base movement -        } -        move += static_cast<float>(settings.tl_y); - -        if (move < 0) { -            DBG(DBG_error, "%s: overriding negative move value %f\n", __func__, move); -            move = 0; -        } -    } -    move = static_cast<float>((move * dev->motor.optical_ydpi) / MM_PER_INCH); -    DBG(DBG_info, "%s: move=%f steps\n", __func__, move); - -    float start = static_cast<float>(settings.tl_x); -    if (xcorrection) { -        if (settings.scan_method == ScanMethod::FLATBED) { -            start += static_cast<float>(dev->model->x_offset); -        } else { -            start += static_cast<float>(dev->model->x_offset_ta); -        } -    } -    start = static_cast<float>((start * sensor.optical_res) / MM_PER_INCH); - -    ScanSession session; -    session.params.xres = settings.xres; -    session.params.yres = settings.yres; -    session.params.startx = static_cast<unsigned>(start); -    session.params.starty = static_cast<unsigned>(move); -    session.params.pixels = settings.pixels; -    session.params.requested_pixels = settings.requested_pixels; -    session.params.lines = settings.lines; -    session.params.depth = settings.depth; -    session.params.channels = settings.get_channels(); -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = settings.scan_mode; -    session.params.color_filter = settings.color_filter; -    session.params.flags = ScanFlag::NONE; -    if (settings.scan_method == ScanMethod::TRANSPARENCY) { -        session.params.flags |= ScanFlag::USE_XPA; -    } -    if (xcorrection) { -        session.params.flags |= ScanFlag::USE_XCORRECTION; -    } -    if (reverse) { -        session.params.flags |= ScanFlag::REVERSE; -    } -    compute_session(dev, session, sensor); - -    dev->cmd_set->init_regs_for_scan_session(dev, sensor, regs, session); -} - -/**   * this function send gamma table to ASIC   */  void CommandSetGl646::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const @@ -1857,9 +1861,7 @@ void CommandSetGl646::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor    int address;    int bits; -  /* gamma table size */ -  if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) -    { +     if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) {        size = 16384;        bits = 14;      } @@ -1903,45 +1905,42 @@ SensorExposure CommandSetGl646::led_calibration(Genesys_Device* dev, const Genes  {      DBG_HELPER(dbg);      (void) regs; -  int total_size;    unsigned int i, j;    int val;    int avg[3], avga, avge;    int turn;    uint16_t expr, expg, expb; -  Genesys_Settings settings; -  SANE_Int resolution;      unsigned channels = dev->settings.get_channels(); -  /* get led calibration resolution */ -  if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) -    { -      settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; -    } -  else -    { -      settings.scan_mode = ScanColorMode::GRAY; +    ScanColorMode scan_mode = ScanColorMode::COLOR_SINGLE_PASS; +    if (dev->settings.scan_mode != ScanColorMode::COLOR_SINGLE_PASS) { +        scan_mode = ScanColorMode::GRAY;      } -  resolution = get_closest_resolution(dev->model->sensor_id, sensor.optical_res, channels); -  /* offset calibration is always done in color mode */ -    settings.scan_method = dev->model->default_method; -  settings.xres = resolution; -  settings.yres = resolution; -  settings.tl_x = 0; -  settings.tl_y = 0; -    settings.pixels = (sensor.sensor_pixels * resolution) / sensor.optical_res; -    settings.requested_pixels = settings.pixels; -  settings.lines = 1; -  settings.depth = 16; -  settings.color_filter = ColorFilter::RED; +    // offset calibration is always done in color mode +    unsigned pixels = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH; -  settings.disable_interpolation = 0; -  settings.threshold = 0; +    ScanSession session; +    session.params.xres = sensor.full_resolution; +    session.params.yres = sensor.full_resolution; +    session.params.startx = 0; +    session.params.starty = 0; +    session.params.pixels = pixels; +    session.params.lines = 1; +    session.params.depth = 16; +    session.params.channels = channels; +    session.params.scan_method = dev->settings.scan_method; +    session.params.scan_mode = scan_mode; +    session.params.color_filter = ColorFilter::RED; +    session.params.flags = ScanFlag::DISABLE_SHADING; +    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { +        session.params.flags |= ScanFlag::USE_XPA; +    } +    compute_session(dev, session, sensor); -  /* colors * bytes_per_color * scan lines */ -  total_size = settings.pixels * channels * 2 * 1; +    // colors * bytes_per_color * scan lines +    unsigned total_size = pixels * channels * 2 * 1;    std::vector<uint8_t> line(total_size); @@ -1968,38 +1967,34 @@ SensorExposure CommandSetGl646::led_calibration(Genesys_Device* dev, const Genes        DBG(DBG_info, "%s: starting first line reading\n", __func__); -        simple_scan(dev, calib_sensor, settings, false, true, false, line, "led_calibration"); +        dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); +        simple_scan(dev, calib_sensor, session, false, line, "led_calibration");          if (is_testing_mode()) {              return calib_sensor.exposure;          } -      if (DBG_LEVEL >= DBG_data) -	{ -          char fn[30]; -            std::snprintf(fn, 30, "gl646_led_%02d.pnm", turn); -          sanei_genesys_write_pnm_file(fn, line.data(), 16, channels, settings.pixels, 1); -	} +        if (dbg_log_image_data()) { +            char fn[30]; +            std::snprintf(fn, 30, "gl646_led_%02d.tiff", turn); +            write_tiff_file(fn, line.data(), 16, channels, pixels, 1); +        }          acceptable = true;        for (j = 0; j < channels; j++)  	{  	  avg[j] = 0; -	  for (i = 0; i < settings.pixels; i++) -	    { -	      if (dev->model->is_cis) -		val = -		  line[i * 2 + j * 2 * settings.pixels + 1] * 256 + -		  line[i * 2 + j * 2 * settings.pixels]; -	      else -		val = -		  line[i * 2 * channels + 2 * j + 1] * 256 + -		  line[i * 2 * channels + 2 * j]; -	      avg[j] += val; +            for (i = 0; i < pixels; i++) { +                if (dev->model->is_cis) { +                    val = line[i * 2 + j * 2 * pixels + 1] * 256 + line[i * 2 + j * 2 * pixels]; +                } else { +                    val = line[i * 2 * channels + 2 * j + 1] * 256 + line[i * 2 * channels + 2 * j]; +                } +            avg[j] += val;  	    } -	  avg[j] /= settings.pixels; +      avg[j] /= pixels;  	}        DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]); @@ -2088,31 +2083,40 @@ 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 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; -  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.tl_x = 0; -  settings.tl_y = 0; -    settings.pixels = (calib_sensor.sensor_pixels * resolution) / calib_sensor.optical_res; -    settings.requested_pixels = settings.pixels; -  settings.lines = CALIBRATION_LINES; -  settings.depth = 8; -  settings.color_filter = ColorFilter::RED; - -  settings.disable_interpolation = 0; -  settings.threshold = 0; + +    // FIXME: maybe reuse `sensor` +    const auto& calib_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, 3, +                                                         ScanMethod::FLATBED); +    black_pixels = (calib_sensor.black_pixels * sensor.full_resolution) / calib_sensor.full_resolution; + +    unsigned pixels = dev->model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH; +    unsigned lines = CALIBRATION_LINES; + +    if (dev->model->is_cis) { +        lines = ((lines + 2) / 3) * 3; +    } + +    ScanSession session; +    session.params.xres = sensor.full_resolution; +    session.params.yres = sensor.full_resolution; +    session.params.startx = 0; +    session.params.starty = 0; +    session.params.pixels = pixels; +    session.params.lines = lines; +    session.params.depth = 8; +    session.params.channels = 3; +    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 = ScanFlag::DISABLE_SHADING; +    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { +        session.params.flags |= ScanFlag::USE_XPA; +    } +    compute_session(dev, session, calib_sensor);    /* scan first line of data with no gain */    dev->frontend.set_gain(0, 0); @@ -2129,27 +2133,24 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor&        dev->frontend.set_offset(0, bottom);        dev->frontend.set_offset(1, bottom);        dev->frontend.set_offset(2, bottom); -        simple_scan(dev, calib_sensor, settings, false, true, false, line, -                    "ad_fe_offset_calibration"); + +        dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); +        simple_scan(dev, calib_sensor, session, false, line, "ad_fe_offset_calibration");          if (is_testing_mode()) {              return;          } -      if (DBG_LEVEL >= DBG_data) -	{ -          char title[30]; -          std::snprintf(title, 30, "gl646_offset%03d.pnm", static_cast<int>(bottom)); -          sanei_genesys_write_pnm_file (title, line.data(), 8, channels, -					settings.pixels, settings.lines); -	} +        if (dbg_log_image_data()) { +            char title[30]; +            std::snprintf(title, 30, "gl646_offset%03d.tiff", static_cast<int>(bottom)); +            write_tiff_file(title, line.data(), 8, channels, pixels, lines); +        }        min = 0; -      for (y = 0; y < settings.lines; y++) -	{ -	  for (x = 0; x < black_pixels; x++) -	    { -	      adr = (x + y * settings.pixels) * channels; +        for (unsigned y = 0; y < lines; y++) { +            for (unsigned x = 0; x < black_pixels; x++) { +                adr = (x + y * pixels) * channels;  	      if (line[adr] > min)  		min = line[adr];  	      if (line[adr + 1] > min) @@ -2159,7 +2160,7 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor&  	    }  	} -      DBG(DBG_io2, "%s: pass=%d, min=%d\n", __func__, pass, min); +      DBG(DBG_info, "%s: pass=%d, min=%d\n", __func__, pass, min);        bottom++;      }    while (pass < 128 && min == 0); @@ -2187,9 +2188,7 @@ 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;    int top, bottom, black_pixels; @@ -2198,32 +2197,38 @@ void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sens          return;      } -  DBG(DBG_proc, "%s: start\n", __func__); // TODO -    /* 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); - -    const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3, ScanMethod::FLATBED); -    black_pixels = (calib_sensor.black_pixels * resolution) / calib_sensor.optical_res; +    unsigned resolution = dev->settings.xres; +    unsigned channels = 3; -  DBG(DBG_io2, "%s: black_pixels=%d\n", __func__, black_pixels); +    const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, +                                                         ScanMethod::FLATBED); +    black_pixels = (calib_sensor.black_pixels * resolution) / calib_sensor.full_resolution; -    settings.scan_method = dev->model->default_method; -  settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; -  settings.xres = resolution; -  settings.yres = resolution; -  settings.tl_x = 0; -  settings.tl_y = 0; -    settings.pixels = (calib_sensor.sensor_pixels * resolution) / calib_sensor.optical_res; -    settings.requested_pixels = settings.pixels; -  settings.lines = CALIBRATION_LINES; -  settings.depth = 8; -  settings.color_filter = ColorFilter::RED; +    unsigned pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; +    unsigned lines = CALIBRATION_LINES; +    if (dev->model->is_cis) { +        lines = ((lines + 2) / 3) * 3; +    } -  settings.disable_interpolation = 0; -  settings.threshold = 0; +    ScanSession session; +    session.params.xres = resolution; +    session.params.yres = resolution; +    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 = ColorFilter::RED; +    session.params.flags = ScanFlag::DISABLE_SHADING; +    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { +        session.params.flags |= ScanFlag::USE_XPA; +    } +    compute_session(dev, session, sensor);    /* scan first line of data with no gain, but with offset from     * last calibration */ @@ -2239,38 +2244,32 @@ void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sens    std::vector<uint8_t> first_line, second_line; -    simple_scan(dev, calib_sensor, settings, false, true, false, first_line, -                "offset_first_line"); +    dev->cmd_set->init_regs_for_scan_session(dev, sensor, &dev->reg, session); +    simple_scan(dev, calib_sensor, session, false, first_line, "offset_first_line"); -  if (DBG_LEVEL >= DBG_data) -    { -      char title[30]; -        std::snprintf(title, 30, "gl646_offset%03d.pnm", bottom); -      sanei_genesys_write_pnm_file(title, first_line.data(), 8, channels, -                                   settings.pixels, settings.lines); +    if (dbg_log_image_data()) { +        char title[30]; +        std::snprintf(title, 30, "gl646_offset%03d.tiff", bottom); +        write_tiff_file(title, first_line.data(), 8, channels, pixels, lines);      } -  bottomavg = dark_average(first_line.data(), settings.pixels, settings.lines, channels, -                           black_pixels); -  DBG(DBG_io2, "%s: bottom avg=%d\n", __func__, bottomavg); +    bottomavg = dark_average(first_line.data(), pixels, lines, channels, black_pixels); +    DBG(DBG_info, "%s: bottom avg=%d\n", __func__, bottomavg);    /* now top value */    top = 231;    dev->frontend.set_offset(0, top);    dev->frontend.set_offset(1, top);    dev->frontend.set_offset(2, top); -    simple_scan(dev, calib_sensor, settings, false, true, false, second_line, -                "offset_second_line"); +    dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); +    simple_scan(dev, calib_sensor, session, false, second_line, "offset_second_line"); -  if (DBG_LEVEL >= DBG_data) -    { -      char title[30]; -        std::snprintf(title, 30, "gl646_offset%03d.pnm", top); -      sanei_genesys_write_pnm_file (title, second_line.data(), 8, channels, -				    settings.pixels, settings.lines); +    if (dbg_log_image_data()) { +        char title[30]; +        std::snprintf(title, 30, "gl646_offset%03d.tiff", top); +        write_tiff_file(title, second_line.data(), 8, channels, pixels, lines);      } -  topavg = dark_average(second_line.data(), settings.pixels, settings.lines, channels, -                        black_pixels); -  DBG(DBG_io2, "%s: top avg=%d\n", __func__, topavg); +    topavg = dark_average(second_line.data(), pixels, lines, channels, black_pixels); +    DBG(DBG_info, "%s: top avg=%d\n", __func__, topavg);      if (is_testing_mode()) {          return; @@ -2287,20 +2286,17 @@ void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sens        dev->frontend.set_offset(2, (top + bottom) / 2);          // scan with no move -        simple_scan(dev, calib_sensor, settings, false, true, false, second_line, +        dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); +        simple_scan(dev, calib_sensor, session, false, second_line,                      "offset_calibration_i"); -      if (DBG_LEVEL >= DBG_data) -	{ -          char title[30]; -            std::snprintf(title, 30, "gl646_offset%03d.pnm", dev->frontend.get_offset(1)); -          sanei_genesys_write_pnm_file (title, second_line.data(), 8, channels, -					settings.pixels, settings.lines); -	} +        if (dbg_log_image_data()) { +            char title[30]; +            std::snprintf(title, 30, "gl646_offset%03d.tiff", dev->frontend.get_offset(1)); +            write_tiff_file(title, second_line.data(), 8, channels, pixels, lines); +        } -      avg = -        dark_average (second_line.data(), settings.pixels, settings.lines, channels, -		      black_pixels); +        avg = dark_average(second_line.data(), pixels, lines, channels, black_pixels);        DBG(DBG_info, "%s: avg=%d offset=%d\n", __func__, avg, dev->frontend.get_offset(1));        /* compute new boundaries */ @@ -2322,102 +2318,6 @@ void CommandSetGl646::offset_calibration(Genesys_Device* dev, const Genesys_Sens        dev->frontend.get_offset(2));  } -/** @brief gain calibration for Analog Device frontends - * Alternative coarse gain calibration - */ -static void ad_fe_coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                          Genesys_Register_Set& regs, int dpi) -{ -    DBG_HELPER(dbg); -    (void) sensor; -    (void) regs; - -  unsigned int i, channels, val; -  unsigned int size, count, resolution, pass; -  float average; -  Genesys_Settings settings; -  char title[32]; - -  /* 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); - -  settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; - -    settings.scan_method = dev->model->default_method; -  settings.xres = resolution; -  settings.yres = resolution; -  settings.tl_x = 0; -  settings.tl_y = 0; -    settings.pixels = (calib_sensor.sensor_pixels * resolution) / calib_sensor.optical_res; -    settings.requested_pixels = settings.pixels; -  settings.lines = CALIBRATION_LINES; -  settings.depth = 8; -  settings.color_filter = ColorFilter::RED; - -  settings.disable_interpolation = 0; -  settings.threshold = 0; - -  size = channels * settings.pixels * settings.lines; - -  /* start gain value */ -  dev->frontend.set_gain(0, 1); -  dev->frontend.set_gain(1, 1); -  dev->frontend.set_gain(2, 1); - -  average = 0; -  pass = 0; - -  std::vector<uint8_t> line; - -    // loop until each channel raises to acceptable level -    while ((average < calib_sensor.gain_white_ref) && (pass < 30)) { -        // scan with no move -        simple_scan(dev, calib_sensor, settings, false, true, false, line, -                    "ad_fe_coarse_gain_calibration"); - -      /* log scanning data */ -      if (DBG_LEVEL >= DBG_data) -	{ -            std::sprintf(title, "gl646_alternative_gain%02d.pnm", pass); -          sanei_genesys_write_pnm_file(title, line.data(), 8, channels, settings.pixels, -                                       settings.lines); -	} -      pass++; - -      /* computes white average */ -      average = 0; -      count = 0; -      for (i = 0; i < size; i++) -	{ -	  val = line[i]; -	  average += val; -	  count++; -	} -      average = average / count; - -        uint8_t gain0 = dev->frontend.get_gain(0); -        // adjusts gain for the channel -        if (average < calib_sensor.gain_white_ref) { -            gain0 += 1; -        } - -        dev->frontend.set_gain(0, gain0); -        dev->frontend.set_gain(1, gain0); -        dev->frontend.set_gain(2, gain0); - -      DBG(DBG_proc, "%s: average = %.2f, gain = %d\n", __func__, average, gain0); -    } - -  DBG(DBG_info, "%s: gains=(%d,%d,%d)\n", __func__, -      dev->frontend.get_gain(0), -      dev->frontend.get_gain(1), -      dev->frontend.get_gain(2)); -} -  /**   * Alternative coarse gain calibration   * this on uses the settings from offset_calibration. First scan moves so @@ -2430,76 +2330,67 @@ void CommandSetGl646::coarse_gain_calibration(Genesys_Device* dev, const Genesys  {      DBG_HELPER(dbg);      (void) dpi; +    (void) sensor; +    (void) regs; -  unsigned int i, j, k, channels, val, maximum, idx; -  unsigned int count, resolution, pass;    float average[3]; -  Genesys_Settings settings;    char title[32]; -    if (dev->model->sensor_id == SensorId::CIS_XP200) { -      return ad_fe_coarse_gain_calibration(dev, sensor, regs, sensor.optical_res); -    } -    /* setup for a RGB scan, one full sensor's width line */    /* resolution is the one from the final scan          */ -  channels = 3; +    unsigned 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.tl_y = 0; -  if (settings.scan_method == ScanMethod::FLATBED) -    { -      settings.tl_x = 0; -        settings.pixels = (calib_sensor.sensor_pixels * resolution) / calib_sensor.optical_res; +    unsigned pixels = 0; +    float start = 0; +    if (dev->settings.scan_method == ScanMethod::FLATBED) { +        pixels = dev->model->x_size_calib_mm * dev->settings.xres / MM_PER_INCH; +    } else { +        start = dev->model->x_offset_ta; +        pixels = static_cast<unsigned>( +                              (dev->model->x_size_ta * 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); + +    unsigned lines = CALIBRATION_LINES; +    // round up to multiple of 3 in case of CIS scanner +    if (dev->model->is_cis) { +        lines = ((lines + 2) / 3) * 3;      } -    settings.requested_pixels = settings.pixels; -  settings.lines = CALIBRATION_LINES; -  settings.depth = 8; -  settings.color_filter = ColorFilter::RED; -  settings.disable_interpolation = 0; -  settings.threshold = 0; +    start = static_cast<float>((start * dev->settings.xres) / MM_PER_INCH); + +    ScanSession session; +    session.params.xres = dev->settings.xres; +    session.params.yres = dev->settings.xres; +    session.params.startx = static_cast<unsigned>(start); +    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 = ColorFilter::RED; +    session.params.flags = ScanFlag::DISABLE_SHADING; +    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { +        session.params.flags |= ScanFlag::USE_XPA; +    } +    compute_session(dev, session, calib_sensor);    /* start gain value */    dev->frontend.set_gain(0, 1);    dev->frontend.set_gain(1, 1);    dev->frontend.set_gain(2, 1); -  if (channels > 1) -    { -      average[0] = 0; -      average[1] = 0; -      average[2] = 0; -      idx = 0; -    } -  else -    { -      average[0] = 255; -      average[1] = 255; -      average[2] = 255; -        switch (dev->settings.color_filter) { -            case ColorFilter::RED: idx = 0; break; -            case ColorFilter::GREEN: idx = 1; break; -            case ColorFilter::BLUE: idx = 2; break; -            default: idx = 0; break; // should not happen -        } -      average[idx] = 0; -    } -  pass = 0; +    average[0] = 0; +    average[1] = 0; +    average[2] = 0; + +    unsigned pass = 0;    std::vector<uint8_t> line; @@ -2509,75 +2400,60 @@ void CommandSetGl646::coarse_gain_calibration(Genesys_Device* dev, const Genesys              (average[2] < calib_sensor.gain_white_ref)) && (pass < 30))      {          // scan with no move -        simple_scan(dev, calib_sensor, settings, false, true, false, line, -                    "coarse_gain_calibration"); +        dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, &dev->reg, session); +        simple_scan(dev, calib_sensor, session, false, line, "coarse_gain_calibration"); -      /* log scanning data */ -      if (DBG_LEVEL >= DBG_data) -	{ -          std::sprintf(title, "gl646_gain%02d.pnm", pass); -          sanei_genesys_write_pnm_file(title, line.data(), 8, channels, settings.pixels, -                                       settings.lines); -	} -      pass++; - -      /* 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 (k = idx; k < idx + channels; k++) -	{ -	  /* we find the maximum white value, so we can deduce a threshold -	     to average white values */ -	  maximum = 0; -	  for (i = 0; i < settings.lines; i++) -	    { -	      for (j = 0; j < settings.pixels; j++) -		{ -		  val = line[i * channels * settings.pixels + j + k]; -		  if (val > maximum) -		    maximum = val; -		} -	    } +        if (dbg_log_image_data()) { +            std::sprintf(title, "gl646_gain%02d.tiff", pass); +            write_tiff_file(title, line.data(), 8, channels, pixels, lines); +        } +        pass++; + +        // 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 k = 0; k < channels; k++) { + +            // we find the maximum white value, so we can deduce a threshold +            // to average white values +            unsigned maximum = 0; +            for (unsigned i = 0; i < lines; i++) { +                for (unsigned j = 0; j < pixels; j++) { +                    unsigned val = line[i * channels * pixels + j + k]; +                    maximum = std::max(maximum, val); +                } +            } -	  /* threshold */              maximum = static_cast<int>(maximum * 0.9); -	  /* computes white average */ -	  average[k] = 0; -	  count = 0; -	  for (i = 0; i < settings.lines; i++) -	    { -	      for (j = 0; j < settings.pixels; j++) -		{ -		  /* averaging only white points allow us not to care about dark margins */ -		  val = line[i * channels * settings.pixels + j + k]; -		  if (val > maximum) -		    { -		      average[k] += val; -		      count++; -		    } -		} -	    } -	  average[k] = average[k] / count; - -	  /* adjusts gain for the channel */ -          if (average[k] < calib_sensor.gain_white_ref) -            dev->frontend.set_gain(k, dev->frontend.get_gain(k) + 1); +            // computes white average +            average[k] = 0; +            unsigned count = 0; +            for (unsigned i = 0; i < lines; i++) { +                for (unsigned j = 0; j < pixels; j++) { +                    // averaging only white points allow us not to care about dark margins +                    unsigned val = line[i * channels * pixels + j + k]; +                    if (val > maximum) { +                        average[k] += val; +                        count++; +                    } +                } +            } +            average[k] = average[k] / count; -	  DBG(DBG_proc, "%s: channel %d, average = %.2f, gain = %d\n", __func__, k, average[k], -              dev->frontend.get_gain(k)); -	} -    } +            // adjusts gain for the channel +            if (average[k] < calib_sensor.gain_white_ref) { +                dev->frontend.set_gain(k, dev->frontend.get_gain(k) + 1); +            } -    if (channels < 3) { -        dev->frontend.set_gain(1, dev->frontend.get_gain(0)); -        dev->frontend.set_gain(2, dev->frontend.get_gain(0)); +            DBG(DBG_info, "%s: channel %d, average = %.2f, gain = %d\n", __func__, k, average[k], +                dev->frontend.get_gain(k)); +        }      } -  DBG(DBG_info, "%s: gains=(%d,%d,%d)\n", __func__, -      dev->frontend.get_gain(0), -      dev->frontend.get_gain(1), -      dev->frontend.get_gain(2)); +    DBG(DBG_info, "%s: gains=(%d,%d,%d)\n", __func__, +        dev->frontend.get_gain(0), +        dev->frontend.get_gain(1), +        dev->frontend.get_gain(2));  }  /** @@ -2585,46 +2461,43 @@ void CommandSetGl646::coarse_gain_calibration(Genesys_Device* dev, const Genesys   *   */  void CommandSetGl646::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                           Genesys_Register_Set* local_reg, int* channels, -                                           int* total_size) const +                                           Genesys_Register_Set* local_reg) const  {      DBG_HELPER(dbg);      (void) sensor; -  Genesys_Settings settings; -  int resolution, 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); -  /* set up for a half width 2 lines gray scan without moving */ -    settings.scan_method = dev->model->default_method; -  settings.scan_mode = ScanColorMode::GRAY; -  settings.xres = resolution; -  settings.yres = resolution; -  settings.tl_x = 0; -  settings.tl_y = 0; -    settings.pixels = (local_sensor.sensor_pixels * resolution) / local_sensor.optical_res; -    settings.requested_pixels = settings.pixels; -  settings.lines = 2; -  settings.depth = 8; -  settings.color_filter = ColorFilter::RED; - -  settings.disable_interpolation = 0; -  settings.threshold = 0; - -    // setup for scan -    setup_for_scan(dev, local_sensor, &dev->reg, settings, true, false, false, false); +    // set up for a full width 2 lines gray scan without moving +    unsigned pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; -  /* we are not going to move, so clear these bits */ -    dev->reg.find_reg(0x02).value &= ~(REG_0x02_FASTFED | REG_0x02_AGOHOME); +    ScanSession session; +    session.params.xres = resolution; +    session.params.yres = resolution; +    session.params.startx = 0; +    session.params.starty = 0; +    session.params.pixels = pixels; +    session.params.lines = 2; +    session.params.depth = dev->model->bpp_gray_values.front(); +    session.params.channels = 1; +    session.params.scan_method = dev->settings.scan_method; +    session.params.scan_mode = ScanColorMode::GRAY; +    session.params.color_filter =  ColorFilter::RED; +    session.params.flags = ScanFlag::DISABLE_SHADING | +                           ScanFlag::DISABLE_GAMMA; +    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY) { +        session.params.flags |= ScanFlag::USE_XPA; +    } +    compute_session(dev, session, local_sensor); + +    dev->cmd_set->init_regs_for_scan_session(dev, local_sensor, &dev->reg, session); -  /* don't enable any correction for this scan */ -    dev->reg.find_reg(0x01).value &= ~REG_0x01_DVDSET; +  /* we are not going to move, so clear these bits */ +    dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED;    /* copy to local_reg */    *local_reg = dev->reg; @@ -2632,66 +2505,8 @@ void CommandSetGl646::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Se    /* turn off motor during this scan */    sanei_genesys_set_motor_power(*local_reg, false); -  /* returned value to higher level warmup function */ -  *channels = 1; -    lines = local_reg->get24(REG_LINCNT) + 1; -  *total_size = lines * settings.pixels; -      // now registers are ok, write them to scanner -    gl646_set_fe(dev, local_sensor, AFE_SET, settings.xres); -    dev->interface->write_registers(*local_reg); -} - - -/* - * this function moves head without scanning, forward, then backward - * so that the head goes to park position. - * as a by-product, also check for lock - */ -static void gl646_repark_head(Genesys_Device* dev) -{ -    DBG_HELPER(dbg); -  Genesys_Settings settings; -  unsigned int expected, steps; - -    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.yres = settings.xres; -  settings.tl_x = 0; -  settings.tl_y = 5; -  settings.pixels = 600; -    settings.requested_pixels = settings.pixels; -  settings.lines = 4; -  settings.depth = 8; -  settings.color_filter = ColorFilter::RED; - -  settings.disable_interpolation = 0; -  settings.threshold = 0; - -    const auto& sensor = sanei_genesys_find_sensor(dev, settings.xres, 3, -                                                   dev->model->default_method); - -    setup_for_scan(dev, sensor, &dev->reg, settings, false, false, false, false); - -  /* TODO seems wrong ... no effective scan */ -    regs_set_optical_off(dev->model->asic_type, dev->reg); - -    dev->interface->write_registers(dev->reg); - -    // start scan -    dev->cmd_set->begin_scan(dev, sensor, &dev->reg, true); - -    expected = dev->reg.get24(REG_FEEDL); -  do -    { -        dev->interface->sleep_ms(100); -        sanei_genesys_read_feed_steps (dev, &steps); -    } -  while (steps < expected); - -    // toggle motor flag, put an huge step number and redo move backward -    dev->cmd_set->move_back_home(dev, 1); +    gl646_set_fe(dev, local_sensor, AFE_SET, session.params.xres);  }  /* * @@ -2731,10 +2546,11 @@ void CommandSetGl646::init(Genesys_Device* dev) const        gl646_init_regs (dev);          // Init shading data -        sanei_genesys_init_shading_data(dev, sensor, sensor.sensor_pixels); +        sanei_genesys_init_shading_data(dev, sensor, +                                        dev->model->x_size_calib_mm * sensor.full_resolution / +                                            MM_PER_INCH); -      /* initial calibration reg values */ -      dev->calib_reg = dev->reg; +        dev->initial_regs = dev->reg;      }      // execute physical unit init only if cold @@ -2787,7 +2603,7 @@ void CommandSetGl646::init(Genesys_Device* dev) const      if (dev->model->gpio_id != GpioId::HP3670 &&          dev->model->gpio_id != GpioId::HP2400)      { -      switch (sensor.optical_res) +      switch (sensor.full_resolution)  	{  	case 600:  	  addr = 0x08200; @@ -2810,9 +2626,6 @@ void CommandSetGl646::init(Genesys_Device* dev) const          } catch (...) {              dev->interface->bulk_read_data(0x45, dev->control, len);          } -        DBG(DBG_info, "%s: control read=0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", __func__, -            dev->control[0], dev->control[1], dev->control[2], dev->control[3], dev->control[4], -            dev->control[5]);        sanei_usb_set_timeout (30 * 1000);      }    else @@ -2828,104 +2641,44 @@ void CommandSetGl646::init(Genesys_Device* dev) const    /* ensure head is correctly parked, and check lock */      if (!dev->model->is_sheetfed) { -      if (dev->model->flags & GENESYS_FLAG_REPARK) -	{ -            // FIXME: if repark fails, we should print an error message that the scanner is locked and -            // the user should unlock the lock. We should also rethrow with SANE_STATUS_JAMMED -            gl646_repark_head(dev); -	} -      else -    { -            move_back_home(dev, true); -	} +        move_back_home(dev, true);      }    /* here session and device are initialized */      dev->already_initialized = true;  } -void CommandSetGl646::move_to_ta(Genesys_Device* dev) const -{ -    DBG_HELPER(dbg); - -    simple_move(dev, static_cast<int>(dev->model->y_offset_sensor_to_ta)); -} - - -/** - * Does a simple scan: ie no line reordering and avanced data buffering and - * shading correction. Memory for data is allocated in this function - * and must be freed by caller. - * @param dev device of the scanner - * @param settings parameters of the scan - * @param move true if moving during scan - * @param forward true if moving forward during scan - * @param shading true to enable shading correction - * @param data pointer for the data - */  static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, -                        Genesys_Settings settings, bool move, bool forward, -                        bool shading, std::vector<uint8_t>& data, -                        const char* scan_identifier) +                        const ScanSession& session, bool move, +                        std::vector<uint8_t>& data, const char* scan_identifier)  { -    DBG_HELPER_ARGS(dbg, "move=%d, forward=%d, shading=%d", move, forward, shading); -  unsigned int size, lines, x, y, bpp; -    bool split; - -  /* round up to multiple of 3 in case of CIS scanner */ -    if (dev->model->is_cis) { -      settings.lines = ((settings.lines + 2) / 3) * 3; +    unsigned lines = session.output_line_count; +    if (!dev->model->is_cis) { +        lines++;      } -  /* setup for move then scan */ -    split = !(move && settings.tl_y > 0); -    setup_for_scan(dev, sensor, &dev->reg, settings, split, false, false, !forward); +    std::size_t size = lines * session.params.pixels; +    unsigned bpp = session.params.depth == 16 ? 2 : 1; -  /* allocate memory fo scan : LINCNT may have been adjusted for CCD reordering */ -    if (dev->model->is_cis) { -        lines = dev->reg.get24(REG_LINCNT) / 3; -    } else { -        lines = dev->reg.get24(REG_LINCNT) + 1; -    } -  size = lines * settings.pixels; -    if (settings.depth == 16) { -        bpp = 2; -    } else { -        bpp = 1; -    } -    size *= bpp * settings.get_channels(); +    size *= bpp * session.params.channels;    data.clear();    data.resize(size); -  DBG(DBG_io, "%s: allocated %d bytes of memory for %d lines\n", __func__, size, lines); - -  /* put back real line number in settings */ -  settings.lines = lines; -      // initialize frontend -    gl646_set_fe(dev, sensor, AFE_SET, settings.xres); - -  /* no shading correction and not watch dog for simple scan */ -    dev->reg.find_reg(0x01).value &= ~(REG_0x01_DVDSET | REG_0x01_DOGENB); -    if (shading) { -      dev->reg.find_reg(0x01).value |= REG_0x01_DVDSET; -    } +    gl646_set_fe(dev, sensor, AFE_SET, session.params.xres); -  /* enable gamma table for the scan */ -    dev->reg.find_reg(0x05).value |= REG_0x05_GMMENB; +    // no watch dog for simple scan +    dev->reg.find_reg(0x01).value &= ~REG_0x01_DOGENB;    /* one table movement for simple scan */      dev->reg.find_reg(0x02).value &= ~REG_0x02_FASTFED;      if (!move) { -      sanei_genesys_set_motor_power(dev->reg, false); - -      /* no automatic go home if no movement */ -        dev->reg.find_reg(0x02).value &= ~REG_0x02_AGOHOME; +        sanei_genesys_set_motor_power(dev->reg, false);      }    /* no automatic go home when using XPA */ -  if (settings.scan_method == ScanMethod::TRANSPARENCY) { +    if (session.params.scan_method == ScanMethod::TRANSPARENCY) {          dev->reg.find_reg(0x02).value &= ~REG_0x02_AGOHOME;      } @@ -2946,46 +2699,38 @@ static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,      sanei_genesys_read_data_from_scanner(dev, data.data(), size);    /* in case of CIS scanner, we must reorder data */ -    if (dev->model->is_cis && settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) { -      /* alloc one line sized working buffer */ -      std::vector<uint8_t> buffer(settings.pixels * 3 * bpp); - -      /* reorder one line of data and put it back to buffer */ -      if (bpp == 1) -	{ -	  for (y = 0; y < lines; y++) -	    { -	      /* reorder line */ -	      for (x = 0; x < settings.pixels; x++) -		{ -                  buffer[x * 3] = data[y * settings.pixels * 3 + x]; -                  buffer[x * 3 + 1] = data[y * settings.pixels * 3 + settings.pixels + x]; -                  buffer[x * 3 + 2] = data[y * settings.pixels * 3 + 2 * settings.pixels + x]; -		} -	      /* copy line back */ -              memcpy (data.data() + settings.pixels * 3 * y, buffer.data(), -		      settings.pixels * 3); -	    } -	} -      else -	{ -	  for (y = 0; y < lines; y++) -	    { -	      /* reorder line */ -	      for (x = 0; x < settings.pixels; x++) -		{ -                  buffer[x * 6] = data[y * settings.pixels * 6 + x * 2]; -                  buffer[x * 6 + 1] = data[y * settings.pixels * 6 + x * 2 + 1]; -                  buffer[x * 6 + 2] = data[y * settings.pixels * 6 + 2 * settings.pixels + x * 2]; -                  buffer[x * 6 + 3] = data[y * settings.pixels * 6 + 2 * settings.pixels + x * 2 + 1]; -                  buffer[x * 6 + 4] = data[y * settings.pixels * 6 + 4 * settings.pixels + x * 2]; -                  buffer[x * 6 + 5] = data[y * settings.pixels * 6 + 4 * settings.pixels + x * 2 + 1]; -		} -	      /* copy line back */ -              memcpy (data.data() + settings.pixels * 6 * y, buffer.data(), -		      settings.pixels * 6); -	    } -	} +    if (dev->model->is_cis && session.params.scan_mode == ScanColorMode::COLOR_SINGLE_PASS) { +        auto pixels_count = session.params.pixels; + +        std::vector<uint8_t> buffer(pixels_count * 3 * bpp); + +        if (bpp == 1) { +            for (unsigned y = 0; y < lines; y++) { +                // reorder line +                for (unsigned x = 0; x < pixels_count; x++) { +                    buffer[x * 3] = data[y * pixels_count * 3 + x]; +                    buffer[x * 3 + 1] = data[y * pixels_count * 3 + pixels_count + x]; +                    buffer[x * 3 + 2] = data[y * pixels_count * 3 + 2 * pixels_count + x]; +                } +                // copy line back +                std::memcpy(data.data() + pixels_count * 3 * y, buffer.data(), pixels_count * 3); +            } +        } else { +            for (unsigned y = 0; y < lines; y++) { +                // reorder line +                auto pixels_count = session.params.pixels; +                for (unsigned x = 0; x < pixels_count; x++) { +                    buffer[x * 6] = data[y * pixels_count * 6 + x * 2]; +                    buffer[x * 6 + 1] = data[y * pixels_count * 6 + x * 2 + 1]; +                    buffer[x * 6 + 2] = data[y * pixels_count * 6 + 2 * pixels_count + x * 2]; +                    buffer[x * 6 + 3] = data[y * pixels_count * 6 + 2 * pixels_count + x * 2 + 1]; +                    buffer[x * 6 + 4] = data[y * pixels_count * 6 + 4 * pixels_count + x * 2]; +                    buffer[x * 6 + 5] = data[y * pixels_count * 6 + 4 * pixels_count + x * 2 + 1]; +                } +                // copy line back +                std::memcpy(data.data() + pixels_count * 6 * y, buffer.data(),pixels_count * 6); +            } +        }      }      // end scan , waiting the motor to stop if needed (if moving), but without ejecting doc @@ -2993,42 +2738,6 @@ static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,  }  /** - * Does a simple move of the given distance by doing a scan at lowest resolution - * shading correction. Memory for data is allocated in this function - * and must be freed by caller. - * @param dev device of the scanner - * @param distance distance to move in MM - */ -static void simple_move(Genesys_Device* dev, SANE_Int distance) -{ -    DBG_HELPER_ARGS(dbg, "%d mm", distance); -  Genesys_Settings settings; - -    unsigned resolution = sanei_genesys_get_lowest_dpi(dev); - -  const auto& sensor = sanei_genesys_find_sensor(dev, resolution, 3, dev->model->default_method); - -  /* TODO give a no AGOHOME flag */ -    settings.scan_method = dev->model->default_method; -  settings.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; -  settings.xres = resolution; -  settings.yres = resolution; -  settings.tl_y = 0; -  settings.tl_x = 0; -  settings.pixels = (sensor.sensor_pixels * settings.xres) / sensor.optical_res; -    settings.requested_pixels = settings.pixels; -    settings.lines = static_cast<unsigned>((distance * settings.xres) / MM_PER_INCH); -  settings.depth = 8; -  settings.color_filter = ColorFilter::RED; - -  settings.disable_interpolation = 0; -  settings.threshold = 0; - -  std::vector<uint8_t> data; -    simple_scan(dev, sensor, settings, true, true, false, data, "simple_move"); -} - -/**   * update the status of the required sensor in the scanner session   * the button fileds are used to make events 'sticky'   */ @@ -3130,22 +2839,16 @@ void CommandSetGl646::update_hardware_sensors(Genesys_Scanner* session) const      }    /* XPA detection */ -  if (dev->model->flags & GENESYS_FLAG_XPA) -    { +    if (dev->model->has_method(ScanMethod::TRANSPARENCY)) {          switch (dev->model->gpio_id) {              case GpioId::HP3670:              case GpioId::HP2400:  	  /* test if XPA is plugged-in */ -	  if ((value & 0x40) == 0) -	    { -	      DBG(DBG_io, "%s: enabling XPA\n", __func__); -	      session->opt[OPT_SOURCE].cap &= ~SANE_CAP_INACTIVE; -	    } -	  else -	    { -	      DBG(DBG_io, "%s: disabling XPA\n", __func__); -	      session->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; -	    } +            if ((value & 0x40) == 0) { +                session->opt[OPT_SOURCE].cap &= ~SANE_CAP_INACTIVE; +            } else { +                session->opt[OPT_SOURCE].cap |= SANE_CAP_INACTIVE; +            }        break;              default:                  throw SaneException(SANE_STATUS_UNSUPPORTED, "unknown gpo type"); @@ -3153,6 +2856,11 @@ void CommandSetGl646::update_hardware_sensors(Genesys_Scanner* session) const      }  } +void CommandSetGl646::update_home_sensor_gpio(Genesys_Device& dev) const +{ +    DBG_HELPER(dbg); +    (void) dev; +}  static void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int resolution)  { @@ -3167,7 +2875,7 @@ static void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int    /* MD6471/G2410/HP2300 and XP200 read/write data from an undocumented memory area which     * is after the second slope table */ -  switch (sensor.optical_res) +  switch (sensor.full_resolution)      {      case 600:        addr = 0x08200; @@ -3203,159 +2911,9 @@ static void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int        break;      } -  DBG(DBG_info, "%s: control write=0x%02x 0x%02x 0x%02x 0x%02x\n", __func__, control[0], control[1], -      control[2], control[3]);      dev->interface->write_buffer(0x3c, addr, control, 4);  } -/** - * search for a full width black or white strip. - * @param dev scanner device - * @param forward true if searching forward, false if searching backward - * @param black true if searching for a black strip, false for a white strip - */ -void CommandSetGl646::search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, bool forward, -                                   bool black) const -{ -    DBG_HELPER(dbg); -    (void) sensor; - -  Genesys_Settings settings; -  int res = get_closest_resolution(dev->model->sensor_id, 75, 1); -  unsigned int pass, count, found, x, y; -  char title[80]; - -    const auto& calib_sensor = sanei_genesys_find_sensor(dev, res, 1, ScanMethod::FLATBED); - -  /* we set up for a lowest available resolution color grey scan, full width */ -    settings.scan_method = dev->model->default_method; -  settings.scan_mode = ScanColorMode::GRAY; -  settings.xres = res; -  settings.yres = res; -  settings.tl_x = 0; -  settings.tl_y = 0; -    settings.pixels = static_cast<unsigned>((dev->model->x_size * res) / MM_PER_INCH); -    settings.pixels /= calib_sensor.get_ccd_size_divisor_for_dpi(res); -    settings.requested_pixels = settings.pixels; - -  /* 15 mm at at time */ -    settings.lines = static_cast<unsigned>((15 * settings.yres) / MM_PER_INCH); -  settings.depth = 8; -  settings.color_filter = ColorFilter::RED; - -  settings.disable_interpolation = 0; -  settings.threshold = 0; - -  /* signals if a strip of the given color has been found */ -  found = 0; - -  /* detection pass done */ -  pass = 0; - -  std::vector<uint8_t> data; - -  /* loop until strip is found or maximum pass number done */ -  while (pass < 20 && !found) -    { -        // scan a full width strip -        simple_scan(dev, calib_sensor, settings, true, forward, false, data, "search_strip"); - -        if (is_testing_mode()) { -            return; -        } - -      if (DBG_LEVEL >= DBG_data) -	{ -            std::sprintf(title, "gl646_search_strip_%s%02d.pnm", forward ? "fwd" : "bwd", pass); -          sanei_genesys_write_pnm_file (title, data.data(), settings.depth, 1, -					settings.pixels, settings.lines); -	} - -      /* search data to find black strip */ -      /* when searching forward, we only need one line of the searched color since we -       * will scan forward. But when doing backward search, we need all the area of the -       * same color */ -      if (forward) -	{ -	  for (y = 0; y < settings.lines && !found; y++) -	    { -	      count = 0; -	      /* count of white/black pixels depending on the color searched */ -	      for (x = 0; x < settings.pixels; x++) -		{ -		  /* when searching for black, detect white pixels */ -		  if (black && data[y * settings.pixels + x] > 90) -		    { -		      count++; -		    } -		  /* when searching for white, detect black pixels */ -		  if (!black && data[y * settings.pixels + x] < 60) -		    { -		      count++; -		    } -		} - -	      /* at end of line, if count >= 3%, line is not fully of the desired color -	       * so we must go to next line of the buffer */ -	      /* count*100/pixels < 3 */ -	      if ((count * 100) / settings.pixels < 3) -		{ -		  found = 1; -		  DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__, -		      pass, y); -		} -	      else -		{ -		  DBG(DBG_data, "%s: pixels=%d, count=%d\n", __func__, settings.pixels, count); -		} -	    } -	} -      else			/* since calibration scans are done forward, we need the whole area -				   to be of the required color when searching backward */ -	{ -	  count = 0; -	  for (y = 0; y < settings.lines; y++) -	    { -	      /* count of white/black pixels depending on the color searched */ -	      for (x = 0; x < settings.pixels; x++) -		{ -		  /* when searching for black, detect white pixels */ -		  if (black && data[y * settings.pixels + x] > 60) -		    { -		      count++; -		    } -		  /* when searching for white, detect black pixels */ -		  if (!black && data[y * settings.pixels + x] < 60) -		    { -		      count++; -		    } -		} -	    } - -	  /* at end of area, if count >= 3%, area is not fully of the desired color -	   * so we must go to next buffer */ -	  if ((count * 100) / (settings.pixels * settings.lines) < 3) -	    { -	      found = 1; -	      DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass); -	    } -	  else -	    { -	      DBG(DBG_data, "%s: pixels=%d, count=%d\n", __func__, settings.pixels, count); -	    } -	} -      pass++; -    } -  if (found) -    { -      DBG(DBG_info, "%s: strip found\n", __func__); -    } -  else -    { -        throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found", black ? "black" : "white"); -    } -} -  void CommandSetGl646::wait_for_motor_stop(Genesys_Device* dev) const  {      (void) dev; @@ -3377,26 +2935,25 @@ ScanSession CommandSetGl646::calculate_scan_session(const Genesys_Device* dev,  {      // compute distance to move      float move = 0; -    // XXX STEF XXX MD5345 -> optical_ydpi, other base_ydpi => half/full step ? */      if (!dev->model->is_sheetfed) { -        move = static_cast<float>(dev->model->y_offset); +        move = dev->model->y_offset;          // add tl_y to base movement      } -    move += static_cast<float>(settings.tl_y); +    move += settings.tl_y;      if (move < 0) {          DBG(DBG_error, "%s: overriding negative move value %f\n", __func__, move);          move = 0;      } -    move = static_cast<float>((move * dev->motor.optical_ydpi) / MM_PER_INCH); -    float start = static_cast<float>(settings.tl_x); +    move = static_cast<float>((move * dev->motor.base_ydpi) / MM_PER_INCH); +    float start = settings.tl_x;      if (settings.scan_method == ScanMethod::FLATBED) { -        start += static_cast<float>(dev->model->x_offset); +        start += dev->model->x_offset;      } else { -        start += static_cast<float>(dev->model->x_offset_ta); +        start += dev->model->x_offset_ta;      } -    start = static_cast<float>((start * sensor.optical_res) / MM_PER_INCH); +    start = static_cast<float>((start * settings.xres) / MM_PER_INCH);      ScanSession session;      session.params.xres = settings.xres; @@ -3411,7 +2968,7 @@ ScanSession CommandSetGl646::calculate_scan_session(const Genesys_Device* dev,      session.params.scan_method = dev->settings.scan_method;      session.params.scan_mode = settings.scan_mode;      session.params.color_filter = settings.color_filter; -    session.params.flags = ScanFlag::USE_XCORRECTION; +    session.params.flags = ScanFlag::AUTO_GO_HOME;      if (settings.scan_method == ScanMethod::TRANSPARENCY) {          session.params.flags |= ScanFlag::USE_XPA;      } @@ -3427,10 +2984,5 @@ void CommandSetGl646::asic_boot(Genesys_Device *dev, bool cold) const      throw SaneException("not implemented");  } -std::unique_ptr<CommandSet> create_gl646_cmd_set() -{ -    return std::unique_ptr<CommandSet>(new CommandSetGl646{}); -} -  } // namespace gl646  } // namespace genesys diff --git a/backend/genesys/gl646.h b/backend/genesys/gl646.h index afcfa05..8ab2c96 100644 --- a/backend/genesys/gl646.h +++ b/backend/genesys/gl646.h @@ -48,395 +48,13 @@  #define BACKEND_GENESYS_GL646_H  #include "genesys.h" -#include "command_set.h" +#include "command_set_common.h"  #include "motor.h"  namespace genesys {  namespace gl646 { -static void gl646_set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set, int dpi); - -/** - * sets up the scanner for a scan, registers, gamma tables, shading tables - * and slope tables, based on the parameter struct. - * @param dev         device to set up - * @param regs        registers to set up - * @param settings    settings of the scan - * @param split       true if move before scan has to be done - * @param xcorrection true if scanner's X geometry must be taken into account to - * 		     compute X, ie add left margins - * @param ycorrection true if scanner's Y geometry must be taken into account to - * 		     compute Y, ie add top margins - */ -static void setup_for_scan(Genesys_Device* device, -                           const Genesys_Sensor& sensor, -                           Genesys_Register_Set*regs, -                           Genesys_Settings settings, -                           bool split, -                           bool xcorrection, -                           bool ycorrection, -                           bool reverse); - -/** - * Does a simple move of the given distance by doing a scan at lowest resolution - * shading correction. Memory for data is allocated in this function - * and must be freed by caller. - * @param dev device of the scanner - * @param distance distance to move in MM - */ -static void simple_move(Genesys_Device* dev, SANE_Int distance); - -/** - * Does a simple scan of the area given by the settings. Scanned data - * it put in an allocated area which must be freed by the caller. - * and slope tables, based on the parameter struct. There is no shading - * correction while gamma correction is active. - * @param dev      device to set up - * @param settings settings of the scan - * @param move     flag to enable scanhead to move - * @param forward  flag to tell movement direction - * @param shading  flag to tell if shading correction should be done - * @param data     pointer that will point to the scanned data - */ -static void simple_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, -                        Genesys_Settings settings, bool move, bool forward, -                        bool shading, std::vector<uint8_t>& data, const char* test_identifier); - -/** - * Send the stop scan command - * */ -static void end_scan_impl(Genesys_Device* dev, Genesys_Register_Set* reg, bool check_stop, -                          bool eject); -/** - * writes control data to an area behind the last motor table. - */ -static void write_control(Genesys_Device* dev, const Genesys_Sensor& sensor, int resolution); - - -/** - * initialize scanner's registers at SANE init time - */ -static void gl646_init_regs (Genesys_Device * dev); - -/** - * master motor settings table entry - */ -typedef struct -{ -  /* key */ -    MotorId motor_id; -    unsigned dpi; -  unsigned channels; - -  /* settings */ -    StepType steptype; -    bool fastmod; // fast scanning -    bool fastfed; // fast fed slope tables -  SANE_Int mtrpwm; -    MotorSlope slope1; -    MotorSlope slope2; -  SANE_Int fwdbwd;		/* forward/backward steps */ -} Motor_Master; - -/** - * master motor settings, for a given motor and dpi, - * it gives steps and speed informations - */ -static Motor_Master motor_master[] = { -    /* HP3670 motor settings */ -    {MotorId::HP3670, 50, 3, StepType::HALF, false, true, 1, -     MotorSlope::create_from_steps(2329, 120, 229), -     MotorSlope::create_from_steps(3399, 337, 192), 192}, - -    {MotorId::HP3670, 75, 3, StepType::FULL, false, true, 1, -     MotorSlope::create_from_steps(3429, 305, 200), -     MotorSlope::create_from_steps(3399, 337, 192), 192}, - -    {MotorId::HP3670, 100, 3, StepType::HALF, false, true, 1, -     MotorSlope::create_from_steps(2905, 187, 143), -     MotorSlope::create_from_steps(3399, 337, 192), 192}, - -    {MotorId::HP3670, 150, 3, StepType::HALF, false, true, 1, -     MotorSlope::create_from_steps(3429, 305, 73), -     MotorSlope::create_from_steps(3399, 337, 192), 192}, - -    {MotorId::HP3670, 300, 3, StepType::HALF, false, true, 1, -     MotorSlope::create_from_steps(1055, 563, 11), -     MotorSlope::create_from_steps(3399, 337, 192), 192}, - -    {MotorId::HP3670, 600, 3, StepType::FULL, false, true, 0, -     MotorSlope::create_from_steps(10687, 5126, 3), -     MotorSlope::create_from_steps(3399, 337, 192), 192}, - -    {MotorId::HP3670,1200, 3, StepType::HALF, false, true, 0, -     MotorSlope::create_from_steps(15937, 6375, 3), -     MotorSlope::create_from_steps(3399, 337, 192), 192}, - -    {MotorId::HP3670, 50, 1, StepType::HALF, false, true, 1, -     MotorSlope::create_from_steps(2329, 120, 229), -     MotorSlope::create_from_steps(3399, 337, 192), 192}, - -    {MotorId::HP3670, 75, 1, StepType::FULL, false, true, 1, -     MotorSlope::create_from_steps(3429, 305, 200), -     MotorSlope::create_from_steps(3399, 337, 192), 192}, - -    {MotorId::HP3670, 100, 1, StepType::HALF, false, true, 1, -     MotorSlope::create_from_steps(2905, 187, 143), -     MotorSlope::create_from_steps(3399, 337, 192), 192}, - -    {MotorId::HP3670, 150, 1, StepType::HALF, false, true, 1, -     MotorSlope::create_from_steps(3429, 305, 73), -     MotorSlope::create_from_steps(3399, 337, 192), 192}, - -    {MotorId::HP3670, 300, 1, StepType::HALF, false, true, 1, -     MotorSlope::create_from_steps(1055, 563, 11), -     MotorSlope::create_from_steps(3399, 337, 192), 192}, - -    {MotorId::HP3670, 600, 1, StepType::FULL, false, true, 0, -     MotorSlope::create_from_steps(10687, 5126, 3), -     MotorSlope::create_from_steps(3399, 337, 192), 192}, - -    {MotorId::HP3670,1200, 1, StepType::HALF, false, true, 0, -     MotorSlope::create_from_steps(15937, 6375, 3), -     MotorSlope::create_from_steps(3399, 337, 192), 192}, - -    /* HP2400/G2410 motor settings base motor dpi = 600 */ -    {MotorId::HP2400, 50, 3, StepType::FULL, false, true, 63, -     MotorSlope::create_from_steps(8736, 601, 120), -     MotorSlope::create_from_steps(4905, 337, 192), 192}, - -    {MotorId::HP2400, 100, 3, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(8736, 601, 120), -     MotorSlope::create_from_steps(4905, 337, 192), 192}, - -    {MotorId::HP2400, 150, 3, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(15902, 902, 67), -     MotorSlope::create_from_steps(4905, 337, 192), 192}, - -    {MotorId::HP2400, 300, 3, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(16703, 2188, 32), -     MotorSlope::create_from_steps(4905, 337, 192), 192}, - -    {MotorId::HP2400, 600, 3, StepType::FULL, false, true, 63, -     MotorSlope::create_from_steps(18761, 18761, 3), -     MotorSlope::create_from_steps(4905, 627, 192), 192}, - -    {MotorId::HP2400,1200, 3, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(43501, 43501, 3), -     MotorSlope::create_from_steps(4905, 627, 192), 192}, - -    {MotorId::HP2400, 50, 1, StepType::FULL, false, true, 63, -     MotorSlope::create_from_steps(8736, 601, 120), -     MotorSlope::create_from_steps(4905, 337, 192), 192}, - -    {MotorId::HP2400, 100, 1, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(8736, 601, 120), -     MotorSlope::create_from_steps(4905, 337, 192), 192}, - -    {MotorId::HP2400, 150, 1, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(15902, 902, 67), -     MotorSlope::create_from_steps(4905, 337, 192), 192}, - -    {MotorId::HP2400, 300, 1, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(16703, 2188, 32), -     MotorSlope::create_from_steps(4905, 337, 192), 192}, - -    {MotorId::HP2400, 600, 1, StepType::FULL, false, true, 63, -     MotorSlope::create_from_steps(18761, 18761, 3), -     MotorSlope::create_from_steps(4905, 337, 192), 192}, - -    {MotorId::HP2400,1200, 1, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(43501, 43501, 3), -     MotorSlope::create_from_steps(4905, 337, 192), 192}, - -    /* XP 200 motor settings */ -    {MotorId::XP200, 75, 3, StepType::HALF, true, false, 0, -     MotorSlope::create_from_steps(6000, 2136, 4), -     MotorSlope::create_from_steps(12000, 1200, 8), 1}, - -    {MotorId::XP200, 100, 3, StepType::HALF, true, false, 0, -     MotorSlope::create_from_steps(6000, 2850, 4), -     MotorSlope::create_from_steps(12000, 1200, 8), 1}, - -    {MotorId::XP200, 200, 3, StepType::HALF, true, false, 0, -     MotorSlope::create_from_steps(6999, 5700, 4), -     MotorSlope::create_from_steps(12000, 1200, 8), 1}, - -    {MotorId::XP200, 250, 3, StepType::HALF, true, false, 0, -     MotorSlope::create_from_steps(6999, 6999, 4), -     MotorSlope::create_from_steps(12000, 1200, 8), 1}, - -    {MotorId::XP200, 300, 3, StepType::HALF, true, false, 0, -     MotorSlope::create_from_steps(13500, 13500, 4), -     MotorSlope::create_from_steps(12000, 1200, 8), 1}, - -    {MotorId::XP200, 600, 3, StepType::HALF, true, true, 0, -     MotorSlope::create_from_steps(31998, 31998, 4), -     MotorSlope::create_from_steps(12000, 1200, 2), 1}, - -    {MotorId::XP200, 75, 1, StepType::HALF, true, false, 0, -     MotorSlope::create_from_steps(6000, 2000, 4), -     MotorSlope::create_from_steps(12000, 1200, 8), 1}, - -    {MotorId::XP200, 100, 1, StepType::HALF, true, false, 0, -     MotorSlope::create_from_steps(6000, 1300, 4), -     MotorSlope::create_from_steps(12000, 1200, 8), 1}, - -    {MotorId::XP200, 200, 1, StepType::HALF, true, true, 0, -     MotorSlope::create_from_steps(6000, 3666, 4), -     MotorSlope::create_from_steps(12000, 1200, 8), 1}, - -    {MotorId::XP200, 300, 1, StepType::HALF, true, false, 0, -     MotorSlope::create_from_steps(6500, 6500, 4), -     MotorSlope::create_from_steps(12000, 1200, 8), 1}, - -    {MotorId::XP200, 600, 1, StepType::HALF, true, true, 0, -     MotorSlope::create_from_steps(24000, 24000, 4), -     MotorSlope::create_from_steps(12000, 1200, 2), 1}, - -    /* HP scanjet 2300c */ -    {MotorId::HP2300, 75, 3, StepType::FULL, false, true, 63, -     MotorSlope::create_from_steps(8139, 560, 120), -     MotorSlope::create_from_steps(4905, 337, 120), 16}, - -    {MotorId::HP2300, 150, 3, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(7903, 543, 67), -     MotorSlope::create_from_steps(4905, 337, 120), 16}, - -    {MotorId::HP2300, 300, 3, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(2175, 1087, 3), -     MotorSlope::create_from_steps(4905, 337, 120), 16}, - -    {MotorId::HP2300, 600, 3, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(8700, 4350, 3), -     MotorSlope::create_from_steps(4905, 337, 120), 16}, - -    {MotorId::HP2300,1200, 3, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(17400, 8700, 3), -     MotorSlope::create_from_steps(4905, 337, 120), 16}, - -    {MotorId::HP2300, 75, 1, StepType::FULL, false, true, 63, -     MotorSlope::create_from_steps(8139, 560, 120), -     MotorSlope::create_from_steps(4905, 337, 120), 16}, - -    {MotorId::HP2300, 150, 1, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(7903, 543, 67), -     MotorSlope::create_from_steps(4905, 337, 120), 16}, - -    {MotorId::HP2300, 300, 1, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(2175, 1087, 3), -     MotorSlope::create_from_steps(4905, 337, 120), 16}, - -    {MotorId::HP2300, 600, 1, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(8700, 4350, 3), -     MotorSlope::create_from_steps(4905, 337, 120), 16}, - -    {MotorId::HP2300,1200, 1, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(17400, 8700, 3), -     MotorSlope::create_from_steps(4905, 337, 120), 16}, - -    /* non half ccd settings for 300 dpi -    {MotorId::HP2300, 300, 3, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(5386, 2175, 44), -     MotorSlope::create_from_steps(4905, 337, 120), 16}, - -    {MotorId::HP2300, 300, 1, StepType::HALF, false, true, 63, -     MotorSlope::create_from_steps(5386, 2175, 44), -     MotorSlope::create_from_steps(4905, 337, 120), 16}, -    */ - -    /* MD5345/6471 motor settings */ -    /* vfinal=(exposure/(1200/dpi))/step_type */ -    {MotorId::MD_5345, 50, 3, StepType::HALF, false, true, 2, -     MotorSlope::create_from_steps(2500, 250, 255), -     MotorSlope::create_from_steps(2000, 300, 255), 64}, - -    {MotorId::MD_5345, 75, 3, StepType::HALF, false, true, 2, -     MotorSlope::create_from_steps(2500, 343, 255), -     MotorSlope::create_from_steps(2000, 300, 255), 64}, - -    {MotorId::MD_5345, 100, 3, StepType::HALF, false, true, 2, -     MotorSlope::create_from_steps(2500, 458, 255), -     MotorSlope::create_from_steps(2000, 300, 255), 64}, - -    {MotorId::MD_5345, 150, 3, StepType::HALF, false, true, 2, -     MotorSlope::create_from_steps(2500, 687, 255), -     MotorSlope::create_from_steps(2000, 300, 255), 64}, - -    {MotorId::MD_5345, 200, 3, StepType::HALF, false, true, 2, -     MotorSlope::create_from_steps(2500, 916, 255), -     MotorSlope::create_from_steps(2000, 300, 255), 64}, - -    {MotorId::MD_5345, 300, 3, StepType::HALF, false, true, 2, -     MotorSlope::create_from_steps(2500, 1375, 255), -     MotorSlope::create_from_steps(2000, 300, 255), 64}, - -    {MotorId::MD_5345, 400, 3, StepType::HALF, false, true, 0, -     MotorSlope::create_from_steps(2000, 1833, 32), -     MotorSlope::create_from_steps(2000, 300, 255), 32}, - -    {MotorId::MD_5345, 500, 3, StepType::HALF, false, true, 0, -     MotorSlope::create_from_steps(2291, 2291, 32), -     MotorSlope::create_from_steps(2000, 300, 255), 32}, - -    {MotorId::MD_5345, 600, 3, StepType::HALF, false, true, 0, -     MotorSlope::create_from_steps(2750, 2750, 32), -     MotorSlope::create_from_steps(2000, 300, 255), 32}, - -    {MotorId::MD_5345, 1200, 3, StepType::QUARTER, false, true, 0, -     MotorSlope::create_from_steps(2750, 2750, 16), -     MotorSlope::create_from_steps(2000, 300, 255), 146}, - -    {MotorId::MD_5345, 2400, 3, StepType::QUARTER, false, true, 0, -     MotorSlope::create_from_steps(5500, 5500, 16), -     MotorSlope::create_from_steps(2000, 300, 255), 146}, - -    {MotorId::MD_5345, 50, 1, StepType::HALF, false, true, 2, -     MotorSlope::create_from_steps(2500, 250, 255), -     MotorSlope::create_from_steps(2000, 300, 255), 64}, - -    {MotorId::MD_5345, 75, 1, StepType::HALF, false, true, 2, -     MotorSlope::create_from_steps(2500, 343, 255), -     MotorSlope::create_from_steps(2000, 300, 255), 64}, - -    {MotorId::MD_5345, 100, 1, StepType::HALF, false, true, 2, -     MotorSlope::create_from_steps(2500, 458, 255), -     MotorSlope::create_from_steps(2000, 300, 255), 64}, - -    {MotorId::MD_5345, 150, 1, StepType::HALF, false, true, 2, -     MotorSlope::create_from_steps(2500, 687, 255), -     MotorSlope::create_from_steps(2000, 300, 255), 64}, - -    {MotorId::MD_5345, 200, 1, StepType::HALF, false, true, 2, -     MotorSlope::create_from_steps(2500, 916, 255), -     MotorSlope::create_from_steps(2000, 300, 255), 64}, - -    {MotorId::MD_5345, 300, 1, StepType::HALF, false, true, 2, -     MotorSlope::create_from_steps(2500, 1375, 255), -     MotorSlope::create_from_steps(2000, 300, 255), 64}, - -    {MotorId::MD_5345, 400, 1, StepType::HALF, false, true, 0, -     MotorSlope::create_from_steps(2000, 1833, 32), -     MotorSlope::create_from_steps(2000, 300, 255), 32}, - -    {MotorId::MD_5345, 500, 1, StepType::HALF, false, true, 0, -     MotorSlope::create_from_steps(2291, 2291, 32), -     MotorSlope::create_from_steps(2000, 300, 255), 32}, - -    {MotorId::MD_5345, 600, 1, StepType::HALF, false, true, 0, -     MotorSlope::create_from_steps(2750, 2750, 32), -     MotorSlope::create_from_steps(2000, 300, 255), 32}, - -    {MotorId::MD_5345, 1200, 1, StepType::QUARTER, false, true, 0, -     MotorSlope::create_from_steps(2750, 2750, 16), -     MotorSlope::create_from_steps(2000, 300, 255), 146}, - -    {MotorId::MD_5345, 2400, 1, StepType::QUARTER, false, true, 0, -     MotorSlope::create_from_steps(5500, 5500, 16), -     MotorSlope::create_from_steps(2000, 300, 255), 146}, /* 5500 guessed */ -}; - -class CommandSetGl646 : public CommandSet +class CommandSetGl646 : public CommandSetCommon  {  public:      ~CommandSetGl646() override = default; @@ -446,17 +64,11 @@ public:      void init(Genesys_Device* dev) const override;      void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, -                              Genesys_Register_Set* regs, int* channels, -                              int* total_size) const override; - -    void init_regs_for_coarse_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                          Genesys_Register_Set& regs) const override; +                              Genesys_Register_Set* regs) const override;      void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,                                 Genesys_Register_Set& regs) const override; -    void init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; -      void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,                                      Genesys_Register_Set* reg,                                      const ScanSession& session) const override; @@ -472,8 +84,6 @@ public:      void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; -    void search_start_position(Genesys_Device* dev) const override; -      void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,                              Genesys_Register_Set& regs) const override; @@ -489,17 +99,14 @@ public:      void update_hardware_sensors(struct Genesys_Scanner* s) const override; +    void update_home_sensor_gpio(Genesys_Device& dev) const override; +      void load_document(Genesys_Device* dev) const override;      void detect_document_end(Genesys_Device* dev) const override;      void eject_document(Genesys_Device* dev) const override; -    void search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, -                      bool forward, bool black) const override; - -    void move_to_ta(Genesys_Device* dev) const override; -      void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t* data,                             int size) const override; diff --git a/backend/genesys/gl646_registers.h b/backend/genesys/gl646_registers.h index 2fe8f19..6ee9549 100644 --- a/backend/genesys/gl646_registers.h +++ b/backend/genesys/gl646_registers.h @@ -88,6 +88,7 @@ static constexpr RegMask REG_0x04_ADTYPE = 0x30;  static constexpr RegMask REG_0x04_FILTER = 0x0c;  static constexpr RegMask REG_0x04_FESET = 0x03; +static constexpr RegAddr REG_0x05 = 0x05;  static constexpr RegMask REG_0x05_DPIHW = 0xc0;  static constexpr RegMask REG_0x05_DPIHW_600 = 0x00;  static constexpr RegMask REG_0x05_DPIHW_1200 = 0x40; diff --git a/backend/genesys/gl841.cpp b/backend/genesys/gl841.cpp index 470f9ba..731354f 100644 --- a/backend/genesys/gl841.cpp +++ b/backend/genesys/gl841.cpp @@ -63,315 +63,11 @@ namespace gl841 {  static int gl841_exposure_time(Genesys_Device *dev, const Genesys_Sensor& sensor, +                               const MotorProfile& profile,                                 float slope_dpi, -                               StepType scan_step_type,                                 int start,                                 int used_pixels); -/** copy sensor specific settings */ -/* *dev  : device infos -   *regs : registers to be set -   extended : do extended set up -   ccd_size_divisor: set up for half ccd resolution -   all registers 08-0B, 10-1D, 52-59 are set up. They shouldn't -   appear anywhere else but in register_ini - -Responsible for signals to CCD/CIS: -  CCD_CK1X (CK1INV(0x16),CKDIS(0x16),CKTOGGLE(0x18),CKDELAY(0x18),MANUAL1(0x1A),CK1MTGL(0x1C),CK1LOW(0x1D),CK1MAP(0x74,0x75,0x76),CK1NEG(0x7D)) -  CCD_CK2X (CK2INV(0x16),CKDIS(0x16),CKTOGGLE(0x18),CKDELAY(0x18),MANUAL1(0x1A),CK1LOW(0x1D),CK1NEG(0x7D)) -  CCD_CK3X (MANUAL3(0x1A),CK3INV(0x1A),CK3MTGL(0x1C),CK3LOW(0x1D),CK3MAP(0x77,0x78,0x79),CK3NEG(0x7D)) -  CCD_CK4X (MANUAL3(0x1A),CK4INV(0x1A),CK4MTGL(0x1C),CK4LOW(0x1D),CK4MAP(0x7A,0x7B,0x7C),CK4NEG(0x7D)) -  CCD_CPX  (CTRLHI(0x16),CTRLINV(0x16),CTRLDIS(0x16),CPH(0x72),CPL(0x73),CPNEG(0x7D)) -  CCD_RSX  (CTRLHI(0x16),CTRLINV(0x16),CTRLDIS(0x16),RSH(0x70),RSL(0x71),RSNEG(0x7D)) -  CCD_TGX  (TGINV(0x16),TGMODE(0x17),TGW(0x17),EXPR(0x10,0x11),TGSHLD(0x1D)) -  CCD_TGG  (TGINV(0x16),TGMODE(0x17),TGW(0x17),EXPG(0x12,0x13),TGSHLD(0x1D)) -  CCD_TGB  (TGINV(0x16),TGMODE(0x17),TGW(0x17),EXPB(0x14,0x15),TGSHLD(0x1D)) -  LAMP_SW  (EXPR(0x10,0x11),XPA_SEL(0x03),LAMP_PWR(0x03),LAMPTIM(0x03),MTLLAMP(0x04),LAMPPWM(0x29)) -  XPA_SW   (EXPG(0x12,0x13),XPA_SEL(0x03),LAMP_PWR(0x03),LAMPTIM(0x03),MTLLAMP(0x04),LAMPPWM(0x29)) -  LAMP_B   (EXPB(0x14,0x15),LAMP_PWR(0x03)) - -other registers: -  CISSET(0x01),CNSET(0x18),DCKSEL(0x18),SCANMOD(0x18),EXPDMY(0x19),LINECLP(0x1A),CKAREA(0x1C),TGTIME(0x1C),LINESEL(0x1E),DUMMY(0x34) - -Responsible for signals to AFE: -  VSMP  (VSMP(0x58),VSMPW(0x58)) -  BSMP  (BSMP(0x59),BSMPW(0x59)) - -other register settings depending on this: -  RHI(0x52),RLOW(0x53),GHI(0x54),GLOW(0x55),BHI(0x56),BLOW(0x57), - -*/ -static void sanei_gl841_setup_sensor(Genesys_Device * dev, const Genesys_Sensor& sensor, -                                     Genesys_Register_Set * regs, -                                     bool extended, unsigned ccd_size_divisor) -{ -    DBG(DBG_proc, "%s\n", __func__); - -    // that one is tricky at least -    for (uint16_t addr = 0x08; addr <= 0x0b; ++addr) { -        regs->set8(0x70 + addr - 0x08, sensor.custom_regs.get_value(addr)); -    } - -    // ignore registers in range [0x10..0x16) -    for (uint16_t addr = 0x16; addr < 0x1e; ++addr) { -        regs->set8(addr, sensor.custom_regs.get_value(addr)); -    } - -    // ignore registers in range [0x5b..0x5e] -    for (uint16_t addr = 0x52; addr < 0x52 + 9; ++addr) { -        regs->set8(addr, sensor.custom_regs.get_value(addr)); -    } - -  /* don't go any further if no extended setup */ -  if (!extended) -    return; - -  /* todo : add more CCD types if needed */ -  /* we might want to expand the Sensor struct to have these -     2 kind of settings */ -    if (dev->model->sensor_id == SensorId::CCD_5345) { -        if (ccd_size_divisor > 1) { -          GenesysRegister* r; -	  /* settings for CCD used at half is max resolution */ -	  r = sanei_genesys_get_address (regs, 0x70); -	  r->value = 0x00; -	  r = sanei_genesys_get_address (regs, 0x71); -	  r->value = 0x05; -	  r = sanei_genesys_get_address (regs, 0x72); -	  r->value = 0x06; -	  r = sanei_genesys_get_address (regs, 0x73); -	  r->value = 0x08; -	  r = sanei_genesys_get_address (regs, 0x18); -	  r->value = 0x28; -	  r = sanei_genesys_get_address (regs, 0x58); -	  r->value = 0x80 | (r->value & 0x03);	/* VSMP=16 */ -	} -      else -	{ -          GenesysRegister* r; -	  /* swap latch times */ -	  r = sanei_genesys_get_address (regs, 0x18); -	  r->value = 0x30; -          regs->set8(0x52, sensor.custom_regs.get_value(0x55)); -          regs->set8(0x53, sensor.custom_regs.get_value(0x56)); -          regs->set8(0x54, sensor.custom_regs.get_value(0x57)); -          regs->set8(0x55, sensor.custom_regs.get_value(0x52)); -          regs->set8(0x56, sensor.custom_regs.get_value(0x53)); -          regs->set8(0x57, sensor.custom_regs.get_value(0x54)); -	  r = sanei_genesys_get_address (regs, 0x58); -	  r->value = 0x20 | (r->value & 0x03);	/* VSMP=4 */ -	} -      return; -    } - -    if (dev->model->sensor_id == SensorId::CCD_HP2300) { -      /* settings for CCD used at half is max resolution */ -      GenesysRegister* r; -        if (ccd_size_divisor > 1) { -	  r = sanei_genesys_get_address (regs, 0x70); -	  r->value = 0x16; -	  r = sanei_genesys_get_address (regs, 0x71); -	  r->value = 0x00; -	  r = sanei_genesys_get_address (regs, 0x72); -	  r->value = 0x01; -	  r = sanei_genesys_get_address (regs, 0x73); -	  r->value = 0x03; -	  /* manual clock programming */ -	  r = sanei_genesys_get_address (regs, 0x1d); -	  r->value |= 0x80; -	} -      else -	{ -	  r = sanei_genesys_get_address (regs, 0x70); -	  r->value = 1; -	  r = sanei_genesys_get_address (regs, 0x71); -	  r->value = 3; -	  r = sanei_genesys_get_address (regs, 0x72); -	  r->value = 4; -	  r = sanei_genesys_get_address (regs, 0x73); -	  r->value = 6; -	} -      r = sanei_genesys_get_address (regs, 0x58); -      r->value = 0x80 | (r->value & 0x03);	/* VSMP=16 */ -      return; -    } -} - -/* - * Set all registers LiDE 80 to default values - * (function called only once at the beginning) - * we are doing a special case to ease development - */ -static void -gl841_init_lide80 (Genesys_Device * dev) -{ -    dev->reg.init_reg(0x01, 0x82); // 0x02 = SHDAREA  and no CISSET ! -    dev->reg.init_reg(0x02, 0x10); -    dev->reg.init_reg(0x03, 0x50); -    dev->reg.init_reg(0x04, 0x02); -    dev->reg.init_reg(0x05, 0x4c);  // 1200 DPI -    dev->reg.init_reg(0x06, 0x38);  // 0x38 scanmod=1, pwrbit, GAIN4 -    dev->reg.init_reg(0x07, 0x00); -    dev->reg.init_reg(0x08, 0x00); -    dev->reg.init_reg(0x09, 0x11); -    dev->reg.init_reg(0x0a, 0x00); - -    dev->reg.init_reg(0x10, 0x40); -    dev->reg.init_reg(0x11, 0x00); -    dev->reg.init_reg(0x12, 0x40); -    dev->reg.init_reg(0x13, 0x00); -    dev->reg.init_reg(0x14, 0x40); -    dev->reg.init_reg(0x15, 0x00); -    dev->reg.init_reg(0x16, 0x00); -    dev->reg.init_reg(0x17, 0x01); -    dev->reg.init_reg(0x18, 0x00); -    dev->reg.init_reg(0x19, 0x06); -    dev->reg.init_reg(0x1a, 0x00); -    dev->reg.init_reg(0x1b, 0x00); -    dev->reg.init_reg(0x1c, 0x00); -    dev->reg.init_reg(0x1d, 0x04); -    dev->reg.init_reg(0x1e, 0x10); -    dev->reg.init_reg(0x1f, 0x04); -    dev->reg.init_reg(0x20, 0x02); -    dev->reg.init_reg(0x21, 0x10); -    dev->reg.init_reg(0x22, 0x20); -    dev->reg.init_reg(0x23, 0x20); -    dev->reg.init_reg(0x24, 0x10); -    dev->reg.init_reg(0x25, 0x00); -    dev->reg.init_reg(0x26, 0x00); -    dev->reg.init_reg(0x27, 0x00); - -    dev->reg.init_reg(0x29, 0xff); - -    const auto& sensor = sanei_genesys_find_sensor_any(dev); -    dev->reg.init_reg(0x2c, sensor.optical_res>>8); -    dev->reg.init_reg(0x2d, sensor.optical_res & 0xff); -    dev->reg.init_reg(0x2e, 0x80); -    dev->reg.init_reg(0x2f, 0x80); -    dev->reg.init_reg(0x30, 0x00); -    dev->reg.init_reg(0x31, 0x10); -    dev->reg.init_reg(0x32, 0x15); -    dev->reg.init_reg(0x33, 0x0e); -    dev->reg.init_reg(0x34, 0x40); -    dev->reg.init_reg(0x35, 0x00); -    dev->reg.init_reg(0x36, 0x2a); -    dev->reg.init_reg(0x37, 0x30); -    dev->reg.init_reg(0x38, 0x2a); -    dev->reg.init_reg(0x39, 0xf8); - -    dev->reg.init_reg(0x3d, 0x00); -    dev->reg.init_reg(0x3e, 0x00); -    dev->reg.init_reg(0x3f, 0x00); - -    dev->reg.init_reg(0x52, 0x03); -    dev->reg.init_reg(0x53, 0x07); -    dev->reg.init_reg(0x54, 0x00); -    dev->reg.init_reg(0x55, 0x00); -    dev->reg.init_reg(0x56, 0x00); -    dev->reg.init_reg(0x57, 0x00); -    dev->reg.init_reg(0x58, 0x29); -    dev->reg.init_reg(0x59, 0x69); -    dev->reg.init_reg(0x5a, 0x55); - -    dev->reg.init_reg(0x5d, 0x20); -    dev->reg.init_reg(0x5e, 0x41); -    dev->reg.init_reg(0x5f, 0x40); -    dev->reg.init_reg(0x60, 0x00); -    dev->reg.init_reg(0x61, 0x00); -    dev->reg.init_reg(0x62, 0x00); -    dev->reg.init_reg(0x63, 0x00); -    dev->reg.init_reg(0x64, 0x00); -    dev->reg.init_reg(0x65, 0x00); -    dev->reg.init_reg(0x66, 0x00); -    dev->reg.init_reg(0x67, 0x40); -    dev->reg.init_reg(0x68, 0x40); -    dev->reg.init_reg(0x69, 0x20); -    dev->reg.init_reg(0x6a, 0x20); -    dev->reg.init_reg(0x6c, 0x00); -    dev->reg.init_reg(0x6d, 0x00); -    dev->reg.init_reg(0x6e, 0x00); -    dev->reg.init_reg(0x6f, 0x00); -    dev->reg.init_reg(0x70, 0x00); -    dev->reg.init_reg(0x71, 0x05); -    dev->reg.init_reg(0x72, 0x07); -    dev->reg.init_reg(0x73, 0x09); -    dev->reg.init_reg(0x74, 0x00); -    dev->reg.init_reg(0x75, 0x01); -    dev->reg.init_reg(0x76, 0xff); -    dev->reg.init_reg(0x77, 0x00); -    dev->reg.init_reg(0x78, 0x0f); -    dev->reg.init_reg(0x79, 0xf0); -    dev->reg.init_reg(0x7a, 0xf0); -    dev->reg.init_reg(0x7b, 0x00); -    dev->reg.init_reg(0x7c, 0x1e); -    dev->reg.init_reg(0x7d, 0x11); -    dev->reg.init_reg(0x7e, 0x00); -    dev->reg.init_reg(0x7f, 0x50); -    dev->reg.init_reg(0x80, 0x00); -    dev->reg.init_reg(0x81, 0x00); -    dev->reg.init_reg(0x82, 0x0f); -    dev->reg.init_reg(0x83, 0x00); -    dev->reg.init_reg(0x84, 0x0e); -    dev->reg.init_reg(0x85, 0x00); -    dev->reg.init_reg(0x86, 0x0d); -    dev->reg.init_reg(0x87, 0x02); -    dev->reg.init_reg(0x88, 0x00); -    dev->reg.init_reg(0x89, 0x00); - -    for (const auto& reg : dev->gpo.regs) { -        dev->reg.set8(reg.address, reg.value); -    } - -    // specific scanner settings, clock and gpio first -    // FIXME: remove the dummy reads as we don't use the values -    if (!is_testing_mode()) { -        dev->interface->read_register(REG_0x6B); -    } -    dev->interface->write_register(REG_0x6B, 0x0c); -    dev->interface->write_register(0x06, 0x10); -    dev->interface->write_register(REG_0x6E, 0x6d); -    dev->interface->write_register(REG_0x6F, 0x80); -    dev->interface->write_register(REG_0x6B, 0x0e); -    if (!is_testing_mode()) { -        dev->interface->read_register(REG_0x6C); -    } -    dev->interface->write_register(REG_0x6C, 0x00); -    if (!is_testing_mode()) { -        dev->interface->read_register(REG_0x6D); -    } -    dev->interface->write_register(REG_0x6D, 0x8f); -    if (!is_testing_mode()) { -        dev->interface->read_register(REG_0x6B); -    } -    dev->interface->write_register(REG_0x6B, 0x0e); -    if (!is_testing_mode()) { -        dev->interface->read_register(REG_0x6B); -    } -    dev->interface->write_register(REG_0x6B, 0x0e); -    if (!is_testing_mode()) { -        dev->interface->read_register(REG_0x6B); -    } -    dev->interface->write_register(REG_0x6B, 0x0a); -    if (!is_testing_mode()) { -        dev->interface->read_register(REG_0x6B); -    } -    dev->interface->write_register(REG_0x6B, 0x02); -    if (!is_testing_mode()) { -        dev->interface->read_register(REG_0x6B); -    } -    dev->interface->write_register(REG_0x6B, 0x06); - -    dev->interface->write_0x8c(0x10, 0x94); -    dev->interface->write_register(0x09, 0x10); - -  // FIXME: the following code originally changed 0x6b, but due to bug the 0x6c register was -  // effectively changed. The current behavior matches the old code, but should probably be fixed. -    dev->reg.find_reg(0x6c).value |= REG_0x6B_GPO18; -    dev->reg.find_reg(0x6c).value &= ~REG_0x6B_GPO17; - -    sanei_gl841_setup_sensor(dev, sensor, &dev->reg, 0, 1); -} -  /*   * Set all registers to default values   * (function called only once at the beginning) @@ -379,139 +75,232 @@ gl841_init_lide80 (Genesys_Device * dev)  static void  gl841_init_registers (Genesys_Device * dev)  { -  int addr; - -  DBG(DBG_proc, "%s\n", __func__); - -  dev->reg.clear(); -    if (dev->model->model_id == ModelId::CANON_LIDE_80) { -      gl841_init_lide80(dev); -      return ; -    } - -    for (addr = 1; addr <= 0x0a; addr++) { -        dev->reg.init_reg(addr, 0); -    } -    for (addr = 0x10; addr <= 0x27; addr++) { -        dev->reg.init_reg(addr, 0); -    } -    dev->reg.init_reg(0x29, 0); -    for (addr = 0x2c; addr <= 0x39; addr++) -        dev->reg.init_reg(addr, 0); -    for (addr = 0x3d; addr <= 0x3f; addr++) -        dev->reg.init_reg(addr, 0); -    for (addr = 0x52; addr <= 0x5a; addr++) -        dev->reg.init_reg(addr, 0); -    for (addr = 0x5d; addr <= 0x87; addr++) -        dev->reg.init_reg(addr, 0); - +    DBG_HELPER(dbg); -    dev->reg.find_reg(0x01).value = 0x20;	/* (enable shading), CCD, color, 1M */ +    dev->reg.init_reg(0x01, 0x20);      if (dev->model->is_cis) {          dev->reg.find_reg(0x01).value |= REG_0x01_CISSET;      } else {          dev->reg.find_reg(0x01).value &= ~REG_0x01_CISSET;      } +    if (dev->model->model_id == ModelId::CANON_LIDE_80) { +        dev->reg.init_reg(0x01, 0x82); +    } -    dev->reg.find_reg(0x02).value = 0x30 /*0x38 */ ;	/* auto home, one-table-move, full step */ -    dev->reg.find_reg(0x02).value |= REG_0x02_AGOHOME; -    sanei_genesys_set_motor_power(dev->reg, true); -    dev->reg.find_reg(0x02).value |= REG_0x02_FASTFED; - -    dev->reg.find_reg(0x03).value = 0x1f /*0x17 */ ;	/* lamp on */ -    dev->reg.find_reg(0x03).value |= REG_0x03_AVEENB; +    dev->reg.init_reg(0x02, 0x38); +    if (dev->model->model_id == ModelId::CANON_LIDE_80) { +        dev->reg.init_reg(0x02, 0x10); +    } -    if (dev->model->sensor_id == SensorId::CCD_PLUSTEK_OPTICPRO_3600) { -        // AD front end -        dev->reg.find_reg(0x04).value  = (2 << REG_0x04S_AFEMOD) | 0x02; +    dev->reg.init_reg(0x03, 0x5f); +    if (dev->model->model_id == ModelId::CANON_LIDE_80) { +        dev->reg.init_reg(0x03, 0x50);      } -  else /* Wolfson front end */ -    { -        dev->reg.find_reg(0x04).value |= 1 << REG_0x04S_AFEMOD; + +    dev->reg.init_reg(0x04, 0x10); +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICPRO_3600) { +        dev->reg.init_reg(0x04, 0x22); +    } else if (dev->model->model_id == ModelId::CANON_LIDE_80) { +        dev->reg.init_reg(0x04, 0x02);      } -  const auto& sensor = sanei_genesys_find_sensor_any(dev); +    const auto& sensor = sanei_genesys_find_sensor_any(dev); -  dev->reg.find_reg(0x05).value = 0x00;	/* disable gamma, 24 clocks/pixel */ +    dev->reg.init_reg(0x05, 0x00); // disable gamma, 24 clocks/pixel -    unsigned dpihw = 0; -    if (sensor.sensor_pixels < 0x1500) { -        dpihw = 600; -    } else if (sensor.sensor_pixels < 0x2a80) { -        dpihw = 1200; -    } else if (sensor.sensor_pixels < 0x5400) { -        dpihw = 2400; -    } else { -        throw SaneException("Cannot handle sensor pixel count %d", sensor.sensor_pixels); -    } -    sanei_genesys_set_dpihw(dev->reg, sensor, dpihw); +    sanei_genesys_set_dpihw(dev->reg, sensor.register_dpihw); -    dev->reg.find_reg(0x06).value |= REG_0x06_PWRBIT; -    dev->reg.find_reg(0x06).value |= REG_0x06_GAIN4; +    if (dev->model->model_id == ModelId::CANON_LIDE_80) { +        dev->reg.init_reg(0x05, 0x4c); +    } -  /* XP300 CCD needs different clock and clock/pixels values */ -    if (dev->model->sensor_id != SensorId::CCD_XP300 && -        dev->model->sensor_id != SensorId::CCD_DP685 && -        dev->model->sensor_id != SensorId::CCD_PLUSTEK_OPTICPRO_3600) -    { -        dev->reg.find_reg(0x06).value |= 0 << REG_0x06S_SCANMOD; -        dev->reg.find_reg(0x09).value |= 1 << REG_0x09S_CLKSET; +    dev->reg.init_reg(0x06, 0x18); +    if (dev->model->model_id == ModelId::CANON_LIDE_80) { +        dev->reg.init_reg(0x06, 0x38);      } -  else +    if (dev->model->model_id == ModelId::VISIONEER_STROBE_XP300 || +        dev->model->model_id == ModelId::SYSCAN_DOCKETPORT_485 || +        dev->model->model_id == ModelId::DCT_DOCKETPORT_487 || +        dev->model->model_id == ModelId::SYSCAN_DOCKETPORT_685 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICPRO_3600)      { -        dev->reg.find_reg(0x06).value |= 0x05 << REG_0x06S_SCANMOD; /* 15 clocks/pixel */ -      dev->reg.find_reg(0x09).value = 0; /* 24 MHz CLKSET */ +        dev->reg.init_reg(0x06, 0xb8);      } -  dev->reg.find_reg(0x1e).value = 0xf0;	/* watch-dog time */ - -    dev->reg.find_reg(0x17).value |= 1 << REG_0x17S_TGW; - -  dev->reg.find_reg(0x19).value = 0x50; - -    dev->reg.find_reg(0x1d).value |= 1 << REG_0x1DS_TGSHLD; - -    dev->reg.find_reg(0x1e).value |= 1 << REG_0x1ES_WDTIME; - -/*SCANFED*/ -  dev->reg.find_reg(0x1f).value = 0x01; +    dev->reg.init_reg(0x07, 0x00); +    dev->reg.init_reg(0x08, 0x00); -/*BUFSEL*/ -  dev->reg.find_reg(0x20).value = 0x20; +    dev->reg.init_reg(0x09, 0x10); +    if (dev->model->model_id == ModelId::CANON_LIDE_80) { +        dev->reg.init_reg(0x09, 0x11); +    } +    if (dev->model->model_id == ModelId::VISIONEER_STROBE_XP300 || +        dev->model->model_id == ModelId::SYSCAN_DOCKETPORT_485 || +        dev->model->model_id == ModelId::DCT_DOCKETPORT_487 || +        dev->model->model_id == ModelId::SYSCAN_DOCKETPORT_685 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICPRO_3600) +    { +        dev->reg.init_reg(0x09, 0x00); +    } +    dev->reg.init_reg(0x0a, 0x00); -/*LAMPPWM*/ -  dev->reg.find_reg(0x29).value = 0xff; +    // EXPR[0:15], EXPG[0:15], EXPB[0:15]: Exposure time settings +    dev->reg.init_reg(0x10, 0x00); // SENSOR_DEF +    dev->reg.init_reg(0x11, 0x00); // SENSOR_DEF +    dev->reg.init_reg(0x12, 0x00); // SENSOR_DEF +    dev->reg.init_reg(0x13, 0x00); // SENSOR_DEF +    dev->reg.init_reg(0x14, 0x00); // SENSOR_DEF +    dev->reg.init_reg(0x15, 0x00); // SENSOR_DEF +    if (dev->model->model_id == ModelId::CANON_LIDE_80) { +        dev->reg.init_reg(0x10, 0x40); +        dev->reg.init_reg(0x11, 0x00); +        dev->reg.init_reg(0x12, 0x40); +        dev->reg.init_reg(0x13, 0x00); +        dev->reg.init_reg(0x14, 0x40); +        dev->reg.init_reg(0x15, 0x00); +    } + +    dev->reg.init_reg(0x16, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x17, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x18, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x19, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x1a, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x1b, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x1c, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x1d, 0x01); // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x1e, 0xf0); +    if (dev->model->model_id == ModelId::CANON_LIDE_80) { +        dev->reg.init_reg(0x1e, 0x10); +    } +    dev->reg.init_reg(0x1f, 0x01); +    if (dev->model->model_id == ModelId::CANON_LIDE_80) { +        dev->reg.init_reg(0x1f, 0x04); +    } +    dev->reg.init_reg(0x20, 0x20); +    dev->reg.init_reg(0x21, 0x01); +    dev->reg.init_reg(0x22, 0x01); +    dev->reg.init_reg(0x23, 0x01); +    dev->reg.init_reg(0x24, 0x01); +    dev->reg.init_reg(0x25, 0x00); +    dev->reg.init_reg(0x26, 0x00); +    dev->reg.init_reg(0x27, 0x00); +    dev->reg.init_reg(0x29, 0xff); -/*BWHI*/ -  dev->reg.find_reg(0x2e).value = 0x80; +    dev->reg.init_reg(0x2c, 0x00); +    dev->reg.init_reg(0x2d, 0x00); +    if (dev->model->model_id == ModelId::CANON_LIDE_80) { +        dev->reg.init_reg(0x2c, sensor.full_resolution >> 8); +        dev->reg.init_reg(0x2d, sensor.full_resolution & 0xff); +    } +    dev->reg.init_reg(0x2e, 0x80); +    dev->reg.init_reg(0x2f, 0x80); -/*BWLOW*/ -  dev->reg.find_reg(0x2f).value = 0x80; +    dev->reg.init_reg(0x30, 0x00); +    dev->reg.init_reg(0x31, 0x00); +    dev->reg.init_reg(0x32, 0x00); +    dev->reg.init_reg(0x33, 0x00); +    dev->reg.init_reg(0x34, 0x00); +    dev->reg.init_reg(0x35, 0x00); +    dev->reg.init_reg(0x36, 0x00); +    dev->reg.init_reg(0x37, 0x00); +    dev->reg.init_reg(0x38, 0x4f); +    dev->reg.init_reg(0x39, 0xc1); +    if (dev->model->model_id == ModelId::CANON_LIDE_80) { +        dev->reg.init_reg(0x31, 0x10); +        dev->reg.init_reg(0x32, 0x15); +        dev->reg.init_reg(0x33, 0x0e); +        dev->reg.init_reg(0x34, 0x40); +        dev->reg.init_reg(0x35, 0x00); +        dev->reg.init_reg(0x36, 0x2a); +        dev->reg.init_reg(0x37, 0x30); +        dev->reg.init_reg(0x38, 0x2a); +        dev->reg.init_reg(0x39, 0xf8); +    } -/*LPERIOD*/ -  dev->reg.find_reg(0x38).value = 0x4f; -  dev->reg.find_reg(0x39).value = 0xc1; +    dev->reg.init_reg(0x3d, 0x00); +    dev->reg.init_reg(0x3e, 0x00); +    dev->reg.init_reg(0x3f, 0x00); -/*VSMPW*/ -    dev->reg.find_reg(0x58).value |= 3 << REG_0x58S_VSMPW; +    dev->reg.init_reg(0x52, 0x00);  // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x53, 0x00);  // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x54, 0x00);  // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x55, 0x00);  // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x56, 0x00);  // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x57, 0x00);  // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x58, 0x00);  // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x59, 0x00);  // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x5a, 0x00);  // SENSOR_DEF, overwritten in scanner_setup_sensor() below -/*BSMPW*/ -    dev->reg.find_reg(0x59).value |= 3 << REG_0x59S_BSMPW; +    if (dev->model->model_id == ModelId::CANON_LIDE_80) { +        dev->reg.init_reg(0x5d, 0x20); +        dev->reg.init_reg(0x5e, 0x41); +        dev->reg.init_reg(0x5f, 0x40); +        dev->reg.init_reg(0x60, 0x00); +        dev->reg.init_reg(0x61, 0x00); +        dev->reg.init_reg(0x62, 0x00); +        dev->reg.init_reg(0x63, 0x00); +        dev->reg.init_reg(0x64, 0x00); +        dev->reg.init_reg(0x65, 0x00); +        dev->reg.init_reg(0x66, 0x00); +        dev->reg.init_reg(0x67, 0x40); +        dev->reg.init_reg(0x68, 0x40); +        dev->reg.init_reg(0x69, 0x20); +        dev->reg.init_reg(0x6a, 0x20); +        dev->reg.init_reg(0x6c, 0x00); +        dev->reg.init_reg(0x6d, 0x00); +        dev->reg.init_reg(0x6e, 0x00); +        dev->reg.init_reg(0x6f, 0x00); +    } else { +        for (unsigned addr = 0x5d; addr <= 0x6f; addr++) { +            dev->reg.init_reg(addr, 0); +        } +        dev->reg.init_reg(0x5e, 0x02); +        if (dev->model->model_id == ModelId::CANON_LIDE_60) { +            dev->reg.init_reg(0x66, 0xff); +        } +    } -/*RLCSEL*/ -    dev->reg.find_reg(0x5a).value |= REG_0x5A_RLCSEL; +    dev->reg.init_reg(0x70, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x71, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x72, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x73, 0x00); // SENSOR_DEF, overwritten in scanner_setup_sensor() below -/*STOPTIM*/ -    dev->reg.find_reg(0x5e).value |= 0x2 << REG_0x5ES_STOPTIM; +    if (dev->model->model_id == ModelId::CANON_LIDE_80) { +        dev->reg.init_reg(0x74, 0x00); +        dev->reg.init_reg(0x75, 0x01); +        dev->reg.init_reg(0x76, 0xff); +        dev->reg.init_reg(0x77, 0x00); +        dev->reg.init_reg(0x78, 0x0f); +        dev->reg.init_reg(0x79, 0xf0); +        dev->reg.init_reg(0x7a, 0xf0); +        dev->reg.init_reg(0x7b, 0x00); +        dev->reg.init_reg(0x7c, 0x1e); +        dev->reg.init_reg(0x7d, 0x11); +        dev->reg.init_reg(0x7e, 0x00); +        dev->reg.init_reg(0x7f, 0x50); +        dev->reg.init_reg(0x80, 0x00); +        dev->reg.init_reg(0x81, 0x00); +        dev->reg.init_reg(0x82, 0x0f); +        dev->reg.init_reg(0x83, 0x00); +        dev->reg.init_reg(0x84, 0x0e); +        dev->reg.init_reg(0x85, 0x00); +        dev->reg.init_reg(0x86, 0x0d); +        dev->reg.init_reg(0x87, 0x02); +        dev->reg.init_reg(0x88, 0x00); +        dev->reg.init_reg(0x89, 0x00); +    } else { +        for (unsigned addr = 0x74; addr <= 0x87; addr++) { +            dev->reg.init_reg(addr, 0); +        } +    } -    sanei_gl841_setup_sensor(dev, sensor, &dev->reg, 0, 1); +    scanner_setup_sensor(*dev, sensor, dev->reg);      // set up GPIO      for (const auto& reg : dev->gpo.regs) {          dev->reg.set8(reg.address, reg.value);      } -  /* TODO there is a switch calling to be written here */      if (dev->model->gpio_id == GpioId::CANON_LIDE_35) {          dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO18;          dev->reg.find_reg(0x6b).value &= ~REG_0x6B_GPO17; @@ -523,70 +312,43 @@ gl841_init_registers (Genesys_Device * dev)      if (dev->model->gpio_id == GpioId::DP685) {        /* REG_0x6B_GPO18 lights on green led */ -        dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO17|REG_0x6B_GPO18; +        dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO17 | REG_0x6B_GPO18;      } -  DBG(DBG_proc, "%s complete\n", __func__); -} - -// Send slope table for motor movement slope_table in machine byte order -static void gl841_send_slope_table(Genesys_Device* dev, int table_nr, -                                   const std::vector<uint16_t>& slope_table, -                                   int steps) -{ -    DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %d", table_nr, steps); -  int dpihw; -  int start_address; -  char msg[4000]; -/*#ifdef WORDS_BIGENDIAN*/ -  int i; -/*#endif*/ - -  dpihw = dev->reg.find_reg(0x05).value >> 6; - -  if (dpihw == 0)		/* 600 dpi */ -    start_address = 0x08000; -  else if (dpihw == 1)		/* 1200 dpi */ -    start_address = 0x10000; -  else if (dpihw == 2)		/* 2400 dpi */ -    start_address = 0x20000; -    else { -        throw SaneException("Unexpected dpihw"); -    } - -  std::vector<uint8_t> table(steps * 2); -  for(i = 0; i < steps; i++) { -      table[i * 2] = slope_table[i] & 0xff; -      table[i * 2 + 1] = slope_table[i] >> 8; -  } - -  if (DBG_LEVEL >= DBG_io) -    { -        std::sprintf(msg, "write slope %d (%d)=", table_nr, steps); -        for (i = 0; i < steps; i++) { -            std::sprintf (msg+strlen(msg), ",%d", slope_table[i]); -	} -      DBG(DBG_io, "%s: %s\n", __func__, msg); -    } - -    if (dev->interface->is_mock()) { -        dev->interface->record_slope_table(table_nr, slope_table); +    if (dev->model->model_id == ModelId::CANON_LIDE_80) { +        // specific scanner settings, clock and gpio first +        dev->interface->write_register(REG_0x6B, 0x0c); +        dev->interface->write_register(0x06, 0x10); +        dev->interface->write_register(REG_0x6E, 0x6d); +        dev->interface->write_register(REG_0x6F, 0x80); +        dev->interface->write_register(REG_0x6B, 0x0e); +        dev->interface->write_register(REG_0x6C, 0x00); +        dev->interface->write_register(REG_0x6D, 0x8f); +        dev->interface->write_register(REG_0x6B, 0x0e); +        dev->interface->write_register(REG_0x6B, 0x0e); +        dev->interface->write_register(REG_0x6B, 0x0a); +        dev->interface->write_register(REG_0x6B, 0x02); +        dev->interface->write_register(REG_0x6B, 0x06); + +        dev->interface->write_0x8c(0x10, 0x94); +        dev->interface->write_register(0x09, 0x10); + +        // FIXME: the following code originally changed 0x6b, but due to bug the 0x6c register was +        // effectively changed. The current behavior matches the old code, but should probably be fixed. +        dev->reg.find_reg(0x6c).value |= REG_0x6B_GPO18; +        dev->reg.find_reg(0x6c).value &= ~REG_0x6B_GPO17;      } -    dev->interface->write_buffer(0x3c, start_address + table_nr * 0x200, table.data(), steps * 2);  }  static void gl841_set_lide80_fe(Genesys_Device* dev, uint8_t set)  {      DBG_HELPER(dbg); -  if (set == AFE_INIT) -    { -        DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, -            static_cast<unsigned>(dev->model->adc_id)); - -      dev->frontend = dev->frontend_initial; +    if (set == AFE_INIT) { +        dev->frontend = dev->frontend_initial; -        // write them to analog frontend +        // BUG: the following code does not make sense. The addresses are different than AFE_SET +        // case          dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00));          dev->interface->write_fe_register(0x03, dev->frontend.regs.get_value(0x01));          dev->interface->write_fe_register(0x06, dev->frontend.regs.get_value(0x02)); @@ -611,11 +373,7 @@ static void gl841_set_ad_fe(Genesys_Device* dev, uint8_t set)          return;      } -  if (set == AFE_INIT) -    { -        DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, -            static_cast<unsigned>(dev->model->adc_id)); - +    if (set == AFE_INIT) {        dev->frontend = dev->frontend_initial;          // write them to analog frontend @@ -674,15 +432,11 @@ void CommandSetGl841::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor,          throw SaneException("unsupported frontend type %d", frontend_type);      } -  if (set == AFE_INIT) -    { -        DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, -            static_cast<unsigned>(dev->model->adc_id)); -      dev->frontend = dev->frontend_initial; +    if (set == AFE_INIT) { +        dev->frontend = dev->frontend_initial;          // reset only done on init          dev->interface->write_fe_register(0x04, 0x80); -      DBG(DBG_proc, "%s(): frontend reset complete\n", __func__);      } @@ -712,71 +466,34 @@ void CommandSetGl841::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor,      }  } -enum MotorAction { -    MOTOR_ACTION_FEED = 1, -    MOTOR_ACTION_GO_HOME = 2, -    MOTOR_ACTION_HOME_FREE = 3 -}; -  // @brief turn off motor  static void gl841_init_motor_regs_off(Genesys_Register_Set* reg, unsigned int scan_lines)  {      DBG_HELPER_ARGS(dbg, "scan_lines=%d", scan_lines);      unsigned int feedl; -    GenesysRegister* r;      feedl = 2; -    r = sanei_genesys_get_address (reg, 0x3d); -    r->value = (feedl >> 16) & 0xf; -    r = sanei_genesys_get_address (reg, 0x3e); -    r->value = (feedl >> 8) & 0xff; -    r = sanei_genesys_get_address (reg, 0x3f); -    r->value = feedl & 0xff; -    r = sanei_genesys_get_address (reg, 0x5e); -    r->value &= ~0xe0; - -    r = sanei_genesys_get_address (reg, 0x25); -    r->value = (scan_lines >> 16) & 0xf; -    r = sanei_genesys_get_address (reg, 0x26); -    r->value = (scan_lines >> 8) & 0xff; -    r = sanei_genesys_get_address (reg, 0x27); -    r->value = scan_lines & 0xff; - -    r = sanei_genesys_get_address (reg, 0x02); -    r->value &= ~0x01; /*LONGCURV OFF*/ -    r->value &= ~0x80; /*NOT_HOME OFF*/ - -    r->value &= ~0x10; - -    r->value &= ~0x06; - -    r->value &= ~0x08; +    reg->set8(0x3d, (feedl >> 16) & 0xf); +    reg->set8(0x3e, (feedl >> 8) & 0xff); +    reg->set8(0x3f, feedl & 0xff); +    reg->find_reg(0x5e).value &= ~0xe0; -    r->value &= ~0x20; +    reg->set8(0x25, (scan_lines >> 16) & 0xf); +    reg->set8(0x26, (scan_lines >> 8) & 0xff); +    reg->set8(0x27, scan_lines & 0xff); -    r->value &= ~0x40; +    reg->set8(0x02, 0x00); -    r = sanei_genesys_get_address (reg, 0x67); -    r->value = 0x3f; +    reg->set8(0x67, 0x3f); +    reg->set8(0x68, 0x3f); -    r = sanei_genesys_get_address (reg, 0x68); -    r->value = 0x3f; +    reg->set8(REG_STEPNO, 1); +    reg->set8(REG_FASTNO, 1); -    r = sanei_genesys_get_address(reg, REG_STEPNO); -    r->value = 0; - -    r = sanei_genesys_get_address(reg, REG_FASTNO); -    r->value = 0; - -    r = sanei_genesys_get_address (reg, 0x69); -    r->value = 0; - -    r = sanei_genesys_get_address (reg, 0x6a); -    r->value = 0; - -    r = sanei_genesys_get_address (reg, 0x5f); -    r->value = 0; +    reg->set8(0x69, 1); +    reg->set8(0x6a, 1); +    reg->set8(0x5f, 1);  }  /** @brief write motor table frequency @@ -814,207 +531,122 @@ uint8_t *table;              table=tdefault;          }          dev->interface->write_register(0x66, 0x00); -        dev->interface->write_gamma(0x28, 0xc000, table, 128, -                                    ScannerInterface::FLAG_SWAP_REGISTERS); +        dev->interface->write_gamma(0x28, 0xc000, table, 128);          dev->interface->write_register(0x5b, 0x00);          dev->interface->write_register(0x5c, 0x00);      }  } - -static void gl841_init_motor_regs(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                  Genesys_Register_Set* reg, unsigned int feed_steps,/*1/base_ydpi*/ -                                  /*maybe float for half/quarter step resolution?*/ -                                  unsigned int action, MotorFlag flags) +static void gl841_init_motor_regs_feed(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                       Genesys_Register_Set* reg, unsigned int feed_steps,/*1/base_ydpi*/ +                                       ScanFlag flags)  { -    DBG_HELPER_ARGS(dbg, "feed_steps=%d, action=%d, flags=%x", feed_steps, action, -                    static_cast<unsigned>(flags)); -    unsigned int fast_exposure = 0; +    DBG_HELPER_ARGS(dbg, "feed_steps=%d, flags=%x", feed_steps, static_cast<unsigned>(flags)); +    unsigned step_multiplier = 2;      int use_fast_fed = 0;      unsigned int feedl; -    GenesysRegister* r;  /*number of scan lines to add in a scan_lines line*/      {          std::vector<uint16_t> table;          table.resize(256, 0xffff); -        gl841_send_slope_table(dev, 0, table, 256); -        gl841_send_slope_table(dev, 1, table, 256); -        gl841_send_slope_table(dev, 2, table, 256); -        gl841_send_slope_table(dev, 3, table, 256); -        gl841_send_slope_table(dev, 4, table, 256); +        scanner_send_slope_table(dev, sensor, 0, table); +        scanner_send_slope_table(dev, sensor, 1, table); +        scanner_send_slope_table(dev, sensor, 2, table); +        scanner_send_slope_table(dev, sensor, 3, table); +        scanner_send_slope_table(dev, sensor, 4, table);      }      gl841_write_freq(dev, dev->motor.base_ydpi / 4); -    if (action == MOTOR_ACTION_FEED || action == MOTOR_ACTION_GO_HOME) { -        /* FEED and GO_HOME can use fastest slopes available */ -        fast_exposure = gl841_exposure_time(dev, sensor, -                                            dev->motor.base_ydpi / 4, -                                            StepType::FULL, -                                            0, -                                            0); -        DBG(DBG_info, "%s : fast_exposure=%d pixels\n", __func__, fast_exposure); -      } +    // FIXME: use proper scan session +    ScanSession session; +    session.params.yres = dev->motor.base_ydpi; +    session.params.scan_method = dev->model->default_method; -    if (action == MOTOR_ACTION_HOME_FREE) { -/* HOME_FREE must be able to stop in one step, so do not try to get faster */ -        fast_exposure = dev->motor.get_slope(StepType::FULL).max_speed_w; +    const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); +    if (fast_profile == nullptr) { +        fast_profile = get_motor_profile_ptr(dev->motor.profiles, 0, session);      } +    auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, +                                                 *fast_profile); -    auto fast_table = sanei_genesys_create_slope_table3(dev->model->asic_type, dev->motor, -                                                        StepType::FULL, fast_exposure, -                                                        dev->motor.base_ydpi / 4); - -    feedl = feed_steps - fast_table.steps_count * 2; +    // BUG: fast table is counted in base_ydpi / 4 +    feedl = feed_steps - fast_table.table.size() * 2;      use_fast_fed = 1; +    if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) { +        use_fast_fed = false; +    } -/* all needed slopes available. we did even decide which mode to use. -   what next? -   - transfer slopes -SCAN: -flags \ use_fast_fed    ! 0         1 -------------------------\-------------------- -                      0 ! 0,1,2     0,1,2,3 -MotorFlag::AUTO_GO_HOME ! 0,1,2,4   0,1,2,3,4 -OFF:       none -FEED:      3 -GO_HOME:   3 -HOME_FREE: 3 -   - setup registers -     * slope specific registers (already done) -     * DECSEL for HOME_FREE/GO_HOME/SCAN -     * FEEDL -     * MTRREV -     * MTRPWR -     * FASTFED -     * STEPSEL -     * MTRPWM -     * FSTPSEL -     * FASTPWM -     * HOMENEG -     * BWDSTEP -     * FWDSTEP -     * Z1 -     * Z2 - */ +    reg->set8(0x3d, (feedl >> 16) & 0xf); +    reg->set8(0x3e, (feedl >> 8) & 0xff); +    reg->set8(0x3f, feedl & 0xff); +    reg->find_reg(0x5e).value &= ~0xe0; -    r = sanei_genesys_get_address(reg, 0x3d); -    r->value = (feedl >> 16) & 0xf; -    r = sanei_genesys_get_address(reg, 0x3e); -    r->value = (feedl >> 8) & 0xff; -    r = sanei_genesys_get_address(reg, 0x3f); -    r->value = feedl & 0xff; -    r = sanei_genesys_get_address(reg, 0x5e); -    r->value &= ~0xe0; - -    r = sanei_genesys_get_address(reg, 0x25); -    r->value = 0; -    r = sanei_genesys_get_address(reg, 0x26); -    r->value = 0; -    r = sanei_genesys_get_address(reg, 0x27); -    r->value = 0; - -    r = sanei_genesys_get_address(reg, 0x02); -    r->value &= ~0x01; /*LONGCURV OFF*/ -    r->value &= ~0x80; /*NOT_HOME OFF*/ - -    r->value |= 0x10; - -    if (action == MOTOR_ACTION_GO_HOME) -	r->value |= 0x06; -    else -	r->value &= ~0x06; +    reg->set8(0x25, 0); +    reg->set8(0x26, 0); +    reg->set8(0x27, 0); + +    reg->find_reg(0x02).value &= ~0x01; /*LONGCURV OFF*/ +    reg->find_reg(0x02).value &= ~0x80; /*NOT_HOME OFF*/ + +    reg->find_reg(0x02).value |= REG_0x02_MTRPWR;      if (use_fast_fed) -	r->value |= 0x08; +    reg->find_reg(0x02).value |= 0x08;      else -	r->value &= ~0x08; +    reg->find_reg(0x02).value &= ~0x08; -    if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) { -        r->value |= 0x20; +    if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { +        reg->find_reg(0x02).value |= 0x20;      } else { -        r->value &= ~0x20; +        reg->find_reg(0x02).value &= ~0x20;      } -    r->value &= ~0x40; +    reg->find_reg(0x02).value &= ~0x40; -    if (has_flag(flags, MotorFlag::REVERSE)) { -        r->value |= REG_0x02_MTRREV; +    if (has_flag(flags, ScanFlag::REVERSE)) { +        reg->find_reg(0x02).value |= REG_0x02_MTRREV; +    } else { +        reg->find_reg(0x02).value &= ~REG_0x02_MTRREV;      } -    gl841_send_slope_table(dev, 3, fast_table.table, 256); +    scanner_send_slope_table(dev, sensor, 3, fast_table.table); -    r = sanei_genesys_get_address(reg, 0x67); -    r->value = 0x3f; - -    r = sanei_genesys_get_address(reg, 0x68); -    r->value = 0x3f; - -    r = sanei_genesys_get_address(reg, REG_STEPNO); -    r->value = 0; - -    r = sanei_genesys_get_address(reg, REG_FASTNO); -    r->value = 0; - -    r = sanei_genesys_get_address(reg, 0x69); -    r->value = 0; - -    r = sanei_genesys_get_address(reg, 0x6a); -    r->value = (fast_table.steps_count >> 1) + (fast_table.steps_count & 1); - -    r = sanei_genesys_get_address(reg, 0x5f); -    r->value = (fast_table.steps_count >> 1) + (fast_table.steps_count & 1); +    reg->set8(0x67, 0x3f); +    reg->set8(0x68, 0x3f); +    reg->set8(REG_STEPNO, 1); +    reg->set8(REG_FASTNO, 1); +    reg->set8(0x69, 1); +    reg->set8(0x6a, fast_table.table.size() / step_multiplier); +    reg->set8(0x5f, 1);  }  static void gl841_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                       Genesys_Register_Set* reg, +                                       const ScanSession& session, +                                       Genesys_Register_Set* reg, const MotorProfile& motor_profile,                                         unsigned int scan_exposure_time,/*pixel*/                                         unsigned scan_yres, // dpi, motor resolution -                                       StepType scan_step_type,                                         unsigned int scan_lines,/*lines, scan resolution*/                                         unsigned int scan_dummy,                                         // number of scan lines to add in a scan_lines line                                         unsigned int feed_steps,/*1/base_ydpi*/                                         // maybe float for half/quarter step resolution? -                                       MotorFlag flags) +                                       ScanFlag flags)  {      DBG_HELPER_ARGS(dbg, "scan_exposure_time=%d, scan_yres=%d, scan_step_type=%d, scan_lines=%d,"                           " scan_dummy=%d, feed_steps=%d, flags=%x", -                    scan_exposure_time, scan_yres, static_cast<unsigned>(scan_step_type), +                    scan_exposure_time, scan_yres, static_cast<unsigned>(motor_profile.step_type),                      scan_lines, scan_dummy, feed_steps, static_cast<unsigned>(flags)); -    unsigned int fast_exposure; + +    unsigned step_multiplier = 2; +      int use_fast_fed = 0;      unsigned int fast_time;      unsigned int slow_time;      unsigned int feedl; -    GenesysRegister* r;      unsigned int min_restep = 0x20; -    uint32_t z1, z2; - -    fast_exposure = gl841_exposure_time(dev, sensor, -                                        dev->motor.base_ydpi / 4, -                                        StepType::FULL, -                                        0, -                                        0); - -    DBG(DBG_info, "%s : fast_exposure=%d pixels\n", __func__, fast_exposure); - -    { -        std::vector<uint16_t> table; -        table.resize(256, 0xffff); - -        gl841_send_slope_table(dev, 0, table, 256); -        gl841_send_slope_table(dev, 1, table, 256); -        gl841_send_slope_table(dev, 2, table, 256); -        gl841_send_slope_table(dev, 3, table, 256); -        gl841_send_slope_table(dev, 4, table, 256); -    } - - -    /* motor frequency table */ -    gl841_write_freq(dev, scan_yres);  /*    we calculate both tables for SCAN. the fast slope step count depends on @@ -1022,30 +654,31 @@ static void gl841_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor    allowed to use.   */ -    auto slow_table = sanei_genesys_create_slope_table3(dev->model->asic_type, dev->motor, -                                                        scan_step_type, scan_exposure_time, -                                                        scan_yres); +    // At least in LiDE 50, 60 the fast movement table is counted in full steps. +    const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); +    if (fast_profile == nullptr) { +        fast_profile = &motor_profile; +    } -    auto back_table = sanei_genesys_create_slope_table3(dev->model->asic_type, dev->motor, -                                                        scan_step_type, 0, scan_yres); +    auto slow_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, +                                         scan_exposure_time, step_multiplier, motor_profile); -    if (feed_steps < (slow_table.steps_count >> static_cast<unsigned>(scan_step_type))) { +    if (feed_steps < (slow_table.table.size() >> static_cast<unsigned>(motor_profile.step_type))) {  	/*TODO: what should we do here?? go back to exposure calculation?*/ -        feed_steps = slow_table.steps_count >> static_cast<unsigned>(scan_step_type); +        feed_steps = slow_table.table.size() >> static_cast<unsigned>(motor_profile.step_type);      } -    auto fast_table = sanei_genesys_create_slope_table3(dev->model->asic_type, dev->motor, -                                                        StepType::FULL, fast_exposure, -                                                        dev->motor.base_ydpi / 4); +    auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, +                                                 *fast_profile); -    unsigned max_fast_slope_steps_count = 1; -    if (feed_steps > (slow_table.steps_count >> static_cast<unsigned>(scan_step_type)) + 2) { +    unsigned max_fast_slope_steps_count = step_multiplier; +    if (feed_steps > (slow_table.table.size() >> static_cast<unsigned>(motor_profile.step_type)) + 2) {          max_fast_slope_steps_count = (feed_steps - -            (slow_table.steps_count >> static_cast<unsigned>(scan_step_type))) / 2; +            (slow_table.table.size() >> static_cast<unsigned>(motor_profile.step_type))) / 2;      } -    if (fast_table.steps_count > max_fast_slope_steps_count) { -        fast_table.slice_steps(max_fast_slope_steps_count); +    if (fast_table.table.size() > max_fast_slope_steps_count) { +        fast_table.slice_steps(max_fast_slope_steps_count, step_multiplier);      }      /* fast fed special cases handling */ @@ -1056,8 +689,8 @@ static void gl841_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor  	   2-feed mode */  	use_fast_fed = 0;        } -    else if (feed_steps < fast_table.steps_count * 2 + -             (slow_table.steps_count >> static_cast<unsigned>(scan_step_type))) +    else if (feed_steps < fast_table.table.size() * 2 + +             (slow_table.table.size() >> static_cast<unsigned>(motor_profile.step_type)))      {          use_fast_fed = 0;          DBG(DBG_info, "%s: feed too short, slow move forced.\n", __func__); @@ -1071,113 +704,70 @@ static void gl841_init_motor_regs_scan(Genesys_Device* dev, const Genesys_Sensor  /*NOTE: fast_exposure is per base_ydpi/4*/  /*we use full steps as base unit here*/  	fast_time = -	    fast_exposure / 4 * -        (feed_steps - fast_table.steps_count*2 - -         (slow_table.steps_count >> static_cast<unsigned>(scan_step_type))) -        + fast_table.pixeltime_sum*2 + slow_table.pixeltime_sum; +        (fast_table.table.back() << static_cast<unsigned>(fast_profile->step_type)) / 4 * +        (feed_steps - fast_table.table.size()*2 - +         (slow_table.table.size() >> static_cast<unsigned>(motor_profile.step_type))) +        + fast_table.pixeltime_sum() * 2 + slow_table.pixeltime_sum();  	slow_time =  	    (scan_exposure_time * scan_yres) / dev->motor.base_ydpi * -        (feed_steps - (slow_table.steps_count >> static_cast<unsigned>(scan_step_type))) -        + slow_table.pixeltime_sum; +        (feed_steps - (slow_table.table.size() >> static_cast<unsigned>(motor_profile.step_type))) +        + slow_table.pixeltime_sum(); -	DBG(DBG_info, "%s: Time for slow move: %d\n", __func__, slow_time); -	DBG(DBG_info, "%s: Time for fast move: %d\n", __func__, fast_time); +        use_fast_fed = fast_time < slow_time; +    } -	use_fast_fed = fast_time < slow_time; +    if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) { +        use_fast_fed = false;      }      if (use_fast_fed) { -        feedl = feed_steps - fast_table.steps_count * 2 - -                (slow_table.steps_count >> static_cast<unsigned>(scan_step_type)); -    } else if ((feed_steps << static_cast<unsigned>(scan_step_type)) < slow_table.steps_count) { +        feedl = feed_steps - fast_table.table.size() * 2 - +                (slow_table.table.size() >> static_cast<unsigned>(motor_profile.step_type)); +    } else if ((feed_steps << static_cast<unsigned>(motor_profile.step_type)) < slow_table.table.size()) {          feedl = 0;      } else { -        feedl = (feed_steps << static_cast<unsigned>(scan_step_type)) - slow_table.steps_count; +        feedl = (feed_steps << static_cast<unsigned>(motor_profile.step_type)) - slow_table.table.size();      }      DBG(DBG_info, "%s: Decided to use %s mode\n", __func__, use_fast_fed?"fast feed":"slow feed"); -/* all needed slopes available. we did even decide which mode to use. -   what next? -   - transfer slopes -SCAN: -flags \ use_fast_fed    ! 0         1 -------------------------\-------------------- -                      0 ! 0,1,2     0,1,2,3 -MotorFlag::AUTO_GO_HOME ! 0,1,2,4   0,1,2,3,4 -OFF:       none -FEED:      3 -GO_HOME:   3 -HOME_FREE: 3 -   - setup registers -     * slope specific registers (already done) -     * DECSEL for HOME_FREE/GO_HOME/SCAN -     * FEEDL -     * MTRREV -     * MTRPWR -     * FASTFED -     * STEPSEL -     * MTRPWM -     * FSTPSEL -     * FASTPWM -     * HOMENEG -     * BWDSTEP -     * FWDSTEP -     * Z1 -     * Z2 - */ - -    r = sanei_genesys_get_address (reg, 0x3d); -    r->value = (feedl >> 16) & 0xf; -    r = sanei_genesys_get_address (reg, 0x3e); -    r->value = (feedl >> 8) & 0xff; -    r = sanei_genesys_get_address (reg, 0x3f); -    r->value = feedl & 0xff; -    r = sanei_genesys_get_address (reg, 0x5e); -    r->value &= ~0xe0; - -    r = sanei_genesys_get_address (reg, 0x25); -    r->value = (scan_lines >> 16) & 0xf; -    r = sanei_genesys_get_address (reg, 0x26); -    r->value = (scan_lines >> 8) & 0xff; -    r = sanei_genesys_get_address (reg, 0x27); -    r->value = scan_lines & 0xff; - -    r = sanei_genesys_get_address (reg, 0x02); -    r->value &= ~0x01; /*LONGCURV OFF*/ -    r->value &= ~0x80; /*NOT_HOME OFF*/ -    r->value |= 0x10; - -    r->value &= ~0x06; +    reg->set8(0x3d, (feedl >> 16) & 0xf); +    reg->set8(0x3e, (feedl >> 8) & 0xff); +    reg->set8(0x3f, feedl & 0xff); +    reg->find_reg(0x5e).value &= ~0xe0; +    reg->set8(0x25, (scan_lines >> 16) & 0xf); +    reg->set8(0x26, (scan_lines >> 8) & 0xff); +    reg->set8(0x27, scan_lines & 0xff); +    reg->find_reg(0x02).value = REG_0x02_MTRPWR; + +    if (has_flag(flags, ScanFlag::REVERSE)) { +        reg->find_reg(0x02).value |= REG_0x02_MTRREV; +    } else { +        reg->find_reg(0x02).value &= ~REG_0x02_MTRREV; +    }      if (use_fast_fed) -	r->value |= 0x08; +    reg->find_reg(0x02).value |= 0x08;      else -	r->value &= ~0x08; +    reg->find_reg(0x02).value &= ~0x08; -    if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) -	r->value |= 0x20; +    if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) +    reg->find_reg(0x02).value |= 0x20;      else -	r->value &= ~0x20; +    reg->find_reg(0x02).value &= ~0x20; -    if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE)) { -        r->value |= 0x40; +    if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE)) { +        reg->find_reg(0x02).value |= 0x40;      } else { -        r->value &= ~0x40; +        reg->find_reg(0x02).value &= ~0x40;      } -    gl841_send_slope_table(dev, 0, slow_table.table, 256); - -    gl841_send_slope_table(dev, 1, back_table.table, 256); +    scanner_send_slope_table(dev, sensor, 0, slow_table.table); +    scanner_send_slope_table(dev, sensor, 1, slow_table.table); +    scanner_send_slope_table(dev, sensor, 2, slow_table.table); +    scanner_send_slope_table(dev, sensor, 3, fast_table.table); +    scanner_send_slope_table(dev, sensor, 4, fast_table.table); -    gl841_send_slope_table(dev, 2, slow_table.table, 256); - -    if (use_fast_fed) { -        gl841_send_slope_table(dev, 3, fast_table.table, 256); -    } - -    if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) { -        gl841_send_slope_table(dev, 4, fast_table.table, 256); -    } +    gl841_write_freq(dev, scan_yres);  /* now reg 0x21 and 0x24 are available, we can calculate reg 0x22 and 0x23,     reg 0x60-0x62 and reg 0x63-0x65 @@ -1185,19 +775,18 @@ HOME_FREE: 3     2*STEPNO+FWDSTEP=2*FASTNO+BWDSTEP  */  /* steps of table 0*/ -    if (min_restep < slow_table.steps_count * 2 + 2) { -        min_restep = slow_table.steps_count * 2 + 2; +    if (min_restep < slow_table.table.size() * 2 + 2) { +        min_restep = slow_table.table.size() * 2 + 2;      }  /* steps of table 1*/ -    if (min_restep < back_table.steps_count * 2 + 2) { -        min_restep = back_table.steps_count * 2 + 2; +    if (min_restep < slow_table.table.size() * 2 + 2) { +        min_restep = slow_table.table.size() * 2 + 2;      }  /* steps of table 0*/ -    r = sanei_genesys_get_address(reg, REG_FWDSTEP); -    r->value = min_restep - slow_table.steps_count*2; +    reg->set8(REG_FWDSTEP, min_restep - slow_table.table.size()*2); +  /* steps of table 1*/ -    r = sanei_genesys_get_address(reg, REG_BWDSTEP); -    r->value = min_restep - back_table.steps_count*2; +    reg->set8(REG_BWDSTEP, min_restep - slow_table.table.size()*2);  /*    for z1/z2: @@ -1214,64 +803,17 @@ HOME_FREE: 3     z1 = (slope_0_time-1) % exposure_time;     z2 = (slope_0_time-1) % exposure_time;  */ -    z1 = z2 = 0; - -    DBG(DBG_info, "%s: z1 = %d\n", __func__, z1); -    DBG(DBG_info, "%s: z2 = %d\n", __func__, z2); -    r = sanei_genesys_get_address (reg, 0x60); -    r->value = ((z1 >> 16) & 0xff); -    r = sanei_genesys_get_address (reg, 0x61); -    r->value = ((z1 >> 8) & 0xff); -    r = sanei_genesys_get_address (reg, 0x62); -    r->value = (z1 & 0xff); -    r = sanei_genesys_get_address (reg, 0x63); -    r->value = ((z2 >> 16) & 0xff); -    r = sanei_genesys_get_address (reg, 0x64); -    r->value = ((z2 >> 8) & 0xff); -    r = sanei_genesys_get_address (reg, 0x65); -    r->value = (z2 & 0xff); - -    r = sanei_genesys_get_address(reg, REG_0x1E); -    r->value &= REG_0x1E_WDTIME; -    r->value |= scan_dummy; - -    r = sanei_genesys_get_address (reg, 0x67); -    r->value = 0x3f | (static_cast<unsigned>(scan_step_type) << 6); - -    r = sanei_genesys_get_address (reg, 0x68); -    r->value = 0x3f; - -    r = sanei_genesys_get_address(reg, REG_STEPNO); -    r->value = (slow_table.steps_count >> 1) + (slow_table.steps_count & 1); - -    r = sanei_genesys_get_address(reg, REG_FASTNO); -    r->value = (back_table.steps_count >> 1) + (back_table.steps_count & 1); - -    r = sanei_genesys_get_address (reg, 0x69); -    r->value = (slow_table.steps_count >> 1) + (slow_table.steps_count & 1); - -    r = sanei_genesys_get_address (reg, 0x6a); -    r->value = (fast_table.steps_count >> 1) + (fast_table.steps_count & 1); - -    r = sanei_genesys_get_address (reg, 0x5f); -    r->value = (fast_table.steps_count >> 1) + (fast_table.steps_count & 1); -} - -static int -gl841_get_dpihw(Genesys_Device * dev) -{ -  GenesysRegister* r; -  r = sanei_genesys_get_address(&dev->reg, 0x05); -    if ((r->value & REG_0x05_DPIHW) == REG_0x05_DPIHW_600) { -        return 600; -    } -    if ((r->value & REG_0x05_DPIHW) == REG_0x05_DPIHW_1200) { -        return 1200; -    } -    if ((r->value & REG_0x05_DPIHW) == REG_0x05_DPIHW_2400) { -        return 2400; -    } -  return 0; +    reg->set24(REG_0x60, 0); +    reg->set24(REG_0x63, 0); +    reg->find_reg(REG_0x1E).value &= REG_0x1E_WDTIME; +    reg->find_reg(REG_0x1E).value |= scan_dummy; +    reg->set8(0x67, 0x3f | (static_cast<unsigned>(motor_profile.step_type) << 6)); +    reg->set8(0x68, 0x3f | (static_cast<unsigned>(fast_profile->step_type) << 6)); +    reg->set8(REG_STEPNO, slow_table.table.size() / step_multiplier); +    reg->set8(REG_FASTNO, slow_table.table.size() / step_multiplier); +    reg->set8(0x69, slow_table.table.size() / step_multiplier); +    reg->set8(0x6a, fast_table.table.size() / step_multiplier); +    reg->set8(0x5f, fast_table.table.size() / step_multiplier);  }  static void gl841_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, @@ -1279,108 +821,99 @@ static void gl841_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens                                           const ScanSession& session)  {      DBG_HELPER_ARGS(dbg, "exposure_time=%d", exposure_time); -    GenesysRegister* r;      uint16_t expavg, expr, expb, expg;      dev->cmd_set->set_fe(dev, sensor, AFE_SET);      /* gpio part.*/      if (dev->model->gpio_id == GpioId::CANON_LIDE_35) { -        r = sanei_genesys_get_address(reg, REG_0x6C); -        if (session.ccd_size_divisor > 1) { -            r->value &= ~0x80; +        if (session.params.xres <= 600) { +            reg->find_reg(REG_0x6C).value &= ~0x80;          } else { -            r->value |= 0x80; +            reg->find_reg(REG_0x6C).value |= 0x80;          }        }      if (dev->model->gpio_id == GpioId::CANON_LIDE_80) { -        r = sanei_genesys_get_address(reg, REG_0x6C); -        if (session.ccd_size_divisor > 1) { -	    r->value &= ~0x40; -	    r->value |= 0x20; +        if (session.params.xres <= 600) { +            reg->find_reg(REG_0x6C).value &= ~0x40; +            reg->find_reg(REG_0x6C).value |= 0x20;          } else { -	    r->value &= ~0x20; -	    r->value |= 0x40; +            reg->find_reg(REG_0x6C).value &= ~0x20; +            reg->find_reg(REG_0x6C).value |= 0x40;          } -      } +    }      /* enable shading */ -    r = sanei_genesys_get_address (reg, 0x01); -    r->value |= REG_0x01_SCAN; +    reg->find_reg(0x01).value |= REG_0x01_SCAN;      if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || -        (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)) { -        r->value &= ~REG_0x01_DVDSET; +        has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION)) { +        reg->find_reg(0x01).value &= ~REG_0x01_DVDSET;      } else { -        r->value |= REG_0x01_DVDSET; +        reg->find_reg(0x01).value |= REG_0x01_DVDSET;      }      /* average looks better than deletion, and we are already set up to         use  one of the average enabled resolutions      */ -    r = sanei_genesys_get_address (reg, 0x03); -    r->value |= REG_0x03_AVEENB; +    reg->find_reg(0x03).value |= REG_0x03_AVEENB;      sanei_genesys_set_lamp_power(dev, sensor, *reg,                                   !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP));      /* BW threshold */ -    r = sanei_genesys_get_address (reg, 0x2e); -    r->value = dev->settings.threshold; -    r = sanei_genesys_get_address (reg, 0x2f); -    r->value = dev->settings.threshold; +    reg->set8(0x2e, 0x7f); +    reg->set8(0x2f, 0x7f);      /* monochrome / color scan */ -    r = sanei_genesys_get_address (reg, 0x04);      switch (session.params.depth) {  	case 8: -            r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); +            reg->find_reg(0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET);  	    break;  	case 16: -            r->value &= ~REG_0x04_LINEART; -            r->value |= REG_0x04_BITSET; +            reg->find_reg(0x04).value &= ~REG_0x04_LINEART; +            reg->find_reg(0x04).value |= REG_0x04_BITSET;  	    break;      }      /* AFEMOD should depend on FESET, and we should set these       * bits separately */ -    r->value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); +    reg->find_reg(0x04).value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD);      if (has_flag(session.params.flags, ScanFlag::ENABLE_LEDADD)) { -        r->value |= 0x10;	/* no filter */ +        reg->find_reg(0x04).value |= 0x10;	/* no filter */      }      else if (session.params.channels == 1)        {      switch (session.params.color_filter)  	  {              case ColorFilter::RED: -                r->value |= 0x14; +                reg->find_reg(0x04).value |= 0x14;                  break;              case ColorFilter::GREEN: -                r->value |= 0x18; +                reg->find_reg(0x04).value |= 0x18;                  break;              case ColorFilter::BLUE: -                r->value |= 0x1c; +                reg->find_reg(0x04).value |= 0x1c;                  break;              default: -                r->value |= 0x10; +                reg->find_reg(0x04).value |= 0x10;                  break;  	  }        }      else        {          if (dev->model->sensor_id == SensorId::CCD_PLUSTEK_OPTICPRO_3600) { -            r->value |= 0x22;	/* slow color pixel by pixel */ +            reg->find_reg(0x04).value |= 0x22;	/* slow color pixel by pixel */            }  	else            { -	    r->value |= 0x10;	/* color pixel by pixel */ +        reg->find_reg(0x04).value |= 0x10;	/* color pixel by pixel */            }        }      /* CIS scanners can do true gray by setting LEDADD */ -    r = sanei_genesys_get_address (reg, 0x87); -    r->value &= ~REG_0x87_LEDADD; +    reg->find_reg(0x87).value &= ~REG_0x87_LEDADD;      if (has_flag(session.params.flags, ScanFlag::ENABLE_LEDADD)) { -        r->value |= REG_0x87_LEDADD; +        reg->find_reg(0x87).value |= REG_0x87_LEDADD;          expr = reg->get16(REG_EXPR);          expg = reg->get16(REG_EXPG);          expb = reg->get16(REG_EXPB); @@ -1405,21 +938,14 @@ static void gl841_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens      }      /* sensor parameters */ -    sanei_gl841_setup_sensor(dev, sensor, &dev->reg, 1, session.ccd_size_divisor); - -    r = sanei_genesys_get_address (reg, 0x29); -    r->value = 255; /*<<<"magic" number, only suitable for cis*/ - -    reg->set16(REG_DPISET, gl841_get_dpihw(dev) * session.output_resolution / session.optical_resolution); +    scanner_setup_sensor(*dev, sensor, dev->reg); +    reg->set8(0x29, 255); /*<<<"magic" number, only suitable for cis*/ +    reg->set16(REG_DPISET, sensor.register_dpiset);      reg->set16(REG_STRPIXEL, session.pixel_startx);      reg->set16(REG_ENDPIXEL, session.pixel_endx); -      reg->set24(REG_MAXWD, session.output_line_bytes); -      reg->set16(REG_LPERIOD, exposure_time); - -    r = sanei_genesys_get_address (reg, 0x34); -    r->value = sensor.dummy_pixel; +    reg->set8(0x34, sensor.dummy_pixel);  }  static int @@ -1446,56 +972,17 @@ gl841_get_led_exposure(Genesys_Device * dev, const Genesys_Sensor& sensor)  /** @brief compute exposure time   * Compute exposure time for the device and the given scan resolution   */ -static int -gl841_exposure_time(Genesys_Device *dev, const Genesys_Sensor& sensor, -                    float slope_dpi, -                    StepType scan_step_type, -                    int start, -                    int used_pixels) +static int gl841_exposure_time(Genesys_Device *dev, const Genesys_Sensor& sensor, +                               const MotorProfile& profile, float slope_dpi, +                               int start, +                               int used_pixels)  { -int exposure_time = 0;  int led_exposure;    led_exposure=gl841_get_led_exposure(dev, sensor); -  exposure_time = sanei_genesys_exposure_time2( -      dev, -      slope_dpi, -      scan_step_type, -      start+used_pixels,/*+tgtime? currently done in sanei_genesys_exposure_time2 with tgtime = 32 pixel*/ -      led_exposure); - -  return exposure_time; -} - -/**@brief compute scan_step_type - * Try to do at least 4 steps per line. if that is impossible we will have to - * live with that. - * @param dev device - * @param yres motor resolution - */ -static StepType gl841_scan_step_type(Genesys_Device *dev, int yres) -{ -    StepType type = StepType::FULL; - -  /* TODO : check if there is a bug around the use of max_step_type   */ -  /* should be <=1, need to chek all devices entry in genesys_devices */ -    if (yres * 4 < dev->motor.base_ydpi || dev->motor.max_step_type() == StepType::FULL) { -        type = StepType::FULL; -    } else if (yres * 4 < dev->motor.base_ydpi * 2 || -               dev->motor.max_step_type() <= StepType::HALF) -    { -        type = StepType::HALF; -    } else { -        type = StepType::QUARTER; -    } - -  /* this motor behaves differently */ -    if (dev->model->motor_id==MotorId::CANON_LIDE_80) { -        // driven by 'frequency' tables ? -        type = StepType::FULL; -    } - -    return type; +    return sanei_genesys_exposure_time2(dev, profile, slope_dpi, +                                        start + used_pixels,/*+tgtime? currently done in sanei_genesys_exposure_time2 with tgtime = 32 pixel*/ +                                        led_exposure);  }  void CommandSetGl841::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, @@ -1511,34 +998,6 @@ void CommandSetGl841::init_regs_for_scan_session(Genesys_Device* dev, const Gene    int slope_dpi = 0;    int dummy = 0; -/* -results: - -for scanner: -start -end -dpiset -exposure_time -dummy -z1 -z2 - -for ordered_read: -  dev->words_per_line -  dev->read_factor -  dev->requested_buffer_size -  dev->read_buffer_size -  dev->read_pos -  dev->read_bytes_in_buffer -  dev->read_bytes_left -  dev->max_shift -  dev->stagger - -independent of our calculated values: -  dev->total_bytes_read -  dev->bytes_to_read - */ -  /* dummy */    /* dummy lines: may not be usefull, for instance 250 dpi works with 0 or 1       dummy line. Maybe the dummy line adds correctness since the motor runs @@ -1577,48 +1036,34 @@ dummy \ scanned lines    slope_dpi = slope_dpi * (1 + dummy); -    StepType scan_step_type = gl841_scan_step_type(dev, session.params.yres); -    exposure_time = gl841_exposure_time(dev, sensor, -                    slope_dpi, -                    scan_step_type, -                                        session.pixel_startx, -                                        session.optical_pixels); -  DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time); +    const auto& motor_profile = get_motor_profile(dev->motor.profiles, 0, session); + +    exposure_time = gl841_exposure_time(dev, sensor, motor_profile, slope_dpi, +                                        session.pixel_startx, session.optical_pixels);      gl841_init_optical_regs_scan(dev, sensor, reg, exposure_time, session);      move = session.params.starty; -  DBG(DBG_info, "%s: move=%d steps\n", __func__, move);    /* subtract current head position */      move -= (dev->head_pos(ScanHeadId::PRIMARY) * session.params.yres) / dev->motor.base_ydpi; -  DBG(DBG_info, "%s: move=%d steps\n", __func__, move);    if (move < 0)        move = 0;    /* round it */  /* the move is not affected by dummy -- pierre */ -/*  move = ((move + dummy) / (dummy + 1)) * (dummy + 1); -    DBG(DBG_info, "%s: move=%d steps\n", __func__, move);*/ +/*  move = ((move + dummy) / (dummy + 1)) * (dummy + 1);*/      if (has_flag(session.params.flags, ScanFlag::SINGLE_LINE)) { -        gl841_init_motor_regs_off(reg, dev->model->is_cis ? session.output_line_count * session.params.channels -                                                          : session.output_line_count); +        gl841_init_motor_regs_off(reg, session.optical_line_count);      } else { -        auto motor_flag = has_flag(session.params.flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) ? -                              MotorFlag::DISABLE_BUFFER_FULL_MOVE : MotorFlag::NONE; - -        gl841_init_motor_regs_scan(dev, sensor, reg, exposure_time, slope_dpi, scan_step_type, -                                   dev->model->is_cis ? session.output_line_count * session.params.channels -                                                      : session.output_line_count, -                                   dummy, move, motor_flag); +        gl841_init_motor_regs_scan(dev, sensor, session, reg, motor_profile, exposure_time, +                                   slope_dpi, session.optical_line_count, dummy, move, +                                   session.params.flags);    } -    dev->read_buffer.clear(); -    dev->read_buffer.alloc(session.buffer_size_read); - -    build_image_pipeline(dev, session); +    setup_image_pipeline(*dev, session);      dev->read_active = true; @@ -1634,32 +1079,62 @@ ScanSession CommandSetGl841::calculate_scan_session(const Genesys_Device* dev,                                                      const Genesys_Sensor& sensor,                                                      const Genesys_Settings& settings) const  { -  int start; - -    DBG(DBG_info, "%s ", __func__); +    DBG_HELPER(dbg);      debug_dump(DBG_info, settings); -/* start */ -    start = static_cast<int>(dev->model->x_offset); -    start += static_cast<int>(settings.tl_x); +    /* steps to move to reach scanning area: +       - first we move to physical start of scanning +       either by a fixed steps amount from the black strip +       or by a fixed amount from parking position, +       minus the steps done during shading calibration +       - then we move by the needed offset whitin physical +       scanning area + +       assumption: steps are expressed at maximum motor resolution + +       we need: +       float y_offset; +       float y_size; +       float y_offset_calib; +       mm_to_steps()=motor dpi / 2.54 / 10=motor dpi / MM_PER_INCH +    */ +    float move = dev->model->y_offset; +    move += dev->settings.tl_y; -    start = static_cast<int>((start * sensor.optical_res) / MM_PER_INCH); +    int move_dpi = dev->motor.base_ydpi; +    move = static_cast<float>((move * move_dpi) / MM_PER_INCH); -    ScanSession session; -    session.params.xres = settings.xres; -    session.params.yres = settings.yres; -    session.params.startx = start; -    session.params.starty = 0; // not used -    session.params.pixels = settings.pixels; -    session.params.requested_pixels = settings.requested_pixels; -    session.params.lines = settings.lines; -    session.params.depth = settings.depth; -    session.params.channels = settings.get_channels(); -    session.params.scan_method = settings.scan_method; -    session.params.scan_mode = settings.scan_mode; -    session.params.color_filter = settings.color_filter; -    session.params.flags = ScanFlag::NONE; +    float start = dev->model->x_offset; +    start += dev->settings.tl_x; +    start = static_cast<float>((start * dev->settings.xres) / MM_PER_INCH); +    // we enable true gray for cis scanners only, and just when doing +    // scan since color calibration is OK for this mode +    ScanFlag flags = ScanFlag::NONE; + +    // true gray (led add for cis scanners) +    if (dev->model->is_cis && dev->settings.true_gray && +        dev->settings.scan_mode != ScanColorMode::COLOR_SINGLE_PASS && +        dev->model->sensor_id != SensorId::CIS_CANON_LIDE_80) +    { +        // on Lide 80 the LEDADD bit results in only red LED array being lit +        flags |= ScanFlag::ENABLE_LEDADD; +    } + +    ScanSession session; +    session.params.xres = dev->settings.xres; +    session.params.yres = dev->settings.yres; +    session.params.startx = static_cast<unsigned>(start); +    session.params.starty = static_cast<unsigned>(move); +    session.params.pixels = dev->settings.pixels; +    session.params.requested_pixels = dev->settings.requested_pixels; +    session.params.lines = dev->settings.lines; +    session.params.depth = dev->settings.depth; +    session.params.channels = dev->settings.get_channels(); +    session.params.scan_method = dev->settings.scan_method; +    session.params.scan_mode = dev->settings.scan_mode; +    session.params.color_filter = dev->settings.color_filter; +    session.params.flags = flags;      compute_session(dev, session, sensor);      return session; @@ -1709,7 +1184,7 @@ void CommandSetGl841::save_power(Genesys_Device* dev, bool enable) const              uint8_t val = dev->interface->read_register(REG_0x6B);              dev->interface->write_register(REG_0x6B, val & ~REG_0x6B_GPO17);              dev->reg.find_reg(0x6b).value &= ~REG_0x6B_GPO17; -            dev->calib_reg.find_reg(0x6b).value &= ~REG_0x6B_GPO17; +            dev->initial_regs.find_reg(0x6b).value &= ~REG_0x6B_GPO17;  	  }          set_fe(dev, sensor, AFE_POWER_SAVE); @@ -1741,13 +1216,13 @@ void CommandSetGl841::save_power(Genesys_Device* dev, bool enable) const              val = dev->interface->read_register(REG_0x6B);              dev->interface->write_register(REG_0x6B, val | REG_0x6B_GPO17);              dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO17; -            dev->calib_reg.find_reg(0x6b).value |= REG_0x6B_GPO17; +            dev->initial_regs.find_reg(0x6b).value |= REG_0x6B_GPO17;  	    /*enable GPO18*/              val = dev->interface->read_register(REG_0x6B);              dev->interface->write_register(REG_0x6B, val | REG_0x6B_GPO18);              dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO18; -            dev->calib_reg.find_reg(0x6b).value |= REG_0x6B_GPO18; +            dev->initial_regs.find_reg(0x6b).value |= REG_0x6B_GPO18;  	}      if (dev->model->gpio_id == GpioId::DP665 @@ -1756,7 +1231,7 @@ void CommandSetGl841::save_power(Genesys_Device* dev, bool enable) const              uint8_t val = dev->interface->read_register(REG_0x6B);              dev->interface->write_register(REG_0x6B, val | REG_0x6B_GPO17);              dev->reg.find_reg(0x6b).value |= REG_0x6B_GPO17; -            dev->calib_reg.find_reg(0x6b).value |= REG_0x6B_GPO17; +            dev->initial_regs.find_reg(0x6b).value |= REG_0x6B_GPO17;  	  }      } @@ -1826,47 +1301,6 @@ void CommandSetGl841::set_powersaving(Genesys_Device* dev, int delay /* in minut      dev->interface->write_registers(local_reg);  } -static void gl841_stop_action(Genesys_Device* dev) -{ -    DBG_HELPER(dbg); -  Genesys_Register_Set local_reg; -  unsigned int loop; - -    scanner_read_print_status(*dev); - -    if (scanner_is_motor_stopped(*dev)) { -        DBG(DBG_info, "%s: already stopped\n", __func__); -        return; -    } - -  local_reg = dev->reg; - -    regs_set_optical_off(dev->model->asic_type, local_reg); - -    gl841_init_motor_regs_off(&local_reg,0); -    dev->interface->write_registers(local_reg); - -    if (is_testing_mode()) { -        return; -    } - -  /* looks like writing the right registers to zero is enough to get the chip -     out of scan mode into command mode, actually triggering(writing to -     register 0x0f) seems to be unnecessary */ - -  loop = 10; -    while (loop > 0) { -        if (scanner_is_motor_stopped(*dev)) { -            return; -        } - -        dev->interface->sleep_ms(100); -        loop--; -    } - -    throw SaneException(SANE_STATUS_IO_ERROR, "could not stop motor"); -} -  static bool gl841_get_paper_sensor(Genesys_Device* dev)  {      DBG_HELPER(dbg); @@ -1886,7 +1320,6 @@ void CommandSetGl841::eject_document(Genesys_Device* dev) const      if (!dev->model->is_sheetfed) {        DBG(DBG_proc, "%s: there is no \"eject sheet\"-concept for non sheet fed\n", __func__); -      DBG(DBG_proc, "%s: finished\n", __func__);        return;      } @@ -1895,22 +1328,21 @@ void CommandSetGl841::eject_document(Genesys_Device* dev) const      // FIXME: unused result      scanner_read_status(*dev); - -    gl841_stop_action(dev); +    scanner_stop_action(*dev);    local_reg = dev->reg;      regs_set_optical_off(dev->model->asic_type, local_reg);    const auto& sensor = sanei_genesys_find_sensor_any(dev); -    gl841_init_motor_regs(dev, sensor, &local_reg, 65536, MOTOR_ACTION_FEED, MotorFlag::NONE); +    gl841_init_motor_regs_feed(dev, sensor, &local_reg, 65536, ScanFlag::NONE);      dev->interface->write_registers(local_reg);      try {          scanner_start_action(*dev, true);      } catch (...) { -        catch_all_exceptions(__func__, [&]() { gl841_stop_action(dev); }); +        catch_all_exceptions(__func__, [&]() { scanner_stop_action(*dev); });          // restore original registers          catch_all_exceptions(__func__, [&]()          { @@ -1921,7 +1353,7 @@ void CommandSetGl841::eject_document(Genesys_Device* dev) const      if (is_testing_mode()) {          dev->interface->test_checkpoint("eject_document"); -        gl841_stop_action(dev); +        scanner_stop_action(*dev);          return;      } @@ -1936,10 +1368,9 @@ void CommandSetGl841::eject_document(Genesys_Device* dev) const  	{              if (!gl841_get_paper_sensor(dev)) { -	      DBG(DBG_info, "%s: reached home position\n", __func__); -	      DBG(DBG_proc, "%s: finished\n", __func__); -	      break; -	    } +                DBG(DBG_info, "%s: reached home position\n", __func__); +                break; +            }            dev->interface->sleep_ms(100);  	  --loop;  	} @@ -1948,16 +1379,15 @@ void CommandSetGl841::eject_document(Genesys_Device* dev) const  	{            // when we come here then the scanner needed too much time for this, so we better stop            // the motor -          catch_all_exceptions(__func__, [&](){ gl841_stop_action(dev); }); +          catch_all_exceptions(__func__, [&](){ scanner_stop_action(*dev); });            throw SaneException(SANE_STATUS_IO_ERROR,                                "timeout while waiting for scanhead to go home");  	}      } -    feed_mm = static_cast<float>(dev->model->eject_feed); -  if (dev->document) -    { -        feed_mm += static_cast<float>(dev->model->post_scan); +    feed_mm = dev->model->eject_feed; +    if (dev->document) { +        feed_mm += dev->model->post_scan;      }          sanei_genesys_read_feed_steps(dev, &init_steps); @@ -1981,11 +1411,22 @@ void CommandSetGl841::eject_document(Genesys_Device* dev) const        ++loop;      } -    gl841_stop_action(dev); +    scanner_stop_action(*dev);      dev->document = false;  } +void CommandSetGl841::update_home_sensor_gpio(Genesys_Device& dev) const +{ +    if (dev.model->gpio_id == GpioId::CANON_LIDE_35) { +        dev.interface->read_register(REG_0x6C); +        dev.interface->write_register(REG_0x6C, dev.gpo.regs.get_value(0x6c)); +    } +    if (dev.model->gpio_id == GpioId::CANON_LIDE_80) { +        dev.interface->read_register(REG_0x6B); +        dev.interface->write_register(REG_0x6B, REG_0x6B_GPO18 | REG_0x6B_GPO17); +    } +}  void CommandSetGl841::load_document(Genesys_Device* dev) const  { @@ -2064,8 +1505,6 @@ void CommandSetGl841::detect_document_end(Genesys_Device* dev) const              auto skip_lines = scan_end_lines - output_lines;              if (remaining_lines > skip_lines) { -                DBG(DBG_io, "%s: skip_lines=%zu\n", __func__, skip_lines); -                  remaining_lines -= skip_lines;                  dev->get_pipeline_source().set_remaining_bytes(remaining_lines *                                                                 dev->session.output_line_bytes_raw); @@ -2092,6 +1531,21 @@ void CommandSetGl841::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sens          dev->interface->write_register(REG_0x6B, val);      } +    if (dev->model->model_id == ModelId::CANON_LIDE_50 || +        dev->model->model_id == ModelId::CANON_LIDE_60) +    { +        if (dev->session.params.yres >= 1200) { +            dev->interface->write_register(REG_0x6C, 0x82); +        } else { +            dev->interface->write_register(REG_0x6C, 0x02); +        } +        if (dev->session.params.yres >= 600) { +            dev->interface->write_register(REG_0x6B, 0x01); +        } else { +            dev->interface->write_register(REG_0x6B, 0x03); +        } +    } +      if (dev->model->sensor_id != SensorId::CCD_PLUSTEK_OPTICPRO_3600) {          local_reg.init_reg(0x03, reg->get8(0x03) | REG_0x03_LAMPPWR);      } else { @@ -2123,439 +1577,53 @@ void CommandSetGl841::end_scan(Genesys_Device* dev, Genesys_Register_Set __sane_      DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop);      if (!dev->model->is_sheetfed) { -        gl841_stop_action(dev); +        scanner_stop_action(*dev);      }  } -// Moves the slider to steps -static void gl841_feed(Genesys_Device* dev, int steps) -{ -    DBG_HELPER_ARGS(dbg, "steps = %d", steps); -  Genesys_Register_Set local_reg; -  int loop; - -    gl841_stop_action(dev); - -  // FIXME: we should pick sensor according to the resolution scanner is currently operating on -  const auto& sensor = sanei_genesys_find_sensor_any(dev); - -  local_reg = dev->reg; - -    regs_set_optical_off(dev->model->asic_type, local_reg); - -    gl841_init_motor_regs(dev, sensor, &local_reg, steps, MOTOR_ACTION_FEED, MotorFlag::NONE); - -    dev->interface->write_registers(local_reg); - -    try { -        scanner_start_action(*dev, true); -    } catch (...) { -        catch_all_exceptions(__func__, [&]() { gl841_stop_action (dev); }); -        // restore original registers -        catch_all_exceptions(__func__, [&]() -        { -            dev->interface->write_registers(dev->reg); -        }); -        throw; -    } - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("feed"); -        dev->advance_head_pos_by_steps(ScanHeadId::PRIMARY, Direction::FORWARD, steps); -        gl841_stop_action(dev); -        return; -    } - -  loop = 0; -  while (loop < 300)		/* do not wait longer then 30 seconds */ -  { -        auto status = scanner_read_status(*dev); - -        if (!status.is_motor_enabled) { -            DBG(DBG_proc, "%s: finished\n", __func__); -            dev->advance_head_pos_by_steps(ScanHeadId::PRIMARY, Direction::FORWARD, steps); -            return; -      } -        dev->interface->sleep_ms(100); -      ++loop; -  } - -  /* when we come here then the scanner needed too much time for this, so we better stop the motor */ -  gl841_stop_action (dev); - -    dev->set_head_pos_unknown(); - -    throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home"); -} -  // Moves the slider to the home (top) position slowly  void CommandSetGl841::move_back_home(Genesys_Device* dev, bool wait_until_home) const  { -    DBG_HELPER_ARGS(dbg, "wait_until_home = %d", wait_until_home); -  Genesys_Register_Set local_reg; -  int loop = 0; - -    if (dev->model->is_sheetfed) { -      DBG(DBG_proc, "%s: there is no \"home\"-concept for sheet fed\n", __func__); -      DBG(DBG_proc, "%s: finished\n", __func__); -      return; -    } - -    // reset gpio pin -    uint8_t val; -    if (dev->model->gpio_id == GpioId::CANON_LIDE_35) { -        val = dev->interface->read_register(REG_0x6C); -        val = dev->gpo.regs.get_value(0x6c); -        dev->interface->write_register(REG_0x6C, val); -    } -    if (dev->model->gpio_id == GpioId::CANON_LIDE_80) { -        val = dev->interface->read_register(REG_0x6B); -        val = REG_0x6B_GPO18 | REG_0x6B_GPO17; -        dev->interface->write_register(REG_0x6B, val); -    } -    dev->cmd_set->save_power(dev, false); - -    // first read gives HOME_SENSOR true -    auto status = scanner_read_reliable_status(*dev); - - -    if (status.is_at_home) { -      DBG(DBG_info, "%s: already at home, completed\n", __func__); -        dev->set_head_pos_zero(ScanHeadId::PRIMARY); -      return; -    } - -    scanner_stop_action_no_move(*dev, dev->reg); - -  /* if motor is on, stop current action */ -    if (status.is_motor_enabled) { -        gl841_stop_action(dev); -    } - -  local_reg = dev->reg; - -  const auto& sensor = sanei_genesys_find_sensor_any(dev); - -    gl841_init_motor_regs(dev, sensor, &local_reg, 65536, MOTOR_ACTION_GO_HOME, MotorFlag::REVERSE); - -    // set up for no scan -    regs_set_optical_off(dev->model->asic_type, local_reg); - -    dev->interface->write_registers(local_reg); - -    try { -        scanner_start_action(*dev, true); -    } catch (...) { -        catch_all_exceptions(__func__, [&]() { gl841_stop_action(dev); }); -        // restore original registers -        catch_all_exceptions(__func__, [&]() -        { -            dev->interface->write_registers(dev->reg); -        }); -        throw; -    } - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("move_back_home"); -        dev->set_head_pos_zero(ScanHeadId::PRIMARY); -        return; -    } - -  if (wait_until_home) -    { -      while (loop < 300)		/* do not wait longer then 30 seconds */ -	{ -            auto status = scanner_read_status(*dev); -            if (status.is_at_home) { -	      DBG(DBG_info, "%s: reached home position\n", __func__); -	      DBG(DBG_proc, "%s: finished\n", __func__); -                dev->set_head_pos_zero(ScanHeadId::PRIMARY); -                return; -	    } -            dev->interface->sleep_ms(100); -	  ++loop; -	} - -        // when we come here then the scanner needed too much time for this, so we better stop -        // the motor -        catch_all_exceptions(__func__, [&](){ gl841_stop_action(dev); }); -        dev->set_head_pos_unknown(); -        throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home"); -    } - -  DBG(DBG_info, "%s: scanhead is still moving\n", __func__); -} - -// Automatically set top-left edge of the scan area by scanning a 200x200 pixels area at 600 dpi -// from very top of scanner -void CommandSetGl841::search_start_position(Genesys_Device* dev) const -{ -    DBG_HELPER(dbg); -  int size; -  Genesys_Register_Set local_reg; - -  int pixels = 600; -  int dpi = 300; - -  local_reg = dev->reg; - -  /* sets for a 200 lines * 600 pixels */ -  /* normal scan with no shading */ - -    // FIXME: the current approach of doing search only for one resolution does not work on scanners -    // whith employ different sensors with potentially different settings. -    const auto& sensor = sanei_genesys_find_sensor(dev, dpi, 1, dev->model->default_method); - -    ScanSession session; -    session.params.xres = dpi; -    session.params.yres = dpi; -    session.params.startx = 0; -    session.params.starty = 0; /*we should give a small offset here~60 steps*/ -    session.params.pixels = 600; -    session.params.lines = dev->model->search_lines; -    session.params.depth = 8; -    session.params.channels = 1; -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = ScanColorMode::GRAY; -    session.params.color_filter = ColorFilter::GREEN; -    session.params.flags = ScanFlag::DISABLE_SHADING | -                           ScanFlag::DISABLE_GAMMA | -                           ScanFlag::IGNORE_LINE_DISTANCE | -                           ScanFlag::DISABLE_BUFFER_FULL_MOVE; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, &local_reg, session); - -    // send to scanner -    dev->interface->write_registers(local_reg); - -  size = pixels * dev->model->search_lines; - -  std::vector<uint8_t> data(size); - -    dev->cmd_set->begin_scan(dev, sensor, &local_reg, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("search_start_position"); -        dev->cmd_set->end_scan(dev, &local_reg, true); -        dev->reg = local_reg; -        return; -    } - -    wait_until_buffer_non_empty(dev); - -    // now we're on target, we can read data -    sanei_genesys_read_data_from_scanner(dev, data.data(), size); - -    if (DBG_LEVEL >= DBG_data) { -        sanei_genesys_write_pnm_file("gl841_search_position.pnm", data.data(), 8, 1, pixels, -                                     dev->model->search_lines); -    } - -    dev->cmd_set->end_scan(dev, &local_reg, true); - -  /* update regs to copy ASIC internal state */ -  dev->reg = local_reg; - -    for (auto& sensor_update : -            sanei_genesys_find_sensors_all_for_write(dev, dev->model->default_method)) -    { -        sanei_genesys_search_reference_point(dev, sensor_update, data.data(), 0, dpi, pixels, -                                             dev->model->search_lines); -    } +    scanner_move_back_home(*dev, wait_until_home);  } -// sets up register for coarse gain calibration -// todo: check it for scanners using it -void CommandSetGl841::init_regs_for_coarse_calibration(Genesys_Device* dev, -                                                       const Genesys_Sensor& sensor, -                                                       Genesys_Register_Set& regs) const -{ -    DBG_HELPER(dbg); - -    ScanSession session; -    session.params.xres = dev->settings.xres; -    session.params.yres = dev->settings.yres; -    session.params.startx = 0; -    session.params.starty = 0; -    session.params.pixels = sensor.optical_res / sensor.ccd_pixels_per_system_pixel(); -    session.params.lines = 20; -    session.params.depth = 16; -    session.params.channels = dev->settings.get_channels(); -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = dev->settings.scan_mode; -    session.params.color_filter = dev->settings.color_filter; -    session.params.flags = ScanFlag::DISABLE_SHADING | -                           ScanFlag::DISABLE_GAMMA | -                           ScanFlag::SINGLE_LINE | -                           ScanFlag::IGNORE_LINE_DISTANCE; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, ®s, session); - -  DBG(DBG_info, "%s: optical sensor res: %d dpi, actual res: %d\n", __func__, -      sensor.optical_res / sensor.ccd_pixels_per_system_pixel(), dev->settings.xres); - -    dev->interface->write_registers(regs); - -/*  if (DBG_LEVEL >= DBG_info) -    sanei_gl841_print_registers (regs);*/ -} - -  // init registers for shading calibration  void CommandSetGl841::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,                                              Genesys_Register_Set& regs) const  { -    DBG_HELPER_ARGS(dbg, "lines = %zu", dev->calib_lines); -  SANE_Int ydpi; -    unsigned starty = 0; - -  /* initial calibration reg values */ -  regs = dev->reg; - -  ydpi = dev->motor.base_ydpi; -  if (dev->model->motor_id == MotorId::PLUSTEK_OPTICPRO_3600)  /* TODO PLUSTEK_3600: 1200dpi not yet working, produces dark bar */ -    { -      ydpi = 600; -    } -    if (dev->model->motor_id == MotorId::CANON_LIDE_80) { -      ydpi = gl841_get_dpihw(dev); -      /* get over extra dark area for this model. -	 It looks like different devices have dark areas of different width -	 due to manufacturing variability. The initial value of starty was 140, -	 but it moves the sensor almost past the dark area completely in places -	 on certain devices. - -	 On a particular device the black area starts at roughly position -	 160 to 230 depending on location (the dark area is not completely -	 parallel to the frame). -      */ -      starty = 70; -    } - -  dev->calib_channels = 3; -  dev->calib_lines = dev->model->shading_lines; +    DBG_HELPER(dbg); -    unsigned resolution = sensor.get_logical_hwdpi(dev->settings.xres); -    unsigned factor = sensor.optical_res / resolution; +    unsigned channels = 3; -    const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, dev->calib_channels, +    unsigned resolution = sensor.shading_resolution; +    const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,                                                           dev->settings.scan_method); -    dev->calib_pixels = calib_sensor.sensor_pixels / factor; - +    unsigned calib_lines = +            static_cast<unsigned>(dev->model->y_size_calib_dark_white_mm * resolution / MM_PER_INCH); +    unsigned starty = +            static_cast<unsigned>(dev->model->y_offset_calib_dark_white_mm * dev->motor.base_ydpi / MM_PER_INCH);      ScanSession session;      session.params.xres = resolution; -    session.params.yres = ydpi; +    session.params.yres = resolution;      session.params.startx = 0;      session.params.starty = starty; -    session.params.pixels = dev->calib_pixels; -    session.params.lines = dev->calib_lines; +    session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; +    session.params.lines = calib_lines;      session.params.depth = 16; -    session.params.channels = dev->calib_channels; +    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::DISABLE_BUFFER_FULL_MOVE |*/ -                           ScanFlag::IGNORE_LINE_DISTANCE; +                           ScanFlag::DISABLE_GAMMA;      compute_session(dev, session, calib_sensor);      init_regs_for_scan_session(dev, calib_sensor, ®s, session); -    dev->interface->write_registers(regs); +    dev->calib_session = session;  } -// set up registers for the actual scan -void CommandSetGl841::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const -{ -    DBG_HELPER(dbg); -  float move; -  int move_dpi; -  float start; - -    debug_dump(DBG_info, dev->settings); - -  /* steps to move to reach scanning area: -     - first we move to physical start of scanning -     either by a fixed steps amount from the black strip -     or by a fixed amount from parking position, -     minus the steps done during shading calibration -     - then we move by the needed offset whitin physical -     scanning area - -     assumption: steps are expressed at maximum motor resolution - -     we need: -     float y_offset; -     float y_size; -     float y_offset_calib; -     mm_to_steps()=motor dpi / 2.54 / 10=motor dpi / MM_PER_INCH */ - -  /* if scanner uses GENESYS_FLAG_SEARCH_START y_offset is -     relative from origin, else, it is from parking position */ - -  move_dpi = dev->motor.base_ydpi; - -  move = 0; -    if (dev->model->flags & GENESYS_FLAG_SEARCH_START) { -        move += static_cast<float>(dev->model->y_offset_calib_white); -    } - -  DBG(DBG_info, "%s move=%f steps\n", __func__, move); - -    move += static_cast<float>(dev->model->y_offset); -  DBG(DBG_info, "%s: move=%f steps\n", __func__, move); - -    move += static_cast<float>(dev->settings.tl_y); -  DBG(DBG_info, "%s: move=%f steps\n", __func__, move); - -    move = static_cast<float>((move * move_dpi) / MM_PER_INCH); - -/* start */ -    start = static_cast<float>(dev->model->x_offset); - -    start += static_cast<float>(dev->settings.tl_x); - -    start = static_cast<float>((start * sensor.optical_res) / MM_PER_INCH); - -  /* we enable true gray for cis scanners only, and just when doing -   * scan since color calibration is OK for this mode -   */ -    ScanFlag flags = ScanFlag::NONE; - -  /* true gray (led add for cis scanners) */ -  if(dev->model->is_cis && dev->settings.true_gray -    && dev->settings.scan_mode != ScanColorMode::COLOR_SINGLE_PASS -    && dev->model->sensor_id != SensorId::CIS_CANON_LIDE_80) -    { -      // on Lide 80 the LEDADD bit results in only red LED array being lit -      DBG(DBG_io, "%s: activating LEDADD\n", __func__); -        flags |= ScanFlag::ENABLE_LEDADD; -    } - -    ScanSession session; -    session.params.xres = dev->settings.xres; -    session.params.yres = dev->settings.yres; -    session.params.startx = static_cast<unsigned>(start); -    session.params.starty = static_cast<unsigned>(move); -    session.params.pixels = dev->settings.pixels; -    session.params.requested_pixels = dev->settings.requested_pixels; -    session.params.lines = dev->settings.lines; -    session.params.depth = dev->settings.depth; -    session.params.channels = dev->settings.get_channels(); -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = dev->settings.scan_mode; -    session.params.color_filter = dev->settings.color_filter; -    session.params.flags = flags; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, &dev->reg, session); -} - -  // this function sends generic gamma table (ie linear ones) or the Sensor specific one if provided  void CommandSetGl841::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const  { @@ -2581,216 +1649,7 @@ void CommandSetGl841::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor  SensorExposure CommandSetGl841::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,                                                  Genesys_Register_Set& regs) const  { -    DBG_HELPER(dbg); -  int num_pixels; -  int total_size; -  int i, j; -  int val; -  int channels; -  int avg[3], avga, avge; -  int turn; -  uint16_t exp[3], target; -  int move; - -  /* these 2 boundaries should be per sensor */ -  uint16_t min_exposure=500; -  uint16_t max_exposure; - -  /* feed to white strip if needed */ -    if (dev->model->y_offset_calib_white > 0) { -        move = static_cast<int>(dev->model->y_offset_calib_white); -        move = static_cast<int>((move * (dev->motor.base_ydpi)) / MM_PER_INCH); -      DBG(DBG_io, "%s: move=%d lines\n", __func__, move); -        gl841_feed(dev, move); -    } - -  /* offset calibration is always done in color mode */ -  channels = 3; - -    unsigned resolution = sensor.get_logical_hwdpi(dev->settings.xres); -    unsigned factor = sensor.optical_res / resolution; - -    const auto& calib_sensor_base = sanei_genesys_find_sensor(dev, resolution, channels, -                                                              dev->settings.scan_method); - -    num_pixels = calib_sensor_base.sensor_pixels / factor; - -    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 = 1; -    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_LINE_DISTANCE; -    compute_session(dev, session, calib_sensor_base); - -    init_regs_for_scan_session(dev, calib_sensor_base, ®s, session); - -    dev->interface->write_registers(regs); - - -  total_size = num_pixels * channels * 2 * 1;	/* colors * bytes_per_color * scan lines */ - -  std::vector<uint8_t> line(total_size); - -/* -   we try to get equal bright leds here: - -   loop: -     average per color -     adjust exposure times - */ - -  exp[0] = sensor.exposure.red; -  exp[1] = sensor.exposure.green; -  exp[2] = sensor.exposure.blue; - -  turn = 0; -  /* max exposure is set to ~2 time initial average -   * exposure, or 2 time last calibration exposure */ -  max_exposure=((exp[0]+exp[1]+exp[2])/3)*2; -  target=sensor.gain_white_ref*256; - -    auto calib_sensor = calib_sensor_base; - -    bool acceptable = false; -    do { -        calib_sensor.exposure.red = exp[0]; -        calib_sensor.exposure.green = exp[1]; -        calib_sensor.exposure.blue = exp[2]; - -        regs_set_exposure(dev->model->asic_type, regs, calib_sensor.exposure); -        dev->interface->write_register(0x10, (calib_sensor.exposure.red >> 8) & 0xff); -        dev->interface->write_register(0x11, calib_sensor.exposure.red & 0xff); -        dev->interface->write_register(0x12, (calib_sensor.exposure.green >> 8) & 0xff); -        dev->interface->write_register(0x13, calib_sensor.exposure.green & 0xff); -        dev->interface->write_register(0x14, (calib_sensor.exposure.blue >> 8) & 0xff); -        dev->interface->write_register(0x15, calib_sensor.exposure.blue & 0xff); - -        dev->interface->write_registers(regs); - -      DBG(DBG_info, "%s: starting line reading\n", __func__); -        dev->cmd_set->begin_scan(dev, calib_sensor, ®s, true); - -        if (is_testing_mode()) { -            dev->interface->test_checkpoint("led_calibration"); -            move_back_home(dev, true); -            return calib_sensor.exposure; -        } - -        sanei_genesys_read_data_from_scanner(dev, line.data(), total_size); - -      if (DBG_LEVEL >= DBG_data) { -          char fn[30]; -          std::snprintf(fn, 30, "gl841_led_%d.pnm", turn); -          sanei_genesys_write_pnm_file(fn, line.data(), 16, channels, num_pixels, 1); -      } - -     /* compute average */ -      for (j = 0; j < channels; j++) -      { -	  avg[j] = 0; -	  for (i = 0; i < num_pixels; i++) -	  { -	      if (dev->model->is_cis) -		  val = -		      line[i * 2 + j * 2 * num_pixels + 1] * 256 + -		      line[i * 2 + j * 2 * num_pixels]; -	      else -		  val = -		      line[i * 2 * channels + 2 * j + 1] * 256 + -		      line[i * 2 * channels + 2 * j]; -	      avg[j] += val; -	  } - -	  avg[j] /= num_pixels; -      } - -      DBG(DBG_info,"%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]); - -        acceptable = true; - -     /* exposure is acceptable if each color is in the %5 range -      * of other color channels */ -      if (avg[0] < avg[1] * 0.95 || avg[1] < avg[0] * 0.95 || -	  avg[0] < avg[2] * 0.95 || avg[2] < avg[0] * 0.95 || -	  avg[1] < avg[2] * 0.95 || avg[2] < avg[1] * 0.95) -        { -            acceptable = false; -        } - -      /* led exposure is not acceptable if white level is too low -       * ~80 hardcoded value for white level */ -      if(avg[0]<20000 || avg[1]<20000 || avg[2]<20000) -        { -            acceptable = false; -        } - -      /* for scanners using target value */ -      if(target>0) -        { -            acceptable = true; -          for(i=0;i<3;i++) -            { -              /* we accept +- 2% delta from target */ -              if(abs(avg[i]-target)>target/50) -                { -                  exp[i]=(exp[i]*target)/avg[i]; -                    acceptable = false; -                } -            } -        } -      else -        { -          if (!acceptable) -            { -              avga = (avg[0]+avg[1]+avg[2])/3; -              exp[0] = (exp[0] * avga) / avg[0]; -              exp[1] = (exp[1] * avga) / avg[1]; -              exp[2] = (exp[2] * avga) / avg[2]; -              /* -                keep the resulting exposures below this value. -                too long exposure drives the ccd into saturation. -                we may fix this by relying on the fact that -                we get a striped scan without shading, by means of -                statistical calculation -              */ -              avge = (exp[0] + exp[1] + exp[2]) / 3; - -              if (avge > max_exposure) { -                  exp[0] = (exp[0] * max_exposure) / avge; -                  exp[1] = (exp[1] * max_exposure) / avge; -                  exp[2] = (exp[2] * max_exposure) / avge; -              } -              if (avge < min_exposure) { -                  exp[0] = (exp[0] * min_exposure) / avge; -                  exp[1] = (exp[1] * min_exposure) / avge; -                  exp[2] = (exp[2] * min_exposure) / avge; -              } - -            } -        } - -        gl841_stop_action(dev); - -      turn++; - -  } while (!acceptable && turn < 100); - -  DBG(DBG_info,"%s: acceptable exposure: %d,%d,%d\n", __func__, exp[0], exp[1], exp[2]); - -    dev->cmd_set->move_back_home(dev, true); - -    return calib_sensor.exposure; +    return scanner_led_calibration(*dev, sensor, regs);  }  /** @brief calibration for AD frontend devices @@ -2804,9 +1663,6 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor&                                       Genesys_Register_Set& regs)  {      DBG_HELPER(dbg); -  int num_pixels; -  int total_size; -  int i;    int average;    int turn;    int top; @@ -2818,14 +1674,12 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor&        return;      } -    unsigned resolution = sensor.get_logical_hwdpi(dev->settings.xres); -    unsigned factor = sensor.optical_res / resolution; +    unsigned resolution = sensor.shading_resolution;      const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3,                                                                dev->settings.scan_method); -    num_pixels = calib_sensor.sensor_pixels / factor; - +    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; @@ -2841,14 +1695,15 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor&      session.params.flags = ScanFlag::DISABLE_SHADING |                             ScanFlag::DISABLE_GAMMA |                             ScanFlag::SINGLE_LINE | -                           ScanFlag::IGNORE_LINE_DISTANCE; +                           ScanFlag::IGNORE_STAGGER_OFFSET | +                           ScanFlag::IGNORE_COLOR_OFFSET;      compute_session(dev, session, calib_sensor);      dev->cmd_set->init_regs_for_scan_session(dev, calib_sensor, ®s, session); -  total_size = num_pixels * 3 * 2 * 1; - -  std::vector<uint8_t> line(total_size); +    // FIXME: we're reading twice as much data for no reason +    std::size_t total_size = session.output_line_bytes * 2; +    std::vector<uint8_t> line(total_size);    dev->frontend.set_gain(0, 0);    dev->frontend.set_gain(1, 0); @@ -2873,23 +1728,23 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor&          if (is_testing_mode()) {              dev->interface->test_checkpoint("ad_fe_offset_calibration"); -            gl841_stop_action(dev); +            scanner_stop_action(*dev);              return;          }        sanei_genesys_read_data_from_scanner(dev, line.data(), total_size); -      gl841_stop_action (dev); -      if (DBG_LEVEL >= DBG_data) { +      scanner_stop_action(*dev); +      if (dbg_log_image_data()) {            char fn[30]; -          std::snprintf(fn, 30, "gl841_offset_%02d.pnm", turn); -          sanei_genesys_write_pnm_file(fn, line.data(), 8, 3, num_pixels, 1); +          std::snprintf(fn, 30, "gl841_offset_%02d.tiff", turn); +          write_tiff_file(fn, line.data(), 8, 3, num_pixels, 1);        }        /* search for minimal value */        average=0; -      for(i=0;i<total_size;i++) +        for (std::size_t i = 0; i < total_size; i++)          { -          average+=line[i]; +            average += line[i];          }        average/=total_size;        DBG(DBG_data, "%s: average=%d\n", __func__, average); @@ -2919,8 +1774,6 @@ static void ad_fe_offset_calibration(Genesys_Device* dev, const Genesys_Sensor&  /* this function does the offset calibration by scanning one line of the calibration     area below scanner's top. There is a black margin and the remaining is white. -   sanei_genesys_search_start() must have been called so that the offsets and margins -   are allready known.  this function expects the slider to be where?  */ @@ -2928,39 +1781,32 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens                                           Genesys_Register_Set& regs) const  {      DBG_HELPER(dbg); -  int num_pixels; -  int total_size; -  int i, j; -  int val; -  int channels;    int off[3],offh[3],offl[3],off1[3],off2[3];    int min1[3],min2[3]; -  int cmin[3],cmax[3]; +    unsigned cmin[3],cmax[3];    int turn;    int mintgt = 0x400;    /* Analog Device fronted have a different calibration */      if ((dev->reg.find_reg(0x04).value & REG_0x04_FESET) == 0x02) { -      return ad_fe_offset_calibration(dev, sensor, regs); +        ad_fe_offset_calibration(dev, sensor, regs); +        return;      }    /* offset calibration is always done in color mode */ -  channels = 3; +    unsigned channels = 3; -    unsigned resolution = sensor.get_logical_hwdpi(dev->settings.xres); -    unsigned factor = sensor.optical_res / resolution; +    unsigned resolution = sensor.shading_resolution;      const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,                                                           dev->settings.scan_method); -    num_pixels = calib_sensor.sensor_pixels / factor; -      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.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH;      session.params.lines = 1;      session.params.depth = 16;      session.params.channels = channels; @@ -2970,17 +1816,13 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens      session.params.flags = ScanFlag::DISABLE_SHADING |                             ScanFlag::DISABLE_GAMMA |                             ScanFlag::SINGLE_LINE | -                           ScanFlag::IGNORE_LINE_DISTANCE | +                           ScanFlag::IGNORE_STAGGER_OFFSET | +                           ScanFlag::IGNORE_COLOR_OFFSET |                             ScanFlag::DISABLE_LAMP;      compute_session(dev, session, calib_sensor);      init_regs_for_scan_session(dev, calib_sensor, ®s, session); -  total_size = num_pixels * channels * 2 * 1;	/* colors * bytes_per_color * scan lines */ - -  std::vector<uint8_t> first_line(total_size); -  std::vector<uint8_t> second_line(total_size); -    /* scan first line of data with no offset nor gain */  /*WM8199: gain=0.73; offset=-260mV*/  /*okay. the sensor black level is now at -260mV. we only get 0 from AFE...*/ @@ -3011,12 +1853,14 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens    offl[2] = 0x00;    turn = 0; +    Image first_line; +      bool acceptable = false;    do {          dev->interface->write_registers(regs); -      for (j=0; j < channels; j++) { +        for (unsigned j = 0; j < channels; j++) {  	  off[j] = (offh[j]+offl[j])/2;            dev->frontend.set_offset(j, off[j]);        } @@ -3031,57 +1875,51 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens              return;          } -        sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size); +        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, "gl841_offset1_%02d.pnm", turn); -          sanei_genesys_write_pnm_file(fn, first_line.data(), 16, channels, num_pixels, 1); -      } +        if (dbg_log_image_data()) { +            char fn[30]; +            std::snprintf(fn, 30, "gl841_offset1_%02d.tiff", turn); +            write_tiff_file(fn, first_line); +        }          acceptable = true; -      for (j = 0; j < channels; j++) -      { -	  cmin[j] = 0; -	  cmax[j] = 0; +        for (unsigned ch = 0; ch < channels; ch++) { +            cmin[ch] = 0; +            cmax[ch] = 0; -	  for (i = 0; i < num_pixels; i++) -	  { -	      if (dev->model->is_cis) -		  val = -		      first_line[i * 2 + j * 2 * num_pixels + 1] * 256 + -		      first_line[i * 2 + j * 2 * num_pixels]; -	      else -		  val = -		      first_line[i * 2 * channels + 2 * j + 1] * 256 + -		      first_line[i * 2 * channels + 2 * j]; -	      if (val < 10) -		  cmin[j]++; -	      if (val > 65525) -		  cmax[j]++; -	  } +            for (std::size_t x = 0; x < first_line.get_width(); x++) { +                auto value = first_line.get_raw_channel(x, 0, ch); +                if (value < 10) { +                    cmin[ch]++; +                } +                if (value > 65525) { +                    cmax[ch]++; +                } +            }            /* TODO the DP685 has a black strip in the middle of the sensor             * should be handled in a more elegant way , could be a bug */ -          if (dev->model->sensor_id == SensorId::CCD_DP685) -              cmin[j] -= 20; +            if (dev->model->sensor_id == SensorId::CCD_DP685) { +                cmin[ch] -= 20; +            } -	  if (cmin[j] > num_pixels/100) { +            if (cmin[ch] > first_line.get_width() / 100) {            acceptable = false;  	      if (dev->model->is_cis)  		  offl[0] = off[0];  	      else -		  offl[j] = off[j]; -	  } -	  if (cmax[j] > num_pixels/100) { +          offl[ch] = off[ch]; +            } +            if (cmax[ch] > first_line.get_width() / 100) {            acceptable = false;  	      if (dev->model->is_cis)  		  offh[0] = off[0];  	      else -		  offh[j] = off[j]; -	  } -      } +          offh[ch] = off[ch]; +            } +        }        DBG(DBG_info,"%s: black/white pixels: %d/%d,%d/%d,%d/%d\n", __func__, cmin[0], cmax[0],            cmin[1], cmax[1], cmin[2], cmax[2]); @@ -3091,7 +1929,7 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens  	  offl[2] = offl[1] = offl[0];        } -        gl841_stop_action(dev); +        scanner_stop_action(*dev);        turn++;    } while (!acceptable && turn < 100); @@ -3099,26 +1937,19 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens    DBG(DBG_info,"%s: acceptable offsets: %d,%d,%d\n", __func__, off[0], off[1], off[2]); -  for (j = 0; j < channels; j++) -  { -      off1[j] = off[j]; +    for (unsigned ch = 0; ch < channels; ch++) { +        off1[ch] = off[ch]; -      min1[j] = 65536; +        min1[ch] = 65536; -      for (i = 0; i < num_pixels; i++) -      { -	  if (dev->model->is_cis) -	      val = -		  first_line[i * 2 + j * 2 * num_pixels + 1] * 256 + -		  first_line[i * 2 + j * 2 * num_pixels]; -	  else -	      val = -		  first_line[i * 2 * channels + 2 * j + 1] * 256 + -		  first_line[i * 2 * channels + 2 * j]; -	  if (min1[j] > val && val >= 10) -	      min1[j] = val; -      } -  } +        for (std::size_t x = 0; x < first_line.get_width(); x++) { +            auto value = first_line.get_raw_channel(x, 0, ch); + +            if (min1[ch] > value && value >= 10) { +                min1[ch] = value; +            } +        } +    }    offl[0] = off[0]; @@ -3126,64 +1957,59 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens    offl[2] = off[0];    turn = 0; +    Image second_line;    do { -      for (j=0; j < channels; j++) { +        for (unsigned j=0; j < channels; j++) {  	  off[j] = (offh[j]+offl[j])/2;            dev->frontend.set_offset(j, off[j]); -      } +        }          dev->cmd_set->set_fe(dev, calib_sensor, AFE_SET);        DBG(DBG_info, "%s: starting second line reading\n", __func__);          dev->interface->write_registers(regs);          dev->cmd_set->begin_scan(dev, calib_sensor, ®s, true); -        sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size); +        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, "gl841_offset2_%02d.pnm", turn); -          sanei_genesys_write_pnm_file(fn, second_line.data(), 16, channels, num_pixels, 1); -      } +        if (dbg_log_image_data()) { +            char fn[30]; +            std::snprintf(fn, 30, "gl841_offset2_%02d.tiff", turn); +            write_tiff_file(fn, second_line); +        }          acceptable = true; -      for (j = 0; j < channels; j++) -      { -	  cmin[j] = 0; -	  cmax[j] = 0; +        for (unsigned ch = 0; ch < channels; ch++) { +            cmin[ch] = 0; +            cmax[ch] = 0; -	  for (i = 0; i < num_pixels; i++) -	  { -	      if (dev->model->is_cis) -		  val = -		      second_line[i * 2 + j * 2 * num_pixels + 1] * 256 + -		      second_line[i * 2 + j * 2 * num_pixels]; -	      else -		  val = -		      second_line[i * 2 * channels + 2 * j + 1] * 256 + -		      second_line[i * 2 * channels + 2 * j]; -	      if (val < 10) -		  cmin[j]++; -	      if (val > 65525) -		  cmax[j]++; -	  } +            for (std::size_t x = 0; x < second_line.get_width(); x++) { +                auto value = second_line.get_raw_channel(x, 0, ch); -	  if (cmin[j] > num_pixels/100) { +                if (value < 10) { +                    cmin[ch]++; +                } +                if (value > 65525) { +                    cmax[ch]++; +                } +            } + +            if (cmin[ch] > second_line.get_width() / 100) {              acceptable = false;  	      if (dev->model->is_cis)  		  offl[0] = off[0];  	      else -		  offl[j] = off[j]; -	  } -	  if (cmax[j] > num_pixels/100) { +                    offl[ch] = off[ch]; +            } +            if (cmax[ch] > second_line.get_width() / 100) {              acceptable = false;  	      if (dev->model->is_cis)  		  offh[0] = off[0];  	      else -		  offh[j] = off[j]; -	  } -      } +                offh[ch] = off[ch]; +            } +        }        DBG(DBG_info, "%s: black/white pixels: %d/%d,%d/%d,%d/%d\n", __func__, cmin[0], cmax[0],            cmin[1], cmax[1], cmin[2], cmax[2]); @@ -3193,7 +2019,7 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens  	  offl[2] = offl[1] = offl[0];        } -        gl841_stop_action(dev); +        scanner_stop_action(*dev);        turn++; @@ -3202,26 +2028,19 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens    DBG(DBG_info, "%s: acceptable offsets: %d,%d,%d\n", __func__, off[0], off[1], off[2]); -  for (j = 0; j < channels; j++) -  { -      off2[j] = off[j]; +    for (unsigned ch = 0; ch < channels; ch++) { +        off2[ch] = off[ch]; -      min2[j] = 65536; +        min2[ch] = 65536; -      for (i = 0; i < num_pixels; i++) -      { -	  if (dev->model->is_cis) -	      val = -		  second_line[i * 2 + j * 2 * num_pixels + 1] * 256 + -		  second_line[i * 2 + j * 2 * num_pixels]; -	  else -	      val = -		  second_line[i * 2 * channels + 2 * j + 1] * 256 + -		  second_line[i * 2 * channels + 2 * j]; -	  if (min2[j] > val && val != 0) -	      min2[j] = val; -      } -  } +        for (std::size_t x = 0; x < second_line.get_width(); x++) { +            auto value = second_line.get_raw_channel(x, 0, ch); + +            if (min2[ch] > value && value != 0) { +                min2[ch] = value; +            } +        } +    }    DBG(DBG_info, "%s: first set: %d/%d,%d/%d,%d/%d\n", __func__, off1[0], min1[0], off1[1], min1[1],        off1[2], min1[2]); @@ -3247,22 +2066,25 @@ void CommandSetGl841::offset_calibration(Genesys_Device* dev, const Genesys_Sens    off=(min*(off1-off2)+min1*off2-off1*min2)/(min1-min2)   */ -  for (j = 0; j < channels; j++) -  { -      if (min2[j]-min1[j] == 0) { +    for (unsigned ch = 0; ch < channels; ch++) { +        if (min2[ch] - min1[ch] == 0) {  /*TODO: try to avoid this*/  	  DBG(DBG_warn, "%s: difference too small\n", __func__); -	  if (mintgt * (off1[j] - off2[j]) + min1[j] * off2[j] - min2[j] * off1[j] >= 0) -	      off[j] = 0x0000; -	  else -	      off[j] = 0xffff; -      } else -	  off[j] = (mintgt * (off1[j] - off2[j]) + min1[j] * off2[j] - min2[j] * off1[j])/(min1[j]-min2[j]); -      if (off[j] > 255) -	  off[j] = 255; -      if (off[j] < 0) -	  off[j] = 0; -      dev->frontend.set_offset(j, off[j]); +            if (mintgt * (off1[ch] - off2[ch]) + min1[ch] * off2[ch] - min2[ch] * off1[ch] >= 0) { +                off[ch] = 0x0000; +            } else { +                off[ch] = 0xffff; +            } +        } else { +            off[ch] = (mintgt * (off1[ch] - off2[ch]) + min1[ch] * off2[ch] - min2[ch] * off1[ch])/(min1[ch]-min2[ch]); +        } +        if (off[ch] > 255) { +            off[ch] = 255; +        } +        if (off[ch] < 0) { +            off[ch] = 0; +        } +      dev->frontend.set_offset(ch, off[ch]);    }    DBG(DBG_info, "%s: final offsets: %d,%d,%d\n", __func__, off[0], off[1], off[2]); @@ -3297,171 +2119,13 @@ 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); -  int num_pixels; -  int total_size; -  int i, j, channels; -  int max[3]; -  float gain[3]; -  int val; -  int lines=1; -  int move; - -    // feed to white strip if needed -    if (dev->model->y_offset_calib_white > 0) { -        move = static_cast<int>(dev->model->y_offset_calib_white); -        move = static_cast<int>((move * (dev->motor.base_ydpi)) / MM_PER_INCH); -      DBG(DBG_io, "%s: move=%d lines\n", __func__, move); -        gl841_feed(dev, move); -    } - -  /* coarse gain calibration is allways done in color mode */ -  channels = 3; - -    unsigned resolution = sensor.get_logical_hwdpi(dev->settings.xres); -    unsigned factor = sensor.optical_res / resolution; - -    const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, -                                                         dev->settings.scan_method); - -    num_pixels = calib_sensor.sensor_pixels / factor; - -    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_LINE_DISTANCE; -    compute_session(dev, session, calib_sensor); - -    init_regs_for_scan_session(dev, calib_sensor, ®s, session); - -    dev->interface->write_registers(regs); - -  total_size = num_pixels * channels * 2 * lines;	/* colors * bytes_per_color * scan lines */ - -  std::vector<uint8_t> line(total_size); - -    dev->cmd_set->begin_scan(dev, calib_sensor, ®s, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("coarse_gain_calibration"); -        gl841_stop_action(dev); -        move_back_home(dev, true); -        return; -    } - -    sanei_genesys_read_data_from_scanner(dev, line.data(), total_size); - -  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 (j = 0; j < channels; j++) -    { -      max[j] = 0; -      for (i = 0; i < num_pixels; i++) -	{ -	  if (dev->model->is_cis) -	      val = -		  line[i * 2 + j * 2 * num_pixels + 1] * 256 + -		  line[i * 2 + j * 2 * num_pixels]; -	  else -	      val = -		  line[i * 2 * channels + 2 * j + 1] * 256 + -		  line[i * 2 * channels + 2 * j]; - -	  if (val > max[j]) -	    max[j] = val; -	} - -        gain[j] = 65535.0f / max[j]; - -      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[j] *= 0.69f; // seems we don't get the real maximum. empirically derived -	  if (283 - 208/gain[j] > 255) -              out_gain = 255; -	  else if (283 - 208/gain[j] < 0) -              out_gain = 0; -	  else -              out_gain = static_cast<std::uint8_t>(283 - 208 / gain[j]); -        } else if (dev->model->adc_id == AdcId::CANON_LIDE_80) { -              out_gain = static_cast<std::uint8_t>(gain[j] * 12); -        } -      dev->frontend.set_gain(j, out_gain); - -      DBG(DBG_proc, "%s: channel %d, max=%d, gain = %f, setting:%d\n", __func__, j, max[j], gain[j], -          out_gain); -    } - -  for (j = 0; j < channels; j++) -    { -      if(gain[j] > 10) -        { -	  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  // between 2 scans is below a threshold  void CommandSetGl841::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                           Genesys_Register_Set* local_reg, int* channels, -                                           int* total_size) const +                                           Genesys_Register_Set* local_reg) const  {      DBG_HELPER(dbg);      int num_pixels = 4 * 300; @@ -3475,51 +2139,34 @@ void CommandSetGl841::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Se    dev->frontend.set_offset(1, 0x80);    dev->frontend.set_offset(2, 0x80); +    auto 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 = sensor.optical_res; +    session.params.xres = sensor.full_resolution;      session.params.yres = dev->settings.yres;      session.params.startx = sensor.dummy_pixel;      session.params.starty = 0;      session.params.pixels = num_pixels;      session.params.lines = 1; -    session.params.depth = 16; -    session.params.channels = *channels; +    session.params.depth = dev->model->bpp_color_values.front(); +    session.params.channels = 3;      session.params.scan_method = dev->settings.scan_method; -    if (*channels == 3) { -        session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS; -    } else { -        session.params.scan_mode = ScanColorMode::GRAY; -    } +    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_LINE_DISTANCE; +    session.params.flags = flags; +      compute_session(dev, session, sensor);      init_regs_for_scan_session(dev, sensor, local_reg, session); - -    num_pixels = session.output_pixels; - -  *total_size = num_pixels * 3 * 2 * 1;	/* colors * bytes_per_color * scan lines */ - -    dev->interface->write_registers(*local_reg); -} - - -/* - * this function moves head without scanning, forward, then backward - * so that the head goes to park position. - * as a by-product, also check for lock - */ -static void sanei_gl841_repark_head(Genesys_Device* dev) -{ -    DBG_HELPER(dbg); - -    gl841_feed(dev,232); - -    // toggle motor flag, put an huge step number and redo move backward -    dev->cmd_set->move_back_home(dev, true);  }  /* @@ -3528,123 +2175,9 @@ static void sanei_gl841_repark_head(Genesys_Device* dev)   */  void CommandSetGl841::init(Genesys_Device* dev) const  { -  size_t size; - -  DBG_INIT (); +    DBG_INIT();      DBG_HELPER(dbg); - -    dev->set_head_pos_zero(ScanHeadId::PRIMARY); - -  /* Check if the device has already been initialized and powered up */ -  if (dev->already_initialized) -    { -        auto status = scanner_read_status(*dev); -        if (!status.is_replugged) { -            DBG(DBG_info, "%s: already initialized\n", __func__); -            return; -        } -    } - -  dev->dark_average_data.clear(); -  dev->white_average_data.clear(); - -  dev->settings.color_filter = ColorFilter::RED; - -    // ASIC reset -    dev->interface->write_register(0x0e, 0x01); -    dev->interface->write_register(0x0e, 0x00); - -  /* Set default values for registers */ -  gl841_init_registers (dev); - -    // Write initial registers -    dev->interface->write_registers(dev->reg); - -  const auto& sensor = sanei_genesys_find_sensor_any(dev); - -    // Set analog frontend -    dev->cmd_set->set_fe(dev, sensor, AFE_INIT); - -    // FIXME: move_back_home modifies dev->calib_reg and requires it to be filled -    dev->calib_reg = dev->reg; - -    // Move home -    dev->cmd_set->move_back_home(dev, true); - -    // Init shading data -    sanei_genesys_init_shading_data(dev, sensor, sensor.sensor_pixels); - -  /* ensure head is correctly parked, and check lock */ -  if (dev->model->flags & GENESYS_FLAG_REPARK) -    { -        // FIXME: if repark fails, we should print an error message that the scanner is locked and -        // the user should unlock the lock. We should also rethrow with SANE_STATUS_JAMMED -        sanei_gl841_repark_head(dev); -    } - -    // send gamma tables -    dev->cmd_set->send_gamma_table(dev, sensor); - -  /* initial calibration reg values */ -  Genesys_Register_Set& regs = dev->calib_reg; -  regs = dev->reg; - -    unsigned resolution = sensor.get_logical_hwdpi(300); -    unsigned factor = sensor.optical_res / resolution; - -    const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, 3, -                                                         dev->settings.scan_method); - -    unsigned num_pixels = 16 / factor; - -    ScanSession session; -    session.params.xres = resolution; -    session.params.yres = 300; -    session.params.startx = 0; -    session.params.starty = 0; -    session.params.pixels = num_pixels; -    session.params.lines = 1; -    session.params.depth = 16; -    session.params.channels = 3; -    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 = ScanFlag::DISABLE_SHADING | -                           ScanFlag::DISABLE_GAMMA | -                           ScanFlag::SINGLE_LINE | -                           ScanFlag::IGNORE_LINE_DISTANCE; -    compute_session(dev, session, calib_sensor); - -    init_regs_for_scan_session(dev, calib_sensor, ®s, session); - -    dev->interface->write_registers(regs); - -    size = num_pixels * 3 * 2 * 1; // colors * bytes_per_color * scan lines - -  std::vector<uint8_t> line(size); - -  DBG(DBG_info, "%s: starting dummy data reading\n", __func__); -    dev->cmd_set->begin_scan(dev, calib_sensor, ®s, true); - -  sanei_usb_set_timeout(1000);/* 1 second*/ - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("init"); -    } else { -        // ignore errors. next read will succeed -        catch_all_exceptions(__func__, -                             [&](){ sanei_genesys_read_data_from_scanner(dev, line.data(), size); }); -    } - -  sanei_usb_set_timeout(30 * 1000);/* 30 seconds*/ - -    end_scan(dev, ®s, true); - -  regs = dev->reg; - -    // Set powersaving(default = 15 minutes) -    set_powersaving(dev, 15); -    dev->already_initialized = true; +    sanei_genesys_asic_init(dev);  }  void CommandSetGl841::update_hardware_sensors(Genesys_Scanner* s) const @@ -3676,225 +2209,6 @@ void CommandSetGl841::update_hardware_sensors(Genesys_Scanner* s) const      }  } -/** @brief search for a full width black or white strip. - * This function searches for a black or white stripe across the scanning area. - * When searching backward, the searched area must completely be of the desired - * color since this area will be used for calibration which scans forward. - * @param dev scanner device - * @param forward true if searching forward, false if searching backward - * @param black true if searching for a black strip, false for a white strip - */ -void CommandSetGl841::search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, bool forward, -                                   bool black) const -{ -    DBG_HELPER_ARGS(dbg, "%s %s", black ? "black" : "white", forward ? "forward" : "reverse"); -  unsigned int pixels, lines, channels; -  Genesys_Register_Set local_reg; -  size_t size; -  unsigned int pass, count, found, x, y, length; -  char title[80]; -  GenesysRegister *r; -  uint8_t white_level=90;  /**< default white level to detect white dots */ -  uint8_t black_level=60;  /**< default black level to detect black dots */ - -  /* use maximum gain when doing forward white strip detection -   * since we don't have calibrated the sensor yet */ -  if(!black && forward) -    { -      dev->frontend.set_gain(0, 0xff); -      dev->frontend.set_gain(1, 0xff); -      dev->frontend.set_gain(2, 0xff); -    } - -    dev->cmd_set->set_fe(dev, sensor, AFE_SET); -    gl841_stop_action(dev); - -    // set up for a gray scan at lowest dpi -    const auto& resolution_settings = dev->model->get_resolution_settings(dev->settings.scan_method); -    unsigned dpi = resolution_settings.get_min_resolution_x(); -  channels = 1; - -  /* shading calibation is done with dev->motor.base_ydpi */ -  /* lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi; */ -    lines = static_cast<unsigned>((10 * dpi) / MM_PER_INCH); - -  pixels = (sensor.sensor_pixels * dpi) / sensor.optical_res; - -  /* 20 cm max length for calibration sheet */ -    length = static_cast<unsigned>(((200 * dpi) / MM_PER_INCH) / lines); - -    dev->set_head_pos_zero(ScanHeadId::PRIMARY); - -  local_reg = dev->reg; - -    ScanSession session; -    session.params.xres = dpi; -    session.params.yres = dpi; -    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::GRAY; -    session.params.color_filter = ColorFilter::RED; -    session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_GAMMA; -    compute_session(dev, session, sensor); - -    size = pixels * channels * lines * (session.params.depth / 8); -    std::vector<uint8_t> data(size); - -    init_regs_for_scan_session(dev, sensor, &local_reg, session); - -  /* set up for reverse or forward */ -  r = sanei_genesys_get_address(&local_reg, 0x02); -    if (forward) { -        r->value &= ~4; -    } else { -        r->value |= 4; -    } - -    dev->interface->write_registers(local_reg); - -    dev->cmd_set->begin_scan(dev, sensor, &local_reg, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("search_strip"); -        gl841_stop_action(dev); -        return; -    } - -    // waits for valid data -    wait_until_buffer_non_empty(dev); - -    // now we're on target, we can read data -    sanei_genesys_read_data_from_scanner(dev, data.data(), size); - -    gl841_stop_action(dev); - -  pass = 0; -  if (DBG_LEVEL >= DBG_data) -    { -        std::sprintf(title, "gl841_search_strip_%s_%s%02u.pnm", black ? "black" : "white", -                     forward ? "fwd" : "bwd", pass); -      sanei_genesys_write_pnm_file(title, data.data(), session.params.depth, -                                   channels, pixels, lines); -    } - -  /* loop until strip is found or maximum pass number done */ -  found = 0; -  while (pass < length && !found) -    { -        dev->interface->write_registers(local_reg); - -        //now start scan -        dev->cmd_set->begin_scan(dev, sensor, &local_reg, true); - -        // waits for valid data -        wait_until_buffer_non_empty(dev); - -        // now we're on target, we can read data -        sanei_genesys_read_data_from_scanner(dev, data.data(), size); - -        gl841_stop_action (dev); - -      if (DBG_LEVEL >= DBG_data) -	{ -            std::sprintf(title, "gl841_search_strip_%s_%s%02u.pnm", -                         black ? "black" : "white", forward ? "fwd" : "bwd", pass); -          sanei_genesys_write_pnm_file(title, data.data(), session.params.depth, -                                       channels, pixels, lines); -	} - -      /* search data to find black strip */ -      /* when searching forward, we only need one line of the searched color since we -       * will scan forward. But when doing backward search, we need all the area of the -       * same color */ -      if (forward) -	{ -	  for (y = 0; y < lines && !found; y++) -	    { -	      count = 0; -	      /* count of white/black pixels depending on the color searched */ -	      for (x = 0; x < pixels; x++) -		{ -		  /* when searching for black, detect white pixels */ -		  if (black && data[y * pixels + x] > white_level) -		    { -		      count++; -		    } -		  /* when searching for white, detect black pixels */ -		  if (!black && data[y * pixels + x] < black_level) -		    { -		      count++; -		    } -		} - -	      /* at end of line, if count >= 3%, line is not fully of the desired color -	       * so we must go to next line of the buffer */ -	      /* count*100/pixels < 3 */ -	      if ((count * 100) / pixels < 3) -		{ -		  found = 1; -		  DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__, -		      pass, y); -		} -	      else -		{ -		  DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count, -		      (100 * count) / pixels); -		} -	    } -	} -      else			/* since calibration scans are done forward, we need the whole area -				   to be of the required color when searching backward */ -	{ -	  count = 0; -	  for (y = 0; y < lines; y++) -	    { -	      /* count of white/black pixels depending on the color searched */ -	      for (x = 0; x < pixels; x++) -		{ -		  /* when searching for black, detect white pixels */ -		  if (black && data[y * pixels + x] > white_level) -		    { -		      count++; -		    } -		  /* when searching for white, detect black pixels */ -		  if (!black && data[y * pixels + x] < black_level) -		    { -		      count++; -		    } -		} -	    } - -	  /* at end of area, if count >= 3%, area is not fully of the desired color -	   * so we must go to next buffer */ -	  if ((count * 100) / (pixels * lines) < 3) -	    { -	      found = 1; -	      DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass); -	    } -	  else -	    { -	      DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count, -		  (100 * count) / pixels); -	    } -	} -      pass++; -    } - -  if (found) -    { -      DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white"); -    } -  else -    { -        throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found", black ? "black" : "white"); -    } -} -  /**   * Send shading calibration data. The buffer is considered to always hold values   * for all the channels. @@ -3903,42 +2217,30 @@ void CommandSetGl841::send_shading_data(Genesys_Device* dev, const Genesys_Senso                                          uint8_t* data, int size) const  {      DBG_HELPER_ARGS(dbg, "writing %d bytes of shading data", size); -  uint32_t length, x, factor, pixels, i; -    uint16_t dpiset, dpihw, beginpixel; +  uint32_t length, x, pixels, i;    uint8_t *ptr,*src;    /* old method if no SHDAREA */      if ((dev->reg.find_reg(0x01).value & REG_0x01_SHDAREA) == 0) { +        // Note that this requires the sensor pixel offset to be exactly the same as to start +        // reading from dummy_pixel + 1 position.          dev->interface->write_buffer(0x3c, 0x0000, data, size);          return;      }    /* data is whole line, we extract only the part for the scanned area */      length = static_cast<std::uint32_t>(size / 3); -    unsigned strpixel = dev->session.pixel_startx; -    unsigned endpixel = dev->session.pixel_endx; - -  /* compute deletion/average factor */ -    dpiset = dev->reg.get16(REG_DPISET); -  dpihw = gl841_get_dpihw(dev); -    unsigned ccd_size_divisor = dev->session.ccd_size_divisor; -  factor=dpihw/dpiset; -  DBG(DBG_io2, "%s: dpihw=%d, dpiset=%d, ccd_size_divisor=%d, factor=%d\n", __func__, dpihw, dpiset, -      ccd_size_divisor, factor); - -  /* turn pixel value into bytes 2x16 bits words */ -  strpixel*=2*2; /* 2 words of 2 bytes */ -  endpixel*=2*2; -  pixels=endpixel-strpixel; - -  /* shading pixel begin is start pixel minus start pixel during shading -   * calibration. Currently only cases handled are full and half ccd resolution. -   */ -    beginpixel = sensor.ccd_start_xoffset / ccd_size_divisor; -  beginpixel += sensor.dummy_pixel + 1; -  DBG(DBG_io2, "%s: ORIGIN PIXEL=%d\n", __func__, beginpixel); -  beginpixel = (strpixel-beginpixel*2*2)/factor; -  DBG(DBG_io2, "%s: BEGIN PIXEL=%d\n", __func__, beginpixel/4); + +    // turn pixel value into bytes 2x16 bits words +    pixels = dev->session.pixel_endx - dev->session.pixel_startx; +    pixels *= 4; + +    // shading pixel begin is start pixel minus start pixel during shading +    // calibration. Currently only cases handled are full and half ccd resolution. +    unsigned beginpixel = dev->session.params.startx * dev->session.optical_resolution / +            dev->session.params.xres; +    beginpixel *= 4; +    beginpixel /= sensor.shading_factor;      dev->interface->record_key_value("shading_offset", std::to_string(beginpixel));      dev->interface->record_key_value("shading_pixels", std::to_string(pixels)); @@ -3962,7 +2264,7 @@ void CommandSetGl841::send_shading_data(Genesys_Device* dev, const Genesys_Senso        for(x=0;x<pixels;x+=4)          {            /* coefficient source */ -          src=data+x+beginpixel+i*length; +            src = data + x + beginpixel + i * length;            ptr[0]=src[0];            ptr[1]=src[1];            ptr[2]=src[2]; @@ -3988,22 +2290,29 @@ void CommandSetGl841::wait_for_motor_stop(Genesys_Device* dev) const      (void) dev;  } -void CommandSetGl841::move_to_ta(Genesys_Device* dev) const -{ -    (void) dev; -    throw SaneException("not implemented"); -} -  void CommandSetGl841::asic_boot(Genesys_Device *dev, bool cold) const  { -    (void) dev; -    (void) cold; -    throw SaneException("not implemented"); -} +    // reset ASIC in case of cold boot +    if (cold) { +        dev->interface->write_register(0x0e, 0x01); +        dev->interface->write_register(0x0e, 0x00); +    } -std::unique_ptr<CommandSet> create_gl841_cmd_set() -{ -    return std::unique_ptr<CommandSet>(new CommandSetGl841{}); +    gl841_init_registers(dev); + +    // Write initial registers +    dev->interface->write_registers(dev->reg); + +    // FIXME: 0x0b is not set, but on all other backends we do set it +    // dev->reg.remove_reg(0x0b); + +    if (dev->model->model_id == ModelId::CANON_LIDE_60) { +        dev->interface->write_0x8c(0x10, 0xa4); +    } + +    // FIXME: we probably don't need this +    const auto& sensor = sanei_genesys_find_sensor_any(dev); +    dev->cmd_set->set_fe(dev, sensor, AFE_INIT);  }  } // namespace gl841 diff --git a/backend/genesys/gl841.h b/backend/genesys/gl841.h index 5e24249..c9f15ee 100644 --- a/backend/genesys/gl841.h +++ b/backend/genesys/gl841.h @@ -42,7 +42,7 @@  */  #include "genesys.h" -#include "command_set.h" +#include "command_set_common.h"  #ifndef BACKEND_GENESYS_GL841_H  #define BACKEND_GENESYS_GL841_H @@ -50,7 +50,7 @@  namespace genesys {  namespace gl841 { -class CommandSetGl841 : public CommandSet +class CommandSetGl841 : public CommandSetCommon  {  public:      ~CommandSetGl841() override = default; @@ -60,17 +60,11 @@ public:      void init(Genesys_Device* dev) const override;      void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, -                              Genesys_Register_Set* regs, int* channels, -                              int* total_size) const override; - -    void init_regs_for_coarse_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                          Genesys_Register_Set& regs) const override; +                              Genesys_Register_Set* regs) const override;      void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,                                 Genesys_Register_Set& regs) const override; -    void init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; -      void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,                                      Genesys_Register_Set* reg,                                      const ScanSession& session) const override; @@ -86,8 +80,6 @@ public:      void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; -    void search_start_position(Genesys_Device* dev) const override; -      void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,                              Genesys_Register_Set& regs) const override; @@ -103,17 +95,14 @@ public:      void update_hardware_sensors(struct Genesys_Scanner* s) const override; +    void update_home_sensor_gpio(Genesys_Device& dev) const override; +      void load_document(Genesys_Device* dev) const override;      void detect_document_end(Genesys_Device* dev) const override;      void eject_document(Genesys_Device* dev) const override; -    void search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, -                      bool forward, bool black) const override; - -    void move_to_ta(Genesys_Device* dev) const override; -      void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t* data,                             int size) const override; diff --git a/backend/genesys/gl841_registers.h b/backend/genesys/gl841_registers.h index 8e0c204..2fac278 100644 --- a/backend/genesys/gl841_registers.h +++ b/backend/genesys/gl841_registers.h @@ -224,10 +224,12 @@ static constexpr RegShift REG_0x5ES_DECSEL = 5;  static constexpr RegMask REG_0x5E_STOPTIM = 0x1f;  static constexpr RegShift REG_0x5ES_STOPTIM = 0; +static constexpr RegAddr REG_0x60 = 0x60;  static constexpr RegMask REG_0x60_ZIMOD = 0x1f;  static constexpr RegMask REG_0x61_Z1MOD = 0xff;  static constexpr RegMask REG_0x62_Z1MOD = 0xff; +static constexpr RegAddr REG_0x63 = 0x63;  static constexpr RegMask REG_0x63_Z2MOD = 0x1f;  static constexpr RegMask REG_0x64_Z2MOD = 0xff;  static constexpr RegMask REG_0x65_Z2MOD = 0xff; diff --git a/backend/genesys/gl842.cpp b/backend/genesys/gl842.cpp new file mode 100644 index 0000000..d5bebe5 --- /dev/null +++ b/backend/genesys/gl842.cpp @@ -0,0 +1,1066 @@ +/*  sane - Scanner Access Now Easy. + +    Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr> +    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. + +    As a special exception, the authors of SANE give permission for +    additional uses of the libraries contained in this release of SANE. +*/ + +#define DEBUG_DECLARE_ONLY + +#include "gl842_registers.h" +#include "gl842.h" +#include "test_settings.h" + +#include <string> +#include <vector> + +namespace genesys { +namespace gl842 { + +static void gl842_init_registers(Genesys_Device& dev) +{ +    // Within this function SENSOR_DEF marker documents that a register is part +    // of the sensors definition and the actual value is set in +    // gl842_setup_sensor(). + +    DBG_HELPER(dbg); + +    dev.reg.clear(); + +    if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { +        dev.reg.init_reg(0x01, 0x00); +        dev.reg.init_reg(0x02, 0x78); +        dev.reg.init_reg(0x03, 0xbf); +        dev.reg.init_reg(0x04, 0x22); +        dev.reg.init_reg(0x05, 0x48); + +        dev.reg.init_reg(0x06, 0xb8); + +        dev.reg.init_reg(0x07, 0x00); +        dev.reg.init_reg(0x08, 0x00); +        dev.reg.init_reg(0x09, 0x00); +        dev.reg.init_reg(0x0a, 0x00); +        dev.reg.init_reg(0x0d, 0x01); +    } else if (dev.model->model_id == ModelId::CANON_LIDE_90) { +        dev.reg.init_reg(0x01, 0x82); +        dev.reg.init_reg(0x02, 0x10); +        dev.reg.init_reg(0x03, 0x60); +        dev.reg.init_reg(0x04, 0x10); +        dev.reg.init_reg(0x05, 0x8c); + +        dev.reg.init_reg(0x06, 0x18); + +        //dev.reg.init_reg(0x07, 0x00); +        dev.reg.init_reg(0x08, 0x00); +        dev.reg.init_reg(0x09, 0x21); +        dev.reg.init_reg(0x0a, 0x00); +        dev.reg.init_reg(0x0d, 0x00); +    } + +    dev.reg.init_reg(0x10, 0x00); // exposure, overwritten in scanner_setup_sensor() below +    dev.reg.init_reg(0x11, 0x00); // exposure, overwritten in scanner_setup_sensor() below +    dev.reg.init_reg(0x12, 0x00); // exposure, overwritten in scanner_setup_sensor() below +    dev.reg.init_reg(0x13, 0x00); // exposure, overwritten in scanner_setup_sensor() below +    dev.reg.init_reg(0x14, 0x00); // exposure, overwritten in scanner_setup_sensor() below +    dev.reg.init_reg(0x15, 0x00); // exposure, overwritten in scanner_setup_sensor() below + +    // CCD signal settings. +    dev.reg.init_reg(0x16, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x17, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x18, 0x00); // SENSOR_DEF + +    // EXPDMY[0:7]: Exposure time of dummy lines. +    dev.reg.init_reg(0x19, 0x00); // SENSOR_DEF + +    // Various CCD clock settings. +    dev.reg.init_reg(0x1a, 0x00); // SENSOR_DEF +    if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { +        dev.reg.init_reg(0x1b, 0x00); // SENSOR_DEF +    } +    dev.reg.init_reg(0x1c, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x1d, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x1e, 0x10); // WDTIME, LINESEL: setup during sensor and motor setup + +    if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { +        dev.reg.init_reg(0x1f, 0x01); +        dev.reg.init_reg(0x20, 0x27); // BUFSEL: buffer full condition +    } else if (dev.model->model_id == ModelId::CANON_LIDE_90) { +        dev.reg.init_reg(0x1f, 0x02); +        dev.reg.init_reg(0x20, 0x02); // BUFSEL: buffer full condition +    } + +    dev.reg.init_reg(0x21, 0x10); // STEPNO: set during motor setup +    dev.reg.init_reg(0x22, 0x10); // FWDSTEP: set during motor setup +    dev.reg.init_reg(0x23, 0x10); // BWDSTEP: set during motor setup +    dev.reg.init_reg(0x24, 0x10); // FASTNO: set during motor setup +    dev.reg.init_reg(0x25, 0x00); // LINCNT: set during motor setup +    dev.reg.init_reg(0x26, 0x00); // LINCNT: set during motor setup +    dev.reg.init_reg(0x27, 0x00); // LINCNT: set during motor setup + +    dev.reg.init_reg(0x29, 0xff); // LAMPPWM + +    dev.reg.init_reg(0x2c, 0x02); // DPISET: set during sensor setup +    dev.reg.init_reg(0x2d, 0x58); // DPISET: set during sensor setup + +    dev.reg.init_reg(0x2e, 0x80); // BWHI: black/white low threshdold +    dev.reg.init_reg(0x2f, 0x80); // BWLOW: black/white low threshold + +    dev.reg.init_reg(0x30, 0x00); // STRPIXEL: set during sensor setup +    dev.reg.init_reg(0x31, 0x49); // STRPIXEL: set during sensor setup +    dev.reg.init_reg(0x32, 0x53); // ENDPIXEL: set during sensor setup +    dev.reg.init_reg(0x33, 0xb9); // ENDPIXEL: set during sensor setup + +    dev.reg.init_reg(0x34, 0x13); // DUMMY: SENSOR_DEF +    dev.reg.init_reg(0x35, 0x00); // MAXWD: set during scan setup +    dev.reg.init_reg(0x36, 0x40); // MAXWD: set during scan setup +    dev.reg.init_reg(0x37, 0x00); // MAXWD: set during scan setup +    dev.reg.init_reg(0x38, 0x2a); // LPERIOD: SENSOR_DEF +    dev.reg.init_reg(0x39, 0xf8); // LPERIOD: SENSOR_DEF +    dev.reg.init_reg(0x3d, 0x00); // FEEDL: set during motor setup +    dev.reg.init_reg(0x3e, 0x00); // FEEDL: set during motor setup +    dev.reg.init_reg(0x3f, 0x01); // FEEDL: set during motor setup + +    dev.reg.init_reg(0x52, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x53, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x54, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x55, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x56, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x57, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x58, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x59, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x5a, 0x00); // SENSOR_DEF + +    if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { +        dev.reg.init_reg(0x5e, 0x01); // DECSEL, STOPTIM +    } else if (dev.model->model_id == ModelId::CANON_LIDE_90) { +        dev.reg.init_reg(0x5e, 0x41); // DECSEL, STOPTIM +        dev.reg.init_reg(0x5d, 0x20); +    } +    dev.reg.init_reg(0x5f, 0x10); // FMOVDEC: set during motor setup + +    dev.reg.init_reg(0x60, 0x00); // Z1MOD: overwritten during motor setup +    dev.reg.init_reg(0x61, 0x00); // Z1MOD: overwritten during motor setup +    dev.reg.init_reg(0x62, 0x00); // Z1MOD: overwritten during motor setup +    dev.reg.init_reg(0x63, 0x00); // Z2MOD: overwritten during motor setup +    dev.reg.init_reg(0x64, 0x00); // Z2MOD: overwritten during motor setup +    dev.reg.init_reg(0x65, 0x00); // Z2MOD: overwritten during motor setup + +    if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { +        dev.reg.init_reg(0x67, 0x7f); // STEPSEL, MTRPWM: partially overwritten during motor setup +        dev.reg.init_reg(0x68, 0x7f); // FSTPSEL, FASTPWM: partially overwritten during motor setup +    } else if (dev.model->model_id == ModelId::CANON_LIDE_90) { +        dev.reg.init_reg(0x66, 0x00); // PHFREQ +        dev.reg.init_reg(0x67, 0x40); // STEPSEL, MTRPWM: partially overwritten during motor setup +        dev.reg.init_reg(0x68, 0x40); // FSTPSEL, FASTPWM: partially overwritten during motor setup +    } +    dev.reg.init_reg(0x69, 0x10); // FSHDEC: overwritten during motor setup +    dev.reg.init_reg(0x6a, 0x10); // FMOVNO: overwritten during motor setup + +    // 0x6b, 0x6c, 0x6d, 0x6e, 0x6f - set according to gpio tables. See gl842_init_gpio. + +    dev.reg.init_reg(0x70, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x71, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x72, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x73, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x74, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x75, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x76, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x77, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x78, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x79, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x7a, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x7b, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x7c, 0x00); // SENSOR_DEF +    dev.reg.init_reg(0x7d, 0x00); // SENSOR_DEF + +    // 0x7e - set according to gpio tables. See gl842_init_gpio. + +    dev.reg.init_reg(0x7f, 0x00); // SENSOR_DEF + +    // VRHOME, VRMOVE, VRBACK, VRSCAN: Vref settings of the motor driver IC for +    // moving in various situations. +    dev.reg.init_reg(0x80, 0x00); // MOTOR_PROFILE + +    if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { +        dev.reg.init_reg(0x81, 0x00); +        dev.reg.init_reg(0x82, 0x00); +        dev.reg.init_reg(0x83, 0x00); +        dev.reg.init_reg(0x84, 0x00); +        dev.reg.init_reg(0x85, 0x00); +        dev.reg.init_reg(0x86, 0x00); +        dev.reg.init_reg(0x87, 0x00); +    } else if (dev.model->model_id == ModelId::CANON_LIDE_90) { +        dev.reg.init_reg(0x7e, 0x00); +        dev.reg.init_reg(0x81, 0x00); +        dev.reg.init_reg(0x82, 0x0f); +        dev.reg.init_reg(0x83, 0x00); +        dev.reg.init_reg(0x84, 0x0e); +        dev.reg.init_reg(0x85, 0x00); +        dev.reg.init_reg(0x86, 0x0d); +        dev.reg.init_reg(0x87, 0x00); +        dev.reg.init_reg(0x88, 0x00); +        dev.reg.init_reg(0x89, 0x00); +    } + +    const auto& sensor = sanei_genesys_find_sensor_any(&dev); +    sanei_genesys_set_dpihw(dev.reg, sensor.register_dpihw); + +    scanner_setup_sensor(dev, sensor, dev.reg); +} + +// Set values of analog frontend +void CommandSetGl842::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const +{ +    DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" : +                               set == AFE_SET ? "set" : +                               set == AFE_POWER_SAVE ? "powersave" : "huh?"); +    (void) sensor; + +    if (set == AFE_INIT) { +        dev->frontend = dev->frontend_initial; +    } + +    // check analog frontend type +    // FIXME: looks like we write to that register with initial data +    uint8_t fe_type = dev->interface->read_register(REG_0x04) & REG_0x04_FESET; +    if (fe_type == 2 || dev->model->model_id == ModelId::CANON_LIDE_90) { +        for (const auto& reg : dev->frontend.regs) { +            dev->interface->write_fe_register(reg.address, reg.value); +        } +        return; +    } +    if (fe_type != 0) { +        throw SaneException(SANE_STATUS_UNSUPPORTED, "unsupported frontend type %d", fe_type); +    } + +    for (unsigned i = 1; i <= 3; i++) { +        dev->interface->write_fe_register(i, dev->frontend.regs.get_value(0x00 + i)); +    } +    for (const auto& reg : sensor.custom_fe_regs) { +        dev->interface->write_fe_register(reg.address, reg.value); +    } + +    for (unsigned i = 0; i < 3; i++) { +        dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i)); +    } + +    for (unsigned i = 0; i < 3; i++) { +        dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i)); +    } +} + +static void gl842_init_motor_regs_scan(Genesys_Device* dev, +                                       const Genesys_Sensor& sensor, +                                       const ScanSession& session, +                                       Genesys_Register_Set* reg, +                                       const MotorProfile& motor_profile, +                                       unsigned int exposure, +                                       unsigned scan_yres, +                                       unsigned int scan_lines, +                                       unsigned int scan_dummy, +                                       unsigned int feed_steps, +                                       ScanFlag flags) +{ +    DBG_HELPER_ARGS(dbg, "exposure=%d, scan_yres=%d, step_type=%d, scan_lines=%d, scan_dummy=%d, " +                         "feed_steps=%d, flags=%x", +                    exposure, scan_yres, static_cast<unsigned>(motor_profile.step_type), +                    scan_lines, scan_dummy, feed_steps, static_cast<unsigned>(flags)); + +    unsigned step_multiplier = 2; +    bool use_fast_fed = false; + +    if ((scan_yres >= 300 && feed_steps > 900) || (has_flag(flags, ScanFlag::FEEDING))) { +        use_fast_fed = true; +    } +    if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) { +        use_fast_fed = false; +    } + +    reg->set24(REG_LINCNT, scan_lines); + +    reg->set8(REG_0x02, 0); +    sanei_genesys_set_motor_power(*reg, true); + +    std::uint8_t reg02 = reg->get8(REG_0x02); +    if (use_fast_fed) { +        reg02 |= REG_0x02_FASTFED; +    } else { +        reg02 &= ~REG_0x02_FASTFED; +    } + +    // in case of automatic go home, move until home sensor +    if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { +        reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME; +    } + +    // disable backtracking if needed +    if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || +        (scan_yres >= 2400) || +        (scan_yres >= sensor.full_resolution)) +    { +        reg02 |= REG_0x02_ACDCDIS; +    } + +    if (has_flag(flags, ScanFlag::REVERSE)) { +        reg02 |= REG_0x02_MTRREV; +    } else { +        reg02 &= ~REG_0x02_MTRREV; +    } +    reg->set8(REG_0x02, reg02); + +    // scan and backtracking slope table +    auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, exposure, +                                         step_multiplier, motor_profile); + +    scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); +    scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); +    scanner_send_slope_table(dev, sensor, STOP_TABLE, scan_table.table); + +    reg->set8(REG_STEPNO, scan_table.table.size() / step_multiplier); +    reg->set8(REG_FASTNO, scan_table.table.size() / step_multiplier); +    reg->set8(REG_FSHDEC, scan_table.table.size() / step_multiplier); + +    // fast table +    const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); +    if (fast_profile == nullptr) { +        fast_profile = &motor_profile; +    } + +    auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, +                                                 *fast_profile); + +    scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); +    scanner_send_slope_table(dev, sensor, HOME_TABLE, fast_table.table); + +    reg->set8(REG_FMOVNO, fast_table.table.size() / step_multiplier); + +    if (motor_profile.motor_vref != -1 && fast_profile->motor_vref != 1) { +        std::uint8_t vref = 0; +        vref |= (motor_profile.motor_vref << REG_0x80S_TABLE1_NORMAL) & REG_0x80_TABLE1_NORMAL; +        vref |= (motor_profile.motor_vref << REG_0x80S_TABLE2_BACK) & REG_0x80_TABLE2_BACK; +        vref |= (fast_profile->motor_vref << REG_0x80S_TABLE4_FAST) & REG_0x80_TABLE4_FAST; +        vref |= (fast_profile->motor_vref << REG_0x80S_TABLE5_GO_HOME) & REG_0x80_TABLE5_GO_HOME; +        reg->set8(REG_0x80, vref); +    } + +    // substract acceleration distance from feedl +    unsigned feedl = feed_steps; +    feedl <<= static_cast<unsigned>(motor_profile.step_type); + +    unsigned dist = scan_table.table.size() / step_multiplier; + +    if (use_fast_fed) { +        dist += (fast_table.table.size() / step_multiplier) * 2; +    } + +    // make sure when don't insane value : XXX STEF XXX in this case we should +    // fall back to single table move +    if (dist < feedl) { +        feedl -= dist; +    } else { +        feedl = 1; +    } + +    reg->set24(REG_FEEDL, feedl); + +    // doesn't seem to matter that much +    std::uint32_t z1, z2; +    sanei_genesys_calculate_zmod(use_fast_fed, +                                 exposure, +                                 scan_table.table, +                                 scan_table.table.size() / step_multiplier, +                                 feedl, +                                 scan_table.table.size() / step_multiplier, +                                 &z1, +                                 &z2); +    if (scan_yres > 600) { +        z1 = 0; +        z2 = 0; +    } + +    reg->set24(REG_Z1MOD, z1); +    reg->set24(REG_Z2MOD, z2); + +    reg->set8_mask(REG_0x1E, scan_dummy, 0x0f); + +    reg->set8_mask(REG_0x67, static_cast<unsigned>(motor_profile.step_type) << REG_0x67S_STEPSEL, +                   REG_0x67_STEPSEL); +    reg->set8_mask(REG_0x68, static_cast<unsigned>(fast_profile->step_type) << REG_0x68S_FSTPSEL, +                   REG_0x68_FSTPSEL); + +    // steps for STOP table +    reg->set8(REG_FMOVDEC, fast_table.table.size() / step_multiplier); +} + +static void gl842_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                         Genesys_Register_Set* reg, unsigned int exposure, +                                         const ScanSession& session) +{ +    DBG_HELPER(dbg); + +    scanner_setup_sensor(*dev, sensor, *reg); + +    dev->cmd_set->set_fe(dev, sensor, AFE_SET); + +    // enable shading +    regs_set_optical_off(dev->model->asic_type, *reg); +    if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || +        has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || +        session.use_host_side_calib) +    { +        reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET; + +    } else { +        reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET; +    } + +    bool use_shdarea = true; + +    if (use_shdarea) { +        reg->find_reg(REG_0x01).value |= REG_0x01_SHDAREA; +    } else { +        reg->find_reg(REG_0x01).value &= ~REG_0x01_SHDAREA; +    } + +    if (dev->model->model_id == ModelId::CANON_8600F) { +        reg->find_reg(REG_0x03).value |= REG_0x03_AVEENB; +    } else { +        reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB; +    } + +    // FIXME: we probably don't need to set exposure to registers at this point. It was this way +    // before a refactor. +    sanei_genesys_set_lamp_power(dev, sensor, *reg, +                                 !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP)); + +    // select XPA +    reg->find_reg(REG_0x03).value &= ~REG_0x03_XPASEL; +    if (has_flag(session.params.flags, ScanFlag::USE_XPA)) { +        reg->find_reg(REG_0x03).value |= REG_0x03_XPASEL; +    } +    reg->state.is_xpa_on = has_flag(session.params.flags, ScanFlag::USE_XPA); + +    // BW threshold +    reg->set8(REG_0x2E, 0x7f); +    reg->set8(REG_0x2F, 0x7f); + +    // monochrome / color scan parameters +    std::uint8_t reg04 = reg->get8(REG_0x04); +    reg04 = reg04 & REG_0x04_FESET; + +    switch (session.params.depth) { +        case 8: +            break; +        case 16: +            reg04 |= REG_0x04_BITSET; +            break; +    } + +    if (session.params.channels == 1) { +        switch (session.params.color_filter) { +            case ColorFilter::RED: reg04 |= 0x14; break; +            case ColorFilter::BLUE: reg04 |= 0x1c; break; +            case ColorFilter::GREEN: reg04 |= 0x18; break; +            default: +                break; // should not happen +        } +    } else { +        switch (dev->frontend.layout.type) { +            case FrontendType::WOLFSON: +                // pixel by pixel +                reg04 |= 0x10; +                break; +            case FrontendType::ANALOG_DEVICES: +                // slow color pixel by pixel +                reg04 |= 0x20; +                break; +            default: +                throw SaneException("Invalid frontend type %d", +                                    static_cast<unsigned>(dev->frontend.layout.type)); +        } +    } + +    reg->set8(REG_0x04, reg04); + +    const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, +                                                         session.params.channels, +                                                         session.params.scan_method); +    sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw); + +    if (should_enable_gamma(session, sensor)) { +        reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; +    } else { +        reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB; +    } + +    reg->set16(REG_DPISET, sensor.register_dpiset); + +    reg->set16(REG_STRPIXEL, session.pixel_startx); +    reg->set16(REG_ENDPIXEL, session.pixel_endx); + +    if (dev->model->is_cis) { +        reg->set24(REG_MAXWD, session.output_line_bytes_raw * session.params.channels); +    } else { +        reg->set24(REG_MAXWD, session.output_line_bytes_raw); +    } + +    unsigned tgtime = exposure / 65536 + 1; +    reg->set16(REG_LPERIOD, exposure / tgtime); + +    reg->set8(REG_DUMMY, sensor.dummy_pixel); +} + +void CommandSetGl842::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                                 Genesys_Register_Set* reg, +                                                 const ScanSession& session) const +{ +    DBG_HELPER(dbg); +    session.assert_computed(); + +    // we enable true gray for cis scanners only, and just when doing scan since color calibration +    // is OK for this mode + +    int dummy = 0; + +  /* slope_dpi */ +  /* cis color scan is effectively a gray scan with 3 gray lines per color line and a FILTER of 0 */ +    int slope_dpi = 0; +    if (dev->model->is_cis) { +        slope_dpi = session.params.yres * session.params.channels; +    } else { +        slope_dpi = session.params.yres; +    } +    slope_dpi = slope_dpi * (1 + dummy); + +    int exposure = sensor.exposure_lperiod; +    if (exposure < 0) { +        throw std::runtime_error("Exposure not defined in sensor definition"); +    } +    if (dev->model->model_id == ModelId::CANON_LIDE_90) { +        exposure *= 2; +    } +    const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure, session); + +    // now _LOGICAL_ optical values used are known, setup registers +    gl842_init_optical_regs_scan(dev, sensor, reg, exposure, session); +    gl842_init_motor_regs_scan(dev, sensor, session, reg, motor_profile, exposure, slope_dpi, +                               session.optical_line_count, dummy, session.params.starty, +                               session.params.flags); + +    setup_image_pipeline(*dev, session); + +    dev->read_active = true; + +    dev->session = session; + +    dev->total_bytes_read = 0; +    dev->total_bytes_to_read = session.output_line_bytes_requested * session.params.lines; +} + +ScanSession CommandSetGl842::calculate_scan_session(const Genesys_Device* dev, +                                                    const Genesys_Sensor& sensor, +                                                    const Genesys_Settings& settings) const +{ +    DBG_HELPER(dbg); +    debug_dump(DBG_info, settings); + +    ScanFlag flags = ScanFlag::NONE; + +    float move = 0.0f; +    if (settings.scan_method == ScanMethod::TRANSPARENCY || +        settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +    { +        // note: scanner_move_to_ta() function has already been called and the sensor is at the +        // transparency adapter +        if (!dev->ignore_offsets) { +            move = dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta; +        } +        flags |= ScanFlag::USE_XPA; +    } else { +        if (!dev->ignore_offsets) { +            move = dev->model->y_offset; +        } +    } + +    move += settings.tl_y; + +    int move_dpi = dev->motor.base_ydpi; +    move = static_cast<float>((move * move_dpi) / MM_PER_INCH); + +    float start = 0.0f; +    if (settings.scan_method==ScanMethod::TRANSPARENCY || +        settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +    { +        start = dev->model->x_offset_ta; +    } else { +        start = dev->model->x_offset; +    } +    start = start + settings.tl_x; + +    start = static_cast<float>((start * settings.xres) / MM_PER_INCH); + +    ScanSession session; +    session.params.xres = settings.xres; +    session.params.yres = settings.yres; +    session.params.startx = static_cast<unsigned>(start); +    session.params.starty = static_cast<unsigned>(move); +    session.params.pixels = settings.pixels; +    session.params.requested_pixels = settings.requested_pixels; +    session.params.lines = settings.lines; +    session.params.depth = settings.depth; +    session.params.channels = settings.get_channels(); +    session.params.scan_method = settings.scan_method; +    session.params.scan_mode = settings.scan_mode; +    session.params.color_filter = settings.color_filter; +    session.params.flags = flags; +    compute_session(dev, session, sensor); + +    return session; +} + +void CommandSetGl842::save_power(Genesys_Device* dev, bool enable) const +{ +    (void) dev; +    DBG_HELPER_ARGS(dbg, "enable = %d", enable); +} + +void CommandSetGl842::set_powersaving(Genesys_Device* dev, int delay /* in minutes */) const +{ +    (void) dev; +    DBG_HELPER_ARGS(dbg, "delay = %d", delay); +} + +void CommandSetGl842::eject_document(Genesys_Device* dev) const +{ +    (void) dev; +    DBG_HELPER(dbg); +} + + +void CommandSetGl842::load_document(Genesys_Device* dev) const +{ +    DBG_HELPER(dbg); +    (void) dev; +} + +void CommandSetGl842::detect_document_end(Genesys_Device* dev) const +{ +    DBG_HELPER(dbg); +    (void) dev; +    throw SaneException(SANE_STATUS_UNSUPPORTED); +} + +// Send the low-level scan command +void CommandSetGl842::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                 Genesys_Register_Set* reg, bool start_motor) const +{ +    DBG_HELPER(dbg); +    (void) sensor; + +    if (reg->state.is_xpa_on && reg->state.is_lamp_on && +        !has_flag(dev->model->flags, ModelFlag::TA_NO_SECONDARY_LAMP)) +    { +        dev->cmd_set->set_xpa_lamp_power(*dev, true); +    } +    if (reg->state.is_xpa_on && !has_flag(dev->model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR)) { +        dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY); +    } + +    if (dev->model->model_id == ModelId::CANON_LIDE_90) { +        if (has_flag(dev->session.params.flags, ScanFlag::REVERSE)) { +            dev->interface->write_register(REG_0x6B, 0x01); +            dev->interface->write_register(REG_0x6C, 0x02); +        } else { +            dev->interface->write_register(REG_0x6B, 0x03); +            switch (dev->session.params.xres) { +                case 150: dev->interface->write_register(REG_0x6C, 0x74); break; +                case 300: dev->interface->write_register(REG_0x6C, 0x38); break; +                case 600: dev->interface->write_register(REG_0x6C, 0x1c); break; +                case 1200: dev->interface->write_register(REG_0x6C, 0x2c); break; +                case 2400: dev->interface->write_register(REG_0x6C, 0x0c); break; +                default: +                    break; +            } +        } +        dev->interface->sleep_ms(100); +    } + +    scanner_clear_scan_and_feed_counts(*dev); + +    // enable scan and motor +    std::uint8_t val = dev->interface->read_register(REG_0x01); +    val |= REG_0x01_SCAN; +    dev->interface->write_register(REG_0x01, val); + +    scanner_start_action(*dev, start_motor); + +    switch (reg->state.motor_mode) { +        case MotorMode::PRIMARY: { +            if (reg->state.is_motor_on) { +                dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); +            } +            break; +        } +        case MotorMode::PRIMARY_AND_SECONDARY: { +            if (reg->state.is_motor_on) { +                dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); +                dev->advance_head_pos_by_session(ScanHeadId::SECONDARY); +            } +            break; +        } +        case MotorMode::SECONDARY: { +            if (reg->state.is_motor_on) { +                dev->advance_head_pos_by_session(ScanHeadId::SECONDARY); +            } +            break; +        } +    } +} + +void CommandSetGl842::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg, +                               bool check_stop) const +{ +    DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop); + +    if (reg->state.is_xpa_on) { +        dev->cmd_set->set_xpa_lamp_power(*dev, false); +    } + +    if (!dev->model->is_sheetfed) { +        scanner_stop_action(*dev); +    } +} + +void CommandSetGl842::move_back_home(Genesys_Device* dev, bool wait_until_home) const +{ +    scanner_move_back_home(*dev, wait_until_home); +} + +void CommandSetGl842::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                            Genesys_Register_Set& regs) const +{ +    DBG_HELPER(dbg); +    int move; + +    float calib_size_mm = 0; +    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || +        dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +    { +        calib_size_mm = dev->model->y_size_calib_ta_mm; +    } else { +        calib_size_mm = dev->model->y_size_calib_mm; +    } + +    unsigned resolution = sensor.shading_resolution; + +    unsigned channels = 3; +    const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels, +                                                         dev->settings.scan_method); + +    unsigned calib_pixels = 0; +    unsigned calib_pixels_offset = 0; + +    if (should_calibrate_only_active_area(*dev, dev->settings)) { +        float offset = dev->model->x_offset_ta; +        // FIXME: we should use resolution here +        offset = static_cast<float>((offset * dev->settings.xres) / MM_PER_INCH); + +        float size = dev->model->x_size_ta; +        size = static_cast<float>((size * dev->settings.xres) / MM_PER_INCH); + +        calib_pixels_offset = static_cast<std::size_t>(offset); +        calib_pixels = static_cast<std::size_t>(size); +    } else { +        calib_pixels_offset = 0; +        calib_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; +    } + +    ScanFlag flags = ScanFlag::DISABLE_SHADING | +                     ScanFlag::DISABLE_GAMMA | +                     ScanFlag::DISABLE_BUFFER_FULL_MOVE; + +    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || +        dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +    { +        // note: scanner_move_to_ta() function has already been called and the sensor is at the +        // transparency adapter +        move = static_cast<int>(dev->model->y_offset_calib_white_ta - +                                dev->model->y_offset_sensor_to_ta); +        flags |= ScanFlag::USE_XPA; +    } else { +        move = static_cast<int>(dev->model->y_offset_calib_white); +    } + +    move = static_cast<int>((move * resolution) / MM_PER_INCH); +    unsigned calib_lines = static_cast<unsigned>(calib_size_mm * resolution / MM_PER_INCH); + +    ScanSession session; +    session.params.xres = resolution; +    session.params.yres = resolution; +    session.params.startx = calib_pixels_offset; +    session.params.starty = move; +    session.params.pixels = calib_pixels; +    session.params.lines = calib_lines; +    session.params.depth = 16; +    session.params.channels = channels; +    session.params.scan_method = dev->settings.scan_method; +    session.params.scan_mode = dev->settings.scan_mode; +    session.params.color_filter = dev->settings.color_filter; +    session.params.flags = flags; +    compute_session(dev, session, calib_sensor); + +    init_regs_for_scan_session(dev, calib_sensor, ®s, session); + +    dev->calib_session = session; +} + +void CommandSetGl842::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const +{ +    DBG_HELPER(dbg); + +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) +        return; // No gamma on this model + +    unsigned size = 256; + +    std::vector<uint8_t> gamma(size * 2 * 3); + +    std::vector<uint16_t> rgamma = get_gamma_table(dev, sensor, GENESYS_RED); +    std::vector<uint16_t> ggamma = get_gamma_table(dev, sensor, GENESYS_GREEN); +    std::vector<uint16_t> bgamma = get_gamma_table(dev, sensor, GENESYS_BLUE); + +    // copy sensor specific's gamma tables +    for (unsigned i = 0; i < size; i++) { +        gamma[i * 2 + size * 0 + 0] = rgamma[i] & 0xff; +        gamma[i * 2 + size * 0 + 1] = (rgamma[i] >> 8) & 0xff; +        gamma[i * 2 + size * 2 + 0] = ggamma[i] & 0xff; +        gamma[i * 2 + size * 2 + 1] = (ggamma[i] >> 8) & 0xff; +        gamma[i * 2 + size * 4 + 0] = bgamma[i] & 0xff; +        gamma[i * 2 + size * 4 + 1] = (bgamma[i] >> 8) & 0xff; +    } + +    dev->interface->write_gamma(0x28, 0x0000, gamma.data(), size * 2 * 3); +} + +SensorExposure CommandSetGl842::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                                Genesys_Register_Set& regs) const +{ +    return scanner_led_calibration(*dev, sensor, regs); +} + +void CommandSetGl842::offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                         Genesys_Register_Set& regs) const +{ +    scanner_offset_calibration(*dev, sensor, regs); +} + +void CommandSetGl842::coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                              Genesys_Register_Set& regs, int dpi) const +{ +    scanner_coarse_gain_calibration(*dev, sensor, regs, dpi); +} + +void CommandSetGl842::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                           Genesys_Register_Set* reg) const +{ +    DBG_HELPER(dbg); +    (void) sensor; + +    unsigned channels = 3; +    unsigned resolution = dev->model->get_resolution_settings(dev->settings.scan_method) +                                     .get_nearest_resolution_x(600); + +    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 / 2; + +    *reg = dev->reg; + +    auto 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 = (num_pixels / 2) * resolution / calib_sensor.full_resolution; +    session.params.starty = 0; +    session.params.pixels = num_pixels; +    session.params.lines = 1; +    session.params.depth = dev->model->bpp_color_values.front(); +    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); + +    init_regs_for_scan_session(dev, calib_sensor, reg, session); + +    sanei_genesys_set_motor_power(*reg, false); +} + +static void gl842_init_gpio(Genesys_Device* dev) +{ +    DBG_HELPER(dbg); +    apply_registers_ordered(dev->gpo.regs, { 0x6e, 0x6f }, [&](const GenesysRegisterSetting& reg) +    { +        dev->interface->write_register(reg.address, reg.value); +    }); +} + +void CommandSetGl842::asic_boot(Genesys_Device* dev, bool cold) const +{ +    DBG_HELPER(dbg); + +    if (cold) { +        dev->interface->write_register(0x0e, 0x01); +        dev->interface->write_register(0x0e, 0x00); +    } + +    // setup initial register values +    gl842_init_registers(*dev); +    dev->interface->write_registers(dev->reg); + +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { +        uint8_t data[32] = { +            0xd0, 0x38, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, +            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +            0x6a, 0x73, 0x63, 0x68, 0x69, 0x65, 0x6e, 0x00, +        }; + +        dev->interface->write_buffer(0x3c, 0x010a00, data, 32); +    } + +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200) { +        dev->interface->write_0x8c(0x10, 0x94); +    } +    if (dev->model->model_id == ModelId::CANON_LIDE_90) { +        dev->interface->write_0x8c(0x10, 0xd4); +    } + +    // set RAM read address +    dev->interface->write_register(REG_0x2A, 0x00); +    dev->interface->write_register(REG_0x2B, 0x00); + +    // setup gpio +    gl842_init_gpio(dev); +    dev->interface->sleep_ms(100); +} + +void CommandSetGl842::init(Genesys_Device* dev) const +{ +    DBG_INIT(); +    DBG_HELPER(dbg); + +    sanei_genesys_asic_init(dev); +} + +void CommandSetGl842::update_hardware_sensors(Genesys_Scanner* s) const +{ +    DBG_HELPER(dbg); +    (void) s; +} + +void CommandSetGl842::update_home_sensor_gpio(Genesys_Device& dev) const +{ +    DBG_HELPER(dbg); +    if (dev.model->model_id == ModelId::CANON_LIDE_90) { +        std::uint8_t val = dev.interface->read_register(REG_0x6C); +        val |= 0x02; +        dev.interface->write_register(REG_0x6C, val); +    } +} + +/** + * Send shading calibration data. The buffer is considered to always hold values + * for all the channels. + */ +void CommandSetGl842::send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                        uint8_t* data, int size) const +{ +    DBG_HELPER(dbg); + +    int offset = 0; +    unsigned length = size; + +    if (dev->reg.get8(REG_0x01) & REG_0x01_SHDAREA) { +        offset = dev->session.params.startx * sensor.shading_resolution / +                 dev->session.params.xres; + +        length = dev->session.output_pixels * sensor.shading_resolution / +                 dev->session.params.xres; + +        offset += sensor.shading_pixel_offset; + +        // 16 bit words, 2 words per color, 3 color channels +        length *= 2 * 2 * 3; +        offset *= 2 * 2 * 3; +    } else { +        offset += sensor.shading_pixel_offset * 2 * 2 * 3; +    } + +    dev->interface->record_key_value("shading_offset", std::to_string(offset)); +    dev->interface->record_key_value("shading_length", std::to_string(length)); + +    std::vector<uint8_t> final_data(length, 0); + +    unsigned count = 0; +    if (offset < 0) { +        count += (-offset); +        length -= (-offset); +        offset = 0; +    } +    if (static_cast<int>(length) + offset > static_cast<int>(size)) { +        length = size - offset; +    } + +    for (unsigned i = 0; i < length; i++) { +        final_data[count++] = data[offset + i]; +        count++; +    } + +    dev->interface->write_buffer(0x3c, 0, final_data.data(), count); +} + +bool CommandSetGl842::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const +{ +    (void) dev; +    return true; +} + +void CommandSetGl842::wait_for_motor_stop(Genesys_Device* dev) const +{ +    (void) dev; +} + +} // namespace gl842 +} // namespace genesys diff --git a/backend/genesys/gl842.h b/backend/genesys/gl842.h new file mode 100644 index 0000000..288d29c --- /dev/null +++ b/backend/genesys/gl842.h @@ -0,0 +1,128 @@ +/* sane - Scanner Access Now Easy. + +   Copyright (C) 2010-2013 Stéphane Voltz <stef.dev@free.fr> + +   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. + +   As a special exception, the authors of SANE give permission for +   additional uses of the libraries contained in this release of SANE. + +   The exception is that, if you link a SANE library with other files +   to produce an executable, this does not by itself cause the +   resulting executable to be covered by the GNU General Public +   License.  Your use of that executable is in no way restricted on +   account of linking the SANE library code into it. + +   This exception does not, however, invalidate any other reasons why +   the executable file might be covered by the GNU General Public +   License. + +   If you submit changes to SANE to the maintainers to be included in +   a subsequent release, you agree by submitting the changes that +   those changes may be distributed with this exception intact. + +   If you write modifications of your own for SANE, it is your choice +   whether to permit this exception to apply to your modifications. +   If you do not wish that, delete this exception notice. +*/ + +#include "genesys.h" +#include "command_set_common.h" + +#ifndef BACKEND_GENESYS_GL842_H +#define BACKEND_GENESYS_GL842_H + +namespace genesys { +namespace gl842 { + +class CommandSetGl842 : public CommandSetCommon +{ +public: +    ~CommandSetGl842() override = default; + +    bool needs_home_before_init_regs_for_scan(Genesys_Device* dev) const override; + +    void init(Genesys_Device* dev) const override; + +    void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, +                              Genesys_Register_Set* regs) const override; + +    void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor, +                               Genesys_Register_Set& regs) const override; + +    void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                    Genesys_Register_Set* reg, +                                    const ScanSession& session) const override; + +    void set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const override; +    void set_powersaving(Genesys_Device* dev, int delay) const override; +    void save_power(Genesys_Device* dev, bool enable) const override; + +    void begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor, +                    Genesys_Register_Set* regs, bool start_motor) const override; + +    void end_scan(Genesys_Device* dev, Genesys_Register_Set* regs, bool check_stop) const override; + +    void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; + +    void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, +                            Genesys_Register_Set& regs) const override; + +    void coarse_gain_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                 Genesys_Register_Set& regs, int dpi) const override; + +    SensorExposure led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                   Genesys_Register_Set& regs) const override; + +    void wait_for_motor_stop(Genesys_Device* dev) const override; + +    void move_back_home(Genesys_Device* dev, bool wait_until_home) const override; + +    void update_hardware_sensors(struct Genesys_Scanner* s) const override; + +    void update_home_sensor_gpio(Genesys_Device& dev) const override; + +    void load_document(Genesys_Device* dev) const override; + +    void detect_document_end(Genesys_Device* dev) const override; + +    void eject_document(Genesys_Device* dev) const override; + +    void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t* data, +                           int size) const override; + +    ScanSession calculate_scan_session(const Genesys_Device* dev, +                                       const Genesys_Sensor& sensor, +                                       const Genesys_Settings& settings) const override; + +    void asic_boot(Genesys_Device* dev, bool cold) const override; +}; + +enum SlopeTable +{ +    SCAN_TABLE = 0, // table 1 at 0x4000 +    BACKTRACK_TABLE = 1, // table 2 at 0x4800 +    STOP_TABLE = 2, // table 3 at 0x5000 +    FAST_TABLE = 3, // table 4 at 0x5800 +    HOME_TABLE = 4, // table 5 at 0x6000 +}; + +} // namespace gl842 +} // namespace genesys + +#endif // BACKEND_GENESYS_GL842_H diff --git a/backend/genesys/gl842_registers.h b/backend/genesys/gl842_registers.h new file mode 100644 index 0000000..b6934ce --- /dev/null +++ b/backend/genesys/gl842_registers.h @@ -0,0 +1,285 @@ +/* sane - Scanner Access Now Easy. + +   Copyright (C) 2019 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. + +   As a special exception, the authors of SANE give permission for +   additional uses of the libraries contained in this release of SANE. + +   The exception is that, if you link a SANE library with other files +   to produce an executable, this does not by itself cause the +   resulting executable to be covered by the GNU General Public +   License.  Your use of that executable is in no way restricted on +   account of linking the SANE library code into it. + +   This exception does not, however, invalidate any other reasons why +   the executable file might be covered by the GNU General Public +   License. + +   If you submit changes to SANE to the maintainers to be included in +   a subsequent release, you agree by submitting the changes that +   those changes may be distributed with this exception intact. + +   If you write modifications of your own for SANE, it is your choice +   whether to permit this exception to apply to your modifications. +   If you do not wish that, delete this exception notice. +*/ + +#ifndef BACKEND_GENESYS_gl842_REGISTERS_H +#define BACKEND_GENESYS_gl842_REGISTERS_H + +#include <cstdint> + +namespace genesys { +namespace gl842 { + +using RegAddr = std::uint16_t; +using RegMask = std::uint8_t; +using RegShift = unsigned; + +static constexpr RegAddr REG_0x01 = 0x01; +static constexpr RegMask REG_0x01_CISSET = 0x80; +static constexpr RegMask REG_0x01_DOGENB = 0x40; +static constexpr RegMask REG_0x01_DVDSET = 0x20; +static constexpr RegMask REG_0x01_M15DRAM = 0x08; +static constexpr RegMask REG_0x01_DRAMSEL = 0x04; +static constexpr RegMask REG_0x01_SHDAREA = 0x02; +static constexpr RegMask REG_0x01_SCAN = 0x01; + +static constexpr RegAddr REG_0x02 = 0x02; +static constexpr RegMask REG_0x02_NOTHOME = 0x80; +static constexpr RegMask REG_0x02_ACDCDIS = 0x40; +static constexpr RegMask REG_0x02_AGOHOME = 0x20; +static constexpr RegMask REG_0x02_MTRPWR = 0x10; +static constexpr RegMask REG_0x02_FASTFED = 0x08; +static constexpr RegMask REG_0x02_MTRREV = 0x04; +static constexpr RegMask REG_0x02_HOMENEG = 0x02; +static constexpr RegMask REG_0x02_LONGCURV = 0x01; + +static constexpr RegAddr REG_0x03 = 0x03; +static constexpr RegMask REG_0x03_LAMPDOG = 0x80; +static constexpr RegMask REG_0x03_AVEENB = 0x40; +static constexpr RegMask REG_0x03_XPASEL = 0x20; +static constexpr RegMask REG_0x03_LAMPPWR = 0x10; +static constexpr RegMask REG_0x03_LAMPTIM = 0x0f; + +static constexpr RegAddr REG_0x04 = 0x04; +static constexpr RegMask REG_0x04_LINEART = 0x80; +static constexpr RegMask REG_0x04_BITSET = 0x40; +static constexpr RegMask REG_0x04_AFEMOD = 0x30; +static constexpr RegMask REG_0x04_FILTER = 0x0c; +static constexpr RegMask REG_0x04_FESET = 0x03; + +static constexpr RegShift REG_0x04S_AFEMOD = 4; + +static constexpr RegAddr REG_0x05 = 0x05; +static constexpr RegMask REG_0x05_DPIHW = 0xc0; +static constexpr RegMask REG_0x05_DPIHW_600 = 0x00; +static constexpr RegMask REG_0x05_DPIHW_1200 = 0x40; +static constexpr RegMask REG_0x05_DPIHW_2400 = 0x80; +static constexpr RegMask REG_0x05_DPIHW_4800 = 0xc0; +static constexpr RegMask REG_0x05_MTLLAMP = 0x30; +static constexpr RegMask REG_0x05_GMMENB = 0x08; +static constexpr RegMask REG_0x05_MTLBASE = 0x03; + +static constexpr RegAddr REG_0x06 = 0x06; +static constexpr RegMask REG_0x06_SCANMOD = 0xe0; +static constexpr RegShift REG_0x06S_SCANMOD = 5; +static constexpr RegMask REG_0x06_PWRBIT = 0x10; +static constexpr RegMask REG_0x06_GAIN4 = 0x08; +static constexpr RegMask REG_0x06_OPTEST = 0x07; + +static constexpr RegMask REG_0x08_DECFLAG = 0x40; +static constexpr RegMask REG_0x08_GMMFFR = 0x20; +static constexpr RegMask REG_0x08_GMMFFG = 0x10; +static constexpr RegMask REG_0x08_GMMFFB = 0x08; +static constexpr RegMask REG_0x08_GMMZR = 0x04; +static constexpr RegMask REG_0x08_GMMZG = 0x02; +static constexpr RegMask REG_0x08_GMMZB = 0x01; + +static constexpr RegMask REG_0x09_MCNTSET = 0xc0; +static constexpr RegMask REG_0x09_CLKSET = 0x30; +static constexpr RegMask REG_0x09_BACKSCAN = 0x08; +static constexpr RegMask REG_0x09_ENHANCE = 0x04; +static constexpr RegMask REG_0x09_SHORTTG = 0x02; +static constexpr RegMask REG_0x09_NWAIT = 0x01; + +static constexpr RegAddr REG_0x0D = 0x0d; +static constexpr RegMask REG_0x0D_CLRLNCNT = 0x01; + +static constexpr RegAddr REG_0x0F = 0x0f; + +static constexpr RegAddr REG_EXPR = 0x10; +static constexpr RegAddr REG_EXPG = 0x12; +static constexpr RegAddr REG_EXPB = 0x14; + +static constexpr RegMask REG_0x16_CTRLHI = 0x80; +static constexpr RegMask REG_0x16_TOSHIBA = 0x40; +static constexpr RegMask REG_0x16_TGINV = 0x20; +static constexpr RegMask REG_0x16_CK1INV = 0x10; +static constexpr RegMask REG_0x16_CK2INV = 0x08; +static constexpr RegMask REG_0x16_CTRLINV = 0x04; +static constexpr RegMask REG_0x16_CKDIS = 0x02; +static constexpr RegMask REG_0x16_CTRLDIS = 0x01; + +static constexpr RegMask REG_0x17_TGMODE = 0xc0; +static constexpr RegMask REG_0x17_TGMODE_NO_DUMMY = 0x00; +static constexpr RegMask REG_0x17_TGMODE_REF = 0x40; +static constexpr RegMask REG_0x17_TGMODE_XPA = 0x80; +static constexpr RegMask REG_0x17_TGW = 0x3f; + +static constexpr RegAddr REG_0x18 = 0x18; +static constexpr RegMask REG_0x18_CNSET = 0x80; +static constexpr RegMask REG_0x18_DCKSEL = 0x60; +static constexpr RegMask REG_0x18_CKTOGGLE = 0x10; +static constexpr RegMask REG_0x18_CKDELAY = 0x0c; +static constexpr RegMask REG_0x18_CKSEL = 0x03; + +static constexpr RegAddr REG_EXPDMY = 0x19; + +static constexpr RegAddr REG_0x1A = 0x1a; +static constexpr RegMask REG_0x1A_MANUAL3 = 0x02; +static constexpr RegMask REG_0x1A_MANUAL1 = 0x01; +static constexpr RegMask REG_0x1A_CK4INV = 0x08; +static constexpr RegMask REG_0x1A_CK3INV = 0x04; +static constexpr RegMask REG_0x1A_LINECLP = 0x02; + +static constexpr RegAddr REG_0x1C = 0x1c; +static constexpr RegMask REG_0x1C_TGTIME = 0x07; + +static constexpr RegMask REG_0x1D_CK4LOW = 0x80; +static constexpr RegMask REG_0x1D_CK3LOW = 0x40; +static constexpr RegMask REG_0x1D_CK1LOW = 0x20; +static constexpr RegMask REG_0x1D_TGSHLD = 0x1f; + +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; +static constexpr RegShift REG_0x1ES_LINESEL = 0; + +static constexpr RegAddr REG_0x21 = 0x21; +static constexpr RegAddr REG_STEPNO = 0x21; +static constexpr RegAddr REG_FWDSTEP = 0x22; +static constexpr RegAddr REG_BWDSTEP = 0x23; +static constexpr RegAddr REG_FASTNO = 0x24; +static constexpr RegAddr REG_LINCNT = 0x25; + +static constexpr RegAddr REG_0x29 = 0x29; +static constexpr RegAddr REG_0x2A = 0x2a; +static constexpr RegAddr REG_0x2B = 0x2b; +static constexpr RegAddr REG_DPISET = 0x2c; +static constexpr RegAddr REG_0x2E = 0x2e; +static constexpr RegAddr REG_0x2F = 0x2f; + +static constexpr RegAddr REG_STRPIXEL = 0x30; +static constexpr RegAddr REG_ENDPIXEL = 0x32; +static constexpr RegAddr REG_DUMMY = 0x34; +static constexpr RegAddr REG_MAXWD = 0x35; +static constexpr RegAddr REG_LPERIOD = 0x38; +static constexpr RegAddr REG_FEEDL = 0x3d; + +static constexpr RegAddr REG_0x40 = 0x40; +static constexpr RegMask REG_0x40_HISPDFLG = 0x04; +static constexpr RegMask REG_0x40_MOTMFLG = 0x02; +static constexpr RegMask REG_0x40_DATAENB = 0x01; + +static constexpr RegMask REG_0x41_PWRBIT = 0x80; +static constexpr RegMask REG_0x41_BUFEMPTY = 0x40; +static constexpr RegMask REG_0x41_FEEDFSH = 0x20; +static constexpr RegMask REG_0x41_SCANFSH = 0x10; +static constexpr RegMask REG_0x41_HOMESNR = 0x08; +static constexpr RegMask REG_0x41_LAMPSTS = 0x04; +static constexpr RegMask REG_0x41_FEBUSY = 0x02; +static constexpr RegMask REG_0x41_MOTORENB = 0x01; + +static constexpr RegMask REG_0x5A_ADCLKINV = 0x80; +static constexpr RegMask REG_0x5A_RLCSEL = 0x40; +static constexpr RegMask REG_0x5A_CDSREF = 0x30; +static constexpr RegShift REG_0x5AS_CDSREF = 4; +static constexpr RegMask REG_0x5A_RLC = 0x0f; +static constexpr RegShift REG_0x5AS_RLC = 0; + +static constexpr RegAddr REG_0x5E = 0x5e; +static constexpr RegMask REG_0x5E_DECSEL = 0xe0; +static constexpr RegShift REG_0x5ES_DECSEL = 5; +static constexpr RegMask REG_0x5E_STOPTIM = 0x1f; +static constexpr RegShift REG_0x5ES_STOPTIM = 0; + +static constexpr RegAddr REG_FMOVDEC = 0x5f; + +static constexpr RegAddr REG_0x60 = 0x60; +static constexpr RegMask REG_0x60_Z1MOD = 0x1f; +static constexpr RegAddr REG_0x61 = 0x61; +static constexpr RegMask REG_0x61_Z1MOD = 0xff; +static constexpr RegAddr REG_0x62 = 0x62; +static constexpr RegMask REG_0x62_Z1MOD = 0xff; + +static constexpr RegAddr REG_0x63 = 0x63; +static constexpr RegMask REG_0x63_Z2MOD = 0x1f; +static constexpr RegAddr REG_0x64 = 0x64; +static constexpr RegMask REG_0x64_Z2MOD = 0xff; +static constexpr RegAddr REG_0x65 = 0x65; +static constexpr RegMask REG_0x65_Z2MOD = 0xff; + +static constexpr RegAddr REG_0x67 = 0x67; +static constexpr RegAddr REG_0x68 = 0x68; + +static constexpr RegShift REG_0x67S_STEPSEL = 6; +static constexpr RegMask REG_0x67_STEPSEL = 0xc0; + +static constexpr RegShift REG_0x68S_FSTPSEL = 6; +static constexpr RegMask REG_0x68_FSTPSEL = 0xc0; + +static constexpr RegAddr REG_FSHDEC = 0x69; +static constexpr RegAddr REG_FMOVNO = 0x6a; + +static constexpr RegAddr REG_0x6B = 0x6b; +static constexpr RegMask REG_0x6B_MULTFILM = 0x80; + +static constexpr RegAddr REG_Z1MOD = 0x60; +static constexpr RegAddr REG_Z2MOD = 0x63; + +static constexpr RegAddr REG_0x6C = 0x6c; +static constexpr RegAddr REG_0x6D = 0x6d; +static constexpr RegAddr REG_0x6E = 0x6e; +static constexpr RegAddr REG_0x6F = 0x6f; + +static constexpr RegAddr REG_CK1MAP = 0x74; +static constexpr RegAddr REG_CK3MAP = 0x77; +static constexpr RegAddr REG_CK4MAP = 0x7a; + +static constexpr RegAddr REG_0x7E = 0x7e; + +static constexpr RegAddr REG_0x80 = 0x80; +static constexpr RegMask REG_0x80_TABLE1_NORMAL = 0x03; +static constexpr RegShift REG_0x80S_TABLE1_NORMAL = 0; +static constexpr RegMask REG_0x80_TABLE2_BACK = 0x0c; +static constexpr RegShift REG_0x80S_TABLE2_BACK = 2; +static constexpr RegMask REG_0x80_TABLE4_FAST = 0x30; +static constexpr RegShift REG_0x80S_TABLE4_FAST = 4; +static constexpr RegMask REG_0x80_TABLE5_GO_HOME = 0xc0; +static constexpr RegShift REG_0x80S_TABLE5_GO_HOME = 6; + +static constexpr RegMask REG_0x87_LEDADD = 0x04; + +} // namespace gl842 +} // namespace genesys + +#endif // BACKEND_GENESYS_gl842_REGISTERS_H diff --git a/backend/genesys/gl843.cpp b/backend/genesys/gl843.cpp index f83ac8d..8233bde 100644 --- a/backend/genesys/gl843.cpp +++ b/backend/genesys/gl843.cpp @@ -54,60 +54,18 @@  namespace genesys {  namespace gl843 { -// Set address for writing data -static void gl843_set_buffer_address(Genesys_Device* dev, uint32_t addr) -{ -    DBG_HELPER_ARGS(dbg, "setting address to 0x%05x", addr & 0xffff); - -    dev->interface->write_register(0x5b, ((addr >> 8) & 0xff)); -    dev->interface->write_register(0x5c, (addr & 0xff)); -} -  /**   * compute the step multiplier used   */  static int gl843_get_step_multiplier(Genesys_Register_Set* regs)  { -    GenesysRegister *r = sanei_genesys_get_address(regs, REG_0x9D); -    int value = 1; -  if (r != nullptr) -    { -      switch (r->value & 0x0c) -	{ -	case 0x04: -	  value = 2; -	  break; -	case 0x08: -	  value = 4; -	  break; -	default: -	  value = 1; -	} -    } -  DBG(DBG_io, "%s: step multiplier is %d\n", __func__, value); -  return value; -} - -/** copy sensor specific settings */ -static void gl843_setup_sensor(Genesys_Device* dev, const Genesys_Sensor& sensor, -                               Genesys_Register_Set* regs) -{ -    DBG_HELPER(dbg); -    for (const auto& custom_reg : sensor.custom_regs) { -        regs->set8(custom_reg.address, custom_reg.value); +    switch (regs->get8(REG_0x9D) & 0x0c) { +        case 0x04: return 2; +        case 0x08: return 4; +        default: return 1;      } -    if (!(dev->model->flags & GENESYS_FLAG_FULL_HWDPI_MODE) && -        dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I && -        dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300 && -        dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7500I) -    { -        regs->set8(0x7d, 0x90); -    } - -    dev->segment_order = sensor.segment_order;  } -  /** @brief set all registers to default values .   * This function is called only once at the beginning and   * fills register startup values for registers reused across scans. @@ -118,9 +76,9 @@ static void gl843_setup_sensor(Genesys_Device* dev, const Genesys_Sensor& sensor  static void  gl843_init_registers (Genesys_Device * dev)  { -  // Within this function SENSOR_DEF marker documents that a register is part -  // of the sensors definition and the actual value is set in -  // gl843_setup_sensor(). +    // Within this function SENSOR_DEF marker documents that a register is part +    // of the sensors definition and the actual value is set in +    // scanner_setup_sensor().      // 0x6c, 0x6d, 0x6e, 0x6f, 0xa6, 0xa7, 0xa8, 0xa9 are defined in the Gpo sensor struct @@ -158,8 +116,16 @@ gl843_init_registers (Genesys_Device * dev)        dev->reg.init_reg(0x05, 0x08);      } +    auto initial_scan_method = dev->model->default_method; +    if (dev->model->model_id == ModelId::CANON_4400F || +        dev->model->model_id == ModelId::CANON_8600F) +    { +        initial_scan_method = ScanMethod::TRANSPARENCY; +    }      const auto& sensor = sanei_genesys_find_sensor_any(dev); -    sanei_genesys_set_dpihw(dev->reg, sensor, sensor.optical_res); +    const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, +                                                         3, initial_scan_method); +    sanei_genesys_set_dpihw(dev->reg, dpihw_sensor.register_dpihw);      // TODO: on 8600F the windows driver turns off GAIN4 which is recommended      dev->reg.init_reg(0x06, 0xd8); /* SCANMOD=110, PWRBIT and GAIN4 */ @@ -402,11 +368,11 @@ gl843_init_registers (Genesys_Device * dev)      // STEPSEL[0:1]. Motor movement step mode selection for tables 1-3 in      // scanning mode.      // MTRPWM[0:5]. Motor phase PWM duty cycle setting for tables 1-3 -    dev->reg.init_reg(0x67, 0x7f); +    dev->reg.init_reg(0x67, 0x7f); // MOTOR_PROFILE      // FSTPSEL[0:1]: Motor movement step mode selection for tables 4-5 in      // command mode.      // FASTPWM[5:0]: Motor phase PWM duty cycle setting for tables 4-5 -    dev->reg.init_reg(0x68, 0x7f); +    dev->reg.init_reg(0x68, 0x7f); // MOTOR_PROFILE      if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300) {          dev->reg.init_reg(0x67, 0x80); @@ -415,17 +381,11 @@ gl843_init_registers (Genesys_Device * dev)      // FSHDEC[0:7]: The number of deceleration steps after scanning is finished      // (table 3) -    dev->reg.init_reg(0x69, 0x01); -    if (dev->model->model_id == ModelId::CANON_8600F) { -        dev->reg.init_reg(0x69, 64); -    } +    dev->reg.init_reg(0x69, 0x01); // MOTOR_PROFILE      // FMOVNO[0:7] The number of acceleration or deceleration steps for fast      // moving (table 4) -    dev->reg.init_reg(0x6a, 0x04); -    if (dev->model->model_id == ModelId::CANON_8600F) { -        dev->reg.init_reg(0x69, 64); -    } +    dev->reg.init_reg(0x6a, 0x04); // MOTOR_PROFILE      // GPIO-related register bits      dev->reg.init_reg(0x6b, 0x30); @@ -516,7 +476,7 @@ gl843_init_registers (Genesys_Device * dev)      // VRHOME, VRMOVE, VRBACK, VRSCAN: Vref settings of the motor driver IC for      // moving in various situations. -    dev->reg.init_reg(0x80, 0x00); +    dev->reg.init_reg(0x80, 0x00); // MOTOR_PROFILE      if (dev->model->model_id == ModelId::CANON_4400F) {          dev->reg.init_reg(0x80, 0x0c);      } @@ -632,7 +592,7 @@ gl843_init_registers (Genesys_Device * dev)          dev->reg.init_reg(0xaa, 0x00);      } -    // GPOM9, MULSTOP[0-2], NODECEL, TB3TB1, TB5TB2, FIX16CLK. Not documented +    // GPOM9, MULSTOP[0-2], NODECEL, TB3TB1, TB5TB2, FIX16CLK.      if (dev->model->model_id != ModelId::CANON_8400F &&          dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7200I &&          dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7300) { @@ -643,10 +603,9 @@ gl843_init_registers (Genesys_Device * dev)      }      if (dev->model->model_id == ModelId::HP_SCANJET_G4010 ||          dev->model->model_id == ModelId::HP_SCANJET_G4050 || +        dev->model->model_id == ModelId::CANON_8600F ||          dev->model->model_id == ModelId::HP_SCANJET_4850C)      { -        // BUG: this should apply to ModelId::CANON_CANOSCAN_8600F too, but due to previous bug -        // the 8400F case overwrote it          dev->reg.init_reg(0xab, 0x40);      } @@ -660,8 +619,6 @@ gl843_init_registers (Genesys_Device * dev)          dev->reg.init_reg(0xac, 0x00);      } -    dev->calib_reg = dev->reg; -      if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I) {          uint8_t data[32] = {              0x8c, 0x8f, 0xc9, 0x00, 0x01, 0x00, 0x00, 0x00, @@ -670,48 +627,8 @@ gl843_init_registers (Genesys_Device * dev)              0x6a, 0x73, 0x63, 0x68, 0x69, 0x65, 0x6e, 0x00,          }; -        dev->interface->write_buffer(0x3c, 0x3ff000, data, 32, -                                     ScannerInterface::FLAG_SWAP_REGISTERS); -    } -} - -// Send slope table for motor movement slope_table in machine byte order -static void gl843_send_slope_table(Genesys_Device* dev, int table_nr, -                                   const std::vector<uint16_t>& slope_table, -                                   int steps) -{ -    DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %d", table_nr, steps); - -  int i; -  char msg[10000]; - -  std::vector<uint8_t> table(steps * 2); -  for (i = 0; i < steps; i++) -    { -      table[i * 2] = slope_table[i] & 0xff; -      table[i * 2 + 1] = slope_table[i] >> 8; -    } - -  if (DBG_LEVEL >= DBG_io) -    { -        std::sprintf(msg, "write slope %d (%d)=", table_nr, steps); -        for (i = 0; i < steps; i++) { -            std::sprintf (msg+strlen(msg), "%d", slope_table[i]); -	} -      DBG(DBG_io, "%s: %s\n", __func__, msg); -    } - -    if (dev->interface->is_mock()) { -        dev->interface->record_slope_table(table_nr, slope_table); +        dev->interface->write_buffer(0x3c, 0x3ff000, data, 32);      } - -    // slope table addresses are fixed : 0x40000,  0x48000,  0x50000,  0x58000,  0x60000 -    // XXX STEF XXX USB 1.1 ? sanei_genesys_write_0x8c (dev, 0x0f, 0x14); -    dev->interface->write_gamma(0x28,  0x40000 + 0x8000 * table_nr, table.data(), steps * 2, -                                ScannerInterface::FLAG_SWAP_REGISTERS); - -    // FIXME: remove this when updating tests -    gl843_set_buffer_address(dev, 0);  }  static void gl843_set_ad_fe(Genesys_Device* dev) @@ -728,14 +645,9 @@ void CommandSetGl843::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor,                                 set == AFE_SET ? "set" :                                 set == AFE_POWER_SAVE ? "powersave" : "huh?");      (void) sensor; -  int i; -  if (set == AFE_INIT) -    { -        DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, -            static_cast<unsigned>(dev->model->adc_id)); -      dev->frontend = dev->frontend_initial; -        dev->frontend_is_init = true; +    if (set == AFE_INIT) { +        dev->frontend = dev->frontend_initial;      }      // check analog frontend type @@ -749,153 +661,135 @@ void CommandSetGl843::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor,          throw SaneException(SANE_STATUS_UNSUPPORTED, "unsupported frontend type %d", fe_type);      } -  DBG(DBG_proc, "%s(): frontend reset complete\n", __func__); - -  for (i = 1; i <= 3; i++) -    { -        // FIXME: the check below is just historical artifact, we can remove it when convenient -        if (!dev->frontend_is_init) { -            dev->interface->write_fe_register(i, 0x00); -        } else { -            dev->interface->write_fe_register(i, dev->frontend.regs.get_value(0x00 + i)); -        } +    for (unsigned i = 1; i <= 3; i++) { +        dev->interface->write_fe_register(i, dev->frontend.regs.get_value(0x00 + i));      }      for (const auto& reg : sensor.custom_fe_regs) {          dev->interface->write_fe_register(reg.address, reg.value);      } -  for (i = 0; i < 3; i++) -    { -         // FIXME: the check below is just historical artifact, we can remove it when convenient -        if (!dev->frontend_is_init) { -            dev->interface->write_fe_register(0x20 + i, 0x00); -        } else { -            dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i)); -        } +    for (unsigned i = 0; i < 3; i++) { +        dev->interface->write_fe_register(0x20 + i, dev->frontend.get_offset(i));      }      if (dev->model->sensor_id == SensorId::CCD_KVSS080) { -      for (i = 0; i < 3; i++) -	{ -            // FIXME: the check below is just historical artifact, we can remove it when convenient -            if (!dev->frontend_is_init) { -                dev->interface->write_fe_register(0x24 + i, 0x00); -            } else { -                dev->interface->write_fe_register(0x24 + i, dev->frontend.regs.get_value(0x24 + i)); -            } -	} +        for (unsigned i = 0; i < 3; i++) { +            dev->interface->write_fe_register(0x24 + i, dev->frontend.regs.get_value(0x24 + i)); +        }      } -  for (i = 0; i < 3; i++) -    { -        // FIXME: the check below is just historical artifact, we can remove it when convenient -        if (!dev->frontend_is_init) { -            dev->interface->write_fe_register(0x28 + i, 0x00); -        } else { -            dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i)); -        } +    for (unsigned i = 0; i < 3; i++) { +        dev->interface->write_fe_register(0x28 + i, dev->frontend.get_gain(i));      }  } -  static void gl843_init_motor_regs_scan(Genesys_Device* dev,                                         const Genesys_Sensor& sensor, +                                       const ScanSession& session,                                         Genesys_Register_Set* reg, -                                       const Motor_Profile& motor_profile, +                                       const MotorProfile& motor_profile,                                         unsigned int exposure,                                         unsigned scan_yres,                                         unsigned int scan_lines,                                         unsigned int scan_dummy,                                         unsigned int feed_steps, -                                       MotorFlag flags) +                                       ScanFlag flags)  {      DBG_HELPER_ARGS(dbg, "exposure=%d, scan_yres=%d, step_type=%d, scan_lines=%d, scan_dummy=%d, "                           "feed_steps=%d, flags=%x",                      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; +    if ((scan_yres >= 300 && feed_steps > 900) || (has_flag(flags, ScanFlag::FEEDING))) { +        use_fast_fed = true; +    } +    if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) { +        use_fast_fed = false;      } -  lincnt=scan_lines; -    reg->set24(REG_LINCNT, lincnt); -  DBG(DBG_io, "%s: lincnt=%d\n", __func__, lincnt); +    reg->set24(REG_LINCNT, 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 */ -    if (has_flag(flags, MotorFlag::AUTO_GO_HOME)) { -        r->value |= REG_0x02_AGOHOME | REG_0x02_NOTHOME; +    // in case of automatic go home, move until home sensor +    if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { +        reg02 |= REG_0x02_AGOHOME | REG_0x02_NOTHOME;      }    /* disable backtracking */ -    if (has_flag(flags, MotorFlag::DISABLE_BUFFER_FULL_MOVE) -      ||(scan_yres>=2400 && dev->model->model_id != ModelId::CANON_4400F) -      ||(scan_yres>=sensor.optical_res)) +    if (has_flag(flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || +        (scan_yres>=2400 && dev->model->model_id != ModelId::CANON_4400F) || +        (scan_yres>=sensor.full_resolution))      { -        r->value |= REG_0x02_ACDCDIS; +        reg02 |= REG_0x02_ACDCDIS;      } -    if (has_flag(flags, MotorFlag::REVERSE)) { -        r->value |= REG_0x02_MTRREV; +    if (has_flag(flags, ScanFlag::REVERSE)) { +        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, -                                                dev->motor.base_ydpi, step_multiplier, -                                                motor_profile); +    // scan and backtracking slope table +    auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, exposure, +                                         step_multiplier, motor_profile); -    gl843_send_slope_table(dev, SCAN_TABLE, scan_table.table, scan_table.steps_count); -    gl843_send_slope_table(dev, BACKTRACK_TABLE, scan_table.table, scan_table.steps_count); +    scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); +    scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); +    scanner_send_slope_table(dev, sensor, STOP_TABLE, scan_table.table); -    reg->set8(REG_STEPNO, scan_table.steps_count / step_multiplier); -    reg->set8(REG_FASTNO, scan_table.steps_count / step_multiplier); +    reg->set8(REG_STEPNO, scan_table.table.size() / step_multiplier); +    reg->set8(REG_FASTNO, scan_table.table.size() / step_multiplier); +    reg->set8(REG_FSHDEC, scan_table.table.size() / step_multiplier);      // fast table -    unsigned fast_yres = sanei_genesys_get_lowest_ydpi(dev); -    auto fast_table = sanei_genesys_slope_table(dev->model->asic_type, fast_yres, exposure, -                                                dev->motor.base_ydpi, step_multiplier, -                                                motor_profile); -    gl843_send_slope_table(dev, STOP_TABLE, fast_table.table, fast_table.steps_count); -    gl843_send_slope_table(dev, FAST_TABLE, fast_table.table, fast_table.steps_count); -    gl843_send_slope_table(dev, HOME_TABLE, fast_table.table, fast_table.steps_count); +    const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); +    if (fast_profile == nullptr) { +        fast_profile = &motor_profile; +    } -    reg->set8(REG_FSHDEC, fast_table.steps_count / step_multiplier); -    reg->set8(REG_FMOVNO, fast_table.steps_count / step_multiplier); +    auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, +                                                 *fast_profile); + +    scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); +    scanner_send_slope_table(dev, sensor, HOME_TABLE, fast_table.table); + +    reg->set8(REG_FMOVNO, fast_table.table.size() / step_multiplier); + +    if (motor_profile.motor_vref != -1 && fast_profile->motor_vref != 1) { +        std::uint8_t vref = 0; +        vref |= (motor_profile.motor_vref << REG_0x80S_TABLE1_NORMAL) & REG_0x80_TABLE1_NORMAL; +        vref |= (motor_profile.motor_vref << REG_0x80S_TABLE2_BACK) & REG_0x80_TABLE2_BACK; +        vref |= (fast_profile->motor_vref << REG_0x80S_TABLE4_FAST) & REG_0x80_TABLE4_FAST; +        vref |= (fast_profile->motor_vref << REG_0x80S_TABLE5_GO_HOME) & REG_0x80_TABLE5_GO_HOME; +        reg->set8(REG_0x80, vref); +    }    /* substract acceleration distance from feedl */    feedl=feed_steps;      feedl <<= static_cast<unsigned>(motor_profile.step_type); -    dist = scan_table.steps_count / step_multiplier; -  if (use_fast_fed) -    { -        dist += (fast_table.steps_count / step_multiplier) * 2; +    dist = scan_table.table.size() / step_multiplier; + +    if (use_fast_fed) { +        dist += (fast_table.table.size() / step_multiplier) * 2;      } -  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 */ @@ -906,15 +800,15 @@ 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, -                                 scan_table.steps_count / step_multiplier, +                                 scan_table.table.size() / step_multiplier, +                                 feedl, +                                 scan_table.table.size() / step_multiplier,                                    &z1,                                    &z2);    if(scan_yres>600) @@ -924,47 +818,46 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev,      }      reg->set24(REG_Z1MOD, z1); -  DBG(DBG_info, "%s: z1 = %d\n", __func__, z1); -      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>(motor_profile.step_type) << REG_0x68S_FSTPSEL, 0xc0); +    reg->set8_mask(REG_0x68, static_cast<unsigned>(fast_profile->step_type) << REG_0x68S_FSTPSEL, 0xc0);      // steps for STOP table -    reg->set8(REG_FMOVDEC, fast_table.steps_count / step_multiplier); +    reg->set8(REG_FMOVDEC, fast_table.table.size() / step_multiplier); -  /* Vref XXX STEF XXX : optical divider or step type ? */ -  r = sanei_genesys_get_address (reg, 0x80); -  if (!(dev->model->flags & GENESYS_FLAG_FULL_HWDPI_MODE)) +    if (dev->model->model_id == ModelId::PANASONIC_KV_SS080 || +        dev->model->model_id == ModelId::HP_SCANJET_4850C || +        dev->model->model_id == ModelId::HP_SCANJET_G4010 || +        dev->model->model_id == ModelId::HP_SCANJET_G4050 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I)      { -      r->value = 0x50; -        coeff = sensor.get_hwdpi_divisor_for_dpi(scan_yres); +        // FIXME: take this information from motor struct +        std::uint8_t reg_vref = reg->get8(0x80); +        reg_vref = 0x50; +        unsigned coeff = sensor.full_resolution / 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);      }  } @@ -981,7 +874,6 @@ static void gl843_init_motor_regs_scan(Genesys_Device* dev,   * @param pixels logical number of pixels to use   * @param channels number of color channles used (1 or 3)   * @param depth bit depth of the scan (1, 8 or 16 bits) - * @param ccd_size_divisor true specifies how much x coordinates must be shrunk   * @param color_filter to choose the color channel used in gray scans   * @param flags to drive specific settings such no calibration, XPA use ...   */ @@ -990,57 +882,54 @@ static void gl843_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens                                           const ScanSession& session)  {      DBG_HELPER_ARGS(dbg, "exposure=%d", exposure); -    unsigned int dpihw;    unsigned int tgtime;          /**> exposure time multiplier */ -  GenesysRegister *r;    /* tgtime */    tgtime = exposure / 65536 + 1;    DBG(DBG_io2, "%s: tgtime=%d\n", __func__, tgtime); -    // to manage high resolution device while keeping good low resolution scanning speed, we make -    // hardware dpi vary -    dpihw = sensor.get_register_hwdpi(session.output_resolution); -    DBG(DBG_io2, "%s: dpihw=%d\n", __func__, dpihw); - -  /* sensor parameters */ -    gl843_setup_sensor(dev, sensor, reg); - -    // resolution is divided according to CKSEL -    unsigned ccd_pixels_per_system_pixel = sensor.ccd_pixels_per_system_pixel(); -    DBG(DBG_io2, "%s: ccd_pixels_per_system_pixel=%d\n", __func__, ccd_pixels_per_system_pixel); +    // sensor parameters +    scanner_setup_sensor(*dev, sensor, *reg);      dev->cmd_set->set_fe(dev, sensor, AFE_SET);    /* enable shading */      regs_set_optical_off(dev->model->asic_type, *reg); -    r = sanei_genesys_get_address (reg, REG_0x01);      if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || -        (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION || -        (dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE))) +        has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || +        session.use_host_side_calib)      { -        r->value &= ~REG_0x01_DVDSET; +        reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET; +      } else { -        r->value |= REG_0x01_DVDSET; +        reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET;      } -    bool use_shdarea = dpihw > 600; +    bool use_shdarea = false;      if (dev->model->model_id == ModelId::CANON_4400F) {          use_shdarea = session.params.xres <= 600;      } else if (dev->model->model_id == ModelId::CANON_8400F) {          use_shdarea = session.params.xres <= 400; +    } else if (dev->model->model_id == ModelId::CANON_8600F || +               dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || +               dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || +               dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) +    { +        use_shdarea = true; +    } else { +        use_shdarea = session.params.xres > 600;      } +      if (use_shdarea) { -        r->value |= REG_0x01_SHDAREA; +        reg->find_reg(REG_0x01).value |= REG_0x01_SHDAREA;      } else { -        r->value &= ~REG_0x01_SHDAREA; +        reg->find_reg(REG_0x01).value &= ~REG_0x01_SHDAREA;      } -    r = sanei_genesys_get_address (reg, REG_0x03);      if (dev->model->model_id == ModelId::CANON_8600F) { -        r->value |= REG_0x03_AVEENB; +        reg->find_reg(REG_0x03).value |= REG_0x03_AVEENB;      } else { -        r->value &= ~REG_0x03_AVEENB; +        reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB;    }      // FIXME: we probably don't need to set exposure to registers at this point. It was this way @@ -1049,43 +938,40 @@ static void gl843_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens                                   !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP));    /* select XPA */ -    r->value &= ~REG_0x03_XPASEL; +    reg->find_reg(REG_0x03).value &= ~REG_0x03_XPASEL;      if (has_flag(session.params.flags, ScanFlag::USE_XPA)) { -        r->value |= REG_0x03_XPASEL; +        reg->find_reg(REG_0x03).value |= REG_0x03_XPASEL;      }      reg->state.is_xpa_on = has_flag(session.params.flags, ScanFlag::USE_XPA); -  /* BW threshold */ -    r = sanei_genesys_get_address(reg, REG_0x2E); -  r->value = dev->settings.threshold; -    r = sanei_genesys_get_address(reg, REG_0x2F); -  r->value = dev->settings.threshold; +    // BW threshold +    reg->set8(REG_0x2E, 0x7f); +    reg->set8(REG_0x2F, 0x7f);    /* monochrome / color scan */ -    r = sanei_genesys_get_address(reg, REG_0x04);      switch (session.params.depth) {      case 8: -            r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); +            reg->find_reg(REG_0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET);        break;      case 16: -            r->value &= ~REG_0x04_LINEART; -            r->value |= REG_0x04_BITSET; +            reg->find_reg(REG_0x04).value &= ~REG_0x04_LINEART; +            reg->find_reg(REG_0x04).value |= REG_0x04_BITSET;        break;      } -    r->value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); +    reg->find_reg(REG_0x04).value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD);    if (session.params.channels == 1)      {        switch (session.params.color_filter)  	{              case ColorFilter::RED: -                r->value |= 0x14; +                reg->find_reg(REG_0x04).value |= 0x14;                  break;              case ColorFilter::BLUE: -                r->value |= 0x1c; +                reg->find_reg(REG_0x04).value |= 0x1c;                  break;              case ColorFilter::GREEN: -                r->value |= 0x18; +                reg->find_reg(REG_0x04).value |= 0x18;                  break;              default:                  break; // should not happen @@ -1093,10 +979,10 @@ static void gl843_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens      } else {          switch (dev->frontend.layout.type) {              case FrontendType::WOLFSON: -                r->value |= 0x10; // pixel by pixel +                reg->find_reg(REG_0x04).value |= 0x10; // pixel by pixel                  break;              case FrontendType::ANALOG_DEVICES: -                r->value |= 0x20; // slow color pixel by pixel +                reg->find_reg(REG_0x04).value |= 0x20; // slow color pixel by pixel                  break;              default:                  throw SaneException("Invalid frontend type %d", @@ -1104,7 +990,10 @@ static void gl843_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens          }      } -    sanei_genesys_set_dpihw(*reg, sensor, dpihw); +    const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, +                                                         session.params.channels, +                                                         session.params.scan_method); +    sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw);      if (should_enable_gamma(session, sensor)) {          reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; @@ -1112,28 +1001,18 @@ static void gl843_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens          reg->find_reg(REG_0x05).value &= ~REG_0x05_GMMENB;      } -    unsigned dpiset = session.output_resolution * session.ccd_size_divisor * -            ccd_pixels_per_system_pixel; - -    if (sensor.dpiset_override != 0) { -        dpiset = sensor.dpiset_override; -    } -    reg->set16(REG_DPISET, dpiset); -    DBG(DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset); +    reg->set16(REG_DPISET, sensor.register_dpiset);      reg->set16(REG_STRPIXEL, session.pixel_startx);      reg->set16(REG_ENDPIXEL, session.pixel_endx);    /* MAXWD is expressed in 2 words unit */    /* nousedspace = (mem_bank_range * 1024 / 256 -1 ) * 4; */ -    // BUG: the division by ccd_size_divisor likely does not make sense -    reg->set24(REG_MAXWD, (session.output_line_bytes / session.ccd_size_divisor) >> 1); - +    // BUG: the division by optical and full resolution factor likely does not make sense +    reg->set24(REG_MAXWD, (session.output_line_bytes * +                           session.optical_resolution / session.full_resolution) >> 1);      reg->set16(REG_LPERIOD, exposure / tgtime); -  DBG(DBG_io2, "%s: exposure used=%d\n", __func__, exposure/tgtime); - -  r = sanei_genesys_get_address (reg, REG_DUMMY); -  r->value = sensor.dummy_pixel; +    reg->set8(REG_DUMMY, sensor.dummy_pixel);  }  void CommandSetGl843::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, @@ -1170,42 +1049,15 @@ void CommandSetGl843::init_regs_for_scan_session(Genesys_Device* dev, const Gene    if (exposure < 0) {        throw std::runtime_error("Exposure not defined in sensor definition");    } -    const auto& motor_profile = sanei_genesys_get_motor_profile(*gl843_motor_profiles, -                                                                dev->model->motor_id, -                                                                exposure); - -  DBG(DBG_info, "%s : exposure=%d pixels\n", __func__, exposure); -    DBG(DBG_info, "%s : scan_step_type=%d\n", __func__, -        static_cast<unsigned>(motor_profile.step_type)); +    const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure, session);      // now _LOGICAL_ optical values used are known, setup registers      gl843_init_optical_regs_scan(dev, sensor, reg, exposure, session); +    gl843_init_motor_regs_scan(dev, sensor, session, reg, motor_profile, exposure, slope_dpi, +                               session.optical_line_count, dummy, session.params.starty, +                               session.params.flags); -  /*** motor parameters ***/ -    MotorFlag mflags = MotorFlag::NONE; -    if (has_flag(session.params.flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE)) { -        mflags |= MotorFlag::DISABLE_BUFFER_FULL_MOVE; -    } -    if (has_flag(session.params.flags, ScanFlag::FEEDING)) { -        mflags |= MotorFlag::FEED; -    } -    if (has_flag(session.params.flags, ScanFlag::USE_XPA)) { -        mflags |= MotorFlag::USE_XPA; -    } -    if (has_flag(session.params.flags, ScanFlag::REVERSE)) { -        mflags |= MotorFlag::REVERSE; -    } - -    unsigned scan_lines = dev->model->is_cis ? session.output_line_count * session.params.channels -                                             : session.output_line_count; - -    gl843_init_motor_regs_scan(dev, sensor, reg, motor_profile, exposure, slope_dpi, -                               scan_lines, dummy, session.params.starty, mflags); - -    dev->read_buffer.clear(); -    dev->read_buffer.alloc(session.buffer_size_read); - -    build_image_pipeline(dev, session); +    setup_image_pipeline(*dev, session);      dev->read_active = true; @@ -1224,33 +1076,46 @@ ScanSession CommandSetGl843::calculate_scan_session(const Genesys_Device* dev,      DBG_HELPER(dbg);      debug_dump(DBG_info, settings); -  int start; - -  /* we have 2 domains for ccd: xres below or above half ccd max dpi */ -  unsigned ccd_size_divisor = sensor.get_ccd_size_divisor_for_dpi(settings.xres); +    ScanFlag flags = ScanFlag::NONE; +    float move = 0.0f;      if (settings.scan_method == ScanMethod::TRANSPARENCY ||          settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)      { -        start = static_cast<int>(dev->model->x_offset_ta); +        // note: scanner_move_to_ta() function has already been called and the sensor is at the +        // transparency adapter +        if (!dev->ignore_offsets) { +            move = dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta; +        } +        flags |= ScanFlag::USE_XPA;      } else { -        start = static_cast<int>(dev->model->x_offset); +        if (!dev->ignore_offsets) { +            move = dev->model->y_offset; +        }      } -    if (dev->model->model_id == ModelId::CANON_8600F) +    move += settings.tl_y; + +    int move_dpi = dev->motor.base_ydpi; +    move = static_cast<float>((move * move_dpi) / MM_PER_INCH); + +    float start = 0.0f; +    if (settings.scan_method==ScanMethod::TRANSPARENCY || +        settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)      { -        // FIXME: this is probably just an artifact of a bug elsewhere -        start /= ccd_size_divisor; +        start = dev->model->x_offset_ta; +    } else { +        start = dev->model->x_offset;      } +    start = start + settings.tl_x; -    start += static_cast<int>(settings.tl_x); -    start = static_cast<int>((start * sensor.optical_res) / MM_PER_INCH); +    start = static_cast<float>((start * settings.xres) / MM_PER_INCH);      ScanSession session;      session.params.xres = settings.xres;      session.params.yres = settings.yres; -    session.params.startx = start; // not used -    session.params.starty = 0; // not used +    session.params.startx = static_cast<unsigned>(start); +    session.params.starty = static_cast<unsigned>(move);      session.params.pixels = settings.pixels;      session.params.requested_pixels = settings.requested_pixels;      session.params.lines = settings.lines; @@ -1259,8 +1124,7 @@ ScanSession CommandSetGl843::calculate_scan_session(const Genesys_Device* dev,      session.params.scan_method = settings.scan_method;      session.params.scan_mode = settings.scan_mode;      session.params.color_filter = settings.color_filter; -    session.params.flags = ScanFlag::NONE; - +    session.params.flags = flags;      compute_session(dev, session, sensor);      return session; @@ -1352,8 +1216,6 @@ void CommandSetGl843::detect_document_end(Genesys_Device* dev) const              auto skip_lines = scan_end_lines - output_lines;              if (remaining_lines > skip_lines) { -                DBG(DBG_io, "%s: skip_lines=%zu\n", __func__, skip_lines); -                  remaining_lines -= skip_lines;                  dev->get_pipeline_source().set_remaining_bytes(remaining_lines *                                                                 dev->session.output_line_bytes_raw); @@ -1363,194 +1225,6 @@ void CommandSetGl843::detect_document_end(Genesys_Device* dev) const      }  } -// enables or disables XPA slider motor -void gl843_set_xpa_motor_power(Genesys_Device* dev, Genesys_Register_Set& regs, bool set) -{ -    DBG_HELPER(dbg); -    uint8_t val; - -    if (dev->model->model_id == ModelId::CANON_8400F) { - -        if (set) { -            val = dev->interface->read_register(0x6c); -            val &= ~(REG_0x6C_GPIO16 | REG_0x6C_GPIO13); -            if (dev->session.output_resolution >= 2400) { -                val &= ~REG_0x6C_GPIO10; -            } -            dev->interface->write_register(0x6c, val); - -            val = dev->interface->read_register(0xa9); -            val |= REG_0xA9_GPO30; -            val &= ~REG_0xA9_GPO29; -            dev->interface->write_register(0xa9, val); -        } else { -            val = dev->interface->read_register(0x6c); -            val |= REG_0x6C_GPIO16 | REG_0x6C_GPIO13; -            dev->interface->write_register(0x6c, val); - -            val = dev->interface->read_register(0xa9); -            val &= ~REG_0xA9_GPO30; -            val |= REG_0xA9_GPO29; -            dev->interface->write_register(0xa9, val); -        } -    } else if (dev->model->model_id == ModelId::CANON_8600F) { -        if (set) { -            val = dev->interface->read_register(REG_0x6C); -            val &= ~REG_0x6C_GPIO14; -            if (dev->session.output_resolution >= 2400) { -                val |= REG_0x6C_GPIO10; -            } -            dev->interface->write_register(REG_0x6C, val); - -            val = dev->interface->read_register(REG_0xA6); -            val |= REG_0xA6_GPIO17; -            val &= ~REG_0xA6_GPIO23; -            dev->interface->write_register(REG_0xA6, val); -        } else { -            val = dev->interface->read_register(REG_0x6C); -            val |= REG_0x6C_GPIO14; -            val &= ~REG_0x6C_GPIO10; -            dev->interface->write_register(REG_0x6C, val); - -            val = dev->interface->read_register(REG_0xA6); -            val &= ~REG_0xA6_GPIO17; -            val &= ~REG_0xA6_GPIO23; -            dev->interface->write_register(REG_0xA6, val); -        } -    } else if (dev->model->model_id == ModelId::HP_SCANJET_G4050) { -        if (set) { -            // set MULTFILM et GPOADF -            val = dev->interface->read_register(REG_0x6B); -            val |=REG_0x6B_MULTFILM|REG_0x6B_GPOADF; -            dev->interface->write_register(REG_0x6B, val); - -            val = dev->interface->read_register(REG_0x6C); -            val &= ~REG_0x6C_GPIO15; -            dev->interface->write_register(REG_0x6C, val); - -            /* Motor power ? No move at all without this one */ -            val = dev->interface->read_register(REG_0xA6); -            val |= REG_0xA6_GPIO20; -            dev->interface->write_register(REG_0xA6, val); - -            val = dev->interface->read_register(REG_0xA8); -            val &= ~REG_0xA8_GPO27; -            dev->interface->write_register(REG_0xA8, val); - -            val = dev->interface->read_register(REG_0xA9); -            val |= REG_0xA9_GPO32|REG_0xA9_GPO31; -            dev->interface->write_register(REG_0xA9, val); -        } else { -            // unset GPOADF -            val = dev->interface->read_register(REG_0x6B); -            val &= ~REG_0x6B_GPOADF; -            dev->interface->write_register(REG_0x6B, val); - -            val = dev->interface->read_register(REG_0xA8); -            val |= REG_0xA8_GPO27; -            dev->interface->write_register(REG_0xA8, val); - -            val = dev->interface->read_register(REG_0xA9); -            val &= ~REG_0xA9_GPO31; -            dev->interface->write_register(REG_0xA9, val); -        } -    } -    regs.state.is_xpa_motor_on = set; -} - - -/** @brief light XPA lamp - * toggle gpios to switch off regular lamp and light on the - * XPA light - * @param dev device to set up - */ -static void gl843_set_xpa_lamp_power(Genesys_Device* dev, bool set) -{ -    DBG_HELPER(dbg); - -    struct LampSettings { -        ModelId model_id; -        ScanMethod scan_method; -        GenesysRegisterSettingSet regs_on; -        GenesysRegisterSettingSet regs_off; -    }; - -    // FIXME: BUG: we're not clearing the registers to the previous state when returning back when -    // turning off the lamp -    LampSettings settings[] = { -        {   ModelId::CANON_8400F, ScanMethod::TRANSPARENCY, { -                { 0xa6, 0x34, 0xf4 }, -            }, { -                { 0xa6, 0x40, 0x70 }, -            } -        }, -        {   ModelId::CANON_8400F, ScanMethod::TRANSPARENCY_INFRARED, { -                { 0x6c, 0x40, 0x40 }, -                { 0xa6, 0x01, 0xff }, -            }, { -                { 0x6c, 0x00, 0x40 }, -                { 0xa6, 0x00, 0xff }, -            } -        }, -        {   ModelId::CANON_8600F, ScanMethod::TRANSPARENCY, { -                { 0xa6, 0x34, 0xf4 }, -                { 0xa7, 0xe0, 0xe0 }, -            }, { -                { 0xa6, 0x40, 0x70 }, -            } -        }, -        {   ModelId::CANON_8600F, ScanMethod::TRANSPARENCY_INFRARED, { -                { 0xa6, 0x00, 0xc0 }, -                { 0xa7, 0xe0, 0xe0 }, -                { 0x6c, 0x80, 0x80 }, -            }, { -                { 0xa6, 0x00, 0xc0 }, -                { 0x6c, 0x00, 0x80 }, -            } -        }, -        {   ModelId::PLUSTEK_OPTICFILM_7200I, ScanMethod::TRANSPARENCY, { -            }, { -                { 0xa6, 0x40, 0x70 }, // BUG: remove this cleanup write, it was enabled by accident -            } -        }, -        {   ModelId::PLUSTEK_OPTICFILM_7200I, ScanMethod::TRANSPARENCY_INFRARED, { -                { 0xa8, 0x07, 0x07 }, -            }, { -                { 0xa8, 0x00, 0x07 }, -            } -        }, -        {   ModelId::PLUSTEK_OPTICFILM_7300, ScanMethod::TRANSPARENCY, {}, {} }, -        {   ModelId::PLUSTEK_OPTICFILM_7500I, ScanMethod::TRANSPARENCY, {}, {} }, -        {   ModelId::PLUSTEK_OPTICFILM_7500I, ScanMethod::TRANSPARENCY_INFRARED, { -                { 0xa8, 0x07, 0x07 }, -            }, { -                { 0xa8, 0x00, 0x07 }, -            } -        }, -    }; - -    for (const auto& setting : settings) { -        if (setting.model_id == dev->model->model_id && -            setting.scan_method == dev->settings.scan_method) -        { -            apply_reg_settings_to_device(*dev, set ? setting.regs_on : setting.regs_off); -            return; -        } -    } - -    // BUG: we're currently calling the function in shut down path of regular lamp -    if (set) { -        throw SaneException("Unexpected code path entered"); -    } - -    GenesysRegisterSettingSet regs = { -        { 0xa6, 0x40, 0x70 }, -    }; -    apply_reg_settings_to_device(*dev, regs); -    // TODO: throw exception when we're only calling this function in error return path -    // throw SaneException("Could not find XPA lamp settings"); -} -  // Send the low-level scan command  void CommandSetGl843::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sensor,                                   Genesys_Register_Set* reg, bool start_motor) const @@ -1580,30 +1254,44 @@ void CommandSetGl843::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sens              }              if (reg->state.is_xpa_on && reg->state.is_lamp_on) { -                gl843_set_xpa_lamp_power(dev, true); +                dev->cmd_set->set_xpa_lamp_power(*dev, true);              }              if (reg->state.is_xpa_on) { -                gl843_set_xpa_motor_power(dev, *reg, true); +                dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY);              }              // blinking led              dev->interface->write_register(REG_0x7E, 0x01);              break;          case GpioId::CANON_8400F: +            if (dev->session.params.xres == 3200) +            { +                GenesysRegisterSettingSet reg_settings = { +                    { 0x6c, 0x00, 0x02 }, +                }; +                apply_reg_settings_to_device(*dev, reg_settings); +            } +            if (reg->state.is_xpa_on && reg->state.is_lamp_on) { +                dev->cmd_set->set_xpa_lamp_power(*dev, true); +            } +            if (reg->state.is_xpa_on) { +                dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY); +            } +            break;          case GpioId::CANON_8600F:              if (reg->state.is_xpa_on && reg->state.is_lamp_on) { -                gl843_set_xpa_lamp_power(dev, true); +                dev->cmd_set->set_xpa_lamp_power(*dev, true);              }              if (reg->state.is_xpa_on) { -                gl843_set_xpa_motor_power(dev, *reg, true); +                dev->cmd_set->set_motor_mode(*dev, *reg, MotorMode::PRIMARY_AND_SECONDARY);              }              break;          case GpioId::PLUSTEK_OPTICFILM_7200I:          case GpioId::PLUSTEK_OPTICFILM_7300:          case GpioId::PLUSTEK_OPTICFILM_7500I: {              if (reg->state.is_xpa_on && reg->state.is_lamp_on) { -                gl843_set_xpa_lamp_power(dev, true); +                dev->cmd_set->set_xpa_lamp_power(*dev, true);              }              break;          } @@ -1612,8 +1300,7 @@ void CommandSetGl843::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sens              break;      } -    // clear scan and feed count -    dev->interface->write_register(REG_0x0D, REG_0x0D_CLRLNCNT | REG_0x0D_CLRMCNT); +    scanner_clear_scan_and_feed_counts(*dev);      // enable scan and motor      uint8_t val = dev->interface->read_register(REG_0x01); @@ -1622,11 +1309,26 @@ void CommandSetGl843::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sens      scanner_start_action(*dev, start_motor); -    if (reg->state.is_motor_on) { -        dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); -    } -    if (reg->state.is_xpa_motor_on) { -        dev->advance_head_pos_by_session(ScanHeadId::SECONDARY); +    switch (reg->state.motor_mode) { +        case MotorMode::PRIMARY: { +            if (reg->state.is_motor_on) { +                dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); +            } +            break; +        } +        case MotorMode::PRIMARY_AND_SECONDARY: { +            if (reg->state.is_motor_on) { +                dev->advance_head_pos_by_session(ScanHeadId::PRIMARY); +                dev->advance_head_pos_by_session(ScanHeadId::SECONDARY); +            } +            break; +        } +        case MotorMode::SECONDARY: { +            if (reg->state.is_motor_on) { +                dev->advance_head_pos_by_session(ScanHeadId::SECONDARY); +            } +            break; +        }      }  } @@ -1640,10 +1342,8 @@ void CommandSetGl843::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg,      // post scan gpio      dev->interface->write_register(0x7e, 0x00); -    // turn off XPA lamp if needed -    // BUG: the if condition below probably shouldn't be enabled when XPA is off -    if (reg->state.is_xpa_on || reg->state.is_lamp_on) { -        gl843_set_xpa_lamp_power(dev, false); +    if (reg->state.is_xpa_on) { +        dev->cmd_set->set_xpa_lamp_power(*dev, false);      }      if (!dev->model->is_sheetfed) { @@ -1658,202 +1358,79 @@ void CommandSetGl843::move_back_home(Genesys_Device* dev, bool wait_until_home)      scanner_move_back_home(*dev, wait_until_home);  } -// Automatically set top-left edge of the scan area by scanning a 200x200 pixels area at 600 dpi -// from very top of scanner -void CommandSetGl843::search_start_position(Genesys_Device* dev) const -{ -    DBG_HELPER(dbg); -  Genesys_Register_Set local_reg; - -  int pixels = 600; -  int dpi = 300; - -  local_reg = dev->reg; - -  /* sets for a 200 lines * 600 pixels */ -  /* normal scan with no shading */ - -    // FIXME: the current approach of doing search only for one resolution does not work on scanners -    // whith employ different sensors with potentially different settings. -    const auto& sensor = sanei_genesys_find_sensor(dev, dpi, 1, dev->model->default_method); - -    ScanSession session; -    session.params.xres = dpi; -    session.params.yres = dpi; -    session.params.startx = 0; -    session.params.starty = 0; // we should give a small offset here - ~60 steps -    session.params.pixels = 600; -    session.params.lines = dev->model->search_lines; -    session.params.depth = 8; -    session.params.channels = 1; -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = ScanColorMode::GRAY; -    session.params.color_filter = ColorFilter::GREEN; -    session.params.flags =  ScanFlag::DISABLE_SHADING | -                            ScanFlag::DISABLE_GAMMA | -                            ScanFlag::IGNORE_LINE_DISTANCE | -                            ScanFlag::DISABLE_BUFFER_FULL_MOVE; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, &local_reg, session); - -    // send to scanner -    dev->interface->write_registers(local_reg); - -    dev->cmd_set->begin_scan(dev, sensor, &local_reg, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("search_start_position"); -        end_scan(dev, &local_reg, true); -        dev->reg = local_reg; -        return; -    } - -    wait_until_buffer_non_empty(dev); - -    // now we're on target, we can read data -    Image image = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes_raw); - -    scanner_stop_action_no_move(*dev, local_reg); - -    if (DBG_LEVEL >= DBG_data) { -        sanei_genesys_write_pnm_file("gl843_search_position.pnm", image); -    } - -    dev->cmd_set->end_scan(dev, &local_reg, true); - -  /* update regs to copy ASIC internal state */ -  dev->reg = local_reg; - -    for (auto& sensor_update : -            sanei_genesys_find_sensors_all_for_write(dev, dev->model->default_method)) -    { -        sanei_genesys_search_reference_point(dev, sensor_update, image.get_row_ptr(0), 0, dpi, -                                             pixels, dev->model->search_lines); -    } -} - -// sets up register for coarse gain calibration -// todo: check it for scanners using it -void CommandSetGl843::init_regs_for_coarse_calibration(Genesys_Device* dev, -                                                       const Genesys_Sensor& sensor, -                                                       Genesys_Register_Set& regs) const -{ -    DBG_HELPER(dbg); - -    ScanFlag flags = ScanFlag::DISABLE_SHADING | -                     ScanFlag::DISABLE_GAMMA | -                     ScanFlag::SINGLE_LINE | -                     ScanFlag::IGNORE_LINE_DISTANCE; - -    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || -        dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) { -        flags |= ScanFlag::USE_XPA; -    } - -    ScanSession session; -    session.params.xres = dev->settings.xres; -    session.params.yres = dev->settings.yres; -    session.params.startx = 0; -    session.params.starty = 0; -    session.params.pixels = sensor.optical_res / sensor.ccd_pixels_per_system_pixel(); -    session.params.lines = 20; -    session.params.depth = 16; -    session.params.channels = dev->settings.get_channels(); -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = dev->settings.scan_mode; -    session.params.color_filter = dev->settings.color_filter; -    session.params.flags = flags; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, ®s, session); - -  sanei_genesys_set_motor_power(regs, false); - -  DBG(DBG_info, "%s: optical sensor res: %d dpi, actual res: %d\n", __func__, -      sensor.optical_res / sensor.ccd_pixels_per_system_pixel(), dev->settings.xres); - -    dev->interface->write_registers(regs); -} -  // 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  {      DBG_HELPER(dbg); -  int move, resolution, dpihw, factor; - -  /* initial calibration reg values */ -  regs = dev->reg; - -  dev->calib_channels = 3; +    int move; +    float calib_size_mm = 0;      if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||          dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)      { -        dev->calib_lines = dev->model->shading_ta_lines; +        calib_size_mm = dev->model->y_size_calib_ta_mm;      } else { -        dev->calib_lines = dev->model->shading_lines; +        calib_size_mm = dev->model->y_size_calib_mm;      } -    dpihw = sensor.get_logical_hwdpi(dev->settings.xres); -  factor=sensor.optical_res/dpihw; -  resolution=dpihw; +    unsigned resolution = sensor.shading_resolution; -  const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, dev->calib_channels, +    unsigned channels = 3; +  const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,                                                         dev->settings.scan_method); -    if ((dev->settings.scan_method == ScanMethod::TRANSPARENCY || -         dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) && -        dev->model->model_id == ModelId::CANON_8600F && -        dev->settings.xres == 4800) -    { -        float offset = static_cast<float>(dev->model->x_offset_ta); -        offset /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution); -        offset = static_cast<float>((offset * calib_sensor.optical_res) / MM_PER_INCH); +    unsigned calib_pixels = 0; +    unsigned calib_pixels_offset = 0; -        float size = static_cast<float>(dev->model->x_size_ta); -        size /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution); -        size = static_cast<float>((size * calib_sensor.optical_res) / MM_PER_INCH); +    if (should_calibrate_only_active_area(*dev, dev->settings)) { +        float offset = dev->model->x_offset_ta; +        // FIXME: we should use resolution here +        offset = static_cast<float>((offset * dev->settings.xres) / MM_PER_INCH); -        dev->calib_pixels_offset = static_cast<std::size_t>(offset); -        dev->calib_pixels = static_cast<std::size_t>(size); -    } -    else -    { -        dev->calib_pixels_offset = 0; -        dev->calib_pixels = calib_sensor.sensor_pixels / factor; -    } +        float size = dev->model->x_size_ta; +        size = static_cast<float>((size * dev->settings.xres) / MM_PER_INCH); -  dev->calib_resolution = resolution; +        calib_pixels_offset = static_cast<std::size_t>(offset); +        calib_pixels = static_cast<std::size_t>(size); +    } else { +        calib_pixels_offset = 0; +        calib_pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; +    }      ScanFlag flags = ScanFlag::DISABLE_SHADING |                       ScanFlag::DISABLE_GAMMA | -                     ScanFlag::DISABLE_BUFFER_FULL_MOVE | -                     ScanFlag::IGNORE_LINE_DISTANCE; +                     ScanFlag::DISABLE_BUFFER_FULL_MOVE;      if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||          dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)      { -        // note: move_to_ta() function has already been called and the sensor is at the +        // note: scanner_move_to_ta() function has already been called and the sensor is at the          // transparency adapter          move = static_cast<int>(dev->model->y_offset_calib_white_ta - dev->model->y_offset_sensor_to_ta); +        if (dev->model->model_id == ModelId::CANON_8600F && resolution == 2400) { +            move /= 2; +        } +        if (dev->model->model_id == ModelId::CANON_8600F && resolution == 4800) { +            move /= 4; +        }          flags |= ScanFlag::USE_XPA;      } else {          move = static_cast<int>(dev->model->y_offset_calib_white);      }      move = static_cast<int>((move * resolution) / MM_PER_INCH); +    unsigned calib_lines = static_cast<unsigned>(calib_size_mm * resolution / MM_PER_INCH);      ScanSession session;      session.params.xres = resolution;      session.params.yres = resolution; -    session.params.startx = dev->calib_pixels_offset; +    session.params.startx = calib_pixels_offset;      session.params.starty = move; -    session.params.pixels = dev->calib_pixels; -    session.params.lines = dev->calib_lines; +    session.params.pixels = calib_pixels; +    session.params.lines = calib_lines;      session.params.depth = 16; -    session.params.channels = dev->calib_channels; +    session.params.channels = channels;      session.params.scan_method = dev->settings.scan_method;      session.params.scan_mode = dev->settings.scan_mode;      session.params.color_filter = dev->settings.color_filter; @@ -1862,89 +1439,7 @@ void CommandSetGl843::init_regs_for_shading(Genesys_Device* dev, const Genesys_S      init_regs_for_scan_session(dev, calib_sensor, ®s, session); -     // the pixel number may be updated to conform to scanner constraints -    dev->calib_pixels = session.output_pixels; -      dev->calib_session = session; -    dev->calib_total_bytes_to_read = session.output_total_bytes_raw; - -    dev->interface->write_registers(regs); -} - -/** @brief set up registers for the actual scan - */ -void CommandSetGl843::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const -{ -    DBG_HELPER(dbg); -  float move; -  int move_dpi; -  float start; - -    debug_dump(DBG_info, dev->settings); - -  move_dpi = dev->motor.base_ydpi; - -    ScanFlag flags = ScanFlag::NONE; - -    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || -        dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) -    { -        // note: move_to_ta() function has already been called and the sensor is at the -        // transparency adapter -        if (dev->ignore_offsets) { -            move = 0; -        } else { -            move = static_cast<float>(dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta); -        } -        flags |= ScanFlag::USE_XPA; -    } else { -        if (dev->ignore_offsets) { -            move = 0; -        } else { -            move = static_cast<float>(dev->model->y_offset); -        } -    } - -    move += static_cast<float>(dev->settings.tl_y); -    move = static_cast<float>((move * move_dpi) / MM_PER_INCH); -  DBG(DBG_info, "%s: move=%f steps\n", __func__, move); - -  /* start */ -    if (dev->settings.scan_method==ScanMethod::TRANSPARENCY || -        dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) -    { -        start = static_cast<float>(dev->model->x_offset_ta); -    } else { -        start = static_cast<float>(dev->model->x_offset); -    } - -    if (dev->model->model_id == ModelId::CANON_8400F || -        dev->model->model_id == ModelId::CANON_8600F) -    { -        // FIXME: this is probably just an artifact of a bug elsewhere -        start /= sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres); -    } - -    start = static_cast<float>(start + dev->settings.tl_x); -    start = static_cast<float>((start * sensor.optical_res) / MM_PER_INCH); - -    ScanSession session; -    session.params.xres = dev->settings.xres; -    session.params.yres = dev->settings.yres; -    session.params.startx = static_cast<unsigned>(start); -    session.params.starty = static_cast<unsigned>(move); -    session.params.pixels = dev->settings.pixels; -    session.params.requested_pixels = dev->settings.requested_pixels; -    session.params.lines = dev->settings.lines; -    session.params.depth = dev->settings.depth; -    session.params.channels = dev->settings.get_channels(); -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = dev->settings.scan_mode; -    session.params.color_filter = dev->settings.color_filter; -    session.params.flags = flags; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, &dev->reg, session);  }  /** @@ -1975,8 +1470,7 @@ void CommandSetGl843::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor          gamma[i * 2 + size * 4 + 1] = (bgamma[i] >> 8) & 0xff;      } -    dev->interface->write_gamma(0x28, 0x0000, gamma.data(), size * 2 * 3, -                                ScannerInterface::FLAG_SWAP_REGISTERS); +    dev->interface->write_gamma(0x28, 0x0000, gamma.data(), size * 2 * 3);  }  /* this function does the led calibration by scanning one line of the calibration @@ -1987,607 +1481,69 @@ void CommandSetGl843::send_gamma_table(Genesys_Device* dev, const Genesys_Sensor  SensorExposure CommandSetGl843::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,                                                  Genesys_Register_Set& regs) const  { -    DBG_HELPER(dbg); -  int num_pixels; -  int avg[3], avga, avge; -  int turn; -  uint16_t expr, expg, expb; - -    // offset calibration is always done in color mode -    unsigned channels = 3; - -    // take a copy, as we're going to modify exposure -    auto calib_sensor = sanei_genesys_find_sensor(dev, sensor.optical_res, channels, -                                                  dev->settings.scan_method); - -    num_pixels = (calib_sensor.sensor_pixels * calib_sensor.optical_res) / calib_sensor.optical_res; - -  /* initial calibration reg values */ -  regs = dev->reg; - -    ScanSession session; -    session.params.xres = calib_sensor.sensor_pixels; -    session.params.yres = dev->motor.base_ydpi; -    session.params.startx = 0; -    session.params.starty = 0; -    session.params.pixels = num_pixels; -    session.params.lines = 1; -    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_LINE_DISTANCE; -    compute_session(dev, session, calib_sensor); - -    init_regs_for_scan_session(dev, calib_sensor, ®s, session); - -    dev->interface->write_registers(regs); - -/* -   we try to get equal bright leds here: - -   loop: -     average per color -     adjust exposure times - */ - -  expr = calib_sensor.exposure.red; -  expg = calib_sensor.exposure.green; -  expb = calib_sensor.exposure.blue; - -  turn = 0; - -    bool acceptable = false; -  do -    { - -      calib_sensor.exposure.red = expr; -      calib_sensor.exposure.green = expg; -      calib_sensor.exposure.blue = expb; - -        regs_set_exposure(dev->model->asic_type, regs, calib_sensor.exposure); - -        dev->interface->write_registers(regs); - -      DBG(DBG_info, "%s: starting first line reading\n", __func__); -        dev->cmd_set->begin_scan(dev, calib_sensor, ®s, true); - -        if (is_testing_mode()) { -            dev->interface->test_checkpoint("led_calibration"); -            move_back_home(dev, true); -            return calib_sensor.exposure; -        } - -        auto image = 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[30]; -            std::snprintf(fn, 30, "gl843_led_%02d.pnm", turn); -            sanei_genesys_write_pnm_file(fn, image); -	} - -        acceptable = true; - -        for (unsigned ch = 0; ch < channels; ch++) { -            avg[ch] = 0; -            for (std::size_t x = 0; x < image.get_width(); x++) { -                avg[ch] += image.get_raw_channel(x, 0, ch); -            } -            avg[ch] /= image.get_width(); -        } - -      DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]); - -        acceptable = true; - -      if (avg[0] < avg[1] * 0.95 || avg[1] < avg[0] * 0.95 || -	  avg[0] < avg[2] * 0.95 || avg[2] < avg[0] * 0.95 || -	  avg[1] < avg[2] * 0.95 || avg[2] < avg[1] * 0.95) -        acceptable = false; - -      if (!acceptable) -	{ -	  avga = (avg[0] + avg[1] + avg[2]) / 3; -	  expr = (expr * avga) / avg[0]; -	  expg = (expg * avga) / avg[1]; -	  expb = (expb * avga) / avg[2]; -/* -  keep the resulting exposures below this value. -  too long exposure drives the ccd into saturation. -  we may fix this by relying on the fact that -  we get a striped scan without shading, by means of -  statistical calculation -*/ -	  avge = (expr + expg + expb) / 3; - -	  /* don't overflow max exposure */ -	  if (avge > 3000) -	    { -	      expr = (expr * 2000) / avge; -	      expg = (expg * 2000) / avge; -	      expb = (expb * 2000) / avge; -	    } -	  if (avge < 50) -	    { -	      expr = (expr * 50) / avge; -	      expg = (expg * 50) / avge; -	      expb = (expb * 50) / avge; -	    } - -	} -        scanner_stop_action(*dev); - -      turn++; - -    } -  while (!acceptable && turn < 100); - -  DBG(DBG_info, "%s: acceptable exposure: %d,%d,%d\n", __func__, expr, expg, expb); - -    move_back_home(dev, true); - -    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]; +    return scanner_led_calibration(*dev, sensor, regs);  } -/** @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_logical_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 = calib_sensor.sensor_pixels / factor; -  int start_pixel = 0; -  black_pixels = calib_sensor.black_pixels / factor; - -    if ((dev->settings.scan_method == ScanMethod::TRANSPARENCY || -         dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) && -        dev->model->model_id == ModelId::CANON_8600F && -        dev->settings.xres == 4800) -    { -        start_pixel = static_cast<int>(dev->model->x_offset_ta); -        start_pixel /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution); -        start_pixel = static_cast<int>((start_pixel * calib_sensor.optical_res) / MM_PER_INCH); - -        target_pixels = static_cast<int>(dev->model->x_size_ta); -        target_pixels /= calib_sensor.get_ccd_size_divisor_for_dpi(resolution); -        target_pixels = static_cast<int>((target_pixels * calib_sensor.optical_res) / MM_PER_INCH); -    } - -    ScanFlag flags = ScanFlag::DISABLE_SHADING | -                     ScanFlag::DISABLE_GAMMA | -                     ScanFlag::SINGLE_LINE | -                     ScanFlag::IGNORE_LINE_DISTANCE; - -    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, ®s, 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, ®s, 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, ®s, 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, ®s, 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 factor, dpihw; -  float coeff; -    int lines; -  int resolution; - -    if (dev->frontend.layout.type != FrontendType::WOLFSON) -        return; +    scanner_coarse_gain_calibration(*dev, sensor, regs, dpi); +} -    dpihw = sensor.get_logical_hwdpi(dpi); -  factor=sensor.optical_res/dpihw; +// wait for lamp warmup by scanning the same line until difference +// between 2 scans is below a threshold +void CommandSetGl843::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, +                                           Genesys_Register_Set* reg) const +{ +    DBG_HELPER(dbg); +    (void) sensor; -    // coarse gain calibration is always done in color mode      unsigned channels = 3; +    unsigned resolution = dev->model->get_resolution_settings(dev->settings.scan_method) +                                     .get_nearest_resolution_x(600); -  /* 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; -  int target_pixels = sensor.sensor_pixels / factor; +  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 / 2; -    ScanFlag flags = ScanFlag::DISABLE_SHADING | -                     ScanFlag::DISABLE_GAMMA | -                     ScanFlag::SINGLE_LINE | -                     ScanFlag::IGNORE_LINE_DISTANCE; +  *reg = dev->reg; +    auto 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.startx = (num_pixels / 2) * resolution / calib_sensor.full_resolution;      session.params.starty = 0; -    session.params.pixels = target_pixels; -    session.params.lines = lines; -    session.params.depth = 8; +    session.params.pixels = num_pixels; +    session.params.lines = 1; +    session.params.depth = dev->model->bpp_color_values.front();      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, ®s, 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, ®s, 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); -} - -// wait for lamp warmup by scanning the same line until difference -// between 2 scans is below a threshold -void CommandSetGl843::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                           Genesys_Register_Set* reg, int* channels, -                                           int* total_size) const -{ -    DBG_HELPER(dbg); -  int num_pixels; -  int dpihw; -  int resolution; -  int factor; - -  /* setup scan */ -  *channels=3; -  resolution=600; -    dpihw = sensor.get_logical_hwdpi(resolution); -  resolution=dpihw; - -  const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, *channels, -                                                       dev->settings.scan_method); -  factor = calib_sensor.optical_res/dpihw; -  num_pixels = calib_sensor.sensor_pixels/(factor*2); -  *total_size = num_pixels * 3 * 1; - -  *reg = dev->reg; - -    ScanSession session; -    session.params.xres = resolution; -    session.params.yres = resolution; -    session.params.startx = num_pixels/2; -    session.params.starty = 0; -    session.params.pixels = num_pixels; -    session.params.lines = 1; -    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_LINE_DISTANCE;      compute_session(dev, session, calib_sensor);      init_regs_for_scan_session(dev, calib_sensor, reg, session);    sanei_genesys_set_motor_power(*reg, false); -    dev->interface->write_registers(*reg);  }  /** @@ -2654,7 +1610,7 @@ void CommandSetGl843::asic_boot(Genesys_Device* dev, bool cold) const      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; +    dev->reg.find_reg(0x0b).value = val;      if (dev->model->model_id == ModelId::CANON_8400F) {          dev->interface->write_0x8c(0x1e, 0x01); @@ -2691,18 +1647,11 @@ void CommandSetGl843::asic_boot(Genesys_Device* dev, bool cold) const      val = (dev->reg.find_reg(0x0b).value & ~REG_0x0B_CLKSET) | clock_freq;      dev->interface->write_register(REG_0x0B, val); -  dev->reg.find_reg(0x0b).value = val; +    dev->reg.find_reg(0x0b).value = val;    /* prevent further writings by bulk write register */    dev->reg.remove_reg(0x0b); -    if (dev->model->model_id != ModelId::CANON_8600F) { -      // set up end access -      // FIXME: this is overwritten in gl843_init_gpio -        dev->interface->write_register(REG_0xA7, 0x04); -        dev->interface->write_register(REG_0xA9, 0x00); -    } -      // set RAM read address      dev->interface->write_register(REG_0x29, 0x00);      dev->interface->write_register(REG_0x2A, 0x00); @@ -2710,8 +1659,6 @@ void CommandSetGl843::asic_boot(Genesys_Device* dev, bool cold) const      // setup gpio      gl843_init_gpio(dev); - -    scanner_move(*dev, dev->model->default_method, 300, Direction::FORWARD);      dev->interface->sleep_ms(100);  } @@ -2724,7 +1671,7 @@ void CommandSetGl843::init(Genesys_Device* dev) const    DBG_INIT ();      DBG_HELPER(dbg); -    sanei_genesys_asic_init(dev, 0); +    sanei_genesys_asic_init(dev);  }  void CommandSetGl843::update_hardware_sensors(Genesys_Scanner* s) const @@ -2754,216 +1701,10 @@ void CommandSetGl843::update_hardware_sensors(Genesys_Scanner* s) const      }  } -/** @brief move sensor to transparency adaptor - * Move sensor to the calibration of the transparency adapator (XPA). - * @param dev device to use - */ -void CommandSetGl843::move_to_ta(Genesys_Device* dev) const +void CommandSetGl843::update_home_sensor_gpio(Genesys_Device& dev) const  {      DBG_HELPER(dbg); - -    const auto& resolution_settings = dev->model->get_resolution_settings(dev->model->default_method); -    float resolution = resolution_settings.get_min_resolution_y(); - -    unsigned multiplier = 16; -    if (dev->model->model_id == ModelId::CANON_8400F) { -        multiplier = 4; -    } -    unsigned feed = static_cast<unsigned>(multiplier * (dev->model->y_offset_sensor_to_ta * resolution) / -                                          MM_PER_INCH); -    scanner_move(*dev, dev->model->default_method, feed, Direction::FORWARD); -} - - -/** @brief search for a full width black or white strip. - * This function searches for a black or white stripe across the scanning area. - * When searching backward, the searched area must completely be of the desired - * color since this area will be used for calibration which scans forward. - * @param dev scanner device - * @param forward true if searching forward, false if searching backward - * @param black true if searching for a black strip, false for a white strip - */ -void CommandSetGl843::search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                   bool forward, bool black) const -{ -    DBG_HELPER_ARGS(dbg, "%s %s",  black ? "black" : "white", forward ? "forward" : "reverse"); -  unsigned int pixels, lines, channels; -  Genesys_Register_Set local_reg; -    int dpi; -  unsigned int pass, count, found, x, y; - -    dev->cmd_set->set_fe(dev, sensor, AFE_SET); -    scanner_stop_action(*dev); - -  /* set up for a gray scan at lowest dpi */ -  dpi = sanei_genesys_get_lowest_dpi(dev); -  channels = 1; - -  const auto& calib_sensor = sanei_genesys_find_sensor(dev, dpi, channels, -                                                       dev->settings.scan_method); - -  /* 10 MM */ -  /* lines = (10 * dpi) / MM_PER_INCH; */ -  /* shading calibation is done with dev->motor.base_ydpi */ -  lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi; -  pixels = (calib_sensor.sensor_pixels * dpi) / calib_sensor.optical_res; - -    dev->set_head_pos_zero(ScanHeadId::PRIMARY); - -  local_reg = dev->reg; - -    ScanSession session; -    session.params.xres = dpi; -    session.params.yres = dpi; -    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::GRAY; -    session.params.color_filter = ColorFilter::RED; -    session.params.flags = ScanFlag::DISABLE_SHADING | ScanFlag::DISABLE_SHADING; -    if (!forward) { -        session.params.flags = ScanFlag::REVERSE; -    } -    compute_session(dev, session, calib_sensor); - -    init_regs_for_scan_session(dev, calib_sensor, &local_reg, session); - -    dev->interface->write_registers(local_reg); - -    dev->cmd_set->begin_scan(dev, calib_sensor, &local_reg, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("search_strip"); -        scanner_stop_action(*dev); -        return; -    } - -    wait_until_buffer_non_empty(dev); - -    // now we're on target, we can read data -    auto data = read_unshuffled_image_from_scanner(dev, session, -                                                   session.output_total_bytes_raw); - -    scanner_stop_action(*dev); - -  pass = 0; -  if (DBG_LEVEL >= DBG_data) -    { -      char fn[40]; -        std::snprintf(fn, 40, "gl843_search_strip_%s_%s%02d.pnm", -                      black ? "black" : "white", forward ? "fwd" : "bwd", pass); -        sanei_genesys_write_pnm_file(fn, data); -    } - -  /* loop until strip is found or maximum pass number done */ -  found = 0; -  while (pass < 20 && !found) -    { -        dev->interface->write_registers(local_reg); - -        // now start scan -        dev->cmd_set->begin_scan(dev, calib_sensor, &local_reg, true); - -        wait_until_buffer_non_empty(dev); - -        // now we're on target, we can read data -        data = read_unshuffled_image_from_scanner(dev, session, session.output_total_bytes_raw); - -        scanner_stop_action(*dev); - -      if (DBG_LEVEL >= DBG_data) -	{ -          char fn[40]; -            std::snprintf(fn, 40, "gl843_search_strip_%s_%s%02d.pnm", -                          black ? "black" : "white", forward ? "fwd" : "bwd", pass); -            sanei_genesys_write_pnm_file(fn, data); -	} - -      /* search data to find black strip */ -      /* when searching forward, we only need one line of the searched color since we -       * will scan forward. But when doing backward search, we need all the area of the -       * same color */ -      if (forward) -	{ -	  for (y = 0; y < lines && !found; y++) -	    { -	      count = 0; -	      /* count of white/black pixels depending on the color searched */ -	      for (x = 0; x < pixels; x++) -		{ -		  /* when searching for black, detect white pixels */ -                    if (black && data.get_raw_channel(x, y, 0) > 90) { -		      count++; -		    } -		  /* when searching for white, detect black pixels */ -                    if (!black && data.get_raw_channel(x, y, 0) < 60) { -		      count++; -		    } -		} - -	      /* at end of line, if count >= 3%, line is not fully of the desired color -	       * so we must go to next line of the buffer */ -	      /* count*100/pixels < 3 */ -	      if ((count * 100) / pixels < 3) -		{ -		  found = 1; -		  DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__, -		      pass, y); -		} -	      else -		{ -		  DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count, -		      (100 * count) / pixels); -		} -	    } -	} -      else			/* since calibration scans are done forward, we need the whole area -				   to be of the required color when searching backward */ -	{ -	  count = 0; -	  for (y = 0; y < lines; y++) -	    { -	      /* count of white/black pixels depending on the color searched */ -	      for (x = 0; x < pixels; x++) -		{ -                    // when searching for black, detect white pixels -                    if (black && data.get_raw_channel(x, y, 0) > 90) { -		      count++; -		    } -                    // when searching for white, detect black pixels -                    if (!black && data.get_raw_channel(x, y, 0) < 60) { -		      count++; -		    } -		} -	    } - -	  /* at end of area, if count >= 3%, area is not fully of the desired color -	   * so we must go to next buffer */ -	  if ((count * 100) / (pixels * lines) < 3) -	    { -	      found = 1; -	      DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass); -	    } -	  else -	    { -	      DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count, -		  (100 * count) / pixels); -	    } -	} -      pass++; -    } -  if (found) -    { -      DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white"); -    } -  else -    { -        throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found", black ? "black" : "white"); -    } +    (void) dev;  }  /** @@ -2974,43 +1715,27 @@ void CommandSetGl843::send_shading_data(Genesys_Device* dev, const Genesys_Senso                                          uint8_t* data, int size) const  {      DBG_HELPER(dbg); -  uint32_t final_size, length, i; +    uint32_t final_size, i;    uint8_t *buffer; -  int count,offset; -  GenesysRegister *r; -    uint16_t strpixel, endpixel, startx; - -  offset=0; -  length=size; -    r = sanei_genesys_get_address(&dev->reg, REG_0x01); -    if (r->value & REG_0x01_SHDAREA) -    { -      /* recompute STRPIXEL used shading calibration so we can -       * compute offset within data for SHDAREA case */ - -        // FIXME: the following is likely incorrect -        // start coordinate in optical dpi coordinates -        startx = (sensor.dummy_pixel / sensor.ccd_pixels_per_system_pixel()) / dev->session.hwdpi_divisor; -        startx *= dev->session.pixel_count_multiplier; - -      /* current scan coordinates */ -        strpixel = dev->session.pixel_startx; -        endpixel = dev->session.pixel_endx; - -        if (dev->model->model_id == ModelId::CANON_4400F || -            dev->model->model_id == ModelId::CANON_8600F) -        { -            int half_ccd_factor = dev->session.optical_resolution / -                                  sensor.get_logical_hwdpi(dev->session.output_resolution); -            strpixel /= half_ccd_factor * sensor.ccd_pixels_per_system_pixel(); -            endpixel /= half_ccd_factor * sensor.ccd_pixels_per_system_pixel(); -        } +    int count; + +    int offset = 0; +    unsigned length = size; + +    if (dev->reg.get8(REG_0x01) & REG_0x01_SHDAREA) { +        offset = dev->session.params.startx * sensor.shading_resolution / +                 dev->session.params.xres; -      /* 16 bit words, 2 words per color, 3 color channels */ -      offset=(strpixel-startx)*2*2*3; -      length=(endpixel-strpixel)*2*2*3; -      DBG(DBG_info, "%s: STRPIXEL=%d, ENDPIXEL=%d, startx=%d\n", __func__, strpixel, endpixel, -          startx); +        length = dev->session.output_pixels * sensor.shading_resolution / +                 dev->session.params.xres; + +        offset += sensor.shading_pixel_offset; + +        // 16 bit words, 2 words per color, 3 color channels +        length *= 2 * 2 * 3; +        offset *= 2 * 2 * 3; +    } else { +        offset += sensor.shading_pixel_offset * 2 * 2 * 3;      }      dev->interface->record_key_value("shading_offset", std::to_string(offset)); @@ -3024,6 +1749,14 @@ void CommandSetGl843::send_shading_data(Genesys_Device* dev, const Genesys_Senso    /* copy regular shading data to the expected layout */    buffer = final_data.data();    count = 0; +    if (offset < 0) { +        count += (-offset); +        length -= (-offset); +        offset = 0; +    } +    if (static_cast<int>(length) + offset > static_cast<int>(size)) { +        length = size - offset; +    }    /* loop over calibration data */    for (i = 0; i < length; i++) @@ -3036,8 +1769,7 @@ void CommandSetGl843::send_shading_data(Genesys_Device* dev, const Genesys_Senso  	}      } -    dev->interface->write_buffer(0x3c, 0, final_data.data(), count, -                                 ScannerInterface::FLAG_SMALL_ADDRESS); +    dev->interface->write_buffer(0x3c, 0, final_data.data(), count);  }  bool CommandSetGl843::needs_home_before_init_regs_for_scan(Genesys_Device* dev) const @@ -3051,10 +1783,5 @@ void CommandSetGl843::wait_for_motor_stop(Genesys_Device* dev) const      (void) dev;  } -std::unique_ptr<CommandSet> create_gl843_cmd_set() -{ -    return std::unique_ptr<CommandSet>(new CommandSetGl843{}); -} -  } // namespace gl843  } // namespace genesys diff --git a/backend/genesys/gl843.h b/backend/genesys/gl843.h index 9f0a9e9..5326a2d 100644 --- a/backend/genesys/gl843.h +++ b/backend/genesys/gl843.h @@ -42,7 +42,7 @@  */  #include "genesys.h" -#include "command_set.h" +#include "command_set_common.h"  #ifndef BACKEND_GENESYS_GL843_H  #define BACKEND_GENESYS_GL843_H @@ -50,7 +50,7 @@  namespace genesys {  namespace gl843 { -class CommandSetGl843 : public CommandSet +class CommandSetGl843 : public CommandSetCommon  {  public:      ~CommandSetGl843() override = default; @@ -60,17 +60,11 @@ public:      void init(Genesys_Device* dev) const override;      void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, -                              Genesys_Register_Set* regs, int* channels, -                              int* total_size) const override; - -    void init_regs_for_coarse_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                          Genesys_Register_Set& regs) const override; +                              Genesys_Register_Set* regs) const override;      void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,                                 Genesys_Register_Set& regs) const override; -    void init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; -      void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,                                      Genesys_Register_Set* reg,                                      const ScanSession& session) const override; @@ -86,8 +80,6 @@ public:      void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; -    void search_start_position(Genesys_Device* dev) const override; -      void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,                              Genesys_Register_Set& regs) const override; @@ -103,17 +95,14 @@ public:      void update_hardware_sensors(struct Genesys_Scanner* s) const override; +    void update_home_sensor_gpio(Genesys_Device& dev) const override; +      void load_document(Genesys_Device* dev) const override;      void detect_document_end(Genesys_Device* dev) const override;      void eject_document(Genesys_Device* dev) const override; -    void search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, -                      bool forward, bool black) const override; - -    void move_to_ta(Genesys_Device* dev) const override; -      void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t* data,                             int size) const override; diff --git a/backend/genesys/gl843_registers.h b/backend/genesys/gl843_registers.h index 8ecb0fc..cbc38c0 100644 --- a/backend/genesys/gl843_registers.h +++ b/backend/genesys/gl843_registers.h @@ -338,6 +338,16 @@ static constexpr RegAddr REG_CK4MAP = 0x7a;  static constexpr RegAddr REG_0x7E = 0x7e; +static constexpr RegAddr REG_0x80 = 0x80; +static constexpr RegMask REG_0x80_TABLE1_NORMAL = 0x03; +static constexpr RegShift REG_0x80S_TABLE1_NORMAL = 0; +static constexpr RegMask REG_0x80_TABLE2_BACK = 0x0c; +static constexpr RegShift REG_0x80S_TABLE2_BACK = 2; +static constexpr RegMask REG_0x80_TABLE4_FAST = 0x30; +static constexpr RegShift REG_0x80S_TABLE4_FAST = 4; +static constexpr RegMask REG_0x80_TABLE5_GO_HOME = 0xc0; +static constexpr RegShift REG_0x80S_TABLE5_GO_HOME = 6; +  static constexpr RegAddr REG_0x9D = 0x9d;  static constexpr RegShift REG_0x9DS_STEPTIM = 2; diff --git a/backend/genesys/gl846.cpp b/backend/genesys/gl846.cpp index d309d29..cae7414 100644 --- a/backend/genesys/gl846.cpp +++ b/backend/genesys/gl846.cpp @@ -61,38 +61,12 @@ namespace gl846 {  /**   * compute the step multiplier used   */ -static int -gl846_get_step_multiplier (Genesys_Register_Set * regs) +static int gl846_get_step_multiplier (Genesys_Register_Set * regs)  { -    GenesysRegister *r = sanei_genesys_get_address(regs, 0x9d); -    int value = 1; -    if (r != nullptr) { -      value = (r->value & 0x0f)>>1; -      value = 1 << value; -    } -  DBG (DBG_io, "%s: step multiplier is %d\n", __func__, value); -  return value; -} - -/** @brief sensor specific settings -*/ -static void gl846_setup_sensor(Genesys_Device * dev, const Genesys_Sensor& sensor, -                               Genesys_Register_Set* regs) -{ -    DBG_HELPER(dbg); - -    for (const auto& reg : sensor.custom_regs) { -        regs->set8(reg.address, reg.value); -    } - -    regs->set16(REG_EXPR, sensor.exposure.red); -    regs->set16(REG_EXPG, sensor.exposure.green); -    regs->set16(REG_EXPB, sensor.exposure.blue); - -    dev->segment_order = sensor.segment_order; +    unsigned value = (regs->get8(0x9d) & 0x0f) >> 1; +    return 1 << value;  } -  /** @brief set all registers to default values .   * This function is called only once at the beginning and   * fills register startup values for registers reused across scans. @@ -108,23 +82,56 @@ gl846_init_registers (Genesys_Device * dev)      dev->reg.clear();      dev->reg.init_reg(0x01, 0x60); +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) +    { +        dev->reg.init_reg(0x01, 0x22); +    }      dev->reg.init_reg(0x02, 0x38);      dev->reg.init_reg(0x03, 0x03); +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) +    { +        dev->reg.init_reg(0x03, 0xbf); +    }      dev->reg.init_reg(0x04, 0x22);      dev->reg.init_reg(0x05, 0x60); +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) +    { +        dev->reg.init_reg(0x05, 0x48); +    }      dev->reg.init_reg(0x06, 0x10); +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) +    { +        dev->reg.init_reg(0x06, 0xf0); +    }      dev->reg.init_reg(0x08, 0x60); +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) +    { +        dev->reg.init_reg(0x08, 0x00); +    }      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); +    } +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) +    { +        dev->reg.init_reg(0x0b, 0x4a); +    }      dev->reg.init_reg(0x0c, 0x00);      dev->reg.init_reg(0x0d, 0x00); -    dev->reg.init_reg(0x10, 0x00); -    dev->reg.init_reg(0x11, 0x00); -    dev->reg.init_reg(0x12, 0x00); -    dev->reg.init_reg(0x13, 0x00); -    dev->reg.init_reg(0x14, 0x00); -    dev->reg.init_reg(0x15, 0x00); +    dev->reg.init_reg(0x10, 0x00); // exposure, set during sensor setup +    dev->reg.init_reg(0x11, 0x00); // exposure, set during sensor setup +    dev->reg.init_reg(0x12, 0x00); // exposure, set during sensor setup +    dev->reg.init_reg(0x13, 0x00); // exposure, set during sensor setup +    dev->reg.init_reg(0x14, 0x00); // exposure, set during sensor setup +    dev->reg.init_reg(0x15, 0x00); // exposure, set during sensor setup      dev->reg.init_reg(0x16, 0xbb); // SENSOR_DEF      dev->reg.init_reg(0x17, 0x13); // SENSOR_DEF      dev->reg.init_reg(0x18, 0x10); // SENSOR_DEF @@ -133,33 +140,52 @@ gl846_init_registers (Genesys_Device * dev)      dev->reg.init_reg(0x1b, 0x00); // SENSOR_DEF      dev->reg.init_reg(0x1c, 0x20); // SENSOR_DEF      dev->reg.init_reg(0x1d, 0x06); // SENSOR_DEF -    dev->reg.init_reg(0x1e, 0xf0); +    dev->reg.init_reg(0x1e, 0xf0); // WDTIME, LINESEL: set during sensor and motor setup + +     // SCANFED      dev->reg.init_reg(0x1f, 0x01); +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400) { +        dev->reg.init_reg(0x1f, 0x00); +    } +      dev->reg.init_reg(0x20, 0x03); -    dev->reg.init_reg(0x21, 0x10); -    dev->reg.init_reg(0x22, 0x60); -    dev->reg.init_reg(0x23, 0x60); -    dev->reg.init_reg(0x24, 0x60); -    dev->reg.init_reg(0x25, 0x00); -    dev->reg.init_reg(0x26, 0x00); -    dev->reg.init_reg(0x27, 0x00); -    dev->reg.init_reg(0x2c, 0x00); -    dev->reg.init_reg(0x2d, 0x00); -    dev->reg.init_reg(0x2e, 0x80); -    dev->reg.init_reg(0x2f, 0x80); -    dev->reg.init_reg(0x30, 0x00); -    dev->reg.init_reg(0x31, 0x00); -    dev->reg.init_reg(0x32, 0x00); -    dev->reg.init_reg(0x33, 0x00); +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) +    { +        dev->reg.init_reg(0x20, 0x55); +    } +    dev->reg.init_reg(0x21, 0x10); // STEPNO: set during motor setup +    dev->reg.init_reg(0x22, 0x60); // FWDSTEP: set during motor setup +    dev->reg.init_reg(0x23, 0x60); // BWDSTEP: set during motor setup +    dev->reg.init_reg(0x24, 0x60); // FASTNO: set during motor setup +    dev->reg.init_reg(0x25, 0x00); // LINCNT: set during motor setup +    dev->reg.init_reg(0x26, 0x00); // LINCNT: set during motor setup +    dev->reg.init_reg(0x27, 0x00); // LINCNT: set during motor setup +    dev->reg.init_reg(0x2c, 0x00); // DPISET: set during sensor setup +    dev->reg.init_reg(0x2d, 0x00); // DPISET: set during sensor setup +    dev->reg.init_reg(0x2e, 0x80); // BWHI: set during sensor setup +    dev->reg.init_reg(0x2f, 0x80); // BWLOW: set during sensor setup +    dev->reg.init_reg(0x30, 0x00); // STRPIXEL: set during sensor setup +    dev->reg.init_reg(0x31, 0x00); // STRPIXEL: set during sensor setup +    dev->reg.init_reg(0x32, 0x00); // ENDPIXEL: set during sensor setup +    dev->reg.init_reg(0x33, 0x00); // ENDPIXEL: set during sensor setup + +    // DUMMY: the number of CCD dummy pixels      dev->reg.init_reg(0x34, 0x1f); -    dev->reg.init_reg(0x35, 0x00); -    dev->reg.init_reg(0x36, 0x40); -    dev->reg.init_reg(0x37, 0x00); -    dev->reg.init_reg(0x38, 0x2a); -    dev->reg.init_reg(0x39, 0xf8); -    dev->reg.init_reg(0x3d, 0x00); -    dev->reg.init_reg(0x3e, 0x00); -    dev->reg.init_reg(0x3f, 0x01); +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) +    { +        dev->reg.init_reg(0x34, 0x14); +    } + +    dev->reg.init_reg(0x35, 0x00); // MAXWD: set during scan setup +    dev->reg.init_reg(0x36, 0x40); // MAXWD: set during scan setup +    dev->reg.init_reg(0x37, 0x00); // MAXWD: set during scan setup +    dev->reg.init_reg(0x38, 0x2a); // LPERIOD: set during sensor setup +    dev->reg.init_reg(0x39, 0xf8); // LPERIOD: set during sensor setup +    dev->reg.init_reg(0x3d, 0x00); // FEEDL: set during motor setup +    dev->reg.init_reg(0x3e, 0x00); // FEEDL: set during motor setup +    dev->reg.init_reg(0x3f, 0x01); // FEEDL: set during motor setup      dev->reg.init_reg(0x52, 0x02); // SENSOR_DEF      dev->reg.init_reg(0x53, 0x04); // SENSOR_DEF      dev->reg.init_reg(0x54, 0x06); // SENSOR_DEF @@ -169,22 +195,30 @@ gl846_init_registers (Genesys_Device * dev)      dev->reg.init_reg(0x58, 0x59); // SENSOR_DEF      dev->reg.init_reg(0x59, 0x31); // SENSOR_DEF      dev->reg.init_reg(0x5a, 0x40); // SENSOR_DEF + +    // DECSEL, STEPTIM      dev->reg.init_reg(0x5e, 0x1f); -    dev->reg.init_reg(0x5f, 0x01); -    dev->reg.init_reg(0x60, 0x00); -    dev->reg.init_reg(0x61, 0x00); -    dev->reg.init_reg(0x62, 0x00); -    dev->reg.init_reg(0x63, 0x00); -    dev->reg.init_reg(0x64, 0x00); -    dev->reg.init_reg(0x65, 0x00); -    dev->reg.init_reg(0x67, 0x7f); -    dev->reg.init_reg(0x68, 0x7f); -    dev->reg.init_reg(0x69, 0x01); -    dev->reg.init_reg(0x6a, 0x01); -    dev->reg.init_reg(0x70, 0x01); -    dev->reg.init_reg(0x71, 0x00); -    dev->reg.init_reg(0x72, 0x02); -    dev->reg.init_reg(0x73, 0x01); +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) +    { +        dev->reg.init_reg(0x5e, 0x01); +    } +    dev->reg.init_reg(0x5f, 0x01); // FMOVDEC: overwritten during motor setup +    dev->reg.init_reg(0x60, 0x00); // STEPSEL, Z1MOD: overwritten during motor setup +    dev->reg.init_reg(0x61, 0x00); // Z1MOD: overwritten during motor setup +    dev->reg.init_reg(0x62, 0x00); // Z1MOD: overwritten during motor setup +    dev->reg.init_reg(0x63, 0x00); // FSTPSEL, Z2MOD: overwritten during motor setup +    dev->reg.init_reg(0x64, 0x00); // Z2MOD: overwritten during motor setup +    dev->reg.init_reg(0x65, 0x00); // Z2MOD: overwritten during motor setup +    dev->reg.init_reg(0x67, 0x7f); // MTRPWM: overwritten during motor setup +    dev->reg.init_reg(0x68, 0x7f); // FASTPWM: overwritten during motor setup +    dev->reg.init_reg(0x69, 0x01); // FSHDEC: overwritten during motor setup +    dev->reg.init_reg(0x6a, 0x01); // FMOVNO: overwritten during motor setup +    // 0x6b, 0x6c, 0x6d, 0x6e, 0x6f - gpio +    dev->reg.init_reg(0x70, 0x01); // SENSOR_DEF +    dev->reg.init_reg(0x71, 0x00); // SENSOR_DEF +    dev->reg.init_reg(0x72, 0x02); // SENSOR_DEF +    dev->reg.init_reg(0x73, 0x01); // SENSOR_DEF      dev->reg.init_reg(0x74, 0x00); // SENSOR_DEF      dev->reg.init_reg(0x75, 0x00); // SENSOR_DEF      dev->reg.init_reg(0x76, 0x00); // SENSOR_DEF @@ -194,78 +228,80 @@ gl846_init_registers (Genesys_Device * dev)      dev->reg.init_reg(0x7a, 0x00); // SENSOR_DEF      dev->reg.init_reg(0x7b, 0x09); // SENSOR_DEF      dev->reg.init_reg(0x7c, 0x99); // SENSOR_DEF -    dev->reg.init_reg(0x7d, 0x20); +    dev->reg.init_reg(0x7d, 0x20); // SENSOR_DEF      dev->reg.init_reg(0x7f, 0x05); -    dev->reg.init_reg(0x80, 0x4f); -    dev->reg.init_reg(0x87, 0x02); -    dev->reg.init_reg(0x94, 0xff); -    dev->reg.init_reg(0x9d, 0x04); -    dev->reg.init_reg(0x9e, 0x00); -    dev->reg.init_reg(0xa1, 0xe0); -    dev->reg.init_reg(0xa2, 0x1f); -    dev->reg.init_reg(0xab, 0xc0); -    dev->reg.init_reg(0xbb, 0x00); -    dev->reg.init_reg(0xbc, 0x0f); -    dev->reg.init_reg(0xdb, 0xff); -    dev->reg.init_reg(0xfe, 0x08); -    dev->reg.init_reg(0xff, 0x02); -    dev->reg.init_reg(0x98, 0x20); -    dev->reg.init_reg(0x99, 0x00); -    dev->reg.init_reg(0x9a, 0x90); -    dev->reg.init_reg(0x9b, 0x00); -    dev->reg.init_reg(0xf8, 0x05); - -    const auto& sensor = sanei_genesys_find_sensor_any(dev); -    sanei_genesys_set_dpihw(dev->reg, sensor, sensor.optical_res); - -  /* initalize calibration reg */ -  dev->calib_reg = dev->reg; -} +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) +    { +        dev->reg.init_reg(0x7f, 0x00); +    } +    dev->reg.init_reg(0x80, 0x4f); // overwritten during motor setup +    dev->reg.init_reg(0x87, 0x02); // SENSOR_DEF -/**@brief send slope table for motor movement - * Send slope_table in machine byte order - * @param dev device to send slope table - * @param table_nr index of the slope table in ASIC memory - * Must be in the [0-4] range. - * @param slope_table pointer to 16 bit values array of the slope table - * @param steps number of elements in the slope table - */ -static void gl846_send_slope_table(Genesys_Device* dev, int table_nr, -                                   const std::vector<uint16_t>& slope_table, -                                   int steps) -{ -    DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %d", table_nr, steps); -  int i; -  char msg[10000]; +    // MTRPLS: pulse width of ADF motor trigger signal +    dev->reg.init_reg(0x94, 0x00); +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { +        dev->reg.init_reg(0x94, 0xff); +    } +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { +        dev->reg.init_reg(0x98, 0x20); // ONDUR +        dev->reg.init_reg(0x99, 0x00); // ONDUR +        dev->reg.init_reg(0x9a, 0x90); // OFFDUR +        dev->reg.init_reg(0x9b, 0x00); // OFFDUR +    } -  /* sanity check */ -  if(table_nr<0 || table_nr>4) -    { -        throw SaneException("invalid table number %d", table_nr); +    dev->reg.init_reg(0x9d, 0x00); // contains STEPTIM +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { +        dev->reg.init_reg(0x9d, 0x04); +    } +    dev->reg.init_reg(0x9e, 0x00); +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { +        dev->reg.init_reg(0xa1, 0xe0);      } -  std::vector<uint8_t> table(steps * 2); -  for (i = 0; i < steps; i++) +    // RFHSET (SDRAM refresh time) +    dev->reg.init_reg(0xa2, 0x1f); +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I)      { -      table[i * 2] = slope_table[i] & 0xff; -      table[i * 2 + 1] = slope_table[i] >> 8; +        dev->reg.init_reg(0xa2, 0x0f);      } -  if (DBG_LEVEL >= DBG_io) +    // 0xa6, 0xa7 0xa8, 0xa9 - gpio + +    // Various important settings: GPOM9, MULSTOP, NODECEL, TB3TB1, TB5TB2, FIX16CLK +    dev->reg.init_reg(0xab, 0xc0); +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I)      { -        std::sprintf(msg, "write slope %d (%d)=", table_nr, steps); -      for (i = 0; i < steps; i++) -        { -            std::sprintf(msg+strlen(msg), "%d", slope_table[i]); -        } -      DBG (DBG_io, "%s: %s\n", __func__, msg); +        dev->reg.init_reg(0xab, 0x01); +    } +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { +        dev->reg.init_reg(0xbb, 0x00); // FIXME: default is the same +    } +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { +        dev->reg.init_reg(0xbc, 0x0f); +        dev->reg.init_reg(0xdb, 0xff); +    } +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400) { +        dev->reg.init_reg(0xbe, 0x07);      } -    if (dev->interface->is_mock()) { -        dev->interface->record_slope_table(table_nr, slope_table); +    // 0xd0, 0xd1, 0xd2 - SH0DWN, SH1DWN, SH2DWN - shading bank[0..2] for CCD. +    // Set during memory layout setup + +    // [0xe0..0xf7] - image buffer addresses. Set during memory layout setup +    dev->reg.init_reg(0xf8, 0x05); // MAXSEL, MINSEL + +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { +        dev->reg.init_reg(0xfe, 0x08); // MOTTGST, AUTO_O +        dev->reg.init_reg(0xff, 0x02); // AUTO_S      } -    // slope table addresses are fixed -    dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, steps * 2, table.data()); + +    const auto& sensor = sanei_genesys_find_sensor_any(dev); +    const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, +                                                         3, dev->model->default_method); +    sanei_genesys_set_dpihw(dev->reg, dpihw_sensor.register_dpihw);  }  /** @@ -283,11 +319,8 @@ static void gl846_set_adi_fe(Genesys_Device* dev, uint8_t set)          status = scanner_read_status(*dev);      }; -  if (set == AFE_INIT) -    { -        DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, -            static_cast<unsigned>(dev->model->adc_id)); -      dev->frontend = dev->frontend_initial; +    if (set == AFE_INIT) { +        dev->frontend = dev->frontend_initial;      }      // write them to analog frontend @@ -326,115 +359,110 @@ void CommandSetGl846::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor,  // @brief set up motor related register for scan  static void gl846_init_motor_regs_scan(Genesys_Device* dev,                                         const Genesys_Sensor& sensor, +                                       const ScanSession& session,                                         Genesys_Register_Set* reg, -                                       const Motor_Profile& motor_profile, +                                       const MotorProfile& motor_profile,                                         unsigned int scan_exposure_time,                                         unsigned scan_yres,                                         unsigned int scan_lines,                                         unsigned int scan_dummy,                                         unsigned int feed_steps, -                                       MotorFlag flags) +                                       ScanFlag flags)  {      DBG_HELPER_ARGS(dbg, "scan_exposure_time=%d, scan_yres=%d, step_type=%d, scan_lines=%d, "                           "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 */ -    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, ScanFlag::FEEDING)) { +        use_fast_fed = true; +    } +    if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) { +        use_fast_fed = false;      } -  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); -  /* 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; +    if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { +        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, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || (scan_yres>=sensor.full_resolution)) { +        reg02 |= REG_0x02_ACDCDIS;      } -    if (has_flag(flags, MotorFlag::REVERSE)) { -        r->value |= REG_0x02_MTRREV; +    if (has_flag(flags, ScanFlag::REVERSE)) { +        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, -                                                scan_exposure_time, dev->motor.base_ydpi, -                                                step_multiplier, motor_profile); +    // scan and backtracking slope table +    auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, +                                         scan_exposure_time, step_multiplier, motor_profile); -    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); +    scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); +    scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); +    scanner_send_slope_table(dev, sensor, STOP_TABLE, scan_table.table); -  /* fast table */ -  fast_dpi=sanei_genesys_get_lowest_ydpi(dev); +    reg->set8(REG_STEPNO, scan_table.table.size() / step_multiplier); +    reg->set8(REG_FASTNO, scan_table.table.size() / step_multiplier); +    reg->set8(REG_FSHDEC, scan_table.table.size() / step_multiplier); -    // 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; +    // fast table +    const auto* fast_profile = get_motor_profile_ptr(dev->motor.fast_profiles, 0, session); +    if (fast_profile == nullptr) { +        fast_profile = &motor_profile;      } -    Motor_Profile fast_motor_profile = motor_profile; -    fast_motor_profile.step_type = fast_step_type; +    auto fast_table = create_slope_table_fastest(dev->model->asic_type, step_multiplier, +                                                 *fast_profile); -    auto fast_table = sanei_genesys_slope_table(dev->model->asic_type, fast_dpi, -                                                scan_exposure_time, dev->motor.base_ydpi, -                                                step_multiplier, fast_motor_profile); +    scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); +    scanner_send_slope_table(dev, sensor, HOME_TABLE, fast_table.table); -    gl846_send_slope_table(dev, STOP_TABLE, fast_table.table, fast_table.steps_count); -    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); +    reg->set8(REG_FMOVNO, fast_table.table.size() / step_multiplier); +    reg->set8(REG_FMOVDEC, fast_table.table.size() / step_multiplier); -  /* correct move distance by acceleration and deceleration amounts */ -  feedl=feed_steps; -  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; +    if (motor_profile.motor_vref != -1 && fast_profile->motor_vref != 1) { +        std::uint8_t vref = 0; +        vref |= (motor_profile.motor_vref << REG_0x80S_TABLE1_NORMAL) & REG_0x80_TABLE1_NORMAL; +        vref |= (motor_profile.motor_vref << REG_0x80S_TABLE2_BACK) & REG_0x80_TABLE2_BACK; +        vref |= (fast_profile->motor_vref << REG_0x80S_TABLE4_FAST) & REG_0x80_TABLE4_FAST; +        vref |= (fast_profile->motor_vref << REG_0x80S_TABLE5_GO_HOME) & REG_0x80_TABLE5_GO_HOME; +        reg->set8(REG_0x80, vref);      } -  else -    { + +    unsigned feedl = feed_steps; +    unsigned dist = 0; +    if (use_fast_fed) { +        feedl <<= static_cast<unsigned>(fast_profile->step_type); +        dist = (scan_table.table.size() + 2 * fast_table.table.size()); +        // 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 = scan_table.table.size(); +        if (has_flag(flags, ScanFlag::FEEDING)) {              dist *= 2;          }      } -  DBG (DBG_io2, "%s: acceleration distance=%d\n", __func__, dist); -  /* check for overflow */ +    // check for overflow      if (dist < feedl) {          feedl -= dist;      } else { @@ -442,13 +470,9 @@ static void gl846_init_motor_regs_scan(Genesys_Device* dev,      }      reg->set24(REG_FEEDL, 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 */    /* @@ -482,56 +506,31 @@ 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 (scan_yres == sensor.get_register_hwdpi(scan_yres)) { -          val=1; -        } -      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.table.size() / 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, +                                 scan_table.table.size(),                                   feedl,                                   min_restep * step_multiplier,                                   &z1,                                   &z2); -  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);      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 */ - -    r = sanei_genesys_get_address(reg, REG_0x67); -  r->value = 0x7f; +    reg->set8_mask(REG_0x1E, scan_dummy, 0x0f); -    r = sanei_genesys_get_address(reg, REG_0x68); -  r->value = 0x7f; - -    reg->set8(REG_STEPNO, scan_table.steps_count / step_multiplier); -    reg->set8(REG_FASTNO, scan_table.steps_count / step_multiplier); -    reg->set8(REG_FSHDEC, scan_table.steps_count / step_multiplier); -    reg->set8(REG_FMOVNO, fast_table.steps_count / step_multiplier); -    reg->set8(REG_FMOVDEC, fast_table.steps_count / step_multiplier); +    reg->set8(REG_0x67, 0x7f); +    reg->set8(REG_0x68, 0x7f);  } @@ -558,82 +557,69 @@ static void gl846_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens                                           const ScanSession& session)  {      DBG_HELPER_ARGS(dbg, "exposure_time=%d", exposure_time); -    unsigned int dpihw; -  GenesysRegister *r; - -    // resolution is divided according to ccd_pixels_per_system_pixel() -    unsigned ccd_pixels_per_system_pixel = sensor.ccd_pixels_per_system_pixel(); -    DBG(DBG_io2, "%s: ccd_pixels_per_system_pixel=%d\n", __func__, ccd_pixels_per_system_pixel); -    // to manage high resolution device while keeping good low resolution scanning speed, -    // we make hardware dpi vary -    dpihw = sensor.get_register_hwdpi(session.params.xres * ccd_pixels_per_system_pixel); -    DBG(DBG_io2, "%s: dpihw=%d\n", __func__, dpihw); - -    gl846_setup_sensor(dev, sensor, reg); +    scanner_setup_sensor(*dev, sensor, *reg);      dev->cmd_set->set_fe(dev, sensor, AFE_SET);    /* enable shading */      regs_set_optical_off(dev->model->asic_type, *reg); -    r = sanei_genesys_get_address(reg, REG_0x01); -    r->value |= REG_0x01_SHDAREA; +    reg->find_reg(REG_0x01).value |= REG_0x01_SHDAREA;      if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || -        (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)) +        has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || +        session.use_host_side_calib)      { -        r->value &= ~REG_0x01_DVDSET; -    } -  else -    { -        r->value |= REG_0x01_DVDSET; +        reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET; +    } else { +        reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET;      } -    r = sanei_genesys_get_address(reg, REG_0x03); -    r->value &= ~REG_0x03_AVEENB; +    reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB;      sanei_genesys_set_lamp_power(dev, sensor, *reg,                                   !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP)); +    reg->state.is_xpa_on = has_flag(session.params.flags, ScanFlag::USE_XPA); -  /* BW threshold */ -  r = sanei_genesys_get_address (reg, 0x2e); -  r->value = dev->settings.threshold; -  r = sanei_genesys_get_address (reg, 0x2f); -  r->value = dev->settings.threshold; +    // BW threshold +    reg->set8(0x2e, 0x7f); +    reg->set8(0x2f, 0x7f);    /* monochrome / color scan */ -    r = sanei_genesys_get_address(reg, REG_0x04);      switch (session.params.depth) {      case 8: -            r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); +            reg->find_reg(REG_0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET);        break;      case 16: -            r->value &= ~REG_0x04_LINEART; -            r->value |= REG_0x04_BITSET; +            reg->find_reg(REG_0x04).value &= ~REG_0x04_LINEART; +            reg->find_reg(REG_0x04).value |= REG_0x04_BITSET;        break;      } -    r->value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); +    reg->find_reg(REG_0x04).value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD);    if (session.params.channels == 1)      {        switch (session.params.color_filter)          {              case ColorFilter::RED: -                r->value |= 0x24; +                reg->find_reg(REG_0x04).value |= 0x24;                  break;              case ColorFilter::BLUE: -                r->value |= 0x2c; +                reg->find_reg(REG_0x04).value |= 0x2c;                  break;              case ColorFilter::GREEN: -                r->value |= 0x28; +                reg->find_reg(REG_0x04).value |= 0x28;                  break;              default:                  break; // should not happen          }      } else { -        r->value |= 0x20; // mono +        reg->find_reg(REG_0x04).value |= 0x20; // mono      } -    sanei_genesys_set_dpihw(*reg, sensor, dpihw); +    const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, +                                                         session.params.channels, +                                                         session.params.scan_method); +    sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw);      if (should_enable_gamma(session, sensor)) {          reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; @@ -644,38 +630,31 @@ static void gl846_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens    /* CIS scanners can do true gray by setting LEDADD */    /* we set up LEDADD only when asked */      if (dev->model->is_cis) { -      r = sanei_genesys_get_address (reg, 0x87); -        r->value &= ~REG_0x87_LEDADD; +        reg->find_reg(0x87).value &= ~REG_0x87_LEDADD; +          if (session.enable_ledadd) { -            r->value |= REG_0x87_LEDADD; +            reg->find_reg(0x87).value |= REG_0x87_LEDADD;          }        /* RGB weighting -      r = sanei_genesys_get_address (reg, 0x01); -        r->value &= ~REG_0x01_TRUEGRAY; +        reg->find_reg(0x01).value &= ~REG_0x01_TRUEGRAY; +        if (session.enable_ledadd))          { -            r->value |= REG_0x01_TRUEGRAY; +            reg->find_reg(0x01).value |= REG_0x01_TRUEGRAY;          }*/      } -    unsigned dpiset = session.params.xres * ccd_pixels_per_system_pixel; -    reg->set16(REG_DPISET, dpiset); -    DBG(DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset); - +    reg->set16(REG_DPISET, sensor.register_dpiset);      reg->set16(REG_STRPIXEL, session.pixel_startx);      reg->set16(REG_ENDPIXEL, session.pixel_endx); -    build_image_pipeline(dev, session); +    setup_image_pipeline(*dev, session);    /* MAXWD is expressed in 4 words unit */      // BUG: we shouldn't multiply by channels here      reg->set24(REG_MAXWD, (session.output_line_bytes_raw * session.params.channels >> 2)); -      reg->set16(REG_LPERIOD, exposure_time); -  DBG (DBG_io2, "%s: exposure_time used=%d\n", __func__, exposure_time); - -  r = sanei_genesys_get_address (reg, 0x34); -  r->value = sensor.dummy_pixel; +    reg->set8(0x34, sensor.dummy_pixel);  }  void CommandSetGl846::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, @@ -685,13 +664,12 @@ void CommandSetGl846::init_regs_for_scan_session(Genesys_Device* dev, const Gene      DBG_HELPER(dbg);      session.assert_computed(); -  int move;    int exposure_time;    int slope_dpi = 0; -  int dummy = 0; -  dummy = 3-session.params.channels; +    // FIXME: on cis scanners we may want to scan at reduced resolution +    int dummy = 0;  /* slope_dpi */  /* cis color scan is effectively a gray scan with 3 gray lines per color @@ -705,46 +683,18 @@ void CommandSetGl846::init_regs_for_scan_session(Genesys_Device* dev, const Gene    slope_dpi = slope_dpi * (1 + dummy);      exposure_time = sensor.exposure_lperiod; -    const auto& motor_profile = sanei_genesys_get_motor_profile(*gl846_motor_profiles, -                                                                dev->model->motor_id, -                                                                exposure_time); - -  DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time); -    DBG(DBG_info, "%s : scan_step_type=%d\n", __func__, -        static_cast<unsigned>(motor_profile.step_type)); +    const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure_time, session);    /* we enable true gray for cis scanners only, and just when doing     * scan since color calibration is OK for this mode     */      gl846_init_optical_regs_scan(dev, sensor, reg, exposure_time, session); - -/*** motor parameters ***/ - -  /* add tl_y to base movement */ -  move = session.params.starty; -  DBG(DBG_info, "%s: move=%d steps\n", __func__, move); - -    MotorFlag mflags = MotorFlag::NONE; -    if (has_flag(session.params.flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE)) { -        mflags |= MotorFlag::DISABLE_BUFFER_FULL_MOVE; -    } -    if (has_flag(session.params.flags, ScanFlag::FEEDING)) { -        mflags |= MotorFlag::FEED; -    } -    if (has_flag(session.params.flags, ScanFlag::REVERSE)) { -        mflags |= MotorFlag::REVERSE; -    } - -    gl846_init_motor_regs_scan(dev, sensor, reg, motor_profile, exposure_time, slope_dpi, -                               dev->model->is_cis ? session.output_line_count * session.params.channels -                                                  : session.output_line_count, -                               dummy, move, mflags); +    gl846_init_motor_regs_scan(dev, sensor, session, reg, motor_profile, exposure_time, slope_dpi, +                               session.optical_line_count, dummy, session.params.starty, +                               session.params.flags);    /*** prepares data reordering ***/ -    dev->read_buffer.clear(); -    dev->read_buffer.alloc(session.buffer_size_read); -      dev->read_active = true;      dev->session = session; @@ -759,21 +709,50 @@ ScanSession CommandSetGl846::calculate_scan_session(const Genesys_Device* dev,                                                      const Genesys_Sensor& sensor,                                                      const Genesys_Settings& settings) const  { -  int start; -      DBG(DBG_info, "%s ", __func__);      debug_dump(DBG_info, settings); -  /* start */ -    start = static_cast<int>(dev->model->x_offset); -    start += static_cast<int>(settings.tl_x); -    start = static_cast<int>((start * sensor.optical_res) / MM_PER_INCH); +    ScanFlag flags = ScanFlag::NONE; + +    unsigned move_dpi = dev->motor.base_ydpi; + +    float move = dev->model->y_offset; +    if (settings.scan_method == ScanMethod::TRANSPARENCY || +        settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +    { +        // note: scanner_move_to_ta() function has already been called and the sensor is at the +        // transparency adapter +        if (!dev->ignore_offsets) { +            move = dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta; +        } +        flags |= ScanFlag::USE_XPA; +    } else { +        if (!dev->ignore_offsets) { +            move = dev->model->y_offset; +        } +    } + +    move = move + settings.tl_y; +    move = static_cast<float>((move * move_dpi) / MM_PER_INCH); +    move -= dev->head_pos(ScanHeadId::PRIMARY); + +    float start = dev->model->x_offset; +    if (settings.scan_method == ScanMethod::TRANSPARENCY || +        settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +    { +        start = dev->model->x_offset_ta; +    } else { +        start = dev->model->x_offset; +    } + +    start = start + dev->settings.tl_x; +    start = static_cast<float>((start * settings.xres) / MM_PER_INCH);      ScanSession session;      session.params.xres = settings.xres;      session.params.yres = settings.yres; -    session.params.startx = start; // not used -    session.params.starty = 0; // not used +    session.params.startx = static_cast<unsigned>(start); +    session.params.starty = static_cast<unsigned>(move);      session.params.pixels = settings.pixels;      session.params.requested_pixels = settings.requested_pixels;      session.params.lines = settings.lines; @@ -782,7 +761,8 @@ ScanSession CommandSetGl846::calculate_scan_session(const Genesys_Device* dev,      session.params.scan_method = settings.scan_method;      session.params.scan_mode = settings.scan_mode;      session.params.color_filter = settings.color_filter; -    session.params.flags = ScanFlag::NONE; +    // backtracking isn't handled well, so don't enable it +    session.params.flags = flags;      compute_session(dev, session, sensor); @@ -809,24 +789,17 @@ void CommandSetGl846::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sens      DBG_HELPER(dbg);      (void) sensor;    uint8_t val; -  GenesysRegister *r; -  /* XXX STEF XXX SCAN GPIO */ -  /* -    val = dev->interface->read_register(REG_0x6C); -    dev->interface->write_register(REG_0x6C, val); -  */ +    if (reg->state.is_xpa_on && reg->state.is_lamp_on) { +        dev->cmd_set->set_xpa_lamp_power(*dev, true); +    } -    val = REG_0x0D_CLRLNCNT; -    dev->interface->write_register(REG_0x0D, val); -    val = REG_0x0D_CLRMCNT; -    dev->interface->write_register(REG_0x0D, val); +    scanner_clear_scan_and_feed_counts(*dev);      val = dev->interface->read_register(REG_0x01);      val |= REG_0x01_SCAN;      dev->interface->write_register(REG_0x01, val); -    r = sanei_genesys_get_address (reg, REG_0x01); -  r->value = val; +    reg->set8(REG_0x01, val);      scanner_start_action(*dev, start_motor); @@ -841,6 +814,10 @@ void CommandSetGl846::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg,      (void) reg;      DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop); +    if (reg->state.is_xpa_on) { +        dev->cmd_set->set_xpa_lamp_power(*dev, false); +    } +      if (!dev->model->is_sheetfed) {          scanner_stop_action(*dev);      } @@ -852,260 +829,72 @@ void CommandSetGl846::move_back_home(Genesys_Device* dev, bool wait_until_home)      scanner_move_back_home(*dev, wait_until_home);  } -// Automatically set top-left edge of the scan area by scanning a 200x200 pixels area at 600 dpi -// from very top of scanner -void CommandSetGl846::search_start_position(Genesys_Device* dev) const -{ -    DBG_HELPER(dbg); -  int size; -  Genesys_Register_Set local_reg; - -  int pixels = 600; -  int dpi = 300; - -  local_reg = dev->reg; - -  /* sets for a 200 lines * 600 pixels */ -  /* normal scan with no shading */ - -    // FIXME: the current approach of doing search only for one resolution does not work on scanners -    // whith employ different sensors with potentially different settings. -    const auto& sensor = sanei_genesys_find_sensor(dev, dpi, 1, dev->model->default_method); - -    ScanSession session; -    session.params.xres = dpi; -    session.params.yres =  dpi; -    session.params.startx = 0; -    session.params.starty =  0;	/*we should give a small offset here~60 steps */ -    session.params.pixels = 600; -    session.params.lines = dev->model->search_lines; -    session.params.depth = 8; -    session.params.channels = 1; -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = ScanColorMode::GRAY; -    session.params.color_filter = ColorFilter::GREEN; -    session.params.flags = ScanFlag::DISABLE_SHADING | -                           ScanFlag::DISABLE_GAMMA | -                           ScanFlag::IGNORE_LINE_DISTANCE; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, &local_reg, session); - -    // send to scanner -    dev->interface->write_registers(local_reg); - -  size = pixels * dev->model->search_lines; - -  std::vector<uint8_t> data(size); - -    begin_scan(dev, sensor, &local_reg, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("search_start_position"); -        end_scan(dev, &local_reg, true); -        dev->reg = local_reg; -        return; -    } - -    wait_until_buffer_non_empty(dev); - -    // now we're on target, we can read data -    sanei_genesys_read_data_from_scanner(dev, data.data(), size); - -    if (DBG_LEVEL >= DBG_data) { -        sanei_genesys_write_pnm_file("gl846_search_position.pnm", data.data(), 8, 1, pixels, -                                     dev->model->search_lines); -    } - -    end_scan(dev, &local_reg, true); - -  /* update regs to copy ASIC internal state */ -  dev->reg = local_reg; - -    // TODO: find out where sanei_genesys_search_reference_point stores information, -    // and use that correctly -    for (auto& sensor_update : -            sanei_genesys_find_sensors_all_for_write(dev, dev->model->default_method)) -    { -        sanei_genesys_search_reference_point(dev, sensor_update, data.data(), 0, dpi, pixels, -                                             dev->model->search_lines); -    } -} - -// sets up register for coarse gain calibration -// todo: check it for scanners using it -void CommandSetGl846::init_regs_for_coarse_calibration(Genesys_Device* dev, -                                                       const Genesys_Sensor& sensor, -                                                       Genesys_Register_Set& regs) const -{ -    DBG_HELPER(dbg); - -    ScanSession session; -    session.params.xres = dev->settings.xres; -    session.params.yres = dev->settings.yres; -    session.params.startx = 0; -    session.params.starty = 0; -    session.params.pixels = sensor.optical_res / sensor.ccd_pixels_per_system_pixel(); -    session.params.lines = 20; -    session.params.depth = 16; -    session.params.channels = dev->settings.get_channels(); -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = dev->settings.scan_mode; -    session.params.color_filter = dev->settings.color_filter; -    session.params.flags = ScanFlag::DISABLE_SHADING | -                           ScanFlag::DISABLE_GAMMA | -                           ScanFlag::SINGLE_LINE | -                           ScanFlag::IGNORE_LINE_DISTANCE; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, ®s, session); - -    DBG(DBG_info, "%s: optical sensor res: %d dpi, actual res: %d\n", __func__, -        sensor.optical_res / sensor.ccd_pixels_per_system_pixel(), dev->settings.xres); - -    dev->interface->write_registers(regs); -} -  // init registers for shading calibration  void CommandSetGl846::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,                                              Genesys_Register_Set& regs) const  {      DBG_HELPER(dbg); -  float move; -  dev->calib_channels = 3; +    unsigned move_dpi = dev->motor.base_ydpi; -  /* initial calibration reg values */ -  regs = dev->reg; +    float calib_size_mm = 0; +    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || +        dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +    { +        calib_size_mm = dev->model->y_size_calib_ta_mm; +    } else { +        calib_size_mm = dev->model->y_size_calib_mm; +    } -    dev->calib_resolution = sensor.get_register_hwdpi(dev->settings.xres); +    unsigned channels = 3; +    unsigned resolution = sensor.shading_resolution; -    const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->calib_resolution, -                                                         dev->calib_channels, +    const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,                                                           dev->settings.scan_method); -  dev->calib_total_bytes_to_read = 0; -  dev->calib_lines = dev->model->shading_lines; -    if (dev->calib_resolution==4800) { -        dev->calib_lines *= 2; -    } -    dev->calib_pixels = (calib_sensor.sensor_pixels * dev->calib_resolution) / -                        calib_sensor.optical_res; -    DBG(DBG_io, "%s: calib_lines  = %zu\n", __func__, dev->calib_lines); -    DBG(DBG_io, "%s: calib_pixels = %zu\n", __func__, dev->calib_pixels); +    float move = 0; +    ScanFlag flags = ScanFlag::DISABLE_SHADING | +                     ScanFlag::DISABLE_GAMMA | +                     ScanFlag::DISABLE_BUFFER_FULL_MOVE; -  /* this is aworkaround insufficent distance for slope -   * motor acceleration TODO special motor slope for shading  */ -  move=1; -  if(dev->calib_resolution<1200) +    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || +        dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)      { -      move=40; +        // note: scanner_move_to_ta() function has already been called and the sensor is at the +        // transparency adapter +        move = static_cast<int>(dev->model->y_offset_calib_white_ta - dev->model->y_offset_sensor_to_ta); +        flags |= ScanFlag::USE_XPA; +    } else { +        move = static_cast<int>(dev->model->y_offset_calib_white);      } +    move = static_cast<float>((move * move_dpi) / MM_PER_INCH); + +    unsigned calib_lines = static_cast<unsigned>(calib_size_mm * resolution / MM_PER_INCH); +      ScanSession session; -    session.params.xres = dev->calib_resolution; -    session.params.yres = dev->calib_resolution; +    session.params.xres = resolution; +    session.params.yres = resolution;      session.params.startx = 0;      session.params.starty = static_cast<unsigned>(move); -    session.params.pixels = dev->calib_pixels; -    session.params.lines = dev->calib_lines; +    session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; +    session.params.lines = calib_lines;      session.params.depth = 16; -    session.params.channels = dev->calib_channels; +    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::DISABLE_BUFFER_FULL_MOVE | -                   ScanFlag::IGNORE_LINE_DISTANCE; +    session.params.flags = flags;      compute_session(dev, session, calib_sensor);      init_regs_for_scan_session(dev, calib_sensor, ®s, session); -    dev->interface->write_registers(regs); - -  /* we use GENESYS_FLAG_SHADING_REPARK */ +  /* we use ModelFlag::SHADING_REPARK */      dev->set_head_pos_zero(ScanHeadId::PRIMARY); -} - -/** @brief set up registers for the actual scan - */ -void CommandSetGl846::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const -{ -    DBG_HELPER(dbg); -  float move; -  int move_dpi; -  float start; - -    debug_dump(DBG_info, dev->settings); -  /* steps to move to reach scanning area: -     - first we move to physical start of scanning -     either by a fixed steps amount from the black strip -     or by a fixed amount from parking position, -     minus the steps done during shading calibration -     - then we move by the needed offset whitin physical -     scanning area - -     assumption: steps are expressed at maximum motor resolution - -     we need: -     float y_offset; -     float y_size; -     float y_offset_calib; -     mm_to_steps()=motor dpi / 2.54 / 10=motor dpi / MM_PER_INCH */ - -  /* if scanner uses GENESYS_FLAG_SEARCH_START y_offset is -     relative from origin, else, it is from parking position */ - -  move_dpi = dev->motor.base_ydpi; - -    move = static_cast<float>(dev->model->y_offset); -    move = static_cast<float>(move + dev->settings.tl_y); -    move = static_cast<float>((move * move_dpi) / MM_PER_INCH); -    move -= dev->head_pos(ScanHeadId::PRIMARY); -  DBG(DBG_info, "%s: move=%f steps\n", __func__, move); - -  /* fast move to scan area */ -  /* we don't move fast the whole distance since it would involve -   * computing acceleration/deceleration distance for scan -   * resolution. So leave a remainder for it so scan makes the final -   * move tuning */ -    if (dev->settings.get_channels() * dev->settings.yres >= 600 && move > 700) { -        scanner_move(*dev, dev->model->default_method, static_cast<unsigned>(move - 500), -                     Direction::FORWARD); -      move=500; -    } - -  DBG(DBG_info, "%s: move=%f steps\n", __func__, move); -  DBG(DBG_info, "%s: move=%f steps\n", __func__, move); - -  /* start */ -    start = static_cast<float>(dev->model->x_offset); -    start = static_cast<float>(start + dev->settings.tl_x); -    start = static_cast<float>((start * sensor.optical_res) / MM_PER_INCH); - -    ScanSession session; -    session.params.xres = dev->settings.xres; -    session.params.yres = dev->settings.yres; -    session.params.startx = static_cast<unsigned>(start); -    session.params.starty = static_cast<unsigned>(move); -    session.params.pixels = dev->settings.pixels; -    session.params.requested_pixels = dev->settings.requested_pixels; -    session.params.lines = dev->settings.lines; -    session.params.depth = dev->settings.depth; -    session.params.channels = dev->settings.get_channels(); -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = dev->settings.scan_mode; -    session.params.color_filter = dev->settings.color_filter; -    // backtracking isn't handled well, so don't enable it -    session.params.flags = ScanFlag::DISABLE_BUFFER_FULL_MOVE; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, &dev->reg, session); +    dev->calib_session = session;  } -  /**   * Send shading calibration data. The buffer is considered to always hold values   * for all the channels. @@ -1114,39 +903,24 @@ void CommandSetGl846::send_shading_data(Genesys_Device* dev, const Genesys_Senso                                          uint8_t* data, int size) const  {      DBG_HELPER_ARGS(dbg, "writing %d bytes of shading data", size); -  uint32_t addr, length, i, x, factor, pixels; -    uint32_t dpiset, dpihw; +    std::uint32_t addr, i;    uint8_t val,*ptr,*src; -  /* shading data is plit in 3 (up to 5 with IR) areas -     write(0x10014000,0x00000dd8) -     URB 23429  bulk_out len  3544  wrote 0x33 0x10 0x.... -     write(0x1003e000,0x00000dd8) -     write(0x10068000,0x00000dd8) -   */ -    length = static_cast<uint32_t>(size / 3); -    unsigned strpixel = dev->session.pixel_startx; -    unsigned endpixel = dev->session.pixel_endx; - -  /* compute deletion factor */ -    dpiset = dev->reg.get16(REG_DPISET); -    dpihw = sensor.get_register_hwdpi(dpiset); -  factor=dpihw/dpiset; -  DBG(DBG_io2, "%s: factor=%d\n", __func__, factor); - -  pixels=endpixel-strpixel; +    unsigned length = static_cast<unsigned>(size / 3); -  /* since we're using SHDAREA, substract startx coordinate from shading */ -    strpixel -= (sensor.ccd_start_xoffset * 600) / sensor.optical_res; +    // we're using SHDAREA, thus we only need to upload part of the line +    unsigned offset = dev->session.pixel_count_ratio.apply( +                dev->session.params.startx * sensor.full_resolution / dev->session.params.xres); +    unsigned pixels = dev->session.pixel_count_ratio.apply(dev->session.optical_pixels_raw); -  /* turn pixel value into bytes 2x16 bits words */ -  strpixel*=2*2; -  pixels*=2*2; +    // turn pixel value into bytes 2x16 bits words +    offset *= 2 * 2; +    pixels *= 2 * 2; -    dev->interface->record_key_value("shading_offset", std::to_string(strpixel)); +    dev->interface->record_key_value("shading_offset", std::to_string(offset));      dev->interface->record_key_value("shading_pixels", std::to_string(pixels));      dev->interface->record_key_value("shading_length", std::to_string(length)); -    dev->interface->record_key_value("shading_factor", std::to_string(factor)); +    dev->interface->record_key_value("shading_factor", std::to_string(sensor.shading_factor));    std::vector<uint8_t> buffer(pixels, 0); @@ -1163,10 +937,9 @@ void CommandSetGl846::send_shading_data(Genesys_Device* dev, const Genesys_Senso        ptr = buffer.data();        /* iterate on both sensor segment */ -      for(x=0;x<pixels;x+=4*factor) -        { -          /* coefficient source */ -          src=(data+strpixel+i*length)+x; +        for (unsigned x = 0; x < pixels; x += 4 * sensor.shading_factor) { +          // coefficient source +          src = (data + offset + i * length) + x;            /* coefficient copy */            ptr[0]=src[0]; @@ -1192,166 +965,7 @@ void CommandSetGl846::send_shading_data(Genesys_Device* dev, const Genesys_Senso  SensorExposure CommandSetGl846::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,                                                  Genesys_Register_Set& regs) const  { -    DBG_HELPER(dbg); -  int num_pixels; -  int total_size; -  int used_res; -  int i, j; -  int val; -    int channels; -  int avg[3], top[3], bottom[3]; -  int turn; -  uint16_t exp[3]; - -    float move = static_cast<float>(dev->model->y_offset_calib_white); -     move = static_cast<float>((move * (dev->motor.base_ydpi / 4)) / MM_PER_INCH); -  if(move>20) -    { -        scanner_move(*dev, dev->model->default_method, static_cast<unsigned>(move), -                     Direction::FORWARD); -    } -  DBG(DBG_io, "%s: move=%f steps\n", __func__, move); - -  /* offset calibration is always done in color mode */ -  channels = 3; -    used_res = sensor.get_register_hwdpi(dev->settings.xres); -    const auto& calib_sensor = sanei_genesys_find_sensor(dev, used_res, channels, -                                                         dev->settings.scan_method); -    num_pixels = (calib_sensor.sensor_pixels * used_res) / calib_sensor.optical_res; - -  /* initial calibration reg values */ -  regs = dev->reg; - -    ScanSession session; -    session.params.xres = used_res; -    session.params.yres = used_res; -    session.params.startx = 0; -    session.params.starty = 0; -    session.params.pixels = num_pixels; -    session.params.lines = 1; -    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_LINE_DISTANCE; -    compute_session(dev, session, calib_sensor); - -    init_regs_for_scan_session(dev, calib_sensor, ®s, session); - -    total_size = num_pixels * channels * (session.params.depth / 8) * 1; -  std::vector<uint8_t> line(total_size); - -  /* initial loop values and boundaries */ -    exp[0] = calib_sensor.exposure.red; -    exp[1] = calib_sensor.exposure.green; -    exp[2] = calib_sensor.exposure.blue; - -  bottom[0]=29000; -  bottom[1]=29000; -  bottom[2]=29000; - -  top[0]=41000; -  top[1]=51000; -  top[2]=51000; - -  turn = 0; - -  /* no move during led calibration */ -  sanei_genesys_set_motor_power(regs, false); -    bool acceptable = false; -  do -    { -        // set up exposure -        regs.set16(REG_EXPR, exp[0]); -        regs.set16(REG_EXPG, exp[1]); -        regs.set16(REG_EXPB, exp[2]); - -        // write registers and scan data -        dev->interface->write_registers(regs); - -      DBG(DBG_info, "%s: starting line reading\n", __func__); -        begin_scan(dev, calib_sensor, ®s, true); - -        if (is_testing_mode()) { -            dev->interface->test_checkpoint("led_calibration"); -            scanner_stop_action(*dev); -            move_back_home(dev, true); -            return calib_sensor.exposure; -        } - -        sanei_genesys_read_data_from_scanner(dev, line.data(), total_size); - -        // stop scanning -        scanner_stop_action(*dev); - -      if (DBG_LEVEL >= DBG_data) -        { -          char fn[30]; -            std::snprintf(fn, 30, "gl846_led_%02d.pnm", turn); -            sanei_genesys_write_pnm_file(fn, line.data(), session.params.depth, -                                         channels, num_pixels, 1); -        } - -      /* compute average */ -      for (j = 0; j < channels; j++) -        { -          avg[j] = 0; -          for (i = 0; i < num_pixels; i++) -            { -              if (dev->model->is_cis) -                val = -                  line[i * 2 + j * 2 * num_pixels + 1] * 256 + -                  line[i * 2 + j * 2 * num_pixels]; -              else -                val = -                  line[i * 2 * channels + 2 * j + 1] * 256 + -                  line[i * 2 * channels + 2 * j]; -              avg[j] += val; -            } - -          avg[j] /= num_pixels; -        } - -      DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]); - -      /* check if exposure gives average within the boundaries */ -        acceptable = true; -      for(i=0;i<3;i++) -        { -          if(avg[i]<bottom[i]) -            { -              exp[i]=(exp[i]*bottom[i])/avg[i]; -                acceptable = false; -            } -          if(avg[i]>top[i]) -            { -              exp[i]=(exp[i]*top[i])/avg[i]; -                acceptable = false; -            } -        } - -      turn++; -    } -  while (!acceptable && turn < 100); - -  DBG(DBG_info, "%s: acceptable exposure: %d,%d,%d\n", __func__, exp[0], exp[1], exp[2]); - -    // set these values as final ones for scan -    dev->reg.set16(REG_EXPR, exp[0]); -    dev->reg.set16(REG_EXPG, exp[1]); -    dev->reg.set16(REG_EXPB, exp[2]); - -  /* go back home */ -  if(move>20) -    { -        move_back_home(dev, true); -    } - -    return { exp[0], exp[1], exp[2] }; +    return scanner_led_calibration(*dev, sensor, regs);  }  /** @@ -1360,29 +974,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); +    });  }  /** @@ -1391,32 +986,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); -    } -  /* 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); -  /* 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);  }  /* * @@ -1433,15 +1007,14 @@ void CommandSetGl846::asic_boot(Genesys_Device* dev, bool cold) const          dev->interface->write_register(0x0e, 0x00);      } -  if(dev->usb_mode == 1) -    { -      val = 0x14; -    } -  else -    { -      val = 0x11; +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICBOOK_3800) { +        if (dev->usb_mode == 1) { +            val = 0x14; +        } else { +            val = 0x11; +        } +        dev->interface->write_0x8c(0x0f, val);      } -    dev->interface->write_0x8c(0x0f, val);      // test CHKVER      val = dev->interface->read_register(REG_0x40); @@ -1450,18 +1023,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)      { @@ -1470,8 +1036,15 @@ void CommandSetGl846::asic_boot(Genesys_Device* dev, bool cold) const      }      // set up clocks -    dev->interface->write_0x8c(0x10, 0x0e); -    dev->interface->write_0x8c(0x13, 0x0e); +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) +    { +        dev->interface->write_0x8c(0x10, 0x0c); +        dev->interface->write_0x8c(0x13, 0x0c); +    } else { +        dev->interface->write_0x8c(0x10, 0x0e); +        dev->interface->write_0x8c(0x13, 0x0e); +    }      // setup gpio      gl846_init_gpio(dev); @@ -1492,7 +1065,7 @@ void CommandSetGl846::init(Genesys_Device* dev) const    DBG_INIT ();      DBG_HELPER(dbg); -    sanei_genesys_asic_init(dev, 0); +    sanei_genesys_asic_init(dev);  }  void CommandSetGl846::update_hardware_sensors(Genesys_Scanner* s) const @@ -1529,512 +1102,16 @@ void CommandSetGl846::update_home_sensor_gpio(Genesys_Device& dev) const      dev.interface->write_register(REG_0x6C, val);  } -/** @brief search for a full width black or white strip. - * This function searches for a black or white stripe across the scanning area. - * When searching backward, the searched area must completely be of the desired - * color since this area will be used for calibration which scans forward. - * @param dev scanner device - * @param forward true if searching forward, false if searching backward - * @param black true if searching for a black strip, false for a white strip - */ -void CommandSetGl846::search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, bool forward, -                                   bool black) const -{ -    DBG_HELPER_ARGS(dbg, "%s %s", black ? "black" : "white", forward ? "forward" : "reverse"); -  unsigned int pixels, lines, channels; -  Genesys_Register_Set local_reg; -  size_t size; -  unsigned int pass, count, found, x, y; -  char title[80]; - -    set_fe(dev, sensor, AFE_SET); - -    scanner_stop_action(*dev); - -    // set up for a gray scan at lowest dpi -    const auto& resolution_settings = dev->model->get_resolution_settings(dev->settings.scan_method); -    unsigned dpi = resolution_settings.get_min_resolution_x(); -  channels = 1; -  /* 10 MM */ -  /* lines = (10 * dpi) / MM_PER_INCH; */ -  /* shading calibation is done with dev->motor.base_ydpi */ -  lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi; -  pixels = (sensor.sensor_pixels * dpi) / sensor.optical_res; - -    dev->set_head_pos_zero(ScanHeadId::PRIMARY); - -  local_reg = dev->reg; - -    ScanSession session; -    session.params.xres = dpi; -    session.params.yres = dpi; -    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_mode = ScanColorMode::GRAY; -    session.params.color_filter = ColorFilter::RED; -    session.params.flags = ScanFlag::DISABLE_SHADING | -                           ScanFlag::DISABLE_GAMMA; -    if (!forward) { -        session.params.flags |= ScanFlag::REVERSE; -    } -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, &local_reg, session); - -    size = pixels * channels * lines * (session.params.depth / 8); -    std::vector<uint8_t> data(size); - -    dev->interface->write_registers(local_reg); - -    begin_scan(dev, sensor, &local_reg, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("search_strip"); -        scanner_stop_action(*dev); -        return; -    } - -    wait_until_buffer_non_empty(dev); - -    // now we're on target, we can read data -    sanei_genesys_read_data_from_scanner(dev, data.data(), size); - -    scanner_stop_action(*dev); - -  pass = 0; -  if (DBG_LEVEL >= DBG_data) -    { -        std::sprintf(title, "gl846_search_strip_%s_%s%02d.pnm", -                     black ? "black" : "white", forward ? "fwd" : "bwd", pass); -        sanei_genesys_write_pnm_file(title, data.data(), session.params.depth, -                                     channels, pixels, lines); -    } - -  /* loop until strip is found or maximum pass number done */ -  found = 0; -  while (pass < 20 && !found) -    { -        dev->interface->write_registers(local_reg); - -        // now start scan -        begin_scan(dev, sensor, &local_reg, true); - -        wait_until_buffer_non_empty(dev); - -        // now we're on target, we can read data -        sanei_genesys_read_data_from_scanner(dev, data.data(), size); - -        scanner_stop_action(*dev); - -      if (DBG_LEVEL >= DBG_data) -        { -            std::sprintf(title, "gl846_search_strip_%s_%s%02d.pnm", -                         black ? "black" : "white", forward ? "fwd" : "bwd", pass); -            sanei_genesys_write_pnm_file(title, data.data(), session.params.depth, -                                         channels, pixels, lines); -        } - -      /* search data to find black strip */ -      /* when searching forward, we only need one line of the searched color since we -       * will scan forward. But when doing backward search, we need all the area of the -       * same color */ -      if (forward) -        { -          for (y = 0; y < lines && !found; y++) -            { -              count = 0; -              /* count of white/black pixels depending on the color searched */ -              for (x = 0; x < pixels; x++) -                { -                  /* when searching for black, detect white pixels */ -                  if (black && data[y * pixels + x] > 90) -                    { -                      count++; -                    } -                  /* when searching for white, detect black pixels */ -                  if (!black && data[y * pixels + x] < 60) -                    { -                      count++; -                    } -                } - -              /* at end of line, if count >= 3%, line is not fully of the desired color -               * so we must go to next line of the buffer */ -              /* count*100/pixels < 3 */ -              if ((count * 100) / pixels < 3) -                { -                  found = 1; -                  DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__, -                      pass, y); -                } -              else -                { -                  DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count, -                      (100 * count) / pixels); -                } -            } -        } -      else			/* since calibration scans are done forward, we need the whole area -                                   to be of the required color when searching backward */ -        { -          count = 0; -          for (y = 0; y < lines; y++) -            { -              /* count of white/black pixels depending on the color searched */ -              for (x = 0; x < pixels; x++) -                { -                  /* when searching for black, detect white pixels */ -                  if (black && data[y * pixels + x] > 90) -                    { -                      count++; -                    } -                  /* when searching for white, detect black pixels */ -                  if (!black && data[y * pixels + x] < 60) -                    { -                      count++; -                    } -                } -            } - -          /* at end of area, if count >= 3%, area is not fully of the desired color -           * so we must go to next buffer */ -          if ((count * 100) / (pixels * lines) < 3) -            { -              found = 1; -              DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass); -            } -          else -            { -              DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count, -                  (100 * count) / pixels); -            } -        } -      pass++; -    } - -  if (found) -    { -      DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white"); -    } -  else -    { -        throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found", black ? "black" : "white"); -    } -} - -/** - * 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, total_size; -    int topavg, bottomavg, lines; -  int top, bottom, black_pixels, 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; -  dev->calib_pixels = sensor.sensor_pixels; -  lines=1; -    pixels = (sensor.sensor_pixels * sensor.optical_res) / sensor.optical_res; -    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_LINE_DISTANCE; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, ®s, session); - -  sanei_genesys_set_motor_power(regs, false); - -    total_size = pixels * channels * lines * (session.params.depth / 8); - -  std::vector<uint8_t> first_line(total_size); -  std::vector<uint8_t> second_line(total_size); - -  /* 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, ®s, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("offset_calibration"); -        return; -    } - -    sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size); -  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.data(), session.params.depth, -                                     channels, pixels, lines); -   } - -  bottomavg = dark_average(first_line.data(), 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, ®s, true); -    sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size); - -  topavg = dark_average(second_line.data(), 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, ®s, true); -        sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size); - -      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.data(), session.params.depth, -                                         channels, pixels, lines); -        } - -      avg = dark_average(second_line.data(), 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); -  int pixels; -  int total_size; -  int i, j, channels; -  int max[3]; -  float gain[3],coeff; -  int val, 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 */ -  channels = 3; - -  /* follow CKSEL */ -  if(dev->settings.xres<sensor.optical_res) -    { -        coeff = 0.9f; -    } -  else -    { -      coeff=1.0; -    } -  lines=10; -    pixels = (sensor.sensor_pixels * sensor.optical_res) / sensor.optical_res; - -    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_LINE_DISTANCE; -    compute_session(dev, session, sensor); - -    try { -        init_regs_for_scan_session(dev, sensor, ®s, 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); - -    total_size = pixels * channels * (16 / session.params.depth) * lines; - -  std::vector<uint8_t> line(total_size); - -    set_fe(dev, sensor, AFE_SET); -    begin_scan(dev, sensor, ®s, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("coarse_gain_calibration"); -        scanner_stop_action(*dev); -        move_back_home(dev, true); -        return; -    } - -    sanei_genesys_read_data_from_scanner(dev, line.data(), total_size); - -    if (DBG_LEVEL >= DBG_data) { -        sanei_genesys_write_pnm_file("gl846_gain.pnm", line.data(), session.params.depth, -                                     channels, pixels, lines); -    } - -  /* average value on each channel */ -  for (j = 0; j < channels; j++) -    { -      max[j] = 0; -      for (i = pixels/4; i < (pixels*3/4); i++) -        { -          if (dev->model->is_cis) -            val = line[i + j * pixels]; -          else -            val = line[i * channels + j]; - -          max[j] += val; -        } -      max[j] = max[j] / (pixels/2); - -        gain[j] = (static_cast<float>(sensor.gain_white_ref) * coeff) / max[j]; - -      /* turn logical gain value into gain code, checking for overflow */ -        code = static_cast<int>(283 - 208 / gain[j]); -      if (code > 255) -        code = 255; -      else if (code < 0) -        code = 0; -      dev->frontend.set_gain(j, code); - -      DBG(DBG_proc, "%s: channel %d, max=%d, gain = %f, setting:%d\n", __func__, j, max[j], gain[j], -          dev->frontend.get_gain(j)); -    } - -    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 @@ -2044,14 +1121,11 @@ bool CommandSetGl846::needs_home_before_init_regs_for_scan(Genesys_Device* dev)  }  void CommandSetGl846::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                           Genesys_Register_Set* regs, int* channels, -                                           int* total_size) const +                                           Genesys_Register_Set* regs) const  {      (void) dev;      (void) sensor;      (void) regs; -    (void) channels; -    (void) total_size;      throw SaneException("not implemented");  } @@ -2083,16 +1157,5 @@ void CommandSetGl846::eject_document(Genesys_Device* dev) const      throw SaneException("not implemented");  } -void CommandSetGl846::move_to_ta(Genesys_Device* dev) const -{ -    (void) dev; -    throw SaneException("not implemented"); -} - -std::unique_ptr<CommandSet> create_gl846_cmd_set() -{ -    return std::unique_ptr<CommandSet>(new CommandSetGl846{}); -} -  } // namespace gl846  } // namespace genesys diff --git a/backend/genesys/gl846.h b/backend/genesys/gl846.h index 258015a..f794a01 100644 --- a/backend/genesys/gl846.h +++ b/backend/genesys/gl846.h @@ -42,7 +42,7 @@  */  #include "genesys.h" -#include "command_set.h" +#include "command_set_common.h"  #ifndef BACKEND_GENESYS_GL846_H  #define BACKEND_GENESYS_GL846_H @@ -50,82 +50,7 @@  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 CommandSet +class CommandSetGl846 : public CommandSetCommon  {  public:      ~CommandSetGl846() override = default; @@ -135,17 +60,11 @@ public:      void init(Genesys_Device* dev) const override;      void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, -                              Genesys_Register_Set* regs, int* channels, -                              int* total_size) const override; - -    void init_regs_for_coarse_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                          Genesys_Register_Set& regs) const override; +                              Genesys_Register_Set* regs) const override;      void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,                                 Genesys_Register_Set& regs) const override; -    void init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; -      void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,                                      Genesys_Register_Set* reg,                                      const ScanSession& session) const override; @@ -161,8 +80,6 @@ public:      void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; -    void search_start_position(Genesys_Device* dev) const override; -      void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,                              Genesys_Register_Set& regs) const override; @@ -178,8 +95,6 @@ public:      void update_hardware_sensors(struct Genesys_Scanner* s) const override; -    bool needs_update_home_sensor_gpio() const override { return true; } -      void update_home_sensor_gpio(Genesys_Device& dev) const override;      void load_document(Genesys_Device* dev) const override; @@ -188,11 +103,6 @@ public:      void eject_document(Genesys_Device* dev) const override; -    void search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, -                      bool forward, bool black) const override; - -    void move_to_ta(Genesys_Device* dev) const override; -      void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t* data,                             int size) const override; diff --git a/backend/genesys/gl846_registers.h b/backend/genesys/gl846_registers.h index 39b3029..e4a8ac5 100644 --- a/backend/genesys/gl846_registers.h +++ b/backend/genesys/gl846_registers.h @@ -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; @@ -303,6 +303,16 @@ static constexpr RegAddr REG_0x6E = 0x6e;  static constexpr RegAddr REG_0x6F = 0x6f;  static constexpr RegAddr REG_0x7E = 0x7e; +static constexpr RegAddr REG_0x80 = 0x80; +static constexpr RegMask REG_0x80_TABLE1_NORMAL = 0x03; +static constexpr RegShift REG_0x80S_TABLE1_NORMAL = 0; +static constexpr RegMask REG_0x80_TABLE2_BACK = 0x0c; +static constexpr RegShift REG_0x80S_TABLE2_BACK = 2; +static constexpr RegMask REG_0x80_TABLE4_FAST = 0x30; +static constexpr RegShift REG_0x80S_TABLE4_FAST = 4; +static constexpr RegMask REG_0x80_TABLE5_GO_HOME = 0xc0; +static constexpr RegShift REG_0x80S_TABLE5_GO_HOME = 6; +  static constexpr RegMask REG_0x87_ACYCNRLC = 0x10;  static constexpr RegMask REG_0x87_ENOFFSET = 0x08;  static constexpr RegMask REG_0x87_LEDADD = 0x04; diff --git a/backend/genesys/gl847.cpp b/backend/genesys/gl847.cpp index cb0b527..f8f6b1c 100644 --- a/backend/genesys/gl847.cpp +++ b/backend/genesys/gl847.cpp @@ -56,39 +56,12 @@ namespace gl847 {  /**   * compute the step multiplier used   */ -static int -gl847_get_step_multiplier (Genesys_Register_Set * regs) +static unsigned gl847_get_step_multiplier (Genesys_Register_Set * regs)  { -    GenesysRegister *r = sanei_genesys_get_address(regs, 0x9d); -    int value = 1; -    if (r != nullptr) -    { -      value = (r->value & 0x0f)>>1; -      value = 1 << value; -    } -  DBG (DBG_io, "%s: step multiplier is %d\n", __func__, value); -  return value; +    unsigned value = (regs->get8(0x9d) & 0x0f) >> 1; +    return 1 << value;  } -/** @brief sensor specific settings -*/ -static void gl847_setup_sensor(Genesys_Device * dev, const Genesys_Sensor& sensor, -                               Genesys_Register_Set* regs) -{ -    DBG_HELPER(dbg); - -    for (const auto& reg : sensor.custom_regs) { -        regs->set8(reg.address, reg.value); -    } - -    regs->set16(REG_EXPR, sensor.exposure.red); -    regs->set16(REG_EXPG, sensor.exposure.green); -    regs->set16(REG_EXPB, sensor.exposure.blue); - -    dev->segment_order = sensor.segment_order; -} - -  /** @brief set all registers to default values .   * This function is called only once at the beginning and   * fills register startup values for registers reused across scans. @@ -111,30 +84,49 @@ gl847_init_registers (Genesys_Device * dev)      dev->reg.clear();      dev->reg.init_reg(0x01, 0x82); +    if (dev->model->model_id == ModelId::CANON_5600F) { +        dev->reg.init_reg(0x01, 0x40); +    }      dev->reg.init_reg(0x02, 0x18);      dev->reg.init_reg(0x03, 0x50);      dev->reg.init_reg(0x04, 0x12); +    if (dev->model->model_id == ModelId::CANON_5600F) { +        dev->reg.init_reg(0x04, 0x20); +    }      dev->reg.init_reg(0x05, 0x80);      dev->reg.init_reg(0x06, 0x50); // FASTMODE + POWERBIT +    if (dev->model->model_id == ModelId::CANON_5600F) { +        dev->reg.init_reg(0x06, 0xf8); +    }      dev->reg.init_reg(0x08, 0x10); +    if (dev->model->model_id == ModelId::CANON_5600F) { +        dev->reg.init_reg(0x08, 0x20); +    }      dev->reg.init_reg(0x09, 0x01); +    if (dev->model->model_id == ModelId::CANON_5600F) { +        dev->reg.init_reg(0x09, 0x00); +    }      dev->reg.init_reg(0x0a, 0x00);      dev->reg.init_reg(0x0b, 0x01); +    if (dev->model->model_id == ModelId::CANON_5600F) { +        dev->reg.init_reg(0x0b, 0x6b); +    }      dev->reg.init_reg(0x0c, 0x02); +    if (dev->model->model_id == ModelId::CANON_5600F) { +        dev->reg.init_reg(0x0c, 0x00); +    }      // LED exposures -    dev->reg.init_reg(0x10, 0x00); -    dev->reg.init_reg(0x11, 0x00); -    dev->reg.init_reg(0x12, 0x00); -    dev->reg.init_reg(0x13, 0x00); -    dev->reg.init_reg(0x14, 0x00); -    dev->reg.init_reg(0x15, 0x00); +    dev->reg.init_reg(0x10, 0x00); // exposure, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x11, 0x00); // exposure, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x12, 0x00); // exposure, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x13, 0x00); // exposure, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x14, 0x00); // exposure, overwritten in scanner_setup_sensor() below +    dev->reg.init_reg(0x15, 0x00); // exposure, overwritten in scanner_setup_sensor() below      dev->reg.init_reg(0x16, 0x10); // SENSOR_DEF      dev->reg.init_reg(0x17, 0x08); // SENSOR_DEF      dev->reg.init_reg(0x18, 0x00); // SENSOR_DEF - -    // EXPDMY      dev->reg.init_reg(0x19, 0x50); // SENSOR_DEF      dev->reg.init_reg(0x1a, 0x34); // SENSOR_DEF @@ -142,32 +134,40 @@ gl847_init_registers (Genesys_Device * dev)      dev->reg.init_reg(0x1c, 0x02); // SENSOR_DEF      dev->reg.init_reg(0x1d, 0x04); // SENSOR_DEF      dev->reg.init_reg(0x1e, 0x10); +    if (dev->model->model_id == ModelId::CANON_5600F) { +        dev->reg.init_reg(0x1e, 0xf0); +    }      dev->reg.init_reg(0x1f, 0x04); -    dev->reg.init_reg(0x20, 0x02); -    dev->reg.init_reg(0x21, 0x10); -    dev->reg.init_reg(0x22, 0x7f); -    dev->reg.init_reg(0x23, 0x7f); -    dev->reg.init_reg(0x24, 0x10); -    dev->reg.init_reg(0x25, 0x00); -    dev->reg.init_reg(0x26, 0x00); -    dev->reg.init_reg(0x27, 0x00); -    dev->reg.init_reg(0x2c, 0x09); -    dev->reg.init_reg(0x2d, 0x60); -    dev->reg.init_reg(0x2e, 0x80); -    dev->reg.init_reg(0x2f, 0x80); -    dev->reg.init_reg(0x30, 0x00); -    dev->reg.init_reg(0x31, 0x10); -    dev->reg.init_reg(0x32, 0x15); -    dev->reg.init_reg(0x33, 0x0e); -    dev->reg.init_reg(0x34, 0x40); -    dev->reg.init_reg(0x35, 0x00); -    dev->reg.init_reg(0x36, 0x2a); -    dev->reg.init_reg(0x37, 0x30); -    dev->reg.init_reg(0x38, 0x2a); -    dev->reg.init_reg(0x39, 0xf8); -    dev->reg.init_reg(0x3d, 0x00); -    dev->reg.init_reg(0x3e, 0x00); -    dev->reg.init_reg(0x3f, 0x00); +    dev->reg.init_reg(0x20, 0x02); // BUFSEL: buffer full condition +    dev->reg.init_reg(0x21, 0x10); // STEPNO: set during motor setup +    dev->reg.init_reg(0x22, 0x7f); // FWDSTEP: set during motor setup +    dev->reg.init_reg(0x23, 0x7f); // BWDSTEP: set during motor setup +    dev->reg.init_reg(0x24, 0x10); // FASTNO: set during motor setup +    dev->reg.init_reg(0x25, 0x00); // LINCNT: set during motor setup +    dev->reg.init_reg(0x26, 0x00); // LINCNT: set during motor setup +    dev->reg.init_reg(0x27, 0x00); // LINCNT: set during motor setup + +    dev->reg.init_reg(0x2c, 0x09); // DPISET: set during sensor setup +    dev->reg.init_reg(0x2d, 0x60); // DPISET: set during sensor setup + +    dev->reg.init_reg(0x2e, 0x80); // BWHI: black/white low threshdold +    dev->reg.init_reg(0x2f, 0x80); // BWLOW: black/white low threshold + +    dev->reg.init_reg(0x30, 0x00); // STRPIXEL: set during sensor setup +    dev->reg.init_reg(0x31, 0x10); // STRPIXEL: set during sensor setup +    dev->reg.init_reg(0x32, 0x15); // ENDPIXEL: set during sensor setup +    dev->reg.init_reg(0x33, 0x0e); // ENDPIXEL: set during sensor setup + +    dev->reg.init_reg(0x34, 0x40); // DUMMY: SENSOR_DEF +    dev->reg.init_reg(0x35, 0x00); // MAXWD: set during scan setup +    dev->reg.init_reg(0x36, 0x2a); // MAXWD: set during scan setup +    dev->reg.init_reg(0x37, 0x30); // MAXWD: set during scan setup +    dev->reg.init_reg(0x38, 0x2a); // LPERIOD: SENSOR_DEF +    dev->reg.init_reg(0x39, 0xf8); // LPERIOD: SENSOR_DEF +    dev->reg.init_reg(0x3d, 0x00); // FEEDL: set during motor setup +    dev->reg.init_reg(0x3e, 0x00); // FEEDL: set during motor setup +    dev->reg.init_reg(0x3f, 0x00); // FEEDL: set during motor setup +      dev->reg.init_reg(0x52, 0x03); // SENSOR_DEF      dev->reg.init_reg(0x53, 0x07); // SENSOR_DEF      dev->reg.init_reg(0x54, 0x00); // SENSOR_DEF @@ -177,30 +177,27 @@ gl847_init_registers (Genesys_Device * dev)      dev->reg.init_reg(0x58, 0x2a); // SENSOR_DEF      dev->reg.init_reg(0x59, 0xe1); // SENSOR_DEF      dev->reg.init_reg(0x5a, 0x55); // SENSOR_DEF -    dev->reg.init_reg(0x5e, 0x41); -    dev->reg.init_reg(0x5f, 0x40); -    dev->reg.init_reg(0x60, 0x00); -    dev->reg.init_reg(0x61, 0x21); -    dev->reg.init_reg(0x62, 0x40); -    dev->reg.init_reg(0x63, 0x00); -    dev->reg.init_reg(0x64, 0x21); -    dev->reg.init_reg(0x65, 0x40); -    dev->reg.init_reg(0x67, 0x80); -    dev->reg.init_reg(0x68, 0x80); -    dev->reg.init_reg(0x69, 0x20); -    dev->reg.init_reg(0x6a, 0x20); - -    // CK1MAP + +    dev->reg.init_reg(0x5e, 0x41); // DECSEL, STOPTIM +    dev->reg.init_reg(0x5f, 0x40); // FMOVDEC: set during motor setup + +    dev->reg.init_reg(0x60, 0x00); // Z1MOD: overwritten during motor setup +    dev->reg.init_reg(0x61, 0x21); // Z1MOD: overwritten during motor setup +    dev->reg.init_reg(0x62, 0x40); // Z1MOD: overwritten during motor setup +    dev->reg.init_reg(0x63, 0x00); // Z2MOD: overwritten during motor setup +    dev->reg.init_reg(0x64, 0x21); // Z2MOD: overwritten during motor setup +    dev->reg.init_reg(0x65, 0x40); // Z2MOD: overwritten during motor setup +    dev->reg.init_reg(0x67, 0x80); // STEPSEL, MTRPWM: overwritten during motor setup +    dev->reg.init_reg(0x68, 0x80); // FSTPSEL, FASTPWM: overwritten during motor setup +    dev->reg.init_reg(0x69, 0x20); // FSHDEC: overwritten during motor setup +    dev->reg.init_reg(0x6a, 0x20); // FMOVNO: overwritten during motor setup +      dev->reg.init_reg(0x74, 0x00); // SENSOR_DEF      dev->reg.init_reg(0x75, 0x00); // SENSOR_DEF      dev->reg.init_reg(0x76, 0x3c); // SENSOR_DEF - -    // CK3MAP      dev->reg.init_reg(0x77, 0x00); // SENSOR_DEF      dev->reg.init_reg(0x78, 0x00); // SENSOR_DEF      dev->reg.init_reg(0x79, 0x9f); // SENSOR_DEF - -    // CK4MAP      dev->reg.init_reg(0x7a, 0x00); // SENSOR_DEF      dev->reg.init_reg(0x7b, 0x00); // SENSOR_DEF      dev->reg.init_reg(0x7c, 0x55); // SENSOR_DEF @@ -208,11 +205,23 @@ gl847_init_registers (Genesys_Device * dev)      dev->reg.init_reg(0x7d, 0x00);      // NOTE: autoconf is a non working option -    dev->reg.init_reg(0x87, 0x02); -    dev->reg.init_reg(0x9d, 0x06); -    dev->reg.init_reg(0xa2, 0x0f); -    dev->reg.init_reg(0xbd, 0x18); -    dev->reg.init_reg(0xfe, 0x08); +    dev->reg.init_reg(0x87, 0x02); // TODO: move to SENSOR_DEF +    dev->reg.init_reg(0x9d, 0x06); // RAMDLY, MOTLAG, CMODE, STEPTIM, IFRS +    dev->reg.init_reg(0xa2, 0x0f); // misc + +    if (dev->model->model_id == ModelId::CANON_5600F) { +        dev->reg.init_reg(0xab, 0x31); +        dev->reg.init_reg(0xbb, 0x00); +        dev->reg.init_reg(0xbc, 0x0f); +    } +    dev->reg.init_reg(0xbd, 0x18); // misc +    dev->reg.init_reg(0xfe, 0x08); // misc +    if (dev->model->model_id == ModelId::CANON_5600F) { +        dev->reg.init_reg(0x9e, 0x00); // sensor reg, but not in SENSOR_DEF +        dev->reg.init_reg(0x9f, 0x00); // sensor reg, but not in SENSOR_DEF +        dev->reg.init_reg(0xaa, 0x00); // custom data +        dev->reg.init_reg(0xff, 0x00); +    }      // gamma[0] and gamma[256] values      dev->reg.init_reg(0xbe, 0x00); @@ -237,233 +246,180 @@ gl847_init_registers (Genesys_Device * dev)      }      const auto& sensor = sanei_genesys_find_sensor_any(dev); -    sanei_genesys_set_dpihw(dev->reg, sensor, sensor.optical_res); +    const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, sensor.full_resolution, +                                                         3, ScanMethod::FLATBED); +    sanei_genesys_set_dpihw(dev->reg, dpihw_sensor.register_dpihw); -  /* initalize calibration reg */ -  dev->calib_reg = dev->reg; +    if (dev->model->model_id == ModelId::CANON_5600F) { +        scanner_setup_sensor(*dev, sensor, dev->reg); +    }  } -/**@brief send slope table for motor movement - * Send slope_table in machine byte order - * @param dev device to send slope table - * @param table_nr index of the slope table in ASIC memory - * Must be in the [0-4] range. - * @param slope_table pointer to 16 bit values array of the slope table - * @param steps number of elements in the slope table - */ -static void gl847_send_slope_table(Genesys_Device* dev, int table_nr, -                                   const std::vector<uint16_t>& slope_table, -                                   int steps) +// Set values of analog frontend +void CommandSetGl847::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const  { -    DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %d", table_nr, steps); -  int i; -  char msg[10000]; - -  /* sanity check */ -  if(table_nr<0 || table_nr>4) -    { -        throw SaneException("invalid table number %d", table_nr); -    } - -  std::vector<uint8_t> table(steps * 2); -  for (i = 0; i < steps; i++) -    { -      table[i * 2] = slope_table[i] & 0xff; -      table[i * 2 + 1] = slope_table[i] >> 8; -    } +    DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" : +                               set == AFE_SET ? "set" : +                               set == AFE_POWER_SAVE ? "powersave" : "huh?"); -  if (DBG_LEVEL >= DBG_io) -    { -        std::sprintf(msg, "write slope %d (%d)=", table_nr, steps); -      for (i = 0; i < steps; i++) -	{ -            std::sprintf(msg + std::strlen(msg), "%d", slope_table[i]); -	} -      DBG (DBG_io, "%s: %s\n", __func__, msg); -    } +    (void) sensor; -    if (dev->interface->is_mock()) { -        dev->interface->record_slope_table(table_nr, slope_table); +    if (dev->model->model_id != ModelId::CANON_5600F) { +        // FIXME: remove the following read +        dev->interface->read_register(REG_0x04);      } -    // slope table addresses are fixed -    dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, steps * 2, table.data()); -} - -/** - * Set register values of Analog Device type frontend - * */ -static void gl847_set_ad_fe(Genesys_Device* dev, uint8_t set) -{ -    DBG_HELPER(dbg); -  int i;      // wait for FE to be ready      auto status = scanner_read_status(*dev);      while (status.is_front_end_busy) {          dev->interface->sleep_ms(10);          status = scanner_read_status(*dev); -    }; - -  if (set == AFE_INIT) -    { -        DBG(DBG_proc, "%s(): setting DAC %u\n", __func__, -            static_cast<unsigned>(dev->model->adc_id)); - -      dev->frontend = dev->frontend_initial;      } -    // reset DAC -    dev->interface->write_fe_register(0x00, 0x80); - -    // write them to analog frontend -    dev->interface->write_fe_register(0x00, dev->frontend.regs.get_value(0x00)); - -    dev->interface->write_fe_register(0x01, dev->frontend.regs.get_value(0x01)); +    if (set == AFE_INIT) { +        dev->frontend = dev->frontend_initial; +    } -    for (i = 0; i < 3; i++) { -        dev->interface->write_fe_register(0x02 + i, dev->frontend.get_gain(i)); +    if (dev->model->model_id != ModelId::CANON_5600F) { +        // reset DAC (BUG: this does completely different thing on Analog Devices ADCs) +        dev->interface->write_fe_register(0x00, 0x80); +    } else { +        if (dev->frontend.layout.type == FrontendType::WOLFSON) { +            // reset DAC +            dev->interface->write_fe_register(0x04, 0xff); +        }      } -    for (i = 0; i < 3; i++) { -        dev->interface->write_fe_register(0x05 + i, dev->frontend.get_offset(i)); + +    for (const auto& reg : dev->frontend.regs) { +        dev->interface->write_fe_register(reg.address, reg.value);      }  } -// Set values of analog frontend -void CommandSetGl847::set_fe(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t set) const +static void gl847_write_motor_phase_table(Genesys_Device& dev, unsigned ydpi)  { -    DBG_HELPER_ARGS(dbg, "%s", set == AFE_INIT ? "init" : -                               set == AFE_SET ? "set" : -                               set == AFE_POWER_SAVE ? "powersave" : "huh?"); - -    (void) sensor; - -    uint8_t val = dev->interface->read_register(REG_0x04); -    uint8_t frontend_type = val & REG_0x04_FESET; - -    // route to AD devices -    if (frontend_type == 0x02) { -        gl847_set_ad_fe(dev, set); -        return; +    (void) ydpi; +    if (dev.model->model_id == ModelId::CANON_5600F) { +        std::vector<std::uint8_t> phase_table = { +            0x33, 0x00, 0x33, 0x00, 0x33, 0x00, 0x33, 0x00, +            0x32, 0x00, 0x32, 0x00, 0x32, 0x00, 0x32, 0x00, +            0x35, 0x00, 0x35, 0x00, 0x35, 0x00, 0x35, 0x00, +            0x38, 0x00, 0x38, 0x00, 0x38, 0x00, 0x38, 0x00, +            0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, +            0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, +            0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, +            0x12, 0x00, 0x12, 0x00, 0x12, 0x00, 0x12, 0x00, +            0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, +            0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, +            0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, +            0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, +            0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, +            0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, +            0x25, 0x00, 0x25, 0x00, 0x25, 0x00, 0x25, 0x00, +            0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, +        }; +        dev.interface->write_ahb(0x01000a00, phase_table.size(), phase_table.data());      } - -    throw SaneException("unsupported frontend type %d", frontend_type);  } -  // @brief set up motor related register for scan  static void gl847_init_motor_regs_scan(Genesys_Device* dev,                                         const Genesys_Sensor& sensor,                                         Genesys_Register_Set* reg, -                                       const Motor_Profile& motor_profile, +                                       const MotorProfile& motor_profile,                                         unsigned int scan_exposure_time,                                         unsigned scan_yres,                                         unsigned int scan_lines,                                         unsigned int scan_dummy,                                         unsigned int feed_steps, -                                       MotorFlag flags) +                                       ScanFlag flags)  {      DBG_HELPER_ARGS(dbg, "scan_exposure_time=%d, can_yres=%d, step_type=%d, scan_lines=%d, "                           "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, ScanFlag::FEEDING)) { +        use_fast_fed = true; +    } +    if (has_flag(dev->model->flags, ModelFlag::DISABLE_FAST_FEEDING)) { +        use_fast_fed = false;      } -  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); -  /* 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; +    if (has_flag(flags, ScanFlag::AUTO_GO_HOME)) { +        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, ScanFlag::DISABLE_BUFFER_FULL_MOVE) || (scan_yres >= sensor.full_resolution)) { +        reg02 |= REG_0x02_ACDCDIS;      } - -    if (has_flag(flags, MotorFlag::REVERSE)) { -        r->value |= REG_0x02_MTRREV; +    if (has_flag(flags, ScanFlag::REVERSE)) { +        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, -                                                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); +    // scan and backtracking slope table +    auto scan_table = create_slope_table(dev->model->asic_type, dev->motor, scan_yres, +                                         scan_exposure_time, step_multiplier, motor_profile); +    scanner_send_slope_table(dev, sensor, SCAN_TABLE, scan_table.table); +    scanner_send_slope_table(dev, sensor, BACKTRACK_TABLE, scan_table.table); -  /* 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;      } -    Motor_Profile fast_motor_profile = motor_profile; +    MotorProfile fast_motor_profile = motor_profile;      fast_motor_profile.step_type = fast_step_type; -    auto fast_table = sanei_genesys_slope_table(dev->model->asic_type, fast_dpi, -                                                scan_exposure_time, dev->motor.base_ydpi, -                                                step_multiplier, fast_motor_profile); +    auto fast_table = create_slope_table(dev->model->asic_type, dev->motor, fast_dpi, +                                         scan_exposure_time, step_multiplier, fast_motor_profile); + +    scanner_send_slope_table(dev, sensor, STOP_TABLE, fast_table.table); +    scanner_send_slope_table(dev, sensor, FAST_TABLE, fast_table.table); +    scanner_send_slope_table(dev, sensor, HOME_TABLE, fast_table.table); -    gl847_send_slope_table(dev, STOP_TABLE, fast_table.table, fast_table.steps_count); -    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); +    gl847_write_motor_phase_table(*dev, scan_yres); -  /* 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 -    { +        dist = (scan_table.table.size() + 2 * fast_table.table.size()); +        // 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 = scan_table.table.size(); +        if (has_flag(flags, ScanFlag::FEEDING)) {              dist *= 2;          }      } -  DBG(DBG_io2, "%s: acceleration distance=%d\n", __func__, dist); -  /* check for overflow */ +    // check for overflow      if (dist < feedl) {          feedl -= dist;      } else { @@ -471,25 +427,20 @@ static void gl847_init_motor_regs_scan(Genesys_Device* dev,      }      reg->set24(REG_FEEDL, 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); @@ -498,45 +449,37 @@ 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.table.size() / (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, +                                 scan_table.table.size(), +                                 feedl,                                   min_restep * step_multiplier,                                   &z1,                                   &z2); -  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);      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; +    reg->set8(REG_0x67, REG_0x67_MTRPWM); +    reg->set8(REG_0x68, REG_0x68_FASTPWM); -    r = sanei_genesys_get_address(reg, REG_0x68); -    r->value = REG_0x68_FASTPWM; - -    reg->set8(REG_STEPNO, scan_table.steps_count / step_multiplier); -    reg->set8(REG_FASTNO, scan_table.steps_count / step_multiplier); -    reg->set8(REG_FSHDEC, scan_table.steps_count / step_multiplier); -    reg->set8(REG_FMOVNO, fast_table.steps_count / step_multiplier); -    reg->set8(REG_FMOVDEC, fast_table.steps_count / step_multiplier); +    reg->set8(REG_STEPNO, scan_table.table.size() / step_multiplier); +    reg->set8(REG_FASTNO, scan_table.table.size() / step_multiplier); +    reg->set8(REG_FSHDEC, scan_table.table.size() / step_multiplier); +    reg->set8(REG_FMOVNO, fast_table.table.size() / step_multiplier); +    reg->set8(REG_FMOVDEC, fast_table.table.size() / step_multiplier);  } @@ -563,84 +506,84 @@ static void gl847_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens                                           const ScanSession& session)  {      DBG_HELPER_ARGS(dbg, "exposure_time=%d", exposure_time); -    unsigned dpihw; -  GenesysRegister *r; - -    // resolution is divided according to ccd_pixels_per_system_pixel() -    unsigned ccd_pixels_per_system_pixel = sensor.ccd_pixels_per_system_pixel(); -    DBG(DBG_io2, "%s: ccd_pixels_per_system_pixel=%d\n", __func__, ccd_pixels_per_system_pixel); - -    // to manage high resolution device while keeping good low resolution scanning speed, we make -    // hardware dpi vary -    dpihw = sensor.get_register_hwdpi(session.params.xres * ccd_pixels_per_system_pixel); -  DBG(DBG_io2, "%s: dpihw=%d\n", __func__, dpihw); -    gl847_setup_sensor(dev, sensor, reg); +    scanner_setup_sensor(*dev, sensor, *reg);      dev->cmd_set->set_fe(dev, sensor, AFE_SET);    /* enable shading */      regs_set_optical_off(dev->model->asic_type, *reg); -    r = sanei_genesys_get_address(reg, REG_0x01); -    r->value |= REG_0x01_SHDAREA; +    reg->find_reg(REG_0x01).value |= REG_0x01_SHDAREA;      if (has_flag(session.params.flags, ScanFlag::DISABLE_SHADING) || -        (dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)) +        has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) || +        session.use_host_side_calib)      { -        r->value &= ~REG_0x01_DVDSET; -    } -  else -    { -        r->value |= REG_0x01_DVDSET; +        reg->find_reg(REG_0x01).value &= ~REG_0x01_DVDSET; +    } else { +        reg->find_reg(REG_0x01).value |= REG_0x01_DVDSET;      } +    reg->find_reg(REG_0x03).value &= ~REG_0x03_AVEENB; -  r = sanei_genesys_get_address (reg, REG_0x03); -  r->value &= ~REG_0x03_AVEENB; - +    reg->find_reg(REG_0x03).value &= ~REG_0x03_XPASEL; +    if (has_flag(session.params.flags, ScanFlag::USE_XPA)) { +        reg->find_reg(REG_0x03).value |= REG_0x03_XPASEL; +    }      sanei_genesys_set_lamp_power(dev, sensor, *reg,                                   !has_flag(session.params.flags, ScanFlag::DISABLE_LAMP)); +    reg->state.is_xpa_on = has_flag(session.params.flags, ScanFlag::USE_XPA); + +    if (has_flag(session.params.flags, ScanFlag::USE_XPA)) { +        if (dev->model->model_id == ModelId::CANON_5600F) { +            regs_set_exposure(dev->model->asic_type, *reg, sanei_genesys_fixup_exposure({0, 0, 0})); +        } +    } -  /* BW threshold */ -    r = sanei_genesys_get_address (reg, 0x2e); -  r->value = dev->settings.threshold; -    r = sanei_genesys_get_address (reg, 0x2f); -  r->value = dev->settings.threshold; +    // BW threshold +    reg->set8(0x2e, 0x7f); +    reg->set8(0x2f, 0x7f);    /* monochrome / color scan */ -    r = sanei_genesys_get_address (reg, REG_0x04);      switch (session.params.depth) {      case 8: -            r->value &= ~(REG_0x04_LINEART | REG_0x04_BITSET); +            reg->find_reg(REG_0x04).value &= ~(REG_0x04_LINEART | REG_0x04_BITSET);        break;      case 16: -            r->value &= ~REG_0x04_LINEART; -            r->value |= REG_0x04_BITSET; +            reg->find_reg(REG_0x04).value &= ~REG_0x04_LINEART; +            reg->find_reg(REG_0x04).value |= REG_0x04_BITSET;        break;      } -    r->value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD); +    reg->find_reg(REG_0x04).value &= ~(REG_0x04_FILTER | REG_0x04_AFEMOD);    if (session.params.channels == 1)      {        switch (session.params.color_filter)  	{             case ColorFilter::RED: -               r->value |= 0x14; +               reg->find_reg(REG_0x04).value |= 0x14;                 break;             case ColorFilter::BLUE: -               r->value |= 0x1c; +               reg->find_reg(REG_0x04).value |= 0x1c;                 break;             case ColorFilter::GREEN: -               r->value |= 0x18; +               reg->find_reg(REG_0x04).value |= 0x18;                 break;             default:                 break; // should not happen  	}      } else { -        r->value |= 0x10; // mono +        if (dev->model->model_id == ModelId::CANON_5600F) { +            reg->find_reg(REG_0x04).value |= 0x20; +        } else { +            reg->find_reg(REG_0x04).value |= 0x10; // mono +        }      } -    sanei_genesys_set_dpihw(*reg, sensor, dpihw); +    const auto& dpihw_sensor = sanei_genesys_find_sensor(dev, session.output_resolution, +                                                         session.params.channels, +                                                         session.params.scan_method); +    sanei_genesys_set_dpihw(*reg, dpihw_sensor.register_dpihw);      if (should_enable_gamma(session, sensor)) {          reg->find_reg(REG_0x05).value |= REG_0x05_GMMENB; @@ -651,38 +594,30 @@ static void gl847_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens    /* CIS scanners can do true gray by setting LEDADD */    /* we set up LEDADD only when asked */      if (dev->model->is_cis) { -        r = sanei_genesys_get_address (reg, 0x87); -        r->value &= ~REG_0x87_LEDADD; +        reg->find_reg(0x87).value &= ~REG_0x87_LEDADD; +          if (session.enable_ledadd) { -            r->value |= REG_0x87_LEDADD; +            reg->find_reg(0x87).value |= REG_0x87_LEDADD;          }        /* RGB weighting -        r = sanei_genesys_get_address (reg, 0x01); -        r->value &= ~REG_0x01_TRUEGRAY; +        reg->find_reg(0x01).value &= ~REG_0x01_TRUEGRAY;          if (session.enable_ledadd) { -            r->value |= REG_0x01_TRUEGRAY; +            reg->find_reg(0x01).value |= REG_0x01_TRUEGRAY;          }          */      } -    unsigned dpiset = session.params.xres * ccd_pixels_per_system_pixel; -    reg->set16(REG_DPISET, dpiset); -    DBG (DBG_io2, "%s: dpiset used=%d\n", __func__, dpiset); - +    reg->set16(REG_DPISET, sensor.register_dpiset);      reg->set16(REG_STRPIXEL, session.pixel_startx);      reg->set16(REG_ENDPIXEL, session.pixel_endx); -    build_image_pipeline(dev, session); +    setup_image_pipeline(*dev, session);    /* MAXWD is expressed in 4 words unit */      // BUG: we shouldn't multiply by channels here      reg->set24(REG_MAXWD, (session.output_line_bytes_raw * session.params.channels >> 2)); -      reg->set16(REG_LPERIOD, exposure_time); -  DBG(DBG_io2, "%s: exposure_time used=%d\n", __func__, exposure_time); - -  r = sanei_genesys_get_address (reg, 0x34); -  r->value = sensor.dummy_pixel; +    reg->set8(0x34, sensor.dummy_pixel);  }  void CommandSetGl847::init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor, @@ -692,13 +627,18 @@ void CommandSetGl847::init_regs_for_scan_session(Genesys_Device* dev, const Gene      DBG_HELPER(dbg);      session.assert_computed(); -  int move;    int exposure_time;    int slope_dpi = 0;    int dummy = 0; -    dummy = 3 - session.params.channels; +    if (dev->model->model_id == ModelId::CANON_LIDE_100 || +        dev->model->model_id == ModelId::CANON_LIDE_200 || +        dev->model->model_id == ModelId::CANON_LIDE_700F || +        dev->model->model_id == ModelId::HP_SCANJET_N6310) +    { +        dummy = 3 - session.params.channels; +    }  /* slope_dpi */  /* cis color scan is effectively a gray scan with 3 gray lines per color @@ -712,40 +652,15 @@ void CommandSetGl847::init_regs_for_scan_session(Genesys_Device* dev, const Gene    slope_dpi = slope_dpi * (1 + dummy);      exposure_time = sensor.exposure_lperiod; -    const auto& motor_profile = sanei_genesys_get_motor_profile(*gl847_motor_profiles, -                                                                dev->model->motor_id, -                                                                exposure_time); - -  DBG(DBG_info, "%s : exposure_time=%d pixels\n", __func__, exposure_time); -    DBG(DBG_info, "%s : scan_step_type=%d\n", __func__, -        static_cast<unsigned>(motor_profile.step_type)); +    const auto& motor_profile = get_motor_profile(dev->motor.profiles, exposure_time, session);    /* we enable true gray for cis scanners only, and just when doing     * scan since color calibration is OK for this mode     */      gl847_init_optical_regs_scan(dev, sensor, reg, exposure_time, session); - -    move = session.params.starty; -    DBG(DBG_info, "%s: move=%d steps\n", __func__, move); - -    MotorFlag mflags = MotorFlag::NONE; -    if (has_flag(session.params.flags, ScanFlag::DISABLE_BUFFER_FULL_MOVE)) { -        mflags |= MotorFlag::DISABLE_BUFFER_FULL_MOVE; -    } -    if (has_flag(session.params.flags, ScanFlag::FEEDING)) { -        mflags |= MotorFlag::FEED; -  } -    if (has_flag(session.params.flags, ScanFlag::REVERSE)) { -        mflags |= MotorFlag::REVERSE; -    } -      gl847_init_motor_regs_scan(dev, sensor, reg, motor_profile, exposure_time, slope_dpi, -                               dev->model->is_cis ? session.output_line_count * session.params.channels -                                                  : session.output_line_count, -                               dummy, move, mflags); - -    dev->read_buffer.clear(); -    dev->read_buffer.alloc(session.buffer_size_read); +                               session.optical_line_count, dummy, session.params.starty, +                               session.params.flags);      dev->read_active = true; @@ -761,21 +676,59 @@ ScanSession CommandSetGl847::calculate_scan_session(const Genesys_Device* dev,                                                      const Genesys_Sensor& sensor,                                                      const Genesys_Settings& settings) const  { -  int start; -      DBG(DBG_info, "%s ", __func__);      debug_dump(DBG_info, settings); -  /* start */ -    start = static_cast<int>(dev->model->x_offset); -    start = static_cast<int>(start + settings.tl_x); -    start = static_cast<int>((start * sensor.optical_res) / MM_PER_INCH); +    // backtracking isn't handled well, so don't enable it +    ScanFlag flags = ScanFlag::DISABLE_BUFFER_FULL_MOVE; + +    /*  Steps to move to reach scanning area: + +        - first we move to physical start of scanning either by a fixed steps amount from the +          black strip or by a fixed amount from parking position, minus the steps done during +          shading calibration. + +        - then we move by the needed offset whitin physical scanning area +    */ +    unsigned move_dpi = dev->motor.base_ydpi; + +    float move = dev->model->y_offset; +    if (settings.scan_method == ScanMethod::TRANSPARENCY || +        settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +    { +        // note: scanner_move_to_ta() function has already been called and the sensor is at the +        // transparency adapter +        if (!dev->ignore_offsets) { +            move = dev->model->y_offset_ta - dev->model->y_offset_sensor_to_ta; +        } +        flags |= ScanFlag::USE_XPA; +    } else { +        if (!dev->ignore_offsets) { +            move = dev->model->y_offset; +        } +    } + +    move = move + settings.tl_y; +    move = static_cast<float>((move * move_dpi) / MM_PER_INCH); +    move -= dev->head_pos(ScanHeadId::PRIMARY); + +    float start = dev->model->x_offset; +    if (settings.scan_method == ScanMethod::TRANSPARENCY || +        settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +    { +        start = dev->model->x_offset_ta; +    } else { +        start = dev->model->x_offset; +    } + +    start = start + dev->settings.tl_x; +    start = static_cast<float>((start * settings.xres) / MM_PER_INCH);      ScanSession session;      session.params.xres = settings.xres;      session.params.yres = settings.yres; -    session.params.startx = start; // not used -    session.params.starty = 0; // not used +    session.params.startx = static_cast<unsigned>(start); +    session.params.starty = static_cast<unsigned>(move);      session.params.pixels = settings.pixels;      session.params.requested_pixels = settings.requested_pixels;      session.params.lines = settings.lines; @@ -784,7 +737,7 @@ ScanSession CommandSetGl847::calculate_scan_session(const Genesys_Device* dev,      session.params.scan_method = settings.scan_method;      session.params.scan_mode = settings.scan_mode;      session.params.color_filter = settings.color_filter; -    session.params.flags = ScanFlag::NONE; +    session.params.flags = flags;      compute_session(dev, session, sensor); @@ -811,25 +764,61 @@ void CommandSetGl847::begin_scan(Genesys_Device* dev, const Genesys_Sensor& sens      DBG_HELPER(dbg);      (void) sensor;    uint8_t val; -  GenesysRegister *r; -    // clear GPIO 10 -    if (dev->model->gpio_id != GpioId::CANON_LIDE_700F) { +    if (reg->state.is_xpa_on && reg->state.is_lamp_on) { +        dev->cmd_set->set_xpa_lamp_power(*dev, true); +    } + +    if (dev->model->model_id == ModelId::HP_SCANJET_N6310 || +        dev->model->model_id == ModelId::CANON_LIDE_100 || +        dev->model->model_id == ModelId::CANON_LIDE_200) +    {          val = dev->interface->read_register(REG_0x6C);          val &= ~REG_0x6C_GPIO10;          dev->interface->write_register(REG_0x6C, val);      } -    val = REG_0x0D_CLRLNCNT; -    dev->interface->write_register(REG_0x0D, val); -    val = REG_0x0D_CLRMCNT; -    dev->interface->write_register(REG_0x0D, val); +    if (dev->model->model_id == ModelId::CANON_5600F) { +        switch (dev->session.params.xres) { +            case 75: +            case 150: +            case 300: +                scanner_register_rw_bits(*dev, REG_0xA6, 0x04, 0x1c); +                break; +            case 600: +                scanner_register_rw_bits(*dev, REG_0xA6, 0x18, 0x1c); +                break; +            case 1200: +                scanner_register_rw_bits(*dev, REG_0xA6, 0x08, 0x1c); +                break; +            case 2400: +                scanner_register_rw_bits(*dev, REG_0xA6, 0x10, 0x1c); +                break; +            case 4800: +                scanner_register_rw_bits(*dev, REG_0xA6, 0x00, 0x1c); +                break; +            default: +                throw SaneException("Unexpected xres"); +        } +        dev->interface->write_register(0x6c, 0xf0); +        dev->interface->write_register(0x6b, 0x87); +        dev->interface->write_register(0x6d, 0x5f); +    } + +    if (dev->model->model_id == ModelId::CANON_5600F) { +        scanner_clear_scan_and_feed_counts(*dev); +    } else { +        // FIXME: use scanner_clear_scan_and_feed_counts() +        val = REG_0x0D_CLRLNCNT; +        dev->interface->write_register(REG_0x0D, val); +        val = REG_0x0D_CLRMCNT; +        dev->interface->write_register(REG_0x0D, val); +    }      val = dev->interface->read_register(REG_0x01);      val |= REG_0x01_SCAN;      dev->interface->write_register(REG_0x01, val); -    r = sanei_genesys_get_address (reg, REG_0x01); -  r->value = val; +    reg->set8(REG_0x01, val);      scanner_start_action(*dev, start_motor); @@ -844,268 +833,86 @@ void CommandSetGl847::end_scan(Genesys_Device* dev, Genesys_Register_Set* reg,      (void) reg;      DBG_HELPER_ARGS(dbg, "check_stop = %d", check_stop); +    if (reg->state.is_xpa_on) { +        dev->cmd_set->set_xpa_lamp_power(*dev, false); +    } +      if (!dev->model->is_sheetfed) {          scanner_stop_action(*dev);      }  } -/** Park head - * Moves the slider to the home (top) position slowly - * @param dev device to park - * @param wait_until_home true to make the function waiting for head - * to be home before returning, if fals returne immediately -*/  void CommandSetGl847::move_back_home(Genesys_Device* dev, bool wait_until_home) const  {      scanner_move_back_home(*dev, wait_until_home);  } -// Automatically set top-left edge of the scan area by scanning a 200x200 pixels area at 600 dpi -// from very top of scanner -void CommandSetGl847::search_start_position(Genesys_Device* dev) const -{ -    DBG_HELPER(dbg); -  int size; -  Genesys_Register_Set local_reg; - -  int pixels = 600; -  int dpi = 300; - -  local_reg = dev->reg; - -  /* sets for a 200 lines * 600 pixels */ -  /* normal scan with no shading */ - -    // FIXME: the current approach of doing search only for one resolution does not work on scanners -    // whith employ different sensors with potentially different settings. -    const auto& sensor = sanei_genesys_find_sensor(dev, dpi, 1, dev->model->default_method); - -    ScanSession session; -    session.params.xres = dpi; -    session.params.yres = dpi; -    session.params.startx =  0; -    session.params.starty =  0; /*we should give a small offset here~60 steps */ -    session.params.pixels = 600; -    session.params.lines = dev->model->search_lines; -    session.params.depth = 8; -    session.params.channels =  1; -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = ScanColorMode::GRAY; -    session.params.color_filter = ColorFilter::GREEN; -    session.params.flags = ScanFlag::DISABLE_SHADING | -                           ScanFlag::DISABLE_GAMMA | -                           ScanFlag::IGNORE_LINE_DISTANCE; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, &local_reg, session); - -    // send to scanner -    dev->interface->write_registers(local_reg); - -  size = pixels * dev->model->search_lines; - -  std::vector<uint8_t> data(size); - -    begin_scan(dev, sensor, &local_reg, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("search_start_position"); -        end_scan(dev, &local_reg, true); -        dev->reg = local_reg; -        return; -    } - -    wait_until_buffer_non_empty(dev); - -    // now we're on target, we can read data -    sanei_genesys_read_data_from_scanner(dev, data.data(), size); - -    if (DBG_LEVEL >= DBG_data) { -        sanei_genesys_write_pnm_file("gl847_search_position.pnm", data.data(), 8, 1, pixels, -                                     dev->model->search_lines); -    } - -    end_scan(dev, &local_reg, true); - -  /* update regs to copy ASIC internal state */ -  dev->reg = local_reg; - -    // TODO: find out where sanei_genesys_search_reference_point stores information, -    // and use that correctly -    for (auto& sensor_update : -            sanei_genesys_find_sensors_all_for_write(dev, dev->model->default_method)) -    { -        sanei_genesys_search_reference_point(dev, sensor_update, data.data(), 0, dpi, pixels, -                                             dev->model->search_lines); -    } -} - -// sets up register for coarse gain calibration -// todo: check it for scanners using it -void CommandSetGl847::init_regs_for_coarse_calibration(Genesys_Device* dev, -                                                       const Genesys_Sensor& sensor, -                                                       Genesys_Register_Set& regs) const -{ -    DBG_HELPER(dbg); - -    ScanSession session; -    session.params.xres = dev->settings.xres; -    session.params.yres = dev->settings.yres; -    session.params.startx = 0; -    session.params.starty = 0; -    session.params.pixels = sensor.optical_res / sensor.ccd_pixels_per_system_pixel(); -    session.params.lines = 20; -    session.params.depth = 16; -    session.params.channels = dev->settings.get_channels(); -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = dev->settings.scan_mode; -    session.params.color_filter = dev->settings.color_filter; -    session.params.flags = ScanFlag::DISABLE_SHADING | -                           ScanFlag::DISABLE_GAMMA | -                           ScanFlag::SINGLE_LINE | -                           ScanFlag::IGNORE_LINE_DISTANCE; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, ®s, session); - -  DBG(DBG_info, "%s: optical sensor res: %d dpi, actual res: %d\n", __func__, -      sensor.optical_res / sensor.ccd_pixels_per_system_pixel(), dev->settings.xres); - -    dev->interface->write_registers(regs); -} -  // init registers for shading calibration  void CommandSetGl847::init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,                                              Genesys_Register_Set& regs) const  {      DBG_HELPER(dbg); -  dev->calib_channels = 3; +    unsigned move_dpi = dev->motor.base_ydpi; -  /* initial calibration reg values */ -  regs = dev->reg; +    float calib_size_mm = 0; +    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || +        dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +    { +        calib_size_mm = dev->model->y_size_calib_ta_mm; +    } else { +        calib_size_mm = dev->model->y_size_calib_mm; +    } -    dev->calib_resolution = sensor.get_register_hwdpi(dev->settings.xres); +    unsigned channels = 3; +    unsigned resolution = sensor.shading_resolution; -    const auto& calib_sensor = sanei_genesys_find_sensor(dev, dev->calib_resolution, -                                                         dev->calib_channels, +    const auto& calib_sensor = sanei_genesys_find_sensor(dev, resolution, channels,                                                           dev->settings.scan_method); -  dev->calib_total_bytes_to_read = 0; -  dev->calib_lines = dev->model->shading_lines; -    if (dev->calib_resolution == 4800) { -        dev->calib_lines *= 2; +    float move = 0; +    ScanFlag flags = ScanFlag::DISABLE_SHADING | +                     ScanFlag::DISABLE_GAMMA | +                     ScanFlag::DISABLE_BUFFER_FULL_MOVE; + +    if (dev->settings.scan_method == ScanMethod::TRANSPARENCY || +        dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +    { +        // note: scanner_move_to_ta() function has already been called and the sensor is at the +        // transparency adapter +        move = dev->model->y_offset_calib_white_ta - dev->model->y_offset_sensor_to_ta; +        flags |= ScanFlag::USE_XPA; +    } else { +        move = dev->model->y_offset_calib_white;      } -    dev->calib_pixels = (calib_sensor.sensor_pixels * dev->calib_resolution) / -                        calib_sensor.optical_res; -    DBG(DBG_io, "%s: calib_lines  = %zu\n", __func__, dev->calib_lines); -    DBG(DBG_io, "%s: calib_pixels = %zu\n", __func__, dev->calib_pixels); +    move = static_cast<float>((move * move_dpi) / MM_PER_INCH); + +    unsigned calib_lines = static_cast<unsigned>(calib_size_mm * resolution / MM_PER_INCH);      ScanSession session; -    session.params.xres = dev->calib_resolution; -    session.params.yres = dev->motor.base_ydpi; +    session.params.xres = resolution; +    session.params.yres = resolution;      session.params.startx = 0; -    session.params.starty = 20; -    session.params.pixels = dev->calib_pixels; -    session.params.lines = dev->calib_lines; +    session.params.starty = static_cast<unsigned>(move); +    session.params.pixels = dev->model->x_size_calib_mm * resolution / MM_PER_INCH; +    session.params.lines = calib_lines;      session.params.depth = 16; -    session.params.channels = dev->calib_channels; +    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::DISABLE_BUFFER_FULL_MOVE | -                           ScanFlag::IGNORE_LINE_DISTANCE; +    session.params.flags = flags;      compute_session(dev, session, calib_sensor);      init_regs_for_scan_session(dev, calib_sensor, ®s, session); -    dev->interface->write_registers(regs); - -  /* we use GENESYS_FLAG_SHADING_REPARK */ +  /* we use ModelFlag::SHADING_REPARK */      dev->set_head_pos_zero(ScanHeadId::PRIMARY); -} - -/** @brief set up registers for the actual scan - */ -void CommandSetGl847::init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const -{ -    DBG_HELPER(dbg); -  float move; -  int move_dpi; -  float start; -    debug_dump(DBG_info, dev->settings); - -  /* steps to move to reach scanning area: -     - first we move to physical start of scanning -     either by a fixed steps amount from the black strip -     or by a fixed amount from parking position, -     minus the steps done during shading calibration -     - then we move by the needed offset whitin physical -     scanning area - -     assumption: steps are expressed at maximum motor resolution - -     we need: -     float y_offset; -     float y_size; -     float y_offset_calib; -     mm_to_steps()=motor dpi / 2.54 / 10=motor dpi / MM_PER_INCH */ - -  /* if scanner uses GENESYS_FLAG_SEARCH_START y_offset is -     relative from origin, else, it is from parking position */ - -  move_dpi = dev->motor.base_ydpi; - -    move = static_cast<float>(dev->model->y_offset); -    move = static_cast<float>(move + dev->settings.tl_y); -    move = static_cast<float>((move * move_dpi) / MM_PER_INCH); -    move -= dev->head_pos(ScanHeadId::PRIMARY); -  DBG(DBG_info, "%s: move=%f steps\n", __func__, move); - -  /* fast move to scan area */ -  /* we don't move fast the whole distance since it would involve -   * computing acceleration/deceleration distance for scan -   * resolution. So leave a remainder for it so scan makes the final -   * move tuning */ -    if (dev->settings.get_channels() * dev->settings.yres >= 600 && move > 700) { -        scanner_move(*dev, dev->model->default_method, static_cast<unsigned>(move - 500), -                     Direction::FORWARD); -      move=500; -    } - -  DBG(DBG_info, "%s: move=%f steps\n", __func__, move); -  DBG(DBG_info, "%s: move=%f steps\n", __func__, move); - -  /* start */ -    start = static_cast<float>(dev->model->x_offset); -    start = static_cast<float>(start + dev->settings.tl_x); -    start = static_cast<float>((start * sensor.optical_res) / MM_PER_INCH); - -    ScanSession session; -    session.params.xres = dev->settings.xres; -    session.params.yres = dev->settings.yres; -    session.params.startx = static_cast<unsigned>(start); -    session.params.starty = static_cast<unsigned>(move); -    session.params.pixels = dev->settings.pixels; -    session.params.requested_pixels = dev->settings.requested_pixels; -    session.params.lines = dev->settings.lines; -    session.params.depth = dev->settings.depth; -    session.params.channels = dev->settings.get_channels(); -    session.params.scan_method = dev->settings.scan_method; -    session.params.scan_mode = dev->settings.scan_mode; -    session.params.color_filter = dev->settings.color_filter; -    // backtracking isn't handled well, so don't enable it -    session.params.flags = ScanFlag::DISABLE_BUFFER_FULL_MOVE; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, &dev->reg, session); +    dev->calib_session = session;  } -  /**   * Send shading calibration data. The buffer is considered to always hold values   * for all the channels. @@ -1114,39 +921,24 @@ void CommandSetGl847::send_shading_data(Genesys_Device* dev, const Genesys_Senso                                          uint8_t* data, int size) const  {      DBG_HELPER_ARGS(dbg, "writing %d bytes of shading data", size); -  uint32_t addr, length, i, x, factor, pixels; -    uint32_t dpiset, dpihw; +    std::uint32_t addr, i;    uint8_t val,*ptr,*src; -  /* shading data is plit in 3 (up to 5 with IR) areas -     write(0x10014000,0x00000dd8) -     URB 23429  bulk_out len  3544  wrote 0x33 0x10 0x.... -     write(0x1003e000,0x00000dd8) -     write(0x10068000,0x00000dd8) -   */ -    length = static_cast<std::uint32_t>(size / 3); -    std::uint32_t strpixel = dev->session.pixel_startx; -    std::uint32_t endpixel = dev->session.pixel_endx; +    unsigned length = static_cast<unsigned>(size / 3); -  /* compute deletion factor */ -    dpiset = dev->reg.get16(REG_DPISET); -    dpihw = sensor.get_register_hwdpi(dpiset); -  factor=dpihw/dpiset; -  DBG(DBG_io2, "%s: factor=%d\n", __func__, factor); +    // we're using SHDAREA, thus we only need to upload part of the line +    unsigned offset = dev->session.pixel_count_ratio.apply( +                dev->session.params.startx * sensor.full_resolution / dev->session.params.xres); +    unsigned pixels = dev->session.pixel_count_ratio.apply(dev->session.optical_pixels_raw); -  pixels=endpixel-strpixel; +    // turn pixel value into bytes 2x16 bits words +    offset *= 2 * 2; +    pixels *= 2 * 2; -  /* since we're using SHDAREA, substract startx coordinate from shading */ -    strpixel -= (sensor.ccd_start_xoffset * 600) / sensor.optical_res; - -  /* turn pixel value into bytes 2x16 bits words */ -  strpixel*=2*2; -  pixels*=2*2; - -    dev->interface->record_key_value("shading_offset", std::to_string(strpixel)); +    dev->interface->record_key_value("shading_offset", std::to_string(offset));      dev->interface->record_key_value("shading_pixels", std::to_string(pixels));      dev->interface->record_key_value("shading_length", std::to_string(length)); -    dev->interface->record_key_value("shading_factor", std::to_string(factor)); +    dev->interface->record_key_value("shading_factor", std::to_string(sensor.shading_factor));    std::vector<uint8_t> buffer(pixels, 0); @@ -1155,6 +947,10 @@ void CommandSetGl847::send_shading_data(Genesys_Device* dev, const Genesys_Senso    /* base addr of data has been written in reg D0-D4 in 4K word, so AHB address     * is 8192*reg value */ +    if (dev->model->model_id == ModelId::CANON_5600F) { +        return; +    } +    /* write actual color channel data */    for(i=0;i<3;i++)      { @@ -1162,11 +958,10 @@ void CommandSetGl847::send_shading_data(Genesys_Device* dev, const Genesys_Senso         * to the one corresponding to SHDAREA */        ptr = buffer.data(); -      /* iterate on both sensor segment */ -      for(x=0;x<pixels;x+=4*factor) -        { +        // iterate on both sensor segment +        for (unsigned x = 0; x < pixels; x += 4 * sensor.shading_factor) {            /* coefficient source */ -          src=(data+strpixel+i*length)+x; +            src = (data + offset + i * length) + x;            /* coefficient copy */            ptr[0]=src[0]; @@ -1192,160 +987,7 @@ void CommandSetGl847::send_shading_data(Genesys_Device* dev, const Genesys_Senso  SensorExposure CommandSetGl847::led_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,                                                  Genesys_Register_Set& regs) const  { -    DBG_HELPER(dbg); -  int num_pixels; -  int total_size; -  int used_res; -  int i, j; -  int val; -    int channels; -  int avg[3], top[3], bottom[3]; -  int turn; -  uint16_t exp[3]; -  float move; - -    move = static_cast<float>(dev->model->y_offset_calib_white); -    move = static_cast<float>((move * (dev->motor.base_ydpi / 4)) / MM_PER_INCH); -    if (move > 20) { -        scanner_move(*dev, dev->model->default_method, static_cast<unsigned>(move), -                     Direction::FORWARD); -    } -  DBG(DBG_io, "%s: move=%f steps\n", __func__, move); - -  /* offset calibration is always done in color mode */ -  channels = 3; -    used_res = sensor.get_register_hwdpi(dev->settings.xres); -    const auto& calib_sensor = sanei_genesys_find_sensor(dev, used_res, channels, -                                                         dev->settings.scan_method); -    num_pixels = (calib_sensor.sensor_pixels * used_res) / calib_sensor.optical_res; - -  /* initial calibration reg values */ -  regs = dev->reg; - -    ScanSession session; -    session.params.xres = used_res; -    session.params.yres = used_res; -    session.params.startx = 0; -    session.params.starty = 0; -    session.params.pixels = num_pixels; -    session.params.lines = 1; -    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_LINE_DISTANCE; -    compute_session(dev, session, calib_sensor); - -    init_regs_for_scan_session(dev, calib_sensor, ®s, session); - -    total_size = num_pixels * channels * (session.params.depth/8) * 1; -  std::vector<uint8_t> line(total_size); - -    // initial loop values and boundaries -    exp[0] = calib_sensor.exposure.red; -    exp[1] = calib_sensor.exposure.green; -    exp[2] = calib_sensor.exposure.blue; - -    bottom[0] = 28000; -    bottom[1] = 28000; -    bottom[2] = 28000; - -    top[0] = 32000; -    top[1] = 32000; -    top[2] = 32000; - -  turn = 0; - -  /* no move during led calibration */ -    bool acceptable = false; -  sanei_genesys_set_motor_power(regs, false); -  do -    { -        // set up exposure -        regs.set16(REG_EXPR,exp[0]); -        regs.set16(REG_EXPG,exp[1]); -        regs.set16(REG_EXPB,exp[2]); - -        // write registers and scan data -        dev->interface->write_registers(regs); - -      DBG(DBG_info, "%s: starting line reading\n", __func__); -        begin_scan(dev, calib_sensor, ®s, true); - -        if (is_testing_mode()) { -            dev->interface->test_checkpoint("led_calibration"); -            scanner_stop_action(*dev); -            move_back_home(dev, true); -            return calib_sensor.exposure; -        } - -        sanei_genesys_read_data_from_scanner(dev, line.data(), total_size); - -        // stop scanning -        scanner_stop_action(*dev); - -      if (DBG_LEVEL >= DBG_data) -	{ -          char fn[30]; -            std::snprintf(fn, 30, "gl847_led_%02d.pnm", turn); -            sanei_genesys_write_pnm_file(fn, line.data(), session.params.depth, -                                         channels, num_pixels, 1); -	} - -      /* compute average */ -      for (j = 0; j < channels; j++) -	{ -	  avg[j] = 0; -	  for (i = 0; i < num_pixels; i++) -	    { -	      if (dev->model->is_cis) -		val = -		  line[i * 2 + j * 2 * num_pixels + 1] * 256 + -		  line[i * 2 + j * 2 * num_pixels]; -	      else -		val = -		  line[i * 2 * channels + 2 * j + 1] * 256 + -		  line[i * 2 * channels + 2 * j]; -	      avg[j] += val; -	    } - -	  avg[j] /= num_pixels; -	} - -      DBG(DBG_info, "%s: average: %d,%d,%d\n", __func__, avg[0], avg[1], avg[2]); - -      /* check if exposure gives average within the boundaries */ -        acceptable = true; -      for(i=0;i<3;i++) -        { -            if (avg[i] < bottom[i] || avg[i] > top[i]) { -                auto target = (bottom[i] + top[i]) / 2; -                exp[i] = (exp[i] * target) / avg[i]; -                acceptable = false; -            } -        } - -      turn++; -    } -  while (!acceptable && turn < 100); - -  DBG(DBG_info, "%s: acceptable exposure: %d,%d,%d\n", __func__, exp[0], exp[1], exp[2]); - -    // set these values as final ones for scan -    dev->reg.set16(REG_EXPR, exp[0]); -    dev->reg.set16(REG_EXPG, exp[1]); -    dev->reg.set16(REG_EXPB, exp[2]); - -    // go back home -    if (move>20) { -        move_back_home(dev, true); -    } - -    return { exp[0], exp[1], exp[2] }; +    return scanner_led_calibration(*dev, sensor, regs);  }  /** @@ -1354,31 +996,37 @@ SensorExposure CommandSetGl847::led_calibration(Genesys_Device* dev, const Genes  static void gl847_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) { -        throw SaneException("failed to find GPIO profile for sensor_id=%d", -                            static_cast<unsigned>(dev->model->sensor_id)); -    } +    if (dev->model->model_id == ModelId::CANON_5600F) { +        apply_registers_ordered(dev->gpo.regs, {0xa6, 0xa7, 0x6f, 0x6e}, +                                [&](const GenesysRegisterSetting& reg) +        { +            dev->interface->write_register(reg.address, reg.value); +        }); +    } else { +        std::vector<std::uint16_t> order1 = { 0xa7, 0xa6, 0x6e }; +        std::vector<std::uint16_t> order2 = { 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0xa8, 0xa9 }; -    dev->interface->write_register(REG_0xA7, gpios[idx].ra7); -    dev->interface->write_register(REG_0xA6, gpios[idx].ra6); +        for (auto addr : order1) { +            dev->interface->write_register(addr, dev->gpo.regs.find_reg(addr).value); +        } -    dev->interface->write_register(REG_0x6E, gpios[idx].r6e); -    dev->interface->write_register(REG_0x6C, 0x00); +        dev->interface->write_register(REG_0x6C, 0x00); // FIXME: Likely not needed -    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); +        for (auto addr : order2) { +            dev->interface->write_register(addr, dev->gpo.regs.find_reg(addr).value); +        } -    dev->interface->write_register(REG_0xA8, gpios[idx].ra8); -    dev->interface->write_register(REG_0xA9, gpios[idx].ra9); +        for (const auto& reg : dev->gpo.regs) { +            if (std::find(order1.begin(), order1.end(), reg.address) != order1.end()) { +                continue; +            } +            if (std::find(order2.begin(), order2.end(), reg.address) != order2.end()) { +                continue; +            } +            dev->interface->write_register(reg.address, reg.value); +        } +    }  }  /** @@ -1387,77 +1035,24 @@ 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; +    // FIXME: move to initial register list +    switch (dev->model->model_id) { +        case ModelId::CANON_LIDE_100: +        case ModelId::CANON_LIDE_200: +            dev->interface->write_register(REG_0x0B, 0x29); +            break; +        case ModelId::CANON_LIDE_700F: +            dev->interface->write_register(REG_0x0B, 0x2a); +            break; +        default: +            break;      } -  /* 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); - -  /* 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); +    // prevent further writings by bulk write register +    dev->reg.remove_reg(0x0b); + +    apply_reg_settings_to_device_write_only(*dev, dev->memory_layout.regs);  }  /* * @@ -1486,15 +1081,17 @@ void CommandSetGl847::asic_boot(Genesys_Device* dev, bool cold) const      // 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; +    if (dev->model->model_id != ModelId::CANON_5600F) { +        // Enable DRAM by setting a rising edge on bit 3 of reg 0x0b +        // The initial register write also powers on SDRAM +        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 */ -    dev->reg.init_reg(0x08, REG_0x08_CIS_LINE); -    dev->interface->write_register(0x08, dev->reg.find_reg(0x08).value); +        // TODO: remove this write +        dev->interface->write_register(0x08, dev->reg.find_reg(0x08).value); +    }      // set up end access      dev->interface->write_0x8c(0x10, 0x0b); @@ -1506,8 +1103,11 @@ void CommandSetGl847::asic_boot(Genesys_Device* dev, bool cold) const      // setup internal memory layout      gl847_init_memory_layout (dev); -    dev->reg.init_reg(0xf8, 0x01); -    dev->interface->write_register(0xf8, dev->reg.find_reg(0xf8).value); +    if (dev->model->model_id != ModelId::CANON_5600F) { +        // FIXME: move to memory layout +        dev->reg.init_reg(0xf8, 0x01); +        dev->interface->write_register(0xf8, dev->reg.find_reg(0xf8).value); +    }  }  /** @@ -1519,7 +1119,7 @@ void CommandSetGl847::init(Genesys_Device* dev) const    DBG_INIT ();      DBG_HELPER(dbg); -    sanei_genesys_asic_init(dev, 0); +    sanei_genesys_asic_init(dev);  }  void CommandSetGl847::update_hardware_sensors(Genesys_Scanner* s) const @@ -1566,517 +1166,16 @@ void CommandSetGl847::update_home_sensor_gpio(Genesys_Device& dev) const      }  } -/** @brief search for a full width black or white strip. - * This function searches for a black or white stripe across the scanning area. - * When searching backward, the searched area must completely be of the desired - * color since this area will be used for calibration which scans forward. - * @param dev scanner device - * @param forward true if searching forward, false if searching backward - * @param black true if searching for a black strip, false for a white strip - */ -void CommandSetGl847::search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, bool forward, -                                   bool black) const -{ -    DBG_HELPER_ARGS(dbg, "%s %s", black ? "black" : "white", forward ? "forward" : "reverse"); -  unsigned int pixels, lines, channels; -  Genesys_Register_Set local_reg; -  size_t size; -  unsigned int pass, count, found, x, y; -  char title[80]; - -    set_fe(dev, sensor, AFE_SET); -    scanner_stop_action(*dev); - -    // set up for a gray scan at lowest dpi -    const auto& resolution_settings = dev->model->get_resolution_settings(dev->settings.scan_method); -    unsigned dpi = resolution_settings.get_min_resolution_x(); -  channels = 1; -  /* 10 MM */ -  /* lines = (10 * dpi) / MM_PER_INCH; */ -  /* shading calibation is done with dev->motor.base_ydpi */ -  lines = (dev->model->shading_lines * dpi) / dev->motor.base_ydpi; -  pixels = (sensor.sensor_pixels * dpi) / sensor.optical_res; -    dev->set_head_pos_zero(ScanHeadId::PRIMARY); - -  local_reg = dev->reg; - -    ScanSession session; -    session.params.xres = dpi; -    session.params.yres = dpi; -    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::GRAY; -    session.params.color_filter = ColorFilter::RED; -    session.params.flags = ScanFlag::DISABLE_SHADING | -                           ScanFlag::DISABLE_GAMMA; -    if (!forward) { -        session.params.flags |= ScanFlag::REVERSE; -    } -    compute_session(dev, session, sensor); - -    size = pixels * channels * lines * (session.params.depth / 8); -    std::vector<uint8_t> data(size); - -    init_regs_for_scan_session(dev, sensor, &local_reg, session); - -    dev->interface->write_registers(local_reg); - -    begin_scan(dev, sensor, &local_reg, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("search_strip"); -        scanner_stop_action(*dev); -        return; -    } - -    wait_until_buffer_non_empty(dev); - -    // now we're on target, we can read data -    sanei_genesys_read_data_from_scanner(dev, data.data(), size); - -    scanner_stop_action(*dev); - -  pass = 0; -  if (DBG_LEVEL >= DBG_data) -    { -        std::sprintf(title, "gl847_search_strip_%s_%s%02d.pnm", -                     black ? "black" : "white", forward ? "fwd" : "bwd", pass); -        sanei_genesys_write_pnm_file(title, data.data(), session.params.depth, -                                     channels, pixels, lines); -    } - -  /* loop until strip is found or maximum pass number done */ -  found = 0; -  while (pass < 20 && !found) -    { -        dev->interface->write_registers(local_reg); - -        // now start scan -        begin_scan(dev, sensor, &local_reg, true); - -        wait_until_buffer_non_empty(dev); - -        // now we're on target, we can read data -        sanei_genesys_read_data_from_scanner(dev, data.data(), size); - -    scanner_stop_action(*dev); - -      if (DBG_LEVEL >= DBG_data) -	{ -            std::sprintf(title, "gl847_search_strip_%s_%s%02d.pnm", -                         black ? "black" : "white", -                         forward ? "fwd" : "bwd", static_cast<int>(pass)); -            sanei_genesys_write_pnm_file(title, data.data(), session.params.depth, -                                         channels, pixels, lines); -	} - -      /* search data to find black strip */ -      /* when searching forward, we only need one line of the searched color since we -       * will scan forward. But when doing backward search, we need all the area of the -       * same color */ -      if (forward) -	{ -	  for (y = 0; y < lines && !found; y++) -	    { -	      count = 0; -	      /* count of white/black pixels depending on the color searched */ -	      for (x = 0; x < pixels; x++) -		{ -		  /* when searching for black, detect white pixels */ -		  if (black && data[y * pixels + x] > 90) -		    { -		      count++; -		    } -		  /* when searching for white, detect black pixels */ -		  if (!black && data[y * pixels + x] < 60) -		    { -		      count++; -		    } -		} - -	      /* at end of line, if count >= 3%, line is not fully of the desired color -	       * so we must go to next line of the buffer */ -	      /* count*100/pixels < 3 */ -	      if ((count * 100) / pixels < 3) -		{ -		  found = 1; -		  DBG(DBG_data, "%s: strip found forward during pass %d at line %d\n", __func__, -		      pass, y); -		} -	      else -		{ -		  DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count, -		      (100 * count) / pixels); -		} -	    } -	} -      else			/* since calibration scans are done forward, we need the whole area -				   to be of the required color when searching backward */ -	{ -	  count = 0; -	  for (y = 0; y < lines; y++) -	    { -	      /* count of white/black pixels depending on the color searched */ -	      for (x = 0; x < pixels; x++) -		{ -		  /* when searching for black, detect white pixels */ -		  if (black && data[y * pixels + x] > 90) -		    { -		      count++; -		    } -		  /* when searching for white, detect black pixels */ -		  if (!black && data[y * pixels + x] < 60) -		    { -		      count++; -		    } -		} -	    } - -	  /* at end of area, if count >= 3%, area is not fully of the desired color -	   * so we must go to next buffer */ -	  if ((count * 100) / (pixels * lines) < 3) -	    { -	      found = 1; -	      DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass); -	    } -	  else -	    { -	      DBG(DBG_data, "%s: pixels=%d, count=%d (%d%%)\n", __func__, pixels, count, -		  (100 * count) / pixels); -	    } -	} -      pass++; -    } - -  if (found) -    { -      DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white"); -    } -  else -    { -        throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found", black ? "black" : "white"); -    } -} - -/** - * 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, total_size; -    int topavg, bottomavg, lines; -  int top, bottom, black_pixels, 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; -  dev->calib_pixels = sensor.sensor_pixels; -  lines=1; -    pixels= (sensor.sensor_pixels * sensor.optical_res) / sensor.optical_res; -    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_LINE_DISTANCE; -    compute_session(dev, session, sensor); - -    init_regs_for_scan_session(dev, sensor, ®s, session); - -  sanei_genesys_set_motor_power(regs, false); - -  /* allocate memory for scans */ -  total_size = pixels * channels * lines * (session.params.depth / 8);	/* colors * bytes_per_color * scan lines */ - -  std::vector<uint8_t> first_line(total_size); -  std::vector<uint8_t> second_line(total_size); - -  /* 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, ®s, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("offset_calibration"); -        return; -    } - -    sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size); -  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.data(), session.params.depth, -                                     channels, pixels, lines); -   } - -  bottomavg = dark_average (first_line.data(), 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, ®s, true); -    sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size); - -  topavg = dark_average(second_line.data(), 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, ®s, true); -        sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size); - -      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.data(), session.params.depth, -                                       channels, pixels, lines); -	} - -      avg = dark_average(second_line.data(), 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); -  int pixels; -  int total_size; -  int i, j, channels; -  int max[3]; -  float gain[3],coeff; -  int val, 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 */ -  channels = 3; - -  /* follow CKSEL */ -  if(dev->settings.xres<sensor.optical_res) -    { -        coeff = 0.9f; -    } -  else -    { -      coeff=1.0; -    } -  lines=10; -    pixels = (sensor.sensor_pixels * sensor.optical_res) / sensor.optical_res; - -    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_LINE_DISTANCE; -    compute_session(dev, session, sensor); - -    try { -        init_regs_for_scan_session(dev, sensor, ®s, 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); - -    total_size = pixels * channels * (16 / session.params.depth) * lines; - -  std::vector<uint8_t> line(total_size); - -    set_fe(dev, sensor, AFE_SET); -    begin_scan(dev, sensor, ®s, true); - -    if (is_testing_mode()) { -        dev->interface->test_checkpoint("coarse_gain_calibration"); -        scanner_stop_action(*dev); -        move_back_home(dev, true); -        return; -    } - -    sanei_genesys_read_data_from_scanner(dev, line.data(), total_size); - -    if (DBG_LEVEL >= DBG_data) { -        sanei_genesys_write_pnm_file("gl847_gain.pnm", line.data(), session.params.depth, -                                     channels, pixels, lines); -    } - -  /* average value on each channel */ -  for (j = 0; j < channels; j++) -    { -      max[j] = 0; -      for (i = pixels/4; i < (pixels*3/4); i++) -	{ -            if (dev->model->is_cis) { -                val = line[i + j * pixels]; -            } else { -                val = line[i * channels + j]; -            } - -	    max[j] += val; -	} -      max[j] = max[j] / (pixels/2); - -      gain[j] = (static_cast<float>(sensor.gain_white_ref) * coeff) / max[j]; - -      /* turn logical gain value into gain code, checking for overflow */ -        code = static_cast<int>(283 - 208 / gain[j]); -      if (code > 255) -	code = 255; -      else if (code < 0) -	code = 0; -      dev->frontend.set_gain(j, code); - -      DBG(DBG_proc, "%s: channel %d, max=%d, gain = %f, setting:%d\n", __func__, j, max[j], gain[j], -          dev->frontend.get_gain(j)); -    } - -    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 @@ -2086,14 +1185,11 @@ bool CommandSetGl847::needs_home_before_init_regs_for_scan(Genesys_Device* dev)  }  void CommandSetGl847::init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                           Genesys_Register_Set* regs, int* channels, -                                           int* total_size) const +                                           Genesys_Register_Set* regs) const  {      (void) dev;      (void) sensor;      (void) regs; -    (void) channels; -    (void) total_size;      throw SaneException("not implemented");  } @@ -2125,16 +1221,5 @@ void CommandSetGl847::eject_document(Genesys_Device* dev) const      throw SaneException("not implemented");  } -void CommandSetGl847::move_to_ta(Genesys_Device* dev) const -{ -    (void) dev; -    throw SaneException("not implemented"); -} - -std::unique_ptr<CommandSet> create_gl847_cmd_set() -{ -    return std::unique_ptr<CommandSet>(new CommandSetGl847{}); -} -  } // namespace gl847  } // namespace genesys diff --git a/backend/genesys/gl847.h b/backend/genesys/gl847.h index a51c293..aa4fb85 100644 --- a/backend/genesys/gl847.h +++ b/backend/genesys/gl847.h @@ -45,75 +45,12 @@  #define BACKEND_GENESYS_GL847_H  #include "genesys.h" -#include "command_set.h" +#include "command_set_common.h"  namespace genesys {  namespace gl847 { -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::CANON_LIDE_200, 0x02, 0xf9, 0x20, 0xff, 0x00, 0x04, 0x04, 0x00, 0x00}, -    { GpioId::CANON_LIDE_700F, 0x06, 0xdb, 0xff, 0xff, 0x80, 0x15, 0x07, 0x20, 0x10}, -    { 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 CommandSet +class CommandSetGl847 : public CommandSetCommon  {  public:      ~CommandSetGl847() override = default; @@ -123,17 +60,11 @@ public:      void init(Genesys_Device* dev) const override;      void init_regs_for_warmup(Genesys_Device* dev, const Genesys_Sensor& sensor, -                              Genesys_Register_Set* regs, int* channels, -                              int* total_size) const override; - -    void init_regs_for_coarse_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor, -                                          Genesys_Register_Set& regs) const override; +                              Genesys_Register_Set* regs) const override;      void init_regs_for_shading(Genesys_Device* dev, const Genesys_Sensor& sensor,                                 Genesys_Register_Set& regs) const override; -    void init_regs_for_scan(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; -      void init_regs_for_scan_session(Genesys_Device* dev, const Genesys_Sensor& sensor,                                      Genesys_Register_Set* reg,                                      const ScanSession& session) const override; @@ -149,8 +80,6 @@ public:      void send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& sensor) const override; -    void search_start_position(Genesys_Device* dev) const override; -      void offset_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,                              Genesys_Register_Set& regs) const override; @@ -166,8 +95,6 @@ public:      void update_hardware_sensors(struct Genesys_Scanner* s) const override; -    bool needs_update_home_sensor_gpio() const override { return true; } -      void update_home_sensor_gpio(Genesys_Device& dev) const override;      void load_document(Genesys_Device* dev) const override; @@ -176,11 +103,6 @@ public:      void eject_document(Genesys_Device* dev) const override; -    void search_strip(Genesys_Device* dev, const Genesys_Sensor& sensor, -                      bool forward, bool black) const override; - -    void move_to_ta(Genesys_Device* dev) const override; -      void send_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor, uint8_t* data,                             int size) const override; diff --git a/backend/genesys/gl847_registers.h b/backend/genesys/gl847_registers.h index 0603a6a..aa3d43b 100644 --- a/backend/genesys/gl847_registers.h +++ b/backend/genesys/gl847_registers.h @@ -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; diff --git a/backend/genesys/image.cpp b/backend/genesys/image.cpp index 7d386c6..793a209 100644 --- a/backend/genesys/image.cpp +++ b/backend/genesys/image.cpp @@ -45,6 +45,10 @@  #include "image.h" +#if defined(HAVE_TIFFIO_H) +#include <tiffio.h> +#endif +  #include <array>  namespace genesys { @@ -201,4 +205,68 @@ void convert_pixel_row_format(const std::uint8_t* in_data, PixelFormat in_format      }  } +void write_tiff_file(const std::string& filename, const void* data, int depth, int channels, +                     int pixels_per_line, int lines) +{ +    DBG_HELPER_ARGS(dbg, "depth=%d, channels=%d, ppl=%d, lines=%d", depth, channels, +                    pixels_per_line, lines); +#if defined(HAVE_TIFFIO_H) +    auto image = TIFFOpen(filename.c_str(), "w"); +    if (!image) { +        dbg.log(DBG_error, "Could not save debug image"); +        return; +    } +    TIFFSetField(image, TIFFTAG_IMAGEWIDTH, pixels_per_line); +    TIFFSetField(image, TIFFTAG_IMAGELENGTH, lines); +    TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, depth); +    TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, channels); +    if (channels > 1) { +        TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); +    } else { +        TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); +    } +    TIFFSetField(image, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); +    TIFFSetField(image, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + +    std::size_t bytes_per_line = (pixels_per_line * channels * depth + 7) / 8; +    const std::uint8_t* data_ptr = reinterpret_cast<const std::uint8_t*>(data); + +    // we don't need to handle endian because libtiff will handle that +    for (int iline = 0; iline < lines; ++iline) { +        const auto* line_data = data_ptr + bytes_per_line * iline; +        TIFFWriteScanline(image, const_cast<std::uint8_t*>(line_data), iline, 0); +    } +    TIFFClose(image); + +#else +    dbg.log(DBG_error, "Backend has been built without TIFF library support. " +            "Debug images will not be saved"); +#endif +} + +bool is_supported_write_tiff_file_image_format(PixelFormat format) +{ +    switch (format) { +        case PixelFormat::I1: +        case PixelFormat::RGB111: +        case PixelFormat::I8: +        case PixelFormat::RGB888: +        case PixelFormat::I16: +        case PixelFormat::RGB161616: +            return true; +        default: +            return false; +    } +} + +void write_tiff_file(const std::string& filename, const Image& image) +{ +    if (!is_supported_write_tiff_file_image_format(image.get_format())) { +        throw SaneException("Unsupported format %d", static_cast<unsigned>(image.get_format())); +    } + +    write_tiff_file(filename, image.get_row_ptr(0), get_pixel_format_depth(image.get_format()), +                    get_pixel_channels(image.get_format()), image.get_width(), image.get_height()); +} +  } // namespace genesys diff --git a/backend/genesys/image.h b/backend/genesys/image.h index c96b1bb..798594e 100644 --- a/backend/genesys/image.h +++ b/backend/genesys/image.h @@ -82,6 +82,11 @@ private:  void convert_pixel_row_format(const std::uint8_t* in_data, PixelFormat in_format,                                std::uint8_t* out_data, PixelFormat out_format, std::size_t count); +void write_tiff_file(const std::string& filename, const void* data, int depth, +                     int channels, int pixels_per_line, int lines); + +void write_tiff_file(const std::string& filename, const Image& image); +  } // namespace genesys  #endif // ifndef BACKEND_GENESYS_IMAGE_H diff --git a/backend/genesys/image_buffer.cpp b/backend/genesys/image_buffer.cpp index 07c6987..c4f8019 100644 --- a/backend/genesys/image_buffer.cpp +++ b/backend/genesys/image_buffer.cpp @@ -45,13 +45,13 @@  #include "image_buffer.h"  #include "image.h" +#include "utilities.h"  namespace genesys {  ImageBuffer::ImageBuffer(std::size_t size, ProducerCallback producer) :      producer_{producer}, -    size_{size}, -    buffer_offset_{size} +    size_{size}  {      buffer_.resize(size_);  } @@ -81,123 +81,30 @@ bool ImageBuffer::get_data(std::size_t size, std::uint8_t* out_data)      bool got_data = true;      do {          buffer_offset_ = 0; -        got_data &= producer_(size_, buffer_.data()); -        copy_buffer(); -    } while(out_data < out_data_end && got_data); - -    return got_data; -} - -void FakeBufferModel::push_step(std::size_t buffer_size, std::size_t row_bytes) -{ -    sizes_.push_back(buffer_size); -    available_sizes_.push_back(0); -    row_bytes_.push_back(row_bytes); -} - -std::size_t FakeBufferModel::available_space() const -{ -    if (sizes_.empty()) -        throw SaneException("Model has not been setup"); -    return sizes_.front() - available_sizes_.front(); -} - -void FakeBufferModel::simulate_read(std::size_t size) -{ -    if (sizes_.empty()) { -        throw SaneException("Model has not been setup"); -    } -    if (available_space() < size) { -        throw SaneException("Attempted to simulate read of too much memory"); -    } - -    available_sizes_.front() += size; - -    for (unsigned i = 1; i < sizes_.size(); ++i) { -        auto avail_src = available_sizes_[i - 1]; -        auto avail_dst = sizes_[i] - available_sizes_[i]; - -        auto avail = (std::min(avail_src, avail_dst) / row_bytes_[i]) * row_bytes_[i]; -        available_sizes_[i - 1] -= avail; -        available_sizes_[i] += avail; -    } -    available_sizes_.back() = 0; -} - -ImageBufferGenesysUsb::ImageBufferGenesysUsb(std::size_t total_size, -                                             const FakeBufferModel& buffer_model, -                                             ProducerCallback producer) : -    remaining_size_{total_size}, -    buffer_model_{buffer_model}, -    producer_{producer} -{} +        std::size_t size_to_read = size_; +        if (remaining_size_ != BUFFER_SIZE_UNSET) { +            size_to_read = std::min<std::uint64_t>(size_to_read, remaining_size_); +            remaining_size_ -= size_to_read; +        } -bool ImageBufferGenesysUsb::get_data(std::size_t size, std::uint8_t* out_data) -{ -    const std::uint8_t* out_data_end = out_data + size; +        std::size_t aligned_size_to_read = size_to_read; +        if (remaining_size_ == 0 && last_read_multiple_ != BUFFER_SIZE_UNSET) { +            aligned_size_to_read = align_multiple_ceil(size_to_read, last_read_multiple_); +        } -    auto copy_buffer = [&]() -    { -        std::size_t bytes_copy = std::min<std::size_t>(out_data_end - out_data, available()); -        std::memcpy(out_data, buffer_.data() + buffer_offset_, bytes_copy); -        out_data += bytes_copy; -        buffer_offset_ += bytes_copy; -    }; +        got_data &= producer_(aligned_size_to_read, buffer_.data()); +        curr_size_ = size_to_read; -    // first, read remaining data from buffer -    if (available() > 0) {          copy_buffer(); -    } - -    if (out_data == out_data_end) { -        return true; -    } - -    // now the buffer is empty and there's more data to be read -    do { -        if (remaining_size_ == 0) -            return false; -        auto bytes_to_read = get_read_size(); -        buffer_offset_ = 0; -        buffer_end_ = bytes_to_read; -        buffer_.resize(bytes_to_read); - -        producer_(bytes_to_read, buffer_.data()); - -        if (remaining_size_ < bytes_to_read) { -            remaining_size_ = 0; -        } else { -            remaining_size_ -= bytes_to_read; +        if (remaining_size_ == 0 && out_data < out_data_end) { +            got_data = false;          } -        copy_buffer(); -    } while(out_data < out_data_end); -    return true; -} - -std::size_t ImageBufferGenesysUsb::get_read_size() -{ -    std::size_t size = buffer_model_.available_space(); +    } while (out_data < out_data_end && got_data); -    // never read an odd number. exception: last read -    // the chip internal counter does not count half words. -    size &= ~1; - -    // Some setups need the reads to be multiples of 256 bytes -    size &= ~0xff; - -    if (remaining_size_ < size) { -        size = remaining_size_; -        /*round up to a multiple of 256 bytes */ -        size += (size & 0xff) ? 0x100 : 0x00; -        size &= ~0xff; -    } - -    buffer_model_.simulate_read(size); - -    return size; +    return got_data;  }  } // namespace genesys diff --git a/backend/genesys/image_buffer.h b/backend/genesys/image_buffer.h index 43c3eb7..1910244 100644 --- a/backend/genesys/image_buffer.h +++ b/backend/genesys/image_buffer.h @@ -56,72 +56,33 @@ class ImageBuffer  {  public:      using ProducerCallback = std::function<bool(std::size_t size, std::uint8_t* out_data)>; +    static constexpr std::uint64_t BUFFER_SIZE_UNSET = std::numeric_limits<std::uint64_t>::max();      ImageBuffer() {}      ImageBuffer(std::size_t size, ProducerCallback producer); -    std::size_t size() const { return size_; } -    std::size_t available() const { return size_ - buffer_offset_; } +    std::size_t available() const { return curr_size_ - buffer_offset_; } -    bool get_data(std::size_t size, std::uint8_t* out_data); - -private: -    ProducerCallback producer_; -    std::size_t size_ = 0; - -    std::size_t buffer_offset_ = 0; -    std::vector<std::uint8_t> buffer_; -}; - -class FakeBufferModel -{ -public: -    FakeBufferModel() {} - -    void push_step(std::size_t buffer_size, std::size_t row_bytes); - -    std::size_t available_space() const; - -    void simulate_read(std::size_t size); +    // allows adjusting the amount of data left so that we don't do a full size read from the +    // producer on the last iteration. Set to BUFFER_SIZE_UNSET to ignore buffer size. +    std::uint64_t remaining_size() const { return remaining_size_; } +    void set_remaining_size(std::uint64_t bytes) { remaining_size_ = bytes; } -private: -    std::vector<std::size_t> sizes_; -    std::vector<std::size_t> available_sizes_; -    std::vector<std::size_t> row_bytes_; -}; - -// This class is similar to ImageBuffer, but preserves historical peculiarities of buffer handling -// in the backend to preserve exact behavior -class ImageBufferGenesysUsb -{ -public: -    using ProducerCallback = std::function<void(std::size_t size, std::uint8_t* out_data)>; - -    ImageBufferGenesysUsb() {} -    ImageBufferGenesysUsb(std::size_t total_size, const FakeBufferModel& buffer_model, -                          ProducerCallback producer); - -    std::size_t remaining_size() const { return remaining_size_; } - -    void set_remaining_size(std::size_t bytes) { remaining_size_ = bytes; } - -    std::size_t available() const { return buffer_end_ - buffer_offset_; } +    // May be used to force the last read to be rounded up of a certain number of bytes +    void set_last_read_multiple(std::uint64_t bytes) { last_read_multiple_ = bytes; }      bool get_data(std::size_t size, std::uint8_t* out_data);  private: +    ProducerCallback producer_; +    std::size_t size_ = 0; +    std::size_t curr_size_ = 0; -    std::size_t get_read_size(); - -    std::size_t remaining_size_ = 0; +    std::uint64_t remaining_size_ = BUFFER_SIZE_UNSET; +    std::uint64_t last_read_multiple_ = BUFFER_SIZE_UNSET;      std::size_t buffer_offset_ = 0; -    std::size_t buffer_end_ = 0;      std::vector<std::uint8_t> buffer_; - -    FakeBufferModel buffer_model_; - -    ProducerCallback producer_;  };  } // namespace genesys diff --git a/backend/genesys/image_pipeline.cpp b/backend/genesys/image_pipeline.cpp index c01b7f4..8d67be9 100644 --- a/backend/genesys/image_pipeline.cpp +++ b/backend/genesys/image_pipeline.cpp @@ -53,15 +53,6 @@ namespace genesys {  ImagePipelineNode::~ImagePipelineNode() {} -std::size_t ImagePipelineNodeBytesSource::consume_remaining_bytes(std::size_t bytes) -{ -    if (bytes > remaining_bytes_) { -        bytes = remaining_bytes_; -    } -    remaining_bytes_ -= bytes; -    return bytes; -} -  bool ImagePipelineNodeCallableSource::get_next_row_data(std::uint8_t* out_data)  {      bool got_data = producer_(get_row_bytes(), out_data); @@ -78,7 +69,7 @@ ImagePipelineNodeBufferedCallableSource::ImagePipelineNodeBufferedCallableSource      format_{format},      buffer_{input_batch_size, producer}  { -    set_remaining_bytes(height_ * get_row_bytes()); +    buffer_.set_remaining_size(height_ * get_row_bytes());  }  bool ImagePipelineNodeBufferedCallableSource::get_next_row_data(std::uint8_t* out_data) @@ -92,13 +83,7 @@ bool ImagePipelineNodeBufferedCallableSource::get_next_row_data(std::uint8_t* ou      bool got_data = true; -    auto row_bytes = get_row_bytes(); -    auto bytes_to_ask = consume_remaining_bytes(row_bytes); -    if (bytes_to_ask < row_bytes) { -        got_data = false; -    } - -    got_data &= buffer_.get_data(bytes_to_ask, out_data); +    got_data &= buffer_.get_data(get_row_bytes(), out_data);      curr_row_++;      if (!got_data) {          eof_ = true; @@ -106,37 +91,6 @@ bool ImagePipelineNodeBufferedCallableSource::get_next_row_data(std::uint8_t* ou      return got_data;  } - -ImagePipelineNodeBufferedGenesysUsb::ImagePipelineNodeBufferedGenesysUsb( -        std::size_t width, std::size_t height, PixelFormat format, std::size_t total_size, -        const FakeBufferModel& buffer_model, ProducerCallback producer) : -    width_{width}, -    height_{height}, -    format_{format}, -    buffer_{total_size, buffer_model, producer} -{ -    set_remaining_bytes(total_size); -} - -bool ImagePipelineNodeBufferedGenesysUsb::get_next_row_data(std::uint8_t* out_data) -{ -    if (remaining_bytes() != buffer_.remaining_size() + buffer_.available()) { -        buffer_.set_remaining_size(remaining_bytes() - buffer_.available()); -    } -    bool got_data = true; - -    std::size_t row_bytes = get_row_bytes(); -    std::size_t ask_bytes = consume_remaining_bytes(row_bytes); -    if (ask_bytes < row_bytes) { -        got_data = false; -    } -    got_data &= buffer_.get_data(ask_bytes, out_data); -    if (!got_data) { -        eof_ = true; -    } -    return got_data; -} -  ImagePipelineNodeArraySource::ImagePipelineNodeArraySource(std::size_t width, std::size_t height,                                                             PixelFormat format,                                                             std::vector<std::uint8_t> data) : @@ -151,7 +105,6 @@ ImagePipelineNodeArraySource::ImagePipelineNodeArraySource(std::size_t width, st          throw SaneException("The given array is too small (%zu bytes). Need at least %zu",                              data_.size(), size);      } -    set_remaining_bytes(size);  }  bool ImagePipelineNodeArraySource::get_next_row_data(std::uint8_t* out_data) @@ -161,21 +114,11 @@ bool ImagePipelineNodeArraySource::get_next_row_data(std::uint8_t* out_data)          return false;      } -    bool got_data = true; -      auto row_bytes = get_row_bytes(); -    auto bytes_to_ask = consume_remaining_bytes(row_bytes); -    if (bytes_to_ask < row_bytes) { -        got_data = false; -    } - -    std::memcpy(out_data, data_.data() + get_row_bytes() * next_row_, bytes_to_ask); +    std::memcpy(out_data, data_.data() + row_bytes * next_row_, row_bytes);      next_row_++; -    if (!got_data) { -        eof_ = true; -    } -    return got_data; +    return true;  } @@ -319,6 +262,50 @@ bool ImagePipelineNodeSwap16BitEndian::get_next_row_data(std::uint8_t* out_data)      return got_data;  } +ImagePipelineNodeInvert::ImagePipelineNodeInvert(ImagePipelineNode& source) : +    source_(source) +{ +} + +bool ImagePipelineNodeInvert::get_next_row_data(std::uint8_t* out_data) +{ +    bool got_data = source_.get_next_row_data(out_data); +    auto num_values = get_width() * get_pixel_channels(source_.get_format()); +    auto depth = get_pixel_format_depth(source_.get_format()); + +    switch (depth) { +        case 16: { +            auto* data = reinterpret_cast<std::uint16_t*>(out_data); +            for (std::size_t i = 0; i < num_values; ++i) { +                *data = 0xffff - *data; +                data++; +            } +            break; +        } +        case 8: { +            auto* data = out_data; +            for (std::size_t i = 0; i < num_values; ++i) { +                *data = 0xff - *data; +                data++; +            } +            break; +        } +        case 1: { +            auto* data = out_data; +            auto num_bytes = (num_values + 7) / 8; +            for (std::size_t i = 0; i < num_bytes; ++i) { +                *data = ~*data; +                data++; +            } +            break; +        } +        default: +            throw SaneException("Unsupported pixel depth"); +    } + +    return got_data; +} +  ImagePipelineNodeMergeMonoLines::ImagePipelineNodeMergeMonoLines(ImagePipelineNode& source,                                                                   ColorOrder color_order) :      source_(source), @@ -456,6 +443,12 @@ ImagePipelineNodeComponentShiftLines::ImagePipelineNodeComponentShiftLines(                                  static_cast<unsigned>(source.get_format()));      }      extra_height_ = *std::max_element(channel_shifts_.begin(), channel_shifts_.end()); +    height_ = source_.get_height(); +    if (extra_height_ > height_) { +        height_ = 0; +    } else { +        height_ -= extra_height_; +    }  }  bool ImagePipelineNodeComponentShiftLines::get_next_row_data(std::uint8_t* out_data) @@ -492,18 +485,13 @@ ImagePipelineNodePixelShiftLines::ImagePipelineNodePixelShiftLines(      pixel_shifts_{shifts},      buffer_{get_row_bytes()}  { -    DBG_HELPER(dbg); -    DBG(DBG_proc, "%s: shifts={", __func__); -    for (auto el : pixel_shifts_) { -        DBG(DBG_proc, " %zu", el); -    } -    DBG(DBG_proc, " }\n"); - -    if (pixel_shifts_.size() > MAX_SHIFTS) { -        throw SaneException("Unsupported number of shift configurations %zu", pixel_shifts_.size()); -    } -      extra_height_ = *std::max_element(pixel_shifts_.begin(), pixel_shifts_.end()); +    height_ = source_.get_height(); +    if (extra_height_ > height_) { +        height_ = 0; +    } else { +        height_ -= extra_height_; +    }  }  bool ImagePipelineNodePixelShiftLines::get_next_row_data(std::uint8_t* out_data) @@ -521,7 +509,8 @@ bool ImagePipelineNodePixelShiftLines::get_next_row_data(std::uint8_t* out_data)      auto format = get_format();      auto shift_count = pixel_shifts_.size(); -    std::array<std::uint8_t*, MAX_SHIFTS> rows; +    std::vector<std::uint8_t*> rows; +    rows.resize(shift_count, nullptr);      for (std::size_t irow = 0; irow < shift_count; ++irow) {          rows[irow] = buffer_.get_row_ptr(pixel_shifts_[irow]); @@ -536,6 +525,63 @@ bool ImagePipelineNodePixelShiftLines::get_next_row_data(std::uint8_t* out_data)      return got_data;  } +ImagePipelineNodePixelShiftColumns::ImagePipelineNodePixelShiftColumns( +        ImagePipelineNode& source, const std::vector<std::size_t>& shifts) : +    source_(source), +    pixel_shifts_{shifts} +{ +    width_ = source_.get_width(); +    extra_width_ = compute_pixel_shift_extra_width(width_, pixel_shifts_); +    if (extra_width_ > width_) { +        width_ = 0; +    } else { +        width_ -= extra_width_; +    } +    temp_buffer_.resize(source_.get_row_bytes()); +} + +bool ImagePipelineNodePixelShiftColumns::get_next_row_data(std::uint8_t* out_data) +{ +    if (width_ == 0) { +        throw SaneException("Attempt to read zero-width line"); +    } +    bool got_data = source_.get_next_row_data(temp_buffer_.data()); + +    auto format = get_format(); +    auto shift_count = pixel_shifts_.size(); + +    for (std::size_t x = 0, width = get_width(); x < width; x += shift_count) { +        for (std::size_t ishift = 0; ishift < shift_count && x + ishift < width; ishift++) { +            RawPixel pixel = get_raw_pixel_from_row(temp_buffer_.data(), x + pixel_shifts_[ishift], +                                                    format); +            set_raw_pixel_to_row(out_data, x + ishift, pixel, format); +        } +    } +    return got_data; +} + + +std::size_t compute_pixel_shift_extra_width(std::size_t source_width, +                                            const std::vector<std::size_t>& shifts) +{ +    // we iterate across pixel shifts and find the pixel that needs the maximum shift according to +    // source_width. +    int group_size = shifts.size(); +    int non_filled_group = source_width % shifts.size(); +    int extra_width = 0; + +    for (int i = 0; i < group_size; ++i) { +        int shift_groups = shifts[i] / group_size; +        int shift_rem = shifts[i] % group_size; + +        if (shift_rem < non_filled_group) { +            shift_groups--; +        } +        extra_width = std::max(extra_width, shift_groups * group_size + non_filled_group - i); +    } +    return extra_width; +} +  ImagePipelineNodeExtract::ImagePipelineNodeExtract(ImagePipelineNode& source,                                                     std::size_t offset_x, std::size_t offset_y,                                                     std::size_t width, std::size_t height) : @@ -666,16 +712,21 @@ bool ImagePipelineNodeExtract::get_next_row_data(std::uint8_t* out_data)  ImagePipelineNodeCalibrate::ImagePipelineNodeCalibrate(ImagePipelineNode& source,                                                         const std::vector<std::uint16_t>& bottom, -                                                       const std::vector<std::uint16_t>& top) : +                                                       const std::vector<std::uint16_t>& top, +                                                       std::size_t x_start) :      source_{source}  { -    auto size = std::min(bottom.size(), top.size()); +    std::size_t size = 0; +    if (bottom.size() >= x_start && top.size() >= x_start) { +        size = std::min(bottom.size() - x_start, top.size() - x_start); +    } +      offset_.reserve(size);      multiplier_.reserve(size);      for (std::size_t i = 0; i < size; ++i) { -        offset_.push_back(bottom[i] / 65535.0f); -        multiplier_.push_back(65535.0f / (top[i] - bottom[i])); +        offset_.push_back(bottom[i + x_start] / 65535.0f); +        multiplier_.push_back(65535.0f / (top[i + x_start] - bottom[i + x_start]));      }  } @@ -729,10 +780,8 @@ ImagePipelineNodeDebug::~ImagePipelineNodeDebug()          auto format = get_format();          buffer_.linearize(); -        sanei_genesys_write_pnm_file(path_.c_str(), buffer_.get_front_row_ptr(), -                                     get_pixel_format_depth(format), -                                     get_pixel_channels(format), -                                     get_width(), buffer_.height()); +        write_tiff_file(path_, buffer_.get_front_row_ptr(), get_pixel_format_depth(format), +                        get_pixel_channels(format), get_width(), buffer_.height());      });  } diff --git a/backend/genesys/image_pipeline.h b/backend/genesys/image_pipeline.h index 2986837..d4aef49 100644 --- a/backend/genesys/image_pipeline.h +++ b/backend/genesys/image_pipeline.h @@ -75,18 +75,6 @@ public:      virtual bool get_next_row_data(std::uint8_t* out_data) = 0;  }; -class ImagePipelineNodeBytesSource : public ImagePipelineNode -{ -public: -    std::size_t remaining_bytes() const { return remaining_bytes_; } -    void set_remaining_bytes(std::size_t bytes) { remaining_bytes_ = bytes; } - -    std::size_t consume_remaining_bytes(std::size_t bytes); - -private: -    std::size_t remaining_bytes_ = 0; -}; -  // A pipeline node that produces data from a callable  class ImagePipelineNodeCallableSource : public ImagePipelineNode  { @@ -118,7 +106,7 @@ private:  };  // A pipeline node that produces data from a callable requesting fixed-size chunks. -class ImagePipelineNodeBufferedCallableSource : public ImagePipelineNodeBytesSource +class ImagePipelineNodeBufferedCallableSource : public ImagePipelineNode  {  public:      using ProducerCallback = std::function<bool(std::size_t size, std::uint8_t* out_data)>; @@ -135,8 +123,9 @@ public:      bool get_next_row_data(std::uint8_t* out_data) override; -    std::size_t buffer_size() const { return buffer_.size(); } -    std::size_t buffer_available() const { return buffer_.available(); } +    std::size_t remaining_bytes() const { return buffer_.remaining_size(); } +    void set_remaining_bytes(std::size_t bytes) { buffer_.set_remaining_size(bytes); } +    void set_last_read_multiple(std::size_t bytes) { buffer_.set_last_read_multiple(bytes); }  private:      ProducerCallback producer_; @@ -150,39 +139,8 @@ private:      ImageBuffer buffer_;  }; -class ImagePipelineNodeBufferedGenesysUsb : public ImagePipelineNodeBytesSource -{ -public: -    using ProducerCallback = std::function<void(std::size_t size, std::uint8_t* out_data)>; - -    ImagePipelineNodeBufferedGenesysUsb(std::size_t width, std::size_t height, -                                        PixelFormat format, std::size_t total_size, -                                        const FakeBufferModel& buffer_model, -                                        ProducerCallback producer); - -    std::size_t get_width() const override { return width_; } -    std::size_t get_height() const override { return height_; } -    PixelFormat get_format() const override { return format_; } - -    bool eof() const override { return eof_; } - -    bool get_next_row_data(std::uint8_t* out_data) override; - -    std::size_t buffer_available() const { return buffer_.available(); } - -private: -    ProducerCallback producer_; -    std::size_t width_ = 0; -    std::size_t height_ = 0; -    PixelFormat format_ = PixelFormat::UNKNOWN; - -    bool eof_ = false; - -    ImageBufferGenesysUsb buffer_; -}; -  // A pipeline node that produces data from the given array. -class ImagePipelineNodeArraySource : public ImagePipelineNodeBytesSource +class ImagePipelineNodeArraySource : public ImagePipelineNode  {  public:      ImagePipelineNodeArraySource(std::size_t width, std::size_t height, PixelFormat format, @@ -302,7 +260,7 @@ public:                                         std::size_t pixels_per_chunk);  }; -// A pipeline that swaps bytes in 16-bit components on big-endian systems +// A pipeline that swaps bytes in 16-bit components and does nothing otherwise.  class ImagePipelineNodeSwap16BitEndian : public ImagePipelineNode  {  public: @@ -321,6 +279,23 @@ private:      bool needs_swapping_ = false;  }; +class ImagePipelineNodeInvert : public ImagePipelineNode +{ +public: +    ImagePipelineNodeInvert(ImagePipelineNode& source); + +    std::size_t get_width() const override { return source_.get_width(); } +    std::size_t get_height() const override { return source_.get_height(); } +    PixelFormat get_format() const override { return source_.get_format(); } + +    bool eof() const override { return source_.eof(); } + +    bool get_next_row_data(std::uint8_t* out_data) override; + +private: +    ImagePipelineNode& source_; +}; +  // A pipeline node that merges 3 mono lines into a color channel  class ImagePipelineNodeMergeMonoLines : public ImagePipelineNode  { @@ -377,7 +352,7 @@ public:                                           unsigned shift_r, unsigned shift_g, unsigned shift_b);      std::size_t get_width() const override { return source_.get_width(); } -    std::size_t get_height() const override { return source_.get_height() - extra_height_; } +    std::size_t get_height() const override { return height_; }      PixelFormat get_format() const override { return source_.get_format(); }      bool eof() const override { return source_.eof(); } @@ -387,23 +362,23 @@ public:  private:      ImagePipelineNode& source_;      std::size_t extra_height_ = 0; +    std::size_t height_ = 0;      std::array<unsigned, 3> channel_shifts_;      RowBuffer buffer_;  }; -// A pipeline node that shifts pixels across lines by the given offsets (performs unstaggering) +// A pipeline node that shifts pixels across lines by the given offsets (performs vertical +// unstaggering)  class ImagePipelineNodePixelShiftLines : public ImagePipelineNode  {  public: -    constexpr static std::size_t MAX_SHIFTS = 2; -      ImagePipelineNodePixelShiftLines(ImagePipelineNode& source,                                       const std::vector<std::size_t>& shifts);      std::size_t get_width() const override { return source_.get_width(); } -    std::size_t get_height() const override { return source_.get_height() - extra_height_; } +    std::size_t get_height() const override { return height_; }      PixelFormat get_format() const override { return source_.get_format(); }      bool eof() const override { return source_.eof(); } @@ -413,12 +388,44 @@ public:  private:      ImagePipelineNode& source_;      std::size_t extra_height_ = 0; +    std::size_t height_ = 0;      std::vector<std::size_t> pixel_shifts_;      RowBuffer buffer_;  }; +// A pipeline node that shifts pixels across columns by the given offsets. Each row is divided +// into pixel groups of shifts.size() pixels. For each output group starting at position xgroup, +// the i-th pixel will be set to the input pixel at position xgroup + shifts[i]. +class ImagePipelineNodePixelShiftColumns : public ImagePipelineNode +{ +public: +    ImagePipelineNodePixelShiftColumns(ImagePipelineNode& source, +                                       const std::vector<std::size_t>& shifts); + +    std::size_t get_width() const override { return width_; } +    std::size_t get_height() const override { return source_.get_height(); } +    PixelFormat get_format() const override { return source_.get_format(); } + +    bool eof() const override { return source_.eof(); } + +    bool get_next_row_data(std::uint8_t* out_data) override; + +private: +    ImagePipelineNode& source_; +    std::size_t width_ = 0; +    std::size_t extra_width_ = 0; + +    std::vector<std::size_t> pixel_shifts_; + +    std::vector<std::uint8_t> temp_buffer_; +}; + +// exposed for tests +std::size_t compute_pixel_shift_extra_width(std::size_t source_width, +                                            const std::vector<std::size_t>& shifts); +  // A pipeline node that extracts a sub-image from the image. Padding and cropping is done as needed.  // The class can't pad to the left of the image currently, as only positive offsets are accepted.  class ImagePipelineNodeExtract : public ImagePipelineNode @@ -476,7 +483,7 @@ class ImagePipelineNodeCalibrate : public ImagePipelineNode  public:      ImagePipelineNodeCalibrate(ImagePipelineNode& source, const std::vector<std::uint16_t>& bottom, -                               const std::vector<std::uint16_t>& top); +                               const std::vector<std::uint16_t>& top, std::size_t x_start);      std::size_t get_width() const override { return source_.get_width(); }      std::size_t get_height() const override { return source_.get_height(); } @@ -517,6 +524,19 @@ class ImagePipelineStack  {  public:      ImagePipelineStack() {} +    ImagePipelineStack(ImagePipelineStack&& other) +    { +        clear(); +        nodes_ = std::move(other.nodes_); +    } + +    ImagePipelineStack& operator=(ImagePipelineStack&& other) +    { +        clear(); +        nodes_ = std::move(other.nodes_); +        return *this; +    } +      ~ImagePipelineStack() { clear(); }      std::size_t get_input_width() const; @@ -536,20 +556,22 @@ public:      void clear();      template<class Node, class... Args> -    void push_first_node(Args&&... args) +    Node& push_first_node(Args&&... args)      {          if (!nodes_.empty()) {              throw SaneException("Trying to append first node when there are existing nodes");          }          nodes_.emplace_back(std::unique_ptr<Node>(new Node(std::forward<Args>(args)...))); +        return static_cast<Node&>(*nodes_.back());      }      template<class Node, class... Args> -    void push_node(Args&&... args) +    Node& push_node(Args&&... args)      {          ensure_node_exists();          nodes_.emplace_back(std::unique_ptr<Node>(new Node(*nodes_.back(),                                                             std::forward<Args>(args)...))); +        return static_cast<Node&>(*nodes_.back());      }      bool get_next_row_data(std::uint8_t* out_data) diff --git a/backend/genesys/image_pixel.h b/backend/genesys/image_pixel.h index 2dda271..aa9980e 100644 --- a/backend/genesys/image_pixel.h +++ b/backend/genesys/image_pixel.h @@ -51,6 +51,7 @@  namespace genesys { +// 16-bit values are in host endian  enum class PixelFormat  {      UNKNOWN, diff --git a/backend/genesys/low.cpp b/backend/genesys/low.cpp index 7937fcc..05ef46b 100644 --- a/backend/genesys/low.cpp +++ b/backend/genesys/low.cpp @@ -51,12 +51,23 @@  #include "gl124_registers.h"  #include "gl646_registers.h"  #include "gl841_registers.h" +#include "gl842_registers.h"  #include "gl843_registers.h"  #include "gl846_registers.h"  #include "gl847_registers.h"  #include "gl646_registers.h" +#include "gl124.h" +#include "gl646.h" +#include "gl841.h" +#include "gl842.h" +#include "gl843.h" +#include "gl846.h" +#include "gl847.h" +#include "gl646.h" +  #include <cstdio> +#include <chrono>  #include <cmath>  #include <vector> @@ -66,29 +77,17 @@  namespace genesys { -/** - * setup the hardware dependent functions - */ - -namespace gl124 { std::unique_ptr<CommandSet> create_gl124_cmd_set(); } -namespace gl646 { std::unique_ptr<CommandSet> create_gl646_cmd_set(); } -namespace gl841 { std::unique_ptr<CommandSet> create_gl841_cmd_set(); } -namespace gl843 { std::unique_ptr<CommandSet> create_gl843_cmd_set(); } -namespace gl846 { std::unique_ptr<CommandSet> create_gl846_cmd_set(); } -namespace gl847 { std::unique_ptr<CommandSet> create_gl847_cmd_set(); } - -void sanei_genesys_init_cmd_set(Genesys_Device* dev) +std::unique_ptr<CommandSet> create_cmd_set(AsicType asic_type)  { -  DBG_INIT (); -    DBG_HELPER(dbg); -    switch (dev->model->asic_type) { -        case AsicType::GL646: dev->cmd_set = gl646::create_gl646_cmd_set(); break; -        case AsicType::GL841: dev->cmd_set = gl841::create_gl841_cmd_set(); break; -        case AsicType::GL843: dev->cmd_set = gl843::create_gl843_cmd_set(); break; +    switch (asic_type) { +        case AsicType::GL646: return std::unique_ptr<CommandSet>(new gl646::CommandSetGl646{}); +        case AsicType::GL841: return std::unique_ptr<CommandSet>(new gl841::CommandSetGl841{}); +        case AsicType::GL842: return std::unique_ptr<CommandSet>(new gl842::CommandSetGl842{}); +        case AsicType::GL843: return std::unique_ptr<CommandSet>(new gl843::CommandSetGl843{});          case AsicType::GL845: // since only a few reg bits differs we handle both together -        case AsicType::GL846: dev->cmd_set = gl846::create_gl846_cmd_set(); break; -        case AsicType::GL847: dev->cmd_set = gl847::create_gl847_cmd_set(); break; -        case AsicType::GL124: dev->cmd_set = gl124::create_gl124_cmd_set(); break; +        case AsicType::GL846: return std::unique_ptr<CommandSet>(new gl846::CommandSetGl846{}); +        case AsicType::GL847: return std::unique_ptr<CommandSet>(new gl847::CommandSetGl847{}); +        case AsicType::GL124: return std::unique_ptr<CommandSet>(new gl124::CommandSetGl124{});          default: throw SaneException(SANE_STATUS_INVAL, "unknown ASIC type");      }  } @@ -108,116 +107,6 @@ void sanei_genesys_write_file(const char* filename, const std::uint8_t* data, st      std::fclose(out);  } -// Write data to a pnm file (e.g. calibration). For debugging only -// data is RGB or grey, with little endian byte order -void sanei_genesys_write_pnm_file(const char* filename, const std::uint8_t* data, int depth, -                                  int channels, int pixels_per_line, int lines) -{ -    DBG_HELPER_ARGS(dbg, "depth=%d, channels=%d, ppl=%d, lines=%d", depth, channels, -                    pixels_per_line, lines); -  int count; - -    std::FILE* out = std::fopen(filename, "w"); -  if (!out) -    { -        throw SaneException("could not open %s for writing: %s\n", filename, strerror(errno)); -    } -  if(depth==1) -    { -      fprintf (out, "P4\n%d\n%d\n", pixels_per_line, lines); -    } -  else -    { -        std::fprintf(out, "P%c\n%d\n%d\n%d\n", channels == 1 ? '5' : '6', pixels_per_line, lines, -                     static_cast<int>(std::pow(static_cast<double>(2), -                                               static_cast<double>(depth - 1)))); -    } -  if (channels == 3) -    { -      for (count = 0; count < (pixels_per_line * lines * 3); count++) -	{ -	  if (depth == 16) -	    fputc (*(data + 1), out); -	  fputc (*(data++), out); -	  if (depth == 16) -	    data++; -	} -    } -  else -    { -      if (depth==1) -        { -          pixels_per_line/=8; -        } -      for (count = 0; count < (pixels_per_line * lines); count++) -	{ -          switch (depth) -            { -              case 8: -	        fputc (*(data + count), out); -                break; -              case 16: -	        fputc (*(data + 1), out); -	        fputc (*(data), out); -	        data += 2; -                break; -              default: -                fputc(data[count], out); -                break; -            } -	} -    } -    std::fclose(out); -} - -void sanei_genesys_write_pnm_file16(const char* filename, const uint16_t* data, unsigned channels, -                                    unsigned pixels_per_line, unsigned lines) -{ -    DBG_HELPER_ARGS(dbg, "channels=%d, ppl=%d, lines=%d", channels, -                    pixels_per_line, lines); - -    std::FILE* out = std::fopen(filename, "w"); -    if (!out) { -        throw SaneException("could not open %s for writing: %s\n", filename, strerror(errno)); -    } -    std::fprintf(out, "P%c\n%d\n%d\n%d\n", channels == 1 ? '5' : '6', -                 pixels_per_line, lines, 256 * 256 - 1); - -    for (unsigned count = 0; count < (pixels_per_line * lines * channels); count++) { -        fputc(*data >> 8, out); -        fputc(*data & 0xff, out); -        data++; -    } -    std::fclose(out); -} - -bool is_supported_write_pnm_file_image_format(PixelFormat format) -{ -    switch (format) { -        case PixelFormat::I1: -        case PixelFormat::RGB111: -        case PixelFormat::I8: -        case PixelFormat::RGB888: -        case PixelFormat::I16: -        case PixelFormat::RGB161616: -            return true; -        default: -            return false; -    } -} - -void sanei_genesys_write_pnm_file(const char* filename, const Image& image) -{ -    if (!is_supported_write_pnm_file_image_format(image.get_format())) { -        throw SaneException("Unsupported format %d", static_cast<unsigned>(image.get_format())); -    } - -    sanei_genesys_write_pnm_file(filename, image.get_row_ptr(0), -                                 get_pixel_format_depth(image.get_format()), -                                 get_pixel_channels(image.get_format()), -                                 image.get_width(), image.get_height()); -} -  /* ------------------------------------------------------------------------ */  /*                  Read and write RAM, registers and AFE                   */  /* ------------------------------------------------------------------------ */ @@ -276,6 +165,7 @@ Status scanner_read_status(Genesys_Device& dev)          case AsicType::GL124: address = 0x101; break;          case AsicType::GL646:          case AsicType::GL841: +        case AsicType::GL842:          case AsicType::GL843:          case AsicType::GL845:          case AsicType::GL846: @@ -336,28 +226,23 @@ void debug_print_status(DebugMessageHelper& dbg, Status val)      dbg.vlog(DBG_info, "status=%s\n", str.str().c_str());  } -#if 0 -/* returns pixels per line from register set */ -/*candidate for moving into chip specific files?*/ -static int -genesys_pixels_per_line (Genesys_Register_Set * reg) +void scanner_register_rw_clear_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t mask)  { -    int pixels_per_line; - -    pixels_per_line = reg->get8(0x32) * 256 + reg->get8(0x33); -    pixels_per_line -= (reg->get8(0x30) * 256 + reg->get8(0x31)); +    scanner_register_rw_bits(dev, address, 0x00, mask); +} -    return pixels_per_line; +void scanner_register_rw_set_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t mask) +{ +    scanner_register_rw_bits(dev, address, mask, mask);  } -/* returns dpiset from register set */ -/*candidate for moving into chip specific files?*/ -static int -genesys_dpiset (Genesys_Register_Set * reg) +void scanner_register_rw_bits(Genesys_Device& dev, std::uint16_t address, +                              std::uint8_t value, std::uint8_t mask)  { -    return reg->get8(0x2c) * 256 + reg->get8(0x2d); +    auto reg_value = dev.interface->read_register(address); +    reg_value = (reg_value & ~mask) | (value & mask); +    dev.interface->write_register(address, reg_value);  } -#endif  /** read the number of valid words in scanner's RAM   * ie registers 42-43-44 @@ -481,7 +366,7 @@ void wait_until_has_valid_words(Genesys_Device* dev)      unsigned words = 0;      unsigned sleep_time_ms = 10; -    for (unsigned wait_ms = 0; wait_ms < 50000; wait_ms += sleep_time_ms) { +    for (unsigned wait_ms = 0; wait_ms < 70000; wait_ms += sleep_time_ms) {          sanei_genesys_read_valid_words(dev, &words);          if (words != 0)              break; @@ -516,7 +401,7 @@ Image read_unshuffled_image_from_scanner(Genesys_Device* dev, const ScanSession&                                        dev->model->line_mode_color_order);      auto width = get_pixels_from_row_bytes(format, session.output_line_bytes_raw); -    auto height = session.output_line_count * (dev->model->is_cis ? session.params.channels : 1); +    auto height = session.optical_line_count;      Image image(width, height, format); @@ -525,7 +410,7 @@ Image read_unshuffled_image_from_scanner(Genesys_Device* dev, const ScanSession&          throw SaneException("Trying to read too much data %zu (max %zu)", total_bytes, max_bytes);      }      if (total_bytes != max_bytes) { -        DBG(DBG_info, "WARNING %s: trying to read not enough data (%zu, full fill %zu\n", __func__, +        DBG(DBG_info, "WARNING %s: trying to read not enough data (%zu, full fill %zu)\n", __func__,              total_bytes, max_bytes);      } @@ -534,26 +419,138 @@ Image read_unshuffled_image_from_scanner(Genesys_Device* dev, const ScanSession&      ImagePipelineStack pipeline;      pipeline.push_first_node<ImagePipelineNodeImageSource>(image); -    if ((dev->model->flags & GENESYS_FLAG_16BIT_DATA_INVERTED) && session.params.depth == 16) { -        dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>(); +    if (session.segment_count > 1) { +        auto output_width = session.output_segment_pixel_group_count * session.segment_count; +        pipeline.push_node<ImagePipelineNodeDesegment>(output_width, dev->segment_order, +                                                       session.conseq_pixel_dist, +                                                       1, 1);      } +    if (session.params.depth == 16) { +        unsigned num_swaps = 0; +        if (has_flag(dev->model->flags, ModelFlag::SWAP_16BIT_DATA)) { +            num_swaps++; +        }  #ifdef WORDS_BIGENDIAN -    if (depth == 16) { -        dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>(); +        num_swaps++; +#endif +        if (num_swaps % 2 != 0) { +            dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>(); +        }      } + +    if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) { +        pipeline.push_node<ImagePipelineNodeInvert>(); +    } + +    if (dev->model->is_cis && session.params.channels == 3) { +        pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev->model->line_mode_color_order); +    } + +    if (pipeline.get_output_format() == PixelFormat::BGR888) { +        pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888); +    } + +    if (pipeline.get_output_format() == PixelFormat::BGR161616) { +        pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616); +    } + +    return pipeline.get_image(); +} + + +Image read_shuffled_image_from_scanner(Genesys_Device* dev, const ScanSession& session) +{ +    DBG_HELPER(dbg); + +    std::size_t total_bytes = 0; +    std::size_t pixels_per_line = 0; + +    if (dev->model->asic_type == AsicType::GL842 || +        dev->model->asic_type == AsicType::GL843 || +        dev->model->model_id == ModelId::CANON_5600F) +    { +        pixels_per_line = session.output_pixels; +    } else { +        // BUG: this selects incorrect pixel number +        pixels_per_line = session.params.pixels; +    } + +    // FIXME: the current calculation is likely incorrect on non-GL843 implementations, +    // but this needs checking. Note the extra line when computing size. +    if (dev->model->asic_type == AsicType::GL842 || +        dev->model->asic_type == AsicType::GL843 || +        dev->model->model_id == ModelId::CANON_5600F) +    { +        total_bytes = session.output_total_bytes_raw; +    } else { +        total_bytes = session.params.channels * 2 * pixels_per_line * (session.params.lines + 1); +    } + +    auto format = create_pixel_format(session.params.depth, +                                      dev->model->is_cis ? 1 : session.params.channels, +                                      dev->model->line_mode_color_order); + +    // auto width = get_pixels_from_row_bytes(format, session.output_line_bytes_raw); +    auto width = pixels_per_line; +    auto height = session.params.lines + 1; // BUG: incorrect +    if (dev->model->asic_type == AsicType::GL842 || +        dev->model->asic_type == AsicType::GL843 || +        dev->model->model_id == ModelId::CANON_5600F) +    { +        height = session.optical_line_count; +    } + +    Image image(width, height, format); + +    auto max_bytes = image.get_row_bytes() * height; +    if (total_bytes > max_bytes) { +        throw SaneException("Trying to read too much data %zu (max %zu)", total_bytes, max_bytes); +    } +    if (total_bytes != max_bytes) { +        DBG(DBG_info, "WARNING %s: trying to read not enough data (%zu, full fill %zu)\n", __func__, +            total_bytes, max_bytes); +    } + +    sanei_genesys_read_data_from_scanner(dev, image.get_row_ptr(0), total_bytes); + +    ImagePipelineStack pipeline; +    pipeline.push_first_node<ImagePipelineNodeImageSource>(image); + +    if (session.segment_count > 1) { +        auto output_width = session.output_segment_pixel_group_count * session.segment_count; +        pipeline.push_node<ImagePipelineNodeDesegment>(output_width, dev->segment_order, +                                                       session.conseq_pixel_dist, +                                                       1, 1); +    } + +    if (session.params.depth == 16) { +        unsigned num_swaps = 0; +        if (has_flag(dev->model->flags, ModelFlag::SWAP_16BIT_DATA)) { +            num_swaps++; +        } +#ifdef WORDS_BIGENDIAN +        num_swaps++;  #endif +        if (num_swaps % 2 != 0) { +            dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>(); +        } +    } + +    if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) { +        pipeline.push_node<ImagePipelineNodeInvert>(); +    }      if (dev->model->is_cis && session.params.channels == 3) { -        dev->pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev->model->line_mode_color_order); +        pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev->model->line_mode_color_order);      } -    if (dev->pipeline.get_output_format() == PixelFormat::BGR888) { -        dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888); +    if (pipeline.get_output_format() == PixelFormat::BGR888) { +        pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888);      } -    if (dev->pipeline.get_output_format() == PixelFormat::BGR161616) { -        dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616); +    if (pipeline.get_output_format() == PixelFormat::BGR161616) { +        pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616);      }      return pipeline.get_image(); @@ -600,34 +597,27 @@ void sanei_genesys_set_lamp_power(Genesys_Device* dev, const Genesys_Sensor& sen          if (dev->model->asic_type == AsicType::GL843) {              regs_set_exposure(dev->model->asic_type, regs, sensor.exposure); +        } -            // we don't actually turn on lamp on infrared scan -            if ((dev->model->model_id == ModelId::CANON_8400F || -                 dev->model->model_id == ModelId::CANON_8600F || -                 dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || -                 dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) && -                dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) -            { -                regs.find_reg(0x03).value &= ~REG_0x03_LAMPPWR; -            } +        // we don't actually turn on lamp on infrared scan +        if ((dev->model->model_id == ModelId::CANON_8400F || +             dev->model->model_id == ModelId::CANON_8600F || +             dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || +             dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I || +             dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) && +            dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) +        { +            regs.find_reg(0x03).value &= ~REG_0x03_LAMPPWR;          }      } else {          regs.find_reg(0x03).value &= ~REG_0x03_LAMPPWR;          if (dev->model->asic_type == AsicType::GL841) { -            regs_set_exposure(dev->model->asic_type, regs, {0x0101, 0x0101, 0x0101}); +            regs_set_exposure(dev->model->asic_type, regs, sanei_genesys_fixup_exposure({0, 0, 0}));              regs.set8(0x19, 0xff);          } - -        if (dev->model->asic_type == AsicType::GL843) { -            if (dev->model->model_id == ModelId::PANASONIC_KV_SS080 || -                dev->model->model_id == ModelId::HP_SCANJET_4850C || -                dev->model->model_id == ModelId::HP_SCANJET_G4010 || -                dev->model->model_id == ModelId::HP_SCANJET_G4050) -            { -                // BUG: datasheet says we shouldn't set exposure to zero -                regs_set_exposure(dev->model->asic_type, regs, {0, 0, 0}); -            } +        if (dev->model->model_id == ModelId::CANON_5600F) { +            regs_set_exposure(dev->model->asic_type, regs, sanei_genesys_fixup_exposure({0, 0, 0}));          }      }      regs.state.is_lamp_on = set; @@ -786,218 +776,144 @@ void sanei_genesys_send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& s      }  } -static unsigned align_int_up(unsigned num, unsigned alignment) -{ -    unsigned mask = alignment - 1; -    if (num & mask) -        num = (num & ~mask) + alignment; -    return num; -} - -void compute_session_buffer_sizes(AsicType asic, ScanSession& s) +void compute_session_pixel_offsets(const Genesys_Device* dev, ScanSession& s, +                                   const Genesys_Sensor& sensor)  { -    size_t line_bytes = s.output_line_bytes; -    size_t line_bytes_stagger = s.output_line_bytes; - -    if (asic != AsicType::GL646) { -        // BUG: this is historical artifact and should be removed. Note that buffer sizes affect -        // how often we request the scanner for data and thus change the USB traffic. -        line_bytes_stagger = -                multiply_by_depth_ceil(s.optical_pixels, s.params.depth) * s.params.channels; -    } - -    struct BufferConfig { -        size_t* result_size = nullptr; -        size_t lines = 0; -        size_t lines_mult = 0; -        size_t max_size = 0; // does not apply if 0 -        size_t stagger_lines = 0; - -        BufferConfig() = default; -        BufferConfig(std::size_t* rs, std::size_t l, std::size_t lm, std::size_t ms, -                     std::size_t sl) : -            result_size{rs}, -            lines{l}, -            lines_mult{lm}, -            max_size{ms}, -            stagger_lines{sl} -        {} -    }; +    if (dev->model->asic_type == AsicType::GL646) { +        s.pixel_startx += s.output_startx * sensor.full_resolution / s.params.xres; +        s.pixel_endx = s.pixel_startx + s.optical_pixels * s.full_resolution / s.optical_resolution; -    std::array<BufferConfig, 4> configs; -    if (asic == AsicType::GL124 || asic == AsicType::GL843) { -        configs = { { -            { &s.buffer_size_read, 32, 1, 0, s.max_color_shift_lines + s.num_staggered_lines }, -            { &s.buffer_size_lines, 32, 1, 0, s.max_color_shift_lines + s.num_staggered_lines }, -            { &s.buffer_size_shrink, 16, 1, 0, 0 }, -            { &s.buffer_size_out, 8, 1, 0, 0 }, -        } }; -    } else if (asic == AsicType::GL841) { -        size_t max_buf = sanei_genesys_get_bulk_max_size(asic); -        configs = { { -            { &s.buffer_size_read, 8, 2, max_buf, s.max_color_shift_lines + s.num_staggered_lines }, -            { &s.buffer_size_lines, 8, 2, max_buf, s.max_color_shift_lines + s.num_staggered_lines }, -            { &s.buffer_size_shrink, 8, 1, max_buf, 0 }, -            { &s.buffer_size_out, 8, 1, 0, 0 }, -        } }; -    } else { -        configs = { { -            { &s.buffer_size_read, 16, 1, 0, s.max_color_shift_lines + s.num_staggered_lines }, -            { &s.buffer_size_lines, 16, 1, 0, s.max_color_shift_lines + s.num_staggered_lines }, -            { &s.buffer_size_shrink, 8, 1, 0, 0 }, -            { &s.buffer_size_out, 8, 1, 0, 0 }, -        } }; -    } - -    for (BufferConfig& config : configs) { -        size_t buf_size = line_bytes * config.lines; -        if (config.max_size > 0 && buf_size > config.max_size) { -            buf_size = (config.max_size / line_bytes) * line_bytes; +    } else if (dev->model->asic_type == AsicType::GL841 || +               dev->model->asic_type == AsicType::GL842 || +               dev->model->asic_type == AsicType::GL843 || +               dev->model->asic_type == AsicType::GL845 || +               dev->model->asic_type == AsicType::GL846 || +               dev->model->asic_type == AsicType::GL847) +    { +        unsigned startx_xres = s.optical_resolution; +        if (dev->model->model_id == ModelId::CANON_5600F || +            dev->model->model_id == ModelId::CANON_LIDE_90) +        { +            if (s.output_resolution == 1200) { +                startx_xres /= 2; +            } +            if (s.output_resolution >= 2400) { +                startx_xres /= 4; +            }          } -        buf_size *= config.lines_mult; -        buf_size += line_bytes_stagger * config.stagger_lines; -        *config.result_size = buf_size; -    } -} - -void compute_session_pipeline(const Genesys_Device* dev, ScanSession& s) -{ -    auto channels = s.params.channels; -    auto depth = s.params.depth; +        s.pixel_startx = (s.output_startx * startx_xres) / s.params.xres; +        s.pixel_endx = s.pixel_startx + s.optical_pixels_raw; -    s.pipeline_needs_reorder = true; -    if (channels != 3 && depth != 16) { -        s.pipeline_needs_reorder = false; -    } -#ifndef WORDS_BIGENDIAN -    if (channels != 3 && depth == 16) { -        s.pipeline_needs_reorder = false; -    } -    if (channels == 3 && depth == 16 && !dev->model->is_cis && -        dev->model->line_mode_color_order == ColorOrder::RGB) +    } else if (dev->model->asic_type == AsicType::GL124)      { -        s.pipeline_needs_reorder = false; +        s.pixel_startx = s.output_startx * sensor.full_resolution / s.params.xres; +        s.pixel_endx = s.pixel_startx + s.optical_pixels_raw;      } -#endif -    if (channels == 3 && depth == 8 && !dev->model->is_cis && -        dev->model->line_mode_color_order == ColorOrder::RGB) + +    // align pixels to correct boundary for unstaggering +    unsigned needed_x_alignment = std::max(s.stagger_x.size(), s.stagger_y.size()); +    unsigned aligned_pixel_startx = align_multiple_floor(s.pixel_startx, needed_x_alignment); +    s.pixel_endx -= s.pixel_startx - aligned_pixel_startx; +    s.pixel_startx = aligned_pixel_startx; + +    s.pixel_startx = sensor.pixel_count_ratio.apply(s.pixel_startx); +    s.pixel_endx = sensor.pixel_count_ratio.apply(s.pixel_endx); + +    if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || +        dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I)      { -        s.pipeline_needs_reorder = false; +        s.pixel_startx = align_multiple_floor(s.pixel_startx, sensor.pixel_count_ratio.divisor()); +        s.pixel_endx = align_multiple_floor(s.pixel_endx, sensor.pixel_count_ratio.divisor());      } -    s.pipeline_needs_ccd = s.max_color_shift_lines + s.num_staggered_lines > 0; -    s.pipeline_needs_shrink = dev->settings.requested_pixels != s.output_pixels;  } -void compute_session_pixel_offsets(const Genesys_Device* dev, ScanSession& s, -                                   const Genesys_Sensor& sensor) +unsigned session_adjust_output_pixels(unsigned output_pixels, +                                      const Genesys_Device& dev, const Genesys_Sensor& sensor, +                                      unsigned output_xresolution, unsigned output_yresolution, +                                      bool adjust_output_pixels)  { -    unsigned ccd_pixels_per_system_pixel = sensor.ccd_pixels_per_system_pixel(); - -    if (dev->model->asic_type == AsicType::GL646) { - -        // startx cannot be below dummy pixel value -        s.pixel_startx = sensor.dummy_pixel; -        if (has_flag(s.params.flags, ScanFlag::USE_XCORRECTION) && sensor.ccd_start_xoffset > 0) { -            s.pixel_startx = sensor.ccd_start_xoffset; -        } -        s.pixel_startx += s.params.startx; - -        if (sensor.stagger_config.stagger_at_resolution(s.params.xres, s.params.yres) > 0) { -            s.pixel_startx |= 1; -        } - -        s.pixel_endx = s.pixel_startx + s.optical_pixels; - -        s.pixel_startx /= sensor.ccd_pixels_per_system_pixel() * s.ccd_size_divisor; -        s.pixel_endx /= sensor.ccd_pixels_per_system_pixel() * s.ccd_size_divisor; - -    } else if (dev->model->asic_type == AsicType::GL841) { -        s.pixel_startx = ((sensor.ccd_start_xoffset + s.params.startx) * s.optical_resolution) -                                / sensor.optical_res; +    bool adjust_optical_pixels = !adjust_output_pixels; +    if (dev.model->model_id == ModelId::CANON_5600F) { +        adjust_optical_pixels = true; +        adjust_output_pixels = true; +    } +    if (adjust_optical_pixels) { +        auto optical_resolution = sensor.get_optical_resolution(); -        s.pixel_startx += sensor.dummy_pixel + 1; +        // FIXME: better way would be to compute and return the required multiplier +        unsigned optical_pixels = (output_pixels * optical_resolution) / output_xresolution; -        if (s.num_staggered_lines > 0 && (s.pixel_startx & 1) == 0) { -            s.pixel_startx++; +        if (dev.model->asic_type == AsicType::GL841 || +            dev.model->asic_type == AsicType::GL842) +        { +            optical_pixels = align_multiple_ceil(optical_pixels, 2);          } -        /*  In case of SHDAREA, we need to align start on pixel average factor, startx is -            different than 0 only when calling for function to setup for scan, where shading data -            needs to be align. - -            NOTE: we can check the value of the register here, because we don't set this bit -            anywhere except in initialization. -        */ -        const uint8_t REG_0x01_SHDAREA = 0x02; -        if ((dev->reg.find_reg(0x01).value & REG_0x01_SHDAREA) != 0) { -            unsigned average_factor = s.optical_resolution / s.params.xres; -            s.pixel_startx = align_multiple_floor(s.pixel_startx, average_factor); +        if (dev.model->asic_type == AsicType::GL646 && output_xresolution == 400) { +            optical_pixels = align_multiple_floor(optical_pixels, 6);          } -        s.pixel_endx = s.pixel_startx + s.optical_pixels; - -    } else if (dev->model->asic_type == AsicType::GL843) { - -        s.pixel_startx = (s.params.startx + sensor.dummy_pixel) / ccd_pixels_per_system_pixel; -        s.pixel_endx = s.pixel_startx + s.optical_pixels / ccd_pixels_per_system_pixel; - -        s.pixel_startx /= s.hwdpi_divisor; -        s.pixel_endx /= s.hwdpi_divisor; - -        // in case of stagger we have to start at an odd coordinate -        bool stagger_starts_even = dev->model->model_id == ModelId::CANON_8400F; -        if (s.num_staggered_lines > 0) { -            if (!stagger_starts_even && (s.pixel_startx & 1) == 0) { -                s.pixel_startx++; -                s.pixel_endx++; -            } else if (stagger_starts_even && (s.pixel_startx & 1) != 0) { -                s.pixel_startx++; -                s.pixel_endx++; +        if (dev.model->asic_type == AsicType::GL843) { +            // ensure the number of optical pixels is divisible by 2. +            // In quarter-CCD mode optical_pixels is 4x larger than the actual physical number +            optical_pixels = align_multiple_ceil(optical_pixels, +                                                 2 * sensor.full_resolution / optical_resolution); +            if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200 || +                dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || +                dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || +                dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 || +                dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I || +                dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) +            { +                optical_pixels = align_multiple_ceil(optical_pixels, 16);              }          } +        output_pixels = (optical_pixels * output_xresolution) / optical_resolution; +    } -    } else if (dev->model->asic_type == AsicType::GL845 || -               dev->model->asic_type == AsicType::GL846 || -               dev->model->asic_type == AsicType::GL847) -    { -        s.pixel_startx = s.params.startx; - -        if (s.num_staggered_lines > 0) { -            s.pixel_startx |= 1; -        } - -        s.pixel_startx += sensor.ccd_start_xoffset * ccd_pixels_per_system_pixel; -        s.pixel_endx = s.pixel_startx + s.optical_pixels_raw; - -        s.pixel_startx /= s.hwdpi_divisor * s.segment_count * ccd_pixels_per_system_pixel; -        s.pixel_endx /= s.hwdpi_divisor * s.segment_count * ccd_pixels_per_system_pixel; - -    } else if (dev->model->asic_type == AsicType::GL124) { -        s.pixel_startx = s.params.startx; +    if (adjust_output_pixels) { +        // TODO: the following may no longer be needed but were applied historically. -        if (s.num_staggered_lines > 0) { -            s.pixel_startx |= 1; +        // we need an even pixels number +        // TODO invert test logic or generalize behaviour across all ASICs +        if (has_flag(dev.model->flags, ModelFlag::SIS_SENSOR) || +            dev.model->asic_type == AsicType::GL847 || +            dev.model->asic_type == AsicType::GL124 || +            dev.model->asic_type == AsicType::GL845 || +            dev.model->asic_type == AsicType::GL846 || +            dev.model->asic_type == AsicType::GL843) +        { +            if (output_xresolution <= 1200) { +                output_pixels = align_multiple_floor(output_pixels, 4); +            } else if (output_xresolution < output_yresolution) { +                // BUG: this is an artifact of the fact that the resolution was twice as large than +                // the actual resolution when scanning above the supported scanner X resolution +                output_pixels = align_multiple_floor(output_pixels, 8); +            } else { +                output_pixels = align_multiple_floor(output_pixels, 16); +            }          } -        s.pixel_startx /= ccd_pixels_per_system_pixel; -        // FIXME: should we add sensor.dummy_pxel to pixel_startx at this point? -        s.pixel_endx = s.pixel_startx + s.optical_pixels / ccd_pixels_per_system_pixel; - -        s.pixel_startx /= s.hwdpi_divisor * s.segment_count; -        s.pixel_endx /= s.hwdpi_divisor * s.segment_count; - -        std::uint32_t segcnt = (sensor.custom_regs.get_value(gl124::REG_SEGCNT) << 16) + -                               (sensor.custom_regs.get_value(gl124::REG_SEGCNT + 1) << 8) + -                                sensor.custom_regs.get_value(gl124::REG_SEGCNT + 2); -        if (s.pixel_endx == segcnt) { -            s.pixel_endx = 0; +        // corner case for true lineart for sensor with several segments or when xres is doubled +        // to match yres */ +        if (output_xresolution >= 1200 && ( +                    dev.model->asic_type == AsicType::GL124 || +                    dev.model->asic_type == AsicType::GL847 || +                    dev.session.params.xres < dev.session.params.yres)) +        { +            if (output_xresolution < output_yresolution) { +                // FIXME: this is an artifact of the fact that the resolution was twice as large than +                // the actual resolution when scanning above the supported scanner X resolution +                output_pixels = align_multiple_floor(output_pixels, 8); +            } else { +                output_pixels = align_multiple_floor(output_pixels, 16); +            }          }      } -    s.pixel_count_multiplier = sensor.pixel_count_multiplier; - -    s.pixel_startx *= sensor.pixel_count_multiplier; -    s.pixel_endx *= sensor.pixel_count_multiplier; +    return output_pixels;  }  void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Sensor& sensor) @@ -1011,68 +927,36 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se          throw SaneException("Unsupported depth setting %d", s.params.depth);      } -    unsigned ccd_pixels_per_system_pixel = sensor.ccd_pixels_per_system_pixel(); -      // compute optical and output resolutions - -    if (dev->model->asic_type == AsicType::GL843) { -        // FIXME: this may be incorrect, but need more scanners to test -        s.hwdpi_divisor = sensor.get_hwdpi_divisor_for_dpi(s.params.xres); -    } else { -        s.hwdpi_divisor = sensor.get_hwdpi_divisor_for_dpi(s.params.xres * ccd_pixels_per_system_pixel); -    } - -    s.ccd_size_divisor = sensor.get_ccd_size_divisor_for_dpi(s.params.xres); - -    if (dev->model->asic_type == AsicType::GL646) { -        s.optical_resolution = sensor.optical_res; -    } else { -        s.optical_resolution = sensor.optical_res / s.ccd_size_divisor; -    } +    s.full_resolution = sensor.full_resolution; +    s.optical_resolution = sensor.get_optical_resolution();      s.output_resolution = s.params.xres; +    s.pixel_count_ratio = sensor.pixel_count_ratio; +      if (s.output_resolution > s.optical_resolution) {          throw std::runtime_error("output resolution higher than optical resolution");      } -    // compute the number of optical pixels that will be acquired by the chip -    s.optical_pixels = (s.params.pixels * s.optical_resolution) / s.output_resolution; -    if (s.optical_pixels * s.output_resolution < s.params.pixels * s.optical_resolution) { -        s.optical_pixels++; -    } - -    if (dev->model->asic_type == AsicType::GL841) { -        if (s.optical_pixels & 1) -            s.optical_pixels++; -    } - -    if (dev->model->asic_type == AsicType::GL646 && s.params.xres == 400) { -        s.optical_pixels = (s.optical_pixels / 6) * 6; -    } +    s.output_pixels = session_adjust_output_pixels(s.params.pixels, *dev, sensor, +                                                   s.params.xres, s.params.yres, false); -    if (dev->model->asic_type == AsicType::GL843) { -        // ensure the number of optical pixels is divisible by 2. -        // In quarter-CCD mode optical_pixels is 4x larger than the actual physical number -        s.optical_pixels = align_int_up(s.optical_pixels, 2 * s.ccd_size_divisor); +    // Compute the number of optical pixels that will be acquired by the chip. +    // The necessary alignment requirements have already been computed by +    // get_session_output_pixels_multiplier +    s.optical_pixels = (s.output_pixels * s.optical_resolution) / s.output_resolution; -        if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I || -            dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 || -            dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) -        { -            s.optical_pixels = align_int_up(s.optical_pixels, 16); -        } -    } +    if (static_cast<int>(s.params.startx) + sensor.output_pixel_offset < 0) +        throw SaneException("Invalid sensor.output_pixel_offset"); +    s.output_startx = static_cast<unsigned>( +                static_cast<int>(s.params.startx) + sensor.output_pixel_offset); -    // after all adjustments on the optical pixels have been made, compute the number of pixels -    // to retrieve from the chip -    s.output_pixels = (s.optical_pixels * s.output_resolution) / s.optical_resolution; +    s.stagger_x = sensor.stagger_x; +    s.stagger_y = sensor.stagger_y; -    // Note: staggering is not applied for calibration. Staggering starts at 2400 dpi      s.num_staggered_lines = 0; -    if (!has_flag(s.params.flags, ScanFlag::IGNORE_LINE_DISTANCE)) -    { -        s.num_staggered_lines = sensor.stagger_config.stagger_at_resolution(s.params.xres, -                                                                            s.params.yres); +    if (!has_flag(s.params.flags, ScanFlag::IGNORE_STAGGER_OFFSET)) { +        s.num_staggered_lines = s.stagger_y.max_shift() * s.params.yres / s.params.xres;      }      s.color_shift_lines_r = dev->model->ld_shift_r; @@ -1091,12 +975,14 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se      s.color_shift_lines_b = (s.color_shift_lines_b * s.params.yres) / dev->motor.base_ydpi;      s.max_color_shift_lines = 0; -    if (s.params.channels > 1 && !has_flag(s.params.flags, ScanFlag::IGNORE_LINE_DISTANCE)) { +    if (s.params.channels > 1 && !has_flag(s.params.flags, ScanFlag::IGNORE_COLOR_OFFSET)) {          s.max_color_shift_lines = std::max(s.color_shift_lines_r, std::max(s.color_shift_lines_g,                                                                             s.color_shift_lines_b));      }      s.output_line_count = s.params.lines + s.max_color_shift_lines + s.num_staggered_lines; +    s.optical_line_count = dev->model->is_cis ? s.output_line_count * s.params.channels +                                              : s.output_line_count;      s.output_channel_bytes = multiply_by_depth_ceil(s.output_pixels, s.params.depth);      s.output_line_bytes = s.output_channel_bytes * s.params.channels; @@ -1107,29 +993,62 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se      s.output_line_bytes_raw = s.output_line_bytes;      s.conseq_pixel_dist = 0; -    if (dev->model->asic_type == AsicType::GL845 || -        dev->model->asic_type == AsicType::GL846 || -        dev->model->asic_type == AsicType::GL847) +    // FIXME: Use ModelFlag::SIS_SENSOR +    if ((dev->model->asic_type == AsicType::GL845 || +         dev->model->asic_type == AsicType::GL846 || +         dev->model->asic_type == AsicType::GL847) && +        dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7400 && +        dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_8200I)      {          if (s.segment_count > 1) {              s.conseq_pixel_dist = sensor.segment_size; -            // in case of multi-segments sensor, we have to add the width of the sensor crossed by -            // the scan area -            unsigned extra_segment_scan_area = align_multiple_ceil(s.conseq_pixel_dist, 2); -            extra_segment_scan_area *= s.segment_count - 1; -            extra_segment_scan_area *= s.hwdpi_divisor * s.segment_count; -            extra_segment_scan_area *= ccd_pixels_per_system_pixel; +            // in case of multi-segments sensor, we have expand the scan area to sensor boundary +            if (dev->model->model_id == ModelId::CANON_5600F) { +                unsigned startx_xres = s.optical_resolution; +                if (dev->model->model_id == ModelId::CANON_5600F) { +                    if (s.output_resolution == 1200) { +                        startx_xres /= 2; +                    } +                    if (s.output_resolution >= 2400) { +                        startx_xres /= 4; +                    } +                } +                unsigned optical_startx = s.output_startx * startx_xres / s.params.xres; +                unsigned optical_endx = optical_startx + s.optical_pixels; -            s.optical_pixels_raw += extra_segment_scan_area; +                unsigned multi_segment_size_output = s.segment_count * s.conseq_pixel_dist; +                unsigned multi_segment_size_optical = +                        (multi_segment_size_output * s.optical_resolution) / s.output_resolution; + +                optical_endx = align_multiple_ceil(optical_endx, multi_segment_size_optical); +                s.optical_pixels_raw = optical_endx - optical_startx; +                s.optical_pixels_raw = align_multiple_floor(s.optical_pixels_raw, +                                                            4 * s.optical_resolution / s.output_resolution); +            } else { +                // BUG: the following code will likely scan too much. Use the CANON_5600F approach +                unsigned extra_segment_scan_area = align_multiple_ceil(s.conseq_pixel_dist, 2); +                extra_segment_scan_area *= s.segment_count - 1; +                extra_segment_scan_area = s.pixel_count_ratio.apply_inverse(extra_segment_scan_area); + +                s.optical_pixels_raw += extra_segment_scan_area; +            }          } -        s.output_line_bytes_raw = multiply_by_depth_ceil( -                    (s.optical_pixels_raw * s.output_resolution) / sensor.optical_res / s.segment_count, -                    s.params.depth); +        if (dev->model->model_id == ModelId::CANON_5600F) { +            auto output_pixels_raw = (s.optical_pixels_raw * s.output_resolution) / s.optical_resolution; +            auto output_channel_bytes_raw = multiply_by_depth_ceil(output_pixels_raw, s.params.depth); +            s.output_line_bytes_raw = output_channel_bytes_raw * s.params.channels; +        } else { +            s.output_line_bytes_raw = multiply_by_depth_ceil( +                        (s.optical_pixels_raw * s.output_resolution) / sensor.full_resolution / s.segment_count, +                        s.params.depth); +        }      } -    if (dev->model->asic_type == AsicType::GL841) { +    if (dev->model->asic_type == AsicType::GL841 || +        dev->model->asic_type == AsicType::GL842) +    {          if (dev->model->is_cis) {              s.output_line_bytes_raw = s.output_channel_bytes;          } @@ -1139,27 +1058,43 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se          if (dev->model->is_cis) {              s.output_line_bytes_raw = s.output_channel_bytes;          } -        s.conseq_pixel_dist = s.output_pixels / s.ccd_size_divisor / s.segment_count; +        s.conseq_pixel_dist = s.output_pixels / (s.full_resolution / s.optical_resolution) / s.segment_count;      } -    if (dev->model->asic_type == AsicType::GL843) { -        s.conseq_pixel_dist = s.output_pixels / s.segment_count; +    if (dev->model->asic_type == AsicType::GL842 || +        dev->model->asic_type == AsicType::GL843) +    { +        if (dev->model->is_cis) { +            if (s.segment_count > 1) { +                s.conseq_pixel_dist = sensor.segment_size; +            } +        } else { +            s.conseq_pixel_dist = s.output_pixels / s.segment_count; +        }      }      s.output_segment_pixel_group_count = 0;      if (dev->model->asic_type == AsicType::GL124 || +        dev->model->asic_type == AsicType::GL842 ||          dev->model->asic_type == AsicType::GL843)      { -        s.output_segment_pixel_group_count = multiply_by_depth_ceil( -            s.output_pixels / s.ccd_size_divisor / s.segment_count, s.params.depth); +        s.output_segment_pixel_group_count = s.output_pixels / +                (s.full_resolution / s.optical_resolution * s.segment_count);      } + +    if (dev->model->model_id == ModelId::CANON_LIDE_90) { +        s.output_segment_pixel_group_count = s.output_pixels / s.segment_count; +    } +      if (dev->model->asic_type == AsicType::GL845 ||          dev->model->asic_type == AsicType::GL846 ||          dev->model->asic_type == AsicType::GL847)      { -        s.output_segment_pixel_group_count = multiply_by_depth_ceil( -            s.optical_pixels / (s.hwdpi_divisor * s.segment_count * ccd_pixels_per_system_pixel), -            s.params.depth); +        if (dev->model->model_id == ModelId::CANON_5600F) { +            s.output_segment_pixel_group_count = s.output_pixels / s.segment_count; +        } else { +            s.output_segment_pixel_group_count = s.pixel_count_ratio.apply(s.optical_pixels); +        }      }      s.output_line_bytes_requested = multiply_by_depth_ceil( @@ -1167,11 +1102,16 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se      s.output_total_bytes_raw = s.output_line_bytes_raw * s.output_line_count;      s.output_total_bytes = s.output_line_bytes * s.output_line_count; +    if (dev->model->model_id == ModelId::CANON_LIDE_90) { +        s.output_total_bytes_raw *= s.params.channels; +        s.output_total_bytes *= s.params.channels; +    } -    compute_session_buffer_sizes(dev->model->asic_type, s); -    compute_session_pipeline(dev, s); +    s.buffer_size_read = s.output_line_bytes_raw * 64;      compute_session_pixel_offsets(dev, s, sensor); +    s.shading_pixel_offset = sensor.shading_pixel_offset; +      if (dev->model->asic_type == AsicType::GL124 ||          dev->model->asic_type == AsicType::GL845 ||          dev->model->asic_type == AsicType::GL846) @@ -1179,7 +1119,10 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se          s.enable_ledadd = (s.params.channels == 1 && dev->model->is_cis && dev->settings.true_gray);      } +    s.use_host_side_calib = sensor.use_host_side_calib; +      if (dev->model->asic_type == AsicType::GL841 || +        dev->model->asic_type == AsicType::GL842 ||          dev->model->asic_type == AsicType::GL843)      {          // no 16 bit gamma for this ASIC @@ -1194,177 +1137,166 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se      debug_dump(DBG_info, s);  } -static std::size_t get_usb_buffer_read_size(AsicType asic, const ScanSession& session) +ImagePipelineStack build_image_pipeline(const Genesys_Device& dev, const ScanSession& session, +                                        unsigned pipeline_index, bool log_image_data)  { -    switch (asic) { -        case AsicType::GL646: -            // buffer not used on this chip set -            return 1; - -        case AsicType::GL124: -            // BUG: we shouldn't multiply by channels here nor divide by ccd_size_divisor -            return session.output_line_bytes_raw / session.ccd_size_divisor * session.params.channels; - -        case AsicType::GL845: -        case AsicType::GL846: -        case AsicType::GL847: -            // BUG: we shouldn't multiply by channels here -            return session.output_line_bytes_raw * session.params.channels; - -        case AsicType::GL843: -            return session.output_line_bytes_raw * 2; - -        default: -            throw SaneException("Unknown asic type"); -    } -} - -static FakeBufferModel get_fake_usb_buffer_model(const ScanSession& session) -{ -    FakeBufferModel model; -    model.push_step(session.buffer_size_read, 1); - -    if (session.pipeline_needs_reorder) { -        model.push_step(session.buffer_size_lines, session.output_line_bytes); -    } -    if (session.pipeline_needs_ccd) { -        model.push_step(session.buffer_size_shrink, session.output_line_bytes); -    } -    if (session.pipeline_needs_shrink) { -        model.push_step(session.buffer_size_out, session.output_line_bytes); -    } - -    return model; -} - -void build_image_pipeline(Genesys_Device* dev, const ScanSession& session) -{ -    static unsigned s_pipeline_index = 0; - -    s_pipeline_index++; -      auto format = create_pixel_format(session.params.depth, -                                      dev->model->is_cis ? 1 : session.params.channels, -                                      dev->model->line_mode_color_order); +                                      dev.model->is_cis ? 1 : session.params.channels, +                                      dev.model->line_mode_color_order);      auto depth = get_pixel_format_depth(format);      auto width = get_pixels_from_row_bytes(format, session.output_line_bytes_raw); -    auto read_data_from_usb = [dev](std::size_t size, std::uint8_t* data) +    auto read_data_from_usb = [&dev](std::size_t size, std::uint8_t* data)      { -        dev->interface->bulk_read_data(0x45, data, size); +        DBG(DBG_info, "read_data_from_usb: reading %zu bytes\n", size); +        auto begin = std::chrono::high_resolution_clock::now(); +        dev.interface->bulk_read_data(0x45, data, size); +        auto end = std::chrono::high_resolution_clock::now(); +        float us = std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count(); +        float speed = size / us; // bytes/us == MB/s +        DBG(DBG_info, "read_data_from_usb: reading %zu bytes finished %f MB/s\n", size, speed);          return true;      }; -    auto lines = session.output_line_count * (dev->model->is_cis ? session.params.channels : 1); +    auto debug_prefix = "gl_pipeline_" + std::to_string(pipeline_index); -    dev->pipeline.clear(); +    ImagePipelineStack pipeline; -    // FIXME: here we are complicating things for the time being to preserve the existing behaviour -    // This allows to be sure that the changes to the image pipeline have not introduced -    // regressions. +    auto lines = session.optical_line_count; +    auto buffer_size = session.buffer_size_read; -    if (session.segment_count > 1) { -        // BUG: we're reading one line too much -        dev->pipeline.push_first_node<ImagePipelineNodeBufferedCallableSource>( -                width, lines + 1, format, -                get_usb_buffer_read_size(dev->model->asic_type, session), read_data_from_usb); +    // At least GL841 requires reads to be aligned to 2 bytes and will fail on some devices on +    // certain circumstances. +    buffer_size = align_multiple_ceil(buffer_size, 2); + +    auto& src_node = pipeline.push_first_node<ImagePipelineNodeBufferedCallableSource>( +                          width, lines, format, buffer_size, read_data_from_usb); +    src_node.set_last_read_multiple(2); +    if (log_image_data) { +        pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_0_from_usb.tiff"); +    } + +    if (session.segment_count > 1) {          auto output_width = session.output_segment_pixel_group_count * session.segment_count; -        dev->pipeline.push_node<ImagePipelineNodeDesegment>(output_width, dev->segment_order, +        pipeline.push_node<ImagePipelineNodeDesegment>(output_width, dev.segment_order,                                                              session.conseq_pixel_dist,                                                              1, 1); -    } else { -        auto read_bytes_left_after_deseg = session.output_line_bytes * session.output_line_count; -        if (dev->model->asic_type == AsicType::GL646) { -            read_bytes_left_after_deseg *= dev->model->is_cis ? session.params.channels : 1; -        } -        dev->pipeline.push_first_node<ImagePipelineNodeBufferedGenesysUsb>( -                width, lines, format, read_bytes_left_after_deseg, -                get_fake_usb_buffer_model(session), read_data_from_usb); +        if (log_image_data) { +            pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_1_after_desegment.tiff"); +        }      } -    if (DBG_LEVEL >= DBG_io2) { -        dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" + -                                                        std::to_string(s_pipeline_index) + -                                                        "_0_before_swap.pnm"); -    } +    if (depth == 16) { +        unsigned num_swaps = 0; +        if (has_flag(dev.model->flags, ModelFlag::SWAP_16BIT_DATA)) { +            num_swaps++; +        } +#ifdef WORDS_BIGENDIAN +        num_swaps++; +#endif +        if (num_swaps % 2 != 0) { +            pipeline.push_node<ImagePipelineNodeSwap16BitEndian>(); -    if ((dev->model->flags & GENESYS_FLAG_16BIT_DATA_INVERTED) && depth == 16) { -        dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>(); +            if (log_image_data) { +                pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_2_after_swap.tiff"); +            } +        }      } -#ifdef WORDS_BIGENDIAN -    if (depth == 16) { -        dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>(); +    if (has_flag(dev.model->flags, ModelFlag::INVERT_PIXEL_DATA)) { +        pipeline.push_node<ImagePipelineNodeInvert>(); + +        if (log_image_data) { +            pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_3_after_invert.tiff"); +        }      } -#endif -    if (DBG_LEVEL >= DBG_io2) { -        dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" + -                                                        std::to_string(s_pipeline_index) + -                                                        "_1_after_swap.pnm"); +    if (dev.model->is_cis && session.params.channels == 3) { +        pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev.model->line_mode_color_order); + +        if (log_image_data) { +            pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_4_after_merge_mono.tiff"); +        }      } -    if (dev->model->is_cis && session.params.channels == 3) { -        dev->pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev->model->line_mode_color_order); +    if (pipeline.get_output_format() == PixelFormat::BGR888) { +        pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888);      } -    if (dev->pipeline.get_output_format() == PixelFormat::BGR888) { -        dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888); +    if (pipeline.get_output_format() == PixelFormat::BGR161616) { +        pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616);      } -    if (dev->pipeline.get_output_format() == PixelFormat::BGR161616) { -        dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616); +    if (log_image_data) { +        pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_5_after_format.tiff");      }      if (session.max_color_shift_lines > 0 && session.params.channels == 3) { -        dev->pipeline.push_node<ImagePipelineNodeComponentShiftLines>( +        pipeline.push_node<ImagePipelineNodeComponentShiftLines>(                      session.color_shift_lines_r,                      session.color_shift_lines_g,                      session.color_shift_lines_b); + +        if (log_image_data) { +            pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_6_after_color_unshift.tiff"); +        }      } -    if (DBG_LEVEL >= DBG_io2) { -        dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" + -                                                        std::to_string(s_pipeline_index) + -                                                        "_2_after_shift.pnm"); +    if (!session.stagger_x.empty()) { +        // FIXME: the image will be scaled to requested pixel count without regard to the reduction +        // of image size in this step. +        pipeline.push_node<ImagePipelineNodePixelShiftColumns>(session.stagger_x.shifts()); + +        if (log_image_data) { +            pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_7_after_x_unstagger.tiff"); +        }      }      if (session.num_staggered_lines > 0) { -        std::vector<std::size_t> shifts{0, session.num_staggered_lines}; -        dev->pipeline.push_node<ImagePipelineNodePixelShiftLines>(shifts); -    } +        pipeline.push_node<ImagePipelineNodePixelShiftLines>(session.stagger_y.shifts()); -    if (DBG_LEVEL >= DBG_io2) { -        dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" + -                                                        std::to_string(s_pipeline_index) + -                                                        "_3_after_stagger.pnm"); +        if (log_image_data) { +            pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_8_after_y_unstagger.tiff"); +        }      } -    if ((dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE) && -        !(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)) +    if (session.use_host_side_calib && +        !has_flag(dev.model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) && +        !has_flag(session.params.flags, ScanFlag::DISABLE_SHADING))      { -        dev->pipeline.push_node<ImagePipelineNodeCalibrate>(dev->dark_average_data, -                                                            dev->white_average_data); +        unsigned offset_pixels = session.params.startx + dev.calib_session.shading_pixel_offset; +        unsigned offset_bytes = offset_pixels * dev.calib_session.params.channels; +        pipeline.push_node<ImagePipelineNodeCalibrate>(dev.dark_average_data, +                                                       dev.white_average_data, offset_bytes); -        if (DBG_LEVEL >= DBG_io2) { -            dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" + -                                                            std::to_string(s_pipeline_index) + -                                                            "_4_after_calibrate.pnm"); +        if (log_image_data) { +            pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_9_after_calibrate.tiff");          }      } -    if (session.output_pixels != session.params.get_requested_pixels()) { -        dev->pipeline.push_node<ImagePipelineNodeScaleRows>(session.params.get_requested_pixels()); +    if (pipeline.get_output_width() != session.params.get_requested_pixels()) { +        pipeline.push_node<ImagePipelineNodeScaleRows>(session.params.get_requested_pixels());      } -    auto read_from_pipeline = [dev](std::size_t size, std::uint8_t* out_data) +    return pipeline; +} + +void setup_image_pipeline(Genesys_Device& dev, const ScanSession& session) +{ +    static unsigned s_pipeline_index = 0; + +    s_pipeline_index++; + +    dev.pipeline = build_image_pipeline(dev, session, s_pipeline_index, dbg_log_image_data()); + +    auto read_from_pipeline = [&dev](std::size_t size, std::uint8_t* out_data)      { -        (void) size; // will be always equal to dev->pipeline.get_output_row_bytes() -        return dev->pipeline.get_next_row_data(out_data); +        (void) size; // will be always equal to dev.pipeline.get_output_row_bytes() +        return dev.pipeline.get_next_row_data(out_data);      }; -    dev->pipeline_buffer = ImageBuffer{dev->pipeline.get_output_row_bytes(), +    dev.pipeline_buffer = ImageBuffer{dev.pipeline.get_output_row_bytes(),                                         read_from_pipeline};  } @@ -1394,6 +1326,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) @@ -1418,13 +1376,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); -    } -    if (frontend_type == FrontendType::ANALOG_DEVICES) { -        return compute_frontend_gain_analog_devices(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::ANALOG_DEVICES_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");      } -    throw SaneException("Unknown frontend to compute gain for");  }  /** @brief initialize device @@ -1436,7 +1403,7 @@ std::uint8_t compute_frontend_gain(float value, float target_value,   * @param dev device to initialize   * @param max_regs umber of maximum used registers   */ -void sanei_genesys_asic_init(Genesys_Device* dev, bool /*max_regs*/) +void sanei_genesys_asic_init(Genesys_Device* dev)  {      DBG_HELPER(dbg); @@ -1486,8 +1453,7 @@ void sanei_genesys_asic_init(Genesys_Device* dev, bool /*max_regs*/)    dev->settings.color_filter = ColorFilter::RED; -  /* duplicate initial values into calibration registers */ -  dev->calib_reg = dev->reg; +    dev->initial_regs = dev->reg;    const auto& sensor = sanei_genesys_find_sensor_any(dev); @@ -1497,8 +1463,15 @@ void sanei_genesys_asic_init(Genesys_Device* dev, bool /*max_regs*/)      dev->already_initialized = true;      // Move to home if needed +    if (dev->model->model_id == ModelId::CANON_8600F) { +        if (!dev->cmd_set->is_head_home(*dev, ScanHeadId::SECONDARY)) { +            dev->set_head_pos_unknown(ScanHeadId::SECONDARY); +        } +        if (!dev->cmd_set->is_head_home(*dev, ScanHeadId::PRIMARY)) { +            dev->set_head_pos_unknown(ScanHeadId::SECONDARY); +        } +    }      dev->cmd_set->move_back_home(dev, true); -    dev->set_head_pos_zero(ScanHeadId::PRIMARY);      // Set powersaving (default = 15 minutes)      dev->cmd_set->set_powersaving(dev, 15); @@ -1510,6 +1483,7 @@ void scanner_start_action(Genesys_Device& dev, bool start_motor)      switch (dev.model->asic_type) {          case AsicType::GL646:          case AsicType::GL841: +        case AsicType::GL842:          case AsicType::GL843:          case AsicType::GL845:          case AsicType::GL846: @@ -1527,8 +1501,7 @@ void scanner_start_action(Genesys_Device& dev, bool start_motor)      }  } -void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, const Genesys_Sensor& sensor, -                             unsigned dpihw) +void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, unsigned dpihw)  {      // same across GL646, GL841, GL843, GL846, GL847, GL124      const uint8_t REG_0x05_DPIHW_MASK = 0xc0; @@ -1537,10 +1510,6 @@ void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, const Genesys_Sensor& s      const uint8_t REG_0x05_DPIHW_2400 = 0x80;      const uint8_t REG_0x05_DPIHW_4800 = 0xc0; -    if (sensor.register_dpihw_override != 0) { -        dpihw = sensor.register_dpihw_override; -    } -      uint8_t dpihw_setting;      switch (dpihw) {          case 600: @@ -1583,6 +1552,12 @@ void regs_set_exposure(AsicType asic_type, Genesys_Register_Set& regs,              regs.set16(gl841::REG_EXPB, exposure.blue);              break;          } +        case AsicType::GL842: { +            regs.set16(gl842::REG_EXPR, exposure.red); +            regs.set16(gl842::REG_EXPG, exposure.green); +            regs.set16(gl842::REG_EXPB, exposure.blue); +            break; +        }          case AsicType::GL843: {              regs.set16(gl843::REG_EXPR, exposure.red);              regs.set16(gl843::REG_EXPG, exposure.green); @@ -1619,6 +1594,10 @@ void regs_set_optical_off(AsicType asic_type, Genesys_Register_Set& regs)              regs.find_reg(gl841::REG_0x01).value &= ~gl841::REG_0x01_SCAN;              break;          } +        case AsicType::GL842: { +            regs.find_reg(gl842::REG_0x01).value &= ~gl842::REG_0x01_SCAN; +            break; +        }          case AsicType::GL843: {              regs.find_reg(gl843::REG_0x01).value &= ~gl843::REG_0x01_SCAN;              break; @@ -1648,6 +1627,8 @@ bool get_registers_gain4_bit(AsicType asic_type, const Genesys_Register_Set& reg              return static_cast<bool>(regs.get8(gl646::REG_0x06) & gl646::REG_0x06_GAIN4);          case AsicType::GL841:              return static_cast<bool>(regs.get8(gl841::REG_0x06) & gl841::REG_0x06_GAIN4); +        case AsicType::GL842: +            return static_cast<bool>(regs.get8(gl842::REG_0x06) & gl842::REG_0x06_GAIN4);          case AsicType::GL843:              return static_cast<bool>(regs.get8(gl843::REG_0x06) & gl843::REG_0x06_GAIN4);          case AsicType::GL845: @@ -1706,79 +1687,78 @@ void sanei_genesys_wait_for_home(Genesys_Device* dev)      }  } -/** @brief motor profile - * search for the database of motor profiles and get the best one. Each - * profile is at full step and at a reference exposure. Use first entry - * by default. - * @param motors motor profile database - * @param motor_type motor id - * @param exposure exposure time - * @return a pointer to a Motor_Profile struct - */ -const Motor_Profile& sanei_genesys_get_motor_profile(const std::vector<Motor_Profile>& motors, -                                                     MotorId motor_id, int exposure) +const MotorProfile* get_motor_profile_ptr(const std::vector<MotorProfile>& profiles, +                                          unsigned exposure, +                                          const ScanSession& session)  { -  int idx; +    int best_i = -1; + +    for (unsigned i = 0; i < profiles.size(); ++i) { +        const auto& profile = profiles[i]; -  idx=-1; -    for (std::size_t i = 0; i < motors.size(); ++i) { -        // exact match -        if (motors[i].motor_id == motor_id && motors[i].exposure==exposure) { -            return motors[i]; +        if (!profile.resolutions.matches(session.params.yres)) { +            continue; +        } +        if (!profile.scan_methods.matches(session.params.scan_method)) { +            continue;          } -        // closest match -        if (motors[i].motor_id == motor_id) { -          /* if profile exposure is higher than the required one, -           * the entry is a candidate for the closest match */ -            if (motors[i].exposure == 0 || motors[i].exposure >= exposure) -            { -              if(idx<0) -                { -                  /* no match found yet */ -                  idx=i; -                } -              else -                { -                  /* test for better match */ -                  if(motors[i].exposure<motors[idx].exposure) -                    { -                      idx=i; -                    } +        if (profile.max_exposure == exposure) { +            return &profile; +        } + +        if (profile.max_exposure == 0 || profile.max_exposure >= exposure) { +            if (best_i < 0) { +                // no match found yet +                best_i = i; +            } else { +                // test for better match +                if (profiles[i].max_exposure < profiles[best_i].max_exposure) { +                    best_i = i;                  }              }          }      } -  /* default fallback */ -  if(idx<0) -    { -      DBG (DBG_warn,"%s: using default motor profile\n",__func__); -      idx=0; +    if (best_i < 0) { +        return nullptr; +    } + +    return &profiles[best_i]; +} + +const MotorProfile& get_motor_profile(const std::vector<MotorProfile>& profiles, +                                      unsigned exposure, +                                      const ScanSession& session) +{ +    const auto* profile = get_motor_profile_ptr(profiles, exposure, session); +    if (profile == nullptr) { +        throw SaneException("Motor slope is not configured");      } -    return motors[idx]; +    return *profile;  } -MotorSlopeTable sanei_genesys_slope_table(AsicType asic_type, int dpi, int exposure, int base_dpi, -                                          unsigned step_multiplier, -                                          const Motor_Profile& motor_profile) +MotorSlopeTable create_slope_table(AsicType asic_type, const Genesys_Motor& motor, unsigned ydpi, +                                   unsigned exposure, unsigned step_multiplier, +                                   const MotorProfile& motor_profile)  { -    unsigned target_speed_w = ((exposure * dpi) / base_dpi); +    unsigned target_speed_w = ((exposure * ydpi) / motor.base_ydpi); -    auto table = create_slope_table(motor_profile.slope, target_speed_w, motor_profile.step_type, -                                    step_multiplier, 2 * step_multiplier, -                                    get_slope_table_max_size(asic_type)); +    auto table = create_slope_table_for_speed(motor_profile.slope, target_speed_w, +                                              motor_profile.step_type, +                                              step_multiplier, 2 * step_multiplier, +                                              get_slope_table_max_size(asic_type));      return table;  }  MotorSlopeTable create_slope_table_fastest(AsicType asic_type, unsigned step_multiplier, -                                           const Motor_Profile& motor_profile) +                                           const MotorProfile& motor_profile)  { -    return create_slope_table(motor_profile.slope, motor_profile.slope.max_speed_w, -                              motor_profile.step_type, -                              step_multiplier, 2 * step_multiplier, -                              get_slope_table_max_size(asic_type)); +    return create_slope_table_for_speed(motor_profile.slope, motor_profile.slope.max_speed_w, +                                        motor_profile.step_type, +                                        step_multiplier, 2 * step_multiplier, +                                        get_slope_table_max_size(asic_type));  }  /** @brief returns the lowest possible ydpi for the device diff --git a/backend/genesys/low.h b/backend/genesys/low.h index d7f5dd2..d67b427 100644 --- a/backend/genesys/low.h +++ b/backend/genesys/low.h @@ -108,39 +108,6 @@  #define GENESYS_GREEN 1  #define GENESYS_BLUE  2 -/* Flags */ -#define GENESYS_FLAG_UNTESTED     (1 << 0)	/**< Print a warning for these scanners */ -#define GENESYS_FLAG_14BIT_GAMMA  (1 << 1)	/**< use 14bit Gamma table instead of 12 */ -#define GENESYS_FLAG_XPA          (1 << 3) -#define GENESYS_FLAG_SKIP_WARMUP  (1 << 4)	/**< skip genesys_warmup()              */ -/** @brief offset calibration flag - * signals that the scanner does offset calibration. In this case off_calibration() and - * coarse_gain_calibration() functions must be implemented - */ -#define GENESYS_FLAG_OFFSET_CALIBRATION   (1 << 5) -#define GENESYS_FLAG_SEARCH_START (1 << 6)	/**< do start search before scanning    */ -#define GENESYS_FLAG_REPARK       (1 << 7)	/**< repark head (and check for lock) by -						   moving without scanning */ -#define GENESYS_FLAG_DARK_CALIBRATION (1 << 8)	/**< do dark calibration */ - -#define GENESYS_FLAG_MUST_WAIT        (1 << 10)	/**< tells wether the scanner must wait for the head when parking */ - - -#define GENESYS_FLAG_HAS_UTA          (1 << 11)	/**< scanner has a transparency adapter */ - -#define GENESYS_FLAG_DARK_WHITE_CALIBRATION (1 << 12) /**< yet another calibration method. does white and dark shading in one run, depending on a black and a white strip*/ -#define GENESYS_FLAG_CUSTOM_GAMMA     (1 << 13)       /**< allow custom gamma tables */ -#define GENESYS_FLAG_NO_CALIBRATION   (1 << 14)       /**< allow scanners to use skip the calibration, needed for sheetfed scanners */ -#define GENESYS_FLAG_SIS_SENSOR       (1 << 16)       /**< handling of multi-segments sensors in software */ -#define GENESYS_FLAG_SHADING_NO_MOVE  (1 << 17)       /**< scanner doesn't move sensor during shading calibration */ -#define GENESYS_FLAG_SHADING_REPARK   (1 << 18)       /**< repark head between shading scans */ -#define GENESYS_FLAG_FULL_HWDPI_MODE  (1 << 19)       /**< scanner always use maximum hw dpi to setup the sensor */ -// scanner has infrared transparency scanning capability -#define GENESYS_FLAG_HAS_UTA_INFRARED (1 << 20) -// scanner calibration is handled on the host side -#define GENESYS_FLAG_CALIBRATION_HOST_SIDE (1 << 21) -#define GENESYS_FLAG_16BIT_DATA_INVERTED (1 << 22) -  #define GENESYS_HAS_NO_BUTTONS       0              /**< scanner has no supported button */  #define GENESYS_HAS_SCAN_SW          (1 << 0)       /**< scanner has SCAN button */  #define GENESYS_HAS_FILE_SW          (1 << 1)       /**< scanner has FILE button */ @@ -186,66 +153,60 @@  #define AFE_SET        2  #define AFE_POWER_SAVE 4 -#define LOWORD(x)  ((uint16_t)((x) & 0xffff)) -#define HIWORD(x)  ((uint16_t)((x) >> 16)) -#define LOBYTE(x)  ((uint8_t)((x) & 0xFF)) -#define HIBYTE(x)  ((uint8_t)((x) >> 8)) - -/* Global constants */ -/* TODO: emove this leftover of early backend days */ -#define MOTOR_SPEED_MAX		350 -#define DARK_VALUE		0 - -#define MAX_RESOLUTIONS 13 -#define MAX_DPI 4 -  namespace genesys { -struct Genesys_USB_Device_Entry { +class UsbDeviceEntry { +public: +    static constexpr std::uint16_t BCD_DEVICE_NOT_SET = 0xffff; + +    UsbDeviceEntry(std::uint16_t vendor_id, std::uint16_t product_id, +                   const Genesys_Model& model) : +        vendor_{vendor_id}, product_{product_id}, +        bcd_device_{BCD_DEVICE_NOT_SET}, model_{model} +    {} -    Genesys_USB_Device_Entry(unsigned v, unsigned p, const Genesys_Model& m) : -        vendor(v), product(p), model(m) +    UsbDeviceEntry(std::uint16_t vendor_id, std::uint16_t product_id, std::uint16_t bcd_device, +                   const Genesys_Model& model) : +        vendor_{vendor_id}, product_{product_id}, +        bcd_device_{bcd_device}, model_{model}      {} +    std::uint16_t vendor_id() const { return vendor_; } +    std::uint16_t product_id() const { return product_; } +    std::uint16_t bcd_device() const { return bcd_device_; } + +    const Genesys_Model& model() const { return model_; } + +    bool matches(std::uint16_t vendor_id, std::uint16_t product_id, std::uint16_t bcd_device) +    { +        if (vendor_ != vendor_id) +            return false; +        if (product_ != product_id) +            return false; +        if (bcd_device_ != BCD_DEVICE_NOT_SET && bcd_device != BCD_DEVICE_NOT_SET && +            bcd_device_ != bcd_device) +        { +            return false; +        } +        return true; +    } + +private:      // USB vendor identifier -    std::uint16_t vendor; +    std::uint16_t vendor_;      // USB product identifier -    std::uint16_t product; +    std::uint16_t product_; +    // USB bcdProduct identifier +    std::uint16_t bcd_device_;      // Scanner model information -    Genesys_Model model; +    Genesys_Model model_;  }; -/** - * structure for motor database - */ -struct Motor_Profile -{ -    MotorId motor_id; -    int exposure;           // used only to select the wanted motor -    StepType step_type;   // default step type for given exposure -    MotorSlope slope; -}; - -extern StaticInit<std::vector<Motor_Profile>> gl843_motor_profiles; -extern StaticInit<std::vector<Motor_Profile>> gl846_motor_profiles; -extern StaticInit<std::vector<Motor_Profile>> gl847_motor_profiles; -extern StaticInit<std::vector<Motor_Profile>> gl124_motor_profiles; -  /*--------------------------------------------------------------------------*/  /*       common functions needed by low level specific functions            */  /*--------------------------------------------------------------------------*/ -inline GenesysRegister* sanei_genesys_get_address(Genesys_Register_Set* regs, uint16_t addr) -{ -    auto* ret = regs->find_reg_address(addr); -    if (ret == nullptr) { -        DBG(DBG_error, "%s: failed to find address for register 0x%02x, crash expected !\n", -            __func__, addr); -    } -    return ret; -} - -extern void sanei_genesys_init_cmd_set(Genesys_Device* dev); +std::unique_ptr<CommandSet> create_cmd_set(AsicType asic_type);  // reads the status of the scanner  Status scanner_read_status(Genesys_Device& dev); @@ -259,21 +220,26 @@ void scanner_read_print_status(Genesys_Device& dev);  void debug_print_status(DebugMessageHelper& dbg, Status status); +void scanner_register_rw_clear_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t mask); +void scanner_register_rw_set_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t mask); +void scanner_register_rw_bits(Genesys_Device& dev, std::uint16_t address, +                              std::uint8_t value, std::uint8_t mask); +  extern void sanei_genesys_write_ahb(Genesys_Device* dev, uint32_t addr, uint32_t size,                                      uint8_t* data);  extern void sanei_genesys_init_structs (Genesys_Device * dev); -const Genesys_Sensor& sanei_genesys_find_sensor_any(Genesys_Device* dev); -const Genesys_Sensor& sanei_genesys_find_sensor(Genesys_Device* dev, unsigned dpi, +const Genesys_Sensor& sanei_genesys_find_sensor_any(const Genesys_Device* dev); +const Genesys_Sensor& sanei_genesys_find_sensor(const Genesys_Device* dev, unsigned dpi,                                                  unsigned channels, ScanMethod scan_method); -bool sanei_genesys_has_sensor(Genesys_Device* dev, unsigned dpi, unsigned channels, +bool sanei_genesys_has_sensor(const Genesys_Device* dev, unsigned dpi, unsigned channels,                                ScanMethod scan_method);  Genesys_Sensor& sanei_genesys_find_sensor_for_write(Genesys_Device* dev, unsigned dpi,                                                      unsigned channels, ScanMethod scan_method);  std::vector<std::reference_wrapper<const Genesys_Sensor>> -    sanei_genesys_find_sensors_all(Genesys_Device* dev, ScanMethod scan_method); +    sanei_genesys_find_sensors_all(const Genesys_Device* dev, ScanMethod scan_method);  std::vector<std::reference_wrapper<Genesys_Sensor>>      sanei_genesys_find_sensors_all_for_write(Genesys_Device* dev, ScanMethod scan_method); @@ -318,13 +284,9 @@ extern void sanei_genesys_set_buffer_address(Genesys_Device* dev, uint32_t addr)  unsigned sanei_genesys_get_bulk_max_size(AsicType asic_type); -SANE_Int sanei_genesys_exposure_time2(Genesys_Device * dev, float ydpi, StepType step_type, +SANE_Int sanei_genesys_exposure_time2(Genesys_Device* dev, const MotorProfile& profile, float ydpi,                                        int endpixel, int led_exposure); -MotorSlopeTable sanei_genesys_create_slope_table3(AsicType asic_type, const Genesys_Motor& motor, -                                                  StepType step_type, int exposure_time, -                                                  unsigned yres); -  void sanei_genesys_create_default_gamma_table(Genesys_Device* dev,                                                std::vector<uint16_t>& gamma_table, float gamma); @@ -335,28 +297,42 @@ void sanei_genesys_send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& s  extern void sanei_genesys_stop_motor(Genesys_Device* dev); -extern void sanei_genesys_search_reference_point(Genesys_Device* dev, Genesys_Sensor& sensor, -                                                 const uint8_t* src_data, int start_pixel, int dpi, -                                                 int width, int height); -  // moves the scan head by the specified steps at the motor base dpi  void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, Direction direction);  void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home);  void scanner_move_back_home_ta(Genesys_Device& dev); -void scanner_clear_scan_and_feed_counts(Genesys_Device& dev); +/** Search for a full width black or white strip. +    This function searches for a black or white stripe across the scanning area. +    When searching backward, the searched area must completely be of the desired +    color since this area will be used for calibration which scans forward. -extern void sanei_genesys_write_file(const char* filename, const std::uint8_t* data, -                                     std::size_t length); +    @param dev scanner device +    @param forward true if searching forward, false if searching backward +    @param black true if searching for a black strip, false for a white strip + */ +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); + +void scanner_offset_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, +                                Genesys_Register_Set& regs); -extern void sanei_genesys_write_pnm_file(const char* filename, const std::uint8_t* data, int depth, -                                         int channels, int pixels_per_line, int lines); +void scanner_coarse_gain_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, +                                     Genesys_Register_Set& regs, unsigned dpi); + +SensorExposure scanner_led_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor, +                                       Genesys_Register_Set& regs); + +void scanner_clear_scan_and_feed_counts(Genesys_Device& dev); -void sanei_genesys_write_pnm_file(const char* filename, const Image& image); +void scanner_send_slope_table(Genesys_Device* dev, const Genesys_Sensor& sensor, unsigned table_nr, +                              const std::vector<uint16_t>& slope_table); -extern void sanei_genesys_write_pnm_file16(const char* filename, const uint16_t *data, unsigned channels, -                                           unsigned pixels_per_line, unsigned lines); +extern void sanei_genesys_write_file(const char* filename, const std::uint8_t* data, +                                     std::size_t length);  void wait_until_buffer_non_empty(Genesys_Device* dev, bool check_status_twice = false); @@ -370,25 +346,13 @@ void regs_set_exposure(AsicType asic_type, Genesys_Register_Set& regs,  void regs_set_optical_off(AsicType asic_type, Genesys_Register_Set& regs); -void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, const Genesys_Sensor& sensor, -                             unsigned dpihw); - -inline uint16_t sanei_genesys_fixup_exposure_value(uint16_t value) -{ -    if ((value & 0xff00) == 0) { -        value |= 0x100; -    } -    if ((value & 0x00ff) == 0) { -        value |= 0x1; -    } -    return value; -} +void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, unsigned dpihw);  inline SensorExposure sanei_genesys_fixup_exposure(SensorExposure exposure)  { -    exposure.red = sanei_genesys_fixup_exposure_value(exposure.red); -    exposure.green = sanei_genesys_fixup_exposure_value(exposure.green); -    exposure.blue = sanei_genesys_fixup_exposure_value(exposure.blue); +    exposure.red = std::max<std::uint16_t>(1, exposure.red); +    exposure.green = std::max<std::uint16_t>(1, exposure.green); +    exposure.blue = std::max<std::uint16_t>(1, exposure.blue);      return exposure;  } @@ -396,7 +360,7 @@ bool get_registers_gain4_bit(AsicType asic_type, const Genesys_Register_Set& reg  extern void sanei_genesys_wait_for_home(Genesys_Device* dev); -extern void sanei_genesys_asic_init(Genesys_Device* dev, bool cold); +extern void sanei_genesys_asic_init(Genesys_Device* dev);  void scanner_start_action(Genesys_Device& dev, bool start_motor);  void scanner_stop_action(Genesys_Device& dev); @@ -404,15 +368,23 @@ void scanner_stop_action_no_move(Genesys_Device& dev, Genesys_Register_Set& regs  bool scanner_is_motor_stopped(Genesys_Device& dev); -const Motor_Profile& sanei_genesys_get_motor_profile(const std::vector<Motor_Profile>& motors, -                                                     MotorId motor_id, int exposure); +void scanner_setup_sensor(Genesys_Device& dev, const Genesys_Sensor& sensor, +                          Genesys_Register_Set& regs); + +const MotorProfile* get_motor_profile_ptr(const std::vector<MotorProfile>& profiles, +                                          unsigned exposure, +                                          const ScanSession& session); -MotorSlopeTable sanei_genesys_slope_table(AsicType asic_type, int dpi, int exposure, int base_dpi, -                                          unsigned step_multiplier, -                                          const Motor_Profile& motor_profile); +const MotorProfile& get_motor_profile(const std::vector<MotorProfile>& profiles, +                                      unsigned exposure, +                                      const ScanSession& session); + +MotorSlopeTable create_slope_table(AsicType asic_type, const Genesys_Motor& motor, unsigned ydpi, +                                   unsigned exposure, unsigned step_multiplier, +                                   const MotorProfile& motor_profile);  MotorSlopeTable create_slope_table_fastest(AsicType asic_type, unsigned step_multiplier, -                                           const Motor_Profile& motor_profile); +                                           const MotorProfile& motor_profile);  /** @brief find lowest motor resolution for the device.   * Parses the resolution list for motor and @@ -449,52 +421,22 @@ extern void sanei_genesys_generate_gamma_buffer(Genesys_Device* dev,                                      int size,                                      uint8_t* gamma); +unsigned session_adjust_output_pixels(unsigned output_pixels, +                                      const Genesys_Device& dev, const Genesys_Sensor& sensor, +                                      unsigned output_xresolution, unsigned output_yresolution, +                                      bool adjust_output_pixels); +  void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Sensor& sensor); -void build_image_pipeline(Genesys_Device* dev, const ScanSession& session); +ImagePipelineStack build_image_pipeline(const Genesys_Device& dev, const ScanSession& session, +                                        unsigned pipeline_index, bool log_image_data); + +// sets up a image pipeline for device `dev` +void setup_image_pipeline(Genesys_Device& dev, const ScanSession& session);  std::uint8_t compute_frontend_gain(float value, float target_value,                                     FrontendType frontend_type); -template<class T> -inline T abs_diff(T a, T b) -{ -    if (a < b) { -        return b - a; -    } else { -        return a - b; -    } -} - -inline uint64_t align_multiple_floor(uint64_t x, uint64_t multiple) -{ -    return (x / multiple) * multiple; -} - -inline uint64_t align_multiple_ceil(uint64_t x, uint64_t multiple) -{ -    return ((x + multiple - 1) / multiple) * multiple; -} - -inline uint64_t multiply_by_depth_ceil(uint64_t pixels, uint64_t depth) -{ -    if (depth == 1) { -        return (pixels / 8) + ((pixels % 8) ? 1 : 0); -    } else { -        return pixels * (depth / 8); -    } -} - -template<class T> -inline T clamp(const T& value, const T& lo, const T& hi) -{ -    if (value < lo) -        return lo; -    if (value > hi) -        return hi; -    return value; -} -  /*---------------------------------------------------------------------------*/  /*                ASIC specific functions declarations                       */  /*---------------------------------------------------------------------------*/ @@ -502,15 +444,18 @@ 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; +extern StaticInit<std::vector<UsbDeviceEntry>> 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_motor_profile_tables();  void genesys_init_usb_device_tables(); +void verify_sensor_tables(); +void verify_usb_device_tables();  template<class T>  void debug_dump(unsigned level, const T& value) diff --git a/backend/genesys/motor.cpp b/backend/genesys/motor.cpp index 910266a..a18d6e1 100644 --- a/backend/genesys/motor.cpp +++ b/backend/genesys/motor.cpp @@ -43,9 +43,11 @@  #define DEBUG_DECLARE_ONLY +#include "low.h"  #include "motor.h"  #include "utilities.h"  #include <cmath> +#include <numeric>  namespace genesys { @@ -80,19 +82,38 @@ MotorSlope MotorSlope::create_from_steps(unsigned initial_w, unsigned max_w,      return slope;  } -void MotorSlopeTable::slice_steps(unsigned count) +void MotorSlopeTable::slice_steps(unsigned count, unsigned step_multiplier)  { -    if (count >= table.size() || count > steps_count) { -        throw SaneException("Excepssive steps count"); +    if (count > table.size() || count < step_multiplier) { +        throw SaneException("Invalid steps count");      } -    steps_count = count; +    count = align_multiple_floor(count, step_multiplier); +    table.resize(count); +    generate_pixeltime_sum(); +} + +void MotorSlopeTable::expand_table(unsigned count, unsigned step_multiplier) +{ +    if (table.empty()) { +        throw SaneException("Can't expand empty table"); +    } +    count = align_multiple_ceil(count, step_multiplier); +    table.resize(table.size() + count, table.back()); +    generate_pixeltime_sum(); +} + +void MotorSlopeTable::generate_pixeltime_sum() +{ +    pixeltime_sum_ = std::accumulate(table.begin(), table.end(), +                                     std::size_t{0}, std::plus<std::size_t>());  }  unsigned get_slope_table_max_size(AsicType asic_type)  {      switch (asic_type) {          case AsicType::GL646: -        case AsicType::GL841: return 255; +        case AsicType::GL841: +        case AsicType::GL842: return 255;          case AsicType::GL843:          case AsicType::GL845:          case AsicType::GL846: @@ -103,9 +124,9 @@ unsigned get_slope_table_max_size(AsicType asic_type)      }  } -MotorSlopeTable create_slope_table(const MotorSlope& slope, unsigned target_speed_w, -                                   StepType step_type, unsigned steps_alignment, -                                   unsigned min_size, unsigned max_size) +MotorSlopeTable create_slope_table_for_speed(const MotorSlope& slope, unsigned target_speed_w, +                                             StepType step_type, unsigned steps_alignment, +                                             unsigned min_size, unsigned max_size)  {      DBG_HELPER_ARGS(dbg, "target_speed_w: %d, step_type: %d, steps_alignment: %d, min_size: %d",                      target_speed_w, static_cast<unsigned>(step_type), steps_alignment, min_size); @@ -120,6 +141,10 @@ MotorSlopeTable create_slope_table(const MotorSlope& slope, unsigned target_spee          dbg.log(DBG_warn, "failed to reach target speed");      } +    if (target_speed_shifted_w >= std::numeric_limits<std::uint16_t>::max()) { +        throw SaneException("Target motor speed is too low"); +    } +      unsigned final_speed = std::max(target_speed_shifted_w, max_speed_shifted_w);      table.table.reserve(max_size); @@ -130,26 +155,20 @@ MotorSlopeTable create_slope_table(const MotorSlope& slope, unsigned target_spee              break;          }          table.table.push_back(current); -        table.pixeltime_sum += current;      }      // make sure the target speed (or the max speed if target speed is too high) is present in      // the table      table.table.push_back(final_speed); -    table.pixeltime_sum += table.table.back();      // fill the table up to the specified size      while (table.table.size() < max_size - 1 &&             (table.table.size() % steps_alignment != 0 || table.table.size() < min_size))      {          table.table.push_back(table.table.back()); -        table.pixeltime_sum += table.table.back();      } -    table.steps_count = table.table.size(); - -    // fill the rest of the table with the final speed -    table.table.resize(max_size, final_speed); +    table.generate_pixeltime_sum();      return table;  } @@ -164,15 +183,30 @@ std::ostream& operator<<(std::ostream& out, const MotorSlope& slope)      return out;  } +std::ostream& operator<<(std::ostream& out, const MotorProfile& profile) +{ +    out << "MotorProfile{\n" +        << "    max_exposure: " << profile.max_exposure << '\n' +        << "    step_type: " << profile.step_type << '\n' +        << "    motor_vref: " << profile.motor_vref << '\n' +        << "    resolutions: " << format_indent_braced_list(4, profile.resolutions) << '\n' +        << "    scan_methods: " << format_indent_braced_list(4, profile.scan_methods) << '\n' +        << "    slope: " << format_indent_braced_list(4, profile.slope) << '\n' +        << '}'; +    return out; +} +  std::ostream& operator<<(std::ostream& out, const Genesys_Motor& motor)  {      out << "Genesys_Motor{\n" -        << "    id: " << static_cast<unsigned>(motor.id) << '\n' +        << "    id: " << motor.id << '\n'          << "    base_ydpi: " << motor.base_ydpi << '\n' -        << "    optical_ydpi: " << motor.optical_ydpi << '\n' -        << "    slopes: " -        << format_indent_braced_list(4, format_vector_indent_braced(4, "MotorSlope", -                                                                    motor.slopes)) +        << "    profiles: " +        << format_indent_braced_list(4, format_vector_indent_braced(4, "MotorProfile", +                                                                    motor.profiles)) << '\n' +        << "    fast_profiles: " +        << format_indent_braced_list(4, format_vector_indent_braced(4, "MotorProfile", +                                                                    motor.fast_profiles)) << '\n'          << '}';      return out;  } diff --git a/backend/genesys/motor.h b/backend/genesys/motor.h index d80da6d..c433c0e 100644 --- a/backend/genesys/motor.h +++ b/backend/genesys/motor.h @@ -44,9 +44,12 @@  #ifndef BACKEND_GENESYS_MOTOR_H  #define BACKEND_GENESYS_MOTOR_H +#include <algorithm>  #include <cstdint>  #include <vector>  #include "enums.h" +#include "sensor.h" +#include "value_filter.h"  namespace genesys { @@ -123,20 +126,47 @@ struct MotorSlope  struct MotorSlopeTable  {      std::vector<std::uint16_t> table; -    unsigned steps_count = 0; -    unsigned pixeltime_sum = 0; -    void slice_steps(unsigned count); +    void slice_steps(unsigned count, unsigned step_multiplier); + +    // expands the table by the given number of steps +    void expand_table(unsigned count, unsigned step_multiplier); + +    std::uint64_t pixeltime_sum() const { return pixeltime_sum_; } + +    void generate_pixeltime_sum(); +private: +    std::uint64_t pixeltime_sum_ = 0;  };  unsigned get_slope_table_max_size(AsicType asic_type); -MotorSlopeTable create_slope_table(const MotorSlope& slope, unsigned target_speed_w, -                                   StepType step_type, unsigned steps_alignment, -                                   unsigned min_size, unsigned max_size); +MotorSlopeTable create_slope_table_for_speed(const MotorSlope& slope, unsigned target_speed_w, +                                             StepType step_type, unsigned steps_alignment, +                                             unsigned min_size, unsigned max_size);  std::ostream& operator<<(std::ostream& out, const MotorSlope& slope); +struct MotorProfile +{ +    MotorProfile() = default; +    MotorProfile(const MotorSlope& a_slope, StepType a_step_type, unsigned a_max_exposure) : +        slope{a_slope}, step_type{a_step_type}, max_exposure{a_max_exposure} +    {} + +    MotorSlope slope; +    StepType step_type = StepType::FULL; +    int motor_vref = -1; + +    // the resolutions this profile is good for +    ValueFilterAny<unsigned> resolutions = VALUE_FILTER_ANY; +    // the scan method this profile is good for. If the list is empty, good for any method. +    ValueFilterAny<ScanMethod> scan_methods = VALUE_FILTER_ANY; + +    unsigned max_exposure = 0; // 0 - any exposure +}; + +std::ostream& operator<<(std::ostream& out, const MotorProfile& profile);  struct Genesys_Motor  { @@ -146,27 +176,41 @@ struct Genesys_Motor      MotorId id = MotorId::UNKNOWN;      // motor base steps. Unit: 1/inch      int base_ydpi = 0; -    // maximum resolution in y-direction. Unit: 1/inch -    int optical_ydpi = 0;      // slopes to derive individual slopes from -    std::vector<MotorSlope> slopes; +    std::vector<MotorProfile> profiles; +    // slopes to derive individual slopes from for fast moving +    std::vector<MotorProfile> fast_profiles; -    MotorSlope& get_slope(StepType step_type) +    MotorSlope& get_slope_with_step_type(StepType step_type)      { -        return slopes[static_cast<unsigned>(step_type)]; +        for (auto& p : profiles) { +            if (p.step_type == step_type) +                return p.slope; +        } +        throw SaneException("No motor profile with step type");      } -    const MotorSlope& get_slope(StepType step_type) const +    const MotorSlope& get_slope_with_step_type(StepType step_type) const      { -        return slopes[static_cast<unsigned>(step_type)]; +        for (const auto& p : profiles) { +            if (p.step_type == step_type) +                return p.slope; +        } +        throw SaneException("No motor profile with step type");      }      StepType max_step_type() const      { -        if (slopes.empty()) { -            throw std::runtime_error("Slopes table is empty"); +        if (profiles.empty()) { +            throw std::runtime_error("Profiles table is empty"); +        } +        StepType step_type = StepType::FULL; +        for (const auto& p : profiles) { +            step_type = static_cast<StepType>( +                    std::max(static_cast<unsigned>(step_type), +                             static_cast<unsigned>(p.step_type)));          } -        return static_cast<StepType>(slopes.size() - 1); +        return step_type;      }  }; diff --git a/backend/genesys/register.h b/backend/genesys/register.h index bbc7ec8..51aab90 100644 --- a/backend/genesys/register.h +++ b/backend/genesys/register.h @@ -44,6 +44,7 @@  #ifndef BACKEND_GENESYS_REGISTER_H  #define BACKEND_GENESYS_REGISTER_H +#include "enums.h"  #include "utilities.h"  #include <algorithm> @@ -76,7 +77,7 @@ struct GenesysRegisterSetState      bool is_lamp_on = false;      bool is_xpa_on = false;      bool is_motor_on = false; -    bool is_xpa_motor_on = false; +    MotorMode motor_mode = MotorMode::PRIMARY;  };  template<class Value> @@ -414,6 +415,11 @@ public:          }      } +    bool has_reg(AddressType address) const +    { +        return find_reg_index(address) != -1; +    } +      SettingType& find_reg(AddressType address)      {          int i = find_reg_index(address); diff --git a/backend/genesys/scanner_interface.h b/backend/genesys/scanner_interface.h index 03c7132..70413d1 100644 --- a/backend/genesys/scanner_interface.h +++ b/backend/genesys/scanner_interface.h @@ -56,11 +56,6 @@ namespace genesys {  class ScannerInterface  {  public: -    enum Flags { -        FLAG_NONE = 0, -        FLAG_SWAP_REGISTERS = 1 << 0, -        FLAG_SMALL_ADDRESS = 1 << 1 -    };      virtual ~ScannerInterface(); @@ -75,12 +70,11 @@ public:      virtual void bulk_write_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) = 0;      // GL646, GL841, GL843 have different ways to write to RAM and to gamma tables -    // FIXME: remove flags when updating tests      virtual void write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, -                              std::size_t size, Flags flags = FLAG_NONE) = 0; +                              std::size_t size) = 0;      virtual void write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, -                             std::size_t size, Flags flags = FLAG_NONE) = 0; +                             std::size_t size) = 0;      // GL845, GL846, GL847 and GL124 have a uniform way to write to RAM tables      virtual void write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) = 0; diff --git a/backend/genesys/scanner_interface_usb.cpp b/backend/genesys/scanner_interface_usb.cpp index d4d83dd..d405ede 100644 --- a/backend/genesys/scanner_interface_usb.cpp +++ b/backend/genesys/scanner_interface_usb.cpp @@ -101,8 +101,6 @@ std::uint8_t ScannerInterfaceUsb::read_register(std::uint16_t address)          usb_dev_.control_msg(REQUEST_TYPE_IN, REQUEST_REGISTER, VALUE_READ_REGISTER, INDEX,                               1, &value);      } - -    DBG(DBG_proc, "%s (0x%02x, 0x%02x) completed\n", __func__, address, value);      return value;  } @@ -213,6 +211,7 @@ static void bulk_read_data_send_header(UsbDevice& usb_dev, AsicType asic_type, s      uint8_t outdata[8];      if (asic_type == AsicType::GL124 || +        asic_type == AsicType::GL845 ||          asic_type == AsicType::GL846 ||          asic_type == AsicType::GL847)      { @@ -222,7 +221,9 @@ static void bulk_read_data_send_header(UsbDevice& usb_dev, AsicType asic_type, s          outdata[2] = 0;          outdata[3] = 0x10;      } else if (asic_type == AsicType::GL841 || -               asic_type == AsicType::GL843) { +               asic_type == AsicType::GL842 || +               asic_type == AsicType::GL843) +    {          outdata[0] = BULK_IN;          outdata[1] = BULK_RAM;          outdata[2] = 0x82; // @@ -246,12 +247,13 @@ static void bulk_read_data_send_header(UsbDevice& usb_dev, AsicType asic_type, s  void ScannerInterfaceUsb::bulk_read_data(std::uint8_t addr, std::uint8_t* data, std::size_t size)  { -    // currently supported: GL646, GL841, GL843, GL846, GL847, GL124 +    // currently supported: GL646, GL841, GL843, GL845, GL846, GL847, GL124      DBG_HELPER(dbg);      unsigned is_addr_used = 1;      unsigned has_header_before_each_chunk = 0;      if (dev_->model->asic_type == AsicType::GL124 || +        dev_->model->asic_type == AsicType::GL845 ||          dev_->model->asic_type == AsicType::GL846 ||          dev_->model->asic_type == AsicType::GL847)      { @@ -351,30 +353,21 @@ void ScannerInterfaceUsb::bulk_write_data(std::uint8_t addr, std::uint8_t* data,  }  void ScannerInterfaceUsb::write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, -                                       std::size_t size, Flags flags) +                                       std::size_t size)  {      DBG_HELPER_ARGS(dbg, "type: 0x%02x, addr: 0x%08x, size: 0x%08zx", type, addr, size);      if (dev_->model->asic_type != AsicType::GL646 &&          dev_->model->asic_type != AsicType::GL841 && +        dev_->model->asic_type != AsicType::GL842 &&          dev_->model->asic_type != AsicType::GL843)      {          throw SaneException("Unsupported transfer mode");      }      if (dev_->model->asic_type == AsicType::GL843) { -        if (flags & FLAG_SWAP_REGISTERS) { -            if (!(flags & FLAG_SMALL_ADDRESS)) { -                write_register(0x29, ((addr >> 20) & 0xff)); -            } -            write_register(0x2a, ((addr >> 12) & 0xff)); -            write_register(0x2b, ((addr >> 4) & 0xff)); -        } else { -            write_register(0x2b, ((addr >> 4) & 0xff)); -            write_register(0x2a, ((addr >> 12) & 0xff)); -            if (!(flags & FLAG_SMALL_ADDRESS)) { -                write_register(0x29, ((addr >> 20) & 0xff)); -            } -        } +        write_register(0x2b, ((addr >> 4) & 0xff)); +        write_register(0x2a, ((addr >> 12) & 0xff)); +        write_register(0x29, ((addr >> 20) & 0xff));      } else {          write_register(0x2b, ((addr >> 4) & 0xff));          write_register(0x2a, ((addr >> 12) & 0xff)); @@ -383,24 +376,28 @@ void ScannerInterfaceUsb::write_buffer(std::uint8_t type, std::uint32_t addr, st  }  void ScannerInterfaceUsb::write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, -                                      std::size_t size, Flags flags) +                                      std::size_t size)  {      DBG_HELPER_ARGS(dbg, "type: 0x%02x, addr: 0x%08x, size: 0x%08zx", type, addr, size); -    if (dev_->model->asic_type != AsicType::GL646 && -        dev_->model->asic_type != AsicType::GL841 && +    if (dev_->model->asic_type != AsicType::GL841 && +        dev_->model->asic_type != AsicType::GL842 &&          dev_->model->asic_type != AsicType::GL843)      {          throw SaneException("Unsupported transfer mode");      } -    if (flags & FLAG_SWAP_REGISTERS) { -        write_register(0x5b, ((addr >> 12) & 0xff)); -        write_register(0x5c, ((addr >> 4) & 0xff)); -    } else { -        write_register(0x5c, ((addr >> 4) & 0xff)); -        write_register(0x5b, ((addr >> 12) & 0xff)); -    } +    write_register(0x5b, ((addr >> 12) & 0xff)); +    write_register(0x5c, ((addr >> 4) & 0xff));      bulk_write_data(type, data, size); + +    if (dev_->model->asic_type == AsicType::GL842 || +        dev_->model->asic_type == AsicType::GL843) +    { +        // it looks like we need to reset the address so that subsequent buffer operations work. +        // Most likely the MTRTBL register is to blame. +        write_register(0x5b, 0); +        write_register(0x5c, 0); +    }  }  void ScannerInterfaceUsb::write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) diff --git a/backend/genesys/scanner_interface_usb.h b/backend/genesys/scanner_interface_usb.h index 06b51ff..33fb8fe 100644 --- a/backend/genesys/scanner_interface_usb.h +++ b/backend/genesys/scanner_interface_usb.h @@ -67,9 +67,9 @@ public:      void bulk_write_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) override;      void write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, -                      std::size_t size, Flags flags) override; +                      std::size_t size) override;      void write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, -                     std::size_t size, Flags flags) override; +                     std::size_t size) override;      void write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) override; diff --git a/backend/genesys/sensor.cpp b/backend/genesys/sensor.cpp index e54af65..ce51403 100644 --- a/backend/genesys/sensor.cpp +++ b/backend/genesys/sensor.cpp @@ -51,10 +51,16 @@ namespace genesys {  std::ostream& operator<<(std::ostream& out, const StaggerConfig& config)  { -    out << "StaggerConfig{\n" -        << "    min_resolution: " << config.min_resolution() << '\n' -        << "    lines_at_min: " << config.lines_at_min() << '\n' -        << "}"; +    if (config.shifts().empty()) { +        out << "StaggerConfig{}"; +        return out; +    } + +    out << "StaggerConfig{ " << config.shifts().front(); +    for (auto it = std::next(config.shifts().begin()); it != config.shifts().end(); ++it) { +        out << ", " << *it; +    } +    out << " }";      return out;  } @@ -64,6 +70,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::ANALOG_DEVICES_GL847: out << "ANALOG_DEVICES_GL847"; break; +        case FrontendType::WOLFSON_GL124: out << "WOLFSON_GL124"; break;          default: out << "(unknown value)";      }      return out; @@ -91,7 +102,7 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Frontend& frontend)      StreamStateSaver state_saver{out};      out << "Genesys_Frontend{\n" -        << "    id: " << static_cast<unsigned>(frontend.id) << '\n' +        << "    id: " << frontend.id << '\n'          << "    regs: " << format_indent_braced_list(4, frontend.regs) << '\n'          << std::hex          << "    reg2[0]: " << frontend.reg2[0] << '\n' @@ -112,33 +123,23 @@ 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 Genesys_Sensor& sensor)  {      out << "Genesys_Sensor{\n"          << "    sensor_id: " << static_cast<unsigned>(sensor.sensor_id) << '\n' -        << "    optical_res: " << sensor.optical_res << '\n' +        << "    full_resolution: " << sensor.full_resolution << '\n' +        << "    optical_resolution: " << sensor.get_optical_resolution() << '\n'          << "    resolutions: " << format_indent_braced_list(4, sensor.resolutions) << '\n'          << "    channels: " << format_vector_unsigned(4, sensor.channels) << '\n'          << "    method: " << sensor.method << '\n' -        << "    register_dpihw_override: " << sensor.register_dpihw_override << '\n' -        << "    logical_dpihw_override: " << sensor.logical_dpihw_override << '\n' -        << "    dpiset_override: " << sensor.dpiset_override << '\n' -        << "    ccd_size_divisor: " << sensor.ccd_size_divisor << '\n' -        << "    pixel_count_multiplier: " << sensor.pixel_count_multiplier << '\n' +        << "    register_dpihw: " << sensor.register_dpihw << '\n' +        << "    register_dpiset: " << sensor.register_dpiset << '\n' +        << "    shading_factor: " << sensor.shading_factor << '\n' +        << "    shading_pixel_offset: " << sensor.shading_pixel_offset << '\n' +        << "    pixel_count_ratio: " << sensor.pixel_count_ratio << '\n' +        << "    output_pixel_offset: " << sensor.output_pixel_offset << '\n'          << "    black_pixels: " << sensor.black_pixels << '\n'          << "    dummy_pixel: " << sensor.dummy_pixel << '\n' -        << "    ccd_start_xoffset: " << sensor.ccd_start_xoffset << '\n' -        << "    sensor_pixels: " << sensor.sensor_pixels << '\n'          << "    fau_gain_white_ref: " << sensor.fau_gain_white_ref << '\n'          << "    gain_white_ref: " << sensor.gain_white_ref << '\n'          << "    exposure: " << format_indent_braced_list(4, sensor.exposure) << '\n' @@ -146,8 +147,9 @@ std::ostream& operator<<(std::ostream& out, const Genesys_Sensor& sensor)          << "    segment_size: " << sensor.segment_size << '\n'          << "    segment_order: "          << format_indent_braced_list(4, format_vector_unsigned(4, sensor.segment_order)) << '\n' -        << "    stagger_config: " << format_indent_braced_list(4, sensor.stagger_config) << '\n' -        << "    custom_base_regs: " << format_indent_braced_list(4, sensor.custom_base_regs) << '\n' +        << "    stagger_x: " << sensor.stagger_x << '\n' +        << "    stagger_y: " << sensor.stagger_y << '\n' +        << "    use_host_side_calib: " << sensor.use_host_side_calib << '\n'          << "    custom_regs: " << format_indent_braced_list(4, sensor.custom_regs) << '\n'          << "    custom_fe_regs: " << format_indent_braced_list(4, sensor.custom_fe_regs) << '\n'          << "    gamma.red: " << sensor.gamma[0] << '\n' diff --git a/backend/genesys/sensor.h b/backend/genesys/sensor.h index e70728e..ca6fef7 100644 --- a/backend/genesys/sensor.h +++ b/backend/genesys/sensor.h @@ -47,6 +47,7 @@  #include "enums.h"  #include "register.h"  #include "serialize.h" +#include "value_filter.h"  #include <array>  #include <functional> @@ -72,31 +73,30 @@ class StaggerConfig  {  public:      StaggerConfig() = default; -    StaggerConfig(unsigned min_resolution, unsigned lines_at_min) : -        min_resolution_{min_resolution}, -        lines_at_min_{lines_at_min} +    explicit StaggerConfig(std::initializer_list<std::size_t> shifts) : +        shifts_{shifts}      {      } -    unsigned stagger_at_resolution(unsigned xresolution, unsigned yresolution) const +    std::size_t max_shift() const      { -        if (min_resolution_ == 0 || xresolution < min_resolution_) +        if (shifts_.empty()) {              return 0; -        return yresolution / min_resolution_ * lines_at_min_; +        } +        return *std::max_element(shifts_.begin(), shifts_.end());      } -    unsigned min_resolution() const { return min_resolution_; } -    unsigned lines_at_min() const { return lines_at_min_; } +    bool empty() const { return shifts_.empty(); } +    std::size_t size() const { return shifts_.size(); } +    const std::vector<std::size_t>& shifts() const { return shifts_; }      bool operator==(const StaggerConfig& other) const      { -        return min_resolution_ == other.min_resolution_ && -                lines_at_min_ == other.lines_at_min_; +        return shifts_ == other.shifts_;      }  private: -    unsigned min_resolution_ = 0; -    unsigned lines_at_min_ = 0; +    std::vector<std::size_t> shifts_;      template<class Stream>      friend void serialize(Stream& str, StaggerConfig& x); @@ -105,8 +105,7 @@ private:  template<class Stream>  void serialize(Stream& str, StaggerConfig& x)  { -    serialize(str, x.min_resolution_); -    serialize(str, x.lines_at_min_); +    serialize(str, x.shifts_);  }  std::ostream& operator<<(std::ostream& out, const StaggerConfig& config); @@ -114,9 +113,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 +    ANALOG_DEVICES_GL847, // old code path, likely wrong calculation +    WOLFSON_GL124, // old code path, likely wrong calculation  };  inline void serialize(std::istream& str, FrontendType& x) @@ -242,54 +246,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_); -} - -  struct Genesys_Sensor {      Genesys_Sensor() = default; @@ -300,10 +256,15 @@ struct Genesys_Sensor {      // sensor resolution in CCD pixels. Note that we may read more than one CCD pixel per logical      // pixel, see ccd_pixels_per_system_pixel() -    unsigned optical_res = 0; +    unsigned full_resolution = 0; + +    // sensor resolution in pixel values that are read by the chip. Many scanners make low +    // resolutions faster by configuring the timings in such a way that 1/2 or 1/4 of pixel values +    // that are read. If zero, then it is equal to `full_resolution`. +    unsigned optical_resolution = 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 }; @@ -313,29 +274,32 @@ struct Genesys_Sensor {      // The scanner may be setup to use a custom dpihw that does not correspond to any actual      // resolution. The value zero does not set the override. -    unsigned register_dpihw_override = 0; - -    // The scanner may be setup to use a custom logical dpihw that does not correspond to any actual -    // resolution. The value zero does not set the override. -    unsigned logical_dpihw_override = 0; +    unsigned register_dpihw = 0;      // The scanner may be setup to use a custom dpiset value that does not correspond to any actual      // resolution. The value zero does not set the override. -    unsigned dpiset_override = 0; +    unsigned register_dpiset = 0; + +    // The resolution to use for shading calibration +    unsigned shading_resolution = 0; -    // CCD may present itself as half or quarter-size CCD on certain resolutions -    int ccd_size_divisor = 1; +    // How many real pixels correspond to one shading pixel that is sent to the scanner +    unsigned shading_factor = 1; -    // Some scanners need an additional multiplier over the scan coordinates -    int pixel_count_multiplier = 1; +    // How many pixels the shading data is offset to the right from the acquired data. Calculated +    // in shading resolution. +    int shading_pixel_offset = 0; + +    // This defines the ratio between logical pixel coordinates and the pixel coordinates sent to +    // the scanner. +    Ratio pixel_count_ratio = Ratio{1, 1}; + +    // The offset in pixels in terms of scan resolution that needs to be applied to scan position. +    int output_pixel_offset = 0;      int black_pixels = 0;      // value of the dummy register      int dummy_pixel = 0; -    // last pixel of CCD margin at optical resolution -    int ccd_start_xoffset = 0; -    // total pixels used by the sensor -    int sensor_pixels = 0;      // TA CCD target code (reference gain)      int fau_gain_white_ref = 0;      // CCD target code (reference gain) @@ -346,8 +310,7 @@ struct Genesys_Sensor {      int exposure_lperiod = -1; -    // the number of pixels in a single segment. -    // only on gl843 +    // the number of pixels in a single segment. This is counted in output resolution.      unsigned segment_size = 0;      // the order of the segments, if any, for the sensor. If the sensor is not segmented or uses @@ -355,31 +318,28 @@ struct Genesys_Sensor {      // only on gl843      std::vector<unsigned> segment_order; -    // some CCDs use two arrays of pixels for double resolution. On such CCDs when scanning at -    // high-enough resolution, every other pixel column is shifted -    StaggerConfig stagger_config; +    // some CCDs use multiple arrays of pixels for double or quadruple resolution. This can result +    // in the following effects on the output: +    //  - every n-th column may be shifted in a vertical direction. +    //  - the columns themselves may be reordered in arbitrary order and may require shifting +    //    in X direction. +    StaggerConfig stagger_x; +    StaggerConfig stagger_y; + +    // True if calibration should be performed on host-side +    bool use_host_side_calib = false; -    GenesysRegisterSettingSet custom_base_regs; // gl646-specific      GenesysRegisterSettingSet custom_regs;      GenesysRegisterSettingSet custom_fe_regs;      // red, green and blue gamma coefficient for default gamma tables      AssignableArray<float, 3> gamma; -    std::function<unsigned(const Genesys_Sensor&, unsigned)> get_logical_hwdpi_fun; -    std::function<unsigned(const Genesys_Sensor&, unsigned)> get_register_hwdpi_fun; -    std::function<unsigned(const Genesys_Sensor&, unsigned)> get_ccd_size_divisor_fun; -    std::function<unsigned(const Genesys_Sensor&, unsigned)> get_hwdpi_divisor_fun; - -    unsigned get_logical_hwdpi(unsigned xres) const { return get_logical_hwdpi_fun(*this, xres); } -    unsigned get_register_hwdpi(unsigned xres) const { return get_register_hwdpi_fun(*this, xres); } -    unsigned get_ccd_size_divisor_for_dpi(unsigned xres) const -    { -        return get_ccd_size_divisor_fun(*this, xres); -    } -    unsigned get_hwdpi_divisor_for_dpi(unsigned xres) const +    unsigned get_optical_resolution() const      { -        return get_hwdpi_divisor_fun(*this, xres); +        if (optical_resolution != 0) +            return optical_resolution; +        return full_resolution;      }      // how many CCD pixels are processed per system pixel time. This corresponds to CKSEL + 1 @@ -405,22 +365,26 @@ struct Genesys_Sensor {      bool operator==(const Genesys_Sensor& other) const      {          return sensor_id == other.sensor_id && -            optical_res == other.optical_res && +            full_resolution == other.full_resolution && +            optical_resolution == other.optical_resolution &&              resolutions == other.resolutions &&              method == other.method && -            ccd_size_divisor == other.ccd_size_divisor && +            shading_resolution == other.shading_resolution && +            shading_factor == other.shading_factor && +            shading_pixel_offset == other.shading_pixel_offset && +            pixel_count_ratio == other.pixel_count_ratio && +            output_pixel_offset == other.output_pixel_offset &&              black_pixels == other.black_pixels &&              dummy_pixel == other.dummy_pixel && -            ccd_start_xoffset == other.ccd_start_xoffset && -            sensor_pixels == other.sensor_pixels &&              fau_gain_white_ref == other.fau_gain_white_ref &&              gain_white_ref == other.gain_white_ref &&              exposure == other.exposure &&              exposure_lperiod == other.exposure_lperiod &&              segment_size == other.segment_size &&              segment_order == other.segment_order && -            stagger_config == other.stagger_config && -            custom_base_regs == other.custom_base_regs && +            stagger_x == other.stagger_x && +            stagger_y == other.stagger_y && +            use_host_side_calib == other.use_host_side_calib &&              custom_regs == other.custom_regs &&              custom_fe_regs == other.custom_fe_regs &&              gamma == other.gamma; @@ -431,14 +395,16 @@ template<class Stream>  void serialize(Stream& str, Genesys_Sensor& x)  {      serialize(str, x.sensor_id); -    serialize(str, x.optical_res); +    serialize(str, x.full_resolution);      serialize(str, x.resolutions);      serialize(str, x.method); -    serialize(str, x.ccd_size_divisor); +    serialize(str, x.shading_resolution); +    serialize(str, x.shading_factor); +    serialize(str, x.shading_pixel_offset); +    serialize(str, x.output_pixel_offset); +    serialize(str, x.pixel_count_ratio);      serialize(str, x.black_pixels);      serialize(str, x.dummy_pixel); -    serialize(str, x.ccd_start_xoffset); -    serialize(str, x.sensor_pixels);      serialize(str, x.fau_gain_white_ref);      serialize(str, x.gain_white_ref);      serialize_newline(str); @@ -451,9 +417,11 @@ void serialize(Stream& str, Genesys_Sensor& x)      serialize_newline(str);      serialize(str, x.segment_order);      serialize_newline(str); -    serialize(str, x.stagger_config); +    serialize(str, x.stagger_x); +    serialize_newline(str); +    serialize(str, x.stagger_y);      serialize_newline(str); -    serialize(str, x.custom_base_regs); +    serialize(str, x.use_host_side_calib);      serialize_newline(str);      serialize(str, x.custom_regs);      serialize_newline(str); diff --git a/backend/genesys/settings.cpp b/backend/genesys/settings.cpp index 41c66de..c2b54dc 100644 --- a/backend/genesys/settings.cpp +++ b/backend/genesys/settings.cpp @@ -72,14 +72,20 @@ std::ostream& operator<<(std::ostream& out, const SetupParams& params)  {      StreamStateSaver state_saver{out}; +    bool reverse = has_flag(params.flags, ScanFlag::REVERSE); +      out << "SetupParams{\n" -        << "    xres: " << params.xres << " yres: " << params.yres << '\n' -        << "    lines: " << params.lines << '\n' -        << "    pixels per line (actual): " << params.pixels << '\n' -        << "    pixels per line (requested): " << params.requested_pixels << '\n' +        << "    xres: " << params.xres +            << " startx: " << params.startx +            << " pixels per line (actual): " << params.pixels +            << " pixels per line (requested): " << params.requested_pixels << '\n' + +        << "    yres: " << params.yres +            << " lines: " << params.lines +            << " starty: " << params.starty << (reverse ? " (reverse)" : "") << '\n' +          << "    depth: " << params.depth << '\n'          << "    channels: " << params.channels << '\n' -        << "    startx: " << params.startx << " starty: " << params.starty << '\n'          << "    scan_mode: " << params.scan_mode << '\n'          << "    color_filter: " << params.color_filter << '\n'          << "    flags: " << params.flags << '\n' @@ -87,16 +93,56 @@ std::ostream& operator<<(std::ostream& out, const SetupParams& params)      return out;  } +bool ScanSession::operator==(const ScanSession& other) const +{ +    return params == other.params && +        computed == other.computed && +        full_resolution == other.full_resolution && +        optical_resolution == other.optical_resolution && +        optical_pixels == other.optical_pixels && +        optical_pixels_raw == other.optical_pixels_raw && +        optical_line_count == other.optical_line_count && +        output_resolution == other.output_resolution && +        output_startx == other.output_startx && +        output_pixels == other.output_pixels && +        output_channel_bytes == other.output_channel_bytes && +        output_line_bytes == other.output_line_bytes && +        output_line_bytes_raw == other.output_line_bytes_raw && +        output_line_bytes_requested == other.output_line_bytes_requested && +        output_line_count == other.output_line_count && +        output_total_bytes_raw == other.output_total_bytes_raw && +        output_total_bytes == other.output_total_bytes && +        num_staggered_lines == other.num_staggered_lines && +        max_color_shift_lines == other.max_color_shift_lines && +        color_shift_lines_r == other.color_shift_lines_r && +        color_shift_lines_g == other.color_shift_lines_g && +        color_shift_lines_b == other.color_shift_lines_b && +        stagger_x == other.stagger_x && +        stagger_y == other.stagger_y && +        segment_count == other.segment_count && +        pixel_startx == other.pixel_startx && +        pixel_endx == other.pixel_endx && +        pixel_count_ratio == other.pixel_count_ratio && +        conseq_pixel_dist == other.conseq_pixel_dist && +        output_segment_pixel_group_count == other.output_segment_pixel_group_count && +        output_segment_start_offset == other.output_segment_start_offset && +        shading_pixel_offset == other.shading_pixel_offset && +        buffer_size_read == other.buffer_size_read && +        enable_ledadd == other.enable_ledadd && +        use_host_side_calib == other.use_host_side_calib; +} +  std::ostream& operator<<(std::ostream& out, const ScanSession& session)  {      out << "ScanSession{\n"          << "    computed: " << session.computed << '\n' -        << "    hwdpi_divisor: " << session.hwdpi_divisor << '\n' -        << "    ccd_size_divisor: " << session.ccd_size_divisor << '\n' +        << "    full_resolution: " << session.full_resolution << '\n'          << "    optical_resolution: " << session.optical_resolution << '\n'          << "    optical_pixels: " << session.optical_pixels << '\n'          << "    optical_pixels_raw: " << session.optical_pixels_raw << '\n' +        << "    optical_line_count: " << session.optical_line_count << '\n'          << "    output_resolution: " << session.output_resolution << '\n' +        << "    output_startx: " << session.output_startx << '\n'          << "    output_pixels: " << session.output_pixels << '\n'          << "    output_line_bytes: " << session.output_line_bytes << '\n'          << "    output_line_bytes_raw: " << session.output_line_bytes_raw << '\n' @@ -107,20 +153,19 @@ std::ostream& operator<<(std::ostream& out, const ScanSession& session)          << "    color_shift_lines_b: " << session.color_shift_lines_b << '\n'          << "    max_color_shift_lines: " << session.max_color_shift_lines << '\n'          << "    enable_ledadd: " << session.enable_ledadd << '\n' +        << "    stagger_x: " << session.stagger_x << '\n' +        << "    stagger_y: " << session.stagger_y << '\n'          << "    segment_count: " << session.segment_count << '\n'          << "    pixel_startx: " << session.pixel_startx << '\n'          << "    pixel_endx: " << session.pixel_endx << '\n' +        << "    pixel_count_ratio: " << session.pixel_count_ratio << '\n'          << "    conseq_pixel_dist: " << session.conseq_pixel_dist << '\n'          << "    output_segment_pixel_group_count: "              << session.output_segment_pixel_group_count << '\n' +        << "    shading_pixel_offset: " << session.shading_pixel_offset << '\n'          << "    buffer_size_read: " << session.buffer_size_read << '\n' -        << "    buffer_size_read: " << session.buffer_size_lines << '\n' -        << "    buffer_size_shrink: " << session.buffer_size_shrink << '\n' -        << "    buffer_size_out: " << session.buffer_size_out << '\n' -        << "    filters: " -            << (session.pipeline_needs_reorder ? " reorder": "") -            << (session.pipeline_needs_ccd ? " ccd": "") -            << (session.pipeline_needs_shrink ? " shrink": "") << '\n' +        << "    enable_ledadd: " << session.enable_ledadd << '\n' +        << "    use_host_side_calib: " << session.use_host_side_calib << '\n'          << "    params: " << format_indent_braced_list(4, session.params) << '\n'          << "}";      return out; diff --git a/backend/genesys/settings.h b/backend/genesys/settings.h index a697e60..f78845b 100644 --- a/backend/genesys/settings.h +++ b/backend/genesys/settings.h @@ -46,6 +46,8 @@  #include "enums.h"  #include "serialize.h" +#include "utilities.h" +#include "sensor.h"  namespace genesys { @@ -60,9 +62,9 @@ struct Genesys_Settings      unsigned yres = 0;      //x start on scan table in mm -    double tl_x = 0; +    float tl_x = 0;      // y start on scan table in mm -    double tl_y = 0; +    float tl_y = 0;      // number of lines at scan resolution      unsigned int lines = 0; @@ -79,15 +81,6 @@ struct Genesys_Settings      // true if scan is true gray, false if monochrome scan      int true_gray = 0; -    // lineart threshold -    int threshold = 0; - -    // lineart threshold curve for dynamic rasterization -    int threshold_curve = 0; - -    // Disable interpolation for xres<yres -    int disable_interpolation = 0; -      // value for contrast enhancement in the [-100..100] range      int contrast = 0; @@ -116,12 +109,13 @@ struct SetupParams {      unsigned xres = NOT_SET;      // resolution in y direction      unsigned yres = NOT_SET; -    // start pixel in X direction, from dummy_pixel + 1 +    // start pixel in X direction, from dummy_pixel + 1. Counted in terms of xres.      unsigned startx = NOT_SET;      // start pixel in Y direction, counted according to base_ydpi      unsigned starty = NOT_SET; -    // the number of pixels in X direction. Note that each logical pixel may correspond to more -    // than one CCD pixel, see CKSEL and GenesysSensor::ccd_pixels_per_system_pixel() +    // the number of pixels in X direction. Counted in terms of xres. +    // Note that each logical pixel may correspond to more than one CCD pixel, see CKSEL and +    // GenesysSensor::ccd_pixels_per_system_pixel()      unsigned pixels = NOT_SET;      // the number of pixels in the X direction as requested by the frontend. This will be different @@ -144,7 +138,7 @@ struct SetupParams {      ColorFilter color_filter = static_cast<ColorFilter>(NOT_SET); -    ScanFlag flags; +    ScanFlag flags = ScanFlag::NONE;      unsigned get_requested_pixels() const      { @@ -210,15 +204,10 @@ struct ScanSession {      // whether the session setup has been computed via compute_session()      bool computed = false; -    // specifies the reduction (if any) of hardware dpi on the Genesys chip side. -    // except gl646 -    unsigned hwdpi_divisor = 1; - -    // specifies the reduction (if any) of CCD effective dpi which is performed by latching the -    // data coming from CCD in such a way that 1/2 or 3/4 of pixel data is ignored. -    unsigned ccd_size_divisor = 1; +    // specifies the full resolution of the sensor that is being used. +    unsigned full_resolution = 0; -    // the optical resolution of the scanner. +    // the optical resolution of the sensor that is being used.      unsigned optical_resolution = 0;      // the number of pixels at the optical resolution, not including segmentation overhead. @@ -228,10 +217,15 @@ struct ScanSession {      // only on gl846, g847      unsigned optical_pixels_raw = 0; +    // the number of optical scan lines. Equal to output_line_count on CCD scanners. +    unsigned optical_line_count = 0; +      // the resolution of the output data. -    // gl843-only      unsigned output_resolution = 0; +    // the offset in pixels from the beginning of output data +    unsigned output_startx = 0; +      // the number of pixels in output data (after desegmentation)      unsigned output_pixels = 0; @@ -259,7 +253,7 @@ struct ScanSession {      unsigned output_total_bytes = 0;      // the number of staggered lines (i.e. lines that overlap during scanning due to line being -    // thinner than the CCD element) +    // thinner than the CCD element). Computed according to stagger_y.      unsigned num_staggered_lines = 0;      // the number of lines that color channels shift due to different physical positions of @@ -273,6 +267,11 @@ struct ScanSession {      // actual line shift of the blue color      unsigned color_shift_lines_b = 0; +    // The shifts that need to be applied to the output pixels in x direction. +    StaggerConfig stagger_x; +    // The shifts that need to be applied to the output pixels in y direction. +    StaggerConfig stagger_y; +      // the number of scanner segments used in the current scan      unsigned segment_count = 1; @@ -280,8 +279,18 @@ struct ScanSession {      unsigned pixel_startx = 0;      unsigned pixel_endx = 0; -    // certain scanners require the logical pixel count to be multiplied on certain resolutions -    unsigned pixel_count_multiplier = 1; +    /*  The following defines the ratio between logical pixel count and pixel count setting sent to +        the scanner. The ratio is affected by the following: + +        - Certain scanners just like to multiply the pixel number by a multiplier that depends on +          the resolution. + +        - The sensor may be configured to output one value per multiple physical pixels + +        - The scanner will automatically average the pixels that come from the sensor using a +          certain ratio. +    */ +    Ratio pixel_count_ratio = Ratio{1, 1};      // Distance in pixels between consecutive pixels, e.g. between odd and even pixels. Note that      // the number of segments can be large. @@ -297,19 +306,18 @@ struct ScanSession {      // Currently it's always zero.      unsigned output_segment_start_offset = 0; -    // the sizes of the corresponding buffers +    // How many pixels the shading data is offset to the right from the acquired data. Calculated +    // in shading resolution. +    int shading_pixel_offset = 0; + +    // the size of the read buffer.      size_t buffer_size_read = 0; -    size_t buffer_size_lines = 0; -    size_t buffer_size_shrink = 0; -    size_t buffer_size_out = 0;      // whether to enable ledadd functionality      bool enable_ledadd = false; -    // what pipeline modifications are needed -    bool pipeline_needs_reorder = false; -    bool pipeline_needs_ccd = false; -    bool pipeline_needs_shrink = false; +    // whether calibration should be performed host-side +    bool use_host_side_calib = false;      void assert_computed() const      { @@ -317,10 +325,53 @@ struct ScanSession {              throw std::runtime_error("ScanSession is not computed");          }      } + +    bool operator==(const ScanSession& other) const;  };  std::ostream& operator<<(std::ostream& out, const ScanSession& session); +template<class Stream> +void serialize(Stream& str, ScanSession& x) +{ +    serialize(str, x.params); +    serialize_newline(str); +    serialize(str, x.computed); +    serialize(str, x.full_resolution); +    serialize(str, x.optical_resolution); +    serialize(str, x.optical_pixels); +    serialize(str, x.optical_pixels_raw); +    serialize(str, x.optical_line_count); +    serialize(str, x.output_resolution); +    serialize(str, x.output_startx); +    serialize(str, x.output_pixels); +    serialize(str, x.output_channel_bytes); +    serialize(str, x.output_line_bytes); +    serialize(str, x.output_line_bytes_raw); +    serialize(str, x.output_line_bytes_requested); +    serialize(str, x.output_line_count); +    serialize(str, x.output_total_bytes_raw); +    serialize(str, x.output_total_bytes); +    serialize(str, x.num_staggered_lines); +    serialize(str, x.max_color_shift_lines); +    serialize(str, x.color_shift_lines_r); +    serialize(str, x.color_shift_lines_g); +    serialize(str, x.color_shift_lines_b); +    serialize(str, x.stagger_x); +    serialize(str, x.stagger_y); +    serialize(str, x.segment_count); +    serialize(str, x.pixel_startx); +    serialize(str, x.pixel_endx); +    serialize(str, x.pixel_count_ratio); +    serialize(str, x.conseq_pixel_dist); +    serialize(str, x.output_segment_pixel_group_count); +    serialize(str, x.output_segment_start_offset); +    serialize(str, x.shading_pixel_offset); +    serialize(str, x.buffer_size_read); +    serialize(str, x.enable_ledadd); +    serialize(str, x.use_host_side_calib); +} +  std::ostream& operator<<(std::ostream& out, const SANE_Parameters& params);  } // namespace genesys diff --git a/backend/genesys/tables_frontend.cpp b/backend/genesys/tables_frontend.cpp index 1edf32f..5eb6e3c 100644 --- a/backend/genesys/tables_frontend.cpp +++ b/backend/genesys/tables_frontend.cpp @@ -60,7 +60,8 @@ void genesys_init_frontend_tables()      GenesysFrontendLayout analog_devices;      analog_devices.type = FrontendType::ANALOG_DEVICES; - +    analog_devices.offset_addr = { 0x05, 0x06, 0x07 }; +    analog_devices.gain_addr = { 0x02, 0x03, 0x04 };      Genesys_Frontend fe;      fe.id = AdcId::WOLFSON_UMAX; @@ -198,6 +199,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 }, @@ -218,6 +220,30 @@ void genesys_init_frontend_tables()      fe = Genesys_Frontend(); +    fe.id = AdcId::CANON_LIDE_90; +    fe.layout = wolfson_layout; +    fe.layout.type = FrontendType::WOLFSON; +    fe.regs = { +        { 0x01, 0x23 }, +        { 0x02, 0x07 }, +        { 0x03, 0x29 }, +        { 0x06, 0x0d }, +        { 0x08, 0x00 }, +        { 0x09, 0x16 }, +        { 0x20, 0x4d }, +        { 0x21, 0x4d }, +        { 0x22, 0x4d }, +        { 0x23, 0x4d }, +        { 0x28, 0x14 }, +        { 0x29, 0x14 }, +        { 0x2a, 0x14 }, +        { 0x2b, 0x14 }, +    }; +    fe.reg2 = {0x00, 0x00, 0x00}; +    s_frontends->push_back(fe); + + +    fe = Genesys_Frontend();      fe.id = AdcId::AD_XP200;      fe.layout = wolfson_layout;      fe.regs = { @@ -242,6 +268,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 +313,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 }, @@ -307,45 +335,35 @@ void genesys_init_frontend_tables()      fe = Genesys_Frontend();      fe.id = AdcId::CANON_LIDE_200; -    fe.layout = wolfson_layout; +    fe.layout = analog_devices; +    fe.layout.type = FrontendType::ANALOG_DEVICES_GL847;      fe.regs = {          { 0x00, 0x9d },          { 0x01, 0x91 }, -        { 0x02, 0x00 }, -        { 0x03, 0x00 }, -        { 0x20, 0x00 }, -        { 0x21, 0x3f }, -        { 0x22, 0x00 }, -        { 0x24, 0x00 }, -        { 0x25, 0x00 }, -        { 0x26, 0x00 }, -        { 0x28, 0x32 }, -        { 0x29, 0x04 }, -        { 0x2a, 0x00 }, +        { 0x02, 0x32 }, +        { 0x03, 0x04 }, +        { 0x04, 0x00 }, +        { 0x05, 0x00 }, +        { 0x06, 0x3f }, +        { 0x07, 0x00 },      }; -    fe.reg2 = {0x00, 0x00, 0x00};      s_frontends->push_back(fe);      fe = Genesys_Frontend();      fe.id = AdcId::CANON_LIDE_700F; -    fe.layout = wolfson_layout; +    fe.layout = analog_devices; +    fe.layout.type = FrontendType::ANALOG_DEVICES_GL847;      fe.regs = {          { 0x00, 0x9d },          { 0x01, 0x9e }, -        { 0x02, 0x00 }, -        { 0x03, 0x00 }, -        { 0x20, 0x00 }, -        { 0x21, 0x3f }, -        { 0x22, 0x00 }, -        { 0x24, 0x00 }, -        { 0x25, 0x00 }, -        { 0x26, 0x00 }, -        { 0x28, 0x2f }, -        { 0x29, 0x04 }, -        { 0x2a, 0x00 }, +        { 0x02, 0x2f }, +        { 0x03, 0x04 }, +        { 0x04, 0x00 }, +        { 0x05, 0x00 }, +        { 0x06, 0x3f }, +        { 0x07, 0x00 },      }; -    fe.reg2 = {0x00, 0x00, 0x00};      s_frontends->push_back(fe); @@ -396,6 +414,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 +441,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 }, @@ -464,6 +484,23 @@ void genesys_init_frontend_tables()      fe = Genesys_Frontend(); +    fe.id = AdcId::PLUSTEK_OPTICFILM_7200; +    fe.layout = analog_devices; +    fe.regs = { +        { 0x00, 0xf8 }, +        { 0x01, 0x80 }, +        { 0x02, 0x2e }, +        { 0x03, 0x17 }, +        { 0x04, 0x20 }, +        { 0x05, 0x0109 }, +        { 0x06, 0x01 }, +        { 0x07, 0x0104 }, +    }; +    fe.reg2 = {0x00, 0x00, 0x00}; +    s_frontends->push_back(fe); + + +    fe = Genesys_Frontend();      fe.id = AdcId::PLUSTEK_OPTICFILM_7200I;      fe.layout = analog_devices;      fe.regs = { @@ -498,6 +535,23 @@ void genesys_init_frontend_tables()      fe = Genesys_Frontend(); +    fe.id = AdcId::PLUSTEK_OPTICFILM_7400; +    fe.layout = analog_devices; +    fe.regs = { +        { 0x00, 0xf8 }, +        { 0x01, 0x80 }, +        { 0x02, 0x1f }, +        { 0x03, 0x14 }, +        { 0x04, 0x19 }, +        { 0x05, 0x1b }, +        { 0x06, 0x1e }, +        { 0x07, 0x0e }, +    }; +    fe.reg2 = {0x00, 0x00, 0x00}; +    s_frontends->push_back(fe); + + +    fe = Genesys_Frontend();      fe.id = AdcId::PLUSTEK_OPTICFILM_7500I;      fe.layout = analog_devices;      fe.regs = { @@ -515,6 +569,23 @@ void genesys_init_frontend_tables()      fe = Genesys_Frontend(); +    fe.id = AdcId::PLUSTEK_OPTICFILM_8200I; +    fe.layout = analog_devices; +    fe.regs = { +        { 0x00, 0xf8 }, +        { 0x01, 0x80 }, +        { 0x02, 0x28 }, +        { 0x03, 0x20 }, +        { 0x04, 0x28 }, +        { 0x05, 0x2f }, +        { 0x06, 0x2d }, +        { 0x07, 0x23 }, +    }; +    fe.reg2 = {0x00, 0x00, 0x00}; +    s_frontends->push_back(fe); + + +    fe = Genesys_Frontend();      fe.id = AdcId::CANON_4400F;      fe.layout = wolfson_layout;      fe.regs = { @@ -537,6 +608,26 @@ void genesys_init_frontend_tables()      fe = Genesys_Frontend(); +    fe.id = AdcId::CANON_5600F; +    fe.layout = wolfson_layout; +    fe.regs = { +        { 0x01, 0x23 }, +        { 0x02, 0x24 }, +        { 0x03, 0x2f }, +        { 0x06, 0x00 }, +        { 0x08, 0x00 }, +        { 0x09, 0x00 }, +        { 0x20, 0x60 }, +        { 0x21, 0x60 }, +        { 0x22, 0x60 }, +        { 0x28, 0x77 }, +        { 0x29, 0x77 }, +        { 0x2a, 0x77 }, +    }; +    s_frontends->push_back(fe); + + +    fe = Genesys_Frontend();      fe.id = AdcId::CANON_8400F;      fe.layout = wolfson_layout;      fe.regs = { @@ -583,6 +674,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 +697,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 +724,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 }, diff --git a/backend/genesys/tables_gpo.cpp b/backend/genesys/tables_gpo.cpp index 2c9ad5e..5c1c54f 100644 --- a/backend/genesys/tables_gpo.cpp +++ b/backend/genesys/tables_gpo.cpp @@ -131,6 +131,18 @@ void genesys_init_gpo_tables()      gpo = Genesys_Gpo(); +    gpo.id = GpioId::CANON_LIDE_90; +    gpo.regs = { +        { 0x6b, 0x03 }, +        { 0x6c, 0x74 }, +        { 0x6d, 0x80 }, +        { 0x6e, 0x7f }, +        { 0x6f, 0xe0 }, +    }; +    s_gpo->push_back(gpo); + + +    gpo = Genesys_Gpo();      gpo.id = GpioId::XP200;      gpo.regs = {          { 0x66, 0x30 }, @@ -188,10 +200,15 @@ void genesys_init_gpo_tables()      gpo = Genesys_Gpo();      gpo.id = GpioId::CANON_LIDE_200;      gpo.regs = { -        { 0x6c, 0xfb }, // 0xfb when idle , 0xf9/0xe9 (1200) when scanning +        { 0x6b, 0x02 }, +        { 0x6c, 0xf9 }, // 0xfb when idle , 0xf9/0xe9 (1200) when scanning          { 0x6d, 0x20 },          { 0x6e, 0xff },          { 0x6f, 0x00 }, +        { 0xa6, 0x04 }, +        { 0xa7, 0x04 }, +        { 0xa8, 0x00 }, +        { 0xa9, 0x00 },      };      s_gpo->push_back(gpo); @@ -199,10 +216,15 @@ void genesys_init_gpo_tables()      gpo = Genesys_Gpo();      gpo.id = GpioId::CANON_LIDE_700F;      gpo.regs = { +        { 0x6b, 0x06 },          { 0x6c, 0xdb },          { 0x6d, 0xff },          { 0x6e, 0xff },          { 0x6f, 0x80 }, +        { 0xa6, 0x15 }, +        { 0xa7, 0x07 }, +        { 0xa8, 0x20 }, +        { 0xa9, 0x10 },      };      s_gpo->push_back(gpo); @@ -293,6 +315,19 @@ void genesys_init_gpo_tables()      gpo = Genesys_Gpo(); +    gpo.id = GpioId::PLUSTEK_OPTICFILM_7200; +    gpo.regs = { +        { 0x6b, 0x33 }, +        { 0x6c, 0x00 }, +        { 0x6d, 0x80 }, +        { 0x6e, 0x0c }, +        { 0x6f, 0x80 }, +        { 0x7e, 0x00 } +    }; +    s_gpo->push_back(gpo); + + +    gpo = Genesys_Gpo();      gpo.id = GpioId::PLUSTEK_OPTICFILM_7200I;      gpo.regs = {          { 0x6c, 0x4c }, @@ -320,6 +355,16 @@ void genesys_init_gpo_tables()      };      s_gpo->push_back(gpo); + +    gpo = Genesys_Gpo(); +    gpo.id = GpioId::PLUSTEK_OPTICFILM_7400; +    gpo.regs = { +        { 0x6b, 0x30 }, { 0x6c, 0x4c }, { 0x6d, 0x80 }, { 0x6e, 0x4c }, { 0x6f, 0x80 }, +        { 0xa6, 0x00 }, { 0xa7, 0x07 }, { 0xa8, 0x20 }, { 0xa9, 0x01 }, +    }; +    s_gpo->push_back(gpo); + +      gpo = Genesys_Gpo();      gpo.id = GpioId::PLUSTEK_OPTICFILM_7500I;      gpo.regs = { @@ -334,6 +379,16 @@ void genesys_init_gpo_tables()      };      s_gpo->push_back(gpo); + +    gpo = Genesys_Gpo(); +    gpo.id = GpioId::PLUSTEK_OPTICFILM_8200I; +    gpo.regs = { +        { 0x6b, 0x30 }, { 0x6c, 0x4c }, { 0x6d, 0x80 }, { 0x6e, 0x4c }, { 0x6f, 0x80 }, +        { 0xa6, 0x00 }, { 0xa7, 0x07 }, { 0xa8, 0x20 }, { 0xa9, 0x01 }, +    }; +    s_gpo->push_back(gpo); + +      gpo = Genesys_Gpo();      gpo.id = GpioId::CANON_4400F;      gpo.regs = { @@ -350,6 +405,22 @@ void genesys_init_gpo_tables()      gpo = Genesys_Gpo(); +    gpo.id = GpioId::CANON_5600F; +    gpo.regs = { +        { 0x6b, 0x87 }, +        { 0x6c, 0xf0 }, +        { 0x6d, 0x5f }, +        { 0x6e, 0x7f }, +        { 0x6f, 0xa0 }, +        { 0xa6, 0x07 }, +        { 0xa7, 0x1c }, +        { 0xa8, 0x00 }, +        { 0xa9, 0x04 }, +    }; +    s_gpo->push_back(gpo); + + +    gpo = Genesys_Gpo();      gpo.id = GpioId::CANON_8400F;      gpo.regs = {          { 0x6c, 0x9a }, @@ -382,10 +453,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 +462,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); diff --git a/backend/genesys/tables_memory_layout.cpp b/backend/genesys/tables_memory_layout.cpp new file mode 100644 index 0000000..3eaedd4 --- /dev/null +++ b/backend/genesys/tables_memory_layout.cpp @@ -0,0 +1,164 @@ +/*  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); + +    ml = MemoryLayout(); +    ml.models = { ModelId::PLUSTEK_OPTICFILM_7400, ModelId::PLUSTEK_OPTICFILM_8200I }; +    ml.regs = { +        { 0x81, 0x6d }, { 0x82, 0x00 }, { 0x83, 0x00 }, { 0x84, 0x00 }, +        { 0x85, 0x00 }, { 0x86, 0x00 }, +        { 0xd0, 0x0a }, { 0xd1, 0x0a }, { 0xd2, 0x0a }, +        { 0xe0, 0x00 }, { 0xe1, 0x68 }, { 0xe2, 0x03 }, { 0xe3, 0x00 }, +        { 0xe4, 0x03 }, { 0xe5, 0x01 }, { 0xe6, 0x05 }, { 0xe7, 0x99 }, +        { 0xe8, 0x05 }, { 0xe9, 0x9a }, { 0xea, 0x08 }, { 0xeb, 0x32 }, +        { 0xec, 0x08 }, { 0xed, 0x33 }, { 0xee, 0x0a }, { 0xef, 0xcb }, +        { 0xf0, 0x0a }, { 0xf1, 0xcc }, { 0xf2, 0x0d }, { 0xf3, 0x64 }, +        { 0xf4, 0x0d }, { 0xf5, 0x65 }, { 0xf6, 0x0f }, { 0xf7, 0xfd }, +    }; +    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_5600F }; +    ml.regs = { +        { 0xd0, 0x0a }, +        { 0xe0, 0x01 }, { 0xe1, 0x2c }, { 0xe2, 0x06 }, { 0xe3, 0x4e }, +        { 0xe4, 0x06 }, { 0xe5, 0x4f }, { 0xe6, 0x0b }, { 0xe7, 0x71 }, +        { 0xe8, 0x0b }, { 0xe9, 0x72 }, { 0xea, 0x10 }, { 0xeb, 0x94 }, +        { 0xec, 0x10 }, { 0xed, 0x95 }, { 0xee, 0x15 }, { 0xef, 0xb7 }, +        { 0xf0, 0x15 }, { 0xf1, 0xb8 }, { 0xf2, 0x1a }, { 0xf3, 0xda }, +        { 0xf4, 0x1a }, { 0xf5, 0xdb }, { 0xf6, 0x1f }, { 0xf7, 0xfd }, +        { 0xf8, 0x05 } +    }; +    s_memory_layout->push_back(ml); + + +    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(); +    ml.models = { ModelId::CANON_LIDE_200 }; +    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 diff --git a/backend/genesys/tables_model.cpp b/backend/genesys/tables_model.cpp index 0b3a0af..2c5e6a3 100644 --- a/backend/genesys/tables_model.cpp +++ b/backend/genesys/tables_model.cpp @@ -58,10 +58,44 @@  namespace genesys { -StaticInit<std::vector<Genesys_USB_Device_Entry>> s_usb_devices; +StaticInit<std::vector<UsbDeviceEntry>> s_usb_devices;  void genesys_init_usb_device_tables()  { +    /*  Guidelines on calibration area sizes +        ------------------------------------ + +        on many scanners scanning a single line takes aroung 10ms. In order not to take excessive +        amount of time, the sizes of the calibration area are limited as follows: +        2400 dpi or less: 4mm (would take ~4 seconds on 2400 dpi) +        4800 dpi or less: 3mm (would take ~6 seconds on 4800 dpi) +        anything more: 2mm (would take ~7 seconds on 9600 dpi) + +        Optional properties +        ------------------- + +        All fields of the Genesys_Model class are defined even if they use default value, with +        the following exceptions: + +        If the scanner does not have ScanMethod::TRANSPARENCY or ScanMethod::TRANSPARENCY_INFRARED, +        the following properties are optional: + +        model.x_offset_ta = 0.0; +        model.y_offset_ta = 0.0; +        model.x_size_ta = 0.0; +        model.y_size_ta = 0.0; + +        model.y_offset_sensor_to_ta = 0.0; +        model.y_offset_calib_white_ta = 0.0; +        model.y_size_calib_ta_mm = 0.0; + +        If the scanner does not have ModelFlag::DARK_WHITE_CALIBRATION, then the following +        properties are optional: + +        model.y_offset_calib_dark_white_mm = 0.0; +        model.y_size_calib_dark_white_mm = 0.0; +    */ +      s_usb_devices.init();      Genesys_Model model; @@ -87,15 +121,9 @@ void genesys_init_usb_device_tables()      model.y_size = 299.0;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 1.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 228.6;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -112,10 +140,8 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_UMAX;      model.gpio_id = GpioId::UMAX;      model.motor_id = MotorId::UMAX; -    model.flags = GENESYS_FLAG_UNTESTED; +    model.flags = ModelFlag::UNTESTED;      model.buttons = GENESYS_HAS_NO_BUTTONS; -    model.shading_lines = 20; -    model.shading_ta_lines = 0;      model.search_lines = 200;      s_usb_devices->emplace_back(0x0638, 0x0a10, model); @@ -144,17 +170,13 @@ void genesys_init_usb_device_tables()      model.x_size = 218.0;      model.y_size = 299.0; -    model.y_offset_calib_white = 6.0; +    model.y_offset_calib_white = 3.0; +    model.y_size_calib_mm = 3.0; +    model.y_offset_calib_dark_white_mm = 1.0; +    model.y_size_calib_dark_white_mm = 6.0; +    model.x_size_calib_mm = 220.13334;      model.x_offset_calib_black = 0.0; -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; -      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -170,16 +192,12 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::CANON_LIDE_35;      model.gpio_id = GpioId::CANON_LIDE_35;      model.motor_id = MotorId::CANON_LIDE_35; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_DARK_WHITE_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::DARK_WHITE_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW |                      GENESYS_HAS_FILE_SW |                      GENESYS_HAS_EMAIL_SW |                      GENESYS_HAS_COPY_SW; -    model.shading_lines = 280; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x04a9, 0x2213, model); @@ -209,15 +227,9 @@ void genesys_init_usb_device_tables()      model.y_size = 300.0;      model.y_offset_calib_white = 9.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 0.0; -    model.y_size_ta = 0.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 227.584;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -234,12 +246,8 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::KVSS080;      model.gpio_id = GpioId::KVSS080;      model.motor_id = MotorId::KVSS080; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 100;      s_usb_devices->emplace_back(0x04da, 0x100f, model); @@ -269,15 +277,9 @@ void genesys_init_usb_device_tables()      model.y_size = 314.5;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 226.9067;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -294,13 +296,10 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::G4050;      model.gpio_id = GpioId::G4050;      model.motor_id = MotorId::G4050; -    model.flags = GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_SHADING_REPARK | -                  GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::WARMUP | +                  ModelFlag::SHADING_REPARK | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 100;      s_usb_devices->emplace_back(0x03f0, 0x1b05, model); @@ -329,15 +328,9 @@ void genesys_init_usb_device_tables()      model.y_size = 315.0;      model.y_offset_calib_white = 3.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 226.9067;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -353,13 +346,10 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::G4050;      model.gpio_id = GpioId::G4050;      model.motor_id = MotorId::G4050; -    model.flags = GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::WARMUP | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 100;      s_usb_devices->emplace_back(0x03f0, 0x4505, model); @@ -389,15 +379,9 @@ void genesys_init_usb_device_tables()      model.y_size = 315.0;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 8.0; -    model.y_offset_ta = 13.00; -    model.x_size_ta = 217.9; -    model.y_size_ta = 250.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 40.0; +    model.x_size_calib_mm = 226.9067;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -414,13 +398,10 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::G4050;      model.gpio_id = GpioId::G4050;      model.motor_id = MotorId::G4050; -    model.flags = GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::WARMUP | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 100;      s_usb_devices->emplace_back(0x03f0, 0x4605, model); @@ -438,6 +419,10 @@ void genesys_init_usb_device_tables()              { ScanMethod::FLATBED },              { 1200, 600, 300 },              { 1200, 600, 300 }, +        }, { +            { ScanMethod::TRANSPARENCY }, +            { 4800, 2400, 1200 }, +            { 9600, 4800, 2400, 1200 },          }      }; @@ -445,20 +430,23 @@ void genesys_init_usb_device_tables()      model.bpp_color_values = { 8, 16 };      model.x_offset = 6.0; -    model.y_offset = 12.00; +    model.y_offset = 10.00;      model.x_size = 215.9;      model.y_size = 297.0; -    model.y_offset_calib_white = 0.0; +    model.y_offset_calib_white = 2.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; +    model.x_size_calib_mm = 241.3; -    model.x_offset_ta = 8.0; -    model.y_offset_ta = 13.00; -    model.x_size_ta = 217.9; -    model.y_size_ta = 250.0; +    model.x_offset_ta = 115.0; +    model.y_offset_ta = 37.0; +    model.x_size_ta = 35.0; +    model.y_size_ta = 230.0; -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 40.0; +    model.y_offset_sensor_to_ta = 23.0; +    model.y_offset_calib_white_ta = 24.0; +    model.y_size_calib_ta_mm = 2.0;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -475,15 +463,13 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::CANON_4400F;      model.gpio_id = GpioId::CANON_4400F;      model.motor_id = MotorId::CANON_4400F; -    model.flags = GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_FULL_HWDPI_MODE | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_SHADING_REPARK; +    model.flags = ModelFlag::WARMUP | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::SHADING_REPARK | +                  ModelFlag::UTA_NO_SECONDARY_MOTOR; +      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 100;      s_usb_devices->emplace_back(0x04a9, 0x2228, model); @@ -515,13 +501,15 @@ void genesys_init_usb_device_tables()      model.bpp_gray_values = { 8, 16 };      model.bpp_color_values = { 8, 16 }; -    model.x_offset = 3.5; +    model.x_offset = 5.5;      model.y_offset = 17.00;      model.x_size = 219.9;      model.y_size = 300.0;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 3.0;      model.x_offset_calib_black = 10.0; +    model.x_size_calib_mm = 225.425;      model.x_offset_ta = 75.0;      model.y_offset_ta = 45.00; @@ -530,6 +518,7 @@ void genesys_init_usb_device_tables()      model.y_offset_sensor_to_ta = 22.0;      model.y_offset_calib_white_ta = 25.0; +    model.y_size_calib_ta_mm = 3.0;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -546,17 +535,11 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::CANON_8400F;      model.gpio_id = GpioId::CANON_8400F;      model.motor_id = MotorId::CANON_8400F; -    model.flags = GENESYS_FLAG_HAS_UTA | -                  GENESYS_FLAG_HAS_UTA_INFRARED | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_FULL_HWDPI_MODE | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_SHADING_REPARK; +    model.flags = ModelFlag::WARMUP | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::SHADING_REPARK;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW; -    model.shading_lines = 100; -    model.shading_ta_lines = 50;      model.search_lines = 100;      s_usb_devices->emplace_back(0x04a9, 0x221e, model); @@ -590,15 +573,18 @@ void genesys_init_usb_device_tables()      model.y_size = 297.0;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 8.0; +    model.x_size_calib_mm = 240.70734; -    model.x_offset_ta = 85.0; -    model.y_offset_ta = 26.0; +    model.x_offset_ta = 97.0; +    model.y_offset_ta = 38.5;      model.x_size_ta = 70.0;      model.y_size_ta = 230.0; -    model.y_offset_sensor_to_ta = 11.5; -    model.y_offset_calib_white_ta = 14.0; +    model.y_offset_sensor_to_ta = 23.0; +    model.y_offset_calib_white_ta = 25.5; +    model.y_size_calib_ta_mm = 3.0;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -615,17 +601,11 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::CANON_8600F;      model.gpio_id = GpioId::CANON_8600F;      model.motor_id = MotorId::CANON_8600F; -    model.flags = GENESYS_FLAG_HAS_UTA | -                  GENESYS_FLAG_HAS_UTA_INFRARED | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_FULL_HWDPI_MODE | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_SHADING_REPARK; +    model.flags = ModelFlag::WARMUP | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::SHADING_REPARK;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_FILE_SW | GENESYS_HAS_COPY_SW; -    model.shading_lines = 50; -    model.shading_ta_lines = 50;      model.search_lines = 100;      s_usb_devices->emplace_back(0x04a9, 0x2229, model); @@ -654,16 +634,10 @@ void genesys_init_usb_device_tables()      model.x_size = 216.07;      model.y_size = 299.0; -    model.y_offset_calib_white = 1.0; +    model.y_offset_calib_white = 0.4233334; +    model.y_size_calib_mm = 3.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 217.4241;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -680,18 +654,14 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::CANON_LIDE_200;      model.gpio_id = GpioId::CANON_LIDE_200;      model.motor_id = MotorId::CANON_LIDE_100; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_SIS_SENSOR | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_SHADING_REPARK | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::SIS_SENSOR | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::SHADING_REPARK | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW |                      GENESYS_HAS_COPY_SW |                      GENESYS_HAS_EMAIL_SW |                      GENESYS_HAS_FILE_SW; -    model.shading_lines = 50; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x04a9, 0x1904, model); @@ -721,15 +691,9 @@ void genesys_init_usb_device_tables()      model.y_size = 300.0;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 3.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 218.7787;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -745,17 +709,13 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::CANON_LIDE_110;      model.gpio_id = GpioId::CANON_LIDE_110;      model.motor_id = MotorId::CANON_LIDE_110; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_SHADING_REPARK | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::DARK_CALIBRATION | +                  ModelFlag::SHADING_REPARK | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW |                      GENESYS_HAS_COPY_SW |                      GENESYS_HAS_EMAIL_SW |                      GENESYS_HAS_FILE_SW; -    model.shading_lines = 25; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x04a9, 0x1909, model); @@ -785,15 +745,9 @@ void genesys_init_usb_device_tables()      model.y_size = 300.0;      model.y_offset_calib_white = 1.0; +    model.y_size_calib_mm = 3.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 216.0694;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -808,17 +762,13 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::CANON_LIDE_120;      model.gpio_id = GpioId::CANON_LIDE_120;      model.motor_id = MotorId::CANON_LIDE_120; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_SHADING_REPARK | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::DARK_CALIBRATION | +                  ModelFlag::SHADING_REPARK | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW |                      GENESYS_HAS_COPY_SW |                      GENESYS_HAS_EMAIL_SW |                      GENESYS_HAS_FILE_SW; -    model.shading_lines = 50; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x04a9, 0x190e, model); @@ -834,30 +784,23 @@ void genesys_init_usb_device_tables()      model.resolutions = {          {              { ScanMethod::FLATBED }, -            // BUG: 4800 resolution crashes -            { /*4800,*/ 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75 }, -            { /*4800,*/ 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75 }, +            { 4800, 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75 }, +            { 4800, 2400, 1200, 600, /* 400,*/ 300, 150, 100, 75 },          }      };      model.bpp_gray_values = { 8, 16 };      model.bpp_color_values = { 8, 16 }; -    model.x_offset = 2.2; +    model.x_offset = 2.1;      model.y_offset = 8.7;      model.x_size = 216.70;      model.y_size = 297.5;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 3.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 218.7787;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -874,18 +817,14 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::CANON_LIDE_110;      model.gpio_id = GpioId::CANON_LIDE_210;      model.motor_id = MotorId::CANON_LIDE_210; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_SHADING_REPARK | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::DARK_CALIBRATION | +                  ModelFlag::SHADING_REPARK | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW |                      GENESYS_HAS_COPY_SW |                      GENESYS_HAS_EMAIL_SW |                      GENESYS_HAS_FILE_SW |                      GENESYS_HAS_EXTRA_SW; -    model.shading_lines = 60; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x04a9, 0x190a, model); @@ -901,30 +840,23 @@ void genesys_init_usb_device_tables()      model.resolutions = {          {              { ScanMethod::FLATBED }, -            // BUG: 4800 resolution crashes -            { /*4800,*/ 2400, 1200, 600, 300, 150, 100, 75 }, -            { /*4800,*/ 2400, 1200, 600, 300, 150, 100, 75 }, +            { 4800, 2400, 1200, 600, 300, 150, 100, 75 }, +            { 4800, 2400, 1200, 600, 300, 150, 100, 75 },          }      };      model.bpp_gray_values = { 8, 16 };      model.bpp_color_values = { 8, 16 }; -    model.x_offset = 2.2; +    model.x_offset = 2.1;      model.y_offset = 8.7;      model.x_size = 216.70;      model.y_size = 297.5;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 3.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 218.7787;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -940,84 +872,84 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::CANON_LIDE_110;      model.gpio_id = GpioId::CANON_LIDE_210;      model.motor_id = MotorId::CANON_LIDE_210; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_SHADING_REPARK | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::DARK_CALIBRATION | +                  ModelFlag::SHADING_REPARK | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW |                      GENESYS_HAS_COPY_SW |                      GENESYS_HAS_EMAIL_SW |                      GENESYS_HAS_FILE_SW |                      GENESYS_HAS_EXTRA_SW; -    model.shading_lines = 60; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x04a9, 0x190f, model);      model = Genesys_Model(); -    model.name = "canon-5600f"; +    model.name = "canon-canoscan-5600f";      model.vendor = "Canon"; -    model.model = "5600F"; +    model.model = "CanoScan 5600F";      model.model_id = ModelId::CANON_5600F;      model.asic_type = AsicType::GL847;      model.resolutions = {          { -            { ScanMethod::FLATBED }, -            { 4800, 2400, 1200, 600, 400, 300, 200, 150, 100, 75 }, -            { 4800, 2400, 1200, 600, 400, 300, 200, 150, 100, 75 }, +            { ScanMethod::FLATBED, ScanMethod::TRANSPARENCY }, +            { 4800, 2400, 1200, 600, 300, /*150*/ }, +            { 4800, 2400, 1200, 600, 300, /*150*/ },          }      };      model.bpp_gray_values = { 8, 16 };      model.bpp_color_values = { 8, 16 }; -    model.x_offset = 1.1; -    model.y_offset = 8.3; -    model.x_size = 216.07; -    model.y_size = 299.0; +    model.x_offset = 1.5; +    model.y_offset = 10.4; +    model.x_size = 219.00; +    model.y_size = 305.0; -    model.y_offset_calib_white = 3.0; +    model.y_offset_calib_white = 2.0; +    model.y_size_calib_mm = 2.0;      model.x_offset_calib_black = 0.0; +    model.x_size_calib_mm = 220.5; -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; +    model.x_offset_ta = 93.0; +    model.y_offset_ta = 42.4; +    model.x_size_ta = 35.0; +    model.y_size_ta = 230.0; -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.y_offset_sensor_to_ta = 0; +    model.y_offset_calib_white_ta = 21.4; +    model.y_size_calib_ta_mm = 1.0;      model.post_scan = 0.0;      model.eject_feed = 0.0;      model.ld_shift_r = 0; -    model.ld_shift_g = 0; -    model.ld_shift_b = 0; +    model.ld_shift_g = 32; +    model.ld_shift_b = 64;      model.line_mode_color_order = ColorOrder::RGB; -    model.is_cis = true; +    model.is_cis = false;      model.is_sheetfed = false; -    model.sensor_id = SensorId::CIS_CANON_LIDE_200; -    model.adc_id = AdcId::CANON_LIDE_200; -    model.gpio_id = GpioId::CANON_LIDE_200; -    model.motor_id = MotorId::CANON_LIDE_200; -    model.flags = GENESYS_FLAG_UNTESTED | -                  GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_SIS_SENSOR | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.sensor_id = SensorId::CCD_CANON_5600F; +    model.adc_id = AdcId::CANON_5600F; +    model.gpio_id = GpioId::CANON_5600F; +    model.motor_id = MotorId::CANON_5600F; +    model.flags = ModelFlag::SIS_SENSOR | +                  ModelFlag::INVERT_PIXEL_DATA | +                  ModelFlag::DISABLE_ADC_CALIBRATION | +                  ModelFlag::DISABLE_EXPOSURE_CALIBRATION | +                  ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::SHADING_REPARK | +                  ModelFlag::UTA_NO_SECONDARY_MOTOR | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW |                      GENESYS_HAS_COPY_SW |                      GENESYS_HAS_EMAIL_SW |                      GENESYS_HAS_FILE_SW; -    model.shading_lines = 50; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x04a9, 0x1906, model); @@ -1032,9 +964,10 @@ void genesys_init_usb_device_tables()      model.resolutions = {          { +            // FIXME: support 2400 ad 4800 dpi              { ScanMethod::FLATBED }, -            { 4800, 2400, 1200, 600, 300, 200, 150, 100, 75 }, -            { 4800, 2400, 1200, 600, 300, 200, 150, 100, 75 }, +            { 1200, 600, 300, 200, 150, 100, 75 }, +            { 1200, 600, 300, 200, 150, 100, 75 },          }      }; @@ -1046,16 +979,11 @@ void genesys_init_usb_device_tables()      model.x_size = 216.07;      model.y_size = 297.0; -    model.y_offset_calib_white = 1.0; +    model.y_offset_calib_white = 0.4233334; +    model.y_size_calib_mm = 3.0;      model.x_offset_calib_black = 0.0; +    model.x_size_calib_mm = 219.6254; -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -1071,18 +999,14 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::CANON_LIDE_700F;      model.gpio_id = GpioId::CANON_LIDE_700F;      model.motor_id = MotorId::CANON_LIDE_700; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_SIS_SENSOR | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_SHADING_REPARK | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::SIS_SENSOR | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::SHADING_REPARK | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW |                      GENESYS_HAS_COPY_SW |                      GENESYS_HAS_EMAIL_SW |                      GENESYS_HAS_FILE_SW; -    model.shading_lines = 70; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x04a9, 0x1907, model); @@ -1111,16 +1035,10 @@ void genesys_init_usb_device_tables()      model.x_size = 216.07;      model.y_size = 299.0; -    model.y_offset_calib_white = 0.0; +    model.y_offset_calib_white = 0.4233334; +    model.y_size_calib_mm = 3.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 217.4241;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -1136,18 +1054,14 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::CANON_LIDE_200;      model.gpio_id = GpioId::CANON_LIDE_200;      model.motor_id = MotorId::CANON_LIDE_200; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_SIS_SENSOR | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_SHADING_REPARK | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::SIS_SENSOR | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::SHADING_REPARK | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW |                      GENESYS_HAS_COPY_SW |                      GENESYS_HAS_EMAIL_SW |                      GENESYS_HAS_FILE_SW; -    model.shading_lines = 50; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x04a9, 0x1905, model); @@ -1176,16 +1090,12 @@ void genesys_init_usb_device_tables()      model.x_size = 218.0;      model.y_size = 299.0; -    model.y_offset_calib_white = 6.0; +    model.y_offset_calib_white = 3.0; +    model.y_size_calib_mm = 3.0; +    model.y_offset_calib_dark_white_mm = 1.0; +    model.y_size_calib_dark_white_mm = 6.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 220.13334;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -1197,23 +1107,18 @@ void genesys_init_usb_device_tables()      model.is_cis = true;      model.is_sheetfed = false; -    model.sensor_id = SensorId::CIS_CANON_LIDE_35; +    model.sensor_id = SensorId::CIS_CANON_LIDE_60;      model.adc_id = AdcId::CANON_LIDE_35;      model.gpio_id = GpioId::CANON_LIDE_35; -    model.motor_id = MotorId::CANON_LIDE_35; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_DARK_WHITE_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.motor_id = MotorId::CANON_LIDE_60; +    model.flags = ModelFlag::DARK_WHITE_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_COPY_SW |                      GENESYS_HAS_SCAN_SW |                      GENESYS_HAS_FILE_SW |                      GENESYS_HAS_EMAIL_SW; -    model.shading_lines = 300; -    model.shading_ta_lines = 0;      model.search_lines = 400; -    // this is completely untested      s_usb_devices->emplace_back(0x04a9, 0x221c, model); @@ -1240,15 +1145,11 @@ void genesys_init_usb_device_tables()      model.y_size = 299.0;      model.y_offset_calib_white = 4.5; +    model.y_size_calib_mm = 3.0; +    model.y_offset_calib_dark_white_mm = 1.0; +    model.y_size_calib_dark_white_mm = 6.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 216.7467;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -1265,22 +1166,77 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::CANON_LIDE_80;      model.gpio_id = GpioId::CANON_LIDE_80;      model.motor_id = MotorId::CANON_LIDE_80; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_DARK_WHITE_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::DARK_WHITE_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW |                      GENESYS_HAS_FILE_SW |                      GENESYS_HAS_EMAIL_SW |                      GENESYS_HAS_COPY_SW; -    model.shading_lines = 160; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x04a9, 0x2214, model);      model = Genesys_Model(); +    model.name = "canon-lide-90"; +    model.vendor = "Canon"; +    model.model = "LiDE 90"; +    model.model_id = ModelId::CANON_LIDE_90; +    model.asic_type = AsicType::GL842; + +    model.resolutions = { +        { +            { ScanMethod::FLATBED }, +            { 2400, 1200, 600, 300 }, +            { 2400, 1200, 600, 300 }, +        } +    }; + +    model.bpp_gray_values = { 8, 16 }; +    model.bpp_color_values = { 8, 16 }; +    model.x_offset = 3.50; +    model.y_offset = 9.0; +    model.x_size = 219.0; +    model.y_size = 299.0; + +    model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 2.0; +    model.y_offset_calib_dark_white_mm = 0.0; +    model.y_size_calib_dark_white_mm = 0.0; +    model.x_offset_calib_black = 0.0; +    model.x_size_calib_mm = 221.5; + +    model.post_scan = 0.0; +    model.eject_feed = 0.0; + +    model.ld_shift_r = 0; +    model.ld_shift_g = 0; +    model.ld_shift_b = 0; + +    model.line_mode_color_order = ColorOrder::RGB; + +    model.is_cis = true; +    model.is_sheetfed = false; +    model.sensor_id = SensorId::CIS_CANON_LIDE_90; +    model.adc_id = AdcId::CANON_LIDE_90; +    model.gpio_id = GpioId::CANON_LIDE_90; +    model.motor_id = MotorId::CANON_LIDE_90; +    model.flags = ModelFlag::DISABLE_ADC_CALIBRATION | +                  ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN | +                  ModelFlag::USE_CONSTANT_FOR_DARK_CALIBRATION | +                  ModelFlag::DISABLE_FAST_FEEDING | +                  ModelFlag::SHADING_REPARK | +                  ModelFlag::CUSTOM_GAMMA; +    model.buttons = GENESYS_HAS_SCAN_SW | +                    GENESYS_HAS_FILE_SW | +                    GENESYS_HAS_EMAIL_SW | +                    GENESYS_HAS_COPY_SW; +    model.search_lines = 400; + +    s_usb_devices->emplace_back(0x04a9, 0x1900, model); + + +    model = Genesys_Model();      model.name = "hewlett-packard-scanjet-2300c";      model.vendor = "Hewlett Packard";      model.model = "ScanJet 2300c"; @@ -1298,27 +1254,21 @@ void genesys_init_usb_device_tables()      model.bpp_gray_values = { 8, 16 };      model.bpp_color_values = { 8, 16 }; -    model.x_offset = 2.0; -    model.y_offset = 7.5; +    model.x_offset = 6.5; +    model.y_offset = 8;      model.x_size = 215.9;      model.y_size = 295.0;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 1.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 227.2454;      model.post_scan = 0.0;      model.eject_feed = 0.0; -    model.ld_shift_r = 16; -    model.ld_shift_g = 8; +    model.ld_shift_r = 32; +    model.ld_shift_g = 16;      model.ld_shift_b = 0;      model.line_mode_color_order = ColorOrder::RGB; @@ -1328,15 +1278,10 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_HP2300;      model.gpio_id = GpioId::HP2300;      model.motor_id = MotorId::HP2300; -    model.flags = GENESYS_FLAG_14BIT_GAMMA | -                  GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_SEARCH_START | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::GAMMA_14BIT | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_COPY_SW; -    model.shading_lines = 40; -    model.shading_ta_lines = 0;      model.search_lines = 132;      s_usb_devices->emplace_back(0x03f0, 0x0901, model); @@ -1366,15 +1311,9 @@ void genesys_init_usb_device_tables()      model.y_size = 297.2;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 2.0; // FIXME: check if white area is really so small      model.x_offset_calib_black = 1.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 230.1241;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -1391,14 +1330,10 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_HP2400;      model.gpio_id = GpioId::HP2400;      model.motor_id = MotorId::HP2400; -    model.flags = GENESYS_FLAG_14BIT_GAMMA | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::GAMMA_14BIT | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_SCAN_SW; -    model.shading_lines = 20; -    model.shading_ta_lines = 0;      model.search_lines = 132;      s_usb_devices->emplace_back(0x03f0, 0x0a01, model); @@ -1428,15 +1363,9 @@ void genesys_init_usb_device_tables()      model.y_size = 297.2;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 220.1334;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -1453,14 +1382,10 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::AD_XP200;      model.gpio_id = GpioId::XP200;      model.motor_id = MotorId::XP200; -    model.flags = GENESYS_FLAG_14BIT_GAMMA | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_OFFSET_CALIBRATION; +    model.flags = ModelFlag::GAMMA_14BIT | +                  ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DARK_CALIBRATION;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; -    model.shading_lines = 120; -    model.shading_ta_lines = 0;      model.search_lines = 132;      s_usb_devices->emplace_back(0x04a7, 0x0426, model); @@ -1490,15 +1415,9 @@ void genesys_init_usb_device_tables()      model.y_size = 300.0;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 1.0; - -    model.x_offset_ta = 104.0; -    model.y_offset_ta = 55.6; -    model.x_size_ta = 25.6; -    model.y_size_ta = 78.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 76.0; +    model.x_size_calib_mm = 230.1241;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -1515,14 +1434,11 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_HP3670;      model.gpio_id = GpioId::HP3670;      model.motor_id = MotorId::HP3670; -    model.flags = GENESYS_FLAG_14BIT_GAMMA | -                  GENESYS_FLAG_XPA | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::WARMUP | +                  ModelFlag::GAMMA_14BIT | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_COPY_SW | GENESYS_HAS_EMAIL_SW | GENESYS_HAS_SCAN_SW; -    model.shading_lines = 20; -    model.shading_ta_lines = 0;      model.search_lines = 200;      s_usb_devices->emplace_back(0x03f0, 0x1405, model); @@ -1552,15 +1468,9 @@ void genesys_init_usb_device_tables()      model.y_size = 299.0;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 1.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 229.2774;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -1577,10 +1487,8 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_ST12;      model.gpio_id = GpioId::ST12;      model.motor_id = MotorId::UMAX; -    model.flags = GENESYS_FLAG_UNTESTED | GENESYS_FLAG_14BIT_GAMMA; +    model.flags = ModelFlag::UNTESTED | ModelFlag::GAMMA_14BIT;      model.buttons = GENESYS_HAS_NO_BUTTONS; -    model.shading_lines = 20; -    model.shading_ta_lines = 0;      model.search_lines = 200;      s_usb_devices->emplace_back(0x07b3, 0x0600, model); @@ -1604,20 +1512,14 @@ void genesys_init_usb_device_tables()      model.bpp_color_values = { 8, 16 };      model.x_offset = 3.5; -    model.y_offset = 7.5; +    model.y_offset = 7.5; // FIXME: incorrect, needs updating      model.x_size = 218.0;      model.y_size = 299.0;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 1.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 228.6;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -1634,14 +1536,10 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_ST24;      model.gpio_id = GpioId::ST24;      model.motor_id = MotorId::ST24; -    model.flags = GENESYS_FLAG_UNTESTED | -                  GENESYS_FLAG_14BIT_GAMMA | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_SEARCH_START | -                  GENESYS_FLAG_OFFSET_CALIBRATION; +    model.flags = ModelFlag::UNTESTED | +                  ModelFlag::GAMMA_14BIT | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_NO_BUTTONS; -    model.shading_lines = 20; -    model.shading_ta_lines = 0;      model.search_lines = 200;      s_usb_devices->emplace_back(0x07b3, 0x0601, model); @@ -1665,26 +1563,20 @@ void genesys_init_usb_device_tables()      model.bpp_color_values = { 8, 16 };      model.x_offset = 0.30; -    model.y_offset = 0.80; +    model.y_offset = 4.0; // FIXME: incorrect, needs updating      model.x_size = 220.0;      model.y_size = 296.4;      model.y_offset_calib_white = 0.00; +    model.y_size_calib_mm = 2.0;      model.x_offset_calib_black = 0.00; - -    model.x_offset_ta = 0.00; -    model.y_offset_ta = 0.00; -    model.x_size_ta = 0.00; -    model.y_size_ta = 0.00; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.00; +    model.x_size_calib_mm = 230.1241;      model.post_scan = 0.0;      model.eject_feed = 0.0; -    model.ld_shift_r = 48; -    model.ld_shift_g = 24; +    model.ld_shift_r = 96; +    model.ld_shift_g = 48;      model.ld_shift_b = 0;      model.line_mode_color_order = ColorOrder::RGB; @@ -1694,19 +1586,15 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_5345;      model.gpio_id = GpioId::MD_5345;      model.motor_id = MotorId::MD_5345; -    model.flags = GENESYS_FLAG_14BIT_GAMMA | -                  GENESYS_FLAG_SEARCH_START | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_SHADING_NO_MOVE | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::WARMUP | +                  ModelFlag::GAMMA_14BIT | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_COPY_SW |                      GENESYS_HAS_EMAIL_SW |                      GENESYS_HAS_POWER_SW |                      GENESYS_HAS_OCR_SW |                      GENESYS_HAS_SCAN_SW; -    model.shading_lines = 40; -    model.shading_ta_lines = 0;      model.search_lines = 200;      s_usb_devices->emplace_back(0x0461, 0x0377, model); @@ -1735,15 +1623,9 @@ void genesys_init_usb_device_tables()      model.y_size = 511;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 433.4934;      model.post_scan = 26.5;      // this is larger than needed -- accounts for second sensor head, which is a calibration item @@ -1760,13 +1642,9 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_XP300;      model.gpio_id = GpioId::XP300;      model.motor_id = MotorId::XP300; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::DARK_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x04a7, 0x0474, model); @@ -1795,15 +1673,9 @@ void genesys_init_usb_device_tables()      model.y_size = 511;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 105.664;      model.post_scan = 17.5;      model.eject_feed = 0.0; @@ -1820,13 +1692,9 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_XP300;      model.gpio_id = GpioId::DP665;      model.motor_id = MotorId::DP665; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::DARK_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x0a82, 0x4803, model); @@ -1855,15 +1723,9 @@ void genesys_init_usb_device_tables()      model.y_size = 511;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 220.1334;      model.post_scan = 16.0;      model.eject_feed = 0.0; @@ -1880,13 +1742,9 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_XP300;      model.gpio_id = GpioId::DP665;      model.motor_id = MotorId::ROADWARRIOR; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_DARK_CALIBRATION; +    model.flags = ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DARK_CALIBRATION;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x04a7, 0x0494, model); @@ -1915,15 +1773,9 @@ void genesys_init_usb_device_tables()      model.y_size = 511;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 220.1334;      model.post_scan = 16.0;      model.eject_feed = 0.0; @@ -1940,13 +1792,12 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_XP300;      model.gpio_id = GpioId::DP665;      model.motor_id = MotorId::ROADWARRIOR; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_NO_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_UNTESTED; +    model.flags = ModelFlag::DISABLE_ADC_CALIBRATION | +                  ModelFlag::DISABLE_EXPOSURE_CALIBRATION | +                  ModelFlag::DISABLE_SHADING_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::UNTESTED;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW; -    model.shading_lines = 300; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x0a82, 0x4802, model); @@ -1976,15 +1827,9 @@ void genesys_init_usb_device_tables()      model.y_size = 511;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 220.1334;      model.post_scan = 16.0;      model.eject_feed = 0.0; @@ -2001,13 +1846,9 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_XP300;      model.gpio_id = GpioId::DP665;      model.motor_id = MotorId::ROADWARRIOR; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_DARK_CALIBRATION; +    model.flags = ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DARK_CALIBRATION;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x04a7, 0x049b, model); @@ -2036,15 +1877,9 @@ void genesys_init_usb_device_tables()      model.y_size = 511;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 220.1334;      model.post_scan = 16.0;      model.eject_feed = 0.0; @@ -2061,13 +1896,9 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_DSM600;      model.gpio_id = GpioId::DP665;      model.motor_id = MotorId::DSMOBILE_600; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_DARK_CALIBRATION; +    model.flags = ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DARK_CALIBRATION;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x0a17, 0x3210, model); @@ -2098,15 +1929,9 @@ void genesys_init_usb_device_tables()      model.y_size = 511;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 220.1334;      model.post_scan = 16.0;      model.eject_feed = 0.0; @@ -2122,13 +1947,9 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_DSM600;      model.gpio_id = GpioId::DP665;      model.motor_id = MotorId::DSMOBILE_600; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_DARK_CALIBRATION; +    model.flags = ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DARK_CALIBRATION;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x1dcc, 0x4812, model); @@ -2157,15 +1978,9 @@ void genesys_init_usb_device_tables()      model.y_size = 500;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 212.5134;      model.post_scan = 26.5;      // this is larger than needed -- accounts for second sensor head, which is a calibration item @@ -2182,13 +1997,9 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_DSM600;      model.gpio_id = GpioId::DP685;      model.motor_id = MotorId::XP300; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_DARK_CALIBRATION; +    model.flags = ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DARK_CALIBRATION;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 400; @@ -2219,15 +2030,9 @@ void genesys_init_usb_device_tables()      model.y_size = 511;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 433.4934;      model.post_scan = 26.5;      // this is larger than needed -- accounts for second sensor head, which is a calibration item @@ -2244,13 +2049,9 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_XP300;      model.gpio_id = GpioId::XP300;      model.motor_id = MotorId::XP300; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_DARK_CALIBRATION; +    model.flags = ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DARK_CALIBRATION;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x0a82, 0x4800, model); @@ -2280,19 +2081,14 @@ void genesys_init_usb_device_tables()      model.y_size = 511;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 433.4934;      model.post_scan = 26.5;      // this is larger than needed -- accounts for second sensor head, which is a calibration item      model.eject_feed = 0.0; +      model.ld_shift_r = 0;      model.ld_shift_g = 0;      model.ld_shift_b = 0; @@ -2301,18 +2097,14 @@ void genesys_init_usb_device_tables()      model.is_cis = true;      model.is_sheetfed = true; -    model.sensor_id = SensorId::CCD_XP300; +    model.sensor_id = SensorId::CCD_DOCKETPORT_487;      model.adc_id = AdcId::WOLFSON_XP300;      model.gpio_id = GpioId::XP300;      model.motor_id = MotorId::XP300; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_UNTESTED; +    model.flags = ModelFlag::DARK_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::UNTESTED;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x1dcc, 0x4810, model); @@ -2337,26 +2129,20 @@ void genesys_init_usb_device_tables()      model.bpp_color_values = { 8, 16 };      model.x_offset = 4.00; -    model.y_offset = 0.80; +    model.y_offset = 5.0; // FIXME: incorrect, needs updating      model.x_size = 215.9;      model.y_size = 296.4;      model.y_offset_calib_white = 0.00; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.00; - -    model.x_offset_ta = 0.00; -    model.y_offset_ta = 0.00; -    model.x_size_ta = 0.00; -    model.y_size_ta = 0.00; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.00; +    model.x_size_calib_mm = 230.1241;      model.post_scan = 0.0;      model.eject_feed = 0.0; -    model.ld_shift_r = 48; -    model.ld_shift_g = 24; +    model.ld_shift_r = 96; +    model.ld_shift_g = 48;      model.ld_shift_b = 0;      model.line_mode_color_order = ColorOrder::RGB; @@ -2366,18 +2152,15 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_5345;      model.gpio_id = GpioId::MD_5345;      model.motor_id = MotorId::MD_5345; -    model.flags = GENESYS_FLAG_14BIT_GAMMA | -                  GENESYS_FLAG_SEARCH_START | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::WARMUP | +                  ModelFlag::GAMMA_14BIT | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_COPY_SW |                      GENESYS_HAS_EMAIL_SW |                      GENESYS_HAS_POWER_SW |                      GENESYS_HAS_OCR_SW |                      GENESYS_HAS_SCAN_SW; -    model.shading_lines = 40; -    model.shading_ta_lines = 0;      model.search_lines = 200;      s_usb_devices->emplace_back(0x04a7, 0x0229, model); @@ -2402,26 +2185,20 @@ void genesys_init_usb_device_tables()      model.bpp_color_values = { 8, 16 };      model.x_offset = 4.00; -    model.y_offset = 0.80; +    model.y_offset = 5.0; // FIXME: incorrect, needs updating      model.x_size = 215.9;      model.y_size = 296.4;      model.y_offset_calib_white = 0.00; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.00; - -    model.x_offset_ta = 0.00; -    model.y_offset_ta = 0.00; -    model.x_size_ta = 0.00; -    model.y_size_ta = 0.00; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.00; +    model.x_size_calib_mm = 230.1241;      model.post_scan = 0.0;      model.eject_feed = 0.0; -    model.ld_shift_r = 48; -    model.ld_shift_g = 24; +    model.ld_shift_r = 96; +    model.ld_shift_g = 48;      model.ld_shift_b = 0;      model.line_mode_color_order = ColorOrder::RGB; @@ -2431,18 +2208,15 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_5345;      model.gpio_id = GpioId::MD_5345;      model.motor_id = MotorId::MD_5345; -    model.flags = GENESYS_FLAG_14BIT_GAMMA | -                  GENESYS_FLAG_SEARCH_START | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::WARMUP | +                  ModelFlag::GAMMA_14BIT | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_COPY_SW |                      GENESYS_HAS_EMAIL_SW |                      GENESYS_HAS_POWER_SW |                      GENESYS_HAS_OCR_SW |                      GENESYS_HAS_SCAN_SW; -    model.shading_lines = 40; -    model.shading_ta_lines = 0;      model.search_lines = 200;      s_usb_devices->emplace_back(0x0461, 0x038b, model); @@ -2472,15 +2246,9 @@ void genesys_init_usb_device_tables()      model.y_size = 511;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 220.1334;      model.post_scan = 16.0;      model.eject_feed = 0.0; @@ -2497,13 +2265,9 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::WOLFSON_XP300;      model.gpio_id = GpioId::DP665;      model.motor_id = MotorId::ROADWARRIOR; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_DARK_CALIBRATION; +    model.flags = ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DARK_CALIBRATION;      model.buttons = GENESYS_HAS_SCAN_SW | GENESYS_HAS_PAGE_LOADED_SW | GENESYS_HAS_CALIBRATE; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 400;      s_usb_devices->emplace_back(0x04a7, 0x04ac, model); @@ -2533,15 +2297,9 @@ void genesys_init_usb_device_tables()      model.y_size = 297.0;      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 0.0; -    model.y_size_ta = 0.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 213.7834;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -2558,19 +2316,81 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::PLUSTEK_OPTICPRO_3600;      model.gpio_id = GpioId::PLUSTEK_OPTICPRO_3600;      model.motor_id = MotorId::PLUSTEK_OPTICPRO_3600; -    model.flags = GENESYS_FLAG_UNTESTED |                // not fully working yet -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_OFFSET_CALIBRATION; +    model.flags = ModelFlag::UNTESTED |                // not fully working yet +                  ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DARK_CALIBRATION;      model.buttons = GENESYS_HAS_NO_BUTTONS; -    model.shading_lines = 7; -    model.shading_ta_lines = 0;      model.search_lines = 200;      s_usb_devices->emplace_back(0x07b3, 0x0900, model); + +    model = Genesys_Model(); +    model.name = "plustek-opticfilm-7200"; +    model.vendor = "PLUSTEK"; +    model.model = "OpticFilm 7200"; +    model.model_id = ModelId::PLUSTEK_OPTICFILM_7200; +    model.asic_type = AsicType::GL842; + +    model.resolutions = { +        { +            { ScanMethod::TRANSPARENCY }, +            { 7200, 3600, 1800, 900 }, +            { 7200, 3600, 1800, 900 }, +        } +    }; + +    model.bpp_gray_values = { 16 }; +    model.bpp_color_values = { 16 }; +    model.default_method = ScanMethod::TRANSPARENCY; + +    model.x_offset = 0.0; +    model.y_offset = 0.0; +    model.x_size = 36.0; +    model.y_size = 44.0; + +    model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 0.0; +    model.x_offset_calib_black = 6.5; +    model.x_size_calib_mm = 35.9834; + +    model.x_offset_ta = 0.7f; +    model.y_offset_ta = 28.0; +    model.x_size_ta = 36.0; +    model.y_size_ta = 25.0; + +    model.y_offset_sensor_to_ta = 0.0; +    model.y_offset_calib_black_ta = 6.5; +    model.y_offset_calib_white_ta = 0.0; +    model.y_size_calib_ta_mm = 2.0; + +    model.post_scan = 0.0; +    model.eject_feed = 0.0; + +    model.ld_shift_r = 0; +    model.ld_shift_g = 12; +    model.ld_shift_b = 24; + +    model.line_mode_color_order = ColorOrder::RGB; + +    model.is_cis = false; +    model.is_sheetfed = false; + +    model.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7200; +    model.adc_id = AdcId::PLUSTEK_OPTICFILM_7200; +    model.gpio_id = GpioId::PLUSTEK_OPTICFILM_7200; +    model.motor_id = MotorId::PLUSTEK_OPTICFILM_7200; + +    model.flags = ModelFlag::WARMUP | +                  ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::SHADING_REPARK; + +    model.search_lines = 200; +    s_usb_devices->emplace_back(0x07b3, 0x0807, model); + +      model = Genesys_Model();      model.name = "plustek-opticfilm-7200i";      model.vendor = "PLUSTEK"; @@ -2594,16 +2414,22 @@ void genesys_init_usb_device_tables()      model.y_offset = 0.0;      model.x_size = 36.0;      model.y_size = 44.0; +      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 0.0;      model.x_offset_calib_black = 6.5; +    model.x_size_calib_mm = 35.9834;      model.x_offset_ta = 0.0;      model.y_offset_ta = 29.0;      model.x_size_ta = 36.0;      model.y_size_ta = 24.0; +      model.y_offset_sensor_to_ta = 0.0;      model.y_offset_calib_black_ta = 6.5;      model.y_offset_calib_white_ta = 0.0; +    model.y_size_calib_ta_mm = 2.0; +      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -2621,23 +2447,29 @@ void genesys_init_usb_device_tables()      model.gpio_id = GpioId::PLUSTEK_OPTICFILM_7200I;      model.motor_id = MotorId::PLUSTEK_OPTICFILM_7200I; -    model.flags = GENESYS_FLAG_HAS_UTA | -                  GENESYS_FLAG_HAS_UTA_INFRARED | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_HAS_NO_BUTTONS | -                  GENESYS_FLAG_SHADING_REPARK | -                  GENESYS_FLAG_CALIBRATION_HOST_SIDE | -                  GENESYS_FLAG_16BIT_DATA_INVERTED; - -    model.shading_lines = 7; -    model.shading_ta_lines = 50; +    model.flags = ModelFlag::WARMUP | +                  ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::SHADING_REPARK | +                  ModelFlag::SWAP_16BIT_DATA; +      model.search_lines = 200;      s_usb_devices->emplace_back(0x07b3, 0x0c04, model); +    // same as 7200i, just without the infrared channel +    model.name = "plustek-opticfilm-7200-v2"; +    model.model = "OpticFilm 7200 v2"; +    model.resolutions = { +        { +            { ScanMethod::TRANSPARENCY }, +            { 7200, 3600, 1800, 900 }, +            { 7200, 3600, 1800, 900 }, +        } +    }; +    s_usb_devices->emplace_back(0x07b3, 0x0c07, model); + +      model = Genesys_Model();      model.name = "plustek-opticfilm-7300";      model.vendor = "PLUSTEK"; @@ -2661,16 +2493,22 @@ void genesys_init_usb_device_tables()      model.y_offset = 0.0;      model.x_size = 36.0;      model.y_size = 44.0; +      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 0.0;      model.x_offset_calib_black = 6.5; +    model.x_size_calib_mm = 35.9834;      model.x_offset_ta = 0.0;      model.y_offset_ta = 29.0;      model.x_size_ta = 36.0;      model.y_size_ta = 24.0; +      model.y_offset_sensor_to_ta = 0.0;      model.y_offset_calib_black_ta = 6.5;      model.y_offset_calib_white_ta = 0.0; +    model.y_size_calib_ta_mm = 2.0; +      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -2688,21 +2526,91 @@ void genesys_init_usb_device_tables()      model.gpio_id = GpioId::PLUSTEK_OPTICFILM_7300;      model.motor_id = MotorId::PLUSTEK_OPTICFILM_7300; -    model.flags = GENESYS_FLAG_HAS_UTA | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_HAS_NO_BUTTONS | -                  GENESYS_FLAG_SHADING_REPARK | -                  GENESYS_FLAG_CALIBRATION_HOST_SIDE; - -    model.shading_lines = 7; -    model.shading_ta_lines = 50; +    model.flags = ModelFlag::WARMUP | +                  ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::SHADING_REPARK; +      model.search_lines = 200;      s_usb_devices->emplace_back(0x07b3, 0x0c12, model); +    // same as 7300, same USB ID as 7400-v2 +    model.name = "plustek-opticfilm-7400-v1"; +    model.model = "OpticFilm 7400 (v1)"; +    s_usb_devices->emplace_back(0x07b3, 0x0c3a, 0x0400, model); + + +    model = Genesys_Model(); +    model.name = "plustek-opticfilm-7400-v2"; +    model.vendor = "PLUSTEK"; +    model.model = "OpticFilm 7400 (v2)"; +    model.model_id = ModelId::PLUSTEK_OPTICFILM_7400; +    model.asic_type = AsicType::GL845; + +    model.resolutions = { +        { +            { ScanMethod::TRANSPARENCY }, +            { 7200, 3600, 2400, 1200, 600 }, +            { 7200, 3600, 2400, 1200, 600 }, +        } +    }; + +    model.bpp_gray_values = { 16 }; +    model.bpp_color_values = { 16 }; +    model.default_method = ScanMethod::TRANSPARENCY; + +    model.x_offset = 0.0; +    model.y_offset = 0.0; +    model.x_size = 36.0; +    model.y_size = 44.0; + +    model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 0.0; +    model.x_offset_calib_black = 6.5; +    model.x_size_calib_mm = 36.83; + +    model.x_offset_ta = 0.5; +    model.y_offset_ta = 29.0; +    model.x_size_ta = 36.33; +    model.y_size_ta = 25.0; + +    model.y_offset_sensor_to_ta = 0.0; +    model.y_offset_calib_black_ta = 6.5; +    model.y_offset_calib_white_ta = 0.0; +    model.y_size_calib_ta_mm = 2.0; + +    model.post_scan = 0.0; +    model.eject_feed = 0.0; + +    model.ld_shift_r = 0; +    model.ld_shift_g = 12; +    model.ld_shift_b = 24; + +    model.line_mode_color_order = ColorOrder::RGB; + +    model.is_cis = false; +    model.is_sheetfed = false; + +    model.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7400; +    model.adc_id = AdcId::PLUSTEK_OPTICFILM_7400; +    model.gpio_id = GpioId::PLUSTEK_OPTICFILM_7400; +    model.motor_id = MotorId::PLUSTEK_OPTICFILM_7400; + +    model.flags = ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::SHADING_REPARK; + +    model.search_lines = 200; +    s_usb_devices->emplace_back(0x07b3, 0x0c3a, 0x0605, model); + + +    // same as 7400-v2 +    model.name = "plustek-opticfilm-8100"; +    model.model = "OpticFilm 8100"; +    s_usb_devices->emplace_back(0x07b3, 0x130c, model); + +      model = Genesys_Model();      model.name = "plustek-opticfilm-7500i";      model.vendor = "PLUSTEK"; @@ -2726,16 +2634,22 @@ void genesys_init_usb_device_tables()      model.y_offset = 0.0;      model.x_size = 36.0;      model.y_size = 44.0; +      model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 0.0;      model.x_offset_calib_black = 6.5;      model.x_offset_ta = 0.0;      model.y_offset_ta = 29.0;      model.x_size_ta = 36.0;      model.y_size_ta = 24.0; +    model.x_size_calib_mm = 35.9834; +      model.y_offset_sensor_to_ta = 0.0;      model.y_offset_calib_black_ta = 6.5;      model.y_offset_calib_white_ta = 0.0; +    model.y_size_calib_ta_mm = 2.0; +      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -2753,22 +2667,91 @@ void genesys_init_usb_device_tables()      model.gpio_id = GpioId::PLUSTEK_OPTICFILM_7500I;      model.motor_id = MotorId::PLUSTEK_OPTICFILM_7500I; -    model.flags = GENESYS_FLAG_HAS_UTA | -                  GENESYS_FLAG_HAS_UTA_INFRARED | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_HAS_NO_BUTTONS | -                  GENESYS_FLAG_SHADING_REPARK | -                  GENESYS_FLAG_CALIBRATION_HOST_SIDE; - -    model.shading_lines = 7; -    model.shading_ta_lines = 50; +    model.flags = ModelFlag::WARMUP | +                  ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::SHADING_REPARK; +      model.search_lines = 200;      s_usb_devices->emplace_back(0x07b3, 0x0c13, model); +    // same as 7500i +    model.name = "plustek-opticfilm-7600i-v1"; +    model.model = "OpticFilm 7600i (v1)"; +    s_usb_devices->emplace_back(0x07b3, 0x0c3b, 0x0400, model); + + +    model = Genesys_Model(); +    model.name = "plustek-opticfilm-8200i"; +    model.vendor = "PLUSTEK"; +    model.model = "OpticFilm 8200i"; +    model.model_id = ModelId::PLUSTEK_OPTICFILM_8200I; +    model.asic_type = AsicType::GL845; + +    model.resolutions = { +        { +            { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }, +            { 7200, 3600, 1800, 900 }, +            { 7200, 3600, 1800, 900 }, +        } +    }; + +    model.bpp_gray_values = { 16 }; +    model.bpp_color_values = { 16 }; +    model.default_method = ScanMethod::TRANSPARENCY; + +    model.x_offset = 0.0; +    model.y_offset = 0.0; +    model.x_size = 36.0; +    model.y_size = 44.0; + +    model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 0.0; +    model.x_offset_calib_black = 6.5; +    model.x_size_calib_mm = 36.83; + +    model.x_offset_ta = 0.5; +    model.y_offset_ta = 28.5; +    model.x_size_ta = 36.33; +    model.y_size_ta = 25.0; + +    model.y_offset_sensor_to_ta = 0.0; +    model.y_offset_calib_black_ta = 6.5; +    model.y_offset_calib_white_ta = 0.0; +    model.y_size_calib_ta_mm = 2.0; + +    model.post_scan = 0.0; +    model.eject_feed = 0.0; + +    model.ld_shift_r = 0; +    model.ld_shift_g = 12; +    model.ld_shift_b = 24; + +    model.line_mode_color_order = ColorOrder::RGB; + +    model.is_cis = false; +    model.is_sheetfed = false; + +    model.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_8200I; +    model.adc_id = AdcId::PLUSTEK_OPTICFILM_8200I; +    model.gpio_id = GpioId::PLUSTEK_OPTICFILM_8200I; +    model.motor_id = MotorId::PLUSTEK_OPTICFILM_8200I; + +    model.flags = ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::SHADING_REPARK; + +    model.search_lines = 200; +    s_usb_devices->emplace_back(0x07b3, 0x130d, model); + + +    // same as 8200i +    model.name = "plustek-opticfilm-7600i-v2"; +    model.model = "OpticFilm 7600i (v2)"; +    s_usb_devices->emplace_back(0x07b3, 0x0c3b, 0x0605, model); + +      model = Genesys_Model();      model.name = "hewlett-packard-scanjet-N6310";      model.vendor = "Hewlett Packard"; @@ -2792,16 +2775,10 @@ void genesys_init_usb_device_tables()      model.x_size = 216;      model.y_size = 511; -    model.y_offset_calib_white = 3.0; +    model.y_offset_calib_white = 0.0; +    model.y_size_calib_mm = 4.0; // FIXME: y_offset is liely incorrect      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 100.0; -    model.y_size_ta = 100.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0; +    model.x_size_calib_mm = 452.12;      model.post_scan = 0;      model.eject_feed = 0; @@ -2818,17 +2795,15 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::CANON_LIDE_200;        // Not defined yet for N6310      model.gpio_id = GpioId::HP_N6310;      model.motor_id = MotorId::CANON_LIDE_200;    // Not defined yet for N6310 -    model.flags = GENESYS_FLAG_UNTESTED | -                  GENESYS_FLAG_14BIT_GAMMA | -                  GENESYS_FLAG_DARK_CALIBRATION | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_NO_CALIBRATION; +    model.flags = ModelFlag::UNTESTED | +                  ModelFlag::GAMMA_14BIT | +                  ModelFlag::DARK_CALIBRATION | +                  ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::DISABLE_ADC_CALIBRATION | +                  ModelFlag::DISABLE_EXPOSURE_CALIBRATION | +                  ModelFlag::DISABLE_SHADING_CALIBRATION;      model.buttons = GENESYS_HAS_NO_BUTTONS; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 100;      s_usb_devices->emplace_back(0x03f0, 0x4705, model); @@ -2858,15 +2833,9 @@ void genesys_init_usb_device_tables()      model.y_size = 300.0;      model.y_offset_calib_white = 9.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 0.0; -    model.y_size_ta = 0.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 215.9;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -2883,12 +2852,8 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::PLUSTEK_OPTICBOOK_3800;      model.gpio_id = GpioId::PLUSTEK_OPTICBOOK_3800;      model.motor_id = MotorId::PLUSTEK_OPTICBOOK_3800; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA; +    model.flags = ModelFlag::CUSTOM_GAMMA;      model.buttons = GENESYS_HAS_NO_BUTTONS;  // TODO there are 4 buttons to support -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 100;      s_usb_devices->emplace_back(0x07b3, 0x1300, model); @@ -2918,15 +2883,9 @@ void genesys_init_usb_device_tables()      model.y_size = 300.0;      model.y_offset_calib_white = 9.0; +    model.y_size_calib_mm = 4.0;      model.x_offset_calib_black = 0.0; - -    model.x_offset_ta = 0.0; -    model.y_offset_ta = 0.0; -    model.x_size_ta = 0.0; -    model.y_size_ta = 0.0; - -    model.y_offset_sensor_to_ta = 0.0; -    model.y_offset_calib_white_ta = 0.0; +    model.x_size_calib_mm = 228.6;      model.post_scan = 0.0;      model.eject_feed = 0.0; @@ -2943,16 +2902,36 @@ void genesys_init_usb_device_tables()      model.adc_id = AdcId::IMG101;      model.gpio_id = GpioId::IMG101;      model.motor_id = MotorId::IMG101; -    model.flags = GENESYS_FLAG_SKIP_WARMUP | -                  GENESYS_FLAG_OFFSET_CALIBRATION | -                  GENESYS_FLAG_CUSTOM_GAMMA | -                  GENESYS_FLAG_UNTESTED; +    model.flags = ModelFlag::CUSTOM_GAMMA | +                  ModelFlag::UNTESTED;      model.buttons = GENESYS_HAS_NO_BUTTONS ; -    model.shading_lines = 100; -    model.shading_ta_lines = 0;      model.search_lines = 100;      s_usb_devices->emplace_back(0x1083, 0x162e, model); - } +} + +void verify_usb_device_tables() +{ +    for (const auto& device : *s_usb_devices) { +        const auto& model = device.model(); + +        if (model.x_size_calib_mm == 0.0f) { +            throw SaneException("Calibration width can't be zero"); +        } + +        if (model.has_method(ScanMethod::FLATBED)) { +            if (model.y_size_calib_mm == 0.0f) { +                throw SaneException("Calibration size can't be zero"); +            } +        } +        if (model.has_method(ScanMethod::TRANSPARENCY) || +            model.has_method(ScanMethod::TRANSPARENCY_INFRARED)) +        { +            if (model.y_size_calib_ta_mm == 0.0f) { +                throw SaneException("Calibration size can't be zero"); +            } +        } +    } +}  } // namespace genesys diff --git a/backend/genesys/tables_motor.cpp b/backend/genesys/tables_motor.cpp index 2484d2d..a452fe5 100644 --- a/backend/genesys/tables_motor.cpp +++ b/backend/genesys/tables_motor.cpp @@ -53,272 +53,586 @@ void genesys_init_motor_tables()  {      s_motors.init(); +    MotorProfile profile; +      Genesys_Motor motor;      motor.id = MotorId::UMAX; -    motor.base_ydpi = 1200; -    motor.optical_ydpi = 2400; -    motor.slopes.push_back(MotorSlope::create_from_steps(11000, 3000, 128)); -    motor.slopes.push_back(MotorSlope::create_from_steps(11000, 3000, 128)); +    motor.base_ydpi = 2400; +    motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::FULL, 0}); +    motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::HALF, 0});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::MD_5345; // MD5345/6228/6471 -    motor.base_ydpi = 1200; -    motor.optical_ydpi = 2400; -    motor.slopes.push_back(MotorSlope::create_from_steps(2000, 1375, 128)); -    motor.slopes.push_back(MotorSlope::create_from_steps(2000, 1375, 128)); +    motor.base_ydpi = 2400; +    motor.profiles.push_back({MotorSlope::create_from_steps(2000, 1375, 128), StepType::FULL, 0}); +    motor.profiles.push_back({MotorSlope::create_from_steps(2000, 1375, 128), StepType::HALF, 0});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::ST24;      motor.base_ydpi = 2400; -    motor.optical_ydpi = 2400; -    motor.slopes.push_back(MotorSlope::create_from_steps(2289, 2100, 128)); -    motor.slopes.push_back(MotorSlope::create_from_steps(2289, 2100, 128)); +    motor.profiles.push_back({MotorSlope::create_from_steps(2289, 2100, 128), StepType::FULL, 0}); +    motor.profiles.push_back({MotorSlope::create_from_steps(2289, 2100, 128), StepType::HALF, 0});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::HP3670;      motor.base_ydpi = 1200; -    motor.optical_ydpi = 1200; -    motor.slopes.push_back(MotorSlope::create_from_steps(11000, 3000, 128)); -    motor.slopes.push_back(MotorSlope::create_from_steps(11000, 3000, 128)); +    motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::FULL, 0}); +    motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::HALF, 0});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::HP2400;      motor.base_ydpi = 1200; -    motor.optical_ydpi = 1200; -    motor.slopes.push_back(MotorSlope::create_from_steps(11000, 3000, 128)); -    motor.slopes.push_back(MotorSlope::create_from_steps(11000, 3000, 128)); +    motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::FULL, 0}); +    motor.profiles.push_back({MotorSlope::create_from_steps(11000, 3000, 128), StepType::HALF, 0});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::HP2300; -    motor.base_ydpi = 600; -    motor.optical_ydpi = 1200; -    motor.slopes.push_back(MotorSlope::create_from_steps(3200, 1200, 128)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3200, 1200, 128)); +    motor.base_ydpi = 1200; +    motor.profiles.push_back({MotorSlope::create_from_steps(3200, 1200, 128), StepType::FULL, 0}); +    motor.profiles.push_back({MotorSlope::create_from_steps(3200, 1200, 128), StepType::HALF, 0});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::CANON_LIDE_35;      motor.base_ydpi = 1200; -    motor.optical_ydpi = 2400; -    motor.slopes.push_back(MotorSlope::create_from_steps(3500, 1300, 60)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3500, 1400, 60)); + +    profile = MotorProfile{MotorSlope::create_from_steps(3500, 1300, 150), StepType::HALF, 0}; +    profile.resolutions = { 75, 150, 200, 300, 600 }; +    motor.profiles.push_back(profile); + +    profile = MotorProfile{MotorSlope::create_from_steps(3500, 1300, 150), StepType::QUARTER, 0}; +    profile.resolutions = { 1200, 2400 }; +    motor.profiles.push_back(profile); + +    profile = MotorProfile{MotorSlope::create_from_steps(3500, 1400, 150), StepType::FULL, 0}; +    profile.resolutions = { 75, 150, 200, 300 }; +    motor.fast_profiles.push_back(profile); + +    profile = MotorProfile{MotorSlope::create_from_steps(6000, 3000, 100), StepType::FULL, 0}; +    profile.resolutions = { 600, 1200, 2400 }; +    motor.fast_profiles.push_back(profile); + +    s_motors->push_back(std::move(motor)); + + +    motor = Genesys_Motor(); +    motor.id = MotorId::CANON_LIDE_60; +    motor.base_ydpi = 1200; + +    profile = MotorProfile{MotorSlope::create_from_steps(3500, 1400, 150), StepType::HALF, 0}; +    motor.profiles.push_back(profile); + +    profile = MotorProfile{MotorSlope::create_from_steps(3500, 1400, 150), StepType::FULL, 0}; +    profile.resolutions = { 75, 150, 300 }; +    motor.fast_profiles.push_back(profile); + +    profile = MotorProfile{MotorSlope::create_from_steps(6000, 3000, 100), StepType::FULL, 0}; +    profile.resolutions = { 600, 1200, 2400 }; +    motor.fast_profiles.push_back(profile); + +    s_motors->push_back(std::move(motor)); + + +    motor = Genesys_Motor(); +    motor.id = MotorId::CANON_LIDE_90; +    motor.base_ydpi = 1200; +    profile = {MotorSlope::create_from_steps(8000, 3000, 200), StepType::FULL, 0}; +    profile.resolutions = { 150, 300 }; +    motor.profiles.push_back(profile); + +    profile = {MotorSlope::create_from_steps(7000, 3000, 200), StepType::HALF, 0}; +    profile.resolutions = { 600, 1200 }; +    motor.profiles.push_back(profile); + +    profile = {MotorSlope::create_from_steps(7000, 3000, 200), StepType::QUARTER, 0}; +    profile.resolutions = { 2400 }; +    motor.profiles.push_back(profile);      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::XP200;      motor.base_ydpi = 600; -    motor.optical_ydpi = 600; -    motor.slopes.push_back(MotorSlope::create_from_steps(3500, 1300, 60)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3500, 1300, 60)); +    motor.profiles.push_back({MotorSlope::create_from_steps(3500, 1300, 60), StepType::FULL, 0}); +    motor.profiles.push_back({MotorSlope::create_from_steps(3500, 1300, 60), StepType::HALF, 0}); +    motor.fast_profiles.push_back({MotorSlope::create_from_steps(3500, 1300, 60), StepType::FULL, 0});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::XP300;      motor.base_ydpi = 300; -    motor.optical_ydpi = 600;      // works best with GPIO10, GPIO14 off -    motor.slopes.push_back(MotorSlope::create_from_steps(3700, 3700, 2)); -    motor.slopes.push_back(MotorSlope::create_from_steps(11000, 11000, 2)); +    profile = MotorProfile{MotorSlope::create_from_steps(3700, 3700, 2), StepType::FULL, 0}; +    profile.resolutions = {}; // used during fast moves +    motor.profiles.push_back(profile); + +    // FIXME: this motor profile is useless +    profile = MotorProfile{MotorSlope::create_from_steps(11000, 11000, 2), StepType::HALF, 0}; +    profile.resolutions = {75, 150, 300, 600}; +    motor.profiles.push_back(profile); + +    profile = MotorProfile{MotorSlope::create_from_steps(3700, 3700, 2), StepType::FULL, 0}; +    motor.fast_profiles.push_back(profile);      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::DP665;      motor.base_ydpi = 750; -    motor.optical_ydpi = 1500; -    motor.slopes.push_back(MotorSlope::create_from_steps(3000, 2500, 10)); -    motor.slopes.push_back(MotorSlope::create_from_steps(11000, 11000, 2)); + +    profile = MotorProfile{MotorSlope::create_from_steps(3000, 2500, 10), StepType::FULL, 0}; +    profile.resolutions = {75, 150}; +    motor.profiles.push_back(profile); + +    // FIXME: this motor profile is useless +    profile = MotorProfile{MotorSlope::create_from_steps(11000, 11000, 2), StepType::HALF, 0}; +    profile.resolutions = {300, 600, 1200}; +    motor.profiles.push_back(profile); + +    profile = MotorProfile{MotorSlope::create_from_steps(3000, 2500, 10), StepType::FULL, 0}; +    motor.fast_profiles.push_back(profile);      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::ROADWARRIOR;      motor.base_ydpi = 750; -    motor.optical_ydpi = 1500; -    motor.slopes.push_back(MotorSlope::create_from_steps(3000, 2600, 10)); -    motor.slopes.push_back(MotorSlope::create_from_steps(11000, 11000, 2)); + +    profile = MotorProfile{MotorSlope::create_from_steps(3000, 2600, 10), StepType::FULL, 0}; +    profile.resolutions = {75, 150}; +    motor.profiles.push_back(profile); + +    // FIXME: this motor profile is useless +    profile = MotorProfile{MotorSlope::create_from_steps(11000, 11000, 2), StepType::HALF, 0}; +    profile.resolutions = {300, 600, 1200}; +    motor.profiles.push_back(profile); + +    profile = MotorProfile{MotorSlope::create_from_steps(3000, 2600, 10), StepType::FULL, 0}; +    motor.fast_profiles.push_back(profile);      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::DSMOBILE_600;      motor.base_ydpi = 750; -    motor.optical_ydpi = 1500; -    motor.slopes.push_back(MotorSlope::create_from_steps(6666, 3700, 8)); -    motor.slopes.push_back(MotorSlope::create_from_steps(6666, 3700, 8)); + +    profile = MotorProfile{MotorSlope::create_from_steps(6666, 3700, 8), StepType::FULL, 0}; +    profile.resolutions = {75, 150}; +    motor.profiles.push_back(profile); + +    profile = MotorProfile{MotorSlope::create_from_steps(6666, 3700, 8), StepType::HALF, 0}; +    profile.resolutions = {300, 600, 1200}; +    motor.profiles.push_back(profile); + +    profile = MotorProfile{MotorSlope::create_from_steps(6666, 3700, 8), StepType::FULL, 0}; +    motor.fast_profiles.push_back(profile);      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::CANON_LIDE_100;      motor.base_ydpi = 1200; -    motor.optical_ydpi = 6400; -    motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1000, 127)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1500, 127)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3 * 2712, 3 * 2712, 16)); +    motor.profiles.push_back({MotorSlope::create_from_steps(46876, 864, 255), +                              StepType::HALF, 1432}); +    motor.profiles.push_back({MotorSlope::create_from_steps(46876, 864, 279), +                              StepType::QUARTER, 2712}); +    motor.profiles.push_back({MotorSlope::create_from_steps(31680, 864, 247), +                              StepType::EIGHTH, 5280});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::CANON_LIDE_200;      motor.base_ydpi = 1200; -    motor.optical_ydpi = 6400; -    motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1000, 127)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1500, 127)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3 * 2712, 3 * 2712, 16)); +    motor.profiles.push_back({MotorSlope::create_from_steps(46876, 864, 255), +                              StepType::HALF, 1432}); +    motor.profiles.push_back({MotorSlope::create_from_steps(46876, 864, 279), +                              StepType::QUARTER, 2712}); +    motor.profiles.push_back({MotorSlope::create_from_steps(31680, 864, 247), +                              StepType::EIGHTH, 5280}); +    motor.profiles.push_back({MotorSlope::create_from_steps(31680, 864, 247), +                              StepType::EIGHTH, 10416});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::CANON_LIDE_700;      motor.base_ydpi = 1200; -    motor.optical_ydpi = 6400; -    motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1000, 127)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1500, 127)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3 * 2712, 3 * 2712, 16)); +    motor.profiles.push_back({MotorSlope::create_from_steps(46876, 534, 255), +                              StepType::HALF, 1424}); +    motor.profiles.push_back({MotorSlope::create_from_steps(46876, 534, 255), +                              StepType::HALF, 1504}); +    motor.profiles.push_back({MotorSlope::create_from_steps(46876, 2022, 127), +                              StepType::HALF, 2696}); +    motor.profiles.push_back({MotorSlope::create_from_steps(46876, 534, 255), +                              StepType::HALF, 2848}); +    motor.profiles.push_back({MotorSlope::create_from_steps(46876, 15864, 2), +                              StepType::EIGHTH, 10576});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::KVSS080;      motor.base_ydpi = 1200; -    motor.optical_ydpi = 1200; -    motor.slopes.push_back(MotorSlope::create_from_steps(22222, 500, 246)); -    motor.slopes.push_back(MotorSlope::create_from_steps(22222, 500, 246)); -    motor.slopes.push_back(MotorSlope::create_from_steps(22222, 500, 246)); +    motor.profiles.push_back({MotorSlope::create_from_steps(44444, 500, 489), +                              StepType::HALF, 8000});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::G4050;      motor.base_ydpi = 2400; -    motor.optical_ydpi = 9600; -    motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246)); +    motor.profiles.push_back({MotorSlope::create_from_steps(7842, 320, 602), +                              StepType::HALF, 8016}); +    motor.profiles.push_back({MotorSlope::create_from_steps(9422, 254, 1004), +                              StepType::HALF, 15624}); +    motor.profiles.push_back({MotorSlope::create_from_steps(28032, 2238, 604), +                              StepType::HALF, 56064}); +    motor.profiles.push_back({MotorSlope::create_from_steps(42752, 1706, 610), +                              StepType::QUARTER, 42752});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::CANON_4400F;      motor.base_ydpi = 2400; -    motor.optical_ydpi = 9600; -    motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(28597 * 2, 727 * 2, 200); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 1; +    profile.resolutions = { 300, 600 }; +    motor.profiles.push_back(std::move(profile)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(28597 * 2, 727 * 2, 200); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 0; +    profile.resolutions = { 1200, 2400, 4800, 9600 }; +    motor.profiles.push_back(std::move(profile)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(28597 * 2, 279 * 2, 1000); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 0; +    motor.fast_profiles.push_back(std::move(profile)); + +    s_motors->push_back(std::move(motor)); + + +    motor = Genesys_Motor(); +    motor.id = MotorId::CANON_5600F; +    motor.base_ydpi = 2400; + +    // FIXME: real limit is 134, but for some reason the motor can't acquire that speed. +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(2500 * 2, 134 * 2, 1000); +    profile.step_type = StepType::HALF; +    profile.motor_vref = 0; +    profile.resolutions = { 75, 150 }; +    motor.profiles.push_back(std::move(profile)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(2500 * 2, 200 * 2, 1000); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 0; +    profile.resolutions = { 300, 600, 1200, 2400, 4800 }; +    motor.profiles.push_back(std::move(profile)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(2500 * 2, 200 * 2, 1000); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 0; +    motor.fast_profiles.push_back(std::move(profile)); +      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::CANON_8400F;      motor.base_ydpi = 1600; -    motor.optical_ydpi = 6400; -    motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(20202 * 4, 333 * 4, 100); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 0; +    profile.resolutions = VALUE_FILTER_ANY; +    profile.scan_methods = { ScanMethod::FLATBED }; +    motor.profiles.push_back(std::move(profile)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(65535 * 4, 333 * 4, 100); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 2; +    profile.resolutions = VALUE_FILTER_ANY; +    profile.scan_methods = { ScanMethod::TRANSPARENCY, ScanMethod::TRANSPARENCY_INFRARED }; +    motor.profiles.push_back(std::move(profile)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(65535 * 4, 333 * 4, 200); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 2; +    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));      motor = Genesys_Motor();      motor.id = MotorId::CANON_8600F;      motor.base_ydpi = 2400; -    motor.optical_ydpi = 9600; -    motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3961, 240, 246)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 3; +    profile.resolutions = { 300, 600 }; +    profile.scan_methods = { ScanMethod::FLATBED }; +    motor.profiles.push_back(std::move(profile)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 2; +    profile.resolutions = { 1200, 2400 }; +    profile.scan_methods = { ScanMethod::FLATBED }; +    motor.profiles.push_back(std::move(profile)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 2; +    profile.resolutions = { 4800 }; +    profile.scan_methods = { ScanMethod::FLATBED }; +    motor.profiles.push_back(std::move(profile)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 2; +    profile.resolutions = { 300, 600 }; +    profile.scan_methods = { ScanMethod::TRANSPARENCY, +                             ScanMethod::TRANSPARENCY_INFRARED }; +    motor.profiles.push_back(std::move(profile)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 1; +    profile.resolutions = { 1200, 2400 }; +    profile.scan_methods = { ScanMethod::TRANSPARENCY, +                             ScanMethod::TRANSPARENCY_INFRARED }; +    motor.profiles.push_back(std::move(profile)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 0; +    profile.resolutions = { 4800 }; +    profile.scan_methods = { ScanMethod::TRANSPARENCY, +                             ScanMethod::TRANSPARENCY_INFRARED }; +    motor.profiles.push_back(std::move(profile)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(59240, 582, 1020); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 2; +    motor.fast_profiles.push_back(std::move(profile)); +      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::CANON_LIDE_110;      motor.base_ydpi = 4800; -    motor.optical_ydpi = 9600; -    motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1000, 256)); +    motor.profiles.push_back({MotorSlope::create_from_steps(62496, 335, 255), +                              StepType::FULL, 2768}); +    motor.profiles.push_back({MotorSlope::create_from_steps(62496, 335, 469), +                              StepType::HALF, 5360}); +    motor.profiles.push_back({MotorSlope::create_from_steps(62496, 2632, 3), +                              StepType::HALF, 10528}); +    motor.profiles.push_back({MotorSlope::create_from_steps(62496, 10432, 3), +                              StepType::QUARTER, 20864});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::CANON_LIDE_120;      motor.base_ydpi = 4800; -    motor.optical_ydpi = 9600; -    motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1000, 256)); +    motor.profiles.push_back({MotorSlope::create_from_steps(62496, 864, 127), +                              StepType::FULL, 4608}); +    motor.profiles.push_back({MotorSlope::create_from_steps(62496, 2010, 63), +                              StepType::HALF, 5360}); +    motor.profiles.push_back({MotorSlope::create_from_steps(62464, 2632, 3), +                              StepType::QUARTER, 10528}); +    motor.profiles.push_back({MotorSlope::create_from_steps(62592, 10432, 5), +                              StepType::QUARTER, 20864});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::CANON_LIDE_210;      motor.base_ydpi = 4800; -    motor.optical_ydpi = 9600; -    motor.slopes.push_back(MotorSlope::create_from_steps(3000, 1000, 256)); +    motor.profiles.push_back({MotorSlope::create_from_steps(62496, 335, 255), +                              StepType::FULL, 2768}); +    motor.profiles.push_back({MotorSlope::create_from_steps(62496, 335, 469), +                              StepType::HALF, 5360}); +    motor.profiles.push_back({MotorSlope::create_from_steps(62496, 2632, 3), +                              StepType::HALF, 10528}); +    motor.profiles.push_back({MotorSlope::create_from_steps(62496, 10432, 4), +                              StepType::QUARTER, 20864}); +    motor.profiles.push_back({MotorSlope::create_from_steps(62496, 10432, 4), +                              StepType::EIGHTH, 41536});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::PLUSTEK_OPTICPRO_3600;      motor.base_ydpi = 1200; -    motor.optical_ydpi = 2400; -    motor.slopes.push_back(MotorSlope::create_from_steps(3500, 1300, 60)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3500, 3250, 60)); + +    profile = MotorProfile{MotorSlope::create_from_steps(3500, 1300, 60), StepType::FULL, 0}; +    profile.resolutions = {75, 100, 150, 200}; +    motor.profiles.push_back(profile); + +    // FIXME: this motor profile is almost useless +    profile = MotorProfile{MotorSlope::create_from_steps(3500, 3250, 60), StepType::HALF, 0}; +    profile.resolutions = {300, 400, 600, 1200}; +    motor.profiles.push_back(profile); + +    profile = MotorProfile{MotorSlope::create_from_steps(3500, 1300, 60), StepType::FULL, 0}; +    motor.fast_profiles.push_back(profile); +    s_motors->push_back(std::move(motor)); + + +    motor = Genesys_Motor(); +    motor.id = MotorId::PLUSTEK_OPTICFILM_7200; +    motor.base_ydpi = 3600; + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(20000 * 2, 600 * 2, 200); +    profile.step_type = StepType::HALF; +    profile.motor_vref = 0; +    motor.profiles.push_back(std::move(profile)); +      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::PLUSTEK_OPTICFILM_7200I;      motor.base_ydpi = 3600; -    motor.optical_ydpi = 3600; + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(34722 * 2, 454 * 2, 40); +    profile.step_type = StepType::HALF; +    profile.motor_vref = 3; +    motor.profiles.push_back(std::move(profile)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(34722 * 2, 454 * 2, 40); +    profile.step_type = StepType::HALF; +    profile.motor_vref = 0; +    motor.fast_profiles.push_back(std::move(profile)); +      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::PLUSTEK_OPTICFILM_7300;      motor.base_ydpi = 3600; -    motor.optical_ydpi = 3600; + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(56818 * 4, 454 * 4, 30); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 3; +    motor.profiles.push_back(std::move(profile)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(56818 * 4, 454 * 4, 30); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 0; +    motor.fast_profiles.push_back(std::move(profile)); + +    s_motors->push_back(std::move(motor)); + + +    motor = Genesys_Motor(); +    motor.id = MotorId::PLUSTEK_OPTICFILM_7400; +    motor.base_ydpi = 3600; + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(64102 * 4, 400 * 4, 30); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 3; +    motor.profiles.push_back(profile); +    motor.fast_profiles.push_back(profile);      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::PLUSTEK_OPTICFILM_7500I;      motor.base_ydpi = 3600; -    motor.optical_ydpi = 3600; + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(56818 * 4, 454 * 4, 30); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 3; +    motor.profiles.push_back(std::move(profile)); + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(56818 * 4, 454 * 4, 30); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 0; +    motor.fast_profiles.push_back(std::move(profile)); + +    s_motors->push_back(std::move(motor)); + + +    motor = Genesys_Motor(); +    motor.id = MotorId::PLUSTEK_OPTICFILM_8200I; +    motor.base_ydpi = 3600; + +    profile = MotorProfile(); +    profile.slope = MotorSlope::create_from_steps(64102 * 4, 400 * 4, 100); +    profile.step_type = StepType::QUARTER; +    profile.motor_vref = 3; +    motor.profiles.push_back(profile); +    motor.fast_profiles.push_back(profile);      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::IMG101;      motor.base_ydpi = 600; -    motor.optical_ydpi = 1200; -    motor.slopes.push_back(MotorSlope::create_from_steps(3500, 1300, 60)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3500, 3250, 60)); +    motor.profiles.push_back({MotorSlope::create_from_steps(22000, 1000, 1017), +                              StepType::HALF, 11000});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::PLUSTEK_OPTICBOOK_3800;      motor.base_ydpi = 600; -    motor.optical_ydpi = 1200; -    motor.slopes.push_back(MotorSlope::create_from_steps(3500, 1300, 60)); -    motor.slopes.push_back(MotorSlope::create_from_steps(3500, 3250, 60)); +    motor.profiles.push_back({MotorSlope::create_from_steps(22000, 1000, 1017), +                              StepType::HALF, 11000});      s_motors->push_back(std::move(motor));      motor = Genesys_Motor();      motor.id = MotorId::CANON_LIDE_80;      motor.base_ydpi = 2400; -    motor.optical_ydpi = 4800; // 9600 -    motor.slopes.push_back(MotorSlope::create_from_steps(9560, 1912, 31)); +    motor.profiles.push_back({MotorSlope::create_from_steps(9560, 1912, 31), StepType::FULL, 0});      s_motors->push_back(std::move(motor));  } diff --git a/backend/genesys/tables_motor_profile.cpp b/backend/genesys/tables_motor_profile.cpp deleted file mode 100644 index 18f7271..0000000 --- a/backend/genesys/tables_motor_profile.cpp +++ /dev/null @@ -1,380 +0,0 @@ -/*  sane - Scanner Access Now Easy. - -    Copyright (C) 2019 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. - -    As a special exception, the authors of SANE give permission for -    additional uses of the libraries contained in this release of SANE. - -    The exception is that, if you link a SANE library with other files -    to produce an executable, this does not by itself cause the -    resulting executable to be covered by the GNU General Public -    License.  Your use of that executable is in no way restricted on -    account of linking the SANE library code into it. - -    This exception does not, however, invalidate any other reasons why -    the executable file might be covered by the GNU General Public -    License. - -    If you submit changes to SANE to the maintainers to be included in -    a subsequent release, you agree by submitting the changes that -    those changes may be distributed with this exception intact. - -    If you write modifications of your own for SANE, it is your choice -    whether to permit this exception to apply to your modifications. -    If you do not wish that, delete this exception notice. -*/ - -#define DEBUG_DECLARE_ONLY - -#include "low.h" - -namespace genesys { - -StaticInit<std::vector<Motor_Profile>> gl843_motor_profiles; - -void genesys_init_motor_profile_tables_gl843() -{ -    gl843_motor_profiles.init(); - -    auto profile = Motor_Profile(); -    profile.motor_id = MotorId::KVSS080; -    profile.exposure = 8000; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(44444, 500, 489); -    gl843_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::G4050; -    profile.exposure = 8016; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(7842, 320, 602); -    gl843_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::G4050; -    profile.exposure = 15624; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(9422, 254, 1004); -    gl843_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::G4050; -    profile.exposure = 42752; -    profile.step_type = StepType::QUARTER; -    profile.slope = MotorSlope::create_from_steps(42752, 1706, 610); -    gl843_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::G4050; -    profile.exposure = 56064; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(28032, 2238, 604); -    gl843_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_4400F; -    profile.exposure = 11640; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(49152, 484, 1014); -    gl843_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_8400F; -    profile.exposure = 50000; -    profile.step_type = StepType::QUARTER; -    profile.slope = MotorSlope::create_from_steps(8743, 300, 794); -    gl843_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_8600F; -    profile.exposure = 0x59d8; -    profile.step_type = StepType::QUARTER; -    // FIXME: if the exposure is lower then we'll select another motor -    profile.slope = MotorSlope::create_from_steps(54612, 1500, 219); -    gl843_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::PLUSTEK_OPTICFILM_7200I; -    profile.exposure = 0; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(39682, 1191, 15); -    gl843_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::PLUSTEK_OPTICFILM_7300; -    profile.exposure = 0x2f44; -    profile.step_type = StepType::QUARTER; -    profile.slope = MotorSlope::create_from_steps(31250, 1512, 6); -    gl843_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::PLUSTEK_OPTICFILM_7500I; -    profile.exposure = 0; -    profile.step_type = StepType::QUARTER; -    profile.slope = MotorSlope::create_from_steps(31250, 1375, 7); -    gl843_motor_profiles->push_back(profile); -} - -StaticInit<std::vector<Motor_Profile>> gl846_motor_profiles; - -void genesys_init_motor_profile_tables_gl846() -{ -    gl846_motor_profiles.init(); - -    auto profile = Motor_Profile(); -    profile.motor_id = MotorId::IMG101; -    profile.exposure = 11000; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(22000, 1000, 1017); - -    gl846_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::PLUSTEK_OPTICBOOK_3800; -    profile.exposure = 11000; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(22000, 1000, 1017); -    gl846_motor_profiles->push_back(profile); -} - -/** - * database of motor profiles - */ - -StaticInit<std::vector<Motor_Profile>> gl847_motor_profiles; - -void genesys_init_motor_profile_tables_gl847() -{ -    gl847_motor_profiles.init(); - -    auto profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_100; -    profile.exposure = 2848; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(46876, 534, 255); -    gl847_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_100; -    profile.exposure = 1424; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(46876, 534, 255); -    gl847_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_100; -    profile.exposure = 1432; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(46876, 534, 255); -    gl847_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_100; -    profile.exposure = 2712; -    profile.step_type = StepType::QUARTER; -    profile.slope = MotorSlope::create_from_steps(46876, 534, 279); -    gl847_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_100; -    profile.exposure = 5280; -    profile.step_type = StepType::EIGHTH; -    profile.slope = MotorSlope::create_from_steps(31680, 534, 247); -    gl847_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_200; -    profile.exposure = 2848; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(46876, 534, 255); -    gl847_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_200; -    profile.exposure = 1424; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(46876, 534, 255); -    gl847_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_200; -    profile.exposure = 1432; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(46876, 534, 255); -    gl847_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_200; -    profile.exposure = 2712; -    profile.step_type = StepType::QUARTER; -    profile.slope = MotorSlope::create_from_steps(46876, 534, 279); -    gl847_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_200; -    profile.exposure = 5280; -    profile.step_type = StepType::EIGHTH; -    profile.slope = MotorSlope::create_from_steps(31680, 534, 247); -    gl847_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_200; -    profile.exposure = 10416; -    profile.step_type = StepType::EIGHTH; -    profile.slope = MotorSlope::create_from_steps(31680, 534, 247); -    gl847_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_700; -    profile.exposure = 2848; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(46876, 534, 255); -    gl847_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_700; -    profile.exposure = 1424; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(46876, 534, 255); -    gl847_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_700; -    profile.exposure = 1504; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(46876, 534, 255); -    gl847_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_700; -    profile.exposure = 2696; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(46876, 2022, 127); -    gl847_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_700; -    profile.exposure = 10576; -    profile.step_type = StepType::EIGHTH; -    profile.slope = MotorSlope::create_from_steps(46876, 15864, 2); -    gl847_motor_profiles->push_back(profile); -} - -StaticInit<std::vector<Motor_Profile>> gl124_motor_profiles; - -void genesys_init_motor_profile_tables_gl124() -{ -    gl124_motor_profiles.init(); - -    // NEXT LPERIOD=PREVIOUS*2-192 -    Motor_Profile profile; -    profile.motor_id = MotorId::CANON_LIDE_110; -    profile.exposure = 2768; -    profile.step_type = StepType::FULL; -    profile.slope = MotorSlope::create_from_steps(62496, 335, 255); -    gl124_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_110; -    profile.exposure = 5360; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(62496, 335, 469); -    gl124_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_110; -    profile.exposure = 10528; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(62496, 2632, 3); -    gl124_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_110; -    profile.exposure = 20864; -    profile.step_type = StepType::QUARTER; -    profile.slope = MotorSlope::create_from_steps(62496, 10432, 3); -    gl124_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_120; -    profile.exposure = 4608; -    profile.step_type = StepType::FULL; -    profile.slope = MotorSlope::create_from_steps(62496, 864, 127); -    gl124_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_120; -    profile.exposure = 5360; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(62496, 2010, 63); -    gl124_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_120; -    profile.exposure = 10528; -    profile.step_type = StepType::QUARTER; -    profile.slope = MotorSlope::create_from_steps(62464, 2632, 3); -    gl124_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_120; -    profile.exposure = 20864; -    profile.step_type = StepType::QUARTER; -    profile.slope = MotorSlope::create_from_steps(62592, 10432, 5); -    gl124_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_210; -    profile.exposure = 2768; -    profile.step_type = StepType::FULL; -    profile.slope = MotorSlope::create_from_steps(62496, 335, 255); -    gl124_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_210; -    profile.exposure = 5360; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(62496, 335, 469); -    gl124_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_210; -    profile.exposure = 10528; -    profile.step_type = StepType::HALF; -    profile.slope = MotorSlope::create_from_steps(62496, 2632, 3); -    gl124_motor_profiles->push_back(profile); - -    profile = Motor_Profile(); -    profile.motor_id = MotorId::CANON_LIDE_210; -    profile.exposure = 20864; -    profile.step_type = StepType::QUARTER; -    profile.slope = MotorSlope::create_from_steps(62496, 10432, 4); -    gl124_motor_profiles->push_back(profile); -} - -void genesys_init_motor_profile_tables() -{ -    genesys_init_motor_profile_tables_gl843(); -    genesys_init_motor_profile_tables_gl846(); -    genesys_init_motor_profile_tables_gl847(); -    genesys_init_motor_profile_tables_gl124(); -} - -} // namespace genesys diff --git a/backend/genesys/tables_sensor.cpp b/backend/genesys/tables_sensor.cpp index bbbe441..b90355c 100644 --- a/backend/genesys/tables_sensor.cpp +++ b/backend/genesys/tables_sensor.cpp @@ -44,71 +44,10 @@  #define DEBUG_DECLARE_ONLY  #include "low.h" +#include <map>  namespace genesys { -inline unsigned default_get_logical_hwdpi(const Genesys_Sensor& sensor, unsigned xres) -{ -    if (sensor.logical_dpihw_override) -        return sensor.logical_dpihw_override; - -    // can't be below 600 dpi -    if (xres <= 600) { -        return 600; -    } -    if (xres <= static_cast<unsigned>(sensor.optical_res) / 4) { -        return sensor.optical_res / 4; -    } -    if (xres <= static_cast<unsigned>(sensor.optical_res) / 2) { -        return sensor.optical_res / 2; -    } -    return sensor.optical_res; -} - -inline unsigned get_sensor_optical_with_ccd_divisor(const Genesys_Sensor& sensor, unsigned xres) -{ -    unsigned hwres = sensor.optical_res / sensor.get_ccd_size_divisor_for_dpi(xres); - -    if (xres <= hwres / 4) { -        return hwres / 4; -    } -    if (xres <= hwres / 2) { -        return hwres / 2; -    } -    return hwres; -} - -inline unsigned default_get_ccd_size_divisor_for_dpi(const Genesys_Sensor& sensor, unsigned xres) -{ -    if (sensor.ccd_size_divisor >= 4 && xres * 4 <= static_cast<unsigned>(sensor.optical_res)) { -        return 4; -    } -    if (sensor.ccd_size_divisor >= 2 && xres * 2 <= static_cast<unsigned>(sensor.optical_res)) { -        return 2; -    } -    return 1; -} - -inline unsigned get_ccd_size_divisor_exact(const Genesys_Sensor& sensor, unsigned xres) -{ -    (void) xres; -    return sensor.ccd_size_divisor; -} - -inline unsigned get_ccd_size_divisor_gl124(const Genesys_Sensor& sensor, unsigned xres) -{ -    // we have 2 domains for ccd: xres below or above half ccd max dpi -    if (xres <= 300 && sensor.ccd_size_divisor > 1) { -        return 2; -    } -    return 1; -} - -inline unsigned default_get_hwdpi_divisor_for_dpi(const Genesys_Sensor& sensor, unsigned xres) -{ -    return sensor.optical_res / default_get_logical_hwdpi(sensor, xres); -} -  StaticInit<std::vector<Genesys_Sensor>> s_sensors;  void genesys_init_sensor_tables() @@ -118,444 +57,231 @@ void genesys_init_sensor_tables()      Genesys_Sensor sensor;      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_UMAX; -    sensor.optical_res = 1200; +    sensor.sensor_id = SensorId::CCD_UMAX; // gl646 +    sensor.full_resolution = 1200;      sensor.black_pixels = 48;      sensor.dummy_pixel = 64; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 10800;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 230;      sensor.exposure = { 0x0000, 0x0000, 0x0000 };      sensor.custom_regs = { -        { 0x08, 0x01 }, -        { 0x09, 0x03 }, -        { 0x0a, 0x05 }, -        { 0x0b, 0x07 }, -        { 0x16, 0x33 }, -        { 0x17, 0x05 }, -        { 0x18, 0x31 }, -        { 0x19, 0x2a }, -        { 0x1a, 0x00 }, -        { 0x1b, 0x00 }, -        { 0x1c, 0x00 }, -        { 0x1d, 0x02 }, -        { 0x52, 0x13 }, -        { 0x53, 0x17 }, -        { 0x54, 0x03 }, -        { 0x55, 0x07 }, -        { 0x56, 0x0b }, -        { 0x57, 0x0f }, -        { 0x58, 0x23 }, -        { 0x59, 0x00 }, -        { 0x5a, 0xc1 }, -        { 0x5b, 0x00 }, -        { 0x5c, 0x00 }, -        { 0x5d, 0x00 }, -        { 0x5e, 0x00 }, +        { 0x08, 0x01 }, { 0x09, 0x03 }, { 0x0a, 0x05 }, { 0x0b, 0x07 }, +        { 0x16, 0x33 }, { 0x17, 0x05 }, { 0x18, 0x31 }, { 0x19, 0x2a }, +        { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, +        { 0x52, 0x13 }, { 0x53, 0x17 }, { 0x54, 0x03 }, { 0x55, 0x07 }, +        { 0x56, 0x0b }, { 0x57, 0x0f }, { 0x58, 0x23 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +        { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 },      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi; -    s_sensors->push_back(sensor); +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpiset; +            int output_pixel_offset; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 150, 4 }, +            { { 150 }, 300, 8 }, +            { { 300 }, 600, 16 }, +            { { 600 }, 1200, 32 }, +            { { 1200 }, 2400, 64 }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            s_sensors->push_back(sensor); +        } +    }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_ST12; -    sensor.optical_res = 600; +    sensor.sensor_id = SensorId::CCD_ST12; // gl646 +    sensor.full_resolution = 600;      sensor.black_pixels = 48;      sensor.dummy_pixel = 85; -    sensor.ccd_start_xoffset = 152; -    sensor.sensor_pixels = 5416;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 230;      sensor.exposure = { 0x0000, 0x0000, 0x0000 };      sensor.custom_regs = { -        { 0x08, 0x02 }, -        { 0x09, 0x00 }, -        { 0x0a, 0x06 }, -        { 0x0b, 0x04 }, -        { 0x16, 0x2b }, -        { 0x17, 0x08 }, -        { 0x18, 0x20 }, -        { 0x19, 0x2a }, -        { 0x1a, 0x00 }, -        { 0x1b, 0x00 }, -        { 0x1c, 0x0c }, -        { 0x1d, 0x03 }, -        { 0x52, 0x0f }, -        { 0x53, 0x13 }, -        { 0x54, 0x17 }, -        { 0x55, 0x03 }, -        { 0x56, 0x07 }, -        { 0x57, 0x0b }, -        { 0x58, 0x83 }, -        { 0x59, 0x00 }, -        { 0x5a, 0xc1 }, -        { 0x5b, 0x00 }, -        { 0x5c, 0x00 }, -        { 0x5d, 0x00 }, -        { 0x5e, 0x00 }, +        { 0x08, 0x02 }, { 0x09, 0x00 }, { 0x0a, 0x06 }, { 0x0b, 0x04 }, +        { 0x16, 0x2b }, { 0x17, 0x08 }, { 0x18, 0x20 }, { 0x19, 0x2a }, +        { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x0c }, { 0x1d, 0x03 }, +        { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +        { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +        { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 },      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi; -    s_sensors->push_back(sensor); +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            int output_pixel_offset; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 10 }, +            { { 150 }, 21 }, +            { { 300 }, 42 }, +            { { 600 }, 85 }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.register_dpiset = setting.resolutions.values()[0]; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            s_sensors->push_back(sensor); +        } +    }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_ST24; -    sensor.optical_res = 1200; +    sensor.sensor_id = SensorId::CCD_ST24; // gl646 +    sensor.full_resolution = 1200;      sensor.black_pixels = 48;      sensor.dummy_pixel = 64; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 10800;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 230;      sensor.exposure = { 0x0000, 0x0000, 0x0000 };      sensor.custom_regs = { -        { 0x08, 0x0e }, -        { 0x09, 0x0c }, -        { 0x0a, 0x00 }, -        { 0x0b, 0x0c }, -        { 0x16, 0x33 }, -        { 0x17, 0x08 }, -        { 0x18, 0x31 }, -        { 0x19, 0x2a }, -        { 0x1a, 0x00 }, -        { 0x1b, 0x00 }, -        { 0x1c, 0x00 }, -        { 0x1d, 0x02 }, -        { 0x52, 0x17 }, -        { 0x53, 0x03 }, -        { 0x54, 0x07 }, -        { 0x55, 0x0b }, -        { 0x56, 0x0f }, -        { 0x57, 0x13 }, -        { 0x58, 0x03 }, -        { 0x59, 0x00 }, -        { 0x5a, 0xc1 }, -        { 0x5b, 0x00 }, -        { 0x5c, 0x00 }, -        { 0x5d, 0x00 }, -        { 0x5e, 0x00 }, +        { 0x08, 0x0e }, { 0x09, 0x0c }, { 0x0a, 0x00 }, { 0x0b, 0x0c }, +        { 0x16, 0x33 }, { 0x17, 0x08 }, { 0x18, 0x31 }, { 0x19, 0x2a }, +        { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, +        { 0x52, 0x17 }, { 0x53, 0x03 }, { 0x54, 0x07 }, { 0x55, 0x0b }, +        { 0x56, 0x0f }, { 0x57, 0x13 }, { 0x58, 0x03 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +        { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 },      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi; -    s_sensors->push_back(sensor); +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpiset; +            int output_pixel_offset; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 150, 4 }, +            { { 150 }, 300, 8 }, +            { { 300 }, 600, 16 }, +            { { 600 }, 1200, 32 }, +            { { 1200 }, 2400, 64 }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            s_sensors->push_back(sensor); +        } +    }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_5345; -    sensor.optical_res = 1200; -    sensor.ccd_size_divisor = 2; +    sensor.sensor_id = SensorId::CCD_5345; // gl646 +    sensor.full_resolution = 1200;      sensor.black_pixels = 48;      sensor.dummy_pixel = 16; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 10872;      sensor.fau_gain_white_ref = 190;      sensor.gain_white_ref = 190;      sensor.exposure = { 0x0000, 0x0000, 0x0000 }; -    sensor.stagger_config = StaggerConfig{ 1200, 4 }; // FIXME: may be incorrect -    sensor.custom_base_regs = { -        { 0x08, 0x0d }, -        { 0x09, 0x0f }, -        { 0x0a, 0x11 }, -        { 0x0b, 0x13 }, -        { 0x16, 0x0b }, -        { 0x17, 0x0a }, -        { 0x18, 0x30 }, -        { 0x19, 0x2a }, -        { 0x1a, 0x00 }, -        { 0x1b, 0x00 }, -        { 0x1c, 0x00 }, -        { 0x1d, 0x03 }, -        { 0x52, 0x0f }, -        { 0x53, 0x13 }, -        { 0x54, 0x17 }, -        { 0x55, 0x03 }, -        { 0x56, 0x07 }, -        { 0x57, 0x0b }, -        { 0x58, 0x23 }, -        { 0x59, 0x00 }, -        { 0x5a, 0xc1 }, -        { 0x5b, 0x00 }, -        { 0x5c, 0x00 }, -        { 0x5d, 0x00 }, -        { 0x5e, 0x00 }, -    };      sensor.gamma = { 2.38f, 2.35f, 2.34f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions; +            unsigned optical_resolution; +            unsigned register_dpiset;              unsigned exposure_lperiod; -            unsigned ccd_size_divisor; +            Ratio pixel_count_ratio; +            int output_pixel_offset; +            StaggerConfig stagger_y; // FIXME: may be incorrect              GenesysRegisterSettingSet custom_regs;          };          CustomSensorSettings custom_settings[] = { -            { { 50 }, 12000, 2, { -                    { 0x08, 0x00 }, -                    { 0x09, 0x05 }, -                    { 0x0a, 0x06 }, -                    { 0x0b, 0x08 }, -                    { 0x16, 0x0b }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x28 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x03 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x00 }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 75 }, 11000, 2, { -                    { 0x08, 0x00 }, -                    { 0x09, 0x05 }, -                    { 0x0a, 0x06 }, -                    { 0x0b, 0x08 }, -                    { 0x16, 0x0b }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x28 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x03 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x00 }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 100 }, 11000, 2, { -                    { 0x08, 0x00 }, -                    { 0x09, 0x05 }, -                    { 0x0a, 0x06 }, -                    { 0x0b, 0x08 }, -                    { 0x16, 0x0b }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x28 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x03 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x00 }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 150 }, 11000, 2, { -                    { 0x08, 0x00 }, -                    { 0x09, 0x05 }, -                    { 0x0a, 0x06 }, -                    { 0x0b, 0x08 }, -                    { 0x16, 0x0b }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x28 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x03 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x00 }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 200 }, 11000, 2, { -                    { 0x08, 0x00 }, -                    { 0x09, 0x05 }, -                    { 0x0a, 0x06 }, -                    { 0x0b, 0x08 }, -                    { 0x16, 0x0b }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x28 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x03 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x00 }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 300 }, 11000, 2, { -                    { 0x08, 0x00 }, -                    { 0x09, 0x05 }, -                    { 0x0a, 0x06 }, -                    { 0x0b, 0x08 }, -                    { 0x16, 0x0b }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x28 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x03 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x00 }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 400 }, 11000, 2, { -                    { 0x08, 0x00 }, -                    { 0x09, 0x05 }, -                    { 0x0a, 0x06 }, -                    { 0x0b, 0x08 }, -                    { 0x16, 0x0b }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x28 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x03 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x00 }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 600 }, 11000, 2, { -                    { 0x08, 0x00 }, -                    { 0x09, 0x05 }, -                    { 0x0a, 0x06 }, -                    { 0x0b, 0x08 }, -                    { 0x16, 0x0b }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x28 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x03 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x00 }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 1200 }, 11000, 1, { -                    { 0x08, 0x0d }, -                    { 0x09, 0x0f }, -                    { 0x0a, 0x11 }, -                    { 0x0b, 0x13 }, -                    { 0x16, 0x0b }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x30 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x03 }, -                    { 0x52, 0x03 }, -                    { 0x53, 0x07 }, -                    { 0x54, 0x0b }, -                    { 0x55, 0x0f }, -                    { 0x56, 0x13 }, -                    { 0x57, 0x17 }, -                    { 0x58, 0x23 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x00 }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } +            {   { 50 }, 600, 100, 12000, Ratio{1, 2}, 0, StaggerConfig{}, { +                    { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, +                    { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } +                } +            }, +            {   { 75 }, 600, 150, 11000, Ratio{1, 2}, 1, StaggerConfig{}, { +                    { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, +                    { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } +                } +            }, +            {   { 100 }, 600, 200, 11000, Ratio{1, 2}, 1, StaggerConfig{}, { +                    { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, +                    { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } +                } +            }, +            {   { 150 }, 600, 300, 11000, Ratio{1, 2}, 2, StaggerConfig{}, { +                    { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, +                    { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } +                } +            }, +            {   { 200 }, 600, 400, 11000, Ratio{1, 2}, 2, StaggerConfig{}, { +                    { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, +                    { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } +                } +            }, +            {   { 300 }, 600, 600, 11000, Ratio{1, 2}, 4, StaggerConfig{}, { +                    { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, +                    { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } +                } +            }, +            {   { 400 }, 600, 800, 11000, Ratio{1, 2}, 5, StaggerConfig{}, { +                    { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, +                    { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } +                } +            }, +            {   { 600 }, 600, 1200, 11000, Ratio{1, 2}, 8, StaggerConfig{}, { +                    { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, +                    { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x28 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } +                } +            }, +            {   { 1200 }, 1200, 1200, 11000, Ratio{1, 1}, 16, StaggerConfig{4, 0}, { +                    { 0x08, 0x0d }, { 0x09, 0x0f }, { 0x0a, 0x11 }, { 0x0b, 0x13 }, +                    { 0x16, 0x0b }, { 0x17, 0x0a }, { 0x18, 0x30 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x03 }, +                    { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x0b }, { 0x55, 0x0f }, +                    { 0x56, 0x13 }, { 0x57, 0x17 }, { 0x58, 0x23 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 }                  }              },          }; @@ -563,8 +289,12 @@ void genesys_init_sensor_tables()          for (const CustomSensorSettings& setting : custom_settings)          {              sensor.resolutions = setting.resolutions; +            sensor.optical_resolution = setting.optical_resolution; +            sensor.register_dpiset = setting.register_dpiset;              sensor.exposure_lperiod = setting.exposure_lperiod; -            sensor.ccd_size_divisor = setting.ccd_size_divisor; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            sensor.stagger_y = setting.stagger_y;              sensor.custom_regs = setting.custom_regs;              s_sensors->push_back(sensor);          } @@ -572,223 +302,79 @@ void genesys_init_sensor_tables()      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_HP2400; -    sensor.optical_res = 1200; +    sensor.sensor_id = SensorId::CCD_HP2400; // gl646 +    sensor.full_resolution = 1200;      sensor.black_pixels = 48;      sensor.dummy_pixel = 15; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 10872;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.exposure = { 0x0000, 0x0000, 0x0000 }; -    sensor.stagger_config = StaggerConfig{1200, 4}; // FIXME: may be incorrect -    sensor.custom_base_regs = { -        { 0x08, 0x14 }, -        { 0x09, 0x15 }, -        { 0x0a, 0x00 }, -        { 0x0b, 0x00 }, -        { 0x16, 0xbf }, -        { 0x17, 0x08 }, -        { 0x18, 0x3f }, -        { 0x19, 0x2a }, -        { 0x1a, 0x00 }, -        { 0x1b, 0x00 }, -        { 0x1c, 0x00 }, -        { 0x1d, 0x02 }, -        { 0x52, 0x0b }, -        { 0x53, 0x0f }, -        { 0x54, 0x13 }, -        { 0x55, 0x17 }, -        { 0x56, 0x03 }, -        { 0x57, 0x07 }, -        { 0x58, 0x63 }, -        { 0x59, 0x00 }, -        { 0x5a, 0xc1 }, -        { 0x5b, 0x00 }, -        { 0x5c, 0x0e }, -        { 0x5d, 0x00 }, -        { 0x5e, 0x00 }, -    };      sensor.gamma = { 2.1f, 2.1f, 2.1f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpiset;              unsigned exposure_lperiod; +            Ratio pixel_count_ratio; +            int output_pixel_offset; +            StaggerConfig stagger_y;              GenesysRegisterSettingSet custom_regs;          };          CustomSensorSettings custom_settings[] = { -            { { 50 }, 7211, { -                    { 0x08, 0x14 }, -                    { 0x09, 0x15 }, -                    { 0x0a, 0x00 }, -                    { 0x0b, 0x00 }, -                    { 0x16, 0xbf }, -                    { 0x17, 0x08 }, -                    { 0x18, 0x3f }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x02 }, -                    { 0x52, 0x0b }, -                    { 0x53, 0x0f }, -                    { 0x54, 0x13 }, -                    { 0x55, 0x17 }, -                    { 0x56, 0x03 }, -                    { 0x57, 0x07 }, -                    { 0x58, 0x63 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x00 }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 100 }, 7211, { -                    { 0x08, 0x14 }, -                    { 0x09, 0x15 }, -                    { 0x0a, 0x00 }, -                    { 0x0b, 0x00 }, -                    { 0x16, 0xbf }, -                    { 0x17, 0x08 }, -                    { 0x18, 0x3f }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x02 }, -                    { 0x52, 0x0b }, -                    { 0x53, 0x0f }, -                    { 0x54, 0x13 }, -                    { 0x55, 0x17 }, -                    { 0x56, 0x03 }, -                    { 0x57, 0x07 }, -                    { 0x58, 0x63 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x00 }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 150 }, 7211, { -                    { 0x08, 0x14 }, -                    { 0x09, 0x15 }, -                    { 0x0a, 0x00 }, -                    { 0x0b, 0x00 }, -                    { 0x16, 0xbf }, -                    { 0x17, 0x08 }, -                    { 0x18, 0x3f }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x02 }, -                    { 0x52, 0x0b }, -                    { 0x53, 0x0f }, -                    { 0x54, 0x13 }, -                    { 0x55, 0x17 }, -                    { 0x56, 0x03 }, -                    { 0x57, 0x07 }, -                    { 0x58, 0x63 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x00 }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 300 }, 8751, { -                    { 0x08, 0x14 }, -                    { 0x09, 0x15 }, -                    { 0x0a, 0x00 }, -                    { 0x0b, 0x00 }, -                    { 0x16, 0xbf }, -                    { 0x17, 0x08 }, -                    { 0x18, 0x3f }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x02 }, -                    { 0x52, 0x0b }, -                    { 0x53, 0x0f }, -                    { 0x54, 0x13 }, -                    { 0x55, 0x17 }, -                    { 0x56, 0x03 }, -                    { 0x57, 0x07 }, -                    { 0x58, 0x63 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x00 }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 600 }, 18760, { -                    { 0x08, 0x0e }, -                    { 0x09, 0x0f }, -                    { 0x0a, 0x00 }, -                    { 0x0b, 0x00 }, -                    { 0x16, 0xbf }, -                    { 0x17, 0x08 }, -                    { 0x18, 0x31 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x02 }, -                    { 0x52, 0x03 }, -                    { 0x53, 0x07 }, -                    { 0x54, 0x0b }, -                    { 0x55, 0x0f }, -                    { 0x56, 0x13 }, -                    { 0x57, 0x17 }, -                    { 0x58, 0x23 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x00 }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 1200 }, 21749, { -                    { 0x08, 0x02 }, -                    { 0x09, 0x04 }, -                    { 0x0a, 0x00 }, -                    { 0x0b, 0x00 }, -                    { 0x16, 0xbf }, -                    { 0x17, 0x08 }, -                    { 0x18, 0x30 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0xc0 }, -                    { 0x1d, 0x42 }, -                    { 0x52, 0x0b }, -                    { 0x53, 0x0f }, -                    { 0x54, 0x13 }, -                    { 0x55, 0x17 }, -                    { 0x56, 0x03 }, -                    { 0x57, 0x07 }, -                    { 0x58, 0x63 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x0e }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } +            { { 50 }, 200, 7211, Ratio{1, 4}, 0, StaggerConfig{}, { +                    { 0x08, 0x14 }, { 0x09, 0x15 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, +                    { 0x16, 0xbf }, { 0x17, 0x08 }, { 0x18, 0x3f }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, +                    { 0x52, 0x0b }, { 0x53, 0x0f }, { 0x54, 0x13 }, { 0x55, 0x17 }, +                    { 0x56, 0x03 }, { 0x57, 0x07 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } +                } +            }, +            { { 100 }, 400, 7211, Ratio{1, 4}, 1, StaggerConfig{}, { +                    { 0x08, 0x14 }, { 0x09, 0x15 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, +                    { 0x16, 0xbf }, { 0x17, 0x08 }, { 0x18, 0x3f }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, +                    { 0x52, 0x0b }, { 0x53, 0x0f }, { 0x54, 0x13 }, { 0x55, 0x17 }, +                    { 0x56, 0x03 }, { 0x57, 0x07 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } +                } +            }, +            { { 150 }, 600, 7211, Ratio{1, 4}, 1, StaggerConfig{}, { +                    { 0x08, 0x14 }, { 0x09, 0x15 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, +                    { 0x16, 0xbf }, { 0x17, 0x08 }, { 0x18, 0x3f }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, +                    { 0x52, 0x0b }, { 0x53, 0x0f }, { 0x54, 0x13 }, { 0x55, 0x17 }, +                    { 0x56, 0x03 }, { 0x57, 0x07 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } +                } +            }, +            { { 300 }, 1200, 8751, Ratio{1, 4}, 3, StaggerConfig{}, { +                    { 0x08, 0x14 }, { 0x09, 0x15 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, +                    { 0x16, 0xbf }, { 0x17, 0x08 }, { 0x18, 0x3f }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, +                    { 0x52, 0x0b }, { 0x53, 0x0f }, { 0x54, 0x13 }, { 0x55, 0x17 }, +                    { 0x56, 0x03 }, { 0x57, 0x07 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } +                } +            }, +            { { 600 }, 1200, 18760, Ratio{1, 2}, 7, StaggerConfig{}, { +                    { 0x08, 0x0e }, { 0x09, 0x0f }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, +                    { 0x16, 0xbf }, { 0x17, 0x08 }, { 0x18, 0x31 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, +                    { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x0b }, { 0x55, 0x0f }, +                    { 0x56, 0x13 }, { 0x57, 0x17 }, { 0x58, 0x23 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 } +                } +            }, +            { { 1200 }, 1200, 21749, Ratio{1, 1}, 15, StaggerConfig{4, 0}, { +                    { 0x08, 0x02 }, { 0x09, 0x04 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, +                    { 0x16, 0xbf }, { 0x17, 0x08 }, { 0x18, 0x30 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0xc0 }, { 0x1d, 0x42 }, +                    { 0x52, 0x0b }, { 0x53, 0x0f }, { 0x54, 0x13 }, { 0x55, 0x17 }, +                    { 0x56, 0x03 }, { 0x57, 0x07 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x0e }, { 0x5d, 0x00 }, { 0x5e, 0x00 }                  }              },          }; @@ -796,7 +382,11 @@ void genesys_init_sensor_tables()          for (const CustomSensorSettings& setting : custom_settings)          {              sensor.resolutions = setting.resolutions; +            sensor.register_dpiset = setting.register_dpiset;              sensor.exposure_lperiod = setting.exposure_lperiod; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            sensor.stagger_y = setting.stagger_y;              sensor.custom_regs = setting.custom_regs;              s_sensors->push_back(sensor);          } @@ -804,168 +394,61 @@ void genesys_init_sensor_tables()      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_HP2300; -    sensor.optical_res = 600; -    sensor.ccd_size_divisor = 2; +    sensor.sensor_id = SensorId::CCD_HP2300; // gl646 +    sensor.full_resolution = 600;      sensor.black_pixels = 48;      sensor.dummy_pixel = 20; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 5368;      sensor.fau_gain_white_ref = 180;      sensor.gain_white_ref = 180;      sensor.exposure = { 0x0000, 0x0000, 0x0000 }; -    sensor.custom_base_regs = { -        { 0x08, 0x16 }, -        { 0x09, 0x00 }, -        { 0x0a, 0x01 }, -        { 0x0b, 0x03 }, -        { 0x16, 0xb7 }, -        { 0x17, 0x0a }, -        { 0x18, 0x20 }, -        { 0x19, 0x2a }, -        { 0x1a, 0x6a }, -        { 0x1b, 0x8a }, -        { 0x1c, 0x00 }, -        { 0x1d, 0x05 }, -        { 0x52, 0x0f }, -        { 0x53, 0x13 }, -        { 0x54, 0x17 }, -        { 0x55, 0x03 }, -        { 0x56, 0x07 }, -        { 0x57, 0x0b }, -        { 0x58, 0x83 }, -        { 0x59, 0x00 }, -        { 0x5a, 0xc1 }, -        { 0x5b, 0x06 }, -        { 0x5c, 0x0b }, -        { 0x5d, 0x10 }, -        { 0x5e, 0x16 }, -    };      sensor.gamma = { 2.1f, 2.1f, 2.1f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions; +            unsigned optical_resolution; +            unsigned register_dpiset;              unsigned exposure_lperiod; -            unsigned ccd_size_divisor; +            Ratio pixel_count_ratio; +            int output_pixel_offset;              GenesysRegisterSettingSet custom_regs;          };          CustomSensorSettings custom_settings[] = { -            { { 75 }, 4480, 2, { -                    { 0x08, 0x16 }, -                    { 0x09, 0x00 }, -                    { 0x0a, 0x01 }, -                    { 0x0b, 0x03 }, -                    { 0x16, 0xb7 }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x20 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x6a }, -                    { 0x1b, 0x8a }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x85 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x06 }, -                    { 0x5c, 0x0b }, -                    { 0x5d, 0x10 }, -                    { 0x5e, 0x16 } -                } -            }, -            { { 150 }, 4350, 2, { -                    { 0x08, 0x16 }, -                    { 0x09, 0x00 }, -                    { 0x0a, 0x01 }, -                    { 0x0b, 0x03 }, -                    { 0x16, 0xb7 }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x20 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x6a }, -                    { 0x1b, 0x8a }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x85 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x06 }, -                    { 0x5c, 0x0b }, -                    { 0x5d, 0x10 }, -                    { 0x5e, 0x16 } -                } -            }, -            { { 300 }, 4350, 2, { -                    { 0x08, 0x16 }, -                    { 0x09, 0x00 }, -                    { 0x0a, 0x01 }, -                    { 0x0b, 0x03 }, -                    { 0x16, 0xb7 }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x20 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x6a }, -                    { 0x1b, 0x8a }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x85 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x06 }, -                    { 0x5c, 0x0b }, -                    { 0x5d, 0x10 }, -                    { 0x5e, 0x16 } -                } -            }, -            { { 600 }, 8700, 1, { -                    { 0x08, 0x01 }, -                    { 0x09, 0x03 }, -                    { 0x0a, 0x04 }, -                    { 0x0b, 0x06 }, -                    { 0x16, 0xb7 }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x20 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x6a }, -                    { 0x1b, 0x8a }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x05 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x06 }, -                    { 0x5c, 0x0b }, -                    { 0x5d, 0x10 }, -                    { 0x5e, 0x16 } +            { { 75 }, 300, 150, 4480, Ratio{1, 2}, 2, { +                    { 0x08, 0x16 }, { 0x09, 0x00 }, { 0x0a, 0x01 }, { 0x0b, 0x03 }, +                    { 0x16, 0xb7 }, { 0x17, 0x0a }, { 0x18, 0x20 }, { 0x19, 0x2a }, +                    { 0x1a, 0x6a }, { 0x1b, 0x8a }, { 0x1c, 0x00 }, { 0x1d, 0x85 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x06 }, { 0x5c, 0x0b }, { 0x5d, 0x10 }, { 0x5e, 0x16 } +                } +            }, +            { { 150 }, 300, 300, 4350, Ratio{1, 2}, 5, { +                    { 0x08, 0x16 }, { 0x09, 0x00 }, { 0x0a, 0x01 }, { 0x0b, 0x03 }, +                    { 0x16, 0xb7 }, { 0x17, 0x0a }, { 0x18, 0x20 }, { 0x19, 0x2a }, +                    { 0x1a, 0x6a }, { 0x1b, 0x8a }, { 0x1c, 0x00 }, { 0x1d, 0x85 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x06 }, { 0x5c, 0x0b }, { 0x5d, 0x10 }, { 0x5e, 0x16 } +                } +            }, +            { { 300 }, 300, 600, 4350, Ratio{1, 2}, 10, { +                    { 0x08, 0x16 }, { 0x09, 0x00 }, { 0x0a, 0x01 }, { 0x0b, 0x03 }, +                    { 0x16, 0xb7 }, { 0x17, 0x0a }, { 0x18, 0x20 }, { 0x19, 0x2a }, +                    { 0x1a, 0x6a }, { 0x1b, 0x8a }, { 0x1c, 0x00 }, { 0x1d, 0x85 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x06 }, { 0x5c, 0x0b }, { 0x5d, 0x10 }, { 0x5e, 0x16 } +                } +            }, +            { { 600 }, 600, 600, 8700, Ratio{1, 1}, 20, { +                    { 0x08, 0x01 }, { 0x09, 0x03 }, { 0x0a, 0x04 }, { 0x0b, 0x06 }, +                    { 0x16, 0xb7 }, { 0x17, 0x0a }, { 0x18, 0x20 }, { 0x19, 0x2a }, +                    { 0x1a, 0x6a }, { 0x1b, 0x8a }, { 0x1c, 0x00 }, { 0x1d, 0x05 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x06 }, { 0x5c, 0x0b }, { 0x5d, 0x10 }, { 0x5e, 0x16 }                  }              },          }; @@ -973,8 +456,11 @@ void genesys_init_sensor_tables()          for (const CustomSensorSettings& setting : custom_settings)          {              sensor.resolutions = setting.resolutions; +            sensor.optical_resolution = setting.optical_resolution; +            sensor.register_dpiset = setting.register_dpiset;              sensor.exposure_lperiod = setting.exposure_lperiod; -            sensor.ccd_size_divisor = setting.ccd_size_divisor; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.output_pixel_offset = setting.output_pixel_offset;              sensor.custom_regs = setting.custom_regs;              s_sensors->push_back(sensor);          } @@ -982,399 +468,300 @@ void genesys_init_sensor_tables()      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CIS_CANON_LIDE_35; -    sensor.optical_res = 1200; -    sensor.ccd_size_divisor = 2; +    sensor.sensor_id = SensorId::CIS_CANON_LIDE_35; // gl841 +    sensor.full_resolution = 1200; +    sensor.register_dpihw = 1200;      sensor.black_pixels = 87;      sensor.dummy_pixel = 87; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 10400;      sensor.fau_gain_white_ref = 0;      sensor.gain_white_ref = 0;      sensor.exposure = { 0x0400, 0x0400, 0x0400 };      sensor.custom_regs = { -        { 0x08, 0x00 }, -        { 0x09, 0x00 }, -        { 0x0a, 0x00 }, -        { 0x0b, 0x00 }, -        { 0x16, 0x00 }, -        { 0x17, 0x02 }, -        { 0x18, 0x00 }, -        { 0x19, 0x50 }, -        { 0x1a, 0x00 }, // TODO: 1a-1d: these do no harm, but may be neccessery for CCD -        { 0x1b, 0x00 }, -        { 0x1c, 0x00 }, -        { 0x1d, 0x02 }, -        { 0x52, 0x05 }, // [GB](HI|LOW) not needed for cis -        { 0x53, 0x07 }, -        { 0x54, 0x00 }, -        { 0x55, 0x00 }, -        { 0x56, 0x00 }, -        { 0x57, 0x00 }, -        { 0x58, 0x3a }, -        { 0x59, 0x03 }, -        { 0x5a, 0x40 }, -        { 0x5b, 0x00 }, // TODO: 5b-5e -        { 0x5c, 0x00 }, -        { 0x5d, 0x00 }, -        { 0x5e, 0x00 }, +        { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x00 }, { 0x19, 0x50 }, +        { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, +        { 0x52, 0x05 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +        { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x3a }, { 0x59, 0x03 }, { 0x5a, 0x40 }, +        { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 },      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi; -    s_sensors->push_back(sensor); +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned optical_resolution; +            unsigned register_dpiset; +            unsigned shading_resolution; +            int output_pixel_offset; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 600, 150, 600, 11 }, +            { { 100 }, 600, 200, 600, 14 }, +            { { 150 }, 600, 300, 600, 22 }, +            { { 200 }, 600, 400, 600, 29 }, +            { { 300 }, 600, 600, 600, 44 }, +            { { 600 }, 600, 1200, 600, 88 }, +            { { 1200 }, 1200, 1200, 1200, 88 }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.optical_resolution = setting.optical_resolution; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.shading_resolution = setting.shading_resolution; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            s_sensors->push_back(sensor); +        } +    }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CIS_XP200; -    sensor.optical_res = 600; +    sensor.sensor_id = SensorId::CIS_CANON_LIDE_60; // gl841 +    sensor.full_resolution = 1200; +    sensor.register_dpihw = 1200; +    sensor.black_pixels = 87; +    sensor.dummy_pixel = 87; +    sensor.fau_gain_white_ref = 0; +    sensor.gain_white_ref = 0; +    sensor.exposure = { 0x0400, 0x0400, 0x0400 }; +    sensor.custom_regs = { +        { 0x16, 0x00 }, { 0x17, 0x01 }, { 0x18, 0x00 }, { 0x19, 0x50 }, +        { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x02 }, +        { 0x52, 0x05 }, { 0x53, 0x07 }, { 0x54, 0x03 }, { 0x55, 0x05 }, +        { 0x56, 0x02 }, { 0x57, 0x05 }, { 0x58, 0x3a }, { 0x59, 0x03 }, { 0x5a, 0x40 }, +        { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 }, +    }; +    sensor.gamma = { 1.0f, 1.0f, 1.0f }; +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned optical_resolution; +            unsigned register_dpiset; +            unsigned shading_resolution; +            int output_pixel_offset; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 600, 150, 600, 11 }, +            { { 100 }, 600, 200, 600, 14 }, +            { { 150 }, 600, 300, 600, 22 }, +            { { 200 }, 600, 400, 600, 29 }, +            { { 300 }, 600, 600, 600, 44 }, +            { { 600 }, 600, 1200, 600, 88 }, +            { { 1200 }, 1200, 1200, 1200, 88 }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.optical_resolution = setting.optical_resolution; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.shading_resolution = setting.shading_resolution; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            s_sensors->push_back(sensor); +        } +    } + + +    sensor = Genesys_Sensor(); +    sensor.sensor_id = SensorId::CIS_CANON_LIDE_90; // gl842 +    sensor.full_resolution = 2400; +    sensor.black_pixels = 20; +    sensor.dummy_pixel = 253; +    sensor.fau_gain_white_ref = 150; +    sensor.gain_white_ref = 150; +    sensor.use_host_side_calib = true; +    sensor.custom_regs = { +        { 0x16, 0x20 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0xff }, +        { 0x1a, 0x24 }, { 0x1c, 0x00 }, { 0x1d, 0x04 }, +        { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x02 }, { 0x55, 0x04 }, +        { 0x56, 0x02 }, { 0x57, 0x04 }, { 0x58, 0x0a }, { 0x59, 0x71 }, { 0x5a, 0x55 }, +        { 0x70, 0x00 }, { 0x71, 0x05 }, { 0x72, 0x07 }, { 0x73, 0x09 }, +        { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff }, +        { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x3f }, +        { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x1e }, { 0x7d, 0x11 }, { 0x7f, 0x50 } +    }; +    sensor.gamma = { 1.0f, 1.0f, 1.0f }; +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned optical_resolution; +            unsigned register_dpihw; +            unsigned register_dpiset; +            unsigned shading_resolution; +            unsigned shading_factor; +            int output_pixel_offset; +            SensorExposure exposure; +            unsigned exposure_lperiod; +            unsigned segment_size; +            std::vector<unsigned> segment_order; +        }; + +        CustomSensorSettings custom_settings[] = { +            {   { 300 }, 300, 600, 600, 300, 2, 280, { 955, 1235, 675 }, 6500, 5152, +                std::vector<unsigned>{} }, +            {   { 600 }, 600, 600, 600, 600, 1, 250, { 1655, 2075, 1095 }, 6536, 5152, +                std::vector<unsigned>{} }, +            {   { 1200 }, 1200, 1200, 1200, 1200, 1, 500, { 3055, 4175, 1935 }, 12688, 5152, +                {0, 1} }, +            {   { 2400 }, 2400, 2400, 2400, 2400, 1, 1000, { 5855, 7535, 3615 }, 21500, 5152, +                {0, 1, 2, 3} }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.optical_resolution = setting.optical_resolution; +            sensor.register_dpihw = setting.register_dpihw; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.shading_resolution = setting.shading_resolution; +            sensor.shading_factor = setting.shading_factor; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            sensor.exposure = setting.exposure; +            sensor.exposure_lperiod = setting.exposure_lperiod; +            sensor.segment_size = setting.segment_size; +            sensor.segment_order = setting.segment_order; +            s_sensors->push_back(sensor); +        } +    } + + +    sensor = Genesys_Sensor(); +    sensor.sensor_id = SensorId::CIS_XP200; // gl646 +    sensor.full_resolution = 600;      sensor.black_pixels = 5;      sensor.dummy_pixel = 38; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 5200;      sensor.fau_gain_white_ref = 200;      sensor.gain_white_ref = 200;      sensor.exposure = { 0x1450, 0x0c80, 0x0a28 }; -    sensor.custom_base_regs = { -        { 0x08, 0x16 }, -        { 0x09, 0x00 }, -        { 0x0a, 0x01 }, -        { 0x0b, 0x03 }, -        { 0x16, 0xb7 }, -        { 0x17, 0x0a }, -        { 0x18, 0x20 }, -        { 0x19, 0x2a }, -        { 0x1a, 0x6a }, -        { 0x1b, 0x8a }, -        { 0x1c, 0x00 }, -        { 0x1d, 0x05 }, -        { 0x52, 0x0f }, -        { 0x53, 0x13 }, -        { 0x54, 0x17 }, -        { 0x55, 0x03 }, -        { 0x56, 0x07 }, -        { 0x57, 0x0b }, -        { 0x58, 0x83 }, -        { 0x59, 0x00 }, -        { 0x5a, 0xc1 }, -        { 0x5b, 0x06 }, -        { 0x5c, 0x0b }, -        { 0x5d, 0x10 }, -        { 0x5e, 0x16 }, -    };      sensor.custom_regs = { -        { 0x08, 0x06 }, -        { 0x09, 0x07 }, -        { 0x0a, 0x0a }, -        { 0x0b, 0x04 }, -        { 0x16, 0x24 }, -        { 0x17, 0x04 }, -        { 0x18, 0x00 }, -        { 0x19, 0x2a }, -        { 0x1a, 0x0a }, -        { 0x1b, 0x0a }, -        { 0x1c, 0x00 }, -        { 0x1d, 0x11 }, -        { 0x52, 0x08 }, -        { 0x53, 0x02 }, -        { 0x54, 0x00 }, -        { 0x55, 0x00 }, -        { 0x56, 0x00 }, -        { 0x57, 0x00 }, -        { 0x58, 0x1a }, -        { 0x59, 0x51 }, -        { 0x5a, 0x00 }, -        { 0x5b, 0x00 }, -        { 0x5c, 0x00 }, -        { 0x5d, 0x00 }, -        { 0x5e, 0x00 } +        { 0x08, 0x06 }, { 0x09, 0x07 }, { 0x0a, 0x0a }, { 0x0b, 0x04 }, +        { 0x16, 0x24 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x2a }, +        { 0x1a, 0x0a }, { 0x1b, 0x0a }, { 0x1c, 0x00 }, { 0x1d, 0x11 }, +        { 0x52, 0x08 }, { 0x53, 0x02 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +        { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x1a }, { 0x59, 0x51 }, { 0x5a, 0x00 }, +        { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 }      };      sensor.gamma = { 2.1f, 2.1f, 2.1f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions;              std::vector<unsigned> channels;              unsigned exposure_lperiod;              SensorExposure exposure; +            int output_pixel_offset;          };          CustomSensorSettings custom_settings[] = { -            {  { 75 }, { 3 },  5700, { 0x1644, 0x0c80, 0x092e } }, -            { { 100 }, { 3 },  5700, { 0x1644, 0x0c80, 0x092e } }, -            { { 200 }, { 3 },  5700, { 0x1644, 0x0c80, 0x092e } }, -            { { 300 }, { 3 },  9000, { 0x1644, 0x0c80, 0x092e } }, -            { { 600 }, { 3 }, 16000, { 0x1644, 0x0c80, 0x092e } }, -            {  { 75 }, { 1 }, 16000, { 0x050a, 0x0fa0, 0x1010 } }, -            { { 100 }, { 1 },  7800, { 0x050a, 0x0fa0, 0x1010 } }, -            { { 200 }, { 1 }, 11000, { 0x050a, 0x0fa0, 0x1010 } }, -            { { 300 }, { 1 }, 13000, { 0x050a, 0x0fa0, 0x1010 } }, -            { { 600 }, { 1 }, 24000, { 0x050a, 0x0fa0, 0x1010 } }, +            {  { 75 }, { 3 },  5700, { 0x1644, 0x0c80, 0x092e }, 4 }, +            { { 100 }, { 3 },  5700, { 0x1644, 0x0c80, 0x092e }, 6 }, +            { { 200 }, { 3 },  5700, { 0x1644, 0x0c80, 0x092e }, 12 }, +            { { 300 }, { 3 },  9000, { 0x1644, 0x0c80, 0x092e }, 19 }, +            { { 600 }, { 3 }, 16000, { 0x1644, 0x0c80, 0x092e }, 38 }, +            {  { 75 }, { 1 }, 16000, { 0x050a, 0x0fa0, 0x1010 }, 4 }, +            { { 100 }, { 1 },  7800, { 0x050a, 0x0fa0, 0x1010 }, 6 }, +            { { 200 }, { 1 }, 11000, { 0x050a, 0x0fa0, 0x1010 }, 12 }, +            { { 300 }, { 1 }, 13000, { 0x050a, 0x0fa0, 0x1010 }, 19 }, +            { { 600 }, { 1 }, 24000, { 0x050a, 0x0fa0, 0x1010 }, 38 },          };          for (const CustomSensorSettings& setting : custom_settings)          {              sensor.resolutions = setting.resolutions;              sensor.channels = setting.channels; +            sensor.register_dpiset = setting.resolutions.values()[0];              sensor.exposure_lperiod = setting.exposure_lperiod;              sensor.exposure = setting.exposure; +            sensor.output_pixel_offset = setting.output_pixel_offset;              s_sensors->push_back(sensor);          }      }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_HP3670; -    sensor.optical_res = 1200; +    sensor.sensor_id = SensorId::CCD_HP3670; // gl646 +    sensor.full_resolution = 1200;      sensor.black_pixels = 48;      sensor.dummy_pixel = 16; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 10872;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.exposure = { 0, 0, 0 }; -    sensor.stagger_config = StaggerConfig{1200, 4}; // FIXME: may be incorrect -    sensor.custom_base_regs = { -        { 0x08, 0x00 }, -        { 0x09, 0x0a }, -        { 0x0a, 0x0b }, -        { 0x0b, 0x0d }, -        { 0x16, 0x33 }, -        { 0x17, 0x07 }, -        { 0x18, 0x20 }, -        { 0x19, 0x2a }, -        { 0x1a, 0x00 }, -        { 0x1b, 0x00 }, -        { 0x1c, 0xc0 }, -        { 0x1d, 0x43 }, -        { 0x52, 0x0f }, -        { 0x53, 0x13 }, -        { 0x54, 0x17 }, -        { 0x55, 0x03 }, -        { 0x56, 0x07 }, -        { 0x57, 0x0b }, -        { 0x58, 0x83 }, -        { 0x59, 0x00 }, -        { 0x5a, 0x15 }, -        { 0x5b, 0x05 }, -        { 0x5c, 0x0a }, -        { 0x5d, 0x0f }, -        { 0x5e, 0x00 }, -    };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpiset;              unsigned exposure_lperiod; +            Ratio pixel_count_ratio; +            int output_pixel_offset; +            StaggerConfig stagger_y;              GenesysRegisterSettingSet custom_regs;          };          CustomSensorSettings custom_settings[] = { -            { { 50 }, 5758, { -                    { 0x08, 0x00 }, -                    { 0x09, 0x0a }, -                    { 0x0a, 0x0b }, -                    { 0x0b, 0x0d }, -                    { 0x16, 0x33 }, -                    { 0x17, 0x07 }, -                    { 0x18, 0x33 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x02 }, -                    { 0x1b, 0x13 }, -                    { 0x1c, 0xc0 }, -                    { 0x1d, 0x43 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x15 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x05 }, -                    { 0x5c, 0x0a }, -                    { 0x5d, 0x0f }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 75 }, 4879, { -                    { 0x08, 0x00 }, -                    { 0x09, 0x0a }, -                    { 0x0a, 0x0b }, -                    { 0x0b, 0x0d }, -                    { 0x16, 0x33 }, -                    { 0x17, 0x07 }, -                    { 0x18, 0x33 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x02 }, -                    { 0x1b, 0x13 }, -                    { 0x1c, 0xc0 }, -                    { 0x1d, 0x43 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x15 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x05 }, -                    { 0x5c, 0x0a }, -                    { 0x5d, 0x0f }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 100 }, 4487, { -                    { 0x08, 0x00 }, -                    { 0x09, 0x0a }, -                    { 0x0a, 0x0b }, -                    { 0x0b, 0x0d }, -                    { 0x16, 0x33 }, -                    { 0x17, 0x07 }, -                    { 0x18, 0x33 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x02 }, -                    { 0x1b, 0x13 }, -                    { 0x1c, 0xc0 }, -                    { 0x1d, 0x43 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x15 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x05 }, -                    { 0x5c, 0x0a }, -                    { 0x5d, 0x0f }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 150 }, 4879, { -                    { 0x08, 0x00 }, -                    { 0x09, 0x0a }, -                    { 0x0a, 0x0b }, -                    { 0x0b, 0x0d }, -                    { 0x16, 0x33 }, -                    { 0x17, 0x07 }, -                    { 0x18, 0x33 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x02 }, -                    { 0x1b, 0x13 }, -                    { 0x1c, 0xc0 }, -                    { 0x1d, 0x43 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x15 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x05 }, -                    { 0x5c, 0x0a }, -                    { 0x5d, 0x0f }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 300 }, 4503, { -                    { 0x08, 0x00 }, -                    { 0x09, 0x0a }, -                    { 0x0a, 0x0b }, -                    { 0x0b, 0x0d }, -                    { 0x16, 0x33 }, -                    { 0x17, 0x07 }, -                    { 0x18, 0x33 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x02 }, -                    { 0x1b, 0x13 }, -                    { 0x1c, 0xc0 }, -                    { 0x1d, 0x43 }, -                    { 0x52, 0x0f }, -                    { 0x53, 0x13 }, -                    { 0x54, 0x17 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x83 }, -                    { 0x59, 0x15 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x05 }, -                    { 0x5c, 0x0a }, -                    { 0x5d, 0x0f }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 600 }, 10251, { -                    { 0x08, 0x00 }, -                    { 0x09, 0x05 }, -                    { 0x0a, 0x06 }, -                    { 0x0b, 0x08 }, -                    { 0x16, 0x33 }, -                    { 0x17, 0x07 }, -                    { 0x18, 0x31 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x02 }, -                    { 0x1b, 0x0e }, -                    { 0x1c, 0xc0 }, -                    { 0x1d, 0x43 }, -                    { 0x52, 0x0b }, -                    { 0x53, 0x0f }, -                    { 0x54, 0x13 }, -                    { 0x55, 0x17 }, -                    { 0x56, 0x03 }, -                    { 0x57, 0x07 }, -                    { 0x58, 0x63 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x02 }, -                    { 0x5c, 0x0e }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } -                } -            }, -            { { 1200 }, 12750, { -                    { 0x08, 0x0d }, -                    { 0x09, 0x0f }, -                    { 0x0a, 0x11 }, -                    { 0x0b, 0x13 }, -                    { 0x16, 0x2b }, -                    { 0x17, 0x07 }, -                    { 0x18, 0x30 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x00 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0xc0 }, -                    { 0x1d, 0x43 }, -                    { 0x52, 0x03 }, -                    { 0x53, 0x07 }, -                    { 0x54, 0x0b }, -                    { 0x55, 0x0f }, -                    { 0x56, 0x13 }, -                    { 0x57, 0x17 }, -                    { 0x58, 0x23 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc1 }, -                    { 0x5b, 0x00 }, -                    { 0x5c, 0x00 }, -                    { 0x5d, 0x00 }, -                    { 0x5e, 0x00 } +            {   { 50 }, 200, 5758, Ratio{1, 4}, 0, StaggerConfig{}, { +                    { 0x08, 0x00 }, { 0x09, 0x0a }, { 0x0a, 0x0b }, { 0x0b, 0x0d }, +                    { 0x16, 0x33 }, { 0x17, 0x07 }, { 0x18, 0x33 }, { 0x19, 0x2a }, +                    { 0x1a, 0x02 }, { 0x1b, 0x13 }, { 0x1c, 0xc0 }, { 0x1d, 0x43 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x15 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x05 }, { 0x5c, 0x0a }, { 0x5d, 0x0f }, { 0x5e, 0x00 } +                } +            }, +            {   { 75 }, 300, 4879, Ratio{1, 4}, 1, StaggerConfig{}, { +                    { 0x08, 0x00 }, { 0x09, 0x0a }, { 0x0a, 0x0b }, { 0x0b, 0x0d }, +                    { 0x16, 0x33 }, { 0x17, 0x07 }, { 0x18, 0x33 }, { 0x19, 0x2a }, +                    { 0x1a, 0x02 }, { 0x1b, 0x13 }, { 0x1c, 0xc0 }, { 0x1d, 0x43 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x15 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x05 }, { 0x5c, 0x0a }, { 0x5d, 0x0f }, { 0x5e, 0x00 } +                } +            }, +            {   { 100 }, 400, 4487, Ratio{1, 4}, 1, StaggerConfig{}, { +                    { 0x08, 0x00 }, { 0x09, 0x0a }, { 0x0a, 0x0b }, { 0x0b, 0x0d }, +                    { 0x16, 0x33 }, { 0x17, 0x07 }, { 0x18, 0x33 }, { 0x19, 0x2a }, +                    { 0x1a, 0x02 }, { 0x1b, 0x13 }, { 0x1c, 0xc0 }, { 0x1d, 0x43 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x15 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x05 }, { 0x5c, 0x0a }, { 0x5d, 0x0f }, { 0x5e, 0x00 } +                } +            }, +            {   { 150 }, 600, 4879, Ratio{1, 4}, 2, StaggerConfig{}, { +                    { 0x08, 0x00 }, { 0x09, 0x0a }, { 0x0a, 0x0b }, { 0x0b, 0x0d }, +                    { 0x16, 0x33 }, { 0x17, 0x07 }, { 0x18, 0x33 }, { 0x19, 0x2a }, +                    { 0x1a, 0x02 }, { 0x1b, 0x13 }, { 0x1c, 0xc0 }, { 0x1d, 0x43 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x15 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x05 }, { 0x5c, 0x0a }, { 0x5d, 0x0f }, { 0x5e, 0x00 } +                } +            }, +            {   { 300 }, 1200, 4503, Ratio{1, 4}, 4, StaggerConfig{}, { +                    { 0x08, 0x00 }, { 0x09, 0x0a }, { 0x0a, 0x0b }, { 0x0b, 0x0d }, +                    { 0x16, 0x33 }, { 0x17, 0x07 }, { 0x18, 0x33 }, { 0x19, 0x2a }, +                    { 0x1a, 0x02 }, { 0x1b, 0x13 }, { 0x1c, 0xc0 }, { 0x1d, 0x43 }, +                    { 0x52, 0x0f }, { 0x53, 0x13 }, { 0x54, 0x17 }, { 0x55, 0x03 }, +                    { 0x56, 0x07 }, { 0x57, 0x0b }, { 0x58, 0x83 }, { 0x59, 0x15 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x05 }, { 0x5c, 0x0a }, { 0x5d, 0x0f }, { 0x5e, 0x00 } +                } +            }, +            {   { 600 }, 1200, 10251, Ratio{1, 2}, 8, StaggerConfig{}, { +                    { 0x08, 0x00 }, { 0x09, 0x05 }, { 0x0a, 0x06 }, { 0x0b, 0x08 }, +                    { 0x16, 0x33 }, { 0x17, 0x07 }, { 0x18, 0x31 }, { 0x19, 0x2a }, +                    { 0x1a, 0x02 }, { 0x1b, 0x0e }, { 0x1c, 0xc0 }, { 0x1d, 0x43 }, +                    { 0x52, 0x0b }, { 0x53, 0x0f }, { 0x54, 0x13 }, { 0x55, 0x17 }, +                    { 0x56, 0x03 }, { 0x57, 0x07 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x02 }, { 0x5c, 0x0e }, { 0x5d, 0x00 }, { 0x5e, 0x00 } +                } +            }, +            {   { 1200 }, 1200, 12750, Ratio{1, 1}, 16, StaggerConfig{4, 0}, { +                    { 0x08, 0x0d }, { 0x09, 0x0f }, { 0x0a, 0x11 }, { 0x0b, 0x13 }, +                    { 0x16, 0x2b }, { 0x17, 0x07 }, { 0x18, 0x30 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0xc0 }, { 0x1d, 0x43 }, +                    { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x0b }, { 0x55, 0x0f }, +                    { 0x56, 0x13 }, { 0x57, 0x17 }, { 0x58, 0x23 }, { 0x59, 0x00 }, { 0x5a, 0xc1 }, +                    { 0x5b, 0x00 }, { 0x5c, 0x00 }, { 0x5d, 0x00 }, { 0x5e, 0x00 }                  }              },          }; @@ -1382,7 +769,11 @@ void genesys_init_sensor_tables()          for (const CustomSensorSettings& setting : custom_settings)          {              sensor.resolutions = setting.resolutions; +            sensor.register_dpiset = setting.register_dpiset;              sensor.exposure_lperiod = setting.exposure_lperiod; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            sensor.stagger_y = setting.stagger_y;              sensor.custom_regs = setting.custom_regs;              s_sensors->push_back(sensor);          } @@ -1390,251 +781,278 @@ void genesys_init_sensor_tables()      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_DP665; -    sensor.optical_res = 600; +    sensor.sensor_id = SensorId::CCD_DP665; // gl841 +    sensor.full_resolution = 600; +    sensor.register_dpihw = 600; +    sensor.shading_resolution = 600;      sensor.black_pixels = 27;      sensor.dummy_pixel = 27; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 2496;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.exposure = { 0x1100, 0x1100, 0x1100 };      sensor.custom_regs = { -        { 0x08, 0x00 }, -        { 0x09, 0x00 }, -        { 0x0a, 0x00 }, -        { 0x0b, 0x00 }, -        { 0x16, 0x00 }, -        { 0x17, 0x02 }, -        { 0x18, 0x04 }, -        { 0x19, 0x50 }, -        { 0x1a, 0x10 }, -        { 0x1b, 0x00 }, -        { 0x1c, 0x20 }, -        { 0x1d, 0x02 }, -        { 0x52, 0x04 }, // [GB](HI|LOW) not needed for cis -        { 0x53, 0x05 }, -        { 0x54, 0x00 }, -        { 0x55, 0x00 }, -        { 0x56, 0x00 }, -        { 0x57, 0x00 }, -        { 0x58, 0x54 }, -        { 0x59, 0x03 }, -        { 0x5a, 0x00 }, -        { 0x5b, 0x00 }, // TODO: 5b-5e -        { 0x5c, 0x00 }, -        { 0x5d, 0x00 }, -        { 0x5e, 0x01 }, +        { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, +        { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, +        { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +        { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, +        { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 },      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi; -    s_sensors->push_back(sensor); +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpiset; +            int output_pixel_offset; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 75, 1 }, +            { { 150 }, 150, 3 }, +            { { 300 }, 300, 7 }, +            { { 600 }, 600, 14 }, +            { { 1200 }, 1200, 28 }, +        }; +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            s_sensors->push_back(sensor); +        } +    }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_ROADWARRIOR; -    sensor.optical_res = 600; +    sensor.sensor_id = SensorId::CCD_ROADWARRIOR; // gl841 +    sensor.full_resolution = 600; +    sensor.register_dpihw = 600; +    sensor.shading_resolution = 600;      sensor.black_pixels = 27;      sensor.dummy_pixel = 27; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 5200;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.exposure = { 0x1100, 0x1100, 0x1100 };      sensor.custom_regs = { -        { 0x08, 0x00 }, -        { 0x09, 0x00 }, -        { 0x0a, 0x00 }, -        { 0x0b, 0x00 }, -        { 0x16, 0x00 }, -        { 0x17, 0x02 }, -        { 0x18, 0x04 }, -        { 0x19, 0x50 }, -        { 0x1a, 0x10 }, -        { 0x1b, 0x00 }, -        { 0x1c, 0x20 }, -        { 0x1d, 0x02 }, -        { 0x52, 0x04 }, // [GB](HI|LOW) not needed for cis -        { 0x53, 0x05 }, -        { 0x54, 0x00 }, -        { 0x55, 0x00 }, -        { 0x56, 0x00 }, -        { 0x57, 0x00 }, -        { 0x58, 0x54 }, -        { 0x59, 0x03 }, -        { 0x5a, 0x00 }, -        { 0x5b, 0x00 }, // TODO: 5b-5e -        { 0x5c, 0x00 }, -        { 0x5d, 0x00 }, -        { 0x5e, 0x01 }, +        { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, +        { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, +        { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +        { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, +        { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 },      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi; -    s_sensors->push_back(sensor); +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpiset; +            int output_pixel_offset; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 75, 1 }, +            { { 150 }, 150, 3 }, +            { { 300 }, 300, 7 }, +            { { 600 }, 600, 14 }, +            { { 1200 }, 1200, 28 }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            s_sensors->push_back(sensor); +        } +    }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_DSMOBILE600; -    sensor.optical_res = 600; +    sensor.sensor_id = SensorId::CCD_DSMOBILE600; // gl841 +    sensor.full_resolution = 600; +    sensor.register_dpihw = 600; +    sensor.shading_resolution = 600;      sensor.black_pixels = 28;      sensor.dummy_pixel = 28; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 5200;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.exposure = { 0x1544, 0x1544, 0x1544 };      sensor.custom_regs = { -        { 0x08, 0x00 }, -        { 0x09, 0x00 }, -        { 0x0a, 0x00 }, -        { 0x0b, 0x00 }, -        { 0x16, 0x00 }, -        { 0x17, 0x02 }, -        { 0x18, 0x04 }, -        { 0x19, 0x50 }, -        { 0x1a, 0x10 }, -        { 0x1b, 0x00 }, -        { 0x1c, 0x20 }, -        { 0x1d, 0x02 }, -        { 0x52, 0x04 }, // [GB](HI|LOW) not needed for cis -        { 0x53, 0x05 }, -        { 0x54, 0x00 }, -        { 0x55, 0x00 }, -        { 0x56, 0x00 }, -        { 0x57, 0x00 }, -        { 0x58, 0x54 }, -        { 0x59, 0x03 }, -        { 0x5a, 0x00 }, -        { 0x5b, 0x00 }, // TODO: 5b-5e -        { 0x5c, 0x00 }, -        { 0x5d, 0x00 }, -        { 0x5e, 0x01 }, +        { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, +        { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, +        { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +        { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, +        { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 }, +    }; +    sensor.gamma = { 1.0f, 1.0f, 1.0f }; +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpiset; +            int output_pixel_offset; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 75, 3 }, +            { { 150 }, 150, 7 }, +            { { 300 }, 300, 14 }, +            { { 600 }, 600, 29 }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            s_sensors->push_back(sensor); +        } +    } + + +    sensor = Genesys_Sensor(); +    sensor.sensor_id = SensorId::CCD_XP300; // gl841 +    sensor.full_resolution = 600; +    sensor.register_dpihw = 1200; // FIXME: could be incorrect, but previous code used this value +    sensor.shading_resolution = 600; +    sensor.black_pixels = 27; +    sensor.dummy_pixel = 27; +    sensor.fau_gain_white_ref = 210; +    sensor.gain_white_ref = 200; +    sensor.exposure = { 0x1100, 0x1100, 0x1100 }; +    sensor.custom_regs = { +        { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, +        { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, +        { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +        { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, +        { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 },      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi; -    s_sensors->push_back(sensor); +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpiset; +            int output_pixel_offset; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 150, 3 }, +            { { 150 }, 300, 7 }, +            { { 300 }, 600, 14 }, +            { { 600 }, 1200, 28 }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            s_sensors->push_back(sensor); +        } +    }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_XP300; -    sensor.optical_res = 600; +    sensor.sensor_id = SensorId::CCD_DOCKETPORT_487; // gl841 +    sensor.full_resolution = 600; +    sensor.register_dpihw = 600; +    sensor.shading_resolution = 600;      sensor.black_pixels = 27;      sensor.dummy_pixel = 27; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 10240;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.exposure = { 0x1100, 0x1100, 0x1100 };      sensor.custom_regs = { -        { 0x08, 0x00 }, -        { 0x09, 0x00 }, -        { 0x0a, 0x00 }, -        { 0x0b, 0x00 }, -        { 0x16, 0x00 }, -        { 0x17, 0x02 }, -        { 0x18, 0x04 }, -        { 0x19, 0x50 }, -        { 0x1a, 0x10 }, -        { 0x1b, 0x00 }, -        { 0x1c, 0x20 }, -        { 0x1d, 0x02 }, -        { 0x52, 0x04 }, // [GB](HI|LOW) not needed for cis -        { 0x53, 0x05 }, -        { 0x54, 0x00 }, -        { 0x55, 0x00 }, -        { 0x56, 0x00 }, -        { 0x57, 0x00 }, -        { 0x58, 0x54 }, -        { 0x59, 0x03 }, -        { 0x5a, 0x00 }, -        { 0x5b, 0x00 }, // TODO: 5b-5e -        { 0x5c, 0x00 }, -        { 0x5d, 0x00 }, -        { 0x5e, 0x01 }, +        { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, +        { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, +        { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +        { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, +        { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 },      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi; -    s_sensors->push_back(sensor); +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpiset; +            int output_pixel_offset; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 150, 3 }, +            { { 150 }, 300, 7 }, +            { { 300 }, 600, 14 }, +            { { 600 }, 600, 28 }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            s_sensors->push_back(sensor); +        } +    }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_DP685; -    sensor.optical_res = 600; +    sensor.sensor_id = SensorId::CCD_DP685; // gl841 +    sensor.full_resolution = 600; +    sensor.register_dpihw = 600; +    sensor.shading_resolution = 600; +    sensor.full_resolution = 600;      sensor.black_pixels = 27;      sensor.dummy_pixel = 27; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 5020;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.exposure = { 0x1100, 0x1100, 0x1100 };      sensor.custom_regs = { -        { 0x08, 0x00 }, -        { 0x09, 0x00 }, -        { 0x0a, 0x00 }, -        { 0x0b, 0x00 }, -        { 0x16, 0x00 }, -        { 0x17, 0x02 }, -        { 0x18, 0x04 }, -        { 0x19, 0x50 }, -        { 0x1a, 0x10 }, -        { 0x1b, 0x00 }, -        { 0x1c, 0x20 }, -        { 0x1d, 0x02 }, -        { 0x52, 0x04 }, // [GB](HI|LOW) not needed for cis -        { 0x53, 0x05 }, -        { 0x54, 0x00 }, -        { 0x55, 0x00 }, -        { 0x56, 0x00 }, -        { 0x57, 0x00 }, -        { 0x58, 0x54 }, -        { 0x59, 0x03 }, -        { 0x5a, 0x00 }, -        { 0x5b, 0x00 }, // TODO: 5b-5e -        { 0x5c, 0x00 }, -        { 0x5d, 0x00 }, -        { 0x5e, 0x01 }, +        { 0x16, 0x00 }, { 0x17, 0x02 }, { 0x18, 0x04 }, { 0x19, 0x50 }, +        { 0x1a, 0x10 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x02 }, +        { 0x52, 0x04 }, { 0x53, 0x05 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +        { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x54 }, { 0x59, 0x03 }, { 0x5a, 0x00 }, +        { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 },      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi; -    s_sensors->push_back(sensor); +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpiset; +            int output_pixel_offset; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 75, 3 }, +            { { 150 }, 150, 6 }, +            { { 300 }, 300, 13 }, +            { { 600 }, 600, 27 }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            s_sensors->push_back(sensor); +        } +    }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CIS_CANON_LIDE_200; -    sensor.optical_res = 4800; +    sensor.sensor_id = SensorId::CIS_CANON_LIDE_200; // gl847 +    sensor.full_resolution = 4800;      sensor.black_pixels = 87*4;      sensor.dummy_pixel = 16*4; -    sensor.ccd_start_xoffset = 320*8; -    sensor.sensor_pixels = 5136*8;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.exposure = { 0x0000, 0x0000, 0x0000 };      sensor.gamma = { 2.2f, 2.2f, 2.2f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpihw;              int exposure_lperiod;              SensorExposure exposure; +            Ratio pixel_count_ratio; +            unsigned shading_factor; +            int output_pixel_offset;              unsigned segment_size;              std::vector<unsigned> segment_order;              GenesysRegisterSettingSet custom_regs; @@ -1642,7 +1060,44 @@ void genesys_init_sensor_tables()          CustomSensorSettings custom_settings[] = {              // Note: Windows driver uses 1424 lperiod and enables dummy line (0x17) -            {   { 75, 100, 150, 200 }, 2848, { 304, 203, 180 }, 5136, std::vector<unsigned>{}, { +            {   { 75 }, 600, 2848, { 304, 203, 180 }, Ratio{1, 8}, 8, 40, 5136, +                std::vector<unsigned>{}, { +                    { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, +                    { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, +                    { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +                    { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                } +            }, +            // Note: Windows driver uses 1424 lperiod and enables dummy line (0x17) +            {   { 100 }, 600, 2848, { 304, 203, 180 }, Ratio{1, 8}, 6, 53, 5136, +                std::vector<unsigned>{}, { +                    { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, +                    { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, +                    { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +                    { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                } +            }, +            // Note: Windows driver uses 1424 lperiod and enables dummy line (0x17) +            {   { 150 }, 600, 2848, { 304, 203, 180 }, Ratio{1, 8}, 4, 80, 5136, +                std::vector<unsigned>{}, { +                    { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, +                    { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, +                    { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +                    { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                } +            }, +            // Note: Windows driver uses 1424 lperiod and enables dummy line (0x17) +            {   { 200 }, 600, 2848, { 304, 203, 180 }, Ratio{1, 8}, 3, 106, 5136, +                std::vector<unsigned>{}, {                      { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1653,7 +1108,8 @@ void genesys_init_sensor_tables()                  }              },              // Note: Windows driver uses 788 lperiod and enables dummy line (0x17) -            {   { 300, 400 }, 1424, { 304, 203, 180 }, 5136, std::vector<unsigned>{}, { +            {   { 300 }, 600, 1424, { 304, 203, 180 }, Ratio{1, 8}, 2, 160, 5136, +                std::vector<unsigned>{}, {                      { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1663,7 +1119,9 @@ void genesys_init_sensor_tables()                      { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },                  }              }, -            {   { 600 }, 1432, { 492, 326, 296 }, 5136, std::vector<unsigned>{}, { +            // Note: Windows driver uses 788 lperiod and enables dummy line (0x17) +            {   { 400 }, 600, 1424, { 304, 203, 180 }, Ratio{1, 8}, 1, 213, 5136, +                std::vector<unsigned>{}, {                      { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1673,7 +1131,19 @@ void genesys_init_sensor_tables()                      { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },                  }              }, -            {   { 1200 }, 2712, { 935, 592, 538 }, 5136, { 0, 1 }, { +            {   { 600 }, 600, 1432, { 492, 326, 296 }, Ratio{1, 8}, 1, 320, 5136, +                std::vector<unsigned>{}, { +                    { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, +                    { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, +                    { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +                    { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                } +            }, +            {   { 1200 }, 1200, 2712, { 935, 592, 538 }, Ratio{1, 8}, 1, 640, 5136, +                { 0, 1 }, {                      { 0x16, 0x10 }, { 0x17, 0x08 }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1683,7 +1153,8 @@ void genesys_init_sensor_tables()                      { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },                  }              }, -            {   { 2400 }, 5280, { 1777, 1125, 979 }, 5136, { 0, 2, 1, 3 }, { +            {   { 2400 }, 2400, 5280, { 1777, 1125, 979 }, Ratio{1, 8}, 1, 1280, 5136, +                { 0, 2, 1, 3 }, {                      { 0x16, 0x10 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1693,7 +1164,8 @@ void genesys_init_sensor_tables()                      { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },                  }              }, -            {   { 4800 }, 10416, { 3377, 2138, 1780 }, 5136, { 0, 2, 4, 6, 1, 3, 5, 7 }, { +            {   { 4800 }, 4800, 10416, { 3377, 2138, 1780 }, Ratio{1, 8}, 1, 2560, 5136, +                { 0, 2, 4, 6, 1, 3, 5, 7 }, {                      { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1707,8 +1179,14 @@ void genesys_init_sensor_tables()          for (const auto& setting : custom_settings) {              sensor.resolutions = setting.resolutions; +            sensor.register_dpihw = setting.register_dpihw; +            sensor.register_dpiset = setting.resolutions.values()[0]; +            sensor.shading_resolution = setting.register_dpihw;              sensor.exposure_lperiod = setting.exposure_lperiod;              sensor.exposure = setting.exposure; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.shading_factor = setting.shading_factor; +            sensor.output_pixel_offset = setting.output_pixel_offset;              sensor.segment_size = setting.segment_size;              sensor.segment_order = setting.segment_order;              sensor.custom_regs = setting.custom_regs; @@ -1718,34 +1196,64 @@ void genesys_init_sensor_tables()      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CIS_CANON_LIDE_700F; -    sensor.optical_res = 4800; +    sensor.sensor_id = SensorId::CIS_CANON_LIDE_700F; // gl847 +    sensor.full_resolution = 4800;      sensor.black_pixels = 73*8; // black pixels 73 at 600 dpi      sensor.dummy_pixel = 16*8; -    // 384 at 600 dpi -    sensor.ccd_start_xoffset = 384*8; -    // 8x5570 segments, 5187+1 for rounding -    sensor.sensor_pixels = 5188*8;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpihw;              int exposure_lperiod;              SensorExposure exposure; +            Ratio pixel_count_ratio; +            unsigned shading_factor; +            int output_pixel_offset;              unsigned segment_size;              std::vector<unsigned> segment_order;              GenesysRegisterSettingSet custom_regs;          };          CustomSensorSettings custom_settings[] = { -            {   { 75, 100, 150, 200 }, 2848, { 465, 310, 239 }, 5187, std::vector<unsigned>{}, { +            {   { 75 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 8, 48, 5187, +                std::vector<unsigned>{}, { +                    { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff }, +                    { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, +                    { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +                    { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                } +            }, +            {   { 100 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 6, 64, 5187, +                std::vector<unsigned>{}, { +                    { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff }, +                    { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, +                    { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +                    { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                } +            }, +            {   { 150 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 4, 96, 5187, +                std::vector<unsigned>{}, { +                    { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff }, +                    { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, +                    { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +                    { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x87 }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0xf9 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                } +            }, +            {   { 200 }, 600, 2848, { 465, 310, 239 }, Ratio{1, 8}, 3, 128, 5187, +                std::vector<unsigned>{}, {                      { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1755,7 +1263,8 @@ void genesys_init_sensor_tables()                      { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },                  }              }, -            {   { 300 }, 1424, { 465, 310, 239 }, 5187, std::vector<unsigned>{}, { +            {   { 300 }, 600, 1424, { 465, 310, 239 }, Ratio{1, 8}, 2, 192, 5187, +                std::vector<unsigned>{}, {                      { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1765,7 +1274,8 @@ void genesys_init_sensor_tables()                      { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },                  }              }, -            {   { 600 }, 1504, { 465, 310, 239 }, 5187, std::vector<unsigned>{}, { +            {   { 600 }, 600, 1504, { 465, 310, 239 }, Ratio{1, 8}, 1, 384, 5187, +                std::vector<unsigned>{}, {                      { 0x16, 0x10 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1775,7 +1285,8 @@ void genesys_init_sensor_tables()                      { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },                  }              }, -            {   { 1200 }, 2696, { 1464, 844, 555 }, 5187, { 0, 1 }, { +            {   { 1200 }, 1200, 2696, { 1464, 844, 555 }, Ratio{1, 8}, 1, 768, 5187, +                { 0, 1 }, {                      { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1785,7 +1296,8 @@ void genesys_init_sensor_tables()                      { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },                  }              }, -            {   { 2400 }, 10576, { 2798, 1558, 972 }, 5187, { 0, 1, 2, 3 }, { +            {   { 2400 }, 2400, 10576, { 2798, 1558, 972 }, Ratio{1, 8}, 1, 1536, 5187, +                { 0, 1, 2, 3 }, {                      { 0x16, 0x10 }, { 0x17, 0x08 }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1795,7 +1307,8 @@ void genesys_init_sensor_tables()                      { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },                  }              }, -            {   { 4800 }, 10576, { 2798, 1558, 972 }, 5187, { 0, 1, 4, 5, 2, 3, 6, 7 }, { +            {   { 4800 }, 4800, 10576, { 2798, 1558, 972 }, Ratio{1, 8}, 1, 3072, 5187, +                { 0, 1, 4, 5, 2, 3, 6, 7 }, {                      { 0x16, 0x10 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x07 }, { 0x53, 0x03 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1809,8 +1322,14 @@ void genesys_init_sensor_tables()          for (const auto& setting : custom_settings) {              sensor.resolutions = setting.resolutions; +            sensor.register_dpihw = setting.register_dpihw; +            sensor.register_dpiset = setting.resolutions.values()[0]; +            sensor.shading_resolution = setting.register_dpihw;              sensor.exposure_lperiod = setting.exposure_lperiod;              sensor.exposure = setting.exposure; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.shading_factor = setting.shading_factor; +            sensor.output_pixel_offset = setting.output_pixel_offset;              sensor.segment_size = setting.segment_size;              sensor.segment_order = setting.segment_order;              sensor.custom_regs = setting.custom_regs; @@ -1820,33 +1339,32 @@ void genesys_init_sensor_tables()      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CIS_CANON_LIDE_100; -    sensor.optical_res = 2400; +    sensor.sensor_id = SensorId::CIS_CANON_LIDE_100; // gl847 +    sensor.full_resolution = 2400;      sensor.black_pixels = 87*4;      sensor.dummy_pixel = 16*4; -    sensor.ccd_start_xoffset = 320*4; -    sensor.sensor_pixels = 5136*4;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.exposure = { 0x01c1, 0x0126, 0x00e5 };      sensor.gamma = { 2.2f, 2.2f, 2.2f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpihw;              int exposure_lperiod;              SensorExposure exposure; +            Ratio pixel_count_ratio; +            unsigned shading_factor; +            int output_pixel_offset;              unsigned segment_size;              std::vector<unsigned> segment_order;              GenesysRegisterSettingSet custom_regs;          };          CustomSensorSettings custom_settings[] = { -            {   { 75, 100, 150, 200 }, 2304, { 423, 294, 242 }, 5136, std::vector<unsigned>{}, { +            {   { 75 }, 600, 2304, { 423, 294, 242 }, Ratio{1, 4}, 8, 40, 5136, +                std::vector<unsigned>{}, {                      { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1856,7 +1374,8 @@ void genesys_init_sensor_tables()                      { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },                  }              }, -            {   { 300 }, 1728, { 423, 294, 242 }, 5136, std::vector<unsigned>{}, { +            {   { 100 }, 600, 2304, { 423, 294, 242 }, Ratio{1, 4}, 6, 53, 5136, +                std::vector<unsigned>{}, {                      { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1866,7 +1385,41 @@ void genesys_init_sensor_tables()                      { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },                  }              }, -            {   { 600 }, 1432, { 423, 294, 242 }, 5136, std::vector<unsigned>{}, { +            {   { 150 }, 600, 2304, { 423, 294, 242 }, Ratio{1, 4}, 4, 80, 5136, +                std::vector<unsigned>{}, { +                    { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, +                    { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, +                    { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +                    { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                } +            }, +            {   { 200 }, 600, 2304, { 423, 294, 242 }, Ratio{1, 4}, 3, 106, 5136, +                std::vector<unsigned>{}, { +                    { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, +                    { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, +                    { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +                    { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                } +            }, +            {   { 300 }, 600, 1728, { 423, 294, 242 }, Ratio{1, 4}, 2, 160, 5136, +                std::vector<unsigned>{}, { +                    { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff }, +                    { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 }, +                    { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +                    { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x2a }, { 0x59, 0xe1 }, { 0x5a, 0x55 }, +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                } +            }, +            {   { 600 }, 600, 1432, { 423, 294, 242 }, Ratio{1, 4}, 1, 320, 5136, +                std::vector<unsigned>{}, {                      { 0x16, 0x10 }, { 0x17, 0x0a }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1876,7 +1429,7 @@ void genesys_init_sensor_tables()                      { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },                  },              }, -            {   { 1200 }, 2712, { 791, 542, 403 }, 5136, {0, 1}, { +            {   { 1200 }, 1200, 2712, { 791, 542, 403 }, Ratio{1, 4}, 1, 640, 5136, {0, 1}, {                      { 0x16, 0x10 }, { 0x17, 0x08 }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1886,7 +1439,7 @@ void genesys_init_sensor_tables()                      { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },                  }              }, -            {   { 2400 }, 5280, { 1504, 1030, 766 }, 5136, {0, 2, 1, 3}, { +            {   { 2400 }, 2400, 5280, { 1504, 1030, 766 }, Ratio{1, 4}, 1, 1280, 5136, {0, 2, 1, 3}, {                      { 0x16, 0x10 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0xff },                      { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x04 },                      { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, @@ -1900,8 +1453,14 @@ void genesys_init_sensor_tables()          for (const auto& setting : custom_settings) {              sensor.resolutions = setting.resolutions; +            sensor.register_dpihw = setting.register_dpihw; +            sensor.register_dpiset = setting.resolutions.values()[0]; +            sensor.shading_resolution = setting.register_dpihw;              sensor.exposure_lperiod = setting.exposure_lperiod;              sensor.exposure = setting.exposure; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.shading_factor = setting.shading_factor; +            sensor.output_pixel_offset = setting.output_pixel_offset;              sensor.segment_size = setting.segment_size;              sensor.segment_order = setting.segment_order;              sensor.custom_regs = setting.custom_regs; @@ -1910,12 +1469,12 @@ void genesys_init_sensor_tables()      }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_KVSS080; -    sensor.optical_res = 600; +    sensor.sensor_id = SensorId::CCD_KVSS080; // gl843 +    sensor.full_resolution = 600; +    sensor.register_dpihw = 600; +    sensor.shading_resolution = 600;      sensor.black_pixels = 38;      sensor.dummy_pixel = 38; -    sensor.ccd_start_xoffset = 152; -    sensor.sensor_pixels = 5376;      sensor.fau_gain_white_ref = 160;      sensor.gain_white_ref = 160;      sensor.exposure = { 0x0000, 0x0000, 0x0000 }; @@ -1946,191 +1505,170 @@ void genesys_init_sensor_tables()          { 0x58, 0x6b },          { 0x59, 0x00 },          { 0x5a, 0xc0 }, +        { 0x7d, 0x90 },      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi; -    s_sensors->push_back(sensor); +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpiset; +            Ratio pixel_count_ratio; +            int output_pixel_offset; +        }; +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 75, Ratio{1, 1}, 4 }, +            { { 100 }, 100, Ratio{1, 1}, 6 }, +            { { 150 }, 150, Ratio{1, 1}, 9 }, +            { { 200 }, 200, Ratio{1, 1}, 12 }, +            { { 300 }, 300, Ratio{1, 1}, 19 }, +            { { 600 }, 600, Ratio{1, 1}, 38 }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            s_sensors->push_back(sensor); +        } +    }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_G4050; -    sensor.optical_res = 4800; +    sensor.sensor_id = SensorId::CCD_G4050; // gl843 +    sensor.full_resolution = 4800;      sensor.black_pixels = 50*8;      // 31 at 600 dpi dummy_pixels 58 at 1200      sensor.dummy_pixel = 58; -    sensor.ccd_start_xoffset = 152; -    sensor.sensor_pixels = 5360*8;      sensor.fau_gain_white_ref = 160;      sensor.gain_white_ref = 160;      sensor.exposure = { 0x2c09, 0x22b8, 0x10f0 }; -    sensor.stagger_config = StaggerConfig{ 2400, 4 }; // FIXME: may be incorrect      sensor.custom_regs = {};      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpihw; +            unsigned register_dpiset;              int exposure_lperiod;              ScanMethod method; +            Ratio pixel_count_ratio; +            int output_pixel_offset; +            StaggerConfig stagger_y; // FIXME: may be incorrect              GenesysRegisterSettingSet extra_custom_regs;          }; +        GenesysRegisterSettingSet regs_100_to_600 = { +            { 0x0c, 0x00 }, +            { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0x2a }, +            { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, +            { 0x52, 0x0b }, { 0x53, 0x0e }, { 0x54, 0x11 }, { 0x55, 0x02 }, { 0x56, 0x05 }, +            { 0x57, 0x08 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +            { 0x70, 0x00 }, { 0x71, 0x02 }, +            { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff }, +            { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff }, +            { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff }, { 0x7d, 0x90 }, +            { 0x9e, 0x00 }, +            { 0xaa, 0x00 }, +        }; + +        GenesysRegisterSettingSet regs_1200 = { +            { 0x0c, 0x20 }, +            { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, +            { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, +            { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, { 0x56, 0x0e }, +            { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +            { 0x70, 0x08 }, { 0x71, 0x0c }, +            { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, +            { 0x77, 0x00 }, { 0x78, 0x01 }, { 0x79, 0xff }, +            { 0x7a, 0x00 }, { 0x7b, 0x01 }, { 0x7c, 0xff }, { 0x7d, 0x90 }, +            { 0x9e, 0xc0 }, +            { 0xaa, 0x05 }, +        }; + +        GenesysRegisterSettingSet regs_2400 = { +            { 0x0c, 0x20 }, +            { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, +            { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0xc0 }, { 0x1d, 0x08 }, +            { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, { 0x56, 0x0e }, +            { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +            { 0x70, 0x08 }, { 0x71, 0x0a }, +            { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, +            { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, +            { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, { 0x7d, 0x90 }, +            { 0x9e, 0xc0 }, +            { 0xaa, 0x05 }, +        }; + +        GenesysRegisterSettingSet regs_4800 = { +            { 0x0c, 0x21 }, +            { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, +            { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0xc1 }, { 0x1d, 0x08 }, +            { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, { 0x56, 0x0e }, +            { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +            { 0x70, 0x08 }, { 0x71, 0x0a }, +            { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, +            { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, +            { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, { 0x7d, 0x90 }, +            { 0x9e, 0xc0 }, +            { 0xaa, 0x07 }, +        }; + +        GenesysRegisterSettingSet regs_ta_any = { +            { 0x0c, 0x00 }, +            { 0x16, 0x33 }, { 0x17, 0x4c }, { 0x18, 0x01 }, { 0x19, 0x2a }, +            { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, +            { 0x52, 0x0e }, { 0x53, 0x11 }, { 0x54, 0x02 }, { 0x55, 0x05 }, { 0x56, 0x08 }, +            { 0x57, 0x0b }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0xc0 }, +            { 0x70, 0x00 }, { 0x71, 0x02 }, +            { 0x74, 0x00 }, { 0x75, 0x1c }, { 0x76, 0x7f }, +            { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff }, +            { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff }, { 0x7d, 0x90 }, +            { 0x9e, 0x00 }, +            { 0xaa, 0x00 }, +        }; +          CustomSensorSettings custom_settings[] = { -            { { 100, 150, 200, 300, 400, 600 }, 8016, ScanMethod::FLATBED, { -                    { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff }, -                    { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff }, -                    { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff }, -                    { 0x0c, 0x00 }, -                    { 0x70, 0x00 }, -                    { 0x71, 0x02 }, -                    { 0x9e, 0x00 }, -                    { 0xaa, 0x00 }, -                    { 0x16, 0x33 }, -                    { 0x17, 0x0c }, -                    { 0x18, 0x00 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x08 }, -                    { 0x52, 0x0b }, -                    { 0x53, 0x0e }, -                    { 0x54, 0x11 }, -                    { 0x55, 0x02 }, -                    { 0x56, 0x05 }, -                    { 0x57, 0x08 }, -                    { 0x58, 0x63 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, -                } -            }, -            { { 1200 }, 56064, ScanMethod::FLATBED, { -                    { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, -                    { 0x77, 0x00 }, { 0x78, 0x01 }, { 0x79, 0xff }, -                    { 0x7a, 0x00 }, { 0x7b, 0x01 }, { 0x7c, 0xff }, -                    { 0x0c, 0x20 }, -                    { 0x70, 0x08 }, -                    { 0x71, 0x0c }, -                    { 0x9e, 0xc0 }, -                    { 0xaa, 0x05 }, -                    { 0x16, 0x3b }, -                    { 0x17, 0x0c }, -                    { 0x18, 0x10 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x38 }, -                    { 0x1b, 0x10 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x08 }, -                    { 0x52, 0x02 }, -                    { 0x53, 0x05 }, -                    { 0x54, 0x08 }, -                    { 0x55, 0x0b }, -                    { 0x56, 0x0e }, -                    { 0x57, 0x11 }, -                    { 0x58, 0x1b }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, -                } -            }, -            { { 2400 }, 56064, ScanMethod::FLATBED, { -                    { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, -                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, -                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, -                    { 0x0c, 0x20 }, -                    { 0x70, 0x08 }, -                    { 0x71, 0x0a }, -                    { 0x9e, 0xc0 }, -                    { 0xaa, 0x05 }, -                    { 0x16, 0x3b }, -                    { 0x17, 0x0c }, -                    { 0x18, 0x10 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x38 }, -                    { 0x1b, 0x10 }, -                    { 0x1c, 0xc0 }, -                    { 0x1d, 0x08 }, -                    { 0x52, 0x02 }, -                    { 0x53, 0x05 }, -                    { 0x54, 0x08 }, -                    { 0x55, 0x0b }, -                    { 0x56, 0x0e }, -                    { 0x57, 0x11 }, -                    { 0x58, 0x1b }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, -                } -            }, -            { { 4800 }, 42752, ScanMethod::FLATBED, { -                    { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, -                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, -                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, -                    { 0x0c, 0x21 }, -                    { 0x70, 0x08 }, -                    { 0x71, 0x0a }, -                    { 0x9e, 0xc0 }, -                    { 0xaa, 0x07 }, -                    { 0x16, 0x3b }, -                    { 0x17, 0x0c }, -                    { 0x18, 0x10 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x38 }, -                    { 0x1b, 0x10 }, -                    { 0x1c, 0xc1 }, -                    { 0x1d, 0x08 }, -                    { 0x52, 0x02 }, -                    { 0x53, 0x05 }, -                    { 0x54, 0x08 }, -                    { 0x55, 0x0b }, -                    { 0x56, 0x0e }, -                    { 0x57, 0x11 }, -                    { 0x58, 0x1b }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, -                } -            }, -            { ResolutionFilter::ANY, 15624, ScanMethod::TRANSPARENCY, { -                    { 0x74, 0x00 }, { 0x75, 0x1c }, { 0x76, 0x7f }, -                    { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff }, -                    { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff }, -                    { 0x0c, 0x00 }, -                    { 0x70, 0x00 }, -                    { 0x71, 0x02 }, -                    { 0x9e, 0x00 }, -                    { 0xaa, 0x00 }, -                    { 0x16, 0x33 }, -                    { 0x17, 0x4c }, -                    { 0x18, 0x01 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x08 }, -                    { 0x52, 0x0e }, -                    { 0x53, 0x11 }, -                    { 0x54, 0x02 }, -                    { 0x55, 0x05 }, -                    { 0x56, 0x08 }, -                    { 0x57, 0x0b }, -                    { 0x58, 0x6b }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0xc0 }, -                } -            } +            {   { 100 }, 600, 100, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 1, +                StaggerConfig{}, regs_100_to_600 }, +            {   { 150 }, 600, 150, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 1, +                StaggerConfig{}, regs_100_to_600 }, +            {   { 200 }, 600, 200, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 2, +                StaggerConfig{}, regs_100_to_600 }, +            {   { 300 }, 600, 300, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 3, +                StaggerConfig{}, regs_100_to_600 }, +            {   { 400 }, 600, 400, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 4, +                StaggerConfig{}, regs_100_to_600 }, +            {   { 600 }, 600, 600, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 7, +                StaggerConfig{}, regs_100_to_600 }, +            {   { 1200 }, 1200, 1200, 56064, ScanMethod::FLATBED, Ratio{1, 4}, 14, +                StaggerConfig{}, regs_1200 }, +            {   { 2400 }, 2400, 2400, 56064, ScanMethod::FLATBED, Ratio{1, 2}, 29, +                StaggerConfig{4, 0}, regs_2400 }, +            {   { 4800 }, 4800, 4800, 42752, ScanMethod::FLATBED, Ratio{1, 1}, 58, +                StaggerConfig{8, 0}, regs_4800 }, +            {   { 100, 150, 200, 300, 400, 600, 1200 }, 600, 600, 15624, ScanMethod::TRANSPARENCY, +                Ratio{1, 1}, 58, StaggerConfig{}, regs_ta_any }, // FIXME: may be incorrect +            {   { 2400 }, 600, 600, 15624, ScanMethod::TRANSPARENCY, +                Ratio{1, 1}, 58, StaggerConfig{4, 0}, regs_ta_any }, // FIXME: may be incorrect +            {   { 4800 }, 600, 600, 15624, ScanMethod::TRANSPARENCY, +                Ratio{1, 1}, 58, StaggerConfig{8, 0}, regs_ta_any }, // FIXME: may be incorrect          };          auto base_custom_regs = sensor.custom_regs;          for (const CustomSensorSettings& setting : custom_settings)          {              sensor.resolutions = setting.resolutions; +            sensor.register_dpihw = setting.register_dpihw; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.shading_resolution = setting.register_dpihw;              sensor.exposure_lperiod = setting.exposure_lperiod;              sensor.method = setting.method; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            sensor.stagger_y = setting.stagger_y;              sensor.custom_regs = base_custom_regs;              sensor.custom_regs.merge(setting.extra_custom_regs);              s_sensors->push_back(sensor); @@ -2138,110 +1676,136 @@ void genesys_init_sensor_tables()      }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_HP_4850C; -    sensor.optical_res = 4800; +    sensor.sensor_id = SensorId::CCD_HP_4850C; // gl843 +    sensor.full_resolution = 4800;      sensor.black_pixels = 100;      sensor.dummy_pixel = 58; -    sensor.ccd_start_xoffset = 152; -    sensor.sensor_pixels = 5360*8;      sensor.fau_gain_white_ref = 160;      sensor.gain_white_ref = 160;      sensor.exposure = { 0x2c09, 0x22b8, 0x10f0 }; -    sensor.stagger_config = StaggerConfig{ 2400, 4 }; // FIXME: may be incorrect      sensor.custom_regs = {};      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpihw; +            unsigned register_dpiset;              int exposure_lperiod;              ScanMethod method; +            Ratio pixel_count_ratio; +            int output_pixel_offset; +            int shading_pixel_offset; +            StaggerConfig stagger_y; // FIXME: review, may be incorrect              GenesysRegisterSettingSet extra_custom_regs;          }; +        GenesysRegisterSettingSet regs_100_to_600 = { +            { 0x0c, 0x00 }, +            { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0x2a }, +            { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, +            { 0x52, 0x0b }, { 0x53, 0x0e }, { 0x54, 0x11 }, { 0x55, 0x02 }, +            { 0x56, 0x05 }, { 0x57, 0x08 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +            { 0x70, 0x00 }, { 0x71, 0x02 }, +            { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff }, +            { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff }, +            { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff }, { 0x7d, 0x90 }, +            { 0x9e, 0x00 }, +            { 0xaa, 0x00 }, +        }; +        GenesysRegisterSettingSet regs_1200 = { +            { 0x0c, 0x20 }, +            { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, +            { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, +            { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, +            { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +            { 0x70, 0x08 }, { 0x71, 0x0c }, +            { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, +            { 0x77, 0x00 }, { 0x78, 0x01 }, { 0x79, 0xff }, +            { 0x7a, 0x00 }, { 0x7b, 0x01 }, { 0x7c, 0xff }, { 0x7d, 0x90 }, +            { 0x9e, 0xc0 }, +            { 0xaa, 0x05 }, +        }; +        GenesysRegisterSettingSet regs_2400 = { +            { 0x0c, 0x20 }, +            { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, +            { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0xc0 }, { 0x1d, 0x08 }, +            { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, +            { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +            { 0x70, 0x08 }, { 0x71, 0x0a }, +            { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, +            { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, +            { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, { 0x7d, 0x90 }, +            { 0x9e, 0xc0 }, +            { 0xaa, 0x05 }, +        }; +        GenesysRegisterSettingSet regs_4800 = { +            { 0x0c, 0x21 }, +            { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, +            { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0xc1 }, { 0x1d, 0x08 }, +            { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, +            { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +            { 0x70, 0x08 }, { 0x71, 0x0a }, +            { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, +            { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, +            { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, { 0x7d, 0x90 }, +            { 0x9e, 0xc0 }, +            { 0xaa, 0x07 }, +        }; +        GenesysRegisterSettingSet regs_ta_any = { +            { 0x0c, 0x00 }, +            { 0x16, 0x33 }, { 0x17, 0x4c }, { 0x18, 0x01 }, { 0x19, 0x2a }, +            { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, +            { 0x52, 0x0e }, { 0x53, 0x11 }, { 0x54, 0x02 }, { 0x55, 0x05 }, +            { 0x56, 0x08 }, { 0x57, 0x0b }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0xc0 }, +            { 0x70, 0x00 }, { 0x71, 0x02 }, +            { 0x74, 0x00 }, { 0x75, 0x1c }, { 0x76, 0x7f }, +            { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff }, +            { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff }, { 0x7d, 0x90 }, +            { 0x9e, 0x00 }, +            { 0xaa, 0x00 }, +        }; +          CustomSensorSettings custom_settings[] = { -            {   { 100, 150, 200, 300, 400, 600 }, 8016, ScanMethod::FLATBED, { -                    { 0x0c, 0x00 }, -                    { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x00 }, { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, -                    { 0x52, 0x0b }, { 0x53, 0x0e }, { 0x54, 0x11 }, { 0x55, 0x02 }, -                    { 0x56, 0x05 }, { 0x57, 0x08 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0x40 }, -                    { 0x70, 0x00 }, { 0x71, 0x02 }, -                    { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff }, -                    { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff }, -                    { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff }, -                    { 0x9e, 0x00 }, -                    { 0xaa, 0x00 }, -                } -            }, -            {   { 1200 }, 56064, ScanMethod::FLATBED, { -                    { 0x0c, 0x20 }, -                    { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, -                    { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, -                    { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, -                    { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, -                    { 0x70, 0x08 }, { 0x71, 0x0c }, -                    { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, -                    { 0x77, 0x00 }, { 0x78, 0x01 }, { 0x79, 0xff }, -                    { 0x7a, 0x00 }, { 0x7b, 0x01 }, { 0x7c, 0xff }, -                    { 0x9e, 0xc0 }, -                    { 0xaa, 0x05 }, -                } -            }, -            {   { 2400 }, 56064, ScanMethod::FLATBED, { -                    { 0x0c, 0x20 }, -                    { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, -                    { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0xc0 }, { 0x1d, 0x08 }, -                    { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, -                    { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, -                    { 0x70, 0x08 }, { 0x71, 0x0a }, -                    { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, -                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, -                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, -                    { 0x9e, 0xc0 }, -                    { 0xaa, 0x05 }, -                } -            }, -            {   { 4800 }, 42752, ScanMethod::FLATBED, { -                    { 0x0c, 0x21 }, -                    { 0x16, 0x3b }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, -                    { 0x1a, 0x38 }, { 0x1b, 0x10 }, { 0x1c, 0xc1 }, { 0x1d, 0x08 }, -                    { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, -                    { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, -                    { 0x70, 0x08 }, { 0x71, 0x0a }, -                    { 0x74, 0x0f }, { 0x75, 0xff }, { 0x76, 0xff }, -                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, -                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, -                    { 0x9e, 0xc0 }, -                    { 0xaa, 0x07 }, -                } -            }, -            {   ResolutionFilter::ANY, 15624, ScanMethod::TRANSPARENCY, { -                    { 0x0c, 0x00 }, -                    { 0x16, 0x33 }, { 0x17, 0x4c }, { 0x18, 0x01 }, { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x08 }, -                    { 0x52, 0x0e }, { 0x53, 0x11 }, { 0x54, 0x02 }, { 0x55, 0x05 }, -                    { 0x56, 0x08 }, { 0x57, 0x0b }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0xc0 }, -                    { 0x70, 0x00 }, { 0x71, 0x02 }, -                    { 0x74, 0x00 }, { 0x75, 0x1c }, { 0x76, 0x7f }, -                    { 0x77, 0x03 }, { 0x78, 0xff }, { 0x79, 0xff }, -                    { 0x7a, 0x03 }, { 0x7b, 0xff }, { 0x7c, 0xff }, -                    { 0x9e, 0x00 }, -                    { 0xaa, 0x00 }, -                } -            } +            {   { 100 }, 600, 100, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 1, 50, StaggerConfig{}, +                regs_100_to_600 }, +            {   { 150 }, 600, 150, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 1, 50, StaggerConfig{}, +                regs_100_to_600 }, +            {   { 200 }, 600, 200, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 2, 50, StaggerConfig{}, +                regs_100_to_600 }, +            {   { 300 }, 600, 300, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 3, 50, StaggerConfig{}, +                regs_100_to_600 }, +            {   { 400 }, 600, 400, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 4, 50, StaggerConfig{}, +                regs_100_to_600 }, +            {   { 600 }, 600, 600, 8016, ScanMethod::FLATBED, Ratio{1, 8}, 7, 50, StaggerConfig{}, +                regs_100_to_600 }, +            {   { 1200 }, 1200, 1200, 56064, ScanMethod::FLATBED, Ratio{1, 4}, 14, 0, +                StaggerConfig{}, regs_1200 }, +            {   { 2400 }, 2400, 2400, 56064, ScanMethod::FLATBED, Ratio{1, 2}, 29, 0, +                StaggerConfig{0, 4}, regs_2400 }, +            {   { 4800 }, 4800, 4800, 42752, ScanMethod::FLATBED, Ratio{1, 1}, 58, 0, +                StaggerConfig{0, 8}, regs_4800 }, +            {   { 100, 150, 200, 300, 400, 600, 1200}, 600, 600, 15624, ScanMethod::TRANSPARENCY, +                Ratio{1, 1}, 58, 0, StaggerConfig{}, regs_ta_any }, // FIXME: review +            {   { 2400 }, 600, 600, 15624, ScanMethod::TRANSPARENCY, +                Ratio{1, 1}, 58, 0, StaggerConfig{0, 4}, regs_ta_any }, // FIXME: review +            {   { 4800 }, 600, 600, 15624, ScanMethod::TRANSPARENCY, +                Ratio{1, 1}, 58, 0, StaggerConfig{0, 8}, regs_ta_any }, // FIXME: review          };          auto base_custom_regs = sensor.custom_regs;          for (const CustomSensorSettings& setting : custom_settings)          {              sensor.resolutions = setting.resolutions; +            sensor.register_dpihw = setting.register_dpihw; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.shading_resolution = setting.register_dpihw;              sensor.exposure_lperiod = setting.exposure_lperiod;              sensor.method = setting.method; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            sensor.shading_pixel_offset = setting.shading_pixel_offset; +            sensor.stagger_y = setting.stagger_y;              sensor.custom_regs = base_custom_regs;              sensor.custom_regs.merge(setting.extra_custom_regs);              s_sensors->push_back(sensor); @@ -2249,142 +1813,250 @@ void genesys_init_sensor_tables()      }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_CANON_4400F; -    sensor.optical_res = 4800; -    sensor.ccd_size_divisor = 4; +    sensor.sensor_id = SensorId::CCD_CANON_4400F; // gl843 +    sensor.full_resolution = 4800; +    sensor.register_dpihw = 4800;      sensor.black_pixels = 50*8;      // 31 at 600 dpi, 58 at 1200 dpi      sensor.dummy_pixel = 20; -    sensor.ccd_start_xoffset = 152; -    // 5360 max at 600 dpi -    sensor.sensor_pixels = 5700 * 8;      sensor.fau_gain_white_ref = 160;      sensor.gain_white_ref = 160;      sensor.exposure = { 0x9c40, 0x9c40, 0x9c40 };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = get_sensor_optical_with_ccd_divisor; -    sensor.get_register_hwdpi_fun = [](const Genesys_Sensor&, unsigned) { return 4800; }; -    sensor.get_hwdpi_divisor_fun = [](const Genesys_Sensor&, unsigned) { return 1; }; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions; +            unsigned optical_resolution; +            unsigned register_dpiset;              int exposure_lperiod; +            bool use_host_side_calib; +            int output_pixel_offset;              std::vector<ScanMethod> methods; +            StaggerConfig stagger_y;              GenesysRegisterSettingSet extra_custom_regs; +            GenesysRegisterSettingSet extra_custom_fe_regs;          };          CustomSensorSettings custom_settings[] = { -            { { 300, 600, 1200 }, 11640, { ScanMethod::FLATBED }, { -                    { 0x16, 0x13 }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x10 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x6b }, -                    { 0x52, 0x0a }, -                    { 0x53, 0x0d }, -                    { 0x54, 0x00 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x06 }, -                    { 0x57, 0x08 }, -                    { 0x58, 0x5b }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, +            {   { 300 }, 1200, 1200, 11640, false, 197, { ScanMethod::FLATBED }, StaggerConfig{}, { +                    { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, +                    { 0x52, 0x0a }, { 0x53, 0x0d }, { 0x54, 0x00 }, { 0x55, 0x03 }, +                    { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x5b }, { 0x59, 0x00 }, { 0x5a, 0x40 },                      { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x01 }, { 0x73, 0x03 },                      { 0x74, 0x00 }, { 0x75, 0xf8 }, { 0x76, 0x38 },                      { 0x77, 0x00 }, { 0x78, 0xfc }, { 0x79, 0x00 },                      { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0xa4 },                      { 0x9e, 0x2d }, -                } +                }, {} +            }, +            {   { 600 }, 1200, 2400, 11640, false, 392, { ScanMethod::FLATBED }, StaggerConfig{}, { +                    { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, +                    { 0x52, 0x0a }, { 0x53, 0x0d }, { 0x54, 0x00 }, { 0x55, 0x03 }, +                    { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x5b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +                    { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x01 }, { 0x73, 0x03 }, +                    { 0x74, 0x00 }, { 0x75, 0xf8 }, { 0x76, 0x38 }, +                    { 0x77, 0x00 }, { 0x78, 0xfc }, { 0x79, 0x00 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0xa4 }, +                    { 0x9e, 0x2d }, +                }, {} +            }, +            {   { 1200 }, 1200, 4800, 11640, false, 794, { ScanMethod::FLATBED }, StaggerConfig{}, { +                    { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, +                    { 0x52, 0x0a }, { 0x53, 0x0d }, { 0x54, 0x00 }, { 0x55, 0x03 }, +                    { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x5b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +                    { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x01 }, { 0x73, 0x03 }, +                    { 0x74, 0x00 }, { 0x75, 0xf8 }, { 0x76, 0x38 }, +                    { 0x77, 0x00 }, { 0x78, 0xfc }, { 0x79, 0x00 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0xa4 }, +                    { 0x9e, 0x2d }, +                }, {}              }, -            { { 300, 600, 1200 }, 33300, { ScanMethod::TRANSPARENCY }, { -                    { 0x16, 0x13 }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x10 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x6b }, -                    { 0x52, 0x0a }, -                    { 0x53, 0x0d }, -                    { 0x54, 0x00 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x06 }, -                    { 0x57, 0x08 }, -                    { 0x58, 0x5b }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, +            {   { 1200 }, 1200, 4800, 33300, true, 5, { ScanMethod::TRANSPARENCY }, +                StaggerConfig{}, { +                    { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, +                    { 0x52, 0x0a }, { 0x53, 0x0d }, { 0x54, 0x00 }, { 0x55, 0x03 }, +                    { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x5b }, { 0x59, 0x00 }, { 0x5a, 0x40 },                      { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x00 }, { 0x73, 0x02 },                      { 0x74, 0x00 }, { 0x75, 0xf8 }, { 0x76, 0x38 },                      { 0x77, 0x00 }, { 0x78, 0xfc }, { 0x79, 0x00 },                      { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0xa4 },                      { 0x9e, 0x2d }, -                } +                }, {}              }, -            { { 2400 }, 33300, { ScanMethod::TRANSPARENCY }, { -                    { 0x16, 0x13 }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x10 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x01 }, -                    { 0x1d, 0x75 }, -                    { 0x52, 0x0b }, -                    { 0x53, 0x0d }, -                    { 0x54, 0x00 }, -                    { 0x55, 0x03 }, -                    { 0x56, 0x06 }, -                    { 0x57, 0x09 }, -                    { 0x58, 0x53 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, +            {   { 2400 }, 2400, 4800, 33300, true, 10, { ScanMethod::TRANSPARENCY }, +                StaggerConfig{}, { +                    { 0x16, 0x13 }, { 0x17, 0x15 }, { 0x18, 0x10 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x01 }, { 0x1d, 0x75 }, +                    { 0x52, 0x0b }, { 0x53, 0x0d }, { 0x54, 0x00 }, { 0x55, 0x03 }, +                    { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x53 }, { 0x59, 0x00 }, { 0x5a, 0x40 },                      { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 },                      { 0x74, 0x00 }, { 0x75, 0xff }, { 0x76, 0x00 },                      { 0x77, 0x00 }, { 0x78, 0xff }, { 0x79, 0x00 },                      { 0x7a, 0x00 }, { 0x7b, 0x54 }, { 0x7c, 0x92 },                      { 0x9e, 0x2d }, +                }, { +                    { 0x03, 0x1f },                  }              }, -            { { 4800 }, 33300, { ScanMethod::TRANSPARENCY }, { -                    { 0x16, 0x13 }, -                    { 0x17, 0x0a }, -                    { 0x18, 0x10 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x61 }, -                    { 0x1d, 0x75 }, -                    { 0x52, 0x02 }, -                    { 0x53, 0x05 }, -                    { 0x54, 0x08 }, -                    { 0x55, 0x0b }, -                    { 0x56, 0x0d }, -                    { 0x57, 0x0f }, -                    { 0x58, 0x1b }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, +            {   { 4800 }, 4800, 4800, 33300, true, -2063, { ScanMethod::TRANSPARENCY }, +                StaggerConfig{0, 8}, { +                    { 0x16, 0x13 }, { 0x17, 0x15 }, { 0x18, 0x10 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x61 }, { 0x1d, 0x75 }, +                    { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, +                    { 0x56, 0x0d }, { 0x57, 0x0f }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 },                      { 0x70, 0x08 }, { 0x71, 0x0a }, { 0x72, 0x0a }, { 0x73, 0x0c },                      { 0x74, 0x00 }, { 0x75, 0xff }, { 0x76, 0xff },                      { 0x77, 0x00 }, { 0x78, 0xff }, { 0x79, 0xff },                      { 0x7a, 0x00 }, { 0x7b, 0x54 }, { 0x7c, 0x92 },                      { 0x9e, 0x2d }, -                } +                }, {}              }          };          for (const CustomSensorSettings& setting : custom_settings)          {              for (auto method : setting.methods) { +                for (auto resolution : setting.resolutions.values()) { +                    sensor.resolutions = { resolution }; +                    sensor.optical_resolution = setting.optical_resolution; +                    sensor.register_dpiset = setting.register_dpiset; +                    sensor.shading_resolution = resolution; +                    sensor.exposure_lperiod = setting.exposure_lperiod; +                    sensor.output_pixel_offset = setting.output_pixel_offset; +                    sensor.use_host_side_calib = setting.use_host_side_calib; +                    sensor.method = method; +                    sensor.stagger_y = setting.stagger_y; +                    sensor.custom_regs = setting.extra_custom_regs; +                    sensor.custom_fe_regs = setting.extra_custom_fe_regs; +                    s_sensors->push_back(sensor); +                } +            } +        } +    } + + +    sensor = Genesys_Sensor(); +    sensor.sensor_id = SensorId::CCD_CANON_5600F; // gl847 +    sensor.full_resolution = 4800; +    sensor.register_dpihw = 4800; +    sensor.black_pixels = 50*8; +    sensor.dummy_pixel = 10; +    sensor.fau_gain_white_ref = 160; +    sensor.gain_white_ref = 160; +    sensor.exposure = { 0x9c40, 0x9c40, 0x9c40 }; +    sensor.gamma = { 1.0f, 1.0f, 1.0f }; +    sensor.use_host_side_calib = true; +    { +        struct CustomSensorSettings { +            ValueFilterAny<unsigned> resolutions; +            unsigned optical_resolution; +            unsigned register_dpihw; +            unsigned register_dpiset; +            int exposure_lperiod; +            SensorExposure exposure; +            Ratio pixel_count_ratio; +            int output_pixel_offset; +            unsigned segment_size; +            std::vector<unsigned> segment_order; +            StaggerConfig stagger_x; +            StaggerConfig stagger_y; +            GenesysRegisterSettingSet custom_regs; +        }; + +        CustomSensorSettings custom_settings[] = { +            {   { 150 }, 2400, 600, 300, 4288, { 3983/2, 3983/2, 3983/2 }, Ratio{1, 8}, 10, +                5418, std::vector<unsigned>{}, StaggerConfig{}, StaggerConfig{},  { +                    { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, +                    { 0x52, 0x0e }, { 0x53, 0x00 }, { 0x54, 0x02 }, { 0x55, 0x04 }, +                    { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x52 }, { 0x59, 0x3a }, { 0x5a, 0x40 }, +                    { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, +                } +            }, +            {   { 300 }, 2400, 600, 600, 5472, { 4558/2, 4558/2, 4558/2 }, Ratio{1, 8}, 110, +                5418, std::vector<unsigned>{}, StaggerConfig{}, StaggerConfig{}, { +                    { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, +                    { 0x52, 0x0e }, { 0x53, 0x00 }, { 0x54, 0x02 }, { 0x55, 0x04 }, +                    { 0x56, 0x06 }, { 0x57, 0x08 }, { 0x58, 0x52 }, { 0x59, 0x3a }, { 0x5a, 0x40 }, +                    { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, +                } +            }, +            {   { 600 }, 2400, 600, 600, 10944, { 8701/2, 8701/2, 8701/2 }, Ratio{1, 4}, 155, +                5418, std::vector<unsigned>{}, StaggerConfig{}, StaggerConfig{}, { +                    { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, +                    { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x06 }, { 0x55, 0x08 }, +                    { 0x56, 0x0a }, { 0x57, 0x0c }, { 0x58, 0x72 }, { 0x59, 0x5a }, { 0x5a, 0x40 }, +                    { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, +                } +            }, +            {   { 1200 }, 2400, 1200, 1200, 29120, { 17120/2, 17120/2, 17120/2 }, Ratio{1, 2}, 295, +                5418, { 1, 0 }, StaggerConfig{}, StaggerConfig{}, { +                    { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, +                    { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x06 }, { 0x55, 0x08 }, +                    { 0x56, 0x0a }, { 0x57, 0x0c }, { 0x58, 0x72 }, { 0x59, 0x5a }, { 0x5a, 0x40 }, +                    { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, +                } +            }, +            {   { 2400 }, 2400, 2400, 2400, 43776, { 36725/2, 36725/2, 36725/2 }, Ratio{1, 1}, 600, +                5418, { 0, 1, 2, 3 }, +                StaggerConfig{10, 15, 4, 9, 14, 19, 8, 13}, StaggerConfig{}, { +                    { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, +                    { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x06 }, { 0x55, 0x08 }, +                    { 0x56, 0x0a }, { 0x57, 0x0c }, { 0x58, 0x72 }, { 0x59, 0x5a }, { 0x5a, 0x40 }, +                    { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, +                } +            }, +            {   { 4800 }, 4800, 4800, 4800, 43776, { 36725/2, 36725/2, 36725/2 }, Ratio{1, 1}, 1000, +                10784, { 0, 1, 2, 3 }, +                StaggerConfig{5, 9, 6, 10, 3, 7, 16, 20, 13, 17, 14, 18, 11, 15, 24, 28}, +                StaggerConfig{6, 0}, { +                    { 0x16, 0x00 }, { 0x17, 0x06 }, { 0x18, 0x00 }, { 0x19, 0x2a }, +                    { 0x1a, 0x00 }, { 0x1b, 0x10 }, { 0x1c, 0x08 }, { 0x1d, 0x02 }, +                    { 0x52, 0x0a }, { 0x53, 0x0c }, { 0x54, 0x0e }, { 0x55, 0x00 }, +                    { 0x56, 0x02 }, { 0x57, 0x04 }, { 0x58, 0x32 }, { 0x59, 0x1a }, { 0x5a, 0x40 }, +                    { 0x74, 0x00 }, { 0x75, 0x33 }, { 0x76, 0x33 }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x87, 0x00 }, +                } +            } +        }; + +        for (const auto& setting : custom_settings) { +            for (auto method : { ScanMethod::FLATBED, ScanMethod::TRANSPARENCY }) { +                sensor.method = method;                  sensor.resolutions = setting.resolutions; +                sensor.optical_resolution = setting.optical_resolution; +                sensor.register_dpihw = setting.register_dpihw; +                sensor.register_dpiset = setting.register_dpiset; +                sensor.shading_resolution = setting.resolutions.values().front();                  sensor.exposure_lperiod = setting.exposure_lperiod; -                sensor.method = method; -                sensor.custom_regs = setting.extra_custom_regs; +                sensor.exposure = setting.exposure; +                sensor.pixel_count_ratio = setting.pixel_count_ratio; +                sensor.output_pixel_offset = setting.output_pixel_offset; +                sensor.segment_size = setting.segment_size; +                sensor.segment_order = setting.segment_order; +                sensor.stagger_x = setting.stagger_x; +                sensor.stagger_y = setting.stagger_y; +                sensor.custom_regs = setting.custom_regs;                  s_sensors->push_back(sensor);              }          } @@ -2392,57 +2064,39 @@ void genesys_init_sensor_tables()      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_CANON_8400F; -    sensor.optical_res = 3200; -    sensor.register_dpihw_override = 4800; -    sensor.ccd_size_divisor = 1; +    sensor.sensor_id = SensorId::CCD_CANON_8400F; // gl843 +    sensor.full_resolution = 3200; +    sensor.register_dpihw = 4800;      sensor.black_pixels = 50*8;      // 31 at 600 dpi, 58 at 1200 dpi      sensor.dummy_pixel = 20; -    sensor.ccd_start_xoffset = 152; -    sensor.sensor_pixels = 27200;      sensor.fau_gain_white_ref = 160;      sensor.gain_white_ref = 160;      sensor.exposure = { 0x9c40, 0x9c40, 0x9c40 }; -    sensor.stagger_config = StaggerConfig{ 3200, 6 };      sensor.custom_regs = {};      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = get_sensor_optical_with_ccd_divisor; -    sensor.get_register_hwdpi_fun = [](const Genesys_Sensor&, unsigned) { return 4800; }; -    sensor.get_hwdpi_divisor_fun = [](const Genesys_Sensor&, unsigned) { return 1; }; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; -            unsigned dpiset_override; -            unsigned pixel_count_multiplier; +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpiset; +            Ratio pixel_count_ratio;              int exposure_lperiod; +            int output_pixel_offset; +            int shading_pixel_offset;              std::vector<ScanMethod> methods; +            StaggerConfig stagger_y;              GenesysRegisterSettingSet extra_custom_regs;              GenesysRegisterSettingSet custom_fe_regs;          };          CustomSensorSettings custom_settings[] = { -            { { 400 }, 2400, 1, 7200, { ScanMethod::FLATBED }, { -                    { 0x16, 0x33 }, -                    { 0x17, 0x0c }, -                    { 0x18, 0x13 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x84 }, -                    { 0x1e, 0xa0 }, -                    { 0x52, 0x0d }, -                    { 0x53, 0x10 }, -                    { 0x54, 0x01 }, -                    { 0x55, 0x04 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0a }, -                    { 0x58, 0x6b }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, +            {   { 400 }, 2400, Ratio{1, 4}, 7200, 2, 0, { ScanMethod::FLATBED }, +                StaggerConfig{}, { +                    { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x13 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x84 }, { 0x1e, 0xa0 }, +                    { 0x52, 0x0d }, { 0x53, 0x10 }, { 0x54, 0x01 }, { 0x55, 0x04 }, +                    { 0x56, 0x07 }, { 0x57, 0x0a }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 },                      { 0x70, 0x01 }, { 0x71, 0x02 }, { 0x72, 0x03 }, { 0x73, 0x04 },                      { 0x74, 0x00 }, { 0x75, 0x0e }, { 0x76, 0x3f },                      { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, @@ -2450,25 +2104,12 @@ void genesys_init_sensor_tables()                      { 0x80, 0x2a },                  }, {}              }, -            { { 800 }, 4800, 1, 7200, { ScanMethod::FLATBED }, { -                    { 0x16, 0x33 }, -                    { 0x17, 0x0c }, -                    { 0x18, 0x13 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x84 }, -                    { 0x1e, 0xa0 }, -                    { 0x52, 0x0d }, -                    { 0x53, 0x10 }, -                    { 0x54, 0x01 }, -                    { 0x55, 0x04 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0a }, -                    { 0x58, 0x6b }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, +            {   { 800 }, 4800, Ratio{1, 4}, 7200, 5, 13, { ScanMethod::FLATBED }, +                StaggerConfig{}, { +                    { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x13 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x84 }, { 0x1e, 0xa0 }, +                    { 0x52, 0x0d }, { 0x53, 0x10 }, { 0x54, 0x01 }, { 0x55, 0x04 }, +                    { 0x56, 0x07 }, { 0x57, 0x0a }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 },                      { 0x70, 0x01 }, { 0x71, 0x02 }, { 0x72, 0x03 }, { 0x73, 0x04 },                      { 0x74, 0x00 }, { 0x75, 0x0e }, { 0x76, 0x3f },                      { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, @@ -2476,26 +2117,13 @@ void genesys_init_sensor_tables()                      { 0x80, 0x20 },                  }, {}              }, -            { { 1600 }, 4800, 1, 14400, { ScanMethod::FLATBED }, { -                    { 0x16, 0x33 }, -                    { 0x17, 0x0c }, -                    { 0x18, 0x11 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x84 }, -                    { 0x1e, 0xa1 }, -                    { 0x52, 0x0b }, -                    { 0x53, 0x0e }, -                    { 0x54, 0x11 }, -                    { 0x55, 0x02 }, -                    { 0x56, 0x05 }, -                    { 0x57, 0x08 }, -                    { 0x58, 0x63 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, -                    { 0x70, 0x01 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x03 }, +            {   { 1600 }, 4800, Ratio{1, 2}, 14400, 10, 8, { ScanMethod::FLATBED }, +                StaggerConfig{}, { +                    { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x11 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x84 }, { 0x1e, 0xa1 }, +                    { 0x52, 0x0b }, { 0x53, 0x0e }, { 0x54, 0x11 }, { 0x55, 0x02 }, +                    { 0x56, 0x05 }, { 0x57, 0x08 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +                    { 0x70, 0x00 }, { 0x71, 0x01 }, { 0x72, 0x02 }, { 0x73, 0x03 },                      { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff },                      { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },                      { 0x7a, 0x02 }, { 0x7b, 0x49 }, { 0x7c, 0x24 }, @@ -2504,25 +2132,12 @@ void genesys_init_sensor_tables()                      { 0x03, 0x1f },                  }              }, -            { { 3200 }, 4800, 1, 28800, { ScanMethod::FLATBED }, { -                    { 0x16, 0x33 }, -                    { 0x17, 0x0c }, -                    { 0x18, 0x10 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x20 }, -                    { 0x1d, 0x84 }, -                    { 0x1e, 0xa1 }, -                    { 0x52, 0x02 }, -                    { 0x53, 0x05 }, -                    { 0x54, 0x08 }, -                    { 0x55, 0x0b }, -                    { 0x56, 0x0e }, -                    { 0x57, 0x11 }, -                    { 0x58, 0x1b }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, +            {   { 3200 }, 4800, Ratio{1, 1}, 28800, 20, -2, { ScanMethod::FLATBED }, +                StaggerConfig{0, 6}, { +                    { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x84 }, { 0x1e, 0xa1 }, +                    { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, +                    { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 },                      { 0x70, 0x09 }, { 0x71, 0x0a }, { 0x72, 0x0b }, { 0x73, 0x0c },                      { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 },                      { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, @@ -2532,26 +2147,13 @@ void genesys_init_sensor_tables()                      { 0x03, 0x1f },                  },              }, -            { { 400 }, 2400, 1, 14400, { ScanMethod::TRANSPARENCY, -                                         ScanMethod::TRANSPARENCY_INFRARED }, { -                    { 0x16, 0x33 }, -                    { 0x17, 0x0c }, -                    { 0x18, 0x13 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x84 }, -                    { 0x1e, 0xa0 }, -                    { 0x52, 0x0d }, -                    { 0x53, 0x10 }, -                    { 0x54, 0x01 }, -                    { 0x55, 0x04 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0a }, -                    { 0x58, 0x6b }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, +            {   { 400 }, 2400, Ratio{1, 4}, 14400, 2, 0, { ScanMethod::TRANSPARENCY, +                                                           ScanMethod::TRANSPARENCY_INFRARED }, +                StaggerConfig{}, { +                    { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x13 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x84 }, { 0x1e, 0xa0 }, +                    { 0x52, 0x0d }, { 0x53, 0x10 }, { 0x54, 0x01 }, { 0x55, 0x04 }, +                    { 0x56, 0x07 }, { 0x57, 0x0a }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 },                      { 0x70, 0x01 }, { 0x71, 0x02 }, { 0x72, 0x03 }, { 0x73, 0x04 },                      { 0x74, 0x00 }, { 0x75, 0x0e }, { 0x76, 0x3f },                      { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, @@ -2559,53 +2161,27 @@ void genesys_init_sensor_tables()                      { 0x80, 0x20 },                  }, {}              }, -            { { 800 }, 4800, 1, 14400, { ScanMethod::TRANSPARENCY, -                                         ScanMethod::TRANSPARENCY_INFRARED }, { -                    { 0x16, 0x33 }, -                    { 0x17, 0x0c }, -                    { 0x18, 0x13 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x84 }, -                    { 0x1e, 0xa0 }, -                    { 0x52, 0x0d }, -                    { 0x53, 0x10 }, -                    { 0x54, 0x01 }, -                    { 0x55, 0x04 }, -                    { 0x56, 0x07 }, -                    { 0x57, 0x0a }, -                    { 0x58, 0x6b }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, -                    { 0x70, 0x01 }, { 0x71, 0x02 }, { 0x72, 0x03 }, { 0x73, 0x04 }, +            {   { 800 }, 4800, Ratio{1, 4}, 14400, 5, 13, { ScanMethod::TRANSPARENCY, +                                                            ScanMethod::TRANSPARENCY_INFRARED }, +                StaggerConfig{}, { +                    { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x13 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x84 }, { 0x1e, 0xa0 }, +                    { 0x52, 0x0d }, { 0x53, 0x10 }, { 0x54, 0x01 }, { 0x55, 0x04 }, +                    { 0x56, 0x07 }, { 0x57, 0x0a }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +                    { 0x70, 0x00 }, { 0x71, 0x01 }, { 0x72, 0x02 }, { 0x73, 0x03 },                      { 0x74, 0x00 }, { 0x75, 0x0e }, { 0x76, 0x3f },                      { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 },                      { 0x7a, 0x01 }, { 0x7b, 0xb6 }, { 0x7c, 0xdb },                      { 0x80, 0x20 },                  }, {}              }, -            { { 1600 }, 4800, 1, 28800, { ScanMethod::TRANSPARENCY, -                                          ScanMethod::TRANSPARENCY_INFRARED }, { -                    { 0x16, 0x33 }, -                    { 0x17, 0x0c }, -                    { 0x18, 0x11 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x00 }, -                    { 0x1d, 0x84 }, -                    { 0x1e, 0xa0 }, -                    { 0x52, 0x0b }, -                    { 0x53, 0x0e }, -                    { 0x54, 0x11 }, -                    { 0x55, 0x02 }, -                    { 0x56, 0x05 }, -                    { 0x57, 0x08 }, -                    { 0x58, 0x63 }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, +            {   { 1600 }, 4800, Ratio{1, 2}, 28800, 10, 8, { ScanMethod::TRANSPARENCY, +                                                             ScanMethod::TRANSPARENCY_INFRARED }, +                StaggerConfig{}, { +                    { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x11 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x84 }, { 0x1e, 0xa0 }, +                    { 0x52, 0x0b }, { 0x53, 0x0e }, { 0x54, 0x11 }, { 0x55, 0x02 }, +                    { 0x56, 0x05 }, { 0x57, 0x08 }, { 0x58, 0x63 }, { 0x59, 0x00 }, { 0x5a, 0x40 },                      { 0x70, 0x00 }, { 0x71, 0x01 }, { 0x72, 0x02 }, { 0x73, 0x03 },                      { 0x74, 0x00 }, { 0x75, 0x01 }, { 0x76, 0xff },                      { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, @@ -2615,26 +2191,13 @@ void genesys_init_sensor_tables()                      { 0x03, 0x1f },                  },              }, -            { { 3200 }, 4800, 1, 28800, { ScanMethod::TRANSPARENCY, -                                          ScanMethod::TRANSPARENCY_INFRARED }, { -                    { 0x16, 0x33 }, -                    { 0x17, 0x0c }, -                    { 0x18, 0x10 }, -                    { 0x19, 0x2a }, -                    { 0x1a, 0x30 }, -                    { 0x1b, 0x00 }, -                    { 0x1c, 0x20 }, -                    { 0x1d, 0x84 }, -                    { 0x1e, 0xa0 }, -                    { 0x52, 0x02 }, -                    { 0x53, 0x05 }, -                    { 0x54, 0x08 }, -                    { 0x55, 0x0b }, -                    { 0x56, 0x0e }, -                    { 0x57, 0x11 }, -                    { 0x58, 0x1b }, -                    { 0x59, 0x00 }, -                    { 0x5a, 0x40 }, +            {   { 3200 }, 4800, Ratio{1, 1}, 28800, 20, 10, { ScanMethod::TRANSPARENCY, +                                                              ScanMethod::TRANSPARENCY_INFRARED }, +                StaggerConfig{0, 6}, { +                    { 0x16, 0x33 }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x84 }, { 0x1e, 0xa0 }, +                    { 0x52, 0x02 }, { 0x53, 0x05 }, { 0x54, 0x08 }, { 0x55, 0x0b }, +                    { 0x56, 0x0e }, { 0x57, 0x11 }, { 0x58, 0x1b }, { 0x59, 0x00 }, { 0x5a, 0x40 },                      { 0x70, 0x09 }, { 0x71, 0x0a }, { 0x72, 0x0b }, { 0x73, 0x0c },                      { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 },                      { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, @@ -2648,51 +2211,68 @@ void genesys_init_sensor_tables()          for (const CustomSensorSettings& setting : custom_settings)          { -            for (auto method : setting.methods) { -                sensor.resolutions = setting.resolutions; -                sensor.dpiset_override = setting.dpiset_override; -                sensor.pixel_count_multiplier = setting.pixel_count_multiplier; -                sensor.exposure_lperiod = setting.exposure_lperiod; -                sensor.method = method; -                sensor.custom_regs = setting.extra_custom_regs; -                sensor.custom_fe_regs = setting.custom_fe_regs; -                s_sensors->push_back(sensor); +            for (auto method : setting.methods) +                {for (auto resolution : setting.resolutions.values()) { +                    sensor.resolutions = { resolution }; +                    sensor.shading_resolution = resolution; +                    sensor.register_dpiset = setting.register_dpiset; +                    sensor.pixel_count_ratio = setting.pixel_count_ratio; +                    sensor.exposure_lperiod = setting.exposure_lperiod; +                    sensor.output_pixel_offset = setting.output_pixel_offset; +                    sensor.shading_pixel_offset = setting.shading_pixel_offset; +                    sensor.method = method; +                    sensor.stagger_y = setting.stagger_y; +                    sensor.custom_regs = setting.extra_custom_regs; +                    sensor.custom_fe_regs = setting.custom_fe_regs; +                    s_sensors->push_back(sensor); +                }              }          }      }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_CANON_8600F; -    sensor.optical_res = 4800; -    sensor.ccd_size_divisor = 4; +    sensor.sensor_id = SensorId::CCD_CANON_8600F; // gl843 +    sensor.full_resolution = 4800; +    sensor.register_dpihw = 4800;      sensor.black_pixels = 31;      sensor.dummy_pixel = 20; -    sensor.ccd_start_xoffset = 0; // not used at the moment -    // 11372 pixels at 1200 dpi -    sensor.sensor_pixels = 11372*4;      sensor.fau_gain_white_ref = 160;      sensor.gain_white_ref = 160;      sensor.exposure = { 0x9c40, 0x9c40, 0x9c40 }; -    sensor.stagger_config = StaggerConfig{4800, 8};      sensor.custom_regs = {};      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = get_sensor_optical_with_ccd_divisor; -    sensor.get_register_hwdpi_fun = [](const Genesys_Sensor&, unsigned) { return 4800; }; -    sensor.get_hwdpi_divisor_fun = [](const Genesys_Sensor&, unsigned) { return 1; }; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions; +            unsigned optical_resolution; +            unsigned register_dpiset;              int exposure_lperiod; +            int output_pixel_offset;              std::vector<ScanMethod> methods; +            StaggerConfig stagger_y;              GenesysRegisterSettingSet extra_custom_regs;              GenesysRegisterSettingSet custom_fe_regs;          };          CustomSensorSettings custom_settings[] = { -            {   { 300, 600, 1200 }, 24000, { ScanMethod::FLATBED }, { +            {   { 300 }, 1200, 1200, 24000, 1, { ScanMethod::FLATBED }, StaggerConfig{}, { +                    { 0x0c, 0x00 }, +                    { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, +                    { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, +                    { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, +                    { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +                    { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 }, +                    { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, +                    { 0x9e, 0x2d }, +                    { 0xaa, 0x00 }, +                }, +                {}, +            }, +            {   { 600 }, 1200, 2400, 24000, 2, { ScanMethod::FLATBED }, StaggerConfig{}, {                      { 0x0c, 0x00 },                      { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, @@ -2707,8 +2287,24 @@ void genesys_init_sensor_tables()                  },                  {},              }, -            {   { 300, 600, 1200 }, 45000, { ScanMethod::TRANSPARENCY, -                                             ScanMethod::TRANSPARENCY_INFRARED }, { +            {   { 1200 }, 1200, 4800, 24000, 5, { ScanMethod::FLATBED }, StaggerConfig{}, { +                    { 0x0c, 0x00 }, +                    { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, +                    { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, +                    { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, +                    { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +                    { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 }, +                    { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, +                    { 0x9e, 0x2d }, +                    { 0xaa, 0x00 }, +                }, +                {}, +            }, +            {   { 300 }, 1200, 1200, 45000, 6, { ScanMethod::TRANSPARENCY, +                                                 ScanMethod::TRANSPARENCY_INFRARED }, +                StaggerConfig{}, {                      { 0x0c, 0x00 },                      { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, @@ -2723,8 +2319,43 @@ void genesys_init_sensor_tables()                  },                  {},              }, -            {   { 2400 }, 45000, { ScanMethod::TRANSPARENCY, -                                   ScanMethod::TRANSPARENCY_INFRARED }, { +            {   { 600 }, 1200, 2400, 45000, 11, { ScanMethod::TRANSPARENCY, +                                                  ScanMethod::TRANSPARENCY_INFRARED }, +                StaggerConfig{}, { +                    { 0x0c, 0x00 }, +                    { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, +                    { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, +                    { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +                    { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, +                    { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 }, +                    { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, +                    { 0x9e, 0x2d }, +                    { 0xaa, 0x00 }, +                }, +                {}, +            }, +            {   { 1200 }, 1200, 4800, 45000, 23, { ScanMethod::TRANSPARENCY, +                                                   ScanMethod::TRANSPARENCY_INFRARED }, +                StaggerConfig{}, { +                    { 0x0c, 0x00 }, +                    { 0x16, 0x13 }, { 0x17, 0x0a }, { 0x18, 0x10 }, { 0x19, 0x2a }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x6b }, +                    { 0x52, 0x0c }, { 0x53, 0x0f }, { 0x54, 0x00 }, { 0x55, 0x03 }, +                    { 0x56, 0x06 }, { 0x57, 0x09 }, { 0x58, 0x6b }, { 0x59, 0x00 }, { 0x5a, 0x40 }, +                    { 0x70, 0x00 }, { 0x71, 0x02 }, { 0x72, 0x02 }, { 0x73, 0x04 }, +                    { 0x74, 0x03 }, { 0x75, 0xf0 }, { 0x76, 0xf0 }, +                    { 0x77, 0x03 }, { 0x78, 0xfe }, { 0x79, 0x00 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x92 }, { 0x7c, 0x49 }, +                    { 0x9e, 0x2d }, +                    { 0xaa, 0x00 }, +                }, +                {}, +            }, +            {   { 2400 }, 2400, 4800, 45000, 10, { ScanMethod::TRANSPARENCY, +                                                   ScanMethod::TRANSPARENCY_INFRARED }, +                StaggerConfig{}, {                      { 0x0c, 0x00 },                      { 0x16, 0x13 }, { 0x17, 0x15 }, { 0x18, 0x10 }, { 0x19, 0x2a },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x01 }, { 0x1d, 0x75 }, @@ -2739,8 +2370,9 @@ void genesys_init_sensor_tables()                  },                  {},              }, -            {   { 4800 }, 45000, { ScanMethod::TRANSPARENCY, -                                   ScanMethod::TRANSPARENCY_INFRARED }, { +            {   { 4800 }, 4800, 4800, 45000, -1982, { ScanMethod::TRANSPARENCY, +                                                      ScanMethod::TRANSPARENCY_INFRARED }, +                StaggerConfig{8, 0}, {                      { 0x0c, 0x00 },                      { 0x16, 0x13 }, { 0x17, 0x15 }, { 0x18, 0x10 }, { 0x19, 0x2a },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x61 }, { 0x1d, 0x75 }, @@ -2760,25 +2392,30 @@ void genesys_init_sensor_tables()          for (const CustomSensorSettings& setting : custom_settings) {              for (auto method : setting.methods) { -                sensor.resolutions = setting.resolutions; -                sensor.method = method; -                sensor.exposure_lperiod = setting.exposure_lperiod; -                sensor.custom_regs = setting.extra_custom_regs; -                sensor.custom_fe_regs = setting.custom_fe_regs; -                s_sensors->push_back(sensor); +                for (auto resolution : setting.resolutions.values()) { +                    sensor.resolutions = { resolution }; +                    sensor.optical_resolution = setting.optical_resolution; +                    sensor.register_dpiset = setting.register_dpiset; +                    sensor.shading_resolution = resolution; +                    sensor.output_pixel_offset = setting.output_pixel_offset; +                    sensor.method = method; +                    sensor.exposure_lperiod = setting.exposure_lperiod; +                    sensor.stagger_y = setting.stagger_y; +                    sensor.custom_regs = setting.extra_custom_regs; +                    sensor.custom_fe_regs = setting.custom_fe_regs; +                    s_sensors->push_back(sensor); +                }              }          }      }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_HP_N6310; -    sensor.optical_res = 2400; -    // sensor.ccd_size_divisor = 2; Possibly half CCD, needs checking +    sensor.sensor_id = SensorId::CCD_HP_N6310; // gl847 +    sensor.full_resolution = 2400;      sensor.black_pixels = 96;      sensor.dummy_pixel = 26; -    sensor.ccd_start_xoffset = 128; -    sensor.sensor_pixels = 42720; +    sensor.pixel_count_ratio = Ratio{1, 4};      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 230;      sensor.exposure = { 0x0000, 0x0000, 0x0000 }; @@ -2802,41 +2439,102 @@ void genesys_init_sensor_tables()          { 0x5a, 0x40 },      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi; -    s_sensors->push_back(sensor); +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpihw; +            unsigned shading_factor; +            int output_pixel_offset; +        }; +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 600, 8, 4 }, +            { { 100 }, 600, 6, 5 }, +            { { 150 }, 600, 4, 8 }, +            { { 200 }, 600, 3, 10 }, +            { { 300 }, 600, 2, 16 }, +            { { 600 }, 600, 1, 32 }, +            { { 1200 }, 1200, 1, 64 }, +            { { 2400 }, 2400, 1, 128 }, +        }; + +        auto base_custom_regs = sensor.custom_regs; +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.register_dpiset = setting.resolutions.values()[0]; +            sensor.register_dpihw = setting.register_dpihw; +            sensor.shading_resolution = setting.register_dpihw; +            sensor.shading_factor = setting.shading_factor; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            s_sensors->push_back(sensor); +        } +    }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CIS_CANON_LIDE_110; -    sensor.optical_res = 2400; -    sensor.ccd_size_divisor = 2; +    sensor.sensor_id = SensorId::CIS_CANON_LIDE_110; // gl124 +    sensor.full_resolution = 2400;      sensor.black_pixels = 87;      sensor.dummy_pixel = 16; -    sensor.ccd_start_xoffset = 303; -    sensor.sensor_pixels = 5168*4;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.exposure = { 0x0000, 0x0000, 0x0000 };      sensor.gamma = { 2.2f, 2.2f, 2.2f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_gl124;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions; +            unsigned optical_resolution; +            unsigned register_dpihw; +            unsigned register_dpiset; +            unsigned shading_resolution;              int exposure_lperiod;              SensorExposure exposure; +            Ratio pixel_count_ratio; +            unsigned shading_factor;              std::vector<unsigned> segment_order;              GenesysRegisterSettingSet custom_regs;          };          CustomSensorSettings custom_settings[] = { -            {   { 75, 100, 150 }, 4608, { 462, 609, 453 }, std::vector<unsigned>{}, { +            {   { 75 }, 1200, 600, 150, 300, 4608, { 462, 609, 453 }, Ratio{1, 4}, 4, +                std::vector<unsigned>{}, { +                    { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, +                    { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, +                    { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, +                    { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, +                    { 0x61, 0x20 }, +                    { 0x70, 0x06 }, { 0x71, 0x08 }, { 0x72, 0x08 }, { 0x73, 0x0a }, +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                    { 0x88, 0x00 }, { 0x89, 0x65 }, +                    { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, +                    { 0x96, 0x00 }, { 0x97, 0x9a }, +                    { 0x98, 0x21 }, +                } +            }, +            {   { 100 }, 1200, 600, 200, 300, 4608, { 462, 609, 453 }, Ratio{1, 4}, 3, +                std::vector<unsigned>{}, { +                    { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, +                    { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, +                    { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, +                    { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, +                    { 0x61, 0x20 }, +                    { 0x70, 0x06 }, { 0x71, 0x08 }, { 0x72, 0x08 }, { 0x73, 0x0a }, +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                    { 0x88, 0x00 }, { 0x89, 0x65 }, +                    { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, +                    { 0x96, 0x00 }, { 0x97, 0x9a }, +                    { 0x98, 0x21 }, +                } +            }, +            {   { 150 }, 1200, 600, 300, 300, 4608, { 462, 609, 453 }, Ratio{1, 4}, 2, +                std::vector<unsigned>{}, {                      { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c },                      { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, @@ -2853,7 +2551,8 @@ void genesys_init_sensor_tables()                      { 0x98, 0x21 },                  }              }, -            {   { 300 }, 4608, { 462, 609, 453 }, std::vector<unsigned>{}, { +            {   { 300 }, 1200, 600, 600, 300, 4608, { 462, 609, 453 }, Ratio{1, 4}, 1, +                std::vector<unsigned>{}, {                      { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x0c },                      { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, @@ -2870,7 +2569,8 @@ void genesys_init_sensor_tables()                      { 0x98, 0x21 },                  }              }, -            {   { 600 }, 5360, { 823, 1117, 805 }, std::vector<unsigned>{}, { +            {   { 600 }, 2400, 600, 600, 600, 5360, { 823, 1117, 805 }, Ratio{1, 4}, 1, +                std::vector<unsigned>{}, {                      { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x0a },                      { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, @@ -2887,7 +2587,8 @@ void genesys_init_sensor_tables()                      { 0x98, 0x21 },                  },              }, -            {   { 1200 }, 10528, { 6071, 6670, 6042 }, { 0, 1 }, { +            {   { 1200 }, 2400, 1200, 1200, 1200, 10528, { 6071, 6670, 6042 }, Ratio{1, 4}, 1, +                { 0, 1 }, {                      { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 },{ 0x20, 0x08 },                      { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, @@ -2904,7 +2605,8 @@ void genesys_init_sensor_tables()                      { 0x98, 0x22 },                  }              }, -            {   { 2400 }, 20864, { 7451, 8661, 7405 }, { 0, 2, 1, 3 }, { +            {   { 2400 }, 2400, 2400, 2400, 2400, 20864, { 7451, 8661, 7405 }, Ratio{1, 4}, 1, +                { 0, 2, 1, 3 }, {                      { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x06 },                      { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, @@ -2925,8 +2627,14 @@ void genesys_init_sensor_tables()          for (const auto& setting : custom_settings) {              sensor.resolutions = setting.resolutions; +            sensor.optical_resolution = setting.optical_resolution; +            sensor.register_dpihw = setting.register_dpihw; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.shading_resolution = setting.shading_resolution;              sensor.exposure_lperiod = setting.exposure_lperiod;              sensor.exposure = setting.exposure; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.shading_factor = setting.shading_factor;              sensor.segment_order = setting.segment_order;              sensor.custom_regs = setting.custom_regs;              s_sensors->push_back(sensor); @@ -2934,34 +2642,69 @@ void genesys_init_sensor_tables()      }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CIS_CANON_LIDE_120; -    sensor.optical_res = 2400; -    sensor.ccd_size_divisor = 2; +    sensor.sensor_id = SensorId::CIS_CANON_LIDE_120; // gl124 +    sensor.full_resolution = 2400;      sensor.black_pixels = 87;      sensor.dummy_pixel = 16; -    sensor.ccd_start_xoffset = 303; -    // SEGCNT at 600 DPI by number of segments -    sensor.sensor_pixels = 5104*4;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.exposure = { 0x0000, 0x0000, 0x0000 };      sensor.gamma = { 2.2f, 2.2f, 2.2f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_gl124;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions; +            unsigned optical_resolution; +            unsigned register_dpihw; +            unsigned register_dpiset; +            unsigned shading_resolution;              int exposure_lperiod;              SensorExposure exposure; +            Ratio pixel_count_ratio; +            unsigned shading_factor;              std::vector<unsigned> segment_order;              GenesysRegisterSettingSet custom_regs;          };          CustomSensorSettings custom_settings[] = { -            {   { 75, 100, 150, 300 }, 4608, { 1244, 1294, 1144 }, std::vector<unsigned>{}, { +            {   { 75 }, 1200, 600, 150, 300, 4608, { 1244, 1294, 1144 }, Ratio{1, 4}, 4, +                std::vector<unsigned>{}, { +                    { 0x16, 0x15 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 }, +                    { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 }, +                    { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, +                    { 0x5a, 0x3a }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, +                    { 0x61, 0x20 }, +                    { 0x70, 0x00 }, { 0x71, 0x1f }, { 0x72, 0x08 }, { 0x73, 0x0a }, +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                    { 0x88, 0x00 }, { 0x89, 0x5e }, +                    { 0x93, 0x00 }, { 0x94, 0x09 }, { 0x95, 0xf8 }, +                    { 0x96, 0x00 }, { 0x97, 0x70 }, +                    { 0x98, 0x21 }, +                }, +            }, +            {   { 100 }, 1200, 600, 200, 300, 4608, { 1244, 1294, 1144 }, Ratio{1, 4}, 3, +                std::vector<unsigned>{}, { +                    { 0x16, 0x15 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 }, +                    { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 }, +                    { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, +                    { 0x5a, 0x3a }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, +                    { 0x61, 0x20 }, +                    { 0x70, 0x00 }, { 0x71, 0x1f }, { 0x72, 0x08 }, { 0x73, 0x0a }, +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                    { 0x88, 0x00 }, { 0x89, 0x5e }, +                    { 0x93, 0x00 }, { 0x94, 0x09 }, { 0x95, 0xf8 }, +                    { 0x96, 0x00 }, { 0x97, 0x70 }, +                    { 0x98, 0x21 }, +                }, +            }, +            {   { 150 }, 1200, 600, 300, 300, 4608, { 1244, 1294, 1144 }, Ratio{1, 4}, 2, +                std::vector<unsigned>{}, {                      { 0x16, 0x15 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 },                      { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 }, @@ -2978,7 +2721,26 @@ void genesys_init_sensor_tables()                      { 0x98, 0x21 },                  },              }, -            {   { 600 }, 5360, { 2394, 2444, 2144 }, std::vector<unsigned>{}, { +            {   { 300 }, 1200, 600, 600, 300, 4608, { 1244, 1294, 1144 }, Ratio{1, 4}, 1, +                std::vector<unsigned>{}, { +                    { 0x16, 0x15 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 }, +                    { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 }, +                    { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, +                    { 0x5a, 0x3a }, { 0x5b, 0x00 }, { 0x5c, 0x00 }, +                    { 0x61, 0x20 }, +                    { 0x70, 0x00 }, { 0x71, 0x1f }, { 0x72, 0x08 }, { 0x73, 0x0a }, +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                    { 0x88, 0x00 }, { 0x89, 0x5e }, +                    { 0x93, 0x00 }, { 0x94, 0x09 }, { 0x95, 0xf8 }, +                    { 0x96, 0x00 }, { 0x97, 0x70 }, +                    { 0x98, 0x21 }, +                }, +            }, +            {   { 600 }, 2400, 600, 600, 600, 5360, { 2394, 2444, 2144 }, Ratio{1, 4}, 1, +                std::vector<unsigned>{}, {                      { 0x16, 0x11 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 },                      { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 }, @@ -2995,7 +2757,8 @@ void genesys_init_sensor_tables()                      { 0x98, 0x21 },                  },              }, -            {   { 1200 }, 10528, { 4694, 4644, 4094 }, std::vector<unsigned>{}, { +            {   { 1200 }, 2400, 1200, 1200, 1200, 10528, { 4694, 4644, 4094 }, Ratio{1, 2}, 1, +                std::vector<unsigned>{}, {                      { 0x16, 0x15 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 },                      { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 }, @@ -3012,7 +2775,8 @@ void genesys_init_sensor_tables()                      { 0x98, 0x21 },                  },              }, -            {   { 2400 }, 20864, { 8944, 8144, 7994 }, std::vector<unsigned>{}, { +            {   { 2400 }, 2400, 2400, 2400, 2400, 20864, { 8944, 8144, 7994 }, Ratio{1, 1}, 1, +                std::vector<unsigned>{}, {                      { 0x16, 0x11 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x00 }, { 0x20, 0x02 },                      { 0x52, 0x04 }, { 0x53, 0x06 }, { 0x54, 0x00 }, { 0x55, 0x02 }, @@ -3033,8 +2797,14 @@ void genesys_init_sensor_tables()          for (const auto& setting : custom_settings) {              sensor.resolutions = setting.resolutions; +            sensor.optical_resolution = setting.optical_resolution; +            sensor.register_dpihw = setting.register_dpihw; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.shading_resolution = setting.shading_resolution;              sensor.exposure_lperiod = setting.exposure_lperiod;              sensor.exposure = setting.exposure; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.shading_factor = setting.shading_factor;              sensor.segment_order = setting.segment_order;              sensor.custom_regs = setting.custom_regs;              s_sensors->push_back(sensor); @@ -3042,33 +2812,71 @@ void genesys_init_sensor_tables()      }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CIS_CANON_LIDE_210; -    sensor.optical_res = 2400; -    sensor.ccd_size_divisor = 2; +    sensor.sensor_id = SensorId::CIS_CANON_LIDE_210; // gl124 +    sensor.full_resolution = 4800;      sensor.black_pixels = 87;      sensor.dummy_pixel = 16; -    sensor.ccd_start_xoffset = 303; -    sensor.sensor_pixels = 5168*4;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.exposure = { 0x0000, 0x0000, 0x0000 };      sensor.gamma = { 2.2f, 2.2f, 2.2f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_gl124;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions; +            unsigned optical_resolution; +            unsigned register_dpihw; +            unsigned register_dpiset; +            unsigned shading_resolution;              int exposure_lperiod;              SensorExposure exposure; +            Ratio pixel_count_ratio; +            unsigned shading_factor;              std::vector<unsigned> segment_order;              GenesysRegisterSettingSet custom_regs;          };          CustomSensorSettings custom_settings[] = { -            {   { 75, 100, 150, 300 }, 2768, { 388, 574, 393 }, std::vector<unsigned>{}, { +            {   { 75 }, 2400, 600, 150, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 4, +                std::vector<unsigned>{}, { +                    // { 0x16, 0x00 }, // FIXME: check if default value is different +                    { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, +                    { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, +                    { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, +                    { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, +                    { 0x61, 0x20 }, +                    // { 0x70, 0x00 }, // FIXME: check if default value is different +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                    { 0x88, 0x00 }, { 0x89, 0x65 }, +                    { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, +                    { 0x96, 0x00 }, { 0x97, 0x9a }, +                    { 0x98, 0x21 }, +                } +            }, +            {   { 100 }, 2400, 600, 200, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 3, +                std::vector<unsigned>{}, { +                    // { 0x16, 0x00 }, // FIXME: check if default value is different +                    { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, +                    { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, +                    { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, +                    { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, +                    { 0x61, 0x20 }, +                    // { 0x70, 0x00 }, // FIXME: check if default value is different +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                    { 0x88, 0x00 }, { 0x89, 0x65 }, +                    { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, +                    { 0x96, 0x00 }, { 0x97, 0x9a }, +                    { 0x98, 0x21 }, +                } +            }, +            {   { 150 }, 2400, 600, 300, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 2, +                std::vector<unsigned>{}, {                      // { 0x16, 0x00 }, // FIXME: check if default value is different                      { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, @@ -3086,7 +2894,27 @@ void genesys_init_sensor_tables()                      { 0x98, 0x21 },                  }              }, -            {   { 600 }, 5360, { 388, 574, 393 }, std::vector<unsigned>{}, { +            {   { 300 }, 2400, 600, 600, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 1, +                std::vector<unsigned>{}, { +                    // { 0x16, 0x00 }, // FIXME: check if default value is different +                    { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, +                    { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, +                    { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, +                    { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, +                    { 0x61, 0x20 }, +                    // { 0x70, 0x00 }, // FIXME: check if default value is different +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                    { 0x88, 0x00 }, { 0x89, 0x65 }, +                    { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, +                    { 0x96, 0x00 }, { 0x97, 0x9a }, +                    { 0x98, 0x21 }, +                } +            }, +            {   { 600 }, 4800, 600, 600, 600, 5360, { 388, 574, 393 }, Ratio{1, 8}, 1, +                std::vector<unsigned>{}, {                      // { 0x16, 0x00 }, // FIXME: check if default value is different                      { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0a }, @@ -3104,7 +2932,7 @@ void genesys_init_sensor_tables()                      { 0x98, 0x21 },                  }              }, -            {   { 1200 }, 10528, { 388, 574, 393 }, {0, 1}, { +            {   { 1200 }, 4800, 1200, 1200, 1200, 10528, { 388, 574, 393 }, Ratio{1, 8}, 1, {0, 1}, {                      // { 0x16, 0x00 }, // FIXME: check if default value is different                      { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x08 }, @@ -3122,7 +2950,8 @@ void genesys_init_sensor_tables()                      { 0x98, 0x22 },                  },              }, -            {   { 2400 }, 20864, { 6839, 8401, 6859 }, {0, 2, 1, 3}, { +            {   { 2400 }, 4800, 2400, 2400, 2400, 20864, { 6839, 8401, 6859 }, Ratio{1, 8}, 1, +                {0, 2, 1, 3}, {                      // { 0x16, 0x00 }, // FIXME: check if default value is different                      { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x06 }, @@ -3139,13 +2968,38 @@ void genesys_init_sensor_tables()                      { 0x96, 0x00 }, { 0x97, 0xa3 },                      { 0x98, 0x24 },                  }, +            }, +            {   { 4800 }, 4800, 4800, 4800, 4800, 41536, { 9735, 14661, 11345 }, Ratio{1, 8}, 1, +                { 0, 2, 4, 6, 1, 3, 5, 7 }, { +                    // { 0x16, 0x00 }, // FIXME: check if default value is different +                    { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x04 }, +                    { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, +                    { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, +                    { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, +                    { 0x61, 0x20 }, +                    // { 0x70, 0x00 }, // FIXME: check if default value is different +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x1e }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                    { 0x88, 0x12 }, { 0x89, 0x47 }, +                    { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 }, +                    { 0x96, 0x00 }, { 0x97, 0xa5 }, +                    { 0x98, 0x28 }, +                },              }          };          for (const auto& setting : custom_settings) {              sensor.resolutions = setting.resolutions; +            sensor.optical_resolution = setting.optical_resolution; +            sensor.register_dpihw = setting.register_dpihw; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.shading_resolution = setting.shading_resolution;              sensor.exposure_lperiod = setting.exposure_lperiod;              sensor.exposure = setting.exposure; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.shading_factor = setting.shading_factor;              sensor.segment_order = setting.segment_order;              sensor.custom_regs = setting.custom_regs;              s_sensors->push_back(sensor); @@ -3153,33 +3007,33 @@ void genesys_init_sensor_tables()      }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CIS_CANON_LIDE_220; -    sensor.optical_res = 2400; -    sensor.ccd_size_divisor = 2; +    sensor.sensor_id = SensorId::CIS_CANON_LIDE_220; // gl124 +    sensor.full_resolution = 4800;      sensor.black_pixels = 87;      sensor.dummy_pixel = 16; -    sensor.ccd_start_xoffset = 303; -    sensor.sensor_pixels = 5168*4;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.exposure = { 0x0000, 0x0000, 0x0000 };      sensor.gamma = { 2.2f, 2.2f, 2.2f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_gl124;      {          struct CustomSensorSettings { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions; +            unsigned optical_resolution; +            unsigned register_dpihw; +            unsigned register_dpiset; +            unsigned shading_resolution;              int exposure_lperiod;              SensorExposure exposure; +            Ratio pixel_count_ratio; +            unsigned shading_factor;              std::vector<unsigned> segment_order;              GenesysRegisterSettingSet custom_regs;          };          CustomSensorSettings custom_settings[] = { -            {   { 75, 100, 150, 300 }, 2768, { 388, 574, 393 }, std::vector<unsigned>{}, { +            {   { 75 }, 2400, 600, 150, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 4, +                std::vector<unsigned>{}, {                      // { 0x16, 0x00 }, // FIXME: check if default value is different                      { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, @@ -3197,7 +3051,65 @@ void genesys_init_sensor_tables()                      { 0x98, 0x21 },                  }              }, -            {   { 600 }, 5360, { 388, 574, 393 }, std::vector<unsigned>{}, { +            {   { 100 }, 2400, 600, 200, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 3, +                std::vector<unsigned>{}, { +                    // { 0x16, 0x00 }, // FIXME: check if default value is different +                    { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, +                    { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, +                    { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, +                    { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, +                    { 0x61, 0x20 }, +                    // { 0x70, 0x00 }, // FIXME: check if default value is different +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                    { 0x88, 0x00 }, { 0x89, 0x65 }, +                    { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, +                    { 0x96, 0x00 }, { 0x97, 0x9a }, +                    { 0x98, 0x21 }, +                } +            }, +            {   { 150 }, 2400, 600, 300, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 2, +                std::vector<unsigned>{}, { +                    // { 0x16, 0x00 }, // FIXME: check if default value is different +                    { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, +                    { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, +                    { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, +                    { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, +                    { 0x61, 0x20 }, +                    // { 0x70, 0x00 }, // FIXME: check if default value is different +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                    { 0x88, 0x00 }, { 0x89, 0x65 }, +                    { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, +                    { 0x96, 0x00 }, { 0x97, 0x9a }, +                    { 0x98, 0x21 }, +                } +            }, +            {   { 300 }, 2400, 600, 600, 300, 2768, { 388, 574, 393 }, Ratio{1, 8}, 1, +                std::vector<unsigned>{}, { +                    // { 0x16, 0x00 }, // FIXME: check if default value is different +                    { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0c }, +                    { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, +                    { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, +                    { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, +                    { 0x61, 0x20 }, +                    // { 0x70, 0x00 }, // FIXME: check if default value is different +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                    { 0x88, 0x00 }, { 0x89, 0x65 }, +                    { 0x93, 0x00 }, { 0x94, 0x0a }, { 0x95, 0x18 }, +                    { 0x96, 0x00 }, { 0x97, 0x9a }, +                    { 0x98, 0x21 }, +                } +            }, +            {   { 600 }, 4800, 600, 600, 600, 5360, { 388, 574, 393 }, Ratio{1, 8}, 1, +                std::vector<unsigned>{}, {                      // { 0x16, 0x00 }, // FIXME: check if default value is different                      { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x0a }, @@ -3215,7 +3127,8 @@ void genesys_init_sensor_tables()                      { 0x98, 0x21 },                  }              }, -            {   { 1200 }, 10528, { 388, 574, 393 }, {0, 1}, { +            {   { 1200 }, 4800, 1200, 1200, 1200, 10528, { 388, 574, 393 }, Ratio{1, 8}, 1, +                {0, 1}, {                      // { 0x16, 0x00 }, // FIXME: check if default value is different                      { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x08 }, @@ -3233,7 +3146,8 @@ void genesys_init_sensor_tables()                      { 0x98, 0x22 },                  }              }, -            {   { 2400 }, 20864, { 6839, 8401, 6859 }, {0, 2, 1, 3}, { +            {   { 2400 }, 4800, 2400, 2400, 2400, 20864, { 6839, 8401, 6859 }, Ratio{1, 8}, 1, +                {0, 2, 1, 3}, {                      // { 0x16, 0x00 }, // FIXME: check if default value is different                      { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 },                      { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x06 }, @@ -3250,13 +3164,38 @@ void genesys_init_sensor_tables()                      { 0x96, 0x00 }, { 0x97, 0xa3 },                      { 0x98, 0x24 },                  }, +            }, +            {   { 4800 }, 4800, 4800, 4800, 4800, 41536, { 9735, 14661, 11345 }, Ratio{1, 8}, 1, +                { 0, 2, 4, 6, 1, 3, 5, 7 }, { +                    // { 0x16, 0x00 }, // FIXME: check if default value is different +                    { 0x16, 0x10 }, { 0x17, 0x04 }, { 0x18, 0x00 }, { 0x19, 0x01 }, +                    { 0x1a, 0x30 }, { 0x1b, 0x00 }, { 0x1c, 0x02 }, { 0x1d, 0x01 }, { 0x20, 0x04 }, +                    { 0x52, 0x00 }, { 0x53, 0x02 }, { 0x54, 0x04 }, { 0x55, 0x06 }, +                    { 0x56, 0x04 }, { 0x57, 0x04 }, { 0x58, 0x04 }, { 0x59, 0x04 }, +                    { 0x5a, 0x1a }, { 0x5b, 0x00 }, { 0x5c, 0xc0 }, +                    { 0x61, 0x20 }, +                    // { 0x70, 0x00 }, // FIXME: check if default value is different +                    { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x0f }, +                    { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, +                    { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +                    { 0x88, 0x12 }, { 0x89, 0x47 }, +                    { 0x93, 0x00 }, { 0x94, 0x14 }, { 0x95, 0x30 }, +                    { 0x96, 0x00 }, { 0x97, 0xa5 }, +                    { 0x98, 0x28 }, +                },              }          };          for (const auto& setting : custom_settings) {              sensor.resolutions = setting.resolutions; +            sensor.optical_resolution = setting.optical_resolution; +            sensor.register_dpihw = setting.register_dpihw; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.shading_resolution = setting.shading_resolution;              sensor.exposure_lperiod = setting.exposure_lperiod;              sensor.exposure = setting.exposure; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.shading_factor = setting.shading_factor;              sensor.segment_order = setting.segment_order;              sensor.custom_regs = setting.custom_regs;              s_sensors->push_back(sensor); @@ -3264,63 +3203,116 @@ void genesys_init_sensor_tables()      }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICPRO_3600; -    sensor.optical_res = 1200; -    sensor.ccd_size_divisor = 2; +    sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICPRO_3600; // gl841 +    sensor.full_resolution = 1200;      sensor.black_pixels = 87;      sensor.dummy_pixel = 87; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 10100;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 230;      sensor.exposure = { 0x0000, 0x0000, 0x0000 };      sensor.custom_regs = { -        { 0x08, 0x00 }, -        { 0x09, 0x00 }, -        { 0x0a, 0x00 }, -        { 0x0b, 0x00 }, -        { 0x16, 0x33 }, -        { 0x17, 0x0b }, -        { 0x18, 0x11 }, -        { 0x19, 0x2a }, -        { 0x1a, 0x00 }, -        { 0x1b, 0x00 }, -        { 0x1c, 0x00 }, -        { 0x1d, 0xc4 }, -        { 0x52, 0x07 }, // [GB](HI|LOW) not needed for cis -        { 0x53, 0x0a }, -        { 0x54, 0x0c }, -        { 0x55, 0x00 }, -        { 0x56, 0x02 }, -        { 0x57, 0x06 }, -        { 0x58, 0x22 }, -        { 0x59, 0x69 }, -        { 0x5a, 0x40 }, -        { 0x5b, 0x00 }, // TODO: 5b-5e -        { 0x5c, 0x00 }, -        { 0x5d, 0x00 }, -        { 0x5e, 0x02 }, +        { 0x16, 0x33 }, { 0x17, 0x0b }, { 0x18, 0x11 }, { 0x19, 0x2a }, +        { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0xc4 }, +        { 0x52, 0x07 }, { 0x53, 0x0a }, { 0x54, 0x0c }, { 0x55, 0x00 }, +        { 0x56, 0x02 }, { 0x57, 0x06 }, { 0x58, 0x22 }, { 0x59, 0x69 }, { 0x5a, 0x40 }, +        { 0x70, 0x00 }, { 0x71, 0x00 }, { 0x72, 0x00 }, { 0x73, 0x00 }, +    }; +    sensor.gamma = { 1.0f, 1.0f, 1.0f }; +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned optical_resolution; +            unsigned register_dpihw; +            unsigned register_dpiset; +            int output_pixel_offset; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 600, 600, 150, 11 }, +            { { 100 }, 600, 600, 200, 14 }, +            { { 150 }, 600, 600, 300, 22 }, +            { { 200 }, 600, 600, 400, 29 }, +            { { 300 }, 600, 600, 600, 44 }, +            { { 600 }, 600, 600, 1200, 88 }, +            { { 1200 }, 1200, 1200, 1200, 88 }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.optical_resolution = setting.optical_resolution; +            sensor.register_dpihw = setting.register_dpihw; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.shading_resolution = setting.register_dpihw; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            s_sensors->push_back(sensor); +        } +    } + + +    sensor = Genesys_Sensor(); +    sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7200; // gl842 +    sensor.full_resolution = 7200; +    sensor.register_dpihw = 1200; +    sensor.black_pixels = 88; // TODO +    sensor.dummy_pixel = 19; +    sensor.fau_gain_white_ref = 210; +    sensor.gain_white_ref = 230; +    sensor.exposure = { 0x2b00, 0x2b00, 0x2b00 }; +    sensor.exposure_lperiod = 0x694e; +    sensor.use_host_side_calib = true; +    sensor.custom_regs = { +        { 0x16, 0x3b }, { 0x17, 0x4b }, { 0x18, 0x10 }, { 0x19, 0x00 }, +        { 0x1a, 0x24 }, { 0x1b, 0x00 }, { 0x1c, 0x40 }, { 0x1d, 0x84 }, +        { 0x52, 0x09 }, { 0x53, 0x0c }, { 0x54, 0x0e }, { 0x55, 0x02 }, +        { 0x56, 0x04 }, { 0x57, 0x07 }, { 0x58, 0x22 }, { 0x59, 0x69 }, { 0x5a, 0xc0 }, +        { 0x70, 0x08 }, { 0x71, 0x09 }, { 0x72, 0x0b }, { 0x73, 0x0c }, +        { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 }, +        { 0x77, 0x00 }, { 0x78, 0x7f }, { 0x79, 0xff }, +        { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, { 0x7d, 0x00 }, { 0x7f, 0x01 }      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi; -    s_sensors->push_back(sensor); +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            ScanMethod method; +            Ratio pixel_count_ratio; +            int output_pixel_offset; +            unsigned register_dpiset; +            StaggerConfig stagger_y; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 900 }, ScanMethod::TRANSPARENCY, Ratio{8, 8}, 2, 150, StaggerConfig{} }, +            { { 1800 }, ScanMethod::TRANSPARENCY, Ratio{4, 4}, 10, 300, StaggerConfig{} }, +            { { 3600 }, ScanMethod::TRANSPARENCY, Ratio{2, 2}, 10, 600, StaggerConfig{} }, +            { { 7200 }, ScanMethod::TRANSPARENCY, Ratio{1, 1}, 20, 1200, StaggerConfig{0, 4} }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.method = setting.method; +            sensor.shading_resolution = setting.resolutions.values().front(); +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.stagger_y = setting.stagger_y; +            s_sensors->push_back(sensor); +        } +    }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7200I; -    sensor.optical_res = 7200; -    sensor.register_dpihw_override = 1200; +    sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7200I; // gl843 +    sensor.full_resolution = 7200; +    sensor.register_dpihw = 1200;      sensor.black_pixels = 88; // TODO      sensor.dummy_pixel = 20; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 10200; // TODO      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 230;      sensor.exposure = { 0x0000, 0x0000, 0x0000 }; -    sensor.stagger_config = StaggerConfig{7200, 4}; +    sensor.use_host_side_calib = true;      sensor.custom_regs = {          { 0x08, 0x00 },          { 0x09, 0x00 }, @@ -3351,47 +3343,53 @@ void genesys_init_sensor_tables()          { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 },      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;      {          struct CustomSensorSettings          { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions;              ScanMethod method; -            unsigned ccd_size_divisor; -            unsigned logical_dpihw_override; -            unsigned pixel_count_multiplier; +            unsigned shading_resolution; +            Ratio pixel_count_ratio; +            int output_pixel_offset;              unsigned exposure_lperiod; -            unsigned dpiset_override; +            unsigned register_dpiset; +            StaggerConfig stagger_y;              GenesysRegisterSettingSet custom_fe_regs;          };          CustomSensorSettings custom_settings[] = { -            { { 900 }, ScanMethod::TRANSPARENCY, 1, 900, 8, 0x2538, 150, {} }, -            { { 1800 }, ScanMethod::TRANSPARENCY, 1, 1800, 4, 0x2538, 300, {} }, -            { { 3600 }, ScanMethod::TRANSPARENCY, 1, 3600, 2, 0x2538, 600, {} }, -            { { 7200 }, ScanMethod::TRANSPARENCY, 1, 7200, 1, 0x19c8, 1200, { +            {   { 900 }, ScanMethod::TRANSPARENCY, 900, Ratio{8, 8}, 2, 0x2538, 150, +                StaggerConfig{}, {} }, +            {   { 1800 }, ScanMethod::TRANSPARENCY, 1800, Ratio{4, 4}, 5, 0x2538, 300, +                StaggerConfig{}, {} }, +            {   { 3600 }, ScanMethod::TRANSPARENCY, 3600, Ratio{2, 2}, 10, 0x2538, 600, +                StaggerConfig{}, {} }, +            {   { 7200 }, ScanMethod::TRANSPARENCY, 7200, Ratio{1, 1}, 20, 0x19c8, 1200, +                StaggerConfig{4, 0}, {                      { 0x02, 0x1b },                      { 0x03, 0x14 },                      { 0x04, 0x20 },                  }              }, -            { { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 900, 8, 0x1f54, 150, {} }, -            { { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 1800, 4, 0x1f54, 300, {} }, -            { { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 3600, 2, 0x1f54, 600, {} }, -            { { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 7200, 1, 0x1f54, 1200, {} }, +            {   { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 900, Ratio{8, 8}, 2, 0x1f54, 150, +                StaggerConfig{}, {} }, +            {   { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 1800, Ratio{4, 4}, 5, 0x1f54, 300, +                StaggerConfig{}, {} }, +            {   { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 3600, Ratio{2, 2}, 10, 0x1f54, 600, +                StaggerConfig{}, {}}, +            {   { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 7200, Ratio{1, 1}, 20, 0x1f54, 1200, +                StaggerConfig{4, 0}, {} },          };          for (const CustomSensorSettings& setting : custom_settings) {              sensor.resolutions = setting.resolutions;              sensor.method = setting.method; -            sensor.ccd_size_divisor = setting.ccd_size_divisor; -            sensor.logical_dpihw_override = setting.logical_dpihw_override; -            sensor.pixel_count_multiplier = setting.pixel_count_multiplier; +            sensor.shading_resolution = setting.shading_resolution; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.output_pixel_offset = setting.output_pixel_offset;              sensor.exposure_lperiod = setting.exposure_lperiod; -            sensor.dpiset_override = setting.dpiset_override; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.stagger_y = setting.stagger_y;              sensor.custom_fe_regs = setting.custom_fe_regs;              s_sensors->push_back(sensor);          } @@ -3399,19 +3397,17 @@ void genesys_init_sensor_tables()      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7300; -    sensor.optical_res = 7200; +    sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7300; // gl843 +    sensor.full_resolution = 7200;      sensor.method = ScanMethod::TRANSPARENCY; -    sensor.register_dpihw_override = 1200; +    sensor.register_dpihw = 1200;      sensor.black_pixels = 88; // TODO      sensor.dummy_pixel = 20; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 10200; // TODO      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 230;      sensor.exposure = { 0x0000, 0x0000, 0x0000 };      sensor.exposure_lperiod = 0x2f44; -    sensor.stagger_config = StaggerConfig{7200, 4}; +    sensor.use_host_side_calib = true;      sensor.custom_regs = {          { 0x08, 0x00 },          { 0x09, 0x00 }, @@ -3442,50 +3438,98 @@ void genesys_init_sensor_tables()          { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 },      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;      {          struct CustomSensorSettings          { -            ResolutionFilter resolutions; -            unsigned ccd_size_divisor; -            unsigned logical_dpihw_override; -            unsigned pixel_count_multiplier; -            unsigned dpiset_override; +            ValueFilterAny<unsigned> resolutions; +            unsigned shading_resolution; +            Ratio pixel_count_ratio; +            int output_pixel_offset; +            unsigned register_dpiset; +            StaggerConfig stagger_y;          };          CustomSensorSettings custom_settings[] = { -            { { 900 }, 1, 900, 8, 150 }, -            { { 1800 }, 1, 1800, 4, 300 }, -            { { 3600 }, 1, 3600, 2, 600 }, -            { { 7200 }, 1, 7200, 1, 1200 }, +            { { 900 }, 900, Ratio{8, 8}, 2, 150, StaggerConfig{} }, +            { { 1800 }, 1800, Ratio{4, 4}, 5, 300, StaggerConfig{} }, +            { { 3600 }, 3600, Ratio{2, 2}, 10, 600, StaggerConfig{} }, +            { { 7200 }, 7200, Ratio{1, 1}, 20, 1200, StaggerConfig{4, 0} },          };          for (const CustomSensorSettings& setting : custom_settings) {              sensor.resolutions = setting.resolutions; -            sensor.ccd_size_divisor = setting.ccd_size_divisor; -            sensor.logical_dpihw_override = setting.logical_dpihw_override; -            sensor.pixel_count_multiplier = setting.pixel_count_multiplier; -            sensor.dpiset_override = setting.dpiset_override; +            sensor.shading_resolution = setting.shading_resolution; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.stagger_y = setting.stagger_y;              s_sensors->push_back(sensor);          }      }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7500I; -    sensor.optical_res = 7200; -    sensor.register_dpihw_override = 1200; +    sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7400; // gl845 +    sensor.full_resolution = 7200; +    sensor.method = ScanMethod::TRANSPARENCY; +    sensor.register_dpihw = 1200;      sensor.black_pixels = 88; // TODO      sensor.dummy_pixel = 20; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 10200; // TODO      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 230;      sensor.exposure = { 0x0000, 0x0000, 0x0000 }; -    sensor.stagger_config = StaggerConfig{7200, 4}; +    sensor.exposure_lperiod = 14000; +    sensor.use_host_side_calib = true; +    sensor.custom_regs = { +        { 0x08, 0x00 }, { 0x09, 0x00 }, { 0x0a, 0x00 }, +        { 0x16, 0x27 }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, +        { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x84 }, +        { 0x52, 0x09 }, { 0x53, 0x0d }, { 0x54, 0x0f }, { 0x55, 0x01 }, +        { 0x56, 0x04 }, { 0x57, 0x07 }, { 0x58, 0x31 }, { 0x59, 0x79 }, { 0x5a, 0xc0 }, +        { 0x70, 0x0a }, { 0x71, 0x0b }, { 0x72, 0x0c }, { 0x73, 0x0d }, +        { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 }, +        { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, +        { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, { 0x7d, 0x00 }, +        { 0x87, 0x00 }, +    }; +    sensor.gamma = { 1.0f, 1.0f, 1.0f }; +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpiset; +            int output_pixel_offset; +            StaggerConfig stagger_y; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 600 }, 100, 10, StaggerConfig{} }, +            { { 1200 }, 200, 20, StaggerConfig{} }, +            { { 2400 }, 400, 40, StaggerConfig{} }, +            { { 3600 }, 600, 60, StaggerConfig{} }, +            { { 7200 }, 1200, 120, StaggerConfig{4, 0} }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.shading_resolution = setting.resolutions.values()[0]; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            sensor.stagger_y = setting.stagger_y; +            s_sensors->push_back(sensor); +        } +    } + +    sensor = Genesys_Sensor(); +    sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_7500I; // gl843 +    sensor.full_resolution = 7200; +    sensor.register_dpihw = 1200; +    sensor.black_pixels = 88; // TODO +    sensor.dummy_pixel = 20; +    sensor.fau_gain_white_ref = 210; +    sensor.gain_white_ref = 230; +    sensor.exposure = { 0x0000, 0x0000, 0x0000 }; +    sensor.use_host_side_calib = true;      sensor.custom_regs = {          { 0x08, 0x00 },          { 0x09, 0x00 }, @@ -3516,57 +3560,119 @@ void genesys_init_sensor_tables()          { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 },      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = get_ccd_size_divisor_exact;      {          struct CustomSensorSettings          { -            ResolutionFilter resolutions; +            ValueFilterAny<unsigned> resolutions;              ScanMethod method; -            unsigned ccd_size_divisor; -            unsigned logical_dpihw_override; -            unsigned pixel_count_multiplier; +            unsigned shading_resolution; +            Ratio pixel_count_ratio; +            int output_pixel_offset;              unsigned exposure_lperiod; -            unsigned dpiset_override; +            unsigned register_dpiset; +            StaggerConfig stagger_y;          };          CustomSensorSettings custom_settings[] = { -            { { 900 }, ScanMethod::TRANSPARENCY, 1, 900, 8, 0x2f44, 150 }, -            { { 1800 }, ScanMethod::TRANSPARENCY, 1, 1800, 4, 0x2f44, 300 }, -            { { 3600 }, ScanMethod::TRANSPARENCY, 1, 3600, 2, 0x2f44, 600 }, -            { { 7200 }, ScanMethod::TRANSPARENCY, 1, 7200, 1, 0x2f44, 1200 }, -            { { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 900, 8, 0x2af8, 150 }, -            { { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 1800, 4, 0x2af8, 300 }, -            { { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 3600, 2, 0x2af8, 600 }, -            { { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 1, 7200, 1, 0x2af8, 1200 }, +            {   { 900 }, ScanMethod::TRANSPARENCY, 900, Ratio{8, 8}, 2, 0x2f44, 150, +                StaggerConfig{} }, +            {   { 1800 }, ScanMethod::TRANSPARENCY, 1800, Ratio{4, 4}, 5, 0x2f44, 300, +                StaggerConfig{} }, +            {   { 3600 }, ScanMethod::TRANSPARENCY, 3600, Ratio{2, 2}, 10, 0x2f44, 600, +                StaggerConfig{} }, +            {   { 7200 }, ScanMethod::TRANSPARENCY, 7200, Ratio{1, 1}, 20, 0x2f44, 1200, +                StaggerConfig{4, 0} }, +            {   { 900 }, ScanMethod::TRANSPARENCY_INFRARED, 900, Ratio{8, 8}, 2, 0x2af8, 150, +                StaggerConfig{} }, +            {   { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 1800, Ratio{4, 4}, 5, 0x2af8, 300, +                StaggerConfig{} }, +            {   { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 3600, Ratio{2, 2}, 10, 0x2af8, 600, +                StaggerConfig{} }, +            {   { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 7200, Ratio{1, 1}, 20, 0x2af8, 1200, +                StaggerConfig{4, 0} },          };          for (const CustomSensorSettings& setting : custom_settings) {              sensor.resolutions = setting.resolutions;              sensor.method = setting.method; -            sensor.ccd_size_divisor = setting.ccd_size_divisor; -            sensor.logical_dpihw_override = setting.logical_dpihw_override; -            sensor.pixel_count_multiplier = setting.pixel_count_multiplier; +            sensor.shading_resolution = setting.shading_resolution; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.output_pixel_offset = setting.output_pixel_offset;              sensor.exposure_lperiod = setting.exposure_lperiod; -            sensor.dpiset_override = setting.dpiset_override; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.stagger_y = setting.stagger_y;              s_sensors->push_back(sensor);          }      }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_IMG101; +    sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICFILM_8200I; // gl845 +    sensor.full_resolution = 7200; +    sensor.method = ScanMethod::TRANSPARENCY; +    sensor.register_dpihw = 1200; +    sensor.black_pixels = 88; // TODO +    sensor.dummy_pixel = 20; +    sensor.fau_gain_white_ref = 210; +    sensor.gain_white_ref = 230; +    sensor.exposure = { 0x0000, 0x0000, 0x0000 }; +    sensor.exposure_lperiod = 14000; +    sensor.use_host_side_calib = true; +    sensor.custom_regs = { +        { 0x08, 0x00 }, { 0x09, 0x00 }, { 0x0a, 0x00 }, +        { 0x16, 0x27 }, { 0x17, 0x0c }, { 0x18, 0x10 }, { 0x19, 0x2a }, +        { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x84 }, +        { 0x52, 0x09 }, { 0x53, 0x0d }, { 0x54, 0x0f }, { 0x55, 0x01 }, +        { 0x56, 0x04 }, { 0x57, 0x07 }, { 0x58, 0x31 }, { 0x59, 0x79 }, { 0x5a, 0xc0 }, +        { 0x70, 0x0a }, { 0x71, 0x0b }, { 0x72, 0x0c }, { 0x73, 0x0d }, +        { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x00 }, +        { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x00 }, +        { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x00 }, { 0x7d, 0x00 }, +        { 0x87, 0x00 }, +    }; +    sensor.gamma = { 1.0f, 1.0f, 1.0f }; +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            ScanMethod method; +            unsigned register_dpiset; +            int output_pixel_offset; +            StaggerConfig stagger_y; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 900 },  ScanMethod::TRANSPARENCY, 150, 15, StaggerConfig{} }, +            { { 1800 }, ScanMethod::TRANSPARENCY, 300, 30, StaggerConfig{} }, +            { { 3600 }, ScanMethod::TRANSPARENCY, 600, 60, StaggerConfig{} }, +            { { 7200 }, ScanMethod::TRANSPARENCY, 1200, 120, StaggerConfig{4, 0} }, +            { { 900 },  ScanMethod::TRANSPARENCY_INFRARED, 150, 15, StaggerConfig{} }, +            { { 1800 }, ScanMethod::TRANSPARENCY_INFRARED, 300, 30, StaggerConfig{} }, +            { { 3600 }, ScanMethod::TRANSPARENCY_INFRARED, 600, 60, StaggerConfig{} }, +            { { 7200 }, ScanMethod::TRANSPARENCY_INFRARED, 1200, 120, StaggerConfig{4, 0} }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.method = setting.method; +            sensor.shading_resolution = setting.resolutions.values()[0]; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            sensor.stagger_y = setting.stagger_y; +            s_sensors->push_back(sensor); +        } +    } + + +    sensor = Genesys_Sensor(); +    sensor.sensor_id = SensorId::CCD_IMG101; // gl846      sensor.resolutions = { 75, 100, 150, 300, 600, 1200 };      sensor.exposure_lperiod = 11000;      sensor.segment_size = 5136;      sensor.segment_order = {0, 1}; -    sensor.optical_res = 1200; +    sensor.full_resolution = 1200;      sensor.black_pixels = 31;      sensor.dummy_pixel = 31; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 10800;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.exposure = { 0x0000, 0x0000, 0x0000 }; @@ -3580,21 +3686,47 @@ void genesys_init_sensor_tables()          { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 },      };      sensor.gamma = { 1.7f, 1.7f, 1.7f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi; -    s_sensors->push_back(sensor); +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpihw; +            Ratio pixel_count_ratio; +            unsigned shading_factor; +            GenesysRegisterSettingSet extra_custom_regs; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 600, Ratio{1, 4}, 8, { { 0x7e, 0x00 } } }, +            { { 100 }, 600, Ratio{1, 4}, 6, { { 0x7e, 0x00 } } }, +            { { 150 }, 600, Ratio{1, 4}, 4, { { 0x7e, 0x00 } } }, +            { { 300 }, 600, Ratio{1, 4}, 2, { { 0x7e, 0x00 } } }, +            { { 600 }, 600, Ratio{1, 4}, 1, { { 0x7e, 0x01 } } }, +            { { 1200 }, 1200, Ratio{1, 2}, 1, { { 0x7e, 0x01 } } }, +        }; + +        auto base_custom_regs = sensor.custom_regs; +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.register_dpihw = setting.register_dpihw; +            sensor.register_dpiset = setting.resolutions.values()[0]; +            sensor.shading_resolution = setting.register_dpihw; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.shading_factor = setting.shading_factor; +            sensor.custom_regs = base_custom_regs; +            sensor.custom_regs.merge(setting.extra_custom_regs); +            s_sensors->push_back(sensor); +        } +    } +      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICBOOK_3800; +    sensor.sensor_id = SensorId::CCD_PLUSTEK_OPTICBOOK_3800; // gl845      sensor.resolutions = { 75, 100, 150, 300, 600, 1200 };      sensor.exposure_lperiod = 11000; -    sensor.optical_res = 1200; +    sensor.full_resolution = 1200;      sensor.black_pixels = 31;      sensor.dummy_pixel = 31; -    sensor.ccd_start_xoffset = 0; -    sensor.sensor_pixels = 10200;      sensor.fau_gain_white_ref = 210;      sensor.gain_white_ref = 200;      sensor.exposure = { 0, 0, 0 }; @@ -3603,66 +3735,150 @@ void genesys_init_sensor_tables()          { 0x1a, 0x34 }, { 0x1b, 0x00 }, { 0x1c, 0x20 }, { 0x1d, 0x06 },          { 0x52, 0x02 }, { 0x53, 0x04 }, { 0x54, 0x06 }, { 0x55, 0x08 },          { 0x56, 0x0a }, { 0x57, 0x00 }, { 0x58, 0x59 }, { 0x59, 0x31 }, { 0x5a, 0x40 }, +        { 0x70, 0x01 }, { 0x71, 0x00 }, { 0x72, 0x02 }, { 0x73, 0x01 },          { 0x74, 0x00 }, { 0x75, 0x00 }, { 0x76, 0x3c },          { 0x77, 0x00 }, { 0x78, 0x00 }, { 0x79, 0x9f }, -        { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, +        { 0x7a, 0x00 }, { 0x7b, 0x00 }, { 0x7c, 0x55 }, { 0x7d, 0x20 }, +        { 0x87, 0x02 },      };      sensor.gamma = { 1.7f, 1.7f, 1.7f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi; -    s_sensors->push_back(sensor); +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned register_dpihw; +            Ratio pixel_count_ratio; +            unsigned shading_factor; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 600, Ratio{1, 2}, 8 }, +            { { 100 }, 600, Ratio{1, 2}, 6 }, +            { { 150 }, 600, Ratio{1, 2}, 4 }, +            { { 300 }, 600, Ratio{1, 2}, 2 }, +            { { 600 }, 600, Ratio{1, 2}, 1 }, +            { { 1200 }, 1200, Ratio{1, 1}, 1 }, +        }; +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.register_dpihw = setting.register_dpihw; +            sensor.register_dpiset = setting.resolutions.values()[0]; +            sensor.shading_resolution = setting.register_dpihw; +            sensor.pixel_count_ratio = setting.pixel_count_ratio; +            sensor.shading_factor = setting.shading_factor; +            s_sensors->push_back(sensor); +        } +    }      sensor = Genesys_Sensor(); -    sensor.sensor_id = SensorId::CIS_CANON_LIDE_80; -    sensor.optical_res = 1200; // real hardware limit is 2400 -    sensor.ccd_size_divisor = 2; +    sensor.sensor_id = SensorId::CIS_CANON_LIDE_80; // gl841 +    sensor.full_resolution = 1200; // real hardware limit is 2400 +    sensor.register_dpihw = 1200;      sensor.black_pixels = 20;      sensor.dummy_pixel = 6; -    // tuned to give 3*8 multiple startx coordinate during shading calibration -    sensor.ccd_start_xoffset = 34; // 14=>3, 20=>2 -    // 10400, too wide=>10288 in shading data 10240~ -    // 10208 too short for shading, max shading data = 10240 pixels, endpix-startpix=10208 -    sensor.sensor_pixels = 10240;      sensor.fau_gain_white_ref = 150;      sensor.gain_white_ref = 150;      // maps to 0x70-0x73 for GL841      sensor.exposure = { 0x1000, 0x1000, 0x0500 };      sensor.custom_regs = { -        { 0x08, 0x00 }, -        { 0x09, 0x05 }, -        { 0x0a, 0x07 }, -        { 0x0b, 0x09 }, -        { 0x16, 0x00 }, -        { 0x17, 0x01 }, -        { 0x18, 0x00 }, -        { 0x19, 0x06 }, -        { 0x1a, 0x00 }, -        { 0x1b, 0x00 }, -        { 0x1c, 0x00 }, -        { 0x1d, 0x04 }, -        { 0x52, 0x03 }, -        { 0x53, 0x07 }, -        { 0x54, 0x00 }, -        { 0x55, 0x00 }, -        { 0x56, 0x00 }, -        { 0x57, 0x00 }, -        { 0x58, 0x29 }, -        { 0x59, 0x69 }, -        { 0x5a, 0x55 }, -        { 0x5b, 0x00 }, -        { 0x5c, 0x00 }, -        { 0x5d, 0x20 }, -        { 0x5e, 0x41 }, +        { 0x16, 0x00 }, { 0x17, 0x01 }, { 0x18, 0x00 }, { 0x19, 0x06 }, +        { 0x1a, 0x00 }, { 0x1b, 0x00 }, { 0x1c, 0x00 }, { 0x1d, 0x04 }, +        { 0x52, 0x03 }, { 0x53, 0x07 }, { 0x54, 0x00 }, { 0x55, 0x00 }, +        { 0x56, 0x00 }, { 0x57, 0x00 }, { 0x58, 0x29 }, { 0x59, 0x69 }, { 0x5a, 0x55 }, +        { 0x70, 0x00 }, { 0x71, 0x05 }, { 0x72, 0x07 }, { 0x73, 0x09 },      };      sensor.gamma = { 1.0f, 1.0f, 1.0f }; -    sensor.get_logical_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_register_hwdpi_fun = default_get_logical_hwdpi; -    sensor.get_hwdpi_divisor_fun = default_get_hwdpi_divisor_for_dpi; -    sensor.get_ccd_size_divisor_fun = default_get_ccd_size_divisor_for_dpi; -    s_sensors->push_back(sensor); +    { +        struct CustomSensorSettings +        { +            ValueFilterAny<unsigned> resolutions; +            unsigned optical_resolution; +            unsigned register_dpiset; +            unsigned shading_resolution; +            unsigned shading_factor; +            int output_pixel_offset; +        }; + +        CustomSensorSettings custom_settings[] = { +            { { 75 }, 600, 150, 600, 8, 2 }, +            { { 100 }, 600, 200, 600, 6, 3 }, +            { { 150 }, 600, 300, 600, 4, 4 }, +            { { 200 }, 600, 400, 600, 3, 6 }, +            { { 300 }, 600, 600, 600, 2, 9 }, +            { { 600 }, 600, 1200, 600, 1, 17 }, +            { { 1200 }, 1200, 1200, 1200, 1, 35 }, +        }; + +        for (const CustomSensorSettings& setting : custom_settings) { +            sensor.resolutions = setting.resolutions; +            sensor.optical_resolution = setting.optical_resolution; +            sensor.register_dpiset = setting.register_dpiset; +            sensor.shading_resolution = setting.shading_resolution; +            sensor.shading_factor = setting.shading_factor; +            sensor.output_pixel_offset = setting.output_pixel_offset; +            s_sensors->push_back(sensor); +        } +    } +} + +void verify_sensor_tables() +{ +    std::map<SensorId, AsicType> sensor_to_asic; +    for (const auto& device : *s_usb_devices) { +        sensor_to_asic[device.model().sensor_id] = device.model().asic_type; +    } +    for (const auto& sensor : *s_sensors) { +        if (sensor_to_asic.count(sensor.sensor_id) == 0) { +            throw SaneException("Unknown asic for sensor"); +        } +        auto asic_type = sensor_to_asic[sensor.sensor_id]; + +        if (sensor.full_resolution == 0) { +            throw SaneException("full_resolution is not defined"); +        } + +        if (sensor.register_dpiset == 0) { +            throw SaneException("register_dpiset is not defined"); +        } + +        if (asic_type != AsicType::GL646) { +            if (sensor.register_dpihw == 0) { +                throw SaneException("register_dpihw is not defined"); +            } +            if (sensor.shading_resolution == 0) { +                throw SaneException("shading_resolution is not defined"); +            } +        } + +        if (asic_type == AsicType::GL841) { +            auto required_registers = { +                0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, +                0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, +                0x70, 0x71, 0x72, 0x73, +            }; +            for (auto address : required_registers) { +                if (!sensor.custom_regs.has_reg(address)) { +                    throw SaneException("Required register is not present"); +                } +            } +        } + +        if (asic_type == AsicType::GL842) { +            auto required_registers = { +                0x16, 0x17, 0x18, 0x19, 0x1a, 0x1c, 0x1d, +                0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, +                0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, +                0x7f +            }; +            for (auto address : required_registers) { +                if (!sensor.custom_regs.has_reg(address)) { +                    throw SaneException("Required register is not present"); +                } +            } +        } +    }  } +  } // namespace genesys diff --git a/backend/genesys/test_scanner_interface.cpp b/backend/genesys/test_scanner_interface.cpp index 12f726f..e8af494 100644 --- a/backend/genesys/test_scanner_interface.cpp +++ b/backend/genesys/test_scanner_interface.cpp @@ -49,7 +49,10 @@  namespace genesys { -TestScannerInterface::TestScannerInterface(Genesys_Device* dev) : dev_{dev} +TestScannerInterface::TestScannerInterface(Genesys_Device* dev, uint16_t vendor_id, +                                           uint16_t product_id, uint16_t bcd_device) : +    dev_{dev}, +    usb_dev_{vendor_id, product_id, bcd_device}  {      // initialize status registers      if (dev_->model->asic_type == AsicType::GL124) { @@ -58,6 +61,7 @@ TestScannerInterface::TestScannerInterface(Genesys_Device* dev) : dev_{dev}          write_register(0x41, 0x00);      }      if (dev_->model->asic_type == AsicType::GL841 || +        dev_->model->asic_type == AsicType::GL842 ||          dev_->model->asic_type == AsicType::GL843 ||          dev_->model->asic_type == AsicType::GL845 ||          dev_->model->asic_type == AsicType::GL846 || @@ -137,23 +141,21 @@ void TestScannerInterface::bulk_write_data(std::uint8_t addr, std::uint8_t* data  }  void TestScannerInterface::write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, -                                        std::size_t size, Flags flags) +                                        std::size_t size)  {      (void) type;      (void) addr;      (void) data;      (void) size; -    (void) flags;  }  void TestScannerInterface::write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, -                                       std::size_t size, Flags flags) +                                       std::size_t size)  {      (void) type;      (void) addr;      (void) data;      (void) size; -    (void) flags;  }  void TestScannerInterface::write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) diff --git a/backend/genesys/test_scanner_interface.h b/backend/genesys/test_scanner_interface.h index acf0f6d..fc8128c 100644 --- a/backend/genesys/test_scanner_interface.h +++ b/backend/genesys/test_scanner_interface.h @@ -56,7 +56,8 @@ namespace genesys {  class TestScannerInterface : public ScannerInterface  {  public: -    TestScannerInterface(Genesys_Device* dev); +    TestScannerInterface(Genesys_Device* dev, std::uint16_t vendor_id, std::uint16_t product_id, +                         std::uint16_t bcd_device);      ~TestScannerInterface() override; @@ -74,9 +75,9 @@ public:      void bulk_write_data(std::uint8_t addr, std::uint8_t* data, std::size_t size) override;      void write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, -                      std::size_t size, Flags flags) override; +                      std::size_t size) override;      void write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, -                     std::size_t size, Flags flags) override; +                     std::size_t size) override;      void write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) override;      std::uint16_t read_fe_register(std::uint8_t address) override; diff --git a/backend/genesys/test_settings.cpp b/backend/genesys/test_settings.cpp index 425f09c..f328709 100644 --- a/backend/genesys/test_settings.cpp +++ b/backend/genesys/test_settings.cpp @@ -52,6 +52,7 @@ namespace {  bool s_testing_mode = false;  std::uint16_t s_vendor_id = 0;  std::uint16_t s_product_id = 0; +std::uint16_t s_bcd_device = 0;  TestCheckpointCallback s_checkpoint_callback;  } // namespace @@ -66,15 +67,17 @@ void disable_testing_mode()      s_testing_mode = false;      s_vendor_id = 0;      s_product_id = 0; - +    s_bcd_device = 0;  }  void enable_testing_mode(std::uint16_t vendor_id, std::uint16_t product_id, +                         std::uint16_t bcd_device,                           TestCheckpointCallback checkpoint_callback)  {      s_testing_mode = true;      s_vendor_id = vendor_id;      s_product_id = product_id; +    s_bcd_device = bcd_device;      s_checkpoint_callback = checkpoint_callback;  } @@ -88,6 +91,11 @@ std::uint16_t get_testing_product_id()      return s_product_id;  } +std::uint16_t get_testing_bcd_device() +{ +    return s_bcd_device; +} +  std::string get_testing_device_name()  {      std::string name; diff --git a/backend/genesys/test_settings.h b/backend/genesys/test_settings.h index 8ac03e0..38cc3b3 100644 --- a/backend/genesys/test_settings.h +++ b/backend/genesys/test_settings.h @@ -58,9 +58,11 @@ using TestCheckpointCallback = std::function<void(const Genesys_Device&,  bool is_testing_mode();  void disable_testing_mode();  void enable_testing_mode(std::uint16_t vendor_id, std::uint16_t product_id, +                         std::uint16_t bcd_device,                           TestCheckpointCallback checkpoint_callback);  std::uint16_t get_testing_vendor_id();  std::uint16_t get_testing_product_id(); +std::uint16_t get_testing_bcd_device();  std::string get_testing_device_name();  TestCheckpointCallback get_testing_checkpoint_callback(); diff --git a/backend/genesys/test_usb_device.cpp b/backend/genesys/test_usb_device.cpp index de2399e..1612eae 100644 --- a/backend/genesys/test_usb_device.cpp +++ b/backend/genesys/test_usb_device.cpp @@ -48,9 +48,11 @@  namespace genesys { -TestUsbDevice::TestUsbDevice(std::uint16_t vendor, std::uint16_t product) : +TestUsbDevice::TestUsbDevice(std::uint16_t vendor, std::uint16_t product, +                             std::uint16_t bcd_device) :      vendor_{vendor}, -    product_{product} +    product_{product}, +    bcd_device_{bcd_device}  {  } @@ -94,12 +96,25 @@ void TestUsbDevice::close()      name_ = "";  } -void TestUsbDevice::get_vendor_product(int& vendor, int& product) +std::uint16_t TestUsbDevice::get_vendor_id()  {      DBG_HELPER(dbg);      assert_is_open(); -    vendor = vendor_; -    product = product_; +    return vendor_; +} + +std::uint16_t TestUsbDevice::get_product_id() +{ +    DBG_HELPER(dbg); +    assert_is_open(); +    return product_; +} + +std::uint16_t TestUsbDevice::get_bcd_device() +{ +    DBG_HELPER(dbg); +    assert_is_open(); +    return bcd_device_;  }  void TestUsbDevice::control_msg(int rtype, int reg, int value, int index, int length, diff --git a/backend/genesys/test_usb_device.h b/backend/genesys/test_usb_device.h index abbd78a..03b49cc 100644 --- a/backend/genesys/test_usb_device.h +++ b/backend/genesys/test_usb_device.h @@ -50,9 +50,7 @@ namespace genesys {  class TestUsbDevice : public IUsbDevice {  public: -    TestUsbDevice(std::uint16_t vendor, std::uint16_t product); -    TestUsbDevice() = default; - +    TestUsbDevice(std::uint16_t vendor, std::uint16_t product, std::uint16_t bcd_device);      ~TestUsbDevice() override;      bool is_open() const override { return is_open_; } @@ -65,7 +63,9 @@ public:      void reset() override;      void close() override; -    void get_vendor_product(int& vendor, int& product) override; +    std::uint16_t get_vendor_id() override; +    std::uint16_t get_product_id() override; +    std::uint16_t get_bcd_device() override;      void control_msg(int rtype, int reg, int value, int index, int length,                       std::uint8_t* data) override; @@ -78,6 +78,7 @@ private:      bool is_open_ = false;      std::uint16_t vendor_ = 0;      std::uint16_t product_ = 0; +    std::uint16_t bcd_device_ = 0;  };  } // namespace genesys diff --git a/backend/genesys/usb_device.cpp b/backend/genesys/usb_device.cpp index 2d02219..d6cbaed 100644 --- a/backend/genesys/usb_device.cpp +++ b/backend/genesys/usb_device.cpp @@ -101,11 +101,33 @@ void UsbDevice::close()      sanei_usb_close(device_num);  } -void UsbDevice::get_vendor_product(int& vendor, int& product) +std::uint16_t UsbDevice::get_vendor_id()  {      DBG_HELPER(dbg);      assert_is_open(); +    int vendor = 0; +    int product = 0;      TIE(sanei_usb_get_vendor_product(device_num_, &vendor, &product)); +    return static_cast<std::uint16_t>(vendor); +} + +std::uint16_t UsbDevice::get_product_id() +{ +    DBG_HELPER(dbg); +    assert_is_open(); +    int vendor = 0; +    int product = 0; +    TIE(sanei_usb_get_vendor_product(device_num_, &vendor, &product)); +    return static_cast<std::uint16_t>(product); +} + +std::uint16_t UsbDevice::get_bcd_device() +{ +    DBG_HELPER(dbg); +    assert_is_open(); +    sanei_usb_dev_descriptor desc; +    TIE(sanei_usb_get_descriptor(device_num_, &desc)); +    return desc.bcd_dev;  }  void UsbDevice::control_msg(int rtype, int reg, int value, int index, int length, diff --git a/backend/genesys/usb_device.h b/backend/genesys/usb_device.h index 265c57c..aa8b89a 100644 --- a/backend/genesys/usb_device.h +++ b/backend/genesys/usb_device.h @@ -71,7 +71,9 @@ public:      virtual void reset() = 0;      virtual void close() = 0; -    virtual void get_vendor_product(int& vendor, int& product) = 0; +    virtual std::uint16_t get_vendor_id() = 0; +    virtual std::uint16_t get_product_id() = 0; +    virtual std::uint16_t get_bcd_device() = 0;      virtual void control_msg(int rtype, int reg, int value, int index, int length,                               std::uint8_t* data) = 0; @@ -96,7 +98,9 @@ public:      void reset() override;      void close() override; -    void get_vendor_product(int& vendor, int& product) override; +    std::uint16_t get_vendor_id() override; +    std::uint16_t get_product_id() override; +    std::uint16_t get_bcd_device() override;      void control_msg(int rtype, int reg, int value, int index, int length,                       std::uint8_t* data) override; diff --git a/backend/genesys/utilities.h b/backend/genesys/utilities.h index 1e268b5..fdab770 100644 --- a/backend/genesys/utilities.h +++ b/backend/genesys/utilities.h @@ -46,12 +46,81 @@  #include "error.h"  #include <algorithm> +#include <cstdint>  #include <iostream>  #include <sstream>  #include <vector> +  namespace genesys { +// just like SANE_FIX and SANE_UNFIX except that the conversion is done by a function and argument +// precision is handled correctly +inline SANE_Word double_to_fixed(double v) +{ +    return static_cast<SANE_Word>(v * (1 << SANE_FIXED_SCALE_SHIFT)); +} + +inline SANE_Word float_to_fixed(float v) +{ +    return static_cast<SANE_Word>(v * (1 << SANE_FIXED_SCALE_SHIFT)); +} + +inline float fixed_to_float(SANE_Word v) +{ +    return static_cast<float>(v) / (1 << SANE_FIXED_SCALE_SHIFT); +} + +inline double fixed_to_double(SANE_Word v) +{ +    return static_cast<double>(v) / (1 << SANE_FIXED_SCALE_SHIFT); +} + +template<class T> +inline T abs_diff(T a, T b) +{ +    if (a < b) { +        return b - a; +    } else { +        return a - b; +    } +} + +inline std::uint64_t align_multiple_floor(std::uint64_t x, std::uint64_t multiple) +{ +    if (multiple == 0) { +        return x; +    } +    return (x / multiple) * multiple; +} + +inline std::uint64_t align_multiple_ceil(std::uint64_t x, std::uint64_t multiple) +{ +    if (multiple == 0) { +        return x; +    } +    return ((x + multiple - 1) / multiple) * multiple; +} + +inline std::uint64_t multiply_by_depth_ceil(std::uint64_t pixels, std::uint64_t depth) +{ +    if (depth == 1) { +        return (pixels / 8) + ((pixels % 8) ? 1 : 0); +    } else { +        return pixels * (depth / 8); +    } +} + +template<class T> +inline T clamp(const T& value, const T& lo, const T& hi) +{ +    if (value < lo) +        return lo; +    if (value > hi) +        return hi; +    return value; +} +  template<class T>  void compute_array_percentile_approx(T* result, const T* data,                                       std::size_t line_count, std::size_t elements_per_line, @@ -85,6 +154,75 @@ void compute_array_percentile_approx(T* result, const T* data,      }  } +class Ratio +{ +public: +    Ratio() : multiplier_{1}, divisor_{1} +    { +    } + +    Ratio(unsigned multiplier, unsigned divisor) : multiplier_{multiplier}, divisor_{divisor} +    { +    } + +    unsigned multiplier() const { return multiplier_; } +    unsigned divisor() const { return divisor_; } + +    unsigned apply(unsigned arg) const +    { +        return static_cast<std::uint64_t>(arg) * multiplier_ / divisor_; +    } + +    int apply(int arg) const +    { +        return static_cast<std::int64_t>(arg) * multiplier_ / divisor_; +    } + +    float apply(float arg) const +    { +        return arg * multiplier_ / divisor_; +    } + +    unsigned apply_inverse(unsigned arg) const +    { +        return static_cast<std::uint64_t>(arg) * divisor_ / multiplier_; +    } + +    int apply_inverse(int arg) const +    { +        return static_cast<std::int64_t>(arg) * divisor_ / multiplier_; +    } + +    float apply_inverse(float arg) const +    { +        return arg * divisor_ / multiplier_; +    } + +    bool operator==(const Ratio& other) const +    { +        return multiplier_ == other.multiplier_ && divisor_ == other.divisor_; +    } +private: +    unsigned multiplier_; +    unsigned divisor_; + +    template<class Stream> +    friend void serialize(Stream& str, Ratio& x); +}; + +template<class Stream> +void serialize(Stream& str, Ratio& x) +{ +    serialize(str, x.multiplier_); +    serialize(str, x.divisor_); +} + +inline std::ostream& operator<<(std::ostream& out, const Ratio& ratio) +{ +    out << ratio.multiplier() << "/" << ratio.divisor(); +    return out; +} +  template<class Char, class Traits>  class BasicStreamStateSaver  { diff --git a/backend/genesys/value_filter.h b/backend/genesys/value_filter.h new file mode 100644 index 0000000..ba55567 --- /dev/null +++ b/backend/genesys/value_filter.h @@ -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  | 
