/* * $Id: linuxusb.c,v 1.24 2006/10/12 14:21:22 desrod Exp $ * * linuxusb.c: device i/o for linux usb * * Copyright (c) 1996, 1997, D. Jeff Dionne & Kenneth Albanowski. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Library General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library * General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "pi-debug.h" #include "pi-source.h" #include "pi-usb.h" #include "pi-error.h" #ifdef HAVE_SYS_IOCTL_COMPAT_H #include #endif #ifdef HAVE_SYS_SELECT_H #include #endif #ifndef O_NONBLOCK # define O_NONBLOCK 0 #endif static int u_open(pi_socket_t *ps, struct pi_sockaddr *addr, size_t addrlen); static int u_close(pi_socket_t *ps); static int u_write(pi_socket_t *ps, unsigned char *buf, size_t len, int flags); static int u_read(pi_socket_t *ps, pi_buffer_t *buf, size_t len, int flags); static int u_poll(pi_socket_t *ps, int timeout); static int u_flush(pi_socket_t *ps, int flags); void pi_usb_impl_init (struct pi_usb_impl *impl) { impl->open = u_open; impl->close = u_close; impl->write = u_write; impl->read = u_read; impl->flush = u_flush; impl->poll = u_poll; impl->wait_for_device = NULL; /* not implemented in linuxusb yet */ impl->changebaud = NULL; /* we don't need this one on linuxusb * as USB serial adapters redirect to serial ports */ impl->control_request = NULL; } /*********************************************************************** * * Function: u_open * * Summary: Open the usb port and establish a connection for * * Parameters: None * * Returns: The file descriptor * ***********************************************************************/ static int u_open(pi_socket_t *ps, struct pi_sockaddr *addr, size_t addrlen) { int fd, i; char *tty = addr->pi_device; if ((fd = open(tty, O_RDWR | O_NONBLOCK)) < 0) { ps->last_error = PI_ERR_GENERIC_SYSTEM; return PI_ERR_GENERIC_SYSTEM; /* errno already set */ } if (!isatty(fd)) { close(fd); errno = EINVAL; ps->last_error = PI_ERR_GENERIC_SYSTEM; return PI_ERR_GENERIC_SYSTEM; } if ((i = fcntl(fd, F_GETFL, 0)) != -1) { i &= ~O_NONBLOCK; fcntl(fd, F_SETFL, i); } if ((i = pi_socket_setsd(ps, fd)) < 0) return i; return fd; } /*********************************************************************** * * Function: u_close * * Summary: Close the open socket/file descriptor * * Parameters: None * * Returns: Nothing * ***********************************************************************/ static int u_close(pi_socket_t *ps) { LOG((PI_DBG_DEV, PI_DBG_LVL_INFO, "DEV CLOSE linuxusb fd: %d\n", ps->sd)); return close(ps->sd); } /*********************************************************************** * * Function: u_poll * * Summary: Poll the open socket/file descriptor * * Parameters: None * * Returns: 1 on success, PI_ERR_SOCK_TIMEOUT on timeout * ***********************************************************************/ static int u_poll(pi_socket_t *ps, int timeout) { struct timeval t; fd_set ready; FD_ZERO(&ready); FD_SET(ps->sd, &ready); /* If timeout == 0, wait forever for packet, otherwise wait till timeout milliseconds */ if (timeout == 0) select(ps->sd + 1, &ready, 0, 0, 0); else { t.tv_sec = timeout / 1000; t.tv_usec = (timeout % 1000) * 1000; select(ps->sd + 1, &ready, 0, 0, &t); } if (!FD_ISSET(ps->sd, &ready)) { /* otherwise throw out any current packet and return */ LOG((PI_DBG_DEV, PI_DBG_LVL_WARN, "DEV POLL linuxusb timeout\n")); errno = ETIMEDOUT; return pi_set_error(ps->sd, PI_ERR_SOCK_TIMEOUT); } LOG((PI_DBG_DEV, PI_DBG_LVL_DEBUG, "DEV POLL linuxusb found data on fd: %d\n", ps->sd)); return 1; } /*********************************************************************** * * Function: u_write * * Summary: Write to the open socket/file descriptor * * Parameters: None * * Returns: Nothing * ***********************************************************************/ static int u_write(pi_socket_t *ps, unsigned char *buf, size_t len, int flags) { int total, nwrote; struct pi_usb_data *data = (struct pi_usb_data *)ps->device->data; struct timeval t; fd_set ready; FD_ZERO(&ready); FD_SET(ps->sd, &ready); total = len; while (total > 0) { if (data->timeout == 0) select(ps->sd + 1, 0, &ready, 0, 0); else { t.tv_sec = data->timeout / 1000; t.tv_usec = (data->timeout % 1000) * 1000; if (select(ps->sd + 1, 0, &ready, 0, &t)) return pi_set_error(ps->sd, PI_ERR_SOCK_TIMEOUT); } if (!FD_ISSET(ps->sd, &ready)) { ps->state = PI_SOCK_CONN_BREAK; return pi_set_error(ps->sd, PI_ERR_SOCK_DISCONNECTED); } nwrote = write(ps->sd, buf, len); if (nwrote < 0) { ps->state = PI_SOCK_CONN_BREAK; return pi_set_error(ps->sd, PI_ERR_SOCK_DISCONNECTED); } total -= nwrote; } LOG((PI_DBG_DEV, PI_DBG_LVL_DEBUG, "DEV TX linuxusb wrote %d bytes\n", len)); return len; } /*********************************************************************** * * Function: u_read_buf * * Summary: read buffer * * Parameters: pi_socket_t*, char* to buffer, length of buffer * * Returns: number of bytes read * ***********************************************************************/ static int u_read_buf (pi_socket_t *ps, pi_buffer_t *buf, size_t len, int flags) { struct pi_usb_data *data = (struct pi_usb_data *)ps->device->data; size_t rbuf = data->buf_size; if (rbuf > len) rbuf = len; if (pi_buffer_append (buf, data->buf, rbuf) == NULL) { errno = ENOMEM; return pi_set_error(ps->sd, PI_ERR_GENERIC_MEMORY); } if (flags != PI_MSG_PEEK) { data->buf_size -= rbuf; if (data->buf_size > 0) memmove(data->buf, &data->buf[rbuf], data->buf_size); } LOG((PI_DBG_DEV, PI_DBG_LVL_DEBUG, "DEV RX linuxusb read %d bytes from read-ahead buffer\n", rbuf)); return rbuf; } /*********************************************************************** * * Function: u_read * * Summary: Read incoming data from the socket/file descriptor * * Parameters: pi_socket_t*, char* to buffer, buffer length, flags * * Returns: number of bytes read or negative otherwise * ***********************************************************************/ static int u_read(pi_socket_t *ps, pi_buffer_t *buf, size_t len, int flags) { ssize_t rbuf = 0, bytes; struct pi_usb_data *data = (struct pi_usb_data *)ps->device->data; struct timeval t; fd_set ready; /* check whether we have at least partial data in store */ if (data->buf_size) { rbuf = u_read_buf(ps, buf, len, flags); if (rbuf < 0) return rbuf; len -= rbuf; if (len == 0) return rbuf; } /* If timeout == 0, wait forever for packet, otherwise wait till timeout milliseconds */ FD_ZERO(&ready); FD_SET(ps->sd, &ready); if (data->timeout == 0) select(ps->sd + 1, &ready, 0, 0, 0); else { t.tv_sec = data->timeout / 1000; t.tv_usec = (data->timeout % 1000) * 1000; if (select(ps->sd + 1, &ready, 0, 0, &t) == 0) { LOG((PI_DBG_DEV, PI_DBG_LVL_WARN, "DEV RX linuxusb timeout\n")); errno = ETIMEDOUT; return pi_set_error(ps->sd, PI_ERR_SOCK_TIMEOUT); } } /* If data is available in time, read it */ if (FD_ISSET(ps->sd, &ready)) { if (flags == PI_MSG_PEEK && len > 256) len = 256; if (pi_buffer_expect (buf, len) == NULL) { errno = ENOMEM; return pi_set_error(ps->sd, PI_ERR_GENERIC_MEMORY); } bytes = read(ps->sd, buf->data + buf->used, len); if (bytes > 0) { if (flags == PI_MSG_PEEK) { memcpy(data->buf + data->buf_size, buf->data + buf->used, bytes); data->buf_size += bytes; } buf->used += bytes; rbuf += bytes; LOG((PI_DBG_DEV, PI_DBG_LVL_DEBUG, "DEV RX linuxusb read %d bytes\n", bytes)); } } else { LOG((PI_DBG_DEV, PI_DBG_LVL_WARN, "DEV RX linuxusb timeout\n")); errno = ETIMEDOUT; return pi_set_error(ps->sd, PI_ERR_SOCK_TIMEOUT); } return rbuf; } /*********************************************************************** * * Function: u_flush * * Summary: Flush incoming and/or outgoing data from the socket/file * descriptor * * Parameters: ps is of type pi_socket that contains the sd member which is * the file descriptor that the data in buf will be read. It * also contains the read buffer. * * flags is of type int and can be a combination of * PI_FLUSH_INPUT and PI_FLUSH_OUTPUT * * Returns: 0 * ***********************************************************************/ static int u_flush(pi_socket_t *ps, int flags) { char buf[256]; int fl; struct pi_usb_data *data = (struct pi_usb_data *) ps->device->data; if (flags & PI_FLUSH_INPUT) { /* clear internal buffer */ data->buf_size = 0; /* flush pending data */ if ((fl = fcntl(ps->sd, F_GETFL, 0)) != -1) { fcntl(ps->sd, F_SETFL, fl | O_NONBLOCK); while (recv(ps->sd, buf, sizeof(buf), 0) > 0) ; fcntl(ps->sd, F_SETFL, 0); } LOG((PI_DBG_DEV, PI_DBG_LVL_DEBUG, "DEV FLUSH linuxusb flushed input buffer\n")); } return 0; } /* vi: set ts=8 sw=4 sts=4 noexpandtab: cin */ /* ex: set tabstop=4 expandtab: */ /* Local Variables: */ /* indent-tabs-mode: t */ /* c-basic-offset: 8 */ /* End: */