aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/Kconfig8
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/msm_smd_pkt.c466
3 files changed, 475 insertions, 0 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 04f8b2d083c6..ad59b4e0a9b5 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -608,5 +608,13 @@ config RAMOOPS
608 This enables panic and oops messages to be logged to a circular 608 This enables panic and oops messages to be logged to a circular
609 buffer in RAM where it can be read back at some later point. 609 buffer in RAM where it can be read back at some later point.
610 610
611config MSM_SMD_PKT
612 bool "Enable device interface for some SMD packet ports"
613 default n
614 depends on MSM_SMD
615 help
616 Enables userspace clients to read and write to some packet SMD
617 ports via device interface for MSM chipset.
618
611endmenu 619endmenu
612 620
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 057f65452e7b..7a00672bd85d 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_ATARI_DSP56K) += dsp56k.o
9obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o 9obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o
10obj-$(CONFIG_RAW_DRIVER) += raw.o 10obj-$(CONFIG_RAW_DRIVER) += raw.o
11obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o 11obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o
12obj-$(CONFIG_MSM_SMD_PKT) += msm_smd_pkt.o
12obj-$(CONFIG_MSPEC) += mspec.o 13obj-$(CONFIG_MSPEC) += mspec.o
13obj-$(CONFIG_MMTIMER) += mmtimer.o 14obj-$(CONFIG_MMTIMER) += mmtimer.o
14obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o 15obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o
diff --git a/drivers/char/msm_smd_pkt.c b/drivers/char/msm_smd_pkt.c
new file mode 100644
index 000000000000..b6f8a65c9960
--- /dev/null
+++ b/drivers/char/msm_smd_pkt.c
@@ -0,0 +1,466 @@
1/* Copyright (c) 2008-2010, 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 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 *
17 */
18/*
19 * SMD Packet Driver -- Provides userspace interface to SMD packet ports.
20 */
21
22#include <linux/slab.h>
23#include <linux/cdev.h>
24#include <linux/module.h>
25#include <linux/fs.h>
26#include <linux/device.h>
27#include <linux/sched.h>
28#include <linux/mutex.h>
29#include <linux/delay.h>
30#include <linux/uaccess.h>
31#include <linux/workqueue.h>
32#include <linux/poll.h>
33
34#include <mach/msm_smd.h>
35
36#define NUM_SMD_PKT_PORTS 9
37#define DEVICE_NAME "smdpkt"
38#define MAX_BUF_SIZE 2048
39
40struct smd_pkt_dev {
41 struct cdev cdev;
42 struct device *devicep;
43
44 struct smd_channel *ch;
45 int open_count;
46 struct mutex ch_lock;
47 struct mutex rx_lock;
48 struct mutex tx_lock;
49 wait_queue_head_t ch_read_wait_queue;
50 wait_queue_head_t ch_opened_wait_queue;
51
52 int i;
53
54 unsigned char tx_buf[MAX_BUF_SIZE];
55 unsigned char rx_buf[MAX_BUF_SIZE];
56 int remote_open;
57
58} *smd_pkt_devp[NUM_SMD_PKT_PORTS];
59
60struct class *smd_pkt_classp;
61static dev_t smd_pkt_number;
62
63static int msm_smd_pkt_debug_enable;
64module_param_named(debug_enable, msm_smd_pkt_debug_enable,
65 int, S_IRUGO | S_IWUSR | S_IWGRP);
66
67#ifdef DEBUG
68#define D_DUMP_BUFFER(prestr, cnt, buf) do { \
69 int i; \
70 if (msm_smd_pkt_debug_enable) { \
71 pr_debug("%s", prestr); \
72 for (i = 0; i < cnt; i++) \
73 pr_debug("%.2x", buf[i]); \
74 pr_debug("\n"); \
75 } \
76 } while (0)
77#else
78#define D_DUMP_BUFFER(prestr, cnt, buf) do {} while (0)
79#endif
80
81#ifdef DEBUG
82#define DBG(x...) do { \
83 if (msm_smd_pkt_debug_enable) \
84 pr_debug(x); \
85 } while (0)
86#else
87#define DBG(x...) do {} while (0)
88#endif
89
90static void check_and_wakeup_reader(struct smd_pkt_dev *smd_pkt_devp)
91{
92 int sz;
93
94 if (!smd_pkt_devp || !smd_pkt_devp->ch)
95 return;
96
97 sz = smd_cur_packet_size(smd_pkt_devp->ch);
98 if (sz == 0) {
99 DBG("no packet\n");
100 return;
101 }
102 if (sz > smd_read_avail(smd_pkt_devp->ch)) {
103 DBG("incomplete packet\n");
104 return;
105 }
106
107 DBG("waking up reader\n");
108 wake_up_interruptible(&smd_pkt_devp->ch_read_wait_queue);
109}
110
111static int smd_pkt_read(struct file *file, char __user *buf,
112 size_t count, loff_t *ppos)
113{
114 int r, bytes_read;
115 struct smd_pkt_dev *smd_pkt_devp;
116 struct smd_channel *chl;
117
118 DBG("read %d bytes\n", count);
119 if (count > MAX_BUF_SIZE)
120 return -EINVAL;
121
122 smd_pkt_devp = file->private_data;
123 if (!smd_pkt_devp || !smd_pkt_devp->ch)
124 return -EINVAL;
125
126 chl = smd_pkt_devp->ch;
127wait_for_packet:
128 r = wait_event_interruptible(smd_pkt_devp->ch_read_wait_queue,
129 (smd_cur_packet_size(chl) > 0 &&
130 smd_read_avail(chl) >=
131 smd_cur_packet_size(chl)));
132
133 if (r < 0) {
134 if (r != -ERESTARTSYS)
135 pr_err("wait returned %d\n", r);
136 return r;
137 }
138
139 mutex_lock(&smd_pkt_devp->rx_lock);
140
141 bytes_read = smd_cur_packet_size(smd_pkt_devp->ch);
142 if (bytes_read == 0 ||
143 bytes_read < smd_read_avail(smd_pkt_devp->ch)) {
144 mutex_unlock(&smd_pkt_devp->rx_lock);
145 DBG("Nothing to read\n");
146 goto wait_for_packet;
147 }
148
149 if (bytes_read > count) {
150 mutex_unlock(&smd_pkt_devp->rx_lock);
151 pr_info("packet size %d > buffer size %d", bytes_read, count);
152 return -EINVAL;
153 }
154
155 r = smd_read(smd_pkt_devp->ch, smd_pkt_devp->rx_buf, bytes_read);
156 if (r != bytes_read) {
157 mutex_unlock(&smd_pkt_devp->rx_lock);
158 pr_err("smd_read failed to read %d bytes: %d\n", bytes_read, r);
159 return -EIO;
160 }
161
162 D_DUMP_BUFFER("read: ", bytes_read, smd_pkt_devp->rx_buf);
163 r = copy_to_user(buf, smd_pkt_devp->rx_buf, bytes_read);
164 mutex_unlock(&smd_pkt_devp->rx_lock);
165 if (r) {
166 pr_err("copy_to_user failed %d\n", r);
167 return -EFAULT;
168 }
169
170 DBG("read complete %d bytes\n", bytes_read);
171 check_and_wakeup_reader(smd_pkt_devp);
172
173 return bytes_read;
174}
175
176static int smd_pkt_write(struct file *file, const char __user *buf,
177 size_t count, loff_t *ppos)
178{
179 int r;
180 struct smd_pkt_dev *smd_pkt_devp;
181
182 if (count > MAX_BUF_SIZE)
183 return -EINVAL;
184
185 DBG("writting %d bytes\n", count);
186
187 smd_pkt_devp = file->private_data;
188 if (!smd_pkt_devp || !smd_pkt_devp->ch)
189 return -EINVAL;
190
191 mutex_lock(&smd_pkt_devp->tx_lock);
192 if (smd_write_avail(smd_pkt_devp->ch) < count) {
193 mutex_unlock(&smd_pkt_devp->tx_lock);
194 DBG("Not enough space to write\n");
195 return -ENOMEM;
196 }
197
198 D_DUMP_BUFFER("write: ", count, buf);
199 r = copy_from_user(smd_pkt_devp->tx_buf, buf, count);
200 if (r) {
201 mutex_unlock(&smd_pkt_devp->tx_lock);
202 pr_err("copy_from_user failed %d\n", r);
203 return -EFAULT;
204 }
205
206 r = smd_write(smd_pkt_devp->ch, smd_pkt_devp->tx_buf, count);
207 if (r != count) {
208 mutex_unlock(&smd_pkt_devp->tx_lock);
209 pr_err("smd_write failed to write %d bytes: %d.\n", count, r);
210 return -EIO;
211 }
212 mutex_unlock(&smd_pkt_devp->tx_lock);
213
214 DBG("wrote %d bytes\n", count);
215 return count;
216}
217
218static unsigned int smd_pkt_poll(struct file *file, poll_table *wait)
219{
220 struct smd_pkt_dev *smd_pkt_devp;
221 unsigned int mask = 0;
222
223 smd_pkt_devp = file->private_data;
224 if (!smd_pkt_devp)
225 return POLLERR;
226
227 DBG("poll waiting\n");
228 poll_wait(file, &smd_pkt_devp->ch_read_wait_queue, wait);
229 if (smd_read_avail(smd_pkt_devp->ch))
230 mask |= POLLIN | POLLRDNORM;
231
232 DBG("poll return\n");
233 return mask;
234}
235
236static void smd_pkt_ch_notify(void *priv, unsigned event)
237{
238 struct smd_pkt_dev *smd_pkt_devp = priv;
239
240 if (smd_pkt_devp->ch == 0)
241 return;
242
243 switch (event) {
244 case SMD_EVENT_DATA:
245 DBG("data\n");
246 check_and_wakeup_reader(smd_pkt_devp);
247 break;
248
249 case SMD_EVENT_OPEN:
250 DBG("remote open\n");
251 smd_pkt_devp->remote_open = 1;
252 wake_up_interruptible(&smd_pkt_devp->ch_opened_wait_queue);
253 break;
254
255 case SMD_EVENT_CLOSE:
256 smd_pkt_devp->remote_open = 0;
257 pr_info("remote closed\n");
258 break;
259
260 default:
261 pr_err("unknown event %d\n", event);
262 break;
263 }
264}
265
266static char *smd_pkt_dev_name[] = {
267 "smdcntl0",
268 "smdcntl1",
269 "smdcntl2",
270 "smdcntl3",
271 "smdcntl4",
272 "smdcntl5",
273 "smdcntl6",
274 "smdcntl7",
275 "smd22",
276};
277
278static char *smd_ch_name[] = {
279 "DATA5_CNTL",
280 "DATA6_CNTL",
281 "DATA7_CNTL",
282 "DATA8_CNTL",
283 "DATA9_CNTL",
284 "DATA12_CNTL",
285 "DATA13_CNTL",
286 "DATA14_CNTL",
287 "DATA22",
288};
289
290static int smd_pkt_open(struct inode *inode, struct file *file)
291{
292 int r = 0;
293 struct smd_pkt_dev *smd_pkt_devp;
294
295 smd_pkt_devp = container_of(inode->i_cdev, struct smd_pkt_dev, cdev);
296 if (!smd_pkt_devp)
297 return -EINVAL;
298
299 file->private_data = smd_pkt_devp;
300
301 mutex_lock(&smd_pkt_devp->ch_lock);
302 if (smd_pkt_devp->open_count == 0) {
303 r = smd_open(smd_ch_name[smd_pkt_devp->i],
304 &smd_pkt_devp->ch, smd_pkt_devp,
305 smd_pkt_ch_notify);
306 if (r < 0) {
307 pr_err("smd_open failed for %s, %d\n",
308 smd_ch_name[smd_pkt_devp->i], r);
309 goto out;
310 }
311
312 r = wait_event_interruptible_timeout(
313 smd_pkt_devp->ch_opened_wait_queue,
314 smd_pkt_devp->remote_open,
315 msecs_to_jiffies(2 * HZ));
316 if (r == 0)
317 r = -ETIMEDOUT;
318
319 if (r < 0) {
320 pr_err("wait returned %d\n", r);
321 smd_close(smd_pkt_devp->ch);
322 smd_pkt_devp->ch = 0;
323 } else {
324 smd_pkt_devp->open_count++;
325 r = 0;
326 }
327 }
328out:
329 mutex_unlock(&smd_pkt_devp->ch_lock);
330 return r;
331}
332
333static int smd_pkt_release(struct inode *inode, struct file *file)
334{
335 int r = 0;
336 struct smd_pkt_dev *smd_pkt_devp = file->private_data;
337
338 if (!smd_pkt_devp)
339 return -EINVAL;
340
341 mutex_lock(&smd_pkt_devp->ch_lock);
342 if (--smd_pkt_devp->open_count == 0) {
343 r = smd_close(smd_pkt_devp->ch);
344 smd_pkt_devp->ch = 0;
345 }
346 mutex_unlock(&smd_pkt_devp->ch_lock);
347
348 return r;
349}
350
351static const struct file_operations smd_pkt_fops = {
352 .owner = THIS_MODULE,
353 .open = smd_pkt_open,
354 .release = smd_pkt_release,
355 .read = smd_pkt_read,
356 .write = smd_pkt_write,
357 .poll = smd_pkt_poll,
358};
359
360static int __init smd_pkt_init(void)
361{
362 int i;
363 int r;
364
365 r = alloc_chrdev_region(&smd_pkt_number, 0,
366 NUM_SMD_PKT_PORTS, DEVICE_NAME);
367 if (r) {
368 pr_err("alloc_chrdev_region() failed %d\n", r);
369 return r;
370 }
371
372 smd_pkt_classp = class_create(THIS_MODULE, DEVICE_NAME);
373 if (IS_ERR(smd_pkt_classp)) {
374 r = PTR_ERR(smd_pkt_classp);
375 pr_err("class_create() failed %d\n", r);
376 goto unreg_chardev;
377 }
378
379 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
380 smd_pkt_devp[i] = kzalloc(sizeof(struct smd_pkt_dev),
381 GFP_KERNEL);
382 if (IS_ERR(smd_pkt_devp[i])) {
383 r = PTR_ERR(smd_pkt_devp[i]);
384 pr_err("kmalloc() failed %d\n", r);
385 goto clean_cdevs;
386 }
387
388 smd_pkt_devp[i]->i = i;
389
390 init_waitqueue_head(&smd_pkt_devp[i]->ch_read_wait_queue);
391 smd_pkt_devp[i]->remote_open = 0;
392 init_waitqueue_head(&smd_pkt_devp[i]->ch_opened_wait_queue);
393
394 mutex_init(&smd_pkt_devp[i]->ch_lock);
395 mutex_init(&smd_pkt_devp[i]->rx_lock);
396 mutex_init(&smd_pkt_devp[i]->tx_lock);
397
398 cdev_init(&smd_pkt_devp[i]->cdev, &smd_pkt_fops);
399 smd_pkt_devp[i]->cdev.owner = THIS_MODULE;
400
401 r = cdev_add(&smd_pkt_devp[i]->cdev,
402 (smd_pkt_number + i), 1);
403 if (r) {
404 pr_err("cdev_add() failed %d\n", r);
405 kfree(smd_pkt_devp[i]);
406 goto clean_cdevs;
407 }
408
409 smd_pkt_devp[i]->devicep =
410 device_create(smd_pkt_classp, NULL,
411 (smd_pkt_number + i), NULL,
412 smd_pkt_dev_name[i]);
413 if (IS_ERR(smd_pkt_devp[i]->devicep)) {
414 r = PTR_ERR(smd_pkt_devp[i]->devicep);
415 pr_err("device_create() failed %d\n", r);
416 cdev_del(&smd_pkt_devp[i]->cdev);
417 kfree(smd_pkt_devp[i]);
418 goto clean_cdevs;
419 }
420
421 }
422
423 pr_info("SMD Packet Port Driver Initialized.\n");
424 return 0;
425
426clean_cdevs:
427 if (i > 0) {
428 while (--i >= 0) {
429 mutex_destroy(&smd_pkt_devp[i]->ch_lock);
430 mutex_destroy(&smd_pkt_devp[i]->rx_lock);
431 mutex_destroy(&smd_pkt_devp[i]->tx_lock);
432 cdev_del(&smd_pkt_devp[i]->cdev);
433 kfree(smd_pkt_devp[i]);
434 device_destroy(smd_pkt_classp,
435 MKDEV(MAJOR(smd_pkt_number), i));
436 }
437 }
438
439 class_destroy(smd_pkt_classp);
440unreg_chardev:
441 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
442 return r;
443}
444module_init(smd_pkt_init);
445
446static void __exit smd_pkt_cleanup(void)
447{
448 int i;
449
450 for (i = 0; i < NUM_SMD_PKT_PORTS; ++i) {
451 mutex_destroy(&smd_pkt_devp[i]->ch_lock);
452 mutex_destroy(&smd_pkt_devp[i]->rx_lock);
453 mutex_destroy(&smd_pkt_devp[i]->tx_lock);
454 cdev_del(&smd_pkt_devp[i]->cdev);
455 kfree(smd_pkt_devp[i]);
456 device_destroy(smd_pkt_classp,
457 MKDEV(MAJOR(smd_pkt_number), i));
458 }
459
460 class_destroy(smd_pkt_classp);
461 unregister_chrdev_region(MAJOR(smd_pkt_number), NUM_SMD_PKT_PORTS);
462}
463module_exit(smd_pkt_cleanup);
464
465MODULE_DESCRIPTION("MSM Shared Memory Packet Port");
466MODULE_LICENSE("GPL v2");