162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * LAPB release 002 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This code REQUIRES 2.1.15 or higher/ NET3.038 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * History 862306a36Sopenharmony_ci * LAPB 001 Jonathan Naylor Started Coding 962306a36Sopenharmony_ci * LAPB 002 Jonathan Naylor New timer architecture. 1062306a36Sopenharmony_ci * 2000-10-29 Henner Eisen lapb_data_indication() return status. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/types.h> 1862306a36Sopenharmony_ci#include <linux/socket.h> 1962306a36Sopenharmony_ci#include <linux/in.h> 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/jiffies.h> 2262306a36Sopenharmony_ci#include <linux/timer.h> 2362306a36Sopenharmony_ci#include <linux/string.h> 2462306a36Sopenharmony_ci#include <linux/sockios.h> 2562306a36Sopenharmony_ci#include <linux/net.h> 2662306a36Sopenharmony_ci#include <linux/inet.h> 2762306a36Sopenharmony_ci#include <linux/if_arp.h> 2862306a36Sopenharmony_ci#include <linux/skbuff.h> 2962306a36Sopenharmony_ci#include <linux/slab.h> 3062306a36Sopenharmony_ci#include <net/sock.h> 3162306a36Sopenharmony_ci#include <linux/uaccess.h> 3262306a36Sopenharmony_ci#include <linux/fcntl.h> 3362306a36Sopenharmony_ci#include <linux/mm.h> 3462306a36Sopenharmony_ci#include <linux/interrupt.h> 3562306a36Sopenharmony_ci#include <linux/stat.h> 3662306a36Sopenharmony_ci#include <linux/init.h> 3762306a36Sopenharmony_ci#include <net/lapb.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic LIST_HEAD(lapb_list); 4062306a36Sopenharmony_cistatic DEFINE_RWLOCK(lapb_list_lock); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * Free an allocated lapb control block. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_cistatic void lapb_free_cb(struct lapb_cb *lapb) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci kfree(lapb); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic __inline__ void lapb_hold(struct lapb_cb *lapb) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci refcount_inc(&lapb->refcnt); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic __inline__ void lapb_put(struct lapb_cb *lapb) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci if (refcount_dec_and_test(&lapb->refcnt)) 5862306a36Sopenharmony_ci lapb_free_cb(lapb); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* 6262306a36Sopenharmony_ci * Socket removal during an interrupt is now safe. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistatic void __lapb_remove_cb(struct lapb_cb *lapb) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci if (lapb->node.next) { 6762306a36Sopenharmony_ci list_del(&lapb->node); 6862306a36Sopenharmony_ci lapb_put(lapb); 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* 7362306a36Sopenharmony_ci * Add a socket to the bound sockets list. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_cistatic void __lapb_insert_cb(struct lapb_cb *lapb) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci list_add(&lapb->node, &lapb_list); 7862306a36Sopenharmony_ci lapb_hold(lapb); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic struct lapb_cb *__lapb_devtostruct(struct net_device *dev) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct lapb_cb *lapb, *use = NULL; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci list_for_each_entry(lapb, &lapb_list, node) { 8662306a36Sopenharmony_ci if (lapb->dev == dev) { 8762306a36Sopenharmony_ci use = lapb; 8862306a36Sopenharmony_ci break; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (use) 9362306a36Sopenharmony_ci lapb_hold(use); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return use; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic struct lapb_cb *lapb_devtostruct(struct net_device *dev) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct lapb_cb *rc; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci read_lock_bh(&lapb_list_lock); 10362306a36Sopenharmony_ci rc = __lapb_devtostruct(dev); 10462306a36Sopenharmony_ci read_unlock_bh(&lapb_list_lock); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return rc; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci/* 10962306a36Sopenharmony_ci * Create an empty LAPB control block. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_cistatic struct lapb_cb *lapb_create_cb(void) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct lapb_cb *lapb = kzalloc(sizeof(*lapb), GFP_ATOMIC); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (!lapb) 11662306a36Sopenharmony_ci goto out; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci skb_queue_head_init(&lapb->write_queue); 11962306a36Sopenharmony_ci skb_queue_head_init(&lapb->ack_queue); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci timer_setup(&lapb->t1timer, NULL, 0); 12262306a36Sopenharmony_ci timer_setup(&lapb->t2timer, NULL, 0); 12362306a36Sopenharmony_ci lapb->t1timer_running = false; 12462306a36Sopenharmony_ci lapb->t2timer_running = false; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci lapb->t1 = LAPB_DEFAULT_T1; 12762306a36Sopenharmony_ci lapb->t2 = LAPB_DEFAULT_T2; 12862306a36Sopenharmony_ci lapb->n2 = LAPB_DEFAULT_N2; 12962306a36Sopenharmony_ci lapb->mode = LAPB_DEFAULT_MODE; 13062306a36Sopenharmony_ci lapb->window = LAPB_DEFAULT_WINDOW; 13162306a36Sopenharmony_ci lapb->state = LAPB_STATE_0; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci spin_lock_init(&lapb->lock); 13462306a36Sopenharmony_ci refcount_set(&lapb->refcnt, 1); 13562306a36Sopenharmony_ciout: 13662306a36Sopenharmony_ci return lapb; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ciint lapb_register(struct net_device *dev, 14062306a36Sopenharmony_ci const struct lapb_register_struct *callbacks) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct lapb_cb *lapb; 14362306a36Sopenharmony_ci int rc = LAPB_BADTOKEN; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci write_lock_bh(&lapb_list_lock); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci lapb = __lapb_devtostruct(dev); 14862306a36Sopenharmony_ci if (lapb) { 14962306a36Sopenharmony_ci lapb_put(lapb); 15062306a36Sopenharmony_ci goto out; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci lapb = lapb_create_cb(); 15462306a36Sopenharmony_ci rc = LAPB_NOMEM; 15562306a36Sopenharmony_ci if (!lapb) 15662306a36Sopenharmony_ci goto out; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci lapb->dev = dev; 15962306a36Sopenharmony_ci lapb->callbacks = callbacks; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci __lapb_insert_cb(lapb); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci lapb_start_t1timer(lapb); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci rc = LAPB_OK; 16662306a36Sopenharmony_ciout: 16762306a36Sopenharmony_ci write_unlock_bh(&lapb_list_lock); 16862306a36Sopenharmony_ci return rc; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ciEXPORT_SYMBOL(lapb_register); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciint lapb_unregister(struct net_device *dev) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct lapb_cb *lapb; 17562306a36Sopenharmony_ci int rc = LAPB_BADTOKEN; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci write_lock_bh(&lapb_list_lock); 17862306a36Sopenharmony_ci lapb = __lapb_devtostruct(dev); 17962306a36Sopenharmony_ci if (!lapb) 18062306a36Sopenharmony_ci goto out; 18162306a36Sopenharmony_ci lapb_put(lapb); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Wait for other refs to "lapb" to drop */ 18462306a36Sopenharmony_ci while (refcount_read(&lapb->refcnt) > 2) 18562306a36Sopenharmony_ci usleep_range(1, 10); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci spin_lock_bh(&lapb->lock); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci lapb_stop_t1timer(lapb); 19062306a36Sopenharmony_ci lapb_stop_t2timer(lapb); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci lapb_clear_queues(lapb); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci spin_unlock_bh(&lapb->lock); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* Wait for running timers to stop */ 19762306a36Sopenharmony_ci del_timer_sync(&lapb->t1timer); 19862306a36Sopenharmony_ci del_timer_sync(&lapb->t2timer); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci __lapb_remove_cb(lapb); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci lapb_put(lapb); 20362306a36Sopenharmony_ci rc = LAPB_OK; 20462306a36Sopenharmony_ciout: 20562306a36Sopenharmony_ci write_unlock_bh(&lapb_list_lock); 20662306a36Sopenharmony_ci return rc; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ciEXPORT_SYMBOL(lapb_unregister); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ciint lapb_getparms(struct net_device *dev, struct lapb_parms_struct *parms) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci int rc = LAPB_BADTOKEN; 21362306a36Sopenharmony_ci struct lapb_cb *lapb = lapb_devtostruct(dev); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (!lapb) 21662306a36Sopenharmony_ci goto out; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci spin_lock_bh(&lapb->lock); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci parms->t1 = lapb->t1 / HZ; 22162306a36Sopenharmony_ci parms->t2 = lapb->t2 / HZ; 22262306a36Sopenharmony_ci parms->n2 = lapb->n2; 22362306a36Sopenharmony_ci parms->n2count = lapb->n2count; 22462306a36Sopenharmony_ci parms->state = lapb->state; 22562306a36Sopenharmony_ci parms->window = lapb->window; 22662306a36Sopenharmony_ci parms->mode = lapb->mode; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (!timer_pending(&lapb->t1timer)) 22962306a36Sopenharmony_ci parms->t1timer = 0; 23062306a36Sopenharmony_ci else 23162306a36Sopenharmony_ci parms->t1timer = (lapb->t1timer.expires - jiffies) / HZ; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (!timer_pending(&lapb->t2timer)) 23462306a36Sopenharmony_ci parms->t2timer = 0; 23562306a36Sopenharmony_ci else 23662306a36Sopenharmony_ci parms->t2timer = (lapb->t2timer.expires - jiffies) / HZ; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci spin_unlock_bh(&lapb->lock); 23962306a36Sopenharmony_ci lapb_put(lapb); 24062306a36Sopenharmony_ci rc = LAPB_OK; 24162306a36Sopenharmony_ciout: 24262306a36Sopenharmony_ci return rc; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ciEXPORT_SYMBOL(lapb_getparms); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciint lapb_setparms(struct net_device *dev, struct lapb_parms_struct *parms) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci int rc = LAPB_BADTOKEN; 24962306a36Sopenharmony_ci struct lapb_cb *lapb = lapb_devtostruct(dev); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (!lapb) 25262306a36Sopenharmony_ci goto out; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci spin_lock_bh(&lapb->lock); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci rc = LAPB_INVALUE; 25762306a36Sopenharmony_ci if (parms->t1 < 1 || parms->t2 < 1 || parms->n2 < 1) 25862306a36Sopenharmony_ci goto out_put; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (lapb->state == LAPB_STATE_0) { 26162306a36Sopenharmony_ci if (parms->mode & LAPB_EXTENDED) { 26262306a36Sopenharmony_ci if (parms->window < 1 || parms->window > 127) 26362306a36Sopenharmony_ci goto out_put; 26462306a36Sopenharmony_ci } else { 26562306a36Sopenharmony_ci if (parms->window < 1 || parms->window > 7) 26662306a36Sopenharmony_ci goto out_put; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci lapb->mode = parms->mode; 26962306a36Sopenharmony_ci lapb->window = parms->window; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci lapb->t1 = parms->t1 * HZ; 27362306a36Sopenharmony_ci lapb->t2 = parms->t2 * HZ; 27462306a36Sopenharmony_ci lapb->n2 = parms->n2; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci rc = LAPB_OK; 27762306a36Sopenharmony_ciout_put: 27862306a36Sopenharmony_ci spin_unlock_bh(&lapb->lock); 27962306a36Sopenharmony_ci lapb_put(lapb); 28062306a36Sopenharmony_ciout: 28162306a36Sopenharmony_ci return rc; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ciEXPORT_SYMBOL(lapb_setparms); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ciint lapb_connect_request(struct net_device *dev) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct lapb_cb *lapb = lapb_devtostruct(dev); 28862306a36Sopenharmony_ci int rc = LAPB_BADTOKEN; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (!lapb) 29162306a36Sopenharmony_ci goto out; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci spin_lock_bh(&lapb->lock); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci rc = LAPB_OK; 29662306a36Sopenharmony_ci if (lapb->state == LAPB_STATE_1) 29762306a36Sopenharmony_ci goto out_put; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci rc = LAPB_CONNECTED; 30062306a36Sopenharmony_ci if (lapb->state == LAPB_STATE_3 || lapb->state == LAPB_STATE_4) 30162306a36Sopenharmony_ci goto out_put; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci lapb_establish_data_link(lapb); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci lapb_dbg(0, "(%p) S0 -> S1\n", lapb->dev); 30662306a36Sopenharmony_ci lapb->state = LAPB_STATE_1; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci rc = LAPB_OK; 30962306a36Sopenharmony_ciout_put: 31062306a36Sopenharmony_ci spin_unlock_bh(&lapb->lock); 31162306a36Sopenharmony_ci lapb_put(lapb); 31262306a36Sopenharmony_ciout: 31362306a36Sopenharmony_ci return rc; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ciEXPORT_SYMBOL(lapb_connect_request); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int __lapb_disconnect_request(struct lapb_cb *lapb) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci switch (lapb->state) { 32062306a36Sopenharmony_ci case LAPB_STATE_0: 32162306a36Sopenharmony_ci return LAPB_NOTCONNECTED; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci case LAPB_STATE_1: 32462306a36Sopenharmony_ci lapb_dbg(1, "(%p) S1 TX DISC(1)\n", lapb->dev); 32562306a36Sopenharmony_ci lapb_dbg(0, "(%p) S1 -> S0\n", lapb->dev); 32662306a36Sopenharmony_ci lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND); 32762306a36Sopenharmony_ci lapb->state = LAPB_STATE_0; 32862306a36Sopenharmony_ci lapb_start_t1timer(lapb); 32962306a36Sopenharmony_ci return LAPB_NOTCONNECTED; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci case LAPB_STATE_2: 33262306a36Sopenharmony_ci return LAPB_OK; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci lapb_clear_queues(lapb); 33662306a36Sopenharmony_ci lapb->n2count = 0; 33762306a36Sopenharmony_ci lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND); 33862306a36Sopenharmony_ci lapb_start_t1timer(lapb); 33962306a36Sopenharmony_ci lapb_stop_t2timer(lapb); 34062306a36Sopenharmony_ci lapb->state = LAPB_STATE_2; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci lapb_dbg(1, "(%p) S3 DISC(1)\n", lapb->dev); 34362306a36Sopenharmony_ci lapb_dbg(0, "(%p) S3 -> S2\n", lapb->dev); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return LAPB_OK; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ciint lapb_disconnect_request(struct net_device *dev) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct lapb_cb *lapb = lapb_devtostruct(dev); 35162306a36Sopenharmony_ci int rc = LAPB_BADTOKEN; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (!lapb) 35462306a36Sopenharmony_ci goto out; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci spin_lock_bh(&lapb->lock); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci rc = __lapb_disconnect_request(lapb); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci spin_unlock_bh(&lapb->lock); 36162306a36Sopenharmony_ci lapb_put(lapb); 36262306a36Sopenharmony_ciout: 36362306a36Sopenharmony_ci return rc; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ciEXPORT_SYMBOL(lapb_disconnect_request); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ciint lapb_data_request(struct net_device *dev, struct sk_buff *skb) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct lapb_cb *lapb = lapb_devtostruct(dev); 37062306a36Sopenharmony_ci int rc = LAPB_BADTOKEN; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (!lapb) 37362306a36Sopenharmony_ci goto out; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci spin_lock_bh(&lapb->lock); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci rc = LAPB_NOTCONNECTED; 37862306a36Sopenharmony_ci if (lapb->state != LAPB_STATE_3 && lapb->state != LAPB_STATE_4) 37962306a36Sopenharmony_ci goto out_put; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci skb_queue_tail(&lapb->write_queue, skb); 38262306a36Sopenharmony_ci lapb_kick(lapb); 38362306a36Sopenharmony_ci rc = LAPB_OK; 38462306a36Sopenharmony_ciout_put: 38562306a36Sopenharmony_ci spin_unlock_bh(&lapb->lock); 38662306a36Sopenharmony_ci lapb_put(lapb); 38762306a36Sopenharmony_ciout: 38862306a36Sopenharmony_ci return rc; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ciEXPORT_SYMBOL(lapb_data_request); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ciint lapb_data_received(struct net_device *dev, struct sk_buff *skb) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct lapb_cb *lapb = lapb_devtostruct(dev); 39562306a36Sopenharmony_ci int rc = LAPB_BADTOKEN; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (lapb) { 39862306a36Sopenharmony_ci spin_lock_bh(&lapb->lock); 39962306a36Sopenharmony_ci lapb_data_input(lapb, skb); 40062306a36Sopenharmony_ci spin_unlock_bh(&lapb->lock); 40162306a36Sopenharmony_ci lapb_put(lapb); 40262306a36Sopenharmony_ci rc = LAPB_OK; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci return rc; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ciEXPORT_SYMBOL(lapb_data_received); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_civoid lapb_connect_confirmation(struct lapb_cb *lapb, int reason) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci if (lapb->callbacks->connect_confirmation) 41262306a36Sopenharmony_ci lapb->callbacks->connect_confirmation(lapb->dev, reason); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_civoid lapb_connect_indication(struct lapb_cb *lapb, int reason) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci if (lapb->callbacks->connect_indication) 41862306a36Sopenharmony_ci lapb->callbacks->connect_indication(lapb->dev, reason); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_civoid lapb_disconnect_confirmation(struct lapb_cb *lapb, int reason) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci if (lapb->callbacks->disconnect_confirmation) 42462306a36Sopenharmony_ci lapb->callbacks->disconnect_confirmation(lapb->dev, reason); 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_civoid lapb_disconnect_indication(struct lapb_cb *lapb, int reason) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci if (lapb->callbacks->disconnect_indication) 43062306a36Sopenharmony_ci lapb->callbacks->disconnect_indication(lapb->dev, reason); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ciint lapb_data_indication(struct lapb_cb *lapb, struct sk_buff *skb) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci if (lapb->callbacks->data_indication) 43662306a36Sopenharmony_ci return lapb->callbacks->data_indication(lapb->dev, skb); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci kfree_skb(skb); 43962306a36Sopenharmony_ci return NET_RX_SUCCESS; /* For now; must be != NET_RX_DROP */ 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ciint lapb_data_transmit(struct lapb_cb *lapb, struct sk_buff *skb) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci int used = 0; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (lapb->callbacks->data_transmit) { 44762306a36Sopenharmony_ci lapb->callbacks->data_transmit(lapb->dev, skb); 44862306a36Sopenharmony_ci used = 1; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return used; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/* Handle device status changes. */ 45562306a36Sopenharmony_cistatic int lapb_device_event(struct notifier_block *this, unsigned long event, 45662306a36Sopenharmony_ci void *ptr) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 45962306a36Sopenharmony_ci struct lapb_cb *lapb; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (!net_eq(dev_net(dev), &init_net)) 46262306a36Sopenharmony_ci return NOTIFY_DONE; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (dev->type != ARPHRD_X25) 46562306a36Sopenharmony_ci return NOTIFY_DONE; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci lapb = lapb_devtostruct(dev); 46862306a36Sopenharmony_ci if (!lapb) 46962306a36Sopenharmony_ci return NOTIFY_DONE; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci spin_lock_bh(&lapb->lock); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci switch (event) { 47462306a36Sopenharmony_ci case NETDEV_UP: 47562306a36Sopenharmony_ci lapb_dbg(0, "(%p) Interface up: %s\n", dev, dev->name); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (netif_carrier_ok(dev)) { 47862306a36Sopenharmony_ci lapb_dbg(0, "(%p): Carrier is already up: %s\n", dev, 47962306a36Sopenharmony_ci dev->name); 48062306a36Sopenharmony_ci if (lapb->mode & LAPB_DCE) { 48162306a36Sopenharmony_ci lapb_start_t1timer(lapb); 48262306a36Sopenharmony_ci } else { 48362306a36Sopenharmony_ci if (lapb->state == LAPB_STATE_0) { 48462306a36Sopenharmony_ci lapb->state = LAPB_STATE_1; 48562306a36Sopenharmony_ci lapb_establish_data_link(lapb); 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci break; 49062306a36Sopenharmony_ci case NETDEV_GOING_DOWN: 49162306a36Sopenharmony_ci if (netif_carrier_ok(dev)) 49262306a36Sopenharmony_ci __lapb_disconnect_request(lapb); 49362306a36Sopenharmony_ci break; 49462306a36Sopenharmony_ci case NETDEV_DOWN: 49562306a36Sopenharmony_ci lapb_dbg(0, "(%p) Interface down: %s\n", dev, dev->name); 49662306a36Sopenharmony_ci lapb_dbg(0, "(%p) S%d -> S0\n", dev, lapb->state); 49762306a36Sopenharmony_ci lapb_clear_queues(lapb); 49862306a36Sopenharmony_ci lapb->state = LAPB_STATE_0; 49962306a36Sopenharmony_ci lapb->n2count = 0; 50062306a36Sopenharmony_ci lapb_stop_t1timer(lapb); 50162306a36Sopenharmony_ci lapb_stop_t2timer(lapb); 50262306a36Sopenharmony_ci break; 50362306a36Sopenharmony_ci case NETDEV_CHANGE: 50462306a36Sopenharmony_ci if (netif_carrier_ok(dev)) { 50562306a36Sopenharmony_ci lapb_dbg(0, "(%p): Carrier detected: %s\n", dev, 50662306a36Sopenharmony_ci dev->name); 50762306a36Sopenharmony_ci if (lapb->mode & LAPB_DCE) { 50862306a36Sopenharmony_ci lapb_start_t1timer(lapb); 50962306a36Sopenharmony_ci } else { 51062306a36Sopenharmony_ci if (lapb->state == LAPB_STATE_0) { 51162306a36Sopenharmony_ci lapb->state = LAPB_STATE_1; 51262306a36Sopenharmony_ci lapb_establish_data_link(lapb); 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci } else { 51662306a36Sopenharmony_ci lapb_dbg(0, "(%p) Carrier lost: %s\n", dev, dev->name); 51762306a36Sopenharmony_ci lapb_dbg(0, "(%p) S%d -> S0\n", dev, lapb->state); 51862306a36Sopenharmony_ci lapb_clear_queues(lapb); 51962306a36Sopenharmony_ci lapb->state = LAPB_STATE_0; 52062306a36Sopenharmony_ci lapb->n2count = 0; 52162306a36Sopenharmony_ci lapb_stop_t1timer(lapb); 52262306a36Sopenharmony_ci lapb_stop_t2timer(lapb); 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci break; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci spin_unlock_bh(&lapb->lock); 52862306a36Sopenharmony_ci lapb_put(lapb); 52962306a36Sopenharmony_ci return NOTIFY_DONE; 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic struct notifier_block lapb_dev_notifier = { 53362306a36Sopenharmony_ci .notifier_call = lapb_device_event, 53462306a36Sopenharmony_ci}; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic int __init lapb_init(void) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci return register_netdevice_notifier(&lapb_dev_notifier); 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic void __exit lapb_exit(void) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci WARN_ON(!list_empty(&lapb_list)); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci unregister_netdevice_notifier(&lapb_dev_notifier); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ciMODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>"); 54962306a36Sopenharmony_ciMODULE_DESCRIPTION("The X.25 Link Access Procedure B link layer protocol"); 55062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cimodule_init(lapb_init); 55362306a36Sopenharmony_cimodule_exit(lapb_exit); 554