| /**************************************************************************** |
| * |
| * 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); |
| } |