linux駆動に関する知識整理

14434 ワード

仕事の必要性のため、emmc駆動のコードを見に行く必要があります.私の学習プロセスに基づいて、まず駆動アーキテクチャに対する理解をまとめます.
一、モジュールの構築と運行
1.1、モジュールのロードとアンインストールLinuxには、モジュールを通じてkernelをロードする機能が多く、必要に応じてkernelをロードすることで、kernelを簡素化し、効率を向上させ、大きな弾力性を保つことができます.このようなロード可能なモジュールは、通常、デバイスドライバです.insmod:モジュールをロードするには、完全なパスとモジュール名modprobeを指定する必要があります:depmod-aの出力/lib/modules/version/modulesに従って依存するモジュールをロードします.depは、必要なすべてのモジュールをロードします.rmmod:モジュールをアンインストールします.
1.2、モジュールの初期化とクリア
module_Init(init_function):モジュールがロードされたときに呼び出される関数.
module_exit(cleanup_function):モジュールのアンインストール時に呼び出される関数.
insmodを使用してモジュールをロードするとmodule_initのinit_function関数が呼び出されます.
module_を使用する場合exitがモジュールをロードする場合、module_initのcleanup_function関数が呼び出されます.
二、バスバスはプロセッサと設備の間の通路であり、設備モデルでは、すべての設備がバスで接続され、バスで設備と駆動関数を管理する.
2.1、バス構造:
struct bus_type {
	const char		*name;
	const char		*dev_name;
	struct device		*dev_root;
	struct device_attribute	*dev_attrs;	/* use dev_groups instead */
	const struct attribute_group **bus_groups;
	const struct attribute_group **dev_groups;
	const struct attribute_group **drv_groups;

	int (*match)(struct device *dev, struct device_driver *drv);
	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
	int (*probe)(struct device *dev);
	int (*remove)(struct device *dev);
	void (*shutdown)(struct device *dev);

	int (*online)(struct device *dev);
	int (*offline)(struct device *dev);

	int (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);

	const struct dev_pm_ops *pm;

	struct iommu_ops *iommu_ops;

	struct subsys_private *p;
	struct lock_class_key lock_key;
};

いくつかの比較的重要なメンバー変数を紹介します:name:バスの名前、バスの登録後、この名前は「/sys/bus/」ディレクトリの下に表示されます.
関数:http://blog.csdn.net/cppgp/article/details/6333359match:デバイスと駆動が一致しているかどうかを判定する(デバイスと駆動構造体のバス名が一致しているかどうかによって判断する)、バスシステムに関連する.uevent:ホットスワップイベントメッセージをユーザースペースに送信する前に環境変数を追加します.Probe:matchが成功すると、デバイスはこの関数によってデバイス関連のマッチングプローブ、デバイス初期化、リソース割り当てなどを実行します.(http://blog.csdn.net/zirconsdu/article/details/8792184)remove:デバイスを削除する;
2.1、バスの登録とログアウト
int bus_register(struct bus_type *bus);
登録バス、バス登録後、登録したバスの名前が/sys/busディレクトリの下に表示されます.bus_です.typeの名前;
void bus_unregister(struct bus_type *bus)
バスをログアウトし、/sys/busで対応するディレクトリをクリアします.(バスにデバイスまたはドライバが登録されている場合は、デバイスとドライバを先にログアウトする必要があります)
三、ブロック設備
3.1、設備構造体
struct device {
	struct device		*parent;

	struct device_private	*p;

	struct kobject kobj;
	const char		*init_name; /* initial name of the device */
	const struct device_type *type;

	struct mutex		mutex;	/* mutex to synchronize calls to
					 * its driver.
					 */

	struct bus_type	*bus;		/* type of bus device is on */
	struct device_driver *driver;	/* which driver has allocated this
					   device */
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */
	struct dev_pm_info	power;
	struct dev_pm_domain	*pm_domain;

#ifdef CONFIG_PINCTRL
	struct dev_pin_info	*pins;
#endif

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#endif
	u64		*dma_mask;	/* dma mask (if dma'able device) */
	u64		coherent_dma_mask;/* Like dma_mask, but for
					     alloc_coherent mappings as
					     not all hardware supports
					     64 bit addresses for consistent
					     allocations such descriptors. */

	struct device_dma_parameters *dma_parms;

	struct list_head	dma_pools;	/* dma pools (if dma'ble) */

	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */
#ifdef CONFIG_DMA_CMA
	struct cma *cma_area;		/* contiguous memory area for dma
					   allocations */
#endif
	/* arch specific additions */
	struct dev_archdata	archdata;

	struct device_node	*of_node; /* associated device tree node */
	struct acpi_dev_node	acpi_node; /* associated ACPI device node */

	dev_t			devt;	/* dev_t, creates the sysfs "dev" */
	u32			id;	/* device instance */

	spinlock_t		devres_lock;
	struct list_head	devres_head;

	struct klist_node	knode_class;
	struct class		*class;
	const struct attribute_group **groups;	/* optional groups */

	void	(*release)(struct device *dev);
	struct iommu_group	*iommu_group;

	bool			offline_disabled:1;
	bool			offline:1;
};

このように長い構造体では、各フィールドの意味を明らかにするのは難しいです.現在、2つのフィールドに注目しています.
const char *init_name;/* initial name of the device*//デバイスの名前
struct bus_type *bus;/* type of bus device is on*//デバイスのバス
3.2、ブロックデバイスの登録とログアウト
int register_blkdev(unsigned int major, const char *name)
この関数は、カーネルに自己登録するために使用されます.
パラメータは、デバイスのプライマリ・デバイス番号とその名前(カーネルが/proc/devicesに表示される名前)です.渡されたプライマリ・デバイス番号が0の場合、カーネルは新しいプライマリ・デバイス番号をデバイスに割り当て、そのデバイス番号を呼び出し元に返します.負の値を返すと、エラーが発生します.linuxデバイスドライバでは、カーネル2.6でregister_blkdevの呼び出しは完全にオプションで、この関数が実行する機能は時間の経過とともに少なくなります.実際、コアを比較すべきカーネル登録デバイスの作業はadd_にあるはずです.disk関数で実装され、3.3節で説明する.
register_とblkdevに対応するログアウトブロックデバイスのドライバの関数は、次のとおりです.
void unregister_blkdev(unsigned int major, const char *name)
ここで、パラメータはregister_に渡さなければならない.blkdevのパラメータは一致します.そうしないと、関数はEINVALに戻り、ログアウトは行われません.
3.3、登録ディスク
void add_disk(struct gendisk *disk)
add_が呼び出されるとディスクデバイスが「アクティブ」になり、いつでも呼び出されます.
それが提供する方法.実際にこれらのメソッドの呼び出しはadd_で初めて行われる可能性があります.diskが戻る前に発生しました.これは、カーネルが前のブロックのデータを読み出してパーティションテーブルを取得する可能性があるためです.したがって、ドライバが完全に初期化され、ディスクへの対応要求に影響を与える前にadd_を呼び出さないでください.disk.
では、どのような方法を提供していますか?この時gendiskが何なのか見てみましょう.
struct gendisk {
	/* major, first_minor and minors are input parameters only,
	 * don't use directly.  Use disk_devt() and disk_max_parts().
	 */
	int major;			/* major number of driver */
	int first_minor;
	int minors;                     /* maximum number of minors, =1 for
                                         * disks that can't be partitioned. */

	char disk_name[DISK_NAME_LEN];	/* name of major driver */
	char *(*devnode)(struct gendisk *gd, umode_t *mode);

	unsigned int events;		/* supported events */
	unsigned int async_events;	/* async events, subset of all */

	/* Array of pointers to partitions indexed by partno.
	 * Protected with matching bdev lock but stat and other
	 * non-critical accesses use RCU.  Always access through
	 * helpers.
	 */
	struct disk_part_tbl __rcu *part_tbl;
	struct hd_struct part0;

	const struct block_device_operations *fops;
	struct request_queue *queue;
	void *private_data;

	int flags;
	struct device *driverfs_dev;  // FIXME: remove
	struct kobject *slave_dir;

	struct timer_rand_state *random;
	atomic_t sync_io;		/* RAID */
	struct disk_events *ev;
#ifdef  CONFIG_BLK_DEV_INTEGRITY
	struct blk_integrity *integrity;
#endif
	int node_id;
};

正直に言うと、この構造体は重要で複雑で、カーネルは独立したディスクデバイスを表すために使用され、実際にはカーネルはパーティションを表すために使用され、ドライバは初期化する必要があります.
その前のいくつかのメンバー変数の役割は、注釈を通じて大体その意味が分かります.ここでは、主に以下のいくつかに注目します.
3.3.1、block_device_operations
const struct block_device_operations *fops;
この構造体のソースコードは以下の通りである.
struct block_device_operations {
	int (*open) (struct block_device *, fmode_t);
	void (*release) (struct gendisk *, fmode_t);
	int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
	int (*compat_ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
	int (*direct_access) (struct block_device *, sector_t,
						void **, unsigned long *);
	unsigned int (*check_events) (struct gendisk *disk,
				      unsigned int clearing);
	/* ->media_changed() is DEPRECATED, use ->check_events() instead */
	int (*media_changed) (struct gendisk *);
	void (*unlock_native_capacity) (struct gendisk *);
	int (*revalidate_disk) (struct gendisk *);
	int (*getgeo)(struct block_device *, struct hd_geometry *);
	/* this callback is with swap_lock and sometimes page table lock held */
	void (*swap_slot_free_notify) (struct block_device *, unsigned long);
	struct module *owner;
};
この構造体は、システムブロック装置が提供する操作インタフェースを示すために使用され、文字装置に対応する構造体はfile_であるoperations、興味のある自分で理解します.
ここでは、関数の役割を簡単に説明します.
int (*open) (struct block_device *, fmode_t);
void (*release) (struct gendisk *, fmode_t);

デバイスがオンまたはオフになったときに呼び出されます.1つのブロックデバイスドライバは、open呼び出しに応答するために、回転ディスク、倉庫ドアのロックなどを使用することができる.
ユーザがデバイスにメディアを入れてロックする場合、release関数ではもちろんロックを解除します.
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
ioctlシステム呼び出しを実現する関数.ブロック・デバイス・レイヤは、まず大量の標準要求を切り取るため、ほとんどのデバイスのioctl関数は非常に短い.
int (*media_changed) (struct gendisk *);
ドライブ内のメディア(リムーバブルメディア)が交換されたかどうかを判断し、交換された場合は0以外の値を返します.
int (*revalidate_disk) (struct gendisk *);
メディアが交換されると、この関数が呼び出されて応答する.新しいメディアを使用するには、ドライバに必要な作業を完了するように伝えます.戻り値はカーネルによって無視されます.
struct module *owner;
構造を有するモジュールポインタを指し、通常はTHIS_に初期化される.MODULE.
ここでは、いったいどの関数がデータの読み取りと書き込みの機能を担当しているのか疑問に思っています.
gendisk構造体に戻り、次のメンバー変数に注目します.
3.3.2、request_queue
struct request_queue *queue;
カーネルはこの構造を使用してデバイス管理I/O要求を行う.
ブロックデバイス要求対列:ブロックデバイスI/O要求を含むシーケンスについては、デバイスが誰が処理できるかを記述する要求のパラメータ:最大サイズ、同じ要求に含まれる独立したセグメントの数、ハードウェアセクタのサイズ、整列需要などが保存されている.要求対列には、複数のI/Oスケジューラ(要求を最適化し、要求のパフォーマンスを最適化するために使用される)が可能になるプラグインインタフェースも実装されています.
「Linuxデバイスドライバ」第3版の第16章のリクエスト処理について、このセクションの最初の文は次のように書かれています.
「各ブロックデバイスドライバのコアは、その要求関数です.実際の作業は、少なくともデバイスの起動など、この関数で完了します....」
では、リクエスト処理関数とリクエスト対列はどのように結びついているのでしょうか.
3.3.3、blk_init_queue
struct request_queue *blk_init_queue(request_fn_proc *rfn, spinlock_t *lock)
この関数は、メッセージ対列を申請するために使用される.
パラメータrequest_fn_proc*rfnは、メッセージ対カラムのメッセージを処理するための関数ポインタである.
パラメータspinlock_t*lockは、キュー作成プロセスの一部であるスピンロックです.rfn関数が呼び出されると、ロックはカーネルによって制御される.
この関数に対応する関数は、void mmc_cleanup_queue(struct mmc_queue *mq).キューを破棄します.
3.3.4、個別要求の紹介
単一の要求は、a、ディスクへの書き込みデータの読み出し要求、b、メーカー情報を要求する;c、下部診断操作;d、特殊機器モードに関する指令:記録可能媒体への書き込みモードの設定等.
構造体は次のとおりです.
struct request {
	union {
		struct list_head queuelist;
		struct llist_node ll_list;
	};
	union {
		struct call_single_data csd;
		struct work_struct mq_flush_data;
	};

	struct request_queue *q;
	struct blk_mq_ctx *mq_ctx;

	u64 cmd_flags;
	enum rq_cmd_type_bits cmd_type;
	unsigned long atomic_flags;

	int cpu;

	/* the following two fields are internal, NEVER access directly */
	unsigned int __data_len;	/* total data len */
	sector_t __sector;		/* sector cursor */

	struct bio *bio;
	struct bio *biotail;

	struct hlist_node hash;	/* merge hash */
	/*
	 * The rb_node is only used inside the io scheduler, requests
	 * are pruned when moved to the dispatch queue. So let the
	 * completion_data share space with the rb_node.
	 */
	union {
		struct rb_node rb_node;	/* sort/lookup */
		void *completion_data;
	};

	/*
	 * Three pointers are available for the IO schedulers, if they need
	 * more they have to dynamically allocate it.  Flush requests are
	 * never put on the IO scheduler. So let the flush fields share
	 * space with the elevator data.
	 */
	union {
		struct {
			struct io_cq		*icq;
			void			*priv[2];
		} elv;

		struct {
			unsigned int		seq;
			struct list_head	list;
			rq_end_io_fn		*saved_end_io;
		} flush;
	};

	struct gendisk *rq_disk;
	struct hd_struct *part;
	unsigned long start_time;
#ifdef CONFIG_BLK_CGROUP
	struct request_list *rl;		/* rl this rq is alloced from */
	unsigned long long start_time_ns;
	unsigned long long io_start_time_ns;    /* when passed to hardware */
#endif
	/* Number of scatter-gather DMA addr+len pairs after
	 * physical address coalescing is performed.
	 */
	unsigned short nr_phys_segments;
#if defined(CONFIG_BLK_DEV_INTEGRITY)
	unsigned short nr_integrity_segments;
#endif

	unsigned short ioprio;

	void *special;		/* opaque pointer available for LLD use */
	char *buffer;		/* kaddr of the current segment if available */

	int tag;
	int errors;

	/*
	 * when request is used as a packet command carrier
	 */
	unsigned char __cmd[BLK_MAX_CDB];
	unsigned char *cmd;
	unsigned short cmd_len;

	unsigned int extra_len;	/* length of alignment and padding */
	unsigned int sense_len;
	unsigned int resid_len;	/* residual count */
	void *sense;

	unsigned long deadline;
	struct list_head timeout_list;
	unsigned int timeout;
	int retries;

	/*
	 * completion callback.
	 */
	rq_end_io_fn *end_io;
	void *end_io_data;

	/* for bidi */
	struct request *next_rq;
};

この構造体を引き出す目的は、構造体の中に重要な変数があることを重点的に説明することです.
struct bio *bio;

この構造体はlinuxカーネルにおける汎用ブロック層のコアデータ構造であり、ブロックデバイスのI/O動作を記述する.メモリバッファとブロックデバイスを接続します.詳細については、以下を参照してください.http://blog.chinaunix.net/uid-13245160-id-84372.html
四、駆動
前のブロックデバイスのセクションでは、block_について後述します.device_operationsとリクエストは列にドライバのカテゴリを入力しました.ここでは主にドライバの登録とログアウト、およびドライバ構造体について説明します.
4.1、駆動構造体
struct device_driver {
	const char		*name;
	struct bus_type		*bus;

	struct module		*owner;
	const char		*mod_name;	/* used for built-in modules */

	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */

	const struct of_device_id	*of_match_table;
	const struct acpi_device_id	*acpi_match_table;

	int (*probe) (struct device *dev);
	int (*remove) (struct device *dev);
	void (*shutdown) (struct device *dev);
	int (*suspend) (struct device *dev, pm_message_t state);
	int (*resume) (struct device *dev);
	const struct attribute_group **groups;

	const struct dev_pm_ops *pm;

	struct driver_private *p;
};

name:ドライバの名前、デバイスと一致する場合、このフィールドの値は一般的にデバイス構造体のinit_と一致する必要があります.name名が一致します(バスのmatch関数によって異なります).
bus:どのバスに登録しますか
その他は以下を参照してください.http://www.cnblogs.com/armlinux/archive/2010/09/20/2396909.html
4.2、ドライバの登録とログアウト
int driver_register(struct device_driver *drv)
void driver_unregister(struct device_driver *drv)

五、バス、設備、駆動の関連
バス、デバイス、駆動の基本的な知識について説明しましたが、3つの関係を整理します.
5.1、まずバスを登録する
第2節では、バス登録後、/sys/busの下にバス名に対応するフォルダが生成され、下にデバイス、駆動の空のフォルダがあることを知っています.
5.2、登録駆動或いは登録設備
登録デバイスと登録ドライバには厳密な順序はありません.どちらが登録しても、バスはmatch関数(2.1参照)を呼び出し、それに対応する駆動またはデバイスがあるかどうかを検出します.一致に成功すると、カーネルは駆動構造体で定義されたprobe関数(4.1参照)を呼び出して、デバイスがこの駆動操作を受けることができるかどうかを問い合せます.ドライバは、初期化などのデバイスに対応する操作を行います.
したがって,真の駆動関数入口はprobe関数にある.
5.3、駆動アンインストール
ドライバまたはドライバ関数が動作しているデバイスが削除されると、カーネルはドライバ関数のremove関数呼び出しを呼び出し、一部のデバイスのアンインストールに対応する操作を行います.
この関数定義(4.1参照).
matchとprobeの実行プロセスについては、以下を参照してください.http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=28758680&id=4561036
詳細については、以下を参照してください.http://blog.chinaunix.net/uid-25014876-id-110295.html