cleanup
[linux-2.4.21-pre4.git] / net / 802 / cl2llc.pre
1 /*
2  * NET          An implementation of the IEEE 802.2 LLC protocol for the
3  *              LINUX operating system.  LLC is implemented as a set of 
4  *              state machines and callbacks for higher networking layers.
5  *
6  *              Class 2 llc algorithm.
7  *              Pseudocode interpreter, transition table lookup,
8  *                      data_request & indicate primitives...
9  *
10  *              Code for initialization, termination, registration and 
11  *              MAC layer glue.
12  *
13  *              Copyright Tim Alpaerts, 
14  *                      <Tim_Alpaerts@toyota-motor-europe.com>
15  *
16  *              This program is free software; you can redistribute it and/or
17  *              modify it under the terms of the GNU General Public License
18  *              as published by the Free Software Foundation; either version
19  *              2 of the License, or (at your option) any later version.
20  *
21  *      Changes
22  *              Alan Cox        :       Chainsawed into Linux format
23  *                                      Modified to use llc_ names
24  *                                      Changed callbacks
25  *
26  *      This file must be processed by sed before it can be compiled.
27  */
28
29 #include <linux/types.h>
30 #include <linux/kernel.h>
31 #include <linux/slab.h>
32 #include <linux/netdevice.h>
33 #include <linux/skbuff.h>
34 #include <net/p8022.h>
35 #include <linux/proc_fs.h>
36 #include <linux/stat.h>
37 #include <asm/byteorder.h>
38
39 #include "pseudo/pseudocode.h"
40 #include "transit/pdutr.h"
41 #include "transit/timertr.h"
42 #include <net/llc_frame.h>
43 #include <net/llc.h>
44
45 /*
46  *      Data_request() is called by the client to present a data unit
47  *      to the llc for transmission.
48  *      In the future this function should also check if the transmit window
49  *      allows the sending of another pdu, and if not put the skb on the atq
50  *      for deferred sending.
51  */
52
53 int llc_data_request(llcptr lp, struct sk_buff *skb)
54 {
55         if (skb_headroom(skb) < (lp->dev->hard_header_len +4)){
56                 printk("cl2llc: data_request() not enough headroom in skb\n");
57                 return -1;
58         };
59
60         skb_push(skb, 4);
61
62         if ((lp->state != NORMAL) && (lp->state != BUSY) && (lp->state != REJECT))
63         {
64                 printk("cl2llc: data_request() while no llc connection\n"); 
65                 return -1;  
66         }
67
68         if (lp->remote_busy)
69         {     /* if the remote llc is BUSY, */
70                 ADD_TO_ATQ(skb);      /* save skb in the await transmit queue */
71                 return 0;
72         }                           
73         else
74         {
75                 /*
76                  *      Else proceed with xmit 
77                  */
78
79                 switch(lp->state)
80                 {
81                         case NORMAL:
82                                 if(lp->p_flag)
83                                         llc_interpret_pseudo_code(lp, NORMAL2, skb, NO_FRAME);
84                                 else
85                                         llc_interpret_pseudo_code(lp, NORMAL1, skb, NO_FRAME);
86                                 break;
87                         case BUSY:
88                                 if (lp->p_flag)
89                                         llc_interpret_pseudo_code(lp, BUSY2, skb, NO_FRAME);
90                                 else
91                                         llc_interpret_pseudo_code(lp, BUSY1, skb, NO_FRAME);
92                                 break;
93                         case REJECT:
94                                 if (lp->p_flag)
95                                         llc_interpret_pseudo_code(lp, REJECT2, skb, NO_FRAME);
96                                 else
97                                         llc_interpret_pseudo_code(lp, REJECT1, skb, NO_FRAME);
98                                 break;
99                         default:;
100                 }
101                 if(lp->llc_callbacks)
102                 {
103                         lp->llc_event(lp);
104                         lp->llc_callbacks=0;
105                 }
106                 return 0;  
107         }              
108 }
109
110
111
112 /* 
113  *      Disconnect_request() requests that the llc to terminate a connection
114  */
115
116 void disconnect_request(llcptr lp)
117 {
118         if ((lp->state == NORMAL) ||
119                 (lp->state == BUSY) ||
120                 (lp->state == REJECT) ||
121                 (lp->state == AWAIT) ||
122                 (lp->state == AWAIT_BUSY) ||
123                 (lp->state == AWAIT_REJECT))
124         {
125                 lp->state = D_CONN;
126                 llc_interpret_pseudo_code(lp, SH1, NULL, NO_FRAME);
127                 if(lp->llc_callbacks)
128                 {
129                         lp->llc_event(lp);
130                         lp->llc_callbacks=0;
131                 }
132                 /*
133                  *      lp may be invalid after the callback
134                  */
135         }
136 }
137
138
139 /*
140  *      Connect_request() requests that the llc to start a connection
141  */
142
143 void connect_request(llcptr lp)
144 {
145         if (lp->state == ADM)
146         {
147                 lp->state = SETUP;
148                 llc_interpret_pseudo_code(lp, ADM1, NULL, NO_FRAME);
149                 if(lp->llc_callbacks)
150                 {
151                         lp->llc_event(lp);
152                         lp->llc_callbacks=0;
153                 }
154                 /*
155                  *      lp may be invalid after the callback
156                  */
157         }
158 }
159
160
161 /*
162  *      Interpret_pseudo_code() executes the actions in the connection component
163  *      state transition table. Table 4 in document on p88.
164  *
165  *      If this function is called to handle an incoming pdu, skb will point
166  *      to the buffer with the pdu and type will contain the decoded pdu type.
167  *
168  *      If called by data_request skb points to an skb that was skb_alloc-ed by 
169  *      the llc client to hold the information unit to be transmitted, there is
170  *      no valid type in this case.  
171  *
172  *      If called because a timer expired no skb is passed, and there is no 
173  *      type.
174  */
175
176 void llc_interpret_pseudo_code(llcptr lp, int pc_label, struct sk_buff *skb, 
177                 char type)
178 {    
179         short int pc;   /* program counter in pseudo code array */ 
180         char p_flag_received;
181         frameptr fr;
182         int resend_count;   /* number of pdus resend by llc_resend_ipdu() */
183         int ack_count;      /* number of pdus acknowledged */
184         struct sk_buff *skb2;
185
186         if (skb != NULL) 
187         {
188                 fr = (frameptr) skb->data;
189         }
190         else
191                 fr = NULL;
192
193         pc = pseudo_code_idx[pc_label];
194         while(pseudo_code[pc])
195         {
196                 switch(pseudo_code[pc])
197                 {
198                         case IF_F=1_CLEAR_REMOTE_BUSY:
199                                 if ((type != I_CMD) || (fr->i_hdr.i_pflag == 0))
200                                         break;
201                         case CLEAR_REMOTE_BUSY:
202                                 lp->remote_busy = 0;
203                                 llc_stop_timer(lp, BUSY_TIMER);
204                                 if ((lp->state == NORMAL) ||
205                                         (lp->state == REJECT) ||
206                                         (lp->state == BUSY))
207                                 {
208                                         skb2 = llc_pull_from_atq(lp);
209                                         if (skb2 != NULL) 
210                                                 llc_start_timer(lp, ACK_TIMER);
211                                         while (skb2 != NULL)
212                                         {
213                                                 llc_sendipdu( lp, I_CMD, 0, skb2);
214                                                 skb2 = llc_pull_from_atq(lp);
215                                         }
216                                 }          
217                                 break;
218                         case CONNECT_INDICATION:
219                                 lp->state = NORMAL;  /* needed to eliminate connect_response() */
220                                 lp->llc_mode = MODE_ABM;
221                                 lp->llc_callbacks|=LLC_CONN_INDICATION;
222                                 break;
223                         case CONNECT_CONFIRM:
224                                 lp->llc_mode = MODE_ABM;
225                                 lp->llc_callbacks|=LLC_CONN_CONFIRM;
226                                 break;
227                         case DATA_INDICATION:
228                                 skb_pull(skb, 4);
229                                 lp->inc_skb=skb;
230                                 lp->llc_callbacks|=LLC_DATA_INDIC;
231                                 break;
232                         case DISCONNECT_INDICATION:
233                                 lp->llc_mode = MODE_ADM;
234                                 lp->llc_callbacks|=LLC_DISC_INDICATION;
235                                 break;
236                         case RESET_INDICATION(LOCAL):
237                                 lp->llc_callbacks|=LLC_RESET_INDIC_LOC;
238                                 break;
239                         case RESET_INDICATION(REMOTE):
240                                 lp->llc_callbacks|=LLC_RESET_INDIC_REM;
241                                 break;
242                         case RESET_CONFIRM:
243                                 lp->llc_callbacks|=LLC_RST_CONFIRM;
244                                 break;
245                         case REPORT_STATUS(FRMR_RECEIVED):
246                                 lp->llc_callbacks|=LLC_FRMR_RECV;
247                                 break;
248                         case REPORT_STATUS(FRMR_SENT):
249                                 lp->llc_callbacks|=LLC_FRMR_SENT;
250                                 break;
251                         case REPORT_STATUS(REMOTE_BUSY):
252                                 lp->llc_callbacks|=LLC_REMOTE_BUSY;
253                                 break;
254                         case REPORT_STATUS(REMOTE_NOT_BUSY):
255                                 lp->llc_callbacks|=LLC_REMOTE_NOTBUSY;
256                                 break;
257                         case SEND_DISC_CMD(P=X):
258                                 llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL);
259                                 break;
260                         case SEND_DM_RSP(F=X):
261                                 llc_sendpdu(lp, DM_RSP, 0, 0, NULL);
262                                 break;
263                         case SEND_FRMR_RSP(F=X):                        
264                                 lp->frmr_info_fld.cntl1 = fr->pdu_cntl.byte1;
265                                 lp->frmr_info_fld.cntl2 = fr->pdu_cntl.byte2;
266                                 lp->frmr_info_fld.vs = lp->vs;
267                                 lp->frmr_info_fld.vr_cr = lp->vr;
268                                 llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
269                                 break;
270                         case RE-SEND_FRMR_RSP(F=0):
271                                 llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
272                                 break;
273                         case RE-SEND_FRMR_RSP(F=P):
274                                 llc_sendpdu(lp, FRMR_RSP, lp->p_flag,
275                                         5, (char *) &lp->frmr_info_fld);
276                                 break;
277                         case SEND_I_CMD(P=1):
278                                 llc_sendipdu(lp, I_CMD, 1, skb);   
279                                 break;
280                         case RE-SEND_I_CMD(P=1):
281                                 resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
282                                 break;
283                         case RE-SEND_I_CMD(P=1)_OR_SEND_RR:
284                                 resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
285                                 if (resend_count == 0) 
286                                 {
287                                         llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
288                                 }    
289                                 break;
290                         case SEND_I_XXX(X=0):
291                                 llc_sendipdu(lp, I_CMD, 0, skb);   
292                                 break;
293                         case RE-SEND_I_XXX(X=0):
294                                 resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
295                                 break;
296                         case RE-SEND_I_XXX(X=0)_OR_SEND_RR:
297                                 resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
298                                 if (resend_count == 0) 
299                                 {
300                                         llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
301                                 }    
302                                 break;
303                         case RE-SEND_I_RSP(F=1):
304                                 resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_RSP, 1);
305                                 break;
306                         case SEND_REJ_CMD(P=1):
307                                 llc_sendpdu(lp, REJ_CMD, 1, 0, NULL);
308                                 break;
309                         case SEND_REJ_RSP(F=1):
310                                 llc_sendpdu(lp, REJ_RSP, 1, 0, NULL);
311                                 break;
312                         case SEND_REJ_XXX(X=0):
313                                 if (IS_RSP(fr))
314                                         llc_sendpdu(lp, REJ_CMD, 0, 0, NULL);
315                                 else
316                                         llc_sendpdu(lp, REJ_RSP, 0, 0, NULL);
317                                 break;
318                         case SEND_RNR_CMD(F=1):
319                                 llc_sendpdu(lp, RNR_CMD, 1, 0, NULL);
320                                 break;
321                         case SEND_RNR_RSP(F=1):
322                                 llc_sendpdu(lp, RNR_RSP, 1, 0, NULL);
323                                 break;
324                         case SEND_RNR_XXX(X=0):
325                                 if (IS_RSP(fr))
326                                         llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
327                                 else
328                                         llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
329                                 break;
330                         case SET_REMOTE_BUSY:
331                                 if (lp->remote_busy == 0)
332                                 {
333                                         lp->remote_busy = 1;
334                                         llc_start_timer(lp, BUSY_TIMER);
335                                         lp->llc_callbacks|=LLC_REMOTE_BUSY;
336                                 }
337                                 else if (lp->timer_state[BUSY_TIMER] == TIMER_IDLE)
338                                 {
339                                         llc_start_timer(lp, BUSY_TIMER);
340                                 }
341                                 break;
342                         case OPTIONAL_SEND_RNR_XXX(X=0):
343                                 if (IS_RSP(fr)) 
344                                         llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
345                                 else
346                                         llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
347                                 break;
348                         case SEND_RR_CMD(P=1):
349                                 llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
350                                 break;
351                         case SEND_ACKNOWLEDGE_CMD(P=1):
352                                 llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
353                                 break;
354                         case SEND_RR_RSP(F=1):
355                                 llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
356                                 break;
357                         case SEND_ACKNOWLEDGE_RSP(F=1):
358                                 llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
359                                 break;
360                         case SEND_RR_XXX(X=0):
361                                 llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
362                                 break;
363                         case SEND_ACKNOWLEDGE_XXX(X=0):
364                                 if (IS_RSP(fr)) 
365                                         llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
366                                 else
367                                         llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
368                                 break;
369                         case SEND_SABME_CMD(P=X):
370                                 llc_sendpdu(lp, SABME_CMD, 0, 0, NULL);
371                                 lp->f_flag = 0;
372                                 break;
373                         case SEND_UA_RSP(F=X):
374                                 llc_sendpdu(lp, UA_RSP, lp->f_flag, 0, NULL);
375                                 break;
376                         case S_FLAG:=0:
377                                 lp->s_flag = 0;
378                                 break;
379                         case S_FLAG:=1:
380                                 lp->s_flag = 1;
381                                 break;
382                         case START_P_TIMER:
383                                 if(lp->timer_state[P_TIMER] == TIMER_RUNNING)
384                                         llc_stop_timer(lp, P_TIMER);
385                                 llc_start_timer(lp, P_TIMER);
386                                 if (lp->p_flag == 0)
387                                 {
388                                         lp->retry_count = 0;
389                                         lp->p_flag = 1;
390                                 }
391                                 break;
392                         case START_ACK_TIMER_IF_NOT_RUNNING:
393                                 if (lp->timer_state[ACK_TIMER] == TIMER_IDLE)
394                                         llc_start_timer(lp, ACK_TIMER);
395                                 break;
396                         case START_ACK_TIMER:
397                                 llc_start_timer(lp, ACK_TIMER);
398                                 break;
399                         case START_REJ_TIMER:
400                                 llc_start_timer(lp, REJ_TIMER);
401                                 break;
402                         case STOP_ACK_TIMER:
403                                 llc_stop_timer(lp, ACK_TIMER);
404                                 break;
405                         case STOP_P_TIMER:
406                                 llc_stop_timer(lp, ACK_TIMER);
407                                 lp->p_flag = 0;
408                                 break;
409                         case IF_DATA_FLAG=2_STOP_REJ_TIMER:
410                                 if (lp->data_flag == 2)
411                                         llc_stop_timer(lp, REJ_TIMER);
412                                 break;
413                         case STOP_REJ_TIMER:
414                                 llc_stop_timer(lp, REJ_TIMER);
415                                 break;
416                         case STOP_ALL_TIMERS:
417                                 llc_stop_timer(lp, ACK_TIMER);
418                                 llc_stop_timer(lp, P_TIMER);
419                                 llc_stop_timer(lp, REJ_TIMER);
420                                 llc_stop_timer(lp, BUSY_TIMER);
421                                 break;
422                         case STOP_OTHER_TIMERS:
423                                 llc_stop_timer(lp, P_TIMER);
424                                 llc_stop_timer(lp, REJ_TIMER);
425                                 llc_stop_timer(lp, BUSY_TIMER);
426                                 break;
427                         case UPDATE_N(R)_RECEIVED:             
428                                 ack_count = llc_free_acknowledged_skbs(lp,
429                                         (unsigned char) fr->s_hdr.nr);
430                                 if (ack_count > 0)
431                                 {
432                                         lp->retry_count = 0;
433                                         llc_stop_timer(lp, ACK_TIMER);  
434                                         if (skb_peek(&lp->rtq) != NULL)
435                                         {
436                                                 /*
437                                                  *      Re-transmit queue not empty 
438                                                  */
439                                                 llc_start_timer(lp, ACK_TIMER);  
440                                         }
441                                 }        
442                                 break;
443                         case UPDATE_P_FLAG:
444                                 if (IS_UFRAME(fr)) 
445                                         p_flag_received = fr->u_hdr.u_pflag;
446                                 else
447                                         p_flag_received = fr->i_hdr.i_pflag;
448                                 if ((fr->pdu_hdr.ssap & 0x01) && (p_flag_received))
449                                 {
450                                         lp->p_flag = 0;
451                                         llc_stop_timer(lp, P_TIMER);  
452                                 }
453                                 break;
454                         case DATA_FLAG:=2:
455                                 lp->data_flag = 2;
456                                 break;
457                         case DATA_FLAG:=0:
458                                 lp->data_flag = 0;
459                                 break;
460                         case DATA_FLAG:=1:
461                                 lp->data_flag = 1;
462                                 break;
463                         case IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1:
464                                 if (lp->data_flag == 0)
465                                         lp->data_flag = 1;
466                                 break;
467                         case P_FLAG:=0:
468                                 lp->p_flag = 0;
469                                 break;
470                         case P_FLAG:=P:
471                                 lp->p_flag = lp->f_flag;
472                                 break;
473                         case REMOTE_BUSY:=0:
474                                 lp->remote_busy = 0;
475                                 break;
476                         case RETRY_COUNT:=0:
477                                 lp->retry_count = 0;
478                                 break;
479                         case RETRY_COUNT:=RETRY_COUNT+1:
480                                 lp->retry_count++;
481                                 break;
482                         case V(R):=0:
483                                 lp->vr = 0;
484                                 break;
485                         case V(R):=V(R)+1:
486                                 lp->vr++;
487                                 break;
488                         case V(S):=0:
489                                 lp->vs = 0;
490                                 break;
491                         case V(S):=N(R):
492                                 lp->vs = fr->i_hdr.nr;
493                                 break;
494                         case F_FLAG:=P:
495                                 if (IS_UFRAME(fr)) 
496                                         lp->f_flag = fr->u_hdr.u_pflag;
497                                 else
498                                         lp->f_flag = fr->i_hdr.i_pflag;
499                                 break;
500                         default:;
501                 }
502                 pc++;   
503         }
504 }
505
506
507 /*
508  *      Process_otype2_frame will handle incoming frames
509  *      for 802.2 Type 2 Procedure.
510  */
511
512 void llc_process_otype2_frame(llcptr lp, struct sk_buff *skb, char type)
513 {
514         int idx;                /*      index in transition table */
515         int pc_label;           /*      action to perform, from tr tbl */
516         int validation;         /*      result of validate_seq_nos */
517         int p_flag_received;    /*      p_flag in received frame */
518         frameptr fr;
519
520         fr = (frameptr) skb->data;
521
522         if (IS_UFRAME(fr))
523                 p_flag_received = fr->u_hdr.u_pflag;
524         else
525                 p_flag_received = fr->i_hdr.i_pflag;
526
527         switch(lp->state)
528         {
529                 /*      Compute index in transition table: */
530                 case ADM:
531                         idx = type;
532                         idx = (idx << 1) + p_flag_received;
533                         break;
534                 case CONN:
535                 case RESET_WAIT:
536                 case RESET_CHECK:
537                 case ERROR:
538                         idx = type;
539                         break;
540                 case SETUP:
541                 case RESET:
542                 case D_CONN:
543                         idx = type;
544                         idx = (idx << 1) + lp->p_flag;
545                         break;
546                 case NORMAL:
547                 case BUSY:
548                 case REJECT:
549                 case AWAIT:
550                 case AWAIT_BUSY:
551                 case AWAIT_REJECT:
552                         validation = llc_validate_seq_nos(lp, fr);
553                         if (validation > 3) 
554                                 type = BAD_FRAME;
555                         idx = type;
556                         idx = (idx << 1);
557                         if (validation & 1) 
558                                 idx = idx +1;
559                         idx = (idx << 1) + p_flag_received;
560                         idx = (idx << 1) + lp->p_flag;
561                 default:
562                         printk("llc_proc: bad state\n");
563                         return;
564         }
565         idx = (idx << 1) + pdutr_offset[lp->state];
566         lp->state = pdutr_entry[idx +1]; 
567         pc_label = pdutr_entry[idx];
568         if (pc_label != NOP)
569         { 
570                 llc_interpret_pseudo_code(lp, pc_label, skb, type);
571                 if(lp->llc_callbacks)
572                 {
573                         lp->llc_event(lp);
574                         lp->llc_callbacks=0;
575                 }
576                 /*
577                  *      lp may no longer be valid after this point. Be
578                  *      careful what is added!
579                  */
580         }
581 }
582
583
584 void llc_timer_expired(llcptr lp, int t)
585 {
586         int idx;                /* index in transition table    */
587         int pc_label;           /* action to perform, from tr tbl */
588
589         lp->timer_state[t] = TIMER_EXPIRED;
590         idx = lp->state;            /* Compute index in transition table: */
591         idx = (idx << 2) + t;
592         idx = idx << 1;
593         if (lp->retry_count >= lp->n2) 
594                 idx = idx + 1;
595         idx = (idx << 1) + lp->s_flag;
596         idx = (idx << 1) + lp->p_flag;
597         idx = idx << 1;             /* 2 bytes per entry: action & newstate */
598
599         pc_label = timertr_entry[idx];
600         if (pc_label != NOP)
601         {
602                 llc_interpret_pseudo_code(lp, pc_label, NULL, NO_FRAME);
603                 lp->state = timertr_entry[idx +1];
604         }
605         lp->timer_state[t] = TIMER_IDLE;
606         if(lp->llc_callbacks)
607         {
608                 lp->llc_event(lp);
609                 lp->llc_callbacks=0;
610         }
611         /*
612          *      And lp may have vanished in the event callback
613          */
614 }
615