diff options
| author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2020-03-30 21:30:45 +0200 | 
|---|---|---|
| committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2020-03-30 21:30:45 +0200 | 
| commit | ee770c2346eb37e0dcb8b6cf3eaacf3d8efd6bbc (patch) | |
| tree | 58f05092be1a17a939e861f8cadcda1b6ca2ecef /backend/genesys/scanner_interface_usb.cpp | |
| parent | 0da9e21872802cfc6e975b1ebaf9efb9e5934d84 (diff) | |
| parent | fef76e17ed4c607ea73b81279f9ef1d7121be900 (diff) | |
Merge branch 'release/experimental/1.0.29-1_experimental1'experimental/1.0.29-1_experimental1
Diffstat (limited to 'backend/genesys/scanner_interface_usb.cpp')
| -rw-r--r-- | backend/genesys/scanner_interface_usb.cpp | 515 | 
1 files changed, 515 insertions, 0 deletions
diff --git a/backend/genesys/scanner_interface_usb.cpp b/backend/genesys/scanner_interface_usb.cpp new file mode 100644 index 0000000..d4d83dd --- /dev/null +++ b/backend/genesys/scanner_interface_usb.cpp @@ -0,0 +1,515 @@ +/* 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 "scanner_interface_usb.h" +#include "low.h" +#include <thread> + +namespace genesys { + +ScannerInterfaceUsb::~ScannerInterfaceUsb() = default; + +ScannerInterfaceUsb::ScannerInterfaceUsb(Genesys_Device* dev) : dev_{dev} {} + +bool ScannerInterfaceUsb::is_mock() const +{ +    return false; +} + +std::uint8_t ScannerInterfaceUsb::read_register(std::uint16_t address) +{ +    DBG_HELPER(dbg); + +    std::uint8_t value = 0; + +    if (dev_->model->asic_type == AsicType::GL847 || +        dev_->model->asic_type == AsicType::GL845 || +        dev_->model->asic_type == AsicType::GL846 || +        dev_->model->asic_type == AsicType::GL124) +    { +        std::uint8_t value2x8[2]; +        std::uint16_t address16 = 0x22 + (address << 8); + +        std::uint16_t usb_value = VALUE_GET_REGISTER; +        if (address > 0xff) { +            usb_value |= 0x100; +        } + +        usb_dev_.control_msg(REQUEST_TYPE_IN, REQUEST_BUFFER, usb_value, address16, 2, value2x8); + +        // check usb link status +        if (value2x8[1] != 0x55) { +            throw SaneException(SANE_STATUS_IO_ERROR, "invalid read, scanner unplugged?"); +        } + +        DBG(DBG_io, "%s (0x%02x, 0x%02x) completed\n", __func__, address, value2x8[0]); + +        value = value2x8[0]; + +    } else { + +        if (address > 0xff) { +            throw SaneException("Invalid register address 0x%04x", address); +        } + +        std::uint8_t address8 = address & 0xff; + +        usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, INDEX, +                             1, &address8); +        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; +} + +void ScannerInterfaceUsb::write_register(std::uint16_t address, std::uint8_t value) +{ +    DBG_HELPER_ARGS(dbg, "address: 0x%04x, value: 0x%02x", static_cast<unsigned>(address), +                    static_cast<unsigned>(value)); + +    if (dev_->model->asic_type == AsicType::GL847 || +        dev_->model->asic_type == AsicType::GL845 || +        dev_->model->asic_type == AsicType::GL846 || +        dev_->model->asic_type == AsicType::GL124) +    { +        std::uint8_t buffer[2]; + +        buffer[0] = address & 0xff; +        buffer[1] = value; + +        std::uint16_t usb_value = VALUE_SET_REGISTER; +        if (address > 0xff) { +            usb_value |= 0x100; +        } + +        usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, usb_value, INDEX, +                                  2, buffer); + +    } else { +        if (address > 0xff) { +            throw SaneException("Invalid register address 0x%04x", address); +        } + +        std::uint8_t address8 = address & 0xff; + +        usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, INDEX, +                             1, &address8); + +        usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_WRITE_REGISTER, INDEX, +                             1, &value); + +    } +    DBG(DBG_io, "%s (0x%02x, 0x%02x) completed\n", __func__, address, value); +} + +void ScannerInterfaceUsb::write_registers(const Genesys_Register_Set& regs) +{ +    DBG_HELPER(dbg); +    if (dev_->model->asic_type == AsicType::GL646 || +        dev_->model->asic_type == AsicType::GL841) +    { +        uint8_t outdata[8]; +        std::vector<uint8_t> buffer; +        buffer.reserve(regs.size() * 2); + +        /* copy registers and values in data buffer */ +        for (const auto& r : regs) { +            buffer.push_back(r.address); +            buffer.push_back(r.value); +        } + +        DBG(DBG_io, "%s (elems= %zu, size = %zu)\n", __func__, regs.size(), buffer.size()); + +        if (dev_->model->asic_type == AsicType::GL646) { +            outdata[0] = BULK_OUT; +            outdata[1] = BULK_REGISTER; +            outdata[2] = 0x00; +            outdata[3] = 0x00; +            outdata[4] = (buffer.size() & 0xff); +            outdata[5] = ((buffer.size() >> 8) & 0xff); +            outdata[6] = ((buffer.size() >> 16) & 0xff); +            outdata[7] = ((buffer.size() >> 24) & 0xff); + +            usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, INDEX, +                                 sizeof(outdata), outdata); + +            size_t write_size = buffer.size(); + +            usb_dev_.bulk_write(buffer.data(), &write_size); +        } else { +            for (std::size_t i = 0; i < regs.size();) { +                std::size_t c = regs.size() - i; +                if (c > 32)  /*32 is max on GL841. checked that.*/ +                    c = 32; + +                usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_SET_REGISTER, +                                     INDEX, c * 2, buffer.data() + i * 2); + +                i += c; +            } +        } +    } else { +        for (const auto& r : regs) { +            write_register(r.address, r.value); +        } +    } + +    DBG(DBG_io, "%s: wrote %zu registers\n", __func__, regs.size()); +} + +void ScannerInterfaceUsb::write_0x8c(std::uint8_t index, std::uint8_t value) +{ +    DBG_HELPER_ARGS(dbg, "0x%02x,0x%02x", index, value); +    usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_BUF_ENDACCESS, index, 1, &value); +} + +static void bulk_read_data_send_header(UsbDevice& usb_dev, AsicType asic_type, size_t size) +{ +    DBG_HELPER(dbg); + +    uint8_t outdata[8]; +    if (asic_type == AsicType::GL124 || +        asic_type == AsicType::GL846 || +        asic_type == AsicType::GL847) +    { +        // hard coded 0x10000000 address +        outdata[0] = 0; +        outdata[1] = 0; +        outdata[2] = 0; +        outdata[3] = 0x10; +    } else if (asic_type == AsicType::GL841 || +               asic_type == AsicType::GL843) { +        outdata[0] = BULK_IN; +        outdata[1] = BULK_RAM; +        outdata[2] = 0x82; // +        outdata[3] = 0x00; +    } else { +        outdata[0] = BULK_IN; +        outdata[1] = BULK_RAM; +        outdata[2] = 0x00; +        outdata[3] = 0x00; +    } + +    /* data size to transfer */ +    outdata[4] = (size & 0xff); +    outdata[5] = ((size >> 8) & 0xff); +    outdata[6] = ((size >> 16) & 0xff); +    outdata[7] = ((size >> 24) & 0xff); + +   usb_dev.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, 0x00, +                       sizeof(outdata), outdata); +} + +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 +    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::GL846 || +        dev_->model->asic_type == AsicType::GL847) +    { +        is_addr_used = 0; +        has_header_before_each_chunk = 1; +    } + +    if (is_addr_used) { +        DBG(DBG_io, "%s: requesting %zu bytes from 0x%02x addr\n", __func__, size, addr); +    } else { +        DBG(DBG_io, "%s: requesting %zu bytes\n", __func__, size); +    } + +    if (size == 0) +        return; + +    if (is_addr_used) { +        usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, 0x00, +                             1, &addr); +    } + +    std::size_t target_size = size; + +    std::size_t max_in_size = sanei_genesys_get_bulk_max_size(dev_->model->asic_type); + +    if (!has_header_before_each_chunk) { +        bulk_read_data_send_header(usb_dev_, dev_->model->asic_type, size); +    } + +    // loop until computed data size is read +    while (target_size > 0) { +        std::size_t block_size = std::min(target_size, max_in_size); + +        if (has_header_before_each_chunk) { +            bulk_read_data_send_header(usb_dev_, dev_->model->asic_type, block_size); +        } + +        DBG(DBG_io2, "%s: trying to read %zu bytes of data\n", __func__, block_size); + +        usb_dev_.bulk_read(data, &block_size); + +        DBG(DBG_io2, "%s: read %zu bytes, %zu remaining\n", __func__, block_size, target_size - block_size); + +        target_size -= block_size; +        data += block_size; +    } +} + +void ScannerInterfaceUsb::bulk_write_data(std::uint8_t addr, std::uint8_t* data, std::size_t len) +{ +    DBG_HELPER_ARGS(dbg, "writing %zu bytes", len); + +    // supported: GL646, GL841, GL843 +    std::size_t size; +    std::uint8_t outdata[8]; + +    usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_REGISTER, VALUE_SET_REGISTER, INDEX, +                             1, &addr); + +    std::size_t max_out_size = sanei_genesys_get_bulk_max_size(dev_->model->asic_type); + +    while (len) { +        if (len > max_out_size) +            size = max_out_size; +        else +            size = len; + +        if (dev_->model->asic_type == AsicType::GL841) { +            outdata[0] = BULK_OUT; +            outdata[1] = BULK_RAM; +            // both 0x82 and 0x00 works on GL841. +            outdata[2] = 0x82; +            outdata[3] = 0x00; +        } else { +            outdata[0] = BULK_OUT; +            outdata[1] = BULK_RAM; +            // 8600F uses 0x82, but 0x00 works too. 8400F uses 0x02 for certain transactions. +            outdata[2] = 0x00; +            outdata[3] = 0x00; +        } + +        outdata[4] = (size & 0xff); +        outdata[5] = ((size >> 8) & 0xff); +        outdata[6] = ((size >> 16) & 0xff); +        outdata[7] = ((size >> 24) & 0xff); + +        usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, 0x00, +                             sizeof(outdata), outdata); + +        usb_dev_.bulk_write(data, &size); + +        DBG(DBG_io2, "%s: wrote %zu bytes, %zu remaining\n", __func__, size, len - size); + +        len -= size; +        data += size; +    } +} + +void ScannerInterfaceUsb::write_buffer(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, +                                       std::size_t size, Flags flags) +{ +    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::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)); +            } +        } +    } else { +        write_register(0x2b, ((addr >> 4) & 0xff)); +        write_register(0x2a, ((addr >> 12) & 0xff)); +    } +    bulk_write_data(type, data, size); +} + +void ScannerInterfaceUsb::write_gamma(std::uint8_t type, std::uint32_t addr, std::uint8_t* data, +                                      std::size_t size, Flags flags) +{ +    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::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)); +    } +    bulk_write_data(type, data, size); +} + +void ScannerInterfaceUsb::write_ahb(std::uint32_t addr, std::uint32_t size, std::uint8_t* data) +{ +    DBG_HELPER_ARGS(dbg, "address: 0x%08x, size: %d", static_cast<unsigned>(addr), +                    static_cast<unsigned>(size)); + +    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) +    { +        throw SaneException("Unsupported transfer type"); +    } +    std::uint8_t outdata[8]; +    outdata[0] = addr & 0xff; +    outdata[1] = ((addr >> 8) & 0xff); +    outdata[2] = ((addr >> 16) & 0xff); +    outdata[3] = ((addr >> 24) & 0xff); +    outdata[4] = (size & 0xff); +    outdata[5] = ((size >> 8) & 0xff); +    outdata[6] = ((size >> 16) & 0xff); +    outdata[7] = ((size >> 24) & 0xff); + +    // write addr and size for AHB +    usb_dev_.control_msg(REQUEST_TYPE_OUT, REQUEST_BUFFER, VALUE_BUFFER, 0x01, 8, outdata); + +    std::size_t max_out_size = sanei_genesys_get_bulk_max_size(dev_->model->asic_type); + +    // write actual data +    std::size_t written = 0; +    do { +        std::size_t block_size = std::min(size - written, max_out_size); + +        usb_dev_.bulk_write(data + written, &block_size); + +        written += block_size; +    } while (written < size); +} + +std::uint16_t ScannerInterfaceUsb::read_fe_register(std::uint8_t address) +{ +    DBG_HELPER(dbg); +    Genesys_Register_Set reg; + +    reg.init_reg(0x50, address); + +    // set up read address +    write_registers(reg); + +    // read data +    std::uint16_t value = read_register(0x46) << 8; +    value |= read_register(0x47); + +    DBG(DBG_io, "%s (0x%02x, 0x%04x)\n", __func__, address, value); +    return value; +} + +void ScannerInterfaceUsb::write_fe_register(std::uint8_t address, std::uint16_t value) +{ +    DBG_HELPER_ARGS(dbg, "0x%02x, 0x%04x", address, value); +    Genesys_Register_Set reg(Genesys_Register_Set::SEQUENTIAL); + +    reg.init_reg(0x51, address); +    if (dev_->model->asic_type == AsicType::GL124) { +        reg.init_reg(0x5d, (value / 256) & 0xff); +        reg.init_reg(0x5e, value & 0xff); +    } else { +        reg.init_reg(0x3a, (value / 256) & 0xff); +        reg.init_reg(0x3b, value & 0xff); +    } + +    write_registers(reg); +} + +IUsbDevice& ScannerInterfaceUsb::get_usb_device() +{ +    return usb_dev_; +} + +void ScannerInterfaceUsb::sleep_us(unsigned microseconds) +{ +    if (sanei_usb_is_replay_mode_enabled()) { +        return; +    } +    std::this_thread::sleep_for(std::chrono::microseconds{microseconds}); +} + +void ScannerInterfaceUsb::record_progress_message(const char* msg) +{ +    sanei_usb_testing_record_message(msg); +} + +void ScannerInterfaceUsb::record_slope_table(unsigned table_nr, +                                             const std::vector<std::uint16_t>& steps) +{ +    (void) table_nr; +    (void) steps; +} + +void ScannerInterfaceUsb::record_key_value(const std::string& key, const std::string& value) +{ +    (void) key; +    (void) value; +} + +void ScannerInterfaceUsb::test_checkpoint(const std::string& name) +{ +    (void) name; +} + +} // namespace genesys  | 
