1/*
2 * Copyright (C) 2014 Andrew Duggan
3 * Copyright (C) 2014 Synaptics Inc
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <stdio.h>
19#include <string.h>
20#include <errno.h>
21#include <getopt.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <fcntl.h>
25#include <dirent.h>
26#include <unistd.h>
27#include <time.h>
28#include <string>
29#include <sstream>
30
31#include "hiddevice.h"
32#include "rmi4update.h"
33
34#define VERSION_MAJOR		1
35#define VERSION_MINOR		2
36#define VERSION_SUBMINOR	0
37
38#define RMI4UPDATE_GETOPTS	"hfd:plv"
39
40void printHelp(const char *prog_name)
41{
42	fprintf(stdout, "Usage: %s [OPTIONS] FIRMWAREFILE\n", prog_name);
43	fprintf(stdout, "\t-h, --help\tPrint this message\n");
44	fprintf(stdout, "\t-f, --force\tForce updating firmware even it the image provided is older\n\t\t\tthen the current firmware on the device.\n");
45	fprintf(stdout, "\t-d, --device\thidraw device file associated with the device being updated.\n");
46	fprintf(stdout, "\t-p, --fw-props\tPrint the firmware properties.\n");
47	fprintf(stdout, "\t-l, --lockdown\tPerform lockdown.\n");
48	fprintf(stdout, "\t-v, --version\tPrint version number.\n");
49}
50
51void printVersion()
52{
53	fprintf(stdout, "rmi4update version %d.%d.%d\n",
54		VERSION_MAJOR, VERSION_MINOR, VERSION_SUBMINOR);
55}
56
57int UpdateDevice(FirmwareImage & image, bool force, bool performLockdown, const char * deviceFile)
58{
59	HIDDevice rmidevice;
60	int rc;
61
62	rc = rmidevice.Open(deviceFile);
63	if (rc)
64		return rc;
65
66	RMI4Update update(rmidevice, image);
67	rc = update.UpdateFirmware(force, performLockdown);
68	if (rc != UPDATE_SUCCESS)
69		return rc;
70
71	return rc;
72}
73
74int GetFirmwareProps(const char * deviceFile, std::string &props)
75{
76	HIDDevice rmidevice;
77	int rc = UPDATE_SUCCESS;
78	std::stringstream ss;
79
80	rc = rmidevice.Open(deviceFile);
81	if (rc)
82		return rc;
83
84	rmidevice.ScanPDT(0x1);
85	rmidevice.QueryBasicProperties();
86
87	ss << rmidevice.GetFirmwareVersionMajor() << "."
88		<< rmidevice.GetFirmwareVersionMinor() << "."
89		<< std::hex << rmidevice.GetFirmwareID();
90
91	if (rmidevice.InBootloader())
92		ss << " bootloader";
93
94	props = ss.str();
95
96	return rc;
97}
98
99int main(int argc, char **argv)
100{
101	int rc;
102	FirmwareImage image;
103	int opt;
104	int index;
105	char *deviceName = NULL;
106	const char *firmwareName = NULL;
107	bool force = false;
108	static struct option long_options[] = {
109		{"help", 0, NULL, 'h'},
110		{"force", 0, NULL, 'f'},
111		{"device", 1, NULL, 'd'},
112		{"fw-props", 0, NULL, 'p'},
113		{"lockdown", 0, NULL, 'l'},
114		{"version", 0, NULL, 'v'},
115		{0, 0, 0, 0},
116	};
117	struct dirent * devDirEntry;
118	DIR * devDir;
119	bool printFirmwareProps = false;
120	bool performLockdown = false;
121
122	while ((opt = getopt_long(argc, argv, RMI4UPDATE_GETOPTS, long_options, &index)) != -1) {
123		switch (opt) {
124			case 'h':
125				printHelp(argv[0]);
126				return 0;
127			case 'f':
128				force = true;
129				break;
130			case 'd':
131				deviceName = optarg;
132				break;
133			case 'p':
134				printFirmwareProps = true;
135				break;
136			case 'l':
137				performLockdown = true;
138				break;
139			case 'v':
140				printVersion();
141				return 0;
142			default:
143				break;
144
145		}
146	}
147
148	if (printFirmwareProps) {
149		std::string props;
150
151		if (!deviceName) {
152			fprintf(stderr, "Specifiy which device to query\n");
153			return 1;
154		}
155		rc = GetFirmwareProps(deviceName, props);
156		if (rc) {
157			fprintf(stderr, "Failed to read properties from device: %s\n", update_err_to_string(rc));
158			return 1;
159		}
160		fprintf(stdout, "%s\n", props.c_str());
161		return 0;
162	}
163
164	if (optind < argc) {
165		firmwareName = argv[optind];
166	} else {
167		printHelp(argv[0]);
168		return -1;
169	}
170
171	rc = image.Initialize(firmwareName);
172	if (rc != UPDATE_SUCCESS) {
173		fprintf(stderr, "Failed to initialize the firmware image: %s\n", update_err_to_string(rc));
174		return 1;
175	}
176
177	if (deviceName) {
178		rc = UpdateDevice(image, force, performLockdown, deviceName);
179
180		return rc;
181	} else {
182		char deviceFile[PATH_MAX];
183		bool found = false;
184
185		devDir = opendir("/dev");
186		if (!devDir)
187			return -1;
188
189		while ((devDirEntry = readdir(devDir)) != NULL) {
190			if (strstr(devDirEntry->d_name, "hidraw")) {
191				char rawDevice[PATH_MAX];
192				strncpy(rawDevice, devDirEntry->d_name, PATH_MAX);
193				snprintf(deviceFile, PATH_MAX, "/dev/%s", devDirEntry->d_name);
194				rc = UpdateDevice(image, force, performLockdown, deviceFile);
195				if (rc != 0) {
196					continue;
197				} else {
198					found = true;
199					break;
200				}
201			}
202		}
203		closedir(devDir);
204
205		if (!found)
206			return rc;
207	}
208
209	return 0;
210}
211