/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    s_poll.c

Abstract:

    This module implements the STREAMS api, poll()

Author:

    Eric Chin (ericc)           July 26, 1991

Revision History:

    Sam Patton (sampa)          August 13, 1991
                                changed errno to {get|set}lasterror

--*/
#include "common.h"


/*
 * BUGBUG
 * Confirm that the following is a sane number.
 */
#define MAX_FDS             NPOLLFILE           /* max handles to poll */




int
poll(
    IN OUT struct pollfd   *fds     OPTIONAL,
    IN unsigned int         nfds,
    IN int                  timeout
    )

/*++

Routine Description:

    This procedure is called to poll a set of stream descriptors.

Arguments:

    fds       - pointer to a array of poll structures
    nfds      - number of poll structures pointed to by fds
    timeout   - 0, INFTIM (-1), or timeout in milliseconds.

Return Value:

    no of stream descriptors selected, or -1 if failure.

--*/

{
    char *chunk;
    NTSTATUS status;
    IO_STATUS_BLOCK iosb;
    int chunksz, selected;
    struct pollfd *overlay;
    HANDLE hijack = INVALID_HANDLE_VALUE;

    if (!fds || (nfds <= 0) || (nfds > MAX_FDS)) {
        SetLastError(EINVAL);
        return(-1);
    }

    /*
     * hijack a handle to the Stream Head driver.
     *
     * BUGBUG:
     * In Unix, the user can set pollfd.fd to less than 0 to indicate that
     * the entry should be ignored.  On NT, that isn't possible:
     * INVALID_HANDLE_VALUE must be used.
     */
    for (overlay = fds; overlay < &fds[nfds]; overlay++) {
        if (overlay->fd != INVALID_HANDLE_VALUE) {
            hijack = overlay->fd;
            break;
        }
    }
    if (hijack == INVALID_HANDLE_VALUE) {
        SetLastError(EINVAL);
        return(-1);
    }

    chunksz = sizeof(nfds) + nfds * sizeof(*fds) + sizeof(timeout);

    if (!(chunk = (char *) LocalAlloc(LMEM_FIXED, chunksz))) {
        SetLastError(EAGAIN);
        return(-1);
    }

    /*
     * marshall the arguments into one contiguous chunk, laid out as:
     *
     *      nfds                    (required)
     *      timeout                 (required)
     *      struct fds[nfds]        (required)
     */
    * ((size_t *) chunk)             = nfds;
    * (int *) (chunk + sizeof(nfds)) = timeout;
    overlay                          = (struct pollfd *) (chunk +
                                            sizeof(nfds) + sizeof(timeout));

    memcpy(overlay, fds, nfds * sizeof(*fds));

    status = NtDeviceIoControlFile(
        hijack,
        NULL,                                   // Event
        NULL,                                   // ApcRoutine
        NULL,                                   // ApcContext
        &iosb,                                  // IoStatusBlock
        IOCTL_STREAMS_POLL,                     // IoControlCode
        (PVOID) chunk,                          // InputBuffer
        chunksz,                                // InputBufferSize
        (PVOID) chunk,                          // OutputBuffer
        chunksz);                               // OutputBufferSize

    if (status == STATUS_PENDING) {
        status =
        NtWaitForSingleObject(
            hijack,
            TRUE,
            NULL);
    }

    if (!NT_SUCCESS(status)) {
        SetLastError(MapNtToPosixStatus(status));
        LocalFree((HANDLE) chunk);
        return(-1);
    }

    /*
     * the Stream Head Driver marshalled the return parameters into one
     * contiguous chunk, laid out as:
     *
     *      return value            (required)
     *      errno                   (required)
     *      struct fds[nfds]        (required)
     */
    if ((selected = * (int *) chunk) == -1) {
        SetLastError(* (int *) (chunk + sizeof(nfds)));
        LocalFree((HANDLE) chunk);
        return(selected);
    }
    overlay = (struct pollfd *) (chunk + sizeof(nfds) + sizeof(timeout));

    while (nfds--) {
        fds[nfds].revents = overlay[nfds].revents;
    }
    LocalFree((HANDLE) chunk);
    return(selected);
}