1dd3f92deSKalle Valo /* 2dd3f92deSKalle Valo * Copyright (C) 2008, cozybit Inc. 3dd3f92deSKalle Valo * Copyright (C) 2003-2006, Marvell International Ltd. 4dd3f92deSKalle Valo * 5dd3f92deSKalle Valo * This program is free software; you can redistribute it and/or modify 6dd3f92deSKalle Valo * it under the terms of the GNU General Public License as published by 7dd3f92deSKalle Valo * the Free Software Foundation; either version 2 of the License, or (at 8dd3f92deSKalle Valo * your option) any later version. 9dd3f92deSKalle Valo */ 10dd3f92deSKalle Valo #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 11dd3f92deSKalle Valo 12dd3f92deSKalle Valo #include <linux/hardirq.h> 13dd3f92deSKalle Valo #include <linux/slab.h> 14dd3f92deSKalle Valo #include <linux/export.h> 15dd3f92deSKalle Valo 16dd3f92deSKalle Valo #include "libertas_tf.h" 17dd3f92deSKalle Valo 18dd3f92deSKalle Valo static const struct channel_range channel_ranges[] = { 19dd3f92deSKalle Valo { LBTF_REGDOMAIN_US, 1, 12 }, 20dd3f92deSKalle Valo { LBTF_REGDOMAIN_CA, 1, 12 }, 21dd3f92deSKalle Valo { LBTF_REGDOMAIN_EU, 1, 14 }, 22dd3f92deSKalle Valo { LBTF_REGDOMAIN_JP, 1, 14 }, 23dd3f92deSKalle Valo { LBTF_REGDOMAIN_SP, 1, 14 }, 24dd3f92deSKalle Valo { LBTF_REGDOMAIN_FR, 1, 14 }, 25dd3f92deSKalle Valo }; 26dd3f92deSKalle Valo 27dd3f92deSKalle Valo static u16 lbtf_region_code_to_index[MRVDRV_MAX_REGION_CODE] = 28dd3f92deSKalle Valo { 29dd3f92deSKalle Valo LBTF_REGDOMAIN_US, LBTF_REGDOMAIN_CA, LBTF_REGDOMAIN_EU, 30dd3f92deSKalle Valo LBTF_REGDOMAIN_SP, LBTF_REGDOMAIN_FR, LBTF_REGDOMAIN_JP, 31dd3f92deSKalle Valo }; 32dd3f92deSKalle Valo 33dd3f92deSKalle Valo static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv); 34dd3f92deSKalle Valo 35dd3f92deSKalle Valo 36dd3f92deSKalle Valo /** 37dd3f92deSKalle Valo * lbtf_cmd_copyback - Simple callback that copies response back into command 38dd3f92deSKalle Valo * 39dd3f92deSKalle Valo * @priv A pointer to struct lbtf_private structure 40dd3f92deSKalle Valo * @extra A pointer to the original command structure for which 41dd3f92deSKalle Valo * 'resp' is a response 42dd3f92deSKalle Valo * @resp A pointer to the command response 43dd3f92deSKalle Valo * 44dd3f92deSKalle Valo * Returns: 0 on success, error on failure 45dd3f92deSKalle Valo */ 46dd3f92deSKalle Valo int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra, 47dd3f92deSKalle Valo struct cmd_header *resp) 48dd3f92deSKalle Valo { 49dd3f92deSKalle Valo struct cmd_header *buf = (void *)extra; 50dd3f92deSKalle Valo uint16_t copy_len; 51dd3f92deSKalle Valo 52dd3f92deSKalle Valo copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size)); 53dd3f92deSKalle Valo memcpy(buf, resp, copy_len); 54dd3f92deSKalle Valo return 0; 55dd3f92deSKalle Valo } 56dd3f92deSKalle Valo EXPORT_SYMBOL_GPL(lbtf_cmd_copyback); 57dd3f92deSKalle Valo 58dd3f92deSKalle Valo #define CHAN_TO_IDX(chan) ((chan) - 1) 59dd3f92deSKalle Valo 60dd3f92deSKalle Valo static void lbtf_geo_init(struct lbtf_private *priv) 61dd3f92deSKalle Valo { 62dd3f92deSKalle Valo const struct channel_range *range = channel_ranges; 63dd3f92deSKalle Valo u8 ch; 64dd3f92deSKalle Valo int i; 65dd3f92deSKalle Valo 66dd3f92deSKalle Valo for (i = 0; i < ARRAY_SIZE(channel_ranges); i++) 67dd3f92deSKalle Valo if (channel_ranges[i].regdomain == priv->regioncode) { 68dd3f92deSKalle Valo range = &channel_ranges[i]; 69dd3f92deSKalle Valo break; 70dd3f92deSKalle Valo } 71dd3f92deSKalle Valo 72dd3f92deSKalle Valo for (ch = priv->range.start; ch < priv->range.end; ch++) 73dd3f92deSKalle Valo priv->channels[CHAN_TO_IDX(ch)].flags = 0; 74dd3f92deSKalle Valo } 75dd3f92deSKalle Valo 76dd3f92deSKalle Valo /** 77dd3f92deSKalle Valo * lbtf_update_hw_spec: Updates the hardware details. 78dd3f92deSKalle Valo * 79dd3f92deSKalle Valo * @priv A pointer to struct lbtf_private structure 80dd3f92deSKalle Valo * 81dd3f92deSKalle Valo * Returns: 0 on success, error on failure 82dd3f92deSKalle Valo */ 83dd3f92deSKalle Valo int lbtf_update_hw_spec(struct lbtf_private *priv) 84dd3f92deSKalle Valo { 85dd3f92deSKalle Valo struct cmd_ds_get_hw_spec cmd; 86dd3f92deSKalle Valo int ret = -1; 87dd3f92deSKalle Valo u32 i; 88dd3f92deSKalle Valo 89dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_CMD); 90dd3f92deSKalle Valo 91dd3f92deSKalle Valo memset(&cmd, 0, sizeof(cmd)); 92dd3f92deSKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 93dd3f92deSKalle Valo memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN); 94dd3f92deSKalle Valo ret = lbtf_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd); 95dd3f92deSKalle Valo if (ret) 96dd3f92deSKalle Valo goto out; 97dd3f92deSKalle Valo 98dd3f92deSKalle Valo priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo); 99dd3f92deSKalle Valo 100dd3f92deSKalle Valo /* The firmware release is in an interesting format: the patch 101dd3f92deSKalle Valo * level is in the most significant nibble ... so fix that: */ 102dd3f92deSKalle Valo priv->fwrelease = le32_to_cpu(cmd.fwrelease); 103dd3f92deSKalle Valo priv->fwrelease = (priv->fwrelease << 8) | 104dd3f92deSKalle Valo (priv->fwrelease >> 24 & 0xff); 105dd3f92deSKalle Valo 106dd3f92deSKalle Valo printk(KERN_INFO "libertastf: %pM, fw %u.%u.%up%u, cap 0x%08x\n", 107dd3f92deSKalle Valo cmd.permanentaddr, 108dd3f92deSKalle Valo priv->fwrelease >> 24 & 0xff, 109dd3f92deSKalle Valo priv->fwrelease >> 16 & 0xff, 110dd3f92deSKalle Valo priv->fwrelease >> 8 & 0xff, 111dd3f92deSKalle Valo priv->fwrelease & 0xff, 112dd3f92deSKalle Valo priv->fwcapinfo); 113dd3f92deSKalle Valo lbtf_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n", 114dd3f92deSKalle Valo cmd.hwifversion, cmd.version); 115dd3f92deSKalle Valo 116dd3f92deSKalle Valo /* Clamp region code to 8-bit since FW spec indicates that it should 117dd3f92deSKalle Valo * only ever be 8-bit, even though the field size is 16-bit. Some 118dd3f92deSKalle Valo * firmware returns non-zero high 8 bits here. 119dd3f92deSKalle Valo */ 120dd3f92deSKalle Valo priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; 121dd3f92deSKalle Valo 122dd3f92deSKalle Valo for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { 123dd3f92deSKalle Valo /* use the region code to search for the index */ 124dd3f92deSKalle Valo if (priv->regioncode == lbtf_region_code_to_index[i]) 125dd3f92deSKalle Valo break; 126dd3f92deSKalle Valo } 127dd3f92deSKalle Valo 128dd3f92deSKalle Valo /* if it's unidentified region code, use the default (USA) */ 129dd3f92deSKalle Valo if (i >= MRVDRV_MAX_REGION_CODE) { 130dd3f92deSKalle Valo priv->regioncode = 0x10; 131dd3f92deSKalle Valo pr_info("unidentified region code; using the default (USA)\n"); 132dd3f92deSKalle Valo } 133dd3f92deSKalle Valo 134dd3f92deSKalle Valo if (priv->current_addr[0] == 0xff) 135dd3f92deSKalle Valo memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN); 136dd3f92deSKalle Valo 137dd3f92deSKalle Valo SET_IEEE80211_PERM_ADDR(priv->hw, priv->current_addr); 138dd3f92deSKalle Valo 139dd3f92deSKalle Valo lbtf_geo_init(priv); 140dd3f92deSKalle Valo out: 141dd3f92deSKalle Valo lbtf_deb_leave(LBTF_DEB_CMD); 142dd3f92deSKalle Valo return ret; 143dd3f92deSKalle Valo } 144dd3f92deSKalle Valo 145dd3f92deSKalle Valo /** 146dd3f92deSKalle Valo * lbtf_set_channel: Set the radio channel 147dd3f92deSKalle Valo * 148dd3f92deSKalle Valo * @priv A pointer to struct lbtf_private structure 149dd3f92deSKalle Valo * @channel The desired channel, or 0 to clear a locked channel 150dd3f92deSKalle Valo * 151dd3f92deSKalle Valo * Returns: 0 on success, error on failure 152dd3f92deSKalle Valo */ 153dd3f92deSKalle Valo int lbtf_set_channel(struct lbtf_private *priv, u8 channel) 154dd3f92deSKalle Valo { 155dd3f92deSKalle Valo int ret = 0; 156dd3f92deSKalle Valo struct cmd_ds_802_11_rf_channel cmd; 157dd3f92deSKalle Valo 158dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_CMD); 159dd3f92deSKalle Valo 160dd3f92deSKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 161dd3f92deSKalle Valo cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET); 162dd3f92deSKalle Valo cmd.channel = cpu_to_le16(channel); 163dd3f92deSKalle Valo 164dd3f92deSKalle Valo ret = lbtf_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); 165dd3f92deSKalle Valo lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret); 166dd3f92deSKalle Valo return ret; 167dd3f92deSKalle Valo } 168dd3f92deSKalle Valo 169dd3f92deSKalle Valo int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon) 170dd3f92deSKalle Valo { 171dd3f92deSKalle Valo struct cmd_ds_802_11_beacon_set cmd; 172dd3f92deSKalle Valo int size; 173dd3f92deSKalle Valo 174dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_CMD); 175dd3f92deSKalle Valo 176dd3f92deSKalle Valo if (beacon->len > MRVL_MAX_BCN_SIZE) { 177dd3f92deSKalle Valo lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", -1); 178dd3f92deSKalle Valo return -1; 179dd3f92deSKalle Valo } 180dd3f92deSKalle Valo size = sizeof(cmd) - sizeof(cmd.beacon) + beacon->len; 181dd3f92deSKalle Valo cmd.hdr.size = cpu_to_le16(size); 182dd3f92deSKalle Valo cmd.len = cpu_to_le16(beacon->len); 183dd3f92deSKalle Valo memcpy(cmd.beacon, (u8 *) beacon->data, beacon->len); 184dd3f92deSKalle Valo 185dd3f92deSKalle Valo lbtf_cmd_async(priv, CMD_802_11_BEACON_SET, &cmd.hdr, size); 186dd3f92deSKalle Valo 187dd3f92deSKalle Valo lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", 0); 188dd3f92deSKalle Valo return 0; 189dd3f92deSKalle Valo } 190dd3f92deSKalle Valo 191dd3f92deSKalle Valo int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable, 192dd3f92deSKalle Valo int beacon_int) 193dd3f92deSKalle Valo { 194dd3f92deSKalle Valo struct cmd_ds_802_11_beacon_control cmd; 195dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_CMD); 196dd3f92deSKalle Valo 197dd3f92deSKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 198dd3f92deSKalle Valo cmd.action = cpu_to_le16(CMD_ACT_SET); 199dd3f92deSKalle Valo cmd.beacon_enable = cpu_to_le16(beacon_enable); 200dd3f92deSKalle Valo cmd.beacon_period = cpu_to_le16(beacon_int); 201dd3f92deSKalle Valo 202dd3f92deSKalle Valo lbtf_cmd_async(priv, CMD_802_11_BEACON_CTRL, &cmd.hdr, sizeof(cmd)); 203dd3f92deSKalle Valo 204dd3f92deSKalle Valo lbtf_deb_leave(LBTF_DEB_CMD); 205dd3f92deSKalle Valo return 0; 206dd3f92deSKalle Valo } 207dd3f92deSKalle Valo 208dd3f92deSKalle Valo static void lbtf_queue_cmd(struct lbtf_private *priv, 209dd3f92deSKalle Valo struct cmd_ctrl_node *cmdnode) 210dd3f92deSKalle Valo { 211dd3f92deSKalle Valo unsigned long flags; 212dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_HOST); 213dd3f92deSKalle Valo 214dd3f92deSKalle Valo if (!cmdnode) { 215dd3f92deSKalle Valo lbtf_deb_host("QUEUE_CMD: cmdnode is NULL\n"); 216dd3f92deSKalle Valo goto qcmd_done; 217dd3f92deSKalle Valo } 218dd3f92deSKalle Valo 219dd3f92deSKalle Valo if (!cmdnode->cmdbuf->size) { 220dd3f92deSKalle Valo lbtf_deb_host("DNLD_CMD: cmd size is zero\n"); 221dd3f92deSKalle Valo goto qcmd_done; 222dd3f92deSKalle Valo } 223dd3f92deSKalle Valo 224dd3f92deSKalle Valo cmdnode->result = 0; 225dd3f92deSKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 226dd3f92deSKalle Valo list_add_tail(&cmdnode->list, &priv->cmdpendingq); 227dd3f92deSKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 228dd3f92deSKalle Valo 229dd3f92deSKalle Valo lbtf_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n", 230dd3f92deSKalle Valo le16_to_cpu(cmdnode->cmdbuf->command)); 231dd3f92deSKalle Valo 232dd3f92deSKalle Valo qcmd_done: 233dd3f92deSKalle Valo lbtf_deb_leave(LBTF_DEB_HOST); 234dd3f92deSKalle Valo } 235dd3f92deSKalle Valo 236dd3f92deSKalle Valo static void lbtf_submit_command(struct lbtf_private *priv, 237dd3f92deSKalle Valo struct cmd_ctrl_node *cmdnode) 238dd3f92deSKalle Valo { 239dd3f92deSKalle Valo unsigned long flags; 240dd3f92deSKalle Valo struct cmd_header *cmd; 241dd3f92deSKalle Valo uint16_t cmdsize; 242dd3f92deSKalle Valo uint16_t command; 243dd3f92deSKalle Valo int timeo = 5 * HZ; 244dd3f92deSKalle Valo int ret; 245dd3f92deSKalle Valo 246dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_HOST); 247dd3f92deSKalle Valo 248dd3f92deSKalle Valo cmd = cmdnode->cmdbuf; 249dd3f92deSKalle Valo 250dd3f92deSKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 251dd3f92deSKalle Valo priv->cur_cmd = cmdnode; 252dd3f92deSKalle Valo cmdsize = le16_to_cpu(cmd->size); 253dd3f92deSKalle Valo command = le16_to_cpu(cmd->command); 254dd3f92deSKalle Valo 255dd3f92deSKalle Valo lbtf_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n", 256dd3f92deSKalle Valo command, le16_to_cpu(cmd->seqnum), cmdsize); 257dd3f92deSKalle Valo lbtf_deb_hex(LBTF_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize); 258dd3f92deSKalle Valo 259dd3f92deSKalle Valo ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize); 260dd3f92deSKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 261dd3f92deSKalle Valo 262dd3f92deSKalle Valo if (ret) { 263dd3f92deSKalle Valo pr_info("DNLD_CMD: hw_host_to_card failed: %d\n", ret); 264dd3f92deSKalle Valo /* Let the timer kick in and retry, and potentially reset 265dd3f92deSKalle Valo the whole thing if the condition persists */ 266dd3f92deSKalle Valo timeo = HZ; 267dd3f92deSKalle Valo } 268dd3f92deSKalle Valo 269dd3f92deSKalle Valo /* Setup the timer after transmit command */ 270dd3f92deSKalle Valo mod_timer(&priv->command_timer, jiffies + timeo); 271dd3f92deSKalle Valo 272dd3f92deSKalle Valo lbtf_deb_leave(LBTF_DEB_HOST); 273dd3f92deSKalle Valo } 274dd3f92deSKalle Valo 275dd3f92deSKalle Valo /** 276dd3f92deSKalle Valo * This function inserts command node to cmdfreeq 277dd3f92deSKalle Valo * after cleans it. Requires priv->driver_lock held. 278dd3f92deSKalle Valo */ 279dd3f92deSKalle Valo static void __lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv, 280dd3f92deSKalle Valo struct cmd_ctrl_node *cmdnode) 281dd3f92deSKalle Valo { 282dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_HOST); 283dd3f92deSKalle Valo 284dd3f92deSKalle Valo if (!cmdnode) 285dd3f92deSKalle Valo goto cl_ins_out; 286dd3f92deSKalle Valo 287dd3f92deSKalle Valo cmdnode->callback = NULL; 288dd3f92deSKalle Valo cmdnode->callback_arg = 0; 289dd3f92deSKalle Valo 290dd3f92deSKalle Valo memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE); 291dd3f92deSKalle Valo 292dd3f92deSKalle Valo list_add_tail(&cmdnode->list, &priv->cmdfreeq); 293dd3f92deSKalle Valo 294dd3f92deSKalle Valo cl_ins_out: 295dd3f92deSKalle Valo lbtf_deb_leave(LBTF_DEB_HOST); 296dd3f92deSKalle Valo } 297dd3f92deSKalle Valo 298dd3f92deSKalle Valo static void lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv, 299dd3f92deSKalle Valo struct cmd_ctrl_node *ptempcmd) 300dd3f92deSKalle Valo { 301dd3f92deSKalle Valo unsigned long flags; 302dd3f92deSKalle Valo 303dd3f92deSKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 304dd3f92deSKalle Valo __lbtf_cleanup_and_insert_cmd(priv, ptempcmd); 305dd3f92deSKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 306dd3f92deSKalle Valo } 307dd3f92deSKalle Valo 308dd3f92deSKalle Valo void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd, 309dd3f92deSKalle Valo int result) 310dd3f92deSKalle Valo { 311dd3f92deSKalle Valo cmd->result = result; 312dd3f92deSKalle Valo cmd->cmdwaitqwoken = 1; 313dd3f92deSKalle Valo wake_up_interruptible(&cmd->cmdwait_q); 314dd3f92deSKalle Valo 315dd3f92deSKalle Valo if (!cmd->callback) 316dd3f92deSKalle Valo __lbtf_cleanup_and_insert_cmd(priv, cmd); 317dd3f92deSKalle Valo priv->cur_cmd = NULL; 318dd3f92deSKalle Valo } 319dd3f92deSKalle Valo 320dd3f92deSKalle Valo int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv) 321dd3f92deSKalle Valo { 322dd3f92deSKalle Valo struct cmd_ds_mac_multicast_addr cmd; 323dd3f92deSKalle Valo 324dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_CMD); 325dd3f92deSKalle Valo 326dd3f92deSKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 327dd3f92deSKalle Valo cmd.action = cpu_to_le16(CMD_ACT_SET); 328dd3f92deSKalle Valo 329dd3f92deSKalle Valo cmd.nr_of_adrs = cpu_to_le16((u16) priv->nr_of_multicastmacaddr); 330dd3f92deSKalle Valo 331dd3f92deSKalle Valo lbtf_deb_cmd("MULTICAST_ADR: setting %d addresses\n", cmd.nr_of_adrs); 332dd3f92deSKalle Valo 333dd3f92deSKalle Valo memcpy(cmd.maclist, priv->multicastlist, 334dd3f92deSKalle Valo priv->nr_of_multicastmacaddr * ETH_ALEN); 335dd3f92deSKalle Valo 336dd3f92deSKalle Valo lbtf_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &cmd.hdr, sizeof(cmd)); 337dd3f92deSKalle Valo 338dd3f92deSKalle Valo lbtf_deb_leave(LBTF_DEB_CMD); 339dd3f92deSKalle Valo return 0; 340dd3f92deSKalle Valo } 341dd3f92deSKalle Valo 342dd3f92deSKalle Valo void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode) 343dd3f92deSKalle Valo { 344dd3f92deSKalle Valo struct cmd_ds_set_mode cmd; 345dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_WEXT); 346dd3f92deSKalle Valo 347dd3f92deSKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 348dd3f92deSKalle Valo cmd.mode = cpu_to_le16(mode); 349dd3f92deSKalle Valo lbtf_deb_wext("Switching to mode: 0x%x\n", mode); 350dd3f92deSKalle Valo lbtf_cmd_async(priv, CMD_802_11_SET_MODE, &cmd.hdr, sizeof(cmd)); 351dd3f92deSKalle Valo 352dd3f92deSKalle Valo lbtf_deb_leave(LBTF_DEB_WEXT); 353dd3f92deSKalle Valo } 354dd3f92deSKalle Valo 355dd3f92deSKalle Valo void lbtf_set_bssid(struct lbtf_private *priv, bool activate, const u8 *bssid) 356dd3f92deSKalle Valo { 357dd3f92deSKalle Valo struct cmd_ds_set_bssid cmd; 358dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_CMD); 359dd3f92deSKalle Valo 360dd3f92deSKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 361dd3f92deSKalle Valo cmd.activate = activate ? 1 : 0; 362dd3f92deSKalle Valo if (activate) 363dd3f92deSKalle Valo memcpy(cmd.bssid, bssid, ETH_ALEN); 364dd3f92deSKalle Valo 365dd3f92deSKalle Valo lbtf_cmd_async(priv, CMD_802_11_SET_BSSID, &cmd.hdr, sizeof(cmd)); 366dd3f92deSKalle Valo lbtf_deb_leave(LBTF_DEB_CMD); 367dd3f92deSKalle Valo } 368dd3f92deSKalle Valo 369dd3f92deSKalle Valo int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr) 370dd3f92deSKalle Valo { 371dd3f92deSKalle Valo struct cmd_ds_802_11_mac_address cmd; 372dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_CMD); 373dd3f92deSKalle Valo 374dd3f92deSKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 375dd3f92deSKalle Valo cmd.action = cpu_to_le16(CMD_ACT_SET); 376dd3f92deSKalle Valo 377dd3f92deSKalle Valo memcpy(cmd.macadd, mac_addr, ETH_ALEN); 378dd3f92deSKalle Valo 379dd3f92deSKalle Valo lbtf_cmd_async(priv, CMD_802_11_MAC_ADDRESS, &cmd.hdr, sizeof(cmd)); 380dd3f92deSKalle Valo lbtf_deb_leave(LBTF_DEB_CMD); 381dd3f92deSKalle Valo return 0; 382dd3f92deSKalle Valo } 383dd3f92deSKalle Valo 384dd3f92deSKalle Valo int lbtf_set_radio_control(struct lbtf_private *priv) 385dd3f92deSKalle Valo { 386dd3f92deSKalle Valo int ret = 0; 387dd3f92deSKalle Valo struct cmd_ds_802_11_radio_control cmd; 388dd3f92deSKalle Valo 389dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_CMD); 390dd3f92deSKalle Valo 391dd3f92deSKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 392dd3f92deSKalle Valo cmd.action = cpu_to_le16(CMD_ACT_SET); 393dd3f92deSKalle Valo 394dd3f92deSKalle Valo switch (priv->preamble) { 395dd3f92deSKalle Valo case CMD_TYPE_SHORT_PREAMBLE: 396dd3f92deSKalle Valo cmd.control = cpu_to_le16(SET_SHORT_PREAMBLE); 397dd3f92deSKalle Valo break; 398dd3f92deSKalle Valo 399dd3f92deSKalle Valo case CMD_TYPE_LONG_PREAMBLE: 400dd3f92deSKalle Valo cmd.control = cpu_to_le16(SET_LONG_PREAMBLE); 401dd3f92deSKalle Valo break; 402dd3f92deSKalle Valo 403dd3f92deSKalle Valo case CMD_TYPE_AUTO_PREAMBLE: 404dd3f92deSKalle Valo default: 405dd3f92deSKalle Valo cmd.control = cpu_to_le16(SET_AUTO_PREAMBLE); 406dd3f92deSKalle Valo break; 407dd3f92deSKalle Valo } 408dd3f92deSKalle Valo 409dd3f92deSKalle Valo if (priv->radioon) 410dd3f92deSKalle Valo cmd.control |= cpu_to_le16(TURN_ON_RF); 411dd3f92deSKalle Valo else 412dd3f92deSKalle Valo cmd.control &= cpu_to_le16(~TURN_ON_RF); 413dd3f92deSKalle Valo 414dd3f92deSKalle Valo lbtf_deb_cmd("RADIO_SET: radio %d, preamble %d\n", priv->radioon, 415dd3f92deSKalle Valo priv->preamble); 416dd3f92deSKalle Valo 417dd3f92deSKalle Valo ret = lbtf_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd); 418dd3f92deSKalle Valo 419dd3f92deSKalle Valo lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret); 420dd3f92deSKalle Valo return ret; 421dd3f92deSKalle Valo } 422dd3f92deSKalle Valo 423dd3f92deSKalle Valo void lbtf_set_mac_control(struct lbtf_private *priv) 424dd3f92deSKalle Valo { 425dd3f92deSKalle Valo struct cmd_ds_mac_control cmd; 426dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_CMD); 427dd3f92deSKalle Valo 428dd3f92deSKalle Valo cmd.hdr.size = cpu_to_le16(sizeof(cmd)); 429dd3f92deSKalle Valo cmd.action = cpu_to_le16(priv->mac_control); 430dd3f92deSKalle Valo cmd.reserved = 0; 431dd3f92deSKalle Valo 432dd3f92deSKalle Valo lbtf_cmd_async(priv, CMD_MAC_CONTROL, 433dd3f92deSKalle Valo &cmd.hdr, sizeof(cmd)); 434dd3f92deSKalle Valo 435dd3f92deSKalle Valo lbtf_deb_leave(LBTF_DEB_CMD); 436dd3f92deSKalle Valo } 437dd3f92deSKalle Valo 438dd3f92deSKalle Valo /** 439dd3f92deSKalle Valo * lbtf_allocate_cmd_buffer - Allocates cmd buffer, links it to free cmd queue 440dd3f92deSKalle Valo * 441dd3f92deSKalle Valo * @priv A pointer to struct lbtf_private structure 442dd3f92deSKalle Valo * 443dd3f92deSKalle Valo * Returns: 0 on success. 444dd3f92deSKalle Valo */ 445dd3f92deSKalle Valo int lbtf_allocate_cmd_buffer(struct lbtf_private *priv) 446dd3f92deSKalle Valo { 447dd3f92deSKalle Valo int ret = 0; 448dd3f92deSKalle Valo u32 bufsize; 449dd3f92deSKalle Valo u32 i; 450dd3f92deSKalle Valo struct cmd_ctrl_node *cmdarray; 451dd3f92deSKalle Valo 452dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_HOST); 453dd3f92deSKalle Valo 454dd3f92deSKalle Valo /* Allocate and initialize the command array */ 455dd3f92deSKalle Valo bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS; 456dd3f92deSKalle Valo cmdarray = kzalloc(bufsize, GFP_KERNEL); 457dd3f92deSKalle Valo if (!cmdarray) { 458dd3f92deSKalle Valo lbtf_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n"); 459dd3f92deSKalle Valo ret = -1; 460dd3f92deSKalle Valo goto done; 461dd3f92deSKalle Valo } 462dd3f92deSKalle Valo priv->cmd_array = cmdarray; 463dd3f92deSKalle Valo 464dd3f92deSKalle Valo /* Allocate and initialize each command buffer in the command array */ 465dd3f92deSKalle Valo for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { 466dd3f92deSKalle Valo cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL); 467dd3f92deSKalle Valo if (!cmdarray[i].cmdbuf) { 468dd3f92deSKalle Valo lbtf_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n"); 469dd3f92deSKalle Valo ret = -1; 470dd3f92deSKalle Valo goto done; 471dd3f92deSKalle Valo } 472dd3f92deSKalle Valo } 473dd3f92deSKalle Valo 474dd3f92deSKalle Valo for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { 475dd3f92deSKalle Valo init_waitqueue_head(&cmdarray[i].cmdwait_q); 476dd3f92deSKalle Valo lbtf_cleanup_and_insert_cmd(priv, &cmdarray[i]); 477dd3f92deSKalle Valo } 478dd3f92deSKalle Valo 479dd3f92deSKalle Valo ret = 0; 480dd3f92deSKalle Valo 481dd3f92deSKalle Valo done: 482dd3f92deSKalle Valo lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret); 483dd3f92deSKalle Valo return ret; 484dd3f92deSKalle Valo } 485dd3f92deSKalle Valo 486dd3f92deSKalle Valo /** 487dd3f92deSKalle Valo * lbtf_free_cmd_buffer - Frees the cmd buffer. 488dd3f92deSKalle Valo * 489dd3f92deSKalle Valo * @priv A pointer to struct lbtf_private structure 490dd3f92deSKalle Valo * 491dd3f92deSKalle Valo * Returns: 0 492dd3f92deSKalle Valo */ 493dd3f92deSKalle Valo int lbtf_free_cmd_buffer(struct lbtf_private *priv) 494dd3f92deSKalle Valo { 495dd3f92deSKalle Valo struct cmd_ctrl_node *cmdarray; 496dd3f92deSKalle Valo unsigned int i; 497dd3f92deSKalle Valo 498dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_HOST); 499dd3f92deSKalle Valo 500dd3f92deSKalle Valo /* need to check if cmd array is allocated or not */ 501dd3f92deSKalle Valo if (priv->cmd_array == NULL) { 502dd3f92deSKalle Valo lbtf_deb_host("FREE_CMD_BUF: cmd_array is NULL\n"); 503dd3f92deSKalle Valo goto done; 504dd3f92deSKalle Valo } 505dd3f92deSKalle Valo 506dd3f92deSKalle Valo cmdarray = priv->cmd_array; 507dd3f92deSKalle Valo 508dd3f92deSKalle Valo /* Release shared memory buffers */ 509dd3f92deSKalle Valo for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { 510dd3f92deSKalle Valo kfree(cmdarray[i].cmdbuf); 511dd3f92deSKalle Valo cmdarray[i].cmdbuf = NULL; 512dd3f92deSKalle Valo } 513dd3f92deSKalle Valo 514dd3f92deSKalle Valo /* Release cmd_ctrl_node */ 515dd3f92deSKalle Valo kfree(priv->cmd_array); 516dd3f92deSKalle Valo priv->cmd_array = NULL; 517dd3f92deSKalle Valo 518dd3f92deSKalle Valo done: 519dd3f92deSKalle Valo lbtf_deb_leave(LBTF_DEB_HOST); 520dd3f92deSKalle Valo return 0; 521dd3f92deSKalle Valo } 522dd3f92deSKalle Valo 523dd3f92deSKalle Valo /** 524dd3f92deSKalle Valo * lbtf_get_cmd_ctrl_node - Gets free cmd node from free cmd queue. 525dd3f92deSKalle Valo * 526dd3f92deSKalle Valo * @priv A pointer to struct lbtf_private structure 527dd3f92deSKalle Valo * 528dd3f92deSKalle Valo * Returns: pointer to a struct cmd_ctrl_node or NULL if none available. 529dd3f92deSKalle Valo */ 530dd3f92deSKalle Valo static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv) 531dd3f92deSKalle Valo { 532dd3f92deSKalle Valo struct cmd_ctrl_node *tempnode; 533dd3f92deSKalle Valo unsigned long flags; 534dd3f92deSKalle Valo 535dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_HOST); 536dd3f92deSKalle Valo 537dd3f92deSKalle Valo if (!priv) 538dd3f92deSKalle Valo return NULL; 539dd3f92deSKalle Valo 540dd3f92deSKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 541dd3f92deSKalle Valo 542dd3f92deSKalle Valo if (!list_empty(&priv->cmdfreeq)) { 543dd3f92deSKalle Valo tempnode = list_first_entry(&priv->cmdfreeq, 544dd3f92deSKalle Valo struct cmd_ctrl_node, list); 545dd3f92deSKalle Valo list_del(&tempnode->list); 546dd3f92deSKalle Valo } else { 547dd3f92deSKalle Valo lbtf_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n"); 548dd3f92deSKalle Valo tempnode = NULL; 549dd3f92deSKalle Valo } 550dd3f92deSKalle Valo 551dd3f92deSKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 552dd3f92deSKalle Valo 553dd3f92deSKalle Valo lbtf_deb_leave(LBTF_DEB_HOST); 554dd3f92deSKalle Valo return tempnode; 555dd3f92deSKalle Valo } 556dd3f92deSKalle Valo 557dd3f92deSKalle Valo /** 558dd3f92deSKalle Valo * lbtf_execute_next_command: execute next command in cmd pending queue. 559dd3f92deSKalle Valo * 560dd3f92deSKalle Valo * @priv A pointer to struct lbtf_private structure 561dd3f92deSKalle Valo * 562dd3f92deSKalle Valo * Returns: 0 on success. 563dd3f92deSKalle Valo */ 564dd3f92deSKalle Valo int lbtf_execute_next_command(struct lbtf_private *priv) 565dd3f92deSKalle Valo { 566dd3f92deSKalle Valo struct cmd_ctrl_node *cmdnode = NULL; 567dd3f92deSKalle Valo struct cmd_header *cmd; 568dd3f92deSKalle Valo unsigned long flags; 569dd3f92deSKalle Valo int ret = 0; 570dd3f92deSKalle Valo 571dd3f92deSKalle Valo /* Debug group is lbtf_deb_THREAD and not lbtf_deb_HOST, because the 572dd3f92deSKalle Valo * only caller to us is lbtf_thread() and we get even when a 573dd3f92deSKalle Valo * data packet is received */ 574dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_THREAD); 575dd3f92deSKalle Valo 576dd3f92deSKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 577dd3f92deSKalle Valo 578dd3f92deSKalle Valo if (priv->cur_cmd) { 579dd3f92deSKalle Valo pr_alert("EXEC_NEXT_CMD: already processing command!\n"); 580dd3f92deSKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 581dd3f92deSKalle Valo ret = -1; 582dd3f92deSKalle Valo goto done; 583dd3f92deSKalle Valo } 584dd3f92deSKalle Valo 585dd3f92deSKalle Valo if (!list_empty(&priv->cmdpendingq)) { 586dd3f92deSKalle Valo cmdnode = list_first_entry(&priv->cmdpendingq, 587dd3f92deSKalle Valo struct cmd_ctrl_node, list); 588dd3f92deSKalle Valo } 589dd3f92deSKalle Valo 590dd3f92deSKalle Valo if (cmdnode) { 591dd3f92deSKalle Valo cmd = cmdnode->cmdbuf; 592dd3f92deSKalle Valo 593dd3f92deSKalle Valo list_del(&cmdnode->list); 594dd3f92deSKalle Valo lbtf_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", 595dd3f92deSKalle Valo le16_to_cpu(cmd->command)); 596dd3f92deSKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 597dd3f92deSKalle Valo lbtf_submit_command(priv, cmdnode); 598dd3f92deSKalle Valo } else 599dd3f92deSKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 600dd3f92deSKalle Valo 601dd3f92deSKalle Valo ret = 0; 602dd3f92deSKalle Valo done: 603dd3f92deSKalle Valo lbtf_deb_leave(LBTF_DEB_THREAD); 604dd3f92deSKalle Valo return ret; 605dd3f92deSKalle Valo } 606dd3f92deSKalle Valo 607dd3f92deSKalle Valo static struct cmd_ctrl_node *__lbtf_cmd_async(struct lbtf_private *priv, 608dd3f92deSKalle Valo uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, 609dd3f92deSKalle Valo int (*callback)(struct lbtf_private *, unsigned long, 610dd3f92deSKalle Valo struct cmd_header *), 611dd3f92deSKalle Valo unsigned long callback_arg) 612dd3f92deSKalle Valo { 613dd3f92deSKalle Valo struct cmd_ctrl_node *cmdnode; 614dd3f92deSKalle Valo 615dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_HOST); 616dd3f92deSKalle Valo 617dd3f92deSKalle Valo if (priv->surpriseremoved) { 618dd3f92deSKalle Valo lbtf_deb_host("PREP_CMD: card removed\n"); 619dd3f92deSKalle Valo cmdnode = ERR_PTR(-ENOENT); 620dd3f92deSKalle Valo goto done; 621dd3f92deSKalle Valo } 622dd3f92deSKalle Valo 623dd3f92deSKalle Valo cmdnode = lbtf_get_cmd_ctrl_node(priv); 624dd3f92deSKalle Valo if (cmdnode == NULL) { 625dd3f92deSKalle Valo lbtf_deb_host("PREP_CMD: cmdnode is NULL\n"); 626dd3f92deSKalle Valo 627dd3f92deSKalle Valo /* Wake up main thread to execute next command */ 628dd3f92deSKalle Valo queue_work(lbtf_wq, &priv->cmd_work); 629dd3f92deSKalle Valo cmdnode = ERR_PTR(-ENOBUFS); 630dd3f92deSKalle Valo goto done; 631dd3f92deSKalle Valo } 632dd3f92deSKalle Valo 633dd3f92deSKalle Valo cmdnode->callback = callback; 634dd3f92deSKalle Valo cmdnode->callback_arg = callback_arg; 635dd3f92deSKalle Valo 636dd3f92deSKalle Valo /* Copy the incoming command to the buffer */ 637dd3f92deSKalle Valo memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size); 638dd3f92deSKalle Valo 639dd3f92deSKalle Valo /* Set sequence number, clean result, move to buffer */ 640dd3f92deSKalle Valo priv->seqnum++; 641dd3f92deSKalle Valo cmdnode->cmdbuf->command = cpu_to_le16(command); 642dd3f92deSKalle Valo cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size); 643dd3f92deSKalle Valo cmdnode->cmdbuf->seqnum = cpu_to_le16(priv->seqnum); 644dd3f92deSKalle Valo cmdnode->cmdbuf->result = 0; 645dd3f92deSKalle Valo 646dd3f92deSKalle Valo lbtf_deb_host("PREP_CMD: command 0x%04x\n", command); 647dd3f92deSKalle Valo 648dd3f92deSKalle Valo cmdnode->cmdwaitqwoken = 0; 649dd3f92deSKalle Valo lbtf_queue_cmd(priv, cmdnode); 650dd3f92deSKalle Valo queue_work(lbtf_wq, &priv->cmd_work); 651dd3f92deSKalle Valo 652dd3f92deSKalle Valo done: 653dd3f92deSKalle Valo lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %p", cmdnode); 654dd3f92deSKalle Valo return cmdnode; 655dd3f92deSKalle Valo } 656dd3f92deSKalle Valo 657dd3f92deSKalle Valo void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command, 658dd3f92deSKalle Valo struct cmd_header *in_cmd, int in_cmd_size) 659dd3f92deSKalle Valo { 660dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_CMD); 661dd3f92deSKalle Valo __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, NULL, 0); 662dd3f92deSKalle Valo lbtf_deb_leave(LBTF_DEB_CMD); 663dd3f92deSKalle Valo } 664dd3f92deSKalle Valo 665dd3f92deSKalle Valo int __lbtf_cmd(struct lbtf_private *priv, uint16_t command, 666dd3f92deSKalle Valo struct cmd_header *in_cmd, int in_cmd_size, 667dd3f92deSKalle Valo int (*callback)(struct lbtf_private *, 668dd3f92deSKalle Valo unsigned long, struct cmd_header *), 669dd3f92deSKalle Valo unsigned long callback_arg) 670dd3f92deSKalle Valo { 671dd3f92deSKalle Valo struct cmd_ctrl_node *cmdnode; 672dd3f92deSKalle Valo unsigned long flags; 673dd3f92deSKalle Valo int ret = 0; 674dd3f92deSKalle Valo 675dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_HOST); 676dd3f92deSKalle Valo 677dd3f92deSKalle Valo cmdnode = __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, 678dd3f92deSKalle Valo callback, callback_arg); 679dd3f92deSKalle Valo if (IS_ERR(cmdnode)) { 680dd3f92deSKalle Valo ret = PTR_ERR(cmdnode); 681dd3f92deSKalle Valo goto done; 682dd3f92deSKalle Valo } 683dd3f92deSKalle Valo 684dd3f92deSKalle Valo might_sleep(); 685dd3f92deSKalle Valo ret = wait_event_interruptible(cmdnode->cmdwait_q, 686dd3f92deSKalle Valo cmdnode->cmdwaitqwoken); 687dd3f92deSKalle Valo if (ret) { 688dd3f92deSKalle Valo pr_info("PREP_CMD: command 0x%04x interrupted by signal: %d\n", 689dd3f92deSKalle Valo command, ret); 690dd3f92deSKalle Valo goto done; 691dd3f92deSKalle Valo } 692dd3f92deSKalle Valo 693dd3f92deSKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 694dd3f92deSKalle Valo ret = cmdnode->result; 695dd3f92deSKalle Valo if (ret) 696dd3f92deSKalle Valo pr_info("PREP_CMD: command 0x%04x failed: %d\n", 697dd3f92deSKalle Valo command, ret); 698dd3f92deSKalle Valo 699dd3f92deSKalle Valo __lbtf_cleanup_and_insert_cmd(priv, cmdnode); 700dd3f92deSKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 701dd3f92deSKalle Valo 702dd3f92deSKalle Valo done: 703dd3f92deSKalle Valo lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret); 704dd3f92deSKalle Valo return ret; 705dd3f92deSKalle Valo } 706dd3f92deSKalle Valo EXPORT_SYMBOL_GPL(__lbtf_cmd); 707dd3f92deSKalle Valo 708dd3f92deSKalle Valo /* Call holding driver_lock */ 709dd3f92deSKalle Valo void lbtf_cmd_response_rx(struct lbtf_private *priv) 710dd3f92deSKalle Valo { 711dd3f92deSKalle Valo priv->cmd_response_rxed = 1; 712dd3f92deSKalle Valo queue_work(lbtf_wq, &priv->cmd_work); 713dd3f92deSKalle Valo } 714dd3f92deSKalle Valo EXPORT_SYMBOL_GPL(lbtf_cmd_response_rx); 715dd3f92deSKalle Valo 716dd3f92deSKalle Valo int lbtf_process_rx_command(struct lbtf_private *priv) 717dd3f92deSKalle Valo { 718dd3f92deSKalle Valo uint16_t respcmd, curcmd; 719dd3f92deSKalle Valo struct cmd_header *resp; 720dd3f92deSKalle Valo int ret = 0; 721dd3f92deSKalle Valo unsigned long flags; 722dd3f92deSKalle Valo uint16_t result; 723dd3f92deSKalle Valo 724dd3f92deSKalle Valo lbtf_deb_enter(LBTF_DEB_CMD); 725dd3f92deSKalle Valo 726dd3f92deSKalle Valo mutex_lock(&priv->lock); 727dd3f92deSKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 728dd3f92deSKalle Valo 729dd3f92deSKalle Valo if (!priv->cur_cmd) { 730dd3f92deSKalle Valo ret = -1; 731dd3f92deSKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 732dd3f92deSKalle Valo goto done; 733dd3f92deSKalle Valo } 734dd3f92deSKalle Valo 735dd3f92deSKalle Valo resp = (void *)priv->cmd_resp_buff; 736dd3f92deSKalle Valo curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command); 737dd3f92deSKalle Valo respcmd = le16_to_cpu(resp->command); 738dd3f92deSKalle Valo result = le16_to_cpu(resp->result); 739dd3f92deSKalle Valo 740*744972b2SLubomir Rintel lbtf_deb_cmd("libertastf: cmd response 0x%04x, seq %d, size %d\n", 741dd3f92deSKalle Valo respcmd, le16_to_cpu(resp->seqnum), 742dd3f92deSKalle Valo le16_to_cpu(resp->size)); 743dd3f92deSKalle Valo 744dd3f92deSKalle Valo if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) { 745dd3f92deSKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 746dd3f92deSKalle Valo ret = -1; 747dd3f92deSKalle Valo goto done; 748dd3f92deSKalle Valo } 749dd3f92deSKalle Valo if (respcmd != CMD_RET(curcmd)) { 750dd3f92deSKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 751dd3f92deSKalle Valo ret = -1; 752dd3f92deSKalle Valo goto done; 753dd3f92deSKalle Valo } 754dd3f92deSKalle Valo 755dd3f92deSKalle Valo if (resp->result == cpu_to_le16(0x0004)) { 756dd3f92deSKalle Valo /* 0x0004 means -EAGAIN. Drop the response, let it time out 757dd3f92deSKalle Valo and be resubmitted */ 758dd3f92deSKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 759dd3f92deSKalle Valo ret = -1; 760dd3f92deSKalle Valo goto done; 761dd3f92deSKalle Valo } 762dd3f92deSKalle Valo 763dd3f92deSKalle Valo /* Now we got response from FW, cancel the command timer */ 764dd3f92deSKalle Valo del_timer(&priv->command_timer); 765dd3f92deSKalle Valo priv->cmd_timed_out = 0; 766dd3f92deSKalle Valo if (priv->nr_retries) 767dd3f92deSKalle Valo priv->nr_retries = 0; 768dd3f92deSKalle Valo 769dd3f92deSKalle Valo /* If the command is not successful, cleanup and return failure */ 770dd3f92deSKalle Valo if ((result != 0 || !(respcmd & 0x8000))) { 771dd3f92deSKalle Valo /* 772dd3f92deSKalle Valo * Handling errors here 773dd3f92deSKalle Valo */ 774dd3f92deSKalle Valo switch (respcmd) { 775dd3f92deSKalle Valo case CMD_RET(CMD_GET_HW_SPEC): 776dd3f92deSKalle Valo case CMD_RET(CMD_802_11_RESET): 777dd3f92deSKalle Valo pr_info("libertastf: reset failed\n"); 778dd3f92deSKalle Valo break; 779dd3f92deSKalle Valo 780dd3f92deSKalle Valo } 781dd3f92deSKalle Valo lbtf_complete_command(priv, priv->cur_cmd, result); 782dd3f92deSKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 783dd3f92deSKalle Valo 784dd3f92deSKalle Valo ret = -1; 785dd3f92deSKalle Valo goto done; 786dd3f92deSKalle Valo } 787dd3f92deSKalle Valo 788dd3f92deSKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 789dd3f92deSKalle Valo 790dd3f92deSKalle Valo if (priv->cur_cmd && priv->cur_cmd->callback) { 791dd3f92deSKalle Valo ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg, 792dd3f92deSKalle Valo resp); 793dd3f92deSKalle Valo } 794dd3f92deSKalle Valo spin_lock_irqsave(&priv->driver_lock, flags); 795dd3f92deSKalle Valo 796dd3f92deSKalle Valo if (priv->cur_cmd) { 797dd3f92deSKalle Valo /* Clean up and Put current command back to cmdfreeq */ 798dd3f92deSKalle Valo lbtf_complete_command(priv, priv->cur_cmd, result); 799dd3f92deSKalle Valo } 800dd3f92deSKalle Valo spin_unlock_irqrestore(&priv->driver_lock, flags); 801dd3f92deSKalle Valo 802dd3f92deSKalle Valo done: 803dd3f92deSKalle Valo mutex_unlock(&priv->lock); 804dd3f92deSKalle Valo lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret); 805dd3f92deSKalle Valo return ret; 806dd3f92deSKalle Valo } 807