i4l.c 17.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
{
T
Tilman Schmidt 已提交
42
	struct cardstate *cs = gigaset_get_cs_by_id(driverID);
43
	struct bc_state *bcs;
44
	unsigned char *ack_header;
45 46
	unsigned len;

T
Tilman Schmidt 已提交
47
	if (!cs) {
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:
T
Tilman Schmidt 已提交
219
		gig_dbg(DEBUG_CMD,
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 308
			gigaset_free_channel(bcs);
			return -ENOMEM;
		}
		gigaset_schedule_event(cs);
		break;
309
	case ISDN_CMD_ACCEPTD:
T
Tilman Schmidt 已提交
310
		gig_dbg(DEBUG_CMD, "ISDN_CMD_ACCEPTD");
311
		if (ch >= cs->channels) {
312
			dev_err(cs->dev,
313
				"ISDN_CMD_ACCEPTD: invalid channel (%d)\n", ch);
314 315
			return -EINVAL;
		}
316 317 318
		bcs = cs->bcs + ch;
		if (!gigaset_add_event(cs, &bcs->at_state,
				       EV_ACCEPT, NULL, 0, NULL))
319 320 321 322 323
			return -ENOMEM;
		gigaset_schedule_event(cs);

		break;
	case ISDN_CMD_HANGUP:
T
Tilman Schmidt 已提交
324
		gig_dbg(DEBUG_CMD, "ISDN_CMD_HANGUP");
325
		if (ch >= cs->channels) {
326
			dev_err(cs->dev,
327
				"ISDN_CMD_HANGUP: invalid channel (%d)\n", ch);
328 329
			return -EINVAL;
		}
330 331 332
		bcs = cs->bcs + ch;
		if (!gigaset_add_event(cs, &bcs->at_state,
				       EV_HUP, NULL, 0, NULL))
333 334 335 336
			return -ENOMEM;
		gigaset_schedule_event(cs);

		break;
337 338
	case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */
		dev_info(cs->dev, "ignoring ISDN_CMD_CLREAZ\n");
339
		break;
340 341 342
	case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */
		dev_info(cs->dev, "ignoring ISDN_CMD_SETEAZ (%s)\n",
			 cntrl->parm.num);
343
		break;
344
	case ISDN_CMD_SETL2: /* Set L2 to given protocol */
345
		if (ch >= cs->channels) {
346
			dev_err(cs->dev,
347
				"ISDN_CMD_SETL2: invalid channel (%d)\n", ch);
348 349
			return -EINVAL;
		}
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
		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;
370 371
		}
		break;
372
	case ISDN_CMD_SETL3: /* Set L3 to given protocol */
T
Tilman Schmidt 已提交
373
		gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL3");
374
		if (ch >= cs->channels) {
375
			dev_err(cs->dev,
376
				"ISDN_CMD_SETL3: invalid channel (%d)\n", ch);
377 378 379 380
			return -EINVAL;
		}

		if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) {
381
			dev_err(cs->dev,
382
				"ISDN_CMD_SETL3: unsupported protocol (%lu)\n",
383
				cntrl->arg >> 8);
384 385 386 387
			return -EINVAL;
		}

		break;
T
Tilman Schmidt 已提交
388

389
	default:
T
Tilman Schmidt 已提交
390
		gig_dbg(DEBUG_CMD, "unknown command %d from LL",
391
			cntrl->command);
392 393 394 395
		return -EINVAL;
	}

	return retval;
396 397 398 399 400 401

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

404
static void gigaset_i4l_cmd(struct cardstate *cs, int cmd)
405
{
406
	isdn_if *iif = cs->iif;
407 408 409 410 411
	isdn_ctrl command;

	command.driver = cs->myid;
	command.command = cmd;
	command.arg = 0;
412
	iif->statcallb(&command);
413 414
}

415
static void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd)
416
{
417
	isdn_if *iif = bcs->cs->iif;
418 419 420 421 422
	isdn_ctrl command;

	command.driver = bcs->cs->myid;
	command.command = cmd;
	command.arg = bcs->channel;
423
	iif->statcallb(&command);
424 425
}

T
Tilman Schmidt 已提交
426 427 428 429 430 431 432 433 434
/**
 * 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_*)
 */
435 436 437 438
int gigaset_isdn_icall(struct at_state_t *at_state)
{
	struct cardstate *cs = at_state->cs;
	struct bc_state *bcs = at_state->bcs;
439
	isdn_if *iif = cs->iif;
440 441 442 443 444 445
	isdn_ctrl response;
	int retval;

	/* fill ICALL structure */
	response.parm.setup.si1 = 0;	/* default: unknown */
	response.parm.setup.si2 = 0;
T
Tilman Schmidt 已提交
446
	response.parm.setup.screen = 0;
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
	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 {
462
		dev_warn(cs->dev, "RING ignored - unsupported BC %s\n",
463 464 465 466
		     at_state->str_var[STR_ZBC]);
		return ICALL_IGNORE;
	}
	if (at_state->str_var[STR_NMBR]) {
T
Tilman Schmidt 已提交
467 468
		strlcpy(response.parm.setup.phone, at_state->str_var[STR_NMBR],
			sizeof response.parm.setup.phone);
469 470 471
	} else
		response.parm.setup.phone[0] = 0;
	if (at_state->str_var[STR_ZCPN]) {
T
Tilman Schmidt 已提交
472 473
		strlcpy(response.parm.setup.eazmsn, at_state->str_var[STR_ZCPN],
			sizeof response.parm.setup.eazmsn);
474 475 476 477
	} else
		response.parm.setup.eazmsn[0] = 0;

	if (!bcs) {
478
		dev_notice(cs->dev, "no channel for incoming call\n");
479
		response.command = ISDN_STAT_ICALLW;
T
Tilman Schmidt 已提交
480
		response.arg = 0;
481
	} else {
482
		gig_dbg(DEBUG_CMD, "Sending ICALL");
483
		response.command = ISDN_STAT_ICALL;
T
Tilman Schmidt 已提交
484
		response.arg = bcs->channel;
485 486
	}
	response.driver = cs->myid;
487
	retval = iif->statcallb(&response);
488
	gig_dbg(DEBUG_CMD, "Response: %d", retval);
489 490 491 492 493 494 495 496 497
	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 */
498 499
		dev_warn(cs->dev,
		       "LL requested unsupported feature: Incomplete Number\n");
500 501 502 503 504 505 506
		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 */
507 508
		dev_warn(cs->dev,
			 "LL requested unsupported feature: Call Deflection\n");
509 510
		return ICALL_IGNORE;
	default:
511
		dev_err(cs->dev, "LL error %d on ICALL\n", retval);
512 513 514 515
		return ICALL_IGNORE;
	}
}

516 517 518 519 520 521 522 523
/**
 * 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)
524
{
525 526 527
	gig_dbg(DEBUG_CMD, "sending DCONN");
	gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN);
}
528

529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 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
/**
 * 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);
}

/**
595
 * gigaset_isdn_regdev() - register to LL
596 597 598 599 600
 * @cs:		device descriptor structure.
 * @isdnid:	device name.
 *
 * Return value: 1 for success, 0 for failure
 */
601
int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid)
602 603 604 605 606 607 608 609 610 611
{
	isdn_if *iif;

	pr_info("ISDN4Linux interface\n");

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

	if (snprintf(iif->id, sizeof iif->id, "%s_%u", isdnid, cs->minor_index)
T
Tilman Schmidt 已提交
614 615
	    >= sizeof iif->id) {
		pr_err("ID too long: %s\n", isdnid);
616
		kfree(iif);
T
Tilman Schmidt 已提交
617 618
		return 0;
	}
619 620

	iif->owner = THIS_MODULE;
621
	iif->channels = cs->channels;
622
	iif->maxbufsize = MAX_BUF_SIZE;
623
	iif->features = ISDN_FEATURE_L2_TRANS |
624 625 626 627 628 629
		ISDN_FEATURE_L2_HDLC |
#ifdef GIG_X75
		ISDN_FEATURE_L2_X75I |
#endif
		ISDN_FEATURE_L3_TRANS |
		ISDN_FEATURE_P_EURO;
630
	iif->hl_hdrlen = HW_HDR_LEN;		/* Area for storing ack */
631 632
	iif->command = command_from_LL;
	iif->writebuf_skb = writebuf_from_LL;
633 634 635 636
	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 */
637

T
Tilman Schmidt 已提交
638 639
	if (!register_isdn(iif)) {
		pr_err("register_isdn failed\n");
640
		kfree(iif);
641
		return 0;
T
Tilman Schmidt 已提交
642
	}
643

644
	cs->iif = iif;
645
	cs->myid = iif->channels;		/* Set my device id */
646
	cs->hw_hdr_len = HW_HDR_LEN;
647 648
	return 1;
}
649 650

/**
651
 * gigaset_isdn_unregdev() - unregister device from LL
652 653
 * @cs:		device descriptor structure.
 */
654
void gigaset_isdn_unregdev(struct cardstate *cs)
655 656 657 658 659 660
{
	gig_dbg(DEBUG_CMD, "sending UNLOAD");
	gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD);
	kfree(cs->iif);
	cs->iif = NULL;
}
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676

/**
 * 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 */
}