10.9. xxx_isa_attach

如果探测例程返回成功并且系统选择连接那个驱动程序,则连接例程 负责将驱动程序实际连接到系统。如果探测例程返回0 ,则连接例程期望 接收完整的设备结构softc,此结构由探测例程设置。同时,如果探测例程 返回0,它可能期望这个设备的连接例程应当在将来的某点被调用。如果 探测例程返回负值,则驱动程序可能不会作此假设。

如果成功完成,连接例程返回0,否则返回错误码。

连接例程的启动跟探测例程相似,将一些常用数据取到一些更容易 访问的变量中。

struct xxx_softc *sc = device_get_softc(dev); int unit = device_get_unit(dev); int error = 0;

然后分配并激活所需资源。由于端口范围通常在从探测返回前就 被释放,因此需要重新分配。我们希望探测例程已经适当地设置了 所有的资源范围,并将它们保存在结构softc中。如果探测例程留下了 一些被分配的资源,就不需要再次分配(重新分配被视为错误)。

sc->port0_rid = 0; sc->port0_r = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->port0_rid, /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE); if(sc->port0_r == NULL) return ENXIO; /* 板上内存 */ sc->mem0_rid = 0; sc->mem0_r = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mem0_rid, /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE); if(sc->mem0_r == NULL) goto bad; /* 取得虚地址 */ sc->mem0_v = rman_get_virtual(sc->mem0_r);

DMA请求通道(DRQ)以相似方式被分配。使用 isa_dma*()函数族进行初始化。例如:

isa_dmacascade(sc->drq0);

中断请求线(IRQ)有点特殊。除了分配以外,驱动程序的中断处理 函数也应当与它关联。在古老的ISA驱动程序中,由系统传递给中断处理 函数的参量是设备单元号。但在现代驱动程序中,按照约定,建议传递 指向结构softc的指针。一个很重要的原因在于当结构softc被动态分配后, 从softc取得单元号很容易,而从单元号取得softc很困难。同时,这个 约定也使得用于不同总线的应用程序看起来统一,并允许它们共享代码: 每个总线有其自己的探测,连接,分离和其他总线相关的例程,而它们 之间可以共享大块的驱动程序代码。

sc->intr_rid = 0; sc->intr_r = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->intr_rid, /*start*/ 0, /*end*/ ~0, /*count*/ 0, RF_ACTIVE); if(sc->intr_r == NULL) goto bad; /* * 假定对XXX_INTR_TYPE的定义依赖于驱动程序的类型, * 例如INTR_TYPE_CAM用于CAM的驱动程序 */ error = bus_setup_intr(dev, sc->intr_r, XXX_INTR_TYPE, (driver_intr_t *) xxx_intr, (void *) sc, &sc->intr_cookie); if(error) goto bad;

如果驱动程序需要与内存进行DMA,则这块内存应当按前述方式分配:

error=bus_dma_tag_create(NULL, /*alignment*/ 4, /*boundary*/ 0, /*lowaddr*/ BUS_SPACE_MAXADDR_24BIT, /*highaddr*/ BUS_SPACE_MAXADDR, /*filter*/ NULL, /*filterarg*/ NULL, /*maxsize*/ BUS_SPACE_MAXSIZE_24BIT, /*nsegments*/ BUS_SPACE_UNRESTRICTED, /*maxsegsz*/ BUS_SPACE_MAXSIZE_24BIT, /*flags*/ 0, &sc->parent_tag); if(error) goto bad; /* 很多东西是从父标签继承而来 * 假设sc->data指向存储共享数据的结构,例如一个环缓冲区可能是: * struct { * u_short rd_pos; * u_short wr_pos; * char bf[XXX_RING_BUFFER_SIZE] * } *data; */ error=bus_dma_tag_create(sc->parent_tag, 1, 0, BUS_SPACE_MAXADDR, 0, /*filter*/ NULL, /*filterarg*/ NULL, /*maxsize*/ sizeof(* sc->data), /*nsegments*/ 1, /*maxsegsz*/ sizeof(* sc->data), /*flags*/ 0, &sc->data_tag); if(error) goto bad; error = bus_dmamem_alloc(sc->data_tag, &sc->data, /* flags*/ 0, &sc->data_map); if(error) goto bad; /* 在&sc->data_p的情况下,xxx_alloc_callback()只是将物理地址 * 保存到作为其参量传递进去的指针中。 * 参看关于总线内存映射一节中的详细内容。 * 其实现可以像这样: * * static void * xxx_alloc_callback(void *arg, bus_dma_segment_t *seg, * int nseg, int error) * { * *(bus_addr_t *)arg = seg[0].ds_addr; * } */ bus_dmamap_load(sc->data_tag, sc->data_map, (void *)sc->data, sizeof (* sc->data), xxx_alloc_callback, (void *) &sc->data_p, /*flags*/0);

分配了所有的资源后,设备应当被初始化。初始化可能包括测试 所有特性,确保它们起作用。

if(xxx_initialize(sc) < 0) goto bad;

总线子系统将自动在控制台上打印由探测例程设置的设备描述。但 如果驱动程序想打印一些关于设备的额外信息,也是可能的,例如:

device_printf(dev, "has on-card FIFO buffer of %d bytes\n", sc->fifosize);

如果初始化例程遇到任何问题,建议返回错误之前打印有关信息。

连接例程的最后一步是将设备连接到内核中的功能子系统。完成 这个步骤的精确方式依赖于驱动程序的类型:字符设备、块设备、网络 设备、CAM SCSI总线设备等等。

如果所有均工作正常则返回成功。

error = xxx_attach_subsystem(sc); if(error) goto bad; return 0;

最后,处理棘手情况。返回错误前,所有资源应当被取消分配。 我们利用这样一个事实:结构softc传递给我们之前被零化,因此我们 能找出是否分配了某些资源:如果分配则它们的描述符非零。

bad: xxx_free_resources(sc); if(error) return error; else /* exact error is unknown */ return ENXIO;

这就是连接例程的全部。

本文档和其它文档可从这里下载: ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题,请先阅读 文档,如不能解决再联系 <questions@FreeBSD.org>.

关于本文档的问题请发信联系 <doc@FreeBSD.org>.