Linux I2C(三) i2c bus/adapter/client_device注册

一,i2c总线注册

1,i2c_bus_type

i2c_bus_type注册流程:

//kernel\drivers\i2c\i2c-core-base.c
postcore_initcall(i2c_init);
    retval = bus_register(&i2c_bus_type);

注册之后生成i2c bus目录:

/sys/bus/i2c # ls -l
drwxr-xr-x  2 root root    0 2023-12-18 18:16 devices
drwxr-xr-x 25 root root    0 2023-12-18 18:16 drivers
-rw-r--r--  1 root root 4096 2023-12-18 18:16 drivers_autoprobe
--w-------  1 root root 4096 2023-12-18 18:16 drivers_probe
--w-------  1 root root 4096 2023-12-18 18:16 uevent

具体的bus_register流程参考 “Linux设备模型(九) - bus/device/device_driver/class”。

2,调用bus match/probe 函数的时机

在注册设备的时候,设备会被添加到i2c bus的设备链表,然后遍历该总线上驱动链表上的驱动,调用i2c bus的match函数,如果设备与驱动匹配成功会调用bus的probe。

device:
device_add(struct device *dev)
----bus_add_device(dev); //把设备添加到bus
--------klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
----bus_probe_device(dev);
--------device_initial_probe(dev);
------------__device_attach(dev, true);
----------------ret = bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver);
--------------------__device_attach_driver
------------------------driver_match_device(drv, dev);
----------------------------drv->bus->match ? drv->bus->match(dev, drv) : 1;
------------------------driver_probe_device(drv, dev);
----------------------------really_probe(dev, drv);
--------------------------------//如果bus有probe函数优先执行bus的probe函数,如果没有则执行驱动的probe函数
--------------------------------if (dev->bus->probe) {
------------------------------------ret = dev->bus->probe(dev);
------------------------------------if (ret)
----------------------------------------goto probe_failed;
--------------------------------} else if (drv->probe) {
------------------------------------ret = drv->probe(dev);
------------------------------------if (ret)
----------------------------------------goto probe_failed;
--------------------------------}

在注册驱动的时候,驱动会被添加到i2c bus的驱动链表,然后遍历该总线上设备链表上的所有设备,调用i2c bus的match函数,如果设备与驱动匹配成功,调用总线的probe函数。

driver:
driver_register(struct device_driver *drv)
----driver_find(drv->name, drv->bus);
bus_add_driver(drv);
----klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
----if (drv->bus->p->drivers_autoprobe)
--------driver_attach(drv);
------------bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
----------------__driver_attach
--------------------driver_match_device(drv, dev);
------------------------drv->bus->match ? drv->bus->match(dev, drv) : 1;
--------------------device_driver_attach(drv, dev);
------------------------driver_probe_device(drv, dev);
----------------------------really_probe(dev, drv);

3,i2c bus match函数的实现

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
    struct i2c_client    *client = i2c_verify_client(dev);
    struct i2c_driver    *driver;

    /* Attempt an OF style match */
    if (i2c_of_match_device(drv->of_match_table, client))
        return 1;

    /* Then ACPI style match */
    if (acpi_driver_match_device(dev, drv))
        return 1;

    driver = to_i2c_driver(drv);

    /* Finally an I2C match */
    if (i2c_match_id(driver->id_table, client))
        return 1;

    return 0;
}

i2c_device_match就是i2c_driver与i2c_device匹配的部分,在i2c_device_match函数中,可以看到,match函数并不只是提供一种匹配方式:

i2c_of_match_device,看到of我们就应该马上意识到这是设备树的匹配方式。

acpi_driver_match_device则是acpi的匹配方式,acpi的全称为the Advanced Configuration & Power Interface,高级设置与电源管理。

i2c_match_id(),通过注册i2c_driver时提供的id_table进行匹配,这是设备树机制产生之前的主要配对方式。

3.1 设备树匹配方式

Example:

#define TS_DRIVER_NAME                "gtx8_i2c"

#ifdef CONFIG_OF
static const struct of_device_id i2c_matchs[] = { //这里的compatible会与dts中定义的i2c device的device_node中的compatible属性比较
    {.compatible = "goodix,gt9897",}, //这里如果定义了type会与device_node中的device_type相比较
    {.compatible = "goodix,gt9966",}, //这里如果定义了name会与device_node的name相比较
    {.compatible = "goodix,gt9916",},
    {},
};
MODULE_DEVICE_TABLE(of, i2c_matchs);
#endif

static const struct i2c_device_id i2c_id_table[] = {
    {TS_DRIVER_NAME, 0},
    {},
};
MODULE_DEVICE_TABLE(i2c, i2c_id_table);

static struct i2c_driver goodix_i2c_driver = {
    .driver = {
        .name = TS_DRIVER_NAME,
        //.owner = THIS_MODULE,
        .of_match_table = of_match_ptr(i2c_matchs),
    },
    .probe = goodix_i2c_probe,
    .remove = goodix_i2c_remove,
    .id_table = i2c_id_table,
};

OF match关键代码实现:

i2c_of_match_device(drv->of_match_table, client)
----of_match_device(matches, &client->dev);
--------of_match_node(matches, dev->of_node);
------------match = __of_match_node(matches, node);


static
const struct of_device_id *__of_match_node(const struct of_device_id *matches,
                       const struct device_node *node)
{
    const struct of_device_id *best_match = NULL;
    int score, best_score = 0;

    if (!matches)
        return NULL;

    for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) { //遍历of_device_id数组
        score = __of_device_is_compatible(node, matches->compatible,
                          matches->type, matches->name);
        if (score > best_score) {
            best_match = matches;
            best_score = score; //选择一个得分最高的结果,即最佳匹配
        }
    }

    return best_match; //返回最佳匹配的device_id
}

/**
* __of_device_is_compatible() - Check if the node matches given constraints
* @device: pointer to node
* @compat: required compatible string, NULL or "" for any match
* @type: required device_type value, NULL or "" for any match
* @name: required node name, NULL or "" for any match
*
* Checks if the given @compat, @type and @name strings match the
* properties of the given @device. A constraints can be skipped by
* passing NULL or an empty string as the constraint.
*
* Returns 0 for no match, and a positive integer on match. The return
* value is a relative score with larger values indicating better
* matches. The score is weighted for the most specific compatible value
* to get the highest score. Matching type is next, followed by matching
* name. Practically speaking, this results in the following priority
* order for matches:
*
* 1. specific compatible && type && name
* 2. specific compatible && type
* 3. specific compatible && name
* 4. specific compatible
* 5. general compatible && type && name
* 6. general compatible && type
* 7. general compatible && name
* 8. general compatible
* 9. type && name
* 10. type
* 11. name
*/
static int __of_device_is_compatible(const struct device_node *device,
                     const char *compat, const char *type, const char *name)
{
    struct property *prop;
    const char *cp;
    int index = 0, score = 0;

    /* Compatible match has highest priority */
    if (compat && compat[0]) {
        prop = __of_find_property(device, "compatible", NULL); //获取compatible property
        for (cp = of_prop_next_string(prop, NULL); cp;
             cp = of_prop_next_string(prop, cp), index++) { //循环比较compatible中的string和matches->compatible
            if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
                score = INT_MAX/2 - (index << 2); //找到匹配的sting,index越大,匹配分值越低
                break;
            }
        }
        if (!score)
            return 0;
    }

    /* Matching type is better than matching name */
    if (type && type[0]) {
        if (!__of_node_is_type(device, type)) //__of_get_property(np, "device_type", NULL);
            return 0;
        score += 2;
    }

    /* Matching name is a bit better than not */
    if (name && name[0]) {
        if (!of_node_name_eq(device, name)) //node_name = kbasename(np->full_name);
            return 0;
        score++;
    }

    return score;
}
3.2 acpi匹配方式较为少用且较为复杂,这里暂且不做详细讨论
3.3 id_table匹配方式
const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
                        const struct i2c_client *client)
{
    if (!(id && client))
        return NULL;

    while (id->name[0]) {
        if (strcmp(client->name, id->name) == 0)
            return id;
        id++;
    }
    return NULL;
}
EXPORT_SYMBOL_GPL(i2c_match_id);

4,i2c bus probe函数的实现

static int i2c_device_probe(struct device *dev)
{
    struct i2c_client    *client = i2c_verify_client(dev);
    struct i2c_driver    *driver;
    int status;
    ... ...
    driver = to_i2c_driver(dev->driver);
    
    /*
     * When there are no more users of probe(),
     * rename probe_new to probe.
     */
    if (driver->probe_new)
        status = driver->probe_new(client);
    else if (driver->probe)
        status = driver->probe(client,
                       i2c_match_id(driver->id_table, client)); //i2c_driver的probe函数调用时机
    else
        status = -EINVAL;
    ... ...
}

二,I2c适配器注册

1,i2c_add_adapter注册流程

module_init(i2c_dev_init); // kernel\drivers\i2c\busses\i2c-msm-geni.c
----i2c_dev_init(void)
--------platform_driver_register(&geni_i2c_driver);
------------geni_i2c_probe(struct platform_device *pdev)
----------------gi2c->adap.algo = &geni_i2c_algo;
----------------gi2c->adap.dev.parent = gi2c->dev; //controller -> adapter
----------------gi2c->adap.dev.of_node = pdev->dev.of_node;
----------------i2c_add_adapter(&gi2c->adap);
--------------------adapter->nr = id;
--------------------i2c_register_adapter(adapter);
----------------------------dev_set_name(&adap->dev, "i2c-%d", adap->nr);
----------------------------adap->dev.bus = &i2c_bus_type;
----------------------------adap->dev.type = &i2c_adapter_type; //i2c_adapter_type
----------------------------res = device_register(&adap->dev); //register adapter


----------------------------of_i2c_register_devices(adap); // kernel\drivers\i2c\i2c-core-of.c /* 对应下文 3.1.1 Declare the I2C devices via devicetree */
--------------------------------for_each_available_child_of_node(bus, node)
------------------------------------client = of_i2c_register_device(adap, node);
----------------------------------------client = i2c_new_client_device(adap, &info);
--------------------------------------------client->addr = info->addr;
--------------------------------------------client->dev.parent = &client->adapter->dev;
--------------------------------------------client->dev.bus = &i2c_bus_type;
--------------------------------------------client->dev.type = &i2c_client_type; //i2c_client_type
--------------------------------------------status = device_register(&client->dev); //register i2c client


----------------------------i2c_scan_static_board_info(adap); /* 对应下文 3.1.3 Declare the I2C devices in board files */
--------------------------------list_for_each_entry(devinfo, &__i2c_board_list, list)
------------------------------------i2c_new_client_device(adapter, &devinfo->board_info)
----------------------------------------client->addr = info->addr;
--------------------------------------------client->dev.parent = &client->adapter->dev;
--------------------------------------------client->dev.bus = &i2c_bus_type;
--------------------------------------------client->dev.type = &i2c_client_type;
--------------------------------------------status = device_register(&client->dev); //register i2c client


----------------------------bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
--------------------------------__process_new_adapter(struct device_driver *d, void *data)
------------------------------------i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap)
----------------------------------------i2c_detect(adap, driver); // 对应下文 3.3 Method 3: Probe an I2C bus for certain devices
--------------------------------------------i2c_detect_address(temp_client, driver);
------------------------------------------------driver->detect(temp_client, &info);
------------------------------------------------i2c_new_client_device(adapter, &info);
----------------------------------------------------client->addr = info->addr;
----------------------------------------------------client->dev.parent = &client->adapter->dev;
----------------------------------------------------client->dev.bus = &i2c_bus_type;
----------------------------------------------------client->dev.type = &i2c_client_type;
----------------------------------------------------status = device_register(&client->dev); //register i2c client
------------------------------------------------list_add_tail(&client->detected, &driver->clients);

在i2c adapter device注册之后,还有几种注册i2c slave device的方式:

  • 扫描挂接在该adapter device node下的slave device

  • 扫描在board files中定义的i2c slave device

  • 通过在i2c driver中实现的探测函数和slave device的地址列表,探测到存在的slave device并注册

后续会详细介绍Linux内核中实例化i2c设备的几种方法。

2,qcom i2c controller for example (qcom i2c bus driver)

dts:
/* TUI over I2C */
qupv3_se0_i2c: i2c@980000 {
    compatible = "qcom,i2c-geni";                    
    reg = <0x980000 0x4000>;
    #address-cells = <1>;
    #size-cells = <0>;
    interrupts = <GIC_SPI 601 IRQ_TYPE_LEVEL_HIGH>;
    clock-names = "se-clk", "m-ahb", "s-ahb";
    clocks = <&gcc GCC_QUPV3_WRAP0_S0_CLK>,
        <&gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
        <&gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&qupv3_se0_i2c_active>;
    pinctrl-1 = <&qupv3_se0_i2c_sleep>;
    dmas = <&gpi_dma0 0 0 3 64 2>,
        <&gpi_dma0 1 0 3 64 2>;
    dma-names = "tx", "rx";
    qcom,wrapper-core = <&qupv3_0>;
    status = "disabled";
};
controller driver:
在platform driver框架下的实现

//kernel/drivers/i2c/busses/i2c-msm-geni.c
#define I2C_FUNC_SMBUS_EMUL        (I2C_FUNC_SMBUS_QUICK | \
                     I2C_FUNC_SMBUS_BYTE | \
                     I2C_FUNC_SMBUS_BYTE_DATA | \
                     I2C_FUNC_SMBUS_WORD_DATA | \
                     I2C_FUNC_SMBUS_PROC_CALL | \
                     I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
                     I2C_FUNC_SMBUS_I2C_BLOCK | \
                     I2C_FUNC_SMBUS_PEC)


static u32 geni_i2c_func(struct i2c_adapter *adap)
{
    return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
}

//传输函数的实现
static int geni_i2c_xfer(struct i2c_adapter *adap,
             struct i2c_msg msgs[],
             int num)
{
}

static const struct i2c_algorithm geni_i2c_algo = {
    .master_xfer    = geni_i2c_xfer,
    .functionality    = geni_i2c_func,
};

static int geni_i2c_probe(struct platform_device *pdev)
{
    struct geni_i2c_dev *gi2c;
    struct resource *res;

    gi2c->dev = &pdev->dev;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res)
        return -EINVAL;

    gi2c->base = devm_ioremap_resource(gi2c->dev, res);
    if (IS_ERR(gi2c->base))
        return PTR_ERR(gi2c->base);

    if (!gi2c->is_le_vm) {
        gi2c->irq = platform_get_irq(pdev, 0);
        if (gi2c->irq < 0)
            return gi2c->irq;

        irq_set_status_flags(gi2c->irq, IRQ_NOAUTOEN);
        ret = devm_request_irq(gi2c->dev, gi2c->irq, geni_i2c_irq,
                    0, "i2c_geni", gi2c);
        if (ret) {
            dev_err(gi2c->dev, "Request_irq failed:%d: err:%d\n",
                       gi2c->irq, ret);
            return ret;
        }
    }

    gi2c->adap.algo = &geni_i2c_algo;
    init_completion(&gi2c->xfer);
    platform_set_drvdata(pdev, gi2c);
    i2c_set_adapdata(&gi2c->adap, gi2c);
    gi2c->adap.dev.parent = gi2c->dev;
    gi2c->adap.dev.of_node = pdev->dev.of_node;

    strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));

    ret = i2c_add_adapter(&gi2c->adap);
    if (ret) {
        dev_err(gi2c->dev, "Add adapter failed, ret=%d\n", ret);
        return ret;
    }
    ... ...
}

static const struct of_device_id geni_i2c_dt_match[] = {
    { .compatible = "qcom,i2c-geni" },
    {}
};
MODULE_DEVICE_TABLE(of, geni_i2c_dt_match);

static struct platform_driver geni_i2c_driver = {
    .probe  = geni_i2c_probe,
    .remove = geni_i2c_remove,
    .driver = {
        .name = "i2c_geni",
        .pm = &geni_i2c_pm_ops,
        .of_match_table = geni_i2c_dt_match,
    },
};

static int __init i2c_dev_init(void)
{
    return platform_driver_register(&geni_i2c_driver);
}

module_init(i2c_dev_init);

不同vendor IC的i2c controller driver框架实现大同小异,只是传输函数的实现上不同,需要读写的寄存器地址也不一样。

3,传输函数的实现 geni_i2c_xfer

三,I2C从设备注册

1,I2C从设备注册API - i2c_new_client_device

struct i2c_board_info - 描述的是具体的I2C从设备

/**
* struct i2c_board_info - template for device creation
* @type: chip type, to initialize i2c_client.name
* @flags: to initialize i2c_client.flags
* @addr: stored in i2c_client.addr
* @dev_name: Overrides the default <busnr>-<addr> dev_name if set
* @platform_data: stored in i2c_client.dev.platform_data
* @of_node: pointer to OpenFirmware device node
* @fwnode: device node supplied by the platform firmware
* @properties: additional device properties for the device
* @resources: resources associated with the device
* @num_resources: number of resources in the @resources array
* @irq: stored in i2c_client.irq
*
* I2C doesn't actually support hardware probing, although controllers and
* devices may be able to use I2C_SMBUS_QUICK to tell whether or not there's
* a device at a given address.  Drivers commonly need more information than
* that, such as chip type, configuration, associated IRQ, and so on.
*
* i2c_board_info is used to build tables of information listing I2C devices
* that are present.  This information is used to grow the driver model tree.
* For mainboards this is done statically using i2c_register_board_info();
* bus numbers identify adapters that aren't yet available.  For add-on boards,
* i2c_new_client_device() does this dynamically with the adapter already known.
*/
struct i2c_board_info {
    char        type[I2C_NAME_SIZE];
    unsigned short    flags;
    unsigned short    addr;
    const char    *dev_name;
    void        *platform_data;
    struct device_node *of_node;
    struct fwnode_handle *fwnode;
    const struct property_entry *properties;
    const struct resource *resources;
    unsigned int    num_resources;
    int        irq;
};

struct i2c_devinfo - struct i2c_devinfo 和 struct i2c_board_info 类似,同样用于描述具体的 I2C 从设备

struct i2c_devinfo {
    struct list_head    list;
    int            busnum;
    struct i2c_board_info    board_info;
};

i2c_new_client_device函数的实现

struct bus_type i2c_bus_type = {
    .name        = "i2c",
    .match        = i2c_device_match,
    .probe        = i2c_device_probe,
    .remove        = i2c_device_remove,
    .shutdown    = i2c_device_shutdown,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);

struct device_type i2c_client_type= {
    .groups        = i2c_dev_groups,
    .uevent        = i2c_device_uevent,
    .release    = i2c_client_dev_release,
};
EXPORT_SYMBOL_GPL(i2c_client_type);

static void i2c_dev_set_name(struct i2c_adapter *adap,
                 struct i2c_client *client,
                 struct i2c_board_info const *info)
{
    struct acpi_device *adev = ACPI_COMPANION(&client->dev);

    if (info && info->dev_name) {
        dev_set_name(&client->dev, "i2c-%s", info->dev_name);
        return;
    }

    if (adev) {
        dev_set_name(&client->dev, "i2c-%s", acpi_dev_name(adev));
        return;
    }

    dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
             i2c_encode_flags_to_addr(client)); //client device example:/sys/devices/platform/soc/a94000.i2c/i2c-1/1-0052
}

/**
* device_add_properties - Add a collection of properties to a device object.
* @dev: Device to add properties to.
* @properties: Collection of properties to add.
*
* Associate a collection of device properties represented by @properties with
* @dev. The function takes a copy of @properties.
*
* WARNING: The callers should not use this function if it is known that there
* is no real firmware node associated with @dev! In that case the callers
* should create a software node and assign it to @dev directly.
*/
int device_add_properties(struct device *dev,
              const struct property_entry *properties)
{
    struct fwnode_handle *fwnode;

    fwnode = fwnode_create_software_node(properties, NULL);
    if (IS_ERR(fwnode))
        return PTR_ERR(fwnode);

    set_secondary_fwnode(dev, fwnode);
    return 0;
}
EXPORT_SYMBOL_GPL(device_add_properties);

/**
* i2c_new_client_device - instantiate an i2c device
* @adap: the adapter managing the device
* @info: describes one I2C device; bus_num is ignored
* Context: can sleep
*
* Create an i2c device. Binding is handled through driver model
* probe()/remove() methods.  A driver may be bound to this device when we
* return from this function, or any later moment (e.g. maybe hotplugging will
* load the driver module).  This call is not appropriate for use by mainboard
* initialization logic, which usually runs during an arch_initcall() long
* before any i2c_adapter could exist.
*
* This returns the new i2c client, which may be saved for later use with
* i2c_unregister_device(); or an ERR_PTR to describe the error.
*/
struct i2c_client *
i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
    struct i2c_client    *client;
    int            status;
    ... ...
    client->dev.parent = &client->adapter->dev; //parent
    client->dev.bus = &i2c_bus_type; //bus_type
    client->dev.type = &i2c_client_type; //device_type
    client->dev.of_node = of_node_get(info->of_node);
    client->dev.fwnode = info->fwnode;

    i2c_dev_set_name(adap, client, info);

    if (info->properties) {
        //把board info中定义的properties拷贝到device的fwnode中 (dev->fwnode->secondary = fwnode; 或者 dev->fwnode = fwnode;)
        status = device_add_properties(&client->dev, info->properties);
        if (status) {
            dev_err(&adap->dev,
                "Failed to add properties to client %s: %d\n",
                client->name, status);
            goto out_err_put_of_node;
        }
    }

    status = device_register(&client->dev);
    if (status)
        goto out_free_props;
    ... ...
}

2,struct fwnode_handle 与 struct device_node的区别

struct fwnode_handle结构体的定义:

struct fwnode_handle {
    struct fwnode_handle *secondary;
    const struct fwnode_operations *ops;
    struct device *dev;
    struct list_head suppliers;
    struct list_head consumers;
    u8 flags;
    ANDROID_KABI_RESERVE(1);
};

struct device_node结构体的定义:

struct device_node {
    const char *name;
    phandle phandle;
    const char *full_name;
    struct fwnode_handle fwnode;

    struct    property *properties;
    struct    property *deadprops;    /* removed properties */
    struct    device_node *parent;
    struct    device_node *child;
    struct    device_node *sibling;
#if defined(CONFIG_OF_KOBJ)
    struct    kobject kobj;
#endif
    unsigned long _flags;
    void    *data;
#if defined(CONFIG_SPARC)
    unsigned int unique_id;
    struct of_irq_controller *irq_trans;
#endif
};

使用fwnode API读取u32类型的property的一个API - fwnode_property_read_u32的实现:

fwnode_property_read_u32(const struct fwnode_handle *fwnode, const char *propname, u32 *val)
----fwnode_property_read_u32_array(fwnode, propname, val, 1);
--------fwnode_property_read_int_array(fwnode, propname, sizeof(u32), val, nval);
------------fwnode_call_int_op(fwnode, property_read_int_array, propname, elem_size, val, nval);
------------fwnode_call_int_op(fwnode->secondary, property_read_int_array, propname, elem_size, val, nval);
----------------of_fwnode_property_read_int_array


//kernel\msm_kernel\drivers\of\property.c
const struct fwnode_operations of_fwnode_ops = {
    .get = of_fwnode_get,
    .put = of_fwnode_put,
    .device_is_available = of_fwnode_device_is_available,
    .device_get_match_data = of_fwnode_device_get_match_data,
    .property_present = of_fwnode_property_present,
    .property_read_int_array = of_fwnode_property_read_int_array,
    .property_read_string_array = of_fwnode_property_read_string_array,
    .get_name = of_fwnode_get_name,
    .get_name_prefix = of_fwnode_get_name_prefix,
    .get_parent = of_fwnode_get_parent,
    .get_next_child_node = of_fwnode_get_next_child_node,
    .get_named_child_node = of_fwnode_get_named_child_node,
    .get_reference_args = of_fwnode_get_reference_args,
    .graph_get_next_endpoint = of_fwnode_graph_get_next_endpoint,
    .graph_get_remote_endpoint = of_fwnode_graph_get_remote_endpoint,
    .graph_get_port_parent = of_fwnode_graph_get_port_parent,
    .graph_parse_endpoint = of_fwnode_graph_parse_endpoint,
    .add_links = of_fwnode_add_links,
};
EXPORT_SYMBOL_GPL(of_fwnode_ops);


static int of_fwnode_property_read_int_array(const struct fwnode_handle *fwnode,
                         const char *propname,
                         unsigned int elem_size, void *val,
                         size_t nval)
{
    const struct device_node *node = to_of_node(fwnode);

    if (!val)
        return of_property_count_elems_of_size(node, propname,
                               elem_size);

    switch (elem_size) {
    case sizeof(u8):
        return of_property_read_u8_array(node, propname, val, nval);
    case sizeof(u16):
        return of_property_read_u16_array(node, propname, val, nval);
    case sizeof(u32):
        return of_property_read_u32_array(node, propname, val, nval);
    case sizeof(u64):
        return of_property_read_u64_array(node, propname, val, nval);
    }

    return -ENXIO;
}

fwnode_handle 与  device_node的互相转换:

static inline struct fwnode_handle *of_node_to_fwnode(struct device_node *node)
{
    return node ? &node->fwnode : NULL;
}


#define to_of_node(__fwnode)                        \
    ({                                \
        typeof(__fwnode) __to_of_node_fwnode = (__fwnode);    \
                                    \
        is_of_node(__to_of_node_fwnode) ?            \
            container_of(__to_of_node_fwnode,        \
                     struct device_node, fwnode) :    \
            NULL;                        \
    })

fwnode就是of_node,所有的fwnode_函数都是二次封装的of_函数。

所以在驱动中,有的人喜欢有fwnode对设备树进行解析,有的人喜欢用of,功能都是一样的,没必要纠结。

3,Linux内核中实例化i2c设备的几种方法

对于i2c节点,显然,它会转换成一个platform_device,内核中应该会有一个与它匹配的platform_driver,配对成功之后调用这个platform_driver的probe函数。

但是,对于i2c节点的子节点,它并不会转换成一个platform_device,而是交给i2c节点转换成的platform_device对应的platform_driver的probe函数来处理。

这是因为,i2c节点的子节点可能转换成其他结构体更合适,而不是platform_device结构体。

事实上,i2c节点的子节点会被probe函数处理,转换成一个 i2c_client,之后根据这个 i2c_client 找到对应的驱动程序。

参考内核文档的描述 Documentation/i2c/instantiating-devices,分析Linux内核中实例化i2c设备的几种方法。

3.1 Method 1: Declare the I2C devices statically

This method is appropriate when the I2C bus is a system bus as is the case

for many embedded systems. On such systems, each I2C bus has a number which

is known in advance. It is thus possible to pre-declare the I2C devices

which live on this bus.

This information is provided to the kernel in a different way on different

architectures: device tree, ACPI or board files.

When the I2C bus in question is registered, the I2C devices will be

instantiated automatically by i2c-core. The devices will be automatically

unbound and destroyed when the I2C bus they sit on goes away (if ever).

3.1.1 Declare the I2C devices via devicetree

On platforms using devicetree, the declaration of I2C devices is done in

subnodes of the master controller.

Example::

dts : kernel/arch/arm/boot/dts/imx6dl-riotboard.dts

&i2c1 {
    clock-frequency = <100000>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_i2c1>;
    status = "okay";

    codec: sgtl5000@a {
        compatible = "fsl,sgtl5000";
        reg = <0x0a>;
        clocks = <&clks IMX6QDL_CLK_CKO>;
        VDDA-supply = <&reg_2p5v>;
        VDDIO-supply = <&reg_3p3v>;
    };

    pmic: pf0100@8 {
        compatible = "fsl,pfuze100";
        reg = <0x08>;
        interrupt-parent = <&gpio5>;
        interrupts = <16 8>;
        fsl,pmic-stby-poweroff;

        regulators {
            reg_vddcore: sw1ab {                /* VDDARM_IN */
                regulator-min-microvolt = <300000>;
                regulator-max-microvolt = <1875000>;
                regulator-always-on;
            };
        };
    };
};

I2C framework core会在每一个I2C adapter注册时,为它下面所有的slave device创建struct i2c_client结构,并匹配对应的struct i2c_driver变量,调用driver的probe接口。

dts里配置的i2c slave device实例化成i2c_client的流程: 

i2c_register_adapter(adapter);
--------dev_set_name(&adap->dev, "i2c-%d", adap->nr);
--------adap->dev.bus = &i2c_bus_type;
--------adap->dev.type = &i2c_adapter_type;
--------res = device_register(&adap->dev); //register adapter


--------of_i2c_register_devices(adap); // kernel\msm_kernel\drivers\i2c\i2c-core-of.c
------------for_each_available_child_of_node(bus, node)
----------------client = of_i2c_register_device(adap, node);
--------------------of_i2c_get_board_info(&adap->dev, node, &info);
------------------------info->addr = addr;
------------------------info->of_node = node;
------------------------info->fwnode = of_fwnode_handle(node);
--------------------client = i2c_new_client_device(adap, &info);
------------------------client->addr = info->addr;
------------------------client->init_irq = i2c_dev_irq_from_resources(info->resources, info->num_resources);
------------------------client->dev.parent = &client->adapter->dev;
------------------------client->dev.bus = &i2c_bus_type;
------------------------client->dev.type = &i2c_client_type;
------------------------client->dev.of_node = of_node_get(info->of_node);
------------------------client->dev.fwnode = info->fwnode;
------------------------status = device_register(&client->dev); //register i2c client
3.1.2 Declare the I2C devices via ACPI

3.1.3 Declare the I2C devices in board files

In many embedded architectures, devicetree has replaced the old hardware

description based on board files, but the latter are still used in old

code. Instantiating I2C devices via board files is done with an array of

struct i2c_board_info which is registered by calling

i2c_register_board_info().

(1) Example

D:\kernel\msm_kernel\arch\arm\mach-pxa\stargate2.c

static struct i2c_board_info __initdata stargate2_i2c_board_info[] = {
    /* Techically this a pca9500 - but it's compatible with the 8574
     * for gpio expansion and the 24c02 for eeprom access.
     */
    {
        .type = "pcf8574",
        .addr =  0x27,
        .platform_data = &platform_data_pcf857x,
    }, {
        .type = "24c02",
        .addr = 0x57,
        .properties = pca9500_eeprom_properties,
    }, {
        .type = "max1238",
        .addr = 0x35,
    }, { /* ITS400 Sensor board only */
        .type = "max1363",
        .addr = 0x34,
        /* Through a nand gate - Also beware, on V2 sensor board the
         * pull up resistors are missing.
         */
        .irq = PXA_GPIO_TO_IRQ(99),
    }, { /* ITS400 Sensor board only */
        .type = "tsl2561",
        .addr = 0x49,
        /* Through a nand gate - Also beware, on V2 sensor board the
         * pull up resistors are missing.
         */
        .irq = PXA_GPIO_TO_IRQ(99),
    }, { /* ITS400 Sensor board only */
        .type = "tmp175",
        .addr = 0x4A,
        .irq = PXA_GPIO_TO_IRQ(96),
    },
};

static void __init stargate2_init(void)
{
    ... ...
    i2c_register_board_info(0, ARRAY_AND_SIZE(stargate2_i2c_board_info));
    ... ...
}

The above code declares 6 devices on I2C bus 0, including their respective

addresses and custom data needed by their drivers.

(2) custom data - struct property_entry

static const struct property_entry pca9500_eeprom_properties[] = {
    PROPERTY_ENTRY_U32("pagesize", 4),
    { }
};

//定义i2c_board_info中的property的一些宏
#define PROPERTY_ENTRY_U8(_name_, _val_)                \
    __PROPERTY_ENTRY_ELEMENT(_name_, u8_data, U8, _val_)
#define PROPERTY_ENTRY_U16(_name_, _val_)                \
    __PROPERTY_ENTRY_ELEMENT(_name_, u16_data, U16, _val_)
#define PROPERTY_ENTRY_U32(_name_, _val_)                \
    __PROPERTY_ENTRY_ELEMENT(_name_, u32_data, U32, _val_)
#define PROPERTY_ENTRY_U64(_name_, _val_)                \
    __PROPERTY_ENTRY_ELEMENT(_name_, u64_data, U64, _val_)
#define PROPERTY_ENTRY_STRING(_name_, _val_)                \
    __PROPERTY_ENTRY_ELEMENT(_name_, str, STRING, _val_)

#define PROPERTY_ENTRY_BOOL(_name_)        \
(struct property_entry) {            \
    .name = _name_,                \
    .is_inline = true,            \
}

(3) 注册i2c board info

i2c_register_board_info - statically declare I2C devices,source code path kernel\msm_kernel\drivers\i2c\i2c-boardinfo.c

This should be done in board-specific init code

near arch_initcall() time, or equivalent, before any I2C adapter driver is registered.

The I2C devices will be created later, after the adapter for the relevant bus has been registered.  After that moment, standard driver model tools are used to bind "new style" I2C drivers to the devices.

extern struct list_head    __i2c_board_list;

i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
----struct i2c_devinfo    *devinfo;
----devinfo->board_info = *info;
----if (info->properties)
--------devinfo->board_info.properties = property_entries_dup(info->properties);
------------property_entry_copy_data(&p[i], &properties[i]);
----list_add_tail(&devinfo->list, &__i2c_board_list);

(4) 根据i2c board info创建i2c client device

i2c_register_adapter(struct i2c_adapter *adap)
----res = device_register(&adap->dev); //register adapter


----i2c_scan_static_board_info(adap);
--------list_for_each_entry(devinfo, &__i2c_board_list, list)
------------i2c_new_client_device(adapter, &devinfo->board_info)
----------------client->addr = info->addr;
----------------client->dev.parent = &client->adapter->dev;
----------------client->dev.bus = &i2c_bus_type;
----------------client->dev.type = &i2c_client_type;
----------------status = device_register(&client->dev); //register i2c client
3.2 Method 2: Instantiate the devices explicitly
3.2.1 i2c_new_client_device

This method is appropriate when a larger device uses an I2C bus for

internal communication. A typical case is TV adapters.

You won't know the number of the I2C

bus in advance, so the method 1 described above can't be used. Instead,

you can instantiate your I2C devices explicitly. This is done by filling

a struct i2c_board_info and calling i2c_new_client_device().

Example:

kernel\drivers\net\ethernet\sfc\falcon\falcon_boards.c

static const struct i2c_board_info sfe4001_hwmon_info = {
    I2C_BOARD_INFO("max6647", 0x4e),
};

static int sfe4001_init(struct ef4_nic *efx)
{
    struct falcon_board *board = falcon_board(efx);
    int rc;
    ... ...
        board->hwmon_client =
        i2c_new_client_device(&board->i2c_adap, &sfe4001_hwmon_info);
    ... ...
}
3.2.2 i2c_new_scanned_device

A variant of this is when you don't know for sure if an I2C device is

present or not (for example for an optional feature which is not present

on cheap variants of a board but you have no way to tell them apart), or

it may have different addresses from one board to the next (manufacturer

changing its design without notice). In this case, you can call

i2c_new_scanned_device() instead of i2c_new_client_device().

Example:

kernel\drivers\i2c\busses\i2c-icy.c

static unsigned short const icy_ltc2990_addresses[] = {
    0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END
};

static int icy_probe(struct zorro_dev *z,
             const struct zorro_device_id *ent)
{
    struct icy_i2c *i2c;
    struct i2c_board_info ltc2990_info = {
        .type        = "ltc2990",
    };

    if (i2c_pcf_add_bus(&i2c->adapter)) {
        dev_err(&z->dev, "i2c_pcf_add_bus() failed\n");
        return -ENXIO;
    }
    ... ...
    i2c->ltc2990_client =
        i2c_new_scanned_device(&i2c->adapter,
                       &ltc2990_info,
                       icy_ltc2990_addresses,
                       NULL);
    ... ...
}

It first tries at address 0x4c, if nothing

is found there it tries address 0x4d, and if still nothing is found, it

simply gives up.

static void icy_remove(struct zorro_dev *z)
{
    struct icy_i2c *i2c = dev_get_drvdata(&z->dev);

    i2c_unregister_device(i2c->ltc2990_client);
    fwnode_remove_software_node(i2c->ltc2990_fwnode);

    i2c_del_adapter(&i2c->adapter);
}

The driver which instantiated the I2C device is responsible for destroying

it on cleanup. This is done by calling i2c_unregister_device() on the

pointer that was earlier returned by i2c_new_client_device() or

i2c_new_scanned_device().

3.3 Method 3: Probe an I2C bus for certain devices

In that case, I2C devices are neither declared nor instantiated

explicitly. Instead, i2c-core will probe for such devices as soon as their

drivers are loaded, and if any is found, an I2C device will be

instantiated automatically. In order to prevent any misbehavior of this

mechanism, the following restrictions apply:

  • The I2C device driver must implement the detect() method, which

  identifies a supported device by reading from arbitrary registers.

  • Only buses which are likely to have a supported device and agree to be

  probed, will be probed. For example this avoids probing for hardware

  monitoring chips on a TV adapter.

(1) Example

kernel\msm_kernel\drivers\leds

/* Return 0 if detection is successful, -ENODEV otherwise */
static int blinkm_detect(struct i2c_client *client, struct i2c_board_info *info)
{
    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
                     | I2C_FUNC_SMBUS_WORD_DATA
                     | I2C_FUNC_SMBUS_WRITE_BYTE))
        return -ENODEV;

    ... ....
    /* Step 1: Read BlinkM address back  -  cmd_char 'a' */
    ret = blinkm_write(client, BLM_GET_ADDR, NULL);
    if (ret < 0)
        return ret;
    usleep_range(20000, 30000);    /* allow a small delay */
    ret = blinkm_read(client, BLM_GET_ADDR, tmpargs);
    if (ret < 0)
        return ret;

    if (tmpargs[0] != 0x09) {
        dev_err(&client->dev, "enodev DEV ADDR = 0x%02X\n", tmpargs[0]);
        return -ENODEV;
    }
    ... ....
    return 0;
}

/* Addresses to scan - BlinkM is on 0x09 by default*/
static const unsigned short normal_i2c[] = { 0x09, I2C_CLIENT_END };

  /* This is the driver that will be inserted */
static struct i2c_driver blinkm_driver = {
    .class = I2C_CLASS_HWMON, //#define I2C_CLASS_HWMON        (1<<0)    /* lm_sensors, ... */
    .driver = {
           .name = "blinkm",
           },
    .probe = blinkm_probe,
    .remove = blinkm_remove,
    .id_table = blinkm_id,
    .detect = blinkm_detect,
    .address_list = normal_i2c,
};

module_i2c_driver(blinkm_driver);

Once again, method 3 should be avoided wherever possible. Explicit device

instantiation (methods 1 and 2) is much preferred for it is safer and

faster.

(2) An I2C device will be instantiated automatically:注册i2c adapter的时候会探测adapter上支持的i2c device并创建i2c_client

i2c_register_adapter(adapter);
--------dev_set_name(&adap->dev, "i2c-%d", adap->nr);
--------adap->dev.bus = &i2c_bus_type;
--------adap->dev.type = &i2c_adapter_type;
--------res = device_register(&adap->dev); //register adapter


--------bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
------------__process_new_adapter(struct device_driver *d, void *data)
----------------i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap)
--------------------i2c_detect(adap, driver); // 8.3 Method 3: Probe an I2C bus for certain devices
------------------------i2c_detect_address(temp_client, driver);
----------------------------driver->detect(temp_client, &info);
----------------------------i2c_new_client_device(adapter, &info);
--------------------------------client->addr = info->addr;
--------------------------------client->dev.parent = &client->adapter->dev;
--------------------------------client->dev.bus = &i2c_bus_type;
--------------------------------client->dev.type = &i2c_client_type;
--------------------------------status = device_register(&client->dev); //register i2c client
----------------------------list_add_tail(&client->detected, &driver->clients);

(3) 注册i2c device driver的时候也会探测adapter上支持的i2c device并创建i2c_client

i2c_add_driver(driver)
----i2c_register_driver(THIS_MODULE, driver)
--------driver_register(&driver->driver);


--------i2c_for_each_dev(driver, __process_new_driver);
------------__process_new_driver(struct device *dev, void *data)
----------------i2c_do_add_adapter(struct i2c_driver *driver, struct i2c_adapter *adap)
--------------------i2c_detect(adap, driver); // 8.3 Method 3: Probe an I2C bus for certain devices
------------------------i2c_detect_address(temp_client, driver);
----------------------------driver->detect(temp_client, &info); // Detect supported devices on that bus, and instantiate them
----------------------------i2c_new_client_device(adapter, &info);
--------------------------------client->addr = info->addr;
--------------------------------client->dev.parent = &client->adapter->dev;
--------------------------------client->dev.bus = &i2c_bus_type;
--------------------------------client->dev.type = &i2c_client_type;
--------------------------------status = device_register(&client->dev); //register i2c client
----------------------------list_add_tail(&client->detected, &driver->clients);
3.4 Method 4: Instantiate from user-space

In general, the kernel should know which I2C devices are connected and

what addresses they live at. However, in certain cases, it does not, so a

sysfs interface was added to let the user provide the information.

This interface is made of 2 attribute files which are created in every I2C bus

directory: ``new_device`` and ``delete_device``. Both files are write

only and you must write the right parameters to them in order to properly

instantiate, respectively delete, an I2C device.

File ``new_device`` takes 2 parameters: the name of the I2C device (a

string) and the address of the I2C device (a number, typically expressed

in hexadecimal starting with 0x, but can also be expressed in decimal.)

File ``delete_device`` takes a single parameter: the address of the I2C

device. As no two devices can live at the same address on a given I2C

segment, the address is sufficient to uniquely identify the device to be

deleted.

Example::

  # echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

windows/linux 安装php的 sql server 扩展

Windowsphpstudyphp7.1 下载&#xff1a;ODBC、下载php 的sql server 扩展 路径&#xff1a;下载地址 版本&#xff1a;我的是7.1 对应的ODBC 是13&#xff0c;php 的sql server 扩展为4.3 安装&#xff1a;msodbcsql 直接安装、sqlsrv43 安装完把 扩展复制到php71 的扩展文…

Python脚本抢票【笔记】

Python脚本抢票【笔记】 前言版权推荐Python脚本抢票【Python】microsoft edge驱动器下载以及使用最后 前言 2024-4-17 18:19:15 以下内容源自《【笔记】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN日星月云 博客主页是ht…

Unity WebGL 转换微信小游戏方案

方案特点 保持原引擎工具链与技术栈无需重写游戏核心逻辑&#xff0c;支持大部分第三方插件由转换工具与微信小游戏运行环境保证适配兼容&#xff0c;保持较高还原度微信小游戏平台能力以C# SDK方式提供给开发者&#xff0c;快速对接平台开放能力 技术原理 Unity的BuildTarget支…

【RocketMQ知识点总结-1】

文章目录 RocketMQ介绍RocketMQ架构&#xff1a;NameServer:BrokerProducerTopic&#xff08;主题&#xff09;&#xff1a;Queue&#xff08;队列&#xff09;&#xff1a;Message&#xff08;消息&#xff09;&#xff1a; RocketMQ的工作流程RocketMQ的使用场景异步消息传递…

【数据结构】Map和Set(1)

&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;个人主页&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388; &#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;数据结构专栏&#x1f388;&#x1f388;&#x1f388;&…

【c++】探究C++中的list:精彩的接口与仿真实现解密

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章来到list有关部分&#xff0c;这一部分函数与前面的类似&#xff0c;我们简单讲解&#xff0c;重难点在模拟实现时的迭代器有关实现 目录 1.List介绍2.接…

【博特激光】激光焊接机在塑料领域的应用

激光焊接机在塑料领域的应用已经越来越广泛&#xff0c;这主要得益于其独特的优势和特性。激光焊接机利用激光束产生高能量、高温的条件&#xff0c;将塑料材料熔化并融合在一起&#xff0c;实现焊接的目的。 在塑料领域&#xff0c;激光焊接机主要用于各种塑料制品的焊接&…

【项目分享】用 Python 写一个桌面倒计日程序!

事情是这样的&#xff0c;我们班主任想委托我做一个程序&#xff0c;能显示还有几天考试。我立即理解了这个意思&#xff0c;接下了这个项目。 话不多说&#xff0c;来看看这个项目吧—— 项目简介 仓库地址&#xff1a;https://gitee.com/yaoqx/desktop-countdown-day 这是 …

C语言入门课程学习笔记-6

C语言入门课程学习笔记-6 第27课 - 字符数组与字符串&#xff08;上&#xff09;第28课 - 字符数组与字符串&#xff08;下&#xff09;第29课 - 数组专题练习&#xff08;上&#xff09;第30课 - 数组专题练习&#xff08;下&#xff09; 本文学习自狄泰软件学院 唐佐林老师的…

matplotlib 安装失败:Failed building wheel for matplotlib 解决方案

Python | Failed building wheel for matplotlib 朋友遇到 python 安装 matplotlib 时的问题&#xff0c;笔者帮忙远程调试(踩了不少坑)。网上的解决方案有很多无效&#xff0c;以此来记录以下个人解决方案。 在使用指令 pip install matplotlib出现如下报错&#xff1a; “…

移远通信再推系列高性能卫星、5G、GNSS及三合一组合天线

4月23日&#xff0c;全球领先的物联网整体解决方案供应商移远通信正式宣布&#xff0c;再次推出多款高性能天线产品&#xff0c;以进一步满足物联网市场对高品质天线产品的需求。 其中包括卫星天线YETN001L1A、三合一组合天线YEMA300QXA和YEMN302Q1A&#xff0c;外部5G天线YECN…

Unity对应的c#版本

本文主要是记录一下unity已经开始兼容c#的版本和.net版本&#xff0c;以便更好的利用c#的特性。 c#和.net对应情况 微软已经将.net开发到.net 9了&#xff0c;但是unity的迭代速度远没有c#迭代速度快&#xff0c;已知unity最新的LTS版本unity2023已经兼容了c#9 可以在unity手册…

生成数据能否帮助模型训练?

能否利用生成模型生成的假数据来辅助学习&#xff1f; 到底是可以左脚踩右脚&#xff08;bootsrap&#xff09;地实现 weak-to-strong 的不断提升&#xff0c;还是像鸡生蛋、蛋生鸡一样&#xff0c;只不过是徒劳无功&#xff1f; 论文题目&#xff1a; Do Generated Data Alw…

集成学习算法学习笔记

一、集成学习的基本思想 三个臭皮匠顶一个诸葛亮 集成学习会考虑多个评估器的建模结果&#xff0c;汇总后得到一个综合的结果&#xff0c;以此来获取比单个模型更好的回归或分类表现。 很多独立的机器学习算法&#xff1a;决策树、神经网络、支持向量机 集成学习构建了一组基…

如何在iPhone/iPad上恢复已删除的微信消息?

“我从我的iPhone上删除了一些微信消息。我想知道我是否可以从我的iPhone上恢复已删除的微信消息。我尝试了一些方法&#xff0c;但没有一个可以恢复我丢失的消息&#xff0c;只能恢复我的短信。谁可以给我有什么建议吗&#xff1f;” ——蒂娜 如何在iPhone或iPad上恢复已删除…

3122.使矩阵满足条件的最少操作次数

周赛第三题,知道要用动态规划,但是不知道怎么回到子问题 显然根据题意我们需要让每一列都相同,但是相邻列不能选择同一种数字,观察到数据nums[i]介于0-9,我们就以此为突破口. 首先我们用count[n][10], count[i][j]记录第i1列值为j的元素个数,转移方程如下: dfs(i,pre) max(dfs…

根据标签最大层面ROI提取原始图像区域

今天要实现的任务是提取肿瘤的感兴趣区域。 有两个文件&#xff0c;一个是nii的原始图像文件&#xff0c;一个是nii的标签文件。 我们要实现的是&#xff1a;在标签文件上选出最大层面&#xff0c;然后把最大层面的ROI映射到原始图像区域&#xff0c;在原始图像上提裁剪出ROI…

6.模板初阶

目录 1.泛型编程 2. 函数模板 2.1 函数模板概念 2.2函数模板格式 2.3 模板的实现 2.4函数模板的原理 2.5 函数模板的实例化 3.类模板 1.泛型编程 我们如何实现一个 交换函数呢&#xff1f; 使用函数重载虽然可以实现&#xff0c;但是有一下几个不好的地方&#xff1a; …

(学习日记)2024.04.26:UCOSIII第五十节:User文件夹函数概览(uC-CPU文件夹)

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

docker容器技术篇:集群管理实战mesos+zookeeper+marathon(二)

docker集群管理实战mesoszookeepermarathon&#xff08;二&#xff09; 一 实验环境 操作系统&#xff1a;centos7.9 二 基础环境配置以及安装mesos 安装过程请点击下面的链接查看&#xff1a; 容器集群管理实战mesoszookeepermarathon&#xff08;一&#xff09; 三 安装…