/*
 * i2400m_report_hook - (maybe) act on a report
 *
 * @i2400m: device descriptor
 * @l3l4_hdr: pointer to message; it has been already validated for
 *            consistent size.
 * @size: size of the message (header + payload). The header length
 *        declaration is assumed to be congruent with @size (as in
 *        sizeof(*l3l4_hdr) + l3l4_hdr->length == size)
 *
 * Extract information we might need (like carrien on/off) from a
 * device report.
 */
void i2400m_report_hook(struct i2400m *i2400m,
			const struct i2400m_l3l4_hdr *l3l4_hdr, size_t size)
{
	struct device *dev = i2400m_dev(i2400m);
	unsigned msg_type;

	d_fnstart(3, dev, "(i2400m %p l3l4_hdr %p size %zu)\n",
		  i2400m, l3l4_hdr, size);
	/* Chew on the message, we might need some information from
	 * here */
	msg_type = le16_to_cpu(l3l4_hdr->type);
	switch (msg_type) {
	case I2400M_MT_REPORT_STATE:	/* carrier detection... */
		i2400m_report_state_hook(i2400m,
					 l3l4_hdr, size, "REPORT STATE");
		break;
	/* If the device is ready for power save, then ask it to do
	 * it. */
	case I2400M_MT_REPORT_POWERSAVE_READY:	/* zzzzz */
		if (l3l4_hdr->status == cpu_to_le16(I2400M_MS_DONE_OK)) {
			if (i2400m_power_save_disabled)
				d_printf(1, dev, "ready for powersave, "
					 "not requesting (disabled by module "
					 "parameter)\n");
			else {
				d_printf(1, dev, "ready for powersave, "
					 "requesting\n");
				i2400m_cmd_enter_powersave(i2400m);
			}
		}
		break;
	}
	d_fnend(3, dev, "(i2400m %p l3l4_hdr %p size %zu) = void\n",
		i2400m, l3l4_hdr, size);
}
void i2400m_report_hook(struct i2400m *i2400m,
			const struct i2400m_l3l4_hdr *l3l4_hdr, size_t size)
{
	struct device *dev = i2400m_dev(i2400m);
	unsigned msg_type;

	d_fnstart(3, dev, "(i2400m %p l3l4_hdr %p size %zu)\n",
		  i2400m, l3l4_hdr, size);
	msg_type = le16_to_cpu(l3l4_hdr->type);
	switch (msg_type) {
	case I2400M_MT_REPORT_STATE:	
		i2400m_report_state_hook(i2400m,
					 l3l4_hdr, size, "REPORT STATE");
		break;
	case I2400M_MT_REPORT_POWERSAVE_READY:	
		if (l3l4_hdr->status == cpu_to_le16(I2400M_MS_DONE_OK)) {
			if (i2400m_power_save_disabled)
				d_printf(1, dev, "ready for powersave, "
					 "not requesting (disabled by module "
					 "parameter)\n");
			else {
				d_printf(1, dev, "ready for powersave, "
					 "requesting\n");
				i2400m_cmd_enter_powersave(i2400m);
			}
		}
		break;
	}
	d_fnend(3, dev, "(i2400m %p l3l4_hdr %p size %zu) = void\n",
		i2400m, l3l4_hdr, size);
}
/*
 * Query the device for its state, update the WiMAX stack's idea of it
 *
 * @i2400m: device descriptor
 *
 * Returns: 0 if ok, < 0 errno code on error.
 *
 * Executes a 'Get State' command and parses the returned
 * TLVs.
 *
 * Because this is almost identical to a 'Report State', we use
 * i2400m_report_state_hook() to parse the answer. This will set the
 * carrier state, as well as the RF Kill switches state.
 */
static int i2400m_cmd_get_state(struct i2400m *i2400m)
{
	int result;
	struct device *dev = i2400m_dev(i2400m);
	struct sk_buff *ack_skb;
	struct i2400m_l3l4_hdr *cmd;
	const struct i2400m_l3l4_hdr *ack;
	size_t ack_len;
	char strerr[32];

	result = -ENOMEM;
	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
	if (cmd == NULL)
		goto error_alloc;
	cmd->type = cpu_to_le16(I2400M_MT_GET_STATE);
	cmd->length = 0;
	cmd->version = cpu_to_le16(I2400M_L3L4_VERSION);

	ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd));
	if (IS_ERR(ack_skb)) {
		dev_err(dev, "Failed to issue 'get state' command: %ld\n",
			PTR_ERR(ack_skb));
		result = PTR_ERR(ack_skb);
		goto error_msg_to_dev;
	}
	ack = wimax_msg_data_len(ack_skb, &ack_len);
	result = i2400m_msg_check_status(ack, strerr, sizeof(strerr));
	if (result < 0) {
		dev_err(dev, "'get state' (0x%04x) command failed: "
			"%d - %s\n", I2400M_MT_GET_STATE, result, strerr);
		goto error_cmd_failed;
	}
	i2400m_report_state_hook(i2400m, ack, ack_len - sizeof(*ack),
				 "GET STATE");
	result = 0;
	kfree_skb(ack_skb);
error_cmd_failed:
error_msg_to_dev:
	kfree(cmd);
error_alloc:
	return result;
}