diff options
Diffstat (limited to 'net/irda/ircomm/ircomm_tty_attach.c')
-rw-r--r-- | net/irda/ircomm/ircomm_tty_attach.c | 1006 |
1 files changed, 1006 insertions, 0 deletions
diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c new file mode 100644 index 00000000000..99f5eddbb4b --- /dev/null +++ b/net/irda/ircomm/ircomm_tty_attach.c | |||
@@ -0,0 +1,1006 @@ | |||
1 | /********************************************************************* | ||
2 | * | ||
3 | * Filename: ircomm_tty_attach.c | ||
4 | * Version: | ||
5 | * Description: Code for attaching the serial driver to IrCOMM | ||
6 | * Status: Experimental. | ||
7 | * Author: Dag Brattli <dagb@cs.uit.no> | ||
8 | * Created at: Sat Jun 5 17:42:00 1999 | ||
9 | * Modified at: Tue Jan 4 14:20:49 2000 | ||
10 | * Modified by: Dag Brattli <dagb@cs.uit.no> | ||
11 | * | ||
12 | * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. | ||
13 | * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com> | ||
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 | #include <net/irda/irttp.h> | ||
39 | #include <net/irda/irias_object.h> | ||
40 | #include <net/irda/parameters.h> | ||
41 | |||
42 | #include <net/irda/ircomm_core.h> | ||
43 | #include <net/irda/ircomm_param.h> | ||
44 | #include <net/irda/ircomm_event.h> | ||
45 | |||
46 | #include <net/irda/ircomm_tty.h> | ||
47 | #include <net/irda/ircomm_tty_attach.h> | ||
48 | |||
49 | static void ircomm_tty_ias_register(struct ircomm_tty_cb *self); | ||
50 | static void ircomm_tty_discovery_indication(discinfo_t *discovery, | ||
51 | DISCOVERY_MODE mode, | ||
52 | void *priv); | ||
53 | static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id, | ||
54 | struct ias_value *value, void *priv); | ||
55 | static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self, | ||
56 | int timeout); | ||
57 | static void ircomm_tty_watchdog_timer_expired(void *data); | ||
58 | |||
59 | static int ircomm_tty_state_idle(struct ircomm_tty_cb *self, | ||
60 | IRCOMM_TTY_EVENT event, | ||
61 | struct sk_buff *skb, | ||
62 | struct ircomm_tty_info *info); | ||
63 | static int ircomm_tty_state_search(struct ircomm_tty_cb *self, | ||
64 | IRCOMM_TTY_EVENT event, | ||
65 | struct sk_buff *skb, | ||
66 | struct ircomm_tty_info *info); | ||
67 | static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self, | ||
68 | IRCOMM_TTY_EVENT event, | ||
69 | struct sk_buff *skb, | ||
70 | struct ircomm_tty_info *info); | ||
71 | static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self, | ||
72 | IRCOMM_TTY_EVENT event, | ||
73 | struct sk_buff *skb, | ||
74 | struct ircomm_tty_info *info); | ||
75 | static int ircomm_tty_state_setup(struct ircomm_tty_cb *self, | ||
76 | IRCOMM_TTY_EVENT event, | ||
77 | struct sk_buff *skb, | ||
78 | struct ircomm_tty_info *info); | ||
79 | static int ircomm_tty_state_ready(struct ircomm_tty_cb *self, | ||
80 | IRCOMM_TTY_EVENT event, | ||
81 | struct sk_buff *skb, | ||
82 | struct ircomm_tty_info *info); | ||
83 | |||
84 | char *ircomm_tty_state[] = { | ||
85 | "IRCOMM_TTY_IDLE", | ||
86 | "IRCOMM_TTY_SEARCH", | ||
87 | "IRCOMM_TTY_QUERY_PARAMETERS", | ||
88 | "IRCOMM_TTY_QUERY_LSAP_SEL", | ||
89 | "IRCOMM_TTY_SETUP", | ||
90 | "IRCOMM_TTY_READY", | ||
91 | "*** ERROR *** ", | ||
92 | }; | ||
93 | |||
94 | #ifdef CONFIG_IRDA_DEBUG | ||
95 | static char *ircomm_tty_event[] = { | ||
96 | "IRCOMM_TTY_ATTACH_CABLE", | ||
97 | "IRCOMM_TTY_DETACH_CABLE", | ||
98 | "IRCOMM_TTY_DATA_REQUEST", | ||
99 | "IRCOMM_TTY_DATA_INDICATION", | ||
100 | "IRCOMM_TTY_DISCOVERY_REQUEST", | ||
101 | "IRCOMM_TTY_DISCOVERY_INDICATION", | ||
102 | "IRCOMM_TTY_CONNECT_CONFIRM", | ||
103 | "IRCOMM_TTY_CONNECT_INDICATION", | ||
104 | "IRCOMM_TTY_DISCONNECT_REQUEST", | ||
105 | "IRCOMM_TTY_DISCONNECT_INDICATION", | ||
106 | "IRCOMM_TTY_WD_TIMER_EXPIRED", | ||
107 | "IRCOMM_TTY_GOT_PARAMETERS", | ||
108 | "IRCOMM_TTY_GOT_LSAPSEL", | ||
109 | "*** ERROR ****", | ||
110 | }; | ||
111 | #endif /* CONFIG_IRDA_DEBUG */ | ||
112 | |||
113 | static int (*state[])(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event, | ||
114 | struct sk_buff *skb, struct ircomm_tty_info *info) = | ||
115 | { | ||
116 | ircomm_tty_state_idle, | ||
117 | ircomm_tty_state_search, | ||
118 | ircomm_tty_state_query_parameters, | ||
119 | ircomm_tty_state_query_lsap_sel, | ||
120 | ircomm_tty_state_setup, | ||
121 | ircomm_tty_state_ready, | ||
122 | }; | ||
123 | |||
124 | /* | ||
125 | * Function ircomm_tty_attach_cable (driver) | ||
126 | * | ||
127 | * Try to attach cable (IrCOMM link). This function will only return | ||
128 | * when the link has been connected, or if an error condition occurs. | ||
129 | * If success, the return value is the resulting service type. | ||
130 | */ | ||
131 | int ircomm_tty_attach_cable(struct ircomm_tty_cb *self) | ||
132 | { | ||
133 | IRDA_DEBUG(0, "%s()\n", __FUNCTION__ ); | ||
134 | |||
135 | IRDA_ASSERT(self != NULL, return -1;); | ||
136 | IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); | ||
137 | |||
138 | /* Check if somebody has already connected to us */ | ||
139 | if (ircomm_is_connected(self->ircomm)) { | ||
140 | IRDA_DEBUG(0, "%s(), already connected!\n", __FUNCTION__ ); | ||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | /* Make sure nobody tries to write before the link is up */ | ||
145 | self->tty->hw_stopped = 1; | ||
146 | |||
147 | ircomm_tty_ias_register(self); | ||
148 | |||
149 | ircomm_tty_do_event(self, IRCOMM_TTY_ATTACH_CABLE, NULL, NULL); | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * Function ircomm_detach_cable (driver) | ||
156 | * | ||
157 | * Detach cable, or cable has been detached by peer | ||
158 | * | ||
159 | */ | ||
160 | void ircomm_tty_detach_cable(struct ircomm_tty_cb *self) | ||
161 | { | ||
162 | IRDA_DEBUG(0, "%s()\n", __FUNCTION__ ); | ||
163 | |||
164 | IRDA_ASSERT(self != NULL, return;); | ||
165 | IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); | ||
166 | |||
167 | del_timer(&self->watchdog_timer); | ||
168 | |||
169 | /* Remove discovery handler */ | ||
170 | if (self->ckey) { | ||
171 | irlmp_unregister_client(self->ckey); | ||
172 | self->ckey = NULL; | ||
173 | } | ||
174 | /* Remove IrCOMM hint bits */ | ||
175 | if (self->skey) { | ||
176 | irlmp_unregister_service(self->skey); | ||
177 | self->skey = NULL; | ||
178 | } | ||
179 | |||
180 | if (self->iriap) { | ||
181 | iriap_close(self->iriap); | ||
182 | self->iriap = NULL; | ||
183 | } | ||
184 | |||
185 | /* Remove LM-IAS object */ | ||
186 | if (self->obj) { | ||
187 | irias_delete_object(self->obj); | ||
188 | self->obj = NULL; | ||
189 | } | ||
190 | |||
191 | ircomm_tty_do_event(self, IRCOMM_TTY_DETACH_CABLE, NULL, NULL); | ||
192 | |||
193 | /* Reset some values */ | ||
194 | self->daddr = self->saddr = 0; | ||
195 | self->dlsap_sel = self->slsap_sel = 0; | ||
196 | |||
197 | memset(&self->settings, 0, sizeof(struct ircomm_params)); | ||
198 | } | ||
199 | |||
200 | /* | ||
201 | * Function ircomm_tty_ias_register (self) | ||
202 | * | ||
203 | * Register with LM-IAS depending on which service type we are | ||
204 | * | ||
205 | */ | ||
206 | static void ircomm_tty_ias_register(struct ircomm_tty_cb *self) | ||
207 | { | ||
208 | __u8 oct_seq[6]; | ||
209 | __u16 hints; | ||
210 | |||
211 | IRDA_DEBUG(0, "%s()\n", __FUNCTION__ ); | ||
212 | |||
213 | IRDA_ASSERT(self != NULL, return;); | ||
214 | IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); | ||
215 | |||
216 | /* Compute hint bits based on service */ | ||
217 | hints = irlmp_service_to_hint(S_COMM); | ||
218 | if (self->service_type & IRCOMM_3_WIRE_RAW) | ||
219 | hints |= irlmp_service_to_hint(S_PRINTER); | ||
220 | |||
221 | /* Advertise IrCOMM hint bit in discovery */ | ||
222 | if (!self->skey) | ||
223 | self->skey = irlmp_register_service(hints); | ||
224 | /* Set up a discovery handler */ | ||
225 | if (!self->ckey) | ||
226 | self->ckey = irlmp_register_client(hints, | ||
227 | ircomm_tty_discovery_indication, | ||
228 | NULL, (void *) self); | ||
229 | |||
230 | /* If already done, no need to do it again */ | ||
231 | if (self->obj) | ||
232 | return; | ||
233 | |||
234 | if (self->service_type & IRCOMM_3_WIRE_RAW) { | ||
235 | /* Register IrLPT with LM-IAS */ | ||
236 | self->obj = irias_new_object("IrLPT", IAS_IRLPT_ID); | ||
237 | irias_add_integer_attrib(self->obj, "IrDA:IrLMP:LsapSel", | ||
238 | self->slsap_sel, IAS_KERNEL_ATTR); | ||
239 | } else { | ||
240 | /* Register IrCOMM with LM-IAS */ | ||
241 | self->obj = irias_new_object("IrDA:IrCOMM", IAS_IRCOMM_ID); | ||
242 | irias_add_integer_attrib(self->obj, "IrDA:TinyTP:LsapSel", | ||
243 | self->slsap_sel, IAS_KERNEL_ATTR); | ||
244 | |||
245 | /* Code the parameters into the buffer */ | ||
246 | irda_param_pack(oct_seq, "bbbbbb", | ||
247 | IRCOMM_SERVICE_TYPE, 1, self->service_type, | ||
248 | IRCOMM_PORT_TYPE, 1, IRCOMM_SERIAL); | ||
249 | |||
250 | /* Register parameters with LM-IAS */ | ||
251 | irias_add_octseq_attrib(self->obj, "Parameters", oct_seq, 6, | ||
252 | IAS_KERNEL_ATTR); | ||
253 | } | ||
254 | irias_insert_object(self->obj); | ||
255 | } | ||
256 | |||
257 | /* | ||
258 | * Function ircomm_tty_ias_unregister (self) | ||
259 | * | ||
260 | * Remove our IAS object and client hook while connected. | ||
261 | * | ||
262 | */ | ||
263 | static void ircomm_tty_ias_unregister(struct ircomm_tty_cb *self) | ||
264 | { | ||
265 | /* Remove LM-IAS object now so it is not reused. | ||
266 | * IrCOMM deals very poorly with multiple incoming connections. | ||
267 | * It should looks a lot more like IrNET, and "dup" a server TSAP | ||
268 | * to the application TSAP (based on various rules). | ||
269 | * This is a cheap workaround allowing multiple clients to | ||
270 | * connect to us. It will not always work. | ||
271 | * Each IrCOMM socket has an IAS entry. Incoming connection will | ||
272 | * pick the first one found. So, when we are fully connected, | ||
273 | * we remove our IAS entries so that the next IAS entry is used. | ||
274 | * We do that for *both* client and server, because a server | ||
275 | * can also create client instances. | ||
276 | * Jean II */ | ||
277 | if (self->obj) { | ||
278 | irias_delete_object(self->obj); | ||
279 | self->obj = NULL; | ||
280 | } | ||
281 | |||
282 | #if 0 | ||
283 | /* Remove discovery handler. | ||
284 | * While we are connected, we no longer need to receive | ||
285 | * discovery events. This would be the case if there is | ||
286 | * multiple IrLAP interfaces. Jean II */ | ||
287 | if (self->ckey) { | ||
288 | irlmp_unregister_client(self->ckey); | ||
289 | self->ckey = NULL; | ||
290 | } | ||
291 | #endif | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * Function ircomm_send_initial_parameters (self) | ||
296 | * | ||
297 | * Send initial parameters to the remote IrCOMM device. These parameters | ||
298 | * must be sent before any data. | ||
299 | */ | ||
300 | int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self) | ||
301 | { | ||
302 | IRDA_ASSERT(self != NULL, return -1;); | ||
303 | IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); | ||
304 | |||
305 | if (self->service_type & IRCOMM_3_WIRE_RAW) | ||
306 | return 0; | ||
307 | |||
308 | /* | ||
309 | * Set default values, but only if the application for some reason | ||
310 | * haven't set them already | ||
311 | */ | ||
312 | IRDA_DEBUG(2, "%s(), data-rate = %d\n", __FUNCTION__ , | ||
313 | self->settings.data_rate); | ||
314 | if (!self->settings.data_rate) | ||
315 | self->settings.data_rate = 9600; | ||
316 | IRDA_DEBUG(2, "%s(), data-format = %d\n", __FUNCTION__ , | ||
317 | self->settings.data_format); | ||
318 | if (!self->settings.data_format) | ||
319 | self->settings.data_format = IRCOMM_WSIZE_8; /* 8N1 */ | ||
320 | |||
321 | IRDA_DEBUG(2, "%s(), flow-control = %d\n", __FUNCTION__ , | ||
322 | self->settings.flow_control); | ||
323 | /*self->settings.flow_control = IRCOMM_RTS_CTS_IN|IRCOMM_RTS_CTS_OUT;*/ | ||
324 | |||
325 | /* Do not set delta values for the initial parameters */ | ||
326 | self->settings.dte = IRCOMM_DTR | IRCOMM_RTS; | ||
327 | |||
328 | /* Only send service type parameter when we are the client */ | ||
329 | if (self->client) | ||
330 | ircomm_param_request(self, IRCOMM_SERVICE_TYPE, FALSE); | ||
331 | ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE); | ||
332 | ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE); | ||
333 | |||
334 | /* For a 3 wire service, we just flush the last parameter and return */ | ||
335 | if (self->settings.service_type == IRCOMM_3_WIRE) { | ||
336 | ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE); | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | /* Only 9-wire service types continue here */ | ||
341 | ircomm_param_request(self, IRCOMM_FLOW_CONTROL, FALSE); | ||
342 | #if 0 | ||
343 | ircomm_param_request(self, IRCOMM_XON_XOFF, FALSE); | ||
344 | ircomm_param_request(self, IRCOMM_ENQ_ACK, FALSE); | ||
345 | #endif | ||
346 | /* Notify peer that we are ready to receive data */ | ||
347 | ircomm_param_request(self, IRCOMM_DTE, TRUE); | ||
348 | |||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | /* | ||
353 | * Function ircomm_tty_discovery_indication (discovery) | ||
354 | * | ||
355 | * Remote device is discovered, try query the remote IAS to see which | ||
356 | * device it is, and which services it has. | ||
357 | * | ||
358 | */ | ||
359 | static void ircomm_tty_discovery_indication(discinfo_t *discovery, | ||
360 | DISCOVERY_MODE mode, | ||
361 | void *priv) | ||
362 | { | ||
363 | struct ircomm_tty_cb *self; | ||
364 | struct ircomm_tty_info info; | ||
365 | |||
366 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); | ||
367 | |||
368 | /* Important note : | ||
369 | * We need to drop all passive discoveries. | ||
370 | * The LSAP management of IrComm is deficient and doesn't deal | ||
371 | * with the case of two instance connecting to each other | ||
372 | * simultaneously (it will deadlock in LMP). | ||
373 | * The proper fix would be to use the same technique as in IrNET, | ||
374 | * to have one server socket and separate instances for the | ||
375 | * connecting/connected socket. | ||
376 | * The workaround is to drop passive discovery, which drastically | ||
377 | * reduce the probability of this happening. | ||
378 | * Jean II */ | ||
379 | if(mode == DISCOVERY_PASSIVE) | ||
380 | return; | ||
381 | |||
382 | info.daddr = discovery->daddr; | ||
383 | info.saddr = discovery->saddr; | ||
384 | |||
385 | /* FIXME. We have a locking problem on the hashbin here. | ||
386 | * We probably need to use hashbin_find_next(), but we first | ||
387 | * need to ensure that "line" is unique. - Jean II */ | ||
388 | self = (struct ircomm_tty_cb *) hashbin_get_first(ircomm_tty); | ||
389 | while (self != NULL) { | ||
390 | IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); | ||
391 | |||
392 | ircomm_tty_do_event(self, IRCOMM_TTY_DISCOVERY_INDICATION, | ||
393 | NULL, &info); | ||
394 | |||
395 | self = (struct ircomm_tty_cb *) hashbin_get_next(ircomm_tty); | ||
396 | } | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | * Function ircomm_tty_disconnect_indication (instance, sap, reason, skb) | ||
401 | * | ||
402 | * Link disconnected | ||
403 | * | ||
404 | */ | ||
405 | void ircomm_tty_disconnect_indication(void *instance, void *sap, | ||
406 | LM_REASON reason, | ||
407 | struct sk_buff *skb) | ||
408 | { | ||
409 | struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; | ||
410 | |||
411 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); | ||
412 | |||
413 | IRDA_ASSERT(self != NULL, return;); | ||
414 | IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); | ||
415 | |||
416 | if (!self->tty) | ||
417 | return; | ||
418 | |||
419 | /* This will stop control data transfers */ | ||
420 | self->flow = FLOW_STOP; | ||
421 | |||
422 | /* Stop data transfers */ | ||
423 | self->tty->hw_stopped = 1; | ||
424 | |||
425 | ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL, | ||
426 | NULL); | ||
427 | } | ||
428 | |||
429 | /* | ||
430 | * Function ircomm_tty_getvalue_confirm (result, obj_id, value, priv) | ||
431 | * | ||
432 | * Got result from the IAS query we make | ||
433 | * | ||
434 | */ | ||
435 | static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id, | ||
436 | struct ias_value *value, | ||
437 | void *priv) | ||
438 | { | ||
439 | struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) priv; | ||
440 | |||
441 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); | ||
442 | |||
443 | IRDA_ASSERT(self != NULL, return;); | ||
444 | IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); | ||
445 | |||
446 | /* We probably don't need to make any more queries */ | ||
447 | iriap_close(self->iriap); | ||
448 | self->iriap = NULL; | ||
449 | |||
450 | /* Check if request succeeded */ | ||
451 | if (result != IAS_SUCCESS) { | ||
452 | IRDA_DEBUG(4, "%s(), got NULL value!\n", __FUNCTION__ ); | ||
453 | return; | ||
454 | } | ||
455 | |||
456 | switch (value->type) { | ||
457 | case IAS_OCT_SEQ: | ||
458 | IRDA_DEBUG(2, "%s(), got octet sequence\n", __FUNCTION__ ); | ||
459 | |||
460 | irda_param_extract_all(self, value->t.oct_seq, value->len, | ||
461 | &ircomm_param_info); | ||
462 | |||
463 | ircomm_tty_do_event(self, IRCOMM_TTY_GOT_PARAMETERS, NULL, | ||
464 | NULL); | ||
465 | break; | ||
466 | case IAS_INTEGER: | ||
467 | /* Got LSAP selector */ | ||
468 | IRDA_DEBUG(2, "%s(), got lsapsel = %d\n", __FUNCTION__ , | ||
469 | value->t.integer); | ||
470 | |||
471 | if (value->t.integer == -1) { | ||
472 | IRDA_DEBUG(0, "%s(), invalid value!\n", __FUNCTION__ ); | ||
473 | } else | ||
474 | self->dlsap_sel = value->t.integer; | ||
475 | |||
476 | ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL); | ||
477 | break; | ||
478 | case IAS_MISSING: | ||
479 | IRDA_DEBUG(0, "%s(), got IAS_MISSING\n", __FUNCTION__ ); | ||
480 | break; | ||
481 | default: | ||
482 | IRDA_DEBUG(0, "%s(), got unknown type!\n", __FUNCTION__ ); | ||
483 | break; | ||
484 | } | ||
485 | irias_delete_value(value); | ||
486 | } | ||
487 | |||
488 | /* | ||
489 | * Function ircomm_tty_connect_confirm (instance, sap, qos, max_sdu_size, skb) | ||
490 | * | ||
491 | * Connection confirmed | ||
492 | * | ||
493 | */ | ||
494 | void ircomm_tty_connect_confirm(void *instance, void *sap, | ||
495 | struct qos_info *qos, | ||
496 | __u32 max_data_size, | ||
497 | __u8 max_header_size, | ||
498 | struct sk_buff *skb) | ||
499 | { | ||
500 | struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; | ||
501 | |||
502 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); | ||
503 | |||
504 | IRDA_ASSERT(self != NULL, return;); | ||
505 | IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); | ||
506 | |||
507 | self->client = TRUE; | ||
508 | self->max_data_size = max_data_size; | ||
509 | self->max_header_size = max_header_size; | ||
510 | self->flow = FLOW_START; | ||
511 | |||
512 | ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_CONFIRM, NULL, NULL); | ||
513 | |||
514 | /* No need to kfree_skb - see ircomm_ttp_connect_confirm() */ | ||
515 | } | ||
516 | |||
517 | /* | ||
518 | * Function ircomm_tty_connect_indication (instance, sap, qos, max_sdu_size, | ||
519 | * skb) | ||
520 | * | ||
521 | * we are discovered and being requested to connect by remote device ! | ||
522 | * | ||
523 | */ | ||
524 | void ircomm_tty_connect_indication(void *instance, void *sap, | ||
525 | struct qos_info *qos, | ||
526 | __u32 max_data_size, | ||
527 | __u8 max_header_size, | ||
528 | struct sk_buff *skb) | ||
529 | { | ||
530 | struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; | ||
531 | int clen; | ||
532 | |||
533 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); | ||
534 | |||
535 | IRDA_ASSERT(self != NULL, return;); | ||
536 | IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); | ||
537 | |||
538 | self->client = FALSE; | ||
539 | self->max_data_size = max_data_size; | ||
540 | self->max_header_size = max_header_size; | ||
541 | self->flow = FLOW_START; | ||
542 | |||
543 | clen = skb->data[0]; | ||
544 | if (clen) | ||
545 | irda_param_extract_all(self, skb->data+1, | ||
546 | IRDA_MIN(skb->len, clen), | ||
547 | &ircomm_param_info); | ||
548 | |||
549 | ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_INDICATION, NULL, NULL); | ||
550 | |||
551 | /* No need to kfree_skb - see ircomm_ttp_connect_indication() */ | ||
552 | } | ||
553 | |||
554 | /* | ||
555 | * Function ircomm_tty_link_established (self) | ||
556 | * | ||
557 | * Called when the IrCOMM link is established | ||
558 | * | ||
559 | */ | ||
560 | void ircomm_tty_link_established(struct ircomm_tty_cb *self) | ||
561 | { | ||
562 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); | ||
563 | |||
564 | IRDA_ASSERT(self != NULL, return;); | ||
565 | IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); | ||
566 | |||
567 | if (!self->tty) | ||
568 | return; | ||
569 | |||
570 | del_timer(&self->watchdog_timer); | ||
571 | |||
572 | /* | ||
573 | * IrCOMM link is now up, and if we are not using hardware | ||
574 | * flow-control, then declare the hardware as running. Otherwise we | ||
575 | * will have to wait for the peer device (DCE) to raise the CTS | ||
576 | * line. | ||
577 | */ | ||
578 | if ((self->flags & ASYNC_CTS_FLOW) && ((self->settings.dce & IRCOMM_CTS) == 0)) { | ||
579 | IRDA_DEBUG(0, "%s(), waiting for CTS ...\n", __FUNCTION__ ); | ||
580 | return; | ||
581 | } else { | ||
582 | IRDA_DEBUG(1, "%s(), starting hardware!\n", __FUNCTION__ ); | ||
583 | |||
584 | self->tty->hw_stopped = 0; | ||
585 | |||
586 | /* Wake up processes blocked on open */ | ||
587 | wake_up_interruptible(&self->open_wait); | ||
588 | } | ||
589 | |||
590 | schedule_work(&self->tqueue); | ||
591 | } | ||
592 | |||
593 | /* | ||
594 | * Function ircomm_tty_start_watchdog_timer (self, timeout) | ||
595 | * | ||
596 | * Start the watchdog timer. This timer is used to make sure that any | ||
597 | * connection attempt is successful, and if not, we will retry after | ||
598 | * the timeout | ||
599 | */ | ||
600 | static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self, | ||
601 | int timeout) | ||
602 | { | ||
603 | IRDA_ASSERT(self != NULL, return;); | ||
604 | IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); | ||
605 | |||
606 | irda_start_timer(&self->watchdog_timer, timeout, (void *) self, | ||
607 | ircomm_tty_watchdog_timer_expired); | ||
608 | } | ||
609 | |||
610 | /* | ||
611 | * Function ircomm_tty_watchdog_timer_expired (data) | ||
612 | * | ||
613 | * Called when the connect procedure have taken to much time. | ||
614 | * | ||
615 | */ | ||
616 | static void ircomm_tty_watchdog_timer_expired(void *data) | ||
617 | { | ||
618 | struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) data; | ||
619 | |||
620 | IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); | ||
621 | |||
622 | IRDA_ASSERT(self != NULL, return;); | ||
623 | IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); | ||
624 | |||
625 | ircomm_tty_do_event(self, IRCOMM_TTY_WD_TIMER_EXPIRED, NULL, NULL); | ||
626 | } | ||
627 | |||
628 | |||
629 | /* | ||
630 | * Function ircomm_tty_do_event (self, event, skb) | ||
631 | * | ||
632 | * Process event | ||
633 | * | ||
634 | */ | ||
635 | int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event, | ||
636 | struct sk_buff *skb, struct ircomm_tty_info *info) | ||
637 | { | ||
638 | IRDA_ASSERT(self != NULL, return -1;); | ||
639 | IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); | ||
640 | |||
641 | IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ , | ||
642 | ircomm_tty_state[self->state], ircomm_tty_event[event]); | ||
643 | |||
644 | return (*state[self->state])(self, event, skb, info); | ||
645 | } | ||
646 | |||
647 | /* | ||
648 | * Function ircomm_tty_next_state (self, state) | ||
649 | * | ||
650 | * Switch state | ||
651 | * | ||
652 | */ | ||
653 | static inline void ircomm_tty_next_state(struct ircomm_tty_cb *self, IRCOMM_TTY_STATE state) | ||
654 | { | ||
655 | /* | ||
656 | IRDA_ASSERT(self != NULL, return;); | ||
657 | IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); | ||
658 | |||
659 | IRDA_DEBUG(2, "%s: next state=%s, service type=%d\n", __FUNCTION__ , | ||
660 | ircomm_tty_state[self->state], self->service_type); | ||
661 | */ | ||
662 | self->state = state; | ||
663 | } | ||
664 | |||
665 | /* | ||
666 | * Function ircomm_tty_state_idle (self, event, skb, info) | ||
667 | * | ||
668 | * Just hanging around | ||
669 | * | ||
670 | */ | ||
671 | static int ircomm_tty_state_idle(struct ircomm_tty_cb *self, | ||
672 | IRCOMM_TTY_EVENT event, | ||
673 | struct sk_buff *skb, | ||
674 | struct ircomm_tty_info *info) | ||
675 | { | ||
676 | int ret = 0; | ||
677 | |||
678 | IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ , | ||
679 | ircomm_tty_state[self->state], ircomm_tty_event[event]); | ||
680 | switch (event) { | ||
681 | case IRCOMM_TTY_ATTACH_CABLE: | ||
682 | /* Try to discover any remote devices */ | ||
683 | ircomm_tty_start_watchdog_timer(self, 3*HZ); | ||
684 | ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); | ||
685 | |||
686 | irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS); | ||
687 | break; | ||
688 | case IRCOMM_TTY_DISCOVERY_INDICATION: | ||
689 | self->daddr = info->daddr; | ||
690 | self->saddr = info->saddr; | ||
691 | |||
692 | if (self->iriap) { | ||
693 | IRDA_WARNING("%s(), busy with a previous query\n", | ||
694 | __FUNCTION__); | ||
695 | return -EBUSY; | ||
696 | } | ||
697 | |||
698 | self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, | ||
699 | ircomm_tty_getvalue_confirm); | ||
700 | |||
701 | iriap_getvaluebyclass_request(self->iriap, | ||
702 | self->saddr, self->daddr, | ||
703 | "IrDA:IrCOMM", "Parameters"); | ||
704 | |||
705 | ircomm_tty_start_watchdog_timer(self, 3*HZ); | ||
706 | ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS); | ||
707 | break; | ||
708 | case IRCOMM_TTY_CONNECT_INDICATION: | ||
709 | del_timer(&self->watchdog_timer); | ||
710 | |||
711 | /* Accept connection */ | ||
712 | ircomm_connect_response(self->ircomm, NULL); | ||
713 | ircomm_tty_next_state(self, IRCOMM_TTY_READY); | ||
714 | break; | ||
715 | case IRCOMM_TTY_WD_TIMER_EXPIRED: | ||
716 | /* Just stay idle */ | ||
717 | break; | ||
718 | case IRCOMM_TTY_DETACH_CABLE: | ||
719 | ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); | ||
720 | break; | ||
721 | default: | ||
722 | IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ , | ||
723 | ircomm_tty_event[event]); | ||
724 | ret = -EINVAL; | ||
725 | } | ||
726 | return ret; | ||
727 | } | ||
728 | |||
729 | /* | ||
730 | * Function ircomm_tty_state_search (self, event, skb, info) | ||
731 | * | ||
732 | * Trying to discover an IrCOMM device | ||
733 | * | ||
734 | */ | ||
735 | static int ircomm_tty_state_search(struct ircomm_tty_cb *self, | ||
736 | IRCOMM_TTY_EVENT event, | ||
737 | struct sk_buff *skb, | ||
738 | struct ircomm_tty_info *info) | ||
739 | { | ||
740 | int ret = 0; | ||
741 | |||
742 | IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ , | ||
743 | ircomm_tty_state[self->state], ircomm_tty_event[event]); | ||
744 | |||
745 | switch (event) { | ||
746 | case IRCOMM_TTY_DISCOVERY_INDICATION: | ||
747 | self->daddr = info->daddr; | ||
748 | self->saddr = info->saddr; | ||
749 | |||
750 | if (self->iriap) { | ||
751 | IRDA_WARNING("%s(), busy with a previous query\n", | ||
752 | __FUNCTION__); | ||
753 | return -EBUSY; | ||
754 | } | ||
755 | |||
756 | self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, | ||
757 | ircomm_tty_getvalue_confirm); | ||
758 | |||
759 | if (self->service_type == IRCOMM_3_WIRE_RAW) { | ||
760 | iriap_getvaluebyclass_request(self->iriap, self->saddr, | ||
761 | self->daddr, "IrLPT", | ||
762 | "IrDA:IrLMP:LsapSel"); | ||
763 | ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL); | ||
764 | } else { | ||
765 | iriap_getvaluebyclass_request(self->iriap, self->saddr, | ||
766 | self->daddr, | ||
767 | "IrDA:IrCOMM", | ||
768 | "Parameters"); | ||
769 | |||
770 | ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS); | ||
771 | } | ||
772 | ircomm_tty_start_watchdog_timer(self, 3*HZ); | ||
773 | break; | ||
774 | case IRCOMM_TTY_CONNECT_INDICATION: | ||
775 | del_timer(&self->watchdog_timer); | ||
776 | ircomm_tty_ias_unregister(self); | ||
777 | |||
778 | /* Accept connection */ | ||
779 | ircomm_connect_response(self->ircomm, NULL); | ||
780 | ircomm_tty_next_state(self, IRCOMM_TTY_READY); | ||
781 | break; | ||
782 | case IRCOMM_TTY_WD_TIMER_EXPIRED: | ||
783 | #if 1 | ||
784 | /* Give up */ | ||
785 | #else | ||
786 | /* Try to discover any remote devices */ | ||
787 | ircomm_tty_start_watchdog_timer(self, 3*HZ); | ||
788 | irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS); | ||
789 | #endif | ||
790 | break; | ||
791 | case IRCOMM_TTY_DETACH_CABLE: | ||
792 | ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); | ||
793 | break; | ||
794 | default: | ||
795 | IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ , | ||
796 | ircomm_tty_event[event]); | ||
797 | ret = -EINVAL; | ||
798 | } | ||
799 | return ret; | ||
800 | } | ||
801 | |||
802 | /* | ||
803 | * Function ircomm_tty_state_query (self, event, skb, info) | ||
804 | * | ||
805 | * Querying the remote LM-IAS for IrCOMM parameters | ||
806 | * | ||
807 | */ | ||
808 | static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self, | ||
809 | IRCOMM_TTY_EVENT event, | ||
810 | struct sk_buff *skb, | ||
811 | struct ircomm_tty_info *info) | ||
812 | { | ||
813 | int ret = 0; | ||
814 | |||
815 | IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ , | ||
816 | ircomm_tty_state[self->state], ircomm_tty_event[event]); | ||
817 | |||
818 | switch (event) { | ||
819 | case IRCOMM_TTY_GOT_PARAMETERS: | ||
820 | if (self->iriap) { | ||
821 | IRDA_WARNING("%s(), busy with a previous query\n", | ||
822 | __FUNCTION__); | ||
823 | return -EBUSY; | ||
824 | } | ||
825 | |||
826 | self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, | ||
827 | ircomm_tty_getvalue_confirm); | ||
828 | |||
829 | iriap_getvaluebyclass_request(self->iriap, self->saddr, | ||
830 | self->daddr, "IrDA:IrCOMM", | ||
831 | "IrDA:TinyTP:LsapSel"); | ||
832 | |||
833 | ircomm_tty_start_watchdog_timer(self, 3*HZ); | ||
834 | ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL); | ||
835 | break; | ||
836 | case IRCOMM_TTY_WD_TIMER_EXPIRED: | ||
837 | /* Go back to search mode */ | ||
838 | ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); | ||
839 | ircomm_tty_start_watchdog_timer(self, 3*HZ); | ||
840 | break; | ||
841 | case IRCOMM_TTY_CONNECT_INDICATION: | ||
842 | del_timer(&self->watchdog_timer); | ||
843 | ircomm_tty_ias_unregister(self); | ||
844 | |||
845 | /* Accept connection */ | ||
846 | ircomm_connect_response(self->ircomm, NULL); | ||
847 | ircomm_tty_next_state(self, IRCOMM_TTY_READY); | ||
848 | break; | ||
849 | case IRCOMM_TTY_DETACH_CABLE: | ||
850 | ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); | ||
851 | break; | ||
852 | default: | ||
853 | IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ , | ||
854 | ircomm_tty_event[event]); | ||
855 | ret = -EINVAL; | ||
856 | } | ||
857 | return ret; | ||
858 | } | ||
859 | |||
860 | /* | ||
861 | * Function ircomm_tty_state_query_lsap_sel (self, event, skb, info) | ||
862 | * | ||
863 | * Query remote LM-IAS for the LSAP selector which we can connect to | ||
864 | * | ||
865 | */ | ||
866 | static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self, | ||
867 | IRCOMM_TTY_EVENT event, | ||
868 | struct sk_buff *skb, | ||
869 | struct ircomm_tty_info *info) | ||
870 | { | ||
871 | int ret = 0; | ||
872 | |||
873 | IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ , | ||
874 | ircomm_tty_state[self->state], ircomm_tty_event[event]); | ||
875 | |||
876 | switch (event) { | ||
877 | case IRCOMM_TTY_GOT_LSAPSEL: | ||
878 | /* Connect to remote device */ | ||
879 | ret = ircomm_connect_request(self->ircomm, self->dlsap_sel, | ||
880 | self->saddr, self->daddr, | ||
881 | NULL, self->service_type); | ||
882 | ircomm_tty_start_watchdog_timer(self, 3*HZ); | ||
883 | ircomm_tty_next_state(self, IRCOMM_TTY_SETUP); | ||
884 | break; | ||
885 | case IRCOMM_TTY_WD_TIMER_EXPIRED: | ||
886 | /* Go back to search mode */ | ||
887 | ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); | ||
888 | ircomm_tty_start_watchdog_timer(self, 3*HZ); | ||
889 | break; | ||
890 | case IRCOMM_TTY_CONNECT_INDICATION: | ||
891 | del_timer(&self->watchdog_timer); | ||
892 | ircomm_tty_ias_unregister(self); | ||
893 | |||
894 | /* Accept connection */ | ||
895 | ircomm_connect_response(self->ircomm, NULL); | ||
896 | ircomm_tty_next_state(self, IRCOMM_TTY_READY); | ||
897 | break; | ||
898 | case IRCOMM_TTY_DETACH_CABLE: | ||
899 | ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); | ||
900 | break; | ||
901 | default: | ||
902 | IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ , | ||
903 | ircomm_tty_event[event]); | ||
904 | ret = -EINVAL; | ||
905 | } | ||
906 | return ret; | ||
907 | } | ||
908 | |||
909 | /* | ||
910 | * Function ircomm_tty_state_setup (self, event, skb, info) | ||
911 | * | ||
912 | * Trying to connect | ||
913 | * | ||
914 | */ | ||
915 | static int ircomm_tty_state_setup(struct ircomm_tty_cb *self, | ||
916 | IRCOMM_TTY_EVENT event, | ||
917 | struct sk_buff *skb, | ||
918 | struct ircomm_tty_info *info) | ||
919 | { | ||
920 | int ret = 0; | ||
921 | |||
922 | IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __FUNCTION__ , | ||
923 | ircomm_tty_state[self->state], ircomm_tty_event[event]); | ||
924 | |||
925 | switch (event) { | ||
926 | case IRCOMM_TTY_CONNECT_CONFIRM: | ||
927 | del_timer(&self->watchdog_timer); | ||
928 | ircomm_tty_ias_unregister(self); | ||
929 | |||
930 | /* | ||
931 | * Send initial parameters. This will also send out queued | ||
932 | * parameters waiting for the connection to come up | ||
933 | */ | ||
934 | ircomm_tty_send_initial_parameters(self); | ||
935 | ircomm_tty_link_established(self); | ||
936 | ircomm_tty_next_state(self, IRCOMM_TTY_READY); | ||
937 | break; | ||
938 | case IRCOMM_TTY_CONNECT_INDICATION: | ||
939 | del_timer(&self->watchdog_timer); | ||
940 | ircomm_tty_ias_unregister(self); | ||
941 | |||
942 | /* Accept connection */ | ||
943 | ircomm_connect_response(self->ircomm, NULL); | ||
944 | ircomm_tty_next_state(self, IRCOMM_TTY_READY); | ||
945 | break; | ||
946 | case IRCOMM_TTY_WD_TIMER_EXPIRED: | ||
947 | /* Go back to search mode */ | ||
948 | ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); | ||
949 | ircomm_tty_start_watchdog_timer(self, 3*HZ); | ||
950 | break; | ||
951 | case IRCOMM_TTY_DETACH_CABLE: | ||
952 | /* ircomm_disconnect_request(self->ircomm, NULL); */ | ||
953 | ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); | ||
954 | break; | ||
955 | default: | ||
956 | IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ , | ||
957 | ircomm_tty_event[event]); | ||
958 | ret = -EINVAL; | ||
959 | } | ||
960 | return ret; | ||
961 | } | ||
962 | |||
963 | /* | ||
964 | * Function ircomm_tty_state_ready (self, event, skb, info) | ||
965 | * | ||
966 | * IrCOMM is now connected | ||
967 | * | ||
968 | */ | ||
969 | static int ircomm_tty_state_ready(struct ircomm_tty_cb *self, | ||
970 | IRCOMM_TTY_EVENT event, | ||
971 | struct sk_buff *skb, | ||
972 | struct ircomm_tty_info *info) | ||
973 | { | ||
974 | int ret = 0; | ||
975 | |||
976 | switch (event) { | ||
977 | case IRCOMM_TTY_DATA_REQUEST: | ||
978 | ret = ircomm_data_request(self->ircomm, skb); | ||
979 | break; | ||
980 | case IRCOMM_TTY_DETACH_CABLE: | ||
981 | ircomm_disconnect_request(self->ircomm, NULL); | ||
982 | ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); | ||
983 | break; | ||
984 | case IRCOMM_TTY_DISCONNECT_INDICATION: | ||
985 | ircomm_tty_ias_register(self); | ||
986 | ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); | ||
987 | ircomm_tty_start_watchdog_timer(self, 3*HZ); | ||
988 | |||
989 | if (self->flags & ASYNC_CHECK_CD) { | ||
990 | /* Drop carrier */ | ||
991 | self->settings.dce = IRCOMM_DELTA_CD; | ||
992 | ircomm_tty_check_modem_status(self); | ||
993 | } else { | ||
994 | IRDA_DEBUG(0, "%s(), hanging up!\n", __FUNCTION__ ); | ||
995 | if (self->tty) | ||
996 | tty_hangup(self->tty); | ||
997 | } | ||
998 | break; | ||
999 | default: | ||
1000 | IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ , | ||
1001 | ircomm_tty_event[event]); | ||
1002 | ret = -EINVAL; | ||
1003 | } | ||
1004 | return ret; | ||
1005 | } | ||
1006 | |||