fxsdk/fxlink/tooling/udisks2.c
Lephenixnoir 9d30377d90
fxlink: full rewrite; deeper device management and TUI
This commit rewrites the entire device management layer of fxlink on the
libusb side, providing new abstractions that support live/async device
management, including communication.

This system is put to use in a new TUI interactive mode (fxlink -t)
which can run in the background, connects to calculators automatically
without interfering with file transfer tools, and is much more detailed
in its interface than the previous interactive mode (fxlink -i).

The TUI mode will also soon be extended to support sending data.
2023-03-03 00:29:00 +01:00

286 lines
6.8 KiB
C

//---------------------------------------------------------------------------//
// ==>/[_]\ fxlink: A community communication tool for CASIO calculators. //
// |:::| Made by Lephe' as part of the fxSDK. //
// \___/ License: MIT <https://opensource.org/licenses/MIT> //
//---------------------------------------------------------------------------//
#include <fxlink/config.h>
#ifndef FXLINK_DISABLE_UDISKS2
#include <fxlink/tooling/udisks2.h>
#include <fxlink/filter.h>
#include <fxlink/logging.h>
int ud2_start(UDisksClient **udc_ptr, UDisksManager **udm_ptr)
{
GError *error = NULL;
UDisksClient *udc = udisks_client_new_sync(NULL, &error);
if(error)
return elog("cannot open udisks2 client: %s\n", error->message);
UDisksManager *udm = udisks_client_get_manager(udc);
if(!udm) {
g_object_unref(udc);
return elog("udisks2 daemon does not seem to be running\n");
}
*udc_ptr = udc;
*udm_ptr = udm;
return 0;
}
void ud2_end(UDisksClient *udc, UDisksManager *udm)
{
(void)udm;
g_object_unref(udc);
}
gchar **ud2_block_devices(UDisksManager *udm)
{
gchar **blocks = NULL;
GVariant *args = g_variant_new("a{sv}", NULL);
GError *error = NULL;
udisks_manager_call_get_block_devices_sync(udm,args,&blocks,NULL,&error);
if(error)
elog("cannot list udisks2 block devices: %s\n", error->message);
return blocks;
}
UDisksBlock *ud2_block(UDisksClient *udc, gchar const *name)
{
UDisksObject *obj = udisks_client_get_object(udc, name);
return obj ? udisks_object_get_block(obj) : NULL;
}
UDisksDrive *ud2_drive(UDisksClient *udc, gchar const *name)
{
UDisksObject *obj = udisks_client_get_object(udc, name);
return obj ? udisks_object_get_drive(obj) : NULL;
}
UDisksFilesystem *ud2_filesystem(UDisksClient *udc, gchar const *name)
{
UDisksObject *obj = udisks_client_get_object(udc, name);
return obj ? udisks_object_get_filesystem(obj) : NULL;
}
//---
// Matching and properties
//---
bool is_casio_drive(UDisksDrive *drive)
{
return strstr(udisks_drive_get_vendor(drive), "CASIO") != NULL;
}
struct fxlink_filter ud2_properties(UDisksDrive *drive)
{
struct fxlink_filter props = { 0 };
props.p7 = false;
props.mass_storage = true;
if(!strcmp(udisks_drive_get_model(drive), "ColorGraph"))
props.series_cg = true;
else if(!strcmp(udisks_drive_get_model(drive), "Calculator"))
props.series_g3 = true;
gchar const *s = udisks_drive_get_serial(drive);
/* LINK sends a 12-byte serial number with four leading 0. Remove them */
if(s && strlen(s) == 12 && !strncmp(s, "0000", 4)) s += 4;
props.serial = strdup(s);
return props;
}
int ud2_unique_matching(struct fxlink_filter const *filter, UDisksClient *udc,
UDisksManager *udm, UDisksBlock **block_ptr, UDisksDrive **drive_ptr,
UDisksFilesystem **fs_ptr)
{
int status = FXLINK_FILTER_NONE;
bool error;
UDisksBlock *block = NULL;
UDisksDrive *drive = NULL;
UDisksFilesystem *fs = NULL;
for_udisks2_devices(it, udc, udm, &error) {
if(!fxlink_filter_match(&it.props, filter))
continue;
/* Already found a device before */
if(status == FXLINK_FILTER_UNIQUE) {
status = FXLINK_FILTER_MULTIPLE;
g_object_unref(fs);
g_object_unref(drive);
g_object_unref(block);
block = NULL;
drive = NULL;
fs = NULL;
break;
}
/* First device: record it */
block = g_object_ref(it.block);
drive = g_object_ref(it.drive);
fs = g_object_ref(it.fs);
status = FXLINK_FILTER_UNIQUE;
}
if(error)
return FXLINK_FILTER_ERROR;
if(block_ptr)
*block_ptr = block;
else if(block)
g_object_unref(block);
if(drive_ptr)
*drive_ptr = drive;
else if(drive)
g_object_unref(drive);
if(fs_ptr)
*fs_ptr = fs;
else if(fs)
g_object_unref(fs);
return status;
}
int ud2_unique_wait(struct fxlink_filter const *filter, delay_t *delay,
UDisksClient *udc, UDisksManager *udm, UDisksBlock **block,
UDisksDrive **drive, UDisksFilesystem **fs)
{
while(true) {
int rc = ud2_unique_matching(filter, udc, udm, block, drive, fs);
if(rc != FXLINK_FILTER_NONE) return rc;
if(delay_cycle(delay)) return FXLINK_FILTER_NONE;
udisks_client_settle(udc);
}
}
//---
// Iteration on UDisks2 devices
//---
ud2_iterator_t ud2_iter_start(UDisksClient *udc, UDisksManager *udm,
bool *error)
{
ud2_iterator_t it = { .udc = udc };
it.devices = ud2_block_devices(udm);
if(!it.devices) {
it.done = true;
if(error) *error = true;
return it;
}
it.index = -1;
ud2_iter_next(&it);
if(error) *error = false;
return it;
}
void ud2_iter_next(ud2_iterator_t *it)
{
if(it->done == true) return;
/* Free the resources from the previous iteration */
if(it->fs) g_object_unref(it->fs);
if(it->drive) g_object_unref(it->drive);
if(it->block) g_object_unref(it->block);
it->block = NULL;
it->drive = NULL;
it->fs = NULL;
/* Load the next device */
if(!it->devices[++it->index]) {
it->done = true;
}
else {
gchar const *path = it->devices[it->index];
it->block = ud2_block(it->udc, path);
if(!it->block) return ud2_iter_next(it);
/* Skip non-CASIO devices right away */
it->drive = ud2_drive(it->udc, udisks_block_get_drive(it->block));
if(!it->drive || !is_casio_drive(it->drive))
return ud2_iter_next(it);
/* Only consider file systems (not partition tables) */
it->fs = ud2_filesystem(it->udc, path);
if(!it->fs) return ud2_iter_next(it);
it->props = ud2_properties(it->drive);
}
if(it->done)
g_strfreev(it->devices);
}
//---
// Main functions
//---
int main_blocks(struct fxlink_filter *filter, delay_t *delay)
{
fxlink_filter_clean_udisks2(filter);
UDisksClient *udc = NULL;
UDisksManager *udm = NULL;
if(ud2_start(&udc, &udm)) return 1;
ud2_unique_wait(filter, delay, udc, udm, NULL, NULL, NULL);
int total_devices = 0;
bool error;
for_udisks2_devices(it, udc, udm, &error) {
if(!fxlink_filter_match(&it.props, filter))
continue;
if(total_devices > 0) printf("\n");
if(it.props.series_cg)
printf("fx-CG series USB Mass Storage filesystem\n");
else if(it.props.series_g3)
printf("G-III series USB Mass Storage filesystem\n");
else
printf("Unknown USB Mass Storage filesystem\n");
printf(" Block device: %s\n",
udisks_block_get_device(it.block));
printf(" Identification: Vendor: %s, Model: %s\n",
udisks_drive_get_vendor(it.drive),
udisks_drive_get_model(it.drive));
if(it.props.serial)
printf(" Serial number: %s\n", it.props.serial);
gchar const * const * mount_points =
udisks_filesystem_get_mount_points(it.fs);
if(!mount_points || !mount_points[0]) {
printf(" Mounted: no\n");
}
else for(int i = 0; mount_points[i]; i++) {
printf(" Mounted at: %s\n", mount_points[i]);
}
printf(" Properties: ");
fxlink_filter_print(stdout, &it.props);
printf("\n");
total_devices++;
}
if(!error && !total_devices)
printf("No%s device found.\n", filter ? " matching" : "");
ud2_end(udc, udm);
return 0;
}
#endif /* FXLINK_DISABLE_UDISKS2 */