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). You may also find that you need to change the sample rate for recording sound from 22kHz to 44.1kHz depending on the application. 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 Device control I/O function handler).
When the _IO_DEVCTL message is received by the resource manager, it's handled by your device control I/O function handler. 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 <string.h> #include <unistd.h> #include <errno.h> #include <sys/neutrino.h> #include <sys/iofunc.h> #include <devctl.h> #define AUDIO_DAC 1 #define AUDIO_ADC 2 extern void audio_set_nchannels(unsigned num); extern uint32_t audio_get_samplerate(unsigned port); extern void audio_set_samplerate(unsigned port, unsigned rate); #define DCMD_AUDIO_SET_CHANNEL_MONO __DION(_DCMD_MIXER, 1) #define DCMD_AUDIO_SET_CHANNEL_STEREO __DION(_DCMD_MIXER, 2) #define DCMD_AUDIO_GET_SAMPLE_RATE_DAC __DIOF(_DCMD_MIXER, 4, uint32_t) #define DCMD_AUDIO_SET_SAMPLE_RATE_DAC __DIOT(_DCMD_MIXER, 5, uint32_t) #define DCMD_AUDIO_GET_SAMPLE_RATE_ADC __DIOF(_DCMD_MIXER, 6, uint32_t) #define DCMD_AUDIO_SET_SAMPLE_RATE_ADC __DIOT(_DCMD_MIXER, 7, uint32_t) int io_devctl (resmgr_context_t *ctp, io_devctl_t *msg, iofunc_ocb_t *ocb) { int sts; /* 1) Create the reply buffer at the start of ctp->msg so we make * sure we have enough space */ struct _io_devctl_reply *reply = (struct _io_devctl_reply *)ctp->msg; /* Create a pointer to the rate variable for the _GET_ devctls. */ unsigned nbytes = 0; uint32_t *rate = _DEVCTL_DATA(*reply); /* 2) Verify we have the entire devctl header in the buffer */ if( ctp->size < sizeof(msg->i) ) { return EBADMSG; } /* 3) See if it's a standard devctl() */ if ((sts = iofunc_devctl_default (ctp, msg, ocb)) != _RESMGR_DEFAULT) { return (sts); } /* How many bytes did the client send in addition to the devctl header? */ size_t payload_size = ctp->size - sizeof(msg->i); /* 4) See which command it was, and act on it */ switch (msg -> i.dcmd) { case DCMD_AUDIO_SET_CHANNEL_MONO: /* Read or write access are sufficient */ if( !(ocb->ioflag & (_IO_FLAG_WR | _IO_FLAG_RD)) ) { return EPERM; } audio_set_nchannels (1); break; case DCMD_AUDIO_SET_CHANNEL_STEREO: /* Read or write access are sufficient */ if( !(ocb->ioflag & (_IO_FLAG_WR | _IO_FLAG_RD)) ) { return EPERM; } audio_set_nchannels (2); break; case DCMD_AUDIO_GET_SAMPLE_RATE_DAC: /* Write access is required for accessing the DAC */ if( !(ocb->ioflag & _IO_FLAG_WR) ) { return EPERM; } /* Verify that the client wants enough bytes */ if( ctp->info.dstmsglen < (sizeof(*reply) + sizeof(uint32_t)) ) { return EINVAL; } /* Set up the reply to the client */ nbytes = sizeof(uint32_t); *rate = audio_get_samplerate (AUDIO_DAC); break; case DCMD_AUDIO_SET_SAMPLE_RATE_DAC: /* Write access is required for accessing the DAC */ if( !(ocb->ioflag & _IO_FLAG_WR) ) { return EPERM; } /* Verify that we got all the bytes. */ if( payload_size < sizeof(uint32_t) ) { return EBADMSG; } rate = _DEVCTL_DATA(msg->i); audio_set_samplerate (AUDIO_DAC, *rate); break; case DCMD_AUDIO_GET_SAMPLE_RATE_ADC: /* Read access is required for accessing the ADC */ if( !(ocb->ioflag & _IO_FLAG_RD) ) { return EPERM; } /* Verify that the client wants enough bytes */ if( ctp->info.dstmsglen < (sizeof(*reply) + sizeof(uint32_t)) ) { return EINVAL; } /* Set up the reply to the client */ nbytes = sizeof(uint32_t); *rate = audio_get_samplerate (AUDIO_ADC); break; case DCMD_AUDIO_SET_SAMPLE_RATE_ADC: /* Read access is required for accessing the ADC */ if( !(ocb->ioflag & _IO_FLAG_RD) ) { return EPERM; } /* Verify that we got all the bytes. */ if( payload_size < sizeof(uint32_t) ) { return EBADMSG; } rate = _DEVCTL_DATA(msg->i); audio_set_samplerate (AUDIO_ADC, *rate); break; /* 5) In case it's a command that we don't recognize, fail it */ default: return (ENOSYS); } /* 6) Tell the client that it worked */ memset(reply, 0, sizeof(*reply)); reply->nbytes = nbytes; SETIOV (ctp->iov, reply, sizeof(*reply) + nbytes); return (_RESMGR_NPARTS (1)); }
We include iofunc_devctl_default() in our device control I/O function handler function because we want it to handle all the regular 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.
As part of our processing of the various audio devctl() commands, we need to check permissions. If the client is getting or setting the sample rate of the digital to analog converter (DAC), which is output, then they need to have opened the resource manager handle for writing. If the client is setting the sample rate of the analog to digital converter (ADC), which is input, then they need to have opened the resource manager handle for reading. Note here that the permissions being checked are related to whether the DAC or ADC is being accessed, not based on whether devctl() is sending or receiving data from the client (i.e., the use of __DIOF and __DIOT does not correlate with read and write permissions).
In devctl() calls that set sample rate, we also need to make sure that the client sent enough data. The length checks at the beginning of the devctl() function are not sufficient because each devctl() might be expecting the client to send a different amount of data. We do the full check for data now that we know exactly how much data to expect. Once we've done this check, we can safely access this data and set the sample rate.
In devctl() calls that get sample rate, we make sure the client has enough space to receive the response from the resource manager.
Instead of using the macro _RESMGR_NPARTS(), we could have used the _RESMGR_PTR() macro:
/* 6) tell the client it worked */ memset(reply, 0, sizeof(*reply)); reply->nbytes = nbytes; return (_RESMGR_PTR (ctp, reply, sizeof(*reply) + nbytes));
The devctl() command requires us to send a response to the client for devctl() to be successful. It is insufficient to just return EOK without any response message.