/*static char *SCCSID = "@(#)writeq.c   6.3 91/03/29";*/
/****************************************************************************/
/*                                                                          */
/*                       IBM/Microsoft Confidential                         */
/*                                                                          */
/*                 Copyright (c) IBM Corporation  1987, 1990                */
/*                 Copyright (c) Microsoft Corp.  1987, 1990                */
/*                           All Rights Reserved                            */
/*                                                                          */
/****************************************************************************/

/*
 *      This code will implement DosWriteQueue
 */

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

unsigned pascal far w_DOSWRITEQUEUE(unsigned, unsigned long, unsigned long, unsigned char far *, unsigned char, unsigned short);

/********************** START OF SPECIFICATIONS *********************/
/*                                                                  */
/* SOURCE FILE NAME: writeq.c                                       */
/*                                                                  */
/* DESCRIPTIVE NAME: DosWriteQueue                                  */
/*                                                                  */
/* STATUS: RELEASE 1 LEVEL 0                                        */
/*                                                                  */
/* FUNCTION: DosWriteQueue will add an element to the specified     */
/*           queue.                                                 */
/*                                                                  */
/* NOTES:                                                           */
/*    DEPENDENCIES:                                                 */
/*    RESTRICTIONS:                                                 */
/*                                                                  */
/* ENTRY POINTS:        DosWriteQueue                               */
/*                                                                  */
/* INTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*                      w_DosWriteQueue                             */
/*                                                                  */
/* EXTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*                      Open16EventSem:FAR                          */
/*                      queues_sys_trace:NEAR                       */
/*                      FindMem:NEAR                                */
/*                      FuncFree:NEAR                               */
/*                      FuncInit:NEAR                               */
/*                                                                  */
/*                   Modification Log                               */
/*  Date   Name   Description                                       */
/* 4/4/10  ChangeTeam Added check for ERROR_TOO_MANY_POSTS          */
/*                    Defect : 118139                               */
/*********************** 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_DosWriteQueue                                 */
/*                                                                  */
/* DESCRIPTIVE NAME: DosWriteQueue Worker                           */
/*                                                                  */
/* FUNCTION: Main routine for DosWriteQueue                         */
/*                                                                  */
/* NOTES:                                                           */
/*                                                                  */
/* ENTRY POINT: DosWriteQueue                                       */
/*    LINKAGE:  far at pl 2                                         */
/*                                                                  */
/* INPUT:                                                           */
/*              DW     QHandle         ;Queue handle                */
/*              DD     Request         ;Request ID data             */
/*              DD     Data Length     ;length of element           */
/*              other  @Data Address   ;address of element          */
/*              DB     ElemPriority    ;priority of element         */
/*              DW     flag            ;flag from 32-bit app        */
/*                                                                  */
/* EXIT-NORMAL:                                                     */
/*             AX = 0                                               */
/*             RWHandle                                             */
/*                                                                  */
/* EXIT-ERROR:                                                      */
/*             AX != 0                                              */
/*             'INSUFFICIENT MEMORY'                                */
/*             'INVALID QUEUE HANDLE'                               */
/*             'SYS ERROR'                                          */
/*             'PROCESS NO ACCESS'                                  */
/*                                                                  */
/* EFFECTS:                                                         */
/*                                                                  */
/* INTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*                                                                  */
/* EXTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*             DosSemClear:FAR                                      */
/*             queues_sys_trace:NEAR                                */
/*             FindMem:NEAR                                         */
/*             FuncFree:NEAR                                        */
/*             FuncInit:NEAR                                        */
/*             Open16EventSem:FAR                                   */
/*             Post16EventSem:FAR                                   */
/*                                                                  */
/*********************** END OF SPECIFICATIONS **********************/

/********************************************************************/
/*                                                                  */
/*w_DosWriteQueue acts as the main line control for                 */
/*DosWriteQueue.  It first checks for proper initialization, then   */
/*attempts to create an Element entry. If the Element can be created*/
/*it will be inserted in the Queue's Element chain according to the */
/*proper priority method.  If the owning process had been put to    */
/*sleep because of an empty queue situation, it will be awakened.   */
/*                                                                  */
/********************************************************************/
/*                                                                  */
/* w_DosWriteQueue:                                                 */
/*                                                                  */
/*      initialize error        'NO_ERROR'                          */
/*      if (tracing active)                                         */
/*        call queues_sys_trace                                     */
/*      endif                                                       */
/*      call FuncInit                                               */
/*      if (FuncInit returns in error) then                         */
/*        set error code        'SYS ERROR'                         */
/*      else                                                        */
/*        q_offset = statseg.fst_q                                  */
/*        if (statseg.fst_q not NULL)                               */
/*          while (queue handle not found and not end of chain)     */
/*            q_offset = q_offset.q_chain                           */
/*          endwhile                                                */
/*          if (q_offset not NULL)                                  */
/*            rq_fst = q_offset.q_fst                               */
/*            rq_lst = q_offset.q_lst                               */
/*            obtain other 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)                           */
/*            l_offset = statseg.fst_link                           */
/*            while (l_offset not NULL and (current pid not found   */
/*                   or queue handle not found)                     */
/*              l_offset = l_offset.l_chain                         */
/*            endwhile                                              */
/*            if (link not found)                                   */
/*              set error       'INVALID HANDLE'                    */
/*            endif                                                 */
/*          endif                                                   */
/*          if (NO ERROR)                                           */
/*            call FindMem                                          */
/*            if (FindMem returns in error)                         */
/*              set error       'NO MEMORY'                         */
/*            else                                                  */
/*              initialize element structure e_offset               */
/*              if (minor tracing active)                           */
/*                call queues_sys_trace                             */
/*              endif                                               */
/*              initialize common exitlist action variables         */
/*              if (queue was empty)                                */
/*                initialize exitlist action variables              */
/*                set exitlist action flag = ACT_ADD_ONLY_EMT       */
/*                q_offset.q_fst = e_offset                         */
/*                q_offset.q_lst = e_offset                         */
/*                if called from 16-bit code                        */
/*                  perform DosSemClear on the wait semaphore       */
/*                else   (called from 32-bit code)                  */
/*                  if wait semaphore not yet opened                */
/*                    call Open16EventSem to perform                */
/*                         Dos32OpenEventSem on the wait semaphore  */
/*                  endif                                           */
/*                  if Open16EventSem successful                    */
/*                    call Post16EventSem to perform                */
/*                         Dos32PostEventSem on the wait semaphore  */
/*                  else                                            */
/*                    set error 'ERROR_SYS_INTERNAL'                */
/*                  endif                                           */
/*                endif                                             */
/*                if (DosSemClear returns in error) then            */
/*                  set error   'ERROR_SYS_INTERNAL'                */
/*              elseif (LIFO or FIFO                                */
/*                initialize exitlist action variables              */
/*                set exitlist action flag = ACT_ADD_LST_EMT        */
/*                q_offset.q_lst = e_offset                         */
/*                rq_lst.e_next = e_offset                          */
/*                e_offset.e_prev = rq_lst                          */
/*              else                                                */
/*                in_prio = e_offset.e_prio                         */
/*                if rq_fst.e_prio = < in_prio                      */
/*                  initialize exitlist action variables            */
/*                  set exitlist action flag = ACT_ADD_FST_EMT      */
/*                  q_offset.q_fst = e_offset                       */
/*                  rq_fst = e_offset                               */
/*                  e_offset.e_next = rq_fst                        */
/*                else                                              */
/*                  emt_chn = rq_fst                                */
/*                  repeat                                          */
/*                    emt_chn = emt_chn.e_next                      */
/*                  until (end of chain reached or                  */
/*                         emt_chn.e_prio < in_prio)                */
/*                  if (emt_chn != NULL                             */
/*                    initialize exitlist action variables          */
/*                    set exitlist action flag = ACT_ADD_MID_EMT    */
/*                    echain = emt_chn.e_prev                       */
/*                    emt_chn.e_prev = e_offset                     */
/*                    echain.e_next = e_offset                      */
/*                    e_offset.e_next = emt_chn                     */
/*                    e_offset.e_prev = echain                      */
/*                  else                                            */
/*                    initialize exitlist action variables          */
/*                    set exitlist action flag = ACT_ADD_LST_EMT    */
/*                    q_offset.q_lst = e_offset                     */
/*                    rq_lst.e_next = e_offset                      */
/*                    e_offset.e_prev = rq_lst                      */
/*                  endif                                           */
/*                endif                                             */
/*              endif                                               */
/*              increment q_offset.q_cnt                            */
/*              set exitlist action flag off                        */
/*            endif                                                 */
/*          endif                                                   */
/*        endif                                                     */
/*        call FuncFree                                             */
/*        if (FuncFree returns error and error is not set)          */
/*          set error           'SYS INTERNAL'                      */
/*        endif                                                     */
/*      if (tracing active)                                         */
/*        call queues_sys_trace                                     */
/*      endif                                                       */
/*      return                                                      */

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

        struct  E_ENTRY far *e_offset = 0;

unsigned pascal far w_DOSWRITEQUEUE(QHnd,Request,E_Length,E_Addr,E_Prio,flag)
register unsigned QHnd;
unsigned long Request;
unsigned long E_Length;
unsigned char    far     *E_Addr;
unsigned char    E_Prio;
unsigned short   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  L_ENTRY far *l_offset;
        struct  E_ENTRY far     *echain;
        struct  E_ENTRY far     *emt_chn;
        unsigned char  in_prio;
        register unsigned int rc;
        struct E_ENTRY far *rq_fst;
        struct E_ENTRY far *rq_lst;
        unsigned int rq_prio;
        unsigned int rq_pid;
        unsigned int rq_cnt;
        unsigned char rq_flags;
        unsigned int  rq_hndl;
        unsigned long rq_semhnd;
        unsigned long rq_ramsem;
        unsigned dt_ecode;
#if defined(MMIOPH)
        unsigned int trace_cnt;
        struct RAS_PRE_WRITE {
                unsigned long rp_hnd;
                unsigned long rp_req;
                unsigned long rp_len;
                char far *rp_addr;
                unsigned char rp_prio;
        } ras_pre_write;
        struct RAS_WRITE_EVENT {
                unsigned long re_len;
                char far *re_addr;
                unsigned re_ecode;
        } ras_write_event;
        struct RAS_POST_WRITE {
                unsigned long rp_rc;
        } ras_post_write;
#endif

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

/***DT DosWriteQueue - Pre-Invocation Dynamic Trace
 *     TRACE MINOR =0x0008,
 *           TP=@
 *           TYPE  =(PRE,API),
 *           DESC  ="(OS) DosWriteQueue Pre-Invocation",
 *           FMT   ="Handle=%pw  Request=%pd  Length=%pd ",
 *           FMT   ="Address=%pa  Priority=%pb",
 *           MEM   =(.QHnd,d,2),
 *           MEM   =(.Request,d,4),
 *           MEM   =(.E_Length,d,4),
 *           MEM   =(.E_Addr,d,4),
 *           MEM   =(.E_Prio,d,1)
 */

#if defined(MMIOPH)
        trace_cnt = (*globptr).mec_table[17];
        if ((trace_cnt & 0x80) != NULL)
        {
                ras_pre_write.rp_hnd = (unsigned long)QHnd;
                ras_pre_write.rp_req = (unsigned long)Request;
                ras_pre_write.rp_len = (unsigned long)E_Length;
                ras_pre_write.rp_addr = E_Addr;
                ras_pre_write.rp_prio = (unsigned char)E_Prio;
                queues_sys_trace(((unsigned) 0x88),sizeof(struct RAS_PRE_WRITE),((unsigned) 8),(char far *) &ras_pre_write);
        }
#endif

        if ((FuncInit()) != NO_ERROR)
                rc = ERROR_SYS_INTERNAL;
        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_fst = (*q_offset).q_fst;
                                rq_lst = (*q_offset).q_lst;
                                rq_prio = (*q_offset).q_prio;
                                rq_pid = (*q_offset).q_pid;
                                rq_cnt = (*q_offset).q_cnt;
                                rq_flags = (*q_offset).q_flags;
                                rq_hndl = (*q_offset).q_hndl;
                                rq_semhnd = (*q_offset).q_semhnd;
                                rq_ramsem = (unsigned long) &((*q_offset).q_ramsem);
                        }
                }
                if ((q_offset == NULL) || (rq_hndl != QHnd))
                        rc = ERROR_QUE_INVALID_HANDLE;
                else
                {
                        if (statseg.st_pid != rq_pid)
                        {
                                l_offset = statseg.fst_link;
                                while ((l_offset != NULL) &&
                                        (((*l_offset).l_pid != statseg.st_pid) ||
                                        ((*l_offset).l_qhndl != QHnd)))
                                {
                                        l_offset = (*l_offset).l_chain;
                                }
                                if (l_offset == NULL)
                                        rc = ERROR_QUE_INVALID_HANDLE;
                        }
                        if (rc == NO_ERROR)
                        {
                                if ((FindMem(sizeof(struct E_ENTRY),(unsigned char far * far *) &e_offset)) != NO_ERROR)
                                        rc = ERROR_QUE_NO_MEMORY;
                                else
                                {
                                        if (rq_prio == PRIO_PRIO)
                                        {
                                                if (E_Prio > HIGH_PRIO)
                                                /* modifies local copy of value */
                                                        E_Prio = HIGH_PRIO;
                                        }
                                        else
                                        {
                                                E_Prio = NULL;
                                        }
                                        (*e_offset).e_next = NULL;
                                        (*e_offset).e_prev = NULL;
                                        (*e_offset).e_flags = (unsigned char)flag;
                                        (*e_offset).e_apfld = Request;
                                        (*e_offset).e_len = E_Length;
                                        (*e_offset).e_spid = statseg.st_pid;
                                        (*e_offset).e_addr = E_Addr;
                                        (*e_offset).e_prio = E_Prio;
                                     #if defined(MMIOPH)
                                        if ((trace_cnt & 0x40) != NULL)
                                        {
                                                ras_write_event.re_len = E_Length;
                                                ras_write_event.re_addr = E_Addr;
                                                ras_write_event.re_ecode = FP_OFF(e_offset);
                                                queues_sys_trace(((unsigned) 0x89),sizeof(struct RAS_WRITE_EVENT),((unsigned) 1),
                                                        (char far *) &ras_write_event);
                                        }
                                     #endif

                                        dt_ecode = FP_OFF(e_offset);

/***DT DosWriteQueue - Internal Dynamic Trace
 *     TRACE MINOR =0x000B,
 *           TP=@
 *           TYPE  =(INT),
 *           DESC  ="(OS) Write Data Packet To Queue",
 *           FMT   ="Length=%pd  Address=%pa  Element Code=%pw",
 *           MEM   =(.E_Length,d,4),
 *           MEM   =(.E_Addr,d,4),
 *           MEM   =(.dt_ecode,d,2)
 */

                                        que1 = q_offset;
                                        emt1 = e_offset;
                                        count = rq_cnt;
                                        if (rq_cnt == 0)
                                        {
                                                action = ACT_ADD_ONLY_EMT;
                                                (*q_offset).q_fst = e_offset;
                                                (*q_offset).q_lst = e_offset;
                                                if (DOSSEMWAIT(rq_ramsem,0l)==NO_ERROR)   /*no one waiting, clear the wait sem*/
                                                {
                                                       if (rq_semhnd != 0)
                                                       {
                                                         /* if function called from 16-bit code */
                                                         if ((rq_flags & Q_BIT32) == 0)
                                                         {
                                                           if ((rc = DOSSEMCLEAR(rq_semhnd)) != NO_ERROR)
                                                           {
                                                             /* if semaphore had already been posted (cleared) */
                                                             if (rc == ERROR_ALREADY_POSTED || rc == ERROR_TOO_MANY_POSTS)
                                                             {
                                                               /* that is not an error */
                                                               rc = NO_ERROR;
                                                             }
                                                             else
                                                             {
                                                               rc = ERROR_SYS_INTERNAL;
                                                             } /* endif */
                                                           } /* endif */
                                                         } /* endif */
                                                         /* else function was called from 32-bit code */
                                                         else
                                                         {
                                                           if (statseg.st_pid != rq_pid && ((*l_offset).l_flags & S_OPENED) == 0)
                                                           {
                                                             rc = OPEN16EVENTSEM(rq_semhnd);
                                                             (*l_offset).l_flags |= S_OPENED;
                                                           }

                                                           if (rc == NO_ERROR)
                                                           {
                                                             /* call thunk to call Dos32PostEventSem */
                                                             /*if (POST16EVENTSEM(rq_semhnd) != NO_ERROR) */
                                                             rc = POST16EVENTSEM(rq_semhnd);
                                                             if (rc != NO_ERROR)
                                                             {
                                                                /* if semaphore had already been posted (cleared) */
                                                                /* if (rc == ERROR_ALREADY_POSTED)             D118139 - Commented out */
                                                                if ( ( rc == ERROR_ALREADY_POSTED ) || ( rc == ERROR_TOO_MANY_POSTS ) )  /* D118139 */
                                                                {
                                                                  /* that is not an error */
                                                                  rc = NO_ERROR;
                                                                }
                                                                else
                                                                {
                                                                  rc = ERROR_SYS_INTERNAL;
                                                                } /* endif */
                                                             }
                                                           }
                                                           else
                                                           {
                                                             rc = ERROR_SYS_INTERNAL;
                                                           }
                                                         } /* end elseif */
                                                       }
                                                }
                                                else
                                                {
                                                       if ((DOSSEMCLEAR(rq_ramsem)) != NO_ERROR)
                                                              rc = ERROR_SYS_INTERNAL;
                                                }
                                        }
                                        else if ((rq_prio == FIFO_PRIO) ||
                                            (rq_prio == LIFO_PRIO))
                                        {
                                                (*e_offset).e_prev = rq_lst;
                                                emt2 = rq_lst;
                                                action = ACT_ADD_LST_EMT;
                                                (*q_offset).q_lst = e_offset;
                                                (*rq_lst).e_next = e_offset;
                                        }
                                        else
                                        {
                                                in_prio = (*e_offset).e_prio;
                                                if ((*rq_fst).e_prio < in_prio)
                                                {
                                                        (*e_offset).e_next = rq_fst;
                                                        emt2 = rq_fst;
                                                        action = ACT_ADD_FST_EMT;
                                                        (*q_offset).q_fst = e_offset;
                                                        (*rq_fst).e_prev = e_offset;
                                                }
                                                else
                                                {
                                                        emt_chn = rq_fst;
                                                        do
                                                        {
                                                            emt_chn = (*emt_chn).e_next;
                                                        }
                                                        while ((emt_chn != NULL) &&
                                                           ((*emt_chn).e_prio >= in_prio));
                                                        if (emt_chn != NULL)
                                                        {
                                                                echain = (*emt_chn).e_prev;
                                                                (*e_offset).e_next = emt_chn;
                                                                (*e_offset).e_prev = echain;
                                                                emt2 = echain;
                                                                emt3 = emt_chn;
                                                                action = ACT_ADD_MID_EMT;
                                                                (*emt_chn).e_prev = e_offset;
                                                                (*echain).e_next = e_offset;
                                                        }
                                                        else
                                                        {
                                                                (*e_offset).e_prev = rq_lst;
                                                                emt2 = rq_lst;
                                                                action = ACT_ADD_LST_EMT;
                                                                (*q_offset).q_lst = e_offset;
                                                                (*rq_lst).e_next = e_offset;
                                                        }
                                                }
                                        }
                                        (*q_offset).q_cnt++;
                                        action = 0;
                                }
                        }
                }
                if ((FuncFree() != NO_ERROR) && (rc == NO_ERROR))
                        rc = ERROR_SYS_INTERNAL;
        }

/***DT DosWriteQueue - Post-Invocation Dynamic Trace
 *     TRACE MINOR =0x8008,
 *           TP=@
 *           TYPE  =(POST,API),
 *           DESC  ="(OS) DosWriteQueue Post-Invocation",
 *           FMT   ="Return Code=%pw",
 *           MEM   =(.rc,d,2)
 */

#if defined(MMIOPH)
        if ((trace_cnt & 0x80) != NULL)
        {
                ras_post_write.rp_rc = (unsigned long)rc;
                queues_sys_trace(((unsigned) 0x88),sizeof(struct RAS_POST_WRITE),((unsigned) 0x88),(char far *) &ras_post_write);
        }
#endif

        return(POPALL(rc));
}

/********************** START OF SPECIFICATIONS *********************/
/*                                                                  */
/* SUBROUTINE NAME: DosWriteQueue                                   */
/*                                                                  */
/* DESCRIPTIVE NAME: DosWriteQueue                                  */
/*                                                                  */
/* FUNCTION: Parent routine for 16 bit DosWriteQueue                */
/*                                                                  */
/* NOTES:                                                           */
/*                                                                  */
/* ENTRY POINT: DosWriteQueue                                       */
/*    LINKAGE:  far at pl 2                                         */
/*                                                                  */
/* INPUT:                                                           */
/*              DW     QHandle         ;Queue handle                */
/*              DW     Request         ;Request ID data             */
/*              DW     Data Length     ;length of element           */
/*              other  @Data Address   ;address of element          */
/*              DW     ElemPriority    ;priority of element         */
/*                                                                  */
/* EXIT-NORMAL:                                                     */
/*             AX = 0                                               */
/*             RWHandle                                             */
/*                                                                  */
/* EXIT-ERROR:                                                      */
/*             AX != 0                                              */
/*             'INSUFFICIENT MEMORY'                                */
/*             'INVALID QUEUE HANDLE'                               */
/*             'SYS ERROR'                                          */
/*                                                                  */
/* EFFECTS:                                                         */
/*                                                                  */
/* INTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*              w_DosWriteQueue                                     */
/*                                                                  */
/* EXTERNAL REFERENCES:                                             */
/*    ROUTINES:                                                     */
/*                                                                  */
/*********************** END OF SPECIFICATIONS **********************/

/********************************************************************/
/*                                                                  */
/* DosWriteQueue:                                                   */
/*                                                                  */
/*      call w_DOSWRITEQUEUE                                        */
/*      return                                                      */

        struct  E_ENTRY far *e_offset;

unsigned far pascal
DOSWRITEQUEUE(QHnd,Request,E_Length,E_Addr,E_Prio)
unsigned QHnd;
unsigned Request,E_Length;
unsigned char    far     *E_Addr;
unsigned char    E_Prio;
{
unsigned long element_length,request_data;
unsigned rc;
PUSHALL_DS();
element_length=E_Length;
request_data=Request;
rc=w_DOSWRITEQUEUE(QHnd,request_data,element_length,E_Addr,E_Prio,(unsigned short)0);
        return(POPALL(rc));
}
