Quantcast
Channel: vinxru
Viewing all articles
Browse latest Browse all 319

[Программирование] Расширение библиотеки PetitFat

$
0
0
В своем SD-контроллере, для работы с файловой системой FAT16/FAT32 я применил библиотеку PatitFat. Библиотека крутится внутри микроконтроллера с 8 Кб ПЗУ и 1 Кб ОЗУ. Причем из 1 Кб, половина сразу уходит на внутренние нужны и стек. Памяти в обрез, зато такой МК стоит 15 рублей (при заказе из Китая, включая пересыл).

Эта библиотека не умеет изменять размер размер файлов, создавать новые файлы и папки, а так же удалять их. Поэтому я написал расширение, позволяющее это делать.

Принимаются критические замечания от людей знающих Си и Fat32.

/*-----------------------------------------------------------------------*/
/* Format name for directory entry                                       */
/*-----------------------------------------------------------------------*/

// Стандартная функция create_name изменяет второй аргумент, а это
// нам не надо.

static FRESULT create_name_2(DIR* dj, const char* name) {
  return create_name(dj, &name);
}

/*-----------------------------------------------------------------------*/
/* Save sector                                                           */
/*-----------------------------------------------------------------------*/

// Сохранить сектор на диск

static DRESULT disk_write(const BYTE* buf, DWORD sector) {  
  DRESULT r;
  // Начало операции записи
  if(r = disk_writep(0, sector)) return r;
  // Запись данных
  if(r = disk_writep(buf, 512)) return r;
  // Окончание операции записи
  return disk_writep(0, 0);
}

/*-----------------------------------------------------------------------*/
/* Reset free space counters on start                                    */
/*-----------------------------------------------------------------------*/

// Вызов этой функции надо добавить в конец pf_mount
//
// if(fs->fs_type==FS_FAT32 && LD_WORD(buf+BPB_RsvdSecCnt-13)>0) 
//   if(resetFreeSpace(bsect+1, buf512)) 
//     return FR_DISK_ERR; 

static DRESULT resetFreeSpaceFat32(unsigned long bootSectorPlus1, BYTE* buf512) {  
  DRESULT r;
  if(r = disk_readp(buf512, bootSectorPlus1, 0, 512)) return r;
  if(*(unsigned long*)buf512 != 0x41615252) return RES_OK;
  if(*(unsigned long*)(buf512+0x1E4) != 0x61417272) return RES_OK;
  if(*(unsigned long*)(buf512+0x1FC) != 0xAA550000) return RES_OK;
  *(unsigned long*)(buf512+0x1E8) = 0xFFFFFFFF;
  *(unsigned long*)(buf512+0x1EC) = 0xFFFFFFFF;
  return disk_write(buf512, bootSectorPlus1);
}

/*-----------------------------------------------------------------------*/
/* Save fat sector in all FAT tables                                     */
/*-----------------------------------------------------------------------*/

static char saveFatSector(unsigned long sector, const BYTE* buf) { 
  if(sector == -1) return 0;
  if(FatFs->fatbase2) if(disk_write(buf, FatFs->fatbase2 + sector)) return 1;
  return disk_write(buf, FatFs->fatbase + sector);
}

/*-----------------------------------------------------------------------*/
/* Alloc cluster                                                         */
/*-----------------------------------------------------------------------*/

static FRESULT allocClusterFat32(unsigned long* cluster, BYTE* buf) {
  unsigned char i;
  unsigned long s, sectorsInFat, *a;

  // Кол-во секторов в таблице FAT
  sectorsInFat = (FatFs->n_fatent + 127) / 128;
  // В первом секторе первых 2 числа пропускаем
  i = 2, a = ((unsigned long*)buf)+2;
  // Последовательно перебираем сектора
  for(s = 0; s < sectorsInFat; s++) {
    if(disk_readp(buf, FatFs->fatbase + s, 0, 512)) return FR_DISK_ERR;
    // Среди 128 чисел в секторе ищем 0
    for(; i < 128; i++, a++) {
      if(*a == 0) {
        // Мы могли вылететь за пределы таблицы FAT
        *cluster = i + s * 128;
        if(*cluster >= FatFs->n_fatent) return FR_NO_FREE_SPACE;
        // Помечаем кластер как последний
        *a = 0x0FFFFFFF;
        // Сохраняем изменения
        if(saveFatSector(s, buf)) return FR_DISK_ERR;
        // Ок
        return FR_OK;
      }
    }
    // Для цикла выше.
    i = 0, a = (unsigned long*)buf;
  }
  // Свобожных кластеров нет.
  return FR_NO_FREE_SPACE;
}

/*-----------------------------------------------------------------------*/
/* Add cluster in chain                                                  */
/*-----------------------------------------------------------------------*/

static char setNextClusterFat32(unsigned long cluster, unsigned long nextCluster, BYTE* buf512) {
  unsigned long sector;
  sector = cluster/128;
  if(disk_readp(buf512, FatFs->fatbase + sector, 0, 512)) return 1;
  ((unsigned long*)buf512)[cluster % 128] = nextCluster;  
  return saveFatSector(sector, buf512);
}

/*-----------------------------------------------------------------------*/
/* Alloc directoy entry                                                  */
/*-----------------------------------------------------------------------*/

static FRESULT allocEntryFat32(DIR* dj, const char* name, BYTE* buf512, BYTE** outEntry) {
	FRESULT res;
	BYTE c, *a;
  unsigned char i;
  DWORD cluster;
 
  // Форматируем имя
  if(res = create_name_2(dj, name)) return res;
  // Ищем это имя в папке
  res = dir_find(dj, buf512);
  if(res != FR_NO_FILE) {
    if(res == FR_OK) return FR_FILE_EXISTS;
    return res;
  }
  // Перематываем папку в начало
  if(res = dir_rewind(dj)) return res;
  // Ищем в папке пустой описатель
	res = FR_NO_FILE;
	while (dj->sect) {
		if (disk_readp(buf512, dj->sect, 0, 512)) return FR_DISK_ERR;
    for(i=0, a=buf512; i<16; i++, a+=32) {
      if((a[DIR_Attr] & AM_VOL) == 0 && (*a == 0 || *a == 0xE5)) {
        // Если мы добавляем в последний элемент, то надо надо создать последний элемент
        if(*a==0) {
          if(i<15) {
            memset(a+32, 0, 512-32-i*32); 
          } else
          if(dj->index/16+1 < FatFs->csize) {
            // Это последний элемент в секторе, но не в кластере
            memset(buf512, 0, 512);
          	if(disk_write(buf512, dj->sect+1)) return FR_DISK_ERR;
            // Восстанавливаем буфер
            if(disk_readp(buf512, dj->sect, 0, 512)) return FR_DISK_ERR;
          }
        }
        // Инициализируем найденный элемент
        memset(a, 0, 32);
        goto break2;
      }
    }
    // Следующий сектор
		dj->index += 15;
		if(res = dir_next(dj)) {
      if(res != FR_NO_FILE) return res;
      // В папке может быть не более 65535 файлов
      if((unsigned short)dj->index==65535)
        return FR_DIR_FULL;
      // Добавить еще один кластер к папке.
      if(res = allocClusterFat32(&cluster, buf512)) return res;
      if(setNextClusterFat32(dj->clust, cluster, buf512)) return FR_DISK_ERR; 
      dj->clust = cluster;
      dj->sect = clust2sect(cluster);
      // Инициализируем сектор
      memset(buf512, 0, 512);
      a = buf512;
      break;
    }
	}
break2:
  // Запоняем имя
  dj->fn = a;
  create_name_2(dj, name);
  // Возвращаем указатель на найденный элемент
  if(outEntry) *outEntry = a;
  // Ок
  return FR_OK;
}

/*-----------------------------------------------------------------------*/
/* Free cluster chain                                                    */
/*-----------------------------------------------------------------------*/

static FRESULT freeChain(unsigned long cluster, BYTE* buf512) {
  unsigned long s, s1, *a;
  s1 = -1;
  while(cluster >= 2 && cluster < FatFs->n_fatent) {
    s = cluster/128;
    if(s!=s1) {
      if(saveFatSector(s1, buf512)) return FR_DISK_ERR;
      s1 = s;
      if(disk_readp(buf512, FatFs->fatbase+s, 0, 512)) return FR_DISK_ERR;
    }
    a = (unsigned long*)(buf512 + (cluster%128)*4);
    cluster = *a;
    *a = 0;
  }
  if(saveFatSector(s1, buf512)) return FR_DISK_ERR;
  return FR_OK;
}

/*-----------------------------------------------------------------------*/
/* Is empty folder                                                       */
/*-----------------------------------------------------------------------*/

static FRESULT isEmptyFolder(DWORD cluster, BYTE* buf512) {
  DIR dir;
  FRESULT res;
  BYTE* a;
  unsigned char i;
 
  // Для последовательного четния всех секторов папки
  dir.sclust = cluster;
  if(res = dir_rewind(&dir)) return res;
  // Первые два элемента пропускаем (должны быть .. и .)
  i = 2, a = buf512 + 64;
  // Обрабатываем все сектора
  while(dir.sect) {
    if(disk_readp(buf512, dir.sect, 0, 512)) return FR_DISK_ERR;
    // В секторе 16 описателей
    for(; i < 16; i++, a += 32) {
      // Это был последний описатель
      if(*a == 0) return FR_OK;
      // Найден файл или папка, ошибка
      if((a[DIR_Attr] & AM_VOL) == 0 && *a != 0xE5) return FR_DIR_NOT_EMPTY;
    }
    // Следующий сектор
    dir.index += 15;
    if(res = dir_next(&dir)) {
      // Это был последний сектор, всё ок.
      if(res == FR_NO_FILE) return FR_OK;
      // Ошибка
      return res;
    }
    // Для цикла выше
    i = 0, a = buf512;
  }
}

/*-----------------------------------------------------------------------*/
/* Convert bytyes to clusters                                            */
/*-----------------------------------------------------------------------*/

static unsigned long bytesToClusters(unsigned long sizeInBytes) {
  unsigned long bytesInCluster;
  bytesInCluster = FatFs->csize*512;
  return (sizeInBytes + bytesInCluster - 1) / bytesInCluster;
}

/*-----------------------------------------------------------------------*/
/* Create file                                                           */
/*-----------------------------------------------------------------------*/

FRESULT pf_createFile(const char *path, const char* new_name, BYTE* buf512) {
  DIR dir;
  FRESULT res;

  if(FatFs->fs_type != FS_FAT32) return FR_NO_FREE_SPACE;

  // Ищем папку
  if(res = pf_opendir(&dir, path)) return res;

  // Добавляем в папку описатель
  if(res = allocEntryFat32(&dir, new_name, buf512, 0)) return res;

  // Сохраняем описатель на диск
  if(disk_write(buf512, dir.sect)) return FR_DISK_ERR;
  return FR_OK;
}

/*-----------------------------------------------------------------------*/
/* Create Directory                                                      */
/*-----------------------------------------------------------------------*/

FRESULT pf_createDir(const char *path, const char* new_name, BYTE* buf512) {
  DIR dir;
  FRESULT res;
  BYTE* entry;
  DWORD cluster;

  if(FatFs->fs_type != FS_FAT32) return FR_NO_FREE_SPACE;

  // Ищем папку
  if(res = pf_opendir(&dir, path)) return res;                     

  // Выделяем кластер
  if(res = allocClusterFat32(&cluster, buf512)) return res;             

  // Добавляем в папку описатель
  if(res = allocEntryFat32(&dir, new_name, buf512, &entry)) return res; 

  // Заполняем описатель
  entry[DIR_Attr] = AM_DIR;
  *(unsigned short*)(entry+DIR_FstClusHI) = cluster>>16;
  *(unsigned short*)(entry+DIR_FstClusLO) = (unsigned short)cluster;

  // Сохраняем описатель на диск
  if(disk_write(buf512, dir.sect)) return FR_DISK_ERR;

  // Создаем пустую папку
  memset(buf512, 0, 512);
  memset(buf512, '', 11);
  buf512[0] = '.';
  buf512[DIR_Attr] = 0x10;
  *(unsigned short*)(buf512+DIR_FstClusHI) = cluster>>16;
  *(unsigned short*)(buf512+DIR_FstClusLO) = (unsigned short)cluster;
  memset(buf512+32, '', 11);
  buf512[32+0] = '.';
  buf512[32+1] = '.';
  buf512[32+DIR_Attr] = 0x10;
  if(dir.sclust>2) {
    *(unsigned short*)(buf512+32+DIR_FstClusHI) = dir.sclust>>16;
    *(unsigned short*)(buf512+32+DIR_FstClusLO) = (unsigned short)dir.sclust;
  } 

  // Сохраняем папку
  if(disk_write(buf512, clust2sect(cluster))) return FR_DISK_ERR;

  return FR_OK;
}

/*-----------------------------------------------------------------------*/
/* Delete file or folder                                                 */
/*-----------------------------------------------------------------------*/

FRESULT pf_delete(const char *path, BYTE* buf512) {
	DIR dir;
	FRESULT res;
  long cluster;

  if(FatFs->fs_type != FS_FAT32) return FR_NO_FREE_SPACE;

  // Ищем файл
  dir.fn = buf512 + 32;
  if(res = follow_path(&dir, buf512, path)) return res;
  
  // Это корневая папка
  if(buf512[0] == 0) return FR_NO_PATH;

  // Первый кластер файла или папки
  cluster = LD_CLUST(buf512);

  // В папке не должно быть файлов
  if(buf512[DIR_Attr] & AM_DIR) {
    if(res = isEmptyFolder(cluster, buf512)) return res;
  }

  // Удаляем описатель
  if(disk_readp(buf512, dir.sect, 0, 512)) return FR_DISK_ERR;
  buf512[(dir.index % 16) * 32] = 0xE5;
  if(disk_write(buf512, dir.sect)) return FR_DISK_ERR;    

  // Освобождаем цепочку кластеров
  return freeChain(cluster, buf512);
}

/*-----------------------------------------------------------------------*/
/* Change file length                                                    */
/*-----------------------------------------------------------------------*/

FRESULT pf_setSize(const char *path, unsigned long fileSize, BYTE* buf512) {
  DIR dir;
  FRESULT res;
  BYTE* entry;
  unsigned long cluster, needClusters, firstCluster, prevCluster;

  if(FatFs->fs_type != FS_FAT32) return FR_NO_FREE_SPACE;

  // Ищем файл
  dir.fn = buf512 + 32;
  res = follow_path(&dir, buf512, path);
  if(res != FR_OK) return res;
  if(buf512[0]==0 || (buf512[DIR_Attr]&AM_DIR)!=0) return FR_NO_PATH; // Это корневая папка или папка
 
  // Первый кластер файла 
  cluster = LD_CLUST(buf512);
  if(cluster < 2 || cluster >= FatFs->n_fatent) cluster=0; // Коррекция ошибки
  firstCluster = cluster;

  // Расчет кол-ва кластеров
  needClusters = bytesToClusters(fileSize);

  // Пропуск кластеров
  prevCluster = 0;
  while(cluster && needClusters) {
    needClusters--;
    prevCluster = cluster;
    cluster = get_fat(cluster);
    if(cluster < 2 || cluster >= FatFs->n_fatent) cluster=0;
  }

  if(needClusters) {
    // Добавление кластеров
    for(;needClusters; --needClusters) {
      res = allocClusterFat32(&cluster, buf512);
      if(res != FR_OK) return res;
      if(prevCluster) setNextClusterFat32(prevCluster, cluster, buf512);
                 else firstCluster = cluster;
      prevCluster = cluster;
    }
  } else
  if(cluster) {
    // Удаление кластеров
    if(prevCluster) setNextClusterFat32(prevCluster, 0x0FFFFFFF, buf512);
               else firstCluster = 0;
    freeChain(cluster, buf512);
  }

  // Корректируем описание файла

  if(disk_readp(buf512, dir.sect, 0, 512)) return FR_DISK_ERR;

  entry = buf512 + (dir.index % 16) * 32;
  *(unsigned long *)(entry+DIR_FileSize ) = fileSize;
  *(unsigned short*)(entry+DIR_FstClusHI) = firstCluster>>16;
  *(unsigned short*)(entry+DIR_FstClusLO) = (unsigned short)firstCluster;

  if(disk_write(buf512, dir.sect)) return FR_DISK_ERR;

  return FR_OK;
}


Работа со структурами в самой библиотеке сделана так *(unsigned short*)(entry+DIR_FstClusLO), поэтому я сделал так же.

Viewing all articles
Browse latest Browse all 319

Latest Images

Trending Articles