http://gmd20.blog.163.com/blog/static/16843923201002274341552/

Linux中的sg驱动 (“The Linux SCSI Generic (sg) Driver“ http://sg.danny.cz/sg/index.html) 是一个通用SCSI设备的模型,应用程序通过定义的“SG_IO ioctl ”可以像scsi磁盘等设备发送自定义的scsi指令集。  2.6版本的,这个ioctl 将在  block驱动层的 (http://lxr.linux.no/#linux+v2.6.32/block/scsi_ioctl.c 文件的 
static int sg_io(struct request_queue *q, struct gendisk *bd_disk, struct sg_io_hdr *hdr, fmode_t mode)

函数中得到处理,然后组成 request发送的底层的 scsi驱动。
使用的关键数据结构在  /include/scsi/sg.h 里面定义

  83typedef struct sg_io_hdr84{85    int interface_id;           /* [i] 'S' for SCSI generic (required) */86    int dxfer_direction;        /* [i] data transfer direction  */87    unsigned char cmd_len;      /* [i] SCSI command length ( <= 16 bytes) */88    unsigned char mx_sb_len;    /* [i] max length to write to sbp */89    unsigned short iovec_count; /* [i] 0 implies no scatter gather */90    unsigned int dxfer_len;     /* [i] byte count of data transfer */91    void __user *dxferp;        /* [i], [*io] points to data transfer memory92                                              or scatter gather list */93    unsigned char __user *cmdp; /* [i], [*i] points to command to perform */94    void __user *sbp;           /* [i], [*o] points to sense_buffer memory */95    unsigned int timeout;       /* [i] MAX_UINT->no timeout (unit: millisec) */96    unsigned int flags;         /* [i] 0 -> default, see SG_FLAG... */97    int pack_id;                /* [i->o] unused internally (normally) */98    void __user * usr_ptr;      /* [i->o] unused internally */99    unsigned char status;       /* [o] scsi status */100    unsigned char masked_status;/* [o] shifted, masked scsi status */101    unsigned char msg_status;   /* [o] messaging level data (optional) */102    unsigned char sb_len_wr;    /* [o] byte count actually written to sbp */103    unsigned short host_status; /* [o] errors from host adapter */104    unsigned short driver_status;/* [o] errors from software driver */105    int resid;                  /* [o] dxfer_len - actual_transferred */106    unsigned int duration;      /* [o] time taken by cmd (unit: millisec) */107    unsigned int info;          /* [o] auxiliary information */108} sg_io_hdr_t;  /* 64 bytes long (on i386) */

关于具体用法,可以在“The Linux SCSI Generic (sg) HOWTO http://tldp.org/HOWTO/SCSI-Generic-HOWTO/index.html” 找到一些说明,“sg3_utils(http://sg.danny.cz/sg/sg3_utils.html)”的源码包里面的代码很不错的参考例 子,提供了各种scsi指令的封装代码,简单的scsi指令测试也可以直接诶使用sg3_utils里面的命令就可以了。  更多信息可以参考http://sg.danny.cz/sg/index.html 站点。

scatter-gather DMA
是一些磁盘或者网络硬件的扩展特性,可以让硬件设备在一次DMA映射里面访问不连续的多块内存,利用这个特性,可以直接让用户空间的地址直接映射DMA上 面去,减少传输过程中的数据复制量,提高系统西能。 “ Understanding the linux kernel ”一书的“block device” 一章的开头部分有介绍。

如果构建多段的 scatter-gather scsi request,可以参考sg3_utils 中的example目录下的sg_iovec_tst 那个例子。我简单修改一下源代码,发送一个自定义的scatter-gather列表的read10 来读磁盘的scsi request到设备的代码。其中 单个request的扇区总数和 scatter-gather列表个数的限制和block驱动层数和scsi3层驱动的scsi_host等参数设置有关,scatter-gather 列表还可能依赖于硬件实现。

#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "sg_lib.h"
#include "sg_io_linux.h"

/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
device driver.
*  Copyright (C) 2003-2007 D. Gilbert
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2, or (at your option)
*  any later version.

This program will read a certain number of blocks of a given block size
from a given sg device node and write what is retrieved out to a
normal file. The purpose is to test the sg_iovec mechanism within the
sg_io_hdr structure.

Version 0.12 (20070121)
*/

#define ME "sg_iovec_tst: "

#define A_PRIME 509
#define IOVEC_ELEMS 2048

#define SENSE_BUFF_LEN 32
#define DEF_TIMEOUT 40000       /* 40,000 milliseconds */

struct sg_iovec iovec[IOVEC_ELEMS];

/* Returns 0 if everything ok */
int sg_read(int sg_fd, unsigned char * buff, int num_blocks, int from_block,
int bs)
{
unsigned char rdCmd[10] = {READ_10, 0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned char senseBuff[SENSE_BUFF_LEN];
struct sg_io_hdr io_hdr;
int dxfer_len = bs * num_blocks;
int k=0, pos =0, rem=0;

rdCmd[2] = (unsigned char)((from_block >> 24) & 0xff);
rdCmd[3] = (unsigned char)((from_block >> 16) & 0xff);
rdCmd[4] = (unsigned char)((from_block >> 8) & 0xff);
rdCmd[5] = (unsigned char)(from_block & 0xff);
rdCmd[7] = (unsigned char)((num_blocks >> 8) & 0xff);
rdCmd[8] = (unsigned char)(num_blocks & 0xff);

/*
for (k = 0, pos = 0, rem = dxfer_len; k < IOVEC_ELEMS; ++k) {
iovec[k].iov_base = buff + pos;
iovec[k].iov_len = (rem > A_PRIME) ? A_PRIME : rem;
if (rem <= A_PRIME)
break;
pos += A_PRIME;
rem -= A_PRIME;
}

*/
//create 32 x 509 scatter-gather list , so remain ">128kb data"  was split into the second request.

for (k = 0; k < 32; ++k) {
iovec[k].iov_base = buff + pos;
iovec[k].iov_len = 509;
pos += 512;
}

//put the remain into 2 scatter-gather list , so we don't exceed maxnium 64 segments.
rem = dxfer_len - (k+1) * 509 ;

iovec[k++].iov_base = buff + pos;
iovec[k++].iov_len = (rem /2) -3;

pos += rem /2 ;
iovec[k++].iov_base = buff + pos;
iovec[k++].iov_len = rem - (rem /2) + 3;

if (k >= IOVEC_ELEMS) {
fprintf(stderr, "Can't fit dxfer_len=%d bytes in iovec\n", dxfer_len);
return -1;
}
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
io_hdr.interface_id = 'S';
io_hdr.cmd_len = sizeof(rdCmd);
io_hdr.cmdp = rdCmd;
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.dxfer_len = dxfer_len;
io_hdr.iovec_count = k + 1;
io_hdr.dxferp = iovec;
io_hdr.mx_sb_len = SENSE_BUFF_LEN;
io_hdr.sbp = senseBuff;
io_hdr.timeout = DEF_TIMEOUT;
io_hdr.pack_id = from_block;

if (ioctl(sg_fd, SG_IO, &io_hdr)) {
perror("reading (SG_IO) on sg device, error");
return -1;
}
switch (sg_err_category3(&io_hdr)) {
case SG_LIB_CAT_CLEAN:
break;
case SG_LIB_CAT_RECOVERED:
fprintf(stderr, "Recovered error while reading block=%d, num=%d\n",
from_block, num_blocks);
break;
case SG_LIB_CAT_UNIT_ATTENTION:
fprintf(stderr, "Unit attention\n");
return -1;
default:
sg_chk_n_print3("reading", &io_hdr, 1);
return -1;
}
return 0;
}

int main(int argc, char * argv[])
{
int sg_fd, fd, res, j, m, dxfer_len;
unsigned int k, num;
int do_help = 0;
int blk_size = 512;
int count = 0;
char * sg_file_name = 0;
char * out_file_name = 0;
unsigned char * buffp;

for (j = 1; j < argc; ++j) {
if (0 == strncmp("-b=", argv[j], 3)) {
m = 3;
num = sscanf(argv[j] + m, "%d", &blk_size);
if ((1 != num) || (blk_size <= 0)) {
printf("Couldn't decode number after '-b' switch\n");
sg_file_name = 0;
break;
}
}
else if (0 == strncmp("-c=", argv[j], 3)) {
m = 3;
num = sscanf(argv[j] + m, "%d", &count);
if (1 != num) {
printf("Couldn't decode number after '-c' switch\n");
sg_file_name = 0;
break;
}
}
else if (0 == strcmp("-h", argv[j]))
do_help = 1;
else if (*argv[j] == '-') {
printf("Unrecognized switch: %s\n", argv[j]);
sg_file_name = 0;
break;
}
else if (NULL == sg_file_name)
sg_file_name = argv[j];
else
out_file_name = argv[j];
}
if ((NULL == sg_file_name) || (NULL == out_file_name) || (0 == count)) {
printf("Usage: sg_iovec_tst [-h] [-b=num] -c=num <generic_device> "
"<output_filename>\n");
printf("  where: -h       this usage message\n");
printf("         -b=num   block size (default 512 Bytes)\n");
printf("         -c=num   count of blocks to transfer\n");
printf(" reads from <generic_device> and sends to <output_filename>\n");
return 1;
}

sg_fd = open(sg_file_name, O_RDONLY);
if (sg_fd < 0) {
perror(ME "sg device node open error");
return 1;
}
/* Don't worry, being very careful not to write to a none-sg file ... */
res = ioctl(sg_fd, SG_GET_VERSION_NUM, &k);
if ((res < 0) || (k < 30000)) {
printf(ME "not a sg device, or driver prior to 3.x\n");
return 1;
}
fd = open(out_file_name, O_WRONLY | O_CREAT, 0666);
if (fd < 0) {
perror(ME "output file open error");
return 1;
}

count = 512 -1 ;  //don't exceed 256kb IO
blk_size = 512;

dxfer_len = count * blk_size;  //最终生成的request的大小

const int inter_space= 1024;

buffp = (unsigned char *)malloc(dxfer_len + inter_space);
if (buffp) {
if (0 == sg_read(sg_fd, buffp, count, 0, blk_size)) {
if (write(fd, buffp, dxfer_len) < 0)
perror(ME "output write failed");
}
free(buffp);
}
res = close(fd);
if (res < 0) {
perror(ME "output file close error");
close(sg_fd);
return 1;
}
res = close(sg_fd);
if (res < 0) {
perror(ME "sg device close error");
return 1;
}
return 0;
}

使用SG_IO发送SCSI 指令测试底层驱动的scatter-gather 功能相关推荐

  1. Linux USB 驱动入门之发送SCSI 指令READ_10给U盘读取数据

    以下模块实现了发送SCSI指令READ_10给U盘,然后读取指定Block的数据. 只是证明此模块的READ_10的实现没有问题,没有对读取的数据做任何处理,只是打印了接收到多少个Byte. #def ...

  2. linux USB驱动入门之发送SCSI指令INQUERY给U盘获取U盘信息

    以下驱动代码参考了 Bus Hound 抓包数据 . USB设备是一个金士顿的U盘 . 发送INQUERY 0x12,返回内容为 KingstonDataTraveler 3.0. 测试的内核版本是 ...

  3. 基于Linux操作系统的底层驱动技术

    5.3 基于Linux操作系统的底层驱动技术 这里的底层驱动是指Linux下的底层设备驱动,这些驱动通常都是加载在内核态的,可以提供给上层用户态的应用程序访问底层设备的能力.也就是说,上层应用程序通过 ...

  4. 可复用的基于ARM的W5100底层驱动设计

    摘要: 为了缩短基于ARM的网络化嵌入式应用开发周期.降低开发成本和提高产品质量,提出一种有别于传统利用操作系统开发嵌入式应用的模式.在该模式的框架下,对W5100网络芯片底层驱动进行编写.封装和测试 ...

  5. 【ESP01S】使用串口调试助手,发送AT指令收回的是乱码/重复一遍AT指令发回的问题

    调试帮助,技术交流Q:1083091092(备注CSDN) 一. 问题描述 在使用ESP01S,外观如下图所示: 使用串口调试助手,发送AT指令出现异常情况,比如返回乱码或者重复一遍用户发送的内容 二 ...

  6. NXP S32K146 FLEXI2C底层驱动+IAM-20680(二)

    NXP S32K146 FLEXI2C底层驱动+IAM-20680(一) 在上一篇文章的基础上,写IAM-20680的接口层,与上一篇差不多,os任务中分为初始化与正常运行,下面根据IAM-30680 ...

  7. 发送ZPL指令到斑马打印机,并监控打印成功或者失败的状态信息

    Visual C# 入门 本文共分为两个部分: 第一部分:介绍如何与Zebar进行连接,把ZPL指令或者模板文件发送到斑马打印机进行打印. 第二部分:介绍如何接收Zebar进行打印之后如何得到斑马打印 ...

  8. 实现Android底层驱动开发并裁剪定制Android操作系统

    毕业论文 题   目实现Android底层驱动开发并裁剪定制Android操作系统 学   院电子信息与电气工程学院 姓   名牛xxx民 专   业电子信息科学与技术 学   号2012xxxxxx ...

  9. 【STM32F429】第6章 RL-TCPnet V7.X底层驱动说明

    最新教程下载:http://www.armbbs.cn/forum.php?mod=viewthread&tid=95243 第6章   RL-TCPnet底层驱动说明 本章节为大家讲解RL- ...

最新文章

  1. ntoskrnl损坏
  2. 如何防止单例模式被JAVA反射攻击
  3. 今日份凡尔赛培训满分了没?
  4. 一键洞悉员工级人效!智邦国际集团业财一体化管理系统:业财一心,基业长青
  5. 防盗链python_python一行代码,实现网页视频下载
  6. centos的ftp和smb被防火墙挡住了
  7. ffmpeg 中 swscale 的用法
  8. 管理感悟:遇到大事极少数人能处理好
  9. 三星note10 android q,【极光ROM】-【三星NOTE10/NOTE10+/5G N97XX-9825】-【V5.0 Android-Q-TE9】...
  10. ArcGIS 软件中路网数据的制作,手把手教学
  11. Windows 10 开启代理软件代理流量之后,系统的某些自带软件无法联网
  12. 【DP算法篇之初学】LIS\LCS\二维DP\带条件DP
  13. word 此文件来自其它计算机,问题解决: 此文件来自其他计算机,可能被阻止以帮助保护该计算机/WORD在试图打开文件时遇到错误……...
  14. 四年级计算机教学目的,四年级计算机教学计划
  15. 牛顿法及其下山法+C代码
  16. 使用Excel宏功能将考勤记录生成上班工时表
  17. 历届真题 杨辉三角形【第十二届】【省赛】【B组】
  18. 【开源免费】微软官方推荐的桌面小工具
  19. php长字符加密成短字符,大神们 怎么把一串长的字符串加密为短的能还原
  20. 历届试题 矩阵翻硬币

热门文章

  1. 美国服务器百度抓取耗时不稳定,百度或者其他搜索引擎抓取频次快慢的因素,还会受什么有影响?...
  2. mysql 5.1.17 redis_redis作为mysql的缓存服务器(读写分离)
  3. python中比较重要的几个函数_【python】python re模块中几个比较重要的函数
  4. html 换行符_每个非网站开发人员都应该了解的21个HTML基础知识
  5. python升级pip怎么出错了_Python,开启吐槽模式,新手必看!
  6. 寻仙新马源码一键端_强大,腾讯开源前后端接口开发工具!
  7. dofilter 无效_“鹅厂”商标注册成功,腾讯异议无效
  8. hadoop删除DataNode节点
  9. 又一个神器!只需一行代码,纯文本秒变Markdown
  10. 史上最怂黑客?新病毒从上线到“自首”只勒索到五块钱,最后主动提交了密钥...