在設備樹中描述platform_device
背景
在高通平台做gpio按鍵配置的時候,根據之前的經驗,想從設備樹中對應的關鍵字找到實際的驅動解析實現,以此加深對設備樹屬性配置的理解。
但是我並沒有找到,只是知道在drivers/input/keyboard/gpio_keys.c
解析了那一段的配置。翻閱以前自己整理的文章,也沒發現一個所以然。
看來,對設備樹的學習還是不能停下。
Reference:
介紹
定義:在設備樹的dts文件里,帶有compatible
屬性的節點就是表示一個platform_device.
用法:在設備樹里增加一個設備節點,在內核里的dts文件里描述設備節點;此后,編寫驅動代碼完成對其的解析即可。
接口函數
在設備驅動里獲取設備樹中的設備資源需要一套接口函數來實現:
#include <linux/property.h>
函數以device
開頭表示讀取設備的屬性, 以fwnode
開頭表示讀取子節點的屬性。
參數propname
代表指定要獲取值的屬性名。
設備屬性
判斷屬性是否存在
bool device_property_present(struct device *dev, const char *propname);
常見用法1:作為指定的屬性
nvmem->read_only = device_property_present(config->dev, "read-only") |
config->read_only;
//...;
if (nvmem->read_only) {
//...;
}
或
/* Retrieve the PHY configuration properties */
if (device_property_present(pdata->phy_dev, XGBE_BLWC_PROPERTY)) {
ret = device_property_read_u32_array(pdata->phy_dev,
XGBE_BLWC_PROPERTY,
phy_data->blwc,
XGBE_SPEEDS);
if (ret) {
dev_err(pdata->dev, "invalid %s property\n",
XGBE_BLWC_PROPERTY);
return ret;
}
} else {
memcpy(phy_data->blwc, xgbe_phy_blwc,
sizeof(phy_data->blwc));
}
獲取整數值
int device_property_read_u8_array(struct device *dev, const char *propname,
u8 *val, size_t nval);
int device_property_read_u16_array(struct device *dev, const char *propname,
u16 *val, size_t nval);
int device_property_read_u32_array(struct device *dev, const char *propname,
u32 *val, size_t nval);
int device_property_read_u64_array(struct device *dev, const char *propname,
u64 *val, size_t nval);
nval 代表 返回的數組成員個數。
文本值
獲取
int device_property_read_string(struct device *dev, const char *propname,
const char **val);
注:獲取到的值會放進
val
中;
常見用法:
device_property_read_string(dev, "label", &pdata->name);
my_key {
label = "btn1";
gpios = <&tlmm 102 0x1>;
linux,input-type = <1>;
linux,code = <59>;
debounce-interval = <15>;
linux,can-disable;
gpio-key,wakeup;
};
判斷文本值是否匹配
int device_property_match_string(struct device *dev,
const char *propname, const char *string);
例如:
if (!device_property_match_string(dev, "rotary-encoder,encoding",
"binary")) {
dev_info(dev, "binary");
encoder->encoding = ROTENC_BINARY;
} else {
dev_err(dev, "unknown encoding setting\n");
return -EINVAL;
}
獲取子節點
/* 獲取個數 */
unsigned int device_get_child_node_count(struct device *dev); //獲取設備的子節點個數
/* 獲取下一個子節點 */
struct fwnode_handle *device_get_next_child_node(struct device *dev,
struct fwnode_handle *child);
子節點屬性
fwnode
是表示子節點的對象地址,因為和上面的用法類似,因此不再詳細說明。
/* 用於獲取設備子節點的屬性值函數. */
bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname);
int fwnode_property_read_u8_array(struct fwnode_handle *fwnode,
const char *propname, u8 *val,
size_t nval);
int fwnode_property_read_u16_array(struct fwnode_handle *fwnode,
const char *propname, u16 *val,
size_t nval);
int fwnode_property_read_u32_array(struct fwnode_handle *fwnode,
const char *propname, u32 *val,
size_t nval);
int fwnode_property_read_u64_array(struct fwnode_handle *fwnode,
const char *propname, u64 *val,
size_t nval);
int fwnode_property_read_string_array(struct fwnode_handle *fwnode,
const char *propname, const char **val,
size_t nval);
int fwnode_property_read_string(struct fwnode_handle *fwnode,
const char *propname, const char **val);
int fwnode_property_match_string(struct fwnode_handle *fwnode,
const char *propname, const char *string);
例子
設備樹
舉一個例子,如在某個被編譯的.dts
文件里加入以下內容:
mynodes@77885566 { /* 則創建出來的platform_device的名為mynodes@77885566 */
compatible = "mynodes"; /* 設備節點的屬性 */
autorepeat = <1>;
/* 設備子節點 */
btn1 {
label = "btn1"; /* 設備子節點的屬性 */
code = <0x11>;
};
btn2 {
label = "btn2";
code = <0x22>;
};
};
增加內容后,則重編設備樹:
make dtbs ARCH=arm64 CROSS_COMPILE=...
再把編譯出來的dtb
替換板上所用的dtb
文件,重啟系統后,可以查看到內容:
# ls /sys/bus/platform/devices/mynodes@77885566/
driver_override of_node/ subsystem/
modalias power/ uevent
# ls /sys/bus/platform/devices/mynodes@77885566/of_node/
autorepeat btn1/ btn2/ compatible name123456
在dst設備樹文件描述設備后就需要與platform_driver
進行匹配和驅動了。
驅動代碼
用於獲取mynodes
設備資源的驅動源碼:
/* mydrv.c */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
//產生一個for循環用於檢查所有的子節點
#define device_for_each_child_node(dev, child) \
for (child = device_get_next_child_node(dev, NULL); child; \
child = device_get_next_child_node(dev, child))
int myprobe(struct platform_device *pdev)
{
struct fwnode_handle *fwhandle;
const char *str;
u32 val;
//獲取設備子節點的個數
printk("child node count : %d\n", device_get_child_node_count(&pdev->dev));
//獲取設備屬性autorepeat的值
printk("%d\n", device_property_read_bool(&pdev->dev, "autorepeat"));
//遍歷設備的每個子節點
device_for_each_child_node(&pdev->dev, fwhandle) {
//獲取設備子節點的label屬性值
fwnode_property_read_string(fwhandle, "label", &str);
printk("label = %s\n", str);
//獲取設備子節點的code屬性值
fwnode_property_read_u32(fwhandle, "code", &val);
printk("code = %x\n", val);
};
return 0;
}
int myremove(struct platform_device *pdev)
{
printk("in myremove ...\n");
return 0;
}
struct of_device_id ids[] = {
{.compatible = "mynodes"},
{},
};
struct platform_driver mydrv = {
.probe = myprobe,
.remove = myremove,
.driver = {
.owner = THIS_MODULE,
.name = "mydrv" ,
.of_match_table = ids,
},
};
module_platform_driver(mydrv);
MODULE_LICENSE("GPL");
編譯驅動模塊加載后的輸出結果:
[ 111.222065] child node count : 2
[ 111.222429] 1
[ 111.223054] label = btn1
[ 111.223690] code = 11
[ 111.224000] label = btn2
[ 111.224623] code = 22