OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [net/] [802/] [llc_sendpdu.c] - Diff between revs 1275 and 1765

Only display areas with differences | Details | Blame | View Log

Rev 1275 Rev 1765
/*
/*
 * NET          An implementation of the IEEE 802.2 LLC protocol for the
 * NET          An implementation of the IEEE 802.2 LLC protocol for the
 *              LINUX operating system.  LLC is implemented as a set of
 *              LINUX operating system.  LLC is implemented as a set of
 *              state machines and callbacks for higher networking layers.
 *              state machines and callbacks for higher networking layers.
 *
 *
 *              llc_sendpdu(), llc_sendipdu(), resend() + queue handling code
 *              llc_sendpdu(), llc_sendipdu(), resend() + queue handling code
 *
 *
 *              Written by Tim Alpaerts, Tim_Alpaerts@toyota-motor-europe.com
 *              Written by Tim Alpaerts, Tim_Alpaerts@toyota-motor-europe.com
 *
 *
 *              This program is free software; you can redistribute it and/or
 *              This program is free software; you can redistribute it and/or
 *              modify it under the terms of the GNU General Public License
 *              modify it under the terms of the GNU General Public License
 *              as published by the Free Software Foundation; either version
 *              as published by the Free Software Foundation; either version
 *              2 of the License, or (at your option) any later version.
 *              2 of the License, or (at your option) any later version.
 *
 *
 *      Changes
 *      Changes
 *              Alan Cox        :       Chainsawed into Linux format, style
 *              Alan Cox        :       Chainsawed into Linux format, style
 *                                      Added llc_ to function names
 *                                      Added llc_ to function names
 */
 */
 
 
#include <linux/types.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/skbuff.h>
#include <net/p8022.h>
#include <net/p8022.h>
#include <linux/stat.h>
#include <linux/stat.h>
#include <asm/byteorder.h>
#include <asm/byteorder.h>
#include <net/llc_frame.h>
#include <net/llc_frame.h>
#include <net/llc.h>
#include <net/llc.h>
 
 
static unsigned char cntl_byte_encode[] =
static unsigned char cntl_byte_encode[] =
{
{
        0x00,   /* I_CMD */
        0x00,   /* I_CMD */
        0x01,   /* RR_CMD */
        0x01,   /* RR_CMD */
        0x05,   /* RNR_CMD */
        0x05,   /* RNR_CMD */
        0x09,   /* REJ_CMD */
        0x09,   /* REJ_CMD */
        0x43,   /* DISC_CMD */
        0x43,   /* DISC_CMD */
        0x7F,   /* SABME_CMD */
        0x7F,   /* SABME_CMD */
        0x00,   /* I_RSP */
        0x00,   /* I_RSP */
        0x01,   /* RR_RSP */
        0x01,   /* RR_RSP */
        0x05,   /* RNR_RSP */
        0x05,   /* RNR_RSP */
        0x09,   /* REJ_RSP */
        0x09,   /* REJ_RSP */
        0x63,   /* UA_RSP */
        0x63,   /* UA_RSP */
        0x0F,   /* DM_RSP */
        0x0F,   /* DM_RSP */
        0x87,   /* FRMR_RSP */
        0x87,   /* FRMR_RSP */
        0xFF,   /* BAD_FRAME */
        0xFF,   /* BAD_FRAME */
        0x03,   /* UI_CMD */
        0x03,   /* UI_CMD */
        0xBF,   /* XID_CMD */
        0xBF,   /* XID_CMD */
        0xE3,   /* TEST_CMD */
        0xE3,   /* TEST_CMD */
        0xBF,   /* XID_RSP */
        0xBF,   /* XID_RSP */
        0xE3    /* TEST_RSP */
        0xE3    /* TEST_RSP */
};
};
 
 
static unsigned char fr_length_encode[] =
static unsigned char fr_length_encode[] =
{
{
        0x04,   /* I_CMD */
        0x04,   /* I_CMD */
        0x04,   /* RR_CMD */
        0x04,   /* RR_CMD */
        0x04,   /* RNR_CMD */
        0x04,   /* RNR_CMD */
        0x04,   /* REJ_CMD */
        0x04,   /* REJ_CMD */
        0x03,   /* DISC_CMD */
        0x03,   /* DISC_CMD */
        0x03,   /* SABME_CMD */
        0x03,   /* SABME_CMD */
        0x04,   /* I_RSP */
        0x04,   /* I_RSP */
        0x04,   /* RR_RSP */
        0x04,   /* RR_RSP */
        0x04,   /* RNR_RSP */
        0x04,   /* RNR_RSP */
        0x04,   /* REJ_RSP */
        0x04,   /* REJ_RSP */
        0x03,   /* UA_RSP */
        0x03,   /* UA_RSP */
        0x03,   /* DM_RSP */
        0x03,   /* DM_RSP */
        0x03,   /* FRMR_RSP */
        0x03,   /* FRMR_RSP */
        0x00,   /* BAD_FRAME */
        0x00,   /* BAD_FRAME */
        0x03,   /* UI_CMD */
        0x03,   /* UI_CMD */
        0x03,   /* XID_CMD */
        0x03,   /* XID_CMD */
        0x03,   /* TEST_CMD */
        0x03,   /* TEST_CMD */
        0x03,   /* XID_RSP */
        0x03,   /* XID_RSP */
        0x03    /* TEST_RSP */
        0x03    /* TEST_RSP */
};
};
 
 
static unsigned char cr_bit_encode[] = {
static unsigned char cr_bit_encode[] = {
        0x00,   /* I_CMD */
        0x00,   /* I_CMD */
        0x00,   /* RR_CMD */
        0x00,   /* RR_CMD */
        0x00,   /* RNR_CMD */
        0x00,   /* RNR_CMD */
        0x00,   /* REJ_CMD */
        0x00,   /* REJ_CMD */
        0x00,   /* DISC_CMD */
        0x00,   /* DISC_CMD */
        0x00,   /* SABME_CMD */
        0x00,   /* SABME_CMD */
        0x01,   /* I_RSP */
        0x01,   /* I_RSP */
        0x01,   /* RR_RSP */
        0x01,   /* RR_RSP */
        0x01,   /* RNR_RSP */
        0x01,   /* RNR_RSP */
        0x01,   /* REJ_RSP */
        0x01,   /* REJ_RSP */
        0x01,   /* UA_RSP */
        0x01,   /* UA_RSP */
        0x01,   /* DM_RSP */
        0x01,   /* DM_RSP */
        0x01,   /* FRMR_RSP */
        0x01,   /* FRMR_RSP */
        0x00,   /* BAD_FRAME */
        0x00,   /* BAD_FRAME */
        0x00,   /* UI_CMD */
        0x00,   /* UI_CMD */
        0x00,   /* XID_CMD */
        0x00,   /* XID_CMD */
        0x00,   /* TEST_CMD */
        0x00,   /* TEST_CMD */
        0x01,   /* XID_RSP */
        0x01,   /* XID_RSP */
        0x01    /* TEST_RSP */
        0x01    /* TEST_RSP */
};
};
 
 
/*
/*
 *      Sendpdu() constructs an output frame in a new skb and
 *      Sendpdu() constructs an output frame in a new skb and
 *      gives it to the MAC layer for transmission.
 *      gives it to the MAC layer for transmission.
 *      This function is not used to send I pdus.
 *      This function is not used to send I pdus.
 *      No queues are updated here, nothing is saved for retransmission.
 *      No queues are updated here, nothing is saved for retransmission.
 *
 *
 *      Parameter pf controls both the poll/final bit and dsap
 *      Parameter pf controls both the poll/final bit and dsap
 *      fields in the output pdu.
 *      fields in the output pdu.
 *      The dsap trick was needed to implement XID_CMD send with
 *      The dsap trick was needed to implement XID_CMD send with
 *      zero dsap field as described in doc 6.6 item 1 of enum.
 *      zero dsap field as described in doc 6.6 item 1 of enum.
 */
 */
 
 
void llc_sendpdu(llcptr lp, char type, char pf, int data_len, char *pdu_data)
void llc_sendpdu(llcptr lp, char type, char pf, int data_len, char *pdu_data)
{
{
        frameptr fr;                /* ptr to output pdu buffer */
        frameptr fr;                /* ptr to output pdu buffer */
        unsigned short int fl;      /* frame length == 802.3 "length" value */
        unsigned short int fl;      /* frame length == 802.3 "length" value */
        struct sk_buff *skb;
        struct sk_buff *skb;
 
 
        fl = data_len + fr_length_encode[(int)type];
        fl = data_len + fr_length_encode[(int)type];
        skb = alloc_skb(16 + fl, GFP_ATOMIC);
        skb = alloc_skb(16 + fl, GFP_ATOMIC);
        if (skb != NULL)
        if (skb != NULL)
        {
        {
                skb->dev = lp->dev;
                skb->dev = lp->dev;
                skb_reserve(skb, 16);
                skb_reserve(skb, 16);
                fr = (frameptr) skb_put(skb, fl);
                fr = (frameptr) skb_put(skb, fl);
                memset(fr, 0, fl);
                memset(fr, 0, fl);
                /*
                /*
                 *      Construct 802.2 header
                 *      Construct 802.2 header
                 */
                 */
                if (pf & 0x02)
                if (pf & 0x02)
                        fr->pdu_hdr.dsap = 0;
                        fr->pdu_hdr.dsap = 0;
                else
                else
                        fr->pdu_hdr.dsap = lp->remote_sap;
                        fr->pdu_hdr.dsap = lp->remote_sap;
                fr->pdu_hdr.ssap = lp->local_sap + cr_bit_encode[(int)type];
                fr->pdu_hdr.ssap = lp->local_sap + cr_bit_encode[(int)type];
                fr->pdu_cntl.byte1 = cntl_byte_encode[(int)type];
                fr->pdu_cntl.byte1 = cntl_byte_encode[(int)type];
                /*
                /*
                 *      Fill in pflag and seq nbrs:
                 *      Fill in pflag and seq nbrs:
                 */
                 */
                if (IS_SFRAME(fr))
                if (IS_SFRAME(fr))
                {
                {
                        /* case S-frames */
                        /* case S-frames */
                        if (pf & 0x01)
                        if (pf & 0x01)
                                fr->i_hdr.i_pflag = 1;
                                fr->i_hdr.i_pflag = 1;
                        fr->i_hdr.nr = lp->vr;
                        fr->i_hdr.nr = lp->vr;
                }
                }
                else
                else
                {
                {
                        /* case U frames */
                        /* case U frames */
                        if (pf & 0x01)
                        if (pf & 0x01)
                                fr->u_hdr.u_pflag = 1;
                                fr->u_hdr.u_pflag = 1;
                }
                }
 
 
                if (data_len > 0)
                if (data_len > 0)
                {                       /* append data if any  */
                {                       /* append data if any  */
                        if (IS_UFRAME(fr))
                        if (IS_UFRAME(fr))
                        {
                        {
                                memcpy(fr->u_hdr.u_info, pdu_data, data_len);
                                memcpy(fr->u_hdr.u_info, pdu_data, data_len);
                        }
                        }
                        else
                        else
                        {
                        {
                                memcpy(fr->i_hdr.is_info, pdu_data, data_len);
                                memcpy(fr->i_hdr.is_info, pdu_data, data_len);
                        }
                        }
                }
                }
                lp->dev->hard_header(skb, lp->dev, ETH_P_802_3,
                lp->dev->hard_header(skb, lp->dev, ETH_P_802_3,
                         lp->remote_mac, NULL, fl);
                         lp->remote_mac, NULL, fl);
                skb->dev=lp->dev;
                skb->dev=lp->dev;
                dev_queue_xmit(skb);
                dev_queue_xmit(skb);
        }
        }
        else
        else
                printk(KERN_DEBUG "cl2llc: skb_alloc() in llc_sendpdu() failed\n");
                printk(KERN_DEBUG "cl2llc: skb_alloc() in llc_sendpdu() failed\n");
}
}
 
 
void llc_xid_request(llcptr lp, char opt, int ll, char * data)
void llc_xid_request(llcptr lp, char opt, int ll, char * data)
{
{
        llc_sendpdu(lp, XID_CMD, opt, ll, data);
        llc_sendpdu(lp, XID_CMD, opt, ll, data);
}
}
 
 
void llc_test_request(llcptr lp, int ll, char * data)
void llc_test_request(llcptr lp, int ll, char * data)
{
{
        llc_sendpdu(lp, TEST_CMD, 0, ll, data);
        llc_sendpdu(lp, TEST_CMD, 0, ll, data);
}
}
 
 
void llc_unit_data_request(llcptr lp, int ll, char * data)
void llc_unit_data_request(llcptr lp, int ll, char * data)
{
{
        llc_sendpdu(lp, UI_CMD, 0, ll, data);
        llc_sendpdu(lp, UI_CMD, 0, ll, data);
}
}
 
 
 
 
/*
/*
 *      llc_sendipdu() Completes an I pdu in an existing skb and gives it
 *      llc_sendipdu() Completes an I pdu in an existing skb and gives it
 *      to the MAC layer for transmission.
 *      to the MAC layer for transmission.
 *      Parameter "type" must be either I_CMD or I_RSP.
 *      Parameter "type" must be either I_CMD or I_RSP.
 *      The skb is not freed after xmit, it is kept in case a retransmission
 *      The skb is not freed after xmit, it is kept in case a retransmission
 *      is requested. If needed it can be picked up again from the rtq.
 *      is requested. If needed it can be picked up again from the rtq.
 */
 */
 
 
void llc_sendipdu(llcptr lp, char type, char pf, struct sk_buff *skb)
void llc_sendipdu(llcptr lp, char type, char pf, struct sk_buff *skb)
{
{
        frameptr fr;                /* ptr to output pdu buffer */
        frameptr fr;                /* ptr to output pdu buffer */
        struct sk_buff *tmp;
        struct sk_buff *tmp;
 
 
        fr = (frameptr) skb->data;
        fr = (frameptr) skb->data;
 
 
        fr->pdu_hdr.dsap = lp->remote_sap;
        fr->pdu_hdr.dsap = lp->remote_sap;
        fr->pdu_hdr.ssap = lp->local_sap + cr_bit_encode[(int)type];
        fr->pdu_hdr.ssap = lp->local_sap + cr_bit_encode[(int)type];
        fr->pdu_cntl.byte1 = cntl_byte_encode[(int)type];
        fr->pdu_cntl.byte1 = cntl_byte_encode[(int)type];
 
 
        if (pf)
        if (pf)
                fr->i_hdr.i_pflag = 1; /* p/f and seq numbers */
                fr->i_hdr.i_pflag = 1; /* p/f and seq numbers */
        fr->i_hdr.nr = lp->vr;
        fr->i_hdr.nr = lp->vr;
        fr->i_hdr.ns = lp->vs;
        fr->i_hdr.ns = lp->vs;
        lp->vs++;
        lp->vs++;
        if (lp->vs > 127)
        if (lp->vs > 127)
                lp->vs = 0;
                lp->vs = 0;
        lp->dev->hard_header(skb, lp->dev, ETH_P_802_3,
        lp->dev->hard_header(skb, lp->dev, ETH_P_802_3,
                lp->remote_mac, NULL, skb->len);
                lp->remote_mac, NULL, skb->len);
        ADD_TO_RTQ(skb);                /* add skb to the retransmit queue */
        ADD_TO_RTQ(skb);                /* add skb to the retransmit queue */
        tmp=skb_clone(skb, GFP_ATOMIC);
        tmp=skb_clone(skb, GFP_ATOMIC);
        if(tmp!=NULL)
        if(tmp!=NULL)
        {
        {
                tmp->dev=lp->dev;
                tmp->dev=lp->dev;
                dev_queue_xmit(tmp);
                dev_queue_xmit(tmp);
        }
        }
}
}
 
 
 
 
/*
/*
 *      Resend_ipdu() will resend the pdus in the retransmit queue (rtq)
 *      Resend_ipdu() will resend the pdus in the retransmit queue (rtq)
 *      the return value is the number of pdus resend.
 *      the return value is the number of pdus resend.
 *      ack_nr is N(R) of 1st pdu to resent.
 *      ack_nr is N(R) of 1st pdu to resent.
 *      Type is I_CMD or I_RSP for 1st pdu resent.
 *      Type is I_CMD or I_RSP for 1st pdu resent.
 *      p is p/f flag 0 or 1 for 1st pdu resent.
 *      p is p/f flag 0 or 1 for 1st pdu resent.
 *      All subsequent pdus will be sent as I_CMDs with p/f set to 0
 *      All subsequent pdus will be sent as I_CMDs with p/f set to 0
 */
 */
 
 
int llc_resend_ipdu(llcptr lp, unsigned char ack_nr, unsigned char type, char p)
int llc_resend_ipdu(llcptr lp, unsigned char ack_nr, unsigned char type, char p)
{
{
        struct sk_buff *skb,*tmp;
        struct sk_buff *skb,*tmp;
        int resend_count;
        int resend_count;
        frameptr fr;
        frameptr fr;
        unsigned long flags;
        unsigned long flags;
 
 
 
 
        resend_count = 0;
        resend_count = 0;
 
 
        save_flags(flags);
        save_flags(flags);
        cli();
        cli();
 
 
        skb = skb_peek(&lp->rtq);
        skb = skb_peek(&lp->rtq);
 
 
        while(skb && skb != (struct sk_buff *)&lp->rtq)
        while(skb && skb != (struct sk_buff *)&lp->rtq)
        {
        {
                fr = (frameptr) (skb->data + lp->dev->hard_header_len);
                fr = (frameptr) (skb->data + lp->dev->hard_header_len);
                if (resend_count == 0)
                if (resend_count == 0)
                {
                {
                        /*
                        /*
                         *      Resending 1st pdu:
                         *      Resending 1st pdu:
                         */
                         */
 
 
                        if (p)
                        if (p)
                                fr->i_hdr.i_pflag = 1;
                                fr->i_hdr.i_pflag = 1;
                        else
                        else
                                fr->i_hdr.i_pflag = 0;
                                fr->i_hdr.i_pflag = 0;
 
 
                        if (type == I_CMD)
                        if (type == I_CMD)
                                fr->pdu_hdr.ssap = fr->pdu_hdr.ssap & 0xfe;
                                fr->pdu_hdr.ssap = fr->pdu_hdr.ssap & 0xfe;
                        else
                        else
                                fr->pdu_hdr.ssap = fr->pdu_hdr.ssap | 0x01;
                                fr->pdu_hdr.ssap = fr->pdu_hdr.ssap | 0x01;
                }
                }
                else
                else
                {
                {
                        /*
                        /*
                         *      Resending pdu 2...n
                         *      Resending pdu 2...n
                         */
                         */
 
 
                        fr->pdu_hdr.ssap = fr->pdu_hdr.ssap & 0xfe;
                        fr->pdu_hdr.ssap = fr->pdu_hdr.ssap & 0xfe;
                        fr->i_hdr.i_pflag = 0;
                        fr->i_hdr.i_pflag = 0;
                }
                }
                fr->i_hdr.nr = lp->vr;
                fr->i_hdr.nr = lp->vr;
                fr->i_hdr.ns = lp->vs;
                fr->i_hdr.ns = lp->vs;
                lp->vs++;
                lp->vs++;
                if (lp->vs > 127)
                if (lp->vs > 127)
                        lp->vs = 0;
                        lp->vs = 0;
                tmp=skb_clone(skb, GFP_ATOMIC);
                tmp=skb_clone(skb, GFP_ATOMIC);
                if(tmp!=NULL)
                if(tmp!=NULL)
                {
                {
                        tmp->dev = lp->dev;
                        tmp->dev = lp->dev;
                        dev_queue_xmit(tmp);
                        dev_queue_xmit(tmp);
                }
                }
                resend_count++;
                resend_count++;
                skb = skb->next;
                skb = skb->next;
        }
        }
        restore_flags(flags);
        restore_flags(flags);
        return resend_count;
        return resend_count;
}
}
 
 
/* ************** internal queue management code ****************** */
/* ************** internal queue management code ****************** */
 
 
 
 
/*
/*
 *      Remove one skb from the front of the awaiting transmit queue
 *      Remove one skb from the front of the awaiting transmit queue
 *      (this is the skb longest on the queue) and return a pointer to
 *      (this is the skb longest on the queue) and return a pointer to
 *      that skb.
 *      that skb.
 */
 */
 
 
struct sk_buff *llc_pull_from_atq(llcptr lp)
struct sk_buff *llc_pull_from_atq(llcptr lp)
{
{
        return skb_dequeue(&lp->atq);
        return skb_dequeue(&lp->atq);
}
}
 
 
/*
/*
 *      Free_acknowledged_skbs(), remove from retransmit queue (rtq)
 *      Free_acknowledged_skbs(), remove from retransmit queue (rtq)
 *      and free all skbs with an N(S) chronologicaly before 'pdu_ack'.
 *      and free all skbs with an N(S) chronologicaly before 'pdu_ack'.
 *      The return value is the number of pdus acknowledged.
 *      The return value is the number of pdus acknowledged.
 */
 */
 
 
int llc_free_acknowledged_skbs(llcptr lp, unsigned char pdu_ack)
int llc_free_acknowledged_skbs(llcptr lp, unsigned char pdu_ack)
{
{
        struct sk_buff *pp;
        struct sk_buff *pp;
        frameptr fr;
        frameptr fr;
        int ack_count;
        int ack_count;
        unsigned char ack;      /* N(S) of most recently ack'ed pdu */
        unsigned char ack;      /* N(S) of most recently ack'ed pdu */
        unsigned char ns_save;
        unsigned char ns_save;
        unsigned long flags;
        unsigned long flags;
 
 
        if (pdu_ack > 0)
        if (pdu_ack > 0)
                ack = pdu_ack -1;
                ack = pdu_ack -1;
        else
        else
                ack = 127;
                ack = 127;
 
 
        ack_count = 0;
        ack_count = 0;
 
 
        save_flags(flags);
        save_flags(flags);
        cli();
        cli();
 
 
        pp = skb_dequeue(&lp->rtq);
        pp = skb_dequeue(&lp->rtq);
        while (pp != NULL)
        while (pp != NULL)
        {
        {
                /*
                /*
                 *      Locate skb with N(S) == ack
                 *      Locate skb with N(S) == ack
                 */
                 */
 
 
                /*
                /*
                 *      BUG: FIXME - use skb->h.*
                 *      BUG: FIXME - use skb->h.*
                 */
                 */
                fr = (frameptr) (pp->data + lp->dev->hard_header_len);
                fr = (frameptr) (pp->data + lp->dev->hard_header_len);
                ns_save = fr->i_hdr.ns;
                ns_save = fr->i_hdr.ns;
 
 
                kfree_skb(pp);
                kfree_skb(pp);
                ack_count++;
                ack_count++;
 
 
                if (ns_save == ack)
                if (ns_save == ack)
                        break;
                        break;
                pp = skb_dequeue(&lp->rtq);
                pp = skb_dequeue(&lp->rtq);
        }
        }
        restore_flags(flags);
        restore_flags(flags);
        return ack_count;
        return ack_count;
}
}
 
 
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.