aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/scripts/python/export-to-sqlite.py
blob: f827bf77e9d26177fe7e19cd7e65d05cb97ac366 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# export-to-sqlite.py: export perf data to a sqlite3 database
# Copyright (c) 2017, Intel Corporation.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms and conditions of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.

import os
import sys
import struct
import datetime

# To use this script you will need to have installed package python-pyside which
# provides LGPL-licensed Python bindings for Qt.  You will also need the package
# libqt4-sql-sqlite for Qt sqlite3 support.
#
# An example of using this script with Intel PT:
#
#	$ perf record -e intel_pt//u ls
#	$ perf script -s ~/libexec/perf-core/scripts/python/export-to-sqlite.py pt_example branches calls
#	2017-07-31 14:26:07.326913 Creating database...
#	2017-07-31 14:26:07.538097 Writing records...
#	2017-07-31 14:26:09.889292 Adding indexes
#	2017-07-31 14:26:09.958746 Done
#
# To browse the database, sqlite3 can be used e.g.
#
#	$ sqlite3 pt_example
#	sqlite> .header on
#	sqlite> select * from samples_view where id < 10;
#	sqlite> .mode column
#	sqlite> select * from samples_view where id < 10;
#	sqlite> .tables
#	sqlite> .schema samples_view
#	sqlite> .quit
#
# An example of using the database is provided by the script
# call-graph-from-sql.py.  Refer to that script for details.
#
# The database structure is practically the same as created by the script
# export-to-postgresql.py. Refer to that script for details.  A notable
# difference is  the 'transaction' column of the 'samples' table which is
# renamed 'transaction_' in sqlite because 'transaction' is a reserved word.

from PySide.QtSql import *

sys.path.append(os.environ['PERF_EXEC_PATH'] + \
	'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')

# These perf imports are not used at present
#from perf_trace_context import *
#from Core import *

perf_db_export_mode = True
perf_db_export_calls = False
perf_db_export_callchains = False

def usage():
	print >> sys.stderr, "Usage is: export-to-sqlite.py <database name> [<columns>] [<calls>] [<callchains>]"
	print >> sys.stderr, "where:	columns		'all' or 'branches'"
	print >> sys.stderr, "		calls		'calls' => create calls and call_paths table"
	print >> sys.stderr, "		callchains	'callchains' => create call_paths table"
	raise Exception("Too few arguments")

if (len(sys.argv) < 2):
	usage()

dbname = sys.argv[1]

if (len(sys.argv) >= 3):
	columns = sys.argv[2]
else:
	columns = "all"

if columns not in ("all", "branches"):
	usage()

branches = (columns == "branches")

for i in range(3,len(sys.argv)):
	if (sys.argv[i] == "calls"):
		perf_db_export_calls = True
	elif (sys.argv[i] == "callchains"):
		perf_db_export_callchains = True
	else:
		usage()

def do_query(q, s):
	if (q.exec_(s)):
		return
	raise Exception("Query failed: " + q.lastError().text())

def do_query_(q):
	if (q.exec_()):
		return
	raise Exception("Query failed: " + q.lastError().text())

print datetime.datetime.today(), "Creating database..."

db_exists = False
try:
	f = open(dbname)
	f.close()
	db_exists = True
except:
	pass

if db_exists:
	raise Exception(dbname + " already exists")

db = QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName(dbname)
db.open()

query = QSqlQuery(db)

do_query(query, 'PRAGMA journal_mode = OFF')
do_query(query, 'BEGIN TRANSACTION')

do_query(query, 'CREATE TABLE selected_events ('
		'id		integer		NOT NULL	PRIMARY KEY,'
		'name		varchar(80))')
do_query(query, 'CREATE TABLE machines ('
		'id		integer		NOT NULL	PRIMARY KEY,'
		'pid		integer,'
		'root_dir 	varchar(4096))')
do_query(query, 'CREATE TABLE threads ('
		'id		integer		NOT NULL	PRIMARY KEY,'
		'machine_id	bigint,'
		'process_id	bigint,'
		'pid		integer,'
		'tid		integer)')
do_query(query, 'CREATE TABLE comms ('
		'id		integer		NOT NULL	PRIMARY KEY,'
		'comm		varchar(16))')
do_query(query, 'CREATE TABLE comm_threads ('
		'id		integer		NOT NULL	PRIMARY KEY,'
		'comm_id	bigint,'
		'thread_id	bigint)')
do_query(query, 'CREATE TABLE dsos ('
		'id		integer		NOT NULL	PRIMARY KEY,'
		'machine_id	bigint,'
		'short_name	varchar(256),'
		'long_name	varchar(4096),'
		'build_id	varchar(64))')
do_query(query, 'CREATE TABLE symbols ('
		'id		integer		NOT NULL	PRIMARY KEY,'
		'dso_id		bigint,'
		'sym_start	bigint,'
		'sym_end	bigint,'
		'binding	integer,'
		'name		varchar(2048))')
do_query(query, 'CREATE TABLE branch_types ('
		'id		integer		NOT NULL	PRIMARY KEY,'
		'name		varchar(80))')

if branches:
	do_query(query, 'CREATE TABLE samples ('
		'id		integer		NOT NULL	PRIMARY KEY,'
		'evsel_id	bigint,'
		'machine_id	bigint,'
		'thread_id	bigint,'
		'comm_id	bigint,'
		'dso_id		bigint,'
		'symbol_id	bigint,'
		'sym_offset	bigint,'
		'ip		bigint,'
		'time		bigint,'
		'cpu		integer,'
		'to_dso_id	bigint,'
		'to_symbol_id	bigint,'
		'to_sym_offset	bigint,'
		'to_ip		bigint,'
		'branch_type	integer,'
		'in_tx		boolean,'
		'call_path_id	bigint)')
else:
	do_query(query, 'CREATE TABLE samples ('
		'id		integer		NOT NULL	PRIMARY KEY,'
		'evsel_id	bigint,'
		'machine_id	bigint,'
		'thread_id	bigint,'
		'comm_id	bigint,'
		'dso_id		bigint,'
		'symbol_id	bigint,'
		'sym_offset	bigint,'
		'ip		bigint,'
		'time		bigint,'
		'cpu		integer,'
		'to_dso_id	bigint,'
		'to_symbol_id	bigint,'
		'to_sym_offset	bigint,'
		'to_ip		bigint,'
		'period		bigint,'
		'weight		bigint,'
		'transaction_	bigint,'
		'data_src	bigint,'
		'branch_type	integer,'
		'in_tx		boolean,'
		'call_path_id	bigint)')

if perf_db_export_calls or perf_db_export_callchains:
	do_query(query, 'CREATE TABLE call_paths ('
		'id		integer		NOT NULL	PRIMARY KEY,'
		'parent_id	bigint,'
		'symbol_id	bigint,'
		'ip		bigint)')
if perf_db_export_calls:
	do_query(query, 'CREATE TABLE calls ('
		'id		integer		NOT NULL	PRIMARY KEY,'
		'thread_id	bigint,'
		'comm_id	bigint,'
		'call_path_id	bigint,'
		'call_time	bigint,'
		'return_time	bigint,'
		'branch_count	bigint,'
		'call_id	bigint,'
		'return_id	bigint,'
		'parent_call_path_id	bigint,'
		'flags		integer)')

# printf was added to sqlite in version 3.8.3
sqlite_has_printf = False
try:
	do_query(query, 'SELECT printf("") FROM machines')
	sqlite_has_printf = True
except:
	pass

def emit_to_hex(x):
	if sqlite_has_printf:
		return 'printf("%x", ' + x + ')'
	else:
		return x

do_query(query, 'CREATE VIEW machines_view AS '
	'SELECT '
		'id,'
		'pid,'
		'root_dir,'
		'CASE WHEN id=0 THEN \'unknown\' WHEN pid=-1 THEN \'host\' ELSE \'guest\' END AS host_or_guest'
	' FROM machines')

do_query(query, 'CREATE VIEW dsos_view AS '
	'SELECT '
		'id,'
		'machine_id,'
		'(SELECT host_or_guest FROM machines_view WHERE id = machine_id) AS host_or_guest,'
		'short_name,'
		'long_name,'
		'build_id'
	' FROM dsos')

do_query(query, 'CREATE VIEW symbols_view AS '
	'SELECT '
		'id,'
		'name,'
		'(SELECT short_name FROM dsos WHERE id=dso_id) AS dso,'
		'dso_id,'
		'sym_start,'
		'sym_end,'
		'CASE WHEN binding=0 THEN \'local\' WHEN binding=1 THEN \'global\' ELSE \'weak\' END AS binding'
	' FROM symbols')

do_query(query, 'CREATE VIEW threads_view AS '
	'SELECT '
		'id,'
		'machine_id,'
		'(SELECT host_or_guest FROM machines_view WHERE id = machine_id) AS host_or_guest,'
		'process_id,'
		'pid,'
		'tid'
	' FROM threads')

do_query(query, 'CREATE VIEW comm_threads_view AS '
	'SELECT '
		'comm_id,'
		'(SELECT comm FROM comms WHERE id = comm_id) AS command,'
		'thread_id,'
		'(SELECT pid FROM threads WHERE id = thread_id) AS pid,'
		'(SELECT tid FROM threads WHERE id = thread_id) AS tid'
	' FROM comm_threads')

if perf_db_export_calls or perf_db_export_callchains:
	do_query(query, 'CREATE VIEW call_paths_view AS '
		'SELECT '
			'c.id,'
			+ emit_to_hex('c.ip') + ' AS ip,'
			'c.symbol_id,'
			'(SELECT name FROM symbols WHERE id = c.symbol_id) AS symbol,'
			'(SELECT dso_id FROM symbols WHERE id = c.symbol_id) AS dso_id,'
			'(SELECT dso FROM symbols_view  WHERE id = c.symbol_id) AS dso_short_name,'
			'c.parent_id,'
			+ emit_to_hex('p.ip') + ' AS parent_ip,'
			'p.symbol_id AS parent_symbol_id,'
			'(SELECT name FROM symbols WHERE id = p.symbol_id) AS parent_symbol,'
			'(SELECT dso_id FROM symbols WHERE id = p.symbol_id) AS parent_dso_id,'
			'(SELECT dso FROM symbols_view  WHERE id = p.symbol_id) AS parent_dso_short_name'
		' FROM call_paths c INNER JOIN call_paths p ON p.id = c.parent_id')
if perf_db_export_calls:
	do_query(query, 'CREATE VIEW calls_view AS '
		'SELECT '
			'calls.id,'
			'thread_id,'
			'(SELECT pid FROM threads WHERE id = thread_id) AS pid,'
			'(SELECT tid FROM threads WHERE id = thread_id) AS tid,'
			'(SELECT comm FROM comms WHERE id = comm_id) AS command,'
			'call_path_id,'
			+ emit_to_hex('ip') + ' AS ip,'
			'symbol_id,'
			'(SELECT name FROM symbols WHERE id = symbol_id) AS symbol,'
			'call_time,'
			'return_time,'
			'return_time - call_time AS elapsed_time,'
			'branch_count,'
			'call_id,'
			'return_id,'
			'CASE WHEN flags=1 THEN \'no call\' WHEN flags=2 THEN \'no return\' WHEN flags=3 THEN \'no call/return\' ELSE \'\' END AS flags,'
			'parent_call_path_id'
		' FROM calls INNER JOIN call_paths ON call_paths.id = call_path_id')

do_query(query, 'CREATE VIEW samples_view AS '
	'SELECT '
		'id,'
		'time,'
		'cpu,'
		'(SELECT pid FROM threads WHERE id = thread_id) AS pid,'
		'(SELECT tid FROM threads WHERE id = thread_id) AS tid,'
		'(SELECT comm FROM comms WHERE id = comm_id) AS command,'
		'(SELECT name FROM selected_events WHERE id = evsel_id) AS event,'
		+ emit_to_hex('ip') + ' AS ip_hex,'
		'(SELECT name FROM symbols WHERE id = symbol_id) AS symbol,'
		'sym_offset,'
		'(SELECT short_name FROM dsos WHERE id = dso_id) AS dso_short_name,'
		+ emit_to_hex('to_ip') + ' AS to_ip_hex,'
		'(SELECT name FROM symbols WHERE id = to_symbol_id) AS to_symbol,'
		'to_sym_offset,'
		'(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,'
		'(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,'
		'in_tx'
	' FROM samples')

do_query(query, 'END TRANSACTION')

evsel_query = QSqlQuery(db)
evsel_query.prepare("INSERT INTO selected_events VALUES (?, ?)")
machine_query = QSqlQuery(db)
machine_query.prepare("INSERT INTO machines VALUES (?, ?, ?)")
thread_query = QSqlQuery(db)
thread_query.prepare("INSERT INTO threads VALUES (?, ?, ?, ?, ?)")
comm_query = QSqlQuery(db)
comm_query.prepare("INSERT INTO comms VALUES (?, ?)")
comm_thread_query = QSqlQuery(db)
comm_thread_query.prepare("INSERT INTO comm_threads VALUES (?, ?, ?)")
dso_query = QSqlQuery(db)
dso_query.prepare("INSERT INTO dsos VALUES (?, ?, ?, ?, ?)")
symbol_query = QSqlQuery(db)
symbol_query.prepare("INSERT INTO symbols VALUES (?, ?, ?, ?, ?, ?)")
branch_type_query = QSqlQuery(db)
branch_type_query.prepare("INSERT INTO branch_types VALUES (?, ?)")
sample_query = QSqlQuery(db)
if branches:
	sample_query.prepare("INSERT INTO samples VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
else:
	sample_query.prepare("INSERT INTO samples VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
if perf_db_export_calls or perf_db_export_callchains:
	call_path_query = QSqlQuery(db)
	call_path_query.prepare("INSERT INTO call_paths VALUES (?, ?, ?, ?)")
if perf_db_export_calls:
	call_query = QSqlQuery(db)
	call_query.prepare("INSERT INTO calls VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")

def trace_begin():
	print datetime.datetime.today(), "Writing records..."
	do_query(query, 'BEGIN TRANSACTION')
	# id == 0 means unknown.  It is easier to create records for them than replace the zeroes with NULLs
	evsel_table(0, "unknown")
	machine_table(0, 0, "unknown")
	thread_table(0, 0, 0, -1, -1)
	comm_table(0, "unknown")
	dso_table(0, 0, "unknown", "unknown", "")
	symbol_table(0, 0, 0, 0, 0, "unknown")
	sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
	if perf_db_export_calls or perf_db_export_callchains:
		call_path_table(0, 0, 0, 0)

unhandled_count = 0

def trace_end():
	do_query(query, 'END TRANSACTION')

	print datetime.datetime.today(), "Adding indexes"
	if perf_db_export_calls:
		do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)')

	if (unhandled_count):
		print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events"
	print datetime.datetime.today(), "Done"

def trace_unhandled(event_name, context, event_fields_dict):
	global unhandled_count
	unhandled_count += 1

def sched__sched_switch(*x):
	pass

def bind_exec(q, n, x):
	for xx in x[0:n]:
		q.addBindValue(str(xx))
	do_query_(q)

def evsel_table(*x):
	bind_exec(evsel_query, 2, x)

def machine_table(*x):
	bind_exec(machine_query, 3, x)

def thread_table(*x):
	bind_exec(thread_query, 5, x)

def comm_table(*x):
	bind_exec(comm_query, 2, x)

def comm_thread_table(*x):
	bind_exec(comm_thread_query, 3, x)

def dso_table(*x):
	bind_exec(dso_query, 5, x)

def symbol_table(*x):
	bind_exec(symbol_query, 6, x)

def branch_type_table(*x):
	bind_exec(branch_type_query, 2, x)

def sample_table(*x):
	if branches:
		bind_exec(sample_query, 18, x)
	else:
		bind_exec(sample_query, 22, x)

def call_path_table(*x):
	bind_exec(call_path_query, 4, x)

def call_return_table(*x):
	bind_exec(call_query, 11, x)
s="kt">short address, unsigned char *valp, int size) { int retval = 0; int retry_count = 0; int index; struct i2c_client *i2c = pdata->i2c_client; mutex_lock(&(pdata->rmi4_page_mutex)); retval = synaptics_rmi4_set_page(pdata, address); if (retval != PAGE_LEN) goto exit; index = address & MASK_8BIT; retry: retval = i2c_smbus_read_i2c_block_data(i2c, index, size, valp); if (retval != size) { if (++retry_count == MAX_RETRY_COUNT) dev_err(&i2c->dev, "%s:address 0x%04x size %d failed:%d\n", __func__, address, size, retval); else { synaptics_rmi4_set_page(pdata, address); goto retry; } } exit: mutex_unlock(&(pdata->rmi4_page_mutex)); return retval; } /** * synaptics_rmi4_i2c_byte_write() - write the single byte data * @pdata: pointer to synaptics_rmi4_data structure * @address: write the block of data from this offset * @data: data to be write * * This function is to write the single byte data and returns integer. */ static int synaptics_rmi4_i2c_byte_write(struct synaptics_rmi4_data *pdata, unsigned short address, unsigned char data) { unsigned char txbuf[2]; int retval = 0; struct i2c_client *i2c = pdata->i2c_client; /* Can't have anyone else changing the page behind our backs */ mutex_lock(&(pdata->rmi4_page_mutex)); retval = synaptics_rmi4_set_page(pdata, address); if (retval != PAGE_LEN) goto exit; txbuf[0] = address & MASK_8BIT; txbuf[1] = data; retval = i2c_master_send(pdata->i2c_client, txbuf, 2); /* Add in retry on writes only in certian error return values */ if (retval != 2) { dev_err(&i2c->dev, "%s:failed:%d\n", __func__, retval); retval = -EIO; } else retval = 1; exit: mutex_unlock(&(pdata->rmi4_page_mutex)); return retval; } /** * synpatics_rmi4_touchpad_report() - reports for the rmi4 touchpad device * @pdata: pointer to synaptics_rmi4_data structure * @rfi: pointer to synaptics_rmi4_fn structure * * This function calls to reports for the rmi4 touchpad device */ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata, struct synaptics_rmi4_fn *rfi) { /* number of touch points - fingers down in this case */ int touch_count = 0; int finger; int fingers_supported; int finger_registers; int reg; int finger_shift; int finger_status; int retval; unsigned short data_base_addr; unsigned short data_offset; unsigned char data_reg_blk_size; unsigned char values[2]; unsigned char data[DATA_LEN]; int x[RMI4_NUMBER_OF_MAX_FINGERS]; int y[RMI4_NUMBER_OF_MAX_FINGERS]; int wx[RMI4_NUMBER_OF_MAX_FINGERS]; int wy[RMI4_NUMBER_OF_MAX_FINGERS]; struct i2c_client *client = pdata->i2c_client; /* get 2D sensor finger data */ /* * First get the finger status field - the size of the finger status * field is determined by the number of finger supporte - 2 bits per * finger, so the number of registers to read is: * registerCount = ceil(numberOfFingers/4). * Read the required number of registers and check each 2 bit field to * determine if a finger is down: * 00 = finger not present, * 01 = finger present and data accurate, * 10 = finger present but data may not be accurate, * 11 = reserved for product use. */ fingers_supported = rfi->num_of_data_points; finger_registers = (fingers_supported + 3)/4; data_base_addr = rfi->fn_desc.data_base_addr; retval = synaptics_rmi4_i2c_block_read(pdata, data_base_addr, values, finger_registers); if (retval != finger_registers) { dev_err(&client->dev, "%s:read status registers failed\n", __func__); return 0; } /* * For each finger present, read the proper number of registers * to get absolute data. */ data_reg_blk_size = rfi->size_of_data_register_block; for (finger = 0; finger < fingers_supported; finger++) { /* determine which data byte the finger status is in */ reg = finger/4; /* bit shift to get finger's status */ finger_shift = (finger % 4) * 2; finger_status = (values[reg] >> finger_shift) & 3; /* * if finger status indicates a finger is present then * read the finger data and report it */ if (finger_status == 1 || finger_status == 2) { /* Read the finger data */ data_offset = data_base_addr + ((finger * data_reg_blk_size) + finger_registers); retval = synaptics_rmi4_i2c_block_read(pdata, data_offset, data, data_reg_blk_size); if (retval != data_reg_blk_size) { printk(KERN_ERR "%s:read data failed\n", __func__); return 0; } else { x[touch_count] = (data[0] << 4) | (data[2] & MASK_4BIT); y[touch_count] = (data[1] << 4) | ((data[2] >> 4) & MASK_4BIT); wy[touch_count] = (data[3] >> 4) & MASK_4BIT; wx[touch_count] = (data[3] & MASK_4BIT); if (pdata->board->x_flip) x[touch_count] = pdata->sensor_max_x - x[touch_count]; if (pdata->board->y_flip) y[touch_count] = pdata->sensor_max_y - y[touch_count]; } /* number of active touch points */ touch_count++; } } /* report to input subsystem */ if (touch_count) { for (finger = 0; finger < touch_count; finger++) { input_report_abs(pdata->input_dev, ABS_MT_TOUCH_MAJOR, max(wx[finger] , wy[finger])); input_report_abs(pdata->input_dev, ABS_MT_POSITION_X, x[finger]); input_report_abs(pdata->input_dev, ABS_MT_POSITION_Y, y[finger]); input_mt_sync(pdata->input_dev); } } else input_mt_sync(pdata->input_dev); /* sync after groups of events */ input_sync(pdata->input_dev); /* return the number of touch points */ return touch_count; } /** * synaptics_rmi4_report_device() - reports the rmi4 device * @pdata: pointer to synaptics_rmi4_data structure * @rfi: pointer to synaptics_rmi4_fn * * This function is used to call the report function of the rmi4 device. */ static int synaptics_rmi4_report_device(struct synaptics_rmi4_data *pdata, struct synaptics_rmi4_fn *rfi) { int touch = 0; struct i2c_client *client = pdata->i2c_client; static int num_error_reports; if (rfi->fn_number != SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM) { num_error_reports++; if (num_error_reports < MAX_ERROR_REPORT) dev_err(&client->dev, "%s:report not supported\n", __func__); } else touch = synpatics_rmi4_touchpad_report(pdata, rfi); return touch; } /** * synaptics_rmi4_sensor_report() - reports to input subsystem * @pdata: pointer to synaptics_rmi4_data structure * * This function is used to reads in all data sources and reports * them to the input subsystem. */ static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *pdata) { unsigned char intr_status[4]; /* number of touch points - fingers or buttons */ int touch = 0; unsigned int retval; struct synaptics_rmi4_fn *rfi; struct synaptics_rmi4_device_info *rmi; struct i2c_client *client = pdata->i2c_client; /* * Get the interrupt status from the function $01 * control register+1 to find which source(s) were interrupting * so we can read the data from the source(s) (2D sensor, buttons..) */ retval = synaptics_rmi4_i2c_block_read(pdata, pdata->fn01_data_base_addr + 1, intr_status, pdata->number_of_interrupt_register); if (retval != pdata->number_of_interrupt_register) { dev_err(&client->dev, "could not read interrupt status registers\n"); return 0; } /* * check each function that has data sources and if the interrupt for * that triggered then call that RMI4 functions report() function to * gather data and report it to the input subsystem */ rmi = &(pdata->rmi4_mod_info); list_for_each_entry(rfi, &rmi->support_fn_list, link) { if (rfi->num_of_data_sources) { if (intr_status[rfi->index_to_intr_reg] & rfi->intr_mask) touch = synaptics_rmi4_report_device(pdata, rfi); } } /* return the number of touch points */ return touch; } /** * synaptics_rmi4_irq() - thread function for rmi4 attention line * @irq: irq value * @data: void pointer * * This function is interrupt thread function. It just notifies the * application layer that attention is required. */ static irqreturn_t synaptics_rmi4_irq(int irq, void *data) { struct synaptics_rmi4_data *pdata = data; int touch_count; do { touch_count = synaptics_rmi4_sensor_report(pdata); if (touch_count) wait_event_timeout(pdata->wait, pdata->touch_stopped, msecs_to_jiffies(1)); else break; } while (!pdata->touch_stopped); return IRQ_HANDLED; } /** * synpatics_rmi4_touchpad_detect() - detects the rmi4 touchpad device * @pdata: pointer to synaptics_rmi4_data structure * @rfi: pointer to synaptics_rmi4_fn structure * @fd: pointer to synaptics_rmi4_fn_desc structure * @interruptcount: count the number of interrupts * * This function calls to detects the rmi4 touchpad device */ static int synpatics_rmi4_touchpad_detect(struct synaptics_rmi4_data *pdata, struct synaptics_rmi4_fn *rfi, struct synaptics_rmi4_fn_desc *fd, unsigned int interruptcount) { unsigned char queries[QUERY_LEN]; unsigned short intr_offset; unsigned char abs_data_size; unsigned char abs_data_blk_size; unsigned char egr_0, egr_1; unsigned int all_data_blk_size; int has_pinch, has_flick, has_tap; int has_tapandhold, has_doubletap; int has_earlytap, has_press; int has_palmdetect, has_rotate; int has_rel; int i; int retval; struct i2c_client *client = pdata->i2c_client; rfi->fn_desc.query_base_addr = fd->query_base_addr; rfi->fn_desc.data_base_addr = fd->data_base_addr; rfi->fn_desc.intr_src_count = fd->intr_src_count; rfi->fn_desc.fn_number = fd->fn_number; rfi->fn_number = fd->fn_number; rfi->num_of_data_sources = fd->intr_src_count; rfi->fn_desc.ctrl_base_addr = fd->ctrl_base_addr; rfi->fn_desc.cmd_base_addr = fd->cmd_base_addr; /* * need to get number of fingers supported, data size, etc. * to be used when getting data since the number of registers to * read depends on the number of fingers supported and data size. */ retval = synaptics_rmi4_i2c_block_read(pdata, fd->query_base_addr, queries, sizeof(queries)); if (retval != sizeof(queries)) { dev_err(&client->dev, "%s:read function query registers\n", __func__); return retval; } /* * 2D data sources have only 3 bits for the number of fingers * supported - so the encoding is a bit wierd. */ if ((queries[1] & MASK_3BIT) <= 4) /* add 1 since zero based */ rfi->num_of_data_points = (queries[1] & MASK_3BIT) + 1; else { /* * a value of 5 is up to 10 fingers - 6 and 7 are reserved * (shouldn't get these i int retval;n a normal 2D source). */ if ((queries[1] & MASK_3BIT) == 5) rfi->num_of_data_points = 10; } /* Need to get interrupt info for handling interrupts */ rfi->index_to_intr_reg = (interruptcount + 7)/8; if (rfi->index_to_intr_reg != 0) rfi->index_to_intr_reg -= 1; /* * loop through interrupts for each source in fn $11 * and or in a bit to the interrupt mask for each. */ intr_offset = interruptcount % 8; rfi->intr_mask = 0; for (i = intr_offset; i < ((fd->intr_src_count & MASK_3BIT) + intr_offset); i++) rfi->intr_mask |= 1 << i; /* Size of just the absolute data for one finger */ abs_data_size = queries[5] & MASK_2BIT; /* One each for X and Y, one for LSB for X & Y, one for W, one for Z */ abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0)); rfi->size_of_data_register_block = abs_data_blk_size; /* * need to determine the size of data to read - this depends on * conditions such as whether Relative data is reported and if Gesture * data is reported. */ egr_0 = queries[7]; egr_1 = queries[8]; /* * Get info about what EGR data is supported, whether it has * Relative data supported, etc. */ has_pinch = egr_0 & HAS_PINCH; has_flick = egr_0 & HAS_FLICK; has_tap = egr_0 & HAS_TAP; has_earlytap = egr_0 & HAS_EARLYTAP; has_press = egr_0 & HAS_PRESS; has_rotate = egr_1 & HAS_ROTATE; has_rel = queries[1] & HAS_RELEASE; has_tapandhold = egr_0 & HAS_TAPANDHOLD; has_doubletap = egr_0 & HAS_DOUBLETAP; has_palmdetect = egr_1 & HAS_PALMDETECT; /* * Size of all data including finger status, absolute data for each * finger, relative data and EGR data */ all_data_blk_size = /* finger status, four fingers per register */ ((rfi->num_of_data_points + 3) / 4) + /* absolute data, per finger times number of fingers */ (abs_data_blk_size * rfi->num_of_data_points) + /* * two relative registers (if relative is being reported) */ 2 * has_rel + /* * F11_2D_data8 is only present if the egr_0 * register is non-zero. */ !!(egr_0) + /* * F11_2D_data9 is only present if either egr_0 or * egr_1 registers are non-zero. */ (egr_0 || egr_1) + /* * F11_2D_data10 is only present if EGR_PINCH or EGR_FLICK of * egr_0 reports as 1. */ !!(has_pinch | has_flick) + /* * F11_2D_data11 and F11_2D_data12 are only present if * EGR_FLICK of egr_0 reports as 1. */ 2 * !!(has_flick); return retval; } /** * synpatics_rmi4_touchpad_config() - confiures the rmi4 touchpad device * @pdata: pointer to synaptics_rmi4_data structure * @rfi: pointer to synaptics_rmi4_fn structure * * This function calls to confiures the rmi4 touchpad device */ int synpatics_rmi4_touchpad_config(struct synaptics_rmi4_data *pdata, struct synaptics_rmi4_fn *rfi) { /* * For the data source - print info and do any * source specific configuration. */ unsigned char data[BUF_LEN]; int retval = 0; struct i2c_client *client = pdata->i2c_client; /* Get and print some info about the data source... */ /* To Query 2D devices we need to read from the address obtained * from the function descriptor stored in the RMI function info. */ retval = synaptics_rmi4_i2c_block_read(pdata, rfi->fn_desc.query_base_addr, data, QUERY_LEN); if (retval != QUERY_LEN) dev_err(&client->dev, "%s:read query registers failed\n", __func__); else { retval = synaptics_rmi4_i2c_block_read(pdata, rfi->fn_desc.ctrl_base_addr, data, DATA_BUF_LEN); if (retval != DATA_BUF_LEN) { dev_err(&client->dev, "%s:read control registers failed\n", __func__); return retval; } /* Store these for use later*/ pdata->sensor_max_x = ((data[6] & MASK_8BIT) << 0) | ((data[7] & MASK_4BIT) << 8); pdata->sensor_max_y = ((data[8] & MASK_5BIT) << 0) | ((data[9] & MASK_4BIT) << 8); } return retval; } /** * synaptics_rmi4_i2c_query_device() - query the rmi4 device * @pdata: pointer to synaptics_rmi4_data structure * * This function is used to query the rmi4 device. */ static int synaptics_rmi4_i2c_query_device(struct synaptics_rmi4_data *pdata) { int i; int retval; unsigned char std_queries[STD_QUERY_LEN]; unsigned char intr_count = 0; int data_sources = 0; unsigned int ctrl_offset; struct synaptics_rmi4_fn *rfi; struct synaptics_rmi4_fn_desc rmi_fd; struct synaptics_rmi4_device_info *rmi; struct i2c_client *client = pdata->i2c_client; /* * init the physical drivers RMI module * info list of functions */ INIT_LIST_HEAD(&pdata->rmi4_mod_info.support_fn_list); /* * Read the Page Descriptor Table to determine what functions * are present */ for (i = PDT_START_SCAN_LOCATION; i > PDT_END_SCAN_LOCATION; i -= PDT_ENTRY_SIZE) { retval = synaptics_rmi4_i2c_block_read(pdata, i, (unsigned char *)&rmi_fd, sizeof(rmi_fd)); if (retval != sizeof(rmi_fd)) { /* failed to read next PDT entry */ dev_err(&client->dev, "%s: read error\n", __func__); return -EIO; } rfi = NULL; if (rmi_fd.fn_number) { switch (rmi_fd.fn_number & MASK_8BIT) { case SYNAPTICS_RMI4_DEVICE_CONTROL_FUNC_NUM: pdata->fn01_query_base_addr = rmi_fd.query_base_addr; pdata->fn01_ctrl_base_addr = rmi_fd.ctrl_base_addr; pdata->fn01_data_base_addr = rmi_fd.data_base_addr; break; case SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM: if (rmi_fd.intr_src_count) { rfi = kmalloc(sizeof(*rfi), GFP_KERNEL); if (!rfi) { dev_err(&client->dev, "%s:kmalloc failed\n", __func__); return -ENOMEM; } retval = synpatics_rmi4_touchpad_detect (pdata, rfi, &rmi_fd, intr_count); if (retval < 0) return retval; } break; } /* interrupt count for next iteration */ intr_count += (rmi_fd.intr_src_count & MASK_3BIT); /* * We only want to add functions to the list * that have data associated with them. */ if (rfi && rmi_fd.intr_src_count) { /* link this function info to the RMI module */ mutex_lock(&(pdata->fn_list_mutex)); list_add_tail(&rfi->link, &pdata->rmi4_mod_info.support_fn_list); mutex_unlock(&(pdata->fn_list_mutex)); } } else { /* * A zero in the function number * signals the end of the PDT */ dev_dbg(&client->dev, "%s:end of PDT\n", __func__); break; } } /* * calculate the interrupt register count - used in the * ISR to read the correct number of interrupt registers */ pdata->number_of_interrupt_register = (intr_count + 7) / 8; /* * Function $01 will be used to query the product properties, * and product ID so we had to read the PDT above first to get * the Fn $01 query address and prior to filling in the product * info. NOTE: Even an unflashed device will still have FN $01. */ /* Load up the standard queries and get the RMI4 module info */ retval = synaptics_rmi4_i2c_block_read(pdata, pdata->fn01_query_base_addr, std_queries, sizeof(std_queries)); if (retval != sizeof(std_queries)) { dev_err(&client->dev, "%s:Failed reading queries\n", __func__); return -EIO; } /* Currently supported RMI version is 4.0 */ pdata->rmi4_mod_info.version_major = 4; pdata->rmi4_mod_info.version_minor = 0; /* * get manufacturer id, product_props, product info, * date code, tester id, serial num and product id (name) */ pdata->rmi4_mod_info.manufacturer_id = std_queries[0]; pdata->rmi4_mod_info.product_props = std_queries[1]; pdata->rmi4_mod_info.product_info[0] = std_queries[2]; pdata->rmi4_mod_info.product_info[1] = std_queries[3]; /* year - 2001-2032 */ pdata->rmi4_mod_info.date_code[0] = std_queries[4] & MASK_5BIT; /* month - 1-12 */ pdata->rmi4_mod_info.date_code[1] = std_queries[5] & MASK_4BIT; /* day - 1-31 */ pdata->rmi4_mod_info.date_code[2] = std_queries[6] & MASK_5BIT; pdata->rmi4_mod_info.tester_id = ((std_queries[7] & MASK_7BIT) << 8) | (std_queries[8] & MASK_7BIT); pdata->rmi4_mod_info.serial_number = ((std_queries[9] & MASK_7BIT) << 8) | (std_queries[10] & MASK_7BIT); memcpy(pdata->rmi4_mod_info.product_id_string, &std_queries[11], 10); /* Check if this is a Synaptics device - report if not. */ if (pdata->rmi4_mod_info.manufacturer_id != 1) dev_err(&client->dev, "%s: non-Synaptics mfg id:%d\n", __func__, pdata->rmi4_mod_info.manufacturer_id); list_for_each_entry(rfi, &pdata->rmi4_mod_info.support_fn_list, link) data_sources += rfi->num_of_data_sources; if (data_sources) { rmi = &(pdata->rmi4_mod_info); list_for_each_entry(rfi, &rmi->support_fn_list, link) { if (rfi->num_of_data_sources) { if (rfi->fn_number == SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM) { retval = synpatics_rmi4_touchpad_config (pdata, rfi); if (retval < 0) return retval; } else dev_err(&client->dev, "%s:fn_number not supported\n", __func__); /* * Turn on interrupts for this * function's data sources. */ ctrl_offset = pdata->fn01_ctrl_base_addr + 1 + rfi->index_to_intr_reg; retval = synaptics_rmi4_i2c_byte_write(pdata, ctrl_offset, rfi->intr_mask); if (retval < 0) return retval; } } } return 0; } /** * synaptics_rmi4_probe() - Initialze the i2c-client touchscreen driver * @i2c: i2c client structure pointer * @id:i2c device id pointer * * This function will allocate and initialize the instance * data and request the irq and set the instance data as the clients * platform data then register the physical driver which will do a scan of * the rmi4 Physical Device Table and enumerate any rmi4 functions that * have data sources associated with them. */ static int __devinit synaptics_rmi4_probe (struct i2c_client *client, const struct i2c_device_id *dev_id) { int retval; unsigned char intr_status[4]; struct synaptics_rmi4_data *rmi4_data; const struct synaptics_rmi4_platform_data *platformdata = client->dev.platform_data; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&client->dev, "i2c smbus byte data not supported\n"); return -EIO; } if (!platformdata) { dev_err(&client->dev, "%s: no platform data\n", __func__); return -EINVAL; } /* Allocate and initialize the instance data for this client */ rmi4_data = kzalloc(sizeof(struct synaptics_rmi4_data) * 2, GFP_KERNEL); if (!rmi4_data) { dev_err(&client->dev, "%s: no memory allocated\n", __func__); return -ENOMEM; } rmi4_data->input_dev = input_allocate_device(); if (rmi4_data->input_dev == NULL) { dev_err(&client->dev, "%s:input device alloc failed\n", __func__); retval = -ENOMEM; goto err_input; } dev_set_name(&client->dev, platformdata->name); if (platformdata->regulator_en) { rmi4_data->regulator = regulator_get(&client->dev, "v-touch"); if (IS_ERR(rmi4_data->regulator)) { dev_err(&client->dev, "%s:get regulator failed\n", __func__); retval = PTR_ERR(rmi4_data->regulator); goto err_regulator; } regulator_enable(rmi4_data->regulator); } init_waitqueue_head(&rmi4_data->wait); /* * Copy i2c_client pointer into RTID's i2c_client pointer for * later use in rmi4_read, rmi4_write, etc. */ rmi4_data->i2c_client = client; /* So we set the page correctly the first time */ rmi4_data->current_page = MASK_16BIT; rmi4_data->board = platformdata; rmi4_data->touch_stopped = false; /* init the mutexes for maintain the lists */ mutex_init(&(rmi4_data->fn_list_mutex)); mutex_init(&(rmi4_data->rmi4_page_mutex)); /* * Register physical driver - this will call the detect function that * will then scan the device and determine the supported * rmi4 functions. */ retval = synaptics_rmi4_i2c_query_device(rmi4_data); if (retval) { dev_err(&client->dev, "%s: rmi4 query device failed\n", __func__); goto err_query_dev; } /* Store the instance data in the i2c_client */ i2c_set_clientdata(client, rmi4_data); /*initialize the input device parameters */ rmi4_data->input_dev->name = DRIVER_NAME; rmi4_data->input_dev->phys = "Synaptics_Clearpad"; rmi4_data->input_dev->id.bustype = BUS_I2C; rmi4_data->input_dev->dev.parent = &client->dev; input_set_drvdata(rmi4_data->input_dev, rmi4_data); /* Initialize the function handlers for rmi4 */ set_bit(EV_SYN, rmi4_data->input_dev->evbit); set_bit(EV_KEY, rmi4_data->input_dev->evbit); set_bit(EV_ABS, rmi4_data->input_dev->evbit); input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_X, 0, rmi4_data->sensor_max_x, 0, 0); input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_Y, 0, rmi4_data->sensor_max_y, 0, 0); input_set_abs_params(rmi4_data->input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_TOUCH_MAJOR, 0, 0); retval = input_register_device(rmi4_data->input_dev); if (retval) { dev_err(&client->dev, "%s:input register failed\n", __func__); goto err_input_register; } /* Clear interrupts */ synaptics_rmi4_i2c_block_read(rmi4_data, rmi4_data->fn01_data_base_addr + 1, intr_status, rmi4_data->number_of_interrupt_register); retval = request_threaded_irq(platformdata->irq_number, NULL, synaptics_rmi4_irq, platformdata->irq_type, platformdata->name, rmi4_data); if (retval) { dev_err(&client->dev, "%s:Unable to get attn irq %d\n", __func__, platformdata->irq_number); goto err_request_irq; } return retval; err_request_irq: free_irq(platformdata->irq_number, rmi4_data); input_unregister_device(rmi4_data->input_dev); err_input_register: i2c_set_clientdata(client, NULL); err_query_dev: if (platformdata->regulator_en) { regulator_disable(rmi4_data->regulator); regulator_put(rmi4_data->regulator); } err_regulator: input_free_device(rmi4_data->input_dev); rmi4_data->input_dev = NULL; err_input: kfree(rmi4_data); return retval; } /** * synaptics_rmi4_remove() - Removes the i2c-client touchscreen driver * @client: i2c client structure pointer * * This funtion uses to remove the i2c-client * touchscreen driver and returns integer. */ static int __devexit synaptics_rmi4_remove(struct i2c_client *client) { struct synaptics_rmi4_data *rmi4_data = i2c_get_clientdata(client); const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board; rmi4_data->touch_stopped = true; wake_up(&rmi4_data->wait); free_irq(pdata->irq_number, rmi4_data); input_unregister_device(rmi4_data->input_dev); if (pdata->regulator_en) { regulator_disable(rmi4_data->regulator); regulator_put(rmi4_data->regulator); } kfree(rmi4_data); return 0; } #ifdef CONFIG_PM /** * synaptics_rmi4_suspend() - suspend the touch screen controller * @dev: pointer to device structure * * This funtion is used to suspend the * touch panel controller and returns integer */ static int synaptics_rmi4_suspend(struct device *dev) { /* Touch sleep mode */ int retval; unsigned char intr_status; struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board; rmi4_data->touch_stopped = true; disable_irq(pdata->irq_number); retval = synaptics_rmi4_i2c_block_read(rmi4_data, rmi4_data->fn01_data_base_addr + 1, &intr_status, rmi4_data->number_of_interrupt_register); if (retval < 0) return retval; retval = synaptics_rmi4_i2c_byte_write(rmi4_data, rmi4_data->fn01_ctrl_base_addr + 1, (intr_status & ~TOUCHPAD_CTRL_INTR)); if (retval < 0) return retval; if (pdata->regulator_en) regulator_disable(rmi4_data->regulator); return 0; } /** * synaptics_rmi4_resume() - resume the touch screen controller * @dev: pointer to device structure * * This funtion is used to resume the touch panel * controller and returns integer. */ static int synaptics_rmi4_resume(struct device *dev) { int retval; unsigned char intr_status; struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board; if (pdata->regulator_en) regulator_enable(rmi4_data->regulator); enable_irq(pdata->irq_number); rmi4_data->touch_stopped = false; retval = synaptics_rmi4_i2c_block_read(rmi4_data, rmi4_data->fn01_data_base_addr + 1, &intr_status, rmi4_data->number_of_interrupt_register); if (retval < 0) return retval; retval = synaptics_rmi4_i2c_byte_write(rmi4_data, rmi4_data->fn01_ctrl_base_addr + 1, (intr_status | TOUCHPAD_CTRL_INTR)); if (retval < 0) return retval; return 0; } static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = { .suspend = synaptics_rmi4_suspend, .resume = synaptics_rmi4_resume, }; #endif static const struct i2c_device_id synaptics_rmi4_id_table[] = { { DRIVER_NAME, 0 }, { }, }; MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table); static struct i2c_driver synaptics_rmi4_driver = { .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &synaptics_rmi4_dev_pm_ops, #endif }, .probe = synaptics_rmi4_probe, .remove = __devexit_p(synaptics_rmi4_remove), .id_table = synaptics_rmi4_id_table, }; /** * synaptics_rmi4_init() - Initialize the touchscreen driver * * This funtion uses to initializes the synaptics * touchscreen driver and returns integer. */ static int __init synaptics_rmi4_init(void) { return i2c_add_driver(&synaptics_rmi4_driver); } /** * synaptics_rmi4_exit() - De-initialize the touchscreen driver * * This funtion uses to de-initialize the synaptics * touchscreen driver and returns none. */ static void __exit synaptics_rmi4_exit(void) { i2c_del_driver(&synaptics_rmi4_driver); } module_init(synaptics_rmi4_init); module_exit(synaptics_rmi4_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("naveen.gaddipati@stericsson.com, js.ha@stericsson.com"); MODULE_DESCRIPTION("synaptics rmi4 i2c touch Driver"); MODULE_ALIAS("i2c:synaptics_rmi4_ts");