/* * Canon SELPHY ES series print assister * * (c) 2007 Solomon Peachy * * This utility takes the output of the es1 gutenprint driver and sends * it onto the low-level device. It will eventually be merged into CUPS * or something similar. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program 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 General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * [http://www.gnu.org/licenses/old-licenses/gpl-2.0.html] * */ // Compile with: gcc -o es_print -Wall es_print_assist.c #define VERSION "0.4" #include #include #include #include #include #include #include #include #include #define BUFFER_SIZE 4096 #define RDBUF_LEN 12 #define PLANE_Y 0x01 #define PLANE_M 0x03 #define PLANE_C 0x07 // Readback values seen in the wild (with 'P' color cartridge) // 02 00 00 00 02 01 01 01 00 00 00 00 [idle, waiting for init seq] // 04 00 00 00 02 01 01 01 00 00 00 00 [init received, not ready..] // 04 00 01 00 02 01 01 01 00 00 00 00 [waiting for Y data] // 04 00 03 00 02 01 01 01 00 00 00 00 [waiting for M data] // 04 00 07 00 02 01 01 01 00 00 00 00 [waiting for C data] // 04 00 00 00 02 01 01 01 00 00 00 00 [all data sent; not ready..] // 05 00 00 00 02 01 01 01 00 00 00 00 [?? transitions to this] // 06 00 00 00 02 01 01 01 00 00 00 00 [?? transitions to this] // 02 00 00 00 02 01 01 01 00 00 00 00 [..transitions back to idle] int status_poll(int dev_fd, int plane, int timeout) { char buffer[RDBUF_LEN]; struct timeval tv, tv2; gettimeofday(&tv, NULL); tv.tv_sec += timeout; while(1) { read(dev_fd, buffer, RDBUF_LEN); // wait for 02 00 00 00 02 01 01 01 00 00 00 00 if (plane == 0 && buffer[0] == 0x02 && buffer[1] == 0x00 && buffer[2] == 0x00 && buffer[3] == 0x00) { break; } // Wait for 04 00 [PLANE] 00 02 01 01 01 00 00 00 00 if (buffer[0] == 0x04 && buffer[1] == 0x00 && buffer[2] == plane && buffer[3] == 0x00) { break; } /* Enforce the timeout */ gettimeofday(&tv2, NULL); if (tv2.tv_sec > tv.tv_sec) return -1; sleep (1); } return 0; } #if (__BYTE_ORDER == __LITTLE_ENDIAN) #define cpu_to_le32(__x) __x #else #define cpu_to_le32(x) \ ({ \ uint32_t __x = (x); \ ((uint32_t)( \ (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \ }) #endif int dump_chunk(int in_fd, int out_fd, int blksize, int len, int write_data) { char buffer[BUFFER_SIZE]; int cnt; int i = 0; int unknown_len = 0; int wrote = 0; if (!blksize) { blksize = BUFFER_SIZE; } /* deal with unknown lengths */ if (len == 0) { len = 12; unknown_len = 1; } while (len > 0) { cnt = read(in_fd, buffer, (len < blksize) ? len : blksize); if (cnt < 0) { return -1; } if (unknown_len) { /* Figure out remaining length */ // 40 01 01 [plane] [32-bit LE length] 00 00 00 00 uint32_t *chunk = (uint32_t *)(buffer + 4); len += cpu_to_le32(*chunk); unknown_len = 0; } if (write_data) { i = write(out_fd, buffer, cnt); if (i < 0) { return wrote; } if (i != cnt) { fprintf(stderr, "write mismatch %d ...\n", i); } wrote += i; } len -= cnt; } return wrote; } int main(int argc, char **argv) { int in_fd, out_fd; int chunk_len; char *in_file; char *out_dev; struct stat statbuf; int i; if (argc < 2) { fprintf(stderr, "SELPHY ES Print Assist version %s\n\nUsage:\n\t%s infile outdev\n", VERSION, argv[0]); fprintf(stderr, "\n\t(use '-' as infile for stdin)\n"); exit(1); } in_file = argv[1]; out_dev = argv[2]; if (in_file[0] == '-') { /* stdin. Figure out length from the stream */ in_fd = 0; chunk_len = 0; } else { /* open the file, get the length directly */ i = stat(in_file, &statbuf); if (i < 0) { perror("Can't stat input file"); exit(1); } in_fd = open(in_file, O_RDONLY); if (i < 0) { perror("Can't open input file"); exit(1); } chunk_len = (statbuf.st_size - 12) / 3; if (((chunk_len * 3) + 12) != statbuf.st_size) { fprintf(stderr, "Bogus chunk length!\n"); exit(1); } } out_fd = open(out_dev, O_RDWR); if (i < 0) { perror("Can't open output device"); exit(1); } /* Make sure the printer is ready */ if (status_poll(out_fd, 0, 5)) { fprintf(stderr, "Printer not ready!\n"); goto done; } /* Dump over init sequence */ fprintf(stderr, "Sending init sequence\n"); i = dump_chunk(in_fd, out_fd, 12, 12, 1); fprintf(stderr, "Wrote %d bytes (%d)\n", i, 12); /* Dump the Y plane */ if (status_poll(out_fd, PLANE_Y, 30)) { fprintf(stderr, "Timeout waiting on YELLOW plane ready!\n"); goto done; } fprintf(stderr, "Sending YELLOW plane\n"); i = dump_chunk(in_fd, out_fd, 0, chunk_len, 1); fprintf(stderr, "Wrote %d bytes (%d)\n", i, chunk_len); /* Dump the M plane */ if (status_poll(out_fd, PLANE_M, 30)) { fprintf(stderr, "Timeout waiting on MAGENTA plane ready!\n"); goto done; } fprintf(stderr, "Sending MAGENTA plane\n"); i = dump_chunk(in_fd, out_fd, 0, chunk_len, 1); fprintf(stderr, "Wrote %d bytes (%d)\n", i, chunk_len); /* Dump the C plane */ if (status_poll(out_fd, PLANE_C, 30)) { fprintf(stderr, "Timeout waiting on CYAN plane ready!\n"); goto done; } fprintf(stderr, "Sending CYAN plane\n"); i = dump_chunk(in_fd, out_fd, 0, chunk_len, 1); fprintf(stderr, "Wrote %d bytes (%d)\n", i, chunk_len); fprintf(stderr, "All data sent to printer!\n"); done: if(in_fd) close(in_fd); if (out_fd) close(out_fd); return 0; }