Sample code for handling _IO_DEVCTL messages
You can add the following code samples to either of the
/dev/null
examples provided in the
Simple device resource manager examples
section of the Bones of a Resource Manager chapter.
Both of those code samples provided the name /dev/sample.
With the changes indicated below,
the client can use devctl() to set and retrieve a global value
(an integer in this case) that's maintained in the resource manager.
typedef union _my_devctl_msg {
int tx; /* Filled by client on send */
int rx; /* Filled by server on reply */
} data_t;
#define MY_CMD_CODE 1
#define MY_DEVCTL_GETVAL __DIOF(_DCMD_MISC, MY_CMD_CODE + 0, int)
#define MY_DEVCTL_SETVAL __DIOT(_DCMD_MISC, MY_CMD_CODE + 1, int)
#define MY_DEVCTL_SETGET __DIOTF(_DCMD_MISC, MY_CMD_CODE + 2, union _my_devctl_msg)
In the above code, we defined three commands that the client can use:
- MY_DEVCTL_SETVAL
- Sets the server's global variable to the integer the client provides.
- MY_DEVCTL_GETVAL
- Gets the value of the server's global variable and puts it into the client's buffer.
- MY_DEVCTL_SETGET
- Sets the server's global variable to the integer that the client provides, and then returns the previous value of the server's global variable in the client's buffer.
/* For handling _IO_DEVCTL, sent by devctl() */
io_funcs.devctl = io_devctl;
int io_devctl(resmgr_context_t *ctp, io_devctl_t *msg, RESMGR_OCB_T *ocb);
int global_integer = 0;
int io_devctl(resmgr_context_t *ctp, io_devctl_t *msg, RESMGR_OCB_T *ocb)
{
int nbytes, status, previous;
union { /* See note 1 */
data_t data;
int data32;
/* ... other devctl types you can receive */
} *rx_data;
/*
Let common code handle DCMD_ALL_* cases.
You can do this before or after you intercept devctls, depending
on your intentions. Here we aren't using any predefined values,
so let the system ones be handled first. See note 2.
*/
if ((status = iofunc_devctl_default(ctp, msg, ocb)) !=
_RESMGR_DEFAULT) {
return(status);
}
status = nbytes = 0;
/*
Note this assumes that you can fit the entire data portion of
the devctl into one message. In reality you should probably
perform a MsgReadv() once you know the type of message you
have received to get all of the data, rather than assume
it all fits in the message. We have set in our main routine
that we'll accept a total message size of up to 2 KB, so we
don't worry about it in this example where we deal with ints.
*/
/* Get the data from the message. See Note 3. */
rx_data = _IO_INPUT_PAYLOAD(msg);
/*
Three examples of devctl operations:
SET: Set a value (int) in the server
GET: Get a value (int) from the server
SETGET: Set a new value and return the previous value
*/
switch (msg->i.dcmd) {
case MY_DEVCTL_SETVAL:
global_integer = rx_data->data32;
nbytes = 0;
break;
case MY_DEVCTL_GETVAL:
rx_data->data32 = global_integer; /* See note 4 */
nbytes = sizeof(rx_data->data32);
break;
case MY_DEVCTL_SETGET:
previous = global_integer;
global_integer = rx_data->data.tx;
/* See note 4. The rx data overwrites the tx data
for this command. */
rx_data->data.rx = previous;
nbytes = sizeof(rx_data->data.rx);
break;
default:
return(ENOSYS);
}
/* Clear the return message. Note that we saved our data past
this location in the message. */
memset(&msg->o, 0, sizeof(msg->o));
/*
If you wanted to pass something different to the return
field of the devctl() you could do it through this member.
See note 5.
*/
msg->o.ret_val = status;
return(_RESMGR_PTR(ctp, &msg->o, sizeof(msg->o) + nbytes));
}
Here are the notes for the above code:
- We define a
union
for all the possible types of received data. The MY_DEVCTL_SETVAL and MY_DEVCTL_GETVAL commands use the data32 member, and the MY_DEVCTL_SETGET uses the data member, of type data_t, which is a union of the received and transmitted data. - The default devctl() handler is called before we begin to service our messages. This allows normal system messages to be processed. If the message isn't handled by the default handler, then it returns _RESMGR_DEFAULT to indicate that the message might be a custom message. This means that we should check the incoming command against commands that our resource manager understands.
- The data to be passed follows directly after the io_devctl_t
structure. You can get a pointer to this location by using the
_IO_INPUT_PAYLOAD(msg)
macro defined in <sys/iomsg.h>. The argument to this macro must be a pointer to the io_devctl_t structure.For your convenience, we've defined a union of all of the messages that this server can receive. But, this won't work with large data messages. In this case, you'd use resmgr_msgget() to read the message from the client. Our messages are never larger than
sizeof(int)
and this comfortably fits into the minimum receive buffer size. - The data being returned to the client is placed at the end of the reply message.
You can get a pointer to this location by using the
_IO_OUTPUT_PAYLOAD(msg)
macro defined in <sys/iomsg.h>. The argument to this macro must be a pointer to the io_devctl_t structure. With large replies that wouldn't necessarily fit into the server's receive buffer, you should use one of the reply mechanisms described in theMethods of returning and replying
section in theHandling Read and Write Messages
chapter. Again, in this example, we're only returning an integer that fits into the receive buffer without any problem. - The last argument to the devctl() function is a pointer to an integer.
If this pointer is provided, then the integer is filled with the value
stored in the
msg->o.ret_val
reply message. This is a convenient way for a resource manager to return simple status information without affecting the core devctl() operation. It's not used in this example.
int main(int argc, char **argv)
{
int fd, ret, val;
data_t data;
if ((fd = open("/dev/sample", O_RDONLY)) == -1) {
return(1);
}
/* Find out what the value is set to initially */
val = -1;
ret = devctl(fd, MY_DEVCTL_GETVAL, &val, sizeof(val), NULL);
printf("GET returned %d w/ server value %d \n", ret, val);
/* Set the value to something else */
val = 25;
ret = devctl(fd, MY_DEVCTL_SETVAL, &val, sizeof(val), NULL);
printf("SET returned %d \n", ret);
/* Verify we actually did set the value */
val = -1;
ret = devctl(fd, MY_DEVCTL_GETVAL, &val, sizeof(val), NULL);
printf("GET returned %d w/ server value %d == 25? \n", ret, val);
/* Now do a set/get combination */
memset(&data, 0, sizeof(data));
data.tx = 50;
ret = devctl(fd, MY_DEVCTL_SETGET, &data, sizeof(data), NULL);
printf("SETGET returned with %d w/ server value %d == 25?\n",
ret, data.rx);
/* Check set/get worked */
val = -1;
ret = devctl(fd, MY_DEVCTL_GETVAL, &val, sizeof(val), NULL);
printf("GET returned %d w/ server value %d == 50? \n", ret, val);
return(0);
}