/*******************************************************************************
 **
 **_DebugFSWrite
 **
 *******************************************************************************/
static ssize_t
_DebugFSWrite (
                struct file *file ,
                const char __user * buffer ,
                size_t length ,
                loff_t * offset
                )
{
    caddr_t message = NULL ;
    int n ;
    gcsDebugFileSystemNode*node ;

    /* get the metadata about this log */
    if ( ( node = _GetNodeInfo ( file->f_dentry->d_inode ) ) == NULL )
    {
        return - EIO ;
    }

    if ( down_interruptible ( gcmkNODE_SEM ( node ) ) )
    {
        return - ERESTARTSYS ;
    }

    /* if the message is longer than the buffer, just take the beginning
     * of it, in hopes that the reader (if any) will have time to read
     * before we wrap around and obliterate it */
    n = gcmkMIN ( length , node->size - 1 ) ;

    /* make sure we have the memory for it */
    if ( ( message = kmalloc ( n , GFP_KERNEL ) ) == NULL )
    {
        up ( gcmkNODE_SEM ( node ) ) ;
        return - ENOMEM ;
    }

    /* copy into our temp buffer */
    if ( copy_from_user ( message , buffer , n ) > 0 )
    {
        up ( gcmkNODE_SEM ( node ) ) ;
        kfree ( message ) ;
        return - EFAULT ;
    }

    /* now copy it into the circular buffer and free our temp copy */
    _WriteToNode ( node , message , n ) ;

    kfree ( message ) ;
    up ( gcmkNODE_SEM ( node ) ) ;

    /* wake up any readers that might be waiting for the data.  we call
     * schedule in the vague hope that a reader will run before the
     * writer's next write, to avoid losing data. */
    wake_up_interruptible ( gcmkNODE_READQ ( node ) ) ;

    return n ;
}
gctINT
gckDebugFileSystemCreateNode (
                               IN gctINT SizeInKB ,
                               IN gctCONST_STRING ParentName ,
                               IN gctCONST_STRING NodeName ,
                               OUT gcsDebugFileSystemNode **Node
                               )
{
    gcsDebugFileSystemNode*node ;
    /* allocate space for our metadata and initialize it */
    if ( ( node = kmalloc ( sizeof (gcsDebugFileSystemNode ) , GFP_KERNEL ) ) == NULL )
        goto struct_malloc_failed ;

    /*Zero it out*/
    memset ( node , 0 , sizeof (gcsDebugFileSystemNode ) ) ;

    /*Init the sync primitives*/
#if defined(DECLARE_WAIT_QUEUE_HEAD)
    init_waitqueue_head ( gcmkNODE_READQ ( node ) ) ;
#else
    init_waitqueue ( gcmkNODE_READQ ( node ) ) ;
#endif

#if defined(DECLARE_WAIT_QUEUE_HEAD)
    init_waitqueue_head ( gcmkNODE_WRITEQ ( node ) ) ;
#else
    init_waitqueue ( gcmkNODE_WRITEQ ( node ) ) ;
#endif
    sema_init ( gcmkNODE_SEM ( node ) , 1 ) ;
    /*End the sync primitives*/


    /* figure out how much of a buffer this should be and allocate the buffer */
    node->size = 1024 * SizeInKB ;
    if ( ( node->data = ( char * ) vmalloc ( sizeof (char ) * node->size ) ) == NULL )
        goto data_malloc_failed ;

    /*creating the debug file system*/
    node->parent = debugfs_create_dir ( ParentName , NULL ) ;

    /*creating the file*/
    node->filen = debugfs_create_file ( NodeName , S_IRUGO | S_IWUSR , node->parent , NULL ,
                                        &debugfs_operations ) ;

    /* add it to our linked list */
    node->next = gc_dbgfs.linkedlist ;
    gc_dbgfs.linkedlist = node ;

    /* pass the struct back */
    *Node = node ;
    return 0 ;

    vfree ( node->data ) ;
data_malloc_failed:
    kfree ( node ) ;
struct_malloc_failed:
    return - ENOMEM ;
}
/*******************************************************************************
 **
 **  gckDebugFileSystemFreeNode
 **
 **
 **  INPUT:
 **
 **  OUTPUT:
 **
 *******************************************************************************/
void
gckDebugFileSystemFreeNode (
                             IN gcsDebugFileSystemNode * Node
                             )
{

    gcsDebugFileSystemNode **ptr ;

    if ( Node == NULL )
    {
        printk ( "null passed to free_vinfo\n" ) ;
        return ;
    }

    down ( gcmkNODE_SEM ( Node ) ) ;
    /*free data*/
    vfree ( Node->data ) ;

    /*Close Debug fs*/
    if ( Node->filen )
    {
        debugfs_remove ( Node->filen ) ;
    }
    if ( Node->parent )
    {
        debugfs_remove ( Node->parent ) ;
    }

    /* now delete the node from the linked list */
    ptr = & ( gc_dbgfs.linkedlist ) ;
    while ( *ptr != Node )
    {
        if ( ! *ptr )
        {
            printk ( "corrupt info list!\n" ) ;
            break ;
        }
        else
            ptr = & ( ( **ptr ).next ) ;
    }
    *ptr = Node->next ;
    up ( gcmkNODE_SEM ( Node ) ) ;
}
/*******************************************************************************
 **
 ** _DebugFSPrint
 **
 **
 *******************************************************************************/
static void
_DebugFSPrint (
                IN unsigned int ArgumentSize ,
                IN const char* Message ,
                IN gctDBGARGS Arguments

                )
{
    char buffer[MAX_LINE_SIZE] ;
    int len ;
    down ( gcmkNODE_SEM ( gc_dbgfs.currentNode ) ) ;
    len = vsnprintf ( buffer , sizeof (buffer ) , Message , *( va_list * ) & Arguments ) ;
    buffer[len] = '\0' ;

    if ( buffer[len - 1] != '\n' )
    {
        buffer[len ++] = '\n' ;
        buffer[len] = '\0' ;
    }
    _AppendString ( gc_dbgfs.currentNode , buffer , len ) ;
    up ( gcmkNODE_SEM ( gc_dbgfs.currentNode ) ) ;
    wake_up_interruptible ( gcmkNODE_READQ ( gc_dbgfs.currentNode ) ) ; /* blocked in read*/
}
/*******************************************************************************
 **
 **   _DebugFSRead
 **
 *******************************************************************************/
static ssize_t
_DebugFSRead (
               struct file *file ,
               char __user * buffer ,
               size_t length ,
               loff_t * offset
               )
{
    int retval ;
    caddr_t data_to_return ;
    gcsDebugFileSystemNode* node ;
    /* get the metadata about this emlog */
    if ( ( node = _GetNodeInfo ( file->f_dentry->d_inode ) ) == NULL )
    {
        printk ( "debugfs_read: record not found\n" ) ;
        return - EIO ;
    }

    if ( down_interruptible ( gcmkNODE_SEM ( node ) ) )
    {
        return - ERESTARTSYS ;
    }

    /* wait until there's data available (unless we do nonblocking reads) */
    while ( *offset >= gcmkNODE_FIRST_EMPTY_BYTE ( node ) )
    {
        up ( gcmkNODE_SEM ( node ) ) ;
        if ( file->f_flags & O_NONBLOCK )
        {
            return - EAGAIN ;
        }
        if ( wait_event_interruptible ( ( *( gcmkNODE_READQ ( node ) ) ) , ( *offset < gcmkNODE_FIRST_EMPTY_BYTE ( node ) ) ) )
        {
            return - ERESTARTSYS ; /* signal: tell the fs layer to handle it */
        }
        /* otherwise loop, but first reacquire the lock */
        if ( down_interruptible ( gcmkNODE_SEM ( node ) ) )
        {
            return - ERESTARTSYS ;
        }
    }
    data_to_return = _ReadFromNode ( node , &length , offset ) ;
    if ( data_to_return == NULL )
    {
        retval = 0 ;
        goto unlock ;
    }
    if ( copy_to_user ( buffer , data_to_return , length ) > 0 )
    {
        retval = - EFAULT ;
    }
    else
    {
        retval = length ;
    }
    kfree ( data_to_return ) ;
unlock:
    up ( gcmkNODE_SEM ( node ) ) ;
    wake_up_interruptible ( gcmkNODE_WRITEQ ( node ) ) ;
    return retval ;
}