1
0
Fork 0

feat: add AmigaOS support

This commit is contained in:
Thomas Touhey 2024-04-19 21:49:17 +02:00
parent 52a8446ffb
commit aaa28a5688
8 changed files with 455 additions and 0 deletions

View File

@ -175,6 +175,20 @@ Link management related function declarations
In case of error, the value of ``*linkp`` mustn't be used nor freed.
The format of the device name or path will vary depending on the platform:
* On Microsoft Windows, it will either be the DOS COM device name (e.g.
``COM3``) or the path to the device.
* On other POSIX-compatible platforms, it will be the path to the device,
usually ``/dev/cu*`` or ``/dev/tty*`` (e.g. ``/dev/ttyUSB0`` for a
serial link over a USB-serial cable);
* On AmigaOS, it will be the serial unit number with the case-insensitive
``U=`` or ``UNIT=`` keyword parameter, e.g. ``unit=0`` for the
built-in serial device.
Available device names can be probed using :c:func:`cahute_detect_serial`,
although the list may be incomplete and other devices may be used.
Since serial links do not offer any metadata, the protocol to use on the
serial link is selected manually, amongst the following:

View File

@ -210,6 +210,18 @@ Available mediums are the following:
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN`;
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN_OHP`.
.. c:macro:: CAHUTE_LINK_MEDIUM_AMIGAOS_SERIAL
Serial medium using AmigaOS serial I/O, as described in the
`AmigaOS Serial Device Guide`_.
Available protocols on this medium are the following:
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_AUTO`;
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_CASIOLINK`;
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN`;
* :c:macro:`CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN_OHP`.
.. c:macro:: CAHUTE_LINK_MEDIUM_WIN32_SERIAL
Serial medium using the Windows API, with a |HANDLE|_ and
@ -679,5 +691,7 @@ of the original function).
https://libusb.sourceforge.io/api-1.0/group__libusb__syncio.html
#ga2f90957ccc1285475ae96ad2ceb1f58c
.. _AmigaOS Serial Device Guide:
https://wiki.amigaos.net/wiki/Serial_Device
.. _USB Mass Storage Class, Bulk-Only Transport:
https://www.usb.org/sites/default/files/usbmassbulk_10.pdf

View File

@ -245,6 +245,21 @@ end:
return err;
#endif
#if AMIGAOS_ENABLED
cahute_serial_detection_entry entry;
/* When opening a serial port on the AmigaOS, we systematically use
* the serial device, but allow selecting the unit number using the
* name. */
entry.cahute_serial_detection_entry_name = "UNIT=0";
if (func(cookie, &entry))
return CAHUTE_ERROR_INT;
/* TODO: How to detect multiple serial devices? */
return CAHUTE_OK;
#endif
CAHUTE_RETURN_IMPL("No serial device detection method available.");
}

View File

@ -50,6 +50,12 @@
# define POSIX_ENABLED 0
#endif
#if defined(AMIGA) || defined(__amigaos__)
# define AMIGAOS_ENABLED 1
#else
# define AMIGAOS_ENABLED 0
#endif
#if POSIX_ENABLED
# include <fcntl.h>
# include <sys/ioctl.h>
@ -62,6 +68,17 @@
# include <libusb.h>
#endif
#if AMIGAOS_ENABLED
# include <exec/io.h>
# include <devices/serial.h>
CAHUTE_EXTERN(int)
cahute_get_amiga_timer(
struct MsgPort **msg_portp,
struct TimeRequest **timerp
);
#endif
/* ---
* Logging internals.
* --- */
@ -197,6 +214,9 @@ cahute__log_win_error(
# define CAHUTE_LINK_MEDIUM_LIBUSB 5
# define CAHUTE_LINK_MEDIUM_LIBUSB_UMS 6
#endif
#if AMIGAOS_ENABLED
# define CAHUTE_LINK_MEDIUM_AMIGAOS_SERIAL 7
#endif
/* Protocol selection for 'initialize_link_protocol()'. */
#define CAHUTE_LINK_PROTOCOL_SERIAL_AUTO 0
@ -266,6 +286,19 @@ struct cahute_link_libusb_medium_state {
};
#endif
#if defined(CAHUTE_LINK_MEDIUM_AMIGAOS_SERIAL)
/**
* AmigaOS serial device medium state.
*
* @property msg_port Message port with which the device was opened.
* @property io IO structure of the device.
*/
struct cahute_link_amigaos_serial_medium_state {
struct MsgPort *msg_port;
struct IOExtSer *io;
};
#endif
/**
* Medium state, to be used depending on the link flags regarding the medium.
*
@ -290,6 +323,9 @@ union cahute_link_medium_state {
#if defined(CAHUTE_LINK_MEDIUM_LIBUSB)
struct cahute_link_libusb_medium_state libusb;
#endif
#if defined(CAHUTE_LINK_MEDIUM_AMIGAOS_SERIAL)
struct cahute_link_amigaos_serial_medium_state amigaos_serial;
#endif
};
/* Absolute minimum buffer size for CASIOLINK. */

View File

@ -959,6 +959,45 @@ fail:
}
#endif
#if AMIGAOS_ENABLED
/**
* Get the AmigaOS serial port from the raw device name.
*
* This function expects a format such as "U=<unit>" or "UNIT=<unit>",
* case-insensitive;
*
* @param raw Raw device name.
* @param unitp Pointer to the unit number to set.
* @return Cahute error, or 0 if successful.
*/
CAHUTE_LOCAL(int) get_amigaos_serial_port(char const *raw, int *unitp) {
int unit;
if (tolower(raw[0]) == 'u' && raw[1] == '=')
raw += 2;
else if (tolower(raw[0]) == 'u' && tolower(raw[1]) == 'n' && tolower(raw[2]) == 'i' && tolower(raw[3]) == 't' && raw[4] == '=')
raw += 5;
else {
/* Unknown port format. */
return CAHUTE_ERROR_NOT_FOUND;
}
if (!isdigit(raw[0]))
return CAHUTE_ERROR_NOT_FOUND;
unit = raw[0] - '0';
while (*++raw) {
if (!isdigit(*raw))
return CAHUTE_ERROR_NOT_FOUND;
unit = unit * 10 + (raw[0] - '0');
}
*unitp = unit;
return CAHUTE_OK;
}
#endif
/**
* Open a link over a serial medium.
*
@ -1231,6 +1270,54 @@ cahute_open_serial_link(
CloseHandle(handle);
return CAHUTE_ERROR_UNKNOWN;
}
#elif defined(CAHUTE_LINK_MEDIUM_AMIGAOS_SERIAL)
struct MsgPort *msg_port;
struct IOExtSer *io;
int ret, unit;
/* The serial device name is the unit number for the serial device. */
ret = get_amigaos_serial_port(name_or_path, &unit);
if (ret)
return ret;
msg_port = IExec->AllocSysObjectTags(ASOT_PORT, TAG_END);
if (!msg_port) {
msg(ll_error, "Could not open message port.");
return CAHUTE_ERROR_UNKNOWN;
}
io = IExec->AllocSysObjectTags(
ASOT_IOREQUEST,
ASOIOR_ReplyPort,
msg_port,
ASOIOR_Size,
sizeof(struct IOExtSer),
TAG_END
);
if (!io) {
msg(ll_error, "Could not create IORequest.");
IExec->FreeSysObject(ASOT_PORT, msg_port);
return CAHUTE_ERROR_UNKNOWN;
}
ret = IExec->OpenDevice(SERIALNAME, unit, (struct IORequest *)io, 0L);
if (ret) {
msg(ll_error,
"Error %d has occurred while opening device \"%s\".",
ret,
name_or_path);
if (ret == IOERR_BADADDRESS)
ret = CAHUTE_ERROR_NOT_FOUND;
else if (ret == IOERR_UNITBUSY)
ret = CAHUTE_ERROR_PRIV;
else
ret = CAHUTE_ERROR_UNKNOWN;
IExec->FreeSysObject(ASOT_IOREQUEST, io);
IExec->FreeSysObject(ASOT_PORT, msg_port);
return ret;
}
#else
CAHUTE_RETURN_IMPL("No serial device opening method available.");
#endif
@ -1266,6 +1353,18 @@ cahute_open_serial_link(
sizeof(OVERLAPPED)
);
link->medium_state.windows.overlapped.hEvent = overlapped_event_handle;
#elif defined(CAHUTE_LINK_MEDIUM_AMIGAOS_SERIAL)
if (err) {
IExec->CloseDevice((struct IORequest *)io);
IExec->FreeSysObject(ASOT_IOREQUEST, io);
IExec->FreeSysObject(ASOT_PORT, msg_port);
return err;
}
link->flags = CAHUTE_LINK_FLAG_CLOSE_MEDIUM;
link->medium = CAHUTE_LINK_MEDIUM_AMIGAOS_SERIAL;
link->medium_state.amigaos_serial.msg_port = msg_port;
link->medium_state.amigaos_serial.io = io;
#endif
link->serial_flags = 0;
@ -1927,6 +2026,22 @@ CAHUTE_EXTERN(void) cahute_close_link(cahute_link *link) {
libusb_exit(link->medium_state.libusb.context);
break;
#endif
#ifdef CAHUTE_LINK_MEDIUM_AMIGAOS_SERIAL
case CAHUTE_LINK_MEDIUM_AMIGAOS_SERIAL:
IExec->CloseDevice(
(struct IORequest *)link->medium_state.amigaos_serial.io
);
IExec->FreeSysObject(
ASOT_IOREQUEST,
link->medium_state.amigaos_serial.io
);
IExec->FreeSysObject(
ASOT_PORT,
link->medium_state.amigaos_serial.msg_port
);
break;
#endif
}
}

View File

@ -478,6 +478,82 @@ cahute_read_from_link(
} break;
#endif
#if defined(CAHUTE_LINK_MEDIUM_AMIGAOS_SERIAL)
case CAHUTE_LINK_MEDIUM_AMIGAOS_SERIAL: {
struct TimeRequest *timer;
struct IOExtSer *io = link->medium_state.amigaos_serial.io;
struct MsgPort *timer_msgport,
*serial_msgport = link->medium_state.amigaos_serial.msg_port;
int err;
err = cahute_get_amiga_timer(&timer_msgport, &timer);
if (err)
return err;
/* Run two operations at once:
* - Read into the buffer.
* - Start a timer to the currently requested timeout. */
io->IOSer.io_Length = target_size;
io->IOSer.io_Data = dest;
io->IOSer.io_Command = CMD_READ;
IExec->SendIO((struct IORequest *)io);
if (IExec->CheckIO((struct IORequest *)io)) {
/* Request is already complete, no need to wait for the
* timer! */
if (io->IOSer.io_Error) {
msg(ll_error,
"Error %d occurred while reading from device.",
io->IOSer.io_Error);
return CAHUTE_ERROR_UNKNOWN;
}
bytes_read = io->IOSer.io_Actual;
break;
}
if (timeout > 0) {
cahute_u32 signals;
/* Request did not complete immediately, we want to start the
* timer and wait for the first event between serial and
* timer. */
timer->Time.tv_secs = timeout / 1000;
timer->Time.tv_micro = timeout % 1000 * 1000;
timer->Request.io_Command = TR_ADDREQUEST;
IExec->SendIO((struct IORequest *)timer);
signals = Wait(
(1L << serial_msgport->mp_SigBit)
| (1L << timer_msgport->mp_SigBit)
);
if (~signals & (1L << timer_msgport->mp_SigBit))
IExec->AbortIO((struct IORequest *)timer);
if (~signals & (1L << serial_msgport->mp_SigBit)) {
IExec->AbortIO((struct IORequest *)io);
goto time_out;
}
} else {
/* Infinite timeout, we can just wait for the I/O to be
* completed on the request. */
IExec->WaitIO((struct IORequest *)io);
}
/* I/O request was completed, we want to read the contents. */
if (io->IOSer.io_Error) {
msg(ll_error,
"Error %d occurred while reading from device.",
io->IOSer.io_Error);
return CAHUTE_ERROR_UNKNOWN;
}
bytes_read = io->IOSer.io_Actual;
} break;
#endif
default:
CAHUTE_RETURN_IMPL("No method available for reading.");
}
@ -750,6 +826,22 @@ cahute_write_to_link(cahute_link *link, cahute_u8 const *buf, size_t size) {
} break;
#endif
#if defined(CAHUTE_LINK_MEDIUM_AMIGAOS_SERIAL)
case CAHUTE_LINK_MEDIUM_AMIGAOS_SERIAL: {
struct IOExtSer *io = link->medium_state.amigaos_serial.io;
io->IOSer.io_Length = size;
io->IOSer.io_Data = buf;
io->IOSer.io_Command = CMD_WRITE;
if (IExec->DoIO((struct IORequest *)io)) {
msg(ll_error, "Unable to set the serial parameters!");
return CAHUTE_ERROR_UNKNOWN;
}
bytes_written = size;
} break;
#endif
default:
CAHUTE_RETURN_IMPL("No method available for writing.");
}
@ -1071,6 +1163,56 @@ cahute_set_serial_params_to_link(
} break;
#endif
#if defined(CAHUTE_LINK_MEDIUM_AMIGAOS_SERIAL)
case CAHUTE_LINK_MEDIUM_AMIGAOS_SERIAL: {
struct IOReq *io = link->medium_state.amigaos_serial.io;
io->io_CtlChar = (0x13 << 24) | (0x11 << 16); /* XON, XOFF, INQ, ACK */
io->io_Baud = speed;
io->io_ReadLen = 8;
io->io_WriteLen = 8;
io->io_SerFlags &=
~(SERF_XDISABLED | SERF_EOFMODE | SERF_RAD_BOOGIE | SERF_QUEUEDBRK
| SERF_7WIRE | SERF_PARTY_ODD | SERF_PARTY_ON);
switch (flags & CAHUTE_SERIAL_XONXOFF_MASK) {
case CAHUTE_SERIAL_XONXOFF_ENABLE:
io->io_SerFlags |= SERF_XDISABLED;
break;
}
switch (flags & CAHUTE_SERIAL_STOP_MASK) {
case CAHUTE_SERIAL_STOP_ONE:
io->io_StopBits = 1;
break;
case CAHUTE_SERIAL_STOP_TWO:
io->io_StopBits = 2;
break;
}
switch (flags & CAHUTE_SERIAL_PARITY_MASK) {
case CAHUTE_SERIAL_PARITY_EVEN:
io->io_SerFlags |= SERF_PARTY_ON;
break;
case CAHUTE_SERIAL_PARITY_ODD:
io->io_SerFlags |= SERF_PARTY_ON | SERF_PARTY_ODD;
break;
}
/* TODO: AmigaOS doesn't manage DTR/RTS directly, which means we
* may have to manage it here manually! */
io->IOSer.io_Command = SDCMD_SETPARAMS;
if (IExec->DoIO((struct IORequest *)io)) {
msg(ll_error, "Unable to set the serial parameters!");
return CAHUTE_ERROR_UNKNOWN;
}
} break;
#endif
default:
CAHUTE_RETURN_IMPL("No method available for setting serial params.");
}

View File

@ -130,6 +130,110 @@ CAHUTE_EXTERN(int) cahute_monotonic(unsigned long *msp) {
return CAHUTE_OK;
}
#elif AMIGAOS_ENABLED
struct cahute_amiga_timer {
struct MsgPort *msg_port;
struct TimeRequest *timer_io;
};
CAHUTE_LOCAL_DATA(struct cahute_amiga_timer) cahute_amiga_timer = {0};
CAHUTE_LOCAL(void) close_amiga_timer() {
IExec->CloseDevice(cahute_amiga_timer.timer_io);
IExec->FreeSysObject(ASOT_IOREQUEST, cahute_amiga_timer.timer_io);
IExec->FreeSysObject(ASOT_PORT, cahute_amiga_timer.msg_port);
}
CAHUTE_EXTERN(int)
cahute_get_amiga_timer(
struct MsgPort **msg_portp,
struct TimeRequest **timerp
) {
struct MsgPort *msg_port;
struct TimeRequest *timer_io;
if (cahute_amiga_vblank_timer.timer_io)
goto end;
msg_port = IExec->AllocSysObjectTags(ASOT_PORT, 0);
if (!msg_port) {
msg(ll_error,
"An error has occurred while creating the port for the timer.");
return CAHUTE_ERROR_UNKNOWN;
}
timer_io = IExec->AllocSysObjectTags(
ASOT_IOREQUEST,
ASOIOR_Size,
sizeof(struct TimeRequest),
ASOIOR_ReplyPort,
msg_port,
TAG_END
);
if (!timer_io) {
msg(ll_error, "An error has occurred while creating the timer I/O.");
IExec->FreeSysObject(ASOT_PORT, msg_port);
return CAHUTE_ERROR_UNKNOWN;
}
ret = IExec->OpenDevice(
TIMERNAME,
UNIT_VBLANK,
(struct IORequest *)timer_io,
0L
);
if (ret) {
msg(ll_error, "An error has occurred while creating the timer I/O.");
IExec->FreeSysObject(ASOT_IOREQUEST, timer_io);
IExec->FreeSysObject(ASOT_PORT, msg_port);
return CAHUTE_ERROR_UNKNOWN;
}
atexit(close_amiga_timer);
cahute_amiga_timer.msg_port = msg_port;
cahute_amiga_timer.timer_io = timer_io;
end:
if (msg_portp)
*msg_portp = cahute_amiga_timer.msg_port;
if (timerp)
*timerp = cahute_amiga_timer.timer_io;
return CAHUTE_OK;
}
CAHUTE_EXTERN(int) cahute_sleep(unsigned long ms) {
struct TimeRequest *timer;
int err;
err = cahute_get_amiga_timer(NULL, &timer);
if (err)
return err;
timer->Time.tv_secs = ms / 1000;
timer->Time.tv_micro = ms % 1000 * 1000;
timer->Request.io_Command = TR_ADDREQUEST;
IExec->DoIO((struct IORequest *)timer);
return CAHUTE_OK;
}
CAHUTE_EXTERN(int) cahute_monotonic(unsigned long *msp) {
struct TimeRequest *timer;
int err;
err = cahute_get_amiga_timer(NULL, &timer);
if (err)
return err;
timer->Request.io_Command = TR_GETSYSTIME;
IExec->DoIO((struct IORequest *)timer);
*msp = timer->Time.tv_secs * 1000 + timer->Time.tv_micro / 1000;
return CAHUTE_OK;
}
#else
CAHUTE_EXTERN(int) cahute_sleep(unsigned long ms) {

15
misc/amigaos-readme.in Normal file
View File

@ -0,0 +1,15 @@
Short: Communication tools for CASIO calculators
Author: Thomas Touhey
Uploader: thomas@touhey.fr (Thomas Touhey)
Type: comm/misc
Architecture: m68k-amigaos
Version: @PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@
Distribution: Aminet
Cahute is a library and set of command-line utilities to handle serial
and USB communication protocols and file formats related to CASIO calculators,
dating from the 1990s to today.
For guides, topics and references, see the following website:
https://cahuteproject.org/