ドライバの初期化処理について

はじめに

 本章から、実際に「kernel/mediatek/4.4/drivers/misc/mediatek/sensors-1.0/alsps/ltr578/」にある、照度センサドライバの本体であるltr578.cを見始めます。
まず、moduleとしての初期化と、driverとしての初期化に分けて、全体の初期化の流れを説明します。

moduleとしての初期化

 処理順は下から上に書かれる傾向があるので一番下から見始めましょう。最下部付近に

module_init(ltr578_init);

の記載があります、引数のltr578_initがドライバの初期化で初めに呼ばれる関数になります。
module_initは、「kernel/mediatek/4.4/include/linux/module.h」 に以下の定義があります。

define module_init(x) __initcall(x);

__initcallは、「kernel/mediatek/4.4/include/linux/init.h」 に以下の定義があります。

define __initcall(fn) device_initcall(fn)

device_initcallは、同ファイルに以下の定義があります。

define device_initcall(fn) __define_initcall(fn, 6)

__define_initcallは、同ファイルに以下の定義があります。

define __define_initcall(fn, id) \
    static initcall_t __initcall_##fn##id __used \
    __attribute__((__section__(".initcall" #id ".init"))) = fn; \
    LTO_REFERENCE_INITCALL(__initcall_##fn##id)

 上記の定義により、引数のfnが「kernel/mediatek/4.4/init/main.c」の __initcall6_start にビルド時に叩き込まれます。

static initcall_t *initcall_levels[] __initdata = {
__initcall0_start,
__initcall1_start,
__initcall2_start,
__initcall3_start,
__initcall4_start,
__initcall5_start,
__initcall6_start,
__initcall7_start,
__initcall_end,
};

これを、同ファイルの以下処理で、起動時にLinuxKernelが順番に呼び出していくことで、最終的にmodule_initで登録したltr578_init関数が呼び出されます。

static void __init do_initcall_level(int level)
{
	initcall_t *fn;

	strcpy(initcall_command_line, saved_command_line);
	parse_args(initcall_level_names[level],
		   initcall_command_line, __start___param,
		   __stop___param - __start___param,
		   level, level,
		   NULL, &repair_env_string);

	for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
		do_one_initcall(*fn);
}

 module_initを使用したので、__initcall6_startに叩き込まれてますが、
「kernel/mediatek/4.4/include/linux/init.h」 には、以下の定義があり、module_init以外を使うことで、ドライバの起動順序をコントロールすることが可能です。

#define pure_initcall(fn)		__define_initcall(fn, 0)

#define core_initcall(fn)		__define_initcall(fn, 1)
#define core_initcall_sync(fn)		__define_initcall(fn, 1s)
#define postcore_initcall(fn)		__define_initcall(fn, 2)
#define postcore_initcall_sync(fn)	__define_initcall(fn, 2s)
#define arch_initcall(fn)		__define_initcall(fn, 3)
#define arch_initcall_sync(fn)		__define_initcall(fn, 3s)
#define subsys_initcall(fn)		__define_initcall(fn, 4)
#define subsys_initcall_sync(fn)	__define_initcall(fn, 4s)
#define fs_initcall(fn)			__define_initcall(fn, 5)
#define fs_initcall_sync(fn)		__define_initcall(fn, 5s)
#define rootfs_initcall(fn)		__define_initcall(fn, rootfs)
#define device_initcall(fn)		__define_initcall(fn, 6)
#define device_initcall_sync(fn)	__define_initcall(fn, 6s)
#define late_initcall(fn)		__define_initcall(fn, 7)
#define late_initcall_sync(fn)		__define_initcall(fn, 7s)

 同じレベルの時は環境依存になりますが、ビルド時に叩き込まれる都合、基本的にはMakefileの上から順に呼ばれます。また、前章で軽く触れた、ローダブルモジュールでのビルドにおいては、起動後に追加していく仕組み上、これらの順番は無視されます。「kernel/mediatek/4.4/include/linux/module.h」 で全て同じ定義に置き換えられています。

#define early_initcall(fn)		module_init(fn)
#define core_initcall(fn)		module_init(fn)
#define core_initcall_sync(fn)		module_init(fn)
#define postcore_initcall(fn)		module_init(fn)
#define postcore_initcall_sync(fn)	module_init(fn)
#define arch_initcall(fn)		module_init(fn)
#define subsys_initcall(fn)		module_init(fn)
#define subsys_initcall_sync(fn)	module_init(fn)
#define fs_initcall(fn)			module_init(fn)
#define fs_initcall_sync(fn)		module_init(fn)
#define rootfs_initcall(fn)		module_init(fn)
#define device_initcall(fn)		module_init(fn)
#define device_initcall_sync(fn)	module_init(fn)
#define late_initcall(fn)		module_init(fn)
#define late_initcall_sync(fn)		module_init(fn)

driverとしての初期化

 初めに呼ばれる関数と記載した、ltr578_initの中身について確認していきます。ltr578_initではalsps_driver_add関数を呼んでいます。この関数は一つ上の階層「kernel/mediatek/4.4/drivers/misc/mediatek/sensors-1.0/alsps/」にあるalsps.cに定義されています。このファイルは、照度センサーに対する抽象化インターフェースを提供するソースコードです。alsps_driver_addでは、platform_driver_register関数を呼び出しLinuxのドライバとしての登録処理を行います。
 platform_driver_registerの引数に渡している、als_ps_driverのprobe処理であるals_ps_probeにdriverの初期化処理を記載するのが一般的ですが、本ドライバにおいては、処理をあまりしておりません。
 代わりにalsps_driver_addで、alsps_init_listにltr578_init_infoを登録し、late_initcallで登録しているalsps_initから、alsps_probe、alsps_real_driver_initを呼び出し、ltr578_init_infoに登録している、実際の初期化処理であるltr578_local_initを呼び出します。前述の、moduleの初期化順の仕様を活用し、alsps_initより前にltr578_initが必ず先に呼び出されるようになっています。
 ltr578_local_initでは、ltr578_i2c_driverを引数としてi2c_add_driverを呼び出し、LinuxのI2Cドライバとしての登録処理を行います。登録処理完了後、ltr578_i2c_driverにコールバック登録されており、実際のデバイスの初期化処理が記載されている、ltr578_i2c_probeが呼び出されます。

最後に

 次章からは、本章で出てきたdriverとしての初期化処理に含まれる内容について、ソフトウェアの初期化処理(5章)、ハードウェアの初期化処理(6章)、インターフェースの初期化処理(7章)の3章に分けて説明します。

コメント

タイトルとURLをコピーしました