/* evlist/device.c * * Device functions. * * Copyright (C) 2003, 2004 * Andy Goth * * This code is available under the GNU General Public License; see COPYING. */ #include #include #include #include #include #include #include #include #include #include "evlist/evlist.h" #include "prjlibs/vec.h" /* Set this to nonzero to enable a nifty colorized display of all I/O. */ #define DEBUG_IO 0 #if DEBUG_IO static device_t* device_io_dev; /* Display, in red, all bytes as they are sent from the buffer to the device. */ #define PRINT_SENT() \ do { \ int i; \ if (device_io_dev != dev) { \ if (device_io_dev != NULL) fprintf(stderr, "\n"); \ fprintf(stderr, "%s (%s): ", dev->name, dev->path); \ device_io_dev = dev; \ } \ fprintf(stderr, "\e[1;32m"); \ for (i = 0; i < ret; ++i) { \ fprintf(stderr, "%02x", VEC_ELTS(byte_t, &dev->buf_out)[i]); \ } \ fprintf(stderr, "\e[0m "); \ } while (0) /* Display in green all bytes received from the device. */ #define PRINT_RECV() \ do { \ int i; \ if (device_io_dev != dev) { \ if (device_io_dev != NULL) fprintf(stderr, "\n"); \ fprintf(stderr, "%s (%s): ", dev->name, dev->path); \ device_io_dev = dev; \ } \ fprintf(stderr, "\e[1;31m"); \ i = VEC_LEN(byte_t, &dev->buf_in) - ret; \ for (; i < VEC_LEN(byte_t, &dev->buf_in); ++i) { \ fprintf(stderr, "%02x", VEC_ELTS(byte_t, &dev->buf_in)[i]); \ } \ fprintf(stderr, "\e[0m "); \ } while (0) #else #define PRINT_SENT() #define PRINT_RECV() #endif int device_init(device_t* dev, char* name) { char* name_dup = strdup(name); if (name_dup == NULL) return -1; dev->name = name_dup; dev->path = NULL; dev->buf_size = 0 ; dev->buf_cursor = 0 ; VEC_INIT(byte_t, &dev->buf_in); VEC_INIT(byte_t, &dev->buf_out); dev->pollfd_in = NULL; dev->pollfd_out = NULL; dev->evlist = NULL; dev->hw_state = NULL; dev->hw_cleanup = NULL; dev->hw_open = NULL; dev->hw_close = NULL; dev->hw_readable = NULL; dev->hw_writable = NULL; dev->sw_state = NULL; dev->sw_cleanup = NULL; dev->sw_open = NULL; dev->sw_close = NULL; dev->sw_incoming = NULL; dev->sw_outgoing = NULL; return 0; } int device_cleanup(device_t* dev) { if (dev->sw_cleanup != NULL) { if (dev->sw_cleanup(dev) == -1) return -1; } if (dev->hw_cleanup != NULL) { if (dev->hw_cleanup(dev) == -1) return -1; } free(dev->name); dev->name = NULL; return 0; } int device_attach_fd(device_t* dev, int fd, int mode) { int i; ptrdiff_t adj; pollfd_t pollfd, *pollfds, **pollfdpp; device_t* devp; pollfd.fd = fd; switch (mode) { case O_RDONLY: pollfd.events = POLLIN; pollfdpp = &dev->pollfd_in; break; case O_WRONLY: pollfd.events = VEC_LEN(byte_t, &dev->buf_out) && POLLOUT; pollfdpp = &dev->pollfd_out; break; case O_RDWR: pollfd.events = POLLIN; if (VEC_LEN(byte_t, &dev->buf_out) != 0) pollfd.events |= POLLOUT; pollfdpp = &dev->pollfd_in; if (dev->pollfd_out != NULL) return -1; break; default: return -1; } if (*pollfdpp != NULL) return -1; VEC_ALLOC(pollfd_t, &dev->evlist->pollfds, 1, 0); pollfds = VEC_ELTS(pollfd_t, &dev->evlist->pollfds); VEC_PUSH(pollfd_t, &dev->evlist->pollfds, pollfd); adj = VEC_ELTS(pollfd_t, &dev->evlist->pollfds) - pollfds; if (adj != 0) { for (i = 0; i < VEC_LEN(device_t*, &dev->evlist->devices); ++i) { devp = VEC_ELTS(device_t*, &dev->evlist->devices)[i]; if (devp->pollfd_in != NULL) devp->pollfd_in += adj; if (devp->pollfd_out != NULL) devp->pollfd_out += adj; } } *pollfdpp = &VEC_ELTS(pollfd_t, &dev->evlist->pollfds)[ VEC_LEN(pollfd_t, &dev->evlist->pollfds) - 1]; if (mode == O_RDWR) dev->pollfd_out = *pollfdpp; return 0; } int device_detatch_fd(device_t* dev, int mode) { int i, j; device_t* devp; pollfd_t* pollfdp, **pollfdpp; switch (mode) { case O_RDONLY: pollfdpp = &dev->pollfd_in; break; case O_WRONLY: pollfdpp = &dev->pollfd_out; break; case O_RDWR: if (dev->pollfd_in != NULL) { if (device_detatch_fd(dev, O_RDONLY) == -1) return -1; } if (dev->pollfd_out != NULL) { if (device_detatch_fd(dev, O_WRONLY) == -1) return -1; } return 0; default: return -1; } if (*pollfdpp == NULL) return -1; if (dev->pollfd_out == dev->pollfd_in) { /* Don't delete the pollfd from the list if it's still in use. */ } else { /* Locate and remove pollfd. */ for (i = 0; i < VEC_LEN(pollfd_t, &dev->evlist->pollfds); ++i) { pollfdp = &VEC_ELTS(pollfd_t, &dev->evlist->pollfds)[i]; if (*pollfdpp == pollfdp) { VEC_CUT(pollfd_t, &dev->evlist->pollfds, i, NULL, 1); break; } } /* Fix pointers to remaining pollfds. */ for (; i < VEC_LEN(pollfd_t, &dev->evlist->pollfds); ++i) { for (j = 0; j < VEC_LEN(device_t*, &dev->evlist->devices); ++j) { devp = VEC_ELTS(device_t* , &dev->evlist->devices)[j]; if (devp->pollfd_in != NULL && devp->pollfd_in > pollfdp) { --devp->pollfd_in; } if (devp->pollfd_out != NULL && devp->pollfd_out > pollfdp) { --devp->pollfd_out; } } } } *pollfdpp = NULL; return 0; } int device_readable(device_t* dev) { int ret; again: if (dev->buf_size == 0) { /* Make sure there's available space in the input buffer. */ if (VEC_CAP(byte_t, &dev->buf_in) - VEC_LEN(byte_t, &dev->buf_in) < 32) { if (VEC_CAP(byte_t, &dev->buf_in) < 32) { VEC_ALLOC(byte_t, &dev->buf_in, 32, 0); } else { VEC_ALLOC(byte_t, &dev->buf_in, 2 * VEC_CAP(byte_t, &dev->buf_in), 32); } } } else { /* Use a specific buffer size. */ if (VEC_CAP(byte_t, &dev->buf_in) != dev->buf_size) { VEC_ALLOC(byte_t, &dev->buf_in, dev->buf_size, 0); } /* Sanity check. */ if (VEC_LEN(byte_t, &dev->buf_in) == VEC_CAP(byte_t, &dev->buf_in)) { errno = EMSGSIZE; return -1; } } /* Try to fill the input buffer. */ ret = read(dev->pollfd_in->fd, &VEC_ELTS(byte_t, &dev->buf_in)[VEC_LEN(byte_t, &dev->buf_in)], VEC_CAP(byte_t, &dev->buf_in) - VEC_LEN(byte_t, &dev->buf_in)); if (ret == -1) { if (errno == EAGAIN) { /* No (more) data available for the moment. */ return 0; } else { /* Genuine error. */ return -1; } } else if (ret == 0) { /* File closed. */ errno = EPIPE; return -1; } else { /* Data! */ VEC_PUSHN(byte_t, &dev->buf_in, NULL, ret); PRINT_RECV(); if (VEC_LEN(byte_t, &dev->buf_in) == VEC_CAP(byte_t, &dev->buf_in)) { if (dev->buf_size == 0) { /* Maybe there's more data... try again. */ goto again; } else { /* Even though there's probably more data, don't exceed the * requested buffer size. Come back for it another day. */ return 0; } } else { /* That's all for now. */ return 0; } } } int device_writable(device_t* dev) { int ret; if (VEC_LEN(byte_t, &dev->buf_out) != 0) { /* Try to write all buffered data. */ ret = write(dev->pollfd_out->fd, VEC_ELTS(byte_t, &dev->buf_out), VEC_LEN(byte_t, &dev->buf_out)); if (ret == -1) { if (errno == EAGAIN) { /* Can't write data just yet. */ return 0; } else { /* Genuine error. */ return -1; } } else if (ret == 0) { /* File closed. */ errno = EPIPE; return -1; } else { /* Data successfully written. */ PRINT_SENT(); VEC_SHIFTN(byte_t, &dev->buf_out, ret); if (VEC_LEN(byte_t, &dev->buf_out) == 0) { /* No more data to write... */ dev->pollfd_out->events &= ~POLLOUT; } return 0; } } } int device_sendf(device_t* dev, char* fmt, ...) { va_list ap; int ret; int size = VEC_CAP(byte_t, &dev->buf_out) - VEC_LEN(byte_t, &dev->buf_out); if (size == 0) size = 32; /* Silently avoid writing to read-only devices. */ if (dev->pollfd_out == NULL) return 0; while (1) { /* Make room. */ VEC_ALLOC(byte_t, &dev->buf_out, VEC_LEN(byte_t, &dev->buf_out) + size, 0); /* Attempt to write the string to the output buffer. Hmm, I could do * without the terminating \0, but who really cares? */ va_start(ap, fmt); ret = vsnprintf(VEC_ELTS(byte_t, &dev->buf_out) + VEC_LEN(byte_t, &dev->buf_out), size, fmt, ap); va_end(ap); if (ret > -1 && ret < size) { /* Success. */ VEC_PUSHN(byte_t, &dev->buf_out, NULL, ret); dev->pollfd_out->events |= POLLOUT; return 0; } else if (ret > -1) { /* Glibc 2.1. */ size = ret + 1; } else { /* Glibc 2.0. */ size *= 2; } } } /* vim: set ts=4 sts=4 sw=4 tw=80 et: */