编写设备驱动程序

attach() 入口点(SCSI HBA 驱动程序)

在为设备配置和附加驱动程序实例时,SCSI HBA 驱动程序的 attach(9E) 入口点将执行多个任务。对于实际设备的典型驱动程序,必须处理以下操作系统和硬件问题:

软状态结构

分配按设备实例的软状态结构时,如果发生错误,驱动程序必须仔细清理。

DMA

HBA 驱动程序必须通过正确初始化 ddi_dma_attr_t 结构来描述其 DMA 引擎的属性。

static ddi_dma_attr_t isp_dma_attr = {
     DMA_ATTR_V0,        /* ddi_dma_attr version */
     0,                  /* low address */
     0xffffffff,         /* high address */
     0x00ffffff,         /* counter upper bound */
     1,                  /* alignment requirements */
     0x3f,               /* burst sizes */
     1,                  /* minimum DMA access */
     0xffffffff,         /* maximum DMA access */
     (1<<24)-1,          /* segment boundary restrictions */
     1,                  /* scatter-gather list length */
     512,                /* device granularity */
     0                   /* DMA flags */
};

如果该驱动程序提供 DMA,则还应检查其硬件是否已安装在支持 DMA 的槽中:

if (ddi_slaveonly(dip) == DDI_SUCCESS) {
    return (DDI_FAILURE);
}

传输结构

驱动程序应进一步分配和初始化此实例的传输结构。tran_hba_private 字段会设置为指向此实例的软状态结构。如果无需特殊的探测自定义,则可将 tran_tgt_probe 字段设置为 NULL 以实现缺省行为。

tran = scsi_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP);

isp->isp_tran                   = tran;
isp->isp_dip                    = dip;

tran->tran_hba_private          = isp;
tran->tran_tgt_private          = NULL;
tran->tran_tgt_init             = isp_tran_tgt_init;
tran->tran_tgt_probe            = scsi_hba_probe;
tran->tran_tgt_free             = (void (*)())NULL;

tran->tran_start                = isp_scsi_start;
tran->tran_abort                = isp_scsi_abort;
tran->tran_reset                = isp_scsi_reset;
tran->tran_getcap               = isp_scsi_getcap;
tran->tran_setcap               = isp_scsi_setcap;
tran->tran_init_pkt             = isp_scsi_init_pkt;
tran->tran_destroy_pkt          = isp_scsi_destroy_pkt;
tran->tran_dmafree              = isp_scsi_dmafree;
tran->tran_sync_pkt             = isp_scsi_sync_pkt;
tran->tran_reset_notify         = isp_scsi_reset_notify;
tran->tran_bus_quiesce          = isp_tran_bus_quiesce
tran->tran_bus_unquiesce        = isp_tran_bus_unquiesce
tran->tran_bus_reset            = isp_tran_bus_reset
tran->tran_interconnect_type    = isp_tran_interconnect_type

附加 HBA 驱动程序

驱动程序应附加此设备实例并执行错误清理(如有必要)。

i = scsi_hba_attach_setup(dip, &isp_dma_attr, tran, 0);
if (i != DDI_SUCCESS) {
    /* do error recovery */
    return (DDI_FAILURE);
}

寄存器映射

驱动程序应在其设备的寄存器中进行映射。驱动程序需要指定以下项:

ddi_device_acc_attr_t    dev_attributes;

     dev_attributes.devacc_attr_version = DDI_DEVICE_ATTR_V0;
     dev_attributes.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
     dev_attributes.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;

     if (ddi_regs_map_setup(dip, 0, (caddr_t *)&isp->isp_reg,
     0, sizeof (struct ispregs), &dev_attributes,
     &isp->isp_acc_handle) != DDI_SUCCESS) {
        /* do error recovery */
        return (DDI_FAILURE);
     }

添加中断处理程序

驱动程序必须首先获取 iblock cookie 才能初始化驱动程序处理程序中使用的所有互斥锁。仅当初始化这些互斥锁后才能添加中断处理程序。

i = ddi_get_iblock_cookie(dip, 0, &isp->iblock_cookie};
if (i != DDI_SUCCESS) {
    /* do error recovery */
    return (DDI_FAILURE);
}

mutex_init(&isp->mutex, "isp_mutex", MUTEX_DRIVER,
(void *)isp->iblock_cookie);
i = ddi_add_intr(dip, 0, &isp->iblock_cookie,
0, isp_intr, (caddr_t)isp);
if (i != DDI_SUCCESS) {
    /* do error recovery */
    return (DDI_FAILURE);
}

如果需要高级处理程序,则应对驱动程序进行编码以提供此类处理程序。否则,驱动程序必须能够停止附加操作。有关高级中断处理的说明,请参见处理高级别中断

创建可管理电源的组件

如果主机总线适配器只需要在所有目标适配器的电源级别为 0 时关闭电源,则使用电源管理,HBA 驱动程序只需提供 power(9E) 入口点。请参阅第 12 章。另外,HBA 驱动程序还需要创建 pm-components(9P) 属性,用于描述设备实现的组件。

由于这些组件将缺省为空闲,并且电源管理框架的缺省相关性处理会确保主机总线适配器在目标适配器每次通电时也随之通电,因此无需再执行任何操作。如果自动启用自动电源管理,则该处理还将在所有目标适配器都断电时关闭主机总线适配器电源。

报告附加状态

最后,驱动程序应报告已附加的此驱动程序实例并返回成功信息。

ddi_report_dev(dip);
    return (DDI_SUCCESS);