| /* |
| * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved. |
| * |
| * Previously licensed under the ISC license by Qualcomm Atheros, Inc. |
| * |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /* |
| * This file was originally distributed by Qualcomm Atheros, Inc. |
| * under proprietary terms before Copyright ownership was assigned |
| * to the Linux Foundation. |
| */ |
| |
| /****************************************************************************** |
| * wlan_nlink_srv.c |
| * |
| * This file contains the definitions specific to the wlan_nlink_srv |
| * |
| ******************************************************************************/ |
| |
| #include <linux/version.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/netdevice.h> |
| #include <linux/netlink.h> |
| #include <linux/skbuff.h> |
| #include <net/sock.h> |
| #include <wlan_nlink_srv.h> |
| #include <vos_trace.h> |
| |
| #ifdef CNSS_GENL |
| #include <vos_memory.h> |
| #include <wlan_nlink_common.h> |
| #include <net/genetlink.h> |
| #include <net/cnss_nl.h> |
| #endif |
| |
| #if defined(CONFIG_CNSS_LOGGER) |
| |
| #include <net/cnss_logger.h> |
| |
| static int radio_idx = -EINVAL; |
| static void *wiphy_ptr; |
| static bool logger_initialized; |
| |
| /** |
| * nl_srv_init() - wrapper function to register to cnss_logger |
| * @wiphy: the pointer to the wiphy structure |
| * |
| * The netlink socket is no longer initialized in the driver itself, instead |
| * will be initialized in the cnss_logger module, the driver should register |
| * itself to cnss_logger module to get the radio_index for all the netlink |
| * operation. (cfg80211 vendor command is using different netlink socket). |
| * |
| * The cnss_logger_device_register() use to register the driver with the |
| * wiphy structure and the module name (debug purpose) and then return the |
| * radio_index depending on the availibility. |
| * |
| * Return: radio index for success and -EINVAL for failure |
| */ |
| int nl_srv_init(void *wiphy) |
| { |
| if (logger_initialized) |
| goto initialized; |
| |
| wiphy_ptr = wiphy; |
| radio_idx = cnss_logger_device_register(wiphy, THIS_MODULE->name); |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "%s: radio_index: %d", __func__, radio_idx); |
| |
| if (radio_idx >= 0) |
| logger_initialized = true; |
| |
| initialized: |
| return radio_idx; |
| } |
| |
| /** |
| * nl_srv_exit() - wrapper function to unregister from cnss_logger |
| * |
| * The cnss_logger_device_unregister() use to unregister the driver with |
| * the radio_index assigned and wiphy structure from cnss_logger. |
| * |
| * Return: None |
| */ |
| void nl_srv_exit(void) |
| { |
| if (logger_initialized) { |
| cnss_logger_device_unregister(radio_idx, wiphy_ptr); |
| radio_idx = -EINVAL; |
| wiphy_ptr = NULL; |
| logger_initialized = false; |
| } |
| } |
| |
| /** |
| * nl_srv_ucast() - wrapper function to do unicast tx through cnss_logger |
| * @skb: the socket buffer to send |
| * @dst_pid: the port id |
| * @flag: the blocking or nonblocking flag |
| * |
| * The nl_srv_is_initialized() is used to do sanity check if the netlink |
| * service is ready, e.g if the radio_index is assigned properly, if not |
| * the driver should take the responsibility to free the skb. |
| * |
| * The cnss_logger_nl_ucast() use the same parameters to send the socket |
| * buffers. |
| * |
| * Return: the error of the transmission status |
| */ |
| int nl_srv_ucast(struct sk_buff *skb, int dst_pid, int flag) |
| { |
| int err = -EINVAL; |
| |
| /* sender's pid */ |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)) |
| NETLINK_CB(skb).pid = 0; |
| #else |
| NETLINK_CB(skb).portid = 0; |
| #endif |
| /* not multicast */ |
| NETLINK_CB(skb).dst_group = 0; |
| |
| if (nl_srv_is_initialized() == 0) |
| err = cnss_logger_nl_ucast(skb, dst_pid, flag); |
| else |
| dev_kfree_skb(skb); |
| return err; |
| } |
| |
| /** |
| * nl_srv_bcast() - wrapper function to do broadcast tx through cnss_logger |
| * @skb: the socket buffer to send |
| * |
| * The cnss_logger_nl_bcast() is used to transmit the socket buffer. |
| * |
| * Return: status of transmission |
| */ |
| int nl_srv_bcast(struct sk_buff *skb) |
| { |
| int err = -EINVAL; |
| int flags = GFP_KERNEL; |
| |
| if (in_interrupt() || irqs_disabled() || in_atomic()) |
| flags = GFP_ATOMIC; |
| |
| /* sender's pid */ |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)) |
| NETLINK_CB(skb).pid = 0; |
| #else |
| NETLINK_CB(skb).portid = 0; |
| #endif |
| /* destination group */ |
| NETLINK_CB(skb).dst_group = WLAN_NLINK_MCAST_GRP_ID; |
| |
| if (nl_srv_is_initialized() == 0) |
| err = cnss_logger_nl_bcast(skb, WLAN_NLINK_MCAST_GRP_ID, flags); |
| else |
| dev_kfree_skb(skb); |
| return err; |
| } |
| |
| /** |
| * nl_srv_unregister() - wrapper function to unregister event to cnss_logger |
| * @msg_type: the message to unregister |
| * @msg_handler: the message handler |
| * |
| * The cnss_logger_event_unregister() is used to unregister the message and |
| * message handler. |
| * |
| * Return: 0 if successfully unregister, otherwise proper error code |
| */ |
| int nl_srv_unregister(tWlanNlModTypes msg_type, nl_srv_msg_callback msg_handler) |
| { |
| int ret = -EINVAL; |
| |
| if (nl_srv_is_initialized() != 0) |
| return ret; |
| |
| if ((msg_type >= WLAN_NL_MSG_BASE) && (msg_type < WLAN_NL_MSG_MAX) && |
| msg_handler != NULL) { |
| ret = cnss_logger_event_unregister(radio_idx, msg_type, |
| msg_handler); |
| } else { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "NLINK: nl_srv_unregister failed for msg_type %d", |
| msg_type); |
| ret = -EINVAL; |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * nl_srv_register() - wrapper function to register event to cnss_logger |
| * @msg_type: the message to register |
| * @msg_handler: the message handler |
| * |
| * The cnss_logger_event_register() is used to register the message and |
| * message handler. |
| * |
| * Return: 0 if successfully register, otherwise proper error code |
| */ |
| int nl_srv_register(tWlanNlModTypes msg_type, nl_srv_msg_callback msg_handler) |
| { |
| int ret = -EINVAL; |
| |
| if (nl_srv_is_initialized() != 0) |
| return ret; |
| |
| if ((msg_type >= WLAN_NL_MSG_BASE) && (msg_type < WLAN_NL_MSG_MAX) && |
| msg_handler != NULL) { |
| ret = cnss_logger_event_register(radio_idx, msg_type, |
| msg_handler); |
| } else { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "NLINK: nl_srv_register failed for msg_type %d", |
| msg_type); |
| ret = -EINVAL; |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * nl_srv_is_initialized() - check if netlink service is initialized |
| * |
| * Return: 0 if it is initialized, otherwise error code |
| */ |
| inline int nl_srv_is_initialized(void) |
| { |
| if (logger_initialized) |
| return 0; |
| else |
| return -EPERM; |
| } |
| |
| #elif !defined(MULTI_IF_NAME) |
| /* Global variables */ |
| static DEFINE_MUTEX(nl_srv_sem); |
| static struct sock *nl_srv_sock; |
| static nl_srv_msg_callback nl_srv_msg_handler[NLINK_MAX_CALLBACKS]; |
| |
| /* Forward declaration */ |
| static void nl_srv_rcv (struct sk_buff *sk); |
| static void nl_srv_rcv_skb (struct sk_buff *skb); |
| static void nl_srv_rcv_msg (struct sk_buff *skb, struct nlmsghdr *nlh); |
| |
| /* |
| * Initialize the netlink service. |
| * Netlink service is usable after this. |
| */ |
| int nl_srv_init(void *wiphy) |
| { |
| int retcode = 0; |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) |
| struct netlink_kernel_cfg cfg = { |
| .groups = WLAN_NLINK_MCAST_GRP_ID, |
| .input = nl_srv_rcv |
| }; |
| #endif |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) |
| nl_srv_sock = netlink_kernel_create(&init_net, WLAN_NLINK_PROTO_FAMILY, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)) |
| THIS_MODULE, |
| #endif |
| &cfg); |
| #else |
| nl_srv_sock = netlink_kernel_create(&init_net, WLAN_NLINK_PROTO_FAMILY, |
| WLAN_NLINK_MCAST_GRP_ID, nl_srv_rcv, NULL, THIS_MODULE); |
| #endif |
| |
| if (nl_srv_sock != NULL) { |
| memset(nl_srv_msg_handler, 0, sizeof(nl_srv_msg_handler)); |
| } else { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "NLINK: netlink_kernel_create failed"); |
| retcode = -ECONNREFUSED; |
| } |
| return retcode; |
| } |
| |
| /* |
| * Deinit the netlink service. |
| * Netlink service is unusable after this. |
| */ |
| void nl_srv_exit(void) |
| { |
| netlink_kernel_release(nl_srv_sock); |
| nl_srv_sock = NULL; |
| } |
| |
| /* |
| * Register a message handler for a specified module. |
| * Each module (e.g. WLAN_NL_MSG_BTC )will register a |
| * handler to handle messages addressed to it. |
| */ |
| int nl_srv_register(tWlanNlModTypes msg_type, nl_srv_msg_callback msg_handler) |
| { |
| int retcode = 0; |
| |
| if ((msg_type >= WLAN_NL_MSG_BASE) && (msg_type < WLAN_NL_MSG_MAX) && |
| msg_handler != NULL) |
| { |
| nl_srv_msg_handler[msg_type - WLAN_NL_MSG_BASE] = msg_handler; |
| } |
| else { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, |
| "NLINK: nl_srv_register failed for msg_type %d", msg_type); |
| retcode = -EINVAL; |
| } |
| |
| return retcode; |
| } |
| /* |
| * Unregister the message handler for a specified module. |
| */ |
| int nl_srv_unregister(tWlanNlModTypes msg_type, nl_srv_msg_callback msg_handler) |
| { |
| int retcode = 0; |
| |
| if ((msg_type >= WLAN_NL_MSG_BASE) && (msg_type < WLAN_NL_MSG_MAX) && |
| (nl_srv_msg_handler[msg_type - WLAN_NL_MSG_BASE] == msg_handler)) |
| { |
| nl_srv_msg_handler[msg_type - WLAN_NL_MSG_BASE] = NULL; |
| } |
| else |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, |
| "NLINK: nl_srv_unregister failed for msg_type %d", msg_type); |
| retcode = -EINVAL; |
| } |
| |
| return retcode; |
| } |
| |
| #ifdef CNSS_GENL |
| |
| /** |
| * nl80211hdr_put() - API to fill genlmsg header |
| * @skb: Sk buffer |
| * @portid: Port ID |
| * @seq: Sequence number |
| * @flags: Flags |
| * @cmd: Command id |
| * |
| * API to fill genl message header for brodcast events to user space |
| * |
| * Return: Pointer to user specific header/payload |
| */ |
| static inline void *nl80211hdr_put(struct sk_buff *skb, uint32_t portid, |
| uint32_t seq, int flags, uint8_t cmd) |
| { |
| struct genl_family *cld80211_fam = cld80211_get_genl_family(); |
| |
| return genlmsg_put(skb, portid, seq, cld80211_fam, flags, cmd); |
| } |
| |
| /** |
| * cld80211_fill_data() - API to fill payload to nl message |
| * @msg: Sk buffer |
| * @portid: Port ID |
| * @seq: Sequence number |
| * @flags: Flags |
| * @cmd: Command ID |
| * @buf: data buffer/payload to be filled |
| * @len: length of the payload ie. @buf |
| * |
| * API to fill the payload/data of the nl message to be sent |
| * |
| * Return: zero on success |
| */ |
| static int cld80211_fill_data(struct sk_buff *msg, uint32_t portid, |
| uint32_t seq, int flags, uint8_t cmd, |
| uint8_t *buf, int len) |
| { |
| void *hdr; |
| struct nlattr *nest; |
| |
| hdr = nl80211hdr_put(msg, portid, seq, flags, cmd); |
| if (!hdr) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "nl80211 hdr put failed"); |
| return -EPERM; |
| } |
| |
| nest = nla_nest_start(msg, CLD80211_ATTR_VENDOR_DATA); |
| if (!nest) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "nla_nest_start failed"); |
| goto nla_put_failure; |
| } |
| |
| if (nla_put(msg, CLD80211_ATTR_DATA, len, buf)) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "nla_put failed"); |
| goto nla_put_failure; |
| } |
| |
| nla_nest_end(msg, nest); |
| genlmsg_end(msg, hdr); |
| |
| return 0; |
| nla_put_failure: |
| genlmsg_cancel(msg, hdr); |
| return -EPERM; |
| } |
| |
| /** |
| * send_msg_to_cld80211() - API to send message to user space Application |
| * @mcgroup_id: Multicast group ID |
| * @pid: Port ID |
| * @app_id: Application ID |
| * @buf: Data/payload buffer to be sent |
| * @len: Length of the data ie. @buf |
| * |
| * API to send the nl message to user space application. |
| * |
| * Return: zero on success |
| */ |
| static int send_msg_to_cld80211(int mcgroup_id, int pid, int app_id, |
| uint8_t *buf, int len) |
| { |
| struct sk_buff *msg; |
| struct genl_family *cld80211_fam = cld80211_get_genl_family(); |
| int status; |
| int flags = GFP_KERNEL; |
| |
| if (in_interrupt() || irqs_disabled() || in_atomic()) |
| flags = GFP_ATOMIC; |
| |
| msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags); |
| if (!msg) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "nlmsg malloc fails"); |
| return -EPERM; |
| } |
| |
| status = cld80211_fill_data(msg, pid, 0, 0, app_id, buf, len); |
| if (status) { |
| nlmsg_free(msg); |
| return -EPERM; |
| } |
| |
| genlmsg_multicast_netns(cld80211_fam, &init_net, msg, 0, |
| mcgroup_id, flags); |
| return 0; |
| } |
| |
| /** |
| * nl_srv_bcast() - wrapper function to do broadcast events to user space apps |
| * @skb: the socket buffer to send |
| * @mcgroup_id: multicast group id |
| * @app_id: application id |
| * |
| * This function is common wrapper to send broadcast events to different |
| * user space applications. |
| * |
| * return: none |
| */ |
| int nl_srv_bcast(struct sk_buff *skb, int mcgroup_id, int app_id) |
| { |
| struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; |
| void *msg = NLMSG_DATA(nlh); |
| uint32_t msg_len = nlmsg_len(nlh); |
| uint8_t *tempbuf; |
| int status; |
| |
| tempbuf = (uint8_t *)vos_mem_malloc(msg_len); |
| vos_mem_copy(tempbuf, msg, msg_len); |
| status = send_msg_to_cld80211(mcgroup_id, 0, app_id, tempbuf, msg_len); |
| if (status) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "send msg to cld80211 fails for app id %d", app_id); |
| return -EPERM; |
| } |
| |
| dev_kfree_skb(skb); |
| vos_mem_free(tempbuf); |
| return 0; |
| } |
| |
| /** |
| * nl_srv_ucast() - wrapper function to do unicast events to user space apps |
| * @skb: the socket buffer to send |
| * @dst_pid: destination process IF |
| * @flag: flags |
| * @app_id: application id |
| * @mcgroup_id: Multicast group ID |
| * |
| * This function is common wrapper to send unicast events to different |
| * user space applications. This internally used broadcast API with multicast |
| * group mcgrp_id. This wrapper serves as a common API in both |
| * new generic netlink infra and legacy implementation. |
| * |
| * return: zero on success, error code otherwise |
| */ |
| int nl_srv_ucast(struct sk_buff *skb, int dst_pid, int flag, |
| int app_id, int mcgroup_id) |
| { |
| struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data; |
| void *msg = NLMSG_DATA(nlh); |
| uint32_t msg_len = nlmsg_len(nlh); |
| uint8_t *tempbuf; |
| int status; |
| |
| tempbuf = (uint8_t *)vos_mem_malloc(msg_len); |
| vos_mem_copy(tempbuf, msg, msg_len); |
| status = send_msg_to_cld80211(mcgroup_id, dst_pid, app_id, |
| tempbuf, msg_len); |
| if (status) { |
| VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR, |
| "send msg to cld80211 fails for app id %d", app_id); |
| return -EPERM; |
| } |
| |
| dev_kfree_skb(skb); |
| vos_mem_free(tempbuf); |
| return 0; |
| } |
| |
| #else |
| /* |
| * Unicast the message to the process in user space identfied |
| * by the dst-pid |
| */ |
| int nl_srv_ucast(struct sk_buff *skb, int dst_pid, int flag) |
| { |
| int err = 0; |
| |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)) |
| NETLINK_CB(skb).pid = 0; //sender's pid |
| #else |
| NETLINK_CB(skb).portid = 0; //sender's pid |
| #endif |
| NETLINK_CB(skb).dst_group = 0; //not multicast |
| |
| if (nl_srv_sock != NULL) { |
| err = netlink_unicast(nl_srv_sock, skb, dst_pid, flag); |
| } else { |
| dev_kfree_skb(skb); |
| } |
| |
| if (err < 0) |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, |
| "NLINK: netlink_unicast to pid[%d] failed, ret[%d]", dst_pid, err); |
| |
| return err; |
| } |
| |
| int nl_srv_bcast(struct sk_buff *skb) |
| { |
| int err = 0; |
| int flags = GFP_KERNEL; |
| |
| if (in_interrupt() || irqs_disabled() || in_atomic()) |
| flags = GFP_ATOMIC; |
| |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)) |
| NETLINK_CB(skb).pid = 0; //sender's pid |
| #else |
| NETLINK_CB(skb).portid = 0; //sender's pid |
| #endif |
| NETLINK_CB(skb).dst_group = WLAN_NLINK_MCAST_GRP_ID; //destination group |
| |
| if (nl_srv_sock != NULL) |
| err = netlink_broadcast(nl_srv_sock, skb, 0, WLAN_NLINK_MCAST_GRP_ID, flags); |
| else |
| dev_kfree_skb(skb); |
| |
| if ((err < 0) && (err != -ESRCH)) |
| { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, |
| "NLINK: netlink_broadcast failed err = %d", err); |
| } |
| return err; |
| } |
| |
| #endif |
| |
| /* |
| * Processes the Netlink socket input queue. |
| * Dequeue skb's from the socket input queue and process |
| * all the netlink messages in that skb, before moving |
| * to the next skb. |
| */ |
| static void nl_srv_rcv(struct sk_buff *sk) |
| { |
| mutex_lock(&nl_srv_sem); |
| nl_srv_rcv_skb(sk); |
| mutex_unlock(&nl_srv_sem); |
| } |
| |
| /* |
| * Each skb could contain multiple Netlink messages. Process all the |
| * messages in one skb and discard malformed skb's silently. |
| */ |
| static void nl_srv_rcv_skb (struct sk_buff *skb) |
| { |
| struct nlmsghdr * nlh; |
| |
| while (skb->len >= NLMSG_SPACE(0)) { |
| u32 rlen; |
| |
| nlh = (struct nlmsghdr *)skb->data; |
| |
| if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, "NLINK: Invalid " |
| "Netlink message: skb[%pK], len[%d], nlhdr[%pK], nlmsg_len[%d]", |
| skb, skb->len, nlh, nlh->nlmsg_len); |
| return; |
| } |
| |
| rlen = NLMSG_ALIGN(nlh->nlmsg_len); |
| if (rlen > skb->len) |
| rlen = skb->len; |
| nl_srv_rcv_msg(skb, nlh); |
| skb_pull(skb, rlen); |
| } |
| } |
| |
| /* |
| * Process a netlink message. |
| * Each netlink message will have a message of type tAniMsgHdr inside. |
| */ |
| static void nl_srv_rcv_msg (struct sk_buff *skb, struct nlmsghdr *nlh) |
| { |
| int type; |
| |
| /* Only requests are handled by kernel now */ |
| if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, |
| "NLINK: Received Invalid NL Req type [%x]", nlh->nlmsg_flags); |
| return; |
| } |
| |
| type = nlh->nlmsg_type; |
| |
| /* Unknown message */ |
| if (type < WLAN_NL_MSG_BASE || type >= WLAN_NL_MSG_MAX) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, |
| "NLINK: Received Invalid NL Msg type [%x]", type); |
| return; |
| } |
| |
| /* |
| * All the messages must at least carry the tAniMsgHdr |
| * Drop any message with invalid length |
| */ |
| if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(tAniMsgHdr))) { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, |
| "NLINK: Received NL Msg with invalid len[%x]", nlh->nlmsg_len); |
| return; |
| } |
| |
| // turn type into dispatch table offset |
| type -= WLAN_NL_MSG_BASE; |
| |
| // dispatch to handler |
| if (nl_srv_msg_handler[type] != NULL) { |
| (nl_srv_msg_handler[type])(skb); |
| } else { |
| VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_WARN, |
| "NLINK: No handler for Netlink Msg [0x%X]", type); |
| } |
| } |
| |
| /** |
| * nl_srv_is_initialized() - This function is used check if the netlink |
| * service is initialized |
| * |
| * This function is used check if the netlink service is initialized |
| * |
| * Return: Return -EPERM if the service is not initialized |
| * |
| */ |
| int nl_srv_is_initialized() |
| { |
| if (nl_srv_sock) |
| return 0; |
| else |
| return -EPERM; |
| } |
| |
| #endif |