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>.