blob: 8023129ad906c40af0663aa6081bd8e79e4c978a [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>
34
35#include <mach/msm_smd.h>
36#include <mach/peripheral-loader.h>
37
38#include "smd_private.h"
39#ifdef CONFIG_ARCH_FSM9XXX
40#define NUM_SMD_PKT_PORTS 4
41#else
42#define NUM_SMD_PKT_PORTS 12
43#endif
44
45#define LOOPBACK_INX (NUM_SMD_PKT_PORTS - 1)
46
47#define DEVICE_NAME "smdpkt"
48
49struct smd_pkt_dev {
50 struct cdev cdev;
51 struct device *devicep;
52 void *pil;
53 struct platform_driver driver;
54
55 struct smd_channel *ch;
56 struct mutex ch_lock;
57 struct mutex rx_lock;
58 struct mutex tx_lock;
59 wait_queue_head_t ch_read_wait_queue;
60 wait_queue_head_t ch_write_wait_queue;
61 wait_queue_head_t ch_opened_wait_queue;
62
63 int i;
64
65 int blocking_write;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070066 int is_open;
67 unsigned ch_size;
68 uint open_modem_wait;
69
70 int has_reset;
71 int do_reset_notification;
72 struct completion ch_allocated;
73
74} *smd_pkt_devp[NUM_SMD_PKT_PORTS];
75
76struct class *smd_pkt_classp;
77static dev_t smd_pkt_number;
78static struct delayed_work loopback_work;
79static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp);
80static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp);
81static uint32_t is_modem_smsm_inited(void);
82
83static int msm_smd_pkt_debug_mask;
84module_param_named(debug_mask, msm_smd_pkt_debug_mask,
85 int, S_IRUGO | S_IWUSR | S_IWGRP);
86#define DEBUG
87
88#ifdef DEBUG
89#define D_DUMP_BUFFER(prestr, cnt, buf) \
90do { \
91 if (msm_smd_pkt_debug_mask) \
92 print_hex_dump(KERN_DEBUG, prestr, \
93 DUMP_PREFIX_NONE, 16, 1, \
94 buf, cnt, 1); \
95} while (0)
96#else
97#define D_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
98#endif
99
100#ifdef DEBUG
101#define D(x...) if (msm_smd_pkt_debug_mask) printk(x)
102#else
103#define D(x...) do {} while (0)
104#endif
105
106static ssize_t open_timeout_store(struct device *d,
107 struct device_attribute *attr,
108 const char *buf,
109 size_t n)
110{
111 int i;
112 unsigned long tmp;
113 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
114 if (smd_pkt_devp[i]->devicep == d)
115 break;
116 }
Jeff Hugo31f83b42012-01-25 15:15:26 -0700117 if (i >= NUM_SMD_PKT_PORTS) {
118 pr_err("%s: unable to match device to valid smd_pkt port\n",
119 __func__);
120 return -EINVAL;
121 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122 if (!strict_strtoul(buf, 10, &tmp)) {
123 smd_pkt_devp[i]->open_modem_wait = tmp;
124 return n;
125 } else {
126 pr_err("%s: unable to convert: %s to an int\n", __func__,
127 buf);
128 return -EINVAL;
129 }
130}
131
132static ssize_t open_timeout_show(struct device *d,
133 struct device_attribute *attr,
134 char *buf)
135{
136 int i;
137 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
138 if (smd_pkt_devp[i]->devicep == d)
139 break;
140 }
Jeff Hugo31f83b42012-01-25 15:15:26 -0700141 if (i >= NUM_SMD_PKT_PORTS) {
142 pr_err("%s: unable to match device to valid smd_pkt port\n",
143 __func__);
144 return -EINVAL;
145 }
Karthikeyan Ramasubramanian63fa3d32011-09-29 17:06:26 -0600146 return snprintf(buf, PAGE_SIZE, "%d\n",
147 smd_pkt_devp[i]->open_modem_wait);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700148}
149
150static DEVICE_ATTR(open_timeout, 0664, open_timeout_show, open_timeout_store);
151
152static int notify_reset(struct smd_pkt_dev *smd_pkt_devp)
153{
154 smd_pkt_devp->do_reset_notification = 0;
155
156 return -ENETRESET;
157}
158
159static void clean_and_signal(struct smd_pkt_dev *smd_pkt_devp)
160{
161 smd_pkt_devp->do_reset_notification = 1;
162 smd_pkt_devp->has_reset = 1;
163
164 smd_pkt_devp->is_open = 0;
165
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700166 wake_up(&smd_pkt_devp->ch_read_wait_queue);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700167 wake_up(&smd_pkt_devp->ch_write_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700168 wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
169}
170
171static void loopback_probe_worker(struct work_struct *work)
172{
173
174 /* Wait for the modem SMSM to be inited for the SMD
175 ** Loopback channel to be allocated at the modem. Since
176 ** the wait need to be done atmost once, using msleep
177 ** doesn't degrade the performance. */
178 if (!is_modem_smsm_inited())
179 schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000));
180 else
181 smsm_change_state(SMSM_APPS_STATE,
182 0, SMSM_SMD_LOOPBACK);
183
184}
185
186static long smd_pkt_ioctl(struct file *file, unsigned int cmd,
187 unsigned long arg)
188{
189 int ret;
190 struct smd_pkt_dev *smd_pkt_devp;
191
192 smd_pkt_devp = file->private_data;
193 if (!smd_pkt_devp)
194 return -EINVAL;
195
196 switch (cmd) {
197 case TIOCMGET:
198 ret = smd_tiocmget(smd_pkt_devp->ch);
199 break;
200 case TIOCMSET:
201 ret = smd_tiocmset(smd_pkt_devp->ch, arg, ~arg);
202 break;
203 case SMD_PKT_IOCTL_BLOCKING_WRITE:
204 ret = get_user(smd_pkt_devp->blocking_write, (int *)arg);
205 break;
206 default:
207 ret = -1;
208 }
209
210 return ret;
211}
212
213ssize_t smd_pkt_read(struct file *file,
214 char __user *buf,
215 size_t count,
216 loff_t *ppos)
217{
218 int r;
219 int bytes_read;
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700220 int pkt_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700221 struct smd_pkt_dev *smd_pkt_devp;
222 struct smd_channel *chl;
223
224 D(KERN_ERR "%s: read %i bytes\n",
225 __func__, count);
226
227 smd_pkt_devp = file->private_data;
228
229 if (!smd_pkt_devp || !smd_pkt_devp->ch)
230 return -EINVAL;
231
232 if (smd_pkt_devp->do_reset_notification) {
233 /* notify client that a reset occurred */
234 return notify_reset(smd_pkt_devp);
235 }
236
237 chl = smd_pkt_devp->ch;
238wait_for_packet:
239 r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue,
240 (smd_cur_packet_size(chl) > 0 &&
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700241 smd_read_avail(chl)) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700242 smd_pkt_devp->has_reset);
243
244 if (smd_pkt_devp->has_reset)
245 return notify_reset(smd_pkt_devp);
246
247 if (r < 0) {
248 /* qualify error message */
249 if (r != -ERESTARTSYS) {
250 /* we get this anytime a signal comes in */
251 printk(KERN_ERR "ERROR:%s:%i:%s: "
252 "wait_event_interruptible ret %i\n",
253 __FILE__,
254 __LINE__,
255 __func__,
256 r
257 );
258 }
259 return r;
260 }
261
262 /* Here we have a whole packet waiting for us */
263
264 mutex_lock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700265 pkt_size = smd_cur_packet_size(smd_pkt_devp->ch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700266
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700267 if (!pkt_size) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700268 D(KERN_ERR "%s: Nothing to read\n", __func__);
269 mutex_unlock(&smd_pkt_devp->rx_lock);
270 goto wait_for_packet;
271 }
272
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700273 if (pkt_size > count) {
274 pr_err("packet size %i > buffer size %i,", pkt_size, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700275 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700276 return -ETOOSMALL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700277 }
278
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700279 bytes_read = 0;
280 do {
281 r = smd_read_user_buffer(smd_pkt_devp->ch,
282 (buf + bytes_read),
283 (pkt_size - bytes_read));
284 if (r < 0) {
285 mutex_unlock(&smd_pkt_devp->rx_lock);
286 if (smd_pkt_devp->has_reset)
287 return notify_reset(smd_pkt_devp);
288 return r;
289 }
290 bytes_read += r;
291 if (pkt_size != bytes_read)
292 wait_event(smd_pkt_devp->ch_read_wait_queue,
293 smd_read_avail(smd_pkt_devp->ch) ||
294 smd_pkt_devp->has_reset);
295 if (smd_pkt_devp->has_reset) {
296 mutex_unlock(&smd_pkt_devp->rx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700298 }
299 } while (pkt_size != bytes_read);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700300 D_DUMP_BUFFER("read: ", bytes_read, buf);
301 mutex_unlock(&smd_pkt_devp->rx_lock);
302
303 D(KERN_ERR "%s: just read %i bytes\n",
304 __func__, bytes_read);
305
306 /* check and wakeup read threads waiting on this device */
307 check_and_wakeup_reader(smd_pkt_devp);
308
309 return bytes_read;
310}
311
312ssize_t smd_pkt_write(struct file *file,
313 const char __user *buf,
314 size_t count,
315 loff_t *ppos)
316{
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700317 int r = 0, bytes_written;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700318 struct smd_pkt_dev *smd_pkt_devp;
319 DEFINE_WAIT(write_wait);
320
321 D(KERN_ERR "%s: writting %i bytes\n",
322 __func__, count);
323
324 smd_pkt_devp = file->private_data;
325
326 if (!smd_pkt_devp || !smd_pkt_devp->ch)
327 return -EINVAL;
328
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700329 if (smd_pkt_devp->do_reset_notification) {
330 /* notify client that a reset occurred */
331 return notify_reset(smd_pkt_devp);
332 }
333
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700334 mutex_lock(&smd_pkt_devp->tx_lock);
335 if (!smd_pkt_devp->blocking_write) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700336 if (smd_write_avail(smd_pkt_devp->ch) < count) {
337 D(KERN_ERR "%s: Not enough space to write\n",
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700338 __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700339 mutex_unlock(&smd_pkt_devp->tx_lock);
340 return -ENOMEM;
341 }
342 }
343
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700344 r = smd_write_start(smd_pkt_devp->ch, count);
345 if (r < 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700346 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700347 pr_err("%s: Error %d @ smd_write_start\n", __func__, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700348 return r;
349 }
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700350
351 bytes_written = 0;
352 do {
353 prepare_to_wait(&smd_pkt_devp->ch_write_wait_queue,
354 &write_wait, TASK_UNINTERRUPTIBLE);
355 if (!smd_write_avail(smd_pkt_devp->ch) &&
356 !smd_pkt_devp->has_reset) {
357 smd_enable_read_intr(smd_pkt_devp->ch);
358 schedule();
359 }
360 finish_wait(&smd_pkt_devp->ch_write_wait_queue, &write_wait);
361 smd_disable_read_intr(smd_pkt_devp->ch);
362
363 if (smd_pkt_devp->has_reset) {
364 mutex_unlock(&smd_pkt_devp->tx_lock);
365 return notify_reset(smd_pkt_devp);
366 } else {
367 r = smd_write_segment(smd_pkt_devp->ch,
368 (void *)(buf + bytes_written),
369 (count - bytes_written), 1);
370 if (r < 0) {
371 mutex_unlock(&smd_pkt_devp->tx_lock);
372 if (smd_pkt_devp->has_reset)
373 return notify_reset(smd_pkt_devp);
374 }
375 bytes_written += r;
376 }
377 } while (bytes_written != count);
378 smd_write_end(smd_pkt_devp->ch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700379 mutex_unlock(&smd_pkt_devp->tx_lock);
380
381 D(KERN_ERR "%s: just wrote %i bytes\n",
382 __func__, count);
383
384 return count;
385}
386
387static unsigned int smd_pkt_poll(struct file *file, poll_table *wait)
388{
389 struct smd_pkt_dev *smd_pkt_devp;
390 unsigned int mask = 0;
391
392 smd_pkt_devp = file->private_data;
393 if (!smd_pkt_devp)
394 return POLLERR;
395
396 poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait);
397 if (smd_read_avail(smd_pkt_devp->ch))
398 mask |= POLLIN | POLLRDNORM;
399
400 return mask;
401}
402
403static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp)
404{
405 int sz;
406
407 if (!smd_pkt_devp || !smd_pkt_devp->ch)
408 return;
409
410 sz = smd_cur_packet_size(smd_pkt_devp->ch);
411 if (sz == 0) {
412 D(KERN_ERR "%s: packet size is 0\n", __func__);
413 return;
414 }
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700415 if (!smd_read_avail(smd_pkt_devp->ch)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700416 D(KERN_ERR "%s: packet size is %i - "
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700417 "but the data isn't here\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700418 __func__, sz);
419 return;
420 }
421
422 /* here we have a packet of size sz ready */
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700423 wake_up(&smd_pkt_devp->ch_read_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700424 D(KERN_ERR "%s: after wake_up\n", __func__);
425}
426
427static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp)
428{
429 int sz;
430
431 if (!smd_pkt_devp || !smd_pkt_devp->ch)
432 return;
433
434 sz = smd_write_avail(smd_pkt_devp->ch);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700435 if (sz) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700436 D(KERN_ERR "%s: %d bytes Write Space available\n",
437 __func__, sz);
438 smd_disable_read_intr(smd_pkt_devp->ch);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700439 wake_up(&smd_pkt_devp->ch_write_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700440 }
441}
442
443static void ch_notify(void *priv, unsigned event)
444{
445 struct smd_pkt_dev *smd_pkt_devp = priv;
446
447 if (smd_pkt_devp->ch == 0)
448 return;
449
450 switch (event) {
451 case SMD_EVENT_DATA: {
452 D(KERN_ERR "%s: data\n", __func__);
453 check_and_wakeup_reader(smd_pkt_devp);
454 if (smd_pkt_devp->blocking_write)
455 check_and_wakeup_writer(smd_pkt_devp);
456 D(KERN_ERR "%s: data after check_and_wakeup\n", __func__);
457 break;
458 }
459 case SMD_EVENT_OPEN:
460 D(KERN_ERR "%s: smd opened\n",
461 __func__);
462
463 smd_pkt_devp->has_reset = 0;
464 smd_pkt_devp->is_open = 1;
465 wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
466 break;
467 case SMD_EVENT_CLOSE:
468 smd_pkt_devp->is_open = 0;
469 printk(KERN_ERR "%s: smd closed\n",
470 __func__);
471
472 /* put port into reset state */
473 clean_and_signal(smd_pkt_devp);
474 if (smd_pkt_devp->i == LOOPBACK_INX)
475 schedule_delayed_work(&loopback_work,
476 msecs_to_jiffies(1000));
477 break;
478 }
479}
480
481#ifdef CONFIG_ARCH_FSM9XXX
482static char *smd_pkt_dev_name[] = {
483 "smdcntl1",
484 "smdcntl2",
485 "smd22",
486 "smd_pkt_loopback",
487};
488
489static char *smd_ch_name[] = {
490 "DATA6_CNTL",
491 "DATA7_CNTL",
492 "DATA22",
493 "LOOPBACK",
494};
495
496static uint32_t smd_ch_edge[] = {
497 SMD_APPS_QDSP,
498 SMD_APPS_QDSP,
499 SMD_APPS_QDSP,
500 SMD_APPS_QDSP
501};
502#else
503static char *smd_pkt_dev_name[] = {
504 "smdcntl0",
505 "smdcntl1",
506 "smdcntl2",
507 "smdcntl3",
508 "smdcntl4",
509 "smdcntl5",
510 "smdcntl6",
511 "smdcntl7",
512 "smd22",
513 "smd_sns_dsps",
Angshuman Sarkara18a2722011-07-29 13:41:13 +0530514 "apr_apps2",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700515 "smd_pkt_loopback",
516};
517
518static char *smd_ch_name[] = {
519 "DATA5_CNTL",
520 "DATA6_CNTL",
521 "DATA7_CNTL",
522 "DATA8_CNTL",
523 "DATA9_CNTL",
524 "DATA12_CNTL",
525 "DATA13_CNTL",
526 "DATA14_CNTL",
527 "DATA22",
528 "SENSOR",
Angshuman Sarkara18a2722011-07-29 13:41:13 +0530529 "apr_apps2",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700530 "LOOPBACK",
531};
532
533static uint32_t smd_ch_edge[] = {
534 SMD_APPS_MODEM,
535 SMD_APPS_MODEM,
536 SMD_APPS_MODEM,
537 SMD_APPS_MODEM,
538 SMD_APPS_MODEM,
539 SMD_APPS_MODEM,
540 SMD_APPS_MODEM,
541 SMD_APPS_MODEM,
542 SMD_APPS_MODEM,
543 SMD_APPS_DSPS,
544 SMD_APPS_QDSP,
545 SMD_APPS_MODEM,
546};
547#endif
548
549static int smd_pkt_dummy_probe(struct platform_device *pdev)
550{
551 int i;
552
553 for (i = 0; i < NUM_SMD_PKT_PORTS; i++) {
Jeff Hugoa5ee4362011-07-15 13:48:48 -0600554 if (!strncmp(pdev->name, smd_ch_name[i], SMD_MAX_CH_NAME_LEN)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700555 complete_all(&smd_pkt_devp[i]->ch_allocated);
556 break;
557 }
558 }
559 return 0;
560}
561
562static uint32_t is_modem_smsm_inited(void)
563{
564 uint32_t modem_state;
565 uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT);
566
567 modem_state = smsm_get_state(SMSM_MODEM_STATE);
568 return (modem_state & ready_state) == ready_state;
569}
570
571int smd_pkt_open(struct inode *inode, struct file *file)
572{
573 int r = 0;
574 struct smd_pkt_dev *smd_pkt_devp;
575 char *peripheral = NULL;
576
577 smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev);
578
579 if (!smd_pkt_devp)
580 return -EINVAL;
581
582 file->private_data = smd_pkt_devp;
583
584 mutex_lock(&smd_pkt_devp->ch_lock);
585 if (smd_pkt_devp->ch == 0) {
586
587 if (smd_ch_edge[smd_pkt_devp->i] == SMD_APPS_MODEM)
588 peripheral = "modem";
589 else if (smd_ch_edge[smd_pkt_devp->i] == SMD_APPS_QDSP)
590 peripheral = "q6";
591
592 if (peripheral) {
593 smd_pkt_devp->pil = pil_get(peripheral);
594 if (IS_ERR(smd_pkt_devp->pil)) {
595 r = PTR_ERR(smd_pkt_devp->pil);
596 goto out;
597 }
598
599 /* Wait for the modem SMSM to be inited for the SMD
600 ** Loopback channel to be allocated at the modem. Since
601 ** the wait need to be done atmost once, using msleep
602 ** doesn't degrade the performance. */
Jeff Hugoa5ee4362011-07-15 13:48:48 -0600603 if (!strncmp(smd_ch_name[smd_pkt_devp->i], "LOOPBACK",
604 SMD_MAX_CH_NAME_LEN)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700605 if (!is_modem_smsm_inited())
606 msleep(5000);
607 smsm_change_state(SMSM_APPS_STATE,
608 0, SMSM_SMD_LOOPBACK);
609 msleep(100);
610 }
611
612 /*
613 * Wait for a packet channel to be allocated so we know
614 * the modem is ready enough.
615 */
616 if (smd_pkt_devp->open_modem_wait) {
617 r = wait_for_completion_interruptible_timeout(
618 &smd_pkt_devp->ch_allocated,
619 msecs_to_jiffies(
620 smd_pkt_devp->open_modem_wait
621 * 1000));
622 if (r == 0)
623 r = -ETIMEDOUT;
624 if (r < 0) {
625 pr_err("%s: wait failed for smd port:"
626 " %d\n", __func__, r);
627 goto release_pil;
628 }
629 }
630 }
631
632 r = smd_named_open_on_edge(smd_ch_name[smd_pkt_devp->i],
633 smd_ch_edge[smd_pkt_devp->i],
634 &smd_pkt_devp->ch,
635 smd_pkt_devp,
636 ch_notify);
637 if (r < 0) {
638 pr_err("%s: %s open failed %d\n", __func__,
639 smd_ch_name[smd_pkt_devp->i], r);
640 goto release_pil;
641 }
642
643 r = wait_event_interruptible_timeout(
644 smd_pkt_devp->ch_opened_wait_queue,
645 smd_pkt_devp->is_open, (2 * HZ));
646 if (r == 0)
647 r = -ETIMEDOUT;
648
649 if (r < 0) {
650 pr_err("%s: wait failed for smd open: %d\n",
651 __func__, r);
652 } else if (!smd_pkt_devp->is_open) {
653 pr_err("%s: Invalid open notification\n", __func__);
654 r = -ENODEV;
655 } else {
656 smd_disable_read_intr(smd_pkt_devp->ch);
657 smd_pkt_devp->ch_size =
658 smd_write_avail(smd_pkt_devp->ch);
659 r = 0;
660 }
661 }
662release_pil:
663 if (peripheral && (r < 0))
664 pil_put(smd_pkt_devp->pil);
665out:
666 mutex_unlock(&smd_pkt_devp->ch_lock);
667
668 return r;
669}
670
671int smd_pkt_release(struct inode *inode, struct file *file)
672{
673 int r = 0;
674 struct smd_pkt_dev *smd_pkt_devp = file->private_data;
675
676 if (!smd_pkt_devp)
677 return -EINVAL;
678
679 clean_and_signal(smd_pkt_devp);
680
681 mutex_lock(&smd_pkt_devp->ch_lock);
682 if (smd_pkt_devp->ch != 0) {
683 r = smd_close(smd_pkt_devp->ch);
684 smd_pkt_devp->ch = 0;
685 smd_pkt_devp->blocking_write = 0;
686 if (smd_pkt_devp->pil)
687 pil_put(smd_pkt_devp->pil);
688 }
689 mutex_unlock(&smd_pkt_devp->ch_lock);
690
691 smd_pkt_devp->has_reset = 0;
692 smd_pkt_devp->do_reset_notification = 0;
693
694 return r;
695}
696
697static const struct file_operations smd_pkt_fops = {
698 .owner = THIS_MODULE,
699 .open = smd_pkt_open,
700 .release = smd_pkt_release,
701 .read = smd_pkt_read,
702 .write = smd_pkt_write,
703 .poll = smd_pkt_poll,
704 .unlocked_ioctl = smd_pkt_ioctl,
705};
706
707static int __init smd_pkt_init(void)
708{
709 int i;
710 int r;
711
712 r = alloc_chrdev_region(&smd_pkt_number,
713 0,
714 NUM_SMD_PKT_PORTS,
715 DEVICE_NAME);
716 if (IS_ERR_VALUE(r)) {
717 printk(KERN_ERR "ERROR:%s:%i:%s: "
718 "alloc_chrdev_region() ret %i.\n",
719 __FILE__,
720 __LINE__,
721 __func__,
722 r);
723 goto error0;
724 }
725
726 smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME);
727 if (IS_ERR(smd_pkt_classp)) {
728 printk(KERN_ERR "ERROR:%s:%i:%s: "
729 "class_create() ENOMEM\n",
730 __FILE__,
731 __LINE__,
732 __func__);
733 r = -ENOMEM;
734 goto error1;
735 }
736
737 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
738 smd_pkt_devp[i] = kzalloc(sizeof(struct smd_pkt_dev),
739 GFP_KERNEL);
740 if (IS_ERR(smd_pkt_devp[i])) {
741 printk(KERN_ERR "ERROR:%s:%i:%s kmalloc() ENOMEM\n",
742 __FILE__,
743 __LINE__,
744 __func__);
745 r = -ENOMEM;
746 goto error2;
747 }
748
749 smd_pkt_devp[i]->i = i;
750
751 init_waitqueue_head(&smd_pkt_devp[i]->ch_read_wait_queue);
752 init_waitqueue_head(&smd_pkt_devp[i]->ch_write_wait_queue);
753 smd_pkt_devp[i]->is_open = 0;
754 init_waitqueue_head(&smd_pkt_devp[i]->ch_opened_wait_queue);
755
756 mutex_init(&smd_pkt_devp[i]->ch_lock);
757 mutex_init(&smd_pkt_devp[i]->rx_lock);
758 mutex_init(&smd_pkt_devp[i]->tx_lock);
759 init_completion(&smd_pkt_devp[i]->ch_allocated);
760
761 cdev_init(&smd_pkt_devp[i]->cdev, &smd_pkt_fops);
762 smd_pkt_devp[i]->cdev.owner = THIS_MODULE;
763
764 r = cdev_add(&smd_pkt_devp[i]->cdev,
765 (smd_pkt_number + i),
766 1);
767
768 if (IS_ERR_VALUE(r)) {
769 printk(KERN_ERR "%s:%i:%s: cdev_add() ret %i\n",
770 __FILE__,
771 __LINE__,
772 __func__,
773 r);
774 kfree(smd_pkt_devp[i]);
775 goto error2;
776 }
777
778 smd_pkt_devp[i]->devicep =
779 device_create(smd_pkt_classp,
780 NULL,
781 (smd_pkt_number + i),
782 NULL,
783 smd_pkt_dev_name[i]);
784
785 if (IS_ERR(smd_pkt_devp[i]->devicep)) {
786 printk(KERN_ERR "%s:%i:%s: "
787 "device_create() ENOMEM\n",
788 __FILE__,
789 __LINE__,
790 __func__);
791 r = -ENOMEM;
792 cdev_del(&smd_pkt_devp[i]->cdev);
793 kfree(smd_pkt_devp[i]);
794 goto error2;
795 }
796 if (device_create_file(smd_pkt_devp[i]->devicep,
797 &dev_attr_open_timeout))
798 pr_err("%s: unable to create device attr on #%d\n",
799 __func__, i);
800
801 smd_pkt_devp[i]->driver.probe = smd_pkt_dummy_probe;
802 smd_pkt_devp[i]->driver.driver.name = smd_ch_name[i];
803 smd_pkt_devp[i]->driver.driver.owner = THIS_MODULE;
804 r = platform_driver_register(&smd_pkt_devp[i]->driver);
805 if (r)
806 goto error2;
807 }
808
809 INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
810
811 D(KERN_INFO "SMD Packet Port Driver Initialized.\n");
812 return 0;
813
814 error2:
815 if (i > 0) {
816 while (--i >= 0) {
817 platform_driver_unregister(&smd_pkt_devp[i]->driver);
818 cdev_del(&smd_pkt_devp[i]->cdev);
819 kfree(smd_pkt_devp[i]);
820 device_destroy(smd_pkt_classp,
821 MKDEV(MAJOR(smd_pkt_number), i));
822 }
823 }
824
825 class_destroy(smd_pkt_classp);
826 error1:
827 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
828 error0:
829 return r;
830}
831
832static void __exit smd_pkt_cleanup(void)
833{
834 int i;
835
836 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
837 platform_driver_unregister(&smd_pkt_devp[i]->driver);
838 cdev_del(&smd_pkt_devp[i]->cdev);
839 kfree(smd_pkt_devp[i]);
840 device_destroy(smd_pkt_classp,
841 MKDEV(MAJOR(smd_pkt_number), i));
842 }
843
844 class_destroy(smd_pkt_classp);
845
846 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
847}
848
849module_init(smd_pkt_init);
850module_exit(smd_pkt_cleanup);
851
852MODULE_DESCRIPTION("MSM Shared Memory Packet Port");
853MODULE_LICENSE("GPL v2");