📄 hfc4s8s_l1.c
字号:
/*************************************************************************//* $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 + -