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