From c6773e1436ba3adda0a6456dbe54ba1320a095be Mon Sep 17 00:00:00 2001 From: Björn Stenberg Date: Tue, 23 Apr 2002 09:13:23 +0000 Subject: Moved driver to 'drivers' subdir git-svn-id: svn://svn.rockbox.org/rockbox/trunk@189 a1c6a512-1295-4272-9138-f99709370657 --- firmware/drivers/ata.c | 242 +++++++++ firmware/drivers/ata.h | 32 ++ firmware/drivers/button.c | 197 +++++++ firmware/drivers/button.h | 54 ++ firmware/drivers/fat.c | 1315 +++++++++++++++++++++++++++++++++++++++++++++ firmware/drivers/fat.h | 154 ++++++ firmware/drivers/i2c.c | 212 ++++++++ firmware/drivers/i2c.h | 33 ++ firmware/drivers/lcd.c | 680 +++++++++++++++++++++++ firmware/drivers/lcd.h | 162 ++++++ firmware/drivers/led.c | 70 +++ firmware/drivers/led.h | 50 ++ firmware/drivers/mas.c | 164 ++++++ firmware/drivers/mas.h | 54 ++ firmware/drivers/serial.c | 75 +++ firmware/drivers/serial.h | 31 ++ 16 files changed, 3525 insertions(+) create mode 100644 firmware/drivers/ata.c create mode 100644 firmware/drivers/ata.h create mode 100644 firmware/drivers/button.c create mode 100644 firmware/drivers/button.h create mode 100644 firmware/drivers/fat.c create mode 100644 firmware/drivers/fat.h create mode 100644 firmware/drivers/i2c.c create mode 100644 firmware/drivers/i2c.h create mode 100644 firmware/drivers/lcd.c create mode 100644 firmware/drivers/lcd.h create mode 100644 firmware/drivers/led.c create mode 100644 firmware/drivers/led.h create mode 100644 firmware/drivers/mas.c create mode 100644 firmware/drivers/mas.h create mode 100644 firmware/drivers/serial.c create mode 100644 firmware/drivers/serial.h (limited to 'firmware/drivers') diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c new file mode 100644 index 0000000000..cb4e1e6b90 --- /dev/null +++ b/firmware/drivers/ata.c @@ -0,0 +1,242 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Alan Korr + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "ata.h" +#include "kernel.h" +#include "led.h" + +#define ATA_DATA (*((volatile unsigned short*)0x06104100)) +#define ATA_ERROR (*((volatile unsigned char*)0x06100101)) +#define ATA_FEATURE ATA_ERROR +#define ATA_NSECTOR (*((volatile unsigned char*)0x06100102)) +#define ATA_SECTOR (*((volatile unsigned char*)0x06100103)) +#define ATA_LCYL (*((volatile unsigned char*)0x06100104)) +#define ATA_HCYL (*((volatile unsigned char*)0x06100105)) +#define ATA_SELECT (*((volatile unsigned char*)0x06100106)) +#define ATA_COMMAND (*((volatile unsigned char*)0x06100107)) +#define ATA_STATUS ATA_COMMAND +#define ATA_CONTROL (*((volatile unsigned char*)0x06100306)) + +#define SELECT_LBA 0x40 + +#define STATUS_BSY 0x80 +#define STATUS_RDY 0x40 +#define STATUS_DRQ 0x08 +#define STATUS_ERR 0x01 + +#define CONTROL_nIEN 0x02 +#define CONTROL_SRST 0x04 + +#define CMD_READ_SECTORS 0x20 +#define CMD_WRITE_SECTORS 0x30 +#define CMD_STANDBY_IMMEDIATE 0xE0 +#define CMD_STANDBY 0xE2 +#define CMD_SLEEP 0xE6 +#define CMD_SECURITY_FREEZE_LOCK 0xF5 + +static int wait_for_bsy(void) +{ + char timeout = current_tick + HZ; + while (TIME_BEFORE(current_tick, timeout) && (ATA_STATUS & STATUS_BSY)) + yield(); + + if (TIME_BEFORE(current_tick, timeout)) + return 1; + else + return 0; /* timeout */ +} + +static int wait_for_rdy(void) +{ + if (!wait_for_bsy()) + return 0; + return ATA_STATUS & STATUS_RDY; +} + +static int wait_for_start_of_transfer(void) +{ + if (!wait_for_bsy()) + return 0; + return (ATA_STATUS & (STATUS_RDY|STATUS_DRQ)) == (STATUS_RDY|STATUS_DRQ); +} + +static int wait_for_end_of_transfer(void) +{ + if (!wait_for_bsy()) + return 0; + return (ATA_STATUS & (STATUS_RDY|STATUS_DRQ)) == STATUS_RDY; +} + +int ata_read_sectors(unsigned long start, + unsigned char count, + void* buf) +{ + int i; + + if (!wait_for_rdy()) + return 0; + + led_turn_on(); + + ATA_NSECTOR = count; + ATA_SECTOR = start & 0xff; + ATA_LCYL = (start >> 8) & 0xff; + ATA_HCYL = (start >> 16) & 0xff; + ATA_SELECT = ((start >> 24) & 0xf) | SELECT_LBA; + ATA_COMMAND = CMD_READ_SECTORS; + + for (i=0; i> 8) & 0xff; + ATA_HCYL = (start >> 16) & 0xff; + ATA_SELECT = ((start >> 24) & 0xf) | SELECT_LBA; + ATA_COMMAND = CMD_WRITE_SECTORS; + + for (i=0; i= 5us */ + + ATA_CONTROL = CONTROL_nIEN; + sleep(HZ/400); /* >2ms */ + + return wait_for_rdy(); +} + +int ata_init(void) +{ + led_turn_off(); + + /* activate ATA */ + PADR |= 0x80; + + if (!ata_hard_reset()) + return -1; + + if (!check_registers()) + return -2; + + if (!check_harddisk()) + return -3; + + if (!freeze_lock()) + return -4; + + ATA_SELECT = SELECT_LBA; + ATA_CONTROL = CONTROL_nIEN; + + return 0; +} diff --git a/firmware/drivers/ata.h b/firmware/drivers/ata.h new file mode 100644 index 0000000000..50cd85d755 --- /dev/null +++ b/firmware/drivers/ata.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Alan Korr + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __ATA_H__ +#define __ATA_H__ + +extern int ata_hard_reset(void); +extern int ata_soft_reset(void); +extern int ata_init(void); +extern int ata_read_sectors(unsigned long start, + unsigned char count, + void* buf); +extern int ata_write_sectors(unsigned long start, + unsigned char count, + void* buf); + +#endif diff --git a/firmware/drivers/button.c b/firmware/drivers/button.c new file mode 100644 index 0000000000..a1abccd32a --- /dev/null +++ b/firmware/drivers/button.c @@ -0,0 +1,197 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Daniel Stenberg + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +/* + * Archos Jukebox Recorder button functions + */ + +#include "config.h" +#include "sh7034.h" +#include "button.h" + +#ifdef HAVE_RECORDER_KEYPAD + +/* AJBR buttons are connected to the CPU as follows: + * + * ON and OFF are connected to separate port B input pins. + * + * F1, F2, F3, and UP are connected to the AN4 analog input, each through + * a separate voltage divider. The voltage on AN4 depends on which button + * (or none, or a combination) is pressed. + * + * DOWN, PLAY, LEFT, and RIGHT are likewise connected to AN5. */ + +/* Button voltage levels on AN4 and AN5 */ +#define LEVEL1 50 +#define LEVEL2 125 +#define LEVEL3 175 +#define LEVEL4 225 + +/* Number of calls to get_button for a button to stop bouncing + * and to be considered held down. + * Should really use a hardware timer for this. + */ +#define BOUNCE_COUNT 200 +#define HOLD_COUNT 10000 + +static int last; /* Last button pressed */ +static int count; /* Number of calls button has been down */ + + +/* + *Initialize buttons + */ +void button_init() +{ +#ifndef SIMULATOR + /* Set PB4 and PB8 as input pins */ + PBCR1 &= 0xfffc; /* PB8MD = 00 */ + PBCR2 &= 0xfcff; /* PB4MD = 00 */ + PBIOR &= ~(PBDR_BTN_ON|PBDR_BTN_OFF); /* Inputs */ + + /* Set A/D to scan AN4 and AN5. + * This needs to be changed to scan other analog pins + * for battery level, etc. */ + ADCSR = 0; + ADCR = 0; + ADCSR = ADCSR_ADST | ADCSR_SCAN | 0x5; +#endif + last = BUTTON_NONE; + count = 0; +} + +/* + * Get button pressed from hardware + */ +static int get_raw_button (void) +{ + /* Check port B pins for ON and OFF */ + int data = PBDR; + if ((data & PBDR_BTN_ON) == 0) + return BUTTON_ON; + else if ((data & PBDR_BTN_OFF) == 0) + return BUTTON_OFF; + + /* Check AN4 pin for F1-3 and UP */ + data = ADDRAH; + if (data >= LEVEL4) + return BUTTON_F3; + else if (data >= LEVEL3) + return BUTTON_UP; + else if (data >= LEVEL2) + return BUTTON_F2; + else if (data >= LEVEL1) + return BUTTON_F1; + + /* Check AN5 pin for DOWN, PLAY, LEFT, RIGHT */ + data = ADDRBH; + if (data >= LEVEL4) + return BUTTON_DOWN; + else if (data >= LEVEL3) + return BUTTON_PLAY; + else if (data >= LEVEL2) + return BUTTON_LEFT; + else if (data >= LEVEL1) + return BUTTON_RIGHT; + + return BUTTON_NONE; +} + +/* + * Get the currently pressed button. + * Returns one of BUTTON_xxx codes, with possibly a modifier bit set. + * No modifier bits are set when the button is first pressed. + * BUTTON_HELD bit is while the button is being held. + * BUTTON_REL bit is set when button has been released. + */ +int button_get(void) +{ + int btn = get_raw_button(); + int ret; + + /* Last button pressed is still down */ + if (btn != BUTTON_NONE && btn == last) { + count++; + if (count == BOUNCE_COUNT) + return btn; + else if (count >= HOLD_COUNT) + return btn | BUTTON_HELD; + else + return BUTTON_NONE; + } + + /* Last button pressed now released */ + if (btn == BUTTON_NONE && last != BUTTON_NONE) + ret = last | BUTTON_REL; + else + ret = BUTTON_NONE; + + last = btn; + count = 0; + return ret; +} + +#elif HAVE_PLAYER_KEYPAD + +/* The player has all buttons on port pins: + + LEFT: PC0 + RIGHT: PC2 + PLAY: PC3 + STOP: PA11 + ON: PA5 + MENU: PC1 +*/ + +void button_init(void) +{ + /* set port pins as input */ + PAIOR &= ~0x820; +} + +int button_get(void) +{ + int porta = PADR; + int portc = PCDR; + int btn = 0; + + if ( portc & 1 ) + btn |= BUTTON_LEFT; + if ( portc & 2 ) + btn |= BUTTON_MENU; + if ( portc & 4 ) + btn |= BUTTON_RIGHT; + if ( portc & 8 ) + btn |= BUTTON_PLAY | BUTTON_UP; + if ( porta & 0x20 ) + btn |= BUTTON_ON; + if ( porta & 0x800 ) + btn |= BUTTON_STOP | BUTTON_DOWN; + + return btn; +} + +#endif + + + +/* ----------------------------------------------------------------- + * local variables: + * eval: (load-file "rockbox-mode.el") + * end: + */ diff --git a/firmware/drivers/button.h b/firmware/drivers/button.h new file mode 100644 index 0000000000..a8cc71b875 --- /dev/null +++ b/firmware/drivers/button.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Daniel Stenberg + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" + +void button_init (void); +int button_get (void); + +/* Shared button codes */ +#define BUTTON_NONE 0x0000 +#define BUTTON_ON 0x0001 +#define BUTTON_UP 0x0010 +#define BUTTON_DOWN 0x0020 +#define BUTTON_LEFT 0x0040 +#define BUTTON_RIGHT 0x0080 + +/* Button modifiers */ +#define BUTTON_HELD 0x4000 +#define BUTTON_REL 0x8000 + + +#ifdef HAVE_RECORDER_KEYPAD + +/* Recorder specific button codes */ +#define BUTTON_OFF 0x0002 +#define BUTTON_PLAY 0x0004 +#define BUTTON_F1 0x0100 +#define BUTTON_F2 0x0200 +#define BUTTON_F3 0x0400 + +#elif HAVE_PLAYER_KEYPAD + +/* Jukebox 6000 and Studio specific button codes */ +#define BUTTON_MENU 0x0002 +#define BUTTON_PLAY BUTTON_UP +#define BUTTON_STOP BUTTON_DOWN + +#endif diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c new file mode 100644 index 0000000000..328428a941 --- /dev/null +++ b/firmware/drivers/fat.c @@ -0,0 +1,1315 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Linus Nielsen Feltzing + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#define BLOCK_SIZE 512 + +#include "fat.h" +#include "ata.h" + +#define NUM_ROOT_DIR_ENTRIES 512 +#define NUM_FATS 2 +#define NUM_RESERVED_SECTORS 1 +#define NUM_BLOCKS 10000 + +struct dsksz2secperclus +{ + unsigned int disk_size; + unsigned int sec_per_cluster; +}; + +/* +** This is the table for FAT16 drives. NOTE that this table includes +** entries for disk sizes larger than 512 MB even though typically +** only the entries for disks < 512 MB in size are used. +** The way this table is accessed is to look for the first entry +** in the table for which the disk size is less than or equal +** to the DiskSize field in that table entry. For this table to +** work properly BPB_RsvdSecCnt must be 1, BPB_NumFATs +** must be 2, and BPB_RootEntCnt must be 512. Any of these values +** being different may require the first table entries DiskSize value +** to be changed otherwise the cluster count may be to low for FAT16. +*/ +struct dsksz2secperclus dsk_table_fat16 [] = +{ + { 8400, 0}, /* disks up to 4.1 MB, the 0 value for SecPerClusVal + trips an error */ + { 32680, 2}, /* disks up to 16 MB, 1k cluster */ + { 262144, 4}, /* disks up to 128 MB, 2k cluster */ + { 524288, 8}, /* disks up to 256 MB, 4k cluster */ + { 1048576, 16}, /* disks up to 512 MB, 8k cluster */ +/* The entries after this point are not used unless FAT16 is forced */ + { 2097152, 32}, /* disks up to 1 GB, 16k cluster */ + { 4194304, 64}, /* disks up to 2 GB, 32k cluster */ + { 0xFFFFFFFF, 0} /* any disk greater than 2GB, + 0 value for SecPerClusVal trips an error */ +}; + +int fat_num_rootdir_sectors(struct bpb *bpb); +int fat_first_sector_of_cluster(struct bpb *bpb, unsigned int cluster); +int fat_get_fatsize(struct bpb* bpb); +int fat_get_totsec(struct bpb* bpb); +int fat_get_rootdir_sector(struct bpb *bpb); +int fat_first_data_sector(struct bpb* bpb); +int fat_get_bpb(struct bpb *bpb); +int fat_bpb_is_sane(struct bpb *bpb); +int fat_create_fat(struct bpb* bpb); +int fat_dbg_read_block(char *name, unsigned char *buf); +int fat_flush_fat(struct bpb *bpb); +unsigned char *fat_cache_fat_sector(struct bpb *bpb, int secnum); +int fat_update_entry(struct bpb *bpb, int entry, unsigned int val); +unsigned int fat_getcurrdostime(unsigned short *dosdate, + unsigned short *dostime, + unsigned char *dostenth); +int fat_create_root_dir(struct bpb *bpb); +int fat_create_dos_name(unsigned char *name, unsigned char *newname); +int fat_create_file(struct bpb *bpb, unsigned int currdir, char *name); + +unsigned char *fat_cache[256]; +int fat_cache_dirty[256]; +char current_directory[256] = "\\"; +struct bpb *global_bpb; +struct disk_info di; + +extern int yyparse(void); + + +#ifdef TEST_FAT +void prompt(void) +{ + printf("C:%s>", current_directory); +} + +int main(int argc, char *argv[]) +{ + struct bpb bpb; + + memset(fat_cache, 0, sizeof(fat_cache)); + memset(fat_cache_dirty, 0, sizeof(fat_cache_dirty)); + + disk_init(NUM_BLOCKS); + + di.num_sectors = NUM_BLOCKS; + di.sec_per_track = 40; + di.num_heads = 250; + di.hidden_sectors = 0; + + if(read_disk("diskdump.dmp") < 0) + { + printf("*** Warning! The disk is uninitialized\n"); + } + else + { + fat_get_bpb(&bpb); + } + + global_bpb = &bpb; + prompt(); + yyparse(); + + dump_disk("diskdump.dmp"); + return 0; +} +#endif + +int fat_sec2cluster(struct bpb *bpb, unsigned int sec) +{ + int first_sec = fat_first_data_sector(bpb); + + if(sec < first_sec) + { + fprintf(stderr, "fat_sec2cluster() - Bad sector number (%d)\n", sec); + return -1; + } + + return ((sec - first_sec) / bpb->bpb_secperclus) + 2; +} + +int fat_last_cluster_in_chain(struct bpb *bpb, unsigned int cluster) +{ + int iseof = 0; + + switch(bpb->fat_type) + { + case FATTYPE_FAT12: + if(cluster >= 0x0ff8) + iseof = 1; + break; + case FATTYPE_FAT16: + if(cluster >= 0xfff8) + iseof = 1; + break; + case FATTYPE_FAT32: + if(cluster >= 0x0ffffff8) + iseof = 1; + break; + } + return iseof; +} + +int fat_cluster2sec(struct bpb *bpb, unsigned int cluster) +{ + int max_cluster = (fat_get_totsec(bpb) - fat_first_data_sector(bpb)) / + bpb->bpb_secperclus + 1; + + if(cluster > max_cluster) + { + fprintf(stderr, "fat_cluster2sec() - Bad cluster number (%d)\n", + cluster); + return -1; + } + + return fat_first_sector_of_cluster(bpb, cluster); +} + +int fat_first_sector_of_cluster(struct bpb *bpb, unsigned int cluster) +{ + return (cluster - 2) * bpb->bpb_secperclus + fat_first_data_sector(bpb); +} + +int fat_num_rootdir_sectors(struct bpb *bpb) +{ + return ((bpb->bpb_rootentcnt * 32) + (bpb->bpb_bytspersec - 1)) / + bpb->bpb_bytspersec; +} + +int fat_get_fatsize(struct bpb* bpb) +{ + if(bpb->bpb_fatsz16 != 0) + return bpb->bpb_fatsz16; + else + return bpb->bpb_fatsz32; +} + +int fat_get_totsec(struct bpb* bpb) +{ + if(bpb->bpb_totsec16 != 0) + return bpb->bpb_totsec16; + else + return bpb->bpb_totsec32; +} + +int fat_get_rootdir_sector(struct bpb *bpb) +{ + return bpb->bpb_rsvdseccnt + bpb->bpb_numfats * fat_get_fatsize(bpb); +} + +int fat_first_data_sector(struct bpb* bpb) +{ + int fatsz; + int rootdirsectors; + + fatsz = fat_get_fatsize(bpb); + + rootdirsectors = fat_num_rootdir_sectors(bpb); + + return bpb->bpb_rsvdseccnt + bpb->bpb_numfats * fatsz + rootdirsectors; +} + +int fat_format(struct disk_info *di, char *vol_name) +{ + unsigned char buf[BLOCK_SIZE]; + struct bpb bpb; + unsigned int root_dir_sectors; + unsigned int tmp1, tmp2; + int sec_per_clus = 0; + int fat_size; + int i = 0; + int err; + + while(di->num_sectors > dsk_table_fat16[i].disk_size) + { + i++; + } + + sec_per_clus = dsk_table_fat16[i].sec_per_cluster; + + if(sec_per_clus == 0) + { + fprintf(stderr, "fat_format() - Bad disk size (%u)\n", + di->num_sectors); + return -1; + } + + /* First calculate how many sectors we need for + the root directory */ + root_dir_sectors = ((NUM_ROOT_DIR_ENTRIES * 32) + + (BLOCK_SIZE - 1)) / BLOCK_SIZE; + + /* Now calculate the FAT size */ + tmp1 = di->num_sectors - (NUM_RESERVED_SECTORS + root_dir_sectors); + tmp2 = (256 * sec_per_clus) + NUM_FATS; + + fat_size = (tmp1 + (tmp2 - 1)) / tmp2; + + /* Now create the BPB. We must be careful, so we really make + it little endian. */ + memset(buf, 0xff, BLOCK_SIZE); + + strncpy(&buf[BS_OEMNAME], "MSWIN4.1", 8); + buf[BPB_BYTSPERSEC] = BLOCK_SIZE & 0xff; + buf[BPB_BYTSPERSEC+1] = BLOCK_SIZE >> 8; + buf[BPB_SECPERCLUS] = sec_per_clus; + buf[BPB_RSVDSECCNT] = 1; + buf[BPB_RSVDSECCNT+1] = 0; + buf[BPB_NUMFATS] = 2; + buf[BPB_ROOTENTCNT] = NUM_ROOT_DIR_ENTRIES & 0xff; + buf[BPB_ROOTENTCNT+1] = NUM_ROOT_DIR_ENTRIES >> 8; + buf[BPB_TOTSEC16] = di->num_sectors & 0xff; + buf[BPB_TOTSEC16+1] = di->num_sectors >> 8; + buf[BPB_MEDIA] = 0xf0; + buf[BPB_FATSZ16] = fat_size & 0xff; + buf[BPB_FATSZ16+1] = fat_size >> 8; + buf[BPB_SECPERTRK] = di->sec_per_track & 0xff; + buf[BPB_SECPERTRK+1] = di->sec_per_track >> 8; + buf[BPB_NUMHEADS] = di->num_heads & 0xff; + buf[BPB_NUMHEADS+1] = di->num_heads >> 8; + buf[BPB_HIDDSEC] = di->hidden_sectors & 0xff; + buf[BPB_HIDDSEC+1] = (di->hidden_sectors >> 8) & 0xff; + buf[BPB_HIDDSEC+2] = (di->hidden_sectors >> 16) & 0xff; + buf[BPB_HIDDSEC+3] = (di->hidden_sectors >> 24) & 0xff; + buf[BPB_TOTSEC32] = 0; + buf[BPB_TOTSEC32+1] = 0; + buf[BPB_TOTSEC32+2] = 0; + buf[BPB_TOTSEC32+3] = 0; + + buf[BS_DRVNUM] = 0; + buf[BS_RESERVED1] = 0; + buf[BS_BOOTSIG] = 0x29; + buf[BS_VOLID] = 0x78; + buf[BS_VOLID+1] = 0x56; + buf[BS_VOLID+2] = 0x34; + buf[BS_VOLID+3] = 0x12; + memset(&buf[BS_VOLLAB], ' ', 11); + strncpy(&buf[BS_VOLLAB], vol_name, MIN(11, strlen(vol_name)); + strncpy(&buf[BS_FILSYSTYPE], "FAT16 ", 8); + + /* The final signature */ + buf[BPB_LAST_WORD] = 0x55; + buf[BPB_LAST_WORD+1] = 0xaa; + + /* Now write the sector to disk */ + err = ata_write_sectors(0,1,buf); + if(err) + { + fprintf(stderr, "fat_format() - Couldn't write BSB (error code %i)\n", + err); + return -1; + } + + if(fat_get_bpb(&bpb) < 0) + { + fprintf(stderr, "fat_format() - Couldn't read BPB\n"); + return -1; + } + + if(fat_create_fat(&bpb) < 0) + { + fprintf(stderr, "fat_format() - Couldn't create FAT\n"); + return -1; + } + + if(fat_create_root_dir(&bpb) < 0) + { + fprintf(stderr, "fat_format() - Couldn't write root dir sector\n"); + return -1; + } + + return 0; +} + +int fat_get_bpb(struct bpb *bpb) +{ + unsigned char buf[BLOCK_SIZE]; + int err; + int fatsz; + int rootdirsectors; + int totsec; + int datasec; + int countofclusters; + + /* Read the sector */ + err = ata_read_sectors(0,1,buf); + if(err) + { + fprintf(stderr, "fat_get_bpb() - Couldn't read BPB (error code %i)\n", + err); + return -1; + } + + memset(bpb, 0, sizeof(struct bpb)); + + strncpy(bpb->bs_oemname, &buf[BS_OEMNAME], 8); + bpb->bs_oemname[8] = 0; + + bpb->bpb_bytspersec = buf[BPB_BYTSPERSEC] | (buf[BPB_BYTSPERSEC+1] << 8); + bpb->bpb_secperclus = buf[BPB_SECPERCLUS]; + bpb->bpb_rsvdseccnt = buf[BPB_RSVDSECCNT] | (buf[BPB_RSVDSECCNT+1] << 8); + bpb->bpb_numfats = buf[BPB_NUMFATS]; + bpb->bpb_rootentcnt = buf[BPB_ROOTENTCNT] | (buf[BPB_ROOTENTCNT+1] << 8); + bpb->bpb_totsec16 = buf[BPB_TOTSEC16] | (buf[BPB_TOTSEC16+1] << 8); + bpb->bpb_media = buf[BPB_MEDIA]; + bpb->bpb_fatsz16 = buf[BPB_FATSZ16] | (buf[BPB_FATSZ16+1] << 8); + bpb->bpb_secpertrk = buf[BPB_SECPERTRK] | (buf[BPB_SECPERTRK+1] << 8); + bpb->bpb_numheads = buf[BPB_NUMHEADS] | (buf[BPB_NUMHEADS+1] << 8); + bpb->bpb_hiddsec = buf[BPB_HIDDSEC] | (buf[BPB_HIDDSEC+1] << 8) | + (buf[BPB_HIDDSEC+2] << 16) | (buf[BPB_HIDDSEC+3] << 24); + bpb->bpb_totsec32 = buf[BPB_TOTSEC32] | (buf[BPB_TOTSEC32+1] << 8) | + (buf[BPB_TOTSEC32+2] << 16) | (buf[BPB_TOTSEC32+3] << 24); + + bpb->bs_drvnum = buf[BS_DRVNUM]; + bpb->bs_bootsig = buf[BS_BOOTSIG]; + if(bpb->bs_bootsig == 0x29) + { + bpb->bs_volid = buf[BS_VOLID] | (buf[BS_VOLID+1] << 8) | + (buf[BS_VOLID+2] << 16) | (buf[BS_VOLID+3] << 24); + strncpy(bpb->bs_vollab, &buf[BS_VOLLAB], 11); + strncpy(bpb->bs_filsystype, &buf[BS_FILSYSTYPE], 8); + } + + bpb->bpb_fatsz32 = (buf[BPB_FATSZ32] + (buf[BPB_FATSZ32+1] << 8)) | + (buf[BPB_FATSZ32+2] << 16) | (buf[BPB_FATSZ32+3] << 24); + + bpb->last_word = buf[BPB_LAST_WORD] | (buf[BPB_LAST_WORD+1] << 8); + + /* Determine FAT type */ + fatsz = fat_get_fatsize(bpb); + + if(bpb->bpb_totsec16 != 0) + totsec = bpb->bpb_totsec16; + else + totsec = bpb->bpb_totsec32; + + rootdirsectors = fat_num_rootdir_sectors(bpb); + datasec = totsec - (bpb->bpb_rsvdseccnt + bpb->bpb_numfats * fatsz + + rootdirsectors); + countofclusters = datasec / bpb->bpb_secperclus; + + if(countofclusters < 4085) + { + bpb->fat_type = FATTYPE_FAT12; + } + else + { + if(countofclusters < 65525) + { + bpb->fat_type = FATTYPE_FAT16; + } + else + { + bpb->fat_type = FATTYPE_FAT32; + } + } + + if(fat_bpb_is_sane(bpb) < 0) + { + fprintf(stderr, "fat_get_bpb() - BPB is not sane\n"); + return -1; + } + + return 0; +} + +int fat_bpb_is_sane(struct bpb *bpb) +{ + if(bpb->fat_type == FATTYPE_FAT32) + { + fprintf(stderr, "fat_bpb_is_sane() - Error: FAT32 not supported\n"); + return -1; + } + + if(bpb->bpb_bytspersec != 512) + { + fprintf(stderr, + "fat_bpb_is_sane() - Warning: sector size is not 512 (%i)\n", + bpb->bpb_bytspersec); + } + if(bpb->bpb_secperclus * bpb->bpb_bytspersec > 32768) + { + fprintf(stderr, + "fat_bpb_is_sane() - Warning: cluster size is larger than 32K " + "(%i * %i = %i)\n", + bpb->bpb_bytspersec, bpb->bpb_secperclus, + bpb->bpb_bytspersec * bpb->bpb_secperclus); + } + if(bpb->bpb_rsvdseccnt != 1) + { + fprintf(stderr, + "fat_bpb_is_sane() - Warning: Reserved sectors is not 1 (%i)\n", + bpb->bpb_rsvdseccnt); + } + if(bpb->bpb_numfats != 2) + { + fprintf(stderr, + "fat_bpb_is_sane() - Warning: NumFATS is not 2 (%i)\n", + bpb->bpb_numfats); + } + if(bpb->bpb_rootentcnt != 512) + { + fprintf(stderr, + "fat_bpb_is_sane() - Warning: RootEntCnt is not 512 (%i)\n", + bpb->bpb_rootentcnt); + } + if(bpb->bpb_totsec16 < 200) + { + if(bpb->bpb_totsec16 == 0) + { + fprintf(stderr, "fat_bpb_is_sane() - Error: TotSec16 is 0\n"); + return -1; + } + else + { + fprintf(stderr, + "fat_bpb_is_sane() - Warning: TotSec16 " + "is quite small (%i)\n", + bpb->bpb_totsec16); + } + } + if(bpb->bpb_media != 0xf0 && bpb->bpb_media < 0xf8) + { + fprintf(stderr, + "fat_bpb_is_sane() - Warning: Non-standard " + "media type (0x%02x)\n", + bpb->bpb_media); + } + if(bpb->last_word != 0xaa55) + { + fprintf(stderr, "fat_bpb_is_sane() - Error: Last word is not " + "0xaa55 (0x%04x)\n", bpb->last_word); + return -1; + } + return 0; +} + +int fat_create_fat(struct bpb* bpb) +{ + unsigned char *sec; + int i; + int secnum = 0; + int fatsz; + + if(fat_bpb_is_sane(bpb) < 0) + { + fprintf(stderr, "fat_create_fat() - BPB is not sane\n"); + return -1; + } + + if(bpb->bpb_fatsz16 != 0) + fatsz = bpb->bpb_fatsz16; + else + fatsz = bpb->bpb_fatsz32; + + sec = fat_cache_fat_sector(bpb, secnum); + if(!sec) + { + fprintf(stderr, "fat_create_fat() - Couldn't cache fat sector" + " (%d)\n", secnum); + return -1; + } + + fat_cache_dirty[secnum] = 1; + + /* First entry should have the media type in the + low byte and the rest of the bits set to 1. + The second should be the EOC mark. */ + memset(sec, 0, BLOCK_SIZE); + sec[0] = bpb->bpb_media; + if(bpb->fat_type == FATTYPE_FAT12) + { + sec[1] = 0xff; + sec[2] = 0xff; + } + if(bpb->fat_type == FATTYPE_FAT16) + { + sec[0] = bpb->bpb_media; + sec[1] = 0xff; + sec[2] = 0xff; + sec[3] = 0xff; + } + secnum++; + + for(i = 0; i < fatsz - 1;i++) + { + sec = fat_cache_fat_sector(bpb, secnum); + if(!sec) + { + fprintf(stderr, "fat_create_fat() - Couldn't cache fat sector" + " (%d)\n", i); + return -1; + } + fat_cache_dirty[secnum] = 1; + secnum++; + memset(sec, 0, BLOCK_SIZE); + } + + if(fat_flush_fat(bpb) < 0) + { + fprintf(stderr, "fat_create_fat() - Couldn't flush fat\n"); + return -1; + } + return 0; +} + +int fat_dbg_read_block(char *name, unsigned char *buf) +{ + FILE *f; + + f = fopen(name, "rb"); + if(f) + { + if(fread(buf, 1, 512, f) != 512) + { + fprintf(stderr, "Could not read file \"%s\"\n", name); + fclose(f); + return -1; + } + /* Now write the sector to disk */ + ata_write_sectors(0,1,buf); + fclose(f); + } + else + { + fprintf(stderr, "Could not open file \"%s\"\n", name); + return -1; + } + return 0; +} + +unsigned char *fat_cache_fat_sector(struct bpb *bpb, int secnum) +{ + unsigned char *sec; + + sec = fat_cache[secnum]; + /* Load the sector if it is not cached */ + if(!sec) + { + sec = malloc(bpb->bpb_bytspersec); + if(!sec) + { + fprintf(stderr, "fat_cache_fat_sector() - Out of memory\n"); + return NULL; + } + if(ata_read_sectors(secnum + bpb->bpb_rsvdseccnt,1,sec)) + { + fprintf(stderr, "fat_cache_fat_sector() - Could" + " not read sector %d\n", + secnum); + free(sec); + return NULL; + } + fat_cache[secnum] = sec; + } + return sec; +} + +int fat_update_entry(struct bpb *bpb, int entry, unsigned int val) +{ + unsigned char *sec; + unsigned char *sec2; + int fatsz; + int fatoffset; + int thisfatsecnum; + int thisfatentoffset; + unsigned int tmp; + + fatsz = fat_get_fatsize(bpb); + + if(bpb->fat_type == FATTYPE_FAT12) + { + fatoffset = entry + (entry / 2); + } + else + { + if(bpb->fat_type == FATTYPE_FAT16) + fatoffset = entry * 2; + else + fatoffset = entry * 4; + } + thisfatsecnum = fatoffset / bpb->bpb_bytspersec; + thisfatentoffset = fatoffset % bpb->bpb_bytspersec; + + sec = fat_cache_fat_sector(bpb, thisfatsecnum); + /* Load the sector if it is not cached */ + if(!sec) + { + fprintf(stderr, "fat_update_entry() - Could not cache sector %d\n", + thisfatsecnum); + return -1; + } + + fat_cache_dirty[thisfatsecnum] = 1; + + switch(bpb->fat_type) + { + case FATTYPE_FAT12: + if(thisfatentoffset == bpb->bpb_bytspersec - 1) + { + /* This entry spans a sector boundary. Take care */ + sec2 = fat_cache_fat_sector(bpb, thisfatsecnum + 1); + /* Load the sector if it is not cached */ + if(!sec2) + { + fprintf(stderr, "fat_update_entry() - Could not " + "cache sector %d\n", + thisfatsecnum + 1); + return -1; + } + fat_cache_dirty[thisfatsecnum + 1] = 1; + } + else + { + if(entry & 1) /* Odd entry number? */ + { + tmp = sec[thisfatentoffset] & 0xf0; + sec[thisfatentoffset] = tmp | (val & 0x0f); + sec[thisfatentoffset+1] = (val >> 4) & 0xff; + } + else + { + sec[thisfatentoffset] = val & 0xff; + tmp = sec[thisfatentoffset+1] & 0x0f; + sec[thisfatentoffset+1] = tmp | ((val >> 4) & 0xf0); + } + } + break; + case FATTYPE_FAT16: + *(unsigned short *)(&sec[thisfatentoffset]) = val; + break; + case FATTYPE_FAT32: + tmp = *(unsigned short *)(&sec[thisfatentoffset]) & 0xf000000; + val = tmp | (val & 0x0fffffff); + *(unsigned short *)(&sec[thisfatentoffset]) = val; + break; + } + return 0; +} + +int fat_read_entry(struct bpb *bpb, int entry) +{ + unsigned char *sec; + unsigned char *sec2; + int fatsz; + int fatoffset; + int thisfatsecnum; + int thisfatentoffset; + int val = -1; + + fatsz = fat_get_fatsize(bpb); + + if(bpb->fat_type == FATTYPE_FAT12) + { + fatoffset = entry + (entry / 2); + } + else + { + if(bpb->fat_type == FATTYPE_FAT16) + fatoffset = entry * 2; + else + fatoffset = entry * 4; + } + thisfatsecnum = fatoffset / bpb->bpb_bytspersec; + thisfatentoffset = fatoffset % bpb->bpb_bytspersec; + + sec = fat_cache_fat_sector(bpb, thisfatsecnum); + /* Load the sector if it is not cached */ + if(!sec) + { + fprintf(stderr, "fat_update_entry() - Could not cache sector %d\n", + thisfatsecnum); + return -1; + } + + switch(bpb->fat_type) + { + case FATTYPE_FAT12: + if(thisfatentoffset == bpb->bpb_bytspersec - 1) + { + /* This entry spans a sector boundary. Take care */ + sec2 = fat_cache_fat_sector(bpb, thisfatsecnum + 1); + /* Load the sector if it is not cached */ + if(!sec2) + { + fprintf(stderr, "fat_update_entry() - Could not " + "cache sector %d\n", + thisfatsecnum + 1); + return -1; + } + } + else + { + if(entry & 1) /* Odd entry number? */ + { + val = (sec[thisfatentoffset] & 0x0f) | + (sec[thisfatentoffset+1] << 4); + } + else + { + val = (sec[thisfatentoffset] & 0xff) | + ((sec[thisfatentoffset+1] & 0x0f) << 8); + } + } + break; + case FATTYPE_FAT16: + val = *(unsigned short *)(&sec[thisfatentoffset]); + break; + case FATTYPE_FAT32: + val = *(unsigned int *)(&sec[thisfatentoffset]); + break; + } + return val; +} + +int fat_flush_fat(struct bpb *bpb) +{ + int i; + int err; + unsigned char *sec; + int fatsz; + unsigned short d, t; + char m; + + fatsz = fat_get_fatsize(bpb); + + for(i = 0;i < 256;i++) + { + if(fat_cache[i] && fat_cache_dirty[i]) + { + printf("Flushing FAT sector %d\n", i); + sec = fat_cache[i]; + err = ata_write_sectors(i + bpb->bpb_rsvdseccnt,1,sec); + if(err) + { + fprintf(stderr, "fat_flush_fat() - Couldn't write" + " sector (%d)\n", i + bpb->bpb_rsvdseccnt); + return -1; + } + err = ata_write_sectors(i + bpb->bpb_rsvdseccnt + fatsz,1,sec); + if(err) + { + fprintf(stderr, "fat_flush_fat() - Couldn't write" + " sector (%d)\n", i + bpb->bpb_rsvdseccnt + fatsz); + return -1; + } + fat_cache_dirty[i] = 0; + } + } + + fat_getcurrdostime(&d, &t, &m); + return 0; +} + +unsigned int fat_getcurrdostime(unsigned short *dosdate, + unsigned short *dostime, + unsigned char *dostenth) +{ + struct timeb tb; + struct tm *tm; + + ftime(&tb); + tm = localtime(&tb.time); + + *dosdate = ((tm->tm_year - 80) << 9) | + ((tm->tm_mon + 1) << 5) | + (tm->tm_mday); + + *dostime = (tm->tm_hour << 11) | + (tm->tm_min << 5) | + (tm->tm_sec >> 1); + + *dostenth = (tm->tm_sec & 1) * 100 + tb.millitm / 10; + return 0; +} + +int fat_create_root_dir(struct bpb *bpb) +{ + unsigned char buf[BLOCK_SIZE]; + int fatsz; + int sec; + int res; + int i; + unsigned short dosdate; + unsigned short dostime; + unsigned char dostenth; + int num_root_sectors; + + fatsz = fat_get_fatsize(bpb); + + sec = bpb->bpb_rsvdseccnt + bpb->bpb_numfats * fatsz; + + memset(buf, 0, sizeof(buf)); + + strncpy(&buf[FATDIR_NAME], bpb->bs_vollab, 11); + buf[FATDIR_ATTR] = FAT_ATTR_VOLUME_ID; + buf[FATDIR_NTRES] = 0; + + fat_getcurrdostime(&dosdate, &dostime, &dostenth); + buf[FATDIR_WRTDATE] = dosdate & 0xff; + buf[FATDIR_WRTDATE+1] = dosdate >> 8; + buf[FATDIR_WRTTIME] = dostime & 0xff; + buf[FATDIR_WRTTIME+1] = dostime >> 8; + + printf("Writing rootdir to sector %d...\n", sec); + + res = ata_write_sectors(sec,1,buf); + if(res) + { + fprintf(stderr, "fat_create_root_dir() - Couldn't write sector (%d)\n", + sec); + return -1; + } + + printf("Clearing the rest of the root dir.\n"); + sec++; + num_root_sectors = bpb->bpb_rootentcnt * 32 / bpb->bpb_bytspersec; + memset(buf, 0, BLOCK_SIZE); + + for(i = 1;i < num_root_sectors;i++) + { + if(ata_write_sectors(sec++,1,buf)) + { + fprintf(stderr, "fat_create_root_dir() - " + " Couldn't write sector (%d)\n", sec); + return -1; + } + } + + return 0; +} + +int fat_get_next_cluster(struct bpb *bpb, unsigned int cluster) +{ + int next_cluster = fat_read_entry(bpb, cluster); + + if(fat_last_cluster_in_chain(bpb, next_cluster)) + return 0; + else + return next_cluster; +} + +int fat_add_dir_entry(struct bpb *bpb, unsigned int currdir, + struct fat_direntry *de) +{ + unsigned char buf[BLOCK_SIZE]; + unsigned char *eptr; + int i; + int err; + unsigned int sec; + unsigned int sec_cnt; + int need_to_update_last_empty_marker = 0; + int is_rootdir = (currdir == 0); + int done = 0; + unsigned char firstbyte; + + if(is_rootdir) + { + sec = fat_get_rootdir_sector(bpb); + } + else + { + sec = fat_first_sector_of_cluster(bpb, currdir); + } + + sec_cnt = 0; + + while(!done) + { + /* The root dir has a fixed size */ + if(is_rootdir) + { + if(sec_cnt >= bpb->bpb_rootentcnt * 32 / bpb->bpb_bytspersec) + { + /* We have reached the last sector of the root dir */ + if(need_to_update_last_empty_marker) + { + /* Since the directory is full, there is no room for + a marker, so we just exit */ + return 0; + } + else + { + fprintf(stderr, "fat_add_dir_entry() -" + " Root dir is full\n"); + return -1; + } + } + } + else + { + if(sec_cnt >= bpb->bpb_secperclus) + { + /* We have reached the end of this cluster */ + printf("Moving to the next cluster..."); + currdir = fat_get_next_cluster(bpb, currdir); + printf("new cluster is %d\n", currdir); + + if(!currdir) + { + /* This was the last in the chain, + we have to allocate a new cluster */ + /* TODO */ + } + } + } + + printf("Reading sector %d...\n", sec); + /* Read the next sector in the current dir */ + err = ata_read_sectors(sec,1,buf); + if(err) + { + fprintf(stderr, "fat_add_dir_entry() - Couldn't read dir sector" + " (error code %i)\n", err); + return -1; + } + + if(need_to_update_last_empty_marker) + { + /* All we need to do is to set the first entry to 0 */ + printf("Clearing the first entry in sector %d\n", sec); + buf[0] = 0; + done = 1; + } + else + { + /* Look for a free slot */ + for(i = 0;i < BLOCK_SIZE;i+=32) + { + firstbyte = buf[i]; + if(firstbyte == 0xe5 || firstbyte == 0) + { + printf("Found free slot at entry %d in sector %d\n", + i/32, sec); + eptr = &buf[i]; + memset(eptr, 0, 32); + strncpy(&eptr[FATDIR_NAME], de->name, 11); + eptr[FATDIR_ATTR] = de->attr; + eptr[FATDIR_NTRES] = 0; + + eptr[FATDIR_CRTTIMETENTH] = de->crttimetenth; + eptr[FATDIR_CRTDATE] = de->crtdate & 0xff; + eptr[FATDIR_CRTDATE+1] = de->crtdate >> 8; + eptr[FATDIR_CRTTIME] = de->crttime & 0xff; + eptr[FATDIR_CRTTIME+1] = de->crttime >> 8; + + eptr[FATDIR_WRTDATE] = de->wrtdate & 0xff; + eptr[FATDIR_WRTDATE+1] = de->wrtdate >> 8; + eptr[FATDIR_WRTTIME] = de->wrttime & 0xff; + eptr[FATDIR_WRTTIME+1] = de->wrttime >> 8; + + eptr[FATDIR_FILESIZE] = de->filesize & 0xff; + eptr[FATDIR_FILESIZE+1] = (de->filesize >> 8) & 0xff; + eptr[FATDIR_FILESIZE+2] = (de->filesize >> 16) & 0xff; + eptr[FATDIR_FILESIZE+3] = (de->filesize >> 24) & 0xff; + + /* Advance the last_empty_entry marker */ + if(firstbyte == 0) + { + i += 32; + if(i < BLOCK_SIZE) + { + buf[i] = 0; + /* We are done */ + done = 1; + } + else + { + /* We must fill in the first entry + in the next sector */ + need_to_update_last_empty_marker = 1; + } + } + + err = ata_write_sectors(sec,1,buf); + if(err) + { + fprintf(stderr, "fat_add_dir_entry() - " + " Couldn't write dir" + " sector (error code %i)\n", err); + return -1; + } + break; + } + } + } + sec++; + sec_cnt++; + } + + return 0; +} + +unsigned char fat_char2dos(unsigned char c) +{ + switch(c) + { + case 0xe5: /* Special kanji character */ + c = 0x05; + break; + case 0x22: + case 0x2a: + case 0x2b: + case 0x2c: + case 0x2e: + case 0x3a: + case 0x3b: + case 0x3c: + case 0x3d: + case 0x3e: + case 0x3f: + case 0x5b: + case 0x5c: + case 0x5d: + case 0x7c: + /* Illegal name */ + c = 0; + break; + + default: + if(c < 0x20) + { + /* Illegal name */ + c = 0; + } + break; + } + return c; +} + +int fat_create_dos_name(unsigned char *name, unsigned char *newname) +{ + unsigned char n[12]; + unsigned char c; + int i; + char *ext; + + if(strlen(name) > 12) + { + return -1; + } + + strcpy(n, name); + + ext = strchr(n, '.'); + if(ext) + { + *ext++ = 0; + } + + /* The file name is either empty, or there was only an extension. + In either case it is illegal. */ + if(n[0] == 0) + { + return -1; + } + + /* Name part */ + for(i = 0;n[i] && (i < 8);i++) + { + c = fat_char2dos(n[i]); + if(c) + { + newname[i] = toupper(c); + } + } + while(i < 8) + { + newname[i++] = ' '; + } + + /* Extension part */ + for(i = 0;ext && ext[i] && (i < 3);i++) + { + c = fat_char2dos(ext[i]); + if(c) + { + newname[8+i] = toupper(c); + } + } + while(i < 3) + { + newname[8+i++] = ' '; + } + return 0; +} + +int fat_create_dir(struct bpb *bpb, unsigned int currdir, char *name) +{ + struct fat_direntry de; + int err; + + printf("fat_create_file()\n"); + memset(&de, 0, sizeof(struct fat_direntry)); + if(fat_create_dos_name(name, de.name) < 0) + { + fprintf(stderr, "fat_create_file() - Illegal file name (%s)\n", name); + return -1; + } + + fat_getcurrdostime(&de.crtdate, &de.crttime, &de.crttimetenth); + de.wrtdate = de.crtdate; + de.wrttime = de.crttime; + de.filesize = 0; + de.attr = FAT_ATTR_DIRECTORY; + + err = fat_add_dir_entry(bpb, currdir, &de); + return 0; +} + +int fat_create_file(struct bpb *bpb, unsigned int currdir, char *name) +{ + struct fat_direntry de; + int err; + + printf("fat_create_file()\n"); + memset(&de, 0, sizeof(struct fat_direntry)); + if(fat_create_dos_name(name, de.name) < 0) + { + fprintf(stderr, "fat_create_file() - Illegal file name (%s)\n", name); + return -1; + } + fat_getcurrdostime(&de.crtdate, &de.crttime, &de.crttimetenth); + de.wrtdate = de.crtdate; + de.wrttime = de.crttime; + de.filesize = 0; + + err = fat_add_dir_entry(bpb, currdir, &de); + return err; +} + +void fat_fill_direntry(struct fat_direntry *de, char *buf) +{ + memset(de, 0, sizeof(struct fat_direntry)); + + strncpy(de->name, &buf[FATDIR_NAME], 11); + de->attr = buf[FATDIR_ATTR]; + de->crttimetenth = buf[FATDIR_CRTTIMETENTH]; + de->crtdate = buf[FATDIR_CRTDATE] | (buf[FATDIR_CRTDATE+1] << 8); + de->crttime = buf[FATDIR_CRTTIME] | (buf[FATDIR_CRTTIME+1] << 8); + de->wrtdate = buf[FATDIR_WRTDATE] | (buf[FATDIR_WRTDATE+1] << 8); + de->wrttime = buf[FATDIR_WRTTIME] | (buf[FATDIR_WRTTIME+1] << 8); + + de->filesize = buf[FATDIR_FILESIZE] | + (buf[FATDIR_FILESIZE+1] << 8) | + (buf[FATDIR_FILESIZE+2] << 16) | + (buf[FATDIR_FILESIZE+3] << 24); +} + +int fat_opendir(struct bpb *bpb, struct fat_dirent *ent, unsigned int currdir) +{ + int is_rootdir = (currdir == 0); + unsigned int sec; + int err; + + if(is_rootdir) + { + sec = fat_get_rootdir_sector(bpb); + } + else + { + sec = fat_first_sector_of_cluster(bpb, currdir); + } + + /* Read the first sector in the current dir */ + err = ata_read_sectors(sec,1,ent->cached_buf); + if(err) + { + fprintf(stderr, "fat_getfirst() - Couldn't read dir sector" + " (error code %i)\n", err); + return -1; + } + + ent->entry = 0; + ent->cached_sec = sec; + ent->num_sec = 0; + return 0; +} + +int fat_getnext(struct bpb *bpb, struct fat_dirent *ent, + struct fat_direntry *entry) +{ + int done = 0; + int i; + int err; + unsigned char firstbyte; + + while(!done) + { + /* Look for a free slot */ + for(i = ent->entry;i < BLOCK_SIZE/32;i++) + { + firstbyte = ent->cached_buf[i*32]; + if(firstbyte == 0xe5) + { + continue; + } + + if(firstbyte == 0) + { + return -1; + } + + fat_fill_direntry(entry, &ent->cached_buf[i*32]); + done = 1; + break; + } + + /* Next sector? */ + if(i >= BLOCK_SIZE/32) + { + ent->num_sec++; + ent->cached_sec++; + + /* Do we need to advance one cluster? */ + if(ent->num_sec >= bpb->bpb_secperclus) + { + ent->num_sec = 0; + ent->cached_sec = fat_get_next_cluster( + bpb, fat_sec2cluster(bpb, ent->cached_sec)); + if(!ent->cached_sec) + { + printf("End of cluster chain.\n"); + return -1; + } + } + + /* Read the next sector */ + err = ata_read_sectors(ent->cached_sec,1,ent->cached_buf); + if(err) + { + fprintf(stderr, "fat_getnext() - Couldn't read dir sector" + " (error code %i)\n", err); + return -1; + } + + i = 0; + } + else + { + i++; + } + ent->entry = i; + } + return 0; +} diff --git a/firmware/drivers/fat.h b/firmware/drivers/fat.h new file mode 100644 index 0000000000..f1dc8dc5a0 --- /dev/null +++ b/firmware/drivers/fat.h @@ -0,0 +1,154 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Linus Nielsen Feltzing + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef FAT_H +#define FAT_H + +#define FATTYPE_FAT12 0 +#define FATTYPE_FAT16 1 +#define FATTYPE_FAT32 2 + +#define BS_JMPBOOT 0 +#define BS_OEMNAME 3 +#define BPB_BYTSPERSEC 11 +#define BPB_SECPERCLUS 13 +#define BPB_RSVDSECCNT 14 +#define BPB_NUMFATS 16 +#define BPB_ROOTENTCNT 17 +#define BPB_TOTSEC16 19 +#define BPB_MEDIA 21 +#define BPB_FATSZ16 22 +#define BPB_SECPERTRK 24 +#define BPB_NUMHEADS 26 +#define BPB_HIDDSEC 28 +#define BPB_TOTSEC32 32 + +#define BS_DRVNUM 36 +#define BS_RESERVED1 37 +#define BS_BOOTSIG 38 +#define BS_VOLID 39 +#define BS_VOLLAB 43 +#define BS_FILSYSTYPE 54 + +#define BPB_FATSZ32 36 + +#define BPB_LAST_WORD 510 + +#define MIN(a,b) (((a) < (b))?(a):(b))) + +struct bpb +{ + char bs_oemname[9]; /* OEM string, ending with \0 */ + int bpb_bytspersec; /* Bytes per sectory, typically 512 */ + int bpb_secperclus; /* Sectors per cluster */ + int bpb_rsvdseccnt; /* Number of reserved sectors */ + int bpb_numfats; /* Number of FAT structures, typically 2 */ + int bpb_rootentcnt; /* Number of dir entries in the root */ + int bpb_totsec16; /* Number of sectors on the volume (old 16-bit) */ + int bpb_media; /* Media type (typically 0xf0 or 0xf8) */ + int bpb_fatsz16; /* Number of used sectors per FAT structure */ + int bpb_secpertrk; /* Number of sectors per track */ + int bpb_numheads; /* Number of heads */ + int bpb_hiddsec; /* Hidden sectors before the volume */ + unsigned int bpb_totsec32; /* Number of sectors on the volume + (new 32-bit) */ + /**** FAT12/16 specific *****/ + int bs_drvnum; /* Drive number */ + int bs_bootsig; /* Is 0x29 if the following 3 fields are valid */ + unsigned int bs_volid; /* Volume ID */ + char bs_vollab[12]; /* Volume label, 11 chars plus \0 */ + char bs_filsystype[9]; /* File system type, 8 chars plus \0 */ + + /**** FAT32 specific *****/ + int bpb_fatsz32; + + int last_word; /* Must be 0xaa55 */ + + int fat_type; /* What type of FAT is this? */ +}; + +#define FAT_ATTR_READ_ONLY 0x01 +#define FAT_ATTR_HIDDEN 0x02 +#define FAT_ATTR_SYSTEM 0x04 +#define FAT_ATTR_VOLUME_ID 0x08 +#define FAT_ATTR_DIRECTORY 0x10 +#define FAT_ATTR_ARCHIVE 0x20 +#define FAT_ATTR_LONG_NAME (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \ + FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID) + + +#define FATDIR_NAME 0 +#define FATDIR_ATTR 11 +#define FATDIR_NTRES 12 +#define FATDIR_CRTTIMETENTH 13 +#define FATDIR_CRTTIME 14 +#define FATDIR_CRTDATE 16 +#define FATDIR_LSTACCDATE 18 +#define FATDIR_FSTCLUSHI 20 +#define FATDIR_WRTTIME 22 +#define FATDIR_WRTDATE 24 +#define FATDIR_FSTCLUSLO 26 +#define FATDIR_FILESIZE 28 + +struct fat_direntry +{ + unsigned char name[12]; /* Name plus \0 */ + unsigned short attr; /* Attributes */ + unsigned char crttimetenth; /* Millisecond creation + time stamp (0-199) */ + unsigned short crttime; /* Creation time */ + unsigned short crtdate; /* Creation date */ + unsigned short lstaccdate; /* Last access date */ + unsigned short fstclushi; /* High word of first cluster + (0 for FAT12/16) */ + unsigned short wrttime; /* Last write time */ + unsigned short wrtdate; /* Last write date */ + unsigned short fstcluslo; /* Low word of first cluster */ + unsigned int filesize; /* File size in bytes */ +}; + +struct fat_context +{ + unsigned int curr_dir_sec; /* Current directory sector */ + +}; + +struct disk_info +{ + int num_sectors; + int sec_per_track; + int num_heads; + unsigned int hidden_sectors; +}; + +struct fat_dirent +{ + int entry; + unsigned int cached_sec; + unsigned int num_sec; + char cached_buf[BLOCK_SIZE]; +}; + +int fat_format(struct disk_info *di, char *vol_name); +int fat_create_file(struct bpb *bpb, unsigned int currdir, char *name); +int fat_opendir(struct bpb *bpb, struct fat_dirent *ent, unsigned int currdir); +int fat_getnext(struct bpb *bpb, struct fat_dirent *ent, + struct fat_direntry *entry); + +#endif diff --git a/firmware/drivers/i2c.c b/firmware/drivers/i2c.c new file mode 100644 index 0000000000..3a5603ddd4 --- /dev/null +++ b/firmware/drivers/i2c.c @@ -0,0 +1,212 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Linus Nielsen Feltzing + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "lcd.h" +#include "sh7034.h" +#include "debug.h" + +#define PB13 0x2000 +#define PB7 0x0080 +#define PB5 0x0020 + +/* cute little functions */ +#define SDA_LO (PBDR &= ~PB7) +#define SDA_HI (PBDR |= PB7) +#define SDA_INPUT (PBIOR &= ~PB7) +#define SDA_OUTPUT (PBIOR |= PB7) +#define SDA (PBDR & PB7) + +#define SCL_INPUT (PBIOR &= ~PB13) +#define SCL_OUTPUT (PBIOR |= PB13) +#define SCL_LO (PBDR &= ~PB13) +#define SCL_HI (PBDR |= PB13) +#define SCL (PBDR & PB13) + +/* arbitrary delay loop */ +#define DELAY do { int _x; for(_x=0;_x<20;_x++);} while (0) + +void i2c_start(void) +{ + SDA_OUTPUT; + SDA_HI; + SCL_HI; + SDA_LO; + DELAY; + SCL_LO; +} + +void i2c_stop(void) +{ + SDA_LO; + SCL_HI; + DELAY; + SDA_HI; +} + +void i2c_init(void) +{ + int i; + + /* make PB5, PB7 & PB13 general I/O */ + PBCR1 &= ~0x0c00; /* PB13 */ + PBCR2 &= ~0xcc00; /* PB5 abd PB7 */ + + /* PB5 is "MAS enable". make it output and high */ + PBIOR |= PB5; + PBDR |= PB5; + + /* Set the clock line to an output */ + PBIOR |= PB13; + + SDA_OUTPUT; + SDA_HI; + SCL_LO; + for (i=0;i<3;i++) + i2c_stop(); +} + +void i2c_ack(int bit) +{ + /* Here's the deal. The MAS is slow, and sometimes needs to wait + before it can receive the acknowledge. Therefore it forces the clock + low until it is ready. We need to poll the clock line until it goes + high before we release the ack. */ + + SCL_LO; /* Set the clock low */ + if ( bit ) + SDA_HI; + else + SDA_LO; + + SCL_INPUT; /* Set the clock to input */ + while(!SCL); /* and wait for the MAS to release it */ + + DELAY; + SCL_OUTPUT; + SCL_LO; +} + +int i2c_getack(void) +{ + unsigned short x; + + /* Here's the deal. The MAS is slow, and sometimes needs to wait + before it can send the acknowledge. Therefore it forces the clock + low until it is ready. We need to poll the clock line until it goes + high before we read the ack. */ + + SDA_LO; /* First, discharge the data line */ + SDA_INPUT; /* And set to input */ + SCL_LO; /* Set the clock low */ + SCL_INPUT; /* Set the clock to input */ + while(!SCL); /* and wait for the MAS to release it */ + + x = SDA; + if (x) + /* ack failed */ + return 0; + SCL_OUTPUT; + SCL_LO; + SDA_HI; + SDA_OUTPUT; + return 1; +} + +void i2c_outb(unsigned char byte) +{ + int i; + + /* clock out each bit, MSB first */ + for ( i=0x80; i; i>>=1 ) { + if ( i & byte ) + SDA_HI; + else + SDA_LO; + SCL_HI; + SCL_LO; + } + + SDA_HI; +} + +unsigned char i2c_inb(int ack) +{ + int i; + unsigned char byte = 0; + + /* clock in each bit, MSB first */ + for ( i=0x80; i; i>>=1 ) { + /* Tricky business. Here we discharge the data line by driving it low + and then set it to input to see if it stays low or goes high */ + SDA_LO; /* First, discharge the data line */ + SDA_INPUT; /* And set to input */ + SCL_HI; + if ( SDA ) + byte |= i; + SCL_LO; + SDA_OUTPUT; + } + + i2c_ack(ack); + + return byte; +} + +int i2c_write(int address, unsigned char* buf, int count ) +{ + int i,x=0; + + i2c_start(); + i2c_outb(address & 0xfe); + if (i2c_getack()) + { + for (i=0; i ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Linus Nielsen Feltzing + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef I2C_H +#define I2C_H + +extern void i2c_init(void); +extern int i2c_write(int device, unsigned char* buf, int count ); +extern int i2c_read(int device, unsigned char* buf, int count ); +extern int i2c_readmem(int device, int address, unsigned char* buf, int count ); +extern void i2c_outb(unsigned char byte); +extern unsigned char i2c_inb(int ack); +extern void i2c_start(void); +extern void i2c_stop(void); +extern void i2c_ack(int bit); +extern int i2c_getack(void); + +#endif diff --git a/firmware/drivers/lcd.c b/firmware/drivers/lcd.c new file mode 100644 index 0000000000..737bf92a6b --- /dev/null +++ b/firmware/drivers/lcd.c @@ -0,0 +1,680 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Alan Korr + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +#include "lcd.h" + +#ifndef SIMULATOR +/* + * About /CS,DS,SC,SD + * ------------------ + * + * LCD on JBP and JBR uses a SPI protocol to receive orders (SDA and SCK lines) + * + * - /CS -> Chip Selection line : + * 0 : LCD chipset is activated. + * - DS -> Data Selection line, latched at the rising edge + * of the 8th serial clock (*) : + * 0 : instruction register, + * 1 : data register; + * - SC -> Serial Clock line (SDA). + * - SD -> Serial Data line (SCK), latched at the rising edge + * of each serial clock (*). + * + * _ _ + * /CS \ / + * \______________________________________________________/ + * _____ ____ ____ ____ ____ ____ ____ ____ ____ _____ + * SD \/ D7 \/ D6 \/ D5 \/ D4 \/ D3 \/ D2 \/ D1 \/ D0 \/ + * _____/\____/\____/\____/\____/\____/\____/\____/\____/\_____ + * + * _____ _ _ _ _ _ _ _ ________ + * SC \ * \ * \ * \ * \ * \ * \ * \ * + * \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ + * _ _________________________________________________________ + * DS \/ + * _/\_________________________________________________________ + * + */ + +/* + * The only way to do logical operations in an atomic way + * on SH1 is using : + * + * or.b/and.b/tst.b/xor.b #imm,@(r0,gbr) + * + * but GCC doesn't generate them at all so some assembly + * codes are needed here. + * + * The Global Base Register gbr is expected to be zero + * and r0 is the address of one register in the on-chip + * peripheral module. + * + */ + + /* + * Enter a LCD session : + * + * QI(LCDR) &= ~(LCD_CS|LCD_DS|LCD_SD|LCD_SC); + */ +static void lcd_start (void) +{ + asm + ("and.b\t%0,@(r0,gbr)" + : + : /* %0 */ "I"(~(LCD_CS|LCD_DS|LCD_SD|LCD_SC)), + /* %1 */ "z"(LCDR)); +} + + /* + * Leave a LCD session : + * + * QI(LCDR) |= LCD_CS|LCD_RS|LCD_SD|LCD_SC; + */ +static void lcd_stop (void) +{ + asm + ("or.b\t%0,@(r0,gbr)" + : + : /* %0 */ "I"(LCD_CS|LCD_DS|LCD_SD|LCD_SC), + /* %1 */ "z"(LCDR)); +} + +static void lcd_byte (int byte,int rs) + /* + * char j = 0x80; + * if (rs) + * do + * { + * QI(LCDR) &= ~(LCD_SC|LCD_SD); + * if (j & byte) + * QI(LCDR) |= LCD_SD; + * QI(LCDR) |= LCD_SC|LCD_DS; + * } + * while ((unsigned char)j >>= 1); + * else + * do + * { + * QI(LCDR) &= ~(LCD_SC|LCD_SD|LCD_DS); + * if (j & byte) + * QI(LCDR) |= LCD_SD; + * QI(LCDR) |= LCD_SC; + * } + * while ((unsigned char)j >>= 1); + */ +{ + if (rs > 0) + asm + ("shll8\t%0\n" + "0:\n\t" + "and.b\t%2,@(r0,gbr)\n\t" + "shll\t%0\n\t" + "bf\t1f\n\t" + "or.b\t%3,@(r0,gbr)\n" + "1:\n\t" + "or.b\t%4,@(r0,gbr)\n" + "add\t#-1,%1\n\t" + "cmp/pl\t%1\n\t" + "bt\t0b" + : + : /* %0 */ "r"(((unsigned)byte)<<16), + /* %1 */ "r"(8), + /* %2 */ "I"(~(LCD_SC|LCD_SD)), + /* %3 */ "I"(LCD_SD), + /* %4 */ "I"(LCD_SC|LCD_DS), + /* %5 */ "z"(LCDR)); + else + asm + ("shll8\t%0\n" + "0:\n\t" + "and.b\t%2,@(r0,gbr)\n\t" + "shll\t%0\n\t" + "bf\t1f\n\t" + "or.b\t%3,@(r0,gbr)\n" + "1:\n\t" + "or.b\t%4,@(r0,gbr)\n" + "add\t#-1,%1\n\t" + "cmp/pl\t%1\n\t" + "bt\t0b" + : + : /* %0 */ "r"(((unsigned)byte)<<16), + /* %1 */ "r"(8), + /* %2 */ "I"(~(LCD_SC|LCD_DS|LCD_SD)), + /* %3 */ "I"(LCD_SD), + /* %4 */ "I"(LCD_SC), + /* %5 */ "z"(LCDR)); +} + +void lcd_data (int data) +{ + lcd_byte (data,1); +} + +void lcd_instruction (int instruction) +{ + lcd_byte (instruction,0); +} + +void lcd_zero (int length) +{ + length *= 8; + while (--length >= 0) + lcd_data (0); +} + +void lcd_fill (int data,int length) +{ + length *= 8; + while (--length >= 0) + lcd_data (data); +} + +void lcd_copy (void *data,int count) +{ + while (--count >= 0) + lcd_data (*((char *)data)++); +} + +static void lcd_goto (int x,int y) +{ + lcd_instruction (LCD_CURSOR(x,y)); +} + +/*** BACKLIGHT ***/ + +static void lcd_toggle_backlight (void) +{ + PAIOR ^= LCD_BL; +} + +static void lcd_turn_on_backlight (void) +{ + PAIOR |= LCD_BL; +} + +static void lcd_turn_off_backlight (void) +{ + PAIOR &= ~LCD_BL; +} + +/*** ICONS ***/ +#endif + +#ifdef HAVE_LCD_CHARCELLS +# ifndef JBP_OLD + +static char const lcd_ascii[] = +{ +/*****************************************************************************************/ +/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ +/* ************************************************************************************/ +/* 0x */ 0x00,0x01,0x02,0x03,0x00,0x00,0x00,0x00,0x04,0x05,0x00,0x00,0x00,0x00,0x00,0x00, +/* 1x */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 2x */ 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, +/* 3x */ 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, +/* 4x */ 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, +/* 5x */ 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, +/* 6x */ 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, +/* 7x */ 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x20,0x20,0x20,0x20,0x20, +/* 8x */ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +/* 9x */ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +/* Ax */ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +/* Bx */ 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, +/* Cx */ 0x41,0x41,0x41,0x41,0x41,0x41,0x20,0x43,0x45,0x45,0x45,0x45,0x49,0x49,0x49,0x49, +/* Dx */ 0x44,0x4E,0x4F,0x4F,0x4F,0x4F,0x4F,0x20,0x20,0x55,0x55,0x55,0x55,0x59,0x20,0x20, +/* Ex */ 0x61,0x61,0x61,0x61,0x61,0x61,0x20,0x63,0x65,0x65,0x65,0x65,0x69,0x69,0x69,0x69, +/* Fx */ 0x64,0x6E,0x6F,0x6F,0x6F,0x6F,0x6F,0x20,0x20,0x75,0x75,0x75,0x75,0x79,0x79,0x79 +/******/ + }; + +# else + +static char const lcd_ascii[] = + { +/*****************************************************************************************/ +/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ +/* ************************************************************************************/ +/* 0x */ 0x00,0x01,0x02,0x03,0x00,0x00,0x00,0x00,0x85,0x89,0x00,0x00,0x00,0x00,0x00,0x00, +/* 1x */ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +/* 2x */ 0x24,0x25,0x26,0x37,0x06,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33, +/* 3x */ 0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43, +/* 4x */ 0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,0x51,0x52,0x53, +/* 5x */ 0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0xA9,0x33,0xCE,0x00,0x15, +/* 6x */ 0x00,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73, +/* 7x */ 0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x24,0x24,0x24,0x24,0x24, +/* 8x */ 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, +/* 9x */ 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, +/* Ax */ 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, +/* Bx */ 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, +/* Cx */ 0x45,0x45,0x45,0x45,0x45,0x45,0x24,0x47,0x49,0x49,0x49,0x49,0x4D,0x4D,0x4D,0x4D, +/* Dx */ 0x48,0x52,0x53,0x53,0x53,0x53,0x53,0x24,0x24,0x59,0x59,0x59,0x59,0x5D,0x24,0x24, +/* Ex */ 0x65,0x65,0x65,0x65,0x65,0x65,0x24,0x67,0x69,0x69,0x69,0x69,0x6D,0x6D,0x6D,0x6D, +/* Fx */ 0x73,0x72,0x73,0x73,0x73,0x73,0x73,0x24,0x24,0x79,0x79,0x79,0x79,0x7D,0x24,0x7D +/******/ + }; + +# endif + +void lcd_puts (char const *string) +{ + while (*string) + lcd_data (LCD_ASCII(*string++)); +} + +void lcd_putns (char const *string,int n) +{ + while (n--) + lcd_data (LCD_ASCII(*string++)); +} + +void lcd_putc (int character) +{ + lcd_data (LCD_ASCII(character)); +} + +void lcd_pattern (int which,char const *pattern,int count) +{ + lcd_instruction (LCD_PRAM|which); + lcd_copy ((void *)pattern,count); +} + +void lcd_puthex (unsigned int value,int digits) +{ + switch (digits) { + case 8: + lcd_puthex (value >> 16,4); + case 4: + lcd_puthex (value >> 8,2); + case 2: + lcd_puthex (value >> 4,1); + case 1: + value &= 15; + lcd_putc (value+((value < 10) ? '0' : ('A'-10))); + } +} + + +/* HAVE_LCD_CHARCELLS */ +#elif defined(HAVE_LCD_BITMAP) + +/* + * All bitmaps have this format: + * Bits within a byte are arranged veritcally, LSB at top. + * Bytes are stored in column-major format, with byte 0 at top left, + * byte 1 is 2nd from top, etc. Bytes following left-most column + * starts 2nd left column, etc. + * + * Note: The HW takes bitmap bytes in row-major order. + * + * Memory copy of display bitmap + */ +unsigned char display[DISP_X][DISP_Y/8]; + +/* + * ASCII character generation tables + * + * This contains only the printable characters (0x20-0x7f). + * Each element in this table is a character pattern bitmap. + */ +#define ASCII_MIN 0x20 /* First char in table */ +#define ASCII_MAX 0x7f /* Last char in table */ + +extern const unsigned char char_gen_6x8[][5][1]; +extern const unsigned char char_gen_8x12[][7][2]; +extern const unsigned char char_gen_12x16[][11][2]; + + +/* All zeros and ones bitmaps for area filling */ +static const unsigned char zeros[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 }; +static const unsigned char ones[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff }; + +static int lcd_y; /* Current pixel row */ +static int lcd_x; /* Current pixel column */ +static int lcd_size; /* Current font width */ + +#ifndef SIMULATOR + +/* + * Initialize LCD + */ +void lcd_init (void) +{ + /* Initialize PB0-3 as output pins */ + PBCR2 &= 0xff00; /* MD = 00 */ + PBIOR |= 0x000f; /* IOR = 1 */ + + /* Initialize LCD */ + lcd_write (TRUE, LCD_CNTL_RESET); + lcd_write (TRUE, LCD_CNTL_POWER); + lcd_write (TRUE, LCD_CNTL_SEGREMAP); + lcd_write (TRUE, LCD_CNTL_OUTSCAN); + lcd_write (TRUE, LCD_CNTL_CONTRAST); + lcd_write (TRUE, 0x30); /* Contrast parameter */ + lcd_write (TRUE, LCD_CNTL_DISPON); + + lcd_clear_display(); + lcd_update(); +} + +/* + * Update the display. + * This must be called after all other LCD funtions that change the display. + */ +void lcd_update (void) +{ + int x, y; + + /* Copy display bitmap to hardware */ + for (y = 0; y < DISP_Y/8; y++) + { + lcd_write (TRUE, LCD_CNTL_PAGE | (y & 0xf)); + lcd_write (TRUE, LCD_CNTL_HIGHCOL); + lcd_write (TRUE, LCD_CNTL_LOWCOL); + + for (x = 0; x < DISP_X; x++) + lcd_write (FALSE, display[x][y]); + } +} + +static void lcd_write (bool command, int value) +{ + int bit; + + /* Enable chip select, set DC if data */ + PBDR &= ~(PBDR_LCD_CS1|PBDR_LCD_DC); + if (!command) + PBDR |= PBDR_LCD_DC; + + /* Send each bit, starting with MSB */ + for (bit = 0x80; bit > 0; bit >>= 1) + { + PBDR &= ~(PBDR_LCD_SDA|PBDR_LCD_SCK); + if (value & bit) + PBDR |= PBDR_LCD_SDA; + PBDR |= PBDR_LCD_SCK; + } + + /* Disable chip select */ + PBDR |= PBDR_LCD_CS1; +} + +#endif /* SIMULATOR */ + +/* + * Clear the display + */ +void lcd_clear_display (void) +{ + lcd_position (0, 0, 8); + memset (display, 0, sizeof display); +} + +/* + * Set current x,y position and font size + */ +void lcd_position (int x, int y, int size) +{ + if (x >= 0 && x < DISP_X && y >= 0 && y < DISP_Y) + { + lcd_x = x; + lcd_y = y; + } + + lcd_size = size; +} + +/* + * Display a string at current position and size + */ +void lcd_string (const char *str) +{ + int x = lcd_x; + int nx = lcd_size; + int ny, ch; + const unsigned char *src; + + if (nx == 12) + ny = 16; + else if (nx == 8) + ny = 12; + else + { + nx = 6; + ny = 8; + } + + while ((ch = *str++) != '\0') + { + if (ch == '\n' || lcd_x + nx > DISP_X) + { + /* Wrap to next line */ + lcd_x = x; + lcd_y += ny; + } + + if (lcd_y + ny > DISP_Y) + return; + + /* Limit to char generation table */ + if (ch >= ASCII_MIN && ch <= ASCII_MAX) + { + if (nx == 12) + src = char_gen_12x16[ch-ASCII_MIN][0]; + else if (nx == 8) + src = char_gen_8x12[ch-ASCII_MIN][0]; + else + src = char_gen_6x8[ch-ASCII_MIN][0]; + + lcd_bitmap (src, lcd_x, lcd_y, nx-1, ny, TRUE); + lcd_bitmap (zeros, lcd_x+nx-1, lcd_y, 1, ny, TRUE); + + lcd_x += nx; + } + } +} + +/* + * Display a bitmap at (x, y), size (nx, ny) + * clear is TRUE to clear destination area first + */ +void lcd_bitmap (const unsigned char *src, int x, int y, int nx, int ny, + bool clear) +{ + unsigned char *dst; + unsigned char *dst2 = &display[x][y/8]; + unsigned int data, mask, mask2, mask3, mask4; + int shift = y & 7; + + ny += shift; + + /* Calculate bit masks */ + mask4 = ~(0xfe << ((ny-1) & 7)); + if (clear) + { + mask = ~(0xff << shift); + mask2 = 0; + mask3 = ~mask4; + if (ny <= 8) + mask3 |= mask; + } + else + mask = mask2 = mask3 = 0xff; + + /* Loop for each column */ + for (x = 0; x < nx; x++) + { + dst = dst2; + dst2 += DISP_Y/8; + data = 0; + y = 0; + + if (ny > 8) + { + /* First partial row */ + data = *src++ << shift; + *dst = (*dst & mask) ^ data; + data >>= 8; + dst++; + + /* Intermediate rows */ + for (y = 8; y < ny-8; y += 8) + { + data |= *src++ << shift; + *dst = (*dst & mask2) ^ data; + data >>= 8; + dst++; + } + } + + /* Last partial row */ + if (y + shift < ny) + data |= *src++ << shift; + *dst = (*dst & mask3) ^ (data & mask4); + } +} + +/* + * Clear a rectangular area at (x, y), size (nx, ny) + */ +void lcd_clearrect (int x, int y, int nx, int ny) +{ + int i; + for (i = 0; i < nx; i++) + lcd_bitmap (zeros, x+i, y, 1, ny, TRUE); +} + +/* + * Fill a rectangular area at (x, y), size (nx, ny) + */ +void lcd_fillrect (int x, int y, int nx, int ny) +{ + int i; + for (i = 0; i < nx; i++) + lcd_bitmap (ones, x+i, y, 1, ny, TRUE); +} + +/* Invert a rectangular area at (x, y), size (nx, ny) */ +void lcd_invertrect (int x, int y, int nx, int ny) +{ + int i; + for (i = 0; i < nx; i++) + lcd_bitmap (ones, x+i, y, 1, ny, FALSE); +} + +#define DRAW_PIXEL(x,y) display[x][y/8] |= (1<<(y&7)) +#define CLEAR_PIXEL(x,y) display[x][y/8] &= ~(1<<(y&7)) + +void lcd_drawline( int x1, int y1, int x2, int y2 ) +{ + int numpixels; + int i; + int deltax, deltay; + int d, dinc1, dinc2; + int x, xinc1, xinc2; + int y, yinc1, yinc2; + + deltax = abs(x2 - x1); + deltay = abs(y2 - y1); + + if(deltax >= deltay) + { + numpixels = deltax; + d = 2 * deltay - deltax; + dinc1 = deltay * 2; + dinc2 = (deltay - deltax) * 2; + xinc1 = 1; + xinc2 = 1; + yinc1 = 0; + yinc2 = 1; + } + else + { + numpixels = deltay; + d = 2 * deltax - deltay; + dinc1 = deltax * 2; + dinc2 = (deltax - deltay) * 2; + xinc1 = 0; + xinc2 = 1; + yinc1 = 1; + yinc2 = 1; + } + numpixels++; /* include endpoints */ + + if(x1 > x2) + { + xinc1 = -xinc1; + xinc2 = -xinc2; + } + + if(y1 > y2) + { + yinc1 = -yinc1; + yinc2 = -yinc2; + } + + x = x1; + y = y1; + + for(i=0; i ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Alan Korr + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef __LCD_H__ +#define __LCD_H__ + +#include "sh7034.h" +#include "types.h" +#include "config.h" + +#define LCDR (PBDR+1) + +/* PA14 : /LCD-BL --- backlight */ +#define LCD_BL (14-8) + +#ifdef HAVE_LCD_CHARCELLS + /* JukeBox MP3 Player - AJB6K, AJBS20 */ +# define LCD_DS +1 // PB0 = 1 --- 0001 --- LCD-DS +# define LCD_CS +2 // PB1 = 1 --- 0010 --- /LCD-CS +# define LCD_SD +4 // PB2 = 1 --- 0100 --- LCD-SD +# define LCD_SC +8 // PB3 = 1 --- 1000 --- LCD-SC +# ifndef JBP_OLD +# define LCD_CONTRAST_SET ((char)0x50) +# define LCD_CRAM ((char)0x80) /* Characters */ +# define LCD_PRAM ((char)0xC0) /* Patterns */ +# define LCD_IRAM ((char)0x40) /* Icons */ +# else +# define LCD_CONTRAST_SET ((char)0xA8) +# define LCD_CRAM ((char)0xB0) /* Characters */ +# define LCD_PRAM ((char)0x80) /* Patterns */ +# define LCD_IRAM ((char)0xE0) /* Icons */ +# endif +# define LCD_ASCII(c) (lcd_ascii[(c)&255]) +# define LCD_CURSOR(x,y) ((char)(LCD_CRAM+((y)*16+(x)))) +# define LCD_ICON(i) ((char)(LCD_IRAM+i)) +# define LCD_ICON_BATTERY 0 +# define LCD_BATTERY_FRAME 0x02 +# define LCD_BATTERY_BAR1 0x08 +# define LCD_BATTERY_BAR2 0x04 +# define LCD_BATTERY_BAR3 0x10 +# define LCD_ICON_USB 2 +# define LCD_USB_LOGO 0xFF +# define LCD_ICON_PLAY 3 +# define LCD_PLAY_ICON 0xFF +# define LCD_ICON_RECORD 4 +# define LCD_RECORD_ICON 0x10 +# define LCD_ICON_STOP 5 +# define LCD_STOP_ICON 0x0F +# define LCD_ICON_AUDIO 5 +# define LCD_AUDIO_ICON 0xF0 +# define LCD_ICON_REVERSE 6 +# define LCD_REVERSE_ICON 0xFF +# define LCD_ICON_SINGLE 7 +# define LCD_SINGLE_ICON 0xFF +# define LCD_ICON_VOLUME0 9 +# define LCD_VOLUME_ICON 0x04 +# define LCD_VOLUME_BAR1 0x02 +# define LCD_VOLUME_BAR2 0x01 +# define LCD_ICON_VOLUME1 10 +# define LCD_VOLUME_BAR3 0x08 +# define LCD_VOLUME_BAR4 0x04 +# define LCD_VOLUME_BAR5 0x01 +# define LCD_ICON_PARAM 10 +# define LCD_PARAM_SYMBOL 0xF0 +#endif + +#ifdef HAVE_LCD_BITMAP +/* JukeBox MP3 Recorder - AJBR --- FIXME */ + +/* Defines from Alan on IRC, April 11th 2002 */ +#define LCD_SD +1 // PB0 = 1 --- 0001 +#define LCD_SC +2 // PB1 = 1 --- 0010 +#define LCD_RS +4 // PB2 = 1 --- 0100 +#define LCD_CS +8 // PB3 = 1 --- 1000 + +#define LCD_DS LCD_RS + +#define LCD_SET_LOWER_COLUMN_ADDRESS ((char)0x00) +#define LCD_SET_HIGHER_COLUMN_ADDRESS ((char)0x10) +#define LCD_SET_INTERNAL_REGULATOR_RESISTOR_RATIO ((char)0x20) +#define LCD_SET_POWER_CONTROL_REGISTER ((char)0x28) +#define LCD_SET_DISPLAY_START_LINE ((char)0x40) +#define LCD_SET_CONTRAST_CONTROL_REGISTER ((char)0x81) +#define LCD_SET_SEGMENT_REMAP ((char)0xA0) +#define LCD_SET_LCD_BIAS ((char)0xA2) +#define LCD_SET_ENTIRE_DISPLAY_OFF ((char)0xA4) +#define LCD_SET_ENTIRE_DISPLAY_ON ((char)0xA5) +#define LCD_SET_NORMAL_DISPLAY ((char)0xA6) +#define LCD_SET_REVERSE_DISPLAY ((char)0xA7) +#define LCD_SET_INDICATOR_OFF ((char)0xAC) +#define LCD_SET_INDICATOR_ON ((char)0xAD) +#define LCD_SET_DISPLAY_OFF ((char)0xAE) +#define LCD_SET_DISPLAY_ON ((char)0xAF) +#define LCD_SET_PAGE_ADDRESS ((char)0xB0) +#define LCD_SET_COM_OUTPUT_SCAN_DIRECTION ((char)0xC0) +#define LCD_SET_DISPLAY_OFFSET ((char)0xD3) +#define LCD_SET_READ_MODIFY_WRITE_MODE ((char)0xE0) +#define LCD_SOFTWARE_RESET ((char)0xE2) +#define LCD_NOP ((char)0xE3) +#define LCD_SET_END_OF_READ_MODIFY_WRITE_MODE ((char)0xEE) + + +#define DISP_X 112 +#define DISP_Y 64 + +#define LCD_WIDTH DISP_X /* Display width in pixels */ +#define LCD_HEIGHT DISP_Y /* Display height in pixels */ + +void lcd_init (void); +void lcd_update (void); +void lcd_clear_display (void); +void lcd_position (int x, int y, int size); +void lcd_string (const char *str); +void lcd_bitmap (const unsigned char *src, int x, int y, int nx, int ny, + bool clear); +void lcd_clearrect (int x, int y, int nx, int ny); +void lcd_fillrect (int x, int y, int nx, int ny); +void lcd_invertrect (int x, int y, int nx, int ny); +void lcd_drawline( int x1, int y1, int x2, int y2 ); +void lcd_drawpixel(int x, int y); +void lcd_clearpixel(int x, int y); +#endif + + +#ifndef SIMULATOR + +extern void lcd_data (int data); +extern void lcd_instruction (int instruction); +extern void lcd_zero (int length); +extern void lcd_fill (int data,int length); +extern void lcd_copy (void *data,int count); + +#ifdef HAVE_LCD_CHARCELLS + +extern void lcd_puts (char const *string); +extern void lcd_putns (char const *string,int n); +extern void lcd_putc (int character); +extern void lcd_puthex (unsigned int value,int digits); + +extern void lcd_pattern (int which,char const *pattern,int count); + +#endif /* HAVE_LCD_CHARCELLS */ + +#endif /* SIMULATOR */ + +#endif /* __LCD_H__ */ diff --git a/firmware/drivers/led.c b/firmware/drivers/led.c new file mode 100644 index 0000000000..d01c9de612 --- /dev/null +++ b/firmware/drivers/led.c @@ -0,0 +1,70 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Alan Korr + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" + +#include + +#define turn_on() \ + set_bit (LEDB,PBDR_ADDR+1) + +#define turn_off() \ + clear_bit (LEDB,PBDR_ADDR+1) + +#define start_timer() \ + set_bit (2,TSTR_ADDR) + +#define stop_timer() \ + clear_bit (2,TSTR_ADDR) + +#define eoi(subinterrupt) \ + clear_bit (subinterrupt,TSR2_ADDR) + +#define set_volume(volume) \ + GRA2 = volume & 0x7FFF + + +void led_set_volume (unsigned short volume) +{ + volume <<= 10; + if (volume == 0) + led_turn_off (); + else if (volume == 0x8000) + led_turn_on (); + else + { + set_volume (volume); + start_timer (); + } +} + +#pragma interrupt +void IMIA2 (void) +{ + turn_off (); + eoi (0); +} + +#pragma interrupt +void OVI2 (void) +{ + turn_on (); + eoi (2); +} + diff --git a/firmware/drivers/led.h b/firmware/drivers/led.h new file mode 100644 index 0000000000..955c59aa81 --- /dev/null +++ b/firmware/drivers/led.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Alan Korr + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef __LED_H__ +#define __LED_H__ + +#include +#include + +#define LEDB 6 /* PB6 : red LED */ + +static inline void led_turn_off (void) +{ + clear_bit (LEDB,PBDR+1); + clear_bit (2,TSTR_ADDR); +} + +static inline void led_turn_on (void) +{ + set_bit (LEDB,PBDR+1); + set_bit (2,TSTR_ADDR); +} + +static inline void led_toggle (void) +{ + toggle_bit (LEDB,PBDR+1); +} + +extern void led_set_volume (unsigned short volume); +extern void led_setup (void); + +#endif + + diff --git a/firmware/drivers/mas.c b/firmware/drivers/mas.c new file mode 100644 index 0000000000..decfff612b --- /dev/null +++ b/firmware/drivers/mas.c @@ -0,0 +1,164 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Linus Nielsen Feltzing + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "i2c.h" +#include "debug.h" +#include "mas.h" + +/* note: 'len' is number of 32-bit words, not number of bytes! */ +int mas_readmem(int bank, int addr, unsigned long* dest, int len) +{ + int i; + unsigned char buf[16]; + + i=0; + buf[i++] = MAS_DATA_WRITE; + buf[i++] = bank?0xf0:0xe0; + buf[i++] = 0x00; + buf[i++] = (len & 0xff00) >> 8; + buf[i++] = len & 0xff; + buf[i++] = (addr & 0xff00) >> 8; + buf[i++] = addr & 0xff; + + /* send read command */ + if (i2c_write(MAS_DEV_WRITE,buf,i)) + { + return -1; + } + + return mas_devread(dest, len); +} + +/* note: 'len' is number of 32-bit words, not number of bytes! */ +int mas_writemem(int bank, int addr, unsigned long* src, int len) +{ + int i, j; + unsigned char buf[60]; + unsigned char* ptr = (unsigned char*)src; + + i=0; + buf[i++] = MAS_DATA_WRITE; + buf[i++] = bank; + buf[i++] = 0x00; + buf[i++] = (len & 0xff00) >> 8; + buf[i++] = len & 0xff; + buf[i++] = (addr & 0xff00) >> 8; + buf[i++] = addr & 0xff; + + j = 0; + while(len--) { + buf[i++] = ptr[j*4+1]; + buf[i++] = ptr[j*4+0]; + buf[i++] = 0; + buf[i++] = ptr[j*4+2]; + j += 4; + } + + /* send write command */ + if (i2c_write(MAS_DEV_WRITE,buf,i)) + { + return -1; + } + + return 0; +} + +int mas_readreg(int reg) +{ + int i; + unsigned char buf[16]; + + i=0; + buf[i++] = MAS_DATA_WRITE; + buf[i++] = 0xd0 | reg >> 4; + buf[i++] = (reg & 0x0f) << 4; + + /* send read command */ + if (i2c_write(MAS_DEV_WRITE,buf,i)) + { + return -1; + } + + if(mas_devread((unsigned long *)buf, 1)) + { + return -2; + } + + return buf[0] | buf[1] << 8 | buf[3] << 16; +} + +int mas_writereg(int reg, unsigned short val) +{ + int i; + unsigned char buf[16]; + + i=0; + buf[i++] = MAS_DATA_WRITE; + buf[i++] = 0x90 | reg >> 4; + buf[i++] = ((reg & 0x0f) << 4) | (val & 0x0f); + buf[i++] = (val >> 12) & 0xff; + buf[i++] = (val >> 4) & 0xff; + + /* send write command */ + if (i2c_write(MAS_DEV_WRITE,buf,i)) + { + return -1; + } + return 0; +} + +/* note: 'len' is number of 32-bit words, not number of bytes! */ +int mas_devread(unsigned long *dest, int len) +{ + unsigned char* ptr = (unsigned char*)dest; + int ret = 0; + int i; + + /* handle read-back */ + i2c_start(); + i2c_outb(MAS_DEV_WRITE); + if (i2c_getack()) { + i2c_outb(MAS_DATA_READ); + if (i2c_getack()) { + i2c_start(); + i2c_outb(MAS_DEV_READ); + if (i2c_getack()) { + for (i=0;len;i++) { + len--; + ptr[i*4+1] = i2c_inb(0); + ptr[i*4+0] = i2c_inb(0); + ptr[i*4+3] = i2c_inb(0); + if(len) + ptr[i*4+2] = i2c_inb(0); + else + ptr[i*4+2] = i2c_inb(1); /* NAK the last byte */ + } + } + else + ret = -3; + } + else + ret = -2; + } + else + ret = -1; + + i2c_stop(); + + return ret; +} diff --git a/firmware/drivers/mas.h b/firmware/drivers/mas.h new file mode 100644 index 0000000000..65e23f1498 --- /dev/null +++ b/firmware/drivers/mas.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Linus Nielsen Feltzing + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef _MAS_H_ +#define _MAS_H_ + +#define MAS_BANK_D0 0 +#define MAS_BANK_D1 1 + +/* + MAS I2C defs +*/ +#define MAS_ADR 0x3a +#define MAS_DEV_WRITE (MAS_ADR | 0x00) +#define MAS_DEV_READ (MAS_ADR | 0x01) + +/* registers..*/ +#define MAS_DATA_WRITE 0x68 +#define MAS_DATA_READ 0x69 +#define MAS_CONTROL 0x6a + +/* + * MAS register + */ +#define MAS_REG_DCCF 0x8e +#define MAS_REG_MUTE 0xaa +#define MAS_REG_PIODATA 0xc8 +#define MAS_REG_StartUpConfig 0xe6 +#define MAS_REG_KPRESCALE 0xe7 +#define MAS_REG_KBASS 0x6b +#define MAS_REG_KTREBLE 0x6f + +int mas_readmem(int bank, int addr, unsigned long* dest, int len); +int mas_writemem(int bank, int addr, unsigned long* src, int len); +int mas_devread(unsigned long *buf, int len); +int mas_readreg(int reg); +int mas_writereg(int reg, unsigned short val); + +#endif diff --git a/firmware/drivers/serial.c b/firmware/drivers/serial.c new file mode 100644 index 0000000000..d101ab8095 --- /dev/null +++ b/firmware/drivers/serial.c @@ -0,0 +1,75 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Alan Korr + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "serial.h" + +#define TDRE 7 /* transmit data register empty */ +#define RDRF 6 /* receive data register full */ +#define ORER 5 /* overrun error */ +#define FER 4 /* frame error */ +#define PER 3 /* parity error */ + +static int serial_byte,serial_flag; + +void serial_putc (char byte) +{ + while (!(SSR1 & (1< ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Alan Korr + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef __SERIAL_H__ +#define __SERIAL_H__ + +#include +#include + +extern void serial_putc (char); +extern void serial_puts (char const *); +extern int serial_getc (void); +extern void serial_setup (int); + +#endif -- cgit v1.2.3