summaryrefslogtreecommitdiffstats
path: root/firmware/include/dircache.h
blob: 2cf838e5390c62854e820fa4f82f8a41f9feb143 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2005 by Miika Pekkarinen
 * Copyright (C) 2014 by Michael Sevakis
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#ifndef _DIRCACHE_H
#define _DIRCACHE_H

#include "mv.h"
#include <string.h>    /* size_t */
#include <sys/types.h> /* ssize_t */

#ifdef HAVE_DIRCACHE

/****************************************************************************
 ** Configurable values
 **/

#if 0
/* enable dumping code */
#define DIRCACHE_DUMPSTER
#define DIRCACHE_DUMPSTER_BIN   "/dircache_dump.bin"
#define DIRCACHE_DUMPSTER_CSV   "/dircache_dump.csv"
#endif

/* dircache builds won't search below this but will work down to this point
   while below it the cache will just pass requests through to the storage;
   the limiting factor is the scanning thread stack size, not the
   implementation -- tune the two together */
#define DIRCACHE_MAX_DEPTH  15
#define DIRCACHE_STACK_SIZE (DEFAULT_STACK_SIZE + 0x100)

/* memory buffer constants that control allocation */
#define DIRCACHE_RESERVE (1024*64)     /* 64 KB - new entry slack */
#define DIRCACHE_MIN     (1024*1024*1) /* 1 MB - provision min size */
#define DIRCACHE_LIMIT   (1024*1024*6) /* 6 MB - provision max size */

/* make it easy to change serialnumber size without modifying anything else;
   32 bits allows 21845 builds before wrapping in a 6MB cache that is filled
   exclusively with entries and nothing else (32 byte entries), making that
   figure pessimistic */
typedef uint32_t dc_serial_t;

/* these should agree with size of dc_serial_t */
#define DC_SERHASH_START        0xffffffff

/* I was originally using FNV hash but decided this is probably okay
   (for now) */
#define dc_hash_serialnum(s, h) \
    ({ dc_serial_t __x = (s); crc_32(&(__x), sizeof(dc_serial_t), (h)); })
#define DC_SERIAL_FMT           "0x%08lX"

/**
 ****************************************************************************/

#define IF_DIRCACHE(...) __VA_ARGS__
#define IFN_DIRCACHE(...)

#if CONFIG_PLATFORM & PLATFORM_NATIVE
/* native dircache is lower-level than on a hosted target */
#define DIRCACHE_NATIVE
#endif

struct dircache_file
{
    int         idx;        /* this file's cache index */
    dc_serial_t serialnum;  /* this file's serial number */
};

enum dircache_status
{
    DIRCACHE_IDLE     = 0,  /* no volume is initialized */
    DIRCACHE_SCANNING = 1,  /* dircache is scanning a volume */
    DIRCACHE_READY    = 2,  /* dircache is ready to be used */
};

/** Dircache control **/
void dircache_wait(void);
void dircache_suspend(void);
int dircache_resume(void);
int dircache_enable(void);
void dircache_disable(void);
void dircache_free_buffer(void);

/** Volume mounting **/
void dircache_mount(void); /* always tries building everything it can */
void dircache_unmount(IF_MV_NONVOID(int volume));


/** File API service functions **/

/* avoid forcing #include of file_internal.h, fat.h and dir.h */
struct filestr_base;
struct file_base_info;
struct file_base_binding;
struct dirent;
struct dirscan_info;
struct dirinfo_native;

int dircache_readdir_dirent(struct filestr_base *stream,
                            struct dirscan_info *scanp,
                            struct dirent *entry);
void dircache_rewinddir_dirent(struct dirscan_info *scanp);

#ifdef DIRCACHE_NATIVE
struct fat_direntry;
int dircache_readdir_internal(struct filestr_base *stream,
                              struct file_base_info *infop,
                              struct fat_direntry *fatent);
void dircache_rewinddir_internal(struct file_base_info *info);
#endif /* DIRCACHE_NATIVE */


/** Dircache live updating **/

void dircache_get_rootinfo(struct file_base_info *infop);
void dircache_bind_file(struct file_base_binding *bindp);
void dircache_unbind_file(struct file_base_binding *bindp);
void dircache_fileop_create(struct file_base_info *dirinfop,
                            struct file_base_binding *bindp,
                            const char *basename,
                            const struct dirinfo_native *dinp);
void dircache_fileop_rename(struct file_base_info *dirinfop,
                            struct file_base_binding *bindp,
                            const char *basename);
void dircache_fileop_remove(struct file_base_binding *bindp);
void dircache_fileop_sync(struct file_base_binding *infop,
                          const struct dirinfo_native *dinp);


/** Dircache paths, files and shortcuts **/
struct dircache_fileref
{
    struct dircache_file dcfile;
    dc_serial_t          serialhash; /* Hash of serialnumbers to root */
};

void dircache_fileref_init(struct dircache_fileref *dcfrefp);
ssize_t dircache_get_fileref_path(const struct dircache_fileref *dcfrefp,
                                  char *buf, size_t size);

/* Bitflags for dircache_search() */
enum dircache_search_flags
{
    DCS_FILEREF        = 0x01, /* Check fileref existence and serial number */
    _DCS_VERIFY_FLAG   = 0x02, /* Internal: Only valid with DCS_FILEREF */
    DCS_FILEREF_VERIFY = 0x03, /* Do DCS_FILEREF check + verify serial hash */
    DCS_CACHED_PATH    = 0x04, /* Check only cache for provided path */
    _DCS_STORAGE_FLAG  = 0x08, /* Internal: Only valid with DCS_CACHED_PATH */
    DCS_STORAGE_PATH   = 0x0c, /* Read-through if needed for provided path */
    DCS_UPDATE_FILEREF = 0x10, /* If fileref is not valid but path is found or
                                  searching a path, update the reference
                                  information */
};

int dircache_search(unsigned int flags, struct dircache_fileref *dcfrefp,
                    const char *path);

int dircache_fileref_cmp(const struct dircache_fileref *dcfrefp1,
                         const struct dircache_fileref *dcfrefp2);


/** Debug screen/info stuff **/

struct dircache_info
{
    enum dircache_status status; /* current composite status value */
    const char   *statusdesc;    /* pointer to string describing 'status' */
    size_t       last_size;      /* cache size after last build */
    size_t       size;           /* total size of entries (with holes) */
    size_t       sizeused;       /* bytes of 'size' actually utilized */
    size_t       size_limit;     /* maximum possible size */
    size_t       reserve;        /* size of reserve area */
    size_t       reserve_used;   /* amount of reserve used */
    unsigned int entry_count;    /* number of cache entries */
    long         build_ticks;    /* total time used to build cache */
};

void dircache_get_info(struct dircache_info *info);
#ifdef DIRCACHE_DUMPSTER
void dircache_dump(void);
#endif /* DIRCACHE_DUMPSTER */


/** Misc. stuff **/
void dircache_dcfile_init(struct dircache_file *dcfilep);

#ifdef HAVE_EEPROM_SETTINGS
int dircache_load(void);
int dircache_save(void);
#endif /* HAVE_EEPROM_SETTINGS */

void dircache_init(size_t last_size) INIT_ATTR;

#else /* !HAVE_DIRCACHE */

#define IF_DIRCACHE(...)
#define IFN_DIRCACHE(...) __VA_ARGS__

#endif /* HAVE_DIRCACHE */

#endif /* _DIRCACHE_H */