162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file contains functions used in USB interface module. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/firmware.h> 1162306a36Sopenharmony_ci#include <linux/netdevice.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/usb.h> 1462306a36Sopenharmony_ci#include <linux/olpc-ec.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#ifdef CONFIG_OLPC 1762306a36Sopenharmony_ci#include <asm/olpc.h> 1862306a36Sopenharmony_ci#endif 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define DRV_NAME "usb8xxx" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "host.h" 2362306a36Sopenharmony_ci#include "decl.h" 2462306a36Sopenharmony_ci#include "defs.h" 2562306a36Sopenharmony_ci#include "dev.h" 2662306a36Sopenharmony_ci#include "cmd.h" 2762306a36Sopenharmony_ci#include "if_usb.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define INSANEDEBUG 0 3062306a36Sopenharmony_ci#define lbs_deb_usb2(...) do { if (INSANEDEBUG) lbs_deb_usbd(__VA_ARGS__); } while (0) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define MESSAGE_HEADER_LEN 4 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/usb8388_v9.bin"); 3562306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/usb8388_v5.bin"); 3662306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/usb8388.bin"); 3762306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/usb8682.bin"); 3862306a36Sopenharmony_ciMODULE_FIRMWARE("usb8388.bin"); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cienum { 4162306a36Sopenharmony_ci MODEL_UNKNOWN = 0x0, 4262306a36Sopenharmony_ci MODEL_8388 = 0x1, 4362306a36Sopenharmony_ci MODEL_8682 = 0x2 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* table of firmware file names */ 4762306a36Sopenharmony_cistatic const struct lbs_fw_table fw_table[] = { 4862306a36Sopenharmony_ci { MODEL_8388, "libertas/usb8388_olpc.bin", NULL }, 4962306a36Sopenharmony_ci { MODEL_8388, "libertas/usb8388_v9.bin", NULL }, 5062306a36Sopenharmony_ci { MODEL_8388, "libertas/usb8388_v5.bin", NULL }, 5162306a36Sopenharmony_ci { MODEL_8388, "libertas/usb8388.bin", NULL }, 5262306a36Sopenharmony_ci { MODEL_8388, "usb8388.bin", NULL }, 5362306a36Sopenharmony_ci { MODEL_8682, "libertas/usb8682.bin", NULL }, 5462306a36Sopenharmony_ci { 0, NULL, NULL } 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic const struct usb_device_id if_usb_table[] = { 5862306a36Sopenharmony_ci /* Enter the device signature inside */ 5962306a36Sopenharmony_ci { USB_DEVICE(0x1286, 0x2001), .driver_info = MODEL_8388 }, 6062306a36Sopenharmony_ci { USB_DEVICE(0x05a3, 0x8388), .driver_info = MODEL_8388 }, 6162306a36Sopenharmony_ci {} /* Terminating entry */ 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, if_usb_table); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void if_usb_receive(struct urb *urb); 6762306a36Sopenharmony_cistatic void if_usb_receive_fwload(struct urb *urb); 6862306a36Sopenharmony_cistatic void if_usb_prog_firmware(struct lbs_private *priv, int ret, 6962306a36Sopenharmony_ci const struct firmware *fw, 7062306a36Sopenharmony_ci const struct firmware *unused); 7162306a36Sopenharmony_cistatic int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, 7262306a36Sopenharmony_ci uint8_t *payload, uint16_t nb); 7362306a36Sopenharmony_cistatic int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, 7462306a36Sopenharmony_ci uint16_t nb); 7562306a36Sopenharmony_cistatic void if_usb_free(struct if_usb_card *cardp); 7662306a36Sopenharmony_cistatic int if_usb_submit_rx_urb(struct if_usb_card *cardp); 7762306a36Sopenharmony_cistatic int if_usb_reset_device(struct if_usb_card *cardp); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/** 8062306a36Sopenharmony_ci * if_usb_write_bulk_callback - callback function to handle the status 8162306a36Sopenharmony_ci * of the URB 8262306a36Sopenharmony_ci * @urb: pointer to &urb structure 8362306a36Sopenharmony_ci * returns: N/A 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_cistatic void if_usb_write_bulk_callback(struct urb *urb) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct if_usb_card *cardp = (struct if_usb_card *) urb->context; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* handle the transmission complete validations */ 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (urb->status == 0) { 9262306a36Sopenharmony_ci struct lbs_private *priv = cardp->priv; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci lbs_deb_usb2(&urb->dev->dev, "URB status is successful\n"); 9562306a36Sopenharmony_ci lbs_deb_usb2(&urb->dev->dev, "Actual length transmitted %d\n", 9662306a36Sopenharmony_ci urb->actual_length); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* Boot commands such as UPDATE_FW and UPDATE_BOOT2 are not 9962306a36Sopenharmony_ci * passed up to the lbs level. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci if (priv && priv->dnld_sent != DNLD_BOOTCMD_SENT) 10262306a36Sopenharmony_ci lbs_host_to_card_done(priv); 10362306a36Sopenharmony_ci } else { 10462306a36Sopenharmony_ci /* print the failure status number for debug */ 10562306a36Sopenharmony_ci pr_info("URB in failure status: %d\n", urb->status); 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/** 11062306a36Sopenharmony_ci * if_usb_free - free tx/rx urb, skb and rx buffer 11162306a36Sopenharmony_ci * @cardp: pointer to &if_usb_card 11262306a36Sopenharmony_ci * returns: N/A 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_cistatic void if_usb_free(struct if_usb_card *cardp) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci /* Unlink tx & rx urb */ 11762306a36Sopenharmony_ci usb_kill_urb(cardp->tx_urb); 11862306a36Sopenharmony_ci usb_kill_urb(cardp->rx_urb); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci usb_free_urb(cardp->tx_urb); 12162306a36Sopenharmony_ci cardp->tx_urb = NULL; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci usb_free_urb(cardp->rx_urb); 12462306a36Sopenharmony_ci cardp->rx_urb = NULL; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci kfree(cardp->ep_out_buf); 12762306a36Sopenharmony_ci cardp->ep_out_buf = NULL; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void if_usb_setup_firmware(struct lbs_private *priv) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct if_usb_card *cardp = priv->card; 13362306a36Sopenharmony_ci struct cmd_ds_set_boot2_ver b2_cmd; 13462306a36Sopenharmony_ci struct cmd_ds_802_11_fw_wake_method wake_method; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci b2_cmd.hdr.size = cpu_to_le16(sizeof(b2_cmd)); 13762306a36Sopenharmony_ci b2_cmd.action = 0; 13862306a36Sopenharmony_ci b2_cmd.version = cardp->boot2_version; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (lbs_cmd_with_response(priv, CMD_SET_BOOT2_VER, &b2_cmd)) 14162306a36Sopenharmony_ci lbs_deb_usb("Setting boot2 version failed\n"); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci priv->wol_gpio = 2; /* Wake via GPIO2... */ 14462306a36Sopenharmony_ci priv->wol_gap = 20; /* ... after 20ms */ 14562306a36Sopenharmony_ci lbs_host_sleep_cfg(priv, EHS_WAKE_ON_UNICAST_DATA, 14662306a36Sopenharmony_ci (struct wol_config *) NULL); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci wake_method.hdr.size = cpu_to_le16(sizeof(wake_method)); 14962306a36Sopenharmony_ci wake_method.action = cpu_to_le16(CMD_ACT_GET); 15062306a36Sopenharmony_ci if (lbs_cmd_with_response(priv, CMD_802_11_FW_WAKE_METHOD, &wake_method)) { 15162306a36Sopenharmony_ci netdev_info(priv->dev, "Firmware does not seem to support PS mode\n"); 15262306a36Sopenharmony_ci priv->fwcapinfo &= ~FW_CAPINFO_PS; 15362306a36Sopenharmony_ci } else { 15462306a36Sopenharmony_ci if (le16_to_cpu(wake_method.method) == CMD_WAKE_METHOD_COMMAND_INT) { 15562306a36Sopenharmony_ci lbs_deb_usb("Firmware seems to support PS with wake-via-command\n"); 15662306a36Sopenharmony_ci } else { 15762306a36Sopenharmony_ci /* The versions which boot up this way don't seem to 15862306a36Sopenharmony_ci work even if we set it to the command interrupt */ 15962306a36Sopenharmony_ci priv->fwcapinfo &= ~FW_CAPINFO_PS; 16062306a36Sopenharmony_ci netdev_info(priv->dev, 16162306a36Sopenharmony_ci "Firmware doesn't wake via command interrupt; disabling PS mode\n"); 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic void if_usb_fw_timeo(struct timer_list *t) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct if_usb_card *cardp = from_timer(cardp, t, fw_timeout); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (cardp->fwdnldover) { 17162306a36Sopenharmony_ci lbs_deb_usb("Download complete, no event. Assuming success\n"); 17262306a36Sopenharmony_ci } else { 17362306a36Sopenharmony_ci pr_err("Download timed out\n"); 17462306a36Sopenharmony_ci cardp->surprise_removed = 1; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci wake_up(&cardp->fw_wq); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci#ifdef CONFIG_OLPC 18062306a36Sopenharmony_cistatic void if_usb_reset_olpc_card(struct lbs_private *priv) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci printk(KERN_CRIT "Resetting OLPC wireless via EC...\n"); 18362306a36Sopenharmony_ci olpc_ec_cmd(0x25, NULL, 0, NULL, 0); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci#endif 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/** 18862306a36Sopenharmony_ci * if_usb_probe - sets the configuration values 18962306a36Sopenharmony_ci * @intf: &usb_interface pointer 19062306a36Sopenharmony_ci * @id: pointer to usb_device_id 19162306a36Sopenharmony_ci * returns: 0 on success, error code on failure 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_cistatic int if_usb_probe(struct usb_interface *intf, 19462306a36Sopenharmony_ci const struct usb_device_id *id) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci struct usb_device *udev; 19762306a36Sopenharmony_ci struct usb_host_interface *iface_desc; 19862306a36Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 19962306a36Sopenharmony_ci struct lbs_private *priv; 20062306a36Sopenharmony_ci struct if_usb_card *cardp; 20162306a36Sopenharmony_ci int r = -ENOMEM; 20262306a36Sopenharmony_ci int i; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci udev = interface_to_usbdev(intf); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci cardp = kzalloc(sizeof(struct if_usb_card), GFP_KERNEL); 20762306a36Sopenharmony_ci if (!cardp) 20862306a36Sopenharmony_ci goto error; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci timer_setup(&cardp->fw_timeout, if_usb_fw_timeo, 0); 21162306a36Sopenharmony_ci init_waitqueue_head(&cardp->fw_wq); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci cardp->udev = udev; 21462306a36Sopenharmony_ci cardp->model = (uint32_t) id->driver_info; 21562306a36Sopenharmony_ci iface_desc = intf->cur_altsetting; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci lbs_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" 21862306a36Sopenharmony_ci " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", 21962306a36Sopenharmony_ci le16_to_cpu(udev->descriptor.bcdUSB), 22062306a36Sopenharmony_ci udev->descriptor.bDeviceClass, 22162306a36Sopenharmony_ci udev->descriptor.bDeviceSubClass, 22262306a36Sopenharmony_ci udev->descriptor.bDeviceProtocol); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { 22562306a36Sopenharmony_ci endpoint = &iface_desc->endpoint[i].desc; 22662306a36Sopenharmony_ci if (usb_endpoint_is_bulk_in(endpoint)) { 22762306a36Sopenharmony_ci cardp->ep_in_size = le16_to_cpu(endpoint->wMaxPacketSize); 22862306a36Sopenharmony_ci cardp->ep_in = usb_endpoint_num(endpoint); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci lbs_deb_usbd(&udev->dev, "in_endpoint = %d\n", cardp->ep_in); 23162306a36Sopenharmony_ci lbs_deb_usbd(&udev->dev, "Bulk in size is %d\n", cardp->ep_in_size); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci } else if (usb_endpoint_is_bulk_out(endpoint)) { 23462306a36Sopenharmony_ci cardp->ep_out_size = le16_to_cpu(endpoint->wMaxPacketSize); 23562306a36Sopenharmony_ci cardp->ep_out = usb_endpoint_num(endpoint); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci lbs_deb_usbd(&udev->dev, "out_endpoint = %d\n", cardp->ep_out); 23862306a36Sopenharmony_ci lbs_deb_usbd(&udev->dev, "Bulk out size is %d\n", cardp->ep_out_size); 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci if (!cardp->ep_out_size || !cardp->ep_in_size) { 24262306a36Sopenharmony_ci lbs_deb_usbd(&udev->dev, "Endpoints not found\n"); 24362306a36Sopenharmony_ci goto dealloc; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci if (!(cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL))) { 24662306a36Sopenharmony_ci lbs_deb_usbd(&udev->dev, "Rx URB allocation failed\n"); 24762306a36Sopenharmony_ci goto dealloc; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci if (!(cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL))) { 25062306a36Sopenharmony_ci lbs_deb_usbd(&udev->dev, "Tx URB allocation failed\n"); 25162306a36Sopenharmony_ci goto dealloc; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci cardp->ep_out_buf = kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, GFP_KERNEL); 25462306a36Sopenharmony_ci if (!cardp->ep_out_buf) { 25562306a36Sopenharmony_ci lbs_deb_usbd(&udev->dev, "Could not allocate buffer\n"); 25662306a36Sopenharmony_ci goto dealloc; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci priv = lbs_add_card(cardp, &intf->dev); 26062306a36Sopenharmony_ci if (IS_ERR(priv)) { 26162306a36Sopenharmony_ci r = PTR_ERR(priv); 26262306a36Sopenharmony_ci goto err_add_card; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci cardp->priv = priv; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci priv->hw_host_to_card = if_usb_host_to_card; 26862306a36Sopenharmony_ci priv->enter_deep_sleep = NULL; 26962306a36Sopenharmony_ci priv->exit_deep_sleep = NULL; 27062306a36Sopenharmony_ci priv->reset_deep_sleep_wakeup = NULL; 27162306a36Sopenharmony_ci priv->is_polling = false; 27262306a36Sopenharmony_ci#ifdef CONFIG_OLPC 27362306a36Sopenharmony_ci if (machine_is_olpc()) 27462306a36Sopenharmony_ci priv->reset_card = if_usb_reset_olpc_card; 27562306a36Sopenharmony_ci#endif 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci cardp->boot2_version = udev->descriptor.bcdDevice; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci usb_get_dev(udev); 28062306a36Sopenharmony_ci usb_set_intfdata(intf, cardp); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci r = lbs_get_firmware_async(priv, &udev->dev, cardp->model, 28362306a36Sopenharmony_ci fw_table, if_usb_prog_firmware); 28462306a36Sopenharmony_ci if (r) 28562306a36Sopenharmony_ci goto err_get_fw; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cierr_get_fw: 29062306a36Sopenharmony_ci usb_put_dev(udev); 29162306a36Sopenharmony_ci lbs_remove_card(priv); 29262306a36Sopenharmony_cierr_add_card: 29362306a36Sopenharmony_ci if_usb_reset_device(cardp); 29462306a36Sopenharmony_cidealloc: 29562306a36Sopenharmony_ci if_usb_free(cardp); 29662306a36Sopenharmony_ci kfree(cardp); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cierror: 29962306a36Sopenharmony_ci return r; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/** 30362306a36Sopenharmony_ci * if_usb_disconnect - free resource and cleanup 30462306a36Sopenharmony_ci * @intf: USB interface structure 30562306a36Sopenharmony_ci * returns: N/A 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_cistatic void if_usb_disconnect(struct usb_interface *intf) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci struct if_usb_card *cardp = usb_get_intfdata(intf); 31062306a36Sopenharmony_ci struct lbs_private *priv = cardp->priv; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci cardp->surprise_removed = 1; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (priv) { 31562306a36Sopenharmony_ci lbs_stop_card(priv); 31662306a36Sopenharmony_ci lbs_remove_card(priv); 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* Unlink and free urb */ 32062306a36Sopenharmony_ci if_usb_free(cardp); 32162306a36Sopenharmony_ci kfree(cardp); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci usb_set_intfdata(intf, NULL); 32462306a36Sopenharmony_ci usb_put_dev(interface_to_usbdev(intf)); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/** 32862306a36Sopenharmony_ci * if_usb_send_fw_pkt - download FW 32962306a36Sopenharmony_ci * @cardp: pointer to &struct if_usb_card 33062306a36Sopenharmony_ci * returns: 0 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_cistatic int if_usb_send_fw_pkt(struct if_usb_card *cardp) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct fwdata *fwdata = cardp->ep_out_buf; 33562306a36Sopenharmony_ci const uint8_t *firmware = cardp->fw->data; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* If we got a CRC failure on the last block, back 33862306a36Sopenharmony_ci up and retry it */ 33962306a36Sopenharmony_ci if (!cardp->CRC_OK) { 34062306a36Sopenharmony_ci cardp->totalbytes = cardp->fwlastblksent; 34162306a36Sopenharmony_ci cardp->fwseqnum--; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "totalbytes = %d\n", 34562306a36Sopenharmony_ci cardp->totalbytes); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* struct fwdata (which we sent to the card) has an 34862306a36Sopenharmony_ci extra __le32 field in between the header and the data, 34962306a36Sopenharmony_ci which is not in the struct fwheader in the actual 35062306a36Sopenharmony_ci firmware binary. Insert the seqnum in the middle... */ 35162306a36Sopenharmony_ci memcpy(&fwdata->hdr, &firmware[cardp->totalbytes], 35262306a36Sopenharmony_ci sizeof(struct fwheader)); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci cardp->fwlastblksent = cardp->totalbytes; 35562306a36Sopenharmony_ci cardp->totalbytes += sizeof(struct fwheader); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci memcpy(fwdata->data, &firmware[cardp->totalbytes], 35862306a36Sopenharmony_ci le32_to_cpu(fwdata->hdr.datalength)); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "Data length = %d\n", 36162306a36Sopenharmony_ci le32_to_cpu(fwdata->hdr.datalength)); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum); 36462306a36Sopenharmony_ci cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) + 36762306a36Sopenharmony_ci le32_to_cpu(fwdata->hdr.datalength)); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) { 37062306a36Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "There are data to follow\n"); 37162306a36Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "seqnum = %d totalbytes = %d\n", 37262306a36Sopenharmony_ci cardp->fwseqnum, cardp->totalbytes); 37362306a36Sopenharmony_ci } else if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) { 37462306a36Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "Host has finished FW downloading\n"); 37562306a36Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "Downloading FW JUMP BLOCK\n"); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci cardp->fwfinalblk = 1; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "Firmware download done; size %d\n", 38162306a36Sopenharmony_ci cardp->totalbytes); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return 0; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic int if_usb_reset_device(struct if_usb_card *cardp) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct cmd_header *cmd = cardp->ep_out_buf + 4; 38962306a36Sopenharmony_ci int ret; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci cmd->command = cpu_to_le16(CMD_802_11_RESET); 39462306a36Sopenharmony_ci cmd->size = cpu_to_le16(sizeof(cmd)); 39562306a36Sopenharmony_ci cmd->result = cpu_to_le16(0); 39662306a36Sopenharmony_ci cmd->seqnum = cpu_to_le16(0x5a5a); 39762306a36Sopenharmony_ci usb_tx_block(cardp, cardp->ep_out_buf, 4 + sizeof(struct cmd_header)); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci msleep(100); 40062306a36Sopenharmony_ci ret = usb_reset_device(cardp->udev); 40162306a36Sopenharmony_ci msleep(100); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci#ifdef CONFIG_OLPC 40462306a36Sopenharmony_ci if (ret && machine_is_olpc()) 40562306a36Sopenharmony_ci if_usb_reset_olpc_card(NULL); 40662306a36Sopenharmony_ci#endif 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return ret; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci/** 41262306a36Sopenharmony_ci * usb_tx_block - transfer the data to the device 41362306a36Sopenharmony_ci * @cardp: pointer to &struct if_usb_card 41462306a36Sopenharmony_ci * @payload: pointer to payload data 41562306a36Sopenharmony_ci * @nb: data length 41662306a36Sopenharmony_ci * returns: 0 for success or negative error code 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_cistatic int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci int ret; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci /* check if device is removed */ 42362306a36Sopenharmony_ci if (cardp->surprise_removed) { 42462306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "Device removed\n"); 42562306a36Sopenharmony_ci ret = -ENODEV; 42662306a36Sopenharmony_ci goto tx_ret; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci usb_fill_bulk_urb(cardp->tx_urb, cardp->udev, 43062306a36Sopenharmony_ci usb_sndbulkpipe(cardp->udev, 43162306a36Sopenharmony_ci cardp->ep_out), 43262306a36Sopenharmony_ci payload, nb, if_usb_write_bulk_callback, cardp); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) { 43762306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret); 43862306a36Sopenharmony_ci } else { 43962306a36Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n"); 44062306a36Sopenharmony_ci ret = 0; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_citx_ret: 44462306a36Sopenharmony_ci return ret; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic int __if_usb_submit_rx_urb(struct if_usb_card *cardp, 44862306a36Sopenharmony_ci void (*callbackfn)(struct urb *urb)) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct sk_buff *skb; 45162306a36Sopenharmony_ci int ret = -1; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) { 45462306a36Sopenharmony_ci pr_err("No free skb\n"); 45562306a36Sopenharmony_ci goto rx_ret; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci cardp->rx_skb = skb; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* Fill the receive configuration URB and initialise the Rx call back */ 46162306a36Sopenharmony_ci usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, 46262306a36Sopenharmony_ci usb_rcvbulkpipe(cardp->udev, cardp->ep_in), 46362306a36Sopenharmony_ci skb->data + IPFIELD_ALIGN_OFFSET, 46462306a36Sopenharmony_ci MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, 46562306a36Sopenharmony_ci cardp); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); 46862306a36Sopenharmony_ci if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) { 46962306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "Submit Rx URB failed: %d\n", ret); 47062306a36Sopenharmony_ci kfree_skb(skb); 47162306a36Sopenharmony_ci cardp->rx_skb = NULL; 47262306a36Sopenharmony_ci ret = -1; 47362306a36Sopenharmony_ci } else { 47462306a36Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "Submit Rx URB success\n"); 47562306a36Sopenharmony_ci ret = 0; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cirx_ret: 47962306a36Sopenharmony_ci return ret; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int if_usb_submit_rx_urb_fwload(struct if_usb_card *cardp) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload); 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic int if_usb_submit_rx_urb(struct if_usb_card *cardp) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci return __if_usb_submit_rx_urb(cardp, &if_usb_receive); 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic void if_usb_receive_fwload(struct urb *urb) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct if_usb_card *cardp = urb->context; 49562306a36Sopenharmony_ci struct sk_buff *skb = cardp->rx_skb; 49662306a36Sopenharmony_ci struct fwsyncheader *syncfwheader; 49762306a36Sopenharmony_ci struct bootcmdresp bootcmdresp; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (urb->status) { 50062306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, 50162306a36Sopenharmony_ci "URB status is failed during fw load\n"); 50262306a36Sopenharmony_ci kfree_skb(skb); 50362306a36Sopenharmony_ci return; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (cardp->fwdnldover) { 50762306a36Sopenharmony_ci __le32 *tmp = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) && 51062306a36Sopenharmony_ci tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) { 51162306a36Sopenharmony_ci pr_info("Firmware ready event received\n"); 51262306a36Sopenharmony_ci wake_up(&cardp->fw_wq); 51362306a36Sopenharmony_ci } else { 51462306a36Sopenharmony_ci lbs_deb_usb("Waiting for confirmation; got %x %x\n", 51562306a36Sopenharmony_ci le32_to_cpu(tmp[0]), le32_to_cpu(tmp[1])); 51662306a36Sopenharmony_ci if_usb_submit_rx_urb_fwload(cardp); 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci kfree_skb(skb); 51962306a36Sopenharmony_ci return; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci if (cardp->bootcmdresp <= 0) { 52262306a36Sopenharmony_ci memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET, 52362306a36Sopenharmony_ci sizeof(bootcmdresp)); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) { 52662306a36Sopenharmony_ci kfree_skb(skb); 52762306a36Sopenharmony_ci if_usb_submit_rx_urb_fwload(cardp); 52862306a36Sopenharmony_ci cardp->bootcmdresp = BOOT_CMD_RESP_OK; 52962306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, 53062306a36Sopenharmony_ci "Received valid boot command response\n"); 53162306a36Sopenharmony_ci return; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci if (bootcmdresp.magic != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) { 53462306a36Sopenharmony_ci if (bootcmdresp.magic == cpu_to_le32(CMD_TYPE_REQUEST) || 53562306a36Sopenharmony_ci bootcmdresp.magic == cpu_to_le32(CMD_TYPE_DATA) || 53662306a36Sopenharmony_ci bootcmdresp.magic == cpu_to_le32(CMD_TYPE_INDICATION)) { 53762306a36Sopenharmony_ci if (!cardp->bootcmdresp) 53862306a36Sopenharmony_ci pr_info("Firmware already seems alive; resetting\n"); 53962306a36Sopenharmony_ci cardp->bootcmdresp = -1; 54062306a36Sopenharmony_ci } else { 54162306a36Sopenharmony_ci pr_info("boot cmd response wrong magic number (0x%x)\n", 54262306a36Sopenharmony_ci le32_to_cpu(bootcmdresp.magic)); 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci } else if ((bootcmdresp.cmd != BOOT_CMD_FW_BY_USB) && 54562306a36Sopenharmony_ci (bootcmdresp.cmd != BOOT_CMD_UPDATE_FW) && 54662306a36Sopenharmony_ci (bootcmdresp.cmd != BOOT_CMD_UPDATE_BOOT2)) { 54762306a36Sopenharmony_ci pr_info("boot cmd response cmd_tag error (%d)\n", 54862306a36Sopenharmony_ci bootcmdresp.cmd); 54962306a36Sopenharmony_ci } else if (bootcmdresp.result != BOOT_CMD_RESP_OK) { 55062306a36Sopenharmony_ci pr_info("boot cmd response result error (%d)\n", 55162306a36Sopenharmony_ci bootcmdresp.result); 55262306a36Sopenharmony_ci } else { 55362306a36Sopenharmony_ci cardp->bootcmdresp = 1; 55462306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, 55562306a36Sopenharmony_ci "Received valid boot command response\n"); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci kfree_skb(skb); 55862306a36Sopenharmony_ci if_usb_submit_rx_urb_fwload(cardp); 55962306a36Sopenharmony_ci return; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci syncfwheader = kmemdup(skb->data + IPFIELD_ALIGN_OFFSET, 56362306a36Sopenharmony_ci sizeof(struct fwsyncheader), GFP_ATOMIC); 56462306a36Sopenharmony_ci if (!syncfwheader) { 56562306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "Failure to allocate syncfwheader\n"); 56662306a36Sopenharmony_ci kfree_skb(skb); 56762306a36Sopenharmony_ci return; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (!syncfwheader->cmd) { 57162306a36Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "FW received Blk with correct CRC\n"); 57262306a36Sopenharmony_ci lbs_deb_usb2(&cardp->udev->dev, "FW received Blk seqnum = %d\n", 57362306a36Sopenharmony_ci le32_to_cpu(syncfwheader->seqnum)); 57462306a36Sopenharmony_ci cardp->CRC_OK = 1; 57562306a36Sopenharmony_ci } else { 57662306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "FW received Blk with CRC error\n"); 57762306a36Sopenharmony_ci cardp->CRC_OK = 0; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci kfree_skb(skb); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* Give device 5s to either write firmware to its RAM or eeprom */ 58362306a36Sopenharmony_ci mod_timer(&cardp->fw_timeout, jiffies + (HZ*5)); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (cardp->fwfinalblk) { 58662306a36Sopenharmony_ci cardp->fwdnldover = 1; 58762306a36Sopenharmony_ci goto exit; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if_usb_send_fw_pkt(cardp); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci exit: 59362306a36Sopenharmony_ci if_usb_submit_rx_urb_fwload(cardp); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci kfree(syncfwheader); 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci#define MRVDRV_MIN_PKT_LEN 30 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, 60162306a36Sopenharmony_ci struct if_usb_card *cardp, 60262306a36Sopenharmony_ci struct lbs_private *priv) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + MESSAGE_HEADER_LEN 60562306a36Sopenharmony_ci || recvlength < MRVDRV_MIN_PKT_LEN) { 60662306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "Packet length is Invalid\n"); 60762306a36Sopenharmony_ci kfree_skb(skb); 60862306a36Sopenharmony_ci return; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci skb_reserve(skb, IPFIELD_ALIGN_OFFSET); 61262306a36Sopenharmony_ci skb_put(skb, recvlength); 61362306a36Sopenharmony_ci skb_pull(skb, MESSAGE_HEADER_LEN); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci lbs_process_rxed_packet(priv, skb); 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic inline void process_cmdrequest(int recvlength, uint8_t *recvbuff, 61962306a36Sopenharmony_ci struct sk_buff *skb, 62062306a36Sopenharmony_ci struct if_usb_card *cardp, 62162306a36Sopenharmony_ci struct lbs_private *priv) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci unsigned long flags; 62462306a36Sopenharmony_ci u8 i; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (recvlength > LBS_CMD_BUFFER_SIZE) { 62762306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, 62862306a36Sopenharmony_ci "The receive buffer is too large\n"); 62962306a36Sopenharmony_ci kfree_skb(skb); 63062306a36Sopenharmony_ci return; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci i = (priv->resp_idx == 0) ? 1 : 0; 63662306a36Sopenharmony_ci BUG_ON(priv->resp_len[i]); 63762306a36Sopenharmony_ci priv->resp_len[i] = (recvlength - MESSAGE_HEADER_LEN); 63862306a36Sopenharmony_ci memcpy(priv->resp_buf[i], recvbuff + MESSAGE_HEADER_LEN, 63962306a36Sopenharmony_ci priv->resp_len[i]); 64062306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 64162306a36Sopenharmony_ci lbs_notify_command_response(priv, i); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, 64662306a36Sopenharmony_ci "Wake up main thread to handle cmd response\n"); 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci/** 65062306a36Sopenharmony_ci * if_usb_receive - read the packet into the upload buffer, 65162306a36Sopenharmony_ci * wake up the main thread and initialise the Rx callack 65262306a36Sopenharmony_ci * 65362306a36Sopenharmony_ci * @urb: pointer to &struct urb 65462306a36Sopenharmony_ci * returns: N/A 65562306a36Sopenharmony_ci */ 65662306a36Sopenharmony_cistatic void if_usb_receive(struct urb *urb) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci struct if_usb_card *cardp = urb->context; 65962306a36Sopenharmony_ci struct sk_buff *skb = cardp->rx_skb; 66062306a36Sopenharmony_ci struct lbs_private *priv = cardp->priv; 66162306a36Sopenharmony_ci int recvlength = urb->actual_length; 66262306a36Sopenharmony_ci uint8_t *recvbuff = NULL; 66362306a36Sopenharmony_ci uint32_t recvtype = 0; 66462306a36Sopenharmony_ci __le32 *pkt = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET); 66562306a36Sopenharmony_ci uint32_t event; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (recvlength) { 66862306a36Sopenharmony_ci if (urb->status) { 66962306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "RX URB failed: %d\n", 67062306a36Sopenharmony_ci urb->status); 67162306a36Sopenharmony_ci kfree_skb(skb); 67262306a36Sopenharmony_ci goto setup_for_next; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci recvbuff = skb->data + IPFIELD_ALIGN_OFFSET; 67662306a36Sopenharmony_ci recvtype = le32_to_cpu(pkt[0]); 67762306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, 67862306a36Sopenharmony_ci "Recv length = 0x%x, Recv type = 0x%X\n", 67962306a36Sopenharmony_ci recvlength, recvtype); 68062306a36Sopenharmony_ci } else if (urb->status) { 68162306a36Sopenharmony_ci kfree_skb(skb); 68262306a36Sopenharmony_ci return; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci switch (recvtype) { 68662306a36Sopenharmony_ci case CMD_TYPE_DATA: 68762306a36Sopenharmony_ci process_cmdtypedata(recvlength, skb, cardp, priv); 68862306a36Sopenharmony_ci break; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci case CMD_TYPE_REQUEST: 69162306a36Sopenharmony_ci process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); 69262306a36Sopenharmony_ci break; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci case CMD_TYPE_INDICATION: 69562306a36Sopenharmony_ci /* Event handling */ 69662306a36Sopenharmony_ci event = le32_to_cpu(pkt[1]); 69762306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "**EVENT** 0x%X\n", event); 69862306a36Sopenharmony_ci kfree_skb(skb); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci /* Icky undocumented magic special case */ 70162306a36Sopenharmony_ci if (event & 0xffff0000) { 70262306a36Sopenharmony_ci u32 trycount = (event & 0xffff0000) >> 16; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci lbs_send_tx_feedback(priv, trycount); 70562306a36Sopenharmony_ci } else 70662306a36Sopenharmony_ci lbs_queue_event(priv, event & 0xFF); 70762306a36Sopenharmony_ci break; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci default: 71062306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "Unknown command type 0x%X\n", 71162306a36Sopenharmony_ci recvtype); 71262306a36Sopenharmony_ci kfree_skb(skb); 71362306a36Sopenharmony_ci break; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cisetup_for_next: 71762306a36Sopenharmony_ci if_usb_submit_rx_urb(cardp); 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci/** 72162306a36Sopenharmony_ci * if_usb_host_to_card - downloads data to FW 72262306a36Sopenharmony_ci * @priv: pointer to &struct lbs_private structure 72362306a36Sopenharmony_ci * @type: type of data 72462306a36Sopenharmony_ci * @payload: pointer to data buffer 72562306a36Sopenharmony_ci * @nb: number of bytes 72662306a36Sopenharmony_ci * returns: 0 for success or negative error code 72762306a36Sopenharmony_ci */ 72862306a36Sopenharmony_cistatic int if_usb_host_to_card(struct lbs_private *priv, uint8_t type, 72962306a36Sopenharmony_ci uint8_t *payload, uint16_t nb) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci struct if_usb_card *cardp = priv->card; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev,"*** type = %u\n", type); 73462306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev,"size after = %d\n", nb); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (type == MVMS_CMD) { 73762306a36Sopenharmony_ci *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_REQUEST); 73862306a36Sopenharmony_ci priv->dnld_sent = DNLD_CMD_SENT; 73962306a36Sopenharmony_ci } else { 74062306a36Sopenharmony_ci *(__le32 *)cardp->ep_out_buf = cpu_to_le32(CMD_TYPE_DATA); 74162306a36Sopenharmony_ci priv->dnld_sent = DNLD_DATA_SENT; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci memcpy((cardp->ep_out_buf + MESSAGE_HEADER_LEN), payload, nb); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci return usb_tx_block(cardp, cardp->ep_out_buf, nb + MESSAGE_HEADER_LEN); 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci/** 75062306a36Sopenharmony_ci * if_usb_issue_boot_command - issues Boot command to the Boot2 code 75162306a36Sopenharmony_ci * @cardp: pointer to &if_usb_card 75262306a36Sopenharmony_ci * @ivalue: 1:Boot from FW by USB-Download 75362306a36Sopenharmony_ci * 2:Boot from FW in EEPROM 75462306a36Sopenharmony_ci * returns: 0 for success or negative error code 75562306a36Sopenharmony_ci */ 75662306a36Sopenharmony_cistatic int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci struct bootcmd *bootcmd = cardp->ep_out_buf; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci /* Prepare command */ 76162306a36Sopenharmony_ci bootcmd->magic = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER); 76262306a36Sopenharmony_ci bootcmd->cmd = ivalue; 76362306a36Sopenharmony_ci memset(bootcmd->pad, 0, sizeof(bootcmd->pad)); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci /* Issue command */ 76662306a36Sopenharmony_ci usb_tx_block(cardp, cardp->ep_out_buf, sizeof(*bootcmd)); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci return 0; 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci/** 77362306a36Sopenharmony_ci * check_fwfile_format - check the validity of Boot2/FW image 77462306a36Sopenharmony_ci * 77562306a36Sopenharmony_ci * @data: pointer to image 77662306a36Sopenharmony_ci * @totlen: image length 77762306a36Sopenharmony_ci * returns: 0 (good) or 1 (failure) 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_cistatic int check_fwfile_format(const uint8_t *data, uint32_t totlen) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci uint32_t bincmd, exit; 78262306a36Sopenharmony_ci uint32_t blksize, offset, len; 78362306a36Sopenharmony_ci int ret; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci ret = 1; 78662306a36Sopenharmony_ci exit = len = 0; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci do { 78962306a36Sopenharmony_ci struct fwheader *fwh = (void *)data; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci bincmd = le32_to_cpu(fwh->dnldcmd); 79262306a36Sopenharmony_ci blksize = le32_to_cpu(fwh->datalength); 79362306a36Sopenharmony_ci switch (bincmd) { 79462306a36Sopenharmony_ci case FW_HAS_DATA_TO_RECV: 79562306a36Sopenharmony_ci offset = sizeof(struct fwheader) + blksize; 79662306a36Sopenharmony_ci data += offset; 79762306a36Sopenharmony_ci len += offset; 79862306a36Sopenharmony_ci if (len >= totlen) 79962306a36Sopenharmony_ci exit = 1; 80062306a36Sopenharmony_ci break; 80162306a36Sopenharmony_ci case FW_HAS_LAST_BLOCK: 80262306a36Sopenharmony_ci exit = 1; 80362306a36Sopenharmony_ci ret = 0; 80462306a36Sopenharmony_ci break; 80562306a36Sopenharmony_ci default: 80662306a36Sopenharmony_ci exit = 1; 80762306a36Sopenharmony_ci break; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci } while (!exit); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (ret) 81262306a36Sopenharmony_ci pr_err("firmware file format check FAIL\n"); 81362306a36Sopenharmony_ci else 81462306a36Sopenharmony_ci lbs_deb_fw("firmware file format check PASS\n"); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci return ret; 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic void if_usb_prog_firmware(struct lbs_private *priv, int ret, 82062306a36Sopenharmony_ci const struct firmware *fw, 82162306a36Sopenharmony_ci const struct firmware *unused) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci struct if_usb_card *cardp = priv->card; 82462306a36Sopenharmony_ci int i = 0; 82562306a36Sopenharmony_ci static int reset_count = 10; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (ret) { 82862306a36Sopenharmony_ci pr_err("failed to find firmware (%d)\n", ret); 82962306a36Sopenharmony_ci goto done; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci cardp->fw = fw; 83362306a36Sopenharmony_ci if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) { 83462306a36Sopenharmony_ci ret = -EINVAL; 83562306a36Sopenharmony_ci goto done; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* Cancel any pending usb business */ 83962306a36Sopenharmony_ci usb_kill_urb(cardp->rx_urb); 84062306a36Sopenharmony_ci usb_kill_urb(cardp->tx_urb); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci cardp->fwlastblksent = 0; 84362306a36Sopenharmony_ci cardp->fwdnldover = 0; 84462306a36Sopenharmony_ci cardp->totalbytes = 0; 84562306a36Sopenharmony_ci cardp->fwfinalblk = 0; 84662306a36Sopenharmony_ci cardp->bootcmdresp = 0; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cirestart: 84962306a36Sopenharmony_ci if (if_usb_submit_rx_urb_fwload(cardp) < 0) { 85062306a36Sopenharmony_ci lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); 85162306a36Sopenharmony_ci ret = -EIO; 85262306a36Sopenharmony_ci goto done; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci cardp->bootcmdresp = 0; 85662306a36Sopenharmony_ci do { 85762306a36Sopenharmony_ci int j = 0; 85862306a36Sopenharmony_ci i++; 85962306a36Sopenharmony_ci if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB); 86062306a36Sopenharmony_ci /* wait for command response */ 86162306a36Sopenharmony_ci do { 86262306a36Sopenharmony_ci j++; 86362306a36Sopenharmony_ci msleep_interruptible(100); 86462306a36Sopenharmony_ci } while (cardp->bootcmdresp == 0 && j < 10); 86562306a36Sopenharmony_ci } while (cardp->bootcmdresp == 0 && i < 5); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (cardp->bootcmdresp == BOOT_CMD_RESP_NOT_SUPPORTED) { 86862306a36Sopenharmony_ci /* Return to normal operation */ 86962306a36Sopenharmony_ci ret = -EOPNOTSUPP; 87062306a36Sopenharmony_ci usb_kill_urb(cardp->rx_urb); 87162306a36Sopenharmony_ci usb_kill_urb(cardp->tx_urb); 87262306a36Sopenharmony_ci if (if_usb_submit_rx_urb(cardp) < 0) 87362306a36Sopenharmony_ci ret = -EIO; 87462306a36Sopenharmony_ci goto done; 87562306a36Sopenharmony_ci } else if (cardp->bootcmdresp <= 0) { 87662306a36Sopenharmony_ci if (--reset_count >= 0) { 87762306a36Sopenharmony_ci if_usb_reset_device(cardp); 87862306a36Sopenharmony_ci goto restart; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci ret = -EIO; 88162306a36Sopenharmony_ci goto done; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci i = 0; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci cardp->totalbytes = 0; 88762306a36Sopenharmony_ci cardp->fwlastblksent = 0; 88862306a36Sopenharmony_ci cardp->CRC_OK = 1; 88962306a36Sopenharmony_ci cardp->fwdnldover = 0; 89062306a36Sopenharmony_ci cardp->fwseqnum = -1; 89162306a36Sopenharmony_ci cardp->totalbytes = 0; 89262306a36Sopenharmony_ci cardp->fwfinalblk = 0; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* Send the first firmware packet... */ 89562306a36Sopenharmony_ci if_usb_send_fw_pkt(cardp); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci /* ... and wait for the process to complete */ 89862306a36Sopenharmony_ci wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci del_timer_sync(&cardp->fw_timeout); 90162306a36Sopenharmony_ci usb_kill_urb(cardp->rx_urb); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (!cardp->fwdnldover) { 90462306a36Sopenharmony_ci pr_info("failed to load fw, resetting device!\n"); 90562306a36Sopenharmony_ci if (--reset_count >= 0) { 90662306a36Sopenharmony_ci if_usb_reset_device(cardp); 90762306a36Sopenharmony_ci goto restart; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci pr_info("FW download failure, time = %d ms\n", i * 100); 91162306a36Sopenharmony_ci ret = -EIO; 91262306a36Sopenharmony_ci goto done; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci cardp->priv->fw_ready = 1; 91662306a36Sopenharmony_ci if_usb_submit_rx_urb(cardp); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (lbs_start_card(priv)) 91962306a36Sopenharmony_ci goto done; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if_usb_setup_firmware(priv); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* 92462306a36Sopenharmony_ci * EHS_REMOVE_WAKEUP is not supported on all versions of the firmware. 92562306a36Sopenharmony_ci */ 92662306a36Sopenharmony_ci priv->wol_criteria = EHS_REMOVE_WAKEUP; 92762306a36Sopenharmony_ci if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL)) 92862306a36Sopenharmony_ci priv->ehs_remove_supported = false; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci done: 93162306a36Sopenharmony_ci cardp->fw = NULL; 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci#ifdef CONFIG_PM 93662306a36Sopenharmony_cistatic int if_usb_suspend(struct usb_interface *intf, pm_message_t message) 93762306a36Sopenharmony_ci{ 93862306a36Sopenharmony_ci struct if_usb_card *cardp = usb_get_intfdata(intf); 93962306a36Sopenharmony_ci struct lbs_private *priv = cardp->priv; 94062306a36Sopenharmony_ci int ret; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci if (priv->psstate != PS_STATE_FULL_POWER) { 94362306a36Sopenharmony_ci ret = -1; 94462306a36Sopenharmony_ci goto out; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci#ifdef CONFIG_OLPC 94862306a36Sopenharmony_ci if (machine_is_olpc()) { 94962306a36Sopenharmony_ci if (priv->wol_criteria == EHS_REMOVE_WAKEUP) 95062306a36Sopenharmony_ci olpc_ec_wakeup_clear(EC_SCI_SRC_WLAN); 95162306a36Sopenharmony_ci else 95262306a36Sopenharmony_ci olpc_ec_wakeup_set(EC_SCI_SRC_WLAN); 95362306a36Sopenharmony_ci } 95462306a36Sopenharmony_ci#endif 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci ret = lbs_suspend(priv); 95762306a36Sopenharmony_ci if (ret) 95862306a36Sopenharmony_ci goto out; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci /* Unlink tx & rx urb */ 96162306a36Sopenharmony_ci usb_kill_urb(cardp->tx_urb); 96262306a36Sopenharmony_ci usb_kill_urb(cardp->rx_urb); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci out: 96562306a36Sopenharmony_ci return ret; 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic int if_usb_resume(struct usb_interface *intf) 96962306a36Sopenharmony_ci{ 97062306a36Sopenharmony_ci struct if_usb_card *cardp = usb_get_intfdata(intf); 97162306a36Sopenharmony_ci struct lbs_private *priv = cardp->priv; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci if_usb_submit_rx_urb(cardp); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci lbs_resume(priv); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci return 0; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci#else 98062306a36Sopenharmony_ci#define if_usb_suspend NULL 98162306a36Sopenharmony_ci#define if_usb_resume NULL 98262306a36Sopenharmony_ci#endif 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic struct usb_driver if_usb_driver = { 98562306a36Sopenharmony_ci .name = DRV_NAME, 98662306a36Sopenharmony_ci .probe = if_usb_probe, 98762306a36Sopenharmony_ci .disconnect = if_usb_disconnect, 98862306a36Sopenharmony_ci .id_table = if_usb_table, 98962306a36Sopenharmony_ci .suspend = if_usb_suspend, 99062306a36Sopenharmony_ci .resume = if_usb_resume, 99162306a36Sopenharmony_ci .reset_resume = if_usb_resume, 99262306a36Sopenharmony_ci .disable_hub_initiated_lpm = 1, 99362306a36Sopenharmony_ci}; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_cimodule_usb_driver(if_usb_driver); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ciMODULE_DESCRIPTION("8388 USB WLAN Driver"); 99862306a36Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd. and Red Hat, Inc."); 99962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1000