x49gp/block.c
claudiol c61f11cff2 On behalf of: 3298 - Applied 23 patches by 3298:
Misc changes, mostly fixes:
- fix ./newconfig systems other than OSX (broke in c8b823f)
- fix palette usage in 2-bit color mode (was broken ever since grayscale was implemented in 18e1003 and its improperly attributed copy f7913eb)
- fix continuing from breakpoints in the debugger (never worked, was exposed when the debugger was enabled in 9c1f2ed)
- restore the printf statements commented out in 9c1f2ed and hide them with #ifdefs instead
- close the server socket after accepting a debugger connection to allow another simultaneous debug session to be started using the same TCP port
- use the symbolic constant DEFAULT_GDBSTUB_PORT (already defined in gdb_stub.h as 1234) when starting the gdb server in main.c in place of the raw number 1234
- change Makefile to read the name of the firmware file from the file update.scp instead of hardcoding it; this allows users to switch to another firmware
by simply pasting it along with its accompanying update.scp into the x49gp directory

- Enhance port G (keyboard) handling to remember the value of output bits across periods with these bits configured as input
This fixes interaction with HPGCC3 keyboard routines, and it also fixes keys with eint==7 (assuming the stock firmware is in use)
needing a double-tap to work unless pressed very shortly after another keypress (the latter broke in b5f93ed)
- Get rid of the deprecated function warning by switching from gdk_pixbuf_new_from_inline to gdk_pixbuf_new_from_data (based on code by chwdt)
- Delete remaining now-redundant CVS files
- Don't release all buttons anymore if there are still physical keys and/or the left mouse button are holding some down
On the other hand, forcibly release all buttons when losing focus to avoid getting stuck in a state with buttons down
when they are not held down by anything; this would happen due to missed events while not in focus
- Add a context menu to the screen, containing only "Reset" and "Quit" items for now
- Ensure that the files backing flash, sram, and s3c2410-sram exist and have the correct size when opening them
Note that if the flash file does not exist, this will not fill it with the code that's supposed to be in there, obviously causing the calculator to crash. That's an improvement for later.
- Allow the config system to fill not only numbers, but also strings (including filenames) with default values
basename is excluded, but it's planned to be dropped entirely.
- Add an "install" target to the Makefile
- Implement a more generic command-line parser for substantially improved flexibility
- Also adds a proper help option, though the manual referenced in the corresponding output (a manpage, hopefully) does not exist yet.
- Drop the "basename" config property in favor of interpreting relative paths in the config as relative to the config file's location
- Retire the "image" config property in favor of simply loading the image from next to the binary or from the install directory
- Split the UI name property into name (affecting only the window title) and type (affecting the UI image and in the future also the default bootcode) properties
- Change the default calculator type to the 50g everywhere, which probably matches today's user expectations better than the 49g+.
- Create a flash file from the calculator model's appropriate boot file if it does not exist, relying on the bootcode to detect the absence of a firmware
  The bootcode will complain about the missing firmware and enter update mode, so the user needs to supply their favorite firmware version and point the bootcode's updater to it.
  The easiest way is probably pointing the emulated SD card at a directory containing the firmware and its accompanying update.scp file, and then starting the SD-based update.
- Add SD mount / unmount options to the right-click / menu-key popup menu
- Remove most of the old script-based config-generating system since the binary now has these capabilities as well
- Add an applications menu item for installing
- Keep some debug output on stderr and a huge vvfat.log file from showing up when not debugging x49gp itself
- Allow (re-)connecting a debugger to a running session
  This is done through the right-click / menu-key popup menu.
  To avoid confusion due to the accidental clicks leading to an unresponsive interface (caused by waiting for the debugger to connect),
  this option is hidden unless option -d or its new companion -D (same as -d, but does not start the debug interface right away) is present.
- Improved support for hardware keyboards
- Update README.md, add manpage, rename other README files to TODO to reflect their contents
2018-05-07 17:32:14 -04:00

681 lines
14 KiB
C

/* $Id: block.c,v 1.1 2008/12/11 12:18:17 ecd Exp $
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#ifndef QEMU_OLD
#include "qemu-common.h"
#endif
#include <block.h>
#ifndef QEMU_OLD
#include "block_int.h"
#endif
#define SECTOR_BITS 9
#define SECTOR_SIZE (1 << SECTOR_BITS)
static BlockDriverState *bdrv_first;
static BlockDriver *first_drv;
#ifndef ENOMEDIUM
#define ENOMEDIUM ENODEV
#endif
static void bdrv_close(BlockDriverState *bs);
static int
path_is_absolute(const char *path)
{
const char *p;
#ifdef _WIN32
/* specific case for names like: "\\.\d:" */
if (*path == '/' || *path == '\\')
return 1;
#endif
p = strchr(path, ':');
if (p)
p++;
else
p = path;
#ifdef _WIN32
return (*p == '/' || *p == '\\');
#else
return (*p == '/');
#endif
}
/* if filename is absolute, just copy it to dest. Otherwise, build a
path to it by considering it is relative to base_path. URL are
supported. */
static void
path_combine(char *dest, int dest_size,
const char *base_path,
const char *filename)
{
const char *p, *p1;
int len;
if (dest_size <= 0)
return;
if (path_is_absolute(filename)) {
pstrcpy(dest, dest_size, filename);
} else {
p = strchr(base_path, ':');
if (p)
p++;
else
p = base_path;
p1 = strrchr(base_path, '/');
#ifdef _WIN32
{
const char *p2;
p2 = strrchr(base_path, '\\');
if (!p1 || p2 > p1)
p1 = p2;
}
#endif
if (p1)
p1++;
else
p1 = base_path;
if (p1 > p)
p = p1;
len = p - base_path;
if (len > dest_size - 1)
len = dest_size - 1;
memcpy(dest, base_path, len);
dest[len] = '\0';
pstrcat(dest, dest_size, filename);
}
}
#ifdef _WIN32
void get_tmp_filename(char *filename, int size)
{
char temp_dir[MAX_PATH];
GetTempPath(MAX_PATH, temp_dir);
GetTempFileName(temp_dir, "x49", 0, filename);
}
#else
void get_tmp_filename(char *filename, int size)
{
int fd;
/* XXX: race condition possible */
pstrcpy(filename, size, "/tmp/x49gp.XXXXXX");
fd = mkstemp(filename);
close(fd);
}
#endif
#ifdef _WIN32
static int is_windows_drive_prefix(const char *filename)
{
return (((filename[0] >= 'a' && filename[0] <= 'z') ||
(filename[0] >= 'A' && filename[0] <= 'Z')) &&
filename[1] == ':');
}
static int is_windows_drive(const char *filename)
{
if (is_windows_drive_prefix(filename) &&
filename[2] == '\0')
return 1;
if (strstart(filename, "\\\\.\\", NULL) ||
strstart(filename, "//./", NULL))
return 1;
return 0;
}
#endif
static BlockDriver *
find_protocol(const char *filename)
{
BlockDriver *drv1;
char protocol[128];
int len;
const char *p;
#ifdef _WIN32
if (is_windows_drive(filename) ||
is_windows_drive_prefix(filename))
return &bdrv_raw;
#endif
p = strchr(filename, ':');
if (!p)
return &bdrv_raw;
len = p - filename;
if (len > sizeof(protocol) - 1)
len = sizeof(protocol) - 1;
memcpy(protocol, filename, len);
protocol[len] = '\0';
for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) {
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr,
"%s:%u: protocol '%s', drv->protocol_name '%s'\n",
__FUNCTION__, __LINE__, protocol, drv1->protocol_name);
#endif
if (drv1->protocol_name &&
!strcmp(drv1->protocol_name, protocol)) {
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: protocol '%s', drv %p\n",
__FUNCTION__, __LINE__, protocol, drv1);
#endif
return drv1;
}
}
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: protocol '%s', NULL\n",
__FUNCTION__, __LINE__, protocol);
#endif
return NULL;
}
/* XXX: force raw format if block or character device ? It would
simplify the BSD case */
static BlockDriver *
find_image_format(const char *filename)
{
int ret, score, score_max;
BlockDriver *drv1, *drv;
uint8_t buf[2048];
BlockDriverState *bs;
drv = find_protocol(filename);
/* no need to test disk image formats for vvfat */
if (drv == &bdrv_vvfat)
return drv;
ret = bdrv_file_open(&bs, filename, BDRV_O_RDONLY);
if (ret < 0)
return NULL;
ret = bdrv_pread(bs, 0, buf, sizeof(buf));
bdrv_delete(bs);
if (ret < 0) {
return NULL;
}
score_max = 0;
for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) {
if (drv1->bdrv_probe) {
score = drv1->bdrv_probe(buf, ret, filename);
if (score > score_max) {
score_max = score;
drv = drv1;
}
}
}
return drv;
}
int
bdrv_create(BlockDriver *drv,
const char *filename, int64_t size_in_sectors,
const char *backing_file, int flags)
{
if (!drv->bdrv_create)
return -ENOTSUP;
return drv->bdrv_create(filename, size_in_sectors, backing_file, flags);
}
static void
bdrv_register(BlockDriver *bdrv)
{
bdrv->next = first_drv;
first_drv = bdrv;
}
/* create a new block device (by default it is empty) */
BlockDriverState *
bdrv_new(const char *device_name)
{
BlockDriverState **pbs, *bs;
bs = qemu_mallocz(sizeof(BlockDriverState));
if(!bs)
return NULL;
pstrcpy(bs->device_name, sizeof(bs->device_name), device_name);
if (device_name[0] != '\0') {
/* insert at the end */
pbs = &bdrv_first;
while (*pbs != NULL)
pbs = &(*pbs)->next;
*pbs = bs;
}
return bs;
}
/**
* Truncate file to 'offset' bytes (needed only for file protocols)
*/
int
bdrv_truncate(BlockDriverState *bs, int64_t offset)
{
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOMEDIUM;
if (!drv->bdrv_truncate)
return -ENOTSUP;
return drv->bdrv_truncate(bs, offset);
}
/**
* Length of a file in bytes. Return < 0 if error or unknown.
*/
int64_t
bdrv_getlength(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOMEDIUM;
if (!drv->bdrv_getlength) {
/* legacy mode */
return bs->total_sectors * SECTOR_SIZE;
}
return drv->bdrv_getlength(bs);
}
int
bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags)
{
BlockDriverState *bs;
int ret;
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: filename '%s'\n",
__FUNCTION__, __LINE__, filename);
#endif
bs = bdrv_new("");
if (!bs)
return -ENOMEM;
ret = bdrv_open(bs, filename, flags | BDRV_O_FILE);
if (ret < 0) {
bdrv_delete(bs);
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: '%s': %d\n",
__FUNCTION__, __LINE__, filename, ret);
#endif
return ret;
}
*pbs = bs;
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: return 0\n", __FUNCTION__, __LINE__);
#endif
return 0;
}
int
bdrv_open(BlockDriverState *bs, const char *filename, int flags)
{
int ret, open_flags;
char backing_filename[1024];
BlockDriver *drv = NULL;
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: filename '%s'\n",
__FUNCTION__, __LINE__, filename);
#endif
bs->read_only = 0;
bs->is_temporary = 0;
bs->encrypted = 0;
pstrcpy(bs->filename, sizeof(bs->filename), filename);
if (flags & BDRV_O_FILE) {
drv = find_protocol(filename);
if (!drv) {
#ifdef DEBUG_X49GP_BLOCK
f printf(stderr, "%s:%u: drv: %p\n",
__FUNCTION__, __LINE__, drv);
#endif
return -ENOENT;
}
} else {
if (!drv) {
drv = find_image_format(filename);
if (!drv) {
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: drv: %p\n",
__FUNCTION__, __LINE__, drv);
#endif
return -1;
}
}
}
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: drv: %p\n", __FUNCTION__, __LINE__, drv);
#endif
bs->drv = drv;
bs->opaque = qemu_mallocz(drv->instance_size);
if (bs->opaque == NULL && drv->instance_size > 0) {
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: no opaque\n", __FUNCTION__, __LINE__);
#endif
return -1;
}
/* Note: for compatibility, we open disk image files as RDWR, and
RDONLY as fallback */
if (!(flags & BDRV_O_FILE))
open_flags = BDRV_O_RDWR;
else
open_flags = flags & ~(BDRV_O_FILE);
ret = drv->bdrv_open(bs, filename, open_flags);
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: drv->bdrv_open: %d\n",
__FUNCTION__, __LINE__, ret);
#endif
if (ret == -EACCES && !(flags & BDRV_O_FILE)) {
ret = drv->bdrv_open(bs, filename, BDRV_O_RDONLY);
bs->read_only = 1;
}
if (ret < 0) {
qemu_free(bs->opaque);
bs->opaque = NULL;
bs->drv = NULL;
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: return %d\n",
__FUNCTION__, __LINE__, ret);
#endif
return ret;
}
if (drv->bdrv_getlength) {
bs->total_sectors = bdrv_getlength(bs) >> SECTOR_BITS;
}
#ifndef _WIN32
if (bs->is_temporary) {
unlink(filename);
}
#endif
if (bs->backing_file[0] != '\0') {
/* if there is a backing file, use it */
bs->backing_hd = bdrv_new("");
if (!bs->backing_hd) {
fail:
bdrv_close(bs);
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: return -ENOMEM\n",
__FUNCTION__, __LINE__);
#endif
return -ENOMEM;
}
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: combine '%s' '%s'\n",
__FUNCTION__, __LINE__, filename, bs->backing_file);
#endif
path_combine(backing_filename, sizeof(backing_filename),
filename, bs->backing_file);
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: combine: '%s'\n",
__FUNCTION__, __LINE__, backing_filename);
#endif
if (bdrv_open(bs->backing_hd, backing_filename, 0) < 0) {
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: backing fail\n",
__FUNCTION__, __LINE__);
#endif
goto fail;
}
}
/* call the change callback */
bs->media_changed = 1;
if (bs->change_cb)
bs->change_cb(bs->change_opaque);
#ifdef DEBUG_X49GP_BLOCK
fprintf(stderr, "%s:%u: return 0\n", __FUNCTION__, __LINE__);
#endif
return 0;
}
static void
bdrv_close(BlockDriverState *bs)
{
if (NULL == bs->drv)
return;
/* call the change callback */
bs->media_changed = 1;
if (bs->change_cb)
bs->change_cb(bs->change_opaque);
if (bs->backing_hd)
bdrv_delete(bs->backing_hd);
bs->drv->bdrv_close(bs);
#ifdef _WIN32
if (bs->is_temporary) {
unlink(bs->filename);
}
#endif
qemu_free(bs->opaque);
bs->opaque = NULL;
bs->drv = NULL;
}
void
bdrv_delete(BlockDriverState *bs)
{
/* XXX: remove the driver list */
bdrv_close(bs);
qemu_free(bs);
}
/* return < 0 if error. See bdrv_write() for the return codes */
int
bdrv_read(BlockDriverState * bs, int64_t sector_num,
uint8_t * buf, int nb_sectors)
{
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOMEDIUM;
if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) {
memcpy(buf, bs->boot_sector_data, 512);
sector_num++;
nb_sectors--;
buf += 512;
if (nb_sectors == 0)
return 0;
}
if (drv->bdrv_pread) {
int ret, len;
len = nb_sectors * 512;
ret = drv->bdrv_pread(bs, sector_num * 512, buf, len);
if (ret < 0)
return ret;
else if (ret != len)
return -EINVAL;
else
return 0;
} else {
return drv->bdrv_read(bs, sector_num, buf, nb_sectors);
}
}
/* Return < 0 if error. Important errors are:
-EIO generic I/O error (may happen for all errors)
-ENOMEDIUM No media inserted.
-EINVAL Invalid sector number or nb_sectors
-EACCES Trying to write a read-only device
*/
static int
bdrv_write(BlockDriverState * bs, int64_t sector_num,
const uint8_t * buf, int nb_sectors)
{
BlockDriver *drv = bs->drv;
if (!bs->drv)
return -ENOMEDIUM;
if (bs->read_only)
return -EACCES;
if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) {
memcpy(bs->boot_sector_data, buf, 512);
}
if (drv->bdrv_pwrite) {
int ret, len;
len = nb_sectors * 512;
ret = drv->bdrv_pwrite(bs, sector_num * 512, buf, len);
if (ret < 0)
return ret;
else if (ret != len)
return -EIO;
else
return 0;
} else {
return drv->bdrv_write(bs, sector_num, buf, nb_sectors);
}
}
static int
bdrv_pread_em(BlockDriverState * bs, int64_t offset, uint8_t * buf, int count1)
{
uint8_t tmp_buf[SECTOR_SIZE];
int len, nb_sectors, count;
int64_t sector_num;
count = count1;
/* first read to align to sector start */
len = (SECTOR_SIZE - offset) & (SECTOR_SIZE - 1);
if (len > count)
len = count;
sector_num = offset >> SECTOR_BITS;
if (len > 0) {
if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0)
return -EIO;
memcpy(buf, tmp_buf + (offset & (SECTOR_SIZE - 1)), len);
count -= len;
if (count == 0)
return count1;
sector_num++;
buf += len;
}
/* read the sectors "in place" */
nb_sectors = count >> SECTOR_BITS;
if (nb_sectors > 0) {
if (bdrv_read(bs, sector_num, buf, nb_sectors) < 0)
return -EIO;
sector_num += nb_sectors;
len = nb_sectors << SECTOR_BITS;
buf += len;
count -= len;
}
/* add data from the last sector */
if (count > 0) {
if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0)
return -EIO;
memcpy(buf, tmp_buf, count);
}
return count1;
}
static int
bdrv_pwrite_em(BlockDriverState * bs, int64_t offset,
const uint8_t * buf, int count1)
{
uint8_t tmp_buf[SECTOR_SIZE];
int len, nb_sectors, count;
int64_t sector_num;
count = count1;
/* first write to align to sector start */
len = (SECTOR_SIZE - offset) & (SECTOR_SIZE - 1);
if (len > count)
len = count;
sector_num = offset >> SECTOR_BITS;
if (len > 0) {
if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0)
return -EIO;
memcpy(tmp_buf + (offset & (SECTOR_SIZE - 1)), buf, len);
if (bdrv_write(bs, sector_num, tmp_buf, 1) < 0)
return -EIO;
count -= len;
if (count == 0)
return count1;
sector_num++;
buf += len;
}
/* write the sectors "in place" */
nb_sectors = count >> SECTOR_BITS;
if (nb_sectors > 0) {
if (bdrv_write(bs, sector_num, buf, nb_sectors) < 0)
return -EIO;
sector_num += nb_sectors;
len = nb_sectors << SECTOR_BITS;
buf += len;
count -= len;
}
/* add data from the last sector */
if (count > 0) {
if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0)
return -EIO;
memcpy(tmp_buf, buf, count);
if (bdrv_write(bs, sector_num, tmp_buf, 1) < 0)
return -EIO;
}
return count1;
}
/**
* Read with byte offsets (needed only for file protocols)
*/
int
bdrv_pread(BlockDriverState * bs, int64_t offset, void *buf1, int count1)
{
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOMEDIUM;
if (!drv->bdrv_pread)
return bdrv_pread_em(bs, offset, buf1, count1);
return drv->bdrv_pread(bs, offset, buf1, count1);
}
/**
* Write with byte offsets (needed only for file protocols)
*/
int
bdrv_pwrite(BlockDriverState * bs, int64_t offset, const void *buf1, int count1)
{
BlockDriver *drv = bs->drv;
if (!drv)
return -ENOMEDIUM;
if (!drv->bdrv_pwrite)
return bdrv_pwrite_em(bs, offset, buf1, count1);
return drv->bdrv_pwrite(bs, offset, buf1, count1);
}
void
bdrv_flush(BlockDriverState *bs)
{
if (bs->drv->bdrv_flush)
bs->drv->bdrv_flush(bs);
if (bs->backing_hd)
bdrv_flush(bs->backing_hd);
}
void
bdrv_init(void)
{
/* bdrv_register(&bdrv_raw); */
/* bdrv_register(&bdrv_host_device); */
bdrv_register(&bdrv_qcow);
bdrv_register(&bdrv_vvfat);
}