blob: 5cbf888caa45ddd1304f0116fb7bdb75c64497f1 [file] [log] [blame]
Shalabh Jain1c99e4c2012-03-26 18:47:59 -07001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
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>
19#include <linux/workqueue.h>
20#include <linux/pm_runtime.h>
21#include <linux/platform_device.h>
22#include <asm/current.h>
23#ifdef CONFIG_DIAG_OVER_USB
24#include <mach/usbdiag.h>
25#endif
26#include "diagchar_hdlc.h"
27#include "diagmem.h"
28#include "diagchar.h"
29#include "diagfwd.h"
30#include "diag_dci.h"
31
32unsigned int dci_max_reg = 100;
33unsigned int dci_max_clients = 10;
34
35static void diag_smd_dci_send_req(int proc_num)
36{
37 void *buf = NULL;
38 smd_channel_t *smd_ch = NULL;
39 int i, r, found = 1;
40 int cmd_code_len = 1;
41
42 if (driver->in_busy_dci)
43 return;
44
45 if (proc_num == MODEM_PROC) {
46 buf = driver->buf_in_dci;
47 smd_ch = driver->ch_dci;
48 }
49
50 if (!smd_ch || !buf)
51 return;
52
53 r = smd_read_avail(smd_ch);
54 if (r > IN_BUF_SIZE) {
55 if (r < MAX_IN_BUF_SIZE) {
56 pr_err("diag: SMD DCI sending pkt upto %d bytes", r);
57 buf = krealloc(buf, r, GFP_KERNEL);
58 } else {
59 pr_err("diag: DCI pkt > %d bytes", MAX_IN_BUF_SIZE);
60 return;
61 }
62 }
63 if (buf && r > 0) {
64 smd_read(smd_ch, buf, r);
65 pr_debug("diag: data received ---\n");
66 for (i = 0; i < r; i++)
67 pr_debug("\t %x \t", *(((unsigned char *)buf)+i));
68
69 if (*(uint8_t *)(buf+4) != DCI_CMD_CODE)
70 cmd_code_len = 4; /* delayed response */
71 driver->write_ptr_dci->length =
72 (int)(*(uint16_t *)(buf+2)) - (4+cmd_code_len);
73 pr_debug("diag: len = %d\n", (int)(*(uint16_t *)(buf+2))
74 - (4+cmd_code_len));
75 /* look up DCI client with tag */
76 for (i = 0; i < dci_max_reg; i++) {
77 if (driver->dci_tbl[i].tag ==
78 *(int *)(buf+(4+cmd_code_len))) {
79 found = 0;
80 break;
81 }
82 }
83 if (found)
84 pr_alert("diag: No matching PID for DCI data\n");
85 pr_debug("\n diag PID = %d", driver->dci_tbl[i].pid);
86 if (driver->dci_tbl[i].pid == 0)
87 pr_alert("diag: Receiving DCI process deleted\n");
88 *(int *)(buf+4+cmd_code_len) = driver->dci_tbl[i].uid;
89 /* update len after adding UID */
90 driver->write_ptr_dci->length =
91 driver->write_ptr_dci->length + 4;
92 pr_debug("diag: data receivd, wake process\n");
93 driver->in_busy_dci = 1;
94 diag_update_sleeping_process(driver->dci_tbl[i].pid,
95 DCI_DATA_TYPE);
96 /* delete immediate response entry */
97 if (driver->buf_in_dci[8+cmd_code_len] != 0x80)
98 driver->dci_tbl[i].pid = 0;
99 for (i = 0; i < dci_max_reg; i++)
100 if (driver->dci_tbl[i].pid != 0)
101 pr_debug("diag: PID = %d, UID = %d, tag = %d\n",
102 driver->dci_tbl[i].pid, driver->dci_tbl[i].uid,
103 driver->dci_tbl[i].tag);
104 pr_debug("diag: completed clearing table\n");
105 }
106}
107
108void diag_read_smd_dci_work_fn(struct work_struct *work)
109{
110 diag_smd_dci_send_req(MODEM_PROC);
111}
112
113static void diag_smd_dci_notify(void *ctxt, unsigned event)
114{
115 queue_work(driver->diag_wq, &(driver->diag_read_smd_dci_work));
116}
117
118static int diag_dci_probe(struct platform_device *pdev)
119{
120 int err = 0;
121
122 if (pdev->id == SMD_APPS_MODEM) {
123 err = smd_open("DIAG_2", &driver->ch_dci, driver,
124 diag_smd_dci_notify);
125 if (err)
126 pr_err("diag: cannot open DCI port, Id = %d, err ="
127 " %d\n", pdev->id, err);
128 }
129 return err;
130}
131
132
133int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
134 int len, int index)
135{
136 int i;
137
138 /* remove UID from user space pkt before sending to peripheral */
139 buf = buf + 4;
140 len = len - 4;
141 mutex_lock(&driver->dci_mutex);
142 /* prepare DCI packet */
143 driver->apps_dci_buf[0] = CONTROL_CHAR; /* start */
144 driver->apps_dci_buf[1] = 1; /* version */
145 *(uint16_t *)(driver->apps_dci_buf + 2) = len + 4 + 1; /* length */
146 driver->apps_dci_buf[4] = DCI_CMD_CODE; /* DCI ID */
147 *(int *)(driver->apps_dci_buf + 5) = driver->dci_tbl[index].tag;
148 for (i = 0; i < len; i++)
149 driver->apps_dci_buf[i+9] = *(buf+i);
150 driver->apps_dci_buf[9+len] = CONTROL_CHAR; /* end */
151
152 if (entry.client_id == MODEM_PROC && driver->ch_dci) {
153 smd_write(driver->ch_dci, driver->apps_dci_buf, len + 10);
154 i = DIAG_DCI_NO_ERROR;
155 } else {
156 pr_alert("diag: check DCI channel\n");
157 i = DIAG_DCI_SEND_DATA_FAIL;
158 }
159 mutex_unlock(&driver->dci_mutex);
160 return i;
161}
162
163int diag_register_dci_transaction(int uid)
164{
165 int i, new_dci_client = 1, ret = -1;
166
167 for (i = 0; i < dci_max_reg; i++) {
168 if (driver->dci_tbl[i].pid == current->tgid) {
169 new_dci_client = 0;
170 break;
171 }
172 }
173 mutex_lock(&driver->dci_mutex);
174 if (new_dci_client)
175 driver->num_dci_client++;
176 if (driver->num_dci_client > MAX_DCI_CLIENT) {
177 pr_info("diag: Max DCI Client limit reached\n");
178 driver->num_dci_client--;
179 mutex_unlock(&driver->dci_mutex);
180 return ret;
181 }
182 /* Make an entry in kernel DCI table */
183 driver->dci_tag++;
184 for (i = 0; i < dci_max_reg; i++) {
185 if (driver->dci_tbl[i].pid == 0) {
186 driver->dci_tbl[i].pid = current->tgid;
187 driver->dci_tbl[i].uid = uid;
188 driver->dci_tbl[i].tag = driver->dci_tag;
189 ret = i;
190 break;
191 }
192 }
193 mutex_unlock(&driver->dci_mutex);
194 return ret;
195}
196
197int diag_process_dci_client(unsigned char *buf, int len)
198{
199 unsigned char *temp = buf;
200 uint16_t subsys_cmd_code;
201 int subsys_id, cmd_code, i, ret = -1, index = -1;
202 struct diag_master_table entry;
203
204 /* enter this UID into kernel table and return index */
205 index = diag_register_dci_transaction(*(int *)temp);
206 if (index < 0) {
207 pr_alert("diag: registering new DCI transaction failed\n");
208 return DIAG_DCI_NO_REG;
209 }
210 temp += 4;
211 /* Check for registered peripheral and fwd pkt to apropriate proc */
212 cmd_code = (int)(*(char *)buf);
213 temp++;
214 subsys_id = (int)(*(char *)temp);
215 temp++;
216 subsys_cmd_code = *(uint16_t *)temp;
217 temp += 2;
218 pr_debug("diag: %d %d %d", cmd_code, subsys_id, subsys_cmd_code);
219 for (i = 0; i < diag_max_reg; i++) {
220 entry = driver->table[i];
221 if (entry.process_id != NO_PROCESS) {
222 if (entry.cmd_code == cmd_code && entry.subsys_id ==
223 subsys_id && entry.cmd_code_lo <=
224 subsys_cmd_code &&
225 entry.cmd_code_hi >= subsys_cmd_code) {
226 ret = diag_send_dci_pkt(entry, buf, len, index);
227 } else if (entry.cmd_code == 255
228 && cmd_code == 75) {
229 if (entry.subsys_id ==
230 subsys_id &&
231 entry.cmd_code_lo <=
232 subsys_cmd_code &&
233 entry.cmd_code_hi >=
234 subsys_cmd_code) {
235 ret = diag_send_dci_pkt(entry, buf, len,
236 index);
237 }
238 } else if (entry.cmd_code == 255 &&
239 entry.subsys_id == 255) {
240 if (entry.cmd_code_lo <=
241 cmd_code &&
242 entry.
243 cmd_code_hi >= cmd_code) {
244 ret = diag_send_dci_pkt(entry, buf, len,
245 index);
246 }
247 }
248 }
249 }
250 return ret;
251}
252
253static int diag_dci_runtime_suspend(struct device *dev)
254{
255 dev_dbg(dev, "pm_runtime: suspending...\n");
256 return 0;
257}
258
259static int diag_dci_runtime_resume(struct device *dev)
260{
261 dev_dbg(dev, "pm_runtime: resuming...\n");
262 return 0;
263}
264
265static const struct dev_pm_ops diag_dci_dev_pm_ops = {
266 .runtime_suspend = diag_dci_runtime_suspend,
267 .runtime_resume = diag_dci_runtime_resume,
268};
269
270struct platform_driver msm_diag_dci_driver = {
271 .probe = diag_dci_probe,
272 .driver = {
273 .name = "DIAG_2",
274 .owner = THIS_MODULE,
275 .pm = &diag_dci_dev_pm_ops,
276 },
277};
278
279int diag_dci_init(void)
280{
281 int success = 0;
282
283 driver->dci_tag = 0;
284 driver->dci_client_id = 0;
285 driver->num_dci_client = 0;
286 driver->in_busy_dci = 0;
287 mutex_init(&driver->dci_mutex);
288 if (driver->buf_in_dci == NULL) {
289 driver->buf_in_dci = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
290 if (driver->buf_in_dci == NULL)
291 goto err;
292 }
293 if (driver->write_ptr_dci == NULL) {
294 driver->write_ptr_dci = kzalloc(
295 sizeof(struct diag_write_device), GFP_KERNEL);
296 if (driver->write_ptr_dci == NULL)
297 goto err;
298 }
299 if (driver->dci_tbl == NULL) {
300 driver->dci_tbl = kzalloc(dci_max_reg *
301 sizeof(struct diag_dci_tbl), GFP_KERNEL);
302 if (driver->dci_tbl == NULL)
303 goto err;
304 }
305 if (driver->apps_dci_buf == NULL) {
306 driver->apps_dci_buf = kzalloc(APPS_BUF_SIZE, GFP_KERNEL);
307 if (driver->apps_dci_buf == NULL)
308 goto err;
309 }
310 success = platform_driver_register(&msm_diag_dci_driver);
311 if (success) {
312 pr_err("diag: Could not register DCI driver\n");
313 goto err;
314 }
315 return DIAG_DCI_NO_ERROR;
316err:
317 pr_err("diag: Could not initialize diag DCI buffers");
318 kfree(driver->dci_tbl);
319 kfree(driver->apps_dci_buf);
320 kfree(driver->buf_in_dci);
321 kfree(driver->write_ptr_dci);
322 return DIAG_DCI_NO_REG;
323}
324
325void diag_dci_exit(void)
326{
327 smd_close(driver->ch_dci);
328 driver->ch_dci = 0;
329 platform_driver_unregister(&msm_diag_dci_driver);
330 kfree(driver->dci_tbl);
331 kfree(driver->apps_dci_buf);
332 kfree(driver->buf_in_dci);
333 kfree(driver->write_ptr_dci);
334}
335