/* * WiMAX stack operation: relay a message from user space * * @wimax_dev: device descriptor * @pipe_name: named pipe the message is for * @msg_buf: pointer to the message bytes * @msg_len: length of the buffer * @genl_info: passed by the generic netlink layer * * The WiMAX stack will call this function when a message was received * from user space. * * For the i2400m, this is an L3L4 message, as specified in * include/linux/wimax/i2400m.h, and thus prefixed with a 'struct * i2400m_l3l4_hdr'. Driver (and device) expect the messages to be * coded in Little Endian. * * This function just verifies that the header declaration and the * payload are consistent and then deals with it, either forwarding it * to the device or procesing it locally. * * In the i2400m, messages are basically commands that will carry an * ack, so we use i2400m_msg_to_dev() and then deliver the ack back to * user space. The rx.c code might intercept the response and use it * to update the driver's state, but then it will pass it on so it can * be relayed back to user space. * * Note that asynchronous events from the device are processed and * sent to user space in rx.c. */ static int i2400m_op_msg_from_user(struct wimax_dev *wimax_dev, const char *pipe_name, const void *msg_buf, size_t msg_len, const struct genl_info *genl_info) { int result; struct i2400m *i2400m = wimax_dev_to_i2400m(wimax_dev); struct device *dev = i2400m_dev(i2400m); struct sk_buff *ack_skb; d_fnstart(4, dev, "(wimax_dev %p [i2400m %p] msg_buf %p " "msg_len %zu genl_info %p)\n", wimax_dev, i2400m, msg_buf, msg_len, genl_info); ack_skb = i2400m_msg_to_dev(i2400m, msg_buf, msg_len); result = PTR_ERR(ack_skb); if (IS_ERR(ack_skb)) goto error_msg_to_dev; if (unlikely(i2400m->trace_msg_from_user)) wimax_msg(&i2400m->wimax_dev, "trace", msg_buf, msg_len, GFP_KERNEL); result = wimax_msg_send(&i2400m->wimax_dev, ack_skb); error_msg_to_dev: d_fnend(4, dev, "(wimax_dev %p [i2400m %p] msg_buf %p msg_len %zu " "genl_info %p) = %d\n", wimax_dev, i2400m, msg_buf, msg_len, genl_info, result); return result; }
/** * i2400m_msg_to_dev - Send a control message to the device and get a response * * @i2400m: device descriptor * * @msg_skb: an skb * * * @buf: pointer to the buffer containing the message to be sent; it * has to start with a &struct i2400M_l3l4_hdr and then * followed by the payload. Once this function returns, the * buffer can be reused. * * @buf_len: buffer size * * Returns: * * Pointer to skb containing the ack message. You need to check the * pointer with IS_ERR(), as it might be an error code. Error codes * could happen because: * * - the message wasn't formatted correctly * - couldn't send the message * - failed waiting for a response * - the ack message wasn't formatted correctly * * The returned skb has been allocated with wimax_msg_to_user_alloc(), * it contains the response in a netlink attribute and is ready to be * passed up to user space with wimax_msg_to_user_send(). To access * the payload and its length, use wimax_msg_{data,len}() on the skb. * * The skb has to be freed with kfree_skb() once done. * * Description: * * This function delivers a message/command to the device and waits * for an ack to be received. The format is described in * linux/wimax/i2400m.h. In summary, a command/get/set is followed by an * ack. * * This function will not check the ack status, that's left up to the * caller. Once done with the ack skb, it has to be kfree_skb()ed. * * The i2400m handles only one message at the same time, thus we need * the mutex to exclude other players. * * We write the message and then wait for an answer to come back. The * RX path intercepts control messages and handles them in * i2400m_rx_ctl(). Reports (notifications) are (maybe) processed * locally and then forwarded (as needed) to user space on the WiMAX * stack message pipe. Acks are saved and passed back to us through an * skb in i2400m->ack_skb which is ready to be given to generic * netlink if need be. */ struct sk_buff *i2400m_msg_to_dev(struct i2400m *i2400m, const void *buf, size_t buf_len) { int result; struct device *dev = i2400m_dev(i2400m); const struct i2400m_l3l4_hdr *msg_l3l4_hdr; struct sk_buff *ack_skb; const struct i2400m_l3l4_hdr *ack_l3l4_hdr; size_t ack_len; int ack_timeout; unsigned msg_type; unsigned long flags; d_fnstart(3, dev, "(i2400m %p buf %p len %zu)\n", i2400m, buf, buf_len); rmb(); /* Make sure we see what i2400m_dev_reset_handle() */ if (i2400m->boot_mode) return ERR_PTR(-EL3RST); msg_l3l4_hdr = buf; /* Check msg & payload consistency */ result = i2400m_msg_size_check(i2400m, msg_l3l4_hdr, buf_len); if (result < 0) goto error_bad_msg; msg_type = le16_to_cpu(msg_l3l4_hdr->type); d_printf(1, dev, "CMD/GET/SET 0x%04x %zu bytes\n", msg_type, buf_len); d_dump(2, dev, buf, buf_len); /* Setup the completion, ack_skb ("we are waiting") and send * the message to the device */ mutex_lock(&i2400m->msg_mutex); spin_lock_irqsave(&i2400m->rx_lock, flags); i2400m->ack_skb = ERR_PTR(-EINPROGRESS); spin_unlock_irqrestore(&i2400m->rx_lock, flags); init_completion(&i2400m->msg_completion); result = i2400m_tx(i2400m, buf, buf_len, I2400M_PT_CTRL); if (result < 0) { dev_err(dev, "can't send message 0x%04x: %d\n", le16_to_cpu(msg_l3l4_hdr->type), result); goto error_tx; } /* Some commands take longer to execute because of crypto ops, * so we give them some more leeway on timeout */ switch (msg_type) { case I2400M_MT_GET_TLS_OPERATION_RESULT: case I2400M_MT_CMD_SEND_EAP_RESPONSE: ack_timeout = 5 * HZ; break; default: ack_timeout = HZ; } if (unlikely(i2400m->trace_msg_from_user)) wimax_msg(&i2400m->wimax_dev, "echo", buf, buf_len, GFP_KERNEL); /* The RX path in rx.c will put any response for this message * in i2400m->ack_skb and wake us up. If we cancel the wait, * we need to change the value of i2400m->ack_skb to something * not -EINPROGRESS so RX knows there is no one waiting. */ result = wait_for_completion_interruptible_timeout( &i2400m->msg_completion, ack_timeout); if (result == 0) { dev_err(dev, "timeout waiting for reply to message 0x%04x\n", msg_type); result = -ETIMEDOUT; i2400m_msg_to_dev_cancel_wait(i2400m, result); goto error_wait_for_completion; } else if (result < 0) { dev_err(dev, "error waiting for reply to message 0x%04x: %d\n", msg_type, result); i2400m_msg_to_dev_cancel_wait(i2400m, result); goto error_wait_for_completion; } /* Pull out the ack data from i2400m->ack_skb -- see if it is * an error and act accordingly */ spin_lock_irqsave(&i2400m->rx_lock, flags); ack_skb = i2400m->ack_skb; if (IS_ERR(ack_skb)) result = PTR_ERR(ack_skb); else result = 0; i2400m->ack_skb = NULL; spin_unlock_irqrestore(&i2400m->rx_lock, flags); if (result < 0) goto error_ack_status; ack_l3l4_hdr = wimax_msg_data_len(ack_skb, &ack_len); /* Check the ack and deliver it if it is ok */ if (unlikely(i2400m->trace_msg_from_user)) wimax_msg(&i2400m->wimax_dev, "echo", ack_l3l4_hdr, ack_len, GFP_KERNEL); result = i2400m_msg_size_check(i2400m, ack_l3l4_hdr, ack_len); if (result < 0) { dev_err(dev, "HW BUG? reply to message 0x%04x: %d\n", msg_type, result); goto error_bad_ack_len; } if (msg_type != le16_to_cpu(ack_l3l4_hdr->type)) { dev_err(dev, "HW BUG? bad reply 0x%04x to message 0x%04x\n", le16_to_cpu(ack_l3l4_hdr->type), msg_type); result = -EIO; goto error_bad_ack_type; } i2400m_msg_ack_hook(i2400m, ack_l3l4_hdr, ack_len); mutex_unlock(&i2400m->msg_mutex); d_fnend(3, dev, "(i2400m %p buf %p len %zu) = %p\n", i2400m, buf, buf_len, ack_skb); return ack_skb; error_bad_ack_type: error_bad_ack_len: kfree_skb(ack_skb); error_ack_status: error_wait_for_completion: error_tx: mutex_unlock(&i2400m->msg_mutex); error_bad_msg: d_fnend(3, dev, "(i2400m %p buf %p len %zu) = %d\n", i2400m, buf, buf_len, result); return ERR_PTR(result); }
struct sk_buff *i2400m_msg_to_dev(struct i2400m *i2400m, const void *buf, size_t buf_len) { int result; struct device *dev = i2400m_dev(i2400m); const struct i2400m_l3l4_hdr *msg_l3l4_hdr; struct sk_buff *ack_skb; const struct i2400m_l3l4_hdr *ack_l3l4_hdr; size_t ack_len; int ack_timeout; unsigned msg_type; unsigned long flags; d_fnstart(3, dev, "(i2400m %p buf %p len %zu)\n", i2400m, buf, buf_len); rmb(); if (i2400m->boot_mode) return ERR_PTR(-EL3RST); msg_l3l4_hdr = buf; result = i2400m_msg_size_check(i2400m, msg_l3l4_hdr, buf_len); if (result < 0) goto error_bad_msg; msg_type = le16_to_cpu(msg_l3l4_hdr->type); d_printf(1, dev, "CMD/GET/SET 0x%04x %zu bytes\n", msg_type, buf_len); d_dump(2, dev, buf, buf_len); mutex_lock(&i2400m->msg_mutex); spin_lock_irqsave(&i2400m->rx_lock, flags); i2400m->ack_skb = ERR_PTR(-EINPROGRESS); spin_unlock_irqrestore(&i2400m->rx_lock, flags); init_completion(&i2400m->msg_completion); result = i2400m_tx(i2400m, buf, buf_len, I2400M_PT_CTRL); if (result < 0) { dev_err(dev, "can't send message 0x%04x: %d\n", le16_to_cpu(msg_l3l4_hdr->type), result); goto error_tx; } switch (msg_type) { case I2400M_MT_GET_TLS_OPERATION_RESULT: case I2400M_MT_CMD_SEND_EAP_RESPONSE: ack_timeout = 5 * HZ; break; default: ack_timeout = HZ; } if (unlikely(i2400m->trace_msg_from_user)) wimax_msg(&i2400m->wimax_dev, "echo", buf, buf_len, GFP_KERNEL); result = wait_for_completion_interruptible_timeout( &i2400m->msg_completion, ack_timeout); if (result == 0) { dev_err(dev, "timeout waiting for reply to message 0x%04x\n", msg_type); result = -ETIMEDOUT; i2400m_msg_to_dev_cancel_wait(i2400m, result); goto error_wait_for_completion; } else if (result < 0) { dev_err(dev, "error waiting for reply to message 0x%04x: %d\n", msg_type, result); i2400m_msg_to_dev_cancel_wait(i2400m, result); goto error_wait_for_completion; } spin_lock_irqsave(&i2400m->rx_lock, flags); ack_skb = i2400m->ack_skb; if (IS_ERR(ack_skb)) result = PTR_ERR(ack_skb); else result = 0; i2400m->ack_skb = NULL; spin_unlock_irqrestore(&i2400m->rx_lock, flags); if (result < 0) goto error_ack_status; ack_l3l4_hdr = wimax_msg_data_len(ack_skb, &ack_len); if (unlikely(i2400m->trace_msg_from_user)) wimax_msg(&i2400m->wimax_dev, "echo", ack_l3l4_hdr, ack_len, GFP_KERNEL); result = i2400m_msg_size_check(i2400m, ack_l3l4_hdr, ack_len); if (result < 0) { dev_err(dev, "HW BUG? reply to message 0x%04x: %d\n", msg_type, result); goto error_bad_ack_len; } if (msg_type != le16_to_cpu(ack_l3l4_hdr->type)) { dev_err(dev, "HW BUG? bad reply 0x%04x to message 0x%04x\n", le16_to_cpu(ack_l3l4_hdr->type), msg_type); result = -EIO; goto error_bad_ack_type; } i2400m_msg_ack_hook(i2400m, ack_l3l4_hdr, ack_len); mutex_unlock(&i2400m->msg_mutex); d_fnend(3, dev, "(i2400m %p buf %p len %zu) = %p\n", i2400m, buf, buf_len, ack_skb); return ack_skb; error_bad_ack_type: error_bad_ack_len: kfree_skb(ack_skb); error_ack_status: error_wait_for_completion: error_tx: mutex_unlock(&i2400m->msg_mutex); error_bad_msg: d_fnend(3, dev, "(i2400m %p buf %p len %zu) = %d\n", i2400m, buf, buf_len, result); return ERR_PTR(result); }