📄 icn.c
字号:
/* $Id: icn.c,v 1.65.6.8 2001/09/23 22:24:55 kai Exp $ * * ISDN low-level module for the ICN active ISDN-Card. * * Copyright 1994,95,96 by Fritz Elfert (fritz@isdn4linux.de) * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */#include "icn.h"#include <linux/module.h>#include <linux/init.h>static int portbase = ICN_BASEADDR;static unsigned long membase = ICN_MEMADDR;static char *icn_id = "\0";static char *icn_id2 = "\0";MODULE_DESCRIPTION("ISDN4Linux: Driver for ICN active ISDN card");MODULE_AUTHOR("Fritz Elfert");MODULE_LICENSE("GPL");module_param(portbase, int, 0);MODULE_PARM_DESC(portbase, "Port address of first card");module_param(membase, ulong, 0);MODULE_PARM_DESC(membase, "Shared memory address of all cards");module_param(icn_id, charp, 0);MODULE_PARM_DESC(icn_id, "ID-String of first card");module_param(icn_id2, charp, 0);MODULE_PARM_DESC(icn_id2, "ID-String of first card, second S0 (4B only)");/* * Verbose bootcode- and protocol-downloading. */#undef BOOT_DEBUG/* * Verbose Shmem-Mapping. */#undef MAP_DEBUGstatic char*revision = "$Revision: 1.65.6.8 $";static int icn_addcard(int, char *, char *);/* * Free send-queue completely. * Parameter: * card = pointer to card struct * channel = channel number */static voidicn_free_queue(icn_card * card, int channel){ struct sk_buff_head *queue = &card->spqueue[channel]; struct sk_buff *skb; skb_queue_purge(queue); card->xlen[channel] = 0; card->sndcount[channel] = 0; if ((skb = card->xskb[channel])) { card->xskb[channel] = NULL; dev_kfree_skb(skb); }}/* Put a value into a shift-register, highest bit first. * Parameters: * port = port for output (bit 0 is significant) * val = value to be output * firstbit = Bit-Number of highest bit * bitcount = Number of bits to output */static inline voidicn_shiftout(unsigned short port, unsigned long val, int firstbit, int bitcount){ register u_char s; register u_char c; for (s = firstbit, c = bitcount; c > 0; s--, c--) OUTB_P((u_char) ((val >> s) & 1) ? 0xff : 0, port);}/* * disable a cards shared memory */static inline voidicn_disable_ram(icn_card * card){ OUTB_P(0, ICN_MAPRAM);}/* * enable a cards shared memory */static inline voidicn_enable_ram(icn_card * card){ OUTB_P(0xff, ICN_MAPRAM);}/* * Map a cards channel0 (Bank0/Bank8) or channel1 (Bank4/Bank12) * * must called with holding the devlock */static inline voidicn_map_channel(icn_card * card, int channel){#ifdef MAP_DEBUG printk(KERN_DEBUG "icn_map_channel %d %d\n", dev.channel, channel);#endif if ((channel == dev.channel) && (card == dev.mcard)) return; if (dev.mcard) icn_disable_ram(dev.mcard); icn_shiftout(ICN_BANK, chan2bank[channel], 3, 4); /* Select Bank */ icn_enable_ram(card); dev.mcard = card; dev.channel = channel;#ifdef MAP_DEBUG printk(KERN_DEBUG "icn_map_channel done\n");#endif}/* * Lock a cards channel. * Return 0 if requested card/channel is unmapped (failure). * Return 1 on success. * * must called with holding the devlock */static inline inticn_lock_channel(icn_card * card, int channel){ register int retval;#ifdef MAP_DEBUG printk(KERN_DEBUG "icn_lock_channel %d\n", channel);#endif if ((dev.channel == channel) && (card == dev.mcard)) { dev.chanlock++; retval = 1;#ifdef MAP_DEBUG printk(KERN_DEBUG "icn_lock_channel %d OK\n", channel);#endif } else { retval = 0;#ifdef MAP_DEBUG printk(KERN_DEBUG "icn_lock_channel %d FAILED, dc=%d\n", channel, dev.channel);#endif } return retval;}/* * Release current card/channel lock * * must called with holding the devlock */static inline void__icn_release_channel(void){#ifdef MAP_DEBUG printk(KERN_DEBUG "icn_release_channel l=%d\n", dev.chanlock);#endif if (dev.chanlock > 0) dev.chanlock--;}/* * Release current card/channel lock */static inline voidicn_release_channel(void){ ulong flags; spin_lock_irqsave(&dev.devlock, flags); __icn_release_channel(); spin_unlock_irqrestore(&dev.devlock, flags);}/* * Try to map and lock a cards channel. * Return 1 on success, 0 on failure. */static inline inticn_trymaplock_channel(icn_card * card, int channel){ ulong flags;#ifdef MAP_DEBUG printk(KERN_DEBUG "trymaplock c=%d dc=%d l=%d\n", channel, dev.channel, dev.chanlock);#endif spin_lock_irqsave(&dev.devlock, flags); if ((!dev.chanlock) || ((dev.channel == channel) && (dev.mcard == card))) { dev.chanlock++; icn_map_channel(card, channel); spin_unlock_irqrestore(&dev.devlock, flags);#ifdef MAP_DEBUG printk(KERN_DEBUG "trymaplock %d OK\n", channel);#endif return 1; } spin_unlock_irqrestore(&dev.devlock, flags);#ifdef MAP_DEBUG printk(KERN_DEBUG "trymaplock %d FAILED\n", channel);#endif return 0;}/* * Release current card/channel lock, * then map same or other channel without locking. */static inline voidicn_maprelease_channel(icn_card * card, int channel){ ulong flags;#ifdef MAP_DEBUG printk(KERN_DEBUG "map_release c=%d l=%d\n", channel, dev.chanlock);#endif spin_lock_irqsave(&dev.devlock, flags); if (dev.chanlock > 0) dev.chanlock--; if (!dev.chanlock) icn_map_channel(card, channel); spin_unlock_irqrestore(&dev.devlock, flags);}/* Get Data from the B-Channel, assemble fragmented packets and put them * into receive-queue. Wake up any B-Channel-reading processes. * This routine is called via timer-callback from icn_pollbchan(). */static voidicn_pollbchan_receive(int channel, icn_card * card){ int mch = channel + ((card->secondhalf) ? 2 : 0); int eflag; int cnt; struct sk_buff *skb; if (icn_trymaplock_channel(card, mch)) { while (rbavl) { cnt = readb(&rbuf_l); if ((card->rcvidx[channel] + cnt) > 4000) { printk(KERN_WARNING "icn: (%s) bogus packet on ch%d, dropping.\n", CID, channel + 1); card->rcvidx[channel] = 0; eflag = 0; } else { memcpy_fromio(&card->rcvbuf[channel][card->rcvidx[channel]], &rbuf_d, cnt); card->rcvidx[channel] += cnt; eflag = readb(&rbuf_f); } rbnext; icn_maprelease_channel(card, mch & 2); if (!eflag) { if ((cnt = card->rcvidx[channel])) { if (!(skb = dev_alloc_skb(cnt))) { printk(KERN_WARNING "icn: receive out of memory\n"); break; } memcpy(skb_put(skb, cnt), card->rcvbuf[channel], cnt); card->rcvidx[channel] = 0; card->interface.rcvcallb_skb(card->myid, channel, skb); } } if (!icn_trymaplock_channel(card, mch)) break; } icn_maprelease_channel(card, mch & 2); }}/* Send data-packet to B-Channel, split it up into fragments of * ICN_FRAGSIZE length. If last fragment is sent out, signal * success to upper layers via statcallb with ISDN_STAT_BSENT argument. * This routine is called via timer-callback from icn_pollbchan() or * directly from icn_sendbuf(). */static voidicn_pollbchan_send(int channel, icn_card * card){ int mch = channel + ((card->secondhalf) ? 2 : 0); int cnt; unsigned long flags; struct sk_buff *skb; isdn_ctrl cmd; if (!(card->sndcount[channel] || card->xskb[channel] || !skb_queue_empty(&card->spqueue[channel]))) return; if (icn_trymaplock_channel(card, mch)) { while (sbfree && (card->sndcount[channel] || !skb_queue_empty(&card->spqueue[channel]) || card->xskb[channel])) { spin_lock_irqsave(&card->lock, flags); if (card->xmit_lock[channel]) { spin_unlock_irqrestore(&card->lock, flags); break; } card->xmit_lock[channel]++; spin_unlock_irqrestore(&card->lock, flags); skb = card->xskb[channel]; if (!skb) { skb = skb_dequeue(&card->spqueue[channel]); if (skb) { /* Pop ACK-flag off skb. * Store length to xlen. */ if (*(skb_pull(skb,1))) card->xlen[channel] = skb->len; else card->xlen[channel] = 0; } } if (!skb) break; if (skb->len > ICN_FRAGSIZE) { writeb(0xff, &sbuf_f); cnt = ICN_FRAGSIZE; } else { writeb(0x0, &sbuf_f); cnt = skb->len; } writeb(cnt, &sbuf_l); memcpy_toio(&sbuf_d, skb->data, cnt); skb_pull(skb, cnt); sbnext; /* switch to next buffer */ icn_maprelease_channel(card, mch & 2); spin_lock_irqsave(&card->lock, flags); card->sndcount[channel] -= cnt; if (!skb->len) { if (card->xskb[channel]) card->xskb[channel] = NULL; card->xmit_lock[channel] = 0; spin_unlock_irqrestore(&card->lock, flags); dev_kfree_skb(skb); if (card->xlen[channel]) { cmd.command = ISDN_STAT_BSENT; cmd.driver = card->myid; cmd.arg = channel; cmd.parm.length = card->xlen[channel]; card->interface.statcallb(&cmd); } } else { card->xskb[channel] = skb; card->xmit_lock[channel] = 0; spin_unlock_irqrestore(&card->lock, flags); } if (!icn_trymaplock_channel(card, mch)) break; } icn_maprelease_channel(card, mch & 2); }}/* Send/Receive Data to/from the B-Channel. * This routine is called via timer-callback. * It schedules itself while any B-Channel is open. */static voidicn_pollbchan(unsigned long data){ icn_card *card = (icn_card *) data; unsigned long flags; if (card->flags & ICN_FLAGS_B1ACTIVE) { icn_pollbchan_receive(0, card); icn_pollbchan_send(0, card); } if (card->flags & ICN_FLAGS_B2ACTIVE) { icn_pollbchan_receive(1, card); icn_pollbchan_send(1, card); } if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) { /* schedule b-channel polling again */ spin_lock_irqsave(&card->lock, flags); mod_timer(&card->rb_timer, jiffies+ICN_TIMER_BCREAD); card->flags |= ICN_FLAGS_RBTIMER; spin_unlock_irqrestore(&card->lock, flags); } else card->flags &= ~ICN_FLAGS_RBTIMER;}typedef struct icn_stat { char *statstr; int command; int action;} icn_stat;/* *INDENT-OFF* */static icn_stat icn_stat_table[] ={ {"BCON_", ISDN_STAT_BCONN, 1}, /* B-Channel connected */ {"BDIS_", ISDN_STAT_BHUP, 2}, /* B-Channel disconnected */ /* ** add d-channel connect and disconnect support to link-level */ {"DCON_", ISDN_STAT_DCONN, 10}, /* D-Channel connected */ {"DDIS_", ISDN_STAT_DHUP, 11}, /* D-Channel disconnected */ {"DCAL_I", ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line */ {"DSCA_I", ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV */ {"FCALL", ISDN_STAT_ICALL, 4}, /* Leased line connection up */ {"CIF", ISDN_STAT_CINF, 5}, /* Charge-info, 1TR6-type */ {"AOC", ISDN_STAT_CINF, 6}, /* Charge-info, DSS1-type */ {"CAU", ISDN_STAT_CAUSE, 7}, /* Cause code */ {"TEI OK", ISDN_STAT_RUN, 0}, /* Card connected to wallplug */ {"E_L1: ACT FAIL", ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */ {"E_L2: DATA LIN", ISDN_STAT_BHUP, 8}, /* Layer-2 data link lost */ {"E_L1: ACTIVATION FAILED", ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */ {NULL, 0, -1}};/* *INDENT-ON* *//* * Check Statusqueue-Pointer from isdn-cards. * If there are new status-replies from the interface, check * them against B-Channel-connects/disconnects and set flags accordingly. * Wake-Up any processes, who are reading the status-device. * If there are B-Channels open, initiate a timer-callback to * icn_pollbchan(). * This routine is called periodically via timer. */static voidicn_parse_status(u_char * status, int channel, icn_card * card){ icn_stat *s = icn_stat_table; int action = -1; unsigned long flags; isdn_ctrl cmd; while (s->statstr) { if (!strncmp(status, s->statstr, strlen(s->statstr))) { cmd.command = s->command; action = s->action; break; } s++; } if (action == -1) return; cmd.driver = card->myid; cmd.arg = channel; switch (action) { case 11: spin_lock_irqsave(&card->lock, flags); icn_free_queue(card,channel); card->rcvidx[channel] = 0; if (card->flags & ((channel)?ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE)) { isdn_ctrl ncmd; card->flags &= ~((channel)? ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE); memset(&ncmd, 0, sizeof(ncmd)); ncmd.driver = card->myid; ncmd.arg = channel; ncmd.command = ISDN_STAT_BHUP; spin_unlock_irqrestore(&card->lock, flags); card->interface.statcallb(&cmd); } else spin_unlock_irqrestore(&card->lock, flags); break; case 1: spin_lock_irqsave(&card->lock, flags); icn_free_queue(card,channel); card->flags |= (channel) ? ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE; spin_unlock_irqrestore(&card->lock, flags); break; case 2: spin_lock_irqsave(&card->lock, flags); card->flags &= ~((channel) ? ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE); icn_free_queue(card, channel); card->rcvidx[channel] = 0; spin_unlock_irqrestore(&card->lock, flags); break; case 3: { char *t = status + 6; char *s = strchr(t, ','); *s++ = '\0'; strlcpy(cmd.parm.setup.phone, t, sizeof(cmd.parm.setup.phone)); s = strchr(t = s, ','); *s++ = '\0'; if (!strlen(t)) cmd.parm.setup.si1 = 0; else cmd.parm.setup.si1 = simple_strtoul(t, NULL, 10); s = strchr(t = s, ','); *s++ = '\0'; if (!strlen(t)) cmd.parm.setup.si2 = 0; else cmd.parm.setup.si2 = simple_strtoul(t, NULL, 10); strlcpy(cmd.parm.setup.eazmsn, s, sizeof(cmd.parm.setup.eazmsn)); } cmd.parm.setup.plan = 0; cmd.parm.setup.screen = 0; break; case 4: sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid); sprintf(cmd.parm.setup.eazmsn, "%d", channel + 1); cmd.parm.setup.si1 = 7; cmd.parm.setup.si2 = 0; cmd.parm.setup.plan = 0; cmd.parm.setup.screen = 0; break; case 5: strlcpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num)); break; case 6: snprintf(cmd.parm.num, sizeof(cmd.parm.num), "%d", (int) simple_strtoul(status + 7, NULL, 16)); break; case 7: status += 3; if (strlen(status) == 4) snprintf(cmd.parm.num, sizeof(cmd.parm.num), "%s%c%c", status + 2, *status, *(status + 1)); else strlcpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num)); break; case 8: spin_lock_irqsave(&card->lock, flags); card->flags &= ~ICN_FLAGS_B1ACTIVE; icn_free_queue(card, 0); card->rcvidx[0] = 0; spin_unlock_irqrestore(&card->lock, flags); cmd.arg = 0; cmd.driver = card->myid; card->interface.statcallb(&cmd); cmd.command = ISDN_STAT_DHUP;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -