|
Форум cronyx.ru (архив)
czaptel.c | kmax  :: 2006-02-21 18:41 |
/* * Zaptel protocol layer for Cronyx serial (E1) adapters. * * Copyright (C) 2005 Cronyx Engineering. * Author: Roman Kurakin, <rik@cronyx.ru> * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Authors grant any other persons or organisations permission to use * or modify this software as long as this message is kept with the software, * all derivative works or modified versions. * * $Id: czaptel.c,v 1.3.2.1 2005/11/07 08:31:51 rik Exp $ */ #include "module.h" #include <linux/if_ether.h> #include <linux/if_arp.h> #include <linux/timer.h> #include <linux/pkt_sched.h> #include "cserial.h" #include "zaptel.h"
/* Module information */ MODULE_AUTHOR("Roma Kurakin <rik@cronyx.ru>, Cronyx Engineering."); MODULE_DESCRIPTION("Zaptel protocol driver " CRONYX_VERSION_INFO "
"); #ifdef MODULE_LICENSE MODULE_LICENSE("Dual BSD/GPL"); #endif
typedef struct _zaptel_t { chan_t *chan; int running; int num; int usecount; int dead; int sync; volatile unsigned char writechunk[ZT_MAX_CHUNKSIZE * 32 * 2]; /* Double-word aligned write memory */ volatile unsigned char readchunk[ZT_MAX_CHUNKSIZE * 32 * 2]; /* Double-word aligned read memory */ unsigned char ec_chunk1[31][ZT_CHUNKSIZE]; unsigned char ec_chunk2[31][ZT_CHUNKSIZE]; struct zt_span span; /* Span */ struct zt_chan chans[31]; /* Channels */ struct sk_buff *txskb; int flag; int txbusy; int cas; #define CAS_LOF 1 #define CAS_SYNC 2 #define CAS_ALL_ZERO 4 int cas_state; int cas_lofcount; int cas_rbsbits[16]; int cas_rbsbits_copy[16]; int cas_rbsbits_tx[16]; int cas_tx_index; int cas_shift; int cas_half; } zaptel_t;
static void zaptel_transmit_done (chan_t *h);
static void zaptel_receive (chan_t *h, struct sk_buff *skb) { zaptel_t *p = h->sw; int x; int y; int i; unsigned char rxs; if (skb->len % 32 != 0) { printk (KERN_ERR "Data should be 32*N length
"); SET_SKB_FREE (skb); KFREE_SKB (skb, FREE_READ); return; } /* XXX */ if (skb->len % (32 * ZT_CHUNKSIZE)) { LOG2 (p->chan, "Data should be 32*ZT_CHUNKSIZE*N length
"); } /* XXX add check for FAS */
for (i = 1; i <= skb->len; i += 32 * ZT_CHUNKSIZE) { p->cas_half = p->cas_half ? 0 : 8; for (y=0;y<ZT_CHUNKSIZE;y++) { for (x=0;x<p->span.channels;x++) { p->chans[x].readchunk[y] = skb->data[i + 32 * y + x]; /* Note i starts from 1 */ } } if (p->cas) { for (y=0;y<ZT_CHUNKSIZE;y++) { /* hacky ZT_CHNUKSIZE==8 */ p->cas_rbsbits[y+p->cas_half] = skb->data[i + 32 * y + 15]; } /* Check if we are in sync */ if (p->cas_state & CAS_SYNC) { if (p->cas_rbsbits[p->cas_shift]&0xf0) { if (p->cas_lofcount < 2) { LOG (p->chan, "LOF (%d)
", p->cas_lofcount); p->cas_lofcount++; } else { LOG (p->chan, "LOF, out of frame sync
"); p->cas_state ^= (CAS_SYNC | CAS_LOF); } } } /* Try to find cas */ if ((p->cas_state & CAS_LOF) && p->cas_half) { for (y=0;y<16;y++) { if ((p->cas_rbsbits[p->cas_shift]&0xf0) == 0) { p->cas_state ^= (CAS_SYNC | CAS_LOF); LOG (p->chan, "Got multifram sync
"); break; } p->cas_shift = (p->cas_shift + 1) & 0x0f; } } if (p->cas_half) { LOG2 (h, "shift: %d %x
", p->cas_shift, p->cas_state); LOG2 (h, "1: %02x%02x%02x%02x
", p->cas_rbsbits[0], p->cas_rbsbits[1], p->cas_rbsbits[2], p->cas_rbsbits[3]); LOG2 (h, "2: %02x%02x%02x%02x
", p->cas_rbsbits[4+0], p->cas_rbsbits[4+1], p->cas_rbsbits[4+2], p->cas_rbsbits[4+3]); LOG2 (h, "3: %02x%02x%02x%02x
", p->cas_rbsbits[8+0], p->cas_rbsbits[8+1], p->cas_rbsbits[8+2], p->cas_rbsbits[8+3]); LOG2 (h, "4: %02x%02x%02x%02x
", p->cas_rbsbits[12+0], p->cas_rbsbits[12+1], p->cas_rbsbits[12+2], p->cas_rbsbits[12+3]); } /* XXX do some logic here in cas of LOF */ if ((p->cas_state & CAS_SYNC) && p->cas_half) { for (y=1; y<16; y++) { rxs = p->cas_rbsbits[(p->cas_shift + y) & 0x0f]; if (!(p->chans[y+15].sig & ZT_SIG_CLEAR)) { if (p->chans[i+15].rxsig != (rxs&0x0f)) { /* printk (KERN_ERR "Change detected %d %d %x
", y, 1, rxs);*/ zt_rbsbits(&p->chans[y+15], (rxs&0x0f)); } } rxs >>= 4; if (!(p->chans[y-1].sig & ZT_SIG_CLEAR)) { if (p->chans[y-1].rxsig != rxs) { /* printk (KERN_ERR "Change detected %d %d %x
", y, 0, rxs);*/ zt_rbsbits(&p->chans[y-1], rxs); } } #if 1 if (p->cas_rbsbits[(p->cas_shift + y) & 0x0f]!=p->cas_rbsbits_copy[(p->cas_shift + y) & 0x0f]) { LOG2 (h, "Changed %d %x->%x
", (p->cas_shift + y) & 0x0f, p->cas_rbsbits_copy[(p->cas_shift + y) & 0x0f], p->cas_rbsbits[(p->cas_shift + y) & 0x0f]); p->cas_rbsbits_copy[(p->cas_shift + y) & 0x0f] = p->cas_rbsbits[(p->cas_shift + y) & 0x0f]; } #endif } } } for (x=0;x<p->span.channels;x++) { zt_ec_chunk(&p->chans[x], p->chans[x].readchunk, p->ec_chunk2[x]); memcpy(p->ec_chunk2[x],p->ec_chunk1[x],ZT_CHUNKSIZE); memcpy(p->ec_chunk1[x],p->chans[x].writechunk,ZT_CHUNKSIZE); } zt_receive(&p->span); } SET_SKB_FREE (skb); KFREE_SKB (skb, FREE_READ); zaptel_transmit_done (h); }
static void zaptel_receive_error (chan_t *h, int errcode) { /* zaptel_t *p = h->sw;*/
LOG2 (h, "receive error %x
", errcode);
/* * XXXRIK: we are not ready yet to treat errors, so just log * it and ignore. That is Ok for now. */ return; }
static void zaptel_transmit_done (chan_t *h) { /* static unsigned char buf[32*ZT_CHUNKSIZE];*/ zaptel_t *p = h->sw; struct sk_buff *skb; int y; int x; int k; int res; int multiplier;
if (test_and_set_bit (0, (void *)&p->txbusy) != 0) return; if (p->flag && p->txskb == 0) { printk (KERN_ERR "Bad p->flag state detected
"); p->flag = 0; } if (p->flag == 0) { unsigned char *buf; multiplier = h->mtu / (32 * ZT_CHUNKSIZE);
skb = dev_alloc_skb (32*ZT_CHUNKSIZE * multiplier);
if (!skb) { p->txbusy = 0; LOG2 (h, "ENOMEMM
"); return; }
p->txskb = skb; skb_put (skb, 32*ZT_CHUNKSIZE * multiplier); p->flag = 1;
for (buf = skb->data; buf < skb->data + 32*ZT_CHUNKSIZE*multiplier; buf += 32*ZT_CHUNKSIZE) { zt_transmit(&p->span); for (y=0;y<ZT_CHUNKSIZE;y++) { if (p->cas && p->cas_rbsbits_tx[0] != 0x0b) { printk (KERN_ERR "Ups, wrong CAS transmit table
"); } for (x=0;x<p->span.channels;x++) { buf [y*32+x+1] = p->chans[x].writechunk[y]; } if (p->cas) { buf [y*32+16] = p->cas_rbsbits_tx[p->cas_tx_index]; p->cas_tx_index = (p->cas_tx_index + 1) & 0x0f; } } } } k = p->txskb->len; res = h->transmit (h, p->txskb); if (res > 0) { p->flag = 0; /* XXXRIK do the same on unload/detach */ KFREE_SKB (p->txskb, FREE_WRITE); p->txskb = 0; } LOG2 (h, "tr res:%d %d
", res, k); p->txbusy = 0; }
static int zap_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) { switch(cmd) { default: return -ENOTTY; } }
static int zap_close(struct zt_chan *chan) { zaptel_t *p = chan->pvt; p->usecount--; #ifndef LINUX26 MOD_DEC_USE_COUNT; #endif if (!p->usecount && p->chan && p->chan->down) p->chan->down(p->chan);
/* If we're dead, release us now */ if (!p->usecount && p->dead) { /* zt_unregister(&p->span);*/ /* kfree(wc);*/ /* printk("Freed a Wildcard
");*/ } return 0; }
static int zap_open(struct zt_chan *chan) { zaptel_t *p = chan->pvt; if (p->dead) return -ENODEV; if (!p->usecount && p->chan && p->chan->up) p->chan->up(p->chan); p->usecount++; LOG2 (p->chan, "ZAP_OPEN: %d
", p->usecount); if (p->usecount == 1) { zaptel_transmit_done (p->chan); zaptel_transmit_done (p->chan); zaptel_transmit_done (p->chan); } #ifndef LINUX26 MOD_INC_USE_COUNT; #endif return 0; }
static int zap_rbsbits(struct zt_chan *chan, int bits) { zaptel_t *p = chan->pvt; /* int b,o;*/ unsigned char mask;
LOG (p->chan, " rbsbits trunk:%d set bits: %x(was %x)
", chan->chanpos, bits, p->cas_rbsbits_tx[chan->chanpos%16]); /* Byte offset */ if (p->cas_rbsbits_tx[0] != 0x0b) { printk (KERN_ERR "Ups, wrong CAS transmit table
"); } if (chan->chanpos < 16) { mask = ((bits << 4) | p->chans[chan->chanpos - 1 + 16].txsig); p->cas_rbsbits_tx[chan->chanpos] = mask; } else if (chan->chanpos > 16) { mask = (bits | (p->chans[chan->chanpos - 1 - 16].txsig << 4)); p->cas_rbsbits_tx[chan->chanpos - 16] = mask; } p->chans[chan->chanpos - 1].txsig = bits; if (p->cas_rbsbits_tx[0] != 0x0b) { printk (KERN_ERR "Ups, wrong CAS transmit table
"); } return 0; }
static int zap_startup(struct zt_span *span) { zaptel_t *p = span->pvt; chan_t *h = p->chan; int i, alreadyrunning = span->flags & ZT_FLAG_RUNNING;
/* initialize the start value for the entire chunk of last ec buffer */ for(i = 0; i < span->channels; i++) { memset(p->ec_chunk1[i], ZT_LIN2X(0,&span->chans[i]),ZT_CHUNKSIZE); memset(p->ec_chunk2[i], ZT_LIN2X(0,&span->chans[i]),ZT_CHUNKSIZE); }
/* Reset framer with proper parameters and start */ /* Build up config */ if (p->span.lineconfig & ZT_CONFIG_CCS) { LOG (h, "Set CCS mode
"); p->cas = 0; } else { LOG (h, "Set CAS mode
"); p->cas = 1; p->cas_state = CAS_LOF; } if (p->span.lineconfig & ZT_CONFIG_HDB3) { LOG (h, "Set HDB3 mode: ignore we are always HDB3
"); } else { LOG (h, "Set AMI mode: ignore we are always HDB3
"); } if (p->span.lineconfig & ZT_CONFIG_CRC4) { LOG (h, "Set CRC4 mode: ignore, use sconfig instead
"); } if (!alreadyrunning) { p->span.flags |= ZT_FLAG_RUNNING; } LOG2(h, "Startup flags is %d
", span->flags);
if (!alreadyrunning) { /* Only if we're not already going */ span->flags |= ZT_FLAG_RUNNING; } return 0; }
static int zap_shutdown(struct zt_span *span) { /* zaptel_t *p = span->pvt;*/ /* unsigned long flags;*/
span->flags &= ~ZT_FLAG_RUNNING;
return 0; }
static int zap_maint(struct zt_span *span, int cmd) { zaptel_t *p = span->pvt; int res = 0;
switch(cmd) { case ZT_MAINT_NONE: /* loop=off, rloop=off */ LOG2 (p->chan, "No loops, not supported
"); break; case ZT_MAINT_LOCALLOOP: /* loop=on, rloop=? */ LOG2 (p->chan, "Local loop, not supported
"); break; case ZT_MAINT_REMOTELOOP: /* loop=?, rloop=on */ LOG2 (p->chan, "Local rloop, not supported
"); break; case ZT_MAINT_LOOPUP: case ZT_MAINT_LOOPDOWN: case ZT_MAINT_LOOPSTOP: res = -ENOSYS; break; default: LOG2(p->chan, "Unknown maint command: %d
", cmd); res = -EINVAL; break; } return res; }
static int zap_chanconfig(struct zt_chan *chan, int sigtype) { return 0; }
static int zap_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) { zaptel_t *p = span->pvt; span->lineconfig = lc->lineconfig; span->txlevel = lc->lbo; span->rxlevel = 0; /* Do we want to SYNC on receive or not */ p->sync = lc->sync; /* If already running, apply changes immediately */ if (span->flags & ZT_FLAG_RUNNING) return zap_startup (span); return 0; }
static int zaptel_attach (chan_t *h) { zaptel_t *p; int x;
p = kmalloc (sizeof (zaptel_t), GFP_KERNEL); if (! p) return -ENOMEM; memset (p, 0, sizeof (zaptel_t)); h->sw = p; p->chan = h; p->cas_rbsbits_tx[0] = 0x0b; memset (p->cas_rbsbits_tx+1, 0xff, 15); p->span.spanconfig = zap_spanconfig; p->span.chanconfig = zap_chanconfig; p->span.startup = zap_startup; p->span.shutdown = zap_shutdown; p->span.rbsbits = zap_rbsbits; p->span.maint = zap_maint; p->span.open = zap_open; p->span.close = zap_close; p->span.ioctl = zap_ioctl; p->span.channels = 31; p->span.chans = p->chans; p->span.flags = 0; /*ZT_FLAG_RBS;*/ p->span.linecompat = ZT_CONFIG_HDB3 | ZT_CONFIG_CCS; p->span.pvt = p; p->span.deflaw = ZT_LAW_ALAW; for (x=0;x<p->span.channels;x++) { sprintf(p->chans[x].name, "CP0/%d/%d", 0, x + 1); p->chans[x].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_EM_E1 | ZT_SIG_FXSLS | ZT_SIG_FXSGS | ZT_SIG_FXSKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS | ZT_SIG_SF; p->chans[x].pvt = p; p->chans[x].chanpos = x + 1; } if (zt_register(&p->span, 0)) { printk("Unable to register span with zaptel
"); return 0; /* XXX do smth bad */ } #if LINUX_VERSION_CODE < 0x020600 MOD_INC_USE_COUNT; #else try_module_get (THIS_MODULE); #endif return 0; }
static int zaptel_detach (chan_t *h) { zaptel_t *p = h->sw; if (p) { /* Release span, possibly delayed */ if (!p->usecount) zt_unregister(&p->span); else p->dead = 1; }
kfree (p); h->sw = 0; #if LINUX_VERSION_CODE < 0x020600 MOD_DEC_USE_COUNT; #else module_put (THIS_MODULE); #endif return 0; }
static int zaptel_control (chan_t *h, unsigned int cmd, unsigned long arg) { /* zaptel_t *p = h->sw;*/
return 0; }
/* * Protocol callbacks */ static proto_t zaptel_tab = { "zaptel", 0,
/* Interface to channel */ zaptel_receive, zaptel_receive_error, zaptel_transmit_done, 0, /* modem event */
/* I/O interface */ 0, /* open */ 0, /* close */ 0, /* read */ 0, /* write */ 0, /* select */ 0, /* fasync */
/* Control interface */ zaptel_attach, zaptel_detach, zaptel_control, /* control */ };
/*////////////////////////////////////////////////////////////////////// * Loadable module control */ int init_module (void) { #if LINUX_VERSION_CODE < 0x020600 EXPORT_NO_SYMBOLS; #endif binder_register_protocol (&zaptel_tab); printk (KERN_DEBUG "Zaptel protocol loaded
"); return 0; }
void cleanup_module (void) { binder_unregister_protocol (&zaptel_tab); printk (KERN_DEBUG "Zaptel protocol unloaded
"); }
|