18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This file contains functions used in USB interface module. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/firmware.h> 118c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/usb.h> 148c2ecf20Sopenharmony_ci#include <linux/olpc-ec.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#ifdef CONFIG_OLPC 178c2ecf20Sopenharmony_ci#include <asm/olpc.h> 188c2ecf20Sopenharmony_ci#endif 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define DRV_NAME "usb8xxx" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "host.h" 238c2ecf20Sopenharmony_ci#include "decl.h" 248c2ecf20Sopenharmony_ci#include "defs.h" 258c2ecf20Sopenharmony_ci#include "dev.h" 268c2ecf20Sopenharmony_ci#include "cmd.h" 278c2ecf20Sopenharmony_ci#include "if_usb.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define INSANEDEBUG 0 308c2ecf20Sopenharmony_ci#define lbs_deb_usb2(...) do { if (INSANEDEBUG) lbs_deb_usbd(__VA_ARGS__); } while (0) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define MESSAGE_HEADER_LEN 4 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/usb8388_v9.bin"); 358c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/usb8388_v5.bin"); 368c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/usb8388.bin"); 378c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/usb8682.bin"); 388c2ecf20Sopenharmony_ciMODULE_FIRMWARE("usb8388.bin"); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cienum { 418c2ecf20Sopenharmony_ci MODEL_UNKNOWN = 0x0, 428c2ecf20Sopenharmony_ci MODEL_8388 = 0x1, 438c2ecf20Sopenharmony_ci MODEL_8682 = 0x2 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* table of firmware file names */ 478c2ecf20Sopenharmony_cistatic const struct lbs_fw_table fw_table[] = { 488c2ecf20Sopenharmony_ci { MODEL_8388, "libertas/usb8388_olpc.bin", NULL }, 498c2ecf20Sopenharmony_ci { MODEL_8388, "libertas/usb8388_v9.bin", NULL }, 508c2ecf20Sopenharmony_ci { MODEL_8388, "libertas/usb8388_v5.bin", NULL }, 518c2ecf20Sopenharmony_ci { MODEL_8388, "libertas/usb8388.bin", NULL }, 528c2ecf20Sopenharmony_ci { MODEL_8388, "usb8388.bin", NULL }, 538c2ecf20Sopenharmony_ci { MODEL_8682, "libertas/usb8682.bin", NULL }, 548c2ecf20Sopenharmony_ci { 0, NULL, NULL } 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic const struct usb_device_id if_usb_table[] = { 588c2ecf20Sopenharmony_ci /* Enter the device signature inside */ 598c2ecf20Sopenharmony_ci { USB_DEVICE(0x1286, 0x2001), .driver_info = MODEL_8388 }, 608c2ecf20Sopenharmony_ci { USB_DEVICE(0x05a3, 0x8388), .driver_info = MODEL_8388 }, 618c2ecf20Sopenharmony_ci {} /* Terminating entry */ 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, if_usb_table); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic void if_usb_receive(struct urb *urb); 678c2ecf20Sopenharmony_cistatic void if_usb_receive_fwload(struct urb *urb); 688c2ecf20Sopenharmony_cistatic void if_usb_prog_firmware(struct lbs_private *priv, int ret, 698c2ecf20Sopenharmony_ci const struct firmware *fw, 708c2ecf20Sopenharmony_ci const struct firmware *unused); 718c2ecf20Sopenharmony_cistatic int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, 728c2ecf20Sopenharmony_ci uint8_t *payload, uint16_t nb); 738c2ecf20Sopenharmony_cistatic int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, 748c2ecf20Sopenharmony_ci uint16_t nb); 758c2ecf20Sopenharmony_cistatic void if_usb_free(struct if_usb_card *cardp); 768c2ecf20Sopenharmony_cistatic int if_usb_submit_rx_urb(struct if_usb_card *cardp); 778c2ecf20Sopenharmony_cistatic int if_usb_reset_device(struct if_usb_card *cardp); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/** 808c2ecf20Sopenharmony_ci * if_usb_write_bulk_callback - callback function to handle the status 818c2ecf20Sopenharmony_ci * of the URB 828c2ecf20Sopenharmony_ci * @urb: pointer to &urb structure 838c2ecf20Sopenharmony_ci * returns: N/A 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_cistatic void if_usb_write_bulk_callback(struct urb *urb) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct if_usb_card *cardp = (struct if_usb_card *) urb->context; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* handle the transmission complete validations */ 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (urb->status == 0) { 928c2ecf20Sopenharmony_ci struct lbs_private *priv = cardp->priv; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci lbs_deb_usb2(&urb->dev->dev, "URB status is successful\n"); 958c2ecf20Sopenharmony_ci lbs_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n", 968c2ecf20Sopenharmony_ci urb->actual_length); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* Boot commands such as UPDATE_FW and UPDATE_BOOT2 are not 998c2ecf20Sopenharmony_ci * passed up to the lbs level. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci if (priv && priv->dnld_sent != DNLD_BOOTCMD_SENT) 1028c2ecf20Sopenharmony_ci lbs_host_to_card_done(priv); 1038c2ecf20Sopenharmony_ci } else { 1048c2ecf20Sopenharmony_ci /* print the failure status number for debug */ 1058c2ecf20Sopenharmony_ci pr_info("URB in failure status: %d\n", urb->status); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/** 1108c2ecf20Sopenharmony_ci * if_usb_free - free tx/rx urb, skb and rx buffer 1118c2ecf20Sopenharmony_ci * @cardp: pointer to &if_usb_card 1128c2ecf20Sopenharmony_ci * returns: N/A 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_cistatic void if_usb_free(struct if_usb_card *cardp) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci /* Unlink tx & rx urb */ 1178c2ecf20Sopenharmony_ci usb_kill_urb(cardp->tx_urb); 1188c2ecf20Sopenharmony_ci usb_kill_urb(cardp->rx_urb); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci usb_free_urb(cardp->tx_urb); 1218c2ecf20Sopenharmony_ci cardp->tx_urb = NULL; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci usb_free_urb(cardp->rx_urb); 1248c2ecf20Sopenharmony_ci cardp->rx_urb = NULL; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci kfree(cardp->ep_out_buf); 1278c2ecf20Sopenharmony_ci cardp->ep_out_buf = NULL; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void if_usb_setup_firmware(struct lbs_private *priv) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct if_usb_card *cardp = priv->card; 1338c2ecf20Sopenharmony_ci struct cmd_ds_set_boot2_ver b2_cmd; 1348c2ecf20Sopenharmony_ci struct cmd_ds_802_11_fw_wake_method wake_method; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd)); 1378c2ecf20Sopenharmony_ci b2_cmd.action = 0; 1388c2ecf20Sopenharmony_ci b2_cmd.version = cardp->boot2_version; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (lbs_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd)) 1418c2ecf20Sopenharmony_ci lbs_deb_usb("Setting boot2 version failed\n"); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci priv->wol_gpio = 2; /* Wake via GPIO2... */ 1448c2ecf20Sopenharmony_ci priv->wol_gap = 20; /* ... after 20ms */ 1458c2ecf20Sopenharmony_ci lbs_host_sleep_cfg(priv, EHS_WAKE_ON_UNICAST_DATA, 1468c2ecf20Sopenharmony_ci (struct wol_config *) NULL); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci wake_method.hdr.size = cpu_to_le16(sizeof(wake_method)); 1498c2ecf20Sopenharmony_ci wake_method.action = cpu_to_le16(CMD_ACT_GET); 1508c2ecf20Sopenharmony_ci if (lbs_cmd_with_response(priv, CMD_802_11_FW_WAKE_METHOD, &wake_method)) { 1518c2ecf20Sopenharmony_ci netdev_info(priv->dev, "Firmware does not seem to support PS mode\n"); 1528c2ecf20Sopenharmony_ci priv->fwcapinfo &= ~FW_CAPINFO_PS; 1538c2ecf20Sopenharmony_ci } else { 1548c2ecf20Sopenharmony_ci if (le16_to_cpu(wake_method.method) == CMD_WAKE_METHOD_COMMAND_INT) { 1558c2ecf20Sopenharmony_ci lbs_deb_usb("Firmware seems to support PS with wake-via-command\n"); 1568c2ecf20Sopenharmony_ci } else { 1578c2ecf20Sopenharmony_ci /* The versions which boot up this way don't seem to 1588c2ecf20Sopenharmony_ci work even if we set it to the command interrupt */ 1598c2ecf20Sopenharmony_ci priv->fwcapinfo &= ~FW_CAPINFO_PS; 1608c2ecf20Sopenharmony_ci netdev_info(priv->dev, 1618c2ecf20Sopenharmony_ci "Firmware doesn't wake via command interrupt; disabling PS mode\n"); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic void if_usb_fw_timeo(struct timer_list *t) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct if_usb_card *cardp = from_timer(cardp, t, fw_timeout); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (cardp->fwdnldover) { 1718c2ecf20Sopenharmony_ci lbs_deb_usb("Download complete, no event. Assuming success\n"); 1728c2ecf20Sopenharmony_ci } else { 1738c2ecf20Sopenharmony_ci pr_err("Download timed out\n"); 1748c2ecf20Sopenharmony_ci cardp->surprise_removed = 1; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci wake_up(&cardp->fw_wq); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci#ifdef CONFIG_OLPC 1808c2ecf20Sopenharmony_cistatic void if_usb_reset_olpc_card(struct lbs_private *priv) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci printk(KERN_CRIT "Resetting OLPC wireless via EC...\n"); 1838c2ecf20Sopenharmony_ci olpc_ec_cmd(0x25, NULL, 0, NULL, 0); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci#endif 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/** 1888c2ecf20Sopenharmony_ci * if_usb_probe - sets the configuration values 1898c2ecf20Sopenharmony_ci * @intf: &usb_interface pointer 1908c2ecf20Sopenharmony_ci * @id: pointer to usb_device_id 1918c2ecf20Sopenharmony_ci * returns: 0 on success, error code on failure 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_cistatic int if_usb_probe(struct usb_interface *intf, 1948c2ecf20Sopenharmony_ci const struct usb_device_id *id) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct usb_device *udev; 1978c2ecf20Sopenharmony_ci struct usb_host_interface *iface_desc; 1988c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 1998c2ecf20Sopenharmony_ci struct lbs_private *priv; 2008c2ecf20Sopenharmony_ci struct if_usb_card *cardp; 2018c2ecf20Sopenharmony_ci int r = -ENOMEM; 2028c2ecf20Sopenharmony_ci int i; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci udev = interface_to_usbdev(intf); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL); 2078c2ecf20Sopenharmony_ci if (!cardp) 2088c2ecf20Sopenharmony_ci goto error; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci timer_setup(&cardp->fw_timeout, if_usb_fw_timeo, 0); 2118c2ecf20Sopenharmony_ci init_waitqueue_head(&cardp->fw_wq); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci cardp->udev = udev; 2148c2ecf20Sopenharmony_ci cardp->model = (uint32_t) id->driver_info; 2158c2ecf20Sopenharmony_ci iface_desc = intf->cur_altsetting; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci lbs_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" 2188c2ecf20Sopenharmony_ci " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", 2198c2ecf20Sopenharmony_ci le16_to_cpu(udev->descriptor.bcdUSB), 2208c2ecf20Sopenharmony_ci udev->descriptor.bDeviceClass, 2218c2ecf20Sopenharmony_ci udev->descriptor.bDeviceSubClass, 2228c2ecf20Sopenharmony_ci udev->descriptor.bDeviceProtocol); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { 2258c2ecf20Sopenharmony_ci endpoint = &iface_desc->endpoint[i].desc; 2268c2ecf20Sopenharmony_ci if (usb_endpoint_is_bulk_in(endpoint)) { 2278c2ecf20Sopenharmony_ci cardp->ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize); 2288c2ecf20Sopenharmony_ci cardp->ep_in = usb_endpoint_num(endpoint); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci lbs_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in); 2318c2ecf20Sopenharmony_ci lbs_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci } else if (usb_endpoint_is_bulk_out(endpoint)) { 2348c2ecf20Sopenharmony_ci cardp->ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize); 2358c2ecf20Sopenharmony_ci cardp->ep_out = usb_endpoint_num(endpoint); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci lbs_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out); 2388c2ecf20Sopenharmony_ci lbs_deb_usbd(&udev->dev, "Bulk out size is %d\n", cardp->ep_out_size); 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci if (!cardp->ep_out_size || !cardp->ep_in_size) { 2428c2ecf20Sopenharmony_ci lbs_deb_usbd(&udev->dev, "Endpoints not found\n"); 2438c2ecf20Sopenharmony_ci goto dealloc; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci if (!(cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL))) { 2468c2ecf20Sopenharmony_ci lbs_deb_usbd(&udev->dev, "Rx URB allocation failed\n"); 2478c2ecf20Sopenharmony_ci goto dealloc; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci if (!(cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL))) { 2508c2ecf20Sopenharmony_ci lbs_deb_usbd(&udev->dev, "Tx URB allocation failed\n"); 2518c2ecf20Sopenharmony_ci goto dealloc; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, GFP_KERNEL); 2548c2ecf20Sopenharmony_ci if (!cardp->ep_out_buf) { 2558c2ecf20Sopenharmony_ci lbs_deb_usbd(&udev->dev, "Could not allocate buffer\n"); 2568c2ecf20Sopenharmony_ci goto dealloc; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci priv = lbs_add_card(cardp, &intf->dev); 2608c2ecf20Sopenharmony_ci if (IS_ERR(priv)) { 2618c2ecf20Sopenharmony_ci r = PTR_ERR(priv); 2628c2ecf20Sopenharmony_ci goto err_add_card; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci cardp->priv = priv; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci priv->hw_host_to_card = if_usb_host_to_card; 2688c2ecf20Sopenharmony_ci priv->enter_deep_sleep = NULL; 2698c2ecf20Sopenharmony_ci priv->exit_deep_sleep = NULL; 2708c2ecf20Sopenharmony_ci priv->reset_deep_sleep_wakeup = NULL; 2718c2ecf20Sopenharmony_ci priv->is_polling = false; 2728c2ecf20Sopenharmony_ci#ifdef CONFIG_OLPC 2738c2ecf20Sopenharmony_ci if (machine_is_olpc()) 2748c2ecf20Sopenharmony_ci priv->reset_card = if_usb_reset_olpc_card; 2758c2ecf20Sopenharmony_ci#endif 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci cardp->boot2_version = udev->descriptor.bcdDevice; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci usb_get_dev(udev); 2808c2ecf20Sopenharmony_ci usb_set_intfdata(intf, cardp); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci r = lbs_get_firmware_async(priv, &udev->dev, cardp->model, 2838c2ecf20Sopenharmony_ci fw_table, if_usb_prog_firmware); 2848c2ecf20Sopenharmony_ci if (r) 2858c2ecf20Sopenharmony_ci goto err_get_fw; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cierr_get_fw: 2908c2ecf20Sopenharmony_ci usb_put_dev(udev); 2918c2ecf20Sopenharmony_ci lbs_remove_card(priv); 2928c2ecf20Sopenharmony_cierr_add_card: 2938c2ecf20Sopenharmony_ci if_usb_reset_device(cardp); 2948c2ecf20Sopenharmony_cidealloc: 2958c2ecf20Sopenharmony_ci if_usb_free(cardp); 2968c2ecf20Sopenharmony_ci kfree(cardp); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cierror: 2998c2ecf20Sopenharmony_ci return r; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/** 3038c2ecf20Sopenharmony_ci * if_usb_disconnect - free resource and cleanup 3048c2ecf20Sopenharmony_ci * @intf: USB interface structure 3058c2ecf20Sopenharmony_ci * returns: N/A 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_cistatic void if_usb_disconnect(struct usb_interface *intf) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci struct if_usb_card *cardp = usb_get_intfdata(intf); 3108c2ecf20Sopenharmony_ci struct lbs_private *priv = cardp->priv; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci cardp->surprise_removed = 1; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (priv) { 3158c2ecf20Sopenharmony_ci lbs_stop_card(priv); 3168c2ecf20Sopenharmony_ci lbs_remove_card(priv); 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* Unlink and free urb */ 3208c2ecf20Sopenharmony_ci if_usb_free(cardp); 3218c2ecf20Sopenharmony_ci kfree(cardp); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 3248c2ecf20Sopenharmony_ci usb_put_dev(interface_to_usbdev(intf)); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/** 3288c2ecf20Sopenharmony_ci * if_usb_send_fw_pkt - download FW 3298c2ecf20Sopenharmony_ci * @cardp: pointer to &struct if_usb_card 3308c2ecf20Sopenharmony_ci * returns: 0 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_cistatic int if_usb_send_fw_pkt(struct if_usb_card *cardp) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct fwdata *fwdata = cardp->ep_out_buf; 3358c2ecf20Sopenharmony_ci const uint8_t *firmware = cardp->fw->data; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* If we got a CRC failure on the last block, back 3388c2ecf20Sopenharmony_ci up and retry it */ 3398c2ecf20Sopenharmony_ci if (!cardp->CRC_OK) { 3408c2ecf20Sopenharmony_ci cardp->totalbytes = cardp->fwlastblksent; 3418c2ecf20Sopenharmony_ci cardp->fwseqnum--; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n", 3458c2ecf20Sopenharmony_ci cardp->totalbytes); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* struct fwdata (which we sent to the card) has an 3488c2ecf20Sopenharmony_ci extra __le32 field in between the header and the data, 3498c2ecf20Sopenharmony_ci which is not in the struct fwheader in the actual 3508c2ecf20Sopenharmony_ci firmware binary. Insert the seqnum in the middle... */ 3518c2ecf20Sopenharmony_ci memcpy(&fwdata->hdr, &firmware[cardp->totalbytes], 3528c2ecf20Sopenharmony_ci sizeof(struct fwheader)); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci cardp->fwlastblksent = cardp->totalbytes; 3558c2ecf20Sopenharmony_ci cardp->totalbytes += sizeof(struct fwheader); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci memcpy(fwdata->data, &firmware[cardp->totalbytes], 3588c2ecf20Sopenharmony_ci le32_to_cpu(fwdata->hdr.datalength)); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "Data length = %d\n", 3618c2ecf20Sopenharmony_ci le32_to_cpu(fwdata->hdr.datalength)); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum); 3648c2ecf20Sopenharmony_ci cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) + 3678c2ecf20Sopenharmony_ci le32_to_cpu(fwdata->hdr.datalength)); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) { 3708c2ecf20Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "There are data to follow\n"); 3718c2ecf20Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "seqnum = %d totalbytes = %d\n", 3728c2ecf20Sopenharmony_ci cardp->fwseqnum, cardp->totalbytes); 3738c2ecf20Sopenharmony_ci } else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) { 3748c2ecf20Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "Host has finished FW downloading\n"); 3758c2ecf20Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "Downloading FW JUMP BLOCK\n"); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci cardp->fwfinalblk = 1; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n", 3818c2ecf20Sopenharmony_ci cardp->totalbytes); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic int if_usb_reset_device(struct if_usb_card *cardp) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct cmd_header *cmd = cardp->ep_out_buf + 4; 3898c2ecf20Sopenharmony_ci int ret; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci cmd->command = cpu_to_le16(CMD_802_11_RESET); 3948c2ecf20Sopenharmony_ci cmd->size = cpu_to_le16(sizeof(cmd)); 3958c2ecf20Sopenharmony_ci cmd->result = cpu_to_le16(0); 3968c2ecf20Sopenharmony_ci cmd->seqnum = cpu_to_le16(0x5a5a); 3978c2ecf20Sopenharmony_ci usb_tx_block(cardp, cardp->ep_out_buf, 4 + sizeof(struct cmd_header)); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci msleep(100); 4008c2ecf20Sopenharmony_ci ret = usb_reset_device(cardp->udev); 4018c2ecf20Sopenharmony_ci msleep(100); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci#ifdef CONFIG_OLPC 4048c2ecf20Sopenharmony_ci if (ret && machine_is_olpc()) 4058c2ecf20Sopenharmony_ci if_usb_reset_olpc_card(NULL); 4068c2ecf20Sopenharmony_ci#endif 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return ret; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci/** 4128c2ecf20Sopenharmony_ci * usb_tx_block - transfer the data to the device 4138c2ecf20Sopenharmony_ci * @cardp: pointer to &struct if_usb_card 4148c2ecf20Sopenharmony_ci * @payload: pointer to payload data 4158c2ecf20Sopenharmony_ci * @nb: data length 4168c2ecf20Sopenharmony_ci * returns: 0 for success or negative error code 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_cistatic int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci int ret; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* check if device is removed */ 4238c2ecf20Sopenharmony_ci if (cardp->surprise_removed) { 4248c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "Device removed\n"); 4258c2ecf20Sopenharmony_ci ret = -ENODEV; 4268c2ecf20Sopenharmony_ci goto tx_ret; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci usb_fill_bulk_urb(cardp->tx_urb, cardp->udev, 4308c2ecf20Sopenharmony_ci usb_sndbulkpipe(cardp->udev, 4318c2ecf20Sopenharmony_ci cardp->ep_out), 4328c2ecf20Sopenharmony_ci payload, nb, if_usb_write_bulk_callback, cardp); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) { 4378c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret); 4388c2ecf20Sopenharmony_ci } else { 4398c2ecf20Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n"); 4408c2ecf20Sopenharmony_ci ret = 0; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_citx_ret: 4448c2ecf20Sopenharmony_ci return ret; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic int __if_usb_submit_rx_urb(struct if_usb_card *cardp, 4488c2ecf20Sopenharmony_ci void (*callbackfn)(struct urb *urb)) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci struct sk_buff *skb; 4518c2ecf20Sopenharmony_ci int ret = -1; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) { 4548c2ecf20Sopenharmony_ci pr_err("No free skb\n"); 4558c2ecf20Sopenharmony_ci goto rx_ret; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci cardp->rx_skb = skb; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* Fill the receive configuration URB and initialise the Rx call back */ 4618c2ecf20Sopenharmony_ci usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, 4628c2ecf20Sopenharmony_ci usb_rcvbulkpipe(cardp->udev, cardp->ep_in), 4638c2ecf20Sopenharmony_ci skb->data + IPFIELD_ALIGN_OFFSET, 4648c2ecf20Sopenharmony_ci MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, 4658c2ecf20Sopenharmony_ci cardp); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); 4688c2ecf20Sopenharmony_ci if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) { 4698c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "Submit Rx URB failed: %d\n", ret); 4708c2ecf20Sopenharmony_ci kfree_skb(skb); 4718c2ecf20Sopenharmony_ci cardp->rx_skb = NULL; 4728c2ecf20Sopenharmony_ci ret = -1; 4738c2ecf20Sopenharmony_ci } else { 4748c2ecf20Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n"); 4758c2ecf20Sopenharmony_ci ret = 0; 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cirx_ret: 4798c2ecf20Sopenharmony_ci return ret; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int if_usb_submit_rx_urb(struct if_usb_card *cardp) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci return __if_usb_submit_rx_urb(cardp, &if_usb_receive); 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cistatic void if_usb_receive_fwload(struct urb *urb) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci struct if_usb_card *cardp = urb->context; 4958c2ecf20Sopenharmony_ci struct sk_buff *skb = cardp->rx_skb; 4968c2ecf20Sopenharmony_ci struct fwsyncheader *syncfwheader; 4978c2ecf20Sopenharmony_ci struct bootcmdresp bootcmdresp; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (urb->status) { 5008c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, 5018c2ecf20Sopenharmony_ci "URB status is failed during fw load\n"); 5028c2ecf20Sopenharmony_ci kfree_skb(skb); 5038c2ecf20Sopenharmony_ci return; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (cardp->fwdnldover) { 5078c2ecf20Sopenharmony_ci __le32 *tmp = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) && 5108c2ecf20Sopenharmony_ci tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) { 5118c2ecf20Sopenharmony_ci pr_info("Firmware ready event received\n"); 5128c2ecf20Sopenharmony_ci wake_up(&cardp->fw_wq); 5138c2ecf20Sopenharmony_ci } else { 5148c2ecf20Sopenharmony_ci lbs_deb_usb("Waiting for confirmation; got %x %x\n", 5158c2ecf20Sopenharmony_ci le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1])); 5168c2ecf20Sopenharmony_ci if_usb_submit_rx_urb_fwload(cardp); 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci kfree_skb(skb); 5198c2ecf20Sopenharmony_ci return; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci if (cardp->bootcmdresp <= 0) { 5228c2ecf20Sopenharmony_ci memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET, 5238c2ecf20Sopenharmony_ci sizeof(bootcmdresp)); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { 5268c2ecf20Sopenharmony_ci kfree_skb(skb); 5278c2ecf20Sopenharmony_ci if_usb_submit_rx_urb_fwload(cardp); 5288c2ecf20Sopenharmony_ci cardp->bootcmdresp = BOOT_CMD_RESP_OK; 5298c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, 5308c2ecf20Sopenharmony_ci "Received valid boot command response\n"); 5318c2ecf20Sopenharmony_ci return; 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci if (bootcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) { 5348c2ecf20Sopenharmony_ci if (bootcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) || 5358c2ecf20Sopenharmony_ci bootcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) || 5368c2ecf20Sopenharmony_ci bootcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) { 5378c2ecf20Sopenharmony_ci if (!cardp->bootcmdresp) 5388c2ecf20Sopenharmony_ci pr_info("Firmware already seems alive; resetting\n"); 5398c2ecf20Sopenharmony_ci cardp->bootcmdresp = -1; 5408c2ecf20Sopenharmony_ci } else { 5418c2ecf20Sopenharmony_ci pr_info("boot cmd response wrong magic number (0x%x)\n", 5428c2ecf20Sopenharmony_ci le32_to_cpu(bootcmdresp.magic)); 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci } else if ((bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) && 5458c2ecf20Sopenharmony_ci (bootcmdresp.cmd != BOOT_CMD_UPDATE_FW) && 5468c2ecf20Sopenharmony_ci (bootcmdresp.cmd != BOOT_CMD_UPDATE_BOOT2)) { 5478c2ecf20Sopenharmony_ci pr_info("boot cmd response cmd_tag error (%d)\n", 5488c2ecf20Sopenharmony_ci bootcmdresp.cmd); 5498c2ecf20Sopenharmony_ci } else if (bootcmdresp.result != BOOT_CMD_RESP_OK) { 5508c2ecf20Sopenharmony_ci pr_info("boot cmd response result error (%d)\n", 5518c2ecf20Sopenharmony_ci bootcmdresp.result); 5528c2ecf20Sopenharmony_ci } else { 5538c2ecf20Sopenharmony_ci cardp->bootcmdresp = 1; 5548c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, 5558c2ecf20Sopenharmony_ci "Received valid boot command response\n"); 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci kfree_skb(skb); 5588c2ecf20Sopenharmony_ci if_usb_submit_rx_urb_fwload(cardp); 5598c2ecf20Sopenharmony_ci return; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci syncfwheader = kmemdup(skb->data + IPFIELD_ALIGN_OFFSET, 5638c2ecf20Sopenharmony_ci sizeof(struct fwsyncheader), GFP_ATOMIC); 5648c2ecf20Sopenharmony_ci if (!syncfwheader) { 5658c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "Failure to allocate syncfwheader\n"); 5668c2ecf20Sopenharmony_ci kfree_skb(skb); 5678c2ecf20Sopenharmony_ci return; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (!syncfwheader->cmd) { 5718c2ecf20Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n"); 5728c2ecf20Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n", 5738c2ecf20Sopenharmony_ci le32_to_cpu(syncfwheader->seqnum)); 5748c2ecf20Sopenharmony_ci cardp->CRC_OK = 1; 5758c2ecf20Sopenharmony_ci } else { 5768c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "FW received Blk with CRC error\n"); 5778c2ecf20Sopenharmony_ci cardp->CRC_OK = 0; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci kfree_skb(skb); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* Give device 5s to either write firmware to its RAM or eeprom */ 5838c2ecf20Sopenharmony_ci mod_timer(&cardp->fw_timeout, jiffies + (HZ*5)); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci if (cardp->fwfinalblk) { 5868c2ecf20Sopenharmony_ci cardp->fwdnldover = 1; 5878c2ecf20Sopenharmony_ci goto exit; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if_usb_send_fw_pkt(cardp); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci exit: 5938c2ecf20Sopenharmony_ci if_usb_submit_rx_urb_fwload(cardp); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci kfree(syncfwheader); 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci#define MRVDRV_MIN_PKT_LEN 30 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cistatic inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, 6018c2ecf20Sopenharmony_ci struct if_usb_card *cardp, 6028c2ecf20Sopenharmony_ci struct lbs_private *priv) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN 6058c2ecf20Sopenharmony_ci || recvlength < MRVDRV_MIN_PKT_LEN) { 6068c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n"); 6078c2ecf20Sopenharmony_ci kfree_skb(skb); 6088c2ecf20Sopenharmony_ci return; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci skb_reserve(skb, IPFIELD_ALIGN_OFFSET); 6128c2ecf20Sopenharmony_ci skb_put(skb, recvlength); 6138c2ecf20Sopenharmony_ci skb_pull(skb, MESSAGE_HEADER_LEN); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci lbs_process_rxed_packet(priv, skb); 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic inline void process_cmdrequest(int recvlength, uint8_t *recvbuff, 6198c2ecf20Sopenharmony_ci struct sk_buff *skb, 6208c2ecf20Sopenharmony_ci struct if_usb_card *cardp, 6218c2ecf20Sopenharmony_ci struct lbs_private *priv) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci unsigned long flags; 6248c2ecf20Sopenharmony_ci u8 i; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (recvlength > LBS_CMD_BUFFER_SIZE) { 6278c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, 6288c2ecf20Sopenharmony_ci "The receive buffer is too large\n"); 6298c2ecf20Sopenharmony_ci kfree_skb(skb); 6308c2ecf20Sopenharmony_ci return; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci i = (priv->resp_idx == 0) ? 1 : 0; 6368c2ecf20Sopenharmony_ci BUG_ON(priv->resp_len[i]); 6378c2ecf20Sopenharmony_ci priv->resp_len[i] = (recvlength - MESSAGE_HEADER_LEN); 6388c2ecf20Sopenharmony_ci memcpy(priv->resp_buf[i], recvbuff + MESSAGE_HEADER_LEN, 6398c2ecf20Sopenharmony_ci priv->resp_len[i]); 6408c2ecf20Sopenharmony_ci dev_kfree_skb_irq(skb); 6418c2ecf20Sopenharmony_ci lbs_notify_command_response(priv, i); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, 6468c2ecf20Sopenharmony_ci "Wake up main thread to handle cmd response\n"); 6478c2ecf20Sopenharmony_ci} 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci/** 6508c2ecf20Sopenharmony_ci * if_usb_receive - read the packet into the upload buffer, 6518c2ecf20Sopenharmony_ci * wake up the main thread and initialise the Rx callack 6528c2ecf20Sopenharmony_ci * 6538c2ecf20Sopenharmony_ci * @urb: pointer to &struct urb 6548c2ecf20Sopenharmony_ci * returns: N/A 6558c2ecf20Sopenharmony_ci */ 6568c2ecf20Sopenharmony_cistatic void if_usb_receive(struct urb *urb) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci struct if_usb_card *cardp = urb->context; 6598c2ecf20Sopenharmony_ci struct sk_buff *skb = cardp->rx_skb; 6608c2ecf20Sopenharmony_ci struct lbs_private *priv = cardp->priv; 6618c2ecf20Sopenharmony_ci int recvlength = urb->actual_length; 6628c2ecf20Sopenharmony_ci uint8_t *recvbuff = NULL; 6638c2ecf20Sopenharmony_ci uint32_t recvtype = 0; 6648c2ecf20Sopenharmony_ci __le32 *pkt = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET); 6658c2ecf20Sopenharmony_ci uint32_t event; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (recvlength) { 6688c2ecf20Sopenharmony_ci if (urb->status) { 6698c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n", 6708c2ecf20Sopenharmony_ci urb->status); 6718c2ecf20Sopenharmony_ci kfree_skb(skb); 6728c2ecf20Sopenharmony_ci goto setup_for_next; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci recvbuff = skb->data + IPFIELD_ALIGN_OFFSET; 6768c2ecf20Sopenharmony_ci recvtype = le32_to_cpu(pkt[0]); 6778c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, 6788c2ecf20Sopenharmony_ci "Recv length = 0x%x, Recv type = 0x%X\n", 6798c2ecf20Sopenharmony_ci recvlength, recvtype); 6808c2ecf20Sopenharmony_ci } else if (urb->status) { 6818c2ecf20Sopenharmony_ci kfree_skb(skb); 6828c2ecf20Sopenharmony_ci return; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci switch (recvtype) { 6868c2ecf20Sopenharmony_ci case CMD_TYPE_DATA: 6878c2ecf20Sopenharmony_ci process_cmdtypedata(recvlength, skb, cardp, priv); 6888c2ecf20Sopenharmony_ci break; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci case CMD_TYPE_REQUEST: 6918c2ecf20Sopenharmony_ci process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci case CMD_TYPE_INDICATION: 6958c2ecf20Sopenharmony_ci /* Event handling */ 6968c2ecf20Sopenharmony_ci event = le32_to_cpu(pkt[1]); 6978c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n", event); 6988c2ecf20Sopenharmony_ci kfree_skb(skb); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* Icky undocumented magic special case */ 7018c2ecf20Sopenharmony_ci if (event & 0xffff0000) { 7028c2ecf20Sopenharmony_ci u32 trycount = (event & 0xffff0000) >> 16; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci lbs_send_tx_feedback(priv, trycount); 7058c2ecf20Sopenharmony_ci } else 7068c2ecf20Sopenharmony_ci lbs_queue_event(priv, event & 0xFF); 7078c2ecf20Sopenharmony_ci break; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci default: 7108c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "Unknown command type 0x%X\n", 7118c2ecf20Sopenharmony_ci recvtype); 7128c2ecf20Sopenharmony_ci kfree_skb(skb); 7138c2ecf20Sopenharmony_ci break; 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cisetup_for_next: 7178c2ecf20Sopenharmony_ci if_usb_submit_rx_urb(cardp); 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci/** 7218c2ecf20Sopenharmony_ci * if_usb_host_to_card - downloads data to FW 7228c2ecf20Sopenharmony_ci * @priv: pointer to &struct lbs_private structure 7238c2ecf20Sopenharmony_ci * @type: type of data 7248c2ecf20Sopenharmony_ci * @payload: pointer to data buffer 7258c2ecf20Sopenharmony_ci * @nb: number of bytes 7268c2ecf20Sopenharmony_ci * returns: 0 for success or negative error code 7278c2ecf20Sopenharmony_ci */ 7288c2ecf20Sopenharmony_cistatic int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, 7298c2ecf20Sopenharmony_ci uint8_t *payload, uint16_t nb) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci struct if_usb_card *cardp = priv->card; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev,"*** type = %u\n", type); 7348c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev,"size after = %d\n", nb); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (type == MVMS_CMD) { 7378c2ecf20Sopenharmony_ci *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); 7388c2ecf20Sopenharmony_ci priv->dnld_sent = DNLD_CMD_SENT; 7398c2ecf20Sopenharmony_ci } else { 7408c2ecf20Sopenharmony_ci *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA); 7418c2ecf20Sopenharmony_ci priv->dnld_sent = DNLD_DATA_SENT; 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN); 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci/** 7508c2ecf20Sopenharmony_ci * if_usb_issue_boot_command - issues Boot command to the Boot2 code 7518c2ecf20Sopenharmony_ci * @cardp: pointer to &if_usb_card 7528c2ecf20Sopenharmony_ci * @ivalue: 1:Boot from FW by USB-Download 7538c2ecf20Sopenharmony_ci * 2:Boot from FW in EEPROM 7548c2ecf20Sopenharmony_ci * returns: 0 for success or negative error code 7558c2ecf20Sopenharmony_ci */ 7568c2ecf20Sopenharmony_cistatic int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci struct bootcmd *bootcmd = cardp->ep_out_buf; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci /* Prepare command */ 7618c2ecf20Sopenharmony_ci bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER); 7628c2ecf20Sopenharmony_ci bootcmd->cmd = ivalue; 7638c2ecf20Sopenharmony_ci memset(bootcmd->pad, 0, sizeof(bootcmd->pad)); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci /* Issue command */ 7668c2ecf20Sopenharmony_ci usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd)); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci return 0; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci/** 7738c2ecf20Sopenharmony_ci * check_fwfile_format - check the validity of Boot2/FW image 7748c2ecf20Sopenharmony_ci * 7758c2ecf20Sopenharmony_ci * @data: pointer to image 7768c2ecf20Sopenharmony_ci * @totlen: image length 7778c2ecf20Sopenharmony_ci * returns: 0 (good) or 1 (failure) 7788c2ecf20Sopenharmony_ci */ 7798c2ecf20Sopenharmony_cistatic int check_fwfile_format(const uint8_t *data, uint32_t totlen) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci uint32_t bincmd, exit; 7828c2ecf20Sopenharmony_ci uint32_t blksize, offset, len; 7838c2ecf20Sopenharmony_ci int ret; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci ret = 1; 7868c2ecf20Sopenharmony_ci exit = len = 0; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci do { 7898c2ecf20Sopenharmony_ci struct fwheader *fwh = (void *)data; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci bincmd = le32_to_cpu(fwh->dnldcmd); 7928c2ecf20Sopenharmony_ci blksize = le32_to_cpu(fwh->datalength); 7938c2ecf20Sopenharmony_ci switch (bincmd) { 7948c2ecf20Sopenharmony_ci case FW_HAS_DATA_TO_RECV: 7958c2ecf20Sopenharmony_ci offset = sizeof(struct fwheader) + blksize; 7968c2ecf20Sopenharmony_ci data += offset; 7978c2ecf20Sopenharmony_ci len += offset; 7988c2ecf20Sopenharmony_ci if (len >= totlen) 7998c2ecf20Sopenharmony_ci exit = 1; 8008c2ecf20Sopenharmony_ci break; 8018c2ecf20Sopenharmony_ci case FW_HAS_LAST_BLOCK: 8028c2ecf20Sopenharmony_ci exit = 1; 8038c2ecf20Sopenharmony_ci ret = 0; 8048c2ecf20Sopenharmony_ci break; 8058c2ecf20Sopenharmony_ci default: 8068c2ecf20Sopenharmony_ci exit = 1; 8078c2ecf20Sopenharmony_ci break; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci } while (!exit); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (ret) 8128c2ecf20Sopenharmony_ci pr_err("firmware file format check FAIL\n"); 8138c2ecf20Sopenharmony_ci else 8148c2ecf20Sopenharmony_ci lbs_deb_fw("firmware file format check PASS\n"); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci return ret; 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_cistatic void if_usb_prog_firmware(struct lbs_private *priv, int ret, 8208c2ecf20Sopenharmony_ci const struct firmware *fw, 8218c2ecf20Sopenharmony_ci const struct firmware *unused) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci struct if_usb_card *cardp = priv->card; 8248c2ecf20Sopenharmony_ci int i = 0; 8258c2ecf20Sopenharmony_ci static int reset_count = 10; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci if (ret) { 8288c2ecf20Sopenharmony_ci pr_err("failed to find firmware (%d)\n", ret); 8298c2ecf20Sopenharmony_ci goto done; 8308c2ecf20Sopenharmony_ci } 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci cardp->fw = fw; 8338c2ecf20Sopenharmony_ci if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) { 8348c2ecf20Sopenharmony_ci ret = -EINVAL; 8358c2ecf20Sopenharmony_ci goto done; 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci /* Cancel any pending usb business */ 8398c2ecf20Sopenharmony_ci usb_kill_urb(cardp->rx_urb); 8408c2ecf20Sopenharmony_ci usb_kill_urb(cardp->tx_urb); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci cardp->fwlastblksent = 0; 8438c2ecf20Sopenharmony_ci cardp->fwdnldover = 0; 8448c2ecf20Sopenharmony_ci cardp->totalbytes = 0; 8458c2ecf20Sopenharmony_ci cardp->fwfinalblk = 0; 8468c2ecf20Sopenharmony_ci cardp->bootcmdresp = 0; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cirestart: 8498c2ecf20Sopenharmony_ci if (if_usb_submit_rx_urb_fwload(cardp) < 0) { 8508c2ecf20Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); 8518c2ecf20Sopenharmony_ci ret = -EIO; 8528c2ecf20Sopenharmony_ci goto done; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci cardp->bootcmdresp = 0; 8568c2ecf20Sopenharmony_ci do { 8578c2ecf20Sopenharmony_ci int j = 0; 8588c2ecf20Sopenharmony_ci i++; 8598c2ecf20Sopenharmony_ci if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB); 8608c2ecf20Sopenharmony_ci /* wait for command response */ 8618c2ecf20Sopenharmony_ci do { 8628c2ecf20Sopenharmony_ci j++; 8638c2ecf20Sopenharmony_ci msleep_interruptible(100); 8648c2ecf20Sopenharmony_ci } while (cardp->bootcmdresp == 0 && j < 10); 8658c2ecf20Sopenharmony_ci } while (cardp->bootcmdresp == 0 && i < 5); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (cardp->bootcmdresp == BOOT_CMD_RESP_NOT_SUPPORTED) { 8688c2ecf20Sopenharmony_ci /* Return to normal operation */ 8698c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 8708c2ecf20Sopenharmony_ci usb_kill_urb(cardp->rx_urb); 8718c2ecf20Sopenharmony_ci usb_kill_urb(cardp->tx_urb); 8728c2ecf20Sopenharmony_ci if (if_usb_submit_rx_urb(cardp) < 0) 8738c2ecf20Sopenharmony_ci ret = -EIO; 8748c2ecf20Sopenharmony_ci goto done; 8758c2ecf20Sopenharmony_ci } else if (cardp->bootcmdresp <= 0) { 8768c2ecf20Sopenharmony_ci if (--reset_count >= 0) { 8778c2ecf20Sopenharmony_ci if_usb_reset_device(cardp); 8788c2ecf20Sopenharmony_ci goto restart; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci ret = -EIO; 8818c2ecf20Sopenharmony_ci goto done; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci i = 0; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci cardp->totalbytes = 0; 8878c2ecf20Sopenharmony_ci cardp->fwlastblksent = 0; 8888c2ecf20Sopenharmony_ci cardp->CRC_OK = 1; 8898c2ecf20Sopenharmony_ci cardp->fwdnldover = 0; 8908c2ecf20Sopenharmony_ci cardp->fwseqnum = -1; 8918c2ecf20Sopenharmony_ci cardp->totalbytes = 0; 8928c2ecf20Sopenharmony_ci cardp->fwfinalblk = 0; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci /* Send the first firmware packet... */ 8958c2ecf20Sopenharmony_ci if_usb_send_fw_pkt(cardp); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci /* ... and wait for the process to complete */ 8988c2ecf20Sopenharmony_ci wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci del_timer_sync(&cardp->fw_timeout); 9018c2ecf20Sopenharmony_ci usb_kill_urb(cardp->rx_urb); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci if (!cardp->fwdnldover) { 9048c2ecf20Sopenharmony_ci pr_info("failed to load fw, resetting device!\n"); 9058c2ecf20Sopenharmony_ci if (--reset_count >= 0) { 9068c2ecf20Sopenharmony_ci if_usb_reset_device(cardp); 9078c2ecf20Sopenharmony_ci goto restart; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci pr_info("FW download failure, time = %d ms\n", i * 100); 9118c2ecf20Sopenharmony_ci ret = -EIO; 9128c2ecf20Sopenharmony_ci goto done; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci cardp->priv->fw_ready = 1; 9168c2ecf20Sopenharmony_ci if_usb_submit_rx_urb(cardp); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (lbs_start_card(priv)) 9198c2ecf20Sopenharmony_ci goto done; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if_usb_setup_firmware(priv); 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci /* 9248c2ecf20Sopenharmony_ci * EHS_REMOVE_WAKEUP is not supported on all versions of the firmware. 9258c2ecf20Sopenharmony_ci */ 9268c2ecf20Sopenharmony_ci priv->wol_criteria = EHS_REMOVE_WAKEUP; 9278c2ecf20Sopenharmony_ci if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL)) 9288c2ecf20Sopenharmony_ci priv->ehs_remove_supported = false; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci done: 9318c2ecf20Sopenharmony_ci cardp->fw = NULL; 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 9368c2ecf20Sopenharmony_cistatic int if_usb_suspend(struct usb_interface *intf, pm_message_t message) 9378c2ecf20Sopenharmony_ci{ 9388c2ecf20Sopenharmony_ci struct if_usb_card *cardp = usb_get_intfdata(intf); 9398c2ecf20Sopenharmony_ci struct lbs_private *priv = cardp->priv; 9408c2ecf20Sopenharmony_ci int ret; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (priv->psstate != PS_STATE_FULL_POWER) { 9438c2ecf20Sopenharmony_ci ret = -1; 9448c2ecf20Sopenharmony_ci goto out; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci#ifdef CONFIG_OLPC 9488c2ecf20Sopenharmony_ci if (machine_is_olpc()) { 9498c2ecf20Sopenharmony_ci if (priv->wol_criteria == EHS_REMOVE_WAKEUP) 9508c2ecf20Sopenharmony_ci olpc_ec_wakeup_clear(EC_SCI_SRC_WLAN); 9518c2ecf20Sopenharmony_ci else 9528c2ecf20Sopenharmony_ci olpc_ec_wakeup_set(EC_SCI_SRC_WLAN); 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci#endif 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci ret = lbs_suspend(priv); 9578c2ecf20Sopenharmony_ci if (ret) 9588c2ecf20Sopenharmony_ci goto out; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* Unlink tx & rx urb */ 9618c2ecf20Sopenharmony_ci usb_kill_urb(cardp->tx_urb); 9628c2ecf20Sopenharmony_ci usb_kill_urb(cardp->rx_urb); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci out: 9658c2ecf20Sopenharmony_ci return ret; 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_cistatic int if_usb_resume(struct usb_interface *intf) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci struct if_usb_card *cardp = usb_get_intfdata(intf); 9718c2ecf20Sopenharmony_ci struct lbs_private *priv = cardp->priv; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci if_usb_submit_rx_urb(cardp); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci lbs_resume(priv); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci return 0; 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci#else 9808c2ecf20Sopenharmony_ci#define if_usb_suspend NULL 9818c2ecf20Sopenharmony_ci#define if_usb_resume NULL 9828c2ecf20Sopenharmony_ci#endif 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_cistatic struct usb_driver if_usb_driver = { 9858c2ecf20Sopenharmony_ci .name = DRV_NAME, 9868c2ecf20Sopenharmony_ci .probe = if_usb_probe, 9878c2ecf20Sopenharmony_ci .disconnect = if_usb_disconnect, 9888c2ecf20Sopenharmony_ci .id_table = if_usb_table, 9898c2ecf20Sopenharmony_ci .suspend = if_usb_suspend, 9908c2ecf20Sopenharmony_ci .resume = if_usb_resume, 9918c2ecf20Sopenharmony_ci .reset_resume = if_usb_resume, 9928c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 9938c2ecf20Sopenharmony_ci}; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cimodule_usb_driver(if_usb_driver); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("8388 USB WLAN Driver"); 9988c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd. and Red Hat, Inc."); 9998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1000