【LED子系统】四、核心层详解(一)

news2024/11/29 22:37:09
img
个人主页:董哥聊技术
我是董哥,嵌入式领域新星创作者
创作理念:专注分享高质量嵌入式文章,让大家读有所得!
img

文章目录

    • 1、前言
    • 2、leds_init分析
      • 2.1 相关数据结构
        • 2.1.1 class
      • 2.2 实现流程
    • 3、leds_class_dev_pm_ops分析
      • 3.1 相关数据结构
        • 3.1.1 dev_pm_ops
      • 3.2 相关实现
    • 4、led_groups分析
      • 4.1 相关数据结构
        • 4.1.1 attribute_group
        • 4.1.2 attribute
      • 4.2 相关实现
      • 4.3 扩展
        • 4.3.1 DEVICE_ATTR_RW分析
    • 5、led class的注册注销分析
      • 5.1 相关实现
        • 5.1.1 devm_of_led_classdev_register
        • 5.1.2 of_led_classdev_register
        • 5.1.3 led_classdev_next_name
    • 6、总结

1、前言

上篇文章我们了解了子系统的硬件驱动层,下面我们来分析驱动框架中核心层的实现以及作用。

image-20230417084033734

LED子系统框架中,核心层包括几个部分:核心层的实现部分(led-core.c)、sysfs文件节点创建(led-class.c)、触发功能实现(led-triggers.cdriver/leds/triggers/led-xxx.c)

其中,触发功能部分较为独立,我们暂且先不去分析。

我们先从led-class.c文件开始分析

 

2、leds_init分析

该函数其主要是为了创建LED设备文件节点,方便用户通过节点直接访问。

该文件,我们直接拉下底部,我们直接看入口函数:leds_init

 

2.1 相关数据结构

2.1.1 class

/**
 * struct class - device classes
 * @name:	Name of the class.
 * @owner:	The module owner.
 * @class_groups: Default attributes of this class.
 * @dev_groups:	Default attributes of the devices that belong to the class.
 * @dev_kobj:	The kobject that represents this class and links it into the hierarchy.
 * @dev_uevent:	Called when a device is added, removed from this class, or a
 *		few other things that generate uevents to add the environment
 *		variables.
 * @devnode:	Callback to provide the devtmpfs.
 * @class_release: Called to release this class.
 * @dev_release: Called to release the device.
 * @shutdown_pre: Called at shut-down time before driver shutdown.
 * @ns_type:	Callbacks so sysfs can detemine namespaces.
 * @namespace:	Namespace of the device belongs to this class.
 * @get_ownership: Allows class to specify uid/gid of the sysfs directories
 *		for the devices belonging to the class. Usually tied to
 *		device's namespace.
 * @pm:		The default device power management operations of this class.
 * @p:		The private data of the driver core, no one other than the
 *		driver core can touch this.
 *
 * A class is a higher-level view of a device that abstracts out low-level
 * implementation details. Drivers may see a SCSI disk or an ATA disk, but,
 * at the class level, they are all simply disks. Classes allow user space
 * to work with devices based on what they do, rather than how they are
 * connected or how they work.
 */
struct class {
    const char		*name;
    struct module		*owner;

    const struct attribute_group	**class_groups;
    const struct attribute_group	**dev_groups;
    struct kobject			*dev_kobj;

    int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
    char *(*devnode)(struct device *dev, umode_t *mode);

    void (*class_release)(struct class *class);
    void (*dev_release)(struct device *dev);

    int (*shutdown_pre)(struct device *dev);

    const struct kobj_ns_type_operations *ns_type;
    const void *(*namespace)(struct device *dev);

    void (*get_ownership)(struct device *dev, kuid_t *uid, kgid_t *gid);

    const struct dev_pm_ops *pm;

    struct subsys_private *p;
};

结构体名称class

文件位置include/linux/device.h

主要作用:设备类,表示某一类设备,在此是为了创建led设备类,源代码为:static struct class *leds_class;

 

2.2 实现流程

static int __init leds_init(void)
{
    leds_class = class_create(THIS_MODULE, "leds");			//	创建leds文件节点
    if (IS_ERR(leds_class))
        return PTR_ERR(leds_class);
    leds_class->pm = &leds_class_dev_pm_ops;
    leds_class->dev_groups = led_groups;					
    return 0;
}

static void __exit leds_exit(void)
{
    class_destroy(leds_class);
}

subsys_initcall(leds_init);
module_exit(leds_exit);

MODULE_AUTHOR("John Lenz, Richard Purdie");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LED Class Interface");

函数介绍leds_init该函数在内核在加载的时候执行,调用class_create创建LED设备类,用于管理该类设备。

实现思路

  1. 调用class_create创建led类设备
  2. 赋值结构体leds_class->pmleds_class_dev_pm_ops配置电源管理接口,用于休眠唤醒,
  3. 赋值结构体leds_class->dev_groups,设置该类设备的文件属性。

 

3、leds_class_dev_pm_ops分析

上面我们在创建led类的时候,赋值了leds_class_dev_pm_ops电源管理接口,那么该接口是怎么定义的呢?

 

3.1 相关数据结构

3.1.1 dev_pm_ops

struct dev_pm_ops {
    int (*prepare)(struct device *dev);
    void (*complete)(struct device *dev);
    int (*suspend)(struct device *dev);
    int (*resume)(struct device *dev);
    int (*freeze)(struct device *dev);
    int (*thaw)(struct device *dev);
    int (*poweroff)(struct device *dev);
    int (*restore)(struct device *dev);
    int (*suspend_late)(struct device *dev);
    int (*resume_early)(struct device *dev);
    int (*freeze_late)(struct device *dev);
    int (*thaw_early)(struct device *dev);
    int (*poweroff_late)(struct device *dev);
    int (*restore_early)(struct device *dev);
    int (*suspend_noirq)(struct device *dev);
    int (*resume_noirq)(struct device *dev);
    int (*freeze_noirq)(struct device *dev);
    int (*thaw_noirq)(struct device *dev);
    int (*poweroff_noirq)(struct device *dev);
    int (*restore_noirq)(struct device *dev);
    int (*runtime_suspend)(struct device *dev);
    int (*runtime_resume)(struct device *dev);
    int (*runtime_idle)(struct device *dev);
};

结构体名称dev_pm_ops

文件位置include/linux/pm.h

主要作用:主要定义了设备电源管理的回调函数接口,我们一般使用suspendresume两个,用于休眠,唤醒。

 

3.2 相关实现

/**
 * led_classdev_suspend - suspend an led_classdev.
 * @led_cdev: the led_classdev to suspend.
 */
void led_classdev_suspend(struct led_classdev *led_cdev)
{
    led_cdev->flags |= LED_SUSPENDED;
    led_set_brightness_nopm(led_cdev, 0);						//	灯灭
}
EXPORT_SYMBOL_GPL(led_classdev_suspend);

/**
 * led_classdev_resume - resume an led_classdev.
 * @led_cdev: the led_classdev to resume.
 */
void led_classdev_resume(struct led_classdev *led_cdev)
{
    led_set_brightness_nopm(led_cdev, led_cdev->brightness);	//	灯亮

    if (led_cdev->flash_resume)
        led_cdev->flash_resume(led_cdev);

    led_cdev->flags &= ~LED_SUSPENDED;
}
EXPORT_SYMBOL_GPL(led_classdev_resume);

#ifdef CONFIG_PM_SLEEP
static int led_suspend(struct device *dev)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);

    if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
        led_classdev_suspend(led_cdev);

    return 0;
}

static int led_resume(struct device *dev)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);

    if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
        led_classdev_resume(led_cdev);

    return 0;
}
#endif

static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);

/*
 * Use this if you want to use the same suspend and resume callbacks for suspend
 * to RAM and hibernation.
 */
#define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \
const struct dev_pm_ops name = { \
    SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \
}

函数介绍:代码的这一部分,主要用于实现休眠唤醒功能,然后将相关函数赋值给dev_pm_ops结构体中的回调函数

实现思路

  1. 定义了几个函数led_suspendled_resumeled_classdev_resumeled_classdev_suspend作为休眠唤醒的实现
  2. 通过宏定义SIMPLE_DEV_PM_OPS将函数绑定到leds_class->pm中,作为该类设备的休眠唤醒管理。

这里SIMPLE_DEV_PM_OPS宏定义较为简单,自行翻阅源码查看即可!

 

4、led_groups分析

上面我们在创建led类的时候,将led_groups赋值给了struct attribute_group属性组结构体,那么led_groups是怎么定义的呢?

 

4.1 相关数据结构

4.1.1 attribute_group

/**
 * struct attribute_group - data structure used to declare an attribute group.
 * @name:	Optional: Attribute group name
 *		If specified, the attribute group will be created in
 *		a new subdirectory with this name.
 * @is_visible:	Optional: Function to return permissions associated with an
 *		attribute of the group. Will be called repeatedly for each
 *		non-binary attribute in the group. Only read/write
 *		permissions as well as SYSFS_PREALLOC are accepted. Must
 *		return 0 if an attribute is not visible. The returned value
 *		will replace static permissions defined in struct attribute.
 * @is_bin_visible:
 *		Optional: Function to return permissions associated with a
 *		binary attribute of the group. Will be called repeatedly
 *		for each binary attribute in the group. Only read/write
 *		permissions as well as SYSFS_PREALLOC are accepted. Must
 *		return 0 if a binary attribute is not visible. The returned
 *		value will replace static permissions defined in
 *		struct bin_attribute.
 * @attrs:	Pointer to NULL terminated list of attributes.
 * @bin_attrs:	Pointer to NULL terminated list of binary attributes.
 *		Either attrs or bin_attrs or both must be provided.
 */
struct attribute_group {
    const char		*name;
    umode_t			(*is_visible)(struct kobject *,
                          struct attribute *, int);
    umode_t			(*is_bin_visible)(struct kobject *,
                          struct bin_attribute *, int);
    struct attribute	**attrs;
    struct bin_attribute	**bin_attrs;
};

结构体名称attribute_group

文件位置include/linux/sysfs.h

主要作用:定义一个属性组,其中包括一组属性和二进制属性,这些属性可以与内核对象相关联,以便用户的访问。

 

4.1.2 attribute

struct attribute {
    const char		*name;
    umode_t			mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
    bool			ignore_lockdep:1;
    struct lock_class_key	*key;
    struct lock_class_key	skey;
#endif
};

结构体名称attribute

文件位置include/linux/sysfs.h

主要作用:代表一个属性

 

4.2 相关实现

static ssize_t brightness_show(struct device *dev,
        struct device_attribute *attr, char *buf)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);

    /* no lock needed for this */
    led_update_brightness(led_cdev);

    return sprintf(buf, "%u\n", led_cdev->brightness);
}

static ssize_t brightness_store(struct device *dev,
        struct device_attribute *attr, const char *buf, size_t size)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);
    unsigned long state;
    ssize_t ret;

    mutex_lock(&led_cdev->led_access);

    if (led_sysfs_is_disabled(led_cdev)) {
        ret = -EBUSY;
        goto unlock;
    }

    ret = kstrtoul(buf, 10, &state);
    if (ret)
        goto unlock;

    if (state == LED_OFF)
        led_trigger_remove(led_cdev);
    led_set_brightness(led_cdev, state);

    ret = size;
unlock:
    mutex_unlock(&led_cdev->led_access);
    return ret;
}
static DEVICE_ATTR_RW(brightness);

static ssize_t max_brightness_show(struct device *dev,
        struct device_attribute *attr, char *buf)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);

    return sprintf(buf, "%u\n", led_cdev->max_brightness);
}
static DEVICE_ATTR_RO(max_brightness);

#ifdef CONFIG_LEDS_TRIGGERS
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);

//	属性文件
static struct attribute *led_trigger_attrs[] = {
    &dev_attr_trigger.attr,
    NULL,
};
static const struct attribute_group led_trigger_group = {
    .attrs = led_trigger_attrs,
};
#endif

//	属性文件
static struct attribute *led_class_attrs[] = {
    &dev_attr_brightness.attr,
    &dev_attr_max_brightness.attr,
    NULL,
};

static const struct attribute_group led_group = {
    .attrs = led_class_attrs,
};

static const struct attribute_group *led_groups[] = {
    &led_group,
#ifdef CONFIG_LEDS_TRIGGERS
    &led_trigger_group,
#endif
    NULL,
};

代码介绍:该部分代码主要用于创建LED属性组,并且负责实现用户空间操作的接口。

实现思路

  1. 定义一个led_groups属性组的二维数组,管理该类设备的所有属性
  2. 这个led_groups二维数组,其中又包括两个属性组:led_group、和led_trigger_group,一个用于LED亮度控制,一个用于触发控制。
  3. led_group属性组中又包括多个属性,如:dev_attr_brightness.attrdev_attr_max_brightness.attr,分别表示LED亮度和最大亮度的设置。
  4. led_trigger_group属性组包括一个属性,如:dev_attr_trigger.attr,用于控制触发属性
  5. 定义完属性后,需要提供操作属性的接口,就是上面的led_trigger_showled_trigger_storemax_brightness_showbrightness_showbrightness_store,其中xxx_show表示读属性,xxx_store表示写属性
  6. 至此,所有的属性定义完毕,并且将其读写属性的接口与该属性进行了绑定
  7. 最后,通过在leds_init接口中,调用leds_class->dev_groups = led_groups;,将属性组注册到LED类中进行管理。

阅读代码时,从下网上看,更容易理解!

 

4.3 扩展

可能代码中有些地方对于初学者不太容易理解,如:为什么没有找到brightness_storebrightness_show与属性绑定的地方?为什么函数要定义成这个名字?

解答这个问题,也是涉及到了Linux内核的设计模式,其中充斥着大量的宏定义,主要用作字符串的拼接,最终生成想要的定义!

4.3.1 DEVICE_ATTR_RW分析

static ssize_t brightness_show(struct device *dev,
        struct device_attribute *attr, char *buf)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);

    /* no lock needed for this */
    led_update_brightness(led_cdev);

    return sprintf(buf, "%u\n", led_cdev->brightness);
}

static ssize_t brightness_store(struct device *dev,
        struct device_attribute *attr, const char *buf, size_t size)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);
    unsigned long state;
    ssize_t ret;

    mutex_lock(&led_cdev->led_access);

    if (led_sysfs_is_disabled(led_cdev)) {
        ret = -EBUSY;
        goto unlock;
    }

    ret = kstrtoul(buf, 10, &state);
    if (ret)
        goto unlock;

    if (state == LED_OFF)
        led_trigger_remove(led_cdev);
    led_set_brightness(led_cdev, state);

    ret = size;
unlock:
    mutex_unlock(&led_cdev->led_access);
    return ret;
}
static DEVICE_ATTR_RW(brightness);

代码介绍:上面定义了两个函数(brightness_showbrightness_store)和一个属性名称(brightness),并且通过DEVICE_ATTR_RW宏定义将属性和函数关联起来。

实现思路

我们分析一下DEVICE_ATTR_RW的宏定义

//	static DEVICE_ATTR_RW(brightness);

#define DEVICE_ATTR_RW(_name) \
    struct device_attribute dev_attr_##_name = __ATTR_RW(_name)

//	static struct device_attribute dev_attr_brightness = __ATTR_RW(brightness)

#define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store)

//	static struct device_attribute dev_attr_brightness = __ATTR(brightness, 0644, brightness_show, brightness_store)

#define __ATTR(_name, _mode, _show, _store) {				\
    .attr = {.name = __stringify(_name),				\
         .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },		\
    .show	= _show,						\
    .store	= _store,						\
}

//	static struct device_attribute dev_attr_brightness = {
//	.attr = {
//		.name = __stringify(brightness),
//		.mode = VERIFY_OCTAL_PERMISSIONS(0644),
//	}
//	.show = brightness_show,
//	.store = brightness_store,
//}

上面屏蔽的内容就是static DEVICE_ATTR_RW(brightness)展开的原貌,这样就与上面的两个函数(brightness_showbrightness_store)关联了起来!

 

5、led class的注册注销分析

led-class.c还剩下一部分代码,那就是负责提供注册,注销led设备的相关接口

 

5.1 相关实现

5.1.1 devm_of_led_classdev_register

/**
 * devm_of_led_classdev_register - resource managed led_classdev_register()
 *
 * @parent: parent of LED device
 * @led_cdev: the led_classdev structure for this device.
 */
int devm_of_led_classdev_register(struct device *parent,
                  struct device_node *np,
                  struct led_classdev *led_cdev)
{
    struct led_classdev **dr;
    int rc;

    dr = devres_alloc(devm_led_classdev_release, sizeof(*dr), GFP_KERNEL);
    if (!dr)
        return -ENOMEM;

    rc = of_led_classdev_register(parent, np, led_cdev);		//	注册到子系统
    if (rc) {
        devres_free(dr);
        return rc;
    }

    *dr = led_cdev;
    devres_add(parent, dr);

    return 0;
}
EXPORT_SYMBOL_GPL(devm_of_led_classdev_register);

函数介绍devm_of_led_classdev_registerof_led_classdev_register函数的资源管理版本。即:在of_led_classdev_register之上,进行了资源的管理。

实现思路

  1. 先通过struct led_classdev **dr创建一个新对象,并将其与给定的设备节点关联
  2. 该函数分配了一个devres结构来管理led_classdev对象的生命周期。
  3. 如果注册成功,则led_classdev对象将存储在devres结构中,并与父设备关联。

 

5.1.2 of_led_classdev_register

/**
 * of_led_classdev_register - register a new object of led_classdev class.
 *
 * @parent: parent of LED device
 * @led_cdev: the led_classdev structure for this device.
 * @np: DT node describing this LED
 */
int of_led_classdev_register(struct device *parent, struct device_node *np,
                struct led_classdev *led_cdev)
{
    char name[LED_MAX_NAME_SIZE];
    int ret;

    ret = led_classdev_next_name(led_cdev->name, name, sizeof(name));		//	生成唯一的节点名称
    if (ret < 0)
        return ret;

    mutex_init(&led_cdev->led_access);
    mutex_lock(&led_cdev->led_access);
    led_cdev->dev = device_create_with_groups(leds_class, parent, 0,
                led_cdev, led_cdev->groups, "%s", name);					//	关联属性文件
    if (IS_ERR(led_cdev->dev)) {
        mutex_unlock(&led_cdev->led_access);
        return PTR_ERR(led_cdev->dev);
    }
    led_cdev->dev->of_node = np;

    if (ret)
        dev_warn(parent, "Led %s renamed to %s due to name collision",
                led_cdev->name, dev_name(led_cdev->dev));

    if (led_cdev->flags & LED_BRIGHT_HW_CHANGED) {
        ret = led_add_brightness_hw_changed(led_cdev);
        if (ret) {
            device_unregister(led_cdev->dev);
            mutex_unlock(&led_cdev->led_access);
            return ret;
        }
    }

    led_cdev->work_flags = 0;
#ifdef CONFIG_LEDS_TRIGGERS
    init_rwsem(&led_cdev->trigger_lock);
#endif
#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
    led_cdev->brightness_hw_changed = -1;
#endif
    /* add to the list of leds */
    down_write(&leds_list_lock);
    list_add_tail(&led_cdev->node, &leds_list);
    up_write(&leds_list_lock);

    if (!led_cdev->max_brightness)
        led_cdev->max_brightness = LED_FULL;

    led_update_brightness(led_cdev);

    led_init_core(led_cdev);					//	核心层初始化

#ifdef CONFIG_LEDS_TRIGGERS
    led_trigger_set_default(led_cdev);
#endif

    mutex_unlock(&led_cdev->led_access);

    dev_dbg(parent, "Registered led device: %s\n",
            led_cdev->name);

    return 0;
}

函数介绍of_led_classdev_register注册一个新的led_classdev对象。

led_classdev该结构体在上一篇文章中有介绍到,我们在led-gpio.c中创建并初始化,随后我们会调用注册函数devm_of_led_classdev_register来将创建的led_classdev对象注册到LED子系统中,也就是该函数的作用。

实现思路

  1. 通过led_classdev_next_name来对LED名字添加序号,生成唯一名称
  2. 使用device_create_with_groups接口,将led_classdev对象与leds_class关联,创建一个新的设备
  3. 最后调用led_init_core接口,初始化了 LED 核心并为设备设置了默认触发器。

led_init_core就是在led-core.c中实现的啦,我们下一篇文章分析。

 

5.1.3 led_classdev_next_name

static int led_classdev_next_name(const char *init_name, char *name,
                  size_t len)
{
    unsigned int i = 0;
    int ret = 0;
    struct device *dev;

    strlcpy(name, init_name, len);

    while ((ret < len) &&
           (dev = class_find_device(leds_class, NULL, name, match_name))) {
        put_device(dev);
        ret = snprintf(name, len, "%s_%u", init_name, ++i);
    }

    if (ret >= len)
        return -ENOMEM;

    return i;
}

函数介绍led_classdev_next_name,该函数根据提供的初始名称,生成一个唯一的 LED 设备名称。它通过在初始名称后添加下划线和数字来实现。

实现思路

  1. 调用strlcpy接口,将初始名称拷贝到name 缓冲区中
  2. 调用class_find_device去循环检查leds_class 类中是否已经存在一个具有当前 name 的设备。
  3. 如果存在,则将调用snprintfinit_name 后面添加下划线和数字

 

6、总结

led-class.c该文件的详细分析如上文,我们回顾一下其主要作用:

  1. 创建leds_class,并且初始化相关字段,如:pm电源管理,dev_groups设备属性
  2. 定义suspendresume函数,用于LED类设备的休眠唤醒;定义多个属性组,多个属性,并且实现对应的函数,如:brightness_showbrightness_store等,并将其注册到LED类中,以便LED属性在用户空间的读写
  3. 提供注册注销函数:devm_of_led_classdev_registerdevm_led_classdev_unregister,为了底层将创建的led_classdevleds_class关联,注册进入子系统
     

点赞+关注,永远不迷路

img

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/512325.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mysql出现问题:ERROR 1062 (23000): Duplicate entry ‘‘ for key ‘PRIMARY‘解决方案

回城传送–》《数据库问题解决方案》 ❤️作者主页:小虚竹 ❤️作者简介:大家好,我是小虚竹。Java领域优质创作者🏆,CSDN博客专家🏆,华为云享专家🏆,掘金年度人气作者🏆,阿里云专家博主🏆,51CTO专家博主🏆 ❤️技术活,该赏 ❤️点赞 👍 收藏 ⭐再看,养成…

QT中的模态对话框及非模态对话框

QT中的模态对话框及非模态对话框 [1] QT中的模态对话框及非模态对话框[2] Qt工作笔记-主界面往模式对话框emit信号&#xff0c;有注意的问题正常情况下&#xff1a;不正常情况下&#xff1a;下面给出正常情况下的代码&#xff1a; [1] QT中的模态对话框及非模态对话框 原文链接…

KVM软件安装/Guest OS图形模式安装

KVM软件安装 首先你的Linux操作系统得带有图形化界面 虚拟机开启硬件虚拟化 关闭防火墙和selinux [rootserver-d ~]# systemctl stop firewalld [rootserver-d ~]# systemctl disable firewalld Removed symlink /etc/systemd/system/multi-user.target.wants/firewalld.ser…

RK3568修改调试串口的波特率

概述 使用了临滴 RK3568 开发板,其调试串口的默认波特率是 1500000 &#xff0c;但并不是所有的 USB 转 TTL 都能使用这么高的波特率&#xff0c;所以我们就将波特率修改为 115200 这个比较通用的波特率。 RK3568 调试串口修改波特率的方法 ddr 运行阶段串口波特率的修改 ddr…

linux利用定时任务提权

背景&#xff1a; 运维为了防止数据丢失等&#xff0c;写个定时任务进行数据的打包压缩。由于数据打包压缩命令为tar&#xff0c;tar可以尝试加参数调用其他命令执行。 压缩命令&#xff1a;tar zxf 1.tar.gz /var/www/* 查看定时任务&#xff1a;cat /etc/crontab root权限下…

WordPress入门之WordPress站点基本设置

在Wordpress站点搭建过程中,我们需要快速去熟悉Wordpress,并进行一些简单的基本设置,在开始设置之前,大家可以先熟悉左边的菜单栏的每个选项,了解它们都是做什么的,今天就简单为大家介绍Wordpress入门之Wordpress站点基本设置。 一、设置个人资料 建议大家完善基本信息…

电容笔一定要防误触吗?苹果平板平替电容笔排行

至于用ipad作为学习工具的学生们&#xff0c;更是将它当成了一种必不可少的工具。但是&#xff0c;由于苹果原装电容笔的价格过高&#xff0c;没有人能负担得起。所以&#xff0c;最好的办法就是使用普通的电容笔。我是IPAD的忠实用户&#xff0c;也是数码爱好者&#xff0c;这…

10.BOM浏览器对象模型

BOM 浏览器对象模型 1. BOM 概述 1.1 什么是 BOM BOM&#xff08;Browser Object Model&#xff09;即浏览器对象模型&#xff0c;它提供了独立于内容而与**浏览器窗口进行交互的对象&#xff0c;其核心对象是 window BOM 由一系列相关的对象构成&#xff0c;并且每个对象都…

在Bamboo上怎么使用iOS的单元测试 | 京东云技术团队

作者&#xff1a;京东零售 吴滔 本教程将使用北汽登录模块为例&#xff0c;一步一步和大家一起搭建单元测试用例&#xff0c;并在Bamboo上跑起来&#xff0c;最终测试结果和代码覆盖率会Bamboo上汇总。 模块名称&#xff1a;BQLoginModule,是通过iBiu创建的一个模块工程 一 建…

浅尝Kubernetes

第一节 内容编排与Kubernetes 为什么要用k8s 集群环境容器部署的困境&#xff0c;假设我们有数十台服务器。分别部署Nginx&#xff0c;redis&#xff0c;mysql&#xff0c;业务服务。如何合理的分配这些资源。这里就需要用到容器编排 容器编排 在实际集群环境下&#xff0…

JAVA-抽象类和接口

文章目录 前言 大家好呀,今天给大家带来抽象类和接口的讲解,那么废话不多说,跟着我一起去学习吧! 1.1抽象类的概念 在面向对象的概念中&#xff0c;所有的对象都是通过类来描绘的&#xff0c;但是反过来&#xff0c;并不是所有的类都是用来描绘对象的&#xff0c;如果 一个类…

Java 11新特性:模块化系统和本地变量类型推断

作为Java语言的最新版本&#xff0c;Java 11带来了许多新特性&#xff0c;其中最引人注目的是模块化系统和本地变量类型推断。这两个新特性对Java开发人员来说具有重要意义&#xff0c;因此在本文中&#xff0c;我们将详细探讨这两个新特性及其对Java开发的影响。 章节1&#…

记录一次uniapp实现APP自动升级

描述 app的版本管理和升级&#xff0c;是一个不可或缺的功能&#xff0c;而uniapp则是提供了一整套的流程&#xff0c;由于官方文档过于复杂&#xff0c;而且写的云里雾里的&#xff0c;所以个人记录一次我的操作&#xff0c;直到配置成功。 总体 一共分为2个部分&#xff0…

scanf与printf函数的用法

前言&#xff1a; 学习c语言编程&#xff0c;必不可少的操作就是键盘输入与屏幕输出。今天我想讲讲自己对scanf与printf使用的看法 一、scanf与printf&#xff1a; 1.scanf()函数&#xff1a; int scanf ( const char * format, ... ); 函数的功能&#xff1a;从标准输入中…

契约锁亮相华为2023中国合作伙伴大会,共谱华为云软件生态新篇章

5月8日-9日&#xff0c;以“因聚而生 众志有为”为主题的“华为中国合作伙伴大会2023”在深圳举行。契约锁作为华为云优秀软件伙伴&#xff0c;受邀参会并在“聚数字化先行者&#xff0c;谱软件生态新乐章”分论坛开展“让数字可信&#xff0c;打通数字化最后一公里”数字化实践…

Google Play 政策更新重点回顾 (下) | 2023 年 4 月

Google Play 和您一样&#xff0c;始终坚持为用户提供更优质的产品、更流畅的体验。为持续打造值得信赖的优质应用平台&#xff0c;我们也在不断进行政策更新。在上一篇文章中与您回顾了 2023 年 4 月 Google Play 政策的更新要点&#xff0c;这篇文章我们将为您带来重点政策解…

golang汇编之常量和全局变量(三)

常量 Go汇编语言中常量以$美元符号为前缀。常量的类型有整数常量、浮点数常量、字符常量和字符串常量等几种类型。以下是几种类型常量的例子&#xff1a; $1 // 十进制 $0xf4f8fcff // 十六进制 $1.5 // 浮点数 $a // 字符 $"abcd" …

【加解密篇】Passware Kit Forensic暴力美学-已知部分密码自定义解密详细参数设置

【加解密篇】Passware Kit Forensic暴力美学-已知部分密码自定义解密详细参数设置 都说"自制武器不一定是最强的&#xff0c;但最强的武器一定是自制的"&#xff0c;对于取证工具也是一样&#xff0c;虽然默认配置足够强&#xff0c;但如果我们能根据实时情景自定义…

arduino 导入 Brain 库

一、引言 最近在做一个可以用脑电波控制的arduino小车&#xff0c;需要用到Brain这个库&#xff0c;而且需要自己导入才能使用。之前试了很多方法&#xff0c;导入成功了&#xff0c;过了几个月又忘记怎么导入了&#xff0c;今天想起来记录一下&#xff0c;好记性不如烂笔头。 …

LNK2019 无法解析的外部符号,一种新的思路

LNK2019 无法解析的外部符号&#xff0c;一种新的思路 一般来说&#xff0c;这种问题的原因有如下几点&#xff1a; 函数只有声明没有实现如果是其它库的函数&#xff0c;可能没有链接lib文件如果函数是封装在dll中的&#xff0c;可能没有导出如果是模板函数或模板类&#xf…