scsi.h
scsi.h
是Linux内核中用于SCSI(Small Computer System Interface)子系统的头文件。SCSI是一种用于连接计算机与外部设备(如硬盘、光驱、磁带机等)的接口标准。该头文件包含了SCSI子系统的数据结构、宏定义、函数声明等内容,为实现SCSI设备驱动程序提供了必要的接口和工具。
主要功能和内容如下:
-
定义SCSI命令和相关常量:例如SCSI命令码、SCSI命令字节长度等。
-
定义SCSI命令相关数据结构:例如SCSI命令块数据结构
struct scsi_cmnd
,用于表示SCSI命令和命令执行的状态信息。 -
定义SCSI设备相关数据结构:例如SCSI设备结构
struct scsi_device
,用于表示SCSI总线上的具体设备。 -
定义SCSI主机相关数据结构:例如SCSI主机结构
struct Scsi_Host
,用于表示主机适配器控制器。 -
定义SCSI子系统函数接口:例如与SCSI设备、主机、命令等相关的函数声明,用于实现SCSI设备驱动程序。
这些功能都是为了支持Linux内核中对SCSI设备的管理和控制,允许内核通过SCSI子系统与外部SCSI设备进行通信和交互。
常量定义
struct scsi_cmnd;
enum scsi_timeouts {
SCSI_DEFAULT_EH_TIMEOUT = 10 * HZ,
};
/*
* DIX-capable adapters effectively support infinite chaining for the
* protection information scatterlist
*/
#define SCSI_MAX_PROT_SG_SEGMENTS 0xFFFF
/*
* Special value for scanning to specify scanning or rescanning of all
* possible channels, (target) ids, or luns on a given shost.
*/
#define SCAN_WILD_CARD ~0
/*
* standard mode-select header prepended to all mode-select commands
*/
-
struct scsi_cmnd;
这是一个前向声明(forward declaration),在这里表示struct scsi_cmnd
结构体的存在,但具体定义在这个头文件之外。在这个头文件中,可能只用到了该结构体的指针,所以不需要包含具体定义。 -
enum scsi_timeouts
这是一个枚举类型,定义了 SCSI 命令执行的超时时间。SCSI_DEFAULT_EH_TIMEOUT
定义了默认的 SCSI 命令执行超时时间,它被设置为 10 秒(10 * HZ,其中 HZ 表示每秒的时钟频率)。 -
#define SCSI_MAX_PROT_SG_SEGMENTS 0xFFFF
这是一个预处理指令,定义了 SCSI 最大的保护信息分散列表(scatterlist)段数。SCSI_MAX_PROT_SG_SEGMENTS
被设置为 0xFFFF,表示适配器支持无限的分散列表段数,这在 DIX(Data Integrity Extensions)功能支持的适配器上是成立的。 -
#define SCAN_WILD_CARD ~0
这也是一个预处理指令,定义了一个特殊的值SCAN_WILD_CARD
,用于在扫描(scanning)或重新扫描(rescanning)时指定对主机(shost)上所有可能的通道(channel)、目标 ID 或逻辑单元号(LUN)进行操作。~0
表示按位取反操作,即所有位都被设置为 1,所以SCAN_WILD_CARD
被设置为一个特殊值,表示通配符,用于扫描所有可能的通道、ID 或 LUN。
ccs_modesel_head
SCSI Mode-Select 命令的标准头部定义,用于在所有的 Mode-Select 命令前添加。让我们解释该结构体的成员:
struct ccs_modesel_head {
__u8 _r1; /* 保留字段 */
__u8 medium; /* 设备特定的介质类型 */
__u8 _r2; /* 保留字段 */
__u8 block_desc_length; /* 块描述符长度 */
__u8 density; /* 设备特定的密度代码 */
__u8 number_blocks_hi; /* 块描述符中的块数量高位 */
__u8 number_blocks_med; /* 块描述符中的块数量中位 */
__u8 number_blocks_lo; /* 块描述符中的块数量低位 */
__u8 _r3; /* 保留字段 */
__u8 block_length_hi; /* 块描述符中的块长度高位 */
__u8 block_length_med; /* 块描述符中的块长度中位 */
__u8 block_length_lo; /* 块描述符中的块长度低位 */
};
这个结构体定义了 SCSI Mode-Select 命令的标准头部,用于在发送 Mode-Select 命令时,指定设备特定的参数。每个成员的含义如下:
_r1
,_r2
,_r3
: 保留字段,用于填充以对齐结构体。medium
: 设备特定的介质类型,用于指定存储介质的类型。block_desc_length
: 块描述符的长度,指定了块描述符字段的字节数。density
: 设备特定的密度代码,用于指定存储介质的密度。number_blocks_hi
,number_blocks_med
,number_blocks_lo
: 块描述符中的块数量,表示要写入或读取的数据块的数量。这三个字段合并成一个 24 位的值。block_length_hi
,block_length_med
,block_length_lo
: 块描述符中的块长度,表示每个数据块的大小。这三个字段合并成一个 24 位的值。
这个结构体定义了 SCSI Mode-Select 命令中的头部,用于配置设备的特定参数,以满足应用的需求。在发送 Mode-Select 命令时,可以根据需要设置这些字段来控制设备的行为。
scsi_is_wlun
/*
* The Well Known LUNS (SAM-3) in our int representation of a LUN
*/
#define SCSI_W_LUN_BASE 0xc100
#define SCSI_W_LUN_REPORT_LUNS (SCSI_W_LUN_BASE + 1)
#define SCSI_W_LUN_ACCESS_CONTROL (SCSI_W_LUN_BASE + 2)
#define SCSI_W_LUN_TARGET_LOG_PAGE (SCSI_W_LUN_BASE + 3)
static inline int scsi_is_wlun(u64 lun)
{
return (lun & 0xff00) == SCSI_W_LUN_BASE;
}
这段代码定义了 Well Known LUNs(SAM-3)的表示形式以及相关的宏和函数。Well Known LUNs 是一种特殊的逻辑单元号 (LUN),用于标识一组预定义的功能或配置。
SCSI_W_LUN_BASE
: 定义了 Well Known LUNs 的基地址,它是 0xc100。SCSI_W_LUN_REPORT_LUNS
: 定义了 Report LUNs 功能的 Well Known LUN 的值,为基地址加上 1。SCSI_W_LUN_ACCESS_CONTROL
: 定义了 Access Control 功能的 Well Known LUN 的值,为基地址加上 2。SCSI_W_LUN_TARGET_LOG_PAGE
: 定义了 Target Logical Page 功能的 Well Known LUN 的值,为基地址加上 3。
接下来是一个内联函数 scsi_is_wlun
:
static inline int scsi_is_wlun(u64 lun)
{
return (lun & 0xff00) == SCSI_W_LUN_BASE;
}
这个函数用于检查给定的逻辑单元号 (LUN) 是否属于 Well Known LUNs。它通过将 LUN 与 SCSI_W_LUN_BASE
进行按位与运算,并检查结果是否与 SCSI_W_LUN_BASE
相等来判断是否为 Well Known LUN。如果是 Well Known LUN,则返回非零值,否则返回 0。
scsi_status_is_check_condition
/**
* scsi_status_is_check_condition - check the status return.
*
* @status: the status passed up from the driver (including host and
* driver components)
*
* This returns true if the status code is SAM_STAT_CHECK_CONDITION.
*/
static inline int scsi_status_is_check_condition(int status)
{
if (status < 0)
return false;
status &= 0xfe;
return status == SAM_STAT_CHECK_CONDITION;
}
这是一个内联函数 scsi_status_is_check_condition
,用于检查 SCSI 命令执行的状态是否为 “CHECK CONDITION”(检查条件)。
函数的输入参数 status
是传递自驱动程序的状态代码,其中包含了主机和驱动程序的组成部分。
函数返回一个布尔值,如果状态代码是 SAM_STAT_CHECK_CONDITION
,则返回 true
(非零值),否则返回 false
(0)。
函数的实现如下:
-
首先,通过条件判断
status < 0
,检查状态是否小于零。如果是,说明出现了错误,直接返回false
,表示不是 “CHECK CONDITION”。 -
然后,将状态码
status
与0xfe
进行按位与运算,将最低有效位清零。这是为了忽略除了第 0 位(低位)之外的所有位,因为 “CHECK CONDITION” 的状态码最低位是 0。 -
最后,将处理过的状态码与
SAM_STAT_CHECK_CONDITION
进行比较,如果相等,则返回true
,表示是 “CHECK CONDITION”;否则返回false
,表示不是 “CHECK CONDITION”。
Extended message codes& scsi_disposition
/*
* Extended message codes.
*/
#define EXTENDED_MODIFY_DATA_POINTER 0x00
#define EXTENDED_SDTR 0x01
#define EXTENDED_EXTENDED_IDENTIFY 0x02 /* SCSI-I only */
#define EXTENDED_WDTR 0x03
#define EXTENDED_PPR 0x04
#define EXTENDED_MODIFY_BIDI_DATA_PTR 0x05
/*
* Internal return values.
*/
enum scsi_disposition {
NEEDS_RETRY = 0x2001,
SUCCESS = 0x2002,
FAILED = 0x2003,
QUEUED = 0x2004,
SOFT_ERROR = 0x2005,
ADD_TO_MLQUEUE = 0x2006,
TIMEOUT_ERROR = 0x2007,
SCSI_RETURN_NOT_HANDLED = 0x2008,
FAST_IO_FAIL = 0x2009,
};
这是一些定义在 scsi.h
头文件中的常量和枚举类型。
-
Extended message codes: 这些是用于 SCSI 命令中的扩展消息代码,用于定义一些扩展的功能或参数设置。
EXTENDED_MODIFY_DATA_POINTER
: 扩展消息码,用于修改数据指针。EXTENDED_SDTR
: 扩展消息码,用于请求同步数据传输。EXTENDED_EXTENDED_IDENTIFY
: 扩展消息码,仅在 SCSI-I 中使用,用于扩展识别功能。EXTENDED_WDTR
: 扩展消息码,用于请求宽数据传输。EXTENDED_PPR
: 扩展消息码,用于请求 PPR (Protocol Parameters Request)。EXTENDED_MODIFY_BIDI_DATA_PTR
: 扩展消息码,用于修改双向数据指针。
-
Internal return values: 这是一些用于 SCSI 命令处理过程中的内部返回值的枚举类型。
NEEDS_RETRY
: 表示需要重试该 SCSI 命令。SUCCESS
: 表示 SCSI 命令成功执行。FAILED
: 表示 SCSI 命令执行失败。QUEUED
: 表示 SCSI 命令已经被放入队列等待执行。SOFT_ERROR
: 表示 SCSI 命令执行过程中遇到软错误。ADD_TO_MLQUEUE
: 表示将 SCSI 命令添加到多级队列(MLQUEUE)中。TIMEOUT_ERROR
: 表示 SCSI 命令执行超时。SCSI_RETURN_NOT_HANDLED
: 表示 SCSI 命令未被处理。FAST_IO_FAIL
: 表示快速 I/O 失败。
常量定义2
/*
* Midlevel queue return values.
*/
#define SCSI_MLQUEUE_HOST_BUSY 0x1055
#define SCSI_MLQUEUE_DEVICE_BUSY 0x1056
#define SCSI_MLQUEUE_EH_RETRY 0x1057
#define SCSI_MLQUEUE_TARGET_BUSY 0x1058
/*
* Use these to separate status msg and our bytes
*
* These are set by:
*
* status byte = set from target device
* msg_byte (unused)
* host_byte = set by low-level driver to indicate status.
*/
#define status_byte(result) (result & 0xff)
#define host_byte(result) (((result) >> 16) & 0xff)
#define sense_class(sense) (((sense) >> 4) & 0x7)
#define sense_error(sense) ((sense) & 0xf)
#define sense_valid(sense) ((sense) & 0x80)
/*
* default timeouts
*/
#define FORMAT_UNIT_TIMEOUT (2 * 60 * 60 * HZ)
#define START_STOP_TIMEOUT (60 * HZ)
#define MOVE_MEDIUM_TIMEOUT (5 * 60 * HZ)
#define READ_ELEMENT_STATUS_TIMEOUT (5 * 60 * HZ)
#define READ_DEFECT_DATA_TIMEOUT (60 * HZ )
#define IDENTIFY_BASE 0x80
#define IDENTIFY(can_disconnect, lun) (IDENTIFY_BASE |\
((can_disconnect) ? 0x40 : 0) |\
((lun) & 0x07))
/*
* struct scsi_device::scsi_level values. For SCSI devices other than those
* prior to SCSI-2 (i.e. over 12 years old) this value is (resp[2] + 1)
* where "resp" is a byte array of the response to an INQUIRY. The scsi_level
* variable is visible to the user via sysfs.
*/
#define SCSI_UNKNOWN 0
#define SCSI_1 1
#define SCSI_1_CCS 2
#define SCSI_2 3
#define SCSI_3 4 /* SPC */
#define SCSI_SPC_2 5
#define SCSI_SPC_3 6
/*
* INQ PERIPHERAL QUALIFIERS
*/
#define SCSI_INQ_PQ_CON 0x00
#define SCSI_INQ_PQ_NOT_CON 0x01
#define SCSI_INQ_PQ_NOT_CAP 0x03
/*
* Here are some scsi specific ioctl commands which are sometimes useful.
*
* Note that include/linux/cdrom.h also defines IOCTL 0x5300 - 0x5395
*/
/* Used to obtain PUN and LUN info. Conflicts with CDROMAUDIOBUFSIZ */
#define SCSI_IOCTL_GET_IDLUN 0x5382
/* 0x5383 and 0x5384 were used for SCSI_IOCTL_TAGGED_{ENABLE,DISABLE} */
/* Used to obtain the host number of a device. */
#define SCSI_IOCTL_PROBE_HOST 0x5385
/* Used to obtain the bus number for a device */
#define SCSI_IOCTL_GET_BUS_NUMBER 0x5386
/* Used to obtain the PCI location of a device */
#define SCSI_IOCTL_GET_PCI 0x5387
这些是在 scsi.h
头文件中定义的一些常量和宏。
-
Midlevel queue return values: 这些是用于中级队列(midlevel queue)处理的返回值常量。
SCSI_MLQUEUE_HOST_BUSY
: 表示主机忙,需要重试。SCSI_MLQUEUE_DEVICE_BUSY
: 表示设备忙,需要重试。SCSI_MLQUEUE_EH_RETRY
: 表示中级队列出错,需要重试。SCSI_MLQUEUE_TARGET_BUSY
: 表示目标设备忙,需要重试。
-
用于解析 SCSI 命令结果的宏:
status_byte(result)
: 用于从结果中提取状态字节。host_byte(result)
: 用于从结果中提取主机字节。sense_class(sense)
: 用于从 sense 字节中提取 sense 类别。sense_error(sense)
: 用于从 sense 字节中提取 sense 错误码。sense_valid(sense)
: 用于检查 sense 字节是否有效。
-
默认超时时间常量:
FORMAT_UNIT_TIMEOUT
: 格式化单元超时时间。START_STOP_TIMEOUT
: 启动或停止设备超时时间。MOVE_MEDIUM_TIMEOUT
: 移动介质超时时间。READ_ELEMENT_STATUS_TIMEOUT
: 读取元素状态超时时间。READ_DEFECT_DATA_TIMEOUT
: 读取缺陷数据超时时间。
-
IDENTIFY 宏:用于生成一个 SCSI 设备的标识符。
-
struct scsi_device::scsi_level 值:用于表示 SCSI 设备的 SCSI 等级。
-
INQ PERIPHERAL QUALIFIERS:用于表示 INQUIRY 命令的外围设备合格性。
-
SCSI 相关的 IOCTL 命令:
SCSI_IOCTL_GET_IDLUN
: 用于获取 PUN(Peripheral Device Number)和 LUN(Logical Unit Number)信息。SCSI_IOCTL_PROBE_HOST
: 用于获取设备所在的主机号。SCSI_IOCTL_GET_BUS_NUMBER
: 用于获取设备所在的总线号。SCSI_IOCTL_GET_PCI
: 用于获取设备的 PCI 位置信息。
scsi_status_is_good
/** scsi_status_is_good - check the status return.
*
* @status: the status passed up from the driver (including host and
* driver components)
*
* This returns true for known good conditions that may be treated as
* command completed normally
*/
static inline bool scsi_status_is_good(int status)
{
if (status < 0)
return false;
if (host_byte(status) == DID_NO_CONNECT)
return false;
/*
* FIXME: bit0 is listed as reserved in SCSI-2, but is
* significant in SCSI-3. For now, we follow the SCSI-2
* behaviour and ignore reserved bits.
*/
status &= 0xfe;
return ((status == SAM_STAT_GOOD) ||
(status == SAM_STAT_CONDITION_MET) ||
/* Next two "intermediate" statuses are obsolete in SAM-4 */
(status == SAM_STAT_INTERMEDIATE) ||
(status == SAM_STAT_INTERMEDIATE_CONDITION_MET) ||
/* FIXME: this is obsolete in SAM-3 */
(status == SAM_STAT_COMMAND_TERMINATED));
}
scsi_status_is_good
是一个内联函数,用于检查从驱动程序返回的状态值(包括主机和驱动程序组件)是否表示命令已成功完成。
函数原型:
static inline bool scsi_status_is_good(int status);
参数 status
是从驱动程序返回的状态码。
函数返回一个布尔值,如果状态码表示命令已成功完成,则返回 true
,否则返回 false
。
该函数会根据状态码的值判断是否表示成功完成,具体判断逻辑如下:
-
如果状态码小于 0,则表示出现错误,返回
false
。 -
如果主机字节(host_byte)为
DID_NO_CONNECT
,表示设备连接失败,返回false
。 -
掩码操作将状态码的低 7 位保留,将其他位设置为 0。这是因为在 SCSI-2 中,bit 0 被列为保留位,但在 SCSI-3 中有意义。暂时遵循 SCSI-2 行为并忽略保留位。
-
最后,判断状态码是否等于以下常量中的任何一个,如果是则返回
true
:SAM_STAT_GOOD
: 命令成功完成。SAM_STAT_CONDITION_MET
: 命令成功完成并满足条件。SAM_STAT_INTERMEDIATE
: 中间状态,已过时(在 SAM-4 中不再使用)。SAM_STAT_INTERMEDIATE_CONDITION_MET
: 中间状态,已过时(在 SAM-4 中不再使用)。SAM_STAT_COMMAND_TERMINATED
: 命令被终止,已过时(在 SAM-3 中不再使用)。
如果以上条件均不满足,则返回 false
,表示命令未成功完成。