第 16 章. PC 卡

本章将讨论 FreeBSD 为 PC 卡或 CardBus 设备编写设备驱动程序的机制。但是,目前它只记录了如何将新设备添加到现有的 pccard 驱动程序中。

16.1. 添加设备

设备驱动程序知道它们支持哪些设备。内核中有一个支持设备表,驱动程序使用它来连接到设备。

16.1.1. 概述

PC 卡通过两种方式之一进行识别,这两种方式都基于存储在卡上的卡信息结构 (CIS)。第一种方法是使用数字制造商和产品编号。第二种方法是使用 CIS 中包含的人类可读字符串。PC 卡总线使用一个集中式数据库和一些宏来促进设计模式,以帮助驱动程序编写者将设备与他们的驱动程序匹配。

原始设备制造商 (OEM) 通常为 PC 卡产品开发参考设计,然后将此设计出售给其他公司进行销售。这些公司改进设计,将其产品推向目标受众或地理区域,并将自己的铭牌放在卡上。对物理卡的改进通常非常小,即使有任何更改也是如此。为了加强其品牌,这些供应商将他们的公司名称放在 CIS 空间中的人类可读字符串中,但保持制造商和产品 ID 不变。

由于这种做法,FreeBSD 驱动程序通常依赖于数字 ID 进行设备识别。使用数字 ID 和集中式数据库会使向系统添加 ID 和对卡的支持变得复杂。必须仔细检查谁真正制造了卡,尤其是在制造卡的供应商似乎可能已经在中央数据库中列出了不同的制造商 ID 时。Linksys、D-Link 和 NetGear 是一些美国制造的局域网硬件制造商,它们经常销售相同的设计。这些相同的设计可以在日本以 Buffalo 和 Corega 等名称销售。通常,这些设备将具有相同的制造商和产品 ID。

PC 卡总线代码维护一个卡信息的中央数据库,但不包含与之关联的驱动程序,位于 /sys/dev/pccard/pccarddevs 中。它还提供了一组宏,允许轻松地在驱动程序用于声明设备的表中构建简单的条目。

最后,一些非常低端的设备根本不包含制造商识别信息。这些设备必须通过匹配人类可读的 CIS 字符串来检测。虽然如果我们不需要这种方法作为后备方法会很好,但对于一些非常低端的 CD-ROM 播放器和以太网卡来说,它是必要的。此方法通常应避免,但本节中列出了一些设备,因为它们是在识别 PC 卡业务的 OEM 性质之前添加的。添加新设备时,优先使用数字方法。

16.1.2. pccarddevs 的格式

pccarddevs 文件中有四个部分。第一部分列出使用其供应商的制造商编号。此部分按数字顺序排序。下一部分包含这些供应商使用的所有产品,以及它们的产品 ID 号和描述字符串。描述字符串通常不使用(相反,我们根据人类可读的 CIS 设置设备的描述,即使我们匹配数字版本)。然后重复这两个部分以用于使用字符串匹配方法的设备。最后,C 样式注释(包含在 // 字符之间)允许在文件中的任何位置使用。

文件的第一部分包含供应商 ID。请保持此列表按数字顺序排序。此外,请协调对该文件的更改,因为我们与 NetBSD 共享它,以帮助促进此信息的公共交换中心。例如,以下是一些供应商 ID

vendor FUJITSU			0x0004  Fujitsu Corporation
vendor NETGEAR_2		0x000b  Netgear
vendor PANASONIC		0x0032	Matsushita Electric Industrial Co.
vendor SANDISK			0x0045	Sandisk Corporation

很有可能 NETGEAR_2 条目实际上是 NETGEAR 从中购买卡的 OEM,并且这些卡的支持作者当时不知道 Netgear 正在使用其他人的 ID。这些条目非常简单。vendor 关键字表示此行的类型,后跟供应商的名称。此名称将在后面 pccarddevs 中重复,并在驱动程序的匹配表中使用,因此请保持简短并使用有效的 C 标识符。十六进制的数字 ID 标识制造商。请勿添加 0xffffffff0xffff 形式的 ID,因为这些是保留的 ID(前者是“未设置 ID”,而后者有时在质量极差的卡中看到,以尝试指示“无”)。最后,还有一段描述制造卡的公司字符串。此字符串在 FreeBSD 中除了注释目的外没有任何用途。

文件的第二部分包含产品。如本例所示,格式类似于供应商行

/* Allied Telesis K.K. */
product ALLIEDTELESIS LA_PCM	0x0002 Allied Telesis LA-PCM

/* Archos */
product	ARCHOS ARC_ATAPI	0x0043 MiniCD

product 关键字后跟从上面重复的供应商名称。后跟产品名称,该名称由驱动程序使用,应为有效的 C 标识符,但也可以以数字开头。与供应商一样,此卡的十六进制产品 ID 遵循与 0xffffffff0xffff 相同的约定。最后,还有一段设备本身的字符串描述。此字符串通常不用于 FreeBSD,因为 FreeBSD 的 pccard 总线驱动程序将根据人类可读的 CIS 条目构建字符串,但它可以在某些情况下使用,在这种情况下,这种方法在某种程度上是不够的。产品按制造商的字母顺序排列,然后按产品 ID 的数字顺序排列。它们在每个制造商的条目之前都有 C 注释,并且条目之间有一空行。

第三部分类似于之前的供应商部分,但所有制造商数字 ID 都设置为-1,表示在 FreeBSD pccard 总线代码中“匹配找到的任何内容”。由于这些是 C 标识符,因此它们的名称必须唯一。否则,格式与文件的第一部分相同。

最后一部分包含必须通过字符串条目识别的那些卡的条目。本部分的格式与通用部分略有不同。

product ADDTRON AWP100		{ "Addtron", "AWP-100&spWireless&spPCMCIA", "Version&sp01.02", NULL }
product ALLIEDTELESIS WR211PCM	{ "Allied&spTelesis&spK.K.", "WR211PCM", NULL, NULL } Allied Telesis WR211PCM

熟悉的product关键字后跟供应商名称和卡名称,就像文件第二部分一样。此处格式与之前使用的格式有所不同。有一个{}分组,后跟多个字符串。这些字符串对应于在 CIS_INFO 元组中定义的供应商、产品和额外信息。生成pccarddevs.h的程序会过滤这些字符串,以将&sp替换为实际空格。空字符串表示应忽略条目的相应部分。此处显示的示例包含一个错误条目。除非对卡的操作至关重要,否则不应包含版本号。有时,供应商在现场会有许多不同的卡版本,而所有这些版本都能正常工作,在这种情况下,该信息只会使拥有类似卡的用户更难使用 FreeBSD。有时,当供应商出于市场考虑(可用性、价格等)希望在同一品牌下销售许多不同的部件时,这是必要的。然后,在供应商保留相同制造商/产品对的那些罕见情况下,它对于区分卡至关重要。目前尚不支持正则表达式匹配。

16.1.3. 示例探测例程

要了解如何将设备添加到支持设备列表中,必须了解许多驱动程序具有的探测和/或匹配例程。在 FreeBSD 5.x 中,它变得稍微复杂了一些,因为也存在用于 OLDCARD 的兼容性层。由于只有修饰不同,因此这里将介绍一个理想化的版本。

static const struct pccard_product wi_pccard_products[] = {
	PCMCIA_CARD(3COM, 3CRWE737A, 0),
	PCMCIA_CARD(BUFFALO, WLI_PCM_S11, 0),
	PCMCIA_CARD(BUFFALO, WLI_CF_S11G, 0),
	PCMCIA_CARD(TDK, LAK_CD011WL, 0),
	{ NULL }
};

static int
wi_pccard_probe(dev)
	device_t	dev;
{
	const struct pccard_product *pp;

	if ((pp = pccard_product_lookup(dev, wi_pccard_products,
	    sizeof(wi_pccard_products[0]), NULL)) != NULL) {
		if (pp->pp_name != NULL)
			device_set_desc(dev, pp->pp_name);
		return (0);
	}
	return (ENXIO);
}

这里我们有一个简单的 pccard 探测例程,它匹配一些设备。如上所述,名称可能会有所不同(如果它不是foo_pccard_probe(),则将是foo_pccard_match())。函数pccard_product_lookup()是一个通用函数,它遍历表并返回指向其匹配的第一个条目的指针。一些驱动程序可能会使用此机制将有关某些卡的其他信息传达给驱动程序的其余部分,因此表中可能存在一些差异。唯一的要求是表的每一行都必须以struct pccard_product作为第一个元素。

查看表wi_pccard_products,可以注意到所有条目都采用PCMCIA_CARD(foo, bar, baz)的形式。foo部分是来自pccarddevs的制造商 ID。bar部分是产品 ID。baz是此卡的预期功能编号。许多 pccard 可以具有多个功能,因此需要某种方法来区分功能 1 和功能 0。您可能会看到PCMCIA_CARD_D,其中包括来自pccarddevs的设备描述。您也可能会看到PCMCIA_CARD2PCMCIA_CARD2_D,当您需要同时匹配 CIS 字符串和制造商编号时,它们用于“使用默认描述”和“从 pccarddevs 获取描述”的风格。

16.1.4. 将所有内容整合在一起

要添加新设备,首先必须从设备获取识别信息。最简单的方法是将设备插入 PC 卡或 CF 插槽并发出devinfo -v。示例输出

        cbb1 pnpinfo vendor=0x104c device=0xac51 subvendor=0x1265 subdevice=0x0300 class=0x060700 at slot=10 function=1
          cardbus1
          pccard1
            unknown pnpinfo manufacturer=0x026f product=0x030c cisvendor="BUFFALO" cisproduct="WLI2-CF-S11" function_type=6 at function=0

manufacturerproduct是此产品的数字 ID,而cisvendorcisproduct是来自 CIS 的产品描述字符串。

由于我们首先希望优先考虑数字选项,因此首先尝试基于该选项构建条目。上面提到的卡已为此示例稍作虚构。供应商是 BUFFALO,我们看到它已经有一个条目

vendor BUFFALO			0x026f	BUFFALO (Melco Corporation)

但此特定卡没有条目。相反,我们发现

/* BUFFALO */
product BUFFALO WLI_PCM_S11	0x0305	BUFFALO AirStation 11Mbps WLAN
product BUFFALO LPC_CF_CLT	0x0307	BUFFALO LPC-CF-CLT
product	BUFFALO	LPC3_CLT	0x030a	BUFFALO LPC3-CLT Ethernet Adapter
product BUFFALO WLI_CF_S11G	0x030b	BUFFALO AirStation 11Mbps CF WLAN

要添加设备,我们可以将此条目添加到pccarddevs

product BUFFALO WLI2_CF_S11G	0x030c	BUFFALO AirStation ultra 802.11b CF

完成这些步骤后,可以将卡添加到驱动程序中。这是一个简单的操作,只需添加一行

static const struct pccard_product wi_pccard_products[] = {
	PCMCIA_CARD(3COM, 3CRWE737A, 0),
	PCMCIA_CARD(BUFFALO, WLI_PCM_S11, 0),
	PCMCIA_CARD(BUFFALO, WLI_CF_S11G, 0),
+	PCMCIA_CARD(BUFFALO, WLI_CF2_S11G, 0),
	PCMCIA_CARD(TDK, LAK_CD011WL, 0),
	{ NULL }
};

请注意,我在添加的行之前的行中包含了一个“+”,但这仅仅是为了突出显示该行。不要将其添加到实际驱动程序中。添加该行后,您可以重新编译内核或模块并进行测试。如果设备被识别并工作,请提交补丁。如果它不起作用,请找出使其工作所需的内容并提交补丁。如果设备根本没有被识别,则说明您做错了什么,应该重新检查每个步骤。

如果您是 FreeBSD src 提交者,并且一切似乎都正常工作,那么您可以将更改提交到树中。但是,需要考虑一些细微的棘手问题。pccarddevs必须首先提交到树中。然后必须重新生成pccarddevs.h并作为第二步提交,确保后者文件中包含正确的 $FreeBSD$ 标记。最后,提交对驱动程序的添加。

16.1.5. 提交新设备

请不要将新设备的条目直接发送给作者。相反,请将其作为 PR 提交,并将 PR 编号发送给作者以供其记录。这确保了条目不会丢失。提交 PR 时,无需在补丁中包含pccardevs.h差异,因为这些差异将重新生成。需要包含设备的描述以及对客户端驱动程序的补丁。如果您不知道名称,请使用 OEM99 作为名称,作者将在调查后相应地调整 OEM99。提交者不应提交 OEM99,而应找到最高的 OEM 条目并提交比该条目多一个的条目。


上次修改时间:2024 年 3 月 9 日,作者 Danilo G. Baio