| /* Copyright (c) 2012, 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. |
| */ |
| |
| #include <mach/ocmem_priv.h> |
| #include <linux/hardirq.h> |
| |
| static unsigned notifier_threshold; |
| |
| /* Protect the notifier structure below */ |
| DEFINE_MUTEX(nc_lock); |
| |
| struct ocmem_notifier { |
| int owner; |
| struct atomic_notifier_head nc; |
| unsigned listeners; |
| } notifiers[OCMEM_CLIENT_MAX]; |
| |
| int check_notifier(int id) |
| { |
| int ret = 0; |
| |
| if (!check_id(id)) |
| return 0; |
| |
| mutex_lock(&nc_lock); |
| ret = notifiers[id].listeners; |
| mutex_unlock(&nc_lock); |
| return ret; |
| } |
| |
| int ocmem_notifier_init(void) |
| { |
| int id; |
| /* Maximum notifiers for each subsystem */ |
| notifier_threshold = 1; |
| mutex_lock(&nc_lock); |
| for (id = 0; id < OCMEM_CLIENT_MAX; id++) { |
| notifiers[id].owner = id; |
| ATOMIC_INIT_NOTIFIER_HEAD(¬ifiers[id].nc); |
| notifiers[id].listeners = 0; |
| } |
| mutex_unlock(&nc_lock); |
| return 0; |
| } |
| |
| /* Broadcast a notification to listeners */ |
| int dispatch_notification(int id, enum ocmem_notif_type notif, |
| struct ocmem_buf *buf) |
| { |
| int ret = 0; |
| struct ocmem_notifier *nc_hndl = NULL; |
| mutex_lock(&nc_lock); |
| nc_hndl = ¬ifiers[id]; |
| if (nc_hndl->listeners == 0) { |
| /* Send an error so that the scheduler can clean up */ |
| mutex_unlock(&nc_lock); |
| return -EINVAL; |
| } |
| ret = atomic_notifier_call_chain(¬ifiers[id].nc, notif, buf); |
| mutex_unlock(&nc_lock); |
| return ret; |
| } |
| |
| struct ocmem_notifier *ocmem_notifier_register(int client_id, |
| struct notifier_block *nb) |
| { |
| |
| int ret = 0; |
| struct ocmem_notifier *nc_hndl = NULL; |
| |
| if (!check_id(client_id)) { |
| pr_err("ocmem: Invalid Client id\n"); |
| return NULL; |
| } |
| |
| if (!zone_active(client_id)) { |
| pr_err("ocmem: Client %s (id: %d) not allowed to use OCMEM\n", |
| get_name(client_id), client_id); |
| return NULL; |
| } |
| |
| if (!nb) { |
| pr_err("ocmem: Invalid Notifier Block\n"); |
| return NULL; |
| } |
| |
| mutex_lock(&nc_lock); |
| |
| nc_hndl = ¬ifiers[client_id]; |
| |
| if (nc_hndl->listeners >= notifier_threshold) { |
| pr_err("ocmem: Max notifiers already registered\n"); |
| mutex_unlock(&nc_lock); |
| return NULL; |
| } |
| |
| ret = atomic_notifier_chain_register(&nc_hndl->nc, nb); |
| |
| if (ret < 0) { |
| mutex_unlock(&nc_lock); |
| return NULL; |
| } |
| |
| nc_hndl->listeners++; |
| pr_info("ocmem: Notifier registered for %d\n", client_id); |
| mutex_unlock(&nc_lock); |
| return nc_hndl; |
| } |
| EXPORT_SYMBOL(ocmem_notifier_register); |
| |
| int ocmem_notifier_unregister(struct ocmem_notifier *nc_hndl, |
| struct notifier_block *nb) |
| { |
| |
| int ret = 0; |
| |
| if (!nc_hndl) { |
| pr_err("ocmem: Invalid notification handle\n"); |
| return -EINVAL; |
| } |
| |
| mutex_lock(&nc_lock); |
| ret = atomic_notifier_chain_unregister(&nc_hndl->nc, nb); |
| nc_hndl->listeners--; |
| mutex_unlock(&nc_lock); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL(ocmem_notifier_unregister); |