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

📄 amd7930.c

📁 这个linux源代码是很全面的~基本完整了~使用c编译的~由于时间问题我没有亲自测试~但就算用来做参考资料也是非常好的
💻 C
📖 第 1 页 / 共 2 页
字号:
/* $Id: amd7930.c,v 1.1.4.1 2001/11/20 14:19:35 kai Exp $ * * HiSax ISDN driver - chip specific routines for AMD 7930 * * Author       Brent Baccala * Copyright    by Brent Baccala <baccala@FreeSoft.org> * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * *    - Existing ISDN HiSax driver provides all the smarts *    - it compiles, runs, talks to an isolated phone switch, connects *      to a Cisco, pings go through *    - AMD 7930 support only (no DBRI yet) *    - no US NI-1 support (may not work on US phone system - untested) *    - periodic packet loss, apparently due to lost interrupts *    - ISDN sometimes freezes, requiring reboot before it will work again * * The code is unreliable enough to be consider alpha * * This file is (c) under GNU General Public License * * Advanced Micro Devices' Am79C30A is an ISDN/audio chip used in the * SparcStation 1+.  The chip provides microphone and speaker interfaces * which provide mono-channel audio at 8K samples per second via either * 8-bit A-law or 8-bit mu-law encoding.  Also, the chip features an * ISDN BRI Line Interface Unit (LIU), I.430 S/T physical interface, * which performs basic D channel LAPD processing and provides raw * B channel data.  The digital audio channel, the two ISDN B channels, * and two 64 Kbps channels to the microprocessor are all interconnected * via a multiplexer. * * This driver interfaces to the Linux HiSax ISDN driver, which performs * all high-level Q.921 and Q.931 ISDN functions.  The file is not * itself a hardware driver; rather it uses functions exported by * the AMD7930 driver in the sparcaudio subsystem (drivers/sbus/audio), * allowing the chip to be simultaneously used for both audio and ISDN data. * The hardware driver does _no_ buffering, but provides several callbacks * which are called during interrupt service and should therefore run quickly. * * D channel transmission is performed by passing the hardware driver the * address and size of an skb's data area, then waiting for a callback * to signal successful transmission of the packet.  A task is then * queued to notify the HiSax driver that another packet may be transmitted. * * D channel reception is quite simple, mainly because of: *   1) the slow speed of the D channel - 16 kbps, and *   2) the presence of an 8- or 32-byte (depending on chip version) FIFO *      to buffer the D channel data on the chip * Worst case scenario of back-to-back packets with the 8 byte buffer * at 16 kbps yields an service time of 4 ms - long enough to preclude * the need for fancy buffering.  We queue a background task that copies * data out of the receive buffer into an skb, and the hardware driver * simply does nothing until we're done with the receive buffer and * reset it for a new packet. * * B channel processing is more complex, because of: *   1) the faster speed - 64 kbps, *   2) the lack of any on-chip buffering (it interrupts for every byte), and *   3) the lack of any chip support for HDLC encapsulation * * The HiSax driver can put each B channel into one of three modes - * L1_MODE_NULL (channel disabled), L1_MODE_TRANS (transparent data relay), * and L1_MODE_HDLC (HDLC encapsulation by low-level driver). * L1_MODE_HDLC is the most common, used for almost all "pure" digital * data sessions.  L1_MODE_TRANS is used for ISDN audio. * * HDLC B channel transmission is performed via a large buffer into * which the skb is copied while performing HDLC bit-stuffing.  A CRC * is computed and attached to the end of the buffer, which is then * passed to the low-level routines for raw transmission.  Once * transmission is complete, the hardware driver is set to enter HDLC * idle by successive transmission of mark (all 1) bytes, waiting for * the ISDN driver to prepare another packet for transmission and * deliver it. * * HDLC B channel reception is performed via an X-byte ring buffer * divided into N sections of X/N bytes each.  Defaults: X=256 bytes, N=4. * As the hardware driver notifies us that each section is full, we * hand it the next section and schedule a background task to peruse * the received section, bit-by-bit, with an HDLC decoder.  As * packets are detected, they are copied into a large buffer while * decoding HDLC bit-stuffing.  The ending CRC is verified, and if * it is correct, we alloc a new skb of the correct length (which we * now know), copy the packet into it, and hand it to the upper layers. * Optimization: for large packets, we hand the buffer (which also * happens to be an skb) directly to the upper layer after an skb_trim, * and alloc a new large buffer for future packets, thus avoiding a copy. * Then we return to HDLC processing; state is saved between calls. *  */#define __NO_VERSION__#include "hisax.h"#include "../../sbus/audio/amd7930.h"#include "isac.h"#include "isdnl1.h"#include "rawhdlc.h"#include <linux/interrupt.h>static const char *amd7930_revision = "$Revision: 1.1.4.1 $";#define RCV_BUFSIZE	1024	/* Size of raw receive buffer in bytes */#define RCV_BUFBLKS	4	/* Number of blocks to divide buffer into				 * (must divide RCV_BUFSIZE) */static void Bchan_fill_fifo(struct BCState *, struct sk_buff *);static voidBchan_xmt_bh(struct BCState *bcs){	struct sk_buff *skb;	if (bcs->hw.amd7930.tx_skb != NULL) {		dev_kfree_skb(bcs->hw.amd7930.tx_skb);		bcs->hw.amd7930.tx_skb = NULL;	}	if ((skb = skb_dequeue(&bcs->squeue))) {		Bchan_fill_fifo(bcs, skb);	} else {		clear_bit(BC_FLG_BUSY, &bcs->Flag);		bcs->event |= 1 << B_XMTBUFREADY;		queue_task(&bcs->tqueue, &tq_immediate);		mark_bh(IMMEDIATE_BH);	}}static voidBchan_xmit_callback(struct BCState *bcs){	queue_task(&bcs->hw.amd7930.tq_xmt, &tq_immediate);	mark_bh(IMMEDIATE_BH);}/* B channel transmission: two modes (three, if you count L1_MODE_NULL) * * L1_MODE_HDLC - We need to do HDLC encapsulation before transmiting * the packet (i.e. make_raw_hdlc_data).  Since this can be a * time-consuming operation, our completion callback just schedules * a bottom half to do encapsulation for the next packet.  In between, * the link will just idle * * L1_MODE_TRANS - Data goes through, well, transparent.  No HDLC encap, * and we can't just let the link idle, so the "bottom half" actually * gets called during the top half (it's our callback routine in this case), * but it's a lot faster now since we don't call make_raw_hdlc_data */static voidBchan_fill_fifo(struct BCState *bcs, struct sk_buff *skb){	struct IsdnCardState *cs = bcs->cs;	int len;	if ((cs->debug & L1_DEB_HSCX) || (cs->debug & L1_DEB_HSCX_FIFO)) {		char tmp[1024];		char *t = tmp;		t += sprintf(t, "amd7930_fill_fifo %c cnt %d",			     bcs->channel ? 'B' : 'A', skb->len);		if (cs->debug & L1_DEB_HSCX_FIFO)			QuickHex(t, skb->data, skb->len);		debugl1(cs, tmp);	}	if (bcs->mode == L1_MODE_HDLC) {		len = make_raw_hdlc_data(skb->data, skb->len,					 bcs->hw.amd7930.tx_buff, RAW_BUFMAX);		if (len > 0)			amd7930_bxmit(0, bcs->channel,				      bcs->hw.amd7930.tx_buff, len,				      (void *) &Bchan_xmit_callback,				      (void *) bcs);		dev_kfree_skb(skb);	} else if (bcs->mode == L1_MODE_TRANS) {		amd7930_bxmit(0, bcs->channel,			      bcs->hw.amd7930.tx_buff, skb->len,			      (void *) &Bchan_xmt_bh,			      (void *) bcs);		bcs->hw.amd7930.tx_skb = skb;	} else {		dev_kfree_skb(skb);	}}static voidBchan_mode(struct BCState *bcs, int mode, int bc){	struct IsdnCardState *cs = bcs->cs;	if (cs->debug & L1_DEB_HSCX) {		char tmp[40];		sprintf(tmp, "AMD 7930 mode %d bchan %d/%d",			mode, bc, bcs->channel);		debugl1(cs, tmp);	}	bcs->mode = mode;}/* Bchan_l2l1 is the entry point for upper layer routines that want to * transmit on the B channel.  PH_DATA_REQ is a normal packet that * we either start transmitting (if idle) or queue (if busy). * PH_PULL_REQ can be called to request a callback message (PH_PULL_CNF) * once the link is idle.  After a "pull" callback, the upper layer * routines can use PH_PULL_IND to send data. */static voidBchan_l2l1(struct PStack *st, int pr, void *arg){	struct sk_buff *skb = arg;	switch (pr) {		case (PH_DATA_REQ):			if (test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) {				skb_queue_tail(&st->l1.bcs->squeue, skb);			} else {				test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);				Bchan_fill_fifo(st->l1.bcs, skb);			}			break;		case (PH_PULL_IND):			if (test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) {				printk(KERN_WARNING "amd7930: this shouldn't happen\n");				break;			}			test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);			Bchan_fill_fifo(st->l1.bcs, skb);			break;		case (PH_PULL_REQ):			if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag)) {				clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);				st->l1.l1l2(st, PH_PULL_CNF, NULL);			} else				set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);			break;	}}/* Receiver callback and bottom half - decodes HDLC at leisure (if * L1_MODE_HDLC) and passes newly received skb on via bcs->rqueue.  If * a large packet is received, stick rv_skb (the buffer that the * packet has been decoded into) on the receive queue and alloc a new * (large) skb to act as buffer for future receives.  If a small * packet is received, leave rv_skb alone, alloc a new skb of the * correct size, and copy the packet into it */static voidBchan_recv_callback(struct BCState *bcs){	struct amd7930_hw *hw = &bcs->hw.amd7930;	hw->rv_buff_in += RCV_BUFSIZE/RCV_BUFBLKS;	hw->rv_buff_in %= RCV_BUFSIZE;	if (hw->rv_buff_in != hw->rv_buff_out) {		amd7930_brecv(0, bcs->channel,			      hw->rv_buff + hw->rv_buff_in,			      RCV_BUFSIZE/RCV_BUFBLKS,			      (void *) &Bchan_recv_callback, (void *) bcs);	}	queue_task(&hw->tq_rcv, &tq_immediate);	mark_bh(IMMEDIATE_BH);}static voidBchan_rcv_bh(struct BCState *bcs){	struct IsdnCardState *cs = bcs->cs;	struct amd7930_hw *hw = &bcs->hw.amd7930;	struct sk_buff *skb;	int len;	if (cs->debug & L1_DEB_HSCX) {		char tmp[1024];		sprintf(tmp, "amd7930_Bchan_rcv (%d/%d)",			hw->rv_buff_in, hw->rv_buff_out);		debugl1(cs, tmp);		QuickHex(tmp, hw->rv_buff + hw->rv_buff_out,			 RCV_BUFSIZE/RCV_BUFBLKS);		debugl1(cs, tmp);	}	do {		if (bcs->mode == L1_MODE_HDLC) {			while ((len = read_raw_hdlc_data(hw->hdlc_state,							 hw->rv_buff + hw->rv_buff_out, RCV_BUFSIZE/RCV_BUFBLKS,							 hw->rv_skb->tail, HSCX_BUFMAX))) {				if (len > 0 && (cs->debug & L1_DEB_HSCX_FIFO)) {					char tmp[1024];					char *t = tmp;					t += sprintf(t, "amd7930_Bchan_rcv %c cnt %d", bcs->channel ? 'B' : 'A', len);					QuickHex(t, hw->rv_skb->tail, len);					debugl1(cs, tmp);				}				if (len > HSCX_BUFMAX/2) {					/* Large packet received */					if (!(skb = dev_alloc_skb(HSCX_BUFMAX))) {						printk(KERN_WARNING "amd7930: receive out of memory");					} else {						skb_put(hw->rv_skb, len);						skb_queue_tail(&bcs->rqueue, hw->rv_skb);						hw->rv_skb = skb;						bcs->event |= 1 << B_RCVBUFREADY;						queue_task(&bcs->tqueue, &tq_immediate);					}				} else if (len > 0) {					/* Small packet received */					if (!(skb = dev_alloc_skb(len))) {						printk(KERN_WARNING "amd7930: receive out of memory\n");					} else {						memcpy(skb_put(skb, len), hw->rv_skb->tail, len);						skb_queue_tail(&bcs->rqueue, skb);						bcs->event |= 1 << B_RCVBUFREADY;						queue_task(&bcs->tqueue, &tq_immediate);						mark_bh(IMMEDIATE_BH);					}				} else {					/* Reception Error */					/* printk("amd7930: B channel receive error\n"); */				}			}		} else if (bcs->mode == L1_MODE_TRANS) {			if (!(skb = dev_alloc_skb(RCV_BUFSIZE/RCV_BUFBLKS))) {				printk(KERN_WARNING "amd7930: receive out of memory\n");			} else {				memcpy(skb_put(skb, RCV_BUFSIZE/RCV_BUFBLKS),				       hw->rv_buff + hw->rv_buff_out,				       RCV_BUFSIZE/RCV_BUFBLKS);				skb_queue_tail(&bcs->rqueue, skb);				bcs->event |= 1 << B_RCVBUFREADY;				queue_task(&bcs->tqueue, &tq_immediate);				mark_bh(IMMEDIATE_BH);			}		}		if (hw->rv_buff_in == hw->rv_buff_out) {			/* Buffer was filled up - need to restart receiver */			amd7930_brecv(0, bcs->channel,				      hw->rv_buff + hw->rv_buff_in,				      RCV_BUFSIZE/RCV_BUFBLKS,				      (void *) &Bchan_recv_callback,				      (void *) bcs);		}		hw->rv_buff_out += RCV_BUFSIZE/RCV_BUFBLKS;		hw->rv_buff_out %= RCV_BUFSIZE;	} while (hw->rv_buff_in != hw->rv_buff_out);}static voidBchan_close(struct BCState *bcs){	struct sk_buff *skb;	Bchan_mode(bcs, 0, 0);	amd7930_bclose(0, bcs->channel);	if (test_bit(BC_FLG_INIT, &bcs->Flag)) {		skb_queue_purge(&bcs->rqueue);		skb_queue_purge(&bcs->squeue);	}	test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);}static intBchan_open(struct BCState *bcs){	struct amd7930_hw *hw = &bcs->hw.amd7930;

⌨️ 快捷键说明

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