pondělí 14. března 2016

Bulid jaderného balíčku pro hwmon

Nejprve je třeba naklonovat verzi OpenWRT pro aktuálně používané jádro. Pomocí příkazu git tag, se nám zobrazí seznam "otagovaných" verzí. Pro verzi Turris OS 2.8 je to vyznačené vydání deploy-2015-12-18.

$ git tag
deploy-2014-04-24
deploy-2015-11-25
deploy-2015-12-18
gen-certuma-0.v1
gen-certuma-0.v2
hotfix-2015-08-18
upstream-merge-2014-06-27

A když už máme představu, jakou verzi požadujeme, tak si ji prostřednictvím checkout můžeme stáhnout.


git checkout deploy-2015-12-18

Nyní necháme jádro přeložit, tak aby se nám stáhly všechny zdroje. Při tvorbě balíčku jsem vycházel z kmod-hwmon-sht21, ten je nutné povolil v make menuconfig (podmenu Kernel modules -> Hardware Monitoring Support).

Soubor příkladu by měl pro jedno zařízení vytvořit dva virtuální soubory, které při čtení vrací inkrementující se hodnoty, viz následující soubor pokus.c.

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/jiffies.h>

/**
* struct pokus - pokus device specific data
* @hwmon_dev: device registered with hwmon
* @lock: mutex to protect measurement values
* @valid: only 0 before first measurement is taken
* @last_update: time of last update (jiffies)
* @number1:
* @number2:
*/
struct pokus
{
        struct device *hwmon_dev;
        struct mutex lock;
        char valid;
        unsigned long last_update;
        int number1;
        int number2;
};

/**
* pokus_show_number1() - return value in sysfs
* @dev: device
* @attr: device attribute
* @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to
*
* Will be called on read access to number1 sysfs attribute.
* Returns number of bytes written into buffer, negative errno on error.
*/
static ssize_t pokus_show_number1(struct device *dev, struct device_attribute *attr, char *buf)
{
        struct pokus *pokus = dev_get_drvdata(dev);
        /*int ret;

        ret = pokus_update_measurements(dev);
        if (ret < 0) return ret;*/
        return sprintf(buf, "%d\n", pokus->number1++);
}

/**
* pokus_show_number2() - return value in sysfs
* @dev: device
* @attr: device attribute
* @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to
*
* Will be called on read access to number1 sysfs attribute.
* Returns number of bytes written into buffer, negative errno on error.
*/
static ssize_t pokus_show_number2(struct device *dev, struct device_attribute *attr, char *buf)
{
        struct pokus *pokus = dev_get_drvdata(dev);
        /*int ret;

        ret = pokus_update_measurements(dev);
        if (ret < 0) return ret;*/
        return sprintf(buf, "%d\n", pokus->number2++);
}

/* sysfs attributes */
static SENSOR_DEVICE_ATTR(number1, S_IRUGO, pokus_show_number1,
        NULL, 0);
static SENSOR_DEVICE_ATTR(number2, S_IRUGO, pokus_show_number2,
        NULL, 0);

static struct attribute *pokus_attributes[] =
{
        &sensor_dev_attr_number1.dev_attr.attr,
        &sensor_dev_attr_number2.dev_attr.attr,
        NULL
};

static const struct attribute_group pokus_attr_group = {
        .attrs = pokus_attributes,
};

static int pokus_probe(struct i2c_client *client,
        const struct i2c_device_id *id)
{
        struct pokus *pokus;
        int err;

        if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
        {
                dev_err(&client->dev, "adapter does not support SMBus word transactions\n");
                return -ENODEV;
        }

        pokus = devm_kzalloc(&client->dev, sizeof(*pokus), GFP_KERNEL);
        if (!pokus) return -ENOMEM;

        i2c_set_clientdata(client, pokus);

        mutex_init(&pokus->lock);

        err = sysfs_create_group(&client->dev.kobj, &pokus_attr_group);
        if (err) {
                dev_dbg(&client->dev, "could not create sysfs files\n");
                return err;
        }
        pokus->hwmon_dev = hwmon_device_register(&client->dev);
        if (IS_ERR(pokus->hwmon_dev)) {
                dev_dbg(&client->dev, "unable to register hwmon device\n");
                err = PTR_ERR(pokus->hwmon_dev);
                goto fail_remove_sysfs;
        }

        dev_info(&client->dev, "initialized\n");

        return 0;

fail_remove_sysfs:
        sysfs_remove_group(&client->dev.kobj, &pokus_attr_group);
        return err;

}

/**
 * pokus_remove() - remove device
 * @client: I2C client device
 */
static int pokus_remove(struct i2c_client *client)
{
        struct pokus *pokus = i2c_get_clientdata(client);

        hwmon_device_unregister(pokus->hwmon_dev);
        sysfs_remove_group(&client->dev.kobj, &pokus_attr_group);

        return 0;
}

/* Device ID table */
static const struct i2c_device_id pokus_id[] =
{
        { "pokus", 0 },
        { }
};
MODULE_DEVICE_TABLE(i2c, pokus_id);

static struct i2c_driver pokus_driver =
{
        .driver.name = "pokus",
        .probe       = pokus_probe,
        .remove      = pokus_remove,
        .id_table    = pokus_id,
};

module_i2c_driver(pokus_driver);

MODULE_AUTHOR("JFila <jfila@xxx.cz>");
MODULE_DESCRIPTION("Pokus driver");
MODULE_LICENSE("GPL");



Do Kconfig doplnit následující definici (build_dir/target-powerpc_8540_uClibc-0.9.33.2/linux-mpc85xx_p2020-nand/linux-3.10.49/drivers/hwmon/Kconfig)

config SENSORS_POKUS
tristate "JFíla pokus driver."
depends on GPIOLIB
help
Pokusný balíček pro hwmon

Do Makefile (build_dir/target-powerpc_8540_uClibc-0.9.33.2/linux-mpc85xx_p2020-nand/linux-3.10.49/drivers/hwmon/Makefile) přidat binárku s názvem balíčku (pokus.o)

obj-$(CONFIG_SENSORS_POKUS) += pokus.o

A hwmon.mk také doplnit popis balíčku a jeho závislostí. (package/kernel/linux/modules/hwmon.mk)

define KernelPackage/hwmon-pokus
  TITLE:=pokus device for test
  KCONFIG:=CONFIG_SENSORS_POKUS
  FILES:=$(LINUX_DIR)/drivers/hwmon/pokus.ko
  AUTOLOAD:=$(call AutoProbe,pokus)
  $(call AddDepends/hwmon,+kmod-i2c-core)
endef

define KernelPackage/hwmon-pokus/description
 Kernel module for pokus device (for test only)
endef

$(eval $(call KernelPackage,hwmon-pokus))

Hotový modul můžeme instalovat jako balíček ale pozor na hash jádra, pro otestování stačí pokus.ko nakopírovat do /lib/modules/3.10.49-xxxxx

Zavedeme modul do jádra a je to:

root@JFila:/# insmod pokus.ko
root@JFila:/# lsmod | grep pokus
hwmon                    990  3 pokus
pokus                   1468  0

Nyní by již mělo stačit vytvořit nové zařízení:

root@JFila:/# echo pokus 0x27 > /sys/bus/i2c/devices/i2c-1/new_device
root@JFila:/# cd /sys/bus/i2c/devices/
0-004c/  0-006f/  1-0027/  1-0040/  i2c-0/   i2c-1/   i2c-2/
root@JFila:/# cd /sys/bus/i2c/devices/1-0027/
root@JFila:/sys/devices/soc.0/ffe03100.i2c/i2c-1/1-0027# ls
driver     hwmon      modalias   name       number1    number2    power      subsystem  uevent

A čtením number jedna bychom měli získávat inkrementující hodnotu:

root@JFila:/sys/devices/soc.0/ffe03100.i2c/i2c-1/1-0027# cat number1
0
root@JFila:/sys/devices/soc.0/ffe03100.i2c/i2c-1/1-0027# cat number1
1
root@JFila:/sys/devices/soc.0/ffe03100.i2c/i2c-1/1-0027# cat number1
2
root@JFila:/sys/devices/soc.0/ffe03100.i2c/i2c-1/1-0027# cat number1
3

https://blog.root.cz/posvic/jak-napsat-ovladac-pro-zarizeni-usb-pro-linux/

Žádné komentáře:

Okomentovat