1char *version="1.0.0";
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <assert.h>
7#include <signal.h>
8#include <ctype.h>
9#include <getopt.h>
10#include <syslog.h>
11
12#include <usb.h>
13#include "lcdoshift.h"
14
15#define LINE_DIM 1024
16#define BUF_SIZE 4096
17#define DESCR_MAX 129
18
19#define SHOW_PROGRESS if (show_progress) printf
20
21int write_bulk(int endpoint, char *message, int length);
22int read_bulk(int endpoint, char *buffer, int length);
23
24int find_first_bulk_output_endpoint(struct usb_device *dev);
25int find_first_bulk_input_endpoint(struct usb_device *dev);
26
27char *TempPP=NULL;
28
29struct usb_device *dev;
30struct usb_dev_handle *devh;
31
32int DefaultVendor=0x1c9e, DefaultProduct=0, TargetVendor=0, TargetProduct=0, TargetClass=0;
33int MessageEndpoint=0, ResponseEndpoint=0, defaultClass=0;
34int targetDeviceCount=0;
35int devnum=-1, busnum=-1;
36int ret;
37
38char DetachStorageOnly=0, HuaweiMode=0, SierraMode=0, SonyMode=0, GCTMode=0;
39char verbose=0, show_progress=1, ResetUSB=0, CheckSuccess=0, config_read=0;
40char NeedResponse=0, InquireDevice=0, sysmode=0;
41
42char imanufact[DESCR_MAX], iproduct[DESCR_MAX], iserial[DESCR_MAX];
43
44char MessageContent[LINE_DIM] = "55534243123456780000000080000606f50402527000000000000000000000";
45char TargetProductList[LINE_DIM];
46char ByteString[LINE_DIM/2];
47char buffer[BUF_SIZE];
48
49// Settable Interface and Configuration (for debugging mostly) (jmw)
50int Interface = 0, Configuration = -1, AltSetting = -1;
51
52
53static struct option long_options[] = {
54	{"help",				no_argument, 0, 'h'},
55	{"version",				no_argument, 0, 'e'},
56	{"default-vendor",		required_argument, 0, 'v'},
57	{"default-product",		required_argument, 0, 'p'},
58	{"target-vendor",		required_argument, 0, 'V'},
59	{"target-product",		required_argument, 0, 'P'},
60	{"target-class",		required_argument, 0, 'C'},
61	{"message-endpoint",	required_argument, 0, 'm'},
62	{"message-content",		required_argument, 0, 'M'},
63	{"response-endpoint",	required_argument, 0, 'r'},
64	{"detach-only",			no_argument, 0, 'd'},
65	{"huawei-mode",			no_argument, 0, 'H'},
66	{"sierra-mode",			no_argument, 0, 'S'},
67	{"sony-mode",			no_argument, 0, 'O'},
68	{"gct-mode",			no_argument, 0, 'G'},
69	{"need-response",		no_argument, 0, 'n'},
70	{"reset-usb",			no_argument, 0, 'R'},
71	{"config",				required_argument, 0, 'c'},
72	{"verbose",				no_argument, 0, 'W'},
73	{"quiet",				no_argument, 0, 'Q'},
74	{"sysmode",				no_argument, 0, 'D'},
75	{"no-inquire",			no_argument, 0, 'I'},
76	{"check-success",		required_argument, 0, 's'},
77	{"interface",			required_argument, 0, 'i'},
78	{"configuration",		required_argument, 0, 'u'},
79	{"altsetting",			required_argument, 0, 'a'},
80	{0, 0, 0, 0}
81};
82
83
84void readConfigFile(const char *configFilename)
85{
86	if (verbose) printf("Reading config file: %s\n", configFilename);
87	ParseParamHex(configFilename, TargetVendor);
88	ParseParamHex(configFilename, TargetProduct);
89	ParseParamString(configFilename, TargetProductList);
90	ParseParamHex(configFilename, TargetClass);
91	ParseParamHex(configFilename, DefaultVendor);
92	ParseParamHex(configFilename, DefaultProduct);
93	ParseParamBool(configFilename, DetachStorageOnly);
94	ParseParamBool(configFilename, HuaweiMode);
95	ParseParamBool(configFilename, SierraMode);
96	ParseParamBool(configFilename, SonyMode);
97	ParseParamBool(configFilename, GCTMode);
98	ParseParamHex(configFilename, MessageEndpoint);
99	ParseParamString(configFilename, MessageContent);
100	ParseParamHex(configFilename, NeedResponse);
101	ParseParamHex(configFilename, ResponseEndpoint);
102	ParseParamHex(configFilename, ResetUSB);
103	ParseParamHex(configFilename, InquireDevice);
104	ParseParamInt(configFilename, CheckSuccess);
105	ParseParamHex(configFilename, Interface);
106	ParseParamHex(configFilename, Configuration);
107	ParseParamHex(configFilename, AltSetting);
108
109	// TargetProductList has priority over TargetProduct
110	if (strlen(TargetProductList))
111		TargetProduct = 0;
112
113	config_read = 1;
114}
115
116
117void printConfig()
118{
119	printf ("DefaultVendor=  0x%04x\n",			DefaultVendor);
120	printf ("DefaultProduct= 0x%04x\n",			DefaultProduct);
121	if ( TargetVendor )
122		printf ("TargetVendor=   0x%04x\n",		TargetVendor);
123	else
124		printf ("TargetVendor=   not set\n");
125	if ( TargetProduct )
126		printf ("TargetProduct=  0x%04x\n",		TargetProduct);
127	else
128		printf ("TargetProduct=  not set\n");
129	if ( strlen(TargetProductList) )
130		printf ("TargetProductList=%s\n",		TargetProductList);
131//	else
132//		printf ("TargetProduct=  not set\n");
133	if ( TargetClass )
134		printf ("TargetClass=    0x%02x\n",		TargetClass);
135	else
136		printf ("TargetClass=    not set\n");
137	printf ("\nDetachStorageOnly=%i\n",	(int)DetachStorageOnly);
138	printf ("HuaweiMode=%i\n",			(int)HuaweiMode);
139	printf ("SierraMode=%i\n",			(int)SierraMode);
140	printf ("SonyMode=%i\n",			(int)SonyMode);
141	printf ("GCTMode=%i\n",			(int)GCTMode);
142	if ( MessageEndpoint )
143		printf ("MessageEndpoint=0x%02x\n",	MessageEndpoint);
144	else
145		printf ("MessageEndpoint= not set\n");
146	if ( strlen(MessageContent) )
147		printf ("MessageContent=\"%s\"\n",	MessageContent);
148	else
149		printf ("MessageContent= not set\n");
150	printf ("NeedResponse=%i\n",		(int)NeedResponse);
151	if ( ResponseEndpoint )
152		printf ("ResponseEndpoint=0x%02x\n",	ResponseEndpoint);
153	else
154		printf ("ResponseEndpoint= not set\n");
155	printf ("Interface=0x%02x\n",			Interface);
156	if ( Configuration > -1 )
157		printf ("Configuration=0x%02x\n",	Configuration);
158	if ( AltSetting > -1 )
159		printf ("AltSetting=0x%02x\n",	AltSetting);
160	if ( InquireDevice )
161		printf ("\nInquireDevice enabled (default)\n");
162	else
163		printf ("\nInquireDevice disabled\n");
164	if ( CheckSuccess )
165		printf ("Success check enabled, max. wait time %d seconds\n", CheckSuccess);
166	else
167		printf ("Success check disabled\n");
168	if ( sysmode )
169		printf ("System integration mode enabled\n");
170	else
171		printf ("System integration mode disabled\n");
172	printf ("\n");
173}
174
175
176int readArguments(int argc, char **argv)
177{
178	int c, option_index = 0, count=0;
179	if(argc==1) return 0;
180
181	while (1)
182	{
183		c = getopt_long (argc, argv, "heWQDndHSOGRIv:p:V:P:C:m:M:r:c:i:u:a:s:",
184						long_options, &option_index);
185
186		/* Detect the end of the options. */
187		if (c == -1)
188			break;
189		count++;
190		switch (c)
191		{
192			case 'R': ResetUSB = 1; break;
193			case 'v': DefaultVendor = strtol(optarg, NULL, 16); break;
194			case 'p': DefaultProduct = strtol(optarg, NULL, 16); break;
195			case 'V': TargetVendor = strtol(optarg, NULL, 16); break;
196			case 'P': TargetProduct = strtol(optarg, NULL, 16); break;
197			case 'C': TargetClass = strtol(optarg, NULL, 16); break;
198			case 'm': MessageEndpoint = strtol(optarg, NULL, 16); break;
199			case 'M': strcpy(MessageContent, optarg); break;
200			case 'n': NeedResponse = 1; break;
201			case 'r': ResponseEndpoint = strtol(optarg, NULL, 16); break;
202			case 'd': DetachStorageOnly = 1; break;
203			case 'H': HuaweiMode = 1; break;
204			case 'S': SierraMode = 1; break;
205			case 'O': SonyMode = 1; break;
206			case 'G': GCTMode = 1; break;
207			case 'c': readConfigFile(optarg); break;
208			case 'W': verbose = 1; show_progress = 1; count--; break;
209			case 'Q': show_progress = 0; verbose = 0; count--; break;
210			case 'D': sysmode = 1; count--; break;
211			case 's': CheckSuccess = strtol(optarg, NULL, 16); count--; break;
212			case 'I': InquireDevice = 0; break;
213
214			case 'i': Interface = strtol(optarg, NULL, 16); break;
215			case 'u': Configuration = strtol(optarg, NULL, 16); break;
216			case 'a': AltSetting = strtol(optarg, NULL, 16); break;
217
218			case 'e':
219				printVersion();
220				exit(0);
221				break;
222			case 'h':
223				printVersion();
224				printf ("Usage: usb-modeswitch [-hvpVPmMrdHn] [-c filename]\n\n");
225				printf (" -h, --help                    this help\n");
226				printf (" -e, --version                 print version number and exit\n");
227				printf (" -v, --default-vendor NUM      vendor ID to look for (mandatory)\n");
228				printf (" -p, --default-product NUM     product ID to look for (mandatory)\n");
229				printf (" -V, --target-vendor NUM       target vendor (optional, for success check)\n");
230				printf (" -P, --target-product NUM      target model (optional, for success check)\n");
231				printf (" -C, --target-class NUM        target device class\n");
232				printf (" -m, --message-endpoint NUM    where to direct the message (optional)\n");
233				printf (" -M, --message-content <msg>   command to send (hex number as string)\n");
234				printf (" -n, --need-response           read a response to the message transfer\n");
235				printf (" -r, --response-endpoint NUM   where from read the response (optional)\n");
236				printf (" -d, --detach-only             just detach the storage driver\n");
237				printf (" -H, --huawei-mode             apply a special procedure\n");
238				printf (" -S, --sierra-mode             apply a special procedure\n");
239				printf (" -O, --sony-mode               apply a special procedure\n");
240				printf (" -G, --gct-mode                apply a special procedure\n");
241				printf (" -R, --reset-usb               reset the device in the end\n");
242				printf (" -c, --config <filename>       load different config file\n");
243				printf (" -Q, --quiet                   don't show progress or error messages\n");
244				printf (" -W, --verbose                 print all settings before running\n");
245				printf (" -D, --sysmode                 specific result and syslog message\n");
246				printf (" -s, --success NUM             check switching result after NUM secs\n");
247				printf (" -I, --no-inquire              do not get device details (default on)\n\n");
248				printf (" -i, --interface NUM           select initial USB interface (default 0)\n");
249				printf (" -u, --configuration NUM       select USB configuration\n");
250				printf (" -a, --altsetting NUM          select alternative USB interface setting\n\n");
251				exit(0);
252				break;
253
254			default: //Unsupported - error message has already been printed
255				printf ("\n");
256				exit(1);
257		}
258	}
259
260	return count;
261}
262
263
264int main(int argc, char **argv)
265{
266	int numDefaults = 0, specialMode = 0, sonySuccess = 0;
267
268	/*
269	 * Parameter parsing, USB preparation/diagnosis, plausibility checks
270	 */
271
272	// Check command arguments, use params instead of config file when given
273	switch (readArguments(argc, argv)) {
274		case 0:						// no argument or -W, -q or -s
275			readConfigFile("/etc/usb-modeswitch.conf");
276			break;
277		default:					// one or more arguments except -W, -q or -s
278			if (!config_read)		// if arguments contain -c, the config file was already processed
279				if (verbose) printf("Taking all parameters from the command line\n\n");
280	}
281
282	if (verbose)
283		printVersion();
284
285	sleep(1);
286	if (verbose)
287		printConfig();
288
289	// libusb initialization
290	usb_init();
291
292	if (verbose)
293		usb_set_debug(15);
294
295	usb_find_busses();
296	usb_find_devices();
297
298	// Plausibility checks. The default IDs are mandatory
299	if (!(DefaultVendor && DefaultProduct)) {
300		SHOW_PROGRESS("No default vendor/product ID given. Aborting.\n\n");
301		exit(1);
302	}
303	if (strlen(MessageContent)) {
304		if (strlen(MessageContent) % 2 != 0) {
305			fprintf(stderr, "Error: MessageContent hex string has uneven length. Aborting.\n\n");
306			exit(1);
307		}
308		if ( hexstr2bin(MessageContent, ByteString, strlen(MessageContent)/2) == -1) {
309			fprintf(stderr, "Error: MessageContent %s\n is not a hex string. Aborting.\n\n", MessageContent);
310			exit(1);
311		}
312	}
313	SHOW_PROGRESS("\n");
314
315	if (show_progress)
316		if (CheckSuccess && !(TargetVendor || TargetProduct || strlen(TargetProductList)) && !TargetClass)
317			printf("Note: target parameter missing; success check limited\n");
318
319	// Count existing target devices (remember for success check)
320	if (TargetVendor || TargetClass) {
321		SHOW_PROGRESS("Looking for target devices ...\n");
322		search_devices(&targetDeviceCount, TargetVendor, TargetProduct, TargetProductList, TargetClass);
323		if (targetDeviceCount) {
324			SHOW_PROGRESS(" Found devices in target mode or class (%d)\n", targetDeviceCount);
325		} else
326			SHOW_PROGRESS(" No devices in target mode or class found\n");
327	}
328
329	// Count default devices, return the last one found
330	SHOW_PROGRESS("Looking for default devices ...\n");
331	dev = search_devices(&numDefaults, DefaultVendor, DefaultProduct, "\0", TargetClass);
332	if (numDefaults) {
333		SHOW_PROGRESS(" Found default devices (%d)\n", numDefaults);
334		if (TargetClass && !(TargetVendor || TargetProduct)) {
335			if ( dev != NULL ) {
336				SHOW_PROGRESS(" Found a default device NOT in target class mode\n");
337			} else {
338				SHOW_PROGRESS(" All devices in target class mode. Nothing to do. Bye.\n\n");
339				exit(0);
340			}
341		}
342	}
343	if (dev != NULL) {
344		devnum = dev->devnum;
345		busnum = (int)strtol(dev->bus->dirname,NULL,10);
346		SHOW_PROGRESS("Accessing device %03d on bus %03d ...\n", devnum, busnum);
347		devh = usb_open(dev);
348	} else {
349		SHOW_PROGRESS(" No default device found. Is it connected? Bye.\n\n");
350		exit(0);
351	}
352
353	// Get class of default device
354	defaultClass = dev->descriptor.bDeviceClass;
355	if (defaultClass == 0)
356		defaultClass = dev->config[0].interface[0].altsetting[0].bInterfaceClass;
357	else
358		if (dev->config[0].interface[0].altsetting[0].bInterfaceClass == 8 && defaultClass != 8) {
359			// Weird device with default class other than 0 and differing interface class
360			SHOW_PROGRESS("Ambiguous Class/InterfaceClass: 0x%02x/0x08", defaultClass);
361			defaultClass = 8;
362		}
363
364	// Check or get endpoints if needed
365	if (!MessageEndpoint && (strlen(MessageContent) || InquireDevice) ) {
366//		SHOW_PROGRESS(" Finding endpoints ...\n");
367		MessageEndpoint = find_first_bulk_output_endpoint(dev);
368		if (!MessageEndpoint && strlen(MessageContent)) {
369			fprintf(stderr,"Error: message endpoint not given or found. Aborting.\n\n");
370			exit(1);
371		}
372	}
373	if (!ResponseEndpoint && (NeedResponse || InquireDevice) ) {
374		ResponseEndpoint = find_first_bulk_input_endpoint(dev);
375		if (!ResponseEndpoint && NeedResponse) {
376			fprintf(stderr,"Error: response endpoint not given or found. Aborting.\n\n");
377			exit(1);
378		}
379	}
380	if (MessageEndpoint && ResponseEndpoint) {
381		SHOW_PROGRESS("Using endpoints 0x%02x (out) and 0x%02x (in)\n", MessageEndpoint, ResponseEndpoint);
382	} else
383		if (InquireDevice && defaultClass == 0x08) {
384			SHOW_PROGRESS("Endpoints not found, skipping SCSI inquiry\n");
385			InquireDevice = 0;
386		}
387
388	if (InquireDevice && show_progress) {
389		if (defaultClass == 0x08) {
390			SHOW_PROGRESS("Inquiring device details; driver will be detached ...\n");
391			detachDriver();
392			if (deviceInquire() >= 0)
393				InquireDevice = 2;
394		} else
395			SHOW_PROGRESS("Not a storage device, skipping SCSI inquiry\n");
396	}
397
398	deviceDescription();
399	if (show_progress) {
400		printf("\nUSB description data (for identification)\n");
401		printf("-------------------------\n");
402		printf("Manufacturer: %s\n", imanufact);
403		printf("     Product: %s\n", iproduct);
404		printf("  Serial No.: %s\n", iserial);
405		printf("-------------------------\n");
406	}
407
408	// Some scenarios are exclusive, so check for unwanted combinations
409	specialMode = DetachStorageOnly + HuaweiMode + SierraMode + SonyMode;
410	if ( specialMode > 1 ) {
411		SHOW_PROGRESS("Invalid mode combination. Check your configuration. Aborting.\n\n");
412		exit(1);
413	}
414
415	if ( !specialMode && !strlen(MessageContent) )
416		SHOW_PROGRESS("Warning: no switching method given.\n");
417
418	/*
419	 * The switching actions
420	 */
421
422	if (sysmode) {
423		openlog("usb-modeswitch", 0, LOG_SYSLOG);
424		syslog(LOG_NOTICE, "switching %04x:%04x (%s: %s)", DefaultVendor, DefaultProduct, imanufact, iproduct);
425	}
426
427	if (DetachStorageOnly) {
428		SHOW_PROGRESS("Only detaching storage driver for switching ...\n");
429		if (InquireDevice == 2) {
430			SHOW_PROGRESS(" Any driver was already detached for inquiry\n");
431		} else {
432			ret = detachDriver();
433			if (ret == 2)
434				SHOW_PROGRESS(" You may want to remove the storage driver manually\n");
435		}
436	}
437
438	if (strlen(MessageContent) && MessageEndpoint) {
439		if (specialMode == 0) {
440			if (InquireDevice != 2)
441				detachDriver();
442			switchSendMessage();
443		} else
444			SHOW_PROGRESS("Warning: ignoring MessageContent. Can't combine with special mode\n");
445	}
446
447	if (Configuration != -1) {
448		switchConfiguration ();
449	}
450
451	if (AltSetting != -1) {
452		switchAltSetting();
453	}
454
455	if (ResetUSB) {
456		resetUSB();
457	}
458
459	if (CheckSuccess) {
460		signal(SIGTERM, release_usb_device);
461		if (checkSuccess()) {
462			if (sysmode)
463				printf("ok:%04x:%04x\n", TargetVendor, TargetProduct);
464			exit(0);
465		} else{
466			if (sysmode)
467				printf("fail:\n");
468			exit(0);
469		}
470	} else {
471		if (SonyMode)
472			if (sonySuccess) {
473				if (sysmode) {
474					syslog(LOG_NOTICE, "switched S.E. MD400 to modem mode");
475					printf("ok:\n"); // ACM device, no driver action
476				}
477				SHOW_PROGRESS("-> device should be stable now. Bye.\n\n");
478			} else {
479				if (sysmode)
480					printf("fail:\n");
481				SHOW_PROGRESS("-> switching was probably not completed. Bye.\n\n");
482			}
483		else
484			SHOW_PROGRESS("-> Run lsusb to note any changes. Bye.\n\n");
485		if (sysmode)
486			closelog();
487		exit(0);
488	}
489
490	if (devh)
491		usb_close(devh);
492
493	return 0;
494}
495
496
497/* Get descriptor strings if available (identification details) */
498void deviceDescription ()
499{
500	int ret;
501	char* c;
502	memset (imanufact, ' ', DESCR_MAX);
503	memset (iproduct, ' ', DESCR_MAX);
504	memset (iserial, ' ', DESCR_MAX);
505
506	if (dev->descriptor.iManufacturer) {
507		ret = usb_get_string_simple(devh, dev->descriptor.iManufacturer, imanufact, DESCR_MAX);
508		if (ret < 0)
509			fprintf(stderr, "Error: could not get description string \"manufacturer\"\n");
510	} else
511		strcpy(imanufact, "not provided");
512	c = strstr(imanufact, "    ");
513	if (c)
514		memset((void*)c, '\0', 1);
515
516	if (dev->descriptor.iProduct) {
517		ret = usb_get_string_simple(devh, dev->descriptor.iProduct, iproduct, DESCR_MAX);
518		if (ret < 0)
519			fprintf(stderr, "Error: could not get description string \"product\"\n");
520	} else
521		strcpy(iproduct, "not provided");
522	c = strstr(iproduct, "    ");
523	if (c)
524		memset((void*)c, '\0', 1);
525
526	if (dev->descriptor.iSerialNumber) {
527		ret = usb_get_string_simple(devh, dev->descriptor.iSerialNumber, iserial, DESCR_MAX);
528		if (ret < 0)
529			fprintf(stderr, "Error: could not get description string \"serial number\"\n");
530	} else
531		strcpy(iserial, "not provided");
532	c = strstr(iserial, "    ");
533	if (c)
534		memset((void*)c, '\0', 1);
535
536}
537
538/* Print result of SCSI command INQUIRY (identification details) */
539int deviceInquire ()
540{
541	const unsigned char inquire_msg[] = {
542	  0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78,
543	  0x24, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x12,
544	  0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00,
545	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
546	};
547	char *command;
548	char data[36];
549	int i, ret;
550
551	command = malloc(31);
552	if (command == NULL) {
553		ret = 1;
554		goto out;
555	}
556
557	memcpy(command, inquire_msg, sizeof (inquire_msg));
558
559	ret = usb_claim_interface(devh, Interface);
560	if (ret != 0) {
561		SHOW_PROGRESS(" Could not claim interface (error %d). Skipping device inquiry\n", ret);
562		goto out;
563	}
564	usb_clear_halt(devh, MessageEndpoint);
565
566	ret = usb_bulk_write(devh, MessageEndpoint, (char *)command, 31, 0);
567	if (ret < 0) {
568		SHOW_PROGRESS(" Could not send INQUIRY message (error %d)\n", ret);
569		goto out;
570	}
571
572	ret = usb_bulk_read(devh, ResponseEndpoint, data, 36, 0);
573	if (ret < 0) {
574		SHOW_PROGRESS(" Could not get INQUIRY response (error %d)\n", ret);
575		goto out;
576	}
577
578	i = usb_bulk_read(devh, ResponseEndpoint, command, 13, 0);
579
580	printf("\nSCSI inquiry data (for identification)\n");
581	printf("-------------------------\n");
582
583	printf("  Vendor String: ");
584	for (i = 8; i < 16; i++) printf("%c",data[i]);
585	printf("\n");
586
587	printf("   Model String: ");
588	for (i = 16; i < 32; i++) printf("%c",data[i]);
589	printf("\n");
590
591	printf("Revision String: ");
592	for (i = 32; i < 36; i++) printf("%c",data[i]);
593
594	printf("\n-------------------------\n");
595
596out:
597	if (strlen(MessageContent) == 0)
598		usb_clear_halt(devh, MessageEndpoint);
599		usb_release_interface(devh, Interface);
600	free(command);
601	return ret;
602}
603
604
605void resetUSB ()
606{
607	int success;
608	int bpoint = 0;
609
610	if (show_progress) {
611		printf("Resetting usb device ");
612		fflush(stdout);
613	}
614
615	sleep( 1 );
616	do {
617		success = usb_reset(devh);
618		if ( ((bpoint % 10) == 0) && show_progress ) {
619			printf(".");
620			fflush(stdout);
621		}
622		bpoint++;
623		if (bpoint > 100)
624			success = 1;
625	} while (success < 0);
626
627	if ( success ) {
628		SHOW_PROGRESS("\n Reset failed. Can be ignored if device switched OK.\n");
629	} else
630		SHOW_PROGRESS("\n OK, device was reset\n");
631}
632
633
634int switchSendMessage ()
635{
636	int message_length, ret;
637
638	SHOW_PROGRESS("Setting up communication with interface %d ...\n", Interface);
639	if (InquireDevice != 2) {
640		ret = usb_claim_interface(devh, Interface);
641		if (ret != 0) {
642			SHOW_PROGRESS(" Could not claim interface (error %d). Skipping message sending\n", ret);
643			return 0;
644		}
645	}
646	usb_clear_halt(devh, MessageEndpoint);
647	SHOW_PROGRESS("Trying to send the message to endpoint 0x%02x ...\n", MessageEndpoint);
648	fflush(stdout);
649
650	message_length = strlen(MessageContent) / 2;
651	ret = write_bulk(MessageEndpoint, ByteString, message_length);
652	if (ret == -19)
653		goto skip;
654
655	if (NeedResponse) {
656		SHOW_PROGRESS("Reading the response to the message ...\n");
657		ret = read_bulk(ResponseEndpoint, ByteString, LINE_DIM/2);
658		if (ret == -19)
659			goto skip;
660	}
661
662	ret = usb_clear_halt(devh, MessageEndpoint);
663	if (ret)
664		goto skip;
665	ret = usb_release_interface(devh, Interface);
666	if (ret)
667		goto skip;
668	return 1;
669
670skip:
671	SHOW_PROGRESS(" Device is gone, skipping any further commands\n");
672	usb_close(devh);
673	devh = 0;
674	return 2;
675}
676
677
678int switchConfiguration ()
679{
680	int ret;
681
682	SHOW_PROGRESS("Changing configuration to %i ...\n", Configuration);
683	ret = usb_set_configuration(devh, Configuration);
684	if (ret == 0 ) {
685		SHOW_PROGRESS(" OK, configuration set\n");
686		return 1;
687	}
688	SHOW_PROGRESS(" Setting the configuration returned error %d. Trying to continue\n", ret);
689	return 0;
690}
691
692
693int switchAltSetting ()
694{
695	int ret;
696
697	SHOW_PROGRESS("Changing to alt setting %i ...\n", AltSetting);
698	ret = usb_claim_interface(devh, Interface);
699	ret = usb_set_altinterface(devh, AltSetting);
700	usb_release_interface(devh, Interface);
701	if (ret != 0) {
702		SHOW_PROGRESS(" Changing to alt setting returned error %d. Trying to continue\n", ret);
703		return 0;
704	} else {
705		SHOW_PROGRESS(" OK, changed to alt setting\n");
706		return 1;
707	}
708}
709
710// Detach driver either as the main action or as preparation for other modes
711int detachDriver()
712{
713	int ret;
714
715#ifndef LIBUSB_HAS_GET_DRIVER_NP
716	printf(" Cant't do driver detection and detaching on this platform.\n");
717	return 2;
718#endif
719
720	SHOW_PROGRESS("Looking for active driver ...\n");
721	ret = usb_get_driver_np(devh, Interface, buffer, BUF_SIZE);
722	if (ret != 0) {
723		SHOW_PROGRESS(" No driver found. Either detached before or never attached\n");
724		return 1;
725	}
726	SHOW_PROGRESS(" OK, driver found (\"%s\")\n", buffer);
727	if (DetachStorageOnly && strcmp(buffer,"usb-storage")) {
728		SHOW_PROGRESS(" Warning: driver is not usb-storage\n");
729//		return 1;
730	}
731
732#ifndef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
733	SHOW_PROGRESS(" Can't do driver detaching on this platform\n");
734	return 2;
735#endif
736
737
738	ret = usb_detach_kernel_driver_np(devh, Interface);
739	if (ret == 0) {
740		SHOW_PROGRESS(" OK, driver \"%s\" detached\n", buffer);
741//		usb_clear_halt(devh, MessageEndpoint);
742//		usb_clear_halt(devh, ResponseEndpoint);
743	} else
744		SHOW_PROGRESS(" Driver \"%s\" detach failed with error %d. Trying to continue\n", buffer, ret);
745	return 1;
746}
747
748
749int checkSuccess()
750{
751	int i=0, ret;
752	int newTargetCount, success=0;
753
754	SHOW_PROGRESS("\nChecking for mode switch (max. %d times, once per second) ...\n", CheckSuccess);
755	sleep(1);
756
757	if (devh) // devh is 0 if device vanished during command transmission
758		for (i=0; i < CheckSuccess; i++) {
759
760			// Test if default device still can be accessed; positive result does
761			// not necessarily mean failure
762			SHOW_PROGRESS(" Waiting for original device to vanish ...\n");
763
764			ret = usb_claim_interface(devh, Interface);
765			if (ret < 0) {
766				SHOW_PROGRESS(" Original device can't be accessed anymore. Good.\n");
767				if (i == CheckSuccess-1)
768					SHOW_PROGRESS(" If you want target checking, increase 'CheckSuccess' value.\n");
769				usb_close(devh);
770				devh = NULL;
771				break;
772			} else
773				usb_release_interface(devh, Interface);
774
775			if (i == CheckSuccess-1) {
776				SHOW_PROGRESS(" Original device still present after the timeout\n\nMode switch most likely failed. Bye.\n\n");
777			} else
778				sleep(1);
779		}
780	else
781		SHOW_PROGRESS(" Original device is gone already, not checking\n");
782
783
784	if ( (TargetVendor && (TargetProduct || strlen(TargetProductList))) || TargetClass )
785
786		// Recount target devices (compare with previous count) if target data is given.
787		// Target device on the same bus with higher device number is returned,
788		// description is read for syslog message
789		for (i=i; i < CheckSuccess; i++) {
790			SHOW_PROGRESS(" Searching for target devices ...\n");
791			usb_find_devices();
792			dev = search_devices(&newTargetCount, TargetVendor, TargetProduct, TargetProductList, TargetClass);
793			if (dev && (newTargetCount > targetDeviceCount)) {
794				devh = usb_open(dev);
795				deviceDescription();
796				usb_close(devh);
797				if (verbose) {
798					printf("\nFound target device %03d on bus %03d\n", \
799					dev->devnum, (int)strtol(dev->bus->dirname,NULL,10));
800					printf("\nTarget device description data\n");
801					printf("-------------------------\n");
802					printf("Manufacturer: %s\n", imanufact);
803					printf("     Product: %s\n", iproduct);
804					printf("  Serial No.: %s\n", iserial);
805					printf("-------------------------\n");
806				}
807				SHOW_PROGRESS(" Found correct target device\n\nMode switch succeeded. Bye.\n\n");
808				success = 2;
809				break;
810			}
811			if (i == CheckSuccess-1) {
812				SHOW_PROGRESS(" No new devices in target mode or class found\n\nMode switch has failed. Bye.\n\n");
813			} else
814				sleep(1);
815		}
816	else
817		// No target data given, rely on the vanished device
818		if (!devh) {
819			SHOW_PROGRESS(" (For a better success check provide target IDs or class)\n");
820			SHOW_PROGRESS(" Original device vanished after switching\n\nMode switch most likely succeeded. Bye.\n\n");
821			success = 1;
822		}
823
824	switch (success) {
825		case 2:
826			if (sysmode)
827				syslog(LOG_NOTICE, "switched to %04x:%04x (%s: %s)", TargetVendor, TargetProduct, imanufact, iproduct);
828			success = 1;
829			break;
830		case 1:
831			if (sysmode)
832				syslog(LOG_NOTICE, "device seems to have switched");
833		default:
834			;
835	}
836	if (sysmode)
837		closelog();
838
839	return success;
840
841}
842
843
844int write_bulk(int endpoint, char *message, int length)
845{
846	int ret;
847	ret = usb_bulk_write(devh, endpoint, message, length, 100);
848	if (ret >= 0 ) {
849		SHOW_PROGRESS(" OK, message successfully sent\n");
850	} else
851		if (ret == -19) {
852			SHOW_PROGRESS(" Device seems to have vanished right after sending. Good.\n");
853		} else
854			SHOW_PROGRESS(" Sending the message returned error %d. Trying to continue\n", ret);
855	return ret;
856
857}
858
859int read_bulk(int endpoint, char *buffer, int length)
860{
861	int ret;
862	ret = usb_bulk_read(devh, endpoint, buffer, length, 100);
863	usb_bulk_read(devh, endpoint, buffer, 13, 100);
864	if (ret >= 0 ) {
865		SHOW_PROGRESS(" OK, response successfully read (%d bytes).\n", ret);
866	} else
867		if (ret == -19) {
868			SHOW_PROGRESS(" Device seems to have vanished after reading. Good.\n");
869		} else
870			SHOW_PROGRESS(" Response reading got error %d, can probably be ignored\n", ret);
871	return ret;
872
873}
874
875void release_usb_device(int dummy) {
876	SHOW_PROGRESS("Program cancelled by system. Bye.\n\n");
877	usb_release_interface(devh, Interface);
878	usb_close(devh);
879	if (sysmode)
880		closelog();
881	exit(0);
882
883}
884
885
886// iterates over busses and devices, counts the ones found and returns the last one of them
887
888struct usb_device* search_devices( int *numFound, int vendor, int product, char* productList, int targetClass)
889{
890	struct usb_bus *bus;
891	char *listcopy, *token, buffer[2];
892	int devClass;
893	struct usb_device* right_dev = NULL;
894
895	if ( targetClass && !(vendor || product) ) {
896		vendor = DefaultVendor;
897		product = DefaultProduct;
898	}
899	*numFound = 0;
900	if (!vendor || (!product && productList==NULL))
901		return NULL;
902	if (productList != NULL)
903		listcopy = malloc(strlen(productList)+1);
904
905	for (bus = usb_get_busses(); bus; bus = bus->next) {
906		struct usb_device *dev;
907		for (dev = bus->devices; dev; dev = dev->next) {
908			if (verbose)
909				printf ("  searching devices, found USB ID %04x:%04x\n", dev->descriptor.idVendor, dev->descriptor.idProduct);
910			if (dev->descriptor.idVendor != vendor)
911				continue;
912			if (verbose)
913				printf ("   found matching vendor ID\n");
914			// product list given
915			if ( strlen(productList) ) {
916				strcpy(listcopy, productList);
917				token = strtok(listcopy, ",");
918				while (token != NULL) {
919					if (strlen(token) != 4) {
920						SHOW_PROGRESS("Error: entry in product ID list has wrong length: %s. Ignoring\n", token);
921						goto NextToken;
922					}
923					if ( hexstr2bin(token, buffer, strlen(token)/2) == -1) {
924						SHOW_PROGRESS("Error: entry in product ID list is not a hex string: %s. Ignoring\n", token);
925						goto NextToken;
926					}
927					product = 0;
928					product += (unsigned char)buffer[0];
929					product <<= 8;
930					product += (unsigned char)buffer[1];
931					if (product == dev->descriptor.idProduct) {
932						if (verbose)
933							printf ("   found matching product ID from list\n");
934						(*numFound)++;
935						if (busnum == -1)
936							right_dev = dev;
937						else
938							if (dev->devnum >= devnum && (int)strtol(dev->bus->dirname,NULL,10) == busnum) {
939								right_dev = dev;
940								TargetProduct = dev->descriptor.idProduct;
941								break;
942							}
943					}
944
945					NextToken:
946					token = strtok(NULL, ",");
947				}
948			// product is given
949			} else
950				if (product == dev->descriptor.idProduct) {
951					if (verbose)
952						printf ("   found matching product ID\n");
953					(*numFound)++;
954					devClass = dev->descriptor.bDeviceClass;
955					if (devClass == 0)
956						devClass = dev->config[0].interface[0].altsetting[0].bInterfaceClass;
957					else
958						if (devClass != dev->config[0].interface[0].altsetting[0].bInterfaceClass)
959							devClass = dev->config[0].interface[0].altsetting[0].bInterfaceClass;
960					if (busnum == -1) {
961						if (devClass != targetClass || targetClass == 0)
962							right_dev = dev;
963					} else
964						if (devClass == targetClass || targetClass == 0)
965							if (dev->devnum >= devnum && (int)strtol(dev->bus->dirname,NULL,10) == busnum)
966								right_dev = dev;
967				}
968			if (right_dev && busnum != -1)
969				break;
970		}
971		if (right_dev && busnum != -1)
972			break;
973	}
974	if (productList != NULL)
975		free(listcopy);
976	return right_dev;
977}
978
979
980#define USB_DIR_OUT 0x00
981#define USB_DIR_IN  0x80
982
983// Autodetect bulk endpoints (ab)
984
985int find_first_bulk_output_endpoint(struct usb_device *dev)
986{
987	int i;
988	struct usb_interface_descriptor *alt = &(dev->config[0].interface[0].altsetting[0]);
989	struct usb_endpoint_descriptor *ep;
990
991	for(i=0;i < alt->bNumEndpoints;i++) {
992		ep=&(alt->endpoint[i]);
993		if( ( (ep->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK) &&
994		    ( (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT ) ) {
995			return ep->bEndpointAddress;
996		}
997	}
998
999	return 0;
1000}
1001
1002
1003int find_first_bulk_input_endpoint(struct usb_device *dev)
1004{
1005	int i;
1006	struct usb_interface_descriptor *alt = &(dev->config[0].interface[0].altsetting[0]);
1007	struct usb_endpoint_descriptor *ep;
1008
1009	for(i=0;i < alt->bNumEndpoints;i++) {
1010		ep=&(alt->endpoint[i]);
1011		if( ( (ep->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK) &&
1012		    ( (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN ) ) {
1013			return ep->bEndpointAddress;
1014		}
1015	}
1016
1017	return 0;
1018}
1019
1020
1021
1022// the parameter parsing stuff
1023
1024char* ReadParseParam(const char* FileName, char *VariableName)
1025{
1026	static char Str[LINE_DIM];
1027	char *VarName, *Comment=NULL, *Equal=NULL;
1028	char *FirstQuote, *LastQuote, *P1, *P2;
1029	int Line=0, Len=0, Pos=0;
1030	FILE *file=fopen(FileName, "r");
1031
1032	if (file==NULL) {
1033		fprintf(stderr, "Error: Could not find file %s\n\n", FileName);
1034		exit(1);
1035	}
1036
1037	while (fgets(Str, LINE_DIM-1, file) != NULL) {
1038		Line++;
1039		Len=strlen(Str);
1040		if (Len==0) goto Next;
1041		if (Str[Len-1]=='\n' or Str[Len-1]=='\r') Str[--Len]='\0';
1042		Equal = strchr (Str, '=');			// search for equal sign
1043		Pos = strcspn (Str, ";#!");			// search for comment
1044		Comment = (Pos==Len) ? NULL : Str+Pos;
1045		if (Equal==NULL or ( Comment!=NULL and Comment<=Equal)) goto Next;	// Only comment
1046		*Equal++ = '\0';
1047		if (Comment!=NULL) *Comment='\0';
1048
1049		// String
1050		FirstQuote=strchr (Equal, '"');		// search for double quote char
1051		LastQuote=strrchr (Equal, '"');
1052		if (FirstQuote!=NULL) {
1053			if (LastQuote==NULL) {
1054				fprintf(stderr, "Error reading parameter file %s line %d - Missing end quote.\n", FileName, Line);
1055				goto Next;
1056			}
1057			*FirstQuote=*LastQuote='\0';
1058			Equal=FirstQuote+1;
1059		}
1060
1061		// removes leading/trailing spaces
1062		Pos=strspn (Str, " \t");
1063		if (Pos==strlen(Str)) {
1064			fprintf(stderr, "Error reading parameter file %s line %d - Missing variable name.\n", FileName, Line);
1065			goto Next;		// No function name
1066		}
1067		while ((P1=strrchr(Str, ' '))!=NULL or (P2=strrchr(Str, '\t'))!=NULL)
1068			if (P1!=NULL) *P1='\0';
1069			else if (P2!=NULL) *P2='\0';
1070		VarName=Str+Pos;
1071		//while (strspn(VarName, " \t")==strlen(VarName)) VarName++;
1072
1073		Pos=strspn (Equal, " \t");
1074		if (Pos==strlen(Equal)) {
1075			fprintf(stderr, "Error reading parameter file %s line %d - Missing value.\n", FileName, Line);
1076			goto Next;		// No function name
1077		}
1078		Equal+=Pos;
1079
1080		if (strcmp(VarName, VariableName)==0) {		// Found it
1081			fclose(file);
1082			return Equal;
1083		}
1084		Next:;
1085	}
1086
1087	// not found
1088//	fprintf(stderr, "Error reading parameter file %s - Variable %s not found.",
1089//				FileName, VariableName);
1090	fclose(file);
1091	return NULL;
1092}
1093
1094
1095int hex2num(char c)
1096{
1097	if (c >= '0' && c <= '9')
1098	return c - '0';
1099	if (c >= 'a' && c <= 'f')
1100	return c - 'a' + 10;
1101	if (c >= 'A' && c <= 'F')
1102	return c - 'A' + 10;
1103	return -1;
1104}
1105
1106
1107int hex2byte(const char *hex)
1108{
1109	int a, b;
1110	a = hex2num(*hex++);
1111	if (a < 0)
1112		return -1;
1113	b = hex2num(*hex++);
1114	if (b < 0)
1115		return -1;
1116	return (a << 4) | b;
1117}
1118
1119int hexstr2bin(const char *hex, char *buffer, int len)
1120{
1121	int i;
1122	int a;
1123	const char *ipos = hex;
1124	char *opos = buffer;
1125//    printf("Debug: hexstr2bin bytestring is ");
1126
1127	for (i = 0; i < len; i++) {
1128	a = hex2byte(ipos);
1129//        printf("%02X", a);
1130	if (a < 0)
1131		return -1;
1132	*opos++ = a;
1133	ipos += 2;
1134	}
1135//    printf(" \n");
1136	return 0;
1137}
1138
1139void printVersion()
1140{
1141	printf("\n * usb-modeswitch: handle USB devices with multiple modes\n");
1142	printf(" * Version %s (C) Josua Dietze 2010\n", version);
1143	printf(" * Based on libusb 0.1.12\n\n");
1144	printf(" ! PLEASE REPORT NEW CONFIGURATIONS !\n\n");
1145}
1146