gonzui


Format: Advanced Search

tkernel_2/kernel/sysmgr/src/deviceio.cbare sourcepermlink (0.14 seconds)

Search this content:

    1: /*
    2:  *----------------------------------------------------------------------
    3:  *    T-Kernel 2.0 Software Package
    4:  *
    5:  *    Copyright 2011 by Ken Sakamura.
    6:  *    This software is distributed under the latest version of T-License 2.x.
    7:  *----------------------------------------------------------------------
    8:  *
    9:  *    Released by T-Engine Forum(http://www.t-engine.org/) at 2011/05/17.
   10:  *    Modified by T-Engine Forum at 2011/09/08.
   11:  *    Modified by T-Engine Forum at 2014/09/10.
   12:  *    Modified by TRON Forum(http://www.tron.org/) at 2015/06/01.
   13:  *
   14:  *----------------------------------------------------------------------
   15:  */
   16: 
   17: /*
   18:  *      deviceio.c (T-Kernel/SM)
   19:  *      Device Management Function: Input/Output
   20:  */
   21: 
   22: #include "sysmgr.h"
   23: #include <sys/rominfo.h>
   24: #include <sys/ssid.h>
   25: 
   26: LOCAL   OpnCB     *OpnCBtbl;  /* Open management information table */
   27: LOCAL   QUEUE     FreeOpnCB;  /* Unused queue */
   28: LOCAL   INT       MaxOpnDev;    /* Maximum number of devices open */
   29: 
   30: #define DD(opncb)               ( (opncb) - OpnCBtbl + 1 )
   31: #define OPNCB(dd)               ( OpnCBtbl + ((dd) - 1) )
   32: 
   33: LOCAL   ReqCB     *ReqCBtbl;  /* Request management information table */
   34: LOCAL   QUEUE     FreeReqCB;  /* Unused queue */
   35: LOCAL   INT       MaxReqDev;    /* Maximum number of device requests */
   36: 
   37: #define REQID(reqcb)            ( (reqcb) - ReqCBtbl + 1 )
   38: #define REQCB(reqid)            ( ReqCBtbl + ((reqid) - 1) )
   39: 
   40: #define DEVREQ_REQCB(devreq)    ((ReqCB*)((B*)(devreq) - offsetof(ReqCB, req)))
   41: 
   42: /*
   43:  * Get resource management information
   44:  */
   45: LOCAL ResCB* GetResCB( ID ssid, ID tskid )
   46: {
   47:         ResCB  *rescb;
   48: 
   49:         rescb = GetResBlk(ssid, tskid);
   50:         if ( rescb == NULL ) {
   51:                 return NULL;
   52:         }
   53: 
   54:         LockDM();
   55: 
   56:         /* If the startup function is not called, initialize at this point */
   57:         if ( rescb->openq.next == NULL ) {
   58:                 /* Initialization of open device management queue */
   59:                 QueInit(&rescb->openq);
   60:         }
   61: 
   62:         UnlockDM();
   63: 
   64:         return rescb;
   65: }
   66: 
   67: /*
   68:  * Verify validity of device descriptor
   69:  */
   70: EXPORT ER check_devdesc( ID dd, UINT mode, OpnCB **p_opncb )
   71: {
   72:         OpnCB  *opncb;
   73: 
   74:         if ( dd < 1 || dd > MaxOpnDev ) {
   75:                 return E_ID;
   76:         }
   77:         opncb = OPNCB(dd);
   78:         if ( opncb->resid <= 0 ) {
   79:                 return E_ID;
   80:         }
   81: 
   82:         if ( opncb->resid != tk_get_rid(TSK_SELF) ) {
   83:                 return E_OACV;
   84:         }
   85: 
   86:         if ( mode != 0 ) {
   87:                 if ( (opncb->omode & mode) == 0 ) {
   88:                         return E_OACV;
   89:                 }
   90:         }
   91: 
   92:         *p_opncb = opncb;
   93:         return E_OK;
   94: }
   95: 
   96: /*
   97:  * Verify validity of request ID
   98:  */
   99: LOCAL ReqCB* check_reqid( ID reqid, OpnCB *opncb )
  100: {
  101:         ReqCB  *reqcb;
  102: 
  103:         if ( reqid < 1 || reqid > MaxReqDev ) {
  104:                 return NULL;
  105:         }
  106:         reqcb = REQCB(reqid);
  107:         if ( reqcb->opncb != opncb ) {
  108:                 return NULL;
  109:         }
  110: 
  111:         return reqcb;
  112: }
  113: 
  114: /*
  115:  * Get open management block
  116:  */
  117: LOCAL OpnCB* newOpnCB( DevCB *devcb, INT unitno, UINT omode, ResCB *rescb )
  118: {
  119:         OpnCB  *opncb;
  120: 
  121:         /* Get space in open management block */
  122:         opncb = (OpnCB*)QueRemoveNext(&FreeOpnCB);
  123:         if ( opncb == NULL ) {
  124:                 return NULL; /* No space */
  125:         }
  126: 
  127:         /* Register as open device */
  128:         QueInsert(&opncb->q, &devcb->openq);
  129:         QueInsert(&opncb->resq, &rescb->openq);
  130: 
  131:         opncb->devcb  = devcb;
  132:         opncb->unitno = unitno;
  133:         opncb->omode  = omode;
  134:         QueInit(&opncb->requestq);
  135:         opncb->syncreq = 0;
  136:         opncb->waitone = 0;
  137:         opncb->nwaireq = 0;
  138:         opncb->abort_tskid = 0;
  139: 
  140:         opncb->resid  = 0; /* Indicate that open processing is not completed */
  141: 
  142:         return opncb;
  143: }
  144: 
  145: /*
  146:  * Free open management block
  147:  */
  148: LOCAL void delOpnCB( OpnCB *opncb )
  149: {
  150:         QueRemove(&opncb->q);
  151:         QueRemove(&opncb->resq);
  152: 
  153:         QueInsert(&opncb->q, &FreeOpnCB);
  154: 
  155:         opncb->resid = 0;
  156: }
  157: 
  158: /*
  159:  * Get request management block
  160:  */
  161: LOCAL ReqCB* newReqCB( OpnCB *opncb )
  162: {
  163:         ReqCB  *reqcb;
  164: 
  165:         /* Get space in request management block */
  166:         reqcb = (ReqCB*)QueRemoveNext(&FreeReqCB);
  167:         if ( reqcb == NULL ) {
  168:                 return NULL; /* No space */
  169:         }
  170: 
  171:         /* Register as requested open device */
  172:         QueInsert(&reqcb->q, &opncb->requestq);
  173: 
  174:         reqcb->opncb = opncb;
  175: 
  176:         return reqcb;
  177: }
  178: 
  179: /*
  180:  * Free request management block
  181:  */
  182: LOCAL void delReqCB( ReqCB *reqcb )
  183: {
  184:         QueRemove(&reqcb->q);
  185: 
  186:         QueInsert(&reqcb->q, &FreeReqCB);
  187:         reqcb->opncb = NULL;
  188: }
  189: 
  190: /* ------------------------------------------------------------------------ */
  191: 
  192: /*
  193:  * Enter Synchronization Queue
  194:  *      entry is registered as the task that has entered synchronization queue
  195:  *      return value
  196:  *      FALSE        entry is the first task that has entered the synchronization
  197:  *              queue
  198:  *      TRUE there were other tasks in the synchronization queue
  199:  */
  200: LOCAL BOOL enterSyncWait( QUEUE *syncq, WaitQ *entry )
  201: {
  202:         BOOL   wait;
  203: 
  204:         wait = !isQueEmpty(syncq);
  205: 
  206:         entry->tskid = tk_get_tid();
  207:         QueInsert(&entry->q, syncq);
  208: 
  209:         return wait;
  210: }
  211: 
  212: /*
  213:  * Erase entry from synchronization queue
  214:  *     Erase entry from the synchronization queue.
  215:  *      If there is another in the synchronizaton queue, wake it up.
  216:  */
  217: LOCAL void leaveSyncWait( QUEUE *syncq, WaitQ *entry )
  218: {
  219:         QueRemove(&entry->q);
  220: 
  221:         if ( !isQueEmpty(syncq) ) {
  222:                 SyncSignalDM(((WaitQ*)syncq->next)->tskid);
  223:         }
  224: }
  225: 
  226: /*
  227:  * Check open mode
  228:  */
  229: LOCAL ER chkopenmode( DevCB *devcb, INT unitno, UINT omode )
  230: {
  231:         QUEUE  *q;
  232:         OpnCB  *opncb;
  233:         INT    read, write, rexcl, wexcl;
  234: 
  235:         if ( (omode & TD_UPDATE) == 0 ) {
  236:                 return E_PAR;
  237:         }
  238: 
  239:         /* Check current open state */
  240:         read = write = rexcl = wexcl = 0;
  241:         for ( q = devcb->openq.next; q != &devcb->openq; q = q->next ) {
  242:                 opncb = (OpnCB*)q;
  243: 
  244:                 if ( unitno == 0 || opncb->unitno == 0
  245:                                                 || opncb->unitno == unitno ) {
  246:                         if ( (opncb->omode & TD_READ)  != 0 ) {
  247:                                 read++;
  248:                         }
  249:                         if ( (opncb->omode & TD_WRITE) != 0 ) {
  250:                                 write++;
  251:                         }
  252:                         if ( (opncb->omode & (TD_EXCL|TD_REXCL)) != 0) {
  253:                                 rexcl++;
  254:                         }
  255:                         if ( (opncb->omode & (TD_EXCL|TD_WEXCL)) != 0) {
  256:                                 wexcl++;
  257:                         }
  258:                 }
  259:         }
  260: 
  261:         /* Is it able to open? */
  262:         if ( (omode & (TD_EXCL|TD_REXCL)) != 0 && read  > 0 ) {
  263:                 return E_BUSY;
  264:         }
  265:         if ( (omode & (TD_EXCL|TD_WEXCL)) != 0 && write > 0 ) {
  266:                 return E_BUSY;
  267:         }
  268:         if ( (omode & TD_READ)  != 0 && rexcl > 0 ) {
  269:                 return E_BUSY;
  270:         }
  271:         if ( (omode & TD_WRITE) != 0 && wexcl > 0 ) {
  272:                 return E_BUSY;
  273:         }
  274: 
  275:         return E_OK;
  276: }
  277: 
  278: /*
  279:  * If the designated device is opened, return TRUE.
  280:  *      However, for devices opend by opncb, or
  281:  *      devices for the open processing is not complete, return FALSE.
  282:  *      For devices being closed, return TRUE.
  283:  */
  284: LOCAL BOOL chkopen( DevCB *devcb, INT unitno, OpnCB *opncb )
  285: {
  286:         QUEUE  *q;
  287: 
  288:         for ( q = devcb->openq.next; q != &devcb->openq; q = q->next ) {
  289:                 if (  (OpnCB*)q != opncb &&
  290:                         ((OpnCB*)q)->unitno == unitno &&
  291:                         ((OpnCB*)q)->resid != 0 ) {
  292:                         return TRUE;
  293:                 }
  294:         }
  295:         return FALSE;
  296: }
  297: 
  298: /*
  299:  * Device open
  300:  */
  301: EXPORT ID _tk_opn_dev( CONST UB *devnm, UINT omode )
  302: {
  303:         UB     pdevnm[L_DEVNM + 1];
  304:         INT    unitno;
  305:         ResCB  *rescb;
  306:         DevCB  *devcb;
  307:         OpnCB  *opncb;
  308:         WaitQ  waiq;
  309:         ER     ercd;
  310: 
  311:         ercd = ChkSpaceBstrR(devnm, 0);
  312:         if ( ercd < E_OK ) {
  313:                 goto err_ret1;
  314:         }
  315: 
  316:         unitno = phydevnm(pdevnm, devnm);
  317: 
  318:         /* Get resource management information */
  319:         rescb = GetResCB(DEVICE_SVC, TSK_SELF);
  320:         if ( rescb == NULL ) {
  321:                 ercd = E_CTX;
  322:                 goto err_ret1;
  323:         }
  324: 
  325:         LockDM();
  326: 
  327:         /* Search device to open */
  328:         devcb = searchDevCB(pdevnm);
  329:         if ( devcb == NULL || unitno > devcb->ddev.nsub ) {
  330:                 ercd = E_NOEXS;
  331:                 goto err_ret2;
  332:         }
  333: 
  334:         /* Check open mode */
  335:         ercd = chkopenmode(devcb, unitno, omode);
  336:         if ( ercd < E_OK ) {
  337:                 goto err_ret2;
  338:         }
  339: 
  340:         /* Get open management block */
  341:         opncb = newOpnCB(devcb, unitno, omode, rescb);
  342:         if ( opncb == NULL ) {
  343:                 ercd = E_LIMIT;
  344:                 goto err_ret2;
  345:         }
  346: 
  347:         /* Multiple tasks can initiate open/close processing,
  348:            so ensure processing only by one task at a time. */
  349:         if ( enterSyncWait(&devcb->syncq, &waiq) ) {
  350:                 /* Wait for synchronization for concurrent open/close */
  351:                 UnlockDM();
  352:                 SyncWaitDM();
  353:                 LockDM();
  354:         }
  355: 
  356:         /* Is device driver call required? */
  357:         if ( ! ( chkopen(devcb, unitno, opncb) &&
  358:                                 (devcb->ddev.drvatr & TDA_OPENREQ) == 0 ) ) {
  359:                 /* Device driver call */
  360:                 UnlockDM();
  361:                 ercd = call_openfn(devcb, DEVID(devcb, unitno), omode);
  362:                 LockDM();
  363:                 if ( ercd < E_OK ) {
  364:                         goto err_ret3;
  365:                 }
  366:         }
  367:         opncb->resid = tk_get_rid(TSK_SELF);
  368:                         /* Indicate that open processing is completed */
  369: 
  370:        /* Wake up task waiting for synchronization for concurrent open/close */
  371:         leaveSyncWait(&devcb->syncq, &waiq);
  372:         UnlockDM();
  373: 
  374:         return DD(opncb);
  375: 
  376: err_ret3:
  377:         delOpnCB(opncb);
  378:         leaveSyncWait(&devcb->syncq, &waiq);
  379: err_ret2:
  380:         UnlockDM();
  381: err_ret1:
  382:         DEBUG_PRINT(("_tk_opn_dev ercd = %d\n", ercd));
  383:         return ercd;
  384: }
  385: 
  386: /*
  387:  * Abort all requests
  388:  */
  389: LOCAL void abort_allrequest( OpnCB *opncb )
  390: {
  391:         DevCB  *devcb;
  392:         ReqCB  *reqcb;
  393:         QUEUE  *q;
  394: 
  395:         /* If 'execfn' and 'waitfn' are called, execute abort request. */
  396:         LockDM();
  397: 
  398:         devcb = opncb->devcb;
  399:         opncb->abort_tskid = tk_get_tid();
  400:         opncb->abort_cnt = 0;
  401: 
  402:         LockDAbort();
  403:         UnlockDM();
  404: 
  405:         if ( opncb->nwaireq > 0 ) {
  406:                 /* Multiple requests wait */
  407:                 reqcb = DEVREQ_REQCB(opncb->waireqlst);
  408: 
  409:                 /* Device driver call */
  410:                 call_abortfn(devcb, reqcb->tskid, opncb->waireqlst,
  411:                                                         opncb->nwaireq);
  412:                 opncb->abort_cnt++;
  413:         } else {
  414:                 /* Start request or single request wait */
  415:                 for ( q = opncb->requestq.next;
  416:                                         q != &opncb->requestq; q = q->next ) {
  417:                         reqcb = (ReqCB*)q;
  418:                         if ( reqcb->tskid == 0 ) {
  419:                                 continue;
  420:                         }
  421:                         reqcb->req.c.abort = TRUE;
  422: 
  423:                         /* Device driver call */
  424:                         call_abortfn(devcb, reqcb->tskid, &reqcb->req, 1);
  425:                         opncb->abort_cnt++;
  426:                 }
  427:         }
  428:         UnlockDAbort();
  429: 
  430:         if ( opncb->abort_cnt > 0 ) {
  431:                 /* Wait for completion of abort request processing */
  432:                 SyncWaitDM();
  433:         }
  434:         opncb->abort_tskid = 0;
  435: 
  436:         /* Abort remaining requests and wait for completion */
  437:         LockDM();
  438:         while ( !isQueEmpty(&opncb->requestq) ) {
  439:                 reqcb = (ReqCB*)opncb->requestq.next;
  440:                 reqcb->req.c.abort = TRUE;
  441: 
  442:                 /* Device driver waitfn call */
  443:                 UnlockDM();
  444:                 call_waitfn(devcb, &reqcb->req, 1, TMO_FEVR);
  445:                 LockDM();
  446: 
  447:                 /* Unregister completed request */
  448:                 delReqCB(reqcb);
  449:         }
  450:         UnlockDM();
  451: }
  452: 
  453: /*
  454:  * Device close processing
  455:  */
  456: LOCAL ER close_device( OpnCB *opncb, UINT option )
  457: {
  458:         ID     devid;
  459:         DevCB  *devcb;
  460:         INT    unitno;
  461:         WaitQ  waiq;
  462:         ER     ercd = E_OK;
  463: 
  464:         /* Abort all requests during processing */
  465:         abort_allrequest(opncb);
  466: 
  467:         LockDM();
  468: 
  469:         devcb  = opncb->devcb;
  470:         unitno = opncb->unitno;
  471:         devid = DEVID(devcb, unitno);
  472: 
  473:         /* Multiple tasks can initiate open/close processing,
  474:            so ensure processing only by one task at a time. */
  475:         if ( enterSyncWait(&devcb->syncq, &waiq) ) {
  476:                 /* Wait for synchronization for concurrent open/close */
  477:                 UnlockDM();
  478:                 SyncWaitDM();
  479:                 LockDM();
  480:         }
  481: 
  482:         /* Is device driver call required? */
  483:         if ( chkopen(devcb, unitno, opncb) ) {
  484:                 if ( (devcb->ddev.drvatr & TDA_OPENREQ) == 0 ) {
  485:                         goto no_drvcall;
  486:                 }
  487:                 option &= ~TD_EJECT;  /* EJECT is effective at last close */
  488:         }
  489:         /* Device driver call */
  490:         UnlockDM();
  491:         ercd = call_closefn(devcb, DEVID(devcb, unitno), option);
  492:         LockDM();
  493: 
  494: no_drvcall:
  495:         /* Free open management block */
  496:         delOpnCB(opncb);
  497: 
  498:        /* Wake up task waiting for synchronization for concurrent open/close */
  499:         leaveSyncWait(&devcb->syncq, &waiq);
  500: 
  501:         UnlockDM();
  502: 
  503:         ERCD_PRINT(("close_device ercd = %d\n", ercd));
  504: 
  505:         return ercd;
  506: }
  507: 
  508: /*
  509:  * Device close
  510:  */
  511: EXPORT ER _tk_cls_dev( ID dd, UINT option )
  512: {
  513:         OpnCB  *opncb;
  514:         ER     ercd;
  515: 
  516:         LockDM();
  517: 
  518:         ercd = check_devdesc(dd, 0, &opncb);
  519:         if ( ercd < E_OK ) {
  520:                 UnlockDM();
  521:                 goto err_ret;
  522:         }
  523: 
  524:         opncb->resid = -1; /* Indicate that it is during close processing */
  525: 
  526:         UnlockDM();
  527: 
  528:         /* Device close processing */
  529:         ercd = close_device(opncb, option);
  530: 
  531: err_ret:
  532:         ERCD_PRINT(("_tk_cls_dev ercd = %d\n", ercd));
  533:         return ercd;
  534: }
  535: 
  536: /* ------------------------------------------------------------------------ */
  537: 
  538: enum ReqType {
  539:         AsyncReq,  /* asynchronous I/O (tk_rea_dev tk_wri_dev tk_wai_dev) */
  540:         SyncReq           /* synchronous I/O  (tk_srea_dev tk_swri_dev) */
  541: };
  542: 
  543: /*
  544:  * Check break request
  545:  */
  546: LOCAL ER check_break( void )
  547: {
  548:         T_RTEX rtex;
  549:         ER     ercd;
  550: 
  551:         ercd = tk_ref_tex(TSK_SELF, &rtex);
  552:         if ( ercd < E_OK ) {
  553:                 goto err_ret;
  554:         }
  555: 
  556:         return ( rtex.pendtex != 0 )? E_ABORT: E_OK;
  557: 
  558: err_ret:
  559:         DEBUG_PRINT(("check_break ercd = %d\n", ercd));
  560:         return ercd;
  561: }
  562: 
  563: /*
  564:  * Request for starting input/output to device
  565:  */
  566: LOCAL ID request( ID dd, D start, void *buf, INT size, TMO_U tmout,
  567:                                         INT cmd, enum ReqType syncreq )
  568: {
  569:         ATR    drvatr;
  570:         OpnCB  *opncb;
  571:         DevCB  *devcb;
  572:         ReqCB  *reqcb;
  573:         UINT   m;
  574:         ER     ercd;
  575: 
  576:         LockDM();
  577: 
  578:         /* Check whether there is a break request */
  579:         ercd = check_break();
  580:         if ( ercd < E_OK ) {
  581:                 goto err_ret1;
  582:         }
  583: 
  584:         m = ( cmd == TDC_READ )? TD_READ: TD_WRITE;
  585:         ercd = check_devdesc(dd, m, &opncb);
  586:         if ( ercd < E_OK ) {
  587:                 goto err_ret1;
  588:         }
  589: 
  590:         if ( syncreq == SyncReq ) {
  591:                 /* synchronous I/O is prohibited while there are
  592:                                         pending I/O requests for completion */
  593:                 if ( opncb->nwaireq > 0 ) { ercd = E_OBJ; goto err_ret1; }
  594:                 opncb->syncreq++;
  595:         }
  596: 
  597:         devcb = opncb->devcb;
  598:         drvatr = devcb->ddev.drvatr;
  599: 
  600:         /* check the range of parameter value */
  601:         if ( (drvatr & TDA_DEV_D) == 0 ) {
  602:                 if ( start > 0x7fffffff || start < (-0x7fffffff-1) ) {
  603:                         ercd = E_PAR; goto err_ret2;
  604:                 }
  605:         }
  606: 
  607:         /* Get request management block */
  608:         reqcb = newReqCB(opncb);
  609:         if ( reqcb == NULL ) {
  610:                 ercd = E_LIMIT;
  611:                 goto err_ret2;
  612:         }
  613: 
  614:         /* Set request packet */
  615:         MEMSET(&reqcb->req, 0, sizeof(DEVREQ));
  616:         reqcb->req.c.devid  = DEVID(devcb, opncb->unitno);
  617:         reqcb->req.c.cmd    = cmd;
  618:         if ( (opncb->omode & TD_NOLOCK) != 0 ) {
  619:                 reqcb->req.c.nolock = TRUE;
  620:         }
  621:         if ( (drvatr & TDA_DEV_D) == 0 ) {
  622:                 reqcb->req.s.start   = start;
  623:                 reqcb->req.s.size    = size;
  624:                 reqcb->req.s.buf     = buf;
  625:         } else {
  626:                 reqcb->req.l.start_d = start;
  627:                 reqcb->req.l.size    = size;
  628:                 reqcb->req.l.buf     = buf;
  629:         }
  630:         ercd = tk_get_tsp(TSK_SELF, &reqcb->req.c.tskspc);
  631:         if ( ercd < E_OK ) {
  632:                 goto err_ret3;
  633:         }
  634: 
  635:         /* Indicate that it is during processing */
  636:         reqcb->tskid = tk_get_tid();
  637: 
  638:         /* Device driver call */
  639:         UnlockDM();
  640:         ercd = call_execfn(devcb, &reqcb->req, tmout);
  641:         LockDM();
  642: 
  643:         LockDAbort();
  644: 
  645:         /* Indicate that it is not during processing */
  646:         reqcb->tskid = 0;
  647: 
  648:         /* If there is an abort completion wait task,
  649:            notify abort completion */
  650:         if ( opncb->abort_tskid > 0 && --opncb->abort_cnt == 0 ) {
  651:                 SyncSignalDM(opncb->abort_tskid);
  652:         }
  653:         UnlockDAbort();
  654: 
  655:         if ( ercd < E_OK ) {
  656:                 goto err_ret3;
  657:         }
  658: 
  659:         UnlockDM();
  660: 
  661:         return REQID(reqcb);
  662: 
  663: err_ret3:
  664:         delReqCB(reqcb);
  665: err_ret2:
  666:         if ( syncreq == SyncReq ) {
  667:                 opncb->syncreq--;
  668:         }
  669: err_ret1:
  670:         UnlockDM();
  671:         DEBUG_PRINT(("request ercd = %d\n", ercd));
  672:         return ercd;
  673: }
  674: 
  675: /*
  676:  * wait for I/O completion for a device
  677:  */
  678: LOCAL ID waitcomplete( ID dd, ID reqid, W *asize, ER *ioer,
  679:                                         TMO_U tmout, enum ReqType syncreq )
  680: {
  681:         ATR    drvatr;
  682:         OpnCB  *opncb;
  683:         DevCB  *devcb;
  684:         ReqCB  *reqcb;
  685:         DEVREQ *devreq;
  686:         INT    reqno, nreq;
  687:         ID     tskid;
  688:         BOOL   abort;
  689:         ER     ercd;
  690: 
  691:         tskid = tk_get_tid();
  692: 
  693:         LockDM();
  694: 
  695:         ercd = check_devdesc(dd, 0, &opncb);
  696:         if ( ercd < E_OK ) {
  697:                 goto err_ret1;
  698:         }
  699: 
  700:         /* Check whether there is a break request */
  701:         abort = FALSE;
  702:         ercd = check_break();
  703:         if ( ercd < E_OK ) {
  704:                 if ( syncreq == AsyncReq ) goto err_ret1;
  705:                 abort = TRUE;
  706:         }
  707: 
  708:         devcb = opncb->devcb;
  709:         drvatr = devcb->ddev.drvatr;
  710: 
  711:         if ( reqid == 0 ) {
  712:                 /* When waiting for completion of any of requests for 'dd' */
  713:                 if ( opncb->nwaireq > 0 || opncb->waitone > 0
  714:                      || opncb->syncreq > 0 ) {
  715:                         ercd = E_OBJ;
  716:                         goto err_ret1;
  717:                 }
  718:                 if ( isQueEmpty(&opncb->requestq) ) {
  719:                         ercd = E_NOEXS;
  720:                         goto err_ret1;
  721:                 }
  722: 
  723:                 /* Create wait request list */
  724:                 reqcb = (ReqCB*)opncb->requestq.next;
  725:                 for ( nreq = 1;; nreq++ ) {
  726:                         reqcb->tskid = tskid;
  727:                         devreq = &reqcb->req;
  728:                         reqcb = (ReqCB*)reqcb->q.next;
  729:                         if ( reqcb == (ReqCB*)&opncb->requestq ) {
  730:                                 break;
  731:                         }
  732:                         devreq->c.next = &reqcb->req;
  733:                 }
  734:                 devreq->c.next = NULL;
  735:                 devreq = &((ReqCB*)opncb->requestq.next)->req;
  736:                 opncb->waireqlst = devreq;
  737:                 opncb->nwaireq = nreq;
  738:         } else {
  739:                 /* Wait for completion of abort request processing */
  740:                 reqcb = check_reqid(reqid, opncb);
  741:                 if ( reqcb == NULL ) {
  742:                         ercd = E_ID;
  743:                         goto err_ret1;
  744:                 }
  745:                 if ( opncb->nwaireq > 0 || reqcb->tskid > 0 ) {
  746:                         ercd = E_OBJ;
  747:                         goto err_ret1;
  748:                 }
  749: 
  750:                 /* Create waiting request list */
  751:                 reqcb->tskid = tskid;
  752:                 devreq = &reqcb->req;
  753:                 devreq->c.next = NULL;
  754:                 if ( abort ) devreq->c.abort = TRUE;
  755:                 nreq = 1;
  756:                 opncb->waitone++;
  757:         }
  758: 
  759:         /* Device driver call */
  760:         UnlockDM();
  761:         reqno = call_waitfn(devcb, devreq, nreq, tmout);
  762:         LockDM();
  763:         if ( reqno <  E_OK ) {
  764:                 ercd = reqno;
  765:         } else if ( reqno >= nreq ) {
  766:                 ercd = E_SYS;
  767:         }
  768: 
  769:         LockDAbort();
  770: 
  771:         /* Free wait processing */
  772:         if ( reqid == 0 ) {
  773:                 opncb->nwaireq = 0;
  774:         } else {
  775:                 opncb->waitone--;
  776:         }
  777: 
  778:         /* If there is an abort completion wait task,
  779:            notify abort completion */
  780:         if ( opncb->abort_tskid > 0 && --opncb->abort_cnt == 0 ) {
  781:                 SyncSignalDM(opncb->abort_tskid);
  782:         }
  783: 
  784:         /* Get processing result */
  785:         while ( devreq != NULL ) {
  786:                 reqcb = DEVREQ_REQCB(devreq);
  787:                 if ( reqno-- == 0 ) {
  788:                         reqid = REQID(reqcb);
  789:                         if ( (drvatr & TDA_DEV_D) == 0 ) {
  790:                                 *asize = devreq->s.asize;
  791:                                 *ioer  = devreq->s.error;
  792:                         } else {
  793:                                 *asize = devreq->l.asize;
  794:                                 *ioer  = devreq->l.error;
  795:                         }
  796:                 }
  797:                 reqcb->tskid = 0;
  798:                 devreq = devreq->c.next;
  799:         }
  800:         UnlockDAbort();
  801: 
  802:         if ( ercd < E_OK ) {
  803:                 goto err_ret1;
  804:         }
  805: 
  806:         if ( syncreq == SyncReq ) {
  807:                 opncb->syncreq--;
  808:         }
  809: 
  810:         /* Unregister completed request */
  811:         delReqCB(REQCB(reqid));
  812: 
  813:         UnlockDM();
  814: 
  815:         return reqid;
  816: 
  817: err_ret1:
  818:         if ( syncreq == SyncReq ) {
  819:                 opncb->syncreq--;
  820:         }
  821:         UnlockDM();
  822:         DEBUG_PRINT(("waitcomplete ercd = %d\n", ercd));
  823:         return ercd;
  824: }
  825: 
  826: /*
  827:  * Start reading from device
  828:  */
  829: EXPORT ID _tk_rea_dev( ID dd, W start, void *buf, W size, TMO tmout )
  830: {
  831:         return _tk_rea_dev_du(dd, start, buf, size, to_usec_tmo(tmout));
  832: }
  833: EXPORT ID _tk_rea_dev_du( ID dd, D start, void *buf, W size, TMO_U tmout )
  834: {
  835:         ER     ercd;
  836: 
  837:         ercd = request(dd, start, buf, size, tmout, TDC_READ, AsyncReq);
  838: 
  839:         ERCD_PRINT(("_tk_rea_dev ercd = %d\n", ercd));
  840:         return ercd;
  841: }
  842: 
  843: /*
  844:  * Synchronous reading from device
  845:  */
  846: EXPORT ER _tk_srea_dev( ID dd, W start, void *buf, W size, W *asize )
  847: {
  848:         return _tk_srea_dev_d(dd, start, buf, size, asize);
  849: }
  850: EXPORT ER _tk_srea_dev_d( ID dd, D start, void *buf, W size, W *asize )
  851: {
  852:         ER     ercd, ioercd;
  853: 
  854:         ercd = ChkSpaceRW(asize, sizeof(W));
  855:         if ( ercd < E_OK ) {
  856:                 goto err_ret;
  857:         }
  858: 
  859:         ercd = request(dd, start, buf, size, TMO_FEVR, TDC_READ, SyncReq);
  860:         if ( ercd < E_OK ) {
  861:                 goto err_ret;
  862:         }
  863: 
  864:         ercd = waitcomplete(dd, ercd, asize, &ioercd, TMO_FEVR, SyncReq);
  865:         if ( ercd < E_OK ) {
  866:                 goto err_ret;
  867:         }
  868: 
  869:         return ioercd;
  870: 
  871: err_ret:
  872:         DEBUG_PRINT(("_tk_srea_dev ercd = %d\n", ercd));
  873:         return ercd;
  874: }
  875: 
  876: /*
  877:  * Start writing to device
  878:  */
  879: EXPORT ID _tk_wri_dev( ID dd, W start, CONST void *buf, W size, TMO tmout )
  880: {
  881:         return _tk_wri_dev_du(dd, start, buf, size, to_usec_tmo(tmout));
  882: }
  883: EXPORT ID _tk_wri_dev_du( ID dd, D start, CONST void *buf, W size, TMO_U tmout )
  884: {
  885:         ER     ercd;
  886: 
  887:         ercd = request(dd, start, (void*)buf, size, tmout, TDC_WRITE, AsyncReq);
  888: 
  889:         ERCD_PRINT(("_tk_wri_dev ercd = %d\n", ercd));
  890:         return ercd;
  891: }
  892: 
  893: /*
  894:  * Synchronous writing to device
  895:  */
  896: EXPORT ER _tk_swri_dev( ID dd, W start, CONST void *buf, W size, W *asize )
  897: {
  898:         return _tk_swri_dev_d(dd, start, buf, size, asize);
  899: }
  900: EXPORT ER _tk_swri_dev_d( ID dd, D start, CONST void *buf, W size, W *asize )
  901: {
  902:         ER     ercd, ioercd;
  903: 
  904:         ercd = ChkSpaceRW(asize, sizeof(W));
  905:         if ( ercd < E_OK ) {
  906:                 goto err_ret;
  907:         }
  908: 
  909:         ercd = request(dd, start, (void*)buf, size, TMO_FEVR,
  910:                                                         TDC_WRITE, SyncReq);
  911:         if ( ercd < E_OK ) {
  912:                 goto err_ret;
  913:         }
  914: 
  915:         ercd = waitcomplete(dd, ercd, asize, &ioercd, TMO_FEVR, SyncReq);
  916:         if ( ercd < E_OK ) {
  917:                 goto err_ret;
  918:         }
  919: 
  920:         return ioercd;
  921: 
  922: err_ret:
  923:         DEBUG_PRINT(("_tk_swri_dev ercd = %d\n", ercd));
  924:         return ercd;
  925: }
  926: 
  927: /*
  928:  * Request completion wait
  929:  */
  930: EXPORT ID _tk_wai_dev( ID dd, ID reqid, W *asize, ER *ioer, TMO tmout )
  931: {
  932:         return _tk_wai_dev_u(dd, reqid, asize, ioer, to_usec_tmo(tmout));
  933: }
  934: EXPORT ID _tk_wai_dev_u( ID dd, ID reqid, W *asize, ER *ioer, TMO_U tmout )
  935: {
  936:         ER     ercd;
  937: 
  938:         ercd = ChkSpaceRW(asize, sizeof(W));
  939:         if ( ercd < E_OK ) {
  940:                 goto err_ret;
  941:         }
  942: 
  943:         ercd = ChkSpaceRW(ioer, sizeof(ER));
  944:         if ( ercd < E_OK ) {
  945:                 goto err_ret;
  946:         }
  947: 
  948:         ercd = waitcomplete(dd, reqid, asize, ioer, tmout, AsyncReq);
  949:         if ( ercd < E_OK ) {
  950:                 goto err_ret;
  951:         }
  952: 
  953:         return ercd;
  954: 
  955: err_ret:
  956:         DEBUG_PRINT(("_tk_wai_dev_u ercd = %d\n", ercd));
  957:         return ercd;
  958: }
  959: 
  960: /* ------------------------------------------------------------------------ */
  961: 
  962: /* Suspend disable request count */
  963: LOCAL   INT       DisSusCnt;
  964: 
  965: /* Maximum number of suspend disable request counts */
  966: #define MAX_DISSUS      0x7fff
  967: 
  968: /*
  969:  * Send driver request event to each device
  970:  */
  971: LOCAL ER sendevt_alldevice( INT evttyp, BOOL disk )
  972: {
  973:         QUEUE  *q;
  974:         DevCB  *devcb;
  975:         BOOL   d;
  976:         ER     ercd = E_OK;
  977: 
  978:         for ( q = UsedDevCB.next; q != &UsedDevCB; q = q->next ) {
  979:                 devcb = (DevCB*)q;
  980: 
  981:                 d = ( (devcb->ddev.devatr & TD_DEVTYPE) == TDK_DISK )?
  982:                                                                 TRUE : FALSE;
  983:                 if ( disk != d ) {
  984:                         continue;
  985:                 }
  986: 
  987:                 /* Device driver call */
  988:                 ercd = call_eventfn(devcb, evttyp, NULL);
  989:         }
  990: 
  991:         ERCD_PRINT(("sendevt_alldevice ercd = %d\n", ercd));
  992:         return ercd;
  993: }
  994: 
  995: /*
  996:  * Suspend
  997:  */
  998: LOCAL ER do_suspend( void )
  999: {
 1000:         ER     ercd;
 1001: 
 1002:         /* Stop accepting device registration/unregistration */
 1003:         LockREG();
 1004: 
 1005:         /* Processing before starting subsystem suspend */
 1006:         ercd = tk_evt_ssy(0, TSEVT_SUSPEND_BEGIN, 0, 0);
 1007:         ERCD_PRINT(("1. do_suspend -> tk_evt_ssy ercd = %d\n", ercd));
 1008: 
 1009:         /* Suspend processing of device except for disks */
 1010:         ercd = sendevt_alldevice(TDV_SUSPEND, FALSE);
 1011:         ERCD_PRINT(("2. do_suspend -> sendevt_alldevice ercd = %d\n", ercd));
 1012: 
 1013:         /* Suspend processing of disk device */
 1014:         ercd = sendevt_alldevice(TDV_SUSPEND, TRUE);
 1015:         ERCD_PRINT(("3. do_suspend -> sendevt_alldevice ercd = %d\n", ercd));
 1016: 
 1017:         /* Stop accepting new requests */
 1018:         LockDM();
 1019: 
 1020:         /* Processing after completion of subsystem suspend */
 1021:         ercd = tk_evt_ssy(0, TSEVT_SUSPEND_DONE, 0, 0);
 1022:         ERCD_PRINT(("4. do_suspend -> tk_evt_ssy ercd = %d\n", ercd));
 1023: 
 1024:         /* Transit to suspend state */
 1025:         ercd = tk_set_pow(TPW_DOSUSPEND);
 1026:         ERCD_PRINT(("5. do_suspend -> tk_set_pow ercd = %d\n", ercd));
 1027: 
 1028:         /* Return from suspend state */
 1029: 
 1030:         /* Processing before starting subsystem resume */
 1031:         ercd = tk_evt_ssy(0, TSEVT_RESUME_BEGIN, 0, 0);
 1032:         ERCD_PRINT(("6. do_suspend -> tk_evt_ssy ercd = %d\n", ercd));
 1033: 
 1034:         /* Resume accepting requests */
 1035:         UnlockDM();
 1036: 
 1037:         /* Resume processing of disk device */
 1038:         ercd = sendevt_alldevice(TDV_RESUME, TRUE);
 1039:         ERCD_PRINT(("7. do_suspend -> sendevt_alldevice ercd = %d\n", ercd));
 1040: 
 1041:         /* Resume processing of device except for disks */
 1042:         ercd = sendevt_alldevice(TDV_RESUME, FALSE);
 1043:         ERCD_PRINT(("8. do_suspend -> sendevt_alldevice ercd = %d\n", ercd));
 1044: 
 1045:         /* Resume accepting device registration/unregistration */
 1046:         UnlockREG();
 1047: 
 1048:         /* Processing after completion of subsystem resume */
 1049:         ercd = tk_evt_ssy(0, TSEVT_RESUME_DONE, 0, 0);
 1050:         ERCD_PRINT(("9. do_suspend -> tk_evt_ssy ercd = %d\n", ercd));
 1051: 
 1052:         return ercd;
 1053: }
 1054: 
 1055: /*
 1056:  * Suspend processing
 1057:  */
 1058: EXPORT INT _tk_sus_dev( UINT mode )
 1059: {
 1060:         ResCB  *rescb;
 1061:         BOOL   suspend = FALSE;
 1062:         ER     ercd;
 1063: 
 1064:         /* Get resource management information */
 1065:         rescb = GetResCB(DEVICE_SVC, TSK_SELF);
 1066:         if ( rescb == NULL ) {
 1067:                 ercd = E_CTX;
 1068:                 goto err_ret1;
 1069:         }
 1070: 
 1071:         LockDM();
 1072: 
 1073:         switch ( mode & 0xf ) {
 1074:           case TD_SUSPEND:     /* Suspend */
 1075:                 if ( DisSusCnt > 0 && (mode & TD_FORCE) == 0 ) {
 1076:                         ercd = E_BUSY;
 1077:                         goto err_ret2;
 1078:                 }
 1079:                 suspend = TRUE;
 1080:                 break;
 1081: 
 1082:           case TD_DISSUS:      /* Disable suspend */
 1083:                 if ( DisSusCnt >= MAX_DISSUS ) {
 1084:                         ercd = E_QOVR;
 1085:                         goto err_ret2;
 1086:                 }
 1087:                 DisSusCnt++;
 1088:                 rescb->dissus++;
 1089:                 break;
 1090: 
 1091:           case TD_ENASUS:      /* Enable suspend */
 1092:                 if ( rescb->dissus > 0 ) {
 1093:                         rescb->dissus--;
 1094:                         DisSusCnt--;
 1095:                 }
 1096:                 break;
 1097: 
 1098:           case TD_CHECK:       /* Get suspend disable request count */
 1099:                 break;
 1100: 
 1101:           default:
 1102:                 ercd = E_PAR;
 1103:                 goto err_ret2;
 1104:         }
 1105: 
 1106:         UnlockDM();
 1107: 
 1108:         if ( suspend ) {
 1109:                 /* Suspend */
 1110:                 ercd = do_suspend();
 1111:                 if ( ercd < E_OK ) {
 1112:                         goto err_ret1;
 1113:                 }
 1114:         }
 1115: 
 1116:         return DisSusCnt;
 1117: 
 1118: err_ret2:
 1119:         UnlockDM();
 1120: err_ret1:
 1121:         DEBUG_PRINT(("_tk_sus_dev ercd = %d\n", ercd));
 1122:         return ercd;
 1123: }
 1124: 
 1125: /* ------------------------------------------------------------------------ */
 1126: 
 1127: /*
 1128:  * Device management break function
 1129:  */
 1130: EXPORT void devmgr_break( ID tskid )
 1131: {
 1132:         ResCB  *rescb;
 1133:         OpnCB  *opncb;
 1134:         ReqCB  *reqcb;
 1135:         QUEUE  *q, *r;
 1136:         DEVREQ *devreq = NULL;
 1137:         INT    nreq;
 1138: 
 1139:         /* Get resource management block */
 1140:         rescb = GetResCB(DEVICE_SVC, tskid);
 1141:         if ( rescb == NULL ) {
 1142:                 return;
 1143:         }
 1144: 
 1145:         LockDM();
 1146: 
 1147:         /* Search the request if 'tskid' task is executing and request abort */
 1148:         nreq = 0;
 1149:         for ( q = rescb->openq.next; q != &rescb->openq; q = q->next ) {
 1150:                 opncb = RESQ_OPNCB(q);
 1151: 
 1152:                 if ( opncb->nwaireq > 0 ) {
 1153:                         /* Multiple requests wait */
 1154:                         reqcb = DEVREQ_REQCB(opncb->waireqlst);
 1155:                         if ( reqcb->tskid == tskid ) {
 1156:                                 devreq = opncb->waireqlst;
 1157:                                 nreq   = opncb->nwaireq;
 1158:                         }
 1159:                 } else {
 1160:                         /* Start request or single request wait */
 1161:                         for ( r = opncb->requestq.next;
 1162:                                         r != &opncb->requestq; r = r->next ) {
 1163:                                 reqcb = (ReqCB*)r;
 1164:                                 if ( reqcb->tskid == tskid ) {
 1165:                                         devreq = &reqcb->req;
 1166:                                         devreq->c.abort = TRUE;
 1167:                                         nreq = 1;
 1168:                                         break;
 1169:                                 }
 1170:                         }
 1171:                 }
 1172:                 if ( nreq > 0 ) {
 1173:                         LockDAbort();
 1174:                         break;
 1175:                 }
 1176:         }
 1177:         UnlockDM();
 1178:         if ( nreq > 0 ) {
 1179:                 /* Abort request: Device driver call */
 1180:                 call_abortfn(opncb->devcb, tskid, devreq, nreq);
 1181:                 UnlockDAbort();
 1182:         }
 1183: }
 1184: 
 1185: /*
 1186:  * Device management startup function
 1187:  */
 1188: EXPORT void devmgr_startup( ID resid, INT info )
 1189: {
 1190:         ResCB  *rescb;
 1191:         ER     ercd;
 1192: 
 1193:         ercd = tk_get_res(resid, DEVICE_SVC, (void**)&rescb);
 1194:         if ( ercd < E_OK ) {
 1195:                 goto err_ret;
 1196:         }
 1197: 
 1198:         LockDM();
 1199: 
 1200:         /* Initialization of open device management queue */
 1201:         QueInit(&rescb->openq);
 1202:         rescb->dissus = 0;
 1203: 
 1204:         UnlockDM();
 1205: 
 1206:         return;
 1207: 
 1208: err_ret:
 1209:         DEBUG_PRINT(("devmgr_cleanup ercd = %d\n", ercd));
 1210:         return;
 1211: }
 1212: 
 1213: /*
 1214:  * Device management cleanup function
 1215:  */
 1216: EXPORT void devmgr_cleanup( ID resid, INT info )
 1217: {
 1218:         ResCB  *rescb;
 1219:         OpnCB  *opncb;
 1220:         ER     ercd;
 1221: 
 1222:         ercd = tk_get_res(resid, DEVICE_SVC, (void**)&rescb);
 1223:         if ( ercd < E_OK ) {
 1224:                 goto err_ret;
 1225:         }
 1226: 
 1227:         /* Do nothing if it is not used even once */
 1228:         if ( rescb->openq.next == NULL ) {
 1229:                 return;
 1230:         }
 1231: 
 1232:         LockDM();
 1233: 
 1234:         /* Free suspend disable request */
 1235:         DisSusCnt -= rescb->dissus;
 1236:         rescb->dissus = 0;
 1237: 
 1238:         /* Close all open devices */
 1239:         while ( !isQueEmpty(&rescb->openq) ) {
 1240:                 opncb = RESQ_OPNCB(rescb->openq.next);
 1241: 
 1242:                 /* Indicate that it is during close processing */
 1243:                 opncb->resid = -1;
 1244: 
 1245:                 /* Device close processing */
 1246:                 UnlockDM();
 1247:                 close_device(opncb, 0);
 1248:                 LockDM();
 1249:         }
 1250:         UnlockDM();
 1251: 
 1252:         return;
 1253: 
 1254: err_ret:
 1255:         DEBUG_PRINT(("devmgr_cleanup ercd = %d\n", ercd));
 1256:         return;
 1257: }
 1258: 
 1259: /*
 1260:  * Initialization sequence of device input/output-related
 1261:  */
 1262: EXPORT ER initDevIO( void )
 1263: {
 1264:         INT    i;
 1265:         ER     ercd;
 1266: 
 1267:         i = _tk_get_cfn(SCTAG_TMAXOPNDEV, &MaxOpnDev, 1);
 1268:         if ( i < 1 ) {
 1269:                 ercd = E_SYS;
 1270:                 goto err_ret;
 1271:         }
 1272:         i = _tk_get_cfn(SCTAG_TMAXREQDEV, &MaxReqDev, 1);
 1273:         if ( i < 1 ) {
 1274:                 ercd = E_SYS;
 1275:                 goto err_ret;
 1276:         }
 1277: 
 1278:         /* Generate open management information table */
 1279:         OpnCBtbl = Imalloc((UINT)MaxOpnDev * sizeof(OpnCB));
 1280:         if ( OpnCBtbl == NULL ) {
 1281:                 ercd = E_NOMEM;
 1282:                 goto err_ret;
 1283:         }
 1284: 
 1285:         QueInit(&FreeOpnCB);
 1286:         for ( i = 0; i < MaxOpnDev; ++i ) {
 1287:                 OpnCBtbl[i].resid = 0;
 1288:                 QueInsert(&OpnCBtbl[i].q, &FreeOpnCB);
 1289:         }
 1290: 
 1291:         /* Generate request management information table */
 1292:         ReqCBtbl = Imalloc((UINT)MaxReqDev * sizeof(ReqCB));
 1293:         if ( ReqCBtbl == NULL ) {
 1294:                 ercd = E_NOMEM;
 1295:                 goto err_ret;
 1296:         }
 1297: 
 1298:         QueInit(&FreeReqCB);
 1299:         for ( i = 0; i < MaxReqDev; ++i ) {
 1300:                 ReqCBtbl[i].opncb = NULL;
 1301:                 QueInsert(&ReqCBtbl[i].q, &FreeReqCB);
 1302:         }
 1303: 
 1304:         return E_OK;
 1305: 
 1306: err_ret:
 1307:         DEBUG_PRINT(("initDevIO ercd = %d\n", ercd));
 1308:         return ercd;
 1309: }
 1310: 
 1311: /*
 1312:  * Finalization sequence of device input/output-related
 1313:  */
 1314: EXPORT ER finishDevIO( void )
 1315: {
 1316:         /* Delete each table */
 1317:         if ( OpnCBtbl != NULL ) {
 1318:                 Ifree(OpnCBtbl);
 1319:                 OpnCBtbl = NULL;
 1320:         }
 1321:         if ( ReqCBtbl != NULL ) {
 1322:                 Ifree(ReqCBtbl);
 1323:                 ReqCBtbl = NULL;
 1324:         }
 1325: 
 1326:         return E_OK;
 1327: }
 1328: