/**************************************************************************** * * File: serial.c * * Serial communication layer. * ****************************************************************************/ /**************************************************************************** * * include files * ****************************************************************************/ #include "config.h" #include <stdio.h> #include <unistd.h> #include <termios.h> #include <fcntl.h> #include <string.h> #include <sys/time.h> #include <errno.h> #include <sys/ioctl.h> #include <stdlib.h> #include <ctype.h> #include <gphoto2/gphoto2.h> #ifdef ENABLE_NLS # include <libintl.h> # undef _ # define _(String) dgettext (GETTEXT_PACKAGE, String) # ifdef gettext_noop # define N_(String) gettext_noop (String) # else # define N_(String) (String) # endif #else # define textdomain(String) (String) # define gettext(String) (String) # define dgettext(Domain,Message) (Message) # define dcgettext(Domain,Message,Type) (Message) # define bindtextdomain(Domain,Directory) (Domain) # define _(String) (String) # define N_(String) (String) #endif #include "library.h" #include "canon.h" #include "serial.h" #include "util.h" #include "crc.h" #ifndef MAX # define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #ifndef MIN # define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif #ifdef __GNUC__ # define __unused__ __attribute__((unused)) #else # define __unused__ #endif /** * serial_flush_input * @gdev: serial port to use * * Dummy function. * */ static void serial_flush_input (GPPort __unused__ *gdev) { } /** * serial_flush_output * @gdev: serial port to use * * Dummy function. * */ static void serial_flush_output (GPPort __unused__ *gdev) { } /** * canon_serial_change_speed * @gdev: serial port to use * @speed: the new speed * * Changes the speed of the communication. * * Returns: 1 on success. * 0 on any error. * */ static int canon_serial_change_speed (GPPort *gdev, int speed) { gp_port_settings settings; /* set speed */ gp_port_get_settings (gdev, &settings); settings.serial.speed = speed; gp_port_set_settings (gdev, settings); usleep (70000); return 1; } /** * canon_serial_get_cts * @gdev: serial port to use * * Gets the status of the CTS (Clear To Send) line on the serial port. * * CTS is "1" when the camera is ON, and "0" when it is OFF. * * Returns: 1 on CTS high. * 0 on CTS low. * */ static int canon_serial_get_cts (GPPort *gdev) { GPLevel level; gp_port_get_pin (gdev, PIN_CTS, &level); return (level); } /** * canon_serial_init * @camera: Camera object to initialize * * Initializes the given serial device by setting speed, parity, etc. * * Returns: %GP_OK * */ int canon_serial_init (Camera *camera) { GPPortSettings settings; GP_DEBUG ("Initializing the (serial) camera."); /* Get the current settings */ gp_port_get_settings (camera->port, &settings); /* Adjust the current settings */ settings.serial.speed = 9600; settings.serial.bits = 8; settings.serial.parity = 0; settings.serial.stopbits = 1; /* Set the new settings */ gp_port_set_settings (camera->port, settings); return GP_OK; } /** * canon_serial_send * @camera: Camera object to work with * @buf: the raw data buffer to send * @len: the length of the buffer * @sleep: time in usec to wait between characters * * Send the given buffer with given length over the serial line. * * Returns: 0 on success, -1 on error. * */ static int canon_serial_send (Camera *camera, const unsigned char *buf, int len, int sleep) { int i; /* the A50 does not like to get too much data in a row at 115200 * The S10 and S20 do not have this problem */ if (sleep > 0 && camera->pl->slow_send == 1) { for (i = 0; i < len; i++) { gp_port_write (camera->port, (char *) buf, 1); buf++; usleep (sleep); } } else { gp_port_write (camera->port, (char *) buf, len); } return 0; } /** * serial_set_timeout * @gdev: serial port to use * @to: timeout in milliseconds * * Sets the timeout, in miliseconds. * */ static void serial_set_timeout (GPPort *gdev, int to) { gp_port_set_timeout (gdev, to); } /** * canon_serial_get_byte * @gdev: serial port to use * * Gets the next byte from the serial line. * Actually the function reads chunks of data and keeps them in a cache. * Only one byte per call will be returned. * * Returns: the byte on success, -1 on error. * */ static int canon_serial_get_byte (GPPort *gdev) { static unsigned char cache[512]; static unsigned char *cachep = cache; static unsigned char *cachee = cache; int recv; /* if still data in cache, get it */ if (cachep < cachee) { return (int) *cachep++; } recv = gp_port_read (gdev, (char *)cache, 1); if (recv < 0) /* An error occurred */ return -1; cachep = cache; cachee = cache + recv; if (recv) { return (int) *cachep++; } return -1; } /* ------------------------- Frame-level processing ------------------------- */ /** * canon_serial_send_frame * @camera: Camera object to work with * @pkt: Data to send to camera * @len: Length of packet * * Sends a frame of data to camera * * Returns: 1 if canon_serial_send() succeeds, 0 if it fails * */ static int canon_serial_send_frame (Camera *camera, const unsigned char *pkt, int len) { static unsigned char buffer[2100]; /* worst case: two maximum-sized packets (~1020 bytes, full of data that needs to be escaped */ unsigned char *p; p = buffer; *p++ = CANON_FBEG; while (len--) { if (p < buffer || (unsigned int)(p - buffer) >= sizeof (buffer) - 1) { GP_DEBUG ("FATAL ERROR: send buffer overflow"); return -1; } if (*pkt != CANON_FBEG && *pkt != CANON_FEND && *pkt != CANON_ESC) *p++ = *pkt++; else { *p++ = CANON_ESC; *p++ = *pkt++ ^ CANON_XOR; } } *p++ = CANON_FEND; return !canon_serial_send (camera, buffer, p - buffer, USLEEP2); } /** * canon_serial_recv_frame * @camera: Camera object to work with * @len: to receive the length of the buffer * * Receive a frame from the camera * * Returns: a buffer containing a frame from the camera, or NULL on error. * On success, @len will contain the length of the buffer. * */ static unsigned char * canon_serial_recv_frame (Camera *camera, int *len) { static unsigned char buffer[5000]; /* more than enough :-) (allow for a few run-together packets) */ unsigned char *p = buffer; int c; while ((c = canon_serial_get_byte (camera->port)) != CANON_FBEG) { if (c == -1) return NULL; } while ((c = canon_serial_get_byte (camera->port)) != CANON_FEND) { if (c < 0) return NULL; if (c == CANON_ESC) c = canon_serial_get_byte (camera->port) ^ CANON_XOR; if (p < buffer || (unsigned int)(p - buffer) >= sizeof (buffer)) { GP_DEBUG ("FATAL ERROR: receive buffer overflow"); return NULL; } *p++ = c; } /* If you don't want to see the data dumped, change the frontend to * set a lower debug level */ gp_log (GP_LOG_DATA, "canon", "RECV (without CANON_FBEG and CANON_FEND bytes)"); gp_log_data ("canon", (char *)buffer, p - buffer); if (len) *len = p - buffer; return buffer; } /* ------------------------ Packet-level processing ------------------------- */ /** * canon_serial_send_packet * @camera: Camera object to work with * @type: * @seq: * @pkt: data to send to camera * @len: length of data * * frames a packet (generates CRC, packs with sequence number and * length) and sends it to the camera through the serial port using * canon_serial_send_frame(). * * Returns: status from canon_serial_send_frame() * */ static int canon_serial_send_packet (Camera *camera, unsigned char type, unsigned char seq, unsigned char *pkt, int len) { unsigned char *hdr = pkt - PKT_HDR_LEN; unsigned short crc; hdr[PKT_TYPE] = type; hdr[PKT_SEQ] = seq; hdr[PKT_LEN_LSB] = len & 0xff; hdr[PKT_LEN_MSB] = len >> 8; if (type == PKT_NACK) { hdr[PKT_TYPE] = PKT_ACK; hdr[PKT_TYPE + 1] = '\xff'; /* PKTACK_NACK; */ } if (type == PKT_UPLOAD_EOT) { hdr[PKT_TYPE] = PKT_EOT; hdr[PKT_TYPE + 1] = 0x3; len = 2; } if (type == PKT_EOT || type == PKT_ACK || type == PKT_NACK) len = 2; /* @@@ hack */ crc = canon_psa50_gen_crc (hdr, len + PKT_HDR_LEN); pkt[len] = crc & 0xff; pkt[len + 1] = crc >> 8; return canon_serial_send_frame (camera, hdr, len + PKT_HDR_LEN + 2); } /** * canon_serial_recv_packet * @camera: Camera object to work with * @type: Type of packet * @seq: Sequence number of packet * @len: length of data received * * Receives a packet from the serial port using * canon_serial_send_frame(), decodes frame information (type, * sequence number, and length), and returns it stripped of frame * information. * * Returns: packet data (or NULL if failure). Type in @type, sequence * number in @seq, and length in @len. * */ static unsigned char * canon_serial_recv_packet (Camera *camera, unsigned char *type, unsigned char *seq, int *len) { unsigned char *pkt; unsigned short crc; int raw_length, length = 0; pkt = canon_serial_recv_frame (camera, &raw_length); if (!pkt) return NULL; if (raw_length < PKT_HDR_LEN) { GP_DEBUG ("ERROR: packet truncated"); return NULL; } if (pkt[PKT_TYPE] == PKT_MSG) { length = pkt[PKT_LEN_LSB] | (pkt[PKT_LEN_MSB] << 8); if (length + PKT_HDR_LEN > raw_length - 2) { GP_DEBUG ("ERROR: invalid length"); /*fprintf(stderr,"Sending NACK"); canon_serial_send_packet(PKT_NACK,camera->pl->seq_rx++,camera->pl->psa50_eot+PKT_HDR_LEN,0); */ camera->pl->receive_error = ERROR_RECEIVED; return NULL; } } crc = pkt[raw_length - 2] | (pkt[raw_length - 1] << 8); if (!canon_psa50_chk_crc (pkt, raw_length - 2, crc)) { GP_DEBUG ("ERROR: CRC error"); return NULL; } *type = pkt[PKT_TYPE]; if (seq) *seq = pkt[PKT_SEQ]; if (len) *len = length; if (*type == PKT_ACK || *type == PKT_EOT) return pkt; return pkt + PKT_HDR_LEN; } /* ----------------------- Message-level processing ------------------------ */ /** * canon_serial_wait_for_ack * @camera: Camera object to work with * * Waits for an "ACK" from the camera. * * Returns: * 1 : ACK received * 0 : communication error (no reply received for example) * -1 : NACK received. */ static int canon_serial_wait_for_ack (Camera *camera) { unsigned char *pkt; unsigned char type, seq, old_seq; int len; while (1) { pkt = canon_serial_recv_packet (camera, &type, &seq, &len); if (!pkt) return 0; if (seq == camera->pl->seq_tx && type == PKT_ACK) { if (pkt[2] == PKTACK_NACK) { GP_DEBUG ("ERROR: NACK received"); return -1; } camera->pl->seq_tx++; return 1; } old_seq = '\0'; if (type == PKT_EOT) { old_seq = pkt[0]; if (camera->pl->receive_error == NOERROR) { GP_DEBUG ("Old EOT received, sending corresponding ACK"); if (!canon_serial_send_packet (camera, PKT_ACK, old_seq, camera->pl->psa50_eot + PKT_HDR_LEN, 0)) return 0; pkt = canon_serial_recv_packet (camera, &type, &seq, &len); if (!pkt) return 0; if (seq == old_seq && type == PKT_ACK) { if (pkt[2] == PKTACK_NACK) { GP_DEBUG ("Old EOT acknowledged"); return -1; } return 1; } } } /* error already aknowledged, we skip the following ones */ if (camera->pl->receive_error == ERROR_RECEIVED) { if (!canon_serial_send_packet (camera, PKT_NACK, old_seq, camera->pl->psa50_eot + PKT_HDR_LEN, 0)) return 0; return 1; } GP_DEBUG ("ERROR: ACK format or sequence error, retrying"); GP_DEBUG ("Sending NACK"); canon_serial_send_packet (camera, PKT_NACK, camera->pl->seq_rx++, camera->pl->psa50_eot + PKT_HDR_LEN, 0); camera->pl->receive_error = ERROR_RECEIVED; /* * just keep on trying. protocol seems to retransmit EOTs, so we may get * some old EOTs when we're actually expecting ACKs. */ } } /** * canon_serial_send_msg * @camera: Camera object to work with * @mtype: message type. * @dir: direction. * @ap: message payload (list of arguments, see 'man va_start' * * Sends a message to the camera. * * Returns: * -1 on error * 0 if canon_serial_send_packet() fails * 1 on good ACK received */ static int canon_serial_send_msg (Camera *camera, unsigned char mtype, unsigned char dir, va_list * ap) { unsigned char buffer[MAX_PKT_PAYLOAD + 2]; /* allow space for CRC */ unsigned char upload_buffer[MAX_PKT_PAYLOAD + 2]; unsigned char *pkt, *pkt2, *pos; int total, good_ack, try; memset (buffer, 0, PKT_HDR_LEN + MSG_HDR_LEN); pkt = buffer + PKT_HDR_LEN; pkt[MSG_02] = 2; pkt[MSG_MTYPE] = mtype; pkt[MSG_DIR] = dir; pos = pkt + MSG_HDR_LEN; total = 0; while (1) { const unsigned char *str; int len; str = va_arg (*ap, unsigned char *); if (!str) break; len = va_arg (*ap, int); if (pos + len - pkt > MAX_MSG_SIZE && camera->pl->uploading != 1) { GP_DEBUG ("FATAL ERROR: message too big (%i)", pos + len - pkt); return -1; } memcpy (pos, str, len); pos += len; } total = pos - pkt; pkt[MSG_LEN_LSB] = total & 0xff; pkt[MSG_LEN_MSB] = total >> 8; if (camera->pl->uploading == 1) { memset (upload_buffer, 0, PKT_HDR_LEN + MSG_HDR_LEN); pkt2 = upload_buffer; memcpy (pkt2, pkt + UPLOAD_DATA_BLOCK, total - UPLOAD_DATA_BLOCK); for (try = 0; try < MAX_TRIES; try++) { canon_serial_send_packet (camera, PKT_MSG, 0, pkt, UPLOAD_DATA_BLOCK); canon_serial_send_packet (camera, PKT_MSG, 0x1, pkt2, total - UPLOAD_DATA_BLOCK); if (!canon_serial_send_packet (camera, PKT_UPLOAD_EOT, camera->pl->seq_tx, camera->pl->psa50_eot + PKT_HDR_LEN, 1)) return 0; if (!canon_serial_send_packet (camera, PKT_UPLOAD_EOT, camera->pl->seq_tx, camera->pl->psa50_eot + PKT_HDR_LEN, 1)) return 0; good_ack = canon_serial_wait_for_ack (camera); if (good_ack == 1) return good_ack; } return -1; } else { pkt[MSG_LEN_LSB] = total & 0xff; pkt[MSG_LEN_MSB] = total >> 8; for (try = 1; try < MAX_TRIES; try++) { if (!canon_serial_send_packet (camera, PKT_MSG, 0, pkt, total)) return 0; if (!canon_serial_send_packet (camera, PKT_EOT, camera->pl->seq_tx, camera->pl->psa50_eot + PKT_HDR_LEN, 1)) return 0; good_ack = canon_serial_wait_for_ack (camera); if (good_ack == -1) { GP_DEBUG ("NACK received, retrying command"); } else if (good_ack == 1) { return good_ack; } else { GP_DEBUG ("No ACK received, retrying command"); if (try == 2) { /* is the camera still there? */ if (!canon_serial_send_packet (camera, PKT_EOT, camera->pl->seq_tx, camera->pl->psa50_eot + PKT_HDR_LEN, 0)) return 0; good_ack = canon_serial_wait_for_ack (camera); if (good_ack == 0) { camera->pl->receive_error = FATAL_ERROR; GP_DEBUG ("ERROR: FATAL ERROR"); clear_readiness (camera); return -1; } } } } return -1; } } /** * canon_serial_recv_msg * @camera: Camera object to work with * @mtype: message type. * @dir: direction. * @total: payload length (set by this function). * @context: context for error reporting * * Receives a message from the camera. * * See the "Protocol" file for an explanation of the various * elements needed to handle a message. * * Returns: * char* pointer to the message payload; NULL on failure. * */ static unsigned char * canon_serial_recv_msg (Camera *camera, unsigned char mtype, unsigned char dir, unsigned int *total, GPContext *context) { static unsigned char *msg = NULL; static int msg_size = 512; /* initial allocation/2 */ unsigned char *frag; unsigned char type, seq; int len, length = 0, msg_pos = 0; while (1) { frag = canon_serial_recv_packet (camera, &type, NULL, &len); if (!frag) return NULL; if (type == PKT_MSG) break; /* uploading is special */ /* if (type == PKT_ACK && mtype == 0x3 && dir == 0x21) break; */ if (type == PKT_EOT) { GP_DEBUG ("Old EOT received sending corresponding ACK"); canon_serial_send_packet (camera, PKT_ACK, frag[0], camera->pl->psa50_eot + PKT_HDR_LEN, 0); } GP_DEBUG ("ERROR: protocol error, retrying"); } /* we keep the fragment only if there was no error */ if (camera->pl->receive_error == NOERROR) { length = frag[MSG_LEN_LSB] | (frag[MSG_LEN_MSB] << 8); /* while uploading we expect 2 ACKs and a message 0x3 0x21 * not always in the same order */ /* if (type == PKT_ACK && mtype == 0x3 && dir == 0x21) { GP_DEBUG("ignoring ACK received while waiting for MSG"); return frag; } */ if (len < MSG_HDR_LEN || frag[MSG_02] != 2) { GP_DEBUG ("ERROR: message format error"); return NULL; } if (frag[MSG_MTYPE] != mtype || frag[MSG_DIR] != dir) { if (frag[MSG_MTYPE] == '\x01' && frag[MSG_DIR] == '\x00' && memcmp (frag + 12, "\x30\x00\x00\x30", 4)) { gp_context_error (context, _("Battery exhausted, camera off.")); camera->pl->receive_error = ERROR_LOWBATT; } else { gp_context_error (context, _("ERROR: unexpected message")); } return NULL; } frag += MSG_HDR_LEN; len -= MSG_HDR_LEN; } while (1) { if (camera->pl->receive_error == NOERROR) { if (msg_pos + len > length) { gp_context_error (context, _("ERROR: message overrun")); return NULL; } if (msg_pos + len > msg_size || !msg) { msg_size *= 2; msg = realloc (msg, msg_size); if (!msg) { perror ("realloc"); exit (1); } } memcpy (msg + msg_pos, frag, len); msg_pos += len; } frag = canon_serial_recv_packet (camera, &type, &seq, &len); if (!frag) return NULL; if (type == PKT_EOT) { /* in case of error we don't want to stop as the camera will send the 1st packet of the sequence again */ if (camera->pl->receive_error == ERROR_RECEIVED) { camera->pl->seq_rx = seq; canon_serial_send_packet (camera, PKT_NACK, camera->pl->seq_rx, camera->pl->psa50_eot + PKT_HDR_LEN, 0); camera->pl->receive_error = ERROR_ADDRESSED; } else { if (seq == camera->pl->seq_rx) break; gp_context_error (context, _("ERROR: out of sequence.")); return NULL; } } if (type != PKT_MSG && camera->pl->receive_error == NOERROR) { gp_context_error (context, _("ERROR: unexpected packet type.")); return NULL; } if (type == PKT_EOT && camera->pl->receive_error == ERROR_RECEIVED) { camera->pl->receive_error = ERROR_ADDRESSED; } if (type == PKT_MSG && camera->pl->receive_error == ERROR_ADDRESSED) { msg_pos = 0; length = frag[MSG_LEN_LSB] | (frag[MSG_LEN_MSB] << 8); if (len < MSG_HDR_LEN || frag[MSG_02] != 2) { gp_context_error (context, _("ERROR: message format error.")); return NULL; } if (frag[MSG_MTYPE] != mtype || frag[MSG_DIR] != dir) { if (frag[MSG_MTYPE] == '\x01' && frag[MSG_DIR] == '\x00' && memcmp (frag + 12, "\x30\x00\x00\x30", 4)) { gp_context_error (context, _("Battery exhausted, camera off.")); camera->pl->receive_error = ERROR_LOWBATT; } else { gp_context_error (context, _("ERROR: unexpected message2.")); } return NULL; } frag += MSG_HDR_LEN; len -= MSG_HDR_LEN; camera->pl->receive_error = NOERROR; } } if (camera->pl->receive_error == ERROR_ADDRESSED) { camera->pl->receive_error = NOERROR; } if (camera->pl->receive_error == NOERROR) { /*we want to be sure the camera U N D E R S T A N D S our packets */ if (camera->pl->uploading == 1 && camera->pl->md->model == CANON_CLASS_1) camera->pl->slow_send = 1; if (!canon_serial_send_packet (camera, PKT_ACK, camera->pl->seq_rx++, camera->pl->psa50_eot + PKT_HDR_LEN, 0)) { if (camera->pl->uploading == 1 && camera->pl->md->model == CANON_CLASS_1) camera->pl->slow_send = 0; return NULL; } if (camera->pl->uploading == 1 && camera->pl->md->model == CANON_CLASS_1) camera->pl->slow_send = 0; if (total) *total = msg_pos; return msg; } return NULL; } /** * canon_serial_dialogue: * @camera: camera with which to communicate * @context: context for error reporting * @mtype : type * @dir : direction * @len : length of the received payload * @Varargs: The rest of the arguments will be put together to * fill up the payload of the request message. * * Higher level function: sends a message and waits for a * reply from the camera. * * Payload: each argument after "len" goes by 2: the variable itself, * and the next argument has to be its length. You also have to finish * the list by a "NULL". * * Example: To send a string called "name" : * canon_serial_dialogue(0x05,0x12,&len,name,strlen(name)+1,NULL); * * Returns: buffer received from canon_serial_recv_msg(), NULL if failure * */ unsigned char * canon_serial_dialogue (Camera *camera, GPContext *context, unsigned char mtype, unsigned char dir, unsigned int *len, ...) { va_list ap; int okay, try; unsigned char *good_ack; for (try = 1; try < MAX_TRIES; try++) { va_start (ap, len); okay = canon_serial_send_msg (camera, mtype, dir, &ap); va_end (ap); if (!okay) return NULL; /* while uploading we receive 2 ACKs and 1 confirmation message * The first ACK has already been received if we are here */ if (camera->pl->uploading == 1) { camera->pl->seq_tx--; good_ack = canon_serial_recv_msg (camera, mtype, dir ^ DIR_REVERSE, len, context); if (!good_ack) return NULL; if (good_ack[0] == camera->pl->seq_tx && good_ack[1] == 0x5) { GP_DEBUG ("ACK received waiting for the confirmation message"); good_ack = canon_serial_recv_msg (camera, mtype, dir ^ DIR_REVERSE, len, context); } else { okay = canon_serial_wait_for_ack (camera); if (okay == 1) return good_ack; } } else good_ack = canon_serial_recv_msg (camera, mtype, dir ^ DIR_REVERSE, len, context); if (good_ack) return good_ack; if (camera->pl->receive_error == NOERROR) { GP_DEBUG ("Resending message..."); camera->pl->seq_tx--; } if (camera->pl->receive_error == FATAL_ERROR) break; } return NULL; } /* ----------------------- Command-level processing ------------------------ */ /** * canon_serial_end: * @camera: the camera to switch off * * Switches the @camera off * * Returns: %GP_OK * */ static int canon_serial_end (Camera *camera) { canon_serial_send (camera, (unsigned char *)"\xC0\x00\x02\x55\x2C\xC1", 6, USLEEP2); canon_serial_send (camera, (unsigned char *)"\xC0\x00\x04\x01\x00\x00\x00\x24\xC6\xC1", 8, USLEEP2); return GP_OK; } /** * canon_serial_off: * @camera: the camera to switch off * * Switches the #camera off, and resets the serial driver to 9600 bauds, * in order to be ready to switch the camera back on again if wanted. * Should better be named psa50_serial_off * * Returns: %GP_OK * */ int canon_serial_off (Camera *camera) { canon_serial_send (camera, (unsigned char *)"\xC0\x00\x02\x55\x2C\xC1", 6, USLEEP2); canon_serial_send (camera, (unsigned char *)"\xC0\x00\x04\x01\x00\x00\x00\x24\xC6\xC1", 8, USLEEP2); canon_serial_change_speed (camera->port, 9600); return GP_OK; } /** * canon_serial_error_type * @camera: Camera object to work with * * logs a debug message corresponding * to the error encountered * */ void canon_serial_error_type (Camera *camera) { switch (camera->pl->receive_error) { case ERROR_LOWBATT: GP_DEBUG ("ERROR: no battery left, Bailing out!"); break; case FATAL_ERROR: GP_DEBUG ("ERROR: camera connection lost!"); break; default: GP_DEBUG ("ERROR: malformed message"); break; } } /** * canon_serial_put_file * @camera: Camera object to work with * @file: CameraFile object to upload * @destname: name file should have on camera * @destpath: pathname for directory to put file * @context: context for error reporting * * Uploads file to @camera via serial port * * Returns: gphoto2 error code * */ int canon_serial_put_file (Camera *camera, CameraFile *file, char *destname, char *destpath, GPContext *context) { unsigned char *msg; char filename[64]; char buf[4096]; int offset = 0; char offset2[4]; int block_len; char block_len2[4]; unsigned int sent = 0; int i, j = 0; unsigned int len, hdr_len; unsigned long int size; const char *data, *name; unsigned int id; camera->pl->uploading = 1; gp_file_get_name (file, &name); for (i = 0; name[i]; i++) filename[i] = toupper (name[i]); filename[i] = '\0'; hdr_len = HDR_FIXED_LEN + strlen (name) + strlen (destpath); gp_file_get_data_and_size (file, &data, &size); id = gp_context_progress_start (context, size, _("Uploading file...")); while (sent < size) { if (size < DATA_BLOCK) block_len = size; else if ((size - sent < DATA_BLOCK)) block_len = size - sent; else block_len = DATA_BLOCK; offset = sent; for (i = 0; i < 4; i++) { offset2[i] = (offset >> (8 * i)) & 0xff; block_len2[i] = (block_len >> (8 * i)) & 0xff; } for (i = 0; i < DATA_BLOCK; i++) { buf[i] = data[j]; j++; } msg = canon_serial_dialogue (camera, context, 0x3, 0x11, &len, "\x02\x00\x00\x00", 4, offset2, 4, block_len2, 4, destpath, strlen (destpath), destname, strlen (destname) + 1, buf, block_len, NULL); if (!msg) { camera->pl->uploading = 0; return GP_ERROR; } sent += block_len; gp_context_progress_update (context, id, sent); } gp_context_progress_stop (context, id); camera->pl->uploading = 0; return GP_OK; } /** * canon_serial_get_file: * @camera: camera to lock keys on * @name: name of file to fetch * @length: to receive length of image data * @context: context for error reporting * * Get a file from a USB_connected Canon camera. * * Returns: buffer containing file data (or NULL on failure); length * in @length. * */ unsigned char * canon_serial_get_file (Camera *camera, const char *name, unsigned int *length, GPContext *context) { unsigned char *file = NULL; unsigned char *msg; unsigned char name_len; unsigned int total = 0, expect = 0, size, id; unsigned int len; if (camera->pl->receive_error == FATAL_ERROR) { GP_DEBUG ("ERROR: can't continue a fatal error condition detected"); return NULL; } name_len = strlen (name) + 1; msg = canon_serial_dialogue (camera, context, 0x1, 0x11, &len, "\x00\x00\x00\x00", 5, &name_len, 1, "\x00", 2, name, strlen (name) + 1, NULL); if (!msg) { canon_serial_error_type (camera); return NULL; } id = gp_context_progress_start (context, le32atoh (msg + 4), _("Getting file...")); while (msg) { if (len < 20 || le32atoh (msg)) { break; } if (!file) { total = le32atoh (msg + 4); if (total > camera->pl->md->max_picture_size) { GP_DEBUG ("ERROR: %d is too big", total); break; } file = malloc (total); if (!file) { perror ("malloc"); break; } if (length) *length = total; } size = le32atoh (msg + 12); if (le32atoh (msg + 8) != expect || expect + size > total || size > len - 20) { GP_DEBUG ("ERROR: doesn't fit"); break; } memcpy (file + expect, msg + 20, size); expect += size; gp_context_progress_update (context, id, expect); if ((expect == total) != le32atoh (msg + 16)) { GP_DEBUG ("ERROR: end mark != end of data"); break; } if (expect == total) { gp_context_progress_stop (context, id); return file; } msg = canon_serial_recv_msg (camera, 0x1, 0x21, &len, context); } free (file); file = NULL; return NULL; } /** * canon_serial_get_dirents: * @camera: camera to initialize * @dirent_data: to receive directory data * @dirents_length: to receive length of @dirent_data * @path: pathname of directory to list * @context: context for error reporting * * Lists a directory. * * Returns: gphoto2 error code * */ int canon_serial_get_dirents (Camera *camera, unsigned char **dirent_data, unsigned int *dirents_length, const char *path, GPContext *context) { unsigned char *p, *temp_ch, *data = NULL; unsigned int mallocd_bytes, total_size; *dirent_data = NULL; /* fetch all directory entries, the first one is a little special */ p = canon_serial_dialogue (camera, context, 0xb, 0x11, dirents_length, "", 1, path, strlen (path) + 1, "\x00", 2, NULL); if (p == NULL) { gp_context_error (context, _("canon_serial_get_dirents: " "canon_serial_dialogue failed to fetch directory entries")); return GP_ERROR; } /* In the RS232 implementation, we should never get less than 5 bytes */ if (*dirents_length < 5) { gp_context_error (context, _("canon_serial_get_dirents: " "Initial dirent packet too short (only %i bytes)"), *dirents_length); return GP_ERROR; } /* don't use GP_DEBUG since we log this with GP_LOG_DATA */ gp_log (GP_LOG_DATA, "canon", "canon_serial_get_dirents: " "dirent packet received from canon_serial_dialogue:"); gp_log_data ("canon", (char *)p, *dirents_length); /* the first five bytes is only for the RS232 implementation * of this command, we do not need to copy them so therefore * we don't need to malloc() them either */ mallocd_bytes = MAX (1024, *dirents_length - 5); data = malloc (mallocd_bytes); if (!data) { gp_context_error (context, _("canon_serial_get_dirents: " "Could not allocate %i bytes of memory"), mallocd_bytes); return GP_ERROR_NO_MEMORY; } /* the first five bytes is only for the RS232 implementation * of this command, do not copy them */ memcpy (data, p + 5, (*dirents_length - 5)); total_size = *dirents_length; /* p[4] indicates this is not the last packet, * read additional packets until there are no more * directory entries to read */ while (!p[4]) { GP_DEBUG ("p[4] is %i", (int) p[4]); p = canon_serial_recv_msg (camera, 0xb, 0x21, dirents_length, context); if (p == NULL) { gp_context_error (context, _("canon_serial_get_dirents: " "Failed to read another directory entry")); free (data); data = NULL; return GP_ERROR; } /* don't use GP_DEBUG since we log this with GP_LOG_DATA */ gp_log (GP_LOG_DATA, "canon", "canon_serial_get_dirents: " "dirent packet received from canon_serial_recv_msg:"); gp_log_data ("canon", (char *)p, *dirents_length); /* the first five bytes is only for the RS232 implementation, * don't count them when checking dirent size */ if (*dirents_length - 5 < CANON_MINIMUM_DIRENT_SIZE) { gp_context_error (context, _("canon_serial_get_dirents: " "Truncated directory entry received")); free (data); data = NULL; return GP_ERROR; } /* check if we need to allocate some more memory, * the first five bytes is only for the RS232 * implementation of this command, don't need to * malloc for them. note that we ensured earlier in this * function that *dirents_length >= 5. */ if (total_size + (unsigned int)(*dirents_length - 5) > mallocd_bytes) { /* we allocate 1024 bytes chunks instead * of the exact number of bytes needed. * this is OK since we will free this * before returning from canon_int_list_directory * (our caller). */ mallocd_bytes += MAX (1024, *dirents_length); /* check if we are reading unrealistic ammounts * of directory entries so that we don't loop * forever. 1024 * 1024 is picked out of the blue. */ if (mallocd_bytes > 1024 * 1024) { gp_context_error (context, _("canon_serial_get_dirents: " "Too many dirents, we must be looping.")); free (data); data = NULL; return GP_ERROR; } temp_ch = realloc (data, mallocd_bytes); if (!temp_ch) { gp_context_error (context, _("canon_serial_get_dirents: " "Could not resize dirent buffer " "to %i bytes"), mallocd_bytes); free (data); data = NULL; return GP_ERROR; } data = temp_ch; } /* the first five bytes is only for the RS232 * implementation of this command, don't copy them. */ memcpy (data + total_size, p + 5, (*dirents_length - 5)); total_size += (*dirents_length - 5); } GP_DEBUG ("OK - this was last dirent"); *dirent_data = data; return GP_OK; } /** * canon_serial_ready: * @camera: camera to get ready * @context: context for error reporting * * serial part of canon_int_ready * * Returns: gphoto2 error code * */ int canon_serial_ready (Camera *camera, GPContext *context) { unsigned char type, seq; int good_ack, speed, try, len, i; unsigned char *pkt; int res; char cam_id_str[2000]; unsigned int id; GP_DEBUG ("canon_int_ready()"); serial_set_timeout (camera->port, 900); /* 1 second is the delay for awakening the camera */ serial_flush_input (camera->port); serial_flush_output (camera->port); camera->pl->receive_error = NOERROR; /* First of all, we must check if the camera is already on */ /* cts=canon_serial_get_cts(); GP_DEBUG("cts : %i",cts); if (cts==32) { CTS == 32 when the camera is connected. */ if (camera->pl->first_init == 0 && camera->pl->cached_ready == 1) { /* First case, the serial speed of the camera is the same as * ours, so let's try to send a ping packet : */ if (!canon_serial_send_packet (camera, PKT_EOT, camera->pl->seq_tx, camera->pl->psa50_eot + PKT_HDR_LEN, 0)) return GP_ERROR; good_ack = canon_serial_wait_for_ack (camera); GP_DEBUG ("good_ack = %i", good_ack); if (good_ack == 0) { /* no answer from the camera, let's try * at the speed saved in the settings... */ speed = camera->pl->speed; if (speed != 9600) { if (!canon_serial_change_speed (camera->port, speed)) { gp_context_error (context, _("Error changing speed.")); } } if (!canon_serial_send_packet (camera, PKT_EOT, camera->pl->seq_tx, camera->pl->psa50_eot + PKT_HDR_LEN, 0)) return GP_ERROR; good_ack = canon_serial_wait_for_ack (camera); if (good_ack == 0) { gp_context_status (context, _("Resetting protocol...")); canon_serial_off (camera); sleep (3); /* The camera takes a while to switch off */ return canon_int_ready (camera, context); } if (good_ack == -1) { GP_DEBUG ("Received a NACK!"); return GP_ERROR; } gp_context_status (context, _("Camera OK.")); return 1; } if (good_ack == -1) { GP_DEBUG ("Received a NACK !\n"); return GP_ERROR; } GP_DEBUG ("Camera replied to ping, proceed.\n"); return GP_OK; } /* Camera was off... */ gp_context_status (context, _("Looking for camera ...")); if (camera->pl->receive_error == FATAL_ERROR) { /* we try to recover from an error we go back to 9600bps */ if (!canon_serial_change_speed (camera->port, 9600)) { GP_DEBUG ("ERROR: Error changing speed"); return GP_ERROR; } camera->pl->receive_error = NOERROR; } id = gp_context_progress_start (context, MAX_TRIES, _("Trying to contact camera...")); for (try = 0; try < MAX_TRIES; try++) { if (canon_serial_send (camera, (unsigned char *)"\x55\x55\x55\x55\x55\x55\x55\x55", 8, USLEEP1) < 0) { gp_context_error (context, _("Communication error 1")); return GP_ERROR; } pkt = canon_serial_recv_frame (camera, &len); gp_context_progress_update (context, id, try + 1); if (pkt) break; } gp_context_progress_stop (context, id); if (try == MAX_TRIES) { gp_context_error (context, _("No response from camera")); return GP_ERROR; } if (!pkt) { gp_context_error (context, _("No response from camera")); return GP_ERROR; } if (len < 40 && strncmp ((char *)pkt + 26, "Canon", 5)) { gp_context_error (context, _("Unrecognized response")); return GP_ERROR; } strncpy (cam_id_str, (char *)pkt + 26, sizeof (cam_id_str) - 1); GP_DEBUG ("cam_id_str : '%s'", cam_id_str); camera->pl->first_init = 0; /* Compare what the camera identified itself as with our list * of known models * * We iterate over the model list testing id_str, even if we * don't actually use id_str, but serial_id_string. */ for (i = 0; models[i].id_str != NULL; i++) { if ((models[i].serial_id_string != NULL) && !strcmp (models[i].serial_id_string, cam_id_str)) { GP_DEBUG ("canon_serial_ready: Serial ID string matches '%s'", models[i].serial_id_string); gp_context_status (context, _("Detected a \"%s\" aka \"%s\""), models[i].id_str, models[i].serial_id_string); camera->pl->md = (struct canonCamModelData *) &models[i]; break; } } if (models[i].id_str == NULL) { gp_context_error (context, _("Unknown model \"%s\""), cam_id_str); return GP_ERROR_MODEL_NOT_FOUND; } /* take care of some model specific things */ switch (camera->pl->md->model) { case CANON_CLASS_3: case CANON_CLASS_1: if (camera->pl->speed > 57600) camera->pl->slow_send = 1; break; default: break; } /* 5 seconds delay should be enough for big flash cards. By * experience, one or two seconds is too little, as a large flash * card needs more access time. */ serial_set_timeout (camera->port, 5000); (void) canon_serial_recv_packet (camera, &type, &seq, NULL); if (type != PKT_EOT || seq) { gp_context_error (context, _("Bad EOT")); return GP_ERROR; } camera->pl->seq_tx = 0; camera->pl->seq_rx = 1; if (!canon_serial_send_frame (camera, (unsigned char *)"\x00\x05\x00\x00\x00\x00\xdb\xd1", 8)) { gp_context_error (context, _("Communication error 2")); return GP_ERROR; } res = 0; switch (camera->pl->speed) { case 9600: res = canon_serial_send_frame (camera, SPEED_9600, 12); break; case 19200: res = canon_serial_send_frame (camera, SPEED_19200, 12); break; case 38400: res = canon_serial_send_frame (camera, SPEED_38400, 12); break; case 57600: res = canon_serial_send_frame (camera, SPEED_57600, 12); break; case 115200: res = canon_serial_send_frame (camera, SPEED_115200, 12); break; } if (!res || !canon_serial_send_frame (camera, (unsigned char *)"\x00\x04\x01\x00\x00\x00\x24\xc6", 8)) { gp_context_error (context, _("Communication error 3")); return GP_ERROR; } speed = camera->pl->speed; gp_context_status (context, _("Changing speed... wait...")); if (!canon_serial_wait_for_ack (camera)) return GP_ERROR; if (speed != 9600) { if (!canon_serial_change_speed (camera->port, speed)) { gp_context_status (context, _("Error changing speed")); } else { GP_DEBUG ("speed changed"); } } for (try = 1; try < MAX_TRIES; try++) { canon_serial_send_packet (camera, PKT_EOT, camera->pl->seq_tx, camera->pl->psa50_eot + PKT_HDR_LEN, 0); if (!canon_serial_wait_for_ack (camera)) { gp_context_status (context, _("Error waiting for ACK during initialization, retrying")); } else break; } if (try == MAX_TRIES) { gp_context_error (context, _("Error waiting ACK during initialization")); return GP_ERROR; } gp_context_status (context, _("Connected to camera")); /* Now is a good time to ask the camera for its owner * name (and Model String as well) */ canon_int_identify_camera (camera, context); canon_int_get_time (camera, NULL, context); return GP_OK; } /** * canon_serial_get_thumbnail: * @camera: camera to work on * @name: file name (complete canon path) of file to get thumbnail for * @data: pointer to data pointer * @length: pointer to data length * @context: context for error reporting * * This is just the serial specific part extracted from the older * canon_get_thumbnail() routine. * * Returns: gphoto2 error code * */ int canon_serial_get_thumbnail (Camera *camera, const char *name, unsigned char **data, unsigned int *length, GPContext *context) { unsigned int expect = 0, size, payload_length, total_file_size; unsigned int total = 0, id; unsigned char *msg; CON_CHECK_PARAM_NULL (length); CON_CHECK_PARAM_NULL (data); *length = 0; *data = NULL; if (camera->pl->receive_error == FATAL_ERROR) { gp_context_error (context, _("ERROR: a fatal error condition was detected, can't continue ")); return GP_ERROR; } payload_length = strlen (name) + 1; msg = canon_serial_dialogue (camera, context, 0x1, 0x11, &total_file_size, "\x01\x00\x00\x00\x00", 5, &payload_length, 1, "\x00", 2, name, strlen (name) + 1, NULL); if (!msg) { canon_serial_error_type (camera); return GP_ERROR; } total = le32atoh (msg + 4); if (total > 2000000) { /* 2 MB thumbnails ? unlikely ... */ gp_context_error (context, _("ERROR: %d is too big"), total); return GP_ERROR; } *data = malloc (total); if (!*data) { perror ("malloc"); return GP_ERROR; } *length = total; id = gp_context_progress_start (context, total, _("Getting thumbnail...")); while (msg) { if (total_file_size < 20 || le32atoh (msg)) { return GP_ERROR; } size = le32atoh (msg + 12); if (le32atoh (msg + 8) != expect || expect + size > total || size > total_file_size - 20) { GP_DEBUG ("ERROR: doesn't fit"); return GP_ERROR; } memcpy (*data + expect, msg + 20, size); expect += size; gp_context_progress_update (context, id, expect); if ((expect == total) != le32atoh (msg + 16)) { GP_DEBUG ("ERROR: end mark != end of data"); return GP_ERROR; } if (expect == total) { /* We finished receiving the file. Parse the header and return just the thumbnail */ break; } msg = canon_serial_recv_msg (camera, 0x1, 0x21, &total_file_size, context); } gp_context_progress_stop (context, id); return GP_OK; } /**************************************************************************** * * End of file: serial.c * ****************************************************************************/ /* * Local Variables: * c-file-style:"linux" * indent-tabs-mode:t * End: */