diff options
Diffstat (limited to 'net/irda/irttp.c')
-rw-r--r-- | net/irda/irttp.c | 1891 |
1 files changed, 0 insertions, 1891 deletions
diff --git a/net/irda/irttp.c b/net/irda/irttp.c deleted file mode 100644 index b6ab41d5b3a3..000000000000 --- a/net/irda/irttp.c +++ /dev/null @@ -1,1891 +0,0 @@ -/********************************************************************* - * - * 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; -} - -/* - * 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; -} - -/*************************** 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); -} - -/* - * 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); -} - -/* - * 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); -} - -/* - * 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; -} - -/* - * 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); -} - -/* - * 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 = param->pv.i; - - pr_debug("%s(), MaxSduSize=%d\n", __func__, param->pv.i); - - return 0; -} - -/*************************** CLIENT CALLS ***************************/ -/************************** LMP CALLBACKS **************************/ -/* Everything is happily mixed up. Waiting for next clean up - Jean II */ - -/* - * Initialization, that has to be done on new tsap - * instance allocation and on duplication - */ -static void irttp_init_tsap(struct tsap_cb *tsap) -{ - spin_lock_init(&tsap->lock); - init_timer(&tsap->todo_timer); - - skb_queue_head_init(&tsap->rx_queue); - skb_queue_head_init(&tsap->tx_queue); - skb_queue_head_init(&tsap->rx_fragments); -} - -/* - * Function irttp_open_tsap (stsap, notify) - * - * Create TSAP connection endpoint, - */ -struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify) -{ - struct tsap_cb *self; - struct lsap_cb *lsap; - notify_t ttp_notify; - - IRDA_ASSERT(irttp->magic == TTP_MAGIC, return NULL;); - - /* The IrLMP spec (IrLMP 1.1 p10) says that we have the right to - * use only 0x01-0x6F. Of course, we can use LSAP_ANY as well. - * JeanII */ - if ((stsap_sel != LSAP_ANY) && - ((stsap_sel < 0x01) || (stsap_sel >= 0x70))) { - pr_debug("%s(), invalid tsap!\n", __func__); - return NULL; - } - - self = kzalloc(sizeof(struct tsap_cb), GFP_ATOMIC); - if (self == NULL) - return NULL; - - /* Initialize internal objects */ - irttp_init_tsap(self); - - /* Initialise todo timer */ - self->todo_timer.data = (unsigned long) self; - self->todo_timer.function = &irttp_todo_expired; - - /* Initialize callbacks for IrLMP to use */ - irda_notify_init(&ttp_notify); - ttp_notify.connect_confirm = irttp_connect_confirm; - ttp_notify.connect_indication = irttp_connect_indication; - ttp_notify.disconnect_indication = irttp_disconnect_indication; - ttp_notify.data_indication = irttp_data_indication; - ttp_notify.udata_indication = irttp_udata_indication; - ttp_notify.flow_indication = irttp_flow_indication; - if (notify->status_indication != NULL) - ttp_notify.status_indication = irttp_status_indication; - ttp_notify.instance = self; - strncpy(ttp_notify.name, notify->name, NOTIFY_MAX_NAME); - - self->magic = TTP_TSAP_MAGIC; - self->connected = FALSE; - - /* - * Create LSAP at IrLMP layer - */ - lsap = irlmp_open_lsap(stsap_sel, &ttp_notify, 0); - if (lsap == NULL) { - pr_debug("%s: unable to allocate LSAP!!\n", __func__); - __irttp_close_tsap(self); - return NULL; - } - - /* - * If user specified LSAP_ANY as source TSAP selector, then IrLMP - * will replace it with whatever source selector which is free, so - * the stsap_sel we have might not be valid anymore - */ - self->stsap_sel = lsap->slsap_sel; - pr_debug("%s(), stsap_sel=%02x\n", __func__, self->stsap_sel); - - self->notify = *notify; - self->lsap = lsap; - - hashbin_insert(irttp->tsaps, (irda_queue_t *) self, (long) self, NULL); - - if (credit > TTP_RX_MAX_CREDIT) - self->initial_credit = TTP_RX_MAX_CREDIT; - else - self->initial_credit = credit; - - return self; -} -EXPORT_SYMBOL(irttp_open_tsap); - -/* - * Function irttp_close (handle) - * - * Remove an instance of a TSAP. This function should only deal with the - * deallocation of the TSAP, and resetting of the TSAPs values; - * - */ -static void __irttp_close_tsap(struct tsap_cb *self) -{ - /* First make sure we're connected. */ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - irttp_flush_queues(self); - - del_timer(&self->todo_timer); - - /* This one won't be cleaned up if we are disconnect_pend + close_pend - * and we receive a disconnect_indication */ - if (self->disconnect_skb) - dev_kfree_skb(self->disconnect_skb); - - self->connected = FALSE; - self->magic = ~TTP_TSAP_MAGIC; - - kfree(self); -} - -/* - * Function irttp_close (self) - * - * Remove TSAP from list of all TSAPs and then deallocate all resources - * associated with this TSAP - * - * Note : because we *free* the tsap structure, it is the responsibility - * of the caller to make sure we are called only once and to deal with - * possible race conditions. - Jean II - */ -int irttp_close_tsap(struct tsap_cb *self) -{ - struct tsap_cb *tsap; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - - /* Make sure tsap has been disconnected */ - if (self->connected) { - /* Check if disconnect is not pending */ - if (!test_bit(0, &self->disconnect_pend)) { - net_warn_ratelimited("%s: TSAP still connected!\n", - __func__); - irttp_disconnect_request(self, NULL, P_NORMAL); - } - self->close_pend = TRUE; - irttp_start_todo_timer(self, HZ/10); - - return 0; /* Will be back! */ - } - - tsap = hashbin_remove(irttp->tsaps, (long) self, NULL); - - IRDA_ASSERT(tsap == self, return -1;); - - /* Close corresponding LSAP */ - if (self->lsap) { - irlmp_close_lsap(self->lsap); - self->lsap = NULL; - } - - __irttp_close_tsap(self); - - return 0; -} -EXPORT_SYMBOL(irttp_close_tsap); - -/* - * Function irttp_udata_request (self, skb) - * - * Send unreliable data on this TSAP - * - */ -int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - /* Take shortcut on zero byte packets */ - if (skb->len == 0) { - ret = 0; - goto err; - } - - /* Check that nothing bad happens */ - if (!self->connected) { - net_warn_ratelimited("%s(), Not connected\n", __func__); - ret = -ENOTCONN; - goto err; - } - - if (skb->len > self->max_seg_size) { - net_err_ratelimited("%s(), UData is too large for IrLAP!\n", - __func__); - ret = -EMSGSIZE; - goto err; - } - - irlmp_udata_request(self->lsap, skb); - self->stats.tx_packets++; - - return 0; - -err: - dev_kfree_skb(skb); - return ret; -} -EXPORT_SYMBOL(irttp_udata_request); - - -/* - * Function irttp_data_request (handle, skb) - * - * Queue frame for transmission. If SAR is enabled, fragement the frame - * and queue the fragments for transmission - */ -int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb) -{ - __u8 *frame; - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - pr_debug("%s() : queue len = %d\n", __func__, - skb_queue_len(&self->tx_queue)); - - /* Take shortcut on zero byte packets */ - if (skb->len == 0) { - ret = 0; - goto err; - } - - /* Check that nothing bad happens */ - if (!self->connected) { - net_warn_ratelimited("%s: Not connected\n", __func__); - ret = -ENOTCONN; - goto err; - } - - /* - * Check if SAR is disabled, and the frame is larger than what fits - * inside an IrLAP frame - */ - if ((self->tx_max_sdu_size == 0) && (skb->len > self->max_seg_size)) { - net_err_ratelimited("%s: SAR disabled, and data is too large for IrLAP!\n", - __func__); - ret = -EMSGSIZE; - goto err; - } - - /* - * Check if SAR is enabled, and the frame is larger than the - * TxMaxSduSize - */ - if ((self->tx_max_sdu_size != 0) && - (self->tx_max_sdu_size != TTP_SAR_UNBOUND) && - (skb->len > self->tx_max_sdu_size)) { - net_err_ratelimited("%s: SAR enabled, but data is larger than TxMaxSduSize!\n", - __func__); - ret = -EMSGSIZE; - goto err; - } - /* - * Check if transmit queue is full - */ - if (skb_queue_len(&self->tx_queue) >= TTP_TX_MAX_QUEUE) { - /* - * Give it a chance to empty itself - */ - irttp_run_tx_queue(self); - - /* Drop packet. This error code should trigger the caller - * to resend the data in the client code - Jean II */ - ret = -ENOBUFS; - goto err; - } - - /* Queue frame, or queue frame segments */ - if ((self->tx_max_sdu_size == 0) || (skb->len < self->max_seg_size)) { - /* Queue frame */ - IRDA_ASSERT(skb_headroom(skb) >= TTP_HEADER, return -1;); - frame = skb_push(skb, TTP_HEADER); - frame[0] = 0x00; /* Clear more bit */ - - skb_queue_tail(&self->tx_queue, skb); - } else { - /* - * Fragment the frame, this function will also queue the - * fragments, we don't care about the fact the transmit - * queue may be overfilled by all the segments for a little - * while - */ - irttp_fragment_skb(self, skb); - } - - /* Check if we can accept more data from client */ - if ((!self->tx_sdu_busy) && - (skb_queue_len(&self->tx_queue) > TTP_TX_HIGH_THRESHOLD)) { - /* Tx queue filling up, so stop client. */ - if (self->notify.flow_indication) { - self->notify.flow_indication(self->notify.instance, - self, FLOW_STOP); - } - /* self->tx_sdu_busy is the state of the client. - * Update state after notifying client to avoid - * race condition with irttp_flow_indication(). - * If the queue empty itself after our test but before - * we set the flag, we will fix ourselves below in - * irttp_run_tx_queue(). - * Jean II */ - self->tx_sdu_busy = TRUE; - } - - /* Try to make some progress */ - irttp_run_tx_queue(self); - - return 0; - -err: - dev_kfree_skb(skb); - return ret; -} -EXPORT_SYMBOL(irttp_data_request); - -/* - * Function irttp_run_tx_queue (self) - * - * Transmit packets queued for transmission (if possible) - * - */ -static void irttp_run_tx_queue(struct tsap_cb *self) -{ - struct sk_buff *skb; - unsigned long flags; - int n; - - pr_debug("%s() : send_credit = %d, queue_len = %d\n", - __func__, - self->send_credit, skb_queue_len(&self->tx_queue)); - - /* Get exclusive access to the tx queue, otherwise don't touch it */ - if (irda_lock(&self->tx_queue_lock) == FALSE) - return; - - /* Try to send out frames as long as we have credits - * and as long as LAP is not full. If LAP is full, it will - * poll us through irttp_flow_indication() - Jean II */ - while ((self->send_credit > 0) && - (!irlmp_lap_tx_queue_full(self->lsap)) && - (skb = skb_dequeue(&self->tx_queue))) { - /* - * Since we can transmit and receive frames concurrently, - * the code below is a critical region and we must assure that - * nobody messes with the credits while we update them. - */ - spin_lock_irqsave(&self->lock, flags); - - n = self->avail_credit; - self->avail_credit = 0; - - /* Only room for 127 credits in frame */ - if (n > 127) { - self->avail_credit = n-127; - n = 127; - } - self->remote_credit += n; - self->send_credit--; - - spin_unlock_irqrestore(&self->lock, flags); - - /* - * More bit must be set by the data_request() or fragment() - * functions - */ - skb->data[0] |= (n & 0x7f); - - /* Detach from socket. - * The current skb has a reference to the socket that sent - * it (skb->sk). When we pass it to IrLMP, the skb will be - * stored in in IrLAP (self->wx_list). When we are within - * IrLAP, we lose the notion of socket, so we should not - * have a reference to a socket. So, we drop it here. - * - * Why does it matter ? - * When the skb is freed (kfree_skb), if it is associated - * with a socket, it release buffer space on the socket - * (through sock_wfree() and sock_def_write_space()). - * If the socket no longer exist, we may crash. Hard. - * When we close a socket, we make sure that associated packets - * in IrTTP are freed. However, we have no way to cancel - * the packet that we have passed to IrLAP. So, if a packet - * remains in IrLAP (retry on the link or else) after we - * close the socket, we are dead ! - * Jean II */ - if (skb->sk != NULL) { - /* IrSOCK application, IrOBEX, ... */ - skb_orphan(skb); - } - /* IrCOMM over IrTTP, IrLAN, ... */ - - /* Pass the skb to IrLMP - done */ - irlmp_data_request(self->lsap, skb); - self->stats.tx_packets++; - } - - /* Check if we can accept more frames from client. - * We don't want to wait until the todo timer to do that, and we - * can't use tasklets (grr...), so we are obliged to give control - * to client. That's ok, this test will be true not too often - * (max once per LAP window) and we are called from places - * where we can spend a bit of time doing stuff. - Jean II */ - if ((self->tx_sdu_busy) && - (skb_queue_len(&self->tx_queue) < TTP_TX_LOW_THRESHOLD) && - (!self->close_pend)) { - if (self->notify.flow_indication) - self->notify.flow_indication(self->notify.instance, - self, FLOW_START); - - /* self->tx_sdu_busy is the state of the client. - * We don't really have a race here, but it's always safer - * to update our state after the client - Jean II */ - self->tx_sdu_busy = FALSE; - } - - /* Reset lock */ - self->tx_queue_lock = 0; -} - -/* - * Function irttp_give_credit (self) - * - * Send a dataless flowdata TTP-PDU and give available credit to peer - * TSAP - */ -static inline void irttp_give_credit(struct tsap_cb *self) -{ - struct sk_buff *tx_skb = NULL; - unsigned long flags; - int n; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - pr_debug("%s() send=%d,avail=%d,remote=%d\n", - __func__, - self->send_credit, self->avail_credit, self->remote_credit); - - /* Give credit to peer */ - tx_skb = alloc_skb(TTP_MAX_HEADER, GFP_ATOMIC); - if (!tx_skb) - return; - - /* Reserve space for LMP, and LAP header */ - skb_reserve(tx_skb, LMP_MAX_HEADER); - - /* - * Since we can transmit and receive frames concurrently, - * the code below is a critical region and we must assure that - * nobody messes with the credits while we update them. - */ - spin_lock_irqsave(&self->lock, flags); - - n = self->avail_credit; - self->avail_credit = 0; - - /* Only space for 127 credits in frame */ - if (n > 127) { - self->avail_credit = n - 127; - n = 127; - } - self->remote_credit += n; - - spin_unlock_irqrestore(&self->lock, flags); - - skb_put(tx_skb, 1); - tx_skb->data[0] = (__u8) (n & 0x7f); - - irlmp_data_request(self->lsap, tx_skb); - self->stats.tx_packets++; -} - -/* - * Function irttp_udata_indication (instance, sap, skb) - * - * Received some unit-data (unreliable) - * - */ -static int irttp_udata_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct tsap_cb *self; - int err; - - self = instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - self->stats.rx_packets++; - - /* Just pass data to layer above */ - if (self->notify.udata_indication) { - err = self->notify.udata_indication(self->notify.instance, - self, skb); - /* Same comment as in irttp_do_data_indication() */ - if (!err) - return 0; - } - /* Either no handler, or handler returns an error */ - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irttp_data_indication (instance, sap, skb) - * - * Receive segment from IrLMP. - * - */ -static int irttp_data_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct tsap_cb *self; - unsigned long flags; - int n; - - self = instance; - - n = skb->data[0] & 0x7f; /* Extract the credits */ - - self->stats.rx_packets++; - - /* Deal with inbound credit - * Since we can transmit and receive frames concurrently, - * the code below is a critical region and we must assure that - * nobody messes with the credits while we update them. - */ - spin_lock_irqsave(&self->lock, flags); - self->send_credit += n; - if (skb->len > 1) - self->remote_credit--; - spin_unlock_irqrestore(&self->lock, flags); - - /* - * Data or dataless packet? Dataless frames contains only the - * TTP_HEADER. - */ - if (skb->len > 1) { - /* - * We don't remove the TTP header, since we must preserve the - * more bit, so the defragment routing knows what to do - */ - skb_queue_tail(&self->rx_queue, skb); - } else { - /* Dataless flowdata TTP-PDU */ - dev_kfree_skb(skb); - } - - - /* Push data to the higher layer. - * We do it synchronously because running the todo timer for each - * receive packet would be too much overhead and latency. - * By passing control to the higher layer, we run the risk that - * it may take time or grab a lock. Most often, the higher layer - * will only put packet in a queue. - * Anyway, packets are only dripping through the IrDA, so we can - * have time before the next packet. - * Further, we are run from NET_BH, so the worse that can happen is - * us missing the optimal time to send back the PF bit in LAP. - * Jean II */ - irttp_run_rx_queue(self); - - /* We now give credits to peer in irttp_run_rx_queue(). - * We need to send credit *NOW*, otherwise we are going - * to miss the next Tx window. The todo timer may take - * a while before it's run... - Jean II */ - - /* - * If the peer device has given us some credits and we didn't have - * anyone from before, then we need to shedule the tx queue. - * We need to do that because our Tx have stopped (so we may not - * get any LAP flow indication) and the user may be stopped as - * well. - Jean II - */ - if (self->send_credit == n) { - /* Restart pushing stuff to LAP */ - irttp_run_tx_queue(self); - /* Note : we don't want to schedule the todo timer - * because it has horrible latency. No tasklets - * because the tasklet API is broken. - Jean II */ - } - - return 0; -} - -/* - * Function irttp_status_indication (self, reason) - * - * Status_indication, just pass to the higher layer... - * - */ -static void irttp_status_indication(void *instance, - LINK_STATUS link, LOCK_STATUS lock) -{ - struct tsap_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - /* Check if client has already closed the TSAP and gone away */ - if (self->close_pend) - return; - - /* - * Inform service user if he has requested it - */ - if (self->notify.status_indication != NULL) - self->notify.status_indication(self->notify.instance, - link, lock); - else - pr_debug("%s(), no handler\n", __func__); -} - -/* - * Function irttp_flow_indication (self, reason) - * - * Flow_indication : IrLAP tells us to send more data. - * - */ -static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) -{ - struct tsap_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - pr_debug("%s(instance=%p)\n", __func__, self); - - /* We are "polled" directly from LAP, and the LAP want to fill - * its Tx window. We want to do our best to send it data, so that - * we maximise the window. On the other hand, we want to limit the - * amount of work here so that LAP doesn't hang forever waiting - * for packets. - Jean II */ - - /* Try to send some packets. Currently, LAP calls us every time - * there is one free slot, so we will send only one packet. - * This allow the scheduler to do its round robin - Jean II */ - irttp_run_tx_queue(self); - - /* Note regarding the interraction with higher layer. - * irttp_run_tx_queue() may call the client when its queue - * start to empty, via notify.flow_indication(). Initially. - * I wanted this to happen in a tasklet, to avoid client - * grabbing the CPU, but we can't use tasklets safely. And timer - * is definitely too slow. - * This will happen only once per LAP window, and usually at - * the third packet (unless window is smaller). LAP is still - * doing mtt and sending first packet so it's sort of OK - * to do that. Jean II */ - - /* If we need to send disconnect. try to do it now */ - if (self->disconnect_pend) - irttp_start_todo_timer(self, 0); -} - -/* - * Function irttp_flow_request (self, command) - * - * This function could be used by the upper layers to tell IrTTP to stop - * delivering frames if the receive queues are starting to get full, or - * to tell IrTTP to start delivering frames again. - */ -void irttp_flow_request(struct tsap_cb *self, LOCAL_FLOW flow) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - switch (flow) { - case FLOW_STOP: - pr_debug("%s(), flow stop\n", __func__); - self->rx_sdu_busy = TRUE; - break; - case FLOW_START: - pr_debug("%s(), flow start\n", __func__); - self->rx_sdu_busy = FALSE; - - /* Client say he can accept more data, try to free our - * queues ASAP - Jean II */ - irttp_run_rx_queue(self); - - break; - default: - pr_debug("%s(), Unknown flow command!\n", __func__); - } -} -EXPORT_SYMBOL(irttp_flow_request); - -/* - * Function irttp_connect_request (self, dtsap_sel, daddr, qos) - * - * Try to connect to remote destination TSAP selector - * - */ -int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel, - __u32 saddr, __u32 daddr, - struct qos_info *qos, __u32 max_sdu_size, - struct sk_buff *userdata) -{ - struct sk_buff *tx_skb; - __u8 *frame; - __u8 n; - - pr_debug("%s(), max_sdu_size=%d\n", __func__, max_sdu_size); - - IRDA_ASSERT(self != NULL, return -EBADR;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -EBADR;); - - if (self->connected) { - if (userdata) - dev_kfree_skb(userdata); - return -EISCONN; - } - - /* Any userdata supplied? */ - if (userdata == NULL) { - tx_skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, - GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - /* Reserve space for MUX_CONTROL and LAP header */ - skb_reserve(tx_skb, TTP_MAX_HEADER + TTP_SAR_HEADER); - } else { - tx_skb = userdata; - /* - * Check that the client has reserved enough space for - * headers - */ - IRDA_ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER, - { dev_kfree_skb(userdata); return -1; }); - } - - /* Initialize connection parameters */ - self->connected = FALSE; - self->avail_credit = 0; - self->rx_max_sdu_size = max_sdu_size; - self->rx_sdu_size = 0; - self->rx_sdu_busy = FALSE; - self->dtsap_sel = dtsap_sel; - - n = self->initial_credit; - - self->remote_credit = 0; - self->send_credit = 0; - - /* - * Give away max 127 credits for now - */ - if (n > 127) { - self->avail_credit = n - 127; - n = 127; - } - - self->remote_credit = n; - - /* SAR enabled? */ - if (max_sdu_size > 0) { - IRDA_ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER), - { dev_kfree_skb(tx_skb); return -1; }); - - /* Insert SAR parameters */ - frame = skb_push(tx_skb, TTP_HEADER + TTP_SAR_HEADER); - - frame[0] = TTP_PARAMETERS | n; - frame[1] = 0x04; /* Length */ - frame[2] = 0x01; /* MaxSduSize */ - frame[3] = 0x02; /* Value length */ - - put_unaligned(cpu_to_be16((__u16) max_sdu_size), - (__be16 *)(frame+4)); - } else { - /* Insert plain TTP header */ - frame = skb_push(tx_skb, TTP_HEADER); - - /* Insert initial credit in frame */ - frame[0] = n & 0x7f; - } - - /* Connect with IrLMP. No QoS parameters for now */ - return irlmp_connect_request(self->lsap, dtsap_sel, saddr, daddr, qos, - tx_skb); -} -EXPORT_SYMBOL(irttp_connect_request); - -/* - * Function irttp_connect_confirm (handle, qos, skb) - * - * Service user confirms TSAP connection with peer. - * - */ -static void irttp_connect_confirm(void *instance, void *sap, - struct qos_info *qos, __u32 max_seg_size, - __u8 max_header_size, struct sk_buff *skb) -{ - struct tsap_cb *self; - int parameters; - int ret; - __u8 plen; - __u8 n; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - self->max_seg_size = max_seg_size - TTP_HEADER; - self->max_header_size = max_header_size + TTP_HEADER; - - /* - * Check if we have got some QoS parameters back! This should be the - * negotiated QoS for the link. - */ - if (qos) { - pr_debug("IrTTP, Negotiated BAUD_RATE: %02x\n", - qos->baud_rate.bits); - pr_debug("IrTTP, Negotiated BAUD_RATE: %d bps.\n", - qos->baud_rate.value); - } - - n = skb->data[0] & 0x7f; - - pr_debug("%s(), Initial send_credit=%d\n", __func__, n); - - self->send_credit = n; - self->tx_max_sdu_size = 0; - self->connected = TRUE; - - parameters = skb->data[0] & 0x80; - - IRDA_ASSERT(skb->len >= TTP_HEADER, return;); - skb_pull(skb, TTP_HEADER); - - if (parameters) { - plen = skb->data[0]; - - ret = irda_param_extract_all(self, skb->data+1, - IRDA_MIN(skb->len-1, plen), - ¶m_info); - - /* Any errors in the parameter list? */ - if (ret < 0) { - net_warn_ratelimited("%s: error extracting parameters\n", - __func__); - dev_kfree_skb(skb); - - /* Do not accept this connection attempt */ - return; - } - /* Remove parameters */ - skb_pull(skb, IRDA_MIN(skb->len, plen+1)); - } - - pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__, - self->send_credit, self->avail_credit, self->remote_credit); - - pr_debug("%s(), MaxSduSize=%d\n", __func__, - self->tx_max_sdu_size); - - if (self->notify.connect_confirm) { - self->notify.connect_confirm(self->notify.instance, self, qos, - self->tx_max_sdu_size, - self->max_header_size, skb); - } else - dev_kfree_skb(skb); -} - -/* - * Function irttp_connect_indication (handle, skb) - * - * Some other device is connecting to this TSAP - * - */ -static void irttp_connect_indication(void *instance, void *sap, - struct qos_info *qos, __u32 max_seg_size, __u8 max_header_size, - struct sk_buff *skb) -{ - struct tsap_cb *self; - struct lsap_cb *lsap; - int parameters; - int ret; - __u8 plen; - __u8 n; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - lsap = sap; - - self->max_seg_size = max_seg_size - TTP_HEADER; - self->max_header_size = max_header_size+TTP_HEADER; - - pr_debug("%s(), TSAP sel=%02x\n", __func__, self->stsap_sel); - - /* Need to update dtsap_sel if its equal to LSAP_ANY */ - self->dtsap_sel = lsap->dlsap_sel; - - n = skb->data[0] & 0x7f; - - self->send_credit = n; - self->tx_max_sdu_size = 0; - - parameters = skb->data[0] & 0x80; - - IRDA_ASSERT(skb->len >= TTP_HEADER, return;); - skb_pull(skb, TTP_HEADER); - - if (parameters) { - plen = skb->data[0]; - - ret = irda_param_extract_all(self, skb->data+1, - IRDA_MIN(skb->len-1, plen), - ¶m_info); - - /* Any errors in the parameter list? */ - if (ret < 0) { - net_warn_ratelimited("%s: error extracting parameters\n", - __func__); - dev_kfree_skb(skb); - - /* Do not accept this connection attempt */ - return; - } - - /* Remove parameters */ - skb_pull(skb, IRDA_MIN(skb->len, plen+1)); - } - - if (self->notify.connect_indication) { - self->notify.connect_indication(self->notify.instance, self, - qos, self->tx_max_sdu_size, - self->max_header_size, skb); - } else - dev_kfree_skb(skb); -} - -/* - * Function irttp_connect_response (handle, userdata) - * - * Service user is accepting the connection, just pass it down to - * IrLMP! - * - */ -int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size, - struct sk_buff *userdata) -{ - struct sk_buff *tx_skb; - __u8 *frame; - int ret; - __u8 n; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - - pr_debug("%s(), Source TSAP selector=%02x\n", __func__, - self->stsap_sel); - - /* Any userdata supplied? */ - if (userdata == NULL) { - tx_skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, - GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - /* Reserve space for MUX_CONTROL and LAP header */ - skb_reserve(tx_skb, TTP_MAX_HEADER + TTP_SAR_HEADER); - } else { - tx_skb = userdata; - /* - * Check that the client has reserved enough space for - * headers - */ - IRDA_ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER, - { dev_kfree_skb(userdata); return -1; }); - } - - self->avail_credit = 0; - self->remote_credit = 0; - self->rx_max_sdu_size = max_sdu_size; - self->rx_sdu_size = 0; - self->rx_sdu_busy = FALSE; - - n = self->initial_credit; - - /* Frame has only space for max 127 credits (7 bits) */ - if (n > 127) { - self->avail_credit = n - 127; - n = 127; - } - - self->remote_credit = n; - self->connected = TRUE; - - /* SAR enabled? */ - if (max_sdu_size > 0) { - IRDA_ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER), - { dev_kfree_skb(tx_skb); return -1; }); - - /* Insert TTP header with SAR parameters */ - frame = skb_push(tx_skb, TTP_HEADER + TTP_SAR_HEADER); - - frame[0] = TTP_PARAMETERS | n; - frame[1] = 0x04; /* Length */ - - /* irda_param_insert(self, IRTTP_MAX_SDU_SIZE, frame+1, */ -/* TTP_SAR_HEADER, ¶m_info) */ - - frame[2] = 0x01; /* MaxSduSize */ - frame[3] = 0x02; /* Value length */ - - put_unaligned(cpu_to_be16((__u16) max_sdu_size), - (__be16 *)(frame+4)); - } else { - /* Insert TTP header */ - frame = skb_push(tx_skb, TTP_HEADER); - - frame[0] = n & 0x7f; - } - - ret = irlmp_connect_response(self->lsap, tx_skb); - - return ret; -} -EXPORT_SYMBOL(irttp_connect_response); - -/* - * Function irttp_dup (self, instance) - * - * Duplicate TSAP, can be used by servers to confirm a connection on a - * new TSAP so it can keep listening on the old one. - */ -struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance) -{ - struct tsap_cb *new; - unsigned long flags; - - /* Protect our access to the old tsap instance */ - spin_lock_irqsave(&irttp->tsaps->hb_spinlock, flags); - - /* Find the old instance */ - if (!hashbin_find(irttp->tsaps, (long) orig, NULL)) { - pr_debug("%s(), unable to find TSAP\n", __func__); - spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); - return NULL; - } - - /* Allocate a new instance */ - new = kmemdup(orig, sizeof(struct tsap_cb), GFP_ATOMIC); - if (!new) { - pr_debug("%s(), unable to kmalloc\n", __func__); - spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); - return NULL; - } - spin_lock_init(&new->lock); - - /* We don't need the old instance any more */ - spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); - - /* Try to dup the LSAP (may fail if we were too slow) */ - new->lsap = irlmp_dup(orig->lsap, new); - if (!new->lsap) { - pr_debug("%s(), dup failed!\n", __func__); - kfree(new); - return NULL; - } - - /* Not everything should be copied */ - new->notify.instance = instance; - - /* Initialize internal objects */ - irttp_init_tsap(new); - - /* This is locked */ - hashbin_insert(irttp->tsaps, (irda_queue_t *) new, (long) new, NULL); - - return new; -} -EXPORT_SYMBOL(irttp_dup); - -/* - * Function irttp_disconnect_request (self) - * - * Close this connection please! If priority is high, the queued data - * segments, if any, will be deallocated first - * - */ -int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata, - int priority) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - - /* Already disconnected? */ - if (!self->connected) { - pr_debug("%s(), already disconnected!\n", __func__); - if (userdata) - dev_kfree_skb(userdata); - return -1; - } - - /* Disconnect already pending ? - * We need to use an atomic operation to prevent reentry. This - * function may be called from various context, like user, timer - * for following a disconnect_indication() (i.e. net_bh). - * Jean II */ - if (test_and_set_bit(0, &self->disconnect_pend)) { - pr_debug("%s(), disconnect already pending\n", - __func__); - if (userdata) - dev_kfree_skb(userdata); - - /* Try to make some progress */ - irttp_run_tx_queue(self); - return -1; - } - - /* - * Check if there is still data segments in the transmit queue - */ - if (!skb_queue_empty(&self->tx_queue)) { - if (priority == P_HIGH) { - /* - * No need to send the queued data, if we are - * disconnecting right now since the data will - * not have any usable connection to be sent on - */ - pr_debug("%s(): High priority!!()\n", __func__); - irttp_flush_queues(self); - } else if (priority == P_NORMAL) { - /* - * Must delay disconnect until after all data segments - * have been sent and the tx_queue is empty - */ - /* We'll reuse this one later for the disconnect */ - self->disconnect_skb = userdata; /* May be NULL */ - - irttp_run_tx_queue(self); - - irttp_start_todo_timer(self, HZ/10); - return -1; - } - } - /* Note : we don't need to check if self->rx_queue is full and the - * state of self->rx_sdu_busy because the disconnect response will - * be sent at the LMP level (so even if the peer has its Tx queue - * full of data). - Jean II */ - - pr_debug("%s(), Disconnecting ...\n", __func__); - self->connected = FALSE; - - if (!userdata) { - struct sk_buff *tx_skb; - tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - /* - * Reserve space for MUX and LAP header - */ - skb_reserve(tx_skb, LMP_MAX_HEADER); - - userdata = tx_skb; - } - ret = irlmp_disconnect_request(self->lsap, userdata); - - /* The disconnect is no longer pending */ - clear_bit(0, &self->disconnect_pend); /* FALSE */ - - return ret; -} -EXPORT_SYMBOL(irttp_disconnect_request); - -/* - * Function irttp_disconnect_indication (self, reason) - * - * Disconnect indication, TSAP disconnected by peer? - * - */ -static void irttp_disconnect_indication(void *instance, void *sap, - LM_REASON reason, struct sk_buff *skb) -{ - struct tsap_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - /* Prevent higher layer to send more data */ - self->connected = FALSE; - - /* Check if client has already tried to close the TSAP */ - if (self->close_pend) { - /* In this case, the higher layer is probably gone. Don't - * bother it and clean up the remains - Jean II */ - if (skb) - dev_kfree_skb(skb); - irttp_close_tsap(self); - return; - } - - /* If we are here, we assume that is the higher layer is still - * waiting for the disconnect notification and able to process it, - * even if he tried to disconnect. Otherwise, it would have already - * attempted to close the tsap and self->close_pend would be TRUE. - * Jean II */ - - /* No need to notify the client if has already tried to disconnect */ - if (self->notify.disconnect_indication) - self->notify.disconnect_indication(self->notify.instance, self, - reason, skb); - else - if (skb) - dev_kfree_skb(skb); -} - -/* - * Function irttp_do_data_indication (self, skb) - * - * Try to deliver reassembled skb to layer above, and requeue it if that - * for some reason should fail. We mark rx sdu as busy to apply back - * pressure is necessary. - */ -static void irttp_do_data_indication(struct tsap_cb *self, struct sk_buff *skb) -{ - int err; - - /* Check if client has already closed the TSAP and gone away */ - if (self->close_pend) { - dev_kfree_skb(skb); - return; - } - - err = self->notify.data_indication(self->notify.instance, self, skb); - - /* Usually the layer above will notify that it's input queue is - * starting to get filled by using the flow request, but this may - * be difficult, so it can instead just refuse to eat it and just - * give an error back - */ - if (err) { - pr_debug("%s() requeueing skb!\n", __func__); - - /* Make sure we take a break */ - self->rx_sdu_busy = TRUE; - - /* Need to push the header in again */ - skb_push(skb, TTP_HEADER); - skb->data[0] = 0x00; /* Make sure MORE bit is cleared */ - - /* Put skb back on queue */ - skb_queue_head(&self->rx_queue, skb); - } -} - -/* - * Function irttp_run_rx_queue (self) - * - * Check if we have any frames to be transmitted, or if we have any - * available credit to give away. - */ -static void irttp_run_rx_queue(struct tsap_cb *self) -{ - struct sk_buff *skb; - int more = 0; - - pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__, - self->send_credit, self->avail_credit, self->remote_credit); - - /* Get exclusive access to the rx queue, otherwise don't touch it */ - if (irda_lock(&self->rx_queue_lock) == FALSE) - return; - - /* - * Reassemble all frames in receive queue and deliver them - */ - while (!self->rx_sdu_busy && (skb = skb_dequeue(&self->rx_queue))) { - /* This bit will tell us if it's the last fragment or not */ - more = skb->data[0] & 0x80; - - /* Remove TTP header */ - skb_pull(skb, TTP_HEADER); - - /* Add the length of the remaining data */ - self->rx_sdu_size += skb->len; - - /* - * If SAR is disabled, or user has requested no reassembly - * of received fragments then we just deliver them - * immediately. This can be requested by clients that - * implements byte streams without any message boundaries - */ - if (self->rx_max_sdu_size == TTP_SAR_DISABLE) { - irttp_do_data_indication(self, skb); - self->rx_sdu_size = 0; - - continue; - } - - /* Check if this is a fragment, and not the last fragment */ - if (more) { - /* - * Queue the fragment if we still are within the - * limits of the maximum size of the rx_sdu - */ - if (self->rx_sdu_size <= self->rx_max_sdu_size) { - pr_debug("%s(), queueing frag\n", - __func__); - skb_queue_tail(&self->rx_fragments, skb); - } else { - /* Free the part of the SDU that is too big */ - dev_kfree_skb(skb); - } - continue; - } - /* - * This is the last fragment, so time to reassemble! - */ - if ((self->rx_sdu_size <= self->rx_max_sdu_size) || - (self->rx_max_sdu_size == TTP_SAR_UNBOUND)) { - /* - * A little optimizing. Only queue the fragment if - * there are other fragments. Since if this is the - * last and only fragment, there is no need to - * reassemble :-) - */ - if (!skb_queue_empty(&self->rx_fragments)) { - skb_queue_tail(&self->rx_fragments, - skb); - - skb = irttp_reassemble_skb(self); - } - - /* Now we can deliver the reassembled skb */ - irttp_do_data_indication(self, skb); - } else { - pr_debug("%s(), Truncated frame\n", __func__); - - /* Free the part of the SDU that is too big */ - dev_kfree_skb(skb); - - /* Deliver only the valid but truncated part of SDU */ - skb = irttp_reassemble_skb(self); - - irttp_do_data_indication(self, skb); - } - self->rx_sdu_size = 0; - } - - /* - * It's not trivial to keep track of how many credits are available - * by incrementing at each packet, because delivery may fail - * (irttp_do_data_indication() may requeue the frame) and because - * we need to take care of fragmentation. - * We want the other side to send up to initial_credit packets. - * We have some frames in our queues, and we have already allowed it - * to send remote_credit. - * No need to spinlock, write is atomic and self correcting... - * Jean II - */ - self->avail_credit = (self->initial_credit - - (self->remote_credit + - skb_queue_len(&self->rx_queue) + - skb_queue_len(&self->rx_fragments))); - - /* Do we have too much credits to send to peer ? */ - if ((self->remote_credit <= TTP_RX_MIN_CREDIT) && - (self->avail_credit > 0)) { - /* Send explicit credit frame */ - irttp_give_credit(self); - /* Note : do *NOT* check if tx_queue is non-empty, that - * will produce deadlocks. I repeat : send a credit frame - * even if we have something to send in our Tx queue. - * If we have credits, it means that our Tx queue is blocked. - * - * Let's suppose the peer can't keep up with our Tx. He will - * flow control us by not sending us any credits, and we - * will stop Tx and start accumulating credits here. - * Up to the point where the peer will stop its Tx queue, - * for lack of credits. - * Let's assume the peer application is single threaded. - * It will block on Tx and never consume any Rx buffer. - * Deadlock. Guaranteed. - Jean II - */ - } - - /* Reset lock */ - self->rx_queue_lock = 0; -} - -#ifdef CONFIG_PROC_FS -struct irttp_iter_state { - int id; -}; - -static void *irttp_seq_start(struct seq_file *seq, loff_t *pos) -{ - struct irttp_iter_state *iter = seq->private; - struct tsap_cb *self; - - /* Protect our access to the tsap list */ - spin_lock_irq(&irttp->tsaps->hb_spinlock); - iter->id = 0; - - for (self = (struct tsap_cb *) hashbin_get_first(irttp->tsaps); - self != NULL; - self = (struct tsap_cb *) hashbin_get_next(irttp->tsaps)) { - if (iter->id == *pos) - break; - ++iter->id; - } - - return self; -} - -static void *irttp_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct irttp_iter_state *iter = seq->private; - - ++*pos; - ++iter->id; - return (void *) hashbin_get_next(irttp->tsaps); -} - -static void irttp_seq_stop(struct seq_file *seq, void *v) -{ - spin_unlock_irq(&irttp->tsaps->hb_spinlock); -} - -static int irttp_seq_show(struct seq_file *seq, void *v) -{ - const struct irttp_iter_state *iter = seq->private; - const struct tsap_cb *self = v; - - seq_printf(seq, "TSAP %d, ", iter->id); - seq_printf(seq, "stsap_sel: %02x, ", - self->stsap_sel); - seq_printf(seq, "dtsap_sel: %02x\n", - self->dtsap_sel); - seq_printf(seq, " connected: %s, ", - self->connected ? "TRUE" : "FALSE"); - seq_printf(seq, "avail credit: %d, ", - self->avail_credit); - seq_printf(seq, "remote credit: %d, ", - self->remote_credit); - seq_printf(seq, "send credit: %d\n", - self->send_credit); - seq_printf(seq, " tx packets: %lu, ", - self->stats.tx_packets); - seq_printf(seq, "rx packets: %lu, ", - self->stats.rx_packets); - seq_printf(seq, "tx_queue len: %u ", - skb_queue_len(&self->tx_queue)); - seq_printf(seq, "rx_queue len: %u\n", - skb_queue_len(&self->rx_queue)); - seq_printf(seq, " tx_sdu_busy: %s, ", - self->tx_sdu_busy ? "TRUE" : "FALSE"); - seq_printf(seq, "rx_sdu_busy: %s\n", - self->rx_sdu_busy ? "TRUE" : "FALSE"); - seq_printf(seq, " max_seg_size: %u, ", - self->max_seg_size); - seq_printf(seq, "tx_max_sdu_size: %u, ", - self->tx_max_sdu_size); - seq_printf(seq, "rx_max_sdu_size: %u\n", - self->rx_max_sdu_size); - - seq_printf(seq, " Used by (%s)\n\n", - self->notify.name); - return 0; -} - -static const struct seq_operations irttp_seq_ops = { - .start = irttp_seq_start, - .next = irttp_seq_next, - .stop = irttp_seq_stop, - .show = irttp_seq_show, -}; - -static int irttp_seq_open(struct inode *inode, struct file *file) -{ - return seq_open_private(file, &irttp_seq_ops, - sizeof(struct irttp_iter_state)); -} - -const struct file_operations irttp_seq_fops = { - .owner = THIS_MODULE, - .open = irttp_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; - -#endif /* PROC_FS */ |