/* casio-qv-commands.c * * Copyright © 2001 Lutz Müller * * 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 "casio-qv-commands.h" #include <stdlib.h> #define STX 0x02 #define ETX 0x03 #define ENQ 0x05 #define ACK 0x06 #define DC2 0x12 #define NAK 0x15 #define ETB 0x17 #define UNKNOWN1 0xfe #define UNKNOWN2 0xe0 #define CASIO_QV_RETRIES 5 #define CR(result) {int r = (result); if (r < 0) return (r);} int QVping (Camera *camera) { unsigned char c; int result = GP_OK, i = 0; /* Send ENQ and wait for ACK */ while (1) { c = ENQ; CR (gp_port_write (camera->port, &c, 1)); result = gp_port_read (camera->port, &c, 1); /* If we got ACK, everything is fine. */ if (result >= 0) { switch (c) { case ACK: case ENQ: /* * According to gphoto, we need to wait * for ACK. But the camera of * David Wolfskill <david@catwhisker.org> * seems to return ENQ after some NAK. */ return (GP_OK); case NAK: /* The camera is not yet ready. */ break; case UNKNOWN1: case UNKNOWN2: /* * David Wolfskill <david@catwhisker.org> * has seen those two bytes if one sends * only ENQs to the camera. The camera first * answers with some NAKs, then with some * ACKs, and finally with UNKNOWN1 and * UNKNOWN2. */ while (gp_port_read (camera->port, &c, 1) >= 0); break; default: while (gp_port_read (camera->port, &c, 1) >= 0); break; } } if (++i < CASIO_QV_RETRIES) continue; /* If we got an error from libgphoto2_port, pass it along */ CR (result); /* Return some error code */ return (GP_ERROR_CORRUPTED_DATA); } } static int QVsend (Camera *camera, unsigned char *cmd, int cmd_len, unsigned char *buf, int buf_len) { unsigned char c; int checksum; const unsigned char *cmd_end; /* The camera does not insist on a ping each command, but */ /* sometimes it hangs up without one. */ CR (QVping (camera)); /* Write the request and calculate the checksum */ CR (gp_port_write (camera->port, cmd, cmd_len)); for (cmd_end = cmd+cmd_len, checksum = 0; cmd < cmd_end; ++cmd) checksum += *cmd; /* Read the checksum */ CR (gp_port_read (camera->port, &c, 1)); if (c != (unsigned char)(~checksum)) return (GP_ERROR_CORRUPTED_DATA); /* Send ACK */ c = ACK; CR (gp_port_write (camera->port, &c, 1)); /* Receive the answer */ if (buf_len) CR (gp_port_read (camera->port, buf, buf_len)); return (GP_OK); } static int QVblockrecv (Camera *camera, unsigned char **buf, unsigned long int *buf_len) { /* XXX - does the caller know to free *buf in case of an error? */ unsigned char c; int retries, pos; retries = 0; *buf = NULL; *buf_len = 0; pos = 0; /* Send DC2 */ c = DC2; CR (gp_port_write (camera->port, &c, 1)); while (1) { unsigned char buffer[2]; int size, i; int sum; unsigned char *new; /* Read STX */ CR (gp_port_read (camera->port, &c, 1)); if (c != STX) { retries++; c = NAK; CR (gp_port_write (camera->port, &c, 1)); if (retries > CASIO_QV_RETRIES) return (GP_ERROR_CORRUPTED_DATA); else continue; } /* Read sector size */ CR (gp_port_read (camera->port, buffer, 2)); size = (buffer[0] << 8) | buffer[1]; sum = buffer[0] + buffer[1]; /* Allocate the memory */ new = (unsigned char*)realloc (*buf, sizeof (char) * (*buf_len + size)); if (new == (unsigned char*)0) { if (*buf != (unsigned char*)0) free(*buf); return (GP_ERROR_NO_MEMORY); } *buf = new; *buf_len += size; /* Get the sector */ CR (gp_port_read (camera->port, *buf + pos, size)); for (i = 0; i < size; i++) sum += (*buf)[i + pos]; /* Get EOT or ETX and the checksum */ CR (gp_port_read (camera->port, buffer, 2)); sum += buffer[0]; /* Verify the checksum */ if ((unsigned char)(~sum) != buffer[1]) { retries++; c = NAK; CR (gp_port_write (camera->port, &c, 1)); if (retries > CASIO_QV_RETRIES) return (GP_ERROR_CORRUPTED_DATA); else continue; } /* Acknowledge and prepare for next packet */ c = ACK; CR (gp_port_write (camera->port, &c, 1)); pos += size; /* Are we done? */ if (buffer[0] == ETX) break; /* Yes */ else if (buffer[0] == ETB) continue; /* No */ else return (GP_ERROR_CORRUPTED_DATA); } return (GP_OK); } int QVbattery (Camera *camera, float *battery) { unsigned char cmd[6]; unsigned char b; cmd[0] = 'R'; cmd[1] = 'B'; cmd[2] = ENQ; cmd[3] = 0xff; cmd[4] = 0xfe; cmd[5] = 0xe6; CR (QVsend (camera, cmd, 6, &b, 1)); *battery = b / 16.; return (GP_OK); } int QVrevision (Camera *camera, long int *revision) { unsigned char cmd[2]; unsigned char buf[4]; cmd[0] = 'S'; cmd[1] = 'U'; CR (QVsend (camera, cmd, 2, buf, 4)); *revision = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; return (GP_OK); } int QVnumpic (Camera *camera) { unsigned char cmd[2]; unsigned char b; cmd[0] = 'M'; cmd[1] = 'P'; CR (QVsend (camera, cmd, 2, &b, 1)); return (b); } int QVpicattr (Camera *camera, int n, unsigned char *picattr) { unsigned char cmd[4]; unsigned char b; cmd[0] = 'D'; cmd[1] = 'Y'; cmd[2] = STX; cmd[3] = n+1; CR (QVsend (camera, cmd, 4, &b, 1)); *picattr = b; return (GP_OK); } int QVshowpic (Camera *camera, int n) { unsigned char cmd[3]; cmd[0] = 'D'; cmd[1] = 'A'; cmd[2] = n+1; CR (QVsend (camera, cmd, 3, NULL, 0)); return (GP_OK); } int QVsetpic (Camera *camera) { unsigned char cmd[2]; cmd[0] = 'D'; cmd[1] = 'L'; CR (QVsend (camera, cmd, 2, NULL, 0)); return (GP_OK); } int QVgetCAMpic (Camera *camera, unsigned char **data, unsigned long int *size, int fine) { unsigned char cmd[2]; cmd[0] = 'M'; cmd[1] = fine ? 'g' : 'G'; CR (QVsend (camera, cmd, 2, NULL, 0)); CR (QVblockrecv (camera, data, size)); return (GP_OK); } int QVgetYCCpic (Camera *camera, unsigned char **data, unsigned long int *size) { unsigned char cmd[2]; cmd[0] = 'M'; cmd[1] = 'K'; CR (QVsend (camera, cmd, 2, NULL, 0)); CR (QVblockrecv (camera, data, size)); return (GP_OK); } int QVdelete (Camera *camera, int n) { unsigned char cmd[4]; cmd[0] = 'D'; cmd[1] = 'F'; cmd[2] = n+1; cmd[3] = 0xff; CR (QVsend (camera, cmd, 4, NULL, 0)); return (GP_OK); } int QVprotect (Camera *camera, int n, int on) { unsigned char cmd[4]; cmd[0] = 'D'; cmd[1] = 'Y'; cmd[2] = on ? 1 : 0; cmd[3] = n+1; CR (QVsend (camera, cmd, 4, NULL, 0)); return (GP_OK); } int QVsize (Camera *camera, long int *size) { unsigned char cmd[2]; unsigned char buf[4]; cmd[0] = 'E'; cmd[1] = 'M'; CR (QVsend (camera, cmd, 2, buf, 4)); *size = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; return (GP_OK); } int QVcapture (Camera *camera) { unsigned char cmd[2]; unsigned char b; cmd[0] = 'D'; cmd[1] = 'R'; CR (QVsend (camera, cmd, 2, &b, 1)); return (GP_OK); } int QVstatus (Camera *camera, char *status) { unsigned char cmd[3]; cmd[0] = 'D'; cmd[1] = 'S'; cmd[2] = STX; CR (QVsend (camera, cmd, 3, status, 2)); return (GP_OK); } int QVreset (Camera *camera) { unsigned char cmd[2]; cmd[0] = 'Q'; cmd[1] = 'R'; CR (QVsend (camera, cmd, 2, NULL, 0)); return (GP_OK); } int QVsetspeed (Camera *camera, int speed) { unsigned char cmd[3]; gp_port_settings settings; cmd[0] = 'C'; cmd[1] = 'B'; switch (speed) { case 9600: cmd[2] = 46; break; case 19200: cmd[2] = 22; break; case 38400: cmd[2] = 11; break; case 57600: cmd[2] = 7; break; case 115200: cmd[2] = 3; break; default: return (GP_ERROR_NOT_SUPPORTED); } CR (QVsend (camera, cmd, 3, NULL, 0)); CR (gp_port_get_settings (camera->port, &settings)); settings.serial.speed = speed; CR (gp_port_set_settings (camera->port, settings)); CR (QVping (camera)); return (GP_OK); }