This adds file transfer support over RATP. The host can export a directory using the bbremote tool which can then be mounted under barebox as a filesystem. Signed-off-by: Jan Luebbe <jlu@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Tested-by: Andrey Smirnov <andrew.smirnov@gmail.com>
476 lines
11 KiB
C
476 lines
11 KiB
C
/*
|
|
* 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; version 2.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "barebox-ratpfs: " fmt
|
|
|
|
#include <common.h>
|
|
#include <command.h>
|
|
#include <init.h>
|
|
#include <malloc.h>
|
|
#include <fs.h>
|
|
#include <errno.h>
|
|
#include <linux/stat.h>
|
|
#include <asm/unaligned.h>
|
|
#include <ratp_bb.h>
|
|
|
|
#define RATPFS_TYPE_MOUNT_CALL 1
|
|
#define RATPFS_TYPE_MOUNT_RETURN 2
|
|
#define RATPFS_TYPE_READDIR_CALL 3
|
|
#define RATPFS_TYPE_READDIR_RETURN 4
|
|
#define RATPFS_TYPE_STAT_CALL 5
|
|
#define RATPFS_TYPE_STAT_RETURN 6
|
|
#define RATPFS_TYPE_OPEN_CALL 7
|
|
#define RATPFS_TYPE_OPEN_RETURN 8
|
|
#define RATPFS_TYPE_READ_CALL 9
|
|
#define RATPFS_TYPE_READ_RETURN 10
|
|
#define RATPFS_TYPE_WRITE_CALL 11
|
|
#define RATPFS_TYPE_WRITE_RETURN 12
|
|
#define RATPFS_TYPE_CLOSE_CALL 13
|
|
#define RATPFS_TYPE_CLOSE_RETURN 14
|
|
#define RATPFS_TYPE_TRUNCATE_CALL 15
|
|
#define RATPFS_TYPE_TRUNCATE_RETURN 16
|
|
|
|
struct ratpfs_file {
|
|
uint32_t handle;
|
|
};
|
|
|
|
struct ratpfs_dir {
|
|
char *entries;
|
|
int len, off;
|
|
DIR dir;
|
|
};
|
|
|
|
static int ratpfs_create(struct device_d __always_unused *dev,
|
|
const char __always_unused *pathname,
|
|
mode_t __always_unused mode)
|
|
{
|
|
pr_debug("%s\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ratpfs_mkdir(struct device_d __always_unused *dev,
|
|
const char __always_unused *pathname)
|
|
{
|
|
pr_debug("%s\n", __func__);
|
|
|
|
return -ENOSYS;
|
|
}
|
|
|
|
static int ratpfs_rm(struct device_d __always_unused *dev,
|
|
const char *pathname)
|
|
{
|
|
pr_debug("%s\n", __func__);
|
|
|
|
/* Get rid of leading '/' */
|
|
pathname = &pathname[1];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ratpfs_truncate(struct device_d __always_unused *dev,
|
|
FILE *f, ulong size)
|
|
{
|
|
int len_tx = 1 /* type */
|
|
+ 4 /* handle */
|
|
+ 4 /* size */;
|
|
struct ratp_bb_pkt *pkt_tx = xzalloc(sizeof(*pkt_tx)+len_tx);
|
|
struct ratp_bb_pkt *pkt_rx = NULL;
|
|
struct ratpfs_file *rfile = f->priv;
|
|
int ret;
|
|
|
|
pr_debug("%s: len_tx=%i handle=%i size=%i\n", __func__,
|
|
len_tx, rfile->handle, (int)size);
|
|
|
|
pkt_tx->len = len_tx;
|
|
pkt_tx->data[0] = RATPFS_TYPE_TRUNCATE_CALL;
|
|
put_unaligned_be32(rfile->handle, &pkt_tx->data[1]);
|
|
put_unaligned_be32(size, &pkt_tx->data[5]);
|
|
|
|
ret = barebox_ratp_fs_call(pkt_tx, &pkt_rx);
|
|
if (ret) {
|
|
ret = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
pr_debug("%s: len_rx=%i\n", __func__, pkt_rx->len);
|
|
|
|
if (pkt_rx->len < 1 || pkt_rx->data[0] != RATPFS_TYPE_TRUNCATE_RETURN) {
|
|
pr_err("invalid truncate response\n");
|
|
ret = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
free(pkt_rx);
|
|
return ret;
|
|
}
|
|
|
|
static int ratpfs_open(struct device_d __always_unused *dev,
|
|
FILE *file, const char *filename)
|
|
{
|
|
int len_name = strlen(filename);
|
|
int len_tx = 1 /* type */
|
|
+ 4 /* flags */
|
|
+ len_name /* path */;
|
|
struct ratp_bb_pkt *pkt_tx = xzalloc(sizeof(*pkt_tx) + len_tx);
|
|
struct ratp_bb_pkt *pkt_rx = NULL;
|
|
struct ratpfs_file *rfile = xzalloc(sizeof(*rfile));
|
|
int ret;
|
|
|
|
pr_debug("%s: len_tx=%i filename='%s'\n", __func__, len_tx, filename);
|
|
|
|
pkt_tx->len = len_tx;
|
|
pkt_tx->data[0] = RATPFS_TYPE_OPEN_CALL;
|
|
put_unaligned_be32(file->flags, &pkt_tx->data[1]);
|
|
memcpy(&pkt_tx->data[5], filename, len_name);
|
|
|
|
ret = barebox_ratp_fs_call(pkt_tx, &pkt_rx);
|
|
if (ret) {
|
|
ret = -EIO;
|
|
goto err;
|
|
}
|
|
|
|
pr_debug("%s: len_rx=%i\n", __func__, pkt_rx->len);
|
|
if (pkt_rx->len < 1 || pkt_rx->data[0] != RATPFS_TYPE_OPEN_RETURN) {
|
|
pr_err("invalid open response\n");
|
|
ret = -EIO;
|
|
goto err;
|
|
}
|
|
rfile->handle = get_unaligned_be32(&pkt_rx->data[1]);
|
|
if (rfile->handle == 0) {
|
|
ret = -get_unaligned_be32(&pkt_rx->data[5]); /* errno */
|
|
goto err;
|
|
}
|
|
file->priv = rfile;
|
|
file->size = get_unaligned_be32(&pkt_rx->data[5]);
|
|
|
|
goto out;
|
|
|
|
err:
|
|
file->priv = NULL;
|
|
free(rfile);
|
|
out:
|
|
free(pkt_rx);
|
|
return ret;
|
|
}
|
|
|
|
static int ratpfs_close(struct device_d __always_unused *dev,
|
|
FILE *f)
|
|
{
|
|
int len_tx = 1 /* type */
|
|
+ 4 /* handle */;
|
|
struct ratp_bb_pkt *pkt_tx = xzalloc(sizeof(*pkt_tx) + len_tx);
|
|
struct ratp_bb_pkt *pkt_rx = NULL;
|
|
struct ratpfs_file *rfile = f->priv;
|
|
int ret;
|
|
|
|
pr_debug("%s: len_tx=%i handle=%i\n", __func__,
|
|
len_tx, rfile->handle);
|
|
|
|
pkt_tx->len = len_tx;
|
|
pkt_tx->data[0] = RATPFS_TYPE_CLOSE_CALL;
|
|
put_unaligned_be32(rfile->handle, &pkt_tx->data[1]);
|
|
|
|
ret = barebox_ratp_fs_call(pkt_tx, &pkt_rx);
|
|
if (ret) {
|
|
ret = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
pr_debug("%s: len_rx=%i\n", __func__, pkt_rx->len);
|
|
|
|
if (pkt_rx->len < 1 || pkt_rx->data[0] != RATPFS_TYPE_CLOSE_RETURN) {
|
|
pr_err("invalid close response\n");
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
free(pkt_rx);
|
|
return ret;
|
|
}
|
|
|
|
static int ratpfs_write(struct device_d __always_unused *dev,
|
|
FILE *f, const void *buf, size_t orig_size)
|
|
{
|
|
int size = min((int)orig_size, 4096);
|
|
int len_tx = 1 /* type */
|
|
+ 4 /* handle */
|
|
+ 4 /* pos */
|
|
+ size /* data */;
|
|
struct ratp_bb_pkt *pkt_tx = xzalloc(sizeof(*pkt_tx) + len_tx);
|
|
struct ratp_bb_pkt *pkt_rx = NULL;
|
|
struct ratpfs_file *rfile = f->priv;
|
|
int ret;
|
|
|
|
pr_debug("%s: len_tx=%i handle=%i pos=%i size=%i\n", __func__,
|
|
len_tx, rfile->handle, (int)f->pos, size);
|
|
|
|
pkt_tx->len = len_tx;
|
|
pkt_tx->data[0] = RATPFS_TYPE_WRITE_CALL;
|
|
put_unaligned_be32(rfile->handle, &pkt_tx->data[1]);
|
|
put_unaligned_be32(f->pos, &pkt_tx->data[5]);
|
|
memcpy(&pkt_tx->data[9], buf, size);
|
|
|
|
ret = barebox_ratp_fs_call(pkt_tx, &pkt_rx);
|
|
if (ret) {
|
|
ret = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
pr_debug("%s: len_rx=%i\n", __func__, pkt_rx->len);
|
|
|
|
if (pkt_rx->len < 1 || pkt_rx->data[0] != RATPFS_TYPE_WRITE_RETURN) {
|
|
pr_err("invalid write response\n");
|
|
ret = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
ret = size;
|
|
out:
|
|
free(pkt_rx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ratpfs_read(struct device_d __always_unused *dev,
|
|
FILE *f, void *buf, size_t orig_size)
|
|
{
|
|
int size = min((int)orig_size, 4096);
|
|
int len_tx = 1 /* type */
|
|
+ 4 /* handle */
|
|
+ 4 /* pos */
|
|
+ 4 /* size */;
|
|
struct ratp_bb_pkt *pkt_tx = xzalloc(sizeof(*pkt_tx) + len_tx);
|
|
struct ratp_bb_pkt *pkt_rx = NULL;
|
|
struct ratpfs_file *rfile = f->priv;
|
|
int ret;
|
|
|
|
pr_debug("%s: len_tx=%i handle=%i pos=%i size=%i\n", __func__,
|
|
len_tx, rfile->handle, (int)f->pos, size);
|
|
|
|
pkt_tx->len = len_tx;
|
|
pkt_tx->data[0] = RATPFS_TYPE_READ_CALL;
|
|
put_unaligned_be32(rfile->handle, &pkt_tx->data[1]);
|
|
put_unaligned_be32(f->pos, &pkt_tx->data[5]);
|
|
put_unaligned_be32(size, &pkt_tx->data[9]);
|
|
|
|
ret = barebox_ratp_fs_call(pkt_tx, &pkt_rx);
|
|
if (ret) {
|
|
ret = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
pr_debug("%s: len_rx=%i\n", __func__, pkt_rx->len);
|
|
if (pkt_rx->len < 1 || pkt_rx->data[0] != RATPFS_TYPE_READ_RETURN) {
|
|
pr_err("invalid read response\n");
|
|
ret = -EIO;
|
|
goto out;
|
|
}
|
|
size = pkt_rx->len - 1;
|
|
memcpy(buf, &pkt_rx->data[1], size);
|
|
ret = size;
|
|
|
|
out:
|
|
free(pkt_rx);
|
|
return ret;
|
|
}
|
|
|
|
static loff_t ratpfs_lseek(struct device_d __always_unused *dev,
|
|
FILE *f, loff_t pos)
|
|
{
|
|
pr_debug("%s\n", __func__);
|
|
f->pos = pos;
|
|
return f->pos;
|
|
}
|
|
|
|
static DIR* ratpfs_opendir(struct device_d __always_unused *dev,
|
|
const char *pathname)
|
|
{
|
|
int len_name = strlen(pathname);
|
|
int len_tx = 1 /* type */
|
|
+ len_name /* path */;
|
|
struct ratp_bb_pkt *pkt_tx = xzalloc(sizeof(*pkt_tx)+len_tx);
|
|
struct ratp_bb_pkt *pkt_rx = NULL;
|
|
struct ratpfs_dir *rdir = xzalloc(sizeof(*rdir));
|
|
int ret;
|
|
|
|
pr_debug("%s: len_tx=%i pathname='%s'\n", __func__, len_tx, pathname);
|
|
|
|
pkt_tx->len = len_tx;
|
|
pkt_tx->data[0] = RATPFS_TYPE_READDIR_CALL;
|
|
memcpy(&pkt_tx->data[1], pathname, len_name);
|
|
|
|
ret = barebox_ratp_fs_call(pkt_tx, &pkt_rx);
|
|
if (!ret) {
|
|
pr_debug("%s: len_rx=%i\n", __func__, pkt_rx->len);
|
|
if (pkt_rx->len < 1 || pkt_rx->data[0] != RATPFS_TYPE_READDIR_RETURN) {
|
|
pr_err("invalid readdir response\n");
|
|
free(pkt_rx);
|
|
return NULL;
|
|
}
|
|
rdir->len = pkt_rx->len - 1;
|
|
rdir->entries = xmemdup(&pkt_rx->data[1], rdir->len);
|
|
free(pkt_rx);
|
|
return &rdir->dir;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static struct dirent *ratpfs_readdir(struct device_d *dev, DIR *dir)
|
|
{
|
|
struct ratpfs_dir *rdir = container_of(dir, struct ratpfs_dir, dir);
|
|
int i;
|
|
|
|
pr_debug("%s\n", __func__);
|
|
|
|
if (rdir->len <= rdir->off)
|
|
return NULL;
|
|
|
|
for (i = 0; rdir->off < rdir->len; rdir->off++, i++) {
|
|
dir->d.d_name[i] = rdir->entries[rdir->off];
|
|
if (dir->d.d_name[i] == 0)
|
|
break;
|
|
}
|
|
rdir->off++;
|
|
|
|
return &dir->d;
|
|
}
|
|
|
|
static int ratpfs_closedir(struct device_d *dev, DIR *dir)
|
|
{
|
|
struct ratpfs_dir *rdir = container_of(dir, struct ratpfs_dir, dir);
|
|
|
|
pr_debug("%s\n", __func__);
|
|
|
|
free(rdir->entries);
|
|
free(rdir);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ratpfs_stat(struct device_d __always_unused *dev,
|
|
const char *filename, struct stat *s)
|
|
{
|
|
int len_name = strlen(filename);
|
|
int len_tx = 1 /* type */
|
|
+ len_name; /* path */
|
|
struct ratp_bb_pkt *pkt_tx = xzalloc(sizeof(*pkt_tx) + len_tx);
|
|
struct ratp_bb_pkt *pkt_rx = NULL;
|
|
int ret;
|
|
|
|
pr_debug("%s: len_tx=%i filename='%s'\n", __func__, len_tx, filename);
|
|
|
|
pkt_tx->len = len_tx;
|
|
pkt_tx->data[0] = RATPFS_TYPE_STAT_CALL;
|
|
memcpy(&pkt_tx->data[1], filename, len_name);
|
|
|
|
ret = barebox_ratp_fs_call(pkt_tx, &pkt_rx);
|
|
if (ret) {
|
|
ret = -EIO;
|
|
goto out;
|
|
}
|
|
|
|
pr_debug("%s: len_rx=%i\n", __func__, pkt_rx->len);
|
|
if (pkt_rx->len < 6 || pkt_rx->data[0] != RATPFS_TYPE_STAT_RETURN) {
|
|
pr_err("invalid stat response\n");
|
|
goto out;
|
|
}
|
|
switch (pkt_rx->data[1]) {
|
|
case 0:
|
|
ret = -ENOENT;
|
|
break;
|
|
case 1:
|
|
s->st_mode = S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO;
|
|
break;
|
|
case 2:
|
|
s->st_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
|
|
break;
|
|
}
|
|
s->st_size = get_unaligned_be32(&pkt_rx->data[2]);
|
|
|
|
out:
|
|
free(pkt_rx);
|
|
return ret;
|
|
}
|
|
|
|
static int ratpfs_probe(struct device_d *dev)
|
|
{
|
|
int len_tx = 1; /* type */
|
|
struct ratp_bb_pkt *pkt_tx = xzalloc(sizeof(*pkt_tx) + len_tx);
|
|
struct ratp_bb_pkt *pkt_rx = NULL;
|
|
int ret;
|
|
struct fs_device_d *fsdev = dev_to_fs_device(dev);
|
|
|
|
pr_debug("%s\n", __func__);
|
|
|
|
ret = barebox_ratp_fs_mount(fsdev->path);
|
|
if (ret)
|
|
return ret;
|
|
|
|
pkt_tx->len = len_tx;
|
|
pkt_tx->data[0] = RATPFS_TYPE_MOUNT_CALL;
|
|
|
|
ret = barebox_ratp_fs_call(pkt_tx, &pkt_rx);
|
|
if (ret)
|
|
goto out;
|
|
|
|
if (pkt_rx->len < 1 || pkt_rx->data[0] != RATPFS_TYPE_MOUNT_RETURN) {
|
|
pr_err("invalid mount response\n");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
free(pkt_rx);
|
|
|
|
if (ret)
|
|
barebox_ratp_fs_mount(NULL);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void ratpfs_remove(struct device_d __always_unused *dev)
|
|
{
|
|
pr_debug("%s\n", __func__);
|
|
|
|
barebox_ratp_fs_mount(NULL);
|
|
}
|
|
|
|
static struct fs_driver_d ratpfs_driver = {
|
|
.open = ratpfs_open,
|
|
.close = ratpfs_close,
|
|
.read = ratpfs_read,
|
|
.lseek = ratpfs_lseek,
|
|
.opendir = ratpfs_opendir,
|
|
.readdir = ratpfs_readdir,
|
|
.closedir = ratpfs_closedir,
|
|
.stat = ratpfs_stat,
|
|
.create = ratpfs_create,
|
|
.unlink = ratpfs_rm,
|
|
.mkdir = ratpfs_mkdir,
|
|
.rmdir = ratpfs_rm,
|
|
.write = ratpfs_write,
|
|
.truncate = ratpfs_truncate,
|
|
.flags = FS_DRIVER_NO_DEV,
|
|
.drv = {
|
|
.probe = ratpfs_probe,
|
|
.remove = ratpfs_remove,
|
|
.name = "ratpfs",
|
|
}
|
|
};
|
|
|
|
static int ratpfs_init(void)
|
|
{
|
|
return register_fs_driver(&ratpfs_driver);
|
|
}
|
|
coredevice_initcall(ratpfs_init); |