Exemple #1
0
int hs_poll(hs_poll_source *sources, unsigned int count, int timeout)
{
    assert(sources);
    assert(count);
    assert(count <= HS_POLL_MAX_SOURCES);

    fd_set fds;
    uint64_t start;
    int maxfd, r;

    FD_ZERO(&fds);
    maxfd = 0;
    for (unsigned int i = 0; i < count; i++) {
        if (sources[i].desc >= FD_SETSIZE) {
            for (unsigned int j = i; j < count; j++)
                sources[j].ready = 0;

            return hs_error(HS_ERROR_SYSTEM, "Cannot select() on descriptor %d (too big)",
                            sources[i].desc);
        }

        FD_SET(sources[i].desc, &fds);
        sources[i].ready = 0;

        if (sources[i].desc > maxfd)
            maxfd = sources[i].desc;
    }

    start = hs_millis();
restart:
    if (timeout >= 0) {
        int adjusted_timeout;
        struct timeval tv;

        adjusted_timeout = hs_adjust_timeout(timeout, start);
        tv.tv_sec = adjusted_timeout / 1000;
        tv.tv_usec = (adjusted_timeout % 1000) * 1000;

        r = select(maxfd + 1, &fds, NULL, NULL, &tv);
    } else {
        r = select(maxfd + 1, &fds, NULL, NULL, NULL);
    }
    if (r < 0) {
        if (errno == EINTR)
            goto restart;
        return hs_error(HS_ERROR_SYSTEM, "poll() failed: %s", strerror(errno));
    }
    if (!r)
        return 0;

    for (unsigned int i = 0; i < count; i++)
        sources[i].ready = !!FD_ISSET(sources[i].desc, &fds);

    return r;
}
Exemple #2
0
int hs_device_open(hs_device *dev, hs_handle **rh)
{
    assert(dev);
    assert(rh);

    if (dev->state != HS_DEVICE_STATUS_ONLINE)
        return hs_error(HS_ERROR_NOT_FOUND, "Device '%s' is not connected", dev->path);

    return (*dev->vtable->open)(dev, rh);
}
Exemple #3
0
ssize_t hs_hid_send_feature_report(hs_handle *h, const uint8_t *buf, size_t size)
{
    assert(h);
    assert(h->dev->type == HS_DEVICE_TYPE_HID);
    assert(buf);

    if (size < 2)
        return 0;

    // Timeout behavior?
    BOOL success = HidD_SetFeature(h->handle, (char *)buf, (DWORD)size);
    if (!success)
        return hs_error(HS_ERROR_IO, "I/O error while writing to '%s'", h->dev->path);

    return (ssize_t)size;
}
Exemple #4
0
int hs_poll(hs_poll_source *sources, unsigned int count, int timeout)
{
    assert(sources);
    assert(count);
    assert(count <= HS_POLL_MAX_SOURCES);

    HANDLE handles[HS_POLL_MAX_SOURCES];

    for (unsigned int i = 0; i < count; i++) {
        handles[i] = sources[i].desc;
        sources[i].ready = 0;
    }

    DWORD ret = WaitForMultipleObjects((DWORD)count, handles, FALSE,
                                       timeout < 0 ? INFINITE : (DWORD)timeout);
    if (ret == WAIT_FAILED)
        return hs_error(HS_ERROR_SYSTEM, "WaitForMultipleObjects() failed: %s",
                        hs_win32_strerror(0));

    for (unsigned int i = 0; i < count; i++)
        sources[i].ready = (i == ret);

    return ret < count;
}
Exemple #5
0
ssize_t hs_hid_read(hs_handle *h, uint8_t *buf, size_t size, int timeout)
{
    assert(h);
    assert(h->dev->type == HS_DEVICE_TYPE_HID);
    assert(buf);
    assert(size);

    ssize_t r;

    if (timeout) {
        struct pollfd pfd;
        uint64_t start;

        pfd.events = POLLIN;
        pfd.fd = h->fd;

        start = hs_millis();
restart:
        r = poll(&pfd, 1, hs_adjust_timeout(timeout, start));
        if (r < 0) {
            if (errno == EINTR)
                goto restart;

            return hs_error(HS_ERROR_SYSTEM, "poll('%s') failed: %s", h->dev->path,
                            strerror(errno));
        }
        if (!r)
            return 0;
    }

    if (h->numbered_reports) {
        /* Work around a hidraw bug introduced in Linux 2.6.28 and fixed in Linux 2.6.34, see
           https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=5a38f2c7c4dd53d5be097930902c108e362584a3 */
        if (detect_kernel26_byte_bug()) {
            if (size + 1 > h->buf_size) {
                free(h->buf);
                h->buf_size = 0;

                h->buf = malloc(size + 1);
                if (!h->buf)
                    return hs_error(HS_ERROR_MEMORY, NULL);
                h->buf_size = size + 1;
            }

            r = read(h->fd, h->buf, size + 1);
            if (r > 0)
                memcpy(buf, h->buf + 1, (size_t)--r);
        } else {
            r = read(h->fd, buf, size);
        }
    } else {
        r = read(h->fd, buf + 1, size - 1);
        if (r > 0) {
            buf[0] = 0;
            r++;
        }
    }
    if (r < 0) {
        switch (errno) {
        case EAGAIN:
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
        case EWOULDBLOCK:
#endif
            return 0;
        case EIO:
        case ENXIO:
            return hs_error(HS_ERROR_IO, "I/O error while reading from '%s'", h->dev->path);
        }
        return hs_error(HS_ERROR_SYSTEM, "read('%s') failed: %s", h->dev->path, strerror(errno));
    }

    return r;
}
Exemple #6
0
static int open_hidraw_device(hs_device *dev, hs_handle **rh)
{
    hs_handle *h;
    struct hidraw_report_descriptor report;
    int size, r;

    h = calloc(1, sizeof(*h));
    if (!h) {
        r = hs_error(HS_ERROR_MEMORY, NULL);
        goto error;
    }
    h->dev = hs_device_ref(dev);

restart:
    h->fd = open(dev->path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
    if (h->fd < 0) {
        switch (errno) {
        case EINTR:
            goto restart;
        case EACCES:
            r = hs_error(HS_ERROR_ACCESS, "Permission denied for device '%s'", dev->path);
            break;
        case EIO:
        case ENXIO:
        case ENODEV:
            r = hs_error(HS_ERROR_IO, "I/O error while opening device '%s'", dev->path);
            break;
        case ENOENT:
        case ENOTDIR:
            r = hs_error(HS_ERROR_NOT_FOUND, "Device '%s' not found", dev->path);
            break;

        default:
            r = hs_error(HS_ERROR_SYSTEM, "open('%s') failed: %s", dev->path, strerror(errno));
            break;
        }
        goto error;
    }

    r = ioctl(h->fd, HIDIOCGRDESCSIZE, &size);
    if (r < 0) {
        r = hs_error(HS_ERROR_SYSTEM, "ioctl('%s', HIDIOCGRDESCSIZE) failed: %s", h->dev->path,
                     strerror(errno));
        goto error;
    }
    report.size = (uint32_t)size;

    r = ioctl(h->fd, HIDIOCGRDESC, &report);
    if (r < 0) {
        r = hs_error(HS_ERROR_SYSTEM, "ioctl('%s', HIDIOCGRDESC) failed: %s", h->dev->path,
                     strerror(errno));
        goto error;
    }

    parse_descriptor(h, &report);

    *rh = h;
    return 0;

error:
    hs_handle_close(h);
    return r;
}
Exemple #7
0
int hs_serial_set_config(hs_handle *h, const hs_serial_config *config)
{
    assert(h);
    assert(config);

    DCB dcb;
    BOOL success;

    dcb.DCBlength = sizeof(dcb);
    success = GetCommState(h->handle, &dcb);
    if (!success)
        return hs_error(HS_ERROR_SYSTEM, "GetCommState() failed on '%s': %s", h->dev->path,
                        hs_win32_strerror(0));

    switch (config->baudrate) {
    case 0:
        break;
    case 110:
    case 134:
    case 150:
    case 200:
    case 300:
    case 600:
    case 1200:
    case 1800:
    case 2400:
    case 4800:
    case 9600:
    case 19200:
    case 38400:
    case 57600:
    case 115200:
    case 230400:
        dcb.BaudRate = config->baudrate;
        break;
    default:
        return hs_error(HS_ERROR_SYSTEM, "Unsupported baud rate value: %u", config->baudrate);
    }

    switch (config->databits) {
    case 0:
        break;
    case 5:
    case 6:
    case 7:
    case 8:
        dcb.ByteSize = (BYTE)config->databits;
        break;
    default:
        return hs_error(HS_ERROR_SYSTEM, "Invalid data bits setting: %u", config->databits);
    }

    switch (config->stopbits) {
    case 0:
        break;
    case 1:
        dcb.StopBits = ONESTOPBIT;
        break;
    case 2:
        dcb.StopBits = TWOSTOPBITS;
        break;
    default:
        return hs_error(HS_ERROR_SYSTEM, "Invalid stop bits setting: %u", config->stopbits);
    }

    switch (config->parity) {
    case 0:
        break;
    case HS_SERIAL_CONFIG_PARITY_OFF:
        dcb.fParity = FALSE;
        dcb.Parity = NOPARITY;
        break;
    case HS_SERIAL_CONFIG_PARITY_EVEN:
        dcb.fParity = TRUE;
        dcb.Parity = EVENPARITY;
        break;
    case HS_SERIAL_CONFIG_PARITY_ODD:
        dcb.fParity = TRUE;
        dcb.Parity = ODDPARITY;
        break;
    case HS_SERIAL_CONFIG_PARITY_MARK:
        dcb.fParity = TRUE;
        dcb.Parity = MARKPARITY;
        break;
    case HS_SERIAL_CONFIG_PARITY_SPACE:
        dcb.fParity = TRUE;
        dcb.Parity = SPACEPARITY;
        break;
    default:
        return hs_error(HS_ERROR_SYSTEM, "Invalid parity setting: %d", config->parity);
    }

    switch (config->rts) {
    case 0:
        break;
    case HS_SERIAL_CONFIG_RTS_OFF:
        dcb.fRtsControl = RTS_CONTROL_DISABLE;
        dcb.fOutxCtsFlow = FALSE;
        break;
    case HS_SERIAL_CONFIG_RTS_ON:
        dcb.fRtsControl = RTS_CONTROL_ENABLE;
        dcb.fOutxCtsFlow = FALSE;
        break;
    case HS_SERIAL_CONFIG_RTS_FLOW:
        dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
        dcb.fOutxCtsFlow = TRUE;
        break;
    default:
        return hs_error(HS_ERROR_SYSTEM, "Invalid RTS setting: %d", config->rts);
    }

    switch (config->dtr) {
    case 0:
        break;
    case HS_SERIAL_CONFIG_DTR_OFF:
        dcb.fDtrControl = DTR_CONTROL_DISABLE;
        dcb.fOutxDsrFlow = FALSE;
        break;
    case HS_SERIAL_CONFIG_DTR_ON:
        dcb.fDtrControl = DTR_CONTROL_ENABLE;
        dcb.fOutxDsrFlow = FALSE;
        break;
    default:
        return hs_error(HS_ERROR_SYSTEM, "Invalid DTR setting: %d", config->dtr);
    }

    switch (config->xonxoff) {
    case 0:
        break;
    case HS_SERIAL_CONFIG_XONXOFF_OFF:
        dcb.fOutX = FALSE;
        dcb.fInX = FALSE;
        break;
    case HS_SERIAL_CONFIG_XONXOFF_IN:
        dcb.fOutX = FALSE;
        dcb.fInX = TRUE;
        break;
    case HS_SERIAL_CONFIG_XONXOFF_OUT:
        dcb.fOutX = TRUE;
        dcb.fInX = FALSE;
        break;
    case HS_SERIAL_CONFIG_XONXOFF_INOUT:
        dcb.fOutX = TRUE;
        dcb.fInX = TRUE;
        break;
    default:
        return hs_error(HS_ERROR_SYSTEM, "Invalid XON/XOFF setting: %d", config->xonxoff);
    }

    success = SetCommState(h->handle, &dcb);
    if (!success)
        return hs_error(HS_ERROR_SYSTEM, "SetCommState() failed on '%s': %s",
                        h->dev->path, hs_win32_strerror(0));

    return 0;
}
Exemple #8
0
int hs_serial_get_config(hs_handle *h, hs_serial_config *config)
{
    assert(h);
    assert(config);

    DCB dcb;
    BOOL success;

    dcb.DCBlength = sizeof(dcb);
    success = GetCommState(h->handle, &dcb);
    if (!success)
        return hs_error(HS_ERROR_SYSTEM, "GetCommState() failed on '%s': %s", h->dev->path,
                        hs_win32_strerror(0));

    /* 0 is the INVALID value for all parameters, we keep that value if we can't interpret
       a DCB setting (only a cross-platform subset of it is exposed in hs_serial_config). */
    memset(config, 0, sizeof(*config));

    config->baudrate = dcb.BaudRate;
    config->databits = dcb.ByteSize;

    // There is also ONE5STOPBITS, ignore it for now (and ever, probably)
    switch (dcb.StopBits) {
    case ONESTOPBIT:
        config->stopbits = 1;
        break;
    case TWOSTOPBITS:
        config->stopbits = 2;
        break;
    }

    if (dcb.fParity) {
        switch (dcb.Parity) {
        case NOPARITY:
            config->parity = HS_SERIAL_CONFIG_PARITY_OFF;
            break;
        case EVENPARITY:
            config->parity = HS_SERIAL_CONFIG_PARITY_EVEN;
            break;
        case ODDPARITY:
            config->parity = HS_SERIAL_CONFIG_PARITY_ODD;
            break;
        case MARKPARITY:
            config->parity = HS_SERIAL_CONFIG_PARITY_MARK;
            break;
        case SPACEPARITY:
            config->parity = HS_SERIAL_CONFIG_PARITY_SPACE;
            break;
        }
    } else {
        config->parity = HS_SERIAL_CONFIG_PARITY_OFF;
    }

    switch (dcb.fRtsControl) {
    case RTS_CONTROL_DISABLE:
        config->rts = HS_SERIAL_CONFIG_RTS_OFF;
        break;
    case RTS_CONTROL_ENABLE:
        config->rts = HS_SERIAL_CONFIG_RTS_ON;
        break;
    case RTS_CONTROL_HANDSHAKE:
        config->rts = HS_SERIAL_CONFIG_RTS_FLOW;
        break;
    }

    switch (dcb.fDtrControl) {
    case DTR_CONTROL_DISABLE:
        config->dtr = HS_SERIAL_CONFIG_DTR_OFF;
        break;
    case DTR_CONTROL_ENABLE:
        config->dtr = HS_SERIAL_CONFIG_DTR_ON;
        break;
    }

    if (dcb.fInX && dcb.fOutX) {
        config->xonxoff = HS_SERIAL_CONFIG_XONXOFF_INOUT;
    } else if (dcb.fInX) {
        config->xonxoff = HS_SERIAL_CONFIG_XONXOFF_IN;
    } else if (dcb.fOutX) {
        config->xonxoff = HS_SERIAL_CONFIG_XONXOFF_OUT;
    } else {
        config->xonxoff = HS_SERIAL_CONFIG_XONXOFF_OFF;
    }

    return 0;
}