/***************************************************************************** SS7 LINK LEVEL (L2) INTERFACE FOR NET4 ----------------------------------------------------------------------------- Copyright (C) 1997, 1998, 1999, 2000 Brian Bidulock 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. Last Modified $Date: 2000/10/09 02:40:40 $ by $Author: brian $ ----------------------------------------------------------------------------- $Log: ss7if.c,v $ Revision 0.7.2.4 2000/10/09 02:40:40 brian Pre-release 1. Revision 0.7.2.3 2000/09/11 23:35:27 brian Changed to work with new header files. Revision 0.7.2.2 2000/09/07 13:29:01 brian ss7link.o compiles as a module and loads fine. Revision 0.7.2.1 2000/09/07 12:49:11 brian Got ss7link interface support for drivers compiling. Revision 0.7 2000/09/07 11:20:33 brian Initial import of OpenSS7 stack. Revision 0.7.2.2 2000/09/07 11:07:36 brian Fixed minor error. Revision 0.7.2.1 2000/09/07 10:51:42 brian Got these files going. Revision 0.7 2000/09/06 19:39:10 brian Added files back in at revision 0.7. Revision 1.1.4.5 2000/09/06 18:39:08 brian Added a lot of stuff. Revision 1.1.4.4 2000/09/06 07:30:22 brian Even more playing with headers. Revision 1.1.4.3 2000/09/06 07:28:26 brian More playing with headers. Revision 1.1.4.2 2000/09/06 07:23:15 brian More playing with headers. *****************************************************************************/ static char const ident[] = "$Id: ss7if.c,v 0.7.2.4 2000/10/09 02:40:40 brian Exp $"; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../net/ss7/af_ss7.h" #include "../../net/ss7/mtp.h" #include "../../net/ss7/mtp_route.h" #include "../../net/ss7/mtp_sm.h" #include "../../include/linux/ss7link.h" #include "../../include/linux/ss7_timers.h" #include "ss7if.h" #include #define SS7LINK_DESCRIPTION "SS7/LINK($Revision: 0.7.2.4 $): SS7 LINK LEVEL (L2) INTERFACE FOR NET4" #define SS7LINK_COPYRIGHT "Copyright (C) 1997-2000 Brian Bidulock. All Rights Reserved." #define SS7LINK_DEVICES "Supporting various serial and T1 cards." #define SS7LINK_AUTHOR "Brian F. G. Bidulock " #define SS7LINK_BANNER SS7LINK_DESCRIPTION "\n" \ SS7LINK_COPYRIGHT "\n" \ SS7LINK_DEVICES "\n" \ SS7LINK_AUTHOR "\n" MODULE_AUTHOR(SS7LINK_AUTHOR); MODULE_DESCRIPTION(SS7LINK_DESCRIPTION); MODULE_SUPPORTED_DEVICE(SS7LINK_DEVICES); static void ss7_timer_init(struct ss7_iface *l, int n, int d) { struct ss7if_timer *t = &l->timers[n]; struct timer_list *k = &t->kernel_timer; init_timer(k); t->taskq.sync = 0; t->taskq.next = 0; /* Its a floor wax AND a dessert topping! */ t->taskq.data = l; t->duration = d; k->data = (unsigned long)t; k->function = (void(*)(unsigned long))ss7_timer_timeout; } /* * This is a wrapper for the dev->open() interface service routine. It * performs initializations on SS7 LINK interface and then calls the * device driver's initialization routine. */ static int ss7if_open(struct device *dev) { struct ss7_iface *iface; if ((iface = dev->ss7_ptr) == NULL) return -ENODEV; if (iface->LSC_state && iface->LSC_state != LSC_STATE_Out_of_Service) return -EINVAL; /* * Initialize interface structures: */ ss7_timer_init(iface,TIMER_T1, TIMER_T1V); ss7_timer_init(iface,TIMER_T2, TIMER_T2V); ss7_timer_init(iface,TIMER_T3, TIMER_T3V); ss7_timer_init(iface,TIMER_T4, TIMER_T4NV); ss7_timer_init(iface,TIMER_T5, TIMER_T5V); ss7_timer_init(iface,TIMER_T6, TIMER_T6V); ss7_timer_init(iface,TIMER_T7, TIMER_T7V); skb_queue_head_init(&iface->xmit_q); /* trasmit buffer */ skb_queue_head_init(&iface->rexm_q); /* retransmit buffer */ skb_queue_head_init(&iface->writ_q); /* output queue */ skb_queue_head_init(&iface->read_q); /* input queue */ iface->tx_taskq.sync = 0; iface->tx_taskq.next = 0; iface->tx_taskq.data = iface; iface->rx_taskq.sync = 0; iface->rx_taskq.next = 0; iface->rx_taskq.data = iface; if (!iface->fisu_skb) if ((iface->fisu_skb = alloc_skb(4,GFP_ATOMIC)) == NULL) return -ENOBUFS; iface->fisu_skb->data[0] = 0x00; iface->fisu_skb->data[1] = 0x00; iface->fisu_skb->data[2] = 0x00; iface->fisu_skb->data[3] = 0x00; if (!iface->lssu_skb) if ((iface->lssu_skb = alloc_skb(5,GFP_ATOMIC)) == NULL) return -ENOBUFS; iface->lssu_skb->data[0] = 0x00; iface->lssu_skb->data[1] = 0x00; iface->lssu_skb->data[2] = 0x01; iface->lssu_skb->data[3] = 0x00; if (iface->if_open) return iface->if_open(dev); /* * The L3 protocol entity will prepare us for service by sending us an * L2 Start command once it sees our NETDEV_UP notification. */ return(0); } /* * This is a wrapper for the dev->stop() interface service routine. It * performs cleanup on the SS7 LINK interface and then calls the device * driver's close routine. If the SS7 link is running (haven't received * L3 Stop command), then we are in trouble 'cause our backlog queue is * about to be flushed. Scream loudly and hope that L3 can recover by * finding out through the notifier chain. * * Should not be called at interrupt level. */ static int ss7if_stop(struct device *dev) { struct ss7_iface *iface; if ((iface = dev->ss7_ptr) == NULL) return -ENODEV; if (iface->LSC_state && iface->LSC_state != LSC_STATE_Out_of_Service) printk(KERN_ERR "SS7/LINK: Running SS7 Link closed!\n"); { /* * FIXME: Is this OK? No interrupts disabled or anything? */ skb_queue_purge(&iface->xmit_q); skb_queue_purge(&iface->rexm_q); skb_queue_purge(&iface->writ_q); skb_queue_purge(&iface->read_q); if (iface->fisu_skb) { kfree_skb(iface->fisu_skb); iface->fisu_skb = NULL; } if (iface->lssu_skb) { kfree_skb(iface->lssu_skb); iface->lssu_skb = NULL; } } /* FIXME: cleanup interface structures but don't deallocate: we wait * for the destructor to deallocate. */ if (iface->if_stop) return iface->if_stop(dev); return(0); } /* * This is a wrapper for the dev->destructor() interface service routine. * It performs the same cleanup as an explicit call to ss7if_unregister() * or ss7if_detach(). The interface is detached from the device and all * associated SS7 LINK interface structure are deallocated. */ static void ss7if_kill(struct device *dev) { return ss7if_unregister(dev); } /* * This is the wrappter for the dev->hard_start_xmit() interface service * routine. It queues frames coming downwards from the L3 protocol entity * and defers processing on them until a bottom-half runs. For proper * operation, the `real' device driver should never set the dev->tbusy * flag. */ static int ss7if_xmit(struct sk_buff *skb, struct device *dev) { struct ss7_iface *iface; if (dev->type != ARPHRD_SS7) goto ss7if_not_for_us; if (!(iface = dev->ss7_ptr)) goto ss7if_not_for_us; return ss7if_output(skb,dev); ss7if_not_for_us: if (net_ratelimit()) printk(KERN_ERR "SS7/LINK: link for %s received unexpected packet!\n", dev->name); kfree_skb(skb); return(0); } /* * This is the wrapper for the dev->do_ioctl() interface service routine. * It is expected that this has been called via dev_ioctl(), so we only * strip off our private ioctl's and pass the rest on to the `real' * device. */ static int ss7if_ioctl(struct device *dev, struct ifreq *ifr, int cmd) { struct ss7_iface *iface; if (!(iface = dev->ss7_ptr)) return -ENODEV; switch (cmd) { /* * We only intercept the SS7 specific private ioctls here. We * allocate from SIOCDEVPRIVATE + 15 downwards to keep away from * the device's SIOCDEVPRIVATE + 0 upwards allocation. * * FIXME: add some ioctls. */ case SIOCSPRIV_f: case SIOCSPRIV_e: case SIOCSPRIV_d: case SIOCSPRIV_c: case SIOCSPRIV_b: default: if (iface->if_ioctl) return iface->if_ioctl(dev, ifr, cmd); } return -EINVAL; } static int __ss7if_remove(struct device *dev, struct ss7if_reg *regs) { struct ss7_iface *iface; if ((iface = dev->ss7_ptr) == NULL) return -ENODEV; if (iface->LSC_state && iface->LSC_state != LSC_STATE_Out_of_Service) printk(KERN_ERR "SS7/LINK: Running SS7 Link closed!\n"); /* FIXME: do some more cleanup here. */ dev->ss7_ptr = NULL; if (regs) (*regs) = iface->calls; #if 0 /* FIXME: get this working. */ if (iface->linkset) { /* oops, my address is linked in... */ unsigned long flags; save_flags(flags); cli(); iface->next->prev = iface->prev; iface->prev->next = iface->next; if (iface->next == iface->prev) iface->next = iface->prev = NULL; if (iface->linkset->link == iface) iface->linkset->link= iface->next; /* * Let L3 find the empty linkset during routing. */ restore_flags(flags); } #endif kfree(iface); MOD_DEC_USE_COUNT; return(0); } void ss7if_unregister(struct device *dev) { __ss7if_remove(dev,NULL); } void ss7if_detach(struct device *dev) { __ss7if_remove(dev,NULL); } int ss7if_register(struct device *dev, struct ss7if_reg *regs) { struct ss7_iface *iface; if (!regs || !regs->hard_start_xmit) return -EINVAL; if ((iface = kmalloc(sizeof(*iface),GFP_ATOMIC)) == 0) return -ENOMEM; memset(iface,0x00,sizeof(*iface)); iface->calls = (*regs); dev->mtu = SS7_MTU; dev->type = ARPHRD_SS7; dev->hard_header_len = 0; dev->addr_len = 0; dev->tx_queue_len = TB_Q_MAX_SIZE; dev->destructor = ss7if_kill; dev->open = ss7if_open; dev->stop = ss7if_stop; dev->hard_start_xmit = ss7if_xmit; dev->do_ioctl = ss7if_ioctl; dev->ss7_ptr = iface; iface->dev = dev; /* User will have to do this: symbol not exported */ /* dev_activate(dev); * reinitialize buffer queues */ /* * The L3 protocol entity will initialize our address and assign us to * a linkset when it sees out NETDEV_REGISTER notification. */ MOD_INC_USE_COUNT; return(0); } int ss7if_attach(struct device *dev) { struct ss7if_reg regs; regs.destructor = dev->destructor ; regs.open = dev->open ; regs.stop = dev->stop ; regs.hard_start_xmit = dev->hard_start_xmit ; regs.do_ioctl = dev->do_ioctl ; return ss7if_register(dev,®s); } /* * A device registered with our hardware type: create a new interface and * attach it to the device. */ static int ss7if_device_event(struct notifier_block *self, unsigned long event, void *devptr) { struct device* dev = devptr; if ( dev->type == ARPHRD_SS7 && event == NETDEV_REGISTER && ss7if_attach(dev) ) return NOTIFY_BAD; return NOTIFY_DONE; } static struct notifier_block ss7if_nb = { ss7if_device_event, NULL, 10 }; int init_module(void) { printk(KERN_INFO SS7LINK_BANNER); register_netdevice_notifier(&ss7if_nb); printk(KERN_INFO "Ready to accept SS7 Link devices.\n"); return(0); } void cleanup_module(void) { unregister_netdevice_notifier(&ss7if_nb); printk(KERN_DEBUG "SS7/LINK: Say \"goodnight,\" Gracy!\n"); }