1/********************************************************************* 2 * 3 * Filename: ircomm_lmp.c 4 * Version: 1.0 5 * Description: Interface between IrCOMM and IrLMP 6 * Status: Stable 7 * Author: Dag Brattli <dagb@cs.uit.no> 8 * Created at: Sun Jun 6 20:48:27 1999 9 * Modified at: Sun Dec 12 13:44:17 1999 10 * Modified by: Dag Brattli <dagb@cs.uit.no> 11 * Sources: Previous IrLPT work by Thomas Davis 12 * 13 * Copyright (c) 1999 Dag Brattli, All Rights Reserved. 14 * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.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 as 18 * published by the Free Software Foundation; either version 2 of 19 * the License, or (at your option) any later version. 20 * 21 * This program is distributed in the hope that it will be useful, 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 * GNU General Public License for more details. 25 * 26 * You should have received a copy of the GNU General Public License 27 * along with this program; if not, see <http://www.gnu.org/licenses/>. 28 * 29 ********************************************************************/ 30 31#include <linux/init.h> 32#include <linux/gfp.h> 33 34#include <net/irda/irda.h> 35#include <net/irda/irlmp.h> 36#include <net/irda/iriap.h> 37#include <net/irda/irda_device.h> /* struct irda_skb_cb */ 38 39#include <net/irda/ircomm_event.h> 40#include <net/irda/ircomm_lmp.h> 41 42 43/* 44 * Function ircomm_lmp_connect_request (self, userdata) 45 * 46 * 47 * 48 */ 49static int ircomm_lmp_connect_request(struct ircomm_cb *self, 50 struct sk_buff *userdata, 51 struct ircomm_info *info) 52{ 53 int ret = 0; 54 55 IRDA_DEBUG(0, "%s()\n", __func__ ); 56 57 /* Don't forget to refcount it - should be NULL anyway */ 58 if(userdata) 59 skb_get(userdata); 60 61 ret = irlmp_connect_request(self->lsap, info->dlsap_sel, 62 info->saddr, info->daddr, NULL, userdata); 63 return ret; 64} 65 66/* 67 * Function ircomm_lmp_connect_response (self, skb) 68 * 69 * 70 * 71 */ 72static int ircomm_lmp_connect_response(struct ircomm_cb *self, 73 struct sk_buff *userdata) 74{ 75 struct sk_buff *tx_skb; 76 77 IRDA_DEBUG(0, "%s()\n", __func__ ); 78 79 /* Any userdata supplied? */ 80 if (userdata == NULL) { 81 tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); 82 if (!tx_skb) 83 return -ENOMEM; 84 85 /* Reserve space for MUX and LAP header */ 86 skb_reserve(tx_skb, LMP_MAX_HEADER); 87 } else { 88 /* 89 * Check that the client has reserved enough space for 90 * headers 91 */ 92 IRDA_ASSERT(skb_headroom(userdata) >= LMP_MAX_HEADER, 93 return -1;); 94 95 /* Don't forget to refcount it - should be NULL anyway */ 96 skb_get(userdata); 97 tx_skb = userdata; 98 } 99 100 return irlmp_connect_response(self->lsap, tx_skb); 101} 102 103static int ircomm_lmp_disconnect_request(struct ircomm_cb *self, 104 struct sk_buff *userdata, 105 struct ircomm_info *info) 106{ 107 struct sk_buff *tx_skb; 108 int ret; 109 110 IRDA_DEBUG(0, "%s()\n", __func__ ); 111 112 if (!userdata) { 113 tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); 114 if (!tx_skb) 115 return -ENOMEM; 116 117 /* Reserve space for MUX and LAP header */ 118 skb_reserve(tx_skb, LMP_MAX_HEADER); 119 userdata = tx_skb; 120 } else { 121 /* Don't forget to refcount it - should be NULL anyway */ 122 skb_get(userdata); 123 } 124 125 ret = irlmp_disconnect_request(self->lsap, userdata); 126 127 return ret; 128} 129 130/* 131 * Function ircomm_lmp_flow_control (skb) 132 * 133 * This function is called when a data frame we have sent to IrLAP has 134 * been deallocated. We do this to make sure we don't flood IrLAP with 135 * frames, since we are not using the IrTTP flow control mechanism 136 */ 137static void ircomm_lmp_flow_control(struct sk_buff *skb) 138{ 139 struct irda_skb_cb *cb; 140 struct ircomm_cb *self; 141 int line; 142 143 IRDA_ASSERT(skb != NULL, return;); 144 145 cb = (struct irda_skb_cb *) skb->cb; 146 147 IRDA_DEBUG(2, "%s()\n", __func__ ); 148 149 line = cb->line; 150 151 self = (struct ircomm_cb *) hashbin_lock_find(ircomm, line, NULL); 152 if (!self) { 153 IRDA_DEBUG(2, "%s(), didn't find myself\n", __func__ ); 154 return; 155 } 156 157 IRDA_ASSERT(self != NULL, return;); 158 IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); 159 160 self->pkt_count--; 161 162 if ((self->pkt_count < 2) && (self->flow_status == FLOW_STOP)) { 163 IRDA_DEBUG(2, "%s(), asking TTY to start again!\n", __func__ ); 164 self->flow_status = FLOW_START; 165 if (self->notify.flow_indication) 166 self->notify.flow_indication(self->notify.instance, 167 self, FLOW_START); 168 } 169} 170 171/* 172 * Function ircomm_lmp_data_request (self, userdata) 173 * 174 * Send data frame to peer device 175 * 176 */ 177static int ircomm_lmp_data_request(struct ircomm_cb *self, 178 struct sk_buff *skb, 179 int not_used) 180{ 181 struct irda_skb_cb *cb; 182 int ret; 183 184 IRDA_ASSERT(skb != NULL, return -1;); 185 186 cb = (struct irda_skb_cb *) skb->cb; 187 188 cb->line = self->line; 189 190 IRDA_DEBUG(4, "%s(), sending frame\n", __func__ ); 191 192 /* Don't forget to refcount it - see ircomm_tty_do_softint() */ 193 skb_get(skb); 194 195 skb_orphan(skb); 196 skb->destructor = ircomm_lmp_flow_control; 197 198 if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) { 199 IRDA_DEBUG(2, "%s(), asking TTY to slow down!\n", __func__ ); 200 self->flow_status = FLOW_STOP; 201 if (self->notify.flow_indication) 202 self->notify.flow_indication(self->notify.instance, 203 self, FLOW_STOP); 204 } 205 ret = irlmp_data_request(self->lsap, skb); 206 if (ret) { 207 IRDA_ERROR("%s(), failed\n", __func__); 208 /* irlmp_data_request already free the packet */ 209 } 210 211 return ret; 212} 213 214/* 215 * Function ircomm_lmp_data_indication (instance, sap, skb) 216 * 217 * Incoming data which we must deliver to the state machine, to check 218 * we are still connected. 219 */ 220static int ircomm_lmp_data_indication(void *instance, void *sap, 221 struct sk_buff *skb) 222{ 223 struct ircomm_cb *self = (struct ircomm_cb *) instance; 224 225 IRDA_DEBUG(4, "%s()\n", __func__ ); 226 227 IRDA_ASSERT(self != NULL, return -1;); 228 IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); 229 IRDA_ASSERT(skb != NULL, return -1;); 230 231 ircomm_do_event(self, IRCOMM_LMP_DATA_INDICATION, skb, NULL); 232 233 /* Drop reference count - see ircomm_tty_data_indication(). */ 234 dev_kfree_skb(skb); 235 236 return 0; 237} 238 239/* 240 * Function ircomm_lmp_connect_confirm (instance, sap, qos, max_sdu_size, 241 * max_header_size, skb) 242 * 243 * Connection has been confirmed by peer device 244 * 245 */ 246static void ircomm_lmp_connect_confirm(void *instance, void *sap, 247 struct qos_info *qos, 248 __u32 max_seg_size, 249 __u8 max_header_size, 250 struct sk_buff *skb) 251{ 252 struct ircomm_cb *self = (struct ircomm_cb *) instance; 253 struct ircomm_info info; 254 255 IRDA_DEBUG(0, "%s()\n", __func__ ); 256 257 IRDA_ASSERT(self != NULL, return;); 258 IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); 259 IRDA_ASSERT(skb != NULL, return;); 260 IRDA_ASSERT(qos != NULL, return;); 261 262 info.max_data_size = max_seg_size; 263 info.max_header_size = max_header_size; 264 info.qos = qos; 265 266 ircomm_do_event(self, IRCOMM_LMP_CONNECT_CONFIRM, skb, &info); 267 268 /* Drop reference count - see ircomm_tty_connect_confirm(). */ 269 dev_kfree_skb(skb); 270} 271 272/* 273 * Function ircomm_lmp_connect_indication (instance, sap, qos, max_sdu_size, 274 * max_header_size, skb) 275 * 276 * Peer device wants to make a connection with us 277 * 278 */ 279static void ircomm_lmp_connect_indication(void *instance, void *sap, 280 struct qos_info *qos, 281 __u32 max_seg_size, 282 __u8 max_header_size, 283 struct sk_buff *skb) 284{ 285 struct ircomm_cb *self = (struct ircomm_cb *)instance; 286 struct ircomm_info info; 287 288 IRDA_DEBUG(0, "%s()\n", __func__ ); 289 290 IRDA_ASSERT(self != NULL, return;); 291 IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); 292 IRDA_ASSERT(skb != NULL, return;); 293 IRDA_ASSERT(qos != NULL, return;); 294 295 info.max_data_size = max_seg_size; 296 info.max_header_size = max_header_size; 297 info.qos = qos; 298 299 ircomm_do_event(self, IRCOMM_LMP_CONNECT_INDICATION, skb, &info); 300 301 /* Drop reference count - see ircomm_tty_connect_indication(). */ 302 dev_kfree_skb(skb); 303} 304 305/* 306 * Function ircomm_lmp_disconnect_indication (instance, sap, reason, skb) 307 * 308 * Peer device has closed the connection, or the link went down for some 309 * other reason 310 */ 311static void ircomm_lmp_disconnect_indication(void *instance, void *sap, 312 LM_REASON reason, 313 struct sk_buff *skb) 314{ 315 struct ircomm_cb *self = (struct ircomm_cb *) instance; 316 struct ircomm_info info; 317 318 IRDA_DEBUG(0, "%s()\n", __func__ ); 319 320 IRDA_ASSERT(self != NULL, return;); 321 IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); 322 323 info.reason = reason; 324 325 ircomm_do_event(self, IRCOMM_LMP_DISCONNECT_INDICATION, skb, &info); 326 327 /* Drop reference count - see ircomm_tty_disconnect_indication(). */ 328 if(skb) 329 dev_kfree_skb(skb); 330} 331/* 332 * Function ircomm_open_lsap (self) 333 * 334 * Open LSAP. This function will only be used when using "raw" services 335 * 336 */ 337int ircomm_open_lsap(struct ircomm_cb *self) 338{ 339 notify_t notify; 340 341 IRDA_DEBUG(0, "%s()\n", __func__ ); 342 343 /* Register callbacks */ 344 irda_notify_init(¬ify); 345 notify.data_indication = ircomm_lmp_data_indication; 346 notify.connect_confirm = ircomm_lmp_connect_confirm; 347 notify.connect_indication = ircomm_lmp_connect_indication; 348 notify.disconnect_indication = ircomm_lmp_disconnect_indication; 349 notify.instance = self; 350 strlcpy(notify.name, "IrCOMM", sizeof(notify.name)); 351 352 self->lsap = irlmp_open_lsap(LSAP_ANY, ¬ify, 0); 353 if (!self->lsap) { 354 IRDA_DEBUG(0,"%sfailed to allocate tsap\n", __func__ ); 355 return -1; 356 } 357 self->slsap_sel = self->lsap->slsap_sel; 358 359 /* 360 * Initialize the call-table for issuing commands 361 */ 362 self->issue.data_request = ircomm_lmp_data_request; 363 self->issue.connect_request = ircomm_lmp_connect_request; 364 self->issue.connect_response = ircomm_lmp_connect_response; 365 self->issue.disconnect_request = ircomm_lmp_disconnect_request; 366 367 return 0; 368} 369