The client's devctl() call is formally defined as:
#include <sys/types.h> #include <unistd.h> #include <devctl.h> int devctl (int fd, int dcmd, void *dev_data_ptr, size_t nbytes, int *dev_info_ptr);
We should first understand this function before we look at the resource manager side of things. The devctl() function is used for "out of band" or "control" operations. For example, you may be writing data to a sound card (the actual digital audio samples that the sound card should convert to analog audio), and you may decide that you need to change the number of channels from 1 (mono) to 2 (stereo), or the sampling rate from the CD-standard (44.1 kHz) to the DAT-standard (48 kHz). The devctl() function is the appropriate way to do this. When you write a resource manager, you may find that you don't need any devctl() support at all and that you can perform all the functionality needed simply through the standard read() and write() functions. You may, on the other hand, find that you need to mix devctl() calls with the read() and write() calls, or indeed that your device uses only devctl() functions and does not use read() or write().
The devctl() function takes these arguments:
The top two bits in the dcmd encode the direction of data transfer, if any. For details, see the description in the I/O reference section (under io_devctl()).
When the _IO_DEVCTL message is received by the resource manager, it's handled by your io_devctl() function. Here is a very simple example, which we'll assume is used to set the number of channels and the sampling rate for the audio device we discussed above:
/* * io_devctl1.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <sys/neutrino.h> #include <sys/iofunc.h> #define DCMD_AUDIO_SET_CHANNEL_MONO 1 #define DCMD_AUDIO_SET_CHANNEL_STEREO 2 #define DCMD_AUDIO_SET_SAMPLE_RATE_CD 3 #define DCMD_AUDIO_SET_SAMPLE_RATE_DAT 4 int io_devctl (resmgr_context_t *ctp, io_devctl_t *msg, iofunc_ocb_t *ocb) { int sts; // 1) see if it's a standard POSIX-supported devctl() if ((sts = iofunc_devctl_default (ctp, msg, ocb)) != _RESMGR_DEFAULT) { return (sts); } // 2) see which command it was, and act on it switch (msg -> i.dcmd) { case DCMD_AUDIO_SET_CHANNEL_MONO: audio_set_nchannels (1); break; case DCMD_AUDIO_SET_CHANNEL_STEREO: audio_set_nchannels (2); break; case DCMD_AUDIO_SET_SAMPLE_RATE_CD: audio_set_samplerate (44100); break; case DCMD_AUDIO_SET_SAMPLE_RATE_DAT: audio_set_samplerate (48000); break; // 3) in case it's a command that we don't recognize, fail it default: return (ENOSYS); } // 4) tell the client it worked memset (&msg -> o, 0, sizeof (msg -> o)); SETIOV (ctp -> iov, &msg -> o, sizeof (msg -> o)); return (_RESMGR_NPARTS (1)); }
We include it in our io_devctl() function because we want it to handle all the regular POSIX devctl() cases for us. We examine the return value; if it's not _RESMGR_DEFAULT, then this means that the iofunc_devctl_default() function "handled" the request, so we just pass along its return value as our return value.
If the constant _RESMGR_DEFAULT is the return value, then we know that the helper function didn't handle the request and that we should check to see if it's one of ours.
An important note that should be mentioned here is that we've specifically avoided touching the data area aspects of devctl()—you may be thinking, "What if I wanted to set the sample rate to some arbitrary number n, how would I do that?" That will be answered in the next io_devctl() example below.
// instead of this // 4) tell the client it worked memset (&msg -> o, 0, sizeof (msg -> o)); SETIOV (ctp -> iov, &msg -> o, sizeof (msg -> o)); return (_RESMGR_NPARTS (1)); // we could have done this // 4) tell the client it worked memset (&msg -> o, 0, sizeof (msg -> o)); return (_RESMGR_PTR (ctp, &msg -> o, sizeof (msg -> o)));
The reason we cleared out the return structure here (and not in the io_read() or io_write() examples) is because in this case, the return structure has actual contents! (In the io_read() case, the only data returned was the data itself and the number of bytes read—there was no "return data structure," and in the io_write() case, the only data returned was the number of bytes written.)