Linux SCSI programming

Hi,

I'm trying to port a Windows SCSI API over to Linux. The API has several functions, most of them using ATA_PASS_THROUGH_EX to send a vendor specific command over to the device. I've managed to port 1 of the API function that is a modified INQUIRY command. However, I'm very confused as to how ATA passthrough is done in Linux. I've looked at the sg3_utils source code to get some inspiration but it's too complicated for me to understand what's going on.

Does anyone know how to port this?

In Windows a ATA_PASS_THROUGH_EX pointer is declared and memory allocated for it. It's fields are then initialized(Length, AtaFlags, DataTransferLength, TimeOutValue, DataBufferOffset, CurrentTaskFile[], PreviousTaskFile[], etc) and then a DeviceIoControl() is called.

Now I know Linux's version of DeviceIoControl is ioctl() and I've used it successfully for the INQUIRY command, but what's Linux's version of ATA_PASS_THROUGH_EX?

Any help would be appreciated. Thanks!
Hi,

I figured out some basics from documentation and code examples.

I'm now trying to read/write to specific sectors (512 bytes) in a SATA storage media(/dev/sda) using ioctl and some simple SCSI read/write commands. I'm having some problems with consistency and just getting it to work. Here's what I've tried:

Using read(10)/read(12), when I send the ioctl over to the device, instead of getting the 1st 512 bytes, I get some identification info instead. For example if the drive is a Toshiba drive I'd see the TOSHIBA and the model number, which is actually the ata ident information.

When I use read(6) and send the ioctl, this seems to work for my HDD despite read(6)'s transfer length field being only 8 bytes. I guess the ioctl's transfer length overrides the SCSI CDB's transfer length? I don't know. Despite this, on some HDDs the io_hdr.info and io_hdr.masked_status is non-zero, which indicates a possible error?

However, when I use read(6) to test on a USB memory stick, it gives me the ident information again instead of the 512byte sector.

As for writes, I've only tried write(6) so far, but whenever I send the ioctl out, it would just hang there forever until I close the shell. I'm wondering what I'm doing wrong?

My function (in this case reading a 512byte sector from LBA 0) is as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
int read_sector(const char *device_name, unsigned char *data)
  int fd, res;
  const int t_length = 512;              // 512 bytes transferred
  unsigned char rdCmdBlk6[SCSI_CDB6_LEN] = 
    { SCSI_READ6, // Command
      0, 0, 0, 0, 0 };
  unsigned char sense_b[SENSE_BUFF_LEN];
  sg_io_hdr_t io_hdr;

  // Open device
  fd = open(device_name, O_RDONLY);

  // Prepare SCSI READ (6) command
  rdCmdBlk6[1]  = 0x00;           // LBA
  rdCmdBlk6[2]  = 0x00;           // LBA
  rdCmdBlk6[3]  = 0x00;           // LBA
  rdCmdBlk6[4]  = t_length;       // transfer length

  // Prepare the sg_io_hdr_t structure
  memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
  io_hdr.interface_id = 'S';                  // Always set to 'S' for sg driver
  io_hdr.cmd_len = sizeof(rdCmdBlk6);         // Size of SCSI command
  io_hdr.mx_sb_len  = sizeof(sense_b);        // Max sense buffer size(for error)
  io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; // Data transfer direction(no data)
  io_hdr.dxfer_len = t_length;                // Data transfer length(512)
  io_hdr.dxferp = data;                       // Data transfer buffer(none)
  io_hdr.cmdp = rdCmdBlk6;                    // SCSI command buffer
  io_hdr.sbp = sense_b;                       // Sense buffer
  io_hdr.timeout = 5000;                      // Timeout(5s)

  // Sends the command to device
  if ((res = ioctl(fd, SG_IO, &io_hdr)) < 0) {
    close(fd);
    return -1;
  }

  // Error processing
  if ( ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) || // check info
       (io_hdr.masked_status != 0x00) ||                  // check status(0 if ioctl success)
       (io_hdr.msg_status != 0x00) ||                     // check message status
       (io_hdr.host_status != 0x00) ||                    // check host status
       (io_hdr.driver_status != 0x00) )                   // check driver status
  {
    close(fd);
    return -1;
  } else 
  {
    close(fd);
    return 0;
  }
}


The write_sector() code is similar except for appropriate changes in io_hdr and wrCmdBlk[].

Anyone knows what's going on?

Thanks!
Last edited on
Topic archived. No new replies allowed.