xref: /inferno-os/os/sa1110/devpower.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
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