libam7xxx  0.1
Communication library for Actions Micro AM7XXX based USB projectors and DPFs
am7xxx.c
1 /* am7xxx - communication with AM7xxx based USB Pico Projectors and DPFs
2  *
3  * Copyright (C) 2012-2014 Antonio Ospite <ao2@ao2.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 #include "tools.h"
30 
31 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
32 
33 /* If we're not using GNU C, elide __attribute__
34  * taken from: http://unixwiz.net/techtips/gnu-c-attributes.html)
35  */
36 #ifndef __GNUC__
37 # define __attribute__(x) /* NOTHING */
38 #endif
39 
40 /* Control shared library symbols visibility */
41 #if defined _WIN32 || defined __CYGWIN__
42  #define AM7XXX_PUBLIC __declspec(dllexport)
43  #define AM7XXX_LOCAL
44 #else
45  #if __GNUC__ >= 4
46  #define AM7XXX_PUBLIC __attribute__ ((visibility ("default")))
47  #define AM7XXX_LOCAL __attribute__ ((visibility ("hidden")))
48  #else
49  #define AM7XXX_PUBLIC
50  #define AM7XXX_LOCAL
51  #endif
52 #endif
53 
54 static void log_message(am7xxx_context *ctx,
55  int level,
56  const char *function_name,
57  int line,
58  const char *fmt,
59  ...) __attribute__ ((format (printf, 5, 6)));
60 
61 #define fatal(...) log_message(NULL, AM7XXX_LOG_FATAL, __func__, __LINE__, __VA_ARGS__)
62 #define error(ctx, ...) log_message(ctx, AM7XXX_LOG_ERROR, __func__, __LINE__, __VA_ARGS__)
63 #define warning(ctx, ...) log_message(ctx, AM7XXX_LOG_WARNING, __func__, 0, __VA_ARGS__)
64 #define info(ctx, ...) log_message(ctx, AM7XXX_LOG_INFO, __func__, 0, __VA_ARGS__)
65 #define debug(ctx, ...) log_message(ctx, AM7XXX_LOG_DEBUG, __func__, 0, __VA_ARGS__)
66 #define trace(ctx, ...) log_message(ctx, AM7XXX_LOG_TRACE, NULL, 0, __VA_ARGS__)
67 
68 struct am7xxx_ops {
69  int (*set_power_mode)(am7xxx_device *dev, am7xxx_power_mode power);
70  int (*set_zoom_mode)(am7xxx_device *dev, am7xxx_zoom_mode zoom);
71 };
72 
73 struct am7xxx_usb_device_descriptor {
74  const char *name;
75  uint16_t vendor_id;
76  uint16_t product_id;
77  uint8_t configuration; /* The bConfigurationValue of the device */
78  uint8_t interface_number; /* The bInterfaceNumber of the device */
79  struct am7xxx_ops ops;
80 };
81 
82 static int default_set_power_mode(am7xxx_device *dev, am7xxx_power_mode power);
83 static int picopix_set_power_mode(am7xxx_device *dev, am7xxx_power_mode power);
84 static int default_set_zoom_mode(am7xxx_device *dev, am7xxx_zoom_mode zoom);
85 static int picopix_set_zoom_mode(am7xxx_device *dev, am7xxx_zoom_mode zoom);
86 
87 #define DEFAULT_OPS { \
88  .set_power_mode = default_set_power_mode, \
89  .set_zoom_mode = default_set_zoom_mode, \
90 }
91 
92 static const struct am7xxx_usb_device_descriptor supported_devices[] = {
93  {
94  .name = "Acer C110",
95  .vendor_id = 0x1de1,
96  .product_id = 0xc101,
97  .configuration = 2,
98  .interface_number = 0,
99  .ops = DEFAULT_OPS,
100  },
101  {
102  .name = "Acer C112",
103  .vendor_id = 0x1de1,
104  .product_id = 0x5501,
105  .configuration = 2,
106  .interface_number = 0,
107  .ops = DEFAULT_OPS,
108  },
109  {
110  .name ="Aiptek PocketCinema T25",
111  .vendor_id = 0x08ca,
112  .product_id = 0x2144,
113  .configuration = 2,
114  .interface_number = 0,
115  .ops = DEFAULT_OPS,
116  },
117  {
118  .name = "Philips/Sagemcom PicoPix 1020",
119  .vendor_id = 0x21e7,
120  .product_id = 0x000e,
121  .configuration = 2,
122  .interface_number = 0,
123  .ops = DEFAULT_OPS,
124  },
125  {
126  .name = "Philips/Sagemcom PicoPix 2055",
127  .vendor_id = 0x21e7,
128  .product_id = 0x0016,
129  .configuration = 2,
130  .interface_number = 0,
131  .ops = {
132  .set_power_mode = picopix_set_power_mode,
133  .set_zoom_mode = picopix_set_zoom_mode,
134  },
135  },
136  {
137  .name = "Philips/Sagemcom PicoPix 2330",
138  .vendor_id = 0x21e7,
139  .product_id = 0x0019,
140  .configuration = 1,
141  .interface_number = 0,
142  },
143 };
144 
145 /* The header size on the wire is known to be always 24 bytes, regardless of
146  * the memory configuration enforced by different architectures or compilers
147  * for struct am7xxx_header
148  */
149 #define AM7XXX_HEADER_WIRE_SIZE 24
150 
151 struct _am7xxx_device {
152  libusb_device_handle *usb_device;
153  struct libusb_transfer *transfer;
154  int transfer_completed;
155  uint8_t buffer[AM7XXX_HEADER_WIRE_SIZE];
156  am7xxx_device_info *device_info;
157  am7xxx_context *ctx;
158  const struct am7xxx_usb_device_descriptor *desc;
159  am7xxx_device *next;
160 };
161 
162 struct _am7xxx_context {
163  libusb_context *usb_context;
164  int log_level;
165  am7xxx_device *devices_list;
166 };
167 
168 typedef enum {
169  AM7XXX_PACKET_TYPE_DEVINFO = 0x01,
170  AM7XXX_PACKET_TYPE_IMAGE = 0x02,
171  AM7XXX_PACKET_TYPE_POWER = 0x04,
172  AM7XXX_PACKET_TYPE_ZOOM = 0x05,
173  AM7XXX_PACKET_TYPE_PICOPIX_POWER_LOW = 0x15,
174  AM7XXX_PACKET_TYPE_PICOPIX_POWER_MEDIUM = 0x16,
175  AM7XXX_PACKET_TYPE_PICOPIX_POWER_HIGH = 0x17,
176  AM7XXX_PACKET_TYPE_PICOPIX_ENABLE_TI = 0x18,
177  AM7XXX_PACKET_TYPE_PICOPIX_DISABLE_TI = 0x19,
178 } am7xxx_packet_type;
179 
180 struct am7xxx_generic_header {
181  uint32_t field0;
182  uint32_t field1;
183  uint32_t field2;
184  uint32_t field3;
185 };
186 
187 struct am7xxx_devinfo_header {
188  uint32_t native_width;
189  uint32_t native_height;
190  uint32_t unknown0;
191  uint32_t unknown1;
192 };
193 
194 struct am7xxx_image_header {
195  uint32_t format;
196  uint32_t width;
197  uint32_t height;
198  uint32_t image_size;
199 };
200 
201 struct am7xxx_power_header {
202  uint32_t bit2;
203  uint32_t bit1;
204  uint32_t bit0;
205 };
206 
207 struct am7xxx_zoom_header {
208  uint32_t bit1;
209  uint32_t bit0;
210 };
211 
212 /*
213  * Examples of packet headers:
214  *
215  * Image header:
216  * 02 00 00 00 00 10 3e 10 01 00 00 00 20 03 00 00 e0 01 00 00 53 E8 00 00
217  *
218  * Power header:
219  * 04 00 00 00 00 0c ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
220  */
221 
222 /* Direction of the communication from the host point of view */
223 #define AM7XXX_DIRECTION_OUT 0 /* host -> device */
224 #define AM7XXX_DIRECTION_IN 1 /* host <- device */
225 
226 struct am7xxx_header {
227  uint32_t packet_type;
228  uint8_t direction;
229  uint8_t header_data_len;
230  uint8_t unknown2;
231  uint8_t unknown3;
232  union {
233  struct am7xxx_generic_header data;
234  struct am7xxx_devinfo_header devinfo;
235  struct am7xxx_image_header image;
236  struct am7xxx_power_header power;
237  struct am7xxx_zoom_header zoom;
238  } header_data;
239 };
240 
241 
242 #ifdef DEBUG
243 static void debug_dump_generic_header(am7xxx_context *ctx, struct am7xxx_generic_header *g)
244 {
245  if (ctx == NULL || g == NULL)
246  return;
247 
248  debug(ctx, "Generic header:\n");
249  debug(ctx, "\tfield0: 0x%08x (%u)\n", g->field0, g->field0);
250  debug(ctx, "\tfield1: 0x%08x (%u)\n", g->field1, g->field1);
251  debug(ctx, "\tfield2: 0x%08x (%u)\n", g->field2, g->field2);
252  debug(ctx, "\tfield3: 0x%08x (%u)\n", g->field3, g->field3);
253 }
254 
255 static void debug_dump_devinfo_header(am7xxx_context *ctx, struct am7xxx_devinfo_header *d)
256 {
257  if (ctx == NULL || d == NULL)
258  return;
259 
260  debug(ctx, "Info header:\n");
261  debug(ctx, "\tnative_width: 0x%08x (%u)\n", d->native_width, d->native_width);
262  debug(ctx, "\tnative_height: 0x%08x (%u)\n", d->native_height, d->native_height);
263  debug(ctx, "\tunknown0: 0x%08x (%u)\n", d->unknown0, d->unknown0);
264  debug(ctx, "\tunknown1: 0x%08x (%u)\n", d->unknown1, d->unknown1);
265 }
266 
267 static void debug_dump_image_header(am7xxx_context *ctx, struct am7xxx_image_header *i)
268 {
269  if (ctx == NULL || i == NULL)
270  return;
271 
272  debug(ctx, "Image header:\n");
273  debug(ctx, "\tformat: 0x%08x (%u)\n", i->format, i->format);
274  debug(ctx, "\twidth: 0x%08x (%u)\n", i->width, i->width);
275  debug(ctx, "\theight: 0x%08x (%u)\n", i->height, i->height);
276  debug(ctx, "\timage size: 0x%08x (%u)\n", i->image_size, i->image_size);
277 }
278 
279 static void debug_dump_power_header(am7xxx_context *ctx, struct am7xxx_power_header *p)
280 {
281  if (ctx == NULL || p == NULL)
282  return;
283 
284  debug(ctx, "Power header:\n");
285  debug(ctx, "\tbit2: 0x%08x (%u)\n", p->bit2, p->bit2);
286  debug(ctx, "\tbit1: 0x%08x (%u)\n", p->bit1, p->bit1);
287  debug(ctx, "\tbit0: 0x%08x (%u)\n", p->bit0, p->bit0);
288 }
289 
290 static void debug_dump_zoom_header(am7xxx_context *ctx, struct am7xxx_zoom_header *z)
291 {
292  if (ctx == NULL || z == NULL)
293  return;
294 
295  debug(ctx, "Zoom header:\n");
296  debug(ctx, "\tbit1: 0x%08x (%u)\n", z->bit1, z->bit1);
297  debug(ctx, "\tbit0: 0x%08x (%u)\n", z->bit0, z->bit0);
298 }
299 
300 static void debug_dump_header(am7xxx_context *ctx, struct am7xxx_header *h)
301 {
302  if (ctx == NULL || h == NULL)
303  return;
304 
305  debug(ctx, "BEGIN\n");
306  debug(ctx, "packet_type: 0x%08x (%u)\n", h->packet_type, h->packet_type);
307  debug(ctx, "direction: 0x%02hhx (%hhu) (%s)\n", h->direction, h->direction,
308  h->direction == AM7XXX_DIRECTION_IN ? "IN" :
309  h->direction == AM7XXX_DIRECTION_OUT ? "OUT" :
310  "UNKNOWN");
311  debug(ctx, "header_data_len: 0x%02hhx (%hhu)\n", h->header_data_len, h->header_data_len);
312  debug(ctx, "unknown2: 0x%02hhx (%hhu)\n", h->unknown2, h->unknown2);
313  debug(ctx, "unknown3: 0x%02hhx (%hhu)\n", h->unknown3, h->unknown3);
314 
315  switch(h->packet_type) {
316  case AM7XXX_PACKET_TYPE_DEVINFO:
317  debug_dump_devinfo_header(ctx, &(h->header_data.devinfo));
318  break;
319 
320  case AM7XXX_PACKET_TYPE_IMAGE:
321  debug_dump_image_header(ctx, &(h->header_data.image));
322  break;
323 
324  case AM7XXX_PACKET_TYPE_POWER:
325  debug_dump_power_header(ctx, &(h->header_data.power));
326  break;
327 
328  case AM7XXX_PACKET_TYPE_ZOOM:
329  debug_dump_zoom_header(ctx, &(h->header_data.zoom));
330  break;
331 
332  default:
333  debug(ctx, "Parsing data not supported for this packet type!\n");
334  debug_dump_generic_header(ctx, &(h->header_data.data));
335  break;
336  }
337  debug(ctx, "END\n\n");
338 }
339 
340 static inline unsigned int in_80chars(unsigned int i)
341 {
342  /* The 3 below is the length of "xx " where xx is the hex string
343  * representation of a byte */
344  return ((i + 1) % (80 / 3));
345 }
346 
347 static void trace_dump_buffer(am7xxx_context *ctx, const char *message,
348  uint8_t *buffer, unsigned int len)
349 {
350  unsigned int i;
351 
352  if (ctx == NULL || buffer == NULL || len == 0)
353  return;
354 
355  trace(ctx, "\n");
356  if (message)
357  trace(ctx, "%s\n", message);
358 
359  for (i = 0; i < len; i++) {
360  trace(ctx, "%02hhX%c", buffer[i], (in_80chars(i) && (i < len - 1)) ? ' ' : '\n');
361  }
362  trace(ctx, "\n");
363 }
364 #else
365 static void debug_dump_header(am7xxx_context *ctx, struct am7xxx_header *h)
366 {
367  (void)ctx;
368  (void)h;
369 }
370 
371 static void trace_dump_buffer(am7xxx_context *ctx, const char *message,
372  uint8_t *buffer, unsigned int len)
373 {
374  (void)ctx;
375  (void)message;
376  (void)buffer;
377  (void)len;
378 }
379 #endif /* DEBUG */
380 
381 static int read_data(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
382 {
383  int ret;
384  int transferred;
385 
386  transferred = 0;
387  ret = libusb_bulk_transfer(dev->usb_device, 0x81, buffer, len, &transferred, 0);
388  if (ret != 0 || (unsigned int)transferred != len) {
389  error(dev->ctx, "%s. Transferred: %d (expected %u)\n",
390  libusb_error_name(ret), transferred, len);
391  return ret;
392  }
393 
394  trace_dump_buffer(dev->ctx, "<-- received", buffer, len);
395 
396  return 0;
397 }
398 
399 static int send_data(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
400 {
401  int ret;
402  int transferred;
403 
404  trace_dump_buffer(dev->ctx, "sending -->", buffer, len);
405 
406  transferred = 0;
407  ret = libusb_bulk_transfer(dev->usb_device, 0x1, buffer, len, &transferred, 0);
408  if (ret != 0 || (unsigned int)transferred != len) {
409  error(dev->ctx, "%s. Transferred: %d (expected %u)\n",
410  libusb_error_name(ret), transferred, len);
411  return ret;
412  }
413 
414  return 0;
415 }
416 
417 static void send_data_async_complete_cb(struct libusb_transfer *transfer)
418 {
419  am7xxx_device *dev = (am7xxx_device *)(transfer->user_data);
420  int *completed = &(dev->transfer_completed);
421  int transferred = transfer->actual_length;
422  int ret;
423 
424  if (transferred != transfer->length) {
425  error(dev->ctx, "transferred: %d (expected %u)\n",
426  transferred, transfer->length);
427  }
428 
429  switch (transfer->status) {
430  case LIBUSB_TRANSFER_COMPLETED:
431  ret = 0;
432  break;
433  case LIBUSB_TRANSFER_TIMED_OUT:
434  ret = LIBUSB_ERROR_TIMEOUT;
435  break;
436  case LIBUSB_TRANSFER_STALL:
437  ret = LIBUSB_ERROR_PIPE;
438  break;
439  case LIBUSB_TRANSFER_OVERFLOW:
440  ret = LIBUSB_ERROR_OVERFLOW;
441  break;
442  case LIBUSB_TRANSFER_NO_DEVICE:
443  ret = LIBUSB_ERROR_NO_DEVICE;
444  break;
445  case LIBUSB_TRANSFER_ERROR:
446  case LIBUSB_TRANSFER_CANCELLED:
447  ret = LIBUSB_ERROR_IO;
448  break;
449  default:
450  error(dev->ctx, "unrecognised status code %d", transfer->status);
451  ret = LIBUSB_ERROR_OTHER;
452  }
453 
454  if (ret < 0)
455  error(dev->ctx, "libusb transfer failed: %s",
456  libusb_error_name(ret));
457 
458  libusb_free_transfer(transfer);
459  transfer = NULL;
460 
461  *completed = 1;
462 }
463 
464 static inline void wait_for_trasfer_completed(am7xxx_device *dev)
465 {
466  while (!dev->transfer_completed) {
467  int ret = libusb_handle_events_completed(dev->ctx->usb_context,
468  &(dev->transfer_completed));
469  if (ret < 0) {
470  if (ret == LIBUSB_ERROR_INTERRUPTED)
471  continue;
472  error(dev->ctx, "libusb_handle_events failed: %s, cancelling transfer and retrying",
473  libusb_error_name(ret));
474  libusb_cancel_transfer(dev->transfer);
475  continue;
476  }
477  }
478 }
479 
480 static int send_data_async(am7xxx_device *dev, uint8_t *buffer, unsigned int len)
481 {
482  int ret;
483  uint8_t *transfer_buffer;
484 
485  dev->transfer = libusb_alloc_transfer(0);
486  if (dev->transfer == NULL) {
487  error(dev->ctx, "cannot allocate transfer (%s)\n",
488  strerror(errno));
489  return -ENOMEM;
490  }
491 
492  /* Make a copy of the buffer so the caller can safely reuse it just
493  * after libusb_submit_transfer() has returned. This technique
494  * requires more dynamic allocations compared to a proper
495  * double-buffering approach but it takes a lot less code. */
496  transfer_buffer = malloc(len);
497  if (transfer_buffer == NULL) {
498  error(dev->ctx, "cannot allocate transfer buffer (%s)\n",
499  strerror(errno));
500  ret = -ENOMEM;
501  goto err;
502  }
503  memcpy(transfer_buffer, buffer, len);
504 
505  dev->transfer->flags |= LIBUSB_TRANSFER_FREE_BUFFER;
506  libusb_fill_bulk_transfer(dev->transfer, dev->usb_device, 0x1,
507  transfer_buffer, len,
508  send_data_async_complete_cb, dev, 0);
509 
510  /* wait for the previous transfer to complete */
511  wait_for_trasfer_completed(dev);
512 
513  trace_dump_buffer(dev->ctx, "sending -->", buffer, len);
514 
515  dev->transfer_completed = 0;
516  ret = libusb_submit_transfer(dev->transfer);
517  if (ret < 0)
518  goto err;
519 
520  return 0;
521 
522 err:
523  libusb_free_transfer(dev->transfer);
524  dev->transfer = NULL;
525  return ret;
526 }
527 
528 static void serialize_header(struct am7xxx_header *h, uint8_t *buffer)
529 {
530  uint8_t **buffer_iterator = &buffer;
531 
532  put_le32(h->packet_type, buffer_iterator);
533  put_8(h->direction, buffer_iterator);
534  put_8(h->header_data_len, buffer_iterator);
535  put_8(h->unknown2, buffer_iterator);
536  put_8(h->unknown3, buffer_iterator);
537  put_le32(h->header_data.data.field0, buffer_iterator);
538  put_le32(h->header_data.data.field1, buffer_iterator);
539  put_le32(h->header_data.data.field2, buffer_iterator);
540  put_le32(h->header_data.data.field3, buffer_iterator);
541 }
542 
543 static void unserialize_header(uint8_t *buffer, struct am7xxx_header *h)
544 {
545  uint8_t **buffer_iterator = &buffer;
546 
547  h->packet_type = get_le32(buffer_iterator);
548  h->direction = get_8(buffer_iterator);
549  h->header_data_len = get_8(buffer_iterator);
550  h->unknown2 = get_8(buffer_iterator);
551  h->unknown3 = get_8(buffer_iterator);
552  h->header_data.data.field0 = get_le32(buffer_iterator);
553  h->header_data.data.field1 = get_le32(buffer_iterator);
554  h->header_data.data.field2 = get_le32(buffer_iterator);
555  h->header_data.data.field3 = get_le32(buffer_iterator);
556 }
557 
558 static int read_header(am7xxx_device *dev, struct am7xxx_header *h)
559 {
560  int ret;
561 
562  ret = read_data(dev, dev->buffer, AM7XXX_HEADER_WIRE_SIZE);
563  if (ret < 0)
564  goto out;
565 
566  unserialize_header(dev->buffer, h);
567 
568  if (h->direction == AM7XXX_DIRECTION_IN) {
569  ret = 0;
570  } else {
571  error(dev->ctx,
572  "Expected an AM7XXX_DIRECTION_IN packet, got one with direction = %d. Weird!\n",
573  h->direction);
574  ret = -EINVAL;
575  }
576 
577  debug_dump_header(dev->ctx, h);
578 
579 out:
580  return ret;
581 }
582 
583 static int send_header(am7xxx_device *dev, struct am7xxx_header *h)
584 {
585  int ret;
586 
587  debug_dump_header(dev->ctx, h);
588 
589  /* For symmetry with read_header() we should check here for
590  * h->direction == AM7XXX_DIRECTION_OUT but we just ensure that in all
591  * the callers and save some cycles here.
592  */
593 
594  serialize_header(h, dev->buffer);
595 
596  ret = send_data(dev, dev->buffer, AM7XXX_HEADER_WIRE_SIZE);
597  if (ret < 0)
598  error(dev->ctx, "failed to send data\n");
599 
600  return ret;
601 }
602 
603 static int send_command(am7xxx_device *dev, am7xxx_packet_type type)
604 {
605  struct am7xxx_header h = {
606  .packet_type = type,
607  .direction = AM7XXX_DIRECTION_OUT,
608  .header_data_len = 0x00,
609  .unknown2 = 0x3e,
610  .unknown3 = 0x10,
611  .header_data = {
612  .data = {
613  .field0 = 0,
614  .field1 = 0,
615  .field2 = 0,
616  .field3 = 0,
617  },
618  },
619  };
620 
621  return send_header(dev, &h);
622 }
623 
624 
625 /* When level == AM7XXX_LOG_FATAL do not check the log_level from the context
626  * and print the message unconditionally, this makes it possible to print
627  * fatal messages even early on initialization, before the context has been
628  * set up */
629 static void log_message(am7xxx_context *ctx,
630  int level,
631  const char *function_name,
632  int line,
633  const char *fmt,
634  ...)
635 {
636  va_list ap;
637 
638  if (level == AM7XXX_LOG_FATAL || (ctx && level <= ctx->log_level)) {
639  if (function_name) {
640  fprintf(stderr, "%s", function_name);
641  if (line)
642  fprintf(stderr, "[%d]", line);
643  fprintf(stderr, ": ");
644  }
645 
646  va_start(ap, fmt);
647  vfprintf(stderr, fmt, ap);
648  va_end(ap);
649  }
650 
651  return;
652 }
653 
654 static am7xxx_device *add_new_device(am7xxx_context *ctx,
655  const struct am7xxx_usb_device_descriptor *desc)
656 {
657  am7xxx_device **devices_list;
658  am7xxx_device *new_device;
659 
660  if (ctx == NULL) {
661  fatal("context must not be NULL!\n");
662  return NULL;
663  }
664 
665  new_device = malloc(sizeof(*new_device));
666  if (new_device == NULL) {
667  debug(ctx, "cannot allocate a new device (%s)\n", strerror(errno));
668  return NULL;
669  }
670  memset(new_device, 0, sizeof(*new_device));
671 
672  new_device->ctx = ctx;
673  new_device->desc = desc;
674  new_device->transfer_completed = 1;
675 
676  devices_list = &(ctx->devices_list);
677 
678  if (*devices_list == NULL) {
679  *devices_list = new_device;
680  } else {
681  am7xxx_device *prev = *devices_list;
682  while (prev->next)
683  prev = prev->next;
684  prev->next = new_device;
685  }
686  return new_device;
687 }
688 
689 static am7xxx_device *find_device(am7xxx_context *ctx,
690  unsigned int device_index)
691 {
692  unsigned int i = 0;
693  am7xxx_device *current;
694 
695  if (ctx == NULL) {
696  fatal("context must not be NULL!\n");
697  return NULL;
698  }
699 
700  current = ctx->devices_list;
701  while (current && i++ < device_index)
702  current = current->next;
703 
704  return current;
705 }
706 
707 static int open_device(am7xxx_context *ctx,
708  unsigned int device_index,
709  libusb_device *usb_dev,
710  am7xxx_device **dev)
711 {
712  int ret;
713  int current_configuration;
714 
715  *dev = find_device(ctx, device_index);
716  if (*dev == NULL) {
717  ret = -ENODEV;
718  goto out;
719  }
720 
721  /* the usb device has already been opened */
722  if ((*dev)->usb_device) {
723  ret = 1;
724  goto out;
725  }
726 
727  ret = libusb_open(usb_dev, &((*dev)->usb_device));
728  if (ret < 0) {
729  debug(ctx, "libusb_open failed: %s\n", libusb_error_name(ret));
730  goto out;
731  }
732 
733  /* XXX, the device is now open, if any of the calls below fail we need
734  * to close it again before bailing out.
735  */
736 
737  current_configuration = -1;
738  ret = libusb_get_configuration((*dev)->usb_device,
739  &current_configuration);
740  if (ret < 0) {
741  debug(ctx, "libusb_get_configuration failed: %s\n",
742  libusb_error_name(ret));
743  goto out_libusb_close;
744  }
745 
746  if (current_configuration != (*dev)->desc->configuration) {
747  /*
748  * In principle, before setting a new configuration, kernel
749  * drivers should be detached from _all_ interfaces; for
750  * example calling something like the following "invented"
751  * function _before_ setting the new configuration:
752  *
753  * libusb_detach_all_kernel_drivers((*dev)->usb_device);
754  *
755  * However, in practice, this is not necessary for most
756  * devices as they have only one configuration.
757  *
758  * When a device only has one configuration:
759  *
760  * - if there was a kernel driver bound to the device, it
761  * had already set the configuration and the call below
762  * will be skipped;
763  *
764  * - if no kernel driver was bound to the device, the call
765  * below will suceed.
766  */
767  ret = libusb_set_configuration((*dev)->usb_device,
768  (*dev)->desc->configuration);
769  if (ret < 0) {
770  debug(ctx, "libusb_set_configuration failed: %s\n",
771  libusb_error_name(ret));
772  debug(ctx, "Cannot set configuration %hhu\n",
773  (*dev)->desc->configuration);
774  goto out_libusb_close;
775  }
776  }
777 
778  libusb_set_auto_detach_kernel_driver((*dev)->usb_device, 1);
779 
780  ret = libusb_claim_interface((*dev)->usb_device,
781  (*dev)->desc->interface_number);
782  if (ret < 0) {
783  debug(ctx, "libusb_claim_interface failed: %s\n",
784  libusb_error_name(ret));
785  debug(ctx, "Cannot claim interface %hhu\n",
786  (*dev)->desc->interface_number);
787  goto out_libusb_close;
788  }
789 
790  /* Checking that the configuration has not changed, as suggested in
791  * http://libusb.sourceforge.net/api-1.0/caveats.html
792  */
793  current_configuration = -1;
794  ret = libusb_get_configuration((*dev)->usb_device,
795  &current_configuration);
796  if (ret < 0) {
797  debug(ctx, "libusb_get_configuration after claim failed: %s\n",
798  libusb_error_name(ret));
799  goto out_libusb_release_interface;
800  }
801 
802  if (current_configuration != (*dev)->desc->configuration) {
803  debug(ctx, "libusb configuration changed (expected: %hhu, current: %d\n",
804  (*dev)->desc->configuration, current_configuration);
805  ret = -EINVAL;
806  goto out_libusb_release_interface;
807  }
808 out:
809  return ret;
810 
811 out_libusb_release_interface:
812  libusb_release_interface((*dev)->usb_device,
813  (*dev)->desc->interface_number);
814 out_libusb_close:
815  libusb_close((*dev)->usb_device);
816  (*dev)->usb_device = NULL;
817  return ret;
818 }
819 
820 typedef enum {
821  SCAN_OP_BUILD_DEVLIST,
822  SCAN_OP_OPEN_DEVICE,
823 } scan_op;
824 
843 static int scan_devices(am7xxx_context *ctx, scan_op op,
844  unsigned int open_device_index, am7xxx_device **dev)
845 {
846  ssize_t num_devices;
847  libusb_device **list;
848  unsigned int current_index;
849  int i;
850  int ret;
851 
852  if (ctx == NULL) {
853  fatal("context must not be NULL!\n");
854  return -EINVAL;
855  }
856  if (op == SCAN_OP_BUILD_DEVLIST && ctx->devices_list != NULL) {
857  error(ctx, "device scan done already? Abort!\n");
858  return -EINVAL;
859  }
860 
861  num_devices = libusb_get_device_list(ctx->usb_context, &list);
862  if (num_devices < 0) {
863  ret = -ENODEV;
864  goto out;
865  }
866 
867  current_index = 0;
868  for (i = 0; i < num_devices; i++) {
869  struct libusb_device_descriptor desc;
870  unsigned int j;
871 
872  ret = libusb_get_device_descriptor(list[i], &desc);
873  if (ret < 0)
874  continue;
875 
876  for (j = 0; j < ARRAY_SIZE(supported_devices); j++) {
877  if (desc.idVendor == supported_devices[j].vendor_id &&
878  desc.idProduct == supported_devices[j].product_id) {
879 
880  if (op == SCAN_OP_BUILD_DEVLIST) {
881  am7xxx_device *new_device;
882  info(ctx, "am7xxx device found, index: %d, name: %s\n",
883  current_index,
884  supported_devices[j].name);
885  new_device = add_new_device(ctx, &supported_devices[j]);
886  if (new_device == NULL) {
887  /* XXX, the caller may want
888  * to call am7xxx_shutdown() if
889  * we fail here, as we may have
890  * added some devices already
891  */
892  debug(ctx, "Cannot create a new device\n");
893  ret = -ENODEV;
894  goto out;
895  }
896  } else if (op == SCAN_OP_OPEN_DEVICE &&
897  current_index == open_device_index) {
898 
899  ret = open_device(ctx,
900  open_device_index,
901  list[i],
902  dev);
903  if (ret < 0)
904  debug(ctx, "open_device failed\n");
905 
906  /* exit the loop unconditionally after
907  * attempting to open the device
908  * requested by the user */
909  goto out;
910  }
911  current_index++;
912  }
913  }
914  }
915 
916  /* if we made it up to here when op == SCAN_OP_OPEN_DEVICE,
917  * no devices to open had been found. */
918  if (op == SCAN_OP_OPEN_DEVICE) {
919  error(ctx, "Cannot find any device to open\n");
920  ret = -ENODEV;
921  goto out;
922  }
923 
924  /* everything went fine when building the device list */
925  ret = 0;
926 out:
927  libusb_free_device_list(list, 1);
928  return ret;
929 }
930 
931 /* Device specific operations */
932 
933 static int default_set_power_mode(am7xxx_device *dev, am7xxx_power_mode power)
934 {
935  int ret;
936  struct am7xxx_header h = {
937  .packet_type = AM7XXX_PACKET_TYPE_POWER,
938  .direction = AM7XXX_DIRECTION_OUT,
939  .header_data_len = sizeof(struct am7xxx_power_header),
940  .unknown2 = 0x3e,
941  .unknown3 = 0x10,
942  };
943 
944  switch(power) {
945  case AM7XXX_POWER_OFF:
946  h.header_data.power.bit2 = 0;
947  h.header_data.power.bit1 = 0;
948  h.header_data.power.bit0 = 0;
949  break;
950 
951  case AM7XXX_POWER_LOW:
952  h.header_data.power.bit2 = 0;
953  h.header_data.power.bit1 = 0;
954  h.header_data.power.bit0 = 1;
955  break;
956 
957  case AM7XXX_POWER_MIDDLE:
958  h.header_data.power.bit2 = 0;
959  h.header_data.power.bit1 = 1;
960  h.header_data.power.bit0 = 0;
961  break;
962 
963  case AM7XXX_POWER_HIGH:
964  h.header_data.power.bit2 = 0;
965  h.header_data.power.bit1 = 1;
966  h.header_data.power.bit0 = 1;
967  break;
968 
969  case AM7XXX_POWER_TURBO:
970  h.header_data.power.bit2 = 1;
971  h.header_data.power.bit1 = 0;
972  h.header_data.power.bit0 = 0;
973  break;
974 
975  default:
976  error(dev->ctx, "Unsupported power mode.\n");
977  return -EINVAL;
978  };
979 
980  ret = send_header(dev, &h);
981  if (ret < 0)
982  return ret;
983 
984  return 0;
985 }
986 
987 static int picopix_set_power_mode(am7xxx_device *dev, am7xxx_power_mode power)
988 {
989  switch(power) {
990  case AM7XXX_POWER_LOW:
991  return send_command(dev, AM7XXX_PACKET_TYPE_PICOPIX_POWER_LOW);
992 
993  case AM7XXX_POWER_MIDDLE:
994  return send_command(dev, AM7XXX_PACKET_TYPE_PICOPIX_POWER_MEDIUM);
995 
996  case AM7XXX_POWER_HIGH:
997  return send_command(dev, AM7XXX_PACKET_TYPE_PICOPIX_POWER_HIGH);
998 
999  case AM7XXX_POWER_OFF:
1000  case AM7XXX_POWER_TURBO:
1001  default:
1002  error(dev->ctx, "Unsupported power mode.\n");
1003  return -EINVAL;
1004  };
1005 }
1006 
1007 static int default_set_zoom_mode(am7xxx_device *dev, am7xxx_zoom_mode zoom)
1008 {
1009  int ret;
1010  struct am7xxx_header h = {
1011  .packet_type = AM7XXX_PACKET_TYPE_ZOOM,
1012  .direction = AM7XXX_DIRECTION_OUT,
1013  .header_data_len = sizeof(struct am7xxx_zoom_header),
1014  .unknown2 = 0x3e,
1015  .unknown3 = 0x10,
1016  };
1017 
1018  switch(zoom) {
1019  case AM7XXX_ZOOM_ORIGINAL:
1020  h.header_data.zoom.bit1 = 0;
1021  h.header_data.zoom.bit0 = 0;
1022  break;
1023 
1024  case AM7XXX_ZOOM_H:
1025  h.header_data.zoom.bit1 = 0;
1026  h.header_data.zoom.bit0 = 1;
1027  break;
1028 
1029  case AM7XXX_ZOOM_H_V:
1030  h.header_data.zoom.bit1 = 1;
1031  h.header_data.zoom.bit0 = 0;
1032  break;
1033 
1034  case AM7XXX_ZOOM_TEST:
1035  h.header_data.zoom.bit1 = 1;
1036  h.header_data.zoom.bit0 = 1;
1037  break;
1038 
1039  case AM7XXX_ZOOM_TELE:
1040  default:
1041  error(dev->ctx, "Unsupported zoom mode.\n");
1042  return -EINVAL;
1043  };
1044 
1045  ret = send_header(dev, &h);
1046  if (ret < 0)
1047  return ret;
1048 
1049  return 0;
1050 }
1051 
1052 static int picopix_set_zoom_mode(am7xxx_device *dev, am7xxx_zoom_mode zoom)
1053 {
1054  int ret;
1055  am7xxx_packet_type packet_type;
1056 
1057  switch(zoom) {
1058  case AM7XXX_ZOOM_ORIGINAL:
1059  packet_type = AM7XXX_PACKET_TYPE_PICOPIX_DISABLE_TI;
1060  break;
1061 
1062  case AM7XXX_ZOOM_TELE:
1063  packet_type = AM7XXX_PACKET_TYPE_PICOPIX_ENABLE_TI;
1064  break;
1065 
1066  case AM7XXX_ZOOM_H:
1067  case AM7XXX_ZOOM_H_V:
1068  case AM7XXX_ZOOM_TEST:
1069  default:
1070  error(dev->ctx, "Unsupported zoom mode.\n");
1071  return -EINVAL;
1072  };
1073 
1074  ret = send_command(dev, packet_type);
1075  if (ret < 0)
1076  return ret;
1077 
1078  /* The Windows drivers wait for 100ms and send the same command again,
1079  * probably to overcome a firmware deficiency */
1080  ret = msleep(100);
1081  if (ret < 0)
1082  return ret;
1083 
1084  return send_command(dev, packet_type);
1085 }
1086 
1087 /* Public API */
1088 
1089 AM7XXX_PUBLIC int am7xxx_init(am7xxx_context **ctx)
1090 {
1091  int ret;
1092 
1093  *ctx = malloc(sizeof(**ctx));
1094  if (*ctx == NULL) {
1095  fatal("cannot allocate the context (%s)\n", strerror(errno));
1096  ret = -ENOMEM;
1097  goto out;
1098  }
1099  memset(*ctx, 0, sizeof(**ctx));
1100 
1101  /* Set the highest log level during initialization */
1102  (*ctx)->log_level = AM7XXX_LOG_TRACE;
1103 
1104  ret = libusb_init(&((*ctx)->usb_context));
1105  if (ret < 0) {
1106  error(*ctx, "libusb_init failed: %s\n", libusb_error_name(ret));
1107  goto out_free_context;
1108  }
1109 
1110  libusb_set_debug((*ctx)->usb_context, LIBUSB_LOG_LEVEL_INFO);
1111 
1112  ret = scan_devices(*ctx, SCAN_OP_BUILD_DEVLIST , 0, NULL);
1113  if (ret < 0) {
1114  error(*ctx, "scan_devices() failed\n");
1115  am7xxx_shutdown(*ctx);
1116  goto out;
1117  }
1118 
1119  /* Set a quieter log level as default for normal operation */
1120  (*ctx)->log_level = AM7XXX_LOG_ERROR;
1121  return 0;
1122 
1123 out_free_context:
1124  free(*ctx);
1125  *ctx = NULL;
1126 out:
1127  return ret;
1128 }
1129 
1130 AM7XXX_PUBLIC void am7xxx_shutdown(am7xxx_context *ctx)
1131 {
1132  am7xxx_device *current;
1133 
1134  if (ctx == NULL) {
1135  fatal("context must not be NULL!\n");
1136  return;
1137  }
1138 
1139  current = ctx->devices_list;
1140  while (current) {
1141  am7xxx_device *next = current->next;
1142  am7xxx_close_device(current);
1143  free(current->device_info);
1144  free(current);
1145  current = next;
1146  }
1147 
1148  libusb_exit(ctx->usb_context);
1149  free(ctx);
1150  ctx = NULL;
1151 }
1152 
1153 AM7XXX_PUBLIC void am7xxx_set_log_level(am7xxx_context *ctx, am7xxx_log_level log_level)
1154 {
1155  ctx->log_level = log_level;
1156 }
1157 
1158 AM7XXX_PUBLIC int am7xxx_open_device(am7xxx_context *ctx, am7xxx_device **dev,
1159  unsigned int device_index)
1160 {
1161  int ret;
1162 
1163  if (ctx == NULL) {
1164  fatal("context must not be NULL!\n");
1165  return -EINVAL;
1166  }
1167 
1168  ret = scan_devices(ctx, SCAN_OP_OPEN_DEVICE, device_index, dev);
1169  if (ret < 0) {
1170  errno = ENODEV;
1171  goto out;
1172  } else if (ret > 0) {
1173  warning(ctx, "device %d already open\n", device_index);
1174  errno = EBUSY;
1175  ret = -EBUSY;
1176  goto out;
1177  }
1178 
1179  /* Philips/Sagemcom PicoPix projectors require that the DEVINFO packet
1180  * is the first one to be sent to the device in order for it to
1181  * successfully return the correct device information.
1182  *
1183  * So, if there is not a cached version of it (from a previous open),
1184  * we ask for device info at open time,
1185  */
1186  if ((*dev)->device_info == NULL) {
1187  ret = am7xxx_get_device_info(*dev, NULL);
1188  if (ret < 0)
1189  error(ctx, "cannot get device info\n");
1190  }
1191 
1192 out:
1193  return ret;
1194 }
1195 
1196 AM7XXX_PUBLIC int am7xxx_close_device(am7xxx_device *dev)
1197 {
1198  if (dev == NULL) {
1199  fatal("dev must not be NULL!\n");
1200  return -EINVAL;
1201  }
1202  if (dev->usb_device) {
1203  wait_for_trasfer_completed(dev);
1204  libusb_release_interface(dev->usb_device, dev->desc->interface_number);
1205  libusb_close(dev->usb_device);
1206  dev->usb_device = NULL;
1207  }
1208  return 0;
1209 }
1210 
1211 AM7XXX_PUBLIC int am7xxx_get_device_info(am7xxx_device *dev,
1212  am7xxx_device_info *device_info)
1213 {
1214  int ret;
1215  struct am7xxx_header h;
1216 
1217  if (dev->device_info) {
1218  memcpy(device_info, dev->device_info, sizeof(*device_info));
1219  return 0;
1220  }
1221 
1222  ret = send_command(dev, AM7XXX_PACKET_TYPE_DEVINFO);
1223  if (ret < 0)
1224  return ret;
1225 
1226  memset(&h, 0, sizeof(h));
1227  ret = read_header(dev, &h);
1228  if (ret < 0)
1229  return ret;
1230 
1231  if (h.packet_type != AM7XXX_PACKET_TYPE_DEVINFO) {
1232  error(dev->ctx, "expected packet type: %d, got %d instead!\n",
1233  AM7XXX_PACKET_TYPE_DEVINFO, h.packet_type);
1234  errno = ENOTSUP;
1235  return -ENOTSUP;
1236  }
1237 
1238  dev->device_info = malloc(sizeof(*dev->device_info));
1239  if (dev->device_info == NULL) {
1240  error(dev->ctx, "cannot allocate a device info (%s)\n",
1241  strerror(errno));
1242  return -ENOMEM;
1243  }
1244  memset(dev->device_info, 0, sizeof(*dev->device_info));
1245 
1246  dev->device_info->native_width = h.header_data.devinfo.native_width;
1247  dev->device_info->native_height = h.header_data.devinfo.native_height;
1248 #if 0
1249  /* No reason to expose these in the public API until we know what they mean */
1250  dev->device_info->unknown0 = h.header_data.devinfo.unknown0;
1251  dev->device_info->unknown1 = h.header_data.devinfo.unknown1;
1252 #endif
1253 
1254  return 0;
1255 }
1256 
1258  unsigned int upscale,
1259  unsigned int original_width,
1260  unsigned int original_height,
1261  unsigned int *scaled_width,
1262  unsigned int *scaled_height)
1263 {
1264 
1265  am7xxx_device_info device_info;
1266  float width_ratio;
1267  float height_ratio;
1268  int ret;
1269 
1270  ret = am7xxx_get_device_info(dev, &device_info);
1271  if (ret < 0) {
1272  error(dev->ctx, "cannot get device info\n");
1273  return ret;
1274  }
1275 
1276  /*
1277  * Check if we need to rescale; if the input image fits the native
1278  * dimensions there is no need to, unless we want to upscale.
1279  */
1280  if (!upscale &&
1281  original_width <= device_info.native_width &&
1282  original_height <= device_info.native_height ) {
1283  debug(dev->ctx, "CASE 0, no rescaling, the original image fits already\n");
1284  *scaled_width = original_width;
1285  *scaled_height = original_height;
1286  return 0;
1287  }
1288 
1289  /* Input dimensions relative to the device native dimensions */
1290  width_ratio = (float)original_width / device_info.native_width;
1291  height_ratio = (float)original_height / device_info.native_height;
1292 
1293  if (width_ratio > height_ratio) {
1294  /*
1295  * The input is proportionally "wider" than the device viewport
1296  * so its height needs to be adjusted
1297  */
1298  debug(dev->ctx, "CASE 1, original image wider, adjust the scaled height\n");
1299  *scaled_width = device_info.native_width;
1300  *scaled_height = (unsigned int)lroundf(original_height / width_ratio);
1301  } else if (width_ratio < height_ratio) {
1302  /*
1303  * The input is proportionally "taller" than the device viewport
1304  * so its width needs to be adjusted
1305  */
1306  debug(dev->ctx, "CASE 2 original image taller, adjust the scaled width\n");
1307  *scaled_width = (unsigned int)lroundf(original_width / height_ratio);
1308  *scaled_height = device_info.native_height;
1309  } else {
1310  debug(dev->ctx, "CASE 3, just rescale, same aspect ratio already\n");
1311  *scaled_width = device_info.native_width;
1312  *scaled_height = device_info.native_height;
1313  }
1314  debug(dev->ctx, "scaled dimensions: %dx%d\n", *scaled_width, *scaled_height);
1315 
1316  return 0;
1317 }
1318 
1319 AM7XXX_PUBLIC int am7xxx_send_image(am7xxx_device *dev,
1320  am7xxx_image_format format,
1321  unsigned int width,
1322  unsigned int height,
1323  uint8_t *image,
1324  unsigned int image_size)
1325 {
1326  int ret;
1327  struct am7xxx_header h = {
1328  .packet_type = AM7XXX_PACKET_TYPE_IMAGE,
1329  .direction = AM7XXX_DIRECTION_OUT,
1330  .header_data_len = sizeof(struct am7xxx_image_header),
1331  .unknown2 = 0x3e,
1332  .unknown3 = 0x10,
1333  .header_data = {
1334  .image = {
1335  .format = format,
1336  .width = width,
1337  .height = height,
1338  .image_size = image_size,
1339  },
1340  },
1341  };
1342 
1343  ret = send_header(dev, &h);
1344  if (ret < 0)
1345  return ret;
1346 
1347  if (image == NULL || image_size == 0) {
1348  warning(dev->ctx, "Not sending any data, check the 'image' or 'image_size' parameters\n");
1349  return 0;
1350  }
1351 
1352  return send_data(dev, image, image_size);
1353 }
1354 
1355 AM7XXX_PUBLIC int am7xxx_send_image_async(am7xxx_device *dev,
1356  am7xxx_image_format format,
1357  unsigned int width,
1358  unsigned int height,
1359  uint8_t *image,
1360  unsigned int image_size)
1361 {
1362  int ret;
1363  struct am7xxx_header h = {
1364  .packet_type = AM7XXX_PACKET_TYPE_IMAGE,
1365  .direction = AM7XXX_DIRECTION_OUT,
1366  .header_data_len = sizeof(struct am7xxx_image_header),
1367  .unknown2 = 0x3e,
1368  .unknown3 = 0x10,
1369  .header_data = {
1370  .image = {
1371  .format = format,
1372  .width = width,
1373  .height = height,
1374  .image_size = image_size,
1375  },
1376  },
1377  };
1378 
1379  ret = send_header(dev, &h);
1380  if (ret < 0)
1381  return ret;
1382 
1383  if (image == NULL || image_size == 0) {
1384  warning(dev->ctx, "Not sending any data, check the 'image' or 'image_size' parameters\n");
1385  return 0;
1386  }
1387 
1388  return send_data_async(dev, image, image_size);
1389 }
1390 
1392 {
1393  if (dev->desc->ops.set_power_mode == NULL) {
1394  warning(dev->ctx,
1395  "setting power mode is unsupported on this device\n");
1396  return 0;
1397  }
1398 
1399  return dev->desc->ops.set_power_mode(dev, power);
1400 }
1401 
1403 {
1404  if (dev->desc->ops.set_zoom_mode == NULL) {
1405  warning(dev->ctx,
1406  "setting zoom mode is unsupported on this device\n");
1407  return 0;
1408  }
1409 
1410  return dev->desc->ops.set_zoom_mode(dev, zoom);
1411 }
Zoom test screen, the firmware version is shown as well.
Definition: am7xxx.h:126
Zoom 1: H Scale (changes aspect ratio).
Definition: am7xxx.h:124
struct _am7xxx_context am7xxx_context
An opaque data type representing a context.
Definition: am7xxx.h:38
Zoom Tele: available on some PicoPix models.
Definition: am7xxx.h:127
A struct describing device specific properties.
Definition: am7xxx.h:56
int am7xxx_init(am7xxx_context **ctx)
Initialize the library context and data structures, and scan for devices.
Definition: am7xxx.c:1089
Original Size, as retrieved via am7xxx_device_info.
Definition: am7xxx.h:123
Max brightness and power consumption.
Definition: am7xxx.h:104
Middle level of brightness.
Definition: am7xxx.h:102
am7xxx_image_format
The image formats accepted by the device.
Definition: am7xxx.h:80
am7xxx_power_mode
The device power modes.
Definition: am7xxx.h:99
Public libam7xxx API.
int am7xxx_open_device(am7xxx_context *ctx, am7xxx_device **dev, unsigned int device_index)
Open an am7xxx_device according to a index.
Definition: am7xxx.c:1158
Zoom 2: H/V Scale (changes aspect ratio).
Definition: am7xxx.h:125
More brightness, but more power consumption.
Definition: am7xxx.h:103
Error messages, typically they describe API functions failures.
Definition: am7xxx.h:70
int am7xxx_set_power_mode(am7xxx_device *dev, am7xxx_power_mode power)
Set the power mode of an am7xxx device.
Definition: am7xxx.c:1391
void am7xxx_set_log_level(am7xxx_context *ctx, am7xxx_log_level log_level)
Set verbosity level of log messages.
Definition: am7xxx.c:1153
int am7xxx_get_device_info(am7xxx_device *dev, am7xxx_device_info *device_info)
Get info about an am7xxx device.
Definition: am7xxx.c:1211
int am7xxx_calc_scaled_image_dimensions(am7xxx_device *dev, unsigned int upscale, unsigned int original_width, unsigned int original_height, unsigned int *scaled_width, unsigned int *scaled_height)
Calculate the dimensions of an image to be shown on an am7xxx device.
Definition: am7xxx.c:1257
unsigned int native_width
The device native width.
Definition: am7xxx.h:57
int am7xxx_send_image_async(am7xxx_device *dev, am7xxx_image_format format, unsigned int width, unsigned int height, unsigned char *image, unsigned int image_size)
Queue transfer of an image for display on an am7xxx device and return immediately.
am7xxx_zoom_mode
The display zoom modes.
Definition: am7xxx.h:122
int am7xxx_set_zoom_mode(am7xxx_device *dev, am7xxx_zoom_mode zoom)
Set the zoom mode of an am7xxx device.
Definition: am7xxx.c:1402
Fatal messages, the user application should stop if it gets one of this.
Definition: am7xxx.h:69
Verbose informations about the communication with the hardware.
Definition: am7xxx.h:74
Low power consumption but also low brightness.
Definition: am7xxx.h:101
int am7xxx_send_image(am7xxx_device *dev, am7xxx_image_format format, unsigned int width, unsigned int height, unsigned char *image, unsigned int image_size)
Send an image for display on an am7xxx device.
unsigned int native_height
The device native height.
Definition: am7xxx.h:58
void am7xxx_shutdown(am7xxx_context *ctx)
Cleanup the library data structures and free the context.
Definition: am7xxx.c:1130
am7xxx_log_level
The verbosity level of logging messages.
Definition: am7xxx.h:68
int am7xxx_close_device(am7xxx_device *dev)
Close an am7xxx_device.
Definition: am7xxx.c:1196
struct _am7xxx_device am7xxx_device
An opaque data type representing an am7xxx device.
Definition: am7xxx.h:46
Display is powered off, no image shown.
Definition: am7xxx.h:100