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