sim: Break out sim file reading into simfs
authorDenis Kenzior <denkenz@gmail.com>
Mon, 6 Sep 2010 02:40:07 +0000 (21:40 -0500)
committerDenis Kenzior <denkenz@gmail.com>
Thu, 9 Sep 2010 13:50:28 +0000 (08:50 -0500)
Makefile.am
include/sim.h
src/sim.c
src/simfs.c [new file with mode: 0644]
src/simfs.h [new file with mode: 0644]

index f31180e..e59d584 100644 (file)
@@ -282,7 +282,8 @@ src_ofonod_SOURCES = $(gdbus_sources) $(builtin_sources) \
                        src/storage.c src/cbs.c src/watch.c src/call-volume.c \
                        src/gprs.c src/idmap.h src/idmap.c \
                        src/radio-settings.c src/stkutil.h src/stkutil.c \
-                       src/nettime.c src/stkagent.c src/stkagent.h
+                       src/nettime.c src/stkagent.c src/stkagent.h \
+                       src/simfs.c src/simfs.h
 
 src_ofonod_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ @CAPNG_LIBS@ -ldl
 
index b4e8541..42b19bd 100644 (file)
@@ -204,7 +204,7 @@ int ofono_sim_write(struct ofono_sim *sim, int id,
                        const unsigned char *data, int length, void *userdata);
 
 int ofono_sim_read_bytes(struct ofono_sim *sim, int id,
-                       unsigned short offset, int num_bytes,
+                       unsigned short offset, unsigned short num_bytes,
                        ofono_sim_file_read_cb_t cb, void *data);
 #ifdef __cplusplus
 }
index 58f3f64..094ce06 100644 (file)
--- a/src/sim.c
+++ b/src/sim.c
 #include "smsutil.h"
 #include "simutil.h"
 #include "storage.h"
-
-#define SIM_CACHE_MODE 0600
-#define SIM_CACHE_PATH STORAGEDIR "/%s-%i/%04x"
-#define SIM_CACHE_PATH_LEN(imsilen) (strlen(SIM_CACHE_PATH) - 3 + imsilen)
-#define SIM_CACHE_HEADER_SIZE 38
-#define SIM_FILE_INFO_SIZE 6
+#include "simfs.h"
 
 static GSList *g_drivers = NULL;
 
-static gboolean sim_op_next(gpointer user_data);
-static gboolean sim_op_retrieve_next(gpointer user);
 static void sim_own_numbers_update(struct ofono_sim *sim);
 static void sim_pin_check(struct ofono_sim *sim);
 static void sim_set_ready(struct ofono_sim *sim);
-static gboolean sim_op_read_block(gpointer user_data);
-
-struct sim_file_op {
-       int id;
-       gboolean cache;
-       enum ofono_sim_file_structure structure;
-       unsigned short offset;
-       int num_bytes;
-       int length;
-       int record_length;
-       int current;
-       gconstpointer cb;
-       gboolean is_read;
-       void *buffer;
-       void *userdata;
-};
 
 struct ofono_sim {
        /* Contents of the SIM file system, in rough initialization order */
@@ -111,8 +88,7 @@ struct ofono_sim {
        enum ofono_sim_state state;
        struct ofono_watchlist *state_watches;
 
-       GQueue *simop_q;
-       gint simop_source;
+       struct sim_fs *simfs;
 
        DBusMessage *pending;
        const struct ofono_sim_driver *driver;
@@ -289,11 +265,6 @@ static char **get_service_numbers(GSList *service_numbers)
        return ret;
 }
 
-static void sim_file_op_free(struct sim_file_op *node)
-{
-       g_free(node);
-}
-
 static void service_number_free(struct service_number *num)
 {
        g_free(num->id);
@@ -1542,633 +1513,17 @@ static void sim_initialize(struct ofono_sim *sim)
                        sim_efpl_read_cb, sim);
 }
 
-static void sim_op_error(struct ofono_sim *sim)
-{
-       struct sim_file_op *op = g_queue_pop_head(sim->simop_q);
-
-       if (g_queue_get_length(sim->simop_q) > 0)
-               sim->simop_source = g_timeout_add(0, sim_op_next, sim);
-
-       if (op->is_read == TRUE)
-               ((ofono_sim_file_read_cb_t) op->cb)
-                       (0, 0, 0, 0, 0, op->userdata);
-       else
-               ((ofono_sim_file_write_cb_t) op->cb)
-                       (0, op->userdata);
-
-       sim_file_op_free(op);
-}
-
-static gboolean cache_record(const char *path, int current, int record_len,
-                               const unsigned char *data, int num_bytes,
-                               enum ofono_sim_file_structure type)
-{
-       int r = 0;
-       int fd;
-
-       fd = TFR(open(path, O_RDWR));
-
-       if (fd == -1)
-               return FALSE;
-
-       if (lseek(fd, (current - 1) * record_len +
-                               SIM_CACHE_HEADER_SIZE, SEEK_SET) != (off_t) -1)
-               r = TFR(write(fd, data, num_bytes));
-
-       /* update present bit for this block if we are a transparent file */
-       if (type == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT) {
-               /* figure out which byte the right bit location is */
-               int byte = (current - 1) / 8;
-               int bit = (current - 1) % 8;
-               guint8 cache;
-
-               /* lseek to correct byte (skip file info) */
-               lseek(fd, byte + SIM_FILE_INFO_SIZE, SEEK_SET);
-
-               /* read byte */
-               TFR(read(fd, &cache, 1));
-
-               /* set present bit */
-               cache |= (1 << bit);
-
-               /* write back */
-               lseek(fd, byte + SIM_FILE_INFO_SIZE, SEEK_SET);
-               TFR(write(fd, &cache, 1));
-       }
-
-       TFR(close(fd));
-
-       if (r < num_bytes) {
-               unlink(path);
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-static void sim_op_retrieve_cb(const struct ofono_error *error,
-                               const unsigned char *data, int len, void *user)
-{
-       struct ofono_sim *sim = user;
-       struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
-       int total = op->length / op->record_length;
-       ofono_sim_file_read_cb_t cb = op->cb;
-       char *imsi = sim->imsi;
-
-       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
-               sim_op_error(sim);
-               return;
-       }
-
-       cb(1, op->length, op->current, data, op->record_length, op->userdata);
-
-       if (op->cache && imsi) {
-               char *path = g_strdup_printf(SIM_CACHE_PATH,
-                                               imsi, sim->phase, op->id);
-
-               op->cache = cache_record(path, op->current, op->record_length,
-                                               data, op->record_length,
-                                               op->structure);
-               g_free(path);
-       }
-
-       if (op->current == total) {
-               op = g_queue_pop_head(sim->simop_q);
-
-               sim_file_op_free(op);
-
-               if (g_queue_get_length(sim->simop_q) > 0)
-                       sim->simop_source = g_timeout_add(0, sim_op_next, sim);
-       } else {
-               op->current += 1;
-               sim->simop_source = g_timeout_add(0, sim_op_retrieve_next, sim);
-       }
-}
-
-static gboolean sim_op_retrieve_next(gpointer user)
-{
-       struct ofono_sim *sim = user;
-       struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
-
-       sim->simop_source = 0;
-
-       switch (op->structure) {
-       case OFONO_SIM_FILE_STRUCTURE_TRANSPARENT:
-               if (!sim->driver->read_file_transparent) {
-                       sim_op_error(sim);
-                       return FALSE;
-               }
-
-               sim->driver->read_file_transparent(sim, op->id, 0, op->length,
-                                               sim_op_retrieve_cb, sim);
-               break;
-       case OFONO_SIM_FILE_STRUCTURE_FIXED:
-               if (!sim->driver->read_file_linear) {
-                       sim_op_error(sim);
-                       return FALSE;
-               }
-
-               sim->driver->read_file_linear(sim, op->id, op->current,
-                                               op->record_length,
-                                               sim_op_retrieve_cb, sim);
-               break;
-       case OFONO_SIM_FILE_STRUCTURE_CYCLIC:
-               if (!sim->driver->read_file_cyclic) {
-                       sim_op_error(sim);
-                       return FALSE;
-               }
-
-               sim->driver->read_file_cyclic(sim, op->id, op->current,
-                                               op->record_length,
-                                               sim_op_retrieve_cb, sim);
-               break;
-       default:
-               ofono_error("Unrecognized file structure, this can't happen");
-       }
-
-       return FALSE;
-}
-
-static void sim_op_info_cb(const struct ofono_error *error, int length,
-                               enum ofono_sim_file_structure structure,
-                               int record_length,
-                               const unsigned char access[3], void *data)
-{
-       struct ofono_sim *sim = data;
-       struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
-       char *imsi = sim->imsi;
-       enum sim_file_access update;
-       enum sim_file_access invalidate;
-       enum sim_file_access rehabilitate;
-
-       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
-               sim_op_error(sim);
-               return;
-       }
-
-       if (structure != op->structure) {
-               ofono_error("Requested file structure differs from SIM: %x",
-                               op->id);
-               sim_op_error(sim);
-               return;
-       }
-
-       /* TS 11.11, Section 9.3 */
-       update = file_access_condition_decode(access[0] & 0xf);
-       rehabilitate = file_access_condition_decode((access[2] >> 4) & 0xf);
-       invalidate = file_access_condition_decode(access[2] & 0xf);
-
-       op->structure = structure;
-       op->length = length;
-       /* Never cache card holder writable files */
-       op->cache = (update == SIM_FILE_ACCESS_ADM ||
-                       update == SIM_FILE_ACCESS_NEVER) &&
-                       (invalidate == SIM_FILE_ACCESS_ADM ||
-                               invalidate == SIM_FILE_ACCESS_NEVER) &&
-                       (rehabilitate == SIM_FILE_ACCESS_ADM ||
-                               rehabilitate == SIM_FILE_ACCESS_NEVER);
-
-       if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT)
-               op->record_length = length;
-       else
-               op->record_length = record_length;
-
-       if (op->num_bytes > 0)
-               sim->simop_source = g_timeout_add(0, sim_op_read_block, sim);
-       else {
-               op->current = 1;
-               sim->simop_source = g_timeout_add(0, sim_op_retrieve_next, sim);
-       }
-
-       if (op->cache && imsi) {
-               unsigned char fileinfo[SIM_CACHE_HEADER_SIZE];
-
-               memset(fileinfo, 0, SIM_CACHE_HEADER_SIZE);
-
-               fileinfo[0] = error->type;
-               fileinfo[1] = length >> 8;
-               fileinfo[2] = length & 0xff;
-               fileinfo[3] = structure;
-               fileinfo[4] = record_length >> 8;
-               fileinfo[5] = record_length & 0xff;
-
-               if (write_file(fileinfo, SIM_CACHE_HEADER_SIZE, SIM_CACHE_MODE,
-                                       SIM_CACHE_PATH, imsi, sim->phase,
-                                       op->id) != SIM_CACHE_HEADER_SIZE)
-                       op->cache = FALSE;
-       }
-}
-
-static void sim_op_write_cb(const struct ofono_error *error, void *data)
-{
-       struct ofono_sim *sim = data;
-       struct sim_file_op *op = g_queue_pop_head(sim->simop_q);
-       ofono_sim_file_write_cb_t cb = op->cb;
-
-       if (g_queue_get_length(sim->simop_q) > 0)
-               sim->simop_source = g_timeout_add(0, sim_op_next, sim);
-
-       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
-               cb(1, op->userdata);
-       else
-               cb(0, op->userdata);
-
-       sim_file_op_free(op);
-}
-
-static gboolean sim_op_check_cached(struct ofono_sim *sim)
-{
-       char *imsi = sim->imsi;
-       struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
-       ofono_sim_file_read_cb_t cb = op->cb;
-       char *path;
-       int fd;
-       unsigned char fileinfo[SIM_CACHE_HEADER_SIZE];
-       ssize_t len;
-       int error_type;
-       unsigned int file_length;
-       enum ofono_sim_file_structure structure;
-       unsigned int record_length;
-       unsigned int record;
-       guint8 *buffer = NULL;
-       gboolean ret = FALSE;
-
-       if (!imsi)
-               return FALSE;
-
-       path = g_strdup_printf(SIM_CACHE_PATH, imsi, sim->phase, op->id);
-
-       if (path == NULL)
-               return FALSE;
-
-       fd = TFR(open(path, O_RDONLY));
-       g_free(path);
-
-       if (fd == -1) {
-               if (errno != ENOENT)
-                       DBG("Error %i opening cache file for "
-                                       "fileid %04x, IMSI %s",
-                                       errno, op->id, imsi);
-
-               return FALSE;
-       }
-
-       len = TFR(read(fd, fileinfo, SIM_CACHE_HEADER_SIZE));
-
-       if (len != SIM_CACHE_HEADER_SIZE)
-               goto cleanup;
-
-       error_type = fileinfo[0];
-       file_length = (fileinfo[1] << 8) | fileinfo[2];
-       structure = fileinfo[3];
-       record_length = (fileinfo[4] << 8) | fileinfo[5];
-
-       if (op->num_bytes > 0) {
-               op->length = file_length;
-               ret = TRUE;
-               goto cleanup;
-       }
-
-       if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT)
-               record_length = file_length;
-
-       if (record_length == 0 || file_length < record_length)
-               goto cleanup;
-
-       if (error_type != OFONO_ERROR_TYPE_NO_ERROR ||
-                       structure != op->structure) {
-               ret = TRUE;
-               cb(0, 0, 0, 0, 0, op->userdata);
-               goto cleanup;
-       }
-
-       buffer = g_try_malloc(file_length);
-
-       if (buffer == NULL)
-               goto cleanup;
-
-       len = TFR(read(fd, buffer, file_length));
-
-       if (len < (ssize_t)file_length)
-               goto cleanup;
-
-       for (record = 0; record < file_length / record_length; record++) {
-               cb(1, file_length, record + 1, &buffer[record * record_length],
-                       record_length, op->userdata);
-       }
-
-       ret = TRUE;
-
-cleanup:
-       if (buffer)
-               g_free(buffer);
-
-       TFR(close(fd));
-
-       return ret;
-}
-
-static void sim_op_read_block_cb(const struct ofono_error *error,
-                               const unsigned char *data, int len, void *user)
-{
-       struct ofono_sim *sim = user;
-       struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
-       char *imsi = sim->imsi;
-       int start_block, end_block;
-       int start, length;
-       unsigned char *buf;
-
-       if ((error->type != OFONO_ERROR_TYPE_NO_ERROR) || (len == 0)) {
-               sim_op_error(sim);
-               return;
-       }
-
-       /* buffer this block */
-       start_block = op->offset / 256;
-       end_block = (op->offset + (op->num_bytes - 1)) / 256;
-
-       if (op->current == start_block) {
-               start = op->offset % 256;
-               buf = op->buffer;
-       } else {
-               start = 0;
-               buf = op->buffer + (op->current * 256);
-       }
-
-       length = op->num_bytes % 256;
-
-       if ((length == 0) || (op->current != end_block))
-               length = 256;
-
-       memcpy(buf, &data[start], length);
-
-       /* cache this block */
-       if (op->cache && imsi) {
-               char *path = g_strdup_printf(SIM_CACHE_PATH,
-                                               sim->imsi, sim->phase, op->id);
-               op->cache = cache_record(path, op->current + 1, 256, data,
-                                               length, op->structure);
-
-               g_free(path);
-       }
-
-       op->current++;
-
-       sim->simop_source = g_timeout_add(0, sim_op_read_block, sim);
-}
-
-static gboolean sim_op_check_cached_block(struct ofono_sim *sim)
-{
-       struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
-       char *path;
-       int fd;
-       char *imsi = sim->imsi;
-       int start_block, end_block;
-       int start, length, len;
-       unsigned char *buf;
-       int bit, byte;
-       guint8 cache;
-       gboolean ret;
-
-       if (!imsi)
-               return FALSE;
-
-       path = g_strdup_printf(SIM_CACHE_PATH, sim->imsi, sim->phase, op->id);
-
-       if (path == NULL)
-               return FALSE;
-
-       fd = TFR(open(path, O_RDONLY));
-       g_free(path);
-
-       if (fd == -1) {
-               if (errno != ENOENT)
-                       DBG("Error %i opening cache file for "
-                                       "fileid %04x, IMSI %s",
-                                       errno, op->id, imsi);
-
-               return FALSE;
-       }
-
-       /* read cache header to see if current block is present */
-       byte = op->current / 8;
-       bit = op->current % 8;
-
-       lseek(fd, SIM_FILE_INFO_SIZE + byte, SEEK_SET);
-
-       len = TFR(read(fd, &cache, 1));
-
-       if ((len != 1) || ((cache & (1 << bit)) == FALSE)) {
-               ret = FALSE;
-               goto cleanup;
-       }
-
-       /* figure out where we should start reading from */
-       start_block = op->offset / 256;
-       end_block = (op->offset + (op->num_bytes - 1)) / 256;
-
-       if (op->current == start_block) {
-               start = op->offset % 256;
-               buf = op->buffer;
-       } else {
-               start = 0;
-               buf = op->buffer + (op->current * 256);
-       }
-
-       length = op->num_bytes % 256;
-
-       if ((length == 0) || (op->current != end_block))
-               length = 256;
-
-       /* lseek to the right place in the file */
-       TFR(lseek(fd, start + SIM_CACHE_HEADER_SIZE, SEEK_SET));
-
-       len = TFR(read(fd, buf, length));
-
-       if (len != length) {
-               ret = FALSE;
-               goto cleanup;
-       }
-
-       ret = TRUE;
-
-       op->current++;
-
-       sim->simop_source = g_timeout_add(0, sim_op_read_block, sim);
-
-cleanup:
-       TFR(close(fd));
-
-       return ret;
-}
-
-static gboolean sim_op_read_block(gpointer user_data)
-{
-       struct ofono_sim *sim = user_data;
-       struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
-       int end_block;
-       ofono_sim_file_read_cb_t cb = op->cb;
-       int read_bytes;
-
-       end_block = (op->offset + (op->num_bytes - 1)) / 256;
-
-       if (op->current > end_block) {
-               cb(1, op->num_bytes, op->current, op->buffer,
-                               op->record_length, op->userdata);
-
-               op = g_queue_pop_head(sim->simop_q);
-
-               g_free(op->buffer);
-
-               sim_file_op_free(op);
-
-               if (g_queue_get_length(sim->simop_q) > 0)
-                       sim->simop_source = g_timeout_add(0, sim_op_next, sim);
-
-               return FALSE;
-       }
-
-       /* see if this block is cached */
-       if (sim_op_check_cached_block(sim) == TRUE)
-               return FALSE;
-
-       if (op->length < ((op->current + 1) * 256))
-               read_bytes = op->length % 256;
-       else
-               read_bytes = 256;
-
-       sim->driver->read_file_transparent(sim, op->id,
-                                               op->current * 256,
-                                               read_bytes,
-                                               sim_op_read_block_cb, sim);
-       return FALSE;
-}
-
-static void sim_op_get_blocks(struct ofono_sim *sim)
-{
-       struct sim_file_op *op = g_queue_peek_head(sim->simop_q);
-
-       /* allocate space to buffer the data till we've collected it all */
-       op->buffer = g_try_malloc0(op->num_bytes);
-
-       /* initialize current */
-       op->current = op->offset / 256;
-
-       /* need to get the length of the file */
-       if (sim_op_check_cached(sim) == FALSE)
-               sim->driver->read_file_info(sim, op->id, sim_op_info_cb, sim);
-       else
-               sim->simop_source = g_timeout_add(0, sim_op_read_block, sim);
-}
-
-static gboolean sim_op_next(gpointer user_data)
-{
-       struct ofono_sim *sim = user_data;
-       struct sim_file_op *op;
-
-       sim->simop_source = 0;
-
-       if (!sim->simop_q)
-               return FALSE;
-
-       op = g_queue_peek_head(sim->simop_q);
-
-       if (op->num_bytes > 0) {
-               sim_op_get_blocks(sim);
-               return FALSE;
-       }
-
-       if (op->is_read == TRUE) {
-               if (sim_op_check_cached(sim)) {
-                       op = g_queue_pop_head(sim->simop_q);
-
-                       sim_file_op_free(op);
-
-                       if (g_queue_get_length(sim->simop_q) > 0)
-                               sim->simop_source =
-                                       g_timeout_add(0, sim_op_next, sim);
-
-                       return FALSE;
-               }
-
-               sim->driver->read_file_info(sim, op->id, sim_op_info_cb, sim);
-       } else {
-               switch (op->structure) {
-               case OFONO_SIM_FILE_STRUCTURE_TRANSPARENT:
-                       sim->driver->write_file_transparent(sim, op->id, 0,
-                                       op->length, op->buffer,
-                                       sim_op_write_cb, sim);
-                       break;
-               case OFONO_SIM_FILE_STRUCTURE_FIXED:
-                       sim->driver->write_file_linear(sim, op->id, op->current,
-                                       op->length, op->buffer,
-                                       sim_op_write_cb, sim);
-                       break;
-               case OFONO_SIM_FILE_STRUCTURE_CYCLIC:
-                       sim->driver->write_file_cyclic(sim, op->id,
-                                       op->length, op->buffer,
-                                       sim_op_write_cb, sim);
-                       break;
-               default:
-                       ofono_error("Unrecognized file structure, "
-                                       "this can't happen");
-               }
-
-               g_free(op->buffer);
-       }
-
-       return FALSE;
-}
-
-static int sim_read_common(struct ofono_sim *sim, int id,
-                               enum ofono_sim_file_structure expected_type,
-                               unsigned short offset, int num_bytes,
-                               ofono_sim_file_read_cb_t cb, void *data)
+int ofono_sim_read_bytes(struct ofono_sim *sim, int id,
+                       unsigned short offset, unsigned short num_bytes,
+                       ofono_sim_file_read_cb_t cb, void *data)
 {
-       struct sim_file_op *op;
-
-       if (!cb)
-               return -1;
-
        if (sim == NULL)
                return -1;
 
-       if (!sim->driver)
-               return -1;
-
-       if (!sim->driver->read_file_info)
+       if (num_bytes == 0)
                return -1;
 
-       /* TODO: We must first check the EFust table to see whether
-        * this file can be read at all
-        */
-
-       if (!sim->simop_q)
-               sim->simop_q = g_queue_new();
-
-       op = g_new0(struct sim_file_op, 1);
-
-       op->id = id;
-       op->structure = expected_type;
-       op->cb = cb;
-       op->userdata = data;
-       op->is_read = TRUE;
-       op->offset = offset;
-       op->num_bytes = num_bytes;
-
-       g_queue_push_tail(sim->simop_q, op);
-
-       if (g_queue_get_length(sim->simop_q) == 1)
-               sim->simop_source = g_timeout_add(0, sim_op_next, sim);
-
-       return 0;
-}
-
-int ofono_sim_read_bytes(struct ofono_sim *sim, int id,
-                       unsigned short offset, int num_bytes,
-                       ofono_sim_file_read_cb_t cb, void *data)
-{
-       return sim_read_common(sim, id, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
+       return sim_fs_read(sim->simfs, id, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
                                offset, num_bytes, cb, data);
 }
 
@@ -2176,7 +1531,10 @@ int ofono_sim_read(struct ofono_sim *sim, int id,
                        enum ofono_sim_file_structure expected_type,
                        ofono_sim_file_read_cb_t cb, void *data)
 {
-       return sim_read_common(sim, id, expected_type, 0, -1, cb, data);
+       if (sim == NULL)
+               return -1;
+
+       return sim_fs_read(sim->simfs, id, expected_type, 0, 0, cb, data);
 }
 
 int ofono_sim_write(struct ofono_sim *sim, int id,
@@ -2184,55 +1542,11 @@ int ofono_sim_write(struct ofono_sim *sim, int id,
                        enum ofono_sim_file_structure structure, int record,
                        const unsigned char *data, int length, void *userdata)
 {
-       struct sim_file_op *op;
-       gconstpointer fn = NULL;
-
-       if (!cb)
-               return -1;
-
        if (sim == NULL)
                return -1;
 
-       if (!sim->driver)
-               return -1;
-
-       switch (structure) {
-       case OFONO_SIM_FILE_STRUCTURE_TRANSPARENT:
-               fn = sim->driver->write_file_transparent;
-               break;
-       case OFONO_SIM_FILE_STRUCTURE_FIXED:
-               fn = sim->driver->write_file_linear;
-               break;
-       case OFONO_SIM_FILE_STRUCTURE_CYCLIC:
-               fn = sim->driver->write_file_cyclic;
-               break;
-       default:
-               ofono_error("Unrecognized file structure, this can't happen");
-       }
-
-       if (fn == NULL)
-               return -1;
-
-       if (!sim->simop_q)
-               sim->simop_q = g_queue_new();
-
-       op = g_new0(struct sim_file_op, 1);
-
-       op->id = id;
-       op->cb = cb;
-       op->userdata = userdata;
-       op->is_read = FALSE;
-       op->buffer = g_memdup(data, length);
-       op->structure = structure;
-       op->length = length;
-       op->current = record;
-
-       g_queue_push_tail(sim->simop_q, op);
-
-       if (g_queue_get_length(sim->simop_q) == 1)
-               sim->simop_source = g_timeout_add(0, sim_op_next, sim);
-
-       return 0;
+       return sim_fs_write(sim->simfs, id, cb, structure, record, data, length,
+                               userdata);
 }
 
 const char *ofono_sim_get_imsi(struct ofono_sim *sim)
@@ -2281,17 +1595,9 @@ static void sim_inserted_update(struct ofono_sim *sim)
 
 static void sim_free_state(struct ofono_sim *sim)
 {
-       if (sim->simop_source) {
-               g_source_remove(sim->simop_source);
-               sim->simop_source = 0;
-       }
-
-       if (sim->simop_q) {
-               /* Note: users of ofono_sim_read/write must not assume that
-                * the callback happens for operations still in progress.  */
-               g_queue_foreach(sim->simop_q, (GFunc)sim_file_op_free, NULL);
-               g_queue_free(sim->simop_q);
-               sim->simop_q = NULL;
+       if (sim->simfs) {
+               sim_fs_free(sim->simfs);
+               sim->simfs = NULL;
        }
 
        if (sim->iccid) {
@@ -2546,6 +1852,7 @@ void ofono_sim_register(struct ofono_sim *sim)
 
        ofono_modem_add_interface(modem, OFONO_SIM_MANAGER_INTERFACE);
        sim->state_watches = __ofono_watchlist_new(g_free);
+       sim->simfs = sim_fs_new(sim, sim->driver);
 
        __ofono_atom_register(sim->atom, sim_unregister);
 
diff --git a/src/simfs.c b/src/simfs.c
new file mode 100644 (file)
index 0000000..7a9ecc5
--- /dev/null
@@ -0,0 +1,721 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2010  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "ofono.h"
+
+#include "simfs.h"
+#include "simutil.h"
+#include "storage.h"
+
+#define SIM_CACHE_MODE 0600
+#define SIM_CACHE_PATH STORAGEDIR "/%s-%i/%04x"
+#define SIM_CACHE_PATH_LEN(imsilen) (strlen(SIM_CACHE_PATH) - 3 + imsilen)
+#define SIM_CACHE_HEADER_SIZE 38
+#define SIM_FILE_INFO_SIZE 6
+
+static gboolean sim_fs_op_next(gpointer user_data);
+static gboolean sim_fs_op_read_record(gpointer user);
+static gboolean sim_fs_op_read_block(gpointer user_data);
+
+struct sim_fs_op {
+       int id;
+       enum ofono_sim_file_structure structure;
+       unsigned short offset;
+       int num_bytes;
+       int length;
+       int record_length;
+       int current;
+       gconstpointer cb;
+       gboolean is_read;
+       void *userdata;
+};
+
+static void sim_fs_op_free(struct sim_fs_op *node)
+{
+       g_free(node);
+}
+
+struct sim_fs {
+       GQueue *op_q;
+       gint op_source;
+       unsigned char bitmap[32];
+       int fd;
+       unsigned char *buffer;
+       struct ofono_sim *sim;
+       const struct ofono_sim_driver *driver;
+};
+
+void sim_fs_free(struct sim_fs *fs)
+{
+       if (fs->op_source) {
+               g_source_remove(fs->op_source);
+               fs->op_source = 0;
+       }
+
+       /*
+        * Note: users of sim_fs must not assume that the callback happens
+        * for operations still in progress
+        */
+       g_queue_foreach(fs->op_q, (GFunc)sim_fs_op_free, NULL);
+       g_queue_free(fs->op_q);
+
+       g_free(fs);
+}
+
+struct sim_fs *sim_fs_new(struct ofono_sim *sim,
+                               const struct ofono_sim_driver *driver)
+{
+       struct sim_fs *fs;
+
+       fs = g_try_new0(struct sim_fs, 1);
+       if (fs == NULL)
+               return NULL;
+
+       fs->sim = sim;
+       fs->driver = driver;
+       fs->fd = -1;
+
+       return fs;
+}
+
+static void sim_fs_end_current(struct sim_fs *fs)
+{
+       struct sim_fs_op *op = g_queue_pop_head(fs->op_q);
+
+       if (g_queue_get_length(fs->op_q) > 0)
+               fs->op_source = g_idle_add(sim_fs_op_next, fs);
+
+       if (fs->fd != -1) {
+               TFR(close(fs->fd));
+               fs->fd = -1;
+       }
+
+       g_free(fs->buffer);
+       fs->buffer = NULL;
+
+       memset(fs->bitmap, 0, sizeof(fs->bitmap));
+
+       sim_fs_op_free(op);
+}
+
+static void sim_fs_op_error(struct sim_fs *fs)
+{
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+
+       if (op->is_read == TRUE)
+               ((ofono_sim_file_read_cb_t) op->cb)
+                       (0, 0, 0, 0, 0, op->userdata);
+       else
+               ((ofono_sim_file_write_cb_t) op->cb)
+                       (0, op->userdata);
+
+       sim_fs_end_current(fs);
+}
+
+static gboolean cache_block(struct sim_fs *fs, int block, int block_len,
+                               const unsigned char *data, int num_bytes)
+{
+       int offset;
+       int bit;
+       ssize_t r;
+       unsigned char b;
+
+       if (fs->fd == -1)
+               return FALSE;
+
+       if (lseek(fs->fd, block * block_len +
+                               SIM_CACHE_HEADER_SIZE, SEEK_SET) == (off_t) -1)
+               return FALSE;
+
+       r = TFR(write(fs->fd, data, num_bytes));
+
+       if (r != num_bytes)
+               return FALSE;
+
+       /* update present bit for this block */
+       offset = block / 8;
+       bit = block % 8;
+
+       /* lseek to correct byte (skip file info) */
+       lseek(fs->fd, offset + SIM_FILE_INFO_SIZE, SEEK_SET);
+
+       b = fs->bitmap[offset];
+       b |= 1 << bit;
+
+       r = TFR(write(fs->fd, &b, sizeof(b)));
+
+       if (r != sizeof(b))
+               return FALSE;
+
+       fs->bitmap[offset] = b;
+
+       return TRUE;
+}
+
+static void sim_fs_op_write_cb(const struct ofono_error *error, void *data)
+{
+       struct sim_fs *fs = data;
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+       ofono_sim_file_write_cb_t cb = op->cb;
+
+       if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
+               cb(1, op->userdata);
+       else
+               cb(0, op->userdata);
+
+       sim_fs_end_current(fs);
+}
+
+static void sim_fs_op_read_block_cb(const struct ofono_error *error,
+                                       const unsigned char *data, int len,
+                                       void *user)
+{
+       struct sim_fs *fs = user;
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+       int start_block;
+       int end_block;
+       int bufoff;
+       int dataoff;
+       int tocopy;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               sim_fs_op_error(fs);
+               return;
+       }
+
+       start_block = op->offset / 256;
+       end_block = (op->offset + (op->num_bytes - 1)) / 256;
+
+       if (op->current == start_block) {
+               bufoff = 0;
+               dataoff = op->offset % 256;
+               tocopy = MIN(256 - op->offset % 256, len);
+       } else {
+               bufoff = (op->current - start_block - 1) * 256 +
+                               op->offset % 256;
+               dataoff = 0;
+               tocopy = len;
+       }
+
+       memcpy(fs->buffer + bufoff, data + dataoff, tocopy);
+       cache_block(fs, op->current, 256, data, len);
+
+       op->current++;
+
+       if (op->current > end_block) {
+               ofono_sim_file_read_cb_t cb = op->cb;
+
+               cb(1, op->num_bytes, 0, fs->buffer,
+                               op->record_length, op->userdata);
+
+               sim_fs_end_current(fs);
+       } else {
+               fs->op_source = g_idle_add(sim_fs_op_read_block, fs);
+       }
+}
+
+static gboolean sim_fs_op_read_block(gpointer user_data)
+{
+       struct sim_fs *fs = user_data;
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+       int start_block;
+       int end_block;
+       unsigned short read_bytes;
+
+       start_block = op->offset / 256;
+       end_block = (op->offset + (op->num_bytes - 1)) / 256;
+
+       if (op->current == start_block) {
+               fs->buffer = g_try_new0(unsigned char, op->num_bytes);
+
+               if (fs->buffer == NULL) {
+                       sim_fs_op_error(fs);
+                       return FALSE;
+               }
+       }
+
+       while (fs->fd != -1 && op->current <= end_block) {
+               int offset = op->current / 8;
+               int bit = 1 << op->current % 8;
+               int bufoff;
+               int seekoff;
+               int toread;
+
+               if ((fs->bitmap[offset] & bit) == 0)
+                       break;
+
+               if (op->current == start_block) {
+                       bufoff = 0;
+                       seekoff = SIM_CACHE_HEADER_SIZE + op->current * 256 +
+                               op->offset % 256;
+                       toread = MIN(256 - op->offset % 256,
+                                       op->length - op->current * 256);
+               } else {
+                       bufoff = (op->current - start_block - 1) * 256 +
+                                       op->offset % 256;
+                       seekoff = SIM_CACHE_HEADER_SIZE + op->current * 256;
+                       toread = MIN(256, op->length - op->current * 256);
+               }
+
+               if (lseek(fs->fd, seekoff, SEEK_SET) == (off_t) -1)
+                       break;
+
+               if (TFR(read(fs->fd, fs->buffer + bufoff, toread)) != toread)
+                       break;
+
+               op->current += 1;
+       }
+
+       if (op->current > end_block) {
+               ofono_sim_file_read_cb_t cb = op->cb;
+
+               cb(1, op->num_bytes, 0, fs->buffer,
+                               op->record_length, op->userdata);
+
+               sim_fs_end_current(fs);
+
+               return FALSE;
+       }
+
+       if (fs->driver->read_file_transparent == NULL) {
+               sim_fs_op_error(fs);
+               return FALSE;
+       }
+
+       read_bytes = MIN(op->length - op->current * 256, 256);
+       fs->driver->read_file_transparent(fs->sim, op->id,
+                                               op->current * 256,
+                                               read_bytes,
+                                               sim_fs_op_read_block_cb, fs);
+
+       return FALSE;
+}
+
+static void sim_fs_op_retrieve_cb(const struct ofono_error *error,
+                                       const unsigned char *data, int len,
+                                       void *user)
+{
+       struct sim_fs *fs = user;
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+       int total = op->length / op->record_length;
+       ofono_sim_file_read_cb_t cb = op->cb;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               sim_fs_op_error(fs);
+               return;
+       }
+
+       cb(1, op->length, op->current, data, op->record_length, op->userdata);
+
+       cache_block(fs, op->current - 1, op->record_length,
+                       data, op->record_length);
+
+       if (op->current < total) {
+               op->current += 1;
+               fs->op_source = g_idle_add(sim_fs_op_read_record, fs);
+       } else {
+               sim_fs_end_current(fs);
+       }
+}
+
+static gboolean sim_fs_op_read_record(gpointer user)
+{
+       struct sim_fs *fs = user;
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+       const struct ofono_sim_driver *driver = fs->driver;
+       int total = op->length / op->record_length;
+       unsigned char buf[256];
+
+       fs->op_source = 0;
+
+       while (fs->fd != -1 && op->current <= total) {
+               int offset = (op->current - 1) / 8;
+               int bit = 1 << ((op->current - 1) % 8);
+               ofono_sim_file_read_cb_t cb = op->cb;
+
+               if ((fs->bitmap[offset] & bit) == 0)
+                       break;
+
+               if (lseek(fs->fd, (op->current - 1) * op->record_length +
+                               SIM_CACHE_HEADER_SIZE, SEEK_SET) == (off_t) -1)
+                       break;
+
+               if (TFR(read(fs->fd, buf, op->record_length)) !=
+                               op->record_length)
+                       break;
+
+               cb(1, op->length, op->current,
+                               buf, op->record_length, op->userdata);
+
+               op->current += 1;
+       }
+
+       if (op->current > total) {
+               sim_fs_end_current(fs);
+
+               return FALSE;
+       }
+
+       switch (op->structure) {
+       case OFONO_SIM_FILE_STRUCTURE_FIXED:
+               if (!driver->read_file_linear) {
+                       sim_fs_op_error(fs);
+                       return FALSE;
+               }
+
+               driver->read_file_linear(fs->sim, op->id, op->current,
+                                               op->record_length,
+                                               sim_fs_op_retrieve_cb, fs);
+               break;
+       case OFONO_SIM_FILE_STRUCTURE_CYCLIC:
+               if (!driver->read_file_cyclic) {
+                       sim_fs_op_error(fs);
+                       return FALSE;
+               }
+
+               driver->read_file_cyclic(fs->sim, op->id, op->current,
+                                               op->record_length,
+                                               sim_fs_op_retrieve_cb, fs);
+               break;
+       default:
+               ofono_error("Unrecognized file structure, this can't happen");
+       }
+
+       return FALSE;
+}
+
+static void sim_fs_op_info_cb(const struct ofono_error *error, int length,
+                               enum ofono_sim_file_structure structure,
+                               int record_length,
+                               const unsigned char access[3], void *data)
+{
+       struct sim_fs *fs = data;
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+       const char *imsi = ofono_sim_get_imsi(fs->sim);
+       enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim);
+       enum sim_file_access update;
+       enum sim_file_access invalidate;
+       enum sim_file_access rehabilitate;
+       unsigned char fileinfo[SIM_CACHE_HEADER_SIZE];
+       gboolean cache;
+       char *path;
+
+       if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+               sim_fs_op_error(fs);
+               return;
+       }
+
+       if (structure != op->structure) {
+               ofono_error("Requested file structure differs from SIM: %x",
+                               op->id);
+               sim_fs_op_error(fs);
+               return;
+       }
+
+       /* TS 11.11, Section 9.3 */
+       update = file_access_condition_decode(access[0] & 0xf);
+       rehabilitate = file_access_condition_decode((access[2] >> 4) & 0xf);
+       invalidate = file_access_condition_decode(access[2] & 0xf);
+
+       op->structure = structure;
+       op->length = length;
+
+       /* Never cache card holder writable files */
+       cache = (update == SIM_FILE_ACCESS_ADM ||
+                       update == SIM_FILE_ACCESS_NEVER) &&
+                       (invalidate == SIM_FILE_ACCESS_ADM ||
+                               invalidate == SIM_FILE_ACCESS_NEVER) &&
+                       (rehabilitate == SIM_FILE_ACCESS_ADM ||
+                               rehabilitate == SIM_FILE_ACCESS_NEVER);
+
+       if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT) {
+               if (op->num_bytes == 0)
+                       op->num_bytes = op->length;
+
+               op->record_length = length;
+               op->current = op->offset / 256;
+               fs->op_source = g_idle_add(sim_fs_op_read_block, fs);
+       } else {
+               op->record_length = record_length;
+               op->current = 1;
+               fs->op_source = g_idle_add(sim_fs_op_read_record, fs);
+       }
+
+       if (imsi == NULL || cache == FALSE)
+               return;
+
+       memset(fileinfo, 0, SIM_CACHE_HEADER_SIZE);
+
+       fileinfo[0] = error->type;
+       fileinfo[1] = length >> 8;
+       fileinfo[2] = length & 0xff;
+       fileinfo[3] = structure;
+       fileinfo[4] = record_length >> 8;
+       fileinfo[5] = record_length & 0xff;
+
+       path = g_strdup_printf(SIM_CACHE_PATH, imsi, phase, op->id);
+       if (create_dirs(path, SIM_CACHE_MODE | S_IXUSR) != 0)
+               goto out;
+
+       fs->fd = TFR(open(path, O_RDWR | O_CREAT | O_TRUNC, SIM_CACHE_MODE));
+
+       if (fs->fd == -1)
+               goto out;
+
+       if (TFR(write(fs->fd, fileinfo, SIM_CACHE_HEADER_SIZE)) !=
+                       SIM_CACHE_HEADER_SIZE) {
+               TFR(close(fs->fd));
+               fs->fd = -1;
+       }
+
+out:
+       g_free(path);
+}
+
+static gboolean sim_fs_op_check_cached(struct sim_fs *fs)
+{
+       const char *imsi = ofono_sim_get_imsi(fs->sim);
+       enum ofono_sim_phase phase = ofono_sim_get_phase(fs->sim);
+       struct sim_fs_op *op = g_queue_peek_head(fs->op_q);
+       gboolean ret = FALSE;
+       char *path;
+       int fd;
+       ssize_t len;
+       unsigned char fileinfo[SIM_CACHE_HEADER_SIZE];
+       int error_type;
+       int file_length;
+       enum ofono_sim_file_structure structure;
+       int record_length;
+
+       if (!imsi)
+               return FALSE;
+
+       path = g_strdup_printf(SIM_CACHE_PATH, imsi, phase, op->id);
+
+       if (path == NULL)
+               return FALSE;
+
+       fd = TFR(open(path, O_RDWR));
+       g_free(path);
+
+       if (fd == -1) {
+               if (errno != ENOENT)
+                       DBG("Error %i opening cache file for "
+                                       "fileid %04x, IMSI %s",
+                                       errno, op->id, imsi);
+
+               return FALSE;
+       }
+
+       len = TFR(read(fd, fileinfo, SIM_CACHE_HEADER_SIZE));
+
+       if (len != SIM_CACHE_HEADER_SIZE)
+               goto error;
+
+       error_type = fileinfo[0];
+       file_length = (fileinfo[1] << 8) | fileinfo[2];
+       structure = fileinfo[3];
+       record_length = (fileinfo[4] << 8) | fileinfo[5];
+
+       if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT)
+               record_length = file_length;
+
+       if (record_length == 0 || file_length < record_length)
+               goto error;
+
+       op->length = file_length;
+       op->record_length = record_length;
+       memcpy(fs->bitmap, fileinfo + SIM_FILE_INFO_SIZE,
+                       SIM_CACHE_HEADER_SIZE - SIM_FILE_INFO_SIZE);
+       fs->fd = fd;
+
+       if (error_type != OFONO_ERROR_TYPE_NO_ERROR ||
+                       structure != op->structure)
+               sim_fs_op_error(fs);
+
+       if (structure == OFONO_SIM_FILE_STRUCTURE_TRANSPARENT) {
+               if (op->num_bytes == 0)
+                       op->num_bytes = op->length;
+
+               op->current = op->offset / 256;
+               fs->op_source = g_idle_add(sim_fs_op_read_block, fs);
+       } else {
+               op->current = 1;
+               fs->op_source = g_idle_add(sim_fs_op_read_record, fs);
+       }
+
+       return TRUE;
+
+error:
+       TFR(close(fd));
+       return ret;
+}
+
+static gboolean sim_fs_op_next(gpointer user_data)
+{
+       struct sim_fs *fs = user_data;
+       const struct ofono_sim_driver *driver = fs->driver;
+       struct sim_fs_op *op;
+
+       fs->op_source = 0;
+
+       if (!fs->op_q)
+               return FALSE;
+
+       op = g_queue_peek_head(fs->op_q);
+
+       if (op->is_read == TRUE) {
+               if (sim_fs_op_check_cached(fs))
+                       return FALSE;
+
+               driver->read_file_info(fs->sim, op->id, sim_fs_op_info_cb, fs);
+       } else {
+               switch (op->structure) {
+               case OFONO_SIM_FILE_STRUCTURE_TRANSPARENT:
+                       driver->write_file_transparent(fs->sim, op->id, 0,
+                                       op->length, fs->buffer,
+                                       sim_fs_op_write_cb, fs);
+                       break;
+               case OFONO_SIM_FILE_STRUCTURE_FIXED:
+                       driver->write_file_linear(fs->sim, op->id, op->current,
+                                       op->length, fs->buffer,
+                                       sim_fs_op_write_cb, fs);
+                       break;
+               case OFONO_SIM_FILE_STRUCTURE_CYCLIC:
+                       driver->write_file_cyclic(fs->sim, op->id,
+                                       op->length, fs->buffer,
+                                       sim_fs_op_write_cb, fs);
+                       break;
+               default:
+                       ofono_error("Unrecognized file structure, "
+                                       "this can't happen");
+               }
+
+               g_free(fs->buffer);
+               fs->buffer = NULL;
+       }
+
+       return FALSE;
+}
+
+int sim_fs_read(struct sim_fs *fs, int id,
+               enum ofono_sim_file_structure expected_type,
+               unsigned short offset, unsigned short num_bytes,
+               ofono_sim_file_read_cb_t cb, void *data)
+{
+       struct sim_fs_op *op;
+
+       if (!cb)
+               return -1;
+
+       if (!fs->driver)
+               return -1;
+
+       if (!fs->driver->read_file_info)
+               return -1;
+
+       if (!fs->op_q)
+               fs->op_q = g_queue_new();
+
+       op = g_new0(struct sim_fs_op, 1);
+
+       op->id = id;
+       op->structure = expected_type;
+       op->cb = cb;
+       op->userdata = data;
+       op->is_read = TRUE;
+       op->offset = offset;
+       op->num_bytes = num_bytes;
+
+       g_queue_push_tail(fs->op_q, op);
+
+       if (g_queue_get_length(fs->op_q) == 1)
+               fs->op_source = g_idle_add(sim_fs_op_next, fs);
+
+       return 0;
+}
+
+int sim_fs_write(struct sim_fs *fs, int id, ofono_sim_file_write_cb_t cb,
+                       enum ofono_sim_file_structure structure, int record,
+                       const unsigned char *data, int length, void *userdata)
+{
+       struct sim_fs_op *op;
+       gconstpointer fn = NULL;
+
+       if (!cb)
+               return -1;
+
+       if (!fs->driver)
+               return -1;
+
+       switch (structure) {
+       case OFONO_SIM_FILE_STRUCTURE_TRANSPARENT:
+               fn = fs->driver->write_file_transparent;
+               break;
+       case OFONO_SIM_FILE_STRUCTURE_FIXED:
+               fn = fs->driver->write_file_linear;
+               break;
+       case OFONO_SIM_FILE_STRUCTURE_CYCLIC:
+               fn = fs->driver->write_file_cyclic;
+               break;
+       default:
+               ofono_error("Unrecognized file structure, this can't happen");
+       }
+
+       if (fn == NULL)
+               return -1;
+
+       if (!fs->op_q)
+               fs->op_q = g_queue_new();
+
+       op = g_new0(struct sim_fs_op, 1);
+
+       op->id = id;
+       op->cb = cb;
+       op->userdata = userdata;
+       op->is_read = FALSE;
+       fs->buffer = g_memdup(data, length);
+       op->structure = structure;
+       op->length = length;
+       op->current = record;
+
+       g_queue_push_tail(fs->op_q, op);
+
+       if (g_queue_get_length(fs->op_q) == 1)
+               fs->op_source = g_idle_add(sim_fs_op_next, fs);
+
+       return 0;
+}
diff --git a/src/simfs.h b/src/simfs.h
new file mode 100644 (file)
index 0000000..7822269
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2010  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct sim_fs;
+
+struct sim_fs *sim_fs_new(struct ofono_sim *sim,
+                               const struct ofono_sim_driver *driver);
+
+int sim_fs_read(struct sim_fs *fs, int id,
+               enum ofono_sim_file_structure expected_type,
+               unsigned short offset, unsigned short num_bytes,
+               ofono_sim_file_read_cb_t cb, void *data);
+
+int sim_fs_write(struct sim_fs *fs, int id, ofono_sim_file_write_cb_t cb,
+                       enum ofono_sim_file_structure structure, int record,
+                       const unsigned char *data, int length, void *userdata);
+
+void sim_fs_free(struct sim_fs *fs);