FreeBSD 系统结构手册
上一页 | 第10章 　ISA设备驱动程序 | 下一页

# 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;

这就是连接例程的全部。

上一页 | 起始页 | 下一页
xxx_isa_probe | 上一级 | xxx_isa_detach

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

如果对于FreeBSD有问题，请先阅读文档，如不能解决再联系<questions@FreeBSD.org>.
关于本文档的问题请发信联系 <doc@FreeBSD.org>.