1/*
2 * Copyright (C) 2013 - 2014 Andrew Duggan
3 * Copyright (C) 2013 - 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 <sys/types.h>
20#include <sys/stat.h>
21#include <fcntl.h>
22#include <errno.h>
23#include <string.h>
24#include <unistd.h>
25#include <sys/ioctl.h>
26#include <sys/select.h>
27#include <getopt.h>
28
29#include <linux/types.h>
30#include <linux/input.h>
31#include <linux/hidraw.h>
32#include <signal.h>
33#include <stdlib.h>
34
35#include "hiddevice.h"
36
37#define RMI4UPDATE_GETOPTS      "hp:ir:w:foambde"
38
39 enum rmihidtool_cmd {
40	RMIHIDTOOL_CMD_INTERACTIVE,
41	RMIHIDTOOL_CMD_READ,
42	RMIHIDTOOL_CMD_WRITE,
43	RMIHIDTOOL_CMD_FW_ID,
44	RMIHIDTOOL_CMD_PROPS,
45	RMIHIDTOOL_CMD_ATTN,
46	RMIHIDTOOL_CMD_PRINT_FUNCTIONS,
47	RMIHIDTOOL_CMD_REBIND_DRIVER,
48	RMIHIDTOOL_CMD_PRINT_DEVICE_INFO,
49	RMIHIDTOOL_CMD_RESET_DEVICE,
50};
51
52static int report_attn = 0;
53static RMIDevice * g_device = NULL;
54
55void print_help(const char *prog_name)
56{
57	fprintf(stdout, "Usage: %s [OPTIONS] DEVICEFILE\n", prog_name);
58	fprintf(stdout, "\t-h, --help\t\t\t\tPrint this message\n");
59	fprintf(stdout, "\t-p, --protocol [protocol]\t\tSet which transport prototocl to use.\n");
60	fprintf(stdout, "\t-i, --interactive\t\t\tRun in interactive mode.\n");
61	fprintf(stdout, "\t-r, --read [address] [length]\t\tRead registers starting at the address.\n");
62	fprintf(stdout, "\t-r, --write [address] [length] [data]\tWrite registers starting at the address.\n");
63	fprintf(stdout, "\t-f, --firmware-id\t\t\tPrint the firmware id\n");
64	fprintf(stdout, "\t-o, --props\t\t\t\tPrint device properties\n");
65	fprintf(stdout, "\t-a, --attention\t\t\t\tPrint attention reports until control + c\n");
66	fprintf(stdout, "\t-m, --print-functions\t\t\tPrint RMI4 functions for the device.\n");
67	fprintf(stdout, "\t-b, --rebind-driver\t\t\tRebind the driver to force an update of device properties.\n");
68	fprintf(stdout, "\t-d, --device-info\t\t\tPrint protocol specific information about the device.\n");
69	fprintf(stdout, "\t-e, --reset-device\t\t\tReset the device.\n");
70}
71
72void print_cmd_usage()
73{
74	fprintf(stdout, "Commands:\n");
75	fprintf(stdout, "s [0,1,2]: Set RMIMode\n");
76	fprintf(stdout, "r address size: read size bytes from address\n");
77	fprintf(stdout, "w address { values }: write bytes to address\n");
78	fprintf(stdout, "a: Wait for attention\n");
79	fprintf(stdout, "q: quit\n");
80}
81
82int find_token(char * input, char * result, size_t result_len, char ** endpp)
83{
84	int i = 0;
85	char * start = input;
86	char * end;
87
88	while (input[i] == ' ') {
89		++start;
90		++i;
91	}
92
93	while (input[i] != '\0') {
94		if (input[++i] == ' ')
95			break;
96	}
97	end = &input[i];
98
99	if (start == end)
100		return 0;
101
102	*endpp = end;
103	if (static_cast<ssize_t>(result_len) < end - start + 1)
104		return 0;
105	strncpy(result, start, end - start);
106	result[end - start] = '\0';
107
108	return 1;
109}
110
111void interactive(RMIDevice * device, unsigned char *report)
112{
113	char token[256];
114	char * start;
115	char * end;
116	int rc;
117
118	for (;;) {
119		fprintf(stdout, "\n");
120		print_cmd_usage();
121		char input[256];
122
123		if (fgets(input, 256, stdin)) {
124			memset(token, 0, 256);
125
126			if (input[0] == 's') {
127				start = input + 2;
128				find_token(start, token, sizeof(token), &end);
129				int mode = strtol(token, NULL, 0);
130				if (mode >= 0 && mode <= 2) {
131					if (device->SetMode(mode)) {
132						fprintf(stderr, "Set RMI Mode to: %d\n", mode);
133					} else {
134						fprintf(stderr, "Set RMI Mode FAILED!\n");
135						continue;
136					}
137				}
138			} else if (input[0] == 'r') {
139				start = input + 2;
140				find_token(start, token, sizeof(token), &end);
141				start = end + 1;
142				unsigned int addr = strtol(token, NULL, 0);
143				find_token(start, token, sizeof(token), &end);
144				start = end + 1;
145				unsigned int len = strtol(token, NULL, 0);
146				fprintf(stdout, "Address = 0x%02x Length = %d\n", addr, len);
147
148				memset(report, 0, 256);
149				rc = device->Read(addr, report, len);
150				if (rc < 0)
151					fprintf(stderr, "Failed to read report: %d\n", rc);
152				print_buffer(report, len);
153			} else if (input[0] == 'w') {
154				int index = 0;
155				start = input + 2;
156				find_token(start, token, sizeof(token), &end);
157				start = end + 1;
158				unsigned int addr = strtol(token, NULL, 0);
159				unsigned int len = 0;
160
161				memset(report, 0, 256);
162				while (find_token(start, token, sizeof(token), &end)) {
163					start = end;
164					report[index++] = strtol(token, NULL, 0);
165					++len;
166				}
167
168				if (device->Write(addr, report, len) < 0) {
169					fprintf(stderr, "Failed to Write Report\n");
170					continue;
171				}
172			} else if (input[0] == 'a') {
173				unsigned int bytes = 256;
174				device->GetAttentionReport(NULL,
175						RMI_INTERUPT_SOURCES_ALL_MASK,
176						report, &bytes);
177				print_buffer(report, bytes);
178			} else if (input[0] == 'q') {
179				return;
180			} else {
181				print_cmd_usage();
182			}
183		}
184	}
185}
186
187static void cleanup(int status)
188{
189	if (report_attn) {
190		report_attn = 0;
191		if (g_device)
192			g_device->Cancel();
193	} else {
194		exit(0);
195	}
196}
197
198int main(int argc, char ** argv)
199{
200	int rc;
201	struct sigaction sig_cleanup_action;
202	int opt;
203	int index;
204	RMIDevice *device;
205	const char *protocol = "HID";
206	unsigned char report[256];
207	char token[256];
208	static struct option long_options[] = {
209		{"help", 0, NULL, 'h'},
210		{"protocol", 1, NULL, 'p'},
211		{"interactive", 0, NULL, 'i'},
212		{"read", 1, NULL, 'r'},
213		{"write", 1, NULL, 'w'},
214		{"firmware-id", 0, NULL, 'f'},
215		{"props", 0, NULL, 'o'},
216		{"attention", 0, NULL, 'a'},
217		{"print-functions", 0, NULL, 'm'},
218		{"rebind-driver", 0, NULL, 'b'},
219		{"device-info", 0, NULL, 'd'},
220		{"reset-device", 0, NULL, 'e'},
221		{0, 0, 0, 0},
222	};
223	enum rmihidtool_cmd cmd = RMIHIDTOOL_CMD_INTERACTIVE;
224	unsigned int addr = 0;
225	unsigned int len = 0;
226	char * data = NULL;
227	char * start;
228	char * end;
229	int i = 0;
230
231	memset(&sig_cleanup_action, 0, sizeof(struct sigaction));
232	sig_cleanup_action.sa_handler = cleanup;
233	sig_cleanup_action.sa_flags = SA_RESTART;
234	sigaction(SIGINT, &sig_cleanup_action, NULL);
235
236	while ((opt = getopt_long(argc, argv, RMI4UPDATE_GETOPTS, long_options, &index)) != -1) {
237		switch (opt) {
238			case 'h':
239				print_help(argv[0]);
240				return 0;
241			case 'p':
242				protocol = optarg;
243				break;
244			case 'i':
245				cmd = RMIHIDTOOL_CMD_INTERACTIVE;
246				break;
247			case 'r':
248				cmd = RMIHIDTOOL_CMD_READ;
249				addr = strtol(optarg, NULL, 0);
250				len = strtol(argv[optind++], NULL, 0);
251				break;
252			case 'w':
253				cmd = RMIHIDTOOL_CMD_WRITE;
254				addr = strtol(optarg, NULL, 0);
255				data = argv[optind++];
256				break;
257			case 'f':
258				cmd = RMIHIDTOOL_CMD_FW_ID;
259				break;
260			case 'o':
261				cmd = RMIHIDTOOL_CMD_PROPS;
262				break;
263			case 'a':
264				cmd = RMIHIDTOOL_CMD_ATTN;
265				break;
266			case 'm':
267				cmd = RMIHIDTOOL_CMD_PRINT_FUNCTIONS;
268				break;
269			case 'b':
270				cmd = RMIHIDTOOL_CMD_REBIND_DRIVER;
271				break;
272			case 'd':
273				cmd = RMIHIDTOOL_CMD_PRINT_DEVICE_INFO;
274				break;
275			case 'e':
276				cmd = RMIHIDTOOL_CMD_RESET_DEVICE;
277				break;
278			default:
279				print_help(argv[0]);
280				return 0;
281				break;
282
283		}
284	}
285
286	if (!strncasecmp("hid", protocol, 3)) {
287		device = new HIDDevice();
288	} else {
289		fprintf(stderr, "Invalid Protocol: %s\n", protocol);
290		return -1;
291	}
292
293	if (optind >= argc) {
294		print_help(argv[0]);
295		return -1;
296	}
297
298	rc = device->Open(argv[optind++]);
299	if (rc) {
300		fprintf(stderr, "%s: failed to initialize rmi device (%d): %s\n", argv[0], errno,
301			strerror(errno));
302		return 1;
303	}
304
305	g_device = device;
306
307	switch (cmd) {
308		case RMIHIDTOOL_CMD_READ:
309			memset(report, 0, sizeof(report));
310			rc = device->Read(addr, report, len);
311			if (rc < 0)
312				fprintf(stderr, "Failed to read report: %d\n", rc);
313
314			print_buffer(report, len);
315			break;
316		case RMIHIDTOOL_CMD_WRITE:
317			i = 0;
318			start = data;
319			memset(report, 0, sizeof(report));
320			while (find_token(start, token, sizeof(token), &end)) {
321				start = end;
322				report[i++] = (unsigned char)strtol(token, NULL, 0);
323				++len;
324			}
325
326			if (device->Write(addr, report, len) < 0) {
327				fprintf(stderr, "Failed to Write Report\n");
328				return -1;
329			}
330			break;
331		case RMIHIDTOOL_CMD_FW_ID:
332			device->ScanPDT();
333			device->QueryBasicProperties();
334			fprintf(stdout, "firmware id: %lu\n", device->GetFirmwareID());
335			break;
336		case RMIHIDTOOL_CMD_PROPS:
337			device->ScanPDT();
338			device->QueryBasicProperties();
339			device->PrintProperties();
340			break;
341		case RMIHIDTOOL_CMD_ATTN:
342			report_attn = 1;
343			while(report_attn) {
344				unsigned int bytes = 256;
345				rc = device->GetAttentionReport(NULL,
346						RMI_INTERUPT_SOURCES_ALL_MASK,
347						report, &bytes);
348				if (rc > 0) {
349					print_buffer(report, bytes);
350					fprintf(stdout, "\n");
351				}
352			}
353			break;
354		case RMIHIDTOOL_CMD_PRINT_FUNCTIONS:
355			device->ScanPDT();
356			device->PrintFunctions();
357			break;
358		case RMIHIDTOOL_CMD_REBIND_DRIVER:
359			device->RebindDriver();
360			break;
361		case RMIHIDTOOL_CMD_PRINT_DEVICE_INFO:
362			device->PrintDeviceInfo();
363			break;
364		case RMIHIDTOOL_CMD_RESET_DEVICE:
365			device->ScanPDT();
366			device->Reset();
367			break;
368		case RMIHIDTOOL_CMD_INTERACTIVE:
369		default:
370			interactive(device, report);
371			break;
372	}
373
374	device->Close();
375
376	return 0;
377}
378