blob: 1c44f9ac0f7a3244a7ad7d9aae68f835e0036cd4 [file] [log] [blame]
Karthikeyan Ramasubramanian608a2bc2012-12-21 18:52:33 -07001/* Copyright (c) 2008-2013, The Linux Foundation. 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>
Stephen Boyd77db8bb2012-06-27 15:15:16 -070037#include <mach/subsystem_restart.h>
Karthikeyan Ramasubramanian085bd622012-09-10 21:45:00 -060038#include <mach/msm_ipc_logging.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070039
40#include "smd_private.h"
41#ifdef CONFIG_ARCH_FSM9XXX
42#define NUM_SMD_PKT_PORTS 4
43#else
Brent Hronik5a883cd2013-07-12 12:05:22 -060044#define NUM_SMD_PKT_PORTS 31
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045#endif
46
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -060047#define PDRIVER_NAME_MAX_SIZE 32
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048#define LOOPBACK_INX (NUM_SMD_PKT_PORTS - 1)
49
50#define DEVICE_NAME "smdpkt"
Eric Holmbergc3c5cd92012-02-07 18:19:49 -070051#define WAKELOCK_TIMEOUT (2*HZ)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052
53struct smd_pkt_dev {
54 struct cdev cdev;
55 struct device *devicep;
56 void *pil;
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -060057 char pdriver_name[PDRIVER_NAME_MAX_SIZE];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070058 struct platform_driver driver;
59
60 struct smd_channel *ch;
61 struct mutex ch_lock;
62 struct mutex rx_lock;
63 struct mutex tx_lock;
64 wait_queue_head_t ch_read_wait_queue;
65 wait_queue_head_t ch_write_wait_queue;
66 wait_queue_head_t ch_opened_wait_queue;
67
68 int i;
Karthikeyan Ramasubramanian540dbf32012-09-14 13:16:50 -060069 int ref_cnt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070070
71 int blocking_write;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072 int is_open;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -070073 int poll_mode;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070074 unsigned ch_size;
75 uint open_modem_wait;
76
77 int has_reset;
78 int do_reset_notification;
79 struct completion ch_allocated;
Eric Holmbergc3c5cd92012-02-07 18:19:49 -070080 struct wake_lock pa_wake_lock; /* Packet Arrival Wake lock*/
81 struct work_struct packet_arrival_work;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -070082 struct spinlock pa_spinlock;
Jeff Hugo93476a32012-06-01 14:32:49 -060083 int wakelock_locked;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070084} *smd_pkt_devp[NUM_SMD_PKT_PORTS];
85
86struct class *smd_pkt_classp;
87static dev_t smd_pkt_number;
88static struct delayed_work loopback_work;
89static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp);
90static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp);
91static uint32_t is_modem_smsm_inited(void);
92
Karthikeyan Ramasubramanian085bd622012-09-10 21:45:00 -060093#define SMD_PKT_IPC_LOG_PAGE_CNT 2
94static void *smd_pkt_ilctxt;
95
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070096static int msm_smd_pkt_debug_mask;
97module_param_named(debug_mask, msm_smd_pkt_debug_mask,
98 int, S_IRUGO | S_IWUSR | S_IWGRP);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -060099
100enum {
101 SMD_PKT_STATUS = 1U << 0,
102 SMD_PKT_READ = 1U << 1,
103 SMD_PKT_WRITE = 1U << 2,
104 SMD_PKT_READ_DUMP_BUFFER = 1U << 3,
105 SMD_PKT_WRITE_DUMP_BUFFER = 1U << 4,
106 SMD_PKT_POLL = 1U << 5,
107};
108
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109#define DEBUG
110
111#ifdef DEBUG
Karthikeyan Ramasubramanian085bd622012-09-10 21:45:00 -0600112
113#define SMD_PKT_LOG_STRING(x...) \
114do { \
115 if (smd_pkt_ilctxt) \
116 ipc_log_string(smd_pkt_ilctxt, "<SMD_PKT>: "x); \
117} while (0)
118
119#define SMD_PKT_LOG_BUF(buf, cnt) \
120do { \
121 char log_buf[128]; \
122 int i; \
123 if (smd_pkt_ilctxt) { \
124 i = cnt < 16 ? cnt : 16; \
125 hex_dump_to_buffer(buf, i, 16, 1, log_buf, \
126 sizeof(log_buf), false); \
127 ipc_log_string(smd_pkt_ilctxt, "<SMD_PKT>: %s", log_buf); \
128 } \
129} while (0)
130
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600131#define D_STATUS(x...) \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132do { \
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600133 if (msm_smd_pkt_debug_mask & SMD_PKT_STATUS) \
134 pr_info("Status: "x); \
Karthikeyan Ramasubramanian085bd622012-09-10 21:45:00 -0600135 SMD_PKT_LOG_STRING(x); \
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600136} while (0)
137
138#define D_READ(x...) \
139do { \
140 if (msm_smd_pkt_debug_mask & SMD_PKT_READ) \
141 pr_info("Read: "x); \
Karthikeyan Ramasubramanian085bd622012-09-10 21:45:00 -0600142 SMD_PKT_LOG_STRING(x); \
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600143} while (0)
144
145#define D_WRITE(x...) \
146do { \
147 if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE) \
148 pr_info("Write: "x); \
Karthikeyan Ramasubramanian085bd622012-09-10 21:45:00 -0600149 SMD_PKT_LOG_STRING(x); \
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600150} while (0)
151
152#define D_READ_DUMP_BUFFER(prestr, cnt, buf) \
153do { \
154 if (msm_smd_pkt_debug_mask & SMD_PKT_READ_DUMP_BUFFER) \
155 print_hex_dump(KERN_INFO, prestr, \
156 DUMP_PREFIX_NONE, 16, 1, \
157 buf, cnt, 1); \
Karthikeyan Ramasubramanian085bd622012-09-10 21:45:00 -0600158 SMD_PKT_LOG_BUF(buf, cnt); \
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600159} while (0)
160
161#define D_WRITE_DUMP_BUFFER(prestr, cnt, buf) \
162do { \
163 if (msm_smd_pkt_debug_mask & SMD_PKT_WRITE_DUMP_BUFFER) \
164 print_hex_dump(KERN_INFO, prestr, \
165 DUMP_PREFIX_NONE, 16, 1, \
166 buf, cnt, 1); \
Karthikeyan Ramasubramanian085bd622012-09-10 21:45:00 -0600167 SMD_PKT_LOG_BUF(buf, cnt); \
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600168} while (0)
169
170#define D_POLL(x...) \
171do { \
172 if (msm_smd_pkt_debug_mask & SMD_PKT_POLL) \
173 pr_info("Poll: "x); \
Karthikeyan Ramasubramanian085bd622012-09-10 21:45:00 -0600174 SMD_PKT_LOG_STRING(x); \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175} while (0)
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600176
177#define E_SMD_PKT_SSR(x) \
178do { \
179 if (x->do_reset_notification) \
180 pr_err("%s notifying reset for smd_pkt_dev id:%d\n", \
181 __func__, x->i); \
182} while (0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183#else
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600184#define D_STATUS(x...) do {} while (0)
185#define D_READ(x...) do {} while (0)
186#define D_WRITE(x...) do {} while (0)
187#define D_READ_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
188#define D_WRITE_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
189#define D_POLL(x...) do {} while (0)
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600190#define E_SMD_PKT_SSR(x) do {} while (0)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700191#endif
192
193static ssize_t open_timeout_store(struct device *d,
194 struct device_attribute *attr,
195 const char *buf,
196 size_t n)
197{
198 int i;
199 unsigned long tmp;
200 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
201 if (smd_pkt_devp[i]->devicep == d)
202 break;
203 }
Jeff Hugo31f83b42012-01-25 15:15:26 -0700204 if (i >= NUM_SMD_PKT_PORTS) {
205 pr_err("%s: unable to match device to valid smd_pkt port\n",
206 __func__);
207 return -EINVAL;
208 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209 if (!strict_strtoul(buf, 10, &tmp)) {
210 smd_pkt_devp[i]->open_modem_wait = tmp;
211 return n;
212 } else {
213 pr_err("%s: unable to convert: %s to an int\n", __func__,
214 buf);
215 return -EINVAL;
216 }
217}
218
219static ssize_t open_timeout_show(struct device *d,
220 struct device_attribute *attr,
221 char *buf)
222{
223 int i;
224 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
225 if (smd_pkt_devp[i]->devicep == d)
226 break;
227 }
Jeff Hugo31f83b42012-01-25 15:15:26 -0700228 if (i >= NUM_SMD_PKT_PORTS) {
229 pr_err("%s: unable to match device to valid smd_pkt port\n",
230 __func__);
231 return -EINVAL;
232 }
Karthikeyan Ramasubramanian63fa3d32011-09-29 17:06:26 -0600233 return snprintf(buf, PAGE_SIZE, "%d\n",
234 smd_pkt_devp[i]->open_modem_wait);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235}
236
237static DEVICE_ATTR(open_timeout, 0664, open_timeout_show, open_timeout_store);
238
239static int notify_reset(struct smd_pkt_dev *smd_pkt_devp)
240{
241 smd_pkt_devp->do_reset_notification = 0;
242
243 return -ENETRESET;
244}
245
246static void clean_and_signal(struct smd_pkt_dev *smd_pkt_devp)
247{
248 smd_pkt_devp->do_reset_notification = 1;
249 smd_pkt_devp->has_reset = 1;
250
251 smd_pkt_devp->is_open = 0;
252
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700253 wake_up(&smd_pkt_devp->ch_read_wait_queue);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700254 wake_up(&smd_pkt_devp->ch_write_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255 wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600256 D_STATUS("%s smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700257}
258
259static void loopback_probe_worker(struct work_struct *work)
260{
261
262 /* Wait for the modem SMSM to be inited for the SMD
263 ** Loopback channel to be allocated at the modem. Since
264 ** the wait need to be done atmost once, using msleep
265 ** doesn't degrade the performance. */
266 if (!is_modem_smsm_inited())
267 schedule_delayed_work(&loopback_work, msecs_to_jiffies(1000));
268 else
269 smsm_change_state(SMSM_APPS_STATE,
270 0, SMSM_SMD_LOOPBACK);
271
272}
273
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700274static void packet_arrival_worker(struct work_struct *work)
275{
276 struct smd_pkt_dev *smd_pkt_devp;
Jeff Hugo93476a32012-06-01 14:32:49 -0600277 unsigned long flags;
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700278
279 smd_pkt_devp = container_of(work, struct smd_pkt_dev,
280 packet_arrival_work);
281 mutex_lock(&smd_pkt_devp->ch_lock);
Jeff Hugo93476a32012-06-01 14:32:49 -0600282 spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
283 if (smd_pkt_devp->ch && smd_pkt_devp->wakelock_locked) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600284 D_READ("%s locking smd_pkt_dev id:%d wakelock\n",
285 __func__, smd_pkt_devp->i);
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700286 wake_lock_timeout(&smd_pkt_devp->pa_wake_lock,
287 WAKELOCK_TIMEOUT);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600288 }
Jeff Hugo93476a32012-06-01 14:32:49 -0600289 spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700290 mutex_unlock(&smd_pkt_devp->ch_lock);
291}
292
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293static long smd_pkt_ioctl(struct file *file, unsigned int cmd,
294 unsigned long arg)
295{
296 int ret;
297 struct smd_pkt_dev *smd_pkt_devp;
298
299 smd_pkt_devp = file->private_data;
300 if (!smd_pkt_devp)
301 return -EINVAL;
302
Karthikeyan Ramasubramanian540dbf32012-09-14 13:16:50 -0600303 mutex_lock(&smd_pkt_devp->ch_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700304 switch (cmd) {
305 case TIOCMGET:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600306 D_STATUS("%s TIOCMGET command on smd_pkt_dev id:%d\n",
307 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700308 ret = smd_tiocmget(smd_pkt_devp->ch);
309 break;
310 case TIOCMSET:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600311 D_STATUS("%s TIOCSET command on smd_pkt_dev id:%d\n",
312 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700313 ret = smd_tiocmset(smd_pkt_devp->ch, arg, ~arg);
314 break;
315 case SMD_PKT_IOCTL_BLOCKING_WRITE:
316 ret = get_user(smd_pkt_devp->blocking_write, (int *)arg);
317 break;
318 default:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600319 pr_err("%s: Unrecognized ioctl command %d\n", __func__, cmd);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700320 ret = -1;
321 }
Karthikeyan Ramasubramanian540dbf32012-09-14 13:16:50 -0600322 mutex_unlock(&smd_pkt_devp->ch_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700323
324 return ret;
325}
326
327ssize_t smd_pkt_read(struct file *file,
328 char __user *buf,
329 size_t count,
330 loff_t *ppos)
331{
332 int r;
333 int bytes_read;
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700334 int pkt_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700335 struct smd_pkt_dev *smd_pkt_devp;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700336 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700337
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700338 smd_pkt_devp = file->private_data;
339
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600340 if (!smd_pkt_devp) {
341 pr_err("%s on NULL smd_pkt_dev\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700342 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600343 }
344
345 if (!smd_pkt_devp->ch) {
346 pr_err("%s on a closed smd_pkt_dev id:%d\n",
347 __func__, smd_pkt_devp->i);
348 return -EINVAL;
349 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700350
351 if (smd_pkt_devp->do_reset_notification) {
352 /* notify client that a reset occurred */
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600353 E_SMD_PKT_SSR(smd_pkt_devp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700354 return notify_reset(smd_pkt_devp);
355 }
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600356 D_READ("Begin %s on smd_pkt_dev id:%d buffer_size %d\n",
357 __func__, smd_pkt_devp->i, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700358
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700359wait_for_packet:
360 r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue,
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600361 !smd_pkt_devp->ch ||
362 (smd_cur_packet_size(smd_pkt_devp->ch) > 0
363 && smd_read_avail(smd_pkt_devp->ch)) ||
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700364 smd_pkt_devp->has_reset);
365
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600366 mutex_lock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600367 if (smd_pkt_devp->has_reset) {
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600368 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600369 E_SMD_PKT_SSR(smd_pkt_devp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700370 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600371 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700372
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600373 if (!smd_pkt_devp->ch) {
374 mutex_unlock(&smd_pkt_devp->rx_lock);
375 pr_err("%s on a closed smd_pkt_dev id:%d\n",
376 __func__, smd_pkt_devp->i);
377 return -EINVAL;
378 }
379
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700380 if (r < 0) {
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600381 mutex_unlock(&smd_pkt_devp->rx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700382 /* qualify error message */
383 if (r != -ERESTARTSYS) {
384 /* we get this anytime a signal comes in */
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600385 pr_err("%s: wait_event_interruptible on smd_pkt_dev"
386 " id:%d ret %i\n",
387 __func__, smd_pkt_devp->i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700388 }
389 return r;
390 }
391
392 /* Here we have a whole packet waiting for us */
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700393 pkt_size = smd_cur_packet_size(smd_pkt_devp->ch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700395 if (!pkt_size) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600396 pr_err("%s: No data on smd_pkt_dev id:%d, False wakeup\n",
397 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700398 mutex_unlock(&smd_pkt_devp->rx_lock);
399 goto wait_for_packet;
400 }
401
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700402 if (pkt_size > count) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600403 pr_err("%s: failure on smd_pkt_dev id: %d - packet size %d"
404 " > buffer size %d,", __func__, smd_pkt_devp->i,
405 pkt_size, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700407 return -ETOOSMALL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700408 }
409
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700410 bytes_read = 0;
411 do {
412 r = smd_read_user_buffer(smd_pkt_devp->ch,
413 (buf + bytes_read),
414 (pkt_size - bytes_read));
415 if (r < 0) {
416 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600417 if (smd_pkt_devp->has_reset) {
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600418 E_SMD_PKT_SSR(smd_pkt_devp);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700419 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600420 }
421 pr_err("%s Error while reading %d\n", __func__, r);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700422 return r;
423 }
424 bytes_read += r;
425 if (pkt_size != bytes_read)
426 wait_event(smd_pkt_devp->ch_read_wait_queue,
427 smd_read_avail(smd_pkt_devp->ch) ||
428 smd_pkt_devp->has_reset);
429 if (smd_pkt_devp->has_reset) {
430 mutex_unlock(&smd_pkt_devp->rx_lock);
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600431 E_SMD_PKT_SSR(smd_pkt_devp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700432 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700433 }
434 } while (pkt_size != bytes_read);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600435 D_READ_DUMP_BUFFER("Read: ", (bytes_read > 16 ? 16 : bytes_read), buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700436 mutex_unlock(&smd_pkt_devp->rx_lock);
437
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700438 mutex_lock(&smd_pkt_devp->ch_lock);
439 spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
440 if (smd_pkt_devp->poll_mode &&
441 !smd_cur_packet_size(smd_pkt_devp->ch)) {
442 wake_unlock(&smd_pkt_devp->pa_wake_lock);
Jeff Hugo93476a32012-06-01 14:32:49 -0600443 smd_pkt_devp->wakelock_locked = 0;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700444 smd_pkt_devp->poll_mode = 0;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600445 D_READ("%s unlocked smd_pkt_dev id:%d wakelock\n",
446 __func__, smd_pkt_devp->i);
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700447 }
448 spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
449 mutex_unlock(&smd_pkt_devp->ch_lock);
450
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600451 D_READ("Finished %s on smd_pkt_dev id:%d %d bytes\n",
452 __func__, smd_pkt_devp->i, bytes_read);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700453
454 /* check and wakeup read threads waiting on this device */
455 check_and_wakeup_reader(smd_pkt_devp);
456
457 return bytes_read;
458}
459
460ssize_t smd_pkt_write(struct file *file,
461 const char __user *buf,
462 size_t count,
463 loff_t *ppos)
464{
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700465 int r = 0, bytes_written;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700466 struct smd_pkt_dev *smd_pkt_devp;
467 DEFINE_WAIT(write_wait);
468
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700469 smd_pkt_devp = file->private_data;
470
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600471 if (!smd_pkt_devp) {
472 pr_err("%s on NULL smd_pkt_dev\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700473 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600474 }
475
476 if (!smd_pkt_devp->ch) {
477 pr_err("%s on a closed smd_pkt_dev id:%d\n",
478 __func__, smd_pkt_devp->i);
479 return -EINVAL;
480 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700481
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600482 if (smd_pkt_devp->do_reset_notification || smd_pkt_devp->has_reset) {
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600483 E_SMD_PKT_SSR(smd_pkt_devp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700484 /* notify client that a reset occurred */
485 return notify_reset(smd_pkt_devp);
486 }
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600487 D_WRITE("Begin %s on smd_pkt_dev id:%d data_size %d\n",
488 __func__, smd_pkt_devp->i, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700489
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700490 mutex_lock(&smd_pkt_devp->tx_lock);
491 if (!smd_pkt_devp->blocking_write) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700492 if (smd_write_avail(smd_pkt_devp->ch) < count) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600493 pr_err("%s: Not enough space in smd_pkt_dev id:%d\n",
494 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700495 mutex_unlock(&smd_pkt_devp->tx_lock);
496 return -ENOMEM;
497 }
498 }
499
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700500 r = smd_write_start(smd_pkt_devp->ch, count);
501 if (r < 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700502 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600503 pr_err("%s: Error:%d in smd_pkt_dev id:%d @ smd_write_start\n",
504 __func__, r, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700505 return r;
506 }
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700507
508 bytes_written = 0;
509 do {
510 prepare_to_wait(&smd_pkt_devp->ch_write_wait_queue,
511 &write_wait, TASK_UNINTERRUPTIBLE);
Jeff Hugo44fd9832013-04-04 15:56:21 -0600512 if (!smd_write_segment_avail(smd_pkt_devp->ch) &&
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700513 !smd_pkt_devp->has_reset) {
514 smd_enable_read_intr(smd_pkt_devp->ch);
515 schedule();
516 }
517 finish_wait(&smd_pkt_devp->ch_write_wait_queue, &write_wait);
518 smd_disable_read_intr(smd_pkt_devp->ch);
519
520 if (smd_pkt_devp->has_reset) {
521 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600522 E_SMD_PKT_SSR(smd_pkt_devp);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700523 return notify_reset(smd_pkt_devp);
524 } else {
525 r = smd_write_segment(smd_pkt_devp->ch,
526 (void *)(buf + bytes_written),
527 (count - bytes_written), 1);
528 if (r < 0) {
529 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600530 if (smd_pkt_devp->has_reset) {
Karthikeyan Ramasubramaniana6dc4b02012-09-07 14:20:06 -0600531 E_SMD_PKT_SSR(smd_pkt_devp);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700532 return notify_reset(smd_pkt_devp);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600533 }
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600534 pr_err("%s on smd_pkt_dev id:%d failed r:%d\n",
535 __func__, smd_pkt_devp->i, r);
536 return r;
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700537 }
538 bytes_written += r;
539 }
540 } while (bytes_written != count);
541 smd_write_end(smd_pkt_devp->ch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700542 mutex_unlock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600543 D_WRITE_DUMP_BUFFER("Write: ",
544 (bytes_written > 16 ? 16 : bytes_written), buf);
545 D_WRITE("Finished %s on smd_pkt_dev id:%d %d bytes\n",
546 __func__, smd_pkt_devp->i, count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547
548 return count;
549}
550
551static unsigned int smd_pkt_poll(struct file *file, poll_table *wait)
552{
553 struct smd_pkt_dev *smd_pkt_devp;
554 unsigned int mask = 0;
555
556 smd_pkt_devp = file->private_data;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600557 if (!smd_pkt_devp) {
558 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700559 return POLLERR;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600560 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700561
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700562 smd_pkt_devp->poll_mode = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700563 poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait);
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600564 mutex_lock(&smd_pkt_devp->ch_lock);
565 if (smd_pkt_devp->has_reset || !smd_pkt_devp->ch) {
566 mutex_unlock(&smd_pkt_devp->ch_lock);
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600567 return POLLERR;
568 }
569
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600570 if (smd_read_avail(smd_pkt_devp->ch)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700571 mask |= POLLIN | POLLRDNORM;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600572 D_POLL("%s sets POLLIN for smd_pkt_dev id: %d\n",
573 __func__, smd_pkt_devp->i);
574 }
Karthikeyan Ramasubramanian762e52a2012-04-30 12:52:14 -0600575 mutex_unlock(&smd_pkt_devp->ch_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700576
577 return mask;
578}
579
580static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp)
581{
582 int sz;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700583 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700584
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600585 if (!smd_pkt_devp) {
586 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700587 return;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600588 }
589
590 if (!smd_pkt_devp->ch) {
591 pr_err("%s on a closed smd_pkt_dev id:%d\n",
592 __func__, smd_pkt_devp->i);
593 return;
594 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700595
596 sz = smd_cur_packet_size(smd_pkt_devp->ch);
597 if (sz == 0) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600598 D_READ("%s: No packet in smd_pkt_dev id:%d\n",
599 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700600 return;
601 }
Karthikeyan Ramasubramanian703e8a12011-11-08 16:50:06 -0700602 if (!smd_read_avail(smd_pkt_devp->ch)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600603 D_READ("%s: packet size is %d in smd_pkt_dev id:%d -"
604 " but the data isn't here\n",
605 __func__, sz, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700606 return;
607 }
608
609 /* here we have a packet of size sz ready */
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700610 spin_lock_irqsave(&smd_pkt_devp->pa_spinlock, flags);
611 wake_lock(&smd_pkt_devp->pa_wake_lock);
Jeff Hugo93476a32012-06-01 14:32:49 -0600612 smd_pkt_devp->wakelock_locked = 1;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -0700613 spin_unlock_irqrestore(&smd_pkt_devp->pa_spinlock, flags);
Jeff Hugo93476a32012-06-01 14:32:49 -0600614 wake_up(&smd_pkt_devp->ch_read_wait_queue);
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700615 schedule_work(&smd_pkt_devp->packet_arrival_work);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600616 D_READ("%s: wake_up smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700617}
618
619static void check_and_wakeup_writer(struct smd_pkt_dev *smd_pkt_devp)
620{
621 int sz;
622
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600623 if (!smd_pkt_devp) {
624 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700625 return;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600626 }
627
628 if (!smd_pkt_devp->ch) {
629 pr_err("%s on a closed smd_pkt_dev id:%d\n",
630 __func__, smd_pkt_devp->i);
631 return;
632 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700633
Jeff Hugo44fd9832013-04-04 15:56:21 -0600634 sz = smd_write_segment_avail(smd_pkt_devp->ch);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700635 if (sz) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600636 D_WRITE("%s: %d bytes write space in smd_pkt_dev id:%d\n",
637 __func__, sz, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700638 smd_disable_read_intr(smd_pkt_devp->ch);
Karthikeyan Ramasubramaniance2c1152011-11-07 14:17:41 -0700639 wake_up(&smd_pkt_devp->ch_write_wait_queue);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700640 }
641}
642
643static void ch_notify(void *priv, unsigned event)
644{
645 struct smd_pkt_dev *smd_pkt_devp = priv;
646
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600647 if (smd_pkt_devp->ch == 0) {
Eric Holmbergdcdaadd2013-03-13 18:34:11 -0600648 if (event != SMD_EVENT_CLOSE)
649 pr_err("%s on a closed smd_pkt_dev id:%d\n",
650 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700651 return;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600652 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700653
654 switch (event) {
655 case SMD_EVENT_DATA: {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600656 D_STATUS("%s: DATA event in smd_pkt_dev id:%d\n",
657 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700658 check_and_wakeup_reader(smd_pkt_devp);
659 if (smd_pkt_devp->blocking_write)
660 check_and_wakeup_writer(smd_pkt_devp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700661 break;
662 }
663 case SMD_EVENT_OPEN:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600664 D_STATUS("%s: OPEN event in smd_pkt_dev id:%d\n",
665 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700666 smd_pkt_devp->has_reset = 0;
667 smd_pkt_devp->is_open = 1;
668 wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
669 break;
670 case SMD_EVENT_CLOSE:
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600671 D_STATUS("%s: CLOSE event in smd_pkt_dev id:%d\n",
672 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700673 smd_pkt_devp->is_open = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700674 /* put port into reset state */
675 clean_and_signal(smd_pkt_devp);
676 if (smd_pkt_devp->i == LOOPBACK_INX)
677 schedule_delayed_work(&loopback_work,
678 msecs_to_jiffies(1000));
679 break;
680 }
681}
682
683#ifdef CONFIG_ARCH_FSM9XXX
684static char *smd_pkt_dev_name[] = {
685 "smdcntl1",
686 "smdcntl2",
687 "smd22",
688 "smd_pkt_loopback",
689};
690
691static char *smd_ch_name[] = {
692 "DATA6_CNTL",
693 "DATA7_CNTL",
694 "DATA22",
695 "LOOPBACK",
696};
697
698static uint32_t smd_ch_edge[] = {
699 SMD_APPS_QDSP,
700 SMD_APPS_QDSP,
701 SMD_APPS_QDSP,
702 SMD_APPS_QDSP
703};
704#else
705static char *smd_pkt_dev_name[] = {
706 "smdcntl0",
707 "smdcntl1",
708 "smdcntl2",
709 "smdcntl3",
710 "smdcntl4",
711 "smdcntl5",
712 "smdcntl6",
713 "smdcntl7",
Brent Hronik5a883cd2013-07-12 12:05:22 -0600714 "smdcntl9",
715 "smdcntl10",
716 "smdcntl11",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700717 "smd22",
Arun Kumar Neelakantam04a680a2012-10-31 13:48:00 +0530718 "smdcnt_rev0",
719 "smdcnt_rev1",
720 "smdcnt_rev2",
721 "smdcnt_rev3",
722 "smdcnt_rev4",
723 "smdcnt_rev5",
724 "smdcnt_rev6",
725 "smdcnt_rev7",
726 "smdcnt_rev8",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700727 "smd_sns_dsps",
Angshuman Sarkara18a2722011-07-29 13:41:13 +0530728 "apr_apps2",
Karthikeyan Ramasubramaniand08e59a2012-03-01 17:35:34 -0700729 "smdcntl8",
Jeff Hugodf5ee322012-04-16 11:56:12 -0600730 "smd_sns_adsp",
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -0600731 "smd_cxm_qmi",
Eric Holmbergc1e645e2013-02-11 19:17:52 -0700732 "smd_test_framework",
733 "smd_logging_0",
734 "smd_data_0",
Eric Holmberg4f65b7b2013-04-16 18:22:44 -0600735 "apr",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700736 "smd_pkt_loopback",
737};
738
739static char *smd_ch_name[] = {
740 "DATA5_CNTL",
741 "DATA6_CNTL",
742 "DATA7_CNTL",
743 "DATA8_CNTL",
744 "DATA9_CNTL",
745 "DATA12_CNTL",
746 "DATA13_CNTL",
747 "DATA14_CNTL",
Brent Hronik5a883cd2013-07-12 12:05:22 -0600748 "DATA15_CNTL",
749 "DATA16_CNTL",
750 "DATA17_CNTL",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700751 "DATA22",
Arun Kumar Neelakantam04a680a2012-10-31 13:48:00 +0530752 "DATA23_CNTL",
753 "DATA24_CNTL",
754 "DATA25_CNTL",
755 "DATA26_CNTL",
756 "DATA27_CNTL",
757 "DATA28_CNTL",
758 "DATA29_CNTL",
759 "DATA30_CNTL",
760 "DATA31_CNTL",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700761 "SENSOR",
Angshuman Sarkara18a2722011-07-29 13:41:13 +0530762 "apr_apps2",
Karthikeyan Ramasubramaniand08e59a2012-03-01 17:35:34 -0700763 "DATA40_CNTL",
Jeff Hugodf5ee322012-04-16 11:56:12 -0600764 "SENSOR",
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -0600765 "CXM_QMI_PORT_8064",
Eric Holmbergc1e645e2013-02-11 19:17:52 -0700766 "TESTFRAMEWORK",
767 "LOGGING",
768 "DATA",
Eric Holmberg4f65b7b2013-04-16 18:22:44 -0600769 "apr",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700770 "LOOPBACK",
771};
772
773static uint32_t smd_ch_edge[] = {
774 SMD_APPS_MODEM,
775 SMD_APPS_MODEM,
776 SMD_APPS_MODEM,
777 SMD_APPS_MODEM,
778 SMD_APPS_MODEM,
779 SMD_APPS_MODEM,
780 SMD_APPS_MODEM,
781 SMD_APPS_MODEM,
782 SMD_APPS_MODEM,
Arun Kumar Neelakantam04a680a2012-10-31 13:48:00 +0530783 SMD_APPS_MODEM,
784 SMD_APPS_MODEM,
785 SMD_APPS_MODEM,
786 SMD_APPS_MODEM,
787 SMD_APPS_MODEM,
788 SMD_APPS_MODEM,
789 SMD_APPS_MODEM,
790 SMD_APPS_MODEM,
791 SMD_APPS_MODEM,
Brent Hronik5a883cd2013-07-12 12:05:22 -0600792 SMD_APPS_MODEM,
793 SMD_APPS_MODEM,
794 SMD_APPS_MODEM,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700795 SMD_APPS_DSPS,
796 SMD_APPS_QDSP,
797 SMD_APPS_MODEM,
Jeff Hugodf5ee322012-04-16 11:56:12 -0600798 SMD_APPS_QDSP,
Karthikeyan Ramasubramanianb7e72732012-05-22 12:31:31 -0600799 SMD_APPS_WCNSS,
Eric Holmbergc1e645e2013-02-11 19:17:52 -0700800 SMD_APPS_QDSP,
801 SMD_APPS_QDSP,
802 SMD_APPS_QDSP,
Eric Holmberg4f65b7b2013-04-16 18:22:44 -0600803 SMD_APPS_QDSP,
Karthikeyan Ramasubramaniand08e59a2012-03-01 17:35:34 -0700804 SMD_APPS_MODEM,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700805};
806#endif
Jeff Hugo223fd452012-07-18 14:35:45 -0600807module_param_named(loopback_edge, smd_ch_edge[LOOPBACK_INX],
808 int, S_IRUGO | S_IWUSR | S_IWGRP);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700809
810static int smd_pkt_dummy_probe(struct platform_device *pdev)
811{
812 int i;
813
814 for (i = 0; i < NUM_SMD_PKT_PORTS; i++) {
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600815 if (smd_ch_edge[i] == pdev->id
816 && !strncmp(pdev->name, smd_ch_name[i],
817 SMD_MAX_CH_NAME_LEN)
818 && smd_pkt_devp[i]->driver.probe) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700819 complete_all(&smd_pkt_devp[i]->ch_allocated);
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600820 D_STATUS("%s allocated SMD ch for smd_pkt_dev id:%d\n",
821 __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700822 break;
823 }
824 }
825 return 0;
826}
827
828static uint32_t is_modem_smsm_inited(void)
829{
830 uint32_t modem_state;
831 uint32_t ready_state = (SMSM_INIT | SMSM_SMDINIT);
832
833 modem_state = smsm_get_state(SMSM_MODEM_STATE);
834 return (modem_state & ready_state) == ready_state;
835}
836
837int smd_pkt_open(struct inode *inode, struct file *file)
838{
839 int r = 0;
840 struct smd_pkt_dev *smd_pkt_devp;
Eric Holmbergcce6daf2012-02-27 14:34:02 -0700841 const char *peripheral = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700842
843 smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev);
844
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600845 if (!smd_pkt_devp) {
846 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700847 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600848 }
849 D_STATUS("Begin %s on smd_pkt_dev id:%d\n", __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700850
851 file->private_data = smd_pkt_devp;
852
853 mutex_lock(&smd_pkt_devp->ch_lock);
854 if (smd_pkt_devp->ch == 0) {
Eric Holmbergb399a4f2012-07-06 11:42:24 -0600855 wake_lock_init(&smd_pkt_devp->pa_wake_lock, WAKE_LOCK_SUSPEND,
856 smd_pkt_dev_name[smd_pkt_devp->i]);
857 INIT_WORK(&smd_pkt_devp->packet_arrival_work,
858 packet_arrival_worker);
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700859 init_completion(&smd_pkt_devp->ch_allocated);
860 smd_pkt_devp->driver.probe = smd_pkt_dummy_probe;
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600861 scnprintf(smd_pkt_devp->pdriver_name, PDRIVER_NAME_MAX_SIZE,
Karthikeyan Ramasubramanian61df1932012-06-28 12:10:41 -0600862 "%s", smd_ch_name[smd_pkt_devp->i]);
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600863 smd_pkt_devp->driver.driver.name = smd_pkt_devp->pdriver_name;
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700864 smd_pkt_devp->driver.driver.owner = THIS_MODULE;
865 r = platform_driver_register(&smd_pkt_devp->driver);
866 if (r) {
867 pr_err("%s: %s Platform driver reg. failed\n",
868 __func__, smd_ch_name[smd_pkt_devp->i]);
869 goto out;
870 }
871
Eric Holmbergcce6daf2012-02-27 14:34:02 -0700872 peripheral = smd_edge_to_subsystem(
873 smd_ch_edge[smd_pkt_devp->i]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700874 if (peripheral) {
Stephen Boyd77db8bb2012-06-27 15:15:16 -0700875 smd_pkt_devp->pil = subsystem_get(peripheral);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700876 if (IS_ERR(smd_pkt_devp->pil)) {
877 r = PTR_ERR(smd_pkt_devp->pil);
Stephen Boyd77db8bb2012-06-27 15:15:16 -0700878 pr_err("%s failed on smd_pkt_dev id:%d - subsystem_get failed for %s\n",
879 __func__, smd_pkt_devp->i, peripheral);
Karthikeyan Ramasubramanian608a2bc2012-12-21 18:52:33 -0700880 /*
881 * Sleep inorder to reduce the frequency of
882 * retry by user-space modules and to avoid
883 * possible watchdog bite.
884 */
885 msleep((smd_pkt_devp->open_modem_wait * 1000));
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700886 goto release_pd;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700887 }
888
889 /* Wait for the modem SMSM to be inited for the SMD
890 ** Loopback channel to be allocated at the modem. Since
891 ** the wait need to be done atmost once, using msleep
892 ** doesn't degrade the performance. */
Jeff Hugoa5ee4362011-07-15 13:48:48 -0600893 if (!strncmp(smd_ch_name[smd_pkt_devp->i], "LOOPBACK",
894 SMD_MAX_CH_NAME_LEN)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700895 if (!is_modem_smsm_inited())
896 msleep(5000);
897 smsm_change_state(SMSM_APPS_STATE,
898 0, SMSM_SMD_LOOPBACK);
899 msleep(100);
900 }
901
902 /*
903 * Wait for a packet channel to be allocated so we know
904 * the modem is ready enough.
905 */
906 if (smd_pkt_devp->open_modem_wait) {
907 r = wait_for_completion_interruptible_timeout(
908 &smd_pkt_devp->ch_allocated,
909 msecs_to_jiffies(
910 smd_pkt_devp->open_modem_wait
911 * 1000));
912 if (r == 0)
913 r = -ETIMEDOUT;
914 if (r < 0) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600915 pr_err("%s: wait on smd_pkt_dev id:%d"
916 " allocation failed rc:%d\n",
917 __func__, smd_pkt_devp->i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700918 goto release_pil;
919 }
920 }
921 }
922
923 r = smd_named_open_on_edge(smd_ch_name[smd_pkt_devp->i],
924 smd_ch_edge[smd_pkt_devp->i],
925 &smd_pkt_devp->ch,
926 smd_pkt_devp,
927 ch_notify);
928 if (r < 0) {
929 pr_err("%s: %s open failed %d\n", __func__,
930 smd_ch_name[smd_pkt_devp->i], r);
931 goto release_pil;
932 }
933
934 r = wait_event_interruptible_timeout(
935 smd_pkt_devp->ch_opened_wait_queue,
936 smd_pkt_devp->is_open, (2 * HZ));
Jeff Hugo4d9fdf32012-04-30 11:10:54 -0600937 if (r == 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700938 r = -ETIMEDOUT;
Jeff Hugo4d9fdf32012-04-30 11:10:54 -0600939 /* close the ch to sync smd's state with smd_pkt */
940 smd_close(smd_pkt_devp->ch);
941 smd_pkt_devp->ch = NULL;
942 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700943
944 if (r < 0) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600945 pr_err("%s: wait on smd_pkt_dev id:%d OPEN event failed"
946 " rc:%d\n", __func__, smd_pkt_devp->i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700947 } else if (!smd_pkt_devp->is_open) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600948 pr_err("%s: Invalid OPEN event on smd_pkt_dev id:%d\n",
949 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700950 r = -ENODEV;
951 } else {
952 smd_disable_read_intr(smd_pkt_devp->ch);
953 smd_pkt_devp->ch_size =
954 smd_write_avail(smd_pkt_devp->ch);
955 r = 0;
Karthikeyan Ramasubramanian540dbf32012-09-14 13:16:50 -0600956 smd_pkt_devp->ref_cnt++;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600957 D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
958 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700959 }
Karthikeyan Ramasubramanian540dbf32012-09-14 13:16:50 -0600960 } else {
961 smd_pkt_devp->ref_cnt++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700962 }
963release_pil:
964 if (peripheral && (r < 0))
Stephen Boyd77db8bb2012-06-27 15:15:16 -0700965 subsystem_put(smd_pkt_devp->pil);
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700966
967release_pd:
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600968 if (r < 0) {
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -0700969 platform_driver_unregister(&smd_pkt_devp->driver);
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -0600970 smd_pkt_devp->driver.probe = NULL;
971 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700972out:
Eric Holmbergb399a4f2012-07-06 11:42:24 -0600973 if (!smd_pkt_devp->ch)
974 wake_lock_destroy(&smd_pkt_devp->pa_wake_lock);
975
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700976 mutex_unlock(&smd_pkt_devp->ch_lock);
977
Eric Holmbergc3c5cd92012-02-07 18:19:49 -0700978
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700979 return r;
980}
981
982int smd_pkt_release(struct inode *inode, struct file *file)
983{
984 int r = 0;
985 struct smd_pkt_dev *smd_pkt_devp = file->private_data;
986
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600987 if (!smd_pkt_devp) {
988 pr_err("%s on a NULL device\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700989 return -EINVAL;
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -0600990 }
991 D_STATUS("Begin %s on smd_pkt_dev id:%d\n",
992 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700993
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700994 mutex_lock(&smd_pkt_devp->ch_lock);
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -0600995 mutex_lock(&smd_pkt_devp->rx_lock);
996 mutex_lock(&smd_pkt_devp->tx_lock);
Karthikeyan Ramasubramanian540dbf32012-09-14 13:16:50 -0600997 if (smd_pkt_devp->ref_cnt > 0)
998 smd_pkt_devp->ref_cnt--;
999
1000 if (smd_pkt_devp->ch != 0 && smd_pkt_devp->ref_cnt == 0) {
1001 clean_and_signal(smd_pkt_devp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001002 r = smd_close(smd_pkt_devp->ch);
1003 smd_pkt_devp->ch = 0;
1004 smd_pkt_devp->blocking_write = 0;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -07001005 smd_pkt_devp->poll_mode = 0;
Karthikeyan Ramasubramanian63a913c2012-03-06 15:43:48 -07001006 platform_driver_unregister(&smd_pkt_devp->driver);
Karthikeyan Ramasubramanian5fa88252012-04-19 23:45:08 -06001007 smd_pkt_devp->driver.probe = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001008 if (smd_pkt_devp->pil)
Stephen Boyd77db8bb2012-06-27 15:15:16 -07001009 subsystem_put(smd_pkt_devp->pil);
Karthikeyan Ramasubramanian540dbf32012-09-14 13:16:50 -06001010 smd_pkt_devp->has_reset = 0;
1011 smd_pkt_devp->do_reset_notification = 0;
1012 smd_pkt_devp->wakelock_locked = 0;
1013 wake_lock_destroy(&smd_pkt_devp->pa_wake_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001014 }
Karthikeyan Ramasubramanian96a14fb2012-04-30 12:04:35 -06001015 mutex_unlock(&smd_pkt_devp->tx_lock);
1016 mutex_unlock(&smd_pkt_devp->rx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001017 mutex_unlock(&smd_pkt_devp->ch_lock);
1018
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001019 D_STATUS("Finished %s on smd_pkt_dev id:%d\n",
1020 __func__, smd_pkt_devp->i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001021
1022 return r;
1023}
1024
1025static const struct file_operations smd_pkt_fops = {
1026 .owner = THIS_MODULE,
1027 .open = smd_pkt_open,
1028 .release = smd_pkt_release,
1029 .read = smd_pkt_read,
1030 .write = smd_pkt_write,
1031 .poll = smd_pkt_poll,
1032 .unlocked_ioctl = smd_pkt_ioctl,
1033};
1034
1035static int __init smd_pkt_init(void)
1036{
1037 int i;
1038 int r;
1039
Brent Hronikbd4a10e2013-07-12 15:03:21 -06001040 if (ARRAY_SIZE(smd_ch_name) != NUM_SMD_PKT_PORTS ||
1041 ARRAY_SIZE(smd_ch_edge) != NUM_SMD_PKT_PORTS ||
1042 ARRAY_SIZE(smd_pkt_dev_name) != NUM_SMD_PKT_PORTS) {
1043 pr_err("%s: mismatch in number of ports\n", __func__);
1044 BUG();
1045 }
1046
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001047 r = alloc_chrdev_region(&smd_pkt_number,
1048 0,
1049 NUM_SMD_PKT_PORTS,
1050 DEVICE_NAME);
1051 if (IS_ERR_VALUE(r)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001052 pr_err("%s: alloc_chrdev_region() failed ret:%i\n",
1053 __func__, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001054 goto error0;
1055 }
1056
1057 smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME);
1058 if (IS_ERR(smd_pkt_classp)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001059 pr_err("%s: class_create() failed ENOMEM\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001060 r = -ENOMEM;
1061 goto error1;
1062 }
1063
1064 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
1065 smd_pkt_devp[i] = kzalloc(sizeof(struct smd_pkt_dev),
1066 GFP_KERNEL);
1067 if (IS_ERR(smd_pkt_devp[i])) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001068 pr_err("%s: kzalloc() failed for smd_pkt_dev id:%d\n",
1069 __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001070 r = -ENOMEM;
1071 goto error2;
1072 }
1073
1074 smd_pkt_devp[i]->i = i;
1075
1076 init_waitqueue_head(&smd_pkt_devp[i]->ch_read_wait_queue);
1077 init_waitqueue_head(&smd_pkt_devp[i]->ch_write_wait_queue);
1078 smd_pkt_devp[i]->is_open = 0;
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -07001079 smd_pkt_devp[i]->poll_mode = 0;
Jeff Hugo93476a32012-06-01 14:32:49 -06001080 smd_pkt_devp[i]->wakelock_locked = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001081 init_waitqueue_head(&smd_pkt_devp[i]->ch_opened_wait_queue);
1082
Karthikeyan Ramasubramanian048ef382012-02-15 10:49:10 -07001083 spin_lock_init(&smd_pkt_devp[i]->pa_spinlock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001084 mutex_init(&smd_pkt_devp[i]->ch_lock);
1085 mutex_init(&smd_pkt_devp[i]->rx_lock);
1086 mutex_init(&smd_pkt_devp[i]->tx_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001087
1088 cdev_init(&smd_pkt_devp[i]->cdev, &smd_pkt_fops);
1089 smd_pkt_devp[i]->cdev.owner = THIS_MODULE;
1090
1091 r = cdev_add(&smd_pkt_devp[i]->cdev,
1092 (smd_pkt_number + i),
1093 1);
1094
1095 if (IS_ERR_VALUE(r)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001096 pr_err("%s: cdev_add() failed for smd_pkt_dev id:%d"
1097 " ret:%i\n", __func__, i, r);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001098 kfree(smd_pkt_devp[i]);
1099 goto error2;
1100 }
1101
1102 smd_pkt_devp[i]->devicep =
1103 device_create(smd_pkt_classp,
1104 NULL,
1105 (smd_pkt_number + i),
1106 NULL,
1107 smd_pkt_dev_name[i]);
1108
1109 if (IS_ERR(smd_pkt_devp[i]->devicep)) {
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001110 pr_err("%s: device_create() failed for smd_pkt_dev"
1111 " id:%d\n", __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001112 r = -ENOMEM;
1113 cdev_del(&smd_pkt_devp[i]->cdev);
1114 kfree(smd_pkt_devp[i]);
1115 goto error2;
1116 }
1117 if (device_create_file(smd_pkt_devp[i]->devicep,
1118 &dev_attr_open_timeout))
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001119 pr_err("%s: unable to create device attr for"
1120 " smd_pkt_dev id:%d\n", __func__, i);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001121 }
1122
1123 INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
1124
Karthikeyan Ramasubramanian085bd622012-09-10 21:45:00 -06001125 smd_pkt_ilctxt = ipc_log_context_create(SMD_PKT_IPC_LOG_PAGE_CNT,
1126 "smd_pkt");
1127
Karthikeyan Ramasubramanian05142ff2012-03-30 17:39:24 -06001128 D_STATUS("SMD Packet Port Driver Initialized.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001129 return 0;
1130
1131 error2:
1132 if (i > 0) {
1133 while (--i >= 0) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001134 cdev_del(&smd_pkt_devp[i]->cdev);
1135 kfree(smd_pkt_devp[i]);
1136 device_destroy(smd_pkt_classp,
1137 MKDEV(MAJOR(smd_pkt_number), i));
1138 }
1139 }
1140
1141 class_destroy(smd_pkt_classp);
1142 error1:
1143 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
1144 error0:
1145 return r;
1146}
1147
1148static void __exit smd_pkt_cleanup(void)
1149{
1150 int i;
1151
1152 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001153 cdev_del(&smd_pkt_devp[i]->cdev);
1154 kfree(smd_pkt_devp[i]);
1155 device_destroy(smd_pkt_classp,
1156 MKDEV(MAJOR(smd_pkt_number), i));
1157 }
1158
1159 class_destroy(smd_pkt_classp);
1160
1161 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
1162}
1163
1164module_init(smd_pkt_init);
1165module_exit(smd_pkt_cleanup);
1166
1167MODULE_DESCRIPTION("MSM Shared Memory Packet Port");
1168MODULE_LICENSE("GPL v2");