/*
 * Loopback Zaptel Driver for Zapata Telephony interface
 * Emulates real zaptel devices allowing testing on hosts
 * which dont have real zaptel cards - usefule for testing.
 * 
 * Copyright (C) 2007, Druid Software Ltd.
 *
 * Used timer tick from ztdummy
 *
 * 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.
 *
 */
#include <linux/version.h>

#ifndef VERSION_CODE
#  define VERSION_CODE(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) )
#endif


#if LINUX_VERSION_CODE < VERSION_CODE(2,4,5)
#  error "This kernel is too old: not supported by this file"
#endif

/*
 * NOTE: (only applies to kernel 2.6)
 * If using an i386 architecture without a PC real-time clock,
 * the #define USE_RTC should be commented out.
 */
#if defined(__i386__) || defined(__x86_64__)
#if LINUX_VERSION_CODE >= VERSION_CODE(2,6,13)
#define USE_RTC
#else
#if 0
#define USE_RTC
#endif
#endif
#endif

#undef USE_RTC

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#ifdef STANDALONE_ZAPATA
#include "zaptel.h"
#else
#include <zaptel/zaptel.h>
#endif
#ifndef LINUX26
#include <linux/usb.h>
#include <linux/pci.h>
#include <asm/io.h>
#endif
#ifdef LINUX26
#ifdef USE_RTC
#include <linux/rtc.h>
#endif
#include <linux/moduleparam.h>
#endif
#include "ztloop.h"

static int num_loops=1;
static int num_taps=1;

static struct ztloop *ztloop;

static int debug = 0;

#undef USE_RTC

#if defined(LINUX26) && defined(USE_RTC)
static int rtc_rate = 0;
static int current_rate = 0;
static int taskletpending = 0;
static struct tasklet_struct ztd_tlet;
static void ztd_tasklet(unsigned long data);
#endif

#ifdef LINUX26
#define ZAPTEL_RATE 1000
#ifndef USE_RTC
/* New 2.6 kernel timer stuff */
static struct timer_list timer;
#endif
#else
#if LINUX_VERSION_CODE < VERSION_CODE(2,4,5)
#  error "This kernel is too old: not supported by this file"
#endif
/* Old UCHI stuff */
static    uhci_desc_t  *td;
static    uhci_t *s;
static int check_int = 0;
static int monitor = 0;

/* exported kernel symbols */
extern int insert_td (uhci_t *s, uhci_desc_t *qh, uhci_desc_t* new, int flags);
extern int alloc_td (uhci_t *s, uhci_desc_t ** new, int flags);
extern  int insert_td_horizontal (uhci_t *s, uhci_desc_t *td, uhci_desc_t* new);
extern int unlink_td (uhci_t *s, uhci_desc_t *element, int phys_unlink);
extern void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer);
extern void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs);
extern int delete_desc (uhci_t *s, uhci_desc_t *element);
extern uhci_t **uhci_devices;

#endif


#ifdef LINUX26
#ifdef USE_RTC
static void update_rtc_rate(struct ztloop *ztd)
{
    if (((rtc_rate & (rtc_rate - 1)) != 0) || (rtc_rate > 8192) || (rtc_rate < 2)) {
        printk("Invalid RTC rate %d specified\n", rtc_rate);
        rtc_rate = current_rate;    /* Set default RTC rate */
    }
    if (!rtc_rate || (rtc_rate != current_rate)) {
        rtc_control(&ztd->rtc_task, RTC_IRQP_SET, current_rate = (rtc_rate ? rtc_rate : 1024));    /* 1024 Hz */
        printk("ztloop: RTC rate is %d\n", rtc_rate);
        ztd->counter = 0;
    }
}

static void ztd_tasklet(unsigned long data)
{
    if (taskletpending)
        update_rtc_rate((struct ztloop *)ztloop);
    taskletpending = 0;
}

/* rtc_interrupt - called at 1024Hz from hook in RTC handler */
static void ztloop_rtc_interrupt(void *private_data)
{
    struct ztloop *ztd = private_data;
    unsigned long flags;

    /* Is spinlock required here??? */
    spin_lock_irqsave(&ztd->rtclock, flags);
    ztd->counter += ZAPTEL_RATE;
    while (ztd->counter >= current_rate)
    {
        int i;
        
        ztd->counter -= current_rate;
        /* Update of RTC IRQ rate isn't possible from interrupt handler :( */
        if (!taskletpending && (current_rate != rtc_rate)) {
            taskletpending = 1;
            tasklet_hi_schedule(&ztd_tlet);
        }
        for (i = 0; i < ((num_loops*2)+(num_taps*2)); i++)
        {
            zt_transmit(&ztd->spans[i]->span);
        }
        for (i = 0; i < ((num_loops*2)+(num_taps*2)); i++)
        {
            zt_receive(&ztd->spans[i]->span);
        }
    }
    spin_unlock_irqrestore(&ztd->rtclock, flags);
}
#else
/* use kernel system tick timer if PC architecture RTC is not available */
static void ztloop_timer(unsigned long param)
{
    timer.expires = jiffies + 1;
    add_timer(&timer);

    ztloop->counter += ZAPTEL_RATE;
    while (ztloop->counter >= HZ) {
        int i;
        ztloop->counter -= HZ;
        for (i = 0; i < ((num_loops*2)+(num_taps*2)); i++)
        {
            zt_transmit(&ztloop->spans[i]->span);
        }
        for (i = 0; i < ((num_loops*2)+(num_taps*2)); i++)
        {
            zt_receive(&ztloop->spans[i]->span);
        }
    }
}
#endif
#else
static void ztloop_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    unsigned short status;
    unsigned int io_addr = s->io_addr;

    status = inw (io_addr + USBSTS);
    if (status != 0)  {    /* interrupt from our USB port */
        int i;
        for (i = 0; i < ((num_loops*2)+(num_taps*2)); i++)
        {
            zt_transmit(&ztd->spans[i]);
        }
        for (i = 0; i < ((num_loops*2)+(num_taps*2)); i++)
        {
            zt_receive(&ztd->spans[i]);
        }
        if (monitor && (check_int==0)) {      /* for testing if interrupt gets triggered*/
            check_int = 1;
            printk("ztloop: interrupt triggered \n");     
        }   
    }
    return;
}
#endif

static int ztloop_spanconfig(struct zt_span *span,struct zt_lineconfig *lc) 
{
    span->lineconfig = lc->lineconfig;
    return 0;
}

static int ztloop_chanconfig(struct zt_chan *chan,int sigtype) 
{
    return 0;
}

static int ztloop_startup(struct zt_span *span) 
{
    int alreadyrunning;

    alreadyrunning = span->flags & ZT_FLAG_RUNNING;

    if (!alreadyrunning) 
    {
        span->flags |= ZT_FLAG_RUNNING;
    }
    return 0;
}

static int ztloop_shutdown(struct zt_span *span) {
    int alreadyrunning;

    alreadyrunning = span->flags & ZT_FLAG_RUNNING;

    if (!alreadyrunning) 
    {
        return 0;
    }
    return 0;
}

static int ztloop_open(struct zt_chan *chan) {
#ifndef LINUX26
    MOD_INC_USE_COUNT;
#else
    try_module_get(THIS_MODULE);
#endif
    return 0;
}

static int ztloop_close(struct zt_chan *chan) {
#ifndef LINUX26
    MOD_DEC_USE_COUNT;
#else
    module_put(THIS_MODULE);
#endif
    return 0;
}

static int ztloop_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) 
{
    switch(cmd) 
    {
      default:
        return -ENOTTY;
    }
    return 0;
}

static int ztloop_rbs(struct zt_chan *chan, int bits)
{
    struct ztloop *loop = chan->pvt;
    struct zt_chan *loopedchan = NULL;
    struct zt_chan *tappingchan = NULL;
    int i = chan->chanpos - 1;
    int s = chan->span->offset;
    int registered = chan->flags & ZT_FLAG_REGISTERED;
    if (!registered) {
        /* if the channel is not registered, we are just starting up with the idle bits
	   and the span to which we're connected may not be registered, so we should
	   not call zt_rbsbits or we will hang/crash the kernel. */
        return 0;
    }
    loopedchan = &(loop->spans[((s%2)==0)?(s+1):s-1]->chans[i]);
    tappingchan = &(loop->spans[s+(num_loops*2)]->chans[i]);
    registered = loopedchan->flags & ZT_FLAG_REGISTERED;
    /* sanity check: verify if the looped and tapping devices are registered */
    if (registered)
        zt_rbsbits(loopedchan, bits);
    registered = tappingchan->flags & ZT_FLAG_REGISTERED;
    if (registered)
        zt_rbsbits(tappingchan, bits);
    return 0;
}

static int ztloop_taprbs(struct zt_chan *chan, int bits)
{
    /* nothing to do for cas tx since we're just tapping */
    return 0;
}

static int ztloop_initialize(struct ztloop *loop) 
{
    int i=0,s=0;

    if ( (num_loops+num_taps) * 2 > ZTLOOP_MAX_SPANS)
    {
        printk(KERN_INFO "ztloop: ztloop - too many loops/taps defined - total spans can not exceed %d!\n", ZTLOOP_MAX_SPANS);
        return -1;
    }

    for (s=0; s < (num_loops*2); s++) 
    {
        memset(&loop->spans[s]->span,0,sizeof(struct zt_span));
        sprintf(loop->spans[s]->span.name,"ztloop/%d",s+1);
        sprintf(loop->spans[s]->span.desc,"LOOP device span %d looped with span %d.",
                s+1, (((s%2)==0)?(s+1):s-1) + 1);

        loop->spans[s]->span.spanconfig = ztloop_spanconfig;
        loop->spans[s]->span.chanconfig = ztloop_chanconfig;
        loop->spans[s]->span.startup = ztloop_startup;
        loop->spans[s]->span.shutdown = ztloop_shutdown;
        loop->spans[s]->span.open = ztloop_open;
        loop->spans[s]->span.close = ztloop_close;
        loop->spans[s]->span.ioctl = ztloop_ioctl;
	loop->spans[s]->span.rbsbits = ztloop_rbs;
	loop->spans[s]->span.flags |= ZT_FLAG_RBS;

        loop->spans[s]->span.chans = loop->spans[s]->chans;
        loop->spans[s]->span.channels = 31;
        loop->spans[s]->span.deflaw = ZT_LAW_ALAW;
        loop->spans[s]->span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_HDB3 | ZT_CONFIG_CCS | ZT_CONFIG_CRC4;
        init_waitqueue_head(&loop->spans[s]->span.maintq);
        loop->spans[s]->span.pvt = loop;
        loop->spans[s]->span.offset = s;

        for (i=0; i < loop->spans[s]->span.channels; i++) {
            memset(&(loop->spans[s]->chans[i]),0x0,sizeof(struct zt_chan));
            sprintf(loop->spans[s]->chans[i].name,"ztloop/%d/%d",s + 1,i + 1);
            loop->spans[s]->chans[i].pvt = loop;
            loop->spans[s]->chans[i].sigcap =  ZT_SIG_CLEAR | ZT_SIG_CAS;
            loop->spans[s]->chans[i].chanpos = i + 1;
            loop->spans[s]->chans[i].readchunk = loop->spans[((s%2)==0)?(s+1):s-1]->chans[i].swritechunk;
            loop->spans[s]->chans[i].writechunk = loop->spans[s]->chans[i].swritechunk;
        }

        if (zt_register(&loop->spans[s]->span,0)) 
        {
            printk(KERN_INFO "ztloop: unable to register zaptel span %d!\n",s+1);
            return -1;
        }
    }

    for (s=(num_loops*2); s < ((num_loops*2)+(num_taps*2)); s++) 
    {
        memset(&loop->spans[s]->span,0,sizeof(struct zt_span));
        sprintf(loop->spans[s]->span.name, "zttap/%d", s+1);
        sprintf(loop->spans[s]->span.desc, 
                "TAP device span %d - tap on TX of span %d.",
                s+1, s-(num_loops*2)+1);
        loop->spans[s]->span.spanconfig = ztloop_spanconfig;
        loop->spans[s]->span.chanconfig = ztloop_chanconfig;
        loop->spans[s]->span.startup = ztloop_startup;
        loop->spans[s]->span.shutdown = ztloop_shutdown;
        loop->spans[s]->span.open = ztloop_open;
        loop->spans[s]->span.close = ztloop_close;
        loop->spans[s]->span.ioctl = ztloop_ioctl;
	loop->spans[s]->span.rbsbits = ztloop_taprbs;
	loop->spans[s]->span.flags |= ZT_FLAG_RBS;

        loop->spans[s]->span.chans = loop->spans[s]->chans;
        loop->spans[s]->span.channels = 31;
        loop->spans[s]->span.deflaw = ZT_LAW_ALAW;
        loop->spans[s]->span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_HDB3 | ZT_CONFIG_CCS;
        init_waitqueue_head(&loop->spans[s]->span.maintq);
        loop->spans[s]->span.pvt = loop;
        loop->spans[s]->span.offset = s;

        for (i=0; i < loop->spans[s]->span.channels; i++) {
            memset(&(loop->spans[s]->chans[i]),0x0,sizeof(struct zt_chan));
            sprintf(loop->spans[s]->chans[i].name,"zttap/%d/%d",s + 1,i + 1);
            loop->spans[s]->chans[i].pvt = loop;
            loop->spans[s]->chans[i].sigcap =  ZT_SIG_CLEAR | ZT_SIG_CAS;
            loop->spans[s]->chans[i].chanpos = i + 1;
            /* Point the readchunk at the TX of what we are tapping */
            loop->spans[s]->chans[i].readchunk = loop->spans[s-(num_loops*2)]->chans[i].swritechunk;
            loop->spans[s]->chans[i].writechunk = loop->spans[s]->chans[i].swritechunk;
        }

        if (zt_register(&loop->spans[s]->span,0)) 
        {
            printk(KERN_INFO "ztloop: unable to register zaptel span %d!\n",s+1);
            return -1;
        }
    }
    return 0;
}


int init_module(void)
{
    int i;
#ifdef LINUX26
#ifdef USE_RTC
    int err;
#endif
#else
    int irq;
#ifdef DEFINE_SPINLOCK
    DEFINE_SPINLOCK(mylock);
#else
    spinlock_t mylock = SPIN_LOCK_UNLOCKED;
#endif
    
    if (uhci_devices==NULL) {
        printk ("ztloop: Uhci_devices pointer error.\n");
        return -ENODEV;
    }
    s=*uhci_devices;    /* uhci device */
    if (s==NULL) {
        printk ("ztloop: No uhci_device found.\n");
        return -ENODEV;
    }
#endif

    ztloop = kmalloc(sizeof(struct ztloop), GFP_KERNEL);
    if (ztloop == NULL) {
        printk("ztloop: Unable to allocate memory\n");
        return -ENOMEM;
    }
    memset(ztloop, 0x0, sizeof(struct ztloop));

    for (i=0; i < ((num_loops*2) + (num_taps*2)); i++)
    {
        ztloop->spans[i] = kmalloc(sizeof(struct ztloop_spans), GFP_KERNEL);
        if (ztloop->spans[i] == NULL)
        {
            for (i--; i > 0; i--)
            {
                kfree(ztloop->spans[i]);
            }
            kfree(ztloop);
            return -ENOMEM;
        }
    }

    if (ztloop_initialize(ztloop)) {
        printk("ztloop: Unable to intialize zaptel driver\n");
        kfree(ztloop);
        return -ENODEV;
    }

#ifdef LINUX26
    ztloop->counter = 0;
#ifdef USE_RTC
    ztloop->rtclock = SPIN_LOCK_UNLOCKED;
    ztloop->rtc_task.func = ztloop_rtc_interrupt;
    ztloop->rtc_task.private_data = ztloop;
    err = rtc_register(&ztloop->rtc_task);
    if (err < 0) {
        int s;
        printk("ztloop: Unable to register zaptel rtc driver\n");
        for (s=0; s < ((num_loops*2)+(num_taps*2)); s++) 
        {
            zt_unregister(&ztloop->spans[s]->span);
        }
        kfree(ztloop);
        return err;
    }
    /* Set default RTC interrupt rate to 1024Hz */
    if (!rtc_rate)
        rtc_rate = 1024;
    update_rtc_rate(ztloop);
    rtc_control(&ztloop->rtc_task, RTC_PIE_ON, 0);
    tasklet_init(&ztd_tlet, ztd_tasklet, 0);
#else
    init_timer(&timer);
    timer.function = ztloop_timer;
    timer.expires = jiffies + 1;
    add_timer(&timer);
#endif
#else
    irq=s->irq;
    spin_lock_irq(&mylock);
    free_irq(s->irq, s);    /* remove uhci_interrupt temporaly */
    if (request_irq (irq, ztloop_interrupt, SA_SHIRQ, "ztloop", ztloop)) {
        spin_unlock_irq(&mylock);
        err("Our request_irq %d failed!",irq);
        kfree(ztloop);
        return -EIO;
    }        /* we add our handler first, to assure, that our handler gets called first */
    if (request_irq (irq, uhci_interrupt, SA_SHIRQ, s->uhci_pci->driver->name, s)) {
        spin_unlock_irq(&mylock);
        err("Original request_irq %d failed!",irq);
    }
    spin_unlock_irq(&mylock);

    /* add td to usb host controller interrupt queue */
    alloc_td(s, &td, 0);
    fill_td(td, TD_CTRL_IOC, 0, 0);
    insert_td_horizontal(s, s->int_chain[0], td);    /* use int_chain[0] to get 1ms interrupts */
#endif    

    if (debug)
        printk("ztloop: init() finished\n");
    return 0;
}


void cleanup_module(void)
{
    int s;
#ifdef LINUX26
#ifdef USE_RTC
    if (taskletpending) {
        tasklet_disable(&ztd_tlet);
        tasklet_kill(&ztd_tlet);
    }
    rtc_control(&ztloop->rtc_task, RTC_PIE_OFF, 0);
    rtc_unregister(&ztloop->rtc_task);
#else
    del_timer(&timer);
#endif
#else
    free_irq(s->irq, ztloop);  /* disable interrupts */
#endif
    for (s=0; s < ((num_loops*2)+(num_taps*2)); s++) 
    {
        zt_unregister(&ztloop->spans[s]->span);
    }
    kfree(ztloop);
#ifndef LINUX26
    unlink_td(s, td, 1);
    delete_desc(s, td);
#endif
    if (debug)
        printk("ztloop: cleanup() finished\n");
}



#ifdef LINUX26
module_param(debug, int, 0600);
module_param(num_loops, int, 0600);
module_param(num_taps, int, 0600);
#ifdef USE_RTC
module_param(rtc_rate, int, 0600);
#endif
#else
MODULE_PARM(debug, "i");
MODULE_PARM(num_taps, "i");
#endif

#ifndef LINUX26
MODULE_PARM(monitor, "i");
#endif
MODULE_DESCRIPTION("Loopback Zaptel Driver");
MODULE_AUTHOR("Druid Software Ltd (liamk)");
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#endif

