⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hfc4s8s_l1.c

📁 底层驱动开发
💻 C
📖 第 1 页 / 共 3 页
字号:
/*************************************************************************//* $Id: hfc4s8s_l1.c,v 1.10 2005/02/09 16:31:09 martinb1 Exp $           *//* HFC-4S/8S low layer interface for Cologne Chip HFC-4S/8S isdn chips   *//* The low layer (L1) is implemented as a loadable module for usage with *//* the HiSax isdn driver for passive cards.                              *//*                                                                       *//* Author: Werner Cornelius                                              *//* (C) 2003 Cornelius Consult (werner@cornelius-consult.de)              *//*                                                                       *//* Driver maintained by Cologne Chip                                     *//*   - Martin Bachem, support@colognechip.com                            *//*                                                                       *//* This driver only works with chip revisions >= 1, older revision 0     *//* engineering samples (only first manufacturer sample cards) will not   *//* work and are rejected by the driver.                                  *//*                                                                       *//* This file distributed under the GNU GPL.                              *//*                                                                       *//* See Version History at the end of this file                           *//*                                                                       *//*************************************************************************/#include <linux/module.h>#include <linux/init.h>#include <linux/config.h>#include <linux/pci.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/timer.h>#include <linux/skbuff.h>#include <linux/wait.h>#include <asm/io.h>#include "hisax_if.h"#include "hfc4s8s_l1.h"static const char hfc4s8s_rev[] = "Revision: 1.10";/***************************************************************//* adjustable transparent mode fifo threshold                  *//* The value defines the used fifo threshold with the equation *//*                                                             *//* notify number of bytes = 2 * 2 ^ TRANS_FIFO_THRES           *//*                                                             *//* The default value is 5 which results in a buffer size of 64 *//* and an interrupt rate of 8ms.                               *//* The maximum value is 7 due to fifo size restrictions.       *//* Values below 3-4 are not recommended due to high interrupt  *//* load of the processor. For non critical applications the    *//* value should be raised to 7 to reduce any interrupt overhead*//***************************************************************/#define TRANS_FIFO_THRES 5/*************//* constants *//*************/#define CLOCKMODE_0     0	/* ext. 24.576 MhZ clk freq, int. single clock mode */#define CLOCKMODE_1     1	/* ext. 49.576 MhZ clk freq, int. single clock mode */#define CHIP_ID_SHIFT   4#define HFC_MAX_ST 8#define MAX_D_FRAME_SIZE 270#define MAX_B_FRAME_SIZE 1536#define TRANS_TIMER_MODE (TRANS_FIFO_THRES & 0xf)#define TRANS_FIFO_BYTES (2 << TRANS_FIFO_THRES)#define MAX_F_CNT 0x0f#define CLKDEL_NT 0x6c#define CLKDEL_TE 0xf#define CTRL0_NT  4#define CTRL0_TE  0#define L1_TIMER_T4 2		/* minimum in jiffies */#define L1_TIMER_T3 (7 * HZ)	/* activation timeout */#define L1_TIMER_T1 ((120 * HZ) / 1000)	/* NT mode deactivation timeout *//******************//* types and vars *//******************/static int card_cnt;/* private driver_data */typedef struct {	int chip_id;	int clock_mode;	int max_st_ports;	char *device_name;} hfc4s8s_param;static struct pci_device_id hfc4s8s_ids[] = {	{.vendor = PCI_VENDOR_ID_CCD,	 .device = PCI_DEVICE_ID_4S,	 .subvendor = 0x1397,	 .subdevice = 0x08b4,	 .driver_data =	 (unsigned long) &((hfc4s8s_param) {CHIP_ID_4S, CLOCKMODE_0, 4,					    "HFC-4S Evaluation Board"}),	 },	{.vendor = PCI_VENDOR_ID_CCD,	 .device = PCI_DEVICE_ID_8S,	 .subvendor = 0x1397,	 .subdevice = 0x16b8,	 .driver_data =	 (unsigned long) &((hfc4s8s_param) {CHIP_ID_8S, CLOCKMODE_0, 8,					    "HFC-8S Evaluation Board"}),	 },	{.vendor = PCI_VENDOR_ID_CCD,	 .device = PCI_DEVICE_ID_4S,	 .subvendor = 0x1397,	 .subdevice = 0xb520,	 .driver_data =	 (unsigned long) &((hfc4s8s_param) {CHIP_ID_4S, CLOCKMODE_1, 4,					    "IOB4ST"}),	 },	{.vendor = PCI_VENDOR_ID_CCD,	 .device = PCI_DEVICE_ID_8S,	 .subvendor = 0x1397,	 .subdevice = 0xb522,	 .driver_data =	 (unsigned long) &((hfc4s8s_param) {CHIP_ID_8S, CLOCKMODE_1, 8,					    "IOB8ST"}),	 },	{}};MODULE_DEVICE_TABLE(pci, hfc4s8s_ids);MODULE_AUTHOR("Werner Cornelius, werner@cornelius-consult.de");MODULE_DESCRIPTION("ISDN layer 1 for Cologne Chip HFC-4S/8S chips");MODULE_LICENSE("GPL");/***********//* layer 1 *//***********/struct hfc4s8s_btype {	spinlock_t lock;	struct hisax_b_if b_if;	struct hfc4s8s_l1 *l1p;	struct sk_buff_head tx_queue;	struct sk_buff *tx_skb;	struct sk_buff *rx_skb;	__u8 *rx_ptr;	int tx_cnt;	int bchan;	int mode;};struct _hfc4s8s_hw;struct hfc4s8s_l1 {	spinlock_t lock;	struct _hfc4s8s_hw *hw;	/* pointer to hardware area */	int l1_state;		/* actual l1 state */	struct timer_list l1_timer;	/* layer 1 timer structure */	int nt_mode;		/* set to nt mode */	int st_num;		/* own index */	int enabled;		/* interface is enabled */	struct sk_buff_head d_tx_queue;	/* send queue */	int tx_cnt;		/* bytes to send */	struct hisax_d_if d_if;	/* D-channel interface */	struct hfc4s8s_btype b_ch[2];	/* B-channel data */	struct hisax_b_if *b_table[2];};/**********************//* hardware structure *//**********************/typedef struct _hfc4s8s_hw {	spinlock_t lock;	int cardnum;	int ifnum;	int iobase;	int nt_mode;	u_char *membase;	u_char *hw_membase;	void *pdev;	int max_fifo;	hfc4s8s_param driver_data;	int irq;	int fifo_sched_cnt;	struct work_struct tqueue;	struct hfc4s8s_l1 l1[HFC_MAX_ST];	char card_name[60];	struct {		u_char r_irq_ctrl;		u_char r_ctrl0;		volatile u_char r_irq_statech;	/* active isdn l1 status */		u_char r_irqmsk_statchg;	/* enabled isdn status ints */		u_char r_irq_fifo_blx[8];	/* fifo status registers */		u_char fifo_rx_trans_enables[8];	/* mask for enabled transparent rx fifos */		u_char fifo_slow_timer_service[8];	/* mask for fifos needing slower timer service */		volatile u_char r_irq_oview;	/* contents of overview register */		volatile u_char timer_irq;		int timer_usg_cnt;	/* number of channels using timer */	} mr;} hfc4s8s_hw;/***************************//* inline function defines *//***************************/#ifdef CONFIG_HISAX_HFC4S8S_PCIMEM	/* inline functions mempry mapped *//* memory write and dummy IO read to avoid PCI byte merge problems */#define Write_hfc8(a,b,c) {(*((volatile u_char *)(a->membase+b)) = c); inb(a->iobase+4);}/* memory write without dummy IO access for fifo data access */#define fWrite_hfc8(a,b,c) (*((volatile u_char *)(a->membase+b)) = c)#define Read_hfc8(a,b) (*((volatile u_char *)(a->membase+b)))#define Write_hfc16(a,b,c) (*((volatile unsigned short *)(a->membase+b)) = c)#define Read_hfc16(a,b) (*((volatile unsigned short *)(a->membase+b)))#define Write_hfc32(a,b,c) (*((volatile unsigned long *)(a->membase+b)) = c)#define Read_hfc32(a,b) (*((volatile unsigned long *)(a->membase+b)))#define wait_busy(a) {while ((Read_hfc8(a, R_STATUS) & M_BUSY));}#define PCI_ENA_MEMIO	0x03#else/* inline functions io mapped */static inline voidSetRegAddr(hfc4s8s_hw * a, u_char b){	outb(b, (a->iobase) + 4);}static inline u_charGetRegAddr(hfc4s8s_hw * a){	return (inb((volatile u_int) (a->iobase + 4)));}static inline voidWrite_hfc8(hfc4s8s_hw * a, u_char b, u_char c){	SetRegAddr(a, b);	outb(c, a->iobase);}static inline voidfWrite_hfc8(hfc4s8s_hw * a, u_char c){	outb(c, a->iobase);}static inline voidWrite_hfc16(hfc4s8s_hw * a, u_char b, u_short c){	SetRegAddr(a, b);	outw(c, a->iobase);}static inline voidWrite_hfc32(hfc4s8s_hw * a, u_char b, u_long c){	SetRegAddr(a, b);	outl(c, a->iobase);}static inline voidfWrite_hfc32(hfc4s8s_hw * a, u_long c){	outl(c, a->iobase);}static inline u_charRead_hfc8(hfc4s8s_hw * a, u_char b){	SetRegAddr(a, b);	return (inb((volatile u_int) a->iobase));}static inline u_charfRead_hfc8(hfc4s8s_hw * a){	return (inb((volatile u_int) a->iobase));}static inline u_shortRead_hfc16(hfc4s8s_hw * a, u_char b){	SetRegAddr(a, b);	return (inw((volatile u_int) a->iobase));}static inline u_longRead_hfc32(hfc4s8s_hw * a, u_char b){	SetRegAddr(a, b);	return (inl((volatile u_int) a->iobase));}static inline u_longfRead_hfc32(hfc4s8s_hw * a){	return (inl((volatile u_int) a->iobase));}static inline voidwait_busy(hfc4s8s_hw * a){	SetRegAddr(a, R_STATUS);	while (inb((volatile u_int) a->iobase) & M_BUSY);}#define PCI_ENA_REGIO	0x01#endif				/* CONFIG_HISAX_HFC4S8S_PCIMEM *//******************************************************//* function to read critical counter registers that   *//* may be udpated by the chip during read             *//******************************************************/static u_charRead_hfc8_stable(hfc4s8s_hw * hw, int reg){	u_char ref8;	u_char in8;	ref8 = Read_hfc8(hw, reg);	while (((in8 = Read_hfc8(hw, reg)) != ref8)) {		ref8 = in8;	}	return in8;}static intRead_hfc16_stable(hfc4s8s_hw * hw, int reg){	int ref16;	int in16;	ref16 = Read_hfc16(hw, reg);	while (((in16 = Read_hfc16(hw, reg)) != ref16)) {		ref16 = in16;	}	return in16;}/*****************************//* D-channel call from HiSax *//*****************************/static voiddch_l2l1(struct hisax_d_if *iface, int pr, void *arg){	struct hfc4s8s_l1 *l1 = iface->ifc.priv;	struct sk_buff *skb = (struct sk_buff *) arg;	u_long flags;	switch (pr) {		case (PH_DATA | REQUEST):			if (!l1->enabled) {				dev_kfree_skb(skb);				break;			}			spin_lock_irqsave(&l1->lock, flags);			skb_queue_tail(&l1->d_tx_queue, skb);			if ((skb_queue_len(&l1->d_tx_queue) == 1) &&			    (l1->tx_cnt <= 0)) {				l1->hw->mr.r_irq_fifo_blx[l1->st_num] |=				    0x10;				spin_unlock_irqrestore(&l1->lock, flags);				schedule_work(&l1->hw->tqueue);			} else				spin_unlock_irqrestore(&l1->lock, flags);			break;		case (PH_ACTIVATE | REQUEST):			if (!l1->enabled)				break;			if (!l1->nt_mode) {				if (l1->l1_state < 6) {					spin_lock_irqsave(&l1->lock,							  flags);					Write_hfc8(l1->hw, R_ST_SEL,						   l1->st_num);					Write_hfc8(l1->hw, A_ST_WR_STA,						   0x60);					mod_timer(&l1->l1_timer,						  jiffies + L1_TIMER_T3);					spin_unlock_irqrestore(&l1->lock,							       flags);				} else if (l1->l1_state == 7)					l1->d_if.ifc.l1l2(&l1->d_if.ifc,							  PH_ACTIVATE |							  INDICATION,							  NULL);			} else {				if (l1->l1_state != 3) {					spin_lock_irqsave(&l1->lock,							  flags);					Write_hfc8(l1->hw, R_ST_SEL,						   l1->st_num);					Write_hfc8(l1->hw, A_ST_WR_STA,						   0x60);					spin_unlock_irqrestore(&l1->lock,							       flags);				} else if (l1->l1_state == 3)					l1->d_if.ifc.l1l2(&l1->d_if.ifc,							  PH_ACTIVATE |							  INDICATION,							  NULL);			}			break;		default:			printk(KERN_INFO			       "HFC-4S/8S: Unknown D-chan cmd 0x%x received, ignored\n",			       pr);			break;	}	if (!l1->enabled)		l1->d_if.ifc.l1l2(&l1->d_if.ifc,				  PH_DEACTIVATE | INDICATION, NULL);}				/* dch_l2l1 *//*****************************//* B-channel call from HiSax *//*****************************/static voidbch_l2l1(struct hisax_if *ifc, int pr, void *arg){	struct hfc4s8s_btype *bch = ifc->priv;	struct hfc4s8s_l1 *l1 = bch->l1p;	struct sk_buff *skb = (struct sk_buff *) arg;	int mode = (int) arg;	u_long flags;	switch (pr) {		case (PH_DATA | REQUEST):			if (!l1->enabled || (bch->mode == L1_MODE_NULL)) {				dev_kfree_skb(skb);				break;			}			spin_lock_irqsave(&l1->lock, flags);			skb_queue_tail(&bch->tx_queue, skb);			if (!bch->tx_skb && (bch->tx_cnt <= 0)) {				l1->hw->mr.r_irq_fifo_blx[l1->st_num] |=				    ((bch->bchan == 1) ? 1 : 4);				spin_unlock_irqrestore(&l1->lock, flags);				schedule_work(&l1->hw->tqueue);			} else				spin_unlock_irqrestore(&l1->lock, flags);			break;		case (PH_ACTIVATE | REQUEST):		case (PH_DEACTIVATE | REQUEST):			if (!l1->enabled)				break;			if (pr == (PH_DEACTIVATE | REQUEST))				mode = L1_MODE_NULL;			switch (mode) {				case L1_MODE_HDLC:					spin_lock_irqsave(&l1->lock,							  flags);					l1->hw->mr.timer_usg_cnt++;					l1->hw->mr.					    fifo_slow_timer_service[l1->								    st_num]					    |=					    ((bch->bchan ==					      1) ? 0x2 : 0x8);					Write_hfc8(l1->hw, R_FIFO,						   (l1->st_num * 8 +						    ((bch->bchan ==						      1) ? 0 : 2)));					wait_busy(l1->hw);					Write_hfc8(l1->hw, A_CON_HDLC, 0xc);	/* HDLC mode, flag fill, connect ST */					Write_hfc8(l1->hw, A_SUBCH_CFG, 0);	/* 8 bits */					Write_hfc8(l1->hw, A_IRQ_MSK, 1);	/* enable TX interrupts for hdlc */					Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);	/* reset fifo */					wait_busy(l1->hw);					Write_hfc8(l1->hw, R_FIFO,						   (l1->st_num * 8 +						    ((bch->bchan ==						      1) ? 1 : 3)));					wait_busy(l1->hw);					Write_hfc8(l1->hw, A_CON_HDLC, 0xc);	/* HDLC mode, flag fill, connect ST */					Write_hfc8(l1->hw, A_SUBCH_CFG, 0);	/* 8 bits */					Write_hfc8(l1->hw, A_IRQ_MSK, 1);	/* enable RX interrupts for hdlc */					Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);	/* reset fifo */					Write_hfc8(l1->hw, R_ST_SEL,						   l1->st_num);					l1->hw->mr.r_ctrl0 |=					    (bch->bchan & 3);					Write_hfc8(l1->hw, A_ST_CTRL0,						   l1->hw->mr.r_ctrl0);					bch->mode = L1_MODE_HDLC;					spin_unlock_irqrestore(&l1->lock,							       flags);					bch->b_if.ifc.l1l2(&bch->b_if.ifc,							   PH_ACTIVATE |							   INDICATION,							   NULL);					break;				case L1_MODE_TRANS:					spin_lock_irqsave(&l1->lock,							  flags);					l1->hw->mr.					    fifo_rx_trans_enables[l1->								  st_num]					    |=					    ((bch->bchan ==					      1) ? 0x2 : 0x8);					l1->hw->mr.timer_usg_cnt++;					Write_hfc8(l1->hw, R_FIFO,						   (l1->st_num * 8 +						    ((bch->bchan ==						      1) ? 0 : 2)));					wait_busy(l1->hw);					Write_hfc8(l1->hw, A_CON_HDLC, 0xf);	/* Transparent mode, 1 fill, connect ST */					Write_hfc8(l1->hw, A_SUBCH_CFG, 0);	/* 8 bits */					Write_hfc8(l1->hw, A_IRQ_MSK, 0);	/* disable TX interrupts */					Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);	/* reset fifo */					wait_busy(l1->hw);					Write_hfc8(l1->hw, R_FIFO,						   (l1->st_num * 8 +						    ((bch->bchan ==						      1) ? 1 : 3)));					wait_busy(l1->hw);					Write_hfc8(l1->hw, A_CON_HDLC, 0xf);	/* Transparent mode, 1 fill, connect ST */					Write_hfc8(l1->hw, A_SUBCH_CFG, 0);	/* 8 bits */					Write_hfc8(l1->hw, A_IRQ_MSK, 0);	/* disable RX interrupts */					Write_hfc8(l1->hw, A_INC_RES_FIFO, 2);	/* reset fifo */					Write_hfc8(l1->hw, R_ST_SEL,						   l1->st_num);					l1->hw->mr.r_ctrl0 |=					    (bch->bchan & 3);					Write_hfc8(l1->hw, A_ST_CTRL0,						   l1->hw->mr.r_ctrl0);					bch->mode = L1_MODE_TRANS;					spin_unlock_irqrestore(&l1->lock,							       flags);					bch->b_if.ifc.l1l2(&bch->b_if.ifc,							   PH_ACTIVATE |							   INDICATION,							   NULL);					break;				default:					if (bch->mode == L1_MODE_NULL)						break;					spin_lock_irqsave(&l1->lock,							  flags);					l1->hw->mr.					    fifo_slow_timer_service[l1->								    st_num]					    &=					    ~((bch->bchan ==					       1) ? 0x3 : 0xc);					l1->hw->mr.					    fifo_rx_trans_enables[l1->								  st_num]					    &=					    ~((bch->bchan ==					       1) ? 0x3 : 0xc);					l1->hw->mr.timer_usg_cnt--;					Write_hfc8(l1->hw, R_FIFO,						   (l1->st_num * 8 +						    ((bch->bchan ==						      1) ? 0 : 2)));

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -