* Re: HDLC driver for MPC8xx SCC
2004-05-25 0:39 HDLC driver for MPC8xx SCC HoJin
@ 2004-05-25 1:48 ` leeyang
0 siblings, 0 replies; 2+ messages in thread
From: leeyang @ 2004-05-25 1:48 UTC (permalink / raw)
To: HoJin; +Cc: linuxppc-embedded
[-- Attachment #1: Type: text/plain, Size: 563 bytes --]
http://qslinux.org/docs/snmc/hdlc/
check this
----- Original Message -----
From: "HoJin" <jhfami@yahoo.co.kr>
To: <linuxppc-embedded@lists.linuxppc.org>
Sent: Tuesday, May 25, 2004 8:39 AM
Subject: HDLC driver for MPC8xx SCC
>
> Hi,
> I'm working on my custom mpc860 board, and kernel 2.4. I want to
> implement sync. HDLC driver. I have found old HDLC driver for mpc860 but
> it is not sync and doesn't works well.
>
> Are there any nice driver for mpc860 HDLC driver?
> Any helps and advices are welcome. :)
>
> --
> HoJin, Yu
>
> jhfami@yahoo.co.kr
>
>
>
[-- Attachment #2: hdlcppp.c --]
[-- Type: application/octet-stream, Size: 32315 bytes --]
// $Header: /projects/cvsroot/snmc/qslinux/drivers/net/hdlcppp.c,v 1.2 2000/08/10 15:56:07 dnevil Exp $
// Copyright 2000 SNMC
// Simple Network Magic Corporation
// Synchronous (HDLC) PPP Driver
// Author: Daris A Nevil
// This driver provides RX/TX functionality for HDLC frames
// on the QS850 SCC controller. Each SCC is configured
// appropriately for PPP packet transport according to RFC 1662.
// $Log: hdlcppp.c,v $
// Revision 1.2 2000/08/10 15:56:07 dnevil
// Removed compiler warnings.
//
// Revision 1.1.1.1 2000/08/10 14:34:25 dnevil
// New files.
//
// Standard includes
// Local includes
#define HAVE_CHANGE_MTU
#include <linux/types.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/netdevice.h>
#include <linux/kerneld.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/semaphore.h>
#include <asm/8xx_immap.h>
#include <asm/mpc8xx.h>
#include <asm/commproc.h>
#include <asm/qspin.h>
#include <linux/syncppp.h>
#include <asm/cpm.h>
#include <asm/hdlcppp.h>
//#define HDLC_PPP_DEBUG
#ifdef HDLC_PPP_DEBUG
#define DEBUG(args...) printk(args)
#else
#define DEBUG(args...)
#endif
#define RX_RING_SIZE 8
#define TX_RING_SIZE 8
// Maximum size for RX Buffers
// Must be a multiple of 4
#define MAX_RX_BUFF_SIZE 1560 // 390*4
// This value must be <= MAX_RX_BUFF_SIZE
#define HDLC_PPP_MTU (1550+8)
#define NUM_SCCS 4 // Number of SCC Channels
#define SCC_PARAM_SIZE 0x100 // SCC Parameter RAM size
#define SYNC_PPP_MODULE "syncppp"
// Defines number of RX Frames before an interrupt is generated
#define RX_FRAME_THRESHOLD 1
// HDLC Addresses and Masks
#define HDLC_C_MASK 0xF0B8 // CRC Constant
#define HDLC_C_PRES 0xFFFF // CRC Preset
#define HDLC_ADDR_MASK 0x00FF // Address Mask (8-bit)
#define HDLC_AS_ADDR 0x00FF // All-Stations Address
#define HDLC_CM_ADDR 0x008F // Cisco Multicast Address
#define HDLC_CU_ADDR 0x000F // Cisco Unicast Address
#define TX_TIMEOUT 200 // Ticks
//-------------------------------------------------------------------
// Local structures
typedef struct {
// SCC device number (1-4)
int scc_num;
int scc_param_index;
// Device structure for SYNC PPP
struct ppp_device pppdev;
// Device Statistics
struct net_device_stats stats;
// Buffer descriptors
// TX Buffers and descriptors
volatile cbd_t* tx_bd_base; // Start of TX Buffer Descriptors
volatile cbd_t* tx_bd[TX_RING_SIZE];
int tx_head;
struct sk_buff* tx_skbuff[TX_RING_SIZE];
// RX Buffers and descriptors
volatile cbd_t* rx_bd_base; // Start of RX Buffer Descriptors
volatile cbd_t* rx_bd[RX_RING_SIZE];
int rx_tail;
struct sk_buff* rx_skbuff[RX_RING_SIZE];
// Bottom Half task queue
struct tq_struct rx_bh_tq;
struct tq_struct tx_bh_tq;
} hdlc_channel_t;
typedef struct
{
char* name;
uint pram_index;
ushort chan_cmd;
ushort irq_vector;
} SCC_Params_t;
//-------------------------------------------------------------------
// Function Prototypes
void sync_ppp_init(void);
#ifdef MODULE
int init_module(void);
#else
int hdlc_ppp_init(void);
#endif
#ifdef MODULE
void cleanup_module(void);
#else
void hdlc_ppp_cleanup_module(void);
#endif
static void hdlc_ppp_interrupt(void *dev_id);
static int hdlc_ppp_hard_start_xmit(struct sk_buff* skb, struct device* dev);
static struct net_device_stats* hdlc_ppp_get_stats(struct device* dev);
static int hdlc_ppp_ioctl(struct device* dev, struct ifreq* ifr, int cmd);
static int hdlc_ppp_change_mtu(struct device* dev, int new_mtu);
static int hdlc_ppp_stop(struct device* dev);
static int hdlc_ppp_alloc_rx_ring(struct device* dev);
static struct sk_buff* hdlc_ppp_alloc_skb(int mtu);
static void hdlc_ppp_restart_rx(struct device* dev);
static void hdlc_ppp_restart_tx(struct device* dev);
static int hdlc_ppp_init_local(struct device* dev);
static int hdlc_ppp_open(struct device* dev);
static void hdlc_ppp_tx_bh(void* dev_id);
static void hdlc_ppp_rx_bh(void* dev_id);
//-------------------------------------------------------------------
// Global initializers
#define PPP_HDLC_CHANNELS 4
static hdlc_channel_t MyChan[PPP_HDLC_CHANNELS];
static SCC_Params_t SCC_Params[PPP_HDLC_CHANNELS] = {
{"hdlcppp1", PROFF_SCC1, CPM_CR_CH_SCC1, CPMVEC_SCC1},
{"hdlcppp2", PROFF_SCC2, CPM_CR_CH_SCC2, CPMVEC_SCC2},
{"hdlcppp3", PROFF_SCC3, CPM_CR_CH_SCC3, CPMVEC_SCC3},
{"hdlcppp4", PROFF_SCC4, CPM_CR_CH_SCC4, CPMVEC_SCC4},
};
// Module Configuration Parameters
MODULE_AUTHOR("dnevil@snmc.com");
MODULE_DESCRIPTION("SCC HDLC Driver for PPP");
//--------------------------------------------------------------
// init_module()
//
// Return:
// 0 on success, negative integer on failure
//
// Input Parameters:
// None.
//
// Initialize the SCC port for operation in HDLC mode suitable
// for transport of PPP according to RFC 1662.
#ifdef MODULE
int init_module(void)
#else
int hdlc_ppp_init(void)
#endif
{
int i;
int result;
struct device* dev;
volatile cpm8xx_t* cp = cpmp;
DEBUG("hdlc_ppp: init_module()\n");
printk(KERN_INFO "SCC HDLC Driver for PPP v1.0\n");
printk(KERN_INFO "(c) Copyright 2000, Simple Network Magic Corporation\n");
#if 0
// Request the SYNC PPP Module
result = request_module(SYNC_PPP_MODULE);
if (result)
return(result);
#endif
for (i = 0; i < PPP_HDLC_CHANNELS; ++i)
{
__clear_user(&MyChan[i], sizeof MyChan[0]);
// Save the SCC Channel number
MyChan[i].scc_num = i+1;
MyChan[i].scc_param_index = i;
// Attach the SYNC PPP driver to this device
sppp_attach(&MyChan[i].pppdev);
dev = &MyChan[i].pppdev.dev;
// Get pointer to base of SCC control registers
// (index, not number)
dev->base_addr = (unsigned long)&cp->cp_scc[i];
// Get beginning of my SCC's Parameter Ram
dev->mem_start = (unsigned long)&
cp->cp_dparam[SCC_Params[i].pram_index];
// Get end of my SCC's Parameter Ram
dev->mem_end = dev->mem_start + SCC_PARAM_SIZE - 1;
// Fill out the function pointers
dev->name = SCC_Params[i].name;
dev->init = hdlc_ppp_init_local;
dev->open = hdlc_ppp_open;
dev->stop = hdlc_ppp_stop;
// dev->set_config = hdlc_ppp_set_config;
dev->set_config = NULL;
dev->hard_start_xmit = hdlc_ppp_hard_start_xmit;
dev->get_stats = hdlc_ppp_get_stats;
dev->set_multicast_list = NULL;
dev->do_ioctl = hdlc_ppp_ioctl;
// dev->neigh_setup = hdlc_ppp_neigh_setup;
dev->set_mac_address = NULL;
dev->change_mtu = hdlc_ppp_change_mtu;
dev->rebuild_header = NULL;
dev->priv = (void*)&MyChan[i];
// Register my device
result = register_netdev(dev);
if (result)
{
printk(KERN_WARNING "%s: register_netdev failed.\n",
dev->name);
sppp_detach(&MyChan[i].pppdev.dev);
return(result);
}
}
// Initialize the sync ppp layer
sync_ppp_init();
// Success
return(0);
}
//--------------------------------------------------------------
// cleanup_module()
//
// Return:
// None.
//
// Input Parameters:
// None.
//
// Remove the device from the list of network devices.
// Return allocated resources back to the system pool.
#ifdef MODULE
void cleanup_module(void)
#else
void hdlc_ppp_cleanup_module(void)
#endif
{
int i;
DEBUG("hdlc_ppp: cleanup_module()\n");
for (i = 0; i < PPP_HDLC_CHANNELS; ++i)
{
// Detach the PPP Layer
sppp_detach(&MyChan[i].pppdev.dev);
// Unregister the device
// sys_delete_module() will check MOD_IN_USE
unregister_netdev(&MyChan[i].pppdev.dev);
}
}
//--------------------------------------------------------------
// hdlc_ppp_init()
//
// Return:
// 0 on success, negative integer on failure
//
// Input Parameters:
// (I) dev - pointer to this driver's device structure
//
// Initialize the SCC port for operation in HDLC mode suitable
// for transport of PPP according to RFC 1662.
static int hdlc_ppp_init_local(struct device* dev)
{
DEBUG("hdlc_ppp_init()\n");
// Initialize device driver buffers
dev_init_buffers(dev);
// Success
return(0);
}
//--------------------------------------------------------------
// hdlc_ppp_open()
//
// Return:
// 0 on success, negative integer on failure
//
// Input Parameters:
// (I) dev - pointer to this driver's device structure
//
// Open the device
static int hdlc_ppp_open(struct device* dev)
{
hdlc_channel_t* chan = (hdlc_channel_t*)dev->priv;
volatile cpm8xx_t* cp = cpmp;
volatile scc_t* sccp = (scc_t*)dev->base_addr;
volatile scc_hdlc_t* hp = (scc_hdlc_t*)dev->mem_start;
int i;
int result;
DEBUG("%s: hdlc_ppp_open()\n", dev->name);
// Disable rx & tx
sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
// Make sure the parameter RAM area is not already allocated
result = check_region(dev->mem_start, SCC_PARAM_SIZE);
if (result)
return(result);
// Allocate the parameter RAM
request_region(dev->mem_start, SCC_PARAM_SIZE, dev->name);
// Configure I/O Pins
// This will be left to user code
#if 0
// Init the SI Clock Route Register (SICR) for SCC3
// Connect SCC3 to NMSI
// Transmit Clock (CLK3) = BRGO2
// Receive Clock (CLK1) = QS850_PIN_A20
cp->cp_sicr &= 0x0000FFFF;
cp->cp_sicr |= 0x00210000;
#else
// Init the SI Clock Route Register (SICR) for SCC3
// Connect SCC3 to NMSI
// Transmit Clock (CLK3) = QS850_PIN_A19
// Receive Clock (CLK3) = QS850_PIN_A19
cp->cp_sicr &= 0x0000FFFF;
cp->cp_sicr |= 0x00360000;
#endif
// Allocate RX Buffer descriptors
i = m8xx_cpm_dpalloc(sizeof(cbd_t) * RX_RING_SIZE);
if (i == CPM_DP_NOSPACE)
{
printk("hdlc_ppp: No more CPM DPRAM\n");
result = -ENOMEM;
goto fail_rx_dpram;
}
hp->genscc.scc_rbase = i;
chan->rx_bd_base = (cbd_t*)&cp->cp_dpmem[i];
// Allocate TX Buffer descriptors
i = m8xx_cpm_dpalloc(sizeof(cbd_t) * TX_RING_SIZE);
if (i == CPM_DP_NOSPACE)
{
printk("hdlc_ppp: No more CPM DPRAM\n");
result = -ENOMEM;
goto fail_tx_dpram;
}
hp->genscc.scc_tbase = i;
chan->tx_bd_base = (cbd_t*)&cp->cp_dpmem[i];
// Init Function Code registers (Big Endian)
hp->genscc.scc_rfcr = SCC_EB;
hp->genscc.scc_tfcr = SCC_EB;
// Set the RX Buffer maximum size
hp->genscc.scc_mrblr = MAX_RX_BUFF_SIZE;
// Set the MTU
hp->mflr = HDLC_PPP_MTU;
// Set the CRC Constant and Preset registers
hp->c_mask = HDLC_C_MASK;
hp->c_pres = HDLC_C_PRES;
// Set the All-Stations Address (used for PPP)
// Also set Cisco Multicast and Cisco Unicast addresses
hp->hmask = HDLC_ADDR_MASK;
hp->haddr1 = HDLC_AS_ADDR;
hp->haddr2 = HDLC_CM_ADDR;
hp->haddr3 = HDLC_CU_ADDR;
hp->haddr4 = HDLC_AS_ADDR;
// Clear the pm counters
hp->disfc = 0;
hp->crcec = 0;
hp->abtsc = 0;
hp->nmarc = 0;
hp->retrc = 0;
// Set the RX Frame Threshold
hp->rfthr = RX_FRAME_THRESHOLD;
// Set the Protocol Specific Mode Register
// NOF = 0 (1 flag between adjacent frames)
// CRC = 16-bit
// No Retransmission
// FSE = Normal
// DRT = Normal
// No HDLC Bus mode
// BRM = Ignored
// MFF = Normal
sccp->scc_psmr = 0x0000;
// Disable All Interrupts for this SCC
sccp->scc_sccm = 0x0000;
// Clear pending events
sccp->scc_scce = 0xFFFF;
// Install my interrupt handler
cpm_install_handler(SCC_Params[chan->scc_param_index].irq_vector,
hdlc_ppp_interrupt, dev);
// Enable Interrupts for Transmit Error, Rx Received, and TXB
sccp->scc_sccm = (SCCM_TXE | SCCM_RX | SCCM_RXF | SCCM_TX);
// Set the GSMR_H and GSMR_L registers
sccp->scc_gsmrh = 0;
sccp->scc_gsmrl = SCC_GSMRL_MODE_HDLC;
// Set the DSR register, just it case it has been modified
// by another driver since reset
sccp->scc_dsr = 0x7E7E;
// Allocate buffers for all RX Buf Descr
if ((result = hdlc_ppp_alloc_rx_ring(dev)))
goto fail_buffer;
// Initialize and start the Receiver and Transmitter
hdlc_ppp_restart_rx(dev);
hdlc_ppp_restart_tx(dev);
// Complete driver-required state info
dev->start = 1;
dev->tbusy = 0;
dev->interrupt = 0;
// Adjust module count
MOD_INC_USE_COUNT;
// Open the PPP driver
result = sppp_open(dev);
if (result)
{
printk(KERN_WARNING "%s: could not open Sync PPP device.\n",
dev->name);
MOD_DEC_USE_COUNT;
goto fail_buffer;
}
goto done;
fail_buffer:
printk("fail_buffer\n");
fail_rx_dpram:
printk("fail_rx_dpram\n");
//m8xx_cpm_dpfree();
fail_tx_dpram:
printk("fail_tx_dpram\n");
//m8xx_cpm_dpfree();
release_region(dev->mem_start, SCC_PARAM_SIZE);
done:
// Done
return(result);
}
//--------------------------------------------------------------
// hdlc_ppp_stop()
//
// Return:
// 0 on success, negative integer on failure
//
// Input Parameters:
// (I) dev - pointer to this driver's device structure
//
// Close the device and free all resources
static int hdlc_ppp_stop(struct device* dev)
{
hdlc_channel_t* chan = (hdlc_channel_t*)dev->priv;
volatile scc_t* sccp = (scc_t*)dev->base_addr;
volatile cpm8xx_t* cp = cpmp;
int i;
DEBUG("%s: hdlc_ppp_stop()\n", dev->name);
// Close the Sync PPP driver
sppp_close(dev);
// Set the driver flags
dev->tbusy = 1;
dev->start = 0;
// Send a Stop Transmit command to the CPM
cp->cp_cpcr = mk_cr_cmd(SCC_Params[chan->scc_param_index].chan_cmd,
CPM_CR_STOP_TX) | CPM_CR_FLG;
while (cp->cp_cpcr & CPM_CR_FLG);
// Disable rx & tx
sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
// Free the socket buffers associated with the RX buffer rings
for (i = 0; i < RX_RING_SIZE; ++i)
{
// Free any allocated socket buffers
if (chan->rx_skbuff[i])
dev_kfree_skb(chan->rx_skbuff[i]);
chan->rx_skbuff[i] = 0;
}
// Free the socket buffers associated with the TX buffer rings
for (i = 0; i < TX_RING_SIZE; ++i)
{
// Free any allocated socket buffers
if (chan->tx_skbuff[i])
dev_kfree_skb(chan->tx_skbuff[i]);
chan->tx_skbuff[i] = 0;
}
// Release the Parameter Ram associated with this channel
release_region(dev->mem_start, SCC_PARAM_SIZE);
// Release RX & TX Buffer descriptors
// m8xx_cpm_dpfree(hp->genscc.scc_rbase);
// m8xx_cpm_dpfree(hp->genscc.scc_tbase);
// Disable All Interrupts for this SCC
sccp->scc_sccm = 0x0000;
// Clear pending events
sccp->scc_scce = 0xFFFF;
// Uninstall the interrupt handler
//?
// Update statistics
//?
// Adjust module count
MOD_DEC_USE_COUNT;
// Done
return(0);
}
//--------------------------------------------------------------
// hdlc_ppp_ioctl()
//
// Return:
// 0 on success, negative integer on failure
//
// Input Parameters:
// (I) dev - pointer to this driver's device structure
// (IO) ifr - pointer to a structure for passing arguments
// (I) cmd - ioctl() function specifier
//
// Perform special device control functions
static int hdlc_ppp_ioctl(struct device* dev,
struct ifreq* ifr, int cmd)
{
volatile scc_t* sccp = (scc_t*)dev->base_addr;
DEBUG("%s: hdlc_ppp_ioctl()\n", dev->name);
switch (cmd)
{
case HDLCPPPIOCNOLOOPECHO:
sccp->scc_gsmrl &= ~(SCC_GSMRL_DIAG_LOOP | SCC_GSMRL_DIAG_ECHO);
break;
case HDLCPPPIOCLOOP:
sccp->scc_gsmrl &= ~SCC_GSMRL_DIAG_ECHO;
sccp->scc_gsmrl |= SCC_GSMRL_DIAG_LOOP;
break;
case HDLCPPPIOCECHO:
sccp->scc_gsmrl &= ~SCC_GSMRL_DIAG_LOOP;
sccp->scc_gsmrl |= SCC_GSMRL_DIAG_ECHO;
break;
case HDLCPPPIOCLOOPECHO:
sccp->scc_gsmrl |= SCC_GSMRL_DIAG_LOOP | SCC_GSMRL_DIAG_ECHO;
break;
case HDLCPPPIOCTXCLKFALL:
sccp->scc_gsmrl &= ~SCC_GSMRL_TCI;
break;
case HDLCPPPIOCTXCLKRISE:
sccp->scc_gsmrl |= SCC_GSMRL_TCI;
break;
default:
// Perform the Sync PPP ioctl() functions
return(sppp_do_ioctl(dev, ifr, cmd));
}
// Success
return(0);
}
//--------------------------------------------------------------
// hdlc_ppp_change_mtu()
//
// Return:
// 0 on success, negative integer on failure
//
// Input Parameters:
// (I) dev - pointer to this driver's device structure
// (I) new_mtu - new size of the mtu
//
// Sets a new value for Maximum Transmission Unit (MTU).
static int hdlc_ppp_change_mtu(struct device* dev, int new_mtu)
{
hdlc_channel_t* chan = (hdlc_channel_t*)dev->priv;
volatile scc_hdlc_t* hp = (scc_hdlc_t*)dev->mem_start;
volatile scc_t* sccp = (scc_t*)dev->base_addr;
int result;
int i;
DEBUG("%s: hdlc_ppp_change_mtu: new mtu=%d, old mtu=%d\n",
dev->name, new_mtu, hp->mflr);
// Stop invalid changes
if ((new_mtu < 32) || (new_mtu > 65534))
return(-EINVAL);
// Disable the Receiver
sccp->scc_gsmrl &= ~SCC_GSMRL_ENR;
// Set the MTU
hp->mflr = new_mtu;
// Set the RX Buffer maximum size
// Must be a multiple of 4 for the SCC in HDLC mode
new_mtu /= 4;
new_mtu += 2; // Add a little cushion
new_mtu *= 4;
hp->genscc.scc_mrblr = new_mtu;
// Free the socket buffers associated with the RX buffer rings
for (i = 0; i < RX_RING_SIZE; ++i)
{
// Free any allocated socket buffers
if (chan->rx_skbuff[i])
dev_kfree_skb(chan->rx_skbuff[i]);
chan->rx_skbuff[i] = 0;
}
// Allocate new buffers
if ((result = hdlc_ppp_alloc_rx_ring(dev)))
return(result);
// Restart Receiver
hdlc_ppp_restart_rx(dev);
// Success
return(0);
}
//--------------------------------------------------------------
// hdlc_ppp_alloc_rx_ring()
//
// Return:
// 0 on success, negative integer on failure
//
// Input Parameters:
// (I) dev - pointer to this driver's device structure
//
// Allocate buffers for each buffer descriptor in the RX ring.
// The receiver must be restarted after calling this function.
static int hdlc_ppp_alloc_rx_ring(struct device* dev)
{
int i;
volatile cbd_t* bdp;
struct sk_buff* newskb;
hdlc_channel_t* chan = (hdlc_channel_t*)dev->priv;
volatile scc_hdlc_t* hp = (scc_hdlc_t*)dev->mem_start;
// Initialize the RX Buffer Descriptor Ring
bdp = chan->rx_bd_base;
for (i = 0; i < RX_RING_SIZE; ++i, ++bdp)
{
newskb = hdlc_ppp_alloc_skb(hp->genscc.scc_mrblr);
if (!newskb)
{
printk("cannot alloc socket buffer\n");
goto fail_rx_buffer;
}
// Assign physical memory to each RX Buffer Descriptor
bdp->cbd_bufaddr = __pa(newskb->data);
chan->rx_skbuff[i] = newskb;
}
// Success
return(0);
fail_rx_buffer:
for (i = 0; i < RX_RING_SIZE; ++i)
{
// Free any allocated socket buffers
if (chan->rx_skbuff[i])
dev_kfree_skb(chan->rx_skbuff[i]);
chan->rx_skbuff[i] = 0;
}
return(-ENOMEM);
}
//--------------------------------------------------------------
// hdlc_ppp_alloc_skb()
//
// Return:
// Pointer to a new socket buffer.
//
// Input Parameters:
// (I) mtu - specifies the size in bytes of the current MTU
//
// Allocates a new socket buffer large enough to receive a complete
// frame of the current MTU size. The buffer is pre-flush from
// the cache so that data received from the SCC is not corrupted
// later.
static struct sk_buff* hdlc_ppp_alloc_skb(int mtu)
{
struct sk_buff* newskb;
// Allocate a socket buffer
newskb = dev_alloc_skb(mtu);
if (newskb)
{
// Push the data cache so updates are made now from cache
// (we don't want data clobbered later)
flush_dcache_range((ulong)newskb->data,
(ulong)(newskb->data + mtu));
}
return(newskb);
}
//--------------------------------------------------------------
// hdlc_ppp_restart_rx()
//
// Return:
// None.
//
// Input Parameters:
// (I) dev - pointer to this driver's device structure
//
// Restarts (or starts for the first time) the Receiver of the
// specified SCC.
static void hdlc_ppp_restart_rx(struct device* dev)
{
volatile cpm8xx_t* cp = cpmp;
volatile scc_t* sccp = (scc_t*)dev->base_addr;
hdlc_channel_t* chan = (hdlc_channel_t*)dev->priv;
volatile cbd_t* bdp;
int i;
// Disable the receiver
sccp->scc_gsmrl &= ~SCC_GSMRL_ENR;
// Re-INIT RX
// This command lets the CPM know it must reinitialize the SCC
// with the new parameter RAM values.
// This intializes the rbptr register
cp->cp_cpcr = mk_cr_cmd(SCC_Params[chan->scc_param_index].chan_cmd,
CPM_CR_INIT_RX) | CPM_CR_FLG;
while (cp->cp_cpcr & CPM_CR_FLG);
// Synchronize the local state machine with the CPM
chan->rx_tail = 0;
// Initialize the RX Buffer Descriptor Ring
bdp = chan->rx_bd_base;
for (i = 0; i < RX_RING_SIZE; ++i, ++bdp)
{
// Save the indexed pointer to the bd
chan->rx_bd[i] = bdp;
if (i == (RX_RING_SIZE - 1))
bdp->cbd_sc = BD_HDLC_RX_EMPTY | BD_HDLC_RX_INTRPT |
BD_HDLC_RX_WRAP;
else
bdp->cbd_sc = BD_HDLC_RX_EMPTY | BD_HDLC_RX_INTRPT;
}
// Enable Receive processing
// Hunt Mode is automatically enetered
sccp->scc_gsmrl |= SCC_GSMRL_ENR;
}
//--------------------------------------------------------------
// hdlc_ppp_restart_tx()
//
// Return:
// None.
//
// Input Parameters:
// (I) dev - pointer to this driver's device structure
//
// Restarts (or starts for the first time) the Transmitter of the
// specified SCC.
static void hdlc_ppp_restart_tx(struct device* dev)
{
volatile cpm8xx_t* cp = cpmp;
volatile scc_t* sccp = (scc_t*)dev->base_addr;
hdlc_channel_t* chan = (hdlc_channel_t*)dev->priv;
volatile cbd_t* bdp;
int i;
// Send the Stop Transmission command to the CPM for this SCC
cp->cp_cpcr = mk_cr_cmd(SCC_Params[chan->scc_param_index].chan_cmd,
CPM_CR_STOP_TX) | CPM_CR_FLG;
while (cp->cp_cpcr & CPM_CR_FLG);
// Disable transmission
sccp->scc_gsmrl &= ~SCC_GSMRL_ENT;
// Re-INIT TX command
// This command lets the CPM know it must reinitialize the SCC
// with the new parameter RAM values.
// This intializes the tbptr register
cp->cp_cpcr = mk_cr_cmd(SCC_Params[chan->scc_param_index].chan_cmd,
CPM_CR_INIT_TX) | CPM_CR_FLG;
while (cp->cp_cpcr & CPM_CR_FLG);
// Synchronize the local state machine with the CPM
chan->tx_head = 0;
// Initialize the TX Buffer Descriptor Ring
bdp = chan->tx_bd_base;
for (i = 0; i < TX_RING_SIZE; ++i, ++bdp)
{
// Save the indexed pointer to the bd
chan->tx_bd[i] = bdp;
#if 0
if (i == (TX_RING_SIZE - 1))
{
bdp->cbd_sc = BD_HDLC_TX_WRAP;
}
else
{
bdp->cbd_sc = 0;
}
#else
bdp->cbd_sc = 0;
#endif
// No pointers yet
bdp->cbd_bufaddr = 0;
// Free any allocated socket buffers
if (chan->tx_skbuff[i])
{
dev_kfree_skb(chan->tx_skbuff[i]);
chan->stats.tx_dropped++;
}
chan->tx_skbuff[i] = 0;
}
// Enable Transmit processing
sccp->scc_gsmrl |= SCC_GSMRL_ENT;
// TX no longer busy
dev->tbusy = 0;
}
//--------------------------------------------------------------
// hdlc_ppp_get_stats()
//
// Return:
// 0 on success, negative integer on failure
//
// Input Parameters:
// (I) dev - pointer to this driver's device structure
//
// Return the device statistics
static struct net_device_stats* hdlc_ppp_get_stats(struct device* dev)
{
hdlc_channel_t* chan = (hdlc_channel_t*)dev->priv;
DEBUG("%s: hdlc_ppp_get_stats()\n", dev->name);
return(&chan->stats);
}
//--------------------------------------------------------------
// hdlc_ppp_hard_start_xmit()
//
// Return:
// 0 on success, negative integer on failure
//
// Input Parameters:
// (I) skb - pointer to a buffer to transmit
// (I) dev - pointer to this driver's device structure
//
// Close the device and free all resources
static int hdlc_ppp_hard_start_xmit(struct sk_buff* skb, struct device* dev)
{
hdlc_channel_t* chan = (hdlc_channel_t*)dev->priv;
volatile cbd_t* bdp;
int i;
DEBUG("%s: hdlc_ppp_hard_start_xmit()\n", dev->name);
if (dev->tbusy)
{
int tickssofar = jiffies - dev->trans_start;
if (tickssofar < TX_TIMEOUT)
{
dev_kfree_skb(skb);
return(-EBUSY);
}
printk("%s: transmit timed out, restarting\n", dev->name);
chan->stats.tx_errors++;
// Restart the transmitter
hdlc_ppp_restart_tx(dev);
}
// Not exactly sure what this code does. It is illustrated
// on p321 of "Linux Device Drivers".
if (skb == NULL)
return(0);
// Get the head of the queue
i = chan->tx_head;
bdp = chan->tx_bd[i];
if (bdp->cbd_sc & BD_HDLC_TX_READY)
{
printk("%s: no tx bd's available\n", dev->name);
// The transmitter is now busy
dev->tbusy = 1;
// We loose this packet
dev_kfree_skb(skb);
chan->stats.tx_dropped++;
return(-EBUSY);
}
// We found a bd we can use, bump the head index
++chan->tx_head;
if (chan->tx_head >= TX_RING_SIZE)
chan->tx_head = 0;
// Set buffer length and pointer
chan->tx_skbuff[i] = skb;
bdp->cbd_datlen = skb->len;
bdp->cbd_bufaddr = __pa(skb->data); // Use the physical address
// Push the data cache so the CPM does not get stale data
flush_dcache_range((ulong)skb->data, (ulong)(skb->data + skb->len));
// Clear status flags
bdp->cbd_sc &= ~BD_HDLC_TX_STATS;
// Record start time of transmission
dev->trans_start = jiffies;
// Send the packet
if (i == (TX_RING_SIZE - 1))
{
bdp->cbd_sc |= BD_HDLC_TX_READY | BD_HDLC_TX_INTRPT |
BD_HDLC_TX_LAST | BD_HDLC_TX_CRC | BD_HDLC_TX_WRAP;
}
else
{
bdp->cbd_sc |= BD_HDLC_TX_READY | BD_HDLC_TX_INTRPT |
BD_HDLC_TX_LAST | BD_HDLC_TX_CRC;
}
// Update statistics
chan->stats.tx_bytes += skb->len;
chan->stats.tx_packets++;
// Done
return(0);
}
//--------------------------------------------------------------
// hdlc_ppp_interrupt()
//
// Return:
// None.
//
// Input Parameters:
// (I) dev_id - pointer to this driver's device structure
//
// Close the device and free all resources
static void hdlc_ppp_interrupt(void *dev_id)
{
struct device* dev = (struct device*)dev_id;
hdlc_channel_t* chan = (hdlc_channel_t*)dev->priv;
volatile scc_t* sccp = (scc_t*)dev->base_addr;
ushort int_events;
DEBUG("%s: Entering ISR\n", dev->name);
// Print message if we are re-entering
if (dev->interrupt)
{
printk("%s: re-entering the interrupt handler\n",
dev->name);
}
// Indicate we are in our ISR
dev->interrupt = 1;
// Get the interrupt events
int_events = sccp->scc_scce;
//DEBUG("%s: sccp->scc_scce=0x%x\n", dev->name, int_events);
// Clear off these event
sccp->scc_scce = int_events;
if (int_events & SCCM_BSY)
{
chan->stats.rx_errors++;
chan->stats.rx_dropped++;
DEBUG("%s: SCCM_BSY=%ld\n", dev->name, chan->stats.rx_errors);
}
if (int_events & (SCCM_RX|SCCM_RXF))
{
// Queue the task into the immediate bottom half queue
chan->rx_bh_tq.routine = hdlc_ppp_rx_bh;
chan->rx_bh_tq.data = (void*)dev;
queue_task(&chan->rx_bh_tq, &tq_immediate);
mark_bh(IMMEDIATE_BH);
}
if (int_events & SCCM_TXE)
{
chan->stats.tx_errors++;
DEBUG("%s: SCCM_TXE=%ld\n", dev->name, chan->stats.tx_errors);
}
if (int_events & (SCCM_TX | SCCM_TXE))
{
// Queue the task into the immediate bottom half queue
chan->tx_bh_tq.routine = hdlc_ppp_tx_bh;
chan->tx_bh_tq.data = (void*)dev;
queue_task(&chan->tx_bh_tq, &tq_immediate);
mark_bh(IMMEDIATE_BH);
}
// Finished with the ISR
dev->interrupt = 0;
}
//--------------------------------------------------------------
// hdlc_ppp_tx_bh()
//
// Return:
// None.
//
// Input Parameters:
// (I) dev_id - pointer to this driver's device structure
//
// This is one of the bottom half handlers for hdlc_ppp_interrupt().
// It completes the service of TX buffers that have completed.
static void hdlc_ppp_tx_bh(void* dev_id)
{
struct device* dev = (struct device*)dev_id;
hdlc_channel_t* chan = (hdlc_channel_t*)dev->priv;
volatile cbd_t* bdp;
int i;
DEBUG("%s: hdlc_ppp_tx_bh()\n", dev->name);
// Adjust module count
// Scan TX bd's and cleanup
for (i = 0; i < TX_RING_SIZE; ++i)
{
bdp = chan->tx_bd[i];
// Don't touch buffers waiting to be transmitted
if (bdp->cbd_sc & BD_HDLC_TX_READY)
continue;
// Unused bd if its pointer is null
if (!chan->tx_skbuff[i])
continue;
// Free the socket buffer
dev_kfree_skb(chan->tx_skbuff[i]);
chan->tx_skbuff[i] = 0;
// We have at least one TX Buff Descr available now
dev->tbusy = 0;
// Accumulate TX statistics
if (bdp->cbd_sc & BD_HDLC_TX_UN)
{
DEBUG("%s: BD_HDLC_TX_UN\n", dev->name);
chan->stats.tx_fifo_errors++;
}
if (bdp->cbd_sc & BD_HDLC_TX_CL)
{
DEBUG("%s: BD_HDLC_TX_CL\n", dev->name);
chan->stats.tx_carrier_errors++;
}
}
}
//--------------------------------------------------------------
// hdlc_ppp_rx_bh()
//
// Return:
// None.
//
// Input Parameters:
// (I) dev_id - pointer to this driver's device structure
//
// This is one of the bottom half handlers for hdlc_ppp_interrupt().
// It completes the service of RX buffers that have completed.
static void hdlc_ppp_rx_bh(void* dev_id)
{
struct device* dev = (struct device*)dev_id;
hdlc_channel_t* chan = (hdlc_channel_t*)dev->priv;
volatile scc_hdlc_t* hp = (scc_hdlc_t*)dev->mem_start;
volatile cbd_t* bdp;
struct sk_buff* skb;
struct sk_buff* newskb;
ushort pkt_len;
ushort rx_stat;
int rxerr;
int i;
DEBUG("%s: hdlc_ppp_rx_bh()\n", dev->name);
while (1)
{
// Get the tail of the queue
i = chan->rx_tail;
bdp = chan->rx_bd[i];
if (bdp->cbd_sc & BD_HDLC_RX_EMPTY)
return; // Nothing more to do
// Get a socket buffer to replace the one just consumed
newskb = hdlc_ppp_alloc_skb(hp->genscc.scc_mrblr);
if (!newskb)
{
printk("%s: no rx buffers, dropping packet\n",
dev->name);
chan->stats.rx_dropped++;
// Return here so we don't get out of sync w/cpm
return;
}
// We found a bd with data, bump the tail index
if (++chan->rx_tail >= RX_RING_SIZE)
chan->rx_tail = 0;
// Get the socket data buffer
skb = chan->rx_skbuff[i];
chan->rx_skbuff[i] = newskb;
pkt_len = bdp->cbd_datlen;
// Get the status of the RX packet
rx_stat = bdp->cbd_sc;
// Clear status flags
bdp->cbd_sc &= ~BD_HDLC_RX_STATS;
// Set the physical address of the new socket buffer
bdp->cbd_bufaddr = __pa(newskb->data);
// Mark ready to receive data
bdp->cbd_sc |= BD_HDLC_RX_EMPTY;
// Update statistics
chan->stats.rx_packets++;
chan->stats.rx_bytes += pkt_len;
rxerr = 0;
if (rx_stat & BD_HDLC_RX_DE)
{
rxerr = 1;
DEBUG("%s: hdlc_ppp_rx_bh(): RX_DE\n", dev->name);
}
if (rx_stat & BD_HDLC_RX_LG)
{
chan->stats.rx_length_errors++;
rxerr = 1;
DEBUG("%s: hdlc_ppp_rx_bh(): RX_LG\n", dev->name);
}
if (rx_stat & BD_HDLC_RX_NO)
{
chan->stats.rx_frame_errors++;
rxerr = 1;
DEBUG("%s: hdlc_ppp_rx_bh(): RX_NO\n", dev->name);
}
if (rx_stat & BD_HDLC_RX_AB)
{
rxerr = 1;
DEBUG("%s: hdlc_ppp_rx_bh(): RX_AB\n", dev->name);
}
if (rx_stat & BD_HDLC_RX_CRC)
{
chan->stats.rx_crc_errors++;
rxerr = 1;
DEBUG("%s: hdlc_ppp_rx_bh(): RX_CRC\n", dev->name);
}
if (rx_stat & BD_HDLC_RX_OV)
{
chan->stats.rx_over_errors++;
rxerr = 1;
DEBUG("%s: hdlc_ppp_rx_bh(): RX_OV\n", dev->name);
}
if (rx_stat & BD_HDLC_RX_CD)
{
chan->stats.rx_missed_errors++;
rxerr = 1;
DEBUG("%s: hdlc_ppp_rx_bh(): RX_CD\n", dev->name);
}
//chan->stats.rx_fifo_errors
if (rxerr)
{
DEBUG("%s: hdlc_ppp_rx_bh(): rxerr=0x%x\n",
dev->name, rxerr);
chan->stats.rx_errors++;
}
// Set the length of the received data, minus the CRC
skb_put(skb, pkt_len - 2);
// Set the protocol type
skb->protocol=htons(ETH_P_WAN_PPP);
// Point to the mac address (first HDLC byte)
skb->mac.raw = skb->data;
// This packet is handled by the SYNC PPP driver
skb->dev = &(chan->pppdev.dev);
// Send it to the PPP layer
netif_rx(skb);
}
}
^ permalink raw reply [flat|nested] 2+ messages in thread