text: /*
   *      Copyright (C) 1997 Claus-Justus Heine
  
   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, 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; see the file COPYING.  If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  
   *
   * $Source: /home/cvs/PSXLinux/drivers/char/ftape/lowlevel/ftape-buffer.c,v $
   * $Revision: 1.1.1.1 $
   * $Date: 2001/02/22 14:58:23 $
   *
   *  This file contains the allocator/dealloctor for ftape's dynamic dma
   *  buffer.
   */
  
  #include <asm/segment.h>
  #include <linux/malloc.h>
  #include <linux/mm.h>
  #include <linux/mman.h>
  #include <linux/wrapper.h>
  #include <asm/dma.h>
  
  #include <linux/ftape.h>
  #include "../lowlevel/ftape-rw.h"
  #include "../lowlevel/ftape-read.h"
  #include "../lowlevel/ftape-tracing.h"
  
  /*  DMA'able memory allocation stuff.
   */
  
  static inline void *dmaalloc(size_t size)
  {
  	unsigned long addr;
  
  	if (size == 0) {
  		return NULL;
  	}
  	addr = __get_dma_pages(GFP_KERNEL, get_order(size));
  	if (addr) {
  		struct page *page;
  
  		for (page = virt_to_page(addr); page < virt_to_page(addr+size); page++)
  			mem_map_reserve(page);
  	}
  	return (void *)addr;
  }
  
  static inline void dmafree(void *addr, size_t size)
  {
  	if (size > 0) {
  		struct page *page;
  
  		for (page = virt_to_page((unsigned long)addr);
  		     page < virt_to_page((unsigned long)addr+size); page++)
  			mem_map_unreserve(page);
  		free_pages((unsigned long) addr, get_order(size));
  	}
  }
  
  static int add_one_buffer(void)
  {
  	TRACE_FUN(ft_t_flow);
  	
  	if (ft_nr_buffers >= FT_MAX_NR_BUFFERS) {
  		TRACE_EXIT -ENOMEM;
  	}
  	ft_buffer[ft_nr_buffers] = kmalloc(sizeof(buffer_struct), GFP_KERNEL);
  	if (ft_buffer[ft_nr_buffers] == NULL) {
  		TRACE_EXIT -ENOMEM;
  	}
  	memset(ft_buffer[ft_nr_buffers], 0, sizeof(buffer_struct));
  	ft_buffer[ft_nr_buffers]->address = dmaalloc(FT_BUFF_SIZE);
  	if (ft_buffer[ft_nr_buffers]->address == NULL) {
  		kfree(ft_buffer[ft_nr_buffers]);
  		ft_buffer[ft_nr_buffers] = NULL;
  		TRACE_EXIT -ENOMEM;
  	}
  	ft_nr_buffers ++;
  	TRACE(ft_t_info, "buffer nr #%d @ %p, dma area @ %p",
  	      ft_nr_buffers,
  	      ft_buffer[ft_nr_buffers-1],
  	      ft_buffer[ft_nr_buffers-1]->address);
  	TRACE_EXIT 0;
  }
  
  static void del_one_buffer(void)
  {
  	TRACE_FUN(ft_t_flow);
  	if (ft_nr_buffers > 0) {
  		TRACE(ft_t_info, "releasing buffer nr #%d @ %p, dma area @ %p",
  		      ft_nr_buffers,
  		      ft_buffer[ft_nr_buffers-1],
  		      ft_buffer[ft_nr_buffers-1]->address);
  		ft_nr_buffers --;
  		dmafree(ft_buffer[ft_nr_buffers]->address, FT_BUFF_SIZE);
  		kfree(ft_buffer[ft_nr_buffers]);
  		ft_buffer[ft_nr_buffers] = NULL;
  	}
  	TRACE_EXIT;
  }
  
  int ftape_set_nr_buffers(int cnt)
  {
  	int delta = cnt - ft_nr_buffers;
  	TRACE_FUN(ft_t_flow);
  
  	if (delta > 0) {
  		while (delta--) {
  			if (add_one_buffer() < 0) {
  				TRACE_EXIT -ENOMEM;
  			}
  		}
  	} else if (delta < 0) {
  		while (delta++) {
  			del_one_buffer();
  		}
  	}
  	ftape_zap_read_buffers();
  	TRACE_EXIT 0;
  }
  