/*static char *SCCSID = "@(#)readq.c    6.6 91/11/07";*/
/****************************************************************************/
/*                                                                          */
/*                       IBM/Microsoft Confidential                         */
/*                                                                          */
/*                 Copyright (c) IBM Corporation  1987, 1990                */
/*                 Copyright (c) Microsoft Corp.  1987, 1990                */
/*                           All Rights Reserved                            */
/*                                                                          */
/****************************************************************************/

/*
 *      This code will implement DosReadQueue
 */

#include        <dos.h>
#include        <error.h>
#include        <error2.h>
#include        <doscalls.h>
#include        <infoseg.h>
#include        <ipcqueue.h>

unsigned far pascal w_DOSREADQUEUE(unsigned, struct LOCATION far *, unsigned long far *, unsigned long far *, unsigned, unsigned char, unsigned char far *, unsigned long, unsigned short far *);

/********************** START OF SPECIFICATIONS *********************/
/*                                                                  */
/* SOURCE FILE NAME: readq.c                                        */
/*                                                                  */
/* DESCRIPTIVE NAME: DosReadQueue                                   */
/*                                                                  */
/* STATUS: RELEASE 1 LEVEL 0                                        */
/*                                                                  */
/* FUNCTION: DosReadQueue will read an element from the specified   */
/*           queue.                                                 */
/*                                                                  */
/* NOTES:                                                           */
/*    DEPENDENCIES:                                                 */
/*    RESTRICTIONS:                                                 */
/*                                                                  */
/* ENTRY POINTS:        DosReadQueue                                */
/*                                                                  */
/* INTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*                      w_DosReadQueue:FAR                          */
/*                                                                  */
/* EXTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*             OPEN16EVENTSEM:FAR                                   */
/*             CLOSE16EVENTSEM:FAR                                  */
/*             DosSubFree:FAR                                       */
/*             queues_sys_trace:NEAR                                */
/*             FuncFree:NEAR                                        */
/*             FuncInit:NEAR                                        */
/*             Q_Empty:FAR                                          */
/*                                                                  */
/*********************** END OF SPECIFICATIONS **********************/

#if defined(MMIOPH)
extern void queues_sys_trace(
unsigned,                                       /* que_major_code */
unsigned,                                       /* int que_data_len */
unsigned,                                       /* que_minor_code */
char far *);                                    /* que_data */
#endif

/********************** START OF SPECIFICATIONS *********************/
/*                                                                  */
/* SUBROUTINE NAME: w_DosReadQueue                                  */
/*                                                                  */
/* DESCRIPTIVE NAME: DosReadQueue Worker                            */
/*                                                                  */
/* FUNCTION: Main routine for DosReadQueue                          */
/*                                                                  */
/* NOTES:                                                           */
/*                                                                  */
/* ENTRY POINT: w_DosReadQueue                                      */
/*    LINKAGE:  far at pl 2                                         */
/*                                                                  */
/* INPUT:                                                           */
/*      DW     Queue Handle            ;Handle of desired queue     */
/*      DW,DD  @Request                ;sender PID, appl. code      */
/*      DD     @Data Length            ;Length of packet            */
/*      DD     @Data Address           ;Address of Data (32 bit)    */
/*      DW     Element Code            ;non zero - specific element */
/*      DW     NoWait                  ;Wait or NoWait              */
/*      DB     @Element Priority       ;priority 0 - 15             */
/*      DD     Semaphore               ;Semaphore Handle            */
/*      DW     @flag                   ;flag to 32-bit app          */
/*                                                                  */
/* EXIT-NORMAL:                                                     */
/*             AX = 0                                               */
/*             Request                                              */
/*             Priority                                             */
/*             Data Address                                         */
/*             Data Length                                          */
/*                                                                  */
/* EXIT-ERROR:                                                      */
/*             AX != 0                                              */
/*             'CURRENT PROCESS DOES NOT OWN QUEUE'                 */
/*             'ELEMENT DOES NOT EXIST'                             */
/*             'INVALID QUEUE HANDLE'                               */
/*             'INVALID WAIT'                                       */
/*             'QUEUE IS EMPTY'                                     */
/*             'INVALID PARAMETER'                                  */
/*             'SYS ERROR'                                          */
/*                                                                  */
/* EFFECTS:    Deletes Element Structure from Queue's Element Chain */
/*                                                                  */
/* INTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*                                                                  */
/* EXTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*             OPEN16EVENTSEM:FAR                                   */
/*             CLOSE16EVENTSEM:FAR                                  */
/*             DosSubFree:FAR                                       */
/*             queues_sys_trace:NEAR                                */
/*             FuncFree:NEAR                                        */
/*             FuncInit:NEAR                                        */
/*             Q_Empty:FAR                                          */
/*                                                                  */
/*********************** END OF SPECIFICATIONS **********************/

/********************************************************************/
/*                                                                  */
/*w_DosReadQueue acts as the main line control for                  */
/*DosReadQueue.  It first checks for proper initialization, then    */
/*attempts to locate the Queue entry.  If the entry can be found it */
/*compares the Queue PID with the current PID to insure legal access*/
/*If the queue is empty and nowait has been specified, an error will*/
/*result; if empty and wait specified the thread will be put to     */
/*sleep.  When a valid Read exists an attempt will be made to       */
/*locate the proper Element.  If this happens the Element will be   */
/*removed from the Queue's Element chain, and the return parameters */
/*will be filled.                                                   */
/*                                                                  */
/********************************************************************/
/*                                                                  */
/* w_DosReadQueue:                                                  */
/*                                                                  */
/*      initialize error        'NO ERROR'                          */
/*      if (tracing active)                                         */
/*        call queues_sys_trace                                     */
/*      endif                                                       */
/*      call FuncInit                                               */
/*      if (FuncInit returns in error)                              */
/*        set error             'SYS ERROR'                         */
/*      else                                                        */
/*        if (NoWait out of range)                                  */
/*          set error           'INVALID WAIT'                      */
/*        else                                                      */
/*          initialize q_offset to statseg.fst_q                    */
/*          if (statseg.fst_q != NULL)                              */
/*            while (queue handle not found and not end of chain)   */
/*              q_offset = q_offset.q_chain                         */
/*            endwhile                                              */
/*            if (q_offset not null)                                */
/*              load variables from queue structure                 */
/*            endif                                                 */
/*          endif                                                   */
/*          if (q_offset null or queue handle not found)            */
/*            set error         'INVALID HANDLE'                    */
/*          else                                                    */
/*            if (current pid != queue pid)                         */
/*              set error       'PROC NOT OWNED'                    */
/*            else                                                  */
/*              if NoWait and 32-bit call                           */
/*                if invalid semaphore                              */
/*                  set error code  'INVALID PARAMETER'             */
/*                else if first no wait call                        */
/*                  open semaphore                                  */
/*                  if open unsuccessful                            */
/*                    set error code  'INVALID PARAMETER'           */
/*                  else                                            */
/*                    close semaphore                               */
/*                  endif                                           */
/*                endif                                             */
/*              endif                                               */
/*              if (NO ERROR and queue count == null)               */
/*                call Q_Empty                                      */
/*              endif                                               */
/*              if (NO ERROR)                                       */
/*                e_offset = q_offset.q_fst                         */
/*                obtain q_lst                                      */
/*                if (e_offset != NULL)                             */
/*                  if (E_Code != NULL)                             */
/*                    while E_Code not found and e_offset != NULL)  */
/*                      e_offset = e_offset.e_next                  */
/*                    endwhile                                      */
/*                  elseif (queue uses LIFO priority)               */
/*                    e_offset = q_lst                              */
/*                  endif                                           */
/*                endif                                             */
/*                if (e_offset == NULL)                             */
/*                  set error   'ELEMENT NOT EXIST'                 */
/*                else                                              */
/*                  if (minor tracing active)                       */
/*                    call queues_sys_trace                         */
/*                  endif                                           */
/*                  load variables from element structure           */
/*                  if (32-bit element or thunking desired)         */
/*                    set flag to 32-bit thunk routine              */
/*                  endif                                           */
/*                  initialize exitlist action variables            */
/*                  set exitlist action flag for appropriate        */
/*                    form of element delete                        */
/*                  unlink e_offset from element chain              */
/*                  decrement q_offset.q_cnt                        */
/*                  set output variables                            */
/*                  call DosSubFree                                 */
/*                  if (DosSubFree returns in error)                */
/*                    set error 'SYS INTERNAL'                      */
/*                  set exitlist action off                         */
/*                  endif                                           */
/*                endif                                             */
/*              endif                                               */
/*            endif                                                 */
/*          endif                                                   */
/*        endif                                                     */
/*        call FuncFree                                             */
/*        if (FuncFree returns in error) then                       */
/*          set error code                'SYS ERROR'               */
/*        endif                                                     */
/*      endif                                                       */
/*      if (tracing active)                                         */
/*        call queues_sys_trace                                     */
/*      endif                                                       */
/*      return                                                      */

/*********/
extern unsigned far OPEN16EVENTSEM(unsigned long);
extern unsigned far CLOSE16EVENTSEM(unsigned long);
/*********/

unsigned far pascal w_DOSREADQUEUE(QHnd,Request,E_Length,E_Addr,E_Code,No_Wait,E_Prio,SemHnd,flag)
register unsigned QHnd;
struct LOCATION far     *Request;
unsigned long   far     *E_Length;
unsigned long   far     *E_Addr;
unsigned E_Code;
unsigned char   No_Wait;
unsigned char   far     *E_Prio;
unsigned long   SemHnd;
unsigned short  far     *flag;
{
        extern  struct  InfoSegGDT far  *globptr;
        extern  struct  STATDATA        statseg;
        extern  unsigned char   action;
        extern  unsigned count;
        extern  struct Q_ENTRY far *que1;
        extern  struct E_ENTRY far *emt1;
        extern  struct E_ENTRY far *emt2;
        extern  struct E_ENTRY far *emt3;

        struct  Q_ENTRY far *q_offset;
        struct  Q_ENTRY far *q_prev;
        struct  E_ENTRY far *e_offset;
        struct  E_ENTRY far     *enext;
        struct  E_ENTRY far     *eprev;
        struct  E_ENTRY far     *rq_lst;
        struct  LOCATION emt_req;
        unsigned int    rq_hnd;
        unsigned int    rq_pid;
        unsigned int    rq_cnt;
        unsigned int    rq_flags;
        unsigned long   remt_addr;
        unsigned long   remt_len;
        unsigned char   remt_prio;
        unsigned int    rq_prio;
        register unsigned int rc;
#if defined(MMIOPH)
        unsigned int    trace_cnt;
        struct RAS_PRE_READ {
                unsigned long rp_hnd;
                unsigned long rp_ecode;
                unsigned long rp_sem;
                unsigned long rp_nowait;
        } ras_pre_read;
        struct RAS_READ_EVENT {
                unsigned long re_len;
                long re_addr;
                unsigned re_ecode;
        } ras_read_event;
        struct RAS_POST_READ {
                unsigned long rp_rc;
                struct LOCATION_MMIOPH rp_req;
                unsigned long rp_addr;
                unsigned long rp_len;
                unsigned char rp_prio;
        } ras_post_read;
#endif

        PUSHALL_DS();
        rc = NO_ERROR;
        #if (cdbug)
        krnldbug();
        #endif

/***DT DosReaQueue - Pre-Invocation Dynamic Trace
 *     TRACE MINOR =0x0007,
 *           TP=@
 *           TYPE  =(PRE,API),
 *           DESC  ="(OS) DosReadQueue Pre-Invocation",
 *           FMT   ="Handle=%pw  Element Code=%pw",
 *           FMT   ="Semaphore Handle=%pd  No Wait Flag=%pb",
 *           MEM   =(.QHnd,d,2),
 *           MEM   =(.E_Code,d,2),
 *           MEM   =(.SemHnd,d,4),
 *           MEM   =(.No_Wait,d,1)
 */

#if defined(MMIOPH)
        trace_cnt = (*globptr).mec_table[17];
        if ((trace_cnt & 0x80) != NULL)
        {
                ras_pre_read.rp_hnd = (unsigned long)QHnd;
                ras_pre_read.rp_ecode = (unsigned long)E_Code;
                ras_pre_read.rp_nowait = (unsigned long)No_Wait;
                ras_pre_read.rp_sem = (unsigned long)SemHnd;
                queues_sys_trace(((unsigned) 0x88),sizeof(struct RAS_PRE_READ),((unsigned) 7),(char far *) &ras_pre_read);
        }
#endif

        if ((FuncInit()) != NO_ERROR)
        {
                rc = ERROR_SYS_INTERNAL;
        }
        else
        {
                if (No_Wait > NO_WAIT)
                        rc = ERROR_QUE_INVALID_WAIT;
                else
                {
                        q_offset = statseg.fst_q;
                        if (statseg.fst_q != NULL)
                        {
                                while (((*q_offset).q_hndl != QHnd) && ((*q_offset).q_chain != NULL))
                                {
                                        q_offset = (*q_offset).q_chain;
                                }
                                if (q_offset != NULL)
                                {
                                        rq_prio = (*q_offset).q_prio;
                                        rq_hnd = (*q_offset).q_hndl;
                                        rq_pid = (*q_offset).q_pid;
                                        rq_cnt = (*q_offset).q_cnt;
                                        rq_flags = (*q_offset).q_flags;
                                }
                        }
                        if ((q_offset == NULL) || (rq_hnd != QHnd))
                                rc = ERROR_QUE_INVALID_HANDLE;
                        else
                        {
                                if (rq_pid != statseg.st_pid)
                                        rc = ERROR_QUE_PROC_NOT_OWNED;
                                else
                                {
                                        if ((No_Wait == NO_WAIT) && ((*q_offset).q_flags & Q_BIT32))
                                        {
                                                if (SemHnd == NULL)
                                                {
                                                        rc = ERROR_INVALID_PARAMETER;
                                                }
                                                else if ((*q_offset).q_semhnd == NULL)
                                                {
                                                        if (OPEN16EVENTSEM(SemHnd) != NO_ERROR)
                                                        {
                                                                rc = ERROR_INVALID_PARAMETER;
                                                        }
                                                        else
                                                        {
                                                                CLOSE16EVENTSEM(SemHnd);      /* Defect 65343 D.B. */
                                                                (*q_offset).q_semhnd = SemHnd;
                                                        }
                                                }
                                                else if ((*q_offset).q_semhnd != SemHnd)
                                                {
                                                        rc = ERROR_INVALID_PARAMETER;
                                                }
                                        }
                                        if (rc == NO_ERROR && rq_cnt == NULL)
                                                rc = Q_Empty(No_Wait,SemHnd,q_offset);
                                        if (rc == NO_ERROR)
                                        {
                                                e_offset = (*q_offset).q_fst;
                                                rq_lst = (*q_offset).q_lst;
                                                if (e_offset != NULL)
                                                {
                                                        if (E_Code != NULL)
                                                        {
                                                                while ((E_Code != FP_OFF(e_offset)) && (e_offset != NULL))
                                                                {
                                                                        e_offset = (*e_offset).e_next;
                                                                }
                                                        }
                                                        else if (rq_prio == LIFO_PRIO)
                                                        {
                                                                e_offset = rq_lst;
                                                        }
                                                }
                                                if (e_offset == NULL)
                                                        rc = ERROR_QUE_ELEMENT_NOT_EXIST;
                                                else
                                                {
                                                     #if defined(MMIOPH)
                                                        if ((trace_cnt & 0x40) != NULL)
                                                        {
                                                                ras_read_event.re_len = (*e_offset).e_len;
                                                                ras_read_event.re_addr = (unsigned long) (*e_offset).e_addr;
                                                                ras_read_event.re_ecode = FP_OFF(e_offset);
                                                                queues_sys_trace(((unsigned) 0x89),sizeof(struct RAS_READ_EVENT),
                                                                        ((unsigned) 2),(char far *) &ras_read_event);
                                                        }
                                                     #endif
                                                        remt_len = (*e_offset).e_len;
                                                        remt_prio = (*e_offset).e_prio;
                                                        remt_addr = (unsigned long) (*e_offset).e_addr;

                                                        if (((*e_offset).e_flags & E_BIT32) ||
                                                            ((*q_offset).q_flags & Q_THUNK))
                                                        {
                                                                *flag = CONVERT_ADDR;
                                                        }

/***DT DosReadQueue - Internal Dynamic Trace
 *     TRACE MINOR =0x000A,
 *           TP=@
 *           TYPE  =(INT),
 *           DESC  ="(OS) Read Data Packet From Queue",
 *           FMT   ="Length=%pd  Address=%pa  Element Code=%pw",
 *           MEM   =(.remt_len,d,4),
 *           MEM   =(.remt_addr,d,4),
 *           MEM   =(.e_offset,d,2)
 */

                                                        emt_req.process_id = (*e_offset).e_spid;
                                                        emt_req.request_data = (*e_offset).e_apfld;
                                                        que1 = q_offset;
                                                        count = rq_cnt;
                                                        emt1 = e_offset;
                                                        emt2 = (*e_offset).e_prev;
                                                        emt3 = (*e_offset).e_next;
                                                        if ((emt2 ==  NULL) && (emt3 == NULL))
                                                        {
                                                                action = ACT_DEL_ONLY_EMT;
                                                                (*q_offset).q_fst = NULL;
                                                                (*q_offset).q_lst = NULL;
                                                        }
                                                        else if (emt2 == NULL)
                                                        {
                                                                action = ACT_DEL_FST_EMT;
                                                                (*q_offset).q_fst = emt3;
                                                                (*emt3).e_prev = NULL;
                                                        }
                                                        else if (emt3 ==  NULL)
                                                        {
                                                                action = ACT_DEL_LST_EMT;
                                                                (*q_offset).q_lst = emt2;
                                                                (*emt2).e_next = NULL;
                                                        }
                                                        else
                                                        {
                                                                action = ACT_DEL_MID_EMT;
                                                                (*emt2).e_next = emt3;
                                                                (*emt3).e_prev = emt2;
                                                        }
                                                        (*q_offset).q_cnt--;
                                                        if ((DOSSUBFREE(FP_SEG(e_offset),FP_OFF(e_offset),sizeof(struct E_ENTRY))) != NO_ERROR)
                                                                rc = ERROR_SYS_INTERNAL;
                                                        action = 0;
                                                        (*Request).process_id = emt_req.process_id;
                                                        (*Request).request_data = emt_req.request_data;
                                                        *E_Length = remt_len;
                                                        *E_Prio = (unsigned char)remt_prio;
                                                        *E_Addr = remt_addr;
                                                }
                                        }
                                }
                        }
                }
                if ((FuncFree() != NO_ERROR) && (rc == NO_ERROR))
                        rc = ERROR_SYS_INTERNAL;
        }

/***DT DosReadQueue - Post-Invocation Dynamic Trace
 *     TRACE MINOR =0x8007,
 *           TP=@
 *           TYPE  =(POST,API),
 *           DESC  ="(OS) DosReadQueue Post-Invocation",
 *           FMT   ="Return Code=%pw  Request=%pw%d  Address=%pa  Length=%pd",
 *           FMT   ="Priority=%pb"
 *           MEM   =(.rc,d,2),
 *           MEM   =(.Request,i,6),
 *           MEM   =(.E_Addr,i,4),
 *           MEM   =(.E_Length,i,4),
 *           MEM   =(.E_Prio,i,1)
 */

#if defined(MMIOPH)
        if ((trace_cnt & 0x80) != NULL)
        {
                ras_post_read.rp_rc = (unsigned long)rc;
                ras_post_read.rp_req.process_id = (unsigned long)
                                                  ((*Request).process_id);
                ras_post_read.rp_req.request_data = (*Request).request_data;
                ras_post_read.rp_addr = *E_Addr;
                ras_post_read.rp_len = *E_Length;
                ras_post_read.rp_prio = (unsigned char)*E_Prio;
                queues_sys_trace(((unsigned) 0x88),sizeof(struct RAS_POST_READ),((unsigned) 0x87),(char far *) &ras_post_read);
        }
#endif

        return(POPALL(rc));
}

/********************** START OF SPECIFICATIONS *********************/
/*                                                                  */
/* SUBROUTINE NAME: DosReadQueue                                    */
/*                                                                  */
/* DESCRIPTIVE NAME: DosReadQueue                                   */
/*                                                                  */
/* FUNCTION: Parent routine for 16 bit DosReadQueue                 */
/*                                                                  */
/* NOTES:                                                           */
/*                                                                  */
/* ENTRY POINT: DosReadQueue                                        */
/*    LINKAGE:  far at pl 2                                         */
/*                                                                  */
/* INPUT:                                                           */
/*      DW     Queue Handle            ;Handle of desired queue     */
/*      DD     @Request                ;sender PID, appl. code      */
/*      DD     @Data Length            ;Length of packet            */
/*      DW     @Data Address           ;Address of Data (32 bit)    */
/*      DW     Element Code            ;non zero - specific element */
/*      DW     NoWait                  ;Wait or NoWait              */
/*      DD     @Element Priority       ;priority 0 - 15             */
/*      DD     Semaphore               ;Semaphore Handle            */
/*                                                                  */
/* EXIT-NORMAL:                                                     */
/*             AX = 0                                               */
/*             Request                                              */
/*             Priority                                             */
/*             Data Address                                         */
/*             Data Length                                          */
/*                                                                  */
/* EXIT-ERROR:                                                      */
/*             AX != 0                                              */
/*             'CURRENT PROCESS DOES NOT OWN QUEUE'                 */
/*             'ELEMENT DOES NOT EXIST'                             */
/*             'INVALID QUEUE HANDLE'                               */
/*             'INVALID WAIT'                                       */
/*             'QUEUE IS EMPTY'                                     */
/*             'SYS ERROR'                                          */
/*                                                                  */
/* EFFECTS:    Deletes Element Structure from Queue's Element Chain */
/*                                                                  */
/* INTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*             w_DosReadQueue                                       */
/*                                                                  */
/* EXTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*                                                                  */
/*********************** END OF SPECIFICATIONS **********************/

/********************************************************************/
/*                                                                  */
/* DosReadQueue:                                                    */
/*                                                                  */
/*      call w_DosReadQueue                                         */
/*      return                                                      */

unsigned far pascal
DOSREADQUEUE(QHnd,Request,E_Length,E_Addr,E_Code,No_Wait,E_Prio,SemHnd)
unsigned QHnd;
unsigned long   far     *Request;
unsigned far    *E_Length;
unsigned long   far     *E_Addr;
unsigned E_Code;
unsigned char   No_Wait;
unsigned char   far     *E_Prio;
unsigned long   SemHnd;
{
unsigned rc;
unsigned long element_length;
struct LOCATION request_field;
PUSHALL_DS();
if (Request != NULL) {
  request_field.process_id=*(unsigned far *)Request;
  request_field.request_data=*(((unsigned far *)(Request))+1);
  }
if (E_Length != NULL) {
  element_length = *E_Length;
  }
rc = w_DOSREADQUEUE(QHnd,(struct LOCATION far *)&request_field,(unsigned long far *)&element_length,E_Addr,E_Code,No_Wait,(unsigned char far *)E_Prio,SemHnd,(unsigned short far *)NULL);
if (rc == 0) {
  if (Request != NULL) {
    *(unsigned far *)Request=request_field.process_id;
    *(((unsigned far *)(Request))+1)=(unsigned)request_field.request_data;
    }
  if (E_Length != NULL) {
    *E_Length = (unsigned)element_length;
    }
  }
return(POPALL(rc));
}
