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.
The first addition defines what the devctl() commands are going to be. This is generally put in a common or shared header file:
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:
Add this code to the main() function:
/* For handling _IO_DEVCTL, sent by devctl() */ io_funcs.devctl = io_devctl;
And the following code gets added before the main() function:
int io_devctl(resmgr_context_t *ctp, io_devctl_t *msg, RESMGR_OCB_T *ocb); int global_integer = 0;
Now, you need to include the new handler function to handle the _IO_DEVCTL message (see the text following the listing for additional notes):
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 = _DEVCTL_DATA(msg->i); /* 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; /* Indicate the number of bytes and return the message */ msg->o.nbytes = nbytes; return(_RESMGR_PTR(ctp, &msg->o, sizeof(msg->o) + nbytes)); }
Here are the notes for the above code:
For your convenience, we've defined a union of all of the messages that this server can receive. However, this won't work with large data messages. In this case, you'd use resmgr_msgread() 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.
If you add the following handler code, a client should be able to open /dev/sample and subsequently set and retrieve the global integer value:
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); }