linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* 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).