GNU Linux-libre 4.9.309-gnu1
[releases.git] / drivers / net / wireless / ti / wlcore / sysfs.c
1 /*
2  * This file is part of wlcore
3  *
4  * Copyright (C) 2013 Texas Instruments Inc.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * version 2 as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18  * 02110-1301 USA
19  *
20  */
21
22 #include "wlcore.h"
23 #include "debug.h"
24 #include "ps.h"
25 #include "sysfs.h"
26
27 static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
28                                                struct device_attribute *attr,
29                                                char *buf)
30 {
31         struct wl1271 *wl = dev_get_drvdata(dev);
32         ssize_t len;
33
34         len = PAGE_SIZE;
35
36         mutex_lock(&wl->mutex);
37         len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
38                        wl->sg_enabled);
39         mutex_unlock(&wl->mutex);
40
41         return len;
42
43 }
44
45 static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
46                                                 struct device_attribute *attr,
47                                                 const char *buf, size_t count)
48 {
49         struct wl1271 *wl = dev_get_drvdata(dev);
50         unsigned long res;
51         int ret;
52
53         ret = kstrtoul(buf, 10, &res);
54         if (ret < 0) {
55                 wl1271_warning("incorrect value written to bt_coex_mode");
56                 return count;
57         }
58
59         mutex_lock(&wl->mutex);
60
61         res = !!res;
62
63         if (res == wl->sg_enabled)
64                 goto out;
65
66         wl->sg_enabled = res;
67
68         if (unlikely(wl->state != WLCORE_STATE_ON))
69                 goto out;
70
71         ret = wl1271_ps_elp_wakeup(wl);
72         if (ret < 0)
73                 goto out;
74
75         wl1271_acx_sg_enable(wl, wl->sg_enabled);
76         wl1271_ps_elp_sleep(wl);
77
78  out:
79         mutex_unlock(&wl->mutex);
80         return count;
81 }
82
83 static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
84                    wl1271_sysfs_show_bt_coex_state,
85                    wl1271_sysfs_store_bt_coex_state);
86
87 static ssize_t wl1271_sysfs_show_hw_pg_ver(struct device *dev,
88                                            struct device_attribute *attr,
89                                            char *buf)
90 {
91         struct wl1271 *wl = dev_get_drvdata(dev);
92         ssize_t len;
93
94         len = PAGE_SIZE;
95
96         mutex_lock(&wl->mutex);
97         if (wl->hw_pg_ver >= 0)
98                 len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
99         else
100                 len = snprintf(buf, len, "n/a\n");
101         mutex_unlock(&wl->mutex);
102
103         return len;
104 }
105
106 static DEVICE_ATTR(hw_pg_ver, S_IRUGO,
107                    wl1271_sysfs_show_hw_pg_ver, NULL);
108
109 static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
110                                        struct bin_attribute *bin_attr,
111                                        char *buffer, loff_t pos, size_t count)
112 {
113         struct device *dev = container_of(kobj, struct device, kobj);
114         struct wl1271 *wl = dev_get_drvdata(dev);
115         ssize_t len;
116         int ret;
117
118         ret = mutex_lock_interruptible(&wl->mutex);
119         if (ret < 0)
120                 return -ERESTARTSYS;
121
122         /* Check if the fwlog is still valid */
123         if (wl->fwlog_size < 0) {
124                 mutex_unlock(&wl->mutex);
125                 return 0;
126         }
127
128         /* Seeking is not supported - old logs are not kept. Disregard pos. */
129         len = min_t(size_t, count, wl->fwlog_size);
130         wl->fwlog_size -= len;
131         memcpy(buffer, wl->fwlog, len);
132
133         /* Make room for new messages */
134         memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
135
136         mutex_unlock(&wl->mutex);
137
138         return len;
139 }
140
141 static struct bin_attribute fwlog_attr = {
142         .attr = {.name = "fwlog", .mode = S_IRUSR},
143         .read = wl1271_sysfs_read_fwlog,
144 };
145
146 int wlcore_sysfs_init(struct wl1271 *wl)
147 {
148         int ret;
149
150         /* Create sysfs file to control bt coex state */
151         ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
152         if (ret < 0) {
153                 wl1271_error("failed to create sysfs file bt_coex_state");
154                 goto out;
155         }
156
157         /* Create sysfs file to get HW PG version */
158         ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
159         if (ret < 0) {
160                 wl1271_error("failed to create sysfs file hw_pg_ver");
161                 goto out_bt_coex_state;
162         }
163
164         /* Create sysfs file for the FW log */
165         ret = device_create_bin_file(wl->dev, &fwlog_attr);
166         if (ret < 0) {
167                 wl1271_error("failed to create sysfs file fwlog");
168                 goto out_hw_pg_ver;
169         }
170
171         goto out;
172
173 out_hw_pg_ver:
174         device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
175
176 out_bt_coex_state:
177         device_remove_file(wl->dev, &dev_attr_bt_coex_state);
178
179 out:
180         return ret;
181 }
182
183 void wlcore_sysfs_free(struct wl1271 *wl)
184 {
185         device_remove_bin_file(wl->dev, &fwlog_attr);
186
187         device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
188
189         device_remove_file(wl->dev, &dev_attr_bt_coex_state);
190 }