8139too

という事で、まずは amazon:Linux カーネル解析入門 の 6 章の順に沿って 8139too.c のチェックをたれ流す。
pci_module_init() (実体は pci_register_driver()) に struct pci_driver 型を渡す事でドライバの load が可能、とある。ちなみに 8139too のエントリポイントは p.252 にもある通り、以下のような記述。

static int __init rtl8139_init_module (void)
{
       /* when we're a module, we always print a version message,
        * even if no 8139 board is found.
        */
#ifdef MODULE
       printk (KERN_INFO RTL8139_DRIVER_NAME "\n");
#endif

       return pci_module_init (&rtl8139_pci_driver);
}

渡している rtl8139_pci_driver の定義は 8139too.c にあり、以下となっている。

static struct pci_driver rtl8139_pci_driver = {
       .name           = DRV_NAME,
       .id_table       = rtl8139_pci_tbl,
       .probe          = rtl8139_init_one,
       .remove         = __devexit_p(rtl8139_remove_one),
#ifdef CONFIG_PM
       .suspend        = rtl8139_suspend,
       .resume         = rtl8139_resume,
#endif /* CONFIG_PM */
};

関数定義の直上だった。このあたりは最近のエントリで記述しているような記憶あり。ちなみにstruct pci_driver は include/linux/pci.h にて定義されている。定義の引用は略。で、関数ポインタなメンバは 6 つくらいあるんですが、上記で初期化されているメンバは 4 つ (設定マターであるが 2 つのケースもあり) となっているのが分かる。

probe

struct pci_driver 型の probe メンバは_「PCI デバイス」が見付かった場合、登録されている「probe」関数を呼び出します_とある。
この関数 (rtl8139_init_one)、当たり前ですが 8139too.c にて定義。テキストでも結構詳細に解説が入っている。先頭部分、非常に興味深いので引用してみる。

static int __devinit rtl8139_init_one (struct pci_dev *pdev,
				       const struct pci_device_id *ent)
{
	struct net_device *dev = NULL;
	struct rtl8139_private *tp;
	int i, addr_len, option;
	void *ioaddr;
	static int board_idx = -1;
	u8 pci_rev;

	assert (pdev != NULL);
	assert (ent != NULL);

	board_idx++;
(後略)

rtl8139_init_one() 関数は struct pci_driver 型の rtl8139_pci_driver における probe メンバにて公開しているんで実名は伏せてるのか。__devinit という修飾子が微妙。
あと、普通に assert() が入ってるんでびっくり (C な assert は久々に見た) と共に、board_idx はこれ仕方無いんだろうか、と。それはでっかいお世話として、テキストの p.257 にある記述によれば

  • struct net_device の allocate が必要
  • allocate したスペースをカーネルに教える必要あり

との事で rtl8139_init_board() を呼び出している模様。定義されているのは rtl8139_init_one() の直上ですが結構長いな。で、手続き先頭部分で net_device 構造体のスペース確保とメンバ初期化をする alloc_etherdev() 関数を呼び出している。定義は net/ethernet/eth.c にて定義されている、と ETAGS は言っている。以下。

struct net_device *alloc_etherdev(int sizeof_priv)
{
	return alloc_netdev(sizeof_priv, "eth%d", ether_setup);
}

む。ether_setup って何だ、と思いつつソースを見てみると、同一ソースにて関数定義あり、おそらくは関数ポインタを渡しているんだな、と。あるいは alloc_netdev() 関数は net/core/dev.c にて定義。
ひっカカルのは以下。

	/* ensure 32-byte alignment of both the device and private area */
	alloc_size = (sizeof(*dev) + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST;
	alloc_size += sizeof_priv + NETDEV_ALIGN_CONST;

あるいはテキストにもこんな記述があるんですが ...

ドライバ固有のデータ構造体の分のメモリも確保されており、「priv」メンバからブラ下がっているのですが、そのメンバは使わずに「netdev_priv()」を使うことが推奨されます。


amazon: Linux カーネル解析入門より引用

むむ。単純にケツの境界調整してるだけ、かと思ったら先頭を調整。

	dev = (struct net_device *)
		(((long)p + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST);
	dev->padded = (char *)dev - (char *)p;

で、ケツは大丈夫なのかと思いきや、二番目の式でケツも調整されている模様。32-byte な境界整列って何のためなんでしょうか。で、テキストの引用の通り、netdev_priv() では struct net_device の後に確保した領域を同じように境界整列 (というか、開始アドレスの調整か) させて戻している。

static inline void *netdev_priv(struct net_device *dev)
{
	return (char *)dev + ((sizeof(struct net_device)
					+ NETDEV_ALIGN_CONST)
				& ~NETDEV_ALIGN_CONST);
}

何故に 32-byte なのかは謎。とりあえず寝る。