зеркало из https://github.com/github/putty.git
Serial back end for Unix. Due to hardware limitations (no Linux box
I own has both an X display and a working serial port) I have been unable to give this the full testing it deserves; I've managed to demonstrate the basic functionality of Unix Plink talking to a serial port, but I haven't been able to test the GTK front end. I have no reason to think it will fail, but I'll be more comfortable once somebody has actually tested it. [originally from svn r6822]
This commit is contained in:
Родитель
efd7cad3d2
Коммит
8c26b44ce6
14
Recipe
14
Recipe
|
@ -223,7 +223,7 @@ GUITERM = TERMINAL window windlg winctrls sizetip winucs winprint
|
||||||
+ winutils wincfg sercfg
|
+ winutils wincfg sercfg
|
||||||
|
|
||||||
# Same thing on Unix.
|
# Same thing on Unix.
|
||||||
UXTERM = TERMINAL uxcfg uxucs uxprint timing
|
UXTERM = TERMINAL uxcfg sercfg uxucs uxprint timing
|
||||||
GTKTERM = UXTERM gtkwin gtkcfg gtkdlg gtkcols gtkpanel xkeysym
|
GTKTERM = UXTERM gtkwin gtkcfg gtkdlg gtkcols gtkpanel xkeysym
|
||||||
OSXTERM = UXTERM osxwin osxdlg osxctrls
|
OSXTERM = UXTERM osxwin osxdlg osxctrls
|
||||||
|
|
||||||
|
@ -266,6 +266,9 @@ BE_NONE = be_none nocproxy
|
||||||
# More backend sets, with the additional Windows serial-port module.
|
# More backend sets, with the additional Windows serial-port module.
|
||||||
W_BE_ALL = be_all_s winser cproxy
|
W_BE_ALL = be_all_s winser cproxy
|
||||||
W_BE_NOSSH = be_nos_s winser nocproxy
|
W_BE_NOSSH = be_nos_s winser nocproxy
|
||||||
|
# And with the Unix serial-port module.
|
||||||
|
U_BE_ALL = be_all_s uxser cproxy
|
||||||
|
U_BE_NOSSH = be_nos_s uxser nocproxy
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
# Definitions of actual programs. The program name, followed by a
|
# Definitions of actual programs. The program name, followed by a
|
||||||
|
@ -293,12 +296,13 @@ puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
|
||||||
|
|
||||||
pterm : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore
|
pterm : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore
|
||||||
+ uxsignal CHARSET cmdline uxpterm version time
|
+ uxsignal CHARSET cmdline uxpterm version time
|
||||||
putty : [X] GTKTERM uxmisc misc ldisc settings uxsel BE_ALL uxstore
|
putty : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_ALL uxstore
|
||||||
+ uxsignal CHARSET uxputty NONSSH UXSSH UXMISC ux_x11
|
+ uxsignal CHARSET uxputty NONSSH UXSSH UXMISC ux_x11
|
||||||
puttytel : [X] GTKTERM uxmisc misc ldisc settings uxsel BE_NOSSH
|
puttytel : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_NOSSH
|
||||||
+ uxstore uxsignal CHARSET uxputty NONSSH UXMISC
|
+ uxstore uxsignal CHARSET uxputty NONSSH UXMISC
|
||||||
|
|
||||||
plink : [U] uxplink uxcons NONSSH UXSSH BE_ALL logging UXMISC uxsignal ux_x11
|
plink : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL logging UXMISC uxsignal
|
||||||
|
+ ux_x11
|
||||||
|
|
||||||
puttygen : [U] cmdgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
|
puttygen : [U] cmdgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
|
||||||
+ sshrand uxnoise sshsha misc sshrsa sshdss uxcons uxstore uxmisc
|
+ sshrand uxnoise sshsha misc sshrsa sshdss uxcons uxstore uxmisc
|
||||||
|
@ -318,5 +322,5 @@ PuTTYgen : [M] macpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
|
||||||
+ sshrand macnoise sshsha macstore misc sshrsa sshdss macmisc sshpubk
|
+ sshrand macnoise sshsha macstore misc sshrsa sshdss macmisc sshpubk
|
||||||
+ sshaes sshsh512 import macpgen.rsrc macpgkey macabout
|
+ sshaes sshsh512 import macpgen.rsrc macpgkey macabout
|
||||||
|
|
||||||
PuTTY : [MX] osxmain OSXTERM OSXMISC CHARSET BE_ALL NONSSH UXSSH
|
PuTTY : [MX] osxmain OSXTERM OSXMISC CHARSET U_BE_ALL NONSSH UXSSH
|
||||||
+ ux_x11 uxpty uxsignal testback putty.icns info.plist
|
+ ux_x11 uxpty uxsignal testback putty.icns info.plist
|
||||||
|
|
|
@ -55,6 +55,7 @@ struct uctrl {
|
||||||
GtkWidget *menu; /* for optionmenu (==droplist) */
|
GtkWidget *menu; /* for optionmenu (==droplist) */
|
||||||
GtkWidget *optmenu; /* also for optionmenu */
|
GtkWidget *optmenu; /* also for optionmenu */
|
||||||
GtkWidget *text; /* for text */
|
GtkWidget *text; /* for text */
|
||||||
|
GtkWidget *label; /* for dlg_label_change */
|
||||||
GtkAdjustment *adj; /* for the scrollbar in a list box */
|
GtkAdjustment *adj; /* for the scrollbar in a list box */
|
||||||
guint textsig;
|
guint textsig;
|
||||||
};
|
};
|
||||||
|
@ -90,6 +91,7 @@ static gboolean widget_focus(GtkWidget *widget, GdkEventFocus *event,
|
||||||
gpointer data);
|
gpointer data);
|
||||||
static void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw,
|
static void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw,
|
||||||
int chr, int action, void *ptr);
|
int chr, int action, void *ptr);
|
||||||
|
static void shortcut_highlight(GtkWidget *label, int chr);
|
||||||
static int listitem_single_key(GtkWidget *item, GdkEventKey *event,
|
static int listitem_single_key(GtkWidget *item, GdkEventKey *event,
|
||||||
gpointer data);
|
gpointer data);
|
||||||
static int listitem_multi_key(GtkWidget *item, GdkEventKey *event,
|
static int listitem_multi_key(GtkWidget *item, GdkEventKey *event,
|
||||||
|
@ -594,13 +596,42 @@ void dlg_text_set(union control *ctrl, void *dlg, char const *text)
|
||||||
|
|
||||||
void dlg_label_change(union control *ctrl, void *dlg, char const *text)
|
void dlg_label_change(union control *ctrl, void *dlg, char const *text)
|
||||||
{
|
{
|
||||||
/*
|
struct dlgparam *dp = (struct dlgparam *)dlg;
|
||||||
* This function is currently only used by the config box to
|
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
|
||||||
* switch the labels on the host and port boxes between serial
|
|
||||||
* and network modes. Since Unix does not (yet) have a serial
|
switch (uc->ctrl->generic.type) {
|
||||||
* back end, this function can safely do nothing for the
|
case CTRL_BUTTON:
|
||||||
* moment.
|
gtk_label_set_text(GTK_LABEL(uc->toplevel), text);
|
||||||
*/
|
shortcut_highlight(uc->toplevel, ctrl->button.shortcut);
|
||||||
|
break;
|
||||||
|
case CTRL_CHECKBOX:
|
||||||
|
gtk_label_set_text(GTK_LABEL(uc->toplevel), text);
|
||||||
|
shortcut_highlight(uc->toplevel, ctrl->checkbox.shortcut);
|
||||||
|
break;
|
||||||
|
case CTRL_RADIO:
|
||||||
|
gtk_label_set_text(GTK_LABEL(uc->label), text);
|
||||||
|
shortcut_highlight(uc->label, ctrl->radio.shortcut);
|
||||||
|
break;
|
||||||
|
case CTRL_EDITBOX:
|
||||||
|
gtk_label_set_text(GTK_LABEL(uc->label), text);
|
||||||
|
shortcut_highlight(uc->label, ctrl->editbox.shortcut);
|
||||||
|
break;
|
||||||
|
case CTRL_FILESELECT:
|
||||||
|
gtk_label_set_text(GTK_LABEL(uc->label), text);
|
||||||
|
shortcut_highlight(uc->label, ctrl->fileselect.shortcut);
|
||||||
|
break;
|
||||||
|
case CTRL_FONTSELECT:
|
||||||
|
gtk_label_set_text(GTK_LABEL(uc->label), text);
|
||||||
|
shortcut_highlight(uc->label, ctrl->fontselect.shortcut);
|
||||||
|
break;
|
||||||
|
case CTRL_LISTBOX:
|
||||||
|
gtk_label_set_text(GTK_LABEL(uc->label), text);
|
||||||
|
shortcut_highlight(uc->label, ctrl->listbox.shortcut);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(!"This shouldn't happen");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn)
|
void dlg_filesel_set(union control *ctrl, void *dlg, Filename fn)
|
||||||
|
@ -1336,6 +1367,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
|
||||||
uc->buttons = NULL;
|
uc->buttons = NULL;
|
||||||
uc->entry = uc->list = uc->menu = NULL;
|
uc->entry = uc->list = uc->menu = NULL;
|
||||||
uc->button = uc->optmenu = uc->text = NULL;
|
uc->button = uc->optmenu = uc->text = NULL;
|
||||||
|
uc->label = NULL;
|
||||||
|
|
||||||
switch (ctrl->generic.type) {
|
switch (ctrl->generic.type) {
|
||||||
case CTRL_BUTTON:
|
case CTRL_BUTTON:
|
||||||
|
@ -1381,6 +1413,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
|
||||||
gtk_widget_show(label);
|
gtk_widget_show(label);
|
||||||
shortcut_add(scs, label, ctrl->radio.shortcut,
|
shortcut_add(scs, label, ctrl->radio.shortcut,
|
||||||
SHORTCUT_UCTRL, uc);
|
SHORTCUT_UCTRL, uc);
|
||||||
|
uc->label = label;
|
||||||
}
|
}
|
||||||
percentages = g_new(gint, ctrl->radio.ncolumns);
|
percentages = g_new(gint, ctrl->radio.ncolumns);
|
||||||
for (i = 0; i < ctrl->radio.ncolumns; i++) {
|
for (i = 0; i < ctrl->radio.ncolumns; i++) {
|
||||||
|
@ -1485,6 +1518,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
|
||||||
gtk_widget_show(w);
|
gtk_widget_show(w);
|
||||||
|
|
||||||
w = container;
|
w = container;
|
||||||
|
uc->label = label;
|
||||||
}
|
}
|
||||||
gtk_signal_connect(GTK_OBJECT(uc->entry), "focus_out_event",
|
gtk_signal_connect(GTK_OBJECT(uc->entry), "focus_out_event",
|
||||||
GTK_SIGNAL_FUNC(editbox_lostfocus), dp);
|
GTK_SIGNAL_FUNC(editbox_lostfocus), dp);
|
||||||
|
@ -1513,6 +1547,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
|
||||||
ctrl->fileselect.shortcut :
|
ctrl->fileselect.shortcut :
|
||||||
ctrl->fontselect.shortcut),
|
ctrl->fontselect.shortcut),
|
||||||
SHORTCUT_UCTRL, uc);
|
SHORTCUT_UCTRL, uc);
|
||||||
|
uc->label = ww;
|
||||||
}
|
}
|
||||||
|
|
||||||
uc->entry = ww = gtk_entry_new();
|
uc->entry = ww = gtk_entry_new();
|
||||||
|
@ -1650,6 +1685,7 @@ GtkWidget *layout_ctrls(struct dlgparam *dp, struct Shortcuts *scs,
|
||||||
shortcut_add(scs, label, ctrl->listbox.shortcut,
|
shortcut_add(scs, label, ctrl->listbox.shortcut,
|
||||||
SHORTCUT_UCTRL, uc);
|
SHORTCUT_UCTRL, uc);
|
||||||
w = container;
|
w = container;
|
||||||
|
uc->label = label;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CTRL_TEXT:
|
case CTRL_TEXT:
|
||||||
|
@ -1716,6 +1752,8 @@ static void treeitem_sel(GtkItem *item, gpointer data)
|
||||||
|
|
||||||
panels_switch_to(sp->panels, sp->panel);
|
panels_switch_to(sp->panels, sp->panel);
|
||||||
|
|
||||||
|
dlg_refresh(NULL, sp->dp);
|
||||||
|
|
||||||
sp->dp->shortcuts = &sp->shortcuts;
|
sp->dp->shortcuts = &sp->shortcuts;
|
||||||
sp->dp->currtreeitem = sp->treeitem;
|
sp->dp->currtreeitem = sp->treeitem;
|
||||||
}
|
}
|
||||||
|
@ -1935,13 +1973,31 @@ int tree_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw,
|
static void shortcut_highlight(GtkWidget *labelw, int chr)
|
||||||
int chr, int action, void *ptr)
|
|
||||||
{
|
{
|
||||||
GtkLabel *label = GTK_LABEL(labelw);
|
GtkLabel *label = GTK_LABEL(labelw);
|
||||||
gchar *currstr, *pattern;
|
gchar *currstr, *pattern;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
gtk_label_get(label, &currstr);
|
||||||
|
for (i = 0; currstr[i]; i++)
|
||||||
|
if (tolower((unsigned char)currstr[i]) == chr) {
|
||||||
|
GtkRequisition req;
|
||||||
|
|
||||||
|
pattern = dupprintf("%*s_", i, "");
|
||||||
|
|
||||||
|
gtk_widget_size_request(GTK_WIDGET(label), &req);
|
||||||
|
gtk_label_set_pattern(label, pattern);
|
||||||
|
gtk_widget_set_usize(GTK_WIDGET(label), -1, req.height);
|
||||||
|
|
||||||
|
sfree(pattern);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw,
|
||||||
|
int chr, int action, void *ptr)
|
||||||
|
{
|
||||||
if (chr == NO_SHORTCUT)
|
if (chr == NO_SHORTCUT)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1959,20 +2015,7 @@ void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw,
|
||||||
scs->sc[chr].uc = (struct uctrl *)ptr;
|
scs->sc[chr].uc = (struct uctrl *)ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_label_get(label, &currstr);
|
shortcut_highlight(labelw, chr);
|
||||||
for (i = 0; currstr[i]; i++)
|
|
||||||
if (tolower((unsigned char)currstr[i]) == chr) {
|
|
||||||
GtkRequisition req;
|
|
||||||
|
|
||||||
pattern = dupprintf("%*s_", i, "");
|
|
||||||
|
|
||||||
gtk_widget_size_request(GTK_WIDGET(label), &req);
|
|
||||||
gtk_label_set_pattern(label, pattern);
|
|
||||||
gtk_widget_set_usize(GTK_WIDGET(label), -1, req.height);
|
|
||||||
|
|
||||||
sfree(pattern);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_listitemheight(void)
|
int get_listitemheight(void)
|
||||||
|
|
|
@ -155,6 +155,8 @@ Filename platform_default_filename(const char *name)
|
||||||
|
|
||||||
char *platform_default_s(const char *name)
|
char *platform_default_s(const char *name)
|
||||||
{
|
{
|
||||||
|
if (!strcmp(name, "SerialLine"))
|
||||||
|
return dupstr("/dev/ttyS0");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3444,7 +3446,7 @@ int pt_main(int argc, char **argv)
|
||||||
|
|
||||||
cmdline_run_saved(&inst->cfg);
|
cmdline_run_saved(&inst->cfg);
|
||||||
|
|
||||||
if (!*inst->cfg.host && !cfgbox(&inst->cfg))
|
if (!cfg_launchable(&inst->cfg) && !cfgbox(&inst->cfg))
|
||||||
exit(0); /* config box hit Cancel */
|
exit(0); /* config box hit Cancel */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,4 +149,9 @@ void *sk_getxdmdata(void *sock, int *lenp);
|
||||||
if (max < fd + 1) max = fd + 1; \
|
if (max < fd + 1) max = fd + 1; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exports from winser.c.
|
||||||
|
*/
|
||||||
|
extern Backend serial_backend;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -69,4 +69,11 @@ void unix_setup_config_box(struct controlbox *b, int midsession)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Serial back end is available on Unix. However, we have to
|
||||||
|
* mask out a couple of the configuration options: mark and
|
||||||
|
* space parity are not conveniently supported, and neither is
|
||||||
|
* DSR/DTR flow control.
|
||||||
|
*/
|
||||||
|
ser_setup_config_box(b, midsession, 0x07, 0x07);
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,8 @@ char *platform_default_s(const char *name)
|
||||||
return dupstr(getenv("TERM"));
|
return dupstr(getenv("TERM"));
|
||||||
if (!strcmp(name, "UserName"))
|
if (!strcmp(name, "UserName"))
|
||||||
return get_username();
|
return get_username();
|
||||||
|
if (!strcmp(name, "SerialLine"))
|
||||||
|
return dupstr("/dev/ttyS0");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -627,7 +629,7 @@ int main(int argc, char **argv)
|
||||||
errors = 1;
|
errors = 1;
|
||||||
}
|
}
|
||||||
} else if (*p) {
|
} else if (*p) {
|
||||||
if (!*cfg.host) {
|
if (!cfg_launchable(&cfg)) {
|
||||||
char *q = p;
|
char *q = p;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -701,7 +703,7 @@ int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
Config cfg2;
|
Config cfg2;
|
||||||
do_defaults(host, &cfg2);
|
do_defaults(host, &cfg2);
|
||||||
if (loaded_session || cfg2.host[0] == '\0') {
|
if (loaded_session || !cfg_launchable(&cfg2)) {
|
||||||
/* No settings for this host; use defaults */
|
/* No settings for this host; use defaults */
|
||||||
/* (or session was already loaded with -load) */
|
/* (or session was already loaded with -load) */
|
||||||
strncpy(cfg.host, host, sizeof(cfg.host) - 1);
|
strncpy(cfg.host, host, sizeof(cfg.host) - 1);
|
||||||
|
@ -755,7 +757,7 @@ int main(int argc, char **argv)
|
||||||
if (errors)
|
if (errors)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (!*cfg.host) {
|
if (!cfg_launchable(&cfg)) {
|
||||||
usage();
|
usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,510 @@
|
||||||
|
/*
|
||||||
|
* Serial back end (Unix-specific).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO:
|
||||||
|
*
|
||||||
|
* - send break.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
#include "tree234.h"
|
||||||
|
|
||||||
|
#define SERIAL_MAX_BACKLOG 4096
|
||||||
|
|
||||||
|
typedef struct serial_backend_data {
|
||||||
|
void *frontend;
|
||||||
|
int fd;
|
||||||
|
int finished;
|
||||||
|
int inbufsize;
|
||||||
|
bufchain output_data;
|
||||||
|
} *Serial;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We store our serial backends in a tree sorted by fd, so that
|
||||||
|
* when we get an uxsel notification we know which backend instance
|
||||||
|
* is the owner of the serial port that caused it.
|
||||||
|
*/
|
||||||
|
static int serial_compare_by_fd(void *av, void *bv)
|
||||||
|
{
|
||||||
|
Serial a = (Serial)av;
|
||||||
|
Serial b = (Serial)bv;
|
||||||
|
|
||||||
|
if (a->fd < b->fd)
|
||||||
|
return -1;
|
||||||
|
else if (a->fd > b->fd)
|
||||||
|
return +1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serial_find_by_fd(void *av, void *bv)
|
||||||
|
{
|
||||||
|
int a = *(int *)av;
|
||||||
|
Serial b = (Serial)bv;
|
||||||
|
|
||||||
|
if (a < b->fd)
|
||||||
|
return -1;
|
||||||
|
else if (a > b->fd)
|
||||||
|
return +1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static tree234 *serial_by_fd = NULL;
|
||||||
|
|
||||||
|
static int serial_select_result(int fd, int event);
|
||||||
|
static void serial_uxsel_setup(Serial serial);
|
||||||
|
static void serial_try_write(Serial serial);
|
||||||
|
|
||||||
|
static const char *serial_configure(Serial serial, Config *cfg)
|
||||||
|
{
|
||||||
|
struct termios options;
|
||||||
|
int bflag, bval;
|
||||||
|
const char *str;
|
||||||
|
char *msg;
|
||||||
|
|
||||||
|
if (serial->fd < 0)
|
||||||
|
return "Unable to reconfigure already-closed serial connection";
|
||||||
|
|
||||||
|
tcgetattr(serial->fd, &options);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find the appropriate baud rate flag.
|
||||||
|
*/
|
||||||
|
#define SETBAUD(x) (bflag = B ## x, bval = x)
|
||||||
|
#define CHECKBAUD(x) do { if (cfg->serspeed >= x) SETBAUD(x); } while (0)
|
||||||
|
SETBAUD(50);
|
||||||
|
#ifdef B75
|
||||||
|
CHECKBAUD(75);
|
||||||
|
#endif
|
||||||
|
#ifdef B110
|
||||||
|
CHECKBAUD(110);
|
||||||
|
#endif
|
||||||
|
#ifdef B134
|
||||||
|
CHECKBAUD(134);
|
||||||
|
#endif
|
||||||
|
#ifdef B150
|
||||||
|
CHECKBAUD(150);
|
||||||
|
#endif
|
||||||
|
#ifdef B200
|
||||||
|
CHECKBAUD(200);
|
||||||
|
#endif
|
||||||
|
#ifdef B300
|
||||||
|
CHECKBAUD(300);
|
||||||
|
#endif
|
||||||
|
#ifdef B600
|
||||||
|
CHECKBAUD(600);
|
||||||
|
#endif
|
||||||
|
#ifdef B1200
|
||||||
|
CHECKBAUD(1200);
|
||||||
|
#endif
|
||||||
|
#ifdef B1800
|
||||||
|
CHECKBAUD(1800);
|
||||||
|
#endif
|
||||||
|
#ifdef B2400
|
||||||
|
CHECKBAUD(2400);
|
||||||
|
#endif
|
||||||
|
#ifdef B4800
|
||||||
|
CHECKBAUD(4800);
|
||||||
|
#endif
|
||||||
|
#ifdef B9600
|
||||||
|
CHECKBAUD(9600);
|
||||||
|
#endif
|
||||||
|
#ifdef B19200
|
||||||
|
CHECKBAUD(19200);
|
||||||
|
#endif
|
||||||
|
#ifdef B38400
|
||||||
|
CHECKBAUD(38400);
|
||||||
|
#endif
|
||||||
|
#ifdef B57600
|
||||||
|
CHECKBAUD(57600);
|
||||||
|
#endif
|
||||||
|
#ifdef B76800
|
||||||
|
CHECKBAUD(76800);
|
||||||
|
#endif
|
||||||
|
#ifdef B115200
|
||||||
|
CHECKBAUD(115200);
|
||||||
|
#endif
|
||||||
|
#ifdef B230400
|
||||||
|
CHECKBAUD(230400);
|
||||||
|
#endif
|
||||||
|
#undef CHECKBAUD
|
||||||
|
#undef SETBAUD
|
||||||
|
cfsetispeed(&options, bflag);
|
||||||
|
cfsetospeed(&options, bflag);
|
||||||
|
msg = dupprintf("Configuring baud rate %d", bval);
|
||||||
|
logevent(serial->frontend, msg);
|
||||||
|
sfree(msg);
|
||||||
|
|
||||||
|
options.c_cflag &= ~CSIZE;
|
||||||
|
switch (cfg->serdatabits) {
|
||||||
|
case 5: options.c_cflag |= CS5; break;
|
||||||
|
case 6: options.c_cflag |= CS6; break;
|
||||||
|
case 7: options.c_cflag |= CS7; break;
|
||||||
|
case 8: options.c_cflag |= CS8; break;
|
||||||
|
default: return "Invalid number of data bits (need 5, 6, 7 or 8)";
|
||||||
|
}
|
||||||
|
msg = dupprintf("Configuring %d data bits", cfg->serdatabits);
|
||||||
|
logevent(serial->frontend, msg);
|
||||||
|
sfree(msg);
|
||||||
|
|
||||||
|
if (cfg->serstopbits >= 4) {
|
||||||
|
options.c_cflag |= CSTOPB;
|
||||||
|
} else {
|
||||||
|
options.c_cflag &= ~CSTOPB;
|
||||||
|
}
|
||||||
|
msg = dupprintf("Configuring %d stop bits",
|
||||||
|
(options.c_cflag & CSTOPB ? 2 : 1));
|
||||||
|
logevent(serial->frontend, msg);
|
||||||
|
sfree(msg);
|
||||||
|
|
||||||
|
options.c_cflag &= ~(IXON|IXOFF);
|
||||||
|
if (cfg->serflow == SER_FLOW_XONXOFF) {
|
||||||
|
options.c_cflag |= IXON | IXOFF;
|
||||||
|
str = "XON/XOFF";
|
||||||
|
} else if (cfg->serflow == SER_FLOW_RTSCTS) {
|
||||||
|
#ifdef CRTSCTS
|
||||||
|
options.c_cflag |= CRTSCTS;
|
||||||
|
#endif
|
||||||
|
#ifdef CNEW_RTSCTS
|
||||||
|
options.c_cflag |= CNEW_RTSCTS;
|
||||||
|
#endif
|
||||||
|
str = "RTS/CTS";
|
||||||
|
} else
|
||||||
|
str = "no";
|
||||||
|
msg = dupprintf("Configuring %s flow control", str);
|
||||||
|
logevent(serial->frontend, msg);
|
||||||
|
sfree(msg);
|
||||||
|
|
||||||
|
/* Parity */
|
||||||
|
if (cfg->serparity == SER_PAR_ODD) {
|
||||||
|
options.c_cflag |= PARENB;
|
||||||
|
options.c_cflag |= PARODD;
|
||||||
|
str = "odd";
|
||||||
|
} else if (cfg->serparity == SER_PAR_EVEN) {
|
||||||
|
options.c_cflag |= PARENB;
|
||||||
|
options.c_cflag &= ~PARODD;
|
||||||
|
str = "even";
|
||||||
|
} else {
|
||||||
|
options.c_cflag &= ~PARENB;
|
||||||
|
str = "no";
|
||||||
|
}
|
||||||
|
msg = dupprintf("Configuring %s parity", str);
|
||||||
|
logevent(serial->frontend, msg);
|
||||||
|
sfree(msg);
|
||||||
|
|
||||||
|
options.c_cflag |= CLOCAL | CREAD;
|
||||||
|
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
||||||
|
options.c_oflag &= ~OPOST;
|
||||||
|
options.c_cc[VMIN] = 1;
|
||||||
|
options.c_cc[VTIME] = 0;
|
||||||
|
|
||||||
|
if (tcsetattr(serial->fd, TCSANOW, &options) < 0)
|
||||||
|
return "Unable to configure serial port";
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called to set up the serial connection.
|
||||||
|
*
|
||||||
|
* Returns an error message, or NULL on success.
|
||||||
|
*
|
||||||
|
* Also places the canonical host name into `realhost'. It must be
|
||||||
|
* freed by the caller.
|
||||||
|
*/
|
||||||
|
static const char *serial_init(void *frontend_handle, void **backend_handle,
|
||||||
|
Config *cfg,
|
||||||
|
char *host, int port, char **realhost, int nodelay,
|
||||||
|
int keepalive)
|
||||||
|
{
|
||||||
|
Serial serial;
|
||||||
|
const char *err;
|
||||||
|
|
||||||
|
serial = snew(struct serial_backend_data);
|
||||||
|
*backend_handle = serial;
|
||||||
|
|
||||||
|
serial->frontend = frontend_handle;
|
||||||
|
serial->finished = FALSE;
|
||||||
|
serial->inbufsize = 0;
|
||||||
|
bufchain_init(&serial->output_data);
|
||||||
|
|
||||||
|
{
|
||||||
|
char *msg = dupprintf("Opening serial device %s", cfg->serline);
|
||||||
|
logevent(serial->frontend, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
serial->fd = open(cfg->serline, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
|
||||||
|
if (serial->fd < 0)
|
||||||
|
return "Unable to open serial port";
|
||||||
|
|
||||||
|
err = serial_configure(serial, cfg);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
*realhost = dupstr(cfg->serline);
|
||||||
|
|
||||||
|
if (!serial_by_fd)
|
||||||
|
serial_by_fd = newtree234(serial_compare_by_fd);
|
||||||
|
add234(serial_by_fd, serial);
|
||||||
|
|
||||||
|
serial_uxsel_setup(serial);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serial_close(Serial serial)
|
||||||
|
{
|
||||||
|
if (serial->fd >= 0) {
|
||||||
|
close(serial->fd);
|
||||||
|
serial->fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serial_free(void *handle)
|
||||||
|
{
|
||||||
|
Serial serial = (Serial) handle;
|
||||||
|
|
||||||
|
serial_close(serial);
|
||||||
|
|
||||||
|
bufchain_clear(&serial->output_data);
|
||||||
|
|
||||||
|
sfree(serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serial_reconfig(void *handle, Config *cfg)
|
||||||
|
{
|
||||||
|
Serial serial = (Serial) handle;
|
||||||
|
const char *err;
|
||||||
|
|
||||||
|
err = serial_configure(serial, cfg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIXME: what should we do if err returns something?
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serial_select_result(int fd, int event)
|
||||||
|
{
|
||||||
|
Serial serial;
|
||||||
|
char buf[4096];
|
||||||
|
int ret;
|
||||||
|
int finished = FALSE;
|
||||||
|
|
||||||
|
serial = find234(serial_by_fd, &fd, serial_find_by_fd);
|
||||||
|
|
||||||
|
if (!serial)
|
||||||
|
return 1; /* spurious event; keep going */
|
||||||
|
|
||||||
|
if (event == 1) {
|
||||||
|
ret = read(serial->fd, buf, sizeof(buf));
|
||||||
|
|
||||||
|
if (ret == 0) {
|
||||||
|
/*
|
||||||
|
* Shouldn't happen on a real serial port, but I'm open
|
||||||
|
* to the idea that there might be two-way devices we
|
||||||
|
* can treat _like_ serial ports which can return EOF.
|
||||||
|
*/
|
||||||
|
finished = TRUE;
|
||||||
|
} else if (ret < 0) {
|
||||||
|
perror("read serial port");
|
||||||
|
exit(1);
|
||||||
|
} else if (ret > 0) {
|
||||||
|
serial->inbufsize = from_backend(serial->frontend, 0, buf, ret);
|
||||||
|
serial_uxsel_setup(serial); /* might acquire backlog and freeze */
|
||||||
|
}
|
||||||
|
} else if (event == 2) {
|
||||||
|
/*
|
||||||
|
* Attempt to send data down the pty.
|
||||||
|
*/
|
||||||
|
serial_try_write(serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finished) {
|
||||||
|
serial_close(serial);
|
||||||
|
|
||||||
|
serial->finished = TRUE;
|
||||||
|
|
||||||
|
notify_remote_exit(serial->frontend);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serial_uxsel_setup(Serial serial)
|
||||||
|
{
|
||||||
|
int rwx = 0;
|
||||||
|
|
||||||
|
if (serial->inbufsize <= SERIAL_MAX_BACKLOG)
|
||||||
|
rwx |= 1;
|
||||||
|
if (bufchain_size(&serial->output_data))
|
||||||
|
rwx |= 2; /* might also want to write to it */
|
||||||
|
uxsel_set(serial->fd, rwx, serial_select_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serial_try_write(Serial serial)
|
||||||
|
{
|
||||||
|
void *data;
|
||||||
|
int len, ret;
|
||||||
|
|
||||||
|
assert(serial->fd >= 0);
|
||||||
|
|
||||||
|
while (bufchain_size(&serial->output_data) > 0) {
|
||||||
|
bufchain_prefix(&serial->output_data, &data, &len);
|
||||||
|
ret = write(serial->fd, data, len);
|
||||||
|
|
||||||
|
if (ret < 0 && (errno == EWOULDBLOCK)) {
|
||||||
|
/*
|
||||||
|
* We've sent all we can for the moment.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
perror("write serial port");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
bufchain_consume(&serial->output_data, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
serial_uxsel_setup(serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called to send data down the serial connection.
|
||||||
|
*/
|
||||||
|
static int serial_send(void *handle, char *buf, int len)
|
||||||
|
{
|
||||||
|
Serial serial = (Serial) handle;
|
||||||
|
|
||||||
|
if (serial->fd < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bufchain_add(&serial->output_data, buf, len);
|
||||||
|
serial_try_write(serial);
|
||||||
|
|
||||||
|
return bufchain_size(&serial->output_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called to query the current sendability status.
|
||||||
|
*/
|
||||||
|
static int serial_sendbuffer(void *handle)
|
||||||
|
{
|
||||||
|
Serial serial = (Serial) handle;
|
||||||
|
return bufchain_size(&serial->output_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called to set the size of the window
|
||||||
|
*/
|
||||||
|
static void serial_size(void *handle, int width, int height)
|
||||||
|
{
|
||||||
|
/* Do nothing! */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send serial special codes.
|
||||||
|
*/
|
||||||
|
static void serial_special(void *handle, Telnet_Special code)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* FIXME: serial break? XON? XOFF?
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a list of the special codes that make sense in this
|
||||||
|
* protocol.
|
||||||
|
*/
|
||||||
|
static const struct telnet_special *serial_get_specials(void *handle)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* FIXME: serial break? XON? XOFF?
|
||||||
|
*/
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serial_connected(void *handle)
|
||||||
|
{
|
||||||
|
return 1; /* always connected */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serial_sendok(void *handle)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serial_unthrottle(void *handle, int backlog)
|
||||||
|
{
|
||||||
|
Serial serial = (Serial) handle;
|
||||||
|
serial->inbufsize = backlog;
|
||||||
|
serial_uxsel_setup(serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serial_ldisc(void *handle, int option)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Local editing and local echo are off by default.
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serial_provide_ldisc(void *handle, void *ldisc)
|
||||||
|
{
|
||||||
|
/* This is a stub. */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void serial_provide_logctx(void *handle, void *logctx)
|
||||||
|
{
|
||||||
|
/* This is a stub. */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int serial_exitcode(void *handle)
|
||||||
|
{
|
||||||
|
Serial serial = (Serial) handle;
|
||||||
|
if (serial->fd >= 0)
|
||||||
|
return -1; /* still connected */
|
||||||
|
else
|
||||||
|
/* Exit codes are a meaningless concept with serial ports */
|
||||||
|
return INT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cfg_info for Serial does nothing at all.
|
||||||
|
*/
|
||||||
|
static int serial_cfg_info(void *handle)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Backend serial_backend = {
|
||||||
|
serial_init,
|
||||||
|
serial_free,
|
||||||
|
serial_reconfig,
|
||||||
|
serial_send,
|
||||||
|
serial_sendbuffer,
|
||||||
|
serial_size,
|
||||||
|
serial_special,
|
||||||
|
serial_get_specials,
|
||||||
|
serial_connected,
|
||||||
|
serial_exitcode,
|
||||||
|
serial_sendok,
|
||||||
|
serial_ldisc,
|
||||||
|
serial_provide_ldisc,
|
||||||
|
serial_provide_logctx,
|
||||||
|
serial_unthrottle,
|
||||||
|
serial_cfg_info,
|
||||||
|
1
|
||||||
|
};
|
Загрузка…
Ссылка в новой задаче