Skip to content

Commit

Permalink
Bug fixes, rewrite transport.c handling, fix libusb code, clear out a…
Browse files Browse the repository at this point in the history
… bunch of risky/undefined behavior

- ptp_read/write functions will not cause misaligned accesses
- remove different ptpipusb and ptpusb handling, replace with much simpler state machine like reader
- House keeping
  • Loading branch information
petabyt committed Dec 14, 2024
1 parent 3e2fb1a commit 02d12fa
Show file tree
Hide file tree
Showing 16 changed files with 342 additions and 409 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ clean:
src/lua/*.o src/lua/lua-cjson/*.o src/*.d examples/*.d src/lua/*.d src/lua/lua-cjson/*.d *.a
cd examples && make clean

install: libcamlib.a
install: libcamlib.a camlib
cp libcamlib.a /usr/lib/
-mkdir /usr/include/camlib
cp src/*.h /usr/include/camlib/
cp camlib /usr/bin

test-ci: test/test.o test/data.o $(UNIX_LIB_FILES)
$(CC) test/test.o test/data.o $(UNIX_LIB_FILES) -lusb-vcam -lexif $(CFLAGS) -o test-ci
Expand Down
6 changes: 6 additions & 0 deletions src/bind.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ int bind_get_device_info(struct BindReq *bind, struct PtpRuntime *r) {
}

int x = ptp_get_device_info(r, r->di);
ptp_dump(r);
if (x) {
return bind->out(bind, "{\"error\": %d}", x);
}
Expand Down Expand Up @@ -535,11 +536,16 @@ int bind_download_file(struct BindReq *bind, struct PtpRuntime *r) {
}
}

int bind_read_int(struct BindReq *bind, struct PtpRuntime *r) {
return bind->out(bind, "{\"error\": %d}", ptp_read_int(r, r->data, 8));
}

struct RouteMap {
char *name;
int (*call)(struct BindReq *, struct PtpRuntime *);
}routes[] = {
{"ptp_hello_world", bind_hello_world},
{"ptp_read_int", bind_read_int},
{"ptp_status", bind_status},
{"ptp_reset", bind_reset},
{"ptp_init", bind_init},
Expand Down
55 changes: 44 additions & 11 deletions src/camlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,14 @@ struct PtpRuntime {
uint8_t *data;
int data_length;

/// @note For optimization on libusb, as many bytes as possible should be read at once. Generally this
/// is 512, but certain comm backends can manage more. For TCP, this isn't used.
/// @note Max size of a USB bulk transfer or max TCP packet size
/// @todo max packet size for IN and OUT
int max_packet_size;

/// @brief Info about current connection, used to detect camera type, supported opodes, etc
/// @note Set by ptp_parse_device_info. This should be NULL when this struct is created.
struct PtpDeviceInfo *di;
int device_type;
//int device_type;

/// @brief For Windows compatibility, this is set to indicate lenth for a data packet
/// that will be sent after a command packet. Will be set to zero when ptp_send_packet is called.
Expand Down Expand Up @@ -258,20 +258,53 @@ PUB int ptp_buffer_resize(struct PtpRuntime *r, size_t size);

// Data structure functions
PUB int ptp_write_unicode_string(char *dat, const char *string);
PUB int ptp_read_unicode_string(char *buffer, char *dat, int max);
PUB int ptp_read_unicode_string(char *buffer, const char *dat, int max);
PUB int ptp_read_utf8_string(void *dat, char *string, int max);
PUB int ptp_read_string(uint8_t *dat, char *string, int max);
PUB int ptp_write_string(uint8_t *dat, const char *string);
PUB int ptp_write_utf8_string(void *dat, const char *string);
PUB int ptp_read_uint16_array(const uint8_t *dat, uint16_t *buf, int max, int *length);
PUB int ptp_read_uint16_array_s(uint8_t *bs, uint8_t *be, uint16_t *buf, int max, int *length);
// TODO: This is bad
inline static int ptp_write_u8 (void *buf, uint8_t out) { ((uint8_t *)buf)[0] = out; return 1; }
inline static int ptp_write_u16(void *buf, uint16_t out) { ((uint16_t *)buf)[0] = out; return 2; }
inline static int ptp_write_u32(void *buf, uint32_t out) { ((uint32_t *)buf)[0] = out; return 4; }
inline static int ptp_read_u32 (const void *buf, uint32_t *out) { *out = ((const uint32_t *)buf)[0]; return 4; }
inline static int ptp_read_u16 (const void *buf, uint16_t *out) { *out = ((const uint16_t *)buf)[0]; return 2; }
inline static int ptp_read_u8 (const void *buf, uint8_t *out) { *out = ((const uint8_t *)buf)[0]; return 1; }
inline static int ptp_write_u8(void *buf, uint8_t out) {
((uint8_t *)buf)[0] = out;
return 1;
}
inline static int ptp_write_u16(void *buf, uint16_t out) {
uint8_t *b = buf;
b[0] = out & 0xFF;
b[1] = (out >> 8) & 0xFF;
return 2;
}
inline static int ptp_write_u32(void *buf, uint32_t out) {
uint8_t *b = buf;
b[0] = out & 0xFF;
b[1] = (out >> 8) & 0xFF;
b[2] = (out >> 16) & 0xFF;
b[3] = (out >> 24) & 0xFF;
return 4;
}
inline static int ptp_read_u8(const void *buf, uint8_t *out) {
const uint8_t *b = buf;
*out = b[0];
return 1;
}
inline static int ptp_read_u16(const void *buf, uint16_t *out) {
const uint8_t *b = buf;
*out = (uint16_t)b[0] | ((uint16_t)b[1] << 8);
return 2;
}
inline static int ptp_read_u32(const void *buf, uint32_t *out) {
const uint8_t *b = buf;
*out = (uint32_t)b[0] | ((uint32_t)b[1] << 8) | ((uint32_t)b[2] << 16) | ((uint32_t)b[3] << 24);
return 4;
}
inline static int ptp_read_u64(const void *buf, uint64_t *out) {
uint32_t lo, hi;
ptp_read_u32(buf, &lo);
ptp_read_u32((const uint8_t *)buf + 4, &hi);
*out = ((uint64_t)hi << 32) | lo;
return 8;
}

// Build a new PTP/IP or PTP/USB command packet in r->data
int ptp_new_cmd_packet(struct PtpRuntime *r, struct PtpCommand *cmd);
Expand Down
6 changes: 4 additions & 2 deletions src/cl_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ struct PtpDeviceInfo {
uint16_t events_supported[256];

int props_supported_length;
uint16_t props_supported[256];
uint16_t props_supported[512];

int capture_formats_length;
uint16_t capture_formats[32];
Expand All @@ -55,6 +55,8 @@ struct PtpStorageInfo {
uint64_t max_capacity;
uint64_t free_space;
uint32_t free_objects;
char storage_desc[128];
char volume_identifier[128];
};

struct PtpObjectInfo {
Expand Down Expand Up @@ -171,7 +173,7 @@ int ptp_prop_desc_json(const struct PtpPropDesc *pd, char *buffer, int max);
int ptp_parse_object_info(struct PtpRuntime *r, struct PtpObjectInfo *oi);
int ptp_storage_info_json(const struct PtpStorageInfo *so, char *buffer, int max);
int ptp_object_info_json(const struct PtpObjectInfo *so, char *buffer, int max);

int ptp_parse_storage_info(struct PtpRuntime *r, struct PtpStorageInfo *si);
int ptp_eos_events(struct PtpRuntime *r, struct PtpGenericEvent **p);
void *ptp_open_eos_events(struct PtpRuntime *r);
void *ptp_get_eos_event(struct PtpRuntime *r, void *e, struct PtpCanonEvent *ce);
Expand Down
2 changes: 1 addition & 1 deletion src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct Options {
int do_open_sessions;
};

static int usage() {
static int usage(void) {
printf(
"camlib\n"
" --dec <input_file> <output_file> Decode any PTP/USB packet dump into a readable text file\n"
Expand Down
155 changes: 154 additions & 1 deletion src/data.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,140 @@ static int osnprintf(char *str, int cur, int size, const char *format, ...) {
return r;
}

static void boundcheck(uint8_t *bs, uint8_t *be, int size) {
if (be == NULL) return;
if ((uintptr_t)bs > (uintptr_t)be) ptp_panic("PTP: bs after be\n");
if (((uintptr_t)be - (uintptr_t)bs) < (uintptr_t)size) {
ptp_panic("PTP: buffer overflow %p-%p (%u)\n", bs, be, size);
}
}

// Read standard UTF16 string
int ptp_read_string(uint8_t *d, char *string, int max) {
int of = 0;
uint8_t length;
of += ptp_read_u8(d + of, &length);

uint8_t i = 0;
while (i < length) {
uint16_t wchr;
of += ptp_read_u16(d + of, &wchr);
if (wchr > 128) wchr = '?';
else if (wchr != '\0' && wchr < 32) wchr = ' ';
string[i] = (char)wchr;
i++;
if (i >= max - 1) break;
}

string[i] = '\0';

return of;
}

int ptp_read_uint16_array(const uint8_t *dat, uint16_t *buf, int max, int *length) {
ptp_panic("Unfinished\n");
int of = 0;

uint32_t n;
of += ptp_read_u32(dat + of, &n);

for (uint32_t i = 0; i < n; i++) {
if (i >= max) {
ptp_panic("ptp_read_uint16_array overflow\n");
} else {
of += ptp_read_u16(dat + of, &buf[i]);
}
}

return of;
}

int ptp_read_uint16_array_s(uint8_t *bs, uint8_t *be, uint16_t *buf, int max, int *length) {
int of = 0;
uint32_t n;
of += ptp_read_u32(bs + of, &n);
(*length) = (int)n;
boundcheck(bs, be, 4 * (int)n + 4);
for (int i = 0; i < (int)n; i++) {
if (i >= max) {
ptp_panic("ptp_read_uint16_array overflow %i >= %d\n", i, n);
} else {
of += ptp_read_u16(bs + of, &buf[i]);
}
}
return of;
}

// Write standard PTP wchar string
int ptp_write_string(uint8_t *dat, const char *string) {
int of = 0;

uint32_t length = strlen(string);
of += ptp_write_u8(dat + of, length);

for (int i = 0; i < (int)length; i++) {
of += ptp_write_u8(dat + of, string[i]);
of += ptp_write_u8(dat + of, '\0');
}

return of;
}

// Write normal UTF-8 string
int ptp_write_utf8_string(void *dat, const char *string) {
char *o = (char *)dat;
int x = 0;
while (string[x] != '\0') {
o[x] = string[x];
x++;
}

o[x] = '\0';
x++;
return x;
}

// Write null-terminated UTF16 string
int ptp_write_unicode_string(char *dat, const char *string) {
int i;
for (i = 0; string[i] != '\0'; i++) {
dat[i * 2] = string[i];
dat[i * 2 + 1] = '\0';
}
dat[i * 2 + 1] = '\0';
return i;
}

// Read null-terminated UTF16 string
int ptp_read_unicode_string(char *buffer, const char *dat, int max) {
int i;
for (i = 0; dat[i] != '\0'; i += 2) {
buffer[i / 2] = dat[i];
if (i >= max - 2) {
buffer[(i / 2) + 1] = '\0';
return i;
}
}

buffer[(i / 2)] = '\0';
return i / 2;
}

int ptp_read_utf8_string(void *dat, char *string, int max) {
char *d = (char *)dat;
int x = 0;
while (d[x] != '\0') {
string[x] = d[x];
x++;
if (x > max - 1) break;
}

string[x] = '\0';
x++;

return x;
}

int ptp_get_prop_size(uint8_t *d, int type) {
uint32_t length32;
uint8_t length8;
Expand Down Expand Up @@ -97,7 +231,7 @@ int ptp_parse_prop_value(struct PtpRuntime *r) {
case 0:
return -1;
default:
ptp_panic("ptp_parse_prop_value: unknown data type size");
ptp_panic("ptp_parse_prop_value: unknown data type size %d", ptp_get_payload_length(r));
}

int out;
Expand Down Expand Up @@ -276,6 +410,25 @@ int ptp_parse_object_info(struct PtpRuntime *r, struct PtpObjectInfo *oi) {
return 0;
}

int ptp_parse_storage_info(struct PtpRuntime *r, struct PtpStorageInfo *si) {
if (ptp_get_payload_length(r) < 26) {
return PTP_RUNTIME_ERR;
}

int of = 0;
uint8_t *b = ptp_get_payload(r);
of += ptp_read_u16(b + of, &si->storage_type);
of += ptp_read_u16(b + of, &si->fs_type);
of += ptp_read_u16(b + of, &si->access_capability);
of += ptp_read_u64(b + of, &si->max_capacity);
of += ptp_read_u64(b + of, &si->free_space);
of += ptp_read_u32(b + of, &si->free_objects);
of += ptp_read_string(b + of, si->storage_desc, sizeof(si->storage_desc));
of += ptp_read_string(b + of, si->volume_identifier, sizeof(si->volume_identifier));

return 0;
}

// TODO: Different API
int ptp_pack_object_info(struct PtpRuntime *r, struct PtpObjectInfo *oi, uint8_t *buf, int max) {
if (1024 > max) {
Expand Down
3 changes: 3 additions & 0 deletions src/ip.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ static struct PtpIpBackend *init_comm(struct PtpRuntime *r) {
r->comm_backend = calloc(1, sizeof(struct PtpIpBackend));
}

// Max packet size for TCP
r->max_packet_size = 65535;

return (struct PtpIpBackend *)r->comm_backend;
}

Expand Down
2 changes: 2 additions & 0 deletions src/lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ int ptp_send(struct PtpRuntime *r, struct PtpCommand *cmd) {
return PTP_IO_ERR;
}

ptp_verbose_log("Sending %04x with %d params\n", cmd->code, cmd->param_length);

r->data_phase_length = 0;

int rc = ptp_send_try(r, cmd);
Expand Down
11 changes: 8 additions & 3 deletions src/libusb.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ int ptp_comm_init(struct PtpRuntime *r) {
ptp_panic("Connection is active\n");
}

// libusb 1.0 has no specificed limit for reads/writes
r->max_packet_size = 512 * 4;
if (r->connection_type != PTP_USB) {
ptp_panic("incorrect connection_type");
}

r->max_packet_size = 512;

if (r->comm_backend == NULL) {
r->comm_backend = malloc(sizeof(struct LibUSBBackend));
Expand Down Expand Up @@ -142,6 +145,7 @@ struct PtpDeviceEntry *ptpusb_device_list(struct PtpRuntime *r) {
if (ep[i].bmAttributes == LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK) {
if (ep[i].bEndpointAddress & LIBUSB_ENDPOINT_IN) {
curr_ent->endpoint_in = ep[i].bEndpointAddress;
r->max_packet_size = ep[i].wMaxPacketSize;
ptp_verbose_log("Endpoint IN addr: 0x%X\n", ep[i].bEndpointAddress);
} else {
curr_ent->endpoint_out = ep[i].bEndpointAddress;
Expand Down Expand Up @@ -344,10 +348,11 @@ int ptp_read_int(struct PtpRuntime *r, void *to, int length) {
int rc = libusb_bulk_transfer(
backend->handle,
backend->endpoint_int,
(unsigned char *)to, length, &transferred, 10);
(unsigned char *)to, length, &transferred, 1000);
if (rc == LIBUSB_ERROR_NO_DEVICE) {
return PTP_IO_ERR;
} else if (rc == LIBUSB_ERROR_TIMEOUT) {
ptp_verbose_log("Timeout");
return 0;
}

Expand Down
Loading

0 comments on commit 02d12fa

Please sign in to comment.