blob: dcbfe9a11d7e675ce4bc5ec5a51b3c55f6db788c [file] [log] [blame]
Eric Holmberg9aa0d472013-04-05 15:33:56 -06001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Eric Holmberg9fff8872012-05-10 19:20:27 -06002 *
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 * Serial Mux Control Driver -- Provides a binary serial muxed control
15 * port interface.
16 */
17
18#define DEBUG
19
20#include <linux/cdev.h>
21#include <linux/types.h>
22#include <linux/module.h>
23#include <linux/fs.h>
24#include <linux/device.h>
25#include <linux/delay.h>
26#include <linux/sched.h>
27#include <linux/spinlock.h>
28#include <linux/mutex.h>
29#include <linux/uaccess.h>
30#include <linux/workqueue.h>
31#include <linux/atomic.h>
32#include <linux/platform_device.h>
33#include <linux/smux.h>
34#include <linux/slab.h>
35#include <linux/debugfs.h>
Eric Holmberg58a677b2012-08-02 11:23:17 -060036#include <linux/poll.h>
Eric Holmberg9fff8872012-05-10 19:20:27 -060037
38#include <asm/ioctls.h>
39
40#define MAX_WRITE_RETRY 5
41#define MAGIC_NO_V1 0x33FC
42#define DEVICE_NAME "smuxctl"
43#define SMUX_CTL_MAX_BUF_SIZE 2048
44#define SMUX_CTL_MODULE_NAME "smux_ctl"
45#define DEBUG
Eric Holmberg9fff8872012-05-10 19:20:27 -060046
47static int msm_smux_ctl_debug_mask;
48module_param_named(debug_mask, msm_smux_ctl_debug_mask,
49 int, S_IRUGO | S_IWUSR | S_IWGRP);
50
51static uint32_t smux_ctl_ch_id[] = {
52 SMUX_DATA_CTL_0,
Arun Kumar Neelakantambed63682013-06-07 15:16:52 +053053 SMUX_DATA_CTL_1,
Eric Holmberg9fff8872012-05-10 19:20:27 -060054};
55
56#define SMUX_CTL_NUM_CHANNELS ARRAY_SIZE(smux_ctl_ch_id)
Angshuman Sarkar76db1412012-07-25 15:17:19 +053057#define DEFAULT_OPEN_TIMEOUT 5
Eric Holmberg9fff8872012-05-10 19:20:27 -060058
59struct smux_ctl_dev {
60 int id;
61 char name[10];
62 struct cdev cdev;
63 struct device *devicep;
64 struct mutex dev_lock;
65 atomic_t ref_count;
66 int state;
67 int is_channel_reset;
68 int is_high_wm;
69 int write_pending;
Angshuman Sarkar76db1412012-07-25 15:17:19 +053070 unsigned open_timeout_val;
Eric Holmberg9fff8872012-05-10 19:20:27 -060071
72 struct mutex rx_lock;
73 uint32_t read_avail;
74 struct list_head rx_list;
75
Eric Holmbergd6704e32012-06-19 08:53:02 -060076 int abort_wait;
Eric Holmberg9fff8872012-05-10 19:20:27 -060077 wait_queue_head_t read_wait_queue;
78 wait_queue_head_t write_wait_queue;
79
80 struct {
81 uint32_t bytes_tx;
82 uint32_t bytes_rx;
83 uint32_t pkts_tx;
84 uint32_t pkts_rx;
85 uint32_t cnt_ssr;
86 uint32_t cnt_read_fail;
87 uint32_t cnt_write_fail;
88 uint32_t cnt_high_wm_hit;
89 } stats;
90
91} *smux_ctl_devp[SMUX_CTL_NUM_CHANNELS];
92
93struct smux_ctl_pkt {
94 int data_size;
95 void *data;
96};
97
98struct smux_ctl_list_elem {
99 struct list_head list;
100 struct smux_ctl_pkt ctl_pkt;
101};
102
103struct class *smux_ctl_classp;
104static dev_t smux_ctl_number;
105static uint32_t smux_ctl_inited;
106
107enum {
108 MSM_SMUX_CTL_DEBUG = 1U << 0,
109 MSM_SMUX_CTL_DUMP_BUFFER = 1U << 1,
110};
111
112#if defined(DEBUG)
113
114static const char *smux_ctl_event_str[] = {
115 "SMUX_CONNECTED",
116 "SMUX_DISCONNECTED",
117 "SMUX_READ_DONE",
118 "SMUX_READ_FAIL",
119 "SMUX_WRITE_DONE",
120 "SMUX_WRITE_FAIL",
121 "SMUX_TIOCM_UPDATE",
122 "SMUX_LOW_WM_HIT",
123 "SMUX_HIGH_WM_HIT",
124};
125
126#define SMUXCTL_DUMP_BUFFER(prestr, cnt, buf) \
127do { \
128 if (msm_smux_ctl_debug_mask & MSM_SMUX_CTL_DUMP_BUFFER) { \
129 int i; \
130 pr_err("%s", prestr); \
131 for (i = 0; i < cnt; i++) \
132 pr_err("%.2x", buf[i]); \
133 pr_err("\n"); \
134 } \
135} while (0)
136
137#define SMUXCTL_DBG(x...) \
138do { \
139 if (msm_smux_ctl_debug_mask & MSM_SMUX_CTL_DEBUG) \
140 pr_err(x); \
141} while (0)
142
143
144#else
145#define SMUXCTL_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
146#define SMUXCTL_DBG(x...) do {} while (0)
147#endif
148
149#if defined(DEBUG_LOOPBACK)
150#define SMUXCTL_SET_LOOPBACK(lcid) \
151 msm_smux_set_ch_option(lcid, SMUX_CH_OPTION_LOCAL_LOOPBACK, 0)
152#else
153#define SMUXCTL_SET_LOOPBACK(lcid) do {} while (0)
154#endif
155
Angshuman Sarkar76db1412012-07-25 15:17:19 +0530156static ssize_t open_timeout_store(struct device *d,
157 struct device_attribute *attr,
158 const char *buf,
159 size_t n)
160{
161 int i;
162 unsigned long tmp;
163 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
164 if (smux_ctl_devp[i]->devicep == d)
165 break;
166 }
167 if (i >= SMUX_CTL_NUM_CHANNELS) {
168 pr_err("%s: unable to match device to valid smux ctl port\n",
169 __func__);
170 return -EINVAL;
171 }
172 if (!kstrtoul(buf, 10, &tmp)) {
173 smux_ctl_devp[i]->open_timeout_val = tmp;
174 return n;
175 } else {
176 pr_err("%s: unable to convert: %s to an int\n", __func__,
177 buf);
178 return -EINVAL;
179 }
180}
181
182static ssize_t open_timeout_show(struct device *d,
183 struct device_attribute *attr,
184 char *buf)
185{
186 int i;
187 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
188 if (smux_ctl_devp[i]->devicep == d)
189 break;
190 }
191 if (i >= SMUX_CTL_NUM_CHANNELS) {
192 pr_err("%s: unable to match device to valid smux ctl port\n",
193 __func__);
194 return -EINVAL;
195 }
196 return snprintf(buf, PAGE_SIZE, "%d\n",
197 smux_ctl_devp[i]->open_timeout_val);
198}
199
200static DEVICE_ATTR(open_timeout, 0664, open_timeout_show, open_timeout_store);
201
Eric Holmberg9fff8872012-05-10 19:20:27 -0600202static int get_ctl_dev_index(int id)
203{
204 int dev_index;
205 for (dev_index = 0; dev_index < SMUX_CTL_NUM_CHANNELS; dev_index++) {
206 if (smux_ctl_ch_id[dev_index] == id)
207 return dev_index;
208 }
209 return -ENODEV;
210}
211
212static int smux_ctl_get_rx_buf_cb(void *priv, void **pkt_priv,
213 void **buffer, int size)
214{
215 void *buf = NULL;
216 int id = ((struct smux_ctl_dev *)(priv))->id;
217 int dev_index;
218
219 if (id < 0 || id > smux_ctl_ch_id[SMUX_CTL_NUM_CHANNELS - 1])
220 return -ENODEV;
221
222 if (!buffer || 0 >= size)
223 return -EINVAL;
224
225 dev_index = get_ctl_dev_index(id);
226 if (dev_index < 0) {
227 pr_err(SMUX_CTL_MODULE_NAME ": %s: Ch%d is not "
228 "exported to user-space\n",
229 __func__, id);
230 return -ENODEV;
231 }
232
233 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: Allocating Rx buf size %d "
234 "for ch%d\n",
235 __func__, size, smux_ctl_devp[dev_index]->id);
236
237 buf = kmalloc(size, GFP_KERNEL);
238 if (!buf) {
239 pr_err(SMUX_CTL_MODULE_NAME ": %s: buffer allocation failed: "
240 "Ch%d, size %d ", __func__, id, size);
241 return -ENOMEM;
242 }
243
244 *buffer = buf;
245 *pkt_priv = NULL;
246 return 0;
247
248}
249
250void smux_ctl_notify_cb(void *priv, int event_type, const void *metadata)
251{
252 int id = ((struct smux_ctl_dev *)(priv))->id;
253 struct smux_ctl_list_elem *list_elem = NULL;
254 int dev_index;
255 void *data;
256 int len;
257
258 if (id < 0 || id > smux_ctl_ch_id[SMUX_CTL_NUM_CHANNELS - 1])
259 return;
260
261 dev_index = get_ctl_dev_index(id);
262 if (dev_index < 0) {
263 pr_err(SMUX_CTL_MODULE_NAME ": %s: Ch%d is not exported "
264 "to user-space\n", __func__, id);
265 return;
266 }
267
268 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: Ch%d, Event %d (%s)\n",
269 __func__, smux_ctl_devp[dev_index]->id,
270 event_type, smux_ctl_event_str[event_type]);
271
272
273 switch (event_type) {
274 case SMUX_CONNECTED:
275 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
276 smux_ctl_devp[dev_index]->state = SMUX_CONNECTED;
277 smux_ctl_devp[dev_index]->is_high_wm = 0;
278 smux_ctl_devp[dev_index]->is_channel_reset = 0;
279 smux_ctl_devp[dev_index]->read_avail = 0;
280 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
281 wake_up(&smux_ctl_devp[dev_index]->write_wait_queue);
282 break;
283
284 case SMUX_DISCONNECTED:
285 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
286 smux_ctl_devp[dev_index]->state = SMUX_DISCONNECTED;
287 smux_ctl_devp[dev_index]->is_channel_reset =
288 ((struct smux_meta_disconnected *)metadata)->is_ssr;
289 if (smux_ctl_devp[dev_index]->is_channel_reset)
290 smux_ctl_devp[dev_index]->stats.cnt_ssr++;
291 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
292 wake_up(&smux_ctl_devp[dev_index]->write_wait_queue);
293 wake_up(&smux_ctl_devp[dev_index]->read_wait_queue);
294 break;
295
296 case SMUX_READ_FAIL:
297 data = ((struct smux_meta_read *)metadata)->buffer;
298 kfree(data);
299 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
300 smux_ctl_devp[dev_index]->stats.cnt_read_fail++;
301 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
302 wake_up(&smux_ctl_devp[dev_index]->read_wait_queue);
303 break;
304
305 case SMUX_READ_DONE:
306 data = ((struct smux_meta_read *)metadata)->buffer;
307 len = ((struct smux_meta_read *)metadata)->len;
308
309 if (data && len > 0) {
310 list_elem = kmalloc(sizeof(struct smux_ctl_list_elem),
311 GFP_KERNEL);
312 if (list_elem) {
313 list_elem->ctl_pkt.data = data;
314 list_elem->ctl_pkt.data_size = len;
315
316 mutex_lock(&smux_ctl_devp[dev_index]->rx_lock);
317 list_add_tail(&list_elem->list,
318 &smux_ctl_devp[dev_index]->rx_list);
319 smux_ctl_devp[dev_index]->read_avail += len;
320 mutex_unlock(
321 &smux_ctl_devp[dev_index]->rx_lock);
322 } else {
323 kfree(data);
324 }
325 }
326
327 wake_up(&smux_ctl_devp[dev_index]->read_wait_queue);
328 break;
329
330 case SMUX_WRITE_DONE:
331 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
332 smux_ctl_devp[dev_index]->write_pending = 0;
333 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
334 data = ((struct smux_meta_write *)metadata)->buffer;
335 kfree(data);
336 wake_up(&smux_ctl_devp[dev_index]->write_wait_queue);
337 break;
338
339 case SMUX_WRITE_FAIL:
340 data = ((struct smux_meta_write *)metadata)->buffer;
341 kfree(data);
342 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
343 smux_ctl_devp[dev_index]->stats.cnt_write_fail++;
344 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
345 wake_up(&smux_ctl_devp[dev_index]->write_wait_queue);
346 break;
347
348 case SMUX_LOW_WM_HIT:
349 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
350 smux_ctl_devp[dev_index]->is_high_wm = 0;
351 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
352 wake_up(&smux_ctl_devp[dev_index]->write_wait_queue);
353 break;
354
355 case SMUX_HIGH_WM_HIT:
356 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
357 smux_ctl_devp[dev_index]->is_high_wm = 1;
358 smux_ctl_devp[dev_index]->stats.cnt_high_wm_hit++;
359 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
360 break;
361
362 case SMUX_TIOCM_UPDATE:
363 default:
364 pr_err(SMUX_CTL_MODULE_NAME ": %s: Event %d not supported\n",
365 __func__, event_type);
366 break;
367
368 }
369
370}
371
372int smux_ctl_open(struct inode *inode, struct file *file)
373{
374 int r = 0;
375 struct smux_ctl_dev *devp;
Angshuman Sarkar76db1412012-07-25 15:17:19 +0530376 unsigned wait_time = DEFAULT_OPEN_TIMEOUT * HZ;
Eric Holmberg9fff8872012-05-10 19:20:27 -0600377
378 if (!smux_ctl_inited)
379 return -EIO;
380
381 devp = container_of(inode->i_cdev, struct smux_ctl_dev, cdev);
382 if (!devp)
383 return -ENODEV;
384
385 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s called on smuxctl%d device\n",
386 __func__, devp->id);
387
388 if (1 == atomic_add_return(1, &devp->ref_count)) {
389
390 SMUXCTL_SET_LOOPBACK(devp->id);
391 r = msm_smux_open(devp->id,
392 devp,
393 smux_ctl_notify_cb,
394 smux_ctl_get_rx_buf_cb);
395 if (r < 0) {
396 pr_err(SMUX_CTL_MODULE_NAME ": %s: smux_open failed "
397 "for smuxctl%d with rc %d\n",
398 __func__, devp->id, r);
399 atomic_dec(&devp->ref_count);
400 return r;
401 }
402
Angshuman Sarkar76db1412012-07-25 15:17:19 +0530403 if (devp->open_timeout_val)
404 wait_time = devp->open_timeout_val * HZ;
405
Eric Holmberg9fff8872012-05-10 19:20:27 -0600406 r = wait_event_interruptible_timeout(
407 devp->write_wait_queue,
Eric Holmbergd6704e32012-06-19 08:53:02 -0600408 (devp->state == SMUX_CONNECTED ||
Angshuman Sarkar76db1412012-07-25 15:17:19 +0530409 devp->abort_wait),
410 wait_time);
Eric Holmberg9fff8872012-05-10 19:20:27 -0600411 if (r == 0)
412 r = -ETIMEDOUT;
413
414 if (r < 0) {
415 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
416 "SMUX open timed out: %d, LCID %d\n",
417 __func__, r, devp->id);
418 atomic_dec(&devp->ref_count);
419 msm_smux_close(devp->id);
420 return r;
421
Eric Holmbergd6704e32012-06-19 08:53:02 -0600422 } else if (devp->abort_wait) {
423 pr_err("%s: %s: Open command aborted\n",
424 SMUX_CTL_MODULE_NAME, __func__);
425 r = -EIO;
426 atomic_dec(&devp->ref_count);
427 msm_smux_close(devp->id);
428 return r;
Eric Holmberg9fff8872012-05-10 19:20:27 -0600429 } else if (devp->state != SMUX_CONNECTED) {
430 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
431 "Invalid open notification\n", __func__);
432 r = -ENODEV;
433 atomic_dec(&devp->ref_count);
434 msm_smux_close(devp->id);
435 return r;
436 }
437 }
438
439 file->private_data = devp;
440 return 0;
441}
442
443int smux_ctl_release(struct inode *inode, struct file *file)
444{
445 struct smux_ctl_dev *devp;
446 struct smux_ctl_list_elem *list_elem = NULL;
447
448 devp = file->private_data;
449 if (!devp)
450 return -EINVAL;
451
452 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s called on smuxctl%d device\n",
453 __func__, devp->id);
454
455 mutex_lock(&devp->dev_lock);
456 if (atomic_dec_and_test(&devp->ref_count)) {
457 mutex_lock(&devp->rx_lock);
458 while (!list_empty(&devp->rx_list)) {
459 list_elem = list_first_entry(
460 &devp->rx_list,
461 struct smux_ctl_list_elem,
462 list);
463 list_del(&list_elem->list);
464 kfree(list_elem->ctl_pkt.data);
465 kfree(list_elem);
466 }
467 devp->read_avail = 0;
468 mutex_unlock(&devp->rx_lock);
469 msm_smux_close(devp->id);
470 }
471 mutex_unlock(&devp->dev_lock);
472 file->private_data = NULL;
473
474 return 0;
475}
476
477static int smux_ctl_readable(int id)
478{
479 int r;
480 int dev_index;
481
482 if (id < 0 || id > smux_ctl_ch_id[SMUX_CTL_NUM_CHANNELS - 1])
483 return -ENODEV;
484
485 dev_index = get_ctl_dev_index(id);
486 if (dev_index < 0) {
487 pr_err(SMUX_CTL_MODULE_NAME ": %s: Ch%d "
488 "is not exported to user-space\n",
489 __func__, id);
490 return -ENODEV;
491 }
492
493 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
494
495 if (signal_pending(current))
496 r = -ERESTARTSYS;
Eric Holmbergd6704e32012-06-19 08:53:02 -0600497 else if (smux_ctl_devp[dev_index]->abort_wait)
498 r = -ENETRESET;
499 else if (smux_ctl_devp[dev_index]->state == SMUX_DISCONNECTED &&
Eric Holmberg9fff8872012-05-10 19:20:27 -0600500 smux_ctl_devp[dev_index]->is_channel_reset != 0)
501 r = -ENETRESET;
502
503 else if (smux_ctl_devp[dev_index]->state != SMUX_CONNECTED)
504 r = -ENODEV;
505
506 else
507 r = smux_ctl_devp[dev_index]->read_avail;
508
509
510 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
511
512 return r;
513
514}
515
516ssize_t smux_ctl_read(struct file *file,
517 char __user *buf,
518 size_t count,
519 loff_t *ppos)
520{
521 int r = 0, id, bytes_to_read, read_err;
522 struct smux_ctl_dev *devp;
523 struct smux_ctl_list_elem *list_elem = NULL;
524
525 devp = file->private_data;
526
527 if (!devp)
528 return -ENODEV;
529
530 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: read from ch%d\n",
531 __func__, devp->id);
532
533 id = devp->id;
534 mutex_lock(&devp->rx_lock);
535 while (devp->read_avail <= 0) {
536 mutex_unlock(&devp->rx_lock);
537 r = wait_event_interruptible(devp->read_wait_queue,
538 0 != (read_err = smux_ctl_readable(id)));
539
540 if (r < 0) {
541 pr_err(SMUX_CTL_MODULE_NAME ": %s:"
542 "wait_event_interruptible "
543 "ret %i\n", __func__, r);
544 return r;
545 }
546
547 if (read_err < 0) {
548 pr_err(SMUX_CTL_MODULE_NAME ": %s:"
549 " Read block failed for Ch%d, err %d\n",
550 __func__, devp->id, read_err);
551 return read_err;
552 }
553
554 mutex_lock(&devp->rx_lock);
555 }
556
557 if (list_empty(&devp->rx_list)) {
558 mutex_unlock(&devp->rx_lock);
559 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: "
560 "Nothing in ch%d's rx_list\n", __func__,
561 devp->id);
562 return -EAGAIN;
563 }
564
565 list_elem = list_first_entry(&devp->rx_list,
566 struct smux_ctl_list_elem, list);
567 bytes_to_read = (uint32_t)(list_elem->ctl_pkt.data_size);
568 if (bytes_to_read > count) {
569 mutex_unlock(&devp->rx_lock);
570 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
571 "Packet size %d > buf size %d\n", __func__,
572 bytes_to_read, count);
573 return -ENOMEM;
574 }
575
576 if (copy_to_user(buf, list_elem->ctl_pkt.data, bytes_to_read)) {
577 mutex_unlock(&devp->rx_lock);
578 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
579 "copy_to_user failed for ch%d\n", __func__,
580 devp->id);
581 return -EFAULT;
582 }
583
584 devp->read_avail -= bytes_to_read;
585 list_del(&list_elem->list);
586 kfree(list_elem->ctl_pkt.data);
587 kfree(list_elem);
588 devp->stats.pkts_rx++;
589 devp->stats.bytes_rx += bytes_to_read;
590 mutex_unlock(&devp->rx_lock);
591
592 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: "
593 "Returning %d bytes to ch%d\n", __func__,
594 bytes_to_read, devp->id);
595 return bytes_to_read;
596}
597
598static int smux_ctl_writeable(int id)
599{
600 int r;
601 int dev_index;
602
603 if (id < 0 || id > smux_ctl_ch_id[SMUX_CTL_NUM_CHANNELS - 1])
604 return -ENODEV;
605
606 dev_index = get_ctl_dev_index(id);
607 if (dev_index < 0) {
608 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
609 "Ch%d is not exported to user-space\n",
610 __func__, id);
611 return -ENODEV;
612 }
613
614 mutex_lock(&smux_ctl_devp[dev_index]->dev_lock);
615
616 if (signal_pending(current))
617 r = -ERESTARTSYS;
Eric Holmbergd6704e32012-06-19 08:53:02 -0600618
619 else if (smux_ctl_devp[dev_index]->abort_wait)
620 r = -ENETRESET;
Eric Holmberg9fff8872012-05-10 19:20:27 -0600621 else if (smux_ctl_devp[dev_index]->state == SMUX_DISCONNECTED &&
622 smux_ctl_devp[dev_index]->is_channel_reset != 0)
623 r = -ENETRESET;
624 else if (smux_ctl_devp[dev_index]->state != SMUX_CONNECTED)
625 r = -ENODEV;
626 else if (smux_ctl_devp[dev_index]->is_high_wm ||
627 smux_ctl_devp[dev_index]->write_pending)
628 r = 0;
629 else
630 r = SMUX_CTL_MAX_BUF_SIZE;
631
632 mutex_unlock(&smux_ctl_devp[dev_index]->dev_lock);
633
634 return r;
635
636}
637
638ssize_t smux_ctl_write(struct file *file,
639 const char __user *buf,
640 size_t count,
641 loff_t *ppos)
642{
643 int r = 0, id, write_err;
644 char *temp_buf;
645 struct smux_ctl_dev *devp;
646
647 if (count <= 0)
648 return -EINVAL;
649
650 devp = file->private_data;
651 if (!devp)
652 return -ENODEV;
653
654 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: writing %i bytes on ch%d\n",
655 __func__, count, devp->id);
656
657 id = devp->id;
658 r = wait_event_interruptible(devp->write_wait_queue,
659 0 != (write_err = smux_ctl_writeable(id)));
660
661 if (r < 0) {
662 pr_err(SMUX_CTL_MODULE_NAME
663 ": %s: wait_event_interruptible "
664 "ret %i\n", __func__, r);
665 return r;
666 }
667
668 if (write_err < 0) {
669 pr_err(SMUX_CTL_MODULE_NAME ": %s:"
670 "Write block failed for Ch%d, err %d\n",
671 __func__, devp->id, write_err);
672 return write_err;
673 }
674
675 temp_buf = kmalloc(count, GFP_KERNEL);
676 if (!temp_buf) {
677 pr_err(SMUX_CTL_MODULE_NAME
678 ": %s: temp_buf alloc failed\n", __func__);
679 return -ENOMEM;
680 }
681
682 if (copy_from_user(temp_buf, buf, count)) {
683 pr_err(SMUX_CTL_MODULE_NAME
684 ": %s: copy_from_user failed\n", __func__);
685 kfree(temp_buf);
686 return -EFAULT;
687 }
688
689 mutex_lock(&devp->dev_lock);
690 devp->write_pending = 1;
691 mutex_unlock(&devp->dev_lock);
692
693 r = msm_smux_write(id, NULL, (void *)temp_buf, count);
694 if (r < 0) {
695 pr_err(SMUX_CTL_MODULE_NAME
696 ": %s: smux_write on Ch%dfailed, err %d\n",
697 __func__, id, r);
698 mutex_lock(&devp->dev_lock);
699 devp->write_pending = 0;
700 mutex_unlock(&devp->dev_lock);
701 return r;
702 }
703
704 r = wait_event_interruptible(devp->write_wait_queue,
705 0 != (write_err = smux_ctl_writeable(id)));
Eric Holmbergd6704e32012-06-19 08:53:02 -0600706
707 if (-EIO == r) {
708 pr_err("%s: %s: wait_event_interruptible ret %i\n",
709 SMUX_CTL_MODULE_NAME, __func__, r);
710 return -EIO;
711 }
712
Eric Holmberg9fff8872012-05-10 19:20:27 -0600713 if (r < 0) {
714 pr_err(SMUX_CTL_MODULE_NAME " :%s: wait_event_interruptible "
715 "ret %i\n", __func__, r);
716 mutex_lock(&devp->dev_lock);
717 devp->write_pending = 0;
718 mutex_unlock(&devp->dev_lock);
719 return r;
720 }
721
722 mutex_lock(&devp->dev_lock);
723 devp->write_pending = 0;
724 devp->stats.pkts_tx++;
725 devp->stats.bytes_tx += count;
726 mutex_unlock(&devp->dev_lock);
727 return count;
728}
729
730static long smux_ctl_ioctl(struct file *file, unsigned int cmd,
731 unsigned long arg)
732{
733 int ret;
734 struct smux_ctl_dev *devp;
735
736 devp = file->private_data;
737 if (!devp)
738 return -ENODEV;
739
740 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s called on smuxctl%d device\n",
741 __func__, devp->id);
742
743 switch (cmd) {
744 case TIOCMGET:
745 ret = msm_smux_tiocm_get(devp->id);
746 break;
747 case TIOCMSET:
748 ret = msm_smux_tiocm_set(devp->id, arg, ~arg);
749 break;
750 default:
751 ret = -EINVAL;
752 break;
753 }
754
755 return ret;
756}
757
Eric Holmberg58a677b2012-08-02 11:23:17 -0600758static unsigned int smux_ctl_poll(struct file *file, poll_table *wait)
759{
760 struct smux_ctl_dev *devp;
761 unsigned int mask = 0;
762 int readable;
763
764 devp = file->private_data;
765 if (!devp)
766 return -ENODEV;
767
768 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s called on smuxctl%d\n",
769 __func__, devp->id);
770
771 poll_wait(file, &devp->read_wait_queue, wait);
772
773 readable = smux_ctl_readable(devp->id);
Eric Holmberg3cbcfa32012-09-21 11:11:53 -0600774 if (readable > 0) {
775 mask = POLLIN | POLLRDNORM;
776 } else if ((readable < 0) && (readable != -ERESTARTSYS)) {
777 /* error case (non-signal) received */
Eric Holmberg58a677b2012-08-02 11:23:17 -0600778 pr_err(SMUX_CTL_MODULE_NAME ": %s err%d during poll for smuxctl%d\n",
779 __func__, readable, devp->id);
780 mask = POLLERR;
Eric Holmberg58a677b2012-08-02 11:23:17 -0600781 }
782
783 return mask;
784}
785
Eric Holmberg9fff8872012-05-10 19:20:27 -0600786static const struct file_operations smux_ctl_fops = {
787 .owner = THIS_MODULE,
788 .open = smux_ctl_open,
789 .release = smux_ctl_release,
790 .read = smux_ctl_read,
791 .write = smux_ctl_write,
792 .unlocked_ioctl = smux_ctl_ioctl,
Eric Holmberg58a677b2012-08-02 11:23:17 -0600793 .poll = smux_ctl_poll,
Eric Holmberg9fff8872012-05-10 19:20:27 -0600794};
795
Eric Holmbergd6704e32012-06-19 08:53:02 -0600796static void smux_ctl_reset_channel(struct smux_ctl_dev *devp)
797{
798 devp->is_high_wm = 0;
799 devp->write_pending = 0;
800 devp->is_channel_reset = 0;
801 devp->state = SMUX_DISCONNECTED;
802 devp->read_avail = 0;
803
804 devp->stats.bytes_tx = 0;
805 devp->stats.bytes_rx = 0;
806 devp->stats.pkts_tx = 0;
807 devp->stats.pkts_rx = 0;
808 devp->stats.cnt_ssr = 0;
809 devp->stats.cnt_read_fail = 0;
810 devp->stats.cnt_write_fail = 0;
811 devp->stats.cnt_high_wm_hit = 0;
812 devp->abort_wait = 0;
813}
814
Eric Holmberg9fff8872012-05-10 19:20:27 -0600815static int smux_ctl_probe(struct platform_device *pdev)
816{
817 int i;
818 int r;
819
820 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s Begins\n", __func__);
821
Eric Holmbergd6704e32012-06-19 08:53:02 -0600822 if (smux_ctl_inited) {
823 /* Already loaded once - reinitialize channels */
824 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
825 struct smux_ctl_dev *devp = smux_ctl_devp[i];
826
827 smux_ctl_reset_channel(devp);
828
829 if (atomic_read(&devp->ref_count)) {
830 r = msm_smux_open(devp->id,
831 devp,
832 smux_ctl_notify_cb,
833 smux_ctl_get_rx_buf_cb);
834 if (r)
835 pr_err("%s: unable to reopen ch %d, ret %d\n",
836 __func__, devp->id, r);
837 }
838 }
839 return 0;
840 }
841
842 /* Create character devices */
Eric Holmberg9fff8872012-05-10 19:20:27 -0600843 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
844 smux_ctl_devp[i] = kzalloc(sizeof(struct smux_ctl_dev),
845 GFP_KERNEL);
846 if (IS_ERR(smux_ctl_devp[i])) {
847 pr_err(SMUX_CTL_MODULE_NAME
848 ": %s kmalloc() ENOMEM\n", __func__);
849 r = -ENOMEM;
850 goto error0;
851 }
852
853 smux_ctl_devp[i]->id = smux_ctl_ch_id[i];
854 atomic_set(&smux_ctl_devp[i]->ref_count, 0);
Eric Holmberg9fff8872012-05-10 19:20:27 -0600855
856 mutex_init(&smux_ctl_devp[i]->dev_lock);
857 init_waitqueue_head(&smux_ctl_devp[i]->read_wait_queue);
858 init_waitqueue_head(&smux_ctl_devp[i]->write_wait_queue);
859 mutex_init(&smux_ctl_devp[i]->rx_lock);
860 INIT_LIST_HEAD(&smux_ctl_devp[i]->rx_list);
Eric Holmbergd6704e32012-06-19 08:53:02 -0600861 smux_ctl_reset_channel(smux_ctl_devp[i]);
Eric Holmberg9fff8872012-05-10 19:20:27 -0600862 }
863
864 r = alloc_chrdev_region(&smux_ctl_number, 0, SMUX_CTL_NUM_CHANNELS,
865 DEVICE_NAME);
866 if (IS_ERR_VALUE(r)) {
867 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
868 "alloc_chrdev_region() ret %i.\n",
869 __func__, r);
870 goto error0;
871 }
872
873 smux_ctl_classp = class_create(THIS_MODULE, DEVICE_NAME);
874 if (IS_ERR(smux_ctl_classp)) {
875 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
876 "class_create() ENOMEM\n", __func__);
877 r = -ENOMEM;
878 goto error1;
879 }
880
881 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
882 cdev_init(&smux_ctl_devp[i]->cdev, &smux_ctl_fops);
883 smux_ctl_devp[i]->cdev.owner = THIS_MODULE;
884
Eric Holmbergd6704e32012-06-19 08:53:02 -0600885 r = cdev_add(&smux_ctl_devp[i]->cdev,
886 (smux_ctl_number + i), 1);
Eric Holmberg9fff8872012-05-10 19:20:27 -0600887
888 if (IS_ERR_VALUE(r)) {
889 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
890 "cdev_add() ret %i\n", __func__, r);
891 kfree(smux_ctl_devp[i]);
892 goto error2;
893 }
894
895 smux_ctl_devp[i]->devicep =
896 device_create(smux_ctl_classp, NULL,
897 (smux_ctl_number + i), NULL,
898 DEVICE_NAME "%d", smux_ctl_ch_id[i]);
899
900 if (IS_ERR(smux_ctl_devp[i]->devicep)) {
901 pr_err(SMUX_CTL_MODULE_NAME ": %s: "
902 "device_create() ENOMEM\n", __func__);
903 r = -ENOMEM;
904 cdev_del(&smux_ctl_devp[i]->cdev);
905 kfree(smux_ctl_devp[i]);
906 goto error2;
907 }
Angshuman Sarkar76db1412012-07-25 15:17:19 +0530908 if (device_create_file(smux_ctl_devp[i]->devicep,
909 &dev_attr_open_timeout))
910 pr_err("%s: unable to create device attr for" \
911 " smux ctl dev id:%d\n", __func__, i);
912
Eric Holmberg9fff8872012-05-10 19:20:27 -0600913 }
914
915 smux_ctl_inited = 1;
916 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s: "
917 "SMUX Control Port Driver Initialized.\n", __func__);
918 return 0;
919
920error2:
921 while (--i >= 0) {
922 cdev_del(&smux_ctl_devp[i]->cdev);
923 device_destroy(smux_ctl_classp,
924 MKDEV(MAJOR(smux_ctl_number), i));
925 }
926
927 class_destroy(smux_ctl_classp);
928 i = SMUX_CTL_NUM_CHANNELS;
929
930error1:
931 unregister_chrdev_region(MAJOR(smux_ctl_number),
932 SMUX_CTL_NUM_CHANNELS);
933
934error0:
935 while (--i >= 0)
936 kfree(smux_ctl_devp[i]);
937
938 return r;
939}
940
941static int smux_ctl_remove(struct platform_device *pdev)
942{
943 int i;
Eric Holmberg9aa0d472013-04-05 15:33:56 -0600944 int ret;
Eric Holmberg9fff8872012-05-10 19:20:27 -0600945
946 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s Begins\n", __func__);
947
948 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
Eric Holmbergd6704e32012-06-19 08:53:02 -0600949 struct smux_ctl_dev *devp = smux_ctl_devp[i];
Eric Holmberg9fff8872012-05-10 19:20:27 -0600950
Eric Holmbergd6704e32012-06-19 08:53:02 -0600951 mutex_lock(&devp->dev_lock);
952 devp->abort_wait = 1;
953 wake_up(&devp->write_wait_queue);
954 wake_up(&devp->read_wait_queue);
Eric Holmberg9aa0d472013-04-05 15:33:56 -0600955
956 if (atomic_read(&devp->ref_count)) {
957 ret = msm_smux_close(devp->id);
958 if (ret)
959 pr_err("%s: unable to close ch %d, ret %d\n",
960 __func__, devp->id, ret);
961 }
Eric Holmbergd6704e32012-06-19 08:53:02 -0600962 mutex_unlock(&devp->dev_lock);
963
964 /* Empty RX queue */
965 mutex_lock(&devp->rx_lock);
966 while (!list_empty(&devp->rx_list)) {
967 struct smux_ctl_list_elem *list_elem;
968
969 list_elem = list_first_entry(
970 &devp->rx_list,
971 struct smux_ctl_list_elem,
972 list);
973 list_del(&list_elem->list);
974 kfree(list_elem->ctl_pkt.data);
975 kfree(list_elem);
976 }
977 devp->read_avail = 0;
978 mutex_unlock(&devp->rx_lock);
979 }
980
981 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s Ends\n", __func__);
Eric Holmberg9fff8872012-05-10 19:20:27 -0600982 return 0;
983}
984
985static struct platform_driver smux_ctl_driver = {
986 .probe = smux_ctl_probe,
987 .remove = smux_ctl_remove,
988 .driver = {
989 .name = "SMUX_CTL",
990 .owner = THIS_MODULE,
991 },
992};
993
994static int __init smux_ctl_init(void)
995{
Eric Holmberg9fff8872012-05-10 19:20:27 -0600996 SMUXCTL_DBG(SMUX_CTL_MODULE_NAME ": %s Begins\n", __func__);
997 return platform_driver_register(&smux_ctl_driver);
998}
999
1000
1001#if defined(CONFIG_DEBUG_FS)
1002
1003#define DEBUG_BUFMAX 4096
1004static char debug_buffer[DEBUG_BUFMAX];
1005
1006static ssize_t debug_read(struct file *file, char __user *buf,
1007 size_t count, loff_t *ppos)
1008{
1009 int bsize = 0;
1010 int i;
1011 if (!smux_ctl_inited) {
1012 pr_err(SMUX_CTL_MODULE_NAME ": %s: SMUX_CTL not yet inited\n",
1013 __func__);
1014 return -EIO;
1015 }
1016
1017 bsize += scnprintf(debug_buffer + bsize, DEBUG_BUFMAX - bsize,
1018 "SMUX_CTL Channel States:\n");
1019
1020 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
1021 bsize += scnprintf(debug_buffer + bsize, DEBUG_BUFMAX - bsize,
1022 "Ch%02d %s RefCnt=%01d State=%02d "
1023 "SSR=%02d HighWM=%02d ReadAvail=%04d WritePending=%02d\n",
1024 smux_ctl_devp[i]->id,
1025 smux_ctl_devp[i]->name,
1026 atomic_read(&smux_ctl_devp[i]->ref_count),
1027 smux_ctl_devp[i]->state,
1028 smux_ctl_devp[i]->is_channel_reset,
1029 smux_ctl_devp[i]->is_high_wm,
1030 smux_ctl_devp[i]->read_avail,
1031 smux_ctl_devp[i]->write_pending);
1032 }
1033
1034 bsize += scnprintf(debug_buffer + bsize, DEBUG_BUFMAX - bsize,
1035 "\nSMUX_CTL Channel Statistics:\n");
1036 for (i = 0; i < SMUX_CTL_NUM_CHANNELS; ++i) {
1037 bsize += scnprintf(debug_buffer + bsize, DEBUG_BUFMAX - bsize,
1038 "Ch%02d %s BytesTX=%08d "
1039 "BytesRx=%08d PktsTx=%04d PktsRx=%04d"
1040 "CntSSR=%02d CntHighWM=%02d "
1041 "CntReadFail%02d CntWriteFailed=%02d\n",
1042 smux_ctl_devp[i]->id,
1043 smux_ctl_devp[i]->name,
1044 smux_ctl_devp[i]->stats.bytes_tx,
1045 smux_ctl_devp[i]->stats.bytes_rx,
1046 smux_ctl_devp[i]->stats.pkts_tx,
1047 smux_ctl_devp[i]->stats.pkts_rx,
1048 smux_ctl_devp[i]->stats.cnt_ssr,
1049 smux_ctl_devp[i]->stats.cnt_high_wm_hit,
1050 smux_ctl_devp[i]->stats.cnt_read_fail,
1051 smux_ctl_devp[i]->stats.cnt_write_fail);
1052 }
1053
1054 return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize);
1055}
1056
1057static int debug_open(struct inode *inode, struct file *file)
1058{
1059 file->private_data = inode->i_private;
1060 return 0;
1061}
1062
1063static const struct file_operations debug_ops = {
1064 .read = debug_read,
1065 .open = debug_open,
1066};
1067
1068static int __init smux_debugfs_init(void)
1069{
1070 struct dentry *dent;
1071
1072 dent = debugfs_create_dir("smux_ctl", 0);
1073 if (!IS_ERR(dent))
1074 debugfs_create_file("smux_ctl_state", 0444, dent,
1075 NULL, &debug_ops);
1076
1077 return 0;
1078}
1079
1080late_initcall(smux_debugfs_init);
1081#endif
1082
1083module_init(smux_ctl_init);
1084MODULE_DESCRIPTION("MSM SMUX Control Port");
1085MODULE_LICENSE("GPL v2");
1086
1087