From: Ken Mills <[email protected]>

Subject: [PATCH] n_gsm: New network interface for the GSM 27.010 mux

This patch adds the ability to open a network data connection over a mux
virtual tty channel. This is for modems that support data connections
with raw IP frames instead of PPP. On high speed data connections this
eliminates a significant amount of PPP overhead. To use this interface,
the application must first tell the modem to open a network channel on a
virtual tty. Once that has been accomplished, the app will issue an IOCTL
on that virtual tty to create the network interface.

The two IOCTL commands are:

        ioctl( fd, GSMIOC_ENABLE_NET );

        ioctl( fd, GSMIOC_DISABLE_NET );

Signed-off-by: Ken Mills <[email protected]>
---
 drivers/char/n_gsm.c   |  206 +++++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/gsmmux.h |    3 +-
 2 files changed, 205 insertions(+), 4 deletions(-)

diff --git a/drivers/char/n_gsm.c b/drivers/char/n_gsm.c
index e4089c4..b809d9e 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 */
@@ -1593,6 +1613,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
@@ -2432,6 +2453,168 @@ 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 struct net_device_stats *gsm_mux_net_get_stats(struct net_device *net)
+{
+       return &((struct gsm_mux_net *)netdev_priv(net))->stats;
+}
+
+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);
+
+       /* check the available fifo space for the packet*/
+       len = kfifo_size(dlci->fifo) - kfifo_len(dlci->fifo);
+       if (len < skb->len)
+               return NETDEV_TX_BUSY;
+
+       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 += skb->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);
+
+       /* 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,
+               .ndo_get_stats          = gsm_mux_net_get_stats,
+       };
+       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;
+       }
+       return net->ifindex;    /* return network index */
+
+error_ret:
+       return retval;
+}
+
 
 /* Line discipline for real tty */
 struct tty_ldisc_ops tty_ldisc_packet = {
@@ -2633,7 +2816,24 @@ 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;  /* return net interface index or error code */
+
+       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..79aacde 100644
--- a/include/linux/gsmmux.h
+++ b/include/linux/gsmmux.h
@@ -20,6 +20,7 @@ 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

Reply via email to