xref: /openbmc/linux/drivers/isdn/capi/kcapi.c (revision cdb27b7b)
11da177e4SLinus Torvalds /* $Id: kcapi.c,v 1.1.2.8 2004/03/26 19:57:20 armin Exp $
21da177e4SLinus Torvalds  *
31da177e4SLinus Torvalds  * Kernel CAPI 2.0 Module
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright 1999 by Carsten Paeth <calle@calle.de>
61da177e4SLinus Torvalds  * Copyright 2002 by Kai Germaschewski <kai@germaschewski.name>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * This software may be used and distributed according to the terms
91da177e4SLinus Torvalds  * of the GNU General Public License, incorporated herein by reference.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  */
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds #include "kcapi.h"
141da177e4SLinus Torvalds #include <linux/module.h>
151da177e4SLinus Torvalds #include <linux/mm.h>
161da177e4SLinus Torvalds #include <linux/interrupt.h>
171da177e4SLinus Torvalds #include <linux/ioport.h>
181da177e4SLinus Torvalds #include <linux/proc_fs.h>
19174cd4b1SIngo Molnar #include <linux/sched/signal.h>
201da177e4SLinus Torvalds #include <linux/seq_file.h>
211da177e4SLinus Torvalds #include <linux/skbuff.h>
221da177e4SLinus Torvalds #include <linux/workqueue.h>
231da177e4SLinus Torvalds #include <linux/capi.h>
241da177e4SLinus Torvalds #include <linux/kernelcapi.h>
251da177e4SLinus Torvalds #include <linux/init.h>
261da177e4SLinus Torvalds #include <linux/moduleparam.h>
271da177e4SLinus Torvalds #include <linux/delay.h>
285a0e3ad6STejun Heo #include <linux/slab.h>
297c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
301da177e4SLinus Torvalds #include <linux/isdn/capicmd.h>
311da177e4SLinus Torvalds #include <linux/isdn/capiutil.h>
329cdf1827SArjan van de Ven #include <linux/mutex.h>
3388c896efSJan Kiszka #include <linux/rcupdate.h>
341da177e4SLinus Torvalds 
352cd24a2eSJason Wang static int showcapimsgs;
36158fa677STejun Heo static struct workqueue_struct *kcapi_wq;
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds module_param(showcapimsgs, uint, 0);
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds /* ------------------------------------------------------------- */
411da177e4SLinus Torvalds 
42ef69bb2eSJan Kiszka struct capictr_event {
431da177e4SLinus Torvalds 	struct work_struct work;
44ef69bb2eSJan Kiszka 	unsigned int type;
451da177e4SLinus Torvalds 	u32 controller;
461da177e4SLinus Torvalds };
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds /* ------------------------------------------------------------- */
491da177e4SLinus Torvalds 
50733a707dSBhumika Goyal static const struct capi_version driver_version = {2, 0, 1, 1 << 4};
511da177e4SLinus Torvalds static char driver_serial[CAPI_SERIAL_LEN] = "0004711";
521da177e4SLinus Torvalds static char capi_manufakturer[64] = "AVM Berlin";
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds #define NCCI2CTRL(ncci)    (((ncci) >> 24) & 0x7f)
551da177e4SLinus Torvalds 
560ca3a017SJan Kiszka struct capi_ctr *capi_controller[CAPI_MAXCONTR];
570ca3a017SJan Kiszka DEFINE_MUTEX(capi_controller_lock);
580ca3a017SJan Kiszka 
591da177e4SLinus Torvalds struct capi20_appl *capi_applications[CAPI_MAXAPPL];
601da177e4SLinus Torvalds 
6152253031SJan Kiszka static int ncontrollers;
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds /* -------- controller ref counting -------------------------------------- */
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds static inline struct capi_ctr *
capi_ctr_get(struct capi_ctr * ctr)6652253031SJan Kiszka capi_ctr_get(struct capi_ctr *ctr)
671da177e4SLinus Torvalds {
6852253031SJan Kiszka 	if (!try_module_get(ctr->owner))
691da177e4SLinus Torvalds 		return NULL;
7052253031SJan Kiszka 	return ctr;
711da177e4SLinus Torvalds }
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds static inline void
capi_ctr_put(struct capi_ctr * ctr)7452253031SJan Kiszka capi_ctr_put(struct capi_ctr *ctr)
751da177e4SLinus Torvalds {
7652253031SJan Kiszka 	module_put(ctr->owner);
771da177e4SLinus Torvalds }
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds /* ------------------------------------------------------------- */
801da177e4SLinus Torvalds 
get_capi_ctr_by_nr(u16 contr)811da177e4SLinus Torvalds static inline struct capi_ctr *get_capi_ctr_by_nr(u16 contr)
821da177e4SLinus Torvalds {
8325dff94fSDan Carpenter 	if (contr < 1 || contr - 1 >= CAPI_MAXCONTR)
841da177e4SLinus Torvalds 		return NULL;
851da177e4SLinus Torvalds 
8652253031SJan Kiszka 	return capi_controller[contr - 1];
871da177e4SLinus Torvalds }
881da177e4SLinus Torvalds 
__get_capi_appl_by_nr(u16 applid)89b003f4e1SJan Kiszka static inline struct capi20_appl *__get_capi_appl_by_nr(u16 applid)
90b003f4e1SJan Kiszka {
91b003f4e1SJan Kiszka 	lockdep_assert_held(&capi_controller_lock);
92b003f4e1SJan Kiszka 
9325dff94fSDan Carpenter 	if (applid < 1 || applid - 1 >= CAPI_MAXAPPL)
94b003f4e1SJan Kiszka 		return NULL;
95b003f4e1SJan Kiszka 
96b003f4e1SJan Kiszka 	return capi_applications[applid - 1];
97b003f4e1SJan Kiszka }
98b003f4e1SJan Kiszka 
get_capi_appl_by_nr(u16 applid)991da177e4SLinus Torvalds static inline struct capi20_appl *get_capi_appl_by_nr(u16 applid)
1001da177e4SLinus Torvalds {
10125dff94fSDan Carpenter 	if (applid < 1 || applid - 1 >= CAPI_MAXAPPL)
1021da177e4SLinus Torvalds 		return NULL;
1031da177e4SLinus Torvalds 
10488c896efSJan Kiszka 	return rcu_dereference(capi_applications[applid - 1]);
1051da177e4SLinus Torvalds }
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds /* -------- util functions ------------------------------------ */
1081da177e4SLinus Torvalds 
capi_cmd_valid(u8 cmd)1091da177e4SLinus Torvalds static inline int capi_cmd_valid(u8 cmd)
1101da177e4SLinus Torvalds {
1111da177e4SLinus Torvalds 	switch (cmd) {
1121da177e4SLinus Torvalds 	case CAPI_ALERT:
1131da177e4SLinus Torvalds 	case CAPI_CONNECT:
1141da177e4SLinus Torvalds 	case CAPI_CONNECT_ACTIVE:
1151da177e4SLinus Torvalds 	case CAPI_CONNECT_B3_ACTIVE:
1161da177e4SLinus Torvalds 	case CAPI_CONNECT_B3:
1171da177e4SLinus Torvalds 	case CAPI_CONNECT_B3_T90_ACTIVE:
1181da177e4SLinus Torvalds 	case CAPI_DATA_B3:
1191da177e4SLinus Torvalds 	case CAPI_DISCONNECT_B3:
1201da177e4SLinus Torvalds 	case CAPI_DISCONNECT:
1211da177e4SLinus Torvalds 	case CAPI_FACILITY:
1221da177e4SLinus Torvalds 	case CAPI_INFO:
1231da177e4SLinus Torvalds 	case CAPI_LISTEN:
1241da177e4SLinus Torvalds 	case CAPI_MANUFACTURER:
1251da177e4SLinus Torvalds 	case CAPI_RESET_B3:
1261da177e4SLinus Torvalds 	case CAPI_SELECT_B_PROTOCOL:
1271da177e4SLinus Torvalds 		return 1;
1281da177e4SLinus Torvalds 	}
1291da177e4SLinus Torvalds 	return 0;
1301da177e4SLinus Torvalds }
1311da177e4SLinus Torvalds 
capi_subcmd_valid(u8 subcmd)1321da177e4SLinus Torvalds static inline int capi_subcmd_valid(u8 subcmd)
1331da177e4SLinus Torvalds {
1341da177e4SLinus Torvalds 	switch (subcmd) {
1351da177e4SLinus Torvalds 	case CAPI_REQ:
1361da177e4SLinus Torvalds 	case CAPI_CONF:
1371da177e4SLinus Torvalds 	case CAPI_IND:
1381da177e4SLinus Torvalds 	case CAPI_RESP:
1391da177e4SLinus Torvalds 		return 1;
1401da177e4SLinus Torvalds 	}
1411da177e4SLinus Torvalds 	return 0;
1421da177e4SLinus Torvalds }
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds /* ------------------------------------------------------------ */
1451da177e4SLinus Torvalds 
14652253031SJan Kiszka static void
register_appl(struct capi_ctr * ctr,u16 applid,capi_register_params * rparam)14752253031SJan Kiszka register_appl(struct capi_ctr *ctr, u16 applid, capi_register_params *rparam)
1481da177e4SLinus Torvalds {
14952253031SJan Kiszka 	ctr = capi_ctr_get(ctr);
1501da177e4SLinus Torvalds 
15152253031SJan Kiszka 	if (ctr)
15252253031SJan Kiszka 		ctr->register_appl(ctr, applid, rparam);
1531da177e4SLinus Torvalds 	else
15452253031SJan Kiszka 		printk(KERN_WARNING "%s: cannot get controller resources\n",
15552253031SJan Kiszka 		       __func__);
1561da177e4SLinus Torvalds }
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds 
release_appl(struct capi_ctr * ctr,u16 applid)15952253031SJan Kiszka static void release_appl(struct capi_ctr *ctr, u16 applid)
1601da177e4SLinus Torvalds {
1611da177e4SLinus Torvalds 	DBG("applid %#x", applid);
1621da177e4SLinus Torvalds 
16352253031SJan Kiszka 	ctr->release_appl(ctr, applid);
16452253031SJan Kiszka 	capi_ctr_put(ctr);
1651da177e4SLinus Torvalds }
1661da177e4SLinus Torvalds 
notify_up(u32 contr)1671da177e4SLinus Torvalds static void notify_up(u32 contr)
1681da177e4SLinus Torvalds {
1691da177e4SLinus Torvalds 	struct capi20_appl *ap;
1703efecf7aSJan Kiszka 	struct capi_ctr *ctr;
1711da177e4SLinus Torvalds 	u16 applid;
1721da177e4SLinus Torvalds 
1730ca3a017SJan Kiszka 	mutex_lock(&capi_controller_lock);
1740ca3a017SJan Kiszka 
1753efecf7aSJan Kiszka 	if (showcapimsgs & 1)
1761da177e4SLinus Torvalds 		printk(KERN_DEBUG "kcapi: notify up contr %d\n", contr);
1773efecf7aSJan Kiszka 
1783efecf7aSJan Kiszka 	ctr = get_capi_ctr_by_nr(contr);
1793efecf7aSJan Kiszka 	if (ctr) {
1803efecf7aSJan Kiszka 		if (ctr->state == CAPI_CTR_RUNNING)
1810ca3a017SJan Kiszka 			goto unlock_out;
1823efecf7aSJan Kiszka 
1833efecf7aSJan Kiszka 		ctr->state = CAPI_CTR_RUNNING;
1843efecf7aSJan Kiszka 
1851da177e4SLinus Torvalds 		for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
186b003f4e1SJan Kiszka 			ap = __get_capi_appl_by_nr(applid);
187b003f4e1SJan Kiszka 			if (ap)
18852253031SJan Kiszka 				register_appl(ctr, applid, &ap->rparam);
1891da177e4SLinus Torvalds 		}
1903efecf7aSJan Kiszka 	} else
1913efecf7aSJan Kiszka 		printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr);
1920ca3a017SJan Kiszka 
1930ca3a017SJan Kiszka unlock_out:
1940ca3a017SJan Kiszka 	mutex_unlock(&capi_controller_lock);
1951da177e4SLinus Torvalds }
1961da177e4SLinus Torvalds 
ctr_down(struct capi_ctr * ctr,int new_state)1970ca3a017SJan Kiszka static void ctr_down(struct capi_ctr *ctr, int new_state)
1981da177e4SLinus Torvalds {
1991da177e4SLinus Torvalds 	struct capi20_appl *ap;
2001da177e4SLinus Torvalds 	u16 applid;
2011da177e4SLinus Torvalds 
2020ca3a017SJan Kiszka 	if (ctr->state == CAPI_CTR_DETECTED || ctr->state == CAPI_CTR_DETACHED)
2033efecf7aSJan Kiszka 		return;
2043efecf7aSJan Kiszka 
2050ca3a017SJan Kiszka 	ctr->state = new_state;
2063efecf7aSJan Kiszka 
2073efecf7aSJan Kiszka 	memset(ctr->manu, 0, sizeof(ctr->manu));
2083efecf7aSJan Kiszka 	memset(&ctr->version, 0, sizeof(ctr->version));
2093efecf7aSJan Kiszka 	memset(&ctr->profile, 0, sizeof(ctr->profile));
2103efecf7aSJan Kiszka 	memset(ctr->serial, 0, sizeof(ctr->serial));
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds 	for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
213b003f4e1SJan Kiszka 		ap = __get_capi_appl_by_nr(applid);
21488c896efSJan Kiszka 		if (ap)
2153efecf7aSJan Kiszka 			capi_ctr_put(ctr);
2161da177e4SLinus Torvalds 	}
2171da177e4SLinus Torvalds }
2183efecf7aSJan Kiszka 
notify_down(u32 contr)2193efecf7aSJan Kiszka static void notify_down(u32 contr)
2203efecf7aSJan Kiszka {
2213efecf7aSJan Kiszka 	struct capi_ctr *ctr;
2223efecf7aSJan Kiszka 
2230ca3a017SJan Kiszka 	mutex_lock(&capi_controller_lock);
2240ca3a017SJan Kiszka 
2253efecf7aSJan Kiszka 	if (showcapimsgs & 1)
2263efecf7aSJan Kiszka 		printk(KERN_DEBUG "kcapi: notify down contr %d\n", contr);
2273efecf7aSJan Kiszka 
2283efecf7aSJan Kiszka 	ctr = get_capi_ctr_by_nr(contr);
2293efecf7aSJan Kiszka 	if (ctr)
2300ca3a017SJan Kiszka 		ctr_down(ctr, CAPI_CTR_DETECTED);
2313efecf7aSJan Kiszka 	else
2323efecf7aSJan Kiszka 		printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr);
2330ca3a017SJan Kiszka 
2340ca3a017SJan Kiszka 	mutex_unlock(&capi_controller_lock);
2353efecf7aSJan Kiszka }
2361da177e4SLinus Torvalds 
do_notify_work(struct work_struct * work)237ef69bb2eSJan Kiszka static void do_notify_work(struct work_struct *work)
238ef69bb2eSJan Kiszka {
239ef69bb2eSJan Kiszka 	struct capictr_event *event =
240ef69bb2eSJan Kiszka 		container_of(work, struct capictr_event, work);
241ef69bb2eSJan Kiszka 
242f59aba2fSArnd Bergmann 	switch (event->type) {
243f59aba2fSArnd Bergmann 	case CAPICTR_UP:
244f59aba2fSArnd Bergmann 		notify_up(event->controller);
245f59aba2fSArnd Bergmann 		break;
246f59aba2fSArnd Bergmann 	case CAPICTR_DOWN:
247f59aba2fSArnd Bergmann 		notify_down(event->controller);
248f59aba2fSArnd Bergmann 		break;
249f59aba2fSArnd Bergmann 	}
250f59aba2fSArnd Bergmann 
251ef69bb2eSJan Kiszka 	kfree(event);
2521da177e4SLinus Torvalds }
2531da177e4SLinus Torvalds 
notify_push(unsigned int event_type,u32 controller)254ef69bb2eSJan Kiszka static int notify_push(unsigned int event_type, u32 controller)
2551da177e4SLinus Torvalds {
256ef69bb2eSJan Kiszka 	struct capictr_event *event = kmalloc(sizeof(*event), GFP_ATOMIC);
2571da177e4SLinus Torvalds 
258ef69bb2eSJan Kiszka 	if (!event)
2591da177e4SLinus Torvalds 		return -ENOMEM;
2601da177e4SLinus Torvalds 
261ef69bb2eSJan Kiszka 	INIT_WORK(&event->work, do_notify_work);
262ef69bb2eSJan Kiszka 	event->type = event_type;
263ef69bb2eSJan Kiszka 	event->controller = controller;
2641da177e4SLinus Torvalds 
265158fa677STejun Heo 	queue_work(kcapi_wq, &event->work);
2661da177e4SLinus Torvalds 	return 0;
2671da177e4SLinus Torvalds }
2681da177e4SLinus Torvalds 
2691da177e4SLinus Torvalds /* -------- Receiver ------------------------------------------ */
2701da177e4SLinus Torvalds 
recv_handler(struct work_struct * work)271c4028958SDavid Howells static void recv_handler(struct work_struct *work)
2721da177e4SLinus Torvalds {
2731da177e4SLinus Torvalds 	struct sk_buff *skb;
274c4028958SDavid Howells 	struct capi20_appl *ap =
275c4028958SDavid Howells 		container_of(work, struct capi20_appl, recv_work);
2761da177e4SLinus Torvalds 
2771da177e4SLinus Torvalds 	if ((!ap) || (ap->release_in_progress))
2781da177e4SLinus Torvalds 		return;
2791da177e4SLinus Torvalds 
28067837f23SMatthias Kaehlcke 	mutex_lock(&ap->recv_mtx);
2811da177e4SLinus Torvalds 	while ((skb = skb_dequeue(&ap->recv_queue))) {
2821da177e4SLinus Torvalds 		if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_IND)
2831da177e4SLinus Torvalds 			ap->nrecvdatapkt++;
2841da177e4SLinus Torvalds 		else
2851da177e4SLinus Torvalds 			ap->nrecvctlpkt++;
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds 		ap->recv_message(ap, skb);
2881da177e4SLinus Torvalds 	}
28967837f23SMatthias Kaehlcke 	mutex_unlock(&ap->recv_mtx);
2901da177e4SLinus Torvalds }
2911da177e4SLinus Torvalds 
292554f200eSTilman Schmidt /**
293554f200eSTilman Schmidt  * capi_ctr_handle_message() - handle incoming CAPI message
29452253031SJan Kiszka  * @ctr:	controller descriptor structure.
295554f200eSTilman Schmidt  * @appl:	application ID.
296554f200eSTilman Schmidt  * @skb:	message.
297554f200eSTilman Schmidt  *
298554f200eSTilman Schmidt  * Called by hardware driver to pass a CAPI message to the application.
299554f200eSTilman Schmidt  */
300554f200eSTilman Schmidt 
capi_ctr_handle_message(struct capi_ctr * ctr,u16 appl,struct sk_buff * skb)30152253031SJan Kiszka void capi_ctr_handle_message(struct capi_ctr *ctr, u16 appl,
30252253031SJan Kiszka 			     struct sk_buff *skb)
3031da177e4SLinus Torvalds {
3041da177e4SLinus Torvalds 	struct capi20_appl *ap;
3051da177e4SLinus Torvalds 	int showctl = 0;
3061da177e4SLinus Torvalds 	u8 cmd, subcmd;
30717f0cd2fSKarsten Keil 	_cdebbuf *cdb;
3081da177e4SLinus Torvalds 
30952253031SJan Kiszka 	if (ctr->state != CAPI_CTR_RUNNING) {
31017f0cd2fSKarsten Keil 		cdb = capi_message2str(skb->data);
31117f0cd2fSKarsten Keil 		if (cdb) {
31217f0cd2fSKarsten Keil 			printk(KERN_INFO "kcapi: controller [%03d] not active, got: %s",
31352253031SJan Kiszka 			       ctr->cnr, cdb->buf);
31417f0cd2fSKarsten Keil 			cdebbuf_free(cdb);
31517f0cd2fSKarsten Keil 		} else
31617f0cd2fSKarsten Keil 			printk(KERN_INFO "kcapi: controller [%03d] not active, cannot trace\n",
31752253031SJan Kiszka 			       ctr->cnr);
3181da177e4SLinus Torvalds 		goto error;
3191da177e4SLinus Torvalds 	}
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 	cmd = CAPIMSG_COMMAND(skb->data);
3221da177e4SLinus Torvalds 	subcmd = CAPIMSG_SUBCOMMAND(skb->data);
3231da177e4SLinus Torvalds 	if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) {
32452253031SJan Kiszka 		ctr->nrecvdatapkt++;
32552253031SJan Kiszka 		if (ctr->traceflag > 2)
32652253031SJan Kiszka 			showctl |= 2;
3271da177e4SLinus Torvalds 	} else {
32852253031SJan Kiszka 		ctr->nrecvctlpkt++;
32952253031SJan Kiszka 		if (ctr->traceflag)
33052253031SJan Kiszka 			showctl |= 2;
3311da177e4SLinus Torvalds 	}
33252253031SJan Kiszka 	showctl |= (ctr->traceflag & 1);
3331da177e4SLinus Torvalds 	if (showctl & 2) {
3341da177e4SLinus Torvalds 		if (showctl & 1) {
33517f0cd2fSKarsten Keil 			printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u\n",
33652253031SJan Kiszka 			       ctr->cnr, CAPIMSG_APPID(skb->data),
3371da177e4SLinus Torvalds 			       capi_cmd2str(cmd, subcmd),
3381da177e4SLinus Torvalds 			       CAPIMSG_LEN(skb->data));
3391da177e4SLinus Torvalds 		} else {
34017f0cd2fSKarsten Keil 			cdb = capi_message2str(skb->data);
34117f0cd2fSKarsten Keil 			if (cdb) {
34217f0cd2fSKarsten Keil 				printk(KERN_DEBUG "kcapi: got [%03d] %s\n",
34352253031SJan Kiszka 				       ctr->cnr, cdb->buf);
34417f0cd2fSKarsten Keil 				cdebbuf_free(cdb);
34517f0cd2fSKarsten Keil 			} else
34617f0cd2fSKarsten Keil 				printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u, cannot trace\n",
34752253031SJan Kiszka 				       ctr->cnr, CAPIMSG_APPID(skb->data),
34817f0cd2fSKarsten Keil 				       capi_cmd2str(cmd, subcmd),
34917f0cd2fSKarsten Keil 				       CAPIMSG_LEN(skb->data));
3501da177e4SLinus Torvalds 		}
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds 	}
3531da177e4SLinus Torvalds 
35488c896efSJan Kiszka 	rcu_read_lock();
3551da177e4SLinus Torvalds 	ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data));
35688c896efSJan Kiszka 	if (!ap) {
35788c896efSJan Kiszka 		rcu_read_unlock();
35817f0cd2fSKarsten Keil 		cdb = capi_message2str(skb->data);
35917f0cd2fSKarsten Keil 		if (cdb) {
3601da177e4SLinus Torvalds 			printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n",
36117f0cd2fSKarsten Keil 			       CAPIMSG_APPID(skb->data), cdb->buf);
36217f0cd2fSKarsten Keil 			cdebbuf_free(cdb);
36317f0cd2fSKarsten Keil 		} else
36417f0cd2fSKarsten Keil 			printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s) cannot trace\n",
36517f0cd2fSKarsten Keil 			       CAPIMSG_APPID(skb->data),
36617f0cd2fSKarsten Keil 			       capi_cmd2str(cmd, subcmd));
3671da177e4SLinus Torvalds 		goto error;
3681da177e4SLinus Torvalds 	}
3691da177e4SLinus Torvalds 	skb_queue_tail(&ap->recv_queue, skb);
370158fa677STejun Heo 	queue_work(kcapi_wq, &ap->recv_work);
37188c896efSJan Kiszka 	rcu_read_unlock();
3721da177e4SLinus Torvalds 
3731da177e4SLinus Torvalds 	return;
3741da177e4SLinus Torvalds 
3751da177e4SLinus Torvalds error:
3761da177e4SLinus Torvalds 	kfree_skb(skb);
3771da177e4SLinus Torvalds }
3781da177e4SLinus Torvalds 
3791da177e4SLinus Torvalds EXPORT_SYMBOL(capi_ctr_handle_message);
3801da177e4SLinus Torvalds 
381554f200eSTilman Schmidt /**
382554f200eSTilman Schmidt  * capi_ctr_ready() - signal CAPI controller ready
38352253031SJan Kiszka  * @ctr:	controller descriptor structure.
384554f200eSTilman Schmidt  *
385554f200eSTilman Schmidt  * Called by hardware driver to signal that the controller is up and running.
386554f200eSTilman Schmidt  */
387554f200eSTilman Schmidt 
capi_ctr_ready(struct capi_ctr * ctr)38852253031SJan Kiszka void capi_ctr_ready(struct capi_ctr *ctr)
3891da177e4SLinus Torvalds {
39052253031SJan Kiszka 	printk(KERN_NOTICE "kcapi: controller [%03d] \"%s\" ready.\n",
39152253031SJan Kiszka 	       ctr->cnr, ctr->name);
3921da177e4SLinus Torvalds 
393ef69bb2eSJan Kiszka 	notify_push(CAPICTR_UP, ctr->cnr);
3941da177e4SLinus Torvalds }
3951da177e4SLinus Torvalds 
3961da177e4SLinus Torvalds EXPORT_SYMBOL(capi_ctr_ready);
3971da177e4SLinus Torvalds 
398554f200eSTilman Schmidt /**
3994e329972STilman Schmidt  * capi_ctr_down() - signal CAPI controller not ready
40052253031SJan Kiszka  * @ctr:	controller descriptor structure.
401554f200eSTilman Schmidt  *
402554f200eSTilman Schmidt  * Called by hardware driver to signal that the controller is down and
403554f200eSTilman Schmidt  * unavailable for use.
404554f200eSTilman Schmidt  */
405554f200eSTilman Schmidt 
capi_ctr_down(struct capi_ctr * ctr)40652253031SJan Kiszka void capi_ctr_down(struct capi_ctr *ctr)
4071da177e4SLinus Torvalds {
40852253031SJan Kiszka 	printk(KERN_NOTICE "kcapi: controller [%03d] down.\n", ctr->cnr);
4091da177e4SLinus Torvalds 
410ef69bb2eSJan Kiszka 	notify_push(CAPICTR_DOWN, ctr->cnr);
4111da177e4SLinus Torvalds }
4121da177e4SLinus Torvalds 
4134e329972STilman Schmidt EXPORT_SYMBOL(capi_ctr_down);
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds /* ------------------------------------------------------------- */
4161da177e4SLinus Torvalds 
417554f200eSTilman Schmidt /**
418554f200eSTilman Schmidt  * attach_capi_ctr() - register CAPI controller
41952253031SJan Kiszka  * @ctr:	controller descriptor structure.
420554f200eSTilman Schmidt  *
421554f200eSTilman Schmidt  * Called by hardware driver to register a controller with the CAPI subsystem.
422554f200eSTilman Schmidt  * Return value: 0 on success, error code < 0 on error
423554f200eSTilman Schmidt  */
424554f200eSTilman Schmidt 
attach_capi_ctr(struct capi_ctr * ctr)42552253031SJan Kiszka int attach_capi_ctr(struct capi_ctr *ctr)
4261da177e4SLinus Torvalds {
4271da177e4SLinus Torvalds 	int i;
4281da177e4SLinus Torvalds 
4290ca3a017SJan Kiszka 	mutex_lock(&capi_controller_lock);
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds 	for (i = 0; i < CAPI_MAXCONTR; i++) {
43252253031SJan Kiszka 		if (!capi_controller[i])
4331da177e4SLinus Torvalds 			break;
4341da177e4SLinus Torvalds 	}
4351da177e4SLinus Torvalds 	if (i == CAPI_MAXCONTR) {
4360ca3a017SJan Kiszka 		mutex_unlock(&capi_controller_lock);
4371da177e4SLinus Torvalds 		printk(KERN_ERR "kcapi: out of controller slots\n");
4381da177e4SLinus Torvalds 		return -EBUSY;
4391da177e4SLinus Torvalds 	}
44052253031SJan Kiszka 	capi_controller[i] = ctr;
4411da177e4SLinus Torvalds 
44252253031SJan Kiszka 	ctr->nrecvctlpkt = 0;
44352253031SJan Kiszka 	ctr->nrecvdatapkt = 0;
44452253031SJan Kiszka 	ctr->nsentctlpkt = 0;
44552253031SJan Kiszka 	ctr->nsentdatapkt = 0;
44652253031SJan Kiszka 	ctr->cnr = i + 1;
44752253031SJan Kiszka 	ctr->state = CAPI_CTR_DETECTED;
44852253031SJan Kiszka 	ctr->blocked = 0;
44952253031SJan Kiszka 	ctr->traceflag = showcapimsgs;
4501da177e4SLinus Torvalds 
45152253031SJan Kiszka 	sprintf(ctr->procfn, "capi/controllers/%d", ctr->cnr);
4522cd1f0ddSChristoph Hellwig 	ctr->procent = proc_create_single_data(ctr->procfn, 0, NULL,
4532cd1f0ddSChristoph Hellwig 			ctr->proc_show, ctr);
4541da177e4SLinus Torvalds 
45552253031SJan Kiszka 	ncontrollers++;
4560ca3a017SJan Kiszka 
4570ca3a017SJan Kiszka 	mutex_unlock(&capi_controller_lock);
4580ca3a017SJan Kiszka 
45952253031SJan Kiszka 	printk(KERN_NOTICE "kcapi: controller [%03d]: %s attached\n",
46052253031SJan Kiszka 	       ctr->cnr, ctr->name);
4611da177e4SLinus Torvalds 	return 0;
4621da177e4SLinus Torvalds }
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds EXPORT_SYMBOL(attach_capi_ctr);
4651da177e4SLinus Torvalds 
466554f200eSTilman Schmidt /**
467554f200eSTilman Schmidt  * detach_capi_ctr() - unregister CAPI controller
46852253031SJan Kiszka  * @ctr:	controller descriptor structure.
469554f200eSTilman Schmidt  *
470554f200eSTilman Schmidt  * Called by hardware driver to remove the registration of a controller
471554f200eSTilman Schmidt  * with the CAPI subsystem.
472554f200eSTilman Schmidt  * Return value: 0 on success, error code < 0 on error
473554f200eSTilman Schmidt  */
474554f200eSTilman Schmidt 
detach_capi_ctr(struct capi_ctr * ctr)47552253031SJan Kiszka int detach_capi_ctr(struct capi_ctr *ctr)
4761da177e4SLinus Torvalds {
4770ca3a017SJan Kiszka 	int err = 0;
4781da177e4SLinus Torvalds 
4790ca3a017SJan Kiszka 	mutex_lock(&capi_controller_lock);
4801da177e4SLinus Torvalds 
4810ca3a017SJan Kiszka 	ctr_down(ctr, CAPI_CTR_DETACHED);
4820ca3a017SJan Kiszka 
4831f3e2e97SXiaolong Huang 	if (ctr->cnr < 1 || ctr->cnr - 1 >= CAPI_MAXCONTR) {
4841f3e2e97SXiaolong Huang 		err = -EINVAL;
4851f3e2e97SXiaolong Huang 		goto unlock_out;
4861f3e2e97SXiaolong Huang 	}
4871f3e2e97SXiaolong Huang 
4880ca3a017SJan Kiszka 	if (capi_controller[ctr->cnr - 1] != ctr) {
4890ca3a017SJan Kiszka 		err = -EINVAL;
4900ca3a017SJan Kiszka 		goto unlock_out;
4911da177e4SLinus Torvalds 	}
49252253031SJan Kiszka 	capi_controller[ctr->cnr - 1] = NULL;
4930ca3a017SJan Kiszka 	ncontrollers--;
4940ca3a017SJan Kiszka 
4950ca3a017SJan Kiszka 	if (ctr->procent)
4960ca3a017SJan Kiszka 		remove_proc_entry(ctr->procfn, NULL);
4970ca3a017SJan Kiszka 
49852253031SJan Kiszka 	printk(KERN_NOTICE "kcapi: controller [%03d]: %s unregistered\n",
49952253031SJan Kiszka 	       ctr->cnr, ctr->name);
5001da177e4SLinus Torvalds 
5010ca3a017SJan Kiszka unlock_out:
5020ca3a017SJan Kiszka 	mutex_unlock(&capi_controller_lock);
5030ca3a017SJan Kiszka 
5040ca3a017SJan Kiszka 	return err;
5051da177e4SLinus Torvalds }
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds EXPORT_SYMBOL(detach_capi_ctr);
5081da177e4SLinus Torvalds 
5091da177e4SLinus Torvalds /* ------------------------------------------------------------- */
5101da177e4SLinus Torvalds /* -------- CAPI2.0 Interface ---------------------------------- */
5111da177e4SLinus Torvalds /* ------------------------------------------------------------- */
5121da177e4SLinus Torvalds 
513554f200eSTilman Schmidt /**
514554f200eSTilman Schmidt  * capi20_isinstalled() - CAPI 2.0 operation CAPI_INSTALLED
515554f200eSTilman Schmidt  *
516554f200eSTilman Schmidt  * Return value: CAPI result code (CAPI_NOERROR if at least one ISDN controller
517554f200eSTilman Schmidt  *	is ready for use, CAPI_REGNOTINSTALLED otherwise)
518554f200eSTilman Schmidt  */
519554f200eSTilman Schmidt 
capi20_isinstalled(void)5201da177e4SLinus Torvalds u16 capi20_isinstalled(void)
5211da177e4SLinus Torvalds {
5220ca3a017SJan Kiszka 	u16 ret = CAPI_REGNOTINSTALLED;
5231da177e4SLinus Torvalds 	int i;
5240ca3a017SJan Kiszka 
5250ca3a017SJan Kiszka 	mutex_lock(&capi_controller_lock);
5260ca3a017SJan Kiszka 
5270ca3a017SJan Kiszka 	for (i = 0; i < CAPI_MAXCONTR; i++)
52852253031SJan Kiszka 		if (capi_controller[i] &&
5290ca3a017SJan Kiszka 		    capi_controller[i]->state == CAPI_CTR_RUNNING) {
5300ca3a017SJan Kiszka 			ret = CAPI_NOERROR;
5310ca3a017SJan Kiszka 			break;
5321da177e4SLinus Torvalds 		}
5330ca3a017SJan Kiszka 
5340ca3a017SJan Kiszka 	mutex_unlock(&capi_controller_lock);
5350ca3a017SJan Kiszka 
5360ca3a017SJan Kiszka 	return ret;
5371da177e4SLinus Torvalds }
5381da177e4SLinus Torvalds 
539554f200eSTilman Schmidt /**
540554f200eSTilman Schmidt  * capi20_register() - CAPI 2.0 operation CAPI_REGISTER
541554f200eSTilman Schmidt  * @ap:		CAPI application descriptor structure.
542554f200eSTilman Schmidt  *
543554f200eSTilman Schmidt  * Register an application's presence with CAPI.
544554f200eSTilman Schmidt  * A unique application ID is assigned and stored in @ap->applid.
545554f200eSTilman Schmidt  * After this function returns successfully, the message receive
546554f200eSTilman Schmidt  * callback function @ap->recv_message() may be called at any time
547554f200eSTilman Schmidt  * until capi20_release() has been called for the same @ap.
548554f200eSTilman Schmidt  * Return value: CAPI result code
549554f200eSTilman Schmidt  */
550554f200eSTilman Schmidt 
capi20_register(struct capi20_appl * ap)5511da177e4SLinus Torvalds u16 capi20_register(struct capi20_appl *ap)
5521da177e4SLinus Torvalds {
5531da177e4SLinus Torvalds 	int i;
5541da177e4SLinus Torvalds 	u16 applid;
5551da177e4SLinus Torvalds 
5561da177e4SLinus Torvalds 	DBG("");
5571da177e4SLinus Torvalds 
5581da177e4SLinus Torvalds 	if (ap->rparam.datablklen < 128)
5591da177e4SLinus Torvalds 		return CAPI_LOGBLKSIZETOSMALL;
5601da177e4SLinus Torvalds 
5611da177e4SLinus Torvalds 	ap->nrecvctlpkt = 0;
5621da177e4SLinus Torvalds 	ap->nrecvdatapkt = 0;
5631da177e4SLinus Torvalds 	ap->nsentctlpkt = 0;
5641da177e4SLinus Torvalds 	ap->nsentdatapkt = 0;
56567837f23SMatthias Kaehlcke 	mutex_init(&ap->recv_mtx);
5661da177e4SLinus Torvalds 	skb_queue_head_init(&ap->recv_queue);
567c4028958SDavid Howells 	INIT_WORK(&ap->recv_work, recv_handler);
5681da177e4SLinus Torvalds 	ap->release_in_progress = 0;
5691da177e4SLinus Torvalds 
5700ca3a017SJan Kiszka 	mutex_lock(&capi_controller_lock);
5710ca3a017SJan Kiszka 
57288c896efSJan Kiszka 	for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
57388c896efSJan Kiszka 		if (capi_applications[applid - 1] == NULL)
57488c896efSJan Kiszka 			break;
57588c896efSJan Kiszka 	}
57688c896efSJan Kiszka 	if (applid > CAPI_MAXAPPL) {
57788c896efSJan Kiszka 		mutex_unlock(&capi_controller_lock);
57888c896efSJan Kiszka 		return CAPI_TOOMANYAPPLS;
57988c896efSJan Kiszka 	}
58088c896efSJan Kiszka 
58188c896efSJan Kiszka 	ap->applid = applid;
58288c896efSJan Kiszka 	capi_applications[applid - 1] = ap;
58388c896efSJan Kiszka 
5841da177e4SLinus Torvalds 	for (i = 0; i < CAPI_MAXCONTR; i++) {
58552253031SJan Kiszka 		if (!capi_controller[i] ||
58652253031SJan Kiszka 		    capi_controller[i]->state != CAPI_CTR_RUNNING)
5871da177e4SLinus Torvalds 			continue;
58852253031SJan Kiszka 		register_appl(capi_controller[i], applid, &ap->rparam);
5891da177e4SLinus Torvalds 	}
5900ca3a017SJan Kiszka 
5910ca3a017SJan Kiszka 	mutex_unlock(&capi_controller_lock);
5921da177e4SLinus Torvalds 
5931da177e4SLinus Torvalds 	if (showcapimsgs & 1) {
5941da177e4SLinus Torvalds 		printk(KERN_DEBUG "kcapi: appl %d up\n", applid);
5951da177e4SLinus Torvalds 	}
5961da177e4SLinus Torvalds 
5971da177e4SLinus Torvalds 	return CAPI_NOERROR;
5981da177e4SLinus Torvalds }
5991da177e4SLinus Torvalds 
600554f200eSTilman Schmidt /**
601554f200eSTilman Schmidt  * capi20_release() - CAPI 2.0 operation CAPI_RELEASE
602554f200eSTilman Schmidt  * @ap:		CAPI application descriptor structure.
603554f200eSTilman Schmidt  *
604554f200eSTilman Schmidt  * Terminate an application's registration with CAPI.
605554f200eSTilman Schmidt  * After this function returns successfully, the message receive
606554f200eSTilman Schmidt  * callback function @ap->recv_message() will no longer be called.
607554f200eSTilman Schmidt  * Return value: CAPI result code
608554f200eSTilman Schmidt  */
609554f200eSTilman Schmidt 
capi20_release(struct capi20_appl * ap)6101da177e4SLinus Torvalds u16 capi20_release(struct capi20_appl *ap)
6111da177e4SLinus Torvalds {
6121da177e4SLinus Torvalds 	int i;
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds 	DBG("applid %#x", ap->applid);
6151da177e4SLinus Torvalds 
61688c896efSJan Kiszka 	mutex_lock(&capi_controller_lock);
61788c896efSJan Kiszka 
6181da177e4SLinus Torvalds 	ap->release_in_progress = 1;
6191da177e4SLinus Torvalds 	capi_applications[ap->applid - 1] = NULL;
6201da177e4SLinus Torvalds 
62188c896efSJan Kiszka 	synchronize_rcu();
6220ca3a017SJan Kiszka 
6231da177e4SLinus Torvalds 	for (i = 0; i < CAPI_MAXCONTR; i++) {
62452253031SJan Kiszka 		if (!capi_controller[i] ||
62552253031SJan Kiszka 		    capi_controller[i]->state != CAPI_CTR_RUNNING)
6261da177e4SLinus Torvalds 			continue;
62752253031SJan Kiszka 		release_appl(capi_controller[i], ap->applid);
6281da177e4SLinus Torvalds 	}
6290ca3a017SJan Kiszka 
6300ca3a017SJan Kiszka 	mutex_unlock(&capi_controller_lock);
6311da177e4SLinus Torvalds 
632158fa677STejun Heo 	flush_workqueue(kcapi_wq);
6331da177e4SLinus Torvalds 	skb_queue_purge(&ap->recv_queue);
6341da177e4SLinus Torvalds 
6351da177e4SLinus Torvalds 	if (showcapimsgs & 1) {
6361da177e4SLinus Torvalds 		printk(KERN_DEBUG "kcapi: appl %d down\n", ap->applid);
6371da177e4SLinus Torvalds 	}
6381da177e4SLinus Torvalds 
6391da177e4SLinus Torvalds 	return CAPI_NOERROR;
6401da177e4SLinus Torvalds }
6411da177e4SLinus Torvalds 
642554f200eSTilman Schmidt /**
643554f200eSTilman Schmidt  * capi20_put_message() - CAPI 2.0 operation CAPI_PUT_MESSAGE
644554f200eSTilman Schmidt  * @ap:		CAPI application descriptor structure.
645554f200eSTilman Schmidt  * @skb:	CAPI message.
646554f200eSTilman Schmidt  *
647554f200eSTilman Schmidt  * Transfer a single message to CAPI.
648554f200eSTilman Schmidt  * Return value: CAPI result code
649554f200eSTilman Schmidt  */
650554f200eSTilman Schmidt 
capi20_put_message(struct capi20_appl * ap,struct sk_buff * skb)6511da177e4SLinus Torvalds u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb)
6521da177e4SLinus Torvalds {
65352253031SJan Kiszka 	struct capi_ctr *ctr;
6541da177e4SLinus Torvalds 	int showctl = 0;
6551da177e4SLinus Torvalds 	u8 cmd, subcmd;
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds 	DBG("applid %#x", ap->applid);
6581da177e4SLinus Torvalds 
65952253031SJan Kiszka 	if (ncontrollers == 0)
6601da177e4SLinus Torvalds 		return CAPI_REGNOTINSTALLED;
6611da177e4SLinus Torvalds 	if ((ap->applid == 0) || ap->release_in_progress)
6621da177e4SLinus Torvalds 		return CAPI_ILLAPPNR;
6631da177e4SLinus Torvalds 	if (skb->len < 12
6641da177e4SLinus Torvalds 	    || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data))
6651da177e4SLinus Torvalds 	    || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data)))
6661da177e4SLinus Torvalds 		return CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
6670ca3a017SJan Kiszka 
6680ca3a017SJan Kiszka 	/*
6690ca3a017SJan Kiszka 	 * The controller reference is protected by the existence of the
6700ca3a017SJan Kiszka 	 * application passed to us. We assume that the caller properly
6710ca3a017SJan Kiszka 	 * synchronizes this service with capi20_release.
6720ca3a017SJan Kiszka 	 */
67352253031SJan Kiszka 	ctr = get_capi_ctr_by_nr(CAPIMSG_CONTROLLER(skb->data));
67452253031SJan Kiszka 	if (!ctr || ctr->state != CAPI_CTR_RUNNING)
6751da177e4SLinus Torvalds 		return CAPI_REGNOTINSTALLED;
67652253031SJan Kiszka 	if (ctr->blocked)
6771da177e4SLinus Torvalds 		return CAPI_SENDQUEUEFULL;
6781da177e4SLinus Torvalds 
6791da177e4SLinus Torvalds 	cmd = CAPIMSG_COMMAND(skb->data);
6801da177e4SLinus Torvalds 	subcmd = CAPIMSG_SUBCOMMAND(skb->data);
6811da177e4SLinus Torvalds 
6821da177e4SLinus Torvalds 	if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) {
68352253031SJan Kiszka 		ctr->nsentdatapkt++;
6841da177e4SLinus Torvalds 		ap->nsentdatapkt++;
68552253031SJan Kiszka 		if (ctr->traceflag > 2)
68652253031SJan Kiszka 			showctl |= 2;
6871da177e4SLinus Torvalds 	} else {
68852253031SJan Kiszka 		ctr->nsentctlpkt++;
6891da177e4SLinus Torvalds 		ap->nsentctlpkt++;
69052253031SJan Kiszka 		if (ctr->traceflag)
69152253031SJan Kiszka 			showctl |= 2;
6921da177e4SLinus Torvalds 	}
69352253031SJan Kiszka 	showctl |= (ctr->traceflag & 1);
6941da177e4SLinus Torvalds 	if (showctl & 2) {
6951da177e4SLinus Torvalds 		if (showctl & 1) {
69617f0cd2fSKarsten Keil 			printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u\n",
6971da177e4SLinus Torvalds 			       CAPIMSG_CONTROLLER(skb->data),
6981da177e4SLinus Torvalds 			       CAPIMSG_APPID(skb->data),
6991da177e4SLinus Torvalds 			       capi_cmd2str(cmd, subcmd),
7001da177e4SLinus Torvalds 			       CAPIMSG_LEN(skb->data));
7011da177e4SLinus Torvalds 		} else {
70217f0cd2fSKarsten Keil 			_cdebbuf *cdb = capi_message2str(skb->data);
70317f0cd2fSKarsten Keil 			if (cdb) {
70417f0cd2fSKarsten Keil 				printk(KERN_DEBUG "kcapi: put [%03d] %s\n",
7051da177e4SLinus Torvalds 				       CAPIMSG_CONTROLLER(skb->data),
70617f0cd2fSKarsten Keil 				       cdb->buf);
70717f0cd2fSKarsten Keil 				cdebbuf_free(cdb);
70817f0cd2fSKarsten Keil 			} else
70917f0cd2fSKarsten Keil 				printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u cannot trace\n",
71017f0cd2fSKarsten Keil 				       CAPIMSG_CONTROLLER(skb->data),
71117f0cd2fSKarsten Keil 				       CAPIMSG_APPID(skb->data),
71217f0cd2fSKarsten Keil 				       capi_cmd2str(cmd, subcmd),
71317f0cd2fSKarsten Keil 				       CAPIMSG_LEN(skb->data));
7141da177e4SLinus Torvalds 		}
7151da177e4SLinus Torvalds 	}
71652253031SJan Kiszka 	return ctr->send_message(ctr, skb);
7171da177e4SLinus Torvalds }
7181da177e4SLinus Torvalds 
719554f200eSTilman Schmidt /**
720554f200eSTilman Schmidt  * capi20_get_manufacturer() - CAPI 2.0 operation CAPI_GET_MANUFACTURER
721554f200eSTilman Schmidt  * @contr:	controller number.
722554f200eSTilman Schmidt  * @buf:	result buffer (64 bytes).
723554f200eSTilman Schmidt  *
724554f200eSTilman Schmidt  * Retrieve information about the manufacturer of the specified ISDN controller
725554f200eSTilman Schmidt  * or (for @contr == 0) the driver itself.
726554f200eSTilman Schmidt  * Return value: CAPI result code
727554f200eSTilman Schmidt  */
728554f200eSTilman Schmidt 
capi20_get_manufacturer(u32 contr,u8 buf[CAPI_MANUFACTURER_LEN])7295ee7d4c7SArnd Bergmann u16 capi20_get_manufacturer(u32 contr, u8 buf[CAPI_MANUFACTURER_LEN])
7301da177e4SLinus Torvalds {
73152253031SJan Kiszka 	struct capi_ctr *ctr;
7320ca3a017SJan Kiszka 	u16 ret;
7331da177e4SLinus Torvalds 
7341da177e4SLinus Torvalds 	if (contr == 0) {
735d63967e4SEric Dumazet 		strncpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN);
7361da177e4SLinus Torvalds 		return CAPI_NOERROR;
7371da177e4SLinus Torvalds 	}
7380ca3a017SJan Kiszka 
7390ca3a017SJan Kiszka 	mutex_lock(&capi_controller_lock);
7400ca3a017SJan Kiszka 
74152253031SJan Kiszka 	ctr = get_capi_ctr_by_nr(contr);
7420ca3a017SJan Kiszka 	if (ctr && ctr->state == CAPI_CTR_RUNNING) {
743d63967e4SEric Dumazet 		strncpy(buf, ctr->manu, CAPI_MANUFACTURER_LEN);
7440ca3a017SJan Kiszka 		ret = CAPI_NOERROR;
7450ca3a017SJan Kiszka 	} else
7460ca3a017SJan Kiszka 		ret = CAPI_REGNOTINSTALLED;
7470ca3a017SJan Kiszka 
7480ca3a017SJan Kiszka 	mutex_unlock(&capi_controller_lock);
7490ca3a017SJan Kiszka 	return ret;
7501da177e4SLinus Torvalds }
7511da177e4SLinus Torvalds 
752554f200eSTilman Schmidt /**
753554f200eSTilman Schmidt  * capi20_get_version() - CAPI 2.0 operation CAPI_GET_VERSION
754554f200eSTilman Schmidt  * @contr:	controller number.
755554f200eSTilman Schmidt  * @verp:	result structure.
756554f200eSTilman Schmidt  *
757554f200eSTilman Schmidt  * Retrieve version information for the specified ISDN controller
758554f200eSTilman Schmidt  * or (for @contr == 0) the driver itself.
759554f200eSTilman Schmidt  * Return value: CAPI result code
760554f200eSTilman Schmidt  */
761554f200eSTilman Schmidt 
capi20_get_version(u32 contr,struct capi_version * verp)7621da177e4SLinus Torvalds u16 capi20_get_version(u32 contr, struct capi_version *verp)
7631da177e4SLinus Torvalds {
76452253031SJan Kiszka 	struct capi_ctr *ctr;
7650ca3a017SJan Kiszka 	u16 ret;
7661da177e4SLinus Torvalds 
7671da177e4SLinus Torvalds 	if (contr == 0) {
7681da177e4SLinus Torvalds 		*verp = driver_version;
7691da177e4SLinus Torvalds 		return CAPI_NOERROR;
7701da177e4SLinus Torvalds 	}
7711da177e4SLinus Torvalds 
7720ca3a017SJan Kiszka 	mutex_lock(&capi_controller_lock);
7730ca3a017SJan Kiszka 
7740ca3a017SJan Kiszka 	ctr = get_capi_ctr_by_nr(contr);
7750ca3a017SJan Kiszka 	if (ctr && ctr->state == CAPI_CTR_RUNNING) {
77652253031SJan Kiszka 		memcpy(verp, &ctr->version, sizeof(capi_version));
7770ca3a017SJan Kiszka 		ret = CAPI_NOERROR;
7780ca3a017SJan Kiszka 	} else
7790ca3a017SJan Kiszka 		ret = CAPI_REGNOTINSTALLED;
7800ca3a017SJan Kiszka 
7810ca3a017SJan Kiszka 	mutex_unlock(&capi_controller_lock);
7820ca3a017SJan Kiszka 	return ret;
7831da177e4SLinus Torvalds }
7841da177e4SLinus Torvalds 
785554f200eSTilman Schmidt /**
786554f200eSTilman Schmidt  * capi20_get_serial() - CAPI 2.0 operation CAPI_GET_SERIAL_NUMBER
787554f200eSTilman Schmidt  * @contr:	controller number.
788554f200eSTilman Schmidt  * @serial:	result buffer (8 bytes).
789554f200eSTilman Schmidt  *
790554f200eSTilman Schmidt  * Retrieve the serial number of the specified ISDN controller
791554f200eSTilman Schmidt  * or (for @contr == 0) the driver itself.
792554f200eSTilman Schmidt  * Return value: CAPI result code
793554f200eSTilman Schmidt  */
794554f200eSTilman Schmidt 
capi20_get_serial(u32 contr,u8 serial[CAPI_SERIAL_LEN])7955ee7d4c7SArnd Bergmann u16 capi20_get_serial(u32 contr, u8 serial[CAPI_SERIAL_LEN])
7961da177e4SLinus Torvalds {
79752253031SJan Kiszka 	struct capi_ctr *ctr;
7980ca3a017SJan Kiszka 	u16 ret;
7991da177e4SLinus Torvalds 
8001da177e4SLinus Torvalds 	if (contr == 0) {
801*cdb27b7bSWolfram Sang 		strscpy(serial, driver_serial, CAPI_SERIAL_LEN);
8021da177e4SLinus Torvalds 		return CAPI_NOERROR;
8031da177e4SLinus Torvalds 	}
8041da177e4SLinus Torvalds 
8050ca3a017SJan Kiszka 	mutex_lock(&capi_controller_lock);
8060ca3a017SJan Kiszka 
8070ca3a017SJan Kiszka 	ctr = get_capi_ctr_by_nr(contr);
8080ca3a017SJan Kiszka 	if (ctr && ctr->state == CAPI_CTR_RUNNING) {
809*cdb27b7bSWolfram Sang 		strscpy(serial, ctr->serial, CAPI_SERIAL_LEN);
8100ca3a017SJan Kiszka 		ret = CAPI_NOERROR;
8110ca3a017SJan Kiszka 	} else
8120ca3a017SJan Kiszka 		ret = CAPI_REGNOTINSTALLED;
8130ca3a017SJan Kiszka 
8140ca3a017SJan Kiszka 	mutex_unlock(&capi_controller_lock);
8150ca3a017SJan Kiszka 	return ret;
8161da177e4SLinus Torvalds }
8171da177e4SLinus Torvalds 
818554f200eSTilman Schmidt /**
819554f200eSTilman Schmidt  * capi20_get_profile() - CAPI 2.0 operation CAPI_GET_PROFILE
820554f200eSTilman Schmidt  * @contr:	controller number.
821554f200eSTilman Schmidt  * @profp:	result structure.
822554f200eSTilman Schmidt  *
823554f200eSTilman Schmidt  * Retrieve capability information for the specified ISDN controller
824554f200eSTilman Schmidt  * or (for @contr == 0) the number of installed controllers.
825554f200eSTilman Schmidt  * Return value: CAPI result code
826554f200eSTilman Schmidt  */
827554f200eSTilman Schmidt 
capi20_get_profile(u32 contr,struct capi_profile * profp)8281da177e4SLinus Torvalds u16 capi20_get_profile(u32 contr, struct capi_profile *profp)
8291da177e4SLinus Torvalds {
83052253031SJan Kiszka 	struct capi_ctr *ctr;
8310ca3a017SJan Kiszka 	u16 ret;
8321da177e4SLinus Torvalds 
8331da177e4SLinus Torvalds 	if (contr == 0) {
83452253031SJan Kiszka 		profp->ncontroller = ncontrollers;
8351da177e4SLinus Torvalds 		return CAPI_NOERROR;
8361da177e4SLinus Torvalds 	}
8371da177e4SLinus Torvalds 
8380ca3a017SJan Kiszka 	mutex_lock(&capi_controller_lock);
8390ca3a017SJan Kiszka 
8400ca3a017SJan Kiszka 	ctr = get_capi_ctr_by_nr(contr);
8410ca3a017SJan Kiszka 	if (ctr && ctr->state == CAPI_CTR_RUNNING) {
84252253031SJan Kiszka 		memcpy(profp, &ctr->profile, sizeof(struct capi_profile));
8430ca3a017SJan Kiszka 		ret = CAPI_NOERROR;
8440ca3a017SJan Kiszka 	} else
8450ca3a017SJan Kiszka 		ret = CAPI_REGNOTINSTALLED;
8460ca3a017SJan Kiszka 
8470ca3a017SJan Kiszka 	mutex_unlock(&capi_controller_lock);
8480ca3a017SJan Kiszka 	return ret;
8491da177e4SLinus Torvalds }
8501da177e4SLinus Torvalds 
851554f200eSTilman Schmidt /**
852554f200eSTilman Schmidt  * capi20_manufacturer() - CAPI 2.0 operation CAPI_MANUFACTURER
853554f200eSTilman Schmidt  * @cmd:	command.
854554f200eSTilman Schmidt  * @data:	parameter.
855554f200eSTilman Schmidt  *
856554f200eSTilman Schmidt  * Perform manufacturer specific command.
857554f200eSTilman Schmidt  * Return value: CAPI result code
858554f200eSTilman Schmidt  */
859554f200eSTilman Schmidt 
capi20_manufacturer(unsigned long cmd,void __user * data)8609ea8aa8dSTilman Schmidt int capi20_manufacturer(unsigned long cmd, void __user *data)
8611da177e4SLinus Torvalds {
86252253031SJan Kiszka 	struct capi_ctr *ctr;
8630ca3a017SJan Kiszka 	int retval;
8641da177e4SLinus Torvalds 
8651da177e4SLinus Torvalds 	switch (cmd) {
8661da177e4SLinus Torvalds 	case KCAPI_CMD_TRACE:
8671da177e4SLinus Torvalds 	{
8681da177e4SLinus Torvalds 		kcapi_flagdef fdef;
8691da177e4SLinus Torvalds 
8701da177e4SLinus Torvalds 		if (copy_from_user(&fdef, data, sizeof(kcapi_flagdef)))
8711da177e4SLinus Torvalds 			return -EFAULT;
8721da177e4SLinus Torvalds 
8730ca3a017SJan Kiszka 		mutex_lock(&capi_controller_lock);
8741da177e4SLinus Torvalds 
8750ca3a017SJan Kiszka 		ctr = get_capi_ctr_by_nr(fdef.contr);
8760ca3a017SJan Kiszka 		if (ctr) {
87752253031SJan Kiszka 			ctr->traceflag = fdef.flag;
87817f0cd2fSKarsten Keil 			printk(KERN_INFO "kcapi: contr [%03d] set trace=%d\n",
87952253031SJan Kiszka 			       ctr->cnr, ctr->traceflag);
8800ca3a017SJan Kiszka 			retval = 0;
8810ca3a017SJan Kiszka 		} else
8820ca3a017SJan Kiszka 			retval = -ESRCH;
8830ca3a017SJan Kiszka 
8840ca3a017SJan Kiszka 		mutex_unlock(&capi_controller_lock);
8850ca3a017SJan Kiszka 
8860ca3a017SJan Kiszka 		return retval;
8871da177e4SLinus Torvalds 	}
8881da177e4SLinus Torvalds 
8891da177e4SLinus Torvalds 	default:
8909ea8aa8dSTilman Schmidt 		printk(KERN_ERR "kcapi: manufacturer command %lu unknown.\n",
8911da177e4SLinus Torvalds 		       cmd);
8921da177e4SLinus Torvalds 		break;
8931da177e4SLinus Torvalds 
8941da177e4SLinus Torvalds 	}
8951da177e4SLinus Torvalds 	return -EINVAL;
8961da177e4SLinus Torvalds }
8971da177e4SLinus Torvalds 
8981da177e4SLinus Torvalds /* ------------------------------------------------------------- */
8991da177e4SLinus Torvalds /* -------- Init & Cleanup ------------------------------------- */
9001da177e4SLinus Torvalds /* ------------------------------------------------------------- */
9011da177e4SLinus Torvalds 
9021da177e4SLinus Torvalds /*
9031da177e4SLinus Torvalds  * init / exit functions
9041da177e4SLinus Torvalds  */
9051da177e4SLinus Torvalds 
kcapi_init(void)906f59aba2fSArnd Bergmann int __init kcapi_init(void)
9071da177e4SLinus Torvalds {
90888549d6bSJan Kiszka 	int err;
9091da177e4SLinus Torvalds 
910158fa677STejun Heo 	kcapi_wq = alloc_workqueue("kcapi", 0, 0);
911158fa677STejun Heo 	if (!kcapi_wq)
912158fa677STejun Heo 		return -ENOMEM;
913158fa677STejun Heo 
91488549d6bSJan Kiszka 	err = cdebug_init();
915158fa677STejun Heo 	if (err) {
916158fa677STejun Heo 		destroy_workqueue(kcapi_wq);
91788549d6bSJan Kiszka 		return err;
9181da177e4SLinus Torvalds 	}
9191da177e4SLinus Torvalds 
920158fa677STejun Heo 	kcapi_proc_init();
921158fa677STejun Heo 	return 0;
922158fa677STejun Heo }
923158fa677STejun Heo 
kcapi_exit(void)924f59aba2fSArnd Bergmann void kcapi_exit(void)
9251da177e4SLinus Torvalds {
9261da177e4SLinus Torvalds 	kcapi_proc_exit();
9271da177e4SLinus Torvalds 
92817f0cd2fSKarsten Keil 	cdebug_exit();
929158fa677STejun Heo 	destroy_workqueue(kcapi_wq);
9301da177e4SLinus Torvalds }
931