!C99Shell v. 1.0 pre-release build #13!

Software: Apache/2.0.54 (Unix) mod_perl/1.99_09 Perl/v5.8.0 mod_ssl/2.0.54 OpenSSL/0.9.7l DAV/2 FrontPage/5.0.2.2635 PHP/4.4.0 mod_gzip/2.0.26.1a 

uname -a: Linux snow.he.net 4.4.276-v2-mono-1 #1 SMP Wed Jul 21 11:21:17 PDT 2021 i686 

uid=99(nobody) gid=98(nobody) groups=98(nobody) 

Safe-mode: OFF (not secure)

/usr/src/linux-2.4.18-xfs-1.1/arch/arm/mach-sa1100/   drwxr-xr-x
Free 318.34 GB of 458.09 GB (69.49%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     dma-sa1100.c (13.84 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/*
 * arch/arm/kernel/dma-sa1100.c
 *
 * Support functions for the SA11x0 internal DMA channels.
 * (see also Documentation/arm/SA1100/DMA)
 *
 * Copyright (C) 2000 Nicolas Pitre
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/errno.h>

#include <asm/system.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/mach/dma.h>


#undef DEBUG
#ifdef DEBUG
#define DPRINTK( s, arg... )  printk( "dma<%s>: " s, dma->device_id , ##arg )
#else
#define DPRINTK( x... )
#endif


/*
 * DMA control register structure
 */
typedef struct {
    volatile u_long DDAR;
    volatile u_long SetDCSR;
    volatile u_long ClrDCSR;
    volatile u_long RdDCSR;
    volatile dma_addr_t DBSA;
    volatile u_long DBTA;
    volatile dma_addr_t DBSB;
    volatile u_long DBTB;
} dma_regs_t;

#include "dma.h"

sa1100_dma_t dma_chan[MAX_SA1100_DMA_CHANNELS];

/*
 * Maximum physical DMA buffer size
 */
#define MAX_DMA_SIZE        0x1fff
#define MAX_DMA_ORDER        12


/*
 * DMA processing...
 */

static inline int start_sa1100_dma(sa1100_dma_t * dma, dma_addr_t dma_ptr, int size)
{
    dma_regs_t *regs = dma->regs;
    int status;

    status = regs->RdDCSR;

    /* If both DMA buffers are started, there's nothing else we can do. */
    if ((status & (DCSR_STRTA | DCSR_STRTB)) == (DCSR_STRTA | DCSR_STRTB)) {
        DPRINTK("start: st %#x busy\n", status);
        return -EBUSY;
    }

    if (((status & DCSR_BIU) && (status & DCSR_STRTB)) ||
        (!(status & DCSR_BIU) && !(status & DCSR_STRTA))) {
        if (status & DCSR_DONEA) {
            /* give a chance for the interrupt to be processed */
            goto irq_pending;
        }
        regs->DBSA = dma_ptr;
        regs->DBTA = size;
        regs->SetDCSR = DCSR_STRTA | DCSR_IE | DCSR_RUN;
        DPRINTK("start a=%#x s=%d on A\n", dma_ptr, size);
    } else {
        if (status & DCSR_DONEB) {
            /* give a chance for the interrupt to be processed */
            goto irq_pending;
        }
        regs->DBSB = dma_ptr;
        regs->DBTB = size;
        regs->SetDCSR = DCSR_STRTB | DCSR_IE | DCSR_RUN;
        DPRINTK("start a=%#x s=%d on B\n", dma_ptr, size);
    }

    return 0;

irq_pending:
    return -EAGAIN;
}


static int start_dma(sa1100_dma_t *dma, dma_addr_t dma_ptr, int size)
{
    if (channel_is_sa1111_sac(dma - dma_chan))
        return start_sa1111_sac_dma(dma, dma_ptr, size);
    return start_sa1100_dma(dma, dma_ptr, size);
}


/* This must be called with IRQ disabled */
static void process_dma(sa1100_dma_t * dma)
{
    dma_buf_t *buf;
    int chunksize;

    for (;;) {
        buf = dma->tail;

        if (!buf || dma->stopped) {
            /* no more data available */
            DPRINTK("process: no more buf (dma %s)\n",
                dma->curr ? "active" : "inactive");
            /*
             * Some devices may require DMA still sending data
             * at any time for clock reference, etc.
             * Note: if there is still a data buffer being
             * processed then the ref count is negative.  This
             * allows for the DMA termination to be accounted in
             * the proper order.
             */
            if (dma->spin_size && dma->spin_ref >= 0) {
                chunksize = dma->spin_size;
                if (chunksize > MAX_DMA_SIZE)
                    chunksize = (1 << MAX_DMA_ORDER);
                while (start_dma(dma, dma->spin_addr, chunksize) == 0)
                    dma->spin_ref++;
                if (dma->curr != NULL)
                    dma->spin_ref = -dma->spin_ref;
            }
            break;
        }

        /*
         * Let's try to start DMA on the current buffer.
         * If DMA is busy then we break here.
         */
        chunksize = buf->size;
        if (chunksize > MAX_DMA_SIZE)
            chunksize = (1 << MAX_DMA_ORDER);
        DPRINTK("process: b=%#x s=%d\n", (int) buf->id, buf->size);
        if (start_dma(dma, buf->dma_ptr, chunksize) != 0)
            break;
        if (!dma->curr)
            dma->curr = buf;
        buf->ref++;
        buf->dma_ptr += chunksize;
        buf->size -= chunksize;
        if (buf->size == 0) {
            /* current buffer is done: move tail to the next one */
            dma->tail = buf->next;
            DPRINTK("process: next b=%#x\n", (int) dma->tail);
        }
    }
}


/* This must be called with IRQ disabled */
void sa1100_dma_done (sa1100_dma_t *dma)
{
    dma_buf_t *buf = dma->curr;

    if (dma->spin_ref > 0) {
        dma->spin_ref--;
    } else if (buf) {
        buf->ref--;
        if (buf->ref == 0 && buf->size == 0) {
            /*
             * Current buffer is done.
             * Move current reference to the next one and send
             * the processed buffer to the callback function,
             * then discard it.
             */
            DPRINTK("IRQ: buf done\n");
            dma->curr = buf->next;
            if (dma->curr == NULL)
                dma->spin_ref = -dma->spin_ref;
            if (dma->head == buf)
                dma->head = NULL;
            if (dma->callback) {
                int size = buf->dma_ptr - buf->dma_start;
                dma->callback(buf->id, size);
            }
            kfree(buf);
        }
    }

    process_dma(dma);
}


static void dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
    sa1100_dma_t *dma = (sa1100_dma_t *) dev_id;
    int status = dma->regs->RdDCSR;

    DPRINTK("IRQ: b=%#x st=%#x\n", (int) dma->curr->id, status);

    if (status & (DCSR_ERROR)) {
        printk(KERN_ERR "DMA on \"%s\" caused an error\n", dma->device_id);
        dma->regs->ClrDCSR = DCSR_ERROR;
    }

    dma->regs->ClrDCSR = status & (DCSR_DONEA | DCSR_DONEB);
    if (status & DCSR_DONEA)
        sa1100_dma_done (dma);
    if (status & DCSR_DONEB)
        sa1100_dma_done (dma);
}


/*
 * DMA interface functions
 */

static spinlock_t dma_list_lock;

int sa1100_request_dma (dmach_t * channel, const char *device_id,
            dma_device_t device)
{
    sa1100_dma_t *dma = NULL;
    dma_regs_t *regs;
    int i, err;

    *channel = -1;        /* to be sure we catch the freeing of a misregistered channel */

    err = 0;
    spin_lock(&dma_list_lock);
    for (i = 0; i < SA1100_DMA_CHANNELS; i++) {
        if (dma_chan[i].in_use) {
            if (dma_chan[i].device == device) {
                err = -EBUSY;
                break;
            }
        } else if (!dma) {
            dma = &dma_chan[i];
        }
    }
    if (!err) {
           if (dma)
               dma->in_use = 1;
           else
               err = -ENOSR;
    }
    spin_unlock(&dma_list_lock);
    if (err)
        return err;

    err = request_irq(dma->irq, dma_irq_handler, SA_INTERRUPT,
              device_id, (void *) dma);
    if (err) {
        printk(KERN_ERR
               "%s: unable to request IRQ %d for DMA channel\n",
               device_id, dma->irq);
        return err;
    }

    *channel = dma - dma_chan;
    dma->device_id = device_id;
    dma->device = device;
    dma->callback = NULL;
    dma->spin_size = 0;

    regs = dma->regs;
    regs->ClrDCSR =
        (DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB |
         DCSR_IE | DCSR_ERROR | DCSR_RUN);
    regs->DDAR = device;
    DPRINTK("requested\n");
    return 0;
}


int sa1100_dma_set_callback(dmach_t channel, dma_callback_t cb)
{
    sa1100_dma_t *dma = &dma_chan[channel];

    if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
        return -EINVAL;

    dma->callback = cb;
    DPRINTK("cb = %p\n", cb);
    return 0;
}


int sa1100_dma_set_spin(dmach_t channel, dma_addr_t addr, int size)
{
    sa1100_dma_t *dma = &dma_chan[channel];
    int flags;

    if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
        return -EINVAL;

    DPRINTK("set spin %d at %#x\n", size, addr);
    local_irq_save(flags);
    dma->spin_addr = addr;
    dma->spin_size = size;
    if (size)
        process_dma(dma);
    local_irq_restore(flags);
    return 0;
}


int sa1100_dma_queue_buffer(dmach_t channel, void *buf_id,
                dma_addr_t data, int size)
{
    sa1100_dma_t *dma;
    dma_buf_t *buf;
    int flags;

    dma = &dma_chan[channel];
    if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
        return -EINVAL;

    buf = kmalloc(sizeof(*buf), GFP_ATOMIC);
    if (!buf)
        return -ENOMEM;

    buf->next = NULL;
    buf->ref = 0;
    buf->dma_ptr = buf->dma_start = data;
    buf->size = size;
    buf->id = buf_id;
    DPRINTK("queueing b=%#x a=%#x s=%d\n", (int) buf_id, data, size);

    local_irq_save(flags);
    if (dma->head)
        dma->head->next = buf;
    dma->head = buf;
    if (!dma->tail)
        dma->tail = buf;
    process_dma(dma);
    local_irq_restore(flags);

    return 0;
}


int sa1100_dma_get_current(dmach_t channel, void **buf_id, dma_addr_t *addr)
{
    sa1100_dma_t *dma = &dma_chan[channel];
    dma_regs_t *regs;
    int flags, ret;

    if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
        return -EINVAL;

    if (channel_is_sa1111_sac(channel))
        return sa1111_dma_get_current(channel, buf_id, addr);

    regs = dma->regs;
    local_irq_save(flags);
    if (dma->curr && dma->spin_ref <= 0) {
        dma_buf_t *buf = dma->curr;
        int status, using_bufa;

        status = regs->RdDCSR;
        /*
         * If we got here, that's because there is, or recently was, a
         * buffer being processed.  We must determine whether buffer
         * A or B is active.  Two possibilities: either we are
         * in the middle of a buffer, or the DMA controller just
         * switched to the next toggle but the interrupt hasn't been
         * serviced yet.  The former case is straight forward.  In
         * the later case, we'll do like if DMA is just at the end
         * of the previous toggle since all registers haven't been
         * reset yet.  This goes around the edge case and since we're
         * always a little behind anyways it shouldn't make a big
         * difference.  If DMA has been stopped prior calling this
         * then the position is always exact.
         */
        using_bufa = ((!(status & DCSR_BIU) &&  (status & DCSR_STRTA)) ||
                  ( (status & DCSR_BIU) && !(status & DCSR_STRTB)));
        if (buf_id)
            *buf_id = buf->id;
        *addr = (using_bufa) ? regs->DBSA : regs->DBSB;
        /*
         * Clamp funky pointers sometimes returned by the hardware
         * on completed DMA transfers
         */
        if (*addr < buf->dma_start ||
            *addr > buf->dma_ptr)
            *addr = buf->dma_ptr;
        DPRINTK("curr_pos: b=%#x a=%#x\n", (int)dma->curr->id, *addr);
        ret = 0;
    } else if (dma->tail && dma->stopped) {
        dma_buf_t *buf = dma->tail;
        if (buf_id)
            *buf_id = buf->id;
        *addr = buf->dma_ptr;
        ret = 0;
    } else {
        if (buf_id)
            *buf_id = NULL;
        *addr = 0;
        ret = -ENXIO;
    }
    local_irq_restore(flags);
    return ret;
}


int sa1100_dma_stop(dmach_t channel)
{
    sa1100_dma_t *dma = &dma_chan[channel];
    int flags;

    if (channel_is_sa1111_sac(channel))
        return sa1111_dma_stop(channel);

    if (dma->stopped)
        return 0;
    local_irq_save(flags);
    dma->stopped = 1;
    /*
     * Stop DMA and tweak state variables so everything could restart
     * from there when resume/wakeup occurs.
     */
    dma->regs->ClrDCSR = DCSR_RUN | DCSR_IE;
    if (dma->curr) {
        dma_buf_t *buf = dma->curr;
        if (dma->spin_ref <= 0) {
            dma_addr_t curpos;
            sa1100_dma_get_current(channel, NULL, &curpos);
            buf->size += buf->dma_ptr - curpos;
            buf->dma_ptr = curpos;
        }
        buf->ref = 0;
        dma->tail = buf;
        dma->curr = NULL;
    }
    dma->spin_ref = 0;
    dma->regs->ClrDCSR = DCSR_STRTA|DCSR_STRTB|DCSR_DONEA|DCSR_DONEB;
    process_dma(dma);
    local_irq_restore(flags);
    return 0;
}


int sa1100_dma_resume(dmach_t channel)
{
    sa1100_dma_t *dma = &dma_chan[channel];

    if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
        return -EINVAL;

    if (channel_is_sa1111_sac(channel))
        return sa1111_dma_resume(channel);

    if (dma->stopped) {
        int flags;
        save_flags_cli(flags);
        dma->stopped = 0;
        dma->spin_ref = 0;
        process_dma(dma);
        restore_flags(flags);
    }
    return 0;
}


int sa1100_dma_flush_all(dmach_t channel)
{
    sa1100_dma_t *dma = &dma_chan[channel];
    dma_buf_t *buf, *next_buf;
    int flags;

    if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
        return -EINVAL;

    local_irq_save(flags);
    if (channel_is_sa1111_sac(channel))
        sa1111_reset_sac_dma(channel);
    else
        dma->regs->ClrDCSR = DCSR_STRTA|DCSR_STRTB|DCSR_DONEA|DCSR_DONEB|DCSR_RUN|DCSR_IE;
    buf = dma->curr;
    if (!buf)
        buf = dma->tail;
    dma->head = dma->tail = dma->curr = NULL;
    dma->stopped = 0;
    dma->spin_ref = 0;
    process_dma(dma);
    local_irq_restore(flags);
    while (buf) {
        next_buf = buf->next;
        kfree(buf);
        buf = next_buf;
    }
    DPRINTK("flushed\n");
    return 0;
}


void sa1100_free_dma(dmach_t channel)
{
    sa1100_dma_t *dma;

    if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS)
        return;

    dma = &dma_chan[channel];
    if (!dma->in_use) {
        printk(KERN_ERR "Trying to free free DMA%d\n", channel);
        return;
    }

    sa1100_dma_set_spin(channel, 0, 0);
    sa1100_dma_flush_all(channel);

    if (channel_is_sa1111_sac(channel)) {
        sa1111_cleanup_sac_dma(channel);
    } else {
        free_irq(IRQ_DMA0 + channel, (void *) dma);
    }
    dma->in_use = 0;

    DPRINTK("freed\n");
}


EXPORT_SYMBOL(sa1100_request_dma);
EXPORT_SYMBOL(sa1100_dma_set_callback);
EXPORT_SYMBOL(sa1100_dma_set_spin);
EXPORT_SYMBOL(sa1100_dma_queue_buffer);
EXPORT_SYMBOL(sa1100_dma_get_current);
EXPORT_SYMBOL(sa1100_dma_stop);
EXPORT_SYMBOL(sa1100_dma_resume);
EXPORT_SYMBOL(sa1100_dma_flush_all);
EXPORT_SYMBOL(sa1100_free_dma);


#ifdef CONFIG_PM
/* Drivers should call this from their PM callback function */

int sa1100_dma_sleep(dmach_t channel)
{
        sa1100_dma_t *dma = &dma_chan[channel];
    int orig_state;

    if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
        return -EINVAL;

    if (channel_is_sa1111_sac(channel)) {
        /* We'll cheat a little until someone actually
         * write the real thing.
         */
        sa1111_reset_sac_dma(channel);
        return 0;
    }

    orig_state = dma->stopped;
    sa1100_dma_stop(channel);
    dma->regs->ClrDCSR = DCSR_RUN | DCSR_IE | DCSR_STRTA | DCSR_STRTB;
    dma->stopped = orig_state;
    dma->spin_ref = 0;
    return 0;
}

int sa1100_dma_wakeup(dmach_t channel)
{
        sa1100_dma_t *dma = &dma_chan[channel];
    dma_regs_t *regs;
    int flags;

    if ((unsigned)channel >= MAX_SA1100_DMA_CHANNELS || !dma->in_use)
        return -EINVAL;

    if (channel_is_sa1111_sac(channel)) {
        /* We'll cheat a little until someone actually
         * write the real thing.
         */
        return 0;
    }

    regs = dma->regs;
    regs->ClrDCSR =
        (DCSR_DONEA | DCSR_DONEB | DCSR_STRTA | DCSR_STRTB |
         DCSR_IE | DCSR_ERROR | DCSR_RUN);
    regs->DDAR = dma->device;
    local_irq_save(flags);
    process_dma(dma);
    local_irq_restore(flags);
    return 0;
}

EXPORT_SYMBOL(sa1100_dma_sleep);
EXPORT_SYMBOL(sa1100_dma_wakeup);

#endif


static int __init sa1100_init_dma(void)
{
    int channel;
    for (channel = 0; channel < SA1100_DMA_CHANNELS; channel++) {
        dma_chan[channel].regs =
            (dma_regs_t *) &DDAR(channel);
        dma_chan[channel].irq = IRQ_DMA0 + channel;
    }
    return 0;
}

__initcall(sa1100_init_dma);

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 1.0 pre-release build #13 powered by Captain Crunch Security Team | http://ccteam.ru | Generation time: 0.0054 ]--