-
Notifications
You must be signed in to change notification settings - Fork 39
/
sample_pm.c
234 lines (214 loc) · 5.55 KB
/
sample_pm.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/* Note : this particular snipset of code is available under
* the LGPL, MPL or BSD license (at your choice).
* Jean II
*/
/* --------------------------- INCLUDE --------------------------- */
/* Backward compatibility for Wireless Extension 9 */
#ifndef IW_POWER_MODIFIER
#define IW_POWER_MODIFIER 0x000F /* Modify a parameter */
#define IW_POWER_MIN 0x0001 /* Value is a minimum */
#define IW_POWER_MAX 0x0002 /* Value is a maximum */
#define IW_POWER_RELATIVE 0x0004 /* Value is not in seconds/ms/us */
#endif IW_POWER_MODIFIER
struct net_local {
int pm_on; // Power Management enabled
int pm_multi; // Receive multicasts
int pm_period; // Power Management period
int pm_period_auto; // Power Management auto mode
int pm_max_period; // Power Management max period
int pm_min_period; // Power Management min period
int pm_timeout; // Power Management timeout
};
/* --------------------------- HANDLERS --------------------------- */
static int ioctl_set_power(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *prq,
char *extra)
{
/* Disable it ? */
if(prq->disabled)
{
local->pm_on = 0;
}
else
{
/* Check mode */
switch(prq->flags & IW_POWER_MODE)
{
case IW_POWER_UNICAST_R:
local->pm_multi = 0;
local->need_commit = 1;
break;
case IW_POWER_ALL_R:
local->pm_multi = 1;
local->need_commit = 1;
break;
case IW_POWER_ON: /* None = ok */
break;
default: /* Invalid */
return(-EINVAL);
}
/* Set period */
if(prq->flags & IW_POWER_PERIOD)
{
int period = prq->value;
#if WIRELESS_EXT < 21
period /= 1000000;
#endif
/* Hum: check if within bounds... */
/* Activate PM */
local->pm_on = 1;
local->need_commit = 1;
/* Check min value */
if(prq->flags & IW_POWER_MIN)
{
local->pm_min_period = period;
local->pm_period_auto = 1;
}
else
/* Check max value */
if(prq->flags & IW_POWER_MAX)
{
local->pm_max_period = period;
local->pm_period_auto = 1;
}
else
{
/* Fixed value */
local->pm_period = period;
local->pm_period_auto = 0;
}
}
/* Set timeout */
if(prq->flags & IW_POWER_TIMEOUT)
{
/* Activate PM */
local->pm_on = 1;
local->need_commit = 1;
/* Fixed value in ms */
local->pm_timeout = prq->value/1000;
}
}
return(0);
}
static int ioctl_get_power(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *prq,
char *extra)
{
prq->disabled = !local->pm_on;
/* By default, display the period */
if(!(prq->flags & IW_POWER_TIMEOUT))
{
int inc_flags = prq->flags;
prq->flags = IW_POWER_PERIOD | IW_POWER_RELATIVE;
/* Check if auto */
if(local->pm_period_auto)
{
/* By default, the min */
if(!(inc_flags & IW_POWER_MAX))
{
prq->value = local->pm_min_period;
#if WIRELESS_EXT < 21
prq->value *= 1000000;
#endif
prq->flags |= IW_POWER_MIN;
}
else
{
prq->value = local->pm_max_period;
#if WIRELESS_EXT < 21
prq->value *= 1000000;
#endif
prq->flags |= IW_POWER_MAX;
}
}
else
{
/* Fixed value. Check the flags */
if(inc_flags & (IW_POWER_MIN | IW_POWER_MAX))
return(-EINVAL);
else
{
prq->value = local->pm_period;
#if WIRELESS_EXT < 21
prq->value *= 1000000;
#endif
}
}
}
else
{
/* Deal with the timeout - always fixed */
prq->flags = IW_POWER_TIMEOUT;
prq->value = local->pm_timeout * 1000;
}
if(local->pm_multi)
prq->flags |= IW_POWER_ALL_R;
else
prq->flags |= IW_POWER_UNICAST_R;
return(0);
}
static int ioctl_get_range(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *rrq,
char *extra)
{
struct iw_range *range = (struct iw_range *) extra;
rrq->length = sizeof(struct iw_range);
memset(range, 0, sizeof(struct iw_range));
#if WIRELESS_EXT > 10
/* Version we are compiled with */
range->we_version_compiled = WIRELESS_EXT;
/* Minimum version we recommend */
range->we_version_source = 8;
#endif /* WIRELESS_EXT > 10 */
#if WIRELESS_EXT > 9
#if WIRELESS_EXT < 21
range.min_pmp = 1000000; /* 1 units */
range.max_pmp = 12000000; /* 12 units */
#else
range.min_pmp = 1; /* 1 units */
range.max_pmp = 12; /* 12 units */
#endif
range.min_pmt = 1000; /* 1 ms */
range.max_pmt = 1000000; /* 1 s */
range.pmp_flags = IW_POWER_PERIOD | IW_POWER_RELATIVE |
IW_POWER_MIN | IW_POWER_MAX;
range.pmt_flags = IW_POWER_TIMEOUT;
range.pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_UNICAST_R;
#endif /* WIRELESS_EXT > 9 */
return(0);
}
/* --------------------------- BINDING --------------------------- */
#if WIRELESS_EXT > 12
/* Use the new driver API, save overhead */
static const iw_handler handler_table[] =
{
...
(iw_handler) ioctl_set_power, /* SIOCSIWPOWER */
(iw_handler) ioctl_get_power, /* SIOCGIWPOWER */
};
#else /* WIRELESS_EXT < 12 */
/* Use old API in the ioctl handler */
static int
do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct iwreq *wrq = (struct iwreq *) ifr;
int err = 0;
switch (cmd)
{
#if WIRELESS_EXT > 8
/* Set the desired Power Management mode */
case SIOCSIWPOWER:
err = ioctl_set_power(dev, NULL, &(wrq->u.power), NULL);
break;
/* Get the power management settings */
case SIOCGIWPOWER:
err = ioctl_get_power(dev, NULL, &(wrq->u.power), NULL);
break;
#endif /* WIRELESS_EXT > 8 */
}
return(err);
}
#endif /* WIRELESS_EXT < 12 */