2 * Copyright (c) 2016 -2018 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
18 * @author Adam Malinowski <a.malinowsk2@partner.samsung.com>
19 * @brief Main sd-mux-ctrl file
28 #include <libftdi1/ftdi.h>
30 #define PRODUCT 0x6001
31 #define SAMSUNG_VENDOR 0x04e8
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)
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)
48 #define DELAY_100MS 100000
49 #define DELAY_500MS 500000
51 #define CCDT_SDMUX_STR "sd-mux"
52 #define CCDT_SDWIRE_STR "sd-wire"
53 #define CCDT_USBMUX_STR "usb-mux"
55 #define STRING_SIZE 128
105 union CCOptionValue {
110 int doPower(bool off, bool on, CCOptionValue options[]);
111 int selectTarget(Target target, CCOptionValue options[]);
113 CCDeviceType getDeviceTypeFromString(char *deviceTypeStr) {
114 if (strcmp(CCDT_SDMUX_STR, deviceTypeStr) == 0) {
118 if (strcmp(CCDT_SDWIRE_STR, deviceTypeStr) == 0) {
122 if (strcmp(CCDT_USBMUX_STR, deviceTypeStr) == 0) {
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
136 if (deviceType >= CCDT_MAX || feature >= CCF_MAX)
139 return featureMatrix[deviceType][feature];
142 int listDevices(CCOptionValue options[]) {
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;
149 if ((ftdi = ftdi_new()) == 0) {
150 fprintf(stderr, "ftdi_new failed\n");
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));
160 if (options[CCO_DeviceId].argn == -1) {
161 printf("Number of FTDI devices found: %d\n", fret);
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;
173 if (options[CCO_DeviceId].argn == -1) {
174 printf("Dev: %d, Manufacturer: %s, Serial: %s, Description: %s\n", i,
175 manufacturer, serial, description);
177 printf("%s", serial);
180 curdev = curdev->next;
184 ftdi_list_free(&devlist);
190 struct ftdi_context* openDevice(CCOptionValue options[], CCDeviceType *deviceType) {
191 struct ftdi_context *ftdi = NULL;
193 char product[STRING_SIZE + 1];
194 CCDeviceType tmpDeviceType;
196 if ((options[CCO_DeviceSerial].args == NULL) && (options[CCO_DeviceId].argn < 0)) {
197 fprintf(stderr, "No serial number or device id provided!\n");
201 if ((ftdi = ftdi_new()) == 0) {
202 fprintf(stderr, "ftdi_new failed\n");
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);
209 fret = ftdi_usb_open_desc_index(ftdi, options[CCO_Vendor].argn, options[CCO_Product].argn, NULL, NULL, options[CCO_DeviceId].argn);
212 fprintf(stderr, "Unable to open ftdi device: %d (%s)\n", fret, ftdi_get_error_string(ftdi));
216 fret = ftdi_read_eeprom(ftdi);
218 fprintf(stderr, "Unable to read ftdi eeprom: %d (%s)\n", fret, ftdi_get_error_string(ftdi));
222 fret = ftdi_eeprom_decode(ftdi, 0);
224 fprintf(stderr, "Unable to decode ftdi eeprom: %d (%s)\n", fret, ftdi_get_error_string(ftdi));
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");
235 *deviceType = tmpDeviceType;
241 ftdi_usb_close(ftdi);
247 int showInfo(CCOptionValue options[]) {
248 struct ftdi_context *ftdi;
249 int fret, ret = EXIT_SUCCESS;
251 ftdi = openDevice(options, NULL);
256 fret = ftdi_eeprom_decode(ftdi, 1);
258 fprintf(stderr, "Unable to decode ftdi eeprom: %d (%s)\n", fret, ftdi_get_error_string(ftdi));
260 ftdi_usb_close(ftdi);
267 int doInit(CCOptionValue options[]) {
268 if (doPower(true, false, options) != EXIT_SUCCESS) {
272 if (selectTarget(T_TS, options) != EXIT_SUCCESS) {
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;
285 fprintf(stderr, "Device type not specified\n");
289 ftdi = openDevice(options, NULL);
294 f = ftdi_eeprom_initdefaults(ftdi, (char *)"SRPOL", type, serialNumber);
296 fprintf(stderr, "Unable to set eeprom strings: %d (%s)\n", f, ftdi_get_error_string(ftdi));
300 f = ftdi_set_eeprom_value(ftdi, VENDOR_ID, SAMSUNG_VENDOR);
302 fprintf(stderr, "Unable to set eeprom strings: %d (%s)\n", f, ftdi_get_error_string(ftdi));
306 f = ftdi_set_eeprom_value(ftdi, PRODUCT_ID, PRODUCT);
308 fprintf(stderr, "Unable to set eeprom strings: %d (%s)\n", f, ftdi_get_error_string(ftdi));
312 if (getDeviceTypeFromString(type) == CCDT_SDWIRE) {
313 f = ftdi_set_eeprom_value(ftdi, CBUS_FUNCTION_0, CBUSH_IOMODE);
315 fprintf(stderr, "Unable to set eeprom value: %d (%s)\n", f, ftdi_get_error_string(ftdi));
320 if (getDeviceTypeFromString(type) == CCDT_USBMUX) {
321 f = ftdi_set_eeprom_value(ftdi, CBUS_FUNCTION_0, CBUSH_IOMODE);
323 fprintf(stderr, "Unable to set eeprom value: %d (%s)\n", f, ftdi_get_error_string(ftdi));
326 f = ftdi_set_eeprom_value(ftdi, CBUS_FUNCTION_1, CBUSH_IOMODE);
328 fprintf(stderr, "Unable to set eeprom value: %d (%s)\n", f, ftdi_get_error_string(ftdi));
331 f = ftdi_set_eeprom_value(ftdi, CBUS_FUNCTION_2, CBUSH_IOMODE);
333 fprintf(stderr, "Unable to set eeprom value: %d (%s)\n", f, ftdi_get_error_string(ftdi));
336 f = ftdi_set_eeprom_value(ftdi, CBUS_FUNCTION_3, CBUSH_IOMODE);
338 fprintf(stderr, "Unable to set eeprom value: %d (%s)\n", f, ftdi_get_error_string(ftdi));
343 f = ftdi_eeprom_build(ftdi);
345 fprintf(stderr, "Unable to build eeprom: %d (%s)\n", f, ftdi_get_error_string(ftdi));
349 f = ftdi_write_eeprom(ftdi);
351 fprintf(stderr, "Unable to write eeprom into device: %d (%s)\n", f, ftdi_get_error_string(ftdi));
358 ftdi_usb_close(ftdi);
364 int writePins(struct ftdi_context *ftdi, unsigned char pins) {
365 int f = ftdi_write_data(ftdi, &pins, 1);
367 fprintf(stderr,"write failed for 0x%x, error %d (%s)\n", pins, f, ftdi_get_error_string(ftdi));
373 struct ftdi_context* prepareDevice(CCOptionValue options[], unsigned char *pins, CCDeviceType *deviceType) {
374 struct ftdi_context *ftdi;
377 ftdi = openDevice(options, deviceType);
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.
386 f = ftdi_set_bitmode(ftdi, 0xFF, BITMODE_BITBANG);
388 fprintf(stderr, "Unable to enable bitbang mode: %d (%s)\n", f, ftdi_get_error_string(ftdi));
389 ftdi_usb_close(ftdi);
395 f = ftdi_read_data(ftdi, pins, 1);
397 fprintf(stderr,"read failed, error %d (%s)\n", f, ftdi_get_error_string(ftdi));
398 ftdi_usb_close(ftdi);
407 int powerOff(struct ftdi_context *ftdi, unsigned char *pins) {
409 *pins |= POWER_SW_ON;
410 *pins &= ~(POWER_SW_OFF);
411 if (writePins(ftdi, *pins) != EXIT_SUCCESS)
418 *pins |= POWER_SW_OFF;
419 if (writePins(ftdi, *pins) != EXIT_SUCCESS)
425 int powerOn(struct ftdi_context *ftdi, unsigned char *pins) {
427 *pins |= POWER_SW_OFF;
428 *pins &= ~(POWER_SW_ON);
429 if (writePins(ftdi, *pins) != EXIT_SUCCESS)
436 *pins |= POWER_SW_ON;
437 if (writePins(ftdi, *pins) != EXIT_SUCCESS)
443 int doPower(bool off, bool on, CCOptionValue options[]) {
445 CCDeviceType deviceType;
446 int ret = EXIT_SUCCESS;
449 if (options[CCO_TickTime].argn > 0) {
450 period = options[CCO_TickTime].argn;
453 struct ftdi_context *ftdi = prepareDevice(options, &pins, &deviceType);
457 if (!hasFeature(deviceType, CCF_POWERSWITCH)) {
458 fprintf(stderr,"Power switching is not available on this device.\n");
463 ret = powerOff(ftdi, &pins);
464 if (off && (ret != EXIT_SUCCESS)) {
469 // Wait for specified period in ms
470 usleep(period * 1000);
472 ret = powerOn(ftdi, &pins);
473 if (on && (ret != EXIT_SUCCESS)) {
479 ftdi_usb_close(ftdi);
485 int selectTarget(Target target, CCOptionValue options[]) {
487 CCDeviceType deviceType;
488 int ret = EXIT_SUCCESS;
490 struct ftdi_context *ftdi = prepareDevice(options, &pins, &deviceType);
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);
503 if (deviceType == CCDT_USBMUX) {
504 unsigned char pinState = 0xF0;
506 if (target == T_DUT) {
507 pinState &= ~UM_DEVICE_PWR;
508 ret |= ftdi_set_bitmode(ftdi, pinState, BITMODE_CBUS);
510 pinState |= UM_DEVICE_PWR;
511 ret |= ftdi_set_bitmode(ftdi, pinState, BITMODE_CBUS);
513 pinState |= UM_DUT_LED;
514 pinState &= ~UM_SOCKET_SEL;
515 pinState &= ~UM_GP_LED;
516 ret |= ftdi_set_bitmode(ftdi, pinState, BITMODE_CBUS);
518 pinState &= ~UM_DUT_LED;
519 pinState &= ~UM_DEVICE_PWR;
520 ret |= ftdi_set_bitmode(ftdi, pinState, BITMODE_CBUS);
522 pinState |= UM_DEVICE_PWR;
523 ret |= ftdi_set_bitmode(ftdi, pinState, BITMODE_CBUS);
525 pinState |= UM_SOCKET_SEL;
526 pinState |= UM_GP_LED;
527 ret |= ftdi_set_bitmode(ftdi, pinState, BITMODE_CBUS);
533 // Currently only old SD-MUX is the other device so do the job in its style.
534 if (target == T_DUT) {
536 pins &= ~(SOCKET_SEL);
537 if (powerOn(ftdi, &pins) != EXIT_SUCCESS) { // Also selects USB and SD
544 if (powerOff(ftdi, &pins) != EXIT_SUCCESS) { // Also selects USB and SD
551 ftdi_usb_close(ftdi);
557 int setPins(unsigned char pins, CCOptionValue options[]) {
558 CCDeviceType deviceType;
559 struct ftdi_context *ftdi = prepareDevice(options, NULL, &deviceType);
563 if (options[CCO_DeviceSerial].args) {
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.
573 printf("Write data: 0x%x\n", pins);
575 int ret = writePins(ftdi, pins);
577 ftdi_usb_close(ftdi);
583 int showStatus(CCOptionValue options[]) {
586 CCDeviceType deviceType;
587 struct ftdi_context *ftdi = prepareDevice(options, &pins, &deviceType);
592 if (deviceType == CCDT_SDWIRE) {
593 if (ftdi_read_pins(ftdi, &pins) != 0) {
594 fprintf(stderr, "Error reading pins state.\n");
598 fprintf(stdout, "SD connected to: %s\n", pins & SOCKET_SEL ? "TS" : "DUT");
602 if (deviceType == CCDT_USBMUX) {
603 if (ftdi_read_pins(ftdi, &pins) != 0) {
604 fprintf(stderr, "Error reading pins state.\n");
610 fprintf(stdout, "Device not initialized!\n");
614 fprintf(stdout, "SD connected to: %s\n", pins & UM_SOCKET_SEL ? "TS" : "DUT");
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");
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");
628 ftdi_usb_close(ftdi);
634 int setDyPer(CCCommand cmd, CCOptionValue options[]) {
637 CCDeviceType deviceType;
638 int ret = EXIT_SUCCESS, dyper;
640 struct ftdi_context *ftdi = prepareDevice(options, &pins, &deviceType);
644 if (!hasFeature(deviceType, CCF_DYPERS)) {
645 fprintf(stderr,"DyPers are not available on this device.\n");
652 if (strcasecmp(STRON, options[CCO_DyPer].args) == 0) {
654 } else if (strcasecmp(STROFF, options[CCO_DyPer].args) == 0) {
657 fprintf(stderr,"Invalid DyPer argument! Use \"on\" or \"off\".\n");
661 dyper = cmd == CCC_DyPer1 ? DYPER1 : DYPER2;
662 pins = switchOn ? pins | dyper : pins & ~dyper;
664 if (writePins(ftdi, pins) != EXIT_SUCCESS)
668 ftdi_usb_close(ftdi);
674 int parseArguments(int argc, const char **argv, CCCommand *cmd, int *arg, char *args, size_t argsLen,
675 CCOptionValue options[]) {
680 struct poptOption optionsTable[] = {
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 },
695 { "tick-time", 'm', POPT_ARG_INT, &options[CCO_TickTime].argn, 'm', "set time delay for 'tick' command",
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 },
706 { NULL, 0, 0, NULL, 0, NULL, NULL }
709 optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
710 poptSetOtherOptionHelp(optCon, "command");
712 poptPrintUsage(optCon, stderr, 0);
713 poptFreeContext(optCon);
716 /* Now do options processing, get portname */
717 while ((c = poptGetNextOpt(optCon)) >= 0) {
726 *cmd = CCC_ShowSerial;
729 *cmd = CCC_SetSerial;
756 options[CCO_BitsInvert].argn = 1;
762 snprintf(args, argsLen, "%s", serial);
766 fprintf(stderr, "%s: %s\n", poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(c));
767 poptFreeContext(optCon);
771 poptFreeContext(optCon);
776 int main(int argc, const char **argv) {
777 CCCommand cmd = CCC_None;
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;
786 if (parseArguments(argc, argv, &cmd, &arg, args, sizeof(args), options) != EXIT_SUCCESS) {
792 fprintf(stderr, "No command specified\n");
795 return listDevices(options);
797 return showInfo(options);
799 return listDevices(options);
801 return setSerial(args, options);
803 return doInit(options);
805 return selectTarget(T_DUT, options);
807 return selectTarget(T_TS, options);
809 return doPower(true, true, options);
811 return setPins((unsigned char)arg, options);
814 return setDyPer(cmd, options);
816 return showStatus(options);