patch-2.2.16 linux/drivers/char/synclink.c
Next file: linux/drivers/char/tty_io.c
Previous file: linux/drivers/char/serial.c
Back to the patch index
Back to the overall index
- Lines: 1241
- Date:
Wed Jun 7 14:26:42 2000
- Orig file:
v2.2.15/linux/drivers/char/synclink.c
- Orig date:
Wed May 3 17:16:39 2000
diff -urN v2.2.15/linux/drivers/char/synclink.c linux/drivers/char/synclink.c
@@ -1,7 +1,7 @@
/*
* linux/drivers/char/synclink.c
*
- * ==FILEDATE 19991207==
+ * ==FILEDATE 20000220==
*
* Device driver for Microgate SyncLink ISA and PCI
* high speed multiprotocol serial adapters.
@@ -33,6 +33,13 @@
* This driver has been tested with a slightly modified ppp.c driver
* for synchronous PPP.
*
+ * 2000/02/16
+ * Added interface for syncppp.c driver (an alternate synchronous PPP
+ * implementation that also supports Cisco HDLC). Each device instance
+ * registers as a tty device AND a network device (if dosyncppp option
+ * is set for the device). The functionality is determined by which
+ * device interface is opened.
+ *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -73,6 +80,8 @@
#include <linux/mm.h>
#include <linux/malloc.h>
+#include <linux/netdevice.h>
+
#if LINUX_VERSION_CODE >= VERSION(2,1,0)
#include <linux/vmalloc.h>
#include <linux/init.h>
@@ -101,6 +110,24 @@
#define set_current_state(a) current->state = (a)
#endif
+#ifdef CONFIG_SYNCLINK_SYNCPPP_MODULE
+#define CONFIG_SYNCLINK_SYNCPPP 1
+#endif
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+#if LINUX_VERSION_CODE < VERSION(2,3,43)
+#include "../net/syncppp.h"
+#define net_device device
+#define netif_stop_queue(a) (a)->tbusy = 1
+#define netif_start_queue(a) (a)->tbusy = 0
+#define netif_wake_queue(a) (a)->tbusy = 0; mark_bh(NET_BH)
+#define netif_queue_stopped(a) ((a)->tbusy)
+#else
+#include "../net/wan/syncppp.h"
+#define netif_queue_stopped(a) test_bit(LINK_STATE_XOFF,&(a)->state)
+#endif
+#endif
+
#if LINUX_VERSION_CODE >= VERSION(2,1,4)
#include <asm/segment.h>
#define GET_USER(error,value,addr) error = get_user(value,addr)
@@ -158,6 +185,7 @@
#define SERIAL_TYPE_NORMAL 1
#define SERIAL_TYPE_CALLOUT 2
typedef int spinlock_t;
+#define spin_lock_init(a)
#define spin_lock_irqsave(a,b) {save_flags((b));cli();}
#define spin_unlock_irqrestore(a,b) {restore_flags((b));}
#define spin_lock(a)
@@ -207,22 +235,11 @@
/* The queue of BH actions to be performed */
-#define BH_TYPE_RECEIVE_DATA 1
-#define BH_TYPE_RECEIVE_STATUS 2
-#define BH_TYPE_RECEIVE_DMA 3
-#define BH_TYPE_TRANSMIT_DATA 4
-#define BH_TYPE_TRANSMIT_STATUS 5
-#define BH_TYPE_STATUS 6
-
-typedef struct _BH_EVENT {
- unsigned char type; /* Set by interrupt routines to reqst */
- u16 status;
- struct _BH_EVENT *link;
-
-} BH_EVENT, *BH_QUEUE; /* Queue of BH actions to be done. */
+#define BH_RECEIVE 1
+#define BH_TRANSMIT 2
+#define BH_STATUS 4
-#define MAX_BH_QUEUE_ENTRIES 200
-#define IO_PIN_SHUTDOWN_LIMIT (MAX_BH_QUEUE_ENTRIES/4)
+#define IO_PIN_SHUTDOWN_LIMIT 100
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
@@ -242,6 +259,7 @@
*/
struct mgsl_struct {
+ void *if_ptr; /* General purpose pointer (used by SPPP) */
int magic;
int flags;
int count; /* count of opens */
@@ -283,12 +301,8 @@
u32 max_frame_size; /* as set by device config */
- BH_EVENT bh_queue[MAX_BH_QUEUE_ENTRIES]; /* Pointer to alloc'ed block */
- BH_QUEUE bh_queue_head; /* Queue of BH actions */
- BH_QUEUE bh_queue_tail; /* Tail of above for perf. */
- BH_QUEUE free_bh_queue_head; /* Queue of Free BH */
- BH_QUEUE free_bh_queue_tail; /* Tail of above for perf. */
- BH_QUEUE bh_action; /* Action for BH */
+ u32 pending_bh;
+
int bh_running; /* Protection from multiple */
int isr_overflow;
int bh_requested;
@@ -366,6 +380,20 @@
BOOLEAN loopmode_send_done_requested;
struct _input_signal_events input_signal_events;
+
+ /* SPPP/Cisco HDLC device parts */
+ int netcount;
+ int dosyncppp;
+ spinlock_t netlock;
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+ struct ppp_device pppdev;
+ char netname[10];
+ struct net_device *netdev;
+ struct net_device_stats netstats;
+#if LINUX_VERSION_CODE >= VERSION(2,3,43)
+ struct net_device netdevice;
+#endif
+#endif
};
#define MGSL_MAGIC 0x5401
@@ -767,6 +795,22 @@
void usc_loopmode_send_done( struct mgsl_struct * info );
int usc_loopmode_send_active( struct mgsl_struct * info );
+int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg);
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+/* SPPP/HDLC stuff */
+void mgsl_sppp_init(struct mgsl_struct *info);
+void mgsl_sppp_delete(struct mgsl_struct *info);
+int mgsl_sppp_open(struct net_device *d);
+int mgsl_sppp_close(struct net_device *d);
+void mgsl_sppp_tx_timeout(struct net_device *d);
+int mgsl_sppp_tx(struct sk_buff *skb, struct net_device *d);
+void mgsl_sppp_rx_done(struct mgsl_struct *info, char *buf, int size);
+void mgsl_sppp_tx_done(struct mgsl_struct *info);
+int mgsl_sppp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+struct net_device_stats *mgsl_net_stats(struct net_device *dev);
+#endif
+
/*
* Defines a BUS descriptor value for the PCI adapter
* local bus address ranges.
@@ -828,14 +872,9 @@
* Bottom half interrupt handlers
*/
void mgsl_bh_handler(void* Context);
-void mgsl_bh_receive_dma( struct mgsl_struct *info, unsigned short status );
-void mgsl_bh_transmit_data( struct mgsl_struct *info, unsigned short Datacount );
-void mgsl_bh_status_handler( struct mgsl_struct *info, unsigned short status );
-
-void mgsl_format_bh_queue( struct mgsl_struct *info );
-void mgsl_bh_queue_put( struct mgsl_struct *info, unsigned char type, unsigned short status );
-int mgsl_bh_queue_get( struct mgsl_struct *info );
-
+void mgsl_bh_receive(struct mgsl_struct *info);
+void mgsl_bh_transmit(struct mgsl_struct *info);
+void mgsl_bh_status(struct mgsl_struct *info);
/*
* Interrupt handler routines and dispatch table.
@@ -911,7 +950,7 @@
static int dma[MAX_ISA_DEVICES] = {0,};
static int debug_level = 0;
static int maxframe[MAX_TOTAL_DEVICES] = {0,};
-
+static int dosyncppp[MAX_TOTAL_DEVICES] = {0,};
#if LINUX_VERSION_CODE >= VERSION(2,1,0)
MODULE_PARM(break_on_load,"i");
@@ -922,10 +961,11 @@
MODULE_PARM(dma,"1-" __MODULE_STRING(MAX_ISA_DEVICES) "i");
MODULE_PARM(debug_level,"i");
MODULE_PARM(maxframe,"1-" __MODULE_STRING(MAX_TOTAL_DEVICES) "i");
+MODULE_PARM(dosyncppp,"1-" __MODULE_STRING(MAX_TOTAL_DEVICES) "i");
#endif
static char *driver_name = "SyncLink serial driver";
-static char *driver_version = "1.15";
+static char *driver_version = "1.18";
static struct tty_driver serial_driver, callout_driver;
static int serial_refcount;
@@ -1037,152 +1077,46 @@
* Bottom half work queue access functions
*/
-/* mgsl_format_bh_queue()
- *
- * Initialize the bottom half processing queue
- *
- * Arguments: info pointer to device instance data
- * Return Value: None
- */
-void mgsl_format_bh_queue( struct mgsl_struct *info )
-{
- BH_QUEUE bh_queue = info->bh_queue;
- int i;
-
- /* go through sequentially tacking the little bits together */
-
- for ( i=0; i < MAX_BH_QUEUE_ENTRIES; i++ ) {
- if ( info->free_bh_queue_tail == NULL )
- info->free_bh_queue_head = bh_queue;
- else
- info->free_bh_queue_tail->link = bh_queue;
- info->free_bh_queue_tail = bh_queue++;
- }
-
- /* As a safety measure, mark the end of the chain with a NULL */
- info->free_bh_queue_tail->link = NULL;
- info->isr_overflow=0;
-
-} /* end of mgsl_format_bh_queue() */
-
-/* mgsl_bh_queue_put()
- *
- * Add a BH event to the BH queue
- *
- * Arguments: info pointer to device instance data
- * type BH event type
- * status BH event status
- *
- * Return Value: None
+/* mgsl_bh_action() Return next bottom half action to perform.
+ * Return Value: BH action code or 0 if nothing to do.
*/
-void mgsl_bh_queue_put( struct mgsl_struct *info, unsigned char type, unsigned short status )
-{
- BH_EVENT *event = info->free_bh_queue_head;
-
- if ( event != NULL ) {
- /* remove free element from head of free list */
- info->free_bh_queue_head = event->link;
- event->link = NULL;
-
- /* file out new BH event */
- event->type = type;
- event->status = status;
-
- /* add element to tail of pending list */
- if ( info->bh_queue_head != NULL ){
- /* BH queue is not empty, add current element to tail */
- info->bh_queue_tail->link = event;
- } else {
- /* the BH queue is empty so this element becomes the head of queue */
- info->bh_queue_head = event;
- }
-
- /* the new element becomes tail of queue */
- info->bh_queue_tail = event;
- } else {
- /* No more free BH action elements in queue. */
- /* This happens when too many interrupts are occuring */
- /* for the mgsl_bh_handler to process so set a flag. */
-
- info->isr_overflow = 1;
- }
-
-} /* end of mgsl_bh_queue_put() */
-
-/* mgsl_bh_queue_get()
- *
- * Free the current work item (if any) and get the
- * next work item from the head of the pending work item queue.
- *
- * Effects:
- *
- * If a BH action element is available on the BH action queue
- * then the head of the queue is removed and bh_action
- * is set to point to the removed element.
- *
- * Arguments: info pointer to device instance data
- * Return Value: 1 if BH action removed from queue
- */
-int mgsl_bh_queue_get( struct mgsl_struct *info )
+int mgsl_bh_action(struct mgsl_struct *info)
{
unsigned long flags;
+ int rc = 0;
spin_lock_irqsave(&info->irq_spinlock,flags);
- if ( info->bh_action ) {
- /* free the current work item */
- if ( info->free_bh_queue_head != NULL ){
- /* free queue is not empty, add current element to tail */
- info->free_bh_queue_tail->link = info->bh_action;
- } else {
- /* free queue is empty so this element becomes the head of queue */
- info->free_bh_queue_head = info->bh_action;
- }
-
- /* add element to tail of free queue */
- info->free_bh_queue_tail = info->bh_action;
- info->free_bh_queue_tail->link = NULL;
+ if (info->pending_bh & BH_RECEIVE) {
+ info->pending_bh &= ~BH_RECEIVE;
+ rc = BH_RECEIVE;
+ } else if (info->pending_bh & BH_TRANSMIT) {
+ info->pending_bh &= ~BH_TRANSMIT;
+ rc = BH_TRANSMIT;
+ } else if (info->pending_bh & BH_STATUS) {
+ info->pending_bh &= ~BH_STATUS;
+ rc = BH_STATUS;
}
-
- /* attempt to remove element from head of queue */
- info->bh_action = info->bh_queue_head;
- if ( info->bh_action != NULL ){
- /* BH queue is not empty, remove element from queue head */
- info->bh_queue_head = info->bh_action->link;
- spin_unlock_irqrestore(&info->irq_spinlock,flags);
- return 1;
+ if (!rc) {
+ /* Mark BH routine as complete */
+ info->bh_running = 0;
+ info->bh_requested = 0;
}
- if ( info->isr_overflow ) {
- if (debug_level >= DEBUG_LEVEL_BH)
- printk("ISR overflow cleared.\n");
- info->isr_overflow=0;
- usc_EnableMasterIrqBit(info);
- usc_EnableDmaInterrupts(info,DICR_MASTER);
- }
-
- /* Mark BH routine as complete */
- info->bh_running = 0;
- info->bh_requested = 0;
-
spin_unlock_irqrestore(&info->irq_spinlock,flags);
- return 0;
-
-} /* end of mgsl_bh_queue_get() */
+ return rc;
+}
-/* mgsl_bh_handler()
- *
+/*
* Perform bottom half processing of work items queued by ISR.
- *
- * Arguments: Context pointer to device instance data
- * Return Value: None
*/
void mgsl_bh_handler(void* Context)
{
struct mgsl_struct *info = (struct mgsl_struct*)Context;
-
+ int action;
+
if (!info)
return;
@@ -1192,34 +1126,27 @@
info->bh_running = 1;
- /* Attempt to clear out the BH queue */
-
- while( mgsl_bh_queue_get(info) ) {
+ while((action = mgsl_bh_action(info)) != 0) {
/* Process work item */
if ( debug_level >= DEBUG_LEVEL_BH )
printk( "%s(%d):mgsl_bh_handler() work item action=%d\n",
- __FILE__,__LINE__,info->bh_action->type);
+ __FILE__,__LINE__,action);
- switch ( info->bh_action->type ) {
+ switch (action) {
- case BH_TYPE_RECEIVE_DMA:
- mgsl_bh_receive_dma( info, info->bh_action->status );
+ case BH_RECEIVE:
+ mgsl_bh_receive(info);
break;
-
- case BH_TYPE_TRANSMIT_STATUS:
- case BH_TYPE_TRANSMIT_DATA:
- mgsl_bh_transmit_data( info, info->bh_action->status );
+ case BH_TRANSMIT:
+ mgsl_bh_transmit(info);
break;
-
- case BH_TYPE_STATUS:
- mgsl_bh_status_handler( info, info->bh_action->status );
+ case BH_STATUS:
+ mgsl_bh_status(info);
break;
-
default:
/* unknown work item ID */
- printk("Unknown work item ID=%08X!\n",
- info->bh_action->type );
+ printk("Unknown work item ID=%08X!\n", action);
break;
}
}
@@ -1227,51 +1154,27 @@
if ( debug_level >= DEBUG_LEVEL_BH )
printk( "%s(%d):mgsl_bh_handler(%s) exit\n",
__FILE__,__LINE__,info->device_name);
-
-} /* end of mgsl_bh_handler() */
+}
-/* mgsl_bh_receive_dma()
- *
- * Perform bottom half processing for a receive DMA interrupt
- * This occurs in HDLC mode after a DMA buffer has terminated
- * or the DMA buffers have been exhausted.
- *
- * Arguments:
- *
- * info pointer to device instance data
- * status status word
- *
- * Return Value: None
- */
-void mgsl_bh_receive_dma( struct mgsl_struct *info, unsigned short status )
+void mgsl_bh_receive(struct mgsl_struct *info)
{
if ( debug_level >= DEBUG_LEVEL_BH )
- printk( "%s(%d):mgsl_bh_receive_dma(%s)\n",
+ printk( "%s(%d):mgsl_bh_receive(%s)\n",
__FILE__,__LINE__,info->device_name);
while( mgsl_get_rx_frame(info) );
+}
-} /* end of mgsl_bh_receive_dma() */
-
-/* mgsl_bh_transmit_data()
- *
- * Process a transmit data interrupt event
- * This occurs in asynchronous communications mode.
- *
- * Arguments: info pointer to device instance data
- * Return Value: None
- */
-void mgsl_bh_transmit_data( struct mgsl_struct *info, unsigned short Datacount )
+void mgsl_bh_transmit(struct mgsl_struct *info)
{
struct tty_struct *tty = info->tty;
unsigned long flags;
if ( debug_level >= DEBUG_LEVEL_BH )
- printk( "%s(%d):mgsl_bh_transmit_data() entry on %s\n",
+ printk( "%s(%d):mgsl_bh_transmit() entry on %s\n",
__FILE__,__LINE__,info->device_name);
-
- /* wakeup any waiting write requests */
- if (tty) {
+
+ if (tty) {
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup) {
if ( debug_level >= DEBUG_LEVEL_BH )
@@ -1280,7 +1183,6 @@
(tty->ldisc.write_wakeup)(tty);
}
wake_up_interruptible(&tty->write_wait);
- wake_up_interruptible(&tty->poll_wait);
}
/* if transmitter idle and loopmode_send_done_requested
@@ -1290,49 +1192,19 @@
if ( !info->tx_active && info->loopmode_send_done_requested )
usc_loopmode_send_done( info );
spin_unlock_irqrestore(&info->irq_spinlock,flags);
+}
-} /* End Of mgsl_bh_transmit_data() */
-
-/* mgsl_bh_status_handler()
- *
- * Peform bottom half processing for a status interrupt
- *
- * This event is generated when a I/O pin (serial signal)
- * has a transition. If there is a pending WaitEvent call
- * and the status transition is identified in the EventMast
- * of the pending call then complete the pending call.
- *
- * Arguments:
- *
- * info pointer to device instance data
- * status status word
- *
- * Return Value: None
- */
-void mgsl_bh_status_handler( struct mgsl_struct *info, unsigned short status )
+void mgsl_bh_status(struct mgsl_struct *info)
{
if ( debug_level >= DEBUG_LEVEL_BH )
- printk( "%s(%d):mgsl_bh_status_handler() entry on %s\n",
+ printk( "%s(%d):mgsl_bh_status() entry on %s\n",
__FILE__,__LINE__,info->device_name);
- if (status & MISCSTATUS_RI_LATCHED) {
- if (info->ri_chkcount)
- (info->ri_chkcount)--;
- }
- if (status & MISCSTATUS_DSR_LATCHED) {
- if (info->dsr_chkcount)
- (info->dsr_chkcount)--;
- }
- if (status & MISCSTATUS_DCD_LATCHED) {
- if (info->dcd_chkcount)
- (info->dcd_chkcount)--;
- }
- if (status & MISCSTATUS_CTS_LATCHED) {
- if (info->cts_chkcount)
- (info->cts_chkcount)--;
- }
-
-} /* End Of mgsl_bh_status_handler() */
+ info->ri_chkcount = 0;
+ info->dsr_chkcount = 0;
+ info->dcd_chkcount = 0;
+ info->cts_chkcount = 0;
+}
/* mgsl_isr_receive_status()
*
@@ -1376,12 +1248,6 @@
}
if (status & RXSTATUS_OVERRUN){
-// /* Purge receive FIFO to allow DMA buffer completion
-// * with overrun status stored in the receive status block.
-// */
-// usc_RCmd( info, RCmd_EnterHuntmode );
-// usc_RTCmd( info, RTCmd_PurgeRxFifo );
-
info->icount.rxover++;
usc_process_rxoverrun_sync( info );
}
@@ -1444,13 +1310,19 @@
}
info->drop_rts_on_tx_done = 0;
}
-
- if (info->tty->stopped || info->tty->hw_stopped) {
- usc_stop_transmitter(info);
- return;
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+ if (info->netcount)
+ mgsl_sppp_tx_done(info);
+ else
+#endif
+ {
+ if (info->tty->stopped || info->tty->hw_stopped) {
+ usc_stop_transmitter(info);
+ return;
+ }
+ info->pending_bh |= BH_TRANSMIT;
}
-
- mgsl_bh_queue_put(info, BH_TYPE_TRANSMIT_STATUS, status);
} /* end of mgsl_isr_transmit_status() */
@@ -1500,9 +1372,13 @@
if ((info->dcd_chkcount)++ >= IO_PIN_SHUTDOWN_LIMIT)
usc_DisablestatusIrqs(info,SICR_DCD);
icount->dcd++;
- if ( status & MISCSTATUS_DCD )
+ if (status & MISCSTATUS_DCD) {
info->input_signal_events.dcd_up++;
- else
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+ if (info->netcount)
+ sppp_reopen(info->netdev);
+#endif
+ } else
info->input_signal_events.dcd_down++;
#ifdef CONFIG_HARD_PPS
if ((info->flags & ASYNC_HARDPPS_CD) &&
@@ -1545,23 +1421,25 @@
if (status & MISCSTATUS_CTS) {
if ( debug_level >= DEBUG_LEVEL_ISR )
printk("CTS tx start...");
- info->tty->hw_stopped = 0;
+ if (info->tty)
+ info->tty->hw_stopped = 0;
usc_start_transmitter(info);
- mgsl_bh_queue_put( info, BH_TYPE_TRANSMIT_DATA, status );
+ info->pending_bh |= BH_TRANSMIT;
return;
}
} else {
if (!(status & MISCSTATUS_CTS)) {
if ( debug_level >= DEBUG_LEVEL_ISR )
printk("CTS tx stop...");
- info->tty->hw_stopped = 1;
+ if (info->tty)
+ info->tty->hw_stopped = 1;
usc_stop_transmitter(info);
}
}
}
}
- mgsl_bh_queue_put(info, BH_TYPE_STATUS, status);
+ info->pending_bh |= BH_STATUS;
/* for diagnostics set IRQ flag */
if ( status & MISCSTATUS_TXC_LATCHED ){
@@ -1599,7 +1477,7 @@
info->tx_active = 0;
if (info->xmit_cnt < WAKEUP_CHARS)
- mgsl_bh_queue_put(info, BH_TYPE_TRANSMIT_DATA, (unsigned short)(info->xmit_cnt));
+ info->pending_bh |= BH_TRANSMIT;
} /* end of mgsl_isr_transmit_data() */
@@ -1791,8 +1669,7 @@
printk("%s(%d):mgsl_isr_receive_dma(%s) status=%04X\n",
__FILE__,__LINE__,info->device_name,status);
- /* Post a receive event for BH processing. */
- mgsl_bh_queue_put( info, BH_TYPE_RECEIVE_DMA, status );
+ info->pending_bh |= BH_RECEIVE;
if ( status & BIT3 ) {
info->rx_overflow = 1;
@@ -1860,7 +1737,7 @@
* for it to do and the bh is not already running
*/
- if ( info->bh_queue_head && !info->bh_running && !info->bh_requested ) {
+ if ( info->pending_bh && !info->bh_running && !info->bh_requested ) {
if ( debug_level >= DEBUG_LEVEL_ISR )
printk("%s(%d):%s queueing bh task.\n",
__FILE__,__LINE__,info->device_name);
@@ -1904,7 +1781,7 @@
}
}
- mgsl_format_bh_queue(info);
+ info->pending_bh = 0;
init_timer(&info->tx_timer);
info->tx_timer.data = (unsigned long)info;
@@ -1963,6 +1840,8 @@
wake_up_interruptible(&info->status_event_wait_q);
wake_up_interruptible(&info->event_wait_q);
+ del_timer(&info->tx_timer);
+
if (info->xmit_buf) {
free_page((unsigned long) info->xmit_buf);
info->xmit_buf = 0;
@@ -2002,17 +1881,43 @@
} /* end of shutdown() */
-/* mgsl_change_params()
- *
- * Reconfigure adapter based on new parameters
- *
- * Arguments: info pointer to device instance data
- * Return Value: None
+static void mgsl_program_hw(struct mgsl_struct *info)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->irq_spinlock,flags);
+
+ usc_stop_receiver(info);
+ usc_stop_transmitter(info);
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+ if (info->params.mode == MGSL_MODE_HDLC || info->netcount)
+ usc_set_sync_mode(info);
+ else
+ usc_set_async_mode(info);
+
+ usc_set_serial_signals(info);
+
+ info->dcd_chkcount = 0;
+ info->cts_chkcount = 0;
+ info->ri_chkcount = 0;
+ info->dsr_chkcount = 0;
+
+ usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI);
+ usc_EnableInterrupts(info, IO_PIN);
+ usc_get_serial_signals(info);
+
+ if (info->netcount || info->tty->termios->c_cflag & CREAD)
+ usc_start_receiver(info);
+
+ spin_unlock_irqrestore(&info->irq_spinlock,flags);
+}
+
+/* Reconfigure adapter based on new parameters
*/
static void mgsl_change_params(struct mgsl_struct *info)
{
unsigned cflag;
- unsigned long flags;
int bits_per_char;
if (!info->tty || !info->tty->termios)
@@ -2120,35 +2025,7 @@
info->ignore_status_mask |= RXSTATUS_OVERRUN;
}
- /* reprogram the hardware */
-
- spin_lock_irqsave(&info->irq_spinlock,flags);
-
- usc_stop_receiver(info);
- usc_stop_transmitter(info);
- info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-
- if ( info->params.mode == MGSL_MODE_HDLC )
- usc_set_sync_mode(info);
- else
- usc_set_async_mode(info);
-
- usc_set_serial_signals(info);
-
- info->dcd_chkcount = 0;
- info->cts_chkcount = 0;
- info->ri_chkcount = 0;
- info->dsr_chkcount = 0;
-
- /* enable modem signal IRQs and read initial signal states */
- usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI);
- usc_EnableInterrupts(info, IO_PIN);
- usc_get_serial_signals(info);
-
- if ( cflag & CREAD )
- usc_start_receiver(info);
-
- spin_unlock_irqrestore(&info->irq_spinlock,flags);
+ mgsl_program_hw(info);
} /* end of mgsl_change_params() */
@@ -2479,7 +2356,6 @@
spin_unlock_irqrestore(&info->irq_spinlock,flags);
wake_up_interruptible(&tty->write_wait);
- wake_up_interruptible(&tty->poll_wait);
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
@@ -3117,11 +2993,7 @@
static int mgsl_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
- int error;
struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data;
- struct mgsl_icount cprev, cnow; /* kernel counter temps */
- struct serial_icounter_struct *p_cuser; /* user space */
- unsigned long flags;
if (debug_level >= DEBUG_LEVEL_INFO)
printk("%s(%d):mgsl_ioctl %s cmd=%08X\n", __FILE__,__LINE__,
@@ -3135,6 +3007,16 @@
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
}
+
+ return mgsl_ioctl_common(info, cmd, arg);
+}
+
+int mgsl_ioctl_common(struct mgsl_struct *info, unsigned int cmd, unsigned long arg)
+{
+ int error;
+ struct mgsl_icount cprev, cnow; /* kernel counter temps */
+ struct serial_icounter_struct *p_cuser; /* user space */
+ unsigned long flags;
switch (cmd) {
case TIOCMGET:
@@ -3235,10 +3117,9 @@
if (error) return error;
#endif
return 0;
-
default:
return -ENOIOCTLCMD;
- }
+ }
return 0;
}
@@ -3664,6 +3545,7 @@
struct mgsl_struct *info;
int retval, line;
unsigned long page;
+ unsigned long flags;
/* verify range of specified line number */
line = MINOR(tty->device) - tty->driver.minor_start;
@@ -3719,7 +3601,15 @@
info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
#endif
+ spin_lock_irqsave(&info->netlock, flags);
+ if (info->netcount) {
+ retval = -EBUSY;
+ spin_unlock_irqrestore(&info->netlock, flags);
+ goto cleanup;
+ }
info->count++;
+ spin_unlock_irqrestore(&info->netlock, flags);
+
if (info->count == 1) {
/* 1st open on this device, init hardware */
retval = startup(info);
@@ -3835,9 +3725,9 @@
/* Append serial signal status to end */
ret += sprintf(buf+ret, " %s\n", stat_buf+1);
- ret += sprintf(buf+ret, "txactive=%d bh_req=%d bh_run=%d bh_q=%p\n",
+ ret += sprintf(buf+ret, "txactive=%d bh_req=%d bh_run=%d pending_bh=%x\n",
info->tx_active,info->bh_requested,info->bh_running,
- info->bh_queue_head);
+ info->pending_bh);
spin_lock_irqsave(&info->irq_spinlock,flags);
{
@@ -3902,7 +3792,7 @@
done:
if (off >= len+begin)
return 0;
- *start = page + (begin-off);
+ *start = page + (off-begin);
return ((count < begin+len-off) ? count : begin+len-off);
} /* end of mgsl_read_proc() */
@@ -4400,6 +4290,12 @@
info->line = mgsl_device_count;
sprintf(info->device_name,"ttySL%d",info->line);
+ if (info->line < MAX_TOTAL_DEVICES) {
+ if (maxframe[info->line])
+ info->max_frame_size = maxframe[info->line];
+ info->dosyncppp = dosyncppp[info->line];
+ }
+
mgsl_device_count++;
if ( !mgsl_device_list )
@@ -4426,7 +4322,11 @@
info->device_name, info->io_base, info->irq_level, info->dma_level,
info->max_frame_size );
}
-
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+ if (info->dosyncppp)
+ mgsl_sppp_init(info);
+#endif
} /* end of mgsl_add_device() */
/* mgsl_allocate_device()
@@ -4458,7 +4358,8 @@
init_waitqueue_head(&info->close_wait);
init_waitqueue_head(&info->status_event_wait_q);
init_waitqueue_head(&info->event_wait_q);
-
+ spin_lock_init(&info->irq_spinlock);
+ spin_lock_init(&info->netlock);
memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS));
info->idle_mode = HDLC_TXIDLE_FLAGS;
}
@@ -4479,7 +4380,6 @@
{
struct mgsl_struct *info;
int i;
- int num_devices = 0;
/* Check for user specified ISA devices */
@@ -4510,15 +4410,7 @@
info->io_addr_size = 16;
info->irq_flags = 0;
- /* override default max frame size if arg available */
- if ( num_devices < MAX_TOTAL_DEVICES &&
- maxframe[num_devices] )
- info->max_frame_size = maxframe[num_devices];
-
- /* add new device to device list */
mgsl_add_device( info );
-
- ++num_devices;
}
@@ -4604,11 +4496,6 @@
info->bus = bus;
info->function = func;
- /* override default max frame size if arg available */
- if ( num_devices < MAX_TOTAL_DEVICES &&
- maxframe[num_devices] )
- info->max_frame_size = maxframe[num_devices];
-
/* Store the PCI9050 misc control register value because a flaw
* in the PCI9050 prevents LCR registers from being read if
* BIOS assigns an LCR base address with bit 7 set.
@@ -4794,6 +4681,10 @@
info = mgsl_device_list;
while(info) {
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+ if (info->dosyncppp)
+ mgsl_sppp_delete(info);
+#endif
mgsl_release_resources(info);
info = info->next_device;
}
@@ -6806,6 +6697,10 @@
else
info->icount.rxcrc++;
framesize = 0;
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+ info->netstats.rx_errors++;
+ info->netstats.rx_frame_errors++;
+#endif
} else {
/* receive frame has no errors, get frame size.
* The frame size is the starting value of the RCC (which was
@@ -6834,19 +6729,14 @@
if (framesize > info->max_frame_size)
info->icount.rxlong++;
else {
-#if 1
- /*
- * copy contents of dma frame buffer(s) to intermediate
- * rxbuffer for presentation to line discipline
- */
+ /* copy dma buffer(s) to contiguous intermediate buffer */
int copy_count = framesize;
int index = StartIndex;
unsigned char *ptmp = info->intermediate_rxbuffer;
info->icount.rxok++;
- while( copy_count )
- {
+ while(copy_count) {
int partial_count;
if ( copy_count > DMABUFFERSIZE )
partial_count = DMABUFFERSIZE;
@@ -6860,17 +6750,19 @@
if ( ++index == info->rx_buffer_count )
index = 0;
-
}
-
- /* Call the line discipline receive callback directly. */
- tty->ldisc.receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
-#else
- info->icount.rxok++;
- pBufEntry = &(info->rx_buffer_list[StartIndex]);
- /* Call the line discipline receive callback directly. */
- tty->ldisc.receive_buf(tty, pBufEntry->virt_addr, info->flag_buf, framesize);
+
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+ if (info->netcount) {
+ /* pass frame to syncppp device */
+ mgsl_sppp_rx_done(info,info->intermediate_rxbuffer,framesize);
+ }
+ else
#endif
+ {
+ /* Call the line discipline receive callback directly. */
+ tty->ldisc.receive_buf(tty, info->intermediate_rxbuffer, info->flag_buf, framesize);
+ }
}
}
/* Free the buffers used by this frame. */
@@ -7590,7 +7482,12 @@
spin_unlock_irqrestore(&info->irq_spinlock,flags);
- mgsl_bh_transmit_data(info,0);
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+ if (info->netcount)
+ mgsl_sppp_tx_done(info);
+ else
+#endif
+ mgsl_bh_transmit(info);
} /* end of mgsl_tx_timeout() */
@@ -7668,3 +7565,223 @@
return usc_InReg( info, CCSR ) & BIT6 ? 1 : 0 ;
}
+#ifdef CONFIG_SYNCLINK_SYNCPPP
+/* syncppp net device routines
+ */
+
+void mgsl_sppp_init(struct mgsl_struct *info)
+{
+ struct net_device *d;
+
+ sprintf(info->netname,"mgsl%d",info->line);
+
+#if LINUX_VERSION_CODE < VERSION(2,3,43)
+ info->netdev = &info->pppdev.dev;
+#else
+ info->if_ptr = &info->pppdev;
+ info->netdev = info->pppdev.dev = &info->netdevice;
+#endif
+ sppp_attach(&info->pppdev);
+
+ d = info->netdev;
+ d->name = info->netname;
+ d->base_addr = info->io_base;
+ d->irq = info->irq_level;
+ d->dma = info->dma_level;
+ d->priv = info;
+ d->init = NULL;
+ d->open = mgsl_sppp_open;
+ d->stop = mgsl_sppp_close;
+ d->hard_start_xmit = mgsl_sppp_tx;
+ d->do_ioctl = mgsl_sppp_ioctl;
+ d->get_stats = mgsl_net_stats;
+#if LINUX_VERSION_CODE >= VERSION(2,3,43)
+ d->tx_timeout = mgsl_sppp_tx_timeout;
+ d->watchdog_timeo = 10*HZ;
+#endif
+ dev_init_buffers(d);
+
+ if (register_netdev(d) == -1) {
+ printk(KERN_WARNING "%s: register_netdev failed.\n", d->name);
+ sppp_detach(info->netdev);
+ return;
+ }
+
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("mgsl_sppp_init()\n");
+}
+
+void mgsl_sppp_delete(struct mgsl_struct *info)
+{
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("mgsl_sppp_delete(%s)\n",info->netname);
+ sppp_detach(info->netdev);
+ unregister_netdev(info->netdev);
+}
+
+int mgsl_sppp_open(struct net_device *d)
+{
+ struct mgsl_struct *info = d->priv;
+ int err, flags;
+
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("mgsl_sppp_open(%s)\n",info->netname);
+
+ spin_lock_irqsave(&info->netlock, flags);
+ if (info->count != 0 || info->netcount != 0) {
+ printk(KERN_WARNING "%s: sppp_open returning busy\n", info->netname);
+ spin_unlock_irqrestore(&info->netlock, flags);
+ return -EBUSY;
+ }
+ info->netcount=1;
+ MOD_INC_USE_COUNT;
+ spin_unlock_irqrestore(&info->netlock, flags);
+
+ /* claim resources and init adapter */
+ if ((err = startup(info)) != 0)
+ goto open_fail;
+
+ /* allow syncppp module to do open processing */
+ if ((err = sppp_open(d)) != 0) {
+ shutdown(info);
+ goto open_fail;
+ }
+
+ info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR;
+ mgsl_program_hw(info);
+
+ d->trans_start = jiffies;
+ netif_start_queue(d);
+ return 0;
+
+open_fail:
+ spin_lock_irqsave(&info->netlock, flags);
+ info->netcount=0;
+ MOD_DEC_USE_COUNT;
+ spin_unlock_irqrestore(&info->netlock, flags);
+ return err;
+}
+
+void mgsl_sppp_tx_timeout(struct net_device *dev)
+{
+ struct mgsl_struct *info = dev->priv;
+ int flags;
+
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("mgsl_sppp_tx_timeout(%s)\n",info->netname);
+
+ info->netstats.tx_errors++;
+ info->netstats.tx_aborted_errors++;
+
+ spin_lock_irqsave(&info->irq_spinlock,flags);
+ usc_stop_transmitter(info);
+ spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+ netif_wake_queue(dev);
+}
+
+int mgsl_sppp_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct mgsl_struct *info = dev->priv;
+ unsigned long flags;
+
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("mgsl_sppp_tx(%s)\n",info->netname);
+
+#if LINUX_VERSION_CODE < VERSION(2,3,43)
+ if (dev->tbusy) {
+ if (time_before(jiffies, dev->trans_start+10*HZ))
+ return -EBUSY; /* 10 seconds timeout */
+ mgsl_sppp_tx_timeout(dev);
+ }
+ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0)
+ return -EBUSY;
+#else
+ netif_stop_queue(dev);
+#endif
+
+ info->xmit_cnt = skb->len;
+ mgsl_load_tx_dma_buffer(info, skb->data, skb->len);
+ info->netstats.tx_packets++;
+ info->netstats.tx_bytes += skb->len;
+ dev_kfree_skb(skb);
+
+ dev->trans_start = jiffies;
+
+ spin_lock_irqsave(&info->irq_spinlock,flags);
+ if (!info->tx_active)
+ usc_start_transmitter(info);
+ spin_unlock_irqrestore(&info->irq_spinlock,flags);
+
+ return 0;
+}
+
+int mgsl_sppp_close(struct net_device *d)
+{
+ struct mgsl_struct *info = d->priv;
+ unsigned long flags;
+
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("mgsl_sppp_close(%s)\n",info->netname);
+
+ /* shutdown adapter and release resources */
+ shutdown(info);
+
+ /* allow syncppp to do close processing */
+ sppp_close(d);
+ netif_stop_queue(d);
+
+ spin_lock_irqsave(&info->netlock, flags);
+ info->netcount=0;
+ MOD_DEC_USE_COUNT;
+ spin_unlock_irqrestore(&info->netlock, flags);
+ return 0;
+}
+
+void mgsl_sppp_rx_done(struct mgsl_struct *info, char *buf, int size)
+{
+ struct sk_buff *skb = dev_alloc_skb(size);
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("mgsl_sppp_rx_done(%s)\n",info->netname);
+ if (skb == NULL) {
+ printk(KERN_NOTICE "%s: cant alloc skb, dropping packet\n",
+ info->netname);
+ info->netstats.rx_dropped++;
+ return;
+ }
+
+ memcpy(skb_put(skb, size),buf,size);
+
+ skb->protocol = htons(ETH_P_WAN_PPP);
+ skb->dev = info->netdev;
+ skb->mac.raw = skb->data;
+ info->netstats.rx_packets++;
+ info->netstats.rx_bytes += size;
+ netif_rx(skb);
+ info->netdev->trans_start = jiffies;
+}
+
+void mgsl_sppp_tx_done(struct mgsl_struct *info)
+{
+ if (netif_queue_stopped(info->netdev))
+ netif_wake_queue(info->netdev);
+}
+
+struct net_device_stats *mgsl_net_stats(struct net_device *dev)
+{
+ struct mgsl_struct *info = dev->priv;
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("mgsl_net_stats(%s)\n",info->netname);
+ return &info->netstats;
+}
+
+int mgsl_sppp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct mgsl_struct *info = (struct mgsl_struct *)dev->priv;
+ if (debug_level >= DEBUG_LEVEL_INFO)
+ printk("%s(%d):mgsl_ioctl %s cmd=%08X\n", __FILE__,__LINE__,
+ info->netname, cmd );
+ return sppp_do_ioctl(dev, ifr, cmd);
+}
+
+#endif /* ifdef CONFIG_SYNCLINK_SYNCPPP */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)