📄 pi2.c
字号:
/* pi2.c: Driver for the Ottawa Amateur Radio Club PI and PI2 interface. Copyright (c) 1994 David Perry This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139, USA. The file skeleton.c by Donald Becker was used as a starting point for this driver. Revision History April 6, 1994 (dp) Created version 0.0 ALPHA April 10, 1994 (dp) Included cleanup, suggestions from J. P. Morrison. version 0.1 ALPHA April 13, 1994 (dp) Included address probing from JPM, autoirq version 0.2 ALPHA April 14, 1994 (ac) Sketched in the NET3 changes. April 17, 1994 (dp) Finished the NET3 changes. Used init_etherdev() instead of kmalloc() to ensure that DMA buffers will reside under the 16 meg line. version 0.4 ALPHA April 18, 1994 (dp) Now using the kernel provided sk_buff handling functions. Fixed a nasty problem with DMA. version 0.5 ALPHA June 6, 1994 (ac) Fixed to match the buffer locking changes. Added a hack to fix a funny I see (search for HACK) and fixed the calls in init() so it doesn't migrate module based ethernet cards up to eth2 Took out the old module ideas as they are no longer relevant to the PI driver. July 16, 1994 (dp) Fixed the B channel rx overrun problem ac referred to above. Also added a bit of a hack to improve the maximum baud rate on the B channel (Search for STUFF2). Included ioctl stuff from John Paul Morrison. version 0.6 ALPHA Feb 9, 1995 (dp) Updated for 1.1.90 kernel version 0.7 ALPHA Apr 6, 1995 (ac) Tweaks for NET3 pre snapshot 002 AX.25 April 23, 1995 (dp) Fixed ioctl so it works properly with piconfig program when changing the baud rate or clock mode. version 0.8 ALPHA July 17, 1995 (ac) Finally polishing of AX25.030+ support Oct 29, 1995 (ac) A couple of minor fixes before this, and this release changes to the proper set_mac_address semantics which will break a few programs I suspect. Aug 18, 1996 (jsn) Converted to be used as a module. Dec 13, 1996 (jsn) Fixed to match Linux networking changes.*//* The following #define invokes a hack that will improve performance (baud) for the B port. The up side is it makes 9600 baud work ok on the B port. It may do 38400, depending on the host. The down side is it burns up CPU cycles with ints locked for up to 1 character time, at the beginning of each transmitted packet. If this causes you to lose sleep, #undefine it.*//*#define STUFF2 1*//* The default configuration */#define PI_DMA 3#define DEF_A_SPEED 0 /* 0 means external clock */#define DEF_A_TXDELAY 15 /* 15 mS transmit delay */#define DEF_A_PERSIST 128 /* 50% persistence */#define DEF_A_SLOTIME 15 /* 15 mS slot time */#define DEF_A_SQUELDELAY 1 /* 1 mS squelch delay - allows fcs and flag */#define DEF_A_CLOCKMODE 0 /* clock mode - 0 is normal */#define DEF_B_SPEED 1200 /* 1200 baud */#define DEF_B_TXDELAY 40 /* 400 mS */#define DEF_B_PERSIST 128 /* 50% */#define DEF_B_SLOTIME 30 /* 300 mS */#define DEF_B_SQUELDELAY 3 /* 30 mS */#define DEF_B_CLOCKMODE 0 /* Normal clock mode *//* The following #define is only really required for the PI card, not the PI2 - but it's safer to leave it in. */#define REALLY_SLOW_IO 1#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/in.h>#include <linux/malloc.h>#include <linux/string.h>#include <linux/errno.h>#include <asm/system.h>#include <asm/bitops.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/uaccess.h>#include <linux/inet.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/timer.h>#include <linux/if_arp.h>#include <linux/pi2.h>#include <linux/init.h>#include "z8530.h"#include <net/ax25.h>struct mbuf { struct mbuf *next; int cnt; char data[0];};/* * The actual devices we will use *//* * PI device declarations. */static int pi0_preprobe(struct device *dev){return 0;} /* Dummy probe function */static struct device pi0a = { "pi0a", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pi0_preprobe };static struct device pi0b = { "pi0b", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, pi0_preprobe };/* The number of low I/O ports used by the card. */#define PI_TOTAL_SIZE 8/* Index to functions, as function prototypes. */static int pi_probe(struct device *dev, int card_type);static int pi_open(struct device *dev);static int pi_send_packet(struct sk_buff *skb, struct device *dev);static void pi_interrupt(int reg_ptr, void *dev_id, struct pt_regs *regs);static int pi_close(struct device *dev);static int pi_ioctl(struct device *dev, struct ifreq *ifr, int cmd);static struct net_device_stats *pi_get_stats(struct device *dev);static void rts(struct pi_local *lp, int x);static void b_rxint(struct device *dev, struct pi_local *lp);static void b_txint(struct pi_local *lp);static void b_exint(struct pi_local *lp);static void a_rxint(struct device *dev, struct pi_local *lp);static void a_txint(struct pi_local *lp);static void a_exint(struct pi_local *lp);static char *get_dma_buffer(unsigned long *mem_ptr);static int valid_dma_page(unsigned long addr, unsigned long dev_buffsize);static char ax25_bcast[7] ={'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};static char ax25_test[7] ={'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};static int ext2_secrm_seed = 152; /* Random generator base */extern inline unsigned char random(void){ return (unsigned char) (ext2_secrm_seed = ext2_secrm_seed * 69069l + 1);}extern inline void wrtscc(int cbase, int ctl, int sccreg, int val){ /* assume caller disables interrupts! */ outb_p(0, cbase + DMAEN); /* Disable DMA while we touch the scc */ outb_p(sccreg, ctl); /* Select register */ outb_p(val, ctl); /* Output value */ outb_p(1, cbase + DMAEN); /* Enable DMA */}extern inline int rdscc(int cbase, int ctl, int sccreg){ int retval; /* assume caller disables interrupts! */ outb_p(0, cbase + DMAEN); /* Disable DMA while we touch the scc */ outb_p(sccreg, ctl); /* Select register */ retval = inb_p(ctl); outb_p(1, cbase + DMAEN); /* Enable DMA */ return retval;}static void switchbuffers(struct pi_local *lp){ if (lp->rcvbuf == lp->rxdmabuf1) lp->rcvbuf = lp->rxdmabuf2; else lp->rcvbuf = lp->rxdmabuf1;}static void hardware_send_packet(struct pi_local *lp, struct sk_buff *skb){ char kickflag; unsigned long flags; lp->stats.tx_packets++; save_flags(flags); cli(); kickflag = (skb_peek(&lp->sndq) == NULL) && (lp->sndbuf == NULL); restore_flags(flags); skb_queue_tail(&lp->sndq, skb); if (kickflag) { /* simulate interrupt to xmit */ switch (lp->base & 2) { case 2: a_txint(lp); /* process interrupt */ break; case 0: save_flags(flags); cli(); if (lp->tstate == IDLE) b_txint(lp); restore_flags(flags); break; } }}static void setup_rx_dma(struct pi_local *lp){ unsigned long flags; int cmd; unsigned long dma_abs; unsigned dmachan; save_flags(flags); cli(); dma_abs = (unsigned long) (lp->rcvbuf->data); dmachan = lp->dmachan; cmd = lp->base + CTL; if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf))) panic("PI: RX buffer violates DMA boundary!"); /* Get ready for RX DMA */ wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB); disable_dma(dmachan); clear_dma_ff(dmachan); /* Set DMA mode register to single transfers, incrementing address, * auto init, writes */ set_dma_mode(dmachan, DMA_MODE_READ | 0x10); set_dma_addr(dmachan, dma_abs); set_dma_count(dmachan, lp->bufsiz); enable_dma(dmachan); /* If a packet is already coming in, this line is supposed to avoid receiving a partial packet. */ wrtscc(lp->cardbase, cmd, R0, RES_Rx_CRC); /* Enable RX dma */ wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | WT_RDY_RT | INT_ERR_Rx | EXT_INT_ENAB); restore_flags(flags);}static void setup_tx_dma(struct pi_local *lp, int length){ unsigned long dma_abs; unsigned long flags; unsigned long dmachan; save_flags(flags); cli(); dmachan = lp->dmachan; dma_abs = (unsigned long) (lp->txdmabuf); if(!valid_dma_page(dma_abs, DMA_BUFF_SIZE + sizeof(struct mbuf))) panic("PI: TX buffer violates DMA boundary!"); disable_dma(dmachan); /* Set DMA mode register to single transfers, incrementing address, * no auto init, reads */ set_dma_mode(dmachan, DMA_MODE_WRITE); clear_dma_ff(dmachan); set_dma_addr(dmachan, dma_abs); /* output byte count */ set_dma_count(dmachan, length); restore_flags(flags);}static void tdelay(struct pi_local *lp, int time){ int port; unsigned int t1; unsigned char sc; if (lp->base & 2) { /* If A channel */ sc = SC1; t1 = time; port = lp->cardbase + TMR1; } else { sc = SC2; t1 = 10 * time; /* 10s of milliseconds for the B channel */ port = lp->cardbase + TMR2; wrtscc(lp->cardbase, lp->base + CTL, R1, INT_ALL_Rx | EXT_INT_ENAB); } /* Setup timer sc */ outb_p(sc | LSB_MSB | MODE0, lp->cardbase + TMRCMD); /* times 2 to make millisecs */ outb_p((t1 << 1) & 0xFF, port); outb_p((t1 >> 7) & 0xFF, port); /* Enable correct int for timeout */ wrtscc(lp->cardbase, lp->base + CTL, R15, CTSIE); wrtscc(lp->cardbase, lp->base + CTL, R0, RES_EXT_INT);}static void a_txint(struct pi_local *lp){ int cmd; unsigned long flags; save_flags(flags); cli(); cmd = CTL + lp->base; switch (lp->tstate) { case IDLE: /* Transmitter idle. Find a frame for transmission */ if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) { rts(lp, OFF); restore_flags(flags); return; } /* If a buffer to send, we drop thru here */ case DEFER: /* we may have deferred prev xmit attempt */ /* Check DCD - debounce it * See Intel Microcommunications Handbook, p2-308 */ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) { lp->tstate = DEFER; tdelay(lp, 100); /* defer until DCD transition or timeout */ wrtscc(lp->cardbase, cmd, R15, CTSIE | DCDIE); restore_flags(flags); return; } if (random() > lp->persist) { lp->tstate = DEFER; tdelay(lp, lp->slotime); restore_flags(flags); return; } /* Assert RTS early minimize collision window */ wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8); rts(lp, ON); /* Transmitter on */ lp->tstate = ST_TXDELAY; tdelay(lp, lp->txdelay); restore_flags(flags); return; default: break; } /* end switch(lp->state) */ restore_flags(flags);} /*a_txint */static void a_exint(struct pi_local *lp){ unsigned long flags; int cmd; char st; int length; save_flags(flags); cli(); /* disable interrupts */ st = rdscc(lp->cardbase, lp->base + CTL, R0); /* Fetch status */ /* reset external status latch */ wrtscc(lp->cardbase, CTL + lp->base, R0, RES_EXT_INT); cmd = lp->base + CTL; if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT)) { setup_rx_dma(lp); lp->rstate = ACTIVE; } switch (lp->tstate) { case ACTIVE: kfree_skb(lp->sndbuf); lp->sndbuf = NULL; lp->tstate = FLAGOUT; tdelay(lp, lp->squeldelay); break; case FLAGOUT: if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) { /* Nothing to send - return to receive mode */ lp->tstate = IDLE; rts(lp, OFF);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -