Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 31cafd95 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6: (52 commits)
  phylib: Add autoload support for the LXT973 phy.
  ISDN: hysdn, fix potential NULL dereference
  vxge: fix memory leak in vxge_alloc_msix() error path
  isdn/gigaset: correct CAPI connection state storage
  isdn/gigaset: encode HLC and BC together
  isdn/gigaset: correct CAPI DATA_B3 Delivery Confirmation
  isdn/gigaset: correct CAPI voice connection encoding
  isdn/gigaset: honor CAPI application's buffer size request
  cpmac: do not leak struct net_device on phy_connect errors
  smc91c92_cs: fix the problem that lan & modem does not work simultaneously
  ipv6: fix NULL reference in proxy neighbor discovery
  Bluetooth: Bring back var 'i' increment
  xfrm: check bundle policy existance before dereferencing it
  sky2: enable rx/tx in sky2_phy_reinit()
  cnic: Disable statistics initialization for eth clients that do not support statistics
  net: add dependency on fw class module to qlcnic and netxen_nic
  snmp: fix SNMP_ADD_STATS()
  hso: remove setting of low_latency flag
  udp: Fix bogus UFO packet generation
  lasi82596: fix netdev_mc_count conversion
  ...
parents e7865c23 e2f5b045
Loading
Loading
Loading
Loading
+2 −8
Original line number Diff line number Diff line
@@ -2985,20 +2985,14 @@ F: drivers/net/ixgb/
F:	drivers/net/ixgbe/

INTEL PRO/WIRELESS 2100 NETWORK CONNECTION SUPPORT
M:	Reinette Chatre <reinette.chatre@intel.com>
M:	Intel Linux Wireless <ilw@linux.intel.com>
L:	linux-wireless@vger.kernel.org
W:	http://ipw2100.sourceforge.net
S:	Odd Fixes
S:	Orphan
F:	Documentation/networking/README.ipw2100
F:	drivers/net/wireless/ipw2x00/ipw2100.*

INTEL PRO/WIRELESS 2915ABG NETWORK CONNECTION SUPPORT
M:	Reinette Chatre <reinette.chatre@intel.com>
M:	Intel Linux Wireless <ilw@linux.intel.com>
L:	linux-wireless@vger.kernel.org
W:	http://ipw2200.sourceforge.net
S:	Odd Fixes
S:	Orphan
F:	Documentation/networking/README.ipw2200
F:	drivers/net/wireless/ipw2x00/ipw2200.*

+11 −33
Original line number Diff line number Diff line
@@ -126,26 +126,6 @@ static unsigned lock_loop(unsigned numbytes, struct inbuf_t *inbuf)
	return numbytes;
}

/* set up next receive skb for data mode
 */
static void new_rcv_skb(struct bc_state *bcs)
{
	struct cardstate *cs = bcs->cs;
	unsigned short hw_hdr_len = cs->hw_hdr_len;

	if (bcs->ignore) {
		bcs->skb = NULL;
		return;
	}

	bcs->skb = dev_alloc_skb(SBUFSIZE + hw_hdr_len);
	if (bcs->skb == NULL) {
		dev_warn(cs->dev, "could not allocate new skb\n");
		return;
	}
	skb_reserve(bcs->skb, hw_hdr_len);
}

/* process a block of received bytes in HDLC data mode
 * (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 == L2_HDLC)
 * Collect HDLC frames, undoing byte stuffing and watching for DLE escapes.
@@ -159,8 +139,8 @@ static unsigned hdlc_loop(unsigned numbytes, struct inbuf_t *inbuf)
	struct cardstate *cs = inbuf->cs;
	struct bc_state *bcs = cs->bcs;
	int inputstate = bcs->inputstate;
	__u16 fcs = bcs->fcs;
	struct sk_buff *skb = bcs->skb;
	__u16 fcs = bcs->rx_fcs;
	struct sk_buff *skb = bcs->rx_skb;
	unsigned char *src = inbuf->data + inbuf->head;
	unsigned procbytes = 0;
	unsigned char c;
@@ -245,8 +225,7 @@ byte_stuff:

				/* prepare reception of next frame */
				inputstate &= ~INS_have_data;
				new_rcv_skb(bcs);
				skb = bcs->skb;
				skb = gigaset_new_rx_skb(bcs);
			} else {
				/* empty frame (7E 7E) */
#ifdef CONFIG_GIGASET_DEBUG
@@ -255,8 +234,7 @@ byte_stuff:
				if (!skb) {
					/* skipped (?) */
					gigaset_isdn_rcv_err(bcs);
					new_rcv_skb(bcs);
					skb = bcs->skb;
					skb = gigaset_new_rx_skb(bcs);
				}
			}

@@ -279,11 +257,11 @@ byte_stuff:
#endif
		inputstate |= INS_have_data;
		if (skb) {
			if (skb->len == SBUFSIZE) {
			if (skb->len >= bcs->rx_bufsize) {
				dev_warn(cs->dev, "received packet too long\n");
				dev_kfree_skb_any(skb);
				/* skip remainder of packet */
				bcs->skb = skb = NULL;
				bcs->rx_skb = skb = NULL;
			} else {
				*__skb_put(skb, 1) = c;
				fcs = crc_ccitt_byte(fcs, c);
@@ -292,7 +270,7 @@ byte_stuff:
	}

	bcs->inputstate = inputstate;
	bcs->fcs = fcs;
	bcs->rx_fcs = fcs;
	return procbytes;
}

@@ -308,18 +286,18 @@ static unsigned iraw_loop(unsigned numbytes, struct inbuf_t *inbuf)
	struct cardstate *cs = inbuf->cs;
	struct bc_state *bcs = cs->bcs;
	int inputstate = bcs->inputstate;
	struct sk_buff *skb = bcs->skb;
	struct sk_buff *skb = bcs->rx_skb;
	unsigned char *src = inbuf->data + inbuf->head;
	unsigned procbytes = 0;
	unsigned char c;

	if (!skb) {
		/* skip this block */
		new_rcv_skb(bcs);
		gigaset_new_rx_skb(bcs);
		return numbytes;
	}

	while (procbytes < numbytes && skb->len < SBUFSIZE) {
	while (procbytes < numbytes && skb->len < bcs->rx_bufsize) {
		c = *src++;
		procbytes++;

@@ -343,7 +321,7 @@ static unsigned iraw_loop(unsigned numbytes, struct inbuf_t *inbuf)
	if (inputstate & INS_have_data) {
		gigaset_skb_rcvd(bcs, skb);
		inputstate &= ~INS_have_data;
		new_rcv_skb(bcs);
		gigaset_new_rx_skb(bcs);
	}

	bcs->inputstate = inputstate;
+285 −120
Original line number Diff line number Diff line
@@ -70,7 +70,7 @@
#define MAX_NUMBER_DIGITS 20
#define MAX_FMT_IE_LEN 20

/* values for gigaset_capi_appl.connected */
/* values for bcs->apconnstate */
#define APCONN_NONE	0	/* inactive/listening */
#define APCONN_SETUP	1	/* connecting */
#define APCONN_ACTIVE	2	/* B channel up */
@@ -80,10 +80,10 @@ struct gigaset_capi_appl {
	struct list_head ctrlist;
	struct gigaset_capi_appl *bcnext;
	u16 id;
	struct capi_register_params rp;
	u16 nextMessageNumber;
	u32 listenInfoMask;
	u32 listenCIPmask;
	int connected;
};

/* CAPI specific controller data structure */
@@ -319,6 +319,39 @@ static const char *format_ie(const char *ie)
	return result;
}

/*
 * emit DATA_B3_CONF message
 */
static void send_data_b3_conf(struct cardstate *cs, struct capi_ctr *ctr,
			      u16 appl, u16 msgid, int channel,
			      u16 handle, u16 info)
{
	struct sk_buff *cskb;
	u8 *msg;

	cskb = alloc_skb(CAPI_DATA_B3_CONF_LEN, GFP_ATOMIC);
	if (!cskb) {
		dev_err(cs->dev, "%s: out of memory\n", __func__);
		return;
	}
	/* frequent message, avoid _cmsg overhead */
	msg = __skb_put(cskb, CAPI_DATA_B3_CONF_LEN);
	CAPIMSG_SETLEN(msg, CAPI_DATA_B3_CONF_LEN);
	CAPIMSG_SETAPPID(msg, appl);
	CAPIMSG_SETCOMMAND(msg, CAPI_DATA_B3);
	CAPIMSG_SETSUBCOMMAND(msg,  CAPI_CONF);
	CAPIMSG_SETMSGID(msg, msgid);
	CAPIMSG_SETCONTROLLER(msg, ctr->cnr);
	CAPIMSG_SETPLCI_PART(msg, channel);
	CAPIMSG_SETNCCI_PART(msg, 1);
	CAPIMSG_SETHANDLE_CONF(msg, handle);
	CAPIMSG_SETINFO_CONF(msg, info);

	/* emit message */
	dump_rawmsg(DEBUG_MCMD, __func__, msg);
	capi_ctr_handle_message(ctr, appl, cskb);
}


/*
 * driver interface functions
@@ -339,7 +372,6 @@ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *dskb)
	struct gigaset_capi_ctr *iif = cs->iif;
	struct gigaset_capi_appl *ap = bcs->ap;
	unsigned char *req = skb_mac_header(dskb);
	struct sk_buff *cskb;
	u16 flags;

	/* update statistics */
@@ -351,39 +383,22 @@ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *dskb)
	}

	/* don't send further B3 messages if disconnected */
	if (ap->connected < APCONN_ACTIVE) {
	if (bcs->apconnstate < APCONN_ACTIVE) {
		gig_dbg(DEBUG_LLDATA, "disconnected, discarding ack");
		return;
	}

	/* ToDo: honor unset "delivery confirmation" bit */
	/*
	 * send DATA_B3_CONF if "delivery confirmation" bit was set in request;
	 * otherwise it has already been sent by do_data_b3_req()
	 */
	flags = CAPIMSG_FLAGS(req);

	/* build DATA_B3_CONF message */
	cskb = alloc_skb(CAPI_DATA_B3_CONF_LEN, GFP_ATOMIC);
	if (!cskb) {
		dev_err(cs->dev, "%s: out of memory\n", __func__);
		return;
	}
	/* frequent message, avoid _cmsg overhead */
	CAPIMSG_SETLEN(cskb->data, CAPI_DATA_B3_CONF_LEN);
	CAPIMSG_SETAPPID(cskb->data, ap->id);
	CAPIMSG_SETCOMMAND(cskb->data, CAPI_DATA_B3);
	CAPIMSG_SETSUBCOMMAND(cskb->data,  CAPI_CONF);
	CAPIMSG_SETMSGID(cskb->data, CAPIMSG_MSGID(req));
	CAPIMSG_SETCONTROLLER(cskb->data, iif->ctr.cnr);
	CAPIMSG_SETPLCI_PART(cskb->data, bcs->channel + 1);
	CAPIMSG_SETNCCI_PART(cskb->data, 1);
	CAPIMSG_SETHANDLE_CONF(cskb->data, CAPIMSG_HANDLE_REQ(req));
	if (flags & ~CAPI_FLAGS_DELIVERY_CONFIRMATION)
		CAPIMSG_SETINFO_CONF(cskb->data,
				     CapiFlagsNotSupportedByProtocol);
	else
		CAPIMSG_SETINFO_CONF(cskb->data, CAPI_NOERROR);

	/* emit message */
	dump_rawmsg(DEBUG_LLDATA, "DATA_B3_CONF", cskb->data);
	capi_ctr_handle_message(&iif->ctr, ap->id, cskb);
	if (flags & CAPI_FLAGS_DELIVERY_CONFIRMATION)
		send_data_b3_conf(cs, &iif->ctr, ap->id, CAPIMSG_MSGID(req),
				  bcs->channel + 1, CAPIMSG_HANDLE_REQ(req),
				  (flags & ~CAPI_FLAGS_DELIVERY_CONFIRMATION) ?
					CapiFlagsNotSupportedByProtocol :
					CAPI_NOERROR);
}
EXPORT_SYMBOL_GPL(gigaset_skb_sent);

@@ -412,7 +427,7 @@ void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
	}

	/* don't send further B3 messages if disconnected */
	if (ap->connected < APCONN_ACTIVE) {
	if (bcs->apconnstate < APCONN_ACTIVE) {
		gig_dbg(DEBUG_LLDATA, "disconnected, discarding data");
		dev_kfree_skb_any(skb);
		return;
@@ -484,6 +499,7 @@ int gigaset_isdn_icall(struct at_state_t *at_state)
	u32 actCIPmask;
	struct sk_buff *skb;
	unsigned int msgsize;
	unsigned long flags;
	int i;

	/*
@@ -608,7 +624,14 @@ int gigaset_isdn_icall(struct at_state_t *at_state)
		format_ie(iif->hcmsg.CalledPartyNumber));

	/* scan application list for matching listeners */
	spin_lock_irqsave(&bcs->aplock, flags);
	if (bcs->ap != NULL || bcs->apconnstate != APCONN_NONE) {
		dev_warn(cs->dev, "%s: channel not properly cleared (%p/%d)\n",
			 __func__, bcs->ap, bcs->apconnstate);
		bcs->ap = NULL;
		bcs->apconnstate = APCONN_NONE;
	}
	spin_unlock_irqrestore(&bcs->aplock, flags);
	actCIPmask = 1 | (1 << iif->hcmsg.CIPValue);
	list_for_each_entry(ap, &iif->appls, ctrlist)
		if (actCIPmask & ap->listenCIPmask) {
@@ -626,10 +649,12 @@ int gigaset_isdn_icall(struct at_state_t *at_state)
			dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);

			/* add to listeners on this B channel, update state */
			spin_lock_irqsave(&bcs->aplock, flags);
			ap->bcnext = bcs->ap;
			bcs->ap = ap;
			bcs->chstate |= CHS_NOTIFY_LL;
			ap->connected = APCONN_SETUP;
			bcs->apconnstate = APCONN_SETUP;
			spin_unlock_irqrestore(&bcs->aplock, flags);

			/* emit message */
			capi_ctr_handle_message(&iif->ctr, ap->id, skb);
@@ -654,7 +679,7 @@ static void send_disconnect_ind(struct bc_state *bcs,
	struct gigaset_capi_ctr *iif = cs->iif;
	struct sk_buff *skb;

	if (ap->connected == APCONN_NONE)
	if (bcs->apconnstate == APCONN_NONE)
		return;

	capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT, CAPI_IND,
@@ -668,7 +693,6 @@ static void send_disconnect_ind(struct bc_state *bcs,
	}
	capi_cmsg2message(&iif->hcmsg, __skb_put(skb, CAPI_DISCONNECT_IND_LEN));
	dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
	ap->connected = APCONN_NONE;
	capi_ctr_handle_message(&iif->ctr, ap->id, skb);
}

@@ -685,9 +709,9 @@ static void send_disconnect_b3_ind(struct bc_state *bcs,
	struct sk_buff *skb;

	/* nothing to do if no logical connection active */
	if (ap->connected < APCONN_ACTIVE)
	if (bcs->apconnstate < APCONN_ACTIVE)
		return;
	ap->connected = APCONN_SETUP;
	bcs->apconnstate = APCONN_SETUP;

	capi_cmsg_header(&iif->hcmsg, ap->id, CAPI_DISCONNECT_B3, CAPI_IND,
			 ap->nextMessageNumber++,
@@ -714,14 +738,25 @@ void gigaset_isdn_connD(struct bc_state *bcs)
{
	struct cardstate *cs = bcs->cs;
	struct gigaset_capi_ctr *iif = cs->iif;
	struct gigaset_capi_appl *ap = bcs->ap;
	struct gigaset_capi_appl *ap;
	struct sk_buff *skb;
	unsigned int msgsize;
	unsigned long flags;

	spin_lock_irqsave(&bcs->aplock, flags);
	ap = bcs->ap;
	if (!ap) {
		spin_unlock_irqrestore(&bcs->aplock, flags);
		dev_err(cs->dev, "%s: no application\n", __func__);
		return;
	}
	if (bcs->apconnstate == APCONN_NONE) {
		spin_unlock_irqrestore(&bcs->aplock, flags);
		dev_warn(cs->dev, "%s: application %u not connected\n",
			 __func__, ap->id);
		return;
	}
	spin_unlock_irqrestore(&bcs->aplock, flags);
	while (ap->bcnext) {
		/* this should never happen */
		dev_warn(cs->dev, "%s: dropping extra application %u\n",
@@ -730,11 +765,6 @@ void gigaset_isdn_connD(struct bc_state *bcs)
				    CapiCallGivenToOtherApplication);
		ap->bcnext = ap->bcnext->bcnext;
	}
	if (ap->connected == APCONN_NONE) {
		dev_warn(cs->dev, "%s: application %u not connected\n",
			 __func__, ap->id);
		return;
	}

	/* prepare CONNECT_ACTIVE_IND message
	 * Note: LLC not supported by device
@@ -772,17 +802,24 @@ void gigaset_isdn_connD(struct bc_state *bcs)
void gigaset_isdn_hupD(struct bc_state *bcs)
{
	struct gigaset_capi_appl *ap;
	unsigned long flags;

	/*
	 * ToDo: pass on reason code reported by device
	 * (requires ev-layer state machine extension to collect
	 * ZCAU device reply)
	 */
	for (ap = bcs->ap; ap != NULL; ap = ap->bcnext) {
	spin_lock_irqsave(&bcs->aplock, flags);
	while (bcs->ap != NULL) {
		ap = bcs->ap;
		bcs->ap = ap->bcnext;
		spin_unlock_irqrestore(&bcs->aplock, flags);
		send_disconnect_b3_ind(bcs, ap);
		send_disconnect_ind(bcs, ap, 0);
		spin_lock_irqsave(&bcs->aplock, flags);
	}
	bcs->ap = NULL;
	bcs->apconnstate = APCONN_NONE;
	spin_unlock_irqrestore(&bcs->aplock, flags);
}

/**
@@ -796,24 +833,21 @@ void gigaset_isdn_connB(struct bc_state *bcs)
{
	struct cardstate *cs = bcs->cs;
	struct gigaset_capi_ctr *iif = cs->iif;
	struct gigaset_capi_appl *ap = bcs->ap;
	struct gigaset_capi_appl *ap;
	struct sk_buff *skb;
	unsigned long flags;
	unsigned int msgsize;
	u8 command;

	spin_lock_irqsave(&bcs->aplock, flags);
	ap = bcs->ap;
	if (!ap) {
		spin_unlock_irqrestore(&bcs->aplock, flags);
		dev_err(cs->dev, "%s: no application\n", __func__);
		return;
	}
	while (ap->bcnext) {
		/* this should never happen */
		dev_warn(cs->dev, "%s: dropping extra application %u\n",
			 __func__, ap->bcnext->id);
		send_disconnect_ind(bcs, ap->bcnext,
				    CapiCallGivenToOtherApplication);
		ap->bcnext = ap->bcnext->bcnext;
	}
	if (!ap->connected) {
	if (!bcs->apconnstate) {
		spin_unlock_irqrestore(&bcs->aplock, flags);
		dev_warn(cs->dev, "%s: application %u not connected\n",
			 __func__, ap->id);
		return;
@@ -825,13 +859,26 @@ void gigaset_isdn_connB(struct bc_state *bcs)
	 * CONNECT_B3_ACTIVE_IND in reply to CONNECT_B3_RESP
	 * Parameters in both cases always: NCCI = 1, NCPI empty
	 */
	if (ap->connected >= APCONN_ACTIVE) {
	if (bcs->apconnstate >= APCONN_ACTIVE) {
		command = CAPI_CONNECT_B3_ACTIVE;
		msgsize = CAPI_CONNECT_B3_ACTIVE_IND_BASELEN;
	} else {
		command = CAPI_CONNECT_B3;
		msgsize = CAPI_CONNECT_B3_IND_BASELEN;
	}
	bcs->apconnstate = APCONN_ACTIVE;

	spin_unlock_irqrestore(&bcs->aplock, flags);

	while (ap->bcnext) {
		/* this should never happen */
		dev_warn(cs->dev, "%s: dropping extra application %u\n",
			 __func__, ap->bcnext->id);
		send_disconnect_ind(bcs, ap->bcnext,
				    CapiCallGivenToOtherApplication);
		ap->bcnext = ap->bcnext->bcnext;
	}

	capi_cmsg_header(&iif->hcmsg, ap->id, command, CAPI_IND,
			 ap->nextMessageNumber++,
			 iif->ctr.cnr | ((bcs->channel + 1) << 8) | (1 << 16));
@@ -842,7 +889,6 @@ void gigaset_isdn_connB(struct bc_state *bcs)
	}
	capi_cmsg2message(&iif->hcmsg, __skb_put(skb, msgsize));
	dump_cmsg(DEBUG_CMD, __func__, &iif->hcmsg);
	ap->connected = APCONN_ACTIVE;
	capi_ctr_handle_message(&iif->ctr, ap->id, skb);
}

@@ -945,8 +991,64 @@ static void gigaset_register_appl(struct capi_ctr *ctr, u16 appl,
		return;
	}
	ap->id = appl;
	ap->rp = *rp;

	list_add(&ap->ctrlist, &iif->appls);
	dev_info(cs->dev, "application %u registered\n", ap->id);
}

/*
 * remove CAPI application from channel
 * helper function to keep indentation levels down and stay in 80 columns
 */

static inline void remove_appl_from_channel(struct bc_state *bcs,
					    struct gigaset_capi_appl *ap)
{
	struct cardstate *cs = bcs->cs;
	struct gigaset_capi_appl *bcap;
	unsigned long flags;
	int prevconnstate;

	spin_lock_irqsave(&bcs->aplock, flags);
	bcap = bcs->ap;
	if (bcap == NULL) {
		spin_unlock_irqrestore(&bcs->aplock, flags);
		return;
	}

	/* check first application on channel */
	if (bcap == ap) {
		bcs->ap = ap->bcnext;
		if (bcs->ap != NULL) {
			spin_unlock_irqrestore(&bcs->aplock, flags);
			return;
		}

		/* none left, clear channel state */
		prevconnstate = bcs->apconnstate;
		bcs->apconnstate = APCONN_NONE;
		spin_unlock_irqrestore(&bcs->aplock, flags);

		if (prevconnstate == APCONN_ACTIVE) {
			dev_notice(cs->dev, "%s: hanging up channel %u\n",
				   __func__, bcs->channel);
			gigaset_add_event(cs, &bcs->at_state,
					  EV_HUP, NULL, 0, NULL);
			gigaset_schedule_event(cs);
		}
		return;
	}

	/* check remaining list */
	do {
		if (bcap->bcnext == ap) {
			bcap->bcnext = bcap->bcnext->bcnext;
			return;
		}
		bcap = bcap->bcnext;
	} while (bcap != NULL);
	spin_unlock_irqrestore(&bcs->aplock, flags);
}

/*
@@ -958,19 +1060,19 @@ static void gigaset_release_appl(struct capi_ctr *ctr, u16 appl)
		= container_of(ctr, struct gigaset_capi_ctr, ctr);
	struct cardstate *cs = iif->ctr.driverdata;
	struct gigaset_capi_appl *ap, *tmp;
	unsigned ch;

	list_for_each_entry_safe(ap, tmp, &iif->appls, ctrlist)
		if (ap->id == appl) {
			if (ap->connected != APCONN_NONE) {
				dev_err(cs->dev,
					"%s: application %u still connected\n",
					__func__, ap->id);
				/* ToDo: clear active connection */
			}
			/* remove from any channels */
			for (ch = 0; ch < cs->channels; ch++)
				remove_appl_from_channel(&cs->bcs[ch], ap);

			/* remove from registration list */
			list_del(&ap->ctrlist);
			kfree(ap);
			dev_info(cs->dev, "application %u released\n", appl);
		}

}

/*
@@ -1149,7 +1251,8 @@ static void do_connect_req(struct gigaset_capi_ctr *iif,
	char **commands;
	char *s;
	u8 *pp;
	int i, l;
	unsigned long flags;
	int i, l, lbc, lhlc;
	u16 info;

	/* decode message */
@@ -1164,8 +1267,18 @@ static void do_connect_req(struct gigaset_capi_ctr *iif,
		send_conf(iif, ap, skb, CapiNoPlciAvailable);
		return;
	}
	spin_lock_irqsave(&bcs->aplock, flags);
	if (bcs->ap != NULL || bcs->apconnstate != APCONN_NONE)
		dev_warn(cs->dev, "%s: channel not properly cleared (%p/%d)\n",
			 __func__, bcs->ap, bcs->apconnstate);
	ap->bcnext = NULL;
	bcs->ap = ap;
	bcs->apconnstate = APCONN_SETUP;
	spin_unlock_irqrestore(&bcs->aplock, flags);

	bcs->rx_bufsize = ap->rp.datablklen;
	dev_kfree_skb(bcs->rx_skb);
	gigaset_new_rx_skb(bcs);
	cmsg->adr.adrPLCI |= (bcs->channel + 1) << 8;

	/* build command table */
@@ -1273,43 +1386,60 @@ static void do_connect_req(struct gigaset_capi_ctr *iif,
		goto error;
	}

	/* check/encode parameter: BC */
	if (cmsg->BC && cmsg->BC[0]) {
		/* explicit BC overrides CIP */
		l = 2*cmsg->BC[0] + 7;
	/*
	 * check/encode parameters: BC & HLC
	 * must be encoded together as device doesn't accept HLC separately
	 * explicit parameters override values derived from CIP
	 */

	/* determine lengths */
	if (cmsg->BC && cmsg->BC[0])		/* BC specified explicitly */
		lbc = 2*cmsg->BC[0];
	else if (cip2bchlc[cmsg->CIPValue].bc)	/* BC derived from CIP */
		lbc = strlen(cip2bchlc[cmsg->CIPValue].bc);
	else					/* no BC */
		lbc = 0;
	if (cmsg->HLC && cmsg->HLC[0])		/* HLC specified explicitly */
		lhlc = 2*cmsg->HLC[0];
	else if (cip2bchlc[cmsg->CIPValue].hlc)	/* HLC derived from CIP */
		lhlc = strlen(cip2bchlc[cmsg->CIPValue].hlc);
	else					/* no HLC */
		lhlc = 0;

	if (lbc) {
		/* have BC: allocate and assemble command string */
		l = lbc + 7;		/* "^SBC=" + value + "\r" + null byte */
		if (lhlc)
			l += lhlc + 7;	/* ";^SHLC=" + value */
		commands[AT_BC] = kmalloc(l, GFP_KERNEL);
		if (!commands[AT_BC])
			goto oom;
		strcpy(commands[AT_BC], "^SBC=");
		if (cmsg->BC && cmsg->BC[0])	/* BC specified explicitly */
			decode_ie(cmsg->BC, commands[AT_BC] + 5);
		strcpy(commands[AT_BC] + l - 2, "\r");
	} else if (cip2bchlc[cmsg->CIPValue].bc) {
		l = strlen(cip2bchlc[cmsg->CIPValue].bc) + 7;
		commands[AT_BC] = kmalloc(l, GFP_KERNEL);
		if (!commands[AT_BC])
			goto oom;
		snprintf(commands[AT_BC], l, "^SBC=%s\r",
		else				/* BC derived from CIP */
			strcpy(commands[AT_BC] + 5,
			       cip2bchlc[cmsg->CIPValue].bc);
	}

	/* check/encode parameter: HLC */
	if (cmsg->HLC && cmsg->HLC[0]) {
		/* explicit HLC overrides CIP */
		l = 2*cmsg->HLC[0] + 7;
		commands[AT_HLC] = kmalloc(l, GFP_KERNEL);
		if (!commands[AT_HLC])
			goto oom;
		strcpy(commands[AT_HLC], "^SHLC=");
		decode_ie(cmsg->HLC, commands[AT_HLC]+5);
		strcpy(commands[AT_HLC] + l - 2, "\r");
	} else if (cip2bchlc[cmsg->CIPValue].hlc) {
		l = strlen(cip2bchlc[cmsg->CIPValue].hlc) + 7;
		commands[AT_HLC] = kmalloc(l, GFP_KERNEL);
		if (!commands[AT_HLC])
			goto oom;
		snprintf(commands[AT_HLC], l, "^SHLC=%s\r",
		if (lhlc) {
			strcpy(commands[AT_BC] + lbc + 5, ";^SHLC=");
			if (cmsg->HLC && cmsg->HLC[0])
				/* HLC specified explicitly */
				decode_ie(cmsg->HLC,
					  commands[AT_BC] + lbc + 12);
			else	/* HLC derived from CIP */
				strcpy(commands[AT_BC] + lbc + 12,
				       cip2bchlc[cmsg->CIPValue].hlc);
		}
		strcpy(commands[AT_BC] + l - 2, "\r");
	} else {
		/* no BC */
		if (lhlc) {
			dev_notice(cs->dev, "%s: cannot set HLC without BC\n",
				   "CONNECT_REQ");
			info = CapiIllMessageParmCoding; /* ? */
			goto error;
		}
	}

	/* check/encode parameter: B Protocol */
	if (cmsg->BProtocol == CAPI_DEFAULT) {
@@ -1322,13 +1452,13 @@ static void do_connect_req(struct gigaset_capi_ctr *iif,
			bcs->proto2 = L2_HDLC;
			break;
		case 1:
			bcs->proto2 = L2_BITSYNC;
			bcs->proto2 = L2_VOICE;
			break;
		default:
			dev_warn(cs->dev,
			    "B1 Protocol %u unsupported, using Transparent\n",
				 cmsg->B1protocol);
			bcs->proto2 = L2_BITSYNC;
			bcs->proto2 = L2_VOICE;
		}
		if (cmsg->B2protocol != 1)
			dev_warn(cs->dev,
@@ -1382,7 +1512,6 @@ static void do_connect_req(struct gigaset_capi_ctr *iif,
		goto error;
	}
	gigaset_schedule_event(cs);
	ap->connected = APCONN_SETUP;
	send_conf(iif, ap, skb, CapiSuccess);
	return;

@@ -1410,6 +1539,7 @@ static void do_connect_resp(struct gigaset_capi_ctr *iif,
	_cmsg *cmsg = &iif->acmsg;
	struct bc_state *bcs;
	struct gigaset_capi_appl *oap;
	unsigned long flags;
	int channel;

	/* decode message */
@@ -1429,12 +1559,24 @@ static void do_connect_resp(struct gigaset_capi_ctr *iif,
	switch (cmsg->Reject) {
	case 0:		/* Accept */
		/* drop all competing applications, keep only this one */
		for (oap = bcs->ap; oap != NULL; oap = oap->bcnext)
			if (oap != ap)
		spin_lock_irqsave(&bcs->aplock, flags);
		while (bcs->ap != NULL) {
			oap = bcs->ap;
			bcs->ap = oap->bcnext;
			if (oap != ap) {
				spin_unlock_irqrestore(&bcs->aplock, flags);
				send_disconnect_ind(bcs, oap,
					CapiCallGivenToOtherApplication);
				spin_lock_irqsave(&bcs->aplock, flags);
			}
		}
		ap->bcnext = NULL;
		bcs->ap = ap;
		spin_unlock_irqrestore(&bcs->aplock, flags);

		bcs->rx_bufsize = ap->rp.datablklen;
		dev_kfree_skb(bcs->rx_skb);
		gigaset_new_rx_skb(bcs);
		bcs->chstate |= CHS_NOTIFY_LL;

		/* check/encode B channel protocol */
@@ -1448,13 +1590,13 @@ static void do_connect_resp(struct gigaset_capi_ctr *iif,
				bcs->proto2 = L2_HDLC;
				break;
			case 1:
				bcs->proto2 = L2_BITSYNC;
				bcs->proto2 = L2_VOICE;
				break;
			default:
				dev_warn(cs->dev,
			"B1 Protocol %u unsupported, using Transparent\n",
					 cmsg->B1protocol);
				bcs->proto2 = L2_BITSYNC;
				bcs->proto2 = L2_VOICE;
			}
			if (cmsg->B2protocol != 1)
				dev_warn(cs->dev,
@@ -1502,31 +1644,45 @@ static void do_connect_resp(struct gigaset_capi_ctr *iif,
		send_disconnect_ind(bcs, ap, 0);

		/* remove it from the list of listening apps */
		spin_lock_irqsave(&bcs->aplock, flags);
		if (bcs->ap == ap) {
			bcs->ap = ap->bcnext;
			if (bcs->ap == NULL)
			if (bcs->ap == NULL) {
				/* last one: stop ev-layer hupD notifications */
				bcs->apconnstate = APCONN_NONE;
				bcs->chstate &= ~CHS_NOTIFY_LL;
			}
			spin_unlock_irqrestore(&bcs->aplock, flags);
			return;
		}
		for (oap = bcs->ap; oap != NULL; oap = oap->bcnext) {
			if (oap->bcnext == ap) {
				oap->bcnext = oap->bcnext->bcnext;
				spin_unlock_irqrestore(&bcs->aplock, flags);
				return;
			}
		}
		spin_unlock_irqrestore(&bcs->aplock, flags);
		dev_err(cs->dev, "%s: application %u not found\n",
			__func__, ap->id);
		return;

	default:		/* Reject */
		/* drop all competing applications, keep only this one */
		for (oap = bcs->ap; oap != NULL; oap = oap->bcnext)
			if (oap != ap)
		spin_lock_irqsave(&bcs->aplock, flags);
		while (bcs->ap != NULL) {
			oap = bcs->ap;
			bcs->ap = oap->bcnext;
			if (oap != ap) {
				spin_unlock_irqrestore(&bcs->aplock, flags);
				send_disconnect_ind(bcs, oap,
					CapiCallGivenToOtherApplication);
				spin_lock_irqsave(&bcs->aplock, flags);
			}
		}
		ap->bcnext = NULL;
		bcs->ap = ap;
		spin_unlock_irqrestore(&bcs->aplock, flags);

		/* reject call - will trigger DISCONNECT_IND for this app */
		dev_info(cs->dev, "%s: Reject=%x\n",
@@ -1549,6 +1705,7 @@ static void do_connect_b3_req(struct gigaset_capi_ctr *iif,
{
	struct cardstate *cs = iif->ctr.driverdata;
	_cmsg *cmsg = &iif->acmsg;
	struct bc_state *bcs;
	int channel;

	/* decode message */
@@ -1563,9 +1720,10 @@ static void do_connect_b3_req(struct gigaset_capi_ctr *iif,
		send_conf(iif, ap, skb, CapiIllContrPlciNcci);
		return;
	}
	bcs = &cs->bcs[channel-1];

	/* mark logical connection active */
	ap->connected = APCONN_ACTIVE;
	bcs->apconnstate = APCONN_ACTIVE;

	/* build NCCI: always 1 (one B3 connection only) */
	cmsg->adr.adrNCCI |= 1 << 16;
@@ -1611,7 +1769,7 @@ static void do_connect_b3_resp(struct gigaset_capi_ctr *iif,

	if (cmsg->Reject) {
		/* Reject: clear B3 connect received flag */
		ap->connected = APCONN_SETUP;
		bcs->apconnstate = APCONN_SETUP;

		/* trigger hangup, causing eventual DISCONNECT_IND */
		if (!gigaset_add_event(cs, &bcs->at_state,
@@ -1683,11 +1841,11 @@ static void do_disconnect_req(struct gigaset_capi_ctr *iif,
	}

	/* skip if DISCONNECT_IND already sent */
	if (!ap->connected)
	if (!bcs->apconnstate)
		return;

	/* check for active logical connection */
	if (ap->connected >= APCONN_ACTIVE) {
	if (bcs->apconnstate >= APCONN_ACTIVE) {
		/*
		 * emit DISCONNECT_B3_IND with cause 0x3301
		 * use separate cmsg structure, as the content of iif->acmsg
@@ -1736,6 +1894,7 @@ static void do_disconnect_b3_req(struct gigaset_capi_ctr *iif,
{
	struct cardstate *cs = iif->ctr.driverdata;
	_cmsg *cmsg = &iif->acmsg;
	struct bc_state *bcs;
	int channel;

	/* decode message */
@@ -1751,17 +1910,17 @@ static void do_disconnect_b3_req(struct gigaset_capi_ctr *iif,
		send_conf(iif, ap, skb, CapiIllContrPlciNcci);
		return;
	}
	bcs = &cs->bcs[channel-1];

	/* reject if logical connection not active */
	if (ap->connected < APCONN_ACTIVE) {
	if (bcs->apconnstate < APCONN_ACTIVE) {
		send_conf(iif, ap, skb,
			  CapiMessageNotSupportedInCurrentState);
		return;
	}

	/* trigger hangup, causing eventual DISCONNECT_B3_IND */
	if (!gigaset_add_event(cs, &cs->bcs[channel-1].at_state,
			       EV_HUP, NULL, 0, NULL)) {
	if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) {
		send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
		return;
	}
@@ -1782,11 +1941,14 @@ static void do_data_b3_req(struct gigaset_capi_ctr *iif,
			   struct sk_buff *skb)
{
	struct cardstate *cs = iif->ctr.driverdata;
	struct bc_state *bcs;
	int channel = CAPIMSG_PLCI_PART(skb->data);
	u16 ncci = CAPIMSG_NCCI_PART(skb->data);
	u16 msglen = CAPIMSG_LEN(skb->data);
	u16 datalen = CAPIMSG_DATALEN(skb->data);
	u16 flags = CAPIMSG_FLAGS(skb->data);
	u16 msgid = CAPIMSG_MSGID(skb->data);
	u16 handle = CAPIMSG_HANDLE_REQ(skb->data);

	/* frequent message, avoid _cmsg overhead */
	dump_rawmsg(DEBUG_LLDATA, "DATA_B3_REQ", skb->data);
@@ -1802,6 +1964,7 @@ static void do_data_b3_req(struct gigaset_capi_ctr *iif,
		send_conf(iif, ap, skb, CapiIllContrPlciNcci);
		return;
	}
	bcs = &cs->bcs[channel-1];
	if (msglen != CAPI_DATA_B3_REQ_LEN && msglen != CAPI_DATA_B3_REQ_LEN64)
		dev_notice(cs->dev, "%s: unexpected length %d\n",
			   "DATA_B3_REQ", msglen);
@@ -1821,7 +1984,7 @@ static void do_data_b3_req(struct gigaset_capi_ctr *iif,
	}

	/* reject if logical connection not active */
	if (ap->connected < APCONN_ACTIVE) {
	if (bcs->apconnstate < APCONN_ACTIVE) {
		send_conf(iif, ap, skb, CapiMessageNotSupportedInCurrentState);
		return;
	}
@@ -1832,17 +1995,19 @@ static void do_data_b3_req(struct gigaset_capi_ctr *iif,
	skb_pull(skb, msglen);

	/* pass to device-specific module */
	if (cs->ops->send_skb(&cs->bcs[channel-1], skb) < 0) {
	if (cs->ops->send_skb(bcs, skb) < 0) {
		send_conf(iif, ap, skb, CAPI_MSGOSRESOURCEERR);
		return;
	}

	/* DATA_B3_CONF reply will be sent by gigaset_skb_sent() */

	/*
	 * ToDo: honor unset "delivery confirmation" bit
	 * (send DATA_B3_CONF immediately?)
	 * DATA_B3_CONF will be sent by gigaset_skb_sent() only if "delivery
	 * confirmation" bit is set; otherwise we have to send it now
	 */
	if (!(flags & CAPI_FLAGS_DELIVERY_CONFIRMATION))
		send_data_b3_conf(cs, &iif->ctr, ap->id, msgid, channel, handle,
				  flags ? CapiFlagsNotSupportedByProtocol
					: CAPI_NOERROR);
}

/*
Loading