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, ¬ify_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, ¬ify_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, ¬ify_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);