GNU/Linux >> Tutoriels Linux >  >> Linux

Objectif de l'ioctl I2C_SLAVE

Si vous utilisez le read() et write() méthodes, appelant ioctl avec I2C_SLAVE Une fois est assez. Vous pouvez également utiliser I2C_SLAVE_FORCE si l'appareil est déjà utilisé.

Cependant, je n'ai pas encore trouvé de moyen cohérent de lire des registres spécifiques pour chaque appareil en utilisant le read()/write() méthodes.


Il existe trois méthodes principales de communication avec les appareils i2c depuis l'espace utilisateur.

1. IOCTL I2C_RDWR

Cette méthode permet une lecture/écriture simultanée et l'envoi d'une séquence ininterrompue de messages. Tous les appareils i2c ne prennent pas en charge cette méthode.

Avant d'effectuer des E/S avec cette méthode, vous devez vérifier si le périphérique prend en charge cette méthode à l'aide d'un ioctl I2C_FUNCS opération.

En utilisant cette méthode, vous ne faites pas besoin d'effectuer un ioctl I2C_SLAVE opération :elle est effectuée en coulisses à l'aide des informations intégrées dans les messages.

2. IOCTL SMBUS

Cette méthode d'e/s est plus puissante mais le code résultant est plus verbeux. Cette méthode peut être utilisée si l'appareil ne prend pas en charge le I2C_RDWR méthode.

En utilisant cette méthode, vous faites besoin d'effectuer un ioctl I2C_SLAVE opération (ou, si l'appareil est occupé, un I2C_SLAVE_FORCE opération).

3. E/S SYSFS

Cette méthode utilise les appels système d'e/s de fichier de base read() et write() . Des opérations séquentielles ininterrompues ne sont pas possibles avec cette méthode. Cette méthode peut être utilisée si l'appareil ne prend pas en charge le I2C_RDWR méthode.

En utilisant cette méthode, vous faites besoin d'effectuer un ioctl I2C_SLAVE opération (ou, si l'appareil est occupé, un I2C_SLAVE_FORCE opération).

Je ne vois aucune situation où cette méthode serait préférable à d'autres, à moins que vous n'ayez besoin que la puce soit traitée comme un fichier.

Exemple IOCTL complet

Je n'ai pas testé cet exemple, mais il montre le flux conceptuel d'écriture sur un périphérique i2c.-- détectant automatiquement s'il faut utiliser l'ioctl I2C_RDWR ou technique smbus.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include <errno.h>
#include <string.h>

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>

#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE  0x00

int i2c_ioctl_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data, size_t size)
{
    int i, j = 0;
    int ret;
    uint8_t *buf;
    // the extra byte is for the regaddr
    size_t buff_size = 1 + size;

    buf = malloc(buff_size);
    if (buf == NULL) {
        return -ENOMEM;
    }

    buf[j ++] = regaddr;
    for (i = 0; i < size / sizeof(uint16_t); i ++) {
        buf[j ++] = (data[i] & 0xff00) >> 8;
        buf[j ++] = data[i] & 0xff;
    }

    struct i2c_msg messages[] = {
        {
            .addr = dev,
            .buf = buf,
            .len = buff_size,
        },
    };

    struct i2c_rdwr_ioctl_data payload = {
        .msgs = messages,
        .nmsgs = sizeof(messages) / sizeof(messages[0]),
    };

    ret = ioctl(fd, I2C_RDWR, &payload);
    if (ret < 0) {
        ret = -errno;
    }

    free (buf);
    return ret;
}

int i2c_ioctl_smbus_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data, size_t size)
{
    int i, j = 0;
    int ret;
    uint8_t *buf;

    buf = malloc(size);
    if (buf == NULL) {
        return -ENOMEM;
    }

    for (i = 0; i < size / sizeof(uint16_t); i ++) {
        buf[j ++] = (data[i] & 0xff00) >> 8;
        buf[j ++] = data[i] & 0xff;
    }

    struct i2c_smbus_ioctl_data payload = {
        .read_write = I2C_SMBUS_WRITE,
        .size = I2C_SMBUS_WORD_DATA,
        .command = regaddr,
        .data = (void *) buf,
    };

    ret = ioctl (fd, I2C_SLAVE_FORCE, dev);
    if (ret < 0)
    {
        ret = -errno;
        goto exit;
    }

    ret = ioctl (fd, I2C_SMBUS, &payload);
    if (ret < 0)
    {
        ret = -errno;
        goto exit;
    }

exit:
    free(buf);
    return ret;
}

int i2c_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data, size_t size)
{
    unsigned long funcs;

    if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
        return -errno;
    }

    if (funcs & I2C_FUNC_I2C) {
        return i2c_ioctl_write (fd, dev, regaddr, data, size);
    } else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {
        return i2c_ioctl_smbus_write (fd, dev, regaddr, data, size);
    } else {
        return -ENOSYS;
    }
}

int parse_args (uint8_t *regaddr, uint16_t *data, size_t size, char *argv[])
{
    char *endptr;
    int i;

    *regaddr = (uint8_t) strtol(argv[1], &endptr, 0);
    if (errno || endptr == argv[1]) {
        return -1;
    }

    for (i = 0; i < size / sizeof(uint16_t); i ++) {
        data[i] = (uint16_t) strtol(argv[i + 2], &endptr, 0);
        if (errno || endptr == argv[i + 2]) {
            return -1;
        }
    }

    return 0;
}

void usage (int argc, char *argv[])
{
    fprintf(stderr, "Usage: %s regaddr data [data]*\n", argv[0]);
    fprintf(stderr, "  regaddr   The 8-bit register address to write to.\n");
    fprintf(stderr, "  data      The 16-bit data to be written.\n");
    exit(-1);
}

int main (int argc, char *argv[])
{
    uint8_t regaddr;
    uint16_t *data;
    size_t size;
    int fd;
    int ret = 0;

    if (argc < 3) {
        usage(argc, argv);
    }

    size = (argc - 2) * sizeof(uint16_t);
    data = malloc(size);
    if (data == NULL) {
        fprintf (stderr, "%s.\n", strerror(ENOMEM));
        return -ENOMEM;
    }

    if (parse_args(&regaddr, data, size, argv) != 0) {
        free(data);
        usage(argc, argv);
    }

    fd = open(I2C_ADAPTER, O_RDWR | O_NONBLOCK);
    ret = i2c_write(fd, I2C_DEVICE, regaddr, data);
    close(fd);

    if (ret) {
        fprintf (stderr, "%s.\n", strerror(-ret));
    }

    free(data);

    return ret;
}

Je ne sais pas trop si cela aide car je n'utilise pas ioctl I2C_RDWR mais j'utilise le code suivant avec succès :

int fd;
fd = open("/dev/i2c-5", O_RDWR);
ioctl(fd, I2C_SLAVE_FORCE, 0x20);
i2c_smbus_write_word_data(fd, ___, ___);
i2c_smbus_read_word_data(fd, ___);

Tout ce que je fais est de définir I2C_SLAVE_FORCE une fois au début et je peux lire et écrire autant que je veux après cela.

PS - Ceci n'est qu'un exemple de code et vous devriez évidemment vérifier les retours de toutes ces fonctions. J'utilise ce code pour communiquer avec une puce d'E/S numérique. Les deux fonctions i2c_* ne sont que des wrappers qui appellent ioctl(fd, I2C_SMBUS, &args); où args est un type struct i2c_smbus_ioctl_data.


Linux
  1. Le but d'utiliser un Fifo Vs un fichier temporaire ou un tuyau ?

  2. Le but de la commande "install" ?

  3. Linux – But du répertoire /net ?

  4. Le but de la commande de hachage ?

  5. Pourquoi utiliser Setuid() dans les programmes Suid ?

Créer un serveur cloud à usage général

Pilote de périphérique Linux IOCTL

Obtenir des adresses IPv6 sous Linux en utilisant ioctl

Quel est le but de cd ` (backtick) ?

Quel est le but du répertoire setgid ?

Quel est le but d'un '-' final dans une application Kubernetes -f -