FreeBSD 系统结构手册
上一页 |  | 下一页

# 第11章 　PCI设备

翻译：spellar @SMTH.

本章将讨论FreeBSD为了给PCI总线上的设备编写驱动程序而提供的机制。

# 11.1 探测与连接

这儿的信息是关于PCI总线代码如何迭代通过未连接的设备，并查看新 加载的kld是否会连接其中一个。

## 11.1.1 示例驱动程序源代码(`mypci.c`)

    /*
     * 与PCI函数进行交互的简单KLD
     *
     * Murray Stokely
     */

    #include <sys/param.h>        /* kernel.h中使用的定义 */
    #include <sys/module.h>
    #include <sys/systm.h>
    #include <sys/errno.h>
    #include <sys/kernel.h>       /* 模块初始化中使用的类型 */
    #include <sys/conf.h>     /* cdevsw结构 */
    #include <sys/uio.h>      /* uio结构 */
    #include <sys/malloc.h>
    #include <sys/bus.h>      /* pci总线用到的结构、原型 */

    #include <machine/bus.h>
    #include <sys/rman.h>
    #include <machine/resource.h>

    #include <dev/pci/pcivar.h>   /* 为了使用get_pci宏! */
    #include <dev/pci/pcireg.h>

    /* softc保存我们每个实例的数据。 */
    struct mypci_softc {
        device_t    my_dev;
        struct cdev *my_cdev;
    };

    /* 函数原型 */
    static d_open_t     mypci_open;
    static d_close_t    mypci_close;
    static d_read_t     mypci_read;
    static d_write_t    mypci_write;

    /* 字符设备入口点 */

    static struct cdevsw mypci_cdevsw = {
        .d_version =    D_VERSION,
        .d_open =   mypci_open,
        .d_close =  mypci_close,
        .d_read =   mypci_read,
        .d_write =  mypci_write,
        .d_name =   "mypci",
    };

    /*
     * 在cdevsw例程中，我们通过结构体cdev中的成员si_drv1找出我们的softc。
     * 当我们建立/dev项时，在我们的已附着的例程中，
     * 我们设置这个变量指向我们的softc。
     */

    int
    mypci_open(struct cdev *dev, int oflags, int devtype, d_thread_t *td)
    {
        struct mypci_softc *sc;

        /* Look up our softc. */
        sc = dev->si_drv1;
        device_printf(sc->my_dev, "Opened successfully.\n");
        return (0);
    }

    int
    mypci_close(struct cdev *dev, int fflag, int devtype, d_thread_t *td)
    {
        struct mypci_softc *sc;

        /* Look up our softc. */
        sc = dev->si_drv1;
        device_printf(sc->my_dev, "Closed.\n");
        return (0);
    }

    int
    mypci_read(struct cdev *dev, struct uio *uio, int ioflag)
    {
        struct mypci_softc *sc;

        /* Look up our softc. */
        sc = dev->si_drv1;
        device_printf(sc->my_dev, "Asked to read %d bytes.\n", uio->uio_resid);
        return (0);
    }

    int
    mypci_write(struct cdev *dev, struct uio *uio, int ioflag)
    {
        struct mypci_softc *sc;

        /* Look up our softc. */
        sc = dev->si_drv1;
        device_printf(sc->my_dev, "Asked to write %d bytes.\n", uio->uio_resid);
        return (0);
    }

    /* PCI支持函数 */

    /*
     * 将某个设置的标识与这个驱动程序支持的标识相比较。
     * 如果相符，设置描述字符并返回成功。
     */
    static int
    mypci_probe(device_t dev)
    {

        device_printf(dev, "MyPCI Probe\nVendor ID : 0x%x\nDevice ID : 0x%x\n",
            pci_get_vendor(dev), pci_get_device(dev));

        if (pci_get_vendor(dev) == 0x11c1) {
            printf("We've got the Winmodem, probe successful!\n");
            device_set_desc(dev, "WinModem");
            return (BUS_PROBE_DEFAULT);
        }
        return (ENXIO);
    }

    /* 只有当探测成功时才调用连接函数 */

    static int
    mypci_attach(device_t dev)
    {
        struct mypci_softc *sc;

        printf("MyPCI Attach for : deviceID : 0x%x\n", pci_get_devid(dev));

        /* Look up our softc and initialize its fields. */
        sc = device_get_softc(dev);
        sc->my_dev = dev;

        /*
         * Create a /dev entry for this device.  The kernel will assign us
         * a major number automatically.  We use the unit number of this
         * device as the minor number and name the character device
         * "mypci<unit>".
         */
        sc->my_cdev = make_dev(&mypci_cdevsw, device_get_unit(dev),
            UID_ROOT, GID_WHEEL, 0600, "mypci%u", device_get_unit(dev));
        sc->my_cdev->si_drv1 = sc;
        printf("Mypci device loaded.\n");
        return (0);
    }

    /* 分离设备。 */

    static int
    mypci_detach(device_t dev)
    {
        struct mypci_softc *sc;

        /* Teardown the state in our softc created in our attach routine. */
        sc = device_get_softc(dev);
        destroy_dev(sc->my_cdev);
        printf("Mypci detach!\n");
        return (0);
    }

    /* 系统关闭期间在sync之后调用。 */

    static int
    mypci_shutdown(device_t dev)
    {

        printf("Mypci shutdown!\n");
        return (0);
    }

    /*
     * 设备挂起例程。
     */
    static int
    mypci_suspend(device_t dev)
    {

        printf("Mypci suspend!\n");
        return (0);
    }

    /*
     * 设备恢复（重新开始）例程。
     */
    static int
    mypci_resume(device_t dev)
    {

        printf("Mypci resume!\n");
        return (0);
    }

    static device_method_t mypci_methods[] = {
        /* 设备接口 */
        DEVMETHOD(device_probe,     mypci_probe),
        DEVMETHOD(device_attach,    mypci_attach),
        DEVMETHOD(device_detach,    mypci_detach),
        DEVMETHOD(device_shutdown,  mypci_shutdown),
        DEVMETHOD(device_suspend,   mypci_suspend),
        DEVMETHOD(device_resume,    mypci_resume),

        { 0, 0 }
    };

    static devclass_t mypci_devclass;

    DEFINE_CLASS_0(mypci, mypci_driver, mypci_methods, sizeof(struct mypci_softc));
    DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);

## 11.1.2 示例驱动程序的`Makefile`

    # 驱动程序mypci的Makefile

    KMOD=   mypci
    SRCS=   mypci.c
    SRCS+=  device_if.h bus_if.h pci_if.h

    .include <bsd.kmod.mk>

如果你将上面的源文件和 `Makefile`放入一个目录，你可以运行 `make`编译示例驱动程序。 还有，你可以运行`make load` 将驱动程序装载到当前正在运行的内核中，而`make unload`可在装载后卸载驱动程序。

## 11.1.3 更多资源

- PCI Special Interest Group

- PCI System Architecture, Fourth Edition by Tom Shanley, et al.

上一页 | 起始页 | 下一页
xxx_intr | 上一级 | 总线资源

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

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