e809c55600ff63a37b77f57167b22ce7c577b2e9
[libam7xxx.git] / src / am7xxx.c
1 /* am7xxx - communication with AM7xxx based USB Pico Projectors and DPFs
2  *
3  * Copyright (C) 2012  Antonio Ospite <ospite@studenti.unina.it>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdarg.h>
23 #include <errno.h>
24 #include <libusb.h>
25 #include <math.h>
26
27 #include "am7xxx.h"
28 #include "serialize.h"
29
30 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
31
32 /* If we're not using GNU C, elide __attribute__
33  * taken from: http://unixwiz.net/techtips/gnu-c-attributes.html)
34  */
35 #ifndef __GNUC__
36 #  define  __attribute__(x)  /*NOTHING*/
37 #endif
38
39 /* Control shared library symbols visibility */
40 #if defined _WIN32 || defined __CYGWIN__
41         #define AM7XXX_PUBLIC __declspec(dllexport)
42         #define AM7XXX_LOCAL
43 #else
44         #if __GNUC__ >= 4
45                 #define AM7XXX_PUBLIC __attribute__ ((visibility ("default")))
46                 #define AM7XXX_LOCAL  __attribute__ ((visibility ("hidden")))
47         #else
48                 #define AM7XXX_PUBLIC
49                 #define AM7XXX_LOCAL
50         #endif
51 #endif
52
53 static void log_message(am7xxx_context *ctx,
54                         int level,
55                         const char *function,
56                         int line,
57                         const char *fmt,
58                         ...) __attribute__ ((format (printf, 5, 6)));
59
60 #define fatal(...)        log_message(NULL, AM7XXX_LOG_FATAL,   __func__, __LINE__, __VA_ARGS__)
61 #define error(ctx, ...)   log_message(ctx,  AM7XXX_LOG_ERROR,   __func__, __LINE__, __VA_ARGS__)
62 #define warning(ctx, ...) log_message(ctx,  AM7XXX_LOG_WARNING, __func__, 0,        __VA_ARGS__)
63 #define info(ctx, ...)    log_message(ctx,  AM7XXX_LOG_INFO,    __func__, 0,        __VA_ARGS__)
64 #define debug(ctx, ...)   log_message(ctx,  AM7XXX_LOG_DEBUG,   __func__, 0,        __VA_ARGS__)
65 #define trace(ctx, ...)   log_message(ctx,  AM7XXX_LOG_TRACE,   NULL,     0,        __VA_ARGS__)
66
67 struct am7xxx_usb_device_descriptor {
68         const char *name;
69         uint16_t vendor_id;
70         uint16_t product_id;
71 };
72
73 static struct am7xxx_usb_device_descriptor supported_devices[] = {
74         {
75                 .name       = "Acer C110",
76                 .vendor_id  = 0x1de1,
77                 .product_id = 0xc101,
78         },
79         {
80                 .name       = "Acer C112",
81                 .vendor_id  = 0x1de1,
82                 .product_id = 0x5501,
83         },
84         {
85                 .name       ="Aiptek PocketCinema T25",
86                 .vendor_id  = 0x08ca,
87                 .product_id = 0x2144,
88         },
89         {
90                 .name       = "Philips/Sagemcom PicoPix 1020",
91                 .vendor_id  = 0x21e7,
92                 .product_id = 0x000e,
93         },
94         {
95                 .name       = "Philips/Sagemcom PicoPix 2055",
96                 .vendor_id  = 0x21e7,
97                 .product_id = 0x0016,
98         },
99 };
100
101 /* The header size on the wire is known to be always 24 bytes, regardless of
102  * the memory configuration enforced by different architectures or compilers
103  * for struct am7xxx_header
104  */
105 #define AM7XXX_HEADER_WIRE_SIZE 24
106
107 struct _am7xxx_device {
108         libusb_device_handle *usb_device;
109         uint8_t buffer[AM7XXX_HEADER_WIRE_SIZE];
110         am7xxx_device_info *device_info;
111         am7xxx_context *ctx;
112         const struct am7xxx_usb_device_descriptor *desc;
113         am7xxx_device *next;
114 };
115
116 struct _am7xxx_context {
117         libusb_context *usb_context;
118         int log_level;
119         am7xxx_device *devices_list;
120 };
121
122 typedef enum {
123         AM7XXX_PACKET_TYPE_DEVINFO = 0x01,
124         AM7XXX_PACKET_TYPE_IMAGE   = 0x02,
125         AM7XXX_PACKET_TYPE_POWER   = 0x04,
126         AM7XXX_PACKET_TYPE_ZOOM    = 0x05,
127 } am7xxx_packet_type;
128
129 struct am7xxx_generic_header {
130         uint32_t field0;
131         uint32_t field1;
132         uint32_t field2;
133         uint32_t field3;
134 };
135
136 struct am7xxx_devinfo_header {
137         uint32_t native_width;
138         uint32_t native_height;
139         uint32_t unknown0;
140         uint32_t unknown1;
141 };
142
143 struct am7xxx_image_header {
144         uint32_t format;
145         uint32_t width;
146         uint32_t height;
147         uint32_t image_size;
148 };
149
150 struct am7xxx_power_header {
151         uint32_t bit2;
152         uint32_t bit1;
153         uint32_t bit0;
154 };
155
156 struct am7xxx_zoom_header {
157         uint32_t bit1;
158         uint32_t bit0;
159 };
160
161 /*
162  * Examples of packet headers:
163  *
164  * Image header:
165  * 02 00 00 00 00 10 3e 10 01 00 00 00 20 03 00 00 e0 01 00 00 53 E8 00 00
166  *
167  * Power header:
168  * 04 00 00 00 00 0c ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
169  */
170
171 /* Direction of the communication from the host point of view */
172 #define AM7XXX_DIRECTION_OUT 0 /* host -> device */
173 #define AM7XXX_DIRECTION_IN  1 /* host <- device */
174
175 struct am7xxx_header {
176         uint32_t packet_type;
177         uint8_t direction;
178         uint8_t header_data_len;
179         uint8_t unknown2;
180         uint8_t unknown3;
181         union {
182                 struct am7xxx_generic_header data;
183                 struct am7xxx_devinfo_header devinfo;
184                 struct am7xxx_image_header image;
185                 struct am7xxx_power_header power;
186                 struct am7xxx_zoom_header zoom;
187         } header_data;
188 };
189
190
191 #ifdef DEBUG
192 static void debug_dump_devinfo_header(am7xxx_context *ctx, struct am7xxx_devinfo_header *d)
193 {
194         if (ctx == NULL || d == NULL)
195                 return;
196
197         debug(ctx, "Info header:\n");
198         debug(ctx, "\tnative_width:  0x%08x (%u)\n", d->native_width, d->native_width);
199         debug(ctx, "\tnative_height: 0x%08x (%u)\n", d->native_height, d->native_height);
200         debug(ctx, "\tunknown0:      0x%08x (%u)\n", d->unknown0, d->unknown0);
201         debug(ctx, "\tunknown1:      0x%08x (%u)\n", d->unknown1, d->unknown1);
202 }
203
204 static void debug_dump_image_header(am7xxx_context *ctx, struct am7xxx_image_header *i)
205 {
206         if (ctx == NULL || i == NULL)
207                 return;
208
209         debug(ctx, "Image header:\n");
210         debug(ctx, "\tformat:     0x%08x (%u)\n", i->format, i->format);
211         debug(ctx, "\twidth:      0x%08x (%u)\n", i->width, i->width);
212         debug(ctx, "\theight:     0x%08x (%u)\n", i->height, i->height);
213         debug(ctx, "\timage size: 0x%08x (%u)\n", i->image_size, i->image_size);
214 }
215
216 static void debug_dump_power_header(am7xxx_context *ctx, struct am7xxx_power_header *p)
217 {
218         if (ctx == NULL || p == NULL)
219                 return;
220
221         debug(ctx, "Power header:\n");
222         debug(ctx, "\tbit2: 0x%08x (%u)\n", p->bit2, p->bit2);
223         debug(ctx, "\tbit1: 0x%08x (%u)\n", p->bit1, p->bit1);
224         debug(ctx, "\tbit0: 0x%08x (%u)\n", p->bit0, p->bit0);
225 }
226
227 static void debug_dump_zoom_header(am7xxx_context *ctx, struct am7xxx_zoom_header *z)
228 {
229         if (ctx == NULL || z == NULL)
230                 return;
231
232         debug(ctx, "Zoom header:\n");
233         debug(ctx, "\tbit1: 0x%08x (%u)\n", z->bit1, z->bit1);
234         debug(ctx, "\tbit0: 0x%08x (%u)\n", z->bit0, z->bit0);
235 }
236
237 static void debug_dump_header(am7xxx_context *ctx, struct am7xxx_header *h)
238 {
239         if (ctx == NULL || h == NULL)
240                 return;
241
242         debug(ctx, "BEGIN\n");
243         debug(ctx, "packet_type:     0x%08x (%u)\n", h->packet_type, h->packet_type);
244         debug(ctx, "direction:       0x%02hhx (%hhu) (%s)\n", h->direction, h->direction,
245               h->direction == AM7XXX_DIRECTION_IN ? "IN" :
246               h->direction == AM7XXX_DIRECTION_OUT ? "OUT" :
247               "UNKNOWN");
248         debug(ctx, "header_data_len: 0x%02hhx (%hhu)\n", h->header_data_len, h->header_data_len);
249         debug(ctx, "unknown2:        0x%02hhx (%hhu)\n", h->unknown2, h->unknown2);
250         debug(ctx, "unknown3:        0x%02hhx (%hhu)\n", h->unknown3, h->unknown3);
251
252         switch(h->packet_type) {
253         case AM7XXX_PACKET_TYPE_DEVINFO:
254                 debug_dump_devinfo_header(ctx, &(h->header_data.devinfo));
255                 break;
256
257         case AM7XXX_PACKET_TYPE_IMAGE:
258                 debug_dump_image_header(ctx, &(h->header_data.image));
259                 break;
260
261         case AM7XXX_PACKET_TYPE_POWER:
262                 debug_dump_power_header(ctx, &(h->header_data.power));
263                 break;
264
265         case AM7XXX_PACKET_TYPE_ZOOM:
266                 debug_dump_zoom_header(ctx, &(h->header_data.zoom));
267                 break;
268
269         default:
270                 debug(ctx, "Packet type not supported!\n");
271                 break;
272         }
273         debug(ctx, "END\n\n");
274 }
275
276 static inline unsigned int in_80chars(unsigned int i)
277 {
278         /* The 3 below is the length of "xx " where xx is the hex string
279          * representation of a byte */
280         return ((i+1) % (80/3));
281 }
282
283 static void trace_dump_buffer(am7xxx_context *ctx, const char *message,
284                               uint8_t *buffer, unsigned int len)
285 {
286         unsigned int i;
287
288         if (ctx == NULL || buffer == NULL || len == 0)
289                 return;
290
291         trace(ctx, "\n");
292         if (message)
293                 trace(ctx, "%s\n", message);
294
295         for (i = 0; i < len; i++) {
296                 trace(ctx, "%02hhX%c", buffer[i], (in_80chars(i) && (i < len - 1)) ? ' ' : '\n');
297         }
298         trace(ctx, "\n");
299 }
300 #else
301 static void debug_dump_header(am7xxx_context *ctx, struct am7xxx_header *h)
302 {
303         (void)ctx;
304         (void)h;
305 }
306
307 static void trace_dump_buffer(am7xxx_context *ctx, const char *message,
308                               uint8_t *buffer, unsigned int len)
309 {
310         (void)ctx;
311         (void)message;
312         (void)buffer;
313         (void)len;
314 }
315 #endif /* DEBUG */
316
317 static int read_data(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
318 {
319         int ret;
320         int transferred = 0;
321
322         ret = libusb_bulk_transfer(dev->usb_device, 0x81, buffer, len, &transferred, 0);
323         if (ret != 0 || (unsigned int)transferred != len) {
324                 error(dev->ctx, "ret: %d\ttransferred: %d (expected %u)\n",
325                       ret, transferred, len);
326                 return ret;
327         }
328
329         trace_dump_buffer(dev->ctx, "<-- received", buffer, len);
330
331         return 0;
332 }
333
334 static int send_data(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
335 {
336         int ret;
337         int transferred = 0;
338
339         trace_dump_buffer(dev->ctx, "sending -->", buffer, len);
340
341         ret = libusb_bulk_transfer(dev->usb_device, 0x1, buffer, len, &transferred, 0);
342         if (ret != 0 || (unsigned int)transferred != len) {
343                 error(dev->ctx, "ret: %d\ttransferred: %d (expected %u)\n",
344                       ret, transferred, len);
345                 return ret;
346         }
347
348         return 0;
349 }
350
351 static void serialize_header(struct am7xxx_header *h, uint8_t *buffer)
352 {
353         uint8_t **buffer_iterator = &buffer;
354
355         put_le32(h->packet_type, buffer_iterator);
356         put_8(h->direction, buffer_iterator);
357         put_8(h->header_data_len, buffer_iterator);
358         put_8(h->unknown2, buffer_iterator);
359         put_8(h->unknown3, buffer_iterator);
360         put_le32(h->header_data.data.field0, buffer_iterator);
361         put_le32(h->header_data.data.field1, buffer_iterator);
362         put_le32(h->header_data.data.field2, buffer_iterator);
363         put_le32(h->header_data.data.field3, buffer_iterator);
364 }
365
366 static void unserialize_header(uint8_t *buffer, struct am7xxx_header *h)
367 {
368         uint8_t **buffer_iterator = &buffer;
369
370         h->packet_type = get_le32(buffer_iterator);
371         h->direction = get_8(buffer_iterator);
372         h->header_data_len = get_8(buffer_iterator);
373         h->unknown2 = get_8(buffer_iterator);
374         h->unknown3 = get_8(buffer_iterator);
375         h->header_data.data.field0 = get_le32(buffer_iterator);
376         h->header_data.data.field1 = get_le32(buffer_iterator);
377         h->header_data.data.field2 = get_le32(buffer_iterator);
378         h->header_data.data.field3 = get_le32(buffer_iterator);
379 }
380
381 static int read_header(am7xxx_device *dev, struct am7xxx_header *h)
382 {
383         int ret;
384
385         ret = read_data(dev, dev->buffer, AM7XXX_HEADER_WIRE_SIZE);
386         if (ret < 0)
387                 goto out;
388
389         unserialize_header(dev->buffer, h);
390
391         if (h->direction == AM7XXX_DIRECTION_IN) {
392                 ret = 0;
393         } else {
394                 error(dev->ctx,
395                       "Expected an AM7XXX_DIRECTION_IN packet, got one with direction = %d. Weird!\n",
396                       h->direction);
397                 ret = -EINVAL;
398         }
399
400         debug_dump_header(dev->ctx, h);
401
402 out:
403         return ret;
404 }
405
406 static int send_header(am7xxx_device *dev, struct am7xxx_header *h)
407 {
408         int ret;
409
410         debug_dump_header(dev->ctx, h);
411
412         /* For symmetry with read_header() we should check here for
413          * h->direction == AM7XXX_DIRECTION_OUT but we just ensure that in all
414          * the callers and save some cycles here.
415          */
416
417         serialize_header(h, dev->buffer);
418
419         ret = send_data(dev, dev->buffer, AM7XXX_HEADER_WIRE_SIZE);
420         if (ret < 0)
421                 error(dev->ctx, "failed to send data\n");
422
423         return ret;
424 }
425
426 /* When level == AM7XXX_LOG_FATAL do not check the log_level from the context
427  * and print the message unconditionally, this makes it possible to print
428  * fatal messages even early on initialization, before the context has been
429  * set up */
430 static void log_message(am7xxx_context *ctx,
431                         int level,
432                         const char *function,
433                         int line,
434                         const char *fmt,
435                         ...)
436 {
437         va_list ap;
438
439         if (level == AM7XXX_LOG_FATAL || (ctx && level <= ctx->log_level)) {
440                 if (function) {
441                         fprintf(stderr, "%s", function);
442                         if (line)
443                                 fprintf(stderr, "[%d]", line);
444                         fprintf(stderr, ": ");
445                 }
446
447                 va_start(ap, fmt);
448                 vfprintf(stderr, fmt, ap);
449                 va_end(ap);
450         }
451
452         return;
453 }
454
455 static am7xxx_device *add_new_device(am7xxx_context *ctx,
456                                      const struct am7xxx_usb_device_descriptor *desc)
457 {
458         am7xxx_device **devices_list;
459         am7xxx_device *new_device;
460
461         if (ctx == NULL) {
462                 fatal("context must not be NULL!\n");
463                 return NULL;
464         }
465
466         new_device = malloc(sizeof(*new_device));
467         if (new_device == NULL) {
468                 fatal("cannot allocate a new device (%s)\n", strerror(errno));
469                 return NULL;
470         }
471         memset(new_device, 0, sizeof(*new_device));
472
473         new_device->ctx = ctx;
474         new_device->desc = desc;
475
476         devices_list = &(ctx->devices_list);
477
478         if (*devices_list == NULL) {
479                 *devices_list = new_device;
480         } else {
481                 am7xxx_device *prev = *devices_list;
482                 while (prev->next)
483                         prev = prev->next;
484                 prev->next = new_device;
485         }
486         return new_device;
487 }
488
489 static am7xxx_device *find_device(am7xxx_context *ctx,
490                                   unsigned int device_index)
491 {
492         unsigned int i = 0;
493         am7xxx_device *current;
494
495         if (ctx == NULL) {
496                 fatal("context must not be NULL!\n");
497                 return NULL;
498         }
499
500         current = ctx->devices_list;
501         while (current && i++ < device_index)
502                 current = current->next;
503
504         return current;
505 }
506
507 typedef enum {
508         SCAN_OP_BUILD_DEVLIST,
509         SCAN_OP_OPEN_DEVICE,
510 } scan_op;
511
512 /**
513  * This is where the central logic of multi-device support is.
514  *
515  * When 'op' == SCAN_OP_BUILD_DEVLIST the parameters 'open_device_index' and
516  * 'dev' are ignored; the function returns 0 on success and a negative value
517  * on error.
518  *
519  * When 'op' == SCAN_OP_OPEN_DEVICE the function opens the supported USB
520  * device with index 'open_device_index' and returns the correspondent
521  * am7xxx_device in the 'dev' parameter; the function returns 0 on success,
522  * 1 if the device was already open and a negative value on error.
523  *
524  * NOTES:
525  * if scan_devices() fails when called with 'op' == SCAN_OP_BUILD_DEVLIST,
526  * the caller might want to call am7xxx_shutdown() in order to remove
527  * devices possibly added before the failure.
528  */
529 static int scan_devices(am7xxx_context *ctx, scan_op op,
530                         unsigned int open_device_index, am7xxx_device **dev)
531 {
532         int num_devices;
533         libusb_device** list;
534         unsigned int current_index;
535         int i;
536         int ret;
537
538         if (ctx == NULL) {
539                 fatal("context must not be NULL!\n");
540                 return -EINVAL;
541         }
542         if (op == SCAN_OP_BUILD_DEVLIST && ctx->devices_list != NULL) {
543                 error(ctx, "device scan done already? Abort!\n");
544                 return -EINVAL;
545         }
546
547         num_devices = libusb_get_device_list(ctx->usb_context, &list);
548         if (num_devices < 0) {
549                 ret = -ENODEV;
550                 goto out;
551         }
552
553         current_index = 0;
554         for (i = 0; i < num_devices; i++) {
555                 struct libusb_device_descriptor desc;
556                 unsigned int j;
557
558                 ret = libusb_get_device_descriptor(list[i], &desc);
559                 if (ret < 0)
560                         continue;
561
562                 for (j = 0; j < ARRAY_SIZE(supported_devices); j++) {
563                         if (desc.idVendor == supported_devices[j].vendor_id &&
564                             desc.idProduct == supported_devices[j].product_id) {
565
566                                 if (op == SCAN_OP_BUILD_DEVLIST) {
567                                         am7xxx_device *new_device;
568                                         info(ctx, "am7xxx device found, index: %d, name: %s\n",
569                                              current_index,
570                                              supported_devices[j].name);
571                                         new_device = add_new_device(ctx, &supported_devices[j]);
572                                         if (new_device == NULL) {
573                                                 /* XXX, the caller may want
574                                                  * to call am7xxx_shutdown() if
575                                                  * we fail here, as we may have
576                                                  * added some devices already
577                                                  */
578                                                 debug(ctx, "Cannot create a new device\n");
579                                                 ret = -ENODEV;
580                                                 goto out;
581                                         }
582                                 } else if (op == SCAN_OP_OPEN_DEVICE &&
583                                            current_index == open_device_index) {
584
585                                         *dev = find_device(ctx, open_device_index);
586                                         if (*dev == NULL) {
587                                                 ret = -ENODEV;
588                                                 goto out;
589                                         }
590
591                                         /* the usb device has already been opened */
592                                         if ((*dev)->usb_device) {
593                                                 debug(ctx, "(*dev)->usb_device already set\n");
594                                                 ret = 1;
595                                                 goto out;
596                                         }
597
598                                         ret = libusb_open(list[i], &((*dev)->usb_device));
599                                         if (ret < 0) {
600                                                 debug(ctx, "libusb_open failed\n");
601                                                 goto out;
602                                         }
603
604                                         /* XXX, the device is now open, if any
605                                          * of the calls below fail we need to
606                                          * close it again before bailing out.
607                                          */
608
609                                         ret = libusb_set_configuration((*dev)->usb_device, 2);
610                                         if (ret < 0) {
611                                                 debug(ctx, "libusb_set_configuration failed\n");
612                                                 debug(ctx, "Cannot set configuration 2\n");
613                                                 goto out_libusb_close;
614                                         }
615
616                                         ret = libusb_claim_interface((*dev)->usb_device, 0);
617                                         if (ret < 0) {
618                                                 debug(ctx, "libusb_claim_interface failed\n");
619                                                 debug(ctx, "Cannot claim interface 0\n");
620 out_libusb_close:
621                                                 libusb_close((*dev)->usb_device);
622                                                 (*dev)->usb_device = NULL;
623                                                 goto out;
624                                         }
625
626                                         goto out;
627                                 }
628                                 current_index++;
629                         }
630                 }
631         }
632
633         /* if we made it up to here we didn't find any device to open */
634         if (op == SCAN_OP_OPEN_DEVICE) {
635                 error(ctx, "Cannot find any device to open\n");
636                 ret = -ENODEV;
637                 goto out;
638         }
639
640         /* everything went fine when building the device list */
641         ret = 0;
642 out:
643         libusb_free_device_list(list, 1);
644         return ret;
645 }
646
647 /* Public API */
648
649 AM7XXX_PUBLIC int am7xxx_init(am7xxx_context **ctx)
650 {
651         int ret = 0;
652
653         *ctx = malloc(sizeof(**ctx));
654         if (*ctx == NULL) {
655                 fatal("cannot allocate the context (%s)\n", strerror(errno));
656                 ret = -ENOMEM;
657                 goto out;
658         }
659         memset(*ctx, 0, sizeof(**ctx));
660
661         /* Set the highest log level during initialization */
662         (*ctx)->log_level = AM7XXX_LOG_TRACE;
663
664         ret = libusb_init(&((*ctx)->usb_context));
665         if (ret < 0)
666                 goto out_free_context;
667
668         libusb_set_debug((*ctx)->usb_context, 3);
669
670         ret = scan_devices(*ctx, SCAN_OP_BUILD_DEVLIST , 0, NULL);
671         if (ret < 0) {
672                 error(*ctx, "scan_devices() failed\n");
673                 am7xxx_shutdown(*ctx);
674                 goto out;
675         }
676
677         /* Set a quieter log level as default for normal operation */
678         (*ctx)->log_level = AM7XXX_LOG_ERROR;
679         return 0;
680
681 out_free_context:
682         free(*ctx);
683         *ctx = NULL;
684 out:
685         return ret;
686 }
687
688 AM7XXX_PUBLIC void am7xxx_shutdown(am7xxx_context *ctx)
689 {
690         am7xxx_device *current;
691
692         if (ctx == NULL) {
693                 fatal("context must not be NULL!\n");
694                 return;
695         }
696
697         current = ctx->devices_list;
698         while (current) {
699                 am7xxx_device *next = current->next;
700                 am7xxx_close_device(current);
701                 free(current->device_info);
702                 free(current);
703                 current = next;
704         }
705
706         libusb_exit(ctx->usb_context);
707         free(ctx);
708         ctx = NULL;
709 }
710
711 AM7XXX_PUBLIC void am7xxx_set_log_level(am7xxx_context *ctx, am7xxx_log_level log_level)
712 {
713         ctx->log_level = log_level;
714 }
715
716 AM7XXX_PUBLIC int am7xxx_open_device(am7xxx_context *ctx, am7xxx_device **dev,
717                        unsigned int device_index)
718 {
719         int ret;
720
721         if (ctx == NULL) {
722                 fatal("context must not be NULL!\n");
723                 return -EINVAL;
724         }
725
726         ret = scan_devices(ctx, SCAN_OP_OPEN_DEVICE, device_index, dev);
727         if (ret < 0) {
728                 errno = ENODEV;
729                 goto out;
730         } else if (ret > 0) {
731                 warning(ctx, "device %d already open\n", device_index);
732                 errno = EBUSY;
733                 ret = -EBUSY;
734                 goto out;
735         }
736
737         /* Philips/Sagemcom PicoPix projectors require that the DEVINFO packet
738          * is the first one to be sent to the device in order for it to
739          * successfully return the correct device information.
740          *
741          * So, if there is not a cached version of it (from a previous open),
742          * we ask for device info at open time,
743          */
744         if ((*dev)->device_info == NULL) {
745                 ret = am7xxx_get_device_info(*dev, NULL);
746                 if (ret < 0)
747                         error(ctx, "cannot get device info\n");
748         }
749
750 out:
751         return ret;
752 }
753
754 AM7XXX_PUBLIC int am7xxx_close_device(am7xxx_device *dev)
755 {
756         if (dev == NULL) {
757                 fatal("dev must not be NULL!\n");
758                 return -EINVAL;
759         }
760         if (dev->usb_device) {
761                 libusb_release_interface(dev->usb_device, 0);
762                 libusb_close(dev->usb_device);
763                 dev->usb_device = NULL;
764         }
765         return 0;
766 }
767
768 AM7XXX_PUBLIC int am7xxx_get_device_info(am7xxx_device *dev,
769                            am7xxx_device_info *device_info)
770 {
771         int ret;
772         struct am7xxx_header h = {
773                 .packet_type     = AM7XXX_PACKET_TYPE_DEVINFO,
774                 .direction       = AM7XXX_DIRECTION_OUT,
775                 .header_data_len = 0x00,
776                 .unknown2        = 0x3e,
777                 .unknown3        = 0x10,
778                 .header_data = {
779                         .devinfo = {
780                                 .native_width  = 0,
781                                 .native_height = 0,
782                                 .unknown0      = 0,
783                                 .unknown1      = 0,
784                         },
785                 },
786         };
787
788         if (dev->device_info) {
789                 memcpy(device_info, dev->device_info, sizeof(*device_info));
790                 return 0;
791         }
792
793         ret = send_header(dev, &h);
794         if (ret < 0)
795                 return ret;
796
797         ret = read_header(dev, &h);
798         if (ret < 0)
799                 return ret;
800
801         if (h.packet_type != AM7XXX_PACKET_TYPE_DEVINFO) {
802                 error(dev->ctx, "expected packet type: %d, got %d instead!\n",
803                       AM7XXX_PACKET_TYPE_DEVINFO, h.packet_type);
804                 errno = ENOTSUP;
805                 return -ENOTSUP;
806         }
807
808         dev->device_info = malloc(sizeof(*dev->device_info));
809         if (dev->device_info == NULL) {
810                 error(dev->ctx, "cannot allocate a device info (%s)\n",
811                        strerror(errno));
812                 return -ENOMEM;
813         }
814         memset(dev->device_info, 0, sizeof(*dev->device_info));
815
816         dev->device_info->native_width = h.header_data.devinfo.native_width;
817         dev->device_info->native_height = h.header_data.devinfo.native_height;
818 #if 0
819         /* No reason to expose these in the public API until we know what they mean */
820         dev->device_info->unknown0 = h.header_data.devinfo.unknown0;
821         dev->device_info->unknown1 = h.header_data.devinfo.unknown1;
822 #endif
823
824         return 0;
825 }
826
827 AM7XXX_PUBLIC int am7xxx_calc_scaled_image_dimensions(am7xxx_device *dev,
828                                         unsigned int upscale,
829                                         unsigned int original_width,
830                                         unsigned int original_height,
831                                         unsigned int *scaled_width,
832                                         unsigned int *scaled_height)
833 {
834
835         am7xxx_device_info device_info;
836         float width_ratio;
837         float height_ratio;
838         int ret;
839
840         ret = am7xxx_get_device_info(dev, &device_info);
841         if (ret < 0) {
842                 error(dev->ctx, "cannot get device info\n");
843                 return ret;
844         }
845
846         /*
847          * Check if we need to rescale; if the input image fits the native
848          * dimensions there is no need to, unless we want to upscale.
849          */
850         if (!upscale &&
851             original_width <= device_info.native_width &&
852             original_height <= device_info.native_height ) {
853                 debug(dev->ctx, "CASE 0, no rescaling, the original image fits already\n");
854                 *scaled_width = original_width;
855                 *scaled_height = original_height;
856                 return 0;
857         }
858
859         /* Input dimensions relative to the device native dimensions */
860         width_ratio =  (float)original_width / device_info.native_width;
861         height_ratio = (float)original_height / device_info.native_height;
862
863         if (width_ratio > height_ratio) {
864                 /*
865                  * The input is proportionally "wider" than the device viewport
866                  * so its height needs to be adjusted
867                  */
868                 debug(dev->ctx, "CASE 1, original image wider, adjust the scaled height\n");
869                 *scaled_width = device_info.native_width;
870                 *scaled_height = (unsigned int)lroundf(original_height / width_ratio);
871         } else if (width_ratio < height_ratio) {
872                 /*
873                  * The input is proportionally "taller" than the device viewport
874                  * so its width needs to be adjusted
875                  */
876                 debug(dev->ctx, "CASE 2 original image taller, adjust the scaled width\n");
877                 *scaled_width = (unsigned int)lroundf(original_width / height_ratio);
878                 *scaled_height = device_info.native_height;
879         } else {
880                 debug(dev->ctx, "CASE 3, just rescale, same aspect ratio already\n");
881                 *scaled_width = device_info.native_width;
882                 *scaled_height = device_info.native_height;
883         }
884         debug(dev->ctx, "scaled dimensions: %dx%d\n", *scaled_width, *scaled_height);
885
886         return 0;
887 }
888
889 AM7XXX_PUBLIC int am7xxx_send_image(am7xxx_device *dev,
890                       am7xxx_image_format format,
891                       unsigned int width,
892                       unsigned int height,
893                       uint8_t *image,
894                       unsigned int image_size)
895 {
896         int ret;
897         struct am7xxx_header h = {
898                 .packet_type     = AM7XXX_PACKET_TYPE_IMAGE,
899                 .direction       = AM7XXX_DIRECTION_OUT,
900                 .header_data_len = sizeof(struct am7xxx_image_header),
901                 .unknown2        = 0x3e,
902                 .unknown3        = 0x10,
903                 .header_data = {
904                         .image = {
905                                 .format     = format,
906                                 .width      = width,
907                                 .height     = height,
908                                 .image_size = image_size,
909                         },
910                 },
911         };
912
913         ret = send_header(dev, &h);
914         if (ret < 0)
915                 return ret;
916
917         if (image == NULL || image_size == 0) {
918                 warning(dev->ctx, "Not sending any data, check the 'image' or 'image_size' parameters\n");
919                 return 0;
920         }
921
922         return send_data(dev, image, image_size);
923 }
924
925 AM7XXX_PUBLIC int am7xxx_set_power_mode(am7xxx_device *dev, am7xxx_power_mode power)
926 {
927         int ret;
928         struct am7xxx_header h = {
929                 .packet_type     = AM7XXX_PACKET_TYPE_POWER,
930                 .direction       = AM7XXX_DIRECTION_OUT,
931                 .header_data_len = sizeof(struct am7xxx_power_header),
932                 .unknown2        = 0x3e,
933                 .unknown3        = 0x10,
934         };
935
936         switch(power) {
937         case AM7XXX_POWER_OFF:
938                 h.header_data.power.bit2 = 0;
939                 h.header_data.power.bit1 = 0;
940                 h.header_data.power.bit0 = 0;
941                 break;
942
943         case AM7XXX_POWER_LOW:
944                 h.header_data.power.bit2 = 0;
945                 h.header_data.power.bit1 = 0;
946                 h.header_data.power.bit0 = 1;
947                 break;
948
949         case AM7XXX_POWER_MIDDLE:
950                 h.header_data.power.bit2 = 0;
951                 h.header_data.power.bit1 = 1;
952                 h.header_data.power.bit0 = 0;
953                 break;
954
955         case AM7XXX_POWER_HIGH:
956                 h.header_data.power.bit2 = 0;
957                 h.header_data.power.bit1 = 1;
958                 h.header_data.power.bit0 = 1;
959                 break;
960
961         case AM7XXX_POWER_TURBO:
962                 h.header_data.power.bit2 = 1;
963                 h.header_data.power.bit1 = 0;
964                 h.header_data.power.bit0 = 0;
965                 break;
966
967         default:
968                 error(dev->ctx, "Unsupported power mode.\n");
969                 return -EINVAL;
970         };
971
972         ret = send_header(dev, &h);
973         if (ret < 0)
974                 return ret;
975
976         return 0;
977 }
978
979 AM7XXX_PUBLIC int am7xxx_set_zoom_mode(am7xxx_device *dev, am7xxx_zoom_mode zoom)
980 {
981         int ret;
982         struct am7xxx_header h = {
983                 .packet_type     = AM7XXX_PACKET_TYPE_ZOOM,
984                 .direction       = AM7XXX_DIRECTION_OUT,
985                 .header_data_len = sizeof(struct am7xxx_zoom_header),
986                 .unknown2        = 0x3e,
987                 .unknown3        = 0x10,
988         };
989
990         switch(zoom) {
991         case AM7XXX_ZOOM_ORIGINAL:
992                 h.header_data.zoom.bit1 = 0;
993                 h.header_data.zoom.bit0 = 0;
994                 break;
995
996         case AM7XXX_ZOOM_H:
997                 h.header_data.zoom.bit1 = 0;
998                 h.header_data.zoom.bit0 = 1;
999                 break;
1000
1001         case AM7XXX_ZOOM_H_V:
1002                 h.header_data.zoom.bit1 = 1;
1003                 h.header_data.zoom.bit0 = 0;
1004                 break;
1005
1006         case AM7XXX_ZOOM_TEST:
1007                 h.header_data.zoom.bit1 = 1;
1008                 h.header_data.zoom.bit0 = 1;
1009                 break;
1010
1011         default:
1012                 error(dev->ctx, "Unsupported zoom mode.\n");
1013                 return -EINVAL;
1014         };
1015
1016         ret = send_header(dev, &h);
1017         if (ret < 0)
1018                 return ret;
1019
1020         return 0;
1021 }