/********************************************************************* * * Filename: qos.c * Version: 0.8 * Description: IrLAP QoS negotiation * Status: Experimental. * Author: Dag Brattli * Created at: Tue Sep 9 00:00:26 1997 * Modified at: Mon May 3 21:15:08 1999 * Modified by: Dag Brattli * * Copyright (c) 1998-1999 Dag Brattli , * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * 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 #include #include #include #include #ifdef CONFIG_IRDA_COMPRESSION #include #include "../../drivers/net/zlib.h" #define CI_BZIP2 27 /* Random pick */ #endif int min_turn_time[] = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; int baud_rates[] = { 2400, 9600, 19200, 38400, 57600, 115200, 576000, 1152000, 4000000 }; int data_size[] = { 64, 128, 256, 512, 1024, 2048 }; int add_bofs[] = { 48, 24, 12, 5, 3, 2, 1, 0 }; int max_turn_time[] = { 500, 250, 100, 50 }; int link_disc_time[] = { 3, 8, 12, 16, 20, 25, 30, 40 }; #ifdef CONFIG_IRDA_COMPRESSION int compression[] = { CI_BZIP2, CI_DEFLATE, CI_DEFLATE_DRAFT }; #endif /* * Function irda_qos_compute_intersection (qos, new) * * Compute the intersection of the old QoS capabilites with new ones * */ void irda_qos_compute_intersection(struct qos_info *qos, struct qos_info *new) { ASSERT(qos != NULL, return;); ASSERT(new != NULL, return;); /* Apply */ qos->baud_rate.bits &= new->baud_rate.bits; qos->window_size.bits &= new->window_size.bits; qos->min_turn_time.bits &= new->min_turn_time.bits; qos->max_turn_time.bits &= new->max_turn_time.bits; qos->data_size.bits &= new->data_size.bits; qos->link_disc_time.bits &= new->link_disc_time.bits; qos->additional_bofs.bits &= new->additional_bofs.bits; #ifdef CONFIG_IRDA_COMPRESSION qos->compression.bits &= new->compression.bits; #endif irda_qos_bits_to_value(qos); } /* * Function irda_init_max_qos_capabilies (qos) * * The purpose of this function is for layers and drivers to be able to * set the maximum QoS possible and then "and in" their own limitations * */ void irda_init_max_qos_capabilies( struct qos_info *qos) { /* * These are the maximum supported values as specified on pages * 39-43 in IrLAP */ /* LSB is first byte, MSB is second byte */ qos->baud_rate.bits = 0x01ff; qos->window_size.bits = 0x7f; qos->min_turn_time.bits = 0xff; qos->max_turn_time.bits = 0x0f; qos->data_size.bits = 0x3f; qos->link_disc_time.bits = 0xff; qos->additional_bofs.bits = 0xff; #ifdef CONFIG_IRDA_COMPRESSION qos->compression.bits = 0x03; #endif } /* * Function irlap_negotiate (qos_device, qos_session, skb) * * Negotiate QoS values, not really that much negotiation :-) * We just set the QoS capabilities for the peer station * */ void irda_qos_negotiate(struct qos_info *qos_rx, struct qos_info *qos_tx, struct sk_buff *skb) { int n=0; #ifdef CONFIG_IRDA_COMPRESSION int comp_seen = FALSE; #endif __u8 length; __u8 *frame; __u8 final_byte; __u8 code; __u8 byte; __u16 final_word; __u16_host_order word; ASSERT( qos_tx != NULL, return;); ASSERT( qos_rx != NULL, return;); ASSERT( skb != NULL, return;); frame = skb->data; while( n < skb->len-2) { code = frame[n++]; /* Length */ length = frame[n++]; /* * Get the value, since baud_rate may need two bytes, we * Just use word size all the time */ switch( length) { case 1: byte = frame[n++]; word.word = byte; /* To make things a little easier */ break; case 2: #ifdef __LITTLE_ENDIAN word.byte[0] = frame[n++]; word.byte[1] = frame[n++]; #else ifdef __BIG_ENDIAN word.byte[1] = frame[n++]; word.byte[0] = frame[n++]; #endif byte = 0; break; default: DEBUG( 0, __FUNCTION__ "Error\n"); word.word = byte = 0; n += length; break; } switch(code) { case PI_BAUD_RATE: /* * Stations must agree on baud rate, so calculate * intersection */ DEBUG( 4, "Requested BAUD_RATE: 0x%04x\n", word.word); final_word = word.word & qos_rx->baud_rate.bits; DEBUG( 4, "Final BAUD_RATE: 0x%04x\n", final_word); qos_tx->baud_rate.bits = final_word; qos_rx->baud_rate.bits = final_word; break; case PI_MAX_TURN_TIME: /* * Negotiated independently for each station */ DEBUG(4, __FUNCTION__ "(), MAX_TURN_TIME: %02x\n", byte); qos_tx->max_turn_time.bits = byte; break; case PI_DATA_SIZE: /* * Negotiated independently for each station */ DEBUG( 4, "DATA_SIZE: %02x\n", byte); qos_tx->data_size.bits = byte; break; case PI_WINDOW_SIZE: /* * Negotiated independently for each station */ qos_tx->window_size.bits = byte; break; case PI_ADD_BOFS: /* * Negotiated independently for each station */ DEBUG( 4, "ADD_BOFS: %02x\n", byte); qos_tx->additional_bofs.bits = byte; break; case PI_MIN_TURN_TIME: DEBUG( 4, "MIN_TURN_TIME: %02x\n", byte); qos_tx->min_turn_time.bits = byte; break; case PI_LINK_DISC: /* * Stations must agree on link disconnect/threshold * time. */ DEBUG( 4, "LINK_DISC: %02x\n", byte); final_byte = byte & qos_rx->link_disc_time.bits; DEBUG( 4, "Final LINK_DISC: %02x\n", final_byte); qos_tx->link_disc_time.bits = final_byte; qos_rx->link_disc_time.bits = final_byte; break; #ifdef CONFIG_IRDA_COMPRESSION case PI_COMPRESSION: final_byte = byte & qos_rx->compression.bits; qos_rx->compression.bits = byte; qos_tx->compression.bits = byte; comp_seen = TRUE; break; #endif default: DEBUG( 0, __FUNCTION__ "(), Unknown value\n"); break; } } #ifdef CONFIG_IRDA_COMPRESSION if ( !comp_seen) { DEBUG( 4, __FUNCTION__ "(), Compression not seen!\n"); qos_tx->compression.bits = 0x00; qos_rx->compression.bits = 0x00; } #endif /* Convert the negotiated bits to values */ irda_qos_bits_to_value( qos_tx); irda_qos_bits_to_value( qos_rx); DEBUG( 4, "Setting BAUD_RATE to %d bps.\n", qos_tx->baud_rate.value); DEBUG( 4, "Setting DATA_SIZE to %d bytes\n", qos_tx->data_size.value); DEBUG( 4, "Setting WINDOW_SIZE to %d\n", qos_tx->window_size.value); DEBUG( 4, "Setting XBOFS to %d\n", qos_tx->additional_bofs.value); DEBUG( 4, "Setting MAX_TURN_TIME to %d ms.\n", qos_tx->max_turn_time.value); DEBUG( 4, "Setting MIN_TURN_TIME to %d usecs.\n", qos_tx->min_turn_time.value); DEBUG( 4, "Setting LINK_DISC to %d secs.\n", qos_tx->link_disc_time.value); #ifdef CONFIG_IRDA_COMPRESSION DEBUG( 4, "Setting COMPRESSION to %d\n", qos_tx->compression.value); #endif } /* * Function irlap_insert_negotiation_params (qos, fp) * * Insert QoS negotiaion pararameters into frame * */ int irda_insert_qos_negotiation_params(struct qos_info *qos, __u8 *frame) { int n; __u16_host_order word; ASSERT( qos != NULL, return 0;); ASSERT( frame != NULL, return 0;); n = 0; /* Set baud rate */ if (qos->baud_rate.bits < 256) { frame[n++] = PI_BAUD_RATE; frame[n++] = 0x01; /* length 1 */ frame[n++] = qos->baud_rate.bits; } else { frame[n++] = PI_BAUD_RATE; frame[n++] = 0x02; /* length 2 */ /* * qos->baud_rate.bits is in host byte order, so make sure * we transmit it in little endian format */ word.word = qos->baud_rate.bits; #ifdef __LITTLE_ENDIAN frame[n++] = word.byte[0]; /* LSB */ frame[n++] = word.byte[1]; /* MSB */ #else ifdef __BIG_ENDIAN frame[n++] = word.byte[1]; /* LSB */ frame[n++] = word.byte[0]; /* MSB */ #endif } /* Set Maximum Turn Around Time */ frame[n++] = PI_MAX_TURN_TIME; frame[n++] = 0x01; /* length 1 */ frame[n++] = qos->max_turn_time.bits; /* Set data size */ frame[n++] = PI_DATA_SIZE; frame[n++] = 0x01; /* length 1 */ frame[n++] = qos->data_size.bits; /* Set window size */ frame[n++] = PI_WINDOW_SIZE; frame[n++] = 0x01; /* length 1 */ frame[n++] = qos->window_size.bits; /* Set additional BOFs */ frame[n++] = PI_ADD_BOFS; frame[n++] = 0x01; /* length 1 */ frame[n++] = qos->additional_bofs.bits; /* Set minimum turn around time */ frame[n++] = PI_MIN_TURN_TIME; frame[n++] = 0x01; /* length 1 */ frame[n++] = qos->min_turn_time.bits; /* Set Link Disconnect/Threshold Time */ frame[n++] = PI_LINK_DISC; frame[n++] = 0x01; /* length 1 */ frame[n++] = qos->link_disc_time.bits; #ifdef CONFIG_IRDA_COMPRESSION /* Set compression bits*/ if ( qos->compression.bits) { DEBUG( 4, __FUNCTION__ "(), inserting compresion bits\n"); frame[n++] = PI_COMPRESSION; frame[n++] = 0x01; /* length 1 */ frame[n++] = qos->compression.bits; } #endif return n; } int byte_value( __u8 byte, int *array) { int index; ASSERT( array != NULL, return -1;); index = msb_index( byte); return index_value( index, array); } /* __u8 value_byte( int value, int *array) */ /* { */ /* int index; */ /* __u8 byte; */ /* index = value_index( value, array); */ /* byte = */ /* } */ /* * Function msb_index (word) * * Returns index to most significant bit (MSB) in word * */ int msb_index ( __u16 word) { __u16 msb = 0x8000; int index = 15; /* Current MSB */ while( msb) { if ( word & msb) break; /* Found it! */ msb >>=1; index--; } return index; } /* * Function value_index (value, array) * * Returns the index to the value in the specified array */ int value_index( int value, int *array) { int i; for( i=0;i<8;i++) if ( array[i] == value) break; return i; } /* * Function index_value (index, array) * * Returns value to index in array, easy! * */ int index_value( int index, int *array) { return array[index]; } void irda_qos_bits_to_value( struct qos_info *qos) { int index; ASSERT( qos != NULL, return;); index = msb_index( qos->baud_rate.bits); qos->baud_rate.value = baud_rates[index]; index = msb_index( qos->data_size.bits); qos->data_size.value = data_size[index]; index = msb_index( qos->window_size.bits); qos->window_size.value = index+1; index = msb_index( qos->min_turn_time.bits); qos->min_turn_time.value = min_turn_time[index]; index = msb_index( qos->max_turn_time.bits); qos->max_turn_time.value = max_turn_time[index]; index = msb_index( qos->link_disc_time.bits); qos->link_disc_time.value = link_disc_time[index]; index = msb_index( qos->additional_bofs.bits); qos->additional_bofs.value = add_bofs[index]; #ifdef CONFIG_IRDA_COMPRESSION index = msb_index( qos->compression.bits); if ( index >= 0) qos->compression.value = compression[index]; else qos->compression.value = 0; #endif }