162306a36Sopenharmony_ci/* $Id: kcapi.c,v 1.1.2.8 2004/03/26 19:57:20 armin Exp $ 262306a36Sopenharmony_ci * 362306a36Sopenharmony_ci * Kernel CAPI 2.0 Module 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 1999 by Carsten Paeth <calle@calle.de> 662306a36Sopenharmony_ci * Copyright 2002 by Kai Germaschewski <kai@germaschewski.name> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This software may be used and distributed according to the terms 962306a36Sopenharmony_ci * of the GNU General Public License, incorporated herein by reference. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "kcapi.h" 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/mm.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/ioport.h> 1862306a36Sopenharmony_ci#include <linux/proc_fs.h> 1962306a36Sopenharmony_ci#include <linux/sched/signal.h> 2062306a36Sopenharmony_ci#include <linux/seq_file.h> 2162306a36Sopenharmony_ci#include <linux/skbuff.h> 2262306a36Sopenharmony_ci#include <linux/workqueue.h> 2362306a36Sopenharmony_ci#include <linux/capi.h> 2462306a36Sopenharmony_ci#include <linux/kernelcapi.h> 2562306a36Sopenharmony_ci#include <linux/init.h> 2662306a36Sopenharmony_ci#include <linux/moduleparam.h> 2762306a36Sopenharmony_ci#include <linux/delay.h> 2862306a36Sopenharmony_ci#include <linux/slab.h> 2962306a36Sopenharmony_ci#include <linux/uaccess.h> 3062306a36Sopenharmony_ci#include <linux/isdn/capicmd.h> 3162306a36Sopenharmony_ci#include <linux/isdn/capiutil.h> 3262306a36Sopenharmony_ci#include <linux/mutex.h> 3362306a36Sopenharmony_ci#include <linux/rcupdate.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int showcapimsgs; 3662306a36Sopenharmony_cistatic struct workqueue_struct *kcapi_wq; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cimodule_param(showcapimsgs, uint, 0); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* ------------------------------------------------------------- */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistruct capictr_event { 4362306a36Sopenharmony_ci struct work_struct work; 4462306a36Sopenharmony_ci unsigned int type; 4562306a36Sopenharmony_ci u32 controller; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* ------------------------------------------------------------- */ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic const struct capi_version driver_version = {2, 0, 1, 1 << 4}; 5162306a36Sopenharmony_cistatic char driver_serial[CAPI_SERIAL_LEN] = "0004711"; 5262306a36Sopenharmony_cistatic char capi_manufakturer[64] = "AVM Berlin"; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistruct capi_ctr *capi_controller[CAPI_MAXCONTR]; 5762306a36Sopenharmony_ciDEFINE_MUTEX(capi_controller_lock); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct capi20_appl *capi_applications[CAPI_MAXAPPL]; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int ncontrollers; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* -------- controller ref counting -------------------------------------- */ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic inline struct capi_ctr * 6662306a36Sopenharmony_cicapi_ctr_get(struct capi_ctr *ctr) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci if (!try_module_get(ctr->owner)) 6962306a36Sopenharmony_ci return NULL; 7062306a36Sopenharmony_ci return ctr; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic inline void 7462306a36Sopenharmony_cicapi_ctr_put(struct capi_ctr *ctr) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci module_put(ctr->owner); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* ------------------------------------------------------------- */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic inline struct capi_ctr *get_capi_ctr_by_nr(u16 contr) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci if (contr < 1 || contr - 1 >= CAPI_MAXCONTR) 8462306a36Sopenharmony_ci return NULL; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return capi_controller[contr - 1]; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic inline struct capi20_appl *__get_capi_appl_by_nr(u16 applid) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci lockdep_assert_held(&capi_controller_lock); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (applid < 1 || applid - 1 >= CAPI_MAXAPPL) 9462306a36Sopenharmony_ci return NULL; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return capi_applications[applid - 1]; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic inline struct capi20_appl *get_capi_appl_by_nr(u16 applid) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci if (applid < 1 || applid - 1 >= CAPI_MAXAPPL) 10262306a36Sopenharmony_ci return NULL; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return rcu_dereference(capi_applications[applid - 1]); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* -------- util functions ------------------------------------ */ 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic inline int capi_cmd_valid(u8 cmd) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci switch (cmd) { 11262306a36Sopenharmony_ci case CAPI_ALERT: 11362306a36Sopenharmony_ci case CAPI_CONNECT: 11462306a36Sopenharmony_ci case CAPI_CONNECT_ACTIVE: 11562306a36Sopenharmony_ci case CAPI_CONNECT_B3_ACTIVE: 11662306a36Sopenharmony_ci case CAPI_CONNECT_B3: 11762306a36Sopenharmony_ci case CAPI_CONNECT_B3_T90_ACTIVE: 11862306a36Sopenharmony_ci case CAPI_DATA_B3: 11962306a36Sopenharmony_ci case CAPI_DISCONNECT_B3: 12062306a36Sopenharmony_ci case CAPI_DISCONNECT: 12162306a36Sopenharmony_ci case CAPI_FACILITY: 12262306a36Sopenharmony_ci case CAPI_INFO: 12362306a36Sopenharmony_ci case CAPI_LISTEN: 12462306a36Sopenharmony_ci case CAPI_MANUFACTURER: 12562306a36Sopenharmony_ci case CAPI_RESET_B3: 12662306a36Sopenharmony_ci case CAPI_SELECT_B_PROTOCOL: 12762306a36Sopenharmony_ci return 1; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic inline int capi_subcmd_valid(u8 subcmd) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci switch (subcmd) { 13562306a36Sopenharmony_ci case CAPI_REQ: 13662306a36Sopenharmony_ci case CAPI_CONF: 13762306a36Sopenharmony_ci case CAPI_IND: 13862306a36Sopenharmony_ci case CAPI_RESP: 13962306a36Sopenharmony_ci return 1; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* ------------------------------------------------------------ */ 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic void 14762306a36Sopenharmony_ciregister_appl(struct capi_ctr *ctr, u16 applid, capi_register_params *rparam) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci ctr = capi_ctr_get(ctr); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (ctr) 15262306a36Sopenharmony_ci ctr->register_appl(ctr, applid, rparam); 15362306a36Sopenharmony_ci else 15462306a36Sopenharmony_ci printk(KERN_WARNING "%s: cannot get controller resources\n", 15562306a36Sopenharmony_ci __func__); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void release_appl(struct capi_ctr *ctr, u16 applid) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci DBG("applid %#x", applid); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ctr->release_appl(ctr, applid); 16462306a36Sopenharmony_ci capi_ctr_put(ctr); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic void notify_up(u32 contr) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct capi20_appl *ap; 17062306a36Sopenharmony_ci struct capi_ctr *ctr; 17162306a36Sopenharmony_ci u16 applid; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci mutex_lock(&capi_controller_lock); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (showcapimsgs & 1) 17662306a36Sopenharmony_ci printk(KERN_DEBUG "kcapi: notify up contr %d\n", contr); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ctr = get_capi_ctr_by_nr(contr); 17962306a36Sopenharmony_ci if (ctr) { 18062306a36Sopenharmony_ci if (ctr->state == CAPI_CTR_RUNNING) 18162306a36Sopenharmony_ci goto unlock_out; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci ctr->state = CAPI_CTR_RUNNING; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { 18662306a36Sopenharmony_ci ap = __get_capi_appl_by_nr(applid); 18762306a36Sopenharmony_ci if (ap) 18862306a36Sopenharmony_ci register_appl(ctr, applid, &ap->rparam); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci } else 19162306a36Sopenharmony_ci printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ciunlock_out: 19462306a36Sopenharmony_ci mutex_unlock(&capi_controller_lock); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic void ctr_down(struct capi_ctr *ctr, int new_state) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct capi20_appl *ap; 20062306a36Sopenharmony_ci u16 applid; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (ctr->state == CAPI_CTR_DETECTED || ctr->state == CAPI_CTR_DETACHED) 20362306a36Sopenharmony_ci return; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci ctr->state = new_state; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci memset(ctr->manu, 0, sizeof(ctr->manu)); 20862306a36Sopenharmony_ci memset(&ctr->version, 0, sizeof(ctr->version)); 20962306a36Sopenharmony_ci memset(&ctr->profile, 0, sizeof(ctr->profile)); 21062306a36Sopenharmony_ci memset(ctr->serial, 0, sizeof(ctr->serial)); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { 21362306a36Sopenharmony_ci ap = __get_capi_appl_by_nr(applid); 21462306a36Sopenharmony_ci if (ap) 21562306a36Sopenharmony_ci capi_ctr_put(ctr); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void notify_down(u32 contr) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct capi_ctr *ctr; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci mutex_lock(&capi_controller_lock); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (showcapimsgs & 1) 22662306a36Sopenharmony_ci printk(KERN_DEBUG "kcapi: notify down contr %d\n", contr); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci ctr = get_capi_ctr_by_nr(contr); 22962306a36Sopenharmony_ci if (ctr) 23062306a36Sopenharmony_ci ctr_down(ctr, CAPI_CTR_DETECTED); 23162306a36Sopenharmony_ci else 23262306a36Sopenharmony_ci printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci mutex_unlock(&capi_controller_lock); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void do_notify_work(struct work_struct *work) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct capictr_event *event = 24062306a36Sopenharmony_ci container_of(work, struct capictr_event, work); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci switch (event->type) { 24362306a36Sopenharmony_ci case CAPICTR_UP: 24462306a36Sopenharmony_ci notify_up(event->controller); 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci case CAPICTR_DOWN: 24762306a36Sopenharmony_ci notify_down(event->controller); 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci kfree(event); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int notify_push(unsigned int event_type, u32 controller) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct capictr_event *event = kmalloc(sizeof(*event), GFP_ATOMIC); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (!event) 25962306a36Sopenharmony_ci return -ENOMEM; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci INIT_WORK(&event->work, do_notify_work); 26262306a36Sopenharmony_ci event->type = event_type; 26362306a36Sopenharmony_ci event->controller = controller; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci queue_work(kcapi_wq, &event->work); 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* -------- Receiver ------------------------------------------ */ 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic void recv_handler(struct work_struct *work) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct sk_buff *skb; 27462306a36Sopenharmony_ci struct capi20_appl *ap = 27562306a36Sopenharmony_ci container_of(work, struct capi20_appl, recv_work); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if ((!ap) || (ap->release_in_progress)) 27862306a36Sopenharmony_ci return; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci mutex_lock(&ap->recv_mtx); 28162306a36Sopenharmony_ci while ((skb = skb_dequeue(&ap->recv_queue))) { 28262306a36Sopenharmony_ci if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_IND) 28362306a36Sopenharmony_ci ap->nrecvdatapkt++; 28462306a36Sopenharmony_ci else 28562306a36Sopenharmony_ci ap->nrecvctlpkt++; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci ap->recv_message(ap, skb); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci mutex_unlock(&ap->recv_mtx); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci/** 29362306a36Sopenharmony_ci * capi_ctr_handle_message() - handle incoming CAPI message 29462306a36Sopenharmony_ci * @ctr: controller descriptor structure. 29562306a36Sopenharmony_ci * @appl: application ID. 29662306a36Sopenharmony_ci * @skb: message. 29762306a36Sopenharmony_ci * 29862306a36Sopenharmony_ci * Called by hardware driver to pass a CAPI message to the application. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_civoid capi_ctr_handle_message(struct capi_ctr *ctr, u16 appl, 30262306a36Sopenharmony_ci struct sk_buff *skb) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct capi20_appl *ap; 30562306a36Sopenharmony_ci int showctl = 0; 30662306a36Sopenharmony_ci u8 cmd, subcmd; 30762306a36Sopenharmony_ci _cdebbuf *cdb; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (ctr->state != CAPI_CTR_RUNNING) { 31062306a36Sopenharmony_ci cdb = capi_message2str(skb->data); 31162306a36Sopenharmony_ci if (cdb) { 31262306a36Sopenharmony_ci printk(KERN_INFO "kcapi: controller [%03d] not active, got: %s", 31362306a36Sopenharmony_ci ctr->cnr, cdb->buf); 31462306a36Sopenharmony_ci cdebbuf_free(cdb); 31562306a36Sopenharmony_ci } else 31662306a36Sopenharmony_ci printk(KERN_INFO "kcapi: controller [%03d] not active, cannot trace\n", 31762306a36Sopenharmony_ci ctr->cnr); 31862306a36Sopenharmony_ci goto error; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci cmd = CAPIMSG_COMMAND(skb->data); 32262306a36Sopenharmony_ci subcmd = CAPIMSG_SUBCOMMAND(skb->data); 32362306a36Sopenharmony_ci if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) { 32462306a36Sopenharmony_ci ctr->nrecvdatapkt++; 32562306a36Sopenharmony_ci if (ctr->traceflag > 2) 32662306a36Sopenharmony_ci showctl |= 2; 32762306a36Sopenharmony_ci } else { 32862306a36Sopenharmony_ci ctr->nrecvctlpkt++; 32962306a36Sopenharmony_ci if (ctr->traceflag) 33062306a36Sopenharmony_ci showctl |= 2; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci showctl |= (ctr->traceflag & 1); 33362306a36Sopenharmony_ci if (showctl & 2) { 33462306a36Sopenharmony_ci if (showctl & 1) { 33562306a36Sopenharmony_ci printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u\n", 33662306a36Sopenharmony_ci ctr->cnr, CAPIMSG_APPID(skb->data), 33762306a36Sopenharmony_ci capi_cmd2str(cmd, subcmd), 33862306a36Sopenharmony_ci CAPIMSG_LEN(skb->data)); 33962306a36Sopenharmony_ci } else { 34062306a36Sopenharmony_ci cdb = capi_message2str(skb->data); 34162306a36Sopenharmony_ci if (cdb) { 34262306a36Sopenharmony_ci printk(KERN_DEBUG "kcapi: got [%03d] %s\n", 34362306a36Sopenharmony_ci ctr->cnr, cdb->buf); 34462306a36Sopenharmony_ci cdebbuf_free(cdb); 34562306a36Sopenharmony_ci } else 34662306a36Sopenharmony_ci printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u, cannot trace\n", 34762306a36Sopenharmony_ci ctr->cnr, CAPIMSG_APPID(skb->data), 34862306a36Sopenharmony_ci capi_cmd2str(cmd, subcmd), 34962306a36Sopenharmony_ci CAPIMSG_LEN(skb->data)); 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci rcu_read_lock(); 35562306a36Sopenharmony_ci ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data)); 35662306a36Sopenharmony_ci if (!ap) { 35762306a36Sopenharmony_ci rcu_read_unlock(); 35862306a36Sopenharmony_ci cdb = capi_message2str(skb->data); 35962306a36Sopenharmony_ci if (cdb) { 36062306a36Sopenharmony_ci printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n", 36162306a36Sopenharmony_ci CAPIMSG_APPID(skb->data), cdb->buf); 36262306a36Sopenharmony_ci cdebbuf_free(cdb); 36362306a36Sopenharmony_ci } else 36462306a36Sopenharmony_ci printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s) cannot trace\n", 36562306a36Sopenharmony_ci CAPIMSG_APPID(skb->data), 36662306a36Sopenharmony_ci capi_cmd2str(cmd, subcmd)); 36762306a36Sopenharmony_ci goto error; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci skb_queue_tail(&ap->recv_queue, skb); 37062306a36Sopenharmony_ci queue_work(kcapi_wq, &ap->recv_work); 37162306a36Sopenharmony_ci rcu_read_unlock(); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cierror: 37662306a36Sopenharmony_ci kfree_skb(skb); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ciEXPORT_SYMBOL(capi_ctr_handle_message); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci/** 38262306a36Sopenharmony_ci * capi_ctr_ready() - signal CAPI controller ready 38362306a36Sopenharmony_ci * @ctr: controller descriptor structure. 38462306a36Sopenharmony_ci * 38562306a36Sopenharmony_ci * Called by hardware driver to signal that the controller is up and running. 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_civoid capi_ctr_ready(struct capi_ctr *ctr) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci printk(KERN_NOTICE "kcapi: controller [%03d] \"%s\" ready.\n", 39162306a36Sopenharmony_ci ctr->cnr, ctr->name); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci notify_push(CAPICTR_UP, ctr->cnr); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ciEXPORT_SYMBOL(capi_ctr_ready); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci/** 39962306a36Sopenharmony_ci * capi_ctr_down() - signal CAPI controller not ready 40062306a36Sopenharmony_ci * @ctr: controller descriptor structure. 40162306a36Sopenharmony_ci * 40262306a36Sopenharmony_ci * Called by hardware driver to signal that the controller is down and 40362306a36Sopenharmony_ci * unavailable for use. 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_civoid capi_ctr_down(struct capi_ctr *ctr) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci printk(KERN_NOTICE "kcapi: controller [%03d] down.\n", ctr->cnr); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci notify_push(CAPICTR_DOWN, ctr->cnr); 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ciEXPORT_SYMBOL(capi_ctr_down); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci/* ------------------------------------------------------------- */ 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci/** 41862306a36Sopenharmony_ci * attach_capi_ctr() - register CAPI controller 41962306a36Sopenharmony_ci * @ctr: controller descriptor structure. 42062306a36Sopenharmony_ci * 42162306a36Sopenharmony_ci * Called by hardware driver to register a controller with the CAPI subsystem. 42262306a36Sopenharmony_ci * Return value: 0 on success, error code < 0 on error 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ciint attach_capi_ctr(struct capi_ctr *ctr) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci int i; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci mutex_lock(&capi_controller_lock); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci for (i = 0; i < CAPI_MAXCONTR; i++) { 43262306a36Sopenharmony_ci if (!capi_controller[i]) 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci if (i == CAPI_MAXCONTR) { 43662306a36Sopenharmony_ci mutex_unlock(&capi_controller_lock); 43762306a36Sopenharmony_ci printk(KERN_ERR "kcapi: out of controller slots\n"); 43862306a36Sopenharmony_ci return -EBUSY; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci capi_controller[i] = ctr; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci ctr->nrecvctlpkt = 0; 44362306a36Sopenharmony_ci ctr->nrecvdatapkt = 0; 44462306a36Sopenharmony_ci ctr->nsentctlpkt = 0; 44562306a36Sopenharmony_ci ctr->nsentdatapkt = 0; 44662306a36Sopenharmony_ci ctr->cnr = i + 1; 44762306a36Sopenharmony_ci ctr->state = CAPI_CTR_DETECTED; 44862306a36Sopenharmony_ci ctr->blocked = 0; 44962306a36Sopenharmony_ci ctr->traceflag = showcapimsgs; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci sprintf(ctr->procfn, "capi/controllers/%d", ctr->cnr); 45262306a36Sopenharmony_ci ctr->procent = proc_create_single_data(ctr->procfn, 0, NULL, 45362306a36Sopenharmony_ci ctr->proc_show, ctr); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci ncontrollers++; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci mutex_unlock(&capi_controller_lock); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci printk(KERN_NOTICE "kcapi: controller [%03d]: %s attached\n", 46062306a36Sopenharmony_ci ctr->cnr, ctr->name); 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ciEXPORT_SYMBOL(attach_capi_ctr); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/** 46762306a36Sopenharmony_ci * detach_capi_ctr() - unregister CAPI controller 46862306a36Sopenharmony_ci * @ctr: controller descriptor structure. 46962306a36Sopenharmony_ci * 47062306a36Sopenharmony_ci * Called by hardware driver to remove the registration of a controller 47162306a36Sopenharmony_ci * with the CAPI subsystem. 47262306a36Sopenharmony_ci * Return value: 0 on success, error code < 0 on error 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ciint detach_capi_ctr(struct capi_ctr *ctr) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci int err = 0; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci mutex_lock(&capi_controller_lock); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci ctr_down(ctr, CAPI_CTR_DETACHED); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (ctr->cnr < 1 || ctr->cnr - 1 >= CAPI_MAXCONTR) { 48462306a36Sopenharmony_ci err = -EINVAL; 48562306a36Sopenharmony_ci goto unlock_out; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (capi_controller[ctr->cnr - 1] != ctr) { 48962306a36Sopenharmony_ci err = -EINVAL; 49062306a36Sopenharmony_ci goto unlock_out; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci capi_controller[ctr->cnr - 1] = NULL; 49362306a36Sopenharmony_ci ncontrollers--; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (ctr->procent) 49662306a36Sopenharmony_ci remove_proc_entry(ctr->procfn, NULL); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci printk(KERN_NOTICE "kcapi: controller [%03d]: %s unregistered\n", 49962306a36Sopenharmony_ci ctr->cnr, ctr->name); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ciunlock_out: 50262306a36Sopenharmony_ci mutex_unlock(&capi_controller_lock); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci return err; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ciEXPORT_SYMBOL(detach_capi_ctr); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci/* ------------------------------------------------------------- */ 51062306a36Sopenharmony_ci/* -------- CAPI2.0 Interface ---------------------------------- */ 51162306a36Sopenharmony_ci/* ------------------------------------------------------------- */ 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci/** 51462306a36Sopenharmony_ci * capi20_isinstalled() - CAPI 2.0 operation CAPI_INSTALLED 51562306a36Sopenharmony_ci * 51662306a36Sopenharmony_ci * Return value: CAPI result code (CAPI_NOERROR if at least one ISDN controller 51762306a36Sopenharmony_ci * is ready for use, CAPI_REGNOTINSTALLED otherwise) 51862306a36Sopenharmony_ci */ 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ciu16 capi20_isinstalled(void) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci u16 ret = CAPI_REGNOTINSTALLED; 52362306a36Sopenharmony_ci int i; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci mutex_lock(&capi_controller_lock); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci for (i = 0; i < CAPI_MAXCONTR; i++) 52862306a36Sopenharmony_ci if (capi_controller[i] && 52962306a36Sopenharmony_ci capi_controller[i]->state == CAPI_CTR_RUNNING) { 53062306a36Sopenharmony_ci ret = CAPI_NOERROR; 53162306a36Sopenharmony_ci break; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci mutex_unlock(&capi_controller_lock); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return ret; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci/** 54062306a36Sopenharmony_ci * capi20_register() - CAPI 2.0 operation CAPI_REGISTER 54162306a36Sopenharmony_ci * @ap: CAPI application descriptor structure. 54262306a36Sopenharmony_ci * 54362306a36Sopenharmony_ci * Register an application's presence with CAPI. 54462306a36Sopenharmony_ci * A unique application ID is assigned and stored in @ap->applid. 54562306a36Sopenharmony_ci * After this function returns successfully, the message receive 54662306a36Sopenharmony_ci * callback function @ap->recv_message() may be called at any time 54762306a36Sopenharmony_ci * until capi20_release() has been called for the same @ap. 54862306a36Sopenharmony_ci * Return value: CAPI result code 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ciu16 capi20_register(struct capi20_appl *ap) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci int i; 55462306a36Sopenharmony_ci u16 applid; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci DBG(""); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (ap->rparam.datablklen < 128) 55962306a36Sopenharmony_ci return CAPI_LOGBLKSIZETOSMALL; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci ap->nrecvctlpkt = 0; 56262306a36Sopenharmony_ci ap->nrecvdatapkt = 0; 56362306a36Sopenharmony_ci ap->nsentctlpkt = 0; 56462306a36Sopenharmony_ci ap->nsentdatapkt = 0; 56562306a36Sopenharmony_ci mutex_init(&ap->recv_mtx); 56662306a36Sopenharmony_ci skb_queue_head_init(&ap->recv_queue); 56762306a36Sopenharmony_ci INIT_WORK(&ap->recv_work, recv_handler); 56862306a36Sopenharmony_ci ap->release_in_progress = 0; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci mutex_lock(&capi_controller_lock); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { 57362306a36Sopenharmony_ci if (capi_applications[applid - 1] == NULL) 57462306a36Sopenharmony_ci break; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci if (applid > CAPI_MAXAPPL) { 57762306a36Sopenharmony_ci mutex_unlock(&capi_controller_lock); 57862306a36Sopenharmony_ci return CAPI_TOOMANYAPPLS; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci ap->applid = applid; 58262306a36Sopenharmony_ci capi_applications[applid - 1] = ap; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci for (i = 0; i < CAPI_MAXCONTR; i++) { 58562306a36Sopenharmony_ci if (!capi_controller[i] || 58662306a36Sopenharmony_ci capi_controller[i]->state != CAPI_CTR_RUNNING) 58762306a36Sopenharmony_ci continue; 58862306a36Sopenharmony_ci register_appl(capi_controller[i], applid, &ap->rparam); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci mutex_unlock(&capi_controller_lock); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (showcapimsgs & 1) { 59462306a36Sopenharmony_ci printk(KERN_DEBUG "kcapi: appl %d up\n", applid); 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci return CAPI_NOERROR; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci/** 60162306a36Sopenharmony_ci * capi20_release() - CAPI 2.0 operation CAPI_RELEASE 60262306a36Sopenharmony_ci * @ap: CAPI application descriptor structure. 60362306a36Sopenharmony_ci * 60462306a36Sopenharmony_ci * Terminate an application's registration with CAPI. 60562306a36Sopenharmony_ci * After this function returns successfully, the message receive 60662306a36Sopenharmony_ci * callback function @ap->recv_message() will no longer be called. 60762306a36Sopenharmony_ci * Return value: CAPI result code 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ciu16 capi20_release(struct capi20_appl *ap) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci int i; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci DBG("applid %#x", ap->applid); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci mutex_lock(&capi_controller_lock); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci ap->release_in_progress = 1; 61962306a36Sopenharmony_ci capi_applications[ap->applid - 1] = NULL; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci synchronize_rcu(); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci for (i = 0; i < CAPI_MAXCONTR; i++) { 62462306a36Sopenharmony_ci if (!capi_controller[i] || 62562306a36Sopenharmony_ci capi_controller[i]->state != CAPI_CTR_RUNNING) 62662306a36Sopenharmony_ci continue; 62762306a36Sopenharmony_ci release_appl(capi_controller[i], ap->applid); 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci mutex_unlock(&capi_controller_lock); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci flush_workqueue(kcapi_wq); 63362306a36Sopenharmony_ci skb_queue_purge(&ap->recv_queue); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (showcapimsgs & 1) { 63662306a36Sopenharmony_ci printk(KERN_DEBUG "kcapi: appl %d down\n", ap->applid); 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return CAPI_NOERROR; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/** 64362306a36Sopenharmony_ci * capi20_put_message() - CAPI 2.0 operation CAPI_PUT_MESSAGE 64462306a36Sopenharmony_ci * @ap: CAPI application descriptor structure. 64562306a36Sopenharmony_ci * @skb: CAPI message. 64662306a36Sopenharmony_ci * 64762306a36Sopenharmony_ci * Transfer a single message to CAPI. 64862306a36Sopenharmony_ci * Return value: CAPI result code 64962306a36Sopenharmony_ci */ 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ciu16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct capi_ctr *ctr; 65462306a36Sopenharmony_ci int showctl = 0; 65562306a36Sopenharmony_ci u8 cmd, subcmd; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci DBG("applid %#x", ap->applid); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (ncontrollers == 0) 66062306a36Sopenharmony_ci return CAPI_REGNOTINSTALLED; 66162306a36Sopenharmony_ci if ((ap->applid == 0) || ap->release_in_progress) 66262306a36Sopenharmony_ci return CAPI_ILLAPPNR; 66362306a36Sopenharmony_ci if (skb->len < 12 66462306a36Sopenharmony_ci || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data)) 66562306a36Sopenharmony_ci || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data))) 66662306a36Sopenharmony_ci return CAPI_ILLCMDORSUBCMDORMSGTOSMALL; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* 66962306a36Sopenharmony_ci * The controller reference is protected by the existence of the 67062306a36Sopenharmony_ci * application passed to us. We assume that the caller properly 67162306a36Sopenharmony_ci * synchronizes this service with capi20_release. 67262306a36Sopenharmony_ci */ 67362306a36Sopenharmony_ci ctr = get_capi_ctr_by_nr(CAPIMSG_CONTROLLER(skb->data)); 67462306a36Sopenharmony_ci if (!ctr || ctr->state != CAPI_CTR_RUNNING) 67562306a36Sopenharmony_ci return CAPI_REGNOTINSTALLED; 67662306a36Sopenharmony_ci if (ctr->blocked) 67762306a36Sopenharmony_ci return CAPI_SENDQUEUEFULL; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci cmd = CAPIMSG_COMMAND(skb->data); 68062306a36Sopenharmony_ci subcmd = CAPIMSG_SUBCOMMAND(skb->data); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) { 68362306a36Sopenharmony_ci ctr->nsentdatapkt++; 68462306a36Sopenharmony_ci ap->nsentdatapkt++; 68562306a36Sopenharmony_ci if (ctr->traceflag > 2) 68662306a36Sopenharmony_ci showctl |= 2; 68762306a36Sopenharmony_ci } else { 68862306a36Sopenharmony_ci ctr->nsentctlpkt++; 68962306a36Sopenharmony_ci ap->nsentctlpkt++; 69062306a36Sopenharmony_ci if (ctr->traceflag) 69162306a36Sopenharmony_ci showctl |= 2; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci showctl |= (ctr->traceflag & 1); 69462306a36Sopenharmony_ci if (showctl & 2) { 69562306a36Sopenharmony_ci if (showctl & 1) { 69662306a36Sopenharmony_ci printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u\n", 69762306a36Sopenharmony_ci CAPIMSG_CONTROLLER(skb->data), 69862306a36Sopenharmony_ci CAPIMSG_APPID(skb->data), 69962306a36Sopenharmony_ci capi_cmd2str(cmd, subcmd), 70062306a36Sopenharmony_ci CAPIMSG_LEN(skb->data)); 70162306a36Sopenharmony_ci } else { 70262306a36Sopenharmony_ci _cdebbuf *cdb = capi_message2str(skb->data); 70362306a36Sopenharmony_ci if (cdb) { 70462306a36Sopenharmony_ci printk(KERN_DEBUG "kcapi: put [%03d] %s\n", 70562306a36Sopenharmony_ci CAPIMSG_CONTROLLER(skb->data), 70662306a36Sopenharmony_ci cdb->buf); 70762306a36Sopenharmony_ci cdebbuf_free(cdb); 70862306a36Sopenharmony_ci } else 70962306a36Sopenharmony_ci printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u cannot trace\n", 71062306a36Sopenharmony_ci CAPIMSG_CONTROLLER(skb->data), 71162306a36Sopenharmony_ci CAPIMSG_APPID(skb->data), 71262306a36Sopenharmony_ci capi_cmd2str(cmd, subcmd), 71362306a36Sopenharmony_ci CAPIMSG_LEN(skb->data)); 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci return ctr->send_message(ctr, skb); 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci/** 72062306a36Sopenharmony_ci * capi20_get_manufacturer() - CAPI 2.0 operation CAPI_GET_MANUFACTURER 72162306a36Sopenharmony_ci * @contr: controller number. 72262306a36Sopenharmony_ci * @buf: result buffer (64 bytes). 72362306a36Sopenharmony_ci * 72462306a36Sopenharmony_ci * Retrieve information about the manufacturer of the specified ISDN controller 72562306a36Sopenharmony_ci * or (for @contr == 0) the driver itself. 72662306a36Sopenharmony_ci * Return value: CAPI result code 72762306a36Sopenharmony_ci */ 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ciu16 capi20_get_manufacturer(u32 contr, u8 buf[CAPI_MANUFACTURER_LEN]) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci struct capi_ctr *ctr; 73262306a36Sopenharmony_ci u16 ret; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (contr == 0) { 73562306a36Sopenharmony_ci strncpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN); 73662306a36Sopenharmony_ci return CAPI_NOERROR; 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci mutex_lock(&capi_controller_lock); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci ctr = get_capi_ctr_by_nr(contr); 74262306a36Sopenharmony_ci if (ctr && ctr->state == CAPI_CTR_RUNNING) { 74362306a36Sopenharmony_ci strncpy(buf, ctr->manu, CAPI_MANUFACTURER_LEN); 74462306a36Sopenharmony_ci ret = CAPI_NOERROR; 74562306a36Sopenharmony_ci } else 74662306a36Sopenharmony_ci ret = CAPI_REGNOTINSTALLED; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci mutex_unlock(&capi_controller_lock); 74962306a36Sopenharmony_ci return ret; 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci/** 75362306a36Sopenharmony_ci * capi20_get_version() - CAPI 2.0 operation CAPI_GET_VERSION 75462306a36Sopenharmony_ci * @contr: controller number. 75562306a36Sopenharmony_ci * @verp: result structure. 75662306a36Sopenharmony_ci * 75762306a36Sopenharmony_ci * Retrieve version information for the specified ISDN controller 75862306a36Sopenharmony_ci * or (for @contr == 0) the driver itself. 75962306a36Sopenharmony_ci * Return value: CAPI result code 76062306a36Sopenharmony_ci */ 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ciu16 capi20_get_version(u32 contr, struct capi_version *verp) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci struct capi_ctr *ctr; 76562306a36Sopenharmony_ci u16 ret; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (contr == 0) { 76862306a36Sopenharmony_ci *verp = driver_version; 76962306a36Sopenharmony_ci return CAPI_NOERROR; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci mutex_lock(&capi_controller_lock); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci ctr = get_capi_ctr_by_nr(contr); 77562306a36Sopenharmony_ci if (ctr && ctr->state == CAPI_CTR_RUNNING) { 77662306a36Sopenharmony_ci memcpy(verp, &ctr->version, sizeof(capi_version)); 77762306a36Sopenharmony_ci ret = CAPI_NOERROR; 77862306a36Sopenharmony_ci } else 77962306a36Sopenharmony_ci ret = CAPI_REGNOTINSTALLED; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci mutex_unlock(&capi_controller_lock); 78262306a36Sopenharmony_ci return ret; 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci/** 78662306a36Sopenharmony_ci * capi20_get_serial() - CAPI 2.0 operation CAPI_GET_SERIAL_NUMBER 78762306a36Sopenharmony_ci * @contr: controller number. 78862306a36Sopenharmony_ci * @serial: result buffer (8 bytes). 78962306a36Sopenharmony_ci * 79062306a36Sopenharmony_ci * Retrieve the serial number of the specified ISDN controller 79162306a36Sopenharmony_ci * or (for @contr == 0) the driver itself. 79262306a36Sopenharmony_ci * Return value: CAPI result code 79362306a36Sopenharmony_ci */ 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ciu16 capi20_get_serial(u32 contr, u8 serial[CAPI_SERIAL_LEN]) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci struct capi_ctr *ctr; 79862306a36Sopenharmony_ci u16 ret; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (contr == 0) { 80162306a36Sopenharmony_ci strscpy(serial, driver_serial, CAPI_SERIAL_LEN); 80262306a36Sopenharmony_ci return CAPI_NOERROR; 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci mutex_lock(&capi_controller_lock); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci ctr = get_capi_ctr_by_nr(contr); 80862306a36Sopenharmony_ci if (ctr && ctr->state == CAPI_CTR_RUNNING) { 80962306a36Sopenharmony_ci strscpy(serial, ctr->serial, CAPI_SERIAL_LEN); 81062306a36Sopenharmony_ci ret = CAPI_NOERROR; 81162306a36Sopenharmony_ci } else 81262306a36Sopenharmony_ci ret = CAPI_REGNOTINSTALLED; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci mutex_unlock(&capi_controller_lock); 81562306a36Sopenharmony_ci return ret; 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci/** 81962306a36Sopenharmony_ci * capi20_get_profile() - CAPI 2.0 operation CAPI_GET_PROFILE 82062306a36Sopenharmony_ci * @contr: controller number. 82162306a36Sopenharmony_ci * @profp: result structure. 82262306a36Sopenharmony_ci * 82362306a36Sopenharmony_ci * Retrieve capability information for the specified ISDN controller 82462306a36Sopenharmony_ci * or (for @contr == 0) the number of installed controllers. 82562306a36Sopenharmony_ci * Return value: CAPI result code 82662306a36Sopenharmony_ci */ 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ciu16 capi20_get_profile(u32 contr, struct capi_profile *profp) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct capi_ctr *ctr; 83162306a36Sopenharmony_ci u16 ret; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (contr == 0) { 83462306a36Sopenharmony_ci profp->ncontroller = ncontrollers; 83562306a36Sopenharmony_ci return CAPI_NOERROR; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci mutex_lock(&capi_controller_lock); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci ctr = get_capi_ctr_by_nr(contr); 84162306a36Sopenharmony_ci if (ctr && ctr->state == CAPI_CTR_RUNNING) { 84262306a36Sopenharmony_ci memcpy(profp, &ctr->profile, sizeof(struct capi_profile)); 84362306a36Sopenharmony_ci ret = CAPI_NOERROR; 84462306a36Sopenharmony_ci } else 84562306a36Sopenharmony_ci ret = CAPI_REGNOTINSTALLED; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci mutex_unlock(&capi_controller_lock); 84862306a36Sopenharmony_ci return ret; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci/** 85262306a36Sopenharmony_ci * capi20_manufacturer() - CAPI 2.0 operation CAPI_MANUFACTURER 85362306a36Sopenharmony_ci * @cmd: command. 85462306a36Sopenharmony_ci * @data: parameter. 85562306a36Sopenharmony_ci * 85662306a36Sopenharmony_ci * Perform manufacturer specific command. 85762306a36Sopenharmony_ci * Return value: CAPI result code 85862306a36Sopenharmony_ci */ 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ciint capi20_manufacturer(unsigned long cmd, void __user *data) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci struct capi_ctr *ctr; 86362306a36Sopenharmony_ci int retval; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci switch (cmd) { 86662306a36Sopenharmony_ci case KCAPI_CMD_TRACE: 86762306a36Sopenharmony_ci { 86862306a36Sopenharmony_ci kcapi_flagdef fdef; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci if (copy_from_user(&fdef, data, sizeof(kcapi_flagdef))) 87162306a36Sopenharmony_ci return -EFAULT; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci mutex_lock(&capi_controller_lock); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci ctr = get_capi_ctr_by_nr(fdef.contr); 87662306a36Sopenharmony_ci if (ctr) { 87762306a36Sopenharmony_ci ctr->traceflag = fdef.flag; 87862306a36Sopenharmony_ci printk(KERN_INFO "kcapi: contr [%03d] set trace=%d\n", 87962306a36Sopenharmony_ci ctr->cnr, ctr->traceflag); 88062306a36Sopenharmony_ci retval = 0; 88162306a36Sopenharmony_ci } else 88262306a36Sopenharmony_ci retval = -ESRCH; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci mutex_unlock(&capi_controller_lock); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci return retval; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci default: 89062306a36Sopenharmony_ci printk(KERN_ERR "kcapi: manufacturer command %lu unknown.\n", 89162306a36Sopenharmony_ci cmd); 89262306a36Sopenharmony_ci break; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci } 89562306a36Sopenharmony_ci return -EINVAL; 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci/* ------------------------------------------------------------- */ 89962306a36Sopenharmony_ci/* -------- Init & Cleanup ------------------------------------- */ 90062306a36Sopenharmony_ci/* ------------------------------------------------------------- */ 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci/* 90362306a36Sopenharmony_ci * init / exit functions 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ciint __init kcapi_init(void) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci int err; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci kcapi_wq = alloc_workqueue("kcapi", 0, 0); 91162306a36Sopenharmony_ci if (!kcapi_wq) 91262306a36Sopenharmony_ci return -ENOMEM; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci err = cdebug_init(); 91562306a36Sopenharmony_ci if (err) { 91662306a36Sopenharmony_ci destroy_workqueue(kcapi_wq); 91762306a36Sopenharmony_ci return err; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci kcapi_proc_init(); 92162306a36Sopenharmony_ci return 0; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_civoid kcapi_exit(void) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci kcapi_proc_exit(); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci cdebug_exit(); 92962306a36Sopenharmony_ci destroy_workqueue(kcapi_wq); 93062306a36Sopenharmony_ci} 931