Soy relativamente nuevo en controladores de dispositivos en Linux. Lo que estoy tratando de lograr es que al arrancar mi Raspberry, un controlador RGB externo recibirá un comando i2c para que pueda ver un LED iluminado en el arranque.
Mi enfoque está tratando de lograr esto a través de un módulo de kernel que se cargará en el arranque. Intenté muchas cosas para lograr esto, pero en este momento siento que tengo una brecha de conocimiento. Tal vez alguien me puede ayudar? (tenga en cuenta que no es un problema de hardware, desde el espacio del usuario puedo enviar comandos al dispositivo).
El código de mi módulo de kernel es el siguiente:
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/regmap.h>
MODULE_AUTHOR ("Niels");
MODULE_DESCRIPTION("driver rgb led");
MODULE_LICENSE("GPL");
/*CAT3626 control registers*/
#define CAT3626_ADDRESS 0x66
#define CAT3626_ENA 0x03
#define CAT3626_REGA 0x00
#define CAT3626_REGB 0x01
#define CAT3626_REGC 0x02
struct cat3626 {
struct device *dev;
struct regmap * regmap;
};
enum {
cat3626,
};
static const struct of_device_id cat3626_dt_ids[] = {
{ .compatible = "onsemi,cat3626", .data = (void *)cat3626},
{ }
};
MODULE_DEVICE_TABLE(of, cat3626_dt_ids);
static const struct i2c_device_id cat3626_id[] = {
{"cat3626",cat3626},
{ }
};
MODULE_DEVICE_TABLE(i2c, cat3626_id);
static const struct regmap_config regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static int cat3626_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct cat3626 *cat3626;
const struct of_device_id *match;
int ret;
cat3626 = devm_kzalloc(&client->dev, sizeof(struct cat3626), GFP_KERNEL);
if (!cat3626){
return -ENOMEM;
}
dev_set_drvdata(&client->dev, cat3626);
cat3626->dev = &client->dev;
cat3626->regmap = devm_regmap_init_i2c(client, ®map_config);
if (IS_ERR(cat3626->regmap)) {
dev_err(cat3626->dev, "regmap allocation failed\n");
return PTR_ERR(cat3626->regmap);
}
i2c_set_clientdata(client, cat3626);
match = of_match_device(cat3626_dt_ids, &client->dev);
if (!match) {
dev_err(&client->dev, "unknown device model\n");
return -ENODEV;
}
ret = i2c_smbus_write_byte_data(client, CAT3626_ENA, 0x30); /* write LED C on*/
ret = i2c_smbus_write_byte_data(client, CAT3626_REGC, 19); /* write mA*/
return ret;
}
static struct i2c_driver cat3626_driver = {
.driver = {
.name = "cat3626",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(cat3626_dt_ids),
},
.probe = cat3626_probe,
.remove = cat3626_remove,
.id_table = cat3626_id,
};
module_i2c_driver(cat3626_driver);
Aquí está el archivo MAKE:
ifneq ($(KERNELRELEASE),)
obj-m := hiber_rgb_driver.o
else
KERNELDIR ?= \
/lib/modules/`uname -r`/build/
PWD := `pwd`
default:
$(MAKE) -C $(KERNELDIR) \
M=$(PWD) modules
endif
clean:
rm -f *.ko *.o Module* *mod*
En el archivo /boot/config.txt he agregado esto:
dtoverlay = i2c-gpio, bus = 80, i2c_gpio_delay_us = 2, i2c_gpio_sda = 44, i2c_gpio_scl = 45.
Además hice un dtoverlay personalizado:
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = <&i2c80>;
__overlay__ {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
cat3626: cat3626@66 {
compatible = "onsemi,cat3626";
reg = <0x66>;
clock-frequency = <400000>;
};
};
};
};
Desafortunadamente en el arranque no pasa nada. Todo lo que obtengo del dmesg de arranque es lo siguiente:
rgb_driver: loading out-of-tree module taints kernel
¿Alguien puede darme alguna ayuda, o tal vez un enfoque diferente para lograr mi objetivo?
¡Gracias por adelantado!
/etc/init.d/
o similar en lugar de un módulo del núcleo.Respuestas:
Un par de cosas a tener en cuenta: un kernel contaminado a menudo tiene una función reducida y probablemente no desee ir allí si no tiene que hacerlo. Intentaría resolver el problema de la contaminación. He creado módulos de kernel como independientes y no he tocado el problema de la contaminación. Es posible que desee volver a visitar su archivo MAKE, este es un archivo MAKE más estándar de construcción de módulos con un par de arrugas, ya que, por supuesto, está cruzando la compilación:
y construirlo con algo como:
Entonces ahí está eso.
A continuación, las cosas de la sonda de su dispositivo se ven interesantes. No tengo tiempo para depurarlo, pero sugeriría agregar algunos printk para verificar que la sonda está siendo golpeada. Si es así, genial, es solo una cuestión de averiguar por qué no estás 'haciendo coincidir'. Si no está siendo golpeado, sigue leyendo ...
Como probablemente sepa, los buses i2c son un poco especiales cuando se trata de sondeo de dispositivos. No existe un sondeo real automatizado o mágico que normalmente sucedería en un bus PCI. En su lugar, debe construir un árbol de dispositivos que el kernel pueda recorrer en el momento del arranque para completar todas las sondas.
Veo que has creado un fragmento de superposición. Debe asegurarse de que se compila en un binario de código de bytes '.dtb' que el kernel puede analizar y luego colocar en el lugar correcto en su medio de arranque donde grub puede encontrarlo.
También es posible que deba actualizar la dtb maestra de su dispositivo para hacer referencia a esta superposición, de modo que el núcleo sepa a dónde podría ir. Piense en la dtb del dispositivo como un árbol de navidad artificial, y en la superposición como una extremidad que podría unirse en algún momento en el futuro; deberá especificar los puntos de conexión en la dtb del dispositivo. Ojalá pudiera ser más preciso aquí, pero la esperanza te pone en la dirección correcta al menos en este punto.
fuente