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()」を使うことが推奨されます。
むむ。単純にケツの境界調整してるだけ、かと思ったら先頭を調整。
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 なのかは謎。とりあえず寝る。