static void __download_offload_pseudocode()

in host/vub300.c [1199:1349]


static void __download_offload_pseudocode(struct vub300_mmc_host *vub300,
					  const struct firmware *fw)
{
	u8 register_count = 0;
	u16 ts = 0;
	u16 interrupt_size = 0;
	const u8 *data = fw->data;
	int size = fw->size;
	u8 c;
	dev_info(&vub300->udev->dev, "using %s for SDIO offload processing\n",
		 vub300->vub_name);
	do {
		c = *data++;
	} while (size-- && c); /* skip comment */
	dev_info(&vub300->udev->dev, "using offload firmware %s %s\n", fw->data,
		 vub300->vub_name);
	if (size < 4) {
		dev_err(&vub300->udev->dev,
			"corrupt offload pseudocode in firmware %s\n",
			vub300->vub_name);
		strncpy(vub300->vub_name, "corrupt offload pseudocode",
			sizeof(vub300->vub_name));
		return;
	}
	interrupt_size += *data++;
	size -= 1;
	interrupt_size <<= 8;
	interrupt_size += *data++;
	size -= 1;
	if (interrupt_size < size) {
		u16 xfer_length = roundup_to_multiple_of_64(interrupt_size);
		u8 *xfer_buffer = kmalloc(xfer_length, GFP_KERNEL);
		if (xfer_buffer) {
			int retval;
			memcpy(xfer_buffer, data, interrupt_size);
			memset(xfer_buffer + interrupt_size, 0,
			       xfer_length - interrupt_size);
			size -= interrupt_size;
			data += interrupt_size;
			retval =
				usb_control_msg(vub300->udev,
						usb_sndctrlpipe(vub300->udev, 0),
						SET_INTERRUPT_PSEUDOCODE,
						USB_DIR_OUT | USB_TYPE_VENDOR |
						USB_RECIP_DEVICE, 0x0000, 0x0000,
						xfer_buffer, xfer_length, 1000);
			kfree(xfer_buffer);
			if (retval < 0)
				goto copy_error_message;
		} else {
			dev_err(&vub300->udev->dev,
				"not enough memory for xfer buffer to send"
				" INTERRUPT_PSEUDOCODE for %s %s\n", fw->data,
				vub300->vub_name);
			strncpy(vub300->vub_name,
				"SDIO interrupt pseudocode download failed",
				sizeof(vub300->vub_name));
			return;
		}
	} else {
		dev_err(&vub300->udev->dev,
			"corrupt interrupt pseudocode in firmware %s %s\n",
			fw->data, vub300->vub_name);
		strncpy(vub300->vub_name, "corrupt interrupt pseudocode",
			sizeof(vub300->vub_name));
		return;
	}
	ts += *data++;
	size -= 1;
	ts <<= 8;
	ts += *data++;
	size -= 1;
	if (ts < size) {
		u16 xfer_length = roundup_to_multiple_of_64(ts);
		u8 *xfer_buffer = kmalloc(xfer_length, GFP_KERNEL);
		if (xfer_buffer) {
			int retval;
			memcpy(xfer_buffer, data, ts);
			memset(xfer_buffer + ts, 0,
			       xfer_length - ts);
			size -= ts;
			data += ts;
			retval =
				usb_control_msg(vub300->udev,
						usb_sndctrlpipe(vub300->udev, 0),
						SET_TRANSFER_PSEUDOCODE,
						USB_DIR_OUT | USB_TYPE_VENDOR |
						USB_RECIP_DEVICE, 0x0000, 0x0000,
						xfer_buffer, xfer_length, 1000);
			kfree(xfer_buffer);
			if (retval < 0)
				goto copy_error_message;
		} else {
			dev_err(&vub300->udev->dev,
				"not enough memory for xfer buffer to send"
				" TRANSFER_PSEUDOCODE for %s %s\n", fw->data,
				vub300->vub_name);
			strncpy(vub300->vub_name,
				"SDIO transfer pseudocode download failed",
				sizeof(vub300->vub_name));
			return;
		}
	} else {
		dev_err(&vub300->udev->dev,
			"corrupt transfer pseudocode in firmware %s %s\n",
			fw->data, vub300->vub_name);
		strncpy(vub300->vub_name, "corrupt transfer pseudocode",
			sizeof(vub300->vub_name));
		return;
	}
	register_count += *data++;
	size -= 1;
	if (register_count * 4 == size) {
		int I = vub300->dynamic_register_count = register_count;
		int i = 0;
		while (I--) {
			unsigned int func_num = 0;
			vub300->sdio_register[i].func_num = *data++;
			size -= 1;
			func_num += *data++;
			size -= 1;
			func_num <<= 8;
			func_num += *data++;
			size -= 1;
			func_num <<= 8;
			func_num += *data++;
			size -= 1;
			vub300->sdio_register[i].sdio_reg = func_num;
			vub300->sdio_register[i].activate = 1;
			vub300->sdio_register[i].prepared = 0;
			i += 1;
		}
		dev_info(&vub300->udev->dev,
			 "initialized %d dynamic pseudocode registers\n",
			 vub300->dynamic_register_count);
		return;
	} else {
		dev_err(&vub300->udev->dev,
			"corrupt dynamic registers in firmware %s\n",
			vub300->vub_name);
		strncpy(vub300->vub_name, "corrupt dynamic registers",
			sizeof(vub300->vub_name));
		return;
	}

	return;

copy_error_message:
	strncpy(vub300->vub_name, "SDIO pseudocode download failed",
		sizeof(vub300->vub_name));
}