summaryrefslogtreecommitdiff
path: root/drivers/misc/mdbg/mdbg.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/mdbg/mdbg.c')
-rw-r--r--drivers/misc/mdbg/mdbg.c621
1 files changed, 621 insertions, 0 deletions
diff --git a/drivers/misc/mdbg/mdbg.c b/drivers/misc/mdbg/mdbg.c
new file mode 100644
index 00000000..d377e481
--- /dev/null
+++ b/drivers/misc/mdbg/mdbg.c
@@ -0,0 +1,621 @@
+/************************************************************************************************************ */
+/*****Copyright: 2014 Spreatrum. All Right Reserved */
+/*****File: mdbg.c */
+/*****Description: Marlin Debug System main file. Module,device & driver related defination. */
+/*****Developer: fan.kou@spreadtrum.com */
+/*****Date: 06/01/2014 */
+/************************************************************************************************************ */
+
+#include "mdbg.h"
+#include "mdbg_sdio.h"
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+
+#define MDBG_MAX_BUFFER_LEN (PAGE_SIZE)
+
+#define MDBG_WCN_WRITE (5)
+#define MDBG_CHANNEL_ASSERT (13)
+#define MDBG_CHANNEL_WDTIRQ (4)
+#define MDBG_CHANNEL_LOOPCHECK (15)
+#define MDBG_CHANNEL_AT_CMD (11)
+
+#define MDBG_WRITE_SIZE (64)
+#define MDBG_ASSERT_SIZE (1024)
+#define MDBG_WDTIRQ_SIZE (128)
+#define MDBG_LOOPCHECK_SIZE (128)
+#define MDBG_AT_CMD_SIZE (128)
+
+unsigned int mdbg_read_count = 0;
+unsigned int first_boot = 0;
+
+struct mdbg_devvice_t *mdbg_dev=NULL;
+wait_queue_head_t mdbg_wait;
+
+struct mdbg_proc_entry {
+ char *name;
+ struct proc_dir_entry *entry;
+ struct completion completed;
+ wait_queue_head_t rxwait;
+ unsigned int rcv_len;
+ void *buf;
+};
+
+struct mdbg_proc_t{
+ char *dir_name;
+ struct proc_dir_entry *procdir;
+ struct mdbg_proc_entry assert;
+ struct mdbg_proc_entry wdtirq;
+ struct mdbg_proc_entry loopcheck;
+ struct mdbg_proc_entry at_cmd;
+ char write_buf[MDBG_WRITE_SIZE];
+};
+
+static struct mdbg_proc_t *mdbg_proc=NULL;
+
+
+void mdbg_assert_interface(void)
+{
+ strncpy(mdbg_proc->assert.buf,"wifi channel is blocked",23);
+ printk(KERN_INFO "mdbg_assert_interface:%s\n",mdbg_proc->assert.buf);
+ mdbg_proc->assert.rcv_len = 23;
+ complete(&mdbg_proc->assert.completed);
+ wake_up_interruptible(&mdbg_proc->assert.rxwait);
+
+ return;
+}
+EXPORT_SYMBOL_GPL(mdbg_assert_interface);
+
+void mdbg_assert_read(void)
+{
+ int read_len;
+ read_len = sdio_dev_get_chn_datalen(MDBG_CHANNEL_ASSERT);
+ if(read_len <= 0){
+ return;
+ }
+
+ if(read_len > MDBG_ASSERT_SIZE)
+ MDBG_ERR( "The assert data len:%d, beyond max read:%d",read_len,MDBG_ASSERT_SIZE);
+
+ sdio_dev_read(MDBG_CHANNEL_ASSERT,mdbg_proc->assert.buf,&read_len);
+ printk(KERN_INFO "mdbg_assert_read:%s\n",mdbg_proc->assert.buf);
+ mdbg_proc->assert.rcv_len = read_len;
+ complete(&mdbg_proc->assert.completed);
+ wake_up_interruptible(&mdbg_proc->assert.rxwait);
+
+ return;
+}
+EXPORT_SYMBOL_GPL(mdbg_assert_read);
+
+void mdbg_wdtirq_read(void)
+{
+ int read_len;
+ read_len = sdio_dev_get_chn_datalen(MDBG_CHANNEL_WDTIRQ);
+ if(read_len <= 0){
+ return;
+ }
+
+ if(read_len > MDBG_WDTIRQ_SIZE)
+ MDBG_ERR( "The assert data len:%d, beyond max read:%d",read_len,MDBG_WDTIRQ_SIZE);
+
+ sdio_dev_read(MDBG_CHANNEL_WDTIRQ,mdbg_proc->wdtirq.buf,&read_len);
+ printk(KERN_INFO "mdbg_wdtirq_read:%s\n",mdbg_proc->wdtirq.buf);
+ mdbg_proc->wdtirq.rcv_len = read_len;
+ complete(&mdbg_proc->wdtirq.completed);
+ wake_up_interruptible(&mdbg_proc->wdtirq.rxwait);
+
+ return;
+}
+EXPORT_SYMBOL_GPL(mdbg_wdtirq_read);
+
+void mdbg_loopcheck_read(void)
+{
+ int read_len;
+
+ read_len = sdio_dev_get_chn_datalen(MDBG_CHANNEL_LOOPCHECK);
+ if(read_len <= 0){
+ return;
+ }
+
+ if(read_len > MDBG_LOOPCHECK_SIZE)
+ MDBG_ERR( "The loopcheck data len:%d, beyond max read:%d",read_len,MDBG_LOOPCHECK_SIZE);
+
+ sdio_dev_read(MDBG_CHANNEL_LOOPCHECK,mdbg_proc->loopcheck.buf,&read_len);
+ printk(KERN_INFO "mdbg_loopcheck_read:%s\n",mdbg_proc->loopcheck.buf);
+ mdbg_proc->loopcheck.rcv_len = read_len;
+ complete(&mdbg_proc->loopcheck.completed);
+
+ return;
+}
+EXPORT_SYMBOL_GPL(mdbg_loopcheck_read);
+
+void mdbg_at_cmd_read(void)
+{
+ int read_len;
+
+ read_len = sdio_dev_get_chn_datalen(MDBG_CHANNEL_AT_CMD);
+ if(read_len <= 0){
+ MDBG_ERR("MDBG_CHANNEL_AT_CMD len err\n");
+ return;
+ }
+
+ if(read_len > MDBG_AT_CMD_SIZE)
+ MDBG_ERR( "The at cmd data len:%d, beyond max read:%d",read_len,MDBG_AT_CMD_SIZE);
+
+ sdio_dev_read(MDBG_CHANNEL_AT_CMD,mdbg_proc->at_cmd.buf,&read_len);
+ mdbg_proc->at_cmd.rcv_len = read_len;
+ printk(KERN_INFO "mdbg_at_cmd_read:%s\n",mdbg_proc->at_cmd.buf);
+ complete(&mdbg_proc->at_cmd.completed);
+
+ return;
+}
+EXPORT_SYMBOL_GPL(mdbg_at_cmd_read);
+
+static int mdbg_proc_open(struct inode *inode, struct file *filp)
+{
+ struct mdbg_proc_entry *entry = (struct mdbg_proc_entry *)PDE_DATA(inode);
+ char *type = entry->name;
+
+ filp->private_data = entry;
+ //MDBG_ERR("%s type:%s\n",__func__,type);
+
+ return 0;
+}
+
+static int mdbg_proc_release(struct inode *inode, struct file *filp)
+{
+ struct mdbg_proc_entry *entry = (struct mdbg_proc_entry *)PDE_DATA(inode);
+ char *type = entry->name;
+
+ //MDBG_ERR("%s type:%s\n",__func__,type);
+
+ return 0;
+}
+
+static ssize_t mdbg_proc_read(struct file *filp,
+ char __user *buf, size_t count, loff_t *ppos)
+{
+ struct mdbg_proc_entry *entry = (struct mdbg_proc_entry *)filp->private_data;
+ char *type = entry->name;
+ int timeout = -1;
+ int len = 0;
+ int ret;
+
+ //printk(KERN_INFO "type:%s\n",type);
+ if (filp->f_flags & O_NONBLOCK) {
+ timeout = 0;
+ }
+
+ if(strcmp(type, "assert") == 0) {
+ if (timeout < 0){
+ while(1){
+ ret = wait_for_completion_timeout(&mdbg_proc->assert.completed,msecs_to_jiffies(1000));
+ if (ret != -ERESTARTSYS)
+ break;
+ }
+ }
+
+ if(copy_to_user((void __user *)buf, mdbg_proc->assert.buf, min(count,(size_t)MDBG_ASSERT_SIZE))){
+ MDBG_ERR( "Read assert info error\n");
+ }
+ len = mdbg_proc->assert.rcv_len;
+ mdbg_proc->assert.rcv_len = 0;
+ memset(mdbg_proc->assert.buf,0,MDBG_ASSERT_SIZE);
+ }
+
+ if(strcmp(type, "wdtirq") == 0) {
+ if (timeout < 0){
+ while(1){
+ ret = wait_for_completion_timeout(&mdbg_proc->wdtirq.completed,msecs_to_jiffies(1000));
+ if (ret != -ERESTARTSYS)
+ break;
+ }
+ }
+
+ if(copy_to_user((void __user *)buf, mdbg_proc->wdtirq.buf, min(count,(size_t)MDBG_WDTIRQ_SIZE))){
+ MDBG_ERR( "Read wdtirq info error\n");
+ }
+
+ len = mdbg_proc->wdtirq.rcv_len;
+ mdbg_proc->wdtirq.rcv_len = 0;
+ memset(mdbg_proc->wdtirq.buf,0,MDBG_WDTIRQ_SIZE);
+ }
+
+ if(strcmp(type, "loopcheck") == 0) {
+ if (timeout < 0){
+ while(1){
+ ret = wait_for_completion_timeout(&mdbg_proc->loopcheck.completed,msecs_to_jiffies(1000));
+ if (ret != -ERESTARTSYS)
+ break;
+ }
+ }
+
+ if(first_boot < 2){
+ if(copy_to_user((void __user *)buf, "loopcheck_ack", 13)){
+ MDBG_ERR("Read loopcheck first info error\n");
+ }
+ first_boot++;
+ len = 13;
+ }else if(get_sprd_marlin_status()){
+ if(copy_to_user((void __user *)buf, "loopcheck_ack", 13)){
+ MDBG_ERR("loopcheck gpio info error\n");
+ }
+ len = 13;
+ }else{
+ if(copy_to_user((void __user *)buf, mdbg_proc->loopcheck.buf, min(count,(size_t)MDBG_LOOPCHECK_SIZE))){
+ MDBG_ERR("Read loopcheck sdio info error\n");
+ }
+ len = mdbg_proc->loopcheck.rcv_len;
+ }
+ memset(mdbg_proc->loopcheck.buf,0,MDBG_LOOPCHECK_SIZE);
+ mdbg_proc->loopcheck.rcv_len = 0;
+ }
+
+ if(strcmp(type, "at_cmd") == 0) {
+ if (timeout < 0){
+ while(1){
+ ret = wait_for_completion_timeout(&mdbg_proc->at_cmd.completed,msecs_to_jiffies(1000));
+ if (ret != -ERESTARTSYS)
+ break;
+ }
+ }
+
+ if(copy_to_user((void __user *)buf, mdbg_proc->at_cmd.buf, min(count,(size_t)MDBG_AT_CMD_SIZE))){
+ MDBG_ERR("Read at cmd ack info error\n");
+ }
+
+ len = mdbg_proc->at_cmd.rcv_len;
+ mdbg_proc->at_cmd.rcv_len = 0;
+ memset(mdbg_proc->at_cmd.buf,0,MDBG_AT_CMD_SIZE);
+ }
+
+ return len;
+}
+
+static ssize_t mdbg_proc_write(struct file *filp,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ int cnt = 0;
+ if(count > MDBG_WRITE_SIZE){
+ MDBG_ERR( "mdbg_proc_write count > MDBG_WRITE_SIZE\n");
+ return -ENOMEM;
+ }
+ memset(mdbg_proc->write_buf,0,MDBG_WRITE_SIZE);
+
+ if (copy_from_user(mdbg_proc->write_buf,buf,count )){
+ return -EFAULT;
+ }
+
+ printk(KERN_INFO "mdbg_proc->write_buf:%s\n",mdbg_proc->write_buf);
+
+ if(strncmp(mdbg_proc->write_buf,"startwcn",8) == 0){
+ first_boot = 0;
+ return count;
+ }
+
+ if(strncmp(mdbg_proc->write_buf,"dumpwcn",7) == 0){
+ mdbg_channel_init();
+ return count;
+ }
+
+ if(strncmp(mdbg_proc->write_buf,"at+loopcheck",12) == 0){
+ printk(KERN_INFO "mdbg start wake marlin\n");
+ while((set_marlin_wakeup(MDBG_CHANNEL_WRITE,0x3) < 0) && (cnt <= 3))
+ {
+ msleep(300);
+ cnt++;
+ }
+ cnt = 0;
+ }
+ else{
+ mdbg_send(mdbg_proc->write_buf , count, MDBG_WCN_WRITE);
+ }
+
+ return count;
+}
+
+static unsigned int mdbg_proc_poll(struct file *filp, poll_table *wait)
+{
+ struct mdbg_proc_entry *entry = (struct mdbg_proc_entry *)filp->private_data;
+ char *type = entry->name;
+ unsigned int mask = 0;
+ //printk(KERN_INFO "%s type:%s\n",__func__,type);
+
+ if(strcmp(type, "assert") == 0)
+ {
+ poll_wait(filp, &mdbg_proc->assert.rxwait, wait);
+ if(mdbg_proc->assert.rcv_len > 0)
+ mask |= POLLIN | POLLRDNORM;
+ }
+
+ if(strcmp(type, "wdtirq") == 0)
+ {
+ poll_wait(filp, &mdbg_proc->wdtirq.rxwait, wait);
+ if(mdbg_proc->wdtirq.rcv_len > 0)
+ mask |= POLLIN | POLLRDNORM;
+ }
+
+ return mask;
+}
+
+struct file_operations mdbg_proc_fops = {
+ .open = mdbg_proc_open,
+ .release = mdbg_proc_release,
+ .read = mdbg_proc_read,
+ .write = mdbg_proc_write,
+ .poll = mdbg_proc_poll,
+};
+
+LOCAL void mdbg_fs_channel_init(void)
+{
+ int retval=0;
+
+ mdbg_proc->assert.buf = kzalloc(MDBG_ASSERT_SIZE, GFP_KERNEL);
+ retval = sdiodev_readchn_init(MDBG_CHANNEL_ASSERT, mdbg_assert_read,0);
+
+ mdbg_proc->wdtirq.buf = kzalloc(MDBG_WDTIRQ_SIZE, GFP_KERNEL);
+ retval = sdiodev_readchn_init(MDBG_CHANNEL_WDTIRQ, mdbg_wdtirq_read,0);
+
+ mdbg_proc->loopcheck.buf = kzalloc(MDBG_LOOPCHECK_SIZE, GFP_KERNEL);
+ retval = sdiodev_readchn_init(MDBG_CHANNEL_LOOPCHECK, mdbg_loopcheck_read,0);
+
+ mdbg_proc->at_cmd.buf = kzalloc(MDBG_AT_CMD_SIZE, GFP_KERNEL);
+ retval = sdiodev_readchn_init(MDBG_CHANNEL_AT_CMD, mdbg_at_cmd_read,0);
+}
+
+LOCAL void mdbg_fs_channel_uninit(void)
+{
+ sdiodev_readchn_uninit(MDBG_CHANNEL_ASSERT);
+ kfree(mdbg_proc->assert.buf);
+ mdbg_proc->assert.buf= NULL;
+
+ sdiodev_readchn_uninit(MDBG_CHANNEL_WDTIRQ);
+ kfree(mdbg_proc->wdtirq.buf);
+ mdbg_proc->wdtirq.buf= NULL;
+
+ sdiodev_readchn_uninit(MDBG_CHANNEL_LOOPCHECK);
+ kfree(mdbg_proc->loopcheck.buf);
+ mdbg_proc->loopcheck.buf= NULL;
+
+ sdiodev_readchn_uninit(MDBG_CHANNEL_AT_CMD);
+ kfree(mdbg_proc->at_cmd.buf);
+ mdbg_proc->at_cmd.buf= NULL;
+}
+
+
+LOCAL void mdbg_fs_init(void)
+{
+ mdbg_proc = kzalloc(sizeof(struct mdbg_proc_t), GFP_KERNEL);
+ if (!mdbg_proc)
+ return;
+
+ mdbg_proc->dir_name = "mdbg";
+ mdbg_proc->procdir = proc_mkdir(mdbg_proc->dir_name, NULL);
+
+ mdbg_proc->assert.name = "assert";
+ mdbg_proc->assert.entry = proc_create_data(mdbg_proc->assert.name, S_IRUSR |S_IWUSR,
+ mdbg_proc->procdir, &mdbg_proc_fops, &(mdbg_proc->assert));
+
+ mdbg_proc->wdtirq.name = "wdtirq";
+ mdbg_proc->wdtirq.entry = proc_create_data(mdbg_proc->wdtirq.name, S_IRUSR | S_IWUSR,
+ mdbg_proc->procdir, &mdbg_proc_fops, &(mdbg_proc->wdtirq));
+
+ mdbg_proc->loopcheck.name = "loopcheck";
+ mdbg_proc->loopcheck.entry = proc_create_data(mdbg_proc->loopcheck.name, S_IRUSR | S_IWUSR,
+ mdbg_proc->procdir, &mdbg_proc_fops, &(mdbg_proc->loopcheck));
+
+ mdbg_proc->at_cmd.name = "at_cmd";
+ mdbg_proc->at_cmd.entry = proc_create_data(mdbg_proc->at_cmd.name, S_IRUSR | S_IWUSR,
+ mdbg_proc->procdir, &mdbg_proc_fops, &(mdbg_proc->at_cmd));
+
+ mdbg_fs_channel_init();
+
+ init_completion(&mdbg_proc->assert.completed);
+ init_completion(&mdbg_proc->wdtirq.completed);
+ init_completion(&mdbg_proc->loopcheck.completed);
+ init_completion(&mdbg_proc->at_cmd.completed);
+ init_waitqueue_head(&mdbg_proc->assert.rxwait);
+ init_waitqueue_head(&mdbg_proc->wdtirq.rxwait);
+ init_waitqueue_head(&mdbg_dev->rxwait);
+}
+
+LOCAL void mdbg_fs_init_exit(void)
+{
+ mdbg_fs_channel_uninit();
+
+ remove_proc_entry(mdbg_proc->assert.name , mdbg_proc->procdir);
+ remove_proc_entry(mdbg_proc->wdtirq.name , mdbg_proc->procdir);
+ remove_proc_entry(mdbg_proc->loopcheck.name , mdbg_proc->procdir);
+ remove_proc_entry(mdbg_proc->at_cmd.name , mdbg_proc->procdir);
+ remove_proc_entry(mdbg_proc->dir_name, NULL);
+
+ kfree(mdbg_proc);
+ mdbg_proc=NULL;
+}
+
+
+LOCAL ssize_t mdbg_read(struct file *filp,char __user *buf,size_t count,loff_t *pos)
+{
+ MDBG_SIZE_T read_size;;
+ int timeout = -1;
+ int rval;
+
+ if (filp->f_flags & O_NONBLOCK) {
+ timeout = 0;
+ }
+
+ if(count > MDBG_MAX_BUFFER_LEN)
+ count = MDBG_MAX_BUFFER_LEN;
+
+ if (timeout < 0){
+ /* wait forever */
+ rval = wait_event_interruptible(mdbg_wait,mdbg_read_count > 0);
+ if (rval < 0){
+ MDBG_ERR("mdbg_read wait interrupted!\n");
+ }
+ }else{
+ //MDBG_ERR("mdbg_read no blok\n");
+ }
+
+ mutex_lock(&mdbg_dev->mdbg_lock);
+
+ if(mdbg_read_count <= 0){
+ mutex_unlock(&mdbg_dev->mdbg_lock);
+ //MDBG_ERR("data no ready");
+ return 0;
+ }
+
+ read_size = mdbg_receive(mdbg_dev->read_buf , MDBG_MAX_BUFFER_LEN);
+ printk_ratelimited(KERN_INFO "%s read_size: %d mdbg_read_count:%d\n",__func__,read_size,mdbg_read_count);
+
+ if((read_size > 0) && (read_size <= MDBG_MAX_BUFFER_LEN)){
+ MDBG_LOG("Show %d bytes data.",read_size);
+
+ if(copy_to_user((void __user *)buf,mdbg_dev->read_buf ,read_size)){
+ mutex_unlock(&mdbg_dev->mdbg_lock);
+ MDBG_ERR("copy from user fail!");
+ return -EFAULT;
+ }
+ mdbg_read_count -= read_size;
+ mutex_unlock(&mdbg_dev->mdbg_lock);
+ return read_size;
+ }else{
+ mdbg_read_count = 0;
+ mutex_unlock(&mdbg_dev->mdbg_lock);
+ MDBG_LOG("Show no data");
+ return (0);
+ }
+}
+
+LOCAL ssize_t mdbg_write(struct file *filp, const char __user *buf,size_t count,loff_t *pos)
+{
+ MDBG_SIZE_T sent_size = 0;
+
+ if(NULL == buf || 0 == count){
+ MDBG_ERR("Param Error!");
+ return count;
+ }
+ mutex_lock(&mdbg_dev->mdbg_lock);
+
+ if (count > MDBG_MAX_BUFFER_LEN ){
+ mutex_unlock(&mdbg_dev->mdbg_lock);
+ MDBG_ERR("write too long!");
+ return -EINVAL;
+ }
+
+ memset(mdbg_dev->write_buf,0,MDBG_MAX_BUFFER_LEN);
+ if (copy_from_user(mdbg_dev->write_buf ,(void __user *)buf,count )){
+ mutex_unlock(&mdbg_dev->mdbg_lock);
+ MDBG_ERR("to user fail!");
+ return -EFAULT;
+ }
+
+ sent_size = mdbg_send(mdbg_dev->write_buf , count, MDBG_CHANNEL_WRITE);
+ mutex_unlock(&mdbg_dev->mdbg_lock);
+
+ MDBG_LOG("sent_size = %d",sent_size);
+ return count;
+}
+
+static int mdbg_open(struct inode *inode,struct file *filp)
+{
+ //printk(KERN_INFO "MDBG:%s entry\n",__func__);
+
+ if (mdbg_dev->open_count!=0) {
+ MDBG_ERR( "mdbg_open %d \n",mdbg_dev->open_count);
+ }
+
+ mdbg_dev->open_count++;
+
+ return 0;
+}
+
+static int mdbg_release(struct inode *inode,struct file *filp)
+{
+ //printk(KERN_INFO "MDBG:%s entry\n",__func__);
+
+ mdbg_dev->open_count--;
+ return 0;
+}
+
+static unsigned int mdbg_poll(struct file *filp, poll_table *wait)
+{
+ unsigned int mask = 0;
+
+ poll_wait(filp, &mdbg_dev->rxwait, wait);
+ if(mdbg_read_count > 0)
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+}
+
+static struct file_operations mdbg_fops = {
+ .owner = THIS_MODULE,
+ .read = mdbg_read,
+ .write = mdbg_write,
+ .open = mdbg_open,
+ .release = mdbg_release,
+ .poll = mdbg_poll,
+};
+static struct miscdevice mdbg_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "slog_wcn",
+ .fops = &mdbg_fops,
+};
+
+LOCAL int __init mdbg_module_init(void)
+{
+ int err;
+
+ MDBG_FUNC_ENTERY;
+
+ mdbg_dev = kzalloc(sizeof(*mdbg_dev), GFP_KERNEL);
+ if (!mdbg_dev)
+ return -ENOMEM;
+
+ mdbg_dev->read_buf = kzalloc(MDBG_MAX_BUFFER_LEN, GFP_KERNEL);
+ if (mdbg_dev->read_buf == NULL) {
+ MDBG_ERR("faile (NO MEM) Error!");
+ return -ENOMEM;
+ }
+ mdbg_dev->write_buf = kzalloc(MDBG_MAX_BUFFER_LEN, GFP_KERNEL);
+ if (mdbg_dev->write_buf == NULL) {
+ kfree(mdbg_dev->read_buf);
+ mdbg_dev->write_buf = NULL;
+ MDBG_ERR("faile (NO MEM) Error!");
+ return -ENOMEM;
+ }
+
+ mdbg_dev->open_count = 0;
+ mutex_init(&mdbg_dev->mdbg_lock);
+ init_waitqueue_head(&mdbg_wait);
+ err = mdbg_sdio_init();
+ if(err < 0)
+ return -ENOMEM;
+
+ mdbg_fs_init();
+
+ return misc_register(&mdbg_device);
+}
+
+LOCAL void __exit mdbg_module_exit(void)
+{
+ MDBG_FUNC_ENTERY;
+ mdbg_sdio_remove();
+ mdbg_fs_init_exit();
+ mutex_destroy(&mdbg_dev->mdbg_lock);
+ kfree(mdbg_dev->read_buf);
+ kfree(mdbg_dev->write_buf);
+ mdbg_dev->read_buf = NULL;
+ mdbg_dev->write_buf = NULL;
+ misc_deregister(&mdbg_device);
+}
+
+module_init(mdbg_module_init);
+module_exit(mdbg_module_exit);
+
+MODULE_AUTHOR("Fan Kou<fan.kou@spreadtrum.com>");
+MODULE_DESCRIPTION("MARLIN DEBUG SYSTEM.");
+MODULE_LICENSE("GPL");
+