221 lines
4.9 KiB
C
221 lines
4.9 KiB
C
/* Copyright (c) 2008-2010, 2012, The Linux Foundation. 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
|
|
|
|
int __init msm_init_modem_notifier_list(void)
|
|
{
|
|
static bool registered;
|
|
|
|
if (registered)
|
|
return 0;
|
|
|
|
registered = true;
|
|
|
|
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(msm_init_modem_notifier_list);
|