Asynchronous method to send an SCSI command

IOCTL is the synchronous(blocking) method to send a command. This means that IOCTL will return only when the command sent is either complete or failed to complete. There is little difference in the synchronous and asynchronous method of sending a command using SCSI Genric. Every command goes through three stages:

  1. The command will be received by SCSI Generic driver, resources will be allocated in kernel memory, if any data needs to be copied then that will be copied; after all of that command will be submitted to MID LEVEL of SCSI Genric driver.
  2. After submitting the command to mid layer of SCSI driver; it will wait for the interrupt which will acknowledge the completion of the command i.e if data was asked then the data is either in Kernel Space(in the case of Indirect IO) or in User Space (in the case of Direct IO).
  3. Data in Kernel Space will be copied in User Space. If necessary Sense Data will be copied in User Space.

In the case of IOCTL, all the three stages will complete in one go, thus making IOCTL calls synchronous. But internally IOCTL uses Write and Read calls to complete the three stages. Write accomplices the first stage i.e it simply submits the command and returns, whereas Read does the rest of the work. Read will wait for the interrupt. Thus Write is non-blocking and Read is blocking. So we can use Write to asynchronously launch the command and then use the Read to wait for its completion.

NOTE: Read and Write calls here are for device file read and write and ATA Read and ATA write commands will be sent using them but not before forming CDB and sg_io_hdr structure.

Write call syntax(read the link for further details):

write(int fd, const void* buffer, size_t count);

The ‘buffer’ should point to an object of type sg_io_hdr_t and ‘count’ should be sizeof(sg_io_hdr_t). If the write() call succeeds then the ‘count’ is returned as the result.

Read call syntax(read this link for further details):

read(int fd, void* buffer, size_t count);

The ‘buffer’ should point to an object of type sg_io_hdr_t and ‘count’ should be sizeof(sg_io_hdr_t). If the read() call succeeds then the ‘count’ is returned as the result.

Let’s understand this with SCSI READ COMMAND. I have explained the IOCTL method of sending SCSI READ command in one of my POST. We will modify the STEP 5 there for making it asynchronous i.e replace the IOCTL single call with Write and Read call. Rest all steps would remain the same.

Below is the final code for sending SCSI READ(16) command asynchronously:

GITHUB Link for Downloading the CODE.

  #include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<error.h>
#include<sys/ioctl.h>
#include<sys/types.h>
#include<sys/mman.h>
#include<scsi/scsi_ioctl.h>
#include<scsi/sg.h>

////////////////this program takes a LBA from user and reads that
////////////////from the device-file passed from the command line
///////////////compil:   gcc read.c
///////////////execute: sudo ./a.out /dev/sda
///////////////”/dev/sda” is the device file name

#define REPLY_LEN 512  // length of the reply from drive
#define CMD_LEN 16     // cdb length

int main(int argc, char* argv[]){
int fd,i;
int lba;

printf(“enter the lba you want to read: “);
scanf(“%d”,&lba);

if(lba>512){
printf(“lba is greater than 512\n”);
return 1;
}

unsigned char read16cmd[CMD_LEN] =
{0x88,0,0,0,0,0,0,0,0,lba,0,0,1,0,0};

sg_io_hdr_t io_hdr;  //object of structure

char* file_name = 0; // to hold the device name passed from terminal
unsigned char data_buffer[REPLY_LEN];
unsigned char sense_buffer[32];

if(argc < 2){
printf(“please enter a device file\n”);
return 1;
}else{
file_name = argv[1];
}

/////////opening the device file/////////////

if((fd = open(file_name,O_RDWR))<0){
printf(“device file opening failed\n”);
return  1;
}

/////////// data buffer ///////////
printf(“********data buffer initially***********\n”);
for(i=0;i<512;i++){
printf(“%hx “,data_buffer[i]);
}
printf(“\n”);

////////////////prepare read///////////////
memset(&io_hdr,0,sizeof(sg_io_hdr_t));
io_hdr.interface_id = ‘S’;
io_hdr.cmd_len = sizeof(read16cmd);
io_hdr.mx_sb_len = sizeof(sense_buffer);
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;// macro is defined in scsi.h
io_hdr.dxfer_len = REPLY_LEN;
io_hdr.dxferp = data_buffer;
io_hdr.cmdp = read16cmd;
io_hdr.sbp = sense_buffer;
io_hdr.timeout = 20000;

/*
if(ioctl(fd,SG_IO,&io_hdr)<0){
printf(“ioctl failed\n”);
return 1;
}

*/

////////asynchronous method for sending command////////
int ret = 0;
ret = write(fd,&io_hdr,sizeof(sg_io_hdr_t));
if(ret == sizeof(sg_io_hdr_t)) printf(“write was successful\n”);
else printf(“write failed\n”);
ret = 0;
ret = read(fd,&io_hdr,sizeof(sg_io_hdr_t));
if(ret == sizeof(sg_io_hdr_t) ) printf(“read was successful\n”);
else printf(“read failed\n”);

printf(“********data buffer after ioctl***********\n”);
for(i=0;i<512;i++){
printf(“%hx “,data_buffer[i]);
}
printf(“\n”);

return 1;
}

NOTE: Code is same as my “READ an LBA” Blog just IOCTL call has been replaced with Write and Read call.

 

GOOD DAY.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s