git-svn-id: https://svn.code.sf.net/p/gphoto/code/trunk/libgphoto2@5859 67ed7778-7388-44ab-90cf-0a291f65f57c
343 lines
7.6 KiB
C
343 lines
7.6 KiB
C
/*
|
|
* serial.c
|
|
*
|
|
* Serial digita support
|
|
*
|
|
* Copyright 1999-2001 Johannes Erdfelt
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
#ifdef OS2
|
|
#include <db.h>
|
|
#endif
|
|
#include <netinet/in.h>
|
|
|
|
#include "digita.h"
|
|
|
|
#define GP_MODULE "digita"
|
|
|
|
#include <gphoto2-port.h>
|
|
|
|
#define MAX_BEACON_RETRIES 5
|
|
#define MAX_BEACON_TIMEOUTS 2
|
|
|
|
/*
|
|
* u8 0xA5
|
|
* u8 0x5A
|
|
* u16 vendorid
|
|
* u16 deviceid
|
|
* u8 checksum
|
|
*/
|
|
#define BEACON_LEN 7
|
|
|
|
/*
|
|
* u8 0x5A
|
|
* u8 0xA5
|
|
* u8 0x55 (iftype)
|
|
* u8 commflag
|
|
* u4 reserved
|
|
* u2 pod_receive_mode
|
|
* u2 host_receive_mode
|
|
* u32 dataspeed
|
|
* u16 deviceframesize
|
|
* u16 hostframesize
|
|
* u8 checksum
|
|
*/
|
|
#define BEACON_ACK_LENGTH 13
|
|
|
|
/*
|
|
* s8 result
|
|
* u8 commflag
|
|
* u4 reserved
|
|
* u2 pod_receive_mode
|
|
* u2 host_receive_mode
|
|
* u32 dataspeed
|
|
* u16 deviceframesize
|
|
* u16 hostframesize
|
|
*/
|
|
#define BEACON_COMP_LENGTH 10
|
|
|
|
#define POLL_LENGTH_MASK 0x03FF
|
|
#define POLL_BOB 0x0400
|
|
#define POLL_EOB 0x0800
|
|
#define POLL_CMD 0x1000
|
|
#define POLL_POLL_MASK 0xE000
|
|
#define POLL_POLL (1 << 13)
|
|
|
|
#define POLL_ACK 0x01
|
|
#define POLL_NAK 0x02
|
|
|
|
static unsigned int checksum(const unsigned char *buffer, int len)
|
|
{
|
|
int i;
|
|
int limit = len - 1;
|
|
unsigned int sum = 0;
|
|
|
|
for (i = 0; i < limit; i++)
|
|
sum += *(buffer++);
|
|
|
|
return sum & 0xff;
|
|
}
|
|
|
|
static int poll_and_wait(gp_port *dev, int length, int bob, int eob)
|
|
{
|
|
unsigned short s, poll, poll_reply;
|
|
|
|
poll = POLL_POLL | POLL_CMD | (length & POLL_LENGTH_MASK) |
|
|
(bob ? POLL_BOB : 0) | (eob ? POLL_EOB : 0);
|
|
|
|
do {
|
|
s = htons(poll);
|
|
if (gp_port_write(dev, (void *)&s, sizeof(s)) < 0)
|
|
return -1;
|
|
if (gp_port_read(dev, (void *)&s, sizeof(s)) < 0)
|
|
return -1;
|
|
poll_reply = ntohs(s);
|
|
} while (poll_reply & POLL_NAK);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int digita_serial_send(CameraPrivateLibrary *dev, void *_buffer, int len)
|
|
{
|
|
unsigned char *buffer = _buffer;
|
|
unsigned short s;
|
|
int sent = 0, size;
|
|
|
|
while (sent < len) {
|
|
if ((len - sent) > dev->deviceframesize)
|
|
size = dev->deviceframesize;
|
|
else
|
|
size = len - sent;
|
|
|
|
if (poll_and_wait(dev->gpdev, size, sent == 0, (size + sent) == len) < 0)
|
|
return -1;
|
|
|
|
if (gp_port_write(dev->gpdev, buffer + sent, size) < 0)
|
|
return -1;
|
|
|
|
sent += size;
|
|
}
|
|
|
|
s = 0;
|
|
if (gp_port_write(dev->gpdev, (void *)&s, sizeof(s)) < 0)
|
|
return -1;
|
|
|
|
return len;
|
|
}
|
|
|
|
static int poll_and_reply(gp_port *dev, int *length, int *eob, int nak)
|
|
{
|
|
unsigned short s, poll;
|
|
|
|
if (gp_port_read(dev, (void *)&s, sizeof(s)) < 0)
|
|
return -1;
|
|
|
|
poll = ntohs(s);
|
|
if (length)
|
|
*length = poll & POLL_LENGTH_MASK;
|
|
if (eob)
|
|
*eob = poll & POLL_EOB;
|
|
|
|
s = htons(nak ? POLL_NAK : POLL_ACK);
|
|
if (gp_port_write(dev, (void *)&s, sizeof(s)) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int digita_serial_read(CameraPrivateLibrary *dev, void *_buffer, int len)
|
|
{
|
|
unsigned char *buffer = _buffer;
|
|
unsigned short s;
|
|
int received = 0, size, eob;
|
|
|
|
while (received < len) {
|
|
if (poll_and_reply(dev->gpdev, &size, &eob, 0) < 0)
|
|
return -1;
|
|
|
|
if (gp_port_read(dev->gpdev, buffer + received, size) < 0)
|
|
return -1;
|
|
|
|
received += size;
|
|
if (eob)
|
|
break;
|
|
}
|
|
|
|
if (gp_port_read(dev->gpdev, (void *)&s, sizeof(s)) < 0)
|
|
return -1;
|
|
|
|
return received;
|
|
}
|
|
|
|
int digita_serial_open(CameraPrivateLibrary *dev, Camera *camera)
|
|
{
|
|
GPPortSettings settings;
|
|
int selected_speed;
|
|
int ret, retries, negotiated = 0;
|
|
unsigned char buffer[20];
|
|
unsigned short s;
|
|
unsigned int l;
|
|
|
|
/* Get the settings */
|
|
ret = gp_port_get_settings(camera->port, &settings);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Remember the selected speed */
|
|
selected_speed = settings.serial.speed;
|
|
if (!selected_speed)
|
|
selected_speed = 115200; /* Try the maximum speed */
|
|
|
|
/* Set the settings */
|
|
settings.serial.speed = 9600;
|
|
settings.serial.bits = 8;
|
|
settings.serial.parity = 0;
|
|
settings.serial.stopbits = 1;
|
|
ret = gp_port_set_settings(camera->port, settings);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
dev->send = digita_serial_send;
|
|
dev->read = digita_serial_read;
|
|
|
|
gp_port_send_break(dev->gpdev, 4);
|
|
|
|
usleep(10000);
|
|
|
|
/* FIXME: In some situations we may want to try a slower speed */
|
|
for (retries = 0; !negotiated && retries < MAX_BEACON_RETRIES; retries++) {
|
|
unsigned char csum;
|
|
int i, timeouts = 0;
|
|
|
|
/*
|
|
* Read the beacon
|
|
*/
|
|
memset(buffer, 0, sizeof(buffer));
|
|
|
|
for (i = 0; (i < BEACON_LEN * 2) && (timeouts < MAX_BEACON_TIMEOUTS); i++) {
|
|
/* We specifically eat as much as we can to catch any */
|
|
/* garbage before or after */
|
|
ret = gp_port_read(dev->gpdev, (void *)buffer, 1);
|
|
if (ret < 0) {
|
|
GP_DEBUG("couldn't read beacon (ret = %d)", ret);
|
|
timeouts++;
|
|
continue;
|
|
}
|
|
|
|
if (buffer[0] == 0xA5)
|
|
break;
|
|
}
|
|
|
|
if (timeouts >= MAX_BEACON_TIMEOUTS)
|
|
continue;
|
|
|
|
ret = gp_port_read(dev->gpdev, (void *)(buffer + 1), BEACON_LEN - 1);
|
|
if (ret < 0) {
|
|
GP_DEBUG("couldn't read beacon (ret = %d)", ret);
|
|
continue;
|
|
}
|
|
|
|
if (buffer[0] != 0xA5 || buffer[1] != 0x5A) {
|
|
GP_DEBUG("Invalid header for beacon 0x%02x 0x%02x", buffer[0], buffer[1]);
|
|
continue;
|
|
}
|
|
|
|
csum = buffer[6];
|
|
buffer[6] = 0;
|
|
if (checksum(buffer, BEACON_LEN) != csum) {
|
|
GP_DEBUG("Beacon checksum failed (calculated 0x%02x, received 0x%02x)",
|
|
checksum(buffer, BEACON_LEN), csum);
|
|
continue;
|
|
}
|
|
|
|
memcpy((void *)&s, &buffer[2], 2);
|
|
GP_DEBUG("Vendor: 0x%04x", ntohs(s));
|
|
memcpy((void *)&s, &buffer[4], 2);
|
|
GP_DEBUG("Product: 0x%04x", ntohs(s));
|
|
|
|
/*
|
|
* Send the beacon acknowledgement
|
|
*/
|
|
buffer[0] = 0x5A; /* Magic */
|
|
buffer[1] = 0xA5;
|
|
buffer[2] = 0x55; /* I/F Type */
|
|
buffer[3] = 0; /* Comm Flag */
|
|
l = htonl(selected_speed);
|
|
memcpy(&buffer[4], (void *)&l, 4); /* Data speed */
|
|
s = htons(1023);
|
|
memcpy(&buffer[8], (void *)&s, 2); /* Device Frame Size */
|
|
s = htons(1023);
|
|
memcpy(&buffer[10], (void *)&s, 2); /* Host Frame Size */
|
|
buffer[12] = 0;
|
|
buffer[12] = checksum(buffer, BEACON_ACK_LENGTH);
|
|
|
|
ret = gp_port_write(dev->gpdev, (void *)buffer, BEACON_ACK_LENGTH);
|
|
if (ret < 0) {
|
|
GP_DEBUG("couldn't write beacon (ret = %d)", ret);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Read the beacon completion
|
|
*/
|
|
ret = gp_port_read(dev->gpdev, (void *)buffer, BEACON_COMP_LENGTH);
|
|
if (ret < 0) {
|
|
GP_DEBUG("couldn't read beacon_comp (ret = %d)", ret);
|
|
continue;
|
|
}
|
|
|
|
if ((signed char)buffer[0] < 0) {
|
|
GP_DEBUG("Bad status %d during beacon completion", (signed int)buffer[0]);
|
|
continue;
|
|
}
|
|
|
|
memcpy((void *)&s, &buffer[6], sizeof(s));
|
|
dev->deviceframesize = ntohs(s);
|
|
|
|
memcpy((void *)&l, &buffer[2], sizeof(l));
|
|
GP_DEBUG("negotiated %d", ntohl(l));
|
|
settings.serial.speed = ntohl(l);
|
|
|
|
usleep(100000); /* Wait before */
|
|
|
|
ret = gp_port_set_settings(dev->gpdev, settings);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
usleep(100000); /* Wait after */
|
|
|
|
/*
|
|
* The host interface spec mentions kTestSerialData, but
|
|
* doesn't elaborate anywhere else on it. So, I guess we
|
|
* assume everything is working here
|
|
*/
|
|
negotiated = 1;
|
|
}
|
|
|
|
return negotiated ? 0 : -1;
|
|
}
|
|
|