123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "io.h"
- #include "../port/error.h"
- #include "mp.h"
- #include <acpi/acpica/acpi.h>
- enum
- {
- Sdthdrsz = 36, /* size of SDT header */
- Qdir = 0,
- Qctl,
- Qtbl,
- Qio,
- };
- #if 0
- static Cmdtab ctls[] =
- {
- {CMregion, "region", 6},
- {CMgpe, "gpe", 3},
- };
- #endif
- static Dirtab acpidir[]={
- ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555,
- "acpictl", {Qctl}, 0, 0666,
- "acpitbl", {Qtbl}, 0, 0444,
- "acpiregio", {Qio}, 0, 0666,
- };
- #if 0
- static char* regnames[] = {
- "mem", "io", "pcicfg", "embed",
- "smb", "cmos", "pcibar",
- };
- #endif
- static void *rsd;
- /*
- * we use mp->machno (or index in Mach array) as the identifier,
- * but ACPI relies on the apic identifier.
- */
- int
- corecolor(int core)
- {
- Mach *m;
- static int colors[32];
- if(core < 0 || core >= MACHMAX)
- return -1;
- m = sys->machptr[core];
- if(m == nil)
- return -1;
- if(core >= 0 && core < nelem(colors) && colors[core] != 0)
- return colors[core] - 1;
- return -1;
- }
- int
- pickcore(int mycolor, int index)
- {
- return 0;
- }
- static void*
- rsdscan(uint8_t* addr, int len, char* signature)
- {
- int sl;
- uint8_t *e, *p;
- e = addr+len;
- sl = strlen(signature);
- for(p = addr; p+sl < e; p += 16){
- if(memcmp(p, signature, sl))
- continue;
- return p;
- }
- return nil;
- }
- static void*
- rsdsearch(char* signature)
- {
- uintptr_t p;
- uint8_t *bda;
- void *rsd;
- /*
- * Search for the data structure signature:
- * 1) in the first KB of the EBDA;
- * 2) in the BIOS ROM between 0xE0000 and 0xFFFFF.
- */
- if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4) == 0){
- bda = BIOSSEG(0x40);
- if((p = (bda[0x0F]<<8)|bda[0x0E])){
- if(rsd = rsdscan(KADDR(p), 1024, signature))
- return rsd;
- }
- }
- return rsdscan(BIOSSEG(0xE000), 0x20000, signature);
- }
- static void
- acpirsdptr(void)
- {
- rsd = rsdsearch("RSD PTR ");
- if (rsd == nil) {
- print("NO RSD PTR found\n");
- return;
- }
- print("Found RST PTR ta %p\n", rsd);
-
- #if 0
- assert(sizeof(Sdthdr) == 36);
- DBG("acpi: RSD PTR@ %#p, physaddr %#ux length %u %#llux rev %d\n",
- rsd, l32get(rsd->raddr), l32get(rsd->length),
- l64get(rsd->xaddr), rsd->revision);
- if(rsd->revision >= 2){
- if(sdtchecksum(rsd, 36) == nil){
- DBG("acpi: RSD: bad checksum\n");
- return;
- }
- sdtpa = l64get(rsd->xaddr);
- asize = 8;
- }
- else{
- if(sdtchecksum(rsd, 20) == nil){
- DBG("acpi: RSD: bad checksum\n");
- return;
- }
- sdtpa = l32get(rsd->raddr);
- asize = 4;
- }
- /*
- * process the RSDT or XSDT table.
- */
- xsdt = malloc(sizeof(Xsdt));
- if(xsdt == nil){
- DBG("acpi: malloc failed\n");
- return;
- }
- if((xsdt->p = sdtmap(sdtpa, &xsdt->len, 1)) == nil){
- DBG("acpi: sdtmap failed\n");
- return;
- }
- if((xsdt->p[0] != 'R' && xsdt->p[0] != 'X') || memcmp(xsdt->p+1, "SDT", 3) != 0){
- DBG("acpi: xsdt sig: %c%c%c%c\n",
- xsdt->p[0], xsdt->p[1], xsdt->p[2], xsdt->p[3]);
- free(xsdt);
- xsdt = nil;
- vunmap(xsdt, xsdt->len);
- return;
- }
- xsdt->p += sizeof(Sdthdr);
- xsdt->len -= sizeof(Sdthdr);
- xsdt->asize = asize;
- DBG("acpi: XSDT %#p\n", xsdt);
- acpixsdtload(nil);
- /* xsdt is kept and not unmapped */
- #endif
- }
- static int
- acpigen(Chan *c, char* d, Dirtab *tab, int ntab, int i, Dir *dp)
- {
- Qid qid;
- if(i == DEVDOTDOT){
- mkqid(&qid, Qdir, 0, QTDIR);
- devdir(c, qid, ".", 0, eve, 0555, dp);
- return 1;
- }
- i++; /* skip first element for . itself */
- if(tab==0 || i>=ntab)
- return -1;
- tab += i;
- qid = tab->qid;
- qid.path &= ~Qdir;
- qid.vers = 0;
- devdir(c, qid, tab->name, tab->length, eve, tab->perm, dp);
- return 1;
- }
- ACPI_STATUS
- AcpiOsInitialize(void)
- {
- print("%s\n", __func__);
- acpirsdptr();
- return AE_OK;
- }
- ACPI_STATUS
- AcpiOsTerminate (
- void)
- {
- print("%s\n", __func__);
- return AE_OK;
- }
- int
- acpiinit(void)
- {
- ACPI_TABLE_HEADER *h;
- int status;
- int apiccnt;
- status = AcpiInitializeSubsystem();
- if (ACPI_FAILURE(status))
- panic("can't start acpi");
- status = AcpiInitializeTables(NULL, 16, FALSE);
- if (ACPI_FAILURE(status))
- panic("can't set up acpi tables");
- status = AcpiLoadTables();
- if (ACPI_FAILURE(status))
- panic("Can't load ACPI tables");
- status = AcpiEnableSubsystem(0);
- if (ACPI_FAILURE(status))
- panic("Can't enable ACPI subsystem");
- status = AcpiInitializeObjects(0);
- if (ACPI_FAILURE(status))
- panic("Can't Initialize ACPI objects");
- for(apiccnt = 1; ;apiccnt++) {
- extern uint8_t *apicbase;
- ACPI_TABLE_MADT *m;
- status = AcpiGetTable(ACPI_SIG_MADT, apiccnt, &h);
- if (ACPI_FAILURE(status))
- break;
- m = (ACPI_TABLE_MADT *)h;
- print("APIC %d: %p 0x%x\n", apiccnt, (void *)(uint64_t)m->Address, m->Flags);
- if(apicbase == nil){
- if((apicbase = vmap((uintptr_t)m->Address, 1024)) == nil){
- panic("%s: can't map apicbase\n", __func__);
- }
- print("%s: apicbase %#p -> %#p\n", __func__, (void *)(uint64_t)m->Address, apicbase);
- }
- }
- if ((apiccnt == 1) && ACPI_FAILURE(status))
- panic("Can't find a MADT");
- return 0;
- }
- static Chan*
- acpiattach(char *spec)
- {
- return nil;
- // return devattach(L'α', spec);
- }
- static Walkqid*
- acpiwalk(Chan *c, Chan *nc, char **name, int nname)
- {
- return devwalk(c, nc, name, nname, acpidir, nelem(acpidir), acpigen);
- }
- static int32_t
- acpistat(Chan *c, uint8_t *dp, int32_t n)
- {
- return devstat(c, dp, n, acpidir, nelem(acpidir), acpigen);
- }
- static Chan*
- acpiopen(Chan *c, int omode)
- {
- return devopen(c, omode, acpidir, nelem(acpidir), acpigen);
- }
- static void
- acpiclose(Chan *c)
- {
- }
- #if 0
- static char*ttext;
- static int tlen;
- #endif
- static int32_t
- acpiread(Chan *c, void *a, int32_t n, int64_t off)
- {
- uint64_t q;
- q = c->qid.path;
- switch(q){
- case Qdir:
- return devdirread(c, a, n, acpidir, nelem(acpidir), acpigen);
- case Qtbl:
- return -1; //readstr(off, a, n, ttext);
- case Qio:
- return -1; //regio(reg, a, n, off, 0);
- }
- error(Eperm);
- return -1;
- }
- static int32_t
- acpiwrite(Chan *c, void *a, int32_t n, int64_t off)
- {
- if(c->qid.path == Qio){
- //if(reg == nil)
- error("region not configured");
- }
- if(c->qid.path != Qctl)
- error(Eperm);
- error("NP");
- #if 0
- cb = parsecmd(a, n);
- if(waserror()){
- free(cb);
- nexterror();
- }
- ct = lookupcmd(cb, ctls, nelem(ctls));
- DBG("acpi ctl %s\n", cb->f[0]);
- switch(ct->index){
- case CMregion:
- r = reg;
- if(r == nil){
- r = smalloc(sizeof(Reg));
- r->name = nil;
- }
- kstrdup(&r->name, cb->f[1]);
- r->spc = acpiregid(cb->f[2]);
- if(r->spc < 0){
- free(r);
- reg = nil;
- error("bad region type");
- }
- if(r->spc == Rpcicfg || r->spc == Rpcibar){
- rno = r->base>>Rpciregshift & Rpciregmask;
- fun = r->base>>Rpcifunshift & Rpcifunmask;
- dev = r->base>>Rpcidevshift & Rpcidevmask;
- bus = r->base>>Rpcibusshift & Rpcibusmask;
- r->tbdf = MKBUS(BusPCI, bus, dev, fun);
- r->base = rno; /* register ~ our base addr */
- }
- r->base = strtoull(cb->f[3], nil, 0);
- r->len = strtoull(cb->f[4], nil, 0);
- r->accsz = strtoul(cb->f[5], nil, 0);
- if(r->accsz < 1 || r->accsz > 4){
- free(r);
- reg = nil;
- error("bad region access size");
- }
- reg = r;
- DBG("region %s %s %llux %llux sz%d",
- r->name, acpiregstr(r->spc), r->base, r->len, r->accsz);
- break;
- case CMgpe:
- i = strtoul(cb->f[1], nil, 0);
- if(i >= ngpes)
- error("gpe out of range");
- kstrdup(&gpes[i].obj, cb->f[2]);
- DBG("gpe %d %s\n", i, gpes[i].obj);
- setgpeen(i, 1);
- break;
- default:
- panic("acpi: unknown ctl");
- }
- poperror();
- free(cb);
- return n;
- #endif
- return -1;
- }
- Dev acpidevtab = {
- .dc = L'α',
- .name = "acpi",
- .reset = devreset,
- .init = devinit,
- .shutdown = devshutdown,
- .attach = acpiattach,
- .walk = acpiwalk,
- .stat = acpistat,
- .open = acpiopen,
- .create = devcreate,
- .close = acpiclose,
- .read = acpiread,
- .bread = devbread,
- .write = acpiwrite,
- .bwrite = devbwrite,
- .remove = devremove,
- .wstat = devwstat,
- };
- static int tbdf(ACPI_PCI_ID *p)
- {
- return (p->Bus << 8) | (p->Device << 3) | (p->Function);
- }
- ACPI_STATUS
- AcpiOsReadPciConfiguration (
- ACPI_PCI_ID *PciId,
- UINT32 Reg,
- UINT64 *Value,
- UINT32 Width)
- {
- Pcidev p;
- p.tbdf = tbdf(PciId);
- print("%s\n", __func__);
- switch(Width) {
- case 32:
- *Value = pcicfgr32(&p, Reg);
- break;
- case 16:
- *Value = pcicfgr16(&p, Reg);
- break;
- case 8:
- *Value = pcicfgr8(&p, Reg);
- break;
- default:
- panic("Can't read pci: bad width %d\n", Width);
- }
- return AE_OK;
- }
- ACPI_STATUS
- AcpiOsWritePciConfiguration (
- ACPI_PCI_ID *PciId,
- UINT32 Reg,
- UINT64 Value,
- UINT32 Width)
- {
- Pcidev p;
- p.tbdf = tbdf(PciId);
- print("%s\n", __func__);
- switch(Width) {
- case 32:
- pcicfgw32(&p, Reg, Value);
- break;
- case 16:
- pcicfgw16(&p, Reg, Value);
- break;
- case 8:
- pcicfgw8(&p, Reg, Value);
- break;
- default:
- panic("Can't read pci: bad width %d\n", Width);
- }
- return AE_OK;
- }
- /*
- * Miscellaneous
- */
- BOOLEAN
- AcpiOsReadable (
- void *Pointer,
- ACPI_SIZE Length)
- {
- print("%s\n", __func__);
- panic("%s", __func__);
- return AE_OK;
- }
- BOOLEAN
- AcpiOsWritable (
- void *Pointer,
- ACPI_SIZE Length)
- {
- print("%s\n", __func__);
- panic("%s", __func__);
- return AE_OK;
- }
- UINT64
- AcpiOsGetTimer (
- void)
- {
- print("%s\n", __func__);
- panic("%s", __func__);
- return AE_OK;
- }
- ACPI_STATUS
- AcpiOsSignal (
- UINT32 Function,
- void *Info)
- {
- print("%s\n", __func__);
- panic("%s", __func__);
- return AE_OK;
- }
- void ACPI_INTERNAL_VAR_XFACE
- AcpiOsPrintf (
- const char *Format,
- ...)
- {
- va_list args;
- va_start(args, Format);
- print((char *)Format, args);
- va_end(args);
- }
- void
- AcpiOsVprintf (
- const char *Format,
- va_list Args)
- {
- print((char *)Format, Args);
- }
- void
- AcpiOsFree (
- void * Memory)
- {
- //print("%s\n", __func__);
- free(Memory);
- }
- void *
- AcpiOsAllocate (
- ACPI_SIZE Size)
- {
- //print("%s\n", __func__);
- return malloc(Size);
- }
- void *
- AcpiOsMapMemory (
- ACPI_PHYSICAL_ADDRESS Where,
- ACPI_SIZE Length)
- {
- void *v = vmap(Where, Length);
- print("%s %p = vmap(%p,0x%x)\n", __func__, v, (void*)Where, Length);
- print("Val @ %p is 0x%x\n", v, *(int *)v);
- return v;
- }
- void
- AcpiOsUnmapMemory (
- void *LogicalAddress,
- ACPI_SIZE Size)
- {
- print("%s %p %d \n", __func__, LogicalAddress, Size);
- vunmap(LogicalAddress, Size);
- }
- ACPI_STATUS
- AcpiOsGetPhysicalAddress (
- void *LogicalAddress,
- ACPI_PHYSICAL_ADDRESS *PhysicalAddress)
- {
- ACPI_PHYSICAL_ADDRESS ret = mmuphysaddr((uintptr_t)LogicalAddress);
- print("%s %p = mmyphysaddr(%p)", __func__, (void *)ret, LogicalAddress);
- *PhysicalAddress = ret;
- return AE_OK;
- }
- /* This is the single threaded version of
- * these functions. This is now NetBSD does it. */
- ACPI_STATUS
- AcpiOsCreateSemaphore (
- UINT32 MaxUnits,
- UINT32 InitialUnits,
- ACPI_SEMAPHORE *OutHandle)
- {
- //print("%s\n", __func__);
- *OutHandle = (ACPI_SEMAPHORE) 1;
- return AE_OK;
- }
- ACPI_STATUS
- AcpiOsDeleteSemaphore (
- ACPI_SEMAPHORE Handle)
- {
- //print("%s\n", __func__);
- return AE_OK;
- }
- ACPI_STATUS
- AcpiOsWaitSemaphore (
- ACPI_SEMAPHORE Handle,
- UINT32 Units,
- UINT16 Timeout)
- {
- //print("%s\n", __func__);
- return AE_OK;
- }
- ACPI_STATUS
- AcpiOsSignalSemaphore (
- ACPI_SEMAPHORE Handle,
- UINT32 Units)
- {
- //print("%s\n", __func__);
- return AE_OK;
- }
- /* this is the single threaded case and as minix shows there is nothing to do. */
- ACPI_STATUS
- AcpiOsCreateLock (
- ACPI_SPINLOCK *OutHandle)
- {
- //print("%s\n", __func__);
- *OutHandle = nil;
- return AE_OK;
- }
- void
- AcpiOsDeleteLock (
- ACPI_SPINLOCK Handle)
- {
- //print("%s\n", __func__);
- }
- ACPI_CPU_FLAGS
- AcpiOsAcquireLock (
- ACPI_SPINLOCK Handle)
- {
- //print("%s\n", __func__);
- return 0;
- }
- void
- AcpiOsReleaseLock (
- ACPI_SPINLOCK Handle,
- ACPI_CPU_FLAGS Flags)
- {
- //print("%s\n", __func__);
- }
- struct handler {
- ACPI_OSD_HANDLER ServiceRoutine;
- void *Context;
- };
- /* The ACPI interrupt signature and the Harvey one are not compatible. So, we pass an arg to
- * intrenable that can in turn be used to this function to call the ACPI handler. */
- static void acpihandler(Ureg *_, void *arg)
- {
- struct handler *h = arg;
- h->ServiceRoutine(h->Context);
- }
- ACPI_STATUS
- AcpiOsInstallInterruptHandler (
- UINT32 InterruptNumber,
- ACPI_OSD_HANDLER ServiceRoutine,
- void *Context)
- {
- /* minix says "don't do it". So we don't, yet. */
- return AE_OK;
- struct handler *h = malloc(sizeof(*h));
- if (! h)
- return AE_NO_MEMORY;
- h->ServiceRoutine = ServiceRoutine;
- h->Context = Context;
- print("%s %d %p %p \n", __func__, InterruptNumber, ServiceRoutine, Context);
- /* once enabled, can't be disabled; ignore the return value unless it's nil. */
- intrenable(InterruptNumber, acpihandler, h, 0x5, "ACPI interrupt handler");
- return AE_OK;
- }
- ACPI_STATUS
- AcpiOsRemoveInterruptHandler (
- UINT32 InterruptNumber,
- ACPI_OSD_HANDLER ServiceRoutine)
- {
- print("%s\n", __func__);
- panic("%s", __func__);
- return AE_OK;
- }
- void
- AcpiOsWaitEventsComplete (
- void)
- {
- print("%s\n", __func__);
- panic("%s", __func__);
- }
- void
- AcpiOsSleep (
- UINT64 Milliseconds)
- {
- print("%s\n", __func__);
- panic("%s", __func__);
- }
- void
- AcpiOsStall(
- UINT32 Microseconds)
- {
- print("%s\n", __func__);
- panic("%s", __func__);
- }
- ACPI_THREAD_ID
- AcpiOsGetThreadId (
- void)
- {
- /* What to do here? ACPI won't take 0 for an answer.
- * I guess tell it we're 1? What do we do? */
- return 1;
- //print("%s\n", __func__);
- Proc *up = externup();
- return up->pid;
- }
- ACPI_STATUS
- AcpiOsExecute (
- ACPI_EXECUTE_TYPE Type,
- ACPI_OSD_EXEC_CALLBACK Function,
- void *Context)
- {
- print("%s\n", __func__);
- panic("%s", __func__);
- return AE_OK;
- }
- ACPI_STATUS
- AcpiOsReadPort (
- ACPI_IO_ADDRESS Address,
- UINT32 *Value,
- UINT32 Width)
- {
- /* Ooooooookay ... ACPI specifies the IO width in *bits*. */
- switch(Width) {
- case 4*8:
- *Value = inl(Address);
- break;
- case 2*8:
- *Value = ins(Address);
- break;
- case 1*8:
- *Value = inb(Address);
- break;
- default:
- panic("%s, bad width %d", __func__, Width);
- break;
- }
- print("%s 0x%x 0x%x\n", __func__, Address, *Value);
- return AE_OK;
- }
- ACPI_STATUS
- AcpiOsWritePort (
- ACPI_IO_ADDRESS Address,
- UINT32 Value,
- UINT32 Width)
- {
- switch(Width) {
- case 4*8:
- outl(Address, Value);
- break;
- case 2*8:
- outs(Address, Value);
- break;
- case 1*8:
- outb(Address, Value);
- break;
- default:
- panic("%s, bad width %d", __func__, Width);
- break;
- }
- print("%s 0x%x 0x%x\n", __func__, Address, Value);
- return AE_OK;
- }
- /*
- * Platform and hardware-independent physical memory interfaces
- */
- ACPI_STATUS
- AcpiOsReadMemory (
- ACPI_PHYSICAL_ADDRESS Address,
- UINT64 *Value,
- UINT32 Width)
- {
- print("%s\n", __func__);
- panic("%s", __func__);
- return AE_OK;
- }
- ACPI_STATUS
- AcpiOsWriteMemory (
- ACPI_PHYSICAL_ADDRESS Address,
- UINT64 Value,
- UINT32 Width)
- {
- print("%s\n", __func__);
- panic("%s", __func__);
- return AE_OK;
- }
- /*
- * ACPI Table interfaces
- */
- ACPI_PHYSICAL_ADDRESS
- AcpiOsGetRootPointer (
- void)
- {
- print("%s returns %p\n", __func__, rsd);
- return (ACPI_PHYSICAL_ADDRESS) PADDR(rsd);
- }
- ACPI_STATUS
- AcpiOsPredefinedOverride (
- const ACPI_PREDEFINED_NAMES *InitVal,
- ACPI_STRING *NewVal)
- {
- print("%s\n", __func__);
- *NewVal = nil;
- return AE_OK;
- }
- ACPI_STATUS
- AcpiOsTableOverride (
- ACPI_TABLE_HEADER *ExistingTable,
- ACPI_TABLE_HEADER **NewTable)
- {
- print("%s\n", __func__);
- *NewTable = nil;
- return AE_OK;
- }
- ACPI_STATUS
- AcpiOsPhysicalTableOverride (
- ACPI_TABLE_HEADER *ExistingTable,
- ACPI_PHYSICAL_ADDRESS *NewAddress,
- UINT32 *NewTableLength)
- {
- print("%s\n", __func__);
- *NewAddress = (ACPI_PHYSICAL_ADDRESS)nil;
- return AE_OK;
- }
- /*
- * Debug input
- */
- ACPI_STATUS
- AcpiOsGetLine (
- char *Buffer,
- UINT32 BufferLength,
- UINT32 *BytesRead)
- {
- print("%s\n", __func__);
- panic("%s", __func__);
- return AE_OK;
- }
|