13a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
23a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Intel Wireless WiMAX Connection 2400m
33a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Miscellaneous control functions for managing the device
43a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
53a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
63a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
73a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
83a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Redistribution and use in source and binary forms, with or without
93a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * modification, are permitted provided that the following conditions
103a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * are met:
113a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
123a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *   * Redistributions of source code must retain the above copyright
133a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *     notice, this list of conditions and the following disclaimer.
143a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *   * Redistributions in binary form must reproduce the above copyright
153a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *     notice, this list of conditions and the following disclaimer in
163a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *     the documentation and/or other materials provided with the
173a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *     distribution.
183a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *   * Neither the name of Intel Corporation nor the names of its
193a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *     contributors may be used to endorse or promote products derived
203a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *     from this software without specific prior written permission.
213a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
223a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
233a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
243a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
253a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
263a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
273a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
283a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
293a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
303a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
313a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
323a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
333a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
343a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
353a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Intel Corporation <linux-wimax@intel.com>
363a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
373a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *  - Initial implementation
383a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
393a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * This is a collection of functions used to control the device (plus
403a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * a few helpers).
413a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
423a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * There are utilities for handling TLV buffers, hooks on the device's
433a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * reports to act on device changes of state [i2400m_report_hook()],
443a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * on acks to commands [i2400m_msg_ack_hook()], a helper for sending
453a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * commands to the device and blocking until a reply arrives
463a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * [i2400m_msg_to_dev()], a few high level commands for manipulating
473a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * the device state, powersving mode and configuration plus the
483a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * routines to setup the device once communication is stablished with
493a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * it [i2400m_dev_initialize()].
503a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
513a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * ROADMAP
523a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
53421f91d21ad6f799dc7b489bb33cc560ccc56f98Uwe Kleine-König * i2400m_dev_initialize()       Called by i2400m_dev_start()
543a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *   i2400m_set_init_config()
553a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *   i2400m_cmd_get_state()
563a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * i2400m_dev_shutdown()        Called by i2400m_dev_stop()
57c931ceeb780560ff652a8f9875f88778439ee87eInaky Perez-Gonzalez *   i2400m_reset()
583a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
593a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * i2400m_{cmd,get,set}_*()
603a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *   i2400m_msg_to_dev()
613a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *   i2400m_msg_check_status()
623a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
633a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * i2400m_report_hook()         Called on reception of an event
643a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *   i2400m_report_state_hook()
653a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *     i2400m_tlv_buffer_walk()
663a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *     i2400m_tlv_match()
673a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *     i2400m_report_tlv_system_state()
683a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *     i2400m_report_tlv_rf_switches_status()
693a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *     i2400m_report_tlv_media_status()
703a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *   i2400m_cmd_enter_powersave()
713a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
723a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * i2400m_msg_ack_hook()        Called on reception of a reply to a
733a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *                              command, get or set
743a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
753a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
763a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez#include <stdarg.h>
773a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez#include "i2400m.h"
783a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez#include <linux/kernel.h>
795a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
803a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez#include <linux/wimax/i2400m.h>
81ee40fa0656a730491765545ff7550f3c1ceb0fbcPaul Gortmaker#include <linux/export.h>
826eb07caf1ac5723720caea2ee93cd11b7058a0aaPaul Gortmaker#include <linux/moduleparam.h>
833a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
843a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
853a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez#define D_SUBMODULE control
863a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez#include "debug-levels.h"
873a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
889d7fdf1ba9d5b8963bf8ffe29eea17f508e81bdePrasanna S Panchamukhistatic int i2400m_idle_mode_disabled;/* 0 (idle mode enabled) by default */
899d7fdf1ba9d5b8963bf8ffe29eea17f508e81bdePrasanna S Panchamukhimodule_param_named(idle_mode_disabled, i2400m_idle_mode_disabled, int, 0644);
909d7fdf1ba9d5b8963bf8ffe29eea17f508e81bdePrasanna S PanchamukhiMODULE_PARM_DESC(idle_mode_disabled,
919d7fdf1ba9d5b8963bf8ffe29eea17f508e81bdePrasanna S Panchamukhi		 "If true, the device will not enable idle mode negotiation "
929d7fdf1ba9d5b8963bf8ffe29eea17f508e81bdePrasanna S Panchamukhi		 "with the base station (when connected) to save power.");
939d7fdf1ba9d5b8963bf8ffe29eea17f508e81bdePrasanna S Panchamukhi
949d7fdf1ba9d5b8963bf8ffe29eea17f508e81bdePrasanna S Panchamukhi/* 0 (power saving enabled) by default */
959d7fdf1ba9d5b8963bf8ffe29eea17f508e81bdePrasanna S Panchamukhistatic int i2400m_power_save_disabled;
969d7fdf1ba9d5b8963bf8ffe29eea17f508e81bdePrasanna S Panchamukhimodule_param_named(power_save_disabled, i2400m_power_save_disabled, int, 0644);
979d7fdf1ba9d5b8963bf8ffe29eea17f508e81bdePrasanna S PanchamukhiMODULE_PARM_DESC(power_save_disabled,
989d7fdf1ba9d5b8963bf8ffe29eea17f508e81bdePrasanna S Panchamukhi		 "If true, the driver will not tell the device to enter "
999d7fdf1ba9d5b8963bf8ffe29eea17f508e81bdePrasanna S Panchamukhi		 "power saving mode when it reports it is ready for it. "
1009d7fdf1ba9d5b8963bf8ffe29eea17f508e81bdePrasanna S Panchamukhi		 "False by default (so the device is told to do power "
1019d7fdf1ba9d5b8963bf8ffe29eea17f508e81bdePrasanna S Panchamukhi		 "saving).");
1029d7fdf1ba9d5b8963bf8ffe29eea17f508e81bdePrasanna S Panchamukhi
103e3d32687a624845e97f9717d9d2027b44b8c49a2stephen hemmingerstatic int i2400m_passive_mode;	/* 0 (passive mode disabled) by default */
10455a662d6468005ec3cd799fbd8d0ad03dfae6d2aInaky Perez-Gonzalezmodule_param_named(passive_mode, i2400m_passive_mode, int, 0644);
10555a662d6468005ec3cd799fbd8d0ad03dfae6d2aInaky Perez-GonzalezMODULE_PARM_DESC(passive_mode,
10655a662d6468005ec3cd799fbd8d0ad03dfae6d2aInaky Perez-Gonzalez		 "If true, the driver will not do any device setup "
10755a662d6468005ec3cd799fbd8d0ad03dfae6d2aInaky Perez-Gonzalez		 "and leave it up to user space, who must be properly "
10855a662d6468005ec3cd799fbd8d0ad03dfae6d2aInaky Perez-Gonzalez		 "setup.");
10955a662d6468005ec3cd799fbd8d0ad03dfae6d2aInaky Perez-Gonzalez
1103a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
1113a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
1123a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Return if a TLV is of a give type and size
1133a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
1143a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @tlv_hdr: pointer to the TLV
1153a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @tlv_type: type of the TLV we are looking for
1163a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @tlv_size: expected size of the TLV we are looking for (if -1,
1173a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *            don't check the size). This includes the header
1183a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Returns: 0 if the TLV matches
1193a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *          < 0 if it doesn't match at all
1203a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *          > 0 total TLV + payload size, if the type matches, but not
1213a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *              the size
1223a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
1233a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezstatic
1243a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezssize_t i2400m_tlv_match(const struct i2400m_tlv_hdr *tlv,
1253a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		     enum i2400m_tlv tlv_type, ssize_t tlv_size)
1263a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
1273a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (le16_to_cpu(tlv->type) != tlv_type)	/* Not our type? skip */
1283a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		return -1;
1293a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (tlv_size != -1
1303a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	    && le16_to_cpu(tlv->length) + sizeof(*tlv) != tlv_size) {
1313a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		size_t size = le16_to_cpu(tlv->length) + sizeof(*tlv);
1323a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		printk(KERN_WARNING "W: tlv type 0x%x mismatched because of "
1333a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		       "size (got %zu vs %zu expected)\n",
1343a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		       tlv_type, size, tlv_size);
1353a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		return size;
1363a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
1373a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	return 0;
1383a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
1393a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
1403a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
1413a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
1423a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Given a buffer of TLVs, iterate over them
1433a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
1443a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device instance
1453a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @tlv_buf: pointer to the beginning of the TLV buffer
1463a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @buf_size: buffer size in bytes
1473a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @tlv_pos: seek position; this is assumed to be a pointer returned
1483a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *           by i2400m_tlv_buffer_walk() [and thus, validated]. The
1493a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *           TLV returned will be the one following this one.
1503a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
1513a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Usage:
1523a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
1533a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * tlv_itr = NULL;
1543a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * while (tlv_itr = i2400m_tlv_buffer_walk(i2400m, buf, size, tlv_itr))  {
1553a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *         ...
1563a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *         // Do stuff with tlv_itr, DON'T MODIFY IT
1573a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *         ...
1583a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * }
1593a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
1603a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezstatic
1613a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezconst struct i2400m_tlv_hdr *i2400m_tlv_buffer_walk(
1623a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct i2400m *i2400m,
1633a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const void *tlv_buf, size_t buf_size,
1643a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_tlv_hdr *tlv_pos)
1653a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
1663a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
1673a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_tlv_hdr *tlv_top = tlv_buf + buf_size;
1683a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	size_t offset, length, avail_size;
1693a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	unsigned type;
1703a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
1713a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (tlv_pos == NULL)	/* Take the first one? */
1723a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		tlv_pos = tlv_buf;
1733a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	else			/* Nope, the next one */
1743a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		tlv_pos = (void *) tlv_pos
1753a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			+ le16_to_cpu(tlv_pos->length) + sizeof(*tlv_pos);
1763a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (tlv_pos == tlv_top) {	/* buffer done */
1773a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		tlv_pos = NULL;
1783a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_beyond_end;
1793a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
1803a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (tlv_pos > tlv_top) {
1813a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		tlv_pos = NULL;
1823a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		WARN_ON(1);
1833a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_beyond_end;
1843a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
1853a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	offset = (void *) tlv_pos - (void *) tlv_buf;
1863a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	avail_size = buf_size - offset;
1873a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (avail_size < sizeof(*tlv_pos)) {
1883a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "HW BUG? tlv_buf %p [%zu bytes], tlv @%zu: "
1893a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			"short header\n", tlv_buf, buf_size, offset);
1903a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_short_header;
1913a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
1923a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	type = le16_to_cpu(tlv_pos->type);
1933a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	length = le16_to_cpu(tlv_pos->length);
1943a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (avail_size < sizeof(*tlv_pos) + length) {
1953a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "HW BUG? tlv_buf %p [%zu bytes], "
1963a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			"tlv type 0x%04x @%zu: "
1973a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			"short data (%zu bytes vs %zu needed)\n",
1983a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			tlv_buf, buf_size, type, offset, avail_size,
1993a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			sizeof(*tlv_pos) + length);
2003a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_short_header;
2013a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
2023a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_short_header:
2033a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_beyond_end:
2043a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	return tlv_pos;
2053a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
2063a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
2073a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
2083a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
2093a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Find a TLV in a buffer of sequential TLVs
2103a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
2113a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
2123a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @tlv_hdr: pointer to the first TLV in the sequence
2133a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @size: size of the buffer in bytes; all TLVs are assumed to fit
2143a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *        fully in the buffer (otherwise we'll complain).
2153a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @tlv_type: type of the TLV we are looking for
2163a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @tlv_size: expected size of the TLV we are looking for (if -1,
2173a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *            don't check the size). This includes the header
2183a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
2193a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Returns: NULL if the TLV is not found, otherwise a pointer to
2203a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *          it. If the sizes don't match, an error is printed and NULL
2213a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *          returned.
2223a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
2233a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezstatic
2243a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezconst struct i2400m_tlv_hdr *i2400m_tlv_find(
2253a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct i2400m *i2400m,
2263a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_tlv_hdr *tlv_hdr, size_t size,
2273a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	enum i2400m_tlv tlv_type, ssize_t tlv_size)
2283a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
2293a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ssize_t match;
2303a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
2313a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_tlv_hdr *tlv = NULL;
2323a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	while ((tlv = i2400m_tlv_buffer_walk(i2400m, tlv_hdr, size, tlv))) {
2333a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		match = i2400m_tlv_match(tlv, tlv_type, tlv_size);
2343a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		if (match == 0)		/* found it :) */
2353a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			break;
2363a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		if (match > 0)
2373a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			dev_warn(dev, "TLV type 0x%04x found with size "
2383a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez				 "mismatch (%zu vs %zu needed)\n",
2393a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez				 tlv_type, match, tlv_size);
2403a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
2413a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	return tlv;
2423a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
2433a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
2443a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
2453a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezstatic const struct
2463a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
2473a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	char *msg;
2483a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	int errno;
2493a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez} ms_to_errno[I2400M_MS_MAX] = {
2503a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_DONE_OK] = { "", 0 },
2513a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_DONE_IN_PROGRESS] = { "", 0 },
2523a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_INVALID_OP] = { "invalid opcode", -ENOSYS },
2533a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_BAD_STATE] = { "invalid state", -EILSEQ },
2543a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_ILLEGAL_VALUE] = { "illegal value", -EINVAL },
2553a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_MISSING_PARAMS] = { "missing parameters", -ENOMSG },
2563a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_VERSION_ERROR] = { "bad version", -EIO },
2573a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_ACCESSIBILITY_ERROR] = { "accesibility error", -EIO },
2583a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_BUSY] = { "busy", -EBUSY },
2593a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_CORRUPTED_TLV] = { "corrupted TLV", -EILSEQ },
2603a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_UNINITIALIZED] = { "not unitialized", -EILSEQ },
2613a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_UNKNOWN_ERROR] = { "unknown error", -EIO },
2623a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_PRODUCTION_ERROR] = { "production error", -EIO },
2633a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_NO_RF] = { "no RF", -EIO },
2643a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_NOT_READY_FOR_POWERSAVE] =
2653a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		{ "not ready for powersave", -EACCES },
2663a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	[I2400M_MS_THERMAL_CRITICAL] = { "thermal critical", -EL3HLT },
2673a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez};
2683a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
2693a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
2703a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
2713a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * i2400m_msg_check_status - translate a message's status code
2723a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
2733a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
2743a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @l3l4_hdr: message header
2753a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @strbuf: buffer to place a formatted error message (unless NULL).
2763a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @strbuf_size: max amount of available space; larger messages will
2773a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * be truncated.
2783a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
2793a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Returns: errno code corresponding to the status code in @l3l4_hdr
2803a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *          and a message in @strbuf describing the error.
2813a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
2823a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezint i2400m_msg_check_status(const struct i2400m_l3l4_hdr *l3l4_hdr,
2833a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			    char *strbuf, size_t strbuf_size)
2843a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
2853a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	int result;
2863a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	enum i2400m_ms status = le16_to_cpu(l3l4_hdr->status);
2873a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const char *str;
2883a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
2893a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (status == 0)
2903a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		return 0;
291a6346fa583766a51b7723288fc5d73ee5ea93479Roel Kluin	if (status >= ARRAY_SIZE(ms_to_errno)) {
2923a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		str = "unknown status code";
2933a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		result = -EBADR;
2943a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	} else {
2953a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		str = ms_to_errno[status].msg;
2963a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		result = ms_to_errno[status].errno;
2973a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
2983a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (strbuf)
2993a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		snprintf(strbuf, strbuf_size, "%s (%d)", str, status);
3003a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	return result;
3013a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
3023a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
3033a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
3043a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
3053a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Act on a TLV System State reported by the device
3063a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
3073a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
3083a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @ss: validated System State TLV
3093a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
3103a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezstatic
3113a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezvoid i2400m_report_tlv_system_state(struct i2400m *i2400m,
3123a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez				    const struct i2400m_tlv_system_state *ss)
3133a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
3143a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
3153a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
3163a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	enum i2400m_system_state i2400m_state = le32_to_cpu(ss->state);
3173a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
3183a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnstart(3, dev, "(i2400m %p ss %p [%u])\n", i2400m, ss, i2400m_state);
3193a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
3203a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (i2400m->state != i2400m_state) {
3213a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		i2400m->state = i2400m_state;
3223a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		wake_up_all(&i2400m->state_wq);
3233a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
3243a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	switch (i2400m_state) {
3253a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_UNINITIALIZED:
3263a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_INIT:
3273a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_CONFIG:
3283a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_PRODUCTION:
3293a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED);
3303a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		break;
3313a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
3323a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_RF_OFF:
3333a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_RF_SHUTDOWN:
3343a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		wimax_state_change(wimax_dev, WIMAX_ST_RADIO_OFF);
3353a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		break;
3363a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
3373a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_READY:
3383a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_STANDBY:
3393a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_SLEEPACTIVE:
3403a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		wimax_state_change(wimax_dev, WIMAX_ST_READY);
3413a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		break;
3423a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
3433a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_CONNECTING:
3443a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_WIMAX_CONNECTED:
3453a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		wimax_state_change(wimax_dev, WIMAX_ST_READY);
3463a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		break;
3473a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
3483a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_SCAN:
3493a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_OUT_OF_ZONE:
3503a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		wimax_state_change(wimax_dev, WIMAX_ST_SCANNING);
3513a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		break;
3523a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
3533a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_IDLE:
3543a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		d_printf(1, dev, "entering BS-negotiated idle mode\n");
3553a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_DISCONNECTING:
3563a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_SS_DATA_PATH_CONNECTED:
3573a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		wimax_state_change(wimax_dev, WIMAX_ST_CONNECTED);
3583a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		break;
3593a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
3603a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	default:
3613a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		/* Huh? just in case, shut it down */
3623a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "HW BUG? unknown state %u: shutting down\n",
3633a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			i2400m_state);
364c931ceeb780560ff652a8f9875f88778439ee87eInaky Perez-Gonzalez		i2400m_reset(i2400m, I2400M_RT_WARM);
3653a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		break;
366ee289b6440c3b0ccb9459495783e8c299bec6604Joe Perches	}
3673a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnend(3, dev, "(i2400m %p ss %p [%u]) = void\n",
3683a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		i2400m, ss, i2400m_state);
3693a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
3703a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
3713a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
3723a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
3733a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Parse and act on a TLV Media Status sent by the device
3743a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
3753a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
3763a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @ms: validated Media Status TLV
3773a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
3783a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * This will set the carrier up on down based on the device's link
3793a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * report. This is done asides of what the WiMAX stack does based on
3803a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * the device's state as sometimes we need to do a link-renew (the BS
3813a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * wants us to renew a DHCP lease, for example).
3823a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
38325985edcedea6396277003854657b5f3cb31a628Lucas De Marchi * In fact, doc says that every time we get a link-up, we should do a
3843a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * DHCP negotiation...
3853a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
3863a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezstatic
3873a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezvoid i2400m_report_tlv_media_status(struct i2400m *i2400m,
3883a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez				    const struct i2400m_tlv_media_status *ms)
3893a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
3903a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
3913a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
3923a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct net_device *net_dev = wimax_dev->net_dev;
3933a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	enum i2400m_media_status status = le32_to_cpu(ms->media_status);
3943a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
3953a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnstart(3, dev, "(i2400m %p ms %p [%u])\n", i2400m, ms, status);
3963a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
3973a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	switch (status) {
3983a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_MEDIA_STATUS_LINK_UP:
3993a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		netif_carrier_on(net_dev);
4003a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		break;
4013a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_MEDIA_STATUS_LINK_DOWN:
4023a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		netif_carrier_off(net_dev);
4033a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		break;
4043a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	/*
4053a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * This is the network telling us we need to retrain the DHCP
4063a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * lease -- so far, we are trusting the WiMAX Network Service
4073a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * in user space to pick this up and poke the DHCP client.
4083a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 */
4093a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_MEDIA_STATUS_LINK_RENEW:
4103a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		netif_carrier_on(net_dev);
4113a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		break;
4123a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	default:
4133a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "HW BUG? unknown media status %u\n",
4143a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			status);
415ee289b6440c3b0ccb9459495783e8c299bec6604Joe Perches	}
4163a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnend(3, dev, "(i2400m %p ms %p [%u]) = void\n",
4173a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		i2400m, ms, status);
4183a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
4193a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
4203a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
4213a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
4228ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez * Process a TLV from a 'state report'
4238ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez *
4248ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez * @i2400m: device descriptor
4258ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez * @tlv: pointer to the TLV header; it has been already validated for
4268ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez *     consistent size.
4278ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez * @tag: for error messages
4288ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez *
4298ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez * Act on the TLVs from a 'state report'.
4308ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez */
4318ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalezstatic
4328ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalezvoid i2400m_report_state_parse_tlv(struct i2400m *i2400m,
4338ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez				   const struct i2400m_tlv_hdr *tlv,
4348ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez				   const char *tag)
4358ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez{
4368ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
4378ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez	const struct i2400m_tlv_media_status *ms;
4388ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez	const struct i2400m_tlv_system_state *ss;
4398ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez	const struct i2400m_tlv_rf_switches_status *rfss;
4408ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez
4418ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez	if (0 == i2400m_tlv_match(tlv, I2400M_TLV_SYSTEM_STATE, sizeof(*ss))) {
4428ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez		ss = container_of(tlv, typeof(*ss), hdr);
4438ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez		d_printf(2, dev, "%s: system state TLV "
4448ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez			 "found (0x%04x), state 0x%08x\n",
4458ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez			 tag, I2400M_TLV_SYSTEM_STATE,
4468ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez			 le32_to_cpu(ss->state));
4478ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez		i2400m_report_tlv_system_state(i2400m, ss);
4488ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez	}
4498ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez	if (0 == i2400m_tlv_match(tlv, I2400M_TLV_RF_STATUS, sizeof(*rfss))) {
4508ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez		rfss = container_of(tlv, typeof(*rfss), hdr);
4518ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez		d_printf(2, dev, "%s: RF status TLV "
4528ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez			 "found (0x%04x), sw 0x%02x hw 0x%02x\n",
4538ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez			 tag, I2400M_TLV_RF_STATUS,
4548ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez			 le32_to_cpu(rfss->sw_rf_switch),
4558ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez			 le32_to_cpu(rfss->hw_rf_switch));
4568ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez		i2400m_report_tlv_rf_switches_status(i2400m, rfss);
4578ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez	}
4588ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez	if (0 == i2400m_tlv_match(tlv, I2400M_TLV_MEDIA_STATUS, sizeof(*ms))) {
4598ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez		ms = container_of(tlv, typeof(*ms), hdr);
4608ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez		d_printf(2, dev, "%s: Media Status TLV: %u\n",
4618ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez			 tag, le32_to_cpu(ms->media_status));
4628ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez		i2400m_report_tlv_media_status(i2400m, ms);
4638ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez	}
4648ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez}
4658ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez
4668ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez
4678ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez/*
4688ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez * Parse a 'state report' and extract information
4693a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
4703a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
4713a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @l3l4_hdr: pointer to message; it has been already validated for
4723a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *            consistent size.
4733a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @size: size of the message (header + payload). The header length
4743a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *        declaration is assumed to be congruent with @size (as in
4753a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *        sizeof(*l3l4_hdr) + l3l4_hdr->length == size)
4763a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
4778ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez * Walk over the TLVs in a report state and act on them.
4783a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
4793a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezstatic
4803a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezvoid i2400m_report_state_hook(struct i2400m *i2400m,
4813a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			      const struct i2400m_l3l4_hdr *l3l4_hdr,
4823a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			      size_t size, const char *tag)
4833a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
4843a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
4853a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_tlv_hdr *tlv;
4863a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	size_t tlv_size = le16_to_cpu(l3l4_hdr->length);
4873a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
4883a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnstart(4, dev, "(i2400m %p, l3l4_hdr %p, size %zu, %s)\n",
4893a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		  i2400m, l3l4_hdr, size, tag);
4903a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	tlv = NULL;
4913a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
4923a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	while ((tlv = i2400m_tlv_buffer_walk(i2400m, &l3l4_hdr->pl,
4938ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez					     tlv_size, tlv)))
4948ac1101f8cd58a62517ba86745bc000d3a21f09bInaky Perez-Gonzalez		i2400m_report_state_parse_tlv(i2400m, tlv, tag);
4953a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnend(4, dev, "(i2400m %p, l3l4_hdr %p, size %zu, %s) = void\n",
4963a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		i2400m, l3l4_hdr, size, tag);
4973a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
4983a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
4993a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
5003a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
5013a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * i2400m_report_hook - (maybe) act on a report
5023a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
5033a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
5043a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @l3l4_hdr: pointer to message; it has been already validated for
5053a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *            consistent size.
5063a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @size: size of the message (header + payload). The header length
5073a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *        declaration is assumed to be congruent with @size (as in
5083a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *        sizeof(*l3l4_hdr) + l3l4_hdr->length == size)
5093a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
5103a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Extract information we might need (like carrien on/off) from a
5113a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * device report.
5123a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
5133a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezvoid i2400m_report_hook(struct i2400m *i2400m,
5143a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			const struct i2400m_l3l4_hdr *l3l4_hdr, size_t size)
5153a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
5163a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
5173a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	unsigned msg_type;
5183a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
5193a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnstart(3, dev, "(i2400m %p l3l4_hdr %p size %zu)\n",
5203a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		  i2400m, l3l4_hdr, size);
5213a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	/* Chew on the message, we might need some information from
5223a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * here */
5233a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	msg_type = le16_to_cpu(l3l4_hdr->type);
5243a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	switch (msg_type) {
5253a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_MT_REPORT_STATE:	/* carrier detection... */
5263a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		i2400m_report_state_hook(i2400m,
5273a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez					 l3l4_hdr, size, "REPORT STATE");
5283a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		break;
5293a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	/* If the device is ready for power save, then ask it to do
5303a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * it. */
5313a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_MT_REPORT_POWERSAVE_READY:	/* zzzzz */
5323a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		if (l3l4_hdr->status == cpu_to_le16(I2400M_MS_DONE_OK)) {
533fb10167478a3a8e29fe122a7bf4c67b5cfc48a1bInaky Perez-Gonzalez			if (i2400m_power_save_disabled)
534fb10167478a3a8e29fe122a7bf4c67b5cfc48a1bInaky Perez-Gonzalez				d_printf(1, dev, "ready for powersave, "
535fb10167478a3a8e29fe122a7bf4c67b5cfc48a1bInaky Perez-Gonzalez					 "not requesting (disabled by module "
536fb10167478a3a8e29fe122a7bf4c67b5cfc48a1bInaky Perez-Gonzalez					 "parameter)\n");
537fb10167478a3a8e29fe122a7bf4c67b5cfc48a1bInaky Perez-Gonzalez			else {
538fb10167478a3a8e29fe122a7bf4c67b5cfc48a1bInaky Perez-Gonzalez				d_printf(1, dev, "ready for powersave, "
539fb10167478a3a8e29fe122a7bf4c67b5cfc48a1bInaky Perez-Gonzalez					 "requesting\n");
540fb10167478a3a8e29fe122a7bf4c67b5cfc48a1bInaky Perez-Gonzalez				i2400m_cmd_enter_powersave(i2400m);
541fb10167478a3a8e29fe122a7bf4c67b5cfc48a1bInaky Perez-Gonzalez			}
5423a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		}
5433a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		break;
544ee289b6440c3b0ccb9459495783e8c299bec6604Joe Perches	}
5453a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnend(3, dev, "(i2400m %p l3l4_hdr %p size %zu) = void\n",
5463a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		i2400m, l3l4_hdr, size);
5473a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
5483a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
5493a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
5503a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
5513a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * i2400m_msg_ack_hook - process cmd/set/get ack for internal status
5523a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
5533a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
5543a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @l3l4_hdr: pointer to message; it has been already validated for
5553a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *            consistent size.
5563a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @size: size of the message
5573a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
5583a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Extract information we might need from acks to commands and act on
5593a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * it. This is akin to i2400m_report_hook(). Note most of this
5603a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * processing should be done in the function that calls the
5613a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * command. This is here for some cases where it can't happen...
5623a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
563e3d32687a624845e97f9717d9d2027b44b8c49a2stephen hemmingerstatic void i2400m_msg_ack_hook(struct i2400m *i2400m,
564e3d32687a624845e97f9717d9d2027b44b8c49a2stephen hemminger				 const struct i2400m_l3l4_hdr *l3l4_hdr,
565e3d32687a624845e97f9717d9d2027b44b8c49a2stephen hemminger				 size_t size)
5663a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
5673a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	int result;
5683a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
5693a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	unsigned ack_type, ack_status;
5703a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	char strerr[32];
5713a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
5723a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	/* Chew on the message, we might need some information from
5733a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * here */
5743a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ack_type = le16_to_cpu(l3l4_hdr->type);
5753a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ack_status = le16_to_cpu(l3l4_hdr->status);
5763a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	switch (ack_type) {
5773a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_MT_CMD_ENTER_POWERSAVE:
5783a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		/* This is just left here for the sake of example, as
5793a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		 * the processing is done somewhere else. */
5803a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		if (0) {
5813a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			result = i2400m_msg_check_status(
5823a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez				l3l4_hdr, strerr, sizeof(strerr));
5833a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			if (result >= 0)
5843a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez				d_printf(1, dev, "ready for power save: %zd\n",
5853a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez					 size);
5863a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		}
5873a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		break;
588ee289b6440c3b0ccb9459495783e8c299bec6604Joe Perches	}
5893a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
5903a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
5913a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
5923a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
5933a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * i2400m_msg_size_check() - verify message size and header are congruent
5943a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
5953a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * It is ok if the total message size is larger than the expected
5963a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * size, as there can be padding.
5973a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
5983a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezint i2400m_msg_size_check(struct i2400m *i2400m,
5993a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			  const struct i2400m_l3l4_hdr *l3l4_hdr,
6003a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			  size_t msg_size)
6013a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
6023a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	int result;
6033a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
6043a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	size_t expected_size;
6053a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnstart(4, dev, "(i2400m %p l3l4_hdr %p msg_size %zu)\n",
6063a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		  i2400m, l3l4_hdr, msg_size);
6073a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (msg_size < sizeof(*l3l4_hdr)) {
6083a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "bad size for message header "
6093a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			"(expected at least %zu, got %zu)\n",
6103a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			(size_t) sizeof(*l3l4_hdr), msg_size);
6113a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		result = -EIO;
6123a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_hdr_size;
6133a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
6143a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	expected_size = le16_to_cpu(l3l4_hdr->length) + sizeof(*l3l4_hdr);
6153a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (msg_size < expected_size) {
6163a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "bad size for message code 0x%04x (expected %zu, "
6173a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			"got %zu)\n", le16_to_cpu(l3l4_hdr->type),
6183a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			expected_size, msg_size);
6193a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		result = -EIO;
6203a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	} else
6213a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		result = 0;
6223a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_hdr_size:
6233a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnend(4, dev,
6243a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		"(i2400m %p l3l4_hdr %p msg_size %zu) = %d\n",
6253a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		i2400m, l3l4_hdr, msg_size, result);
6263a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	return result;
6273a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
6283a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
6293a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
6303a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
6313a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
6323a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Cancel a wait for a command ACK
6333a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6343a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
6353a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @code: [negative] errno code to cancel with (don't use
6363a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *     -EINPROGRESS)
6373a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6383a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * If there is an ack already filled out, free it.
6393a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
6403a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezvoid i2400m_msg_to_dev_cancel_wait(struct i2400m *i2400m, int code)
6413a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
6423a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct sk_buff *ack_skb;
6433a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	unsigned long flags;
6443a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
6453a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	spin_lock_irqsave(&i2400m->rx_lock, flags);
6463a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ack_skb = i2400m->ack_skb;
6473a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (ack_skb && !IS_ERR(ack_skb))
648f4895b8bc83a22a36446c4aee277e1750fcc6a18Inaky Perez-Gonzalez		kfree_skb(ack_skb);
6493a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	i2400m->ack_skb = ERR_PTR(code);
6503a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	spin_unlock_irqrestore(&i2400m->rx_lock, flags);
6513a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
6523a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
6533a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
6543a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/**
6553a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * i2400m_msg_to_dev - Send a control message to the device and get a response
6563a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6573a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
6583a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6593a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @msg_skb: an skb  *
6603a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6613a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @buf: pointer to the buffer containing the message to be sent; it
6623a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *           has to start with a &struct i2400M_l3l4_hdr and then
6633a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *           followed by the payload. Once this function returns, the
6643a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *           buffer can be reused.
6653a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6663a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @buf_len: buffer size
6673a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6683a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Returns:
6693a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6703a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Pointer to skb containing the ack message. You need to check the
6713a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * pointer with IS_ERR(), as it might be an error code. Error codes
6723a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * could happen because:
6733a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6743a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *  - the message wasn't formatted correctly
6753a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *  - couldn't send the message
6763a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *  - failed waiting for a response
6773a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *  - the ack message wasn't formatted correctly
6783a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6793a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * The returned skb has been allocated with wimax_msg_to_user_alloc(),
68025985edcedea6396277003854657b5f3cb31a628Lucas De Marchi * it contains the response in a netlink attribute and is ready to be
6813a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * passed up to user space with wimax_msg_to_user_send(). To access
6823a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * the payload and its length, use wimax_msg_{data,len}() on the skb.
6833a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6843a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * The skb has to be freed with kfree_skb() once done.
6853a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6863a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Description:
6873a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6883a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * This function delivers a message/command to the device and waits
6893a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * for an ack to be received. The format is described in
6903a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * linux/wimax/i2400m.h. In summary, a command/get/set is followed by an
6913a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * ack.
6923a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6933a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * This function will not check the ack status, that's left up to the
6943a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * caller.  Once done with the ack skb, it has to be kfree_skb()ed.
6953a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6963a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * The i2400m handles only one message at the same time, thus we need
6973a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * the mutex to exclude other players.
6983a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
6993a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * We write the message and then wait for an answer to come back. The
7003a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * RX path intercepts control messages and handles them in
7013a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * i2400m_rx_ctl(). Reports (notifications) are (maybe) processed
7023a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * locally and then forwarded (as needed) to user space on the WiMAX
7033a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * stack message pipe. Acks are saved and passed back to us through an
7043a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * skb in i2400m->ack_skb which is ready to be given to generic
7053a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * netlink if need be.
7063a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
7073a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezstruct sk_buff *i2400m_msg_to_dev(struct i2400m *i2400m,
7083a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez				  const void *buf, size_t buf_len)
7093a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
7103a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	int result;
7113a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
7123a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_l3l4_hdr *msg_l3l4_hdr;
7133a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct sk_buff *ack_skb;
7143a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_l3l4_hdr *ack_l3l4_hdr;
7153a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	size_t ack_len;
7163a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	int ack_timeout;
7173a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	unsigned msg_type;
7183a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	unsigned long flags;
7193a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
7203a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnstart(3, dev, "(i2400m %p buf %p len %zu)\n",
7213a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		  i2400m, buf, buf_len);
7223a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
723b4013f91cdda10f3f15530914f3c7f39738b0b50Inaky Perez-Gonzalez	rmb();		/* Make sure we see what i2400m_dev_reset_handle() */
7243a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (i2400m->boot_mode)
7250bcfc5ef016e8217709c65c5a7335e40ceabc99cCindy H Kao		return ERR_PTR(-EL3RST);
7263a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
7273a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	msg_l3l4_hdr = buf;
7283a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	/* Check msg & payload consistency */
7293a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = i2400m_msg_size_check(i2400m, msg_l3l4_hdr, buf_len);
7303a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (result < 0)
7313a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_bad_msg;
7323a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	msg_type = le16_to_cpu(msg_l3l4_hdr->type);
7333a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_printf(1, dev, "CMD/GET/SET 0x%04x %zu bytes\n",
7343a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		 msg_type, buf_len);
7353a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_dump(2, dev, buf, buf_len);
7363a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
7373a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	/* Setup the completion, ack_skb ("we are waiting") and send
7383a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * the message to the device */
7393a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	mutex_lock(&i2400m->msg_mutex);
7403a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	spin_lock_irqsave(&i2400m->rx_lock, flags);
7413a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	i2400m->ack_skb = ERR_PTR(-EINPROGRESS);
7423a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	spin_unlock_irqrestore(&i2400m->rx_lock, flags);
7433a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	init_completion(&i2400m->msg_completion);
7443a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = i2400m_tx(i2400m, buf, buf_len, I2400M_PT_CTRL);
7453a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (result < 0) {
7463a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "can't send message 0x%04x: %d\n",
7473a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			le16_to_cpu(msg_l3l4_hdr->type), result);
7483a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_tx;
7493a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
7503a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
7513a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	/* Some commands take longer to execute because of crypto ops,
7523a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * so we give them some more leeway on timeout */
7533a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	switch (msg_type) {
7543a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_MT_GET_TLS_OPERATION_RESULT:
7553a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	case I2400M_MT_CMD_SEND_EAP_RESPONSE:
7563a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		ack_timeout = 5 * HZ;
7573a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		break;
7583a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	default:
7593a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		ack_timeout = HZ;
760ee289b6440c3b0ccb9459495783e8c299bec6604Joe Perches	}
7613a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
762223beea23810577353c4cc71ce2f44dbba0d4e16Inaky Perez-Gonzalez	if (unlikely(i2400m->trace_msg_from_user))
763223beea23810577353c4cc71ce2f44dbba0d4e16Inaky Perez-Gonzalez		wimax_msg(&i2400m->wimax_dev, "echo", buf, buf_len, GFP_KERNEL);
7643a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	/* The RX path in rx.c will put any response for this message
7653a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * in i2400m->ack_skb and wake us up. If we cancel the wait,
7663a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * we need to change the value of i2400m->ack_skb to something
7673a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * not -EINPROGRESS so RX knows there is no one waiting. */
7683a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = wait_for_completion_interruptible_timeout(
7693a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		&i2400m->msg_completion, ack_timeout);
7703a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (result == 0) {
7713a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "timeout waiting for reply to message 0x%04x\n",
7723a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			msg_type);
7733a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		result = -ETIMEDOUT;
7743a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		i2400m_msg_to_dev_cancel_wait(i2400m, result);
7753a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_wait_for_completion;
7763a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	} else if (result < 0) {
7773a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "error waiting for reply to message 0x%04x: %d\n",
7783a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			msg_type, result);
7793a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		i2400m_msg_to_dev_cancel_wait(i2400m, result);
7803a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_wait_for_completion;
7813a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
7823a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
7833a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	/* Pull out the ack data from i2400m->ack_skb -- see if it is
7843a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * an error and act accordingly */
7853a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	spin_lock_irqsave(&i2400m->rx_lock, flags);
7863a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ack_skb = i2400m->ack_skb;
7873a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (IS_ERR(ack_skb))
7883a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		result = PTR_ERR(ack_skb);
7893a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	else
7903a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		result = 0;
7913a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	i2400m->ack_skb = NULL;
7923a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	spin_unlock_irqrestore(&i2400m->rx_lock, flags);
7933a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (result < 0)
7943a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_ack_status;
7953a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ack_l3l4_hdr = wimax_msg_data_len(ack_skb, &ack_len);
7963a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
7973a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	/* Check the ack and deliver it if it is ok */
798223beea23810577353c4cc71ce2f44dbba0d4e16Inaky Perez-Gonzalez	if (unlikely(i2400m->trace_msg_from_user))
799223beea23810577353c4cc71ce2f44dbba0d4e16Inaky Perez-Gonzalez		wimax_msg(&i2400m->wimax_dev, "echo",
800223beea23810577353c4cc71ce2f44dbba0d4e16Inaky Perez-Gonzalez			  ack_l3l4_hdr, ack_len, GFP_KERNEL);
8013a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = i2400m_msg_size_check(i2400m, ack_l3l4_hdr, ack_len);
8023a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (result < 0) {
8033a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "HW BUG? reply to message 0x%04x: %d\n",
8043a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			msg_type, result);
8053a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_bad_ack_len;
8063a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
8073a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (msg_type != le16_to_cpu(ack_l3l4_hdr->type)) {
8083a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "HW BUG? bad reply 0x%04x to message 0x%04x\n",
8093a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			le16_to_cpu(ack_l3l4_hdr->type), msg_type);
8103a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		result = -EIO;
8113a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_bad_ack_type;
8123a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
8133a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	i2400m_msg_ack_hook(i2400m, ack_l3l4_hdr, ack_len);
8143a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	mutex_unlock(&i2400m->msg_mutex);
8153a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnend(3, dev, "(i2400m %p buf %p len %zu) = %p\n",
8163a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		i2400m, buf, buf_len, ack_skb);
8173a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	return ack_skb;
8183a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
8193a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_bad_ack_type:
8203a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_bad_ack_len:
8213a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	kfree_skb(ack_skb);
8223a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_ack_status:
8233a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_wait_for_completion:
8243a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_tx:
8253a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	mutex_unlock(&i2400m->msg_mutex);
8263a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_bad_msg:
8273a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnend(3, dev, "(i2400m %p buf %p len %zu) = %d\n",
8283a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		i2400m, buf, buf_len, result);
8293a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	return ERR_PTR(result);
8303a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
8313a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
8323a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
8333a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
8343a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Definitions for the Enter Power Save command
8353a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
8363a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * The Enter Power Save command requests the device to go into power
8373a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * saving mode. The device will ack or nak the command depending on it
8383a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * being ready for it. If it acks, we tell the USB subsystem to
8393a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
8403a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * As well, the device might request to go into power saving mode by
8413a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * sending a report (REPORT_POWERSAVE_READY), in which case, we issue
8423a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * this command. The hookups in the RX coder allow
8433a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
8443a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezenum {
8453a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	I2400M_WAKEUP_ENABLED  = 0x01,
8463a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	I2400M_WAKEUP_DISABLED = 0x02,
8473a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	I2400M_TLV_TYPE_WAKEUP_MODE = 144,
8483a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez};
8493a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
8503a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezstruct i2400m_cmd_enter_power_save {
8513a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct i2400m_l3l4_hdr hdr;
8523a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct i2400m_tlv_hdr tlv;
8533a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	__le32 val;
854ba2d3587912f82d1ab4367975b1df460db60fb1eEric Dumazet} __packed;
8553a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
8563a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
8573a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
8583a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Request entering power save
8593a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
8603a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * This command is (mainly) executed when the device indicates that it
8613a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * is ready to go into powersave mode via a REPORT_POWERSAVE_READY.
8623a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
8633a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezint i2400m_cmd_enter_powersave(struct i2400m *i2400m)
8643a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
8653a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	int result;
8663a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
8673a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct sk_buff *ack_skb;
8683a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct i2400m_cmd_enter_power_save *cmd;
8693a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	char strerr[32];
8703a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
8713a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = -ENOMEM;
8723a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
8733a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (cmd == NULL)
8743a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_alloc;
8753a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->hdr.type = cpu_to_le16(I2400M_MT_CMD_ENTER_POWERSAVE);
8763a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->hdr.length = cpu_to_le16(sizeof(*cmd) - sizeof(cmd->hdr));
8773a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->hdr.version = cpu_to_le16(I2400M_L3L4_VERSION);
8783a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->tlv.type = cpu_to_le16(I2400M_TLV_TYPE_WAKEUP_MODE);
8793a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->tlv.length = cpu_to_le16(sizeof(cmd->val));
8803a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->val = cpu_to_le32(I2400M_WAKEUP_ENABLED);
8813a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
8823a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd));
8833a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = PTR_ERR(ack_skb);
8843a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (IS_ERR(ack_skb)) {
8853a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "Failed to issue 'Enter power save' command: %d\n",
8863a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			result);
8873a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_msg_to_dev;
8883a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
8893a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = i2400m_msg_check_status(wimax_msg_data(ack_skb),
8903a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez					 strerr, sizeof(strerr));
8913a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (result == -EACCES)
8923a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		d_printf(1, dev, "Cannot enter power save mode\n");
8933a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	else if (result < 0)
8943a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "'Enter power save' (0x%04x) command failed: "
8953a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			"%d - %s\n", I2400M_MT_CMD_ENTER_POWERSAVE,
8963a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			result, strerr);
8973a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	else
8983a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		d_printf(1, dev, "device ready to power save\n");
8993a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	kfree_skb(ack_skb);
9003a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_msg_to_dev:
9013a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	kfree(cmd);
9023a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_alloc:
9033a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	return result;
9043a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
9053a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-GonzalezEXPORT_SYMBOL_GPL(i2400m_cmd_enter_powersave);
9063a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
9073a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
9083a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
9093a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Definitions for getting device information
9103a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
9113a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezenum {
9123a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	I2400M_TLV_DETAILED_DEVICE_INFO = 140
9133a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez};
9143a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
9153a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/**
9163a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * i2400m_get_device_info - Query the device for detailed device information
9173a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
9183a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
9193a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
9203a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Returns: an skb whose skb->data points to a 'struct
9213a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *    i2400m_tlv_detailed_device_info'. When done, kfree_skb() it. The
9223a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *    skb is *guaranteed* to contain the whole TLV data structure.
9233a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
9243a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *    On error, IS_ERR(skb) is true and ERR_PTR(skb) is the error
9253a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *    code.
9263a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
9273a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezstruct sk_buff *i2400m_get_device_info(struct i2400m *i2400m)
9283a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
9293a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	int result;
9303a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
9313a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct sk_buff *ack_skb;
9323a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct i2400m_l3l4_hdr *cmd;
9333a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_l3l4_hdr *ack;
9343a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	size_t ack_len;
9353a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_tlv_hdr *tlv;
9363a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_tlv_detailed_device_info *ddi;
9373a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	char strerr[32];
9383a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
9393a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ack_skb = ERR_PTR(-ENOMEM);
9403a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
9413a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (cmd == NULL)
9423a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_alloc;
9433a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->type = cpu_to_le16(I2400M_MT_GET_DEVICE_INFO);
9443a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->length = 0;
9453a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->version = cpu_to_le16(I2400M_L3L4_VERSION);
9463a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
9473a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd));
9483a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (IS_ERR(ack_skb)) {
9493a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "Failed to issue 'get device info' command: %ld\n",
9503a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			PTR_ERR(ack_skb));
9513a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_msg_to_dev;
9523a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
9533a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ack = wimax_msg_data_len(ack_skb, &ack_len);
9543a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = i2400m_msg_check_status(ack, strerr, sizeof(strerr));
9553a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (result < 0) {
9563a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "'get device info' (0x%04x) command failed: "
9573a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			"%d - %s\n", I2400M_MT_GET_DEVICE_INFO, result,
9583a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			strerr);
9593a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_cmd_failed;
9603a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
9613a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	tlv = i2400m_tlv_find(i2400m, ack->pl, ack_len - sizeof(*ack),
9623a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			      I2400M_TLV_DETAILED_DEVICE_INFO, sizeof(*ddi));
9633a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (tlv == NULL) {
9643a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "GET DEVICE INFO: "
9653a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			"detailed device info TLV not found (0x%04x)\n",
9663a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			I2400M_TLV_DETAILED_DEVICE_INFO);
9673a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		result = -EIO;
9683a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_no_tlv;
9693a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
9703a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	skb_pull(ack_skb, (void *) tlv - (void *) ack_skb->data);
9713a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_msg_to_dev:
9723a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	kfree(cmd);
9733a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_alloc:
9743a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	return ack_skb;
9753a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
9763a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_no_tlv:
9773a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_cmd_failed:
9783a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	kfree_skb(ack_skb);
9793a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	kfree(cmd);
9803a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	return ERR_PTR(result);
9813a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
9823a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
9833a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
9843a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/* Firmware interface versions we support */
9853a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezenum {
9863a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	I2400M_HDIv_MAJOR = 9,
9873a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	I2400M_HDIv_MINOR = 1,
988efa05d0f0a723642fd0d88bb97b0f31800a3f716Inaky Perez-Gonzalez	I2400M_HDIv_MINOR_2 = 2,
9893a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez};
9903a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
9913a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
9923a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/**
9933a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * i2400m_firmware_check - check firmware versions are compatible with
9943a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * the driver
9953a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
9963a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
9973a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
9983a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Returns: 0 if ok, < 0 errno code an error and a message in the
9993a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *    kernel log.
10003a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
10013a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Long function, but quite simple; first chunk launches the command
10023a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * and double checks the reply for the right TLV. Then we process the
10033a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * TLV (where the meat is).
10046a0f7ab8305cb60a43a6c4a548f57adab784e6cdInaky Perez-Gonzalez *
10056a0f7ab8305cb60a43a6c4a548f57adab784e6cdInaky Perez-Gonzalez * Once we process the TLV that gives us the firmware's interface
10066a0f7ab8305cb60a43a6c4a548f57adab784e6cdInaky Perez-Gonzalez * version, we encode it and save it in i2400m->fw_version for future
10076a0f7ab8305cb60a43a6c4a548f57adab784e6cdInaky Perez-Gonzalez * reference.
10083a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
10093a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezint i2400m_firmware_check(struct i2400m *i2400m)
10103a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
10113a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	int result;
10123a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
10133a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct sk_buff *ack_skb;
10143a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct i2400m_l3l4_hdr *cmd;
10153a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_l3l4_hdr *ack;
10163a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	size_t ack_len;
10173a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_tlv_hdr *tlv;
10183a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_tlv_l4_message_versions *l4mv;
10193a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	char strerr[32];
10203a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	unsigned major, minor, branch;
10213a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
10223a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = -ENOMEM;
10233a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
10243a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (cmd == NULL)
10253a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_alloc;
10263a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->type = cpu_to_le16(I2400M_MT_GET_LM_VERSION);
10273a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->length = 0;
10283a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->version = cpu_to_le16(I2400M_L3L4_VERSION);
10293a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
10303a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd));
10313a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (IS_ERR(ack_skb)) {
10323a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		result = PTR_ERR(ack_skb);
10333a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "Failed to issue 'get lm version' command: %-d\n",
10343a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			result);
10353a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_msg_to_dev;
10363a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
10373a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ack = wimax_msg_data_len(ack_skb, &ack_len);
10383a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = i2400m_msg_check_status(ack, strerr, sizeof(strerr));
10393a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (result < 0) {
10403a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "'get lm version' (0x%04x) command failed: "
10413a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			"%d - %s\n", I2400M_MT_GET_LM_VERSION, result,
10423a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			strerr);
10433a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_cmd_failed;
10443a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
10453a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	tlv = i2400m_tlv_find(i2400m, ack->pl, ack_len - sizeof(*ack),
10463a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			      I2400M_TLV_L4_MESSAGE_VERSIONS, sizeof(*l4mv));
10473a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (tlv == NULL) {
10483a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "get lm version: TLV not found (0x%04x)\n",
10493a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			I2400M_TLV_L4_MESSAGE_VERSIONS);
10503a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		result = -EIO;
10513a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_no_tlv;
10523a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
10533a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	l4mv = container_of(tlv, typeof(*l4mv), hdr);
10543a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	major = le16_to_cpu(l4mv->major);
10553a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	minor = le16_to_cpu(l4mv->minor);
10563a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	branch = le16_to_cpu(l4mv->branch);
10573a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = -EINVAL;
1058efa05d0f0a723642fd0d88bb97b0f31800a3f716Inaky Perez-Gonzalez	if (major != I2400M_HDIv_MAJOR) {
1059efa05d0f0a723642fd0d88bb97b0f31800a3f716Inaky Perez-Gonzalez		dev_err(dev, "unsupported major fw version "
10603a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			"%u.%u.%u\n", major, minor, branch);
10613a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_bad_major;
10623a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
10633a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = 0;
1064efa05d0f0a723642fd0d88bb97b0f31800a3f716Inaky Perez-Gonzalez	if (minor < I2400M_HDIv_MINOR_2 && minor > I2400M_HDIv_MINOR)
1065efa05d0f0a723642fd0d88bb97b0f31800a3f716Inaky Perez-Gonzalez		dev_warn(dev, "untested minor fw version %u.%u.%u\n",
10663a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			 major, minor, branch);
10676a0f7ab8305cb60a43a6c4a548f57adab784e6cdInaky Perez-Gonzalez	/* Yes, we ignore the branch -- we don't have to track it */
10686a0f7ab8305cb60a43a6c4a548f57adab784e6cdInaky Perez-Gonzalez	i2400m->fw_version = major << 16 | minor;
10693a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	dev_info(dev, "firmware interface version %u.%u.%u\n",
10703a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		 major, minor, branch);
10716a0f7ab8305cb60a43a6c4a548f57adab784e6cdInaky Perez-Gonzalezerror_bad_major:
10723a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_no_tlv:
10733a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_cmd_failed:
10743a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	kfree_skb(ack_skb);
10753a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_msg_to_dev:
10763a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	kfree(cmd);
10773a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_alloc:
10783a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	return result;
10793a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
10803a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
10813a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
10823a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
10833a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Send an DoExitIdle command to the device to ask it to go out of
10843a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * basestation-idle mode.
10853a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
10863a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
10873a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
10883a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * This starts a renegotiation with the basestation that might involve
10893a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * another crypto handshake with user space.
10903a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
10913a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Returns: 0 if ok, < 0 errno code on error.
10923a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
10933a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezint i2400m_cmd_exit_idle(struct i2400m *i2400m)
10943a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
10953a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	int result;
10963a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
10973a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct sk_buff *ack_skb;
10983a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct i2400m_l3l4_hdr *cmd;
10993a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	char strerr[32];
11003a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
11013a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = -ENOMEM;
11023a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
11033a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (cmd == NULL)
11043a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_alloc;
11053a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->type = cpu_to_le16(I2400M_MT_CMD_EXIT_IDLE);
11063a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->length = 0;
11073a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->version = cpu_to_le16(I2400M_L3L4_VERSION);
11083a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
11093a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd));
11103a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = PTR_ERR(ack_skb);
11113a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (IS_ERR(ack_skb)) {
11123a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "Failed to issue 'exit idle' command: %d\n",
11133a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			result);
11143a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_msg_to_dev;
11153a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
11163a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = i2400m_msg_check_status(wimax_msg_data(ack_skb),
11173a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez					 strerr, sizeof(strerr));
11183a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	kfree_skb(ack_skb);
11193a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_msg_to_dev:
11203a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	kfree(cmd);
11213a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_alloc:
11223a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	return result;
11233a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
11243a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
11253a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
11263a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
11273a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/*
11283a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Query the device for its state, update the WiMAX stack's idea of it
11293a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
11303a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
11313a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
11323a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Returns: 0 if ok, < 0 errno code on error.
11333a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
11343a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Executes a 'Get State' command and parses the returned
11353a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * TLVs.
11363a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
11373a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Because this is almost identical to a 'Report State', we use
11383a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * i2400m_report_state_hook() to parse the answer. This will set the
11393a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * carrier state, as well as the RF Kill switches state.
11403a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
1141e3d32687a624845e97f9717d9d2027b44b8c49a2stephen hemmingerstatic int i2400m_cmd_get_state(struct i2400m *i2400m)
11423a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
11433a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	int result;
11443a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
11453a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct sk_buff *ack_skb;
11463a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct i2400m_l3l4_hdr *cmd;
11473a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_l3l4_hdr *ack;
11483a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	size_t ack_len;
11493a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	char strerr[32];
11503a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
11513a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = -ENOMEM;
11523a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
11533a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (cmd == NULL)
11543a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_alloc;
11553a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->type = cpu_to_le16(I2400M_MT_GET_STATE);
11563a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->length = 0;
11573a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->version = cpu_to_le16(I2400M_L3L4_VERSION);
11583a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
11593a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd));
11603a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (IS_ERR(ack_skb)) {
11613a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "Failed to issue 'get state' command: %ld\n",
11623a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			PTR_ERR(ack_skb));
11633a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		result = PTR_ERR(ack_skb);
11643a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_msg_to_dev;
11653a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
11663a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ack = wimax_msg_data_len(ack_skb, &ack_len);
11673a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = i2400m_msg_check_status(ack, strerr, sizeof(strerr));
11683a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (result < 0) {
11693a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "'get state' (0x%04x) command failed: "
11703a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			"%d - %s\n", I2400M_MT_GET_STATE, result, strerr);
11713a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_cmd_failed;
11723a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
11733a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	i2400m_report_state_hook(i2400m, ack, ack_len - sizeof(*ack),
11743a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez				 "GET STATE");
11753a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = 0;
11763a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	kfree_skb(ack_skb);
11773a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_cmd_failed:
11783a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_msg_to_dev:
11793a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	kfree(cmd);
11803a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_alloc:
11813a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	return result;
11823a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
11833a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
11843a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/**
11853a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Set basic configuration settings
11863a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
11873a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
11883a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @args: array of pointers to the TLV headers to send for
11893a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *     configuration (each followed by its payload).
11903a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *     TLV headers and payloads must be properly initialized, with the
11913a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *     right endianess (LE).
11923a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @arg_size: number of pointers in the @args array
11933a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
1194e3d32687a624845e97f9717d9d2027b44b8c49a2stephen hemmingerstatic int i2400m_set_init_config(struct i2400m *i2400m,
1195e3d32687a624845e97f9717d9d2027b44b8c49a2stephen hemminger				  const struct i2400m_tlv_hdr **arg,
1196e3d32687a624845e97f9717d9d2027b44b8c49a2stephen hemminger				  size_t args)
11973a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
11983a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	int result;
11993a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
12003a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct sk_buff *ack_skb;
12013a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct i2400m_l3l4_hdr *cmd;
12023a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	char strerr[32];
12033a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	unsigned argc, argsize, tlv_size;
12043a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_tlv_hdr *tlv_hdr;
12053a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	void *buf, *itr;
12063a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
12073a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnstart(3, dev, "(i2400m %p arg %p args %zu)\n", i2400m, arg, args);
12083a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = 0;
12093a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (args == 0)
12103a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto none;
12113a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	/* Compute the size of all the TLVs, so we can alloc a
12123a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * contiguous command block to copy them. */
12133a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	argsize = 0;
12143a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	for (argc = 0; argc < args; argc++) {
12153a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		tlv_hdr = arg[argc];
12163a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		argsize += sizeof(*tlv_hdr) + le16_to_cpu(tlv_hdr->length);
12173a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
12183a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	WARN_ON(argc >= 9);	/* As per hw spec */
12193a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
12203a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	/* Alloc the space for the command and TLVs*/
12213a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = -ENOMEM;
12223a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	buf = kzalloc(sizeof(*cmd) + argsize, GFP_KERNEL);
12233a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (buf == NULL)
12243a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_alloc;
12253a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd = buf;
12263a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->type = cpu_to_le16(I2400M_MT_SET_INIT_CONFIG);
12273a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->length = cpu_to_le16(argsize);
12283a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	cmd->version = cpu_to_le16(I2400M_L3L4_VERSION);
12293a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
12303a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	/* Copy the TLVs */
12313a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	itr = buf + sizeof(*cmd);
12323a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	for (argc = 0; argc < args; argc++) {
12333a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		tlv_hdr = arg[argc];
12343a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		tlv_size = sizeof(*tlv_hdr) + le16_to_cpu(tlv_hdr->length);
12353a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		memcpy(itr, tlv_hdr, tlv_size);
12363a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		itr += tlv_size;
12373a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
12383a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
12393a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	/* Send the message! */
12403a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	ack_skb = i2400m_msg_to_dev(i2400m, buf, sizeof(*cmd) + argsize);
12413a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = PTR_ERR(ack_skb);
12423a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (IS_ERR(ack_skb)) {
12433a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "Failed to issue 'init config' command: %d\n",
12443a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			result);
12453a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
12463a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error_msg_to_dev;
12473a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
12483a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = i2400m_msg_check_status(wimax_msg_data(ack_skb),
12493a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez					 strerr, sizeof(strerr));
12503a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (result < 0)
12513a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		dev_err(dev, "'init config' (0x%04x) command failed: %d - %s\n",
12523a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez			I2400M_MT_SET_INIT_CONFIG, result, strerr);
12533a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	kfree_skb(ack_skb);
12543a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_msg_to_dev:
12553a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	kfree(buf);
12563a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror_alloc:
12573a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzaleznone:
12583a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnend(3, dev, "(i2400m %p arg %p args %zu) = %d\n",
12593a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		i2400m, arg, args, result);
12603a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	return result;
12613a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
12623a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
12633a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
12643a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/**
12658987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez * i2400m_set_idle_timeout - Set the device's idle mode timeout
12668987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez *
12678987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez * @i2400m: i2400m device descriptor
12688987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez *
12698987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez * @msecs: milliseconds for the timeout to enter idle mode. Between
12708987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez *     100 to 300000 (5m); 0 to disable. In increments of 100.
12718987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez *
12728987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez * After this @msecs of the link being idle (no data being sent or
12738987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez * received), the device will negotiate with the basestation entering
12748987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez * idle mode for saving power. The connection is maintained, but
12758987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez * getting out of it (done in tx.c) will require some negotiation,
12768987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez * possible crypto re-handshake and a possible DHCP re-lease.
12778987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez *
12788987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez * Only available if fw_version >= 0x00090002.
12798987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez *
12808987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez * Returns: 0 if ok, < 0 errno code on error.
12818987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez */
12828987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalezint i2400m_set_idle_timeout(struct i2400m *i2400m, unsigned msecs)
12838987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez{
12848987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	int result;
12858987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
12868987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	struct sk_buff *ack_skb;
12878987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	struct {
12888987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez		struct i2400m_l3l4_hdr hdr;
12898987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez		struct i2400m_tlv_config_idle_timeout cit;
12908987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	} *cmd;
12918987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	const struct i2400m_l3l4_hdr *ack;
12928987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	size_t ack_len;
12938987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	char strerr[32];
12948987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez
12958987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	result = -ENOSYS;
12968987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	if (i2400m_le_v1_3(i2400m))
12978987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez		goto error_alloc;
12988987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	result = -ENOMEM;
12998987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
13008987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	if (cmd == NULL)
13018987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez		goto error_alloc;
13028987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	cmd->hdr.type = cpu_to_le16(I2400M_MT_GET_STATE);
13038987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	cmd->hdr.length = cpu_to_le16(sizeof(*cmd) - sizeof(cmd->hdr));
13048987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	cmd->hdr.version = cpu_to_le16(I2400M_L3L4_VERSION);
13058987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez
13068987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	cmd->cit.hdr.type =
13078987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez		cpu_to_le16(I2400M_TLV_CONFIG_IDLE_TIMEOUT);
13088987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	cmd->cit.hdr.length = cpu_to_le16(sizeof(cmd->cit.timeout));
13098987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	cmd->cit.timeout = cpu_to_le32(msecs);
13108987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez
13118987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd));
13128987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	if (IS_ERR(ack_skb)) {
13138987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez		dev_err(dev, "Failed to issue 'set idle timeout' command: "
13148987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez			"%ld\n", PTR_ERR(ack_skb));
13158987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez		result = PTR_ERR(ack_skb);
13168987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez		goto error_msg_to_dev;
13178987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	}
13188987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	ack = wimax_msg_data_len(ack_skb, &ack_len);
13198987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	result = i2400m_msg_check_status(ack, strerr, sizeof(strerr));
13208987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	if (result < 0) {
13218987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez		dev_err(dev, "'set idle timeout' (0x%04x) command failed: "
13228987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez			"%d - %s\n", I2400M_MT_GET_STATE, result, strerr);
13238987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez		goto error_cmd_failed;
13248987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	}
13258987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	result = 0;
13268987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	kfree_skb(ack_skb);
13278987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalezerror_cmd_failed:
13288987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalezerror_msg_to_dev:
13298987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	kfree(cmd);
13308987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalezerror_alloc:
13318987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	return result;
13328987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez}
13338987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez
13348987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez
13358987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez/**
13363a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * i2400m_dev_initialize - Initialize the device once communications are ready
13373a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
13383a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
13393a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
13403a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Returns: 0 if ok, < 0 errno code on error.
13413a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
13423a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * Configures the device to work the way we like it.
13433a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
13443a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * At the point of this call, the device is registered with the WiMAX
13453a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * and netdev stacks, firmware is uploaded and we can talk to the
13463a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * device normally.
13473a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
13483a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezint i2400m_dev_initialize(struct i2400m *i2400m)
13493a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
13503a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	int result;
13513a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
13523a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct i2400m_tlv_config_idle_parameters idle_params;
13538987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	struct i2400m_tlv_config_idle_timeout idle_timeout;
1354fd5c565c0c04d2716cfdac3f1de3c2261d6a457dInaky Perez-Gonzalez	struct i2400m_tlv_config_d2h_data_format df;
1355c747583d19d5d5147a9f0eae480c1fdbc84c4252Inaky Perez-Gonzalez	struct i2400m_tlv_config_dl_host_reorder dlhr;
13563a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	const struct i2400m_tlv_hdr *args[9];
13573a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	unsigned argc = 0;
13583a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
13593a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
136055a662d6468005ec3cd799fbd8d0ad03dfae6d2aInaky Perez-Gonzalez	if (i2400m_passive_mode)
136155a662d6468005ec3cd799fbd8d0ad03dfae6d2aInaky Perez-Gonzalez		goto out_passive;
1362c747583d19d5d5147a9f0eae480c1fdbc84c4252Inaky Perez-Gonzalez	/* Disable idle mode? (enabled by default) */
13633a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (i2400m_idle_mode_disabled) {
13648987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez		if (i2400m_le_v1_3(i2400m)) {
13658987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez			idle_params.hdr.type =
13668987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez				cpu_to_le16(I2400M_TLV_CONFIG_IDLE_PARAMETERS);
13678987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez			idle_params.hdr.length = cpu_to_le16(
13688987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez				sizeof(idle_params) - sizeof(idle_params.hdr));
13698987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez			idle_params.idle_timeout = 0;
13708987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez			idle_params.idle_paging_interval = 0;
13718987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez			args[argc++] = &idle_params.hdr;
13728987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez		} else {
13738987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez			idle_timeout.hdr.type =
13748987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez				cpu_to_le16(I2400M_TLV_CONFIG_IDLE_TIMEOUT);
13758987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez			idle_timeout.hdr.length = cpu_to_le16(
13768987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez				sizeof(idle_timeout) - sizeof(idle_timeout.hdr));
13778987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez			idle_timeout.timeout = 0;
13788987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez			args[argc++] = &idle_timeout.hdr;
13798987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez		}
13803a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	}
1381fd5c565c0c04d2716cfdac3f1de3c2261d6a457dInaky Perez-Gonzalez	if (i2400m_ge_v1_4(i2400m)) {
1382c747583d19d5d5147a9f0eae480c1fdbc84c4252Inaky Perez-Gonzalez		/* Enable extended RX data format? */
1383fd5c565c0c04d2716cfdac3f1de3c2261d6a457dInaky Perez-Gonzalez		df.hdr.type =
1384fd5c565c0c04d2716cfdac3f1de3c2261d6a457dInaky Perez-Gonzalez			cpu_to_le16(I2400M_TLV_CONFIG_D2H_DATA_FORMAT);
1385fd5c565c0c04d2716cfdac3f1de3c2261d6a457dInaky Perez-Gonzalez		df.hdr.length = cpu_to_le16(
1386fd5c565c0c04d2716cfdac3f1de3c2261d6a457dInaky Perez-Gonzalez			sizeof(df) - sizeof(df.hdr));
1387fd5c565c0c04d2716cfdac3f1de3c2261d6a457dInaky Perez-Gonzalez		df.format = 1;
1388fd5c565c0c04d2716cfdac3f1de3c2261d6a457dInaky Perez-Gonzalez		args[argc++] = &df.hdr;
1389c747583d19d5d5147a9f0eae480c1fdbc84c4252Inaky Perez-Gonzalez
1390c747583d19d5d5147a9f0eae480c1fdbc84c4252Inaky Perez-Gonzalez		/* Enable RX data reordering?
1391c747583d19d5d5147a9f0eae480c1fdbc84c4252Inaky Perez-Gonzalez		 * (switch flipped in rx.c:i2400m_rx_setup() after fw upload) */
1392c747583d19d5d5147a9f0eae480c1fdbc84c4252Inaky Perez-Gonzalez		if (i2400m->rx_reorder) {
1393c747583d19d5d5147a9f0eae480c1fdbc84c4252Inaky Perez-Gonzalez			dlhr.hdr.type =
1394c747583d19d5d5147a9f0eae480c1fdbc84c4252Inaky Perez-Gonzalez				cpu_to_le16(I2400M_TLV_CONFIG_DL_HOST_REORDER);
1395c747583d19d5d5147a9f0eae480c1fdbc84c4252Inaky Perez-Gonzalez			dlhr.hdr.length = cpu_to_le16(
1396c747583d19d5d5147a9f0eae480c1fdbc84c4252Inaky Perez-Gonzalez				sizeof(dlhr) - sizeof(dlhr.hdr));
1397c747583d19d5d5147a9f0eae480c1fdbc84c4252Inaky Perez-Gonzalez			dlhr.reorder = 1;
1398c747583d19d5d5147a9f0eae480c1fdbc84c4252Inaky Perez-Gonzalez			args[argc++] = &dlhr.hdr;
1399c747583d19d5d5147a9f0eae480c1fdbc84c4252Inaky Perez-Gonzalez		}
1400fd5c565c0c04d2716cfdac3f1de3c2261d6a457dInaky Perez-Gonzalez	}
14013a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = i2400m_set_init_config(i2400m, args, argc);
14023a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	if (result < 0)
14033a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez		goto error;
140455a662d6468005ec3cd799fbd8d0ad03dfae6d2aInaky Perez-Gonzalezout_passive:
14053a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	/*
14063a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * Update state: Here it just calls a get state; parsing the
14073a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * result (System State TLV and RF Status TLV [done in the rx
14083a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * path hooks]) will set the hardware and software RF-Kill
14093a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 * status.
14103a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	 */
14113a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	result = i2400m_cmd_get_state(i2400m);
14123a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezerror:
14138987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez	if (result < 0)
14148987691a4aa6622a1b58bb12c56abaf3d2098fadInaky Perez-Gonzalez		dev_err(dev, "failed to initialize the device: %d\n", result);
14153a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
14163a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	return result;
14173a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
14183a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
14193a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
14203a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez/**
14213a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * i2400m_dev_shutdown - Shutdown a running device
14223a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
14233a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez * @i2400m: device descriptor
14243a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez *
1425b4bd07e3b13e3c848c7678c4fc870cca1d22ed4eInaky Perez-Gonzalez * Release resources acquired during the running of the device; in
1426b4bd07e3b13e3c848c7678c4fc870cca1d22ed4eInaky Perez-Gonzalez * theory, should also tell the device to go to sleep, switch off the
1427b4bd07e3b13e3c848c7678c4fc870cca1d22ed4eInaky Perez-Gonzalez * radio, all that, but at this point, in most cases (driver
1428b4bd07e3b13e3c848c7678c4fc870cca1d22ed4eInaky Perez-Gonzalez * disconnection, reset handling) we can't even talk to the device.
14293a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez */
14303a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalezvoid i2400m_dev_shutdown(struct i2400m *i2400m)
14313a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez{
14323a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	struct device *dev = i2400m_dev(i2400m);
14333a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez
14343a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez	d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
1435b4bd07e3b13e3c848c7678c4fc870cca1d22ed4eInaky Perez-Gonzalez	d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
14363a35a1d0bdf7cc32cddc234b956605e6d4db4673Inaky Perez-Gonzalez}
1437