'\"macro stdmacro
.if n .pH ddi_dki.bufcall @(#)bufcall	40.9 of 11/16/89
.\" Copyright 1989 AT&T
.de IX
.ie '\\n(.z'' .tm .Index: \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9	\\n%
.el \\!.IX \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
..
.nr X
.if \nX=0 .ds x} bufcall D3DK "STREAMS" "DDI/DKI" "\&"
.if \nX=1 .ds x} bufcall D3DK "STREAMS" "DDI/DKI"
.if \nX=2 .ds x} bufcall D3DK "" "\&"
.if \nX=3 .ds x} bufcall "" "" "\&"
.TH \*(x}
.IX "\f4bufcall\fP(D3DK)"
.SH NAME
\f4bufcall\f1 \- call a function when a buffer becomes available 
.SH SYNOPSIS
.nf
.na
\f4#include <sys/stream.h>
.sp 0.5
int bufcall(\f4int \f2size, \f4int \f2pri, \f4int (*\f2func\f4)(), long\f1 \f2arg\f4);\f1
.ad
.fi
.SH ARGUMENTS
.RS 0n 10
.IP "\f2size\f1" 10n
Number of bytes in the buffer.
.IP "\f2pri\f1" 10n
Priority of the \f4allocb\f1(D3DK) allocation request (not used).
.IP "\f2func\f1" 10n
Function or driver routine to be called when a buffer becomes available.
.IP "\f2arg\f1" 10n
Argument to the function to be called when a buffer becomes available.
.RE
.SH DESCRIPTION
\f4bufcall\f1 serves as a \f4timeout\f1(D3DK) call of indeterminate length.
When a buffer allocation request 
fails, \f4bufcall\f1 can
be used to schedule the routine \f2func\f1,
to be called with the argument \f2arg\f1 when a buffer becomes available.
\f2func\f1 may be a routine that calls \f4bufcall\f1 or it may be another
kernel function.
.P
\f3NOTE:\f1
Even when \f2func\f1 is called by \f4bufcall\f1,
\f4allocb\f1(D3DK) can still fail if another module or driver 
had allocated the
memory before \f2func\f1 was able to call \f4allocb\f1.
.SH RETURN VALUE
If the \f4bufcall\f1 scheduling fails, \f2func\f1 
is never called and \f40\f1
is returned.  If successful, \f4bufcall\f1 returns \f41\f1.
.SH LEVEL
Base or Interrupt
.SH SEE ALSO
\f2BCI Driver Development Guide\f1, Chapter 7, ``STREAMS''
.P
.na
\f4allocb\f1(D3DK),
\f4esballoc\f1(D3DK),
\f4esbbcall\f1(D3DK),
\f4testb\f1(D3DK),
\f4timeout\f1(D3DK)
.ad
.SH EXAMPLE
.IX "\f4srv\fP(D2DK), example"
.IX "\f4getq\fP(D3DK), example"
.IX "\f4canput\fP(D3DK), example"
.IX "\f4msgdsize\fP(D3DK), example"
.IX "\f4qenable\fP(D3DK), example"
.IX "\f4timeout\fP(D3DK), example"
.IX "\f4putnext\fP(D3DK), example"
.IX "\f4putbq\fP(D3DK), example"
.IX "\f4bufcall\fP(D3DK), example"
.P
The purpose of this \f4srv\f1(D2DK) service routine 
is to add a header to all
\f4M_DATA\f1 messages.
Service routines must process all messages on their queues before
returning, or arrange to be rescheduled.
.P
While there are message to be processed (line 13), check to see if
it is a high priority message or a normal priority message that can
be sent on (line 14).
Normal priority message that cannot be sent are put back
on the message queue (line 34).
If the message was a high priority one, or if was normal
priority and \f4canput\f1(D3DK) succeeded,
then send all but \f4M_DATA\f1 messages to the next stream entity
with \f4putnext\f1(D3DK) (line16).
.P
For \f4M_DATA\f1 messages, try to allocate a buffer large enough to
hold the header (line 18).
If no such buffer is available, the service routine must be
rescheduled for a time when a buffer is available.
The original message is put back on the queue (line 20) and
\f4bufcall\f1 (line 21) is used to attempt the rescheduling.
It will succeed if a buffer of the specified size 
(\f4sizeof (struct hdr)\f1) is available.
If it does, \f4qenable\f1(D3DK) will put \f2q\f1 on the list
of queues to have their service routines called.
If \f4bufcall\f1 fails, \f4timeout\f1(D3DK) (line 22) is used to
try again in about a half second (\f4HZ/2\f1).
.P
If the buffer allocation was successful, initialize the header
(lines 25\-28), make the message type \f4M_PROTO\f1 (line 29), link the
\f4M_DATA\f1 message to it (line 30), and pass it on (line 31).
.ne 4
.P
.nf
.ft 4
.ps 7
 1  struct hdr {
 2        unsigned int h_size;
 3        int          h_version;
 4  };
 5
 6  modsrv(q)
 7        queue_t *q;
 8  {
 9        mblk_t *bp;
10        mblk_t *mp;
11        struct hdr *hp;
12
13       while ((mp = getq(q)) != NULL) {          /* get next message */
14         if (mp->b_datap->db_type >= QPCTL ||    /* if high priority */
               canput(q->q_next)) {          /* normal & can be passed */
15               if (mp->b_datap->db_type != M_DATA)
16                    putnext(q, mp);           /* send all but M_DATA */
17               else {
18                    bp = allocb(sizeof(struct hdr), BPRI_LO);
19                    if (bp == NULL) {             /* if unsuccessful */
20                      putbq(q, mp);               /* put it back */
21                      if (!bufcall(sizeof(struct hdr), BPRI_LO,
                            qenable, (long)q))    /* try to reschedule */
22                          timeout(qenable, (long)q, HZ/2);
23                      return;
24                    }
25                    hp = (struct hdr *)bp->b_wptr;  
26                    hp->h_size = msgdsize(mp);  /* initialize header */
27                    hp->h_version = 1;
28                    bp->b_wptr += sizeof(struct hdr);
29                    bp->b_datap->db_type = M_PROTO; /* make M_PROTO  */
30                    bp->b_cont = mp;                  /* link it */
31                    putnext(q, bp);                    /* pass it on */
32               }   
33         } else {                  /* normal priority, canput failed */
34              putbq(q, mp);         /* put back on the message queue */
35              return;
36         }
37       }
38  }
.ps
.ft 1
.fi
.IX "\f4canput\fP(D3DK), example"
.IX "\f4putnext\fP(D3DK), example"
.IX "\f4putbq\fP(D3DK), example"
.IX "\f4allocb\fP(D3DK), example"
.IX "\f4qenable\fP(D3DK), example"
.IX "\f4timeout\fP(D3DK), example"
