text: /*++
  
  Copyright (c) 1991  Microsoft Corporation
  
  Module Name:
  
      s_putmsg.c
  
  Abstract:
  
      This module implements the STREAMS APIs, ioctl(I_FDINSERT) and
      putmsg().
  
  Author:
  
      Eric Chin (ericc)           July 26, 1991
  
  Revision History:
  
      Sam Patton (sampa)          August 13, 1991
                                  changed errno to {get|set}lasterror
  
  --*/
  #include "common.h"
  
  
  int
  putmsg(
      IN HANDLE           fd,
      IN struct strbuf   *ctrlptr OPTIONAL,
      IN struct strbuf   *dataptr OPTIONAL,
      IN int              flags
      )
  
  /*++
  
  Routine Description:
  
      This function is called to send a STREAMS message downstream.
  
      This function is synchronous, in the NT sense: it blocks until the API
      completes.
  
  Arguments:
  
      fd        - NT file handle
      ctrlptr   - pointer to the control portion of the STREAMS message
      dataptr   - pointer to the data portion of the STREAMS message
      flags     - 0 or RS_HIPRI
  
  Return Value:
  
      0 if successful, -1 otherwise.
  
  --*/
  
  {
      char *tmp;
      int chunksz;
      NTSTATUS status;
      IO_STATUS_BLOCK iosb;
      PSTRM_ARGS_OUT oparm;
      PPUTMSG_ARGS_IN chunk;
      int retval;
  
  
      //
      // marshall the arguments into one contiguous chunk.  However, for
      // commonality with ioctl(I_FDINSERT), we arrange the input arguments
      // as below:
      //
      //  typedef struct _PUTMSG_ARGS_IN_ {
      //      int             a_iocode;           //  0
      //      long            a_flags;            //  0 | RS_HIPRI
      //      struct strbuf   a_ctlbuf;           //  (required)
      //      struct strbuf   a_databuf;          //  (required)
      //      HANDLE          a_insert.i_fildes;  //  -1
      //      int             a_offset;           //  0
      //      char            a_stuff[1];         //  s_ctlbuf.buf  (optional)
      //                                          //  s_databuf.buf (optional)
      //  } PUTMSG_ARGS_IN, *PPUTMSG_ARGS_IN;
      //
      //
      chunksz = sizeof(PUTMSG_ARGS_IN) - 1 +
                  ((ctrlptr && (ctrlptr->len > 0)) ? ctrlptr->len : 0) +
                  ((dataptr && (dataptr->len > 0)) ? dataptr->len : 0);
  
      if (!(chunk = (PPUTMSG_ARGS_IN) LocalAlloc(LMEM_FIXED, chunksz))) {
          SetLastError(ENOSPC);
          return(-1);
      }
      chunk->a_iocode = 0;
      chunk->a_flags  = (long) flags;
  
      tmp = (char *) chunk->a_stuff;
  
      if (ctrlptr && ctrlptr->buf && (ctrlptr->len >= 0)) {
          chunk->a_ctlbuf = *ctrlptr;                         // structure copy
  
          memcpy(tmp, ctrlptr->buf, ctrlptr->len);
          tmp += ctrlptr->len;
      }
      else {
          chunk->a_ctlbuf.len = -1;
      }
  
      if (dataptr && dataptr->buf && (dataptr->len >= 0)) {
          chunk->a_databuf = *dataptr;                        // structure copy
  
          memcpy(tmp, dataptr->buf, dataptr->len);
          tmp += dataptr->len;
      }
      else {
          chunk->a_databuf.len = -1;
      }
      chunk->a_insert.i_fildes = INVALID_HANDLE_VALUE;
      chunk->a_offset          = 0;
  
      ASSERT(chunksz >= sizeof(STRM_ARGS_OUT));
  
      status = NtDeviceIoControlFile(
          fd,                                     // Handle
          NULL,                                   // Event
          NULL,                                   // ApcRoutine
          NULL,                                   // ApcContext
          &iosb,                                  // IoStatusBlock
          IOCTL_STREAMS_PUTMSG,                   // IoControlCode
          (PVOID) chunk,                          // InputBuffer
          chunksz,                                // InputBufferSize
          (PVOID) chunk,                          // OutputBuffer
          chunksz);                               // OutputBufferSize
  
      if (status == STATUS_PENDING) {
          status = NtWaitForSingleObject(
                      fd,                         // Handle
                      TRUE,                       // Alertable
                      NULL);                      // Timeout
      }
  
      if (!NT_SUCCESS(status)) {
          LocalFree(chunk);
          SetLastError(MapNtToPosixStatus(status));
          return(-1);
      }
  
      //
      // the return parameters from the Stream Head Driver are laid out as:
      //
      //  typedef struct _STRM_ARGS_OUT_ {        // generic return parameters
      //      int     a_retval;                   //  return value
      //      int     a_errno;                    //  errno if retval == -1
      //
      //  } STRM_ARGS_OUT, *PSTRM_ARGS_OUT;
      //
      oparm  = (PSTRM_ARGS_OUT) chunk;
  
      if (oparm->a_retval == -1) {
          SetLastError(oparm->a_errno);
      }
      retval = oparm->a_retval;
      LocalFree(chunk);
      return(retval);
  
  } // putmsg
  