驱动开发
如何创建设备属性节点
前言
最简单的设备属性节点
一、代码添加
在AU_LINUX_ANDROID_LA.VENDOR.1.0\kernel_platform\msm-kernel\drivers\misc\目录下新建test_device.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/io.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
static char test_flag[32]="0";
static ssize_t test_mode_show(struct device *dev,struct device_attribute *attr,
char *buf)
{
printk("xxxxx xxx %s,%d: buf: %s test_flag:%s\n",__func__,__LINE__,buf,test_flag);
return sprintf(buf, "%s\n",test_flag);
}
static ssize_t test_mode_store(struct device *dev,struct device_attribute *attr,
const char *buf, size_t count)
{
sprintf(test_flag, "%s",buf);
printk("xxxxx %s,%d: buf: %s \n",__func__,__LINE__,buf);
return count;
}
static DEVICE_ATTR(xxx_test_mode,0644, test_mode_show, test_mode_store);
static int test_driver_probe(struct platform_device *pdev)
{
int ret;
int force_gpio;
struct device_node *np = pdev->dev.of_node;
printk("xxxxx %s,%d: Enter\n",__func__,__LINE__);
force_gpio = of_get_named_gpio(np,"qcom,gpio-force-download", 0);
if(gpio_is_valid(force_gpio)){
ret = gpio_request(force_gpio, "qcom-force-9008-gpio");
gpio_export(force_gpio,0);
}
ret = device_create_file(&pdev->dev, &dev_attr_xxx_test_mode);
if (ret < 0){
printk("xxxxx create files fail\n");
return ret;
}
return 0;
}
static int test_driver_remove(struct platform_device *pdev)
{
printk("xxxxx %s,%d: Enter\n",__func__,__LINE__);
device_remove_file(&pdev->dev, &dev_attr_xxx_test_mode);
return 0;
}
static const struct of_device_id of_xxx_test_mode_match[] = {
{ .compatible = "xxx-force-usb-boot", },
{},
};
static struct platform_driver test_driver ={
.probe = test_driver_probe,
.remove = test_driver_remove,
.driver = {
.name = "xxx-test-mode",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(of_xxx_test_mode_match),
},
};
static int test_driver_init(void)
{
printk("xxxxx %s,%d: Enter\n",__func__,__LINE__);
return platform_driver_register(&test_driver);
}
static void test_driver_exit(void)
{
printk("xxxxx %s,%d: Enter\n",__func__,__LINE__);
platform_driver_unregister(&test_driver);
return;
}
module_init(test_driver_init);
module_exit(test_driver_exit);
MODULE_AUTHOR("zh@testsmart.com");
MODULE_DESCRIPTION("test Smart Hardware Verion driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:test-mode-driver");
kernel_platform/qcom/proprietary/devicetree/qcom/cape-mtp.dtsi
&soc {
gpio_keys {
compatible = "gpio-keys";
label = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&key_vol_up_default>,<&gpio_keys_active>;
vol_up {
label = "volume_up";
gpios = <&pm8350_gpios 6 GPIO_ACTIVE_LOW>;
linux,input-type = <1>;
linux,code = <KEY_VOLUMEUP>;
gpio-key,wakeup;
debounce-interval = <15>;
linux,can-disable;
};
home {
label = "home";
gpios = <&tlmm 46 GPIO_ACTIVE_LOW>;
linux,input-type = <1>;
linux,code = <KEY_HOME>;
debounce-interval = <15>;
gpio-key,wakeup;
linux,can-disable;
};
vol_down {
label = "volume_down";
gpios = <&tlmm 19 GPIO_ACTIVE_LOW>;
linux,input-type = <1>;
linux,code = <KEY_VOLUMEDOWN>;
gpio-key,wakeup;
debounce-interval = <15>;
linux,can-disable;
};
};
+xxx-force-usb {
+ compatible = "xxx-force-usb-boot";
+ qcom,gpio-force-download = <&tlmm 47 0x2002>;
+};
};
修改misc目录下的Makefile
AU_LINUX_ANDROID_LA.VENDOR.1.0\kernel_platform\msm-kernel\drivers\misc\Makefile
+obj-m += test_device.o
注:obj-m最好是以宏控的方式控制,当然也可以以obj-y的方式编译进内核,后续详细介绍
二、编译
以骁龙8 gen1 plus平台为例
在AU_LINUX_ANDROID_LA.VENDOR.1.0目录下执行:
xxxx@u99:~/AU_LINUX_ANDROID_LA.VENDOR.1.0$bash kernel_platform/qcom/proprietary/prebuilt_HY11/vendorsetup.sh
xxxx@u99:~/AU_LINUX_ANDROID_LA.VENDOR.1.0$cd kernel_platform/
xxxx@u99:~/AU_LINUX_ANDROID_LA.VENDOR.1.0/kernel_platform$BUILD_CONFIG=./common/build.config.msm.waipio ./build/all-variants.sh "./build/build.sh"
如上编译完成后AU_LINUX_ANDROID_LA.VENDOR.1.0\kernel_platform\out\msm-waipio-waipio-consolidate\dist\目录下会生成hello_world.ko;push到设备中验证即可。
三、 验证
adb push AU_LINUX_ANDROID_LA.VENDOR.1.0\kernel_platform\out\msm-waipio-waipio-consolidate\dist\test_device.ko /vendor_dlkm/lib/modules/
adb push AU_LINUX_ANDROID_LA.VENDOR.1.0\kernel_platform\out\msm-waipio-waipio-consolidate\dist\test_device.ko /vendor/lib/modules/
taro:/ # insmod vendor/lib/modules/test_device.ko
taro:/ # dmesg |grep xxx
[ 6455.143660] xxxxx test_driver_exit,96: Enter
[ 6455.143738] xxxxx test_driver_remove,66: Enter
[ 6467.130438] xxxxx test_driver_init,89: Enter
[ 6467.131992] xxxxx test_driver_probe,46: Enter
taro:/ #
130|taro:/ # cat sys/devices/platform/soc/soc:xxx-force-usb/xxx_test_mode
0
taro:/ # echo 1 > sys/devices/platform/soc/soc:xxx-force-usb/xxx_test_mode
taro:/ # echo 1 > sys/devices/platform/soc/soc:xxx-force-usb/xxx_test_mode
taro:/ # cat sys/devices/platform/soc/soc:xxx-force-usb/xxx_test_mode
1
taro:/ # dmesg |grep xxx
[ 6455.143660] xxxxx test_driver_exit,96: Enter
[ 6455.143738] xxxxx test_driver_remove,66: Enter
[ 6467.130438] xxxxx test_driver_init,89: Enter
[ 6467.131992] xxxxx test_driver_probe,46: Enter
[ 6486.875923] xxxxx xxx test_mode_show,24: buf: test_flag:0
[ 6491.227092] xxxxx test_mode_store,33: buf: 1\x0a
[ 6493.537732] xxxxx test_mode_store,33: buf: 1\x0a
[ 6495.128476] xxxxx xxx test_mode_show,24: buf: test_flag:1\x0a
taro:/ #
总结
这里是设备属性节点的添加,添加设备树的目的是为了申请gpio47,这样在开机后sys/class/gpio目录下就可以看到gpio348(SM8475平台的gpio偏移是301),以供厂测使用。关机时,此gpio拉高就可以进9008,即FORCE_USB_BOOT功能。
kernel_platform/common/arch/arm64/configs/consolidate.fragment
需要打开宏控,sys/class/gpio
+CONFIG_GPIO_SYSFS=y
关于设备节点有几种比较方式:
static ssize_t xxx_store(struct kobject *dev, struct kobj_attribute *attr, const char *buf, size_t count)
{
int value = 0;
if (num != sscanf(buf, "%d\n", &value)){
pr_err("mcu_wake>>>%s: num=%d!=1,regaddr=%d\n", __func__, num, value);
return -1;
}
if((value==1)||(value==0)){
if(value==1) {
}
else {
}
}else{
pr_err("%s\n",__func__);
}
return count;
}
==========
static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct dwc3_msm *mdwc = dev_get_drvdata(dev);
if (mdwc->vbus_active)
return snprintf(buf, PAGE_SIZE, "peripheral\n");
if (mdwc->id_state == DWC3_ID_GROUND)
return snprintf(buf, PAGE_SIZE, "host\n");
return snprintf(buf, PAGE_SIZE, "none\n");
}
static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct dwc3_msm *mdwc = dev_get_drvdata(dev);
if (sysfs_streq(buf, "peripheral")) {
mdwc->vbus_active = true;
mdwc->id_state = DWC3_ID_FLOAT;
} else if (sysfs_streq(buf, "host")) {
mdwc->vbus_active = false;
mdwc->id_state = DWC3_ID_GROUND;
} else {
mdwc->vbus_active = false;
mdwc->id_state = DWC3_ID_FLOAT;
}
dwc3_ext_event_notify(mdwc);
return count;
}
==============
static ssize_t usb_compliance_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct dwc3_msm *mdwc = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%c\n",
mdwc->usb_compliance_mode ? 'Y' : 'N');
}
static ssize_t usb_compliance_mode_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int ret = 0;
struct dwc3_msm *mdwc = dev_get_drvdata(dev);
ret = strtobool(buf, &mdwc->usb_compliance_mode);
if (ret)
return ret;
return count;
}
=====================
static ssize_t dbg_state_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
bool v;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (kstrtobool(buf, &v))
return -EINVAL;
msm_host->dbg_en = v;
return count;
}
=========
static ssize_t gtp_dofwupdate_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct goodix_ts_data *ts = dev_get_drvdata(dev);
char update_file_name[FW_NAME_MAX_LEN];
int retval;
if (count > FW_NAME_MAX_LEN) {
dev_info(&ts->client->dev, "FW filename is too long\n");
retval = -EINVAL;
goto exit;
}
strlcpy(update_file_name, buf, count);
ts->force_update = true;
retval = gup_update_proc(update_file_name);
if (retval == FAIL)
dev_err(&ts->client->dev, "Fail to update GTP firmware.\n");
else
dev_info(&ts->client->dev, "Update success\n");
return count;
exit: