/* * This file is part of wlcore * * Copyright (C) 2013 Texas Instruments Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "wlcore.h" #include "debug.h" #include "ps.h" #include "sysfs.h" static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev, struct device_attribute *attr, char *buf) { struct wl1271 *wl = dev_get_drvdata(dev); ssize_t len; len = PAGE_SIZE; mutex_lock(&wl->mutex); len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n", wl->sg_enabled); mutex_unlock(&wl->mutex); return len; } static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct wl1271 *wl = dev_get_drvdata(dev); unsigned long res; int ret; ret = kstrtoul(buf, 10, &res); if (ret < 0) { wl1271_warning("incorrect value written to bt_coex_mode"); return count; } mutex_lock(&wl->mutex); res = !!res; if (res == wl->sg_enabled) goto out; wl->sg_enabled = res; if (unlikely(wl->state != WLCORE_STATE_ON)) goto out; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; wl1271_acx_sg_enable(wl, wl->sg_enabled); wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return count; } static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR, wl1271_sysfs_show_bt_coex_state, wl1271_sysfs_store_bt_coex_state); static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev, struct device_attribute *attr, char *buf) { struct wl1271 *wl = dev_get_drvdata(dev); ssize_t len; len = PAGE_SIZE; mutex_lock(&wl->mutex); if (wl->hw_pg_ver >= 0) len = snprintf(buf, len, "%d\n", wl->hw_pg_ver); else len = snprintf(buf, len, "n/a\n"); mutex_unlock(&wl->mutex); return len; } static DEVICE_ATTR(hw_pg_ver, S_IRUGO, wl1271_sysfs_show_hw_pg_ver, NULL); static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); struct wl1271 *wl = dev_get_drvdata(dev); ssize_t len; int ret; ret = mutex_lock_interruptible(&wl->mutex); if (ret < 0) return -ERESTARTSYS; /* Check if the fwlog is still valid */ if (wl->fwlog_size < 0) { mutex_unlock(&wl->mutex); return 0; } /* Seeking is not supported - old logs are not kept. Disregard pos. */ len = min_t(size_t, count, wl->fwlog_size); wl->fwlog_size -= len; memcpy(buffer, wl->fwlog, len); /* Make room for new messages */ memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size); mutex_unlock(&wl->mutex); return len; } static struct bin_attribute fwlog_attr = { .attr = {.name = "fwlog", .mode = S_IRUSR}, .read = wl1271_sysfs_read_fwlog, }; int wlcore_sysfs_init(struct wl1271 *wl) { int ret; /* Create sysfs file to control bt coex state */ ret = device_create_file(wl->dev, &dev_attr_bt_coex_state); if (ret < 0) { wl1271_error("failed to create sysfs file bt_coex_state"); goto out; } /* Create sysfs file to get HW PG version */ ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver); if (ret < 0) { wl1271_error("failed to create sysfs file hw_pg_ver"); goto out_bt_coex_state; } /* Create sysfs file for the FW log */ ret = device_create_bin_file(wl->dev, &fwlog_attr); if (ret < 0) { wl1271_error("failed to create sysfs file fwlog"); goto out_hw_pg_ver; } goto out; out_hw_pg_ver: device_remove_file(wl->dev, &dev_attr_hw_pg_ver); out_bt_coex_state: device_remove_file(wl->dev, &dev_attr_bt_coex_state); out: return ret; } void wlcore_sysfs_free(struct wl1271 *wl) { device_remove_bin_file(wl->dev, &fwlog_attr); device_remove_file(wl->dev, &dev_attr_hw_pg_ver); device_remove_file(wl->dev, &dev_attr_bt_coex_state); }