diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-12-01 07:37:02 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-12-22 13:31:16 -0500 |
commit | 0f78231bffb868a30e8533aace142213266bb811 (patch) | |
tree | 317f65dc6d89e9a89ad83f94fadd780dd1e0ca83 /net/mac80211/debugfs_netdev.c | |
parent | 18974b5b0b5e758d416c550553b143e5c8038281 (diff) |
mac80211: enable spatial multiplexing powersave
Enable spatial multiplexing in mac80211 by telling the
driver what to do and, where necessary, sending action
frames to the AP to update the requested SMPS mode.
Also includes a trivial implementation for hwsim that
just logs the requested mode.
For now, the userspace interface is in debugfs only,
and let you toggle the requested mode at any time.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/debugfs_netdev.c')
-rw-r--r-- | net/mac80211/debugfs_netdev.c | 111 |
1 files changed, 108 insertions, 3 deletions
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 5d9c797635a9..355983503885 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c | |||
@@ -41,6 +41,30 @@ static ssize_t ieee80211_if_read( | |||
41 | return ret; | 41 | return ret; |
42 | } | 42 | } |
43 | 43 | ||
44 | static ssize_t ieee80211_if_write( | ||
45 | struct ieee80211_sub_if_data *sdata, | ||
46 | const char __user *userbuf, | ||
47 | size_t count, loff_t *ppos, | ||
48 | ssize_t (*write)(struct ieee80211_sub_if_data *, const char *, int)) | ||
49 | { | ||
50 | u8 *buf; | ||
51 | ssize_t ret = -ENODEV; | ||
52 | |||
53 | buf = kzalloc(count, GFP_KERNEL); | ||
54 | if (!buf) | ||
55 | return -ENOMEM; | ||
56 | |||
57 | if (copy_from_user(buf, userbuf, count)) | ||
58 | return -EFAULT; | ||
59 | |||
60 | rtnl_lock(); | ||
61 | if (sdata->dev->reg_state == NETREG_REGISTERED) | ||
62 | ret = (*write)(sdata, buf, count); | ||
63 | rtnl_unlock(); | ||
64 | |||
65 | return ret; | ||
66 | } | ||
67 | |||
44 | #define IEEE80211_IF_FMT(name, field, format_string) \ | 68 | #define IEEE80211_IF_FMT(name, field, format_string) \ |
45 | static ssize_t ieee80211_if_fmt_##name( \ | 69 | static ssize_t ieee80211_if_fmt_##name( \ |
46 | const struct ieee80211_sub_if_data *sdata, char *buf, \ | 70 | const struct ieee80211_sub_if_data *sdata, char *buf, \ |
@@ -71,7 +95,7 @@ static ssize_t ieee80211_if_fmt_##name( \ | |||
71 | return scnprintf(buf, buflen, "%pM\n", sdata->field); \ | 95 | return scnprintf(buf, buflen, "%pM\n", sdata->field); \ |
72 | } | 96 | } |
73 | 97 | ||
74 | #define __IEEE80211_IF_FILE(name) \ | 98 | #define __IEEE80211_IF_FILE(name, _write) \ |
75 | static ssize_t ieee80211_if_read_##name(struct file *file, \ | 99 | static ssize_t ieee80211_if_read_##name(struct file *file, \ |
76 | char __user *userbuf, \ | 100 | char __user *userbuf, \ |
77 | size_t count, loff_t *ppos) \ | 101 | size_t count, loff_t *ppos) \ |
@@ -82,12 +106,24 @@ static ssize_t ieee80211_if_read_##name(struct file *file, \ | |||
82 | } \ | 106 | } \ |
83 | static const struct file_operations name##_ops = { \ | 107 | static const struct file_operations name##_ops = { \ |
84 | .read = ieee80211_if_read_##name, \ | 108 | .read = ieee80211_if_read_##name, \ |
109 | .write = (_write), \ | ||
85 | .open = mac80211_open_file_generic, \ | 110 | .open = mac80211_open_file_generic, \ |
86 | } | 111 | } |
87 | 112 | ||
113 | #define __IEEE80211_IF_FILE_W(name) \ | ||
114 | static ssize_t ieee80211_if_write_##name(struct file *file, \ | ||
115 | const char __user *userbuf, \ | ||
116 | size_t count, loff_t *ppos) \ | ||
117 | { \ | ||
118 | return ieee80211_if_write(file->private_data, userbuf, count, \ | ||
119 | ppos, ieee80211_if_parse_##name); \ | ||
120 | } \ | ||
121 | __IEEE80211_IF_FILE(name, ieee80211_if_write_##name) | ||
122 | |||
123 | |||
88 | #define IEEE80211_IF_FILE(name, field, format) \ | 124 | #define IEEE80211_IF_FILE(name, field, format) \ |
89 | IEEE80211_IF_FMT_##format(name, field) \ | 125 | IEEE80211_IF_FMT_##format(name, field) \ |
90 | __IEEE80211_IF_FILE(name) | 126 | __IEEE80211_IF_FILE(name, NULL) |
91 | 127 | ||
92 | /* common attributes */ | 128 | /* common attributes */ |
93 | IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); | 129 | IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); |
@@ -99,6 +135,70 @@ IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); | |||
99 | IEEE80211_IF_FILE(aid, u.mgd.aid, DEC); | 135 | IEEE80211_IF_FILE(aid, u.mgd.aid, DEC); |
100 | IEEE80211_IF_FILE(capab, u.mgd.capab, HEX); | 136 | IEEE80211_IF_FILE(capab, u.mgd.capab, HEX); |
101 | 137 | ||
138 | static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata, | ||
139 | enum ieee80211_smps_mode smps_mode) | ||
140 | { | ||
141 | struct ieee80211_local *local = sdata->local; | ||
142 | int err; | ||
143 | |||
144 | if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) && | ||
145 | smps_mode == IEEE80211_SMPS_STATIC) | ||
146 | return -EINVAL; | ||
147 | |||
148 | /* auto should be dynamic if in PS mode */ | ||
149 | if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) && | ||
150 | (smps_mode == IEEE80211_SMPS_DYNAMIC || | ||
151 | smps_mode == IEEE80211_SMPS_AUTOMATIC)) | ||
152 | return -EINVAL; | ||
153 | |||
154 | /* supported only on managed interfaces for now */ | ||
155 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
156 | return -EOPNOTSUPP; | ||
157 | |||
158 | mutex_lock(&local->iflist_mtx); | ||
159 | err = __ieee80211_request_smps(sdata, smps_mode); | ||
160 | mutex_unlock(&local->iflist_mtx); | ||
161 | |||
162 | return err; | ||
163 | } | ||
164 | |||
165 | static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = { | ||
166 | [IEEE80211_SMPS_AUTOMATIC] = "auto", | ||
167 | [IEEE80211_SMPS_OFF] = "off", | ||
168 | [IEEE80211_SMPS_STATIC] = "static", | ||
169 | [IEEE80211_SMPS_DYNAMIC] = "dynamic", | ||
170 | }; | ||
171 | |||
172 | static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata, | ||
173 | char *buf, int buflen) | ||
174 | { | ||
175 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
176 | return -EOPNOTSUPP; | ||
177 | |||
178 | return snprintf(buf, buflen, "request: %s\nused: %s\n", | ||
179 | smps_modes[sdata->u.mgd.req_smps], | ||
180 | smps_modes[sdata->u.mgd.ap_smps]); | ||
181 | } | ||
182 | |||
183 | static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, | ||
184 | const char *buf, int buflen) | ||
185 | { | ||
186 | enum ieee80211_smps_mode mode; | ||
187 | |||
188 | for (mode = 0; mode < IEEE80211_SMPS_NUM_MODES; mode++) { | ||
189 | if (strncmp(buf, smps_modes[mode], buflen) == 0) { | ||
190 | int err = ieee80211_set_smps(sdata, mode); | ||
191 | if (!err) | ||
192 | return buflen; | ||
193 | return err; | ||
194 | } | ||
195 | } | ||
196 | |||
197 | return -EINVAL; | ||
198 | } | ||
199 | |||
200 | __IEEE80211_IF_FILE_W(smps); | ||
201 | |||
102 | /* AP attributes */ | 202 | /* AP attributes */ |
103 | IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); | 203 | IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); |
104 | IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC); | 204 | IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC); |
@@ -109,7 +209,7 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast( | |||
109 | return scnprintf(buf, buflen, "%u\n", | 209 | return scnprintf(buf, buflen, "%u\n", |
110 | skb_queue_len(&sdata->u.ap.ps_bc_buf)); | 210 | skb_queue_len(&sdata->u.ap.ps_bc_buf)); |
111 | } | 211 | } |
112 | __IEEE80211_IF_FILE(num_buffered_multicast); | 212 | __IEEE80211_IF_FILE(num_buffered_multicast, NULL); |
113 | 213 | ||
114 | /* WDS attributes */ | 214 | /* WDS attributes */ |
115 | IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); | 215 | IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); |
@@ -158,6 +258,10 @@ IEEE80211_IF_FILE(dot11MeshHWMPRootMode, | |||
158 | debugfs_create_file(#name, 0400, sdata->debugfs.dir, \ | 258 | debugfs_create_file(#name, 0400, sdata->debugfs.dir, \ |
159 | sdata, &name##_ops); | 259 | sdata, &name##_ops); |
160 | 260 | ||
261 | #define DEBUGFS_ADD_MODE(name, mode) \ | ||
262 | debugfs_create_file(#name, mode, sdata->debugfs.dir, \ | ||
263 | sdata, &name##_ops); | ||
264 | |||
161 | static void add_sta_files(struct ieee80211_sub_if_data *sdata) | 265 | static void add_sta_files(struct ieee80211_sub_if_data *sdata) |
162 | { | 266 | { |
163 | DEBUGFS_ADD(drop_unencrypted, sta); | 267 | DEBUGFS_ADD(drop_unencrypted, sta); |
@@ -167,6 +271,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) | |||
167 | DEBUGFS_ADD(bssid, sta); | 271 | DEBUGFS_ADD(bssid, sta); |
168 | DEBUGFS_ADD(aid, sta); | 272 | DEBUGFS_ADD(aid, sta); |
169 | DEBUGFS_ADD(capab, sta); | 273 | DEBUGFS_ADD(capab, sta); |
274 | DEBUGFS_ADD_MODE(smps, 0600); | ||
170 | } | 275 | } |
171 | 276 | ||
172 | static void add_ap_files(struct ieee80211_sub_if_data *sdata) | 277 | static void add_ap_files(struct ieee80211_sub_if_data *sdata) |