GNU/Linux >> Linux Esercitazione >  >> Linux

Scopo ioctl I2C_SLAVE

Se usi il read() e write() metodi, chiamando ioctl con I2C_SLAVE una volta è sufficiente. Puoi anche usare I2C_SLAVE_FORCE se il dispositivo è già in uso.

Tuttavia non ho ancora trovato un modo coerente per leggere registri specifici per ogni dispositivo utilizzando il read()/write() metodi.


Esistono tre metodi principali per comunicare con i dispositivi i2c dallo spazio utente.

1. IOCTL I2C_RDWR

Questo metodo consente la lettura/scrittura simultanea e l'invio di una sequenza ininterrotta di messaggi. Non tutti i dispositivi i2c supportano questo metodo.

Prima di eseguire l'i/o con questo metodo, dovresti controllare se il dispositivo supporta questo metodo usando un ioctl I2C_FUNCS funzionamento.

Usando questo metodo, non è necessario eseguire un ioctl I2C_SLAVE operazione:viene eseguita dietro le quinte utilizzando le informazioni incorporate nei messaggi.

2. IOCTLSMBUS

Questo metodo di i/o è più potente ma il codice risultante è più prolisso. Questo metodo può essere utilizzato se il dispositivo non supporta I2C_RDWR metodo.

Usando questo metodo, fai è necessario eseguire un ioctl I2C_SLAVE operazione (o, se il dispositivo è occupato, un I2C_SLAVE_FORCE funzionamento).

3. I/O SYSFS

Questo metodo utilizza le chiamate di sistema di i/o file di base read() e write() . Con questo metodo non è possibile eseguire operazioni sequenziali ininterrotte. Questo metodo può essere utilizzato se il dispositivo non supporta I2C_RDWR metodo.

Usando questo metodo, fai è necessario eseguire un ioctl I2C_SLAVE operazione (o, se il dispositivo è occupato, un I2C_SLAVE_FORCE funzionamento).

Non riesco a pensare a nessuna situazione in cui questo metodo sarebbe preferibile ad altri, a meno che tu non abbia bisogno che il chip venga trattato come un file.

Esempio IOCTL completo

Non ho testato questo esempio, ma mostra il flusso concettuale della scrittura su un dispositivo i2c.-- rilevare automaticamente se usare ioctl I2C_RDWR o tecnica 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;
}

Non sono troppo sicuro se questo aiuta perché non uso ioctl I2C_RDWR ma ho usato con successo il seguente codice:

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, ___);

Tutto quello che faccio è impostare I2C_SLAVE_FORCE una volta all'inizio e dopo posso leggere e scrivere quanto voglio.

PS - Questo è solo un esempio di codice e ovviamente dovresti controllare i ritorni di tutte queste funzioni. Sto usando questo codice per comunicare con un chip I/O digitale. Le due funzioni i2c_* sono solo wrapper che chiamano ioctl(fd, I2C_SMBUS, &args); dove args è un tipo struct i2c_smbus_ioctl_data.


Linux
  1. Lo scopo dell'utilizzo di un Fifo contro un file temporaneo o una pipe?

  2. Lo scopo del comando "installa"?

  3. Linux – Scopo della directory /net?

  4. Qual è lo scopo dell'utente "mysql.sys@localhost".

  5. Qual è lo scopo del file .bashrc in Linux

Crea un Cloud Server generico

Driver di dispositivo Linux IOCTL

Ottieni indirizzi IPv6 in Linux usando ioctl

Qual è lo scopo di cd ` (backtick)?

Qual è lo scopo della directory setgid?

Qual è lo scopo di un '-' finale in un'applicazione Kubernetes -f -