1 /*
2 * power management
3 */
4
5 #include "u.h"
6 #include "../port/lib.h"
7 #include "mem.h"
8 #include "dat.h"
9 #include "fns.h"
10 #include "../port/error.h"
11
12 typedef struct Power Power;
13 typedef struct Puser Puser;
14
15 enum{
16 Qdir,
17 Qctl,
18 Qdata
19 };
20
21 static
22 Dirtab powertab[]={
23 ".", {Qdir, 0, QTDIR}, 0, 0500,
24 "powerctl", {Qctl, 0}, 0, 0600,
25 "powerdata", {Qdata, 0}, 0, 0666,
26 };
27
28 struct Puser {
29 Ref;
30 ulong alarm; /* real time clock alarm time, if non-zero */
31 QLock rl; /* mutual exclusion to protect r */
32 Rendez r; /* wait for event of interest */
33 int state; /* shutdown state of this process */
34 Puser* next;
35 };
36
37 enum{
38 Pwridle,
39 Pwroff,
40 Pwrack
41 };
42
43 static struct {
44 QLock;
45 Puser *list;
46 Lock l; /* protect shutdown, nwaiting */
47 int shutdown; /* non-zero if currently shutting down */
48 int nwaiting; /* waiting for this many processes */
49 Rendez ackr; /* wait here for all acks */
50 } pwrusers;
51
52
53 static Chan*
powerattach(char * spec)54 powerattach(char* spec)
55 {
56 return devattach(L'↓', spec);
57 }
58
59 static int
powerwalk(Chan * c,char * name)60 powerwalk(Chan* c, char* name)
61 {
62 return devwalk(c, name, powertab, nelem(powertab), devgen);
63 }
64
65 static void
powerstat(Chan * c,char * db)66 powerstat(Chan* c, char* db)
67 {
68 devstat(c, db, powertab, nelem(powertab), devgen);
69 }
70
71 static Chan*
poweropen(Chan * c,int omode)72 poweropen(Chan* c, int omode)
73 {
74 Puser *p;
75
76 if(c->qid.type & QTDIR)
77 return devopen(c, omode, powertab, nelem(powertab), devgen);
78 switch(c->qid.path){
79 case Qdata:
80 p = mallocz(sizeof(Puser), 1);
81 if(p == nil)
82 error(Enovmem);
83 p->state = Pwridle;
84 p->ref = 1;
85 if(waserror()){
86 free(p);
87 nexterror();
88 }
89 c = devopen(c, omode, powertab, nelem(powertab), devgen);
90 c->aux = p;
91 qlock(&pwrusers);
92 p->next = pwrusers.list;
93 pwrusers.list = p; /* note: must place on front of list for correct shutdown ordering */
94 qunlock(&pwrusers);
95 poperror();
96 break;
97 case Qctl:
98 c = devopen(c, omode, powertab, nelem(powertab), devgen);
99 break;
100 }
101 return c;
102 }
103
104 static Chan *
powerclone(Chan * c,Chan * nc)105 powerclone(Chan *c, Chan *nc)
106 {
107 Puser *p;
108
109 nc = devclone(c, nc);
110 if((p = nc->aux) != nil)
111 incref(p);
112 return nc;
113 }
114
115 static void
powerclose(Chan * c)116 powerclose(Chan* c)
117 {
118 Puser *p, **l;
119
120 if(c->qid.type & QTDIR || (c->flag & COPEN) == 0)
121 return;
122 p = c->aux;
123 if(p != nil && decref(p) == 0){
124 /* TO DO: cancel alarm */
125 qlock(&pwrusers);
126 for(l = &pwrusers.list; *l != nil; l = &(*l)->next)
127 if(*l == p){
128 *l = p->next;
129 break;
130 }
131 qunlock(&pwrusers);
132 free(p);
133 }
134 }
135
136 static int
isshutdown(void * a)137 isshutdown(void *a)
138 {
139 return ((Puser*)a)->state == Pwroff;
140 }
141
142 static long
powerread(Chan * c,void * a,long n,vlong offset)143 powerread(Chan* c, void* a, long n, vlong offset)
144 {
145 Puser *p;
146 char *msg;
147
148 switch(c->qid.path & ~CHDIR){
149 case Qdir:
150 return devdirread(c, a, n, powertab, nelem(powertab), devgen);
151 case Qdata:
152 p = c->aux;
153 for(;;){
154 if(!canqlock(&p->rl))
155 error(Einuse); /* only one reader at a time */
156 if(waserror()){
157 qunlock(&p->rl);
158 nexterror();
159 }
160 sleep(&p->r, isshutdown, p);
161 poperror();
162 qunlock(&p->rl);
163 msg = nil;
164 lock(p);
165 if(p->state == Pwroff){
166 msg = "power off";
167 p->state = Pwrack;
168 }
169 unlock(p);
170 if(msg != nil)
171 return readstr(offset, a, n, msg);
172 }
173 break;
174 case Qctl:
175 default:
176 n=0;
177 break;
178 }
179 return n;
180 }
181
182 static int
alldown(void *)183 alldown(void*)
184 {
185 return pwrusers.nwaiting == 0;
186 }
187
188 static long
powerwrite(Chan * c,void * a,long n,vlong)189 powerwrite(Chan* c, void *a, long n, vlong)
190 {
191 Cmdbuf *cmd;
192 Puser *p;
193
194 if(c->qid.type & QTDIR)
195 error(Ebadusefd);
196 cmd = parsecmd(a, n);
197 if(waserror()){
198 free(cmd);
199 nexterror();
200 }
201 switch(c->qid.path & ~CHDIR){
202 case Qdata:
203 p = c->aux;
204 if(cmd->nf < 2)
205 error(Ebadarg);
206 if(strcmp(cmd->f[0], "ack") == 0){
207 if(strcmp(cmd->f[1], "power") == 0){
208 lock(p);
209 if(p->state == Pwrack){
210 lock(&pwrusers.l);
211 if(pwrusers.shutdown && pwrusers.nwaiting > 0)
212 pwrusers.nwaiting--;
213 unlock(&pwrusers.l);
214 wakeup(&pwrusers.ackr);
215 p->state = Pwridle;
216 }
217 unlock(p);
218 }else
219 error(Ebadarg);
220 }else if(strcmp(cmd->f[0], "alarm") == 0){
221 /* set alarm */
222 }else
223 error(Ebadarg);
224 break;
225 case Qctl:
226 if(cmd->nf < 1)
227 error(Ebadarg);
228 if(strcmp(cmd->f[0], "suspend") == 0){
229 /* start the suspend action */
230 qlock(&pwrusers);
231 //powersuspend(0); /* calls poweringdown, then archsuspend() */
232 qunlock(&pwrusers);
233 }else if(strcmp(cmd->f[0], "shutdown") == 0){
234 /* go to it */
235 qlock(&pwrusers);
236 if(waserror()){
237 lock(&pwrusers.l);
238 pwrusers.shutdown = 0; /* hard luck for those already notified */
239 unlock(&pwrusers.l);
240 qunlock(&pwrusers);
241 nexterror();
242 }
243 lock(&pwrusers.l);
244 pwrusers.shutdown = 1;
245 pwrusers.nwaiting = 0;
246 unlock(&pwrusers.l);
247 for(p = pwrusers.list; p != nil; p = p->next){
248 lock(p);
249 if(p->state == Pwridle){
250 p->state = Pwroff;
251 lock(&pwrusers.l);
252 pwrusers.nwaiting++;
253 unlock(&pwrusers.l);
254 }
255 unlock(p);
256 wakeup(&p->r);
257 /* putting the tsleep here does each in turn; move out of loop to multicast */
258 tsleep(&pwrusers.ackr, alldown, nil, 1000);
259 }
260 poperror();
261 qunlock(&pwrusers);
262 //powersuspend(1);
263 }else
264 error(Ebadarg);
265 free(cmd);
266 break;
267 default:
268 error(Ebadusefd);
269 }
270 poperror();
271 return n;
272 }
273
274 /*
275 * device-level power management: suspend/resume/shutdown
276 */
277
278 struct Power {
279 void (*f)(int);
280 Power* prev;
281 Power* next;
282 };
283
284 static struct {
285 Lock;
286 Power list;
287 } power;
288
289 void
powerenablereset(void)290 powerenablereset(void)
291 {
292 power.list.next = power.list.prev = &power.list;
293 power.list.f = (void*)-1; /* something not nil */
294 }
295
296 void
powerenable(void (* f)(int))297 powerenable(void (*f)(int))
298 {
299 Power *p, *l;
300
301 p = malloc(sizeof(*p));
302 p->f = f;
303 p->prev = nil;
304 p->next = nil;
305 ilock(&power);
306 for(l = power.list.next; l != &power.list; l = l->next)
307 if(l->f == f){
308 iunlock(&power);
309 free(p);
310 return;
311 }
312 l = &power.list;
313 p->prev = l->prev;
314 l->prev = p;
315 p->next = l;
316 p->prev->next = p;
317 iunlock(&power);
318 }
319
320 void
powerdisable(void (* f)(int))321 powerdisable(void (*f)(int))
322 {
323 Power *l;
324
325 ilock(&power);
326 for(l = power.list.next; l != &power.list; l = l->next)
327 if(l->f == f){
328 l->prev->next = l->next;
329 l->next->prev = l->prev;
330 free(l);
331 break;
332 }
333 iunlock(&power);
334 }
335
336 /*
337 * interrupts are assumed off so there's no need to lock
338 */
339 void
poweringup(void)340 poweringup(void)
341 {
342 Power *l;
343
344 for(l = power.list.next; l != &power.list; l = l->next)
345 (*l->f)(1);
346 }
347
348 void
poweringdown(void)349 poweringdown(void)
350 {
351 Power *l;
352
353 for(l = power.list.prev; l != &power.list; l = l->prev)
354 (*l->f)(0);
355 }
356
357 Dev powerdevtab = {
358 L'↓',
359 "power",
360
361 devreset,
362 devinit,
363 powerattach,
364 devdetach,
365 powerclone,
366 powerwalk,
367 powerstat,
368 poweropen,
369 devcreate,
370 powerclose,
371 powerread,
372 devbread,
373 powerwrite,
374 devbwrite,
375 devremove,
376 devwstat,
377 };
378