Handling out-of-band (_IO_MSG) messages

An _IO_MSG message lets a client send an “out-of-band” or control message to a resource manager, by way of a file descriptor. This interface is more general than an ioctl() or devctl(), but less portable.

The format of the message is specific to the resource manager, aside from the header, which we'll look at shortly. The client program sets up the message and uses MsgSend() to send it to the resource manager. The resource manager must set up an io_msg handler in order to receive the message; there isn't a default handler.

The message header is defined in <sys/iomsg.h> and looks like this:

struct _io_msg {
    uint16_t    type;
    uint16_t    combine_len;
    uint16_t    mgrid;
    uint16_t    subtype;
};

The fields include:

type
_IO_MSG
combine_len
Set this to sizeof (struct _io_msg).
mgrid
A unique ID for your resource manager. The <sys/iomgr.h> header file defines some IDs that are reserved for various QNX Neutrino resource managers. You can use an ID in the range from _IOMGR_PRIVATE_BASE through _IOMGR_PRIVATE_MAX for your resource manager.
subtype
Use this field to distinguish different types of _IO_MSG messages that you want your resource manager to handle.

Any data should follow this header. For example:

typedef struct {
    struct _io_msg hdr;

    /* Add any required data fields here. */

} my_msg_t;

The client program would then do something like this:

#define MY_MGR_ID (_IOMGR_PRIVATE_BASE + 22)

my_msg_t msg, my_reply;
int fd;
long status;

fd = open ("/dev/sample", O_RDWR);
    
msg.hdr.type = _IO_MSG;
msg.hdr.combine_len = sizeof( msg.hdr );
msg.hdr.mgrid = MY_MGR_ID;
msg.hdr.subtype = 0;

/* Fill in the additional fields as required. */
    
status = MsgSend( fd, &msg, sizeof( msg ), &my_reply,
                  sizeof (my_reply));

The resource manager registers a function to handle the _IO_MSG messages:

/* Initialize the functions for handling messages */
iofunc_func_init(_RESMGR_CONNECT_NFUNCS, &connect_funcs,
                 _RESMGR_IO_NFUNCS, &io_funcs);

io_funcs.msg = my_io_msg;

This handler processes the message as appropriate. For example:

int my_io_msg (resmgr_context_t *ctp, io_msg_t *msg,
               RESMGR_OCB_T *ocb)
{
    my_msg_t my_msg;
    
    MsgRead (ctp->rcvid, &my_msg, sizeof (my_msg), 0);
    
    if (my_msg.hdr.mgrid != MY_MGR_ID)
    {
        return (ENOSYS);
    }

    /* Process the data as required. */
    
    /* Reply if necessary and tell the library that we've
       already replied. */

    MsgReply( ctp->rcvid, 0, &my_reply, sizeof(my_reply));
    return (_RESMGR_NOREPLY);
}

Note that the handler returns ENOSYS if the mgrid member of the header isn't the correct manager ID. This handler replies to the client, and then returns _RESMGR_NOREPLY to tell the library that there's no need for it to do the reply.