i4l.c 18.1 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
#include <linux/isdnif.h>

19 20
#define SBUFSIZE	4096	/* sk_buff payload size */
#define TRANSBUFSIZE	768	/* bytes per skb for transparent receive */
21
#define HW_HDR_LEN	2	/* Header size used to store ack info */
22
#define MAX_BUF_SIZE	(SBUFSIZE - HW_HDR_LEN)	/* max data packet from LL */
23

24
/* == Handling of I4L IO =====================================================*/
25 26 27 28 29 30 31 32 33

/* 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
34 35
 *	ack		if != 0 LL wants to be notified on completion via
 *			statcallb(ISDN_STAT_BSENT)
36 37 38 39 40 41
 *	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)
 */
42 43
static int writebuf_from_LL(int driverID, int channel, int ack,
			    struct sk_buff *skb)
44
{
T
Tilman Schmidt 已提交
45
	struct cardstate *cs = gigaset_get_cs_by_id(driverID);
46
	struct bc_state *bcs;
47
	unsigned char *ack_header;
48 49
	unsigned len;

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

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

68 69 70 71
	gig_dbg(DEBUG_LLDATA,
		"Receiving data from LL (id: %d, ch: %d, ack: %d, sz: %d)",
		driverID, channel, ack, len);

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

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
	/* 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]);
101 102

	/* pass to device-specific module */
103
	return cs->ops->send_skb(bcs, skb);
104 105
}

T
Tilman Schmidt 已提交
106 107 108 109 110 111 112 113
/**
 * 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.
 */
114 115
void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
{
116
	isdn_if *iif = bcs->cs->iif;
117
	unsigned char *ack_header = skb_mac_header(skb);
118 119 120 121 122 123
	unsigned len;
	isdn_ctrl response;

	++bcs->trans_up;

	if (skb->len)
124 125
		dev_warn(bcs->cs->dev, "%s: skb->len==%d\n",
			 __func__, skb->len);
126

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

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

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 187 188 189
/**
 * 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);

190 191 192 193 194 195
/* 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)
{
196
	struct cardstate *cs;
197 198
	struct bc_state *bcs;
	int retval = 0;
199 200 201 202
	char **commands;
	int ch;
	int i;
	size_t l;
203 204 205

	gigaset_debugdrivers();

206 207 208 209 210
	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) {
211
		pr_err("%s: invalid driver ID (%d)\n", __func__, cntrl->driver);
212 213
		return -ENODEV;
	}
214
	ch = cntrl->arg & 0xff;
215 216 217

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

	case ISDN_CMD_DIAL:
T
Tilman Schmidt 已提交
222
		gig_dbg(DEBUG_CMD,
223
			"ISDN_CMD_DIAL (phone: %s, msn: %s, si1: %d, si2: %d)",
224 225
			cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn,
			cntrl->parm.setup.si1, cntrl->parm.setup.si2);
226

227
		if (ch >= cs->channels) {
228
			dev_err(cs->dev,
229
				"ISDN_CMD_DIAL: invalid channel (%d)\n", ch);
230 231
			return -EINVAL;
		}
232
		bcs = cs->bcs + ch;
233
		if (!gigaset_get_channel(bcs)) {
234
			dev_err(cs->dev, "ISDN_CMD_DIAL: channel not free\n");
235 236
			return -EBUSY;
		}
237 238 239 240 241 242 243 244 245
		switch (bcs->proto2) {
		case L2_HDLC:
			bcs->rx_bufsize = SBUFSIZE;
			break;
		default:			/* assume transparent */
			bcs->rx_bufsize = TRANSBUFSIZE;
		}
		dev_kfree_skb(bcs->rx_skb);
		gigaset_new_rx_skb(bcs);
246

247 248
		commands = kzalloc(AT_NUM*(sizeof *commands), GFP_ATOMIC);
		if (!commands) {
249
			gigaset_free_channel(bcs);
250
			dev_err(cs->dev, "ISDN_CMD_DIAL: out of memory\n");
251 252 253
			return -ENOMEM;
		}

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 300 301 302 303 304 305 306 307 308 309 310 311
		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 已提交
312
				       bcs->at_state.seq_index, NULL)) {
313 314 315
			for (i = 0; i < AT_NUM; ++i)
				kfree(commands[i]);
			kfree(commands);
316 317 318 319 320
			gigaset_free_channel(bcs);
			return -ENOMEM;
		}
		gigaset_schedule_event(cs);
		break;
321
	case ISDN_CMD_ACCEPTD:
T
Tilman Schmidt 已提交
322
		gig_dbg(DEBUG_CMD, "ISDN_CMD_ACCEPTD");
323
		if (ch >= cs->channels) {
324
			dev_err(cs->dev,
325
				"ISDN_CMD_ACCEPTD: invalid channel (%d)\n", ch);
326 327
			return -EINVAL;
		}
328
		bcs = cs->bcs + ch;
329 330 331 332 333 334 335 336 337
		switch (bcs->proto2) {
		case L2_HDLC:
			bcs->rx_bufsize = SBUFSIZE;
			break;
		default:			/* assume transparent */
			bcs->rx_bufsize = TRANSBUFSIZE;
		}
		dev_kfree_skb(bcs->rx_skb);
		gigaset_new_rx_skb(bcs);
338 339
		if (!gigaset_add_event(cs, &bcs->at_state,
				       EV_ACCEPT, NULL, 0, NULL))
340 341 342 343 344
			return -ENOMEM;
		gigaset_schedule_event(cs);

		break;
	case ISDN_CMD_HANGUP:
T
Tilman Schmidt 已提交
345
		gig_dbg(DEBUG_CMD, "ISDN_CMD_HANGUP");
346
		if (ch >= cs->channels) {
347
			dev_err(cs->dev,
348
				"ISDN_CMD_HANGUP: invalid channel (%d)\n", ch);
349 350
			return -EINVAL;
		}
351 352 353
		bcs = cs->bcs + ch;
		if (!gigaset_add_event(cs, &bcs->at_state,
				       EV_HUP, NULL, 0, NULL))
354 355 356 357
			return -ENOMEM;
		gigaset_schedule_event(cs);

		break;
358 359
	case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */
		dev_info(cs->dev, "ignoring ISDN_CMD_CLREAZ\n");
360
		break;
361 362 363
	case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */
		dev_info(cs->dev, "ignoring ISDN_CMD_SETEAZ (%s)\n",
			 cntrl->parm.num);
364
		break;
365
	case ISDN_CMD_SETL2: /* Set L2 to given protocol */
366
		if (ch >= cs->channels) {
367
			dev_err(cs->dev,
368
				"ISDN_CMD_SETL2: invalid channel (%d)\n", ch);
369 370
			return -EINVAL;
		}
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
		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;
391 392
		}
		break;
393
	case ISDN_CMD_SETL3: /* Set L3 to given protocol */
T
Tilman Schmidt 已提交
394
		gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL3");
395
		if (ch >= cs->channels) {
396
			dev_err(cs->dev,
397
				"ISDN_CMD_SETL3: invalid channel (%d)\n", ch);
398 399 400 401
			return -EINVAL;
		}

		if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) {
402
			dev_err(cs->dev,
403
				"ISDN_CMD_SETL3: unsupported protocol (%lu)\n",
404
				cntrl->arg >> 8);
405 406 407 408
			return -EINVAL;
		}

		break;
T
Tilman Schmidt 已提交
409

410
	default:
T
Tilman Schmidt 已提交
411
		gig_dbg(DEBUG_CMD, "unknown command %d from LL",
412
			cntrl->command);
413 414 415 416
		return -EINVAL;
	}

	return retval;
417 418 419 420 421 422

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

425
static void gigaset_i4l_cmd(struct cardstate *cs, int cmd)
426
{
427
	isdn_if *iif = cs->iif;
428 429 430 431 432
	isdn_ctrl command;

	command.driver = cs->myid;
	command.command = cmd;
	command.arg = 0;
433
	iif->statcallb(&command);
434 435
}

436
static void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd)
437
{
438
	isdn_if *iif = bcs->cs->iif;
439 440 441 442 443
	isdn_ctrl command;

	command.driver = bcs->cs->myid;
	command.command = cmd;
	command.arg = bcs->channel;
444
	iif->statcallb(&command);
445 446
}

T
Tilman Schmidt 已提交
447 448 449 450 451 452 453 454 455
/**
 * 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_*)
 */
456 457 458 459
int gigaset_isdn_icall(struct at_state_t *at_state)
{
	struct cardstate *cs = at_state->cs;
	struct bc_state *bcs = at_state->bcs;
460
	isdn_if *iif = cs->iif;
461 462 463 464 465 466
	isdn_ctrl response;
	int retval;

	/* fill ICALL structure */
	response.parm.setup.si1 = 0;	/* default: unknown */
	response.parm.setup.si2 = 0;
T
Tilman Schmidt 已提交
467
	response.parm.setup.screen = 0;
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
	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 {
483
		dev_warn(cs->dev, "RING ignored - unsupported BC %s\n",
484 485 486 487
		     at_state->str_var[STR_ZBC]);
		return ICALL_IGNORE;
	}
	if (at_state->str_var[STR_NMBR]) {
T
Tilman Schmidt 已提交
488 489
		strlcpy(response.parm.setup.phone, at_state->str_var[STR_NMBR],
			sizeof response.parm.setup.phone);
490 491 492
	} else
		response.parm.setup.phone[0] = 0;
	if (at_state->str_var[STR_ZCPN]) {
T
Tilman Schmidt 已提交
493 494
		strlcpy(response.parm.setup.eazmsn, at_state->str_var[STR_ZCPN],
			sizeof response.parm.setup.eazmsn);
495 496 497 498
	} else
		response.parm.setup.eazmsn[0] = 0;

	if (!bcs) {
499
		dev_notice(cs->dev, "no channel for incoming call\n");
500
		response.command = ISDN_STAT_ICALLW;
T
Tilman Schmidt 已提交
501
		response.arg = 0;
502
	} else {
503
		gig_dbg(DEBUG_CMD, "Sending ICALL");
504
		response.command = ISDN_STAT_ICALL;
T
Tilman Schmidt 已提交
505
		response.arg = bcs->channel;
506 507
	}
	response.driver = cs->myid;
508
	retval = iif->statcallb(&response);
509
	gig_dbg(DEBUG_CMD, "Response: %d", retval);
510 511 512 513 514 515 516 517 518
	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 */
519 520
		dev_warn(cs->dev,
		       "LL requested unsupported feature: Incomplete Number\n");
521 522 523 524 525 526 527
		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 */
528 529
		dev_warn(cs->dev,
			 "LL requested unsupported feature: Call Deflection\n");
530 531
		return ICALL_IGNORE;
	default:
532
		dev_err(cs->dev, "LL error %d on ICALL\n", retval);
533 534 535 536
		return ICALL_IGNORE;
	}
}

537 538 539 540 541 542 543 544
/**
 * 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)
545
{
546 547 548
	gig_dbg(DEBUG_CMD, "sending DCONN");
	gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN);
}
549

550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 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
/**
 * 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);
}

/**
616
 * gigaset_isdn_regdev() - register to LL
617 618 619 620 621
 * @cs:		device descriptor structure.
 * @isdnid:	device name.
 *
 * Return value: 1 for success, 0 for failure
 */
622
int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid)
623 624 625 626 627 628 629 630 631 632
{
	isdn_if *iif;

	pr_info("ISDN4Linux interface\n");

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

	if (snprintf(iif->id, sizeof iif->id, "%s_%u", isdnid, cs->minor_index)
T
Tilman Schmidt 已提交
635 636
	    >= sizeof iif->id) {
		pr_err("ID too long: %s\n", isdnid);
637
		kfree(iif);
T
Tilman Schmidt 已提交
638 639
		return 0;
	}
640 641

	iif->owner = THIS_MODULE;
642
	iif->channels = cs->channels;
643
	iif->maxbufsize = MAX_BUF_SIZE;
644
	iif->features = ISDN_FEATURE_L2_TRANS |
645 646 647 648 649 650
		ISDN_FEATURE_L2_HDLC |
#ifdef GIG_X75
		ISDN_FEATURE_L2_X75I |
#endif
		ISDN_FEATURE_L3_TRANS |
		ISDN_FEATURE_P_EURO;
651
	iif->hl_hdrlen = HW_HDR_LEN;		/* Area for storing ack */
652 653
	iif->command = command_from_LL;
	iif->writebuf_skb = writebuf_from_LL;
654 655 656 657
	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 */
658

T
Tilman Schmidt 已提交
659 660
	if (!register_isdn(iif)) {
		pr_err("register_isdn failed\n");
661
		kfree(iif);
662
		return 0;
T
Tilman Schmidt 已提交
663
	}
664

665
	cs->iif = iif;
666
	cs->myid = iif->channels;		/* Set my device id */
667
	cs->hw_hdr_len = HW_HDR_LEN;
668 669
	return 1;
}
670 671

/**
672
 * gigaset_isdn_unregdev() - unregister device from LL
673 674
 * @cs:		device descriptor structure.
 */
675
void gigaset_isdn_unregdev(struct cardstate *cs)
676 677 678 679 680 681
{
	gig_dbg(DEBUG_CMD, "sending UNLOAD");
	gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD);
	kfree(cs->iif);
	cs->iif = NULL;
}
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697

/**
 * gigaset_isdn_regdrv() - register driver to LL
 */
void gigaset_isdn_regdrv(void)
{
	/* nothing to do */
}

/**
 * gigaset_isdn_unregdrv() - unregister driver from LL
 */
void gigaset_isdn_unregdrv(void)
{
	/* nothing to do */
}