blob: 682d8763a1927312e88763bf75b38cfafaf113b7 [file] [log] [blame]
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Shalabh Jain1c99e4c2012-03-26 18:47:59 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/slab.h>
14#include <linux/init.h>
15#include <linux/uaccess.h>
16#include <linux/diagchar.h>
17#include <linux/sched.h>
18#include <linux/err.h>
Shalabh Jain5e9a92b2012-06-07 21:53:49 -070019#include <linux/delay.h>
Shalabh Jain1c99e4c2012-03-26 18:47:59 -070020#include <linux/workqueue.h>
21#include <linux/pm_runtime.h>
22#include <linux/platform_device.h>
23#include <asm/current.h>
24#ifdef CONFIG_DIAG_OVER_USB
25#include <mach/usbdiag.h>
26#endif
27#include "diagchar_hdlc.h"
28#include "diagmem.h"
29#include "diagchar.h"
30#include "diagfwd.h"
Shalabh Jain16794902012-09-14 10:56:49 -070031#include "diagfwd_cntl.h"
Shalabh Jain1c99e4c2012-03-26 18:47:59 -070032#include "diag_dci.h"
33
34unsigned int dci_max_reg = 100;
35unsigned int dci_max_clients = 10;
Shalabh Jain16794902012-09-14 10:56:49 -070036unsigned char dci_cumulative_log_mask[DCI_LOG_MASK_SIZE];
37unsigned char dci_cumulative_event_mask[DCI_EVENT_MASK_SIZE];
Shalabh Jaina1c69a42012-10-23 12:51:30 -070038struct mutex dci_log_mask_mutex;
39struct mutex dci_event_mask_mutex;
Mohit Aggarwal3aa45862013-03-05 18:37:58 +053040struct mutex dci_health_mutex;
Shalabh Jain16794902012-09-14 10:56:49 -070041
42#define DCI_CHK_CAPACITY(entry, new_data_len) \
43((entry->data_len + new_data_len > entry->total_capacity) ? 1 : 0) \
Shalabh Jain1c99e4c2012-03-26 18:47:59 -070044
Dixon Peterson66fb11b2012-12-04 20:30:54 -080045/* Process the data read from the smd dci channel */
46int diag_process_smd_dci_read_data(struct diag_smd_info *smd_info, void *buf,
47 int recd_bytes)
Shalabh Jain1c99e4c2012-03-26 18:47:59 -070048{
Dixon Peterson66fb11b2012-12-04 20:30:54 -080049 int read_bytes, dci_pkt_len, i;
Shalabh Jain16794902012-09-14 10:56:49 -070050 uint8_t recv_pkt_cmd_code;
Shalabh Jain1c99e4c2012-03-26 18:47:59 -070051
Dixon Peterson66fb11b2012-12-04 20:30:54 -080052 /* Each SMD read can have multiple DCI packets */
53 read_bytes = 0;
54 while (read_bytes < recd_bytes) {
55 /* read actual length of dci pkt */
56 dci_pkt_len = *(uint16_t *)(buf+2);
57 /* process one dci packet */
58 pr_debug("diag: bytes read = %d, single dci pkt len = %d\n",
59 read_bytes, dci_pkt_len);
60 /* print_hex_dump(KERN_DEBUG, "Single DCI packet :",
61 DUMP_PREFIX_ADDRESS, 16, 1, buf, 5 + dci_pkt_len, 1);*/
62 recv_pkt_cmd_code = *(uint8_t *)(buf+4);
63 if (recv_pkt_cmd_code == LOG_CMD_CODE)
64 extract_dci_log(buf+4);
65 else if (recv_pkt_cmd_code == EVENT_CMD_CODE)
66 extract_dci_events(buf+4);
67 else
68 extract_dci_pkt_rsp(buf); /* pkt response */
69 read_bytes += 5 + dci_pkt_len;
70 buf += 5 + dci_pkt_len; /* advance to next DCI pkt */
Shalabh Jain1c99e4c2012-03-26 18:47:59 -070071 }
Dixon Peterson66fb11b2012-12-04 20:30:54 -080072 /* wake up all sleeping DCI clients which have some data */
73 for (i = 0; i < MAX_DCI_CLIENTS; i++) {
74 if (driver->dci_client_tbl[i].client &&
75 driver->dci_client_tbl[i].data_len) {
76 smd_info->in_busy_1 = 1;
77 diag_update_sleeping_process(
78 driver->dci_client_tbl[i].client->tgid,
79 DCI_DATA_TYPE);
Shalabh Jain1c99e4c2012-03-26 18:47:59 -070080 }
81 }
Dixon Peterson66fb11b2012-12-04 20:30:54 -080082
83 return 0;
Shalabh Jain16794902012-09-14 10:56:49 -070084}
Shalabh Jain1c99e4c2012-03-26 18:47:59 -070085
Shalabh Jain16794902012-09-14 10:56:49 -070086void extract_dci_pkt_rsp(unsigned char *buf)
87{
88 int i = 0, index = -1, cmd_code_len = 1;
89 int curr_client_pid = 0, write_len;
90 struct diag_dci_client_tbl *entry;
91 void *temp_buf = NULL;
92 uint8_t recv_pkt_cmd_code;
93
94 recv_pkt_cmd_code = *(uint8_t *)(buf+4);
95 if (recv_pkt_cmd_code != DCI_PKT_RSP_CODE)
96 cmd_code_len = 4; /* delayed response */
97 write_len = (int)(*(uint16_t *)(buf+2)) - cmd_code_len;
98 pr_debug("diag: len = %d\n", write_len);
99 /* look up DCI client with tag */
100 for (i = 0; i < dci_max_reg; i++) {
101 if (driver->req_tracking_tbl[i].tag ==
102 *(int *)(buf+(4+cmd_code_len))) {
103 *(int *)(buf+4+cmd_code_len) =
104 driver->req_tracking_tbl[i].uid;
105 curr_client_pid =
106 driver->req_tracking_tbl[i].pid;
107 index = i;
108 break;
109 }
110 }
Mohit Aggarwal65b69cf2013-02-15 11:33:04 -0800111 if (index == -1) {
Shalabh Jain16794902012-09-14 10:56:49 -0700112 pr_alert("diag: No matching PID for DCI data\n");
Mohit Aggarwal65b69cf2013-02-15 11:33:04 -0800113 return;
114 }
Shalabh Jain16794902012-09-14 10:56:49 -0700115 /* Using PID of client process, find client buffer */
Mohit Aggarwal65b69cf2013-02-15 11:33:04 -0800116 i = diag_dci_find_client_index(curr_client_pid);
117 if (i != DCI_CLIENT_INDEX_INVALID) {
118 /* copy pkt rsp in client buf */
119 entry = &(driver->dci_client_tbl[i]);
120 if (DCI_CHK_CAPACITY(entry, 8+write_len)) {
121 pr_alert("diag: create capacity for pkt rsp\n");
122 entry->total_capacity += 8+write_len;
123 temp_buf = krealloc(entry->dci_data,
124 entry->total_capacity, GFP_KERNEL);
125 if (!temp_buf) {
126 pr_err("diag: DCI realloc failed\n");
127 return;
128 } else {
129 entry->dci_data = temp_buf;
Ravi Aravamudhandf2947a2012-11-16 18:10:05 -0800130 }
Shalabh Jain16794902012-09-14 10:56:49 -0700131 }
Mohit Aggarwal65b69cf2013-02-15 11:33:04 -0800132 *(int *)(entry->dci_data+entry->data_len) =
133 DCI_PKT_RSP_TYPE;
134 entry->data_len += 4;
135 *(int *)(entry->dci_data+entry->data_len)
136 = write_len;
137 entry->data_len += 4;
138 memcpy(entry->dci_data+entry->data_len,
139 buf+4+cmd_code_len, write_len);
140 entry->data_len += write_len;
141 /* delete immediate response entry */
142 if (driver->smd_dci[MODEM_DATA].
143 buf_in_1[8+cmd_code_len] != 0x80)
144 driver->req_tracking_tbl[index].pid = 0;
Shalabh Jain16794902012-09-14 10:56:49 -0700145 }
146}
147
148void extract_dci_events(unsigned char *buf)
149{
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800150 uint16_t event_id, event_id_packet, length, temp_len;
151 uint8_t *event_mask_ptr, byte_mask, payload_len, payload_len_field;
152 uint8_t timestamp[8], bit_index, timestamp_len;
153 uint8_t event_data[MAX_EVENT_SIZE];
154 unsigned int byte_index, total_event_len, i;
Shalabh Jain16794902012-09-14 10:56:49 -0700155 struct diag_dci_client_tbl *entry;
156
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800157 length = *(uint16_t *)(buf + 1); /* total length of event series */
158 if (length == 0) {
159 pr_err("diag: Incoming dci event length is invalid\n");
160 return;
161 }
Shalabh Jain16794902012-09-14 10:56:49 -0700162 temp_len = 0;
163 buf = buf + 3; /* start of event series */
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800164 while (temp_len < (length - 1)) {
165 event_id_packet = *(uint16_t *)(buf + temp_len);
Shalabh Jain16794902012-09-14 10:56:49 -0700166 event_id = event_id_packet & 0x0FFF; /* extract 12 bits */
167 if (event_id_packet & 0x8000) {
168 timestamp_len = 2;
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800169 memset(timestamp, 0, 8);
Shalabh Jain16794902012-09-14 10:56:49 -0700170 } else {
171 timestamp_len = 8;
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800172 memcpy(timestamp, buf + temp_len + 2, timestamp_len);
Shalabh Jain16794902012-09-14 10:56:49 -0700173 }
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800174 /* 13th and 14th bit represent the payload length */
Shalabh Jain16794902012-09-14 10:56:49 -0700175 if (((event_id_packet & 0x6000) >> 13) == 3) {
176 payload_len_field = 1;
177 payload_len = *(uint8_t *)
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800178 (buf + temp_len + 2 + timestamp_len);
179 if (payload_len < (MAX_EVENT_SIZE - 13)) {
180 /* copy the payload length and the payload */
181 memcpy(event_data + 12, buf + temp_len + 2 +
182 timestamp_len, 1);
183 memcpy(event_data + 13, buf + temp_len + 2 +
184 timestamp_len + 1, payload_len);
185 } else {
186 pr_err("diag: event > %d, payload_len = %d\n",
187 (MAX_EVENT_SIZE - 13), payload_len);
188 return;
189 }
Shalabh Jain16794902012-09-14 10:56:49 -0700190 } else {
191 payload_len_field = 0;
192 payload_len = (event_id_packet & 0x6000) >> 13;
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800193 /* copy the payload */
194 memcpy(event_data + 12, buf + temp_len + 2 +
195 timestamp_len, payload_len);
Shalabh Jain16794902012-09-14 10:56:49 -0700196 }
197 /* 2 bytes for the event id & timestamp len is hard coded to 8,
198 as individual events have full timestamp */
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800199 *(uint16_t *)(event_data) = 10 +
200 payload_len_field + payload_len;
201 *(uint16_t *)(event_data + 2) = event_id_packet & 0x7FFF;
202 memcpy(event_data + 4, timestamp, 8);
203 /* 2 bytes for the event length field which is added to
204 the event data */
205 total_event_len = 2 + 10 + payload_len_field + payload_len;
206 byte_index = event_id / 8;
Shalabh Jain16794902012-09-14 10:56:49 -0700207 bit_index = event_id % 8;
208 byte_mask = 0x1 << bit_index;
209 /* parse through event mask tbl of each client and check mask */
210 for (i = 0; i < MAX_DCI_CLIENTS; i++) {
211 if (driver->dci_client_tbl[i].client) {
212 entry = &(driver->dci_client_tbl[i]);
213 event_mask_ptr = entry->dci_event_mask +
214 byte_index;
Mohit Aggarwal3aa45862013-03-05 18:37:58 +0530215 mutex_lock(&dci_health_mutex);
Shalabh Jain16794902012-09-14 10:56:49 -0700216 if (*event_mask_ptr & byte_mask) {
217 /* copy to client buffer */
218 if (DCI_CHK_CAPACITY(entry,
219 4 + total_event_len)) {
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800220 pr_err("diag: DCI event drop\n");
Shalabh Jain16794902012-09-14 10:56:49 -0700221 driver->dci_client_tbl[i].
222 dropped_events++;
Mohit Aggarwal3aa45862013-03-05 18:37:58 +0530223 mutex_unlock(
224 &dci_health_mutex);
225 break;
Shalabh Jain16794902012-09-14 10:56:49 -0700226 }
Shalabh Jaina1c69a42012-10-23 12:51:30 -0700227 driver->dci_client_tbl[i].
228 received_events++;
Shalabh Jain16794902012-09-14 10:56:49 -0700229 *(int *)(entry->dci_data+
230 entry->data_len) = DCI_EVENT_TYPE;
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800231 /* 4 bytes for DCI_EVENT_TYPE */
232 memcpy(entry->dci_data +
233 entry->data_len + 4, event_data
234 , total_event_len);
Shalabh Jain16794902012-09-14 10:56:49 -0700235 entry->data_len += 4 + total_event_len;
236 }
Mohit Aggarwal3aa45862013-03-05 18:37:58 +0530237 mutex_unlock(&dci_health_mutex);
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700238 }
239 }
Shalabh Jain16794902012-09-14 10:56:49 -0700240 temp_len += 2 + timestamp_len + payload_len_field + payload_len;
241 }
242}
243
244void extract_dci_log(unsigned char *buf)
245{
246 uint16_t log_code, item_num;
247 uint8_t equip_id, *log_mask_ptr, byte_mask;
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800248 unsigned int i, byte_index, byte_offset = 0;
Shalabh Jain16794902012-09-14 10:56:49 -0700249 struct diag_dci_client_tbl *entry;
250
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800251 log_code = *(uint16_t *)(buf + 6);
Shalabh Jain16794902012-09-14 10:56:49 -0700252 equip_id = LOG_GET_EQUIP_ID(log_code);
253 item_num = LOG_GET_ITEM_NUM(log_code);
254 byte_index = item_num/8 + 2;
255 byte_mask = 0x01 << (item_num % 8);
256
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800257 byte_offset = (equip_id * 514) + byte_index;
258 if (byte_offset >= DCI_LOG_MASK_SIZE) {
259 pr_err("diag: Invalid byte_offset %d in dci log\n",
260 byte_offset);
261 return;
262 }
263
Shalabh Jain16794902012-09-14 10:56:49 -0700264 /* parse through log mask table of each client and check mask */
265 for (i = 0; i < MAX_DCI_CLIENTS; i++) {
266 if (driver->dci_client_tbl[i].client) {
267 entry = &(driver->dci_client_tbl[i]);
268 log_mask_ptr = entry->dci_log_mask;
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800269 if (!log_mask_ptr)
270 return;
271 log_mask_ptr = log_mask_ptr + byte_offset;
Mohit Aggarwal3aa45862013-03-05 18:37:58 +0530272 mutex_lock(&dci_health_mutex);
Shalabh Jain16794902012-09-14 10:56:49 -0700273 if (*log_mask_ptr & byte_mask) {
274 pr_debug("\t log code %x needed by client %d",
275 log_code, entry->client->tgid);
276 /* copy to client buffer */
277 if (DCI_CHK_CAPACITY(entry,
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800278 4 + *(uint16_t *)(buf + 2))) {
279 pr_err("diag: DCI log drop\n");
Shalabh Jain16794902012-09-14 10:56:49 -0700280 driver->dci_client_tbl[i].
281 dropped_logs++;
Mohit Aggarwal3aa45862013-03-05 18:37:58 +0530282 mutex_unlock(
283 &dci_health_mutex);
Shalabh Jain16794902012-09-14 10:56:49 -0700284 return;
285 }
Shalabh Jaina1c69a42012-10-23 12:51:30 -0700286 driver->dci_client_tbl[i].received_logs++;
Shalabh Jain16794902012-09-14 10:56:49 -0700287 *(int *)(entry->dci_data+entry->data_len) =
288 DCI_LOG_TYPE;
Ravi Aravamudhanc8a192a2013-02-22 10:23:15 -0800289 memcpy(entry->dci_data + entry->data_len + 4,
290 buf + 4, *(uint16_t *)(buf + 2));
291 entry->data_len += 4 + *(uint16_t *)(buf + 2);
Shalabh Jain16794902012-09-14 10:56:49 -0700292 }
Mohit Aggarwal3aa45862013-03-05 18:37:58 +0530293 mutex_unlock(&dci_health_mutex);
Shalabh Jain16794902012-09-14 10:56:49 -0700294 }
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700295 }
296}
297
Dixon Peterson5a26a302012-11-15 17:26:17 -0800298void diag_update_smd_dci_work_fn(struct work_struct *work)
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700299{
Dixon Peterson66fb11b2012-12-04 20:30:54 -0800300 struct diag_smd_info *smd_info = container_of(work,
301 struct diag_smd_info,
302 diag_notify_update_smd_work);
Dixon Peterson5a26a302012-11-15 17:26:17 -0800303 int i, j;
304 char dirty_bits[16];
305 uint8_t *client_log_mask_ptr;
306 uint8_t *log_mask_ptr;
307 int ret;
Dixon Petersoneecbadb2012-12-10 21:59:28 -0800308 int index = smd_info->peripheral;
Dixon Peterson5a26a302012-11-15 17:26:17 -0800309
310 /* Update the peripheral(s) with the dci log and event masks */
311
312 /* If the cntl channel is not up, we can't update logs and events */
Dixon Peterson66fb11b2012-12-04 20:30:54 -0800313 if (!driver->smd_cntl[index].ch)
Dixon Peterson5a26a302012-11-15 17:26:17 -0800314 return;
315
316 memset(dirty_bits, 0, 16 * sizeof(uint8_t));
317
318 /*
319 * From each log entry used by each client, determine
320 * which log entries in the cumulative logs that need
321 * to be updated on the peripheral.
322 */
323 for (i = 0; i < MAX_DCI_CLIENTS; i++) {
324 if (driver->dci_client_tbl[i].client) {
325 client_log_mask_ptr =
326 driver->dci_client_tbl[i].dci_log_mask;
327 for (j = 0; j < 16; j++) {
328 if (*(client_log_mask_ptr+1))
329 dirty_bits[j] = 1;
330 client_log_mask_ptr += 514;
331 }
332 }
333 }
334
335 mutex_lock(&dci_log_mask_mutex);
336 /* Update the appropriate dirty bits in the cumulative mask */
337 log_mask_ptr = dci_cumulative_log_mask;
338 for (i = 0; i < 16; i++) {
339 if (dirty_bits[i])
340 *(log_mask_ptr+1) = dirty_bits[i];
341
342 log_mask_ptr += 514;
343 }
344 mutex_unlock(&dci_log_mask_mutex);
345
Dixon Peterson66fb11b2012-12-04 20:30:54 -0800346 ret = diag_send_dci_log_mask(driver->smd_cntl[index].ch);
Dixon Peterson5a26a302012-11-15 17:26:17 -0800347
Dixon Peterson66fb11b2012-12-04 20:30:54 -0800348 ret = diag_send_dci_event_mask(driver->smd_cntl[index].ch);
349
350 smd_info->notify_context = 0;
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700351}
352
Dixon Peterson5a26a302012-11-15 17:26:17 -0800353void diag_dci_notify_client(int peripheral_mask, int data)
Shalabh Jain5e9a92b2012-06-07 21:53:49 -0700354{
355 int i, stat;
Dixon Peterson5a26a302012-11-15 17:26:17 -0800356 struct siginfo info;
357 memset(&info, 0, sizeof(struct siginfo));
358 info.si_code = SI_QUEUE;
359 info.si_int = (peripheral_mask | data);
Shalabh Jain5e9a92b2012-06-07 21:53:49 -0700360
361 /* Notify the DCI process that the peripheral DCI Channel is up */
Shalabh Jain16794902012-09-14 10:56:49 -0700362 for (i = 0; i < MAX_DCI_CLIENTS; i++) {
363 if (driver->dci_client_tbl[i].list & peripheral_mask) {
Dixon Peterson5a26a302012-11-15 17:26:17 -0800364 info.si_signo = driver->dci_client_tbl[i].signal_type;
365 stat = send_sig_info(
366 driver->dci_client_tbl[i].signal_type,
367 &info, driver->dci_client_tbl[i].client);
Shalabh Jain5e9a92b2012-06-07 21:53:49 -0700368 if (stat)
Dixon Peterson5a26a302012-11-15 17:26:17 -0800369 pr_err("diag: Err sending dci signal to client, signal data: 0x%x, stat: %d\n",
370 info.si_int, stat);
Shalabh Jain5e9a92b2012-06-07 21:53:49 -0700371 }
372 } /* end of loop for all DCI clients */
373}
374
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700375static int diag_dci_probe(struct platform_device *pdev)
376{
377 int err = 0;
Dixon Peterson66fb11b2012-12-04 20:30:54 -0800378 int index;
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700379
380 if (pdev->id == SMD_APPS_MODEM) {
Dixon Petersoneecbadb2012-12-10 21:59:28 -0800381 index = MODEM_DATA;
Dixon Peterson66fb11b2012-12-04 20:30:54 -0800382 err = smd_open("DIAG_2", &driver->smd_dci[index].ch,
383 &driver->smd_dci[index],
384 diag_smd_notify);
385 driver->smd_dci[index].ch_save =
386 driver->smd_dci[index].ch;
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700387 if (err)
Dixon Peterson66fb11b2012-12-04 20:30:54 -0800388 pr_err("diag: In %s, cannot open DCI port, Id = %d, err: %d\n",
389 __func__, pdev->id, err);
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700390 }
Dixon Peterson66fb11b2012-12-04 20:30:54 -0800391
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700392 return err;
393}
394
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700395int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
396 int len, int index)
397{
398 int i;
Dixon Petersoneecbadb2012-12-10 21:59:28 -0800399 int status = 0;
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700400
401 /* remove UID from user space pkt before sending to peripheral */
402 buf = buf + 4;
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800403 if (len > APPS_BUF_SIZE - 10) {
404 pr_err("diag: dci: buffer overwrite possible since payload bigger than buf size\n");
Ravi Aravamudhan5d2a4a82013-02-11 18:56:36 -0800405 return -EIO;
406 }
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700407 len = len - 4;
408 mutex_lock(&driver->dci_mutex);
409 /* prepare DCI packet */
410 driver->apps_dci_buf[0] = CONTROL_CHAR; /* start */
411 driver->apps_dci_buf[1] = 1; /* version */
412 *(uint16_t *)(driver->apps_dci_buf + 2) = len + 4 + 1; /* length */
Shalabh Jain16794902012-09-14 10:56:49 -0700413 driver->apps_dci_buf[4] = DCI_PKT_RSP_CODE;
414 *(int *)(driver->apps_dci_buf + 5) =
415 driver->req_tracking_tbl[index].tag;
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700416 for (i = 0; i < len; i++)
417 driver->apps_dci_buf[i+9] = *(buf+i);
418 driver->apps_dci_buf[9+len] = CONTROL_CHAR; /* end */
419
Dixon Petersoneecbadb2012-12-10 21:59:28 -0800420 for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++) {
421 if (entry.client_id == driver->smd_dci[i].peripheral) {
422 if (driver->smd_dci[i].ch) {
423 smd_write(driver->smd_dci[i].ch,
Dixon Peterson66fb11b2012-12-04 20:30:54 -0800424 driver->apps_dci_buf, len + 10);
Dixon Petersoneecbadb2012-12-10 21:59:28 -0800425 status = DIAG_DCI_NO_ERROR;
426 }
427 break;
428 }
429 }
430
431 if (status != DIAG_DCI_NO_ERROR) {
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700432 pr_alert("diag: check DCI channel\n");
Dixon Petersoneecbadb2012-12-10 21:59:28 -0800433 status = DIAG_DCI_SEND_DATA_FAIL;
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700434 }
435 mutex_unlock(&driver->dci_mutex);
Dixon Petersoneecbadb2012-12-10 21:59:28 -0800436 return status;
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700437}
438
439int diag_register_dci_transaction(int uid)
440{
441 int i, new_dci_client = 1, ret = -1;
442
443 for (i = 0; i < dci_max_reg; i++) {
Shalabh Jain16794902012-09-14 10:56:49 -0700444 if (driver->req_tracking_tbl[i].pid == current->tgid) {
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700445 new_dci_client = 0;
446 break;
447 }
448 }
449 mutex_lock(&driver->dci_mutex);
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700450 /* Make an entry in kernel DCI table */
451 driver->dci_tag++;
452 for (i = 0; i < dci_max_reg; i++) {
Shalabh Jain16794902012-09-14 10:56:49 -0700453 if (driver->req_tracking_tbl[i].pid == 0) {
454 driver->req_tracking_tbl[i].pid = current->tgid;
455 driver->req_tracking_tbl[i].uid = uid;
456 driver->req_tracking_tbl[i].tag = driver->dci_tag;
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700457 ret = i;
458 break;
459 }
460 }
461 mutex_unlock(&driver->dci_mutex);
462 return ret;
463}
464
Shalabh Jain16794902012-09-14 10:56:49 -0700465int diag_process_dci_transaction(unsigned char *buf, int len)
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700466{
467 unsigned char *temp = buf;
Shalabh Jain16794902012-09-14 10:56:49 -0700468 uint16_t subsys_cmd_code, log_code, item_num;
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800469 int subsys_id, cmd_code, ret = -1, index = -1, found = 0, read_len = 0;
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700470 struct diag_master_table entry;
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800471 int count, set_mask, num_codes, bit_index, event_id, offset = 0, i;
472 unsigned int byte_index;
Shalabh Jain16794902012-09-14 10:56:49 -0700473 uint8_t equip_id, *log_mask_ptr, *head_log_mask_ptr, byte_mask;
474 uint8_t *event_mask_ptr;
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700475
Dixon Petersoneecbadb2012-12-10 21:59:28 -0800476 if (!driver->smd_dci[MODEM_DATA].ch) {
Dixon Peterson66fb11b2012-12-04 20:30:54 -0800477 pr_err("diag: DCI smd channel for peripheral %d not valid for dci updates\n",
Dixon Petersoneecbadb2012-12-10 21:59:28 -0800478 driver->smd_dci[MODEM_DATA].peripheral);
Dixon Peterson5a26a302012-11-15 17:26:17 -0800479 return DIAG_DCI_SEND_DATA_FAIL;
480 }
481
Shalabh Jain16794902012-09-14 10:56:49 -0700482 /* This is Pkt request/response transaction */
483 if (*(int *)temp > 0) {
484 /* enter this UID into kernel table and return index */
485 index = diag_register_dci_transaction(*(int *)temp);
486 if (index < 0) {
487 pr_alert("diag: registering new DCI transaction failed\n");
488 return DIAG_DCI_NO_REG;
489 }
490 temp += 4;
491 /*
492 * Check for registered peripheral and fwd pkt to
493 * appropriate proc
494 */
495 cmd_code = (int)(*(char *)temp);
496 temp++;
497 subsys_id = (int)(*(char *)temp);
498 temp++;
499 subsys_cmd_code = *(uint16_t *)temp;
500 temp += 2;
501 pr_debug("diag: %d %d %d", cmd_code, subsys_id,
502 subsys_cmd_code);
503 for (i = 0; i < diag_max_reg; i++) {
504 entry = driver->table[i];
505 if (entry.process_id != NO_PROCESS) {
506 if (entry.cmd_code == cmd_code &&
507 entry.subsys_id == subsys_id &&
508 entry.cmd_code_lo <= subsys_cmd_code &&
509 entry.cmd_code_hi >= subsys_cmd_code) {
510 ret = diag_send_dci_pkt(entry, buf,
511 len, index);
512 } else if (entry.cmd_code == 255
513 && cmd_code == 75) {
514 if (entry.subsys_id == subsys_id &&
515 entry.cmd_code_lo <=
516 subsys_cmd_code &&
517 entry.cmd_code_hi >=
518 subsys_cmd_code) {
519 ret = diag_send_dci_pkt(entry,
520 buf, len, index);
521 }
522 } else if (entry.cmd_code == 255 &&
523 entry.subsys_id == 255) {
524 if (entry.cmd_code_lo <= cmd_code &&
525 entry.cmd_code_hi >=
526 cmd_code) {
527 ret = diag_send_dci_pkt(entry,
528 buf, len, index);
529 }
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700530 }
531 }
532 }
Shalabh Jain16794902012-09-14 10:56:49 -0700533 } else if (*(int *)temp == DCI_LOG_TYPE) {
534 /* find client id and table */
Mohit Aggarwal65b69cf2013-02-15 11:33:04 -0800535 i = diag_dci_find_client_index(current->tgid);
536 if (i == DCI_CLIENT_INDEX_INVALID) {
Shalabh Jain16794902012-09-14 10:56:49 -0700537 pr_err("diag: dci client not registered/found\n");
538 return ret;
539 }
540 /* Extract each log code and put in client table */
541 temp += 4;
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800542 read_len += 4;
Shalabh Jain16794902012-09-14 10:56:49 -0700543 set_mask = *(int *)temp;
544 temp += 4;
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800545 read_len += 4;
Shalabh Jain16794902012-09-14 10:56:49 -0700546 num_codes = *(int *)temp;
547 temp += 4;
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800548 read_len += 4;
Shalabh Jain16794902012-09-14 10:56:49 -0700549
550 head_log_mask_ptr = driver->dci_client_tbl[i].dci_log_mask;
Shalabh Jaina1c69a42012-10-23 12:51:30 -0700551 pr_debug("diag: head of dci log mask %p\n", head_log_mask_ptr);
Shalabh Jain16794902012-09-14 10:56:49 -0700552 count = 0; /* iterator for extracting log codes */
553 while (count < num_codes) {
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800554 if (read_len >= USER_SPACE_DATA) {
555 pr_err("diag: dci: Log type, possible buffer overflow\n");
556 return -EIO;
557 }
Shalabh Jain16794902012-09-14 10:56:49 -0700558 log_code = *(uint16_t *)temp;
559 equip_id = LOG_GET_EQUIP_ID(log_code);
560 item_num = LOG_GET_ITEM_NUM(log_code);
561 byte_index = item_num/8 + 2;
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800562 if (byte_index >= (DCI_MAX_ITEMS_PER_LOG_CODE+2)) {
563 pr_err("diag: dci: Log type, invalid byte index\n");
564 return ret;
565 }
Shalabh Jain16794902012-09-14 10:56:49 -0700566 byte_mask = 0x01 << (item_num % 8);
567 /*
568 * Parse through log mask table and find
569 * relevant range
570 */
571 log_mask_ptr = head_log_mask_ptr;
572 found = 0;
Ravi Aravamudhandf2947a2012-11-16 18:10:05 -0800573 offset = 0;
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800574 while (log_mask_ptr && (offset < DCI_LOG_MASK_SIZE)) {
Shalabh Jain16794902012-09-14 10:56:49 -0700575 if (*log_mask_ptr == equip_id) {
576 found = 1;
Shalabh Jaina1c69a42012-10-23 12:51:30 -0700577 pr_debug("diag: find equip id = %x at %p\n",
Shalabh Jain16794902012-09-14 10:56:49 -0700578 equip_id, log_mask_ptr);
579 break;
580 } else {
Shalabh Jaina1c69a42012-10-23 12:51:30 -0700581 pr_debug("diag: did not find equip id = %x at %p\n",
Shalabh Jain16794902012-09-14 10:56:49 -0700582 equip_id, log_mask_ptr);
583 log_mask_ptr += 514;
Ravi Aravamudhandf2947a2012-11-16 18:10:05 -0800584 offset += 514;
Shalabh Jain16794902012-09-14 10:56:49 -0700585 }
586 }
587 if (!found) {
588 pr_err("diag: dci equip id not found\n");
589 return ret;
590 }
591 *(log_mask_ptr+1) = 1; /* set the dirty byte */
592 log_mask_ptr = log_mask_ptr + byte_index;
593 if (set_mask)
594 *log_mask_ptr |= byte_mask;
595 else
596 *log_mask_ptr &= ~byte_mask;
Ravi Aravamudhandf2947a2012-11-16 18:10:05 -0800597 /* add to cumulative mask */
598 update_dci_cumulative_log_mask(
599 offset, byte_index,
600 byte_mask);
Shalabh Jain16794902012-09-14 10:56:49 -0700601 temp += 2;
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800602 read_len += 2;
Shalabh Jain16794902012-09-14 10:56:49 -0700603 count++;
604 ret = DIAG_DCI_NO_ERROR;
605 }
Shalabh Jain16794902012-09-14 10:56:49 -0700606 /* send updated mask to peripherals */
Dixon Petersoneecbadb2012-12-10 21:59:28 -0800607 ret = diag_send_dci_log_mask(driver->smd_cntl[MODEM_DATA].ch);
Shalabh Jain16794902012-09-14 10:56:49 -0700608 } else if (*(int *)temp == DCI_EVENT_TYPE) {
609 /* find client id and table */
Mohit Aggarwal65b69cf2013-02-15 11:33:04 -0800610 i = diag_dci_find_client_index(current->tgid);
611 if (i == DCI_CLIENT_INDEX_INVALID) {
Shalabh Jain16794902012-09-14 10:56:49 -0700612 pr_err("diag: dci client not registered/found\n");
613 return ret;
614 }
615 /* Extract each log code and put in client table */
616 temp += 4;
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800617 read_len += 4;
Shalabh Jain16794902012-09-14 10:56:49 -0700618 set_mask = *(int *)temp;
619 temp += 4;
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800620 read_len += 4;
Shalabh Jain16794902012-09-14 10:56:49 -0700621 num_codes = *(int *)temp;
622 temp += 4;
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800623 read_len += 4;
Shalabh Jain16794902012-09-14 10:56:49 -0700624
625 event_mask_ptr = driver->dci_client_tbl[i].dci_event_mask;
626 pr_debug("diag: head of dci event mask %p\n", event_mask_ptr);
627 count = 0; /* iterator for extracting log codes */
628 while (count < num_codes) {
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800629 if (read_len >= USER_SPACE_DATA) {
630 pr_err("diag: dci: Event type, possible buffer overflow\n");
631 return -EIO;
632 }
Shalabh Jain16794902012-09-14 10:56:49 -0700633 event_id = *(int *)temp;
634 byte_index = event_id/8;
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800635 if (byte_index >= DCI_EVENT_MASK_SIZE) {
636 pr_err("diag: dci: Event type, invalid byte index\n");
637 return ret;
638 }
Shalabh Jain16794902012-09-14 10:56:49 -0700639 bit_index = event_id % 8;
640 byte_mask = 0x1 << bit_index;
641 /*
642 * Parse through event mask table and set
643 * relevant byte & bit combination
644 */
645 if (set_mask)
646 *(event_mask_ptr + byte_index) |= byte_mask;
647 else
648 *(event_mask_ptr + byte_index) &= ~byte_mask;
Ravi Aravamudhandf2947a2012-11-16 18:10:05 -0800649 /* add to cumulative mask */
650 update_dci_cumulative_event_mask(byte_index, byte_mask);
Shalabh Jain16794902012-09-14 10:56:49 -0700651 temp += sizeof(int);
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800652 read_len += sizeof(int);
Shalabh Jain16794902012-09-14 10:56:49 -0700653 count++;
654 ret = DIAG_DCI_NO_ERROR;
655 }
Shalabh Jain16794902012-09-14 10:56:49 -0700656 /* send updated mask to peripherals */
Dixon Petersoneecbadb2012-12-10 21:59:28 -0800657 ret = diag_send_dci_event_mask(driver->smd_cntl[MODEM_DATA].ch);
Shalabh Jain16794902012-09-14 10:56:49 -0700658 } else {
659 pr_alert("diag: Incorrect DCI transaction\n");
Shalabh Jain1c99e4c2012-03-26 18:47:59 -0700660 }
661 return ret;
662}
663
Mohit Aggarwal65b69cf2013-02-15 11:33:04 -0800664int diag_dci_find_client_index(int client_id)
665{
666 int i, ret = DCI_CLIENT_INDEX_INVALID;
667
668 for (i = 0; i < MAX_DCI_CLIENTS; i++) {
669 if (driver->dci_client_tbl[i].client != NULL) {
670 if (driver->dci_client_tbl[i].client->tgid ==
671 client_id) {
672 ret = i;
673 break;
674 }
675 }
676 }
677 return ret;
678}
679
Ravi Aravamudhandf2947a2012-11-16 18:10:05 -0800680void update_dci_cumulative_event_mask(int offset, uint8_t byte_mask)
Shalabh Jain16794902012-09-14 10:56:49 -0700681{
682 int i;
Shalabh Jain16794902012-09-14 10:56:49 -0700683 uint8_t *event_mask_ptr;
Ravi Aravamudhandf2947a2012-11-16 18:10:05 -0800684 uint8_t *update_ptr = dci_cumulative_event_mask;
685 bool is_set = false;
Shalabh Jain16794902012-09-14 10:56:49 -0700686
Shalabh Jaina1c69a42012-10-23 12:51:30 -0700687 mutex_lock(&dci_event_mask_mutex);
Ravi Aravamudhandf2947a2012-11-16 18:10:05 -0800688 update_ptr += offset;
689 for (i = 0; i < MAX_DCI_CLIENTS; i++) {
690 event_mask_ptr =
691 driver->dci_client_tbl[i].dci_event_mask;
692 event_mask_ptr += offset;
693 if ((*event_mask_ptr & byte_mask) == byte_mask) {
694 is_set = true;
695 /* break even if one client has the event mask set */
696 break;
697 }
698 }
699 if (is_set == false)
700 *update_ptr &= ~byte_mask;
701 else
702 *update_ptr |= byte_mask;
Shalabh Jaina1c69a42012-10-23 12:51:30 -0700703 mutex_unlock(&dci_event_mask_mutex);
Shalabh Jain16794902012-09-14 10:56:49 -0700704}
705
Mohit Aggarwal65b69cf2013-02-15 11:33:04 -0800706void clear_client_dci_cumulative_event_mask(int client_index)
707{
708 int i, j;
709 uint8_t *update_ptr = dci_cumulative_event_mask;
710 uint8_t *event_mask_ptr, *client_event_mask_ptr, byte_mask = 0;
711 bool is_set = false;
712
713 event_mask_ptr =
714 (driver->dci_client_tbl[client_index].dci_event_mask);
715
716 mutex_lock(&dci_event_mask_mutex);
717 for (i = 0; i < DCI_EVENT_MASK_SIZE; i++) {
718 is_set = false;
719 /* Already cleared event masks need not to be considered */
720 if (*event_mask_ptr != 0) {
721 byte_mask = *event_mask_ptr;
722 } else {
723 update_ptr++;
724 event_mask_ptr++;
725 continue;
726 }
727 for (j = 0; j < MAX_DCI_CLIENTS; j++) {
728 /* continue searching for valid client */
729 if (driver->dci_client_tbl[j].client == NULL ||
730 client_index == j)
731 continue;
732 client_event_mask_ptr =
733 (driver->dci_client_tbl[j].dci_event_mask);
734 client_event_mask_ptr += i;
735 if (*client_event_mask_ptr & byte_mask) {
736 /*
737 * Break if another client has same
738 * event mask set
739 */
740 if ((*client_event_mask_ptr &
741 byte_mask) == byte_mask) {
742 is_set = true;
743 break;
744 } else {
745 byte_mask =
746 (~(*client_event_mask_ptr) &
747 byte_mask);
748 is_set = false;
749 }
750 }
751 }
752 /*
753 * Clear only if this client has event mask set else
754 * don't update cumulative event mask ptr
755 */
756 if (is_set == false)
757 *update_ptr &= ~byte_mask;
758
759 update_ptr++;
760 event_mask_ptr++;
761 }
762 event_mask_ptr =
763 (driver->dci_client_tbl[client_index].dci_event_mask);
764 memset(event_mask_ptr, 0, DCI_EVENT_MASK_SIZE);
765 mutex_unlock(&dci_event_mask_mutex);
766}
767
768
Dixon Peterson5a26a302012-11-15 17:26:17 -0800769int diag_send_dci_event_mask(smd_channel_t *ch)
Shalabh Jain16794902012-09-14 10:56:49 -0700770{
771 void *buf = driver->buf_event_mask_update;
772 int header_size = sizeof(struct diag_ctrl_event_mask);
773 int wr_size = -ENOMEM, retry_count = 0, timer;
Dixon Peterson5a26a302012-11-15 17:26:17 -0800774 int ret = DIAG_DCI_NO_ERROR;
Shalabh Jain16794902012-09-14 10:56:49 -0700775
776 mutex_lock(&driver->diag_cntl_mutex);
777 /* send event mask update */
778 driver->event_mask->cmd_type = DIAG_CTRL_MSG_EVENT_MASK;
779 driver->event_mask->data_len = 7 + DCI_EVENT_MASK_SIZE;
780 driver->event_mask->stream_id = DCI_MASK_STREAM;
781 driver->event_mask->status = 3; /* status for valid mask */
782 driver->event_mask->event_config = 1; /* event config */
783 driver->event_mask->event_mask_size = DCI_EVENT_MASK_SIZE;
784 memcpy(buf, driver->event_mask, header_size);
785 memcpy(buf+header_size, dci_cumulative_event_mask, DCI_EVENT_MASK_SIZE);
786 if (ch) {
787 while (retry_count < 3) {
788 wr_size = smd_write(ch, buf,
789 header_size + DCI_EVENT_MASK_SIZE);
790 if (wr_size == -ENOMEM) {
791 retry_count++;
792 for (timer = 0; timer < 5; timer++)
793 udelay(2000);
794 } else {
795 break;
796 }
797 }
Dixon Peterson5a26a302012-11-15 17:26:17 -0800798 if (wr_size != header_size + DCI_EVENT_MASK_SIZE) {
Shalabh Jain16794902012-09-14 10:56:49 -0700799 pr_err("diag: error writing dci event mask %d, tried %d\n",
800 wr_size, header_size + DCI_EVENT_MASK_SIZE);
Dixon Peterson5a26a302012-11-15 17:26:17 -0800801 ret = DIAG_DCI_SEND_DATA_FAIL;
802 }
803 } else {
Shalabh Jain16794902012-09-14 10:56:49 -0700804 pr_err("diag: ch not valid for dci event mask update\n");
Dixon Peterson5a26a302012-11-15 17:26:17 -0800805 ret = DIAG_DCI_SEND_DATA_FAIL;
806 }
Shalabh Jain16794902012-09-14 10:56:49 -0700807 mutex_unlock(&driver->diag_cntl_mutex);
Dixon Peterson5a26a302012-11-15 17:26:17 -0800808
809 return ret;
Shalabh Jain16794902012-09-14 10:56:49 -0700810}
811
Ravi Aravamudhanf70200e2013-02-13 10:05:36 -0800812void update_dci_cumulative_log_mask(int offset, unsigned int byte_index,
Ravi Aravamudhandf2947a2012-11-16 18:10:05 -0800813 uint8_t byte_mask)
Shalabh Jain16794902012-09-14 10:56:49 -0700814{
Ravi Aravamudhandf2947a2012-11-16 18:10:05 -0800815 int i;
Shalabh Jain16794902012-09-14 10:56:49 -0700816 uint8_t *update_ptr = dci_cumulative_log_mask;
Ravi Aravamudhandf2947a2012-11-16 18:10:05 -0800817 uint8_t *log_mask_ptr;
818 bool is_set = false;
Shalabh Jain16794902012-09-14 10:56:49 -0700819
Shalabh Jaina1c69a42012-10-23 12:51:30 -0700820 mutex_lock(&dci_log_mask_mutex);
Ravi Aravamudhandf2947a2012-11-16 18:10:05 -0800821 *update_ptr = 0;
822 /* set the equipment IDs */
823 for (i = 0; i < 16; i++)
824 *(update_ptr + (i*514)) = i;
825
826 update_ptr += offset;
827 /* update the dirty bit */
828 *(update_ptr+1) = 1;
829 update_ptr = update_ptr + byte_index;
830 for (i = 0; i < MAX_DCI_CLIENTS; i++) {
831 log_mask_ptr =
832 (driver->dci_client_tbl[i].dci_log_mask);
833 log_mask_ptr = log_mask_ptr + offset + byte_index;
834 if ((*log_mask_ptr & byte_mask) == byte_mask) {
835 is_set = true;
836 /* break even if one client has the log mask set */
837 break;
Shalabh Jain16794902012-09-14 10:56:49 -0700838 }
Shalabh Jain16794902012-09-14 10:56:49 -0700839 }
Ravi Aravamudhandf2947a2012-11-16 18:10:05 -0800840
841 if (is_set == false)
842 *update_ptr &= ~byte_mask;
843 else
844 *update_ptr |= byte_mask;
Shalabh Jaina1c69a42012-10-23 12:51:30 -0700845 mutex_unlock(&dci_log_mask_mutex);
Shalabh Jain16794902012-09-14 10:56:49 -0700846}
847
Mohit Aggarwal65b69cf2013-02-15 11:33:04 -0800848void clear_client_dci_cumulative_log_mask(int client_index)
849{
850 int i, j, k;
851 uint8_t *update_ptr = dci_cumulative_log_mask;
852 uint8_t *log_mask_ptr, *client_log_mask_ptr, byte_mask = 0;
853 bool is_set = false;
854
855 log_mask_ptr = driver->dci_client_tbl[client_index].dci_log_mask;
856
857 mutex_lock(&dci_log_mask_mutex);
858 *update_ptr = 0;
859 /* set the equipment IDs */
860 for (i = 0; i < 16; i++)
861 *(update_ptr + (i*514)) = i;
862
863 /* update cumulative log mask ptr*/
864 update_ptr += 2;
865 log_mask_ptr += 2;
866 for (i = 0; i < 16; i++) {
867 for (j = 0; j < 512; j++) {
868 is_set = false;
869 /*
870 * Already cleared log masks need
871 * not to be considered
872 */
873 if (*log_mask_ptr != 0) {
874 byte_mask = *log_mask_ptr;
875 } else {
876 update_ptr++;
877 log_mask_ptr++;
878 continue;
879 }
880 for (k = 0; k < MAX_DCI_CLIENTS; k++) {
881 /* continue searching for valid client */
882 if (driver->dci_client_tbl[k].client == NULL ||
883 client_index == k)
884 continue;
885 client_log_mask_ptr =
886 (driver->dci_client_tbl[k].dci_log_mask);
887 client_log_mask_ptr += (i*514) + 2 + j;
888 if (*client_log_mask_ptr & byte_mask) {
889 /*
890 * Break if another client has same
891 * log mask set
892 */
893 if ((*client_log_mask_ptr &
894 byte_mask) == byte_mask) {
895 is_set = true;
896 break;
897 } else {
898 byte_mask =
899 (~(*client_log_mask_ptr) &
900 byte_mask);
901 is_set = false;
902 }
903 }
904 }
905 /*
906 * Clear only if this client has log mask set else
907 * don't update cumulative log mask ptr
908 */
909 if (is_set == false) {
910 /*
911 * Update the dirty bit for the equipment
912 * whose mask is changing
913 */
914 dci_cumulative_log_mask[1+(i*514)] = 1;
915 *update_ptr &= ~byte_mask;
916 }
917
918 update_ptr++;
919 log_mask_ptr++;
920 }
921 update_ptr += 2;
922 log_mask_ptr += 2;
923 }
924 log_mask_ptr = driver->dci_client_tbl[client_index].dci_log_mask;
925 memset(log_mask_ptr, 0, DCI_LOG_MASK_SIZE);
926 mutex_unlock(&dci_log_mask_mutex);
927}
928
Dixon Peterson5a26a302012-11-15 17:26:17 -0800929int diag_send_dci_log_mask(smd_channel_t *ch)
Shalabh Jain16794902012-09-14 10:56:49 -0700930{
931 void *buf = driver->buf_log_mask_update;
932 int header_size = sizeof(struct diag_ctrl_log_mask);
933 uint8_t *log_mask_ptr = dci_cumulative_log_mask;
934 int i, wr_size = -ENOMEM, retry_count = 0, timer;
Dixon Peterson5a26a302012-11-15 17:26:17 -0800935 int ret = DIAG_DCI_NO_ERROR;
936
937 if (!ch) {
938 pr_err("diag: ch not valid for dci log mask update\n");
939 return DIAG_DCI_SEND_DATA_FAIL;
940 }
Shalabh Jain16794902012-09-14 10:56:49 -0700941
942 mutex_lock(&driver->diag_cntl_mutex);
943 for (i = 0; i < 16; i++) {
Ravi Aravamudhan3365bc02013-03-05 16:51:28 -0800944 retry_count = 0;
Shalabh Jain16794902012-09-14 10:56:49 -0700945 driver->log_mask->cmd_type = DIAG_CTRL_MSG_LOG_MASK;
946 driver->log_mask->num_items = 512;
947 driver->log_mask->data_len = 11 + 512;
948 driver->log_mask->stream_id = DCI_MASK_STREAM;
949 driver->log_mask->status = 3; /* status for valid mask */
950 driver->log_mask->equip_id = *log_mask_ptr;
951 driver->log_mask->log_mask_size = 512;
952 memcpy(buf, driver->log_mask, header_size);
953 memcpy(buf+header_size, log_mask_ptr+2, 512);
954 /* if dirty byte is set and channel is valid */
955 if (ch && *(log_mask_ptr+1)) {
956 while (retry_count < 3) {
957 wr_size = smd_write(ch, buf, header_size + 512);
958 if (wr_size == -ENOMEM) {
959 retry_count++;
960 for (timer = 0; timer < 5; timer++)
961 udelay(2000);
962 } else
963 break;
964 }
Dixon Peterson5a26a302012-11-15 17:26:17 -0800965 if (wr_size != header_size + 512) {
Ravi Aravamudhan3365bc02013-03-05 16:51:28 -0800966 pr_err("diag: dci log mask update failed %d, tried %d for equip_id %d\n",
967 wr_size, header_size + 512,
968 driver->log_mask->equip_id);
Dixon Peterson5a26a302012-11-15 17:26:17 -0800969 ret = DIAG_DCI_SEND_DATA_FAIL;
970
971 } else {
Shalabh Jain16794902012-09-14 10:56:49 -0700972 *(log_mask_ptr+1) = 0; /* clear dirty byte */
973 pr_debug("diag: updated dci log equip ID %d\n",
974 *log_mask_ptr);
975 }
976 }
977 log_mask_ptr += 514;
978 }
979 mutex_unlock(&driver->diag_cntl_mutex);
Dixon Peterson5a26a302012-11-15 17:26:17 -0800980
981 return ret;
Shalabh Jain16794902012-09-14 10:56:49 -0700982}
983
984void create_dci_log_mask_tbl(unsigned char *tbl_buf)
985{
986 uint8_t i; int count = 0;
987
988 /* create hard coded table for log mask with 16 categories */
989 for (i = 0; i < 16; i++) {
990 *(uint8_t *)tbl_buf = i;
991 pr_debug("diag: put value %x at %p\n", i, tbl_buf);
992 memset(tbl_buf+1, 0, 513); /* set dirty bit as 0 */
993 tbl_buf += 514;
994 count += 514;
995 }
996}
997
998void create_dci_event_mask_tbl(unsigned char *tbl_buf)
999{
1000 memset(tbl_buf, 0, 512);
1001}
1002
Shalabh Jain1c99e4c2012-03-26 18:47:59 -07001003static int diag_dci_runtime_suspend(struct device *dev)
1004{
1005 dev_dbg(dev, "pm_runtime: suspending...\n");
1006 return 0;
1007}
1008
1009static int diag_dci_runtime_resume(struct device *dev)
1010{
1011 dev_dbg(dev, "pm_runtime: resuming...\n");
1012 return 0;
1013}
1014
1015static const struct dev_pm_ops diag_dci_dev_pm_ops = {
1016 .runtime_suspend = diag_dci_runtime_suspend,
1017 .runtime_resume = diag_dci_runtime_resume,
1018};
1019
1020struct platform_driver msm_diag_dci_driver = {
1021 .probe = diag_dci_probe,
1022 .driver = {
Shalabh Jain16794902012-09-14 10:56:49 -07001023 .name = "DIAG_2",
1024 .owner = THIS_MODULE,
1025 .pm = &diag_dci_dev_pm_ops,
1026 },
Shalabh Jain1c99e4c2012-03-26 18:47:59 -07001027};
1028
1029int diag_dci_init(void)
1030{
1031 int success = 0;
Dixon Peterson66fb11b2012-12-04 20:30:54 -08001032 int i;
Dixon Peterson5a26a302012-11-15 17:26:17 -08001033
Shalabh Jain1c99e4c2012-03-26 18:47:59 -07001034 driver->dci_tag = 0;
1035 driver->dci_client_id = 0;
1036 driver->num_dci_client = 0;
Shalabh Jain1c99e4c2012-03-26 18:47:59 -07001037 mutex_init(&driver->dci_mutex);
Shalabh Jaina1c69a42012-10-23 12:51:30 -07001038 mutex_init(&dci_log_mask_mutex);
1039 mutex_init(&dci_event_mask_mutex);
Mohit Aggarwal3aa45862013-03-05 18:37:58 +05301040 mutex_init(&dci_health_mutex);
1041
Mohit Aggarwal65b69cf2013-02-15 11:33:04 -08001042 for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++) {
1043 success = diag_smd_constructor(&driver->smd_dci[i],
1044 i, SMD_DCI_TYPE);
1045 if (!success)
1046 goto err;
1047 }
Dixon Peterson66fb11b2012-12-04 20:30:54 -08001048
Shalabh Jain16794902012-09-14 10:56:49 -07001049 if (driver->req_tracking_tbl == NULL) {
1050 driver->req_tracking_tbl = kzalloc(dci_max_reg *
1051 sizeof(struct dci_pkt_req_tracking_tbl), GFP_KERNEL);
1052 if (driver->req_tracking_tbl == NULL)
Shalabh Jain5e9a92b2012-06-07 21:53:49 -07001053 goto err;
1054 }
Shalabh Jain1c99e4c2012-03-26 18:47:59 -07001055 if (driver->apps_dci_buf == NULL) {
1056 driver->apps_dci_buf = kzalloc(APPS_BUF_SIZE, GFP_KERNEL);
1057 if (driver->apps_dci_buf == NULL)
1058 goto err;
1059 }
Shalabh Jain16794902012-09-14 10:56:49 -07001060 if (driver->dci_client_tbl == NULL) {
1061 driver->dci_client_tbl = kzalloc(MAX_DCI_CLIENTS *
1062 sizeof(struct diag_dci_client_tbl), GFP_KERNEL);
1063 if (driver->dci_client_tbl == NULL)
1064 goto err;
1065 }
1066 driver->diag_dci_wq = create_singlethread_workqueue("diag_dci_wq");
Shalabh Jain1c99e4c2012-03-26 18:47:59 -07001067 success = platform_driver_register(&msm_diag_dci_driver);
1068 if (success) {
1069 pr_err("diag: Could not register DCI driver\n");
1070 goto err;
1071 }
1072 return DIAG_DCI_NO_ERROR;
1073err:
1074 pr_err("diag: Could not initialize diag DCI buffers");
Shalabh Jain16794902012-09-14 10:56:49 -07001075 kfree(driver->req_tracking_tbl);
1076 kfree(driver->dci_client_tbl);
Shalabh Jain1c99e4c2012-03-26 18:47:59 -07001077 kfree(driver->apps_dci_buf);
Dixon Peterson66fb11b2012-12-04 20:30:54 -08001078 for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++)
1079 diag_smd_destructor(&driver->smd_dci[i]);
Shalabh Jain16794902012-09-14 10:56:49 -07001080 if (driver->diag_dci_wq)
1081 destroy_workqueue(driver->diag_dci_wq);
Shalabh Jain1c99e4c2012-03-26 18:47:59 -07001082 return DIAG_DCI_NO_REG;
1083}
1084
1085void diag_dci_exit(void)
1086{
Dixon Peterson66fb11b2012-12-04 20:30:54 -08001087 int i;
1088
1089 for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++)
1090 diag_smd_destructor(&driver->smd_dci[i]);
1091
Shalabh Jain1c99e4c2012-03-26 18:47:59 -07001092 platform_driver_unregister(&msm_diag_dci_driver);
Dixon Peterson6dba7572013-04-12 18:45:16 -07001093
1094 if (driver->dci_client_tbl) {
1095 for (i = 0; i < MAX_DCI_CLIENTS; i++)
1096 kfree(driver->dci_client_tbl[i].dci_data);
1097 }
1098
Shalabh Jain16794902012-09-14 10:56:49 -07001099 kfree(driver->req_tracking_tbl);
1100 kfree(driver->dci_client_tbl);
Shalabh Jain1c99e4c2012-03-26 18:47:59 -07001101 kfree(driver->apps_dci_buf);
Mohit Aggarwal3aa45862013-03-05 18:37:58 +05301102 mutex_destroy(&driver->dci_mutex);
1103 mutex_destroy(&dci_log_mask_mutex);
1104 mutex_destroy(&dci_event_mask_mutex);
1105 mutex_destroy(&dci_health_mutex);
Shalabh Jain16794902012-09-14 10:56:49 -07001106 destroy_workqueue(driver->diag_dci_wq);
Shalabh Jain1c99e4c2012-03-26 18:47:59 -07001107}
Mohit Aggarwal3aa45862013-03-05 18:37:58 +05301108
1109int diag_dci_clear_log_mask()
1110{
1111 int i, j, k, err = DIAG_DCI_NO_ERROR;
1112 uint8_t *log_mask_ptr, *update_ptr;
1113
1114 i = diag_dci_find_client_index(current->tgid);
1115 if (i == DCI_CLIENT_INDEX_INVALID)
1116 return DIAG_DCI_TABLE_ERR;
1117
1118 mutex_lock(&dci_log_mask_mutex);
1119 create_dci_log_mask_tbl(
1120 driver->dci_client_tbl[i].dci_log_mask);
1121 memset(dci_cumulative_log_mask,
1122 0x0, DCI_LOG_MASK_SIZE);
1123 for (i = 0; i < MAX_DCI_CLIENTS; i++) {
1124 update_ptr = dci_cumulative_log_mask;
1125 if (driver->dci_client_tbl[i].client) {
1126 log_mask_ptr =
1127 driver->dci_client_tbl[i].dci_log_mask;
1128 for (j = 0; j < 16; j++) {
1129 *update_ptr = j;
1130 *(update_ptr + 1) = 1;
1131 update_ptr += 2;
1132 log_mask_ptr += 2;
1133 for (k = 0; k < 513; k++) {
1134 *update_ptr |= *log_mask_ptr;
1135 update_ptr++;
1136 log_mask_ptr++;
1137 }
1138 }
1139 }
1140 }
1141 mutex_unlock(&dci_log_mask_mutex);
1142 err = diag_send_dci_log_mask(driver->smd_cntl[MODEM_DATA].ch);
1143 return err;
1144}
1145
1146int diag_dci_clear_event_mask()
1147{
1148 int i, j, err = DIAG_DCI_NO_ERROR;
1149 uint8_t *event_mask_ptr, *update_ptr;
1150
1151 i = diag_dci_find_client_index(current->tgid);
1152 if (i == DCI_CLIENT_INDEX_INVALID)
1153 return DIAG_DCI_TABLE_ERR;
1154
1155 mutex_lock(&dci_event_mask_mutex);
1156 memset(driver->dci_client_tbl[i].dci_event_mask,
1157 0x0, DCI_EVENT_MASK_SIZE);
1158 memset(dci_cumulative_event_mask,
1159 0x0, DCI_EVENT_MASK_SIZE);
1160 update_ptr = dci_cumulative_event_mask;
1161 for (i = 0; i < MAX_DCI_CLIENTS; i++) {
1162 event_mask_ptr =
1163 driver->dci_client_tbl[i].dci_event_mask;
1164 for (j = 0; j < DCI_EVENT_MASK_SIZE; j++)
1165 *(update_ptr + j) |= *(event_mask_ptr + j);
1166 }
1167 mutex_unlock(&dci_event_mask_mutex);
1168 err = diag_send_dci_event_mask(driver->smd_cntl[MODEM_DATA].ch);
1169 return err;
1170}
1171
1172int diag_dci_query_log_mask(uint16_t log_code)
1173{
1174 uint16_t item_num;
1175 uint8_t equip_id, *log_mask_ptr, byte_mask;
1176 int i, byte_index, offset;
1177
1178 equip_id = LOG_GET_EQUIP_ID(log_code);
1179 item_num = LOG_GET_ITEM_NUM(log_code);
1180 byte_index = item_num/8 + 2;
1181 byte_mask = 0x01 << (item_num % 8);
1182 offset = equip_id * 514;
1183
1184 i = diag_dci_find_client_index(current->tgid);
1185 if (i != DCI_CLIENT_INDEX_INVALID) {
1186 log_mask_ptr = driver->dci_client_tbl[i].dci_log_mask;
1187 log_mask_ptr = log_mask_ptr + offset + byte_index;
1188 return ((*log_mask_ptr & byte_mask) == byte_mask) ?
1189 1 : 0;
1190 }
1191 return 0;
1192}
1193
1194
1195int diag_dci_query_event_mask(uint16_t event_id)
1196{
1197 uint8_t *event_mask_ptr, byte_mask;
1198 int i, byte_index, bit_index;
1199 byte_index = event_id/8;
1200 bit_index = event_id % 8;
1201 byte_mask = 0x1 << bit_index;
1202
1203 i = diag_dci_find_client_index(current->tgid);
1204 if (i != DCI_CLIENT_INDEX_INVALID) {
1205 event_mask_ptr =
1206 driver->dci_client_tbl[i].dci_event_mask;
1207 event_mask_ptr = event_mask_ptr + byte_index;
1208 if ((*event_mask_ptr & byte_mask) == byte_mask)
1209 return 1;
1210 else
1211 return 0;
1212 }
1213 return 0;
1214}
1215
1216