编写设备驱动程序

声明和数据结构

目标驱动程序必须包括头文件 <sys/scsi/scsi.h>

SCSI 目标驱动程序必须使用以下命令生成二进制模块:

ld -r xx xx.o -N"misc/scsi"

scsi_device 结构

调用 probe(9E)attach(9E) 例程之前,主机总线适配器驱动程序将为目标驱动程序分配并初始化 scsi_device(9S) 结构。此结构可存储有关每个 SCSI 逻辑单元的信息,包括指向信息区(包含通用信息和特定于设备的信息)的指针。对于连接到系统的每个逻辑单元,都存在一个 scsi_device(9S) 结构。目标驱动程序可以通过调用 ddi_get_driver_private(9F) 来检索指向此结构的指针。


注意 – 注意 –

由于主机总线适配器驱动程序使用目标设备的 dev_info 结构中的专用字段,因此目标驱动程序不能使用 ddi_set_driver_private(9F)


scsi_device(9S) 结构包含以下字段:

struct scsi_device {
    struct scsi_address           sd_address;    /* opaque address */
    dev_info_t                    *sd_dev;       /* device node */
    kmutex_t                      sd_mutex;
    void                          *sd_reserved;
    struct scsi_inquiry           *sd_inq;
    struct scsi_extended_sense    *sd_sense;
    caddr_t                       sd_private;
};

其中:

sd_address

为了进行 SCSI 资源分配而传递给例程的数据结构。

sd_dev

指向目标的 dev_info 结构的指针。

sd_mutex

供目标驱动程序使用的互斥锁。此互斥锁由主机总线适配器驱动程序初始化,并可被目标驱动程序用作每设备互斥锁。请勿在调用 scsi_transport(9F)scsi_poll(9F) 期间持有此互斥锁。有关互斥锁的更多信息,请参见第 3 章

sd_inq

目标设备的 SCSI 查询数据的指针。scsi_probe(9F) 例程将分配缓冲区,使用查询数据填充该缓冲区,并将该缓冲区连接到此字段。

sd_sense

指向用于包含设备中的 SCSI 请求检测数据的缓冲区的指针。目标驱动程序必须分配和管理此缓冲区。请参见attach() 入口点(SCSI 目标驱动程序)

sd_private

供目标驱动程序使用的指针字段。此字段通常用于存储指向专用目标驱动程序状态结构的指针。

scsi_pkt 结构(目标驱动程序)

scsi_pkt 结构包含以下字段:

struct scsi_pkt {
    opaque_t  pkt_ha_private;         /* private data for host adapter */
    struct scsi_address pkt_address;  /* destination packet is for */
    opaque_t  pkt_private;            /* private data for target driver */
    void     (*pkt_comp)(struct scsi_pkt *);  /* completion routine */
    uint_t   pkt_flags;               /* flags */
    int      pkt_time;                /* time allotted to complete command */
    uchar_t  *pkt_scbp;               /* pointer to status block */
    uchar_t  *pkt_cdbp;               /* pointer to command block */
    ssize_t  pkt_resid;               /* data bytes not transferred */
    uint_t   pkt_state;               /* state of command */
    uint_t   pkt_statistics;          /* statistics */
    uchar_t  pkt_reason;              /* reason completion called */
};

其中:

pkt_address

scsi_init_pkt(9F) 设置的目标设备的地址。

pkt_private

用于存储目标驱动程序的专用数据的位置。pkt_private 通常用于保存命令的 buf(9S) 指针。

pkt_comp

完成例程的地址。主机总线适配器驱动程序将在传输命令后调用此例程。传输命令并不表示命令已成功。目标可能处于繁忙状态。另一种可能性是在经过超时时间段之前目标未响应。请参见 pkt_time 字段的说明。目标驱动程序必须在该字段中提供有效值。如果不需要通知驱动程序,则该值可以为 NULL


注 –

有两种不同的 SCSI 回调例程。pkt_comp 字段标识完成回调例程,该例程将在主机总线适配器完成其处理时调用。此外,还提供了资源回调例程,该例程将在当前尚不可用资源可能可用时调用。请参见 scsi_init_pkt(9F) 手册页。


pkt_flags

提供其他控制信息,例如,在没有断开连接权限的情况下传输命令 (FLAG_NODISCON),或禁用回调 (FLAG_NOINTR)。有关详细信息,请参见 scsi_pkt(9S) 手册页。

pkt_time

超时值(以秒为单位)。如果命令在该时间内未完成,则主机总线适配器将调用完成例程,并将 pkt_reason 设置为 CMD_TIMEOUT。目标驱动程序应将该字段设置为大于命令可能需要的最长时间。如果超时值为零,则不请求超时。超时从在 SCSI 总线上传输命令时开始。

pkt_scbp

指向 SCSI 状态完成块的指针。该字段由主机总线适配器驱动程序填充。

pkt_cdbp

指向 SCSI 命令描述符块(要发送到目标设备的实际命令)的指针。主机总线适配器驱动程序不会解释该字段。目标驱动程序必须使用目标设备可以处理的命令填充该字段。

pkt_resid

剩余操作。pkt_resid 字段有两种不同的用途,具体取决于如何使用 pkt_resid。使用 pkt_resid 为命令 scsi_init_pkt(9F) 分配 DMA 资源时,pkt_resid 指示不可分配的字节数。由于 DMA 硬件具有分散/集中限制或其他设备限制,因此可能无法分配 DMA 资源。传输命令后,pkt_resid 指示不可传输的数据字节数。该字段由主机总线适配器驱动程序在调用完成例程之前填充。

pkt_state

指示命令的状态。主机总线适配器驱动程序在命令执行过程中填充该字段。该字段的每一位分别根据以下五种命令状态设置:

  • STATE_GOT_BUS-已获取总线

  • STATE_GOT_TARGET-已选定目标

  • STATE_SENT_CMD-已发送命令

  • STATE_XFERRED_DATA-已传输数据(如果适用)

  • STATE_GOT_STATUS-已从设备接收状态

pkt_statistics

包含与传输相关的统计信息(由主机总线适配器驱动程序设置)。

pkt_reason

提供调用完成例程的原因。完成例程会对该字段进行解码。然后,执行相应的操作。如果命令完成(即未发生传输错误),则该字段将设置为 CMD_CMPLT。如果该字段中存在其他值,则指示发生了错误。完成命令后,目标驱动程序应检查 pkt_scbp 字段以查看条件状态。有关更多信息,请参见 scsi_pkt(9S) 手册页。