Explicación detallada del marco del controlador I2C del kernel de Linux

Tabla de contenido

1 diagrama de marco general del controlador I2C  

2 controladores I2C

2.1 Dispositivo controlador I2C: el controlador I2C también se considera un dispositivo en el kernel

2.2 controlador del controlador i2c

2.3 ¿Qué hace la función de sonda en la estructura platform_driver?

2.3.1 Pregunta: ¿De dónde viene la función i2cdev_notifier_call?

2.3.2 Pregunta: ¿Por qué hay dos sondas?

2.3.3 Pregunta: ¿Se superponen las funciones de of_i2c_register_devices(adap); y bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter)?

2.3.4 Pregunta: Problemas con platform_bus_type e I2c_bus_type

2.3.5 Pregunta: ¿Por qué finalmente se llaman las funciones de coincidencia y sonda en la función i2c_imx_probe?

3 núcleos i2c

4 dispositivos i2c

4.1 cliente i2c

4.2 controlador i2c

4.2.1 Pregunta: Llame a driver_register(&driver->driver) en la función i2c_register_driver, basta con agregar un controlador a la función, ¿por qué también llamar a i2c_for_each_dev(driver, __process_new_driver);

4.3 ¿Qué hace la función de sonda?

4.3.1 Pregunta: ¿Por qué hay coincidencias y sondas en at24_probe?

5 herramientas i2c

5.1 ¿Por qué i2c-tools es un conjunto de herramientas útil?

5.2 ¿Por qué i2c-tools también es un conjunto de códigos de muestra?

6 controlador universal i2c_dev.c

7 GPIO analógico I2C

8. Método de aprendizaje Feynman: grabé un video de aprendizaje que explica el marco del controlador del subsistema I2C.

referencias:


1 diagrama de marco general del controlador I2C  

  La imagen de arriba es el marco general del sistema I2C, que se presenta a continuación.

  • La capa superior es la capa de aplicación, en la que los usuarios pueden utilizar directamente la lectura y escritura abiertas para operar el dispositivo.
  • A continuación se muestra la capa del controlador del dispositivo, que es el controlador periférico, como algunos sensores o controladores EEPROM conectados al SOC mediante el bus I2C. Esto generalmente es responsabilidad de los ingenieros de controladores comunes.
  • El I2C-Core más abajo es la capa central, que se incluye originalmente en el código fuente del kernel de Linux y contiene principalmente algunas funciones de registro de controladores y dispositivos y la función i2c_transfer.
  • Más abajo está el controlador del controlador I2C, que generalmente lo escribe el programador de la fábrica de chips original.
  • Luego viene el hardware específico.

La imagen de arriba es el marco de software del controlador I2C, que se presenta a continuación.

  • En primer lugar, el extremo derecho es el controlador del dispositivo I2C, que se divide en i2c-client y i2c-driver. El controlador del dispositivo i2c está montado en i2c_bus_type, donde el cliente i2c proviene del archivo del árbol de dispositivos y se convierte a i2c- mediante la función of_i2c_register_devices(adap); cliente, y luego se agrega a la lista de dispositivos del autobús, y luego la estructura i2c_driver se agrega a la lista de conductores del autobús a través de la función de registro. Cuando se ingresa un nuevo controlador o dispositivo agregado, se llamará a la función mach del bus para hacer coincidir, y luego se llamará a la función de sonda en el controlador, agregará una estructura en la función de sonda, y luego esta estructura contiene las funciones de lectura y escritura del dispositivo.
  • El más a la izquierda es el controlador del controlador I2C, donde el nodo i2c del árbol de dispositivos se convierte en platform_device y luego se agrega a la lista de dispositivos de platform_bus_type, y luego hay una estructura de controlador de platform_driver, que se registra en la lista de controladores de platform_bus_type , y luego, al agregar un dispositivo y un controlador, se llamará a la función platform_match. Después de hacer coincidir, se llamará a la función i2x_imx_probe en el controlador platform_driver.
  • El medio es el trabajo realizado en la función i2x_imx_probe. Esta función primero llama a device_register para agregar el adaptador a la estructura del dispositivo de i2c_bus_type. Tenga en cuenta que es i2c_bus_type, no platform_bus_type. El adaptador contiene un miembro del algoritmo. Este algoritmo contiene la función master_xfer, i2c: la función i2c_transfer en el núcleo es la función master_xfer en el algoritmo llamado, y luego también se llama a of_i2c_register_device en la función i2x_imx_probe para agregar i2c-client.

De acuerdo con el método de aprendizaje de Feynman, expresar los puntos de conocimiento puede profundizar su comprensión de los puntos de conocimiento, así que grabé un video en el subsistema I2C y lo envié a la Estación B:

8 minutos para explicar el marco general del controlador I2C del kernel de Linux_bilibili_bilibili

Lo anterior es una introducción general al controlador i2c. A continuación se presenta el contenido relevante del controlador i2c, i2c-core y el controlador de dispositivo i2c.

2 controladores I2C

2.1 Dispositivo controlador I2C: el controlador I2C también se considera un dispositivo en el kernel

Primero mire el dispositivo controlador i2c, puede ver el nodo i2c en el archivo de árbol de dispositivos ./Linux-4.9.88/arch/arm/boot/dts/imx6ull.dtsi,

            i2c1: i2c@021a0000 {                 #address-cells = <1>;                 #size-cells = <0>;                 compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";                 reg = <0x021a0000 0x4000>;                 interrupciones = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;                 clocks = <&clks IMX6UL_CLK_I2C1>;                 status = "disabled"; #En uso real, este lugar debe cambiarse a "ok".             };







of_platform_default_populate(NULL, NULL, parent); la función convierte el nodo I2C en platform_device y agrega el dispositivo. La relación de llamada de función específica es la siguiente:

of_platform_default_populate(NULL, NULL, padre);

    of_platform_populate(raíz, of_default_bus_match_table, búsqueda, padre);

        of_find_node_by_path("/")//Encontrar el nodo raíz del árbol de dispositivos

        of_platform_bus_create//Esta función se llamará cíclicamente

            of_platform_device_create_pdata

                 of_device_alloc(np, bus_id, padre);

                 of_device_add(desarrollador)

                     dispositivo_add(&ofdev->dev);

                         bus_add_device(desarrollador);

                             klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);Agregar a la lista vinculada

                         blocking_notifier_call_chain(&dev->bus->p->bus_notifier,调用bus_notifier

                         i2cdev_notifier_call

                               i2cdev_attach_adapter                                

                               if (dev->type!= &i2c_adapter_type)//devuelve directamente device_create sin llamar

                                   devolver 0;

                                   device_create//Agregar nodo i2c-%d

                       bus_probe_device(desarrollador);

                           dispositivo_inicial_probe(desarrollador);

                               __device_attach(dev, verdadero);                               

                                   bus_for_each_drv(dev->bus, NULL, &datos, __device_attach_driver);

                                       __device_attach_driver

                                           driver_match_device(drv, dev);

                                               drv->bus->partido? drv->bus->coincidencia(dev, drv): 1;

                                           driver_probe_device(drv, dev);

                                                 realmente_probe(dev, drv);

                                                     dev->bus->sonda(dev);或drv->sonda(dev)

    

Finalmente, llame a la función i2c_imx_probe en la estructura platform_driver. La función i2c_imx_probe se analizará más adelante. El dispositivo platform_device se verá aquí primero.

static struct platform_driver i2c_imx_driver = {
	.probe = i2c_imx_probe,
	.remove = i2c_imx_remove,
	.driver = {
		.name = DRIVER_NAME,
		.pm = I2C_IMX_PM_OPS,
		.of_match_table = i2c_imx_dt_ids,
	},
	.id_table = imx_i2c_devtype,
};

2.2 controlador del controlador i2c

El archivo del controlador correspondiente se puede encontrar en el código fuente de Linux a través del valor del atributo compatible del nodo i2c1 anterior. Aquí hay dos valores de atributos compatibles del nodo i2c1: "fsl,imx6ul-i2c" y "fsl,imx21-i2c". Busque estas dos cadenas en el código fuente de Linux para encontrar el archivo del controlador correspondiente. El archivo del controlador del adaptador I2C de I.MX6U es drivers/i2c/busses/i2c-imx.c. El dispositivo controlador i2c anterior finalmente se convierte a platform_device, por lo que el controlador i2c también usa platform_driver y se monta en platform_bus_type.

Al mirar un controlador, comience con la función de entrada. Encontramos la función i2c_adap_imx_init en el archivo drivers/i2c/busses/i2c-imx.c. Primero, llame a platform_driver_register(&i2c_imx_driver) para registrar la estructura i2c_imx_driver. La relación de llamada de función específica es el siguiente, y luego cuando match Cuando la función encuentra que el controlador coincide con el dispositivo, llamará a la función de sonda en el controlador, que es la función i2c_imx_probe.

plataforma_driver_register(&i2c_imx_driver);

    __platform_driver_register(drv, ESTE_MÓDULO)  

        drv->driver.propietario = propietario;

        drv->driver.bus = &platform_bus_type;

        drv->driver.probe = plataforma_drv_probe;

        drv->driver.remove = plataforma_drv_remove;

        drv->driver.shutdown = plataforma_drv_shutdown;

        driver_register(&drv->controlador);    

            bus_add_driver(drv);

                klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);Ponga el controlador en klist_driver

                controlador_attach(drv);

                    bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

                        __conductor_attach

                            driver_match_device(drv, dev);

                                drv->bus->partido? drv->bus->coincidencia(dev, drv): 1; 

                           driver_probe_device(drv, dev);

                               ret = realmente_probe(dev, drv);

                                   dev->bus->sonda(dev);或drv->sonda(dev) 

2.3 ¿Qué hace la función de sonda en la estructura platform_driver?

Cuando se agrega un nuevo dispositivo o controlador, se llamará a la función de coincidencia del autobús y luego la función de coincidencia hará coincidir el dispositivo y el controlador según el valor o nombre del atributo compatible.

 * Platform device IDs are assumed to be encoded like this:
 * "<name><instance>", where <name> is a short description of the type of
 * device, like "pci" or "floppy", and <instance> is the enumerated
 * instance of the device, like '0' or '42'.  Driver IDs are simply
 * "<name>".  So, extract the <name> from the platform_device structure,
 * and compare it against the name of the driver. Return whether they match
 * or not.
 */
static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)
		return !strcmp(pdev->driver_override, drv->name);

	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
		return 1;

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

	/* Then try to match against the id table */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);
}

Después de hacer coincidir, se llamará a la función de sonda en la estructura del controlador. A continuación, echemos un vistazo a lo que hace la función i2c_imx_probe en la estructura struct platform_driver i2c_imx_driver.

static struct platform_driver i2c_imx_driver = {
	.probe = i2c_imx_probe,
	.remove = i2c_imx_remove,
	.driver = {
		.name = DRIVER_NAME,
		.pm = I2C_IMX_PM_OPS,
		.of_match_table = i2c_imx_dt_ids,
	},
	.id_table = imx_i2c_devtype,
};

La relación de llamada de función específica es la siguiente:

i2c_imx_probe

    i2c_add_numbered_adapter

        __i2c_add_numbered_adapter

           i2c_register_adapter

               dispositivo_register(&adap->dev);

                   dispositivo_add(desarrollador);

                       bus_add_device(desarrollador);

                           klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);Agregar a la lista vinculada

                       blocking_notifier_call_chain(&dev->bus->p->bus_notifier,调用bus_notifier

                           i2cdev_notifier_call

                               i2cdev_attach_adapter

                                   device_create//Agregar nodo i2c-%d

                       bus_probe_device(desarrollador);

                           dispositivo_inicial_probe(desarrollador);

                               __device_attach(dev, verdadero);                               

                                   bus_for_each_drv(dev->bus, NULL, &datos, __device_attach_driver);

                                       __device_attach_driver

                                           driver_match_device(drv, dev);

                                               drv->bus->match ? drv->bus->match(dev, drv) : 1;// La coincidencia no tuvo éxito, regrese directamente,

                                           driver_probe_device(drv, dev); La coincidencia anterior no fue exitosa, aquí no se llama directamente

                                                 realmente_probe(dev, drv);

                                                     dev->bus->probe(dev);或drv->probe(dev),

               of_i2c_register_devices(adaptar);

                    of_i2c_register_device(adap, nodo);

                        i2c_new_device(adap, &info); usado para agregar cliente

                        cliente->dev.parent = &cliente->adaptador->dev;

                        client->dev.bus = &i2c_bus_type;//Tenga en cuenta que esto es i2c-bus, no platform_bus

                        cliente->dev.type = &i2c_client_type;

                        cliente->dev.of_node = info->of_node;

                        cliente->dev.fwnode = información->fwnode;

                            status = device_register(&client->dev);Registrar un nuevo dispositivo i2c_client

                                dispositivo_add(desarrollador);

                                    bus_add_device(desarrollador); 

                                        klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);

                                    dispositivo_sonda_bus

                                        dispositivo_inicial_probe(desarrollador);

                                            __device_attach(dev, verdadero);

                                                bus_para_cada_drv

                                                      __device_attach_driver       

                                                          controlador_match_dispositivo

                                                             drv->bus->partido? drv->bus->coincidencia(dev, drv): 1;

                                                          driver_probe_device(drv, dev);

                                                              realmente_probe(dev, drv);

                                                                  dev->bus->sonda(dev);或drv->sonda(dev)

                bus_for_each_drv(&i2c_bus_type, NULL, adapt, __process_new_adapter)

                    __process_new_adapter(struct dispositivo_driver *d, anular *datos)                 

                        i2c_do_add_adapter(estructura i2c_driver *controlador, estructura i2c_adapter *adap)

                            i2c_detect(adap, driver); Detecta si hay un dispositivo adecuado conectado al bus a través de i2c_detect

                                if (!driver->detect || !address_list) devuelve 0; si detect o Address_list no están definidos, devolverá directamente

                                i2c_detect_address(temp_client, controlador);

                                    err = driver->detect(temp_client, &info); envía datos de prueba según el cliente correspondiente. Si no hay problema, prueba que el cliente es el dispositivo requerido por el controlador. Finalmente, el dispositivo se agrega al enlace list y, finalmente, se llama a bus_probe_device para intentar vincular el controlador.

                                    cliente = i2c_new_device(adapter, &info); usado para agregar cliente

                                         dispositivo_register(&cliente->dev);

                                             dispositivo_add(desarrollador);   

                                                 bus_add_device(desarrollador);

                                                     klist_add_tail

                                                 bus_probe_device(desarrollador);

                                                     bus_probe_device(desarrollador);

                                                         dispositivo_inicial_probe(desarrollador);

                                                             __device_attach(dev, verdadero);

                                                                 bus_para_cada_drv

                                                                     __device_attach_driver

                                                                         driver_match_device(drv, dev);

                                                                             drv->bus->partido? drv->bus->coincidencia(dev, drv): 1;

                                                                         driver_probe_device(drv, dev);

                                                                             realmente_probe(dev, drv);

                                                                                 dev->bus->sonda(dev);或drv->sonda(dev)

                    

        

Estaba mirando el código del kernel y obtuve el proceso de llamada a la función anterior, pero al mismo tiempo tenía las siguientes preguntas o dudas;

2.3.1 Pregunta:  ¿De dónde viene la función i2cdev_notifier_call?

En el proceso anterior, por qué blocking_notifier_call_chain (&dev->bus->p->bus_notifier, llamar a bus_notifier llamará a i2cdev_notifier_call, el motivo está aquí.

static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
  		 void *data)
{
  	struct device *dev = data;
  
  	switch (action) {
  	case BUS_NOTIFY_ADD_DEVICE:
  		return i2cdev_attach_adapter(dev, NULL);
  	case BUS_NOTIFY_DEL_DEVICE:
  		return i2cdev_detach_adapter(dev, NULL);
  	}
  
  	return 0;
}
  
static struct notifier_block i2cdev_notifier = {
  	.notifier_call = i2cdev_notifier_call,
};
  
static int __init i2c_dev_init(void)
{
	...
	res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
	...
}

2.3.2 Pregunta: ¿Por qué hay dos sondas?

Hay una función de sonda en la estructura platform_driver,

static struct platform_driver i2c_imx_driver = {
	.probe = i2c_imx_probe,
	.remove = i2c_imx_remove,
	.driver = {
		.name = DRIVER_NAME,
		.pm = I2C_IMX_PM_OPS,
		.of_match_table = i2c_imx_dt_ids,
	},
	.id_table = imx_i2c_devtype,
};

Pero al registrar este controlador, ¿por qué contiene una función platform_drv_probe?

 */
int __platform_driver_register(struct platform_driver *drv,
				struct module *owner)
{
	drv->driver.owner = owner;
	drv->driver.bus = &platform_bus_type;
	drv->driver.probe = platform_drv_probe;
	drv->driver.remove = platform_drv_remove;
	drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}

Después de mirar el código, descubrí que esto se debe a que el platform_drv_probe externo en realidad llama a la sonda en platform_driver.

static int platform_drv_probe(struct device *_dev)
{
	struct platform_driver *drv = to_platform_driver(_dev->driver);
	struct platform_device *dev = to_platform_device(_dev);
	int ret;

	ret = of_clk_set_defaults(_dev->of_node, false);
	if (ret < 0)
		return ret;

	ret = dev_pm_domain_attach(_dev, true);
	if (ret != -EPROBE_DEFER) {
		if (drv->probe) {
			ret = drv->probe(dev);  //在这个地方调用了platform_driver的probe函数
			if (ret)
				dev_pm_domain_detach(_dev, true);
		} else {
			/* don't fail if just dev_pm_domain_attach failed */
			ret = 0;
		}
	}

	if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
		dev_warn(_dev, "probe deferral not supported\n");
		ret = -ENXIO;
	}

	return ret;
}

2.3.3 Pregunta: ¿Se superponen las funciones de of_i2c_register_devices(adap); y bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter)?

Al observar el proceso de llamada a la función anterior, descubrí que las funciones of_i2c_register_devices(adap); y bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter) llaman a i2c_new_device para agregar i2c-client. ¿No es un duplicado de la función? Mire con cuidado Después de leer el código, descubrí que debería ser así, of_i2c_register_devices(adap); es obtener la información del dispositivo del nodo del árbol de dispositivos y luego registrar el cliente i2c y bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter ) en realidad llama a i2c_detect y luego, según la función de detección y la lista de direcciones definidas en el controlador, detecta el cliente i2c en el bus. Esto es equivalente a diferentes métodos para agregar un cliente. Para una explicación detallada, consulte este documento del núcleo : Varios métodos para crear instancias de dispositivos i2c en el kernel de Linux-- --./Linux-4.9.88/Documentation/i2c/instantiating-devices file Translation_blog-CSDN de Chen Hongwei 

2.3.4 Pregunta: Problemas con platform_bus_type e I2c_bus_type

Tenga en cuenta que en la función i2c_adap_imx_init

static int __init i2c_adap_imx_init(void)
{
	return platform_driver_register(&i2c_imx_driver);
}

Luego llame a __platform_driver_register, el autobús en este momento es platform_bus_type

int __platform_driver_register(struct platform_driver *drv,
				struct module *owner)
{
	drv->driver.owner = owner;
	drv->driver.bus = &platform_bus_type;
	drv->driver.probe = platform_drv_probe;
	drv->driver.remove = platform_drv_remove;
	drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}

 Sin embargo, al registrar el adaptador (controlador) en la sonda en el controlador, se llama a la interfaz i2c_add_numbered_adapter, el bus en este momento es i2c_bus_type.

static int i2c_register_adapter(struct i2c_adapter *adap)
{
	...

	dev_set_name(&adap->dev, "i2c-%d", adap->nr);
	//BUS指向I2C
	adap->dev.bus = &i2c_bus_type;
	adap->dev.type = &i2c_adapter_type;
	res = device_register(&adap->dev);
	...

Miré el código detenidamente y lo entendí. De hecho, es así: el nodo I2C en el nodo del árbol de dispositivos se convierte en platform_device y luego se monta en el bus platform_bus. Luego, cuando la función de coincidencia de platform_bus_type encuentra que el dispositivo y la coincidencia del controlador, se llama Función de sonda en la estructura del controlador, luego construye el adaptador en la función de sonda y agrégalo, y luego el adaptador se agrega a i2c_bus_type.

2.3.5 Pregunta: ¿Por qué finalmente se llaman las funciones de coincidencia y sonda en la función i2c_imx_probe?

Esta  función i2c_imx_probe se llama cuando la función de coincidencia de plarform_bus_type encuentra que el controlador del controlador y el dispositivo del controlador coinciden, y luego le agrega un adaptador, pero ¿cómo es que hay drv->bus-> en las capas dentro de la función i2c_imx_probe ? ? drv->bus->match(dev, drv): 1; y dev->bus->probe(dev); o función drv->probe(dev). ¿Por qué se vuelve a llamar a la sonda en la sonda? ¿Qué pasa con el sistema interno ? ¿Para qué se utiliza la sonda? 

i2c_imx_probe

    i2c_add_numbered_adapter

        __i2c_add_numbered_adapter

           i2c_register_adapter

               dispositivo_register(&adap->dev);

                   dispositivo_add(desarrollador);

                       bus_add_device(desarrollador);

                           klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);Agregar a la lista vinculada

                       blocking_notifier_call_chain(&dev->bus->p->bus_notifier,调用bus_notifier

                           i2cdev_notifier_call

                               i2cdev_attach_adapter

                                   device_create//Agregar nodo i2c-%d

                       bus_probe_device(desarrollador);

                           dispositivo_inicial_probe(desarrollador);

                               __device_attach(dev, verdadero);                               

                                   bus_for_each_drv(dev->bus, NULL, &datos, __device_attach_driver);

                                       __device_attach_driver

                                           driver_match_device(drv, dev);

                                               drv->bus->partido? drv->bus->coincidencia(dev, drv): 1;

                                           driver_probe_device(drv, dev);

                                                 realmente_probe(dev, drv);

                                                     dev->bus->sonda(dev);或drv->sonda(dev)

¿Por qué hay una sonda en él? No puedo entenderlo y es difícil. Fui a mirar el código del kernel y le expliqué el proceso de llamada de la función i2c_imx_probe. Descubrí que debería ser así, pero no Seguro si lo que entendí es correcto . No hay problema en añadir cualquier adaptador en los anteriores, al registrar el adaptador el bus es i2c_bus_tyupe.

static int i2c_register_adapter(struct i2c_adapter *adap)
{
    ...
 
    adap->dev.bus = &i2c_bus_type;
	adap->dev.type = &i2c_adapter_type;
	res = device_register(&adap->dev);

     ...
  
}

Luego, cuando se trata de drv->bus->match, aquí el bus es i2c_bus_type, por lo que la llamada es 

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

 Entonces eso es

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

	if (!client)
		return 0;

	/* Attempt an OF style match */
	if (of_driver_match_device(dev, drv))
		return 1;

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

	driver = to_i2c_driver(drv);
	/* match on an id table if there is one */
	if (driver->id_table)
		return i2c_match_id(driver->id_table, client) != NULL;

	return 0;
}

Entonces, dado que lo que se agrega aquí es el dispositivo adaptador, entonces si (!client) no es verdadero en absoluto, entonces la coincidencia aquí es 0, entonces

static int __device_attach_driver(struct device_driver *drv, void *_data)
{
	struct device_attach_data *data = _data;
	struct device *dev = data->dev;
	bool async_allowed;
	int ret;

	/*
	 * Check if device has already been claimed. This may
	 * happen with driver loading, device discovery/registration,
	 * and deferred probe processing happens all at once with
	 * multiple threads.
	 */
	if (dev->driver)
		return -EBUSY;

	ret = driver_match_device(drv, dev);
	if (ret == 0) {
		/* no match */
		return 0;
	} else if (ret == -EPROBE_DEFER) {
		dev_dbg(dev, "Device match requests probe deferral\n");
		driver_deferred_probe_add(dev);
	} else if (ret < 0) {
		dev_dbg(dev, "Bus failed to match device: %d", ret);
		return ret;
	} /* ret > 0 means positive match */

	async_allowed = driver_allows_async_probing(drv);

	if (async_allowed)
		data->have_async = true;

	if (data->check_async && async_allowed != data->want_async)
		return 0;

	return driver_probe_device(drv, dev);
}

Dado que la función driver_match_device(drv, dev); devuelve directamente 0, la función __device_attach_driver también devolverá directamente y no se llamará a la función driver_probe_device(drv, dev);.

3 núcleos i2c

 El proceso de coincidencia de dispositivos y controladores I2C se completa con el núcleo I2C. drivers/i2c/i2c-core.c es la
parte central de I2C. El núcleo I2C proporciona algunas funciones API que no tienen nada que ver con hardware específico, como las mencionadas antes: 
  1, función de registro/cancelación de i2c_adapter 
int i2c_add_adapter(struct i2c_adapter *adapter) 
int i2c_add_numbered_adapter(struct i2c_adapter *adap) 
void i2c_del_adapter(struct i2c_adapter * adap) 
  2. Función de registro/cancelación de i2c_driver 
int i2c_register_ controlador (módulo de estructura *propietario, estructura i2c_driver *driver) 
int i2c_add_driver (struct i2c_driver *driver) 
void i2c_del_driver (struct i2c_driver *driver) 
El proceso de coincidencia entre el dispositivo y el controlador también lo completa el bus I2C. La estructura de datos del bus I2C es i2c_bus_type, que se define
en el archivo drivers/i2c/i2c-core.c.

Además, hay una función i2c_transfer en i2c-core, y luego la función i2c_transfer se usa directamente en el controlador del dispositivo para enviar datos, y esta función i2c_transfer finalmente llama a la función master_xfer en el algoritmo en el adaptador. De ahí que i2c-core juegue un papel como vínculo entre el anterior y el siguiente, conectando el controlador del dispositivo y el controlador del controlador.

4 dispositivos i2c

4.1 cliente i2c

El cliente i2c proviene del archivo del árbol de dispositivos y generalmente se coloca en el nodo secundario del nodo i2c, como el dispositivo ap3216 a continuación.

&i2c1 {         ap3216c@1e {             compatible = "lite-on,ap3216c";             reg = <0x1e>;         };/*Los nodos secundarios en i2c se utilizan para representar dispositivos i2c*/ };




&i2c1 {     frecuencia de reloj = <100000>;     pinctrl-names = "default";     pinctrl-0 = <&pinctrl_i2c1>;     status = "ok"; };/*Esto se usa para representar el controlador i2c, no el dispositivo i2c * /




Los nodos secundarios bajo el nodo del bus i2c no se convertirán en platform_device. Son procesados ​​por el controlador del bus I2C. La conversión del nodo del dispositivo bajo el I2C en cliente en realidad se realiza mediante la función de sonda en el controlador del controlador i2c. Como se mencionó anteriormente Al analizar el proceso interno de la función de sonda, la función of_i2c_register_devices en la parte central se usa para agregar i2c-client.

i2c_imx_probe

    i2c_add_numbered_adapter

        __i2c_add_numbered_adapter

           i2c_register_adapter

               dispositivo_register(&adap->dev);

                   dispositivo_add(desarrollador);

                       bus_add_device(desarrollador);

                           klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);Agregar a la lista vinculada

                       blocking_notifier_call_chain(&dev->bus->p->bus_notifier,调用bus_notifier

                           i2cdev_notifier_call

                               i2cdev_attach_adapter

                                   device_create//Agregar nodo i2c-%d

                       bus_probe_device(desarrollador);

                           dispositivo_inicial_probe(desarrollador);

                               __device_attach(dev, verdadero);                               

                                   bus_for_each_drv(dev->bus, NULL, &datos, __device_attach_driver);

                                       __device_attach_driver

                                           driver_match_device(drv, dev);

                                               drv->bus->partido? drv->bus->coincidencia(dev, drv): 1;

                                           driver_probe_device(drv, dev);

                                                 realmente_probe(dev, drv);

                                                     dev->bus->sonda(dev);或drv->sonda(dev)

               of_i2c_register_devices(adaptar);

                    of_i2c_register_device(adap, nodo);

                        i2c_new_device(adap, &info); usado para agregar cliente

                        cliente->dev.parent = &cliente->adaptador->dev;

                        client->dev.bus = &i2c_bus_type;//Tenga en cuenta que esto es i2c-bus, no platform_bus

                        cliente->dev.type = &i2c_client_type;

                        cliente->dev.of_node = info->of_node;

                        cliente->dev.fwnode = información->fwnode;

                            status = device_register(&client->dev);Registrar un nuevo dispositivo i2c_client

                                dispositivo_add(desarrollador);

                                    bus_add_device(desarrollador); 

                                        klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);

                                    dispositivo_sonda_bus

                                        dispositivo_inicial_probe(desarrollador);

                                            __device_attach(dev, verdadero);

                                                bus_para_cada_drv

                                                      __device_attach_driver       

                                                          controlador_match_dispositivo

                                                             drv->bus->partido? drv->bus->coincidencia(dev, drv): 1;

                                                          driver_probe_device(drv, dev);

                                                              realmente_probe(dev, drv);

                                                                  dev->bus->sonda(dev);或drv->sonda(dev)

                bus_for_each_drv(&i2c_bus_type, NULL, adapt, __process_new_adapter)

                    __process_new_adapter(struct dispositivo_driver *d, anular *datos)                 

                        i2c_do_add_adapter(estructura i2c_driver *controlador, estructura i2c_adapter *adap)

                            i2c_detect(adap, driver); Detecta si hay un dispositivo adecuado conectado al bus a través de i2c_detect

                                if (!driver->detect || !address_list) devuelve 0; si detect o Address_list no están definidos, devolverá directamente

                                i2c_detect_address(temp_client, controlador);

                                    err = driver->detect(temp_client, &info); envía datos de prueba según el cliente correspondiente. Si no hay problema, prueba que el cliente es el dispositivo requerido por el controlador. Finalmente, el dispositivo se agrega al enlace list y, finalmente, se llama a bus_probe_device para intentar vincular el controlador.

                                    cliente = i2c_new_device(adapter, &info); usado para agregar cliente

                                         dispositivo_register(&cliente->dev);

                                             dispositivo_add(desarrollador);   

                                                 bus_add_device(desarrollador);

                                                     klist_add_tail

                                                 bus_probe_device(desarrollador);

                                                     bus_probe_device(desarrollador);

                                                         dispositivo_inicial_probe(desarrollador);

                                                             __device_attach(dev, verdadero);

                                                                 bus_para_cada_drv

                                                                     __device_attach_driver

                                                                         driver_match_device(drv, dev);

                                                                             drv->bus->partido? drv->bus->coincidencia(dev, drv): 1;

                                                                         driver_probe_device(drv, dev);

                                                                             realmente_probe(dev, drv);

                                                                                 dev->bus->sonda(dev);或drv->sonda(dev)

                    

         

4.2 controlador i2c

i2c_driver usa esta estructura de bus

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

Comencemos con la función de entrada module_init(at24_init);, que llama a i2c_add_driver(&at24_driver);, luego llama a i2c_register_driver(THIS_MODULE, driver), y luego llama a driver_register(&driver->driver); y luego lo llama más. Bus_add_driver(drv) ;, y luego continúe llamando a driver_attach(drv); y luego continúe bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); esta función es llamar a la función __driver_attach para cada dispositivo, luego ingrese La función __driver_attach encontró que Hay dos funciones importantes en él.

  • driver_match_device(drv, dev);
  • driver_probe_device(drv, dev);

driver_match_device(drv, dev); además llama a drv->bus->match(dev, drv), que es la función de coincidencia en i2c_bus_type.

driver_probe_device(drv, dev); luego llama a very_probe(dev, drv);, y luego llama a dev->bus->probe(dev);. Esta es la función de sonda en i2c_bus_type.

i2c_add_driver(&at24_driver)

    i2c_register_driver(ESTE_MÓDULO, controlador)

        registro_conductor(&conductor->conductor)

             bus_add_driver(drv)

                  klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers) coloca el controlador en klist_driver

                  controlador_attach(drv)

                      bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

                         __driver_attach(estructura dispositivo *dev, anular *datos)

                             driver_match_device(drv, dev);

                                 drv->bus->partido? drv->bus->coincidencia(dev, drv): 1; 

                              driver_probe_device(drv, dev);

                                  realmente_probe(dev, drv);

                                      dev->bus->sonda(dev);或drv->sonda(dev)   

    /* Recorre los adaptadores que ya están presentes */

    i2c_for_each_dev(controlador, __process_new_driver);//

        __process_new_driver // El siguiente código no se llamará y se devolverá directamente desde aquí.          

            i2c_do_add_adapter(struct i2c_driver *controlador,struct i2c_adapter *adap)

                i2c_detect(adaptación, controlador); 

                   if (!driver->detect || !address_list) devuelve 0; si detect o Address_list no están definidos, devolverá directamente

                        i2c_detect_address(temp_client, controlador);

                            err = controlador->detectar(temp_client, &info);

                                cliente = i2c_new_device(adaptador, &info);

                                     dispositivo_register(&cliente->dev);

                                         dispositivo_add(desarrollador);   

                                             bus_add_device(desarrollador);

                                                 klist_add_tail

                                             bus_probe_device(desarrollador);

                                                 bus_probe_device(desarrollador);

                                                     dispositivo_inicial_probe(desarrollador);

                                                         __device_attach(dev, verdadero);

                                                             bus_para_cada_drv

                                                                 __device_attach_driver

                                                                     driver_match_device(drv, dev);

                                                                         drv->bus->partido? drv->bus->coincidencia(dev, drv): 1;

                                                                      driver_probe_device(drv, dev);

                                                                          realmente_probe(dev, drv);

                                                                             dev->bus->sonda(dev);或drv->sonda(dev)

4.2.1 Pregunta: Llame a driver_register(&driver->driver) en la función i2c_register_driver, basta con agregar un controlador a la función, ¿por qué también llamar a i2c_for_each_dev(driver, __process_new_driver);

Cuando estaba mirando la función i2c_add_driver(&at24_driver), descubrí que llamar a la función driver_register en realidad completó el trabajo de registro del controlador. Luego, también llamé a un i2c_for_each_dev(driver, __process_new_driver); con qué propósito, y el interior de esta función se volvió resultó ser i2c_do_add_adapter. Miré el código nuevamente y descubrí que no se llamó a la función __process_new_driver.

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	int res;

	/* Can't register until after driver model init */
	if (WARN_ON(!is_registered))
		return -EAGAIN;

	/* add the driver to the list of i2c drivers in the driver core */
	driver->driver.owner = owner;
	driver->driver.bus = &i2c_bus_type;
	INIT_LIST_HEAD(&driver->clients);

	/* When registration returns, the driver core
	 * will have called probe() for all matching-but-unbound devices.
	 */
	res = driver_register(&driver->driver);
	if (res)
		return res;

	pr_debug("driver [%s] registered\n", driver->driver.name);

	/* Walk the adapters that are already present */
	i2c_for_each_dev(driver, __process_new_driver);

	return 0;
}

La razón está aquí

static int __process_new_driver(struct device *dev, void *data)
{
	if (dev->type != &i2c_adapter_type)
		return 0;
	return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}

Hay un juicio si (dev->type! = &i2c_adapter_type), por lo que las siguientes funciones no se llaman en absoluto y el problema se resuelve.

4.3 ¿Qué hace la función de sonda?

Cuando se agrega un dispositivo o controlador, se llamará a la función de coincidencia en i2c_bus_type. Después de la coincidencia, se llamará a la función de sonda en el controlador. Echemos un vistazo a lo que hace la función de sonda en el controlador.

at24_probe 

    ....

    at24->nvmem_config.name = dev_name(&cliente->dev);

    at24->nvmem_config.dev = &cliente->dev;

    at24->nvmem_config.read_only = !escribible;

    at24->nvmem_config.root_only = verdadero;

    at24->nvmem_config.owner = ESTE_MODULE;

    at24->nvmem_config.compat = verdadero;

    at24->nvmem_config.base_dev = &cliente->dev;

    at24->nvmem_config.reg_read = at24_read;//función de lectura

    at24->nvmem_config.reg_write = at24_write;//función de escritura

    at24->nvmem_config.priv = at24;

    at24->nvmem_config.stride = 1;

    at24->nvmem_config.word_size = 1;

    at24->nvmem_config.size = chip.byte_len;

    at24->nvmem = nvmem_register(&at24->nvmem_config);

    ....      

        nvmem->id = rval;

         nvmem->propietario = config->propietario;

        nvmem->zancada = config->zancada;

        nvmem->word_size = config->word_size;

        nvmem->tamaño = configuración->tamaño;

        nvmem->dev.type = &nvmem_provider_type;

        nvmem->dev.bus = &nvmem_bus_type;//Presta atención a este lugar.

        nvmem->dev.parent = config->dev;

        nvmem->priv = config->priv;

        nvmem->reg_read = config->reg_read;

        nvmem->reg_write = configuración->reg_write;

        np = configuración->dev->of_node;

        nvmem->dev.of_node = np;

        rval = device_add(&nvmem->dev);// He visto esta función device_add muchas veces antes y no es más que lo mismo.

            bus_add_device(desarrollador);

            bus_probe_device(desarrollador);

                dispositivo_inicial_probe(desarrollador);

                __device_attach(dev, verdadero);

                    bus_for_each_drv(dev->bus, NULL, &data,__device_attach_driver);

                        __device_attach_driver

                            driver_match_device(drv, dev);

                                 devolver drv->bus->partir? drv->bus->coincidencia(dev, drv): 1;

                             driver_probe_device(drv, dev);

                                  driver_probe_device(drv, dev);

                                       realmente_probe(dev, drv);

                                           dev->bus->sonda(dev);或drv->sonda(dev)

4.3.1 Pregunta: ¿Por qué hay coincidencias y sondas en at24_probe?

Tengo entendido que la función at24_probe debería ser similar a la implementación de una estructura file_operation, y luego hay funciones específicas de lectura y escritura. ¿No es eso suficiente? Pero del proceso anterior, podemos ver que la función at24_probe también llama a la función at24_probe. funciones de coincidencia y sonda. Bien, continúa. Mirar el código del kernel resuelve mi confusión. . . . . . . .

primer vistazo a

static struct bus_type nvmem_bus_type = {
	.name		= "nvmem",
};

Luego descubrí que no hay una función de coincidencia definida aquí, entonces la función de coincidencia está vacía, luego devuelve drv->bus->match? drv->bus->match(dev, drv): 1; devuelve 1 directamente, y luego llamar driver_probe_device( drv, dev); función,

static int really_probe(struct device *dev, struct device_driver *drv)
{
	...

	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;
	}
    
    ...
}

Aquí dev->bus->probe está vacío, luego llamo a la función de sonda de la estructura del controlador, luego busco la estructura del controlador nvmem en el código del kernel, pero no puedo encontrarla, luego le pido ayuda a Bing AI

 Entonces

struct nvmem_device {
	const char		*name;
	struct module		*owner;
	struct device		dev;
	int			stride;
	int			word_size;
	int			ncells;
	int			id;
	int			users;
	size_t			size;
	bool			read_only;
	int			flags;
	struct bin_attribute	eeprom;
	struct device		*base_dev;
	nvmem_reg_read_t	reg_read;
	nvmem_reg_write_t	reg_write;
	void *priv;
};

 Aquí no hay función de sonda, de lo contrario, si (drv->probe) no se mantiene.

En este punto, he terminado de leer el marco del controlador I2C. Introduzcamos brevemente otras cosas relacionadas con el controlador I2C.

5 herramientas i2c

i2c-tools es un conjunto de herramientas útiles y un conjunto de códigos de muestra.

5.1 ¿Por qué i2c-tools es un conjunto de herramientas útil?

Por qué i2c-tools es un conjunto de herramientas útiles, porque implementa la función de detección i2cdetect, la función de lectura i2cget, la función de escritura i2cset y la función de transferencia i2ctransfer. Podemos usar estos comandos para operar o depurar dispositivos I2C, como 

5.2 ¿Por qué i2c-tools también es un conjunto de códigos de muestra?

¿Por qué i2c-tools también es un conjunto de códigos de muestra? Por ejemplo, si se utiliza el bus I2C para la transmisión, podemos ver la implementación de su código en ./tools/i2ctransfer.c.

Luego podemos imitar su proceso para operar nuestros propios dispositivos I2C. La implementación específica de la función anterior, como set_slave_addr, está en ./tools/i2cbusses.c, y debemos incluir el archivo ./tools/i2cbusses.c al escribir el código. .

Si se utiliza el bus SMBus para la transmisión, el código de muestra en i2cget.c e i2cset.c es el siguiente

 Luego, si queremos utilizar el bus SMBus para operar nuestro dispositivo i2c, podemos imitar su código. Por ejemplo, la implementación específica de la función i2c_smbus_access anterior está en el archivo ./lib/smbus.c, por lo que cuando escribimos el código , necesitamos incluir el archivo ./lib/smbus.c.

Por ejemplo, escriba un programa de prueba para leer y escribir eeprom


#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <i2c/smbus.h>
#include "i2cbusses.h"
#include <time.h>


/* ./at24c02 <i2c_bus_number> w "100ask.taobao.com"
 * ./at24c02 <i2c_bus_number> r
 */

int main(int argc, char **argv)
{
	unsigned char dev_addr = 0x50;
	unsigned char mem_addr = 0;
	unsigned char buf[32];

	int file;
	char filename[20];
	unsigned char *str;

	int ret;

	struct timespec req;
	
	if (argc != 3 && argc != 4)
	{
		printf("Usage:\n");
		printf("write eeprom: %s <i2c_bus_number> w string\n", argv[0]);
		printf("read  eeprom: %s <i2c_bus_number> r\n", argv[0]);
		return -1;
	}

	file = open_i2c_dev(argv[1][0]-'0', filename, sizeof(filename), 0);
	if (file < 0)
	{
		printf("can't open %s\n", filename);
		return -1;
	}

	if (set_slave_addr(file, dev_addr, 1))
	{
		printf("can't set_slave_addr\n");
		return -1;
	}

	if (argv[2][0] == 'w')
	{
		// write str: argv[3]
		str = argv[3];

		req.tv_sec  = 0;
		req.tv_nsec = 20000000; /* 20ms */
		
		while (*str)
		{
			// mem_addr, *str
			// mem_addr++, str++
			ret = i2c_smbus_write_byte_data(file, mem_addr, *str);
			if (ret)
			{
				printf("i2c_smbus_write_byte_data err\n");
				return -1;
			}
			// wait tWR(10ms)
			nanosleep(&req, NULL);
			
			mem_addr++;
			str++;
		}
		ret = i2c_smbus_write_byte_data(file, mem_addr, 0); // string end char
		if (ret)
		{
			printf("i2c_smbus_write_byte_data err\n");
			return -1;
		}
	}
	else
	{
		// read
		ret = i2c_smbus_read_i2c_block_data(file, mem_addr, sizeof(buf), buf);
		if (ret < 0)
		{
			printf("i2c_smbus_read_i2c_block_data err\n");
			return -1;
		}
		
		buf[31] = '\0';
		printf("get data: %s\n", buf);
	}
	
	return 0;
	
}

6 controlador universal i2c_dev.c

i2c_dev.c es en realidad un controlador universal o controlador universal, que implementa un

static const struct file_operations i2cdev_fops = {
	.owner		= THIS_MODULE,
	.llseek		= no_llseek,
	.read		= i2cdev_read,
	.write		= i2cdev_write,
	.unlocked_ioctl	= i2cdev_ioctl,
	.open		= i2cdev_open,
	.release	= i2cdev_release,
};

Si usamos el controlador universal i2c_dev.c, entonces no necesitamos agregar i2c_client e i2c_driver, entonces podemos operar directamente el controlador i2c en la capa de aplicación y luego comunicarnos con el dispositivo esclavo montado en el bus I2C, que es equivalente a operar La sincronización específica del hardware se implementa en la aplicación, lo que requiere que los desarrolladores de aplicaciones comprendan tanto la sincronización específica de operación del hardware como el protocolo del bus I2C. Esa es la dirección del dibujo de la línea roja.

7 GPIO analógico I2C

Eche un vistazo breve al archivo ./Linux-4.9.88_just_for_read/drivers/i2c/busses/i2c-gpio.c. Comencemos con la función de entrada.

static struct platform_driver i2c_gpio_driver = {
	.driver		= {
		.name	= "i2c-gpio",
		.of_match_table	= of_match_ptr(i2c_gpio_dt_ids),
	},
	.probe		= i2c_gpio_probe,
	.remove		= i2c_gpio_remove,
};
static int __init i2c_gpio_init(void)
{
	int ret;

	ret = platform_driver_register(&i2c_gpio_driver);
	if (ret)
		printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret);

	return ret;
}

La relación de llamada a la función no es más que lo mismo. 

i2c_gpio_init

    plataforma_driver_register

        __platform_driver_register

        drv->driver.propietario = propietario;

        drv->driver.bus = &platform_bus_type;

        drv->driver.probe = plataforma_drv_probe;

        drv->driver.remove = plataforma_drv_remove;

        drv->driver.shutdown = plataforma_drv_shutdown;

        driver_register(&drv->controlador);

            bus_add_driver

                  controlador_attach(drv);

                        __conductor_attach

                            driver_match_device(drv, dev);

                                 drv->bus->partido? drv->bus->coincidencia(dev, drv): 1;

                             driver_probe_device(drv, dev);

                                  realmente_probe(dev, drv);

                                      dev->bus->sonda y drv->sonda(dev)

Después de la coincidencia, se llama a la función i2c_gpio_probe en la estructura del controlador, y luego primero se llama a la función of_i2c_gpio_get_props para obtener la información de gpio y algunos atributos del árbol de dispositivos, es decir, la frecuencia y la configuración de drenaje abierto, y luego el pin sda y Se obtienen los pines scl y luego, de acuerdo con Configure el adaptador con el valor obtenido en el árbol de dispositivos, luego use i2c_bit_add_numbered_bus para registrar el adaptador y luego llame a __i2c_bit_add_bus en i2c_bit_add_numbered_bus, donde se configura el algoritmo algo, y luego agregue_adapter.

i2c_gpio_probe

    of_i2c_gpio_get_pins

    devm_gpio_request(&pdev->dev, sda_pin, "sda");

    devm_gpio_request(&pdev->dev, scl_pin, "scl");

    i2c_bit_add_numbered_bus(adaptar);

        __i2c_bit_add_bus(adaptar, i2c_add_numbered_adapter);         

            adapt->algo = &i2c_bit_algo;

            adaptar->reintentos = 3;

            si (bit_adap->getscl == NULL)

                adapta->quirks = &i2c_bit_quirk_no_clk_stretch;

            ret = add_adapter(adap);//add_adapter就是i2c_add_numbered_adapter

                No miraré las llamadas posteriores, he analizado otras similares muchas veces antes.

8. Método de aprendizaje Feynman: grabé un video de aprendizaje que explica el marco del controlador del subsistema I2C.

8 minutos para explicar el marco general del controlador I2C del kernel de Linux_bilibili_bilibili

Referencias :

Manual de desarrollo de controladores atómicos puntuales

Video de aprendizaje integral del maestro Wei Dongshan sobre el desarrollo del conductor

Código fuente del kernel Linux4.9.88

7. Controlador de dispositivo de plataforma: [Wildfire] Guía práctica de desarrollo de controladores integrados para Linux, basada en documentos de la serie i.MX6ULL

Dos ideas para la implementación del controlador I2C (i2c-dev.c y i2c-core.c)_Blog-CSDN de The Taking-off Snail

https://www.cnblogs.com/happybirthdaytoyou/p/13594060.html  

[I2C] Controlador general i2c-dev análisis_i2c_dev_init_ZHONGCAI0901's Blog-CSDN Blog

Explicación detallada del subsistema I2C del kernel de Linux: solo lea este artículo_Blog-CSDN de The Taking-off Snail

https://www.cnblogs.com/burnk/p/17454052.html

Le llevará diez minutos comprender la arquitectura del software Linux I2C_bilibili_bilibili

I2C——Registro i2c_driver y función de detección de sonda que llama al blog-CSDN del blog de Process_i2c probe_lxllinux

El procesamiento del kernel del árbol de dispositivos __device_node se convierte en platform_device_initcall_from_entry_blog-CSDN de Chen Hongwei  https://www.cnblogs.com/schips/p/linux_driver_device_node_to_platform_device.html

Modelo de dispositivo Linux device_add_Luzhou Blog de Zhuohu Chong-blog CSDN

https://www.cnblogs.com/yangjiguang/p/6220600.html

Adición de dispositivo i2c, carga de controladores y coincidencia de dispositivos_Adición del dispositivo de frecuencia cardíaca Android i2c_bruk_spp's blog-CSDN blog

[I2C] Análisis del subsistema Linux I2C_blog-CSDN de blog de ZHONGCAI0901

Supongo que te gusta

Origin blog.csdn.net/u013171226/article/details/131761869
Recomendado
Clasificación