selphy_print/backend_kodak6800.c

1321 lines
36 KiB
C
Raw Normal View History

/*
* Kodak 6800/6850 Photo Printer CUPS backend -- libusb-1.0 version
*
* (c) 2013-2016 Solomon Peachy <pizza@shaftnet.org>
*
* Development of this backend was sponsored by:
*
* LiveLink Technology [ www.livelinktechnology.net ]
*
* The latest version of this program can be found at:
*
* http://git.shaftnet.org/cgit/selphy_print.git
*
* 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 3 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/gpl-3.0.html]
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#define BACKEND kodak6800_backend
#include "backend_common.h"
#define USB_VID_KODAK 0x040A
#define USB_PID_KODAK_6800 0x4021
#define USB_PID_KODAK_6850 0x402B
/* File header */
struct kodak6800_hdr {
uint8_t hdr[7]; /* Always 03 1b 43 48 43 0a 00 */
uint8_t jobid; /* Non-zero */
uint16_t copies; /* BE, in BCD format (1-9999) */
uint16_t columns; /* BE */
uint16_t rows; /* BE */
uint8_t size; /* 0x06 for 6x8, 0x00 for 6x4, 0x07 for 5x7 */
uint8_t laminate; /* 0x01 to laminate, 0x00 for not */
uint8_t mode; /* 0x00 or 0x01 (for 4x6 on 6x8 media) */
} __attribute__((packed));
struct kodak68x0_status_readback {
uint8_t hdr; /* Always 01 */
uint8_t status; /* STATUS_* */
uint8_t status1; /* STATUS1_* */
uint32_t status2; /* STATUS2_* */
uint8_t errcode; /* Error ## */
uint32_t lifetime; /* Lifetime Prints (BE) */
uint32_t maint; /* Maint Prints (BE) */
uint32_t media; /* Media Prints (6850), Unknown (6800) (BE) */
uint32_t cutter; /* Cutter Actuations (BE) */
uint8_t nullB[2];
uint8_t errtype; /* seen 0x00 or 0xd0 */
uint8_t donor; /* Percentage, 0-100 */
uint16_t main_boot; /* Always 003 */
uint16_t main_fw; /* seen 652, 656, 670, 671 (6850) and 232 (6800) */
uint16_t dsp_boot; /* Always 001 */
uint16_t dsp_fw; /* Seen 540, 541, 560 (6850) and 131 (6800) */
uint8_t b1_jobid;
uint8_t b2_jobid;
uint16_t b1_remain; /* Remaining prints in job */
uint16_t b1_complete; /* Completed prints in job */
uint16_t b1_total; /* Total prints in job */
uint16_t b2_remain; /* Remaining prints in job */
uint16_t b2_complete; /* Completed prints in job */
uint16_t b2_total; /* Total prints in job */
uint8_t curve_status; /* Always seems to be 0x00 */
} __attribute__((packed));
enum {
CMD_CODE_OK = 1,
CMD_CODE_BAD = 2,
};
enum {
STATUS_PRINTING = 1,
STATUS_IDLE = 2,
};
enum {
STATE_STATUS1_STANDBY = 1,
STATE_STATUS1_ERROR = 2,
STATE_STATUS1_WAIT = 3,
};
#define STATE_STANDBY_STATUS2 0x0
enum {
WAIT_STATUS2_INIT = 0,
WAIT_STATUS2_RIBBON = 1,
WAIT_STATUS2_THERMAL = 2,
WAIT_STATUS2_OPERATING = 3,
WAIT_STATUS2_BUSY = 4,
};
#define ERROR_STATUS2_CTRL_CIRCUIT (1<<31)
#define ERROR_STATUS2_MECHANISM_CTRL (1<<30)
#define ERROR_STATUS2_SENSOR (1<<13)
#define ERROR_STATUS2_COVER_OPEN (1<<12)
#define ERROR_STATUS2_TEMP_SENSOR (1<<9)
#define ERROR_STATUS2_PAPER_JAM (1<<8)
#define ERROR_STATUS2_PAPER_EMPTY (1<<6)
#define ERROR_STATUS2_RIBBON_ERR (1<<4)
enum {
CTRL_CIR_ERROR_EEPROM1 = 0x01,
CTRL_CIR_ERROR_EEPROM2 = 0x02,
CTRL_CIR_ERROR_DSP = 0x04,
CTRL_CIR_ERROR_CRC_MAIN = 0x06,
CTRL_CIR_ERROR_DL_MAIN = 0x07,
CTRL_CIR_ERROR_CRC_DSP = 0x08,
CTRL_CIR_ERROR_DL_DSP = 0x09,
CTRL_CIR_ERROR_ASIC = 0x0a,
CTRL_CIR_ERROR_DRAM = 0x0b,
CTRL_CIR_ERROR_DSPCOMM = 0x29,
};
enum {
MECH_ERROR_HEAD_UP = 0x01,
MECH_ERROR_HEAD_DOWN = 0x02,
MECH_ERROR_MAIN_PINCH_UP = 0x03,
MECH_ERROR_MAIN_PINCH_DOWN = 0x04,
MECH_ERROR_SUB_PINCH_UP = 0x05,
MECH_ERROR_SUB_PINCH_DOWN = 0x06,
MECH_ERROR_FEEDIN_PINCH_UP = 0x07,
MECH_ERROR_FEEDIN_PINCH_DOWN = 0x08,
MECH_ERROR_FEEDOUT_PINCH_UP = 0x09,
MECH_ERROR_FEEDOUT_PINCH_DOWN = 0x0a,
MECH_ERROR_CUTTER_LR = 0x0b,
MECH_ERROR_CUTTER_RL = 0x0c,
};
enum {
SENSOR_ERROR_CUTTER = 0x05,
SENSOR_ERROR_HEAD_DOWN = 0x09,
SENSOR_ERROR_HEAD_UP = 0x0a,
SENSOR_ERROR_MAIN_PINCH_DOWN = 0x0b,
SENSOR_ERROR_MAIN_PINCH_UP = 0x0c,
SENSOR_ERROR_FEED_PINCH_DOWN = 0x0d,
SENSOR_ERROR_FEED_PINCH_UP = 0x0e,
SENSOR_ERROR_EXIT_PINCH_DOWN = 0x0f,
SENSOR_ERROR_EXIT_PINCH_UP = 0x10,
SENSOR_ERROR_LEFT_CUTTER = 0x11,
SENSOR_ERROR_RIGHT_CUTTER = 0x12,
SENSOR_ERROR_CENTER_CUTTER = 0x13,
SENSOR_ERROR_UPPER_CUTTER = 0x14,
SENSOR_ERROR_PAPER_FEED_COVER = 0x15,
};
enum {
TEMP_SENSOR_ERROR_HEAD_HIGH = 0x01,
TEMP_SENSOR_ERROR_HEAD_LOW = 0x02,
TEMP_SENSOR_ERROR_ENV_HIGH = 0x03,
TEMP_SENSOR_ERROR_ENV_LOW = 0x04,
};
enum {
COVER_OPEN_ERROR_UPPER = 0x01,
COVER_OPEN_ERROR_LOWER = 0x02,
};
enum {
PAPER_EMPTY_ERROR = 0x00,
};
enum {
RIBBON_ERROR = 0x00,
};
enum {
CURVE_TABLE_STATUS_INITIAL = 0x00,
CURVE_TABLE_STATUS_USERSET = 0x01,
CURVE_TABLE_STATUS_CURRENT = 0x02,
};
struct kodak6800_printsize {
uint8_t hdr; /* Always 0x06 */
uint16_t width; /* BE */
uint16_t height; /* BE */
uint8_t type; /* MEDIA_TYPE_* [ ie paper ] */
uint8_t code; /* 00, 01, 02, 03, 04, 05 seen. An index? */
uint8_t code2; /* 00, 01 seen. Alternates every other 4x6 printed, but only 1 on unknown/1844x2490 print size. */
uint8_t null[2];
} __attribute__((packed));
#define MAX_MEDIA_LEN 128
struct kodak68x0_media_readback {
uint8_t hdr; /* Always 0x01 */
uint8_t type; /* Media code, KODAK68x0_MEDIA_xxx */
uint8_t null[5];
uint8_t count; /* Always 0x04 (6800) or 0x06 (6850)? */
struct kodak6800_printsize sizes[];
} __attribute__((packed));
#define KODAK68x0_MEDIA_6R 0x0b // 197-4096
#define KODAK68x0_MEDIA_UNK 0x03
#define KODAK68x0_MEDIA_6TR2 0x2c // 396-2941
#define KODAK68x0_MEDIA_NONE 0x00
/* 6R: Also seen: 101-0867, 141-9597, 659-9054, 169-6418, DNP 900-060 */
#define CMDBUF_LEN 17
/* Private data stucture */
struct kodak6800_ctx {
struct libusb_device_handle *dev;
uint8_t endp_up;
uint8_t endp_down;
int type;
2015-08-23 19:11:59 -04:00
uint8_t jobid;
2015-08-23 19:11:59 -04:00
struct kodak68x0_media_readback *media;
struct kodak6800_hdr hdr;
uint8_t *databuf;
int datalen;
uint8_t last_donor;
};
static const char *kodak68xx_mediatypes(int type)
{
switch(type) {
case KODAK68x0_MEDIA_NONE:
return "No media";
case KODAK68x0_MEDIA_6R:
case KODAK68x0_MEDIA_6TR2:
return "Kodak 6R";
default:
return "Unknown";
}
return "Unknown";
}
/* Baseline commands */
static int kodak6800_do_cmd(struct kodak6800_ctx *ctx,
void *cmd, int cmd_len,
void *resp, int resp_len,
int *actual_len)
{
int ret;
/* Write command */
if ((ret = send_data(ctx->dev, ctx->endp_down,
cmd, cmd_len)))
return (ret < 0) ? ret : -99;
/* Read response */
ret = read_data(ctx->dev, ctx->endp_up,
resp, resp_len, actual_len);
if (ret < 0)
return ret;
return 0;
}
static void kodak68x0_dump_mediainfo(struct kodak68x0_media_readback *media)
{
int i;
if (media->type == KODAK68x0_MEDIA_NONE) {
INFO("No Media Loaded\n");
return;
}
switch (media->type) {
case KODAK68x0_MEDIA_6R:
INFO("Media type: 6R (Kodak 197-4096 or equivalent)\n");
break;
case KODAK68x0_MEDIA_6TR2:
INFO("Media type: 6R (Kodak 396-2941 or equivalent)\n");
break;
default:
INFO("Media type %02x (unknown, please report!)\n", media->type);
break;
}
INFO("Legal print sizes:\n");
for (i = 0 ; i < media->count ; i++) {
INFO("\t%d: %dx%d (%02x) %s\n", i,
be16_to_cpu(media->sizes[i].width),
be16_to_cpu(media->sizes[i].height),
media->sizes[i].code,
media->sizes[i].code2? "Disallowed?" : "");
}
INFO("\n");
}
static int kodak6800_get_mediainfo(struct kodak6800_ctx *ctx, struct kodak68x0_media_readback *media)
{
uint8_t req[16];
int ret, num;
memset(req, 0, sizeof(req));
memset(media, 0, sizeof(*media));
req[0] = 0x03;
req[1] = 0x1b;
req[2] = 0x43;
req[3] = 0x48;
req[4] = 0x43;
req[5] = 0x1a;
req[6] = 0x00; /* This can be non-zero for additional "banks" */
/* Issue command and get response */
if ((ret = kodak6800_do_cmd(ctx, req, sizeof(req),
media, MAX_MEDIA_LEN,
&num)))
return ret;
/* Validate proper response */
if (media->hdr != CMD_CODE_OK ||
media->null[0] != 0x00) {
ERROR("Unexpected response from media query!\n");
return CUPS_BACKEND_STOP;
}
return 0;
}
static int kodak68x0_canceljob(struct kodak6800_ctx *ctx,
int id)
{
uint8_t req[16];
int ret, num;
struct kodak68x0_status_readback sts;
memset(req, 0, sizeof(req));
req[0] = 0x03;
req[1] = 0x1b;
req[2] = 0x43;
req[3] = 0x48;
req[4] = 0x43;
req[5] = 0x13;
req[6] = id;
/* Issue command and get response */
if ((ret = kodak6800_do_cmd(ctx, req, sizeof(req),
&sts, sizeof(sts),
&num)))
return ret;
/* Validate proper response */
if (sts.hdr != CMD_CODE_OK) {
ERROR("Unexpected response from job cancel!\n");
return -99;
}
return 0;
}
static int kodak68x0_reset(struct kodak6800_ctx *ctx)
{
uint8_t req[16];
int ret, num;
struct kodak68x0_status_readback sts;
memset(req, 0, sizeof(req));
req[0] = 0x03;
req[1] = 0x1b;
req[2] = 0x43;
req[3] = 0x48;
req[4] = 0xc0;
/* Issue command and get response */
if ((ret = kodak6800_do_cmd(ctx, req, sizeof(req),
&sts, sizeof(sts),
&num)))
return ret;
/* Validate proper response */
if (sts.hdr != CMD_CODE_OK) {
ERROR("Unexpected response from job cancel!\n");
return -99;
}
return 0;
}
/* Structure dumps */
static char *kodak68x0_status_str(struct kodak68x0_status_readback *resp)
{
switch(resp->status1) {
case STATE_STATUS1_STANDBY:
return "Standby (Ready)";
case STATE_STATUS1_WAIT:
switch (be32_to_cpu(resp->status2)) {
case WAIT_STATUS2_INIT:
return "Wait (Initializing)";
case WAIT_STATUS2_RIBBON:
return "Wait (Ribbon Winding)";
case WAIT_STATUS2_THERMAL:
return "Wait (Thermal Protection)";
case WAIT_STATUS2_OPERATING:
return "Wait (Operating)";
case WAIT_STATUS2_BUSY:
return "Wait (Busy)";
default:
return "Wait (Unknown)";
}
case STATE_STATUS1_ERROR:
switch (be32_to_cpu(resp->status2)) {
case ERROR_STATUS2_CTRL_CIRCUIT:
switch (resp->errcode) {
case CTRL_CIR_ERROR_EEPROM1:
return "Error (EEPROM1)";
case CTRL_CIR_ERROR_EEPROM2:
return "Error (EEPROM2)";
case CTRL_CIR_ERROR_DSP:
return "Error (DSP)";
case CTRL_CIR_ERROR_CRC_MAIN:
return "Error (Main CRC)";
case CTRL_CIR_ERROR_DL_MAIN:
return "Error (Main Download)";
case CTRL_CIR_ERROR_CRC_DSP:
return "Error (DSP CRC)";
case CTRL_CIR_ERROR_DL_DSP:
return "Error (DSP Download)";
case CTRL_CIR_ERROR_ASIC:
return "Error (ASIC)";
case CTRL_CIR_ERROR_DRAM:
return "Error (DRAM)";
case CTRL_CIR_ERROR_DSPCOMM:
return "Error (DSP Communincation)";
default:
return "Error (Unknown Circuit)";
}
case ERROR_STATUS2_MECHANISM_CTRL:
switch (resp->errcode) {
case MECH_ERROR_HEAD_UP:
return "Error (Head Up Mechanism)";
case MECH_ERROR_HEAD_DOWN:
return "Error (Head Down Mechanism)";
case MECH_ERROR_MAIN_PINCH_UP:
return "Error (Main Pinch Up Mechanism)";
case MECH_ERROR_MAIN_PINCH_DOWN:
return "Error (Main Pinch Down Mechanism)";
case MECH_ERROR_SUB_PINCH_UP:
return "Error (Sub Pinch Up Mechanism)";
case MECH_ERROR_SUB_PINCH_DOWN:
return "Error (Sub Pinch Down Mechanism)";
case MECH_ERROR_FEEDIN_PINCH_UP:
return "Error (Feed-in Pinch Up Mechanism)";
case MECH_ERROR_FEEDIN_PINCH_DOWN:
return "Error (Feed-in Pinch Down Mechanism)";
case MECH_ERROR_FEEDOUT_PINCH_UP:
return "Error (Feed-out Pinch Up Mechanism)";
case MECH_ERROR_FEEDOUT_PINCH_DOWN:
return "Error (Feed-out Pinch Down Mechanism)";
case MECH_ERROR_CUTTER_LR:
return "Error (Left->Right Cutter)";
case MECH_ERROR_CUTTER_RL:
return "Error (Right->Left Cutter)";
default:
return "Error (Unknown Mechanism)";
}
case ERROR_STATUS2_SENSOR:
switch (resp->errcode) {
case SENSOR_ERROR_CUTTER:
return "Error (Cutter Sensor)";
case SENSOR_ERROR_HEAD_DOWN:
return "Error (Head Down Sensor)";
case SENSOR_ERROR_HEAD_UP:
return "Error (Head Up Sensor)";
case SENSOR_ERROR_MAIN_PINCH_DOWN:
return "Error (Main Pinch Down Sensor)";
case SENSOR_ERROR_MAIN_PINCH_UP:
return "Error (Main Pinch Up Sensor)";
case SENSOR_ERROR_FEED_PINCH_DOWN:
return "Error (Feed Pinch Down Sensor)";
case SENSOR_ERROR_FEED_PINCH_UP:
return "Error (Feed Pinch Up Sensor)";
case SENSOR_ERROR_EXIT_PINCH_DOWN:
return "Error (Exit Pinch Up Sensor)";
case SENSOR_ERROR_EXIT_PINCH_UP:
return "Error (Exit Pinch Up Sensor)";
case SENSOR_ERROR_LEFT_CUTTER:
return "Error (Left Cutter Sensor)";
case SENSOR_ERROR_RIGHT_CUTTER:
return "Error (Right Cutter Sensor)";
case SENSOR_ERROR_CENTER_CUTTER:
return "Error (Center Cutter Sensor)";
case SENSOR_ERROR_UPPER_CUTTER:
return "Error (Upper Cutter Sensor)";
case SENSOR_ERROR_PAPER_FEED_COVER:
return "Error (Paper Feed Cover)";
default:
return "Error (Unknown Sensor)";
}
case ERROR_STATUS2_COVER_OPEN:
switch (resp->errcode) {
case COVER_OPEN_ERROR_UPPER:
return "Error (Upper Cover Open)";
case COVER_OPEN_ERROR_LOWER:
return "Error (Lower Cover Open)";
default:
return "Error (Unknown Cover Open)";
}
case ERROR_STATUS2_TEMP_SENSOR:
switch (resp->errcode) {
case TEMP_SENSOR_ERROR_HEAD_HIGH:
return "Error (Head Temperature High)";
case TEMP_SENSOR_ERROR_HEAD_LOW:
return "Error (Head Temperature Low)";
case TEMP_SENSOR_ERROR_ENV_HIGH:
return "Error (Environmental Temperature High)";
case TEMP_SENSOR_ERROR_ENV_LOW:
return "Error (Environmental Temperature Low)";
default:
return "Error (Unknown Temperature)";
}
case ERROR_STATUS2_PAPER_JAM:
return "Error (Paper Jam)";
case ERROR_STATUS2_PAPER_EMPTY:
return "Error (Paper Empty)";
case ERROR_STATUS2_RIBBON_ERR:
return "Error (Ribbon)";
default:
return "Error (Unknown)";
}
default:
return "Unknown!";
}
}
static void kodak68x0_dump_status(struct kodak6800_ctx *ctx, struct kodak68x0_status_readback *status)
{
char *detail;
switch (status->status) {
case STATUS_PRINTING:
detail = "Printing";
break;
case STATUS_IDLE:
detail = "Idle";
break;
default:
detail = "Unknown";
break;
}
INFO("Printer Status : %s\n", detail);
INFO("Printer State : %s # %02x %08x %02x\n",
kodak68x0_status_str(status),
status->status1, be32_to_cpu(status->status2), status->errcode);
INFO("Bank 1 ID: %u\n", status->b1_jobid);
INFO("\tPrints: %d/%d complete\n",
be16_to_cpu(status->b1_complete), be16_to_cpu(status->b1_total));
INFO("Bank 2 ID: %u\n", status->b2_jobid);
INFO("\tPrints: %d/%d complete\n",
be16_to_cpu(status->b2_complete), be16_to_cpu(status->b2_total));
switch (status->curve_status) {
case CURVE_TABLE_STATUS_INITIAL:
detail = "Initial/Default";
break;
case CURVE_TABLE_STATUS_USERSET:
detail = "User Stored";
break;
case CURVE_TABLE_STATUS_CURRENT:
detail = "Current";
break;
default:
detail = "Unknown";
break;
}
INFO("Tone Curve Status: %s\n", detail);
INFO("Counters:\n");
INFO("\tLifetime : %u\n", be32_to_cpu(status->lifetime));
INFO("\tThermal Head : %u\n", be32_to_cpu(status->maint));
INFO("\tCutter : %u\n", be32_to_cpu(status->cutter));
if (ctx->type == P_KODAK_6850) {
int max;
INFO("\tMedia : %u\n", be32_to_cpu(status->media));
switch(ctx->media->type) {
case KODAK68x0_MEDIA_6R:
case KODAK68x0_MEDIA_6TR2:
max = 375;
break;
default:
max = 0;
break;
}
if (max) {
INFO("\t Remaining : %d\n", max - be32_to_cpu(status->media));
} else {
INFO("\t Remaining : Unknown\n");
}
}
INFO("Main FW version : %d\n", be16_to_cpu(status->main_fw));
INFO("DSP FW version : %d\n", be16_to_cpu(status->dsp_fw));
INFO("Donor : %u%%\n", status->donor);
INFO("\n");
}
static int kodak6800_get_status(struct kodak6800_ctx *ctx,
struct kodak68x0_status_readback *status)
{
uint8_t req[16];
int ret, num;
memset(req, 0, sizeof(req));
memset(status, 0, sizeof(*status));
req[0] = 0x03;
req[1] = 0x1b;
req[2] = 0x43;
req[3] = 0x48;
req[4] = 0x43;
req[5] = 0x03;
/* Issue command and get response */
if ((ret = kodak6800_do_cmd(ctx, req, sizeof(req),
status, sizeof(*status),
&num)))
return ret;
/* Validate proper response */
if (status->hdr != CMD_CODE_OK) {
ERROR("Unexpected response from status query!\n");
return -99;
}
return 0;
}
#define UPDATE_SIZE 1536
static int kodak6800_get_tonecurve(struct kodak6800_ctx *ctx, char *fname)
{
uint8_t cmdbuf[16];
uint8_t respbuf[64];
int ret, num = 0;
int i;
uint16_t *data = malloc(UPDATE_SIZE);
if (!data) {
ERROR("Memory Allocation Failure\n");
return -1;
}
INFO("Dump Tone Curve to '%s'\n", fname);
/* Initial Request */
cmdbuf[0] = 0x03;
cmdbuf[1] = 0x1b;
cmdbuf[2] = 0x43;
cmdbuf[3] = 0x48;
cmdbuf[4] = 0x43;
cmdbuf[5] = 0x0c;
cmdbuf[6] = 0x54;
cmdbuf[7] = 0x4f;
cmdbuf[8] = 0x4e;
cmdbuf[9] = 0x45;
cmdbuf[10] = 0x72;
cmdbuf[11] = 0x01; /* 01 for user tonecurve, can be 00 or 02 */
cmdbuf[12] = 0x00; /* param table? */
cmdbuf[13] = 0x00;
cmdbuf[14] = 0x00;
cmdbuf[15] = 0x00;
/* Issue command and get response */
if ((ret = kodak6800_do_cmd(ctx, cmdbuf, sizeof(cmdbuf),
respbuf, sizeof(respbuf),
&num)))
/* Validate proper response */
if (respbuf[0] != CMD_CODE_OK) {
ERROR("Unexpected response from tonecurve query!\n");
ret = -99;
goto done;
}
/* Then we can poll the data */
cmdbuf[0] = 0x03;
cmdbuf[1] = 0x1b;
cmdbuf[2] = 0x43;
cmdbuf[3] = 0x48;
cmdbuf[4] = 0x43;
cmdbuf[5] = 0x0c;
cmdbuf[6] = 0x54;
cmdbuf[7] = 0x4f;
cmdbuf[8] = 0x4e;
cmdbuf[9] = 0x45;
cmdbuf[10] = 0x20;
for (i = 0 ; i < 24 ; i++) {
/* Issue command and get response */
if ((ret = kodak6800_do_cmd(ctx, cmdbuf, sizeof(cmdbuf),
respbuf, sizeof(respbuf),
&num)))
goto done;
if (num != 64) {
ERROR("Short read! (%d/%d)\n", num, 51);
ret = 4;
goto done;
}
/* Copy into buffer */
memcpy(((uint8_t*)data)+i*64, respbuf, 64);
}
/* Open file and write it out */
{
int tc_fd = open(fname, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
if (tc_fd < 0) {
ret = 4;
goto done;
}
for (i = 0 ; i < 768; i++) {
/* Byteswap appropriately */
data[i] = cpu_to_be16(le16_to_cpu(data[i]));
write(tc_fd, &data[i], sizeof(uint16_t));
}
close(tc_fd);
}
done:
/* We're done */
free(data);
return ret;
}
static int kodak6800_set_tonecurve(struct kodak6800_ctx *ctx, char *fname)
{
uint8_t cmdbuf[64];
uint8_t respbuf[64];
int ret, num = 0;
int remain;
uint16_t *data = malloc(UPDATE_SIZE);
uint8_t *ptr;
if (!data) {
ERROR("Memory Allocation Failure\n");
return -1;
}
INFO("Set Tone Curve from '%s'\n", fname);
/* Read in file */
int tc_fd = open(fname, O_RDONLY);
if (tc_fd < 0) {
ret = -1;
goto done;
}
if (read(tc_fd, data, UPDATE_SIZE) != UPDATE_SIZE) {
ret = -2;
goto done;
}
close(tc_fd);
/* Byteswap data to printer's format */
for (ret = 0; ret < (UPDATE_SIZE)/2 ; ret++) {
data[ret] = cpu_to_le16(be16_to_cpu(data[ret]));
}
/* Initial Request */
cmdbuf[0] = 0x03;
cmdbuf[1] = 0x1b;
cmdbuf[2] = 0x43;
cmdbuf[3] = 0x48;
cmdbuf[4] = 0x43;
cmdbuf[5] = 0x0c;
cmdbuf[6] = 0x54;
cmdbuf[7] = 0x4f;
cmdbuf[8] = 0x4e;
cmdbuf[9] = 0x45;
cmdbuf[10] = 0x77;
cmdbuf[11] = 0x01; /* User TC. Can be 00 or 02 */
cmdbuf[12] = 0x00; /* param table? */
cmdbuf[13] = 0x00;
cmdbuf[14] = 0x00;
cmdbuf[15] = 0x00;
/* Issue command and get response */
if ((ret = kodak6800_do_cmd(ctx, cmdbuf, sizeof(cmdbuf),
respbuf, sizeof(respbuf),
&num)))
/* Validate proper response */
if (num != 51) {
ERROR("Short read! (%d/%d)\n", num, 51);
ret = 4;