编写设备驱动程序

attach() 入口点

内核调用驱动程序的 attach(9E) 入口点来连接设备实例或针对已由电源管理框架暂停或关闭的设备实例恢复操作。本节仅讨论连接设备实例的操作。电源管理将在第 12 章中讨论。

调用驱动程序的 attach(9E) 入口点以连接每个绑定到驱动程序的设备实例。基于要连接的设备节点实例,并将 attach(9E)cmd 参数指定为 DDI_ATTACH,来调用此入口点。attach 入口点主要包括以下类型的处理:

驱动程序软状态管理

为了协助设备驱动程序编写人员分配状态结构,Solaris DDI/DKI 提供了一组内存管理例程,称为软件状态管理例程,也称为软状态例程。这些例程可动态分配、检索以及销毁指定大小的内存项,并可隐藏列表管理的详细信息。实例编号标识所需的内存项。此编号通常为系统指定的实例编号。

通常,驱动程序会为与其连接的每个设备实例分配软状态结构,方法是调用 ddi_soft_state_zalloc(9F) 并传递设备的实例编号。由于两个设备节点不能具有相同的实例编号,所以对于已经分配出去的给定实例编号,ddi_soft_state_zalloc(9F) 将失败。

驱动程序的字符入口点或块入口点(cb_ops(9S))通过先解码来自传递到入口点函数的 dev_t 参数的设备实例编号,来引用特定的软状态结构。随后,驱动程序调用 ddi_get_soft_state(9F),传递每个驱动程序的软状态列表和生成的实例编号。返回值 NULL 表明实际上不存在该设备并且应由驱动程序返回相应的代码。

有关实例编号和设备编号(dev_t 编号)之间关系的更多信息,请参见创建从设备节点

锁变量和条件变量的初始化

驱动程序在连接期间应初始化所有基于实例的锁和条件变量。添加任何中断处理程序之前,必须先初始化驱动程序中断处理程序所获取的所有锁。有关锁的初始化和使用的说明,请参见第 3 章。有关中断处理程序和锁问题的讨论,请参见第 8 章

创建从设备节点

连接过程的一个重要部分是为设备实例创建次要节点。次要节点包含由设备和 DDI 框架导出的信息。系统使用此信息为 /devices 下的次要节点创建特殊文件

驱动程序调用 ddi_create_minor_node(9F) 时会创建次要节点。驱动程序提供次要设备号次要名称次要节点类型,以及次要节点是代表块设备还是字符设备。

驱动程序可以为设备创建任意数量的次要节点。Solaris DDI/DKI 期望某些类别的设备具有以特定格式创建的次要节点。例如,期望磁盘驱动程序为连接的每个物理磁盘实例创建 16 个次要节点。将创建八个代表块设备接口 a - h 的次要节点,另外八个次要节点代表字符设备接口 a,raw - h,raw

传递给 ddi_create_minor_node(9F)次要设备号全部由驱动程序定义。次要设备号通常是设备实例编号和次要节点标识符的编码。在前面的示例中,驱动程序会为每个次要节点创建次要设备号,方法是将设备的实例编号左移三位,再将该结果与次要节点索引进行“或”运算。次要节点索引值的范围介于 0 和 7 之间。请注意,次要节点 aa,raw 共用同一次要设备号。这些次要节点根据传递到 ddi_create_minor_node()spec_type 参数来区分。

传递给 ddi_create_minor_node(9F)次要节点类型对设备类型进行分类,如磁盘、磁带、网络接口、帧缓存器等。

下表列出了可以创建的可能的节点类型。

表 6–1 可能节点类型

常量 

说明 

DDI_NT_SERIAL

串行端口 

DDI_NT_SERIAL_DO

拨出端口 

DDI_NT_BLOCK

硬盘 

DDI_NT_BLOCK_CHAN

带有通道或目标编号的硬盘 

DDI_NT_CD

ROM 驱动器 (CD-ROM) 

DDI_NT_CD_CHAN

带有通道或目标编号的 ROM 驱动器 

DDI_NT_FD

软盘 

DDI_NT_TAPE

磁带机 

DDI_NT_NET

网络设备 

DDI_NT_DISPLAY

显示设备 

DDI_NT_MOUSE

鼠标 

DDI_NT_KEYBOARD

键盘 

DDI_NT_AUDIO

音频设备 

DDI_PSEUDO

通用的伪设备 

节点类型 DDI_NT_BLOCKDDI_NT_BLOCK_CHANDDI_NT_CDDDI_NT_CD_CHAN 会使 devfsadm(1M) 将设备实例标识为磁盘,并在 /dev/dsk/dev/rdsk 目录中创建名称。

节点类型 DDI_NT_TAPE 会使 devfsadm(1M) 将设备实例标识为磁带,并在 /dev/rmt 目录中创建名称。

节点类型 DDI_NT_SERIALDDI_NT_SERIAL_DO 会使 devfsadm(1M) 执行以下操作:

供应商提供的字符串应包括使字符串唯一的标识值,如名称或股票名称。该字符串可与 devfsadm(1M)devlinks.tab 文件(请参见 devlinks(1M) 手册页)一起使用以在 /dev 中创建逻辑名称。

延迟连接

在相应实例上的 attach(9E) 成功之前,可能会对次要设备调用 open(9E)。然后 open() 必须返回 ENXIO,这将导致系统尝试连接该设备。如果 attach() 成功,则会自动重试 open()


示例 6–5 典型 attach() 入口点

/*
 * Attach an instance of the driver.  We take all the knowledge we
 * have about our board and check it against what has been filled in
 * for us from our FCode or from our driver.conf(4) file.
 */
static int
xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
    int instance;
    Pio *pio_p;
    ddi_device_acc_attr_t   da_attr;
    static int pio_validate_device(dev_info_t *);

    switch (cmd) {
    case DDI_ATTACH:

    /*
     * first validate the device conforms to a configuration this driver
     * supports
     */
    if (pio_validate_device(dip) == 0)
        return (DDI_FAILURE);

    /*
     * Allocate a soft state structure for this device instance
     * Store a pointer to the device node in our soft state structure
     * and a reference to the soft state structure in the device
     * node.
     */
    instance = ddi_get_instance(dip);
    if (ddi_soft_state_zalloc(pio_softstate, instance) != 0)
        return (DDI_FAILURE);
    pio_p = ddi_get_soft_state(pio_softstate, instance);
    ddi_set_driver_private(dip, (caddr_t)pio_p);
    pio_p->dip = dip;

    /*
     * Before adding the interrupt, get the interrupt block
     * cookie associated with the interrupt specification to
     * initialize the mutex used by the interrupt handler.
     */
    if (ddi_get_iblock_cookie(dip, 0, &pio_p->iblock_cookie) !=
      DDI_SUCCESS) {
        ddi_soft_state_free(pio_softstate, instance);
        return (DDI_FAILURE);
    }

    mutex_init(&pio_p->mutex, NULL, MUTEX_DRIVER, pio_p->iblock_cookie);

    /*
     * Now that the mutex is initialized, add the interrupt itself.
     */
    if (ddi_add_intr(dip, 0, NULL, NULL, pio_intr, (caddr_t)instance) !=
      DDI_SUCCESS) {
        mutex_destroy(&pio_p>mutex);
        ddi_soft_state_free(pio_softstate, instance);
        return (DDI_FAILURE);
    }

    /*
     * Initialize the device access attributes for the register mapping
     */
    dev_acc_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
    dev_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
    dev_acc_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;

    /*
     * Map in the csr register (register 0)
     */
    if (ddi_regs_map_setup(dip, 0, (caddr_t *)&(pio_p->csr), 0,
        sizeof (Pio_csr), &dev_acc_attr, &pio_p->csr_handle) !=
        DDI_SUCCESS) {
        ddi_remove_intr(pio_p->dip, 0, pio_p->iblock_cookie);
        mutex_destroy(&pio_p->mutex);
        ddi_soft_state_free(pio_softstate, instance);
        return (DDI_FAILURE);
    }

    /*
     * Map in the data register (register 1)
     */
    if (ddi_regs_map_setup(dip, 1, (caddr_t *)&(pio_p->data), 0,
        sizeof (uchar_t), &dev_acc_attr, &pio_p->data_handle) !=
        DDI_SUCCESS) {
        ddi_remove_intr(pio_p->dip, 0, pio_p->iblock_cookie);
        ddi_regs_map_free(&pio_p->csr_handle);
        mutex_destroy(&pio_p->mutex);
        ddi_soft_state_free(pio_softstate, instance);
        return (DDI_FAILURE);
    }

    /*
     * Create an entry in /devices for user processes to open(2)
     * This driver will create a minor node entry in /devices
     * of the form:  /devices/..../pio@X,Y:pio
     */
    if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR,
        instance, DDI_PSEUDO, 0) == DDI_FAILURE) {
        ddi_remove_intr(pio_p->dip, 0, pio_p->iblock_cookie);
        ddi_regs_map_free(&pio_p->csr_handle);
        ddi_regs_map_free(&pio_p->data_handle);
        mutex_destroy(&pio_p->mutex);
        ddi_soft_state_free(pio_softstate, instance);
        return (DDI_FAILURE);
    }

    /*
     * reset device (including disabling interrupts)
     */
    ddi_put8(pio_p->csr_handle, pio_p->csr, PIO_RESET);

    /*
     * report the name of the device instance which has attached
     */
    ddi_report_dev(dip);
    return (DDI_SUCCESS);

    case DDI_RESUME:
    return (DDI_SUCCESS);

    default:
    return (DDI_FAILURE);
    }
}


注 –

attach() 例程不能对不同设备实例上的调用顺序做出任何假设。系统可以并行调用不同设备实例上的 attach()。系统还可以在不同设备实例上同时调用 attach()detach()