NetworkManager/src/nm-serial-device.c
Dan Williams 9068f0b6c1 2008-10-08 Dan Williams <dcbw@redhat.com>
* src/nm-serial-device.c
		- Turn on serial debugging when NM_SERIAL_DEBUG is set in the environment



git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@4155 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
2008-10-08 16:14:01 +00:00

1144 lines
26 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
#define _GNU_SOURCE /* for strcasestr() */
#include <termio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <string.h>
#include <stdlib.h>
#include <glib.h>
#include "nm-serial-device.h"
#include "nm-device-interface.h"
#include "nm-device-private.h"
#include "ppp-manager/nm-ppp-manager.h"
#include "nm-setting-ppp.h"
#include "nm-marshal.h"
#include "nm-utils.h"
#include "nm-serial-device-glue.h"
static gboolean serial_debug = FALSE;
#define SERIAL_BUF_SIZE 2048
G_DEFINE_TYPE (NMSerialDevice, nm_serial_device, NM_TYPE_DEVICE)
#define NM_SERIAL_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SERIAL_DEVICE, NMSerialDevicePrivate))
typedef struct {
int fd;
GIOChannel *channel;
NMPPPManager *ppp_manager;
NMIP4Config *pending_ip4_config;
struct termios old_t;
guint pending_id;
guint timeout_id;
/* PPP stats */
guint32 in_bytes;
guint32 out_bytes;
} NMSerialDevicePrivate;
enum {
PPP_STATS,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
static int
parse_baudrate (guint i)
{
int speed;
switch (i) {
case 0:
speed = B0;
break;
case 50:
speed = B50;
break;
case 75:
speed = B75;
break;
case 110:
speed = B110;
break;
case 150:
speed = B150;
break;
case 300:
speed = B300;
break;
case 600:
speed = B600;
break;
case 1200:
speed = B1200;
break;
case 2400:
speed = B2400;
break;
case 4800:
speed = B4800;
break;
case 9600:
speed = B9600;
break;
case 19200:
speed = B19200;
break;
case 38400:
speed = B38400;
break;
case 57600:
speed = B57600;
break;
case 115200:
speed = B115200;
break;
case 460800:
speed = B460800;
break;
default:
g_warning ("Invalid baudrate '%d'", i);
speed = B9600;
}
return speed;
}
static int
parse_bits (guint i)
{
int bits;
switch (i) {
case 5:
bits = CS5;
break;
case 6:
bits = CS6;
break;
case 7:
bits = CS7;
break;
case 8:
bits = CS8;
break;
default:
g_warning ("Invalid bits (%d). Valid values are 5, 6, 7, 8.", i);
bits = CS8;
}
return bits;
}
static int
parse_parity (char c)
{
int parity;
switch (c) {
case 'n':
case 'N':
parity = 0;
break;
case 'e':
case 'E':
parity = PARENB;
break;
case 'o':
case 'O':
parity = PARENB | PARODD;
break;
default:
g_warning ("Invalid parity (%c). Valid values are n, e, o", c);
parity = 0;
}
return parity;
}
static int
parse_stopbits (guint i)
{
int stopbits;
switch (i) {
case 1:
stopbits = 0;
break;
case 2:
stopbits = CSTOPB;
break;
default:
g_warning ("Invalid stop bits (%d). Valid values are 1 and 2)", i);
stopbits = 0;
}
return stopbits;
}
static inline void
nm_serial_debug (const char *prefix, const char *data, int len)
{
GString *str;
int i;
if (!serial_debug)
return;
str = g_string_sized_new (len);
for (i = 0; i < len; i++) {
if (data[i] == '\0')
g_string_append_c (str, ' ');
else if (data[i] == '\r')
g_string_append_c (str, '\n');
else
g_string_append_c (str, data[i]);
}
nm_debug ("%s '%s'", prefix, str->str);
g_string_free (str, TRUE);
}
static NMSetting *
serial_device_get_setting (NMSerialDevice *device, GType setting_type)
{
NMActRequest *req;
NMSetting *setting = NULL;
req = nm_device_get_act_request (NM_DEVICE (device));
if (req) {
NMConnection *connection;
connection = nm_act_request_get_connection (req);
if (connection)
setting = nm_connection_get_setting (connection, setting_type);
}
return setting;
}
/* Timeout handling */
static void
nm_serial_device_timeout_removed (gpointer data)
{
NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (data);
priv->timeout_id = 0;
}
static gboolean
nm_serial_device_timed_out (gpointer data)
{
NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (data);
/* Cancel data reading */
if (priv->pending_id)
g_source_remove (priv->pending_id);
else
nm_warning ("Timeout reached, but there's nothing to time out");
return FALSE;
}
static void
nm_serial_device_add_timeout (NMSerialDevice *self, guint timeout)
{
NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (self);
if (priv->pending_id == 0)
nm_warning ("Adding a time out while not waiting for any data");
if (priv->timeout_id) {
nm_warning ("Trying to add a new time out while the old one still exists");
g_source_remove (priv->timeout_id);
}
priv->timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT,
timeout * 1000,
nm_serial_device_timed_out,
self,
nm_serial_device_timeout_removed);
if (G_UNLIKELY (priv->timeout_id == 0))
nm_warning ("Registering serial device time out failed.");
}
static void
nm_serial_device_remove_timeout (NMSerialDevice *self)
{
NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (self);
if (priv->timeout_id)
g_source_remove (priv->timeout_id);
}
/* Pending data reading */
static guint
nm_serial_device_set_pending (NMSerialDevice *device,
guint timeout,
GIOFunc callback,
gpointer user_data,
GDestroyNotify notify)
{
NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (device);
if (G_UNLIKELY (priv->pending_id)) {
/* FIXME: Probably should queue up pending calls instead? */
/* Multiple pending calls on the same GIOChannel doesn't work, so let's cancel the previous one. */
nm_warning ("Adding new pending call while previous one isn't finished.");
nm_warning ("Cancelling the previous pending call.");
g_source_remove (priv->pending_id);
}
priv->pending_id = g_io_add_watch_full (priv->channel,
G_PRIORITY_DEFAULT,
G_IO_IN | G_IO_ERR | G_IO_HUP,
callback, user_data, notify);
nm_serial_device_add_timeout (device, timeout);
return priv->pending_id;
}
static void
nm_serial_device_pending_done (NMSerialDevice *self)
{
NM_SERIAL_DEVICE_GET_PRIVATE (self)->pending_id = 0;
nm_serial_device_remove_timeout (self);
}
/****/
static gboolean
config_fd (NMSerialDevice *device, NMSettingSerial *setting)
{
NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (device);
struct termio stbuf;
int speed;
int bits;
int parity;
int stopbits;
speed = parse_baudrate (setting->baud);
bits = parse_bits (setting->bits);
parity = parse_parity (setting->parity);
stopbits = parse_stopbits (setting->stopbits);
ioctl (priv->fd, TCGETA, &stbuf);
stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY | IGNPAR );
stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET);
stbuf.c_lflag &= ~(ICANON | XCASE | ECHO | ECHOE | ECHONL);
stbuf.c_lflag &= ~(ECHO | ECHOE);
stbuf.c_cc[VMIN] = 1;
stbuf.c_cc[VTIME] = 0;
stbuf.c_cc[VEOF] = 1;
stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB);
stbuf.c_cflag |= (speed | bits | CREAD | 0 | parity | stopbits);
if (ioctl (priv->fd, TCSETA, &stbuf) < 0) {
nm_warning ("(%s) cannot control device (errno %d)",
nm_device_get_iface (NM_DEVICE (device)), errno);
return FALSE;
}
return TRUE;
}
gboolean
nm_serial_device_open (NMSerialDevice *device,
NMSettingSerial *setting)
{
NMSerialDevicePrivate *priv;
const char *iface;
char *path;
g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), FALSE);
g_return_val_if_fail (NM_IS_SETTING_SERIAL (setting), FALSE);
priv = NM_SERIAL_DEVICE_GET_PRIVATE (device);
iface = nm_device_get_iface (NM_DEVICE (device));
nm_debug ("(%s) opening device...", iface);
path = g_build_filename ("/dev", iface, NULL);
priv->fd = open (path, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY);
g_free (path);
if (priv->fd < 0) {
nm_warning ("(%s) cannot open device (errno %d)", iface, errno);
return FALSE;
}
if (ioctl (priv->fd, TCGETA, &priv->old_t) < 0) {
nm_warning ("(%s) cannot control device (errno %d)", iface, errno);
close (priv->fd);
return FALSE;
}
if (!config_fd (device, setting)) {
close (priv->fd);
return FALSE;
}
priv->channel = g_io_channel_unix_new (priv->fd);
return TRUE;
}
void
nm_serial_device_close (NMSerialDevice *device)
{
NMSerialDevicePrivate *priv;
g_return_if_fail (NM_IS_SERIAL_DEVICE (device));
priv = NM_SERIAL_DEVICE_GET_PRIVATE (device);
if (priv->pending_id)
g_source_remove (priv->pending_id);
if (priv->ppp_manager) {
nm_ppp_manager_stop (priv->ppp_manager);
g_object_unref (priv->ppp_manager);
priv->ppp_manager = NULL;
}
if (priv->fd) {
nm_debug ("Closing device '%s'", nm_device_get_iface (NM_DEVICE (device)));
if (priv->channel) {
g_io_channel_unref (priv->channel);
priv->channel = NULL;
}
ioctl (priv->fd, TCSETA, &priv->old_t);
close (priv->fd);
priv->fd = 0;
}
}
gboolean
nm_serial_device_send_command (NMSerialDevice *device, GByteArray *command)
{
int fd;
NMSettingSerial *setting;
int i, eagain_count = 1000;
ssize_t written;
guint32 send_delay = G_USEC_PER_SEC / 1000;
g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), FALSE);
g_return_val_if_fail (command != NULL, FALSE);
fd = NM_SERIAL_DEVICE_GET_PRIVATE (device)->fd;
setting = NM_SETTING_SERIAL (serial_device_get_setting (device, NM_TYPE_SETTING_SERIAL));
if (setting && setting->send_delay)
send_delay = setting->send_delay;
nm_serial_debug ("Sending:", (char *) command->data, command->len);
for (i = 0; i < command->len && eagain_count > 0;) {
written = write (fd, command->data + i, 1);
if (written > 0)
i += written;
else {
/* Treat written == 0 as EAGAIN to ensure we break out of the
* for() loop eventually.
*/
if ((written < 0) && (errno != EAGAIN)) {
g_warning ("Error in writing (errno %d)", errno);
return FALSE;
}
eagain_count--;
}
g_usleep (send_delay);
}
if (eagain_count <= 0)
nm_serial_debug ("Error: too many retries sending:", (char *) command->data, command->len);
return TRUE;
}
gboolean
nm_serial_device_send_command_string (NMSerialDevice *device, const char *str)
{
GByteArray *command;
gboolean ret;
g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), FALSE);
g_return_val_if_fail (str != NULL, FALSE);
command = g_byte_array_new ();
g_byte_array_append (command, (guint8 *) str, strlen (str));
g_byte_array_append (command, (guint8 *) "\r", 1);
ret = nm_serial_device_send_command (device, command);
g_byte_array_free (command, TRUE);
return ret;
}
static gboolean
find_terminator (const char *line, const char **terminators)
{
int i;
for (i = 0; terminators[i]; i++) {
if (!strncasecmp (line, terminators[i], strlen (terminators[i])))
return TRUE;
}
return FALSE;
}
static const char *
find_response (const char *line, const char **responses, gint *idx)
{
int i;
/* Don't look for a result again if we got one previously */
for (i = 0; responses[i]; i++) {
if (strcasestr (line, responses[i])) {
*idx = i;
return line;
}
}
return NULL;
}
#define RESPONSE_LINE_MAX 128
int
nm_serial_device_wait_reply_blocking (NMSerialDevice *device,
guint32 timeout_secs,
const char **needles,
const char **terminators)
{
char buf[SERIAL_BUF_SIZE + 1];
int fd, reply_index = -1, bytes_read;
GString *result = NULL;
time_t end;
const char *response = NULL;
gboolean done = FALSE;
g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), -1);
g_return_val_if_fail (timeout_secs <= 60, -1);
g_return_val_if_fail (needles != NULL, -1);
fd = NM_SERIAL_DEVICE_GET_PRIVATE (device)->fd;
if (fd < 0)
return -1;
end = time (NULL) + timeout_secs;
result = g_string_sized_new (20);
do {
bytes_read = read (fd, buf, SERIAL_BUF_SIZE);
if (bytes_read < 0 && errno != EAGAIN) {
nm_warning ("%s: read error: %d (%s)",
nm_device_get_iface (NM_DEVICE (device)),
errno,
strerror (errno));
return -1;
}
if (bytes_read == 0)
break; /* EOF */
else if (bytes_read > 0) {
buf[bytes_read] = 0;
g_string_append (result, buf);
nm_serial_debug ("Got:", result->str, result->len);
}
/* Look for needles and terminators */
if ((bytes_read > 0) && result->str) {
char *p = result->str;
/* Break the response up into lines and process each one */
while ((p < result->str + strlen (result->str)) && !done) {
char line[RESPONSE_LINE_MAX] = { '\0', };
char *tmp;
int i;
gboolean got_something = FALSE;
for (i = 0; *p && (i < RESPONSE_LINE_MAX - 1); p++) {
/* Ignore front CR/LF */
if ((*p == '\n') || (*p == '\r')) {
if (got_something)
break;
} else {
line[i++] = *p;
got_something = TRUE;
}
}
line[i] = '\0';
tmp = g_strstrip (line);
if (tmp && strlen (tmp)) {
done = find_terminator (tmp, terminators);
if (reply_index == -1)
response = find_response (tmp, needles, &reply_index);
}
}
}
/* Limit the size of the buffer */
if (result->len > SERIAL_BUF_SIZE) {
g_warning ("%s (%s): response buffer filled before repsonse received",
__func__, nm_device_get_iface (NM_DEVICE (device)));
break;
}
if (!done)
g_usleep (100);
} while (!done && (time (NULL) < end));
return reply_index;
}
typedef struct {
NMSerialDevice *device;
char **str_needles;
char **terminators;
GString *result;
NMSerialWaitForReplyFn callback;
gpointer user_data;
int reply_index;
char *reply_line;
time_t end;
} WaitForReplyInfo;
static void
wait_for_reply_done (gpointer data)
{
WaitForReplyInfo *info = (WaitForReplyInfo *) data;
nm_serial_device_pending_done (info->device);
/* Call the callback */
info->callback (info->device, info->reply_index, info->reply_line, info->user_data);
/* Free info */
if (info->result)
g_string_free (info->result, TRUE);
g_free (info->reply_line);
g_strfreev (info->str_needles);
g_strfreev (info->terminators);
g_slice_free (WaitForReplyInfo, info);
}
static gboolean
wait_for_reply_got_data (GIOChannel *source,
GIOCondition condition,
gpointer data)
{
WaitForReplyInfo *info = (WaitForReplyInfo *) data;
gchar buf[SERIAL_BUF_SIZE + 1];
gsize bytes_read;
GIOStatus status;
gboolean done = FALSE;
if (condition & G_IO_HUP || condition & G_IO_ERR)
return FALSE;
do {
GError *err = NULL;
status = g_io_channel_read_chars (source, buf, SERIAL_BUF_SIZE, &bytes_read, &err);
if (status == G_IO_STATUS_ERROR) {
g_warning ("%s", err->message);
g_error_free (err);
err = NULL;
}
if (bytes_read > 0) {
buf[bytes_read] = 0;
g_string_append (info->result, buf);
nm_serial_debug ("Got:", info->result->str, info->result->len);
}
/* Look for needles and terminators */
if ((bytes_read > 0) && info->result->str) {
char *p = info->result->str;
/* Break the response up into lines and process each one */
while ((p < info->result->str + strlen (info->result->str)) && !done) {
char line[RESPONSE_LINE_MAX] = { '\0', };
char *tmp;
int i;
gboolean got_something = FALSE;
for (i = 0; *p && (i < RESPONSE_LINE_MAX - 1); p++) {
/* Ignore front CR/LF */
if ((*p == '\n') || (*p == '\r')) {
if (got_something)
break;
} else {
line[i++] = *p;
got_something = TRUE;
}
}
line[i] = '\0';
tmp = g_strstrip (line);
if (tmp && strlen (tmp)) {
done = find_terminator (tmp, (const char **) info->terminators);
if (info->reply_index == -1) {
if (find_response (tmp, (const char **) info->str_needles, &(info->reply_index)))
info->reply_line = g_strdup (tmp);
}
}
}
}
/* Limit the size of the buffer */
if (info->result->len > SERIAL_BUF_SIZE) {
nm_warning ("(%s): response buffer filled before repsonse received",
nm_device_get_iface (NM_DEVICE (info->device)));
done = TRUE;
break;
}
/* Make sure we don't go over the timeout, in addition to the timeout
* handler that's been scheduled. If for some reason this loop doesn't
* terminate (terminator not found, whatever) then this should make
* sure that NM doesn't spin the CPU forever.
*/
if (time (NULL) > info->end) {
done = TRUE;
break;
} else if (!done)
g_usleep (50);
} while (!done || bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN);
return !done;
}
guint
nm_serial_device_wait_for_reply (NMSerialDevice *device,
guint timeout,
const char **responses,
const char **terminators,
NMSerialWaitForReplyFn callback,
gpointer user_data)
{
WaitForReplyInfo *info;
g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), 0);
g_return_val_if_fail (responses != NULL, 0);
g_return_val_if_fail (callback != NULL, 0);
info = g_slice_new0 (WaitForReplyInfo);
info->device = device;
info->str_needles = g_strdupv ((char **) responses);
info->terminators = g_strdupv ((char **) terminators);
info->result = g_string_new (NULL);
info->callback = callback;
info->user_data = user_data;
info->reply_index = -1;
info->end = time (NULL) + timeout;
return nm_serial_device_set_pending (device, timeout, wait_for_reply_got_data, info, wait_for_reply_done);
}
#if 0
typedef struct {
NMSerialDevice *device;
gboolean timed_out;
NMSerialWaitQuietFn callback;
gpointer user_data;
} WaitQuietInfo;
static void
wait_quiet_done (gpointer data)
{
WaitQuietInfo *info = (WaitQuietInfo *) data;
nm_serial_device_pending_done (info->device);
/* Call the callback */
info->callback (info->device, info->timed_out, info->user_data);
/* Free info */
g_slice_free (WaitQuietInfo, info);
}
static gboolean
wait_quiet_quiettime (gpointer data)
{
WaitQuietInfo *info = (WaitQuietInfo *) data;
info->timed_out = FALSE;
g_source_remove (NM_SERIAL_DEVICE_GET_PRIVATE (info->device)->pending);
return FALSE;
}
static gboolean
wait_quiet_got_data (GIOChannel *source,
GIOCondition condition,
gpointer data)
{
WaitQuietInfo *info = (WaitQuietInfo *) data;
gsize bytes_read;
char buf[4096];
GIOStatus status;
if (condition & G_IO_HUP || condition & G_IO_ERR)
return FALSE;
if (condition & G_IO_IN) {
do {
status = g_io_channel_read_chars (source, buf, 4096, &bytes_read, NULL);
if (bytes_read) {
/* Reset the quiet time timeout */
g_source_remove (info->quiet_id);
info->quiet_id = g_timeout_add (info->quiet_time, wait_quiet_quiettime, info);
}
} while (bytes_read == 4096 || status == G_IO_STATUS_AGAIN);
}
return TRUE;
}
void
nm_serial_device_wait_quiet (NMSerialDevice *device,
guint timeout,
guint quiet_time,
NMSerialWaitQuietFn callback,
gpointer user_data)
{
WaitQuietInfo *info;
g_return_if_fail (NM_IS_SERIAL_DEVICE (device));
g_return_if_fail (callback != NULL);
info = g_slice_new0 (WaitQuietInfo);
info->device = device;
info->timed_out = TRUE;
info->callback = callback;
info->user_data = user_data;
info->quiet_id = g_timeout_add (quiet_time,
wait_quiet_timeout,
info);
return nm_serial_device_set_pending (device, timeout, wait_quiet_got_data, info, wait_quiet_done);
}
#endif
typedef struct {
NMSerialDevice *device;
speed_t current_speed;
NMSerialFlashFn callback;
gpointer user_data;
} FlashInfo;
static speed_t
get_speed (NMSerialDevice *device)
{
struct termios options;
tcgetattr (NM_SERIAL_DEVICE_GET_PRIVATE (device)->fd, &options);
return cfgetospeed (&options);
}
static void
set_speed (NMSerialDevice *device, speed_t speed)
{
struct termios options;
int fd;
fd = NM_SERIAL_DEVICE_GET_PRIVATE (device)->fd;
tcgetattr (fd, &options);
cfsetispeed (&options, speed);
cfsetospeed (&options, speed);
options.c_cflag |= (CLOCAL | CREAD);
tcsetattr (fd, TCSANOW, &options);
}
static void
flash_done (gpointer data)
{
FlashInfo *info = (FlashInfo *) data;
NM_SERIAL_DEVICE_GET_PRIVATE (info->device)->pending_id = 0;
info->callback (info->device, info->user_data);
g_slice_free (FlashInfo, info);
}
static gboolean
flash_do (gpointer data)
{
FlashInfo *info = (FlashInfo *) data;
set_speed (info->device, info->current_speed);
return FALSE;
}
guint
nm_serial_device_flash (NMSerialDevice *device,
guint32 flash_time,
NMSerialFlashFn callback,
gpointer user_data)
{
FlashInfo *info;
guint id;
g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), 0);
g_return_val_if_fail (callback != NULL, 0);
info = g_slice_new0 (FlashInfo);
info->device = device;
info->current_speed = get_speed (device);
info->callback = callback;
info->user_data = user_data;
set_speed (device, B0);
id = g_timeout_add_full (G_PRIORITY_DEFAULT,
flash_time,
flash_do,
info,
flash_done);
NM_SERIAL_DEVICE_GET_PRIVATE (device)->pending_id = id;
return id;
}
GIOChannel *
nm_serial_device_get_io_channel (NMSerialDevice *device)
{
NMSerialDevicePrivate *priv;
g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), 0);
priv = NM_SERIAL_DEVICE_GET_PRIVATE (device);
if (priv->channel)
return g_io_channel_ref (priv->channel);
return NULL;
}
NMPPPManager *
nm_serial_device_get_ppp_manager (NMSerialDevice *device)
{
g_return_val_if_fail (NM_IS_SERIAL_DEVICE (device), NULL);
return NM_SERIAL_DEVICE_GET_PRIVATE (device)->ppp_manager;
}
static void
ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data)
{
NMDevice *device = NM_DEVICE (user_data);
switch (status) {
case NM_PPP_STATUS_NETWORK:
nm_device_state_changed (device, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE);
break;
case NM_PPP_STATUS_DISCONNECT:
nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_DISCONNECT);
break;
case NM_PPP_STATUS_DEAD:
nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_PPP_FAILED);
break;
case NM_PPP_STATUS_AUTHENTICATE:
nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
break;
default:
break;
}
}
static void
ppp_ip4_config (NMPPPManager *ppp_manager,
const char *iface,
NMIP4Config *config,
gpointer user_data)
{
NMDevice *device = NM_DEVICE (user_data);
nm_device_set_ip_iface (device, iface);
NM_SERIAL_DEVICE_GET_PRIVATE (device)->pending_ip4_config = g_object_ref (config);
nm_device_activate_schedule_stage4_ip_config_get (device);
}
static void
ppp_stats (NMPPPManager *ppp_manager,
guint32 in_bytes,
guint32 out_bytes,
gpointer user_data)
{
NMSerialDevice *device = NM_SERIAL_DEVICE (user_data);
NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (device);
if (priv->in_bytes != in_bytes || priv->out_bytes != out_bytes) {
priv->in_bytes = in_bytes;
priv->out_bytes = out_bytes;
g_signal_emit (device, signals[PPP_STATS], 0, in_bytes, out_bytes);
}
}
static NMActStageReturn
real_act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
{
NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (device);
NMSerialDeviceClass *serial_class = NM_SERIAL_DEVICE_GET_CLASS (device);
NMActRequest *req;
GError *err = NULL;
NMActStageReturn ret;
const char *ppp_name = NULL;
req = nm_device_get_act_request (device);
g_assert (req);
if (serial_class->get_ppp_name)
ppp_name = serial_class->get_ppp_name (NM_SERIAL_DEVICE (device), req);
priv->ppp_manager = nm_ppp_manager_new (nm_device_get_iface (device));
if (nm_ppp_manager_start (priv->ppp_manager, req, ppp_name, &err)) {
g_signal_connect (priv->ppp_manager, "state-changed",
G_CALLBACK (ppp_state_changed),
device);
g_signal_connect (priv->ppp_manager, "ip4-config",
G_CALLBACK (ppp_ip4_config),
device);
g_signal_connect (priv->ppp_manager, "stats",
G_CALLBACK (ppp_stats),
device);
ret = NM_ACT_STAGE_RETURN_POSTPONE;
} else {
nm_warning ("%s", err->message);
g_error_free (err);
g_object_unref (priv->ppp_manager);
priv->ppp_manager = NULL;
*reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED;
ret = NM_ACT_STAGE_RETURN_FAILURE;
}
return ret;
}
static NMActStageReturn
real_act_stage4_get_ip4_config (NMDevice *device,
NMIP4Config **config,
NMDeviceStateReason *reason)
{
NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (device);
*config = priv->pending_ip4_config;
priv->pending_ip4_config = NULL;
return NM_ACT_STAGE_RETURN_SUCCESS;
}
static void
cleanup_device (NMSerialDevice *device)
{
NMSerialDevicePrivate *priv = NM_SERIAL_DEVICE_GET_PRIVATE (device);
nm_device_set_ip_iface (NM_DEVICE (device), NULL);
if (priv->pending_ip4_config) {
g_object_unref (priv->pending_ip4_config);
priv->pending_ip4_config = NULL;
}
priv->in_bytes = priv->out_bytes = 0;
}
static void
real_deactivate_quickly (NMDevice *device)
{
NMSerialDevice *self = NM_SERIAL_DEVICE (device);
cleanup_device (self);
nm_serial_device_close (self);
}
static guint32
real_get_generic_capabilities (NMDevice *dev)
{
return NM_DEVICE_CAP_NM_SUPPORTED;
}
/*****************************************************************************/
static void
nm_serial_device_init (NMSerialDevice *self)
{
if (getenv ("NM_SERIAL_DEBUG"))
serial_debug = TRUE;
}
static void
finalize (GObject *object)
{
NMSerialDevice *self = NM_SERIAL_DEVICE (object);
cleanup_device (self);
nm_serial_device_close (self);
G_OBJECT_CLASS (nm_serial_device_parent_class)->finalize (object);
}
static void
nm_serial_device_class_init (NMSerialDeviceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
g_type_class_add_private (object_class, sizeof (NMSerialDevicePrivate));
/* Virtual methods */
object_class->finalize = finalize;
parent_class->get_generic_capabilities = real_get_generic_capabilities;
parent_class->act_stage2_config = real_act_stage2_config;
parent_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config;
parent_class->deactivate_quickly = real_deactivate_quickly;
/* Signals */
signals[PPP_STATS] =
g_signal_new ("ppp-stats",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMSerialDeviceClass, ppp_stats),
NULL, NULL,
_nm_marshal_VOID__UINT_UINT,
G_TYPE_NONE, 2,
G_TYPE_UINT, G_TYPE_UINT);
dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass),
&dbus_glib_nm_serial_device_object_info);
}