Beispiel #1
0
/*
 * Process a received notification
 *
 * In normal operation mode, we can only receive two types of payloads
 * on the notification endpoint:
 *
 *   - a reboot barker, we do a bootstrap (the device has reseted).
 *
 *   - a block of zeroes: there is pending data in the IN endpoint
 */
static
int i2400mu_notification_grok(struct i2400mu *i2400mu, const void *buf,
				 size_t buf_len)
{
	int ret;
	struct device *dev = &i2400mu->usb_iface->dev;
	struct i2400m *i2400m = &i2400mu->i2400m;

	d_fnstart(4, dev, "(i2400m %p buf %p buf_len %zu)\n",
		  i2400mu, buf, buf_len);
	ret = -EIO;
	if (buf_len < sizeof(i2400m_ZERO_BARKER))
		/* Not a bug, just ignore */
		goto error_bad_size;
	ret = 0;
	if (!memcmp(i2400m_ZERO_BARKER, buf, sizeof(i2400m_ZERO_BARKER))) {
		i2400mu_rx_kick(i2400mu);
		goto out;
	}
	ret = i2400m_is_boot_barker(i2400m, buf, buf_len);
	if (unlikely(ret >= 0))
		ret = i2400m_dev_reset_handle(i2400m, "device rebooted");
	else	/* Unknown or unexpected data in the notif message */
		i2400m_unknown_barker(i2400m, buf, buf_len);
error_bad_size:
out:
	d_fnend(4, dev, "(i2400m %p buf %p buf_len %zu) = %d\n",
		i2400mu, buf, buf_len, ret);
	return ret;
}
Beispiel #2
0
Datei: fw.c Projekt: 020gzh/linux
/*
 * Verify the ack data received
 *
 * Given a reply to a boot mode command, chew it and verify everything
 * is ok.
 *
 * @opcode: opcode which generated this ack. For error messages.
 * @ack: pointer to ack data we received
 * @ack_size: size of that data buffer
 * @flags: I2400M_BM_CMD_* flags we called the command with.
 *
 * Way too long function -- maybe it should be further split
 */
static
ssize_t __i2400m_bm_ack_verify(struct i2400m *i2400m, int opcode,
			       struct i2400m_bootrom_header *ack,
			       size_t ack_size, int flags)
{
	ssize_t result = -ENOMEM;
	struct device *dev = i2400m_dev(i2400m);

	d_fnstart(8, dev, "(i2400m %p opcode %d ack %p size %zu)\n",
		  i2400m, opcode, ack, ack_size);
	if (ack_size < sizeof(*ack)) {
		result = -EIO;
		dev_err(dev, "boot-mode cmd %d: HW BUG? notification didn't "
			"return enough data (%zu bytes vs %zu expected)\n",
			opcode, ack_size, sizeof(*ack));
		goto error_ack_short;
	}
	result = i2400m_is_boot_barker(i2400m, ack, ack_size);
	if (result >= 0) {
		result = -ERESTARTSYS;
		d_printf(6, dev, "boot-mode cmd %d: HW boot barker\n", opcode);
		goto error_reboot;
	}
	if (ack_size == sizeof(i2400m_ACK_BARKER)
		 && memcmp(ack, i2400m_ACK_BARKER, sizeof(*ack)) == 0) {
		result = -EISCONN;
		d_printf(3, dev, "boot-mode cmd %d: HW reboot ack barker\n",
			 opcode);
		goto error_reboot_ack;
	}
	result = 0;
	if (flags & I2400M_BM_CMD_RAW)
		goto out_raw;
	ack->data_size = le32_to_cpu(ack->data_size);
	ack->target_addr = le32_to_cpu(ack->target_addr);
	ack->block_checksum = le32_to_cpu(ack->block_checksum);
	d_printf(5, dev, "boot-mode cmd %d: notification for opcode %u "
		 "response %u csum %u rr %u da %u\n",
		 opcode, i2400m_brh_get_opcode(ack),
		 i2400m_brh_get_response(ack),
		 i2400m_brh_get_use_checksum(ack),
		 i2400m_brh_get_response_required(ack),
		 i2400m_brh_get_direct_access(ack));
	result = -EIO;
	if (i2400m_brh_get_signature(ack) != 0xcbbc) {
		dev_err(dev, "boot-mode cmd %d: HW BUG? wrong signature "
			"0x%04x\n", opcode, i2400m_brh_get_signature(ack));
		goto error_ack_signature;
	}
	if (opcode != -1 && opcode != i2400m_brh_get_opcode(ack)) {
		dev_err(dev, "boot-mode cmd %d: HW BUG? "
			"received response for opcode %u, expected %u\n",
			opcode, i2400m_brh_get_opcode(ack), opcode);
		goto error_ack_opcode;
	}
	if (i2400m_brh_get_response(ack) != 0) {	/* failed? */
		dev_err(dev, "boot-mode cmd %d: error; hw response %u\n",
			opcode, i2400m_brh_get_response(ack));
		goto error_ack_failed;
	}
	if (ack_size < ack->data_size + sizeof(*ack)) {
		dev_err(dev, "boot-mode cmd %d: SW BUG "
			"driver provided only %zu bytes for %zu bytes "
			"of data\n", opcode, ack_size,
			(size_t) le32_to_cpu(ack->data_size) + sizeof(*ack));
		goto error_ack_short_buffer;
	}
	result = ack_size;
	/* Don't you love this stack of empty targets? Well, I don't
	 * either, but it helps track exactly who comes in here and
	 * why :) */
error_ack_short_buffer:
error_ack_failed:
error_ack_opcode:
error_ack_signature:
out_raw:
error_reboot_ack:
error_reboot:
error_ack_short:
	d_fnend(8, dev, "(i2400m %p opcode %d ack %p size %zu) = %d\n",
		i2400m, opcode, ack, ack_size, (int) result);
	return result;
}
static
void i2400ms_rx(struct i2400ms *i2400ms)
{
	int ret;
	struct sdio_func *func = i2400ms->func;
	struct device *dev = &func->dev;
	struct i2400m *i2400m = &i2400ms->i2400m;
	struct sk_buff *skb;
	ssize_t rx_size;

	d_fnstart(7, dev, "(i2400ms %p)\n", i2400ms);
	rx_size = __i2400ms_rx_get_size(i2400ms);
	if (rx_size < 0) {
		ret = rx_size;
		goto error_get_size;
	}
	sdio_writeb(func, 1, I2400MS_INTR_CLEAR_ADDR, &ret);

	ret = -ENOMEM;
	skb = alloc_skb(rx_size, GFP_ATOMIC);
	if (NULL == skb) {
		dev_err(dev, "RX: unable to alloc skb\n");
		goto error_alloc_skb;
	}
	ret = sdio_memcpy_fromio(func, skb->data,
				 I2400MS_DATA_ADDR, rx_size);
	if (ret < 0) {
		dev_err(dev, "RX: SDIO data read failed: %d\n", ret);
		goto error_memcpy_fromio;
	}

	rmb();	
	if (unlikely(i2400m->boot_mode == 1)) {
		spin_lock(&i2400m->rx_lock);
		i2400ms->bm_ack_size = rx_size;
		spin_unlock(&i2400m->rx_lock);
		memcpy(i2400m->bm_ack_buf, skb->data, rx_size);
		wake_up(&i2400ms->bm_wfa_wq);
		d_printf(5, dev, "RX: SDIO boot mode message\n");
		kfree_skb(skb);
		goto out;
	}
	ret = -EIO;
	if (unlikely(rx_size < sizeof(__le32))) {
		dev_err(dev, "HW BUG? only %zu bytes received\n", rx_size);
		goto error_bad_size;
	}
	if (likely(i2400m_is_d2h_barker(skb->data))) {
		skb_put(skb, rx_size);
		i2400m_rx(i2400m, skb);
	} else if (unlikely(i2400m_is_boot_barker(i2400m,
						  skb->data, rx_size))) {
		ret = i2400m_dev_reset_handle(i2400m, "device rebooted");
		dev_err(dev, "RX: SDIO reboot barker\n");
		kfree_skb(skb);
	} else {
		i2400m_unknown_barker(i2400m, skb->data, rx_size);
		kfree_skb(skb);
	}
out:
	d_fnend(7, dev, "(i2400ms %p) = void\n", i2400ms);
	return;

error_memcpy_fromio:
	kfree_skb(skb);
error_alloc_skb:
error_get_size:
error_bad_size:
	d_fnend(7, dev, "(i2400ms %p) = %d\n", i2400ms, ret);
}