Release 4.11 net/irda/irttp.c
/*********************************************************************
*
* Filename: irttp.c
* Version: 1.2
* Description: Tiny Transport Protocol (TTP) implementation
* Status: Stable
* Author: Dag Brattli <dagb@cs.uit.no>
* Created at: Sun Aug 31 20:14:31 1997
* Modified at: Wed Jan 5 11:31:27 2000
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
* All Rights Reserved.
* Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
*
* 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.
*
* Neither Dag Brattli nor University of Tromsø admit liability nor
* provide warranty for any of this software. This material is
* provided "AS-IS" and at no charge.
*
********************************************************************/
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <asm/byteorder.h>
#include <asm/unaligned.h>
#include <net/irda/irda.h>
#include <net/irda/irlap.h>
#include <net/irda/irlmp.h>
#include <net/irda/parameters.h>
#include <net/irda/irttp.h>
static struct irttp_cb *irttp;
static void __irttp_close_tsap(struct tsap_cb *self);
static int irttp_data_indication(void *instance, void *sap,
struct sk_buff *skb);
static int irttp_udata_indication(void *instance, void *sap,
struct sk_buff *skb);
static void irttp_disconnect_indication(void *instance, void *sap,
LM_REASON reason, struct sk_buff *);
static void irttp_connect_indication(void *instance, void *sap,
struct qos_info *qos, __u32 max_sdu_size,
__u8 header_size, struct sk_buff *skb);
static void irttp_connect_confirm(void *instance, void *sap,
struct qos_info *qos, __u32 max_sdu_size,
__u8 header_size, struct sk_buff *skb);
static void irttp_run_tx_queue(struct tsap_cb *self);
static void irttp_run_rx_queue(struct tsap_cb *self);
static void irttp_flush_queues(struct tsap_cb *self);
static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb);
static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self);
static void irttp_todo_expired(unsigned long data);
static int irttp_param_max_sdu_size(void *instance, irda_param_t *param,
int get);
static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow);
static void irttp_status_indication(void *instance,
LINK_STATUS link, LOCK_STATUS lock);
/* Information for parsing parameters in IrTTP */
static const pi_minor_info_t pi_minor_call_table[] = {
{ NULL, 0 }, /* 0x00 */
{ irttp_param_max_sdu_size, PV_INTEGER | PV_BIG_ENDIAN } /* 0x01 */
};
static const pi_major_info_t pi_major_call_table[] = {
{ pi_minor_call_table, 2 }
};
static pi_param_info_t param_info = { pi_major_call_table, 1, 0x0f, 4 };
/************************ GLOBAL PROCEDURES ************************/
/*
* Function irttp_init (void)
*
* Initialize the IrTTP layer. Called by module initialization code
*
*/
int __init irttp_init(void)
{
irttp = kzalloc(sizeof(struct irttp_cb), GFP_KERNEL);
if (irttp == NULL)
return -ENOMEM;
irttp->magic = TTP_MAGIC;
irttp->tsaps = hashbin_new(HB_LOCK);
if (!irttp->tsaps) {
net_err_ratelimited("%s: can't allocate IrTTP hashbin!\n",
__func__);
kfree(irttp);
return -ENOMEM;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 63 | 85.14% | 2 | 25.00% |
Alexey Dobriyan | 5 | 6.76% | 1 | 12.50% |
Martin Dalecki | 2 | 2.70% | 1 | 12.50% |
Harvey Harrison | 1 | 1.35% | 1 | 12.50% |
Jean Tourrilhes | 1 | 1.35% | 1 | 12.50% |
Joe Perches | 1 | 1.35% | 1 | 12.50% |
Panagiotis Issaris | 1 | 1.35% | 1 | 12.50% |
Total | 74 | 100.00% | 8 | 100.00% |
/*
* Function irttp_cleanup (void)
*
* Called by module destruction/cleanup code
*
*/
void irttp_cleanup(void)
{
/* Check for main structure */
IRDA_ASSERT(irttp->magic == TTP_MAGIC, return;);
/*
* Delete hashbin and close all TSAP instances in it
*/
hashbin_delete(irttp->tsaps, (FREE_FUNC) __irttp_close_tsap);
irttp->magic = 0;
/* De-allocate main structure */
kfree(irttp);
irttp = NULL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 43 | 97.73% | 2 | 66.67% |
Jean Tourrilhes | 1 | 2.27% | 1 | 33.33% |
Total | 44 | 100.00% | 3 | 100.00% |
/*************************** SUBROUTINES ***************************/
/*
* Function irttp_start_todo_timer (self, timeout)
*
* Start todo timer.
*
* Made it more effient and unsensitive to race conditions - Jean II
*/
static inline void irttp_start_todo_timer(struct tsap_cb *self, int timeout)
{
/* Set new value for timer */
mod_timer(&self->todo_timer, jiffies + timeout);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jean Tourrilhes | 23 | 82.14% | 1 | 50.00% |
Linus Torvalds (pre-git) | 5 | 17.86% | 1 | 50.00% |
Total | 28 | 100.00% | 2 | 100.00% |
/*
* Function irttp_todo_expired (data)
*
* Todo timer has expired!
*
* One of the restriction of the timer is that it is run only on the timer
* interrupt which run every 10ms. This mean that even if you set the timer
* with a delay of 0, it may take up to 10ms before it's run.
* So, to minimise latency and keep cache fresh, we try to avoid using
* it as much as possible.
* Note : we can't use tasklets, because they can't be asynchronously
* killed (need user context), and we can't guarantee that here...
* Jean II
*/
static void irttp_todo_expired(unsigned long data)
{
struct tsap_cb *self = (struct tsap_cb *) data;
/* Check that we still exist */
if (!self || self->magic != TTP_TSAP_MAGIC)
return;
pr_debug("%s(instance=%p)\n", __func__, self);
/* Try to make some progress, especially on Tx side - Jean II */
irttp_run_rx_queue(self);
irttp_run_tx_queue(self);
/* Check if time for disconnect */
if (test_bit(0, &self->disconnect_pend)) {
/* Check if it's possible to disconnect yet */
if (skb_queue_empty(&self->tx_queue)) {
/* Make sure disconnect is not pending anymore */
clear_bit(0, &self->disconnect_pend); /* FALSE */
/* Note : self->disconnect_skb may be NULL */
irttp_disconnect_request(self, self->disconnect_skb,
P_NORMAL);
self->disconnect_skb = NULL;
} else {
/* Try again later */
irttp_start_todo_timer(self, HZ/10);
/* No reason to try and close now */
return;
}
}
/* Check if it's closing time */
if (self->close_pend)
/* Finish cleanup */
irttp_close_tsap(self);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jean Tourrilhes | 94 | 66.67% | 2 | 25.00% |
Linus Torvalds (pre-git) | 38 | 26.95% | 3 | 37.50% |
Linus Torvalds | 7 | 4.96% | 1 | 12.50% |
Joe Perches | 1 | 0.71% | 1 | 12.50% |
Harvey Harrison | 1 | 0.71% | 1 | 12.50% |
Total | 141 | 100.00% | 8 | 100.00% |
/*
* Function irttp_flush_queues (self)
*
* Flushes (removes all frames) in transitt-buffer (tx_list)
*/
static void irttp_flush_queues(struct tsap_cb *self)
{
struct sk_buff *skb;
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
/* Deallocate frames waiting to be sent */
while ((skb = skb_dequeue(&self->tx_queue)) != NULL)
dev_kfree_skb(skb);
/* Deallocate received frames */
while ((skb = skb_dequeue(&self->rx_queue)) != NULL)
dev_kfree_skb(skb);
/* Deallocate received fragments */
while ((skb = skb_dequeue(&self->rx_fragments)) != NULL)
dev_kfree_skb(skb);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jean Tourrilhes | 64 | 66.67% | 2 | 40.00% |
Linus Torvalds (pre-git) | 31 | 32.29% | 2 | 40.00% |
Roel Kluin | 1 | 1.04% | 1 | 20.00% |
Total | 96 | 100.00% | 5 | 100.00% |
/*
* Function irttp_reassemble (self)
*
* Makes a new (continuous) skb of all the fragments in the fragment
* queue
*
*/
static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self)
{
struct sk_buff *skb, *frag;
int n = 0; /* Fragment index */
IRDA_ASSERT(self != NULL, return NULL;);
IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return NULL;);
pr_debug("%s(), self->rx_sdu_size=%d\n", __func__,
self->rx_sdu_size);
skb = dev_alloc_skb(TTP_HEADER + self->rx_sdu_size);
if (!skb)
return NULL;
/*
* Need to reserve space for TTP header in case this skb needs to
* be requeued in case delivery failes
*/
skb_reserve(skb, TTP_HEADER);
skb_put(skb, self->rx_sdu_size);
/*
* Copy all fragments to a new buffer
*/
while ((frag = skb_dequeue(&self->rx_fragments)) != NULL) {
skb_copy_to_linear_data_offset(skb, n, frag->data, frag->len);
n += frag->len;
dev_kfree_skb(frag);
}
pr_debug("%s(), frame len=%d, rx_sdu_size=%d, rx_max_sdu_size=%d\n",
__func__, n, self->rx_sdu_size, self->rx_max_sdu_size);
/* Note : irttp_run_rx_queue() calculate self->rx_sdu_size
* by summing the size of all fragments, so we should always
* have n == self->rx_sdu_size, except in cases where we
* droped the last fragment (when self->rx_sdu_size exceed
* self->rx_max_sdu_size), where n < self->rx_sdu_size.
* Jean II */
IRDA_ASSERT(n <= self->rx_sdu_size, n = self->rx_sdu_size;);
/* Set the new length */
skb_trim(skb, n);
self->rx_sdu_size = 0;
return skb;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jean Tourrilhes | 125 | 71.43% | 4 | 40.00% |
Linus Torvalds (pre-git) | 35 | 20.00% | 1 | 10.00% |
Linus Torvalds | 8 | 4.57% | 1 | 10.00% |
Joe Perches | 2 | 1.14% | 1 | 10.00% |
Arnaldo Carvalho de Melo | 2 | 1.14% | 1 | 10.00% |
Harvey Harrison | 2 | 1.14% | 1 | 10.00% |
Martin Dalecki | 1 | 0.57% | 1 | 10.00% |
Total | 175 | 100.00% | 10 | 100.00% |
/*
* Function irttp_fragment_skb (skb)
*
* Fragments a frame and queues all the fragments for transmission
*
*/
static inline void irttp_fragment_skb(struct tsap_cb *self,
struct sk_buff *skb)
{
struct sk_buff *frag;
__u8 *frame;
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
/*
* Split frame into a number of segments
*/
while (skb->len > self->max_seg_size) {
pr_debug("%s(), fragmenting ...\n", __func__);
/* Make new segment */
frag = alloc_skb(self->max_seg_size+self->max_header_size,
GFP_ATOMIC);
if (!frag)
return;
skb_reserve(frag, self->max_header_size);
/* Copy data from the original skb into this fragment. */
skb_copy_from_linear_data(skb, skb_put(frag, self->max_seg_size),
self->max_seg_size);
/* Insert TTP header, with the more bit set */
frame = skb_push(frag, TTP_HEADER);
frame[0] = TTP_MORE;
/* Hide the copied data from the original skb */
skb_pull(skb, self->max_seg_size);
/* Queue fragment */
skb_queue_tail(&self->tx_queue, frag);
}
/* Queue what is left of the original skb */
pr_debug("%s(), queuing last segment\n", __func__);
frame = skb_push(skb, TTP_HEADER);
frame[0] = 0x00; /* Clear more bit */
/* Queue fragment */
skb_queue_tail(&self->tx_queue, skb);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Jean Tourrilhes | 118 | 61.78% | 3 | 30.00% |
Linus Torvalds (pre-git) | 61 | 31.94% | 2 | 20.00% |
Samuel Ortiz | 3 | 1.57% | 1 | 10.00% |
Arnaldo Carvalho de Melo | 3 | 1.57% | 1 | 10.00% |
Linus Torvalds | 2 | 1.05% | 1 | 10.00% |
Joe Perches | 2 | 1.05% | 1 | 10.00% |
Harvey Harrison | 2 | 1.05% | 1 | 10.00% |
Total | 191 | 100.00% | 10 | 100.00% |
/*
* Function irttp_param_max_sdu_size (self, param)
*
* Handle the MaxSduSize parameter in the connect frames, this function
* will be called both when this parameter needs to be inserted into, and
* extracted from the connect frames
*/
static int irttp_param_max_sdu_size(void *instance, irda_param_t *param,
int get)
{
struct tsap_cb *self;
self = instance;
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
if (get)
param->pv.i = self->tx_max_sdu_size;
else
self->tx_max_sdu_size =