In the previous io_devctl() example, above, we raised the question of how to set arbitrary sampling rates. Obviously, it's not a good solution to create a large number of DCMD_AUDIO_SET_SAMPLE_RATE_* constants—we'd rapidly use up the available bits in the dcmd member.
From the client side, we'll use the dev_data_ptr pointer to point to the sample rate, which we'll simply pass as an integer. Therefore, the nbytes member will simply be the number of bytes in an integer (4 on a 32-bit machine). We'll assume that the constant DCMD_AUDIO_SET_SAMPLE_RATE is defined for this purpose.
Also, we'd like to be able to read the current sampling rate. We'll also use the dev_data_ptr and nbytes as described above, but in the reverse direction—the resource manager will return data into the memory location pointed to by dev_data_ptr (for nbytes) instead of getting data from that memory location. Let's assume that the constant DCMD_AUDIO_GET_SAMPLE_RATE is defined for this purpose.
Let's see what happens in the resource manager's io_devctl(), as shown here (we won't discuss things that have already been discussed in the previous example):
/* * io_devctl2.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <devctl.h> #include <sys/neutrino.h> #include <sys/iofunc.h> #define DCMD_AUDIO_SET_SAMPLE_RATE 1 #define DCMD_AUDIO_GET_SAMPLE_RATE 2 int io_devctl (resmgr_context_t *ctp, io_devctl_t *msg, iofunc_ocb_t *ocb) { int sts; void *data; int nbytes; if ((sts = iofunc_devctl_default (ctp, msg, ocb)) != _RESMGR_DEFAULT) { return (sts); } // 1) assign a pointer to the data area of the message data = _DEVCTL_DATA (msg); // 2) preset the number of bytes that we'll return to zero nbytes = 0; // check for all commands; we'll just show the ones we're // interested in here switch (msg -> i.dcmd) { // 3) process the SET command case DCMD_AUDIO_SET_SAMPLE_RATE: audio_set_samplerate (* (int *) data); break; // 4) process the GET command case DCMD_AUDIO_GET_SAMPLE_RATE: * (int *) data = audio_get_samplerate (); nbytes = sizeof (int); break; } // 5) return data (if any) to the client memset (&msg -> o, 0, sizeof (msg -> o)); msg -> o.nbytes = nbytes; SETIOV (ctp -> iov, &msg -> o, sizeof (msg -> o) + nbytes); return (_RESMGR_NPARTS (1)); }
Recall the discussion in the io_write() sample above, about the data area following the header. To recap, we stated that the bytes following the header may or may not be complete (i.e., the header may or may not have been read in its entirety from the client), depending on how much data was read in by the resource manager library. Then we went on to discuss how it was inefficient to try to "save" a message pass and to "reuse" the data area.
However, things are slightly different with devctl(), especially if the amount of data being transferred is fairly small (as was the case in our examples). In these cases, there's a good chance that the data has in fact been read into the data area, so it is indeed a waste to reread the data. There is a simple way to tell how much space you have: the size member of ctp contains the number of bytes that are available for you starting at the msg parameter. The size of the data area beyond the end of the message buffer that's available is calculated by subtracting the size of the message buffer from the size member of ctp:
data_area_size = ctp -> size - sizeof (*msg);
Note that this size is equally valid when you are returning data to the client (as in the DCMD_AUDIO_GET_SAMPLE_RATE command).
For anything larger than the allocated region, you'll want to perform the same processing we did with the io_write() example (above) for getting data from the client, and you'll want to allocate a buffer to be used for returning data to the client.