1 #include <u.h> 2 #include <libc.h> 3 #include </386/include/ureg.h> 4 typedef struct Ureg Ureg; 5 #include <auth.h> 6 #include <fcall.h> 7 #include <thread.h> 8 #include <9p.h> 9 10 enum { 11 /* power mgmt event codes */ 12 NotifyStandbyRequest = 0x0001, 13 NotifySuspendRequest = 0x0002, 14 NotifyNormalResume = 0x0003, 15 NotifyCriticalResume = 0x0004, 16 NotifyBatteryLow = 0x0005, 17 NotifyPowerStatusChange = 0x0006, 18 NotifyUpdateTime = 0x0007, 19 NotifyCriticalSuspend = 0x0008, 20 NotifyUserStandbyRequest = 0x0009, 21 NotifyUserSuspendRequest = 0x000A, 22 NotifyStandbyResume = 0x000B, 23 NotifyCapabilitiesChange = 0x000C, 24 25 /* power device ids: add device number or All */ 26 DevBios = 0x0000, 27 DevAll = 0x0001, 28 DevDisplay = 0x0100, 29 DevStorage = 0x0200, 30 DevLpt = 0x0300, 31 DevEia = 0x0400, 32 DevNetwork = 0x0500, 33 DevPCMCIA = 0x0600, 34 DevBattery = 0x8000, 35 All = 0x00FF, 36 DevMask = 0xFF00, 37 38 /* power states */ 39 PowerEnabled = 0x0000, 40 PowerStandby = 0x0001, 41 PowerSuspend = 0x0002, 42 PowerOff = 0x0003, 43 44 /* apm commands */ 45 CmdInstallationCheck = 0x5300, 46 CmdRealModeConnect = 0x5301, 47 CmdProtMode16Connect = 0x5302, 48 CmdProtMode32Connect = 0x5303, 49 CmdDisconnect = 0x5304, 50 CmdCpuIdle = 0x5305, 51 CmdCpuBusy = 0x5306, 52 CmdSetPowerState = 0x5307, 53 CmdSetPowerMgmt = 0x5308, 54 DisablePowerMgmt = 0x0000, /* CX */ 55 EnablePowerMgmt = 0x0001, 56 CmdRestoreDefaults = 0x5309, 57 CmdGetPowerStatus = 0x530A, 58 CmdGetPMEvent = 0x530B, 59 CmdGetPowerState = 0x530C, 60 CmdGetPowerMgmt = 0x530D, 61 CmdDriverVersion = 0x530E, 62 63 /* like CmdDisconnect but doesn't lose the interface */ 64 CmdGagePowerMgmt = 0x530F, 65 DisengagePowerMgmt = 0x0000, /* CX */ 66 EngagePowerManagemenet = 0x0001, 67 68 CmdGetCapabilities = 0x5310, 69 CapStandby = 0x0001, 70 CapSuspend = 0x0002, 71 CapTimerResumeStandby = 0x0004, 72 CapTimerResumeSuspend = 0x0008, 73 CapRingResumeStandby = 0x0010, 74 CapRingResumeSuspend = 0x0020, 75 CapPcmciaResumeStandby = 0x0040, 76 CapPcmciaResumeSuspend = 0x0080, 77 CapSlowCpu = 0x0100, 78 CmdResumeTimer = 0x5311, 79 DisableResumeTimer = 0x00, /* CL */ 80 GetResumeTimer = 0x01, 81 SetResumeTimer = 0x02, 82 CmdResumeOnRing = 0x5312, 83 DisableResumeOnRing = 0x0000, /* CX */ 84 EnableResumeOnRing = 0x0001, 85 GetResumeOnRing = 0x0002, 86 CmdTimerRequests = 0x5313, 87 DisableTimerRequests = 0x0000, /* CX */ 88 EnableTimerRequests = 0x0001, 89 GetTimerRequests = 0x0002, 90 }; 91 92 static char* eventstr[] = { 93 [NotifyStandbyRequest] "system standby request", 94 [NotifySuspendRequest] "system suspend request", 95 [NotifyNormalResume] "normal resume", 96 [NotifyCriticalResume] "critical resume", 97 [NotifyBatteryLow] "battery low", 98 [NotifyPowerStatusChange] "power status change", 99 [NotifyUpdateTime] "update time", 100 [NotifyCriticalSuspend] "critical suspend", 101 [NotifyUserStandbyRequest] "user standby request", 102 [NotifyUserSuspendRequest] "user suspend request", 103 [NotifyCapabilitiesChange] "capabilities change", 104 }; 105 106 static char* 107 apmevent(int e) 108 { 109 static char buf[32]; 110 111 if(0 <= e && e < nelem(eventstr) && eventstr[e]) 112 return eventstr[e]; 113 114 sprint(buf, "event 0x%ux", (uint)e); 115 return buf; 116 } 117 118 static char *error[256] = { 119 [0x01] "power mgmt disabled", 120 [0x02] "real mode connection already established", 121 [0x03] "interface not connected", 122 [0x05] "16-bit protected mode connection already established", 123 [0x06] "16-bit protected mode interface not supported", 124 [0x07] "32-bit protected mode interface already established", 125 [0x08] "32-bit protected mode interface not supported", 126 [0x09] "unrecognized device id", 127 [0x0A] "parameter value out of range", 128 [0x0B] "interface not engaged", 129 [0x0C] "function not supported", 130 [0x0D] "resume timer disabled", 131 [0x60] "unable to enter requested state", 132 [0x80] "no power mgmt events pending", 133 [0x86] "apm not present", 134 }; 135 136 static char* 137 apmerror(int id) 138 { 139 char *e; 140 static char buf[64]; 141 142 if(e = error[id&0xFF]) 143 return e; 144 145 sprint(buf, "unknown error %x", id); 146 return buf; 147 } 148 149 QLock apmlock; 150 int apmdebug; 151 152 static int 153 _apmcall(int fd, Ureg *u) 154 { 155 if(apmdebug) fprint(2, "call ax 0x%lux bx 0x%lux cx 0x%lux\n", 156 u->ax&0xFFFF, u->bx&0xFFFF, u->cx&0xFFFF); 157 158 seek(fd, 0, 0); 159 if(write(fd, u, sizeof *u) != sizeof *u) 160 return -1; 161 162 seek(fd, 0, 0); 163 if(read(fd, u, sizeof *u) != sizeof *u) 164 return -1; 165 166 if(apmdebug) fprint(2, "flags 0x%lux ax 0x%lux bx 0x%lux cx 0x%lux\n", 167 u->flags&0xFFFF, u->ax&0xFFFF, u->bx&0xFFFF, u->cx&0xFFFF); 168 169 if(u->flags & 1) { /* carry flag */ 170 werrstr("%s", apmerror(u->ax>>8)); 171 return -1; 172 } 173 return 0; 174 } 175 176 static int 177 apmcall(int fd, Ureg *u) 178 { 179 int r; 180 181 qlock(&apmlock); 182 r = _apmcall(fd, u); 183 qunlock(&apmlock); 184 return r; 185 } 186 187 typedef struct Apm Apm; 188 typedef struct Battery Battery; 189 190 struct Battery { 191 int status; 192 int percent; 193 int time; 194 }; 195 196 enum { 197 Mbattery = 4, 198 }; 199 struct Apm { 200 int fd; 201 202 int verhi; 203 int verlo; 204 205 int acstatus; 206 int nbattery; 207 208 int capabilities; 209 210 Battery battery[Mbattery]; 211 }; 212 enum { 213 AcUnknown = 0, /* Apm.acstatus */ 214 AcOffline, 215 AcOnline, 216 AcBackup, 217 218 BatteryUnknown = 0, /* Battery.status */ 219 BatteryHigh, 220 BatteryLow, 221 BatteryCritical, 222 BatteryCharging, 223 }; 224 225 static char* 226 acstatusstr[] = { 227 [AcUnknown] "unknown", 228 [AcOffline] "offline", 229 [AcOnline] "online", 230 [AcBackup] "backup", 231 }; 232 233 static char* 234 batterystatusstr[] = { 235 [BatteryUnknown] "unknown", 236 [BatteryHigh] "high", 237 [BatteryLow] "low", 238 [BatteryCritical] "critical", 239 [BatteryCharging] "charging", 240 }; 241 242 static char* 243 powerstatestr[] = { 244 [PowerOff] "off", 245 [PowerSuspend] "suspend", 246 [PowerStandby] "standby", 247 [PowerEnabled] "on", 248 }; 249 250 static char* 251 xstatus(char **str, int nstr, int x) 252 { 253 if(0 <= x && x < nstr && str[x]) 254 return str[x]; 255 return "unknown"; 256 } 257 258 static char* 259 batterystatus(int b) 260 { 261 return xstatus(batterystatusstr, nelem(batterystatusstr), b); 262 } 263 264 static char* 265 powerstate(int s) 266 { 267 return xstatus(powerstatestr, nelem(powerstatestr), s); 268 } 269 270 static char* 271 acstatus(int a) 272 { 273 return xstatus(acstatusstr, nelem(acstatusstr), a); 274 } 275 276 static int 277 apmversion(Apm *apm) 278 { 279 Ureg u; 280 281 u.ax = CmdDriverVersion; 282 u.bx = 0x0000; 283 u.cx = 0x0102; 284 if(apmcall(apm->fd, &u) < 0) 285 return -1; 286 287 apm->verhi = u.cx>>8; 288 apm->verlo = u.cx & 0xFF; 289 290 return u.cx; 291 } 292 293 static int 294 apmcpuidle(Apm *apm) 295 { 296 Ureg u; 297 298 u.ax = CmdCpuIdle; 299 return apmcall(apm->fd, &u); 300 } 301 302 static int 303 apmcpubusy(Apm *apm) 304 { 305 Ureg u; 306 307 u.ax = CmdCpuBusy; 308 return apmcall(apm->fd, &u); 309 } 310 311 static int 312 apmsetpowerstate(Apm *apm, int dev, int state) 313 { 314 Ureg u; 315 316 u.ax = CmdSetPowerState; 317 u.bx = dev; 318 u.cx = state; 319 return apmcall(apm->fd, &u); 320 } 321 322 static int 323 apmsetpowermgmt(Apm *apm, int dev, int state) 324 { 325 Ureg u; 326 327 u.ax = CmdSetPowerMgmt; 328 u.bx = dev; 329 u.cx = state; 330 return apmcall(apm->fd, &u); 331 } 332 333 static int 334 apmrestoredefaults(Apm *apm, int dev) 335 { 336 Ureg u; 337 338 u.ax = CmdRestoreDefaults; 339 u.bx = dev; 340 return apmcall(apm->fd, &u); 341 } 342 343 static int 344 apmgetpowerstatus(Apm *apm, int dev) 345 { 346 Battery *b; 347 Ureg u; 348 349 if(dev == DevAll) 350 b = &apm->battery[0]; 351 else if((dev & DevMask) == DevBattery) { 352 if(dev - DevBattery < nelem(apm->battery)) 353 b = &apm->battery[dev - DevBattery]; 354 else 355 b = nil; 356 } else { 357 werrstr("bad device number"); 358 return -1; 359 } 360 361 u.ax = CmdGetPowerStatus; 362 u.bx = dev; 363 364 if(apmcall(apm->fd, &u) < 0) 365 return -1; 366 367 if((dev & DevMask) == DevBattery) 368 apm->nbattery = u.si; 369 370 switch(u.bx>>8) { 371 case 0x00: 372 apm->acstatus = AcOffline; 373 break; 374 case 0x01: 375 apm->acstatus = AcOnline; 376 break; 377 case 0x02: 378 apm->acstatus = AcBackup; 379 break; 380 default: 381 apm->acstatus = AcUnknown; 382 break; 383 } 384 385 if(b != nil) { 386 switch(u.bx&0xFF) { 387 case 0x00: 388 b->status = BatteryHigh; 389 break; 390 case 0x01: 391 b->status = BatteryLow; 392 break; 393 case 0x02: 394 b->status = BatteryCritical; 395 break; 396 case 0x03: 397 b->status = BatteryCharging; 398 break; 399 default: 400 b->status = BatteryUnknown; 401 break; 402 } 403 404 if((u.cx & 0xFF) == 0xFF) 405 b->percent = -1; 406 else 407 b->percent = u.cx & 0xFF; 408 409 if((u.dx&0xFFFF) == 0xFFFF) 410 b->time = -1; 411 else if(u.dx & 0x8000) 412 b->time = 60*(u.dx & 0x7FFF); 413 else 414 b->time = u.dx & 0x7FFF; 415 } 416 417 return 0; 418 } 419 420 static int 421 apmgetevent(Apm *apm) 422 { 423 Ureg u; 424 425 u.ax = CmdGetPMEvent; 426 u.bx = 0; 427 u.cx = 0; 428 429 //when u.bx == NotifyNormalResume or NotifyCriticalResume, 430 //u.cx & 1 indicates PCMCIA socket was on while suspended, 431 //u.cx & 1 == 0 indicates was off. 432 433 if(apmcall(apm->fd, &u) < 0) 434 return -1; 435 436 return u.bx; 437 } 438 439 static int 440 apmgetpowerstate(Apm *apm, int dev) 441 { 442 Ureg u; 443 444 u.ax = CmdGetPowerState; 445 u.bx = dev; 446 u.cx = 0; 447 448 if(apmcall(apm->fd, &u) < 0) 449 return -1; 450 451 return u.cx; 452 } 453 454 static int 455 apmgetpowermgmt(Apm *apm, int dev) 456 { 457 Ureg u; 458 459 u.ax = CmdGetPowerMgmt; 460 u.bx = dev; 461 462 if(apmcall(apm->fd, &u) < 0) 463 return -1; 464 465 return u.cx; 466 } 467 468 static int 469 apmgetcapabilities(Apm *apm) 470 { 471 Ureg u; 472 473 u.ax = CmdGetCapabilities; 474 u.bx = DevBios; 475 476 if(apmcall(apm->fd, &u) < 0) 477 return -1; 478 479 apm->nbattery = u.bx & 0xFF; 480 apm->capabilities &= ~0xFFFF; 481 apm->capabilities |= u.cx; 482 return 0; 483 } 484 485 static int 486 apminstallationcheck(Apm *apm) 487 { 488 Ureg u; 489 490 u.ax = CmdInstallationCheck; 491 u.bx = DevBios; 492 if(apmcall(apm->fd, &u) < 0) 493 return -1; 494 495 if(u.cx & 0x0004) 496 apm->capabilities |= CapSlowCpu; 497 else 498 apm->capabilities &= ~CapSlowCpu; 499 return 0; 500 } 501 502 void 503 apmsetdisplaystate(Apm *apm, int s) 504 { 505 apmsetpowerstate(apm, DevDisplay, s); 506 } 507 508 void 509 apmblank(Apm *apm) 510 { 511 apmsetdisplaystate(apm, PowerStandby); 512 } 513 514 void 515 apmunblank(Apm *apm) 516 { 517 apmsetdisplaystate(apm, PowerEnabled); 518 } 519 520 void 521 apmsuspend(Apm *apm) 522 { 523 apmsetpowerstate(apm, DevAll, PowerSuspend); 524 } 525 526 Apm apm; 527 528 void 529 powerprint(void) 530 { 531 print("%s", ctime(time(0))); 532 if(apmgetpowerstatus(&apm, DevAll) == 0) { 533 print("%d batteries\n", apm.nbattery); 534 print("battery 0: status %s percent %d time %d:%.2d\n", 535 batterystatus(apm.battery[0].status), apm.battery[0].percent, 536 apm.battery[0].time/60, apm.battery[0].time%60); 537 } 538 } 539 540 void* 541 erealloc(void *v, ulong n) 542 { 543 v = realloc(v, n); 544 if(v == nil) 545 sysfatal("out of memory reallocating %lud", n); 546 setmalloctag(v, getcallerpc(&v)); 547 return v; 548 } 549 550 void* 551 emalloc(ulong n) 552 { 553 void *v; 554 555 v = malloc(n); 556 if(v == nil) 557 sysfatal("out of memory allocating %lud", n); 558 memset(v, 0, n); 559 setmalloctag(v, getcallerpc(&n)); 560 return v; 561 } 562 563 char* 564 estrdup(char *s) 565 { 566 int l; 567 char *t; 568 569 if (s == nil) 570 return nil; 571 l = strlen(s)+1; 572 t = emalloc(l); 573 memcpy(t, s, l); 574 setmalloctag(t, getcallerpc(&s)); 575 return t; 576 } 577 578 char* 579 estrdupn(char *s, int n) 580 { 581 int l; 582 char *t; 583 584 l = strlen(s); 585 if(l > n) 586 l = n; 587 t = emalloc(l+1); 588 memmove(t, s, l); 589 t[l] = '\0'; 590 setmalloctag(t, getcallerpc(&s)); 591 return t; 592 } 593 594 enum { 595 Qroot = 0, 596 Qevent, 597 Qbattery, 598 Qctl, 599 }; 600 601 static void rootread(Req*); 602 static void eventread(Req*); 603 static void ctlread(Req*); 604 static void ctlwrite(Req*); 605 static void batteryread(Req*); 606 607 typedef struct Dfile Dfile; 608 struct Dfile { 609 Qid qid; 610 char *name; 611 ulong mode; 612 void (*read)(Req*); 613 void (*write)(Req*); 614 }; 615 616 Dfile dfile[] = { 617 { {Qroot,0,QTDIR}, "/", DMDIR|0555, rootread, nil, }, 618 { {Qevent}, "event", 0444, eventread, nil, }, 619 { {Qbattery}, "battery", 0444, batteryread, nil, }, 620 { {Qctl}, "ctl", 0666, ctlread, ctlwrite, }, 621 }; 622 623 static int 624 fillstat(uvlong path, Dir *d, int doalloc) 625 { 626 int i; 627 628 for(i=0; i<nelem(dfile); i++) 629 if(path==dfile[i].qid.path) 630 break; 631 if(i==nelem(dfile)) 632 return -1; 633 634 memset(d, 0, sizeof *d); 635 d->uid = doalloc ? estrdup("apm") : "apm"; 636 d->gid = doalloc ? estrdup("apm") : "apm"; 637 d->length = 0; 638 d->name = doalloc ? estrdup(dfile[i].name) : dfile[i].name; 639 d->mode = dfile[i].mode; 640 d->atime = d->mtime = time(0); 641 d->qid = dfile[i].qid; 642 return 0; 643 } 644 645 static char* 646 fswalk1(Fid *fid, char *name, Qid *qid) 647 { 648 int i; 649 650 if(strcmp(name, "..")==0){ 651 *qid = dfile[0].qid; 652 fid->qid = *qid; 653 return nil; 654 } 655 656 for(i=1; i<nelem(dfile); i++){ /* i=1: 0 is root dir */ 657 if(strcmp(dfile[i].name, name)==0){ 658 *qid = dfile[i].qid; 659 fid->qid = *qid; 660 return nil; 661 } 662 } 663 return "file does not exist"; 664 } 665 666 static void 667 fsopen(Req *r) 668 { 669 switch((ulong)r->fid->qid.path){ 670 case Qroot: 671 r->fid->aux = (void*)0; 672 respond(r, nil); 673 return; 674 675 case Qevent: 676 case Qbattery: 677 if(r->ifcall.mode == OREAD){ 678 respond(r, nil); 679 return; 680 } 681 break; 682 683 case Qctl: 684 if((r->ifcall.mode&~(OTRUNC|OREAD|OWRITE|ORDWR)) == 0){ 685 respond(r, nil); 686 return; 687 } 688 break; 689 } 690 respond(r, "permission denied"); 691 return; 692 } 693 694 static void 695 fsstat(Req *r) 696 { 697 fillstat(r->fid->qid.path, &r->d, 1); 698 respond(r, nil); 699 } 700 701 static void 702 fsread(Req *r) 703 { 704 dfile[r->fid->qid.path].read(r); 705 } 706 707 static void 708 fswrite(Req *r) 709 { 710 dfile[r->fid->qid.path].write(r); 711 } 712 713 static void 714 rootread(Req *r) 715 { 716 int n; 717 uvlong offset; 718 char *p, *ep; 719 Dir d; 720 721 if(r->ifcall.offset == 0) 722 offset = 0; 723 else 724 offset = (uvlong)r->fid->aux; 725 726 p = r->ofcall.data; 727 ep = r->ofcall.data+r->ifcall.count; 728 729 if(offset == 0) /* skip root */ 730 offset = 1; 731 for(; p+2 < ep; p+=n){ 732 if(fillstat(offset, &d, 0) < 0) 733 break; 734 n = convD2M(&d, (uchar*)p, ep-p); 735 if(n <= BIT16SZ) 736 break; 737 offset++; 738 } 739 r->fid->aux = (void*)offset; 740 r->ofcall.count = p - r->ofcall.data; 741 respond(r, nil); 742 } 743 744 static void 745 batteryread(Req *r) 746 { 747 char buf[Mbattery*80], *ep, *p; 748 int i; 749 750 apmgetpowerstatus(&apm, DevAll); 751 752 p = buf; 753 ep = buf+sizeof buf; 754 *p = '\0'; /* could be no batteries */ 755 for(i=0; i<apm.nbattery && i<Mbattery; i++) 756 p += snprint(p, ep-p, "%s %d %d\n", 757 batterystatus(apm.battery[i].status), 758 apm.battery[i].percent, apm.battery[i].time); 759 760 readstr(r, buf); 761 respond(r, nil); 762 } 763 764 int 765 iscmd(char *p, char *cmd) 766 { 767 int l; 768 769 l = strlen(cmd); 770 return strncmp(p, cmd, l)==0 && p[l]=='\0' || p[l]==' ' || p[l]=='\t'; 771 } 772 773 char* 774 skip(char *p, char *cmd) 775 { 776 p += strlen(cmd); 777 while(*p==' ' || *p=='\t') 778 p++; 779 return p; 780 } 781 782 static void 783 respondx(Req *r, int c) 784 { 785 char err[ERRMAX]; 786 787 if(c == 0) 788 respond(r, nil); 789 else{ 790 rerrstr(err, sizeof err); 791 respond(r, err); 792 } 793 } 794 795 /* 796 * we don't do suspend because it messes up the 797 * cycle counter as well as the pcmcia ethernet cards. 798 */ 799 static void 800 ctlwrite(Req *r) 801 { 802 char buf[80], *p, *q; 803 int dev; 804 long count; 805 806 count = r->ifcall.count; 807 if(count > sizeof(buf)-1) 808 count = sizeof(buf)-1; 809 memmove(buf, r->ifcall.data, count); 810 buf[count] = '\0'; 811 812 if(count && buf[count-1] == '\n'){ 813 --count; 814 buf[count] = '\0'; 815 } 816 817 q = buf; 818 p = strchr(q, ' '); 819 if(p==nil) 820 p = q+strlen(q); 821 else 822 *p++ = '\0'; 823 824 if(strcmp(q, "")==0 || strcmp(q, "system")==0) 825 dev = DevAll; 826 else if(strcmp(q, "display")==0) 827 dev = DevDisplay; 828 else if(strcmp(q, "storage")==0) 829 dev = DevStorage; 830 else if(strcmp(q, "lpt")==0) 831 dev = DevLpt; 832 else if(strcmp(q, "eia")==0) 833 dev = DevEia; 834 else if(strcmp(q, "network")==0) 835 dev = DevNetwork; 836 else if(strcmp(q, "pcmcia")==0) 837 dev = DevPCMCIA; 838 else{ 839 respond(r, "unknown device"); 840 return; 841 } 842 843 if(strcmp(p, "enable")==0) 844 respondx(r, apmsetpowermgmt(&apm, dev, EnablePowerMgmt)); 845 else if(strcmp(p, "disable")==0) 846 respondx(r, apmsetpowermgmt(&apm, dev, DisablePowerMgmt)); 847 else if(strcmp(p, "standby")==0) 848 respondx(r, apmsetpowerstate(&apm, dev, PowerStandby)); 849 else if(strcmp(p, "on")==0) 850 respondx(r, apmsetpowerstate(&apm, dev, PowerEnabled)); 851 /* 852 else if(strcmp(p, "off")==0) 853 respondx(r, apmsetpowerstate(&apm, dev, PowerOff)); 854 */ 855 else if(strcmp(p, "suspend")==0) 856 respondx(r, apmsetpowerstate(&apm, dev, PowerSuspend)); 857 else 858 respond(r, "unknown verb"); 859 } 860 861 static int 862 statusline(char *buf, int nbuf, char *name, int dev) 863 { 864 int s; 865 char *state; 866 867 state = "unknown"; 868 if((s = apmgetpowerstate(&apm, dev)) >= 0) 869 state = powerstate(s); 870 return snprint(buf, nbuf, "%s %s\n", name, state); 871 } 872 873 static void 874 ctlread(Req *r) 875 { 876 char buf[256+7*50], *ep, *p; 877 878 p = buf; 879 ep = buf+sizeof buf; 880 881 p += snprint(p, ep-p, "ac %s\n", acstatus(apm.acstatus)); 882 p += snprint(p, ep-p, "capabilities"); 883 if(apm.capabilities & CapStandby) 884 p += snprint(p, ep-p, " standby"); 885 if(apm.capabilities & CapSuspend) 886 p += snprint(p, ep-p, " suspend"); 887 if(apm.capabilities & CapSlowCpu) 888 p += snprint(p, ep-p, " slowcpu"); 889 p += snprint(p, ep-p, "\n"); 890 891 p += statusline(p, ep-p, "system", DevAll); 892 p += statusline(p, ep-p, "display", DevDisplay); 893 p += statusline(p, ep-p, "storage", DevStorage); 894 p += statusline(p, ep-p, "lpt", DevLpt); 895 p += statusline(p, ep-p, "eia", DevEia|All); 896 p += statusline(p, ep-p, "network", DevNetwork|All); 897 p += statusline(p, ep-p, "pcmcia", DevPCMCIA|All); 898 USED(p); 899 900 readstr(r, buf); 901 respond(r, nil); 902 } 903 904 enum { 905 STACK = 16384, 906 }; 907 908 Channel *creq; 909 Channel *cflush; 910 Channel *cevent; 911 Req *rlist, **tailp; 912 int rp, wp; 913 int nopoll; 914 char eventq[32][80]; 915 916 static void 917 flushthread(void*) 918 { 919 Req *r, *or, **rq; 920 921 threadsetname("flushthread"); 922 while(r = recvp(cflush)){ 923 or = r->oldreq; 924 for(rq=&rlist; *rq; rq=&(*rq)->aux){ 925 if(*rq == or){ 926 *rq = or->aux; 927 if(tailp==&or->aux) 928 tailp = rq; 929 respond(or, "interrupted"); 930 break; 931 } 932 } 933 respond(r, nil); 934 } 935 } 936 937 static void 938 answerany(void) 939 { 940 char *buf; 941 int l, m; 942 Req *r; 943 944 if(rlist==nil || rp==wp) 945 return; 946 947 while(rlist && rp != wp){ 948 r = rlist; 949 rlist = r->aux; 950 if(rlist==nil) 951 tailp = &rlist; 952 953 l = 0; 954 buf = r->ofcall.data; 955 m = r->ifcall.count; 956 while(rp != wp){ 957 if(l+strlen(eventq[rp]) <= m){ 958 strcpy(buf+l, eventq[rp]); 959 l += strlen(buf+l); 960 }else if(l==0){ 961 strncpy(buf, eventq[rp], m-1); 962 buf[m-1] = '\0'; 963 l += m; 964 }else 965 break; 966 rp++; 967 if(rp == nelem(eventq)) 968 rp = 0; 969 } 970 r->ofcall.count = l; 971 respond(r, nil); 972 } 973 } 974 975 static void 976 eventwatch(void*) 977 { 978 int e, s; 979 980 threadsetname("eventwatch"); 981 for(;;){ 982 s = 0; 983 while((e = apmgetevent(&apm)) >= 0){ 984 sendul(cevent, e); 985 s = 1; 986 } 987 if(s) 988 sendul(cevent, -1); 989 if(sleep(750) < 0) 990 break; 991 } 992 } 993 994 static void 995 eventthread(void*) 996 { 997 int e; 998 999 threadsetname("eventthread"); 1000 for(;;){ 1001 while((e = recvul(cevent)) >= 0){ 1002 snprint(eventq[wp], sizeof(eventq[wp])-1, "%s", apmevent(e)); 1003 strcat(eventq[wp], "\n"); 1004 wp++; 1005 if(wp==nelem(eventq)) 1006 wp = 0; 1007 if(wp+1==rp || (wp+1==nelem(eventq) && rp==0)) 1008 break; 1009 } 1010 answerany(); 1011 } 1012 } 1013 1014 static void 1015 eventproc(void*) 1016 { 1017 Req *r; 1018 1019 threadsetname("eventproc"); 1020 1021 creq = chancreate(sizeof(Req*), 0); 1022 cevent = chancreate(sizeof(ulong), 0); 1023 cflush = chancreate(sizeof(Req*), 0); 1024 1025 tailp = &rlist; 1026 if(!nopoll) 1027 proccreate(eventwatch, nil, STACK); 1028 threadcreate(eventthread, nil, STACK); 1029 threadcreate(flushthread, nil, STACK); 1030 1031 while(r = recvp(creq)){ 1032 *tailp = r; 1033 r->aux = nil; 1034 tailp = &r->aux; 1035 answerany(); 1036 } 1037 } 1038 1039 static void 1040 fsflush(Req *r) 1041 { 1042 sendp(cflush, r); 1043 } 1044 1045 static void 1046 eventread(Req *r) 1047 { 1048 sendp(creq, r); 1049 } 1050 1051 static void 1052 fsattach(Req *r) 1053 { 1054 char *spec; 1055 static int first = 1; 1056 1057 spec = r->ifcall.aname; 1058 1059 if(first){ 1060 first = 0; 1061 proccreate(eventproc, nil, STACK); 1062 } 1063 1064 if(spec && spec[0]){ 1065 respond(r, "invalid attach specifier"); 1066 return; 1067 } 1068 r->fid->qid = dfile[0].qid; 1069 r->ofcall.qid = dfile[0].qid; 1070 respond(r, nil); 1071 } 1072 1073 Srv fs = { 1074 .attach= fsattach, 1075 .walk1= fswalk1, 1076 .open= fsopen, 1077 .read= fsread, 1078 .write= fswrite, 1079 .stat= fsstat, 1080 .flush= fsflush, 1081 }; 1082 1083 void 1084 usage(void) 1085 { 1086 fprint(2, "usage: aux/apm [-ADPi] [-d /dev/apm] [-m /mnt/apm] [-s service]\n"); 1087 exits("usage"); 1088 } 1089 1090 void 1091 threadmain(int argc, char **argv) 1092 { 1093 char *dev, *mtpt, *srv; 1094 1095 dev = nil; 1096 mtpt = "/mnt/apm"; 1097 srv = nil; 1098 ARGBEGIN{ 1099 case 'A': 1100 apmdebug = 1; 1101 break; 1102 case 'D': 1103 chatty9p = 1; 1104 break; 1105 case 'P': 1106 nopoll = 1; 1107 break; 1108 case 'd': 1109 dev = EARGF(usage()); 1110 break; 1111 case 'i': 1112 fs.nopipe++; 1113 fs.infd = 0; 1114 fs.outfd = 1; 1115 mtpt = nil; 1116 break; 1117 case 'm': 1118 mtpt = EARGF(usage()); 1119 break; 1120 case 's': 1121 srv = EARGF(usage()); 1122 break; 1123 }ARGEND 1124 1125 if(dev == nil){ 1126 if((apm.fd = open("/dev/apm", ORDWR)) < 0 1127 && (apm.fd = open("#P/apm", ORDWR)) < 0){ 1128 fprint(2, "open %s: %r\n", dev); 1129 threadexitsall("open"); 1130 } 1131 } else if((apm.fd = open(dev, ORDWR)) < 0){ 1132 fprint(2, "open %s: %r\n", dev); 1133 threadexitsall("open"); 1134 } 1135 1136 if(apmversion(&apm) < 0){ 1137 fprint(2, "cannot get apm version: %r\n"); 1138 threadexitsall("apmversion"); 1139 } 1140 1141 if(apm.verhi < 1 || (apm.verhi==1 && apm.verlo < 2)){ 1142 fprint(2, "apm version %d.%d not supported\n", apm.verhi, apm.verlo); 1143 threadexitsall("apmversion"); 1144 } 1145 1146 if(apmgetcapabilities(&apm) < 0) 1147 fprint(2, "warning: cannot read apm capabilities: %r\n"); 1148 1149 apminstallationcheck(&apm); 1150 apmcpuidle(&apm); 1151 1152 rfork(RFNOTEG); 1153 threadpostmountsrv(&fs, srv, mtpt, MREPL); 1154 } 1155