hope the code can help you writing CD-ROM Driver

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
wangy414
Posts: 13
Joined: Sat Nov 25, 2006 9:06 pm
Location: china

hope the code can help you writing CD-ROM Driver

Post by wangy414 »

Code: Select all


/* 
* if you want to add CD-ROM Driver in your OS.
* this code will be very easy to reused by you.
* 
* now this code is not complete,
* just can list the diretory and the files.
* the diretory size is limit to one sector
* and can show the first content's of the file
*
* hope this code can help for you. 
*        Wang Yong email: [email protected]
*
* this code is based on ie8086.c
* Written by V.M.G.van Acht
* (c) 1998 by Octagone
* 
* and used some data structures in mobius 
*
*/

#include <const.h>
#include <types.h>

#include <string.h>

#include <stdio.h>

#include <arch/i386.h>


#include <kernel/video.h>
#include <kernel/kmalloc.h>

#include <kernel/keyboard.h>
#include <kernel/cdfs.h>


//---------------- used in ISO9660 ----------------------//

#pragma pack(push, 1)
typedef struct both32_t both32_t;
struct both32_t
{
	uint32_t native;	/* little endian */
	uint32_t foreign;	/* big endian */
};

typedef struct both16_t both16_t;
struct both16_t
{
	uint16_t native;	/* little endian */
	uint16_t foreign;	/* big endian */
};


typedef struct path_t path_t;
struct path_t
{
	uint8_t name_length;
	uint8_t num_extended_sectors;
	uint32_t first_sector;
	uint16_t parent_record_num;
	uint8_t name[1];
};


typedef struct entry_t entry_t;
struct entry_t
{
	uint8_t struct_size;
	uint8_t num_extended_sectors;
	both32_t first_sector;
	both32_t length;
	uint8_t year;
	uint8_t month;
	uint8_t day;
	uint8_t hour;
	uint8_t minute;
	uint8_t second;
	uint8_t gmt_offset;
	uint8_t flags;
	uint8_t unit_size;
	uint8_t interleave_gap;
	both16_t volume_seq_num;
	uint8_t id_length;
	//uint8_t id[1];
};

// file attribute 
#define DIR_FLAG_HIDDEN	1
#define DIR_FLAG_DIRECTORY	2
#define DIR_FLAG_ASSOC		4
#define DIR_FLAG_FORMAT	8
#define DIR_FLAG_MORE		127

#define MAX_NAME_LENGTH	32


typedef struct date_time_t date_time_t;
struct date_time_t
{
	uint8_t year[4];
	uint8_t month[2];
	uint8_t day[2];
	uint8_t hour[2];
	uint8_t minute[2];
	uint8_t second[2];
	uint8_t sec100[2];
	int8_t gmt_offset;
};


typedef struct pvd_t pvd_t;
struct pvd_t
{
	uint8_t id[8];
	char system_id[32];
	char volume_id[32];
	uint8_t zero1[8];
	both32_t num_sectors;
	uint8_t zero2[32];
	both16_t volume_set_size;
	both16_t volume_seq_num;
	both16_t sector_size;
	both32_t path_table_length;
	uint32_t le_path_table_1_sector;
	uint32_t le_path_table_2_sector;
	uint32_t be_path_table_1_sector;
	uint32_t be_path_table_2_sector;
	union
	{
		entry_t entry;
		uint8_t padding[34];
	} root;
	uint8_t volume_set_id[128];
	uint8_t publisher_id[128];
	uint8_t data_prep_id[128];
	uint8_t app_id[128];
	uint8_t copyright_file[37];
	uint8_t abstract_file[37];
	uint8_t biblio_file[37];
	date_time_t create_date;
	date_time_t modify_date;
	date_time_t expire_date;
	date_time_t effective_date;
	uint8_t reserved[1167];
};
#pragma pack(pop)


// ------- self defined cd-node type ---//

typedef struct cd_node_t cd_node_t;
struct cd_node_t
{
	entry_t entry;
	char name[30];
	char ext[5];
	cd_node_t* next;
};


//----------- ports and commands --------------//
///this is the secondary ide channel
const unsigned int	IDEport=0X170,	/* IDE port 0=0x1F0, 1=0x170     	*/
					IDERegData=0,  	/* number of IDE registers		*/
					IDERegError=1,
					IDERegFeature=1,
					IDERegSectCount=2,
					IDERegSectNum=3,
					IDERegCylLow=4,
					IDERegCylHigh=5,
					IDERegDevHead=6,
					IDERegStatus=7,
					IDERegCommand=7,
					IDERegAltStatus=14,
					IDERegAdres=15;

/* global variables */
unsigned int IDENop        [6]    ={0,0,0,0,0xA0,0X00},
			IDEATAPIReset [6]    ={0,0,0,0,0XA0,0X08},
			IDEDiagnostic [6]    ={0,0,0,0,0XA0,0X90},
			IDEATAPIPacket[6]    ={0,0,0,0,0XA0,0XA0},
			IDEATAPIIdent [6]    ={0,0,0,0,0XA0,0XA1},
			IDEStandbyImm [6]    ={0,0,0,0,0XA0,0XE0},
			IDEIdleImm	   [6]    ={0,0,0,0,0XA0,0XE1},
			IDECheckpower [6]    ={0,0,0,0,0XA0,0XE5},
			IDESleep      [6]    ={0,0,0,0,0XA0,0XE6},
			IDESetFeatures[6]    ={0,0,0,0,0XA0,0XEF};

/////////////// ATAPI command packet ///////////////////////
unsigned char
		ATAPIRequestSense[12]={0X03,0,0,0,18,0,0,0,0,0,0,0},
		ATAPIStartStopUnit[12]={0X1B,1,0,0,0,0,0,0,0,0,0,0},
		ATAPIRead10      [12]={0X28,0,0,0,0,0,0,0,0,0,0,0},
		ATAPIReadSubChan1[12]={0X42,0,0x40,1,0,0,0,0,16,0,0,0},
		ATAPIReadSubChan2[12]={0x42,0,0x40,2,0,0,0,0,24,0,0,0},
		ATAPIReadSubChan3[12]={0x42,0,0x40,3,0,0,0,0,24,0,0,0},
		ATAPIReadTOC     [12]={0X43,0,0,0,0,0,0,0X03,0X24,0,0,0},
		ATAPIPlayAudioMSF[12]={0X47,0,0,0,0,0,0,0,0,0,0,0},
		ATAPIPauseAudio  [12]={0X4B,0,0,0,0,0,0,0,0,0,0,0},
		ATAPIStopAudio   [12]={0X4E,0,0,0,0,0,0,0,0,0,0,0},
		ATAPISetSpeed    [12]={0XBB,0,0,0,0,0,0,0,0,0,0,0};

///----------- variables --------------------//

#define _countof(a) (sizeof(a)/sizeof(a[0])) //< get the size
#define ATTRI(flag, attri) ((flag)&(attri))  //< get the attribute
#define SECTOR_SIZE 2048					 //< define the sector size


unsigned char SectorBuf[2048]; //< save a sector read from the CD-ROM

cd_node_t *cur_node_list; //<save the cd_node


//----------------- prototypes  -----------------//
///used to read sectors, the sectors read must be consecutive
int read_cd_sector(unsigned long sectnum,unsigned char *data,unsigned int sectcount,unsigned int devnum);
int read_ATAPIData(unsigned char *data,unsigned int len);
int write_ATAPICommand(unsigned char *data,unsigned int devnum,unsigned int maxlen);
void write_IDEReg(unsigned int regnum, unsigned int data);
unsigned int read_IDEReg(unsigned int regnum);
void Long_2CharMSBLSB(unsigned long value,unsigned char *bytes);
void Int_2CharMSBLSB(unsigned int value,unsigned char *bytes);


//--------------- function implement ---------------//

void Int_2CharMSBLSB(unsigned int value,unsigned char *bytes)
{
	*(bytes+1)=(value&0x00ff);
	*(bytes  )=(value&0xff00)>>8;
}
void Long_2CharMSBLSB(unsigned long value,unsigned char *bytes)
{
	*(bytes+3)=(value&0x000000ff);
	*(bytes+2)=(value&0x0000ff00)>>8;
	*(bytes+1)=(value&0x00ff0000)>>16;
	*(bytes  )=(value&0xff000000)>>24;
}

///////////////////////////////////////////////////////
// Read value of IDE register (LOW-LEVEL)
//in: number of register:  0=data register (16 bit)
//1=error register 2=sector count register 3=sector number register
// 4=cylinder register (LSB) 5=cylinder register (MSB)
// 6=drive/head register 7=status register
//8=Not used 9=not used 10=not used 11=not used
//12=not used 13=not used 14=alternate status register 15=adres register
//out:value
///////////////////////////////////////////////////////
unsigned int read_IDEReg(unsigned int regnum)
{
	unsigned int i,j;
	
	if (regnum>=8) 
	{
		i=IDEport+regnum+0X1F8;
	}
	else {
		i=IDEport+regnum;
	}

	j=inportw(i);
	return(j);
}
/////////////////////////////////////////
// Write value to IDE register (LOW-LEVEL)
//in: number of register:  0=data register (16 bit)
//1=feature register 2=sector count register 3=sector number register
//4=cylinder register (LSB) 5=cylinder register (MSB) 6=drive/head register
//7=command register 8=Not used 9=not used 10=not used
//11=not used 12=not used 13=not used 14=control register 15=not used
//data
/////////////////////////////////////////
void write_IDEReg(unsigned int regnum, unsigned int data)
{
	unsigned int i,j;
	
	if (regnum>=8) 
	{
		i=IDEport+regnum+0X1F8;
	}
	else {
		i=IDEport+regnum;
	}
	j=data;
	outportw(i,j);
}

//////////////////////////////////////
// Write ATAPI Command
//in:	*data = pointer to unsigned char array [12] with command arguments
//MaxLen = maximum number of bytes to be transfered in 1 time (0xffff allowed)
//DriveNum = drive number 0/1
//out: -1    = error
//0    = OK
/////////////////////////////////////////
int	write_ATAPICommand(unsigned char *data,unsigned int devnum,unsigned int maxlen)
{
	unsigned int i;
	
	i=0;
	do
	{
		if (i==0XFFFF)
		{
			return(-1);
		}
		i++;
	}
	while ((read_IDEReg(IDERegStatus)&0X80)!=0);

	write_IDEReg(IDERegDevHead,(0XA0+(devnum<<4)));
	
	i=0;
	do
	{
		if (i==0XFFFF)
		{
			return(-1);
		}
		i++;
	}
	while ((read_IDEReg(IDERegStatus)&0X80)!=0);
	
	write_IDEReg(IDERegCylLow,maxlen&0XFF);
	write_IDEReg(IDERegCylHigh,maxlen>>8);
	write_IDEReg(IDERegCommand,0XA0);
	
	i=0;
	do
    {
		if (i==0XFFFF)
		{
			return(-1);
		}
		if ((read_IDEReg(IDERegStatus)&0x01)!=0)
		{
			return(-1);
		}
    }
	while ((read_IDEReg(IDERegStatus)&0x08)==0);
	
	for (i=1;i<=6;i++)
	{
		write_IDEReg(IDERegData,*(data+0)+(*(data+1)<<8));
		data=data+2;
	}
	return(0);
}
//////////////////////////////////////////////////////
// Read ATAPI data from data-register to CHAR array
//in:	*data = pointer to unsigned char array where data is to be located
//len   = number of WORDS
//out: -1    = error
//0    = OK
///////////////////////////////////////////////////////
int	read_ATAPIData(unsigned char *data,unsigned int len)
{
	unsigned int i,j,k;
	
    i=0;
    do
	{
		if (i==0XFFFF)
		{
			return(len);
		}
		if ((read_IDEReg(IDERegStatus)&0x01)!=0)
		{
			return(-1);
		}
		i++;
	}
    while ((read_IDEReg(IDERegStatus)&0x08)==0);
	
    do
	{
		k=(read_IDEReg(4)&0XFF)+((read_IDEReg(5)&0XFF)<<8);
		do
		{
			if (len==0)
			{
				return(0);
			}
			i=read_IDEReg(IDERegData);
			*data=i&0xff;
			data++;
			*data=i>>8;
			len--;
			k--;
			data++;
		}
		while (k!=0);
	}
    while ((read_IDEReg(IDERegStatus)&0X08)!=0);
	return(0);
}
/////////////////////////////////////////////////////
// Read data-sectors from CD
//	in:	sectnum = start sector number to be read
//	data	= pointer to CHAR where data is to be written
//	sectcount= number of sectors to be read
//	devnum  = device number 0/1
//	out: 0	= OK
//	-1	= error
////////////////////////////////////////////////////////
int	read_cd_sector(unsigned long sectnum,unsigned char *data,unsigned int sectcount,unsigned int devnum)
{
	unsigned long i;
	
	Long_2CharMSBLSB(sectnum,ATAPIRead10+2);
	Int_2CharMSBLSB(sectcount,ATAPIRead10+7);
	
	if (write_ATAPICommand(ATAPIRead10,devnum,0XFFFF)!=0)
	{
		return(-1);
	}
	i=0;
	do
	{
		if (i==0x0FFFFFF)
		{
			printf("Duurt te lang");
			return(-1);
		}
		i++;
	}
	while ((read_IDEReg(IDERegStatus)&0X80)!=0);
	
	if (read_ATAPIData(data,sectcount<<10)!=0)
	{
		return(-1);
	}
	return(0);
}
//////////////////////////////////
//free the old cd-node memory 
/////////////////////////////////
void realse_old_list()
{
	cd_node_t *pre = cur_node_list;
	cd_node_t *next = pre;

	while(pre != NULL)
	{
		next = pre->next;
		kfree(pre);
		pre = next;
	}
	cur_node_list = NULL;
}
////////////////////////////////////////////////////////
//first, should free the old list, then get the new list
// in: buf is a sector
// out: is the ptr to the cd-node
////////////////////////////////////////////////////////
cd_node_t * get_dir_list(char *buf)
{
	// realse the old dir list
	realse_old_list();
	
	// get the new dir list
	int i = 0, n =0;
	cd_node_t *tmp = NULL;
	cd_node_t *pre = NULL;
	char fbuf[32];
	char tmpbuf[34];
	//the entry size must be bigger than 33
	while(i < 2035)
	{
		tmp = (cd_node_t*)kmalloc(sizeof(cd_node_t),GFP_KERNEL);
		bzero(tmp,sizeof(cd_node_t));
		if(tmp == NULL)
		{
			printf("malloc mem fail\n");
			return NULL;
		}
		memcpy((void*)&(tmp->entry), (void*)(buf + i), 33);
		if(tmp->entry.struct_size <= 0)       	
		{        		
			break;        	
		}

		if(n == 0)
		{
			// current dir
			tmp->name[0] = '.';
			tmp->name[1] = '\0';

			n = 1;
			
			tmp->ext[0] = ' ';
			tmp->ext[1] = '\0';
			goto isDir;
		} else if(n == 1){
			// up dir
			tmp->name[0] = '.';
			tmp->name[1] = '.';
 			tmp->name[2] = '\0';
			
			n = 2;

			tmp->ext[0] = ' ';
			tmp->ext[1] = '\0';
			goto isDir;
		}else {
			bzero(fbuf,32);
			if(tmp->entry.id_length > 32 )
				tmp->entry.id_length = 32;
			strncpy(fbuf, (unsigned char*)(buf + i + 33), tmp->entry.id_length);
			int k =0;
			// get name
			for(k =0; k < tmp->entry.id_length; k++)
			{
				if(*(fbuf + k) != '.')
				{
					tmp->name[k] = *(fbuf + k);
				}
				else
				{
					break;
				}
			}// end get name
			tmp->name[k] = '\0';
			k++;

			// this is a dir the ext is empty
			if( ATTRI(tmp->entry.flags,DIR_FLAG_DIRECTORY) )
			{
				tmp->ext[0] = ' ';
				tmp->ext[1] = '\0';
				goto isDir;
			}
			// get extent name
			int e = 0;
			for(e = k; e < tmp->entry.id_length; e++)
			{
				if(*(fbuf + e) != ';')
				{
					tmp->ext[e -k] = *(fbuf + e);
				}
				else
				{
					break;
				}
			}// end get ext
			tmp->ext[e] = '\0';
		}// end else
isDir:	if( n==1)
	{
		cur_node_list = tmp;
		pre = tmp;
	}else{
		pre->next = tmp;
		pre = tmp;
	}
	i += tmp->entry.struct_size;
	}
  // end while      
	return cur_node_list;
}
/////////////////////////////////////////
// print the attribute of the file
///////////////////////////////////////
void show_attribute(int flag)
{		
		if(ATTRI(flag,DIR_FLAG_HIDDEN))
		{
			printf("H");
		}else{
			printf("-");	
		}

		if(ATTRI(flag,DIR_FLAG_DIRECTORY))
		{
			printf("D");
		}else{
			printf("-");	
		}

		if(ATTRI(flag, DIR_FLAG_ASSOC))
		{
			printf("F");
		}else{
			printf("-");	
		}

}
////////////////////////////////////////////
//print the current cd-node list int the screen
/////////////////////////////////////////////
void show_cdnode_list()
{
	cd_node_t *ptr = cur_node_list;

	printf("Attribute	Name	Sector	Size	Date	Time\n");

	while(ptr != NULL)
	{
		show_attribute(ptr->entry.flags);

		printf("	%s",ptr->name);
		if(ptr->ext[0] != ' ')
		{
			printf(".%s",ptr->ext);
		}
		printf("	%d",ptr->entry.first_sector.native);
		printf("	%d",ptr->entry.length.native);
		printf("	%d-%d-%d",(ptr->entry.year + 1900),ptr->entry.month,ptr->entry.day);
		printf("	%d:%d:%d\n",ptr->entry.hour,ptr->entry.minute,ptr->entry.second);
		
		ptr = ptr->next;
	}
}
/////////////////////////////////////////////////////
// if the fname is pointer to a file, list the file only
// else list the content's below the dir
//////////////////////////////////////////////////////
void cdfs_ls(char* fname)
{
	printf("dir: %s", fname);
	cd_node_t *ptr = cur_node_list;
	
	while(ptr != NULL)
	{
	
		if(strcmp(ptr->name, fname) == 0)
		{
			printf("f: %s ", ptr->name);
			// get the dir
			if( ATTRI(ptr->entry.flags, DIR_FLAG_DIRECTORY) )
			{
				//get the content of the dir
				long sec = ptr->entry.first_sector.native;
				int szSec = ptr->entry.length.native/SECTOR_SIZE;
				
				if( (ptr->entry.length.native%SECTOR_SIZE) > 0 )
					
					szSec++;
					
				
				bzero(SectorBuf,2048);
				read_cd_sector(sec,SectorBuf,1,0);
				
				
				get_dir_list(SectorBuf);
				
				show_cdnode_list();
			}else {
				
				// show the content of the file
			 	
				long sec = ptr->entry.first_sector.native;
				int szSec = ptr->entry.length.native/SECTOR_SIZE;
				
				if( (ptr->entry.length.native%SECTOR_SIZE) > 0 )
					
					szSec++;
				
				bzero(SectorBuf,2048);
				read_cd_sector(sec,SectorBuf,1,0);
				
				
									
				kprintf("CTRL+C to break \n" );
				
				if( kgetchar() == CTRL_C )
					
					return;
				
				int i = 0;
				
				int row = 0;
				
				int len = 0;
				
				if(ptr->entry.length.native > 2048)
					
					len = 2048;
				
				else
					
					len = ptr->entry.length.native;
					
				
				for(i =0; i < len; i++ )
				
				{
					
					kputchar(SectorBuf[i]);
					
					if((i %80 ) == 0)
						
						row++;
					
					if(row >= 24)
					
					{
						
						if( kgetchar() == CTRL_C )
							
							break;
						
						row = 0;
					
					}
				
				}
				
			
			}
	
		}// end if
		
		ptr = ptr->next;
	}// end while
}
//////////////////////////////////////////////////
//init the cd-rom file system, only just the cd-rom,
//whether is a ISO9660 file system or not.
//////////////////////////////////////////////////
bool init_cdfs()
{
	int dev,sec;
	pvd_t pvd;
	int err;
	char *ch;
 	dev = 0;
 	sec = 16;
	
	// get the primary volume, in the 16 sector
	read_cd_sector(sec,(unsigned char *)&pvd,1,dev);

	for(ch = pvd.volume_id;
	   *ch != ' ' && ch < pvd.volume_id + _countof(pvd.volume_id) -1;
	   ch++)
;
        
	*ch = '\0';
	
	//kprintf("%s\n",pvd.id);
  
	
	long rootsec = pvd.root.entry.first_sector.native;
	int szSec = pvd.root.entry.length.native/SECTOR_SIZE;

	// get the root directory
	read_cd_sector(rootsec,SectorBuf,1,dev);
	       
	kprintf("%d,%d\n",rootsec, szSec);

	get_dir_list(SectorBuf);
	show_cdnode_list();

	return TRUE;
}
frank
Member
Member
Posts: 729
Joined: Sat Dec 30, 2006 2:31 pm
Location: East Coast, USA

Post by frank »

That's great! :lol:

Do you know where I can find a complete list of the commands that you can send to the CD-Rom drive?
User avatar
Alboin
Member
Member
Posts: 1466
Joined: Thu Jan 04, 2007 3:29 pm
Location: Noricum and Pannonia

Post by Alboin »

Awesome code. :D Nice job! (If you have time, why not add some things to the wiki? :) )
C8H10N4O2 | #446691 | Trust the nodes.
wangy414
Posts: 13
Joined: Sat Nov 25, 2006 9:06 pm
Location: china

Post by wangy414 »

I would like to do some work for wiki.
difficulties: one is my English is not very well.
second, i have poor knowledge about os develping,
I just a new learner.
wangy414
Posts: 13
Joined: Sat Nov 25, 2006 9:06 pm
Location: china

Post by wangy414 »

frank wrote:That's great! :lol:

Do you know where I can find a complete list of the commands that you can send to the CD-Rom drive?

INF-8020.pdf , you can use google to search, if you can't find, give me you email, then i can send it to you.
INF1n1t
Member
Member
Posts: 60
Joined: Fri Dec 22, 2006 5:32 pm
Location: Somewhere Down...

Post by INF1n1t »

wangy414 wrote:I would like to do some work for wiki.
difficulties: one is my English is not very well.
second, i have poor knowledge about os develping,
I just a new learner.
I doubt the whole forum are native speakers - you see I can't speak english very well, also, but that's irrelevant. You should write something at the wiki!
I think, I have problems with Bochs. The biggest one: Bochs hates me!
frank
Member
Member
Posts: 729
Joined: Sat Dec 30, 2006 2:31 pm
Location: East Coast, USA

Post by frank »

Well I feel that if you have anything to contribute you should do it. I can understand your english just fine.

Thanks by the way,

Frank
wangy414
Posts: 13
Joined: Sat Nov 25, 2006 9:06 pm
Location: china

Post by wangy414 »

tthank you, I will try my best to do something.
User avatar
JAAman
Member
Member
Posts: 879
Joined: Wed Oct 27, 2004 11:00 pm
Location: WA

Post by JAAman »

frank wrote:That's great! :lol:

Do you know where I can find a complete list of the commands that you can send to the CD-Rom drive?
this is my favorite resource for all things ATA/ATAPI
Post Reply