summaryrefslogtreecommitdiff
path: root/drivers/sipc/sbuf.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/sipc/sbuf.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/sipc/sbuf.c')
-rw-r--r--drivers/sipc/sbuf.c651
1 files changed, 651 insertions, 0 deletions
diff --git a/drivers/sipc/sbuf.c b/drivers/sipc/sbuf.c
new file mode 100644
index 00000000..aade2816
--- /dev/null
+++ b/drivers/sipc/sbuf.c
@@ -0,0 +1,651 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <asm/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+
+#include <linux/sipc.h>
+#include "sbuf.h"
+
+
+static struct sbuf_mgr *sbufs[SIPC_ID_NR][SMSG_CH_NR];
+
+static int sbuf_thread(void *data)
+{
+ struct sbuf_mgr *sbuf = data;
+ struct smsg mcmd, mrecv;
+ int rval, bufid;
+ struct sched_param param = {.sched_priority = 90};
+
+ /*set the thread as a real time thread, and its priority is 90*/
+ sched_setscheduler(current, SCHED_RR, &param);
+
+ /* since the channel open may hang, we call it in the sbuf thread */
+ rval = smsg_ch_open(sbuf->dst, sbuf->channel, -1);
+ if (rval != 0) {
+ printk(KERN_ERR "Failed to open channel %d\n", sbuf->channel);
+ /* assign NULL to thread poniter as failed to open channel */
+ sbuf->thread = NULL;
+ return rval;
+ }
+
+ /* sbuf init done, handle the ring rx events */
+ while (!kthread_should_stop()) {
+ /* monitor sbuf rdptr/wrptr update smsg */
+ smsg_set(&mrecv, sbuf->channel, 0, 0, 0);
+ rval = smsg_recv(sbuf->dst, &mrecv, -1);
+
+ if (rval == -EIO) {
+ /* channel state is free */
+ msleep(5);
+ continue;
+ }
+
+ pr_debug("sbuf thread recv msg: dst=%d, channel=%d, "
+ "type=%d, flag=0x%04x, value=0x%08x\n",
+ sbuf->dst, sbuf->channel,
+ mrecv.type, mrecv.flag, mrecv.value);
+
+ switch (mrecv.type) {
+ case SMSG_TYPE_OPEN:
+ /* handle channel recovery */
+ smsg_open_ack(sbuf->dst, sbuf->channel);
+ break;
+ case SMSG_TYPE_CLOSE:
+ /* handle channel recovery */
+ smsg_close_ack(sbuf->dst, sbuf->channel);
+ sbuf->state = SBUF_STATE_IDLE;
+ break;
+ case SMSG_TYPE_CMD:
+ /* respond cmd done for sbuf init */
+ WARN_ON(mrecv.flag != SMSG_CMD_SBUF_INIT);
+ smsg_set(&mcmd, sbuf->channel, SMSG_TYPE_DONE,
+ SMSG_DONE_SBUF_INIT, sbuf->smem_addr);
+ smsg_send(sbuf->dst, &mcmd, -1);
+ sbuf->state = SBUF_STATE_READY;
+ break;
+ case SMSG_TYPE_EVENT:
+ bufid = mrecv.value;
+ WARN_ON(bufid >= sbuf->ringnr);
+ switch (mrecv.flag) {
+ case SMSG_EVENT_SBUF_RDPTR:
+ wake_up_interruptible_all(&(sbuf->rings[bufid].txwait));
+ if (sbuf->rings[bufid].handler) {
+ sbuf->rings[bufid].handler(SBUF_NOTIFY_WRITE, sbuf->rings[bufid].data);
+ }
+ break;
+ case SMSG_EVENT_SBUF_WRPTR:
+ wake_up_interruptible_all(&(sbuf->rings[bufid].rxwait));
+ if (sbuf->rings[bufid].handler) {
+ sbuf->rings[bufid].handler(SBUF_NOTIFY_READ, sbuf->rings[bufid].data);
+ }
+ break;
+ default:
+ rval = 1;
+ break;
+ }
+ break;
+ default:
+ rval = 1;
+ break;
+ };
+ if (rval) {
+ printk(KERN_WARNING "non-handled sbuf msg: %d-%d, %d, %d, %d\n",
+ sbuf->dst, sbuf->channel,
+ mrecv.type, mrecv.flag, mrecv.value);
+ rval = 0;
+ }
+ }
+
+ return 0;
+}
+
+int sbuf_create(uint8_t dst, uint8_t channel, uint32_t bufnum,
+ uint32_t txbufsize, uint32_t rxbufsize)
+{
+ struct sbuf_mgr *sbuf;
+ volatile struct sbuf_smem_header *smem;
+ volatile struct sbuf_ring_header *ringhd;
+ int hsize, i, result;
+
+ sbuf = kzalloc(sizeof(struct sbuf_mgr), GFP_KERNEL);
+ if (!sbuf) {
+ printk(KERN_ERR "Failed to allocate mgr for sbuf\n");
+ return -ENOMEM;
+ }
+
+ sbuf->state = SBUF_STATE_IDLE;
+ sbuf->dst = dst;
+ sbuf->channel = channel;
+ sbuf->ringnr = bufnum;
+
+ /* allocate smem */
+ hsize = sizeof(struct sbuf_smem_header) + sizeof(struct sbuf_ring_header) * bufnum;
+ sbuf->smem_size = hsize + (txbufsize + rxbufsize) * bufnum;
+ sbuf->smem_addr = smem_alloc(sbuf->smem_size);
+ if (!sbuf->smem_addr) {
+ printk(KERN_ERR "Failed to allocate smem for sbuf\n");
+ kfree(sbuf);
+ return -ENOMEM;
+ }
+ sbuf->smem_virt = ioremap_nocache(sbuf->smem_addr, sbuf->smem_size);
+ if (!sbuf->smem_virt) {
+ printk(KERN_ERR "Failed to map smem for sbuf\n");
+ smem_free(sbuf->smem_addr, sbuf->smem_size);
+ kfree(sbuf);
+ return -EFAULT;
+ }
+
+ /* allocate rings description */
+ sbuf->rings = kzalloc(sizeof(struct sbuf_ring) * bufnum, GFP_KERNEL);
+ if (!sbuf->rings) {
+ printk(KERN_ERR "Failed to allocate rings for sbuf\n");
+ iounmap(sbuf->smem_virt);
+ smem_free(sbuf->smem_addr, sbuf->smem_size);
+ kfree(sbuf);
+ return -ENOMEM;
+ }
+
+ /* initialize all ring bufs */
+ smem = (volatile struct sbuf_smem_header *)sbuf->smem_virt;
+ smem->ringnr = bufnum;
+ for (i = 0; i < bufnum; i++) {
+ ringhd = (volatile struct sbuf_ring_header *)&(smem->headers[i]);
+ ringhd->txbuf_addr = sbuf->smem_addr + hsize +
+ (txbufsize + rxbufsize) * i;
+ ringhd->txbuf_size = txbufsize;
+ ringhd->txbuf_rdptr = 0;
+ ringhd->txbuf_wrptr = 0;
+ ringhd->rxbuf_addr = smem->headers[i].txbuf_addr + txbufsize;
+ ringhd->rxbuf_size = rxbufsize;
+ ringhd->rxbuf_rdptr = 0;
+ ringhd->rxbuf_wrptr = 0;
+
+ sbuf->rings[i].header = ringhd;
+ sbuf->rings[i].txbuf_virt = sbuf->smem_virt + hsize +
+ (txbufsize + rxbufsize) * i;
+ sbuf->rings[i].rxbuf_virt = sbuf->rings[i].txbuf_virt + txbufsize;
+ init_waitqueue_head(&(sbuf->rings[i].txwait));
+ init_waitqueue_head(&(sbuf->rings[i].rxwait));
+ mutex_init(&(sbuf->rings[i].txlock));
+ mutex_init(&(sbuf->rings[i].rxlock));
+ }
+
+ sbuf->thread = kthread_create(sbuf_thread, sbuf,
+ "sbuf-%d-%d", dst, channel);
+ if (IS_ERR(sbuf->thread)) {
+ printk(KERN_ERR "Failed to create kthread: sbuf-%d-%d\n", dst, channel);
+ kfree(sbuf->rings);
+ iounmap(sbuf->smem_virt);
+ smem_free(sbuf->smem_addr, sbuf->smem_size);
+ result = PTR_ERR(sbuf->thread);
+ kfree(sbuf);
+
+ return result;
+ }
+
+ sbufs[dst][channel] = sbuf;
+ wake_up_process(sbuf->thread);
+
+ return 0;
+}
+
+void sbuf_destroy(uint8_t dst, uint8_t channel)
+{
+ struct sbuf_mgr *sbuf = sbufs[dst][channel];
+ int i;
+
+ if (sbuf == NULL) {
+ return;
+ }
+
+ sbuf->state = SBUF_STATE_IDLE;
+ smsg_ch_close(dst, channel, -1);
+
+ /* stop sbuf thread if it's created successfully and still alive */
+ if (!IS_ERR_OR_NULL(sbuf->thread)) {
+ kthread_stop(sbuf->thread);
+ }
+
+ if (sbuf->rings) {
+ for (i = 0; i < sbuf->ringnr; i++) {
+ wake_up_interruptible_all(&sbuf->rings[i].txwait);
+ wake_up_interruptible_all(&sbuf->rings[i].rxwait);
+ }
+ kfree(sbuf->rings);
+ }
+
+ if (sbuf->smem_virt) {
+ iounmap(sbuf->smem_virt);
+ }
+ smem_free(sbuf->smem_addr, sbuf->smem_size);
+ kfree(sbuf);
+
+ sbufs[dst][channel] = NULL;
+}
+
+int sbuf_write(uint8_t dst, uint8_t channel, uint32_t bufid,
+ void *buf, uint32_t len, int timeout)
+{
+ struct sbuf_mgr *sbuf = sbufs[dst][channel];
+ struct sbuf_ring *ring = NULL;
+ volatile struct sbuf_ring_header *ringhd = NULL;
+ struct smsg mevt;
+ void *txpos;
+ int rval, left, tail, txsize;
+
+ if (!sbuf) {
+ return -ENODEV;
+ }
+ ring = &(sbuf->rings[bufid]);
+ ringhd = ring->header;
+ if (sbuf->state != SBUF_STATE_READY) {
+ printk(KERN_ERR "sbuf-%d-%d not ready to write!\n", dst, channel);
+ return -ENODEV;
+ }
+
+ pr_debug("sbuf_write: dst=%d, channel=%d, bufid=%d, len=%d, timeout=%d\n",
+ dst, channel, bufid, len, timeout);
+ pr_debug("sbuf_write: channel=%d, wrptr=%d, rdptr=%d",
+ channel, ringhd->txbuf_wrptr, ringhd->txbuf_rdptr);
+
+ rval = 0;
+ left = len;
+
+ if (timeout) {
+ mutex_lock(&ring->txlock);
+ } else {
+ if (!mutex_trylock(&(ring->txlock))) {
+ printk(KERN_INFO "sbuf_write busy!\n");
+ return -EBUSY;
+ }
+ }
+
+ if (timeout == 0) {
+ /* no wait */
+ if ((int)(ringhd->txbuf_wrptr - ringhd->txbuf_rdptr) >=
+ ringhd->txbuf_size) {
+ printk(KERN_WARNING "sbuf %d-%d ring %d txbuf is full!\n",
+ dst, channel, bufid);
+ rval = -EBUSY;
+ }
+ } else if (timeout < 0) {
+ /* wait forever */
+ rval = wait_event_interruptible(ring->txwait,
+ (int)(ringhd->txbuf_wrptr - ringhd->txbuf_rdptr) <
+ ringhd->txbuf_size || sbuf->state == SBUF_STATE_IDLE);
+ if (rval < 0) {
+ printk(KERN_WARNING "sbuf_write wait interrupted!\n");
+ }
+
+ if (sbuf->state == SBUF_STATE_IDLE) {
+ printk(KERN_ERR "sbuf_write sbuf state is idle!\n");
+ rval = -EIO;
+ }
+ } else {
+ /* wait timeout */
+ rval = wait_event_interruptible_timeout(ring->txwait,
+ (int)(ringhd->txbuf_wrptr - ringhd->txbuf_rdptr) <
+ ringhd->txbuf_size || sbuf->state == SBUF_STATE_IDLE,
+ timeout);
+ if (rval < 0) {
+ printk(KERN_WARNING "sbuf_write wait interrupted!\n");
+ } else if (rval == 0) {
+ printk(KERN_WARNING "sbuf_write wait timeout!\n");
+ rval = -ETIME;
+ }
+
+ if (sbuf->state == SBUF_STATE_IDLE) {
+ printk(KERN_ERR "sbuf_write sbuf state is idle!\n");
+ rval = -EIO;
+ }
+
+ }
+
+ while (left && (int)(ringhd->txbuf_wrptr - ringhd->txbuf_rdptr) < ringhd->txbuf_size &&
+ sbuf->state == SBUF_STATE_READY) {
+ /* calc txpos & txsize */
+ txpos = ring->txbuf_virt + ringhd->txbuf_wrptr % ringhd->txbuf_size;
+ txsize = ringhd->txbuf_size - (int)(ringhd->txbuf_wrptr - ringhd->txbuf_rdptr);
+ txsize = min(txsize, left);
+
+ tail = txpos + txsize - (ring->txbuf_virt + ringhd->txbuf_size);
+ if (tail > 0) {
+ /* ring buffer is rounded */
+ if ((uintptr_t)buf > TASK_SIZE) {
+ unalign_memcpy(txpos, buf, txsize - tail);
+ unalign_memcpy(ring->txbuf_virt, buf + txsize - tail, tail);
+ } else {
+ if(unalign_copy_from_user(txpos, (void __user *)buf, txsize - tail) ||
+ unalign_copy_from_user(ring->txbuf_virt,
+ (void __user *)(buf + txsize - tail), tail)) {
+ printk(KERN_ERR "sbuf_write: failed to copy from user!\n");
+ rval = -EFAULT;
+ break;
+ }
+ }
+ } else {
+ if ((uintptr_t)buf > TASK_SIZE) {
+ unalign_memcpy(txpos, buf, txsize);
+ } else {
+ /* handle the user space address */
+ if(unalign_copy_from_user(txpos, (void __user *)buf, txsize)) {
+ printk(KERN_ERR "sbuf_write: failed to copy from user!\n");
+ rval = -EFAULT;
+ break;
+ }
+ }
+ }
+
+
+ pr_debug("sbuf_write: channel=%d, txpos=%p, txsize=%d\n", channel, txpos, txsize);
+
+ /* update tx wrptr */
+ ringhd->txbuf_wrptr = ringhd->txbuf_wrptr + txsize;
+ /* tx ringbuf is empty, so need to notify peer side */
+ if(ringhd->txbuf_wrptr - ringhd->txbuf_rdptr == txsize) {
+ smsg_set(&mevt, channel, SMSG_TYPE_EVENT, SMSG_EVENT_SBUF_WRPTR, bufid);
+ smsg_send(dst, &mevt, -1);
+ }
+
+ left -= txsize;
+ buf += txsize;
+ }
+
+ mutex_unlock(&ring->txlock);
+
+ pr_debug("sbuf_write done: channel=%d, len=%d\n", channel, len - left);
+
+ if (len == left) {
+ return rval;
+ } else {
+ return (len - left);
+ }
+}
+
+int sbuf_read(uint8_t dst, uint8_t channel, uint32_t bufid,
+ void *buf, uint32_t len, int timeout)
+{
+ struct sbuf_mgr *sbuf = sbufs[dst][channel];
+ struct sbuf_ring *ring = NULL;
+ volatile struct sbuf_ring_header *ringhd = NULL;
+ struct smsg mevt;
+ void *rxpos;
+ int rval, left, tail, rxsize;
+
+ if (!sbuf) {
+ return -ENODEV;
+ }
+ ring = &(sbuf->rings[bufid]);
+ ringhd = ring->header;
+
+ if (sbuf->state != SBUF_STATE_READY) {
+ printk(KERN_ERR "sbuf-%d-%d not ready to read!\n", dst, channel);
+ return -ENODEV;
+ }
+
+ pr_debug("sbuf_read: dst=%d, channel=%d, bufid=%d, len=%d, timeout=%d\n",
+ dst, channel, bufid, len, timeout);
+ pr_debug("sbuf_read: channel=%d, wrptr=%d, rdptr=%d",
+ channel, ringhd->rxbuf_wrptr, ringhd->rxbuf_rdptr);
+
+ rval = 0;
+ left = len;
+
+ if (timeout) {
+ mutex_lock(&ring->rxlock);
+ } else {
+ if (!mutex_trylock(&(ring->rxlock))) {
+ printk(KERN_INFO "sbuf_read busy!\n");
+ return -EBUSY;
+ }
+ }
+
+ if (ringhd->rxbuf_wrptr == ringhd->rxbuf_rdptr) {
+ if (timeout == 0) {
+ /* no wait */
+ printk(KERN_WARNING "sbuf %d-%d ring %d rxbuf is empty!\n",
+ dst, channel, bufid);
+ rval = -ENODATA;
+ } else if (timeout < 0) {
+ /* wait forever */
+ rval = wait_event_interruptible(ring->rxwait,
+ ringhd->rxbuf_wrptr != ringhd->rxbuf_rdptr ||
+ sbuf->state == SBUF_STATE_IDLE);
+ if (rval < 0) {
+ printk(KERN_WARNING "sbuf_read wait interrupted!\n");
+ }
+
+ if (sbuf->state == SBUF_STATE_IDLE) {
+ printk(KERN_ERR "sbuf_read sbuf state is idle!\n");
+ rval = -EIO;
+ }
+ } else {
+ /* wait timeout */
+ rval = wait_event_interruptible_timeout(ring->rxwait,
+ ringhd->rxbuf_wrptr != ringhd->rxbuf_rdptr ||
+ sbuf->state == SBUF_STATE_IDLE, timeout);
+ if (rval < 0) {
+ printk(KERN_WARNING "sbuf_read wait interrupted!\n");
+ } else if (rval == 0) {
+ printk(KERN_WARNING "sbuf_read wait timeout!\n");
+ rval = -ETIME;
+ }
+
+ if (sbuf->state == SBUF_STATE_IDLE) {
+ printk(KERN_ERR "sbuf_read sbuf state is idle!\n");
+ rval = -EIO;
+ }
+ }
+ }
+
+ while (left && (ringhd->rxbuf_wrptr != ringhd->rxbuf_rdptr) &&
+ sbuf->state == SBUF_STATE_READY) {
+ /* calc rxpos & rxsize */
+ rxpos = ring->rxbuf_virt + ringhd->rxbuf_rdptr % ringhd->rxbuf_size;
+ rxsize = (int)(ringhd->rxbuf_wrptr - ringhd->rxbuf_rdptr);
+ /* check overrun */
+ WARN_ON(rxsize > ringhd->rxbuf_size);
+ rxsize = min(rxsize, left);
+
+ pr_debug("sbuf_read: channel=%d, buf=%p, rxpos=%p, rxsize=%d\n", channel, buf, rxpos, rxsize);
+
+ tail = rxpos + rxsize - (ring->rxbuf_virt + ringhd->rxbuf_size);
+
+ if (tail > 0) {
+ /* ring buffer is rounded */
+ if ((uintptr_t)buf > TASK_SIZE) {
+ unalign_memcpy(buf, rxpos, rxsize - tail);
+ unalign_memcpy(buf + rxsize - tail, ring->rxbuf_virt, tail);
+ } else {
+ /* handle the user space address */
+ if(unalign_copy_to_user((void __user *)buf, rxpos, rxsize - tail) ||
+ unalign_copy_to_user((void __user *)(buf + rxsize - tail),
+ ring->rxbuf_virt, tail)) {
+ printk(KERN_ERR "sbuf_read: failed to copy to user!\n");
+ rval = -EFAULT;
+ break;
+ }
+ }
+ } else {
+ if ((uintptr_t)buf > TASK_SIZE) {
+ unalign_memcpy(buf, rxpos, rxsize);
+ } else {
+ /* handle the user space address */
+ if (unalign_copy_to_user((void __user *)buf, rxpos, rxsize)) {
+ printk(KERN_ERR "sbuf_read: failed to copy to user!\n");
+ rval = -EFAULT;
+ break;
+ }
+ }
+ }
+
+ /* update rx rdptr */
+ ringhd->rxbuf_rdptr = ringhd->rxbuf_rdptr + rxsize;
+ /* rx ringbuf is full ,so need to notify peer side */
+ if(ringhd->rxbuf_wrptr - ringhd->rxbuf_rdptr == ringhd->rxbuf_size - rxsize) {
+ smsg_set(&mevt, channel, SMSG_TYPE_EVENT, SMSG_EVENT_SBUF_RDPTR, bufid);
+ smsg_send(dst, &mevt, -1);
+ }
+
+ left -= rxsize;
+ buf += rxsize;
+ }
+
+ mutex_unlock(&ring->rxlock);
+
+ pr_debug("sbuf_read done: channel=%d, len=%d", channel, len - left);
+
+ if (len == left) {
+ return rval;
+ } else {
+ return (len - left);
+ }
+}
+
+int sbuf_poll_wait(uint8_t dst, uint8_t channel, uint32_t bufid,
+ struct file *filp, poll_table *wait)
+{
+ struct sbuf_mgr *sbuf = sbufs[dst][channel];
+ struct sbuf_ring *ring = NULL;
+ volatile struct sbuf_ring_header *ringhd = NULL;
+ unsigned int mask = 0;
+
+ if (!sbuf) {
+ return -ENODEV;
+ }
+ ring = &(sbuf->rings[bufid]);
+ ringhd = ring->header;
+ if (sbuf->state != SBUF_STATE_READY) {
+ printk(KERN_ERR "sbuf-%d-%d not ready to poll !\n", dst, channel);
+ return -ENODEV;
+ }
+
+ poll_wait(filp, &ring->txwait, wait);
+ poll_wait(filp, &ring->rxwait, wait);
+
+ if (ringhd->rxbuf_wrptr != ringhd->rxbuf_rdptr) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+
+ if (ringhd->txbuf_wrptr - ringhd->txbuf_rdptr < ringhd->txbuf_size) {
+ mask |= POLLOUT | POLLWRNORM;
+ }
+
+ return mask;
+}
+
+int sbuf_status(uint8_t dst, uint8_t channel)
+{
+ struct sbuf_mgr *sbuf = sbufs[dst][channel];
+
+ if (!sbuf) {
+ return -ENODEV;
+ }
+ if (sbuf->state != SBUF_STATE_READY) {
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+int sbuf_register_notifier(uint8_t dst, uint8_t channel, uint32_t bufid,
+ void (*handler)(int event, void *data), void *data)
+{
+ struct sbuf_mgr *sbuf = sbufs[dst][channel];
+ struct sbuf_ring *ring = NULL;
+
+ if (!sbuf) {
+ return -ENODEV;
+ }
+ ring = &(sbuf->rings[bufid]);
+ ring->handler = handler;
+ ring->data = data;
+
+ return 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+
+static int sbuf_debug_show(struct seq_file *m, void *private)
+{
+ struct sbuf_mgr *sbuf = NULL;
+ struct sbuf_ring *rings = NULL;
+ volatile struct sbuf_ring_header *ring = NULL;
+ int i, j, n;
+
+ for (i = 0; i < SIPC_ID_NR; i++) {
+ for (j=0; j< SMSG_CH_NR; j++) {
+ sbuf = sbufs[i][j];
+ if (!sbuf) {
+ continue;
+ }
+ seq_printf(m, "sbuf dst 0x%0x, channel: 0x%0x, state: %d, smem_virt: 0x%lx, smem_addr: 0x%0x, smem_size: 0x%0x, ringnr: %d \n",
+ sbuf->dst, sbuf->channel, sbuf->state, (size_t)sbuf->smem_virt, sbuf->smem_addr, sbuf->smem_size, sbuf->ringnr);
+
+ for (n=0; n < sbuf->ringnr; n++) {
+ rings = &(sbuf->rings[n]);
+ ring = rings->header;
+ seq_printf(m, "sbuf ring[%d]: rxbuf_addr :0x%0x, rxbuf_rdptr :0x%0x, rxbuf_wrptr :0x%0x, rxbuf_size :0x%0x \n", n, ring->rxbuf_addr, ring->rxbuf_rdptr, ring->rxbuf_wrptr, ring->rxbuf_size);
+ seq_printf(m, "sbuf ring[%d]: txbuf_addr :0x%0x, txbuf_rdptr :0x%0x, txbuf_wrptr :0x%0x, txbuf_size :0x%0x \n", n, ring->txbuf_addr, ring->txbuf_rdptr, ring->txbuf_wrptr, ring->txbuf_size);
+ }
+ }
+ }
+ return 0;
+}
+
+static int sbuf_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, sbuf_debug_show, inode->i_private);
+}
+
+static const struct file_operations sbuf_debug_fops = {
+ .open = sbuf_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int sbuf_init_debugfs( void *root )
+{
+ if (!root)
+ return -ENXIO;
+ debugfs_create_file("sbuf", S_IRUGO, (struct dentry *)root, NULL, &sbuf_debug_fops);
+ return 0;
+}
+
+#endif /* CONFIG_DEBUG_FS */
+
+
+EXPORT_SYMBOL(sbuf_create);
+EXPORT_SYMBOL(sbuf_destroy);
+EXPORT_SYMBOL(sbuf_write);
+EXPORT_SYMBOL(sbuf_read);
+EXPORT_SYMBOL(sbuf_poll_wait);
+EXPORT_SYMBOL(sbuf_status);
+EXPORT_SYMBOL(sbuf_register_notifier);
+
+MODULE_AUTHOR("Chen Gaopeng");
+MODULE_DESCRIPTION("SIPC/SBUF driver");
+MODULE_LICENSE("GPL");