blob: f5f76f7d5f4e61ad103f6ed324d740d8adffdd2a [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
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -060043#define NUM_SMD_PKT_PORTS 15
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044#endif
45
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -060046#define PDRIVER_NAME_MAX_SIZE 32
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070047#define LOOPBACK_INX (NUM_SMD_PKT_PORTS - 1)
48
49#define DEVICE_NAME "smdpkt"
Eric Holmbergc3c5cd92012-02-07 18:19:49 -070050#define WAKELOCK_TIMEOUT (2*HZ)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051
52struct smd_pkt_dev {
53 struct cdev cdev;
54 struct device *devicep;
55 void *pil;
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -060056 char pdriver_name[PDRIVER_NAME_MAX_SIZE];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070057 struct platform_driver driver;
58
59 struct smd_channel *ch;
60 struct mutex ch_lock;
61 struct mutex rx_lock;
62 struct mutex tx_lock;
63 wait_queue_head_t ch_read_wait_queue;
64 wait_queue_head_t ch_write_wait_queue;
65 wait_queue_head_t ch_opened_wait_queue;
66
67 int i;
68
69 int blocking_write;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070070 int is_open;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -070071 int poll_mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072 unsigned ch_size;
73 uint open_modem_wait;
74
75 int has_reset;
76 int do_reset_notification;
77 struct completion ch_allocated;
Eric Holmbergc3c5cd92012-02-07 18:19:49 -070078 struct wake_lock pa_wake_lock; /* Packet Arrival Wake lock*/
79 struct work_struct packet_arrival_work;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -070080 struct spinlock pa_spinlock;
Jeff Hugo93476a32012-06-01 14:32:49 -060081 int wakelock_locked;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082} *smd_pkt_devp[NUM_SMD_PKT_PORTS];
83
84struct class *smd_pkt_classp;
85static dev_t smd_pkt_number;
86static struct delayed_work loopback_work;
87static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp);
88static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp);
89static uint32_t is_modem_smsm_inited(void);
90
91static int msm_smd_pkt_debug_mask;
92module_param_named(debug_mask, msm_smd_pkt_debug_mask,
93 int, S_IRUGO | S_IWUSR | S_IWGRP);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -060094
95enum {
96 SMD_PKT_STATUS = 1U << 0,
97 SMD_PKT_READ = 1U << 1,
98 SMD_PKT_WRITE = 1U << 2,
99 SMD_PKT_READ_DUMP_BUFFER = 1U << 3,
100 SMD_PKT_WRITE_DUMP_BUFFER = 1U << 4,
101 SMD_PKT_POLL = 1U << 5,
102};
103
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700104#define DEBUG
105
106#ifdef DEBUG
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600107#define D_STATUS(x...) \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700108do { \
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600109 if (msm_smd_pkt_debug_mask & SMD_PKT_STATUS) \
110 pr_info("Status: "x); \
111} while (0)
112
113#define D_READ(x...) \
114do { \
115 if (msm_smd_pkt_debug_mask & SMD_PKT_READ) \
116 pr_info("Read: "x); \
117} while (0)
118
119#define D_WRITE(x...) \
120do { \
121 if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE) \
122 pr_info("Write: "x); \
123} while (0)
124
125#define D_READ_DUMP_BUFFER(prestr, cnt, buf) \
126do { \
127 if (msm_smd_pkt_debug_mask & SMD_PKT_READ_DUMP_BUFFER) \
128 print_hex_dump(KERN_INFO, prestr, \
129 DUMP_PREFIX_NONE, 16, 1, \
130 buf, cnt, 1); \
131} while (0)
132
133#define D_WRITE_DUMP_BUFFER(prestr, cnt, buf) \
134do { \
135 if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE_DUMP_BUFFER) \
136 print_hex_dump(KERN_INFO, prestr, \
137 DUMP_PREFIX_NONE, 16, 1, \
138 buf, cnt, 1); \
139} while (0)
140
141#define D_POLL(x...) \
142do { \
143 if (msm_smd_pkt_debug_mask & SMD_PKT_POLL) \
144 pr_info("Poll: "x); \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700145} while (0)
146#else
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600147#define D_STATUS(x...) do {} while (0)
148#define D_READ(x...) do {} while (0)
149#define D_WRITE(x...) do {} while (0)
150#define D_READ_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
151#define D_WRITE_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
152#define D_POLL(x...) do {} while (0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700153#endif
154
155static ssize_t open_timeout_store(struct device *d,
156 struct device_attribute *attr,
157 const char *buf,
158 size_t n)
159{
160 int i;
161 unsigned long tmp;
162 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
163 if (smd_pkt_devp[i]->devicep == d)
164 break;
165 }
Jeff Hugo31f83b42012-01-25 15:15:26 -0700166 if (i >= NUM_SMD_PKT_PORTS) {
167 pr_err("%s: unable to match device to valid smd_pkt port\n",
168 __func__);
169 return -EINVAL;
170 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171 if (!strict_strtoul(buf, 10, &tmp)) {
172 smd_pkt_devp[i]->open_modem_wait = tmp;
173 return n;
174 } else {
175 pr_err("%s: unable to convert: %s to an int\n", __func__,
176 buf);
177 return -EINVAL;
178 }
179}
180
181static ssize_t open_timeout_show(struct device *d,
182 struct device_attribute *attr,
183 char *buf)
184{
185 int i;
186 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
187 if (smd_pkt_devp[i]->devicep == d)
188 break;
189 }
Jeff Hugo31f83b42012-01-25 15:15:26 -0700190 if (i >= NUM_SMD_PKT_PORTS) {
191 pr_err("%s: unable to match device to valid smd_pkt port\n",
192 __func__);
193 return -EINVAL;
194 }
Karthikeyan Ramasubramanian63fa3d32011-09-29 17:06:26 -0600195 return snprintf(buf, PAGE_SIZE, "%d\n",
196 smd_pkt_devp[i]->open_modem_wait);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197}
198
199static DEVICE_ATTR(open_timeout, 0664, open_timeout_show, open_timeout_store);
200
201static int notify_reset(struct smd_pkt_dev *smd_pkt_devp)
202{
203 smd_pkt_devp->do_reset_notification = 0;
204
205 return -ENETRESET;
206}
207
208static void clean_and_signal(struct smd_pkt_dev *smd_pkt_devp)
209{
210 smd_pkt_devp->do_reset_notification = 1;
211 smd_pkt_devp->has_reset = 1;
212
213 smd_pkt_devp->is_open = 0;
214
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700215 wake_up(&smd_pkt_devp->ch_read_wait_queue);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700216 wake_up(&smd_pkt_devp->ch_write_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700217 wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600218 D_STATUS("%s smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219}
220
221static void loopback_probe_worker(struct work_struct *work)
222{
223
224 /* Wait for the modem SMSM to be inited for the SMD
225 ** Loopback channel to be allocated at the modem. Since
226 ** the wait need to be done atmost once, using msleep
227 ** doesn't degrade the performance. */
228 if (!is_modem_smsm_inited())
229 schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000));
230 else
231 smsm_change_state(SMSM_APPS_STATE,
232 0, SMSM_SMD_LOOPBACK);
233
234}
235
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700236static void packet_arrival_worker(struct work_struct *work)
237{
238 struct smd_pkt_dev *smd_pkt_devp;
Jeff Hugo93476a32012-06-01 14:32:49 -0600239 unsigned long flags;
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700240
241 smd_pkt_devp = container_of(work, struct smd_pkt_dev,
242 packet_arrival_work);
243 mutex_lock(&smd_pkt_devp->ch_lock);
Jeff Hugo93476a32012-06-01 14:32:49 -0600244 spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
245 if (smd_pkt_devp->ch && smd_pkt_devp->wakelock_locked) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600246 D_READ("%s locking smd_pkt_dev id:%d wakelock\n",
247 __func__, smd_pkt_devp->i);
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700248 wake_lock_timeout(&smd_pkt_devp->pa_wake_lock,
249 WAKELOCK_TIMEOUT);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600250 }
Jeff Hugo93476a32012-06-01 14:32:49 -0600251 spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700252 mutex_unlock(&smd_pkt_devp->ch_lock);
253}
254
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255static long smd_pkt_ioctl(struct file *file, unsigned int cmd,
256 unsigned long arg)
257{
258 int ret;
259 struct smd_pkt_dev *smd_pkt_devp;
260
261 smd_pkt_devp = file->private_data;
262 if (!smd_pkt_devp)
263 return -EINVAL;
264
265 switch (cmd) {
266 case TIOCMGET:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600267 D_STATUS("%s TIOCMGET command on smd_pkt_dev id:%d\n",
268 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700269 ret = smd_tiocmget(smd_pkt_devp->ch);
270 break;
271 case TIOCMSET:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600272 D_STATUS("%s TIOCSET command on smd_pkt_dev id:%d\n",
273 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700274 ret = smd_tiocmset(smd_pkt_devp->ch, arg, ~arg);
275 break;
276 case SMD_PKT_IOCTL_BLOCKING_WRITE:
277 ret = get_user(smd_pkt_devp->blocking_write, (int *)arg);
278 break;
279 default:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600280 pr_err("%s: Unrecognized ioctl command %d\n", __func__, cmd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700281 ret = -1;
282 }
283
284 return ret;
285}
286
287ssize_t smd_pkt_read(struct file *file,
288 char __user *buf,
289 size_t count,
290 loff_t *ppos)
291{
292 int r;
293 int bytes_read;
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700294 int pkt_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700295 struct smd_pkt_dev *smd_pkt_devp;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700296 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700298 smd_pkt_devp = file->private_data;
299
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600300 if (!smd_pkt_devp) {
301 pr_err("%s on NULL smd_pkt_dev\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700302 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600303 }
304
305 if (!smd_pkt_devp->ch) {
306 pr_err("%s on a closed smd_pkt_dev id:%d\n",
307 __func__, smd_pkt_devp->i);
308 return -EINVAL;
309 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700310
311 if (smd_pkt_devp->do_reset_notification) {
312 /* notify client that a reset occurred */
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600313 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
314 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700315 return notify_reset(smd_pkt_devp);
316 }
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600317 D_READ("Begin %s on smd_pkt_dev id:%d buffer_size %d\n",
318 __func__, smd_pkt_devp->i, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700319
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700320wait_for_packet:
321 r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue,
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600322 !smd_pkt_devp->ch ||
323 (smd_cur_packet_size(smd_pkt_devp->ch) > 0
324 && smd_read_avail(smd_pkt_devp->ch)) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700325 smd_pkt_devp->has_reset);
326
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600327 mutex_lock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600328 if (smd_pkt_devp->has_reset) {
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600329 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600330 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
331 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700332 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600333 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700334
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600335 if (!smd_pkt_devp->ch) {
336 mutex_unlock(&smd_pkt_devp->rx_lock);
337 pr_err("%s on a closed smd_pkt_dev id:%d\n",
338 __func__, smd_pkt_devp->i);
339 return -EINVAL;
340 }
341
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700342 if (r < 0) {
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600343 mutex_unlock(&smd_pkt_devp->rx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700344 /* qualify error message */
345 if (r != -ERESTARTSYS) {
346 /* we get this anytime a signal comes in */
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600347 pr_err("%s: wait_event_interruptible on smd_pkt_dev"
348 " id:%d ret %i\n",
349 __func__, smd_pkt_devp->i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700350 }
351 return r;
352 }
353
354 /* Here we have a whole packet waiting for us */
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700355 pkt_size = smd_cur_packet_size(smd_pkt_devp->ch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700356
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700357 if (!pkt_size) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600358 pr_err("%s: No data on smd_pkt_dev id:%d, False wakeup\n",
359 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700360 mutex_unlock(&smd_pkt_devp->rx_lock);
361 goto wait_for_packet;
362 }
363
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700364 if (pkt_size > count) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600365 pr_err("%s: failure on smd_pkt_dev id: %d - packet size %d"
366 " > buffer size %d,", __func__, smd_pkt_devp->i,
367 pkt_size, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700368 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700369 return -ETOOSMALL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700370 }
371
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700372 bytes_read = 0;
373 do {
374 r = smd_read_user_buffer(smd_pkt_devp->ch,
375 (buf + bytes_read),
376 (pkt_size - bytes_read));
377 if (r < 0) {
378 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600379 if (smd_pkt_devp->has_reset) {
380 pr_err("%s notifying reset for smd_pkt_dev"
381 " id:%d\n", __func__, smd_pkt_devp->i);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700382 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600383 }
384 pr_err("%s Error while reading %d\n", __func__, r);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700385 return r;
386 }
387 bytes_read += r;
388 if (pkt_size != bytes_read)
389 wait_event(smd_pkt_devp->ch_read_wait_queue,
390 smd_read_avail(smd_pkt_devp->ch) ||
391 smd_pkt_devp->has_reset);
392 if (smd_pkt_devp->has_reset) {
393 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600394 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
395 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700396 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700397 }
398 } while (pkt_size != bytes_read);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600399 D_READ_DUMP_BUFFER("Read: ", (bytes_read > 16 ? 16 : bytes_read), buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700400 mutex_unlock(&smd_pkt_devp->rx_lock);
401
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700402 mutex_lock(&smd_pkt_devp->ch_lock);
403 spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
404 if (smd_pkt_devp->poll_mode &&
405 !smd_cur_packet_size(smd_pkt_devp->ch)) {
406 wake_unlock(&smd_pkt_devp->pa_wake_lock);
Jeff Hugo93476a32012-06-01 14:32:49 -0600407 smd_pkt_devp->wakelock_locked = 0;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700408 smd_pkt_devp->poll_mode = 0;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600409 D_READ("%s unlocked smd_pkt_dev id:%d wakelock\n",
410 __func__, smd_pkt_devp->i);
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700411 }
412 spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
413 mutex_unlock(&smd_pkt_devp->ch_lock);
414
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600415 D_READ("Finished %s on smd_pkt_dev id:%d %d bytes\n",
416 __func__, smd_pkt_devp->i, bytes_read);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700417
418 /* check and wakeup read threads waiting on this device */
419 check_and_wakeup_reader(smd_pkt_devp);
420
421 return bytes_read;
422}
423
424ssize_t smd_pkt_write(struct file *file,
425 const char __user *buf,
426 size_t count,
427 loff_t *ppos)
428{
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700429 int r = 0, bytes_written;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700430 struct smd_pkt_dev *smd_pkt_devp;
431 DEFINE_WAIT(write_wait);
432
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700433 smd_pkt_devp = file->private_data;
434
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600435 if (!smd_pkt_devp) {
436 pr_err("%s on NULL smd_pkt_dev\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600438 }
439
440 if (!smd_pkt_devp->ch) {
441 pr_err("%s on a closed smd_pkt_dev id:%d\n",
442 __func__, smd_pkt_devp->i);
443 return -EINVAL;
444 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700445
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600446 if (smd_pkt_devp->do_reset_notification || smd_pkt_devp->has_reset) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600447 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
448 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449 /* notify client that a reset occurred */
450 return notify_reset(smd_pkt_devp);
451 }
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600452 D_WRITE("Begin %s on smd_pkt_dev id:%d data_size %d\n",
453 __func__, smd_pkt_devp->i, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700454
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700455 mutex_lock(&smd_pkt_devp->tx_lock);
456 if (!smd_pkt_devp->blocking_write) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700457 if (smd_write_avail(smd_pkt_devp->ch) < count) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600458 pr_err("%s: Not enough space in smd_pkt_dev id:%d\n",
459 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700460 mutex_unlock(&smd_pkt_devp->tx_lock);
461 return -ENOMEM;
462 }
463 }
464
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700465 r = smd_write_start(smd_pkt_devp->ch, count);
466 if (r < 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700467 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600468 pr_err("%s: Error:%d in smd_pkt_dev id:%d @ smd_write_start\n",
469 __func__, r, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700470 return r;
471 }
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700472
473 bytes_written = 0;
474 do {
475 prepare_to_wait(&smd_pkt_devp->ch_write_wait_queue,
476 &write_wait, TASK_UNINTERRUPTIBLE);
477 if (!smd_write_avail(smd_pkt_devp->ch) &&
478 !smd_pkt_devp->has_reset) {
479 smd_enable_read_intr(smd_pkt_devp->ch);
480 schedule();
481 }
482 finish_wait(&smd_pkt_devp->ch_write_wait_queue, &write_wait);
483 smd_disable_read_intr(smd_pkt_devp->ch);
484
485 if (smd_pkt_devp->has_reset) {
486 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600487 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
488 __func__, smd_pkt_devp->i);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700489 return notify_reset(smd_pkt_devp);
490 } else {
491 r = smd_write_segment(smd_pkt_devp->ch,
492 (void *)(buf + bytes_written),
493 (count - bytes_written), 1);
494 if (r < 0) {
495 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600496 if (smd_pkt_devp->has_reset) {
497 pr_err("%s notifying reset for"
498 " smd_pkt_dev id:%d\n",
499 __func__, smd_pkt_devp->i);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700500 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600501 }
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600502 pr_err("%s on smd_pkt_dev id:%d failed r:%d\n",
503 __func__, smd_pkt_devp->i, r);
504 return r;
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700505 }
506 bytes_written += r;
507 }
508 } while (bytes_written != count);
509 smd_write_end(smd_pkt_devp->ch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700510 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600511 D_WRITE_DUMP_BUFFER("Write: ",
512 (bytes_written > 16 ? 16 : bytes_written), buf);
513 D_WRITE("Finished %s on smd_pkt_dev id:%d %d bytes\n",
514 __func__, smd_pkt_devp->i, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700515
516 return count;
517}
518
519static unsigned int smd_pkt_poll(struct file *file, poll_table *wait)
520{
521 struct smd_pkt_dev *smd_pkt_devp;
522 unsigned int mask = 0;
523
524 smd_pkt_devp = file->private_data;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600525 if (!smd_pkt_devp) {
526 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700527 return POLLERR;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600528 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700529
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700530 smd_pkt_devp->poll_mode = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700531 poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait);
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600532 mutex_lock(&smd_pkt_devp->ch_lock);
533 if (smd_pkt_devp->has_reset || !smd_pkt_devp->ch) {
534 mutex_unlock(&smd_pkt_devp->ch_lock);
535 pr_err("%s notifying reset for smd_pkt_dev id:%d\n",
536 __func__, smd_pkt_devp->i);
537 return POLLERR;
538 }
539
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600540 if (smd_read_avail(smd_pkt_devp->ch)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541 mask |= POLLIN | POLLRDNORM;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600542 D_POLL("%s sets POLLIN for smd_pkt_dev id: %d\n",
543 __func__, smd_pkt_devp->i);
544 }
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600545 mutex_unlock(&smd_pkt_devp->ch_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700546
547 return mask;
548}
549
550static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp)
551{
552 int sz;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700553 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700554
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600555 if (!smd_pkt_devp) {
556 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700557 return;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600558 }
559
560 if (!smd_pkt_devp->ch) {
561 pr_err("%s on a closed smd_pkt_dev id:%d\n",
562 __func__, smd_pkt_devp->i);
563 return;
564 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700565
566 sz = smd_cur_packet_size(smd_pkt_devp->ch);
567 if (sz == 0) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600568 D_READ("%s: No packet in smd_pkt_dev id:%d\n",
569 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700570 return;
571 }
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700572 if (!smd_read_avail(smd_pkt_devp->ch)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600573 D_READ("%s: packet size is %d in smd_pkt_dev id:%d -"
574 " but the data isn't here\n",
575 __func__, sz, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700576 return;
577 }
578
579 /* here we have a packet of size sz ready */
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700580 spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
581 wake_lock(&smd_pkt_devp->pa_wake_lock);
Jeff Hugo93476a32012-06-01 14:32:49 -0600582 smd_pkt_devp->wakelock_locked = 1;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700583 spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
Jeff Hugo93476a32012-06-01 14:32:49 -0600584 wake_up(&smd_pkt_devp->ch_read_wait_queue);
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700585 schedule_work(&smd_pkt_devp->packet_arrival_work);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600586 D_READ("%s: wake_up smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700587}
588
589static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp)
590{
591 int sz;
592
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600593 if (!smd_pkt_devp) {
594 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700595 return;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600596 }
597
598 if (!smd_pkt_devp->ch) {
599 pr_err("%s on a closed smd_pkt_dev id:%d\n",
600 __func__, smd_pkt_devp->i);
601 return;
602 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700603
604 sz = smd_write_avail(smd_pkt_devp->ch);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700605 if (sz) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600606 D_WRITE("%s: %d bytes write space in smd_pkt_dev id:%d\n",
607 __func__, sz, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700608 smd_disable_read_intr(smd_pkt_devp->ch);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700609 wake_up(&smd_pkt_devp->ch_write_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700610 }
611}
612
613static void ch_notify(void *priv, unsigned event)
614{
615 struct smd_pkt_dev *smd_pkt_devp = priv;
616
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600617 if (smd_pkt_devp->ch == 0) {
618 pr_err("%s on a closed smd_pkt_dev id:%d\n",
619 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700620 return;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600621 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700622
623 switch (event) {
624 case SMD_EVENT_DATA: {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600625 D_STATUS("%s: DATA event in smd_pkt_dev id:%d\n",
626 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700627 check_and_wakeup_reader(smd_pkt_devp);
628 if (smd_pkt_devp->blocking_write)
629 check_and_wakeup_writer(smd_pkt_devp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700630 break;
631 }
632 case SMD_EVENT_OPEN:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600633 D_STATUS("%s: OPEN event in smd_pkt_dev id:%d\n",
634 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700635 smd_pkt_devp->has_reset = 0;
636 smd_pkt_devp->is_open = 1;
637 wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
638 break;
639 case SMD_EVENT_CLOSE:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600640 D_STATUS("%s: CLOSE event in smd_pkt_dev id:%d\n",
641 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700642 smd_pkt_devp->is_open = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700643 /* put port into reset state */
644 clean_and_signal(smd_pkt_devp);
645 if (smd_pkt_devp->i == LOOPBACK_INX)
646 schedule_delayed_work(&loopback_work,
647 msecs_to_jiffies(1000));
648 break;
649 }
650}
651
652#ifdef CONFIG_ARCH_FSM9XXX
653static char *smd_pkt_dev_name[] = {
654 "smdcntl1",
655 "smdcntl2",
656 "smd22",
657 "smd_pkt_loopback",
658};
659
660static char *smd_ch_name[] = {
661 "DATA6_CNTL",
662 "DATA7_CNTL",
663 "DATA22",
664 "LOOPBACK",
665};
666
667static uint32_t smd_ch_edge[] = {
668 SMD_APPS_QDSP,
669 SMD_APPS_QDSP,
670 SMD_APPS_QDSP,
671 SMD_APPS_QDSP
672};
673#else
674static char *smd_pkt_dev_name[] = {
675 "smdcntl0",
676 "smdcntl1",
677 "smdcntl2",
678 "smdcntl3",
679 "smdcntl4",
680 "smdcntl5",
681 "smdcntl6",
682 "smdcntl7",
683 "smd22",
684 "smd_sns_dsps",
Angshuman Sarkara18a2722011-07-29 13:41:13 +0530685 "apr_apps2",
Karthikeyan Ramasubramaniand08e59a2012-03-01 17:35:34 -0700686 "smdcntl8",
Jeff Hugodf5ee322012-04-16 11:56:12 -0600687 "smd_sns_adsp",
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -0600688 "smd_cxm_qmi",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700689 "smd_pkt_loopback",
690};
691
692static char *smd_ch_name[] = {
693 "DATA5_CNTL",
694 "DATA6_CNTL",
695 "DATA7_CNTL",
696 "DATA8_CNTL",
697 "DATA9_CNTL",
698 "DATA12_CNTL",
699 "DATA13_CNTL",
700 "DATA14_CNTL",
701 "DATA22",
702 "SENSOR",
Angshuman Sarkara18a2722011-07-29 13:41:13 +0530703 "apr_apps2",
Karthikeyan Ramasubramaniand08e59a2012-03-01 17:35:34 -0700704 "DATA40_CNTL",
Jeff Hugodf5ee322012-04-16 11:56:12 -0600705 "SENSOR",
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -0600706 "CXM_QMI_PORT_8064",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700707 "LOOPBACK",
708};
709
710static uint32_t smd_ch_edge[] = {
711 SMD_APPS_MODEM,
712 SMD_APPS_MODEM,
713 SMD_APPS_MODEM,
714 SMD_APPS_MODEM,
715 SMD_APPS_MODEM,
716 SMD_APPS_MODEM,
717 SMD_APPS_MODEM,
718 SMD_APPS_MODEM,
719 SMD_APPS_MODEM,
720 SMD_APPS_DSPS,
721 SMD_APPS_QDSP,
722 SMD_APPS_MODEM,
Jeff Hugodf5ee322012-04-16 11:56:12 -0600723 SMD_APPS_QDSP,
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -0600724 SMD_APPS_WCNSS,
Karthikeyan Ramasubramaniand08e59a2012-03-01 17:35:34 -0700725 SMD_APPS_MODEM,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700726};
727#endif
728
729static int smd_pkt_dummy_probe(struct platform_device *pdev)
730{
731 int i;
732
733 for (i = 0; i < NUM_SMD_PKT_PORTS; i++) {
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600734 if (smd_ch_edge[i] == pdev->id
735 && !strncmp(pdev->name, smd_ch_name[i],
736 SMD_MAX_CH_NAME_LEN)
737 && smd_pkt_devp[i]->driver.probe) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700738 complete_all(&smd_pkt_devp[i]->ch_allocated);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600739 D_STATUS("%s allocated SMD ch for smd_pkt_dev id:%d\n",
740 __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700741 break;
742 }
743 }
744 return 0;
745}
746
747static uint32_t is_modem_smsm_inited(void)
748{
749 uint32_t modem_state;
750 uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT);
751
752 modem_state = smsm_get_state(SMSM_MODEM_STATE);
753 return (modem_state & ready_state) == ready_state;
754}
755
756int smd_pkt_open(struct inode *inode, struct file *file)
757{
758 int r = 0;
759 struct smd_pkt_dev *smd_pkt_devp;
Eric Holmbergcce6daf2012-02-27 14:34:02 -0700760 const char *peripheral = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700761
762 smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev);
763
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600764 if (!smd_pkt_devp) {
765 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700766 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600767 }
768 D_STATUS("Begin %s on smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700769
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700770 wake_lock_init(&smd_pkt_devp->pa_wake_lock, WAKE_LOCK_SUSPEND,
771 smd_pkt_dev_name[smd_pkt_devp->i]);
772 INIT_WORK(&smd_pkt_devp->packet_arrival_work, packet_arrival_worker);
773
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700774 file->private_data = smd_pkt_devp;
775
776 mutex_lock(&smd_pkt_devp->ch_lock);
777 if (smd_pkt_devp->ch == 0) {
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700778 init_completion(&smd_pkt_devp->ch_allocated);
779 smd_pkt_devp->driver.probe = smd_pkt_dummy_probe;
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600780 scnprintf(smd_pkt_devp->pdriver_name, PDRIVER_NAME_MAX_SIZE,
Karthikeyan Ramasubramanian61df1932012-06-28 12:10:41 -0600781 "%s", smd_ch_name[smd_pkt_devp->i]);
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600782 smd_pkt_devp->driver.driver.name = smd_pkt_devp->pdriver_name;
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700783 smd_pkt_devp->driver.driver.owner = THIS_MODULE;
784 r = platform_driver_register(&smd_pkt_devp->driver);
785 if (r) {
786 pr_err("%s: %s Platform driver reg. failed\n",
787 __func__, smd_ch_name[smd_pkt_devp->i]);
788 goto out;
789 }
790
Eric Holmbergcce6daf2012-02-27 14:34:02 -0700791 peripheral = smd_edge_to_subsystem(
792 smd_ch_edge[smd_pkt_devp->i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700793 if (peripheral) {
794 smd_pkt_devp->pil = pil_get(peripheral);
795 if (IS_ERR(smd_pkt_devp->pil)) {
796 r = PTR_ERR(smd_pkt_devp->pil);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600797 pr_err("%s failed on smd_pkt_dev id:%d -"
798 " pil_get failed for %s\n", __func__,
799 smd_pkt_devp->i, peripheral);
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700800 goto release_pd;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700801 }
802
803 /* Wait for the modem SMSM to be inited for the SMD
804 ** Loopback channel to be allocated at the modem. Since
805 ** the wait need to be done atmost once, using msleep
806 ** doesn't degrade the performance. */
Jeff Hugoa5ee4362011-07-15 13:48:48 -0600807 if (!strncmp(smd_ch_name[smd_pkt_devp->i], "LOOPBACK",
808 SMD_MAX_CH_NAME_LEN)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700809 if (!is_modem_smsm_inited())
810 msleep(5000);
811 smsm_change_state(SMSM_APPS_STATE,
812 0, SMSM_SMD_LOOPBACK);
813 msleep(100);
814 }
815
816 /*
817 * Wait for a packet channel to be allocated so we know
818 * the modem is ready enough.
819 */
820 if (smd_pkt_devp->open_modem_wait) {
821 r = wait_for_completion_interruptible_timeout(
822 &smd_pkt_devp->ch_allocated,
823 msecs_to_jiffies(
824 smd_pkt_devp->open_modem_wait
825 * 1000));
826 if (r == 0)
827 r = -ETIMEDOUT;
828 if (r < 0) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600829 pr_err("%s: wait on smd_pkt_dev id:%d"
830 " allocation failed rc:%d\n",
831 __func__, smd_pkt_devp->i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700832 goto release_pil;
833 }
834 }
835 }
836
837 r = smd_named_open_on_edge(smd_ch_name[smd_pkt_devp->i],
838 smd_ch_edge[smd_pkt_devp->i],
839 &smd_pkt_devp->ch,
840 smd_pkt_devp,
841 ch_notify);
842 if (r < 0) {
843 pr_err("%s: %s open failed %d\n", __func__,
844 smd_ch_name[smd_pkt_devp->i], r);
845 goto release_pil;
846 }
847
848 r = wait_event_interruptible_timeout(
849 smd_pkt_devp->ch_opened_wait_queue,
850 smd_pkt_devp->is_open, (2 * HZ));
Jeff Hugo4d9fdf32012-04-30 11:10:54 -0600851 if (r == 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700852 r = -ETIMEDOUT;
Jeff Hugo4d9fdf32012-04-30 11:10:54 -0600853 /* close the ch to sync smd's state with smd_pkt */
854 smd_close(smd_pkt_devp->ch);
855 smd_pkt_devp->ch = NULL;
856 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700857
858 if (r < 0) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600859 pr_err("%s: wait on smd_pkt_dev id:%d OPEN event failed"
860 " rc:%d\n", __func__, smd_pkt_devp->i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700861 } else if (!smd_pkt_devp->is_open) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600862 pr_err("%s: Invalid OPEN event on smd_pkt_dev id:%d\n",
863 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700864 r = -ENODEV;
865 } else {
866 smd_disable_read_intr(smd_pkt_devp->ch);
867 smd_pkt_devp->ch_size =
868 smd_write_avail(smd_pkt_devp->ch);
869 r = 0;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600870 D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
871 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700872 }
873 }
874release_pil:
875 if (peripheral && (r < 0))
876 pil_put(smd_pkt_devp->pil);
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700877
878release_pd:
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600879 if (r < 0) {
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700880 platform_driver_unregister(&smd_pkt_devp->driver);
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600881 smd_pkt_devp->driver.probe = NULL;
882 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700883out:
884 mutex_unlock(&smd_pkt_devp->ch_lock);
885
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700886 if (r < 0)
887 wake_lock_destroy(&smd_pkt_devp->pa_wake_lock);
888
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700889 return r;
890}
891
892int smd_pkt_release(struct inode *inode, struct file *file)
893{
894 int r = 0;
895 struct smd_pkt_dev *smd_pkt_devp = file->private_data;
896
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600897 if (!smd_pkt_devp) {
898 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700899 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600900 }
901 D_STATUS("Begin %s on smd_pkt_dev id:%d\n",
902 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700903
904 clean_and_signal(smd_pkt_devp);
905
906 mutex_lock(&smd_pkt_devp->ch_lock);
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600907 mutex_lock(&smd_pkt_devp->rx_lock);
908 mutex_lock(&smd_pkt_devp->tx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700909 if (smd_pkt_devp->ch != 0) {
910 r = smd_close(smd_pkt_devp->ch);
911 smd_pkt_devp->ch = 0;
912 smd_pkt_devp->blocking_write = 0;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700913 smd_pkt_devp->poll_mode = 0;
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700914 platform_driver_unregister(&smd_pkt_devp->driver);
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600915 smd_pkt_devp->driver.probe = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700916 if (smd_pkt_devp->pil)
917 pil_put(smd_pkt_devp->pil);
918 }
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600919 mutex_unlock(&smd_pkt_devp->tx_lock);
920 mutex_unlock(&smd_pkt_devp->rx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700921 mutex_unlock(&smd_pkt_devp->ch_lock);
922
923 smd_pkt_devp->has_reset = 0;
924 smd_pkt_devp->do_reset_notification = 0;
Jeff Hugo93476a32012-06-01 14:32:49 -0600925 smd_pkt_devp->wakelock_locked = 0;
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700926 wake_lock_destroy(&smd_pkt_devp->pa_wake_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600927 D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
928 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700929
930 return r;
931}
932
933static const struct file_operations smd_pkt_fops = {
934 .owner = THIS_MODULE,
935 .open = smd_pkt_open,
936 .release = smd_pkt_release,
937 .read = smd_pkt_read,
938 .write = smd_pkt_write,
939 .poll = smd_pkt_poll,
940 .unlocked_ioctl = smd_pkt_ioctl,
941};
942
943static int __init smd_pkt_init(void)
944{
945 int i;
946 int r;
947
948 r = alloc_chrdev_region(&smd_pkt_number,
949 0,
950 NUM_SMD_PKT_PORTS,
951 DEVICE_NAME);
952 if (IS_ERR_VALUE(r)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600953 pr_err("%s: alloc_chrdev_region() failed ret:%i\n",
954 __func__, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700955 goto error0;
956 }
957
958 smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME);
959 if (IS_ERR(smd_pkt_classp)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600960 pr_err("%s: class_create() failed ENOMEM\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700961 r = -ENOMEM;
962 goto error1;
963 }
964
965 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
966 smd_pkt_devp[i] = kzalloc(sizeof(struct smd_pkt_dev),
967 GFP_KERNEL);
968 if (IS_ERR(smd_pkt_devp[i])) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600969 pr_err("%s: kzalloc() failed for smd_pkt_dev id:%d\n",
970 __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700971 r = -ENOMEM;
972 goto error2;
973 }
974
975 smd_pkt_devp[i]->i = i;
976
977 init_waitqueue_head(&smd_pkt_devp[i]->ch_read_wait_queue);
978 init_waitqueue_head(&smd_pkt_devp[i]->ch_write_wait_queue);
979 smd_pkt_devp[i]->is_open = 0;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700980 smd_pkt_devp[i]->poll_mode = 0;
Jeff Hugo93476a32012-06-01 14:32:49 -0600981 smd_pkt_devp[i]->wakelock_locked = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700982 init_waitqueue_head(&smd_pkt_devp[i]->ch_opened_wait_queue);
983
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700984 spin_lock_init(&smd_pkt_devp[i]->pa_spinlock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700985 mutex_init(&smd_pkt_devp[i]->ch_lock);
986 mutex_init(&smd_pkt_devp[i]->rx_lock);
987 mutex_init(&smd_pkt_devp[i]->tx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700988
989 cdev_init(&smd_pkt_devp[i]->cdev, &smd_pkt_fops);
990 smd_pkt_devp[i]->cdev.owner = THIS_MODULE;
991
992 r = cdev_add(&smd_pkt_devp[i]->cdev,
993 (smd_pkt_number + i),
994 1);
995
996 if (IS_ERR_VALUE(r)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600997 pr_err("%s: cdev_add() failed for smd_pkt_dev id:%d"
998 " ret:%i\n", __func__, i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700999 kfree(smd_pkt_devp[i]);
1000 goto error2;
1001 }
1002
1003 smd_pkt_devp[i]->devicep =
1004 device_create(smd_pkt_classp,
1005 NULL,
1006 (smd_pkt_number + i),
1007 NULL,
1008 smd_pkt_dev_name[i]);
1009
1010 if (IS_ERR(smd_pkt_devp[i]->devicep)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001011 pr_err("%s: device_create() failed for smd_pkt_dev"
1012 " id:%d\n", __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001013 r = -ENOMEM;
1014 cdev_del(&smd_pkt_devp[i]->cdev);
1015 kfree(smd_pkt_devp[i]);
1016 goto error2;
1017 }
1018 if (device_create_file(smd_pkt_devp[i]->devicep,
1019 &dev_attr_open_timeout))
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001020 pr_err("%s: unable to create device attr for"
1021 " smd_pkt_dev id:%d\n", __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001022 }
1023
1024 INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
1025
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001026 D_STATUS("SMD Packet Port Driver Initialized.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001027 return 0;
1028
1029 error2:
1030 if (i > 0) {
1031 while (--i >= 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001032 cdev_del(&smd_pkt_devp[i]->cdev);
1033 kfree(smd_pkt_devp[i]);
1034 device_destroy(smd_pkt_classp,
1035 MKDEV(MAJOR(smd_pkt_number), i));
1036 }
1037 }
1038
1039 class_destroy(smd_pkt_classp);
1040 error1:
1041 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
1042 error0:
1043 return r;
1044}
1045
1046static void __exit smd_pkt_cleanup(void)
1047{
1048 int i;
1049
1050 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001051 cdev_del(&smd_pkt_devp[i]->cdev);
1052 kfree(smd_pkt_devp[i]);
1053 device_destroy(smd_pkt_classp,
1054 MKDEV(MAJOR(smd_pkt_number), i));
1055 }
1056
1057 class_destroy(smd_pkt_classp);
1058
1059 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
1060}
1061
1062module_init(smd_pkt_init);
1063module_exit(smd_pkt_cleanup);
1064
1065MODULE_DESCRIPTION("MSM Shared Memory Packet Port");
1066MODULE_LICENSE("GPL v2");