Files
2014-04-10 12:20:24 +00:00

160 lines
3.5 KiB
C

/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
/* To avoid many #ifdef and associated mess, all headers are included there */
#include "libsdbfs.h"
static struct sdbfs *sdbfs_list;
/* All fields unused by the caller are expected to be zeroed */
int sdbfs_dev_create(struct sdbfs *fs, int verbose)
{
unsigned int magic;
/* First, check we have the magic */
if (fs->data)
magic = *(unsigned int *)(fs->data + fs->entrypoint);
else
fs->read(fs, fs->entrypoint, &magic, sizeof(magic));
if (htonl(magic) != SDB_MAGIC)
return -ENOTDIR;
if (verbose)
fs->flags |= SDBFS_F_VERBOSE;
fs->next = sdbfs_list;
sdbfs_list = fs;
return 0;
}
int sdbfs_dev_destroy(struct sdbfs *fs)
{
struct sdbfs **p;
for (p = &sdbfs_list; *p && *p != fs; p = &(*p)->next)
;
if (!*p)
return -ENOENT;
*p = fs->next;
return 0;
}
struct sdbfs *sdbfs_dev_find(const char *name)
{
struct sdbfs *l;
for (l = sdbfs_list; l && strcmp(l->name, name); l = l->next)
;
if (!l)
return NULL;
return l;
}
/*
* To open by name or by ID we need to scan the tree. The scan
* function is also exported in order for "sdb-ls" to use it
*/
static struct sdb_device *sdbfs_readentry(struct sdbfs *fs,
unsigned long offset)
{
/*
* This function reads an entry from a known good offset. It
* returns the pointer to the entry, which may be stored in
* the fs structure itself. Only touches fs->current_record
*/
if (fs->data)
return (struct sdb_device *)(fs->data + offset);
if (!fs->read)
return NULL;
fs->read(fs, offset, &fs->current_record, sizeof(fs->current_record));
return &fs->current_record;
}
struct sdb_device *sdbfs_scan(struct sdbfs *fs, int newscan)
{
/*
* This returns a pointer to the next sdb record, or a new one.
* Subdirectories are not supported. Uses all internal fields
*/
struct sdb_device *ret;
struct sdb_interconnect *i;
if (newscan) {
fs->f_offset = fs->entrypoint;
} else {
fs->f_offset += sizeof(struct sdb_device);
if (!fs->nleft)
return NULL;
}
ret = sdbfs_readentry(fs, fs->f_offset);
if (newscan) {
i = (typeof(i))ret;
fs->nleft = ntohs(i->sdb_records) - 1;
} else {
fs->nleft--;
}
return ret;
}
static void __open(struct sdbfs *fs)
{
fs->f_offset = htonll(fs->currentp->sdb_component.addr_first);
fs->f_len = htonll(fs->currentp->sdb_component.addr_last)
+ 1 - fs->f_offset;
fs->read_offset = 0;
}
int sdbfs_open_name(struct sdbfs *fs, const char *name)
{
struct sdb_device *d;
int len = strlen(name);
if (len > 19)
return -ENOENT;
sdbfs_scan(fs, 1); /* new scan: get the interconnect and igore it */
while ( (d = sdbfs_scan(fs, 0)) != NULL) {
if (strncmp(name, d->sdb_component.product.name, len))
continue;
if (len < 19 && d->sdb_component.product.name[len] != ' ')
continue;
fs->currentp = d;
__open(fs);
return 0;
}
return -ENOENT;
}
int sdbfs_open_id(struct sdbfs *fs, uint64_t vid, uint32_t did)
{
struct sdb_device *d;
sdbfs_scan(fs, 1); /* new scan: get the interconnect and igore it */
while ( (d = sdbfs_scan(fs, 0)) != NULL) {
if (vid != d->sdb_component.product.vendor_id)
continue;
if (did != d->sdb_component.product.device_id)
continue;
fs->currentp = d;
__open(fs);
return 0;
}
return -ENOENT;
}
int sdbfs_close(struct sdbfs *fs)
{
fs->currentp = NULL;
return 0;
}