diff options
Diffstat (limited to 'lib/op-msg.c')
-rw-r--r-- | lib/op-msg.c | 565 |
1 files changed, 565 insertions, 0 deletions
diff --git a/lib/op-msg.c b/lib/op-msg.c new file mode 100644 index 0000000..73a2daa --- /dev/null +++ b/lib/op-msg.c @@ -0,0 +1,565 @@ +/* + * Linux WiMax + * Messaging interface implementation + * + * + * Copyright (C) 2007-2008 Intel Corporation. All rights reserved. + * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \defgroup the_messaging_interface The message interface + * + * This is a payload agnostic message interface for communication + * between the WiMAX kernel drivers and user space applications. + * + * This interfaces builds on the \e kernel-to-user unidirectional \ref + * the_pipe_interface_group "pipe interface". It writes data to the default + * \e message pipe by sending it to the WiMAX kernel stack, which + * passes it to the driver using the \e wimax_dev->op_msg_from_user() + * call. + * + * Only the default \e message pipe is bidirectional; WiMAX kernel + * drivers receive messages sent with wimax_msg_write(). + * + * \note The wimaxll_msg_fd() and wimaxll_msg_read() functions operate + * on the default \e message pipe, being convenience functions for + * wimaxll_pipe_fd() and wimaxll_pipe_msg_read(). + * + * To wait for a message from the driver: + * + * @code + * void *msg; + * ... + * size = wimaxll_msg_read(wmx, &msg); + * @endcode + * + * Note this call is synchronous and blocking, and won't timeout. You + * can put it on a thread to emulate asynchrony (see \ref + * multithreading), but still it is quite difficult to integrate it in + * an event loop. Read on for mainloop integration options. + * + * In \e msg you get a pointer to a dynamically allocated (by \e + * libwimaxll) area with the message payload. When the application is + * done processing the message, call: + * + * @code + * wimaxll_msg_free(msg); + * @endcode + * + * To write messages to the driver: + * + * @code + * wimaxll_msg_write(wmx, buf, buf_size); + * @endcode + * + * where \a buf points to where the message is stored. + * + * \note Messages can be written to the driver \e only over the + * default \e message pipe. Thus, no wimax_pipe_msg_write() + * function is available. + * + * All functions return negative \a errno codes on error. + * + * To integrate message reception into a mainloop, \ref callbacks + * "callbacks" and select() should be used. The file descriptor + * associated to the default \e message \e pipe can be obtained with + * wimaxll_msg_fd(). When there is activity on the file descriptor, + * wimaxll_pipe_read() should be called on the default pipe: + * + * \code + * wimax_pipe_read(wmx, wimax_msg_pipe_id(wmx)); + * \endcode + * + * this will, as explained in \ref receiving, for each received + * notification, execute its callback. + * + * The callback for reception of messages from the WiMAX kernel stack + * can be set with wimaxll_pipe_set_cb_msg_to_user() (using as \e + * pipe_id the value returned by wimax_msg_pipe_id()). For detailed + * information on the message reception callback, see the definition + * of \ref wimaxll_msg_to_user_cb_f. + * + * The kernel WiMAX stack allows drivers to create any number of pipes + * on which to send information (messages) to user space. This + * interface provides means to read those messages, which are mostly + * device specific. + * + * This is a lower level interface than \ref the_messaging_interface + * "the messaging interface"; however, it operates similarly. + * + * @code + * void *msg; + * ... + * handle = wimaxll_pipe_open(wmx, "PIPENAME"); + * ... + * wimaxll_pipe_msg_read(wmx, handle, &msg); + * ... + * wimaxll_msg_free(msg); + * ... + * wimaxll_pipe_close(wmx, handle); + * @endcode + * + * More information about the details of this interface can be found + * \ref the_pipe_interface_group "here". + * + * \note These pipes are not bidirectional. + */ +#define _GNU_SOURCE +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <assert.h> +#include <linux/types.h> +#include <netlink/msg.h> +#include <netlink/genl/genl.h> +#include <wimaxll.h> +#include "internal.h" +#define D_LOCAL 0 +#include "debug.h" + + + +/** + * WIMAX_GNL_MSG_FROM_USER: policy specification + * + * \ingroup the_messaging_interface + * \internal + * + * Authoritative reference for this is at the kernel code, + * drivers/net/wimax/op-msg.c. + * + */ +static +struct nla_policy wimaxll_gnl_msg_from_user_policy[WIMAX_GNL_ATTR_MAX + 1] = { + [WIMAX_GNL_MSG_DATA] = { + .type = NLA_UNSPEC, + }, +}; + + +/** + * Callback to process an WIMAX_GNL_OP_MSG_TO_USER from the kernel + * + * \internal + * \ingroup the_messaging_interface + * + * \param wmx WiMAX device handle + * \param mch Pointer to \c struct wimaxll_mc_handle + * \param msg Pointer to netlink message + * \return \c enum nl_cb_action + * + * wimaxll_mc_rx_read() calls libnl's nl_recvmsgs() to receive messages; + * when a valid message is received, it goes into a loop that selects + * a callback to run for each type of message and it will call this + * function. + * + * This just expects a _MSG_TO_USER message, whose payload is what + * has to be passed to the caller. Because nl_recvmsgs() will free the + * message data, a new buffer has to be allocated and copied (a patch + * has been merged already to future versions of libnl that helps in + * this). + * + * It stores the buffer and size (or result in case of error) in the + * context passed in \e mch->msg_to_user_context. + */ +int wimaxll_gnl_handle_msg_to_user(struct wimaxll_handle *wmx, + struct wimaxll_mc_handle *mch, + struct nl_msg *msg) +{ + size_t size; + ssize_t result; + struct nlmsghdr *nl_hdr; + struct genlmsghdr *gnl_hdr; + struct nlattr *tb[WIMAX_GNL_ATTR_MAX+1]; + struct wimaxll_gnl_cb_context *ctx = mch->msg_to_user_context; + void *data; + + d_fnstart(7, wmx, "(wmx %p mch %p msg %p)\n", wmx, mch, msg); + nl_hdr = nlmsg_hdr(msg); + gnl_hdr = nlmsg_data(nl_hdr); + + assert(gnl_hdr->cmd == WIMAX_GNL_OP_MSG_TO_USER); + + /* Parse the attributes */ + result = genlmsg_parse(nl_hdr, 0, tb, WIMAX_GNL_ATTR_MAX, + wimaxll_gnl_msg_from_user_policy); + if (result < 0) { + wimaxll_msg(wmx, "E: %s: genlmsg_parse() failed: %d\n", + __func__, result); + wimaxll_cb_context_set_result(ctx, result); + result = NL_SKIP; + goto error_parse; + } + if (tb[WIMAX_GNL_MSG_DATA] == NULL) { + wimaxll_msg(wmx, "E: %s: cannot find MSG_DATA attribute\n", + __func__); + wimaxll_cb_context_set_result(ctx, -ENXIO); + result = NL_SKIP; + goto error_no_attrs; + + } + result = (ssize_t) nla_get_u64(tb[WIMAX_GNL_RESULT_CODE]); + wimaxll_cb_context_set_result(ctx, result); + + size = nla_len(tb[WIMAX_GNL_MSG_DATA]); + data = nla_data(tb[WIMAX_GNL_MSG_DATA]); + + d_printf(1, wmx, "D: CRX genlmsghdr cmd %u version %u\n", + gnl_hdr->cmd, gnl_hdr->version); + d_printf(1, wmx, "D: CRX msg from kernel %u bytes\n", size); + d_dump(2, wmx, data, size); + + /* This was set by whoever called nl_recmvsgs (or + * wimaxll_mc_rx_read() or wimaxll_pipe_read()) */ + if (mch->msg_to_user_cb(wmx, ctx, data, size) == -EBUSY) + result = NL_STOP; + else + result = NL_OK; +error_no_attrs: +error_parse: + d_fnend(7, wmx, "(wmx %p mch %p msg %p) = %d\n", wmx, mch, msg, result); + return result; +} + + +struct wimaxll_cb_msg_to_user_context { + struct wimaxll_gnl_cb_context ctx; + void *data; +}; + + +/* + * Default handling of messages + * + * When someone calls wimaxll_msg_read() or wimaxll_pipe_msg_read(), those + * functions set this default callback, which will just copy the data + * to a buffer and pass that pointer to the caller along with the size. + */ +static +int wimaxll_cb_msg_to_user(struct wimaxll_handle *wmx, + struct wimaxll_gnl_cb_context *ctx, + const char *data, size_t data_size) +{ + struct wimaxll_cb_msg_to_user_context *mtu_ctx = + wimaxll_container_of( + ctx, struct wimaxll_cb_msg_to_user_context, ctx); + + if (mtu_ctx->data) + return -EBUSY; + mtu_ctx->data = malloc(data_size); + if (mtu_ctx->data) { + memcpy(mtu_ctx->data, data, data_size); + ctx->result = data_size; + } else + ctx->result = -ENOMEM; + return 0; +} + + +/** + * Read a message from any WiMAX kernel-user pipe + * + * \param wmx WiMAX device handle + * \param pipe_id Pipe to read from (as returned by + * wimaxll_pipe_open()). To use the default pipe, indicate + * use wimax_msg_pipe_id(). + * \param buf Somewhere where to store the pointer to the message data. + * \return If successful, a positive (and \c *buf set) or zero size of + * the message; on error, a negative \a errno code (\c buf + * n/a). + * + * Returns a message allocated in \c *buf as sent by the kernel via + * the indicated pipe. The message is allocated by the + * library and owned by the caller. When done, it has to be freed with + * wimaxll_msg_free() to release the space allocated to it. + * + * \note This is a blocking call. + * + * \ingroup the_messaging_interface + */ +ssize_t wimaxll_pipe_msg_read(struct wimaxll_handle *wmx, unsigned pipe_id, + void **buf) +{ + ssize_t result; + struct wimaxll_cb_msg_to_user_context mtu_ctx = { + .ctx = WIMAXLL_GNL_CB_CONTEXT_INIT(wmx), + .data = NULL, + }; + wimaxll_msg_to_user_cb_f prev_cb = NULL; + struct wimaxll_gnl_cb_context *prev_priv = NULL; + + d_fnstart(3, wmx, "(wmx %p buf %p)\n", wmx, buf); + wimaxll_pipe_get_cb_msg_to_user(wmx, pipe_id, &prev_cb, &prev_priv); + wimaxll_pipe_set_cb_msg_to_user(wmx, pipe_id, + wimaxll_cb_msg_to_user, &mtu_ctx.ctx); + result = wimaxll_pipe_read(wmx, pipe_id); + if (result >= 0) { + *buf = mtu_ctx.data; + result = mtu_ctx.ctx.result; + } + wimaxll_pipe_set_cb_msg_to_user(wmx, pipe_id, prev_cb, prev_priv); + d_fnend(3, wmx, "(wmx %p buf %p) = %zd\n", wmx, buf, result); + return result; +} + + +/** + * Free a message received with wimaxll_pipe_msg_read() or + * wimaxll_msg_read() + * + * \param msg message pointer returned by wimaxll_pipe_msg_read() or + * wimaxll_msg_read(). + * + * \note this function is the same as wimaxll_msg_free() + * + * \ingroup the_messaging_interface + */ +void wimaxll_pipe_msg_free(void *msg) +{ + d_fnstart(3, NULL, "(msg %p)\n", msg); + free(msg); + d_fnend(3, NULL, "(msg %p) = void\n", msg); +} + + +/** + * Return the file descriptor associated to the default \e message pipe + * + * \param wmx WiMAX device handle + * \return file descriptor associated to the messaging group, that can + * be fed to functions like select(). + * + * This allows to select() on the file descriptor, which will block + * until a message is available, that then can be read with + * wimaxll_pipe_read(). + * + * \ingroup the_messaging_interface + */ +int wimaxll_msg_fd(struct wimaxll_handle *wmx) +{ + return wimaxll_mc_rx_fd(wmx, wmx->mc_msg); +} + + +/** + * Read a message from the WiMAX default \e message pipe. + * + * \param wmx WiMAX device handle + * \param buf Somewhere where to store the pointer to the message data. + * \return If successful, a positive (and \c *buf set) or zero size of + * the message; on error, a negative \a errno code (\c buf + * n/a). + * + * Returns a message allocated in \c *buf as sent by the kernel via + * the default \e message pipe. The message is allocated by the + * library and owned by the caller. When done, it has to be freed with + * wimaxll_msg_free() to release the space allocated to it. + * + * \note This is a blocking call. + * + * \ingroup the_messaging_interface + */ +ssize_t wimaxll_msg_read(struct wimaxll_handle *wmx, void **buf) +{ + return wimaxll_pipe_msg_read(wmx, wmx->mc_msg, buf); +} + + +/** + * Free a message received with wimaxll_pipe_msg_read() or + * wimaxll_msg_read() + * + * \param msg message pointer returned by wimaxll_pipe_msg_read() or + * wimaxll_msg_read(). + * + * \note this function is the same as wimaxll_pipe_msg_free() + * + * \ingroup the_messaging_interface + */ +void wimaxll_msg_free(void *msg) +{ + wimaxll_pipe_msg_free(msg); +} + + +/** + * Send a driver-specific message to a WiMAX device + * + * \param wmx wimax device descriptor + * \param buf Pointer to the wimax message. + * \param size size of the message. + * + * \return 0 if ok < 0 errno code on error. On error it is assumed + * the message wasn't delivered. + * + * Sends a data buffer down to the kernel driver. The format of the + * message is driver specific. + * + * \note This is a blocking call + * + * \ingroup the_messaging_interface + */ +ssize_t wimaxll_msg_write(struct wimaxll_handle *wmx, + const void *buf, size_t size) +{ + ssize_t result; + struct nl_msg *nl_msg; + void *msg; + + d_fnstart(3, wmx, "(wmx %p buf %p size %zu)\n", wmx, buf, size); + nl_msg = nlmsg_new(); + if (nl_msg == NULL) { + result = nl_get_errno(); + wimaxll_msg(wmx, "E: cannot allocate generic netlink " + "message: %m\n"); + goto error_msg_alloc; + } + msg = genlmsg_put(nl_msg, NL_AUTO_PID, NL_AUTO_SEQ, + wimaxll_family_id(wmx), 0, 0, + WIMAX_GNL_OP_MSG_FROM_USER, WIMAX_GNL_VERSION); + if (msg == NULL) { + result = nl_get_errno(); + wimaxll_msg(wmx, "E: %s: error preparing message: %d\n", + __func__, result); + goto error_msg_prep; + } + + nla_put(nl_msg, WIMAX_GNL_MSG_DATA, size, buf); + + d_printf(5, wmx, "D: CTX nl + genl header:\n"); + d_dump(5, wmx, nlmsg_hdr(nl_msg), + sizeof(struct nlmsghdr) + sizeof(struct genlmsghdr)); + d_printf(5, wmx, "D: CTX wimax message:\n"); + d_dump(5, wmx, buf, size); + + result = nl_send_auto_complete(wmx->nlh_tx, nl_msg); + if (result < 0) { + wimaxll_msg(wmx, "E: error sending message: %d\n", result); + goto error_msg_send; + } + + result = wimaxll_wait_for_ack(wmx); /* Get the ACK from netlink */ + if (result < 0) + wimaxll_msg(wmx, "E: %s: generic netlink ack failed: %d\n", + __func__, result); +error_msg_send: +error_msg_prep: + nlmsg_free(nl_msg); +error_msg_alloc: + d_fnend(3, wmx, "(wmx %p buf %p size %zu) = %zd\n", + wmx, buf, size, result); + return result; +} + + +/** + * Return the pipe ID for the messaging interface + * + * @param wmx WiMAX device descriptor + * @return Pipe id of the messaging interface, that can be used with + * the wimaxll_pipe_*() functions. + * + * \ingroup the_messaging_interface_group + */ +unsigned wimaxll_msg_pipe_id(struct wimaxll_handle *wmx) +{ + return wmx->mc_msg; +} + + +/** + * Get the callback and priv pointer for a MSG_TO_USER message + * + * \param wmx WiMAX handle. + * \param pipe_id Pipe on which to listen for the message [as returned + * by wimaxll_pipe_open()]. + * \param cb Where to store the current callback function. + * \param context Where to store the private data pointer passed to the + * callback. + * + * \ingroup the_messaging_interface_group + */ +void wimaxll_pipe_get_cb_msg_to_user( + struct wimaxll_handle *wmx, unsigned pipe_id, + wimaxll_msg_to_user_cb_f *cb, struct wimaxll_gnl_cb_context **context) +{ + struct wimaxll_mc_handle *mch; + + mch = __wimaxll_get_mc_handle(wmx, pipe_id); + if (mch != NULL) { + *cb = mch->msg_to_user_cb; + *context = mch->msg_to_user_context; + } +} + + +/** + * Set the callback and priv pointer for a MSG_TO_USER message + * + * \param wmx WiMAX handle. + * \param pipe_id Pipe on which to listen for the message [as returned + * by wimaxll_pipe_open()]. + * \param cb Callback function to set + * \param context Private data pointer to pass to the callback + * function (wrap a \a struct wimaxll_gnl_cb_context in your context + * struct and pass a pointer to it; then use wimaxll_container_of() + * to extract it back). + * + * \ingroup the_messaging_interface_group + */ +void wimaxll_pipe_set_cb_msg_to_user( + struct wimaxll_handle *wmx, unsigned pipe_id, + wimaxll_msg_to_user_cb_f cb, struct wimaxll_gnl_cb_context *context) +{ + struct wimaxll_mc_handle *mch; + + mch = __wimaxll_get_mc_handle(wmx, pipe_id); + if (mch != NULL) { + mch->msg_to_user_cb = cb; + mch->msg_to_user_context = context; + } +} + + +void wimax_msg_fd() __attribute__ ((weak, alias("wimaxll_msg_fd"))); +void wimax_msg_read() __attribute__ ((weak, alias("wimaxll_msg_read"))); +void wimax_msg_write() __attribute__ ((weak, alias("wimaxll_msg_write"))); +void wimax_msg_free() __attribute__ ((weak, alias("wimaxll_msg_free"))); +void wimax_msg_pipe_id() __attribute__ ((weak, alias("wimaxll_msg_pipe_id"))); +void wimax_pipe_msg_read() + __attribute__ ((weak, alias("wimaxll_pipe_msg_read"))); +void wimax_pipe_msg_free() + __attribute__ ((weak, alias("wimaxll_pipe_msg_free"))); +void wimax_pipe_get_cb_msg_to_user() + __attribute__ ((weak, alias("wimaxll_pipe_get_cb_msg_to_user"))); +void wimax_pipe_set_cb_msg_to_user() + __attribute__ ((weak, alias("wimaxll_pipe_set_cb_msg_to_user"))); |