Increase power off time for USB device
[tools/testlab/sd-mux.git] / src / main.cpp
1 /*
2  *  Copyright (c) 2016 -2018 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Licensed under the Apache License, Version 2.0 (the "License");
5  *  you may not use this file except in compliance with the License.
6  *  You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License
15  */
16 /**
17  * @file        src/main.cpp
18  * @author      Adam Malinowski <a.malinowsk2@partner.samsung.com>
19  * @brief       Main sd-mux-ctrl file
20  */
21
22 #include <popt.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include <libftdi1/ftdi.h>
29
30 #define PRODUCT 0x6001
31 #define SAMSUNG_VENDOR 0x04e8
32
33 // SDMUX specific definitions
34 #define SOCKET_SEL      (0x01 << 0x00)
35 #define USB_SEL         (0x01 << 0x03)
36 #define POWER_SW_OFF    (0x01 << 0x02)
37 #define POWER_SW_ON     (0x01 << 0x04)
38 #define DYPER1          (0x01 << 0x05)
39 #define DYPER2          (0x01 << 0x06)
40
41 // USBMUX specific definitions
42 #define UM_SOCKET_SEL   (0x01 << 0x00)
43 #define UM_DEVICE_PWR   (0x01 << 0x01)
44 #define UM_DUT_LED              (0x01 << 0x02)
45 #define UM_GP_LED               (0x01 << 0x03)
46
47
48 #define DELAY_100MS     100000
49 #define DELAY_500MS     500000
50
51 #define CCDT_SDMUX_STR  "sd-mux"
52 #define CCDT_SDWIRE_STR "sd-wire"
53 #define CCDT_USBMUX_STR "usb-mux"
54
55 #define STRING_SIZE     128
56
57 enum CCCommand {
58     CCC_List,
59     CCC_DUT,
60     CCC_TS,
61     CCC_Tick,
62     CCC_Pins,
63     CCC_Info,
64     CCC_ShowSerial,
65     CCC_SetSerial,
66     CCC_Init,
67     CCC_Status,
68     CCC_DyPer1,
69     CCC_DyPer2,
70     CCC_None
71 };
72
73 enum Target {
74     T_DUT,
75     T_TS
76 };
77
78 enum CCDeviceType {
79     CCDT_SDMUX,
80     CCDT_SDWIRE,
81         CCDT_USBMUX,
82     CCDT_MAX
83 };
84
85 enum CCFeature {
86     CCF_SDMUX,
87     CCF_POWERSWITCH,
88     CCF_USBMUX,
89     CCF_DYPERS,
90     CCF_MAX
91 };
92
93 enum CCOption {
94     CCO_DeviceId,
95     CCO_DeviceSerial,
96     CCO_TickTime,
97     CCO_BitsInvert,
98     CCO_Vendor,
99     CCO_Product,
100     CCO_DyPer,
101     CCO_DeviceType,
102     CCO_MAX
103 };
104
105 union CCOptionValue {
106     int argn;
107     char *args;
108 };
109
110 int doPower(bool off, bool on, CCOptionValue options[]);
111 int selectTarget(Target target, CCOptionValue options[]);
112
113 CCDeviceType getDeviceTypeFromString(char *deviceTypeStr) {
114     if (strcmp(CCDT_SDMUX_STR, deviceTypeStr) == 0) {
115         return CCDT_SDMUX;
116     }
117
118     if (strcmp(CCDT_SDWIRE_STR, deviceTypeStr) == 0) {
119         return CCDT_SDWIRE;
120     }
121
122     if (strcmp(CCDT_USBMUX_STR, deviceTypeStr) == 0) {
123         return CCDT_USBMUX;
124     }
125
126     return CCDT_MAX;
127 }
128
129 bool hasFeature(CCDeviceType deviceType, CCFeature feature) {
130     static const bool featureMatrix[CCDT_MAX][CCF_MAX] = {
131             {true, true, true, true},           // SD-MUX features
132             {true, false, false, false},        // SDWire features
133                         {false, false, true, false},        // SDWire features
134     };
135
136     if (deviceType >= CCDT_MAX || feature >= CCF_MAX)
137         return false;
138
139     return featureMatrix[deviceType][feature];
140 }
141
142 int listDevices(CCOptionValue options[]) {
143     int fret, i;
144     struct ftdi_context *ftdi;
145     struct ftdi_device_list *devlist, *curdev;
146     char manufacturer[STRING_SIZE + 1], description[STRING_SIZE + 1], serial[STRING_SIZE + 1];
147     int retval = EXIT_SUCCESS;
148
149     if ((ftdi = ftdi_new()) == 0) {
150         fprintf(stderr, "ftdi_new failed\n");
151         return EXIT_FAILURE;
152     }
153
154     if ((fret = ftdi_usb_find_all(ftdi, &devlist, options[CCO_Vendor].argn, options[CCO_Product].argn)) < 0) {
155         fprintf(stderr, "ftdi_usb_find_all failed: %d (%s)\n", fret, ftdi_get_error_string(ftdi));
156         ftdi_free(ftdi);
157         return EXIT_FAILURE;
158     }
159
160     if (options[CCO_DeviceId].argn == -1) {
161         printf("Number of FTDI devices found: %d\n", fret);
162     }
163
164     i = 0;
165     for (curdev = devlist; curdev != NULL; i++) {
166         if (options[CCO_DeviceId].argn == -1 || options[CCO_DeviceId].argn == i) {
167             if ((fret = ftdi_usb_get_strings(ftdi, curdev->dev, manufacturer, STRING_SIZE, description, STRING_SIZE,
168                     serial, STRING_SIZE)) < 0) {
169                 fprintf(stderr, "ftdi_usb_get_strings failed: %d (%s)\n", fret, ftdi_get_error_string(ftdi));
170                 retval = EXIT_FAILURE;
171                 goto finish_him;
172             }
173             if (options[CCO_DeviceId].argn == -1) {
174                 printf("Dev: %d, Manufacturer: %s, Serial: %s, Description: %s\n", i,
175                        manufacturer, serial, description);
176             } else {
177                 printf("%s", serial);
178             }
179         }
180         curdev = curdev->next;
181     }
182
183 finish_him:
184     ftdi_list_free(&devlist);
185     ftdi_free(ftdi);
186
187     return retval;
188 }
189
190 struct ftdi_context* openDevice(CCOptionValue options[], CCDeviceType *deviceType) {
191     struct ftdi_context *ftdi = NULL;
192     int fret;
193     char product[STRING_SIZE + 1];
194     CCDeviceType tmpDeviceType;
195
196     if ((options[CCO_DeviceSerial].args == NULL) && (options[CCO_DeviceId].argn < 0)) {
197         fprintf(stderr, "No serial number or device id provided!\n");
198         return NULL;
199     }
200
201     if ((ftdi = ftdi_new()) == 0) {
202         fprintf(stderr, "ftdi_new failed\n");
203         return NULL;
204     }
205
206     if (options[CCO_DeviceSerial].args != NULL) {
207         fret = ftdi_usb_open_desc_index(ftdi, options[CCO_Vendor].argn, options[CCO_Product].argn, NULL, options[CCO_DeviceSerial].args, 0);
208     } else {
209         fret = ftdi_usb_open_desc_index(ftdi, options[CCO_Vendor].argn, options[CCO_Product].argn, NULL, NULL, options[CCO_DeviceId].argn);
210     }
211     if (fret < 0) {
212         fprintf(stderr, "Unable to open ftdi device: %d (%s)\n", fret, ftdi_get_error_string(ftdi));
213         goto error;
214     }
215
216     fret = ftdi_read_eeprom(ftdi);
217     if (fret < 0) {
218         fprintf(stderr, "Unable to read ftdi eeprom: %d (%s)\n", fret, ftdi_get_error_string(ftdi));
219         goto error;
220     }
221
222     fret = ftdi_eeprom_decode(ftdi, 0);
223     if (fret < 0) {
224         fprintf(stderr, "Unable to decode ftdi eeprom: %d (%s)\n", fret, ftdi_get_error_string(ftdi));
225         goto error;
226     }
227
228     if (deviceType != NULL) {
229         ftdi_eeprom_get_strings(ftdi, NULL, 0, product, sizeof(product), NULL, 0);
230         tmpDeviceType = getDeviceTypeFromString(product);
231         if (tmpDeviceType == CCDT_MAX) {
232             fprintf(stderr, "Invalid device type. Device probably not configured!\n");
233             goto error;
234         }
235         *deviceType = tmpDeviceType;
236     }
237
238     return ftdi;
239
240 error:
241     ftdi_usb_close(ftdi);
242     ftdi_free(ftdi);
243
244     return NULL;
245 }
246
247 int showInfo(CCOptionValue options[]) {
248     struct ftdi_context *ftdi;
249     int fret, ret = EXIT_SUCCESS;
250
251     ftdi = openDevice(options, NULL);
252     if (ftdi == NULL) {
253         return EXIT_FAILURE;
254     }
255
256     fret = ftdi_eeprom_decode(ftdi, 1);
257     if (fret < 0) {
258         fprintf(stderr, "Unable to decode ftdi eeprom: %d (%s)\n", fret, ftdi_get_error_string(ftdi));
259         ret = EXIT_FAILURE;
260         ftdi_usb_close(ftdi);
261         ftdi_free(ftdi);
262     }
263
264     return ret;
265 }
266
267 int doInit(CCOptionValue options[]) {
268     if (doPower(true, false, options) != EXIT_SUCCESS) {
269         return EXIT_FAILURE;
270     }
271
272     if (selectTarget(T_TS, options) != EXIT_SUCCESS) {
273         return EXIT_FAILURE;
274     }
275
276     return EXIT_SUCCESS;
277 }
278
279 int setSerial(char *serialNumber, CCOptionValue options[]) {
280     struct ftdi_context *ftdi;
281     int f, ret = EXIT_FAILURE;
282     char *type = options[CCO_DeviceType].args;
283
284     if (!type) {
285         fprintf(stderr, "Device type not specified\n");
286         return EXIT_FAILURE;
287     }
288
289     ftdi = openDevice(options, NULL);
290     if (ftdi == NULL) {
291         return EXIT_FAILURE;
292     }
293
294     f = ftdi_eeprom_initdefaults(ftdi, (char *)"SRPOL", type, serialNumber);
295     if (f < 0) {
296         fprintf(stderr, "Unable to set eeprom strings: %d (%s)\n", f, ftdi_get_error_string(ftdi));
297         goto finish_him;
298     }
299
300     f = ftdi_set_eeprom_value(ftdi, VENDOR_ID, SAMSUNG_VENDOR);
301     if (f < 0) {
302         fprintf(stderr, "Unable to set eeprom strings: %d (%s)\n", f, ftdi_get_error_string(ftdi));
303         goto finish_him;
304     }
305
306     f = ftdi_set_eeprom_value(ftdi, PRODUCT_ID, PRODUCT);
307     if (f < 0) {
308         fprintf(stderr, "Unable to set eeprom strings: %d (%s)\n", f, ftdi_get_error_string(ftdi));
309         goto finish_him;
310     }
311
312     if (getDeviceTypeFromString(type) == CCDT_SDWIRE) {
313         f = ftdi_set_eeprom_value(ftdi, CBUS_FUNCTION_0, CBUSH_IOMODE);
314         if (f < 0) {
315             fprintf(stderr, "Unable to set eeprom value: %d (%s)\n", f, ftdi_get_error_string(ftdi));
316             goto finish_him;
317         }
318     }
319
320     if (getDeviceTypeFromString(type) == CCDT_USBMUX) {
321         f = ftdi_set_eeprom_value(ftdi, CBUS_FUNCTION_0, CBUSH_IOMODE);
322         if (f < 0) {
323             fprintf(stderr, "Unable to set eeprom value: %d (%s)\n", f, ftdi_get_error_string(ftdi));
324             goto finish_him;
325         }
326         f = ftdi_set_eeprom_value(ftdi, CBUS_FUNCTION_1, CBUSH_IOMODE);
327         if (f < 0) {
328             fprintf(stderr, "Unable to set eeprom value: %d (%s)\n", f, ftdi_get_error_string(ftdi));
329             goto finish_him;
330         }
331         f = ftdi_set_eeprom_value(ftdi, CBUS_FUNCTION_2, CBUSH_IOMODE);
332         if (f < 0) {
333             fprintf(stderr, "Unable to set eeprom value: %d (%s)\n", f, ftdi_get_error_string(ftdi));
334             goto finish_him;
335         }
336         f = ftdi_set_eeprom_value(ftdi, CBUS_FUNCTION_3, CBUSH_IOMODE);
337         if (f < 0) {
338             fprintf(stderr, "Unable to set eeprom value: %d (%s)\n", f, ftdi_get_error_string(ftdi));
339             goto finish_him;
340         }
341     }
342
343     f = ftdi_eeprom_build(ftdi);
344     if (f < 0) {
345         fprintf(stderr, "Unable to build eeprom: %d (%s)\n", f, ftdi_get_error_string(ftdi));
346         goto finish_him;
347     }
348
349     f = ftdi_write_eeprom(ftdi);
350     if (f < 0) {
351         fprintf(stderr, "Unable to write eeprom into device: %d (%s)\n", f, ftdi_get_error_string(ftdi));
352         goto finish_him;
353     }
354
355     ret = EXIT_SUCCESS;
356
357 finish_him:
358     ftdi_usb_close(ftdi);
359     ftdi_free(ftdi);
360
361     return ret;
362 }
363
364 int writePins(struct ftdi_context *ftdi, unsigned char pins) {
365     int f = ftdi_write_data(ftdi, &pins, 1);
366     if (f < 0) {
367         fprintf(stderr,"write failed for 0x%x, error %d (%s)\n", pins, f, ftdi_get_error_string(ftdi));
368         return EXIT_FAILURE;
369     }
370     return EXIT_SUCCESS;
371 }
372
373 struct ftdi_context* prepareDevice(CCOptionValue options[], unsigned char *pins, CCDeviceType *deviceType) {
374     struct ftdi_context *ftdi;
375     int f;
376
377     ftdi = openDevice(options, deviceType);
378     if (ftdi == NULL) {
379         return NULL;
380     }
381
382     if (*deviceType == CCDT_SDWIRE || *deviceType == CCDT_USBMUX) {
383         return ftdi; // None of the following steps need to be performed for this type of device.
384     }
385
386     f = ftdi_set_bitmode(ftdi, 0xFF, BITMODE_BITBANG);
387     if (f < 0) {
388         fprintf(stderr, "Unable to enable bitbang mode: %d (%s)\n", f, ftdi_get_error_string(ftdi));
389         ftdi_usb_close(ftdi);
390         ftdi_free(ftdi);
391         return NULL;
392     }
393
394     if (pins != NULL) {
395         f = ftdi_read_data(ftdi, pins, 1);
396         if (f < 0) {
397             fprintf(stderr,"read failed, error %d (%s)\n", f, ftdi_get_error_string(ftdi));
398             ftdi_usb_close(ftdi);
399             ftdi_free(ftdi);
400             return NULL;
401         }
402     }
403
404     return ftdi;
405 }
406
407 int powerOff(struct ftdi_context *ftdi, unsigned char *pins) {
408     // Turn on the coil
409     *pins |= POWER_SW_ON;
410     *pins &= ~(POWER_SW_OFF);
411     if (writePins(ftdi, *pins) != EXIT_SUCCESS)
412         return EXIT_FAILURE;
413
414     // Wait for 100ms
415     usleep(DELAY_100MS);
416
417     // Turn off the coil
418     *pins |= POWER_SW_OFF;
419     if (writePins(ftdi, *pins) != EXIT_SUCCESS)
420         return EXIT_FAILURE;
421
422     return EXIT_SUCCESS;
423 }
424
425 int powerOn(struct ftdi_context *ftdi, unsigned char *pins) {
426     // Turn on the coil
427     *pins |= POWER_SW_OFF;
428     *pins &= ~(POWER_SW_ON);
429     if (writePins(ftdi, *pins) != EXIT_SUCCESS)
430         return EXIT_FAILURE;
431
432     // Wait for 100ms
433     usleep(DELAY_100MS);
434
435     // Turn off the coil
436     *pins |= POWER_SW_ON;
437     if (writePins(ftdi, *pins) != EXIT_SUCCESS)
438         return EXIT_FAILURE;
439
440     return EXIT_SUCCESS;
441 }
442
443 int doPower(bool off, bool on, CCOptionValue options[]) {
444     unsigned char pins;
445     CCDeviceType deviceType;
446     int ret = EXIT_SUCCESS;
447
448     int period = 1000;
449     if (options[CCO_TickTime].argn > 0) {
450         period = options[CCO_TickTime].argn;
451     }
452
453     struct ftdi_context *ftdi = prepareDevice(options, &pins, &deviceType);
454     if (ftdi == NULL)
455         return EXIT_FAILURE;
456
457     if (!hasFeature(deviceType, CCF_POWERSWITCH)) {
458         fprintf(stderr,"Power switching is not available on this device.\n");
459         ret = EXIT_FAILURE;
460         goto finish_him;
461     }
462
463     ret = powerOff(ftdi, &pins);
464     if (off && (ret != EXIT_SUCCESS)) {
465         ret = EXIT_FAILURE;
466         goto finish_him;
467     }
468
469     // Wait for specified period in ms
470     usleep(period * 1000);
471
472     ret = powerOn(ftdi, &pins);
473     if (on && (ret != EXIT_SUCCESS)) {
474         ret = EXIT_FAILURE;
475         goto finish_him;
476     }
477
478 finish_him:
479     ftdi_usb_close(ftdi);
480     ftdi_free(ftdi);
481
482     return ret;
483 }
484
485 int selectTarget(Target target, CCOptionValue options[]) {
486     unsigned char pins;
487     CCDeviceType deviceType;
488     int ret = EXIT_SUCCESS;
489
490     struct ftdi_context *ftdi = prepareDevice(options, &pins, &deviceType);
491     if (ftdi == NULL)
492         return EXIT_FAILURE;
493
494     if (deviceType == CCDT_SDWIRE) {
495         unsigned char pinState = 0x00;
496         pinState |= 0xF0; // Upper half of the byte sets all pins to output (SDWire has only one bit - 0)
497         pinState |= target == T_DUT ? 0x00 : 0x01; // Lower half of the byte sets state of output pins.
498                                                    // In this particular case we care only of bit 0.
499         ret |= ftdi_set_bitmode(ftdi, pinState, BITMODE_CBUS);
500         goto finish_him;
501     }
502
503     if (deviceType == CCDT_USBMUX) {
504         unsigned char pinState = 0xF0;
505
506         if (target == T_DUT) {
507             pinState &= ~UM_DEVICE_PWR;
508             ret |= ftdi_set_bitmode(ftdi, pinState, BITMODE_CBUS);
509             usleep(DELAY_500MS);
510             pinState |= UM_DEVICE_PWR;
511             ret |= ftdi_set_bitmode(ftdi, pinState, BITMODE_CBUS);
512             usleep(DELAY_100MS);
513             pinState |= UM_DUT_LED;
514             pinState &= ~UM_SOCKET_SEL;
515             pinState &= ~UM_GP_LED;
516             ret |= ftdi_set_bitmode(ftdi, pinState, BITMODE_CBUS);
517         } else {
518             pinState &= ~UM_DUT_LED;
519             pinState &= ~UM_DEVICE_PWR;
520             ret |= ftdi_set_bitmode(ftdi, pinState, BITMODE_CBUS);
521             usleep(DELAY_500MS);
522             pinState |= UM_DEVICE_PWR;
523             ret |= ftdi_set_bitmode(ftdi, pinState, BITMODE_CBUS);
524             usleep(DELAY_100MS);
525             pinState |= UM_SOCKET_SEL;
526             pinState |= UM_GP_LED;
527             ret |= ftdi_set_bitmode(ftdi, pinState, BITMODE_CBUS);
528         }
529
530         goto finish_him;
531     }
532
533     // Currently only old SD-MUX is the other device so do the job in its style.
534     if (target == T_DUT) {
535         pins &= ~(USB_SEL);
536         pins &= ~(SOCKET_SEL);
537         if (powerOn(ftdi, &pins) != EXIT_SUCCESS) {  // Also selects USB and SD
538             ret = EXIT_FAILURE;
539             goto finish_him;
540         }
541     } else {
542         pins |= USB_SEL;
543         pins |= SOCKET_SEL;
544         if (powerOff(ftdi, &pins) != EXIT_SUCCESS) { // Also selects USB and SD
545             ret = EXIT_FAILURE;
546             goto finish_him;
547         }
548     }
549
550 finish_him:
551     ftdi_usb_close(ftdi);
552     ftdi_free(ftdi);
553
554     return ret;
555 }
556
557 int setPins(unsigned char pins, CCOptionValue options[]) {
558     CCDeviceType deviceType;
559     struct ftdi_context *ftdi = prepareDevice(options, NULL, &deviceType);
560     if (ftdi == NULL)
561         return EXIT_FAILURE;
562
563     if (options[CCO_DeviceSerial].args) {
564         pins = ~pins;
565     }
566
567     if (deviceType == CCDT_SDWIRE) {
568         // SDWire has only one pin already controlled by selectTarget function.
569         // There is no use to repeat this functionality here.
570         return EXIT_FAILURE;
571     }
572
573     printf("Write data: 0x%x\n", pins);
574
575     int ret = writePins(ftdi, pins);
576
577     ftdi_usb_close(ftdi);
578     ftdi_free(ftdi);
579
580     return ret;
581 }
582
583 int showStatus(CCOptionValue options[]) {
584         int ret = 0;
585     unsigned char pins;
586     CCDeviceType deviceType;
587     struct ftdi_context *ftdi = prepareDevice(options, &pins, &deviceType);
588     if (ftdi == NULL)
589         return EXIT_FAILURE;
590
591
592     if (deviceType == CCDT_SDWIRE) {
593        if (ftdi_read_pins(ftdi, &pins) != 0) {
594            fprintf(stderr, "Error reading pins state.\n");
595            ret = EXIT_FAILURE;
596            goto finish_him;
597        }
598        fprintf(stdout, "SD connected to: %s\n", pins & SOCKET_SEL ? "TS" : "DUT");
599        goto finish_him;
600     }
601
602     if (deviceType == CCDT_USBMUX) {
603        if (ftdi_read_pins(ftdi, &pins) != 0) {
604            fprintf(stderr, "Error reading pins state.\n");
605            ret = EXIT_FAILURE;
606            goto finish_him;
607        }
608
609        if (pins == 0xff) {
610            fprintf(stdout, "Device not initialized!\n");
611            goto finish_him;
612        }
613
614        fprintf(stdout, "SD connected to: %s\n", pins & UM_SOCKET_SEL ? "TS" : "DUT");
615        goto finish_him;
616     }
617
618     // Currently only old SD-MUX is the other device so do the job in its style.
619     if (!((pins & POWER_SW_ON) && (pins & POWER_SW_OFF))) {
620         fprintf(stdout, "Device not initialized!\n");
621         goto finish_him;
622     }
623
624     fprintf(stdout, "USB connected to: %s\n", pins & USB_SEL ? "TS" : "DUT");
625     fprintf(stdout, "SD connected to: %s\n", pins & SOCKET_SEL ? "TS" : "DUT");
626
627 finish_him:
628     ftdi_usb_close(ftdi);
629     ftdi_free(ftdi);
630
631     return ret;
632 }
633
634 int setDyPer(CCCommand cmd, CCOptionValue options[]) {
635     unsigned char pins;
636     bool switchOn;
637     CCDeviceType deviceType;
638     int ret = EXIT_SUCCESS, dyper;
639
640     struct ftdi_context *ftdi = prepareDevice(options, &pins, &deviceType);
641     if (ftdi == NULL)
642         return EXIT_FAILURE;
643
644     if (!hasFeature(deviceType, CCF_DYPERS)) {
645         fprintf(stderr,"DyPers are not available on this device.\n");
646         return EXIT_FAILURE;
647     }
648
649     #define STRON "ON"
650     #define STROFF "OFF"
651
652     if (strcasecmp(STRON, options[CCO_DyPer].args) == 0) {
653       switchOn = true;
654     } else if (strcasecmp(STROFF, options[CCO_DyPer].args) == 0) {
655       switchOn = false;
656     } else {
657       fprintf(stderr,"Invalid DyPer argument! Use \"on\" or \"off\".\n");
658       goto finish_him;
659     }
660
661     dyper = cmd == CCC_DyPer1 ? DYPER1 : DYPER2;
662     pins = switchOn ? pins | dyper : pins & ~dyper;
663
664     if (writePins(ftdi, pins) != EXIT_SUCCESS)
665         ret = EXIT_FAILURE;
666
667 finish_him:
668     ftdi_usb_close(ftdi);
669     ftdi_free(ftdi);
670
671     return ret;
672 }
673
674 int parseArguments(int argc, const char **argv, CCCommand *cmd, int *arg, char *args, size_t argsLen,
675                    CCOptionValue options[]) {
676     int c;
677     char *serial = NULL;
678
679     poptContext optCon;
680     struct poptOption optionsTable[] = {
681             // Commands
682             { "list", 'l', POPT_ARG_NONE, NULL, 'l', "lists all sd-mux devices connected to PC", NULL },
683             { "info", 'i', POPT_ARG_NONE, NULL, 'i', "displays info about device", "serial number" },
684             { "show-serial", 'o', POPT_ARG_NONE, NULL, 'o', "displays serial number of given device", NULL },
685             { "set-serial", 'r', POPT_ARG_STRING, &serial, 'r', "writes serial number to given device", NULL },
686             { "init", 't', POPT_ARG_NONE, NULL, 't', "initialize target board", NULL },
687             { "dut", 'd', POPT_ARG_NONE, NULL, 'd', "connects SD card and USB to the target board", NULL },
688             { "ts", 's', POPT_ARG_NONE, NULL, 's', "connects SD card and USB to the test server", NULL },
689             { "pins", 'p', POPT_ARG_INT, arg, 'p', "write pin state in bitbang mode", NULL },
690             { "tick", 'c', POPT_ARG_NONE, NULL, 'c', "turn off and on power supply of DUT", NULL },
691             { "status", 'u', POPT_ARG_NONE, NULL, 'u', "show current status: DUT or TS or NOINIT", NULL },
692             { "dyper1", 'y', POPT_ARG_STRING, &options[CCO_DyPer].args, 'y', "Connect or disconnect terminals of 1st dynamic jumper; STRING = \"on\" or \"off\"", NULL },
693             { "dyper2", 'z', POPT_ARG_STRING, &options[CCO_DyPer].args, 'z', "Connect or disconnect terminals of 2nd dynamic jumper; STRING = \"on\" or \"off\"", NULL },
694             // Options
695             { "tick-time", 'm', POPT_ARG_INT, &options[CCO_TickTime].argn, 'm', "set time delay for 'tick' command",
696                     NULL },
697             { "device-id", 'v', POPT_ARG_INT, &options[CCO_DeviceId].argn, 'v', "use device with given id", NULL },
698             { "device-serial", 'e', POPT_ARG_STRING, &options[CCO_DeviceSerial].args, 'e',
699                     "use device with given serial number", NULL },
700             { "device-type", 'k', POPT_ARG_STRING, &options[CCO_DeviceType].args, 'k',
701                     "make the device of this type", NULL },
702             { "vendor", 'x', POPT_ARG_INT, &options[CCO_Vendor].argn, 'x', "use device with given vendor id", NULL },
703             { "product", 'a', POPT_ARG_INT, &options[CCO_Product].argn, 'a', "use device with given product id", NULL },
704             { "invert", 'n', POPT_ARG_NONE, NULL, 'n', "invert bits for --pins command", NULL },
705             POPT_AUTOHELP
706             { NULL, 0, 0, NULL, 0, NULL, NULL }
707     };
708
709     optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
710     poptSetOtherOptionHelp(optCon, "command");
711     if (argc < 2) {
712         poptPrintUsage(optCon, stderr, 0);
713         poptFreeContext(optCon);
714         return EXIT_SUCCESS;
715     }
716     /* Now do options processing, get portname */
717     while ((c = poptGetNextOpt(optCon)) >= 0) {
718         switch (c) {
719             case 'l':
720                 *cmd = CCC_List;
721                 break;
722             case 'i':
723                 *cmd = CCC_Info;
724                 break;
725             case 'o':
726                 *cmd = CCC_ShowSerial;
727                 break;
728             case 'r':
729                 *cmd = CCC_SetSerial;
730                 break;
731             case 't':
732                 *cmd = CCC_Init;
733                 break;
734             case 'd':
735                 *cmd = CCC_DUT;
736                 break;
737             case 's':
738                 *cmd = CCC_TS;
739                 break;
740             case 'p':
741                 *cmd = CCC_Pins;
742                 break;
743             case 'c':
744                 *cmd = CCC_Tick;
745                 break;
746             case 'u':
747                 *cmd = CCC_Status;
748                 break;
749             case 'y':
750                 *cmd = CCC_DyPer1;
751                 break;
752             case 'z':
753                 *cmd = CCC_DyPer2;
754                 break;
755             case 'n':
756                 options[CCO_BitsInvert].argn = 1;
757                 break;
758         }
759     }
760
761     if (serial)
762         snprintf(args, argsLen, "%s", serial);
763     free(serial);
764
765     if (c < -1) {
766         fprintf(stderr, "%s: %s\n", poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(c));
767         poptFreeContext(optCon);
768         return EXIT_FAILURE;
769     }
770
771     poptFreeContext(optCon);
772
773     return EXIT_SUCCESS;
774 }
775
776 int main(int argc, const char **argv) {
777     CCCommand cmd = CCC_None;
778     int arg;
779     char args[64];
780     CCOptionValue options[CCO_MAX];
781     memset(&options, 0, sizeof(options));
782     options[CCO_DeviceId].argn = -1;
783     options[CCO_Vendor].argn = SAMSUNG_VENDOR;
784     options[CCO_Product].argn = PRODUCT;
785
786     if (parseArguments(argc, argv, &cmd, &arg, args, sizeof(args), options) != EXIT_SUCCESS) {
787         return EXIT_FAILURE;
788     }
789
790     switch (cmd) {
791     case CCC_None:
792         fprintf(stderr, "No command specified\n");
793         return EXIT_FAILURE;
794     case CCC_List:
795         return listDevices(options);
796     case CCC_Info:
797         return showInfo(options);
798     case CCC_ShowSerial:
799         return listDevices(options);
800     case CCC_SetSerial:
801         return setSerial(args, options);
802     case CCC_Init:
803         return doInit(options);
804     case CCC_DUT:
805         return selectTarget(T_DUT, options);
806     case CCC_TS:
807         return selectTarget(T_TS, options);
808     case CCC_Tick:
809         return doPower(true, true, options);
810     case CCC_Pins:
811         return setPins((unsigned char)arg, options);
812     case CCC_DyPer1:
813     case CCC_DyPer2:
814         return setDyPer(cmd, options);
815     case CCC_Status:
816         return showStatus(options);
817     }
818
819     return EXIT_SUCCESS;
820 }