* HDLC driver for MPC8xx SCC
@ 2004-05-25 0:39 HoJin
2004-05-25 1:48 ` leeyang
0 siblings, 1 reply; 2+ messages in thread
From: HoJin @ 2004-05-25 0:39 UTC (permalink / raw)
To: linuxppc-embedded
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
** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/
^ permalink raw reply [flat|nested] 2+ messages in thread
* 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
end of thread, other threads:[~2004-05-25 1:48 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-05-25 0:39 HDLC driver for MPC8xx SCC HoJin
2004-05-25 1:48 ` leeyang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).