Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/modem_notifier.c b/arch/arm/mach-msm/modem_notifier.c
new file mode 100644
index 0000000..d92098b
--- /dev/null
+++ b/arch/arm/mach-msm/modem_notifier.c
@@ -0,0 +1,213 @@
+/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+/*
+ * Modem Restart Notifier -- Provides notification
+ *			     of modem restart events.
+ */
+
+#include <linux/notifier.h>
+#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+
+#include "modem_notifier.h"
+
+#define DEBUG
+
+static struct srcu_notifier_head modem_notifier_list;
+static struct workqueue_struct *modem_notifier_wq;
+
+static void notify_work_smsm_init(struct work_struct *work)
+{
+	modem_notify(0, MODEM_NOTIFIER_SMSM_INIT);
+}
+static DECLARE_WORK(modem_notifier_smsm_init_work, &notify_work_smsm_init);
+
+void modem_queue_smsm_init_notify(void)
+{
+	int ret;
+
+	ret = queue_work(modem_notifier_wq, &modem_notifier_smsm_init_work);
+
+	if (!ret)
+		printk(KERN_ERR "%s\n", __func__);
+}
+EXPORT_SYMBOL(modem_queue_smsm_init_notify);
+
+static void notify_work_start_reset(struct work_struct *work)
+{
+	modem_notify(0, MODEM_NOTIFIER_START_RESET);
+}
+static DECLARE_WORK(modem_notifier_start_reset_work, &notify_work_start_reset);
+
+void modem_queue_start_reset_notify(void)
+{
+	int ret;
+
+	ret = queue_work(modem_notifier_wq, &modem_notifier_start_reset_work);
+
+	if (!ret)
+		printk(KERN_ERR "%s\n", __func__);
+}
+EXPORT_SYMBOL(modem_queue_start_reset_notify);
+
+static void notify_work_end_reset(struct work_struct *work)
+{
+	modem_notify(0, MODEM_NOTIFIER_END_RESET);
+}
+static DECLARE_WORK(modem_notifier_end_reset_work, &notify_work_end_reset);
+
+void modem_queue_end_reset_notify(void)
+{
+	int ret;
+
+	ret = queue_work(modem_notifier_wq, &modem_notifier_end_reset_work);
+
+	if (!ret)
+		printk(KERN_ERR "%s\n", __func__);
+}
+EXPORT_SYMBOL(modem_queue_end_reset_notify);
+
+int modem_register_notifier(struct notifier_block *nb)
+{
+	int ret;
+
+	ret = srcu_notifier_chain_register(
+		&modem_notifier_list, nb);
+
+	return ret;
+}
+EXPORT_SYMBOL(modem_register_notifier);
+
+int modem_unregister_notifier(struct notifier_block *nb)
+{
+	int ret;
+
+	ret = srcu_notifier_chain_unregister(
+		&modem_notifier_list, nb);
+
+	return ret;
+}
+EXPORT_SYMBOL(modem_unregister_notifier);
+
+void modem_notify(void *data, unsigned int state)
+{
+	srcu_notifier_call_chain(&modem_notifier_list, state, data);
+}
+EXPORT_SYMBOL(modem_notify);
+
+#if defined(CONFIG_DEBUG_FS)
+static int debug_reset_start(const char __user *buf, int count)
+{
+	modem_queue_start_reset_notify();
+	return 0;
+}
+
+static int debug_reset_end(const char __user *buf, int count)
+{
+	modem_queue_end_reset_notify();
+	return 0;
+}
+
+static ssize_t debug_write(struct file *file, const char __user *buf,
+			   size_t count, loff_t *ppos)
+{
+	int (*fling)(const char __user *buf, int max) = file->private_data;
+	fling(buf, count);
+	return count;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static const struct file_operations debug_ops = {
+	.write = debug_write,
+	.open = debug_open,
+};
+
+static void debug_create(const char *name, mode_t mode,
+			 struct dentry *dent,
+			 int (*fling)(const char __user *buf, int max))
+{
+	debugfs_create_file(name, mode, dent, fling, &debug_ops);
+}
+
+static void modem_notifier_debugfs_init(void)
+{
+	struct dentry *dent;
+
+	dent = debugfs_create_dir("modem_notifier", 0);
+	if (IS_ERR(dent))
+		return;
+
+	debug_create("reset_start", 0444, dent, debug_reset_start);
+	debug_create("reset_end", 0444, dent, debug_reset_end);
+}
+#else
+static void modem_notifier_debugfs_init(void) {}
+#endif
+
+#if defined(DEBUG)
+static int modem_notifier_test_call(struct notifier_block *this,
+				  unsigned long code,
+				  void *_cmd)
+{
+	switch (code) {
+	case MODEM_NOTIFIER_START_RESET:
+		printk(KERN_ERR "Notify: start reset\n");
+		break;
+	case MODEM_NOTIFIER_END_RESET:
+		printk(KERN_ERR "Notify: end reset\n");
+		break;
+	case MODEM_NOTIFIER_SMSM_INIT:
+		printk(KERN_ERR "Notify: smsm init\n");
+		break;
+	default:
+		printk(KERN_ERR "Notify: general\n");
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block nb = {
+	.notifier_call = modem_notifier_test_call,
+};
+
+static void register_test_notifier(void)
+{
+	modem_register_notifier(&nb);
+}
+#endif
+
+static int __init init_modem_notifier_list(void)
+{
+	srcu_init_notifier_head(&modem_notifier_list);
+	modem_notifier_debugfs_init();
+#if defined(DEBUG)
+	register_test_notifier();
+#endif
+
+	/* Create the workqueue */
+	modem_notifier_wq = create_singlethread_workqueue("modem_notifier");
+	if (!modem_notifier_wq) {
+		srcu_cleanup_notifier_head(&modem_notifier_list);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+module_init(init_modem_notifier_list);