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  *		Code for initialization, termination, registration and
7  *		MAC layer glue.
8  *
9  *		Written by Tim Alpaerts, Tim_Alpaerts@toyota-motor-europe.com
10  *
11  *		This program is free software; you can redistribute it and/or
12  *		modify it under the terms of the GNU General Public License
13  *		as published by the Free Software Foundation; either version
14  *		2 of the License, or (at your option) any later version.
15  *
16  *	Changes
17  *		Alan Cox	:	Chainsawed to Linux format
18  *					Added llc_ to names
19  *					Started restructuring handlers
20  *
21  *              Horst von Brand :      Add #include <linux/string.h>
22  */
23 
24 #include <linux/module.h>
25 #include <linux/version.h>
26 #include <linux/kernel.h>
27 #include <linux/slab.h>
28 #include <linux/unistd.h>
29 #include <linux/string.h>
30 #include <linux/netdevice.h>
31 #include <linux/init.h>
32 #include <net/p8022.h>
33 
34 #include <asm/byteorder.h>
35 
36 #include <net/llc_frame.h>
37 #include <net/llc.h>
38 
39 /*
40  *	All incoming frames pass thru mac_data_indicate().
41  *	On entry the llc structure related to the frame is passed as parameter.
42  *	The received sk_buffs with pdus other than I_CMD and I_RSP
43  *	are freed by mac_data_indicate() after processing,
44  *	the I pdu buffers are freed by the cl2llc client when it no longer needs
45  *	the skb.
46 */
47 
llc_mac_data_indicate(llcptr lp,struct sk_buff * skb)48 int llc_mac_data_indicate(llcptr lp, struct sk_buff *skb)
49 {
50 	int ll;      		/* logical length == 802.3 length field */
51 	unsigned char p_flag;
52 	unsigned char type;
53 	frameptr fr;
54 	int free=1;
55 
56 	lp->inc_skb=NULL;
57 
58 	/*
59 	 *	Truncate buffer to true 802.3 length
60 	 *	[FIXME: move to 802.2 demux]
61 	 */
62 
63 	ll = *(skb->data -2) * 256 + *(skb->data -1);
64 	skb_trim( skb, ll );
65 
66 	fr = (frameptr) skb->data;
67 	type = llc_decode_frametype( fr );
68 
69 
70 	if (type <= FRMR_RSP)
71 	{
72 		/*
73 		 *	PDU is of the type 2 set
74 		 */
75 		if ((lp->llc_mode == MODE_ABM)||(type == SABME_CMD))
76 			llc_process_otype2_frame(lp, skb, type);
77 
78 	}
79 	else
80 	{
81 		/*
82 		 *	PDU belongs to type 1 set
83 		 */
84 	        p_flag = fr->u_hdr.u_pflag;
85         	switch(type)
86         	{
87 		        case TEST_CMD:
88 				llc_sendpdu(lp, TEST_RSP, 0,ll -3,
89 					fr->u_hdr.u_info);
90 				break;
91 			case TEST_RSP:
92 				lp->llc_callbacks|=LLC_TEST_INDICATION;
93 				lp->inc_skb=skb;
94 				free=0;
95 				break;
96 			case XID_CMD:
97 				/*
98 				 *	Basic format XID is handled by LLC itself
99 				 *	Doc 5.4.1.1.2 p 48/49
100 				 */
101 
102 				if ((ll == 6)&&(fr->u_hdr.u_info[0] == 0x81))
103 				{
104 					lp->k = fr->u_hdr.u_info[2];
105 					llc_sendpdu(lp, XID_RSP,
106 						fr->u_hdr.u_pflag, ll -3,
107 						fr->u_hdr.u_info);
108 				}
109 				break;
110 
111 			case XID_RSP:
112 				if( ll == 6 && fr->u_hdr.u_info[0] == 0x81 )
113 				{
114 					lp->k = fr->u_hdr.u_info[2];
115 				}
116 				lp->llc_callbacks|=LLC_XID_INDICATION;
117 				lp->inc_skb=skb;
118 				free=0;
119 				break;
120 
121 			case UI_CMD:
122 				lp->llc_callbacks|=LLC_UI_DATA;
123 				skb_pull(skb,3);
124 				lp->inc_skb=skb;
125 				free=0;
126 				break;
127 
128 			default:;
129 				/*
130 				 *	All other type 1 pdus ignored for now
131 				 */
132 		}
133 	}
134 
135 	if (free&&(!(IS_IFRAME(fr))))
136 	{
137 		/*
138 		 *	No auto free for I pdus
139 		 */
140 		skb->sk = NULL;
141 		kfree_skb(skb);
142 	}
143 
144 	if(lp->llc_callbacks)
145 	{
146 		if ( lp->llc_event != NULL ) lp->llc_event(lp);
147 		lp->llc_callbacks=0;
148 	}
149 	return 0;
150 }
151 
152 
153 /*
154  *	Create an LLC client. As it is the job of the caller to clean up
155  *	LLC's on device down, the device list must be locked before this call.
156  */
157 
register_cl2llc_client(llcptr lp,const char * device,void (* event)(llcptr),u8 * rmac,u8 ssap,u8 dsap)158 int register_cl2llc_client(llcptr lp, const char *device, void (*event)(llcptr), u8 *rmac, u8 ssap, u8 dsap)
159 {
160 	char eye_init[] = "LLC\0";
161 
162 	memset(lp, 0, sizeof(*lp));
163 	lp->dev = __dev_get_by_name(device);
164 	if(lp->dev == NULL)
165 		return -ENODEV;
166 	memcpy(lp->eye, eye_init, sizeof(lp->eye));
167 	lp->rw = 1;
168 	lp->k = 127;
169 	lp->n1 = 1490;
170 	lp->n2 = 10;
171 	lp->timer_interval[P_TIMER] = HZ;    /* 1 sec */
172 	lp->timer_interval[REJ_TIMER] = HZ/8;
173 	lp->timer_interval[ACK_TIMER] = HZ/8;
174 	lp->timer_interval[BUSY_TIMER] = HZ*2;
175 	lp->local_sap = ssap;
176 	lp->llc_event = event;
177 	memcpy(lp->remote_mac, rmac, sizeof(lp->remote_mac));
178 	lp->state = 0;
179 	lp->llc_mode = MODE_ADM;
180 	lp->remote_sap = dsap;
181 	skb_queue_head_init(&lp->atq);
182 	skb_queue_head_init(&lp->rtq);
183 	MOD_INC_USE_COUNT;
184 	return 0;
185 }
186 
187 
unregister_cl2llc_client(llcptr lp)188 void unregister_cl2llc_client(llcptr lp)
189 {
190 	llc_cancel_timers(lp);
191 	MOD_DEC_USE_COUNT;
192 	kfree(lp);
193 }
194 
195 
196 EXPORT_SYMBOL(register_cl2llc_client);
197 EXPORT_SYMBOL(unregister_cl2llc_client);
198 EXPORT_SYMBOL(llc_data_request);
199 EXPORT_SYMBOL(llc_unit_data_request);
200 EXPORT_SYMBOL(llc_test_request);
201 EXPORT_SYMBOL(llc_xid_request);
202 EXPORT_SYMBOL(llc_mac_data_indicate);
203 EXPORT_SYMBOL(llc_cancel_timers);
204 
205 #define ALL_TYPES_8022 0
206 
llc_init(void)207 static int __init llc_init(void)
208 {
209 	printk(KERN_NOTICE "IEEE 802.2 LLC for Linux 2.1 (c) 1996 Tim Alpaerts\n");
210 	return 0;
211 }
212 
213 
214 module_init(llc_init);
215