iwlwifi: mvm: add bcast_filtering debugfs entries
[pandora-kernel.git] / drivers / net / wireless / iwlwifi / mvm / debugfs.c
index 8d3bf25..6ddc188 100644 (file)
@@ -599,6 +599,187 @@ iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf,
        return count;
 }
 
+#define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__)
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file,
+                                           char __user *user_buf,
+                                           size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       struct iwl_bcast_filter_cmd cmd;
+       const struct iwl_fw_bcast_filter *filter;
+       char *buf;
+       int bufsz = 1024;
+       int i, j, pos = 0;
+       ssize_t ret;
+
+       buf = kzalloc(bufsz, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       mutex_lock(&mvm->mutex);
+       if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) {
+               ADD_TEXT("None\n");
+               mutex_unlock(&mvm->mutex);
+               goto out;
+       }
+       mutex_unlock(&mvm->mutex);
+
+       for (i = 0; cmd.filters[i].attrs[0].mask; i++) {
+               filter = &cmd.filters[i];
+
+               ADD_TEXT("Filter [%d]:\n", i);
+               ADD_TEXT("\tDiscard=%d\n", filter->discard);
+               ADD_TEXT("\tFrame Type: %s\n",
+                        filter->frame_type ? "IPv4" : "Generic");
+
+               for (j = 0; j < ARRAY_SIZE(filter->attrs); j++) {
+                       const struct iwl_fw_bcast_filter_attr *attr;
+
+                       attr = &filter->attrs[j];
+                       if (!attr->mask)
+                               break;
+
+                       ADD_TEXT("\tAttr [%d]: offset=%d (from %s), mask=0x%x, value=0x%x reserved=0x%x\n",
+                                j, attr->offset,
+                                attr->offset_type ? "IP End" :
+                                                    "Payload Start",
+                                be32_to_cpu(attr->mask),
+                                be32_to_cpu(attr->val),
+                                le16_to_cpu(attr->reserved1));
+               }
+       }
+out:
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+       kfree(buf);
+       return ret;
+}
+
+static ssize_t iwl_dbgfs_bcast_filters_write(struct iwl_mvm *mvm, char *buf,
+                                            size_t count, loff_t *ppos)
+{
+       int pos, next_pos;
+       struct iwl_fw_bcast_filter filter = {};
+       struct iwl_bcast_filter_cmd cmd;
+       u32 filter_id, attr_id, mask, value;
+       int err = 0;
+
+       if (sscanf(buf, "%d %hhi %hhi %n", &filter_id, &filter.discard,
+                  &filter.frame_type, &pos) != 3)
+               return -EINVAL;
+
+       if (filter_id >= ARRAY_SIZE(mvm->dbgfs_bcast_filtering.cmd.filters) ||
+           filter.frame_type > BCAST_FILTER_FRAME_TYPE_IPV4)
+               return -EINVAL;
+
+       for (attr_id = 0; attr_id < ARRAY_SIZE(filter.attrs);
+            attr_id++) {
+               struct iwl_fw_bcast_filter_attr *attr =
+                               &filter.attrs[attr_id];
+
+               if (pos >= count)
+                       break;
+
+               if (sscanf(&buf[pos], "%hhi %hhi %i %i %n",
+                          &attr->offset, &attr->offset_type,
+                          &mask, &value, &next_pos) != 4)
+                       return -EINVAL;
+
+               attr->mask = cpu_to_be32(mask);
+               attr->val = cpu_to_be32(value);
+               if (mask)
+                       filter.num_attrs++;
+
+               pos += next_pos;
+       }
+
+       mutex_lock(&mvm->mutex);
+       memcpy(&mvm->dbgfs_bcast_filtering.cmd.filters[filter_id],
+              &filter, sizeof(filter));
+
+       /* send updated bcast filtering configuration */
+       if (mvm->dbgfs_bcast_filtering.override &&
+           iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
+               err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
+                                          sizeof(cmd), &cmd);
+       mutex_unlock(&mvm->mutex);
+
+       return err ?: count;
+}
+
+static ssize_t iwl_dbgfs_bcast_filters_macs_read(struct file *file,
+                                                char __user *user_buf,
+                                                size_t count, loff_t *ppos)
+{
+       struct iwl_mvm *mvm = file->private_data;
+       struct iwl_bcast_filter_cmd cmd;
+       char *buf;
+       int bufsz = 1024;
+       int i, pos = 0;
+       ssize_t ret;
+
+       buf = kzalloc(bufsz, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       mutex_lock(&mvm->mutex);
+       if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) {
+               ADD_TEXT("None\n");
+               mutex_unlock(&mvm->mutex);
+               goto out;
+       }
+       mutex_unlock(&mvm->mutex);
+
+       for (i = 0; i < ARRAY_SIZE(cmd.macs); i++) {
+               const struct iwl_fw_bcast_mac *mac = &cmd.macs[i];
+
+               ADD_TEXT("Mac [%d]: discard=%d attached_filters=0x%x\n",
+                        i, mac->default_discard, mac->attached_filters);
+       }
+out:
+       ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+       kfree(buf);
+       return ret;
+}
+
+static ssize_t iwl_dbgfs_bcast_filters_macs_write(struct iwl_mvm *mvm,
+                                                 char *buf, size_t count,
+                                                 loff_t *ppos)
+{
+       struct iwl_bcast_filter_cmd cmd;
+       struct iwl_fw_bcast_mac mac = {};
+       u32 mac_id, attached_filters;
+       int err = 0;
+
+       if (!mvm->bcast_filters)
+               return -ENOENT;
+
+       if (sscanf(buf, "%d %hhi %i", &mac_id, &mac.default_discard,
+                  &attached_filters) != 3)
+               return -EINVAL;
+
+       if (mac_id >= ARRAY_SIZE(cmd.macs) ||
+           mac.default_discard > 1 ||
+           attached_filters >= BIT(ARRAY_SIZE(cmd.filters)))
+               return -EINVAL;
+
+       mac.attached_filters = cpu_to_le16(attached_filters);
+
+       mutex_lock(&mvm->mutex);
+       memcpy(&mvm->dbgfs_bcast_filtering.cmd.macs[mac_id],
+              &mac, sizeof(mac));
+
+       /* send updated bcast filtering configuration */
+       if (mvm->dbgfs_bcast_filtering.override &&
+           iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
+               err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, CMD_SYNC,
+                                          sizeof(cmd), &cmd);
+       mutex_unlock(&mvm->mutex);
+
+       return err ?: count;
+}
+#endif
+
 #ifdef CONFIG_PM_SLEEP
 static ssize_t iwl_dbgfs_d3_sram_write(struct iwl_mvm *mvm, char *buf,
                                       size_t count, loff_t *ppos)
@@ -661,11 +842,13 @@ static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf,
        _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)
 #define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
        _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm)
-#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) do {                  \
-               if (!debugfs_create_file(#name, mode, parent, mvm,      \
+#define MVM_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do {     \
+               if (!debugfs_create_file(alias, mode, parent, mvm,      \
                                         &iwl_dbgfs_##name##_ops))      \
                        goto err;                                       \
        } while (0)
+#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) \
+       MVM_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode)
 
 /* Device wide debugfs entries */
 MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16);
@@ -680,12 +863,18 @@ MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10);
 MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10);
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
 
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters_macs, 256);
+#endif
+
 #ifdef CONFIG_PM_SLEEP
 MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8);
 #endif
 
 int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
 {
+       struct dentry *bcast_dir __maybe_unused;
        char buf[100];
 
        mvm->debugfs_dir = dbgfs_dir;
@@ -704,6 +893,26 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
        MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir,
                             S_IWUSR | S_IRUSR);
+
+#ifdef CONFIG_IWLWIFI_BCAST_FILTERING
+       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) {
+               bcast_dir = debugfs_create_dir("bcast_filtering",
+                                              mvm->debugfs_dir);
+               if (!bcast_dir)
+                       goto err;
+
+               if (!debugfs_create_bool("override", S_IRUSR | S_IWUSR,
+                               bcast_dir,
+                               &mvm->dbgfs_bcast_filtering.override))
+                       goto err;
+
+               MVM_DEBUGFS_ADD_FILE_ALIAS("filters", bcast_filters,
+                                          bcast_dir, S_IWUSR | S_IRUSR);
+               MVM_DEBUGFS_ADD_FILE_ALIAS("macs", bcast_filters_macs,
+                                          bcast_dir, S_IWUSR | S_IRUSR);
+       }
+#endif
+
 #ifdef CONFIG_PM_SLEEP
        MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
        MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR);