am7xxx: add quirks for Philips/Sagemcom PicoPix 2055
[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 #define AM7XXX_QUIRK_NO_POWER_MODE (1 << 0)
68 #define AM7XXX_QUIRK_NO_ZOOM_MODE  (1 << 1)
69
70 struct am7xxx_usb_device_descriptor {
71         const char *name;
72         uint16_t vendor_id;
73         uint16_t product_id;
74         uint8_t configuration;    /* The bConfigurationValue of the device */
75         uint8_t interface_number; /* The bInterfaceNumber of the device */
76         unsigned long quirks;
77 };
78
79 static const struct am7xxx_usb_device_descriptor supported_devices[] = {
80         {
81                 .name       = "Acer C110",
82                 .vendor_id  = 0x1de1,
83                 .product_id = 0xc101,
84                 .configuration    = 2,
85                 .interface_number = 0,
86         },
87         {
88                 .name       = "Acer C112",
89                 .vendor_id  = 0x1de1,
90                 .product_id = 0x5501,
91                 .configuration    = 2,
92                 .interface_number = 0,
93         },
94         {
95                 .name       ="Aiptek PocketCinema T25",
96                 .vendor_id  = 0x08ca,
97                 .product_id = 0x2144,
98                 .configuration    = 2,
99                 .interface_number = 0,
100         },
101         {
102                 .name       = "Philips/Sagemcom PicoPix 1020",
103                 .vendor_id  = 0x21e7,
104                 .product_id = 0x000e,
105                 .configuration    = 2,
106                 .interface_number = 0,
107         },
108         {
109                 .name       = "Philips/Sagemcom PicoPix 2055",
110                 .vendor_id  = 0x21e7,
111                 .product_id = 0x0016,
112                 .configuration    = 2,
113                 .interface_number = 0,
114                 .quirks = AM7XXX_QUIRK_NO_POWER_MODE | AM7XXX_QUIRK_NO_ZOOM_MODE,
115         },
116         {
117                 .name       = "Philips/Sagemcom PicoPix 2330",
118                 .vendor_id  = 0x21e7,
119                 .product_id = 0x0019,
120                 .configuration    = 1,
121                 .interface_number = 0,
122                 .quirks = AM7XXX_QUIRK_NO_POWER_MODE | AM7XXX_QUIRK_NO_ZOOM_MODE,
123         },
124 };
125
126 /* The header size on the wire is known to be always 24 bytes, regardless of
127  * the memory configuration enforced by different architectures or compilers
128  * for struct am7xxx_header
129  */
130 #define AM7XXX_HEADER_WIRE_SIZE 24
131
132 struct _am7xxx_device {
133         libusb_device_handle *usb_device;
134         struct libusb_transfer *transfer;
135         int transfer_completed;
136         uint8_t buffer[AM7XXX_HEADER_WIRE_SIZE];
137         am7xxx_device_info *device_info;
138         am7xxx_context *ctx;
139         const struct am7xxx_usb_device_descriptor *desc;
140         am7xxx_device *next;
141 };
142
143 struct _am7xxx_context {
144         libusb_context *usb_context;
145         int log_level;
146         am7xxx_device *devices_list;
147 };
148
149 typedef enum {
150         AM7XXX_PACKET_TYPE_DEVINFO = 0x01,
151         AM7XXX_PACKET_TYPE_IMAGE   = 0x02,
152         AM7XXX_PACKET_TYPE_POWER   = 0x04,
153         AM7XXX_PACKET_TYPE_ZOOM    = 0x05,
154 } am7xxx_packet_type;
155
156 struct am7xxx_generic_header {
157         uint32_t field0;
158         uint32_t field1;
159         uint32_t field2;
160         uint32_t field3;
161 };
162
163 struct am7xxx_devinfo_header {
164         uint32_t native_width;
165         uint32_t native_height;
166         uint32_t unknown0;
167         uint32_t unknown1;
168 };
169
170 struct am7xxx_image_header {
171         uint32_t format;
172         uint32_t width;
173         uint32_t height;
174         uint32_t image_size;
175 };
176
177 struct am7xxx_power_header {
178         uint32_t bit2;
179         uint32_t bit1;
180         uint32_t bit0;
181 };
182
183 struct am7xxx_zoom_header {
184         uint32_t bit1;
185         uint32_t bit0;
186 };
187
188 /*
189  * Examples of packet headers:
190  *
191  * Image header:
192  * 02 00 00 00 00 10 3e 10 01 00 00 00 20 03 00 00 e0 01 00 00 53 E8 00 00
193  *
194  * Power header:
195  * 04 00 00 00 00 0c ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
196  */
197
198 /* Direction of the communication from the host point of view */
199 #define AM7XXX_DIRECTION_OUT 0 /* host -> device */
200 #define AM7XXX_DIRECTION_IN  1 /* host <- device */
201
202 struct am7xxx_header {
203         uint32_t packet_type;
204         uint8_t direction;
205         uint8_t header_data_len;
206         uint8_t unknown2;
207         uint8_t unknown3;
208         union {
209                 struct am7xxx_generic_header data;
210                 struct am7xxx_devinfo_header devinfo;
211                 struct am7xxx_image_header image;
212                 struct am7xxx_power_header power;
213                 struct am7xxx_zoom_header zoom;
214         } header_data;
215 };
216
217
218 #ifdef DEBUG
219 static void debug_dump_devinfo_header(am7xxx_context *ctx, struct am7xxx_devinfo_header *d)
220 {
221         if (ctx == NULL || d == NULL)
222                 return;
223
224         debug(ctx, "Info header:\n");
225         debug(ctx, "\tnative_width:  0x%08x (%u)\n", d->native_width, d->native_width);
226         debug(ctx, "\tnative_height: 0x%08x (%u)\n", d->native_height, d->native_height);
227         debug(ctx, "\tunknown0:      0x%08x (%u)\n", d->unknown0, d->unknown0);
228         debug(ctx, "\tunknown1:      0x%08x (%u)\n", d->unknown1, d->unknown1);
229 }
230
231 static void debug_dump_image_header(am7xxx_context *ctx, struct am7xxx_image_header *i)
232 {
233         if (ctx == NULL || i == NULL)
234                 return;
235
236         debug(ctx, "Image header:\n");
237         debug(ctx, "\tformat:     0x%08x (%u)\n", i->format, i->format);
238         debug(ctx, "\twidth:      0x%08x (%u)\n", i->width, i->width);
239         debug(ctx, "\theight:     0x%08x (%u)\n", i->height, i->height);
240         debug(ctx, "\timage size: 0x%08x (%u)\n", i->image_size, i->image_size);
241 }
242
243 static void debug_dump_power_header(am7xxx_context *ctx, struct am7xxx_power_header *p)
244 {
245         if (ctx == NULL || p == NULL)
246                 return;
247
248         debug(ctx, "Power header:\n");
249         debug(ctx, "\tbit2: 0x%08x (%u)\n", p->bit2, p->bit2);
250         debug(ctx, "\tbit1: 0x%08x (%u)\n", p->bit1, p->bit1);
251         debug(ctx, "\tbit0: 0x%08x (%u)\n", p->bit0, p->bit0);
252 }
253
254 static void debug_dump_zoom_header(am7xxx_context *ctx, struct am7xxx_zoom_header *z)
255 {
256         if (ctx == NULL || z == NULL)
257                 return;
258
259         debug(ctx, "Zoom header:\n");
260         debug(ctx, "\tbit1: 0x%08x (%u)\n", z->bit1, z->bit1);
261         debug(ctx, "\tbit0: 0x%08x (%u)\n", z->bit0, z->bit0);
262 }
263
264 static void debug_dump_header(am7xxx_context *ctx, struct am7xxx_header *h)
265 {
266         if (ctx == NULL || h == NULL)
267                 return;
268
269         debug(ctx, "BEGIN\n");
270         debug(ctx, "packet_type:     0x%08x (%u)\n", h->packet_type, h->packet_type);
271         debug(ctx, "direction:       0x%02hhx (%hhu) (%s)\n", h->direction, h->direction,
272               h->direction == AM7XXX_DIRECTION_IN ? "IN" :
273               h->direction == AM7XXX_DIRECTION_OUT ? "OUT" :
274               "UNKNOWN");
275         debug(ctx, "header_data_len: 0x%02hhx (%hhu)\n", h->header_data_len, h->header_data_len);
276         debug(ctx, "unknown2:        0x%02hhx (%hhu)\n", h->unknown2, h->unknown2);
277         debug(ctx, "unknown3:        0x%02hhx (%hhu)\n", h->unknown3, h->unknown3);
278
279         switch(h->packet_type) {
280         case AM7XXX_PACKET_TYPE_DEVINFO:
281                 debug_dump_devinfo_header(ctx, &(h->header_data.devinfo));
282                 break;
283
284         case AM7XXX_PACKET_TYPE_IMAGE:
285                 debug_dump_image_header(ctx, &(h->header_data.image));
286                 break;
287
288         case AM7XXX_PACKET_TYPE_POWER:
289                 debug_dump_power_header(ctx, &(h->header_data.power));
290                 break;
291
292         case AM7XXX_PACKET_TYPE_ZOOM:
293                 debug_dump_zoom_header(ctx, &(h->header_data.zoom));
294                 break;
295
296         default:
297                 debug(ctx, "Packet type not supported!\n");
298                 break;
299         }
300         debug(ctx, "END\n\n");
301 }
302
303 static inline unsigned int in_80chars(unsigned int i)
304 {
305         /* The 3 below is the length of "xx " where xx is the hex string
306          * representation of a byte */
307         return ((i+1) % (80/3));
308 }
309
310 static void trace_dump_buffer(am7xxx_context *ctx, const char *message,
311                               uint8_t *buffer, unsigned int len)
312 {
313         unsigned int i;
314
315         if (ctx == NULL || buffer == NULL || len == 0)
316                 return;
317
318         trace(ctx, "\n");
319         if (message)
320                 trace(ctx, "%s\n", message);
321
322         for (i = 0; i < len; i++) {
323                 trace(ctx, "%02hhX%c", buffer[i], (in_80chars(i) && (i < len - 1)) ? ' ' : '\n');
324         }
325         trace(ctx, "\n");
326 }
327 #else
328 static void debug_dump_header(am7xxx_context *ctx, struct am7xxx_header *h)
329 {
330         (void)ctx;
331         (void)h;
332 }
333
334 static void trace_dump_buffer(am7xxx_context *ctx, const char *message,
335                               uint8_t *buffer, unsigned int len)
336 {
337         (void)ctx;
338         (void)message;
339         (void)buffer;
340         (void)len;
341 }
342 #endif /* DEBUG */
343
344 static int read_data(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
345 {
346         int ret;
347         int transferred = 0;
348
349         ret = libusb_bulk_transfer(dev->usb_device, 0x81, buffer, len, &transferred, 0);
350         if (ret != 0 || (unsigned int)transferred != len) {
351                 error(dev->ctx, "ret: %d\ttransferred: %d (expected %u)\n",
352                       ret, transferred, len);
353                 return ret;
354         }
355
356         trace_dump_buffer(dev->ctx, "<-- received", buffer, len);
357
358         return 0;
359 }
360
361 static int send_data(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
362 {
363         int ret;
364         int transferred = 0;
365
366         trace_dump_buffer(dev->ctx, "sending -->", buffer, len);
367
368         ret = libusb_bulk_transfer(dev->usb_device, 0x1, buffer, len, &transferred, 0);
369         if (ret != 0 || (unsigned int)transferred != len) {
370                 error(dev->ctx, "ret: %d\ttransferred: %d (expected %u)\n",
371                       ret, transferred, len);
372                 return ret;
373         }
374
375         return 0;
376 }
377
378 static void send_data_async_complete_cb(struct libusb_transfer *transfer)
379 {
380         am7xxx_device *dev = (am7xxx_device *)(transfer->user_data);
381         int *completed = &(dev->transfer_completed);
382         int transferred = transfer->actual_length;
383         int ret;
384
385         if (transferred != transfer->length) {
386                 error(dev->ctx, "transferred: %d (expected %u)\n",
387                       transferred, transfer->length);
388         }
389
390         switch (transfer->status) {
391         case LIBUSB_TRANSFER_COMPLETED:
392                 ret = 0;
393                 break;
394         case LIBUSB_TRANSFER_TIMED_OUT:
395                 ret = LIBUSB_ERROR_TIMEOUT;
396                 break;
397         case LIBUSB_TRANSFER_STALL:
398                 ret = LIBUSB_ERROR_PIPE;
399                 break;
400         case LIBUSB_TRANSFER_OVERFLOW:
401                 ret = LIBUSB_ERROR_OVERFLOW;
402                 break;
403         case LIBUSB_TRANSFER_NO_DEVICE:
404                 ret = LIBUSB_ERROR_NO_DEVICE;
405                 break;
406         case LIBUSB_TRANSFER_ERROR:
407         case LIBUSB_TRANSFER_CANCELLED:
408                 ret = LIBUSB_ERROR_IO;
409                 break;
410         default:
411                 error(dev->ctx, "unrecognised status code %d", transfer->status);
412                 ret = LIBUSB_ERROR_OTHER;
413         }
414
415         if (ret < 0)
416                 error(dev->ctx, "libusb transfer failed: %s",
417                       libusb_error_name(ret));
418
419         libusb_free_transfer(transfer);
420         transfer = NULL;
421
422         *completed = 1;
423 }
424
425 static inline void wait_for_trasfer_completed(am7xxx_device *dev)
426 {
427         while (!dev->transfer_completed) {
428                 int ret = libusb_handle_events_completed(dev->ctx->usb_context,
429                                                          &(dev->transfer_completed));
430                 if (ret < 0) {
431                         if (ret == LIBUSB_ERROR_INTERRUPTED)
432                                 continue;
433                         error(dev->ctx, "libusb_handle_events failed: %s, cancelling transfer and retrying",
434                               libusb_error_name(ret));
435                         libusb_cancel_transfer(dev->transfer);
436                         continue;
437                 }
438         }
439 }
440
441 static int send_data_async(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
442 {
443         int ret;
444         uint8_t *transfer_buffer;
445
446         dev->transfer = libusb_alloc_transfer(0);
447         if (dev->transfer == NULL) {
448                 error(dev->ctx, "cannot allocate transfer (%s)\n",
449                       strerror(errno));
450                 return -ENOMEM;
451         }
452
453         /* Make a copy of the buffer so the caller can safely reuse it just
454          * after libusb_submit_transfer() has returned. This technique
455          * requires more allocations than a proper double-buffering approach
456          * but it takes a lot less code. */
457         transfer_buffer = malloc(len);
458         if (transfer_buffer == NULL) {
459                 error(dev->ctx, "cannot allocate transfer buffer (%s)\n",
460                       strerror(errno));
461                 ret = -ENOMEM;
462                 goto err;
463         }
464         memcpy(transfer_buffer, buffer, len);
465
466         dev->transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
467         libusb_fill_bulk_transfer(dev->transfer, dev->usb_device, 0x1,
468                                   transfer_buffer, len,
469                                   send_data_async_complete_cb, dev, 0);
470
471         /* wait for the previous transfer to complete */
472         wait_for_trasfer_completed(dev);
473
474         trace_dump_buffer(dev->ctx, "sending -->", buffer, len);
475
476         dev->transfer_completed = 0;
477         ret = libusb_submit_transfer(dev->transfer);
478         if (ret < 0)
479                 goto err;
480
481         return 0;
482
483 err:
484         libusb_free_transfer(dev->transfer);
485         dev->transfer = NULL;
486         return ret;
487 }
488
489 static void serialize_header(struct am7xxx_header *h, uint8_t *buffer)
490 {
491         uint8_t **buffer_iterator = &buffer;
492
493         put_le32(h->packet_type, buffer_iterator);
494         put_8(h->direction, buffer_iterator);
495         put_8(h->header_data_len, buffer_iterator);
496         put_8(h->unknown2, buffer_iterator);
497         put_8(h->unknown3, buffer_iterator);
498         put_le32(h->header_data.data.field0, buffer_iterator);
499         put_le32(h->header_data.data.field1, buffer_iterator);
500         put_le32(h->header_data.data.field2, buffer_iterator);
501         put_le32(h->header_data.data.field3, buffer_iterator);
502 }
503
504 static void unserialize_header(uint8_t *buffer, struct am7xxx_header *h)
505 {
506         uint8_t **buffer_iterator = &buffer;
507
508         h->packet_type = get_le32(buffer_iterator);
509         h->direction = get_8(buffer_iterator);
510         h->header_data_len = get_8(buffer_iterator);
511         h->unknown2 = get_8(buffer_iterator);
512         h->unknown3 = get_8(buffer_iterator);
513         h->header_data.data.field0 = get_le32(buffer_iterator);
514         h->header_data.data.field1 = get_le32(buffer_iterator);
515         h->header_data.data.field2 = get_le32(buffer_iterator);
516         h->header_data.data.field3 = get_le32(buffer_iterator);
517 }
518
519 static int read_header(am7xxx_device *dev, struct am7xxx_header *h)
520 {
521         int ret;
522
523         ret = read_data(dev, dev->buffer, AM7XXX_HEADER_WIRE_SIZE);
524         if (ret < 0)
525                 goto out;
526
527         unserialize_header(dev->buffer, h);
528
529         if (h->direction == AM7XXX_DIRECTION_IN) {
530                 ret = 0;
531         } else {
532                 error(dev->ctx,
533                       "Expected an AM7XXX_DIRECTION_IN packet, got one with direction = %d. Weird!\n",
534                       h->direction);
535                 ret = -EINVAL;
536         }
537
538         debug_dump_header(dev->ctx, h);
539
540 out:
541         return ret;
542 }
543
544 static int send_header(am7xxx_device *dev, struct am7xxx_header *h)
545 {
546         int ret;
547
548         debug_dump_header(dev->ctx, h);
549
550         /* For symmetry with read_header() we should check here for
551          * h->direction == AM7XXX_DIRECTION_OUT but we just ensure that in all
552          * the callers and save some cycles here.
553          */
554
555         serialize_header(h, dev->buffer);
556
557         ret = send_data(dev, dev->buffer, AM7XXX_HEADER_WIRE_SIZE);
558         if (ret < 0)
559                 error(dev->ctx, "failed to send data\n");
560
561         return ret;
562 }
563
564 /* When level == AM7XXX_LOG_FATAL do not check the log_level from the context
565  * and print the message unconditionally, this makes it possible to print
566  * fatal messages even early on initialization, before the context has been
567  * set up */
568 static void log_message(am7xxx_context *ctx,
569                         int level,
570                         const char *function,
571                         int line,
572                         const char *fmt,
573                         ...)
574 {
575         va_list ap;
576
577         if (level == AM7XXX_LOG_FATAL || (ctx && level <= ctx->log_level)) {
578                 if (function) {
579                         fprintf(stderr, "%s", function);
580                         if (line)
581                                 fprintf(stderr, "[%d]", line);
582                         fprintf(stderr, ": ");
583                 }
584
585                 va_start(ap, fmt);
586                 vfprintf(stderr, fmt, ap);
587                 va_end(ap);
588         }
589
590         return;
591 }
592
593 static am7xxx_device *add_new_device(am7xxx_context *ctx,
594                                      const struct am7xxx_usb_device_descriptor *desc)
595 {
596         am7xxx_device **devices_list;
597         am7xxx_device *new_device;
598
599         if (ctx == NULL) {
600                 fatal("context must not be NULL!\n");
601                 return NULL;
602         }
603
604         new_device = malloc(sizeof(*new_device));
605         if (new_device == NULL) {
606                 fatal("cannot allocate a new device (%s)\n", strerror(errno));
607                 return NULL;
608         }
609         memset(new_device, 0, sizeof(*new_device));
610
611         new_device->ctx = ctx;
612         new_device->desc = desc;
613         new_device->transfer_completed = 1;
614
615         devices_list = &(ctx->devices_list);
616
617         if (*devices_list == NULL) {
618                 *devices_list = new_device;
619         } else {
620                 am7xxx_device *prev = *devices_list;
621                 while (prev->next)
622                         prev = prev->next;
623                 prev->next = new_device;
624         }
625         return new_device;
626 }
627
628 static am7xxx_device *find_device(am7xxx_context *ctx,
629                                   unsigned int device_index)
630 {
631         unsigned int i = 0;
632         am7xxx_device *current;
633
634         if (ctx == NULL) {
635                 fatal("context must not be NULL!\n");
636                 return NULL;
637         }
638
639         current = ctx->devices_list;
640         while (current && i++ < device_index)
641                 current = current->next;
642
643         return current;
644 }
645
646 typedef enum {
647         SCAN_OP_BUILD_DEVLIST,
648         SCAN_OP_OPEN_DEVICE,
649 } scan_op;
650
651 /**
652  * This is where the central logic of multi-device support is.
653  *
654  * When 'op' == SCAN_OP_BUILD_DEVLIST the parameters 'open_device_index' and
655  * 'dev' are ignored; the function returns 0 on success and a negative value
656  * on error.
657  *
658  * When 'op' == SCAN_OP_OPEN_DEVICE the function opens the supported USB
659  * device with index 'open_device_index' and returns the correspondent
660  * am7xxx_device in the 'dev' parameter; the function returns 0 on success,
661  * 1 if the device was already open and a negative value on error.
662  *
663  * NOTES:
664  * if scan_devices() fails when called with 'op' == SCAN_OP_BUILD_DEVLIST,
665  * the caller might want to call am7xxx_shutdown() in order to remove
666  * devices possibly added before the failure.
667  */
668 static int scan_devices(am7xxx_context *ctx, scan_op op,
669                         unsigned int open_device_index, am7xxx_device **dev)
670 {
671         ssize_t num_devices;
672         libusb_device** list;
673         unsigned int current_index;
674         int i;
675         int ret;
676
677         if (ctx == NULL) {
678                 fatal("context must not be NULL!\n");
679                 return -EINVAL;
680         }
681         if (op == SCAN_OP_BUILD_DEVLIST && ctx->devices_list != NULL) {
682                 error(ctx, "device scan done already? Abort!\n");
683                 return -EINVAL;
684         }
685
686         num_devices = libusb_get_device_list(ctx->usb_context, &list);
687         if (num_devices < 0) {
688                 ret = -ENODEV;
689                 goto out;
690         }
691
692         current_index = 0;
693         for (i = 0; i < num_devices; i++) {
694                 struct libusb_device_descriptor desc;
695                 unsigned int j;
696
697                 ret = libusb_get_device_descriptor(list[i], &desc);
698                 if (ret < 0)
699                         continue;
700
701                 for (j = 0; j < ARRAY_SIZE(supported_devices); j++) {
702                         if (desc.idVendor == supported_devices[j].vendor_id &&
703                             desc.idProduct == supported_devices[j].product_id) {
704
705                                 if (op == SCAN_OP_BUILD_DEVLIST) {
706                                         am7xxx_device *new_device;
707                                         info(ctx, "am7xxx device found, index: %d, name: %s\n",
708                                              current_index,
709                                              supported_devices[j].name);
710                                         new_device = add_new_device(ctx, &supported_devices[j]);
711                                         if (new_device == NULL) {
712                                                 /* XXX, the caller may want
713                                                  * to call am7xxx_shutdown() if
714                                                  * we fail here, as we may have
715                                                  * added some devices already
716                                                  */
717                                                 debug(ctx, "Cannot create a new device\n");
718                                                 ret = -ENODEV;
719                                                 goto out;
720                                         }
721                                 } else if (op == SCAN_OP_OPEN_DEVICE &&
722                                            current_index == open_device_index) {
723
724                                         *dev = find_device(ctx, open_device_index);
725                                         if (*dev == NULL) {
726                                                 ret = -ENODEV;
727                                                 goto out;
728                                         }
729
730                                         /* the usb device has already been opened */
731                                         if ((*dev)->usb_device) {
732                                                 debug(ctx, "(*dev)->usb_device already set\n");
733                                                 ret = 1;
734                                                 goto out;
735                                         }
736
737                                         ret = libusb_open(list[i], &((*dev)->usb_device));
738                                         if (ret < 0) {
739                                                 debug(ctx, "libusb_open failed\n");
740                                                 goto out;
741                                         }
742
743                                         /* XXX, the device is now open, if any
744                                          * of the calls below fail we need to
745                                          * close it again before bailing out.
746                                          */
747
748                                         ret = libusb_set_configuration((*dev)->usb_device,
749                                                                        (*dev)->desc->configuration);
750                                         if (ret < 0) {
751                                                 debug(ctx, "libusb_set_configuration failed\n");
752                                                 debug(ctx, "Cannot set configuration %hhu\n",
753                                                       (*dev)->desc->configuration);
754                                                 goto out_libusb_close;
755                                         }
756
757                                         ret = libusb_claim_interface((*dev)->usb_device,
758                                                                      (*dev)->desc->interface_number);
759                                         if (ret < 0) {
760                                                 debug(ctx, "libusb_claim_interface failed\n");
761                                                 debug(ctx, "Cannot claim interface %hhu\n",
762                                                       (*dev)->desc->interface_number);
763 out_libusb_close:
764                                                 libusb_close((*dev)->usb_device);
765                                                 (*dev)->usb_device = NULL;
766                                                 goto out;
767                                         }
768
769                                         goto out;
770                                 }
771                                 current_index++;
772                         }
773                 }
774         }
775
776         /* if we made it up to here we didn't find any device to open */
777         if (op == SCAN_OP_OPEN_DEVICE) {
778                 error(ctx, "Cannot find any device to open\n");
779                 ret = -ENODEV;
780                 goto out;
781         }
782
783         /* everything went fine when building the device list */
784         ret = 0;
785 out:
786         libusb_free_device_list(list, 1);
787         return ret;
788 }
789
790 /* Public API */
791
792 AM7XXX_PUBLIC int am7xxx_init(am7xxx_context **ctx)
793 {
794         int ret;
795
796         *ctx = malloc(sizeof(**ctx));
797         if (*ctx == NULL) {
798                 fatal("cannot allocate the context (%s)\n", strerror(errno));
799                 ret = -ENOMEM;
800                 goto out;
801         }
802         memset(*ctx, 0, sizeof(**ctx));
803
804         /* Set the highest log level during initialization */
805         (*ctx)->log_level = AM7XXX_LOG_TRACE;
806
807         ret = libusb_init(&((*ctx)->usb_context));
808         if (ret < 0)
809                 goto out_free_context;
810
811         libusb_set_debug((*ctx)->usb_context, LIBUSB_LOG_LEVEL_INFO);
812
813         ret = scan_devices(*ctx, SCAN_OP_BUILD_DEVLIST , 0, NULL);
814         if (ret < 0) {
815                 error(*ctx, "scan_devices() failed\n");
816                 am7xxx_shutdown(*ctx);
817                 goto out;
818         }
819
820         /* Set a quieter log level as default for normal operation */
821         (*ctx)->log_level = AM7XXX_LOG_ERROR;
822         return 0;
823
824 out_free_context:
825         free(*ctx);
826         *ctx = NULL;
827 out:
828         return ret;
829 }
830
831 AM7XXX_PUBLIC void am7xxx_shutdown(am7xxx_context *ctx)
832 {
833         am7xxx_device *current;
834
835         if (ctx == NULL) {
836                 fatal("context must not be NULL!\n");
837                 return;
838         }
839
840         current = ctx->devices_list;
841         while (current) {
842                 am7xxx_device *next = current->next;
843                 am7xxx_close_device(current);
844                 free(current->device_info);
845                 free(current);
846                 current = next;
847         }
848
849         libusb_exit(ctx->usb_context);
850         free(ctx);
851         ctx = NULL;
852 }
853
854 AM7XXX_PUBLIC void am7xxx_set_log_level(am7xxx_context *ctx, am7xxx_log_level log_level)
855 {
856         ctx->log_level = log_level;
857 }
858
859 AM7XXX_PUBLIC int am7xxx_open_device(am7xxx_context *ctx, am7xxx_device **dev,
860                        unsigned int device_index)
861 {
862         int ret;
863
864         if (ctx == NULL) {
865                 fatal("context must not be NULL!\n");
866                 return -EINVAL;
867         }
868
869         ret = scan_devices(ctx, SCAN_OP_OPEN_DEVICE, device_index, dev);
870         if (ret < 0) {
871                 errno = ENODEV;
872                 goto out;
873         } else if (ret > 0) {
874                 warning(ctx, "device %d already open\n", device_index);
875                 errno = EBUSY;
876                 ret = -EBUSY;
877                 goto out;
878         }
879
880         /* Philips/Sagemcom PicoPix projectors require that the DEVINFO packet
881          * is the first one to be sent to the device in order for it to
882          * successfully return the correct device information.
883          *
884          * So, if there is not a cached version of it (from a previous open),
885          * we ask for device info at open time,
886          */
887         if ((*dev)->device_info == NULL) {
888                 ret = am7xxx_get_device_info(*dev, NULL);
889                 if (ret < 0)
890                         error(ctx, "cannot get device info\n");
891         }
892
893 out:
894         return ret;
895 }
896
897 AM7XXX_PUBLIC int am7xxx_close_device(am7xxx_device *dev)
898 {
899         if (dev == NULL) {
900                 fatal("dev must not be NULL!\n");
901                 return -EINVAL;
902         }
903         if (dev->usb_device) {
904                 wait_for_trasfer_completed(dev);
905                 libusb_release_interface(dev->usb_device, dev->desc->interface_number);
906                 libusb_close(dev->usb_device);
907                 dev->usb_device = NULL;
908         }
909         return 0;
910 }
911
912 AM7XXX_PUBLIC int am7xxx_get_device_info(am7xxx_device *dev,
913                            am7xxx_device_info *device_info)
914 {
915         int ret;
916         struct am7xxx_header h = {
917                 .packet_type     = AM7XXX_PACKET_TYPE_DEVINFO,
918                 .direction       = AM7XXX_DIRECTION_OUT,
919                 .header_data_len = 0x00,
920                 .unknown2        = 0x3e,
921                 .unknown3        = 0x10,
922                 .header_data = {
923                         .devinfo = {
924                                 .native_width  = 0,
925                                 .native_height = 0,
926                                 .unknown0      = 0,
927                                 .unknown1      = 0,
928                         },
929                 },
930         };
931
932         if (dev->device_info) {
933                 memcpy(device_info, dev->device_info, sizeof(*device_info));
934                 return 0;
935         }
936
937         ret = send_header(dev, &h);
938         if (ret < 0)
939                 return ret;
940
941         ret = read_header(dev, &h);
942         if (ret < 0)
943                 return ret;
944
945         if (h.packet_type != AM7XXX_PACKET_TYPE_DEVINFO) {
946                 error(dev->ctx, "expected packet type: %d, got %d instead!\n",
947                       AM7XXX_PACKET_TYPE_DEVINFO, h.packet_type);
948                 errno = ENOTSUP;
949                 return -ENOTSUP;
950         }
951
952         dev->device_info = malloc(sizeof(*dev->device_info));
953         if (dev->device_info == NULL) {
954                 error(dev->ctx, "cannot allocate a device info (%s)\n",
955                        strerror(errno));
956                 return -ENOMEM;
957         }
958         memset(dev->device_info, 0, sizeof(*dev->device_info));
959
960         dev->device_info->native_width = h.header_data.devinfo.native_width;
961         dev->device_info->native_height = h.header_data.devinfo.native_height;
962 #if 0
963         /* No reason to expose these in the public API until we know what they mean */
964         dev->device_info->unknown0 = h.header_data.devinfo.unknown0;
965         dev->device_info->unknown1 = h.header_data.devinfo.unknown1;
966 #endif
967
968         return 0;
969 }
970
971 AM7XXX_PUBLIC int am7xxx_calc_scaled_image_dimensions(am7xxx_device *dev,
972                                         unsigned int upscale,
973                                         unsigned int original_width,
974                                         unsigned int original_height,
975                                         unsigned int *scaled_width,
976                                         unsigned int *scaled_height)
977 {
978
979         am7xxx_device_info device_info;
980         float width_ratio;
981         float height_ratio;
982         int ret;
983
984         ret = am7xxx_get_device_info(dev, &device_info);
985         if (ret < 0) {
986                 error(dev->ctx, "cannot get device info\n");
987                 return ret;
988         }
989
990         /*
991          * Check if we need to rescale; if the input image fits the native
992          * dimensions there is no need to, unless we want to upscale.
993          */
994         if (!upscale &&
995             original_width <= device_info.native_width &&
996             original_height <= device_info.native_height ) {
997                 debug(dev->ctx, "CASE 0, no rescaling, the original image fits already\n");
998                 *scaled_width = original_width;
999                 *scaled_height = original_height;
1000                 return 0;
1001         }
1002
1003         /* Input dimensions relative to the device native dimensions */
1004         width_ratio =  (float)original_width / device_info.native_width;
1005         height_ratio = (float)original_height / device_info.native_height;
1006
1007         if (width_ratio > height_ratio) {
1008                 /*
1009                  * The input is proportionally "wider" than the device viewport
1010                  * so its height needs to be adjusted
1011                  */
1012                 debug(dev->ctx, "CASE 1, original image wider, adjust the scaled height\n");
1013                 *scaled_width = device_info.native_width;
1014                 *scaled_height = (unsigned int)lroundf(original_height / width_ratio);
1015         } else if (width_ratio < height_ratio) {
1016                 /*
1017                  * The input is proportionally "taller" than the device viewport
1018                  * so its width needs to be adjusted
1019                  */
1020                 debug(dev->ctx, "CASE 2 original image taller, adjust the scaled width\n");
1021                 *scaled_width = (unsigned int)lroundf(original_width / height_ratio);
1022                 *scaled_height = device_info.native_height;
1023         } else {
1024                 debug(dev->ctx, "CASE 3, just rescale, same aspect ratio already\n");
1025                 *scaled_width = device_info.native_width;
1026                 *scaled_height = device_info.native_height;
1027         }
1028         debug(dev->ctx, "scaled dimensions: %dx%d\n", *scaled_width, *scaled_height);
1029
1030         return 0;
1031 }
1032
1033 AM7XXX_PUBLIC int am7xxx_send_image(am7xxx_device *dev,
1034                       am7xxx_image_format format,
1035                       unsigned int width,
1036                       unsigned int height,
1037                       uint8_t *image,
1038                       unsigned int image_size)
1039 {
1040         int ret;
1041         struct am7xxx_header h = {
1042                 .packet_type     = AM7XXX_PACKET_TYPE_IMAGE,
1043                 .direction       = AM7XXX_DIRECTION_OUT,
1044                 .header_data_len = sizeof(struct am7xxx_image_header),
1045                 .unknown2        = 0x3e,
1046                 .unknown3        = 0x10,
1047                 .header_data = {
1048                         .image = {
1049                                 .format     = format,
1050                                 .width      = width,
1051                                 .height     = height,
1052                                 .image_size = image_size,
1053                         },
1054                 },
1055         };
1056
1057         ret = send_header(dev, &h);
1058         if (ret < 0)
1059                 return ret;
1060
1061         if (image == NULL || image_size == 0) {
1062                 warning(dev->ctx, "Not sending any data, check the 'image' or 'image_size' parameters\n");
1063                 return 0;
1064         }
1065
1066         return send_data(dev, image, image_size);
1067 }
1068
1069 AM7XXX_PUBLIC int am7xxx_send_image_async(am7xxx_device *dev,
1070                                           am7xxx_image_format format,
1071                                           unsigned int width,
1072                                           unsigned int height,
1073                                           uint8_t *image,
1074                                           unsigned int image_size)
1075 {
1076         int ret;
1077         struct am7xxx_header h = {
1078                 .packet_type     = AM7XXX_PACKET_TYPE_IMAGE,
1079                 .direction       = AM7XXX_DIRECTION_OUT,
1080                 .header_data_len = sizeof(struct am7xxx_image_header),
1081                 .unknown2        = 0x3e,
1082                 .unknown3        = 0x10,
1083                 .header_data = {
1084                         .image = {
1085                                 .format     = format,
1086                                 .width      = width,
1087                                 .height     = height,
1088                                 .image_size = image_size,
1089                         },
1090                 },
1091         };
1092
1093         ret = send_header(dev, &h);
1094         if (ret < 0)
1095                 return ret;
1096
1097         if (image == NULL || image_size == 0) {
1098                 warning(dev->ctx, "Not sending any data, check the 'image' or 'image_size' parameters\n");
1099                 return 0;
1100         }
1101
1102         return send_data_async(dev, image, image_size);
1103 }
1104
1105 AM7XXX_PUBLIC int am7xxx_set_power_mode(am7xxx_device *dev, am7xxx_power_mode power)
1106 {
1107         int ret;
1108         struct am7xxx_header h = {
1109                 .packet_type     = AM7XXX_PACKET_TYPE_POWER,
1110                 .direction       = AM7XXX_DIRECTION_OUT,
1111                 .header_data_len = sizeof(struct am7xxx_power_header),
1112                 .unknown2        = 0x3e,
1113                 .unknown3        = 0x10,
1114         };
1115
1116         if (dev->desc->quirks & AM7XXX_QUIRK_NO_POWER_MODE) {
1117                 debug(dev->ctx,
1118                       "setting power mode is unsupported on this device\n");
1119                 return 0;
1120         }
1121
1122         switch(power) {
1123         case AM7XXX_POWER_OFF:
1124                 h.header_data.power.bit2 = 0;
1125                 h.header_data.power.bit1 = 0;
1126                 h.header_data.power.bit0 = 0;
1127                 break;
1128
1129         case AM7XXX_POWER_LOW:
1130                 h.header_data.power.bit2 = 0;
1131                 h.header_data.power.bit1 = 0;
1132                 h.header_data.power.bit0 = 1;
1133                 break;
1134
1135         case AM7XXX_POWER_MIDDLE:
1136                 h.header_data.power.bit2 = 0;
1137                 h.header_data.power.bit1 = 1;
1138                 h.header_data.power.bit0 = 0;
1139                 break;
1140
1141         case AM7XXX_POWER_HIGH:
1142                 h.header_data.power.bit2 = 0;
1143                 h.header_data.power.bit1 = 1;
1144                 h.header_data.power.bit0 = 1;
1145                 break;
1146
1147         case AM7XXX_POWER_TURBO:
1148                 h.header_data.power.bit2 = 1;
1149                 h.header_data.power.bit1 = 0;
1150                 h.header_data.power.bit0 = 0;
1151                 break;
1152
1153         default:
1154                 error(dev->ctx, "Unsupported power mode.\n");
1155                 return -EINVAL;
1156         };
1157
1158         ret = send_header(dev, &h);
1159         if (ret < 0)
1160                 return ret;
1161
1162         return 0;
1163 }
1164
1165 AM7XXX_PUBLIC int am7xxx_set_zoom_mode(am7xxx_device *dev, am7xxx_zoom_mode zoom)
1166 {
1167         int ret;
1168         struct am7xxx_header h = {
1169                 .packet_type     = AM7XXX_PACKET_TYPE_ZOOM,
1170                 .direction       = AM7XXX_DIRECTION_OUT,
1171                 .header_data_len = sizeof(struct am7xxx_zoom_header),
1172                 .unknown2        = 0x3e,
1173                 .unknown3        = 0x10,
1174         };
1175
1176         if (dev->desc->quirks & AM7XXX_QUIRK_NO_ZOOM_MODE) {
1177                 debug(dev->ctx,
1178                       "setting zoom mode is unsupported on this device\n");
1179                 return 0;
1180         }
1181
1182         switch(zoom) {
1183         case AM7XXX_ZOOM_ORIGINAL:
1184                 h.header_data.zoom.bit1 = 0;
1185                 h.header_data.zoom.bit0 = 0;
1186                 break;
1187
1188         case AM7XXX_ZOOM_H:
1189                 h.header_data.zoom.bit1 = 0;
1190                 h.header_data.zoom.bit0 = 1;
1191                 break;
1192
1193         case AM7XXX_ZOOM_H_V:
1194                 h.header_data.zoom.bit1 = 1;
1195                 h.header_data.zoom.bit0 = 0;
1196                 break;
1197
1198         case AM7XXX_ZOOM_TEST:
1199                 h.header_data.zoom.bit1 = 1;
1200                 h.header_data.zoom.bit0 = 1;
1201                 break;
1202
1203         default:
1204                 error(dev->ctx, "Unsupported zoom mode.\n");
1205                 return -EINVAL;
1206         };
1207
1208         ret = send_header(dev, &h);
1209         if (ret < 0)
1210                 return ret;
1211
1212         return 0;
1213 }