summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
authorhm <hm@FreeBSD.org>2001-05-25 08:43:30 +0000
committerhm <hm@FreeBSD.org>2001-05-25 08:43:30 +0000
commit7e6e58c4c7f91947ec1f9a10a284f8d9c2fdf1f2 (patch)
tree6b2ce85b45dc25104f9a4e60d014db8fc3de9b95 /usr.sbin
parent8094d979ca0adb982d9e0c5482a2825da1b38e11 (diff)
downloadFreeBSD-src-7e6e58c4c7f91947ec1f9a10a284f8d9c2fdf1f2.zip
FreeBSD-src-7e6e58c4c7f91947ec1f9a10a284f8d9c2fdf1f2.tar.gz
Submitted by: Juha-Matti Liukkonen (Cubical Solutions Ltd) (jml@cubical.fi)
Add a CAPI (hardware independent) driver i4bcapi(4) and hardware driver iavc (4) to support active CAPI-based BRI and PRI cards (currently AVM B1 and T1 cards) to isdn4bsd.
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/i4b/isdnd/config.h8
-rw-r--r--usr.sbin/i4b/isdnd/controller.c217
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.h10
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.rc.525
-rw-r--r--usr.sbin/i4b/isdnd/monitor.c13
-rw-r--r--usr.sbin/i4b/isdnd/rc_config.c72
-rw-r--r--usr.sbin/i4b/isdnd/rc_parse.y18
-rw-r--r--usr.sbin/i4b/isdnd/rc_scan.l8
-rw-r--r--usr.sbin/i4b/isdnd/support.c70
-rw-r--r--usr.sbin/i4b/isdndebug/main.c31
-rw-r--r--usr.sbin/i4b/man/Makefile3
-rw-r--r--usr.sbin/i4b/man/i4bcapi.455
-rw-r--r--usr.sbin/i4b/man/iavc.467
13 files changed, 383 insertions, 214 deletions
diff --git a/usr.sbin/i4b/isdnd/config.h b/usr.sbin/i4b/isdnd/config.h
index 4a7aa8c..a4b05f1 100644
--- a/usr.sbin/i4b/isdnd/config.h
+++ b/usr.sbin/i4b/isdnd/config.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 1999 Hellmuth Michaelis. All rights reserved.
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -27,11 +27,9 @@
* i4b daemon - compile time configuration header file
* ---------------------------------------------------
*
- * $Id: config.h,v 1.8 1999/12/13 21:25:24 hm Exp $
- *
* $FreeBSD$
*
- * last edit-date: [Mon Dec 13 21:45:27 1999]
+ * last edit-date: [Mon May 21 11:21:15 2001]
*
*---------------------------------------------------------------------------*/
@@ -41,7 +39,7 @@
/* general values */
#define UMASK 022 /* file creation perm mask */
-#define CFG_ENTRY_MAX 32 /* max no of config entries */
+#define CFG_ENTRY_MAX 60 /* max no of config entries */
#define ISDN_CTRL_MAX 4 /* max no of controllers */
#define MAX_RE 8 /* max regular expression entries */
diff --git a/usr.sbin/i4b/isdnd/controller.c b/usr.sbin/i4b/isdnd/controller.c
index 9c4fe9e..95f5c90 100644
--- a/usr.sbin/i4b/isdnd/controller.c
+++ b/usr.sbin/i4b/isdnd/controller.c
@@ -29,14 +29,17 @@
*
* $FreeBSD$
*
- * last edit-date: [Fri Jan 26 14:00:10 2001]
+ * last edit-date: [Sun May 20 10:03:53 2001]
*
*---------------------------------------------------------------------------*/
+#include <sys/types.h>
+#include <sys/mman.h>
+
#include "isdnd.h"
static int
-init_controller_state(int controller, int ctrl_type, int card_type, int tei);
+init_controller_state(int controller, int ctrl_type, int card_type, int tei, int nbch);
/*---------------------------------------------------------------------------*
* get name of a controller
@@ -84,6 +87,12 @@ name_of_controller(int ctrl_type, int card_type)
"EICON.Diehl QUADRO",
};
+ static char *capi_card[] = {
+ "AVM T1 PCI",
+ "AVM B1 PCI",
+ "AVM B1 ISA",
+ };
+
if(ctrl_type == CTRL_PASSIVE)
{
int index = card_type - CARD_TYPEP_8;
@@ -100,6 +109,12 @@ name_of_controller(int ctrl_type, int card_type)
{
return "Stollmann tina-dd";
}
+ else if(ctrl_type == CTRL_CAPI)
+ {
+ int index = card_type - CARD_TYPEC_AVM_T1_PCI;
+ if (index >= 0 && index < (sizeof capi_card / sizeof capi_card[0] ))
+ return capi_card[index];
+ }
return "unknown card type";
}
@@ -138,7 +153,7 @@ init_controller(void)
/* init controller tab */
- if((init_controller_state(i, mcir.ctrl_type, mcir.card_type, mcir.tei)) == ERROR)
+ if((init_controller_state(i, mcir.ctrl_type, mcir.card_type, mcir.tei, mcir.nbch)) == ERROR)
{
log(LL_ERR, "init_controller: init_controller_state for controller %d failed", i);
do_exit(1);
@@ -151,8 +166,11 @@ init_controller(void)
* init controller state table entry
*--------------------------------------------------------------------------*/
static int
-init_controller_state(int controller, int ctrl_type, int card_type, int tei)
+init_controller_state(int controller, int ctrl_type, int card_type, int tei,
+ int nbch)
{
+ int i;
+
if((controller < 0) || (controller >= ncontroller))
{
log(LL_ERR, "init_controller_state: invalid controller number [%d]!", controller);
@@ -161,57 +179,49 @@ init_controller_state(int controller, int ctrl_type, int card_type, int tei)
/* init controller tab */
- if(ctrl_type == CTRL_PASSIVE)
- {
+ switch (ctrl_type) {
+ case CTRL_PASSIVE:
if((card_type > CARD_TYPEP_UNK) &&
(card_type <= CARD_TYPEP_MAX))
{
isdn_ctrl_tab[controller].ctrl_type = ctrl_type;
isdn_ctrl_tab[controller].card_type = card_type;
isdn_ctrl_tab[controller].state = CTRL_UP;
- isdn_ctrl_tab[controller].stateb1 = CHAN_IDLE;
- isdn_ctrl_tab[controller].stateb2 = CHAN_IDLE;
- isdn_ctrl_tab[controller].freechans = MAX_CHANCTRL;
- isdn_ctrl_tab[controller].tei = tei;
- isdn_ctrl_tab[controller].l1stat = LAYER_IDLE;
- isdn_ctrl_tab[controller].l2stat = LAYER_IDLE;
- DBGL(DL_RCCF, (log(LL_DBG, "init_controller_state: controller %d is %s",
- controller,
- name_of_controller(isdn_ctrl_tab[controller].ctrl_type,
- isdn_ctrl_tab[controller].card_type))));
}
else
{
log(LL_ERR, "init_controller_state: unknown card type %d", card_type);
return(ERROR);
}
+ break;
- }
- else if(ctrl_type == CTRL_DAIC)
- {
+ case CTRL_DAIC:
isdn_ctrl_tab[controller].ctrl_type = ctrl_type;
isdn_ctrl_tab[controller].card_type = card_type;
isdn_ctrl_tab[controller].state = CTRL_DOWN;
- isdn_ctrl_tab[controller].stateb1 = CHAN_IDLE;
- isdn_ctrl_tab[controller].stateb2 = CHAN_IDLE;
- isdn_ctrl_tab[controller].freechans = MAX_CHANCTRL;
- isdn_ctrl_tab[controller].tei = tei;
- isdn_ctrl_tab[controller].l1stat = LAYER_IDLE;
- isdn_ctrl_tab[controller].l2stat = LAYER_IDLE;
+ break;
- log(LL_DMN, "init_controller_state: controller %d is %s",
- controller,
- name_of_controller(isdn_ctrl_tab[controller].ctrl_type,
- isdn_ctrl_tab[controller].card_type));
- }
- else if(ctrl_type == CTRL_TINADD)
- {
+ case CTRL_TINADD:
isdn_ctrl_tab[controller].ctrl_type = ctrl_type;
isdn_ctrl_tab[controller].card_type = 0;
isdn_ctrl_tab[controller].state = CTRL_DOWN;
- isdn_ctrl_tab[controller].stateb1 = CHAN_IDLE;
- isdn_ctrl_tab[controller].stateb2 = CHAN_IDLE;
- isdn_ctrl_tab[controller].freechans = MAX_CHANCTRL;
+ break;
+
+ case CTRL_CAPI:
+ isdn_ctrl_tab[controller].ctrl_type = ctrl_type;
+ isdn_ctrl_tab[controller].card_type = card_type;
+ isdn_ctrl_tab[controller].state = CTRL_UP;
+ break;
+
+ default:
+ log(LL_ERR, "init_controller_state: unknown controller type %d", ctrl_type);
+ return(ERROR);
+ }
+
+ isdn_ctrl_tab[controller].nbch = nbch;
+ isdn_ctrl_tab[controller].freechans = nbch;
+ for (i = 0; i < nbch; i++)
+ isdn_ctrl_tab[controller].stateb[i] = CHAN_IDLE;
isdn_ctrl_tab[controller].tei = tei;
isdn_ctrl_tab[controller].l1stat = LAYER_IDLE;
isdn_ctrl_tab[controller].l2stat = LAYER_IDLE;
@@ -221,17 +231,11 @@ init_controller_state(int controller, int ctrl_type, int card_type, int tei)
name_of_controller(isdn_ctrl_tab[controller].ctrl_type,
isdn_ctrl_tab[controller].card_type));
- }
- else
- {
- log(LL_ERR, "init_controller_state: unknown controller type %d", ctrl_type);
- return(ERROR);
- }
return(GOOD);
}
/*--------------------------------------------------------------------------*
- * init active controller
+ * init active or capi controller
*--------------------------------------------------------------------------*/
void
init_active_controller(void)
@@ -255,6 +259,54 @@ init_active_controller(void)
do_exit(1);
}
}
+
+ /*
+ * Generic microcode loading. If a controller has
+ * defined a microcode file, load it using the
+ * I4B_CTRL_DOWNLOAD ioctl.
+ */
+
+ if(isdn_ctrl_tab[controller].firmware != NULL)
+ {
+ int fd, ret;
+ struct isdn_dr_prot idp;
+ struct isdn_download_request idr;
+
+ fd = open(isdn_ctrl_tab[controller].firmware, O_RDONLY);
+ if (fd < 0) {
+ log(LL_ERR, "init_active_controller %d: open %s: %s!",
+ controller, isdn_ctrl_tab[controller].firmware,
+ strerror(errno));
+ do_exit(1);
+ }
+
+ idp.bytecount = lseek(fd, 0, SEEK_END);
+ idp.microcode = mmap(0, idp.bytecount, PROT_READ,
+ MAP_SHARED, fd, 0);
+ if (idp.microcode == MAP_FAILED) {
+ log(LL_ERR, "init_active_controller %d: mmap %s: %s!",
+ controller, isdn_ctrl_tab[controller].firmware,
+ strerror(errno));
+ do_exit(1);
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "init_active_controller %d: loading firmware from [%s]", controller, isdn_ctrl_tab[controller].firmware)));
+
+ idr.controller = controller;
+ idr.numprotos = 1;
+ idr.protocols = &idp;
+
+ ret = ioctl(isdnfd, I4B_CTRL_DOWNLOAD, &idr, sizeof(idr));
+ if (ret) {
+ log(LL_ERR, "init_active_controller %d: load %s: %s!",
+ controller, isdn_ctrl_tab[controller].firmware,
+ strerror(errno));
+ do_exit(1);
+ }
+
+ munmap(idp.microcode, idp.bytecount);
+ close(fd);
+ }
}
}
@@ -359,7 +411,7 @@ incr_free_channels(int controller)
log(LL_ERR, "incr_free_channels: invalid controller number [%d]!", controller);
return(ERROR);
}
- if(isdn_ctrl_tab[controller].freechans < MAX_CHANCTRL)
+ if(isdn_ctrl_tab[controller].freechans < isdn_ctrl_tab[controller].nbch)
{
(isdn_ctrl_tab[controller].freechans)++;
DBGL(DL_CNST, (log(LL_DBG, "incr_free_channels: ctrl %d, now %d chan free", controller, isdn_ctrl_tab[controller].freechans)));
@@ -367,7 +419,7 @@ incr_free_channels(int controller)
}
else
{
- log(LL_ERR, "incr_free_channels: controller [%d] already 2 free chans!", controller);
+ log(LL_ERR, "incr_free_channels: controller [%d] already %d free chans!", controller, isdn_ctrl_tab[controller].nbch);
return(ERROR);
}
}
@@ -399,36 +451,20 @@ set_channel_busy(int controller, int channel)
return(ERROR);
}
- switch(channel)
- {
- case CHAN_B1:
- if(isdn_ctrl_tab[controller].stateb1 == CHAN_RUN)
+ if ((channel < 0) || (channel >= isdn_ctrl_tab[controller].nbch))
{
- DBGL(DL_CNST, (log(LL_DBG, "set_channel_busy: controller [%d] channel B1 already busy!", controller)));
- }
- else
- {
- isdn_ctrl_tab[controller].stateb1 = CHAN_RUN;
- DBGL(DL_CNST, (log(LL_DBG, "set_channel_busy: controller [%d] channel B1 set to BUSY!", controller)));
+ log(LL_ERR, "set_channel_busy: controller [%d] invalid channel [%d]!", controller, channel);
+ return(ERROR);
}
- break;
- case CHAN_B2:
- if(isdn_ctrl_tab[controller].stateb2 == CHAN_RUN)
+ if(isdn_ctrl_tab[controller].stateb[channel] == CHAN_RUN)
{
- DBGL(DL_CNST, (log(LL_DBG, "set_channel_busy: controller [%d] channel B2 already busy!", controller)));
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_busy: controller [%d] channel B%d already busy!", controller, channel+1)));
}
else
{
- isdn_ctrl_tab[controller].stateb2 = CHAN_RUN;
- DBGL(DL_CNST, (log(LL_DBG, "set_channel_busy: controller [%d] channel B2 set to BUSY!", controller)));
- }
- break;
-
- default:
- log(LL_ERR, "set_channel_busy: controller [%d], invalid channel [%d]!", controller, channel);
- return(ERROR);
- break;
+ isdn_ctrl_tab[controller].stateb[channel] = CHAN_RUN;
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_busy: controller [%d] channel B%d set to BUSY!", controller, channel+1)));
}
return(GOOD);
}
@@ -445,36 +481,20 @@ set_channel_idle(int controller, int channel)
return(ERROR);
}
- switch(channel)
- {
- case CHAN_B1:
- if(isdn_ctrl_tab[controller].stateb1 == CHAN_IDLE)
- {
- DBGL(DL_CNST, (log(LL_DBG, "set_channel_idle: controller [%d] channel B1 already idle!", controller)));
- }
- else
+ if ((channel < 0) || (channel >= isdn_ctrl_tab[controller].nbch))
{
- isdn_ctrl_tab[controller].stateb1 = CHAN_IDLE;
- DBGL(DL_CNST, (log(LL_DBG, "set_channel_idle: controller [%d] channel B1 set to IDLE!", controller)));
+ log(LL_ERR, "set_channel_busy: controller [%d] invalid channel [%d]!", controller, channel);
+ return(ERROR);
}
- break;
- case CHAN_B2:
- if(isdn_ctrl_tab[controller].stateb2 == CHAN_IDLE)
+ if (isdn_ctrl_tab[controller].stateb[channel] == CHAN_IDLE)
{
- DBGL(DL_CNST, (log(LL_DBG, "set_channel_idle: controller [%d] channel B2 already idle!", controller)));
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_idle: controller [%d] channel B%d already idle!", controller, channel+1)));
}
else
{
- isdn_ctrl_tab[controller].stateb2 = CHAN_IDLE;
- DBGL(DL_CNST, (log(LL_DBG, "set_channel_idle: controller [%d] channel B2 set to IDLE!", controller)));
- }
- break;
-
- default:
- DBGL(DL_CNST, (log(LL_DBG, "set_channel_idle: controller [%d], invalid channel [%d]!", controller, channel)));
- return(ERROR);
- break;
+ isdn_ctrl_tab[controller].stateb[channel] = CHAN_IDLE;
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_idle: controller [%d] channel B%d set to IDLE!", controller, channel+1)));
}
return(GOOD);
}
@@ -491,22 +511,13 @@ ret_channel_state(int controller, int channel)
return(ERROR);
}
- switch(channel)
+ if ((channel < 0) || (channel >= isdn_ctrl_tab[controller].nbch))
{
- case CHAN_B1:
- return(isdn_ctrl_tab[controller].stateb1);
- break;
-
- case CHAN_B2:
- return(isdn_ctrl_tab[controller].stateb2);
- break;
-
- default:
- log(LL_ERR, "ret_channel_state: controller [%d], invalid channel [%d]!", controller, channel);
+ log(LL_ERR, "set_channel_busy: controller [%d] invalid channel [%d]!", controller, channel);
return(ERROR);
- break;
}
- return(ERROR);
+
+ return(isdn_ctrl_tab[controller].stateb[channel]);
}
/* EOF */
diff --git a/usr.sbin/i4b/isdnd/isdnd.h b/usr.sbin/i4b/isdnd/isdnd.h
index 64abbd9..fb88546 100644
--- a/usr.sbin/i4b/isdnd/isdnd.h
+++ b/usr.sbin/i4b/isdnd/isdnd.h
@@ -502,21 +502,23 @@ typedef struct cfg_entry {
} cfg_entry_t;
/*---------------------------------------------------------------------------*
- * this struct describes state of controller with 2 b channels
+ * this struct describes state of controller with MAX_BCHAN b channels
*---------------------------------------------------------------------------*/
typedef struct isdn_ctrl_state {
int ctrl_type; /* type: active/passive */
int card_type; /* manufacturer (CARD_XXXX) */
int protocol; /* ISDN D-channel protocol */
+ char* firmware; /* loadable fimrware file name */
+
int state; /* controller state */
#define CTRL_DOWN 0 /* controller inoparable */
#define CTRL_UP 1 /* controller may be used */
- int stateb1; /* B-channel 1 */
- int stateb2; /* B-channel 2 */
+#define MAX_BCHAN 30
+ int stateb[MAX_BCHAN]; /* b channel state */
#define CHAN_IDLE 0 /* channel is free for usage */
#define CHAN_RUN 1 /* channel is occupied */
+ int nbch; /* number of b channels */
int freechans; /* number of unused channels */
-#define MAX_CHANCTRL 2 /* free channels per controller */
int tei; /* tei or -1 if invalid */
int l1stat; /* layer 1 state */
int l2stat; /* layer 2 state */
diff --git a/usr.sbin/i4b/isdnd/isdnd.rc.5 b/usr.sbin/i4b/isdnd/isdnd.rc.5
index 2be90a4..17fbec8 100644
--- a/usr.sbin/i4b/isdnd/isdnd.rc.5
+++ b/usr.sbin/i4b/isdnd/isdnd.rc.5
@@ -1,5 +1,5 @@
.\"
-.\" Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+.\" Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
@@ -22,13 +22,11 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.\" $Id: isdnd.rc.5,v 1.51 2000/10/09 11:17:07 hm Exp $
-.\"
.\" $FreeBSD$
.\"
-.\" last edit-date: [Mon Oct 9 13:12:29 2000]
+.\" last edit-date: [Mon May 21 11:20:26 2001]
.\"
-.Dd October 9, 2000
+.Dd May 21, 2001
.Dt ISDND.RC 5
.Os
.Sh NAME
@@ -295,6 +293,16 @@ ITU Recommendations Q.921 and Q.931.
.It Ar d64s
An ISDN leased line with a single B-channel (called D64S in Germany).
.El
+.It Li firmware
+This is keyword ist used like firmware=/path/to/file to download the
+firmware to active controllers supported by the
+.Em iavc
+driver (AVM B1, T1). This keyword is supported for all controller types,
+and causes I4B_CTRL_DOWNLOAD ioctl to be invoked with the specified file
+as an argument. In systems equipped with both active and passive adapters,
+and the passive cards being detected first, dummy 'controller' entries
+are required for the passive cards to get the correct firmwares to
+correct adapters.
.El
.It Li entry
This keyword starts one configuration entry.
@@ -380,6 +388,11 @@ and calling back the remote site.
The time in seconds to wait for a remote site calling back the local site
after a call from the local site to the remote site has been made.
(optional)
+.It Li clone
+This causes the contents of the specified entry to be copied from the
+existing named entry to the current one.
+When using this feature at least a new entry specific 'name' and
+'usrdeviceunit' value should be specified for the current entry.
.It Li connectprog
specifies a program run every time after a connection is established and
address negotiation is complete (i.e.: the connection is usable).
@@ -982,7 +995,7 @@ ISDN daemon.
The
.Xr isdnd 8
daemon and this manual page were written by
-.An Hellmuth Michaelis Aq hm@kts.org .
+.An Hellmuth Michaelis Aq hm@freebsd.org .
.Pp
Additions to this manual page by
.An Barry Scott Aq barry@scottb.demon.co.uk .
diff --git a/usr.sbin/i4b/isdnd/monitor.c b/usr.sbin/i4b/isdnd/monitor.c
index 2b73066..bd934d1 100644
--- a/usr.sbin/i4b/isdnd/monitor.c
+++ b/usr.sbin/i4b/isdnd/monitor.c
@@ -941,24 +941,21 @@ static void
hangup_channel(int controller, int channel, const char *source)
{
cfg_entry_t * cep = NULL;
+ int i;
if(controller < ncontroller)
{
if(isdn_ctrl_tab[controller].state != CTRL_UP)
return;
- if(isdn_ctrl_tab[controller].stateb1 != CHAN_IDLE)
+ for (i = 0; i < isdn_ctrl_tab[controller].nbch; i++)
{
- cep = get_cep_by_cc(controller, 0);
- if (cep != NULL && cep->isdnchannelused == channel &&
- cep->isdncontrollerused == controller)
- goto found;
- }
- if(isdn_ctrl_tab[controller].stateb2 != CHAN_IDLE)
+ if(isdn_ctrl_tab[controller].stateb[i] != CHAN_IDLE)
{
- cep = get_cep_by_cc(controller, 1);
+ cep = get_cep_by_cc(controller, i);
if (cep != NULL && cep->isdnchannelused == channel &&
cep->isdncontrollerused == controller)
goto found;
+ }
}
}
/* not found */
diff --git a/usr.sbin/i4b/isdnd/rc_config.c b/usr.sbin/i4b/isdnd/rc_config.c
index 2611743..0b63a09 100644
--- a/usr.sbin/i4b/isdnd/rc_config.c
+++ b/usr.sbin/i4b/isdnd/rc_config.c
@@ -170,6 +170,7 @@ set_config_defaults(void)
for(i=0; i < ncontroller; i++)
{
isdn_ctrl_tab[i].protocol = PROTOCOL_DSS1;
+ isdn_ctrl_tab[i].firmware = NULL;
}
/* entry section cleanup */
@@ -568,6 +569,42 @@ cfg_setval(int keyword)
cfg_entry_tab[entrycount].calledbackwait = yylval.num;
break;
+ case CLONE:
+ /*
+ * clone = <entryname>
+ * Loads the entry from the named, existing one.
+ * Fields such as name and usrdeviceunit should
+ * always be specified after clone as they must be
+ * unique.
+ *
+ * NOTE: all malloc()'d fields must be dup()'d here,
+ * we can't have multiple references to same storage.
+ */
+ for (i = 0; i < entrycount; i++)
+ if (!strcmp(cfg_entry_tab[i].name, yylval.str))
+ break;
+ if (i == entrycount) {
+ log(LL_ERR, "entry %d: clone, unknown entry %s!", entrycount, yylval.str);
+ do_exit(1);
+ }
+
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: clone = %s", entrycount, yylval.str)));
+
+ memcpy(&cfg_entry_tab[entrycount], &cfg_entry_tab[i],
+ sizeof(cfg_entry_tab[0]));
+
+ if (cfg_entry_tab[entrycount].answerprog)
+ cfg_entry_tab[entrycount].answerprog = strdup(cfg_entry_tab[entrycount].answerprog);
+ if (cfg_entry_tab[entrycount].budget_callbacks_file)
+ cfg_entry_tab[entrycount].budget_callbacks_file = strdup(cfg_entry_tab[entrycount].budget_callbacks_file);
+ if (cfg_entry_tab[entrycount].budget_callouts_file)
+ cfg_entry_tab[entrycount].budget_callouts_file = strdup(cfg_entry_tab[entrycount].budget_callouts_file);
+ if (cfg_entry_tab[entrycount].connectprog)
+ cfg_entry_tab[entrycount].connectprog = strdup(cfg_entry_tab[entrycount].connectprog);
+ if (cfg_entry_tab[entrycount].disconnectprog)
+ cfg_entry_tab[entrycount].disconnectprog = strdup(cfg_entry_tab[entrycount].disconnectprog);
+ break;
+
case CONNECTPROG:
if((cfg_entry_tab[entrycount].connectprog = malloc(strlen(yylval.str)+1)) == NULL)
{
@@ -657,6 +694,11 @@ cfg_setval(int keyword)
extcallattr = yylval.booln;
break;
+ case FIRMWARE:
+ DBGL(DL_RCCF, (log(LL_DBG, "controller %d: firmware = %s", controllercount, yylval.str)));
+ isdn_ctrl_tab[controllercount].firmware = strdup(yylval.str);
+ break;
+
case HOLIDAYFILE:
strcpy(holidayfile, yylval.str);
DBGL(DL_RCCF, (log(LL_DBG, "system: holidayfile = %s", yylval.str)));
@@ -696,25 +738,20 @@ cfg_setval(int keyword)
break;
case ISDNCHANNEL:
- switch(yylval.num)
+ if (yylval.num == 0 || yylval.num == -1)
{
- case 0:
- case -1:
cfg_entry_tab[entrycount].isdnchannel = CHAN_ANY;
DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdnchannel = any", entrycount)));
- break;
- case 1:
- cfg_entry_tab[entrycount].isdnchannel = CHAN_B1;
- DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdnchannel = one", entrycount)));
- break;
- case 2:
- cfg_entry_tab[entrycount].isdnchannel = CHAN_B2;
- DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdnchannel = two", entrycount)));
- break;
- default:
+ }
+ else if (yylval.num > MAX_BCHAN)
+ {
log(LL_DBG, "entry %d: isdnchannel value out of range", entrycount);
config_error_flag++;
- break;
+ }
+ else
+ {
+ cfg_entry_tab[entrycount].isdnchannel = yylval.num-1;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdnchannel = B%d", entrycount, yylval.num)));
}
break;
@@ -1523,11 +1560,8 @@ print_config(void)
case CHAN_ANY:
fprintf(PFILE, "-1\t\t# any ISDN B-channel may be used\n");
break;
- case CHAN_B1:
- fprintf(PFILE, "1\t\t# only ISDN B-channel 1 may be used\n");
- break;
- case CHAN_B2:
- fprintf(PFILE, "2\t\t# only ISDN B-channel 2 ay be used\n");
+ default:
+ fprintf(PFILE, "%d\t\t# only ISDN B-channel %d may be used\n", cep->isdnchannel+1, cep->isdnchannel+1);
break;
}
diff --git a/usr.sbin/i4b/isdnd/rc_parse.y b/usr.sbin/i4b/isdnd/rc_parse.y
index 428adc0..2147325 100644
--- a/usr.sbin/i4b/isdnd/rc_parse.y
+++ b/usr.sbin/i4b/isdnd/rc_parse.y
@@ -1,7 +1,7 @@
/*
* Copyright (c) 1997 Joerg Wunsch. All rights reserved.
*
- * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,11 +30,9 @@
* i4b daemon - runtime configuration parser
* -----------------------------------------
*
- * $Id: rc_parse.y,v 1.30 2000/10/09 11:17:07 hm Exp $
- *
* $FreeBSD$
*
- * last edit-date: [Mon Oct 2 22:51:23 2000]
+ * last edit-date: [Mon May 21 11:22:21 2001]
*
*---------------------------------------------------------------------------*/
@@ -94,6 +92,7 @@ int controllercount = -1;
%token CALLIN
%token CALLOUT
%token CHANNELSTATE
+%token CLONE
%token CONNECTPROG
%token CONTROLLER
%token DIALOUTTYPE
@@ -106,6 +105,7 @@ int controllercount = -1;
%token EARLYHANGUP
%token ENTRY
%token EXTCALLATTR
+%token FIRMWARE
%token FULLCMD
%token HOLIDAYFILE
%token IDLETIME_IN
@@ -170,7 +170,7 @@ int controllercount = -1;
%type <num> sysfilekeyword sysnumkeyword sysstrkeyword sysboolkeyword
%type <num> filekeyword numkeyword strkeyword boolkeyword monrights monright
-%type <num> cstrkeyword
+%type <num> cstrkeyword cfilekeyword
%type <str> filename
%union {
@@ -441,6 +441,7 @@ strkeyword: ANSWERPROG { $$ = ANSWERPROG; }
| UNITLENGTHSRC { $$ = UNITLENGTHSRC; }
| USRDEVICENAME { $$ = USRDEVICENAME; }
| VALID { $$ = VALID; }
+ | CLONE { $$ = CLONE; }
;
numkeyword: ALERT { $$ = ALERT; }
@@ -502,10 +503,17 @@ strcontroller: cstrkeyword '=' STRING '\n'
{
cfg_setval($1);
}
+ | cfilekeyword '=' filename '\n'
+ {
+ cfg_setval($1);
+ }
;
cstrkeyword: PROTOCOL { $$ = PROTOCOL; }
;
+cfilekeyword: FIRMWARE { $$ = FIRMWARE; }
+ ;
+
%%
diff --git a/usr.sbin/i4b/isdnd/rc_scan.l b/usr.sbin/i4b/isdnd/rc_scan.l
index 92c5189..c9bfce4 100644
--- a/usr.sbin/i4b/isdnd/rc_scan.l
+++ b/usr.sbin/i4b/isdnd/rc_scan.l
@@ -1,7 +1,7 @@
/*
* Copyright (c) 1997 Joerg Wunsch. All rights reserved.
*
- * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,11 +30,9 @@
* i4b daemon - runtime configuration lexical analyzer
* ---------------------------------------------------
*
- * $Id: rc_scan.l,v 1.33 2000/10/09 11:17:07 hm Exp $
- *
* $FreeBSD$
*
- * last edit-date: [Wed Jan 10 10:37:11 2001]
+ * last edit-date: [Mon May 21 11:22:38 2001]
*
*---------------------------------------------------------------------------*/
@@ -105,6 +103,7 @@ budget-calloutsfile { return BUDGETCALLOUTSFILE; }
budget-calloutsfile-rotate { return BUDGETCALLOUTSFILEROTATE; }
callbackwait { return CALLBACKWAIT; }
calledbackwait { return CALLEDBACKWAIT; }
+clone { return CLONE; }
connectprog { return CONNECTPROG; }
controller { return CONTROLLER; }
dialin-reaction { return REACTION; }
@@ -118,6 +117,7 @@ downtime { return DOWNTIME; }
earlyhangup { return EARLYHANGUP; }
entry { return ENTRY; }
extcallattr { return EXTCALLATTR; }
+firmware { return FIRMWARE; }
holidayfile { return HOLIDAYFILE; }
idletime-incoming { return IDLETIME_IN; }
idletime-outgoing { return IDLETIME_OUT; }
diff --git a/usr.sbin/i4b/isdnd/support.c b/usr.sbin/i4b/isdnd/support.c
index b078b0d..f9585c3 100644
--- a/usr.sbin/i4b/isdnd/support.c
+++ b/usr.sbin/i4b/isdnd/support.c
@@ -230,6 +230,8 @@ find_by_device_for_dialoutnumber(int drivertype, int driverunit, int cmdlen, cha
int
setup_dialout(cfg_entry_t *cep)
{
+ int i;
+
/* check controller operational */
if((get_controller_state(cep->isdncontroller)) != CTRL_UP)
@@ -244,19 +246,14 @@ setup_dialout(cfg_entry_t *cep)
switch(cep->isdnchannel)
{
- case CHAN_B1:
- case CHAN_B2:
- if((ret_channel_state(cep->isdncontroller, cep->isdnchannel)) != CHAN_IDLE)
+ case CHAN_ANY:
+ for (i = 0; i < isdn_ctrl_tab[cep->isdncontroller].nbch; i++)
{
- DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s, channel not free", cep->name)));
- return(ERROR);
- }
- cep->isdnchannelused = cep->isdnchannel;
+ if(ret_channel_state(cep->isdncontroller, i) == CHAN_IDLE)
break;
+ }
- case CHAN_ANY:
- if(((ret_channel_state(cep->isdncontroller, CHAN_B1)) != CHAN_IDLE) &&
- ((ret_channel_state(cep->isdncontroller, CHAN_B2)) != CHAN_IDLE))
+ if (i == isdn_ctrl_tab[cep->isdncontroller].nbch)
{
DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s, no channel free", cep->name)));
return(ERROR);
@@ -265,8 +262,12 @@ setup_dialout(cfg_entry_t *cep)
break;
default:
- DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s, channel undefined", cep->name)));
+ if((ret_channel_state(cep->isdncontroller, cep->isdnchannel)) != CHAN_IDLE)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s, channel not free", cep->name)));
return(ERROR);
+ }
+ cep->isdnchannelused = cep->isdnchannel;
break;
}
@@ -460,19 +461,14 @@ find_matching_entry_incoming(msg_connect_ind_t *mp)
switch(mp->channel)
{
- case CHAN_B1:
- case CHAN_B2:
- if((ret_channel_state(mp->controller, mp->channel)) != CHAN_IDLE)
+ case CHAN_ANY:
+ for (i = 0; i < isdn_ctrl_tab[mp->controller].nbch; i++)
{
- log(LL_CHD, "%05d %s incoming call, channel %s not free!",
- mp->header.cdid, cep->name, mp->channel == CHAN_B1 ? "B1" : "B2");
- return(NULL);
- }
+ if(ret_channel_state(mp->controller, i) == CHAN_IDLE)
break;
+ }
- case CHAN_ANY:
- if(((ret_channel_state(mp->controller, CHAN_B1)) != CHAN_IDLE) &&
- ((ret_channel_state(mp->controller, CHAN_B2)) != CHAN_IDLE))
+ if (i == isdn_ctrl_tab[mp->controller].nbch)
{
log(LL_CHD, "%05d %s incoming call, no channel free!",
mp->header.cdid, cep->name);
@@ -487,9 +483,12 @@ find_matching_entry_incoming(msg_connect_ind_t *mp)
break;
default:
- log(LL_CHD, "%05d %s incoming call, ERROR, channel undefined!",
- mp->header.cdid, cep->name);
+ if((ret_channel_state(mp->controller, mp->channel)) != CHAN_IDLE)
+ {
+ log(LL_CHD, "%05d %s incoming call, channel B%d not free!",
+ mp->header.cdid, cep->name, mp->channel+1);
return(NULL);
+ }
break;
}
@@ -569,7 +568,7 @@ get_cep_by_cc(int ctrlr, int chan)
{
int i;
- if((chan != CHAN_B1) && (chan != CHAN_B2))
+ if((chan < 0) || (chan >= isdn_ctrl_tab[ctrlr].nbch))
return(NULL);
for(i=0; i < nentries; i++)
@@ -724,7 +723,7 @@ unitlen_chkupd(cfg_entry_t *cep)
void
close_allactive(void)
{
- int i, j;
+ int i, j, k;
cfg_entry_t *cep = NULL;
j = 0;
@@ -734,25 +733,11 @@ close_allactive(void)
if((get_controller_state(i)) != CTRL_UP)
continue;
- if((ret_channel_state(i, CHAN_B1)) == CHAN_RUN)
+ for (k = 0; k < isdn_ctrl_tab[i].nbch; k++)
{
- if((cep = get_cep_by_cc(i, CHAN_B1)) != NULL)
+ if((ret_channel_state(i, k)) == CHAN_RUN)
{
-#ifdef USE_CURSES
- if(do_fullscreen)
- display_disconnect(cep);
-#endif
-#ifdef I4B_EXTERNAL_MONITOR
- monitor_evnt_disconnect(cep);
-#endif
- next_state(cep, EV_DRQ);
- j++;
- }
- }
-
- if((ret_channel_state(i, CHAN_B2)) == CHAN_RUN)
- {
- if((cep = get_cep_by_cc(i, CHAN_B2)) != NULL)
+ if((cep = get_cep_by_cc(i, k)) != NULL)
{
#ifdef USE_CURSES
if(do_fullscreen)
@@ -764,6 +749,7 @@ close_allactive(void)
next_state(cep, EV_DRQ);
j++;
}
+ }
}
}
diff --git a/usr.sbin/i4b/isdndebug/main.c b/usr.sbin/i4b/isdndebug/main.c
index 89a8910..0620b3b 100644
--- a/usr.sbin/i4b/isdndebug/main.c
+++ b/usr.sbin/i4b/isdndebug/main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
+ * Copyright (c) 1997, 2001 Hellmuth Michaelis. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -27,11 +27,9 @@
* main.c - i4b set debug options
* ------------------------------
*
- * $Id: main.c,v 1.27 2000/07/24 12:22:08 hm Exp $
- *
* $FreeBSD$
*
- * last edit-date: [Thu Oct 26 08:50:30 2000]
+ * last edit-date: [Mon May 21 10:09:23 2001]
*
*---------------------------------------------------------------------------*/
@@ -617,19 +615,18 @@ void
printl4(unsigned long val)
{
printf("\nLayer 4: %s = 0x%lX\n", bin_str(val, 32), val);
- printf(" |||| |||| ||||\n"),
- printf(" |||| |||| |||+- general error messages\n");
- printf(" |||| |||| ||+-- general messages\n");
- printf(" |||| |||| |+--- B-ch timeout messages\n");
- printf(" |||| |||| +---- network driver dial state\n");
- printf(" |||| |||+------ ipr driver debug messages\n");
- printf(" |||| ||+------- rbch driver debug messages\n");
- printf(" |||| |+-------- isp driver debug messages\n");
- printf(" |||| +--------- tel driver debug messages\n");
- printf(" |||+----------- tina driver debug messages\n");
- printf(" ||+------------ tina driver messages\n");
- printf(" |+------------- tina driver error messages\n");
- printf(" +-------------- ing driver debug messages\n");
+ printf(" ||| |||| ||||\n"),
+ printf(" ||| |||| |||+- general error messages\n");
+ printf(" ||| |||| ||+-- general messages\n");
+ printf(" ||| |||| |+--- B-ch timeout messages\n");
+ printf(" ||| |||| +---- network driver dial state\n");
+ printf(" ||| |||+------ ipr driver debug messages\n");
+ printf(" ||| ||+------- rbch driver debug messages\n");
+ printf(" ||| |+-------- isp driver debug messages\n");
+ printf(" ||| +--------- tel driver debug messages\n");
+ printf(" ||+----------- ing driver debug messages\n");
+ printf(" |+------------ iavc driver debug messages\n");
+ printf(" +------------- capi driver debug messages\n");
printf(" ++++-++++-++++-++++-++++---------------- unassigned\n");
}
diff --git a/usr.sbin/i4b/man/Makefile b/usr.sbin/i4b/man/Makefile
index 2600080..f75cd6b 100644
--- a/usr.sbin/i4b/man/Makefile
+++ b/usr.sbin/i4b/man/Makefile
@@ -1,6 +1,7 @@
# $FreeBSD$
MAN = i4b.4 i4bctl.4 i4bipr.4 i4bq921.4 i4bq931.4 i4brbch.4 i4btel.4 \
- i4btrc.4 isic.4 i4bisppp.4 iwic.4 ifpi.4 ifpnp.4 ihfc.4 itjc.4
+ i4btrc.4 isic.4 i4bisppp.4 iwic.4 ifpi.4 ifpnp.4 ihfc.4 itjc.4 \
+ i4bcapi.4 iavc.4
.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/man/i4bcapi.4 b/usr.sbin/i4b/man/i4bcapi.4
new file mode 100644
index 0000000..3539b03
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bcapi.4
@@ -0,0 +1,55 @@
+.\"
+.\" Copyright (c) 2001 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Fri May 25 09:38:45 2001]
+.\"
+.Dd May 21, 2001
+.Dt I4BCAPI 4
+.Os
+.Sh NAME
+.Nm i4bcapi
+.Nd CAPI driver for the isdn4bsd kernel part
+.Sh SYNOPSIS
+.Cd device \&"i4bcapi\&"
+.Sh DESCRIPTION
+.Nm
+is a CAPI driver for the
+.Em isdn4bsd
+package. It sits between layer 4 of isdn4bsd and a driver for an active
+ISDN card; currently only the
+.Xr iavc 4
+driver for the AVM B1 and T1 family of active cards is supported.
+.Sh STANDARDS
+CAPI 2.0 (http://www.capi.org/)
+.Sh SEE ALSO
+.Xr iavc 4
+.Sh AUTHORS
+The
+.Nm
+device driver was written by
+.An Juha-Matti Liukkonen (Cubical Solutions Ltd, Finnland) Aq jml@cubical.fi .
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@freebsd.org .
diff --git a/usr.sbin/i4b/man/iavc.4 b/usr.sbin/i4b/man/iavc.4
new file mode 100644
index 0000000..7c68d1d
--- /dev/null
+++ b/usr.sbin/i4b/man/iavc.4
@@ -0,0 +1,67 @@
+.\"
+.\" Copyright (c) 2001 Hellmuth Michaelis. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\" last edit-date: [Fri May 25 09:45:35 2001]
+.\"
+.Dd May 22, 2001
+.Dt IAVC 4
+.Os
+.Sh NAME
+.Nm iavc
+.Nd isdn4bsd AVM B1/T1 driver
+.Sh SYNOPSIS
+.Cd "device iavc"
+.Pp
+NOTE:
+For the B1 ISA card
+.Pp
+.Dl hint.iavc.0.at="isa"
+.Dl hint.iavc.0.port="0x150"
+.Dl hint.iavc.0.irq="5"
+.Pp
+must be added to
+.Pa /boot/device.hints
+.Pp
+.Sh DESCRIPTION
+The
+.Nm
+driver is used to access the AVM family of active cards to the
+.Xr i4bcapi 4
+driver and the
+.Em isdn4bsd
+package. Currently the AVM B1 PCI, the AVM B1 ISA and the AVM T1 PCI
+cards are supported.
+.Sh STANDARDS
+CAPI 2.0 (http://www.capi.org/)
+.Sh SEE ALSO
+.Xr i4bcapi 4
+.Sh AUTHORS
+The
+.Nm
+device driver was written by
+.An Juha-Matti Liukkonen (Cubical Solutions Ltd, Finnland) Aq jml@cubical.fi .
+This manpage was written by
+.An Hellmuth Michaelis Aq hm@freebsd.org .
OpenPOWER on IntegriCloud