/* * ax25_ddi.c: Device Driver Independent Module for NEW-AX.25 * * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), * * Comment: Contains device driver interface, scheduler, channel arbitration * LAPB state machine is synchronized here to avoid race conditions * Written from scratch by Matthias Welwarsky in 1998. * * Changelog: * * License: This module 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. */ #include #include #include #include #include #include #include #include #include "af_ax25.h" #include "ax25_ddi.h" #include "ax25_core.h" #include "ax25_in.h" #include "ax25_subr.h" #include "ax25_route.h" #include "ax25_timer.h" struct net_device *ax25_devices[AX25_MAX_DEVICES]; rwlock_t ax25_dev_lock = RW_LOCK_UNLOCKED; /* * ------------------------------------------------------------------------ * declaration of private functions * ------------------------------------------------------------------------ */ static void clear_ax25devices(void); static void ax25_dev_timer(unsigned long); static void ax25_dev_tic(unsigned long); static void ax25_transmit_buffer(ax25_cb*, struct sk_buff*, int); static void ax25_send_iframe(ax25_cb*, struct sk_buff*, int); static void ax25_send_control(ax25_cb*, int, int, int); static void ax25_kick_device(struct ax25_dev*); static __inline__ void ax25_dev_add_ready(struct ax25_dev *, ax25_cb *); static __inline__ void ax25_dev_remove_active(struct ax25_dev *); static __inline__ void ax25_dev_remove_ready(struct ax25_dev *, ax25_cb *); static void ax25_dev_set_tic(struct ax25_dev *); static void ax25_dev_set_timer(struct ax25_dev *, unsigned int); static void ax25_queue_xmit(struct sk_buff *); static struct ax25_dev *ax25_dev_get_dev(struct net_device *); /* * ------------------------------------------------------------------------ * Interface implementation * All public functions of this module are defined here * ------------------------------------------------------------------------ */ void ax25_ddi_init(void) { clear_ax25devices(); } /* * queue a fully assembled frame in the unproto queue of the * device and mark the channel ready for transmission */ void ax25_send_unproto(struct sk_buff* skb, struct net_device* dev) { struct ax25_dev* ax25_device = AX25_PTR(dev); skb->dev = dev; skb_queue_tail(&ax25_device->unproto_queue, skb); ax25_kick_device(ax25_device); } void ax25_send_broadcast(struct sk_buff *skb) { int i; read_lock(&ax25_dev_lock); for (i = 0; i < AX25_MAX_DEVICES; i++) { struct net_device *dev = ax25_devices[i]; if (dev != NULL && (dev->flags & (IFF_UP|IFF_BROADCAST)) != 0) { struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); if (newskb != NULL) ax25_send_unproto(newskb, dev); else printk(KERN_ERR "ax25_send_broadcast: unable to clone packet.\n"); } } read_unlock(&ax25_dev_lock); /* caller frees original packet */ } /* * put a connection on the ready list of it's device and mark the device * ready for transmission. */ void ax25_kick(ax25_cb *ax25) { if (ax25->device != NULL) { struct ax25_dev *ax25_device = AX25_PTR(ax25->device); /* * put the connection on the readylist of this channel, * if it's not already there. */ ax25_dev_add_ready(ax25_device, ax25); /* * mark the channel ready */ ax25_kick_device(ax25_device); } } /* * return the connection list to a given device */ ax25_cb *ax25_dev_list(struct net_device *dev) { struct ax25_dev *ax25_device; if (dev == NULL) return ax25_list; if ((ax25_device = AX25_PTR(dev)) != NULL && ax25_device->magic == AX25_DEV_MAGIC) return ax25_device->list.all; return NULL; } /* * insert a connection into a device queue */ void ax25_dev_insert_cb(ax25_cb *ax25) { struct ax25_dev *ax25_device = AX25_PTR(ax25->device); if (ax25_device->magic != AX25_DEV_MAGIC) { printk(KERN_ERR "ax25_dev_insert_cb: wrong magic number.\n"); return; } ax25->prev = NULL; ax25->next = ax25_device->list.all; write_lock(&ax25_dev_lock); if (ax25_device->list.all != NULL) ax25_device->list.all->prev = ax25; ax25_device->list.all = ax25; write_unlock(&ax25_dev_lock); ax25->inserted = 1; } /* * remove a connection from a device queue */ void ax25_dev_remove_cb(ax25_cb *ax25) { struct ax25_dev *ax25_device = AX25_PTR(ax25->device); struct net_device *dev = ax25->device; struct ax25_cb *axp; if (ax25_device->magic != AX25_DEV_MAGIC) { printk(KERN_ERR "ax25_dev_remove_cb: wrong magic number.\n"); return; } if (ax25_device->list.all == NULL) { printk(KERN_ERR "ax25_dev_remove_cb: empty list.\n"); return; } write_lock(&ax25_dev_lock); if (ax25->prev == NULL) { ax25_device->list.all = ax25->next; } else { ax25->prev->next = ax25->next; } if (ax25->next != NULL) ax25->next->prev = ax25->prev; ax25->inserted = 0; if (xchg(&ax25->ready.state, AX25_SCHED_IDLE) == AX25_SCHED_READY) ax25_dev_remove_ready(ax25_device, ax25); /* search for active circuits and set DAMA flag accordingly */ for (axp=ax25_device->list.all; axp!=NULL; axp=axp->next) if ((axp->state == AX25_STATE_3) || (axp->state == AX25_STATE_4)) break; if (axp == NULL) ax25_dev_set_dama(dev, 0); write_unlock(&ax25_dev_lock); } /* * Look for any matching address. */ int ax25_dev_match_addr(ax25_address *addr, struct net_device *dev) { ax25_cb *s; for (s = ax25_dev_list(dev); s != NULL; s = s->next) { if (s->state == AX25_LISTEN && s->sk == NULL && ax25cmp(&s->addr.src, addr) == 0) return 1; } return 0; } /* * Find a control block that wants to accept the SABM we have just * received. */ ax25_cb *ax25_dev_find_listener(ax25_address *addr, int digi, struct net_device *dev) { ax25_cb *s; read_lock(&ax25_dev_lock); for (s = ax25_dev_list(dev); s != NULL; s = s->next) { if (s->state != AX25_LISTEN) continue; if ((s->iamdigi && !digi) || (!s->iamdigi && digi)) continue; if (ax25cmp(&s->addr.src, addr) == 0) break; } read_unlock(&ax25_dev_lock); return s; } /* * Find an AX.25 socket given both ends. */ struct sock *ax25_dev_find_socket(ax25_address *my_addr, ax25_address *dest_addr, struct net_device *dev, int type) { ax25_cb *s; read_lock(&ax25_dev_lock); for (s = ax25_dev_list(dev); s != NULL; s = s->next) { if (s->sk != NULL && ax25cmp(&s->addr.src, my_addr) == 0 && ax25cmp(&s->addr.dest, dest_addr) == 0 && s->sk->type == type) { read_unlock(&ax25_dev_lock); return s->sk; } } read_unlock(&ax25_dev_lock); return NULL; } /* * This function is called whenever a parameter is modified using * ax25_dev_set_value_notify or via the proc/sysctl interface. It * decides whether to notify the device driver of the event. If the * decision is positive, it uses the parameter_change downcall. * The driver can then react and re-set the value or pick the * closest value the hardware allows (e.g. by baud rate divider etc.). * The most important values for the device driver are duplex, txdelay, * txtail, {tx,rx}bitrate. Slottime and p-persistence are currently * only "for info" since channel arbitration is done by DDI layer now. */ void ax25_notify_dispatcher(struct net_device *dev, int id, int oldval, int newval) { struct ax25_dev *ax_dev; if (!dev) return; /* paranoia */ ax_dev = AX25_PTR(dev); if (!ax_dev) return; /* paranoia */ switch (id) { case AX25_VALUES_MEDIA_DUPLEX: case AX25_VALUES_MEDIA_TXDELAY: case AX25_VALUES_MEDIA_TXTAIL: case AX25_VALUES_MEDIA_TXBITRATE: case AX25_VALUES_MEDIA_RXBITRATE: case AX25_VALUES_MEDIA_SLOTTIME: case AX25_VALUES_MEDIA_PPERSISTENCE: case AX25_VALUES_MEDIA_AUTO_ADJUST: if (ax_dev->hw.parameter_change_notify) { (ax_dev->hw.parameter_change_notify)(dev, id, oldval, newval); } break; default: break; } return; } /* * Call this function from AX.25 driver to check if driver has * to be notified of the event. */ void ax25_dev_set_value_notify(struct net_device *dev, int valueno, int newvalue) { int oldvalue; oldvalue = ax25_dev_get_value(dev, valueno); ax25_dev_set_value(dev, valueno, newvalue); if (oldvalue != newvalue) ax25_notify_dispatcher(dev, valueno, oldvalue, newvalue); } /* * This is called when an interface is brought up. These are * reasonable defaults. We try not to mess with the media parameters * if they appear as having been set already. */ void ax25_dev_device_up(struct net_device *dev) { struct ax25_dev *ax25_device = AX25_PTR(dev); int txbitrate; if (!ax25_device || ax25_device->magic != AX25_DEV_MAGIC) return; ax25_device->ready_lock = RW_LOCK_UNLOCKED; ax25_device->forward = NULL; ax25_device->list.all = NULL; ax25_device->list.ready = NULL; skb_queue_head_init(&ax25_device->unproto_queue); ax25_device->bytes_sent = 0; ax25_device->dama_mode = 0; ax25_dev_set_value_notify(dev, AX25_VALUES_IPDEFMODE, AX25_DEF_IPDEFMODE); ax25_dev_set_value_notify(dev, AX25_VALUES_AXDEFMODE, AX25_DEF_AXDEFMODE); ax25_dev_set_value_notify(dev, AX25_VALUES_BACKOFF, AX25_DEF_BACKOFF); ax25_dev_set_value_notify(dev, AX25_VALUES_CONMODE, AX25_DEF_CONMODE); ax25_dev_set_value_notify(dev, AX25_VALUES_WINDOW, AX25_DEF_WINDOW); ax25_dev_set_value_notify(dev, AX25_VALUES_EWINDOW, AX25_DEF_EWINDOW); ax25_dev_set_value_notify(dev, AX25_VALUES_T1, AX25_DEF_T1); ax25_dev_set_value_notify(dev, AX25_VALUES_T3, AX25_DEF_T3); ax25_dev_set_value_notify(dev, AX25_VALUES_IDLE, AX25_DEF_IDLE); ax25_dev_set_value_notify(dev, AX25_VALUES_N2, AX25_DEF_N2); ax25_dev_set_value_notify(dev, AX25_VALUES_PACLEN, AX25_DEF_PACLEN); ax25_dev_set_value_notify(dev, AX25_VALUES_PROTOCOL, AX25_DEF_PROTOCOL); ax25_dev_set_value_notify(dev, AX25_VALUES_DAMA_SLAVE_TIMEOUT, AX25_DEF_DAMA_SLAVE_TIMEOUT); txbitrate = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE); ax25_dev_set_value_notify(dev, AX25_VALUES_T2, txbitrate > 0 ? (3600 / AX25_TICS) * HZ / txbitrate : 0); if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE) == 0) ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_PPERSISTENCE, AX25_DEF_MEDIA_PPERSISTENCE); if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_SLOTTIME) == 0) ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_SLOTTIME, AX25_DEF_MEDIA_SLOTTIME); ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_AUTO_ADJUST, AX25_DEF_MEDIA_AUTO_ADJUST); init_timer(&ax25_device->timer); ax25_dev_set_timer(ax25_device, AX25_TICS); init_timer(&ax25_device->tics); ax25_dev_set_tic(ax25_device); } /* * this is called when a device is brought down. Delete the device * timers and update the sysctl interface. */ void ax25_dev_device_down(struct net_device *dev) { struct ax25_dev *ax25_device = AX25_PTR(dev); ax25_kill_by_device(dev); ax25_rt_device_down(dev); if (!ax25_device || ax25_device->magic != AX25_DEV_MAGIC) { printk(KERN_ERR "ax25_dev_device_down: not an AX.25 device.\n"); return; } del_timer(&ax25_device->timer); del_timer(&ax25_device->tics); /* FIXME: do I have to lock this or not? */ /* start_bh_atomic(); */ skb_queue_purge(&ax25_device->unproto_queue); /* end_bh_atomic(); */ } /* * Packet forwarding control IOCTL * FIXME: does anybody really need this feature? */ int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd) { struct net_device *dev; struct ax25_dev *ax25_dev; if ((dev = ax25rtr_get_dev(&fwd->port_from)) == NULL) return -EINVAL; if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL) return -EINVAL; switch (cmd) { case SIOCAX25ADDFWD: if ((dev = ax25rtr_get_dev(&fwd->port_to)) == NULL) return -EINVAL; if (ax25_dev->forward != NULL) return -EINVAL; ax25_dev->forward = dev; break; case SIOCAX25DELFWD: if (ax25_dev->forward == NULL) return -EINVAL; ax25_dev->forward = NULL; break; default: return -EINVAL; } return 0; } struct net_device *ax25_fwd_dev(struct net_device *dev) { struct ax25_dev *ax25_dev; if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL) return dev; if (ax25_dev->forward == NULL) return dev; return ax25_dev->forward; } int ax25_dev_get_info(char *buffer, char **start, off_t offset, int length) { int i; struct net_device *dev; char devname[7]; int len = 0; off_t pos = 0; off_t begin = 0; len += sprintf(buffer, "device hwaddr rifr tifr rrej rkby tkby duplex tx-bps rx-bps ppers slot auto txd txt \n"); read_lock(&ax25_dev_lock); for (i = 0; i < AX25_MAX_DEVICES; i++) { if ((dev = ax25_devices[i]) != NULL) { strncpy(devname, dev->name, 6); devname[6] = 0; len += sprintf(buffer+len, "%-6s %-9s %-6ld %-6ld %-6ld %-9ld %-9ld %-6s %-8d %-8d %-5d %-4d %-4s %-4d %-4d\n", devname, ax2asc((ax25_address *)dev->dev_addr), AX25_PTR(dev)->rx_iframes, AX25_PTR(dev)->tx_iframes, AX25_PTR(dev)->rx_rejects, AX25_PTR(dev)->rx_bytes/1024, AX25_PTR(dev)->tx_bytes/1024, ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX) ? "full" : "half", ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE), ax25_dev_get_value(dev, AX25_VALUES_MEDIA_RXBITRATE), ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE), ax25_dev_get_value(dev, AX25_VALUES_MEDIA_SLOTTIME), ax25_dev_get_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST) ? "on" : "off", ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY), ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL)); pos = begin + len; if (pos < offset) { len = 0; begin = pos; } if (pos > offset + length) break; } } read_unlock(&ax25_dev_lock); *start = buffer + (offset - begin); len -= offset - begin; if (len > length) len = length; return len; } /* * This function is called by core/dev.c whenever a new netdevice is * being registerd. We initialize its ax25_dev structure and include * it in our list. We also register the sysctl tree for it and initialize * its parameters. */ void register_ax25device(struct net_device *dev) { int i; struct ax25_dev *axdev = AX25_PTR(dev); axdev->magic = AX25_DEV_MAGIC; axdev->netdev = dev; memcpy((char *) dev->broadcast, (char *) asc2ax("QST-0"), AX25_ADDR_LEN); ax25_unregister_sysctl(); write_lock(&ax25_dev_lock); for (i = 0; i < AX25_MAX_DEVICES; i++) { if (ax25_devices[i] == NULL) { ax25_devices[i] = dev; break; } } ax25_register_sysctl(); if (i == AX25_MAX_DEVICES) { printk(KERN_ERR "AX.25: Too many devices, could not register.\n"); goto done; } ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_DUPLEX, 0); if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY) == 0) ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_TXDELAY, AX25_DEF_MEDIA_TXDELAY); if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL) == 0) ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_TXTAIL, AX25_DEF_MEDIA_TXTAIL); if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE) == 0) ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_TXBITRATE, AX25_DEF_MEDIA_TXBITRATE); if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_RXBITRATE) == 0) ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_RXBITRATE, AX25_DEF_MEDIA_RXBITRATE); /* * slottime, p-persistence and auto-adjust defaults are * loaded upon interface start */ done: write_unlock(&ax25_dev_lock); } /* * This function is executed when an interface is about to be removed. * It must already have been downed before. We remove it from our * list and remove sysctl directory entry. */ void unregister_ax25device(struct net_device *dev) { int i; ax25_unregister_sysctl(); write_lock(&ax25_dev_lock); for (i = 0; i < AX25_MAX_DEVICES; i++) { if (ax25_devices[i] == dev) { ax25_devices[i] = NULL; break; } } write_unlock(&ax25_dev_lock); ax25_register_sysctl(); } /* * Activate/Deactivate DAMA on a given interface. * We automagically configure the media for full duplex if neccessary. */ void ax25_dev_set_dama(struct net_device *dev, int dama) { if (dama && (ax25_dev_get_value(dev, AX25_VALUES_PROTOCOL) == 1)) { if (!(AX25_PTR(dev)->dama_mode & DAMA_SLAVE)) { AX25_PTR(dev)->dama_mode |= DAMA_SLAVE; ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_DUPLEX, 1); } } else { if (AX25_PTR(dev)->dama_mode & DAMA_SLAVE) { AX25_PTR(dev)->dama_mode &= ~DAMA_SLAVE; ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_DUPLEX, 0); } } return; } /* * ------------------------------------------------------------------------ * End of public area, all private functions of this module are defined * here. * ------------------------------------------------------------------------ */ static void clear_ax25devices(void) { int i; write_lock(&ax25_dev_lock); for (i = 0; i < AX25_MAX_DEVICES; i++) ax25_devices[i] = NULL; write_unlock(&ax25_dev_lock); } /* * simple pseudo-random number generator, stolen from hdlcdrv.c :) */ static inline unsigned short random_num(void) { static unsigned short random_seed; random_seed = 28629 * random_seed + 157; return random_seed & 0xFF; } /* * add a connection to the channels readylist */ static inline void ax25_dev_add_ready(struct ax25_dev *ax25_device, ax25_cb *ax25) { write_lock(&ax25_device->ready_lock); if (ax25->ready.state != AX25_SCHED_READY) { ax25->ready.state = AX25_SCHED_READY; if (ax25_device->list.ready == NULL) { ax25->ready.prev = ax25; ax25->ready.next = ax25; ax25_device->list.ready = ax25; } else { ax25->ready.next = ax25_device->list.ready; ax25->ready.prev = ax25_device->list.ready->ready.prev; ax25_device->list.ready->ready.prev->ready.next = ax25; ax25_device->list.ready->ready.prev = ax25; } } write_unlock(&ax25_device->ready_lock); } /* * remove the active connection from the channels readylist * NB: caller must do write_lock() on ax25_device->ready_lock! */ static inline void ax25_dev_remove_active(struct ax25_dev *ax25_device) { ax25_cb *active = ax25_device->list.ready; if (active->ready.next == active) { ax25_device->list.ready = NULL; } else { ax25_device->list.ready = active->ready.next; active->ready.next->ready.prev = active->ready.prev; active->ready.prev->ready.next = active->ready.next; } active->ready.state = AX25_SCHED_IDLE; } /* * remove a connection from the channels readylist */ static inline void ax25_dev_remove_ready(struct ax25_dev *ax25_device, ax25_cb *ax25) { write_lock(&ax25_device->ready_lock); if (ax25 == ax25_device->list.ready) { ax25_dev_remove_active(ax25_device); } else { ax25->ready.next->ready.prev = ax25->ready.prev; ax25->ready.prev->ready.next = ax25->ready.next; ax25->ready.state = AX25_SCHED_IDLE; } write_unlock(&ax25_device->ready_lock); } /* * Timer for a per device 100ms timing tic. AX.25 Timers of all * connections on this device are driven by this timer. */ static void ax25_dev_set_tic(struct ax25_dev *this) { this->tics.data = (unsigned long)this; this->tics.function = &ax25_dev_tic; this->tics.expires = jiffies + AX25_TICS; add_timer(&this->tics); } static void ax25_dev_tic(unsigned long param) { ax25_cb *active; struct ax25_dev *this = (struct ax25_dev *) param; if (!this->needs_transmit && ((!this->hw.ptt) || (!this->hw.ptt(this->netdev)))) { for (active = this->list.all; active; active = active->next) { /* * only run the timer on idle connections. */ if (!active->ready.state) ax25_timer(active); } } ax25_dev_set_tic(this); } /* * Timer for channel access arbitration. Fires every 100ms if the channel * is idle (i.e. no connections need to transmit), and in intervals of * half of a frame length if trying to transmit */ static void ax25_dev_set_timer(struct ax25_dev *this, unsigned int tics) { this->timer.data = (unsigned long)this; this->timer.function = &ax25_dev_timer; this->timer.expires = jiffies + tics; add_timer(&this->timer); } static void ax25_dev_timer(unsigned long param) { struct ax25_dev *this = (struct ax25_dev *) param; struct net_device *dev = this->netdev; ax25_cb *active; struct sk_buff *skb; unsigned int bytes_sent = 0; unsigned int max_bytes; int ppers = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE); int br = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE); int duplex = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX); int bit_per_jiffie; int jiffies_per_slot; if (br == 0) { printk(KERN_ERR "ax25_dev_timer(%s): TX-Bitrate unset!!!\n", dev->name); } bit_per_jiffie = br / HZ; jiffies_per_slot = 1200 * HZ / br + 1; if (this->dama_mode & DAMA_SLAVE) { /* >>>>> DAMA slave <<<<< * * we only transmit when we are asked to do so or when * T3 ran out, which should only occur if the master forgot * our circuits (i.e. had a reset or is broken otherwise). */ if (this->dama_polled) { /* we have been polled, it's ok to transmit */ this->dama_polled = 0; goto arbitration_ok; } else { /* * we are not allowed to transmit. Maybe next time. */ ax25_dev_set_timer(this, jiffies_per_slot); return; } } else if (this->dama_mode & DAMA_MASTER) { /* >>>>> DAMA master <<<<< * * insert code here * this could have been your ad! :-) */ } else { /* >>>>> CSMA <<<<< * * this implements a rather innovative channel access method. * the basic idea is to run the usual slottime/persistence * scheme, but with two significant changes: * 1. slottime is derived from the bitrate of the channel * 2. persistence is variable, depending on the dcd pattern * of the channel. * * "Sample the dcd in intervals of half of a frames length and * - increment persistence value if dcd is inactive, * - decrement persistence value if dcd is active." * * simulations show that this scheme gives good collision * avoidance and throughput without knowledge about the * dcd propagation delay and station count. It will probably * perform *much* too aggressive in a hidden station environment. * * Note: The check for hw.fast skips the channel arbitration * stuff. Set this for KISS and ethernet devices. */ if (!this->hw.fast && !duplex && !this->hw.ptt(this->netdev)) { /* decide whether this is a "good" slot or not */ if (random_num() < ppers) { /* ok, a good one, check the dcd now */ if (this->hw.dcd(this->netdev)) { this->dcd_memory = 1; /* * too bad, dcd is up. we're too aggressive, * but we must wait for a falling edge of the dcd * before we can decrement persistence */ if (this->dcd_dropped && ppers > 1) if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST)) ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_PPERSISTENCE, ppers); if (this->needs_transmit) ax25_dev_set_timer(this, jiffies_per_slot); return; } /* update dcd memory */ this->dcd_memory = 0; this->dcd_dropped = 0; goto arbitration_ok; } else { /* a bad slot, check the dcd */ if (!this->hw.dcd(this->netdev)) { /* um. dcd is down, we should have tx'd here. */ if (ppers < 128) if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST)) ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_PPERSISTENCE, ppers+1); /* was it up the slot before? */ if (this->dcd_memory) { this->dcd_dropped = 1; } this->dcd_memory = 0; } else { this->dcd_memory = 1; } if (this->needs_transmit) ax25_dev_set_timer(this, jiffies_per_slot); return; } } } arbitration_ok: /* * OK, we may transmit, arbitration successful. */ if (this->hw.rts) this->hw.rts(this->netdev); /* * compute the amount of bytes to send during 100ms (AX25_TICS) */ max_bytes = (bit_per_jiffie * AX25_TICS); /* * UI Frames */ while ((bytes_sent < max_bytes || this->hw.fast) && ((skb = skb_dequeue(&this->unproto_queue)) != NULL)) { ax25_queue_xmit(skb); bytes_sent += skb->len; } /* * traverse our list of connections. we're messing with a * private list here and we will not sleep and schedule, so no * further protection should be necessary. * * we implement a simple round robin style packet scheduler here. * each device has a list of cnnections ready to transmit packets, * and we loop through the connections until * a. the list becomes empty * b. the transmit time limit is reached. * if a connection has no more packets left or exceeds its window * of outbound packets, it is removed from the list. */ while ((active = this->list.ready) != NULL && ((bytes_sent < max_bytes) || this->hw.fast)) { unsigned short start; unsigned short end; struct sk_buff *skbn; ax25_cb *peer; int in_retransmit = 0; skbn = skb_peek(&active->ack_queue); /* transmit supervisory stuff first */ if (active->tx_rsp) { int poll_bit = active->tx_rsp & 0x100; int frametype = active->tx_rsp & 0x0ff; active->tx_rsp = 0; ax25_send_control(active, frametype, poll_bit, AX25_RESPONSE); /* * supervisory stuff is all done, clear state-change flag */ ax25_clr_cond(active, AX25_COND_STATE_CHANGE); if ((frametype & AX25_U) == AX25_S) { /* S frames carry NR */ active->ack_timer = 0; ax25_clr_cond(active, AX25_COND_ACK_PENDING); } } if (active->tx_cmd) { int poll_bit = active->tx_cmd & 0x100; int frametype = active->tx_cmd & 0x0ff; active->tx_cmd = 0; /* * a problem exists due to a race condition between linux' * packet-scheduler and the timer routine: a write timeout might * happen before the packet actually reaches the device and is copied * for transmission. our transmit routine will then grab the first * packet off the ack queue, put a header in front of the data and * queue it for transmission. now we have the obscure situation that * we have two packets in our transmit queue that share a single data * segment. this isn't bad by itself, but since the first * retransmitted frame will have the poll bit set and eventually will * carry an updated N(r), we modify the header of a yet unsent packet, * resulting in a protocol violation. * * we do the obvious thing to prevent this here: if the packet we * got from the ack queue is cloned, we make a private copy of the * data. */ if (poll_bit && skbn && frametype == AX25_RR && !(active->condition & (AX25_COND_PEER_RX_BUSY|AX25_COND_STATE_CHANGE)) && active->n2count < 4) { if (skb_cloned(skbn)) { skb = skb_copy(skbn, GFP_ATOMIC); } else skb = skb_clone(skbn, GFP_ATOMIC); if (skb) { active->vs = active->va; ax25_send_iframe(active, skb, AX25_POLLON); active->vs = active->vs_max; } } else { ax25_send_control(active, frametype, poll_bit, AX25_COMMAND); } /* * supervisory stuff is all done, clear state-change flag */ ax25_clr_cond(active, AX25_COND_STATE_CHANGE); if ((frametype & AX25_U) == AX25_S) { /* S frames carry NR */ active->ack_timer = 0; ax25_clr_cond(active, AX25_COND_ACK_PENDING); } } /* * if the write queue and ack queue are both empty, * or connection is not in info transfer state * or the peer station is busy * or the window is closed * or the write queue is empty and we may not retransmit yet * then remove connection from the devices' readylist; * * NOTE: ax25_dev_remove_active implicitly advances the * round robin pointer to schedule the next connection * on the readylist. */ skb = skb_peek(&active->write_queue); if ((skb == NULL && skbn == NULL) || active->state != AX25_STATE_3 || (active->condition & AX25_COND_PEER_RX_BUSY) != 0 || (start = active->vs) == (end = (active->va + active->window) & active->seqmask) || (skb == NULL && start != active->va)) { if (active->condition & AX25_COND_START_T1) { ax25_clr_cond(active, AX25_COND_START_T1); write_lock(&active->timer_lock); active->wrt_timer = active->t1 = ax25_calculate_t1(active); write_unlock(&active->timer_lock); } write_lock(&this->ready_lock); /* paranoia */ ax25_dev_remove_active(this); write_unlock(&this->ready_lock); continue; } /* * handle RTS/CTS handshaking. drivers can request TX-Delay * by returning 0 in the cts method. Note, that the driver still * has to handle handshaking itself, but it can prevent to be * flooded with frames while it's not ready to send. */ if (this->needs_transmit < AX25_TX_STATE_CTS) { if (this->hw.cts == NULL || this->hw.cts(this->netdev)) this->needs_transmit = AX25_TX_STATE_CTS; else if (this->needs_transmit == AX25_TX_STATE_RTS) this->needs_transmit = AX25_TX_STATE_WAIT_CTS; else break; } if (skbn != NULL && start == active->va) { skb = skbn; in_retransmit = 1; } /* * clone the buffer, put the original into the * ack_queue and transmit the copy. That way the * socket will be uncharged from the memory when * the packet is acked, not when it's transmitted. */ if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) break; /* advance pointer to current connection */ this->list.ready = active->ready.next; ax25_send_iframe(active, skbn, AX25_POLLOFF); if (!(DAMA_STATE(active) & DAMA_SLAVE)) { ax25_start_t1(active); } /* implicit ACK */ ax25_clr_cond(active, AX25_COND_ACK_PENDING); if (!in_retransmit) { active->vs_max = active->vs = (active->vs + 1) & active->seqmask; skb_dequeue(&active->write_queue); skb_queue_tail(&active->ack_queue, skb); if (active->vs_rtt == -1) { active->rtt_timestamp = jiffies; active->vs_rtt = active->vs; } this->tx_iframes++; this->tx_bytes += skbn->len; } else { active->vs = active->vs_max; if (active->condition & AX25_COND_START_T1) { ax25_clr_cond(active, AX25_COND_START_T1); write_lock(&active->timer_lock); active->wrt_timer = active->t1 = ax25_calculate_t1(active); write_unlock(&active->timer_lock); } ax25_dev_remove_ready(this, active); } bytes_sent += skbn->len; peer = active->peer; if (peer && (peer->condition & AX25_COND_OWN_RX_BUSY) && skb_queue_len(&active->write_queue) < 5) { ax25_clr_cond(peer, AX25_COND_OWN_RX_BUSY); ax25_set_cond(peer, AX25_COND_STATE_CHANGE); peer->state = AX25_STATE_4; ax25_transmit_enquiry(peer); } } this->bytes_sent += bytes_sent; if (this->list.ready == NULL) { this->bytes_sent = 0; this->needs_transmit = AX25_TX_STATE_IDLE; } else { if (this->bytes_sent > this->max_bytes) { this->bytes_sent = 0; ax25_dev_set_timer(this, HZ/2); } else ax25_dev_set_timer(this, AX25_TICS); } } /* * send a control frame */ static void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type) { struct sk_buff *skb; unsigned char *dptr; struct net_device *dev; if ((dev = ax25->device) == NULL) return; /* Route died */ if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_sizeof_addr(&ax25->addr) + 2, GFP_ATOMIC)) == NULL) return; skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_sizeof_addr(&ax25->addr)); /* Assume a response - address structure for DTE */ if (ax25->seqmask == AX25_SEQMASK) { dptr = skb_put(skb, 1); *dptr = frametype; *dptr |= (poll_bit) ? AX25_PF : 0; if ((frametype & AX25_U) == AX25_S) /* S frames carry NR */ *dptr |= (ax25->vr << 5); } else { if ((frametype & AX25_U) == AX25_U) { dptr = skb_put(skb, 1); *dptr = frametype; *dptr |= (poll_bit) ? AX25_PF : 0; } else { dptr = skb_put(skb, 2); dptr[0] = frametype; dptr[1] = (ax25->vr << 1); dptr[1] |= (poll_bit) ? AX25_EPF : 0; } } skb->nh.raw = skb->data; ax25_transmit_buffer(ax25, skb, type); ax25->vl = ax25->vr; /* vl: last acked frame */ } static void ax25_kick_device(struct ax25_dev* ax25_device) { write_lock(&ax25_dev_lock); if (!ax25_device->needs_transmit) { ax25_device->needs_transmit = AX25_TX_STATE_RTS; ax25_device->task_queue.routine = (void *) ax25_dev_timer; ax25_device->task_queue.data = (void *)ax25_device; ax25_device->task_queue.sync = 0; queue_task(&ax25_device->task_queue, &tq_immediate); mark_bh(IMMEDIATE_BH); } write_unlock(&ax25_dev_lock); } /* * This procedure is passed a buffer descriptor for an iframe. It builds * the rest of the control part of the frame and then writes it out. * */ static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit) { unsigned char *frame; skb->nh.raw = skb->data; if (ax25->seqmask == AX25_SEQMASK) { frame = skb_push(skb, 1); *frame = AX25_I; *frame |= (poll_bit) ? AX25_PF : 0; *frame |= (ax25->vr << 5); *frame |= (ax25->vs << 1); } else { frame = skb_push(skb, 2); frame[0] = AX25_I; frame[0] |= (ax25->vs << 1); frame[1] = (poll_bit) ? AX25_EPF : 0; frame[1] |= (ax25->vr << 1); } ax25->idletimer = ax25->idle; ax25_transmit_buffer(ax25, skb, AX25_COMMAND); ax25->vl = ax25->vr; /* vl: last acked frame */ } static void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type) { unsigned char *ptr; if (ax25->device == NULL) return; if (skb_headroom(skb) < ax25_sizeof_addr(&ax25->addr)) { printk(KERN_WARNING "ax25_transmit_buffer: not enough room for digi-peaters\n"); kfree_skb(skb); return; } ptr = skb_push(skb, ax25_sizeof_addr(&ax25->addr)); ax25_build_addr(ptr, &ax25->addr, type, ax25->seqmask); skb->dev = ax25->device; ax25_queue_xmit(skb); } /* ---------------------------------------------------------------------*/ /* A small shim to dev_queue_xmit to do any packet forwarding in operation. */ static void ax25_queue_xmit(struct sk_buff *skb) { skb->protocol = htons(ETH_P_AX25); skb->dev = ax25_fwd_dev(skb->dev); dev_queue_xmit(skb); } /* ---------------------------------------------------------------------*/ static struct ax25_dev *ax25_dev_get_dev(struct net_device *dev) { struct ax25_dev *ax25_device = AX25_PTR(dev); if (ax25_device == NULL) return NULL; if (ax25_device->magic == AX25_DEV_MAGIC) return ax25_device; return NULL; }