blob: 30acebfd9d2e11f0ec1412e39fde4653f2926871 [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;
Karthikeyan Ramasubramanian540dbf32012-09-14 13:16:50 -060068 int ref_cnt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069
70 int blocking_write;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071 int is_open;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -070072 int poll_mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070073 unsigned ch_size;
74 uint open_modem_wait;
75
76 int has_reset;
77 int do_reset_notification;
78 struct completion ch_allocated;
Eric Holmbergc3c5cd92012-02-07 18:19:49 -070079 struct wake_lock pa_wake_lock; /* Packet Arrival Wake lock*/
80 struct work_struct packet_arrival_work;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -070081 struct spinlock pa_spinlock;
Jeff Hugo93476a32012-06-01 14:32:49 -060082 int wakelock_locked;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070083} *smd_pkt_devp[NUM_SMD_PKT_PORTS];
84
85struct class *smd_pkt_classp;
86static dev_t smd_pkt_number;
87static struct delayed_work loopback_work;
88static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp);
89static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp);
90static uint32_t is_modem_smsm_inited(void);
91
92static int msm_smd_pkt_debug_mask;
93module_param_named(debug_mask, msm_smd_pkt_debug_mask,
94 int, S_IRUGO | S_IWUSR | S_IWGRP);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -060095
96enum {
97 SMD_PKT_STATUS = 1U << 0,
98 SMD_PKT_READ = 1U << 1,
99 SMD_PKT_WRITE = 1U << 2,
100 SMD_PKT_READ_DUMP_BUFFER = 1U << 3,
101 SMD_PKT_WRITE_DUMP_BUFFER = 1U << 4,
102 SMD_PKT_POLL = 1U << 5,
103};
104
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105#define DEBUG
106
107#ifdef DEBUG
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600108#define D_STATUS(x...) \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109do { \
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600110 if (msm_smd_pkt_debug_mask & SMD_PKT_STATUS) \
111 pr_info("Status: "x); \
112} while (0)
113
114#define D_READ(x...) \
115do { \
116 if (msm_smd_pkt_debug_mask & SMD_PKT_READ) \
117 pr_info("Read: "x); \
118} while (0)
119
120#define D_WRITE(x...) \
121do { \
122 if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE) \
123 pr_info("Write: "x); \
124} while (0)
125
126#define D_READ_DUMP_BUFFER(prestr, cnt, buf) \
127do { \
128 if (msm_smd_pkt_debug_mask & SMD_PKT_READ_DUMP_BUFFER) \
129 print_hex_dump(KERN_INFO, prestr, \
130 DUMP_PREFIX_NONE, 16, 1, \
131 buf, cnt, 1); \
132} while (0)
133
134#define D_WRITE_DUMP_BUFFER(prestr, cnt, buf) \
135do { \
136 if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE_DUMP_BUFFER) \
137 print_hex_dump(KERN_INFO, prestr, \
138 DUMP_PREFIX_NONE, 16, 1, \
139 buf, cnt, 1); \
140} while (0)
141
142#define D_POLL(x...) \
143do { \
144 if (msm_smd_pkt_debug_mask & SMD_PKT_POLL) \
145 pr_info("Poll: "x); \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146} while (0)
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600147
148#define E_SMD_PKT_SSR(x) \
149do { \
150 if (x->do_reset_notification) \
151 pr_err("%s notifying reset for smd_pkt_dev id:%d\n", \
152 __func__, x->i); \
153} while (0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154#else
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600155#define D_STATUS(x...) do {} while (0)
156#define D_READ(x...) do {} while (0)
157#define D_WRITE(x...) do {} while (0)
158#define D_READ_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
159#define D_WRITE_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
160#define D_POLL(x...) do {} while (0)
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600161#define E_SMD_PKT_SSR(x) do {} while (0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700162#endif
163
164static ssize_t open_timeout_store(struct device *d,
165 struct device_attribute *attr,
166 const char *buf,
167 size_t n)
168{
169 int i;
170 unsigned long tmp;
171 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
172 if (smd_pkt_devp[i]->devicep == d)
173 break;
174 }
Jeff Hugo31f83b42012-01-25 15:15:26 -0700175 if (i >= NUM_SMD_PKT_PORTS) {
176 pr_err("%s: unable to match device to valid smd_pkt port\n",
177 __func__);
178 return -EINVAL;
179 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180 if (!strict_strtoul(buf, 10, &tmp)) {
181 smd_pkt_devp[i]->open_modem_wait = tmp;
182 return n;
183 } else {
184 pr_err("%s: unable to convert: %s to an int\n", __func__,
185 buf);
186 return -EINVAL;
187 }
188}
189
190static ssize_t open_timeout_show(struct device *d,
191 struct device_attribute *attr,
192 char *buf)
193{
194 int i;
195 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
196 if (smd_pkt_devp[i]->devicep == d)
197 break;
198 }
Jeff Hugo31f83b42012-01-25 15:15:26 -0700199 if (i >= NUM_SMD_PKT_PORTS) {
200 pr_err("%s: unable to match device to valid smd_pkt port\n",
201 __func__);
202 return -EINVAL;
203 }
Karthikeyan Ramasubramanian63fa3d32011-09-29 17:06:26 -0600204 return snprintf(buf, PAGE_SIZE, "%d\n",
205 smd_pkt_devp[i]->open_modem_wait);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206}
207
208static DEVICE_ATTR(open_timeout, 0664, open_timeout_show, open_timeout_store);
209
210static int notify_reset(struct smd_pkt_dev *smd_pkt_devp)
211{
212 smd_pkt_devp->do_reset_notification = 0;
213
214 return -ENETRESET;
215}
216
217static void clean_and_signal(struct smd_pkt_dev *smd_pkt_devp)
218{
219 smd_pkt_devp->do_reset_notification = 1;
220 smd_pkt_devp->has_reset = 1;
221
222 smd_pkt_devp->is_open = 0;
223
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700224 wake_up(&smd_pkt_devp->ch_read_wait_queue);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700225 wake_up(&smd_pkt_devp->ch_write_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226 wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600227 D_STATUS("%s smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700228}
229
230static void loopback_probe_worker(struct work_struct *work)
231{
232
233 /* Wait for the modem SMSM to be inited for the SMD
234 ** Loopback channel to be allocated at the modem. Since
235 ** the wait need to be done atmost once, using msleep
236 ** doesn't degrade the performance. */
237 if (!is_modem_smsm_inited())
238 schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000));
239 else
240 smsm_change_state(SMSM_APPS_STATE,
241 0, SMSM_SMD_LOOPBACK);
242
243}
244
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700245static void packet_arrival_worker(struct work_struct *work)
246{
247 struct smd_pkt_dev *smd_pkt_devp;
Jeff Hugo93476a32012-06-01 14:32:49 -0600248 unsigned long flags;
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700249
250 smd_pkt_devp = container_of(work, struct smd_pkt_dev,
251 packet_arrival_work);
252 mutex_lock(&smd_pkt_devp->ch_lock);
Jeff Hugo93476a32012-06-01 14:32:49 -0600253 spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
254 if (smd_pkt_devp->ch && smd_pkt_devp->wakelock_locked) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600255 D_READ("%s locking smd_pkt_dev id:%d wakelock\n",
256 __func__, smd_pkt_devp->i);
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700257 wake_lock_timeout(&smd_pkt_devp->pa_wake_lock,
258 WAKELOCK_TIMEOUT);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600259 }
Jeff Hugo93476a32012-06-01 14:32:49 -0600260 spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700261 mutex_unlock(&smd_pkt_devp->ch_lock);
262}
263
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264static long smd_pkt_ioctl(struct file *file, unsigned int cmd,
265 unsigned long arg)
266{
267 int ret;
268 struct smd_pkt_dev *smd_pkt_devp;
269
270 smd_pkt_devp = file->private_data;
271 if (!smd_pkt_devp)
272 return -EINVAL;
273
Karthikeyan Ramasubramanian540dbf32012-09-14 13:16:50 -0600274 mutex_lock(&smd_pkt_devp->ch_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700275 switch (cmd) {
276 case TIOCMGET:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600277 D_STATUS("%s TIOCMGET command on smd_pkt_dev id:%d\n",
278 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700279 ret = smd_tiocmget(smd_pkt_devp->ch);
280 break;
281 case TIOCMSET:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600282 D_STATUS("%s TIOCSET command on smd_pkt_dev id:%d\n",
283 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700284 ret = smd_tiocmset(smd_pkt_devp->ch, arg, ~arg);
285 break;
286 case SMD_PKT_IOCTL_BLOCKING_WRITE:
287 ret = get_user(smd_pkt_devp->blocking_write, (int *)arg);
288 break;
289 default:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600290 pr_err("%s: Unrecognized ioctl command %d\n", __func__, cmd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291 ret = -1;
292 }
Karthikeyan Ramasubramanian540dbf32012-09-14 13:16:50 -0600293 mutex_unlock(&smd_pkt_devp->ch_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294
295 return ret;
296}
297
298ssize_t smd_pkt_read(struct file *file,
299 char __user *buf,
300 size_t count,
301 loff_t *ppos)
302{
303 int r;
304 int bytes_read;
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700305 int pkt_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700306 struct smd_pkt_dev *smd_pkt_devp;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700307 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700308
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700309 smd_pkt_devp = file->private_data;
310
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600311 if (!smd_pkt_devp) {
312 pr_err("%s on NULL smd_pkt_dev\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700313 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600314 }
315
316 if (!smd_pkt_devp->ch) {
317 pr_err("%s on a closed smd_pkt_dev id:%d\n",
318 __func__, smd_pkt_devp->i);
319 return -EINVAL;
320 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700321
322 if (smd_pkt_devp->do_reset_notification) {
323 /* notify client that a reset occurred */
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600324 E_SMD_PKT_SSR(smd_pkt_devp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700325 return notify_reset(smd_pkt_devp);
326 }
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600327 D_READ("Begin %s on smd_pkt_dev id:%d buffer_size %d\n",
328 __func__, smd_pkt_devp->i, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700329
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700330wait_for_packet:
331 r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue,
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600332 !smd_pkt_devp->ch ||
333 (smd_cur_packet_size(smd_pkt_devp->ch) > 0
334 && smd_read_avail(smd_pkt_devp->ch)) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700335 smd_pkt_devp->has_reset);
336
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600337 mutex_lock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600338 if (smd_pkt_devp->has_reset) {
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600339 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600340 E_SMD_PKT_SSR(smd_pkt_devp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700341 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600342 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700343
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600344 if (!smd_pkt_devp->ch) {
345 mutex_unlock(&smd_pkt_devp->rx_lock);
346 pr_err("%s on a closed smd_pkt_dev id:%d\n",
347 __func__, smd_pkt_devp->i);
348 return -EINVAL;
349 }
350
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700351 if (r < 0) {
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600352 mutex_unlock(&smd_pkt_devp->rx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700353 /* qualify error message */
354 if (r != -ERESTARTSYS) {
355 /* we get this anytime a signal comes in */
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600356 pr_err("%s: wait_event_interruptible on smd_pkt_dev"
357 " id:%d ret %i\n",
358 __func__, smd_pkt_devp->i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700359 }
360 return r;
361 }
362
363 /* Here we have a whole packet waiting for us */
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700364 pkt_size = smd_cur_packet_size(smd_pkt_devp->ch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700365
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700366 if (!pkt_size) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600367 pr_err("%s: No data on smd_pkt_dev id:%d, False wakeup\n",
368 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700369 mutex_unlock(&smd_pkt_devp->rx_lock);
370 goto wait_for_packet;
371 }
372
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700373 if (pkt_size > count) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600374 pr_err("%s: failure on smd_pkt_dev id: %d - packet size %d"
375 " > buffer size %d,", __func__, smd_pkt_devp->i,
376 pkt_size, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700377 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700378 return -ETOOSMALL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700379 }
380
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700381 bytes_read = 0;
382 do {
383 r = smd_read_user_buffer(smd_pkt_devp->ch,
384 (buf + bytes_read),
385 (pkt_size - bytes_read));
386 if (r < 0) {
387 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600388 if (smd_pkt_devp->has_reset) {
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600389 E_SMD_PKT_SSR(smd_pkt_devp);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700390 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600391 }
392 pr_err("%s Error while reading %d\n", __func__, r);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700393 return r;
394 }
395 bytes_read += r;
396 if (pkt_size != bytes_read)
397 wait_event(smd_pkt_devp->ch_read_wait_queue,
398 smd_read_avail(smd_pkt_devp->ch) ||
399 smd_pkt_devp->has_reset);
400 if (smd_pkt_devp->has_reset) {
401 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600402 E_SMD_PKT_SSR(smd_pkt_devp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700403 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700404 }
405 } while (pkt_size != bytes_read);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600406 D_READ_DUMP_BUFFER("Read: ", (bytes_read > 16 ? 16 : bytes_read), buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407 mutex_unlock(&smd_pkt_devp->rx_lock);
408
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700409 mutex_lock(&smd_pkt_devp->ch_lock);
410 spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
411 if (smd_pkt_devp->poll_mode &&
412 !smd_cur_packet_size(smd_pkt_devp->ch)) {
413 wake_unlock(&smd_pkt_devp->pa_wake_lock);
Jeff Hugo93476a32012-06-01 14:32:49 -0600414 smd_pkt_devp->wakelock_locked = 0;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700415 smd_pkt_devp->poll_mode = 0;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600416 D_READ("%s unlocked smd_pkt_dev id:%d wakelock\n",
417 __func__, smd_pkt_devp->i);
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700418 }
419 spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
420 mutex_unlock(&smd_pkt_devp->ch_lock);
421
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600422 D_READ("Finished %s on smd_pkt_dev id:%d %d bytes\n",
423 __func__, smd_pkt_devp->i, bytes_read);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700424
425 /* check and wakeup read threads waiting on this device */
426 check_and_wakeup_reader(smd_pkt_devp);
427
428 return bytes_read;
429}
430
431ssize_t smd_pkt_write(struct file *file,
432 const char __user *buf,
433 size_t count,
434 loff_t *ppos)
435{
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700436 int r = 0, bytes_written;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437 struct smd_pkt_dev *smd_pkt_devp;
438 DEFINE_WAIT(write_wait);
439
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700440 smd_pkt_devp = file->private_data;
441
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600442 if (!smd_pkt_devp) {
443 pr_err("%s on NULL smd_pkt_dev\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700444 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600445 }
446
447 if (!smd_pkt_devp->ch) {
448 pr_err("%s on a closed smd_pkt_dev id:%d\n",
449 __func__, smd_pkt_devp->i);
450 return -EINVAL;
451 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700452
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600453 if (smd_pkt_devp->do_reset_notification || smd_pkt_devp->has_reset) {
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600454 E_SMD_PKT_SSR(smd_pkt_devp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700455 /* notify client that a reset occurred */
456 return notify_reset(smd_pkt_devp);
457 }
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600458 D_WRITE("Begin %s on smd_pkt_dev id:%d data_size %d\n",
459 __func__, smd_pkt_devp->i, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700460
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700461 mutex_lock(&smd_pkt_devp->tx_lock);
462 if (!smd_pkt_devp->blocking_write) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700463 if (smd_write_avail(smd_pkt_devp->ch) < count) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600464 pr_err("%s: Not enough space in smd_pkt_dev id:%d\n",
465 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700466 mutex_unlock(&smd_pkt_devp->tx_lock);
467 return -ENOMEM;
468 }
469 }
470
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700471 r = smd_write_start(smd_pkt_devp->ch, count);
472 if (r < 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700473 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600474 pr_err("%s: Error:%d in smd_pkt_dev id:%d @ smd_write_start\n",
475 __func__, r, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700476 return r;
477 }
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700478
479 bytes_written = 0;
480 do {
481 prepare_to_wait(&smd_pkt_devp->ch_write_wait_queue,
482 &write_wait, TASK_UNINTERRUPTIBLE);
483 if (!smd_write_avail(smd_pkt_devp->ch) &&
484 !smd_pkt_devp->has_reset) {
485 smd_enable_read_intr(smd_pkt_devp->ch);
486 schedule();
487 }
488 finish_wait(&smd_pkt_devp->ch_write_wait_queue, &write_wait);
489 smd_disable_read_intr(smd_pkt_devp->ch);
490
491 if (smd_pkt_devp->has_reset) {
492 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600493 E_SMD_PKT_SSR(smd_pkt_devp);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700494 return notify_reset(smd_pkt_devp);
495 } else {
496 r = smd_write_segment(smd_pkt_devp->ch,
497 (void *)(buf + bytes_written),
498 (count - bytes_written), 1);
499 if (r < 0) {
500 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600501 if (smd_pkt_devp->has_reset) {
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600502 E_SMD_PKT_SSR(smd_pkt_devp);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700503 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600504 }
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600505 pr_err("%s on smd_pkt_dev id:%d failed r:%d\n",
506 __func__, smd_pkt_devp->i, r);
507 return r;
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700508 }
509 bytes_written += r;
510 }
511 } while (bytes_written != count);
512 smd_write_end(smd_pkt_devp->ch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700513 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600514 D_WRITE_DUMP_BUFFER("Write: ",
515 (bytes_written > 16 ? 16 : bytes_written), buf);
516 D_WRITE("Finished %s on smd_pkt_dev id:%d %d bytes\n",
517 __func__, smd_pkt_devp->i, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700518
519 return count;
520}
521
522static unsigned int smd_pkt_poll(struct file *file, poll_table *wait)
523{
524 struct smd_pkt_dev *smd_pkt_devp;
525 unsigned int mask = 0;
526
527 smd_pkt_devp = file->private_data;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600528 if (!smd_pkt_devp) {
529 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700530 return POLLERR;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600531 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700532
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700533 smd_pkt_devp->poll_mode = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700534 poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait);
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600535 mutex_lock(&smd_pkt_devp->ch_lock);
536 if (smd_pkt_devp->has_reset || !smd_pkt_devp->ch) {
537 mutex_unlock(&smd_pkt_devp->ch_lock);
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600538 return POLLERR;
539 }
540
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600541 if (smd_read_avail(smd_pkt_devp->ch)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700542 mask |= POLLIN | POLLRDNORM;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600543 D_POLL("%s sets POLLIN for smd_pkt_dev id: %d\n",
544 __func__, smd_pkt_devp->i);
545 }
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600546 mutex_unlock(&smd_pkt_devp->ch_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547
548 return mask;
549}
550
551static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp)
552{
553 int sz;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700554 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700555
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600556 if (!smd_pkt_devp) {
557 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700558 return;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600559 }
560
561 if (!smd_pkt_devp->ch) {
562 pr_err("%s on a closed smd_pkt_dev id:%d\n",
563 __func__, smd_pkt_devp->i);
564 return;
565 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700566
567 sz = smd_cur_packet_size(smd_pkt_devp->ch);
568 if (sz == 0) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600569 D_READ("%s: No packet in smd_pkt_dev id:%d\n",
570 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700571 return;
572 }
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700573 if (!smd_read_avail(smd_pkt_devp->ch)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600574 D_READ("%s: packet size is %d in smd_pkt_dev id:%d -"
575 " but the data isn't here\n",
576 __func__, sz, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700577 return;
578 }
579
580 /* here we have a packet of size sz ready */
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700581 spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
582 wake_lock(&smd_pkt_devp->pa_wake_lock);
Jeff Hugo93476a32012-06-01 14:32:49 -0600583 smd_pkt_devp->wakelock_locked = 1;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700584 spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
Jeff Hugo93476a32012-06-01 14:32:49 -0600585 wake_up(&smd_pkt_devp->ch_read_wait_queue);
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700586 schedule_work(&smd_pkt_devp->packet_arrival_work);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600587 D_READ("%s: wake_up smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700588}
589
590static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp)
591{
592 int sz;
593
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600594 if (!smd_pkt_devp) {
595 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700596 return;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600597 }
598
599 if (!smd_pkt_devp->ch) {
600 pr_err("%s on a closed smd_pkt_dev id:%d\n",
601 __func__, smd_pkt_devp->i);
602 return;
603 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700604
605 sz = smd_write_avail(smd_pkt_devp->ch);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700606 if (sz) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600607 D_WRITE("%s: %d bytes write space in smd_pkt_dev id:%d\n",
608 __func__, sz, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700609 smd_disable_read_intr(smd_pkt_devp->ch);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700610 wake_up(&smd_pkt_devp->ch_write_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700611 }
612}
613
614static void ch_notify(void *priv, unsigned event)
615{
616 struct smd_pkt_dev *smd_pkt_devp = priv;
617
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600618 if (smd_pkt_devp->ch == 0) {
619 pr_err("%s on a closed smd_pkt_dev id:%d\n",
620 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700621 return;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600622 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700623
624 switch (event) {
625 case SMD_EVENT_DATA: {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600626 D_STATUS("%s: DATA event in smd_pkt_dev id:%d\n",
627 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700628 check_and_wakeup_reader(smd_pkt_devp);
629 if (smd_pkt_devp->blocking_write)
630 check_and_wakeup_writer(smd_pkt_devp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700631 break;
632 }
633 case SMD_EVENT_OPEN:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600634 D_STATUS("%s: OPEN event in smd_pkt_dev id:%d\n",
635 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700636 smd_pkt_devp->has_reset = 0;
637 smd_pkt_devp->is_open = 1;
638 wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
639 break;
640 case SMD_EVENT_CLOSE:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600641 D_STATUS("%s: CLOSE event in smd_pkt_dev id:%d\n",
642 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700643 smd_pkt_devp->is_open = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700644 /* put port into reset state */
645 clean_and_signal(smd_pkt_devp);
646 if (smd_pkt_devp->i == LOOPBACK_INX)
647 schedule_delayed_work(&loopback_work,
648 msecs_to_jiffies(1000));
649 break;
650 }
651}
652
653#ifdef CONFIG_ARCH_FSM9XXX
654static char *smd_pkt_dev_name[] = {
655 "smdcntl1",
656 "smdcntl2",
657 "smd22",
658 "smd_pkt_loopback",
659};
660
661static char *smd_ch_name[] = {
662 "DATA6_CNTL",
663 "DATA7_CNTL",
664 "DATA22",
665 "LOOPBACK",
666};
667
668static uint32_t smd_ch_edge[] = {
669 SMD_APPS_QDSP,
670 SMD_APPS_QDSP,
671 SMD_APPS_QDSP,
672 SMD_APPS_QDSP
673};
674#else
675static char *smd_pkt_dev_name[] = {
676 "smdcntl0",
677 "smdcntl1",
678 "smdcntl2",
679 "smdcntl3",
680 "smdcntl4",
681 "smdcntl5",
682 "smdcntl6",
683 "smdcntl7",
684 "smd22",
685 "smd_sns_dsps",
Angshuman Sarkara18a2722011-07-29 13:41:13 +0530686 "apr_apps2",
Karthikeyan Ramasubramaniand08e59a2012-03-01 17:35:34 -0700687 "smdcntl8",
Jeff Hugodf5ee322012-04-16 11:56:12 -0600688 "smd_sns_adsp",
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -0600689 "smd_cxm_qmi",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700690 "smd_pkt_loopback",
691};
692
693static char *smd_ch_name[] = {
694 "DATA5_CNTL",
695 "DATA6_CNTL",
696 "DATA7_CNTL",
697 "DATA8_CNTL",
698 "DATA9_CNTL",
699 "DATA12_CNTL",
700 "DATA13_CNTL",
701 "DATA14_CNTL",
702 "DATA22",
703 "SENSOR",
Angshuman Sarkara18a2722011-07-29 13:41:13 +0530704 "apr_apps2",
Karthikeyan Ramasubramaniand08e59a2012-03-01 17:35:34 -0700705 "DATA40_CNTL",
Jeff Hugodf5ee322012-04-16 11:56:12 -0600706 "SENSOR",
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -0600707 "CXM_QMI_PORT_8064",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700708 "LOOPBACK",
709};
710
711static uint32_t smd_ch_edge[] = {
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_MODEM,
721 SMD_APPS_DSPS,
722 SMD_APPS_QDSP,
723 SMD_APPS_MODEM,
Jeff Hugodf5ee322012-04-16 11:56:12 -0600724 SMD_APPS_QDSP,
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -0600725 SMD_APPS_WCNSS,
Karthikeyan Ramasubramaniand08e59a2012-03-01 17:35:34 -0700726 SMD_APPS_MODEM,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700727};
728#endif
Jeff Hugo223fd452012-07-18 14:35:45 -0600729module_param_named(loopback_edge, smd_ch_edge[LOOPBACK_INX],
730 int, S_IRUGO | S_IWUSR | S_IWGRP);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700731
732static int smd_pkt_dummy_probe(struct platform_device *pdev)
733{
734 int i;
735
736 for (i = 0; i < NUM_SMD_PKT_PORTS; i++) {
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600737 if (smd_ch_edge[i] == pdev->id
738 && !strncmp(pdev->name, smd_ch_name[i],
739 SMD_MAX_CH_NAME_LEN)
740 && smd_pkt_devp[i]->driver.probe) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700741 complete_all(&smd_pkt_devp[i]->ch_allocated);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600742 D_STATUS("%s allocated SMD ch for smd_pkt_dev id:%d\n",
743 __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700744 break;
745 }
746 }
747 return 0;
748}
749
750static uint32_t is_modem_smsm_inited(void)
751{
752 uint32_t modem_state;
753 uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT);
754
755 modem_state = smsm_get_state(SMSM_MODEM_STATE);
756 return (modem_state & ready_state) == ready_state;
757}
758
759int smd_pkt_open(struct inode *inode, struct file *file)
760{
761 int r = 0;
762 struct smd_pkt_dev *smd_pkt_devp;
Eric Holmbergcce6daf2012-02-27 14:34:02 -0700763 const char *peripheral = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700764
765 smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev);
766
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600767 if (!smd_pkt_devp) {
768 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700769 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600770 }
771 D_STATUS("Begin %s on smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700772
773 file->private_data = smd_pkt_devp;
774
775 mutex_lock(&smd_pkt_devp->ch_lock);
776 if (smd_pkt_devp->ch == 0) {
Eric Holmbergb399a4f2012-07-06 11:42:24 -0600777 wake_lock_init(&smd_pkt_devp->pa_wake_lock, WAKE_LOCK_SUSPEND,
778 smd_pkt_dev_name[smd_pkt_devp->i]);
779 INIT_WORK(&smd_pkt_devp->packet_arrival_work,
780 packet_arrival_worker);
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700781 init_completion(&smd_pkt_devp->ch_allocated);
782 smd_pkt_devp->driver.probe = smd_pkt_dummy_probe;
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600783 scnprintf(smd_pkt_devp->pdriver_name, PDRIVER_NAME_MAX_SIZE,
Karthikeyan Ramasubramanian61df1932012-06-28 12:10:41 -0600784 "%s", smd_ch_name[smd_pkt_devp->i]);
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600785 smd_pkt_devp->driver.driver.name = smd_pkt_devp->pdriver_name;
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700786 smd_pkt_devp->driver.driver.owner = THIS_MODULE;
787 r = platform_driver_register(&smd_pkt_devp->driver);
788 if (r) {
789 pr_err("%s: %s Platform driver reg. failed\n",
790 __func__, smd_ch_name[smd_pkt_devp->i]);
791 goto out;
792 }
793
Eric Holmbergcce6daf2012-02-27 14:34:02 -0700794 peripheral = smd_edge_to_subsystem(
795 smd_ch_edge[smd_pkt_devp->i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700796 if (peripheral) {
797 smd_pkt_devp->pil = pil_get(peripheral);
798 if (IS_ERR(smd_pkt_devp->pil)) {
799 r = PTR_ERR(smd_pkt_devp->pil);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600800 pr_err("%s failed on smd_pkt_dev id:%d -"
801 " pil_get failed for %s\n", __func__,
802 smd_pkt_devp->i, peripheral);
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700803 goto release_pd;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700804 }
805
806 /* Wait for the modem SMSM to be inited for the SMD
807 ** Loopback channel to be allocated at the modem. Since
808 ** the wait need to be done atmost once, using msleep
809 ** doesn't degrade the performance. */
Jeff Hugoa5ee4362011-07-15 13:48:48 -0600810 if (!strncmp(smd_ch_name[smd_pkt_devp->i], "LOOPBACK",
811 SMD_MAX_CH_NAME_LEN)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700812 if (!is_modem_smsm_inited())
813 msleep(5000);
814 smsm_change_state(SMSM_APPS_STATE,
815 0, SMSM_SMD_LOOPBACK);
816 msleep(100);
817 }
818
819 /*
820 * Wait for a packet channel to be allocated so we know
821 * the modem is ready enough.
822 */
823 if (smd_pkt_devp->open_modem_wait) {
824 r = wait_for_completion_interruptible_timeout(
825 &smd_pkt_devp->ch_allocated,
826 msecs_to_jiffies(
827 smd_pkt_devp->open_modem_wait
828 * 1000));
829 if (r == 0)
830 r = -ETIMEDOUT;
831 if (r < 0) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600832 pr_err("%s: wait on smd_pkt_dev id:%d"
833 " allocation failed rc:%d\n",
834 __func__, smd_pkt_devp->i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700835 goto release_pil;
836 }
837 }
838 }
839
840 r = smd_named_open_on_edge(smd_ch_name[smd_pkt_devp->i],
841 smd_ch_edge[smd_pkt_devp->i],
842 &smd_pkt_devp->ch,
843 smd_pkt_devp,
844 ch_notify);
845 if (r < 0) {
846 pr_err("%s: %s open failed %d\n", __func__,
847 smd_ch_name[smd_pkt_devp->i], r);
848 goto release_pil;
849 }
850
851 r = wait_event_interruptible_timeout(
852 smd_pkt_devp->ch_opened_wait_queue,
853 smd_pkt_devp->is_open, (2 * HZ));
Jeff Hugo4d9fdf32012-04-30 11:10:54 -0600854 if (r == 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700855 r = -ETIMEDOUT;
Jeff Hugo4d9fdf32012-04-30 11:10:54 -0600856 /* close the ch to sync smd's state with smd_pkt */
857 smd_close(smd_pkt_devp->ch);
858 smd_pkt_devp->ch = NULL;
859 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700860
861 if (r < 0) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600862 pr_err("%s: wait on smd_pkt_dev id:%d OPEN event failed"
863 " rc:%d\n", __func__, smd_pkt_devp->i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700864 } else if (!smd_pkt_devp->is_open) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600865 pr_err("%s: Invalid OPEN event on smd_pkt_dev id:%d\n",
866 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700867 r = -ENODEV;
868 } else {
869 smd_disable_read_intr(smd_pkt_devp->ch);
870 smd_pkt_devp->ch_size =
871 smd_write_avail(smd_pkt_devp->ch);
872 r = 0;
Karthikeyan Ramasubramanian540dbf32012-09-14 13:16:50 -0600873 smd_pkt_devp->ref_cnt++;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600874 D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
875 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700876 }
Karthikeyan Ramasubramanian540dbf32012-09-14 13:16:50 -0600877 } else {
878 smd_pkt_devp->ref_cnt++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700879 }
880release_pil:
881 if (peripheral && (r < 0))
882 pil_put(smd_pkt_devp->pil);
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700883
884release_pd:
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600885 if (r < 0) {
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700886 platform_driver_unregister(&smd_pkt_devp->driver);
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600887 smd_pkt_devp->driver.probe = NULL;
888 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700889out:
Eric Holmbergb399a4f2012-07-06 11:42:24 -0600890 if (!smd_pkt_devp->ch)
891 wake_lock_destroy(&smd_pkt_devp->pa_wake_lock);
892
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700893 mutex_unlock(&smd_pkt_devp->ch_lock);
894
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700895
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700896 return r;
897}
898
899int smd_pkt_release(struct inode *inode, struct file *file)
900{
901 int r = 0;
902 struct smd_pkt_dev *smd_pkt_devp = file->private_data;
903
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600904 if (!smd_pkt_devp) {
905 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700906 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600907 }
908 D_STATUS("Begin %s on smd_pkt_dev id:%d\n",
909 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700910
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700911 mutex_lock(&smd_pkt_devp->ch_lock);
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600912 mutex_lock(&smd_pkt_devp->rx_lock);
913 mutex_lock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian540dbf32012-09-14 13:16:50 -0600914 if (smd_pkt_devp->ref_cnt > 0)
915 smd_pkt_devp->ref_cnt--;
916
917 if (smd_pkt_devp->ch != 0 && smd_pkt_devp->ref_cnt == 0) {
918 clean_and_signal(smd_pkt_devp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700919 r = smd_close(smd_pkt_devp->ch);
920 smd_pkt_devp->ch = 0;
921 smd_pkt_devp->blocking_write = 0;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700922 smd_pkt_devp->poll_mode = 0;
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700923 platform_driver_unregister(&smd_pkt_devp->driver);
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600924 smd_pkt_devp->driver.probe = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700925 if (smd_pkt_devp->pil)
926 pil_put(smd_pkt_devp->pil);
Karthikeyan Ramasubramanian540dbf32012-09-14 13:16:50 -0600927 smd_pkt_devp->has_reset = 0;
928 smd_pkt_devp->do_reset_notification = 0;
929 smd_pkt_devp->wakelock_locked = 0;
930 wake_lock_destroy(&smd_pkt_devp->pa_wake_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700931 }
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600932 mutex_unlock(&smd_pkt_devp->tx_lock);
933 mutex_unlock(&smd_pkt_devp->rx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700934 mutex_unlock(&smd_pkt_devp->ch_lock);
935
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600936 D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
937 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700938
939 return r;
940}
941
942static const struct file_operations smd_pkt_fops = {
943 .owner = THIS_MODULE,
944 .open = smd_pkt_open,
945 .release = smd_pkt_release,
946 .read = smd_pkt_read,
947 .write = smd_pkt_write,
948 .poll = smd_pkt_poll,
949 .unlocked_ioctl = smd_pkt_ioctl,
950};
951
952static int __init smd_pkt_init(void)
953{
954 int i;
955 int r;
956
957 r = alloc_chrdev_region(&smd_pkt_number,
958 0,
959 NUM_SMD_PKT_PORTS,
960 DEVICE_NAME);
961 if (IS_ERR_VALUE(r)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600962 pr_err("%s: alloc_chrdev_region() failed ret:%i\n",
963 __func__, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700964 goto error0;
965 }
966
967 smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME);
968 if (IS_ERR(smd_pkt_classp)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600969 pr_err("%s: class_create() failed ENOMEM\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700970 r = -ENOMEM;
971 goto error1;
972 }
973
974 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
975 smd_pkt_devp[i] = kzalloc(sizeof(struct smd_pkt_dev),
976 GFP_KERNEL);
977 if (IS_ERR(smd_pkt_devp[i])) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600978 pr_err("%s: kzalloc() failed for smd_pkt_dev id:%d\n",
979 __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700980 r = -ENOMEM;
981 goto error2;
982 }
983
984 smd_pkt_devp[i]->i = i;
985
986 init_waitqueue_head(&smd_pkt_devp[i]->ch_read_wait_queue);
987 init_waitqueue_head(&smd_pkt_devp[i]->ch_write_wait_queue);
988 smd_pkt_devp[i]->is_open = 0;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700989 smd_pkt_devp[i]->poll_mode = 0;
Jeff Hugo93476a32012-06-01 14:32:49 -0600990 smd_pkt_devp[i]->wakelock_locked = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700991 init_waitqueue_head(&smd_pkt_devp[i]->ch_opened_wait_queue);
992
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700993 spin_lock_init(&smd_pkt_devp[i]->pa_spinlock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700994 mutex_init(&smd_pkt_devp[i]->ch_lock);
995 mutex_init(&smd_pkt_devp[i]->rx_lock);
996 mutex_init(&smd_pkt_devp[i]->tx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700997
998 cdev_init(&smd_pkt_devp[i]->cdev, &smd_pkt_fops);
999 smd_pkt_devp[i]->cdev.owner = THIS_MODULE;
1000
1001 r = cdev_add(&smd_pkt_devp[i]->cdev,
1002 (smd_pkt_number + i),
1003 1);
1004
1005 if (IS_ERR_VALUE(r)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001006 pr_err("%s: cdev_add() failed for smd_pkt_dev id:%d"
1007 " ret:%i\n", __func__, i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001008 kfree(smd_pkt_devp[i]);
1009 goto error2;
1010 }
1011
1012 smd_pkt_devp[i]->devicep =
1013 device_create(smd_pkt_classp,
1014 NULL,
1015 (smd_pkt_number + i),
1016 NULL,
1017 smd_pkt_dev_name[i]);
1018
1019 if (IS_ERR(smd_pkt_devp[i]->devicep)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001020 pr_err("%s: device_create() failed for smd_pkt_dev"
1021 " id:%d\n", __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001022 r = -ENOMEM;
1023 cdev_del(&smd_pkt_devp[i]->cdev);
1024 kfree(smd_pkt_devp[i]);
1025 goto error2;
1026 }
1027 if (device_create_file(smd_pkt_devp[i]->devicep,
1028 &dev_attr_open_timeout))
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001029 pr_err("%s: unable to create device attr for"
1030 " smd_pkt_dev id:%d\n", __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001031 }
1032
1033 INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
1034
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001035 D_STATUS("SMD Packet Port Driver Initialized.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001036 return 0;
1037
1038 error2:
1039 if (i > 0) {
1040 while (--i >= 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001041 cdev_del(&smd_pkt_devp[i]->cdev);
1042 kfree(smd_pkt_devp[i]);
1043 device_destroy(smd_pkt_classp,
1044 MKDEV(MAJOR(smd_pkt_number), i));
1045 }
1046 }
1047
1048 class_destroy(smd_pkt_classp);
1049 error1:
1050 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
1051 error0:
1052 return r;
1053}
1054
1055static void __exit smd_pkt_cleanup(void)
1056{
1057 int i;
1058
1059 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001060 cdev_del(&smd_pkt_devp[i]->cdev);
1061 kfree(smd_pkt_devp[i]);
1062 device_destroy(smd_pkt_classp,
1063 MKDEV(MAJOR(smd_pkt_number), i));
1064 }
1065
1066 class_destroy(smd_pkt_classp);
1067
1068 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
1069}
1070
1071module_init(smd_pkt_init);
1072module_exit(smd_pkt_cleanup);
1073
1074MODULE_DESCRIPTION("MSM Shared Memory Packet Port");
1075MODULE_LICENSE("GPL v2");