/*****************************************************************************

 @(#) $Id: sdti.c,v 0.7.2.5 2001/02/03 05:48:48 brian Test $

 -----------------------------------------------------------------------------

 Copyright (C) 1997-2001  Brian Bidulock <bidulock@dallas.net>

 All Rights Reserved.

 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free Software
 Foundation; either version 2 of the License, or (at your option) any later
 version.

 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 details.

 You should have received a copy of the GNU General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 675 Mass
 Ave, Cambridge, MA 02139, USA.

 -----------------------------------------------------------------------------

 Last Modified $Date: 2001/02/03 05:48:48 $ by $Author: brian $

 *****************************************************************************/

static char const ident[] = "$Id: sdti.c,v 0.7.2.5 2001/02/03 05:48:48 brian Test $";

#define SDT_DESCRIP   "SS7/SDT: SS7 LEVEL 1 (SDT) STREAMS MODULE."
#define SDT_COPYRIGHT "Copyright (c) 1997-2001 Brian Bidulock.  All Rights Reserved."
#define SDT_DEVICES   "Supports all SDTI capable devices."
#define SDT_CONTACT   "Brian F. G. Bidulock <bidulock@openss7.org>"
#define SDT_BANNER    SDT_DESCRIP   "\n" \
                      SDT_COPYRIGHT "\n" \
                      SDT_DEVICES   "\n" \
                      SDT_CONTACT   "\n"

#ifdef MODULE
MODULE_AUTHOR(SDT_CONTACT);
MODULE_DESCRIPTION(SDT_DESCRIP);
MODULE_SUPPORTED_DEVICES(SDT_DEVICES);
#endif

#ifdef SDT_DEBUG
int sdt_debug = SDT_DEBUG;
#else
int sdt_debug = 2;
#endif

#define SDT_DEVICE       "sdt"
#define SDT_MIN_PDU      3
#define SDT_MAX_PDU      277
#define SDT_MOD_NUMBER   0x1111
#define SDT_W_HIWATER    1
#define SDT_W_LOWATER    0
#define SDT_R_HIWATER    1
#define SDT_R_LOWATER    0

typedef struct {
    void (*sdt_daedr_start)(sl_t *sl);
    void (*sdt_daedt_start)(sl_t *sl);
} sdt_ops_t;


struct sdt
{
    queue_t     *rq;        /* read  queue */
    queue_t     *wq;        /* write queue */
    sdt_ulong   state;      /* interface state */
    sdt_ulong   flags;      /* flags for whatever */
    sl_t        *sl;        /* signalling link */
};

#define SDT_N_MINOR 16 /* for now */
struct sdt sdts[SDT_N_MINOR];

typedef enum retval
{
    DONE,
    RETRY,
    ERR
} retval_t;

static struct module_info sdt_r_minfo =
{
    SDT_MOD_NUMBER,     /* Module ID number             */
    "sdt",              /* Module name                  */
    SDT_MIN_PDU,        /* Min packet size accepted     */
    SDT_MAX_PDU,        /* Max packet size accepted     */
    SDT_R_HIWATER,      /* Hi water mark                */
    SDT_R_LOWATER       /* Lo water mark                */
};

static int sdt_open(queue_t *, dev_t *, int, int, cred_t *);
static int sdt_close(queue_t *, int, cred_t *);
static void sdt_rput(queue_t *, mblk_t *);
static void sdt_rsrv(queue_t *);

static struct qinit sdt_rinit =
{
    sdt_rput,     /* qi_putp   */ /* Read put (message from below)  */
    sdt_rsrv,     /* qi_srvp   */ /* Read queue service             */
    sdt_open,     /* qi_qopen  */ /* Each open                      */
    sdt_close,    /* qi_qclose */ /* Last close                     */
    NULL,         /* qi_qadmin */ /* Admin (not used)               */
    &sdt_r_minfo, /* qi_minfo  */ /* Information                    */
    NULL          /* qi_mstat  */ /* Statistics                     */
};

static struct module_info sdt_w_minfo =
{
    SDT_MOD_NUMBER,     /* Module ID number             */
    "sdt",              /* Module name                  */
    SDT_MIN_PDU,        /* Min packet size accepted     */
    SDT_MAX_PDU,        /* Max packet size accepted     */
    SDT_W_HIWATER,      /* Hi water mark                */
    SDT_W_LOWATER       /* Lo water mark                */
};

static void sdt_wput(queue_t *, mblk_t *);
static void sdt_wsrv(queue_t *);

static struct qinit sdt_rinit =
{
    sdt_wput,     /* qi_putp   */ /* Write put (message from above) */
    sdt_wsrv,     /* qi_srvp   */ /* Write queue service            */
    NULL,         /* qi_qopen  */ /* Each open                      */
    NULL,         /* qi_qclose */ /* Last close                     */
    NULL,         /* qi_qadmin */ /* Admin (not used)               */
    &sdt_r_minfo, /* qi_minfo  */ /* Information                    */
    NULL          /* qi_mstat  */ /* Statistics                     */
};

#ifdef MODULE
static
#endif
struct streamtab sdt_info =
{
    sdt_rinit,  /* Upper read  queue                */
    sdt_winit,  /* Upper write queue                */
    NULL,       /* Lower read  queue (not a mux)    */
    NULL        /* Lower write queue (not a mux)    */
};

void sdt_init(void)
{
    printk(KERN_INFO SDT_BANNER);   /* console splash */

    /* Do some groovy initializations... */
};

void sdt_terminate(void)
{
    /* Do some cleanup */
};

static int sdt_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
{
    dev_t i;
    queue_t *rq;

    if (sflag == MODOPEN)
        return EINVAL;  /* not a module */
    if (slfag == CLONEOPEN) {
        if ( WR(q)->q_next != NULL) /* not a module */
            return EINVAL;
        for ( i=1; i<SDT_N_MINOR; i++ )
            if ( sdts[i].rq == NULL )   /* might also probe here */
                break;
    } else {
        if ( (i = getminor(*devp)) >= SDT_N_MINOR)
            return ENXIO;
    }
    *devp = makedevice(getmajor(*devp), i);

    if (q->q_ptr != NULL)
        return 0;   /* already open */

#ifdef MODULE
    MOD_INC_USE_COUNT;
#endif

    /* need to probe and allocate driver resources */

    sdts[i].rq = q;
    sdts[i].wq = WR(q);
    sdts[i].state = SDT_UNATTACHED;
    sdts[i].flags = 0;

    q->q_ptr = WR(q)->q_ptr = &sdts[i];

    return 0;
}

static int sdt_close(queue_t *q, int flag, cred_t *crp)
{
    struct sdt *sdt = (struct *sdt)q->q_ptr;

    assert(sdt != NULL);
    assert(sdt->rq == q);

    sdt->state = SDT_UNATTACHED;
    sdt->rq = NULL;
    q->q_ptr = WR(q)->q_ptr = NULL;

#ifdef MODULE
    MOD_DEC_USE_COUNT;
#endif
    return 0;
}















