18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* ZD1211 USB-WLAN driver for Linux
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
58c2ecf20Sopenharmony_ci * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
68c2ecf20Sopenharmony_ci * Copyright (C) 2006-2007 Michael Wu <flamingice@sourmilk.net>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/firmware.h>
128c2ecf20Sopenharmony_ci#include <linux/device.h>
138c2ecf20Sopenharmony_ci#include <linux/errno.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
168c2ecf20Sopenharmony_ci#include <linux/usb.h>
178c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <net/mac80211.h>
208c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "zd_def.h"
238c2ecf20Sopenharmony_ci#include "zd_mac.h"
248c2ecf20Sopenharmony_ci#include "zd_usb.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic const struct usb_device_id usb_ids[] = {
278c2ecf20Sopenharmony_ci	/* ZD1211 */
288c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0105, 0x145f), .driver_info = DEVICE_ZD1211 },
298c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0586, 0x3401), .driver_info = DEVICE_ZD1211 },
308c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0586, 0x3402), .driver_info = DEVICE_ZD1211 },
318c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0586, 0x3407), .driver_info = DEVICE_ZD1211 },
328c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0586, 0x3409), .driver_info = DEVICE_ZD1211 },
338c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x079b, 0x004a), .driver_info = DEVICE_ZD1211 },
348c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211 },
358c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0ace, 0x1211), .driver_info = DEVICE_ZD1211 },
368c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0ace, 0xa211), .driver_info = DEVICE_ZD1211 },
378c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0b05, 0x170c), .driver_info = DEVICE_ZD1211 },
388c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0b3b, 0x1630), .driver_info = DEVICE_ZD1211 },
398c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0b3b, 0x5630), .driver_info = DEVICE_ZD1211 },
408c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 },
418c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0df6, 0x9075), .driver_info = DEVICE_ZD1211 },
428c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 },
438c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x129b, 0x1666), .driver_info = DEVICE_ZD1211 },
448c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x13b1, 0x001e), .driver_info = DEVICE_ZD1211 },
458c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x1435, 0x0711), .driver_info = DEVICE_ZD1211 },
468c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x14ea, 0xab10), .driver_info = DEVICE_ZD1211 },
478c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x14ea, 0xab13), .driver_info = DEVICE_ZD1211 },
488c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x157e, 0x300a), .driver_info = DEVICE_ZD1211 },
498c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 },
508c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x157e, 0x3204), .driver_info = DEVICE_ZD1211 },
518c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x157e, 0x3207), .driver_info = DEVICE_ZD1211 },
528c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x1740, 0x2000), .driver_info = DEVICE_ZD1211 },
538c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 },
548c2ecf20Sopenharmony_ci	/* ZD1211B */
558c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0053, 0x5301), .driver_info = DEVICE_ZD1211B },
568c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0409, 0x0248), .driver_info = DEVICE_ZD1211B },
578c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0411, 0x00da), .driver_info = DEVICE_ZD1211B },
588c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B },
598c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0471, 0x1237), .driver_info = DEVICE_ZD1211B },
608c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x050d, 0x705c), .driver_info = DEVICE_ZD1211B },
618c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x054c, 0x0257), .driver_info = DEVICE_ZD1211B },
628c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0586, 0x340a), .driver_info = DEVICE_ZD1211B },
638c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0586, 0x340f), .driver_info = DEVICE_ZD1211B },
648c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0586, 0x3410), .driver_info = DEVICE_ZD1211B },
658c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0586, 0x3412), .driver_info = DEVICE_ZD1211B },
668c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0586, 0x3413), .driver_info = DEVICE_ZD1211B },
678c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x079b, 0x0062), .driver_info = DEVICE_ZD1211B },
688c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211B },
698c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x07fa, 0x1196), .driver_info = DEVICE_ZD1211B },
708c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x083a, 0x4505), .driver_info = DEVICE_ZD1211B },
718c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x083a, 0xe501), .driver_info = DEVICE_ZD1211B },
728c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x083a, 0xe503), .driver_info = DEVICE_ZD1211B },
738c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x083a, 0xe506), .driver_info = DEVICE_ZD1211B },
748c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B },
758c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0ace, 0xb215), .driver_info = DEVICE_ZD1211B },
768c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0b05, 0x171b), .driver_info = DEVICE_ZD1211B },
778c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0baf, 0x0121), .driver_info = DEVICE_ZD1211B },
788c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0cde, 0x001a), .driver_info = DEVICE_ZD1211B },
798c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0df6, 0x0036), .driver_info = DEVICE_ZD1211B },
808c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x129b, 0x1667), .driver_info = DEVICE_ZD1211B },
818c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x13b1, 0x0024), .driver_info = DEVICE_ZD1211B },
828c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B },
838c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x1582, 0x6003), .driver_info = DEVICE_ZD1211B },
848c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x2019, 0x5303), .driver_info = DEVICE_ZD1211B },
858c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x2019, 0xed01), .driver_info = DEVICE_ZD1211B },
868c2ecf20Sopenharmony_ci	/* "Driverless" devices that need ejecting */
878c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER },
888c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x0ace, 0x20ff), .driver_info = DEVICE_INSTALLER },
898c2ecf20Sopenharmony_ci	{}
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
938c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("USB driver for devices with the ZD1211 chip.");
948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ulrich Kunitz");
958c2ecf20Sopenharmony_ciMODULE_AUTHOR("Daniel Drake");
968c2ecf20Sopenharmony_ciMODULE_VERSION("1.0");
978c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, usb_ids);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci#define FW_ZD1211_PREFIX	"zd1211/zd1211_"
1008c2ecf20Sopenharmony_ci#define FW_ZD1211B_PREFIX	"zd1211/zd1211b_"
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic bool check_read_regs(struct zd_usb *usb, struct usb_req_read_regs *req,
1038c2ecf20Sopenharmony_ci			    unsigned int count);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/* USB device initialization */
1068c2ecf20Sopenharmony_cistatic void int_urb_complete(struct urb *urb);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic int request_fw_file(
1098c2ecf20Sopenharmony_ci	const struct firmware **fw, const char *name, struct device *device)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	int r;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	dev_dbg_f(device, "fw name %s\n", name);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	r = request_firmware(fw, name, device);
1168c2ecf20Sopenharmony_ci	if (r)
1178c2ecf20Sopenharmony_ci		dev_err(device,
1188c2ecf20Sopenharmony_ci		       "Could not load firmware file %s. Error number %d\n",
1198c2ecf20Sopenharmony_ci		       name, r);
1208c2ecf20Sopenharmony_ci	return r;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic inline u16 get_bcdDevice(const struct usb_device *udev)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	return le16_to_cpu(udev->descriptor.bcdDevice);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cienum upload_code_flags {
1298c2ecf20Sopenharmony_ci	REBOOT = 1,
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/* Ensures that MAX_TRANSFER_SIZE is even. */
1338c2ecf20Sopenharmony_ci#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1)
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int upload_code(struct usb_device *udev,
1368c2ecf20Sopenharmony_ci	const u8 *data, size_t size, u16 code_offset, int flags)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	u8 *p;
1398c2ecf20Sopenharmony_ci	int r;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* USB request blocks need "kmalloced" buffers.
1428c2ecf20Sopenharmony_ci	 */
1438c2ecf20Sopenharmony_ci	p = kmalloc(MAX_TRANSFER_SIZE, GFP_KERNEL);
1448c2ecf20Sopenharmony_ci	if (!p) {
1458c2ecf20Sopenharmony_ci		r = -ENOMEM;
1468c2ecf20Sopenharmony_ci		goto error;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	size &= ~1;
1508c2ecf20Sopenharmony_ci	while (size > 0) {
1518c2ecf20Sopenharmony_ci		size_t transfer_size = size <= MAX_TRANSFER_SIZE ?
1528c2ecf20Sopenharmony_ci			size : MAX_TRANSFER_SIZE;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		dev_dbg_f(&udev->dev, "transfer size %zu\n", transfer_size);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		memcpy(p, data, transfer_size);
1578c2ecf20Sopenharmony_ci		r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
1588c2ecf20Sopenharmony_ci			USB_REQ_FIRMWARE_DOWNLOAD,
1598c2ecf20Sopenharmony_ci			USB_DIR_OUT | USB_TYPE_VENDOR,
1608c2ecf20Sopenharmony_ci			code_offset, 0, p, transfer_size, 1000 /* ms */);
1618c2ecf20Sopenharmony_ci		if (r < 0) {
1628c2ecf20Sopenharmony_ci			dev_err(&udev->dev,
1638c2ecf20Sopenharmony_ci			       "USB control request for firmware upload"
1648c2ecf20Sopenharmony_ci			       " failed. Error number %d\n", r);
1658c2ecf20Sopenharmony_ci			goto error;
1668c2ecf20Sopenharmony_ci		}
1678c2ecf20Sopenharmony_ci		transfer_size = r & ~1;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		size -= transfer_size;
1708c2ecf20Sopenharmony_ci		data += transfer_size;
1718c2ecf20Sopenharmony_ci		code_offset += transfer_size/sizeof(u16);
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (flags & REBOOT) {
1758c2ecf20Sopenharmony_ci		u8 ret;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		/* Use "DMA-aware" buffer. */
1788c2ecf20Sopenharmony_ci		r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
1798c2ecf20Sopenharmony_ci			USB_REQ_FIRMWARE_CONFIRM,
1808c2ecf20Sopenharmony_ci			USB_DIR_IN | USB_TYPE_VENDOR,
1818c2ecf20Sopenharmony_ci			0, 0, p, sizeof(ret), 5000 /* ms */);
1828c2ecf20Sopenharmony_ci		if (r != sizeof(ret)) {
1838c2ecf20Sopenharmony_ci			dev_err(&udev->dev,
1848c2ecf20Sopenharmony_ci				"control request firmware confirmation failed."
1858c2ecf20Sopenharmony_ci				" Return value %d\n", r);
1868c2ecf20Sopenharmony_ci			if (r >= 0)
1878c2ecf20Sopenharmony_ci				r = -ENODEV;
1888c2ecf20Sopenharmony_ci			goto error;
1898c2ecf20Sopenharmony_ci		}
1908c2ecf20Sopenharmony_ci		ret = p[0];
1918c2ecf20Sopenharmony_ci		if (ret & 0x80) {
1928c2ecf20Sopenharmony_ci			dev_err(&udev->dev,
1938c2ecf20Sopenharmony_ci				"Internal error while downloading."
1948c2ecf20Sopenharmony_ci				" Firmware confirm return value %#04x\n",
1958c2ecf20Sopenharmony_ci				(unsigned int)ret);
1968c2ecf20Sopenharmony_ci			r = -ENODEV;
1978c2ecf20Sopenharmony_ci			goto error;
1988c2ecf20Sopenharmony_ci		}
1998c2ecf20Sopenharmony_ci		dev_dbg_f(&udev->dev, "firmware confirm return value %#04x\n",
2008c2ecf20Sopenharmony_ci			(unsigned int)ret);
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	r = 0;
2048c2ecf20Sopenharmony_cierror:
2058c2ecf20Sopenharmony_ci	kfree(p);
2068c2ecf20Sopenharmony_ci	return r;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic u16 get_word(const void *data, u16 offset)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	const __le16 *p = data;
2128c2ecf20Sopenharmony_ci	return le16_to_cpu(p[offset]);
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic char *get_fw_name(struct zd_usb *usb, char *buffer, size_t size,
2168c2ecf20Sopenharmony_ci	               const char* postfix)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	scnprintf(buffer, size, "%s%s",
2198c2ecf20Sopenharmony_ci		usb->is_zd1211b ?
2208c2ecf20Sopenharmony_ci			FW_ZD1211B_PREFIX : FW_ZD1211_PREFIX,
2218c2ecf20Sopenharmony_ci		postfix);
2228c2ecf20Sopenharmony_ci	return buffer;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int handle_version_mismatch(struct zd_usb *usb,
2268c2ecf20Sopenharmony_ci	const struct firmware *ub_fw)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct usb_device *udev = zd_usb_to_usbdev(usb);
2298c2ecf20Sopenharmony_ci	const struct firmware *ur_fw = NULL;
2308c2ecf20Sopenharmony_ci	int offset;
2318c2ecf20Sopenharmony_ci	int r = 0;
2328c2ecf20Sopenharmony_ci	char fw_name[128];
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	r = request_fw_file(&ur_fw,
2358c2ecf20Sopenharmony_ci		get_fw_name(usb, fw_name, sizeof(fw_name), "ur"),
2368c2ecf20Sopenharmony_ci		&udev->dev);
2378c2ecf20Sopenharmony_ci	if (r)
2388c2ecf20Sopenharmony_ci		goto error;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	r = upload_code(udev, ur_fw->data, ur_fw->size, FW_START, REBOOT);
2418c2ecf20Sopenharmony_ci	if (r)
2428c2ecf20Sopenharmony_ci		goto error;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	offset = (E2P_BOOT_CODE_OFFSET * sizeof(u16));
2458c2ecf20Sopenharmony_ci	r = upload_code(udev, ub_fw->data + offset, ub_fw->size - offset,
2468c2ecf20Sopenharmony_ci		E2P_START + E2P_BOOT_CODE_OFFSET, REBOOT);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/* At this point, the vendor driver downloads the whole firmware
2498c2ecf20Sopenharmony_ci	 * image, hacks around with version IDs, and uploads it again,
2508c2ecf20Sopenharmony_ci	 * completely overwriting the boot code. We do not do this here as
2518c2ecf20Sopenharmony_ci	 * it is not required on any tested devices, and it is suspected to
2528c2ecf20Sopenharmony_ci	 * cause problems. */
2538c2ecf20Sopenharmony_cierror:
2548c2ecf20Sopenharmony_ci	release_firmware(ur_fw);
2558c2ecf20Sopenharmony_ci	return r;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic int upload_firmware(struct zd_usb *usb)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	int r;
2618c2ecf20Sopenharmony_ci	u16 fw_bcdDevice;
2628c2ecf20Sopenharmony_ci	u16 bcdDevice;
2638c2ecf20Sopenharmony_ci	struct usb_device *udev = zd_usb_to_usbdev(usb);
2648c2ecf20Sopenharmony_ci	const struct firmware *ub_fw = NULL;
2658c2ecf20Sopenharmony_ci	const struct firmware *uph_fw = NULL;
2668c2ecf20Sopenharmony_ci	char fw_name[128];
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	bcdDevice = get_bcdDevice(udev);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	r = request_fw_file(&ub_fw,
2718c2ecf20Sopenharmony_ci		get_fw_name(usb, fw_name, sizeof(fw_name), "ub"),
2728c2ecf20Sopenharmony_ci		&udev->dev);
2738c2ecf20Sopenharmony_ci	if (r)
2748c2ecf20Sopenharmony_ci		goto error;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	fw_bcdDevice = get_word(ub_fw->data, E2P_DATA_OFFSET);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (fw_bcdDevice != bcdDevice) {
2798c2ecf20Sopenharmony_ci		dev_info(&udev->dev,
2808c2ecf20Sopenharmony_ci			"firmware version %#06x and device bootcode version "
2818c2ecf20Sopenharmony_ci			"%#06x differ\n", fw_bcdDevice, bcdDevice);
2828c2ecf20Sopenharmony_ci		if (bcdDevice <= 0x4313)
2838c2ecf20Sopenharmony_ci			dev_warn(&udev->dev, "device has old bootcode, please "
2848c2ecf20Sopenharmony_ci				"report success or failure\n");
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci		r = handle_version_mismatch(usb, ub_fw);
2878c2ecf20Sopenharmony_ci		if (r)
2888c2ecf20Sopenharmony_ci			goto error;
2898c2ecf20Sopenharmony_ci	} else {
2908c2ecf20Sopenharmony_ci		dev_dbg_f(&udev->dev,
2918c2ecf20Sopenharmony_ci			"firmware device id %#06x is equal to the "
2928c2ecf20Sopenharmony_ci			"actual device id\n", fw_bcdDevice);
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	r = request_fw_file(&uph_fw,
2978c2ecf20Sopenharmony_ci		get_fw_name(usb, fw_name, sizeof(fw_name), "uphr"),
2988c2ecf20Sopenharmony_ci		&udev->dev);
2998c2ecf20Sopenharmony_ci	if (r)
3008c2ecf20Sopenharmony_ci		goto error;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	r = upload_code(udev, uph_fw->data, uph_fw->size, FW_START, REBOOT);
3038c2ecf20Sopenharmony_ci	if (r) {
3048c2ecf20Sopenharmony_ci		dev_err(&udev->dev,
3058c2ecf20Sopenharmony_ci			"Could not upload firmware code uph. Error number %d\n",
3068c2ecf20Sopenharmony_ci			r);
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* FALL-THROUGH */
3108c2ecf20Sopenharmony_cierror:
3118c2ecf20Sopenharmony_ci	release_firmware(ub_fw);
3128c2ecf20Sopenharmony_ci	release_firmware(uph_fw);
3138c2ecf20Sopenharmony_ci	return r;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ciMODULE_FIRMWARE(FW_ZD1211B_PREFIX "ur");
3178c2ecf20Sopenharmony_ciMODULE_FIRMWARE(FW_ZD1211_PREFIX "ur");
3188c2ecf20Sopenharmony_ciMODULE_FIRMWARE(FW_ZD1211B_PREFIX "ub");
3198c2ecf20Sopenharmony_ciMODULE_FIRMWARE(FW_ZD1211_PREFIX "ub");
3208c2ecf20Sopenharmony_ciMODULE_FIRMWARE(FW_ZD1211B_PREFIX "uphr");
3218c2ecf20Sopenharmony_ciMODULE_FIRMWARE(FW_ZD1211_PREFIX "uphr");
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci/* Read data from device address space using "firmware interface" which does
3248c2ecf20Sopenharmony_ci * not require firmware to be loaded. */
3258c2ecf20Sopenharmony_ciint zd_usb_read_fw(struct zd_usb *usb, zd_addr_t addr, u8 *data, u16 len)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	int r;
3288c2ecf20Sopenharmony_ci	struct usb_device *udev = zd_usb_to_usbdev(usb);
3298c2ecf20Sopenharmony_ci	u8 *buf;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/* Use "DMA-aware" buffer. */
3328c2ecf20Sopenharmony_ci	buf = kmalloc(len, GFP_KERNEL);
3338c2ecf20Sopenharmony_ci	if (!buf)
3348c2ecf20Sopenharmony_ci		return -ENOMEM;
3358c2ecf20Sopenharmony_ci	r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
3368c2ecf20Sopenharmony_ci		USB_REQ_FIRMWARE_READ_DATA, USB_DIR_IN | 0x40, addr, 0,
3378c2ecf20Sopenharmony_ci		buf, len, 5000);
3388c2ecf20Sopenharmony_ci	if (r < 0) {
3398c2ecf20Sopenharmony_ci		dev_err(&udev->dev,
3408c2ecf20Sopenharmony_ci			"read over firmware interface failed: %d\n", r);
3418c2ecf20Sopenharmony_ci		goto exit;
3428c2ecf20Sopenharmony_ci	} else if (r != len) {
3438c2ecf20Sopenharmony_ci		dev_err(&udev->dev,
3448c2ecf20Sopenharmony_ci			"incomplete read over firmware interface: %d/%d\n",
3458c2ecf20Sopenharmony_ci			r, len);
3468c2ecf20Sopenharmony_ci		r = -EIO;
3478c2ecf20Sopenharmony_ci		goto exit;
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci	r = 0;
3508c2ecf20Sopenharmony_ci	memcpy(data, buf, len);
3518c2ecf20Sopenharmony_ciexit:
3528c2ecf20Sopenharmony_ci	kfree(buf);
3538c2ecf20Sopenharmony_ci	return r;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci#define urb_dev(urb) (&(urb)->dev->dev)
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic inline void handle_regs_int_override(struct urb *urb)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	struct zd_usb *usb = urb->context;
3618c2ecf20Sopenharmony_ci	struct zd_usb_interrupt *intr = &usb->intr;
3628c2ecf20Sopenharmony_ci	unsigned long flags;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intr->lock, flags);
3658c2ecf20Sopenharmony_ci	if (atomic_read(&intr->read_regs_enabled)) {
3668c2ecf20Sopenharmony_ci		atomic_set(&intr->read_regs_enabled, 0);
3678c2ecf20Sopenharmony_ci		intr->read_regs_int_overridden = 1;
3688c2ecf20Sopenharmony_ci		complete(&intr->read_regs.completion);
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intr->lock, flags);
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic inline void handle_regs_int(struct urb *urb)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct zd_usb *usb = urb->context;
3768c2ecf20Sopenharmony_ci	struct zd_usb_interrupt *intr = &usb->intr;
3778c2ecf20Sopenharmony_ci	unsigned long flags;
3788c2ecf20Sopenharmony_ci	int len;
3798c2ecf20Sopenharmony_ci	u16 int_num;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intr->lock, flags);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	int_num = le16_to_cpu(*(__le16 *)(urb->transfer_buffer+2));
3848c2ecf20Sopenharmony_ci	if (int_num == CR_INTERRUPT) {
3858c2ecf20Sopenharmony_ci		struct zd_mac *mac = zd_hw_mac(zd_usb_to_hw(urb->context));
3868c2ecf20Sopenharmony_ci		spin_lock(&mac->lock);
3878c2ecf20Sopenharmony_ci		memcpy(&mac->intr_buffer, urb->transfer_buffer,
3888c2ecf20Sopenharmony_ci				USB_MAX_EP_INT_BUFFER);
3898c2ecf20Sopenharmony_ci		spin_unlock(&mac->lock);
3908c2ecf20Sopenharmony_ci		schedule_work(&mac->process_intr);
3918c2ecf20Sopenharmony_ci	} else if (atomic_read(&intr->read_regs_enabled)) {
3928c2ecf20Sopenharmony_ci		len = urb->actual_length;
3938c2ecf20Sopenharmony_ci		intr->read_regs.length = urb->actual_length;
3948c2ecf20Sopenharmony_ci		if (len > sizeof(intr->read_regs.buffer))
3958c2ecf20Sopenharmony_ci			len = sizeof(intr->read_regs.buffer);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci		memcpy(intr->read_regs.buffer, urb->transfer_buffer, len);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		/* Sometimes USB_INT_ID_REGS is not overridden, but comes after
4008c2ecf20Sopenharmony_ci		 * USB_INT_ID_RETRY_FAILED. Read-reg retry then gets this
4018c2ecf20Sopenharmony_ci		 * delayed USB_INT_ID_REGS, but leaves USB_INT_ID_REGS of
4028c2ecf20Sopenharmony_ci		 * retry unhandled. Next read-reg command then might catch
4038c2ecf20Sopenharmony_ci		 * this wrong USB_INT_ID_REGS. Fix by ignoring wrong reads.
4048c2ecf20Sopenharmony_ci		 */
4058c2ecf20Sopenharmony_ci		if (!check_read_regs(usb, intr->read_regs.req,
4068c2ecf20Sopenharmony_ci						intr->read_regs.req_count))
4078c2ecf20Sopenharmony_ci			goto out;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci		atomic_set(&intr->read_regs_enabled, 0);
4108c2ecf20Sopenharmony_ci		intr->read_regs_int_overridden = 0;
4118c2ecf20Sopenharmony_ci		complete(&intr->read_regs.completion);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci		goto out;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ciout:
4178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intr->lock, flags);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	/* CR_INTERRUPT might override read_reg too. */
4208c2ecf20Sopenharmony_ci	if (int_num == CR_INTERRUPT && atomic_read(&intr->read_regs_enabled))
4218c2ecf20Sopenharmony_ci		handle_regs_int_override(urb);
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic void int_urb_complete(struct urb *urb)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	int r;
4278c2ecf20Sopenharmony_ci	struct usb_int_header *hdr;
4288c2ecf20Sopenharmony_ci	struct zd_usb *usb;
4298c2ecf20Sopenharmony_ci	struct zd_usb_interrupt *intr;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	switch (urb->status) {
4328c2ecf20Sopenharmony_ci	case 0:
4338c2ecf20Sopenharmony_ci		break;
4348c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
4358c2ecf20Sopenharmony_ci	case -EINVAL:
4368c2ecf20Sopenharmony_ci	case -ENODEV:
4378c2ecf20Sopenharmony_ci	case -ENOENT:
4388c2ecf20Sopenharmony_ci	case -ECONNRESET:
4398c2ecf20Sopenharmony_ci	case -EPIPE:
4408c2ecf20Sopenharmony_ci		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
4418c2ecf20Sopenharmony_ci		return;
4428c2ecf20Sopenharmony_ci	default:
4438c2ecf20Sopenharmony_ci		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
4448c2ecf20Sopenharmony_ci		goto resubmit;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	if (urb->actual_length < sizeof(hdr)) {
4488c2ecf20Sopenharmony_ci		dev_dbg_f(urb_dev(urb), "error: urb %p to small\n", urb);
4498c2ecf20Sopenharmony_ci		goto resubmit;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	hdr = urb->transfer_buffer;
4538c2ecf20Sopenharmony_ci	if (hdr->type != USB_INT_TYPE) {
4548c2ecf20Sopenharmony_ci		dev_dbg_f(urb_dev(urb), "error: urb %p wrong type\n", urb);
4558c2ecf20Sopenharmony_ci		goto resubmit;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	/* USB_INT_ID_RETRY_FAILED triggered by tx-urb submit can override
4598c2ecf20Sopenharmony_ci	 * pending USB_INT_ID_REGS causing read command timeout.
4608c2ecf20Sopenharmony_ci	 */
4618c2ecf20Sopenharmony_ci	usb = urb->context;
4628c2ecf20Sopenharmony_ci	intr = &usb->intr;
4638c2ecf20Sopenharmony_ci	if (hdr->id != USB_INT_ID_REGS && atomic_read(&intr->read_regs_enabled))
4648c2ecf20Sopenharmony_ci		handle_regs_int_override(urb);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	switch (hdr->id) {
4678c2ecf20Sopenharmony_ci	case USB_INT_ID_REGS:
4688c2ecf20Sopenharmony_ci		handle_regs_int(urb);
4698c2ecf20Sopenharmony_ci		break;
4708c2ecf20Sopenharmony_ci	case USB_INT_ID_RETRY_FAILED:
4718c2ecf20Sopenharmony_ci		zd_mac_tx_failed(urb);
4728c2ecf20Sopenharmony_ci		break;
4738c2ecf20Sopenharmony_ci	default:
4748c2ecf20Sopenharmony_ci		dev_dbg_f(urb_dev(urb), "error: urb %p unknown id %x\n", urb,
4758c2ecf20Sopenharmony_ci			(unsigned int)hdr->id);
4768c2ecf20Sopenharmony_ci		goto resubmit;
4778c2ecf20Sopenharmony_ci	}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ciresubmit:
4808c2ecf20Sopenharmony_ci	r = usb_submit_urb(urb, GFP_ATOMIC);
4818c2ecf20Sopenharmony_ci	if (r) {
4828c2ecf20Sopenharmony_ci		dev_dbg_f(urb_dev(urb), "error: resubmit urb %p err code %d\n",
4838c2ecf20Sopenharmony_ci			  urb, r);
4848c2ecf20Sopenharmony_ci		/* TODO: add worker to reset intr->urb */
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci	return;
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cistatic inline int int_urb_interval(struct usb_device *udev)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	switch (udev->speed) {
4928c2ecf20Sopenharmony_ci	case USB_SPEED_HIGH:
4938c2ecf20Sopenharmony_ci		return 4;
4948c2ecf20Sopenharmony_ci	case USB_SPEED_LOW:
4958c2ecf20Sopenharmony_ci		return 10;
4968c2ecf20Sopenharmony_ci	case USB_SPEED_FULL:
4978c2ecf20Sopenharmony_ci	default:
4988c2ecf20Sopenharmony_ci		return 1;
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic inline int usb_int_enabled(struct zd_usb *usb)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	unsigned long flags;
5058c2ecf20Sopenharmony_ci	struct zd_usb_interrupt *intr = &usb->intr;
5068c2ecf20Sopenharmony_ci	struct urb *urb;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intr->lock, flags);
5098c2ecf20Sopenharmony_ci	urb = intr->urb;
5108c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intr->lock, flags);
5118c2ecf20Sopenharmony_ci	return urb != NULL;
5128c2ecf20Sopenharmony_ci}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ciint zd_usb_enable_int(struct zd_usb *usb)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci	int r;
5178c2ecf20Sopenharmony_ci	struct usb_device *udev = zd_usb_to_usbdev(usb);
5188c2ecf20Sopenharmony_ci	struct zd_usb_interrupt *intr = &usb->intr;
5198c2ecf20Sopenharmony_ci	struct urb *urb;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	dev_dbg_f(zd_usb_dev(usb), "\n");
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	urb = usb_alloc_urb(0, GFP_KERNEL);
5248c2ecf20Sopenharmony_ci	if (!urb) {
5258c2ecf20Sopenharmony_ci		r = -ENOMEM;
5268c2ecf20Sopenharmony_ci		goto out;
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	ZD_ASSERT(!irqs_disabled());
5308c2ecf20Sopenharmony_ci	spin_lock_irq(&intr->lock);
5318c2ecf20Sopenharmony_ci	if (intr->urb) {
5328c2ecf20Sopenharmony_ci		spin_unlock_irq(&intr->lock);
5338c2ecf20Sopenharmony_ci		r = 0;
5348c2ecf20Sopenharmony_ci		goto error_free_urb;
5358c2ecf20Sopenharmony_ci	}
5368c2ecf20Sopenharmony_ci	intr->urb = urb;
5378c2ecf20Sopenharmony_ci	spin_unlock_irq(&intr->lock);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	r = -ENOMEM;
5408c2ecf20Sopenharmony_ci	intr->buffer = usb_alloc_coherent(udev, USB_MAX_EP_INT_BUFFER,
5418c2ecf20Sopenharmony_ci					  GFP_KERNEL, &intr->buffer_dma);
5428c2ecf20Sopenharmony_ci	if (!intr->buffer) {
5438c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
5448c2ecf20Sopenharmony_ci			"couldn't allocate transfer_buffer\n");
5458c2ecf20Sopenharmony_ci		goto error_set_urb_null;
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, EP_INT_IN),
5498c2ecf20Sopenharmony_ci			 intr->buffer, USB_MAX_EP_INT_BUFFER,
5508c2ecf20Sopenharmony_ci			 int_urb_complete, usb,
5518c2ecf20Sopenharmony_ci			 intr->interval);
5528c2ecf20Sopenharmony_ci	urb->transfer_dma = intr->buffer_dma;
5538c2ecf20Sopenharmony_ci	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb);
5568c2ecf20Sopenharmony_ci	r = usb_submit_urb(urb, GFP_KERNEL);
5578c2ecf20Sopenharmony_ci	if (r) {
5588c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
5598c2ecf20Sopenharmony_ci			 "Couldn't submit urb. Error number %d\n", r);
5608c2ecf20Sopenharmony_ci		goto error;
5618c2ecf20Sopenharmony_ci	}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	return 0;
5648c2ecf20Sopenharmony_cierror:
5658c2ecf20Sopenharmony_ci	usb_free_coherent(udev, USB_MAX_EP_INT_BUFFER,
5668c2ecf20Sopenharmony_ci			  intr->buffer, intr->buffer_dma);
5678c2ecf20Sopenharmony_cierror_set_urb_null:
5688c2ecf20Sopenharmony_ci	spin_lock_irq(&intr->lock);
5698c2ecf20Sopenharmony_ci	intr->urb = NULL;
5708c2ecf20Sopenharmony_ci	spin_unlock_irq(&intr->lock);
5718c2ecf20Sopenharmony_cierror_free_urb:
5728c2ecf20Sopenharmony_ci	usb_free_urb(urb);
5738c2ecf20Sopenharmony_ciout:
5748c2ecf20Sopenharmony_ci	return r;
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_civoid zd_usb_disable_int(struct zd_usb *usb)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	unsigned long flags;
5808c2ecf20Sopenharmony_ci	struct usb_device *udev = zd_usb_to_usbdev(usb);
5818c2ecf20Sopenharmony_ci	struct zd_usb_interrupt *intr = &usb->intr;
5828c2ecf20Sopenharmony_ci	struct urb *urb;
5838c2ecf20Sopenharmony_ci	void *buffer;
5848c2ecf20Sopenharmony_ci	dma_addr_t buffer_dma;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	spin_lock_irqsave(&intr->lock, flags);
5878c2ecf20Sopenharmony_ci	urb = intr->urb;
5888c2ecf20Sopenharmony_ci	if (!urb) {
5898c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&intr->lock, flags);
5908c2ecf20Sopenharmony_ci		return;
5918c2ecf20Sopenharmony_ci	}
5928c2ecf20Sopenharmony_ci	intr->urb = NULL;
5938c2ecf20Sopenharmony_ci	buffer = intr->buffer;
5948c2ecf20Sopenharmony_ci	buffer_dma = intr->buffer_dma;
5958c2ecf20Sopenharmony_ci	intr->buffer = NULL;
5968c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&intr->lock, flags);
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	usb_kill_urb(urb);
5998c2ecf20Sopenharmony_ci	dev_dbg_f(zd_usb_dev(usb), "urb %p killed\n", urb);
6008c2ecf20Sopenharmony_ci	usb_free_urb(urb);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	usb_free_coherent(udev, USB_MAX_EP_INT_BUFFER, buffer, buffer_dma);
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic void handle_rx_packet(struct zd_usb *usb, const u8 *buffer,
6068c2ecf20Sopenharmony_ci			     unsigned int length)
6078c2ecf20Sopenharmony_ci{
6088c2ecf20Sopenharmony_ci	int i;
6098c2ecf20Sopenharmony_ci	const struct rx_length_info *length_info;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	if (length < sizeof(struct rx_length_info)) {
6128c2ecf20Sopenharmony_ci		/* It's not a complete packet anyhow. */
6138c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb), "invalid, small RX packet : %d\n",
6148c2ecf20Sopenharmony_ci					   length);
6158c2ecf20Sopenharmony_ci		return;
6168c2ecf20Sopenharmony_ci	}
6178c2ecf20Sopenharmony_ci	length_info = (struct rx_length_info *)
6188c2ecf20Sopenharmony_ci		(buffer + length - sizeof(struct rx_length_info));
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	/* It might be that three frames are merged into a single URB
6218c2ecf20Sopenharmony_ci	 * transaction. We have to check for the length info tag.
6228c2ecf20Sopenharmony_ci	 *
6238c2ecf20Sopenharmony_ci	 * While testing we discovered that length_info might be unaligned,
6248c2ecf20Sopenharmony_ci	 * because if USB transactions are merged, the last packet will not
6258c2ecf20Sopenharmony_ci	 * be padded. Unaligned access might also happen if the length_info
6268c2ecf20Sopenharmony_ci	 * structure is not present.
6278c2ecf20Sopenharmony_ci	 */
6288c2ecf20Sopenharmony_ci	if (get_unaligned_le16(&length_info->tag) == RX_LENGTH_INFO_TAG)
6298c2ecf20Sopenharmony_ci	{
6308c2ecf20Sopenharmony_ci		unsigned int l, k, n;
6318c2ecf20Sopenharmony_ci		for (i = 0, l = 0;; i++) {
6328c2ecf20Sopenharmony_ci			k = get_unaligned_le16(&length_info->length[i]);
6338c2ecf20Sopenharmony_ci			if (k == 0)
6348c2ecf20Sopenharmony_ci				return;
6358c2ecf20Sopenharmony_ci			n = l+k;
6368c2ecf20Sopenharmony_ci			if (n > length)
6378c2ecf20Sopenharmony_ci				return;
6388c2ecf20Sopenharmony_ci			zd_mac_rx(zd_usb_to_hw(usb), buffer+l, k);
6398c2ecf20Sopenharmony_ci			if (i >= 2)
6408c2ecf20Sopenharmony_ci				return;
6418c2ecf20Sopenharmony_ci			l = (n+3) & ~3;
6428c2ecf20Sopenharmony_ci		}
6438c2ecf20Sopenharmony_ci	} else {
6448c2ecf20Sopenharmony_ci		zd_mac_rx(zd_usb_to_hw(usb), buffer, length);
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_cistatic void rx_urb_complete(struct urb *urb)
6498c2ecf20Sopenharmony_ci{
6508c2ecf20Sopenharmony_ci	int r;
6518c2ecf20Sopenharmony_ci	struct zd_usb *usb;
6528c2ecf20Sopenharmony_ci	struct zd_usb_rx *rx;
6538c2ecf20Sopenharmony_ci	const u8 *buffer;
6548c2ecf20Sopenharmony_ci	unsigned int length;
6558c2ecf20Sopenharmony_ci	unsigned long flags;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	switch (urb->status) {
6588c2ecf20Sopenharmony_ci	case 0:
6598c2ecf20Sopenharmony_ci		break;
6608c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
6618c2ecf20Sopenharmony_ci	case -EINVAL:
6628c2ecf20Sopenharmony_ci	case -ENODEV:
6638c2ecf20Sopenharmony_ci	case -ENOENT:
6648c2ecf20Sopenharmony_ci	case -ECONNRESET:
6658c2ecf20Sopenharmony_ci	case -EPIPE:
6668c2ecf20Sopenharmony_ci		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
6678c2ecf20Sopenharmony_ci		return;
6688c2ecf20Sopenharmony_ci	default:
6698c2ecf20Sopenharmony_ci		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
6708c2ecf20Sopenharmony_ci		goto resubmit;
6718c2ecf20Sopenharmony_ci	}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	buffer = urb->transfer_buffer;
6748c2ecf20Sopenharmony_ci	length = urb->actual_length;
6758c2ecf20Sopenharmony_ci	usb = urb->context;
6768c2ecf20Sopenharmony_ci	rx = &usb->rx;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	tasklet_schedule(&rx->reset_timer_tasklet);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	if (length%rx->usb_packet_size > rx->usb_packet_size-4) {
6818c2ecf20Sopenharmony_ci		/* If there is an old first fragment, we don't care. */
6828c2ecf20Sopenharmony_ci		dev_dbg_f(urb_dev(urb), "*** first fragment ***\n");
6838c2ecf20Sopenharmony_ci		ZD_ASSERT(length <= ARRAY_SIZE(rx->fragment));
6848c2ecf20Sopenharmony_ci		spin_lock_irqsave(&rx->lock, flags);
6858c2ecf20Sopenharmony_ci		memcpy(rx->fragment, buffer, length);
6868c2ecf20Sopenharmony_ci		rx->fragment_length = length;
6878c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&rx->lock, flags);
6888c2ecf20Sopenharmony_ci		goto resubmit;
6898c2ecf20Sopenharmony_ci	}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	spin_lock_irqsave(&rx->lock, flags);
6928c2ecf20Sopenharmony_ci	if (rx->fragment_length > 0) {
6938c2ecf20Sopenharmony_ci		/* We are on a second fragment, we believe */
6948c2ecf20Sopenharmony_ci		ZD_ASSERT(length + rx->fragment_length <=
6958c2ecf20Sopenharmony_ci			  ARRAY_SIZE(rx->fragment));
6968c2ecf20Sopenharmony_ci		dev_dbg_f(urb_dev(urb), "*** second fragment ***\n");
6978c2ecf20Sopenharmony_ci		memcpy(rx->fragment+rx->fragment_length, buffer, length);
6988c2ecf20Sopenharmony_ci		handle_rx_packet(usb, rx->fragment,
6998c2ecf20Sopenharmony_ci			         rx->fragment_length + length);
7008c2ecf20Sopenharmony_ci		rx->fragment_length = 0;
7018c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&rx->lock, flags);
7028c2ecf20Sopenharmony_ci	} else {
7038c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&rx->lock, flags);
7048c2ecf20Sopenharmony_ci		handle_rx_packet(usb, buffer, length);
7058c2ecf20Sopenharmony_ci	}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ciresubmit:
7088c2ecf20Sopenharmony_ci	r = usb_submit_urb(urb, GFP_ATOMIC);
7098c2ecf20Sopenharmony_ci	if (r)
7108c2ecf20Sopenharmony_ci		dev_dbg_f(urb_dev(urb), "urb %p resubmit error %d\n", urb, r);
7118c2ecf20Sopenharmony_ci}
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_cistatic struct urb *alloc_rx_urb(struct zd_usb *usb)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	struct usb_device *udev = zd_usb_to_usbdev(usb);
7168c2ecf20Sopenharmony_ci	struct urb *urb;
7178c2ecf20Sopenharmony_ci	void *buffer;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	urb = usb_alloc_urb(0, GFP_KERNEL);
7208c2ecf20Sopenharmony_ci	if (!urb)
7218c2ecf20Sopenharmony_ci		return NULL;
7228c2ecf20Sopenharmony_ci	buffer = usb_alloc_coherent(udev, USB_MAX_RX_SIZE, GFP_KERNEL,
7238c2ecf20Sopenharmony_ci				    &urb->transfer_dma);
7248c2ecf20Sopenharmony_ci	if (!buffer) {
7258c2ecf20Sopenharmony_ci		usb_free_urb(urb);
7268c2ecf20Sopenharmony_ci		return NULL;
7278c2ecf20Sopenharmony_ci	}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN),
7308c2ecf20Sopenharmony_ci			  buffer, USB_MAX_RX_SIZE,
7318c2ecf20Sopenharmony_ci			  rx_urb_complete, usb);
7328c2ecf20Sopenharmony_ci	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	return urb;
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_cistatic void free_rx_urb(struct urb *urb)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	if (!urb)
7408c2ecf20Sopenharmony_ci		return;
7418c2ecf20Sopenharmony_ci	usb_free_coherent(urb->dev, urb->transfer_buffer_length,
7428c2ecf20Sopenharmony_ci			  urb->transfer_buffer, urb->transfer_dma);
7438c2ecf20Sopenharmony_ci	usb_free_urb(urb);
7448c2ecf20Sopenharmony_ci}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_cistatic int __zd_usb_enable_rx(struct zd_usb *usb)
7478c2ecf20Sopenharmony_ci{
7488c2ecf20Sopenharmony_ci	int i, r;
7498c2ecf20Sopenharmony_ci	struct zd_usb_rx *rx = &usb->rx;
7508c2ecf20Sopenharmony_ci	struct urb **urbs;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	dev_dbg_f(zd_usb_dev(usb), "\n");
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	r = -ENOMEM;
7558c2ecf20Sopenharmony_ci	urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL);
7568c2ecf20Sopenharmony_ci	if (!urbs)
7578c2ecf20Sopenharmony_ci		goto error;
7588c2ecf20Sopenharmony_ci	for (i = 0; i < RX_URBS_COUNT; i++) {
7598c2ecf20Sopenharmony_ci		urbs[i] = alloc_rx_urb(usb);
7608c2ecf20Sopenharmony_ci		if (!urbs[i])
7618c2ecf20Sopenharmony_ci			goto error;
7628c2ecf20Sopenharmony_ci	}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	ZD_ASSERT(!irqs_disabled());
7658c2ecf20Sopenharmony_ci	spin_lock_irq(&rx->lock);
7668c2ecf20Sopenharmony_ci	if (rx->urbs) {
7678c2ecf20Sopenharmony_ci		spin_unlock_irq(&rx->lock);
7688c2ecf20Sopenharmony_ci		r = 0;
7698c2ecf20Sopenharmony_ci		goto error;
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci	rx->urbs = urbs;
7728c2ecf20Sopenharmony_ci	rx->urbs_count = RX_URBS_COUNT;
7738c2ecf20Sopenharmony_ci	spin_unlock_irq(&rx->lock);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	for (i = 0; i < RX_URBS_COUNT; i++) {
7768c2ecf20Sopenharmony_ci		r = usb_submit_urb(urbs[i], GFP_KERNEL);
7778c2ecf20Sopenharmony_ci		if (r)
7788c2ecf20Sopenharmony_ci			goto error_submit;
7798c2ecf20Sopenharmony_ci	}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	return 0;
7828c2ecf20Sopenharmony_cierror_submit:
7838c2ecf20Sopenharmony_ci	for (i = 0; i < RX_URBS_COUNT; i++) {
7848c2ecf20Sopenharmony_ci		usb_kill_urb(urbs[i]);
7858c2ecf20Sopenharmony_ci	}
7868c2ecf20Sopenharmony_ci	spin_lock_irq(&rx->lock);
7878c2ecf20Sopenharmony_ci	rx->urbs = NULL;
7888c2ecf20Sopenharmony_ci	rx->urbs_count = 0;
7898c2ecf20Sopenharmony_ci	spin_unlock_irq(&rx->lock);
7908c2ecf20Sopenharmony_cierror:
7918c2ecf20Sopenharmony_ci	if (urbs) {
7928c2ecf20Sopenharmony_ci		for (i = 0; i < RX_URBS_COUNT; i++)
7938c2ecf20Sopenharmony_ci			free_rx_urb(urbs[i]);
7948c2ecf20Sopenharmony_ci	}
7958c2ecf20Sopenharmony_ci	return r;
7968c2ecf20Sopenharmony_ci}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ciint zd_usb_enable_rx(struct zd_usb *usb)
7998c2ecf20Sopenharmony_ci{
8008c2ecf20Sopenharmony_ci	int r;
8018c2ecf20Sopenharmony_ci	struct zd_usb_rx *rx = &usb->rx;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	mutex_lock(&rx->setup_mutex);
8048c2ecf20Sopenharmony_ci	r = __zd_usb_enable_rx(usb);
8058c2ecf20Sopenharmony_ci	mutex_unlock(&rx->setup_mutex);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	zd_usb_reset_rx_idle_timer(usb);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	return r;
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_cistatic void __zd_usb_disable_rx(struct zd_usb *usb)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	int i;
8158c2ecf20Sopenharmony_ci	unsigned long flags;
8168c2ecf20Sopenharmony_ci	struct urb **urbs;
8178c2ecf20Sopenharmony_ci	unsigned int count;
8188c2ecf20Sopenharmony_ci	struct zd_usb_rx *rx = &usb->rx;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	spin_lock_irqsave(&rx->lock, flags);
8218c2ecf20Sopenharmony_ci	urbs = rx->urbs;
8228c2ecf20Sopenharmony_ci	count = rx->urbs_count;
8238c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&rx->lock, flags);
8248c2ecf20Sopenharmony_ci	if (!urbs)
8258c2ecf20Sopenharmony_ci		return;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
8288c2ecf20Sopenharmony_ci		usb_kill_urb(urbs[i]);
8298c2ecf20Sopenharmony_ci		free_rx_urb(urbs[i]);
8308c2ecf20Sopenharmony_ci	}
8318c2ecf20Sopenharmony_ci	kfree(urbs);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	spin_lock_irqsave(&rx->lock, flags);
8348c2ecf20Sopenharmony_ci	rx->urbs = NULL;
8358c2ecf20Sopenharmony_ci	rx->urbs_count = 0;
8368c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&rx->lock, flags);
8378c2ecf20Sopenharmony_ci}
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_civoid zd_usb_disable_rx(struct zd_usb *usb)
8408c2ecf20Sopenharmony_ci{
8418c2ecf20Sopenharmony_ci	struct zd_usb_rx *rx = &usb->rx;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	mutex_lock(&rx->setup_mutex);
8448c2ecf20Sopenharmony_ci	__zd_usb_disable_rx(usb);
8458c2ecf20Sopenharmony_ci	mutex_unlock(&rx->setup_mutex);
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	tasklet_kill(&rx->reset_timer_tasklet);
8488c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&rx->idle_work);
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_cistatic void zd_usb_reset_rx(struct zd_usb *usb)
8528c2ecf20Sopenharmony_ci{
8538c2ecf20Sopenharmony_ci	bool do_reset;
8548c2ecf20Sopenharmony_ci	struct zd_usb_rx *rx = &usb->rx;
8558c2ecf20Sopenharmony_ci	unsigned long flags;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	mutex_lock(&rx->setup_mutex);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	spin_lock_irqsave(&rx->lock, flags);
8608c2ecf20Sopenharmony_ci	do_reset = rx->urbs != NULL;
8618c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&rx->lock, flags);
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	if (do_reset) {
8648c2ecf20Sopenharmony_ci		__zd_usb_disable_rx(usb);
8658c2ecf20Sopenharmony_ci		__zd_usb_enable_rx(usb);
8668c2ecf20Sopenharmony_ci	}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	mutex_unlock(&rx->setup_mutex);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	if (do_reset)
8718c2ecf20Sopenharmony_ci		zd_usb_reset_rx_idle_timer(usb);
8728c2ecf20Sopenharmony_ci}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci/**
8758c2ecf20Sopenharmony_ci * zd_usb_disable_tx - disable transmission
8768c2ecf20Sopenharmony_ci * @usb: the zd1211rw-private USB structure
8778c2ecf20Sopenharmony_ci *
8788c2ecf20Sopenharmony_ci * Frees all URBs in the free list and marks the transmission as disabled.
8798c2ecf20Sopenharmony_ci */
8808c2ecf20Sopenharmony_civoid zd_usb_disable_tx(struct zd_usb *usb)
8818c2ecf20Sopenharmony_ci{
8828c2ecf20Sopenharmony_ci	struct zd_usb_tx *tx = &usb->tx;
8838c2ecf20Sopenharmony_ci	unsigned long flags;
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	atomic_set(&tx->enabled, 0);
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	/* kill all submitted tx-urbs */
8888c2ecf20Sopenharmony_ci	usb_kill_anchored_urbs(&tx->submitted);
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	spin_lock_irqsave(&tx->lock, flags);
8918c2ecf20Sopenharmony_ci	WARN_ON(!skb_queue_empty(&tx->submitted_skbs));
8928c2ecf20Sopenharmony_ci	WARN_ON(tx->submitted_urbs != 0);
8938c2ecf20Sopenharmony_ci	tx->submitted_urbs = 0;
8948c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&tx->lock, flags);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	/* The stopped state is ignored, relying on ieee80211_wake_queues()
8978c2ecf20Sopenharmony_ci	 * in a potentionally following zd_usb_enable_tx().
8988c2ecf20Sopenharmony_ci	 */
8998c2ecf20Sopenharmony_ci}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci/**
9028c2ecf20Sopenharmony_ci * zd_usb_enable_tx - enables transmission
9038c2ecf20Sopenharmony_ci * @usb: a &struct zd_usb pointer
9048c2ecf20Sopenharmony_ci *
9058c2ecf20Sopenharmony_ci * This function enables transmission and prepares the &zd_usb_tx data
9068c2ecf20Sopenharmony_ci * structure.
9078c2ecf20Sopenharmony_ci */
9088c2ecf20Sopenharmony_civoid zd_usb_enable_tx(struct zd_usb *usb)
9098c2ecf20Sopenharmony_ci{
9108c2ecf20Sopenharmony_ci	unsigned long flags;
9118c2ecf20Sopenharmony_ci	struct zd_usb_tx *tx = &usb->tx;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&tx->lock, flags);
9148c2ecf20Sopenharmony_ci	atomic_set(&tx->enabled, 1);
9158c2ecf20Sopenharmony_ci	tx->submitted_urbs = 0;
9168c2ecf20Sopenharmony_ci	ieee80211_wake_queues(zd_usb_to_hw(usb));
9178c2ecf20Sopenharmony_ci	tx->stopped = 0;
9188c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&tx->lock, flags);
9198c2ecf20Sopenharmony_ci}
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_cistatic void tx_dec_submitted_urbs(struct zd_usb *usb)
9228c2ecf20Sopenharmony_ci{
9238c2ecf20Sopenharmony_ci	struct zd_usb_tx *tx = &usb->tx;
9248c2ecf20Sopenharmony_ci	unsigned long flags;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	spin_lock_irqsave(&tx->lock, flags);
9278c2ecf20Sopenharmony_ci	--tx->submitted_urbs;
9288c2ecf20Sopenharmony_ci	if (tx->stopped && tx->submitted_urbs <= ZD_USB_TX_LOW) {
9298c2ecf20Sopenharmony_ci		ieee80211_wake_queues(zd_usb_to_hw(usb));
9308c2ecf20Sopenharmony_ci		tx->stopped = 0;
9318c2ecf20Sopenharmony_ci	}
9328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&tx->lock, flags);
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_cistatic void tx_inc_submitted_urbs(struct zd_usb *usb)
9368c2ecf20Sopenharmony_ci{
9378c2ecf20Sopenharmony_ci	struct zd_usb_tx *tx = &usb->tx;
9388c2ecf20Sopenharmony_ci	unsigned long flags;
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	spin_lock_irqsave(&tx->lock, flags);
9418c2ecf20Sopenharmony_ci	++tx->submitted_urbs;
9428c2ecf20Sopenharmony_ci	if (!tx->stopped && tx->submitted_urbs > ZD_USB_TX_HIGH) {
9438c2ecf20Sopenharmony_ci		ieee80211_stop_queues(zd_usb_to_hw(usb));
9448c2ecf20Sopenharmony_ci		tx->stopped = 1;
9458c2ecf20Sopenharmony_ci	}
9468c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&tx->lock, flags);
9478c2ecf20Sopenharmony_ci}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci/**
9508c2ecf20Sopenharmony_ci * tx_urb_complete - completes the execution of an URB
9518c2ecf20Sopenharmony_ci * @urb: a URB
9528c2ecf20Sopenharmony_ci *
9538c2ecf20Sopenharmony_ci * This function is called if the URB has been transferred to a device or an
9548c2ecf20Sopenharmony_ci * error has happened.
9558c2ecf20Sopenharmony_ci */
9568c2ecf20Sopenharmony_cistatic void tx_urb_complete(struct urb *urb)
9578c2ecf20Sopenharmony_ci{
9588c2ecf20Sopenharmony_ci	int r;
9598c2ecf20Sopenharmony_ci	struct sk_buff *skb;
9608c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *info;
9618c2ecf20Sopenharmony_ci	struct zd_usb *usb;
9628c2ecf20Sopenharmony_ci	struct zd_usb_tx *tx;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	skb = (struct sk_buff *)urb->context;
9658c2ecf20Sopenharmony_ci	info = IEEE80211_SKB_CB(skb);
9668c2ecf20Sopenharmony_ci	/*
9678c2ecf20Sopenharmony_ci	 * grab 'usb' pointer before handing off the skb (since
9688c2ecf20Sopenharmony_ci	 * it might be freed by zd_mac_tx_to_dev or mac80211)
9698c2ecf20Sopenharmony_ci	 */
9708c2ecf20Sopenharmony_ci	usb = &zd_hw_mac(info->rate_driver_data[0])->chip.usb;
9718c2ecf20Sopenharmony_ci	tx = &usb->tx;
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	switch (urb->status) {
9748c2ecf20Sopenharmony_ci	case 0:
9758c2ecf20Sopenharmony_ci		break;
9768c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
9778c2ecf20Sopenharmony_ci	case -EINVAL:
9788c2ecf20Sopenharmony_ci	case -ENODEV:
9798c2ecf20Sopenharmony_ci	case -ENOENT:
9808c2ecf20Sopenharmony_ci	case -ECONNRESET:
9818c2ecf20Sopenharmony_ci	case -EPIPE:
9828c2ecf20Sopenharmony_ci		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
9838c2ecf20Sopenharmony_ci		break;
9848c2ecf20Sopenharmony_ci	default:
9858c2ecf20Sopenharmony_ci		dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status);
9868c2ecf20Sopenharmony_ci		goto resubmit;
9878c2ecf20Sopenharmony_ci	}
9888c2ecf20Sopenharmony_cifree_urb:
9898c2ecf20Sopenharmony_ci	skb_unlink(skb, &usb->tx.submitted_skbs);
9908c2ecf20Sopenharmony_ci	zd_mac_tx_to_dev(skb, urb->status);
9918c2ecf20Sopenharmony_ci	usb_free_urb(urb);
9928c2ecf20Sopenharmony_ci	tx_dec_submitted_urbs(usb);
9938c2ecf20Sopenharmony_ci	return;
9948c2ecf20Sopenharmony_ciresubmit:
9958c2ecf20Sopenharmony_ci	usb_anchor_urb(urb, &tx->submitted);
9968c2ecf20Sopenharmony_ci	r = usb_submit_urb(urb, GFP_ATOMIC);
9978c2ecf20Sopenharmony_ci	if (r) {
9988c2ecf20Sopenharmony_ci		usb_unanchor_urb(urb);
9998c2ecf20Sopenharmony_ci		dev_dbg_f(urb_dev(urb), "error resubmit urb %p %d\n", urb, r);
10008c2ecf20Sopenharmony_ci		goto free_urb;
10018c2ecf20Sopenharmony_ci	}
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci/**
10058c2ecf20Sopenharmony_ci * zd_usb_tx: initiates transfer of a frame of the device
10068c2ecf20Sopenharmony_ci *
10078c2ecf20Sopenharmony_ci * @usb: the zd1211rw-private USB structure
10088c2ecf20Sopenharmony_ci * @skb: a &struct sk_buff pointer
10098c2ecf20Sopenharmony_ci *
10108c2ecf20Sopenharmony_ci * This function tranmits a frame to the device. It doesn't wait for
10118c2ecf20Sopenharmony_ci * completion. The frame must contain the control set and have all the
10128c2ecf20Sopenharmony_ci * control set information available.
10138c2ecf20Sopenharmony_ci *
10148c2ecf20Sopenharmony_ci * The function returns 0 if the transfer has been successfully initiated.
10158c2ecf20Sopenharmony_ci */
10168c2ecf20Sopenharmony_ciint zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb)
10178c2ecf20Sopenharmony_ci{
10188c2ecf20Sopenharmony_ci	int r;
10198c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
10208c2ecf20Sopenharmony_ci	struct usb_device *udev = zd_usb_to_usbdev(usb);
10218c2ecf20Sopenharmony_ci	struct urb *urb;
10228c2ecf20Sopenharmony_ci	struct zd_usb_tx *tx = &usb->tx;
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	if (!atomic_read(&tx->enabled)) {
10258c2ecf20Sopenharmony_ci		r = -ENOENT;
10268c2ecf20Sopenharmony_ci		goto out;
10278c2ecf20Sopenharmony_ci	}
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	urb = usb_alloc_urb(0, GFP_ATOMIC);
10308c2ecf20Sopenharmony_ci	if (!urb) {
10318c2ecf20Sopenharmony_ci		r = -ENOMEM;
10328c2ecf20Sopenharmony_ci		goto out;
10338c2ecf20Sopenharmony_ci	}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT),
10368c2ecf20Sopenharmony_ci		          skb->data, skb->len, tx_urb_complete, skb);
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	info->rate_driver_data[1] = (void *)jiffies;
10398c2ecf20Sopenharmony_ci	skb_queue_tail(&tx->submitted_skbs, skb);
10408c2ecf20Sopenharmony_ci	usb_anchor_urb(urb, &tx->submitted);
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	r = usb_submit_urb(urb, GFP_ATOMIC);
10438c2ecf20Sopenharmony_ci	if (r) {
10448c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb), "error submit urb %p %d\n", urb, r);
10458c2ecf20Sopenharmony_ci		usb_unanchor_urb(urb);
10468c2ecf20Sopenharmony_ci		skb_unlink(skb, &tx->submitted_skbs);
10478c2ecf20Sopenharmony_ci		goto error;
10488c2ecf20Sopenharmony_ci	}
10498c2ecf20Sopenharmony_ci	tx_inc_submitted_urbs(usb);
10508c2ecf20Sopenharmony_ci	return 0;
10518c2ecf20Sopenharmony_cierror:
10528c2ecf20Sopenharmony_ci	usb_free_urb(urb);
10538c2ecf20Sopenharmony_ciout:
10548c2ecf20Sopenharmony_ci	return r;
10558c2ecf20Sopenharmony_ci}
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_cistatic bool zd_tx_timeout(struct zd_usb *usb)
10588c2ecf20Sopenharmony_ci{
10598c2ecf20Sopenharmony_ci	struct zd_usb_tx *tx = &usb->tx;
10608c2ecf20Sopenharmony_ci	struct sk_buff_head *q = &tx->submitted_skbs;
10618c2ecf20Sopenharmony_ci	struct sk_buff *skb, *skbnext;
10628c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *info;
10638c2ecf20Sopenharmony_ci	unsigned long flags, trans_start;
10648c2ecf20Sopenharmony_ci	bool have_timedout = false;
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_ci	spin_lock_irqsave(&q->lock, flags);
10678c2ecf20Sopenharmony_ci	skb_queue_walk_safe(q, skb, skbnext) {
10688c2ecf20Sopenharmony_ci		info = IEEE80211_SKB_CB(skb);
10698c2ecf20Sopenharmony_ci		trans_start = (unsigned long)info->rate_driver_data[1];
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci		if (time_is_before_jiffies(trans_start + ZD_TX_TIMEOUT)) {
10728c2ecf20Sopenharmony_ci			have_timedout = true;
10738c2ecf20Sopenharmony_ci			break;
10748c2ecf20Sopenharmony_ci		}
10758c2ecf20Sopenharmony_ci	}
10768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&q->lock, flags);
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	return have_timedout;
10798c2ecf20Sopenharmony_ci}
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_cistatic void zd_tx_watchdog_handler(struct work_struct *work)
10828c2ecf20Sopenharmony_ci{
10838c2ecf20Sopenharmony_ci	struct zd_usb *usb =
10848c2ecf20Sopenharmony_ci		container_of(work, struct zd_usb, tx.watchdog_work.work);
10858c2ecf20Sopenharmony_ci	struct zd_usb_tx *tx = &usb->tx;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	if (!atomic_read(&tx->enabled) || !tx->watchdog_enabled)
10888c2ecf20Sopenharmony_ci		goto out;
10898c2ecf20Sopenharmony_ci	if (!zd_tx_timeout(usb))
10908c2ecf20Sopenharmony_ci		goto out;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	/* TX halted, try reset */
10938c2ecf20Sopenharmony_ci	dev_warn(zd_usb_dev(usb), "TX-stall detected, resetting device...");
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	usb_queue_reset_device(usb->intf);
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	/* reset will stop this worker, don't rearm */
10988c2ecf20Sopenharmony_ci	return;
10998c2ecf20Sopenharmony_ciout:
11008c2ecf20Sopenharmony_ci	queue_delayed_work(zd_workqueue, &tx->watchdog_work,
11018c2ecf20Sopenharmony_ci			   ZD_TX_WATCHDOG_INTERVAL);
11028c2ecf20Sopenharmony_ci}
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_civoid zd_tx_watchdog_enable(struct zd_usb *usb)
11058c2ecf20Sopenharmony_ci{
11068c2ecf20Sopenharmony_ci	struct zd_usb_tx *tx = &usb->tx;
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	if (!tx->watchdog_enabled) {
11098c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb), "\n");
11108c2ecf20Sopenharmony_ci		queue_delayed_work(zd_workqueue, &tx->watchdog_work,
11118c2ecf20Sopenharmony_ci				   ZD_TX_WATCHDOG_INTERVAL);
11128c2ecf20Sopenharmony_ci		tx->watchdog_enabled = 1;
11138c2ecf20Sopenharmony_ci	}
11148c2ecf20Sopenharmony_ci}
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_civoid zd_tx_watchdog_disable(struct zd_usb *usb)
11178c2ecf20Sopenharmony_ci{
11188c2ecf20Sopenharmony_ci	struct zd_usb_tx *tx = &usb->tx;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	if (tx->watchdog_enabled) {
11218c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb), "\n");
11228c2ecf20Sopenharmony_ci		tx->watchdog_enabled = 0;
11238c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&tx->watchdog_work);
11248c2ecf20Sopenharmony_ci	}
11258c2ecf20Sopenharmony_ci}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_cistatic void zd_rx_idle_timer_handler(struct work_struct *work)
11288c2ecf20Sopenharmony_ci{
11298c2ecf20Sopenharmony_ci	struct zd_usb *usb =
11308c2ecf20Sopenharmony_ci		container_of(work, struct zd_usb, rx.idle_work.work);
11318c2ecf20Sopenharmony_ci	struct zd_mac *mac = zd_usb_to_mac(usb);
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	if (!test_bit(ZD_DEVICE_RUNNING, &mac->flags))
11348c2ecf20Sopenharmony_ci		return;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	dev_dbg_f(zd_usb_dev(usb), "\n");
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_ci	/* 30 seconds since last rx, reset rx */
11398c2ecf20Sopenharmony_ci	zd_usb_reset_rx(usb);
11408c2ecf20Sopenharmony_ci}
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_cistatic void zd_usb_reset_rx_idle_timer_tasklet(struct tasklet_struct *t)
11438c2ecf20Sopenharmony_ci{
11448c2ecf20Sopenharmony_ci	struct zd_usb *usb = from_tasklet(usb, t, rx.reset_timer_tasklet);
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	zd_usb_reset_rx_idle_timer(usb);
11478c2ecf20Sopenharmony_ci}
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_civoid zd_usb_reset_rx_idle_timer(struct zd_usb *usb)
11508c2ecf20Sopenharmony_ci{
11518c2ecf20Sopenharmony_ci	struct zd_usb_rx *rx = &usb->rx;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	mod_delayed_work(zd_workqueue, &rx->idle_work, ZD_RX_IDLE_INTERVAL);
11548c2ecf20Sopenharmony_ci}
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_cistatic inline void init_usb_interrupt(struct zd_usb *usb)
11578c2ecf20Sopenharmony_ci{
11588c2ecf20Sopenharmony_ci	struct zd_usb_interrupt *intr = &usb->intr;
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	spin_lock_init(&intr->lock);
11618c2ecf20Sopenharmony_ci	intr->interval = int_urb_interval(zd_usb_to_usbdev(usb));
11628c2ecf20Sopenharmony_ci	init_completion(&intr->read_regs.completion);
11638c2ecf20Sopenharmony_ci	atomic_set(&intr->read_regs_enabled, 0);
11648c2ecf20Sopenharmony_ci	intr->read_regs.cr_int_addr = cpu_to_le16((u16)CR_INTERRUPT);
11658c2ecf20Sopenharmony_ci}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_cistatic inline void init_usb_rx(struct zd_usb *usb)
11688c2ecf20Sopenharmony_ci{
11698c2ecf20Sopenharmony_ci	struct zd_usb_rx *rx = &usb->rx;
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	spin_lock_init(&rx->lock);
11728c2ecf20Sopenharmony_ci	mutex_init(&rx->setup_mutex);
11738c2ecf20Sopenharmony_ci	if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH) {
11748c2ecf20Sopenharmony_ci		rx->usb_packet_size = 512;
11758c2ecf20Sopenharmony_ci	} else {
11768c2ecf20Sopenharmony_ci		rx->usb_packet_size = 64;
11778c2ecf20Sopenharmony_ci	}
11788c2ecf20Sopenharmony_ci	ZD_ASSERT(rx->fragment_length == 0);
11798c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&rx->idle_work, zd_rx_idle_timer_handler);
11808c2ecf20Sopenharmony_ci	rx->reset_timer_tasklet.func = (void (*))
11818c2ecf20Sopenharmony_ci					zd_usb_reset_rx_idle_timer_tasklet;
11828c2ecf20Sopenharmony_ci	rx->reset_timer_tasklet.data = (unsigned long)&rx->reset_timer_tasklet;
11838c2ecf20Sopenharmony_ci}
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_cistatic inline void init_usb_tx(struct zd_usb *usb)
11868c2ecf20Sopenharmony_ci{
11878c2ecf20Sopenharmony_ci	struct zd_usb_tx *tx = &usb->tx;
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	spin_lock_init(&tx->lock);
11908c2ecf20Sopenharmony_ci	atomic_set(&tx->enabled, 0);
11918c2ecf20Sopenharmony_ci	tx->stopped = 0;
11928c2ecf20Sopenharmony_ci	skb_queue_head_init(&tx->submitted_skbs);
11938c2ecf20Sopenharmony_ci	init_usb_anchor(&tx->submitted);
11948c2ecf20Sopenharmony_ci	tx->submitted_urbs = 0;
11958c2ecf20Sopenharmony_ci	tx->watchdog_enabled = 0;
11968c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&tx->watchdog_work, zd_tx_watchdog_handler);
11978c2ecf20Sopenharmony_ci}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_civoid zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw,
12008c2ecf20Sopenharmony_ci	         struct usb_interface *intf)
12018c2ecf20Sopenharmony_ci{
12028c2ecf20Sopenharmony_ci	memset(usb, 0, sizeof(*usb));
12038c2ecf20Sopenharmony_ci	usb->intf = usb_get_intf(intf);
12048c2ecf20Sopenharmony_ci	usb_set_intfdata(usb->intf, hw);
12058c2ecf20Sopenharmony_ci	init_usb_anchor(&usb->submitted_cmds);
12068c2ecf20Sopenharmony_ci	init_usb_interrupt(usb);
12078c2ecf20Sopenharmony_ci	init_usb_tx(usb);
12088c2ecf20Sopenharmony_ci	init_usb_rx(usb);
12098c2ecf20Sopenharmony_ci}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_civoid zd_usb_clear(struct zd_usb *usb)
12128c2ecf20Sopenharmony_ci{
12138c2ecf20Sopenharmony_ci	usb_set_intfdata(usb->intf, NULL);
12148c2ecf20Sopenharmony_ci	usb_put_intf(usb->intf);
12158c2ecf20Sopenharmony_ci	ZD_MEMCLEAR(usb, sizeof(*usb));
12168c2ecf20Sopenharmony_ci	/* FIXME: usb_interrupt, usb_tx, usb_rx? */
12178c2ecf20Sopenharmony_ci}
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_cistatic const char *speed(enum usb_device_speed speed)
12208c2ecf20Sopenharmony_ci{
12218c2ecf20Sopenharmony_ci	switch (speed) {
12228c2ecf20Sopenharmony_ci	case USB_SPEED_LOW:
12238c2ecf20Sopenharmony_ci		return "low";
12248c2ecf20Sopenharmony_ci	case USB_SPEED_FULL:
12258c2ecf20Sopenharmony_ci		return "full";
12268c2ecf20Sopenharmony_ci	case USB_SPEED_HIGH:
12278c2ecf20Sopenharmony_ci		return "high";
12288c2ecf20Sopenharmony_ci	default:
12298c2ecf20Sopenharmony_ci		return "unknown speed";
12308c2ecf20Sopenharmony_ci	}
12318c2ecf20Sopenharmony_ci}
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_cistatic int scnprint_id(struct usb_device *udev, char *buffer, size_t size)
12348c2ecf20Sopenharmony_ci{
12358c2ecf20Sopenharmony_ci	return scnprintf(buffer, size, "%04hx:%04hx v%04hx %s",
12368c2ecf20Sopenharmony_ci		le16_to_cpu(udev->descriptor.idVendor),
12378c2ecf20Sopenharmony_ci		le16_to_cpu(udev->descriptor.idProduct),
12388c2ecf20Sopenharmony_ci		get_bcdDevice(udev),
12398c2ecf20Sopenharmony_ci		speed(udev->speed));
12408c2ecf20Sopenharmony_ci}
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ciint zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size)
12438c2ecf20Sopenharmony_ci{
12448c2ecf20Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(usb->intf);
12458c2ecf20Sopenharmony_ci	return scnprint_id(udev, buffer, size);
12468c2ecf20Sopenharmony_ci}
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci#ifdef DEBUG
12498c2ecf20Sopenharmony_cistatic void print_id(struct usb_device *udev)
12508c2ecf20Sopenharmony_ci{
12518c2ecf20Sopenharmony_ci	char buffer[40];
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	scnprint_id(udev, buffer, sizeof(buffer));
12548c2ecf20Sopenharmony_ci	buffer[sizeof(buffer)-1] = 0;
12558c2ecf20Sopenharmony_ci	dev_dbg_f(&udev->dev, "%s\n", buffer);
12568c2ecf20Sopenharmony_ci}
12578c2ecf20Sopenharmony_ci#else
12588c2ecf20Sopenharmony_ci#define print_id(udev) do { } while (0)
12598c2ecf20Sopenharmony_ci#endif
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_cistatic int eject_installer(struct usb_interface *intf)
12628c2ecf20Sopenharmony_ci{
12638c2ecf20Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(intf);
12648c2ecf20Sopenharmony_ci	struct usb_host_interface *iface_desc = intf->cur_altsetting;
12658c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *endpoint;
12668c2ecf20Sopenharmony_ci	unsigned char *cmd;
12678c2ecf20Sopenharmony_ci	u8 bulk_out_ep;
12688c2ecf20Sopenharmony_ci	int r;
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	if (iface_desc->desc.bNumEndpoints < 2)
12718c2ecf20Sopenharmony_ci		return -ENODEV;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	/* Find bulk out endpoint */
12748c2ecf20Sopenharmony_ci	for (r = 1; r >= 0; r--) {
12758c2ecf20Sopenharmony_ci		endpoint = &iface_desc->endpoint[r].desc;
12768c2ecf20Sopenharmony_ci		if (usb_endpoint_dir_out(endpoint) &&
12778c2ecf20Sopenharmony_ci		    usb_endpoint_xfer_bulk(endpoint)) {
12788c2ecf20Sopenharmony_ci			bulk_out_ep = endpoint->bEndpointAddress;
12798c2ecf20Sopenharmony_ci			break;
12808c2ecf20Sopenharmony_ci		}
12818c2ecf20Sopenharmony_ci	}
12828c2ecf20Sopenharmony_ci	if (r == -1) {
12838c2ecf20Sopenharmony_ci		dev_err(&udev->dev,
12848c2ecf20Sopenharmony_ci			"zd1211rw: Could not find bulk out endpoint\n");
12858c2ecf20Sopenharmony_ci		return -ENODEV;
12868c2ecf20Sopenharmony_ci	}
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci	cmd = kzalloc(31, GFP_KERNEL);
12898c2ecf20Sopenharmony_ci	if (cmd == NULL)
12908c2ecf20Sopenharmony_ci		return -ENODEV;
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	/* USB bulk command block */
12938c2ecf20Sopenharmony_ci	cmd[0] = 0x55;	/* bulk command signature */
12948c2ecf20Sopenharmony_ci	cmd[1] = 0x53;	/* bulk command signature */
12958c2ecf20Sopenharmony_ci	cmd[2] = 0x42;	/* bulk command signature */
12968c2ecf20Sopenharmony_ci	cmd[3] = 0x43;	/* bulk command signature */
12978c2ecf20Sopenharmony_ci	cmd[14] = 6;	/* command length */
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	cmd[15] = 0x1b;	/* SCSI command: START STOP UNIT */
13008c2ecf20Sopenharmony_ci	cmd[19] = 0x2;	/* eject disc */
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	dev_info(&udev->dev, "Ejecting virtual installer media...\n");
13038c2ecf20Sopenharmony_ci	r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, bulk_out_ep),
13048c2ecf20Sopenharmony_ci		cmd, 31, NULL, 2000);
13058c2ecf20Sopenharmony_ci	kfree(cmd);
13068c2ecf20Sopenharmony_ci	if (r)
13078c2ecf20Sopenharmony_ci		return r;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	/* At this point, the device disconnects and reconnects with the real
13108c2ecf20Sopenharmony_ci	 * ID numbers. */
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, NULL);
13138c2ecf20Sopenharmony_ci	return 0;
13148c2ecf20Sopenharmony_ci}
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ciint zd_usb_init_hw(struct zd_usb *usb)
13178c2ecf20Sopenharmony_ci{
13188c2ecf20Sopenharmony_ci	int r;
13198c2ecf20Sopenharmony_ci	struct zd_mac *mac = zd_usb_to_mac(usb);
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	dev_dbg_f(zd_usb_dev(usb), "\n");
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	r = upload_firmware(usb);
13248c2ecf20Sopenharmony_ci	if (r) {
13258c2ecf20Sopenharmony_ci		dev_err(zd_usb_dev(usb),
13268c2ecf20Sopenharmony_ci		       "couldn't load firmware. Error number %d\n", r);
13278c2ecf20Sopenharmony_ci		return r;
13288c2ecf20Sopenharmony_ci	}
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	r = usb_reset_configuration(zd_usb_to_usbdev(usb));
13318c2ecf20Sopenharmony_ci	if (r) {
13328c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
13338c2ecf20Sopenharmony_ci			"couldn't reset configuration. Error number %d\n", r);
13348c2ecf20Sopenharmony_ci		return r;
13358c2ecf20Sopenharmony_ci	}
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	r = zd_mac_init_hw(mac->hw);
13388c2ecf20Sopenharmony_ci	if (r) {
13398c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
13408c2ecf20Sopenharmony_ci		         "couldn't initialize mac. Error number %d\n", r);
13418c2ecf20Sopenharmony_ci		return r;
13428c2ecf20Sopenharmony_ci	}
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	usb->initialized = 1;
13458c2ecf20Sopenharmony_ci	return 0;
13468c2ecf20Sopenharmony_ci}
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_cistatic int probe(struct usb_interface *intf, const struct usb_device_id *id)
13498c2ecf20Sopenharmony_ci{
13508c2ecf20Sopenharmony_ci	int r;
13518c2ecf20Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(intf);
13528c2ecf20Sopenharmony_ci	struct zd_usb *usb;
13538c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = NULL;
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	print_id(udev);
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	if (id->driver_info & DEVICE_INSTALLER)
13588c2ecf20Sopenharmony_ci		return eject_installer(intf);
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	switch (udev->speed) {
13618c2ecf20Sopenharmony_ci	case USB_SPEED_LOW:
13628c2ecf20Sopenharmony_ci	case USB_SPEED_FULL:
13638c2ecf20Sopenharmony_ci	case USB_SPEED_HIGH:
13648c2ecf20Sopenharmony_ci		break;
13658c2ecf20Sopenharmony_ci	default:
13668c2ecf20Sopenharmony_ci		dev_dbg_f(&intf->dev, "Unknown USB speed\n");
13678c2ecf20Sopenharmony_ci		r = -ENODEV;
13688c2ecf20Sopenharmony_ci		goto error;
13698c2ecf20Sopenharmony_ci	}
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	r = usb_reset_device(udev);
13728c2ecf20Sopenharmony_ci	if (r) {
13738c2ecf20Sopenharmony_ci		dev_err(&intf->dev,
13748c2ecf20Sopenharmony_ci			"couldn't reset usb device. Error number %d\n", r);
13758c2ecf20Sopenharmony_ci		goto error;
13768c2ecf20Sopenharmony_ci	}
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	hw = zd_mac_alloc_hw(intf);
13798c2ecf20Sopenharmony_ci	if (hw == NULL) {
13808c2ecf20Sopenharmony_ci		r = -ENOMEM;
13818c2ecf20Sopenharmony_ci		goto error;
13828c2ecf20Sopenharmony_ci	}
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	usb = &zd_hw_mac(hw)->chip.usb;
13858c2ecf20Sopenharmony_ci	usb->is_zd1211b = (id->driver_info == DEVICE_ZD1211B) != 0;
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci	r = zd_mac_preinit_hw(hw);
13888c2ecf20Sopenharmony_ci	if (r) {
13898c2ecf20Sopenharmony_ci		dev_dbg_f(&intf->dev,
13908c2ecf20Sopenharmony_ci		         "couldn't initialize mac. Error number %d\n", r);
13918c2ecf20Sopenharmony_ci		goto error;
13928c2ecf20Sopenharmony_ci	}
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci	r = ieee80211_register_hw(hw);
13958c2ecf20Sopenharmony_ci	if (r) {
13968c2ecf20Sopenharmony_ci		dev_dbg_f(&intf->dev,
13978c2ecf20Sopenharmony_ci			 "couldn't register device. Error number %d\n", r);
13988c2ecf20Sopenharmony_ci		goto error;
13998c2ecf20Sopenharmony_ci	}
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	dev_dbg_f(&intf->dev, "successful\n");
14028c2ecf20Sopenharmony_ci	dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy));
14038c2ecf20Sopenharmony_ci	return 0;
14048c2ecf20Sopenharmony_cierror:
14058c2ecf20Sopenharmony_ci	usb_reset_device(interface_to_usbdev(intf));
14068c2ecf20Sopenharmony_ci	if (hw) {
14078c2ecf20Sopenharmony_ci		zd_mac_clear(zd_hw_mac(hw));
14088c2ecf20Sopenharmony_ci		ieee80211_free_hw(hw);
14098c2ecf20Sopenharmony_ci	}
14108c2ecf20Sopenharmony_ci	return r;
14118c2ecf20Sopenharmony_ci}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_cistatic void disconnect(struct usb_interface *intf)
14148c2ecf20Sopenharmony_ci{
14158c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = zd_intf_to_hw(intf);
14168c2ecf20Sopenharmony_ci	struct zd_mac *mac;
14178c2ecf20Sopenharmony_ci	struct zd_usb *usb;
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	/* Either something really bad happened, or we're just dealing with
14208c2ecf20Sopenharmony_ci	 * a DEVICE_INSTALLER. */
14218c2ecf20Sopenharmony_ci	if (hw == NULL)
14228c2ecf20Sopenharmony_ci		return;
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	mac = zd_hw_mac(hw);
14258c2ecf20Sopenharmony_ci	usb = &mac->chip.usb;
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	dev_dbg_f(zd_usb_dev(usb), "\n");
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	ieee80211_unregister_hw(hw);
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci	/* Just in case something has gone wrong! */
14328c2ecf20Sopenharmony_ci	zd_usb_disable_tx(usb);
14338c2ecf20Sopenharmony_ci	zd_usb_disable_rx(usb);
14348c2ecf20Sopenharmony_ci	zd_usb_disable_int(usb);
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	/* If the disconnect has been caused by a removal of the
14378c2ecf20Sopenharmony_ci	 * driver module, the reset allows reloading of the driver. If the
14388c2ecf20Sopenharmony_ci	 * reset will not be executed here, the upload of the firmware in the
14398c2ecf20Sopenharmony_ci	 * probe function caused by the reloading of the driver will fail.
14408c2ecf20Sopenharmony_ci	 */
14418c2ecf20Sopenharmony_ci	usb_reset_device(interface_to_usbdev(intf));
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	zd_mac_clear(mac);
14448c2ecf20Sopenharmony_ci	ieee80211_free_hw(hw);
14458c2ecf20Sopenharmony_ci	dev_dbg(&intf->dev, "disconnected\n");
14468c2ecf20Sopenharmony_ci}
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_cistatic void zd_usb_resume(struct zd_usb *usb)
14498c2ecf20Sopenharmony_ci{
14508c2ecf20Sopenharmony_ci	struct zd_mac *mac = zd_usb_to_mac(usb);
14518c2ecf20Sopenharmony_ci	int r;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	dev_dbg_f(zd_usb_dev(usb), "\n");
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci	r = zd_op_start(zd_usb_to_hw(usb));
14568c2ecf20Sopenharmony_ci	if (r < 0) {
14578c2ecf20Sopenharmony_ci		dev_warn(zd_usb_dev(usb), "Device resume failed "
14588c2ecf20Sopenharmony_ci			 "with error code %d. Retrying...\n", r);
14598c2ecf20Sopenharmony_ci		if (usb->was_running)
14608c2ecf20Sopenharmony_ci			set_bit(ZD_DEVICE_RUNNING, &mac->flags);
14618c2ecf20Sopenharmony_ci		usb_queue_reset_device(usb->intf);
14628c2ecf20Sopenharmony_ci		return;
14638c2ecf20Sopenharmony_ci	}
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	if (mac->type != NL80211_IFTYPE_UNSPECIFIED) {
14668c2ecf20Sopenharmony_ci		r = zd_restore_settings(mac);
14678c2ecf20Sopenharmony_ci		if (r < 0) {
14688c2ecf20Sopenharmony_ci			dev_dbg(zd_usb_dev(usb),
14698c2ecf20Sopenharmony_ci				"failed to restore settings, %d\n", r);
14708c2ecf20Sopenharmony_ci			return;
14718c2ecf20Sopenharmony_ci		}
14728c2ecf20Sopenharmony_ci	}
14738c2ecf20Sopenharmony_ci}
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_cistatic void zd_usb_stop(struct zd_usb *usb)
14768c2ecf20Sopenharmony_ci{
14778c2ecf20Sopenharmony_ci	dev_dbg_f(zd_usb_dev(usb), "\n");
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	zd_op_stop(zd_usb_to_hw(usb));
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	zd_usb_disable_tx(usb);
14828c2ecf20Sopenharmony_ci	zd_usb_disable_rx(usb);
14838c2ecf20Sopenharmony_ci	zd_usb_disable_int(usb);
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci	usb->initialized = 0;
14868c2ecf20Sopenharmony_ci}
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_cistatic int pre_reset(struct usb_interface *intf)
14898c2ecf20Sopenharmony_ci{
14908c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = usb_get_intfdata(intf);
14918c2ecf20Sopenharmony_ci	struct zd_mac *mac;
14928c2ecf20Sopenharmony_ci	struct zd_usb *usb;
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci	if (!hw || intf->condition != USB_INTERFACE_BOUND)
14958c2ecf20Sopenharmony_ci		return 0;
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci	mac = zd_hw_mac(hw);
14988c2ecf20Sopenharmony_ci	usb = &mac->chip.usb;
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci	usb->was_running = test_bit(ZD_DEVICE_RUNNING, &mac->flags);
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_ci	zd_usb_stop(usb);
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	mutex_lock(&mac->chip.mutex);
15058c2ecf20Sopenharmony_ci	return 0;
15068c2ecf20Sopenharmony_ci}
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_cistatic int post_reset(struct usb_interface *intf)
15098c2ecf20Sopenharmony_ci{
15108c2ecf20Sopenharmony_ci	struct ieee80211_hw *hw = usb_get_intfdata(intf);
15118c2ecf20Sopenharmony_ci	struct zd_mac *mac;
15128c2ecf20Sopenharmony_ci	struct zd_usb *usb;
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	if (!hw || intf->condition != USB_INTERFACE_BOUND)
15158c2ecf20Sopenharmony_ci		return 0;
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	mac = zd_hw_mac(hw);
15188c2ecf20Sopenharmony_ci	usb = &mac->chip.usb;
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ci	mutex_unlock(&mac->chip.mutex);
15218c2ecf20Sopenharmony_ci
15228c2ecf20Sopenharmony_ci	if (usb->was_running)
15238c2ecf20Sopenharmony_ci		zd_usb_resume(usb);
15248c2ecf20Sopenharmony_ci	return 0;
15258c2ecf20Sopenharmony_ci}
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_cistatic struct usb_driver driver = {
15288c2ecf20Sopenharmony_ci	.name		= KBUILD_MODNAME,
15298c2ecf20Sopenharmony_ci	.id_table	= usb_ids,
15308c2ecf20Sopenharmony_ci	.probe		= probe,
15318c2ecf20Sopenharmony_ci	.disconnect	= disconnect,
15328c2ecf20Sopenharmony_ci	.pre_reset	= pre_reset,
15338c2ecf20Sopenharmony_ci	.post_reset	= post_reset,
15348c2ecf20Sopenharmony_ci	.disable_hub_initiated_lpm = 1,
15358c2ecf20Sopenharmony_ci};
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_cistruct workqueue_struct *zd_workqueue;
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_cistatic int __init usb_init(void)
15408c2ecf20Sopenharmony_ci{
15418c2ecf20Sopenharmony_ci	int r;
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	pr_debug("%s usb_init()\n", driver.name);
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	zd_workqueue = create_singlethread_workqueue(driver.name);
15468c2ecf20Sopenharmony_ci	if (zd_workqueue == NULL) {
15478c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s couldn't create workqueue\n", driver.name);
15488c2ecf20Sopenharmony_ci		return -ENOMEM;
15498c2ecf20Sopenharmony_ci	}
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci	r = usb_register(&driver);
15528c2ecf20Sopenharmony_ci	if (r) {
15538c2ecf20Sopenharmony_ci		destroy_workqueue(zd_workqueue);
15548c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s usb_register() failed. Error number %d\n",
15558c2ecf20Sopenharmony_ci		       driver.name, r);
15568c2ecf20Sopenharmony_ci		return r;
15578c2ecf20Sopenharmony_ci	}
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	pr_debug("%s initialized\n", driver.name);
15608c2ecf20Sopenharmony_ci	return 0;
15618c2ecf20Sopenharmony_ci}
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_cistatic void __exit usb_exit(void)
15648c2ecf20Sopenharmony_ci{
15658c2ecf20Sopenharmony_ci	pr_debug("%s usb_exit()\n", driver.name);
15668c2ecf20Sopenharmony_ci	usb_deregister(&driver);
15678c2ecf20Sopenharmony_ci	destroy_workqueue(zd_workqueue);
15688c2ecf20Sopenharmony_ci}
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_cimodule_init(usb_init);
15718c2ecf20Sopenharmony_cimodule_exit(usb_exit);
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_cistatic int zd_ep_regs_out_msg(struct usb_device *udev, void *data, int len,
15748c2ecf20Sopenharmony_ci			      int *actual_length, int timeout)
15758c2ecf20Sopenharmony_ci{
15768c2ecf20Sopenharmony_ci	/* In USB 2.0 mode EP_REGS_OUT endpoint is interrupt type. However in
15778c2ecf20Sopenharmony_ci	 * USB 1.1 mode endpoint is bulk. Select correct type URB by endpoint
15788c2ecf20Sopenharmony_ci	 * descriptor.
15798c2ecf20Sopenharmony_ci	 */
15808c2ecf20Sopenharmony_ci	struct usb_host_endpoint *ep;
15818c2ecf20Sopenharmony_ci	unsigned int pipe;
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	pipe = usb_sndintpipe(udev, EP_REGS_OUT);
15848c2ecf20Sopenharmony_ci	ep = usb_pipe_endpoint(udev, pipe);
15858c2ecf20Sopenharmony_ci	if (!ep)
15868c2ecf20Sopenharmony_ci		return -EINVAL;
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	if (usb_endpoint_xfer_int(&ep->desc)) {
15898c2ecf20Sopenharmony_ci		return usb_interrupt_msg(udev, pipe, data, len,
15908c2ecf20Sopenharmony_ci					 actual_length, timeout);
15918c2ecf20Sopenharmony_ci	} else {
15928c2ecf20Sopenharmony_ci		pipe = usb_sndbulkpipe(udev, EP_REGS_OUT);
15938c2ecf20Sopenharmony_ci		return usb_bulk_msg(udev, pipe, data, len, actual_length,
15948c2ecf20Sopenharmony_ci				    timeout);
15958c2ecf20Sopenharmony_ci	}
15968c2ecf20Sopenharmony_ci}
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_cistatic void prepare_read_regs_int(struct zd_usb *usb,
15998c2ecf20Sopenharmony_ci				  struct usb_req_read_regs *req,
16008c2ecf20Sopenharmony_ci				  unsigned int count)
16018c2ecf20Sopenharmony_ci{
16028c2ecf20Sopenharmony_ci	struct zd_usb_interrupt *intr = &usb->intr;
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	spin_lock_irq(&intr->lock);
16058c2ecf20Sopenharmony_ci	atomic_set(&intr->read_regs_enabled, 1);
16068c2ecf20Sopenharmony_ci	intr->read_regs.req = req;
16078c2ecf20Sopenharmony_ci	intr->read_regs.req_count = count;
16088c2ecf20Sopenharmony_ci	reinit_completion(&intr->read_regs.completion);
16098c2ecf20Sopenharmony_ci	spin_unlock_irq(&intr->lock);
16108c2ecf20Sopenharmony_ci}
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_cistatic void disable_read_regs_int(struct zd_usb *usb)
16138c2ecf20Sopenharmony_ci{
16148c2ecf20Sopenharmony_ci	struct zd_usb_interrupt *intr = &usb->intr;
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci	spin_lock_irq(&intr->lock);
16178c2ecf20Sopenharmony_ci	atomic_set(&intr->read_regs_enabled, 0);
16188c2ecf20Sopenharmony_ci	spin_unlock_irq(&intr->lock);
16198c2ecf20Sopenharmony_ci}
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_cistatic bool check_read_regs(struct zd_usb *usb, struct usb_req_read_regs *req,
16228c2ecf20Sopenharmony_ci			    unsigned int count)
16238c2ecf20Sopenharmony_ci{
16248c2ecf20Sopenharmony_ci	int i;
16258c2ecf20Sopenharmony_ci	struct zd_usb_interrupt *intr = &usb->intr;
16268c2ecf20Sopenharmony_ci	struct read_regs_int *rr = &intr->read_regs;
16278c2ecf20Sopenharmony_ci	struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer;
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	/* The created block size seems to be larger than expected.
16308c2ecf20Sopenharmony_ci	 * However results appear to be correct.
16318c2ecf20Sopenharmony_ci	 */
16328c2ecf20Sopenharmony_ci	if (rr->length < struct_size(regs, regs, count)) {
16338c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
16348c2ecf20Sopenharmony_ci			 "error: actual length %d less than expected %zu\n",
16358c2ecf20Sopenharmony_ci			 rr->length, struct_size(regs, regs, count));
16368c2ecf20Sopenharmony_ci		return false;
16378c2ecf20Sopenharmony_ci	}
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_ci	if (rr->length > sizeof(rr->buffer)) {
16408c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
16418c2ecf20Sopenharmony_ci			 "error: actual length %d exceeds buffer size %zu\n",
16428c2ecf20Sopenharmony_ci			 rr->length, sizeof(rr->buffer));
16438c2ecf20Sopenharmony_ci		return false;
16448c2ecf20Sopenharmony_ci	}
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
16478c2ecf20Sopenharmony_ci		struct reg_data *rd = &regs->regs[i];
16488c2ecf20Sopenharmony_ci		if (rd->addr != req->addr[i]) {
16498c2ecf20Sopenharmony_ci			dev_dbg_f(zd_usb_dev(usb),
16508c2ecf20Sopenharmony_ci				 "rd[%d] addr %#06hx expected %#06hx\n", i,
16518c2ecf20Sopenharmony_ci				 le16_to_cpu(rd->addr),
16528c2ecf20Sopenharmony_ci				 le16_to_cpu(req->addr[i]));
16538c2ecf20Sopenharmony_ci			return false;
16548c2ecf20Sopenharmony_ci		}
16558c2ecf20Sopenharmony_ci	}
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	return true;
16588c2ecf20Sopenharmony_ci}
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_cistatic int get_results(struct zd_usb *usb, u16 *values,
16618c2ecf20Sopenharmony_ci		       struct usb_req_read_regs *req, unsigned int count,
16628c2ecf20Sopenharmony_ci		       bool *retry)
16638c2ecf20Sopenharmony_ci{
16648c2ecf20Sopenharmony_ci	int r;
16658c2ecf20Sopenharmony_ci	int i;
16668c2ecf20Sopenharmony_ci	struct zd_usb_interrupt *intr = &usb->intr;
16678c2ecf20Sopenharmony_ci	struct read_regs_int *rr = &intr->read_regs;
16688c2ecf20Sopenharmony_ci	struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer;
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	spin_lock_irq(&intr->lock);
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci	r = -EIO;
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	/* Read failed because firmware bug? */
16758c2ecf20Sopenharmony_ci	*retry = !!intr->read_regs_int_overridden;
16768c2ecf20Sopenharmony_ci	if (*retry)
16778c2ecf20Sopenharmony_ci		goto error_unlock;
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	if (!check_read_regs(usb, req, count)) {
16808c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb), "error: invalid read regs\n");
16818c2ecf20Sopenharmony_ci		goto error_unlock;
16828c2ecf20Sopenharmony_ci	}
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
16858c2ecf20Sopenharmony_ci		struct reg_data *rd = &regs->regs[i];
16868c2ecf20Sopenharmony_ci		values[i] = le16_to_cpu(rd->value);
16878c2ecf20Sopenharmony_ci	}
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	r = 0;
16908c2ecf20Sopenharmony_cierror_unlock:
16918c2ecf20Sopenharmony_ci	spin_unlock_irq(&intr->lock);
16928c2ecf20Sopenharmony_ci	return r;
16938c2ecf20Sopenharmony_ci}
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ciint zd_usb_ioread16v(struct zd_usb *usb, u16 *values,
16968c2ecf20Sopenharmony_ci	             const zd_addr_t *addresses, unsigned int count)
16978c2ecf20Sopenharmony_ci{
16988c2ecf20Sopenharmony_ci	int r, i, req_len, actual_req_len, try_count = 0;
16998c2ecf20Sopenharmony_ci	struct usb_device *udev;
17008c2ecf20Sopenharmony_ci	struct usb_req_read_regs *req = NULL;
17018c2ecf20Sopenharmony_ci	unsigned long timeout;
17028c2ecf20Sopenharmony_ci	bool retry = false;
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci	if (count < 1) {
17058c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb), "error: count is zero\n");
17068c2ecf20Sopenharmony_ci		return -EINVAL;
17078c2ecf20Sopenharmony_ci	}
17088c2ecf20Sopenharmony_ci	if (count > USB_MAX_IOREAD16_COUNT) {
17098c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
17108c2ecf20Sopenharmony_ci			 "error: count %u exceeds possible max %u\n",
17118c2ecf20Sopenharmony_ci			 count, USB_MAX_IOREAD16_COUNT);
17128c2ecf20Sopenharmony_ci		return -EINVAL;
17138c2ecf20Sopenharmony_ci	}
17148c2ecf20Sopenharmony_ci	if (in_atomic()) {
17158c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
17168c2ecf20Sopenharmony_ci			 "error: io in atomic context not supported\n");
17178c2ecf20Sopenharmony_ci		return -EWOULDBLOCK;
17188c2ecf20Sopenharmony_ci	}
17198c2ecf20Sopenharmony_ci	if (!usb_int_enabled(usb)) {
17208c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
17218c2ecf20Sopenharmony_ci			  "error: usb interrupt not enabled\n");
17228c2ecf20Sopenharmony_ci		return -EWOULDBLOCK;
17238c2ecf20Sopenharmony_ci	}
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex));
17268c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct usb_req_read_regs) + USB_MAX_IOREAD16_COUNT *
17278c2ecf20Sopenharmony_ci		     sizeof(__le16) > sizeof(usb->req_buf));
17288c2ecf20Sopenharmony_ci	BUG_ON(sizeof(struct usb_req_read_regs) + count * sizeof(__le16) >
17298c2ecf20Sopenharmony_ci	       sizeof(usb->req_buf));
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci	req_len = sizeof(struct usb_req_read_regs) + count * sizeof(__le16);
17328c2ecf20Sopenharmony_ci	req = (void *)usb->req_buf;
17338c2ecf20Sopenharmony_ci
17348c2ecf20Sopenharmony_ci	req->id = cpu_to_le16(USB_REQ_READ_REGS);
17358c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++)
17368c2ecf20Sopenharmony_ci		req->addr[i] = cpu_to_le16((u16)addresses[i]);
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ciretry_read:
17398c2ecf20Sopenharmony_ci	try_count++;
17408c2ecf20Sopenharmony_ci	udev = zd_usb_to_usbdev(usb);
17418c2ecf20Sopenharmony_ci	prepare_read_regs_int(usb, req, count);
17428c2ecf20Sopenharmony_ci	r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/);
17438c2ecf20Sopenharmony_ci	if (r) {
17448c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
17458c2ecf20Sopenharmony_ci			"error in zd_ep_regs_out_msg(). Error number %d\n", r);
17468c2ecf20Sopenharmony_ci		goto error;
17478c2ecf20Sopenharmony_ci	}
17488c2ecf20Sopenharmony_ci	if (req_len != actual_req_len) {
17498c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb), "error in zd_ep_regs_out_msg()\n"
17508c2ecf20Sopenharmony_ci			" req_len %d != actual_req_len %d\n",
17518c2ecf20Sopenharmony_ci			req_len, actual_req_len);
17528c2ecf20Sopenharmony_ci		r = -EIO;
17538c2ecf20Sopenharmony_ci		goto error;
17548c2ecf20Sopenharmony_ci	}
17558c2ecf20Sopenharmony_ci
17568c2ecf20Sopenharmony_ci	timeout = wait_for_completion_timeout(&usb->intr.read_regs.completion,
17578c2ecf20Sopenharmony_ci					      msecs_to_jiffies(50));
17588c2ecf20Sopenharmony_ci	if (!timeout) {
17598c2ecf20Sopenharmony_ci		disable_read_regs_int(usb);
17608c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb), "read timed out\n");
17618c2ecf20Sopenharmony_ci		r = -ETIMEDOUT;
17628c2ecf20Sopenharmony_ci		goto error;
17638c2ecf20Sopenharmony_ci	}
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci	r = get_results(usb, values, req, count, &retry);
17668c2ecf20Sopenharmony_ci	if (retry && try_count < 20) {
17678c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb), "read retry, tries so far: %d\n",
17688c2ecf20Sopenharmony_ci				try_count);
17698c2ecf20Sopenharmony_ci		goto retry_read;
17708c2ecf20Sopenharmony_ci	}
17718c2ecf20Sopenharmony_cierror:
17728c2ecf20Sopenharmony_ci	return r;
17738c2ecf20Sopenharmony_ci}
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_cistatic void iowrite16v_urb_complete(struct urb *urb)
17768c2ecf20Sopenharmony_ci{
17778c2ecf20Sopenharmony_ci	struct zd_usb *usb = urb->context;
17788c2ecf20Sopenharmony_ci
17798c2ecf20Sopenharmony_ci	if (urb->status && !usb->cmd_error)
17808c2ecf20Sopenharmony_ci		usb->cmd_error = urb->status;
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_ci	if (!usb->cmd_error &&
17838c2ecf20Sopenharmony_ci			urb->actual_length != urb->transfer_buffer_length)
17848c2ecf20Sopenharmony_ci		usb->cmd_error = -EIO;
17858c2ecf20Sopenharmony_ci}
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_cistatic int zd_submit_waiting_urb(struct zd_usb *usb, bool last)
17888c2ecf20Sopenharmony_ci{
17898c2ecf20Sopenharmony_ci	int r = 0;
17908c2ecf20Sopenharmony_ci	struct urb *urb = usb->urb_async_waiting;
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_ci	if (!urb)
17938c2ecf20Sopenharmony_ci		return 0;
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci	usb->urb_async_waiting = NULL;
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci	if (!last)
17988c2ecf20Sopenharmony_ci		urb->transfer_flags |= URB_NO_INTERRUPT;
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci	usb_anchor_urb(urb, &usb->submitted_cmds);
18018c2ecf20Sopenharmony_ci	r = usb_submit_urb(urb, GFP_KERNEL);
18028c2ecf20Sopenharmony_ci	if (r) {
18038c2ecf20Sopenharmony_ci		usb_unanchor_urb(urb);
18048c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
18058c2ecf20Sopenharmony_ci			"error in usb_submit_urb(). Error number %d\n", r);
18068c2ecf20Sopenharmony_ci		goto error;
18078c2ecf20Sopenharmony_ci	}
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	/* fall-through with r == 0 */
18108c2ecf20Sopenharmony_cierror:
18118c2ecf20Sopenharmony_ci	usb_free_urb(urb);
18128c2ecf20Sopenharmony_ci	return r;
18138c2ecf20Sopenharmony_ci}
18148c2ecf20Sopenharmony_ci
18158c2ecf20Sopenharmony_civoid zd_usb_iowrite16v_async_start(struct zd_usb *usb)
18168c2ecf20Sopenharmony_ci{
18178c2ecf20Sopenharmony_ci	ZD_ASSERT(usb_anchor_empty(&usb->submitted_cmds));
18188c2ecf20Sopenharmony_ci	ZD_ASSERT(usb->urb_async_waiting == NULL);
18198c2ecf20Sopenharmony_ci	ZD_ASSERT(!usb->in_async);
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_ci	ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex));
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	usb->in_async = 1;
18248c2ecf20Sopenharmony_ci	usb->cmd_error = 0;
18258c2ecf20Sopenharmony_ci	usb->urb_async_waiting = NULL;
18268c2ecf20Sopenharmony_ci}
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ciint zd_usb_iowrite16v_async_end(struct zd_usb *usb, unsigned int timeout)
18298c2ecf20Sopenharmony_ci{
18308c2ecf20Sopenharmony_ci	int r;
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex));
18338c2ecf20Sopenharmony_ci	ZD_ASSERT(usb->in_async);
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	/* Submit last iowrite16v URB */
18368c2ecf20Sopenharmony_ci	r = zd_submit_waiting_urb(usb, true);
18378c2ecf20Sopenharmony_ci	if (r) {
18388c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
18398c2ecf20Sopenharmony_ci			"error in zd_submit_waiting_usb(). "
18408c2ecf20Sopenharmony_ci			"Error number %d\n", r);
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci		usb_kill_anchored_urbs(&usb->submitted_cmds);
18438c2ecf20Sopenharmony_ci		goto error;
18448c2ecf20Sopenharmony_ci	}
18458c2ecf20Sopenharmony_ci
18468c2ecf20Sopenharmony_ci	if (timeout)
18478c2ecf20Sopenharmony_ci		timeout = usb_wait_anchor_empty_timeout(&usb->submitted_cmds,
18488c2ecf20Sopenharmony_ci							timeout);
18498c2ecf20Sopenharmony_ci	if (!timeout) {
18508c2ecf20Sopenharmony_ci		usb_kill_anchored_urbs(&usb->submitted_cmds);
18518c2ecf20Sopenharmony_ci		if (usb->cmd_error == -ENOENT) {
18528c2ecf20Sopenharmony_ci			dev_dbg_f(zd_usb_dev(usb), "timed out");
18538c2ecf20Sopenharmony_ci			r = -ETIMEDOUT;
18548c2ecf20Sopenharmony_ci			goto error;
18558c2ecf20Sopenharmony_ci		}
18568c2ecf20Sopenharmony_ci	}
18578c2ecf20Sopenharmony_ci
18588c2ecf20Sopenharmony_ci	r = usb->cmd_error;
18598c2ecf20Sopenharmony_cierror:
18608c2ecf20Sopenharmony_ci	usb->in_async = 0;
18618c2ecf20Sopenharmony_ci	return r;
18628c2ecf20Sopenharmony_ci}
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ciint zd_usb_iowrite16v_async(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
18658c2ecf20Sopenharmony_ci			    unsigned int count)
18668c2ecf20Sopenharmony_ci{
18678c2ecf20Sopenharmony_ci	int r;
18688c2ecf20Sopenharmony_ci	struct usb_device *udev;
18698c2ecf20Sopenharmony_ci	struct usb_req_write_regs *req = NULL;
18708c2ecf20Sopenharmony_ci	int i, req_len;
18718c2ecf20Sopenharmony_ci	struct urb *urb;
18728c2ecf20Sopenharmony_ci	struct usb_host_endpoint *ep;
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex));
18758c2ecf20Sopenharmony_ci	ZD_ASSERT(usb->in_async);
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_ci	if (count == 0)
18788c2ecf20Sopenharmony_ci		return 0;
18798c2ecf20Sopenharmony_ci	if (count > USB_MAX_IOWRITE16_COUNT) {
18808c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
18818c2ecf20Sopenharmony_ci			"error: count %u exceeds possible max %u\n",
18828c2ecf20Sopenharmony_ci			count, USB_MAX_IOWRITE16_COUNT);
18838c2ecf20Sopenharmony_ci		return -EINVAL;
18848c2ecf20Sopenharmony_ci	}
18858c2ecf20Sopenharmony_ci	if (in_atomic()) {
18868c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
18878c2ecf20Sopenharmony_ci			"error: io in atomic context not supported\n");
18888c2ecf20Sopenharmony_ci		return -EWOULDBLOCK;
18898c2ecf20Sopenharmony_ci	}
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	udev = zd_usb_to_usbdev(usb);
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci	ep = usb_pipe_endpoint(udev, usb_sndintpipe(udev, EP_REGS_OUT));
18948c2ecf20Sopenharmony_ci	if (!ep)
18958c2ecf20Sopenharmony_ci		return -ENOENT;
18968c2ecf20Sopenharmony_ci
18978c2ecf20Sopenharmony_ci	urb = usb_alloc_urb(0, GFP_KERNEL);
18988c2ecf20Sopenharmony_ci	if (!urb)
18998c2ecf20Sopenharmony_ci		return -ENOMEM;
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_ci	req_len = struct_size(req, reg_writes, count);
19028c2ecf20Sopenharmony_ci	req = kmalloc(req_len, GFP_KERNEL);
19038c2ecf20Sopenharmony_ci	if (!req) {
19048c2ecf20Sopenharmony_ci		r = -ENOMEM;
19058c2ecf20Sopenharmony_ci		goto error;
19068c2ecf20Sopenharmony_ci	}
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci	req->id = cpu_to_le16(USB_REQ_WRITE_REGS);
19098c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
19108c2ecf20Sopenharmony_ci		struct reg_data *rw  = &req->reg_writes[i];
19118c2ecf20Sopenharmony_ci		rw->addr = cpu_to_le16((u16)ioreqs[i].addr);
19128c2ecf20Sopenharmony_ci		rw->value = cpu_to_le16(ioreqs[i].value);
19138c2ecf20Sopenharmony_ci	}
19148c2ecf20Sopenharmony_ci
19158c2ecf20Sopenharmony_ci	/* In USB 2.0 mode endpoint is interrupt type. However in USB 1.1 mode
19168c2ecf20Sopenharmony_ci	 * endpoint is bulk. Select correct type URB by endpoint descriptor.
19178c2ecf20Sopenharmony_ci	 */
19188c2ecf20Sopenharmony_ci	if (usb_endpoint_xfer_int(&ep->desc))
19198c2ecf20Sopenharmony_ci		usb_fill_int_urb(urb, udev, usb_sndintpipe(udev, EP_REGS_OUT),
19208c2ecf20Sopenharmony_ci				 req, req_len, iowrite16v_urb_complete, usb,
19218c2ecf20Sopenharmony_ci				 ep->desc.bInterval);
19228c2ecf20Sopenharmony_ci	else
19238c2ecf20Sopenharmony_ci		usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_REGS_OUT),
19248c2ecf20Sopenharmony_ci				  req, req_len, iowrite16v_urb_complete, usb);
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci	urb->transfer_flags |= URB_FREE_BUFFER;
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_ci	/* Submit previous URB */
19298c2ecf20Sopenharmony_ci	r = zd_submit_waiting_urb(usb, false);
19308c2ecf20Sopenharmony_ci	if (r) {
19318c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
19328c2ecf20Sopenharmony_ci			"error in zd_submit_waiting_usb(). "
19338c2ecf20Sopenharmony_ci			"Error number %d\n", r);
19348c2ecf20Sopenharmony_ci		goto error;
19358c2ecf20Sopenharmony_ci	}
19368c2ecf20Sopenharmony_ci
19378c2ecf20Sopenharmony_ci	/* Delay submit so that URB_NO_INTERRUPT flag can be set for all URBs
19388c2ecf20Sopenharmony_ci	 * of currect batch except for very last.
19398c2ecf20Sopenharmony_ci	 */
19408c2ecf20Sopenharmony_ci	usb->urb_async_waiting = urb;
19418c2ecf20Sopenharmony_ci	return 0;
19428c2ecf20Sopenharmony_cierror:
19438c2ecf20Sopenharmony_ci	usb_free_urb(urb);
19448c2ecf20Sopenharmony_ci	return r;
19458c2ecf20Sopenharmony_ci}
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_ciint zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
19488c2ecf20Sopenharmony_ci			unsigned int count)
19498c2ecf20Sopenharmony_ci{
19508c2ecf20Sopenharmony_ci	int r;
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_ci	zd_usb_iowrite16v_async_start(usb);
19538c2ecf20Sopenharmony_ci	r = zd_usb_iowrite16v_async(usb, ioreqs, count);
19548c2ecf20Sopenharmony_ci	if (r) {
19558c2ecf20Sopenharmony_ci		zd_usb_iowrite16v_async_end(usb, 0);
19568c2ecf20Sopenharmony_ci		return r;
19578c2ecf20Sopenharmony_ci	}
19588c2ecf20Sopenharmony_ci	return zd_usb_iowrite16v_async_end(usb, 50 /* ms */);
19598c2ecf20Sopenharmony_ci}
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ciint zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits)
19628c2ecf20Sopenharmony_ci{
19638c2ecf20Sopenharmony_ci	int r;
19648c2ecf20Sopenharmony_ci	struct usb_device *udev;
19658c2ecf20Sopenharmony_ci	struct usb_req_rfwrite *req = NULL;
19668c2ecf20Sopenharmony_ci	int i, req_len, actual_req_len;
19678c2ecf20Sopenharmony_ci	u16 bit_value_template;
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ci	if (in_atomic()) {
19708c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
19718c2ecf20Sopenharmony_ci			"error: io in atomic context not supported\n");
19728c2ecf20Sopenharmony_ci		return -EWOULDBLOCK;
19738c2ecf20Sopenharmony_ci	}
19748c2ecf20Sopenharmony_ci	if (bits < USB_MIN_RFWRITE_BIT_COUNT) {
19758c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
19768c2ecf20Sopenharmony_ci			"error: bits %d are smaller than"
19778c2ecf20Sopenharmony_ci			" USB_MIN_RFWRITE_BIT_COUNT %d\n",
19788c2ecf20Sopenharmony_ci			bits, USB_MIN_RFWRITE_BIT_COUNT);
19798c2ecf20Sopenharmony_ci		return -EINVAL;
19808c2ecf20Sopenharmony_ci	}
19818c2ecf20Sopenharmony_ci	if (bits > USB_MAX_RFWRITE_BIT_COUNT) {
19828c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
19838c2ecf20Sopenharmony_ci			"error: bits %d exceed USB_MAX_RFWRITE_BIT_COUNT %d\n",
19848c2ecf20Sopenharmony_ci			bits, USB_MAX_RFWRITE_BIT_COUNT);
19858c2ecf20Sopenharmony_ci		return -EINVAL;
19868c2ecf20Sopenharmony_ci	}
19878c2ecf20Sopenharmony_ci#ifdef DEBUG
19888c2ecf20Sopenharmony_ci	if (value & (~0UL << bits)) {
19898c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
19908c2ecf20Sopenharmony_ci			"error: value %#09x has bits >= %d set\n",
19918c2ecf20Sopenharmony_ci			value, bits);
19928c2ecf20Sopenharmony_ci		return -EINVAL;
19938c2ecf20Sopenharmony_ci	}
19948c2ecf20Sopenharmony_ci#endif /* DEBUG */
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci	dev_dbg_f(zd_usb_dev(usb), "value %#09x bits %d\n", value, bits);
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_ci	r = zd_usb_ioread16(usb, &bit_value_template, ZD_CR203);
19998c2ecf20Sopenharmony_ci	if (r) {
20008c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
20018c2ecf20Sopenharmony_ci			"error %d: Couldn't read ZD_CR203\n", r);
20028c2ecf20Sopenharmony_ci		return r;
20038c2ecf20Sopenharmony_ci	}
20048c2ecf20Sopenharmony_ci	bit_value_template &= ~(RF_IF_LE|RF_CLK|RF_DATA);
20058c2ecf20Sopenharmony_ci
20068c2ecf20Sopenharmony_ci	ZD_ASSERT(mutex_is_locked(&zd_usb_to_chip(usb)->mutex));
20078c2ecf20Sopenharmony_ci	BUILD_BUG_ON(sizeof(struct usb_req_rfwrite) +
20088c2ecf20Sopenharmony_ci		     USB_MAX_RFWRITE_BIT_COUNT * sizeof(__le16) >
20098c2ecf20Sopenharmony_ci		     sizeof(usb->req_buf));
20108c2ecf20Sopenharmony_ci	BUG_ON(sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16) >
20118c2ecf20Sopenharmony_ci	       sizeof(usb->req_buf));
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci	req_len = sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16);
20148c2ecf20Sopenharmony_ci	req = (void *)usb->req_buf;
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci	req->id = cpu_to_le16(USB_REQ_WRITE_RF);
20178c2ecf20Sopenharmony_ci	/* 1: 3683a, but not used in ZYDAS driver */
20188c2ecf20Sopenharmony_ci	req->value = cpu_to_le16(2);
20198c2ecf20Sopenharmony_ci	req->bits = cpu_to_le16(bits);
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci	for (i = 0; i < bits; i++) {
20228c2ecf20Sopenharmony_ci		u16 bv = bit_value_template;
20238c2ecf20Sopenharmony_ci		if (value & (1 << (bits-1-i)))
20248c2ecf20Sopenharmony_ci			bv |= RF_DATA;
20258c2ecf20Sopenharmony_ci		req->bit_values[i] = cpu_to_le16(bv);
20268c2ecf20Sopenharmony_ci	}
20278c2ecf20Sopenharmony_ci
20288c2ecf20Sopenharmony_ci	udev = zd_usb_to_usbdev(usb);
20298c2ecf20Sopenharmony_ci	r = zd_ep_regs_out_msg(udev, req, req_len, &actual_req_len, 50 /*ms*/);
20308c2ecf20Sopenharmony_ci	if (r) {
20318c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb),
20328c2ecf20Sopenharmony_ci			"error in zd_ep_regs_out_msg(). Error number %d\n", r);
20338c2ecf20Sopenharmony_ci		goto out;
20348c2ecf20Sopenharmony_ci	}
20358c2ecf20Sopenharmony_ci	if (req_len != actual_req_len) {
20368c2ecf20Sopenharmony_ci		dev_dbg_f(zd_usb_dev(usb), "error in zd_ep_regs_out_msg()"
20378c2ecf20Sopenharmony_ci			" req_len %d != actual_req_len %d\n",
20388c2ecf20Sopenharmony_ci			req_len, actual_req_len);
20398c2ecf20Sopenharmony_ci		r = -EIO;
20408c2ecf20Sopenharmony_ci		goto out;
20418c2ecf20Sopenharmony_ci	}
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	/* FALL-THROUGH with r == 0 */
20448c2ecf20Sopenharmony_ciout:
20458c2ecf20Sopenharmony_ci	return r;
20468c2ecf20Sopenharmony_ci}
2047