1/********************************************************************* 2 * 3 * Filename: irlan_provider.c 4 * Version: 0.9 5 * Description: IrDA LAN Access Protocol Implementation 6 * Status: Experimental. 7 * Author: Dag Brattli <dagb@cs.uit.no> 8 * Created at: Sun Aug 31 20:14:37 1997 9 * Modified at: Sat Oct 30 12:52:10 1999 10 * Modified by: Dag Brattli <dagb@cs.uit.no> 11 * Sources: skeleton.c by Donald Becker <becker@CESDIS.gsfc.nasa.gov> 12 * slip.c by Laurence Culhane, <loz@holmes.demon.co.uk> 13 * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> 14 * 15 * Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>, 16 * All Rights Reserved. 17 * 18 * This program is free software; you can redistribute it and/or 19 * modify it under the terms of the GNU General Public License as 20 * published by the Free Software Foundation; either version 2 of 21 * the License, or (at your option) any later version. 22 * 23 * Neither Dag Brattli nor University of Troms� admit liability nor 24 * provide warranty for any of this software. This material is 25 * provided "AS-IS" and at no charge. 26 * 27 ********************************************************************/ 28 29#include <linux/kernel.h> 30#include <linux/string.h> 31#include <linux/errno.h> 32#include <linux/netdevice.h> 33#include <linux/etherdevice.h> 34#include <linux/init.h> 35#include <linux/random.h> 36#include <linux/bitops.h> 37 38#include <asm/system.h> 39#include <asm/byteorder.h> 40 41#include <net/irda/irda.h> 42#include <net/irda/irttp.h> 43#include <net/irda/irlmp.h> 44#include <net/irda/irias_object.h> 45#include <net/irda/iriap.h> 46#include <net/irda/timer.h> 47 48#include <net/irda/irlan_common.h> 49#include <net/irda/irlan_eth.h> 50#include <net/irda/irlan_event.h> 51#include <net/irda/irlan_provider.h> 52#include <net/irda/irlan_filter.h> 53#include <net/irda/irlan_client.h> 54 55static void irlan_provider_connect_indication(void *instance, void *sap, 56 struct qos_info *qos, 57 __u32 max_sdu_size, 58 __u8 max_header_size, 59 struct sk_buff *skb); 60 61/* 62 * Function irlan_provider_control_data_indication (handle, skb) 63 * 64 * This function gets the data that is received on the control channel 65 * 66 */ 67static int irlan_provider_data_indication(void *instance, void *sap, 68 struct sk_buff *skb) 69{ 70 struct irlan_cb *self; 71 __u8 code; 72 73 IRDA_DEBUG(4, "%s()\n", __FUNCTION__ ); 74 75 self = (struct irlan_cb *) instance; 76 77 IRDA_ASSERT(self != NULL, return -1;); 78 IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); 79 80 IRDA_ASSERT(skb != NULL, return -1;); 81 82 code = skb->data[0]; 83 switch(code) { 84 case CMD_GET_PROVIDER_INFO: 85 IRDA_DEBUG(4, "Got GET_PROVIDER_INFO command!\n"); 86 irlan_do_provider_event(self, IRLAN_GET_INFO_CMD, skb); 87 break; 88 89 case CMD_GET_MEDIA_CHAR: 90 IRDA_DEBUG(4, "Got GET_MEDIA_CHAR command!\n"); 91 irlan_do_provider_event(self, IRLAN_GET_MEDIA_CMD, skb); 92 break; 93 case CMD_OPEN_DATA_CHANNEL: 94 IRDA_DEBUG(4, "Got OPEN_DATA_CHANNEL command!\n"); 95 irlan_do_provider_event(self, IRLAN_OPEN_DATA_CMD, skb); 96 break; 97 case CMD_FILTER_OPERATION: 98 IRDA_DEBUG(4, "Got FILTER_OPERATION command!\n"); 99 irlan_do_provider_event(self, IRLAN_FILTER_CONFIG_CMD, skb); 100 break; 101 case CMD_RECONNECT_DATA_CHAN: 102 IRDA_DEBUG(2, "%s(), Got RECONNECT_DATA_CHAN command\n", __FUNCTION__ ); 103 IRDA_DEBUG(2, "%s(), NOT IMPLEMENTED\n", __FUNCTION__ ); 104 break; 105 case CMD_CLOSE_DATA_CHAN: 106 IRDA_DEBUG(2, "Got CLOSE_DATA_CHAN command!\n"); 107 IRDA_DEBUG(2, "%s(), NOT IMPLEMENTED\n", __FUNCTION__ ); 108 break; 109 default: 110 IRDA_DEBUG(2, "%s(), Unknown command!\n", __FUNCTION__ ); 111 break; 112 } 113 return 0; 114} 115 116/* 117 * Function irlan_provider_connect_indication (handle, skb, priv) 118 * 119 * Got connection from peer IrLAN client 120 * 121 */ 122static void irlan_provider_connect_indication(void *instance, void *sap, 123 struct qos_info *qos, 124 __u32 max_sdu_size, 125 __u8 max_header_size, 126 struct sk_buff *skb) 127{ 128 struct irlan_cb *self; 129 struct tsap_cb *tsap; 130 __u32 saddr, daddr; 131 132 IRDA_DEBUG(0, "%s()\n", __FUNCTION__ ); 133 134 self = (struct irlan_cb *) instance; 135 tsap = (struct tsap_cb *) sap; 136 137 IRDA_ASSERT(self != NULL, return;); 138 IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); 139 140 IRDA_ASSERT(tsap == self->provider.tsap_ctrl,return;); 141 IRDA_ASSERT(self->provider.state == IRLAN_IDLE, return;); 142 143 daddr = irttp_get_daddr(tsap); 144 saddr = irttp_get_saddr(tsap); 145 self->provider.max_sdu_size = max_sdu_size; 146 self->provider.max_header_size = max_header_size; 147 148 irlan_do_provider_event(self, IRLAN_CONNECT_INDICATION, NULL); 149 150 /* 151 * If we are in peer mode, the client may not have got the discovery 152 * indication it needs to make progress. If the client is still in 153 * IDLE state, we must kick it. 154 */ 155 if ((self->provider.access_type == ACCESS_PEER) && 156 (self->client.state == IRLAN_IDLE)) 157 { 158 irlan_client_wakeup(self, self->saddr, self->daddr); 159 } 160} 161 162/* 163 * Function irlan_provider_connect_response (handle) 164 * 165 * Accept incoming connection 166 * 167 */ 168void irlan_provider_connect_response(struct irlan_cb *self, 169 struct tsap_cb *tsap) 170{ 171 IRDA_ASSERT(self != NULL, return;); 172 IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); 173 174 /* Just accept */ 175 irttp_connect_response(tsap, IRLAN_MTU, NULL); 176} 177 178static void irlan_provider_disconnect_indication(void *instance, void *sap, 179 LM_REASON reason, 180 struct sk_buff *userdata) 181{ 182 struct irlan_cb *self; 183 struct tsap_cb *tsap; 184 185 IRDA_DEBUG(4, "%s(), reason=%d\n", __FUNCTION__ , reason); 186 187 self = (struct irlan_cb *) instance; 188 tsap = (struct tsap_cb *) sap; 189 190 IRDA_ASSERT(self != NULL, return;); 191 IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); 192 IRDA_ASSERT(tsap != NULL, return;); 193 IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;); 194 195 IRDA_ASSERT(tsap == self->provider.tsap_ctrl, return;); 196 197 irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL); 198} 199 200/* 201 * Function irlan_parse_open_data_cmd (self, skb) 202 * 203 * 204 * 205 */ 206int irlan_parse_open_data_cmd(struct irlan_cb *self, struct sk_buff *skb) 207{ 208 int ret; 209 210 ret = irlan_provider_parse_command(self, CMD_OPEN_DATA_CHANNEL, skb); 211 212 /* Open data channel */ 213 irlan_open_data_tsap(self); 214 215 return ret; 216} 217 218/* 219 * Function parse_command (skb) 220 * 221 * Extract all parameters from received buffer, then feed them to 222 * check_params for parsing 223 * 224 */ 225int irlan_provider_parse_command(struct irlan_cb *self, int cmd, 226 struct sk_buff *skb) 227{ 228 __u8 *frame; 229 __u8 *ptr; 230 int count; 231 __u16 val_len; 232 int i; 233 char *name; 234 char *value; 235 int ret = RSP_SUCCESS; 236 237 IRDA_ASSERT(skb != NULL, return -RSP_PROTOCOL_ERROR;); 238 239 IRDA_DEBUG(4, "%s(), skb->len=%d\n", __FUNCTION__ , (int)skb->len); 240 241 IRDA_ASSERT(self != NULL, return -RSP_PROTOCOL_ERROR;); 242 IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -RSP_PROTOCOL_ERROR;); 243 244 if (!skb) 245 return -RSP_PROTOCOL_ERROR; 246 247 frame = skb->data; 248 249 name = kmalloc(255, GFP_ATOMIC); 250 if (!name) 251 return -RSP_INSUFFICIENT_RESOURCES; 252 value = kmalloc(1016, GFP_ATOMIC); 253 if (!value) { 254 kfree(name); 255 return -RSP_INSUFFICIENT_RESOURCES; 256 } 257 258 /* How many parameters? */ 259 count = frame[1]; 260 261 IRDA_DEBUG(4, "Got %d parameters\n", count); 262 263 ptr = frame+2; 264 265 /* For all parameters */ 266 for (i=0; i<count;i++) { 267 ret = irlan_extract_param(ptr, name, value, &val_len); 268 if (ret < 0) { 269 IRDA_DEBUG(2, "%s(), IrLAN, Error!\n", __FUNCTION__ ); 270 break; 271 } 272 ptr+=ret; 273 ret = RSP_SUCCESS; 274 irlan_check_command_param(self, name, value); 275 } 276 /* Cleanup */ 277 kfree(name); 278 kfree(value); 279 280 return ret; 281} 282 283/* 284 * Function irlan_provider_send_reply (self, info) 285 * 286 * Send reply to query to peer IrLAN layer 287 * 288 */ 289void irlan_provider_send_reply(struct irlan_cb *self, int command, 290 int ret_code) 291{ 292 struct sk_buff *skb; 293 294 IRDA_DEBUG(4, "%s()\n", __FUNCTION__ ); 295 296 IRDA_ASSERT(self != NULL, return;); 297 IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); 298 299 skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + 300 /* Bigger param length comes from CMD_GET_MEDIA_CHAR */ 301 IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") + 302 IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "BORADCAST") + 303 IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "MULTICAST") + 304 IRLAN_STRING_PARAMETER_LEN("ACCESS_TYPE", "HOSTED"), 305 GFP_ATOMIC); 306 307 if (!skb) 308 return; 309 310 /* Reserve space for TTP, LMP, and LAP header */ 311 skb_reserve(skb, self->provider.max_header_size); 312 skb_put(skb, 2); 313 314 switch (command) { 315 case CMD_GET_PROVIDER_INFO: 316 skb->data[0] = 0x00; /* Success */ 317 skb->data[1] = 0x02; /* 2 parameters */ 318 switch (self->media) { 319 case MEDIA_802_3: 320 irlan_insert_string_param(skb, "MEDIA", "802.3"); 321 break; 322 case MEDIA_802_5: 323 irlan_insert_string_param(skb, "MEDIA", "802.5"); 324 break; 325 default: 326 IRDA_DEBUG(2, "%s(), unknown media type!\n", __FUNCTION__ ); 327 break; 328 } 329 irlan_insert_short_param(skb, "IRLAN_VER", 0x0101); 330 break; 331 332 case CMD_GET_MEDIA_CHAR: 333 skb->data[0] = 0x00; /* Success */ 334 skb->data[1] = 0x05; /* 5 parameters */ 335 irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); 336 irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST"); 337 irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST"); 338 339 switch (self->provider.access_type) { 340 case ACCESS_DIRECT: 341 irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT"); 342 break; 343 case ACCESS_PEER: 344 irlan_insert_string_param(skb, "ACCESS_TYPE", "PEER"); 345 break; 346 case ACCESS_HOSTED: 347 irlan_insert_string_param(skb, "ACCESS_TYPE", "HOSTED"); 348 break; 349 default: 350 IRDA_DEBUG(2, "%s(), Unknown access type\n", __FUNCTION__ ); 351 break; 352 } 353 irlan_insert_short_param(skb, "MAX_FRAME", 0x05ee); 354 break; 355 case CMD_OPEN_DATA_CHANNEL: 356 skb->data[0] = 0x00; /* Success */ 357 if (self->provider.send_arb_val) { 358 skb->data[1] = 0x03; /* 3 parameters */ 359 irlan_insert_short_param(skb, "CON_ARB", 360 self->provider.send_arb_val); 361 } else 362 skb->data[1] = 0x02; /* 2 parameters */ 363 irlan_insert_byte_param(skb, "DATA_CHAN", self->stsap_sel_data); 364 irlan_insert_string_param(skb, "RECONNECT_KEY", "LINUX RULES!"); 365 break; 366 case CMD_FILTER_OPERATION: 367 irlan_filter_request(self, skb); 368 break; 369 default: 370 IRDA_DEBUG(2, "%s(), Unknown command!\n", __FUNCTION__ ); 371 break; 372 } 373 374 irttp_data_request(self->provider.tsap_ctrl, skb); 375} 376 377/* 378 * Function irlan_provider_register(void) 379 * 380 * Register provider support so we can accept incoming connections. 381 * 382 */ 383int irlan_provider_open_ctrl_tsap(struct irlan_cb *self) 384{ 385 struct tsap_cb *tsap; 386 notify_t notify; 387 388 IRDA_DEBUG(4, "%s()\n", __FUNCTION__ ); 389 390 IRDA_ASSERT(self != NULL, return -1;); 391 IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); 392 393 /* Check if already open */ 394 if (self->provider.tsap_ctrl) 395 return -1; 396 397 /* 398 * First register well known control TSAP 399 */ 400 irda_notify_init(¬ify); 401 notify.data_indication = irlan_provider_data_indication; 402 notify.connect_indication = irlan_provider_connect_indication; 403 notify.disconnect_indication = irlan_provider_disconnect_indication; 404 notify.instance = self; 405 strlcpy(notify.name, "IrLAN ctrl (p)", sizeof(notify.name)); 406 407 tsap = irttp_open_tsap(LSAP_ANY, 1, ¬ify); 408 if (!tsap) { 409 IRDA_DEBUG(2, "%s(), Got no tsap!\n", __FUNCTION__ ); 410 return -1; 411 } 412 self->provider.tsap_ctrl = tsap; 413 414 /* Register with LM-IAS */ 415 irlan_ias_register(self, tsap->stsap_sel); 416 417 return 0; 418} 419