/* * Copyright (C) 1997-2000 SpellCaster Telecommunications Inc. * $Id: mvip_if.c,v 1.1 2004/03/11 03:59:31 bcrl Exp $ * Released under the GNU Public License. See LICENSE file for details. */ #define __KERNEL__ #include #include #include #include "includes.h" #include "message.h" #include "adapter.h" #include "mvip_if.h" #include "msgq.h" extern int cinst; extern adapter_t *cards[]; extern void scm_lock(void); extern void scm_unlock(void); enum Reqs { ReqGetChan, ReqConnect, /* to user */ ReqConnecting, /* to kernel */ ReqDisconnect, /* to kernel */ ReqDisconnected /* to user */ }; struct Req { enum Reqs req; struct channel *ch; unsigned long data; char num[52]; }; struct MsgQ q; static int have_daemon = 0; /* * Babylon's entry for making a connection * - queues a message for userland to read and blah blah... */ void call_connect (struct channel *ch, char *num, __u32 data) { struct Req req; printk("call_connect: ConnectComplete=%p\n", ch->ConnectComplete); ch->state = CS_DIALING; req.req = ReqConnect; req.ch = ch; strncpy(req.num, num, sizeof(req.num)); if (have_daemon && 0 != queueMsg(&q, &req, sizeof(req))) { ch->ConnectComplete(ch, 0x900); ch->state = CS_IDLE; } } /* * Babylon's entry for disconnecting the call * - mark channel disconnecting * - initiate mvip teardown of the call */ void call_hangup (struct channel *ch, int cause) { printk("call_hangup\n"); if (CS_CONNECTED == ch->state) { ch->Down(ch); ch->Close(ch); } if (CS_DISCONNECTING != ch->state) { ch->state = CS_DISCONNECTING; call_disconnect(ch); } } /* * */ void call_connected (struct channel *ch) { printk("call_connected(%p)\n", ch); printk("call_connected: ConnectComplete=%p\n", ch->ConnectComplete); if (CS_CONNECTING != ch->state) printk("call_connected: state=%d\n", ch->state); ch->state = CS_CONNECTED; ch->Open(ch); ch->Up(ch); } /* * called when channel is disconnected from the mvip bus * - report disconnection to the mvip-daemon-thingy * - if call was connected, report back to babylon * the disconnect */ void call_disconnected (struct channel *ch) { struct Req req; port_t *p; printk("call_disconnected(%p)\n", ch); p = ch->dev; if (CS_DISCONNECTING != ch->state) printk("call_disconnected: state!=disconnecting\n"); printk("call_disconnected: ConnectComplete=%p\n", ch->ConnectComplete); ch->ConnectComplete(ch, p->disc_cause); ch->state = CS_IDLE; req.req = ReqDisconnected; req.ch = ch; printk("queuingMsg\n"); if (have_daemon && 0 != queueMsg(&q, &req, sizeof(req))) printk("doh: call_hangup - queueMsg failed\n"); } /**********************************/ struct channel *getIdleChannel ( void ) { adapter_t *card = cards[0]; int i; printk("getIdleChannel\n"); /* cli/sti only here to protect from Babylon calling Connect() on irq */ cli(); for (i=4; inr_ports; i++) { if (card->ports[i] && CS_IDLE == card->ports[i]->ch.state) { card->ports[i]->ch.state = CS_RINGING; sti(); printk("getIdleChannel: %d/%p\n", i, &card->ports[i]->ch); return &card->ports[i]->ch; } } sti(); return NULL; } int call_write(struct inode *i, struct file *filp, const char *buf, int len) { struct channel *ch = filp->private_data; struct Req req; int ret; if (sizeof(req) != len) return -EIO; ret = verify_area(VERIFY_READ, buf, sizeof(req)); if (ret) return ret; memcpy_fromfs(&req, buf, sizeof(req)); switch (req.req) { case ReqGetChan: req.ch = getIdleChannel(); if (req.ch) printk("got channel %p (%s)\n", req.ch, req.ch->device_name); if (have_daemon && 0 != queueMsg(&q, &req, sizeof(req))) { if (req.ch) req.ch->state = CS_IDLE; return -ENOMEM; } break; case ReqConnecting: ch = req.ch; if (!ch) { printk("stupid idiot! no ch!!!\n"); break; } printk("ReqConnecting(%p)\n", ch); printk("ReqConnecting: ConnectComplete=%p\n", ch->ConnectComplete); cli(); switch (ch->state) { case CS_RINGING: case CS_DIALING: ret = (CS_RINGING == ch->state); ch->state = CS_CONNECTING; call_connecting(ch, ret, req.data); break; default: printk("Urg: ReqConnecting with state=%d\n", ch->state); } sti(); break; case ReqDisconnect: ch = req.ch; if (!ch) { printk("stupid idiot! no ch!!!\n"); break; } printk("ReqDisconnect(%p)\n", ch); printk("ReqDisconnect: ConnectComplete=%p\n", ch->ConnectComplete); cli(); switch (ch->state) { case CS_DIALING: ch->ConnectComplete(ch, req.data); ch->state = CS_IDLE; break; case CS_CONNECTING: case CS_CONNECTED: ((port_t *)ch->dev)->disc_cause = req.data; ch->state = CS_DISCONNECTING; call_disconnect(ch); break; default: printk("doWrite: ReqDisconnect: state=%d\n", ch->state); break; } sti(); break; default: printk("call_write: unknown request %d\n", req.req); break; } return len; } int call_read(struct inode *inode, struct file *filp, char *buf, int len) { struct qMsg *msg; int ret; if (len < 0) return -EINVAL; ret = verify_area(VERIFY_WRITE, buf, len); if (ret) return ret; msg = waitMsg(&q); if (!msg) return -EAGAIN; len = msg->len < len ? msg->len : len; memcpy_tofs(buf, msg->data, len); kfree(msg); return len; } int call_select(struct inode *inode, struct file *filp, int sel_type, select_table *wait) { int ret; switch (sel_type) { case SEL_IN: /* read */ select_wait(&q.wait, wait); ret = !!q.msgQ; break; case SEL_OUT: /* write */ ret = 1; break; default: ret = 0; break; } return ret; } int call_open(struct inode *inode, struct file *filp) { if (have_daemon) return -EBUSY; have_daemon++; scm_lock(); filp->private_data = NULL; return 0; } void call_release(struct inode *inode, struct file *filp) { int i, j; have_daemon--; cli(); for (i=0; iports[j]) continue; ch = &cards[i]->ports[j]->ch; switch (ch->state) { case CS_CONNECTING: ((port_t *)ch->dev)->disc_cause = 0x1100; ch->state = CS_DISCONNECTING; call_disconnect(ch); break; case CS_CONNECTED: case CS_STALLED: ch->Down(ch); ch->Close(ch); default: #if 0 amsg_drop(cards[i], cards[i]->ports[j]->index, 0); ch->state = CS_IDLE; #endif case CS_IDLE: case CS_UNAVAIL: break; } filp->private_data = NULL; } } sti(); if (!have_daemon) { struct qMsg *msg; while (NULL != (msg = getMsg(&q))) kfree(msg); } scm_unlock(); } struct file_operations call_file_operations = { NULL, /* lseek */ call_read, call_write, NULL, /* readdir */ call_select, /* select */ NULL, /* ioctl */ NULL, /* mmap */ call_open, call_release, NULL, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ NULL /* revalidate */ }; struct inode_operations call_inode_operations = { &call_file_operations, 0, }; struct proc_dir_entry proc_scmctrl = { 0, 7, "scmctrl", S_IFREG | S_IRUGO | 0666, 1, 0, 0, 0, &call_inode_operations, 0, 0, 0, 0, 0 }; int call_init(void) { proc_register_dynamic(&proc_root, &proc_scmctrl); return 0; } void call_cleanup() { proc_unregister(&proc_root, proc_scmctrl.low_ino); }