blob: 388c02d5d07214f314ce70b56047e2eb1354f36f [file] [log] [blame]
/****************************************************************************
*
* Copyright (c) 2012 - 2016 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
#include "dev.h"
#include "debug.h"
#include "udi.h"
#include "log_clients.h"
#include "unifiio.h"
/* These functions should NOT be called from interrupt context */
/* It is supposed to be called from process context, or
* NET_TX_SOFTIRQ - with BHs disabled and interrupts disabled
*/
/* Do not sleep */
void slsi_log_clients_log_signal_safe(struct slsi_dev *sdev, struct sk_buff *skb, u32 direction)
{
struct list_head *pos, *n;
struct slsi_log_client *log_client;
int dir = (direction == SLSI_LOG_DIRECTION_FROM_HOST) ? UDI_FROM_HOST : UDI_TO_HOST;
spin_lock_bh(&sdev->log_clients.log_client_spinlock);
list_for_each_safe(pos, n, &sdev->log_clients.log_client_list) {
log_client = list_entry(pos, struct slsi_log_client, q);
log_client->log_client_cb(log_client, skb, dir);
}
spin_unlock_bh(&sdev->log_clients.log_client_spinlock);
}
void slsi_log_clients_init(struct slsi_dev *sdev)
{
INIT_LIST_HEAD(&sdev->log_clients.log_client_list);
spin_lock_init(&sdev->log_clients.log_client_spinlock);
}
/* The arg called "filter" will eventually be passed to kfree().
* - so pass a NULL if you are not doing any filtering
*/
int slsi_log_client_register(struct slsi_dev *sdev, void *log_client_ctx,
int (*log_client_cb)(struct slsi_log_client *, struct sk_buff *, int),
char *filter, int min_signal_id, int max_signal_id)
{
struct slsi_log_client *log_client;
int first_in_list = 0;
first_in_list = list_empty(&sdev->log_clients.log_client_list);
log_client = kmalloc(sizeof(*log_client), GFP_KERNEL);
if (log_client == NULL)
return -ENOMEM;
log_client->min_signal_id = min_signal_id;
log_client->max_signal_id = max_signal_id;
log_client->signal_filter = filter;
log_client->log_client_ctx = log_client_ctx;
log_client->log_client_cb = log_client_cb;
/* Add to tail of log queue */
spin_lock_bh(&sdev->log_clients.log_client_spinlock);
list_add_tail(&log_client->q, &sdev->log_clients.log_client_list);
spin_unlock_bh(&sdev->log_clients.log_client_spinlock);
return 0;
}
void slsi_log_clients_terminate(struct slsi_dev *sdev)
{
/* If the driver is configured to try and terminate UDI user space
* applications, the following will try to do so.
*/
if (*sdev->term_udi_users) {
int num_polls_left = 50;
unsigned int timeout_ms = 4;
slsi_log_client_msg(sdev, UDI_DRV_UNLOAD_IND, 0, NULL);
/* Poll until all refs have gone away or timeout */
while (slsi_check_cdev_refs() && num_polls_left) {
msleep(timeout_ms);
num_polls_left--;
}
}
}
void slsi_log_client_msg(struct slsi_dev *sdev, u16 event, u32 event_data_length, const u8 *event_data)
{
struct list_head *pos, *n;
struct slsi_log_client *log_client;
spin_lock_bh(&sdev->log_clients.log_client_spinlock);
list_for_each_safe(pos, n, &sdev->log_clients.log_client_list) {
log_client = list_entry(pos, struct slsi_log_client, q);
spin_unlock_bh(&sdev->log_clients.log_client_spinlock);
if (slsi_kernel_to_user_space_event(log_client, event, event_data_length, event_data))
SLSI_WARN(sdev, "Failed to send event(0x%.4X) to UDI client 0x%p\n", event, log_client);
spin_lock_bh(&sdev->log_clients.log_client_spinlock);
}
spin_unlock_bh(&sdev->log_clients.log_client_spinlock);
}
void slsi_log_client_unregister(struct slsi_dev *sdev, void *log_client_ctx)
{
struct list_head *pos, *n;
struct slsi_log_client *log_client;
spin_lock_bh(&sdev->log_clients.log_client_spinlock);
list_for_each_safe(pos, n, &sdev->log_clients.log_client_list) {
log_client = list_entry(pos, struct slsi_log_client, q);
if (log_client->log_client_ctx == log_client_ctx) {
kfree(log_client->signal_filter);
list_del(pos);
kfree(log_client);
}
}
spin_unlock_bh(&sdev->log_clients.log_client_spinlock);
}