static int carl9170_check_sequence(struct ar9170 *ar, unsigned int seq) { if (ar->cmd_seq < -1) return 0; /* * Initialize Counter */ if (ar->cmd_seq < 0) ar->cmd_seq = seq; /* * The sequence is strictly monotonic increasing and it never skips! * * Therefore we can safely assume that whenever we received an * unexpected sequence we have lost some valuable data. */ if (seq != ar->cmd_seq) { int count; count = (seq - ar->cmd_seq) % ar->fw.cmd_bufs; wiphy_err(ar->hw->wiphy, "lost %d command responses/traps! " "w:%d g:%d\n", count, ar->cmd_seq, seq); carl9170_restart(ar, CARL9170_RR_LOST_RSP); return -EIO; } ar->cmd_seq = (ar->cmd_seq + 1) % ar->fw.cmd_bufs; return 0; }
static void carl9170_dbg_message(struct ar9170 *ar, const char *buf, u32 len) { bool restart = false; enum carl9170_restart_reasons reason = CARL9170_RR_NO_REASON; if (len > 3) { if (memcmp(buf, CARL9170_ERR_MAGIC, 3) == 0) { ar->fw.err_counter++; if (ar->fw.err_counter > 3) { restart = true; reason = CARL9170_RR_TOO_MANY_FIRMWARE_ERRORS; } } if (memcmp(buf, CARL9170_BUG_MAGIC, 3) == 0) { ar->fw.bug_counter++; restart = true; reason = CARL9170_RR_FATAL_FIRMWARE_ERROR; } } wiphy_info(ar->hw->wiphy, "FW: %.*s\n", len, buf); if (restart) carl9170_restart(ar, reason); }
static void carl9170_cmd_callback(struct ar9170 *ar, u32 len, void *buffer) { /* * Some commands may have a variable response length * and we cannot predict the correct length in advance. * So we only check if we provided enough space for the data. */ if (unlikely(ar->readlen != (len - 4))) { dev_warn(&ar->udev->dev, "received invalid command response:" "got %d, instead of %d\n", len - 4, ar->readlen); print_hex_dump_bytes("carl9170 cmd:", DUMP_PREFIX_OFFSET, ar->cmd_buf, (ar->cmd.hdr.len + 4) & 0x3f); print_hex_dump_bytes("carl9170 rsp:", DUMP_PREFIX_OFFSET, buffer, len); /* * Do not complete. The command times out, * and we get a stack trace from there. */ carl9170_restart(ar, CARL9170_RR_INVALID_RSP); } spin_lock(&ar->cmd_lock); if (ar->readbuf) { if (len >= 4) memcpy(ar->readbuf, buffer + 4, len - 4); ar->readbuf = NULL; } complete(&ar->cmd_wait); spin_unlock(&ar->cmd_lock); }
int carl9170_exec_cmd(struct ar9170 *ar, const enum carl9170_cmd_oids cmd, unsigned int plen, void *payload, unsigned int outlen, void *out) { int err = -ENOMEM; if (!IS_ACCEPTING_CMD(ar)) return -EIO; if (!(cmd & CARL9170_CMD_ASYNC_FLAG)) might_sleep(); ar->cmd.hdr.len = plen; ar->cmd.hdr.cmd = cmd; /* writing multiple regs fills this buffer already */ if (plen && payload != (u8 *)(ar->cmd.data)) memcpy(ar->cmd.data, payload, plen); spin_lock_bh(&ar->cmd_lock); ar->readbuf = (u8 *)out; ar->readlen = outlen; spin_unlock_bh(&ar->cmd_lock); err = __carl9170_exec_cmd(ar, &ar->cmd, false); if (!(cmd & CARL9170_CMD_ASYNC_FLAG)) { err = wait_for_completion_timeout(&ar->cmd_wait, HZ); if (err == 0) { err = -ETIMEDOUT; goto err_unbuf; } if (ar->readlen != outlen) { err = -EMSGSIZE; goto err_unbuf; } } return 0; err_unbuf: /* Maybe the device was removed in the moment we were waiting? */ if (IS_STARTED(ar)) { dev_err(&ar->udev->dev, "no command feedback " "received (%d).\n", err); /* provide some maybe useful debug information */ print_hex_dump_bytes("carl9170 cmd: ", DUMP_PREFIX_NONE, &ar->cmd, plen + 4); carl9170_restart(ar, CARL9170_RR_COMMAND_TIMEOUT); } /* invalidate to avoid completing the next command prematurely */ spin_lock_bh(&ar->cmd_lock); ar->readbuf = NULL; ar->readlen = 0; spin_unlock_bh(&ar->cmd_lock); return err; }
static void carl9170_usb_rx_complete(struct urb *urb) { struct ar9170 *ar = (struct ar9170 *)urb->context; int err; if (WARN_ON_ONCE(!ar)) return; atomic_dec(&ar->rx_anch_urbs); switch (urb->status) { case 0: /* rx path */ usb_anchor_urb(urb, &ar->rx_work); atomic_inc(&ar->rx_work_urbs); break; case -ENOENT: case -ECONNRESET: case -ENODEV: case -ESHUTDOWN: /* handle disconnect events*/ return; default: /* handle all other errors */ usb_anchor_urb(urb, &ar->rx_pool); atomic_inc(&ar->rx_pool_urbs); break; } err = carl9170_usb_submit_rx_urb(ar, GFP_ATOMIC); if (unlikely(err)) { /* * usb_submit_rx_urb reported a problem. * In case this is due to a rx buffer shortage, * elevate the tasklet worker priority to * the highest available level. */ tasklet_hi_schedule(&ar->usb_tasklet); if (atomic_read(&ar->rx_anch_urbs) == 0) { /* * The system is too slow to cope with * the enormous workload. We have simply * run out of active rx urbs and this * unfortunatly leads to an unpredictable * device. */ carl9170_restart(ar, CARL9170_RR_SLOW_SYSTEM); } } else { /* * Using anything less than _high_ priority absolutely * kills the rx performance my UP-System... */ tasklet_hi_schedule(&ar->usb_tasklet); } }
void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len) { struct carl9170_rsp *cmd = (void *) buf; struct ieee80211_vif *vif; if (carl9170_check_sequence(ar, cmd->hdr.seq)) return; if ((cmd->hdr.cmd & CARL9170_RSP_FLAG) != CARL9170_RSP_FLAG) { if (!(cmd->hdr.cmd & CARL9170_CMD_ASYNC_FLAG)) carl9170_cmd_callback(ar, len, buf); return; } if (unlikely(cmd->hdr.len != (len - 4))) { if (net_ratelimit()) { wiphy_err(ar->hw->wiphy, "FW: received over-/under" "sized event %x (%d, but should be %d).\n", cmd->hdr.cmd, cmd->hdr.len, len - 4); print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len); } return; } /* hardware event handlers */ switch (cmd->hdr.cmd) { case CARL9170_RSP_PRETBTT: /* pre-TBTT event */ rcu_read_lock(); vif = carl9170_get_main_vif(ar); if (!vif) { rcu_read_unlock(); break; } switch (vif->type) { case NL80211_IFTYPE_STATION: carl9170_handle_ps(ar, cmd); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_ADHOC: carl9170_update_beacon(ar, true); break; default: break; } rcu_read_unlock(); break; case CARL9170_RSP_TXCOMP: /* TX status notification */ carl9170_tx_process_status(ar, cmd); break; case CARL9170_RSP_BEACON_CONFIG: /* * (IBSS) beacon send notification * bytes: 04 c2 XX YY B4 B3 B2 B1 * * XX always 80 * YY always 00 * B1-B4 "should" be the number of send out beacons. */ break; case CARL9170_RSP_ATIM: /* End of Atim Window */ break; case CARL9170_RSP_WATCHDOG: /* Watchdog Interrupt */ carl9170_restart(ar, CARL9170_RR_WATCHDOG); break; case CARL9170_RSP_TEXT: /* firmware debug */ carl9170_dbg_message(ar, (char *)buf + 4, len - 4); break; case CARL9170_RSP_HEXDUMP: wiphy_dbg(ar->hw->wiphy, "FW: HD %d\n", len - 4); print_hex_dump_bytes("FW:", DUMP_PREFIX_NONE, (char *)buf + 4, len - 4); break; case CARL9170_RSP_RADAR: if (!net_ratelimit()) break; wiphy_info(ar->hw->wiphy, "FW: RADAR! Please report this " "incident to [email protected] !\n"); break; case CARL9170_RSP_GPIO: #ifdef CONFIG_CARL9170_WPC if (ar->wps.pbc) { bool state = !!(cmd->gpio.gpio & cpu_to_le32( AR9170_GPIO_PORT_WPS_BUTTON_PRESSED)); if (state != ar->wps.pbc_state) { ar->wps.pbc_state = state; input_report_key(ar->wps.pbc, KEY_WPS_BUTTON, state); input_sync(ar->wps.pbc); } } #endif /* CONFIG_CARL9170_WPC */ break; case CARL9170_RSP_BOOT: complete(&ar->fw_boot_wait); break; default: wiphy_err(ar->hw->wiphy, "FW: received unhandled event %x\n", cmd->hdr.cmd); print_hex_dump_bytes("dump:", DUMP_PREFIX_NONE, buf, len); break; } }