第 11 章 PCI 设备
11.1. 探测与附加
11.1.1. 示例驱动程序源代码 (mypci.c)
/*
* 简单的 KLD,用于测试 PCI 函数。
*
* 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 总线相关的结构、原型和 DEVMETHOD 宏 */
#include <machine/bus.h>
#include <sys/rman.h>
#include <machine/resource.h>
#include <dev/pci/pcivar.h> /* 用于 pci_get 宏! */
#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 例程中,我们通过使用 struct cdev 的 si_drv1 成员来查找我们的 softc。
* 我们在附加例程中创建 /dev 条目时将此变量设置为指向我们的 softc。
*/
int
mypci_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
{
struct mypci_softc *sc;
/* 查找我们的 softc。 */
sc = dev->si_drv1;
device_printf(sc->my_dev, "打开成功。\n");
return (0);
}
int
mypci_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
{
struct mypci_softc *sc;
/* 查找我们的 softc。 */
sc = dev->si_drv1;
device_printf(sc->my_dev, "已关闭。\n");
return (0);
}
int
mypci_read(struct cdev *dev, struct uio *uio, int ioflag)
{
struct mypci_softc *sc;
/* 查找我们的 softc。 */
sc = dev->si_drv1;
device_printf(sc->my_dev, "请求读取 %zd 字节。\n", uio->uio_resid);
return (0);
}
int
mypci_write(struct cdev *dev, struct uio *uio, int ioflag)
{
struct mypci_softc *sc;
/* 查找我们的 softc。 */
sc = dev->si_drv1;
device_printf(sc->my_dev, "请求写入 %zd 字节。\n", uio->uio_resid);
return (0);
}
/* PCI 支持函数 */
/*
* 将此设备的设备 ID 与此驱动程序支持的 ID 进行比较。
* 如果匹配,设置描述并返回成功。
*/
static int
mypci_probe(device_t dev)
{
device_printf(dev, "MyPCI 探测\n供应商 ID : 0x%x\n设备 ID : 0x%x\n",
pci_get_vendor(dev), pci_get_device(dev));
if (pci_get_vendor(dev) == 0x11c1) {
printf("检测到 Winmodem,探测成功!\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 附加:设备 ID : 0x%x\n", pci_get_devid(dev));
/* 查找我们的 softc 并初始化其字段。 */
sc = device_get_softc(dev);
sc->my_dev = dev;
/*
* 为此设备创建 /dev 条目。内核会自动分配给我们一个主设备号。
* 我们使用此设备的单元号作为次设备号,并将字符设备命名为 "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 设备加载完毕。\n");
return (0);
}
/* 卸载设备。 */
static int
mypci_detach(device_t dev)
{
struct mypci_softc *sc;
/* 清理我们在附加例程中创建的 softc 状态。 */
sc = device_get_softc(dev);
destroy_dev(sc->my_cdev);
printf("Mypci 卸载!\n");
return (0);
}
/* 在系统关闭期间,同步之后调用。 */
static int
mypci_shutdown(device_t dev)
{
printf("Mypci 关闭!\n");
return (0);
}
/*
* 设备挂起例程。
*/
static int
mypci_suspend(device_t dev)
{
printf("Mypci 挂起!\n");
return (0);
}
/*
* 设备恢复例程。
*/
static int
mypci_resume(device_t dev)
{
printf("Mypci 恢复!\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),
DEVMETHOD_END
};
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
11.1.3. 其他资源
11.2. 总线资源
11.2.1. 基地址寄存器
11.2.2. 中断
11.2.3. DMA
11.2.4. 资源的释放
最后更新于