This chapter includes:
The iofunc_*() default functions operate on the assumption that you've used the default definitions for the context block and the attributes structures. This is a safe assumption for two reasons:
The default structures must be the first members of their respective superstructures, so that the iofunc_*() default functions can access them:
In our /dev/sample example, we had a static buffer associated with the entire resource. Sometimes you may want to keep a pointer to a buffer associated with the resource, rather than in a global area. To maintain the pointer with the resource, we would have to store it in the iofunc_attr_t attribute structure. Since the attribute structure doesn't have any spare fields, we would have to extend it to contain that pointer.
Sometimes you may want to add extra entries to the standard iofunc_*() OCB (iofunc_ocb_t).
Let's see how we can extend both of these structures. The basic strategy used is to encapsulate the existing attributes and OCB structures within a newly defined superstructure that also contains our extensions. Here's the code (see the text following the listing for comments):
/* Define our overrides before including <sys/iofunc.h> */ struct device; #define IOFUNC_ATTR_T struct device /* see note 1 */ struct ocb; #define IOFUNC_OCB_T struct ocb /* see note 1 */ #include <sys/iofunc.h> #include <sys/dispatch.h> struct ocb { /* see note 2 */ iofunc_ocb_t hdr; /* see note 4; must always be first */ struct ocb *next; struct ocb **prev; /* see note 3 */ }; struct device { /* see note 2 */ iofunc_attr_t attr; /* must always be first */ struct ocb *list; /* waiting for write */ }; /* Prototypes, needed since we refer to them a few lines down */ struct ocb *ocb_calloc (resmgr_context_t *ctp, struct device *device); void ocb_free (struct ocb *ocb); iofunc_funcs_t ocb_funcs = { /* our ocb allocating & freeing functions */ _IOFUNC_NFUNCS, ocb_calloc, ocb_free }; /* The mount structure. We have only one, so we statically declare it */ iofunc_mount_t mountpoint = { 0, 0, 0, 0, &ocb_funcs }; /* One struct device per attached name (there's only one name in this example) */ struct device deviceattr; main() { ... /* * deviceattr will indirectly contain the addresses * of the OCB allocating and freeing functions */ deviceattr.attr.mount = &mountpoint; resmgr_attach (..., &deviceattr); ... } /* * ocb_calloc * * The purpose of this is to give us a place to allocate our own OCB. * It is called as a result of the open being done * (e.g. iofunc_open_default causes it to be called). We * registered it through the mount structure. */ IOFUNC_OCB_T ocb_calloc (resmgr_context_t *ctp, IOFUNC_ATTR_T *device) { struct ocb *ocb; if (!(ocb = calloc (1, sizeof (*ocb)))) { return 0; } /* see note 3 */ ocb -> prev = &device -> list; if (ocb -> next = device -> list) { device -> list -> prev = &ocb -> next; } device -> list = ocb; return (ocb); } /* * ocb_free * * The purpose of this is to give us a place to free our OCB. * It is called as a result of the close being done * (e.g. iofunc_close_ocb_default causes it to be called). We * registered it through the mount structure. */ void ocb_free (IOFUNC_OCB_T *ocb) { /* see note 3 */ if (*ocb -> prev = ocb -> next) { ocb -> next -> prev = ocb -> prev; } free (ocb); }
Here are the notes for the above code:
You can also extend the iofunc_mount_t structure in the same manner as the attribute and OCB structures. In this case, you'd define:
#define IOFUNC_MOUNT_T struct newmount
and then declare the new structure:
struct newmount { iofunc_mount_t mount; int ourflag; };