Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / 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         /* Let only one thread read the log at a time, blocking others */
123         while (wl->fwlog_size == 0) {
124                 DEFINE_WAIT(wait);
125
126                 prepare_to_wait_exclusive(&wl->fwlog_waitq,
127                                           &wait,
128                                           TASK_INTERRUPTIBLE);
129
130                 if (wl->fwlog_size != 0) {
131                         finish_wait(&wl->fwlog_waitq, &wait);
132                         break;
133                 }
134
135                 mutex_unlock(&wl->mutex);
136
137                 schedule();
138                 finish_wait(&wl->fwlog_waitq, &wait);
139
140                 if (signal_pending(current))
141                         return -ERESTARTSYS;
142
143                 ret = mutex_lock_interruptible(&wl->mutex);
144                 if (ret < 0)
145                         return -ERESTARTSYS;
146         }
147
148         /* Check if the fwlog is still valid */
149         if (wl->fwlog_size < 0) {
150                 mutex_unlock(&wl->mutex);
151                 return 0;
152         }
153
154         /* Seeking is not supported - old logs are not kept. Disregard pos. */
155         len = min_t(size_t, count, wl->fwlog_size);
156         wl->fwlog_size -= len;
157         memcpy(buffer, wl->fwlog, len);
158
159         /* Make room for new messages */
160         memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
161
162         mutex_unlock(&wl->mutex);
163
164         return len;
165 }
166
167 static struct bin_attribute fwlog_attr = {
168         .attr = {.name = "fwlog", .mode = S_IRUSR},
169         .read = wl1271_sysfs_read_fwlog,
170 };
171
172 int wlcore_sysfs_init(struct wl1271 *wl)
173 {
174         int ret;
175
176         /* Create sysfs file to control bt coex state */
177         ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
178         if (ret < 0) {
179                 wl1271_error("failed to create sysfs file bt_coex_state");
180                 goto out;
181         }
182
183         /* Create sysfs file to get HW PG version */
184         ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
185         if (ret < 0) {
186                 wl1271_error("failed to create sysfs file hw_pg_ver");
187                 goto out_bt_coex_state;
188         }
189
190         /* Create sysfs file for the FW log */
191         ret = device_create_bin_file(wl->dev, &fwlog_attr);
192         if (ret < 0) {
193                 wl1271_error("failed to create sysfs file fwlog");
194                 goto out_hw_pg_ver;
195         }
196
197         goto out;
198
199 out_hw_pg_ver:
200         device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
201
202 out_bt_coex_state:
203         device_remove_file(wl->dev, &dev_attr_bt_coex_state);
204
205 out:
206         return ret;
207 }
208
209 void wlcore_sysfs_free(struct wl1271 *wl)
210 {
211         device_remove_bin_file(wl->dev, &fwlog_attr);
212
213         device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
214
215         device_remove_file(wl->dev, &dev_attr_bt_coex_state);
216 }