/*************************************************************
 *                                                           *
 *    ths  Filesystem                  04.10.94      V1.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"

static struct super_operations ths_sops = { 
        ths_read_inode,
        NULL,
        NULL,
        NULL,
        ths_put_super,
        NULL,
        ths_statfs,
        NULL
};


/*
 * Lesen des MSDOS-Bootblocks und Ausfuellen der
 * super_block Struktur
 */

struct super_block *ths_read_super(struct super_block *s, void *data, int silent)
{
	struct ths_sb_info *ths_sb;

	ths_sb = (struct ths_sb_info *)kmalloc(512,GFP_KERNEL);
	s->u.generic_sbp = ths_sb;

	if(data==NULL)
	{
		ths_sb->art=0;			/* normales DOS */
		return ths_read_super_normal(s,data,silent);
	}
	else
	{
		ths_sb->art=1;			/* DoubleSpace */
		return ths_read_super_dblspace(s,data,silent);
	}
}


/*
 * Lesen des Super-Blocks fuer DoubleSpace-Laufwerke
 */

struct super_block *ths_read_super_dblspace(struct super_block *s, void *data, int silent)
{
	struct buffer_head *bh;
	struct ths_boot_sektor *bs;
	struct dbl_boot_sektor *dbl;
	struct ths_sb_info *ths_sb;
	char *data1=NULL;
	long RootStart;

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

#ifdef DEBUG
	printk("DoubleSpace-Superblock\n");
#endif

	lock_super(s);
	bh = bread(s->s_dev,0,BLOCK_SIZE);

	if (bh == NULL)
	{
		s->s_dev = 0;
		printk("ths bread failed\n");
		unlock_super(s);
		return NULL;
	}
	bs = (struct ths_boot_sektor *)bh->b_data;

	ths_sb->uFATStart = CHS(bs->ReservierteSektoren);
	ths_sb->uDatenStart = CHS(bs->ReservierteSektoren) +
					  (bs->AnzahlFats * CHS(bs->SektorenProFat)) +
					  (CHS(bs->EintraegeRootverzeichnis) / 16);
	ths_sb->uSektorenProCluster = bs->SektorenProCluster;
	ths_sb->uAnzahlCluster = (short)(((CHS(bs->SektorenImVolume) ? CHS(bs->SektorenImVolume) :
						    CHL(bs->SektorenImVolume2)) -
							CHS(bs->ReservierteSektoren) -
						   (bs->AnzahlFats * CHS(bs->SektorenProFat)) -
						   (CHS(bs->EintraegeRootverzeichnis) / 16)) /
						   bs->SektorenProCluster);
	ths_sb->uBitsProCluster = ths_sb->uAnzahlCluster < 4078 ? 12 : 16;
	RootStart = CHS(bs->ReservierteSektoren) +
					  (bs->AnzahlFats * CHS(bs->SektorenProFat));


	ths_sb->StartCluster = findeStartCluster(s,RootStart,CHS(bs->EintraegeRootverzeichnis),data);
	if(ths_sb->StartCluster == 0)
	{
		printk("CVF nicht gefunden !!\n");
		brelse(bh);
		unlock_super(s);
		return NULL;
	}
#ifdef DEBUG
	printk("StartCluster : %d\n",ths_sb->StartCluster);
#endif

	unlock_super(s);


	data1 = ths_uread(s,0,&bh);
	bs = (struct ths_boot_sektor *)data1;
	superfill(s,ths_sb,bs);

	dbl = (struct dbl_boot_sektor *)data1;

	ths_sb->BitFATStart = 1;

	ths_sb->MDFATStart = (long)CHS(dbl->MDFATStart)+1;

	ths_sb->BootStart = (long)CHS(dbl->res0);

	ths_sb->FatStart = (long)(CHS(dbl->res0)+CHS(dbl->ReservierteSektoren));

	ths_sb->RootStart = (long)(CHS(dbl->res0) + CHS(dbl->RootDirStart));

	ths_sb->DatenStart = (long)(CHS(dbl->res0) + CHS(dbl->DatenStart)+2);

	ths_sb->AnzahlCluster = ths_sb->AnzahlCluster*8;

	ths_sb->dcluster = CHS(dbl->ClusterStart);

#ifdef DEBUG
	printk("BitFATStart : %ld\n",ths_sb->BitFATStart);
	printk("MDFATStart = %ld\n",ths_sb->MDFATStart);
	printk("BootStart : %ld\n",ths_sb->BootStart);
	printk("FatStart : %ld\n",ths_sb->FatStart);
	printk("RootDirStart = %ld\n",ths_sb->RootStart);
	printk("DatenStart = %ld\n",ths_sb->DatenStart);
	printk("Anzahl Cluster = %d\n",ths_sb->AnzahlCluster);
	printk("dev : %d\n",s->s_dev);
#endif

	brelse(bh);

	cvffill(s);
	fatmem2(s);

	s->s_blocksize = BLOCK_SIZE;
	s->s_blocksize_bits = 10;
	s->s_op = &ths_sops;

	s->s_mounted = iget(s,1);


	return s;
}


/*
 * Lesen des Super-Blocks fuer normale DOS-Laufwerke
 */

struct super_block *ths_read_super_normal(struct super_block *s, void *data, int silent)
{
	struct buffer_head *bh;
	struct ths_boot_sektor *bs;
	struct ths_sb_info *ths_sb;

#ifdef DEBUG
	printk("read_super\n");
	printk("Daten : %s\n",(char *)data);
#endif

    /*
     * Lesen des Bootblocks
     */

	lock_super(s);
	bh = bread(s->s_dev,0,BLOCK_SIZE);

	if (bh == NULL)
	{
		s->s_dev = 0;
		printk("ths bread failed\n");
		unlock_super(s);
		return NULL;
	}

	bs = (struct ths_boot_sektor *)bh->b_data;

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


/*
 * Fuellen der Struktur
 */
	superfill(s,ths_sb,bs);

	unlock_super(s);

	brelse(bh);

	fatmem2(s);

	s->s_blocksize = BLOCK_SIZE;
	s->s_blocksize_bits = 10;
	s->s_op = &ths_sops;

	s->s_mounted = iget(s,1);

#ifdef DEBUG
	printk("dev : %d\n",s->s_dev);
#endif

	return s;
}


/*
 * Informationen aus dem Superblock extrahieren.
 */

void superfill(struct super_block *s,struct ths_sb_info *ths_sb,struct ths_boot_sektor *bs)
{
	s->s_magic = THS_MAGIC;

	ths_sb->SektorenProCluster = bs->SektorenProCluster;

	ths_sb->SektorenProFat = CHS(bs->SektorenProFat);

	ths_sb->AnzahlCluster = (short)(((CHS(bs->SektorenImVolume) ? CHS(bs->SektorenImVolume) :
							    CHL(bs->SektorenImVolume2)) -
								CHS(bs->ReservierteSektoren) -
							   (bs->AnzahlFats * CHS(bs->SektorenProFat)) -
							   (CHS(bs->EintraegeRootverzeichnis) / 16)) /
							   bs->SektorenProCluster);

	ths_sb->BitsProCluster = ths_sb->AnzahlCluster < 4078 ? 12 : 16;

	ths_sb->FatStart = CHS(bs->ReservierteSektoren);

	ths_sb->RootMaxEintraege = CHS(bs->EintraegeRootverzeichnis);

	ths_sb->DatenStart = CHS(bs->ReservierteSektoren) +
						  (bs->AnzahlFats * CHS(bs->SektorenProFat)) +
						  (CHS(bs->EintraegeRootverzeichnis) / 16);

	ths_sb->RootStart = CHS(bs->ReservierteSektoren) +
						  (bs->AnzahlFats * CHS(bs->SektorenProFat));

#ifdef DEBUG
	printk("SektorenProCluster :%i\n",ths_sb->SektorenProCluster);
	printk("SektorenProFat :%i\n",ths_sb->SektorenProFat);
	printk("Sektoren1 : %d\n",CHS(bs->SektorenImVolume));
	printk("Sektoren2 : %ld\n",CHL(bs->SektorenImVolume2));
	printk("AnzahlCluster :%d\n",ths_sb->AnzahlCluster);
	printk("BitsProCluster :%d\n",ths_sb->BitsProCluster);
	printk("FatStart :%ld\n",ths_sb->FatStart);
	printk("RootMaxEintraege :%d\n",ths_sb->RootMaxEintraege);
	printk("DatenStart :%ld\n",ths_sb->DatenStart);
	printk("RootStart :%ld\n",ths_sb->RootStart);
#endif
}


/*
 * Suche nach dem Startcluster des CVF
 */

short findeStartCluster(struct super_block *s,long Rootdir, short Eintraege, char *name)
{
	int i,j;
	char *data=NULL;
	struct buffer_head *bh=NULL;
	struct ths_dir *dir;

	for(j=0;name[j]!='\0';j++);

#ifdef DEBUG
	printk("%s : %d\n",name,j);
#endif

	for(i=0;i<Eintraege*32;)
	{
		if(!(i&0x1ff))
		{
			data = ths_uread_sektor(Rootdir+i/512,s->s_dev,&bh);
		}
		dir = (struct ths_dir *)&data[i%512];


		if(vergleich(dir,(const char *)name,j))
		{
			brelse(bh);
			return CHS(dir->cluster);
		}
		i+=32;
		if(!(i&0x1ff))
			brelse(bh);
	}
	return 0;
}


void ths_put_super(struct super_block *s)
{
#ifdef DEBUG
	printk("put_super\n");
#endif
	lock_super(s);
	fatfree(s);
	cvffree(s);
	kfree_s(s->u.generic_sbp,512);
	s->s_dev=0;
	unlock_super(s);
	
	return;
}


void ths_statfs(struct super_block *s, struct statfs *buf)
{
	struct ths_sb_info *ths_sb;
	long i,j,k;
	short *addr;

#ifdef DEBUG
	printk("statfs\n");
#endif

	ths_sb = (struct ths_sb_info *)s->u.generic_sbp;
	put_fs_long(THS_MAGIC, &buf->f_type);
	put_fs_long(1024, &buf->f_bsize);
	put_fs_long(ths_sb->AnzahlCluster*ths_sb->SektorenProCluster/2, &buf->f_blocks);

	j=0;

/*
 * k wird auf 2 gesetzt, da die ersten beide Eintraege
 * der FAT keine Cluster-Zeiger sind.
 */
	k=2;
	addr=ths_sb->fat[0];
	for (i=0;i<ths_sb->AnzahlCluster;i++)
	{
		if(addr[k]==0)
			j++;
		k++;
		if(k==2048)
		{
			k=0;
			addr=ths_sb->fat[(i+3)/2048];
		}
	}
	j*=ths_sb->SektorenProCluster;
	j/=2;
#ifdef DEBUG
	printk("frei : %ld\n",j);
#endif
	put_fs_long(j, &buf->f_bfree);
	put_fs_long(j, &buf->f_bavail);
}


