linux/drivers/net/wireless/ti/wlcore/sysfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * This file is part of wlcore
   4 *
   5 * Copyright (C) 2013 Texas Instruments Inc.
   6 */
   7
   8#include <linux/pm_runtime.h>
   9
  10#include "acx.h"
  11#include "wlcore.h"
  12#include "debug.h"
  13#include "sysfs.h"
  14
  15static ssize_t bt_coex_state_show(struct device *dev,
  16                                  struct device_attribute *attr,
  17                                  char *buf)
  18{
  19        struct wl1271 *wl = dev_get_drvdata(dev);
  20        ssize_t len;
  21
  22        len = PAGE_SIZE;
  23
  24        mutex_lock(&wl->mutex);
  25        len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
  26                       wl->sg_enabled);
  27        mutex_unlock(&wl->mutex);
  28
  29        return len;
  30
  31}
  32
  33static ssize_t bt_coex_state_store(struct device *dev,
  34                                   struct device_attribute *attr,
  35                                   const char *buf, size_t count)
  36{
  37        struct wl1271 *wl = dev_get_drvdata(dev);
  38        unsigned long res;
  39        int ret;
  40
  41        ret = kstrtoul(buf, 10, &res);
  42        if (ret < 0) {
  43                wl1271_warning("incorrect value written to bt_coex_mode");
  44                return count;
  45        }
  46
  47        mutex_lock(&wl->mutex);
  48
  49        res = !!res;
  50
  51        if (res == wl->sg_enabled)
  52                goto out;
  53
  54        wl->sg_enabled = res;
  55
  56        if (unlikely(wl->state != WLCORE_STATE_ON))
  57                goto out;
  58
  59        ret = pm_runtime_get_sync(wl->dev);
  60        if (ret < 0) {
  61                pm_runtime_put_noidle(wl->dev);
  62                goto out;
  63        }
  64
  65        wl1271_acx_sg_enable(wl, wl->sg_enabled);
  66        pm_runtime_mark_last_busy(wl->dev);
  67        pm_runtime_put_autosuspend(wl->dev);
  68
  69 out:
  70        mutex_unlock(&wl->mutex);
  71        return count;
  72}
  73
  74static DEVICE_ATTR_RW(bt_coex_state);
  75
  76static ssize_t hw_pg_ver_show(struct device *dev,
  77                              struct device_attribute *attr,
  78                              char *buf)
  79{
  80        struct wl1271 *wl = dev_get_drvdata(dev);
  81        ssize_t len;
  82
  83        len = PAGE_SIZE;
  84
  85        mutex_lock(&wl->mutex);
  86        if (wl->hw_pg_ver >= 0)
  87                len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
  88        else
  89                len = snprintf(buf, len, "n/a\n");
  90        mutex_unlock(&wl->mutex);
  91
  92        return len;
  93}
  94
  95static DEVICE_ATTR_RO(hw_pg_ver);
  96
  97static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
  98                                       struct bin_attribute *bin_attr,
  99                                       char *buffer, loff_t pos, size_t count)
 100{
 101        struct device *dev = kobj_to_dev(kobj);
 102        struct wl1271 *wl = dev_get_drvdata(dev);
 103        ssize_t len;
 104        int ret;
 105
 106        ret = mutex_lock_interruptible(&wl->mutex);
 107        if (ret < 0)
 108                return -ERESTARTSYS;
 109
 110        /* Check if the fwlog is still valid */
 111        if (wl->fwlog_size < 0) {
 112                mutex_unlock(&wl->mutex);
 113                return 0;
 114        }
 115
 116        /* Seeking is not supported - old logs are not kept. Disregard pos. */
 117        len = min_t(size_t, count, wl->fwlog_size);
 118        wl->fwlog_size -= len;
 119        memcpy(buffer, wl->fwlog, len);
 120
 121        /* Make room for new messages */
 122        memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
 123
 124        mutex_unlock(&wl->mutex);
 125
 126        return len;
 127}
 128
 129static const struct bin_attribute fwlog_attr = {
 130        .attr = { .name = "fwlog", .mode = 0400 },
 131        .read = wl1271_sysfs_read_fwlog,
 132};
 133
 134int wlcore_sysfs_init(struct wl1271 *wl)
 135{
 136        int ret;
 137
 138        /* Create sysfs file to control bt coex state */
 139        ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
 140        if (ret < 0) {
 141                wl1271_error("failed to create sysfs file bt_coex_state");
 142                goto out;
 143        }
 144
 145        /* Create sysfs file to get HW PG version */
 146        ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
 147        if (ret < 0) {
 148                wl1271_error("failed to create sysfs file hw_pg_ver");
 149                goto out_bt_coex_state;
 150        }
 151
 152        /* Create sysfs file for the FW log */
 153        ret = device_create_bin_file(wl->dev, &fwlog_attr);
 154        if (ret < 0) {
 155                wl1271_error("failed to create sysfs file fwlog");
 156                goto out_hw_pg_ver;
 157        }
 158
 159        goto out;
 160
 161out_hw_pg_ver:
 162        device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
 163
 164out_bt_coex_state:
 165        device_remove_file(wl->dev, &dev_attr_bt_coex_state);
 166
 167out:
 168        return ret;
 169}
 170
 171void wlcore_sysfs_free(struct wl1271 *wl)
 172{
 173        device_remove_bin_file(wl->dev, &fwlog_attr);
 174
 175        device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
 176
 177        device_remove_file(wl->dev, &dev_attr_bt_coex_state);
 178}
 179