blob: d034277f2e9f975a9988352863eee492f58b0528 [file] [log] [blame]
/* Copyright (c) 2018, 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/completion.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/spinlock_types.h>
#include <linux/wait.h>
#include "../hif_sdio/hif.h"
#include "htca.h"
#include "htca_mbox_internal.h"
/* Host Target Communications Event Management */
/* Protect all event tables -- global as well as per-endpoint. */
static spinlock_t event_lock; /* protects all event tables */
/* Mapping table for global events -- avail/unavail */
static struct htca_event_table_element
global_event_table[HTCA_EVENT_GLOBAL_COUNT];
struct htca_event_table_element *
htca_event_id_to_event(struct htca_target *target,
u8 end_point_id,
u8 event_id)
{
struct htca_event_table_element *ev = NULL;
/* is ep event */
if ((event_id >= HTCA_EVENT_EP_START) &&
(event_id <= HTCA_EVENT_EP_END)) {
struct htca_endpoint *end_point;
int ep_evid;
ep_evid = event_id - HTCA_EVENT_EP_START;
end_point = &target->end_point[end_point_id];
ev = &end_point->endpoint_event_tbl[ep_evid];
/* is global event */
} else if ((event_id >= HTCA_EVENT_GLOBAL_START) &&
(event_id <= HTCA_EVENT_GLOBAL_END)) {
int global_evid;
global_evid = event_id - HTCA_EVENT_GLOBAL_START;
ev = &global_event_table[global_evid];
} else {
WARN_ON(1); /* unknown event */
}
return ev;
}
void htca_dispatch_event(struct htca_target *target,
u8 end_point_id,
u8 event_id,
struct htca_event_info *event_info)
{
struct htca_event_table_element *ev;
ev = htca_event_id_to_event(target, end_point_id, event_id);
if (!ev) {
panic("BUG");
return;
}
if (ev->handler) {
htca_event_handler handler;
void *param;
unsigned long flags;
spin_lock_irqsave(&event_lock, flags);
handler = ev->handler;
param = ev->param;
spin_unlock_irqrestore(&event_lock, flags);
handler((void *)target, end_point_id, event_id,
event_info, param);
}
}
int htca_add_to_event_table(struct htca_target *target,
u8 end_point_id,
u8 event_id,
htca_event_handler handler, void *param) {
struct htca_event_table_element *ev;
unsigned long flags;
ev = htca_event_id_to_event(target, end_point_id, event_id);
if (!ev)
return HTCA_ERROR;
spin_lock_irqsave(&event_lock, flags);
ev->handler = handler;
ev->param = param;
spin_unlock_irqrestore(&event_lock, flags);
return HTCA_OK;
}
int htca_remove_from_event_table(struct htca_target *target,
u8 end_point_id,
u8 event_id) {
struct htca_event_table_element *ev;
unsigned long flags;
ev = htca_event_id_to_event(target, end_point_id, event_id);
if (!ev)
return HTCA_ERROR;
spin_lock_irqsave(&event_lock, flags);
/* Clear event handler info */
memset(ev, 0, sizeof(*ev));
spin_unlock_irqrestore(&event_lock, flags);
return HTCA_OK;
}
/* Called once during module initialization */
void htca_event_table_init(void)
{
spin_lock_init(&event_lock);
}