static int
io_read (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb)
{
int nbytes;
int nleft;
int sts;
char string [MAX_DIGITS + 1];
// 1) we don't do any xtypes here...
if ((msg -> i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) {
return (ENOSYS);
}
// standard helper
sts = iofunc_read_verify (ctp, msg, &ocb -> base, NULL);
if (sts != EOK) {
return (sts);
}
// 2) generate and compress an image
if (!ocb -> output) {
unsigned char *input; // limited scope
input = calloc (optx, opty);
if (input == NULL) {
return (ENOMEM);
}
ocb -> output = calloc (optx, opty);
if (ocb -> output == NULL) {
free (input);
return (ENOMEM);
}
sprintf (string, "%0*d", optd, ocb -> base.attr -> count++);
render_7segment (string, input, optx, opty);
ocb -> size = encode_image (input, optx, opty, ocb -> output);
free (input);
}
// 3) figure out how many bytes are left
nleft = ocb -> size - ocb -> base.offset;
// 4) and how many we can return to the client
nbytes = min (nleft, msg -> i.nbytes);
if (nbytes) {
// 5) return it to the client
MsgReply (ctp -> rcvid, nbytes,
ocb -> output + ocb -> base.offset, nbytes);
// 6) update flags and offset
ocb -> base.attr -> base.flags |=
IOFUNC_ATTR_ATIME | IOFUNC_ATTR_DIRTY_TIME;
ocb -> base.offset += nbytes;
} else {
// 7) nothing to return, indicate End Of File
MsgReply (ctp -> rcvid, EOK, NULL, 0);
}
// 8) already done the reply ourselves
return (_RESMGR_NOREPLY);
}
Let's look at this code step-by-step:
- If there are any XTYPE directives, we return ENOSYS because we don't handle XTYPEs.
XTYPEs are discussed in the Getting Started with QNX Neutrino book in the
Resource Managers chapter.
- If we currently don't have a compressed image to work from (i.e., this is the first time
that we've been called for this particular open()
request), we allocate the temporary input (raw buffer) and OCB's output (compressed buffer) data
areas, call render_7segment() to draw the picture into the raw buffer,
and then encode_image() to compress it.
Notice that encode_image() returns the number of bytes that it generated and
we store that into the OCB's size member.
Then we free the temporary input buffer area.
- We calculate the number of bytes that are left, which is simply the difference between
the number of bytes that we have in the compressed image buffer and our current
offset within that buffer.
Note that we use the OCB's size member rather than the attributes structure's
nbytes member (see note after step 8 below).
- We then calculate the number of bytes we can return to the client.
This could be smaller than the number of bytes that we have left because the client
may request fewer bytes than what we could give them.
- If we have any bytes to return to the client (i.e., we haven't reached EOF),
we perform the MsgReply() ourselves, giving the client nbytes'
worth of data, starting at the output area plus the current offset.
- As per POSIX, we update our ATIME flag, because we just accessed the device and
returned more than zero bytes.
We also move our offset to reflect the number of bytes we just returned to the client,
so that we have the correct position within the file for the next time.
- If, on the other hand, we were not returning any bytes to the client (i.e., we've
reached EOF), we indicate this by doing a MsgReply() with zero bytes.
- By returning _RESMGR_NOREPLY we're indicating to the QNX Neutrino
resource manager framework that we've already called MsgReply()
and that it should not.
Note:
Notice that we used the OCB's size member rather than the attributes structure's
nbytes member.
This is because the image that we generated has a different size (shorter) than the
size stored in the attributes structure's nbytes member.
Since we want to return the correct number of bytes to the client, we use the smaller size
number.