文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>Tour the Linux generic SCSI driver(2)

Tour the Linux generic SCSI driver(2)

时间:2009-03-08  来源:soararing

Example: Executing an inquiry command

An inquiry command is the most common SCSI command that all SCSI devices implement. This command is used to request the basic information of the SCSI device and is often used as a ping operation to test to see if the SCSI device is online. Table 2 shows how the SCSI standard is defined.


Table 2. The inquiry command format definition

 

bit 7

bit 6

bit 5

bit 4

bit 3

bit 2

bit 1

bit 0

byte 0

Operation code = 12h

byte 1

LUN

Reserved

EVPD

byte 2

Page code

byte 3

Reserved

byte 4

Allocation length

byte 5

Control

If the EVPD parameter bit (for enable vital product data) is zero and the Page Code parameter byte is zero, then the target will return the standard inquiry data. If the EVPD parameter is one, then the target will return vendor-specific data according to the page code fields.

Listing 2 shows the source code clips of using the SCSI generic API. Let's first look at the examples of setting sg_io_hdr.


Listing 2. Setting sg_io_hdr

 

struct  sg_io_hdr * init_io_hdr() {

  struct sg_io_hdr * p_scsi_hdr = (struct sg_io_hdr *)malloc(sizeof(struct sg_io_hdr));

  memset(p_scsi_hdr, 0, sizeof(struct sg_io_hdr));

  if (p_scsi_hdr) {

   p_scsi_hdr->interface_id = 'S'; /* this is the only choice we have! */

    /* this would put the LUN to 2nd byte of cdb*/

    p_scsi_hdr->flags = SG_FLAG_LUN_INHIBIT;

  }

  return p_scsi_hdr;

}

 

void destroy_io_hdr(struct sg_io_hdr * p_hdr) {

    if (p_hdr) {

        free(p_hdr);

    }

}

 

void set_xfer_data(struct sg_io_hdr * p_hdr, void * data, unsigned int length) {

    if (p_hdr) {

        p_hdr->dxferp = data;

        p_hdr->dxfer_len = length;

    }

}

 

void set_sense_data(struct sg_io_hdr * p_hdr, unsigned char * data,

        unsigned int length) {

    if (p_hdr) {

        p_hdr->sbp = data;

        p_hdr->mx_sb_len = length;

    }

}

 

These functions are used for setting the sg_io_hdr object. Some of the fields point to user space memory; when the execution is finished, inquiry output data from the SCSI command is copied into the memory where dxferp points to. If there is an error and the sense data is required, the sense data would be copied to where sbp points to. The example for sending an inquiry command to a SCSI target is shown in Listing 3.


Listing 3. Sending an inquiry command to a SCSI target

 

int execute_Inquiry(int fd, int page_code, int evpd, struct sg_io_hdr * p_hdr) {

    unsigned char cdb[6];

    /* set the cdb format */

    cdb[0] = 0x12; /*This is for Inquery*/

    cdb[1] = evpd & 1;

    cdb[2] = page_code & 0xff;

    cdb[3] = 0;

    cdb[4] = 0xff;

    cdb[5] = 0; /*For control filed, just use 0 */

   

    p_hdr->dxfer_direction = SG_DXFER_FROM_DEV;

    p_hdr->cmdp = cdb;

    p_hdr->cmd_len = 6;

 

    int ret = ioctl(fd, SG_IO, p_hdr);

    if (ret<0) {

        printf("Sending SCSI Command failed.\n");

        close(fd);

        exit(1);

    }

    return p_hdr->status;

}

 

So the function first prepares the CDB according to the inquiry standard format and then calls the ioctl() function, handing file descriptor SG_IO, and the sg_io_hdr object; the return status is stored in the status field of the sg_io_hdr object.

Now let's see how the application program uses this function to execute the inquiry command (Listing 4):


Listing 4. Application program executes the inquiry command

 

unsigned char sense_buffer[SENSE_LEN];

unsigned char data_buffer[BLOCK_LEN*256];

void test_execute_Inquiry(char * path, int evpd, int page_code) {

    struct sg_io_hdr * p_hdr = init_io_hdr();

    set_xfer_data(p_hdr, data_buffer, BLOCK_LEN*256);

    set_sense_data(p_hdr, sense_buffer, SENSE_LEN);

    int status = 0;

    int fd = open(path, O_RDWR);

    if (fd>0) {

        status = execute_Inquiry(fd, page_code, evpd, p_hdr);

        printf("the return status is %d\n", status);

        if (status!=0) {

            show_sense_buffer(p_hdr);

        } else{

            show_vendor(p_hdr);

            show_product(p_hdr);

            show_product_rev(p_hdr);

        }

    } else {

        printf("failed to open sg file %s\n", path);

    }

    close(fd);

    destroy_io_hdr(p_hdr);

}

 

The process of sending the SCSI command here is quite simple. First the user space data buffer and sense buffer should be allocated and made to point to the sg_io_hdr object. Then, open the device driver and get the file descriptor. With these parameters, the SCSI command could be sent to the target device. The output from the SCSI target would then be copied to the user space buffers when the command is finished.


Listing 5. Using parameters to send SCSI command to target device

 

void show_vendor(struct sg_io_hdr * hdr) {

    unsigned char * buffer = hdr->dxferp;

    int i;

    printf("vendor id:");

    for (i=8; i<16; ++i) {

        putchar(buffer[i]);

    }

    putchar('\n');

}

 

void show_product(struct sg_io_hdr * hdr) {

    unsigned char * buffer = hdr->dxferp;

    int i;

    printf("product id:");

    for (i=16; i<32; ++i) {

        putchar(buffer[i]);

    }

    putchar('\n');

}

 

void show_product_rev(struct sg_io_hdr * hdr) {

    unsigned char * buffer = hdr->dxferp;

    int i;

    printf("product ver:");

    for (i=32; i<36; ++i) {

        putchar(buffer[i]);

    }

    putchar('\n');

}

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

    test_execute_Inquiry(argv[1], 0, 0);

    return EXIT_SUCCESS;

}

 

The standard response of the SCSI Inquiry Command (Page Code and EVPD fields are all set to 0) is complicated. According to the standard, vendor ID extends from the 8th to the 15th byte, product ID from the 16th to the 31st byte, and product version from the 32nd to the 35th byte. This information could be retrieved to check whether the command has been successfully executed.

After building this simple example, run it on /dev/sg0, which is normally the local hard disk. You should get the following:

[root@taomaoy scsi_test]# ./scsi_test /dev/sg0

the return status is 0

vendor id:ATA

product id:ST3160812AS

product ver:3.AA

 

The result is the same as the sg_map tool reports.

 

Back to top

 

Summary

Linux provides a generic driver for SCSI devices and an application programming interface so you can build applications to send SCSI commands directly to SCSI devices. You can manually make SCSI commands and set other related parameters in an sg_io_hdr object, then call ioctl() to execute their SCSI commands and get output from the same sg_io_hdr object.

 

Back to top

 

Download

Description

Name

Size

Download method

Sample code for this article

scsi_test.zip

3KB

HTTP

 

Information about download methods

 

 

 

Resources

Learn

  • Want more on Linux and the SCSI subsystem? Try these:
    • "Anatomy of the Linux SCSI subsystem" (developerWorks, November 2007) introduces the Linux SCSI subsystem and discusses where this subsystem is going in the future.
    • "Anatomy of Linux synchronization methods" (developerWorks, October 2007) outlines atomic synchronization operations (often used by SCSI drivers).
    • "GCC hacks in the Linux kernel" (developerWorks, November 2008) introduces the GNU Compiler Collection suite; in it you'll find an example of range extension in use in a SCSI switch block (Listing 2).

 


Get products and technologies

  • With IBM trial software, available for download directly from developerWorks, build your next development project on Linux.


Discuss

相关阅读 更多 +
排行榜 更多 +
几何飞行内购修改版

几何飞行内购修改版

飞行射击 下载
别踩白块内购修改版

别踩白块内购修改版

休闲益智 下载
乐涂数字填色游戏

乐涂数字填色游戏

休闲益智 下载