static int wil_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); struct wireless_dev *wdev = wil->wdev; struct { struct wmi_start_scan_cmd cmd; u16 chnl[4]; } __packed cmd; uint i, n; int rc; if (wil->scan_request) { wil_err(wil, "Already scanning\n"); return -EAGAIN; } /* check we are client side */ switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: break; default: return -EOPNOTSUPP; } /* FW don't support scan after connection attempt */ if (test_bit(wil_status_dontscan, &wil->status)) { wil_err(wil, "Can't scan now\n"); return -EBUSY; } wil->scan_request = request; mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO); memset(&cmd, 0, sizeof(cmd)); cmd.cmd.num_channels = 0; n = min(request->n_channels, 4U); for (i = 0; i < n; i++) { int ch = request->channels[i]->hw_value; if (ch == 0) { wil_err(wil, "Scan requested for unknown frequency %dMhz\n", request->channels[i]->center_freq); continue; } /* 0-based channel indexes */ cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1; wil_dbg_misc(wil, "Scan for ch %d : %d MHz\n", ch, request->channels[i]->center_freq); } rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); if (rc) wil->scan_request = NULL; return rc; }
static ssize_t wil_write_file_ssid(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct wil6210_priv *wil = file->private_data; struct wireless_dev *wdev = wil_to_wdev(wil); struct net_device *ndev = wil_to_ndev(wil); if (*ppos != 0) { wil_err(wil, "Unable to set SSID substring from [%d]\n", (int)*ppos); return -EINVAL; } if (count > sizeof(wdev->ssid)) { wil_err(wil, "SSID too long, len = %d\n", (int)count); return -EINVAL; } if (netif_running(ndev)) { wil_err(wil, "Unable to change SSID on running interface\n"); return -EINVAL; } wdev->ssid_len = count; return simple_write_to_buffer(wdev->ssid, wdev->ssid_len, ppos, buf, count); }
static void __iomem *wil_ioc_addr(struct wil6210_priv *wil, uint32_t addr, uint32_t size, enum wil_memio_op op) { void __iomem *a; u32 off; switch (op & wil_mmio_addr_mask) { case wil_mmio_addr_linker: a = wmi_buffer(wil, cpu_to_le32(addr)); break; case wil_mmio_addr_ahb: a = wmi_addr(wil, addr); break; case wil_mmio_addr_bar: a = wmi_addr(wil, addr + WIL6210_FW_HOST_OFF); break; default: wil_err(wil, "Unsupported address mode, op = 0x%08x\n", op); return NULL; } off = a - wil->csr; if (size >= WIL6210_MEM_SIZE - off) { wil_err(wil, "Requested block does not fit into memory: " "off = 0x%08x size = 0x%08x\n", off, size); return NULL; } return a; }
static int wil_ioc_memio_block(struct wil6210_priv *wil, void __user *data) { struct wil_memio_block io; void *block; void __iomem *a; int rc = 0; if (copy_from_user(&io, data, sizeof(io))) return -EFAULT; wil_dbg_ioctl(wil, "IO: addr = 0x%08x size = 0x%08x op = 0x%08x\n", io.addr, io.size, io.op); /* size */ if (io.size % 4) { wil_err(wil, "size is not multiple of 4: 0x%08x\n", io.size); return -EINVAL; } a = wil_ioc_addr(wil, io.addr, io.size, io.op); if (!a) { wil_err(wil, "invalid address 0x%08x, op = 0x%08x\n", io.addr, io.op); return -EINVAL; } block = kmalloc(io.size, GFP_USER); if (!block) return -ENOMEM; /* operation */ switch (io.op & wil_mmio_op_mask) { case wil_mmio_read: wil_memcpy_fromio_32(block, a, io.size); wil_hex_dump_ioctl("Read ", block, io.size); if (copy_to_user(io.block, block, io.size)) { rc = -EFAULT; goto out_free; } break; case wil_mmio_write: if (copy_from_user(block, io.block, io.size)) { rc = -EFAULT; goto out_free; } wil_memcpy_toio_32(a, block, io.size); wmb(); /* make sure write propagated to HW */ wil_hex_dump_ioctl("Write ", block, io.size); break; default: wil_err(wil, "Unsupported operation, op = 0x%08x\n", io.op); rc = -EINVAL; break; } out_free: kfree(block); return rc; }
/* Bus ops */ static int wil_if_pcie_enable(struct wil6210_priv *wil) { struct pci_dev *pdev = wil->pdev; int rc; pci_set_master(pdev); /* * how many MSI interrupts to request? */ switch (use_msi) { case 3: case 1: case 0: break; default: wil_err(wil, "Invalid use_msi=%d, default to 1\n", use_msi); use_msi = 1; } wil->n_msi = use_msi; if (wil->n_msi) { wil_dbg_misc(wil, "Setup %d MSI interrupts\n", use_msi); rc = pci_enable_msi_block(pdev, wil->n_msi); if (rc && (wil->n_msi == 3)) { wil_err(wil, "3 MSI mode failed, try 1 MSI\n"); wil->n_msi = 1; rc = pci_enable_msi_block(pdev, wil->n_msi); } if (rc) { wil_err(wil, "pci_enable_msi failed, use INTx\n"); wil->n_msi = 0; } } else { wil_dbg_misc(wil, "MSI interrupts disabled, use INTx\n"); } rc = wil6210_init_irq(wil, pdev->irq); if (rc) goto stop_master; /* need reset here to obtain MAC */ rc = wil_reset(wil); if (debug_fw) rc = 0; if (rc) goto release_irq; return 0; release_irq: wil6210_fini_irq(wil, pdev->irq); /* safe to call if no MSI */ pci_disable_msi(pdev); stop_master: pci_clear_master(pdev); return rc; }
int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration, struct ieee80211_channel *chan, u64 *cookie) { struct wil_p2p_info *p2p = &wil->p2p; u8 channel = P2P_DMG_SOCIAL_CHANNEL; int rc; if (!chan) return -EINVAL; channel = chan->hw_value; wil_dbg_misc(wil, "%s: duration %d\n", __func__, duration); mutex_lock(&wil->mutex); if (p2p->discovery_started) { wil_err(wil, "%s: discovery already ongoing\n", __func__); rc = -EBUSY; goto out; } rc = wmi_p2p_cfg(wil, channel, P2P_DEFAULT_BI); if (rc) { wil_err(wil, "%s: wmi_p2p_cfg failed\n", __func__); goto out; } rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID); if (rc) { wil_err(wil, "%s: wmi_set_ssid failed\n", __func__); goto out_stop; } rc = wmi_start_listen(wil); if (rc) { wil_err(wil, "%s: wmi_start_listen failed\n", __func__); goto out_stop; } memcpy(&p2p->listen_chan, chan, sizeof(*chan)); *cookie = ++p2p->cookie; p2p->discovery_started = 1; INIT_WORK(&p2p->discovery_expired_work, wil_p2p_listen_expired); mod_timer(&p2p->discovery_timer, jiffies + msecs_to_jiffies(duration)); out_stop: if (rc) wmi_stop_discovery(wil); out: mutex_unlock(&wil->mutex); return rc; }
/* block ack control, write: * - "add <ringid> <agg_size> <timeout>" to trigger ADDBA * - "del_tx <ringid> <reason>" to trigger DELBA for Tx side * - "del_rx <CID> <TID> <reason>" to trigger DELBA for Rx side */ static ssize_t wil_write_back(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { struct wil6210_priv *wil = file->private_data; int rc; char *kbuf = kmalloc(len + 1, GFP_KERNEL); char cmd[9]; int p1, p2, p3; if (!kbuf) return -ENOMEM; rc = simple_write_to_buffer(kbuf, len, ppos, buf, len); if (rc != len) { kfree(kbuf); return rc >= 0 ? -EIO : rc; } kbuf[len] = '\0'; rc = sscanf(kbuf, "%8s %d %d %d", cmd, &p1, &p2, &p3); kfree(kbuf); if (rc < 0) return rc; if (rc < 2) return -EINVAL; if (0 == strcmp(cmd, "add")) { if (rc < 3) { wil_err(wil, "BACK: add require at least 2 params\n"); return -EINVAL; } if (rc < 4) p3 = 0; wmi_addba(wil, p1, p2, p3); } else if (0 == strcmp(cmd, "del_tx")) { if (rc < 3) p2 = WLAN_REASON_QSTA_LEAVE_QBSS; wmi_delba_tx(wil, p1, p2); } else if (0 == strcmp(cmd, "del_rx")) { if (rc < 3) { wil_err(wil, "BACK: del_rx require at least 2 params\n"); return -EINVAL; } if (rc < 4) p3 = WLAN_REASON_QSTA_LEAVE_QBSS; wmi_delba_rx(wil, mk_cidxtid(p1, p2), p3); } else { wil_err(wil, "BACK: Unrecognized command \"%s\"\n", cmd); return -EINVAL; } return len; }
static int wil_suspend_radio_off(struct wil6210_priv *wil) { int rc = 0; bool active_ifaces; wil_dbg_pm(wil, "suspend radio off\n"); rc = down_write_trylock(&wil->mem_lock); if (!rc) { wil_err(wil, "device is busy. down_write_trylock failed, returned (0x%x)\n", rc); wil->suspend_stats.rejected_by_host++; return -EBUSY; } set_bit(wil_status_suspending, wil->status); up_write(&wil->mem_lock); /* if netif up, hardware is alive, shut it down */ mutex_lock(&wil->vif_mutex); active_ifaces = wil_has_active_ifaces(wil, true, false); mutex_unlock(&wil->vif_mutex); if (active_ifaces) { rc = wil_down(wil); if (rc) { wil_err(wil, "wil_down : %d\n", rc); wil->suspend_stats.r_off.failed_suspends++; goto out; } } /* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */ wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n"); wil_disable_irq(wil); if (wil->platform_ops.suspend) { rc = wil->platform_ops.suspend(wil->platform_handle, false); if (rc) { wil_enable_irq(wil); wil->suspend_stats.r_off.failed_suspends++; goto out; } } set_bit(wil_status_suspended, wil->status); out: clear_bit(wil_status_suspending, wil->status); wil_dbg_pm(wil, "suspend radio off: %d\n", rc); return rc; }
/* Bus ops */ static int wil_if_pcie_enable(struct wil6210_priv *wil) { struct pci_dev *pdev = wil->pdev; int rc; /* on platforms with buggy ACPI, pdev->msi_enabled may be set to * allow pci_enable_device to work. This indicates INTx was not routed * and only MSI should be used */ int msi_only = pdev->msi_enabled; bool _use_msi = use_msi; wil_dbg_misc(wil, "%s()\n", __func__); pdev->msi_enabled = 0; pci_set_master(pdev); wil_dbg_misc(wil, "Setup %s interrupt\n", use_msi ? "MSI" : "INTx"); if (use_msi && pci_enable_msi(pdev)) { wil_err(wil, "pci_enable_msi failed, use INTx\n"); _use_msi = false; } if (!_use_msi && msi_only) { wil_err(wil, "Interrupt pin not routed, unable to use INTx\n"); rc = -ENODEV; goto stop_master; } rc = wil6210_init_irq(wil, pdev->irq, _use_msi); if (rc) goto stop_master; /* need reset here to obtain MAC */ mutex_lock(&wil->mutex); rc = wil_reset(wil, false); mutex_unlock(&wil->mutex); if (rc) goto release_irq; return 0; release_irq: wil6210_fini_irq(wil, pdev->irq); /* safe to call if no MSI */ pci_disable_msi(pdev); stop_master: pci_clear_master(pdev); return rc; }
/* pmc control, write: * - "alloc <num descriptors> <descriptor_size>" to allocate PMC * - "free" to release memory allocated for PMC */ static ssize_t wil_write_pmccfg(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { struct wil6210_priv *wil = file->private_data; int rc; char *kbuf = kmalloc(len + 1, GFP_KERNEL); char cmd[9]; int num_descs, desc_size; if (!kbuf) return -ENOMEM; rc = simple_write_to_buffer(kbuf, len, ppos, buf, len); if (rc != len) { kfree(kbuf); return rc >= 0 ? -EIO : rc; } kbuf[len] = '\0'; rc = sscanf(kbuf, "%8s %d %d", cmd, &num_descs, &desc_size); kfree(kbuf); if (rc < 0) return rc; if (rc < 1) { wil_err(wil, "pmccfg: no params given\n"); return -EINVAL; } if (0 == strcmp(cmd, "alloc")) { if (rc != 3) { wil_err(wil, "pmccfg: alloc requires 2 params\n"); return -EINVAL; } wil_pmc_alloc(wil, num_descs, desc_size); } else if (0 == strcmp(cmd, "free")) { if (rc != 1) { wil_err(wil, "pmccfg: free does not have any params\n"); return -EINVAL; } wil_pmc_free(wil, true); } else { wil_err(wil, "pmccfg: Unrecognized command \"%s\"\n", cmd); return -EINVAL; } return len; }
int wil_resume(struct wil6210_priv *wil, bool is_runtime, bool keep_radio_on) { int rc = 0; wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system"); if (wil->platform_ops.resume) { rc = wil->platform_ops.resume(wil->platform_handle, keep_radio_on); if (rc) { wil_err(wil, "platform_ops.resume : %d\n", rc); goto out; } } if (keep_radio_on) rc = wil_resume_keep_radio_on(wil); else rc = wil_resume_radio_off(wil); out: wil_dbg_pm(wil, "resume: %s => %d\n", is_runtime ? "runtime" : "system", rc); return rc; }
static void wil_connect_worker(struct work_struct *work) { int rc; struct wil6210_priv *wil = container_of(work, struct wil6210_priv, connect_worker); int cid = wil->pending_connect_cid; int tid = wil->tid_to_use & 0xf; int ringid = wil_find_free_vring(wil); if (cid < 0) { wil_err(wil, "No connection pending\n"); return; } wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid); rc = wil_vring_init_tx(wil, ringid, WIL6210_TX_RING_SIZE, cid, tid); wil->pending_connect_cid = -1; if (rc == 0) { wil->sta[cid].status = wil_sta_connected; wil_link_on(wil); } else { wil->sta[cid].status = wil_sta_unused; } }
/** * wil6210_debugfs_init_offset - create set of debugfs files * @wil - driver's context, used for printing * @dbg - directory on the debugfs, where files will be created * @base - base address used in address calculation * @tbl - table with file descriptions. Should be terminated with empty element. * * Creates files accordingly to the @tbl. */ static void wil6210_debugfs_init_offset(struct wil6210_priv *wil, struct dentry *dbg, void *base, const struct dbg_off * const tbl) { int i; for (i = 0; tbl[i].name; i++) { struct dentry *f; switch (tbl[i].type) { case doff_u32: f = debugfs_create_u32(tbl[i].name, tbl[i].mode, dbg, base + tbl[i].off); break; case doff_x32: f = debugfs_create_x32(tbl[i].name, tbl[i].mode, dbg, base + tbl[i].off); break; case doff_ulong: f = wil_debugfs_create_ulong(tbl[i].name, tbl[i].mode, dbg, base + tbl[i].off); break; case doff_io32: f = wil_debugfs_create_iomem_x32(tbl[i].name, tbl[i].mode, dbg, base + tbl[i].off); break; default: f = ERR_PTR(-EINVAL); } if (IS_ERR_OR_NULL(f)) wil_err(wil, "Create file \"%s\": err %ld\n", tbl[i].name, PTR_ERR(f)); } }
/*---write channel 1..4 to rxon for it, 0 to rxoff---*/ static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { struct wil6210_priv *wil = file->private_data; int rc; long channel; bool on; char *kbuf = memdup_user_nul(buf, len); if (IS_ERR(kbuf)) return PTR_ERR(kbuf); rc = kstrtol(kbuf, 0, &channel); kfree(kbuf); if (rc) return rc; if ((channel < 0) || (channel > 4)) { wil_err(wil, "Invalid channel %ld\n", channel); return -EINVAL; } on = !!channel; if (on) { rc = wmi_set_channel(wil, (int)channel); if (rc) return rc; } rc = wmi_rxon(wil, on); if (rc) return rc; return len; }
int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie) { struct wil_p2p_info *p2p = &wil->p2p; u8 started; mutex_lock(&wil->mutex); if (cookie != p2p->cookie) { wil_info(wil, "Cookie mismatch: 0x%016llx vs. 0x%016llx\n", p2p->cookie, cookie); mutex_unlock(&wil->mutex); return -ENOENT; } started = wil_p2p_stop_discovery(wil); mutex_unlock(&wil->mutex); if (!started) { wil_err(wil, "listen not started\n"); return -ENOENT; } mutex_lock(&wil->p2p_wdev_mutex); cfg80211_remain_on_channel_expired(wil->radio_wdev, p2p->cookie, &p2p->listen_chan, GFP_KERNEL); wil->radio_wdev = wil->wdev; mutex_unlock(&wil->p2p_wdev_mutex); return 0; }
static void wil_scan_timer_fn(ulong x) { struct wil6210_priv *wil = (void *)x; clear_bit(wil_status_fwready, &wil->status); wil_err(wil, "Scan timeout detected, start fw error recovery\n"); schedule_work(&wil->fw_error_worker); }
static int wil_ioc_memio_dword(struct wil6210_priv *wil, void __user *data) { struct wil_memio io; void __iomem *a; bool need_copy = false; if (copy_from_user(&io, data, sizeof(io))) return -EFAULT; wil_dbg_ioctl(wil, "IO: addr = 0x%08x val = 0x%08x op = 0x%08x\n", io.addr, io.val, io.op); a = wil_ioc_addr(wil, io.addr, sizeof(u32), io.op); if (!a) { wil_err(wil, "invalid address 0x%08x, op = 0x%08x\n", io.addr, io.op); return -EINVAL; } /* operation */ switch (io.op & wil_mmio_op_mask) { case wil_mmio_read: io.val = ioread32(a); need_copy = true; break; case wil_mmio_write: iowrite32(io.val, a); wmb(); /* make sure write propagated to HW */ break; default: wil_err(wil, "Unsupported operation, op = 0x%08x\n", io.op); return -EINVAL; } if (need_copy) { wil_dbg_ioctl(wil, "IO done: addr = 0x%08x" " val = 0x%08x op = 0x%08x\n", io.addr, io.val, io.op); if (copy_to_user(data, &io, sizeof(io))) return -EFAULT; } return 0; }
static int wil_resume_keep_radio_on(struct wil6210_priv *wil) { int rc = 0; /* wil_status_resuming will be cleared when getting * WMI_TRAFFIC_RESUME_EVENTID */ set_bit(wil_status_resuming, wil->status); clear_bit(wil_status_suspended, wil->status); wil_c(wil, RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); wil_unmask_irq(wil); wil6210_bus_request(wil, wil->bus_request_kbps_pre_suspend); /* Send WMI resume request to the device */ rc = wmi_resume(wil); if (rc) { wil_err(wil, "device failed to resume (%d)\n", rc); if (no_fw_recovery) goto out; rc = wil_down(wil); if (rc) { wil_err(wil, "wil_down failed (%d)\n", rc); goto out; } rc = wil_up(wil); if (rc) { wil_err(wil, "wil_up failed (%d)\n", rc); goto out; } } /* Wake all queues */ if (test_bit(wil_status_fwconnected, wil->status)) wil_update_net_queues_bh(wil, NULL, false); out: if (rc) set_bit(wil_status_suspended, wil->status); return rc; }
/** * Read from required position up to the end of current descriptor, * depends on descriptor size configured during alloc request. */ ssize_t wil_pmc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct wil6210_priv *wil = filp->private_data; struct pmc_ctx *pmc = &wil->pmc; size_t retval = 0; unsigned long long idx; loff_t offset; size_t pmc_size = pmc->descriptor_size * pmc->num_descriptors; mutex_lock(&pmc->lock); if (!wil_is_pmc_allocated(pmc)) { wil_err(wil, "%s: error, pmc is not allocated!\n", __func__); pmc->last_cmd_status = -EPERM; mutex_unlock(&pmc->lock); return -EPERM; } wil_dbg_misc(wil, "%s: size %u, pos %lld\n", __func__, (unsigned)count, *f_pos); pmc->last_cmd_status = 0; idx = *f_pos; do_div(idx, pmc->descriptor_size); offset = *f_pos - (idx * pmc->descriptor_size); if (*f_pos >= pmc_size) { wil_dbg_misc(wil, "%s: reached end of pmc buf: %lld >= %u\n", __func__, *f_pos, (unsigned)pmc_size); pmc->last_cmd_status = -ERANGE; goto out; } wil_dbg_misc(wil, "%s: read from pos %lld (descriptor %llu, offset %llu) %zu bytes\n", __func__, *f_pos, idx, offset, count); /* if no errors, return the copied byte count */ retval = simple_read_from_buffer(buf, count, &offset, pmc->descriptors[idx].va, pmc->descriptor_size); *f_pos += retval; out: mutex_unlock(&pmc->lock); return retval; }
static int wil_open(struct net_device *ndev) { struct wil6210_priv *wil = ndev_to_wil(ndev); wil_dbg_misc(wil, "%s()\n", __func__); if (debug_fw) { wil_err(wil, "%s() while in debug_fw mode\n", __func__); return -EINVAL; } return wil_up(wil); }
static int wil_wait_for_fw_ready(struct wil6210_priv *wil) { ulong to = msecs_to_jiffies(1000); ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); if (0 == left) { wil_err(wil, "Firmware not ready\n"); return -ETIME; } else { wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n", jiffies_to_msecs(to-left), wil->hw_version); } return 0; }
static int wil_suspend_radio_off(struct wil6210_priv *wil) { int rc = 0; bool active_ifaces; wil_dbg_pm(wil, "suspend radio off\n"); set_bit(wil_status_suspending, wil->status); if (test_bit(wil_status_collecting_dumps, wil->status)) { /* Device collects crash dump, cancel the suspend */ wil_dbg_pm(wil, "reject suspend while collecting crash dump\n"); clear_bit(wil_status_suspending, wil->status); wil->suspend_stats.rejected_by_host++; return -EBUSY; } /* if netif up, hardware is alive, shut it down */ mutex_lock(&wil->vif_mutex); active_ifaces = wil_has_active_ifaces(wil, true, false); mutex_unlock(&wil->vif_mutex); if (active_ifaces) { rc = wil_down(wil); if (rc) { wil_err(wil, "wil_down : %d\n", rc); wil->suspend_stats.r_off.failed_suspends++; goto out; } } /* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */ wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n"); wil_disable_irq(wil); if (wil->platform_ops.suspend) { rc = wil->platform_ops.suspend(wil->platform_handle, false); if (rc) { wil_enable_irq(wil); wil->suspend_stats.r_off.failed_suspends++; goto out; } } set_bit(wil_status_suspended, wil->status); out: clear_bit(wil_status_suspending, wil->status); wil_dbg_pm(wil, "suspend radio off: %d\n", rc); return rc; }
static int wil_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_beacon_data *bcon) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); int rc; wil_dbg_misc(wil, "%s()\n", __func__); if (wil_fix_bcon(wil, bcon)) { wil_dbg_misc(wil, "Fixed bcon\n"); wil_print_bcon_data(bcon); } /* FW do not form regular beacon, so bcon IE's are not set * For the DMG bcon, when it will be supported, bcon IE's will * be reused; add something like: * wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len, * bcon->beacon_ies); */ rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len, bcon->proberesp_ies); if (rc) { wil_err(wil, "set_ie(PROBE_RESP) failed\n"); return rc; } rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len, bcon->assocresp_ies); if (rc) { wil_err(wil, "set_ie(ASSOC_RESP) failed\n"); return rc; } return 0; }
int wil_pm_runtime_get(struct wil6210_priv *wil) { int rc; struct device *dev = wil_to_dev(wil); rc = pm_runtime_get_sync(dev); if (rc < 0) { wil_err(wil, "pm_runtime_get_sync() failed, rc = %d\n", rc); pm_runtime_put_noidle(dev); return rc; } return 0; }
static int wil_change_mtu(struct net_device *ndev, int new_mtu) { struct wil6210_priv *wil = ndev_to_wil(ndev); if (new_mtu < 68 || new_mtu > mtu_max) { wil_err(wil, "invalid MTU %d\n", new_mtu); return -EINVAL; } wil_dbg_misc(wil, "change MTU %d -> %d\n", ndev->mtu, new_mtu); ndev->mtu = new_mtu; return 0; }
int wil_p2p_listen(struct wil6210_priv *wil, struct wireless_dev *wdev, unsigned int duration, struct ieee80211_channel *chan, u64 *cookie) { struct wil_p2p_info *p2p = &wil->p2p; int rc; if (!chan) return -EINVAL; wil_dbg_misc(wil, "p2p_listen: duration %d\n", duration); mutex_lock(&wil->mutex); if (p2p->discovery_started) { wil_err(wil, "discovery already ongoing\n"); rc = -EBUSY; goto out; } memcpy(&p2p->listen_chan, chan, sizeof(*chan)); *cookie = ++p2p->cookie; p2p->listen_duration = duration; mutex_lock(&wil->p2p_wdev_mutex); if (wil->scan_request) { wil_dbg_misc(wil, "Delaying p2p listen until scan done\n"); p2p->pending_listen_wdev = wdev; p2p->discovery_started = 1; rc = 0; mutex_unlock(&wil->p2p_wdev_mutex); goto out; } mutex_unlock(&wil->p2p_wdev_mutex); rc = wil_p2p_start_listen(wil); if (rc) goto out; p2p->discovery_started = 1; wil->radio_wdev = wdev; cfg80211_ready_on_channel(wdev, *cookie, chan, duration, GFP_KERNEL); out: mutex_unlock(&wil->mutex); return rc; }
static void wil_fw_error_worker(struct work_struct *work) { struct wil6210_priv *wil = container_of(work, struct wil6210_priv, fw_error_worker); struct wireless_dev *wdev = wil->wdev; wil_dbg_misc(wil, "fw error worker\n"); if (no_fw_recovery) return; /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO * passed since last recovery attempt */ if (time_is_after_jiffies(wil->last_fw_recovery + WIL6210_FW_RECOVERY_TO)) wil->recovery_count++; else wil->recovery_count = 1; /* fw was alive for a long time */ if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) { wil_err(wil, "too many recovery attempts (%d), giving up\n", wil->recovery_count); return; } wil->last_fw_recovery = jiffies; mutex_lock(&wil->mutex); switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_MONITOR: wil_info(wil, "fw error recovery started (try %d)...\n", wil->recovery_count); wil_reset(wil); /* need to re-allocate Rx ring after reset */ wil_rx_init(wil); break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: /* recovery in these modes is done by upper layers */ break; default: break; } mutex_unlock(&wil->mutex); }
static int wil_p2p_start_listen(struct wil6210_priv *wil) { struct wil_p2p_info *p2p = &wil->p2p; u8 channel = p2p->listen_chan.hw_value; int rc; lockdep_assert_held(&wil->mutex); rc = wmi_p2p_cfg(wil, channel, P2P_DEFAULT_BI); if (rc) { wil_err(wil, "wmi_p2p_cfg failed\n"); goto out; } rc = wmi_set_ssid(wil, strlen(P2P_WILDCARD_SSID), P2P_WILDCARD_SSID); if (rc) { wil_err(wil, "wmi_set_ssid failed\n"); goto out_stop; } rc = wmi_start_listen(wil); if (rc) { wil_err(wil, "wmi_start_listen failed\n"); goto out_stop; } INIT_WORK(&p2p->discovery_expired_work, wil_p2p_listen_expired); mod_timer(&p2p->discovery_timer, jiffies + msecs_to_jiffies(p2p->listen_duration)); out_stop: if (rc) wmi_stop_discovery(wil); out: return rc; }
static void wil_connect_worker(struct work_struct *work) { int rc; struct wil6210_priv *wil = container_of(work, struct wil6210_priv, connect_worker); int cid = wil->pending_connect_cid; if (cid < 0) { wil_err(wil, "No connection pending\n"); return; } wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid); rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, cid, 0); wil->pending_connect_cid = -1; if (rc == 0) wil_link_on(wil); }
/*---write channel 1..4 to rxon for it, 0 to rxoff---*/ static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { struct wil6210_priv *wil = file->private_data; int rc; long channel; bool on; char *kbuf = kmalloc(len + 1, GFP_KERNEL); if (!kbuf) return -ENOMEM; if (copy_from_user(kbuf, buf, len)) { kfree(kbuf); return -EIO; } kbuf[len] = '\0'; rc = kstrtol(kbuf, 0, &channel); kfree(kbuf); if (rc) return rc; if ((channel < 0) || (channel > 4)) { wil_err(wil, "Invalid channel %ld\n", channel); return -EINVAL; } on = !!channel; if (on) { rc = wmi_set_channel(wil, (int)channel); if (rc) return rc; } rc = wmi_rxon(wil, on); if (rc) return rc; return len; }