/*************************************************************
 *                                                           *
 *    ths  Filesystem                  04.08.94      V0.1    *
 *                                                           *
 *    Thomas Scheuermann     ths@ai-lab.fh-furtwangen.de     *
 *                                                           *
 *************************************************************/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/locks.h>
#include <linux/fs.h>
#include <linux/malloc.h>

#include <asm/system.h>
#include <asm/segment.h>
#include <asm/bitops.h>

#include "ths.h"
#include "ths_i.h"


/*
 * Lesen eines bestimmten Sektors
 */

int ths_read_sektor(struct super_block *s, unsigned long sektor, struct ths_buffer *tbf)
{
	int i,j;
	unsigned char *data,*data1=NULL;
	struct ths_sb_info *ths_sb;
	struct buffer_head *bh;

#ifdef DEBUG
	printk("ths_read_sektor : %ld\n",sektor);
#endif

	ths_sb = (struct ths_sb_info *)s->u.generic_sbp;

	tbf->sektore = 1;

	j=0;
	if(sektor & 0x01)
		j = 512;

	tbf->hl[0]=j;
	tbf->dirty=0;

	if(!(tbf->data[0]=(unsigned char *)kmalloc(512,GFP_KERNEL)))
	{
		printk("No Memory left !\n");
		return -1;
	}

/*
 * Datenumsetzung
 */

	switch(ths_sb->art)
	{
		case 0:
			if(!(bh = bread(s->s_dev,sektor>>1,BLOCK_SIZE)))
			{
				printk("bread-Fehler\n");
				return -1;
			}
			tbf->bnr[0] = sektor>>1;
			data1 = &bh->b_data[j];
			break;
		case 1:
			data1 = ths_uread(s,sektor,&bh);
			break;
	}

	tbf->sektnr[0] = sektor;
	tbf->cluster=0;
	data = tbf->data[0];
	for(i=0;i<512;i++)
	{
		data[i] = data1[i];
	}

	brelse(bh);
	return 0;
}


/*
 * Speicher freigeben nach dem Lesen eines Sektors
 */

void ths_free_sektor(struct ths_buffer *tbf)
{
#ifdef DEBUG
	printk("ths_free_sektor : 0x%lx\n",tbf->sektnr[0]);
#endif

	if(tbf->dirty != 0)
		printk("read-only\n");
	kfree_s(tbf->data[0],512);
	tbf->data[0]=NULL;
	tbf->dirty=0;
	tbf->sektore=0;
	tbf->hl[0]=0;
}


/*
 * Lesen eines Clusters
 */

int ths_read_cluster(struct super_block *s, unsigned short cluster, long offset, struct ths_buffer *tbf)
{
	unsigned char *data,*data1=NULL;
	int i,j,k;
	unsigned short cl1,cl2;
	long sektor=0,tmp=0;
	struct ths_sb_info *ths_sb;
	struct buffer_head *bh;

#ifdef DEBUG
	printk("ths_read_cluster : %d bei %ld",cluster,offset);
#endif

	ths_sb = (struct ths_sb_info *)s->u.generic_sbp;

	tbf->sektore = ths_sb->SektorenProCluster;
	tbf->dirty = 0;

	j = offset/(ths_sb->SektorenProCluster * 512);
	cl1 = cluster;
	for(i=0;i<j;i++)
	{
		cl2 = (ths_sb->fat[cl1/2048])[cl1%2048];
		cl1 = cl2;
	}
	tbf->cluster = cl1;
#ifdef DEBUG
	printk(" = Cluster %d\n",cl1);
#endif

	switch(ths_sb->art)
	{
		case 0:
			sektor = (long)ths_sb->DatenStart;
			sektor += (cl1-2) * ths_sb->SektorenProCluster;
			break;
		case 1:
			tmp = dblsektor(s,cl1);
			sektor = (tmp & 0x1fffff)+1;
			break;
	}
#ifdef DEBUG
	printk("sektor : %ld\n",sektor);
#endif

	for(i=0;i<tbf->sektore;i++)
	{
		if(!(tbf->data[i]=(unsigned char *)kmalloc(512,GFP_KERNEL)))
		{
			printk("No Memory left !\n");
			return -1;
		}
	}
	switch(ths_sb->art)
	{
		case 0:
			for(i=0;i<tbf->sektore;i++)
			{
				if(!(bh = bread(s->s_dev,(sektor+i)>>1,BLOCK_SIZE)))
				{
					printk("bread-Fehler\n");
					return -1;
				}
				j=0;
				if((sektor+i) & 0x01)
					j = 512;
				data1 = &(bh->b_data[j]);

				tbf->bnr[i] = (sektor+i)>>1;
				tbf->sektnr[i] = (sektor+i);

				data = tbf->data[i];
				for(k=0;k<512;k++)
				{
					data[k] = data1[k];
				}

				brelse(bh);

			}
			break;
		case 1:
			if(tmp & 0x40000000)
			{
#ifdef DEBUG
				printk("Unkomprimiert\n");
#endif
				for(i=0;i<((tmp & 0x3c000000)>>26)+1;i++)
				{
					data1 = ths_uread(s,sektor+i,&bh);
					tbf->bnr[i] = (sektor+i)>>1;
					tbf->sektnr[i] = ths_sb->DatenStart+cluster*16+i;

					data = tbf->data[i];
					for(k=0;k<512;k++)
					{
						data[k] = data1[k];
					}

					brelse(bh);
				}
			}
			else
			{
#ifdef DEBUG
				printk("Komprimiert\n");
#endif
				decompress(s,tbf,tmp);
			}
			break;
	}
	return 0;
}


/*
 * Freigeben des Speichers nach dem Lesen eines Clusters
 */

void ths_free_cluster(struct ths_buffer *tbf)
{
	int i;
#ifdef DEBUG
	printk("ths_free_cluster : %d\n",tbf->cluster);
#endif

	if(tbf->dirty != 0)
		printk("read-only\n");

	for(i=0;i<tbf->sektore;i++)
	{
		kfree_s(tbf->data[i],512);
		tbf->data[i]=NULL;
		tbf->hl[i]=0;
	}
	tbf->dirty=0;
	tbf->sektore=0;
	tbf->cluster=0;
}


/*
 * ths_uread_sektor liest einen Sektor von dem angegebenen Device.
 * Es wird der angegebene Sektor gelesen und ein Zeiger auf den
 * Datenpuffer zurueckgegeben. Der belegte Block muss mit brelse(bh)
 * wieder freigegeben werden.
 */

char *ths_uread_sektor(long sektor, int dev, struct buffer_head **bh)
{
	int i;
	*bh = bread(dev,sektor>>1,BLOCK_SIZE);
	i=0;
	if(sektor&1)
		i=512;
	return &((*bh)->b_data[i]);
}


/*
 * ths_uread liest einen Sektor aus dem CVF, welcher als offset
 * angegeben wird. Der belegte Block muss durch brelse(bh)
 * wieder freigegeben werden.
 */

char *ths_uread(struct super_block *s, long offset, struct buffer_head **bh)
{
	int i,j,clb;
	long sektor,fatsektor;
	unsigned short cluster;
	struct ths_sb_info *ths_sb;
	struct buffer_head *fbh;
	unsigned char b1=0;
	unsigned char *data;

	ths_sb = (struct ths_sb_info *)s->u.generic_sbp;


	cluster  = ths_sb->StartCluster;

	for(i=0;i<offset/ths_sb->uSektorenProCluster;)
	{
		clb = (cluster*(ths_sb->uBitsProCluster>>2));
		fatsektor = ths_sb->uFATStart + ((clb>>1)/512);
		fbh = bread(s->s_dev,fatsektor>>1,BLOCK_SIZE);
		j=0;
		if(fatsektor&1)
			j=512;
		data = &(fbh->b_data[j]);
		b1 = data[(clb>>1) & 0x1ff];
		if(clb & 1)
			cluster = (unsigned short)(b1 & 0xf0)>>4;
		else
			cluster = (unsigned short)b1;

		if(((clb>1) & 0x1ff)==511)
		{
			brelse(fbh);
			fbh = bread(s->s_dev,(fatsektor+1)>>1,BLOCK_SIZE);
			j=0;
			if((fatsektor+1)&1)
				j=512;
			data = &(fbh->b_data[j]);
		}

		b1 = data[((clb>>1)+1) & 0x1ff];
		if(clb & 1)
			cluster |= (unsigned short)b1  << 4;
		else
			cluster |= (unsigned short)b1 << 8;

		if(ths_sb->uBitsProCluster == 12)
			cluster &=0xfff;
		i++;
		brelse(fbh);
	}
	sektor = ths_sb->uDatenStart + (cluster-2)*ths_sb->uSektorenProCluster + (offset % ths_sb->uSektorenProCluster);
#ifdef DEBUG
	printk("Sektor : 0x%lx\n",sektor);
#endif

	return ths_uread_sektor(sektor,s->s_dev,bh);
}


/*
 * dblsektor holt aus der MDFAT-Tabelle den Eintrag
 * eines Clusters und gibt ihn zurueck.
 */

long dblsektor(struct super_block *s, unsigned short cluster)
{
	struct buffer_head *bh;
	struct ths_sb_info *ths_sb;
	unsigned char *data,*x;
	int i=0;
	long cl;

	ths_sb = (struct ths_sb_info *)s->u.generic_sbp;

	cl = (long)(ths_sb->dcluster + cluster )*4;

	data = ths_uread(s,ths_sb->MDFATStart+cl/512,&bh);

	x = &data[cl%512];
	i= CHL(x);
	brelse(bh);
	return i;
}

