/* * 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 "gphoto2-endian.h" #include "digita.h" #define GP_MODULE "digita" #include <gphoto2/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) { 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) { 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; }