00001
00038 #include <dma.h>
00039 #include <util.h>
00040 #include <assert.h>
00041 #include <malloc.h>
00042 #include <stdint.h>
00043 #include <string.h>
00044 #include <byteorder.h>
00045 #include <status_codes.h>
00046
00047 #include <board/physmem.h>
00048
00049 #include <fs/tsfs.h>
00050
00051 #ifdef CONFIG_FS_TSFS_USE_HUGEMEM
00052 # include <hugemem.h>
00053 #endif
00054
00060 #ifdef CONFIG_FS_TSFS_USE_HUGEMEM
00061 static void tsfs_read_filetable_page_done(struct tsfs *tsfs);
00062 #endif
00063
00067 static void tsfs_buf_list_done(struct block_device *bdev,
00068 struct block_request *breq, struct slist *buf_list)
00069 {
00070
00071 }
00072
00076 static void tsfs_read_page_done(struct block_device *bdev,
00077 struct block_request *breq)
00078 {
00079 struct tsfs *tsfs = breq->context;
00080
00081 block_free_request(bdev, breq);
00082 tsfs->current_breq = NULL;
00083
00084 tsfs->page_read_callback(tsfs);
00085 }
00086
00087
00088 static void tsfs_read_page(struct tsfs *tsfs, block_addr_t lba,
00089 void (*callback_func)(struct tsfs *tsfs))
00090 {
00091 struct block_device *bdev = tsfs->bdev;
00092
00093
00094 assert(!tsfs->current_breq);
00095
00096 tsfs->page_read_callback = callback_func;
00097 tsfs->current_breq = block_alloc_request(bdev);
00098 tsfs->lba_in_buf = lba;
00099
00100
00101 block_prepare_req(bdev, tsfs->current_breq, lba, 1, BLK_OP_READ);
00102
00103 tsfs->current_breq->req_done = tsfs_read_page_done;
00104 tsfs->current_breq->buf_list_done = tsfs_buf_list_done;
00105 tsfs->current_breq->context = tsfs;
00106
00107 buffer_init_rx(&tsfs->buffer, tsfs->buffer_data, TSFS_BLOCKSIZE);
00108 blk_req_add_buffer(tsfs->current_breq, &tsfs->buffer);
00109
00110 block_submit_req(bdev, tsfs->current_breq);
00111 }
00112
00113 static void tsfs_parse_filetable_from_buffer(struct tsfs *tsfs,
00114 uint_fast8_t offset_filetable, uint_fast8_t offset_block,
00115 uint_fast8_t nr_entries)
00116 {
00117 struct tsfs_filetable_entry ft_entry;
00118 uint_fast8_t size;
00119 uint_fast8_t i;
00120
00121 size = sizeof(struct tsfs_filetable_entry);
00122
00123 for (i = 0; i < nr_entries; i++) {
00124 #ifdef CONFIG_FS_TSFS_USE_HUGEMEM
00125 uint16_t mem_table_offset;
00126 #endif
00127 uint_fast16_t mem_block_offset = (offset_block + i) * size;
00128
00129 memcpy(&ft_entry, tsfs->buffer_data + mem_block_offset, size);
00130
00131 ft_entry.file_offset = be32_to_cpu(ft_entry.file_offset);
00132 ft_entry.file_size = be32_to_cpu(ft_entry.file_size);
00133
00134 #ifdef CONFIG_FS_TSFS_USE_HUGEMEM
00135 mem_table_offset = (offset_filetable + i) * size;
00136 hugemem_write_block((hugemem_ptr_t)((phys_addr_t)
00137 tsfs->filetable_address
00138 + mem_table_offset), &ft_entry, size);
00139 #else
00140 memcpy(&tsfs->filetable[offset_filetable + i], &ft_entry, size);
00141 #endif
00142 }
00143
00144 #ifdef CONFIG_FS_TSFS_USE_HUGEMEM
00145 tsfs->filetable_entries_read += nr_entries;
00146
00147 if (tsfs->filetable_entries_read < tsfs->header.nr_files) {
00148
00149 block_addr_t next_lba = tsfs->lba_in_buf + 1;
00150 tsfs_read_page(tsfs, next_lba, tsfs_read_filetable_page_done);
00151 } else {
00152 #endif
00153
00154
00155
00156 tsfs->status = STATUS_OK;
00157
00158 if (tsfs->current_read_request.task) {
00159 workqueue_add_task(&main_workqueue,
00160 tsfs->current_read_request.task);
00161 }
00162 #ifdef CONFIG_FS_TSFS_USE_HUGEMEM
00163 }
00164 #endif
00165 }
00166
00167 #ifdef CONFIG_FS_TSFS_USE_HUGEMEM
00168 static void tsfs_read_filetable_page_done(struct tsfs *tsfs)
00169 {
00170 uint_fast8_t offset_filetable = tsfs->filetable_entries_read;
00171
00172 tsfs_parse_filetable_from_buffer(tsfs, offset_filetable, 0,
00173 TSFS_FILETABLE_ENTRIES_PER_BLOCK);
00174 }
00175 #endif
00176
00177 static void tsfs_read_header_done(struct tsfs *tsfs)
00178 {
00179 memcpy(&tsfs->header, tsfs->buffer_data, sizeof(tsfs->header));
00180
00181 tsfs->header.id = be16_to_cpu(tsfs->header.id);
00182 tsfs->header.volume_size = be32_to_cpu(tsfs->header.volume_size);
00183 tsfs->header.nr_files = be32_to_cpu(tsfs->header.nr_files);
00184
00185 if (tsfs->header.nr_files > TSFS_MAX_FILES)
00186 tsfs->header.nr_files = TSFS_MAX_FILES;
00187
00188 if (tsfs->header.id == TSFS_ID) {
00189 uint8_t entries_to_read = TSFS_FILETABLE_ENTRIES_PER_BLOCK;
00190
00191
00192 entries_to_read -= 1;
00193
00194 uint8_t nr_to_read = min_u(tsfs->header.nr_files, entries_to_read );
00195
00196 tsfs_parse_filetable_from_buffer(tsfs, 0, 1, nr_to_read);
00197 } else {
00198 tsfs->status = ERR_BAD_FORMAT;
00199
00200 if (tsfs->current_read_request.task) {
00201 workqueue_add_task(&main_workqueue,
00202 tsfs->current_read_request.task);
00203 }
00204 }
00205 }
00206
00207 static void read_header(struct tsfs *tsfs)
00208 {
00209 tsfs_read_page(tsfs, 0, tsfs_read_header_done);
00210 }
00211
00232 status_t tsfs_init(struct tsfs *tsfs, struct block_device *bdev,
00233 struct workqueue_task *init_done_task)
00234 {
00235 tsfs->buffer_data = malloc(TSFS_BLOCKSIZE);
00236 if (!tsfs->buffer_data)
00237 return ERR_NO_MEMORY;
00238
00239 tsfs->bdev = bdev;
00240 tsfs->status = ERR_BUSY;
00241
00242 tsfs->current_read_request.task = init_done_task;
00243 #ifdef CONFIG_FS_TSFS_USE_HUGEMEM
00244 tsfs->filetable_address =
00245 (hugemem_ptr_t)physmem_alloc(&board_extram_pool,
00246 sizeof(struct tsfs_filetable_entry) *
00247 TSFS_MAX_FILES, CPU_DMA_ALIGN);
00248
00249 assert(tsfs->filetable_address != PHYSMEM_ALLOC_ERR);
00250 #endif
00251 read_header(tsfs);
00252
00253 return STATUS_OK;
00254 }
00255
00256 static void tsfs_read_buffer_ready(struct tsfs *tsfs)
00257 {
00258 uint16_t copy_start;
00259 uint16_t copy_len;
00260 void *buffer;
00261
00262
00263 copy_start = tsfs->current_read_request.cursor % TSFS_BLOCKSIZE;
00264 copy_len = min_u(TSFS_BLOCKSIZE - copy_start,
00265 tsfs->current_read_request.remaining_bytes);
00266
00267 buffer = tsfs->buffer_data + copy_start;
00268
00269 memcpy(tsfs->current_read_request.buffer, buffer, copy_len);
00270
00271
00272 tsfs->current_read_request.buffer=
00273 (uint8_t *)tsfs->current_read_request.buffer+ copy_len;
00274 tsfs->current_read_request.cursor += copy_len;
00275 tsfs->current_read_request.remaining_bytes -= copy_len;
00276
00277 if (tsfs->current_read_request.remaining_bytes > 0) {
00278
00279 block_addr_t next_lba =
00280 tsfs->current_read_request.cursor
00281 >> ilog2(TSFS_BLOCKSIZE);
00282 tsfs_read_page(tsfs, next_lba, tsfs_read_buffer_ready);
00283 } else {
00284
00285
00286
00287 tsfs->status = STATUS_OK;
00288 workqueue_add_task(&main_workqueue,
00289 tsfs->current_read_request.task);
00290 }
00291 }
00292
00317 status_t tsfs_read(struct tsfs *tsfs, struct tsfs_file *file,
00318 void *buffer, uint32_t length, struct workqueue_task *task)
00319 {
00320 block_addr_t block_nr;
00321
00322 if (tsfs->status != STATUS_OK)
00323 return ERR_BUSY;
00324
00325
00326 if (file->cursor + length > file->end)
00327 length = file->end - file->cursor;
00328 if (!length)
00329 return ERR_INVALID_ARG;
00330
00331 tsfs->status = ERR_BUSY;
00332
00333 tsfs->current_read_request.task = task;
00334 tsfs->current_read_request.buffer = buffer;
00335 tsfs->current_read_request.cursor = file->cursor;
00336 tsfs->current_read_request.remaining_bytes = length;
00337
00338
00339 block_nr = file->cursor >> ilog2(TSFS_BLOCKSIZE);
00340
00341 if (tsfs->lba_in_buf == block_nr) {
00342
00343 tsfs_read_buffer_ready(tsfs);
00344 } else {
00345
00346 tsfs_read_page(tsfs, block_nr, tsfs_read_buffer_ready);
00347 }
00348
00349
00350 file->cursor += length;
00351
00352 return STATUS_OK;
00353 }
00354
00355 static void tsfs_get_filetable_entry(struct tsfs *tsfs, uint8_t file_index,
00356 struct tsfs_filetable_entry *ft_entry)
00357 {
00358 uint8_t size = sizeof(struct tsfs_filetable_entry);
00359
00360 #ifdef CONFIG_FS_TSFS_USE_HUGEMEM
00361 hugemem_read_block(ft_entry,
00362 (hugemem_ptr_t)((phys_addr_t)tsfs->filetable_address
00363 + (size * file_index)), size);
00364 #else
00365 memcpy(ft_entry, &tsfs->filetable[file_index], size);
00366 #endif
00367 }
00368
00369 static uint32_t tsfs_locate_file_in_table(struct tsfs *tsfs,
00370 const char *filename)
00371 {
00372 struct tsfs_filetable_entry ft_entry;
00373 uint32_t file_index = 0;
00374 char name_buffer[TSFS_FILENAME_LEN];
00375
00376 while (file_index < tsfs->header.nr_files) {
00377 tsfs_get_filetable_entry(tsfs, file_index, &ft_entry);
00378
00379 memcpy(name_buffer, ft_entry.filename, TSFS_FILENAME_LEN);
00380
00381 if (strncmp(name_buffer, filename, TSFS_FILENAME_LEN) == 0)
00382 return file_index;
00383
00384 file_index++;
00385 }
00386
00387 return file_index;
00388 }
00389
00404 status_t tsfs_open(struct tsfs *tsfs, const char *filename,
00405 struct tsfs_file *filehandle)
00406 {
00407 struct tsfs_filetable_entry entry;
00408 uint32_t file_index;
00409
00410 file_index = tsfs_locate_file_in_table(tsfs, filename);
00411
00412
00413
00414
00415 if (file_index >= tsfs->header.nr_files)
00416 return ERR_INVALID_ARG;
00417
00418 tsfs_get_filetable_entry(tsfs, file_index, &entry);
00419
00420 filehandle->start = entry.file_offset;
00421 filehandle->cursor = entry.file_offset;
00422 filehandle->end = entry.file_offset + entry.file_size;
00423
00424 return STATUS_OK;
00425 }
00426
00442 status_t tsfs_seek(struct tsfs_file* file, int32_t offset,
00443 enum tsfs_seek_origin origin)
00444 {
00445 switch(origin) {
00446 case SEEK_SET:
00447 file->cursor = file->start + offset;
00448 break;
00449
00450 case SEEK_CUR:
00451 file->cursor += offset;
00452 break;
00453
00454 case SEEK_END:
00455 file->cursor = file->end + offset;
00456 break;
00457
00458 default:
00459 unhandled_case(origin);
00460 break;
00461 }
00462
00463 return STATUS_OK;
00464 }
00465
00475 void tsfs_get_filename(struct tsfs *tsfs, uint_fast8_t file_index,
00476 uint8_t *buffer)
00477 {
00478 struct tsfs_filetable_entry entry;
00479 tsfs_get_filetable_entry(tsfs, file_index, &entry);
00480 memcpy(buffer, entry.filename, TSFS_FILENAME_LEN);
00481 }
00482