前回のブログ【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");