mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 17:18:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
10
drivers/isdn/pcbit/Kconfig
Normal file
10
drivers/isdn/pcbit/Kconfig
Normal 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.
|
9
drivers/isdn/pcbit/Makefile
Normal file
9
drivers/isdn/pcbit/Makefile
Normal 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
|
345
drivers/isdn/pcbit/callbacks.c
Normal file
345
drivers/isdn/pcbit/callbacks.c
Normal 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);
|
||||
}
|
44
drivers/isdn/pcbit/callbacks.h
Normal file
44
drivers/isdn/pcbit/callbacks.h
Normal 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
649
drivers/isdn/pcbit/capi.c
Normal 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
81
drivers/isdn/pcbit/capi.h
Normal 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
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
313
drivers/isdn/pcbit/edss1.c
Normal 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);
|
||||
|
||||
}
|
99
drivers/isdn/pcbit/edss1.h
Normal file
99
drivers/isdn/pcbit/edss1.h
Normal 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
713
drivers/isdn/pcbit/layer2.c
Normal 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
281
drivers/isdn/pcbit/layer2.h
Normal 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
125
drivers/isdn/pcbit/module.c
Normal 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
177
drivers/isdn/pcbit/pcbit.h
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue