2024-03-11 01:49:58 +06:00
|
|
|
|
2024-03-12 16:04:29 +06:00
|
|
|
/*
|
|
|
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
|
|
|
* running old operating systems and software designed for IBM
|
|
|
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
|
|
|
* system designs based on the PCI bus.
|
|
|
|
|
*
|
|
|
|
|
* This file is part of the 86Box distribution.
|
|
|
|
|
*
|
|
|
|
|
* Hayes AT-compliant modem emulation.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* Authors: Cacodemon345
|
|
|
|
|
* The DOSBox Team
|
|
|
|
|
*
|
|
|
|
|
* Copyright 2024 Cacodemon345
|
|
|
|
|
* Copyright 2002-2021 The DOSBox Team
|
|
|
|
|
*/
|
2024-03-11 01:49:58 +06:00
|
|
|
|
2024-03-08 16:45:17 +06:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <wchar.h>
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
#define HAVE_STDARG_H
|
|
|
|
|
#include <86box/86box.h>
|
|
|
|
|
#include <86box/device.h>
|
|
|
|
|
#include <86box/thread.h>
|
|
|
|
|
#include <86box/fifo.h>
|
|
|
|
|
#include <86box/fifo8.h>
|
|
|
|
|
#include <86box/timer.h>
|
|
|
|
|
#include <86box/serial.h>
|
2024-03-11 01:49:58 +06:00
|
|
|
#include <86box/plat.h>
|
2024-03-08 16:45:17 +06:00
|
|
|
#include <86box/network.h>
|
|
|
|
|
#include <86box/plat_unused.h>
|
2024-03-12 14:27:19 +06:00
|
|
|
#include <86box/plat_netsocket.h>
|
2024-03-08 16:45:17 +06:00
|
|
|
|
|
|
|
|
/* From RFC 1055. */
|
|
|
|
|
#define END 0300 /* indicates end of packet */
|
|
|
|
|
#define ESC 0333 /* indicates byte stuffing */
|
|
|
|
|
#define ESC_END 0334 /* ESC ESC_END means END data byte */
|
|
|
|
|
#define ESC_ESC 0335 /* ESC ESC_ESC means ESC data byte */
|
|
|
|
|
|
|
|
|
|
typedef enum ResTypes {
|
|
|
|
|
ResNONE,
|
|
|
|
|
ResOK,
|
|
|
|
|
ResERROR,
|
|
|
|
|
ResCONNECT,
|
|
|
|
|
ResRING,
|
|
|
|
|
ResBUSY,
|
|
|
|
|
ResNODIALTONE,
|
|
|
|
|
ResNOCARRIER,
|
|
|
|
|
ResNOANSWER
|
|
|
|
|
} ResTypes;
|
|
|
|
|
|
|
|
|
|
enum modem_types
|
|
|
|
|
{
|
|
|
|
|
MODEM_TYPE_SLIP = 1,
|
|
|
|
|
MODEM_TYPE_PPP = 2,
|
|
|
|
|
MODEM_TYPE_TCPIP = 3
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef enum modem_mode_t
|
|
|
|
|
{
|
|
|
|
|
MODEM_MODE_COMMAND = 0,
|
|
|
|
|
MODEM_MODE_DATA = 1
|
|
|
|
|
} modem_mode_t;
|
|
|
|
|
|
2024-03-11 01:49:58 +06:00
|
|
|
typedef enum modem_slip_stage_t
|
|
|
|
|
{
|
|
|
|
|
MODEM_SLIP_STAGE_USERNAME,
|
|
|
|
|
MODEM_SLIP_STAGE_PASSWORD
|
|
|
|
|
} modem_slip_stage_t;
|
|
|
|
|
|
|
|
|
|
typedef struct modem_phonebook_entry_t
|
|
|
|
|
{
|
|
|
|
|
char phone[1024];
|
|
|
|
|
char address[1024];
|
|
|
|
|
} modem_phonebook_entry_t;
|
|
|
|
|
|
2024-03-08 16:45:17 +06:00
|
|
|
typedef struct modem_t
|
|
|
|
|
{
|
|
|
|
|
uint8_t mac[6];
|
|
|
|
|
serial_t *serial;
|
2024-03-09 01:37:13 +06:00
|
|
|
uint32_t baudrate;
|
2024-03-08 16:45:17 +06:00
|
|
|
|
|
|
|
|
modem_mode_t mode;
|
|
|
|
|
|
|
|
|
|
uint8_t esc_character_expected;
|
|
|
|
|
pc_timer_t host_to_serial_timer;
|
|
|
|
|
pc_timer_t dtr_timer;
|
2024-03-09 01:37:13 +06:00
|
|
|
pc_timer_t cmdpause_timer;
|
2024-03-08 16:45:17 +06:00
|
|
|
|
|
|
|
|
uint8_t tx_pkt_ser_line[0x10000]; /* SLIP-encoded. */
|
|
|
|
|
uint32_t tx_count;
|
|
|
|
|
|
|
|
|
|
Fifo8 rx_data; /* Data received from the network. */
|
|
|
|
|
uint8_t reg[100];
|
|
|
|
|
|
|
|
|
|
Fifo8 data_pending; /* Data yet to be sent to the host. */
|
|
|
|
|
|
|
|
|
|
char cmdbuf[512];
|
|
|
|
|
uint32_t cmdpos;
|
2024-03-09 01:37:13 +06:00
|
|
|
uint32_t port;
|
2024-03-08 16:45:17 +06:00
|
|
|
int plusinc, flowcontrol;
|
2024-03-09 01:37:13 +06:00
|
|
|
int in_warmup, dtrmode;
|
2024-03-08 16:45:17 +06:00
|
|
|
|
|
|
|
|
bool connected, ringing;
|
|
|
|
|
bool echo, numericresponse;
|
2024-03-12 14:27:19 +06:00
|
|
|
bool tcpIpMode, tcpIpConnInProgress;
|
|
|
|
|
uint32_t tcpIpConnCounter;
|
2024-03-08 16:45:17 +06:00
|
|
|
|
|
|
|
|
int doresponse;
|
2024-03-09 01:37:13 +06:00
|
|
|
int cmdpause;
|
2024-03-12 14:27:19 +06:00
|
|
|
int listen_port;
|
|
|
|
|
int ringtimer;
|
|
|
|
|
|
|
|
|
|
SOCKET serversocket;
|
|
|
|
|
SOCKET clientsocket;
|
|
|
|
|
SOCKET waitingclientsocket;
|
2024-03-09 01:37:13 +06:00
|
|
|
|
|
|
|
|
struct {
|
|
|
|
|
bool binary[2];
|
|
|
|
|
bool echo[2];
|
|
|
|
|
bool supressGA[2];
|
|
|
|
|
bool timingMark[2];
|
|
|
|
|
bool inIAC;
|
|
|
|
|
bool recCommand;
|
|
|
|
|
uint8_t command;
|
|
|
|
|
} telClient;
|
2024-03-11 01:49:58 +06:00
|
|
|
|
|
|
|
|
modem_phonebook_entry_t entries[20];
|
|
|
|
|
uint32_t entries_num;
|
2024-03-08 16:45:17 +06:00
|
|
|
|
|
|
|
|
netcard_t *card;
|
|
|
|
|
} modem_t;
|
|
|
|
|
|
|
|
|
|
static modem_t *instance;
|
|
|
|
|
|
|
|
|
|
#define MREG_AUTOANSWER_COUNT 0
|
|
|
|
|
#define MREG_RING_COUNT 1
|
|
|
|
|
#define MREG_ESCAPE_CHAR 2
|
|
|
|
|
#define MREG_CR_CHAR 3
|
|
|
|
|
#define MREG_LF_CHAR 4
|
|
|
|
|
#define MREG_BACKSPACE_CHAR 5
|
|
|
|
|
#define MREG_GUARD_TIME 12
|
|
|
|
|
#define MREG_DTR_DELAY 25
|
|
|
|
|
|
2024-03-09 01:37:13 +06:00
|
|
|
static void modem_do_command(modem_t* modem);
|
|
|
|
|
|
2024-03-11 01:49:58 +06:00
|
|
|
extern ssize_t local_getline(char **buf, size_t *bufsiz, FILE *fp);
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
modem_read_phonebook_file(modem_t* modem, const char* path)
|
|
|
|
|
{
|
|
|
|
|
FILE* file = plat_fopen(path, "r");
|
|
|
|
|
char* buf = NULL;
|
|
|
|
|
size_t size = 0;
|
|
|
|
|
if (!file)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
modem->entries_num = 0;
|
|
|
|
|
|
|
|
|
|
while (local_getline(&buf, &size, file) != -1) {
|
|
|
|
|
modem_phonebook_entry_t entry = { { 0 }, { 0 } };
|
|
|
|
|
int res = 0;
|
|
|
|
|
buf[strcspn(buf, "\r\n")] = 0;
|
|
|
|
|
|
|
|
|
|
res = sscanf(buf, "%s %s", entry.phone, entry.address);
|
|
|
|
|
|
|
|
|
|
if (res == 0 || res == 1) {
|
|
|
|
|
/* Appears to be a bad line. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strspn(entry.phone, "01234567890*=,;#+>") != strlen(entry.phone)) {
|
|
|
|
|
/* Invalid characters. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
modem->entries[modem->entries_num++] = entry;
|
|
|
|
|
if (modem->entries_num >= 20)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
fclose(file);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-09 01:37:13 +06:00
|
|
|
static void
|
|
|
|
|
modem_echo(modem_t* modem, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
if (modem->echo && fifo8_num_free(&modem->data_pending)) fifo8_push(&modem->data_pending, c);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-08 16:45:17 +06:00
|
|
|
static uint32_t
|
|
|
|
|
modem_scan_number(char **scan)
|
|
|
|
|
{
|
|
|
|
|
char c = 0;
|
|
|
|
|
uint32_t ret = 0;
|
|
|
|
|
while (1) {
|
|
|
|
|
c = **scan;
|
|
|
|
|
if (c == 0)
|
|
|
|
|
break;
|
|
|
|
|
if (c >= '0' && c <= '9') {
|
|
|
|
|
ret*=10;
|
|
|
|
|
ret+=c-'0';
|
|
|
|
|
*scan = *scan + 1;
|
|
|
|
|
} else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t
|
|
|
|
|
modem_fetch_character(char **scan)
|
|
|
|
|
{
|
|
|
|
|
uint8_t c = **scan;
|
|
|
|
|
*scan = *scan + 1;
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
modem_speed_changed(void *priv)
|
|
|
|
|
{
|
|
|
|
|
modem_t *dev = (modem_t *) priv;
|
|
|
|
|
if (!dev)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
timer_stop(&dev->host_to_serial_timer);
|
|
|
|
|
/* FIXME: do something to dev->baudrate */
|
2024-03-09 01:37:13 +06:00
|
|
|
timer_on_auto(&dev->host_to_serial_timer, (1000000.0 / (double)dev->baudrate) * 9);
|
2024-03-08 16:45:17 +06:00
|
|
|
#if 0
|
|
|
|
|
serial_clear_fifo(dev->serial);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
modem_send_line(modem_t* modem, const char* line)
|
|
|
|
|
{
|
|
|
|
|
fifo8_push(&modem->data_pending, modem->reg[MREG_CR_CHAR]);
|
|
|
|
|
fifo8_push(&modem->data_pending, modem->reg[MREG_LF_CHAR]);
|
|
|
|
|
fifo8_push_all(&modem->data_pending, (uint8_t*)line, strlen(line));
|
|
|
|
|
fifo8_push(&modem->data_pending, modem->reg[MREG_CR_CHAR]);
|
|
|
|
|
fifo8_push(&modem->data_pending, modem->reg[MREG_LF_CHAR]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
modem_send_number(modem_t* modem, uint32_t val)
|
|
|
|
|
{
|
|
|
|
|
fifo8_push(&modem->data_pending, modem->reg[MREG_CR_CHAR]);
|
|
|
|
|
fifo8_push(&modem->data_pending, modem->reg[MREG_LF_CHAR]);
|
|
|
|
|
|
|
|
|
|
fifo8_push(&modem->data_pending, val / 100 + '0');
|
|
|
|
|
val = val%100;
|
|
|
|
|
fifo8_push(&modem->data_pending, val / 10 + '0');
|
|
|
|
|
val = val%10;
|
|
|
|
|
fifo8_push(&modem->data_pending, val + '0');
|
|
|
|
|
|
|
|
|
|
fifo8_push(&modem->data_pending, modem->reg[MREG_CR_CHAR]);
|
|
|
|
|
fifo8_push(&modem->data_pending, modem->reg[MREG_LF_CHAR]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
process_tx_packet(modem_t *modem, uint8_t *p, uint32_t len)
|
|
|
|
|
{
|
|
|
|
|
int received = 0;
|
|
|
|
|
uint32_t pos = 0;
|
|
|
|
|
uint8_t *processed_tx_packet = calloc(len, 1);
|
|
|
|
|
uint8_t c = 0;
|
|
|
|
|
|
2024-03-11 01:49:58 +06:00
|
|
|
pclog("Processing SLIP packet of %u bytes\n", len);
|
|
|
|
|
|
2024-03-08 16:45:17 +06:00
|
|
|
while (pos < len) {
|
|
|
|
|
c = p[pos];
|
|
|
|
|
pos++;
|
|
|
|
|
switch (c) {
|
|
|
|
|
case END:
|
|
|
|
|
if (received)
|
|
|
|
|
goto send_tx_packet;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ESC:
|
|
|
|
|
{
|
|
|
|
|
c = p[pos];
|
|
|
|
|
pos++;
|
|
|
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
|
case ESC_END:
|
|
|
|
|
c = END;
|
|
|
|
|
break;
|
|
|
|
|
case ESC_ESC:
|
|
|
|
|
c = ESC;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
if (received < len)
|
|
|
|
|
processed_tx_packet[received++] = c;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
send_tx_packet:
|
2024-03-11 01:49:58 +06:00
|
|
|
if (received)
|
|
|
|
|
{
|
|
|
|
|
uint8_t* buf = calloc(received + 14, 1);
|
|
|
|
|
buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = 0xFF;
|
|
|
|
|
buf[6] = buf[7] = buf[8] = buf[9] = buf[10] = buf[11] = 0xFC;
|
|
|
|
|
buf[12] = 0x08;
|
|
|
|
|
buf[13] = 0x00;
|
|
|
|
|
memcpy(buf + 14, processed_tx_packet, received);
|
|
|
|
|
network_tx(modem->card, buf, received + 14);
|
|
|
|
|
free(processed_tx_packet);
|
|
|
|
|
free(buf);
|
|
|
|
|
}
|
2024-03-08 16:45:17 +06:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
modem_data_mode_process_byte(modem_t* modem, uint8_t data)
|
|
|
|
|
{
|
|
|
|
|
if (modem->reg[MREG_ESCAPE_CHAR] <= 127) {
|
2024-03-09 01:37:13 +06:00
|
|
|
if (modem->plusinc >= 1 && modem->plusinc <= 3 && modem->reg[MREG_ESCAPE_CHAR] == data) {
|
|
|
|
|
modem->plusinc++;
|
|
|
|
|
} else {
|
|
|
|
|
modem->plusinc = 0;
|
2024-03-08 16:45:17 +06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-09 01:37:13 +06:00
|
|
|
if (modem->tx_count < 0x10000 && modem->connected) {
|
2024-03-08 16:45:17 +06:00
|
|
|
modem->tx_pkt_ser_line[modem->tx_count++] = data;
|
2024-03-12 14:27:19 +06:00
|
|
|
if (data == END && !modem->tcpIpMode) {
|
2024-03-09 01:37:13 +06:00
|
|
|
process_tx_packet(modem, modem->tx_pkt_ser_line, (uint32_t)modem->tx_count);
|
|
|
|
|
modem->tx_count = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-08 16:45:17 +06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
host_to_modem_cb(void *priv)
|
|
|
|
|
{
|
|
|
|
|
modem_t* modem = (modem_t*)priv;
|
|
|
|
|
|
2024-03-12 14:27:19 +06:00
|
|
|
if (modem->in_warmup)
|
|
|
|
|
goto no_write_to_machine;
|
|
|
|
|
|
2024-03-08 16:45:17 +06:00
|
|
|
if ((modem->serial->type >= SERIAL_16550) && modem->serial->fifo_enabled) {
|
|
|
|
|
if (fifo_get_full(modem->serial->rcvr_fifo)) {
|
|
|
|
|
goto no_write_to_machine;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (modem->serial->lsr & 1) {
|
|
|
|
|
goto no_write_to_machine;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-09 01:37:13 +06:00
|
|
|
if (!((modem->serial->mctrl & 2) || modem->flowcontrol != 3))
|
|
|
|
|
goto no_write_to_machine;
|
|
|
|
|
|
2024-03-08 16:45:17 +06:00
|
|
|
if (modem->mode == MODEM_MODE_DATA && fifo8_num_used(&modem->rx_data)) {
|
|
|
|
|
serial_write_fifo(modem->serial, fifo8_pop(&modem->rx_data));
|
|
|
|
|
} else if (fifo8_num_used(&modem->data_pending)) {
|
2024-03-09 14:58:12 +06:00
|
|
|
uint8_t val = fifo8_pop(&modem->data_pending);
|
|
|
|
|
serial_write_fifo(modem->serial, val);
|
2024-03-08 16:45:17 +06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
no_write_to_machine:
|
2024-03-09 01:37:13 +06:00
|
|
|
timer_on_auto(&modem->host_to_serial_timer, (1000000.0 / (double)modem->baudrate) * (double)9);
|
2024-03-08 16:45:17 +06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2024-03-09 01:37:13 +06:00
|
|
|
modem_write(UNUSED(serial_t *s), void *priv, uint8_t txval)
|
2024-03-08 16:45:17 +06:00
|
|
|
{
|
|
|
|
|
modem_t* modem = (modem_t*)priv;
|
2024-03-09 01:37:13 +06:00
|
|
|
|
|
|
|
|
if (modem->mode == MODEM_MODE_COMMAND) {
|
|
|
|
|
if (modem->cmdpos < 2) {
|
|
|
|
|
// Ignore everything until we see "AT" sequence.
|
|
|
|
|
if (modem->cmdpos == 0 && toupper(txval) != 'A') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (modem->cmdpos == 1 && toupper(txval) != 'T') {
|
|
|
|
|
modem_echo(modem, modem->reg[MREG_BACKSPACE_CHAR]);
|
|
|
|
|
modem->cmdpos = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Now entering command.
|
|
|
|
|
if (txval == modem->reg[MREG_BACKSPACE_CHAR]) {
|
|
|
|
|
if (modem->cmdpos > 2) {
|
|
|
|
|
modem_echo(modem, txval);
|
|
|
|
|
modem->cmdpos--;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (txval == modem->reg[MREG_LF_CHAR]) {
|
|
|
|
|
return; // Real modem doesn't seem to skip this?
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (txval == modem->reg[MREG_CR_CHAR]) {
|
|
|
|
|
modem_echo(modem, txval);
|
|
|
|
|
modem_do_command(modem);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-03-09 15:49:08 +06:00
|
|
|
}
|
2024-03-09 01:37:13 +06:00
|
|
|
|
2024-03-09 15:49:08 +06:00
|
|
|
if (modem->cmdpos < 99) {
|
|
|
|
|
modem_echo(modem, txval);
|
|
|
|
|
modem->cmdbuf[modem->cmdpos] = txval;
|
|
|
|
|
modem->cmdpos++;
|
2024-03-09 01:37:13 +06:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
modem_data_mode_process_byte(modem, txval);
|
|
|
|
|
}
|
2024-03-08 16:45:17 +06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void modem_send_res(modem_t* modem, const ResTypes response) {
|
2024-03-11 01:49:58 +06:00
|
|
|
char response_str_connect[256] = { 0 };
|
2024-03-08 16:45:17 +06:00
|
|
|
const char* response_str = NULL;
|
|
|
|
|
uint32_t code = -1;
|
2024-03-11 01:49:58 +06:00
|
|
|
|
|
|
|
|
snprintf(response_str_connect, sizeof(response_str_connect), "CONNECT %u", modem->baudrate);
|
|
|
|
|
|
2024-03-08 16:45:17 +06:00
|
|
|
switch (response) {
|
|
|
|
|
case ResOK: code = 0; response_str = "OK"; break;
|
2024-03-11 01:49:58 +06:00
|
|
|
case ResCONNECT: code = 1; response_str = response_str_connect; break;
|
2024-03-08 16:45:17 +06:00
|
|
|
case ResRING: code = 2; response_str = "RING"; break;
|
|
|
|
|
case ResNOCARRIER: code = 3; response_str = "NO CARRIER"; break;
|
|
|
|
|
case ResERROR: code = 4; response_str = "ERROR"; break;
|
|
|
|
|
case ResNODIALTONE: code = 6; response_str = "NO DIALTONE"; break;
|
|
|
|
|
case ResBUSY: code = 7; response_str = "BUSY"; break;
|
|
|
|
|
case ResNOANSWER: code = 8; response_str = "NO ANSWER"; break;
|
|
|
|
|
case ResNONE: return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (modem->doresponse != 1) {
|
|
|
|
|
if (modem->doresponse == 2 && (response == ResRING ||
|
|
|
|
|
response == ResCONNECT || response == ResNOCARRIER)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (modem->numericresponse && code != ~0) {
|
|
|
|
|
modem_send_number(modem, code);
|
|
|
|
|
} else if (response_str != NULL) {
|
|
|
|
|
modem_send_line(modem, response_str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if(CSerial::CanReceiveByte()) // very fast response
|
|
|
|
|
// if(rqueue->inuse() && CSerial::getRTS())
|
|
|
|
|
// { uint8_t rbyte =rqueue->getb();
|
|
|
|
|
// CSerial::receiveByte(rbyte);
|
|
|
|
|
// LOG_MSG("SERIAL: Port %" PRIu8 " modem sending byte %2x back to UART2",
|
|
|
|
|
// GetPortNumber(), rbyte);
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
modem_enter_idle_state(modem_t* modem)
|
|
|
|
|
{
|
|
|
|
|
timer_disable(&modem->dtr_timer);
|
|
|
|
|
modem->connected = false;
|
|
|
|
|
modem->ringing = false;
|
|
|
|
|
modem->mode = MODEM_MODE_COMMAND;
|
|
|
|
|
modem->in_warmup = 0;
|
2024-03-12 14:27:19 +06:00
|
|
|
modem->tcpIpConnInProgress = 0;
|
|
|
|
|
modem->tcpIpConnCounter = 0;
|
|
|
|
|
|
2024-03-12 16:01:00 +06:00
|
|
|
if (modem->waitingclientsocket != (SOCKET)-1)
|
2024-03-12 14:27:19 +06:00
|
|
|
plat_netsocket_close(modem->waitingclientsocket);
|
|
|
|
|
|
2024-03-12 16:01:00 +06:00
|
|
|
if (modem->clientsocket != (SOCKET)-1)
|
2024-03-12 14:27:19 +06:00
|
|
|
plat_netsocket_close(modem->clientsocket);
|
|
|
|
|
|
2024-03-12 16:01:00 +06:00
|
|
|
modem->clientsocket = modem->waitingclientsocket = (SOCKET)-1;
|
|
|
|
|
if (modem->serversocket != (SOCKET)-1) {
|
2024-03-12 14:27:19 +06:00
|
|
|
modem->waitingclientsocket = plat_netsocket_accept(modem->serversocket);
|
2024-03-12 16:01:00 +06:00
|
|
|
while (modem->waitingclientsocket != (SOCKET)-1) {
|
2024-03-12 14:27:19 +06:00
|
|
|
plat_netsocket_close(modem->waitingclientsocket);
|
|
|
|
|
modem->waitingclientsocket = plat_netsocket_accept(modem->serversocket);
|
|
|
|
|
}
|
|
|
|
|
plat_netsocket_close(modem->serversocket);
|
2024-03-12 16:01:00 +06:00
|
|
|
modem->serversocket = (SOCKET)-1;
|
2024-03-12 14:27:19 +06:00
|
|
|
}
|
|
|
|
|
|
2024-03-12 16:01:00 +06:00
|
|
|
if (modem->waitingclientsocket != (SOCKET)-1)
|
2024-03-12 14:27:19 +06:00
|
|
|
plat_netsocket_close(modem->waitingclientsocket);
|
|
|
|
|
|
2024-03-12 16:01:00 +06:00
|
|
|
modem->waitingclientsocket = (SOCKET)-1;
|
2024-03-12 14:27:19 +06:00
|
|
|
modem->tcpIpMode = false;
|
|
|
|
|
modem->tcpIpConnInProgress = false;
|
|
|
|
|
|
|
|
|
|
if (modem->listen_port) {
|
|
|
|
|
modem->serversocket = plat_netsocket_create_server(NET_SOCKET_TCP, modem->listen_port);
|
2024-03-12 16:01:00 +06:00
|
|
|
if (modem->serversocket == (SOCKET)-1) {
|
|
|
|
|
pclog("Failed to set up server on port %d\n", modem->listen_port);
|
|
|
|
|
}
|
2024-03-12 14:27:19 +06:00
|
|
|
}
|
|
|
|
|
|
2024-03-08 16:45:17 +06:00
|
|
|
serial_set_cts(modem->serial, 1);
|
|
|
|
|
serial_set_dsr(modem->serial, 1);
|
|
|
|
|
serial_set_dcd(modem->serial, 0);
|
|
|
|
|
serial_set_ri(modem->serial, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
modem_enter_connected_state(modem_t* modem)
|
|
|
|
|
{
|
|
|
|
|
modem_send_res(modem, ResCONNECT);
|
|
|
|
|
modem->mode = MODEM_MODE_DATA;
|
|
|
|
|
modem->ringing = false;
|
|
|
|
|
modem->connected = true;
|
2024-03-12 14:27:19 +06:00
|
|
|
modem->tcpIpMode = true;
|
|
|
|
|
plat_netsocket_close(modem->serversocket);
|
|
|
|
|
modem->serversocket = -1;
|
2024-03-09 01:37:13 +06:00
|
|
|
memset(&modem->telClient, 0, sizeof(modem->telClient));
|
2024-03-08 16:45:17 +06:00
|
|
|
serial_set_dcd(modem->serial, 1);
|
|
|
|
|
serial_set_ri(modem->serial, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
modem_reset(modem_t* modem)
|
|
|
|
|
{
|
|
|
|
|
modem_enter_idle_state(modem);
|
|
|
|
|
modem->cmdpos = 0;
|
|
|
|
|
modem->cmdbuf[0] = 0;
|
|
|
|
|
modem->flowcontrol = 0;
|
|
|
|
|
modem->plusinc = 0;
|
2024-03-09 01:37:13 +06:00
|
|
|
modem->dtrmode = 2;
|
2024-03-08 16:45:17 +06:00
|
|
|
|
|
|
|
|
memset(&modem->reg,0,sizeof(modem->reg));
|
|
|
|
|
modem->reg[MREG_AUTOANSWER_COUNT] = 0; // no autoanswer
|
|
|
|
|
modem->reg[MREG_RING_COUNT] = 1;
|
|
|
|
|
modem->reg[MREG_ESCAPE_CHAR] = '+';
|
|
|
|
|
modem->reg[MREG_CR_CHAR] = '\r';
|
|
|
|
|
modem->reg[MREG_LF_CHAR] = '\n';
|
|
|
|
|
modem->reg[MREG_BACKSPACE_CHAR] = '\b';
|
|
|
|
|
modem->reg[MREG_GUARD_TIME] = 50;
|
|
|
|
|
modem->reg[MREG_DTR_DELAY] = 5;
|
|
|
|
|
|
|
|
|
|
modem->echo = true;
|
|
|
|
|
modem->doresponse = 0;
|
|
|
|
|
modem->numericresponse = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
modem_dial(modem_t* modem, const char* str)
|
|
|
|
|
{
|
|
|
|
|
/* TODO: Port TCP/IP support from DOSBox. */
|
2024-03-12 14:27:19 +06:00
|
|
|
modem->tcpIpConnCounter = 0;
|
|
|
|
|
modem->tcpIpMode = false;
|
2024-03-11 01:49:58 +06:00
|
|
|
if (!strncmp(str, "0.0.0.0", sizeof("0.0.0.0") - 1))
|
|
|
|
|
{
|
|
|
|
|
pclog("Turning on SLIP\n");
|
2024-03-08 16:45:17 +06:00
|
|
|
modem_enter_connected_state(modem);
|
2024-03-12 14:27:19 +06:00
|
|
|
modem->tcpIpMode = false;
|
2024-03-11 01:49:58 +06:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-03-12 14:27:19 +06:00
|
|
|
char buf[128] = "";
|
|
|
|
|
const char *destination = buf;
|
|
|
|
|
strcpy(buf, str);
|
|
|
|
|
// Scan host for port
|
|
|
|
|
uint16_t port;
|
|
|
|
|
char * hasport = strrchr(buf,':');
|
|
|
|
|
if (hasport) {
|
|
|
|
|
*hasport++ = 0;
|
|
|
|
|
port = (uint16_t)atoi(hasport);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
port = 23;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
modem->clientsocket = plat_netsocket_create(NET_SOCKET_TCP);
|
|
|
|
|
if (modem->clientsocket == -1) {
|
2024-03-12 16:01:00 +06:00
|
|
|
pclog("Failed to create client socket\n");
|
2024-03-12 14:27:19 +06:00
|
|
|
modem_send_res(modem, ResNOCARRIER);
|
|
|
|
|
modem_enter_idle_state(modem);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-12 16:01:00 +06:00
|
|
|
if (-1 == plat_netsocket_connect(modem->clientsocket, buf, port)) {
|
|
|
|
|
pclog("Failed to connect to %s\n", buf);
|
|
|
|
|
modem_send_res(modem, ResNOCARRIER);
|
|
|
|
|
modem_enter_idle_state(modem);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-03-12 14:27:19 +06:00
|
|
|
modem->tcpIpConnInProgress = 1;
|
|
|
|
|
modem->tcpIpConnCounter = 0;
|
2024-03-08 16:45:17 +06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
is_next_token(const char* a, size_t N, const char *b)
|
|
|
|
|
{
|
|
|
|
|
// Is 'b' at least as long as 'a'?
|
|
|
|
|
size_t N_without_null = N - 1;
|
|
|
|
|
if (strnlen(b, N) < N_without_null)
|
|
|
|
|
return false;
|
|
|
|
|
return (strncmp(a, b, N_without_null) == 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://stackoverflow.com/a/122974
|
|
|
|
|
char *trim(char *str)
|
|
|
|
|
{
|
|
|
|
|
size_t len = 0;
|
|
|
|
|
char *frontp = str;
|
|
|
|
|
char *endp = NULL;
|
|
|
|
|
|
|
|
|
|
if( str == NULL ) { return NULL; }
|
|
|
|
|
if( str[0] == '\0' ) { return str; }
|
|
|
|
|
|
|
|
|
|
len = strlen(str);
|
|
|
|
|
endp = str + len;
|
|
|
|
|
|
|
|
|
|
/* Move the front and back pointers to address the first non-whitespace
|
|
|
|
|
* characters from each end.
|
|
|
|
|
*/
|
|
|
|
|
while( isspace((unsigned char) *frontp) ) { ++frontp; }
|
|
|
|
|
if( endp != frontp )
|
|
|
|
|
{
|
|
|
|
|
while( isspace((unsigned char) *(--endp)) && endp != frontp ) {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( frontp != str && endp == frontp )
|
|
|
|
|
*str = '\0';
|
|
|
|
|
else if( str + len - 1 != endp )
|
|
|
|
|
*(endp + 1) = '\0';
|
|
|
|
|
|
|
|
|
|
/* Shift the string so that it starts at str so that if it's dynamically
|
|
|
|
|
* allocated, we can still free it on the returned pointer. Note the reuse
|
|
|
|
|
* of endp to mean the front of the string buffer now.
|
|
|
|
|
*/
|
|
|
|
|
endp = str;
|
|
|
|
|
if( frontp != str )
|
|
|
|
|
{
|
|
|
|
|
while( *frontp ) { *endp++ = *frontp++; }
|
|
|
|
|
*endp = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return str;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-12 14:27:19 +06:00
|
|
|
static const char *modem_get_address_from_phonebook(modem_t* modem, const char *input) {
|
|
|
|
|
int i = 0;
|
|
|
|
|
for (i = 0; i < modem->entries_num; i++) {
|
|
|
|
|
if (strcmp(input, modem->entries[i].phone) == 0)
|
|
|
|
|
return modem->entries[i].address;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-08 16:45:17 +06:00
|
|
|
static void
|
|
|
|
|
modem_do_command(modem_t* modem)
|
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
|
|
|
|
char *scanbuf = NULL;
|
|
|
|
|
modem->cmdbuf[modem->cmdpos] = 0;
|
|
|
|
|
modem->cmdpos = 0;
|
|
|
|
|
for (i = 0; i < sizeof(modem->cmdbuf); i++) {
|
|
|
|
|
modem->cmdbuf[i] = toupper(modem->cmdbuf[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* AT command set interpretation */
|
|
|
|
|
if ((modem->cmdbuf[0] != 'A') || (modem->cmdbuf[1] != 'T')) {
|
|
|
|
|
modem_send_res(modem, ResERROR);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-09 15:49:08 +06:00
|
|
|
pclog("Command received: %s (doresponse = %d)\n", modem->cmdbuf, modem->doresponse);
|
|
|
|
|
|
2024-03-08 16:45:17 +06:00
|
|
|
scanbuf = &modem->cmdbuf[2];
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
char chr = modem_fetch_character(&scanbuf);
|
|
|
|
|
switch (chr) {
|
|
|
|
|
case '+':
|
|
|
|
|
/* None supported yet. */
|
|
|
|
|
modem_send_res(modem, ResERROR);
|
|
|
|
|
return;
|
|
|
|
|
case 'D': { // Dial.
|
|
|
|
|
char buffer[128];
|
|
|
|
|
char obuffer[128];
|
|
|
|
|
char * foundstr = &scanbuf[0];
|
2024-03-12 14:27:19 +06:00
|
|
|
const char *mappedaddr = NULL;
|
2024-03-08 16:45:17 +06:00
|
|
|
size_t i = 0;
|
2024-03-12 14:27:19 +06:00
|
|
|
|
2024-03-08 16:45:17 +06:00
|
|
|
if (*foundstr == 'T' || *foundstr == 'P')
|
|
|
|
|
foundstr++;
|
|
|
|
|
|
|
|
|
|
if ((!foundstr[0]) || (strlen(foundstr) > 253)) {
|
|
|
|
|
modem_send_res(modem, ResERROR);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foundstr = trim(foundstr);
|
2024-03-12 14:27:19 +06:00
|
|
|
|
|
|
|
|
mappedaddr = modem_get_address_from_phonebook(modem, foundstr);
|
|
|
|
|
if (mappedaddr) {
|
|
|
|
|
modem_dial(modem, mappedaddr);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-08 16:45:17 +06:00
|
|
|
if (strlen(foundstr) >= 12) {
|
|
|
|
|
// Check if supplied parameter only consists of digits
|
|
|
|
|
bool isNum = true;
|
|
|
|
|
size_t fl = strlen(foundstr);
|
|
|
|
|
for (i = 0; i < fl; i++)
|
|
|
|
|
if (foundstr[i] < '0' || foundstr[i] > '9')
|
|
|
|
|
isNum = false;
|
|
|
|
|
if (isNum) {
|
|
|
|
|
// Parameter is a number with at least 12 digits => this cannot
|
|
|
|
|
// be a valid IP/name
|
|
|
|
|
// Transform by adding dots
|
|
|
|
|
size_t j = 0;
|
|
|
|
|
const size_t foundlen = strlen(foundstr);
|
|
|
|
|
for (i = 0; i < foundlen; i++) {
|
|
|
|
|
buffer[j++] = foundstr[i];
|
|
|
|
|
// Add a dot after the third, sixth and ninth number
|
|
|
|
|
if (i == 2 || i == 5 || i == 8)
|
|
|
|
|
buffer[j++] = '.';
|
|
|
|
|
// If the string is longer than 12 digits,
|
|
|
|
|
// interpret the rest as port
|
|
|
|
|
if (i == 11 && foundlen > 12)
|
|
|
|
|
buffer[j++] = ':';
|
|
|
|
|
}
|
|
|
|
|
buffer[j] = 0;
|
|
|
|
|
foundstr = buffer;
|
|
|
|
|
|
|
|
|
|
// Remove Zeros from beginning of octets
|
|
|
|
|
size_t k = 0;
|
|
|
|
|
size_t foundlen2 = strlen(foundstr);
|
|
|
|
|
for (i = 0; i < foundlen2; i++) {
|
|
|
|
|
if (i == 0 && foundstr[0] == '0') continue;
|
|
|
|
|
if (i == 1 && foundstr[0] == '0' && foundstr[1] == '0') continue;
|
|
|
|
|
if (foundstr[i] == '0' && foundstr[i-1] == '.') continue;
|
|
|
|
|
if (foundstr[i] == '0' && foundstr[i-1] == '0' && foundstr[i-2] == '.') continue;
|
|
|
|
|
obuffer[k++] = foundstr[i];
|
|
|
|
|
}
|
|
|
|
|
obuffer[k] = 0;
|
|
|
|
|
foundstr = obuffer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
modem_dial(modem, foundstr);
|
2024-03-12 16:01:00 +06:00
|
|
|
break;
|
2024-03-08 16:45:17 +06:00
|
|
|
}
|
|
|
|
|
case 'I': // Some strings about firmware
|
|
|
|
|
switch (modem_scan_number(&scanbuf)) {
|
|
|
|
|
case 3: modem_send_line(modem, "86Box Emulated Modem Firmware V1.00"); break;
|
|
|
|
|
case 4: modem_send_line(modem, "Modem compiled for 86Box"); break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'E': // Echo on/off
|
|
|
|
|
switch (modem_scan_number(&scanbuf)) {
|
|
|
|
|
case 0: modem->echo = false; break;
|
|
|
|
|
case 1: modem->echo = true; break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'V':
|
|
|
|
|
switch (modem_scan_number(&scanbuf)) {
|
|
|
|
|
case 0: modem->numericresponse = true; break;
|
|
|
|
|
case 1: modem->numericresponse = false; break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'H': // Hang up
|
|
|
|
|
switch (modem_scan_number(&scanbuf)) {
|
|
|
|
|
case 0:
|
|
|
|
|
if (modem->connected) {
|
|
|
|
|
modem_send_res(modem, ResNOCARRIER);
|
|
|
|
|
modem_enter_idle_state(modem);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// else return ok
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'O': // Return to data mode
|
|
|
|
|
switch (modem_scan_number(&scanbuf)) {
|
|
|
|
|
case 0:
|
|
|
|
|
if (modem->connected) {
|
|
|
|
|
modem->mode = MODEM_MODE_DATA;
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
|
|
|
|
modem_send_res(modem, ResERROR);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'T': // Tone Dial
|
|
|
|
|
case 'P': // Pulse Dial
|
|
|
|
|
break;
|
|
|
|
|
case 'M': // Monitor
|
|
|
|
|
case 'L': // Volume
|
|
|
|
|
modem_scan_number(&scanbuf);
|
|
|
|
|
break;
|
|
|
|
|
case 'A': // Answer call
|
|
|
|
|
{
|
|
|
|
|
modem_send_res(modem, ResERROR);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
case 'Z': { // Reset and load profiles
|
|
|
|
|
// scan the number away, if any
|
|
|
|
|
modem_scan_number(&scanbuf);
|
|
|
|
|
if (modem->connected)
|
|
|
|
|
modem_send_res(modem, ResNOCARRIER);
|
|
|
|
|
modem_reset(modem);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case ' ': // skip space
|
|
|
|
|
break;
|
|
|
|
|
case 'Q': {
|
|
|
|
|
// Response options
|
|
|
|
|
// 0 = all on, 1 = all off,
|
|
|
|
|
// 2 = no ring and no connect/carrier in answermode
|
|
|
|
|
const uint32_t val = modem_scan_number(&scanbuf);
|
|
|
|
|
if (!(val > 2)) {
|
|
|
|
|
modem->doresponse = val;
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
modem_send_res(modem, ResERROR);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-09 01:37:13 +06:00
|
|
|
|
|
|
|
|
case 'S': { // Registers
|
|
|
|
|
const uint32_t index = modem_scan_number(&scanbuf);
|
|
|
|
|
if (index >= 100) {
|
|
|
|
|
modem_send_res(modem, ResERROR);
|
|
|
|
|
return; //goto ret_none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (scanbuf[0] == ' ')
|
|
|
|
|
scanbuf++; // skip spaces
|
|
|
|
|
|
|
|
|
|
if (scanbuf[0] == '=') { // set register
|
|
|
|
|
scanbuf++;
|
|
|
|
|
while (scanbuf[0] == ' ')
|
|
|
|
|
scanbuf++; // skip spaces
|
|
|
|
|
const uint32_t val = modem_scan_number(&scanbuf);
|
|
|
|
|
modem->reg[index] = val;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (scanbuf[0] == '?') { // get register
|
|
|
|
|
modem_send_number(modem, modem->reg[index]);
|
|
|
|
|
scanbuf++;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// else
|
|
|
|
|
// LOG_MSG("SERIAL: Port %" PRIu8 " print reg %" PRIu32
|
|
|
|
|
// " with %" PRIu8 ".",
|
|
|
|
|
// GetPortNumber(), index, reg[index]);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case '&': { // & escaped commands
|
|
|
|
|
char cmdchar = modem_fetch_character(&scanbuf);
|
|
|
|
|
switch(cmdchar) {
|
|
|
|
|
case 'K': {
|
|
|
|
|
const uint32_t val = modem_scan_number(&scanbuf);
|
|
|
|
|
if (val < 5)
|
|
|
|
|
modem->flowcontrol = val;
|
|
|
|
|
else {
|
|
|
|
|
modem_send_res(modem, ResERROR);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'D': {
|
|
|
|
|
const uint32_t val = modem_scan_number(&scanbuf);
|
|
|
|
|
if (val < 4)
|
|
|
|
|
modem->dtrmode = val;
|
|
|
|
|
else {
|
|
|
|
|
modem_send_res(modem, ResERROR);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case '\0':
|
|
|
|
|
// end of string
|
|
|
|
|
modem_send_res(modem, ResERROR);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case '\\': { // \ escaped commands
|
|
|
|
|
char cmdchar = modem_fetch_character(&scanbuf);
|
|
|
|
|
switch (cmdchar) {
|
|
|
|
|
case 'N':
|
|
|
|
|
// error correction stuff - not emulated
|
|
|
|
|
if (modem_scan_number(&scanbuf) > 5) {
|
|
|
|
|
modem_send_res(modem, ResERROR);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case '\0':
|
|
|
|
|
// end of string
|
|
|
|
|
modem_send_res(modem, ResERROR);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case '\0':
|
|
|
|
|
modem_send_res(modem, ResOK);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
modem_dtr_callback_timer(void* priv)
|
|
|
|
|
{
|
|
|
|
|
modem_t *dev = (modem_t *) priv;
|
|
|
|
|
if (dev->connected) {
|
|
|
|
|
switch (dev->dtrmode) {
|
|
|
|
|
case 1:
|
|
|
|
|
dev->mode = MODEM_MODE_COMMAND;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
modem_send_res(dev, ResNOCARRIER);
|
|
|
|
|
modem_enter_idle_state(dev);
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
modem_send_res(dev, ResNOCARRIER);
|
|
|
|
|
modem_reset(dev);
|
|
|
|
|
break;
|
2024-03-08 16:45:17 +06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
modem_dtr_callback(serial_t* serial, int status, void *priv)
|
|
|
|
|
{
|
|
|
|
|
modem_t *dev = (modem_t *) priv;
|
|
|
|
|
if (status == 1)
|
|
|
|
|
timer_disable(&dev->dtr_timer);
|
|
|
|
|
else if (!timer_is_enabled(&dev->dtr_timer))
|
2024-03-09 01:37:13 +06:00
|
|
|
timer_on_auto(&dev->dtr_timer, 1000000);
|
2024-03-08 16:45:17 +06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
fifo8_resize_2x(Fifo8* fifo)
|
|
|
|
|
{
|
|
|
|
|
uint32_t pos = 0;
|
|
|
|
|
uint32_t size = fifo->capacity * 2;
|
|
|
|
|
uint32_t used = fifo8_num_used(fifo);
|
|
|
|
|
if (!used)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
uint8_t* temp_buf = calloc(fifo->capacity * 2, 1);
|
|
|
|
|
if (!temp_buf) {
|
|
|
|
|
fatal("net_modem: Out Of Memory!\n");
|
|
|
|
|
}
|
|
|
|
|
while (!fifo8_is_empty(fifo)) {
|
|
|
|
|
temp_buf[pos] = fifo8_pop(fifo);
|
|
|
|
|
pos++;
|
|
|
|
|
}
|
|
|
|
|
pos = 0;
|
|
|
|
|
fifo8_destroy(fifo);
|
|
|
|
|
fifo8_create(fifo, size);
|
|
|
|
|
fifo8_push_all(fifo, temp_buf, used);
|
|
|
|
|
free(temp_buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
modem_rx(void *priv, uint8_t *buf, int io_len)
|
|
|
|
|
{
|
2024-03-11 01:49:58 +06:00
|
|
|
#if 1
|
2024-03-08 16:45:17 +06:00
|
|
|
modem_t* modem = (modem_t*)priv;
|
|
|
|
|
uint8_t c = 0;
|
|
|
|
|
uint32_t i = 0;
|
|
|
|
|
|
2024-03-12 14:27:19 +06:00
|
|
|
if (modem->tcpIpMode)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2024-03-09 01:37:13 +06:00
|
|
|
if (!modem->connected) {
|
|
|
|
|
/* Drop packet. */
|
2024-03-11 01:49:58 +06:00
|
|
|
pclog("Dropping %d bytes\n", io_len - 14);
|
2024-03-09 01:37:13 +06:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-12 16:23:35 +06:00
|
|
|
while ((io_len) >= (fifo8_num_free(&modem->rx_data) / 2)) {
|
2024-03-08 16:45:17 +06:00
|
|
|
fifo8_resize_2x(&modem->rx_data);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-11 01:49:58 +06:00
|
|
|
if (!(buf[12] == 0x08 && buf[13] == 0x00)) {
|
|
|
|
|
pclog("Dropping %d bytes (non-IP packet (ethtype 0x%02X%02X))\n", io_len - 14, buf[12], buf[13]);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pclog("Receiving %d bytes\n", io_len - 14);
|
|
|
|
|
/* Strip the Ethernet header. */
|
|
|
|
|
io_len -= 14;
|
|
|
|
|
buf += 14;
|
|
|
|
|
|
2024-03-08 16:45:17 +06:00
|
|
|
fifo8_push(&modem->rx_data, END);
|
|
|
|
|
for (i = 0; i < io_len; i++) {
|
|
|
|
|
switch (buf[i]) {
|
|
|
|
|
case END:
|
|
|
|
|
fifo8_push(&modem->rx_data, ESC);
|
|
|
|
|
fifo8_push(&modem->rx_data, ESC_END);
|
|
|
|
|
break;
|
|
|
|
|
case ESC:
|
|
|
|
|
fifo8_push(&modem->rx_data, ESC);
|
|
|
|
|
fifo8_push(&modem->rx_data, ESC_ESC);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fifo8_push(&modem->rx_data, buf[i]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fifo8_push(&modem->rx_data, END);
|
|
|
|
|
return 1;
|
2024-03-11 01:49:58 +06:00
|
|
|
#endif
|
2024-03-08 16:45:17 +06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
modem_rcr_cb(UNUSED(struct serial_s *serial), void *priv)
|
|
|
|
|
{
|
|
|
|
|
modem_t *dev = (modem_t *) priv;
|
|
|
|
|
|
|
|
|
|
timer_stop(&dev->host_to_serial_timer);
|
|
|
|
|
/* FIXME: do something to dev->baudrate */
|
2024-03-09 01:37:13 +06:00
|
|
|
timer_on_auto(&dev->host_to_serial_timer, (1000000.0 / (double)dev->baudrate) * (double) 9);
|
2024-03-08 16:45:17 +06:00
|
|
|
#if 0
|
|
|
|
|
serial_clear_fifo(dev->serial);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-12 14:27:19 +06:00
|
|
|
static void
|
|
|
|
|
modem_accept_incoming_call(modem_t* modem)
|
|
|
|
|
{
|
|
|
|
|
if (modem->waitingclientsocket != -1) {
|
|
|
|
|
modem->clientsocket = modem->waitingclientsocket;
|
|
|
|
|
modem->waitingclientsocket = -1;
|
|
|
|
|
modem_enter_connected_state(modem);
|
|
|
|
|
modem->in_warmup = 250;
|
|
|
|
|
} else {
|
|
|
|
|
modem_enter_idle_state(modem);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-09 01:37:13 +06:00
|
|
|
static void
|
|
|
|
|
modem_cmdpause_timer_callback(void *priv)
|
|
|
|
|
{
|
2024-03-12 16:01:00 +06:00
|
|
|
modem_t *modem = (modem_t *) priv;
|
2024-03-09 01:37:13 +06:00
|
|
|
uint32_t guard_threashold = 0;
|
2024-03-12 16:01:00 +06:00
|
|
|
timer_on_auto(&modem->cmdpause_timer, 1000);
|
2024-03-09 01:37:13 +06:00
|
|
|
|
2024-03-12 16:01:00 +06:00
|
|
|
if (modem->tcpIpConnInProgress) {
|
|
|
|
|
do {
|
|
|
|
|
int status = plat_netsocket_connected(modem->clientsocket);
|
2024-03-12 14:27:19 +06:00
|
|
|
|
2024-03-12 16:01:00 +06:00
|
|
|
if (status == -1) {
|
|
|
|
|
plat_netsocket_close(modem->clientsocket);
|
|
|
|
|
modem->clientsocket = -1;
|
|
|
|
|
modem_enter_idle_state(modem);
|
|
|
|
|
modem_send_res(modem, ResNOCARRIER);
|
|
|
|
|
modem->tcpIpConnInProgress = 0;
|
|
|
|
|
break;
|
|
|
|
|
} else if (status == 1) {
|
|
|
|
|
modem_enter_connected_state(modem);
|
|
|
|
|
modem->tcpIpConnInProgress = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-03-12 14:27:19 +06:00
|
|
|
|
2024-03-12 16:01:00 +06:00
|
|
|
modem->tcpIpConnCounter++;
|
|
|
|
|
|
|
|
|
|
if (status < 0 || (status == 0 && modem->tcpIpConnCounter >= 5000)) {
|
|
|
|
|
plat_netsocket_close(modem->clientsocket);
|
|
|
|
|
modem->clientsocket = -1;
|
|
|
|
|
modem_enter_idle_state(modem);
|
|
|
|
|
modem_send_res(modem, ResNOANSWER);
|
|
|
|
|
modem->tcpIpConnInProgress = 0;
|
|
|
|
|
modem->tcpIpMode = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} while (0);
|
2024-03-12 14:27:19 +06:00
|
|
|
}
|
|
|
|
|
|
2024-03-12 16:01:00 +06:00
|
|
|
if (!modem->connected && modem->waitingclientsocket == -1 && modem->serversocket != -1) {
|
|
|
|
|
modem->waitingclientsocket = plat_netsocket_accept(modem->serversocket);
|
|
|
|
|
if (modem->waitingclientsocket != -1) {
|
|
|
|
|
if (!(modem->serial->mctrl & 1) && modem->dtrmode != 0) {
|
|
|
|
|
modem_enter_idle_state(modem);
|
2024-03-12 14:27:19 +06:00
|
|
|
} else {
|
2024-03-12 16:01:00 +06:00
|
|
|
modem->ringing = true;
|
|
|
|
|
modem_send_res(modem, ResRING);
|
|
|
|
|
serial_set_ri(modem->serial, !serial_get_ri(modem->serial));
|
|
|
|
|
modem->ringtimer = 3000;
|
|
|
|
|
modem->reg[MREG_RING_COUNT] = 0;
|
2024-03-12 14:27:19 +06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-12 16:01:00 +06:00
|
|
|
if (modem->ringing) {
|
|
|
|
|
if (modem->ringtimer <= 0) {
|
|
|
|
|
modem->reg[MREG_RING_COUNT]++;
|
|
|
|
|
if ((modem->reg[MREG_AUTOANSWER_COUNT] > 0) &&
|
|
|
|
|
(modem->reg[MREG_RING_COUNT] >= modem->reg[MREG_AUTOANSWER_COUNT])) {
|
|
|
|
|
modem_accept_incoming_call(modem);
|
2024-03-12 14:27:19 +06:00
|
|
|
return;
|
|
|
|
|
}
|
2024-03-12 16:01:00 +06:00
|
|
|
modem_send_res(modem, ResRING);
|
|
|
|
|
serial_set_ri(modem->serial, !serial_get_ri(modem->serial));
|
2024-03-12 14:27:19 +06:00
|
|
|
|
2024-03-12 16:01:00 +06:00
|
|
|
modem->ringtimer = 3000;
|
2024-03-12 14:27:19 +06:00
|
|
|
}
|
2024-03-12 16:01:00 +06:00
|
|
|
--modem->ringtimer;
|
2024-03-12 14:27:19 +06:00
|
|
|
}
|
|
|
|
|
|
2024-03-12 16:01:00 +06:00
|
|
|
if (modem->in_warmup) {
|
|
|
|
|
modem->in_warmup--;
|
|
|
|
|
if (modem->in_warmup == 0) {
|
|
|
|
|
modem->tx_count = 0;
|
|
|
|
|
fifo8_reset(&modem->rx_data);
|
2024-03-12 14:27:19 +06:00
|
|
|
}
|
|
|
|
|
}
|
2024-03-12 16:01:00 +06:00
|
|
|
else if (modem->connected && modem->tcpIpMode) {
|
|
|
|
|
if (modem->tx_count) {
|
2024-03-12 14:27:19 +06:00
|
|
|
int wouldblock = 0;
|
2024-03-12 16:01:00 +06:00
|
|
|
int res = plat_netsocket_send(modem->clientsocket, modem->tx_pkt_ser_line, modem->tx_count, &wouldblock);
|
2024-03-12 14:27:19 +06:00
|
|
|
|
|
|
|
|
if (res <= 0 && !wouldblock) {
|
|
|
|
|
/* No bytes sent or error. */
|
2024-03-12 16:01:00 +06:00
|
|
|
modem->tx_count = 0;
|
|
|
|
|
modem_enter_idle_state(modem);
|
|
|
|
|
modem_send_res(modem, ResNOCARRIER);
|
2024-03-12 14:27:19 +06:00
|
|
|
} else if (res > 0) {
|
2024-03-12 16:01:00 +06:00
|
|
|
if (res == modem->tx_count) {
|
|
|
|
|
modem->tx_count = 0;
|
2024-03-12 14:27:19 +06:00
|
|
|
} else {
|
2024-03-12 16:01:00 +06:00
|
|
|
memmove(modem->tx_pkt_ser_line, &modem->tx_pkt_ser_line[res], modem->tx_count - res);
|
|
|
|
|
modem->tx_count -= res;
|
2024-03-12 14:27:19 +06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-03-12 16:01:00 +06:00
|
|
|
if (modem->connected) {
|
2024-03-12 14:27:19 +06:00
|
|
|
uint8_t buffer[16];
|
|
|
|
|
int wouldblock = 0;
|
2024-03-12 16:01:00 +06:00
|
|
|
int res = plat_netsocket_receive(modem->clientsocket, buffer, sizeof(buffer), &wouldblock);
|
2024-03-12 14:27:19 +06:00
|
|
|
|
|
|
|
|
if (res > 0) {
|
2024-03-12 16:01:00 +06:00
|
|
|
fifo8_push_all(&modem->rx_data, buffer, res);
|
2024-03-12 14:27:19 +06:00
|
|
|
} else if (res == 0) {
|
2024-03-12 16:01:00 +06:00
|
|
|
modem->tx_count = 0;
|
|
|
|
|
modem_enter_idle_state(modem);
|
|
|
|
|
modem_send_res(modem, ResNOCARRIER);
|
2024-03-12 14:27:19 +06:00
|
|
|
} else if (!wouldblock) {
|
2024-03-12 16:01:00 +06:00
|
|
|
modem->tx_count = 0;
|
|
|
|
|
modem_enter_idle_state(modem);
|
|
|
|
|
modem_send_res(modem, ResNOCARRIER);
|
2024-03-12 14:27:19 +06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-12 16:01:00 +06:00
|
|
|
modem->cmdpause++;
|
|
|
|
|
guard_threashold = (uint32_t)(modem->reg[MREG_GUARD_TIME] * 20);
|
|
|
|
|
if (modem->cmdpause > guard_threashold) {
|
|
|
|
|
if (modem->plusinc == 0) {
|
|
|
|
|
modem->plusinc = 1;
|
|
|
|
|
} else if (modem->plusinc == 4) {
|
|
|
|
|
modem->mode = MODEM_MODE_COMMAND;
|
|
|
|
|
modem_send_res(modem, ResOK);
|
|
|
|
|
modem->plusinc = 0;
|
2024-03-09 01:37:13 +06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-08 16:45:17 +06:00
|
|
|
/* Initialize the device for use by the user. */
|
|
|
|
|
static void *
|
|
|
|
|
modem_init(const device_t *info)
|
|
|
|
|
{
|
|
|
|
|
modem_t* modem = (modem_t*)calloc(1, sizeof(modem_t));
|
2024-03-12 14:27:19 +06:00
|
|
|
const char* phonebook_file = NULL;
|
2024-03-08 16:45:17 +06:00
|
|
|
memset(modem->mac, 0xfc, 6);
|
|
|
|
|
|
2024-03-09 01:37:13 +06:00
|
|
|
|
|
|
|
|
modem->port = device_get_config_int("port");
|
|
|
|
|
modem->baudrate = device_get_config_int("baudrate");
|
2024-03-12 14:27:19 +06:00
|
|
|
modem->listen_port = device_get_config_int("listen_port");
|
2024-03-09 01:37:13 +06:00
|
|
|
|
2024-03-12 14:29:24 +06:00
|
|
|
modem->clientsocket = modem->serversocket = modem->waitingclientsocket = -1;
|
|
|
|
|
|
2024-03-09 01:37:13 +06:00
|
|
|
fifo8_create(&modem->data_pending, 0x10000);
|
|
|
|
|
fifo8_create(&modem->rx_data, 0x10000);
|
|
|
|
|
|
|
|
|
|
timer_add(&modem->dtr_timer, modem_dtr_callback_timer, modem, 0);
|
|
|
|
|
timer_add(&modem->host_to_serial_timer, host_to_modem_cb, modem, 0);
|
|
|
|
|
timer_add(&modem->cmdpause_timer, modem_cmdpause_timer_callback, modem, 0);
|
|
|
|
|
timer_on_auto(&modem->cmdpause_timer, 1000);
|
|
|
|
|
modem->serial = serial_attach_ex_2(modem->port, modem_rcr_cb, modem_write, modem_dtr_callback, modem);
|
|
|
|
|
|
|
|
|
|
modem_reset(modem);
|
2024-03-09 14:58:12 +06:00
|
|
|
modem->card = network_attach(modem, modem->mac, modem_rx, NULL);
|
2024-03-12 14:27:19 +06:00
|
|
|
|
|
|
|
|
phonebook_file = device_get_config_string("phonebook_file");
|
|
|
|
|
if (phonebook_file && phonebook_file[0] != 0) {
|
|
|
|
|
modem_read_phonebook_file(modem, phonebook_file);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-08 16:45:17 +06:00
|
|
|
return modem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void modem_close(void *priv)
|
|
|
|
|
{
|
2024-03-09 01:37:13 +06:00
|
|
|
modem_t* modem = (modem_t*)priv;
|
2024-03-12 14:27:19 +06:00
|
|
|
modem->listen_port = 0;
|
|
|
|
|
modem_reset(modem);
|
2024-03-09 01:37:13 +06:00
|
|
|
fifo8_destroy(&modem->data_pending);
|
|
|
|
|
fifo8_destroy(&modem->rx_data);
|
|
|
|
|
netcard_close(modem->card);
|
2024-03-08 16:45:17 +06:00
|
|
|
free(priv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const device_config_t modem_config[] = {
|
2024-03-09 01:37:13 +06:00
|
|
|
{
|
|
|
|
|
.name = "port",
|
|
|
|
|
.description = "Serial Port",
|
|
|
|
|
.type = CONFIG_SELECTION,
|
|
|
|
|
.default_string = "",
|
|
|
|
|
.default_int = 0,
|
|
|
|
|
.file_filter = "",
|
|
|
|
|
.spinner = { 0 },
|
|
|
|
|
.selection = {
|
|
|
|
|
{ .description = "COM1", .value = 0 },
|
|
|
|
|
{ .description = "COM2", .value = 1 },
|
|
|
|
|
{ .description = "COM3", .value = 2 },
|
|
|
|
|
{ .description = "COM4", .value = 3 },
|
|
|
|
|
{ .description = "" }
|
|
|
|
|
}
|
|
|
|
|
},
|
2024-03-08 16:45:17 +06:00
|
|
|
{
|
|
|
|
|
.name = "baudrate",
|
|
|
|
|
.description = "Baud Rate",
|
|
|
|
|
.type = CONFIG_SELECTION,
|
|
|
|
|
.default_string = "",
|
|
|
|
|
.default_int = 115200,
|
|
|
|
|
.file_filter = NULL,
|
|
|
|
|
.spinner = { 0 },
|
|
|
|
|
.selection = {
|
|
|
|
|
{ .description = "115200", .value = 115200 },
|
|
|
|
|
{ .description = "57600", .value = 57600 },
|
|
|
|
|
{ .description = "56000", .value = 56000 },
|
|
|
|
|
{ .description = "38400", .value = 38400 },
|
|
|
|
|
{ .description = "19200", .value = 19200 },
|
|
|
|
|
{ .description = "14400", .value = 14400 },
|
|
|
|
|
{ .description = "9600", .value = 9600 },
|
|
|
|
|
{ .description = "7200", .value = 7200 },
|
|
|
|
|
{ .description = "4800", .value = 4800 },
|
|
|
|
|
{ .description = "2400", .value = 2400 },
|
|
|
|
|
{ .description = "1800", .value = 1800 },
|
|
|
|
|
{ .description = "1200", .value = 1200 },
|
|
|
|
|
{ .description = "600", .value = 600 },
|
|
|
|
|
{ .description = "300", .value = 300 },
|
|
|
|
|
}
|
|
|
|
|
},
|
2024-03-12 14:27:19 +06:00
|
|
|
{
|
|
|
|
|
.name = "listen_port",
|
|
|
|
|
.description = "TCP/IP listening port",
|
2024-03-12 16:01:00 +06:00
|
|
|
.type = CONFIG_SPINNER,
|
|
|
|
|
.spinner =
|
|
|
|
|
{
|
|
|
|
|
.min = 0,
|
|
|
|
|
.max = 32767
|
|
|
|
|
},
|
|
|
|
|
.default_int = 0
|
2024-03-12 14:27:19 +06:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
.name = "phonebook_file",
|
|
|
|
|
.description = "Phonebook File",
|
|
|
|
|
.type = CONFIG_FNAME,
|
|
|
|
|
.default_string = "",
|
|
|
|
|
.file_filter = "Text files (*.txt)|*.txt"
|
|
|
|
|
},
|
2024-03-08 16:45:17 +06:00
|
|
|
{ .name = "", .description = "", .type = CONFIG_END }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const device_t modem_device = {
|
|
|
|
|
.name = "Standard Hayes-compliant Modem",
|
2024-03-09 14:58:12 +06:00
|
|
|
.internal_name = "modem",
|
2024-03-09 01:37:13 +06:00
|
|
|
.flags = DEVICE_COM,
|
2024-03-08 16:45:17 +06:00
|
|
|
.local = 0,
|
|
|
|
|
.init = modem_init,
|
|
|
|
|
.close = modem_close,
|
|
|
|
|
.reset = NULL,
|
|
|
|
|
{ .poll = NULL },
|
|
|
|
|
.speed_changed = modem_speed_changed,
|
|
|
|
|
.force_redraw = NULL,
|
|
|
|
|
.config = modem_config
|
|
|
|
|
};
|