qnx之resource manager(三)

qnx之resource manager(三)
device-specific and per-open data
学习目的
1.举例引入概念
2.代码编写步骤

1.举例引入概念
a time resource manager,如下所示:
这里写图片描述
该resource manager必须要有以下特性:
1.操作多个设备
•/dev/time/now
•/dev/time/min
•/dev/time/hour

2.在read时,能返回多个数据
•返回一次数据
•在后面的reads中,返回0以表示读到了文件的最后

3.维护open和read之间的上下文
注:要实现以上需求,必须要满足以下要求:a.name和device的数量都知道;b.device的数量不是特别大(少于100)

2.代码编写步骤:
1.注册这3个新name

resmgr_attach (dpp, &rattr, “/dev/time/now”,
_FTYPE_ANY, 0, &connect_funcs, &io_funcs, &nowattr);
resmgr_attach (dpp, &rattr, “/dev/time/hour”,
_FTYPE_ANY, 0, &connect_funcs, &io_funcs, &hourattr);
resmgr_attach (dpp, &rattr, “/dev/time/min”,
_FTYPE_ANY, 0, &connect_funcs, &io_funcs, &minattr);

或者

char *devnames [NumDevices] = { … initializers … };
…
for (i = 0; i < NumDevices; i++) {
resmgr_attach (…, devnames [i], …);
}

图解:
这里写图片描述

2.编写I/O和connect 函数相关
这里写图片描述
每一个open()都匹配一个ocb,并且指向我们打开的设备的attribute structure。
attribute structure指向更高级别的structure(mount structure),它描述了 普通mountpoint 信息。

重写attr,以包含我们自己的数据:

struct Timeattr_s;
#define IOFUNC_ATTR_T struct Timeattr_s
#include 
#include 
typedef struct Timeattr_s {
iofunc_attr_t attr; // encapsulate iofunc layer’s
// attr
char * format; // our data
} Timeattr_

导致io_open函数原型变化:

int io_open (..., RESMGR_HANDLE_T *handle, ...)
变为
int io_open (..., Timeattr_t *tattr, ...)

重写ocb,以包含我们自己的数据:

struct Timeocb_s;
#define IOFUNC_OCB_T struct Timeocb_s
#include 
#include 
typedef struct Timeocb_s {
iofunc_ocb_t ocb; // encapsulate iofunc layer’s ocb
char *buffer; // for buffer of data we return
int bufsize; // how many bytes are in buffer
} Timeocb_t;

使io_read函数原型变化:

int io_read (..., RESMGR_OCB_T *ocb)
变为
int io_read (..., Timeocb_t *tocb)

ocb的动态分配和释放
1:先定义iofunc_funcs_t结构体,
2:然后将该结构体地址放入iofunc_mount_t结构体中,
3:再将iofunc_mount_t结构体地址放入iofunc_attr_t

IOFUNC_OCB_T *time_ocb_calloc (resmgr_context_t *ctp,
IOFUNC_ATTR_T *attr)  //由iofunc_open_default()函数调用
void time_ocb_free (IOFUNC_OCB_T *ocb)//由iofunc_close_ocb_default()函数调用

Timeocb_t *
time_ocb_calloc (resmgr_context_t *ctp, Timeattr_t *tattr)
{
    Timeocb_t *tocb;
    tocb = calloc (1, sizeof (Timeocb_t));
    // do anything else to our per ocb data
    tocb -> buffer = NULL;
    return tocb;
}
void
time_ocb_free (Timeocb_t *tocb)
{
    if (tocb -> buffer) {
    free (tocb -> buffer);
    }
    free (tocb);
}

//1
iofunc_funcs_t time_ocb_funcs = {
_IOFUNC_NFUNCS,
time_ocb_calloc,
time_ocb_free
};

//2
iofunc_mount_t time_mount = { 0, 0, 0, 0, &time_ocb_funcs };

//3
for (i = 0; i < NUMDEVICES; i++) {
iofunc_attr_init (&timeattrs [i].attr, ...);
timeattrs [i].attr.mount = &time_mount;
timeattrs [i].format = formats[i];
resmgr_attach (dpp, &rattr, devnames [i], _FTYPE_ANY, 0,
&connect_funcs, &io_funcs,
&timeattrs [i]);
}

注:系统中有
typedef struct _iofunc_ocb {
IOFUNC_ATTR_T *attr;

} iofunc_ocb_t;
所以iofunc_ocb_t包含了我们重写的IOFUNC_ATTR_T,并且IOFUNC_ATTR_T包含了iofunc_attr_t

以上几个结构体包含关系如图:
这里写图片描述

总体代码:


/*
 *  time.c
 *
 *  This module contains the source code for the /dev/time
 *  resource manager.  This illustrates returning data to a client
 *  that is different on a per-device basis.
 *
 *  This module contains all of the functions necessary.
 *
*/

#include 
#include 
#include 
#include 
#include 

/*
 * we are extending the ocb and the attr (Timeocb_s and Timeattr_s are
 * really Timeocb_t and Timeattr_t below)
*/

struct Timeattr_s;
#define IOFUNC_ATTR_T   struct Timeattr_s
struct Timeocb_s;
#define IOFUNC_OCB_T    struct Timeocb_s

#include 
#include 
#include 
#include 

/*
 *  Define our device attributes structure.
*/

typedef struct Timeattr_s {
    iofunc_attr_t   attr;
    char *          format;     // output format of each device
} Timeattr_t;

/*
 *  Define our open context block structure.
*/

typedef struct Timeocb_s {
    iofunc_ocb_t    ocb;        // this has 'IOFUNC_ATTR_T *attr' at its top
                                // (ocb.offset is the current position
                                // in the buffer)
    char            *buffer;    // the data returned to the client
    int             bufsize;    // the size of the buffer
} Timeocb_t;

/*
 * Number of devices, and how long or format strings are allowed to be.
*/

#define NUMDEVICES  3

#define MAX_FORMAT_SIZE   64

/*
 *  Declare the tables used by the resource manager.
*/

/* for a shared target, please change the time directory to something
 *  unique.
 */

#define TIME_DIR "/dev/time"
//#define TIME_DIR "/david/time"

//  device names table
char    *devnames [NUMDEVICES] =
{
    TIME_DIR "/now",            // offset HNow
    TIME_DIR "/hour",           // offset HHour
    TIME_DIR "/min"             // offset HMin
};

//  formats for each device
char    formats [NUMDEVICES][MAX_FORMAT_SIZE + 1] =
{
    "%Y %m %d %H:%M:%S\n",
    "%H\n",
    "%M\n"
};

//  pathname ID table
int         pathnameID [NUMDEVICES];

//  device information table
Timeattr_t  timeattrs [NUMDEVICES];

/*
 *  some forward declarations
*/

void options (int argc, char **argv);

//  I/O functions
int io_read (resmgr_context_t *ctp, io_read_t *msg, Timeocb_t *tocb);
int io_write (resmgr_context_t *ctp, io_write_t *msg, Timeocb_t *tocb);
Timeocb_t *time_ocb_calloc (resmgr_context_t *ctp, Timeattr_t *tattr);
void time_ocb_free (Timeocb_t *tocb);

//  miscellaneous support functions
char *format_time (char *format, int time_offset);
/*
 *  our connect and I/O functions
*/

resmgr_connect_funcs_t  connect_funcs;
resmgr_io_funcs_t       io_funcs;

/*
 *  our ocb allocating and freeing functions
*/

iofunc_funcs_t          time_ocb_funcs = {
    _IOFUNC_NFUNCS,
    time_ocb_calloc,
    time_ocb_free
};

/*
 *  the mount structure, we only have one so we statically declare it
*/

iofunc_mount_t          time_mount = { 0, 0, 0, 0, &time_ocb_funcs };

/*
 *  our dispatch, resource manager and iofunc variables
*/

dispatch_t              *dpp;
resmgr_attr_t           rattr;
dispatch_context_t      *ctp;

/*
 *  some miscellaneous variables
*/

char    *progname = "time";                // for diagnostic messages
int     optv;                               // -v for verbose operation

int main (int argc, char **argv)
{
    int i;

    printf ("%s:  starting...\n", progname);

    options (argc, argv);

    dpp = dispatch_create ();
    memset (&rattr, 0, sizeof (rattr));

    /*
     * initialize the connect functions and I/O functions tables to
     * their defaults and then override the defaults with the
     * functions that we are providing.
    */

    iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs,
                      _RESMGR_IO_NFUNCS, &io_funcs);
    io_funcs.read = io_read;
    io_funcs.write = io_write;

    /*
     *  call resmgr_attach to register our 3 prefixes with the
     *  process manager, and also to let it know about our connect
     *  and I/O functions.
     *
     *  On error, returns -1 and errno is set.
    */

    for (i = 0; i < NUMDEVICES; i++) {
        if (optv) {
            printf ("%s:  attaching pathname %s\n", progname, devnames [i]);
        }

        /* 
         *  for this sample program we are using the same mount structure
         *  for all devices, we are using it solely for providing the
         *  addresses of our ocb calloc and free functions.
        */

        iofunc_attr_init (&timeattrs [i].attr, S_IFCHR | 0666, NULL, NULL);
        timeattrs [i].attr.mount = &time_mount;
        timeattrs [i].format = formats[i];
        pathnameID [i] = resmgr_attach (dpp, &rattr, devnames [i],
                    _FTYPE_ANY, 0, &connect_funcs, &io_funcs, &timeattrs [i]);

        if (pathnameID [i] == -1) {
            fprintf (stderr, "%s:  couldn't attach pathname %s, errno %d\n",
                             progname, devnames [i], errno);
            exit (1);
        }
    }

    if (optv) {
        printf ("%s:  entering dispatch loop\n",
                progname);
    }

    ctp = dispatch_context_alloc (dpp);

    while (1) {
        if ((ctp = dispatch_block (ctp)) == NULL) {
            fprintf (stderr, "%s:  dispatch_block failed: %s\n",
                             progname, strerror (errno));
            exit (1);
        }
        dispatch_handler (ctp);
    }
}

/*
 *  time_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 register
 *  it through the mount structure.
*/

Timeocb_t *
time_ocb_calloc (resmgr_context_t *ctp, Timeattr_t *tattr)
{
    Timeocb_t   *tocb;

    if (optv) {
        printf ("%s:  in time_ocb_calloc \n", progname);
    }

    if ((tocb = calloc (1, sizeof (Timeocb_t))) == NULL) {
        if (optv) {
            printf ("%s:  couldn't allocate %d bytes\n",
                    progname, sizeof (Timeocb_t));
        }
        return (NULL);
    }
    // do anything else to our part of the ocb we wish
    tocb -> buffer = NULL;
    return (tocb);
}

/*
 *  time_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 register
 *  it through the mount structure.
*/

void
time_ocb_free (Timeocb_t *tocb)
{
    if (optv) {
        printf ("%s:  in time_ocb_free \n", progname );
    }

    /*
     *  if we actually allocated a buffer (ie:  if the
     *  client called "read", as opposed to just open/close),
     *  return the memory associated with the buffer.
    */

    if (tocb -> buffer) {
        free (tocb -> buffer);
    }

    free (tocb);
}

/*
 *  io_read
 *
 *  At this point, the client has called their library "read"
 *  function, and expects zero or more bytes.
 *  We are getting the format which we are handling for
 *  this request via the "tocb -> ocb . attr -> format" parameter.
*/

int
io_read (resmgr_context_t *ctp, io_read_t *msg, Timeocb_t *tocb)
{
    int     nleft;
    int     onbytes;
    int     status;

    if (optv) {
        printf ("%s:  in io_read, offset is %lld, nbytes %d\n",
                progname, tocb -> ocb . offset, msg -> i.nbytes);
    }

    if ((status = iofunc_read_verify(ctp, msg, &tocb->ocb, NULL)) != EOK)
        return (status);

    // No special xtypes
    if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) {
        return(ENOSYS);
    }

    /*
     *  the first time in, get the time and set up
     *  the size
    */

    if (tocb -> buffer == NULL) {
        /* format the output based on the format string for the device currently being used */
        tocb -> buffer = format_time (tocb -> ocb.attr -> format, 0);
        tocb -> bufsize = strlen (tocb -> buffer) + 1;
    }

    /*
     *  on all reads (first and subsequent) calculate
     *  how many bytes we can return to the client,
     *  based upon the number of bytes available (nleft)
     *  and the client's buffer size
    */

    nleft = tocb -> bufsize - tocb -> ocb . offset;
    onbytes = min (msg -> i.nbytes, nleft);

    /*
     *  do the MsgReply here.  Why are we replying instead of having
     *  the resmgr API do it?  To show that you can.
    */

    if (onbytes) {
        MsgReply (ctp -> rcvid, onbytes, tocb -> buffer + tocb -> ocb . offset, onbytes);
    } else {
        MsgReply (ctp -> rcvid, 0, NULL, 0);
    }

    /*
     *  advance the offset to reflect the number
     *  of bytes returned to the client.
    */

    tocb -> ocb . offset += onbytes;

    if (msg->i.nbytes > 0)
        tocb->ocb.attr->attr.flags |= IOFUNC_ATTR_ATIME;

    /*
     *  return _RESMGR_NOREPLY because we've done the
     *  MsgReply ourselves...
    */

    return (_RESMGR_NOREPLY);
}

/*
 *  io_write
 *
 *  At this point, the client has called their library "write"
 *  function.  Writing is not defined for /dev/time, so we just
 *  swallow any bytes that they may have written, just like
 *  /dev/null.  An alternate approach is to return an error at
 *  open time if we detect that the device has been opened for
 *  writing.
*/

int
io_write (resmgr_context_t *ctp, io_write_t *msg, Timeocb_t *tocb)
{
    int status;

    if (optv) {
        printf ("%s:  in io_write\n", progname);
    }

    if ((status = iofunc_write_verify(ctp, msg, &tocb->ocb, NULL)) != EOK)
        return (status);

    // No special xtypes
    if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) {
        return(ENOSYS);
    }

    _IO_SET_WRITE_NBYTES (ctp, msg -> i.nbytes); // indicate how many we wrote

    if (msg->i.nbytes > 0)
        tocb->ocb.attr->attr.flags |= IOFUNC_ATTR_MTIME | IOFUNC_ATTR_CTIME;

    return (_RESMGR_NPARTS (0));
}

/*
 *  format_time()
 *
 * Format a time based on the correct device and time offset
 * value.
 *
 *  We assume that the malloc always works.
*/

char *
format_time (char *format, int time_offset)
{
    char    *ptr;
    time_t  now;

    ptr = malloc (64);
    time (&now);
    now += time_offset;
    strftime (ptr, 64, format, localtime (&now));
    return (ptr);
}


/*
 *  options
 *
 *  This routine handles the command line options.
 *  For our /dev/time family, we support:
 *      -v      verbose operation
*/

void
options (int argc, char **argv)
{
    int     opt;

    optv = 0;

    while ((opt = getopt (argc, argv, "v")) != -1) {
       switch (opt) {
       case 'v':
           optv = 1;
           break;
       }
    }
}

你可能感兴趣的:(QNX)