1101 lines
30 KiB
C
1101 lines
30 KiB
C
|
/* Copyright (c) 2013, 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.
|
||
|
*/
|
||
|
|
||
|
#include <linux/slab.h>
|
||
|
#include "ipa_i.h"
|
||
|
#include "ipa_rm_resource.h"
|
||
|
#include "ipa_rm_i.h"
|
||
|
|
||
|
/**
|
||
|
* ipa_rm_dep_prod_index() - producer name to producer index mapping
|
||
|
* @resource_name: [in] resource name (should be of producer)
|
||
|
*
|
||
|
* Returns: resource index mapping, IPA_RM_INDEX_INVALID
|
||
|
* in case provided resource name isn't contained
|
||
|
* in enum ipa_rm_resource_name or is not of producers.
|
||
|
*
|
||
|
*/
|
||
|
int ipa_rm_prod_index(enum ipa_rm_resource_name resource_name)
|
||
|
{
|
||
|
int result = resource_name;
|
||
|
switch (resource_name) {
|
||
|
case IPA_RM_RESOURCE_BRIDGE_PROD:
|
||
|
case IPA_RM_RESOURCE_A2_PROD:
|
||
|
case IPA_RM_RESOURCE_USB_PROD:
|
||
|
case IPA_RM_RESOURCE_HSIC_PROD:
|
||
|
case IPA_RM_RESOURCE_STD_ECM_PROD:
|
||
|
case IPA_RM_RESOURCE_WWAN_0_PROD:
|
||
|
case IPA_RM_RESOURCE_WWAN_1_PROD:
|
||
|
case IPA_RM_RESOURCE_WWAN_2_PROD:
|
||
|
case IPA_RM_RESOURCE_WWAN_3_PROD:
|
||
|
case IPA_RM_RESOURCE_WWAN_4_PROD:
|
||
|
case IPA_RM_RESOURCE_WWAN_5_PROD:
|
||
|
case IPA_RM_RESOURCE_WWAN_6_PROD:
|
||
|
case IPA_RM_RESOURCE_WWAN_7_PROD:
|
||
|
case IPA_RM_RESOURCE_WLAN_PROD:
|
||
|
break;
|
||
|
default:
|
||
|
result = IPA_RM_INDEX_INVALID;
|
||
|
break;
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ipa_rm_cons_index() - consumer name to consumer index mapping
|
||
|
* @resource_name: [in] resource name (should be of consumer)
|
||
|
*
|
||
|
* Returns: resource index mapping, IPA_RM_INDEX_INVALID
|
||
|
* in case provided resource name isn't contained
|
||
|
* in enum ipa_rm_resource_name or is not of consumers.
|
||
|
*
|
||
|
*/
|
||
|
int ipa_rm_cons_index(enum ipa_rm_resource_name resource_name)
|
||
|
{
|
||
|
int result = resource_name;
|
||
|
switch (resource_name) {
|
||
|
case IPA_RM_RESOURCE_A2_CONS:
|
||
|
case IPA_RM_RESOURCE_USB_CONS:
|
||
|
case IPA_RM_RESOURCE_HSIC_CONS:
|
||
|
break;
|
||
|
default:
|
||
|
result = IPA_RM_INDEX_INVALID;
|
||
|
break;
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static int ipa_rm_resource_consumer_request(
|
||
|
struct ipa_rm_resource_cons *consumer)
|
||
|
{
|
||
|
int result = 0;
|
||
|
int driver_result;
|
||
|
unsigned long flags;
|
||
|
|
||
|
spin_lock_irqsave(&consumer->resource.state_lock, flags);
|
||
|
IPA_RM_DBG("%s state: %d\n",
|
||
|
ipa_rm_resource_str(consumer->resource.name),
|
||
|
consumer->resource.state);
|
||
|
switch (consumer->resource.state) {
|
||
|
case IPA_RM_RELEASED:
|
||
|
case IPA_RM_RELEASE_IN_PROGRESS:
|
||
|
{
|
||
|
enum ipa_rm_resource_state prev_state =
|
||
|
consumer->resource.state;
|
||
|
consumer->resource.state = IPA_RM_REQUEST_IN_PROGRESS;
|
||
|
spin_unlock_irqrestore(&consumer->resource.state_lock, flags);
|
||
|
IPA_RM_DBG("calling driver CB\n");
|
||
|
driver_result = consumer->request_resource();
|
||
|
IPA_RM_DBG("driver CB returned with %d\n", driver_result);
|
||
|
spin_lock_irqsave(&consumer->resource.state_lock, flags);
|
||
|
if (driver_result == 0)
|
||
|
consumer->resource.state = IPA_RM_GRANTED;
|
||
|
else if (driver_result != -EINPROGRESS) {
|
||
|
consumer->resource.state = prev_state;
|
||
|
result = driver_result;
|
||
|
goto bail;
|
||
|
}
|
||
|
result = driver_result;
|
||
|
break;
|
||
|
}
|
||
|
case IPA_RM_GRANTED:
|
||
|
break;
|
||
|
case IPA_RM_REQUEST_IN_PROGRESS:
|
||
|
result = -EINPROGRESS;
|
||
|
break;
|
||
|
default:
|
||
|
result = -EPERM;
|
||
|
goto bail;
|
||
|
}
|
||
|
consumer->usage_count++;
|
||
|
bail:
|
||
|
IPA_RM_DBG("%s new state: %d\n",
|
||
|
ipa_rm_resource_str(consumer->resource.name),
|
||
|
consumer->resource.state);
|
||
|
spin_unlock_irqrestore(&consumer->resource.state_lock, flags);
|
||
|
IPA_RM_DBG("EXIT with %d\n", result);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static int ipa_rm_resource_consumer_release(
|
||
|
struct ipa_rm_resource_cons *consumer)
|
||
|
{
|
||
|
int result = 0;
|
||
|
int driver_result;
|
||
|
unsigned long flags;
|
||
|
enum ipa_rm_resource_state save_state;
|
||
|
|
||
|
spin_lock_irqsave(&consumer->resource.state_lock, flags);
|
||
|
IPA_RM_DBG("%s state: %d\n",
|
||
|
ipa_rm_resource_str(consumer->resource.name),
|
||
|
consumer->resource.state);
|
||
|
switch (consumer->resource.state) {
|
||
|
case IPA_RM_RELEASED:
|
||
|
break;
|
||
|
case IPA_RM_GRANTED:
|
||
|
case IPA_RM_REQUEST_IN_PROGRESS:
|
||
|
if (consumer->usage_count > 0)
|
||
|
consumer->usage_count--;
|
||
|
if (consumer->usage_count == 0) {
|
||
|
save_state = consumer->resource.state;
|
||
|
consumer->resource.state = IPA_RM_RELEASE_IN_PROGRESS;
|
||
|
spin_unlock_irqrestore(&consumer->resource.state_lock,
|
||
|
flags);
|
||
|
IPA_RM_DBG("calling driver CB\n");
|
||
|
driver_result = consumer->release_resource();
|
||
|
IPA_RM_DBG("driver CB returned with %d\n",
|
||
|
driver_result);
|
||
|
spin_lock_irqsave(&consumer->resource.state_lock,
|
||
|
flags);
|
||
|
if (driver_result == 0)
|
||
|
consumer->resource.state = IPA_RM_RELEASED;
|
||
|
else if (driver_result != -EINPROGRESS)
|
||
|
consumer->resource.state = save_state;
|
||
|
result = driver_result;
|
||
|
}
|
||
|
break;
|
||
|
case IPA_RM_RELEASE_IN_PROGRESS:
|
||
|
if (consumer->usage_count > 0)
|
||
|
consumer->usage_count--;
|
||
|
result = -EINPROGRESS;
|
||
|
break;
|
||
|
default:
|
||
|
result = -EPERM;
|
||
|
goto bail;
|
||
|
}
|
||
|
bail:
|
||
|
IPA_RM_DBG("%s new state: %d\n",
|
||
|
ipa_rm_resource_str(consumer->resource.name),
|
||
|
consumer->resource.state);
|
||
|
spin_unlock_irqrestore(&consumer->resource.state_lock, flags);
|
||
|
IPA_RM_DBG("EXIT with %d\n", result);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ipa_rm_resource_producer_notify_clients() - notify
|
||
|
* all registered clients of given producer
|
||
|
* @producer: producer
|
||
|
* @event: event to notify
|
||
|
* @notify_registered_only: notify only clients registered by
|
||
|
* ipa_rm_register()
|
||
|
*/
|
||
|
void ipa_rm_resource_producer_notify_clients(
|
||
|
struct ipa_rm_resource_prod *producer,
|
||
|
enum ipa_rm_event event,
|
||
|
bool notify_registered_only)
|
||
|
{
|
||
|
struct ipa_rm_notification_info *reg_info, *reg_info_cloned;
|
||
|
struct list_head *pos, *q;
|
||
|
LIST_HEAD(cloned_list);
|
||
|
|
||
|
IPA_RM_DBG("%s event: %d notify_registered_only: %d\n",
|
||
|
ipa_rm_resource_str(producer->resource.name),
|
||
|
event,
|
||
|
notify_registered_only);
|
||
|
read_lock(&producer->event_listeners_lock);
|
||
|
list_for_each(pos, &(producer->event_listeners)) {
|
||
|
reg_info = list_entry(pos,
|
||
|
struct ipa_rm_notification_info,
|
||
|
link);
|
||
|
if (notify_registered_only && !reg_info->explicit)
|
||
|
continue;
|
||
|
reg_info_cloned = kzalloc(sizeof(*reg_info_cloned), GFP_KERNEL);
|
||
|
if (!reg_info_cloned) {
|
||
|
IPA_RM_ERR("No memory\n");
|
||
|
goto clone_list_failed;
|
||
|
}
|
||
|
reg_info_cloned->reg_params.notify_cb =
|
||
|
reg_info->reg_params.notify_cb;
|
||
|
reg_info_cloned->reg_params.user_data =
|
||
|
reg_info->reg_params.user_data;
|
||
|
list_add(®_info_cloned->link, &cloned_list);
|
||
|
}
|
||
|
read_unlock(&producer->event_listeners_lock);
|
||
|
list_for_each_safe(pos, q, &cloned_list) {
|
||
|
reg_info = list_entry(pos,
|
||
|
struct ipa_rm_notification_info,
|
||
|
link);
|
||
|
IPA_RM_DBG("Notifying %s event: %d\n",
|
||
|
ipa_rm_resource_str(producer->resource.name), event);
|
||
|
reg_info->reg_params.notify_cb(
|
||
|
reg_info->reg_params.user_data,
|
||
|
event,
|
||
|
0);
|
||
|
IPA_RM_DBG("back from client CB\n");
|
||
|
list_del(pos);
|
||
|
kfree(reg_info);
|
||
|
}
|
||
|
return;
|
||
|
clone_list_failed:
|
||
|
read_unlock(&producer->event_listeners_lock);
|
||
|
}
|
||
|
|
||
|
static int ipa_rm_resource_producer_create(struct ipa_rm_resource **resource,
|
||
|
struct ipa_rm_resource_prod **producer,
|
||
|
struct ipa_rm_create_params *create_params,
|
||
|
int *max_peers)
|
||
|
{
|
||
|
int result = 0;
|
||
|
*producer = kzalloc(sizeof(**producer), GFP_KERNEL);
|
||
|
if (*producer == NULL) {
|
||
|
IPA_RM_ERR("no mem\n");
|
||
|
result = -ENOMEM;
|
||
|
goto bail;
|
||
|
}
|
||
|
rwlock_init(&(*producer)->event_listeners_lock);
|
||
|
INIT_LIST_HEAD(&((*producer)->event_listeners));
|
||
|
result = ipa_rm_resource_producer_register(*producer,
|
||
|
&(create_params->reg_params),
|
||
|
false);
|
||
|
if (result) {
|
||
|
IPA_RM_ERR("ipa_rm_resource_producer_register() failed\n");
|
||
|
goto register_fail;
|
||
|
}
|
||
|
(*resource) = (struct ipa_rm_resource *) (*producer);
|
||
|
(*resource)->type = IPA_RM_PRODUCER;
|
||
|
*max_peers = IPA_RM_RESOURCE_CONS_MAX;
|
||
|
goto bail;
|
||
|
register_fail:
|
||
|
kfree(*producer);
|
||
|
bail:
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static void ipa_rm_resource_producer_delete(
|
||
|
struct ipa_rm_resource_prod *producer)
|
||
|
{
|
||
|
struct ipa_rm_notification_info *reg_info;
|
||
|
struct list_head *pos, *q;
|
||
|
write_lock(&producer->event_listeners_lock);
|
||
|
list_for_each_safe(pos, q, &(producer->event_listeners)) {
|
||
|
reg_info = list_entry(pos,
|
||
|
struct ipa_rm_notification_info,
|
||
|
link);
|
||
|
list_del(pos);
|
||
|
kfree(reg_info);
|
||
|
}
|
||
|
write_unlock(&producer->event_listeners_lock);
|
||
|
}
|
||
|
|
||
|
static int ipa_rm_resource_consumer_create(struct ipa_rm_resource **resource,
|
||
|
struct ipa_rm_resource_cons **consumer,
|
||
|
struct ipa_rm_create_params *create_params,
|
||
|
int *max_peers)
|
||
|
{
|
||
|
int result = 0;
|
||
|
*consumer = kzalloc(sizeof(**consumer), GFP_KERNEL);
|
||
|
if (*consumer == NULL) {
|
||
|
IPA_RM_ERR("no mem\n");
|
||
|
result = -ENOMEM;
|
||
|
goto bail;
|
||
|
}
|
||
|
(*consumer)->request_resource = create_params->request_resource;
|
||
|
(*consumer)->release_resource = create_params->release_resource;
|
||
|
(*resource) = (struct ipa_rm_resource *) (*consumer);
|
||
|
(*resource)->type = IPA_RM_CONSUMER;
|
||
|
*max_peers = IPA_RM_RESOURCE_PROD_MAX;
|
||
|
bail:
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ipa_rm_resource_create() - creates resource
|
||
|
* @create_params: [in] parameters needed
|
||
|
* for resource initialization with IPA RM
|
||
|
* @resource: [out] created resource
|
||
|
*
|
||
|
* Returns: 0 on success, negative on failure
|
||
|
*/
|
||
|
int ipa_rm_resource_create(
|
||
|
struct ipa_rm_create_params *create_params,
|
||
|
struct ipa_rm_resource **resource)
|
||
|
{
|
||
|
struct ipa_rm_resource_cons *consumer;
|
||
|
struct ipa_rm_resource_prod *producer;
|
||
|
int max_peers;
|
||
|
int result = 0;
|
||
|
|
||
|
if (!create_params) {
|
||
|
result = -EINVAL;
|
||
|
goto bail;
|
||
|
}
|
||
|
if (IPA_RM_RESORCE_IS_PROD(create_params->name)) {
|
||
|
result = ipa_rm_resource_producer_create(resource,
|
||
|
&producer,
|
||
|
create_params,
|
||
|
&max_peers);
|
||
|
if (result) {
|
||
|
IPA_RM_ERR("ipa_rm_resource_producer_create failed\n");
|
||
|
goto bail;
|
||
|
}
|
||
|
} else if (IPA_RM_RESORCE_IS_CONS(create_params->name)) {
|
||
|
result = ipa_rm_resource_consumer_create(resource,
|
||
|
&consumer,
|
||
|
create_params,
|
||
|
&max_peers);
|
||
|
if (result) {
|
||
|
IPA_RM_ERR("ipa_rm_resource_producer_create failed\n");
|
||
|
goto bail;
|
||
|
}
|
||
|
} else {
|
||
|
IPA_RM_ERR("invalied resource\n");
|
||
|
result = -EPERM;
|
||
|
goto bail;
|
||
|
}
|
||
|
result = ipa_rm_peers_list_create(max_peers,
|
||
|
&((*resource)->peers_list));
|
||
|
if (result) {
|
||
|
IPA_RM_ERR("ipa_rm_peers_list_create failed\n");
|
||
|
goto peers_alloc_fail;
|
||
|
}
|
||
|
(*resource)->name = create_params->name;
|
||
|
(*resource)->state = IPA_RM_RELEASED;
|
||
|
spin_lock_init(&((*resource)->state_lock));
|
||
|
goto bail;
|
||
|
peers_alloc_fail:
|
||
|
ipa_rm_resource_delete(*resource);
|
||
|
bail:
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ipa_rm_resource_delete() - deletes resource
|
||
|
* @resource: [in] resource
|
||
|
* for resource initialization with IPA RM
|
||
|
*
|
||
|
* Returns: 0 on success, negative on failure
|
||
|
*/
|
||
|
int ipa_rm_resource_delete(struct ipa_rm_resource *resource)
|
||
|
{
|
||
|
struct ipa_rm_resource *consumer, *producer;
|
||
|
int peers_index, result = 0, list_size;
|
||
|
|
||
|
IPA_RM_DBG("ipa_rm_resource_delete ENTER with resource %d\n",
|
||
|
resource->name);
|
||
|
if (!resource) {
|
||
|
IPA_RM_ERR("invalid params\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
if (resource->type == IPA_RM_PRODUCER) {
|
||
|
if (resource->peers_list) {
|
||
|
list_size = ipa_rm_peers_list_get_size(
|
||
|
resource->peers_list);
|
||
|
for (peers_index = 0;
|
||
|
peers_index < list_size;
|
||
|
peers_index++) {
|
||
|
consumer = ipa_rm_peers_list_get_resource(
|
||
|
peers_index,
|
||
|
resource->peers_list);
|
||
|
if (consumer)
|
||
|
ipa_rm_resource_delete_dependency(
|
||
|
resource,
|
||
|
consumer);
|
||
|
}
|
||
|
ipa_rm_peers_list_delete(resource->peers_list);
|
||
|
}
|
||
|
ipa_rm_resource_producer_delete(
|
||
|
(struct ipa_rm_resource_prod *) resource);
|
||
|
kfree((struct ipa_rm_resource_prod *) resource);
|
||
|
} else if (resource->type == IPA_RM_CONSUMER) {
|
||
|
if (resource->peers_list) {
|
||
|
list_size = ipa_rm_peers_list_get_size(
|
||
|
resource->peers_list);
|
||
|
for (peers_index = 0;
|
||
|
peers_index < list_size;
|
||
|
peers_index++){
|
||
|
producer = ipa_rm_peers_list_get_resource(
|
||
|
peers_index,
|
||
|
resource->peers_list);
|
||
|
if (producer)
|
||
|
ipa_rm_resource_delete_dependency(
|
||
|
producer,
|
||
|
resource);
|
||
|
}
|
||
|
ipa_rm_peers_list_delete(resource->peers_list);
|
||
|
}
|
||
|
kfree((struct ipa_rm_resource_cons *) resource);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ipa_rm_resource_register() - register resource
|
||
|
* @resource: [in] resource
|
||
|
* @reg_params: [in] registration parameters
|
||
|
* @explicit: [in] registered explicitly by ipa_rm_register()
|
||
|
*
|
||
|
* Returns: 0 on success, negative on failure
|
||
|
*
|
||
|
* Producer resource is expected for this call.
|
||
|
*
|
||
|
*/
|
||
|
int ipa_rm_resource_producer_register(struct ipa_rm_resource_prod *producer,
|
||
|
struct ipa_rm_register_params *reg_params,
|
||
|
bool explicit)
|
||
|
{
|
||
|
int result = 0;
|
||
|
struct ipa_rm_notification_info *reg_info;
|
||
|
struct list_head *pos;
|
||
|
if (!producer || !reg_params) {
|
||
|
IPA_RM_ERR("invalid params\n");
|
||
|
result = -EPERM;
|
||
|
goto bail;
|
||
|
}
|
||
|
read_lock(&producer->event_listeners_lock);
|
||
|
list_for_each(pos, &(producer->event_listeners)) {
|
||
|
reg_info = list_entry(pos,
|
||
|
struct ipa_rm_notification_info,
|
||
|
link);
|
||
|
if (reg_info->reg_params.notify_cb ==
|
||
|
reg_params->notify_cb) {
|
||
|
IPA_RM_ERR("already registered\n");
|
||
|
result = -EPERM;
|
||
|
read_unlock(&producer->event_listeners_lock);
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
read_unlock(&producer->event_listeners_lock);
|
||
|
reg_info = kzalloc(sizeof(*reg_info), GFP_KERNEL);
|
||
|
if (reg_info == NULL) {
|
||
|
IPA_RM_ERR("no mem\n");
|
||
|
result = -ENOMEM;
|
||
|
goto bail;
|
||
|
}
|
||
|
reg_info->reg_params.user_data = reg_params->user_data;
|
||
|
reg_info->reg_params.notify_cb = reg_params->notify_cb;
|
||
|
reg_info->explicit = explicit;
|
||
|
INIT_LIST_HEAD(®_info->link);
|
||
|
write_lock(&producer->event_listeners_lock);
|
||
|
list_add(®_info->link, &producer->event_listeners);
|
||
|
write_unlock(&producer->event_listeners_lock);
|
||
|
bail:
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ipa_rm_resource_deregister() - register resource
|
||
|
* @resource: [in] resource
|
||
|
* @reg_params: [in] registration parameters
|
||
|
*
|
||
|
* Returns: 0 on success, negative on failure
|
||
|
*
|
||
|
* Producer resource is expected for this call.
|
||
|
* This function deleted only single instance of
|
||
|
* registration info.
|
||
|
*
|
||
|
*/
|
||
|
int ipa_rm_resource_producer_deregister(struct ipa_rm_resource_prod *producer,
|
||
|
struct ipa_rm_register_params *reg_params)
|
||
|
{
|
||
|
int result = -EINVAL;
|
||
|
struct ipa_rm_notification_info *reg_info;
|
||
|
struct list_head *pos, *q;
|
||
|
if (!producer || !reg_params) {
|
||
|
IPA_RM_ERR("invalid params\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
write_lock(&producer->event_listeners_lock);
|
||
|
list_for_each_safe(pos, q, &(producer->event_listeners)) {
|
||
|
reg_info = list_entry(pos,
|
||
|
struct ipa_rm_notification_info,
|
||
|
link);
|
||
|
if (reg_info->reg_params.notify_cb ==
|
||
|
reg_params->notify_cb) {
|
||
|
list_del(pos);
|
||
|
kfree(reg_info);
|
||
|
result = 0;
|
||
|
goto bail;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
bail:
|
||
|
write_unlock(&producer->event_listeners_lock);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ipa_rm_resource_add_dependency() - add dependency between two
|
||
|
* given resources
|
||
|
* @resource: [in] resource resource
|
||
|
* @depends_on: [in] depends_on resource
|
||
|
*
|
||
|
* Returns: 0 on success, negative on failure
|
||
|
*/
|
||
|
int ipa_rm_resource_add_dependency(struct ipa_rm_resource *resource,
|
||
|
struct ipa_rm_resource *depends_on)
|
||
|
{
|
||
|
int result = 0;
|
||
|
unsigned long flags;
|
||
|
int consumer_result;
|
||
|
if (!resource || !depends_on) {
|
||
|
IPA_RM_ERR("invalid params\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
if (ipa_rm_peers_list_check_dependency(resource->peers_list,
|
||
|
resource->name,
|
||
|
depends_on->peers_list,
|
||
|
depends_on->name)) {
|
||
|
IPA_RM_ERR("dependency already exists\n");
|
||
|
return -EEXIST;
|
||
|
}
|
||
|
ipa_rm_peers_list_add_peer(resource->peers_list, depends_on);
|
||
|
ipa_rm_peers_list_add_peer(depends_on->peers_list, resource);
|
||
|
spin_lock_irqsave(&resource->state_lock, flags);
|
||
|
IPA_RM_DBG("%s state: %d\n", ipa_rm_resource_str(resource->name),
|
||
|
resource->state);
|
||
|
switch (resource->state) {
|
||
|
case IPA_RM_RELEASED:
|
||
|
case IPA_RM_RELEASE_IN_PROGRESS:
|
||
|
break;
|
||
|
case IPA_RM_GRANTED:
|
||
|
case IPA_RM_REQUEST_IN_PROGRESS:
|
||
|
{
|
||
|
enum ipa_rm_resource_state prev_state = resource->state;
|
||
|
resource->state = IPA_RM_REQUEST_IN_PROGRESS;
|
||
|
((struct ipa_rm_resource_prod *)
|
||
|
resource)->pending_request++;
|
||
|
spin_unlock_irqrestore(&resource->state_lock, flags);
|
||
|
consumer_result = ipa_rm_resource_consumer_request(
|
||
|
(struct ipa_rm_resource_cons *)depends_on);
|
||
|
spin_lock_irqsave(&resource->state_lock, flags);
|
||
|
if (consumer_result != -EINPROGRESS) {
|
||
|
resource->state = prev_state;
|
||
|
((struct ipa_rm_resource_prod *)
|
||
|
resource)->pending_request--;
|
||
|
}
|
||
|
result = consumer_result;
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
IPA_RM_ERR("invalid state\n");
|
||
|
result = -EPERM;
|
||
|
goto bail;
|
||
|
}
|
||
|
bail:
|
||
|
IPA_RM_DBG("%s new state: %d\n", ipa_rm_resource_str(resource->name),
|
||
|
resource->state);
|
||
|
spin_unlock_irqrestore(&resource->state_lock, flags);
|
||
|
IPA_RM_DBG("EXIT with %d\n", result);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ipa_rm_resource_delete_dependency() - add dependency between two
|
||
|
* given resources
|
||
|
* @resource: [in] resource resource
|
||
|
* @depends_on: [in] depends_on resource
|
||
|
*
|
||
|
* Returns: 0 on success, negative on failure
|
||
|
* EINPROGRESS is returned in case this is the last dependency
|
||
|
* of given resource and IPA RM client should receive the RELEASED cb
|
||
|
*/
|
||
|
int ipa_rm_resource_delete_dependency(struct ipa_rm_resource *resource,
|
||
|
struct ipa_rm_resource *depends_on)
|
||
|
{
|
||
|
int result = 0;
|
||
|
unsigned long flags;
|
||
|
unsigned long consumer_flags;
|
||
|
bool state_changed = false;
|
||
|
bool release_consumer = false;
|
||
|
enum ipa_rm_event evt;
|
||
|
if (!resource || !depends_on) {
|
||
|
IPA_RM_ERR("invalid params\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
if (!ipa_rm_peers_list_check_dependency(resource->peers_list,
|
||
|
resource->name,
|
||
|
depends_on->peers_list,
|
||
|
depends_on->name)) {
|
||
|
IPA_RM_ERR("dependency does not exist\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
spin_lock_irqsave(&resource->state_lock, flags);
|
||
|
IPA_RM_DBG("%s state: %d\n", ipa_rm_resource_str(resource->name),
|
||
|
resource->state);
|
||
|
switch (resource->state) {
|
||
|
case IPA_RM_RELEASED:
|
||
|
break;
|
||
|
case IPA_RM_GRANTED:
|
||
|
release_consumer = true;
|
||
|
break;
|
||
|
case IPA_RM_RELEASE_IN_PROGRESS:
|
||
|
if (((struct ipa_rm_resource_prod *)
|
||
|
resource)->pending_release > 0)
|
||
|
((struct ipa_rm_resource_prod *)
|
||
|
resource)->pending_release--;
|
||
|
spin_lock_irqsave(&depends_on->state_lock, consumer_flags);
|
||
|
if (depends_on->state == IPA_RM_RELEASE_IN_PROGRESS &&
|
||
|
((struct ipa_rm_resource_prod *)
|
||
|
resource)->pending_release == 0) {
|
||
|
resource->state = IPA_RM_RELEASED;
|
||
|
state_changed = true;
|
||
|
evt = IPA_RM_RESOURCE_RELEASED;
|
||
|
}
|
||
|
spin_unlock_irqrestore(&depends_on->state_lock, consumer_flags);
|
||
|
break;
|
||
|
case IPA_RM_REQUEST_IN_PROGRESS:
|
||
|
release_consumer = true;
|
||
|
if (((struct ipa_rm_resource_prod *)
|
||
|
resource)->pending_request > 0)
|
||
|
((struct ipa_rm_resource_prod *)
|
||
|
resource)->pending_request--;
|
||
|
spin_lock_irqsave(&depends_on->state_lock, consumer_flags);
|
||
|
if (depends_on->state == IPA_RM_REQUEST_IN_PROGRESS &&
|
||
|
((struct ipa_rm_resource_prod *)
|
||
|
resource)->pending_request == 0) {
|
||
|
resource->state = IPA_RM_GRANTED;
|
||
|
state_changed = true;
|
||
|
evt = IPA_RM_RESOURCE_GRANTED;
|
||
|
}
|
||
|
spin_unlock_irqrestore(&depends_on->state_lock, consumer_flags);
|
||
|
break;
|
||
|
default:
|
||
|
result = -EINVAL;
|
||
|
spin_unlock_irqrestore(&resource->state_lock, flags);
|
||
|
goto bail;
|
||
|
}
|
||
|
if (state_changed &&
|
||
|
ipa_rm_peers_list_has_last_peer(resource->peers_list)) {
|
||
|
(void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
|
||
|
resource->name,
|
||
|
evt,
|
||
|
false);
|
||
|
result = -EINPROGRESS;
|
||
|
}
|
||
|
IPA_RM_DBG("%s new state: %d\n", ipa_rm_resource_str(resource->name),
|
||
|
resource->state);
|
||
|
spin_unlock_irqrestore(&resource->state_lock, flags);
|
||
|
ipa_rm_peers_list_remove_peer(resource->peers_list,
|
||
|
depends_on->name);
|
||
|
ipa_rm_peers_list_remove_peer(depends_on->peers_list,
|
||
|
resource->name);
|
||
|
if (release_consumer)
|
||
|
(void) ipa_rm_resource_consumer_release(
|
||
|
(struct ipa_rm_resource_cons *)depends_on);
|
||
|
bail:
|
||
|
IPA_RM_DBG("EXIT with %d\n", result);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ipa_rm_resource_producer_request() - producer resource request
|
||
|
* @producer: [in] producer
|
||
|
*
|
||
|
* Returns: 0 on success, negative on failure
|
||
|
*/
|
||
|
int ipa_rm_resource_producer_request(struct ipa_rm_resource_prod *producer)
|
||
|
{
|
||
|
int peers_index;
|
||
|
int result = 0;
|
||
|
unsigned long flags;
|
||
|
struct ipa_rm_resource *consumer;
|
||
|
int consumer_result;
|
||
|
enum ipa_rm_resource_state state;
|
||
|
|
||
|
if (ipa_rm_peers_list_is_empty(producer->resource.peers_list)) {
|
||
|
spin_lock_irqsave(&producer->resource.state_lock, flags);
|
||
|
state = producer->resource.state;
|
||
|
producer->resource.state = IPA_RM_GRANTED;
|
||
|
(void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
|
||
|
producer->resource.name,
|
||
|
IPA_RM_RESOURCE_GRANTED,
|
||
|
true);
|
||
|
result = 0;
|
||
|
goto unlock_and_bail;
|
||
|
}
|
||
|
spin_lock_irqsave(&producer->resource.state_lock, flags);
|
||
|
state = producer->resource.state;
|
||
|
switch (producer->resource.state) {
|
||
|
case IPA_RM_RELEASED:
|
||
|
case IPA_RM_RELEASE_IN_PROGRESS:
|
||
|
producer->resource.state = IPA_RM_REQUEST_IN_PROGRESS;
|
||
|
break;
|
||
|
case IPA_RM_GRANTED:
|
||
|
goto unlock_and_bail;
|
||
|
case IPA_RM_REQUEST_IN_PROGRESS:
|
||
|
result = -EINPROGRESS;
|
||
|
goto unlock_and_bail;
|
||
|
default:
|
||
|
result = -EINVAL;
|
||
|
goto unlock_and_bail;
|
||
|
}
|
||
|
producer->pending_request = 0;
|
||
|
spin_unlock_irqrestore(&producer->resource.state_lock, flags);
|
||
|
for (peers_index = 0;
|
||
|
peers_index < ipa_rm_peers_list_get_size(
|
||
|
producer->resource.peers_list);
|
||
|
peers_index++) {
|
||
|
consumer = ipa_rm_peers_list_get_resource(peers_index,
|
||
|
producer->resource.peers_list);
|
||
|
if (consumer) {
|
||
|
spin_lock_irqsave(
|
||
|
&producer->resource.state_lock, flags);
|
||
|
producer->pending_request++;
|
||
|
spin_unlock_irqrestore(
|
||
|
&producer->resource.state_lock, flags);
|
||
|
consumer_result = ipa_rm_resource_consumer_request(
|
||
|
(struct ipa_rm_resource_cons *)consumer);
|
||
|
if (consumer_result == -EINPROGRESS) {
|
||
|
result = -EINPROGRESS;
|
||
|
} else {
|
||
|
spin_lock_irqsave(
|
||
|
&producer->resource.state_lock, flags);
|
||
|
producer->pending_request--;
|
||
|
spin_unlock_irqrestore(
|
||
|
&producer->resource.state_lock, flags);
|
||
|
if (consumer_result != 0) {
|
||
|
result = consumer_result;
|
||
|
goto bail;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
spin_lock_irqsave(&producer->resource.state_lock, flags);
|
||
|
if (producer->pending_request == 0) {
|
||
|
producer->resource.state = IPA_RM_GRANTED;
|
||
|
(void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
|
||
|
producer->resource.name,
|
||
|
IPA_RM_RESOURCE_GRANTED,
|
||
|
true);
|
||
|
result = 0;
|
||
|
}
|
||
|
unlock_and_bail:
|
||
|
if (state != producer->resource.state)
|
||
|
IPA_RM_DBG("%s state changed %d->%d\n",
|
||
|
ipa_rm_resource_str(producer->resource.name),
|
||
|
state,
|
||
|
producer->resource.state);
|
||
|
spin_unlock_irqrestore(&producer->resource.state_lock, flags);
|
||
|
bail:
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ipa_rm_resource_producer_release() - producer resource release
|
||
|
* producer: [in] producer resource
|
||
|
*
|
||
|
* Returns: 0 on success, negative on failure
|
||
|
*
|
||
|
*/
|
||
|
int ipa_rm_resource_producer_release(struct ipa_rm_resource_prod *producer)
|
||
|
{
|
||
|
int peers_index;
|
||
|
int result = 0;
|
||
|
unsigned long flags;
|
||
|
struct ipa_rm_resource *consumer;
|
||
|
int consumer_result;
|
||
|
enum ipa_rm_resource_state state;
|
||
|
|
||
|
if (ipa_rm_peers_list_is_empty(producer->resource.peers_list)) {
|
||
|
spin_lock_irqsave(&producer->resource.state_lock, flags);
|
||
|
state = producer->resource.state;
|
||
|
producer->resource.state = IPA_RM_RELEASED;
|
||
|
(void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
|
||
|
producer->resource.name,
|
||
|
IPA_RM_RESOURCE_RELEASED,
|
||
|
true);
|
||
|
goto bail;
|
||
|
}
|
||
|
spin_lock_irqsave(&producer->resource.state_lock, flags);
|
||
|
state = producer->resource.state;
|
||
|
switch (producer->resource.state) {
|
||
|
case IPA_RM_RELEASED:
|
||
|
goto bail;
|
||
|
case IPA_RM_GRANTED:
|
||
|
case IPA_RM_REQUEST_IN_PROGRESS:
|
||
|
producer->resource.state = IPA_RM_RELEASE_IN_PROGRESS;
|
||
|
break;
|
||
|
case IPA_RM_RELEASE_IN_PROGRESS:
|
||
|
result = -EINPROGRESS;
|
||
|
goto bail;
|
||
|
default:
|
||
|
result = -EPERM;
|
||
|
goto bail;
|
||
|
}
|
||
|
producer->pending_release = 0;
|
||
|
spin_unlock_irqrestore(&producer->resource.state_lock, flags);
|
||
|
for (peers_index = 0;
|
||
|
peers_index < ipa_rm_peers_list_get_size(
|
||
|
producer->resource.peers_list);
|
||
|
peers_index++) {
|
||
|
consumer = ipa_rm_peers_list_get_resource(peers_index,
|
||
|
producer->resource.peers_list);
|
||
|
if (consumer) {
|
||
|
spin_lock_irqsave(
|
||
|
&producer->resource.state_lock, flags);
|
||
|
producer->pending_release++;
|
||
|
spin_unlock_irqrestore(
|
||
|
&producer->resource.state_lock, flags);
|
||
|
consumer_result = ipa_rm_resource_consumer_release(
|
||
|
(struct ipa_rm_resource_cons *)consumer);
|
||
|
spin_lock_irqsave(
|
||
|
&producer->resource.state_lock, flags);
|
||
|
producer->pending_release--;
|
||
|
spin_unlock_irqrestore(
|
||
|
&producer->resource.state_lock, flags);
|
||
|
}
|
||
|
}
|
||
|
spin_lock_irqsave(&producer->resource.state_lock, flags);
|
||
|
if (producer->pending_release == 0) {
|
||
|
producer->resource.state = IPA_RM_RELEASED;
|
||
|
(void) ipa_rm_wq_send_cmd(IPA_RM_WQ_NOTIFY_PROD,
|
||
|
producer->resource.name,
|
||
|
IPA_RM_RESOURCE_RELEASED,
|
||
|
true);
|
||
|
}
|
||
|
bail:
|
||
|
if (state != producer->resource.state)
|
||
|
IPA_RM_DBG("%s state changed %d->%d\n",
|
||
|
ipa_rm_resource_str(producer->resource.name),
|
||
|
state,
|
||
|
producer->resource.state);
|
||
|
spin_unlock_irqrestore(&producer->resource.state_lock, flags);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static void ipa_rm_resource_producer_handle_cb(
|
||
|
struct ipa_rm_resource_prod *producer,
|
||
|
enum ipa_rm_event event)
|
||
|
{
|
||
|
unsigned long flags;
|
||
|
spin_lock_irqsave(&producer->resource.state_lock, flags);
|
||
|
IPA_RM_DBG("%s state: %d event: %d pending_request: %d\n",
|
||
|
ipa_rm_resource_str(producer->resource.name),
|
||
|
producer->resource.state,
|
||
|
event,
|
||
|
producer->pending_request);
|
||
|
switch (producer->resource.state) {
|
||
|
case IPA_RM_REQUEST_IN_PROGRESS:
|
||
|
if (event != IPA_RM_RESOURCE_GRANTED)
|
||
|
goto unlock_and_bail;
|
||
|
if (producer->pending_request > 0) {
|
||
|
producer->pending_request--;
|
||
|
if (producer->pending_request == 0) {
|
||
|
producer->resource.state =
|
||
|
IPA_RM_GRANTED;
|
||
|
spin_unlock_irqrestore(
|
||
|
&producer->resource.state_lock, flags);
|
||
|
ipa_rm_resource_producer_notify_clients(
|
||
|
producer,
|
||
|
IPA_RM_RESOURCE_GRANTED,
|
||
|
false);
|
||
|
goto bail;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case IPA_RM_RELEASE_IN_PROGRESS:
|
||
|
if (event != IPA_RM_RESOURCE_RELEASED)
|
||
|
goto unlock_and_bail;
|
||
|
if (producer->pending_release > 0) {
|
||
|
producer->pending_release--;
|
||
|
if (producer->pending_release == 0) {
|
||
|
producer->resource.state =
|
||
|
IPA_RM_RELEASED;
|
||
|
spin_unlock_irqrestore(
|
||
|
&producer->resource.state_lock, flags);
|
||
|
ipa_rm_resource_producer_notify_clients(
|
||
|
producer,
|
||
|
IPA_RM_RESOURCE_RELEASED,
|
||
|
false);
|
||
|
goto bail;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case IPA_RM_GRANTED:
|
||
|
case IPA_RM_RELEASED:
|
||
|
default:
|
||
|
goto unlock_and_bail;
|
||
|
}
|
||
|
unlock_and_bail:
|
||
|
IPA_RM_DBG("%s new state: %d\n",
|
||
|
ipa_rm_resource_str(producer->resource.name),
|
||
|
producer->resource.state);
|
||
|
spin_unlock_irqrestore(&producer->resource.state_lock, flags);
|
||
|
bail:
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* ipa_rm_resource_consumer_handle_cb() - propagates resource
|
||
|
* notification to all dependent producers
|
||
|
* @consumer: [in] notifying resource
|
||
|
*
|
||
|
*/
|
||
|
void ipa_rm_resource_consumer_handle_cb(struct ipa_rm_resource_cons *consumer,
|
||
|
enum ipa_rm_event event)
|
||
|
{
|
||
|
int peers_index;
|
||
|
struct ipa_rm_resource *producer;
|
||
|
unsigned long flags;
|
||
|
if (!consumer) {
|
||
|
IPA_RM_ERR("invalid params\n");
|
||
|
return;
|
||
|
}
|
||
|
spin_lock_irqsave(&consumer->resource.state_lock, flags);
|
||
|
IPA_RM_DBG("%s state: %d event: %d\n",
|
||
|
ipa_rm_resource_str(consumer->resource.name),
|
||
|
consumer->resource.state,
|
||
|
event);
|
||
|
switch (consumer->resource.state) {
|
||
|
case IPA_RM_REQUEST_IN_PROGRESS:
|
||
|
if (event == IPA_RM_RESOURCE_RELEASED)
|
||
|
goto bail;
|
||
|
consumer->resource.state = IPA_RM_GRANTED;
|
||
|
break;
|
||
|
case IPA_RM_RELEASE_IN_PROGRESS:
|
||
|
if (event == IPA_RM_RESOURCE_GRANTED)
|
||
|
goto bail;
|
||
|
consumer->resource.state = IPA_RM_RELEASED;
|
||
|
break;
|
||
|
case IPA_RM_GRANTED:
|
||
|
case IPA_RM_RELEASED:
|
||
|
default:
|
||
|
goto bail;
|
||
|
}
|
||
|
spin_unlock_irqrestore(&consumer->resource.state_lock, flags);
|
||
|
for (peers_index = 0;
|
||
|
peers_index < ipa_rm_peers_list_get_size(
|
||
|
consumer->resource.peers_list);
|
||
|
peers_index++) {
|
||
|
producer = ipa_rm_peers_list_get_resource(peers_index,
|
||
|
consumer->resource.peers_list);
|
||
|
if (producer)
|
||
|
ipa_rm_resource_producer_handle_cb(
|
||
|
(struct ipa_rm_resource_prod *)
|
||
|
producer,
|
||
|
event);
|
||
|
}
|
||
|
return;
|
||
|
bail:
|
||
|
IPA_RM_DBG("%s new state: %d\n",
|
||
|
ipa_rm_resource_str(consumer->resource.name),
|
||
|
consumer->resource.state);
|
||
|
spin_unlock_irqrestore(&consumer->resource.state_lock, flags);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* ipa_rm_resource_producer_print_stat() - print the
|
||
|
* resource status and all his dependencies
|
||
|
*
|
||
|
* @resource: [in] Resource resource
|
||
|
* @buff: [in] The buf used to print
|
||
|
* @size: [in] Buf size
|
||
|
*
|
||
|
* Returns: number of bytes used on success, negative on failure
|
||
|
*/
|
||
|
|
||
|
int ipa_rm_resource_producer_print_stat(
|
||
|
struct ipa_rm_resource *resource,
|
||
|
char *buf,
|
||
|
int size){
|
||
|
|
||
|
int i, nbytes, cnt = 0;
|
||
|
unsigned long flags;
|
||
|
struct ipa_rm_resource *consumer;
|
||
|
|
||
|
if (!buf || size < 0)
|
||
|
return -EINVAL;
|
||
|
nbytes = scnprintf(buf + cnt, size - cnt,
|
||
|
ipa_rm_resource_str(resource->name));
|
||
|
cnt += nbytes;
|
||
|
nbytes = scnprintf(buf + cnt, size - cnt,
|
||
|
"[");
|
||
|
cnt += nbytes;
|
||
|
|
||
|
|
||
|
spin_lock_irqsave(&resource->state_lock, flags);
|
||
|
switch (resource->state) {
|
||
|
case IPA_RM_RELEASED:
|
||
|
nbytes = scnprintf(buf + cnt, size - cnt,
|
||
|
"Released] -> ");
|
||
|
cnt += nbytes;
|
||
|
break;
|
||
|
case IPA_RM_REQUEST_IN_PROGRESS:
|
||
|
nbytes = scnprintf(buf + cnt, size - cnt,
|
||
|
"Request In Progress] -> ");
|
||
|
cnt += nbytes;
|
||
|
break;
|
||
|
case IPA_RM_GRANTED:
|
||
|
nbytes = scnprintf(buf + cnt, size - cnt,
|
||
|
"Granted] -> ");
|
||
|
cnt += nbytes;
|
||
|
break;
|
||
|
case IPA_RM_RELEASE_IN_PROGRESS:
|
||
|
nbytes = scnprintf(buf + cnt, size - cnt,
|
||
|
"Release In Progress] -> ");
|
||
|
cnt += nbytes;
|
||
|
break;
|
||
|
default:
|
||
|
spin_unlock_irqrestore(
|
||
|
&resource->state_lock,
|
||
|
flags);
|
||
|
return -EPERM;
|
||
|
}
|
||
|
spin_unlock_irqrestore(
|
||
|
&resource->state_lock,
|
||
|
flags);
|
||
|
for (i = 0; i < resource->peers_list->max_peers; ++i) {
|
||
|
consumer =
|
||
|
ipa_rm_peers_list_get_resource(
|
||
|
i,
|
||
|
resource->peers_list);
|
||
|
if (consumer) {
|
||
|
nbytes = scnprintf(buf + cnt, size - cnt,
|
||
|
ipa_rm_resource_str(consumer->name));
|
||
|
cnt += nbytes;
|
||
|
nbytes = scnprintf(buf + cnt, size - cnt,
|
||
|
"[");
|
||
|
cnt += nbytes;
|
||
|
|
||
|
spin_lock_irqsave(&consumer->state_lock, flags);
|
||
|
switch (consumer->state) {
|
||
|
case IPA_RM_RELEASED:
|
||
|
nbytes = scnprintf(buf + cnt, size - cnt,
|
||
|
"Released], ");
|
||
|
cnt += nbytes;
|
||
|
break;
|
||
|
case IPA_RM_REQUEST_IN_PROGRESS:
|
||
|
nbytes = scnprintf(buf + cnt, size - cnt,
|
||
|
"Request In Progress], ");
|
||
|
cnt += nbytes;
|
||
|
break;
|
||
|
case IPA_RM_GRANTED:
|
||
|
nbytes = scnprintf(buf + cnt, size - cnt,
|
||
|
"Granted], ");
|
||
|
cnt += nbytes;
|
||
|
break;
|
||
|
case IPA_RM_RELEASE_IN_PROGRESS:
|
||
|
nbytes = scnprintf(buf + cnt, size - cnt,
|
||
|
"Release In Progress], ");
|
||
|
cnt += nbytes;
|
||
|
break;
|
||
|
default:
|
||
|
spin_unlock_irqrestore(
|
||
|
&consumer->state_lock,
|
||
|
flags);
|
||
|
return -EPERM;
|
||
|
}
|
||
|
spin_unlock_irqrestore(
|
||
|
&consumer->state_lock,
|
||
|
flags);
|
||
|
}
|
||
|
}
|
||
|
nbytes = scnprintf(buf + cnt, size - cnt,
|
||
|
"\n");
|
||
|
cnt += nbytes;
|
||
|
return cnt;
|
||
|
}
|