Add initial support for USB output communication
[libam7xxx.git] / picoproj.c
1 /* picoproj - communication with AM7xxx based USB pico projectors
2  *
3  * Copyright (C) 2011  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 3 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 <stdint.h>
22 #include <string.h>
23 #include <endian.h>
24 #include <errno.h>
25 #include <sys/mman.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30
31 #include <libusb.h>
32
33 #define AM7x01_VENDOR_ID  0x1de1
34 #define AM7x01_PRODUCT_ID 0xc101
35
36 static libusb_device_handle *dev;
37
38 typedef enum {
39         AM7x01_PACKET_TYPE_INIT    = 0x01,
40         AM7x01_PACKET_TYPE_IMAGE   = 0x02,
41         AM7x01_PACKET_TYPE_POWER   = 0x04,
42         AM7x01_PACKET_TYPE_UNKNOWN = 0x05,
43 } am7x01_packet_type;
44
45 typedef enum {
46         AM7x01_IMAGE_FORMAT_JPEG = 1,
47 } am7x01_image_format;
48
49 typedef enum {
50         AM7x01_POWER_OFF  = 0,
51         AM7x01_POWER_LOW  = 1,
52         AM7x01_POWER_MID  = 2,
53         AM7x01_POWER_HIGH = 3,
54 } am7x01_power_mode;
55
56 struct image_header {
57         uint32_t format;
58         uint32_t width;
59         uint32_t height;
60         uint32_t image_size;
61 };
62
63 struct power_header {
64         uint32_t power_low;
65         uint32_t power_mid;
66         uint32_t power_high;
67 };
68
69 /*
70  * Examples of packet headers:
71  *
72  * Image widget:
73  * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
74  * +02|00|00|00|00|10|3e|10|01|00|00|00|20|03|00|00|e0|01|00|00|53|E8|00|00+
75  * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
76  *
77  * Brightness widget:
78  * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
79  * +04|00|00|00|00|0c|ff|ff|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00|00+
80  * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
81  */
82
83 static uint8_t reference_image_header[] = {
84         0x02, 0x00, 0x00, 0x00,
85         0x00,
86         0x10,
87         0x3e,
88         0x10,
89         0x01, 0x00, 0x00, 0x00,
90         0x20, 0x03, 0x00, 0x00,
91         0xe0, 0x01, 0x00, 0x00,
92         0x53, 0xE8, 0x00, 0x00
93 };
94
95 struct header {
96         uint32_t packet_type;
97         uint8_t unknown0;
98         uint8_t header_len;
99         uint8_t unknown2;
100         uint8_t unknown3;
101         union {
102                 struct image_header image;
103                 struct power_header power;
104         } header_data;
105 };
106
107
108 static void dump_image_header(struct image_header *i)
109 {
110         if (i == NULL)
111                 return;
112
113         printf("Image header:\n");
114         printf("format:      0x%08x (%u)\n", i->format, i->format);
115         printf("width:       0x%08x (%u)\n", i->width, i->width);
116         printf("height:      0x%08x (%u)\n", i->height, i->height);
117         printf("image size:  0x%08x (%u)\n", i->image_size, i->image_size);
118 }
119
120 static void dump_header(struct header *h)
121 {
122         if (h == NULL)
123                 return;
124
125         printf("packet_type: 0x%08x (%u)\n", h->packet_type, h->packet_type);
126         printf("unknown0:    0x%02hhx (%hhu)\n", h->unknown0, h->unknown0);
127         printf("header_len:  0x%02hhx (%hhu)\n", h->header_len, h->header_len);
128         printf("unknown2:    0x%02hhx (%hhu)\n", h->unknown2, h->unknown2);
129         printf("unknown3:    0x%02hhx (%hhu)\n", h->unknown3, h->unknown3);
130
131         switch(h->packet_type) {
132         case AM7x01_PACKET_TYPE_IMAGE:
133                 dump_image_header(&(h->header_data.image));
134                 break;
135
136         default:
137                 printf("Packet type not supported!\n");
138                 break;
139         }
140
141         fflush(stdout);
142 }
143
144 static inline unsigned int in_80chars(unsigned int i)
145 {
146         return ((i+1) % (80/3));
147 }
148
149 static void dump_buffer(uint8_t *buffer, unsigned int len)
150 {
151         unsigned int i;
152
153         if (buffer == NULL || len == 0)
154                 return;
155
156         for (i = 0; i < len; i++) {
157                 printf("%02hhX%c", buffer[i], (in_80chars(i) && (i < len - 1)) ? ' ' : '\n');
158         }
159         fflush(stdout);
160 }
161
162 static int send_data(uint8_t *buffer, unsigned int len)
163 {
164         int ret;
165         int transferred;
166
167         dump_buffer(buffer, len);
168
169         ret = libusb_bulk_transfer(dev, 1, buffer, len, &transferred, 0);
170         if (ret != 0 || (unsigned int)transferred != len) {
171                 fprintf(stderr, "Error: ret: %d\ttransferred: %d (expected %u)\n",
172                         ret, transferred, len);
173                 return ret;
174         }
175
176         return 0;
177 }
178
179 static int send_header(struct header *h)
180 {
181         union {
182                 struct header header;
183                 uint8_t buffer[sizeof (struct header)];
184         } data;
185
186         data.header = *h;
187
188         return send_data(data.buffer, sizeof (struct header));
189 }
190
191 static int send_image(am7x01_image_format format,
192                       unsigned int width,
193                       unsigned int height,
194                       uint8_t *image,
195                       unsigned int size)
196 {
197         int ret;
198         struct header h = {
199                 .packet_type = htole32(AM7x01_PACKET_TYPE_IMAGE),
200                 .unknown0    = 0x00,
201                 .header_len  = sizeof(struct image_header),
202                 .unknown2    = 0x3e,
203                 .unknown3    = 0x10,
204                 .header_data = {
205                         .image = {
206                                 .format     = htole32(format),
207                                 .width      = htole32(width),
208                                 .height     = htole32(height),
209                                 .image_size = htole32(size),
210                         },
211                 },
212         };
213
214         dump_header(&h);
215         printf("\n");
216
217         printf("Dump Buffers\n");
218         dump_buffer(reference_image_header, sizeof(struct header));
219
220         ret = send_header(&h);
221         if (ret < 0)
222                 return ret;
223
224         if (image == NULL || size == 0)
225                 return 0;
226
227         return send_data(image, size);
228 }
229
230 static void usage(char *name)
231 {
232         printf("usage: %s [OPTIONS]\n\n", name);
233         printf("OPTIONS:\n");
234         printf("\t-f <filename>\t\tthe image file to upload\n");
235         printf("\t-F <format>\t\tthe image format to use (default is JPEG).\n");
236         printf("\t\t\t\tSUPPORTED FORMATS:\n");
237         printf("\t\t\t\t\t1 - JPEG\n");
238         printf("\t-W <image width>\tthe width of the image to upload\n");
239         printf("\t-H <image height>\tthe height of the image to upload\n");
240         printf("\t-h \t\t\tthis help message\n");
241 }
242
243 int main(int argc, char *argv[])
244 {
245         int ret;
246         int exit_code = EXIT_SUCCESS;
247         int opt;
248
249         char filename[FILENAME_MAX] = {0};
250         int image_fd = -1;
251         int format = AM7x01_IMAGE_FORMAT_JPEG;
252         int width = 800;
253         int height = 480;
254         uint8_t *image = NULL;
255         unsigned int size = 59475;
256
257         while ((opt = getopt(argc, argv, "f:F:W:H:h")) != -1) {
258                 switch (opt) {
259                 case 'f':
260                         strncpy(filename, optarg, FILENAME_MAX);
261                         break;
262                 case 'F':
263                         format = atoi(optarg);
264                         if (format != 1) {
265                                 fprintf(stderr, "Unsupported format\n");
266                                 exit(EXIT_FAILURE);
267                         }
268                         break;
269                 case 'W':
270                         width = atoi(optarg);
271                         if (width < 0) {
272                                 fprintf(stderr, "Unsupported width\n");
273                                 exit(EXIT_FAILURE);
274                         }
275                         break;
276                 case 'H':
277                         height = atoi(optarg);
278                         if (height < 0) {
279                                 fprintf(stderr, "Unsupported height\n");
280                                 exit(EXIT_FAILURE);
281                         }
282                         break;
283                 default: /* '?' */
284                         usage(argv[0]);
285                         exit(EXIT_FAILURE);
286                 }
287         }
288
289         if (filename[0] != '\0') {
290                 struct stat st;
291                 
292                 image_fd = open(filename, O_RDONLY);
293                 if (image_fd < 0) {
294                         perror("open");
295                         exit_code = EXIT_FAILURE;
296                         goto out;
297                 }
298                 if (fstat(image_fd, &st) < 0) {
299                         perror("fstat");
300                         exit_code = EXIT_FAILURE;
301                         goto out_close_image_fd;
302                 }
303                 size = st.st_size;
304
305                 image = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, image_fd, 0);
306                 if (image == NULL) {
307                         perror("mmap");
308                         exit_code = EXIT_FAILURE;
309                         goto out_close_image_fd;
310                 }
311         }
312
313         libusb_init(NULL);
314         libusb_set_debug(NULL, 3);
315
316         dev = libusb_open_device_with_vid_pid(NULL,
317                                               AM7x01_VENDOR_ID,
318                                               AM7x01_PRODUCT_ID);
319         if (dev == NULL) {
320                 errno = ENODEV;
321                 perror("libusb_open_device_with_vid_pid");
322                 exit_code = EXIT_FAILURE;
323                 goto out_libusb_exit;
324         }
325
326         libusb_set_configuration(dev, 1);
327         libusb_claim_interface(dev, 0);
328
329         ret = send_image(format, width, height, image, size);
330         if (ret < 0) {
331                 perror("send_image");
332                 exit_code = EXIT_FAILURE;
333                 goto cleanup;
334         }
335
336         exit_code = EXIT_SUCCESS;
337
338 cleanup:
339         libusb_close(dev);
340
341 out_libusb_exit:
342         libusb_exit(NULL);
343
344         if (image != NULL) {
345                 ret = munmap(image, size);
346                 if (ret < 0)
347                         perror("munmap");
348         }
349
350 out_close_image_fd:
351         if (image_fd >= 0) {
352                 ret = close(image_fd);
353                 if (ret < 0)
354                         perror("close");
355         }
356
357 out:
358         exit(exit_code);
359 }