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 = ®s->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 = ®s->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