mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-29 15:28:50 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
9
drivers/isdn/act2000/Kconfig
Normal file
9
drivers/isdn/act2000/Kconfig
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
config ISDN_DRV_ACT2000
|
||||
tristate "IBM Active 2000 support"
|
||||
depends on ISA
|
||||
help
|
||||
Say Y here if you have an IBM Active 2000 ISDN card. In order to use
|
||||
this card, additional firmware is necessary, which has to be loaded
|
||||
into the card using a utility which is part of the latest
|
||||
isdn4k-utils package. Please read the file
|
||||
<file:Documentation/isdn/README.act2000> for more information.
|
||||
9
drivers/isdn/act2000/Makefile
Normal file
9
drivers/isdn/act2000/Makefile
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Makefile for the act2000 ISDN device driver
|
||||
|
||||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_ISDN_DRV_ACT2000) += act2000.o
|
||||
|
||||
# Multipart objects.
|
||||
|
||||
act2000-y := module.o capi.o act2000_isa.o
|
||||
202
drivers/isdn/act2000/act2000.h
Normal file
202
drivers/isdn/act2000/act2000.h
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* $Id: act2000.h,v 1.8.6.3 2001/09/23 22:24:32 kai Exp $
|
||||
*
|
||||
* ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
|
||||
*
|
||||
* Author Fritz Elfert
|
||||
* Copyright by Fritz Elfert <fritz@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* Thanks to Friedemann Baitinger and IBM Germany
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef act2000_h
|
||||
#define act2000_h
|
||||
|
||||
#include <linux/compiler.h>
|
||||
|
||||
#define ACT2000_IOCTL_SETPORT 1
|
||||
#define ACT2000_IOCTL_GETPORT 2
|
||||
#define ACT2000_IOCTL_SETIRQ 3
|
||||
#define ACT2000_IOCTL_GETIRQ 4
|
||||
#define ACT2000_IOCTL_SETBUS 5
|
||||
#define ACT2000_IOCTL_GETBUS 6
|
||||
#define ACT2000_IOCTL_SETPROTO 7
|
||||
#define ACT2000_IOCTL_GETPROTO 8
|
||||
#define ACT2000_IOCTL_SETMSN 9
|
||||
#define ACT2000_IOCTL_GETMSN 10
|
||||
#define ACT2000_IOCTL_LOADBOOT 11
|
||||
#define ACT2000_IOCTL_ADDCARD 12
|
||||
|
||||
#define ACT2000_IOCTL_TEST 98
|
||||
#define ACT2000_IOCTL_DEBUGVAR 99
|
||||
|
||||
#define ACT2000_BUS_ISA 1
|
||||
#define ACT2000_BUS_MCA 2
|
||||
#define ACT2000_BUS_PCMCIA 3
|
||||
|
||||
/* Struct for adding new cards */
|
||||
typedef struct act2000_cdef {
|
||||
int bus;
|
||||
int port;
|
||||
int irq;
|
||||
char id[10];
|
||||
} act2000_cdef;
|
||||
|
||||
/* Struct for downloading firmware */
|
||||
typedef struct act2000_ddef {
|
||||
int length; /* Length of code */
|
||||
char __user *buffer; /* Ptr. to code */
|
||||
} act2000_ddef;
|
||||
|
||||
typedef struct act2000_fwid {
|
||||
char isdn[4];
|
||||
char revlen[2];
|
||||
char revision[504];
|
||||
} act2000_fwid;
|
||||
|
||||
#if defined(__KERNEL__) || defined(__DEBUGVAR__)
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/* Kernel includes */
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/major.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/isdnif.h>
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#define ACT2000_PORTLEN 8
|
||||
|
||||
#define ACT2000_FLAGS_RUNNING 1 /* Cards driver activated */
|
||||
#define ACT2000_FLAGS_PVALID 2 /* Cards port is valid */
|
||||
#define ACT2000_FLAGS_IVALID 4 /* Cards irq is valid */
|
||||
#define ACT2000_FLAGS_LOADED 8 /* Firmware loaded */
|
||||
|
||||
#define ACT2000_BCH 2 /* # of channels per card */
|
||||
|
||||
/* D-Channel states */
|
||||
#define ACT2000_STATE_NULL 0
|
||||
#define ACT2000_STATE_ICALL 1
|
||||
#define ACT2000_STATE_OCALL 2
|
||||
#define ACT2000_STATE_IWAIT 3
|
||||
#define ACT2000_STATE_OWAIT 4
|
||||
#define ACT2000_STATE_IBWAIT 5
|
||||
#define ACT2000_STATE_OBWAIT 6
|
||||
#define ACT2000_STATE_BWAIT 7
|
||||
#define ACT2000_STATE_BHWAIT 8
|
||||
#define ACT2000_STATE_BHWAIT2 9
|
||||
#define ACT2000_STATE_DHWAIT 10
|
||||
#define ACT2000_STATE_DHWAIT2 11
|
||||
#define ACT2000_STATE_BSETUP 12
|
||||
#define ACT2000_STATE_ACTIVE 13
|
||||
|
||||
#define ACT2000_MAX_QUEUED 8000 /* 2 * maxbuff */
|
||||
|
||||
#define ACT2000_LOCK_TX 0
|
||||
#define ACT2000_LOCK_RX 1
|
||||
|
||||
typedef struct act2000_chan {
|
||||
unsigned short callref; /* Call Reference */
|
||||
unsigned short fsm_state; /* Current D-Channel state */
|
||||
unsigned short eazmask; /* EAZ-Mask for this Channel */
|
||||
short queued; /* User-Data Bytes in TX queue */
|
||||
unsigned short plci;
|
||||
unsigned short ncci;
|
||||
unsigned char l2prot; /* Layer 2 protocol */
|
||||
unsigned char l3prot; /* Layer 3 protocol */
|
||||
} act2000_chan;
|
||||
|
||||
typedef struct msn_entry {
|
||||
char eaz;
|
||||
char msn[16];
|
||||
struct msn_entry *next;
|
||||
} msn_entry;
|
||||
|
||||
typedef struct irq_data_isa {
|
||||
__u8 *rcvptr;
|
||||
__u16 rcvidx;
|
||||
__u16 rcvlen;
|
||||
struct sk_buff *rcvskb;
|
||||
__u8 rcvignore;
|
||||
__u8 rcvhdr[8];
|
||||
} irq_data_isa;
|
||||
|
||||
typedef union act2000_irq_data {
|
||||
irq_data_isa isa;
|
||||
} act2000_irq_data;
|
||||
|
||||
/*
|
||||
* Per card driver data
|
||||
*/
|
||||
typedef struct act2000_card {
|
||||
unsigned short port; /* Base-port-address */
|
||||
unsigned short irq; /* Interrupt */
|
||||
u_char ptype; /* Protocol type (1TR6 or Euro) */
|
||||
u_char bus; /* Cardtype (ISA, MCA, PCMCIA) */
|
||||
struct act2000_card *next; /* Pointer to next device struct */
|
||||
spinlock_t lock; /* protect critical operations */
|
||||
int myid; /* Driver-Nr. assigned by linklevel */
|
||||
unsigned long flags; /* Statusflags */
|
||||
unsigned long ilock; /* Semaphores for IRQ-Routines */
|
||||
struct sk_buff_head rcvq; /* Receive-Message queue */
|
||||
struct sk_buff_head sndq; /* Send-Message queue */
|
||||
struct sk_buff_head ackq; /* Data-Ack-Message queue */
|
||||
u_char *ack_msg; /* Ptr to User Data in User skb */
|
||||
__u16 need_b3ack; /* Flag: Need ACK for current skb */
|
||||
struct sk_buff *sbuf; /* skb which is currently sent */
|
||||
struct timer_list ptimer; /* Poll timer */
|
||||
struct work_struct snd_tq; /* Task struct for xmit bh */
|
||||
struct work_struct rcv_tq; /* Task struct for rcv bh */
|
||||
struct work_struct poll_tq; /* Task struct for polled rcv bh */
|
||||
msn_entry *msn_list;
|
||||
unsigned short msgnum; /* Message number for sending */
|
||||
spinlock_t mnlock; /* lock for msgnum */
|
||||
act2000_chan bch[ACT2000_BCH]; /* B-Channel status/control */
|
||||
char status_buf[256]; /* Buffer for status messages */
|
||||
char *status_buf_read;
|
||||
char *status_buf_write;
|
||||
char *status_buf_end;
|
||||
act2000_irq_data idat; /* Data used for IRQ handler */
|
||||
isdn_if interface; /* Interface to upper layer */
|
||||
char regname[35]; /* Name used for request_region */
|
||||
} act2000_card;
|
||||
|
||||
static inline void act2000_schedule_tx(act2000_card *card)
|
||||
{
|
||||
schedule_work(&card->snd_tq);
|
||||
}
|
||||
|
||||
static inline void act2000_schedule_rx(act2000_card *card)
|
||||
{
|
||||
schedule_work(&card->rcv_tq);
|
||||
}
|
||||
|
||||
static inline void act2000_schedule_poll(act2000_card *card)
|
||||
{
|
||||
schedule_work(&card->poll_tq);
|
||||
}
|
||||
|
||||
extern char *act2000_find_eaz(act2000_card *, char);
|
||||
|
||||
#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
|
||||
#endif /* act2000_h */
|
||||
443
drivers/isdn/act2000/act2000_isa.c
Normal file
443
drivers/isdn/act2000/act2000_isa.c
Normal file
|
|
@ -0,0 +1,443 @@
|
|||
/* $Id: act2000_isa.c,v 1.11.6.3 2001/09/23 22:24:32 kai Exp $
|
||||
*
|
||||
* ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
|
||||
*
|
||||
* Author Fritz Elfert
|
||||
* Copyright by Fritz Elfert <fritz@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* Thanks to Friedemann Baitinger and IBM Germany
|
||||
*
|
||||
*/
|
||||
|
||||
#include "act2000.h"
|
||||
#include "act2000_isa.h"
|
||||
#include "capi.h"
|
||||
|
||||
/*
|
||||
* Reset Controller, then try to read the Card's signature.
|
||||
+ Return:
|
||||
* 1 = Signature found.
|
||||
* 0 = Signature not found.
|
||||
*/
|
||||
static int
|
||||
act2000_isa_reset(unsigned short portbase)
|
||||
{
|
||||
unsigned char reg;
|
||||
int i;
|
||||
int found;
|
||||
int serial = 0;
|
||||
|
||||
found = 0;
|
||||
if ((reg = inb(portbase + ISA_COR)) != 0xff) {
|
||||
outb(reg | ISA_COR_RESET, portbase + ISA_COR);
|
||||
mdelay(10);
|
||||
outb(reg, portbase + ISA_COR);
|
||||
mdelay(10);
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (inb(portbase + ISA_ISR) & ISA_ISR_SERIAL)
|
||||
serial |= 0x10000;
|
||||
serial >>= 1;
|
||||
}
|
||||
if (serial == ISA_SER_ID)
|
||||
found++;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
int
|
||||
act2000_isa_detect(unsigned short portbase)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (request_region(portbase, ACT2000_PORTLEN, "act2000isa")) {
|
||||
ret = act2000_isa_reset(portbase);
|
||||
release_region(portbase, ISA_REGION);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
act2000_isa_interrupt(int dummy, void *dev_id)
|
||||
{
|
||||
act2000_card *card = dev_id;
|
||||
u_char istatus;
|
||||
|
||||
istatus = (inb(ISA_PORT_ISR) & 0x07);
|
||||
if (istatus & ISA_ISR_OUT) {
|
||||
/* RX fifo has data */
|
||||
istatus &= ISA_ISR_OUT_MASK;
|
||||
outb(0, ISA_PORT_SIS);
|
||||
act2000_isa_receive(card);
|
||||
outb(ISA_SIS_INT, ISA_PORT_SIS);
|
||||
}
|
||||
if (istatus & ISA_ISR_ERR) {
|
||||
/* Error Interrupt */
|
||||
istatus &= ISA_ISR_ERR_MASK;
|
||||
printk(KERN_WARNING "act2000: errIRQ\n");
|
||||
}
|
||||
if (istatus)
|
||||
printk(KERN_DEBUG "act2000: ?IRQ %d %02x\n", card->irq, istatus);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void
|
||||
act2000_isa_select_irq(act2000_card *card)
|
||||
{
|
||||
unsigned char reg;
|
||||
|
||||
reg = (inb(ISA_PORT_COR) & ~ISA_COR_IRQOFF) | ISA_COR_PERR;
|
||||
switch (card->irq) {
|
||||
case 3:
|
||||
reg = ISA_COR_IRQ03;
|
||||
break;
|
||||
case 5:
|
||||
reg = ISA_COR_IRQ05;
|
||||
break;
|
||||
case 7:
|
||||
reg = ISA_COR_IRQ07;
|
||||
break;
|
||||
case 10:
|
||||
reg = ISA_COR_IRQ10;
|
||||
break;
|
||||
case 11:
|
||||
reg = ISA_COR_IRQ11;
|
||||
break;
|
||||
case 12:
|
||||
reg = ISA_COR_IRQ12;
|
||||
break;
|
||||
case 15:
|
||||
reg = ISA_COR_IRQ15;
|
||||
break;
|
||||
}
|
||||
outb(reg, ISA_PORT_COR);
|
||||
}
|
||||
|
||||
static void
|
||||
act2000_isa_enable_irq(act2000_card *card)
|
||||
{
|
||||
act2000_isa_select_irq(card);
|
||||
/* Enable READ irq */
|
||||
outb(ISA_SIS_INT, ISA_PORT_SIS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Install interrupt handler, enable irq on card.
|
||||
* If irq is -1, choose next free irq, else irq is given explicitly.
|
||||
*/
|
||||
int
|
||||
act2000_isa_config_irq(act2000_card *card, short irq)
|
||||
{
|
||||
int old_irq;
|
||||
|
||||
if (card->flags & ACT2000_FLAGS_IVALID) {
|
||||
free_irq(card->irq, card);
|
||||
}
|
||||
card->flags &= ~ACT2000_FLAGS_IVALID;
|
||||
outb(ISA_COR_IRQOFF, ISA_PORT_COR);
|
||||
if (!irq)
|
||||
return 0;
|
||||
|
||||
old_irq = card->irq;
|
||||
card->irq = irq;
|
||||
if (request_irq(irq, &act2000_isa_interrupt, 0, card->regname, card)) {
|
||||
card->irq = old_irq;
|
||||
card->flags |= ACT2000_FLAGS_IVALID;
|
||||
printk(KERN_WARNING
|
||||
"act2000: Could not request irq %d\n", irq);
|
||||
return -EBUSY;
|
||||
} else {
|
||||
act2000_isa_select_irq(card);
|
||||
/* Disable READ and WRITE irq */
|
||||
outb(0, ISA_PORT_SIS);
|
||||
outb(0, ISA_PORT_SOS);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
act2000_isa_config_port(act2000_card *card, unsigned short portbase)
|
||||
{
|
||||
if (card->flags & ACT2000_FLAGS_PVALID) {
|
||||
release_region(card->port, ISA_REGION);
|
||||
card->flags &= ~ACT2000_FLAGS_PVALID;
|
||||
}
|
||||
if (request_region(portbase, ACT2000_PORTLEN, card->regname) == NULL)
|
||||
return -EBUSY;
|
||||
else {
|
||||
card->port = portbase;
|
||||
card->flags |= ACT2000_FLAGS_PVALID;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Release ressources, used by an adaptor.
|
||||
*/
|
||||
void
|
||||
act2000_isa_release(act2000_card *card)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
if (card->flags & ACT2000_FLAGS_IVALID)
|
||||
free_irq(card->irq, card);
|
||||
|
||||
card->flags &= ~ACT2000_FLAGS_IVALID;
|
||||
if (card->flags & ACT2000_FLAGS_PVALID)
|
||||
release_region(card->port, ISA_REGION);
|
||||
card->flags &= ~ACT2000_FLAGS_PVALID;
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
}
|
||||
|
||||
static int
|
||||
act2000_isa_writeb(act2000_card *card, u_char data)
|
||||
{
|
||||
u_char timeout = 40;
|
||||
|
||||
while (timeout) {
|
||||
if (inb(ISA_PORT_SOS) & ISA_SOS_READY) {
|
||||
outb(data, ISA_PORT_SDO);
|
||||
return 0;
|
||||
} else {
|
||||
timeout--;
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
act2000_isa_readb(act2000_card *card, u_char *data)
|
||||
{
|
||||
u_char timeout = 40;
|
||||
|
||||
while (timeout) {
|
||||
if (inb(ISA_PORT_SIS) & ISA_SIS_READY) {
|
||||
*data = inb(ISA_PORT_SDI);
|
||||
return 0;
|
||||
} else {
|
||||
timeout--;
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
act2000_isa_receive(act2000_card *card)
|
||||
{
|
||||
u_char c;
|
||||
|
||||
if (test_and_set_bit(ACT2000_LOCK_RX, (void *) &card->ilock) != 0)
|
||||
return;
|
||||
while (!act2000_isa_readb(card, &c)) {
|
||||
if (card->idat.isa.rcvidx < 8) {
|
||||
card->idat.isa.rcvhdr[card->idat.isa.rcvidx++] = c;
|
||||
if (card->idat.isa.rcvidx == 8) {
|
||||
int valid = actcapi_chkhdr(card, (actcapi_msghdr *)&card->idat.isa.rcvhdr);
|
||||
|
||||
if (valid) {
|
||||
card->idat.isa.rcvlen = ((actcapi_msghdr *)&card->idat.isa.rcvhdr)->len;
|
||||
card->idat.isa.rcvskb = dev_alloc_skb(card->idat.isa.rcvlen);
|
||||
if (card->idat.isa.rcvskb == NULL) {
|
||||
card->idat.isa.rcvignore = 1;
|
||||
printk(KERN_WARNING
|
||||
"act2000_isa_receive: no memory\n");
|
||||
test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock);
|
||||
return;
|
||||
}
|
||||
memcpy(skb_put(card->idat.isa.rcvskb, 8), card->idat.isa.rcvhdr, 8);
|
||||
card->idat.isa.rcvptr = skb_put(card->idat.isa.rcvskb, card->idat.isa.rcvlen - 8);
|
||||
} else {
|
||||
card->idat.isa.rcvidx = 0;
|
||||
printk(KERN_WARNING
|
||||
"act2000_isa_receive: Invalid CAPI msg\n");
|
||||
{
|
||||
int i; __u8 *p; __u8 *t; __u8 tmp[30];
|
||||
for (i = 0, p = (__u8 *)&card->idat.isa.rcvhdr, t = tmp; i < 8; i++)
|
||||
t += sprintf(t, "%02x ", *(p++));
|
||||
printk(KERN_WARNING "act2000_isa_receive: %s\n", tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!card->idat.isa.rcvignore)
|
||||
*card->idat.isa.rcvptr++ = c;
|
||||
if (++card->idat.isa.rcvidx >= card->idat.isa.rcvlen) {
|
||||
if (!card->idat.isa.rcvignore) {
|
||||
skb_queue_tail(&card->rcvq, card->idat.isa.rcvskb);
|
||||
act2000_schedule_rx(card);
|
||||
}
|
||||
card->idat.isa.rcvidx = 0;
|
||||
card->idat.isa.rcvlen = 8;
|
||||
card->idat.isa.rcvignore = 0;
|
||||
card->idat.isa.rcvskb = NULL;
|
||||
card->idat.isa.rcvptr = card->idat.isa.rcvhdr;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(card->flags & ACT2000_FLAGS_IVALID)) {
|
||||
/* In polling mode, schedule myself */
|
||||
if ((card->idat.isa.rcvidx) &&
|
||||
(card->idat.isa.rcvignore ||
|
||||
(card->idat.isa.rcvidx < card->idat.isa.rcvlen)))
|
||||
act2000_schedule_poll(card);
|
||||
}
|
||||
test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock);
|
||||
}
|
||||
|
||||
void
|
||||
act2000_isa_send(act2000_card *card)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct sk_buff *skb;
|
||||
actcapi_msg *msg;
|
||||
int l;
|
||||
|
||||
if (test_and_set_bit(ACT2000_LOCK_TX, (void *) &card->ilock) != 0)
|
||||
return;
|
||||
while (1) {
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
if (!(card->sbuf)) {
|
||||
if ((card->sbuf = skb_dequeue(&card->sndq))) {
|
||||
card->ack_msg = card->sbuf->data;
|
||||
msg = (actcapi_msg *)card->sbuf->data;
|
||||
if ((msg->hdr.cmd.cmd == 0x86) &&
|
||||
(msg->hdr.cmd.subcmd == 0)) {
|
||||
/* Save flags in message */
|
||||
card->need_b3ack = msg->msg.data_b3_req.flags;
|
||||
msg->msg.data_b3_req.flags = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
if (!(card->sbuf)) {
|
||||
/* No more data to send */
|
||||
test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock);
|
||||
return;
|
||||
}
|
||||
skb = card->sbuf;
|
||||
l = 0;
|
||||
while (skb->len) {
|
||||
if (act2000_isa_writeb(card, *(skb->data))) {
|
||||
/* Fifo is full, but more data to send */
|
||||
test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock);
|
||||
/* Schedule myself */
|
||||
act2000_schedule_tx(card);
|
||||
return;
|
||||
}
|
||||
skb_pull(skb, 1);
|
||||
l++;
|
||||
}
|
||||
msg = (actcapi_msg *)card->ack_msg;
|
||||
if ((msg->hdr.cmd.cmd == 0x86) &&
|
||||
(msg->hdr.cmd.subcmd == 0)) {
|
||||
/*
|
||||
* If it's user data, reset data-ptr
|
||||
* and put skb into ackq.
|
||||
*/
|
||||
skb->data = card->ack_msg;
|
||||
/* Restore flags in message */
|
||||
msg->msg.data_b3_req.flags = card->need_b3ack;
|
||||
skb_queue_tail(&card->ackq, skb);
|
||||
} else
|
||||
dev_kfree_skb(skb);
|
||||
card->sbuf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get firmware ID, check for 'ISDN' signature.
|
||||
*/
|
||||
static int
|
||||
act2000_isa_getid(act2000_card *card)
|
||||
{
|
||||
|
||||
act2000_fwid fid;
|
||||
u_char *p = (u_char *)&fid;
|
||||
int count = 0;
|
||||
|
||||
while (1) {
|
||||
if (count > 510)
|
||||
return -EPROTO;
|
||||
if (act2000_isa_readb(card, p++))
|
||||
break;
|
||||
count++;
|
||||
}
|
||||
if (count <= 20) {
|
||||
printk(KERN_WARNING "act2000: No Firmware-ID!\n");
|
||||
return -ETIME;
|
||||
}
|
||||
*p = '\0';
|
||||
fid.revlen[0] = '\0';
|
||||
if (strcmp(fid.isdn, "ISDN")) {
|
||||
printk(KERN_WARNING "act2000: Wrong Firmware-ID!\n");
|
||||
return -EPROTO;
|
||||
}
|
||||
if ((p = strchr(fid.revision, '\n')))
|
||||
*p = '\0';
|
||||
printk(KERN_INFO "act2000: Firmware-ID: %s\n", fid.revision);
|
||||
if (card->flags & ACT2000_FLAGS_IVALID) {
|
||||
printk(KERN_DEBUG "Enabling Interrupts ...\n");
|
||||
act2000_isa_enable_irq(card);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Download microcode into card, check Firmware signature.
|
||||
*/
|
||||
int
|
||||
act2000_isa_download(act2000_card *card, act2000_ddef __user *cb)
|
||||
{
|
||||
unsigned int length;
|
||||
int l;
|
||||
int c;
|
||||
long timeout;
|
||||
u_char *b;
|
||||
u_char __user *p;
|
||||
u_char *buf;
|
||||
act2000_ddef cblock;
|
||||
|
||||
if (!act2000_isa_reset(card->port))
|
||||
return -ENXIO;
|
||||
msleep_interruptible(500);
|
||||
if (copy_from_user(&cblock, cb, sizeof(cblock)))
|
||||
return -EFAULT;
|
||||
length = cblock.length;
|
||||
p = cblock.buffer;
|
||||
if (!access_ok(VERIFY_READ, p, length))
|
||||
return -EFAULT;
|
||||
buf = kmalloc(1024, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
timeout = 0;
|
||||
while (length) {
|
||||
l = (length > 1024) ? 1024 : length;
|
||||
c = 0;
|
||||
b = buf;
|
||||
if (copy_from_user(buf, p, l)) {
|
||||
kfree(buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
while (c < l) {
|
||||
if (act2000_isa_writeb(card, *b++)) {
|
||||
printk(KERN_WARNING
|
||||
"act2000: loader timed out"
|
||||
" len=%d c=%d\n", length, c);
|
||||
kfree(buf);
|
||||
return -ETIME;
|
||||
}
|
||||
c++;
|
||||
}
|
||||
length -= l;
|
||||
p += l;
|
||||
}
|
||||
kfree(buf);
|
||||
msleep_interruptible(500);
|
||||
return (act2000_isa_getid(card));
|
||||
}
|
||||
136
drivers/isdn/act2000/act2000_isa.h
Normal file
136
drivers/isdn/act2000/act2000_isa.h
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/* $Id: act2000_isa.h,v 1.4.6.1 2001/09/23 22:24:32 kai Exp $
|
||||
*
|
||||
* ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
|
||||
*
|
||||
* Author Fritz Elfert
|
||||
* Copyright by Fritz Elfert <fritz@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* Thanks to Friedemann Baitinger and IBM Germany
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef act2000_isa_h
|
||||
#define act2000_isa_h
|
||||
|
||||
#define ISA_POLL_LOOP 40 /* Try to read-write before give up */
|
||||
|
||||
typedef enum {
|
||||
INT_NO_CHANGE = 0, /* Do not change the Mask */
|
||||
INT_ON = 1, /* Set to Enable */
|
||||
INT_OFF = 2, /* Set to Disable */
|
||||
} ISA_INT_T;
|
||||
|
||||
/**************************************************************************/
|
||||
/* Configuration Register COR (RW) */
|
||||
/**************************************************************************/
|
||||
/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */
|
||||
/* Soft Res| IRQM | IRQ Select | N/A | WAIT |Proc err */
|
||||
/**************************************************************************/
|
||||
#define ISA_COR 0 /* Offset for ISA config register */
|
||||
#define ISA_COR_PERR 0x01 /* Processor Error Enabled */
|
||||
#define ISA_COR_WS 0x02 /* Insert Wait State if 1 */
|
||||
#define ISA_COR_IRQOFF 0x38 /* No Interrupt */
|
||||
#define ISA_COR_IRQ07 0x30 /* IRQ 7 Enable */
|
||||
#define ISA_COR_IRQ05 0x28 /* IRQ 5 Enable */
|
||||
#define ISA_COR_IRQ03 0x20 /* IRQ 3 Enable */
|
||||
#define ISA_COR_IRQ10 0x18 /* IRQ 10 Enable */
|
||||
#define ISA_COR_IRQ11 0x10 /* IRQ 11 Enable */
|
||||
#define ISA_COR_IRQ12 0x08 /* IRQ 12 Enable */
|
||||
#define ISA_COR_IRQ15 0x00 /* IRQ 15 Enable */
|
||||
#define ISA_COR_IRQPULSE 0x40 /* 0 = Level 1 = Pulse Interrupt */
|
||||
#define ISA_COR_RESET 0x80 /* Soft Reset for Transputer */
|
||||
|
||||
/**************************************************************************/
|
||||
/* Interrupt Source Register ISR (RO) */
|
||||
/**************************************************************************/
|
||||
/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */
|
||||
/* N/A | N/A | N/A |Err sig |Ser ID |IN Intr |Out Intr| Error */
|
||||
/**************************************************************************/
|
||||
#define ISA_ISR 1 /* Offset for Interrupt Register */
|
||||
#define ISA_ISR_ERR 0x01 /* Error Interrupt */
|
||||
#define ISA_ISR_OUT 0x02 /* Output Interrupt */
|
||||
#define ISA_ISR_INP 0x04 /* Input Interrupt */
|
||||
#define ISA_ISR_SERIAL 0x08 /* Read out Serial ID after Reset */
|
||||
#define ISA_ISR_ERRSIG 0x10 /* Error Signal Input */
|
||||
#define ISA_ISR_ERR_MASK 0xfe /* Mask Error Interrupt */
|
||||
#define ISA_ISR_OUT_MASK 0xfd /* Mask Output Interrupt */
|
||||
#define ISA_ISR_INP_MASK 0xfb /* Mask Input Interrupt */
|
||||
|
||||
/* Signature delivered after Reset at ISA_ISR_SERIAL (LSB first) */
|
||||
#define ISA_SER_ID 0x0201 /* ID for ISA Card */
|
||||
|
||||
/**************************************************************************/
|
||||
/* EEPROM Register EPR (RW) */
|
||||
/**************************************************************************/
|
||||
/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */
|
||||
/* N/A | N/A | N/A |ROM Hold| ROM CS |ROM CLK | ROM IN |ROM Out */
|
||||
/**************************************************************************/
|
||||
#define ISA_EPR 2 /* Offset for this Register */
|
||||
#define ISA_EPR_OUT 0x01 /* Rome Register Out (RO) */
|
||||
#define ISA_EPR_IN 0x02 /* Rom Register In (WR) */
|
||||
#define ISA_EPR_CLK 0x04 /* Rom Clock (WR) */
|
||||
#define ISA_EPR_CS 0x08 /* Rom Cip Select (WR) */
|
||||
#define ISA_EPR_HOLD 0x10 /* Rom Hold Signal (WR) */
|
||||
|
||||
/**************************************************************************/
|
||||
/* EEPROM enable Register EER (unused) */
|
||||
/**************************************************************************/
|
||||
#define ISA_EER 3 /* Offset for this Register */
|
||||
|
||||
/**************************************************************************/
|
||||
/* SLC Data Input SDI (RO) */
|
||||
/**************************************************************************/
|
||||
#define ISA_SDI 4 /* Offset for this Register */
|
||||
|
||||
/**************************************************************************/
|
||||
/* SLC Data Output SDO (WO) */
|
||||
/**************************************************************************/
|
||||
#define ISA_SDO 5 /* Offset for this Register */
|
||||
|
||||
/**************************************************************************/
|
||||
/* IMS C011 Mode 2 Input Status Register for INMOS CPU SIS (RW) */
|
||||
/**************************************************************************/
|
||||
/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */
|
||||
/* N/A | N/A | N/A | N/A | N/A | N/A |Int Ena |Data Pre */
|
||||
/**************************************************************************/
|
||||
#define ISA_SIS 6 /* Offset for this Register */
|
||||
#define ISA_SIS_READY 0x01 /* If 1 : data is available */
|
||||
#define ISA_SIS_INT 0x02 /* Enable Interrupt for READ */
|
||||
|
||||
/**************************************************************************/
|
||||
/* IMS C011 Mode 2 Output Status Register from INMOS CPU SOS (RW) */
|
||||
/**************************************************************************/
|
||||
/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */
|
||||
/* N/A | N/A | N/A | N/A | N/A | N/A |Int Ena |Out Rdy */
|
||||
/**************************************************************************/
|
||||
#define ISA_SOS 7 /* Offset for this Register */
|
||||
#define ISA_SOS_READY 0x01 /* If 1 : we can write Data */
|
||||
#define ISA_SOS_INT 0x02 /* Enable Interrupt for WRITE */
|
||||
|
||||
#define ISA_REGION 8 /* Number of Registers */
|
||||
|
||||
|
||||
/* Macros for accessing ports */
|
||||
#define ISA_PORT_COR (card->port + ISA_COR)
|
||||
#define ISA_PORT_ISR (card->port + ISA_ISR)
|
||||
#define ISA_PORT_EPR (card->port + ISA_EPR)
|
||||
#define ISA_PORT_EER (card->port + ISA_EER)
|
||||
#define ISA_PORT_SDI (card->port + ISA_SDI)
|
||||
#define ISA_PORT_SDO (card->port + ISA_SDO)
|
||||
#define ISA_PORT_SIS (card->port + ISA_SIS)
|
||||
#define ISA_PORT_SOS (card->port + ISA_SOS)
|
||||
|
||||
/* Prototypes */
|
||||
|
||||
extern int act2000_isa_detect(unsigned short portbase);
|
||||
extern int act2000_isa_config_irq(act2000_card *card, short irq);
|
||||
extern int act2000_isa_config_port(act2000_card *card, unsigned short portbase);
|
||||
extern int act2000_isa_download(act2000_card *card, act2000_ddef __user *cb);
|
||||
extern void act2000_isa_release(act2000_card *card);
|
||||
extern void act2000_isa_receive(act2000_card *card);
|
||||
extern void act2000_isa_send(act2000_card *card);
|
||||
|
||||
#endif /* act2000_isa_h */
|
||||
1180
drivers/isdn/act2000/capi.c
Normal file
1180
drivers/isdn/act2000/capi.c
Normal file
File diff suppressed because it is too large
Load diff
365
drivers/isdn/act2000/capi.h
Normal file
365
drivers/isdn/act2000/capi.h
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
/* $Id: capi.h,v 1.6.6.2 2001/09/23 22:24:32 kai Exp $
|
||||
*
|
||||
* ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
|
||||
*
|
||||
* Author Fritz Elfert
|
||||
* Copyright by Fritz Elfert <fritz@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* Thanks to Friedemann Baitinger and IBM Germany
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CAPI_H
|
||||
#define CAPI_H
|
||||
|
||||
/* Command-part of a CAPI message */
|
||||
typedef struct actcapi_msgcmd {
|
||||
__u8 cmd;
|
||||
__u8 subcmd;
|
||||
} actcapi_msgcmd;
|
||||
|
||||
/* CAPI message header */
|
||||
typedef struct actcapi_msghdr {
|
||||
__u16 len;
|
||||
__u16 applicationID;
|
||||
actcapi_msgcmd cmd;
|
||||
__u16 msgnum;
|
||||
} actcapi_msghdr;
|
||||
|
||||
/* CAPI message description (for debugging) */
|
||||
typedef struct actcapi_msgdsc {
|
||||
actcapi_msgcmd cmd;
|
||||
char *description;
|
||||
} actcapi_msgdsc;
|
||||
|
||||
/* CAPI Address */
|
||||
typedef struct actcapi_addr {
|
||||
__u8 len; /* Length of element */
|
||||
__u8 tnp; /* Type/Numbering Plan */
|
||||
__u8 num[20]; /* Caller ID */
|
||||
} actcapi_addr;
|
||||
|
||||
/* CAPI INFO element mask */
|
||||
typedef union actcapi_infonr { /* info number */
|
||||
__u16 mask; /* info-mask field */
|
||||
struct bmask { /* bit definitions */
|
||||
unsigned codes:3; /* code set */
|
||||
unsigned rsvd:5; /* reserved */
|
||||
unsigned svind:1; /* single, variable length ind. */
|
||||
unsigned wtype:7; /* W-element type */
|
||||
} bmask;
|
||||
} actcapi_infonr;
|
||||
|
||||
/* CAPI INFO element */
|
||||
typedef union actcapi_infoel { /* info element */
|
||||
__u8 len; /* length of info element */
|
||||
__u8 display[40]; /* display contents */
|
||||
__u8 uuinfo[40]; /* User-user info field */
|
||||
struct cause { /* Cause information */
|
||||
unsigned ext2:1; /* extension */
|
||||
unsigned cod:2; /* coding standard */
|
||||
unsigned spare:1; /* spare */
|
||||
unsigned loc:4; /* location */
|
||||
unsigned ext1:1; /* extension */
|
||||
unsigned cval:7; /* Cause value */
|
||||
} cause;
|
||||
struct charge { /* Charging information */
|
||||
__u8 toc; /* type of charging info */
|
||||
__u8 unit[10]; /* charging units */
|
||||
} charge;
|
||||
__u8 date[20]; /* date fields */
|
||||
__u8 stat; /* state of remote party */
|
||||
} actcapi_infoel;
|
||||
|
||||
/* Message for EAZ<->MSN Mapping */
|
||||
typedef struct actcapi_msn {
|
||||
__u8 eaz;
|
||||
__u8 len; /* Length of MSN */
|
||||
__u8 msn[15];
|
||||
} __attribute__((packed)) actcapi_msn;
|
||||
|
||||
typedef struct actcapi_dlpd {
|
||||
__u8 len; /* Length of structure */
|
||||
__u16 dlen; /* Data Length */
|
||||
__u8 laa; /* Link Address A */
|
||||
__u8 lab; /* Link Address B */
|
||||
__u8 modulo; /* Modulo Mode */
|
||||
__u8 win; /* Window size */
|
||||
__u8 xid[100]; /* XID Information */
|
||||
} __attribute__((packed)) actcapi_dlpd;
|
||||
|
||||
typedef struct actcapi_ncpd {
|
||||
__u8 len; /* Length of structure */
|
||||
__u16 lic;
|
||||
__u16 hic;
|
||||
__u16 ltc;
|
||||
__u16 htc;
|
||||
__u16 loc;
|
||||
__u16 hoc;
|
||||
__u8 modulo;
|
||||
} __attribute__((packed)) actcapi_ncpd;
|
||||
#define actcapi_ncpi actcapi_ncpd
|
||||
|
||||
/*
|
||||
* Layout of NCCI field in a B3 DATA CAPI message is different from
|
||||
* standard at act2000:
|
||||
*
|
||||
* Bit 0-4 = PLCI
|
||||
* Bit 5-7 = Controller
|
||||
* Bit 8-15 = NCCI
|
||||
*/
|
||||
#define MAKE_NCCI(plci, contr, ncci) \
|
||||
((plci & 0x1f) | ((contr & 0x7) << 5) | ((ncci & 0xff) << 8))
|
||||
|
||||
#define EVAL_NCCI(fakencci, plci, contr, ncci) { \
|
||||
plci = fakencci & 0x1f; \
|
||||
contr = (fakencci >> 5) & 0x7; \
|
||||
ncci = (fakencci >> 8) & 0xff; \
|
||||
}
|
||||
|
||||
/*
|
||||
* Layout of PLCI field in a B3 DATA CAPI message is different from
|
||||
* standard at act2000:
|
||||
*
|
||||
* Bit 0-4 = PLCI
|
||||
* Bit 5-7 = Controller
|
||||
* Bit 8-15 = reserved (must be 0)
|
||||
*/
|
||||
#define MAKE_PLCI(plci, contr) \
|
||||
((plci & 0x1f) | ((contr & 0x7) << 5))
|
||||
|
||||
#define EVAL_PLCI(fakeplci, plci, contr) { \
|
||||
plci = fakeplci & 0x1f; \
|
||||
contr = (fakeplci >> 5) & 0x7; \
|
||||
}
|
||||
|
||||
typedef struct actcapi_msg {
|
||||
actcapi_msghdr hdr;
|
||||
union {
|
||||
__u16 manuf_msg;
|
||||
struct manufacturer_req_net {
|
||||
__u16 manuf_msg;
|
||||
__u16 controller;
|
||||
__u8 nettype;
|
||||
} manufacturer_req_net;
|
||||
struct manufacturer_req_v42 {
|
||||
__u16 manuf_msg;
|
||||
__u16 controller;
|
||||
__u32 v42control;
|
||||
} manufacturer_req_v42;
|
||||
struct manufacturer_conf_v42 {
|
||||
__u16 manuf_msg;
|
||||
__u16 controller;
|
||||
} manufacturer_conf_v42;
|
||||
struct manufacturer_req_err {
|
||||
__u16 manuf_msg;
|
||||
__u16 controller;
|
||||
} manufacturer_req_err;
|
||||
struct manufacturer_ind_err {
|
||||
__u16 manuf_msg;
|
||||
__u16 controller;
|
||||
__u32 errcode;
|
||||
__u8 errstring; /* actually up to 160 */
|
||||
} manufacturer_ind_err;
|
||||
struct manufacturer_req_msn {
|
||||
__u16 manuf_msg;
|
||||
__u16 controller;
|
||||
actcapi_msn msnmap;
|
||||
} __attribute ((packed)) manufacturer_req_msn;
|
||||
/* TODO: TraceInit-req/conf/ind/resp and
|
||||
* TraceDump-req/conf/ind/resp
|
||||
*/
|
||||
struct connect_req {
|
||||
__u8 controller;
|
||||
__u8 bchan;
|
||||
__u32 infomask;
|
||||
__u8 si1;
|
||||
__u8 si2;
|
||||
__u8 eaz;
|
||||
actcapi_addr addr;
|
||||
} __attribute__ ((packed)) connect_req;
|
||||
struct connect_conf {
|
||||
__u16 plci;
|
||||
__u16 info;
|
||||
} connect_conf;
|
||||
struct connect_ind {
|
||||
__u16 plci;
|
||||
__u8 controller;
|
||||
__u8 si1;
|
||||
__u8 si2;
|
||||
__u8 eaz;
|
||||
actcapi_addr addr;
|
||||
} __attribute__ ((packed)) connect_ind;
|
||||
struct connect_resp {
|
||||
__u16 plci;
|
||||
__u8 rejectcause;
|
||||
} connect_resp;
|
||||
struct connect_active_ind {
|
||||
__u16 plci;
|
||||
actcapi_addr addr;
|
||||
} __attribute__ ((packed)) connect_active_ind;
|
||||
struct connect_active_resp {
|
||||
__u16 plci;
|
||||
} connect_active_resp;
|
||||
struct connect_b3_req {
|
||||
__u16 plci;
|
||||
actcapi_ncpi ncpi;
|
||||
} __attribute__ ((packed)) connect_b3_req;
|
||||
struct connect_b3_conf {
|
||||
__u16 plci;
|
||||
__u16 ncci;
|
||||
__u16 info;
|
||||
} connect_b3_conf;
|
||||
struct connect_b3_ind {
|
||||
__u16 ncci;
|
||||
__u16 plci;
|
||||
actcapi_ncpi ncpi;
|
||||
} __attribute__ ((packed)) connect_b3_ind;
|
||||
struct connect_b3_resp {
|
||||
__u16 ncci;
|
||||
__u8 rejectcause;
|
||||
actcapi_ncpi ncpi;
|
||||
} __attribute__ ((packed)) connect_b3_resp;
|
||||
struct disconnect_req {
|
||||
__u16 plci;
|
||||
__u8 cause;
|
||||
} disconnect_req;
|
||||
struct disconnect_conf {
|
||||
__u16 plci;
|
||||
__u16 info;
|
||||
} disconnect_conf;
|
||||
struct disconnect_ind {
|
||||
__u16 plci;
|
||||
__u16 info;
|
||||
} disconnect_ind;
|
||||
struct disconnect_resp {
|
||||
__u16 plci;
|
||||
} disconnect_resp;
|
||||
struct connect_b3_active_ind {
|
||||
__u16 ncci;
|
||||
actcapi_ncpi ncpi;
|
||||
} __attribute__ ((packed)) connect_b3_active_ind;
|
||||
struct connect_b3_active_resp {
|
||||
__u16 ncci;
|
||||
} connect_b3_active_resp;
|
||||
struct disconnect_b3_req {
|
||||
__u16 ncci;
|
||||
actcapi_ncpi ncpi;
|
||||
} __attribute__ ((packed)) disconnect_b3_req;
|
||||
struct disconnect_b3_conf {
|
||||
__u16 ncci;
|
||||
__u16 info;
|
||||
} disconnect_b3_conf;
|
||||
struct disconnect_b3_ind {
|
||||
__u16 ncci;
|
||||
__u16 info;
|
||||
actcapi_ncpi ncpi;
|
||||
} __attribute__ ((packed)) disconnect_b3_ind;
|
||||
struct disconnect_b3_resp {
|
||||
__u16 ncci;
|
||||
} disconnect_b3_resp;
|
||||
struct info_ind {
|
||||
__u16 plci;
|
||||
actcapi_infonr nr;
|
||||
actcapi_infoel el;
|
||||
} __attribute__ ((packed)) info_ind;
|
||||
struct info_resp {
|
||||
__u16 plci;
|
||||
} info_resp;
|
||||
struct listen_b3_req {
|
||||
__u16 plci;
|
||||
} listen_b3_req;
|
||||
struct listen_b3_conf {
|
||||
__u16 plci;
|
||||
__u16 info;
|
||||
} listen_b3_conf;
|
||||
struct select_b2_protocol_req {
|
||||
__u16 plci;
|
||||
__u8 protocol;
|
||||
actcapi_dlpd dlpd;
|
||||
} __attribute__ ((packed)) select_b2_protocol_req;
|
||||
struct select_b2_protocol_conf {
|
||||
__u16 plci;
|
||||
__u16 info;
|
||||
} select_b2_protocol_conf;
|
||||
struct select_b3_protocol_req {
|
||||
__u16 plci;
|
||||
__u8 protocol;
|
||||
actcapi_ncpd ncpd;
|
||||
} __attribute__ ((packed)) select_b3_protocol_req;
|
||||
struct select_b3_protocol_conf {
|
||||
__u16 plci;
|
||||
__u16 info;
|
||||
} select_b3_protocol_conf;
|
||||
struct listen_req {
|
||||
__u8 controller;
|
||||
__u32 infomask;
|
||||
__u16 eazmask;
|
||||
__u16 simask;
|
||||
} __attribute__ ((packed)) listen_req;
|
||||
struct listen_conf {
|
||||
__u8 controller;
|
||||
__u16 info;
|
||||
} __attribute__ ((packed)) listen_conf;
|
||||
struct data_b3_req {
|
||||
__u16 fakencci;
|
||||
__u16 datalen;
|
||||
__u32 unused;
|
||||
__u8 blocknr;
|
||||
__u16 flags;
|
||||
} __attribute ((packed)) data_b3_req;
|
||||
struct data_b3_ind {
|
||||
__u16 fakencci;
|
||||
__u16 datalen;
|
||||
__u32 unused;
|
||||
__u8 blocknr;
|
||||
__u16 flags;
|
||||
} __attribute__ ((packed)) data_b3_ind;
|
||||
struct data_b3_resp {
|
||||
__u16 ncci;
|
||||
__u8 blocknr;
|
||||
} __attribute__ ((packed)) data_b3_resp;
|
||||
struct data_b3_conf {
|
||||
__u16 ncci;
|
||||
__u8 blocknr;
|
||||
__u16 info;
|
||||
} __attribute__ ((packed)) data_b3_conf;
|
||||
} msg;
|
||||
} __attribute__ ((packed)) actcapi_msg;
|
||||
|
||||
static inline unsigned short
|
||||
actcapi_nextsmsg(act2000_card *card)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned short n;
|
||||
|
||||
spin_lock_irqsave(&card->mnlock, flags);
|
||||
n = card->msgnum;
|
||||
card->msgnum++;
|
||||
card->msgnum &= 0x7fff;
|
||||
spin_unlock_irqrestore(&card->mnlock, flags);
|
||||
return n;
|
||||
}
|
||||
#define DEBUG_MSG
|
||||
#undef DEBUG_DATA_MSG
|
||||
#undef DEBUG_DUMP_SKB
|
||||
|
||||
extern int actcapi_chkhdr(act2000_card *, actcapi_msghdr *);
|
||||
extern int actcapi_listen_req(act2000_card *);
|
||||
extern int actcapi_manufacturer_req_net(act2000_card *);
|
||||
extern int actcapi_manufacturer_req_errh(act2000_card *);
|
||||
extern int actcapi_manufacturer_req_msn(act2000_card *);
|
||||
extern int actcapi_connect_req(act2000_card *, act2000_chan *, char *, char, int, int);
|
||||
extern void actcapi_select_b2_protocol_req(act2000_card *, act2000_chan *);
|
||||
extern void actcapi_disconnect_b3_req(act2000_card *, act2000_chan *);
|
||||
extern void actcapi_connect_resp(act2000_card *, act2000_chan *, __u8);
|
||||
extern void actcapi_dispatch(struct work_struct *);
|
||||
#ifdef DEBUG_MSG
|
||||
extern void actcapi_debug_msg(struct sk_buff *skb, int);
|
||||
#else
|
||||
#define actcapi_debug_msg(skb, len)
|
||||
#endif
|
||||
#endif
|
||||
813
drivers/isdn/act2000/module.c
Normal file
813
drivers/isdn/act2000/module.c
Normal file
|
|
@ -0,0 +1,813 @@
|
|||
/* $Id: module.c,v 1.14.6.4 2001/09/23 22:24:32 kai Exp $
|
||||
*
|
||||
* ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
|
||||
*
|
||||
* Author Fritz Elfert
|
||||
* Copyright by Fritz Elfert <fritz@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* Thanks to Friedemann Baitinger and IBM Germany
|
||||
*
|
||||
*/
|
||||
|
||||
#include "act2000.h"
|
||||
#include "act2000_isa.h"
|
||||
#include "capi.h"
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
static unsigned short act2000_isa_ports[] =
|
||||
{
|
||||
0x0200, 0x0240, 0x0280, 0x02c0, 0x0300, 0x0340, 0x0380,
|
||||
0xcfe0, 0xcfa0, 0xcf60, 0xcf20, 0xcee0, 0xcea0, 0xce60,
|
||||
};
|
||||
|
||||
static act2000_card *cards = (act2000_card *) NULL;
|
||||
|
||||
/* Parameters to be set by insmod */
|
||||
static int act_bus = 0;
|
||||
static int act_port = -1; /* -1 = Autoprobe */
|
||||
static int act_irq = -1;
|
||||
static char *act_id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
||||
|
||||
MODULE_DESCRIPTION("ISDN4Linux: Driver for IBM Active 2000 ISDN card");
|
||||
MODULE_AUTHOR("Fritz Elfert");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_PARM_DESC(act_bus, "BusType of first card, 1=ISA, 2=MCA, 3=PCMCIA, currently only ISA");
|
||||
MODULE_PARM_DESC(membase, "Base port address of first card");
|
||||
MODULE_PARM_DESC(act_irq, "IRQ of first card");
|
||||
MODULE_PARM_DESC(act_id, "ID-String of first card");
|
||||
module_param(act_bus, int, 0);
|
||||
module_param(act_port, int, 0);
|
||||
module_param(act_irq, int, 0);
|
||||
module_param(act_id, charp, 0);
|
||||
|
||||
static int act2000_addcard(int, int, int, char *);
|
||||
|
||||
static act2000_chan *
|
||||
find_channel(act2000_card *card, int channel)
|
||||
{
|
||||
if ((channel >= 0) && (channel < ACT2000_BCH))
|
||||
return &(card->bch[channel]);
|
||||
printk(KERN_WARNING "act2000: Invalid channel %d\n", channel);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free MSN list
|
||||
*/
|
||||
static void
|
||||
act2000_clear_msn(act2000_card *card)
|
||||
{
|
||||
struct msn_entry *p = card->msn_list;
|
||||
struct msn_entry *q;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
card->msn_list = NULL;
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
while (p) {
|
||||
q = p->next;
|
||||
kfree(p);
|
||||
p = q;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Find an MSN entry in the list.
|
||||
* If ia5 != 0, return IA5-encoded EAZ, else
|
||||
* return a bitmask with corresponding bit set.
|
||||
*/
|
||||
static __u16
|
||||
act2000_find_msn(act2000_card *card, char *msn, int ia5)
|
||||
{
|
||||
struct msn_entry *p = card->msn_list;
|
||||
__u8 eaz = '0';
|
||||
|
||||
while (p) {
|
||||
if (!strcmp(p->msn, msn)) {
|
||||
eaz = p->eaz;
|
||||
break;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
if (!ia5)
|
||||
return (1 << (eaz - '0'));
|
||||
else
|
||||
return eaz;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find an EAZ entry in the list.
|
||||
* return a string with corresponding msn.
|
||||
*/
|
||||
char *
|
||||
act2000_find_eaz(act2000_card *card, char eaz)
|
||||
{
|
||||
struct msn_entry *p = card->msn_list;
|
||||
|
||||
while (p) {
|
||||
if (p->eaz == eaz)
|
||||
return (p->msn);
|
||||
p = p->next;
|
||||
}
|
||||
return ("\0");
|
||||
}
|
||||
|
||||
/*
|
||||
* Add or delete an MSN to the MSN list
|
||||
*
|
||||
* First character of msneaz is EAZ, rest is MSN.
|
||||
* If length of eazmsn is 1, delete that entry.
|
||||
*/
|
||||
static int
|
||||
act2000_set_msn(act2000_card *card, char *eazmsn)
|
||||
{
|
||||
struct msn_entry *p = card->msn_list;
|
||||
struct msn_entry *q = NULL;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
if (!strlen(eazmsn))
|
||||
return 0;
|
||||
if (strlen(eazmsn) > 16)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < strlen(eazmsn); i++)
|
||||
if (!isdigit(eazmsn[i]))
|
||||
return -EINVAL;
|
||||
if (strlen(eazmsn) == 1) {
|
||||
/* Delete a single MSN */
|
||||
while (p) {
|
||||
if (p->eaz == eazmsn[0]) {
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
if (q)
|
||||
q->next = p->next;
|
||||
else
|
||||
card->msn_list = p->next;
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
kfree(p);
|
||||
printk(KERN_DEBUG
|
||||
"Mapping for EAZ %c deleted\n",
|
||||
eazmsn[0]);
|
||||
return 0;
|
||||
}
|
||||
q = p;
|
||||
p = p->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* Add a single MSN */
|
||||
while (p) {
|
||||
/* Found in list, replace MSN */
|
||||
if (p->eaz == eazmsn[0]) {
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
strcpy(p->msn, &eazmsn[1]);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
printk(KERN_DEBUG
|
||||
"Mapping for EAZ %c changed to %s\n",
|
||||
eazmsn[0],
|
||||
&eazmsn[1]);
|
||||
return 0;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
/* Not found in list, add new entry */
|
||||
p = kmalloc(sizeof(msn_entry), GFP_KERNEL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
p->eaz = eazmsn[0];
|
||||
strcpy(p->msn, &eazmsn[1]);
|
||||
p->next = card->msn_list;
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
card->msn_list = p;
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
printk(KERN_DEBUG
|
||||
"Mapping %c -> %s added\n",
|
||||
eazmsn[0],
|
||||
&eazmsn[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
act2000_transmit(struct work_struct *work)
|
||||
{
|
||||
struct act2000_card *card =
|
||||
container_of(work, struct act2000_card, snd_tq);
|
||||
|
||||
switch (card->bus) {
|
||||
case ACT2000_BUS_ISA:
|
||||
act2000_isa_send(card);
|
||||
break;
|
||||
case ACT2000_BUS_PCMCIA:
|
||||
case ACT2000_BUS_MCA:
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"act2000_transmit: Illegal bustype %d\n", card->bus);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
act2000_receive(struct work_struct *work)
|
||||
{
|
||||
struct act2000_card *card =
|
||||
container_of(work, struct act2000_card, poll_tq);
|
||||
|
||||
switch (card->bus) {
|
||||
case ACT2000_BUS_ISA:
|
||||
act2000_isa_receive(card);
|
||||
break;
|
||||
case ACT2000_BUS_PCMCIA:
|
||||
case ACT2000_BUS_MCA:
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"act2000_receive: Illegal bustype %d\n", card->bus);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
act2000_poll(unsigned long data)
|
||||
{
|
||||
act2000_card *card = (act2000_card *)data;
|
||||
unsigned long flags;
|
||||
|
||||
act2000_receive(&card->poll_tq);
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
mod_timer(&card->ptimer, jiffies + 3);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
}
|
||||
|
||||
static int
|
||||
act2000_command(act2000_card *card, isdn_ctrl *c)
|
||||
{
|
||||
ulong a;
|
||||
act2000_chan *chan;
|
||||
act2000_cdef cdef;
|
||||
isdn_ctrl cmd;
|
||||
char tmp[17];
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
void __user *arg;
|
||||
|
||||
switch (c->command) {
|
||||
case ISDN_CMD_IOCTL:
|
||||
memcpy(&a, c->parm.num, sizeof(ulong));
|
||||
arg = (void __user *)a;
|
||||
switch (c->arg) {
|
||||
case ACT2000_IOCTL_LOADBOOT:
|
||||
switch (card->bus) {
|
||||
case ACT2000_BUS_ISA:
|
||||
ret = act2000_isa_download(card,
|
||||
arg);
|
||||
if (!ret) {
|
||||
card->flags |= ACT2000_FLAGS_LOADED;
|
||||
if (!(card->flags & ACT2000_FLAGS_IVALID)) {
|
||||
card->ptimer.expires = jiffies + 3;
|
||||
card->ptimer.function = act2000_poll;
|
||||
card->ptimer.data = (unsigned long)card;
|
||||
add_timer(&card->ptimer);
|
||||
}
|
||||
actcapi_manufacturer_req_errh(card);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"act2000: Illegal BUS type %d\n",
|
||||
card->bus);
|
||||
ret = -EIO;
|
||||
}
|
||||
return ret;
|
||||
case ACT2000_IOCTL_SETPROTO:
|
||||
card->ptype = a ? ISDN_PTYPE_EURO : ISDN_PTYPE_1TR6;
|
||||
if (!(card->flags & ACT2000_FLAGS_RUNNING))
|
||||
return 0;
|
||||
actcapi_manufacturer_req_net(card);
|
||||
return 0;
|
||||
case ACT2000_IOCTL_SETMSN:
|
||||
if (copy_from_user(tmp, arg,
|
||||
sizeof(tmp)))
|
||||
return -EFAULT;
|
||||
if ((ret = act2000_set_msn(card, tmp)))
|
||||
return ret;
|
||||
if (card->flags & ACT2000_FLAGS_RUNNING)
|
||||
return (actcapi_manufacturer_req_msn(card));
|
||||
return 0;
|
||||
case ACT2000_IOCTL_ADDCARD:
|
||||
if (copy_from_user(&cdef, arg,
|
||||
sizeof(cdef)))
|
||||
return -EFAULT;
|
||||
if (act2000_addcard(cdef.bus, cdef.port, cdef.irq, cdef.id))
|
||||
return -EIO;
|
||||
return 0;
|
||||
case ACT2000_IOCTL_TEST:
|
||||
if (!(card->flags & ACT2000_FLAGS_RUNNING))
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case ISDN_CMD_DIAL:
|
||||
if (!(card->flags & ACT2000_FLAGS_RUNNING))
|
||||
return -ENODEV;
|
||||
if (!(chan = find_channel(card, c->arg & 0x0f)))
|
||||
break;
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
if (chan->fsm_state != ACT2000_STATE_NULL) {
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
printk(KERN_WARNING "Dial on channel with state %d\n",
|
||||
chan->fsm_state);
|
||||
return -EBUSY;
|
||||
}
|
||||
if (card->ptype == ISDN_PTYPE_EURO)
|
||||
tmp[0] = act2000_find_msn(card, c->parm.setup.eazmsn, 1);
|
||||
else
|
||||
tmp[0] = c->parm.setup.eazmsn[0];
|
||||
chan->fsm_state = ACT2000_STATE_OCALL;
|
||||
chan->callref = 0xffff;
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
ret = actcapi_connect_req(card, chan, c->parm.setup.phone,
|
||||
tmp[0], c->parm.setup.si1,
|
||||
c->parm.setup.si2);
|
||||
if (ret) {
|
||||
cmd.driver = card->myid;
|
||||
cmd.command = ISDN_STAT_DHUP;
|
||||
cmd.arg &= 0x0f;
|
||||
card->interface.statcallb(&cmd);
|
||||
}
|
||||
return ret;
|
||||
case ISDN_CMD_ACCEPTD:
|
||||
if (!(card->flags & ACT2000_FLAGS_RUNNING))
|
||||
return -ENODEV;
|
||||
if (!(chan = find_channel(card, c->arg & 0x0f)))
|
||||
break;
|
||||
if (chan->fsm_state == ACT2000_STATE_ICALL)
|
||||
actcapi_select_b2_protocol_req(card, chan);
|
||||
return 0;
|
||||
case ISDN_CMD_ACCEPTB:
|
||||
if (!(card->flags & ACT2000_FLAGS_RUNNING))
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
case ISDN_CMD_HANGUP:
|
||||
if (!(card->flags & ACT2000_FLAGS_RUNNING))
|
||||
return -ENODEV;
|
||||
if (!(chan = find_channel(card, c->arg & 0x0f)))
|
||||
break;
|
||||
switch (chan->fsm_state) {
|
||||
case ACT2000_STATE_ICALL:
|
||||
case ACT2000_STATE_BSETUP:
|
||||
actcapi_connect_resp(card, chan, 0x15);
|
||||
break;
|
||||
case ACT2000_STATE_ACTIVE:
|
||||
actcapi_disconnect_b3_req(card, chan);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
case ISDN_CMD_SETEAZ:
|
||||
if (!(card->flags & ACT2000_FLAGS_RUNNING))
|
||||
return -ENODEV;
|
||||
if (!(chan = find_channel(card, c->arg & 0x0f)))
|
||||
break;
|
||||
if (strlen(c->parm.num)) {
|
||||
if (card->ptype == ISDN_PTYPE_EURO) {
|
||||
chan->eazmask = act2000_find_msn(card, c->parm.num, 0);
|
||||
}
|
||||
if (card->ptype == ISDN_PTYPE_1TR6) {
|
||||
int i;
|
||||
chan->eazmask = 0;
|
||||
for (i = 0; i < strlen(c->parm.num); i++)
|
||||
if (isdigit(c->parm.num[i]))
|
||||
chan->eazmask |= (1 << (c->parm.num[i] - '0'));
|
||||
}
|
||||
} else
|
||||
chan->eazmask = 0x3ff;
|
||||
actcapi_listen_req(card);
|
||||
return 0;
|
||||
case ISDN_CMD_CLREAZ:
|
||||
if (!(card->flags & ACT2000_FLAGS_RUNNING))
|
||||
return -ENODEV;
|
||||
if (!(chan = find_channel(card, c->arg & 0x0f)))
|
||||
break;
|
||||
chan->eazmask = 0;
|
||||
actcapi_listen_req(card);
|
||||
return 0;
|
||||
case ISDN_CMD_SETL2:
|
||||
if (!(card->flags & ACT2000_FLAGS_RUNNING))
|
||||
return -ENODEV;
|
||||
if (!(chan = find_channel(card, c->arg & 0x0f)))
|
||||
break;
|
||||
chan->l2prot = (c->arg >> 8);
|
||||
return 0;
|
||||
case ISDN_CMD_SETL3:
|
||||
if (!(card->flags & ACT2000_FLAGS_RUNNING))
|
||||
return -ENODEV;
|
||||
if ((c->arg >> 8) != ISDN_PROTO_L3_TRANS) {
|
||||
printk(KERN_WARNING "L3 protocol unknown\n");
|
||||
return -1;
|
||||
}
|
||||
if (!(chan = find_channel(card, c->arg & 0x0f)))
|
||||
break;
|
||||
chan->l3prot = (c->arg >> 8);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
act2000_sendbuf(act2000_card *card, int channel, int ack, struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *xmit_skb;
|
||||
int len;
|
||||
act2000_chan *chan;
|
||||
actcapi_msg *msg;
|
||||
|
||||
if (!(chan = find_channel(card, channel)))
|
||||
return -1;
|
||||
if (chan->fsm_state != ACT2000_STATE_ACTIVE)
|
||||
return -1;
|
||||
len = skb->len;
|
||||
if ((chan->queued + len) >= ACT2000_MAX_QUEUED)
|
||||
return 0;
|
||||
if (!len)
|
||||
return 0;
|
||||
if (skb_headroom(skb) < 19) {
|
||||
printk(KERN_WARNING "act2000_sendbuf: Headroom only %d\n",
|
||||
skb_headroom(skb));
|
||||
xmit_skb = alloc_skb(len + 19, GFP_ATOMIC);
|
||||
if (!xmit_skb) {
|
||||
printk(KERN_WARNING "act2000_sendbuf: Out of memory\n");
|
||||
return 0;
|
||||
}
|
||||
skb_reserve(xmit_skb, 19);
|
||||
skb_copy_from_linear_data(skb, skb_put(xmit_skb, len), len);
|
||||
} else {
|
||||
xmit_skb = skb_clone(skb, GFP_ATOMIC);
|
||||
if (!xmit_skb) {
|
||||
printk(KERN_WARNING "act2000_sendbuf: Out of memory\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
dev_kfree_skb(skb);
|
||||
msg = (actcapi_msg *)skb_push(xmit_skb, 19);
|
||||
msg->hdr.len = 19 + len;
|
||||
msg->hdr.applicationID = 1;
|
||||
msg->hdr.cmd.cmd = 0x86;
|
||||
msg->hdr.cmd.subcmd = 0x00;
|
||||
msg->hdr.msgnum = actcapi_nextsmsg(card);
|
||||
msg->msg.data_b3_req.datalen = len;
|
||||
msg->msg.data_b3_req.blocknr = (msg->hdr.msgnum & 0xff);
|
||||
msg->msg.data_b3_req.fakencci = MAKE_NCCI(chan->plci, 0, chan->ncci);
|
||||
msg->msg.data_b3_req.flags = ack; /* Will be set to 0 on actual sending */
|
||||
actcapi_debug_msg(xmit_skb, 1);
|
||||
chan->queued += len;
|
||||
skb_queue_tail(&card->sndq, xmit_skb);
|
||||
act2000_schedule_tx(card);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/* Read the Status-replies from the Interface */
|
||||
static int
|
||||
act2000_readstatus(u_char __user *buf, int len, act2000_card *card)
|
||||
{
|
||||
int count;
|
||||
u_char __user *p;
|
||||
|
||||
for (p = buf, count = 0; count < len; p++, count++) {
|
||||
if (card->status_buf_read == card->status_buf_write)
|
||||
return count;
|
||||
put_user(*card->status_buf_read++, p);
|
||||
if (card->status_buf_read > card->status_buf_end)
|
||||
card->status_buf_read = card->status_buf;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find card with given driverId
|
||||
*/
|
||||
static inline act2000_card *
|
||||
act2000_findcard(int driverid)
|
||||
{
|
||||
act2000_card *p = cards;
|
||||
|
||||
while (p) {
|
||||
if (p->myid == driverid)
|
||||
return p;
|
||||
p = p->next;
|
||||
}
|
||||
return (act2000_card *) 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper functions for interface to linklevel
|
||||
*/
|
||||
static int
|
||||
if_command(isdn_ctrl *c)
|
||||
{
|
||||
act2000_card *card = act2000_findcard(c->driver);
|
||||
|
||||
if (card)
|
||||
return (act2000_command(card, c));
|
||||
printk(KERN_ERR
|
||||
"act2000: if_command %d called with invalid driverId %d!\n",
|
||||
c->command, c->driver);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int
|
||||
if_writecmd(const u_char __user *buf, int len, int id, int channel)
|
||||
{
|
||||
act2000_card *card = act2000_findcard(id);
|
||||
|
||||
if (card) {
|
||||
if (!(card->flags & ACT2000_FLAGS_RUNNING))
|
||||
return -ENODEV;
|
||||
return (len);
|
||||
}
|
||||
printk(KERN_ERR
|
||||
"act2000: if_writecmd called with invalid driverId!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int
|
||||
if_readstatus(u_char __user *buf, int len, int id, int channel)
|
||||
{
|
||||
act2000_card *card = act2000_findcard(id);
|
||||
|
||||
if (card) {
|
||||
if (!(card->flags & ACT2000_FLAGS_RUNNING))
|
||||
return -ENODEV;
|
||||
return (act2000_readstatus(buf, len, card));
|
||||
}
|
||||
printk(KERN_ERR
|
||||
"act2000: if_readstatus called with invalid driverId!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int
|
||||
if_sendbuf(int id, int channel, int ack, struct sk_buff *skb)
|
||||
{
|
||||
act2000_card *card = act2000_findcard(id);
|
||||
|
||||
if (card) {
|
||||
if (!(card->flags & ACT2000_FLAGS_RUNNING))
|
||||
return -ENODEV;
|
||||
return (act2000_sendbuf(card, channel, ack, skb));
|
||||
}
|
||||
printk(KERN_ERR
|
||||
"act2000: if_sendbuf called with invalid driverId!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Allocate a new card-struct, initialize it
|
||||
* link it into cards-list.
|
||||
*/
|
||||
static void
|
||||
act2000_alloccard(int bus, int port, int irq, char *id)
|
||||
{
|
||||
int i;
|
||||
act2000_card *card;
|
||||
if (!(card = kzalloc(sizeof(act2000_card), GFP_KERNEL))) {
|
||||
printk(KERN_WARNING
|
||||
"act2000: (%s) Could not allocate card-struct.\n", id);
|
||||
return;
|
||||
}
|
||||
spin_lock_init(&card->lock);
|
||||
spin_lock_init(&card->mnlock);
|
||||
skb_queue_head_init(&card->sndq);
|
||||
skb_queue_head_init(&card->rcvq);
|
||||
skb_queue_head_init(&card->ackq);
|
||||
INIT_WORK(&card->snd_tq, act2000_transmit);
|
||||
INIT_WORK(&card->rcv_tq, actcapi_dispatch);
|
||||
INIT_WORK(&card->poll_tq, act2000_receive);
|
||||
init_timer(&card->ptimer);
|
||||
card->interface.owner = THIS_MODULE;
|
||||
card->interface.channels = ACT2000_BCH;
|
||||
card->interface.maxbufsize = 4000;
|
||||
card->interface.command = if_command;
|
||||
card->interface.writebuf_skb = if_sendbuf;
|
||||
card->interface.writecmd = if_writecmd;
|
||||
card->interface.readstat = if_readstatus;
|
||||
card->interface.features =
|
||||
ISDN_FEATURE_L2_X75I |
|
||||
ISDN_FEATURE_L2_HDLC |
|
||||
ISDN_FEATURE_L3_TRANS |
|
||||
ISDN_FEATURE_P_UNKNOWN;
|
||||
card->interface.hl_hdrlen = 20;
|
||||
card->ptype = ISDN_PTYPE_EURO;
|
||||
strlcpy(card->interface.id, id, sizeof(card->interface.id));
|
||||
for (i = 0; i < ACT2000_BCH; i++) {
|
||||
card->bch[i].plci = 0x8000;
|
||||
card->bch[i].ncci = 0x8000;
|
||||
card->bch[i].l2prot = ISDN_PROTO_L2_X75I;
|
||||
card->bch[i].l3prot = ISDN_PROTO_L3_TRANS;
|
||||
}
|
||||
card->myid = -1;
|
||||
card->bus = bus;
|
||||
card->port = port;
|
||||
card->irq = irq;
|
||||
card->next = cards;
|
||||
cards = card;
|
||||
}
|
||||
|
||||
/*
|
||||
* register card at linklevel
|
||||
*/
|
||||
static int
|
||||
act2000_registercard(act2000_card *card)
|
||||
{
|
||||
switch (card->bus) {
|
||||
case ACT2000_BUS_ISA:
|
||||
break;
|
||||
case ACT2000_BUS_MCA:
|
||||
case ACT2000_BUS_PCMCIA:
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"act2000: Illegal BUS type %d\n",
|
||||
card->bus);
|
||||
return -1;
|
||||
}
|
||||
if (!register_isdn(&card->interface)) {
|
||||
printk(KERN_WARNING
|
||||
"act2000: Unable to register %s\n",
|
||||
card->interface.id);
|
||||
return -1;
|
||||
}
|
||||
card->myid = card->interface.channels;
|
||||
sprintf(card->regname, "act2000-isdn (%s)", card->interface.id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
unregister_card(act2000_card *card)
|
||||
{
|
||||
isdn_ctrl cmd;
|
||||
|
||||
cmd.command = ISDN_STAT_UNLOAD;
|
||||
cmd.driver = card->myid;
|
||||
card->interface.statcallb(&cmd);
|
||||
switch (card->bus) {
|
||||
case ACT2000_BUS_ISA:
|
||||
act2000_isa_release(card);
|
||||
break;
|
||||
case ACT2000_BUS_MCA:
|
||||
case ACT2000_BUS_PCMCIA:
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"act2000: Invalid BUS type %d\n",
|
||||
card->bus);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
act2000_addcard(int bus, int port, int irq, char *id)
|
||||
{
|
||||
act2000_card *p;
|
||||
act2000_card *q = NULL;
|
||||
int initialized;
|
||||
int added = 0;
|
||||
int failed = 0;
|
||||
int i;
|
||||
|
||||
if (!bus)
|
||||
bus = ACT2000_BUS_ISA;
|
||||
if (port != -1) {
|
||||
/* Port defined, do fixed setup */
|
||||
act2000_alloccard(bus, port, irq, id);
|
||||
} else {
|
||||
/* No port defined, perform autoprobing.
|
||||
* This may result in more than one card detected.
|
||||
*/
|
||||
switch (bus) {
|
||||
case ACT2000_BUS_ISA:
|
||||
for (i = 0; i < ARRAY_SIZE(act2000_isa_ports); i++)
|
||||
if (act2000_isa_detect(act2000_isa_ports[i])) {
|
||||
printk(KERN_INFO "act2000: Detected "
|
||||
"ISA card at port 0x%x\n",
|
||||
act2000_isa_ports[i]);
|
||||
act2000_alloccard(bus,
|
||||
act2000_isa_ports[i], irq, id);
|
||||
}
|
||||
break;
|
||||
case ACT2000_BUS_MCA:
|
||||
case ACT2000_BUS_PCMCIA:
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"act2000: addcard: Invalid BUS type %d\n", bus);
|
||||
}
|
||||
}
|
||||
if (!cards)
|
||||
return 1;
|
||||
p = cards;
|
||||
while (p) {
|
||||
initialized = 0;
|
||||
if (!p->interface.statcallb) {
|
||||
/* Not yet registered.
|
||||
* Try to register and activate it.
|
||||
*/
|
||||
added++;
|
||||
switch (p->bus) {
|
||||
case ACT2000_BUS_ISA:
|
||||
if (act2000_isa_detect(p->port)) {
|
||||
if (act2000_registercard(p))
|
||||
break;
|
||||
if (act2000_isa_config_port(p, p->port)) {
|
||||
printk(KERN_WARNING
|
||||
"act2000: Could not request port 0x%04x\n",
|
||||
p->port);
|
||||
unregister_card(p);
|
||||
p->interface.statcallb = NULL;
|
||||
break;
|
||||
}
|
||||
if (act2000_isa_config_irq(p, p->irq)) {
|
||||
printk(KERN_INFO
|
||||
"act2000: No IRQ available, fallback to polling\n");
|
||||
/* Fall back to polled operation */
|
||||
p->irq = 0;
|
||||
}
|
||||
printk(KERN_INFO
|
||||
"act2000: ISA"
|
||||
"-type card at port "
|
||||
"0x%04x ",
|
||||
p->port);
|
||||
if (p->irq)
|
||||
printk("irq %d\n", p->irq);
|
||||
else
|
||||
printk("polled\n");
|
||||
initialized = 1;
|
||||
}
|
||||
break;
|
||||
case ACT2000_BUS_MCA:
|
||||
case ACT2000_BUS_PCMCIA:
|
||||
default:
|
||||
printk(KERN_WARNING
|
||||
"act2000: addcard: Invalid BUS type %d\n",
|
||||
p->bus);
|
||||
}
|
||||
} else
|
||||
/* Card already initialized */
|
||||
initialized = 1;
|
||||
if (initialized) {
|
||||
/* Init OK, next card ... */
|
||||
q = p;
|
||||
p = p->next;
|
||||
} else {
|
||||
/* Init failed, remove card from list, free memory */
|
||||
printk(KERN_WARNING
|
||||
"act2000: Initialization of %s failed\n",
|
||||
p->interface.id);
|
||||
if (q) {
|
||||
q->next = p->next;
|
||||
kfree(p);
|
||||
p = q->next;
|
||||
} else {
|
||||
cards = p->next;
|
||||
kfree(p);
|
||||
p = cards;
|
||||
}
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
return (added - failed);
|
||||
}
|
||||
|
||||
#define DRIVERNAME "IBM Active 2000 ISDN driver"
|
||||
|
||||
static int __init act2000_init(void)
|
||||
{
|
||||
printk(KERN_INFO "%s\n", DRIVERNAME);
|
||||
if (!cards)
|
||||
act2000_addcard(act_bus, act_port, act_irq, act_id);
|
||||
if (!cards)
|
||||
printk(KERN_INFO "act2000: No cards defined yet\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit act2000_exit(void)
|
||||
{
|
||||
act2000_card *card = cards;
|
||||
act2000_card *last;
|
||||
while (card) {
|
||||
unregister_card(card);
|
||||
del_timer_sync(&card->ptimer);
|
||||
card = card->next;
|
||||
}
|
||||
card = cards;
|
||||
while (card) {
|
||||
last = card;
|
||||
card = card->next;
|
||||
act2000_clear_msn(last);
|
||||
kfree(last);
|
||||
}
|
||||
printk(KERN_INFO "%s unloaded\n", DRIVERNAME);
|
||||
}
|
||||
|
||||
module_init(act2000_init);
|
||||
module_exit(act2000_exit);
|
||||
Loading…
Add table
Add a link
Reference in a new issue