hope the code can help you writing CD-ROM Driver
Posted: Fri Jan 12, 2007 7:38 am
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;
}