blob: 63c5fefc3963024ca9a46515a8bcb23b25a6a5ea [file] [log] [blame]
Jeff Hugo31f83b42012-01-25 15:15:26 -07001/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -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/*
14 * SMD Packet Driver -- Provides a binary SMD non-muxed packet port
15 * interface.
16 */
17
18#include <linux/slab.h>
19#include <linux/cdev.h>
20#include <linux/module.h>
21#include <linux/fs.h>
22#include <linux/device.h>
23#include <linux/sched.h>
24#include <linux/spinlock.h>
25#include <linux/mutex.h>
26#include <linux/delay.h>
27#include <linux/uaccess.h>
28#include <linux/workqueue.h>
29#include <linux/platform_device.h>
30#include <linux/completion.h>
31#include <linux/msm_smd_pkt.h>
32#include <linux/poll.h>
33#include <asm/ioctls.h>
Eric Holmbergc3c5cd92012-02-07 18:19:49 -070034#include <linux/wakelock.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035
36#include <mach/msm_smd.h>
37#include <mach/peripheral-loader.h>
38
39#include "smd_private.h"
40#ifdef CONFIG_ARCH_FSM9XXX
41#define NUM_SMD_PKT_PORTS 4
42#else
43#define NUM_SMD_PKT_PORTS 12
44#endif
45
46#define LOOPBACK_INX (NUM_SMD_PKT_PORTS - 1)
47
48#define DEVICE_NAME "smdpkt"
Eric Holmbergc3c5cd92012-02-07 18:19:49 -070049#define WAKELOCK_TIMEOUT (2*HZ)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050
51struct smd_pkt_dev {
52 struct cdev cdev;
53 struct device *devicep;
54 void *pil;
55 struct platform_driver driver;
56
57 struct smd_channel *ch;
58 struct mutex ch_lock;
59 struct mutex rx_lock;
60 struct mutex tx_lock;
61 wait_queue_head_t ch_read_wait_queue;
62 wait_queue_head_t ch_write_wait_queue;
63 wait_queue_head_t ch_opened_wait_queue;
64
65 int i;
66
67 int blocking_write;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070068 int is_open;
69 unsigned ch_size;
70 uint open_modem_wait;
71
72 int has_reset;
73 int do_reset_notification;
74 struct completion ch_allocated;
Eric Holmbergc3c5cd92012-02-07 18:19:49 -070075 struct wake_lock pa_wake_lock; /* Packet Arrival Wake lock*/
76 struct work_struct packet_arrival_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077} *smd_pkt_devp[NUM_SMD_PKT_PORTS];
78
79struct class *smd_pkt_classp;
80static dev_t smd_pkt_number;
81static struct delayed_work loopback_work;
82static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp);
83static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp);
84static uint32_t is_modem_smsm_inited(void);
85
86static int msm_smd_pkt_debug_mask;
87module_param_named(debug_mask, msm_smd_pkt_debug_mask,
88 int, S_IRUGO | S_IWUSR | S_IWGRP);
89#define DEBUG
90
91#ifdef DEBUG
92#define D_DUMP_BUFFER(prestr, cnt, buf) \
93do { \
94 if (msm_smd_pkt_debug_mask) \
95 print_hex_dump(KERN_DEBUG, prestr, \
96 DUMP_PREFIX_NONE, 16, 1, \
97 buf, cnt, 1); \
98} while (0)
99#else
100#define D_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
101#endif
102
103#ifdef DEBUG
104#define D(x...) if (msm_smd_pkt_debug_mask) printk(x)
105#else
106#define D(x...) do {} while (0)
107#endif
108
109static ssize_t open_timeout_store(struct device *d,
110 struct device_attribute *attr,
111 const char *buf,
112 size_t n)
113{
114 int i;
115 unsigned long tmp;
116 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
117 if (smd_pkt_devp[i]->devicep == d)
118 break;
119 }
Jeff Hugo31f83b42012-01-25 15:15:26 -0700120 if (i >= NUM_SMD_PKT_PORTS) {
121 pr_err("%s: unable to match device to valid smd_pkt port\n",
122 __func__);
123 return -EINVAL;
124 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700125 if (!strict_strtoul(buf, 10, &tmp)) {
126 smd_pkt_devp[i]->open_modem_wait = tmp;
127 return n;
128 } else {
129 pr_err("%s: unable to convert: %s to an int\n", __func__,
130 buf);
131 return -EINVAL;
132 }
133}
134
135static ssize_t open_timeout_show(struct device *d,
136 struct device_attribute *attr,
137 char *buf)
138{
139 int i;
140 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
141 if (smd_pkt_devp[i]->devicep == d)
142 break;
143 }
Jeff Hugo31f83b42012-01-25 15:15:26 -0700144 if (i >= NUM_SMD_PKT_PORTS) {
145 pr_err("%s: unable to match device to valid smd_pkt port\n",
146 __func__);
147 return -EINVAL;
148 }
Karthikeyan Ramasubramanian63fa3d32011-09-29 17:06:26 -0600149 return snprintf(buf, PAGE_SIZE, "%d\n",
150 smd_pkt_devp[i]->open_modem_wait);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700151}
152
153static DEVICE_ATTR(open_timeout, 0664, open_timeout_show, open_timeout_store);
154
155static int notify_reset(struct smd_pkt_dev *smd_pkt_devp)
156{
157 smd_pkt_devp->do_reset_notification = 0;
158
159 return -ENETRESET;
160}
161
162static void clean_and_signal(struct smd_pkt_dev *smd_pkt_devp)
163{
164 smd_pkt_devp->do_reset_notification = 1;
165 smd_pkt_devp->has_reset = 1;
166
167 smd_pkt_devp->is_open = 0;
168
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700169 wake_up(&smd_pkt_devp->ch_read_wait_queue);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700170 wake_up(&smd_pkt_devp->ch_write_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171 wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
172}
173
174static void loopback_probe_worker(struct work_struct *work)
175{
176
177 /* Wait for the modem SMSM to be inited for the SMD
178 ** Loopback channel to be allocated at the modem. Since
179 ** the wait need to be done atmost once, using msleep
180 ** doesn't degrade the performance. */
181 if (!is_modem_smsm_inited())
182 schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000));
183 else
184 smsm_change_state(SMSM_APPS_STATE,
185 0, SMSM_SMD_LOOPBACK);
186
187}
188
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700189static void packet_arrival_worker(struct work_struct *work)
190{
191 struct smd_pkt_dev *smd_pkt_devp;
192
193 smd_pkt_devp = container_of(work, struct smd_pkt_dev,
194 packet_arrival_work);
195 mutex_lock(&smd_pkt_devp->ch_lock);
196 if (smd_pkt_devp->ch)
197 wake_lock_timeout(&smd_pkt_devp->pa_wake_lock,
198 WAKELOCK_TIMEOUT);
199 mutex_unlock(&smd_pkt_devp->ch_lock);
200}
201
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700202static long smd_pkt_ioctl(struct file *file, unsigned int cmd,
203 unsigned long arg)
204{
205 int ret;
206 struct smd_pkt_dev *smd_pkt_devp;
207
208 smd_pkt_devp = file->private_data;
209 if (!smd_pkt_devp)
210 return -EINVAL;
211
212 switch (cmd) {
213 case TIOCMGET:
214 ret = smd_tiocmget(smd_pkt_devp->ch);
215 break;
216 case TIOCMSET:
217 ret = smd_tiocmset(smd_pkt_devp->ch, arg, ~arg);
218 break;
219 case SMD_PKT_IOCTL_BLOCKING_WRITE:
220 ret = get_user(smd_pkt_devp->blocking_write, (int *)arg);
221 break;
222 default:
223 ret = -1;
224 }
225
226 return ret;
227}
228
229ssize_t smd_pkt_read(struct file *file,
230 char __user *buf,
231 size_t count,
232 loff_t *ppos)
233{
234 int r;
235 int bytes_read;
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700236 int pkt_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700237 struct smd_pkt_dev *smd_pkt_devp;
238 struct smd_channel *chl;
239
240 D(KERN_ERR "%s: read %i bytes\n",
241 __func__, count);
242
243 smd_pkt_devp = file->private_data;
244
245 if (!smd_pkt_devp || !smd_pkt_devp->ch)
246 return -EINVAL;
247
248 if (smd_pkt_devp->do_reset_notification) {
249 /* notify client that a reset occurred */
250 return notify_reset(smd_pkt_devp);
251 }
252
253 chl = smd_pkt_devp->ch;
254wait_for_packet:
255 r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue,
256 (smd_cur_packet_size(chl) > 0 &&
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700257 smd_read_avail(chl)) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700258 smd_pkt_devp->has_reset);
259
260 if (smd_pkt_devp->has_reset)
261 return notify_reset(smd_pkt_devp);
262
263 if (r < 0) {
264 /* qualify error message */
265 if (r != -ERESTARTSYS) {
266 /* we get this anytime a signal comes in */
267 printk(KERN_ERR "ERROR:%s:%i:%s: "
268 "wait_event_interruptible ret %i\n",
269 __FILE__,
270 __LINE__,
271 __func__,
272 r
273 );
274 }
275 return r;
276 }
277
278 /* Here we have a whole packet waiting for us */
279
280 mutex_lock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700281 pkt_size = smd_cur_packet_size(smd_pkt_devp->ch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700282
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700283 if (!pkt_size) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700284 D(KERN_ERR "%s: Nothing to read\n", __func__);
285 mutex_unlock(&smd_pkt_devp->rx_lock);
286 goto wait_for_packet;
287 }
288
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700289 if (pkt_size > count) {
290 pr_err("packet size %i > buffer size %i,", pkt_size, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700292 return -ETOOSMALL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293 }
294
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700295 bytes_read = 0;
296 do {
297 r = smd_read_user_buffer(smd_pkt_devp->ch,
298 (buf + bytes_read),
299 (pkt_size - bytes_read));
300 if (r < 0) {
301 mutex_unlock(&smd_pkt_devp->rx_lock);
302 if (smd_pkt_devp->has_reset)
303 return notify_reset(smd_pkt_devp);
304 return r;
305 }
306 bytes_read += r;
307 if (pkt_size != bytes_read)
308 wait_event(smd_pkt_devp->ch_read_wait_queue,
309 smd_read_avail(smd_pkt_devp->ch) ||
310 smd_pkt_devp->has_reset);
311 if (smd_pkt_devp->has_reset) {
312 mutex_unlock(&smd_pkt_devp->rx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700313 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700314 }
315 } while (pkt_size != bytes_read);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700316 D_DUMP_BUFFER("read: ", bytes_read, buf);
317 mutex_unlock(&smd_pkt_devp->rx_lock);
318
319 D(KERN_ERR "%s: just read %i bytes\n",
320 __func__, bytes_read);
321
322 /* check and wakeup read threads waiting on this device */
323 check_and_wakeup_reader(smd_pkt_devp);
324
325 return bytes_read;
326}
327
328ssize_t smd_pkt_write(struct file *file,
329 const char __user *buf,
330 size_t count,
331 loff_t *ppos)
332{
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700333 int r = 0, bytes_written;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700334 struct smd_pkt_dev *smd_pkt_devp;
335 DEFINE_WAIT(write_wait);
336
337 D(KERN_ERR "%s: writting %i bytes\n",
338 __func__, count);
339
340 smd_pkt_devp = file->private_data;
341
342 if (!smd_pkt_devp || !smd_pkt_devp->ch)
343 return -EINVAL;
344
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700345 if (smd_pkt_devp->do_reset_notification) {
346 /* notify client that a reset occurred */
347 return notify_reset(smd_pkt_devp);
348 }
349
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700350 mutex_lock(&smd_pkt_devp->tx_lock);
351 if (!smd_pkt_devp->blocking_write) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700352 if (smd_write_avail(smd_pkt_devp->ch) < count) {
353 D(KERN_ERR "%s: Not enough space to write\n",
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700354 __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700355 mutex_unlock(&smd_pkt_devp->tx_lock);
356 return -ENOMEM;
357 }
358 }
359
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700360 r = smd_write_start(smd_pkt_devp->ch, count);
361 if (r < 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700362 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700363 pr_err("%s: Error %d @ smd_write_start\n", __func__, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700364 return r;
365 }
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700366
367 bytes_written = 0;
368 do {
369 prepare_to_wait(&smd_pkt_devp->ch_write_wait_queue,
370 &write_wait, TASK_UNINTERRUPTIBLE);
371 if (!smd_write_avail(smd_pkt_devp->ch) &&
372 !smd_pkt_devp->has_reset) {
373 smd_enable_read_intr(smd_pkt_devp->ch);
374 schedule();
375 }
376 finish_wait(&smd_pkt_devp->ch_write_wait_queue, &write_wait);
377 smd_disable_read_intr(smd_pkt_devp->ch);
378
379 if (smd_pkt_devp->has_reset) {
380 mutex_unlock(&smd_pkt_devp->tx_lock);
381 return notify_reset(smd_pkt_devp);
382 } else {
383 r = smd_write_segment(smd_pkt_devp->ch,
384 (void *)(buf + bytes_written),
385 (count - bytes_written), 1);
386 if (r < 0) {
387 mutex_unlock(&smd_pkt_devp->tx_lock);
388 if (smd_pkt_devp->has_reset)
389 return notify_reset(smd_pkt_devp);
390 }
391 bytes_written += r;
392 }
393 } while (bytes_written != count);
394 smd_write_end(smd_pkt_devp->ch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700395 mutex_unlock(&smd_pkt_devp->tx_lock);
396
397 D(KERN_ERR "%s: just wrote %i bytes\n",
398 __func__, count);
399
400 return count;
401}
402
403static unsigned int smd_pkt_poll(struct file *file, poll_table *wait)
404{
405 struct smd_pkt_dev *smd_pkt_devp;
406 unsigned int mask = 0;
407
408 smd_pkt_devp = file->private_data;
409 if (!smd_pkt_devp)
410 return POLLERR;
411
412 poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait);
413 if (smd_read_avail(smd_pkt_devp->ch))
414 mask |= POLLIN | POLLRDNORM;
415
416 return mask;
417}
418
419static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp)
420{
421 int sz;
422
423 if (!smd_pkt_devp || !smd_pkt_devp->ch)
424 return;
425
426 sz = smd_cur_packet_size(smd_pkt_devp->ch);
427 if (sz == 0) {
428 D(KERN_ERR "%s: packet size is 0\n", __func__);
429 return;
430 }
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700431 if (!smd_read_avail(smd_pkt_devp->ch)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700432 D(KERN_ERR "%s: packet size is %i - "
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700433 "but the data isn't here\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700434 __func__, sz);
435 return;
436 }
437
438 /* here we have a packet of size sz ready */
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700439 wake_up(&smd_pkt_devp->ch_read_wait_queue);
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700440 schedule_work(&smd_pkt_devp->packet_arrival_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700441 D(KERN_ERR "%s: after wake_up\n", __func__);
442}
443
444static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp)
445{
446 int sz;
447
448 if (!smd_pkt_devp || !smd_pkt_devp->ch)
449 return;
450
451 sz = smd_write_avail(smd_pkt_devp->ch);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700452 if (sz) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700453 D(KERN_ERR "%s: %d bytes Write Space available\n",
454 __func__, sz);
455 smd_disable_read_intr(smd_pkt_devp->ch);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700456 wake_up(&smd_pkt_devp->ch_write_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700457 }
458}
459
460static void ch_notify(void *priv, unsigned event)
461{
462 struct smd_pkt_dev *smd_pkt_devp = priv;
463
464 if (smd_pkt_devp->ch == 0)
465 return;
466
467 switch (event) {
468 case SMD_EVENT_DATA: {
469 D(KERN_ERR "%s: data\n", __func__);
470 check_and_wakeup_reader(smd_pkt_devp);
471 if (smd_pkt_devp->blocking_write)
472 check_and_wakeup_writer(smd_pkt_devp);
473 D(KERN_ERR "%s: data after check_and_wakeup\n", __func__);
474 break;
475 }
476 case SMD_EVENT_OPEN:
477 D(KERN_ERR "%s: smd opened\n",
478 __func__);
479
480 smd_pkt_devp->has_reset = 0;
481 smd_pkt_devp->is_open = 1;
482 wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
483 break;
484 case SMD_EVENT_CLOSE:
485 smd_pkt_devp->is_open = 0;
486 printk(KERN_ERR "%s: smd closed\n",
487 __func__);
488
489 /* put port into reset state */
490 clean_and_signal(smd_pkt_devp);
491 if (smd_pkt_devp->i == LOOPBACK_INX)
492 schedule_delayed_work(&loopback_work,
493 msecs_to_jiffies(1000));
494 break;
495 }
496}
497
498#ifdef CONFIG_ARCH_FSM9XXX
499static char *smd_pkt_dev_name[] = {
500 "smdcntl1",
501 "smdcntl2",
502 "smd22",
503 "smd_pkt_loopback",
504};
505
506static char *smd_ch_name[] = {
507 "DATA6_CNTL",
508 "DATA7_CNTL",
509 "DATA22",
510 "LOOPBACK",
511};
512
513static uint32_t smd_ch_edge[] = {
514 SMD_APPS_QDSP,
515 SMD_APPS_QDSP,
516 SMD_APPS_QDSP,
517 SMD_APPS_QDSP
518};
519#else
520static char *smd_pkt_dev_name[] = {
521 "smdcntl0",
522 "smdcntl1",
523 "smdcntl2",
524 "smdcntl3",
525 "smdcntl4",
526 "smdcntl5",
527 "smdcntl6",
528 "smdcntl7",
529 "smd22",
530 "smd_sns_dsps",
Angshuman Sarkara18a2722011-07-29 13:41:13 +0530531 "apr_apps2",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700532 "smd_pkt_loopback",
533};
534
535static char *smd_ch_name[] = {
536 "DATA5_CNTL",
537 "DATA6_CNTL",
538 "DATA7_CNTL",
539 "DATA8_CNTL",
540 "DATA9_CNTL",
541 "DATA12_CNTL",
542 "DATA13_CNTL",
543 "DATA14_CNTL",
544 "DATA22",
545 "SENSOR",
Angshuman Sarkara18a2722011-07-29 13:41:13 +0530546 "apr_apps2",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547 "LOOPBACK",
548};
549
550static uint32_t smd_ch_edge[] = {
551 SMD_APPS_MODEM,
552 SMD_APPS_MODEM,
553 SMD_APPS_MODEM,
554 SMD_APPS_MODEM,
555 SMD_APPS_MODEM,
556 SMD_APPS_MODEM,
557 SMD_APPS_MODEM,
558 SMD_APPS_MODEM,
559 SMD_APPS_MODEM,
560 SMD_APPS_DSPS,
561 SMD_APPS_QDSP,
562 SMD_APPS_MODEM,
563};
564#endif
565
566static int smd_pkt_dummy_probe(struct platform_device *pdev)
567{
568 int i;
569
570 for (i = 0; i < NUM_SMD_PKT_PORTS; i++) {
Jeff Hugoa5ee4362011-07-15 13:48:48 -0600571 if (!strncmp(pdev->name, smd_ch_name[i], SMD_MAX_CH_NAME_LEN)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700572 complete_all(&smd_pkt_devp[i]->ch_allocated);
573 break;
574 }
575 }
576 return 0;
577}
578
579static uint32_t is_modem_smsm_inited(void)
580{
581 uint32_t modem_state;
582 uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT);
583
584 modem_state = smsm_get_state(SMSM_MODEM_STATE);
585 return (modem_state & ready_state) == ready_state;
586}
587
588int smd_pkt_open(struct inode *inode, struct file *file)
589{
590 int r = 0;
591 struct smd_pkt_dev *smd_pkt_devp;
592 char *peripheral = NULL;
593
594 smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev);
595
596 if (!smd_pkt_devp)
597 return -EINVAL;
598
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700599 wake_lock_init(&smd_pkt_devp->pa_wake_lock, WAKE_LOCK_SUSPEND,
600 smd_pkt_dev_name[smd_pkt_devp->i]);
601 INIT_WORK(&smd_pkt_devp->packet_arrival_work, packet_arrival_worker);
602
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700603 file->private_data = smd_pkt_devp;
604
605 mutex_lock(&smd_pkt_devp->ch_lock);
606 if (smd_pkt_devp->ch == 0) {
607
608 if (smd_ch_edge[smd_pkt_devp->i] == SMD_APPS_MODEM)
609 peripheral = "modem";
610 else if (smd_ch_edge[smd_pkt_devp->i] == SMD_APPS_QDSP)
611 peripheral = "q6";
612
613 if (peripheral) {
614 smd_pkt_devp->pil = pil_get(peripheral);
615 if (IS_ERR(smd_pkt_devp->pil)) {
616 r = PTR_ERR(smd_pkt_devp->pil);
617 goto out;
618 }
619
620 /* Wait for the modem SMSM to be inited for the SMD
621 ** Loopback channel to be allocated at the modem. Since
622 ** the wait need to be done atmost once, using msleep
623 ** doesn't degrade the performance. */
Jeff Hugoa5ee4362011-07-15 13:48:48 -0600624 if (!strncmp(smd_ch_name[smd_pkt_devp->i], "LOOPBACK",
625 SMD_MAX_CH_NAME_LEN)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700626 if (!is_modem_smsm_inited())
627 msleep(5000);
628 smsm_change_state(SMSM_APPS_STATE,
629 0, SMSM_SMD_LOOPBACK);
630 msleep(100);
631 }
632
633 /*
634 * Wait for a packet channel to be allocated so we know
635 * the modem is ready enough.
636 */
637 if (smd_pkt_devp->open_modem_wait) {
638 r = wait_for_completion_interruptible_timeout(
639 &smd_pkt_devp->ch_allocated,
640 msecs_to_jiffies(
641 smd_pkt_devp->open_modem_wait
642 * 1000));
643 if (r == 0)
644 r = -ETIMEDOUT;
645 if (r < 0) {
646 pr_err("%s: wait failed for smd port:"
647 " %d\n", __func__, r);
648 goto release_pil;
649 }
650 }
651 }
652
653 r = smd_named_open_on_edge(smd_ch_name[smd_pkt_devp->i],
654 smd_ch_edge[smd_pkt_devp->i],
655 &smd_pkt_devp->ch,
656 smd_pkt_devp,
657 ch_notify);
658 if (r < 0) {
659 pr_err("%s: %s open failed %d\n", __func__,
660 smd_ch_name[smd_pkt_devp->i], r);
661 goto release_pil;
662 }
663
664 r = wait_event_interruptible_timeout(
665 smd_pkt_devp->ch_opened_wait_queue,
666 smd_pkt_devp->is_open, (2 * HZ));
667 if (r == 0)
668 r = -ETIMEDOUT;
669
670 if (r < 0) {
671 pr_err("%s: wait failed for smd open: %d\n",
672 __func__, r);
673 } else if (!smd_pkt_devp->is_open) {
674 pr_err("%s: Invalid open notification\n", __func__);
675 r = -ENODEV;
676 } else {
677 smd_disable_read_intr(smd_pkt_devp->ch);
678 smd_pkt_devp->ch_size =
679 smd_write_avail(smd_pkt_devp->ch);
680 r = 0;
681 }
682 }
683release_pil:
684 if (peripheral && (r < 0))
685 pil_put(smd_pkt_devp->pil);
686out:
687 mutex_unlock(&smd_pkt_devp->ch_lock);
688
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700689 if (r < 0)
690 wake_lock_destroy(&smd_pkt_devp->pa_wake_lock);
691
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700692 return r;
693}
694
695int smd_pkt_release(struct inode *inode, struct file *file)
696{
697 int r = 0;
698 struct smd_pkt_dev *smd_pkt_devp = file->private_data;
699
700 if (!smd_pkt_devp)
701 return -EINVAL;
702
703 clean_and_signal(smd_pkt_devp);
704
705 mutex_lock(&smd_pkt_devp->ch_lock);
706 if (smd_pkt_devp->ch != 0) {
707 r = smd_close(smd_pkt_devp->ch);
708 smd_pkt_devp->ch = 0;
709 smd_pkt_devp->blocking_write = 0;
710 if (smd_pkt_devp->pil)
711 pil_put(smd_pkt_devp->pil);
712 }
713 mutex_unlock(&smd_pkt_devp->ch_lock);
714
715 smd_pkt_devp->has_reset = 0;
716 smd_pkt_devp->do_reset_notification = 0;
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700717 wake_lock_destroy(&smd_pkt_devp->pa_wake_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700718
719 return r;
720}
721
722static const struct file_operations smd_pkt_fops = {
723 .owner = THIS_MODULE,
724 .open = smd_pkt_open,
725 .release = smd_pkt_release,
726 .read = smd_pkt_read,
727 .write = smd_pkt_write,
728 .poll = smd_pkt_poll,
729 .unlocked_ioctl = smd_pkt_ioctl,
730};
731
732static int __init smd_pkt_init(void)
733{
734 int i;
735 int r;
736
737 r = alloc_chrdev_region(&smd_pkt_number,
738 0,
739 NUM_SMD_PKT_PORTS,
740 DEVICE_NAME);
741 if (IS_ERR_VALUE(r)) {
742 printk(KERN_ERR "ERROR:%s:%i:%s: "
743 "alloc_chrdev_region() ret %i.\n",
744 __FILE__,
745 __LINE__,
746 __func__,
747 r);
748 goto error0;
749 }
750
751 smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME);
752 if (IS_ERR(smd_pkt_classp)) {
753 printk(KERN_ERR "ERROR:%s:%i:%s: "
754 "class_create() ENOMEM\n",
755 __FILE__,
756 __LINE__,
757 __func__);
758 r = -ENOMEM;
759 goto error1;
760 }
761
762 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
763 smd_pkt_devp[i] = kzalloc(sizeof(struct smd_pkt_dev),
764 GFP_KERNEL);
765 if (IS_ERR(smd_pkt_devp[i])) {
766 printk(KERN_ERR "ERROR:%s:%i:%s kmalloc() ENOMEM\n",
767 __FILE__,
768 __LINE__,
769 __func__);
770 r = -ENOMEM;
771 goto error2;
772 }
773
774 smd_pkt_devp[i]->i = i;
775
776 init_waitqueue_head(&smd_pkt_devp[i]->ch_read_wait_queue);
777 init_waitqueue_head(&smd_pkt_devp[i]->ch_write_wait_queue);
778 smd_pkt_devp[i]->is_open = 0;
779 init_waitqueue_head(&smd_pkt_devp[i]->ch_opened_wait_queue);
780
781 mutex_init(&smd_pkt_devp[i]->ch_lock);
782 mutex_init(&smd_pkt_devp[i]->rx_lock);
783 mutex_init(&smd_pkt_devp[i]->tx_lock);
784 init_completion(&smd_pkt_devp[i]->ch_allocated);
785
786 cdev_init(&smd_pkt_devp[i]->cdev, &smd_pkt_fops);
787 smd_pkt_devp[i]->cdev.owner = THIS_MODULE;
788
789 r = cdev_add(&smd_pkt_devp[i]->cdev,
790 (smd_pkt_number + i),
791 1);
792
793 if (IS_ERR_VALUE(r)) {
794 printk(KERN_ERR "%s:%i:%s: cdev_add() ret %i\n",
795 __FILE__,
796 __LINE__,
797 __func__,
798 r);
799 kfree(smd_pkt_devp[i]);
800 goto error2;
801 }
802
803 smd_pkt_devp[i]->devicep =
804 device_create(smd_pkt_classp,
805 NULL,
806 (smd_pkt_number + i),
807 NULL,
808 smd_pkt_dev_name[i]);
809
810 if (IS_ERR(smd_pkt_devp[i]->devicep)) {
811 printk(KERN_ERR "%s:%i:%s: "
812 "device_create() ENOMEM\n",
813 __FILE__,
814 __LINE__,
815 __func__);
816 r = -ENOMEM;
817 cdev_del(&smd_pkt_devp[i]->cdev);
818 kfree(smd_pkt_devp[i]);
819 goto error2;
820 }
821 if (device_create_file(smd_pkt_devp[i]->devicep,
822 &dev_attr_open_timeout))
823 pr_err("%s: unable to create device attr on #%d\n",
824 __func__, i);
825
826 smd_pkt_devp[i]->driver.probe = smd_pkt_dummy_probe;
827 smd_pkt_devp[i]->driver.driver.name = smd_ch_name[i];
828 smd_pkt_devp[i]->driver.driver.owner = THIS_MODULE;
829 r = platform_driver_register(&smd_pkt_devp[i]->driver);
830 if (r)
831 goto error2;
832 }
833
834 INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
835
836 D(KERN_INFO "SMD Packet Port Driver Initialized.\n");
837 return 0;
838
839 error2:
840 if (i > 0) {
841 while (--i >= 0) {
842 platform_driver_unregister(&smd_pkt_devp[i]->driver);
843 cdev_del(&smd_pkt_devp[i]->cdev);
844 kfree(smd_pkt_devp[i]);
845 device_destroy(smd_pkt_classp,
846 MKDEV(MAJOR(smd_pkt_number), i));
847 }
848 }
849
850 class_destroy(smd_pkt_classp);
851 error1:
852 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
853 error0:
854 return r;
855}
856
857static void __exit smd_pkt_cleanup(void)
858{
859 int i;
860
861 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
862 platform_driver_unregister(&smd_pkt_devp[i]->driver);
863 cdev_del(&smd_pkt_devp[i]->cdev);
864 kfree(smd_pkt_devp[i]);
865 device_destroy(smd_pkt_classp,
866 MKDEV(MAJOR(smd_pkt_number), i));
867 }
868
869 class_destroy(smd_pkt_classp);
870
871 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
872}
873
874module_init(smd_pkt_init);
875module_exit(smd_pkt_cleanup);
876
877MODULE_DESCRIPTION("MSM Shared Memory Packet Port");
878MODULE_LICENSE("GPL v2");