Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,10 @@
config ISDN_DRV_PCBIT
tristate "PCBIT-D support"
depends on ISA && (BROKEN || X86)
help
This enables support for the PCBIT ISDN-card. This card is
manufactured in Portugal by Octal. For running this card,
additional firmware is necessary, which has to be downloaded into
the card using a utility which is distributed separately. See
<file:Documentation/isdn/README> and
<file:Documentation/isdn/README.pcbit> for more information.

View file

@ -0,0 +1,9 @@
# Makefile for the pcbit ISDN device driver
# Each configuration option enables a list of files.
obj-$(CONFIG_ISDN_DRV_PCBIT) += pcbit.o
# Multipart objects.
pcbit-y := module.o edss1.o drv.o layer2.o capi.o callbacks.o

View file

@ -0,0 +1,345 @@
/*
* Callbacks for the FSM
*
* Copyright (C) 1996 Universidade de Lisboa
*
* Written by Pedro Roque Marques (roque@di.fc.ul.pt)
*
* This software may be used and distributed according to the terms of
* the GNU General Public License, incorporated herein by reference.
*/
/*
* Fix: 19981230 - Carlos Morgado <chbm@techie.com>
* Port of Nelson Escravana's <nelson.escravana@usa.net> fix to CalledPN
* NULL pointer dereference in cb_in_1 (originally fixed in 2.0)
*/
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/skbuff.h>
#include <asm/io.h>
#include <linux/isdnif.h>
#include "pcbit.h"
#include "layer2.h"
#include "edss1.h"
#include "callbacks.h"
#include "capi.h"
ushort last_ref_num = 1;
/*
* send_conn_req
*
*/
void cb_out_1(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *cbdata)
{
struct sk_buff *skb;
int len;
ushort refnum;
#ifdef DEBUG
printk(KERN_DEBUG "Called Party Number: %s\n",
cbdata->data.setup.CalledPN);
#endif
/*
* hdr - kmalloc in capi_conn_req
* - kfree when msg has been sent
*/
if ((len = capi_conn_req(cbdata->data.setup.CalledPN, &skb,
chan->proto)) < 0)
{
printk("capi_conn_req failed\n");
return;
}
refnum = last_ref_num++ & 0x7fffU;
chan->callref = 0;
chan->layer2link = 0;
chan->snum = 0;
chan->s_refnum = refnum;
pcbit_l2_write(dev, MSG_CONN_REQ, refnum, skb, len);
}
/*
* rcv CONNECT
* will go into ACTIVE state
* send CONN_ACTIVE_RESP
* send Select protocol request
*/
void cb_out_2(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data)
{
isdn_ctrl ictl;
struct sk_buff *skb;
int len;
ushort refnum;
if ((len = capi_conn_active_resp(chan, &skb)) < 0)
{
printk("capi_conn_active_req failed\n");
return;
}
refnum = last_ref_num++ & 0x7fffU;
chan->s_refnum = refnum;
pcbit_l2_write(dev, MSG_CONN_ACTV_RESP, refnum, skb, len);
ictl.command = ISDN_STAT_DCONN;
ictl.driver = dev->id;
ictl.arg = chan->id;
dev->dev_if->statcallb(&ictl);
/* ACTIVE D-channel */
/* Select protocol */
if ((len = capi_select_proto_req(chan, &skb, 1 /*outgoing*/)) < 0) {
printk("capi_select_proto_req failed\n");
return;
}
refnum = last_ref_num++ & 0x7fffU;
chan->s_refnum = refnum;
pcbit_l2_write(dev, MSG_SELP_REQ, refnum, skb, len);
}
/*
* Incoming call received
* inform user
*/
void cb_in_1(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *cbdata)
{
isdn_ctrl ictl;
unsigned short refnum;
struct sk_buff *skb;
int len;
ictl.command = ISDN_STAT_ICALL;
ictl.driver = dev->id;
ictl.arg = chan->id;
/*
* ictl.num >= strlen() + strlen() + 5
*/
if (cbdata->data.setup.CallingPN == NULL) {
printk(KERN_DEBUG "NULL CallingPN to phone; using 0\n");
strcpy(ictl.parm.setup.phone, "0");
}
else {
strcpy(ictl.parm.setup.phone, cbdata->data.setup.CallingPN);
}
if (cbdata->data.setup.CalledPN == NULL) {
printk(KERN_DEBUG "NULL CalledPN to eazmsn; using 0\n");
strcpy(ictl.parm.setup.eazmsn, "0");
}
else {
strcpy(ictl.parm.setup.eazmsn, cbdata->data.setup.CalledPN);
}
ictl.parm.setup.si1 = 7;
ictl.parm.setup.si2 = 0;
ictl.parm.setup.plan = 0;
ictl.parm.setup.screen = 0;
#ifdef DEBUG
printk(KERN_DEBUG "statstr: %s\n", ictl.num);
#endif
dev->dev_if->statcallb(&ictl);
if ((len = capi_conn_resp(chan, &skb)) < 0) {
printk(KERN_DEBUG "capi_conn_resp failed\n");
return;
}
refnum = last_ref_num++ & 0x7fffU;
chan->s_refnum = refnum;
pcbit_l2_write(dev, MSG_CONN_RESP, refnum, skb, len);
}
/*
* user has replied
* open the channel
* send CONNECT message CONNECT_ACTIVE_REQ in CAPI
*/
void cb_in_2(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data)
{
unsigned short refnum;
struct sk_buff *skb;
int len;
if ((len = capi_conn_active_req(chan, &skb)) < 0) {
printk(KERN_DEBUG "capi_conn_active_req failed\n");
return;
}
refnum = last_ref_num++ & 0x7fffU;
chan->s_refnum = refnum;
printk(KERN_DEBUG "sending MSG_CONN_ACTV_REQ\n");
pcbit_l2_write(dev, MSG_CONN_ACTV_REQ, refnum, skb, len);
}
/*
* CONN_ACK arrived
* start b-proto selection
*
*/
void cb_in_3(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data)
{
unsigned short refnum;
struct sk_buff *skb;
int len;
if ((len = capi_select_proto_req(chan, &skb, 0 /*incoming*/)) < 0)
{
printk("capi_select_proto_req failed\n");
return;
}
refnum = last_ref_num++ & 0x7fffU;
chan->s_refnum = refnum;
pcbit_l2_write(dev, MSG_SELP_REQ, refnum, skb, len);
}
/*
* Received disconnect ind on active state
* send disconnect resp
* send msg to user
*/
void cb_disc_1(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data)
{
struct sk_buff *skb;
int len;
ushort refnum;
isdn_ctrl ictl;
if ((len = capi_disc_resp(chan, &skb)) < 0) {
printk("capi_disc_resp failed\n");
return;
}
refnum = last_ref_num++ & 0x7fffU;
chan->s_refnum = refnum;
pcbit_l2_write(dev, MSG_DISC_RESP, refnum, skb, len);
ictl.command = ISDN_STAT_BHUP;
ictl.driver = dev->id;
ictl.arg = chan->id;
dev->dev_if->statcallb(&ictl);
}
/*
* User HANGUP on active/call proceeding state
* send disc.req
*/
void cb_disc_2(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data)
{
struct sk_buff *skb;
int len;
ushort refnum;
if ((len = capi_disc_req(chan->callref, &skb, CAUSE_NORMAL)) < 0)
{
printk("capi_disc_req failed\n");
return;
}
refnum = last_ref_num++ & 0x7fffU;
chan->s_refnum = refnum;
pcbit_l2_write(dev, MSG_DISC_REQ, refnum, skb, len);
}
/*
* Disc confirm received send BHUP
* Problem: when the HL driver sends the disc req itself
* LL receives BHUP
*/
void cb_disc_3(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data)
{
isdn_ctrl ictl;
ictl.command = ISDN_STAT_BHUP;
ictl.driver = dev->id;
ictl.arg = chan->id;
dev->dev_if->statcallb(&ictl);
}
void cb_notdone(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data)
{
}
/*
* send activate b-chan protocol
*/
void cb_selp_1(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data)
{
struct sk_buff *skb;
int len;
ushort refnum;
if ((len = capi_activate_transp_req(chan, &skb)) < 0)
{
printk("capi_conn_activate_transp_req failed\n");
return;
}
refnum = last_ref_num++ & 0x7fffU;
chan->s_refnum = refnum;
pcbit_l2_write(dev, MSG_ACT_TRANSP_REQ, refnum, skb, len);
}
/*
* Inform User that the B-channel is available
*/
void cb_open(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data)
{
isdn_ctrl ictl;
ictl.command = ISDN_STAT_BCONN;
ictl.driver = dev->id;
ictl.arg = chan->id;
dev->dev_if->statcallb(&ictl);
}

View file

@ -0,0 +1,44 @@
/*
* Callbacks prototypes for FSM
*
* Copyright (C) 1996 Universidade de Lisboa
*
* Written by Pedro Roque Marques (roque@di.fc.ul.pt)
*
* This software may be used and distributed according to the terms of
* the GNU General Public License, incorporated herein by reference.
*/
#ifndef CALLBACKS_H
#define CALLBACKS_H
extern void cb_out_1(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data);
extern void cb_out_2(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data);
extern void cb_in_1(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data);
extern void cb_in_2(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data);
extern void cb_in_3(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data);
extern void cb_disc_1(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data);
extern void cb_disc_2(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data);
extern void cb_disc_3(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data);
extern void cb_notdone(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data);
extern void cb_selp_1(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data);
extern void cb_open(struct pcbit_dev *dev, struct pcbit_chan *chan,
struct callb_data *data);
#endif

649
drivers/isdn/pcbit/capi.c Normal file
View file

@ -0,0 +1,649 @@
/*
* CAPI encoder/decoder for
* Portugal Telecom CAPI 2.0
*
* Copyright (C) 1996 Universidade de Lisboa
*
* Written by Pedro Roque Marques (roque@di.fc.ul.pt)
*
* This software may be used and distributed according to the terms of
* the GNU General Public License, incorporated herein by reference.
*
* Not compatible with the AVM Gmbh. CAPI 2.0
*
*/
/*
* Documentation:
* - "Common ISDN API - Perfil Português - Versão 2.1",
* Telecom Portugal, Fev 1992.
* - "Common ISDN API - Especificação de protocolos para
* acesso aos canais B", Inesc, Jan 1994.
*/
/*
* TODO: better decoding of Information Elements
* for debug purposes mainly
* encode our number in CallerPN and ConnectedPN
*/
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/skbuff.h>
#include <asm/io.h>
#include <asm/string.h>
#include <linux/isdnif.h>
#include "pcbit.h"
#include "edss1.h"
#include "capi.h"
/*
* Encoding of CAPI messages
*
*/
int capi_conn_req(const char *calledPN, struct sk_buff **skb, int proto)
{
ushort len;
/*
* length
* AppInfoMask - 2
* BC0 - 3
* BC1 - 1
* Chan - 2
* Keypad - 1
* CPN - 1
* CPSA - 1
* CalledPN - 2 + strlen
* CalledPSA - 1
* rest... - 4
* ----------------
* Total 18 + strlen
*/
len = 18 + strlen(calledPN);
if (proto == ISDN_PROTO_L2_TRANS)
len++;
if ((*skb = dev_alloc_skb(len)) == NULL) {
printk(KERN_WARNING "capi_conn_req: alloc_skb failed\n");
return -1;
}
/* InfoElmMask */
*((ushort *)skb_put(*skb, 2)) = AppInfoMask;
if (proto == ISDN_PROTO_L2_TRANS)
{
/* Bearer Capability - Mandatory*/
*(skb_put(*skb, 1)) = 3; /* BC0.Length */
*(skb_put(*skb, 1)) = 0x80; /* Speech */
*(skb_put(*skb, 1)) = 0x10; /* Circuit Mode */
*(skb_put(*skb, 1)) = 0x23; /* A-law */
}
else
{
/* Bearer Capability - Mandatory*/
*(skb_put(*skb, 1)) = 2; /* BC0.Length */
*(skb_put(*skb, 1)) = 0x88; /* Digital Information */
*(skb_put(*skb, 1)) = 0x90; /* BC0.Octect4 */
}
/* Bearer Capability - Optional*/
*(skb_put(*skb, 1)) = 0; /* BC1.Length = 0 */
*(skb_put(*skb, 1)) = 1; /* ChannelID.Length = 1 */
*(skb_put(*skb, 1)) = 0x83; /* Basic Interface - Any Channel */
*(skb_put(*skb, 1)) = 0; /* Keypad.Length = 0 */
*(skb_put(*skb, 1)) = 0; /* CallingPN.Length = 0 */
*(skb_put(*skb, 1)) = 0; /* CallingPSA.Length = 0 */
/* Called Party Number */
*(skb_put(*skb, 1)) = strlen(calledPN) + 1;
*(skb_put(*skb, 1)) = 0x81;
memcpy(skb_put(*skb, strlen(calledPN)), calledPN, strlen(calledPN));
/* '#' */
*(skb_put(*skb, 1)) = 0; /* CalledPSA.Length = 0 */
/* LLC.Length = 0; */
/* HLC0.Length = 0; */
/* HLC1.Length = 0; */
/* UTUS.Length = 0; */
memset(skb_put(*skb, 4), 0, 4);
return len;
}
int capi_conn_resp(struct pcbit_chan *chan, struct sk_buff **skb)
{
if ((*skb = dev_alloc_skb(5)) == NULL) {
printk(KERN_WARNING "capi_conn_resp: alloc_skb failed\n");
return -1;
}
*((ushort *)skb_put(*skb, 2)) = chan->callref;
*(skb_put(*skb, 1)) = 0x01; /* ACCEPT_CALL */
*(skb_put(*skb, 1)) = 0;
*(skb_put(*skb, 1)) = 0;
return 5;
}
int capi_conn_active_req(struct pcbit_chan *chan, struct sk_buff **skb)
{
/*
* 8 bytes
*/
if ((*skb = dev_alloc_skb(8)) == NULL) {
printk(KERN_WARNING "capi_conn_active_req: alloc_skb failed\n");
return -1;
}
*((ushort *)skb_put(*skb, 2)) = chan->callref;
#ifdef DEBUG
printk(KERN_DEBUG "Call Reference: %04x\n", chan->callref);
#endif
*(skb_put(*skb, 1)) = 0; /* BC.Length = 0; */
*(skb_put(*skb, 1)) = 0; /* ConnectedPN.Length = 0 */
*(skb_put(*skb, 1)) = 0; /* PSA.Length */
*(skb_put(*skb, 1)) = 0; /* LLC.Length = 0; */
*(skb_put(*skb, 1)) = 0; /* HLC.Length = 0; */
*(skb_put(*skb, 1)) = 0; /* UTUS.Length = 0; */
return 8;
}
int capi_conn_active_resp(struct pcbit_chan *chan, struct sk_buff **skb)
{
/*
* 2 bytes
*/
if ((*skb = dev_alloc_skb(2)) == NULL) {
printk(KERN_WARNING "capi_conn_active_resp: alloc_skb failed\n");
return -1;
}
*((ushort *)skb_put(*skb, 2)) = chan->callref;
return 2;
}
int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb,
int outgoing)
{
/*
* 18 bytes
*/
if ((*skb = dev_alloc_skb(18)) == NULL) {
printk(KERN_WARNING "capi_select_proto_req: alloc_skb failed\n");
return -1;
}
*((ushort *)skb_put(*skb, 2)) = chan->callref;
/* Layer2 protocol */
switch (chan->proto) {
case ISDN_PROTO_L2_X75I:
*(skb_put(*skb, 1)) = 0x05; /* LAPB */
break;
case ISDN_PROTO_L2_HDLC:
*(skb_put(*skb, 1)) = 0x02;
break;
case ISDN_PROTO_L2_TRANS:
/*
* Voice (a-law)
*/
*(skb_put(*skb, 1)) = 0x06;
break;
default:
#ifdef DEBUG
printk(KERN_DEBUG "Transparent\n");
#endif
*(skb_put(*skb, 1)) = 0x03;
break;
}
*(skb_put(*skb, 1)) = (outgoing ? 0x02 : 0x42); /* Don't ask */
*(skb_put(*skb, 1)) = 0x00;
*((ushort *) skb_put(*skb, 2)) = MRU;
*(skb_put(*skb, 1)) = 0x08; /* Modulo */
*(skb_put(*skb, 1)) = 0x07; /* Max Window */
*(skb_put(*skb, 1)) = 0x01; /* No Layer3 Protocol */
/*
* 2 - layer3 MTU [10]
* - Modulo [12]
* - Window
* - layer1 proto [14]
* - bitrate
* - sub-channel [16]
* - layer1dataformat [17]
*/
memset(skb_put(*skb, 8), 0, 8);
return 18;
}
int capi_activate_transp_req(struct pcbit_chan *chan, struct sk_buff **skb)
{
if ((*skb = dev_alloc_skb(7)) == NULL) {
printk(KERN_WARNING "capi_activate_transp_req: alloc_skb failed\n");
return -1;
}
*((ushort *)skb_put(*skb, 2)) = chan->callref;
*(skb_put(*skb, 1)) = chan->layer2link; /* Layer2 id */
*(skb_put(*skb, 1)) = 0x00; /* Transmit by default */
*((ushort *) skb_put(*skb, 2)) = MRU;
*(skb_put(*skb, 1)) = 0x01; /* Enables reception*/
return 7;
}
int capi_tdata_req(struct pcbit_chan *chan, struct sk_buff *skb)
{
ushort data_len;
/*
* callref - 2
* layer2link - 1
* wBlockLength - 2
* data - 4
* sernum - 1
*/
data_len = skb->len;
if (skb_headroom(skb) < 10)
{
printk(KERN_CRIT "No headspace (%u) on headroom %p for capi header\n", skb_headroom(skb), skb);
}
else
{
skb_push(skb, 10);
}
*((u16 *) (skb->data)) = chan->callref;
skb->data[2] = chan->layer2link;
*((u16 *) (skb->data + 3)) = data_len;
chan->s_refnum = (chan->s_refnum + 1) % 8;
*((u32 *) (skb->data + 5)) = chan->s_refnum;
skb->data[9] = 0; /* HDLC frame number */
return 10;
}
int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff **skb)
{
if ((*skb = dev_alloc_skb(4)) == NULL) {
printk(KERN_WARNING "capi_tdata_resp: alloc_skb failed\n");
return -1;
}
*((ushort *)skb_put(*skb, 2)) = chan->callref;
*(skb_put(*skb, 1)) = chan->layer2link;
*(skb_put(*skb, 1)) = chan->r_refnum;
return (*skb)->len;
}
int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause)
{
if ((*skb = dev_alloc_skb(6)) == NULL) {
printk(KERN_WARNING "capi_disc_req: alloc_skb failed\n");
return -1;
}
*((ushort *)skb_put(*skb, 2)) = callref;
*(skb_put(*skb, 1)) = 2; /* Cause.Length = 2; */
*(skb_put(*skb, 1)) = 0x80;
*(skb_put(*skb, 1)) = 0x80 | cause;
/*
* Change it: we should send 'Sic transit gloria Mundi' here ;-)
*/
*(skb_put(*skb, 1)) = 0; /* UTUS.Length = 0; */
return 6;
}
int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb)
{
if ((*skb = dev_alloc_skb(2)) == NULL) {
printk(KERN_WARNING "capi_disc_resp: alloc_skb failed\n");
return -1;
}
*((ushort *)skb_put(*skb, 2)) = chan->callref;
return 2;
}
/*
* Decoding of CAPI messages
*
*/
int capi_decode_conn_ind(struct pcbit_chan *chan,
struct sk_buff *skb,
struct callb_data *info)
{
int CIlen, len;
/* Call Reference [CAPI] */
chan->callref = *((ushort *)skb->data);
skb_pull(skb, 2);
#ifdef DEBUG
printk(KERN_DEBUG "Call Reference: %04x\n", chan->callref);
#endif
/* Channel Identification */
/* Expect
Len = 1
Octect 3 = 0100 10CC - [ 7 Basic, 4 , 2-1 chan ]
*/
CIlen = skb->data[0];
#ifdef DEBUG
if (CIlen == 1) {
if (((skb->data[1]) & 0xFC) == 0x48)
printk(KERN_DEBUG "decode_conn_ind: chan ok\n");
printk(KERN_DEBUG "phyChan = %d\n", skb->data[1] & 0x03);
}
else
printk(KERN_DEBUG "conn_ind: CIlen = %d\n", CIlen);
#endif
skb_pull(skb, CIlen + 1);
/* Calling Party Number */
/* An "additional service" as far as Portugal Telecom is concerned */
len = skb->data[0];
if (len > 0) {
int count = 1;
#ifdef DEBUG
printk(KERN_DEBUG "CPN: Octect 3 %02x\n", skb->data[1]);
#endif
if ((skb->data[1] & 0x80) == 0)
count = 2;
if (!(info->data.setup.CallingPN = kmalloc(len - count + 1, GFP_ATOMIC)))
return -1;
skb_copy_from_linear_data_offset(skb, count + 1,
info->data.setup.CallingPN,
len - count);
info->data.setup.CallingPN[len - count] = 0;
}
else {
info->data.setup.CallingPN = NULL;
printk(KERN_DEBUG "NULL CallingPN\n");
}
skb_pull(skb, len + 1);
/* Calling Party Subaddress */
skb_pull(skb, skb->data[0] + 1);
/* Called Party Number */
len = skb->data[0];
if (len > 0) {
int count = 1;
if ((skb->data[1] & 0x80) == 0)
count = 2;
if (!(info->data.setup.CalledPN = kmalloc(len - count + 1, GFP_ATOMIC)))
return -1;
skb_copy_from_linear_data_offset(skb, count + 1,
info->data.setup.CalledPN,
len - count);
info->data.setup.CalledPN[len - count] = 0;
}
else {
info->data.setup.CalledPN = NULL;
printk(KERN_DEBUG "NULL CalledPN\n");
}
skb_pull(skb, len + 1);
/* Called Party Subaddress */
skb_pull(skb, skb->data[0] + 1);
/* LLC */
skb_pull(skb, skb->data[0] + 1);
/* HLC */
skb_pull(skb, skb->data[0] + 1);
/* U2U */
skb_pull(skb, skb->data[0] + 1);
return 0;
}
/*
* returns errcode
*/
int capi_decode_conn_conf(struct pcbit_chan *chan, struct sk_buff *skb,
int *complete)
{
int errcode;
chan->callref = *((ushort *)skb->data); /* Update CallReference */
skb_pull(skb, 2);
errcode = *((ushort *) skb->data); /* read errcode */
skb_pull(skb, 2);
*complete = *(skb->data);
skb_pull(skb, 1);
/* FIX ME */
/* This is actually a firmware bug */
if (!*complete)
{
printk(KERN_DEBUG "complete=%02x\n", *complete);
*complete = 1;
}
/* Optional Bearer Capability */
skb_pull(skb, *(skb->data) + 1);
/* Channel Identification */
skb_pull(skb, *(skb->data) + 1);
/* High Layer Compatibility follows */
skb_pull(skb, *(skb->data) + 1);
return errcode;
}
int capi_decode_conn_actv_ind(struct pcbit_chan *chan, struct sk_buff *skb)
{
ushort len;
#ifdef DEBUG
char str[32];
#endif
/* Yet Another Bearer Capability */
skb_pull(skb, *(skb->data) + 1);
/* Connected Party Number */
len = *(skb->data);
#ifdef DEBUG
if (len > 1 && len < 31) {
skb_copy_from_linear_data_offset(skb, 2, str, len - 1);
str[len] = 0;
printk(KERN_DEBUG "Connected Party Number: %s\n", str);
}
else
printk(KERN_DEBUG "actv_ind CPN len = %d\n", len);
#endif
skb_pull(skb, len + 1);
/* Connected Subaddress */
skb_pull(skb, *(skb->data) + 1);
/* Low Layer Capability */
skb_pull(skb, *(skb->data) + 1);
/* High Layer Capability */
skb_pull(skb, *(skb->data) + 1);
return 0;
}
int capi_decode_conn_actv_conf(struct pcbit_chan *chan, struct sk_buff *skb)
{
ushort errcode;
errcode = *((ushort *)skb->data);
skb_pull(skb, 2);
/* Channel Identification
skb_pull(skb, skb->data[0] + 1);
*/
return errcode;
}
int capi_decode_sel_proto_conf(struct pcbit_chan *chan, struct sk_buff *skb)
{
ushort errcode;
chan->layer2link = *(skb->data);
skb_pull(skb, 1);
errcode = *((ushort *)skb->data);
skb_pull(skb, 2);
return errcode;
}
int capi_decode_actv_trans_conf(struct pcbit_chan *chan, struct sk_buff *skb)
{
ushort errcode;
if (chan->layer2link != *(skb->data))
printk("capi_decode_actv_trans_conf: layer2link doesn't match\n");
skb_pull(skb, 1);
errcode = *((ushort *)skb->data);
skb_pull(skb, 2);
return errcode;
}
int capi_decode_disc_ind(struct pcbit_chan *chan, struct sk_buff *skb)
{
ushort len;
#ifdef DEBUG
int i;
#endif
/* Cause */
len = *(skb->data);
skb_pull(skb, 1);
#ifdef DEBUG
for (i = 0; i < len; i++)
printk(KERN_DEBUG "Cause Octect %d: %02x\n", i + 3,
*(skb->data + i));
#endif
skb_pull(skb, len);
return 0;
}
#ifdef DEBUG
int capi_decode_debug_188(u_char *hdr, ushort hdrlen)
{
char str[64];
int len;
len = hdr[0];
if (len < 64 && len == hdrlen - 1) {
memcpy(str, hdr + 1, hdrlen - 1);
str[hdrlen - 1] = 0;
printk("%s\n", str);
}
else
printk("debug message incorrect\n");
return 0;
}
#endif

81
drivers/isdn/pcbit/capi.h Normal file
View file

@ -0,0 +1,81 @@
/*
* CAPI encode/decode prototypes and defines
*
* Copyright (C) 1996 Universidade de Lisboa
*
* Written by Pedro Roque Marques (roque@di.fc.ul.pt)
*
* This software may be used and distributed according to the terms of
* the GNU General Public License, incorporated herein by reference.
*/
#ifndef CAPI_H
#define CAPI_H
#define REQ_CAUSE 0x01
#define REQ_DISPLAY 0x04
#define REQ_USER_TO_USER 0x08
#define AppInfoMask REQ_CAUSE | REQ_DISPLAY | REQ_USER_TO_USER
/* Connection Setup */
extern int capi_conn_req(const char *calledPN, struct sk_buff **buf,
int proto);
extern int capi_decode_conn_conf(struct pcbit_chan *chan, struct sk_buff *skb,
int *complete);
extern int capi_decode_conn_ind(struct pcbit_chan *chan, struct sk_buff *skb,
struct callb_data *info);
extern int capi_conn_resp(struct pcbit_chan *chan, struct sk_buff **skb);
extern int capi_conn_active_req(struct pcbit_chan *chan, struct sk_buff **skb);
extern int capi_decode_conn_actv_conf(struct pcbit_chan *chan,
struct sk_buff *skb);
extern int capi_decode_conn_actv_ind(struct pcbit_chan *chan,
struct sk_buff *skb);
extern int capi_conn_active_resp(struct pcbit_chan *chan,
struct sk_buff **skb);
/* Data */
extern int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb,
int outgoing);
extern int capi_decode_sel_proto_conf(struct pcbit_chan *chan,
struct sk_buff *skb);
extern int capi_activate_transp_req(struct pcbit_chan *chan,
struct sk_buff **skb);
extern int capi_decode_actv_trans_conf(struct pcbit_chan *chan,
struct sk_buff *skb);
extern int capi_tdata_req(struct pcbit_chan *chan, struct sk_buff *skb);
extern int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff **skb);
/* Connection Termination */
extern int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause);
extern int capi_decode_disc_ind(struct pcbit_chan *chan, struct sk_buff *skb);
extern int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb);
#ifdef DEBUG
extern int capi_decode_debug_188(u_char *hdr, ushort hdrlen);
#endif
static inline struct pcbit_chan *
capi_channel(struct pcbit_dev *dev, struct sk_buff *skb)
{
ushort callref;
callref = *((ushort *)skb->data);
skb_pull(skb, 2);
if (dev->b1->callref == callref)
return dev->b1;
else if (dev->b2->callref == callref)
return dev->b2;
return NULL;
}
#endif

1077
drivers/isdn/pcbit/drv.c Normal file

File diff suppressed because it is too large Load diff

313
drivers/isdn/pcbit/edss1.c Normal file
View file

@ -0,0 +1,313 @@
/*
* DSS.1 Finite State Machine
* base: ITU-T Rec Q.931
*
* Copyright (C) 1996 Universidade de Lisboa
*
* Written by Pedro Roque Marques (roque@di.fc.ul.pt)
*
* This software may be used and distributed according to the terms of
* the GNU General Public License, incorporated herein by reference.
*/
/*
* TODO: complete the FSM
* move state/event descriptions to a user space logger
*/
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/skbuff.h>
#include <linux/timer.h>
#include <asm/io.h>
#include <linux/isdnif.h>
#include "pcbit.h"
#include "edss1.h"
#include "layer2.h"
#include "callbacks.h"
const char * const isdn_state_table[] = {
"Closed",
"Call initiated",
"Overlap sending",
"Outgoing call proceeding",
"NOT DEFINED",
"Call delivered",
"Call present",
"Call received",
"Connect request",
"Incoming call proceeding",
"Active",
"Disconnect request",
"Disconnect indication",
"NOT DEFINED",
"NOT DEFINED",
"Suspend request",
"NOT DEFINED",
"Resume request",
"NOT DEFINED",
"Release Request",
"NOT DEFINED",
"NOT DEFINED",
"NOT DEFINED",
"NOT DEFINED",
"NOT DEFINED",
"Overlap receiving",
"Select protocol on B-Channel",
"Activate B-channel protocol"
};
#ifdef DEBUG_ERRS
static
struct CauseValue {
byte nr;
char *descr;
} cvlist[] = {
{0x01, "Unallocated (unassigned) number"},
{0x02, "No route to specified transit network"},
{0x03, "No route to destination"},
{0x04, "Send special information tone"},
{0x05, "Misdialled trunk prefix"},
{0x06, "Channel unacceptable"},
{0x07, "Channel awarded and being delivered in an established channel"},
{0x08, "Preemption"},
{0x09, "Preemption - circuit reserved for reuse"},
{0x10, "Normal call clearing"},
{0x11, "User busy"},
{0x12, "No user responding"},
{0x13, "No answer from user (user alerted)"},
{0x14, "Subscriber absent"},
{0x15, "Call rejected"},
{0x16, "Number changed"},
{0x1a, "non-selected user clearing"},
{0x1b, "Destination out of order"},
{0x1c, "Invalid number format (address incomplete)"},
{0x1d, "Facility rejected"},
{0x1e, "Response to Status enquiry"},
{0x1f, "Normal, unspecified"},
{0x22, "No circuit/channel available"},
{0x26, "Network out of order"},
{0x27, "Permanent frame mode connection out-of-service"},
{0x28, "Permanent frame mode connection operational"},
{0x29, "Temporary failure"},
{0x2a, "Switching equipment congestion"},
{0x2b, "Access information discarded"},
{0x2c, "Requested circuit/channel not available"},
{0x2e, "Precedence call blocked"},
{0x2f, "Resource unavailable, unspecified"},
{0x31, "Quality of service unavailable"},
{0x32, "Requested facility not subscribed"},
{0x35, "Outgoing calls barred within CUG"},
{0x37, "Incoming calls barred within CUG"},
{0x39, "Bearer capability not authorized"},
{0x3a, "Bearer capability not presently available"},
{0x3e, "Inconsistency in designated outgoing access information and subscriber class"},
{0x3f, "Service or option not available, unspecified"},
{0x41, "Bearer capability not implemented"},
{0x42, "Channel type not implemented"},
{0x43, "Requested facility not implemented"},
{0x44, "Only restricted digital information bearer capability is available"},
{0x4f, "Service or option not implemented"},
{0x51, "Invalid call reference value"},
{0x52, "Identified channel does not exist"},
{0x53, "A suspended call exists, but this call identity does not"},
{0x54, "Call identity in use"},
{0x55, "No call suspended"},
{0x56, "Call having the requested call identity has been cleared"},
{0x57, "User not member of CUG"},
{0x58, "Incompatible destination"},
{0x5a, "Non-existent CUG"},
{0x5b, "Invalid transit network selection"},
{0x5f, "Invalid message, unspecified"},
{0x60, "Mandatory information element is missing"},
{0x61, "Message type non-existent or not implemented"},
{0x62, "Message not compatible with call state or message type non-existent or not implemented"},
{0x63, "Information element/parameter non-existent or not implemented"},
{0x64, "Invalid information element contents"},
{0x65, "Message not compatible with call state"},
{0x66, "Recovery on timer expiry"},
{0x67, "Parameter non-existent or not implemented - passed on"},
{0x6e, "Message with unrecognized parameter discarded"},
{0x6f, "Protocol error, unspecified"},
{0x7f, "Interworking, unspecified"}
};
#endif
static struct isdn_event_desc {
unsigned short ev;
char *desc;
} isdn_event_table[] = {
{EV_USR_SETUP_REQ, "CC->L3: Setup Request"},
{EV_USR_SETUP_RESP, "CC->L3: Setup Response"},
{EV_USR_PROCED_REQ, "CC->L3: Proceeding Request"},
{EV_USR_RELEASE_REQ, "CC->L3: Release Request"},
{EV_NET_SETUP, "NET->TE: setup "},
{EV_NET_CALL_PROC, "NET->TE: call proceeding"},
{EV_NET_SETUP_ACK, "NET->TE: setup acknowledge (more info needed)"},
{EV_NET_CONN, "NET->TE: connect"},
{EV_NET_CONN_ACK, "NET->TE: connect acknowledge"},
{EV_NET_DISC, "NET->TE: disconnect indication"},
{EV_NET_RELEASE, "NET->TE: release"},
{EV_NET_RELEASE_COMP, "NET->TE: release complete"},
{EV_NET_SELP_RESP, "Board: Select B-channel protocol ack"},
{EV_NET_ACTV_RESP, "Board: Activate B-channel protocol ack"},
{EV_TIMER, "Timeout"},
{0, "NULL"}
};
char *strisdnevent(ushort ev)
{
struct isdn_event_desc *entry;
for (entry = isdn_event_table; entry->ev; entry++)
if (entry->ev == ev)
break;
return entry->desc;
}
/*
* Euro ISDN finite state machine
*/
static struct fsm_timer_entry fsm_timers[] = {
{ST_CALL_PROC, 10},
{ST_DISC_REQ, 2},
{ST_ACTIVE_SELP, 5},
{ST_ACTIVE_ACTV, 5},
{ST_INCM_PROC, 10},
{ST_CONN_REQ, 2},
{0xff, 0}
};
static struct fsm_entry fsm_table[] = {
/* Connect Phase */
/* Outgoing */
{ST_NULL, ST_CALL_INIT, EV_USR_SETUP_REQ, cb_out_1},
{ST_CALL_INIT, ST_OVER_SEND, EV_NET_SETUP_ACK, cb_notdone},
{ST_CALL_INIT, ST_CALL_PROC, EV_NET_CALL_PROC, NULL},
{ST_CALL_INIT, ST_NULL, EV_NET_DISC, cb_out_2},
{ST_CALL_PROC, ST_ACTIVE_SELP, EV_NET_CONN, cb_out_2},
{ST_CALL_PROC, ST_NULL, EV_NET_DISC, cb_disc_1},
{ST_CALL_PROC, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
/* Incoming */
{ST_NULL, ST_CALL_PRES, EV_NET_SETUP, NULL},
{ST_CALL_PRES, ST_INCM_PROC, EV_USR_PROCED_REQ, cb_in_1},
{ST_CALL_PRES, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
{ST_INCM_PROC, ST_CONN_REQ, EV_USR_SETUP_RESP, cb_in_2},
{ST_INCM_PROC, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
{ST_CONN_REQ, ST_ACTIVE_SELP, EV_NET_CONN_ACK, cb_in_3},
/* Active */
{ST_ACTIVE, ST_NULL, EV_NET_DISC, cb_disc_1},
{ST_ACTIVE, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
{ST_ACTIVE, ST_NULL, EV_NET_RELEASE, cb_disc_3},
/* Disconnect */
{ST_DISC_REQ, ST_NULL, EV_NET_DISC, cb_disc_1},
{ST_DISC_REQ, ST_NULL, EV_NET_RELEASE, cb_disc_3},
/* protocol selection */
{ST_ACTIVE_SELP, ST_ACTIVE_ACTV, EV_NET_SELP_RESP, cb_selp_1},
{ST_ACTIVE_SELP, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
{ST_ACTIVE_ACTV, ST_ACTIVE, EV_NET_ACTV_RESP, cb_open},
{ST_ACTIVE_ACTV, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
/* Timers */
{ST_CALL_PROC, ST_DISC_REQ, EV_TIMER, cb_disc_2},
{ST_DISC_REQ, ST_NULL, EV_TIMER, cb_disc_3},
{ST_ACTIVE_SELP, ST_DISC_REQ, EV_TIMER, cb_disc_2},
{ST_ACTIVE_ACTV, ST_DISC_REQ, EV_TIMER, cb_disc_2},
{ST_INCM_PROC, ST_DISC_REQ, EV_TIMER, cb_disc_2},
{ST_CONN_REQ, ST_CONN_REQ, EV_TIMER, cb_in_2},
{0xff, 0, 0, NULL}
};
static void pcbit_fsm_timer(unsigned long data)
{
struct pcbit_dev *dev;
struct pcbit_chan *chan;
chan = (struct pcbit_chan *) data;
del_timer(&chan->fsm_timer);
chan->fsm_timer.function = NULL;
dev = chan2dev(chan);
if (dev == NULL) {
printk(KERN_WARNING "pcbit: timer for unknown device\n");
return;
}
pcbit_fsm_event(dev, chan, EV_TIMER, NULL);
}
void pcbit_fsm_event(struct pcbit_dev *dev, struct pcbit_chan *chan,
unsigned short event, struct callb_data *data)
{
struct fsm_entry *action;
struct fsm_timer_entry *tentry;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
for (action = fsm_table; action->init != 0xff; action++)
if (action->init == chan->fsm_state && action->event == event)
break;
if (action->init == 0xff) {
spin_unlock_irqrestore(&dev->lock, flags);
printk(KERN_DEBUG "fsm error: event %x on state %x\n",
event, chan->fsm_state);
return;
}
if (chan->fsm_timer.function) {
del_timer(&chan->fsm_timer);
chan->fsm_timer.function = NULL;
}
chan->fsm_state = action->final;
pcbit_state_change(dev, chan, action->init, event, action->final);
for (tentry = fsm_timers; tentry->init != 0xff; tentry++)
if (tentry->init == chan->fsm_state)
break;
if (tentry->init != 0xff) {
init_timer(&chan->fsm_timer);
chan->fsm_timer.function = &pcbit_fsm_timer;
chan->fsm_timer.data = (ulong) chan;
chan->fsm_timer.expires = jiffies + tentry->timeout * HZ;
add_timer(&chan->fsm_timer);
}
spin_unlock_irqrestore(&dev->lock, flags);
if (action->callb)
action->callb(dev, chan, data);
}

View file

@ -0,0 +1,99 @@
/*
* DSS.1 module definitions
*
* Copyright (C) 1996 Universidade de Lisboa
*
* Written by Pedro Roque Marques (roque@di.fc.ul.pt)
*
* This software may be used and distributed according to the terms of
* the GNU General Public License, incorporated herein by reference.
*/
#ifndef EDSS1_H
#define EDSS1_H
/* ISDN states */
#define ST_NULL 0
#define ST_CALL_INIT 1 /* Call initiated */
#define ST_OVER_SEND 2 /* Overlap sending - Requests More Info 4 call */
#define ST_CALL_PROC 3 /* Call Proceeding */
#define ST_CALL_DELV 4
#define ST_CALL_PRES 6 /* Call Present - Received CONN.IND */
#define ST_CALL_RECV 7 /* Alerting sent */
#define ST_CONN_REQ 8 /* Answered - waiting 4 CONN.CONF */
#define ST_INCM_PROC 9
#define ST_ACTIVE 10
#define ST_DISC_REQ 11
#define ST_DISC_IND 12
#define ST_SUSP_REQ 15
#define ST_RESM_REQ 17
#define ST_RELS_REQ 19
#define ST_OVER_RECV 25
#define ST_ACTIVE_SELP 26 /* Select protocol on B-Channel */
#define ST_ACTIVE_ACTV 27 /* Activate B-channel protocol */
#define MAX_STATE ST_ACTIVE_ACTV
#define EV_NULL 0
#define EV_USR_SETUP_REQ 1
#define EV_USR_SETUP_RESP 2
#define EV_USR_PROCED_REQ 3
#define EV_USR_RELEASE_REQ 4
#define EV_USR_REJECT_REQ 4
#define EV_NET_SETUP 16
#define EV_NET_CALL_PROC 17
#define EV_NET_SETUP_ACK 18
#define EV_NET_CONN 19
#define EV_NET_CONN_ACK 20
#define EV_NET_SELP_RESP 21
#define EV_NET_ACTV_RESP 22
#define EV_NET_DISC 23
#define EV_NET_RELEASE 24
#define EV_NET_RELEASE_COMP 25
#define EV_TIMER 26
#define EV_ERROR 32
/*
* Cause values
* only the ones we use
*/
#define CAUSE_NORMAL 0x10U
#define CAUSE_NOCHAN 0x22U
struct callb_data {
unsigned short type;
union {
struct ConnInfo {
char *CalledPN;
char *CallingPN;
} setup;
unsigned short cause;
} data;
};
struct fsm_entry {
unsigned short init;
unsigned short final;
unsigned short event;
void (*callb)(struct pcbit_dev *, struct pcbit_chan *, struct callb_data*);
};
struct fsm_timer_entry {
unsigned short init;
unsigned long timeout; /* in seconds */
};
extern const char * const isdn_state_table[];
void pcbit_fsm_event(struct pcbit_dev *, struct pcbit_chan *,
unsigned short event, struct callb_data *);
char *strisdnevent(ushort ev);
#endif

713
drivers/isdn/pcbit/layer2.c Normal file
View file

@ -0,0 +1,713 @@
/*
* PCBIT-D low-layer interface
*
* Copyright (C) 1996 Universidade de Lisboa
*
* Written by Pedro Roque Marques (roque@di.fc.ul.pt)
*
* This software may be used and distributed according to the terms of
* the GNU General Public License, incorporated herein by reference.
*/
/*
* 19991203 - Fernando Carvalho - takion@superbofh.org
* Hacked to compile with egcs and run with current version of isdn modules
*/
/*
* Based on documentation provided by Inesc:
* - "Interface com bus do PC para o PCBIT e PCBIT-D", Inesc, Jan 93
*/
/*
* TODO: better handling of errors
* re-write/remove debug printks
*/
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/mm.h>
#include <linux/skbuff.h>
#include <linux/isdnif.h>
#include <asm/io.h>
#include "pcbit.h"
#include "layer2.h"
#include "edss1.h"
#undef DEBUG_FRAG
/*
* Prototypes
*/
static void pcbit_transmit(struct pcbit_dev *dev);
static void pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack);
static void pcbit_l2_error(struct pcbit_dev *dev);
static void pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info);
static void pcbit_l2_err_recover(unsigned long data);
static void pcbit_firmware_bug(struct pcbit_dev *dev);
static __inline__ void
pcbit_sched_delivery(struct pcbit_dev *dev)
{
schedule_work(&dev->qdelivery);
}
/*
* Called from layer3
*/
int
pcbit_l2_write(struct pcbit_dev *dev, ulong msg, ushort refnum,
struct sk_buff *skb, unsigned short hdr_len)
{
struct frame_buf *frame,
*ptr;
unsigned long flags;
if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) {
dev_kfree_skb(skb);
return -1;
}
if ((frame = kmalloc(sizeof(struct frame_buf),
GFP_ATOMIC)) == NULL) {
printk(KERN_WARNING "pcbit_2_write: kmalloc failed\n");
dev_kfree_skb(skb);
return -1;
}
frame->msg = msg;
frame->refnum = refnum;
frame->copied = 0;
frame->hdr_len = hdr_len;
if (skb)
frame->dt_len = skb->len - hdr_len;
else
frame->dt_len = 0;
frame->skb = skb;
frame->next = NULL;
spin_lock_irqsave(&dev->lock, flags);
if (dev->write_queue == NULL) {
dev->write_queue = frame;
spin_unlock_irqrestore(&dev->lock, flags);
pcbit_transmit(dev);
} else {
for (ptr = dev->write_queue; ptr->next; ptr = ptr->next);
ptr->next = frame;
spin_unlock_irqrestore(&dev->lock, flags);
}
return 0;
}
static __inline__ void
pcbit_tx_update(struct pcbit_dev *dev, ushort len)
{
u_char info;
dev->send_seq = (dev->send_seq + 1) % 8;
dev->fsize[dev->send_seq] = len;
info = 0;
info |= dev->rcv_seq << 3;
info |= dev->send_seq;
writeb(info, dev->sh_mem + BANK4);
}
/*
* called by interrupt service routine or by write_2
*/
static void
pcbit_transmit(struct pcbit_dev *dev)
{
struct frame_buf *frame = NULL;
unsigned char unacked;
int flen; /* fragment frame length including all headers */
int free;
int count,
cp_len;
unsigned long flags;
unsigned short tt;
if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
return;
unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07;
spin_lock_irqsave(&dev->lock, flags);
if (dev->free > 16 && dev->write_queue && unacked < 7) {
if (!dev->w_busy)
dev->w_busy = 1;
else {
spin_unlock_irqrestore(&dev->lock, flags);
return;
}
frame = dev->write_queue;
free = dev->free;
spin_unlock_irqrestore(&dev->lock, flags);
if (frame->copied == 0) {
/* Type 0 frame */
ulong msg;
if (frame->skb)
flen = FRAME_HDR_LEN + PREHDR_LEN + frame->skb->len;
else
flen = FRAME_HDR_LEN + PREHDR_LEN;
if (flen > free)
flen = free;
msg = frame->msg;
/*
* Board level 2 header
*/
pcbit_writew(dev, flen - FRAME_HDR_LEN);
pcbit_writeb(dev, GET_MSG_CPU(msg));
pcbit_writeb(dev, GET_MSG_PROC(msg));
/* TH */
pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
/* TD */
pcbit_writew(dev, frame->dt_len);
/*
* Board level 3 fixed-header
*/
/* LEN = TH */
pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
/* XX */
pcbit_writew(dev, 0);
/* C + S */
pcbit_writeb(dev, GET_MSG_CMD(msg));
pcbit_writeb(dev, GET_MSG_SCMD(msg));
/* NUM */
pcbit_writew(dev, frame->refnum);
count = FRAME_HDR_LEN + PREHDR_LEN;
} else {
/* Type 1 frame */
flen = 2 + (frame->skb->len - frame->copied);
if (flen > free)
flen = free;
/* TT */
tt = ((ushort) (flen - 2)) | 0x8000U; /* Type 1 */
pcbit_writew(dev, tt);
count = 2;
}
if (frame->skb) {
cp_len = frame->skb->len - frame->copied;
if (cp_len > flen - count)
cp_len = flen - count;
memcpy_topcbit(dev, frame->skb->data + frame->copied,
cp_len);
frame->copied += cp_len;
}
/* bookkeeping */
dev->free -= flen;
pcbit_tx_update(dev, flen);
spin_lock_irqsave(&dev->lock, flags);
if (frame->skb == NULL || frame->copied == frame->skb->len) {
dev->write_queue = frame->next;
if (frame->skb != NULL) {
/* free frame */
dev_kfree_skb(frame->skb);
}
kfree(frame);
}
dev->w_busy = 0;
spin_unlock_irqrestore(&dev->lock, flags);
} else {
spin_unlock_irqrestore(&dev->lock, flags);
#ifdef DEBUG
printk(KERN_DEBUG "unacked %d free %d write_queue %s\n",
unacked, dev->free, dev->write_queue ? "not empty" :
"empty");
#endif
}
}
/*
* deliver a queued frame to the upper layer
*/
void
pcbit_deliver(struct work_struct *work)
{
struct frame_buf *frame;
unsigned long flags, msg;
struct pcbit_dev *dev =
container_of(work, struct pcbit_dev, qdelivery);
spin_lock_irqsave(&dev->lock, flags);
while ((frame = dev->read_queue)) {
dev->read_queue = frame->next;
spin_unlock_irqrestore(&dev->lock, flags);
msg = 0;
SET_MSG_CPU(msg, 0);
SET_MSG_PROC(msg, 0);
SET_MSG_CMD(msg, frame->skb->data[2]);
SET_MSG_SCMD(msg, frame->skb->data[3]);
frame->refnum = *((ushort *)frame->skb->data + 4);
frame->msg = *((ulong *)&msg);
skb_pull(frame->skb, 6);
pcbit_l3_receive(dev, frame->msg, frame->skb, frame->hdr_len,
frame->refnum);
kfree(frame);
spin_lock_irqsave(&dev->lock, flags);
}
spin_unlock_irqrestore(&dev->lock, flags);
}
/*
* Reads BANK 2 & Reassembles
*/
static void
pcbit_receive(struct pcbit_dev *dev)
{
unsigned short tt;
u_char cpu,
proc;
struct frame_buf *frame = NULL;
unsigned long flags;
u_char type1;
if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
return;
tt = pcbit_readw(dev);
if ((tt & 0x7fffU) > 511) {
printk(KERN_INFO "pcbit: invalid frame length -> TT=%04x\n",
tt);
pcbit_l2_error(dev);
return;
}
if (!(tt & 0x8000U)) { /* Type 0 */
type1 = 0;
if (dev->read_frame) {
printk(KERN_DEBUG "pcbit_receive: Type 0 frame and read_frame != NULL\n");
/* discard previous queued frame */
kfree_skb(dev->read_frame->skb);
kfree(dev->read_frame);
dev->read_frame = NULL;
}
frame = kzalloc(sizeof(struct frame_buf), GFP_ATOMIC);
if (frame == NULL) {
printk(KERN_WARNING "kmalloc failed\n");
return;
}
cpu = pcbit_readb(dev);
proc = pcbit_readb(dev);
if (cpu != 0x06 && cpu != 0x02) {
printk(KERN_DEBUG "pcbit: invalid cpu value\n");
kfree(frame);
pcbit_l2_error(dev);
return;
}
/*
* we discard cpu & proc on receiving
* but we read it to update the pointer
*/
frame->hdr_len = pcbit_readw(dev);
frame->dt_len = pcbit_readw(dev);
/*
* 0 sized packet
* I don't know if they are an error or not...
* But they are very frequent
* Not documented
*/
if (frame->hdr_len == 0) {
kfree(frame);
#ifdef DEBUG
printk(KERN_DEBUG "0 sized frame\n");
#endif
pcbit_firmware_bug(dev);
return;
}
/* sanity check the length values */
if (frame->hdr_len > 1024 || frame->dt_len > 2048) {
#ifdef DEBUG
printk(KERN_DEBUG "length problem: ");
printk(KERN_DEBUG "TH=%04x TD=%04x\n",
frame->hdr_len,
frame->dt_len);
#endif
pcbit_l2_error(dev);
kfree(frame);
return;
}
/* minimum frame read */
frame->skb = dev_alloc_skb(frame->hdr_len + frame->dt_len +
((frame->hdr_len + 15) & ~15));
if (!frame->skb) {
printk(KERN_DEBUG "pcbit_receive: out of memory\n");
kfree(frame);
return;
}
/* 16 byte alignment for IP */
if (frame->dt_len)
skb_reserve(frame->skb, (frame->hdr_len + 15) & ~15);
} else {
/* Type 1 */
type1 = 1;
tt &= 0x7fffU;
if (!(frame = dev->read_frame)) {
printk("Type 1 frame and no frame queued\n");
/* usually after an error: toss frame */
dev->readptr += tt;
if (dev->readptr > dev->sh_mem + BANK2 + BANKLEN)
dev->readptr -= BANKLEN;
return;
}
}
memcpy_frompcbit(dev, skb_put(frame->skb, tt), tt);
frame->copied += tt;
spin_lock_irqsave(&dev->lock, flags);
if (frame->copied == frame->hdr_len + frame->dt_len) {
if (type1) {
dev->read_frame = NULL;
}
if (dev->read_queue) {
struct frame_buf *ptr;
for (ptr = dev->read_queue; ptr->next; ptr = ptr->next);
ptr->next = frame;
} else
dev->read_queue = frame;
} else {
dev->read_frame = frame;
}
spin_unlock_irqrestore(&dev->lock, flags);
}
/*
* The board sends 0 sized frames
* They are TDATA_CONFs that get messed up somehow
* gotta send a fake acknowledgment to the upper layer somehow
*/
static __inline__ void
pcbit_fake_conf(struct pcbit_dev *dev, struct pcbit_chan *chan)
{
isdn_ctrl ictl;
if (chan->queued) {
chan->queued--;
ictl.driver = dev->id;
ictl.command = ISDN_STAT_BSENT;
ictl.arg = chan->id;
dev->dev_if->statcallb(&ictl);
}
}
static void
pcbit_firmware_bug(struct pcbit_dev *dev)
{
struct pcbit_chan *chan;
chan = dev->b1;
if (chan->fsm_state == ST_ACTIVE) {
pcbit_fake_conf(dev, chan);
}
chan = dev->b2;
if (chan->fsm_state == ST_ACTIVE) {
pcbit_fake_conf(dev, chan);
}
}
irqreturn_t
pcbit_irq_handler(int interrupt, void *devptr)
{
struct pcbit_dev *dev;
u_char info,
ack_seq,
read_seq;
dev = (struct pcbit_dev *) devptr;
if (!dev) {
printk(KERN_WARNING "pcbit_irq_handler: wrong device\n");
return IRQ_NONE;
}
if (dev->interrupt) {
printk(KERN_DEBUG "pcbit: reentering interrupt handler\n");
return IRQ_HANDLED;
}
dev->interrupt = 1;
info = readb(dev->sh_mem + BANK3);
if (dev->l2_state == L2_STARTING || dev->l2_state == L2_ERROR) {
pcbit_l2_active_conf(dev, info);
dev->interrupt = 0;
return IRQ_HANDLED;
}
if (info & 0x40U) { /* E bit set */
#ifdef DEBUG
printk(KERN_DEBUG "pcbit_irq_handler: E bit on\n");
#endif
pcbit_l2_error(dev);
dev->interrupt = 0;
return IRQ_HANDLED;
}
if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) {
dev->interrupt = 0;
return IRQ_HANDLED;
}
ack_seq = (info >> 3) & 0x07U;
read_seq = (info & 0x07U);
dev->interrupt = 0;
if (read_seq != dev->rcv_seq) {
while (read_seq != dev->rcv_seq) {
pcbit_receive(dev);
dev->rcv_seq = (dev->rcv_seq + 1) % 8;
}
pcbit_sched_delivery(dev);
}
if (ack_seq != dev->unack_seq) {
pcbit_recv_ack(dev, ack_seq);
}
info = dev->rcv_seq << 3;
info |= dev->send_seq;
writeb(info, dev->sh_mem + BANK4);
return IRQ_HANDLED;
}
static void
pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info)
{
u_char state;
state = dev->l2_state;
#ifdef DEBUG
printk(KERN_DEBUG "layer2_active_confirm\n");
#endif
if (info & 0x80U) {
dev->rcv_seq = info & 0x07U;
dev->l2_state = L2_RUNNING;
} else
dev->l2_state = L2_DOWN;
if (state == L2_STARTING)
wake_up_interruptible(&dev->set_running_wq);
if (state == L2_ERROR && dev->l2_state == L2_RUNNING) {
pcbit_transmit(dev);
}
}
static void
pcbit_l2_err_recover(unsigned long data)
{
struct pcbit_dev *dev;
struct frame_buf *frame;
dev = (struct pcbit_dev *) data;
del_timer(&dev->error_recover_timer);
if (dev->w_busy || dev->r_busy) {
init_timer(&dev->error_recover_timer);
dev->error_recover_timer.expires = jiffies + ERRTIME;
add_timer(&dev->error_recover_timer);
return;
}
dev->w_busy = dev->r_busy = 1;
if (dev->read_frame) {
kfree_skb(dev->read_frame->skb);
kfree(dev->read_frame);
dev->read_frame = NULL;
}
if (dev->write_queue) {
frame = dev->write_queue;
#ifdef FREE_ON_ERROR
dev->write_queue = dev->write_queue->next;
if (frame->skb) {
dev_kfree_skb(frame->skb);
}
kfree(frame);
#else
frame->copied = 0;
#endif
}
dev->rcv_seq = dev->send_seq = dev->unack_seq = 0;
dev->free = 511;
dev->l2_state = L2_ERROR;
/* this is an hack... */
pcbit_firmware_bug(dev);
dev->writeptr = dev->sh_mem;
dev->readptr = dev->sh_mem + BANK2;
writeb((0x80U | ((dev->rcv_seq & 0x07) << 3) | (dev->send_seq & 0x07)),
dev->sh_mem + BANK4);
dev->w_busy = dev->r_busy = 0;
}
static void
pcbit_l2_error(struct pcbit_dev *dev)
{
if (dev->l2_state == L2_RUNNING) {
printk(KERN_INFO "pcbit: layer 2 error\n");
#ifdef DEBUG
log_state(dev);
#endif
dev->l2_state = L2_DOWN;
init_timer(&dev->error_recover_timer);
dev->error_recover_timer.function = &pcbit_l2_err_recover;
dev->error_recover_timer.data = (ulong) dev;
dev->error_recover_timer.expires = jiffies + ERRTIME;
add_timer(&dev->error_recover_timer);
}
}
/*
* Description:
* if board acks frames
* update dev->free
* call pcbit_transmit to write possible queued frames
*/
static void
pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack)
{
int i,
count;
int unacked;
unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07;
/* dev->unack_seq < ack <= dev->send_seq; */
if (unacked) {
if (dev->send_seq > dev->unack_seq) {
if (ack <= dev->unack_seq || ack > dev->send_seq) {
printk(KERN_DEBUG
"layer 2 ack unacceptable - dev %d",
dev->id);
pcbit_l2_error(dev);
} else if (ack > dev->send_seq && ack <= dev->unack_seq) {
printk(KERN_DEBUG
"layer 2 ack unacceptable - dev %d",
dev->id);
pcbit_l2_error(dev);
}
}
/* ack is acceptable */
i = dev->unack_seq;
do {
dev->unack_seq = i = (i + 1) % 8;
dev->free += dev->fsize[i];
} while (i != ack);
count = 0;
while (count < 7 && dev->write_queue) {
u8 lsend_seq = dev->send_seq;
pcbit_transmit(dev);
if (dev->send_seq == lsend_seq)
break;
count++;
}
} else
printk(KERN_DEBUG "recv_ack: unacked = 0\n");
}

281
drivers/isdn/pcbit/layer2.h Normal file
View file

@ -0,0 +1,281 @@
/*
* PCBIT-D low-layer interface definitions
*
* Copyright (C) 1996 Universidade de Lisboa
*
* Written by Pedro Roque Marques (roque@di.fc.ul.pt)
*
* This software may be used and distributed according to the terms of
* the GNU General Public License, incorporated herein by reference.
*/
/*
* 19991203 - Fernando Carvalho - takion@superbofh.org
* Hacked to compile with egcs and run with current version of isdn modules
*/
#ifndef LAYER2_H
#define LAYER2_H
#include <linux/interrupt.h>
#include <asm/byteorder.h>
#define BANK1 0x0000U /* PC -> Board */
#define BANK2 0x01ffU /* Board -> PC */
#define BANK3 0x03feU /* Att Board */
#define BANK4 0x03ffU /* Att PC */
#define BANKLEN 0x01FFU
#define LOAD_ZONE_START 0x03f8U
#define LOAD_ZONE_END 0x03fdU
#define LOAD_RETRY 18000000
/* TAM - XX - C - S - NUM */
#define PREHDR_LEN 8
/* TT - M - I - TH - TD */
#define FRAME_HDR_LEN 8
#define MSG_CONN_REQ 0x08000100
#define MSG_CONN_CONF 0x00000101
#define MSG_CONN_IND 0x00000102
#define MSG_CONN_RESP 0x08000103
#define MSG_CONN_ACTV_REQ 0x08000300
#define MSG_CONN_ACTV_CONF 0x00000301
#define MSG_CONN_ACTV_IND 0x00000302
#define MSG_CONN_ACTV_RESP 0x08000303
#define MSG_DISC_REQ 0x08000400
#define MSG_DISC_CONF 0x00000401
#define MSG_DISC_IND 0x00000402
#define MSG_DISC_RESP 0x08000403
#define MSG_TDATA_REQ 0x0908E200
#define MSG_TDATA_CONF 0x0000E201
#define MSG_TDATA_IND 0x0000E202
#define MSG_TDATA_RESP 0x0908E203
#define MSG_SELP_REQ 0x09004000
#define MSG_SELP_CONF 0x00004001
#define MSG_ACT_TRANSP_REQ 0x0908E000
#define MSG_ACT_TRANSP_CONF 0x0000E001
#define MSG_STPROT_REQ 0x09004100
#define MSG_STPROT_CONF 0x00004101
#define MSG_PING188_REQ 0x09030500
#define MSG_PING188_CONF 0x000005bc
#define MSG_WATCH188 0x09030400
#define MSG_API_ON 0x08020102
#define MSG_POOL_PCBIT 0x08020400
#define MSG_POOL_PCBIT_CONF 0x00000401
#define MSG_INFO_IND 0x00002602
#define MSG_INFO_RESP 0x08002603
#define MSG_DEBUG_188 0x0000ff00
/*
long 4 3 2 1
Intel 1 2 3 4
*/
#ifdef __LITTLE_ENDIAN
#define SET_MSG_SCMD(msg, ch) (msg = (msg & 0xffffff00) | (((ch) & 0xff)))
#define SET_MSG_CMD(msg, ch) (msg = (msg & 0xffff00ff) | (((ch) & 0xff) << 8))
#define SET_MSG_PROC(msg, ch) (msg = (msg & 0xff00ffff) | (((ch) & 0xff) << 16))
#define SET_MSG_CPU(msg, ch) (msg = (msg & 0x00ffffff) | (((ch) & 0xff) << 24))
#define GET_MSG_SCMD(msg) ((msg) & 0xFF)
#define GET_MSG_CMD(msg) ((msg) >> 8 & 0xFF)
#define GET_MSG_PROC(msg) ((msg) >> 16 & 0xFF)
#define GET_MSG_CPU(msg) ((msg) >> 24)
#else
#error "Non-Intel CPU"
#endif
#define MAX_QUEUED 7
#define SCHED_READ 0x01
#define SCHED_WRITE 0x02
#define SET_RUN_TIMEOUT 2 * HZ /* 2 seconds */
struct frame_buf {
ulong msg;
unsigned int refnum;
unsigned int dt_len;
unsigned int hdr_len;
struct sk_buff *skb;
unsigned int copied;
struct frame_buf *next;
};
extern int pcbit_l2_write(struct pcbit_dev *dev, ulong msg, ushort refnum,
struct sk_buff *skb, unsigned short hdr_len);
extern irqreturn_t pcbit_irq_handler(int interrupt, void *);
extern struct pcbit_dev *dev_pcbit[MAX_PCBIT_CARDS];
#ifdef DEBUG
static __inline__ void log_state(struct pcbit_dev *dev) {
printk(KERN_DEBUG "writeptr = %ld\n",
(ulong) (dev->writeptr - dev->sh_mem));
printk(KERN_DEBUG "readptr = %ld\n",
(ulong) (dev->readptr - (dev->sh_mem + BANK2)));
printk(KERN_DEBUG "{rcv_seq=%01x, send_seq=%01x, unack_seq=%01x}\n",
dev->rcv_seq, dev->send_seq, dev->unack_seq);
}
#endif
static __inline__ struct pcbit_dev *chan2dev(struct pcbit_chan *chan)
{
struct pcbit_dev *dev;
int i;
for (i = 0; i < MAX_PCBIT_CARDS; i++)
if ((dev = dev_pcbit[i]))
if (dev->b1 == chan || dev->b2 == chan)
return dev;
return NULL;
}
static __inline__ struct pcbit_dev *finddev(int id)
{
struct pcbit_dev *dev;
int i;
for (i = 0; i < MAX_PCBIT_CARDS; i++)
if ((dev = dev_pcbit[i]))
if (dev->id == id)
return dev;
return NULL;
}
/*
* Support routines for reading and writing in the board
*/
static __inline__ void pcbit_writeb(struct pcbit_dev *dev, unsigned char dt)
{
writeb(dt, dev->writeptr++);
if (dev->writeptr == dev->sh_mem + BANKLEN)
dev->writeptr = dev->sh_mem;
}
static __inline__ void pcbit_writew(struct pcbit_dev *dev, unsigned short dt)
{
int dist;
dist = BANKLEN - (dev->writeptr - dev->sh_mem);
switch (dist) {
case 2:
writew(dt, dev->writeptr);
dev->writeptr = dev->sh_mem;
break;
case 1:
writeb((u_char) (dt & 0x00ffU), dev->writeptr);
dev->writeptr = dev->sh_mem;
writeb((u_char) (dt >> 8), dev->writeptr++);
break;
default:
writew(dt, dev->writeptr);
dev->writeptr += 2;
break;
};
}
static __inline__ void memcpy_topcbit(struct pcbit_dev *dev, u_char *data,
int len)
{
int diff;
diff = len - (BANKLEN - (dev->writeptr - dev->sh_mem));
if (diff > 0)
{
memcpy_toio(dev->writeptr, data, len - diff);
memcpy_toio(dev->sh_mem, data + (len - diff), diff);
dev->writeptr = dev->sh_mem + diff;
}
else
{
memcpy_toio(dev->writeptr, data, len);
dev->writeptr += len;
if (diff == 0)
dev->writeptr = dev->sh_mem;
}
}
static __inline__ unsigned char pcbit_readb(struct pcbit_dev *dev)
{
unsigned char val;
val = readb(dev->readptr++);
if (dev->readptr == dev->sh_mem + BANK2 + BANKLEN)
dev->readptr = dev->sh_mem + BANK2;
return val;
}
static __inline__ unsigned short pcbit_readw(struct pcbit_dev *dev)
{
int dist;
unsigned short val;
dist = BANKLEN - (dev->readptr - (dev->sh_mem + BANK2));
switch (dist) {
case 2:
val = readw(dev->readptr);
dev->readptr = dev->sh_mem + BANK2;
break;
case 1:
val = readb(dev->readptr);
dev->readptr = dev->sh_mem + BANK2;
val = (readb(dev->readptr++) << 8) | val;
break;
default:
val = readw(dev->readptr);
dev->readptr += 2;
break;
};
return val;
}
static __inline__ void memcpy_frompcbit(struct pcbit_dev *dev, u_char *data, int len)
{
int diff;
diff = len - (BANKLEN - (dev->readptr - (dev->sh_mem + BANK2)));
if (diff > 0)
{
memcpy_fromio(data, dev->readptr, len - diff);
memcpy_fromio(data + (len - diff), dev->sh_mem + BANK2 , diff);
dev->readptr = dev->sh_mem + BANK2 + diff;
}
else
{
memcpy_fromio(data, dev->readptr, len);
dev->readptr += len;
if (diff == 0)
dev->readptr = dev->sh_mem + BANK2;
}
}
#endif

125
drivers/isdn/pcbit/module.c Normal file
View file

@ -0,0 +1,125 @@
/*
* PCBIT-D module support
*
* Copyright (C) 1996 Universidade de Lisboa
*
* Written by Pedro Roque Marques (roque@di.fc.ul.pt)
*
* This software may be used and distributed according to the terms of
* the GNU General Public License, incorporated herein by reference.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/isdnif.h>
#include "pcbit.h"
MODULE_DESCRIPTION("ISDN4Linux: Driver for PCBIT-T card");
MODULE_AUTHOR("Pedro Roque Marques");
MODULE_LICENSE("GPL");
static int mem[MAX_PCBIT_CARDS];
static int irq[MAX_PCBIT_CARDS];
module_param_array(mem, int, NULL, 0);
module_param_array(irq, int, NULL, 0);
static int num_boards;
struct pcbit_dev *dev_pcbit[MAX_PCBIT_CARDS];
static int __init pcbit_init(void)
{
int board;
num_boards = 0;
printk(KERN_NOTICE
"PCBIT-D device driver v 0.5-fjpc0 19991204 - "
"Copyright (C) 1996 Universidade de Lisboa\n");
if (mem[0] || irq[0])
{
for (board = 0; board < MAX_PCBIT_CARDS && mem[board] && irq[board]; board++)
{
if (!mem[board])
mem[board] = 0xD0000;
if (!irq[board])
irq[board] = 5;
if (pcbit_init_dev(board, mem[board], irq[board]) == 0)
num_boards++;
else
{
printk(KERN_WARNING
"pcbit_init failed for dev %d",
board + 1);
return -EIO;
}
}
}
/* Hardcoded default settings detection */
if (!num_boards)
{
printk(KERN_INFO
"Trying to detect board using default settings\n");
if (pcbit_init_dev(0, 0xD0000, 5) == 0)
num_boards++;
else
return -EIO;
}
return 0;
}
static void __exit pcbit_exit(void)
{
#ifdef MODULE
int board;
for (board = 0; board < num_boards; board++)
pcbit_terminate(board);
printk(KERN_NOTICE
"PCBIT-D module unloaded\n");
#endif
}
#ifndef MODULE
#define MAX_PARA (MAX_PCBIT_CARDS * 2)
static int __init pcbit_setup(char *line)
{
int i, j, argc;
char *str;
int ints[MAX_PARA + 1];
str = get_options(line, MAX_PARA, ints);
argc = ints[0];
i = 0;
j = 1;
while (argc && (i < MAX_PCBIT_CARDS)) {
if (argc) {
mem[i] = ints[j];
j++; argc--;
}
if (argc) {
irq[i] = ints[j];
j++; argc--;
}
i++;
}
return (1);
}
__setup("pcbit=", pcbit_setup);
#endif
module_init(pcbit_init);
module_exit(pcbit_exit);

177
drivers/isdn/pcbit/pcbit.h Normal file
View file

@ -0,0 +1,177 @@
/*
* PCBIT-D device driver definitions
*
* Copyright (C) 1996 Universidade de Lisboa
*
* Written by Pedro Roque Marques (roque@di.fc.ul.pt)
*
* This software may be used and distributed according to the terms of
* the GNU General Public License, incorporated herein by reference.
*/
#ifndef PCBIT_H
#define PCBIT_H
#include <linux/workqueue.h>
#define MAX_PCBIT_CARDS 4
#define BLOCK_TIMER
#ifdef __KERNEL__
struct pcbit_chan {
unsigned short id;
unsigned short callref; /* Call Reference */
unsigned char proto; /* layer2protocol */
unsigned char queued; /* unacked data messages */
unsigned char layer2link; /* used in TData */
unsigned char snum; /* used in TData */
unsigned short s_refnum;
unsigned short r_refnum;
unsigned short fsm_state;
struct timer_list fsm_timer;
#ifdef BLOCK_TIMER
struct timer_list block_timer;
#endif
};
struct msn_entry {
char *msn;
struct msn_entry *next;
};
struct pcbit_dev {
/* board */
volatile unsigned char __iomem *sh_mem; /* RDP address */
unsigned long ph_mem;
unsigned int irq;
unsigned int id;
unsigned int interrupt; /* set during interrupt
processing */
spinlock_t lock;
/* isdn4linux */
struct msn_entry *msn_list; /* ISDN address list */
isdn_if *dev_if;
ushort ll_hdrlen;
ushort hl_hdrlen;
/* link layer */
unsigned char l2_state;
struct frame_buf *read_queue;
struct frame_buf *read_frame;
struct frame_buf *write_queue;
/* Protocol start */
wait_queue_head_t set_running_wq;
struct timer_list set_running_timer;
struct timer_list error_recover_timer;
struct work_struct qdelivery;
u_char w_busy;
u_char r_busy;
volatile unsigned char __iomem *readptr;
volatile unsigned char __iomem *writeptr;
ushort loadptr;
unsigned short fsize[8]; /* sent layer2 frames size */
unsigned char send_seq;
unsigned char rcv_seq;
unsigned char unack_seq;
unsigned short free;
/* channels */
struct pcbit_chan *b1;
struct pcbit_chan *b2;
};
#define STATS_TIMER (10 * HZ)
#define ERRTIME (HZ / 10)
/* MRU */
#define MAXBUFSIZE 1534
#define MRU MAXBUFSIZE
#define STATBUF_LEN 2048
/*
*
*/
#endif /* __KERNEL__ */
/* isdn_ctrl only allows a long sized argument */
struct pcbit_ioctl {
union {
struct byte_op {
ushort addr;
ushort value;
} rdp_byte;
unsigned long l2_status;
} info;
};
#define PCBIT_IOCTL_GETSTAT 0x01 /* layer2 status */
#define PCBIT_IOCTL_LWMODE 0x02 /* linear write mode */
#define PCBIT_IOCTL_STRLOAD 0x03 /* start load mode */
#define PCBIT_IOCTL_ENDLOAD 0x04 /* end load mode */
#define PCBIT_IOCTL_SETBYTE 0x05 /* set byte */
#define PCBIT_IOCTL_GETBYTE 0x06 /* get byte */
#define PCBIT_IOCTL_RUNNING 0x07 /* set protocol running */
#define PCBIT_IOCTL_WATCH188 0x08 /* set watch 188 */
#define PCBIT_IOCTL_PING188 0x09 /* ping 188 */
#define PCBIT_IOCTL_FWMODE 0x0A /* firmware write mode */
#define PCBIT_IOCTL_STOP 0x0B /* stop protocol */
#define PCBIT_IOCTL_APION 0x0C /* issue API_ON */
#ifndef __KERNEL__
#define PCBIT_GETSTAT (PCBIT_IOCTL_GETSTAT + IIOCDRVCTL)
#define PCBIT_LWMODE (PCBIT_IOCTL_LWMODE + IIOCDRVCTL)
#define PCBIT_STRLOAD (PCBIT_IOCTL_STRLOAD + IIOCDRVCTL)
#define PCBIT_ENDLOAD (PCBIT_IOCTL_ENDLOAD + IIOCDRVCTL)
#define PCBIT_SETBYTE (PCBIT_IOCTL_SETBYTE + IIOCDRVCTL)
#define PCBIT_GETBYTE (PCBIT_IOCTL_GETBYTE + IIOCDRVCTL)
#define PCBIT_RUNNING (PCBIT_IOCTL_RUNNING + IIOCDRVCTL)
#define PCBIT_WATCH188 (PCBIT_IOCTL_WATCH188 + IIOCDRVCTL)
#define PCBIT_PING188 (PCBIT_IOCTL_PING188 + IIOCDRVCTL)
#define PCBIT_FWMODE (PCBIT_IOCTL_FWMODE + IIOCDRVCTL)
#define PCBIT_STOP (PCBIT_IOCTL_STOP + IIOCDRVCTL)
#define PCBIT_APION (PCBIT_IOCTL_APION + IIOCDRVCTL)
#define MAXSUPERLINE 3000
#endif
#define L2_DOWN 0
#define L2_LOADING 1
#define L2_LWMODE 2
#define L2_FWMODE 3
#define L2_STARTING 4
#define L2_RUNNING 5
#define L2_ERROR 6
void pcbit_deliver(struct work_struct *work);
int pcbit_init_dev(int board, int mem_base, int irq);
void pcbit_terminate(int board);
void pcbit_l3_receive(struct pcbit_dev *dev, ulong msg, struct sk_buff *skb,
ushort hdr_len, ushort refnum);
void pcbit_state_change(struct pcbit_dev *dev, struct pcbit_chan *chan,
unsigned short i, unsigned short ev, unsigned short f);
#endif