i4l.c 18.5 KB
Newer Older
1 2 3
/*
 * Stuff used by all variants of the driver
 *
4 5 6
 * Copyright (c) 2001 by Stefan Eilers,
 *                       Hansjoerg Lipp <hjlipp@web.de>,
 *                       Tilman Schmidt <tilman@imap.cc>.
7 8 9 10 11 12 13 14 15 16
 *
 * =====================================================================
 *	This program is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU General Public License as
 *	published by the Free Software Foundation; either version 2 of
 *	the License, or (at your option) any later version.
 * =====================================================================
 */

#include "gigaset.h"
17 18 19
#include <linux/isdnif.h>

#define HW_HDR_LEN	2	/* Header size used to store ack info */
20

21
/* == Handling of I4L IO =====================================================*/
22 23 24 25 26 27 28 29 30

/* writebuf_from_LL
 * called by LL to transmit data on an open channel
 * inserts the buffer data into the send queue and starts the transmission
 * Note that this operation must not sleep!
 * When the buffer is processed completely, gigaset_skb_sent() should be called.
 * parameters:
 *	driverID	driver ID as assigned by LL
 *	channel		channel number
31 32
 *	ack		if != 0 LL wants to be notified on completion via
 *			statcallb(ISDN_STAT_BSENT)
33 34 35 36 37 38
 *	skb		skb containing data to send
 * return value:
 *	number of accepted bytes
 *	0 if temporarily unable to accept data (out of buffer space)
 *	<0 on error (eg. -EINVAL)
 */
39 40
static int writebuf_from_LL(int driverID, int channel, int ack,
			    struct sk_buff *skb)
41 42 43
{
	struct cardstate *cs;
	struct bc_state *bcs;
44
	unsigned char *ack_header;
45 46 47
	unsigned len;

	if (!(cs = gigaset_get_cs_by_id(driverID))) {
48
		pr_err("%s: invalid driver ID (%d)\n", __func__, driverID);
49 50 51
		return -ENODEV;
	}
	if (channel < 0 || channel >= cs->channels) {
52 53
		dev_err(cs->dev, "%s: invalid channel ID (%d)\n",
			__func__, channel);
54 55 56
		return -ENODEV;
	}
	bcs = &cs->bcs[channel];
T
Tilman Schmidt 已提交
57 58 59 60 61 62

	/* can only handle linear sk_buffs */
	if (skb_linearize(skb) < 0) {
		dev_err(cs->dev, "%s: skb_linearize failed\n", __func__);
		return -ENOMEM;
	}
63 64
	len = skb->len;

65 66 67 68
	gig_dbg(DEBUG_LLDATA,
		"Receiving data from LL (id: %d, ch: %d, ack: %d, sz: %d)",
		driverID, channel, ack, len);

69 70
	if (!len) {
		if (ack)
71 72
			dev_notice(cs->dev, "%s: not ACKing empty packet\n",
				   __func__);
73 74 75
		return 0;
	}
	if (len > MAX_BUF_SIZE) {
76 77
		dev_err(cs->dev, "%s: packet too large (%d bytes)\n",
			__func__, len);
78 79 80
		return -EINVAL;
	}

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
	/* set up acknowledgement header */
	if (skb_headroom(skb) < HW_HDR_LEN) {
		/* should never happen */
		dev_err(cs->dev, "%s: insufficient skb headroom\n", __func__);
		return -ENOMEM;
	}
	skb_set_mac_header(skb, -HW_HDR_LEN);
	skb->mac_len = HW_HDR_LEN;
	ack_header = skb_mac_header(skb);
	if (ack) {
		ack_header[0] = len & 0xff;
		ack_header[1] = len >> 8;
	} else {
		ack_header[0] = ack_header[1] = 0;
	}
	gig_dbg(DEBUG_MCMD, "skb: len=%u, ack=%d: %02x %02x",
		len, ack, ack_header[0], ack_header[1]);
98 99

	/* pass to device-specific module */
100
	return cs->ops->send_skb(bcs, skb);
101 102
}

T
Tilman Schmidt 已提交
103 104 105 106 107 108 109 110
/**
 * gigaset_skb_sent() - acknowledge sending an skb
 * @bcs:	B channel descriptor structure.
 * @skb:	sent data.
 *
 * Called by hardware module {bas,ser,usb}_gigaset when the data in a
 * skb has been successfully sent, for signalling completion to the LL.
 */
111 112
void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
{
113
	isdn_if *iif = bcs->cs->iif;
114
	unsigned char *ack_header = skb_mac_header(skb);
115 116 117 118 119 120
	unsigned len;
	isdn_ctrl response;

	++bcs->trans_up;

	if (skb->len)
121 122
		dev_warn(bcs->cs->dev, "%s: skb->len==%d\n",
			 __func__, skb->len);
123

124
	len = ack_header[0] + ((unsigned) ack_header[1] << 8);
125
	if (len) {
126 127
		gig_dbg(DEBUG_MCMD, "ACKing to LL (id: %d, ch: %d, sz: %u)",
			bcs->cs->myid, bcs->channel, len);
128 129 130 131 132

		response.driver = bcs->cs->myid;
		response.command = ISDN_STAT_BSENT;
		response.arg = bcs->channel;
		response.parm.length = len;
133
		iif->statcallb(&response);
134 135 136 137
	}
}
EXPORT_SYMBOL_GPL(gigaset_skb_sent);

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
/**
 * gigaset_skb_rcvd() - pass received skb to LL
 * @bcs:	B channel descriptor structure.
 * @skb:	received data.
 *
 * Called by hardware module {bas,ser,usb}_gigaset when user data has
 * been successfully received, for passing to the LL.
 * Warning: skb must not be accessed anymore!
 */
void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
{
	isdn_if *iif = bcs->cs->iif;

	iif->rcvcallb_skb(bcs->cs->myid, bcs->channel, skb);
	bcs->trans_down++;
}
EXPORT_SYMBOL_GPL(gigaset_skb_rcvd);

/**
 * gigaset_isdn_rcv_err() - signal receive error
 * @bcs:	B channel descriptor structure.
 *
 * Called by hardware module {bas,ser,usb}_gigaset when a receive error
 * has occurred, for signalling to the LL.
 */
void gigaset_isdn_rcv_err(struct bc_state *bcs)
{
	isdn_if *iif = bcs->cs->iif;
	isdn_ctrl response;

	/* if currently ignoring packets, just count down */
	if (bcs->ignore) {
		bcs->ignore--;
		return;
	}

	/* update statistics */
	bcs->corrupted++;

	/* error -> LL */
	gig_dbg(DEBUG_CMD, "sending L1ERR");
	response.driver = bcs->cs->myid;
	response.command = ISDN_STAT_L1ERR;
	response.arg = bcs->channel;
	response.parm.errcode = ISDN_STAT_L1ERR_RECV;
	iif->statcallb(&response);
}
EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);

187 188 189 190 191 192
/* This function will be called by LL to send commands
 * NOTE: LL ignores the returned value, for commands other than ISDN_CMD_IOCTL,
 * so don't put too much effort into it.
 */
static int command_from_LL(isdn_ctrl *cntrl)
{
193
	struct cardstate *cs;
194 195
	struct bc_state *bcs;
	int retval = 0;
196 197 198 199
	char **commands;
	int ch;
	int i;
	size_t l;
200 201 202

	gigaset_debugdrivers();

203 204 205 206 207
	gig_dbg(DEBUG_CMD, "driver: %d, command: %d, arg: 0x%lx",
		cntrl->driver, cntrl->command, cntrl->arg);

	cs = gigaset_get_cs_by_id(cntrl->driver);
	if (cs == NULL) {
208
		pr_err("%s: invalid driver ID (%d)\n", __func__, cntrl->driver);
209 210
		return -ENODEV;
	}
211
	ch = cntrl->arg & 0xff;
212 213 214

	switch (cntrl->command) {
	case ISDN_CMD_IOCTL:
215
		dev_warn(cs->dev, "ISDN_CMD_IOCTL not supported\n");
216 217 218
		return -EINVAL;

	case ISDN_CMD_DIAL:
219
		gig_dbg(DEBUG_ANY,
220
			"ISDN_CMD_DIAL (phone: %s, msn: %s, si1: %d, si2: %d)",
221 222
			cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn,
			cntrl->parm.setup.si1, cntrl->parm.setup.si2);
223

224
		if (ch >= cs->channels) {
225
			dev_err(cs->dev,
226
				"ISDN_CMD_DIAL: invalid channel (%d)\n", ch);
227 228
			return -EINVAL;
		}
229
		bcs = cs->bcs + ch;
230
		if (!gigaset_get_channel(bcs)) {
231
			dev_err(cs->dev, "ISDN_CMD_DIAL: channel not free\n");
232 233 234
			return -EBUSY;
		}

235 236
		commands = kzalloc(AT_NUM*(sizeof *commands), GFP_ATOMIC);
		if (!commands) {
237
			gigaset_free_channel(bcs);
238
			dev_err(cs->dev, "ISDN_CMD_DIAL: out of memory\n");
239 240 241
			return -ENOMEM;
		}

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
		l = 3 + strlen(cntrl->parm.setup.phone);
		commands[AT_DIAL] = kmalloc(l, GFP_ATOMIC);
		if (!commands[AT_DIAL])
			goto oom;
		if (cntrl->parm.setup.phone[0] == '*' &&
		    cntrl->parm.setup.phone[1] == '*') {
			/* internal call: translate ** prefix to CTP value */
			commands[AT_TYPE] = kstrdup("^SCTP=0\r", GFP_ATOMIC);
			if (!commands[AT_TYPE])
				goto oom;
			snprintf(commands[AT_DIAL], l,
				 "D%s\r", cntrl->parm.setup.phone+2);
		} else {
			commands[AT_TYPE] = kstrdup("^SCTP=1\r", GFP_ATOMIC);
			if (!commands[AT_TYPE])
				goto oom;
			snprintf(commands[AT_DIAL], l,
				 "D%s\r", cntrl->parm.setup.phone);
		}

		l = strlen(cntrl->parm.setup.eazmsn);
		if (l) {
			l += 8;
			commands[AT_MSN] = kmalloc(l, GFP_ATOMIC);
			if (!commands[AT_MSN])
				goto oom;
			snprintf(commands[AT_MSN], l, "^SMSN=%s\r",
				 cntrl->parm.setup.eazmsn);
		}

		switch (cntrl->parm.setup.si1) {
		case 1:		/* audio */
			/* BC = 9090A3: 3.1 kHz audio, A-law */
			commands[AT_BC] = kstrdup("^SBC=9090A3\r", GFP_ATOMIC);
			if (!commands[AT_BC])
				goto oom;
			break;
		case 7:		/* data */
		default:	/* hope the app knows what it is doing */
			/* BC = 8890: unrestricted digital information */
			commands[AT_BC] = kstrdup("^SBC=8890\r", GFP_ATOMIC);
			if (!commands[AT_BC])
				goto oom;
		}
		/* ToDo: other si1 values, inspect si2, set HLC/LLC */

		commands[AT_PROTO] = kmalloc(9, GFP_ATOMIC);
		if (!commands[AT_PROTO])
			goto oom;
		snprintf(commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2);

		commands[AT_ISO] = kmalloc(9, GFP_ATOMIC);
		if (!commands[AT_ISO])
			goto oom;
		snprintf(commands[AT_ISO], 9, "^SISO=%u\r",
			 (unsigned) bcs->channel + 1);

		if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, commands,
T
Tilman Schmidt 已提交
300
				       bcs->at_state.seq_index, NULL)) {
301 302 303
			for (i = 0; i < AT_NUM; ++i)
				kfree(commands[i]);
			kfree(commands);
304 305 306 307
			gigaset_free_channel(bcs);
			return -ENOMEM;
		}

308
		gig_dbg(DEBUG_CMD, "scheduling DIAL");
309 310
		gigaset_schedule_event(cs);
		break;
311 312
	case ISDN_CMD_ACCEPTD:
		if (ch >= cs->channels) {
313
			dev_err(cs->dev,
314
				"ISDN_CMD_ACCEPTD: invalid channel (%d)\n", ch);
315 316
			return -EINVAL;
		}
317 318 319
		bcs = cs->bcs + ch;
		if (!gigaset_add_event(cs, &bcs->at_state,
				       EV_ACCEPT, NULL, 0, NULL))
320 321
			return -ENOMEM;

322
		gig_dbg(DEBUG_CMD, "scheduling ACCEPT");
323 324 325 326 327 328
		gigaset_schedule_event(cs);

		break;
	case ISDN_CMD_ACCEPTB:
		break;
	case ISDN_CMD_HANGUP:
329
		if (ch >= cs->channels) {
330
			dev_err(cs->dev,
331
				"ISDN_CMD_HANGUP: invalid channel (%d)\n", ch);
332 333
			return -EINVAL;
		}
334 335 336
		bcs = cs->bcs + ch;
		if (!gigaset_add_event(cs, &bcs->at_state,
				       EV_HUP, NULL, 0, NULL))
337 338
			return -ENOMEM;

339
		gig_dbg(DEBUG_CMD, "scheduling HUP");
340 341 342
		gigaset_schedule_event(cs);

		break;
343 344
	case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */
		dev_info(cs->dev, "ignoring ISDN_CMD_CLREAZ\n");
345
		break;
346 347 348
	case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */
		dev_info(cs->dev, "ignoring ISDN_CMD_SETEAZ (%s)\n",
			 cntrl->parm.num);
349
		break;
350
	case ISDN_CMD_SETL2: /* Set L2 to given protocol */
351
		if (ch >= cs->channels) {
352
			dev_err(cs->dev,
353
				"ISDN_CMD_SETL2: invalid channel (%d)\n", ch);
354 355
			return -EINVAL;
		}
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
		bcs = cs->bcs + ch;
		if (bcs->chstate & CHS_D_UP) {
			dev_err(cs->dev,
				"ISDN_CMD_SETL2: channel active (%d)\n", ch);
			return -EINVAL;
		}
		switch (cntrl->arg >> 8) {
		case ISDN_PROTO_L2_HDLC:
			gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_HDLC");
			bcs->proto2 = L2_HDLC;
			break;
		case ISDN_PROTO_L2_TRANS:
			gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_VOICE");
			bcs->proto2 = L2_VOICE;
			break;
		default:
			dev_err(cs->dev,
				"ISDN_CMD_SETL2: unsupported protocol (%lu)\n",
				cntrl->arg >> 8);
			return -EINVAL;
376 377
		}
		break;
378
	case ISDN_CMD_SETL3: /* Set L3 to given protocol */
379
		if (ch >= cs->channels) {
380
			dev_err(cs->dev,
381
				"ISDN_CMD_SETL3: invalid channel (%d)\n", ch);
382 383 384 385
			return -EINVAL;
		}

		if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) {
386
			dev_err(cs->dev,
387
				"ISDN_CMD_SETL3: unsupported protocol (%lu)\n",
388
				cntrl->arg >> 8);
389 390 391 392 393
			return -EINVAL;
		}

		break;
	case ISDN_CMD_PROCEED:
394
		gig_dbg(DEBUG_ANY, "ISDN_CMD_PROCEED"); //FIXME
395 396
		break;
	case ISDN_CMD_ALERT:
397
		gig_dbg(DEBUG_ANY, "ISDN_CMD_ALERT"); //FIXME
398
		if (cntrl->arg >= cs->channels) {
399 400 401
			dev_err(cs->dev,
				"ISDN_CMD_ALERT: invalid channel (%d)\n",
				(int) cntrl->arg);
402 403 404 405 406 407 408
			return -EINVAL;
		}
		//bcs = cs->bcs + cntrl->arg;
		//bcs->proto2 = -1;
		// FIXME
		break;
	case ISDN_CMD_REDIR:
409
		gig_dbg(DEBUG_ANY, "ISDN_CMD_REDIR"); //FIXME
410 411
		break;
	case ISDN_CMD_PROT_IO:
412
		gig_dbg(DEBUG_ANY, "ISDN_CMD_PROT_IO");
413 414
		break;
	case ISDN_CMD_FAXCMD:
415
		gig_dbg(DEBUG_ANY, "ISDN_CMD_FAXCMD");
416 417
		break;
	case ISDN_CMD_GETL2:
418
		gig_dbg(DEBUG_ANY, "ISDN_CMD_GETL2");
419 420
		break;
	case ISDN_CMD_GETL3:
421
		gig_dbg(DEBUG_ANY, "ISDN_CMD_GETL3");
422 423
		break;
	case ISDN_CMD_GETEAZ:
424
		gig_dbg(DEBUG_ANY, "ISDN_CMD_GETEAZ");
425 426
		break;
	case ISDN_CMD_SETSIL:
427
		gig_dbg(DEBUG_ANY, "ISDN_CMD_SETSIL");
428 429
		break;
	case ISDN_CMD_GETSIL:
430
		gig_dbg(DEBUG_ANY, "ISDN_CMD_GETSIL");
431 432
		break;
	default:
433 434
		dev_err(cs->dev, "unknown command %d from LL\n",
			cntrl->command);
435 436 437 438
		return -EINVAL;
	}

	return retval;
439 440 441 442 443 444

oom:
	dev_err(bcs->cs->dev, "out of memory\n");
	for (i = 0; i < AT_NUM; ++i)
		kfree(commands[i]);
	return -ENOMEM;
445 446
}

447
static void gigaset_i4l_cmd(struct cardstate *cs, int cmd)
448
{
449
	isdn_if *iif = cs->iif;
450 451 452 453 454
	isdn_ctrl command;

	command.driver = cs->myid;
	command.command = cmd;
	command.arg = 0;
455
	iif->statcallb(&command);
456 457
}

458
static void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd)
459
{
460
	isdn_if *iif = bcs->cs->iif;
461 462 463 464 465
	isdn_ctrl command;

	command.driver = bcs->cs->myid;
	command.command = cmd;
	command.arg = bcs->channel;
466
	iif->statcallb(&command);
467 468
}

T
Tilman Schmidt 已提交
469 470 471 472 473 474 475 476 477
/**
 * gigaset_isdn_icall() - signal incoming call
 * @at_state:	connection state structure.
 *
 * Called by main module to notify the LL that an incoming call has been
 * received. @at_state contains the parameters of the call.
 *
 * Return value: call disposition (ICALL_*)
 */
478 479 480 481
int gigaset_isdn_icall(struct at_state_t *at_state)
{
	struct cardstate *cs = at_state->cs;
	struct bc_state *bcs = at_state->bcs;
482
	isdn_if *iif = cs->iif;
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
	isdn_ctrl response;
	int retval;

	/* fill ICALL structure */
	response.parm.setup.si1 = 0;	/* default: unknown */
	response.parm.setup.si2 = 0;
	response.parm.setup.screen = 0;	//FIXME how to set these?
	response.parm.setup.plan = 0;
	if (!at_state->str_var[STR_ZBC]) {
		/* no BC (internal call): assume speech, A-law */
		response.parm.setup.si1 = 1;
	} else if (!strcmp(at_state->str_var[STR_ZBC], "8890")) {
		/* unrestricted digital information */
		response.parm.setup.si1 = 7;
	} else if (!strcmp(at_state->str_var[STR_ZBC], "8090A3")) {
		/* speech, A-law */
		response.parm.setup.si1 = 1;
	} else if (!strcmp(at_state->str_var[STR_ZBC], "9090A3")) {
		/* 3,1 kHz audio, A-law */
		response.parm.setup.si1 = 1;
		response.parm.setup.si2 = 2;
	} else {
505
		dev_warn(cs->dev, "RING ignored - unsupported BC %s\n",
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
		     at_state->str_var[STR_ZBC]);
		return ICALL_IGNORE;
	}
	if (at_state->str_var[STR_NMBR]) {
		strncpy(response.parm.setup.phone, at_state->str_var[STR_NMBR],
			sizeof response.parm.setup.phone - 1);
		response.parm.setup.phone[sizeof response.parm.setup.phone - 1] = 0;
	} else
		response.parm.setup.phone[0] = 0;
	if (at_state->str_var[STR_ZCPN]) {
		strncpy(response.parm.setup.eazmsn, at_state->str_var[STR_ZCPN],
			sizeof response.parm.setup.eazmsn - 1);
		response.parm.setup.eazmsn[sizeof response.parm.setup.eazmsn - 1] = 0;
	} else
		response.parm.setup.eazmsn[0] = 0;

	if (!bcs) {
523
		dev_notice(cs->dev, "no channel for incoming call\n");
524 525 526
		response.command = ISDN_STAT_ICALLW;
		response.arg = 0; //FIXME
	} else {
527
		gig_dbg(DEBUG_CMD, "Sending ICALL");
528 529 530 531
		response.command = ISDN_STAT_ICALL;
		response.arg = bcs->channel; //FIXME
	}
	response.driver = cs->myid;
532
	retval = iif->statcallb(&response);
533
	gig_dbg(DEBUG_CMD, "Response: %d", retval);
534 535 536 537 538 539 540 541 542
	switch (retval) {
	case 0:	/* no takers */
		return ICALL_IGNORE;
	case 1:	/* alerting */
		bcs->chstate |= CHS_NOTIFY_LL;
		return ICALL_ACCEPT;
	case 2:	/* reject */
		return ICALL_REJECT;
	case 3:	/* incomplete */
543 544
		dev_warn(cs->dev,
		       "LL requested unsupported feature: Incomplete Number\n");
545 546 547 548 549 550 551
		return ICALL_IGNORE;
	case 4:	/* proceeding */
		/* Gigaset will send ALERTING anyway.
		 * There doesn't seem to be a way to avoid this.
		 */
		return ICALL_ACCEPT;
	case 5:	/* deflect */
552 553
		dev_warn(cs->dev,
			 "LL requested unsupported feature: Call Deflection\n");
554 555
		return ICALL_IGNORE;
	default:
556
		dev_err(cs->dev, "LL error %d on ICALL\n", retval);
557 558 559 560
		return ICALL_IGNORE;
	}
}

561 562 563 564 565 566 567 568
/**
 * gigaset_isdn_connD() - signal D channel connect
 * @bcs:	B channel descriptor structure.
 *
 * Called by main module to notify the LL that the D channel connection has
 * been established.
 */
void gigaset_isdn_connD(struct bc_state *bcs)
569
{
570 571 572
	gig_dbg(DEBUG_CMD, "sending DCONN");
	gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN);
}
573

574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
/**
 * gigaset_isdn_hupD() - signal D channel hangup
 * @bcs:	B channel descriptor structure.
 *
 * Called by main module to notify the LL that the D channel connection has
 * been shut down.
 */
void gigaset_isdn_hupD(struct bc_state *bcs)
{
	gig_dbg(DEBUG_CMD, "sending DHUP");
	gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP);
}

/**
 * gigaset_isdn_connB() - signal B channel connect
 * @bcs:	B channel descriptor structure.
 *
 * Called by main module to notify the LL that the B channel connection has
 * been established.
 */
void gigaset_isdn_connB(struct bc_state *bcs)
{
	gig_dbg(DEBUG_CMD, "sending BCONN");
	gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BCONN);
}

/**
 * gigaset_isdn_hupB() - signal B channel hangup
 * @bcs:	B channel descriptor structure.
 *
 * Called by main module to notify the LL that the B channel connection has
 * been shut down.
 */
void gigaset_isdn_hupB(struct bc_state *bcs)
{
	gig_dbg(DEBUG_CMD, "sending BHUP");
	gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BHUP);
}

/**
 * gigaset_isdn_start() - signal device availability
 * @cs:		device descriptor structure.
 *
 * Called by main module to notify the LL that the device is available for
 * use.
 */
void gigaset_isdn_start(struct cardstate *cs)
{
	gig_dbg(DEBUG_CMD, "sending RUN");
	gigaset_i4l_cmd(cs, ISDN_STAT_RUN);
}

/**
 * gigaset_isdn_stop() - signal device unavailability
 * @cs:		device descriptor structure.
 *
 * Called by main module to notify the LL that the device is no longer
 * available for use.
 */
void gigaset_isdn_stop(struct cardstate *cs)
{
	gig_dbg(DEBUG_CMD, "sending STOP");
	gigaset_i4l_cmd(cs, ISDN_STAT_STOP);
}

/**
 * gigaset_isdn_register() - register to LL
 * @cs:		device descriptor structure.
 * @isdnid:	device name.
 *
 * Called by main module to register the device with the LL.
 *
 * Return value: 1 for success, 0 for failure
 */
int gigaset_isdn_register(struct cardstate *cs, const char *isdnid)
{
	isdn_if *iif;

	pr_info("ISDN4Linux interface\n");

	iif = kmalloc(sizeof *iif, GFP_KERNEL);
	if (!iif) {
		pr_err("out of memory\n");
		return 0;
	}
659 660

	if (snprintf(iif->id, sizeof iif->id, "%s_%u", isdnid, cs->minor_index)
T
Tilman Schmidt 已提交
661 662
	    >= sizeof iif->id) {
		pr_err("ID too long: %s\n", isdnid);
663
		kfree(iif);
T
Tilman Schmidt 已提交
664 665
		return 0;
	}
666 667

	iif->owner = THIS_MODULE;
668
	iif->channels = cs->channels;
669
	iif->maxbufsize = MAX_BUF_SIZE;
670
	iif->features = ISDN_FEATURE_L2_TRANS |
671 672 673 674 675 676
		ISDN_FEATURE_L2_HDLC |
#ifdef GIG_X75
		ISDN_FEATURE_L2_X75I |
#endif
		ISDN_FEATURE_L3_TRANS |
		ISDN_FEATURE_P_EURO;
677
	iif->hl_hdrlen = HW_HDR_LEN;		/* Area for storing ack */
678 679
	iif->command = command_from_LL;
	iif->writebuf_skb = writebuf_from_LL;
680 681 682 683
	iif->writecmd = NULL;			/* Don't support isdnctrl */
	iif->readstat = NULL;			/* Don't support isdnctrl */
	iif->rcvcallb_skb = NULL;		/* Will be set by LL */
	iif->statcallb = NULL;			/* Will be set by LL */
684

T
Tilman Schmidt 已提交
685 686
	if (!register_isdn(iif)) {
		pr_err("register_isdn failed\n");
687
		kfree(iif);
688
		return 0;
T
Tilman Schmidt 已提交
689
	}
690

691
	cs->iif = iif;
692
	cs->myid = iif->channels;		/* Set my device id */
693
	cs->hw_hdr_len = HW_HDR_LEN;
694 695
	return 1;
}
696 697 698 699 700 701 702 703 704 705 706 707 708 709

/**
 * gigaset_isdn_unregister() - unregister from LL
 * @cs:		device descriptor structure.
 *
 * Called by main module to unregister the device from the LL.
 */
void gigaset_isdn_unregister(struct cardstate *cs)
{
	gig_dbg(DEBUG_CMD, "sending UNLOAD");
	gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD);
	kfree(cs->iif);
	cs->iif = NULL;
}