Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 259323
b: refs/heads/master
c: bcd5abe
h: refs/heads/master
i:
  259321: f5dcd5f
  259319: 2171f4f
v: v3
  • Loading branch information
Russ Gorby authored and Greg Kroah-Hartman committed Jul 1, 2011
1 parent 9633a61 commit 0144b41
Show file tree
Hide file tree
Showing 3 changed files with 282 additions and 10 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: d50f6dcaf22a3234a65ae4f6087173e66b7fff56
refs/heads/master: bcd5abe28f40cc6a935d3339cde27976f6be3f1a
279 changes: 270 additions & 9 deletions trunk/drivers/tty/n_gsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@
#include <linux/serial.h>
#include <linux/kfifo.h>
#include <linux/skbuff.h>
#include <net/arp.h>
#include <linux/ip.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/gsmmux.h>

static int debug;
Expand All @@ -77,8 +81,24 @@ module_param(debug, int, 0600);
* Semi-arbitrary 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 1500
#define MAX_MTU 1500
#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 kref ref;
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
Expand Down Expand Up @@ -113,6 +133,7 @@ struct gsm_dlci {
#define DLCI_OPENING 1 /* Sending SABM not seen UA */
#define DLCI_OPEN 2 /* SABM/UA complete */
#define DLCI_CLOSING 3 /* Sending DISC not seen UA/DM */
struct mutex mutex;

/* Link layer */
spinlock_t lock; /* Protects the internal state */
Expand All @@ -123,6 +144,7 @@ struct gsm_dlci {
struct kfifo *fifo; /* Queue fifo for the DLCI */
struct kfifo _fifo; /* For new fifo API porting only */
int adaption; /* Adaption layer in use */
int prev_adaption;
u32 modem_rx; /* Our incoming virtual modem lines */
u32 modem_tx; /* Our outgoing modem lines */
int dead; /* Refuse re-open */
Expand All @@ -134,6 +156,8 @@ struct gsm_dlci {
struct sk_buff_head skb_list; /* Queued frames */
/* Data handling callback */
void (*data)(struct gsm_dlci *dlci, u8 *data, int len);
void (*prev_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 */
Expand Down Expand Up @@ -880,8 +904,10 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm,
}
memcpy(dp, skb_pull(dlci->skb, len), len);
__gsm_data_queue(dlci, msg);
if (last)
if (last) {
kfree_skb(dlci->skb);
dlci->skb = NULL;
}
return size;
}

Expand Down Expand Up @@ -914,7 +940,7 @@ static void gsm_dlci_data_sweep(struct gsm_mux *gsm)
i++;
continue;
}
if (dlci->adaption < 3)
if (dlci->adaption < 3 && !dlci->net)
len = gsm_dlci_data_output(gsm, dlci);
else
len = gsm_dlci_data_output_framed(gsm, dlci);
Expand All @@ -941,9 +967,12 @@ static void gsm_dlci_data_kick(struct gsm_dlci *dlci)

spin_lock_irqsave(&dlci->gsm->tx_lock, flags);
/* If we have nothing running then we need to fire up */
if (dlci->gsm->tx_bytes == 0)
gsm_dlci_data_output(dlci->gsm, dlci);
else if (dlci->gsm->tx_bytes < TX_THRESH_LO)
if (dlci->gsm->tx_bytes == 0) {
if (dlci->net)
gsm_dlci_data_output_framed(dlci->gsm, dlci);
else
gsm_dlci_data_output(dlci->gsm, dlci);
} else if (dlci->gsm->tx_bytes < TX_THRESH_LO)
gsm_dlci_data_sweep(dlci->gsm);
spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags);
}
Expand Down Expand Up @@ -1577,6 +1606,7 @@ static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr)
if (dlci == NULL)
return NULL;
spin_lock_init(&dlci->lock);
mutex_init(&dlci->mutex);
dlci->fifo = &dlci->_fifo;
if (kfifo_alloc(&dlci->_fifo, 4096, GFP_KERNEL) < 0) {
kfree(dlci);
Expand Down Expand Up @@ -2059,7 +2089,6 @@ struct gsm_mux *gsm_alloc_mux(void)
gsm->t2 = T2;
gsm->n2 = N2;
gsm->ftype = UIH;
gsm->initiator = 0;
gsm->adaption = 1;
gsm->encoding = 1;
gsm->mru = 64; /* Default to encoding 1 so these should be 64 */
Expand Down Expand Up @@ -2478,6 +2507,210 @@ 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 void dlci_net_free(struct gsm_dlci *dlci)
{
if (!dlci->net) {
WARN_ON(1);
return;
}
dlci->adaption = dlci->prev_adaption;
dlci->data = dlci->prev_data;
free_netdev(dlci->net);
dlci->net = NULL;
}
static void net_free(struct kref *ref)
{
struct gsm_mux_net *mux_net;
struct gsm_dlci *dlci;

mux_net = container_of(ref, struct gsm_mux_net, ref);
dlci = mux_net->dlci;

if (dlci->net) {
unregister_netdev(dlci->net);
dlci_net_free(dlci);
}
}

static int gsm_mux_net_start_xmit(struct sk_buff *skb,
struct net_device *net)
{
struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net);
struct gsm_dlci *dlci = mux_net->dlci;
kref_get(&mux_net->ref);

skb_queue_head(&dlci->skb_list, skb);
STATS(net).tx_packets++;
STATS(net).tx_bytes += skb->len;
gsm_dlci_data_kick(dlci);
/* And tell the kernel when the last transmit started. */
net->trans_start = jiffies;
kref_put(&mux_net->ref, net_free);
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;
struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net);
kref_get(&mux_net->ref);

/* Allocate an sk_buff */
skb = dev_alloc_skb(size + NET_IP_ALIGN);
if (!skb) {
/* We got no receive buffer. */
STATS(net).rx_dropped++;
kref_put(&mux_net->ref, net_free);
return;
}
skb_reserve(skb, NET_IP_ALIGN);
memcpy(skb_put(skb, size), in_buf, 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;
kref_put(&mux_net->ref, net_free);
return;
}

int gsm_change_mtu(struct net_device *net, int new_mtu)
{
struct gsm_mux_net *mux_net = (struct gsm_mux_net *)netdev_priv(net);
if ((new_mtu < 8) || (new_mtu > mux_net->dlci->gsm->mtu))
return -EINVAL;
net->mtu = new_mtu;
return 0;
}

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,
.ndo_change_mtu = gsm_change_mtu,
};

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->tx_queue_len = 10;
}


/* caller holds the dlci mutex */
static void gsm_destroy_network(struct gsm_dlci *dlci)
{
struct gsm_mux_net *mux_net;

pr_debug("destroy network interface");
if (!dlci->net)
return;
mux_net = (struct gsm_mux_net *)netdev_priv(dlci->net);
kref_put(&mux_net->ref, net_free);
}


/* caller holds the dlci mutex */
static int gsm_create_network(struct gsm_dlci *dlci, struct gsm_netconfig *nc)
{
char *netname;
int retval = 0;
struct net_device *net;
struct gsm_mux_net *mux_net;

if (!capable(CAP_NET_ADMIN))
return -EPERM;

/* Already in a non tty mode */
if (dlci->adaption > 2)
return -EBUSY;

if (nc->protocol != htons(ETH_P_IP))
return -EPROTONOSUPPORT;

if (nc->adaption != 3 && nc->adaption != 4)
return -EPROTONOSUPPORT;

pr_debug("create network interface");

netname = "gsm%d";
if (nc->if_name[0] != '\0')
netname = nc->if_name;
net = alloc_netdev(sizeof(struct gsm_mux_net),
netname,
gsm_mux_net_init);
if (!net) {
pr_err("alloc_netdev failed");
return -ENOMEM;
}
net->mtu = dlci->gsm->mtu;
mux_net = (struct gsm_mux_net *)netdev_priv(net);
mux_net->dlci = dlci;
kref_init(&mux_net->ref);
strncpy(nc->if_name, net->name, IFNAMSIZ); /* return net name */

/* reconfigure dlci for network */
dlci->prev_adaption = dlci->adaption;
dlci->prev_data = dlci->data;
dlci->adaption = nc->adaption;
dlci->data = gsm_mux_rx_netchar;
dlci->net = net;

pr_debug("register netdev");
retval = register_netdev(net);
if (retval) {
pr_err("network register fail %d\n", retval);
dlci_net_free(dlci);
return retval;
}
return net->ifindex; /* return network index */
}

/* Line discipline for real tty */
struct tty_ldisc_ops tty_ldisc_packet = {
Expand Down Expand Up @@ -2598,6 +2831,9 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp)
struct gsm_dlci *dlci = tty->driver_data;
if (dlci == NULL)
return;
mutex_lock(&dlci->mutex);
gsm_destroy_network(dlci);
mutex_unlock(&dlci->mutex);
if (tty_port_close_start(&dlci->port, tty, filp) == 0)
return;
gsm_dlci_begin_close(dlci);
Expand Down Expand Up @@ -2679,7 +2915,32 @@ static int gsmtty_tiocmset(struct tty_struct *tty,
static int gsmtty_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
return -ENOIOCTLCMD;
struct gsm_dlci *dlci = tty->driver_data;
struct gsm_netconfig nc;
int index;

switch (cmd) {
case GSMIOC_ENABLE_NET:
if (copy_from_user(&nc, (void __user *)arg, sizeof(nc)))
return -EFAULT;
nc.if_name[IFNAMSIZ-1] = '\0';
/* return net interface index or error code */
mutex_lock(&dlci->mutex);
index = gsm_create_network(dlci, &nc);
mutex_unlock(&dlci->mutex);
if (copy_to_user((void __user *)arg, &nc, sizeof(nc)))
return -EFAULT;
return index;
case GSMIOC_DISABLE_NET:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
mutex_lock(&dlci->mutex);
gsm_destroy_network(dlci);
mutex_unlock(&dlci->mutex);
return 0;
default:
return -ENOIOCTLCMD;
}
}

static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old)
Expand Down
11 changes: 11 additions & 0 deletions trunk/include/linux/gsmmux.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,16 @@ struct gsm_config
#define GSMIOC_GETCONF _IOR('G', 0, struct gsm_config)
#define GSMIOC_SETCONF _IOW('G', 1, struct gsm_config)

struct gsm_netconfig {
unsigned int adaption; /* Adaption to use in network mode */
unsigned short protocol;/* Protocol to use - only ETH_P_IP supported */
unsigned short unused2;
char if_name[IFNAMSIZ]; /* interface name format string */
__u8 unused[28]; /* For future use */
};

#define GSMIOC_ENABLE_NET _IOW('G', 2, struct gsm_netconfig)
#define GSMIOC_DISABLE_NET _IO('G', 3)


#endif

0 comments on commit 0144b41

Please sign in to comment.