Rev 214 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/signal.h>
#include <time.h>
#include <usb.h>
#include "garmin.h"
/* #if defined (HAVE_SYS_TERMIOS_H)
#include <sys/termios.h>
#else
#if defined (HAVE_TERMIOS_H) */
#include <termios.h>
/* #endif
#endif
*/
#define INTR_TIMEOUT 3000
#define BULK_TIMEOUT 3000
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/* something magic about 64, garmin driver will not return more than
* 64 at a time. If you read less than 64 bytes the next read will
* just get the last of the 64 byte buffer.
*/
#define ASYNC_DATA_SIZE 64
#define PRIV_PKTID_SET_MODE 2
#define PRIV_PKTID_INFO_RESP 4
#define PRIV_PKTID_INFO_REQ 3
#define GARMIN_LAYERID_PRIVATE 0x01106E4B
const char *USBdevice = NULL;
int method = 0; /* 0 = Read directly from USB-Device, 1 = read from special device */
struct termios ttyset, ttyset_old;
static fd_set all_fds;
void signal_handler_IO (int status); /* definition of signal handler */
int wait_flag = TRUE; /* TRUE while no signal received */
int garmin_set_speed(garmin_unit *garmin, unsigned int speed, unsigned int stopbits);
void garmin_set_device(const char *device)
{
USBdevice = device;
}
void garmin_set_method(int mth)
{
method = (mth != 0) ? 1 : 0;
}
/* Close the USB connection with the Garmin device. */
int garmin_close(garmin_unit *garmin)
{
if (method && garmin->usb.fd != -1)
{
if (isatty(garmin->usb.fd))
{
/* force hangup on close on systems that don't do HUPCL properly */
cfsetispeed(&ttyset, (speed_t)B0);
cfsetospeed(&ttyset, (speed_t)B0);
tcsetattr(garmin->usb.fd, TCSANOW, &ttyset);
}
/* this is the clean way to do it */
ttyset_old.c_cflag |= HUPCL;
tcsetattr(garmin->usb.fd, TCSANOW, &ttyset_old);
close(garmin->usb.fd);
garmin->usb.fd = -1;
USBdevice = NULL;
}
else if (garmin->usb.handle != NULL )
{
usb_release_interface(garmin->usb.handle,0);
usb_close(garmin->usb.handle);
garmin->usb.handle = NULL;
}
return 0;
}
/*
Open the USB connection with the first Garmin device we find. Eventually,
I'd like to add the ability to select a particular device.
*/
int garmin_open(garmin_unit *garmin)
{
struct usb_bus * bi;
struct usb_device * di;
int i;
static unsigned int rates[] = {0, 4800, 9600, 19200, 38400, 57600};
garmin_packet p;
struct sigaction saio;
char hv0[1024];
struct termios tp;
if (method && garmin->usb.fd == -1)
{
/* check for USB serial drivers -- very Linux-specific */
/* if (access("/sys/module/garmin_gps", R_OK) != 0)
{
garmin_queue_error("garmin_gps not active!", err_error);
return 0;
}
*/
if ((garmin->usb.fd = open(USBdevice, O_RDWR)) == -1)
return 0;
if (tcgetattr(garmin->usb.fd, &tp) == -1)
return 0;
tp.c_iflag &=
~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXOFF|IXANY|INPCK|IUCLC);
tp.c_oflag &= ~OPOST;
tp.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN|ECHOE);
tp.c_cflag &= ~(CSIZE|PARENB);
tp.c_cflag |= CS8 | CLOCAL | CREAD | CRTSCTS;
if (cfsetispeed(&tp, B115200) == -1)
return 0;
if (cfsetospeed(&tp, B115200) == -1)
return 0;
tp.c_cc[VMIN] = 1;
tp.c_cc[VTIME] = 0;
if (tcsetattr(garmin->usb.fd, TCSANOW, &tp) == -1)
return 0;
return 1;
}
else if (!method && garmin->usb.handle == NULL)
{
usb_init();
usb_find_busses();
usb_find_devices();
for ( bi = usb_busses; bi != NULL; bi = bi->next )
{
for ( di = bi->devices; di != NULL; di = di->next )
{
if ( di->descriptor.idVendor == GARMIN_USB_VID &&
di->descriptor.idProduct == GARMIN_USB_PID )
{
if ( garmin->verbose != 0 )
{
sprintf(hv0, "[garmin] found VID %04x, PID %04x on %s/%s!",
di->descriptor.idVendor,
di->descriptor.idProduct,
bi->dirname,
di->filename);
garmin_queue_error(hv0, err_info);
}
garmin->usb.handle = usb_open(di);
garmin->usb.read_bulk = 0;
if ( garmin->usb.handle == NULL )
{
sprintf(hv0, "usb_open failed: %s!",usb_strerror());
garmin_queue_error(hv0, err_error);
return 0;
}
if (usb_set_configuration(garmin->usb.handle, 1) < 0 )
{
sprintf(hv0, "usb_set_configuration failed: %s!",usb_strerror());
garmin_queue_error(hv0, err_error);
return 0;
}
if ( usb_claim_interface(garmin->usb.handle,0) < 0 )
{
sprintf(hv0, "usb_claim_interface failed: %s!",usb_strerror());
garmin_queue_error(hv0, err_error);
return 0;
}
for ( i = 0; i < di->config->interface->altsetting->bNumEndpoints; i++ )
{
struct usb_endpoint_descriptor * ep;
ep = &di->config->interface->altsetting->endpoint[i];
switch ( ep->bmAttributes & USB_ENDPOINT_TYPE_MASK )
{
case USB_ENDPOINT_TYPE_BULK:
if ( ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK )
{
garmin->usb.bulk_in = ep->bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK;
}
else
{
garmin->usb.bulk_out = ep->bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK;
}
break;
case USB_ENDPOINT_TYPE_INTERRUPT:
if ( ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK )
{
garmin->usb.intr_in = ep->bEndpointAddress & USB_ENDPOINT_ADDRESS_MASK;
}
break;
default:
break;
}
}
break;
}
}
if ( garmin->usb.handle != NULL )
break;
}
if (garmin->usb.handle == NULL)
garmin_queue_error("Couldn't detect any Garmin device!", err_info);
}
return (garmin->usb.handle != NULL);
}
void signal_handler_IO (int status)
{
wait_flag = FALSE;
}
int garmin_set_speed(garmin_unit *garmin, unsigned int speed, unsigned int stopbits)
{
unsigned int rate;
if (speed < 300)
rate = B0;
else if (speed < 1200)
rate = B300;
else if (speed < 2400)
rate = B1200;
else if (speed < 4800)
rate = B2400;
else if (speed < 9600)
rate = B4800;
else if (speed < 19200)
rate = B9600;
else if (speed < 38400)
rate = B19200;
else if (speed < 57600)
rate = B38400;
else if (speed < 115200)
rate = B57600;
else
rate = B115200;
tcflush(garmin->usb.fd, TCIOFLUSH); /* toss stale data */
if (rate != cfgetispeed(&ttyset) || stopbits != 1)
{
cfsetispeed(&ttyset, (speed_t)rate);
cfsetospeed(&ttyset, (speed_t)rate);
ttyset.c_cflag &=~ CSIZE;
ttyset.c_cflag |= (CSIZE & (stopbits == 2 ? CS7 : CS8));
if (tcsetattr(garmin->usb.fd, TCSANOW, &ttyset) != 0)
return 0;
tcflush(garmin->usb.fd, TCIOFLUSH);
}
/* if ((session->packet_type = packet_sniff(session)) == BAD_PACKET)
return 0;
*/
return 1;
}
uint8 garmin_packet_type(garmin_packet *p)
{
return p->packet.type;
}
uint16 garmin_packet_id(garmin_packet *p)
{
return get_uint16(p->packet.id);
}
uint32 garmin_packet_size(garmin_packet *p)
{
return get_uint32(p->packet.size);
}
uint8 *garmin_packet_data(garmin_packet *p)
{
return p->packet.data;
}
int garmin_packetize(garmin_packet *p, uint16 id, uint32 size, uint8 *data)
{
int ok = 0;
if (size + PACKET_HEADER_SIZE < sizeof(garmin_packet))
{
p->packet.type = GARMIN_PROTOCOL_APP;
p->packet.reserved1 = 0;
p->packet.reserved2 = 0;
p->packet.reserved3 = 0;
p->packet.id[0] = id;
p->packet.id[1] = id >> 8;
p->packet.reserved4 = 0;
p->packet.reserved5 = 0;
p->packet.size[0] = size;
p->packet.size[1] = size >> 8;
p->packet.size[2] = size >> 16;
p->packet.size[3] = size >> 24;
if ( size > 0 && data != NULL )
{
memcpy(p->packet.data,data,size);
}
ok = 1;
}
return ok;
}
int garmin_read(garmin_unit *garmin, garmin_packet *p)
{
int r = -1;
int cnt = 0;
int bLen = 0;
struct timespec delay, rem;
fd_set fds, rfds;
struct timeval tv;
int sel_ret = 0;
int ok = 0;
char hv0[1024];
if (garmin->usb.fd == -1 && garmin->usb.handle == NULL)
garmin_open(garmin);
if (method && garmin->usb.fd != -1)
{
long theBytesReturned = 0;
char *buf = (char *)p->data;
wait_flag = FALSE;
for(cnt = 0 ; cnt < 10 ; cnt++)
{
if (wait_flag != FALSE)
{
cnt--;
usleep (100000);
wait_flag = FALSE;
continue;
}
/* Read async data until the driver returns less than the
* max async data size, which signifies the end of a packet
* not optimal, but given the speed and packet nature of
* the USB not too bad for a start
*/
wait_flag = TRUE;
theBytesReturned = read(garmin->usb.fd, buf + bLen, ASYNC_DATA_SIZE);
if (0 > theBytesReturned)
{
/* read error...
* or EAGAIN, but O_NONBLOCK is never set
*/
sprintf(hv0, "Read error=%ld, errno=%d!", theBytesReturned, errno);
garmin_queue_error(hv0, err_error);
continue;
}
sprintf(hv0, "got %ld bytes.", theBytesReturned);
garmin_queue_error(hv0, err_info);
bLen += theBytesReturned;
if (theBytesReturned < ASYNC_DATA_SIZE)
{
/* zero length, or short, read is a flag for got the whole packet */
break;
}
if (256 <= bLen)
{
/* really bad read error... */
bLen = 0;
garmin_queue_error("Packet too long!", err_fatal);
break;
}
delay.tv_sec = 0;
delay.tv_nsec = 3330000L;
while (nanosleep(&delay, &rem) < 0)
continue;
}
}
else if (garmin->usb.handle != NULL)
{
if (garmin->usb.read_bulk == 0)
{
r = usb_interrupt_read(garmin->usb.handle,
garmin->usb.intr_in,
p->data,
sizeof(garmin_packet),
INTR_TIMEOUT);
/*
* If the packet is a "Pid_Data_Available" packet, we need to read
* from the bulk endpoint until we get an empty packet.
*/
if (garmin_packet_type(p) == GARMIN_PROTOCOL_USB &&
garmin_packet_id(p) == Pid_Data_Available)
{
/* FIXME!!! */
sprintf(hv0, "Received a Pid_Data_Available from the unit!");
garmin_queue_error(hv0, err_warning);
}
}
else
{
r = usb_bulk_read(garmin->usb.handle,
garmin->usb.bulk_in,
p->data,
sizeof(garmin_packet),
BULK_TIMEOUT);
}
}
if (garmin->verbose != 0 && r >= 0)
{
garmin_print_packet(p, GARMIN_DIR_READ, stdout);
}
return r;
}
int garmin_write(garmin_unit *garmin, garmin_packet *p)
{
int r = -1;
int s = garmin_packet_size(p) + PACKET_HEADER_SIZE;
if (garmin->usb.fd == -1 && garmin->usb.handle == NULL)
garmin_open(garmin);
if (garmin->usb.handle != NULL || garmin->usb.fd != -1)
{
if (garmin->verbose != 0)
{
garmin_print_packet(p, GARMIN_DIR_WRITE, stdout);
}
if (method)
r = write(garmin->usb.fd, p->data, s);
else
r = usb_bulk_write(garmin->usb.handle,
garmin->usb.bulk_out,
p->data,
s,
BULK_TIMEOUT);
if (r != s)
{
char hv0[1024];
if (method)
{
sprintf(hv0, "Write to device failed!");
garmin_queue_error(hv0, err_error);
}
else
{
sprintf(stderr, "usb_bulk_write failed: %s!", usb_strerror());
garmin_queue_error(hv0, err_error);
}
return 0;
}
}
return r;
}
uint32 garmin_start_session(garmin_unit *garmin)
{
garmin_packet p;
int i;
fd_set fds, rfds;
struct timeval tv;
int sel_ret = 0;
int ok = 0;
garmin_packetize(&p, Pid_Start_Session, 0, NULL);
p.packet.type = GARMIN_PROTOCOL_USB;
if (method && garmin->usb.fd != -1)
{
FD_ZERO(&fds);
FD_SET(garmin->usb.fd, &fds);
}
garmin_write(garmin, &p);
garmin_write(garmin, &p);
garmin_write(garmin, &p);
if (garmin_read(garmin, &p) == 16)
{
garmin->id = get_uint32(p.packet.data);
}
else
{
garmin->id = 0;
}
garmin_callback("start_session");
return garmin->id;
}
void garmin_print_packet(garmin_packet *p, int dir, FILE *fp)
{
int i;
int j;
uint32 s;
char hex[128];
char dec[128];
s = garmin_packet_size(p);
switch ( dir )
{
case GARMIN_DIR_READ: fprintf(fp,"<read"); break;
case GARMIN_DIR_WRITE: fprintf(fp,"<write"); break;
default: fprintf(fp,"<packet"); break;
}
fprintf(fp," type=\"0x%02x\" id=\"0x%04x\" size=\"%u\"",
garmin_packet_type(p),garmin_packet_id(p),s);
if (s > 0)
{
fprintf(fp,">\n");
for ( i = 0, j = 0; i < s; i++ )
{
sprintf(&hex[(3*(i&0x0f))]," %02x",p->packet.data[i]);
sprintf(&dec[(i&0x0f)],"%c",
(isalnum(p->packet.data[i]) ||
ispunct(p->packet.data[i]) ||
p->packet.data[i] == ' ') ?
p->packet.data[i] : '_');
if ( (i & 0x0f) == 0x0f )
{
j = 0;
fprintf(fp,"[%04x] %-54s %s\n",i-15,hex,dec);
}
else
{
j++;
}
}
if (j > 0)
{
fprintf(fp,"[%04x] %-54s %s\n",s-(s & 0x0f),hex,dec);
}
switch (dir)
{
case GARMIN_DIR_READ: fprintf(fp,"</read>\n"); break;
case GARMIN_DIR_WRITE: fprintf(fp,"</write>\n"); break;
default: fprintf(fp,"</packet>\n"); break;
}
}
else
{
fprintf(fp,"/>\n");
}
}