/* This file is part of s10sh
*
* Copyright (C) 2000 by Salvatore Sanfilippo <antirez@invece.org>
* Copyright (C) 2001 by Salvatore Sanfilippo <antirez@invece.org>
*
* S10sh IS FREE SOFTWARE, UNDER THE TERMS OF THE GPL VERSION 2
* don't forget what free software means, even if today is so diffused.
*
* USB driver implementation
*
* ALL THIRD PARTY BRAND, PRODUCT AND SERVICE NAMES MENTIONED ARE
* THE TRADEMARK OR REGISTERED TRADEMARK OF THEIR RESPECTIVE OWNERS
*/
#ifdef HAVE_USB_SUPPORT
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#ifdef __linux__
#include <asm/page.h>
#endif /* __linux__ */
#ifndef PAGE_SIZE
/* This should be ok in most archs: what matter is that the real
* page size is equal or major than 0x1000, not that it matches */
#define PAGE_SIZE 0x1000
#endif
#include "s10sh.h"
static struct canon_camera_entry {
int id;
char *name;
} cameras[] = {
{0x3041, "Powershot S10"},
{0x3043, "Powershot S20"},
{0x3045, "Powershot S100"},
{0x3046, "IXY Digital"},
{0x3047, "Digital IXUS"},
{0x3048, "Powershot G1"},
{0x3049, "Powershot Pro90 IS"},
{0x304B, "IXY Digital 300"},
{0x304C, "PowerShot S300"},
{0x304D, "Digital IXUS 300"},
{0x304E, "Powershot A20"},
{0x304F, "PowerShot A10"},
{0x3051, "Powershot S110"},
{0x30FF, "Unknown camera that may use the same protocol"},
{0x0000, NULL} /* nul term */
};
/**************************
* USB API *
**************************/
/* WARNING: functions with "USB" prefix are s10sh USB api functions,
* functions with "usb" perfix are libusb functions
*/
/* USB settings */
static usb_dev_handle *cameraudh;
static int usb_timeout = 1000;
static int input_ep = 0x81;
static int output_ep = 0x02;
static int configuration = 1;
static int interface = 0;
static int alternate = 0;
static struct canon_camera_entry *supported_product(int id)
{
int i;
for (i = 0; cameras[i].id; i++)
if (cameras[i].id == id)
return &cameras[i];
if ((id & 0xFF00) == 0x3000) /* seems a camera... */
for (i = 0; cameras[i].id; i++)
if (cameras[i].id == 0x30FF) /* special ID, see the list */
return &cameras[i];
return NULL; /* camera not found */
}
int USB_camera_init(struct usb_device **camera_dev)
{
struct usb_bus *bus;
struct usb_device *dev;
usb_init();
usb_find_busses();
usb_find_devices();
if (!usb_busses) {
if (opt_debug)
fprintf(stderr, "USB initialization failed\n");
return USB_INIT_FAILED;
}
for (bus = usb_busses; bus; bus = bus->next) {
for (dev = bus->devices; dev; dev = dev->next) {
struct canon_camera_entry *e;
if (opt_debug)
printf("Found device %04X/%04X\n",
dev->descriptor.idVendor,
dev->descriptor.idProduct);
if (dev->descriptor.idVendor != VENDOR_ID_CANON)
continue;
printf("Canon product found...\n");
e = supported_product(dev->descriptor.idProduct);
if (e) {
printf("Canon `%s' found!\n", e->name);
*camera_dev = dev;
return e->id;
}
}
}
return USB_INIT_NOCAMERA;
}
/* The following two functions are based on gpio library:
* they are simple wrappers to the libusb */
int USB_write_control_msg(int value, char *buffer, int size)
{
int retval;
retval = usb_control_msg(cameraudh,
USB_TYPE_VENDOR|USB_RECIP_DEVICE|USB_DIR_OUT,
size > 1 ? 0x04 : 0x0c,
value,
0,
buffer,
size,
usb_timeout);
if (opt_debug) {
printf("WRITE CONTROL MSG, value %X, size %d: %s\n",
value, size, retval == -1 ? "FAILED" : "OK");
if (retval != -1)
dump_hex("DATA", buffer, size);
}
return retval;
}
int USB_read_control_msg(int value, char *buffer, int size)
{
int retval;
retval = usb_control_msg(cameraudh,
USB_TYPE_VENDOR|USB_RECIP_DEVICE|USB_DIR_IN,
size > 1 ? 0x04 : 0x0c,
value,
0,
buffer,
size,
usb_timeout);
if (opt_debug) {
printf("READ CONTROL MSG, value %X, size %d: %s\n",
value, size, retval == -1 ? "FAILED" : "OK");
if (retval != -1)
dump_hex("DATA", buffer, size);
}
return retval;
}
/* Bulk read/write, again just wrappers for the libusb */
int USB_read(void *buffer, int size)
{
int retval;
retval = usb_bulk_read(cameraudh, input_ep, buffer, size, usb_timeout);
if (opt_debug) {
printf("USB READ: %s (%X)\n", retval == -1 ? "FAILED" : "OK", retval);
if (retval != -1)
dump_hex("DATA", buffer, size);
}
return retval;
}
int USB_write(void *buffer, int size)
{
int retval;
retval = usb_bulk_write(cameraudh, output_ep, buffer, size, usb_timeout);
if (opt_debug) {
printf("USB WRITE: %s (%X)\n", retval == -1? "FAILED" : "OK", retval);
if (retval != -1)
dump_hex("DATA", buffer, size);
}
return retval;
}
/* Assemble and send an USB command for the canon protocol */
int USB_cmd(unsigned char cmd1, unsigned char cmd2, unsigned int cmd3, unsigned int serial, unsigned char *payload, int size)
{
unsigned char buffer[4096];
unsigned int aux;
serial = getpid()+0xAABBCCDD;
aux = size+0x10;
memset(buffer, 0, 4096);
*(unsigned int*)buffer = byteswap32(aux);
*(unsigned int*)(buffer+4) = byteswap32(cmd3);
buffer[0x40] = 0x02;
buffer[0x44] = cmd1;
buffer[0x47] = cmd2;
*(unsigned int*)(buffer+0x48) = byteswap32(aux);
*(unsigned int*)(buffer+0x4c) = byteswap32(serial);
if (payload != NULL)
memcpy(buffer+0x50, payload, size);
return USB_write_control_msg(0x10, buffer, 0x50+size);
}
/* Initializes the interface and performs the wake-up of the camera:
* the name "initial_sync" comes from the serial protocol, nothing
* to sync with USB :) */
int USB_initial_sync(void)
{
struct usb_device *camera_dev;
int retval;
unsigned char buffer[4096];
usb_timeout = 500;
retval = USB_camera_init(&camera_dev);
if (retval == USB_INIT_NOCAMERA) {
printf("Camera not found, please press the shot button and\n"
"check that the camera is in PC mode, then retry\n");
exit(1);
} else if (retval == USB_INIT_FAILED) {
printf("Fatal error initializing USB\n");
exit(1);
}
cameraudh = usb_open(camera_dev);
if (!cameraudh) {
printf("usb_open() error, can't open the camera\n");
exit(1);
}
retval = usb_set_configuration(cameraudh, configuration);
if (retval == USB_ERROR) {
printf("usb_set_configuration() error\n");
exit(1);
}
retval = usb_claim_interface(cameraudh, interface);
if (retval == USB_ERROR) {
printf("usb_claim_interface() error\n");
exit(1);
}
retval = usb_set_altinterface(cameraudh, alternate);
if (retval == USB_ERROR) {
printf("usb_set_altinterface() error\n");
exit(1);
}
if (opt_debug)
printf("USB: Camera successful open\n");
/* Camera initialization */
USB_read_control_msg(0x55, buffer, 1);
USB_read_control_msg(0x1, buffer, 0x58);
USB_write_control_msg(0x11, buffer+0x48, 0x10);
USB_read(buffer, 0x44);
usb_timeout = 3000;
return 0;
}
/* --------------- low level functions to control the camera ---------------- */
char *USB_get_id(void)
{
int retval;
static char buffer[0x50+0x4c];
USB_cmd(0x01, 0x12, 0x201, 0x01, NULL, 0);
retval = USB_read(buffer, 0x50+0x4c);
if (retval == -1)
return NULL;
firmware[1] = firmware[3] = firmware[5] = '.';
firmware[0] = buffer[0x5b]+'0';
firmware[2] = buffer[0x5a]+'0';
firmware[4] = buffer[0x59]+'0';
firmware[6] = buffer[0x58]+'0';
firmware[7] = '\0';
return buffer+0x5c;
}
char *USB_get_disk(void)
{
int retval;
static char buffer[4096];
USB_cmd(0x0a, 0x11, 0x202, 0x01, NULL, 0);
USB_read(buffer, 0x40);
memcpy(&retval, buffer+6, 4);
USB_read(buffer, retval);
return buffer;
}
#define BULK_TR_SIZE PAGE_SIZE
unsigned char *USB_get_data(char *pathname, int reqtype, int *retlen)
{
unsigned char buffer[4096*2];
unsigned char *image;
int aux = BULK_TR_SIZE;
int size;
int totalsize;
int n_read = 0;
memset(buffer, 0, 4);
buffer[0] = reqtype; /* select image or thumbnail */
*(unsigned int*)(buffer+4) = byteswap32(aux);
memcpy(buffer+8, pathname, strlen(pathname)+1);
USB_cmd(0x01, 0x11, 0x202, 0x01, buffer, strlen(pathname)+9);
USB_read(buffer, 0x40);
totalsize = byteswap32(*(unsigned int*)(buffer+6));
if (totalsize == 0)
return NULL;
*retlen = totalsize;
image = malloc(totalsize);
if (!image) {
perror("malloc");
exit(1);
}
printf("Getting %s, %d bytes\n", pathname, totalsize);
progressbar(PROGRESS_RESET, 0, 0);
while(1) {
size = (totalsize > BULK_TR_SIZE) ? BULK_TR_SIZE : totalsize;
USB_read(image+n_read, size);
totalsize -= size;
n_read += size;
progressbar(PROGRESS_PRINT, *retlen, n_read);
if (totalsize == 0) break;
}
return image;
}
time_t USB_get_date(void)
{
time_t curtime;
unsigned char buffer[1024];
USB_cmd(0x03, 0x12, 0x201, 0x01, NULL, 0);
USB_read(buffer, 0x60);
curtime = byteswap32(*(time_t*)(buffer+0x54));
return curtime;
}
int USB_get_disk_info(char *disk, int *size, int *free)
{
unsigned char buffer[1024];
char diskstr[] = "X:\\";
diskstr[0] = disk[0];
USB_cmd(0x09, 0x11, 0x201, 0x01, diskstr, 4);
USB_read(buffer, 0x5c);
if (buffer[0x50] != 0)
return -1;
*size = byteswap32(*(unsigned int*)(buffer+0x54));
*free = byteswap32(*(unsigned int*)(buffer+0x58));
return 0;
}
int USB_get_power_status(int *good, int *ac)
{
unsigned char buffer[1024];
USB_cmd(0x0a, 0x12, 0x201, 0x01, NULL, 0);
USB_read(buffer, 0x58);
if (*(buffer+0x54) == 0x06)
*good = 1;
else
*good = 0;
if (*(buffer+0x57) == 0x10)
*ac = 1;
else
*ac = 0;
return 0;
}
int USB_mkdir(char *pathname)
{
unsigned char buffer[1024];
unsigned char arg[1024];
if (pathname[1] != ':') {
snprintf(arg, 1024, "%s\\%s", lastpath, pathname);
pathname = arg;
}
USB_cmd(0x5, 0x11, 0x201, 0x01, pathname, strlen(pathname)+1);
USB_read(buffer, 0x54);
if (buffer[0x50] == 0)
return 0;
else
return -1;
}
int USB_rmdir(char *pathname)
{
unsigned char buffer[1024];
unsigned char arg[1024];
if (pathname[1] != ':') {
snprintf(arg, 1024, "%s\\%s", lastpath, pathname);
pathname = arg;
}
USB_cmd(0x6, 0x11, 0x201, 0x01, pathname, strlen(pathname)+1);
USB_read(buffer, 0x54);
if (buffer[0x50] == 0)
return 0;
else
return -1;
}
int USB_delete(char *pathname)
{
unsigned char buffer[1024];
memcpy(buffer, lastpath, strlen(lastpath)+1);
memcpy(buffer+strlen(lastpath)+1, pathname, strlen(pathname)+1);
buffer[strlen(lastpath)] = '\\';
USB_cmd(0x0d, 0x11, 0x201, 0x01, buffer, strlen(lastpath)+1+
strlen(pathname)+1);
USB_read(buffer, 0x54);
if (buffer[0x50] == 0x86)
return 0;
else
return -1;
}
int USB_set_file_attrib(char *pathname, unsigned char newattrib)
{
unsigned char buffer[1024];
buffer[0] = newattrib;
buffer[1] = buffer[2] = buffer[3] = 0x00;
memcpy(buffer+4, lastpath, strlen(lastpath)+1);
buffer[4+strlen(lastpath)] = '\\';
memcpy(buffer+4+strlen(lastpath)+1, pathname, strlen(pathname)+1);
USB_cmd(0x0e, 0x11, 0x201, 0x01, buffer, 4+strlen(lastpath)+1+
strlen(pathname)+1);
USB_read(buffer, 0x54);
if (buffer[0x50] == 0x86)
return 0;
else
return -1;
}
int USB_upload(char *source, char *target)
{
struct stat buf;
unsigned char buffer[4096*2];
char read_buffer[0x1400];
unsigned int serial, datalen, offset;
unsigned int len1, lenaux, aux;
unsigned short saux;
int fd, progress_bar = 0;
char arg[1024];
if (target == NULL) {
char *p;
p = strrchr(source, '/');
if (p != NULL)
target = p++;
else
target = source;
p = strchr(target, '.');
if (p != NULL && strchr(p+1, '.') != NULL) {
printf("sorry, only one dot allowed in target filename\n");
return -1;
}
}
if (strlen(target) <= 2 || target[1] != ':') {
snprintf(arg, 1024, "%s\\%s", lastpath, target);
target = arg;
}
serial = 0x12345678;
offset = 0;
fd = open(source, O_RDONLY);
if (fd == -1) {
perror("open");
return -1;
}
if (fstat(fd, &buf) == -1) {
perror("stat");
printf("WARING: s10sh will not show the progress bar\n\n");
} else {
progress_bar = 1;
progressbar(PROGRESS_RESET, 0, 0);
}
while(1) {
datalen = read(fd, read_buffer, 0x300);
if (datalen == 0) {
break;
} else if (datalen == -1) {
perror("read");
return -1;
}
len1 = 0x1c+strlen(target)+1+datalen;
memset(buffer, 0, 4);
buffer[4] = 0x03;
buffer[5] = 0x02;
lenaux = len1+0x40;
memcpy(buffer+6, &lenaux, 4);
memset(buffer+10, 0, 0x36);
USB_write_control_msg(0x10, buffer, 0x40);
USB_read(buffer, 0x40);
memcpy(buffer, &len1, 4);
aux = 0x0403;
memcpy(buffer+4, &aux, 4);
memset(buffer+8, 0, 0x38);
aux = 0x02;
memcpy(buffer+0x40, &aux, 4);
saux = 0x03;
memcpy(buffer+0x44, &saux, 2);
buffer[0x46] = 0x00;
buffer[0x47] = 0x11;
memcpy(buffer+0x48, &len1, 4);
memcpy(buffer+0x4c, &serial, 4);
aux = 0x02;
memcpy(buffer+0x50, &aux, 4);
memcpy(buffer+0x54, &offset, 4);
memcpy(buffer+0x58, &datalen, 4);
memcpy(buffer+0x5c, target, strlen(target)+1);
memcpy(buffer+0x5c+strlen(target)+1, read_buffer, datalen);
USB_write(buffer, len1+0x40);
USB_read(buffer, 0x5c);
offset += datalen;
progressbar(PROGRESS_PRINT, buf.st_size, offset);
}
close(fd);
printf("\n");
return 0;
}
void USB_close(void)
{
int retval;
retval = usb_release_interface(cameraudh, interface);
if (retval == USB_ERROR) {
printf("usb_release_interface() error\n");
}
if (usb_close(cameraudh) == USB_ERROR) {
printf("usb_close() error\n");
}
}
#endif /* HAVE_USB_SUPPORT */