Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aca25753 authored by Krzysztof Hałasa's avatar Krzysztof Hałasa
Browse files

WAN: Port COSA driver to generic HDLC.



Signed-off-by: default avatarKrzysztof Hałasa <khc@pm.waw.pl>
parent 0bee8db8
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -37,7 +37,7 @@ config HOSTESS_SV11
# The COSA/SRP driver has not been tested as non-modular yet.
# The COSA/SRP driver has not been tested as non-modular yet.
config COSA
config COSA
	tristate "COSA/SRP sync serial boards support"
	tristate "COSA/SRP sync serial boards support"
	depends on ISA && m && ISA_DMA_API
	depends on ISA && m && ISA_DMA_API && HDLC
	---help---
	---help---
	  Driver for COSA and SRP synchronous serial boards.
	  Driver for COSA and SRP synchronous serial boards.


+1 −1
Original line number Original line Diff line number Diff line
@@ -23,7 +23,7 @@ pc300-objs := $(pc300-y)


obj-$(CONFIG_HOSTESS_SV11)	+= z85230.o	syncppp.o	hostess_sv11.o
obj-$(CONFIG_HOSTESS_SV11)	+= z85230.o	syncppp.o	hostess_sv11.o
obj-$(CONFIG_SEALEVEL_4021)	+= z85230.o	syncppp.o	sealevel.o
obj-$(CONFIG_SEALEVEL_4021)	+= z85230.o	syncppp.o	sealevel.o
obj-$(CONFIG_COSA)		+=		syncppp.o	cosa.o
obj-$(CONFIG_COSA)		+= cosa.o
obj-$(CONFIG_FARSYNC)		+= farsync.o
obj-$(CONFIG_FARSYNC)		+= farsync.o
obj-$(CONFIG_DSCC4)             +=				dscc4.o
obj-$(CONFIG_DSCC4)             +=				dscc4.o
obj-$(CONFIG_LANMEDIA)		+=		syncppp.o
obj-$(CONFIG_LANMEDIA)		+=		syncppp.o
+128 −165
Original line number Original line Diff line number Diff line
@@ -2,6 +2,7 @@


/*
/*
 *  Copyright (C) 1995-1997  Jan "Yenya" Kasprzak <kas@fi.muni.cz>
 *  Copyright (C) 1995-1997  Jan "Yenya" Kasprzak <kas@fi.muni.cz>
 *  Generic HDLC port Copyright (C) 2008 Krzysztof Halasa <khc@pm.waw.pl>
 *
 *
 *  This program is free software; you can redistribute it and/or modify
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  it under the terms of the GNU General Public License as published by
@@ -54,7 +55,7 @@
 *
 *
 * The Linux driver (unlike the present *BSD drivers :-) can work even
 * The Linux driver (unlike the present *BSD drivers :-) can work even
 * for the COSA and SRP in one computer and allows each channel to work
 * for the COSA and SRP in one computer and allows each channel to work
 * in one of the three modes (character device, Cisco HDLC, Sync PPP).
 * in one of the two modes (character or network device).
 *
 *
 * AUTHOR
 * AUTHOR
 *
 *
@@ -72,12 +73,6 @@
 * The Comtrol Hostess SV11 driver by Alan Cox
 * The Comtrol Hostess SV11 driver by Alan Cox
 * The Sync PPP/Cisco HDLC layer (syncppp.c) ported to Linux by Alan Cox
 * The Sync PPP/Cisco HDLC layer (syncppp.c) ported to Linux by Alan Cox
 */
 */
/*
 *     5/25/1999 : Marcelo Tosatti <marcelo@conectiva.com.br>
 *             fixed a deadlock in cosa_sppp_open
 */

/* ---------- Headers, macros, data structures ---------- */


#include <linux/module.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
@@ -86,6 +81,7 @@
#include <linux/fs.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/delay.h>
#include <linux/hdlc.h>
#include <linux/errno.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/ioport.h>
#include <linux/netdevice.h>
#include <linux/netdevice.h>
@@ -93,14 +89,12 @@
#include <linux/mutex.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/device.h>
#include <linux/smp_lock.h>
#include <linux/smp_lock.h>

#undef COSA_SLOW_IO	/* for testing purposes only */

#include <asm/io.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/dma.h>
#include <asm/byteorder.h>
#include <asm/byteorder.h>


#include <net/syncppp.h>
#undef COSA_SLOW_IO	/* for testing purposes only */

#include "cosa.h"
#include "cosa.h"


/* Maximum length of the identification string. */
/* Maximum length of the identification string. */
@@ -112,7 +106,6 @@
/* Per-channel data structure */
/* Per-channel data structure */


struct channel_data {
struct channel_data {
	void *if_ptr;	/* General purpose pointer (used by SPPP) */
	int usage;	/* Usage count; >0 for chrdev, -1 for netdev */
	int usage;	/* Usage count; >0 for chrdev, -1 for netdev */
	int num;	/* Number of the channel */
	int num;	/* Number of the channel */
	struct cosa_data *cosa;	/* Pointer to the per-card structure */
	struct cosa_data *cosa;	/* Pointer to the per-card structure */
@@ -136,10 +129,9 @@ struct channel_data {
	wait_queue_head_t txwaitq, rxwaitq;
	wait_queue_head_t txwaitq, rxwaitq;
	int tx_status, rx_status;
	int tx_status, rx_status;


	/* SPPP/HDLC device parts */
	/* generic HDLC device parts */
	struct ppp_device pppdev;
	struct net_device *netdev;
	struct sk_buff *rx_skb, *tx_skb;
	struct sk_buff *rx_skb, *tx_skb;
	struct net_device_stats stats;
};
};


/* cosa->firmware_status bits */
/* cosa->firmware_status bits */
@@ -281,21 +273,19 @@ static int cosa_start_tx(struct channel_data *channel, char *buf, int size);
static void cosa_kick(struct cosa_data *cosa);
static void cosa_kick(struct cosa_data *cosa);
static int cosa_dma_able(struct channel_data *chan, char *buf, int data);
static int cosa_dma_able(struct channel_data *chan, char *buf, int data);


/* SPPP/HDLC stuff */
/* Network device stuff */
static void sppp_channel_init(struct channel_data *chan);
static int cosa_net_attach(struct net_device *dev, unsigned short encoding,
static void sppp_channel_delete(struct channel_data *chan);
			   unsigned short parity);
static int cosa_sppp_open(struct net_device *d);
static int cosa_net_open(struct net_device *d);
static int cosa_sppp_close(struct net_device *d);
static int cosa_net_close(struct net_device *d);
static void cosa_sppp_timeout(struct net_device *d);
static void cosa_net_timeout(struct net_device *d);
static int cosa_sppp_tx(struct sk_buff *skb, struct net_device *d);
static int cosa_net_tx(struct sk_buff *skb, struct net_device *d);
static char *sppp_setup_rx(struct channel_data *channel, int size);
static char *cosa_net_setup_rx(struct channel_data *channel, int size);
static int sppp_rx_done(struct channel_data *channel);
static int cosa_net_rx_done(struct channel_data *channel);
static int sppp_tx_done(struct channel_data *channel, int size);
static int cosa_net_tx_done(struct channel_data *channel, int size);
static int cosa_sppp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static int cosa_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static struct net_device_stats *cosa_net_stats(struct net_device *dev);


/* Character device */
/* Character device */
static void chardev_channel_init(struct channel_data *chan);
static char *chrdev_setup_rx(struct channel_data *channel, int size);
static char *chrdev_setup_rx(struct channel_data *channel, int size);
static int chrdev_rx_done(struct channel_data *channel);
static int chrdev_rx_done(struct channel_data *channel);
static int chrdev_tx_done(struct channel_data *channel, int size);
static int chrdev_tx_done(struct channel_data *channel, int size);
@@ -357,17 +347,17 @@ static void debug_status_in(struct cosa_data *cosa, int status);
static void debug_status_out(struct cosa_data *cosa, int status);
static void debug_status_out(struct cosa_data *cosa, int status);
#endif
#endif



static inline struct channel_data* dev_to_chan(struct net_device *dev)
{
	return (struct channel_data *)dev_to_hdlc(dev)->priv;
}

/* ---------- Initialization stuff ---------- */
/* ---------- Initialization stuff ---------- */


static int __init cosa_init(void)
static int __init cosa_init(void)
{
{
	int i, err = 0;
	int i, err = 0;


	printk(KERN_INFO "cosa v1.08 (c) 1997-2000 Jan Kasprzak <kas@fi.muni.cz>\n");
#ifdef CONFIG_SMP
	printk(KERN_INFO "cosa: SMP found. Please mail any success/failure reports to the author.\n");
#endif
	if (cosa_major > 0) {
	if (cosa_major > 0) {
		if (register_chrdev(cosa_major, "cosa", &cosa_fops)) {
		if (register_chrdev(cosa_major, "cosa", &cosa_fops)) {
			printk(KERN_WARNING "cosa: unable to get major %d\n",
			printk(KERN_WARNING "cosa: unable to get major %d\n",
@@ -414,16 +404,17 @@ static void __exit cosa_exit(void)
{
{
	struct cosa_data *cosa;
	struct cosa_data *cosa;
	int i;
	int i;
	printk(KERN_INFO "Unloading the cosa module\n");


	for (i = 0; i < nr_cards; i++)
	for (i = 0; i < nr_cards; i++)
		device_destroy(cosa_class, MKDEV(cosa_major, i));
		device_destroy(cosa_class, MKDEV(cosa_major, i));
	class_destroy(cosa_class);
	class_destroy(cosa_class);

	for (cosa = cosa_cards; nr_cards--; cosa++) {
	for (cosa = cosa_cards; nr_cards--; cosa++) {
		/* Clean up the per-channel data */
		/* Clean up the per-channel data */
		for (i = 0; i < cosa->nchannels; i++) {
		for (i = 0; i < cosa->nchannels; i++) {
			/* Chardev driver has no alloc'd per-channel data */
			/* Chardev driver has no alloc'd per-channel data */
			sppp_channel_delete(cosa->chan+i);
			unregister_hdlc_device(cosa->chan[i].netdev);
			free_netdev(cosa->chan[i].netdev);
		}
		}
		/* Clean up the per-card data */
		/* Clean up the per-card data */
		kfree(cosa->chan);
		kfree(cosa->chan);
@@ -436,21 +427,6 @@ static void __exit cosa_exit(void)
}
}
module_exit(cosa_exit);
module_exit(cosa_exit);


/*
 * This function should register all the net devices needed for the
 * single channel.
 */
static __inline__ void channel_init(struct channel_data *chan)
{
	sprintf(chan->name, "cosa%dc%d", chan->cosa->num, chan->num);

	/* Initialize the chardev data structures */
	chardev_channel_init(chan);

	/* Register the sppp interface */
	sppp_channel_init(chan);
}
	
static int cosa_probe(int base, int irq, int dma)
static int cosa_probe(int base, int irq, int dma)
{
{
	struct cosa_data *cosa = cosa_cards+nr_cards;
	struct cosa_data *cosa = cosa_cards+nr_cards;
@@ -579,10 +555,40 @@ static int cosa_probe(int base, int irq, int dma)
		err = -ENOMEM;
		err = -ENOMEM;
		goto err_out3;
		goto err_out3;
	}
	}

	for (i = 0; i < cosa->nchannels; i++) {
	for (i = 0; i < cosa->nchannels; i++) {
		cosa->chan[i].cosa = cosa;
		struct channel_data *chan = &cosa->chan[i];
		cosa->chan[i].num = i;

		channel_init(cosa->chan+i);
		chan->cosa = cosa;
		chan->num = i;
		sprintf(chan->name, "cosa%dc%d", chan->cosa->num, i);

		/* Initialize the chardev data structures */
		mutex_init(&chan->rlock);
		init_MUTEX(&chan->wsem);

		/* Register the network interface */
		if (!(chan->netdev = alloc_hdlcdev(chan))) {
			printk(KERN_WARNING "%s: alloc_hdlcdev failed.\n",
			       chan->name);
			goto err_hdlcdev;
		}
		dev_to_hdlc(chan->netdev)->attach = cosa_net_attach;
		dev_to_hdlc(chan->netdev)->xmit = cosa_net_tx;
		chan->netdev->open = cosa_net_open;
		chan->netdev->stop = cosa_net_close;
		chan->netdev->do_ioctl = cosa_net_ioctl;
		chan->netdev->tx_timeout = cosa_net_timeout;
		chan->netdev->watchdog_timeo = TX_TIMEOUT;
		chan->netdev->base_addr = chan->cosa->datareg;
		chan->netdev->irq = chan->cosa->irq;
		chan->netdev->dma = chan->cosa->dma;
		if (register_hdlc_device(chan->netdev)) {
			printk(KERN_WARNING "%s: register_hdlc_device()"
			       " failed.\n", chan->netdev->name);
			free_netdev(chan->netdev);
			goto err_hdlcdev;
		}
	}
	}


	printk (KERN_INFO "cosa%d: %s (%s at 0x%x irq %d dma %d), %d channels\n",
	printk (KERN_INFO "cosa%d: %s (%s at 0x%x irq %d dma %d), %d channels\n",
@@ -590,6 +596,13 @@ static int cosa_probe(int base, int irq, int dma)
		cosa->datareg, cosa->irq, cosa->dma, cosa->nchannels);
		cosa->datareg, cosa->irq, cosa->dma, cosa->nchannels);


	return nr_cards++;
	return nr_cards++;

err_hdlcdev:
	while (i-- > 0) {
		unregister_hdlc_device(cosa->chan[i].netdev);
		free_netdev(cosa->chan[i].netdev);
	}
	kfree(cosa->chan);
err_out3:
err_out3:
	kfree(cosa->bouncebuf);
	kfree(cosa->bouncebuf);
err_out2:
err_out2:
@@ -604,54 +617,19 @@ err_out:
}
}




/*---------- SPPP/HDLC netdevice ---------- */
/*---------- network device ---------- */

static void cosa_setup(struct net_device *d)
{
	d->open = cosa_sppp_open;
	d->stop = cosa_sppp_close;
	d->hard_start_xmit = cosa_sppp_tx;
	d->do_ioctl = cosa_sppp_ioctl;
	d->get_stats = cosa_net_stats;
	d->tx_timeout = cosa_sppp_timeout;
	d->watchdog_timeo = TX_TIMEOUT;
}

static void sppp_channel_init(struct channel_data *chan)
{
	struct net_device *d;
	chan->if_ptr = &chan->pppdev;
	d = alloc_netdev(0, chan->name, cosa_setup);
	if (!d) {
		printk(KERN_WARNING "%s: alloc_netdev failed.\n", chan->name);
		return;
	}
	chan->pppdev.dev = d;
	d->base_addr = chan->cosa->datareg;
	d->irq = chan->cosa->irq;
	d->dma = chan->cosa->dma;
	d->ml_priv = chan;
	sppp_attach(&chan->pppdev);
	if (register_netdev(d)) {
		printk(KERN_WARNING "%s: register_netdev failed.\n", d->name);
		sppp_detach(d);
		free_netdev(d);
		chan->pppdev.dev = NULL;
		return;
	}
}


static void sppp_channel_delete(struct channel_data *chan)
static int cosa_net_attach(struct net_device *dev, unsigned short encoding,
			   unsigned short parity)
{
{
	unregister_netdev(chan->pppdev.dev);
	if (encoding == ENCODING_NRZ && parity == PARITY_CRC16_PR1_CCITT)
	sppp_detach(chan->pppdev.dev);
		return 0;
	free_netdev(chan->pppdev.dev);
	return -EINVAL;
	chan->pppdev.dev = NULL;
}
}


static int cosa_sppp_open(struct net_device *d)
static int cosa_net_open(struct net_device *dev)
{
{
	struct channel_data *chan = d->ml_priv;
	struct channel_data *chan = dev_to_chan(dev);
	int err;
	int err;
	unsigned long flags;
	unsigned long flags;


@@ -662,36 +640,35 @@ static int cosa_sppp_open(struct net_device *d)
	}
	}
	spin_lock_irqsave(&chan->cosa->lock, flags);
	spin_lock_irqsave(&chan->cosa->lock, flags);
	if (chan->usage != 0) {
	if (chan->usage != 0) {
		printk(KERN_WARNING "%s: sppp_open called with usage count %d\n",
		printk(KERN_WARNING "%s: cosa_net_open called with usage count"
			chan->name, chan->usage);
		       " %d\n", chan->name, chan->usage);
		spin_unlock_irqrestore(&chan->cosa->lock, flags);
		spin_unlock_irqrestore(&chan->cosa->lock, flags);
		return -EBUSY;
		return -EBUSY;
	}
	}
	chan->setup_rx = sppp_setup_rx;
	chan->setup_rx = cosa_net_setup_rx;
	chan->tx_done = sppp_tx_done;
	chan->tx_done = cosa_net_tx_done;
	chan->rx_done = sppp_rx_done;
	chan->rx_done = cosa_net_rx_done;
	chan->usage = -1;
	chan->usage = -1;
	chan->cosa->usage++;
	chan->cosa->usage++;
	spin_unlock_irqrestore(&chan->cosa->lock, flags);
	spin_unlock_irqrestore(&chan->cosa->lock, flags);


	err = sppp_open(d);
	err = hdlc_open(dev);
	if (err) {
	if (err) {
		spin_lock_irqsave(&chan->cosa->lock, flags);
		spin_lock_irqsave(&chan->cosa->lock, flags);
		chan->usage = 0;
		chan->usage = 0;
		chan->cosa->usage--;
		chan->cosa->usage--;
		
		spin_unlock_irqrestore(&chan->cosa->lock, flags);
		spin_unlock_irqrestore(&chan->cosa->lock, flags);
		return err;
		return err;
	}
	}


	netif_start_queue(d);
	netif_start_queue(dev);
	cosa_enable_rx(chan);
	cosa_enable_rx(chan);
	return 0;
	return 0;
}
}


static int cosa_sppp_tx(struct sk_buff *skb, struct net_device *dev)
static int cosa_net_tx(struct sk_buff *skb, struct net_device *dev)
{
{
	struct channel_data *chan = dev->ml_priv;
	struct channel_data *chan = dev_to_chan(dev);


	netif_stop_queue(dev);
	netif_stop_queue(dev);


@@ -700,16 +677,16 @@ static int cosa_sppp_tx(struct sk_buff *skb, struct net_device *dev)
	return 0;
	return 0;
}
}


static void cosa_sppp_timeout(struct net_device *dev)
static void cosa_net_timeout(struct net_device *dev)
{
{
	struct channel_data *chan = dev->ml_priv;
	struct channel_data *chan = dev_to_chan(dev);


	if (test_bit(RXBIT, &chan->cosa->rxtx)) {
	if (test_bit(RXBIT, &chan->cosa->rxtx)) {
		chan->stats.rx_errors++;
		chan->netdev->stats.rx_errors++;
		chan->stats.rx_missed_errors++;
		chan->netdev->stats.rx_missed_errors++;
	} else {
	} else {
		chan->stats.tx_errors++;
		chan->netdev->stats.tx_errors++;
		chan->stats.tx_aborted_errors++;
		chan->netdev->stats.tx_aborted_errors++;
	}
	}
	cosa_kick(chan->cosa);
	cosa_kick(chan->cosa);
	if (chan->tx_skb) {
	if (chan->tx_skb) {
@@ -719,13 +696,13 @@ static void cosa_sppp_timeout(struct net_device *dev)
	netif_wake_queue(dev);
	netif_wake_queue(dev);
}
}


static int cosa_sppp_close(struct net_device *d)
static int cosa_net_close(struct net_device *dev)
{
{
	struct channel_data *chan = d->ml_priv;
	struct channel_data *chan = dev_to_chan(dev);
	unsigned long flags;
	unsigned long flags;


	netif_stop_queue(d);
	netif_stop_queue(dev);
	sppp_close(d);
	hdlc_close(dev);
	cosa_disable_rx(chan);
	cosa_disable_rx(chan);
	spin_lock_irqsave(&chan->cosa->lock, flags);
	spin_lock_irqsave(&chan->cosa->lock, flags);
	if (chan->rx_skb) {
	if (chan->rx_skb) {
@@ -742,7 +719,7 @@ static int cosa_sppp_close(struct net_device *d)
	return 0;
	return 0;
}
}


static char *sppp_setup_rx(struct channel_data *chan, int size)
static char *cosa_net_setup_rx(struct channel_data *chan, int size)
{
{
	/*
	/*
	 * We can safely fall back to non-dma-able memory, because we have
	 * We can safely fall back to non-dma-able memory, because we have
@@ -754,66 +731,53 @@ static char *sppp_setup_rx(struct channel_data *chan, int size)
	if (chan->rx_skb == NULL) {
	if (chan->rx_skb == NULL) {
		printk(KERN_NOTICE "%s: Memory squeeze, dropping packet\n",
		printk(KERN_NOTICE "%s: Memory squeeze, dropping packet\n",
			chan->name);
			chan->name);
		chan->stats.rx_dropped++;
		chan->netdev->stats.rx_dropped++;
		return NULL;
		return NULL;
	}
	}
	chan->pppdev.dev->trans_start = jiffies;
	chan->netdev->trans_start = jiffies;
	return skb_put(chan->rx_skb, size);
	return skb_put(chan->rx_skb, size);
}
}


static int sppp_rx_done(struct channel_data *chan)
static int cosa_net_rx_done(struct channel_data *chan)
{
{
	if (!chan->rx_skb) {
	if (!chan->rx_skb) {
		printk(KERN_WARNING "%s: rx_done with empty skb!\n",
		printk(KERN_WARNING "%s: rx_done with empty skb!\n",
			chan->name);
			chan->name);
		chan->stats.rx_errors++;
		chan->netdev->stats.rx_errors++;
		chan->stats.rx_frame_errors++;
		chan->netdev->stats.rx_frame_errors++;
		return 0;
		return 0;
	}
	}
	chan->rx_skb->protocol = htons(ETH_P_WAN_PPP);
	chan->rx_skb->protocol = hdlc_type_trans(chan->rx_skb, chan->netdev);
	chan->rx_skb->dev = chan->pppdev.dev;
	chan->rx_skb->dev = chan->netdev;
	skb_reset_mac_header(chan->rx_skb);
	skb_reset_mac_header(chan->rx_skb);
	chan->stats.rx_packets++;
	chan->netdev->stats.rx_packets++;
	chan->stats.rx_bytes += chan->cosa->rxsize;
	chan->netdev->stats.rx_bytes += chan->cosa->rxsize;
	netif_rx(chan->rx_skb);
	netif_rx(chan->rx_skb);
	chan->rx_skb = NULL;
	chan->rx_skb = NULL;
	chan->pppdev.dev->last_rx = jiffies;
	chan->netdev->last_rx = jiffies;
	return 0;
	return 0;
}
}


/* ARGSUSED */
/* ARGSUSED */
static int sppp_tx_done(struct channel_data *chan, int size)
static int cosa_net_tx_done(struct channel_data *chan, int size)
{
{
	if (!chan->tx_skb) {
	if (!chan->tx_skb) {
		printk(KERN_WARNING "%s: tx_done with empty skb!\n",
		printk(KERN_WARNING "%s: tx_done with empty skb!\n",
			chan->name);
			chan->name);
		chan->stats.tx_errors++;
		chan->netdev->stats.tx_errors++;
		chan->stats.tx_aborted_errors++;
		chan->netdev->stats.tx_aborted_errors++;
		return 1;
		return 1;
	}
	}
	dev_kfree_skb_irq(chan->tx_skb);
	dev_kfree_skb_irq(chan->tx_skb);
	chan->tx_skb = NULL;
	chan->tx_skb = NULL;
	chan->stats.tx_packets++;
	chan->netdev->stats.tx_packets++;
	chan->stats.tx_bytes += size;
	chan->netdev->stats.tx_bytes += size;
	netif_wake_queue(chan->pppdev.dev);
	netif_wake_queue(chan->netdev);
	return 1;
	return 1;
}
}


static struct net_device_stats *cosa_net_stats(struct net_device *dev)
{
	struct channel_data *chan = dev->ml_priv;
	return &chan->stats;
}


/*---------- Character device ---------- */
/*---------- Character device ---------- */


static void chardev_channel_init(struct channel_data *chan)
{
	mutex_init(&chan->rlock);
	init_MUTEX(&chan->wsem);
}

static ssize_t cosa_read(struct file *file,
static ssize_t cosa_read(struct file *file,
	char __user *buf, size_t count, loff_t *ppos)
	char __user *buf, size_t count, loff_t *ppos)
{
{
@@ -1223,16 +1187,15 @@ static int cosa_ioctl_common(struct cosa_data *cosa,
	return -ENOIOCTLCMD;
	return -ENOIOCTLCMD;
}
}


static int cosa_sppp_ioctl(struct net_device *dev, struct ifreq *ifr,
static int cosa_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
	int cmd)
{
{
	int rv;
	int rv;
	struct channel_data *chan = dev->ml_priv;
	struct channel_data *chan = dev_to_chan(dev);
	rv = cosa_ioctl_common(chan->cosa, chan, cmd, (unsigned long)ifr->ifr_data);
	rv = cosa_ioctl_common(chan->cosa, chan, cmd,
	if (rv == -ENOIOCTLCMD) {
			       (unsigned long)ifr->ifr_data);
		return sppp_do_ioctl(dev, ifr, cmd);
	if (rv != -ENOIOCTLCMD)
	}
		return rv;
		return rv;
	return hdlc_ioctl(dev, ifr, cmd);
}
}


static int cosa_chardev_ioctl(struct inode *inode, struct file *file,
static int cosa_chardev_ioctl(struct inode *inode, struct file *file,