1

我发现 Linux SANE不支持我的 Nikon LS-9000 ED 扫描仪,因此决定使用 Linux Firewire 内核 API libraw1394制作我自己的驱动程序。

尼康最近发布了扫描仪的库程序和命令 API 规范。扫描仪使用串行总线协议 2 (SBP-2) 和IEEE Std 1394-1995标准。

我用 libraw1394 做了一个简单的测试程序,发现我无法读取(或写入)大多数特定于串行总线的寄存器。阅读测试程序如下:

// gcc -Wall -o read read.c -l raw1394
#include <stdio.h>
#include <libraw1394/csr.h>
#include <libraw1394/raw1394.h>


int main()
{

    raw1394handle_t handle;

    handle = raw1394_new_handle_on_port(0);

    printf("node: %X\n", raw1394_get_local_id(handle));


    quadlet_t read;

    for (int i = 0; i <= 1048576; i++)  // 0 to 0x100000
    {
        int result = raw1394_read(handle, 
                                  raw1394_get_local_id(handle), 
                                  CSR_REGISTER_BASE + (i*4), 
                                  sizeof(quadlet_t), 
                                  &read);

        if (result == 0)
            printf("%X\n", i*4);            
    }

    raw1394_destroy_handle(handle);

}

输出(缩写形式)是:

$ sudo ./read
node: FFC1
0
4
8
18
1C
200
204
210
218
21C
220
224
228
230
234
400 - 7FC
1000 - 13FC

这些偏移不包括最重要的 MANAGEMENT_AGENT 寄存器,对于尼康 LS9000 ED,该寄存器位于 0x30000。我也无法写入该寄存器。

内核中必须有一种内存访问限制。如何将命令写入 MANAGEMENT_AGENT 寄存器,例如查询登录 ORB?

连接扫描仪之前:

$ lsmod | grep firewire
firewire_ohci          40960  0
firewire_core          65536  1 firewire_ohci
crc_itu_t              16384  1 firewire_core

$ dmesg | grep firewire
[    0.776039] firewire_ohci 0000:03:00.0: added OHCI v1.10 device as card  0, 4 IR + 8 IT contexts, quirks 0x2
[    1.276095] firewire_core 0000:03:00.0: created device fw0: GUID 000000000000017e, S400

扫描仪连接后:

$ lsmod | grep firewire
firewire_sbp2          24576  0
firewire_ohci          40960  0
firewire_core          65536  2 firewire_ohci,firewire_sbp2
crc_itu_t              16384  1 firewire_core

$ dmesg | grep firewire
[    0.776039] firewire_ohci 0000:03:00.0: added OHCI v1.10 device as card 0, 4 IR + 8 IT contexts, quirks 0x2
[    1.276095] firewire_core 0000:03:00.0: created device fw0: GUID 000000000000017e, S400
[ 3289.660782] firewire_core 0000:03:00.0: rediscovered device fw0
[ 3292.688185] firewire_core 0000:03:00.0: created device fw1: GUID 0090b54003ffffff, S400
[ 3292.688190] firewire_core 0000:03:00.0: phy config: new root=ffc0, gap_count=5
[ 3292.922459] firewire_sbp2 fw1.0: logged in to LUN 0000 (0 retries)

似乎内核模块 firewire_sbp2 在扫描仪连接时启动并且显然进行了登录。firewire_sbp2 中的函数可以在应用程序中使用吗?

4

3 回答 3

0

SBP2是一种通过 FireWire 传输 SCSI 命令的协议。

在 Linux 中,您可以使用SCSI 通用驱动程序( sg) 将 SCSI 命令发送到此类设备。

于 2017-11-28T15:07:29.860 回答
0

我现在很高兴地说,我制作了一个驱动程序,可以在 Linux 上使用我的 Nikon Super Coolscan 9000ED 创建完整、正确曝光的扫描。

该驱动程序由一系列 C 程序组成。此外,我使用 Linux 实用程序 Convert 从 bin 中制作 tiff。可以在GitHub 上找到处于当前原始状态的驱动程序。请注意,它目前仅适用于 FH-869S Brownie Strip 胶卷架。

我附上了用于制作缩略图的完整 C 程序:

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <scsi/sg.h> 



#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte)  \
  (byte & 0x80 ? '1' : '0'), \
  (byte & 0x40 ? '1' : '0'), \
  (byte & 0x20 ? '1' : '0'), \
  (byte & 0x10 ? '1' : '0'), \
  (byte & 0x08 ? '1' : '0'), \
  (byte & 0x04 ? '1' : '0'), \
  (byte & 0x02 ? '1' : '0'), \
  (byte & 0x01 ? '1' : '0')



#define MODESELECT_OPCODE   0x15
#define MODESELECT_CMD_LEN  6
#define PAGEFORMAT          0x10    // 00010000
#define PAGECODE            0x03    // 00 0000 11
#define MODE_LENGTH         0x14    // 20d 



#define GET_OPCODE          0x25    // p.46 25h
#define GET_CMD_LEN         10
#define WINDOW_HEADER       8
#define SINGLE              0x01    
#define GET_LENGTH_MSB      0x00   
#define GET_LENGTH_LSB      0x3A    // 58



#define SET_OPCODE          0x24
#define SET_CMD_LEN         10
#define SET_LENGTH_MSB      0x00   
#define SET_LENGTH_LSB      0x3A


#define RED     1
#define GREEN   2
#define BLUE    3


#define SCAN_OPCODE         0x1B    // SCAN 
#define CMD_SCAN_LEN        6
#define SCAN_LENGTH         0x03    // 0 for BW  3 for RGB ?


#define READ_OPCODE         0x28    // p.53 28h
#define READ_CMD_LEN        10
#define DATA_TYPE_CODE      0x00        
#define DATA_TYPE_QUAL_MSB  0x00   
#define DATA_TYPE_QUAL_LSB  0x00
#define READ_LENGTH_MSB     0x00   
#define READ_LENGTH_LSB     0x00    
#define READ_DATA_HEADER    6   




int sg_device;



unsigned char modeCmd[MODESELECT_CMD_LEN] =
    {MODESELECT_OPCODE, PAGEFORMAT, 0, 0, MODE_LENGTH, 0};


unsigned char getCmd[GET_CMD_LEN] =
    {GET_OPCODE, SINGLE, 0, 0, 0, RED, GET_LENGTH_MSB, GET_LENGTH_MSB, GET_LENGTH_LSB, 0};


unsigned char setCmd[SET_CMD_LEN] =
    {SET_OPCODE, 0, 0, 0, 0, 0, SET_LENGTH_MSB, SET_LENGTH_MSB, SET_LENGTH_LSB, 0};


unsigned char scanCommand[CMD_SCAN_LEN] =
    {SCAN_OPCODE, 0, 0, 0, SCAN_LENGTH, 0}; 


unsigned char readCmd[READ_CMD_LEN] =
    {READ_OPCODE, 0, DATA_TYPE_CODE, 0, DATA_TYPE_QUAL_MSB, DATA_TYPE_QUAL_LSB, 
     READ_LENGTH_MSB, READ_LENGTH_MSB, READ_LENGTH_LSB, 0};




unsigned char sense_buffer[32];     // p.6 'Status of this unit' 8 quadlets




sg_io_hdr_t io_hdr;




void printSense()
{

    printf("0: "BYTE_TO_BINARY_PATTERN" %02X %02X%02X\n", BYTE_TO_BINARY(sense_buffer[0]), sense_buffer[1], sense_buffer[2], sense_buffer[3]);
    printf("1: %02X%02X%02X%02X\n", sense_buffer[4], sense_buffer[5], sense_buffer[6], sense_buffer[7]);
    printf("2: "BYTE_TO_BINARY_PATTERN" "BYTE_TO_BINARY_PATTERN" %02X %02X\n", BYTE_TO_BINARY(sense_buffer[8]), 
                BYTE_TO_BINARY(sense_buffer[9]), sense_buffer[10], sense_buffer[11]);
    printf("3: %02X%02X%02X%02X\n", sense_buffer[12], sense_buffer[13], sense_buffer[14], sense_buffer[15]);
    printf("4: %02X%02X%02X%02X\n", sense_buffer[16], sense_buffer[17], sense_buffer[18], sense_buffer[19]);
    printf("5: %02X%02X%02X%02X\n", sense_buffer[20], sense_buffer[21], sense_buffer[22], sense_buffer[23]);
    printf("6: %02X%02X%02X%02X\n", sense_buffer[24], sense_buffer[25], sense_buffer[26], sense_buffer[27]);
    printf("7: %02X%02X%02X%02X\n\n", sense_buffer[28], sense_buffer[29], sense_buffer[30], sense_buffer[31]);

}




int modeselect()
{



    unsigned char parameter_buffer[MODE_LENGTH];



    parameter_buffer[0] = 0x13; 
    parameter_buffer[1] = 0x00;     
    parameter_buffer[2] = 0x00;
    parameter_buffer[3] = 0x08;     


    parameter_buffer[4] = 0x00; 
    parameter_buffer[5] = 0x00;     
    parameter_buffer[6] = 0x00;
    parameter_buffer[7] = 0x00;
    parameter_buffer[8] = 0x00;
    parameter_buffer[9] = 0x00;
    parameter_buffer[10] = 0x00;
    parameter_buffer[11] = 0x01;

    parameter_buffer[12] = 0x03; 
    parameter_buffer[13] = 0x06;    
    parameter_buffer[14] = 0x00;
    parameter_buffer[15] = 0x00;
    parameter_buffer[16] = 0x0F;        // max resolution
    parameter_buffer[17] = 0xA0;        // max resolution
    parameter_buffer[18] = 0x00;
    parameter_buffer[19] = 0x00;




    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
    io_hdr.interface_id = 'S';
    io_hdr.mx_sb_len = sizeof(sense_buffer);
    io_hdr.sbp = sense_buffer;
    io_hdr.dxfer_len = sizeof(parameter_buffer);
    io_hdr.dxferp = parameter_buffer;     
    io_hdr.cmd_len = sizeof(modeCmd);
    io_hdr.cmdp = modeCmd;
    io_hdr.dxfer_direction = SG_DXFER_TO_DEV;               //  /usr/include/scsi/sg.h
    io_hdr.timeout = 20000; 



    for (int i = 0; i < 10; i++)
    {

        if (ioctl(sg_device, SG_IO, &io_hdr) < 0) 
        {
            perror("MODESELECT ioctl error");
            return 1;
        }

        if (io_hdr.status == 0)
            break;

        usleep(20000);

    }   


    printf("ModeSelect: %02X\n\n", io_hdr.status);





    return 0; 

}




int set(int color, uint32_t exposure)
{



    unsigned char parameter_buffer[58];


    parameter_buffer[0] = 0x00; 
    parameter_buffer[1] = 0x00;     
    parameter_buffer[2] = 0x00;
    parameter_buffer[3] = 0x00;
    parameter_buffer[4] = 0x00;
    parameter_buffer[5] = 0x00;
    parameter_buffer[6] = 0x00;
    parameter_buffer[7] = 0x32;     // 50   <<<<< this is 50, not 58 >>>>>




    parameter_buffer[WINDOW_HEADER+0] = 0x00 | color;

    parameter_buffer[WINDOW_HEADER+1] = 0x00;

    parameter_buffer[WINDOW_HEADER+2] = 0x00 | 0x00;        // 83=0053  666=029A
    parameter_buffer[WINDOW_HEADER+3] = 0x00 | 0x53;

    parameter_buffer[WINDOW_HEADER+4] = 0x00 | 0x00;        // 
    parameter_buffer[WINDOW_HEADER+5] = 0x00 | 0x53;


    parameter_buffer[WINDOW_HEADER+6] = 0x00;       // Upper Left X Offset
    parameter_buffer[WINDOW_HEADER+7] = 0x00;
    parameter_buffer[WINDOW_HEADER+8] = 0x00 | 0x02;        
    parameter_buffer[WINDOW_HEADER+9] = 0x00 | 0x06;

    parameter_buffer[WINDOW_HEADER+10] = 0x00;      // Upper Left Y Offset
    parameter_buffer[WINDOW_HEADER+11] = 0x00;
    parameter_buffer[WINDOW_HEADER+12] = 0x00 | 0x08;       
    parameter_buffer[WINDOW_HEADER+13] = 0x00 | 0xBC;

    parameter_buffer[WINDOW_HEADER+14] = 0x00;      // Window Width (X)
    parameter_buffer[WINDOW_HEADER+15] = 0x00;
    parameter_buffer[WINDOW_HEADER+16] = 0x00 | 0x23;   // 8964 px   
    parameter_buffer[WINDOW_HEADER+17] = 0x00 | 0x04;   // 4000 / 83 = 48;
                                                        // 8964 / 48 = 186*3*2 = 1116 bytes pr. line

    parameter_buffer[WINDOW_HEADER+18] = 0x00;      // Window Length (Y)
    parameter_buffer[WINDOW_HEADER+19] = 0x00;  
    parameter_buffer[WINDOW_HEADER+20] = 0x00 | 0x75;   // 8754h - (2x8BCh) = 30172                                                                 
    parameter_buffer[WINDOW_HEADER+21] = 0x00 | 0xC0;   // 30172 - 12 = 30160 - 16 = 30144 (75C0) / 48 = 628 lines


    parameter_buffer[WINDOW_HEADER+22] = 0x00;
    parameter_buffer[WINDOW_HEADER+23] = 0x00;
    parameter_buffer[WINDOW_HEADER+24] = 0x00;

    parameter_buffer[WINDOW_HEADER+25] = 0x00 | 0x05;   // RBG (02 = BW) 
    parameter_buffer[WINDOW_HEADER+26] = 0x00 | 0x10;   // 16 bits (not 8 bits)

    parameter_buffer[WINDOW_HEADER+27] = 0x00;      // 
    parameter_buffer[WINDOW_HEADER+28] = 0x00;

    parameter_buffer[WINDOW_HEADER+29] = 0x00; 

    parameter_buffer[WINDOW_HEADER+30] = 0x00;
    parameter_buffer[WINDOW_HEADER+31] = 0x00;
    parameter_buffer[WINDOW_HEADER+32] = 0x00;
    parameter_buffer[WINDOW_HEADER+33] = 0x00;
    parameter_buffer[WINDOW_HEADER+34] = 0x00;
    parameter_buffer[WINDOW_HEADER+35] = 0x00;
    parameter_buffer[WINDOW_HEADER+36] = 0x00;
    parameter_buffer[WINDOW_HEADER+37] = 0x00;
    parameter_buffer[WINDOW_HEADER+38] = 0x00;
    parameter_buffer[WINDOW_HEADER+39] = 0x00;

    parameter_buffer[WINDOW_HEADER+40] = 0x00;          // color 0=gray
    parameter_buffer[WINDOW_HEADER+41] = 0x00 | 0x01;   // 1=positive





    parameter_buffer[WINDOW_HEADER+42] = 0x00 | 0x02;       // 2=Thumbnail 
    parameter_buffer[WINDOW_HEADER+43] = 0x00 | 0x02;       // 4=high speed
    parameter_buffer[WINDOW_HEADER+44] = 0x00 | 0x02;
    parameter_buffer[WINDOW_HEADER+45] = 0x00;              // 

    parameter_buffer[WINDOW_HEADER+46] = (exposure >> 24) & 0xFF;   
    parameter_buffer[WINDOW_HEADER+47] = (exposure >> 16) & 0xFF;               
    parameter_buffer[WINDOW_HEADER+48] = (exposure >> 8) & 0xFF;                  
    parameter_buffer[WINDOW_HEADER+49] = exposure & 0xFF;




    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
    io_hdr.interface_id = 'S';
    io_hdr.mx_sb_len = sizeof(sense_buffer);
    io_hdr.sbp = sense_buffer;
    io_hdr.dxfer_len = sizeof(parameter_buffer);
    io_hdr.dxferp = parameter_buffer;     
    io_hdr.cmd_len = sizeof(setCmd);
    io_hdr.cmdp = setCmd;
    io_hdr.dxfer_direction = SG_DXFER_TO_DEV;               //  /usr/include/scsi/sg.h
    io_hdr.timeout = 20000; 




    for (int i = 0; i < 50; i++)
    {       
        if (ioctl(sg_device, SG_IO, &io_hdr) < 0) 
        {
            perror("SET ioctl error");
            return 1;
        }

        if (io_hdr.status == 0)
            break;

        usleep(20000);
    }

    printf("\nCOLOR:%d Status:%02X\n", color, io_hdr.status);



    return 0; 

}



int scan()
{


    unsigned char parameter_buffer[3];

    parameter_buffer[0] = 0x01;
    parameter_buffer[1] = 0x02;     
    parameter_buffer[2] = 0x03;



    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
    io_hdr.interface_id = 'S';
    io_hdr.mx_sb_len = sizeof(sense_buffer);
    io_hdr.sbp = sense_buffer;
    io_hdr.dxfer_len = sizeof(parameter_buffer);
    io_hdr.dxferp = parameter_buffer;     
    io_hdr.cmd_len = sizeof(scanCommand);
    io_hdr.cmdp = scanCommand;
    io_hdr.dxfer_direction = SG_DXFER_TO_DEV;               //  /usr/include/scsi/sg.h
    io_hdr.timeout = 20000; 



    if (ioctl(sg_device, SG_IO, &io_hdr) < 0) 
    {
        perror("SCAN ioctl error");
        return 1;
    }



    printf("scan: %02X\n", io_hdr.status);
    printSense(); 


    return 0;

}




int maxValue(int color)
{

    readCmd[2] = 0x00 | 0x81;   // Data type code       
    readCmd[4] = 0x00 | color;  // DATA_TYPE_QUAL_MSB
    readCmd[5] = 0x00 | 0x01;   // DATA_TYPE_QUAL_LSB   2-byte-data     
    readCmd[8] = 0x00 | 0x08;   // READ_DATA_HEADER + 2




    unsigned char parameter_buffer[8];

    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
    io_hdr.interface_id = 'S';
    io_hdr.mx_sb_len = sizeof(sense_buffer);
    io_hdr.sbp = sense_buffer;
    io_hdr.dxfer_len = sizeof(parameter_buffer);
    io_hdr.dxferp = parameter_buffer;     
    io_hdr.cmd_len = sizeof(readCmd);
    io_hdr.cmdp = readCmd;
    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;             //  /usr/include/scsi/sg.h
    io_hdr.timeout = 20000;




    if (ioctl(sg_device, SG_IO, &io_hdr) < 0) 
    {
        perror("READ ioctl error");
        return 1;
    }     


    printf("Max color %d: %02X%02X\n", color, parameter_buffer[6] & 0x3F, parameter_buffer[7]);


    return 0;   

}


uint32_t wbValue(int color)
{


    readCmd[2] = 0x00 | 0x8C;           // Data type code
    readCmd[4] = 0x00 | color;          // DATA_TYPE_QUAL_MSB   
    readCmd[5] = 0x00 | 0x03;           // DATA_TYPE_QUAL_LSB   
    readCmd[8] = 0x00 | 0x0A;           // READ_DATA_HEADER + 4



    unsigned char parameter_buffer[10];




    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
    io_hdr.interface_id = 'S';
    io_hdr.mx_sb_len = sizeof(sense_buffer);
    io_hdr.sbp = sense_buffer;
    io_hdr.dxfer_len = sizeof(parameter_buffer);
    io_hdr.dxferp = parameter_buffer;     
    io_hdr.cmd_len = sizeof(readCmd);
    io_hdr.cmdp = readCmd;
    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;             //  /usr/include/scsi/sg.h
    io_hdr.timeout = 20000;



    if (ioctl(sg_device, SG_IO, &io_hdr) < 0) 
    {
        perror("READ ioctl error");
        return 1;
    }


    uint32_t result = ((parameter_buffer[6] << 24) | 
                       (parameter_buffer[7] << 16) |
                       (parameter_buffer[8] << 8) |
                        parameter_buffer[9]);

    printf("WB%d: %08X\n", color, result);


    return result; 

}



float analogGain()
{


    readCmd[2] = 0x00 | 0x8A;           // Data type code
    readCmd[4] = 0x00 | 0x00;           // DATA_TYPE_QUAL_MSB   
    readCmd[5] = 0x00 | 0x03;           // DATA_TYPE_QUAL_LSB   
    readCmd[8] = 0x00 | 0x0E;           // READ_DATA_HEADER + 8



    unsigned char parameter_buffer[14];




    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
    io_hdr.interface_id = 'S';
    io_hdr.mx_sb_len = sizeof(sense_buffer);
    io_hdr.sbp = sense_buffer;
    io_hdr.dxfer_len = sizeof(parameter_buffer);
    io_hdr.dxferp = parameter_buffer;     
    io_hdr.cmd_len = sizeof(readCmd);
    io_hdr.cmdp = readCmd;
    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;             //  /usr/include/scsi/sg.h
    io_hdr.timeout = 20000;


    printf("\nAnalog Gain\n");

    if (ioctl(sg_device, SG_IO, &io_hdr) < 0) 
    {
        perror("READ ioctl error");
        return 1;
    }     
    printf("Read: %02X\n", io_hdr.status);


    printf("0    : %02X\n", parameter_buffer[0]);
    printf("1    : %02X\n", parameter_buffer[1]);
    printf("2-5  : %02X%02X%02X%02X\n", parameter_buffer[2], parameter_buffer[3], parameter_buffer[4], parameter_buffer[5]);


    printf(" 6: %02X\n", parameter_buffer[6]);
    printf(" 7: %02X\n", parameter_buffer[7]);
    printf(" 8: %02X\n", parameter_buffer[8]);
    printf(" 9: %02X\n", parameter_buffer[9]);

    printf("10: %02X\n", parameter_buffer[10]);
    printf("11: %02X\n", parameter_buffer[11]);
    printf("12: %02X\n", parameter_buffer[12]);
    printf("13: %02X\n", parameter_buffer[13]);



    union 
    {
        float result;
        unsigned char bytearray[sizeof(float)];
    } u;



    u.bytearray[3] = parameter_buffer[10];
    u.bytearray[2] = parameter_buffer[11];
    u.bytearray[1] = parameter_buffer[12];
    u.bytearray[0] = parameter_buffer[13];





    printf("Analog gain: %.7f\n", u.result);             





    return u.result;    

}



int coopActionParameter()
{

    readCmd[2] = 0x00 | 0x87;           // Data type code
    readCmd[3] = 0x00;
    readCmd[4] = 0x00;                  // no meaning
    readCmd[5] = 0x00;                  // 1 byte
    readCmd[6] = 0x00;
    readCmd[7] = 0x00;





    unsigned char parameter_buffer[24];




    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
    io_hdr.interface_id = 'S';
    io_hdr.mx_sb_len = sizeof(sense_buffer);
    io_hdr.sbp = sense_buffer;

    io_hdr.dxferp = parameter_buffer;     
    io_hdr.cmd_len = sizeof(readCmd);
    io_hdr.cmdp = readCmd;
    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;             //  /usr/include/scsi/sg.h
    io_hdr.timeout = 20000;





    printf("\nInitiator cooperative action parameter\n");


    readCmd[8] = 0x00 | 0x06;                       // READ_DATA_HEADER
    io_hdr.dxfer_len = 0x0 | 0x06;



    if (ioctl(sg_device, SG_IO, &io_hdr) < 0) 
    {
        perror("READ ioctl error");
        return 1;
    }

    printf("Read: %02X\n", io_hdr.status);

    printf("0    : %02X\n", parameter_buffer[0]);
    printf("1    : %02X\n", parameter_buffer[1]);
    printf("2-5  : %02X%02X%02X%02X\n", parameter_buffer[2], parameter_buffer[3], parameter_buffer[4], parameter_buffer[5]);




    readCmd[8] = 0x00 | 0x18;                       // READ_DATA_HEADER + 18    
    io_hdr.dxfer_len = sizeof(parameter_buffer);



    if (ioctl(sg_device, SG_IO, &io_hdr) < 0) 
    {
        perror("READ ioctl error");
        return 1;
    } 

    printf("Read: %02X\n", io_hdr.status);

    for (int i = READ_DATA_HEADER; i < READ_DATA_HEADER + parameter_buffer[5]; i++)
        printf("%d : %02X\n", i, parameter_buffer[i]);

    return 0;   

}





#define READBUFFER_MSB  0x04        // 2 * 3 * 186 = 1116 (045C)
#define READBUFFER_LSB  0x5C
#define READBUFFER      1116        // 



void readData()
{

    readCmd[2] = 0x00;          // Data type code
    readCmd[3] = 0x00;
    readCmd[4] = 0x00;
    readCmd[5] = 0x00;
    readCmd[6] = 0x00;
    readCmd[7] = 0x00 | READBUFFER_MSB;
    readCmd[8] = 0x00 | READBUFFER_LSB;





    unsigned char parameter_buffer[READBUFFER];



    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
    io_hdr.interface_id = 'S';
    io_hdr.mx_sb_len = sizeof(sense_buffer);
    io_hdr.sbp = sense_buffer;
    io_hdr.dxfer_len = sizeof(parameter_buffer);
    io_hdr.dxferp = parameter_buffer;     
    io_hdr.cmd_len = sizeof(readCmd);
    io_hdr.cmdp = readCmd;
    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;             //  /usr/include/scsi/sg.h
    io_hdr.timeout = 20000;



    unsigned char *buffer;

    buffer = NULL;


    // 1116 * 628 = 700848 bytes < 685*1024

    size_t mem_size = 685 * 1024;
    buffer = malloc(mem_size);
    if (buffer == NULL) 
    {
        printf("Buffer=NULL\n");
        return;
    } 



    printf("Read data\n");

    int bytes = 0;


    while (1)
    {

        if (ioctl(sg_device, SG_IO, &io_hdr) < 0) 
        {
            perror("READ ioctl error");
            return;
        }    

        if (io_hdr.status != 0)
            break;


        for (int i = 0; i < READBUFFER; i++)
            buffer[bytes + i] = parameter_buffer[i];

        bytes += READBUFFER;



        usleep(45000);      //microseconds

    }


    printf("Bytes: %d\n", bytes);



    FILE *write_ptr;

    write_ptr = fopen("../tmp/testThumb.bin", "wb");  // w for write, b for binary

    fwrite(buffer, 1, bytes, write_ptr); 

    fclose(write_ptr);

    free(buffer);


}






int main(int argc, char * argv[])
{


    if (argc != 2) 
    {
        printf("Usage: 'thumb /dev/sg<device number>'\n");
        return 1;
    }



    if ((sg_device = open(argv[1], O_RDWR)) < 0) 
    {
        perror("Error opening device");
        return 1;
    }



    modeselect();


    uint32_t wbR = wbValue(RED);
    uint32_t wbG = wbValue(GREEN);
    uint32_t wbB = wbValue(BLUE);


    maxValue(1);     
    maxValue(2);
    maxValue(3);

    analogGain();



    set(0, wbG);            // default same as GREEN
    set(RED, wbR);    
    set(GREEN, wbG);
    set(BLUE, wbB);



    scan();


    coopActionParameter();

    scan(); 


    sleep(3);  
    readData();





    close(sg_device);
    return 0;

}

我发现 9000ED 没有自动曝光,所以我也必须为此编写代码。在“驱动程序”成为实用和通用的工具之前,显然还有很多工作要做,但我已经解决了所有的原则问题。我当然会感谢所有的帮助、建议和评论。

这个项目对我来说是一次很棒的学习经历,我将在未来几年继续(断断续续地)致力于它。

于 2019-07-30T15:11:30.363 回答
0

我很高兴地说,我设法将 SCSI 通用驱动程序用于我的目的。

这是一个完整的程序,专为弹出尼康 LS-9000 ED 扫描仪的胶片托盘而定制。

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <scsi/sg.h> 

/* A program to eject the tray of a Nikon LS-9000 ED scanner
*  using the Linux SCSI generic (sg) driver.
*
*  Based on the example by D. Gilbert at
*  http://tldp.org/HOWTO/SCSI-Generic-HOWTO/pexample.html
*/



#define OPCODE_EXECUTE  0xC1
#define OPCODE_SET      0xE0                

#define CMD_EXECUTE_LEN 6
#define CMD_SET_LEN     10

#define EJECT_TRAY      0xD0    


#define PARAM_LEN       0xD



int main(int argc, char * argv[])
{

    int sg_device;

    unsigned char setCmdBlk[CMD_SET_LEN] =
        {OPCODE_SET, 0, EJECT_TRAY, 0, 0, 0, 0, 0, PARAM_LEN, 0};

    unsigned char exeCmdBlk[CMD_EXECUTE_LEN] =
        {OPCODE_EXECUTE, 0, 0, 0, 0, 0};

    unsigned char sense_buffer[32];
    sg_io_hdr_t io_hdr;


    if (argc != 2) 
    {
        printf("Usage: 'eject /dev/sg<device number>'\n");
        return 1;
    }

    if ((sg_device = open(argv[1], O_RDWR)) < 0) 
    {
        perror("Error opening device");
        return 1;
    }


    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
    io_hdr.interface_id = 'S';
    io_hdr.cmd_len = sizeof(setCmdBlk);
    io_hdr.mx_sb_len = sizeof(sense_buffer);
    io_hdr.dxfer_direction = SG_DXFER_NONE;
    io_hdr.dxfer_len = 0;
    io_hdr.dxferp = NULL;
    io_hdr.cmdp = setCmdBlk;
    io_hdr.sbp = sense_buffer;
    io_hdr.timeout = 20000; 

    if (ioctl(sg_device, SG_IO, &io_hdr) < 0) 
    {
        perror("SET PARAM ioctl error");
        return 1;
    }


    io_hdr.cmd_len = sizeof(exeCmdBlk);
    io_hdr.cmdp = exeCmdBlk;

    if (ioctl(sg_device, SG_IO, &io_hdr) < 0) 
    {
        perror("EXECUTE ioctl error");
        return 1;
    }


    close(sg_device);
    return 0;

}

我认为现在原则上应该可以解决在 Linux 下与尼康扫描仪的通信和使用问题。

显然,要实际生成扫描并实现控制扫描仪的所有设置,还有很多工作要做。我计划制作一个函数库和一个桌面应用程序。目标是制作一个与 Nikon Scan 4.0.3 大体相似的界面。

谢谢您的帮助。

于 2017-12-13T16:26:21.220 回答