xref: /openbmc/linux/net/bluetooth/cmtp/capi.c (revision 1fa6ac37)
1 /*
2    CMTP implementation for Linux Bluetooth stack (BlueZ).
3    Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License version 2 as
7    published by the Free Software Foundation;
8 
9    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
12    IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
13    CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
14    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 
18    ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
19    COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
20    SOFTWARE IS DISCLAIMED.
21 */
22 
23 #include <linux/module.h>
24 #include <linux/proc_fs.h>
25 #include <linux/seq_file.h>
26 #include <linux/types.h>
27 #include <linux/errno.h>
28 #include <linux/kernel.h>
29 #include <linux/sched.h>
30 #include <linux/slab.h>
31 #include <linux/poll.h>
32 #include <linux/fcntl.h>
33 #include <linux/skbuff.h>
34 #include <linux/socket.h>
35 #include <linux/ioctl.h>
36 #include <linux/file.h>
37 #include <linux/wait.h>
38 #include <net/sock.h>
39 
40 #include <linux/isdn/capilli.h>
41 #include <linux/isdn/capicmd.h>
42 #include <linux/isdn/capiutil.h>
43 
44 #include "cmtp.h"
45 
46 #define CAPI_INTEROPERABILITY		0x20
47 
48 #define CAPI_INTEROPERABILITY_REQ	CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
49 #define CAPI_INTEROPERABILITY_CONF	CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
50 #define CAPI_INTEROPERABILITY_IND	CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
51 #define CAPI_INTEROPERABILITY_RESP	CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
52 
53 #define CAPI_INTEROPERABILITY_REQ_LEN	(CAPI_MSG_BASELEN + 2)
54 #define CAPI_INTEROPERABILITY_CONF_LEN	(CAPI_MSG_BASELEN + 4)
55 #define CAPI_INTEROPERABILITY_IND_LEN	(CAPI_MSG_BASELEN + 2)
56 #define CAPI_INTEROPERABILITY_RESP_LEN	(CAPI_MSG_BASELEN + 2)
57 
58 #define CAPI_FUNCTION_REGISTER		0
59 #define CAPI_FUNCTION_RELEASE		1
60 #define CAPI_FUNCTION_GET_PROFILE	2
61 #define CAPI_FUNCTION_GET_MANUFACTURER	3
62 #define CAPI_FUNCTION_GET_VERSION	4
63 #define CAPI_FUNCTION_GET_SERIAL_NUMBER	5
64 #define CAPI_FUNCTION_MANUFACTURER	6
65 #define CAPI_FUNCTION_LOOPBACK		7
66 
67 
68 #define CMTP_MSGNUM	1
69 #define CMTP_APPLID	2
70 #define CMTP_MAPPING	3
71 
72 static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
73 {
74 	struct cmtp_application *app = kzalloc(sizeof(*app), GFP_KERNEL);
75 
76 	BT_DBG("session %p application %p appl %d", session, app, appl);
77 
78 	if (!app)
79 		return NULL;
80 
81 	app->state = BT_OPEN;
82 	app->appl = appl;
83 
84 	list_add_tail(&app->list, &session->applications);
85 
86 	return app;
87 }
88 
89 static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
90 {
91 	BT_DBG("session %p application %p", session, app);
92 
93 	if (app) {
94 		list_del(&app->list);
95 		kfree(app);
96 	}
97 }
98 
99 static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
100 {
101 	struct cmtp_application *app;
102 	struct list_head *p, *n;
103 
104 	list_for_each_safe(p, n, &session->applications) {
105 		app = list_entry(p, struct cmtp_application, list);
106 		switch (pattern) {
107 		case CMTP_MSGNUM:
108 			if (app->msgnum == value)
109 				return app;
110 			break;
111 		case CMTP_APPLID:
112 			if (app->appl == value)
113 				return app;
114 			break;
115 		case CMTP_MAPPING:
116 			if (app->mapping == value)
117 				return app;
118 			break;
119 		}
120 	}
121 
122 	return NULL;
123 }
124 
125 static int cmtp_msgnum_get(struct cmtp_session *session)
126 {
127 	session->msgnum++;
128 
129 	if ((session->msgnum & 0xff) > 200)
130 		session->msgnum = CMTP_INITIAL_MSGNUM + 1;
131 
132 	return session->msgnum;
133 }
134 
135 static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
136 {
137 	struct cmtp_scb *scb = (void *) skb->cb;
138 
139 	BT_DBG("session %p skb %p len %d", session, skb, skb->len);
140 
141 	scb->id = -1;
142 	scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
143 
144 	skb_queue_tail(&session->transmit, skb);
145 
146 	cmtp_schedule(session);
147 }
148 
149 static void cmtp_send_interopmsg(struct cmtp_session *session,
150 					__u8 subcmd, __u16 appl, __u16 msgnum,
151 					__u16 function, unsigned char *buf, int len)
152 {
153 	struct sk_buff *skb;
154 	unsigned char *s;
155 
156 	BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum);
157 
158 	if (!(skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC))) {
159 		BT_ERR("Can't allocate memory for interoperability packet");
160 		return;
161 	}
162 
163 	s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
164 
165 	capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
166 	capimsg_setu16(s, 2, appl);
167 	capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
168 	capimsg_setu8 (s, 5, subcmd);
169 	capimsg_setu16(s, 6, msgnum);
170 
171 	/* Interoperability selector (Bluetooth Device Management) */
172 	capimsg_setu16(s, 8, 0x0001);
173 
174 	capimsg_setu8 (s, 10, 3 + len);
175 	capimsg_setu16(s, 11, function);
176 	capimsg_setu8 (s, 13, len);
177 
178 	if (len > 0)
179 		memcpy(s + 14, buf, len);
180 
181 	cmtp_send_capimsg(session, skb);
182 }
183 
184 static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
185 {
186 	struct capi_ctr *ctrl = &session->ctrl;
187 	struct cmtp_application *application;
188 	__u16 appl, msgnum, func, info;
189 	__u32 controller;
190 
191 	BT_DBG("session %p skb %p len %d", session, skb, skb->len);
192 
193 	switch (CAPIMSG_SUBCOMMAND(skb->data)) {
194 	case CAPI_CONF:
195 		if (skb->len < CAPI_MSG_BASELEN + 10)
196 			break;
197 
198 		func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
199 		info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
200 
201 		switch (func) {
202 		case CAPI_FUNCTION_REGISTER:
203 			msgnum = CAPIMSG_MSGID(skb->data);
204 
205 			application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
206 			if (application) {
207 				application->state = BT_CONNECTED;
208 				application->msgnum = 0;
209 				application->mapping = CAPIMSG_APPID(skb->data);
210 				wake_up_interruptible(&session->wait);
211 			}
212 
213 			break;
214 
215 		case CAPI_FUNCTION_RELEASE:
216 			appl = CAPIMSG_APPID(skb->data);
217 
218 			application = cmtp_application_get(session, CMTP_MAPPING, appl);
219 			if (application) {
220 				application->state = BT_CLOSED;
221 				application->msgnum = 0;
222 				wake_up_interruptible(&session->wait);
223 			}
224 
225 			break;
226 
227 		case CAPI_FUNCTION_GET_PROFILE:
228 			if (skb->len < CAPI_MSG_BASELEN + 11 + sizeof(capi_profile))
229 				break;
230 
231 			controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
232 			msgnum = CAPIMSG_MSGID(skb->data);
233 
234 			if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
235 				session->ncontroller = controller;
236 				wake_up_interruptible(&session->wait);
237 				break;
238 			}
239 
240 			if (!info && ctrl) {
241 				memcpy(&ctrl->profile,
242 					skb->data + CAPI_MSG_BASELEN + 11,
243 					sizeof(capi_profile));
244 				session->state = BT_CONNECTED;
245 				capi_ctr_ready(ctrl);
246 			}
247 
248 			break;
249 
250 		case CAPI_FUNCTION_GET_MANUFACTURER:
251 			if (skb->len < CAPI_MSG_BASELEN + 15)
252 				break;
253 
254 			controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10);
255 
256 			if (!info && ctrl) {
257 				int len = min_t(uint, CAPI_MANUFACTURER_LEN,
258 						skb->data[CAPI_MSG_BASELEN + 14]);
259 
260 				memset(ctrl->manu, 0, CAPI_MANUFACTURER_LEN);
261 				strncpy(ctrl->manu,
262 					skb->data + CAPI_MSG_BASELEN + 15, len);
263 			}
264 
265 			break;
266 
267 		case CAPI_FUNCTION_GET_VERSION:
268 			if (skb->len < CAPI_MSG_BASELEN + 32)
269 				break;
270 
271 			controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
272 
273 			if (!info && ctrl) {
274 				ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
275 				ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
276 				ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
277 				ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
278 			}
279 
280 			break;
281 
282 		case CAPI_FUNCTION_GET_SERIAL_NUMBER:
283 			if (skb->len < CAPI_MSG_BASELEN + 17)
284 				break;
285 
286 			controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12);
287 
288 			if (!info && ctrl) {
289 				int len = min_t(uint, CAPI_SERIAL_LEN,
290 						skb->data[CAPI_MSG_BASELEN + 16]);
291 
292 				memset(ctrl->serial, 0, CAPI_SERIAL_LEN);
293 				strncpy(ctrl->serial,
294 					skb->data + CAPI_MSG_BASELEN + 17, len);
295 			}
296 
297 			break;
298 		}
299 
300 		break;
301 
302 	case CAPI_IND:
303 		if (skb->len < CAPI_MSG_BASELEN + 6)
304 			break;
305 
306 		func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
307 
308 		if (func == CAPI_FUNCTION_LOOPBACK) {
309 			int len = min_t(uint, skb->len - CAPI_MSG_BASELEN - 6,
310 						skb->data[CAPI_MSG_BASELEN + 5]);
311 			appl = CAPIMSG_APPID(skb->data);
312 			msgnum = CAPIMSG_MSGID(skb->data);
313 			cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
314 						skb->data + CAPI_MSG_BASELEN + 6, len);
315 		}
316 
317 		break;
318 	}
319 
320 	kfree_skb(skb);
321 }
322 
323 void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
324 {
325 	struct capi_ctr *ctrl = &session->ctrl;
326 	struct cmtp_application *application;
327 	__u16 cmd, appl;
328 	__u32 contr;
329 
330 	BT_DBG("session %p skb %p len %d", session, skb, skb->len);
331 
332 	if (skb->len < CAPI_MSG_BASELEN)
333 		return;
334 
335 	if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
336 		cmtp_recv_interopmsg(session, skb);
337 		return;
338 	}
339 
340 	if (session->flags & (1 << CMTP_LOOPBACK)) {
341 		kfree_skb(skb);
342 		return;
343 	}
344 
345 	cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data));
346 	appl = CAPIMSG_APPID(skb->data);
347 	contr = CAPIMSG_CONTROL(skb->data);
348 
349 	application = cmtp_application_get(session, CMTP_MAPPING, appl);
350 	if (application) {
351 		appl = application->appl;
352 		CAPIMSG_SETAPPID(skb->data, appl);
353 	} else {
354 		BT_ERR("Can't find application with id %d", appl);
355 		kfree_skb(skb);
356 		return;
357 	}
358 
359 	if ((contr & 0x7f) == 0x01) {
360 		contr = (contr & 0xffffff80) | session->num;
361 		CAPIMSG_SETCONTROL(skb->data, contr);
362 	}
363 
364 	if (!ctrl) {
365 		BT_ERR("Can't find controller %d for message", session->num);
366 		kfree_skb(skb);
367 		return;
368 	}
369 
370 	capi_ctr_handle_message(ctrl, appl, skb);
371 }
372 
373 static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
374 {
375 	BT_DBG("ctrl %p data %p", ctrl, data);
376 
377 	return 0;
378 }
379 
380 static void cmtp_reset_ctr(struct capi_ctr *ctrl)
381 {
382 	struct cmtp_session *session = ctrl->driverdata;
383 
384 	BT_DBG("ctrl %p", ctrl);
385 
386 	capi_ctr_down(ctrl);
387 
388 	atomic_inc(&session->terminate);
389 	cmtp_schedule(session);
390 }
391 
392 static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
393 {
394 	DECLARE_WAITQUEUE(wait, current);
395 	struct cmtp_session *session = ctrl->driverdata;
396 	struct cmtp_application *application;
397 	unsigned long timeo = CMTP_INTEROP_TIMEOUT;
398 	unsigned char buf[8];
399 	int err = 0, nconn, want = rp->level3cnt;
400 
401 	BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d",
402 		ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
403 
404 	application = cmtp_application_add(session, appl);
405 	if (!application) {
406 		BT_ERR("Can't allocate memory for new application");
407 		return;
408 	}
409 
410 	if (want < 0)
411 		nconn = ctrl->profile.nbchannel * -want;
412 	else
413 		nconn = want;
414 
415 	if (nconn == 0)
416 		nconn = ctrl->profile.nbchannel;
417 
418 	capimsg_setu16(buf, 0, nconn);
419 	capimsg_setu16(buf, 2, rp->datablkcnt);
420 	capimsg_setu16(buf, 4, rp->datablklen);
421 
422 	application->state = BT_CONFIG;
423 	application->msgnum = cmtp_msgnum_get(session);
424 
425 	cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
426 				CAPI_FUNCTION_REGISTER, buf, 6);
427 
428 	add_wait_queue(&session->wait, &wait);
429 	while (1) {
430 		set_current_state(TASK_INTERRUPTIBLE);
431 
432 		if (!timeo) {
433 			err = -EAGAIN;
434 			break;
435 		}
436 
437 		if (application->state == BT_CLOSED) {
438 			err = -application->err;
439 			break;
440 		}
441 
442 		if (application->state == BT_CONNECTED)
443 			break;
444 
445 		if (signal_pending(current)) {
446 			err = -EINTR;
447 			break;
448 		}
449 
450 		timeo = schedule_timeout(timeo);
451 	}
452 	set_current_state(TASK_RUNNING);
453 	remove_wait_queue(&session->wait, &wait);
454 
455 	if (err) {
456 		cmtp_application_del(session, application);
457 		return;
458 	}
459 }
460 
461 static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
462 {
463 	struct cmtp_session *session = ctrl->driverdata;
464 	struct cmtp_application *application;
465 
466 	BT_DBG("ctrl %p appl %d", ctrl, appl);
467 
468 	application = cmtp_application_get(session, CMTP_APPLID, appl);
469 	if (!application) {
470 		BT_ERR("Can't find application");
471 		return;
472 	}
473 
474 	application->msgnum = cmtp_msgnum_get(session);
475 
476 	cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
477 				CAPI_FUNCTION_RELEASE, NULL, 0);
478 
479 	wait_event_interruptible_timeout(session->wait,
480 			(application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT);
481 
482 	cmtp_application_del(session, application);
483 }
484 
485 static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
486 {
487 	struct cmtp_session *session = ctrl->driverdata;
488 	struct cmtp_application *application;
489 	__u16 appl;
490 	__u32 contr;
491 
492 	BT_DBG("ctrl %p skb %p", ctrl, skb);
493 
494 	appl = CAPIMSG_APPID(skb->data);
495 	contr = CAPIMSG_CONTROL(skb->data);
496 
497 	application = cmtp_application_get(session, CMTP_APPLID, appl);
498 	if ((!application) || (application->state != BT_CONNECTED)) {
499 		BT_ERR("Can't find application with id %d", appl);
500 		return CAPI_ILLAPPNR;
501 	}
502 
503 	CAPIMSG_SETAPPID(skb->data, application->mapping);
504 
505 	if ((contr & 0x7f) == session->num) {
506 		contr = (contr & 0xffffff80) | 0x01;
507 		CAPIMSG_SETCONTROL(skb->data, contr);
508 	}
509 
510 	cmtp_send_capimsg(session, skb);
511 
512 	return CAPI_NOERROR;
513 }
514 
515 static char *cmtp_procinfo(struct capi_ctr *ctrl)
516 {
517 	return "CAPI Message Transport Protocol";
518 }
519 
520 static int cmtp_proc_show(struct seq_file *m, void *v)
521 {
522 	struct capi_ctr *ctrl = m->private;
523 	struct cmtp_session *session = ctrl->driverdata;
524 	struct cmtp_application *app;
525 	struct list_head *p, *n;
526 
527 	seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl));
528 	seq_printf(m, "addr %s\n", session->name);
529 	seq_printf(m, "ctrl %d\n", session->num);
530 
531 	list_for_each_safe(p, n, &session->applications) {
532 		app = list_entry(p, struct cmtp_application, list);
533 		seq_printf(m, "appl %d -> %d\n", app->appl, app->mapping);
534 	}
535 
536 	return 0;
537 }
538 
539 static int cmtp_proc_open(struct inode *inode, struct file *file)
540 {
541 	return single_open(file, cmtp_proc_show, PDE(inode)->data);
542 }
543 
544 static const struct file_operations cmtp_proc_fops = {
545 	.owner		= THIS_MODULE,
546 	.open		= cmtp_proc_open,
547 	.read		= seq_read,
548 	.llseek		= seq_lseek,
549 	.release	= single_release,
550 };
551 
552 int cmtp_attach_device(struct cmtp_session *session)
553 {
554 	unsigned char buf[4];
555 	long ret;
556 
557 	BT_DBG("session %p", session);
558 
559 	capimsg_setu32(buf, 0, 0);
560 
561 	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
562 				CAPI_FUNCTION_GET_PROFILE, buf, 4);
563 
564 	ret = wait_event_interruptible_timeout(session->wait,
565 			session->ncontroller, CMTP_INTEROP_TIMEOUT);
566 
567 	BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
568 
569 	if (!ret)
570 		return -ETIMEDOUT;
571 
572 	if (!session->ncontroller)
573 		return -ENODEV;
574 
575 	if (session->ncontroller > 1)
576 		BT_INFO("Setting up only CAPI controller 1");
577 
578 	session->ctrl.owner      = THIS_MODULE;
579 	session->ctrl.driverdata = session;
580 	strcpy(session->ctrl.name, session->name);
581 
582 	session->ctrl.driver_name   = "cmtp";
583 	session->ctrl.load_firmware = cmtp_load_firmware;
584 	session->ctrl.reset_ctr     = cmtp_reset_ctr;
585 	session->ctrl.register_appl = cmtp_register_appl;
586 	session->ctrl.release_appl  = cmtp_release_appl;
587 	session->ctrl.send_message  = cmtp_send_message;
588 
589 	session->ctrl.procinfo      = cmtp_procinfo;
590 	session->ctrl.proc_fops = &cmtp_proc_fops;
591 
592 	if (attach_capi_ctr(&session->ctrl) < 0) {
593 		BT_ERR("Can't attach new controller");
594 		return -EBUSY;
595 	}
596 
597 	session->num = session->ctrl.cnr;
598 
599 	BT_DBG("session %p num %d", session, session->num);
600 
601 	capimsg_setu32(buf, 0, 1);
602 
603 	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
604 				CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
605 
606 	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
607 				CAPI_FUNCTION_GET_VERSION, buf, 4);
608 
609 	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
610 				CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
611 
612 	cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
613 				CAPI_FUNCTION_GET_PROFILE, buf, 4);
614 
615 	return 0;
616 }
617 
618 void cmtp_detach_device(struct cmtp_session *session)
619 {
620 	BT_DBG("session %p", session);
621 
622 	detach_capi_ctr(&session->ctrl);
623 }
624