This adds a network interface to the existing 27.010 mux.
Signed-off-by: Ken Mills <[email protected]>
---
drivers/char/n_gsm.c | 195 +++++++++++++++++++++++++++++++++++++++++++++++-
include/linux/gsmmux.h | 2 +
2 files changed, 193 insertions(+), 4 deletions(-)
diff --git a/drivers/char/n_gsm.c b/drivers/char/n_gsm.c
index 597deab..b3e8d91 100644
--- a/drivers/char/n_gsm.c
+++ b/drivers/char/n_gsm.c
@@ -60,6 +60,10 @@
#include <linux/kfifo.h>
#include <linux/skbuff.h>
#include <linux/gsmmux.h>
+#include <net/arp.h>
+#include <linux/ip.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
static int debug;
module_param(debug, int, 0600);
@@ -76,8 +80,23 @@ module_param(debug, int, 0600);
/* Semi-arbitary buffer size limits. 0710 is normally run with 32-64 byte
limits so this is plenty */
-#define MAX_MRU 512
-#define MAX_MTU 512
+#define MAX_MRU 1509
+#define MAX_MTU 1509
+#define GSM_NET_TX_TIMEOUT (HZ*10)
+
+/**
+ * struct gsm_mux_net - network interface
+ * @struct gsm_dlci* dlci
+ * @struct net_device_stats stats;
+ *
+ * Created when net interface is initialized.
+ **/
+struct gsm_mux_net {
+ struct gsm_dlci *dlci;
+ struct net_device_stats stats;
+};
+
+#define STATS(net) (((struct gsm_mux_net *)netdev_priv(net))->stats)
/*
* Each block of data we have queued to go out is in the form of
@@ -133,6 +152,7 @@ struct gsm_dlci {
struct sk_buff_head skb_list; /* Queued frames */
/* Data handling callback */
void (*data)(struct gsm_dlci *dlci, u8 *data, int len);
+ struct net_device *net; /* network interface, if created */
};
/* DLCI 0, 62/63 are special or reseved see gsmtty_open */
@@ -493,7 +513,6 @@ static void gsm_print_packet(const char *hdr, int addr, int
cr,
pr_debug("\n");
}
-
/*
* Link level transmission side
*/
@@ -1515,6 +1534,7 @@ static void gsm_dlci_data(struct gsm_dlci *dlci, u8
*data, int clen)
pr_debug("gsm_dlci_data: %d bytes for tty %p\n", len, tty);
if (tty) {
+
switch (dlci->adaption) {
/* Unsupported types */
/* Packetised interruptible data */
@@ -1613,6 +1633,7 @@ static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux
*gsm, int addr)
dlci->addr = addr;
dlci->adaption = gsm->adaption;
dlci->state = DLCI_CLOSED;
+ dlci->net = NULL; /* network not initially created */
if (addr)
dlci->data = gsm_dlci_data;
else
@@ -2479,6 +2500,156 @@ static int gsmld_ioctl(struct tty_struct *tty, struct
file *file,
}
}
+/*
+ * Network interface
+ *
+ */
+
+static int gsm_mux_net_open(struct net_device *net)
+{
+ pr_debug("%s called\n", __func__);
+ netif_start_queue(net);
+ return 0;
+}
+
+static int gsm_mux_net_close(struct net_device *net)
+{
+ netif_stop_queue(net);
+ return 0;
+}
+
+static int gsm_mux_net_start_xmit(struct sk_buff *skb,
+ struct net_device *net)
+{
+ unsigned int len;
+ struct gsm_dlci *dlci = ((struct gsm_mux_net *)netdev_priv(net))->dlci;
+
+ netif_stop_queue(net);
+
+ len = kfifo_in_locked(dlci->fifo, skb->data, skb->len, &dlci->lock);
+ len = skb->len - len;
+ dev_kfree_skb(skb);
+ netif_start_queue(net);
+ gsm_dlci_data_kick(dlci);
+
+ if (len)
+ STATS(net).tx_dropped++;
+ else {
+ STATS(net).tx_packets++;
+ STATS(net).tx_bytes += len;
+ /* And tell the kernel when the last transmit started. */
+ net->trans_start = jiffies;
+ }
+
+ return NETDEV_TX_OK;
+}
+
+/* called when a packet did not ack after watchdogtimeout */
+static void gsm_mux_net_tx_timeout(struct net_device *net)
+{
+ /* Tell syslog we are hosed. */
+ dev_dbg(&net->dev, "Tx timed out.\n");
+
+ /* Update statistics */
+ STATS(net).tx_errors++;
+}
+
+static void gsm_mux_rx_netchar(struct gsm_dlci *dlci,
+ unsigned char *in_buf, int size)
+{
+ struct net_device *net = dlci->net;
+ struct sk_buff *skb;
+
+ /* Allocate an sk_buff */
+ skb = dev_alloc_skb(size + NET_IP_ALIGN);
+ if (!skb) {
+ /* We got no receive buffer. */
+ STATS(net).rx_dropped++;
+ return;
+ }
+ skb_reserve(skb, NET_IP_ALIGN);
+ memcpy(skb->data, in_buf, size);
+ skb_put(skb, size);
+ skb->dev = net;
+ skb->protocol = __constant_htons(ETH_P_IP);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ /* Ship it off to the kernel */
+ netif_rx(skb);
+
+ /* update out statistics */
+ STATS(net).rx_packets++;
+ STATS(net).rx_bytes += size;
+ return;
+}
+
+static void gsm_mux_net_init(struct net_device *net)
+{
+ static const struct net_device_ops gsm_netdev_ops = {
+ .ndo_open = gsm_mux_net_open,
+ .ndo_stop = gsm_mux_net_close,
+ .ndo_start_xmit = gsm_mux_net_start_xmit,
+ .ndo_tx_timeout = gsm_mux_net_tx_timeout,
+ };
+ net->netdev_ops = &gsm_netdev_ops;
+
+ /* fill in the other fields */
+ net->watchdog_timeo = GSM_NET_TX_TIMEOUT;
+ net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ net->type = ARPHRD_NONE;
+ net->mtu = MAX_MTU;
+ net->tx_queue_len = 10;
+}
+
+
+static void gsm_destroy_network(struct gsm_dlci *dlci)
+{
+
+ pr_debug("destroy network interface");
+ if (dlci->net) {
+ unregister_netdev(dlci->net);
+ free_netdev(dlci->net);
+ dlci->net = 0;
+ dlci->data = gsm_dlci_data;
+ }
+}
+
+
+static int gsm_create_network(struct gsm_dlci *dlci)
+{
+ char netname[6];
+ int retval = 0;
+ int channel = dlci->addr;
+ struct net_device *net;
+ struct gsm_mux_net *mux_net;
+
+ pr_debug("create network interface");
+ netname[5] = 0;
+ snprintf(netname, 6, "gsm%02d", channel);
+ net = alloc_netdev(sizeof(struct gsm_mux_net),
+ netname,
+ gsm_mux_net_init);
+ if (!net) {
+ pr_err("alloc_netdev failed");
+ retval = -ENOMEM;
+ goto error_ret;
+ }
+
+ dlci->net = net;
+ dlci->data = gsm_mux_rx_netchar;
+ mux_net = (struct gsm_mux_net *)netdev_priv(net);
+ memset(mux_net, 0, sizeof(struct gsm_mux_net));
+ mux_net->dlci = dlci;
+ pr_debug("register netdev");
+ retval = register_netdev(net);
+ if (retval) {
+ pr_err("network register fail %d\n", retval);
+ goto error_ret;
+ }
+
+error_ret:
+ return retval;
+}
/* Line discipline for real tty */
struct tty_ldisc_ops tty_ldisc_packet = {
@@ -2680,7 +2851,23 @@ static int gsmtty_tiocmset(struct tty_struct *tty,
struct file *filp,
static int gsmtty_ioctl(struct tty_struct *tty, struct file *filp,
unsigned int cmd, unsigned long arg)
{
- return -ENOIOCTLCMD;
+ struct gsm_dlci *dlci = tty->driver_data;
+ int retval = 0;
+
+ switch (cmd) {
+ case GSMIOC_ENABLE_NET:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ retval = gsm_create_network(dlci);
+ return retval;
+ case GSMIOC_DISABLE_NET:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ gsm_destroy_network(dlci);
+ return 0;
+ default:
+ return n_tty_ioctl_helper(tty, filp, cmd, arg);
+ }
}
static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old)
diff --git a/include/linux/gsmmux.h b/include/linux/gsmmux.h
index 378de41..314c681 100644
--- a/include/linux/gsmmux.h
+++ b/include/linux/gsmmux.h
@@ -20,6 +20,8 @@ struct gsm_config
#define GSMIOC_GETCONF _IOR('G', 0, struct gsm_config)
#define GSMIOC_SETCONF _IOW('G', 1, struct gsm_config)
+#define GSMIOC_ENABLE_NET _IOW('G', 2, struct gsm_config)
+#define GSMIOC_DISABLE_NET _IOW('G', 3, struct gsm_config)
#endif
--
1.7.0.4
_______________________________________________
MeeGo-kernel mailing list
[email protected]
http://lists.meego.com/listinfo/meego-kernel