You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
461 lines
13 KiB
461 lines
13 KiB
4 years ago
|
/*
|
||
|
* $Id: freebsdusb.c,v 1.28 2006/10/12 14:21:22 desrod Exp $
|
||
|
*
|
||
|
* freebsdusb.c: device IO for FreeBSD usb devices
|
||
|
*
|
||
|
* Copyright (c) 1996, 1997, D. Jeff Dionne & Kenneth Albanowski.
|
||
|
* 2002, Anish Mistry
|
||
|
*
|
||
|
* 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 <config.h>
|
||
|
#endif
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/socket.h>
|
||
|
|
||
|
#include "pi-debug.h"
|
||
|
#include "pi-source.h"
|
||
|
#include "pi-usb.h"
|
||
|
|
||
|
#ifdef HAVE_SYS_IOCTL_COMPAT_H
|
||
|
#include <sys/ioctl_compat.h>
|
||
|
#endif
|
||
|
|
||
|
#ifdef HAVE_SYS_SELECT_H
|
||
|
#include <sys/select.h>
|
||
|
#endif
|
||
|
|
||
|
#ifndef O_NONBLOCK
|
||
|
# define O_NONBLOCK 0
|
||
|
#endif
|
||
|
|
||
|
#if defined(__FreeBSD__)
|
||
|
/* freebsd usb header */
|
||
|
#include <dev/usb/usb.h>
|
||
|
#define MAX_BUF 256
|
||
|
#endif
|
||
|
|
||
|
/* Declare prototypes */
|
||
|
static int u_open(pi_socket_t *ps, struct pi_sockaddr *addr, size_t addrlen);
|
||
|
static int u_close(pi_socket_t *ps);
|
||
|
static ssize_t u_write(pi_socket_t *ps, const unsigned char *buf, size_t len, int flags);
|
||
|
static ssize_t 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: u_open
|
||
|
*
|
||
|
* Summary: Open the usb port and establish a connection for
|
||
|
* communicating over the usb port.
|
||
|
*
|
||
|
* Parameters: ps is of type pi_socket_t which will given a copy of the
|
||
|
* valid file descriptor that is returned by the function.
|
||
|
*
|
||
|
* addr of type pi_socketaddr contains the member pi_device
|
||
|
* which is a character string of the usb device.
|
||
|
*
|
||
|
* XXX addrlen is of type int contain the size of addr? I'm
|
||
|
* not using this, should I? Possible buffer overflow because
|
||
|
* I'm not checking something?
|
||
|
*
|
||
|
* Returns: The file descriptor
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
static int
|
||
|
u_open(pi_socket_t *ps, struct pi_sockaddr *addr, size_t addrlen)
|
||
|
{
|
||
|
int fd,
|
||
|
i,
|
||
|
endpoint_fd;
|
||
|
|
||
|
struct usb_device_info udi;
|
||
|
/* struct usb_ctl_request ur; */
|
||
|
/* unsigned char usbresponse[50]; */
|
||
|
|
||
|
char *tty = addr->pi_device;
|
||
|
char *pEndPoint = NULL;
|
||
|
|
||
|
/* open the usb device */
|
||
|
if ((fd = open(tty, O_RDWR, 0)) < 0) {
|
||
|
ps->last_error = PI_ERR_GENERIC_SYSTEM;
|
||
|
return PI_ERR_GENERIC_SYSTEM;
|
||
|
}
|
||
|
|
||
|
/* check for a valid file descriptor */
|
||
|
if (fd < 0) {
|
||
|
ps->last_error = PI_ERR_GENERIC_SYSTEM;
|
||
|
return PI_ERR_GENERIC_SYSTEM;
|
||
|
}
|
||
|
|
||
|
/* fill the udi structure with information about the handheld, after
|
||
|
this is where you will probably want to do device specific stuff
|
||
|
*/
|
||
|
|
||
|
if (ioctl(fd, USB_GET_DEVICEINFO, &udi)) {
|
||
|
close(fd);
|
||
|
ps->last_error = PI_ERR_GENERIC_SYSTEM;
|
||
|
return PI_ERR_GENERIC_SYSTEM;
|
||
|
}
|
||
|
|
||
|
LOG((PI_DBG_DEV, PI_DBG_LVL_INFO,
|
||
|
"DEV USB_GET_DEVICE_INFO USB FreeBSD fd: %d\n", fd));
|
||
|
|
||
|
/* set the configuration */
|
||
|
i = 1;
|
||
|
if (ioctl(fd, USB_SET_CONFIG, &i) < 0) {
|
||
|
LOG((PI_DBG_DEV, PI_DBG_LVL_ERR,
|
||
|
"DEV USB_SET_CONFIG USB FreeBSD fd: %d failed\n", fd));
|
||
|
|
||
|
close(fd);
|
||
|
ps->last_error = PI_ERR_GENERIC_SYSTEM;
|
||
|
return PI_ERR_GENERIC_SYSTEM;
|
||
|
}
|
||
|
|
||
|
/* close the main communication pipe since we have initilized
|
||
|
everything we needed to
|
||
|
NOTE: we HAVE to do all this stuff to the main pipe or we will
|
||
|
cause a kernel panic when data is sent over the endpoint */
|
||
|
close(fd);
|
||
|
|
||
|
/* open endpoint */
|
||
|
/* allocate data for the usb endpoint string */
|
||
|
pEndPoint = malloc(strlen(tty)+20);
|
||
|
if(!pEndPoint)
|
||
|
return pi_set_error(ps->sd, PI_ERR_GENERIC_MEMORY);
|
||
|
|
||
|
/* create device endpoint name string */
|
||
|
sprintf(pEndPoint, "%s.%d", tty, 2);
|
||
|
|
||
|
/* open the endpoint with read write access */
|
||
|
endpoint_fd = open(pEndPoint, O_RDWR, 0);
|
||
|
if(endpoint_fd < 0) {
|
||
|
/* we failed to open the endpoint */
|
||
|
free(pEndPoint);
|
||
|
return pi_set_error(ps->sd, PI_ERR_GENERIC_SYSTEM);
|
||
|
}
|
||
|
|
||
|
if ((i = fcntl(endpoint_fd, F_GETFL, 0)) != -1) {
|
||
|
i &= ~O_NONBLOCK;
|
||
|
fcntl(endpoint_fd, F_SETFL, i);
|
||
|
}
|
||
|
|
||
|
/* set short transfer so that we can allow "short" reads since we
|
||
|
will don't know exactly
|
||
|
what is coming so we can't specify exact byte amounts */
|
||
|
i = 1;
|
||
|
if (ioctl(endpoint_fd, USB_SET_SHORT_XFER, &i) < 0) {
|
||
|
LOG((PI_DBG_DEV, PI_DBG_LVL_WARN,
|
||
|
"DEV USB_SET_SHORT_XFER USB FreeBSD fd: %d failed\n",
|
||
|
endpoint_fd));
|
||
|
}
|
||
|
|
||
|
/* 0 timeout value will cause us the wait until the device has data
|
||
|
available or is disconnected */
|
||
|
i = 0;
|
||
|
if (ioctl(endpoint_fd, USB_SET_TIMEOUT, &i) < 0) {
|
||
|
LOG((PI_DBG_DEV, PI_DBG_LVL_WARN,
|
||
|
"DEV USB_SET_TIMEOUT USB FreeBSD fd: %d failed\n",
|
||
|
endpoint_fd));
|
||
|
}
|
||
|
|
||
|
/* save our file descriptor in the pi_socket structure */
|
||
|
if ((i = pi_socket_setsd(ps, endpoint_fd)) < 0) {
|
||
|
free(pEndPoint);
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
/* free endpoint string memory */
|
||
|
free(pEndPoint);
|
||
|
|
||
|
LOG((PI_DBG_DEV, PI_DBG_LVL_INFO,
|
||
|
"DEV OPEN USB FreeBSD fd: %d\n", endpoint_fd));
|
||
|
|
||
|
/* return our endpoint file descriptor since this is where the
|
||
|
reading and writing will be done */
|
||
|
return endpoint_fd;
|
||
|
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: u_close
|
||
|
*
|
||
|
* Summary: Close the open socket/file descriptor
|
||
|
*
|
||
|
* Parameters: ps is of type pi_socket that contains the member sd which is
|
||
|
* the file descriptor that is to be closed.
|
||
|
*
|
||
|
* Returns: 0 if sucessful or -1 if it failed and errno set to the error
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
static int
|
||
|
u_close(pi_socket_t *ps)
|
||
|
{
|
||
|
LOG((PI_DBG_DEV, PI_DBG_LVL_INFO,
|
||
|
"DEV CLOSE USB FreeBSD fd: %d\n", ps->sd));
|
||
|
|
||
|
return close(ps->sd);
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: u_poll
|
||
|
*
|
||
|
* Summary: Poll the open socket/file descriptor
|
||
|
*
|
||
|
* Parameters: None
|
||
|
*
|
||
|
* Returns: 1 always since we buffer our own data and you can't poll()
|
||
|
* FreeBSD USB devices?
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
static int
|
||
|
u_poll(pi_socket_t *ps, int timeout)
|
||
|
{
|
||
|
/* stub this function and log an error that this should never needed
|
||
|
to be called */
|
||
|
LOG((PI_DBG_DEV, PI_DBG_LVL_WARN,
|
||
|
"DEV POLL USB FreeBSD Timeout: %d\npoll()"
|
||
|
" should not be called for FreeBSD USB\n", timeout));
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: u_write
|
||
|
*
|
||
|
* Parameters: ps is of type pi_socket that contains the sd member which is
|
||
|
* the file descriptor that the data in buf will be written to.
|
||
|
*
|
||
|
* buf is a unsigned char pointer that points to the data that
|
||
|
* is to be written to ps->sd.
|
||
|
*
|
||
|
* len is of type int and indicated the number of bytes to
|
||
|
* write to ps->sd.
|
||
|
*
|
||
|
* flags is of type int and contains various write flags. What
|
||
|
* flags and should I respect them, since I am currently not
|
||
|
* checking this variable?
|
||
|
*
|
||
|
* Parameters: None
|
||
|
*
|
||
|
* Returns: The number of bytes written from buf or negative to indicate an
|
||
|
* error
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
static ssize_t
|
||
|
u_write(pi_socket_t *ps, const unsigned char *buf, size_t len, int flags)
|
||
|
{
|
||
|
int nwrote,
|
||
|
total,
|
||
|
write_len;
|
||
|
fd_set ready;
|
||
|
|
||
|
total = len;
|
||
|
write_len = len;
|
||
|
|
||
|
/* FIXME: there is no timeout handling in the original freebsdusb code! */
|
||
|
|
||
|
while (total > 0) {
|
||
|
FD_ZERO(&ready);
|
||
|
FD_SET(ps->sd, &ready);
|
||
|
|
||
|
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, write_len);
|
||
|
if (nwrote < 0) {
|
||
|
ps->state = PI_SOCK_CONN_BREAK;
|
||
|
return pi_set_error(ps->sd, PI_ERR_SOCK_DISCONNECTED);
|
||
|
}
|
||
|
|
||
|
write_len -= nwrote;
|
||
|
buf += nwrote;
|
||
|
total -= nwrote;
|
||
|
}
|
||
|
|
||
|
LOG((PI_DBG_DEV, PI_DBG_LVL_INFO,
|
||
|
"DEV TX USB FreeBSD Bytes: %d\n", len));
|
||
|
|
||
|
return len;
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* Function: u_read
|
||
|
*
|
||
|
* Summary: Read incoming data from the socket/file descriptor and
|
||
|
* buffer any extra data that is read in the process of
|
||
|
* receiving data from the device.
|
||
|
*
|
||
|
* 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.
|
||
|
*
|
||
|
* buf is a pointer to an unsigned character string that will
|
||
|
* receive the data the is read from the device.
|
||
|
*
|
||
|
* len is of type int that is the the number of bytes to read
|
||
|
* from the device/buffer into buf.
|
||
|
*
|
||
|
* flags is of type in that currently only indicated if you
|
||
|
* want to peek data from the device/buffer .
|
||
|
*
|
||
|
* Returns: The number of bytes that was read from the device or from
|
||
|
* the buffer and copied to buf.
|
||
|
*
|
||
|
***********************************************************************/
|
||
|
static ssize_t
|
||
|
u_read(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;
|
||
|
ssize_t rlen;
|
||
|
int bytes_read = 0;
|
||
|
fd_set ready;
|
||
|
struct timeval t;
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/* first extract anything we had in the "peek" buffer */
|
||
|
if (data->buf_size > 0) {
|
||
|
bytes_read = len > data->buf_size ? data->buf_size : len;
|
||
|
len -= bytes_read;
|
||
|
pi_buffer_append(buf, data->buf, bytes_read);
|
||
|
if (flags != PI_MSG_PEEK) {
|
||
|
data->buf_size -= bytes_read;
|
||
|
if (data->buf_size > 0)
|
||
|
memmove(data->buf, data->buf + bytes_read, data->buf_size);
|
||
|
}
|
||
|
if (!len)
|
||
|
return bytes_read;
|
||
|
}
|
||
|
|
||
|
/* check to see if device is ready for read */
|
||
|
FD_ZERO(&ready);
|
||
|
FD_SET(ps->sd, &ready);
|
||
|
|
||
|
/* If timeout == 0, wait forever for packet, otherwise wait till
|
||
|
timeout milliseconds */
|
||
|
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)
|
||
|
return pi_set_error(ps->sd, PI_ERR_SOCK_TIMEOUT);
|
||
|
}
|
||
|
|
||
|
/* If data is available in time, read it */
|
||
|
if (!FD_ISSET(ps->sd, &ready)) {
|
||
|
LOG((PI_DBG_DEV, PI_DBG_LVL_WARN,
|
||
|
"DEV RX USB FreeBSD timeout\n"));
|
||
|
errno = ETIMEDOUT;
|
||
|
return pi_set_error(ps->sd, PI_ERR_SOCK_TIMEOUT);
|
||
|
}
|
||
|
|
||
|
/* read data to pre-sized buffer */
|
||
|
rlen = read(ps->sd, &buf->data[buf->used], len);
|
||
|
if (rlen > 0) {
|
||
|
if (flags == PI_MSG_PEEK) {
|
||
|
memcpy(data->buf, buf->data + buf->used, rlen);
|
||
|
data->buf_size = rlen;
|
||
|
}
|
||
|
buf->used += rlen;
|
||
|
bytes_read += rlen;
|
||
|
|
||
|
LOG((PI_DBG_DEV, PI_DBG_LVL_INFO,
|
||
|
"DEV RX USB FreeBSD Bytes: %d:%d\n", bytes_read,
|
||
|
bytes_read + data->buf_size));
|
||
|
|
||
|
}
|
||
|
else
|
||
|
bytes_read = PI_ERR_SOCK_IO;
|
||
|
|
||
|
return bytes_read;
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
*
|
||
|
* 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 (read(ps->sd, buf, sizeof(buf)) > 0)
|
||
|
;
|
||
|
fcntl(ps->sd, F_SETFL, fl);
|
||
|
}
|
||
|
}
|
||
|
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: */
|