⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dma.c

📁 自己根据lkd和情境分析
💻 C
字号:
// LAPUTA fix 030718// dma.h undefine HOOK_LOOK_INT// because it do not need DMA processing ///* * linux/arch/arm/mach-s3c2410/dma.c * * Copyright (C) 2001 MIZI Research, Inc. * * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * * * * History * */#include <linux/module.h>#include <linux/init.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/errno.h>#include <asm/irq.h>#include <asm/hardware.h>#include <asm/io.h>#include <asm/dma.h>#include "dma.h"/* debug macros */#undef DEBUG#ifdef DEBUG#define DPRINTK( s, arg... )  printk( "dma<%s>: " s, dma->device_id , ##arg )#else#define DPRINTK( x... )#endif/* * DMA processing... */static void process_dma(s3c2410_dma_t *dma){	dma_buf_t *buf, *next_buf;	dma_regs_t *regs = dma->regs;	int data_size, data_cnt;	dma_device_t *device;	buf = dma->head;	if (buf && (!dma->active)) {		if (buf->write) {			device = &dma->write;			regs->DISRCC = device->src_ctl;			regs->DISRC = DMA_BASE_ADDR(buf->dma_start);			regs->DIDSTC = device->dst_ctl;			regs->DIDST = device->dst;		} else {			device = &dma->read;			regs->DISRCC = device->src_ctl;			regs->DISRC = device->src;			regs->DIDSTC = device->dst_ctl;			regs->DIDST = DMA_BASE_ADDR(buf->dma_start);		}		data_size = readDSZ(device->ctl);		switch(data_size) {			case DSZ_BYTE: data_cnt = TX_CNT(buf->size); break;			case DSZ_HALFWORD: data_cnt = TX_CNT(buf->size/2); break;			default: data_cnt = TX_CNT(buf->size/4); break;		}		regs->DCON = device->ctl | data_cnt;		regs->DMASKTRIG = (DMA_STOP_CLR | CHANNEL_ON | DMA_SW_REQ_CLR);		dma->curr = buf;		next_buf = dma->head->next;		dma->head = next_buf;		if (!next_buf)			dma->tail = NULL;		dma->active = 1;		dma->queue_count--;		DPRINTK("start dma_ptr=%#x size=%d\n", buf->dma_start, buf->size);		DPRINTK("number of buffers in queue: %ld\n", dma->queue_count);#ifdef HOOK_LOST_INT		if (buf->write) start_dma_timer();#endif	}}static inline void s3c2410_dma_done(s3c2410_dma_t *dma){	dma_buf_t *buf = dma->curr;	dma_callback_t callback;	if (buf->write) callback = dma->write.callback;	else callback = dma->read.callback;#ifdef HOOK_LOST_INT	stop_dma_timer();#endif	DPRINTK("IRQ: b=%#x st=%ld\n", (int)buf->id, (long)dma->regs->DSTAT);	if (callback)		callback(buf->id, buf->size);	kfree(buf);	dma->active = 0;	process_dma(dma);}static void dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs){	s3c2410_dma_t *dma = (s3c2410_dma_t *)dev_id;	DPRINTK(__FUNCTION__"\n");		s3c2410_dma_done(dma);}#ifdef HOOK_LOST_INTstatic void dma_timer_irq_handler(int irq, void *dev_id, struct pt_regs *regs){	s3c2410_dma_t *dma = NULL;	int channel;	//DPRINTK("in timer interrupts\r\n");	for (channel = 0; channel < MAX_S3C2410_DMA_CHANNELS; channel++) {		dma = &dma_chan[channel];		if ((dma->in_use == 1) && (dma->regs->DSTAT == 0x0) && (dma->active)) {			DPRINTK(__FUNCTION__" channel=%d\n", (int)channel);			s3c2410_dma_done(dma);		}	}}#endif/* * DMA interface functions */static int fill_dma_source(int channel, const char *dev_name, 			   dma_device_t *write, dma_device_t *read){	int source;	dma_type_t *dma_type = dma_types[channel];			for(source=0;source<4;source++) {		if (strcmp(dma_type[source].name, dev_name) == 0)			break;	}	if (source >= 4) return -1;	dma_type += source;	write->src = dma_type->write_src;	write->dst = dma_type->write_dst;	write->ctl = dma_type->write_ctl;	write->src_ctl = dma_type->write_src_ctl;	write->dst_ctl = dma_type->write_dst_ctl;	read->src = dma_type->read_src;	read->dst = dma_type->read_dst;	read->ctl = dma_type->read_ctl;	read->src_ctl = dma_type->read_src_ctl;	read->dst_ctl = dma_type->read_dst_ctl;	return 0;}static spinlock_t dma_list_lock;int s3c2410_request_dma(const char *device_id, dmach_t channel,			dma_callback_t write_cb, dma_callback_t read_cb){	s3c2410_dma_t *dma;	int err;	if ((channel < 0) || (channel >= MAX_S3C2410_DMA_CHANNELS)) {		printk(KERN_ERR "%s: not support #%d DMA channel\n", device_id, channel);		return  -ENODEV;	}	err = 0;	spin_lock(&dma_list_lock);	dma = &dma_chan[channel];	if (dma->in_use) {		printk(KERN_ERR "%s: DMA channel is busy\n", device_id);		err = -EBUSY;	} else {		dma->in_use = 1;	}	spin_unlock(&dma_list_lock);	if (err)		return err;	err = fill_dma_source(channel, device_id, &dma->write, &dma->read);	if (err < 0) {		printk(KERN_ERR "%s: can not found this devcie\n", device_id);		dma->in_use = 0;		return err;	}	err = request_irq(dma->irq, dma_irq_handler, 0 * 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);		dma->in_use = 0;		return err;	}	dma->device_id = device_id;	dma->head = dma->tail = dma->curr = NULL;	dma->write.callback = write_cb;	dma->read.callback = read_cb;	DPRINTK("write cb = %p, read cb = %p\n", dma->write.callback, dma->read.callback);	DPRINTK("requested\n");	return 0;}int s3c2410_dma_queue_buffer(dmach_t channel, void *buf_id,			     dma_addr_t data, int size, int write){	s3c2410_dma_t *dma;	dma_buf_t *buf;	int flags;		dma = &dma_chan[channel];	if ((channel >= MAX_S3C2410_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_start = data;	buf->size = size;	buf->id = buf_id;	buf->write = write;	DPRINTK("queueing b=%#x, a=%#x, s=%d, w=%d\n", (int) buf_id, data, size, write);	local_irq_save(flags);	if (dma->tail)		dma->tail->next = buf;	else		dma->head = buf;		dma->tail = buf;	buf->next = NULL;	dma->queue_count++;	DPRINTK("number of buffers in queue: %ld\n", dma->queue_count);	process_dma(dma);	local_irq_restore(flags);	return 0;}int s3c2410_dma_get_current(dmach_t channel, void **buf_id, dma_addr_t *addr){	s3c2410_dma_t *dma = &dma_chan[channel];	dma_regs_t *regs;	int flags, ret;		if ((channel >= MAX_S3C2410_DMA_CHANNELS) || (!dma->in_use)) 		return -EINVAL;	regs = dma->regs;	local_irq_save(flags);	if (dma->curr) {		dma_buf_t *buf = dma->curr;		int status = regs->DSTAT;		if (buf_id)			*buf_id = buf->id;		if (status > 0)			*addr = regs->DCSRC;		DPRINTK("curr_pos: b=%#x a=%#x\n", (int)dma->curr->id, *addr);		ret = 0;	} else if (dma->head && !dma->active) {		dma_buf_t *buf = dma->head;		if (buf_id)			*buf_id = buf->id;		*addr = buf->dma_start;		ret = 0;	} else {		if (buf_id)			*buf_id = NULL;		*addr = 0;		ret = -ENXIO;	}	local_irq_restore(flags);	return ret;}int s3c2410_dma_stop(dmach_t channel){	s3c2410_dma_t *dma = &dma_chan[channel];	dma_buf_t *buf = dma->curr;	dma_regs_t *regs = dma->regs;	dma_callback_t callback;	int flags;	if (!dma->active)		return 0;	local_irq_save(flags);#ifdef HOOK_LOST_INT	stop_dma_timer();#endif	regs->DMASKTRIG = DMA_STOP; 	if (buf->write) callback = dma->write.callback;	else callback = dma->read.callback;	if (callback)		callback(buf->id, buf->size);	kfree(buf);	dma->active = 0;	process_dma(dma);	local_irq_restore(flags);	DPRINTK("dma stopped\n");	return 0;}int s3c2410_dma_flush_all(dmach_t channel){	s3c2410_dma_t *dma = &dma_chan[channel];	dma_buf_t *buf, *next_buf;	int flags;	if ((channel >= MAX_S3C2410_DMA_CHANNELS) || (!dma->in_use))		return -EINVAL;	local_irq_save(flags);	dma->regs->DMASKTRIG = DMASKTRIG_STOP;	buf = dma->head;	dma->head = dma->tail = dma->curr = NULL;	dma->active = 0;	dma->queue_count = 0;	dma->active = 0;	local_irq_restore(flags);	while (buf) {		next_buf = buf->next;		kfree(buf);		buf = next_buf;	}	DPRINTK("flushed\n");	return 0;}void s3c2410_free_dma(dmach_t channel){	s3c2410_dma_t *dma;	if (channel >= MAX_S3C2410_DMA_CHANNELS)		return;	dma = &dma_chan[channel];	if (!dma->in_use) {		printk(KERN_ERR "Trying to free DMA%d\n", channel);		return;	}	s3c2410_dma_flush_all(channel);	free_irq(dma->irq, (void *)dma);	dma->in_use = 0;#ifdef HOOK_LOST_INT	stop_dma_timer();#endif	DPRINTK("freed\n");}EXPORT_SYMBOL(s3c2410_request_dma);EXPORT_SYMBOL(s3c2410_dma_queue_buffer);EXPORT_SYMBOL(s3c2410_dma_get_current);EXPORT_SYMBOL(s3c2410_dma_stop);EXPORT_SYMBOL(s3c2410_dma_flush_all);EXPORT_SYMBOL(s3c2410_free_dma);static int __init s3c2410_init_dma(void){	int channel;#ifdef HOOK_LOST_INT	int ret;#endif	for (channel = 0; channel < (MAX_S3C2410_DMA_CHANNELS); channel++) {		dma_chan[channel].regs =				(dma_regs_t *)io_p2v(0x4b000000 + 0x40 * channel);		dma_chan[channel].irq = IRQ_DMA0 + channel;		dma_chan[channel].channel = channel;	}#ifdef HOOK_LOST_INT		stop_dma_timer();		ret = request_irq(IRQ_TIMER3, dma_timer_irq_handler, SA_INTERRUPT,			  "DMA timer", NULL);	if (ret)		printk(__FUNCTION__ " : could not allocate IRQ (errno %d)\n", ret);#endif	return 0;}__initcall(s3c2410_init_dma);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -