/* * Dynamic Span Interface for Zaptel (Multi-Span Ethernet Interface) * * Written by Joseph Benden * * Copyright (C) 2007, Thralling Penguin LLC * * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * */ #include #include #include #include #include #include #include #include #include #ifdef CONFIG_DEVFS_FS #include #endif #ifdef STANDALONE_ZAPATA #include "zaptel.h" #else #include #endif #define ETH_P_ZTDETH 0xd00d struct ztdeth_header { unsigned short subaddr; }; /* Timer for enabling spans - used to combat a Zaptel lock problem */ static struct timer_list timer; /* Variable used by the timer to correctly deduce the amount of time passed */ static int timer_counter; /* We take the raw message, put it in an ethernet frame, and add a two byte addressing header at the top for future use */ #ifdef DEFINE_SPINLOCK static DEFINE_SPINLOCK(zlock); #else static spinlock_t zlock = SPIN_LOCK_UNLOCKED; #endif static struct sk_buff_head skbs; static struct ztdeth { unsigned char addr[ETH_ALEN]; unsigned short subaddr; /* Network byte order */ struct zt_span *span; char ethdev[IFNAMSIZ]; struct net_device *dev; struct ztdeth *next; unsigned char *msgbuf; int msgbuf_len; int ready; int delay; } *zdevs = NULL; struct zt_span *ztdethmf_getspan(unsigned char *addr, unsigned short subaddr) { unsigned long flags; struct ztdeth *z; struct zt_span *span = NULL; spin_lock_irqsave(&zlock, flags); z = zdevs; while(z) { if (!z->delay) { if (!memcmp(addr, z->addr, ETH_ALEN) && z->subaddr == subaddr) break; } z = z->next; } if (z) span = z->span; spin_unlock_irqrestore(&zlock, flags); return span; } #if defined(LINUX26) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) static int ztdethmf_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) #else static int ztdethmf_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) #endif { int num_spans = 0, span_index = 0, len, t; int samples, channels; unsigned char *data; struct zt_span *span; struct ztdeth_header *zh; zh = (struct ztdeth_header *)skb->nh.raw; if (ntohs(zh->subaddr) & 0x8000) { /* got a multi-span frame */ num_spans = ntohs(zh->subaddr) & 0xFF; if (num_spans < 0 || num_spans > 7) { kfree_skb(skb); return 0; } skb_pull(skb, sizeof(struct ztdeth_header)); data = (unsigned char *) skb->data; do { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9) span = ztdethmf_getspan(eth_hdr(skb)->h_source, htons(span_index)); #else span = ztdethmf_getspan(skb->mac.ethernet->h_source, htons(span_index)); #endif if (span) { // skb_pull(skb, sizeof(struct ztdeth_header)); samples = data[0] & 0xFF; channels = data[5] & 0xFF; if (samples == 8 && channels == 24) { len = 210; /* optimize for T1 */ } else if (samples == 8 && channels == 31) { len = 270; /* optimize for E1 */ } else { /* unknown, so calculate on the frame data */ len = samples * channels; len += 6; /* length of ztdynamic frame */ t = channels / 4; if (channels % 4 != 0) { ++t; } len += t * 2; /* length of signalling bits */ } // printk("span=%d samples=%d channels=%d len=%d seq=%x:%x data=%p\n", span_index, samples, channels, len, data[2], data[3], data); if (len > skb->len) { break; } zt_dynamic_receive(span, data, len); data += len; if (data > (skb->data + skb->len)) { break; /* too long */ } } else break; /* if we dont match, then exit */ ++span_index; } while (span_index < num_spans); } kfree_skb(skb); return 0; } static int ztdethmf_notifier(struct notifier_block *block, unsigned long event, void *ptr) { struct net_device *dev = ptr; struct ztdeth *z; unsigned long flags; switch(event) { case NETDEV_GOING_DOWN: case NETDEV_DOWN: spin_lock_irqsave(&zlock, flags); z = zdevs; while(z) { /* Note that the device no longer exists */ if (z->dev == dev) z->dev = NULL; z = z->next; } spin_unlock_irqrestore(&zlock, flags); break; case NETDEV_UP: spin_lock_irqsave(&zlock, flags); z = zdevs; while(z) { /* Now that the device exists again, use it */ if (!strcmp(z->ethdev, dev->name)) z->dev = dev; z = z->next; } spin_unlock_irqrestore(&zlock, flags); break; } return 0; } static int ztdethmf_transmit(void *pvt, unsigned char *msg, int msglen) { struct ztdeth *z, *t, *ready_spans[8]; struct sk_buff *skb; struct ztdeth_header *zh; unsigned long flags; struct net_device *dev; unsigned char addr[ETH_ALEN]; int span_count = 0, spans_ready = 0, index = 0, ttl_size = 0; spin_lock_irqsave(&zlock, flags); z = pvt; if (z->dev) { /* Store the present data in the buffers */ //z->msgbuf = msg; memcpy(z->msgbuf, msg, msglen); z->msgbuf_len = msglen; z->ready = 1; /* Are all available spans ready for transmit? */ t = zdevs; while(t) { if (!t->delay) { if (!memcmp(t->addr, z->addr, ETH_ALEN)) { ++span_count; if (t->ready) { ++spans_ready; ttl_size += t->msgbuf_len; ready_spans[ntohs(t->subaddr)] = t; } } } t = t->next; } /* Copy fields to local variables to remove spinlock ASAP */ dev = z->dev; memcpy(addr, z->addr, sizeof(z->addr)); spin_unlock_irqrestore(&zlock, flags); // if (jiffies % HZ == 0) printk("TDMoEmf: spans=%d ready=%d\n", span_count, spans_ready); if (span_count && spans_ready && span_count == spans_ready) { skb = dev_alloc_skb(ttl_size + dev->hard_header_len + sizeof(struct ztdeth_header) + 32); if (skb) { /* Reserve header space */ skb_reserve(skb, dev->hard_header_len + sizeof(struct ztdeth_header)); do { // printk("TDMoEmf: copied span=%d\n", index); memcpy(skb_put(skb, ready_spans[index]->msgbuf_len), ready_spans[index]->msgbuf, ready_spans[index]->msgbuf_len); ready_spans[index]->ready = 0; ++index; } while (--spans_ready > 0); /* Throw on header */ zh = (struct ztdeth_header *)skb_push(skb, sizeof(struct ztdeth_header)); zh->subaddr = htons((0x8000 | (span_count & 0xFF))); /* Setup protocol and such */ skb->protocol = __constant_htons(ETH_P_ZTDETH); skb->nh.raw = skb->data; skb->dev = dev; if (dev->hard_header) dev->hard_header(skb, dev, ETH_P_ZTDETH, addr, dev->dev_addr, skb->len); skb_queue_tail(&skbs, skb); } } } else spin_unlock_irqrestore(&zlock, flags); return 0; } static int ztdethmf_flush(void) { struct sk_buff *skb; /* Handle all transmissions now */ while ((skb = skb_dequeue(&skbs))) { dev_queue_xmit(skb); } return 0; } static struct packet_type ztdethmf_ptype = { type: __constant_htons(ETH_P_ZTDETH), /* Protocol */ dev: NULL, /* Device (NULL = wildcard) */ func: ztdethmf_rcv, /* Receiver */ }; static int digit2int(char d) { switch(d) { case 'F': case 'E': case 'D': case 'C': case 'B': case 'A': return d - 'A' + 10; case 'f': case 'e': case 'd': case 'c': case 'b': case 'a': return d - 'a' + 10; case '9': case '8': case '7': case '6': case '5': case '4': case '3': case '2': case '1': case '0': return d - '0'; } return -1; } static int hex2int(char *s) { int res; int tmp; /* Gotta be at least one digit */ if (strlen(s) < 1) return -1; /* Can't be more than two */ if (strlen(s) > 2) return -1; /* Grab the first digit */ res = digit2int(s[0]); if (res < 0) return -1; tmp = res; /* Grab the next */ if (strlen(s) > 1) { res = digit2int(s[1]); if (res < 0) return -1; tmp = tmp * 16 + res; } return tmp; } static void ztdethmf_destroy(void *pvt) { struct ztdeth *z = pvt; unsigned long flags; struct ztdeth *prev=NULL, *cur; spin_lock_irqsave(&zlock, flags); cur = zdevs; while(cur) { if (cur == z) { if (prev) prev->next = cur->next; else zdevs = cur->next; break; } prev = cur; cur = cur->next; } spin_unlock_irqrestore(&zlock, flags); if (cur == z) { /* Successfully removed */ printk("TDMoEmf: Removed interface for %s\n", z->span->name); kfree(z->msgbuf); kfree(z); #ifndef LINUX26 MOD_DEC_USE_COUNT; #else module_put(THIS_MODULE); #endif } else { if (z && z->span && z->span->name) { printk("TDMoEmf: Cannot find interface for %s\n", z->span->name); } } } static void *ztdethmf_create(struct zt_span *span, char *addr) { struct ztdeth *z; char src[256]; char tmp[256], *tmp2, *tmp3, *tmp4 = NULL; int res,x,bufsize; unsigned long flags; z = kmalloc(sizeof(struct ztdeth), GFP_KERNEL); if (z) { /* Zero it out */ memset(z, 0, sizeof(struct ztdeth)); /* set a delay for xmit/recv to workaround Zaptel problems */ z->delay = 8; /* create a msg buffer */ /* MAX OF 31 CHANNELS!!!! */ bufsize = 31 * ZT_CHUNKSIZE + 31 / 4 + 48; z->msgbuf = kmalloc(bufsize, GFP_KERNEL); /* Address should be /[/subaddr] */ strncpy(tmp, addr, sizeof(tmp) - 1); tmp2 = strchr(tmp, '/'); if (tmp2) { *tmp2 = '\0'; tmp2++; strncpy(z->ethdev, tmp, sizeof(z->ethdev) - 1); } else { printk("Invalid TDMoEmf address (no device) '%s'\n", addr); kfree(z); return NULL; } if (tmp2) { tmp4 = strchr(tmp2+1, '/'); if (tmp4) { *tmp4 = '\0'; tmp4++; } /* We don't have SSCANF :( Gotta do this the hard way */ tmp3 = strchr(tmp2, ':'); for (x=0;x<6;x++) { if (tmp2) { if (tmp3) { *tmp3 = '\0'; tmp3++; } res = hex2int(tmp2); if (res < 0) break; z->addr[x] = res & 0xff; } else break; if ((tmp2 = tmp3)) tmp3 = strchr(tmp2, ':'); } if (x != 6) { printk("TDMoEmf: Invalid MAC address in: %s\n", addr); kfree(z); return NULL; } } else { printk("TDMoEmf: Missing MAC address\n"); kfree(z); return NULL; } if (tmp4) { int sub = 0; int mul = 1; /* We have a subaddr */ tmp3 = tmp4 + strlen (tmp4) - 1; while (tmp3 >= tmp4) { if (*tmp3 >= '0' && *tmp3 <= '9') { sub += (*tmp3 - '0') * mul; } else { printk("TDMoEmf: Invalid subaddress\n"); kfree(z); return NULL; } mul *= 10; tmp3--; } z->subaddr = htons(sub); } z->dev = dev_get_by_name(z->ethdev); if (!z->dev) { printk("TDMoEmf: Invalid device '%s'\n", z->ethdev); kfree(z); return NULL; } z->span = span; src[0] ='\0'; for (x=0;x<5;x++) sprintf(src + strlen(src), "%02x:", z->dev->dev_addr[x]); sprintf(src + strlen(src), "%02x", z->dev->dev_addr[5]); printk("TDMoEmf: Added new interface for %s at %s (addr=%s, src=%s, subaddr=%d)\n", span->name, z->dev->name, addr, src, ntohs(z->subaddr)); spin_lock_irqsave(&zlock, flags); z->next = zdevs; zdevs = z; spin_unlock_irqrestore(&zlock, flags); #ifndef LINUX26 MOD_INC_USE_COUNT; #else if(!try_module_get(THIS_MODULE)) printk("TDMoEmf: Unable to increment module use count\n"); #endif /* enable the timer for enabling the spans */ timer.expires = jiffies + 1; add_timer(&timer); } return z; } static struct zt_dynamic_driver ztd_ethmf = { "ethmf", "Ethernet", ztdethmf_create, ztdethmf_destroy, ztdethmf_transmit, ztdethmf_flush }; static struct notifier_block ztdethmf_nblock = { notifier_call: ztdethmf_notifier, }; static void timer_callback(unsigned long param) { unsigned long flags; struct ztdeth *z; int count_zero = 0, count_nonzero = 0; /* loop spans */ ++timer_counter; if (timer_counter >= HZ) { timer_counter -= HZ; spin_lock_irqsave(&zlock, flags); z = zdevs; while(z) { if (z->delay) { z->delay--; // ++count_nonzero; if (!z->delay) { printk("TDMoEmf: Span %s is ready for use.\n", z->span->name); } else { ++count_nonzero; } } else { ++count_zero; } z = z->next; } spin_unlock_irqrestore(&zlock, flags); if (count_nonzero /* && count_zero && count_nonzero != count_zero */) { /* re-add the timer event */ timer.expires = jiffies + 1; add_timer(&timer); } else { printk("TDMoEmf: All spans are active - removing delay timer.\n"); } } else { timer.expires = jiffies + 1; add_timer(&timer); } } static int __init ztdethmf_init(void) { timer_counter = 0; init_timer(&timer); timer.expires = jiffies + 1; timer.function = &timer_callback; add_timer(&timer); dev_add_pack(&ztdethmf_ptype); register_netdevice_notifier(&ztdethmf_nblock); zt_dynamic_register(&ztd_ethmf); skb_queue_head_init(&skbs); return 0; } static void __exit ztdethmf_exit(void) { del_timer(&timer); dev_remove_pack(&ztdethmf_ptype); unregister_netdevice_notifier(&ztdethmf_nblock); zt_dynamic_unregister(&ztd_ethmf); } MODULE_DESCRIPTION("Zaptel Dynamic TDMoEmf Support"); MODULE_AUTHOR("Joseph Benden "); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif module_init(ztdethmf_init); module_exit(ztdethmf_exit);