Search code examples
cioctlscsi

how to fix 'invalid argument' for ioctl requests to block device


I'm writing a small c program to make tape status and seek requests via

        ioctl(int fd, long int request, &io_buf) 

but after trial and plenty of error, ioctl is returning -1 with the errno message "Invalid Argument"

I'm on Linux and running my program as sudo. The device I want to issue requests to is an optical drive, connected via SCSI. I've tried tape status and seek requests by passing requests (MTIOCGET or MTIOCTOP, respectively) to ioctl.

Code snippet for tape status function where fd is the file descriptor of the device returned by open() and mtgetbuf is an instance of the mtget struct from sys/mtio.h

       stat = ioctl(fd, MTIOCGET, &mtgetbuf);
       if (stat == -1)
       {
          perror("error on ioctl MTIOCGET request: ")
          return EXIT_FAILURE;
       }

Similar code snippet for seek tape function except mtopbuf is an instance of the mtop structure and MTSEEK is the defined op code for the seek operation, also in sys/mtio.h

        mtopbuf.mt_op = MTSEEK;
        stat = ioctl(fd, MTIOCTOP, &mtopbuf);
        if (stat == -1)
        {
           perror("error on ioctl MTIOCGET request: ")
           return EXIT_FAILURE;
        }

Instead of invalid argument error messages and a return of -1, I would have expected a successful return from ioctl and the respective structure instances, mtgetbuf and mtopbuf, to have their members populated with data provided by the device.

I.e. A successful ioctl() command with the MTIOCGET request would return into the mtgetbuf mt_type member a value of either MT_ISSCSI1, MT_ISSCSI2, or MT_ISUNKNOWN (I don't believe it is any of the other defined values for other vendor-specific devices).

Note: I'm aware of the linux/mtio.h header file and I have tried including that in place of sys/mtio.h but the outcome is the same.


Solution

  • I've recently had success issuing requests to a block device using the SCSI Generic Linux driver (SG). There are three header files (below) that have provided op codes, structures used to pass and retrieve data from the device, among other information.

    SCSI SG Header files:

            /usr/include/scsi/scsi.h
            /usr/include/scsi/scsi_ioctl.h
            /usr/include/scsi/sg.h
    

    A combination of online resources were instrumental in understanding how to package, send, and receive requests:

    1) The TLDP SCSI Generic (sg) HOW-TO guide is a font of information on communicating to SCSI devices via the SG driver. A link to it is provided here. It explains in detail various commands that can be issued, how to package the commands by creating an instance of the sg_io_hdr_t structure, as well as a programming example to send a SCSI INQUIRY command which returns basic vendor information of the device. There are also status and sense codes for error handling and understanding unsuccessful SCSI requests.

    2) Seagate's SCSI Command Reference Manual was helpful at times to understand the structure of bytes/bits in a SCSI command. Typically the op code occupied the first byte and the remaining bytes were zeros. The op codes in this reference manual were defined between those three header files mentioned above.

    I have been able to send successful INQUIRY and GET_SG_VERSION_NUMBER requests and most likely have been able to send SEEK(6), READ_CAPACITY(10), and REZERO_UNIT commands. I say most likely because -1/errno values are not being returned and no information is being passed back into the sense buffer which is an indication of warnings/errors (either SCSI, host adapter, or driver status codes).

    Hope this answers OPs question.