如果探测例程返回成功并且系统选择连接那个驱动程序,则连接例程
负责将驱动程序实际连接到系统。如果探测例程返回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;
这就是连接例程的全部。