【IMX6ULLドライバーの開発と学習】 12. Linuxドライバーのデバイスツリー

前回のブログ【IMX6ULLドライバ開発学習】11. オブジェクト指向ドライバ設計_階層化思考(デバイスツリー遷移部分の学習)に取り組む

コード取得: https://gitee.com/chenshao777/imx6-ull_-drivers
後で 3 つのレイヤー (実際には最初の 2 レイヤーのみ) をマージして 1 つの dev_drv.c にマージしました。当面は GPIO 操作はありませんマージ前のコードは11.button_drv_chip_device-treeフォルダー
にあり、マージされたコードは12.led_button_drv_treeフォルダーにあり、コードは記事の最後に掲載されています

13 番目のコードにGPIO サブシステムのコードを追加し、 Pinctrl サブシステムに従ってデバイス ツリーを記述してペリフェラル制御を容易にする予定ですので、ご期待ください。


以前は、ドライバー プログラムを 3 つの層に分割しました。 1. file_operations構造を含む
ドライバー フレームワーク プログラム(組み込みカーネル)
ここに画像の説明を挿入

2. ハードウェア操作プログラム ( chip_operations 構造 (GPIO のカスタマイズ、初期化、および操作)、platform_driver 構造 (組み込みカーネル、特定のピンの抽出)を含む)
ここに画像の説明を挿入
ここに画像の説明を挿入

3. platform_device 構造体を含むハードウェア リソース定義プログラム(カーネルには付属しており、リソースメンバーは特定の GPIO ピンを指定します)
ここに画像の説明を挿入
ここに画像の説明を挿入


デバイスツリーの紹介

上の3層目のようにハードウェアリソースを定義するのはやはり面倒ですが、もっと便利な方法はないでしょうか?
はい、デバイスツリーです!

1. デバイス ツリーを変更する デバイス
ツリーの構文を紹介しているブログはたくさんあるので、ここでは詳しく説明しませんが、
デバイス ツリーを使用してハードウェア リソースを定義する方法を直接紹介しましょう。
Punctual Atom IMX6ULL Alpha 開発ボードを例として、まず現在使用しているデバイス ツリー ファイルを見つけます。
パスは次のとおりです。

linux内核文件名/arch/arm/boot/dts/imx6ull-alientek-emmc.dtd

接尾辞 dtb はバイナリのデバイス ツリー ファイルです。これを変更する必要があります。実際に操作する必要があるのは、対応する dts ファイル、つまり imx6ull- alientek-emmc.dtsです。このファイルは任意のツールで開くことができます。ここでは gedit を使用します。テキストエディタで開きます。

ここに画像の説明を挿入
次に、ルート ノードの下に独自のデバイス ノードを追加します。たとえば、
ここに画像の説明を挿入

属性 意味
互換性 デバイス ノードとデバイス ドライバーを一致させ、このプロパティのノードを定義すると、カーネルがplatform_device構造体を自動的に生成します。
ピン カスタム プロパティ、ピンの指定、プロパティ値はof_property_read_u32関数を通じて取得できます。
自分の名前 カスタム属性
スターテス ステータス属性「ok」または「disabled」は、ノードを使用するかどうかを示します。

2. デバイスツリーをコンパイルします。

Linux カーネル ディレクトリに戻り、コマンドを実行します。

make dtbs

dts ファイルが変更されている限り、再コンパイルされて新しい dtb ファイルが生成され、uboot ネットワーク ロード メソッドを使用して新しいデバイス ツリーをロードします。ネットワークを使用して zImage と dtb をロードするように uboot を設定します

新しいデバイスツリーに置き換えることができます 置き換えが成功したかどうかの確認方法
自分で追加したデバイスノードがあるかどうかを確認し、コマンドを入力するだけです

ls /proc/device-tree

ここに画像の説明を挿入
後で追加した 3 つのノードが表示されます。最初のステップは成功です。

3. ハードウェアオペレーティングプログラムのplatform_driver構造を変更します。

(1) platform_driver 構造体に of_match_table 属性を追加し、of_device_id 構造体を追加して、デバイス ツリーと照合します。

ここに画像の説明を挿入

(2) プローブ関数でデバイスツリーのピンを抽出し、デバイスツリーノード(of_property_read_u32などの関数)で属性を抽出します。

ここに画像の説明を挿入
他の場所を変更する必要はなく、次回ペリフェラルを追加する場合は、デバイス ツリーを直接変更し、of_device_id 構造体を変更することができます。


3 つのレイヤーを統合した後のコード (実際には 2 つのレイヤーを統合した後)はい、書き終わりました。皆さん、おやすみなさい。

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/of.h>

int major;				//设备号
static struct class *my_dev_class;
int dev_cnt;
int dev_pins[10];

/*=============================file_operations ==============================*/
static ssize_t my_drv_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
    
    
	printk("drv_read function run....\n");
	return 1;
}

static ssize_t my_drv_write (struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
    
    
	printk("drv_write function run....\n");
	return 0;
}

static int my_drv_open (struct inode *node, struct file *filp)
{
    
    
	printk("drv_open function run....\n");
	return 0;
}

static int my_drv_release (struct inode *node, struct file *filp)
{
    
    

	printk("drv_release function run....\n");
	return 0;
}

/* operations结构体:为应用层提供驱动接口 */
static struct file_operations my_dev_ops = {
    
    
	.owner		= 	THIS_MODULE,
	.read 		=	my_drv_read,
	.write		=	my_drv_write,
	.open		=	my_drv_open,
	.release	=	my_drv_release,
};

/*=============================platform_driver==============================*/
/*  如果匹配到了内核根据设备树生成的platform_device,
	该函数会被调用,如果有多个匹配的设备节点,该函数
	会被多次调用
*/
static int my_probe(struct platform_device *pdev)
{
    
    
	/*  从内核根据设备树生成的 platform_device 
		结构体中获取到设备节点
	*/
	struct device *dev = &pdev->dev;
	struct device_node *np = dev->of_node;
	
	int pin;
	char a[20];
	const char *str = a;
	
	of_property_read_u32(np, "pin", &pin);
	of_property_read_string(np, "my_name", &str);

	//保存设备的引脚
	dev_pins[dev_cnt] = pin;  
	//创建设备节点 /dev/xxx
	device_create(my_dev_class, NULL, MKDEV(major, dev_cnt), NULL, str);
	dev_cnt++;

	printk("my_probe run, my_name = %s\n", str);
	
	return 0;
}

static int my_remove(struct platform_device *pdev)
{
    
    
	/*  从内核根据设备树生成的 platform_device 
		结构体中获取到设备节点
	*/
	struct device *dev = &pdev->dev;
	struct device_node *np = dev->of_node;
	
	int pin, i;
	char a[20];
	const char *str = a;
	
	of_property_read_u32(np, "pin", &pin);
	of_property_read_string(np, "my_name", &str);

	for(i = 0; i < dev_cnt; i++){
    
    
		if(dev_pins[i] == pin){
    
    
			dev_pins[i] = -1;
			device_destroy(my_dev_class, MKDEV(major, i));
			break;
		}
	}
	
	printk("my_remove run, device_destroy %s\n", str);
	return 0;
}

static struct of_device_id my_dev_match[] = {
    
    
	{
    
    .compatible = "hc-led-beep"}, 
	{
    
    .compatible = "hc-led-beep"}, 
	{
    
    .compatible = "hc-key"}, 
	{
    
    },
};

static struct platform_driver dev_driver = {
    
    
	.probe		=	my_probe,	
	.remove		= 	my_remove,
	.driver		= {
    
    
		.name	= "my_platform_driver",
		.of_match_table = my_dev_match,
	},
};

/*=============================驱动出入口函数==============================*/
/* 驱动入口函数:insmod xx.ko 时会被调用 */
static int dev_init(void)
{
    
    	
	major = register_chrdev(0, "hc_dev_drv", &my_dev_ops);
	if(major < 0){
    
    
		printk("register_chrdev famy\n");
		return major;
	}

	my_dev_class = class_create
(THIS_MODULE, "my_dev_class");
	if(IS_ERR(my_dev_class)){
    
    
		printk("class_create failed\n");
		return 1;
	}

	platform_driver_register(&dev_driver);

	return 0;
}

/* 驱动出口函数: rmmod xx.ko 时会被调用 */
static void dev_exit(void)
{
    
    
	platform_driver_unregister(&dev_driver);
	class_destroy(my_dev_class);
	unregister_chrdev(major, "hc_dev_drv");
	printk("my_dev driver exit\n");
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");

おすすめ

転載: blog.csdn.net/HuangChen666/article/details/131386445