diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/isdn/act2000 |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/isdn/act2000')
-rw-r--r-- | drivers/isdn/act2000/Kconfig | 13 | ||||
-rw-r--r-- | drivers/isdn/act2000/Makefile | 9 | ||||
-rw-r--r-- | drivers/isdn/act2000/act2000.h | 202 | ||||
-rw-r--r-- | drivers/isdn/act2000/act2000_isa.c | 449 | ||||
-rw-r--r-- | drivers/isdn/act2000/act2000_isa.h | 136 | ||||
-rw-r--r-- | drivers/isdn/act2000/capi.c | 1177 | ||||
-rw-r--r-- | drivers/isdn/act2000/capi.h | 366 | ||||
-rw-r--r-- | drivers/isdn/act2000/module.c | 808 |
8 files changed, 3160 insertions, 0 deletions
diff --git a/drivers/isdn/act2000/Kconfig b/drivers/isdn/act2000/Kconfig new file mode 100644 index 000000000000..78e6ad8d57c5 --- /dev/null +++ b/drivers/isdn/act2000/Kconfig | |||
@@ -0,0 +1,13 @@ | |||
1 | # | ||
2 | # Config.in for IBM Active 2000 ISDN driver | ||
3 | # | ||
4 | config ISDN_DRV_ACT2000 | ||
5 | tristate "IBM Active 2000 support" | ||
6 | depends on ISDN_I4L && ISA | ||
7 | help | ||
8 | Say Y here if you have an IBM Active 2000 ISDN card. In order to use | ||
9 | this card, additional firmware is necessary, which has to be loaded | ||
10 | into the card using a utility which is part of the latest | ||
11 | isdn4k-utils package. Please read the file | ||
12 | <file:Documentation/isdn/README.act2000> for more information. | ||
13 | |||
diff --git a/drivers/isdn/act2000/Makefile b/drivers/isdn/act2000/Makefile new file mode 100644 index 000000000000..05e582fb5c00 --- /dev/null +++ b/drivers/isdn/act2000/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | # Makefile for the act2000 ISDN device driver | ||
2 | |||
3 | # Each configuration option enables a list of files. | ||
4 | |||
5 | obj-$(CONFIG_ISDN_DRV_ACT2000) += act2000.o | ||
6 | |||
7 | # Multipart objects. | ||
8 | |||
9 | act2000-y := module.o capi.o act2000_isa.o | ||
diff --git a/drivers/isdn/act2000/act2000.h b/drivers/isdn/act2000/act2000.h new file mode 100644 index 000000000000..b091d1a54125 --- /dev/null +++ b/drivers/isdn/act2000/act2000.h | |||
@@ -0,0 +1,202 @@ | |||
1 | /* $Id: act2000.h,v 1.8.6.3 2001/09/23 22:24:32 kai Exp $ | ||
2 | * | ||
3 | * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. | ||
4 | * | ||
5 | * Author Fritz Elfert | ||
6 | * Copyright by Fritz Elfert <fritz@isdn4linux.de> | ||
7 | * | ||
8 | * This software may be used and distributed according to the terms | ||
9 | * of the GNU General Public License, incorporated herein by reference. | ||
10 | * | ||
11 | * Thanks to Friedemann Baitinger and IBM Germany | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #ifndef act2000_h | ||
16 | #define act2000_h | ||
17 | |||
18 | #include <linux/compiler.h> | ||
19 | |||
20 | #define ACT2000_IOCTL_SETPORT 1 | ||
21 | #define ACT2000_IOCTL_GETPORT 2 | ||
22 | #define ACT2000_IOCTL_SETIRQ 3 | ||
23 | #define ACT2000_IOCTL_GETIRQ 4 | ||
24 | #define ACT2000_IOCTL_SETBUS 5 | ||
25 | #define ACT2000_IOCTL_GETBUS 6 | ||
26 | #define ACT2000_IOCTL_SETPROTO 7 | ||
27 | #define ACT2000_IOCTL_GETPROTO 8 | ||
28 | #define ACT2000_IOCTL_SETMSN 9 | ||
29 | #define ACT2000_IOCTL_GETMSN 10 | ||
30 | #define ACT2000_IOCTL_LOADBOOT 11 | ||
31 | #define ACT2000_IOCTL_ADDCARD 12 | ||
32 | |||
33 | #define ACT2000_IOCTL_TEST 98 | ||
34 | #define ACT2000_IOCTL_DEBUGVAR 99 | ||
35 | |||
36 | #define ACT2000_BUS_ISA 1 | ||
37 | #define ACT2000_BUS_MCA 2 | ||
38 | #define ACT2000_BUS_PCMCIA 3 | ||
39 | |||
40 | /* Struct for adding new cards */ | ||
41 | typedef struct act2000_cdef { | ||
42 | int bus; | ||
43 | int port; | ||
44 | int irq; | ||
45 | char id[10]; | ||
46 | } act2000_cdef; | ||
47 | |||
48 | /* Struct for downloading firmware */ | ||
49 | typedef struct act2000_ddef { | ||
50 | int length; /* Length of code */ | ||
51 | char __user *buffer; /* Ptr. to code */ | ||
52 | } act2000_ddef; | ||
53 | |||
54 | typedef struct act2000_fwid { | ||
55 | char isdn[4]; | ||
56 | char revlen[2]; | ||
57 | char revision[504]; | ||
58 | } act2000_fwid; | ||
59 | |||
60 | #if defined(__KERNEL__) || defined(__DEBUGVAR__) | ||
61 | |||
62 | #ifdef __KERNEL__ | ||
63 | /* Kernel includes */ | ||
64 | |||
65 | #include <linux/sched.h> | ||
66 | #include <linux/string.h> | ||
67 | #include <linux/workqueue.h> | ||
68 | #include <linux/interrupt.h> | ||
69 | #include <linux/skbuff.h> | ||
70 | #include <linux/errno.h> | ||
71 | #include <linux/fs.h> | ||
72 | #include <linux/major.h> | ||
73 | #include <asm/io.h> | ||
74 | #include <linux/kernel.h> | ||
75 | #include <linux/signal.h> | ||
76 | #include <linux/slab.h> | ||
77 | #include <linux/mm.h> | ||
78 | #include <linux/mman.h> | ||
79 | #include <linux/ioport.h> | ||
80 | #include <linux/timer.h> | ||
81 | #include <linux/wait.h> | ||
82 | #include <linux/delay.h> | ||
83 | #include <linux/ctype.h> | ||
84 | #include <linux/isdnif.h> | ||
85 | |||
86 | #endif /* __KERNEL__ */ | ||
87 | |||
88 | #define ACT2000_PORTLEN 8 | ||
89 | |||
90 | #define ACT2000_FLAGS_RUNNING 1 /* Cards driver activated */ | ||
91 | #define ACT2000_FLAGS_PVALID 2 /* Cards port is valid */ | ||
92 | #define ACT2000_FLAGS_IVALID 4 /* Cards irq is valid */ | ||
93 | #define ACT2000_FLAGS_LOADED 8 /* Firmware loaded */ | ||
94 | |||
95 | #define ACT2000_BCH 2 /* # of channels per card */ | ||
96 | |||
97 | /* D-Channel states */ | ||
98 | #define ACT2000_STATE_NULL 0 | ||
99 | #define ACT2000_STATE_ICALL 1 | ||
100 | #define ACT2000_STATE_OCALL 2 | ||
101 | #define ACT2000_STATE_IWAIT 3 | ||
102 | #define ACT2000_STATE_OWAIT 4 | ||
103 | #define ACT2000_STATE_IBWAIT 5 | ||
104 | #define ACT2000_STATE_OBWAIT 6 | ||
105 | #define ACT2000_STATE_BWAIT 7 | ||
106 | #define ACT2000_STATE_BHWAIT 8 | ||
107 | #define ACT2000_STATE_BHWAIT2 9 | ||
108 | #define ACT2000_STATE_DHWAIT 10 | ||
109 | #define ACT2000_STATE_DHWAIT2 11 | ||
110 | #define ACT2000_STATE_BSETUP 12 | ||
111 | #define ACT2000_STATE_ACTIVE 13 | ||
112 | |||
113 | #define ACT2000_MAX_QUEUED 8000 /* 2 * maxbuff */ | ||
114 | |||
115 | #define ACT2000_LOCK_TX 0 | ||
116 | #define ACT2000_LOCK_RX 1 | ||
117 | |||
118 | typedef struct act2000_chan { | ||
119 | unsigned short callref; /* Call Reference */ | ||
120 | unsigned short fsm_state; /* Current D-Channel state */ | ||
121 | unsigned short eazmask; /* EAZ-Mask for this Channel */ | ||
122 | short queued; /* User-Data Bytes in TX queue */ | ||
123 | unsigned short plci; | ||
124 | unsigned short ncci; | ||
125 | unsigned char l2prot; /* Layer 2 protocol */ | ||
126 | unsigned char l3prot; /* Layer 3 protocol */ | ||
127 | } act2000_chan; | ||
128 | |||
129 | typedef struct msn_entry { | ||
130 | char eaz; | ||
131 | char msn[16]; | ||
132 | struct msn_entry * next; | ||
133 | } msn_entry; | ||
134 | |||
135 | typedef struct irq_data_isa { | ||
136 | __u8 *rcvptr; | ||
137 | __u16 rcvidx; | ||
138 | __u16 rcvlen; | ||
139 | struct sk_buff *rcvskb; | ||
140 | __u8 rcvignore; | ||
141 | __u8 rcvhdr[8]; | ||
142 | } irq_data_isa; | ||
143 | |||
144 | typedef union irq_data { | ||
145 | irq_data_isa isa; | ||
146 | } irq_data; | ||
147 | |||
148 | /* | ||
149 | * Per card driver data | ||
150 | */ | ||
151 | typedef struct act2000_card { | ||
152 | unsigned short port; /* Base-port-address */ | ||
153 | unsigned short irq; /* Interrupt */ | ||
154 | u_char ptype; /* Protocol type (1TR6 or Euro) */ | ||
155 | u_char bus; /* Cardtype (ISA, MCA, PCMCIA) */ | ||
156 | struct act2000_card *next; /* Pointer to next device struct */ | ||
157 | spinlock_t lock; /* protect critical operations */ | ||
158 | int myid; /* Driver-Nr. assigned by linklevel */ | ||
159 | unsigned long flags; /* Statusflags */ | ||
160 | unsigned long ilock; /* Semaphores for IRQ-Routines */ | ||
161 | struct sk_buff_head rcvq; /* Receive-Message queue */ | ||
162 | struct sk_buff_head sndq; /* Send-Message queue */ | ||
163 | struct sk_buff_head ackq; /* Data-Ack-Message queue */ | ||
164 | u_char *ack_msg; /* Ptr to User Data in User skb */ | ||
165 | __u16 need_b3ack; /* Flag: Need ACK for current skb */ | ||
166 | struct sk_buff *sbuf; /* skb which is currently sent */ | ||
167 | struct timer_list ptimer; /* Poll timer */ | ||
168 | struct work_struct snd_tq; /* Task struct for xmit bh */ | ||
169 | struct work_struct rcv_tq; /* Task struct for rcv bh */ | ||
170 | struct work_struct poll_tq; /* Task struct for polled rcv bh */ | ||
171 | msn_entry *msn_list; | ||
172 | unsigned short msgnum; /* Message number for sending */ | ||
173 | spinlock_t mnlock; /* lock for msgnum */ | ||
174 | act2000_chan bch[ACT2000_BCH]; /* B-Channel status/control */ | ||
175 | char status_buf[256]; /* Buffer for status messages */ | ||
176 | char *status_buf_read; | ||
177 | char *status_buf_write; | ||
178 | char *status_buf_end; | ||
179 | irq_data idat; /* Data used for IRQ handler */ | ||
180 | isdn_if interface; /* Interface to upper layer */ | ||
181 | char regname[35]; /* Name used for request_region */ | ||
182 | } act2000_card; | ||
183 | |||
184 | extern __inline__ void act2000_schedule_tx(act2000_card *card) | ||
185 | { | ||
186 | schedule_work(&card->snd_tq); | ||
187 | } | ||
188 | |||
189 | extern __inline__ void act2000_schedule_rx(act2000_card *card) | ||
190 | { | ||
191 | schedule_work(&card->rcv_tq); | ||
192 | } | ||
193 | |||
194 | extern __inline__ void act2000_schedule_poll(act2000_card *card) | ||
195 | { | ||
196 | schedule_work(&card->poll_tq); | ||
197 | } | ||
198 | |||
199 | extern char *act2000_find_eaz(act2000_card *, char); | ||
200 | |||
201 | #endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */ | ||
202 | #endif /* act2000_h */ | ||
diff --git a/drivers/isdn/act2000/act2000_isa.c b/drivers/isdn/act2000/act2000_isa.c new file mode 100644 index 000000000000..bc98d77c5ecd --- /dev/null +++ b/drivers/isdn/act2000/act2000_isa.c | |||
@@ -0,0 +1,449 @@ | |||
1 | /* $Id: act2000_isa.c,v 1.11.6.3 2001/09/23 22:24:32 kai Exp $ | ||
2 | * | ||
3 | * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version). | ||
4 | * | ||
5 | * Author Fritz Elfert | ||
6 | * Copyright by Fritz Elfert <fritz@isdn4linux.de> | ||
7 | * | ||
8 | * This software may be used and distributed according to the terms | ||
9 | * of the GNU General Public License, incorporated herein by reference. | ||
10 | * | ||
11 | * Thanks to Friedemann Baitinger and IBM Germany | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include "act2000.h" | ||
16 | #include "act2000_isa.h" | ||
17 | #include "capi.h" | ||
18 | |||
19 | static act2000_card *irq2card_map[16]; | ||
20 | |||
21 | /* | ||
22 | * Reset Controller, then try to read the Card's signature. | ||
23 | + Return: | ||
24 | * 1 = Signature found. | ||
25 | * 0 = Signature not found. | ||
26 | */ | ||
27 | static int | ||
28 | act2000_isa_reset(unsigned short portbase) | ||
29 | { | ||
30 | unsigned char reg; | ||
31 | int i; | ||
32 | int found; | ||
33 | int serial = 0; | ||
34 | |||
35 | found = 0; | ||
36 | if ((reg = inb(portbase + ISA_COR)) != 0xff) { | ||
37 | outb(reg | ISA_COR_RESET, portbase + ISA_COR); | ||
38 | mdelay(10); | ||
39 | outb(reg, portbase + ISA_COR); | ||
40 | mdelay(10); | ||
41 | |||
42 | for (i = 0; i < 16; i++) { | ||
43 | if (inb(portbase + ISA_ISR) & ISA_ISR_SERIAL) | ||
44 | serial |= 0x10000; | ||
45 | serial >>= 1; | ||
46 | } | ||
47 | if (serial == ISA_SER_ID) | ||
48 | found++; | ||
49 | } | ||
50 | return found; | ||
51 | } | ||
52 | |||
53 | int | ||
54 | act2000_isa_detect(unsigned short portbase) | ||
55 | { | ||
56 | int ret = 0; | ||
57 | |||
58 | if (request_region(portbase, ACT2000_PORTLEN, "act2000isa")) { | ||
59 | ret = act2000_isa_reset(portbase); | ||
60 | release_region(portbase, ISA_REGION); | ||
61 | } | ||
62 | return ret; | ||
63 | } | ||
64 | |||
65 | static irqreturn_t | ||
66 | act2000_isa_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
67 | { | ||
68 | act2000_card *card = irq2card_map[irq]; | ||
69 | u_char istatus; | ||
70 | |||
71 | if (!card) { | ||
72 | printk(KERN_WARNING | ||
73 | "act2000: Spurious interrupt!\n"); | ||
74 | return IRQ_NONE; | ||
75 | } | ||
76 | istatus = (inb(ISA_PORT_ISR) & 0x07); | ||
77 | if (istatus & ISA_ISR_OUT) { | ||
78 | /* RX fifo has data */ | ||
79 | istatus &= ISA_ISR_OUT_MASK; | ||
80 | outb(0, ISA_PORT_SIS); | ||
81 | act2000_isa_receive(card); | ||
82 | outb(ISA_SIS_INT, ISA_PORT_SIS); | ||
83 | } | ||
84 | if (istatus & ISA_ISR_ERR) { | ||
85 | /* Error Interrupt */ | ||
86 | istatus &= ISA_ISR_ERR_MASK; | ||
87 | printk(KERN_WARNING "act2000: errIRQ\n"); | ||
88 | } | ||
89 | if (istatus) | ||
90 | printk(KERN_DEBUG "act2000: ?IRQ %d %02x\n", irq, istatus); | ||
91 | return IRQ_HANDLED; | ||
92 | } | ||
93 | |||
94 | static void | ||
95 | act2000_isa_select_irq(act2000_card * card) | ||
96 | { | ||
97 | unsigned char reg; | ||
98 | |||
99 | reg = (inb(ISA_PORT_COR) & ~ISA_COR_IRQOFF) | ISA_COR_PERR; | ||
100 | switch (card->irq) { | ||
101 | case 3: | ||
102 | reg = ISA_COR_IRQ03; | ||
103 | break; | ||
104 | case 5: | ||
105 | reg = ISA_COR_IRQ05; | ||
106 | break; | ||
107 | case 7: | ||
108 | reg = ISA_COR_IRQ07; | ||
109 | break; | ||
110 | case 10: | ||
111 | reg = ISA_COR_IRQ10; | ||
112 | break; | ||
113 | case 11: | ||
114 | reg = ISA_COR_IRQ11; | ||
115 | break; | ||
116 | case 12: | ||
117 | reg = ISA_COR_IRQ12; | ||
118 | break; | ||
119 | case 15: | ||
120 | reg = ISA_COR_IRQ15; | ||
121 | break; | ||
122 | } | ||
123 | outb(reg, ISA_PORT_COR); | ||
124 | } | ||
125 | |||
126 | static void | ||
127 | act2000_isa_enable_irq(act2000_card * card) | ||
128 | { | ||
129 | act2000_isa_select_irq(card); | ||
130 | /* Enable READ irq */ | ||
131 | outb(ISA_SIS_INT, ISA_PORT_SIS); | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * Install interrupt handler, enable irq on card. | ||
136 | * If irq is -1, choose next free irq, else irq is given explicitely. | ||
137 | */ | ||
138 | int | ||
139 | act2000_isa_config_irq(act2000_card * card, short irq) | ||
140 | { | ||
141 | if (card->flags & ACT2000_FLAGS_IVALID) { | ||
142 | free_irq(card->irq, NULL); | ||
143 | irq2card_map[card->irq] = NULL; | ||
144 | } | ||
145 | card->flags &= ~ACT2000_FLAGS_IVALID; | ||
146 | outb(ISA_COR_IRQOFF, ISA_PORT_COR); | ||
147 | if (!irq) | ||
148 | return 0; | ||
149 | |||
150 | if (!request_irq(irq, &act2000_isa_interrupt, 0, card->regname, NULL)) { | ||
151 | card->irq = irq; | ||
152 | irq2card_map[card->irq] = card; | ||
153 | card->flags |= ACT2000_FLAGS_IVALID; | ||
154 | printk(KERN_WARNING | ||
155 | "act2000: Could not request irq %d\n",irq); | ||
156 | return -EBUSY; | ||
157 | } else { | ||
158 | act2000_isa_select_irq(card); | ||
159 | /* Disable READ and WRITE irq */ | ||
160 | outb(0, ISA_PORT_SIS); | ||
161 | outb(0, ISA_PORT_SOS); | ||
162 | } | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | int | ||
167 | act2000_isa_config_port(act2000_card * card, unsigned short portbase) | ||
168 | { | ||
169 | if (card->flags & ACT2000_FLAGS_PVALID) { | ||
170 | release_region(card->port, ISA_REGION); | ||
171 | card->flags &= ~ACT2000_FLAGS_PVALID; | ||
172 | } | ||
173 | if (request_region(portbase, ACT2000_PORTLEN, card->regname) == NULL) | ||
174 | return -EBUSY; | ||
175 | else { | ||
176 | card->port = portbase; | ||
177 | card->flags |= ACT2000_FLAGS_PVALID; | ||
178 | return 0; | ||
179 | } | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * Release ressources, used by an adaptor. | ||
184 | */ | ||
185 | void | ||
186 | act2000_isa_release(act2000_card * card) | ||
187 | { | ||
188 | unsigned long flags; | ||
189 | |||
190 | spin_lock_irqsave(&card->lock, flags); | ||
191 | if (card->flags & ACT2000_FLAGS_IVALID) { | ||
192 | free_irq(card->irq, NULL); | ||
193 | irq2card_map[card->irq] = NULL; | ||
194 | } | ||
195 | card->flags &= ~ACT2000_FLAGS_IVALID; | ||
196 | if (card->flags & ACT2000_FLAGS_PVALID) | ||
197 | release_region(card->port, ISA_REGION); | ||
198 | card->flags &= ~ACT2000_FLAGS_PVALID; | ||
199 | spin_unlock_irqrestore(&card->lock, flags); | ||
200 | } | ||
201 | |||
202 | static int | ||
203 | act2000_isa_writeb(act2000_card * card, u_char data) | ||
204 | { | ||
205 | u_char timeout = 40; | ||
206 | |||
207 | while (timeout) { | ||
208 | if (inb(ISA_PORT_SOS) & ISA_SOS_READY) { | ||
209 | outb(data, ISA_PORT_SDO); | ||
210 | return 0; | ||
211 | } else { | ||
212 | timeout--; | ||
213 | udelay(10); | ||
214 | } | ||
215 | } | ||
216 | return 1; | ||
217 | } | ||
218 | |||
219 | static int | ||
220 | act2000_isa_readb(act2000_card * card, u_char * data) | ||
221 | { | ||
222 | u_char timeout = 40; | ||
223 | |||
224 | while (timeout) { | ||
225 | if (inb(ISA_PORT_SIS) & ISA_SIS_READY) { | ||
226 | *data = inb(ISA_PORT_SDI); | ||
227 | return 0; | ||
228 | } else { | ||
229 | timeout--; | ||
230 | udelay(10); | ||
231 | } | ||
232 | } | ||
233 | return 1; | ||
234 | } | ||
235 | |||
236 | void | ||
237 | act2000_isa_receive(act2000_card *card) | ||
238 | { | ||
239 | u_char c; | ||
240 | |||
241 | if (test_and_set_bit(ACT2000_LOCK_RX, (void *) &card->ilock) != 0) | ||
242 | return; | ||
243 | while (!act2000_isa_readb(card, &c)) { | ||
244 | if (card->idat.isa.rcvidx < 8) { | ||
245 | card->idat.isa.rcvhdr[card->idat.isa.rcvidx++] = c; | ||
246 | if (card->idat.isa.rcvidx == 8) { | ||
247 | int valid = actcapi_chkhdr(card, (actcapi_msghdr *)&card->idat.isa.rcvhdr); | ||
248 | |||
249 | if (valid) { | ||
250 | card->idat.isa.rcvlen = ((actcapi_msghdr *)&card->idat.isa.rcvhdr)->len; | ||
251 | card->idat.isa.rcvskb = dev_alloc_skb(card->idat.isa.rcvlen); | ||
252 | if (card->idat.isa.rcvskb == NULL) { | ||
253 | card->idat.isa.rcvignore = 1; | ||
254 | printk(KERN_WARNING | ||
255 | "act2000_isa_receive: no memory\n"); | ||
256 | test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock); | ||
257 | return; | ||
258 | } | ||
259 | memcpy(skb_put(card->idat.isa.rcvskb, 8), card->idat.isa.rcvhdr, 8); | ||
260 | card->idat.isa.rcvptr = skb_put(card->idat.isa.rcvskb, card->idat.isa.rcvlen - 8); | ||
261 | } else { | ||
262 | card->idat.isa.rcvidx = 0; | ||
263 | printk(KERN_WARNING | ||
264 | "act2000_isa_receive: Invalid CAPI msg\n"); | ||
265 | { | ||
266 | int i; __u8 *p; __u8 *c; __u8 tmp[30]; | ||
267 | for (i = 0, p = (__u8 *)&card->idat.isa.rcvhdr, c = tmp; i < 8; i++) | ||
268 | c += sprintf(c, "%02x ", *(p++)); | ||
269 | printk(KERN_WARNING "act2000_isa_receive: %s\n", tmp); | ||
270 | } | ||
271 | } | ||
272 | } | ||
273 | } else { | ||
274 | if (!card->idat.isa.rcvignore) | ||
275 | *card->idat.isa.rcvptr++ = c; | ||
276 | if (++card->idat.isa.rcvidx >= card->idat.isa.rcvlen) { | ||
277 | if (!card->idat.isa.rcvignore) { | ||
278 | skb_queue_tail(&card->rcvq, card->idat.isa.rcvskb); | ||
279 | act2000_schedule_rx(card); | ||
280 | } | ||
281 | card->idat.isa.rcvidx = 0; | ||
282 | card->idat.isa.rcvlen = 8; | ||
283 | card->idat.isa.rcvignore = 0; | ||
284 | card->idat.isa.rcvskb = NULL; | ||
285 | card->idat.isa.rcvptr = card->idat.isa.rcvhdr; | ||
286 | } | ||
287 | } | ||
288 | } | ||
289 | if (!(card->flags & ACT2000_FLAGS_IVALID)) { | ||
290 | /* In polling mode, schedule myself */ | ||
291 | if ((card->idat.isa.rcvidx) && | ||
292 | (card->idat.isa.rcvignore || | ||
293 | (card->idat.isa.rcvidx < card->idat.isa.rcvlen))) | ||
294 | act2000_schedule_poll(card); | ||
295 | } | ||
296 | test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock); | ||
297 | } | ||
298 | |||
299 | void | ||
300 | act2000_isa_send(act2000_card * card) | ||
301 | { | ||
302 | unsigned long flags; | ||
303 | struct sk_buff *skb; | ||
304 | actcapi_msg *msg; | ||
305 | int l; | ||
306 | |||
307 | if (test_and_set_bit(ACT2000_LOCK_TX, (void *) &card->ilock) != 0) | ||
308 | return; | ||
309 | while (1) { | ||
310 | spin_lock_irqsave(&card->lock, flags); | ||
311 | if (!(card->sbuf)) { | ||
312 | if ((card->sbuf = skb_dequeue(&card->sndq))) { | ||
313 | card->ack_msg = card->sbuf->data; | ||
314 | msg = (actcapi_msg *)card->sbuf->data; | ||
315 | if ((msg->hdr.cmd.cmd == 0x86) && | ||
316 | (msg->hdr.cmd.subcmd == 0) ) { | ||
317 | /* Save flags in message */ | ||
318 | card->need_b3ack = msg->msg.data_b3_req.flags; | ||
319 | msg->msg.data_b3_req.flags = 0; | ||
320 | } | ||
321 | } | ||
322 | } | ||
323 | spin_unlock_irqrestore(&card->lock, flags); | ||
324 | if (!(card->sbuf)) { | ||
325 | /* No more data to send */ | ||
326 | test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock); | ||
327 | return; | ||
328 | } | ||
329 | skb = card->sbuf; | ||
330 | l = 0; | ||
331 | while (skb->len) { | ||
332 | if (act2000_isa_writeb(card, *(skb->data))) { | ||
333 | /* Fifo is full, but more data to send */ | ||
334 | test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock); | ||
335 | /* Schedule myself */ | ||
336 | act2000_schedule_tx(card); | ||
337 | return; | ||
338 | } | ||
339 | skb_pull(skb, 1); | ||
340 | l++; | ||
341 | } | ||
342 | msg = (actcapi_msg *)card->ack_msg; | ||
343 | if ((msg->hdr.cmd.cmd == 0x86) && | ||
344 | (msg->hdr.cmd.subcmd == 0) ) { | ||
345 | /* | ||
346 | * If it's user data, reset data-ptr | ||
347 | * and put skb into ackq. | ||
348 | */ | ||
349 | skb->data = card->ack_msg; | ||
350 | /* Restore flags in message */ | ||
351 | msg->msg.data_b3_req.flags = card->need_b3ack; | ||
352 | skb_queue_tail(&card->ackq, skb); | ||
353 | } else | ||
354 | dev_kfree_skb(skb); | ||
355 | card->sbuf = NULL; | ||
356 | } | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * Get firmware ID, check for 'ISDN' signature. | ||
361 | */ | ||
362 | static int | ||
363 | act2000_isa_getid(act2000_card * card) | ||
364 | { | ||
365 | |||
366 | act2000_fwid fid; | ||
367 | u_char *p = (u_char *) & fid; | ||
368 | int count = 0; | ||
369 | |||
370 | while (1) { | ||
371 | if (count > 510) | ||
372 | return -EPROTO; | ||
373 | if (act2000_isa_readb(card, p++)) | ||
374 | break; | ||
375 | count++; | ||
376 | } | ||
377 | if (count <= 20) { | ||
378 | printk(KERN_WARNING "act2000: No Firmware-ID!\n"); | ||
379 | return -ETIME; | ||
380 | } | ||
381 | *p = '\0'; | ||
382 | fid.revlen[0] = '\0'; | ||
383 | if (strcmp(fid.isdn, "ISDN")) { | ||
384 | printk(KERN_WARNING "act2000: Wrong Firmware-ID!\n"); | ||
385 | return -EPROTO; | ||
386 | } | ||
387 | if ((p = strchr(fid.revision, '\n'))) | ||
388 | *p = '\0'; | ||
389 | printk(KERN_INFO "act2000: Firmware-ID: %s\n", fid.revision); | ||
390 | if (card->flags & ACT2000_FLAGS_IVALID) { | ||
391 | printk(KERN_DEBUG "Enabling Interrupts ...\n"); | ||
392 | act2000_isa_enable_irq(card); | ||
393 | } | ||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | /* | ||
398 | * Download microcode into card, check Firmware signature. | ||
399 | */ | ||
400 | int | ||
401 | act2000_isa_download(act2000_card * card, act2000_ddef __user * cb) | ||
402 | { | ||
403 | unsigned int length; | ||
404 | int l; | ||
405 | int c; | ||
406 | long timeout; | ||
407 | u_char *b; | ||
408 | u_char __user *p; | ||
409 | u_char *buf; | ||
410 | act2000_ddef cblock; | ||
411 | |||
412 | if (!act2000_isa_reset(card->port)) | ||
413 | return -ENXIO; | ||
414 | msleep_interruptible(500); | ||
415 | if (copy_from_user(&cblock, cb, sizeof(cblock))) | ||
416 | return -EFAULT; | ||
417 | length = cblock.length; | ||
418 | p = cblock.buffer; | ||
419 | if (!access_ok(VERIFY_READ, p, length)) | ||
420 | return -EFAULT; | ||
421 | buf = (u_char *) kmalloc(1024, GFP_KERNEL); | ||
422 | if (!buf) | ||
423 | return -ENOMEM; | ||
424 | timeout = 0; | ||
425 | while (length) { | ||
426 | l = (length > 1024) ? 1024 : length; | ||
427 | c = 0; | ||
428 | b = buf; | ||
429 | if (copy_from_user(buf, p, l)) { | ||
430 | kfree(buf); | ||
431 | return -EFAULT; | ||
432 | } | ||
433 | while (c < l) { | ||
434 | if (act2000_isa_writeb(card, *b++)) { | ||
435 | printk(KERN_WARNING | ||
436 | "act2000: loader timed out" | ||
437 | " len=%d c=%d\n", length, c); | ||
438 | kfree(buf); | ||
439 | return -ETIME; | ||
440 | } | ||
441 | c++; | ||
442 | } | ||
443 | length -= l; | ||
444 | p += l; | ||
445 | } | ||
446 | kfree(buf); | ||
447 | msleep_interruptible(500); | ||
448 | return (act2000_isa_getid(card)); | ||
449 | } | ||
diff --git a/drivers/isdn/act2000/act2000_isa.h b/drivers/isdn/act2000/act2000_isa.h new file mode 100644 index 000000000000..ad86c5ed9aad --- /dev/null +++ b/drivers/isdn/act2000/act2000_isa.h | |||
@@ -0,0 +1,136 @@ | |||
1 | /* $Id: act2000_isa.h,v 1.4.6.1 2001/09/23 22:24:32 kai Exp $ | ||
2 | * | ||
3 | * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version). | ||
4 | * | ||
5 | * Author Fritz Elfert | ||
6 | * Copyright by Fritz Elfert <fritz@isdn4linux.de> | ||
7 | * | ||
8 | * This software may be used and distributed according to the terms | ||
9 | * of the GNU General Public License, incorporated herein by reference. | ||
10 | * | ||
11 | * Thanks to Friedemann Baitinger and IBM Germany | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #ifndef act2000_isa_h | ||
16 | #define act2000_isa_h | ||
17 | |||
18 | #define ISA_POLL_LOOP 40 /* Try to read-write before give up */ | ||
19 | |||
20 | typedef enum { | ||
21 | INT_NO_CHANGE = 0, /* Do not change the Mask */ | ||
22 | INT_ON = 1, /* Set to Enable */ | ||
23 | INT_OFF = 2, /* Set to Disable */ | ||
24 | } ISA_INT_T; | ||
25 | |||
26 | /**************************************************************************/ | ||
27 | /* Configuration Register COR (RW) */ | ||
28 | /**************************************************************************/ | ||
29 | /* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ | ||
30 | /* Soft Res| IRQM | IRQ Select | N/A | WAIT |Proc err */ | ||
31 | /**************************************************************************/ | ||
32 | #define ISA_COR 0 /* Offset for ISA config register */ | ||
33 | #define ISA_COR_PERR 0x01 /* Processor Error Enabled */ | ||
34 | #define ISA_COR_WS 0x02 /* Insert Wait State if 1 */ | ||
35 | #define ISA_COR_IRQOFF 0x38 /* No Interrupt */ | ||
36 | #define ISA_COR_IRQ07 0x30 /* IRQ 7 Enable */ | ||
37 | #define ISA_COR_IRQ05 0x28 /* IRQ 5 Enable */ | ||
38 | #define ISA_COR_IRQ03 0x20 /* IRQ 3 Enable */ | ||
39 | #define ISA_COR_IRQ10 0x18 /* IRQ 10 Enable */ | ||
40 | #define ISA_COR_IRQ11 0x10 /* IRQ 11 Enable */ | ||
41 | #define ISA_COR_IRQ12 0x08 /* IRQ 12 Enable */ | ||
42 | #define ISA_COR_IRQ15 0x00 /* IRQ 15 Enable */ | ||
43 | #define ISA_COR_IRQPULSE 0x40 /* 0 = Level 1 = Pulse Interrupt */ | ||
44 | #define ISA_COR_RESET 0x80 /* Soft Reset for Transputer */ | ||
45 | |||
46 | /**************************************************************************/ | ||
47 | /* Interrupt Source Register ISR (RO) */ | ||
48 | /**************************************************************************/ | ||
49 | /* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ | ||
50 | /* N/A | N/A | N/A |Err sig |Ser ID |IN Intr |Out Intr| Error */ | ||
51 | /**************************************************************************/ | ||
52 | #define ISA_ISR 1 /* Offset for Interrupt Register */ | ||
53 | #define ISA_ISR_ERR 0x01 /* Error Interrupt */ | ||
54 | #define ISA_ISR_OUT 0x02 /* Output Interrupt */ | ||
55 | #define ISA_ISR_INP 0x04 /* Input Interrupt */ | ||
56 | #define ISA_ISR_SERIAL 0x08 /* Read out Serial ID after Reset */ | ||
57 | #define ISA_ISR_ERRSIG 0x10 /* Error Signal Input */ | ||
58 | #define ISA_ISR_ERR_MASK 0xfe /* Mask Error Interrupt */ | ||
59 | #define ISA_ISR_OUT_MASK 0xfd /* Mask Output Interrupt */ | ||
60 | #define ISA_ISR_INP_MASK 0xfb /* Mask Input Interrupt */ | ||
61 | |||
62 | /* Signature delivered after Reset at ISA_ISR_SERIAL (LSB first) */ | ||
63 | #define ISA_SER_ID 0x0201 /* ID for ISA Card */ | ||
64 | |||
65 | /**************************************************************************/ | ||
66 | /* EEPROM Register EPR (RW) */ | ||
67 | /**************************************************************************/ | ||
68 | /* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ | ||
69 | /* N/A | N/A | N/A |ROM Hold| ROM CS |ROM CLK | ROM IN |ROM Out */ | ||
70 | /**************************************************************************/ | ||
71 | #define ISA_EPR 2 /* Offset for this Register */ | ||
72 | #define ISA_EPR_OUT 0x01 /* Rome Register Out (RO) */ | ||
73 | #define ISA_EPR_IN 0x02 /* Rom Register In (WR) */ | ||
74 | #define ISA_EPR_CLK 0x04 /* Rom Clock (WR) */ | ||
75 | #define ISA_EPR_CS 0x08 /* Rom Cip Select (WR) */ | ||
76 | #define ISA_EPR_HOLD 0x10 /* Rom Hold Signal (WR) */ | ||
77 | |||
78 | /**************************************************************************/ | ||
79 | /* EEPROM enable Register EER (unused) */ | ||
80 | /**************************************************************************/ | ||
81 | #define ISA_EER 3 /* Offset for this Register */ | ||
82 | |||
83 | /**************************************************************************/ | ||
84 | /* SLC Data Input SDI (RO) */ | ||
85 | /**************************************************************************/ | ||
86 | #define ISA_SDI 4 /* Offset for this Register */ | ||
87 | |||
88 | /**************************************************************************/ | ||
89 | /* SLC Data Output SDO (WO) */ | ||
90 | /**************************************************************************/ | ||
91 | #define ISA_SDO 5 /* Offset for this Register */ | ||
92 | |||
93 | /**************************************************************************/ | ||
94 | /* IMS C011 Mode 2 Input Status Register for INMOS CPU SIS (RW) */ | ||
95 | /**************************************************************************/ | ||
96 | /* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ | ||
97 | /* N/A | N/A | N/A | N/A | N/A | N/A |Int Ena |Data Pre */ | ||
98 | /**************************************************************************/ | ||
99 | #define ISA_SIS 6 /* Offset for this Register */ | ||
100 | #define ISA_SIS_READY 0x01 /* If 1 : data is available */ | ||
101 | #define ISA_SIS_INT 0x02 /* Enable Interrupt for READ */ | ||
102 | |||
103 | /**************************************************************************/ | ||
104 | /* IMS C011 Mode 2 Output Status Register from INMOS CPU SOS (RW) */ | ||
105 | /**************************************************************************/ | ||
106 | /* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */ | ||
107 | /* N/A | N/A | N/A | N/A | N/A | N/A |Int Ena |Out Rdy */ | ||
108 | /**************************************************************************/ | ||
109 | #define ISA_SOS 7 /* Offset for this Register */ | ||
110 | #define ISA_SOS_READY 0x01 /* If 1 : we can write Data */ | ||
111 | #define ISA_SOS_INT 0x02 /* Enable Interrupt for WRITE */ | ||
112 | |||
113 | #define ISA_REGION 8 /* Number of Registers */ | ||
114 | |||
115 | |||
116 | /* Macros for accessing ports */ | ||
117 | #define ISA_PORT_COR (card->port+ISA_COR) | ||
118 | #define ISA_PORT_ISR (card->port+ISA_ISR) | ||
119 | #define ISA_PORT_EPR (card->port+ISA_EPR) | ||
120 | #define ISA_PORT_EER (card->port+ISA_EER) | ||
121 | #define ISA_PORT_SDI (card->port+ISA_SDI) | ||
122 | #define ISA_PORT_SDO (card->port+ISA_SDO) | ||
123 | #define ISA_PORT_SIS (card->port+ISA_SIS) | ||
124 | #define ISA_PORT_SOS (card->port+ISA_SOS) | ||
125 | |||
126 | /* Prototypes */ | ||
127 | |||
128 | extern int act2000_isa_detect(unsigned short portbase); | ||
129 | extern int act2000_isa_config_irq(act2000_card * card, short irq); | ||
130 | extern int act2000_isa_config_port(act2000_card * card, unsigned short portbase); | ||
131 | extern int act2000_isa_download(act2000_card * card, act2000_ddef __user * cb); | ||
132 | extern void act2000_isa_release(act2000_card * card); | ||
133 | extern void act2000_isa_receive(act2000_card *card); | ||
134 | extern void act2000_isa_send(act2000_card *card); | ||
135 | |||
136 | #endif /* act2000_isa_h */ | ||
diff --git a/drivers/isdn/act2000/capi.c b/drivers/isdn/act2000/capi.c new file mode 100644 index 000000000000..40395f567231 --- /dev/null +++ b/drivers/isdn/act2000/capi.c | |||
@@ -0,0 +1,1177 @@ | |||
1 | /* $Id: capi.c,v 1.9.6.2 2001/09/23 22:24:32 kai Exp $ | ||
2 | * | ||
3 | * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. | ||
4 | * CAPI encoder/decoder | ||
5 | * | ||
6 | * Author Fritz Elfert | ||
7 | * Copyright by Fritz Elfert <fritz@isdn4linux.de> | ||
8 | * | ||
9 | * This software may be used and distributed according to the terms | ||
10 | * of the GNU General Public License, incorporated herein by reference. | ||
11 | * | ||
12 | * Thanks to Friedemann Baitinger and IBM Germany | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include "act2000.h" | ||
17 | #include "capi.h" | ||
18 | |||
19 | static actcapi_msgdsc valid_msg[] = { | ||
20 | {{ 0x86, 0x02}, "DATA_B3_IND"}, /* DATA_B3_IND/CONF must be first because of speed!!! */ | ||
21 | {{ 0x86, 0x01}, "DATA_B3_CONF"}, | ||
22 | {{ 0x02, 0x01}, "CONNECT_CONF"}, | ||
23 | {{ 0x02, 0x02}, "CONNECT_IND"}, | ||
24 | {{ 0x09, 0x01}, "CONNECT_INFO_CONF"}, | ||
25 | {{ 0x03, 0x02}, "CONNECT_ACTIVE_IND"}, | ||
26 | {{ 0x04, 0x01}, "DISCONNECT_CONF"}, | ||
27 | {{ 0x04, 0x02}, "DISCONNECT_IND"}, | ||
28 | {{ 0x05, 0x01}, "LISTEN_CONF"}, | ||
29 | {{ 0x06, 0x01}, "GET_PARAMS_CONF"}, | ||
30 | {{ 0x07, 0x01}, "INFO_CONF"}, | ||
31 | {{ 0x07, 0x02}, "INFO_IND"}, | ||
32 | {{ 0x08, 0x01}, "DATA_CONF"}, | ||
33 | {{ 0x08, 0x02}, "DATA_IND"}, | ||
34 | {{ 0x40, 0x01}, "SELECT_B2_PROTOCOL_CONF"}, | ||
35 | {{ 0x80, 0x01}, "SELECT_B3_PROTOCOL_CONF"}, | ||
36 | {{ 0x81, 0x01}, "LISTEN_B3_CONF"}, | ||
37 | {{ 0x82, 0x01}, "CONNECT_B3_CONF"}, | ||
38 | {{ 0x82, 0x02}, "CONNECT_B3_IND"}, | ||
39 | {{ 0x83, 0x02}, "CONNECT_B3_ACTIVE_IND"}, | ||
40 | {{ 0x84, 0x01}, "DISCONNECT_B3_CONF"}, | ||
41 | {{ 0x84, 0x02}, "DISCONNECT_B3_IND"}, | ||
42 | {{ 0x85, 0x01}, "GET_B3_PARAMS_CONF"}, | ||
43 | {{ 0x01, 0x01}, "RESET_B3_CONF"}, | ||
44 | {{ 0x01, 0x02}, "RESET_B3_IND"}, | ||
45 | /* {{ 0x87, 0x02, "HANDSET_IND"}, not implemented */ | ||
46 | {{ 0xff, 0x01}, "MANUFACTURER_CONF"}, | ||
47 | {{ 0xff, 0x02}, "MANUFACTURER_IND"}, | ||
48 | #ifdef DEBUG_MSG | ||
49 | /* Requests */ | ||
50 | {{ 0x01, 0x00}, "RESET_B3_REQ"}, | ||
51 | {{ 0x02, 0x00}, "CONNECT_REQ"}, | ||
52 | {{ 0x04, 0x00}, "DISCONNECT_REQ"}, | ||
53 | {{ 0x05, 0x00}, "LISTEN_REQ"}, | ||
54 | {{ 0x06, 0x00}, "GET_PARAMS_REQ"}, | ||
55 | {{ 0x07, 0x00}, "INFO_REQ"}, | ||
56 | {{ 0x08, 0x00}, "DATA_REQ"}, | ||
57 | {{ 0x09, 0x00}, "CONNECT_INFO_REQ"}, | ||
58 | {{ 0x40, 0x00}, "SELECT_B2_PROTOCOL_REQ"}, | ||
59 | {{ 0x80, 0x00}, "SELECT_B3_PROTOCOL_REQ"}, | ||
60 | {{ 0x81, 0x00}, "LISTEN_B3_REQ"}, | ||
61 | {{ 0x82, 0x00}, "CONNECT_B3_REQ"}, | ||
62 | {{ 0x84, 0x00}, "DISCONNECT_B3_REQ"}, | ||
63 | {{ 0x85, 0x00}, "GET_B3_PARAMS_REQ"}, | ||
64 | {{ 0x86, 0x00}, "DATA_B3_REQ"}, | ||
65 | {{ 0xff, 0x00}, "MANUFACTURER_REQ"}, | ||
66 | /* Responses */ | ||
67 | {{ 0x01, 0x03}, "RESET_B3_RESP"}, | ||
68 | {{ 0x02, 0x03}, "CONNECT_RESP"}, | ||
69 | {{ 0x03, 0x03}, "CONNECT_ACTIVE_RESP"}, | ||
70 | {{ 0x04, 0x03}, "DISCONNECT_RESP"}, | ||
71 | {{ 0x07, 0x03}, "INFO_RESP"}, | ||
72 | {{ 0x08, 0x03}, "DATA_RESP"}, | ||
73 | {{ 0x82, 0x03}, "CONNECT_B3_RESP"}, | ||
74 | {{ 0x83, 0x03}, "CONNECT_B3_ACTIVE_RESP"}, | ||
75 | {{ 0x84, 0x03}, "DISCONNECT_B3_RESP"}, | ||
76 | {{ 0x86, 0x03}, "DATA_B3_RESP"}, | ||
77 | {{ 0xff, 0x03}, "MANUFACTURER_RESP"}, | ||
78 | #endif | ||
79 | {{ 0x00, 0x00}, NULL}, | ||
80 | }; | ||
81 | #define num_valid_msg (sizeof(valid_msg)/sizeof(actcapi_msgdsc)) | ||
82 | #define num_valid_imsg 27 /* MANUFACTURER_IND */ | ||
83 | |||
84 | /* | ||
85 | * Check for a valid incoming CAPI message. | ||
86 | * Return: | ||
87 | * 0 = Invalid message | ||
88 | * 1 = Valid message, no B-Channel-data | ||
89 | * 2 = Valid message, B-Channel-data | ||
90 | */ | ||
91 | int | ||
92 | actcapi_chkhdr(act2000_card * card, actcapi_msghdr *hdr) | ||
93 | { | ||
94 | int i; | ||
95 | |||
96 | if (hdr->applicationID != 1) | ||
97 | return 0; | ||
98 | if (hdr->len < 9) | ||
99 | return 0; | ||
100 | for (i = 0; i < num_valid_imsg; i++) | ||
101 | if ((hdr->cmd.cmd == valid_msg[i].cmd.cmd) && | ||
102 | (hdr->cmd.subcmd == valid_msg[i].cmd.subcmd)) { | ||
103 | return (i?1:2); | ||
104 | } | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | #define ACTCAPI_MKHDR(l, c, s) { \ | ||
109 | skb = alloc_skb(l + 8, GFP_ATOMIC); \ | ||
110 | if (skb) { \ | ||
111 | m = (actcapi_msg *)skb_put(skb, l + 8); \ | ||
112 | m->hdr.len = l + 8; \ | ||
113 | m->hdr.applicationID = 1; \ | ||
114 | m->hdr.cmd.cmd = c; \ | ||
115 | m->hdr.cmd.subcmd = s; \ | ||
116 | m->hdr.msgnum = actcapi_nextsmsg(card); \ | ||
117 | } else m = NULL;\ | ||
118 | } | ||
119 | |||
120 | #define ACTCAPI_CHKSKB if (!skb) { \ | ||
121 | printk(KERN_WARNING "actcapi: alloc_skb failed\n"); \ | ||
122 | return; \ | ||
123 | } | ||
124 | |||
125 | #define ACTCAPI_QUEUE_TX { \ | ||
126 | actcapi_debug_msg(skb, 1); \ | ||
127 | skb_queue_tail(&card->sndq, skb); \ | ||
128 | act2000_schedule_tx(card); \ | ||
129 | } | ||
130 | |||
131 | int | ||
132 | actcapi_listen_req(act2000_card *card) | ||
133 | { | ||
134 | __u16 eazmask = 0; | ||
135 | int i; | ||
136 | actcapi_msg *m; | ||
137 | struct sk_buff *skb; | ||
138 | |||
139 | for (i = 0; i < ACT2000_BCH; i++) | ||
140 | eazmask |= card->bch[i].eazmask; | ||
141 | ACTCAPI_MKHDR(9, 0x05, 0x00); | ||
142 | if (!skb) { | ||
143 | printk(KERN_WARNING "actcapi: alloc_skb failed\n"); | ||
144 | return -ENOMEM; | ||
145 | } | ||
146 | m->msg.listen_req.controller = 0; | ||
147 | m->msg.listen_req.infomask = 0x3f; /* All information */ | ||
148 | m->msg.listen_req.eazmask = eazmask; | ||
149 | m->msg.listen_req.simask = (eazmask)?0x86:0; /* All SI's */ | ||
150 | ACTCAPI_QUEUE_TX; | ||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | int | ||
155 | actcapi_connect_req(act2000_card *card, act2000_chan *chan, char *phone, | ||
156 | char eaz, int si1, int si2) | ||
157 | { | ||
158 | actcapi_msg *m; | ||
159 | struct sk_buff *skb; | ||
160 | |||
161 | ACTCAPI_MKHDR((11 + strlen(phone)), 0x02, 0x00); | ||
162 | if (!skb) { | ||
163 | printk(KERN_WARNING "actcapi: alloc_skb failed\n"); | ||
164 | chan->fsm_state = ACT2000_STATE_NULL; | ||
165 | return -ENOMEM; | ||
166 | } | ||
167 | m->msg.connect_req.controller = 0; | ||
168 | m->msg.connect_req.bchan = 0x83; | ||
169 | m->msg.connect_req.infomask = 0x3f; | ||
170 | m->msg.connect_req.si1 = si1; | ||
171 | m->msg.connect_req.si2 = si2; | ||
172 | m->msg.connect_req.eaz = eaz?eaz:'0'; | ||
173 | m->msg.connect_req.addr.len = strlen(phone) + 1; | ||
174 | m->msg.connect_req.addr.tnp = 0x81; | ||
175 | memcpy(m->msg.connect_req.addr.num, phone, strlen(phone)); | ||
176 | chan->callref = m->hdr.msgnum; | ||
177 | ACTCAPI_QUEUE_TX; | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static void | ||
182 | actcapi_connect_b3_req(act2000_card *card, act2000_chan *chan) | ||
183 | { | ||
184 | actcapi_msg *m; | ||
185 | struct sk_buff *skb; | ||
186 | |||
187 | ACTCAPI_MKHDR(17, 0x82, 0x00); | ||
188 | ACTCAPI_CHKSKB; | ||
189 | m->msg.connect_b3_req.plci = chan->plci; | ||
190 | memset(&m->msg.connect_b3_req.ncpi, 0, | ||
191 | sizeof(m->msg.connect_b3_req.ncpi)); | ||
192 | m->msg.connect_b3_req.ncpi.len = 13; | ||
193 | m->msg.connect_b3_req.ncpi.modulo = 8; | ||
194 | ACTCAPI_QUEUE_TX; | ||
195 | } | ||
196 | |||
197 | /* | ||
198 | * Set net type (1TR6) or (EDSS1) | ||
199 | */ | ||
200 | int | ||
201 | actcapi_manufacturer_req_net(act2000_card *card) | ||
202 | { | ||
203 | actcapi_msg *m; | ||
204 | struct sk_buff *skb; | ||
205 | |||
206 | ACTCAPI_MKHDR(5, 0xff, 0x00); | ||
207 | if (!skb) { | ||
208 | printk(KERN_WARNING "actcapi: alloc_skb failed\n"); | ||
209 | return -ENOMEM; | ||
210 | } | ||
211 | m->msg.manufacturer_req_net.manuf_msg = 0x11; | ||
212 | m->msg.manufacturer_req_net.controller = 1; | ||
213 | m->msg.manufacturer_req_net.nettype = (card->ptype == ISDN_PTYPE_EURO)?1:0; | ||
214 | ACTCAPI_QUEUE_TX; | ||
215 | printk(KERN_INFO "act2000 %s: D-channel protocol now %s\n", | ||
216 | card->interface.id, (card->ptype == ISDN_PTYPE_EURO)?"euro":"1tr6"); | ||
217 | card->interface.features &= | ||
218 | ~(ISDN_FEATURE_P_UNKNOWN | ISDN_FEATURE_P_EURO | ISDN_FEATURE_P_1TR6); | ||
219 | card->interface.features |= | ||
220 | ((card->ptype == ISDN_PTYPE_EURO)?ISDN_FEATURE_P_EURO:ISDN_FEATURE_P_1TR6); | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | /* | ||
225 | * Switch V.42 on or off | ||
226 | */ | ||
227 | int | ||
228 | actcapi_manufacturer_req_v42(act2000_card *card, ulong arg) | ||
229 | { | ||
230 | actcapi_msg *m; | ||
231 | struct sk_buff *skb; | ||
232 | |||
233 | ACTCAPI_MKHDR(8, 0xff, 0x00); | ||
234 | if (!skb) { | ||
235 | |||
236 | printk(KERN_WARNING "actcapi: alloc_skb failed\n"); | ||
237 | return -ENOMEM; | ||
238 | } | ||
239 | m->msg.manufacturer_req_v42.manuf_msg = 0x10; | ||
240 | m->msg.manufacturer_req_v42.controller = 0; | ||
241 | m->msg.manufacturer_req_v42.v42control = (arg?1:0); | ||
242 | ACTCAPI_QUEUE_TX; | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | * Set error-handler | ||
248 | */ | ||
249 | int | ||
250 | actcapi_manufacturer_req_errh(act2000_card *card) | ||
251 | { | ||
252 | actcapi_msg *m; | ||
253 | struct sk_buff *skb; | ||
254 | |||
255 | ACTCAPI_MKHDR(4, 0xff, 0x00); | ||
256 | if (!skb) { | ||
257 | |||
258 | printk(KERN_WARNING "actcapi: alloc_skb failed\n"); | ||
259 | return -ENOMEM; | ||
260 | } | ||
261 | m->msg.manufacturer_req_err.manuf_msg = 0x03; | ||
262 | m->msg.manufacturer_req_err.controller = 0; | ||
263 | ACTCAPI_QUEUE_TX; | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | /* | ||
268 | * Set MSN-Mapping. | ||
269 | */ | ||
270 | int | ||
271 | actcapi_manufacturer_req_msn(act2000_card *card) | ||
272 | { | ||
273 | msn_entry *p = card->msn_list; | ||
274 | actcapi_msg *m; | ||
275 | struct sk_buff *skb; | ||
276 | int len; | ||
277 | |||
278 | while (p) { | ||
279 | int i; | ||
280 | |||
281 | len = strlen(p->msn); | ||
282 | for (i = 0; i < 2; i++) { | ||
283 | ACTCAPI_MKHDR(6 + len, 0xff, 0x00); | ||
284 | if (!skb) { | ||
285 | printk(KERN_WARNING "actcapi: alloc_skb failed\n"); | ||
286 | return -ENOMEM; | ||
287 | } | ||
288 | m->msg.manufacturer_req_msn.manuf_msg = 0x13 + i; | ||
289 | m->msg.manufacturer_req_msn.controller = 0; | ||
290 | m->msg.manufacturer_req_msn.msnmap.eaz = p->eaz; | ||
291 | m->msg.manufacturer_req_msn.msnmap.len = len; | ||
292 | memcpy(m->msg.manufacturer_req_msn.msnmap.msn, p->msn, len); | ||
293 | ACTCAPI_QUEUE_TX; | ||
294 | } | ||
295 | p = p->next; | ||
296 | } | ||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | void | ||
301 | actcapi_select_b2_protocol_req(act2000_card *card, act2000_chan *chan) | ||
302 | { | ||
303 | actcapi_msg *m; | ||
304 | struct sk_buff *skb; | ||
305 | |||
306 | ACTCAPI_MKHDR(10, 0x40, 0x00); | ||
307 | ACTCAPI_CHKSKB; | ||
308 | m->msg.select_b2_protocol_req.plci = chan->plci; | ||
309 | memset(&m->msg.select_b2_protocol_req.dlpd, 0, | ||
310 | sizeof(m->msg.select_b2_protocol_req.dlpd)); | ||
311 | m->msg.select_b2_protocol_req.dlpd.len = 6; | ||
312 | switch (chan->l2prot) { | ||
313 | case ISDN_PROTO_L2_TRANS: | ||
314 | m->msg.select_b2_protocol_req.protocol = 0x03; | ||
315 | m->msg.select_b2_protocol_req.dlpd.dlen = 4000; | ||
316 | break; | ||
317 | case ISDN_PROTO_L2_HDLC: | ||
318 | m->msg.select_b2_protocol_req.protocol = 0x02; | ||
319 | m->msg.select_b2_protocol_req.dlpd.dlen = 4000; | ||
320 | break; | ||
321 | case ISDN_PROTO_L2_X75I: | ||
322 | case ISDN_PROTO_L2_X75UI: | ||
323 | case ISDN_PROTO_L2_X75BUI: | ||
324 | m->msg.select_b2_protocol_req.protocol = 0x01; | ||
325 | m->msg.select_b2_protocol_req.dlpd.dlen = 4000; | ||
326 | m->msg.select_b2_protocol_req.dlpd.laa = 3; | ||
327 | m->msg.select_b2_protocol_req.dlpd.lab = 1; | ||
328 | m->msg.select_b2_protocol_req.dlpd.win = 7; | ||
329 | m->msg.select_b2_protocol_req.dlpd.modulo = 8; | ||
330 | break; | ||
331 | } | ||
332 | ACTCAPI_QUEUE_TX; | ||
333 | } | ||
334 | |||
335 | static void | ||
336 | actcapi_select_b3_protocol_req(act2000_card *card, act2000_chan *chan) | ||
337 | { | ||
338 | actcapi_msg *m; | ||
339 | struct sk_buff *skb; | ||
340 | |||
341 | ACTCAPI_MKHDR(17, 0x80, 0x00); | ||
342 | ACTCAPI_CHKSKB; | ||
343 | m->msg.select_b3_protocol_req.plci = chan->plci; | ||
344 | memset(&m->msg.select_b3_protocol_req.ncpd, 0, | ||
345 | sizeof(m->msg.select_b3_protocol_req.ncpd)); | ||
346 | switch (chan->l3prot) { | ||
347 | case ISDN_PROTO_L3_TRANS: | ||
348 | m->msg.select_b3_protocol_req.protocol = 0x04; | ||
349 | m->msg.select_b3_protocol_req.ncpd.len = 13; | ||
350 | m->msg.select_b3_protocol_req.ncpd.modulo = 8; | ||
351 | break; | ||
352 | } | ||
353 | ACTCAPI_QUEUE_TX; | ||
354 | } | ||
355 | |||
356 | static void | ||
357 | actcapi_listen_b3_req(act2000_card *card, act2000_chan *chan) | ||
358 | { | ||
359 | actcapi_msg *m; | ||
360 | struct sk_buff *skb; | ||
361 | |||
362 | ACTCAPI_MKHDR(2, 0x81, 0x00); | ||
363 | ACTCAPI_CHKSKB; | ||
364 | m->msg.listen_b3_req.plci = chan->plci; | ||
365 | ACTCAPI_QUEUE_TX; | ||
366 | } | ||
367 | |||
368 | static void | ||
369 | actcapi_disconnect_req(act2000_card *card, act2000_chan *chan) | ||
370 | { | ||
371 | actcapi_msg *m; | ||
372 | struct sk_buff *skb; | ||
373 | |||
374 | ACTCAPI_MKHDR(3, 0x04, 0x00); | ||
375 | ACTCAPI_CHKSKB; | ||
376 | m->msg.disconnect_req.plci = chan->plci; | ||
377 | m->msg.disconnect_req.cause = 0; | ||
378 | ACTCAPI_QUEUE_TX; | ||
379 | } | ||
380 | |||
381 | void | ||
382 | actcapi_disconnect_b3_req(act2000_card *card, act2000_chan *chan) | ||
383 | { | ||
384 | actcapi_msg *m; | ||
385 | struct sk_buff *skb; | ||
386 | |||
387 | ACTCAPI_MKHDR(17, 0x84, 0x00); | ||
388 | ACTCAPI_CHKSKB; | ||
389 | m->msg.disconnect_b3_req.ncci = chan->ncci; | ||
390 | memset(&m->msg.disconnect_b3_req.ncpi, 0, | ||
391 | sizeof(m->msg.disconnect_b3_req.ncpi)); | ||
392 | m->msg.disconnect_b3_req.ncpi.len = 13; | ||
393 | m->msg.disconnect_b3_req.ncpi.modulo = 8; | ||
394 | chan->fsm_state = ACT2000_STATE_BHWAIT; | ||
395 | ACTCAPI_QUEUE_TX; | ||
396 | } | ||
397 | |||
398 | void | ||
399 | actcapi_connect_resp(act2000_card *card, act2000_chan *chan, __u8 cause) | ||
400 | { | ||
401 | actcapi_msg *m; | ||
402 | struct sk_buff *skb; | ||
403 | |||
404 | ACTCAPI_MKHDR(3, 0x02, 0x03); | ||
405 | ACTCAPI_CHKSKB; | ||
406 | m->msg.connect_resp.plci = chan->plci; | ||
407 | m->msg.connect_resp.rejectcause = cause; | ||
408 | if (cause) { | ||
409 | chan->fsm_state = ACT2000_STATE_NULL; | ||
410 | chan->plci = 0x8000; | ||
411 | } else | ||
412 | chan->fsm_state = ACT2000_STATE_IWAIT; | ||
413 | ACTCAPI_QUEUE_TX; | ||
414 | } | ||
415 | |||
416 | static void | ||
417 | actcapi_connect_active_resp(act2000_card *card, act2000_chan *chan) | ||
418 | { | ||
419 | actcapi_msg *m; | ||
420 | struct sk_buff *skb; | ||
421 | |||
422 | ACTCAPI_MKHDR(2, 0x03, 0x03); | ||
423 | ACTCAPI_CHKSKB; | ||
424 | m->msg.connect_resp.plci = chan->plci; | ||
425 | if (chan->fsm_state == ACT2000_STATE_IWAIT) | ||
426 | chan->fsm_state = ACT2000_STATE_IBWAIT; | ||
427 | ACTCAPI_QUEUE_TX; | ||
428 | } | ||
429 | |||
430 | static void | ||
431 | actcapi_connect_b3_resp(act2000_card *card, act2000_chan *chan, __u8 rejectcause) | ||
432 | { | ||
433 | actcapi_msg *m; | ||
434 | struct sk_buff *skb; | ||
435 | |||
436 | ACTCAPI_MKHDR((rejectcause?3:17), 0x82, 0x03); | ||
437 | ACTCAPI_CHKSKB; | ||
438 | m->msg.connect_b3_resp.ncci = chan->ncci; | ||
439 | m->msg.connect_b3_resp.rejectcause = rejectcause; | ||
440 | if (!rejectcause) { | ||
441 | memset(&m->msg.connect_b3_resp.ncpi, 0, | ||
442 | sizeof(m->msg.connect_b3_resp.ncpi)); | ||
443 | m->msg.connect_b3_resp.ncpi.len = 13; | ||
444 | m->msg.connect_b3_resp.ncpi.modulo = 8; | ||
445 | chan->fsm_state = ACT2000_STATE_BWAIT; | ||
446 | } | ||
447 | ACTCAPI_QUEUE_TX; | ||
448 | } | ||
449 | |||
450 | static void | ||
451 | actcapi_connect_b3_active_resp(act2000_card *card, act2000_chan *chan) | ||
452 | { | ||
453 | actcapi_msg *m; | ||
454 | struct sk_buff *skb; | ||
455 | |||
456 | ACTCAPI_MKHDR(2, 0x83, 0x03); | ||
457 | ACTCAPI_CHKSKB; | ||
458 | m->msg.connect_b3_active_resp.ncci = chan->ncci; | ||
459 | chan->fsm_state = ACT2000_STATE_ACTIVE; | ||
460 | ACTCAPI_QUEUE_TX; | ||
461 | } | ||
462 | |||
463 | static void | ||
464 | actcapi_info_resp(act2000_card *card, act2000_chan *chan) | ||
465 | { | ||
466 | actcapi_msg *m; | ||
467 | struct sk_buff *skb; | ||
468 | |||
469 | ACTCAPI_MKHDR(2, 0x07, 0x03); | ||
470 | ACTCAPI_CHKSKB; | ||
471 | m->msg.info_resp.plci = chan->plci; | ||
472 | ACTCAPI_QUEUE_TX; | ||
473 | } | ||
474 | |||
475 | static void | ||
476 | actcapi_disconnect_b3_resp(act2000_card *card, act2000_chan *chan) | ||
477 | { | ||
478 | actcapi_msg *m; | ||
479 | struct sk_buff *skb; | ||
480 | |||
481 | ACTCAPI_MKHDR(2, 0x84, 0x03); | ||
482 | ACTCAPI_CHKSKB; | ||
483 | m->msg.disconnect_b3_resp.ncci = chan->ncci; | ||
484 | chan->ncci = 0x8000; | ||
485 | chan->queued = 0; | ||
486 | ACTCAPI_QUEUE_TX; | ||
487 | } | ||
488 | |||
489 | static void | ||
490 | actcapi_disconnect_resp(act2000_card *card, act2000_chan *chan) | ||
491 | { | ||
492 | actcapi_msg *m; | ||
493 | struct sk_buff *skb; | ||
494 | |||
495 | ACTCAPI_MKHDR(2, 0x04, 0x03); | ||
496 | ACTCAPI_CHKSKB; | ||
497 | m->msg.disconnect_resp.plci = chan->plci; | ||
498 | chan->plci = 0x8000; | ||
499 | ACTCAPI_QUEUE_TX; | ||
500 | } | ||
501 | |||
502 | static int | ||
503 | new_plci(act2000_card *card, __u16 plci) | ||
504 | { | ||
505 | int i; | ||
506 | for (i = 0; i < ACT2000_BCH; i++) | ||
507 | if (card->bch[i].plci == 0x8000) { | ||
508 | card->bch[i].plci = plci; | ||
509 | return i; | ||
510 | } | ||
511 | return -1; | ||
512 | } | ||
513 | |||
514 | static int | ||
515 | find_plci(act2000_card *card, __u16 plci) | ||
516 | { | ||
517 | int i; | ||
518 | for (i = 0; i < ACT2000_BCH; i++) | ||
519 | if (card->bch[i].plci == plci) | ||
520 | return i; | ||
521 | return -1; | ||
522 | } | ||
523 | |||
524 | static int | ||
525 | find_ncci(act2000_card *card, __u16 ncci) | ||
526 | { | ||
527 | int i; | ||
528 | for (i = 0; i < ACT2000_BCH; i++) | ||
529 | if (card->bch[i].ncci == ncci) | ||
530 | return i; | ||
531 | return -1; | ||
532 | } | ||
533 | |||
534 | static int | ||
535 | find_dialing(act2000_card *card, __u16 callref) | ||
536 | { | ||
537 | int i; | ||
538 | for (i = 0; i < ACT2000_BCH; i++) | ||
539 | if ((card->bch[i].callref == callref) && | ||
540 | (card->bch[i].fsm_state == ACT2000_STATE_OCALL)) | ||
541 | return i; | ||
542 | return -1; | ||
543 | } | ||
544 | |||
545 | static int | ||
546 | actcapi_data_b3_ind(act2000_card *card, struct sk_buff *skb) { | ||
547 | __u16 plci; | ||
548 | __u16 ncci; | ||
549 | __u16 controller; | ||
550 | __u8 blocknr; | ||
551 | int chan; | ||
552 | actcapi_msg *msg = (actcapi_msg *)skb->data; | ||
553 | |||
554 | EVAL_NCCI(msg->msg.data_b3_ind.fakencci, plci, controller, ncci); | ||
555 | chan = find_ncci(card, ncci); | ||
556 | if (chan < 0) | ||
557 | return 0; | ||
558 | if (card->bch[chan].fsm_state != ACT2000_STATE_ACTIVE) | ||
559 | return 0; | ||
560 | if (card->bch[chan].plci != plci) | ||
561 | return 0; | ||
562 | blocknr = msg->msg.data_b3_ind.blocknr; | ||
563 | skb_pull(skb, 19); | ||
564 | card->interface.rcvcallb_skb(card->myid, chan, skb); | ||
565 | if (!(skb = alloc_skb(11, GFP_ATOMIC))) { | ||
566 | printk(KERN_WARNING "actcapi: alloc_skb failed\n"); | ||
567 | return 1; | ||
568 | } | ||
569 | msg = (actcapi_msg *)skb_put(skb, 11); | ||
570 | msg->hdr.len = 11; | ||
571 | msg->hdr.applicationID = 1; | ||
572 | msg->hdr.cmd.cmd = 0x86; | ||
573 | msg->hdr.cmd.subcmd = 0x03; | ||
574 | msg->hdr.msgnum = actcapi_nextsmsg(card); | ||
575 | msg->msg.data_b3_resp.ncci = ncci; | ||
576 | msg->msg.data_b3_resp.blocknr = blocknr; | ||
577 | ACTCAPI_QUEUE_TX; | ||
578 | return 1; | ||
579 | } | ||
580 | |||
581 | /* | ||
582 | * Walk over ackq, unlink DATA_B3_REQ from it, if | ||
583 | * ncci and blocknr are matching. | ||
584 | * Decrement queued-bytes counter. | ||
585 | */ | ||
586 | static int | ||
587 | handle_ack(act2000_card *card, act2000_chan *chan, __u8 blocknr) { | ||
588 | unsigned long flags; | ||
589 | struct sk_buff *skb; | ||
590 | struct sk_buff *tmp; | ||
591 | struct actcapi_msg *m; | ||
592 | int ret = 0; | ||
593 | |||
594 | spin_lock_irqsave(&card->lock, flags); | ||
595 | skb = skb_peek(&card->ackq); | ||
596 | spin_unlock_irqrestore(&card->lock, flags); | ||
597 | if (!skb) { | ||
598 | printk(KERN_WARNING "act2000: handle_ack nothing found!\n"); | ||
599 | return 0; | ||
600 | } | ||
601 | tmp = skb; | ||
602 | while (1) { | ||
603 | m = (actcapi_msg *)tmp->data; | ||
604 | if ((((m->msg.data_b3_req.fakencci >> 8) & 0xff) == chan->ncci) && | ||
605 | (m->msg.data_b3_req.blocknr == blocknr)) { | ||
606 | /* found corresponding DATA_B3_REQ */ | ||
607 | skb_unlink(tmp); | ||
608 | chan->queued -= m->msg.data_b3_req.datalen; | ||
609 | if (m->msg.data_b3_req.flags) | ||
610 | ret = m->msg.data_b3_req.datalen; | ||
611 | dev_kfree_skb(tmp); | ||
612 | if (chan->queued < 0) | ||
613 | chan->queued = 0; | ||
614 | return ret; | ||
615 | } | ||
616 | spin_lock_irqsave(&card->lock, flags); | ||
617 | tmp = skb_peek((struct sk_buff_head *)tmp); | ||
618 | spin_unlock_irqrestore(&card->lock, flags); | ||
619 | if ((tmp == skb) || (tmp == NULL)) { | ||
620 | /* reached end of queue */ | ||
621 | printk(KERN_WARNING "act2000: handle_ack nothing found!\n"); | ||
622 | return 0; | ||
623 | } | ||
624 | } | ||
625 | } | ||
626 | |||
627 | void | ||
628 | actcapi_dispatch(act2000_card *card) | ||
629 | { | ||
630 | struct sk_buff *skb; | ||
631 | actcapi_msg *msg; | ||
632 | __u16 ccmd; | ||
633 | int chan; | ||
634 | int len; | ||
635 | act2000_chan *ctmp; | ||
636 | isdn_ctrl cmd; | ||
637 | char tmp[170]; | ||
638 | |||
639 | while ((skb = skb_dequeue(&card->rcvq))) { | ||
640 | actcapi_debug_msg(skb, 0); | ||
641 | msg = (actcapi_msg *)skb->data; | ||
642 | ccmd = ((msg->hdr.cmd.cmd << 8) | msg->hdr.cmd.subcmd); | ||
643 | switch (ccmd) { | ||
644 | case 0x8602: | ||
645 | /* DATA_B3_IND */ | ||
646 | if (actcapi_data_b3_ind(card, skb)) | ||
647 | return; | ||
648 | break; | ||
649 | case 0x8601: | ||
650 | /* DATA_B3_CONF */ | ||
651 | chan = find_ncci(card, msg->msg.data_b3_conf.ncci); | ||
652 | if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_ACTIVE)) { | ||
653 | if (msg->msg.data_b3_conf.info != 0) | ||
654 | printk(KERN_WARNING "act2000: DATA_B3_CONF: %04x\n", | ||
655 | msg->msg.data_b3_conf.info); | ||
656 | len = handle_ack(card, &card->bch[chan], | ||
657 | msg->msg.data_b3_conf.blocknr); | ||
658 | if (len) { | ||
659 | cmd.driver = card->myid; | ||
660 | cmd.command = ISDN_STAT_BSENT; | ||
661 | cmd.arg = chan; | ||
662 | cmd.parm.length = len; | ||
663 | card->interface.statcallb(&cmd); | ||
664 | } | ||
665 | } | ||
666 | break; | ||
667 | case 0x0201: | ||
668 | /* CONNECT_CONF */ | ||
669 | chan = find_dialing(card, msg->hdr.msgnum); | ||
670 | if (chan >= 0) { | ||
671 | if (msg->msg.connect_conf.info) { | ||
672 | card->bch[chan].fsm_state = ACT2000_STATE_NULL; | ||
673 | cmd.driver = card->myid; | ||
674 | cmd.command = ISDN_STAT_DHUP; | ||
675 | cmd.arg = chan; | ||
676 | card->interface.statcallb(&cmd); | ||
677 | } else { | ||
678 | card->bch[chan].fsm_state = ACT2000_STATE_OWAIT; | ||
679 | card->bch[chan].plci = msg->msg.connect_conf.plci; | ||
680 | } | ||
681 | } | ||
682 | break; | ||
683 | case 0x0202: | ||
684 | /* CONNECT_IND */ | ||
685 | chan = new_plci(card, msg->msg.connect_ind.plci); | ||
686 | if (chan < 0) { | ||
687 | ctmp = (act2000_chan *)tmp; | ||
688 | ctmp->plci = msg->msg.connect_ind.plci; | ||
689 | actcapi_connect_resp(card, ctmp, 0x11); /* All Card-Cannels busy */ | ||
690 | } else { | ||
691 | card->bch[chan].fsm_state = ACT2000_STATE_ICALL; | ||
692 | cmd.driver = card->myid; | ||
693 | cmd.command = ISDN_STAT_ICALL; | ||
694 | cmd.arg = chan; | ||
695 | cmd.parm.setup.si1 = msg->msg.connect_ind.si1; | ||
696 | cmd.parm.setup.si2 = msg->msg.connect_ind.si2; | ||
697 | if (card->ptype == ISDN_PTYPE_EURO) | ||
698 | strcpy(cmd.parm.setup.eazmsn, | ||
699 | act2000_find_eaz(card, msg->msg.connect_ind.eaz)); | ||
700 | else { | ||
701 | cmd.parm.setup.eazmsn[0] = msg->msg.connect_ind.eaz; | ||
702 | cmd.parm.setup.eazmsn[1] = 0; | ||
703 | } | ||
704 | memset(cmd.parm.setup.phone, 0, sizeof(cmd.parm.setup.phone)); | ||
705 | memcpy(cmd.parm.setup.phone, msg->msg.connect_ind.addr.num, | ||
706 | msg->msg.connect_ind.addr.len - 1); | ||
707 | cmd.parm.setup.plan = msg->msg.connect_ind.addr.tnp; | ||
708 | cmd.parm.setup.screen = 0; | ||
709 | if (card->interface.statcallb(&cmd) == 2) | ||
710 | actcapi_connect_resp(card, &card->bch[chan], 0x15); /* Reject Call */ | ||
711 | } | ||
712 | break; | ||
713 | case 0x0302: | ||
714 | /* CONNECT_ACTIVE_IND */ | ||
715 | chan = find_plci(card, msg->msg.connect_active_ind.plci); | ||
716 | if (chan >= 0) | ||
717 | switch (card->bch[chan].fsm_state) { | ||
718 | case ACT2000_STATE_IWAIT: | ||
719 | actcapi_connect_active_resp(card, &card->bch[chan]); | ||
720 | break; | ||
721 | case ACT2000_STATE_OWAIT: | ||
722 | actcapi_connect_active_resp(card, &card->bch[chan]); | ||
723 | actcapi_select_b2_protocol_req(card, &card->bch[chan]); | ||
724 | break; | ||
725 | } | ||
726 | break; | ||
727 | case 0x8202: | ||
728 | /* CONNECT_B3_IND */ | ||
729 | chan = find_plci(card, msg->msg.connect_b3_ind.plci); | ||
730 | if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_IBWAIT)) { | ||
731 | card->bch[chan].ncci = msg->msg.connect_b3_ind.ncci; | ||
732 | actcapi_connect_b3_resp(card, &card->bch[chan], 0); | ||
733 | } else { | ||
734 | ctmp = (act2000_chan *)tmp; | ||
735 | ctmp->ncci = msg->msg.connect_b3_ind.ncci; | ||
736 | actcapi_connect_b3_resp(card, ctmp, 0x11); /* All Card-Cannels busy */ | ||
737 | } | ||
738 | break; | ||
739 | case 0x8302: | ||
740 | /* CONNECT_B3_ACTIVE_IND */ | ||
741 | chan = find_ncci(card, msg->msg.connect_b3_active_ind.ncci); | ||
742 | if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_BWAIT)) { | ||
743 | actcapi_connect_b3_active_resp(card, &card->bch[chan]); | ||
744 | cmd.driver = card->myid; | ||
745 | cmd.command = ISDN_STAT_BCONN; | ||
746 | cmd.arg = chan; | ||
747 | card->interface.statcallb(&cmd); | ||
748 | } | ||
749 | break; | ||
750 | case 0x8402: | ||
751 | /* DISCONNECT_B3_IND */ | ||
752 | chan = find_ncci(card, msg->msg.disconnect_b3_ind.ncci); | ||
753 | if (chan >= 0) { | ||
754 | ctmp = &card->bch[chan]; | ||
755 | actcapi_disconnect_b3_resp(card, ctmp); | ||
756 | switch (ctmp->fsm_state) { | ||
757 | case ACT2000_STATE_ACTIVE: | ||
758 | ctmp->fsm_state = ACT2000_STATE_DHWAIT2; | ||
759 | cmd.driver = card->myid; | ||
760 | cmd.command = ISDN_STAT_BHUP; | ||
761 | cmd.arg = chan; | ||
762 | card->interface.statcallb(&cmd); | ||
763 | break; | ||
764 | case ACT2000_STATE_BHWAIT2: | ||
765 | actcapi_disconnect_req(card, ctmp); | ||
766 | ctmp->fsm_state = ACT2000_STATE_DHWAIT; | ||
767 | cmd.driver = card->myid; | ||
768 | cmd.command = ISDN_STAT_BHUP; | ||
769 | cmd.arg = chan; | ||
770 | card->interface.statcallb(&cmd); | ||
771 | break; | ||
772 | } | ||
773 | } | ||
774 | break; | ||
775 | case 0x0402: | ||
776 | /* DISCONNECT_IND */ | ||
777 | chan = find_plci(card, msg->msg.disconnect_ind.plci); | ||
778 | if (chan >= 0) { | ||
779 | ctmp = &card->bch[chan]; | ||
780 | actcapi_disconnect_resp(card, ctmp); | ||
781 | ctmp->fsm_state = ACT2000_STATE_NULL; | ||
782 | cmd.driver = card->myid; | ||
783 | cmd.command = ISDN_STAT_DHUP; | ||
784 | cmd.arg = chan; | ||
785 | card->interface.statcallb(&cmd); | ||
786 | } else { | ||
787 | ctmp = (act2000_chan *)tmp; | ||
788 | ctmp->plci = msg->msg.disconnect_ind.plci; | ||
789 | actcapi_disconnect_resp(card, ctmp); | ||
790 | } | ||
791 | break; | ||
792 | case 0x4001: | ||
793 | /* SELECT_B2_PROTOCOL_CONF */ | ||
794 | chan = find_plci(card, msg->msg.select_b2_protocol_conf.plci); | ||
795 | if (chan >= 0) | ||
796 | switch (card->bch[chan].fsm_state) { | ||
797 | case ACT2000_STATE_ICALL: | ||
798 | case ACT2000_STATE_OWAIT: | ||
799 | ctmp = &card->bch[chan]; | ||
800 | if (msg->msg.select_b2_protocol_conf.info == 0) | ||
801 | actcapi_select_b3_protocol_req(card, ctmp); | ||
802 | else { | ||
803 | ctmp->fsm_state = ACT2000_STATE_NULL; | ||
804 | cmd.driver = card->myid; | ||
805 | cmd.command = ISDN_STAT_DHUP; | ||
806 | cmd.arg = chan; | ||
807 | card->interface.statcallb(&cmd); | ||
808 | } | ||
809 | break; | ||
810 | } | ||
811 | break; | ||
812 | case 0x8001: | ||
813 | /* SELECT_B3_PROTOCOL_CONF */ | ||
814 | chan = find_plci(card, msg->msg.select_b3_protocol_conf.plci); | ||
815 | if (chan >= 0) | ||
816 | switch (card->bch[chan].fsm_state) { | ||
817 | case ACT2000_STATE_ICALL: | ||
818 | case ACT2000_STATE_OWAIT: | ||
819 | ctmp = &card->bch[chan]; | ||
820 | if (msg->msg.select_b3_protocol_conf.info == 0) | ||
821 | actcapi_listen_b3_req(card, ctmp); | ||
822 | else { | ||
823 | ctmp->fsm_state = ACT2000_STATE_NULL; | ||
824 | cmd.driver = card->myid; | ||
825 | cmd.command = ISDN_STAT_DHUP; | ||
826 | cmd.arg = chan; | ||
827 | card->interface.statcallb(&cmd); | ||
828 | } | ||
829 | } | ||
830 | break; | ||
831 | case 0x8101: | ||
832 | /* LISTEN_B3_CONF */ | ||
833 | chan = find_plci(card, msg->msg.listen_b3_conf.plci); | ||
834 | if (chan >= 0) | ||
835 | switch (card->bch[chan].fsm_state) { | ||
836 | case ACT2000_STATE_ICALL: | ||
837 | ctmp = &card->bch[chan]; | ||
838 | if (msg->msg.listen_b3_conf.info == 0) | ||
839 | actcapi_connect_resp(card, ctmp, 0); | ||
840 | else { | ||
841 | ctmp->fsm_state = ACT2000_STATE_NULL; | ||
842 | cmd.driver = card->myid; | ||
843 | cmd.command = ISDN_STAT_DHUP; | ||
844 | cmd.arg = chan; | ||
845 | card->interface.statcallb(&cmd); | ||
846 | } | ||
847 | break; | ||
848 | case ACT2000_STATE_OWAIT: | ||
849 | ctmp = &card->bch[chan]; | ||
850 | if (msg->msg.listen_b3_conf.info == 0) { | ||
851 | actcapi_connect_b3_req(card, ctmp); | ||
852 | ctmp->fsm_state = ACT2000_STATE_OBWAIT; | ||
853 | cmd.driver = card->myid; | ||
854 | cmd.command = ISDN_STAT_DCONN; | ||
855 | cmd.arg = chan; | ||
856 | card->interface.statcallb(&cmd); | ||
857 | } else { | ||
858 | ctmp->fsm_state = ACT2000_STATE_NULL; | ||
859 | cmd.driver = card->myid; | ||
860 | cmd.command = ISDN_STAT_DHUP; | ||
861 | cmd.arg = chan; | ||
862 | card->interface.statcallb(&cmd); | ||
863 | } | ||
864 | break; | ||
865 | } | ||
866 | break; | ||
867 | case 0x8201: | ||
868 | /* CONNECT_B3_CONF */ | ||
869 | chan = find_plci(card, msg->msg.connect_b3_conf.plci); | ||
870 | if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_OBWAIT)) { | ||
871 | ctmp = &card->bch[chan]; | ||
872 | if (msg->msg.connect_b3_conf.info) { | ||
873 | ctmp->fsm_state = ACT2000_STATE_NULL; | ||
874 | cmd.driver = card->myid; | ||
875 | cmd.command = ISDN_STAT_DHUP; | ||
876 | cmd.arg = chan; | ||
877 | card->interface.statcallb(&cmd); | ||
878 | } else { | ||
879 | ctmp->ncci = msg->msg.connect_b3_conf.ncci; | ||
880 | ctmp->fsm_state = ACT2000_STATE_BWAIT; | ||
881 | } | ||
882 | } | ||
883 | break; | ||
884 | case 0x8401: | ||
885 | /* DISCONNECT_B3_CONF */ | ||
886 | chan = find_ncci(card, msg->msg.disconnect_b3_conf.ncci); | ||
887 | if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_BHWAIT)) | ||
888 | card->bch[chan].fsm_state = ACT2000_STATE_BHWAIT2; | ||
889 | break; | ||
890 | case 0x0702: | ||
891 | /* INFO_IND */ | ||
892 | chan = find_plci(card, msg->msg.info_ind.plci); | ||
893 | if (chan >= 0) | ||
894 | /* TODO: Eval Charging info / cause */ | ||
895 | actcapi_info_resp(card, &card->bch[chan]); | ||
896 | break; | ||
897 | case 0x0401: | ||
898 | /* LISTEN_CONF */ | ||
899 | case 0x0501: | ||
900 | /* LISTEN_CONF */ | ||
901 | case 0xff01: | ||
902 | /* MANUFACTURER_CONF */ | ||
903 | break; | ||
904 | case 0xff02: | ||
905 | /* MANUFACTURER_IND */ | ||
906 | if (msg->msg.manuf_msg == 3) { | ||
907 | memset(tmp, 0, sizeof(tmp)); | ||
908 | strncpy(tmp, | ||
909 | &msg->msg.manufacturer_ind_err.errstring, | ||
910 | msg->hdr.len - 16); | ||
911 | if (msg->msg.manufacturer_ind_err.errcode) | ||
912 | printk(KERN_WARNING "act2000: %s\n", tmp); | ||
913 | else { | ||
914 | printk(KERN_DEBUG "act2000: %s\n", tmp); | ||
915 | if ((!strncmp(tmp, "INFO: Trace buffer con", 22)) || | ||
916 | (!strncmp(tmp, "INFO: Compile Date/Tim", 22))) { | ||
917 | card->flags |= ACT2000_FLAGS_RUNNING; | ||
918 | cmd.command = ISDN_STAT_RUN; | ||
919 | cmd.driver = card->myid; | ||
920 | cmd.arg = 0; | ||
921 | actcapi_manufacturer_req_net(card); | ||
922 | actcapi_manufacturer_req_msn(card); | ||
923 | actcapi_listen_req(card); | ||
924 | card->interface.statcallb(&cmd); | ||
925 | } | ||
926 | } | ||
927 | } | ||
928 | break; | ||
929 | default: | ||
930 | printk(KERN_WARNING "act2000: UNHANDLED Message %04x\n", ccmd); | ||
931 | break; | ||
932 | } | ||
933 | dev_kfree_skb(skb); | ||
934 | } | ||
935 | } | ||
936 | |||
937 | #ifdef DEBUG_MSG | ||
938 | static void | ||
939 | actcapi_debug_caddr(actcapi_addr *addr) | ||
940 | { | ||
941 | char tmp[30]; | ||
942 | |||
943 | printk(KERN_DEBUG " Alen = %d\n", addr->len); | ||
944 | if (addr->len > 0) | ||
945 | printk(KERN_DEBUG " Atnp = 0x%02x\n", addr->tnp); | ||
946 | if (addr->len > 1) { | ||
947 | memset(tmp, 0, 30); | ||
948 | memcpy(tmp, addr->num, addr->len - 1); | ||
949 | printk(KERN_DEBUG " Anum = '%s'\n", tmp); | ||
950 | } | ||
951 | } | ||
952 | |||
953 | static void | ||
954 | actcapi_debug_ncpi(actcapi_ncpi *ncpi) | ||
955 | { | ||
956 | printk(KERN_DEBUG " ncpi.len = %d\n", ncpi->len); | ||
957 | if (ncpi->len >= 2) | ||
958 | printk(KERN_DEBUG " ncpi.lic = 0x%04x\n", ncpi->lic); | ||
959 | if (ncpi->len >= 4) | ||
960 | printk(KERN_DEBUG " ncpi.hic = 0x%04x\n", ncpi->hic); | ||
961 | if (ncpi->len >= 6) | ||
962 | printk(KERN_DEBUG " ncpi.ltc = 0x%04x\n", ncpi->ltc); | ||
963 | if (ncpi->len >= 8) | ||
964 | printk(KERN_DEBUG " ncpi.htc = 0x%04x\n", ncpi->htc); | ||
965 | if (ncpi->len >= 10) | ||
966 | printk(KERN_DEBUG " ncpi.loc = 0x%04x\n", ncpi->loc); | ||
967 | if (ncpi->len >= 12) | ||
968 | printk(KERN_DEBUG " ncpi.hoc = 0x%04x\n", ncpi->hoc); | ||
969 | if (ncpi->len >= 13) | ||
970 | printk(KERN_DEBUG " ncpi.mod = %d\n", ncpi->modulo); | ||
971 | } | ||
972 | |||
973 | static void | ||
974 | actcapi_debug_dlpd(actcapi_dlpd *dlpd) | ||
975 | { | ||
976 | printk(KERN_DEBUG " dlpd.len = %d\n", dlpd->len); | ||
977 | if (dlpd->len >= 2) | ||
978 | printk(KERN_DEBUG " dlpd.dlen = 0x%04x\n", dlpd->dlen); | ||
979 | if (dlpd->len >= 3) | ||
980 | printk(KERN_DEBUG " dlpd.laa = 0x%02x\n", dlpd->laa); | ||
981 | if (dlpd->len >= 4) | ||
982 | printk(KERN_DEBUG " dlpd.lab = 0x%02x\n", dlpd->lab); | ||
983 | if (dlpd->len >= 5) | ||
984 | printk(KERN_DEBUG " dlpd.modulo = %d\n", dlpd->modulo); | ||
985 | if (dlpd->len >= 6) | ||
986 | printk(KERN_DEBUG " dlpd.win = %d\n", dlpd->win); | ||
987 | } | ||
988 | |||
989 | #ifdef DEBUG_DUMP_SKB | ||
990 | static void dump_skb(struct sk_buff *skb) { | ||
991 | char tmp[80]; | ||
992 | char *p = skb->data; | ||
993 | char *t = tmp; | ||
994 | int i; | ||
995 | |||
996 | for (i = 0; i < skb->len; i++) { | ||
997 | t += sprintf(t, "%02x ", *p++ & 0xff); | ||
998 | if ((i & 0x0f) == 8) { | ||
999 | printk(KERN_DEBUG "dump: %s\n", tmp); | ||
1000 | t = tmp; | ||
1001 | } | ||
1002 | } | ||
1003 | if (i & 0x07) | ||
1004 | printk(KERN_DEBUG "dump: %s\n", tmp); | ||
1005 | } | ||
1006 | #endif | ||
1007 | |||
1008 | void | ||
1009 | actcapi_debug_msg(struct sk_buff *skb, int direction) | ||
1010 | { | ||
1011 | actcapi_msg *msg = (actcapi_msg *)skb->data; | ||
1012 | char *descr; | ||
1013 | int i; | ||
1014 | char tmp[170]; | ||
1015 | |||
1016 | #ifndef DEBUG_DATA_MSG | ||
1017 | if (msg->hdr.cmd.cmd == 0x86) | ||
1018 | return; | ||
1019 | #endif | ||
1020 | descr = "INVALID"; | ||
1021 | #ifdef DEBUG_DUMP_SKB | ||
1022 | dump_skb(skb); | ||
1023 | #endif | ||
1024 | for (i = 0; i < num_valid_msg; i++) | ||
1025 | if ((msg->hdr.cmd.cmd == valid_msg[i].cmd.cmd) && | ||
1026 | (msg->hdr.cmd.subcmd == valid_msg[i].cmd.subcmd)) { | ||
1027 | descr = valid_msg[i].description; | ||
1028 | break; | ||
1029 | } | ||
1030 | printk(KERN_DEBUG "%s %s msg\n", direction?"Outgoing":"Incoming", descr); | ||
1031 | printk(KERN_DEBUG " ApplID = %d\n", msg->hdr.applicationID); | ||
1032 | printk(KERN_DEBUG " Len = %d\n", msg->hdr.len); | ||
1033 | printk(KERN_DEBUG " MsgNum = 0x%04x\n", msg->hdr.msgnum); | ||
1034 | printk(KERN_DEBUG " Cmd = 0x%02x\n", msg->hdr.cmd.cmd); | ||
1035 | printk(KERN_DEBUG " SubCmd = 0x%02x\n", msg->hdr.cmd.subcmd); | ||
1036 | switch (i) { | ||
1037 | case 0: | ||
1038 | /* DATA B3 IND */ | ||
1039 | printk(KERN_DEBUG " BLOCK = 0x%02x\n", | ||
1040 | msg->msg.data_b3_ind.blocknr); | ||
1041 | break; | ||
1042 | case 2: | ||
1043 | /* CONNECT CONF */ | ||
1044 | printk(KERN_DEBUG " PLCI = 0x%04x\n", | ||
1045 | msg->msg.connect_conf.plci); | ||
1046 | printk(KERN_DEBUG " Info = 0x%04x\n", | ||
1047 | msg->msg.connect_conf.info); | ||
1048 | break; | ||
1049 | case 3: | ||
1050 | /* CONNECT IND */ | ||
1051 | printk(KERN_DEBUG " PLCI = 0x%04x\n", | ||
1052 | msg->msg.connect_ind.plci); | ||
1053 | printk(KERN_DEBUG " Contr = %d\n", | ||
1054 | msg->msg.connect_ind.controller); | ||
1055 | printk(KERN_DEBUG " SI1 = %d\n", | ||
1056 | msg->msg.connect_ind.si1); | ||
1057 | printk(KERN_DEBUG " SI2 = %d\n", | ||
1058 | msg->msg.connect_ind.si2); | ||
1059 | printk(KERN_DEBUG " EAZ = '%c'\n", | ||
1060 | msg->msg.connect_ind.eaz); | ||
1061 | actcapi_debug_caddr(&msg->msg.connect_ind.addr); | ||
1062 | break; | ||
1063 | case 5: | ||
1064 | /* CONNECT ACTIVE IND */ | ||
1065 | printk(KERN_DEBUG " PLCI = 0x%04x\n", | ||
1066 | msg->msg.connect_active_ind.plci); | ||
1067 | actcapi_debug_caddr(&msg->msg.connect_active_ind.addr); | ||
1068 | break; | ||
1069 | case 8: | ||
1070 | /* LISTEN CONF */ | ||
1071 | printk(KERN_DEBUG " Contr = %d\n", | ||
1072 | msg->msg.listen_conf.controller); | ||
1073 | printk(KERN_DEBUG " Info = 0x%04x\n", | ||
1074 | msg->msg.listen_conf.info); | ||
1075 | break; | ||
1076 | case 11: | ||
1077 | /* INFO IND */ | ||
1078 | printk(KERN_DEBUG " PLCI = 0x%04x\n", | ||
1079 | msg->msg.info_ind.plci); | ||
1080 | printk(KERN_DEBUG " Imsk = 0x%04x\n", | ||
1081 | msg->msg.info_ind.nr.mask); | ||
1082 | if (msg->hdr.len > 12) { | ||
1083 | int l = msg->hdr.len - 12; | ||
1084 | int j; | ||
1085 | char *p = tmp; | ||
1086 | for (j = 0; j < l ; j++) | ||
1087 | p += sprintf(p, "%02x ", msg->msg.info_ind.el.display[j]); | ||
1088 | printk(KERN_DEBUG " D = '%s'\n", tmp); | ||
1089 | } | ||
1090 | break; | ||
1091 | case 14: | ||
1092 | /* SELECT B2 PROTOCOL CONF */ | ||
1093 | printk(KERN_DEBUG " PLCI = 0x%04x\n", | ||
1094 | msg->msg.select_b2_protocol_conf.plci); | ||
1095 | printk(KERN_DEBUG " Info = 0x%04x\n", | ||
1096 | msg->msg.select_b2_protocol_conf.info); | ||
1097 | break; | ||
1098 | case 15: | ||
1099 | /* SELECT B3 PROTOCOL CONF */ | ||
1100 | printk(KERN_DEBUG " PLCI = 0x%04x\n", | ||
1101 | msg->msg.select_b3_protocol_conf.plci); | ||
1102 | printk(KERN_DEBUG " Info = 0x%04x\n", | ||
1103 | msg->msg.select_b3_protocol_conf.info); | ||
1104 | break; | ||
1105 | case 16: | ||
1106 | /* LISTEN B3 CONF */ | ||
1107 | printk(KERN_DEBUG " PLCI = 0x%04x\n", | ||
1108 | msg->msg.listen_b3_conf.plci); | ||
1109 | printk(KERN_DEBUG " Info = 0x%04x\n", | ||
1110 | msg->msg.listen_b3_conf.info); | ||
1111 | break; | ||
1112 | case 18: | ||
1113 | /* CONNECT B3 IND */ | ||
1114 | printk(KERN_DEBUG " NCCI = 0x%04x\n", | ||
1115 | msg->msg.connect_b3_ind.ncci); | ||
1116 | printk(KERN_DEBUG " PLCI = 0x%04x\n", | ||
1117 | msg->msg.connect_b3_ind.plci); | ||
1118 | actcapi_debug_ncpi(&msg->msg.connect_b3_ind.ncpi); | ||
1119 | break; | ||
1120 | case 19: | ||
1121 | /* CONNECT B3 ACTIVE IND */ | ||
1122 | printk(KERN_DEBUG " NCCI = 0x%04x\n", | ||
1123 | msg->msg.connect_b3_active_ind.ncci); | ||
1124 | actcapi_debug_ncpi(&msg->msg.connect_b3_active_ind.ncpi); | ||
1125 | break; | ||
1126 | case 26: | ||
1127 | /* MANUFACTURER IND */ | ||
1128 | printk(KERN_DEBUG " Mmsg = 0x%02x\n", | ||
1129 | msg->msg.manufacturer_ind_err.manuf_msg); | ||
1130 | switch (msg->msg.manufacturer_ind_err.manuf_msg) { | ||
1131 | case 3: | ||
1132 | printk(KERN_DEBUG " Contr = %d\n", | ||
1133 | msg->msg.manufacturer_ind_err.controller); | ||
1134 | printk(KERN_DEBUG " Code = 0x%08x\n", | ||
1135 | msg->msg.manufacturer_ind_err.errcode); | ||
1136 | memset(tmp, 0, sizeof(tmp)); | ||
1137 | strncpy(tmp, &msg->msg.manufacturer_ind_err.errstring, | ||
1138 | msg->hdr.len - 16); | ||
1139 | printk(KERN_DEBUG " Emsg = '%s'\n", tmp); | ||
1140 | break; | ||
1141 | } | ||
1142 | break; | ||
1143 | case 30: | ||
1144 | /* LISTEN REQ */ | ||
1145 | printk(KERN_DEBUG " Imsk = 0x%08x\n", | ||
1146 | msg->msg.listen_req.infomask); | ||
1147 | printk(KERN_DEBUG " Emsk = 0x%04x\n", | ||
1148 | msg->msg.listen_req.eazmask); | ||
1149 | printk(KERN_DEBUG " Smsk = 0x%04x\n", | ||
1150 | msg->msg.listen_req.simask); | ||
1151 | break; | ||
1152 | case 35: | ||
1153 | /* SELECT_B2_PROTOCOL_REQ */ | ||
1154 | printk(KERN_DEBUG " PLCI = 0x%04x\n", | ||
1155 | msg->msg.select_b2_protocol_req.plci); | ||
1156 | printk(KERN_DEBUG " prot = 0x%02x\n", | ||
1157 | msg->msg.select_b2_protocol_req.protocol); | ||
1158 | if (msg->hdr.len >= 11) | ||
1159 | printk(KERN_DEBUG "No dlpd\n"); | ||
1160 | else | ||
1161 | actcapi_debug_dlpd(&msg->msg.select_b2_protocol_req.dlpd); | ||
1162 | break; | ||
1163 | case 44: | ||
1164 | /* CONNECT RESP */ | ||
1165 | printk(KERN_DEBUG " PLCI = 0x%04x\n", | ||
1166 | msg->msg.connect_resp.plci); | ||
1167 | printk(KERN_DEBUG " CAUSE = 0x%02x\n", | ||
1168 | msg->msg.connect_resp.rejectcause); | ||
1169 | break; | ||
1170 | case 45: | ||
1171 | /* CONNECT ACTIVE RESP */ | ||
1172 | printk(KERN_DEBUG " PLCI = 0x%04x\n", | ||
1173 | msg->msg.connect_active_resp.plci); | ||
1174 | break; | ||
1175 | } | ||
1176 | } | ||
1177 | #endif | ||
diff --git a/drivers/isdn/act2000/capi.h b/drivers/isdn/act2000/capi.h new file mode 100644 index 000000000000..04d2bcdd37a7 --- /dev/null +++ b/drivers/isdn/act2000/capi.h | |||
@@ -0,0 +1,366 @@ | |||
1 | /* $Id: capi.h,v 1.6.6.2 2001/09/23 22:24:32 kai Exp $ | ||
2 | * | ||
3 | * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. | ||
4 | * | ||
5 | * Author Fritz Elfert | ||
6 | * Copyright by Fritz Elfert <fritz@isdn4linux.de> | ||
7 | * | ||
8 | * This software may be used and distributed according to the terms | ||
9 | * of the GNU General Public License, incorporated herein by reference. | ||
10 | * | ||
11 | * Thanks to Friedemann Baitinger and IBM Germany | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #ifndef CAPI_H | ||
16 | #define CAPI_H | ||
17 | |||
18 | /* Command-part of a CAPI message */ | ||
19 | typedef struct actcapi_msgcmd { | ||
20 | __u8 cmd; | ||
21 | __u8 subcmd; | ||
22 | } actcapi_msgcmd; | ||
23 | |||
24 | /* CAPI message header */ | ||
25 | typedef struct actcapi_msghdr { | ||
26 | __u16 len; | ||
27 | __u16 applicationID; | ||
28 | actcapi_msgcmd cmd; | ||
29 | __u16 msgnum; | ||
30 | } actcapi_msghdr; | ||
31 | |||
32 | /* CAPI message description (for debugging) */ | ||
33 | typedef struct actcapi_msgdsc { | ||
34 | actcapi_msgcmd cmd; | ||
35 | char *description; | ||
36 | } actcapi_msgdsc; | ||
37 | |||
38 | /* CAPI Address */ | ||
39 | typedef struct actcapi_addr { | ||
40 | __u8 len; /* Length of element */ | ||
41 | __u8 tnp; /* Type/Numbering Plan */ | ||
42 | __u8 num[20]; /* Caller ID */ | ||
43 | } actcapi_addr; | ||
44 | |||
45 | /* CAPI INFO element mask */ | ||
46 | typedef union actcapi_infonr { /* info number */ | ||
47 | __u16 mask; /* info-mask field */ | ||
48 | struct bmask { /* bit definitions */ | ||
49 | unsigned codes : 3; /* code set */ | ||
50 | unsigned rsvd : 5; /* reserved */ | ||
51 | unsigned svind : 1; /* single, variable length ind. */ | ||
52 | unsigned wtype : 7; /* W-element type */ | ||
53 | } bmask; | ||
54 | } actcapi_infonr; | ||
55 | |||
56 | /* CAPI INFO element */ | ||
57 | typedef union actcapi_infoel { /* info element */ | ||
58 | __u8 len; /* length of info element */ | ||
59 | __u8 display[40]; /* display contents */ | ||
60 | __u8 uuinfo[40]; /* User-user info field */ | ||
61 | struct cause { /* Cause information */ | ||
62 | unsigned ext2 : 1; /* extension */ | ||
63 | unsigned cod : 2; /* coding standard */ | ||
64 | unsigned spare : 1; /* spare */ | ||
65 | unsigned loc : 4; /* location */ | ||
66 | unsigned ext1 : 1; /* extension */ | ||
67 | unsigned cval : 7; /* Cause value */ | ||
68 | } cause; | ||
69 | struct charge { /* Charging information */ | ||
70 | __u8 toc; /* type of charging info */ | ||
71 | __u8 unit[10]; /* charging units */ | ||
72 | } charge; | ||
73 | __u8 date[20]; /* date fields */ | ||
74 | __u8 stat; /* state of remote party */ | ||
75 | } actcapi_infoel; | ||
76 | |||
77 | /* Message for EAZ<->MSN Mapping */ | ||
78 | typedef struct actcapi_msn { | ||
79 | __u8 eaz; | ||
80 | __u8 len; /* Length of MSN */ | ||
81 | __u8 msn[15] __attribute__ ((packed)); | ||
82 | } actcapi_msn; | ||
83 | |||
84 | typedef struct actcapi_dlpd { | ||
85 | __u8 len; /* Length of structure */ | ||
86 | __u16 dlen __attribute__ ((packed)); /* Data Length */ | ||
87 | __u8 laa __attribute__ ((packed)); /* Link Address A */ | ||
88 | __u8 lab; /* Link Address B */ | ||
89 | __u8 modulo; /* Modulo Mode */ | ||
90 | __u8 win; /* Window size */ | ||
91 | __u8 xid[100]; /* XID Information */ | ||
92 | } actcapi_dlpd; | ||
93 | |||
94 | typedef struct actcapi_ncpd { | ||
95 | __u8 len; /* Length of structure */ | ||
96 | __u16 lic __attribute__ ((packed)); | ||
97 | __u16 hic __attribute__ ((packed)); | ||
98 | __u16 ltc __attribute__ ((packed)); | ||
99 | __u16 htc __attribute__ ((packed)); | ||
100 | __u16 loc __attribute__ ((packed)); | ||
101 | __u16 hoc __attribute__ ((packed)); | ||
102 | __u8 modulo __attribute__ ((packed)); | ||
103 | } actcapi_ncpd; | ||
104 | #define actcapi_ncpi actcapi_ncpd | ||
105 | |||
106 | /* | ||
107 | * Layout of NCCI field in a B3 DATA CAPI message is different from | ||
108 | * standard at act2000: | ||
109 | * | ||
110 | * Bit 0-4 = PLCI | ||
111 | * Bit 5-7 = Controller | ||
112 | * Bit 8-15 = NCCI | ||
113 | */ | ||
114 | #define MAKE_NCCI(plci,contr,ncci) \ | ||
115 | ((plci & 0x1f) | ((contr & 0x7) << 5) | ((ncci & 0xff) << 8)) | ||
116 | |||
117 | #define EVAL_NCCI(fakencci,plci,contr,ncci) { \ | ||
118 | plci = fakencci & 0x1f; \ | ||
119 | contr = (fakencci >> 5) & 0x7; \ | ||
120 | ncci = (fakencci >> 8) & 0xff; \ | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * Layout of PLCI field in a B3 DATA CAPI message is different from | ||
125 | * standard at act2000: | ||
126 | * | ||
127 | * Bit 0-4 = PLCI | ||
128 | * Bit 5-7 = Controller | ||
129 | * Bit 8-15 = reserved (must be 0) | ||
130 | */ | ||
131 | #define MAKE_PLCI(plci,contr) \ | ||
132 | ((plci & 0x1f) | ((contr & 0x7) << 5)) | ||
133 | |||
134 | #define EVAL_PLCI(fakeplci,plci,contr) { \ | ||
135 | plci = fakeplci & 0x1f; \ | ||
136 | contr = (fakeplci >> 5) & 0x7; \ | ||
137 | } | ||
138 | |||
139 | typedef struct actcapi_msg { | ||
140 | actcapi_msghdr hdr; | ||
141 | union { | ||
142 | __u16 manuf_msg; | ||
143 | struct manufacturer_req_net { | ||
144 | __u16 manuf_msg; | ||
145 | __u16 controller; | ||
146 | __u8 nettype; | ||
147 | } manufacturer_req_net; | ||
148 | struct manufacturer_req_v42 { | ||
149 | __u16 manuf_msg; | ||
150 | __u16 controller; | ||
151 | __u32 v42control; | ||
152 | } manufacturer_req_v42; | ||
153 | struct manufacturer_conf_v42 { | ||
154 | __u16 manuf_msg; | ||
155 | __u16 controller; | ||
156 | } manufacturer_conf_v42; | ||
157 | struct manufacturer_req_err { | ||
158 | __u16 manuf_msg; | ||
159 | __u16 controller; | ||
160 | } manufacturer_req_err; | ||
161 | struct manufacturer_ind_err { | ||
162 | __u16 manuf_msg; | ||
163 | __u16 controller; | ||
164 | __u32 errcode; | ||
165 | __u8 errstring; /* actually up to 160 */ | ||
166 | } manufacturer_ind_err; | ||
167 | struct manufacturer_req_msn { | ||
168 | __u16 manuf_msg; | ||
169 | __u16 controller; | ||
170 | actcapi_msn msnmap; | ||
171 | } manufacturer_req_msn; | ||
172 | /* TODO: TraceInit-req/conf/ind/resp and | ||
173 | * TraceDump-req/conf/ind/resp | ||
174 | */ | ||
175 | struct connect_req { | ||
176 | __u8 controller; | ||
177 | __u8 bchan; | ||
178 | __u32 infomask __attribute__ ((packed)); | ||
179 | __u8 si1; | ||
180 | __u8 si2; | ||
181 | __u8 eaz; | ||
182 | actcapi_addr addr; | ||
183 | } connect_req; | ||
184 | struct connect_conf { | ||
185 | __u16 plci; | ||
186 | __u16 info; | ||
187 | } connect_conf; | ||
188 | struct connect_ind { | ||
189 | __u16 plci; | ||
190 | __u8 controller; | ||
191 | __u8 si1; | ||
192 | __u8 si2; | ||
193 | __u8 eaz; | ||
194 | actcapi_addr addr; | ||
195 | } connect_ind; | ||
196 | struct connect_resp { | ||
197 | __u16 plci; | ||
198 | __u8 rejectcause; | ||
199 | } connect_resp; | ||
200 | struct connect_active_ind { | ||
201 | __u16 plci; | ||
202 | actcapi_addr addr; | ||
203 | } connect_active_ind; | ||
204 | struct connect_active_resp { | ||
205 | __u16 plci; | ||
206 | } connect_active_resp; | ||
207 | struct connect_b3_req { | ||
208 | __u16 plci; | ||
209 | actcapi_ncpi ncpi; | ||
210 | } connect_b3_req; | ||
211 | struct connect_b3_conf { | ||
212 | __u16 plci; | ||
213 | __u16 ncci; | ||
214 | __u16 info; | ||
215 | } connect_b3_conf; | ||
216 | struct connect_b3_ind { | ||
217 | __u16 ncci; | ||
218 | __u16 plci; | ||
219 | actcapi_ncpi ncpi; | ||
220 | } connect_b3_ind; | ||
221 | struct connect_b3_resp { | ||
222 | __u16 ncci; | ||
223 | __u8 rejectcause; | ||
224 | actcapi_ncpi ncpi __attribute__ ((packed)); | ||
225 | } connect_b3_resp; | ||
226 | struct disconnect_req { | ||
227 | __u16 plci; | ||
228 | __u8 cause; | ||
229 | } disconnect_req; | ||
230 | struct disconnect_conf { | ||
231 | __u16 plci; | ||
232 | __u16 info; | ||
233 | } disconnect_conf; | ||
234 | struct disconnect_ind { | ||
235 | __u16 plci; | ||
236 | __u16 info; | ||
237 | } disconnect_ind; | ||
238 | struct disconnect_resp { | ||
239 | __u16 plci; | ||
240 | } disconnect_resp; | ||
241 | struct connect_b3_active_ind { | ||
242 | __u16 ncci; | ||
243 | actcapi_ncpi ncpi; | ||
244 | } connect_b3_active_ind; | ||
245 | struct connect_b3_active_resp { | ||
246 | __u16 ncci; | ||
247 | } connect_b3_active_resp; | ||
248 | struct disconnect_b3_req { | ||
249 | __u16 ncci; | ||
250 | actcapi_ncpi ncpi; | ||
251 | } disconnect_b3_req; | ||
252 | struct disconnect_b3_conf { | ||
253 | __u16 ncci; | ||
254 | __u16 info; | ||
255 | } disconnect_b3_conf; | ||
256 | struct disconnect_b3_ind { | ||
257 | __u16 ncci; | ||
258 | __u16 info; | ||
259 | actcapi_ncpi ncpi; | ||
260 | } disconnect_b3_ind; | ||
261 | struct disconnect_b3_resp { | ||
262 | __u16 ncci; | ||
263 | } disconnect_b3_resp; | ||
264 | struct info_ind { | ||
265 | __u16 plci; | ||
266 | actcapi_infonr nr; | ||
267 | actcapi_infoel el; | ||
268 | } info_ind; | ||
269 | struct info_resp { | ||
270 | __u16 plci; | ||
271 | } info_resp; | ||
272 | struct listen_b3_req { | ||
273 | __u16 plci; | ||
274 | } listen_b3_req; | ||
275 | struct listen_b3_conf { | ||
276 | __u16 plci; | ||
277 | __u16 info; | ||
278 | } listen_b3_conf; | ||
279 | struct select_b2_protocol_req { | ||
280 | __u16 plci; | ||
281 | __u8 protocol; | ||
282 | actcapi_dlpd dlpd __attribute__ ((packed)); | ||
283 | } select_b2_protocol_req; | ||
284 | struct select_b2_protocol_conf { | ||
285 | __u16 plci; | ||
286 | __u16 info; | ||
287 | } select_b2_protocol_conf; | ||
288 | struct select_b3_protocol_req { | ||
289 | __u16 plci; | ||
290 | __u8 protocol; | ||
291 | actcapi_ncpd ncpd __attribute__ ((packed)); | ||
292 | } select_b3_protocol_req; | ||
293 | struct select_b3_protocol_conf { | ||
294 | __u16 plci; | ||
295 | __u16 info; | ||
296 | } select_b3_protocol_conf; | ||
297 | struct listen_req { | ||
298 | __u8 controller; | ||
299 | __u32 infomask __attribute__ ((packed)); | ||
300 | __u16 eazmask __attribute__ ((packed)); | ||
301 | __u16 simask __attribute__ ((packed)); | ||
302 | } listen_req; | ||
303 | struct listen_conf { | ||
304 | __u8 controller; | ||
305 | __u16 info __attribute__ ((packed)); | ||
306 | } listen_conf; | ||
307 | struct data_b3_req { | ||
308 | __u16 fakencci; | ||
309 | __u16 datalen; | ||
310 | __u32 unused; | ||
311 | __u8 blocknr; | ||
312 | __u16 flags __attribute__ ((packed)); | ||
313 | } data_b3_req; | ||
314 | struct data_b3_ind { | ||
315 | __u16 fakencci; | ||
316 | __u16 datalen; | ||
317 | __u32 unused; | ||
318 | __u8 blocknr; | ||
319 | __u16 flags __attribute__ ((packed)); | ||
320 | } data_b3_ind; | ||
321 | struct data_b3_resp { | ||
322 | __u16 ncci; | ||
323 | __u8 blocknr; | ||
324 | } data_b3_resp; | ||
325 | struct data_b3_conf { | ||
326 | __u16 ncci; | ||
327 | __u8 blocknr; | ||
328 | __u16 info __attribute__ ((packed)); | ||
329 | } data_b3_conf; | ||
330 | } msg; | ||
331 | } actcapi_msg; | ||
332 | |||
333 | extern __inline__ unsigned short | ||
334 | actcapi_nextsmsg(act2000_card *card) | ||
335 | { | ||
336 | unsigned long flags; | ||
337 | unsigned short n; | ||
338 | |||
339 | spin_lock_irqsave(&card->mnlock, flags); | ||
340 | n = card->msgnum; | ||
341 | card->msgnum++; | ||
342 | card->msgnum &= 0x7fff; | ||
343 | spin_unlock_irqrestore(&card->mnlock, flags); | ||
344 | return n; | ||
345 | } | ||
346 | #define DEBUG_MSG | ||
347 | #undef DEBUG_DATA_MSG | ||
348 | #undef DEBUG_DUMP_SKB | ||
349 | |||
350 | extern int actcapi_chkhdr(act2000_card *, actcapi_msghdr *); | ||
351 | extern int actcapi_listen_req(act2000_card *); | ||
352 | extern int actcapi_manufacturer_req_net(act2000_card *); | ||
353 | extern int actcapi_manufacturer_req_v42(act2000_card *, ulong); | ||
354 | extern int actcapi_manufacturer_req_errh(act2000_card *); | ||
355 | extern int actcapi_manufacturer_req_msn(act2000_card *); | ||
356 | extern int actcapi_connect_req(act2000_card *, act2000_chan *, char *, char, int, int); | ||
357 | extern void actcapi_select_b2_protocol_req(act2000_card *, act2000_chan *); | ||
358 | extern void actcapi_disconnect_b3_req(act2000_card *, act2000_chan *); | ||
359 | extern void actcapi_connect_resp(act2000_card *, act2000_chan *, __u8); | ||
360 | extern void actcapi_dispatch(act2000_card *); | ||
361 | #ifdef DEBUG_MSG | ||
362 | extern void actcapi_debug_msg(struct sk_buff *skb, int); | ||
363 | #else | ||
364 | #define actcapi_debug_msg(skb, len) | ||
365 | #endif | ||
366 | #endif | ||
diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c new file mode 100644 index 000000000000..d89dcde4eade --- /dev/null +++ b/drivers/isdn/act2000/module.c | |||
@@ -0,0 +1,808 @@ | |||
1 | /* $Id: module.c,v 1.14.6.4 2001/09/23 22:24:32 kai Exp $ | ||
2 | * | ||
3 | * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000. | ||
4 | * | ||
5 | * Author Fritz Elfert | ||
6 | * Copyright by Fritz Elfert <fritz@isdn4linux.de> | ||
7 | * | ||
8 | * This software may be used and distributed according to the terms | ||
9 | * of the GNU General Public License, incorporated herein by reference. | ||
10 | * | ||
11 | * Thanks to Friedemann Baitinger and IBM Germany | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include "act2000.h" | ||
16 | #include "act2000_isa.h" | ||
17 | #include "capi.h" | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/init.h> | ||
20 | |||
21 | static unsigned short act2000_isa_ports[] = | ||
22 | { | ||
23 | 0x0200, 0x0240, 0x0280, 0x02c0, 0x0300, 0x0340, 0x0380, | ||
24 | 0xcfe0, 0xcfa0, 0xcf60, 0xcf20, 0xcee0, 0xcea0, 0xce60, | ||
25 | }; | ||
26 | #define ISA_NRPORTS (sizeof(act2000_isa_ports)/sizeof(unsigned short)) | ||
27 | |||
28 | static act2000_card *cards = (act2000_card *) NULL; | ||
29 | |||
30 | /* Parameters to be set by insmod */ | ||
31 | static int act_bus = 0; | ||
32 | static int act_port = -1; /* -1 = Autoprobe */ | ||
33 | static int act_irq = -1; | ||
34 | static char *act_id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; | ||
35 | |||
36 | MODULE_DESCRIPTION( "ISDN4Linux: Driver for IBM Active 2000 ISDN card"); | ||
37 | MODULE_AUTHOR( "Fritz Elfert"); | ||
38 | MODULE_LICENSE( "GPL"); | ||
39 | MODULE_PARM_DESC(act_bus, "BusType of first card, 1=ISA, 2=MCA, 3=PCMCIA, currently only ISA"); | ||
40 | MODULE_PARM_DESC(membase, "Base port address of first card"); | ||
41 | MODULE_PARM_DESC(act_irq, "IRQ of first card"); | ||
42 | MODULE_PARM_DESC(act_id, "ID-String of first card"); | ||
43 | module_param(act_bus, int, 0); | ||
44 | module_param(act_port, int, 0); | ||
45 | module_param(act_irq, int, 0); | ||
46 | module_param(act_id, charp, 0); | ||
47 | |||
48 | static int act2000_addcard(int, int, int, char *); | ||
49 | |||
50 | static act2000_chan * | ||
51 | find_channel(act2000_card *card, int channel) | ||
52 | { | ||
53 | if ((channel >= 0) && (channel < ACT2000_BCH)) | ||
54 | return &(card->bch[channel]); | ||
55 | printk(KERN_WARNING "act2000: Invalid channel %d\n", channel); | ||
56 | return NULL; | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * Free MSN list | ||
61 | */ | ||
62 | static void | ||
63 | act2000_clear_msn(act2000_card *card) | ||
64 | { | ||
65 | struct msn_entry *p = card->msn_list; | ||
66 | struct msn_entry *q; | ||
67 | unsigned long flags; | ||
68 | |||
69 | spin_lock_irqsave(&card->lock, flags); | ||
70 | card->msn_list = NULL; | ||
71 | spin_unlock_irqrestore(&card->lock, flags); | ||
72 | while (p) { | ||
73 | q = p->next; | ||
74 | kfree(p); | ||
75 | p = q; | ||
76 | } | ||
77 | } | ||
78 | |||
79 | /* | ||
80 | * Find an MSN entry in the list. | ||
81 | * If ia5 != 0, return IA5-encoded EAZ, else | ||
82 | * return a bitmask with corresponding bit set. | ||
83 | */ | ||
84 | static __u16 | ||
85 | act2000_find_msn(act2000_card *card, char *msn, int ia5) | ||
86 | { | ||
87 | struct msn_entry *p = card->msn_list; | ||
88 | __u8 eaz = '0'; | ||
89 | |||
90 | while (p) { | ||
91 | if (!strcmp(p->msn, msn)) { | ||
92 | eaz = p->eaz; | ||
93 | break; | ||
94 | } | ||
95 | p = p->next; | ||
96 | } | ||
97 | if (!ia5) | ||
98 | return (1 << (eaz - '0')); | ||
99 | else | ||
100 | return eaz; | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | * Find an EAZ entry in the list. | ||
105 | * return a string with corresponding msn. | ||
106 | */ | ||
107 | char * | ||
108 | act2000_find_eaz(act2000_card *card, char eaz) | ||
109 | { | ||
110 | struct msn_entry *p = card->msn_list; | ||
111 | |||
112 | while (p) { | ||
113 | if (p->eaz == eaz) | ||
114 | return(p->msn); | ||
115 | p = p->next; | ||
116 | } | ||
117 | return("\0"); | ||
118 | } | ||
119 | |||
120 | /* | ||
121 | * Add or delete an MSN to the MSN list | ||
122 | * | ||
123 | * First character of msneaz is EAZ, rest is MSN. | ||
124 | * If length of eazmsn is 1, delete that entry. | ||
125 | */ | ||
126 | static int | ||
127 | act2000_set_msn(act2000_card *card, char *eazmsn) | ||
128 | { | ||
129 | struct msn_entry *p = card->msn_list; | ||
130 | struct msn_entry *q = NULL; | ||
131 | unsigned long flags; | ||
132 | int i; | ||
133 | |||
134 | if (!strlen(eazmsn)) | ||
135 | return 0; | ||
136 | if (strlen(eazmsn) > 16) | ||
137 | return -EINVAL; | ||
138 | for (i = 0; i < strlen(eazmsn); i++) | ||
139 | if (!isdigit(eazmsn[i])) | ||
140 | return -EINVAL; | ||
141 | if (strlen(eazmsn) == 1) { | ||
142 | /* Delete a single MSN */ | ||
143 | while (p) { | ||
144 | if (p->eaz == eazmsn[0]) { | ||
145 | spin_lock_irqsave(&card->lock, flags); | ||
146 | if (q) | ||
147 | q->next = p->next; | ||
148 | else | ||
149 | card->msn_list = p->next; | ||
150 | spin_unlock_irqrestore(&card->lock, flags); | ||
151 | kfree(p); | ||
152 | printk(KERN_DEBUG | ||
153 | "Mapping for EAZ %c deleted\n", | ||
154 | eazmsn[0]); | ||
155 | return 0; | ||
156 | } | ||
157 | q = p; | ||
158 | p = p->next; | ||
159 | } | ||
160 | return 0; | ||
161 | } | ||
162 | /* Add a single MSN */ | ||
163 | while (p) { | ||
164 | /* Found in list, replace MSN */ | ||
165 | if (p->eaz == eazmsn[0]) { | ||
166 | spin_lock_irqsave(&card->lock, flags); | ||
167 | strcpy(p->msn, &eazmsn[1]); | ||
168 | spin_unlock_irqrestore(&card->lock, flags); | ||
169 | printk(KERN_DEBUG | ||
170 | "Mapping for EAZ %c changed to %s\n", | ||
171 | eazmsn[0], | ||
172 | &eazmsn[1]); | ||
173 | return 0; | ||
174 | } | ||
175 | p = p->next; | ||
176 | } | ||
177 | /* Not found in list, add new entry */ | ||
178 | p = kmalloc(sizeof(msn_entry), GFP_KERNEL); | ||
179 | if (!p) | ||
180 | return -ENOMEM; | ||
181 | p->eaz = eazmsn[0]; | ||
182 | strcpy(p->msn, &eazmsn[1]); | ||
183 | p->next = card->msn_list; | ||
184 | spin_lock_irqsave(&card->lock, flags); | ||
185 | card->msn_list = p; | ||
186 | spin_unlock_irqrestore(&card->lock, flags); | ||
187 | printk(KERN_DEBUG | ||
188 | "Mapping %c -> %s added\n", | ||
189 | eazmsn[0], | ||
190 | &eazmsn[1]); | ||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static void | ||
195 | act2000_transmit(struct act2000_card *card) | ||
196 | { | ||
197 | switch (card->bus) { | ||
198 | case ACT2000_BUS_ISA: | ||
199 | act2000_isa_send(card); | ||
200 | break; | ||
201 | case ACT2000_BUS_PCMCIA: | ||
202 | case ACT2000_BUS_MCA: | ||
203 | default: | ||
204 | printk(KERN_WARNING | ||
205 | "act2000_transmit: Illegal bustype %d\n", card->bus); | ||
206 | } | ||
207 | } | ||
208 | |||
209 | static void | ||
210 | act2000_receive(struct act2000_card *card) | ||
211 | { | ||
212 | switch (card->bus) { | ||
213 | case ACT2000_BUS_ISA: | ||
214 | act2000_isa_receive(card); | ||
215 | break; | ||
216 | case ACT2000_BUS_PCMCIA: | ||
217 | case ACT2000_BUS_MCA: | ||
218 | default: | ||
219 | printk(KERN_WARNING | ||
220 | "act2000_receive: Illegal bustype %d\n", card->bus); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | static void | ||
225 | act2000_poll(unsigned long data) | ||
226 | { | ||
227 | act2000_card * card = (act2000_card *)data; | ||
228 | unsigned long flags; | ||
229 | |||
230 | act2000_receive(card); | ||
231 | spin_lock_irqsave(&card->lock, flags); | ||
232 | mod_timer(&card->ptimer, jiffies+3); | ||
233 | spin_unlock_irqrestore(&card->lock, flags); | ||
234 | } | ||
235 | |||
236 | static int | ||
237 | act2000_command(act2000_card * card, isdn_ctrl * c) | ||
238 | { | ||
239 | ulong a; | ||
240 | act2000_chan *chan; | ||
241 | act2000_cdef cdef; | ||
242 | isdn_ctrl cmd; | ||
243 | char tmp[17]; | ||
244 | int ret; | ||
245 | unsigned long flags; | ||
246 | void __user *arg; | ||
247 | |||
248 | switch (c->command) { | ||
249 | case ISDN_CMD_IOCTL: | ||
250 | memcpy(&a, c->parm.num, sizeof(ulong)); | ||
251 | arg = (void __user *)a; | ||
252 | switch (c->arg) { | ||
253 | case ACT2000_IOCTL_LOADBOOT: | ||
254 | switch (card->bus) { | ||
255 | case ACT2000_BUS_ISA: | ||
256 | ret = act2000_isa_download(card, | ||
257 | arg); | ||
258 | if (!ret) { | ||
259 | card->flags |= ACT2000_FLAGS_LOADED; | ||
260 | if (!(card->flags & ACT2000_FLAGS_IVALID)) { | ||
261 | card->ptimer.expires = jiffies + 3; | ||
262 | card->ptimer.function = act2000_poll; | ||
263 | card->ptimer.data = (unsigned long)card; | ||
264 | add_timer(&card->ptimer); | ||
265 | } | ||
266 | actcapi_manufacturer_req_errh(card); | ||
267 | } | ||
268 | break; | ||
269 | default: | ||
270 | printk(KERN_WARNING | ||
271 | "act2000: Illegal BUS type %d\n", | ||
272 | card->bus); | ||
273 | ret = -EIO; | ||
274 | } | ||
275 | return ret; | ||
276 | case ACT2000_IOCTL_SETPROTO: | ||
277 | card->ptype = a?ISDN_PTYPE_EURO:ISDN_PTYPE_1TR6; | ||
278 | if (!(card->flags & ACT2000_FLAGS_RUNNING)) | ||
279 | return 0; | ||
280 | actcapi_manufacturer_req_net(card); | ||
281 | return 0; | ||
282 | case ACT2000_IOCTL_SETMSN: | ||
283 | if (copy_from_user(tmp, arg, | ||
284 | sizeof(tmp))) | ||
285 | return -EFAULT; | ||
286 | if ((ret = act2000_set_msn(card, tmp))) | ||
287 | return ret; | ||
288 | if (card->flags & ACT2000_FLAGS_RUNNING) | ||
289 | return(actcapi_manufacturer_req_msn(card)); | ||
290 | return 0; | ||
291 | case ACT2000_IOCTL_ADDCARD: | ||
292 | if (copy_from_user(&cdef, arg, | ||
293 | sizeof(cdef))) | ||
294 | return -EFAULT; | ||
295 | if (act2000_addcard(cdef.bus, cdef.port, cdef.irq, cdef.id)) | ||
296 | return -EIO; | ||
297 | return 0; | ||
298 | case ACT2000_IOCTL_TEST: | ||
299 | if (!(card->flags & ACT2000_FLAGS_RUNNING)) | ||
300 | return -ENODEV; | ||
301 | return 0; | ||
302 | default: | ||
303 | return -EINVAL; | ||
304 | } | ||
305 | break; | ||
306 | case ISDN_CMD_DIAL: | ||
307 | if (!card->flags & ACT2000_FLAGS_RUNNING) | ||
308 | return -ENODEV; | ||
309 | if (!(chan = find_channel(card, c->arg & 0x0f))) | ||
310 | break; | ||
311 | spin_lock_irqsave(&card->lock, flags); | ||
312 | if (chan->fsm_state != ACT2000_STATE_NULL) { | ||
313 | spin_unlock_irqrestore(&card->lock, flags); | ||
314 | printk(KERN_WARNING "Dial on channel with state %d\n", | ||
315 | chan->fsm_state); | ||
316 | return -EBUSY; | ||
317 | } | ||
318 | if (card->ptype == ISDN_PTYPE_EURO) | ||
319 | tmp[0] = act2000_find_msn(card, c->parm.setup.eazmsn, 1); | ||
320 | else | ||
321 | tmp[0] = c->parm.setup.eazmsn[0]; | ||
322 | chan->fsm_state = ACT2000_STATE_OCALL; | ||
323 | chan->callref = 0xffff; | ||
324 | spin_unlock_irqrestore(&card->lock, flags); | ||
325 | ret = actcapi_connect_req(card, chan, c->parm.setup.phone, | ||
326 | tmp[0], c->parm.setup.si1, | ||
327 | c->parm.setup.si2); | ||
328 | if (ret) { | ||
329 | cmd.driver = card->myid; | ||
330 | cmd.command = ISDN_STAT_DHUP; | ||
331 | cmd.arg &= 0x0f; | ||
332 | card->interface.statcallb(&cmd); | ||
333 | } | ||
334 | return ret; | ||
335 | case ISDN_CMD_ACCEPTD: | ||
336 | if (!card->flags & ACT2000_FLAGS_RUNNING) | ||
337 | return -ENODEV; | ||
338 | if (!(chan = find_channel(card, c->arg & 0x0f))) | ||
339 | break; | ||
340 | if (chan->fsm_state == ACT2000_STATE_ICALL) | ||
341 | actcapi_select_b2_protocol_req(card, chan); | ||
342 | return 0; | ||
343 | case ISDN_CMD_ACCEPTB: | ||
344 | if (!card->flags & ACT2000_FLAGS_RUNNING) | ||
345 | return -ENODEV; | ||
346 | return 0; | ||
347 | case ISDN_CMD_HANGUP: | ||
348 | if (!card->flags & ACT2000_FLAGS_RUNNING) | ||
349 | return -ENODEV; | ||
350 | if (!(chan = find_channel(card, c->arg & 0x0f))) | ||
351 | break; | ||
352 | switch (chan->fsm_state) { | ||
353 | case ACT2000_STATE_ICALL: | ||
354 | case ACT2000_STATE_BSETUP: | ||
355 | actcapi_connect_resp(card, chan, 0x15); | ||
356 | break; | ||
357 | case ACT2000_STATE_ACTIVE: | ||
358 | actcapi_disconnect_b3_req(card, chan); | ||
359 | break; | ||
360 | } | ||
361 | return 0; | ||
362 | case ISDN_CMD_SETEAZ: | ||
363 | if (!card->flags & ACT2000_FLAGS_RUNNING) | ||
364 | return -ENODEV; | ||
365 | if (!(chan = find_channel(card, c->arg & 0x0f))) | ||
366 | break; | ||
367 | if (strlen(c->parm.num)) { | ||
368 | if (card->ptype == ISDN_PTYPE_EURO) { | ||
369 | chan->eazmask = act2000_find_msn(card, c->parm.num, 0); | ||
370 | } | ||
371 | if (card->ptype == ISDN_PTYPE_1TR6) { | ||
372 | int i; | ||
373 | chan->eazmask = 0; | ||
374 | for (i = 0; i < strlen(c->parm.num); i++) | ||
375 | if (isdigit(c->parm.num[i])) | ||
376 | chan->eazmask |= (1 << (c->parm.num[i] - '0')); | ||
377 | } | ||
378 | } else | ||
379 | chan->eazmask = 0x3ff; | ||
380 | actcapi_listen_req(card); | ||
381 | return 0; | ||
382 | case ISDN_CMD_CLREAZ: | ||
383 | if (!card->flags & ACT2000_FLAGS_RUNNING) | ||
384 | return -ENODEV; | ||
385 | if (!(chan = find_channel(card, c->arg & 0x0f))) | ||
386 | break; | ||
387 | chan->eazmask = 0; | ||
388 | actcapi_listen_req(card); | ||
389 | return 0; | ||
390 | case ISDN_CMD_SETL2: | ||
391 | if (!card->flags & ACT2000_FLAGS_RUNNING) | ||
392 | return -ENODEV; | ||
393 | if (!(chan = find_channel(card, c->arg & 0x0f))) | ||
394 | break; | ||
395 | chan->l2prot = (c->arg >> 8); | ||
396 | return 0; | ||
397 | case ISDN_CMD_SETL3: | ||
398 | if (!card->flags & ACT2000_FLAGS_RUNNING) | ||
399 | return -ENODEV; | ||
400 | if ((c->arg >> 8) != ISDN_PROTO_L3_TRANS) { | ||
401 | printk(KERN_WARNING "L3 protocol unknown\n"); | ||
402 | return -1; | ||
403 | } | ||
404 | if (!(chan = find_channel(card, c->arg & 0x0f))) | ||
405 | break; | ||
406 | chan->l3prot = (c->arg >> 8); | ||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | return -EINVAL; | ||
411 | } | ||
412 | |||
413 | static int | ||
414 | act2000_sendbuf(act2000_card *card, int channel, int ack, struct sk_buff *skb) | ||
415 | { | ||
416 | struct sk_buff *xmit_skb; | ||
417 | int len; | ||
418 | act2000_chan *chan; | ||
419 | actcapi_msg *msg; | ||
420 | |||
421 | if (!(chan = find_channel(card, channel))) | ||
422 | return -1; | ||
423 | if (chan->fsm_state != ACT2000_STATE_ACTIVE) | ||
424 | return -1; | ||
425 | len = skb->len; | ||
426 | if ((chan->queued + len) >= ACT2000_MAX_QUEUED) | ||
427 | return 0; | ||
428 | if (!len) | ||
429 | return 0; | ||
430 | if (skb_headroom(skb) < 19) { | ||
431 | printk(KERN_WARNING "act2000_sendbuf: Headroom only %d\n", | ||
432 | skb_headroom(skb)); | ||
433 | xmit_skb = alloc_skb(len + 19, GFP_ATOMIC); | ||
434 | if (!xmit_skb) { | ||
435 | printk(KERN_WARNING "act2000_sendbuf: Out of memory\n"); | ||
436 | return 0; | ||
437 | } | ||
438 | skb_reserve(xmit_skb, 19); | ||
439 | memcpy(skb_put(xmit_skb, len), skb->data, len); | ||
440 | } else { | ||
441 | xmit_skb = skb_clone(skb, GFP_ATOMIC); | ||
442 | if (!xmit_skb) { | ||
443 | printk(KERN_WARNING "act2000_sendbuf: Out of memory\n"); | ||
444 | return 0; | ||
445 | } | ||
446 | } | ||
447 | dev_kfree_skb(skb); | ||
448 | msg = (actcapi_msg *)skb_push(xmit_skb, 19); | ||
449 | msg->hdr.len = 19 + len; | ||
450 | msg->hdr.applicationID = 1; | ||
451 | msg->hdr.cmd.cmd = 0x86; | ||
452 | msg->hdr.cmd.subcmd = 0x00; | ||
453 | msg->hdr.msgnum = actcapi_nextsmsg(card); | ||
454 | msg->msg.data_b3_req.datalen = len; | ||
455 | msg->msg.data_b3_req.blocknr = (msg->hdr.msgnum & 0xff); | ||
456 | msg->msg.data_b3_req.fakencci = MAKE_NCCI(chan->plci, 0, chan->ncci); | ||
457 | msg->msg.data_b3_req.flags = ack; /* Will be set to 0 on actual sending */ | ||
458 | actcapi_debug_msg(xmit_skb, 1); | ||
459 | chan->queued += len; | ||
460 | skb_queue_tail(&card->sndq, xmit_skb); | ||
461 | act2000_schedule_tx(card); | ||
462 | return len; | ||
463 | } | ||
464 | |||
465 | |||
466 | /* Read the Status-replies from the Interface */ | ||
467 | static int | ||
468 | act2000_readstatus(u_char __user * buf, int len, act2000_card * card) | ||
469 | { | ||
470 | int count; | ||
471 | u_char __user *p; | ||
472 | |||
473 | for (p = buf, count = 0; count < len; p++, count++) { | ||
474 | if (card->status_buf_read == card->status_buf_write) | ||
475 | return count; | ||
476 | put_user(*card->status_buf_read++, p); | ||
477 | if (card->status_buf_read > card->status_buf_end) | ||
478 | card->status_buf_read = card->status_buf; | ||
479 | } | ||
480 | return count; | ||
481 | } | ||
482 | |||
483 | /* | ||
484 | * Find card with given driverId | ||
485 | */ | ||
486 | static inline act2000_card * | ||
487 | act2000_findcard(int driverid) | ||
488 | { | ||
489 | act2000_card *p = cards; | ||
490 | |||
491 | while (p) { | ||
492 | if (p->myid == driverid) | ||
493 | return p; | ||
494 | p = p->next; | ||
495 | } | ||
496 | return (act2000_card *) 0; | ||
497 | } | ||
498 | |||
499 | /* | ||
500 | * Wrapper functions for interface to linklevel | ||
501 | */ | ||
502 | static int | ||
503 | if_command(isdn_ctrl * c) | ||
504 | { | ||
505 | act2000_card *card = act2000_findcard(c->driver); | ||
506 | |||
507 | if (card) | ||
508 | return (act2000_command(card, c)); | ||
509 | printk(KERN_ERR | ||
510 | "act2000: if_command %d called with invalid driverId %d!\n", | ||
511 | c->command, c->driver); | ||
512 | return -ENODEV; | ||
513 | } | ||
514 | |||
515 | static int | ||
516 | if_writecmd(const u_char __user *buf, int len, int id, int channel) | ||
517 | { | ||
518 | act2000_card *card = act2000_findcard(id); | ||
519 | |||
520 | if (card) { | ||
521 | if (!card->flags & ACT2000_FLAGS_RUNNING) | ||
522 | return -ENODEV; | ||
523 | return (len); | ||
524 | } | ||
525 | printk(KERN_ERR | ||
526 | "act2000: if_writecmd called with invalid driverId!\n"); | ||
527 | return -ENODEV; | ||
528 | } | ||
529 | |||
530 | static int | ||
531 | if_readstatus(u_char __user * buf, int len, int id, int channel) | ||
532 | { | ||
533 | act2000_card *card = act2000_findcard(id); | ||
534 | |||
535 | if (card) { | ||
536 | if (!card->flags & ACT2000_FLAGS_RUNNING) | ||
537 | return -ENODEV; | ||
538 | return (act2000_readstatus(buf, len, card)); | ||
539 | } | ||
540 | printk(KERN_ERR | ||
541 | "act2000: if_readstatus called with invalid driverId!\n"); | ||
542 | return -ENODEV; | ||
543 | } | ||
544 | |||
545 | static int | ||
546 | if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) | ||
547 | { | ||
548 | act2000_card *card = act2000_findcard(id); | ||
549 | |||
550 | if (card) { | ||
551 | if (!card->flags & ACT2000_FLAGS_RUNNING) | ||
552 | return -ENODEV; | ||
553 | return (act2000_sendbuf(card, channel, ack, skb)); | ||
554 | } | ||
555 | printk(KERN_ERR | ||
556 | "act2000: if_sendbuf called with invalid driverId!\n"); | ||
557 | return -ENODEV; | ||
558 | } | ||
559 | |||
560 | |||
561 | /* | ||
562 | * Allocate a new card-struct, initialize it | ||
563 | * link it into cards-list. | ||
564 | */ | ||
565 | static void | ||
566 | act2000_alloccard(int bus, int port, int irq, char *id) | ||
567 | { | ||
568 | int i; | ||
569 | act2000_card *card; | ||
570 | if (!(card = (act2000_card *) kmalloc(sizeof(act2000_card), GFP_KERNEL))) { | ||
571 | printk(KERN_WARNING | ||
572 | "act2000: (%s) Could not allocate card-struct.\n", id); | ||
573 | return; | ||
574 | } | ||
575 | memset((char *) card, 0, sizeof(act2000_card)); | ||
576 | spin_lock_init(&card->lock); | ||
577 | spin_lock_init(&card->mnlock); | ||
578 | skb_queue_head_init(&card->sndq); | ||
579 | skb_queue_head_init(&card->rcvq); | ||
580 | skb_queue_head_init(&card->ackq); | ||
581 | INIT_WORK(&card->snd_tq, (void *) (void *) act2000_transmit, card); | ||
582 | INIT_WORK(&card->rcv_tq, (void *) (void *) actcapi_dispatch, card); | ||
583 | INIT_WORK(&card->poll_tq, (void *) (void *) act2000_receive, card); | ||
584 | init_timer(&card->ptimer); | ||
585 | card->interface.owner = THIS_MODULE; | ||
586 | card->interface.channels = ACT2000_BCH; | ||
587 | card->interface.maxbufsize = 4000; | ||
588 | card->interface.command = if_command; | ||
589 | card->interface.writebuf_skb = if_sendbuf; | ||
590 | card->interface.writecmd = if_writecmd; | ||
591 | card->interface.readstat = if_readstatus; | ||
592 | card->interface.features = | ||
593 | ISDN_FEATURE_L2_X75I | | ||
594 | ISDN_FEATURE_L2_HDLC | | ||
595 | ISDN_FEATURE_L3_TRANS | | ||
596 | ISDN_FEATURE_P_UNKNOWN; | ||
597 | card->interface.hl_hdrlen = 20; | ||
598 | card->ptype = ISDN_PTYPE_EURO; | ||
599 | strlcpy(card->interface.id, id, sizeof(card->interface.id)); | ||
600 | for (i=0; i<ACT2000_BCH; i++) { | ||
601 | card->bch[i].plci = 0x8000; | ||
602 | card->bch[i].ncci = 0x8000; | ||
603 | card->bch[i].l2prot = ISDN_PROTO_L2_X75I; | ||
604 | card->bch[i].l3prot = ISDN_PROTO_L3_TRANS; | ||
605 | } | ||
606 | card->myid = -1; | ||
607 | card->bus = bus; | ||
608 | card->port = port; | ||
609 | card->irq = irq; | ||
610 | card->next = cards; | ||
611 | cards = card; | ||
612 | } | ||
613 | |||
614 | /* | ||
615 | * register card at linklevel | ||
616 | */ | ||
617 | static int | ||
618 | act2000_registercard(act2000_card * card) | ||
619 | { | ||
620 | switch (card->bus) { | ||
621 | case ACT2000_BUS_ISA: | ||
622 | break; | ||
623 | case ACT2000_BUS_MCA: | ||
624 | case ACT2000_BUS_PCMCIA: | ||
625 | default: | ||
626 | printk(KERN_WARNING | ||
627 | "act2000: Illegal BUS type %d\n", | ||
628 | card->bus); | ||
629 | return -1; | ||
630 | } | ||
631 | if (!register_isdn(&card->interface)) { | ||
632 | printk(KERN_WARNING | ||
633 | "act2000: Unable to register %s\n", | ||
634 | card->interface.id); | ||
635 | return -1; | ||
636 | } | ||
637 | card->myid = card->interface.channels; | ||
638 | sprintf(card->regname, "act2000-isdn (%s)", card->interface.id); | ||
639 | return 0; | ||
640 | } | ||
641 | |||
642 | static void | ||
643 | unregister_card(act2000_card * card) | ||
644 | { | ||
645 | isdn_ctrl cmd; | ||
646 | |||
647 | cmd.command = ISDN_STAT_UNLOAD; | ||
648 | cmd.driver = card->myid; | ||
649 | card->interface.statcallb(&cmd); | ||
650 | switch (card->bus) { | ||
651 | case ACT2000_BUS_ISA: | ||
652 | act2000_isa_release(card); | ||
653 | break; | ||
654 | case ACT2000_BUS_MCA: | ||
655 | case ACT2000_BUS_PCMCIA: | ||
656 | default: | ||
657 | printk(KERN_WARNING | ||
658 | "act2000: Invalid BUS type %d\n", | ||
659 | card->bus); | ||
660 | break; | ||
661 | } | ||
662 | } | ||
663 | |||
664 | static int | ||
665 | act2000_addcard(int bus, int port, int irq, char *id) | ||
666 | { | ||
667 | act2000_card *p; | ||
668 | act2000_card *q = NULL; | ||
669 | int initialized; | ||
670 | int added = 0; | ||
671 | int failed = 0; | ||
672 | int i; | ||
673 | |||
674 | if (!bus) | ||
675 | bus = ACT2000_BUS_ISA; | ||
676 | if (port != -1) { | ||
677 | /* Port defined, do fixed setup */ | ||
678 | act2000_alloccard(bus, port, irq, id); | ||
679 | } else { | ||
680 | /* No port defined, perform autoprobing. | ||
681 | * This may result in more than one card detected. | ||
682 | */ | ||
683 | switch (bus) { | ||
684 | case ACT2000_BUS_ISA: | ||
685 | for (i = 0; i < ISA_NRPORTS; i++) | ||
686 | if (act2000_isa_detect(act2000_isa_ports[i])) { | ||
687 | printk(KERN_INFO | ||
688 | "act2000: Detected ISA card at port 0x%x\n", | ||
689 | act2000_isa_ports[i]); | ||
690 | act2000_alloccard(bus, act2000_isa_ports[i], irq, id); | ||
691 | } | ||
692 | break; | ||
693 | case ACT2000_BUS_MCA: | ||
694 | case ACT2000_BUS_PCMCIA: | ||
695 | default: | ||
696 | printk(KERN_WARNING | ||
697 | "act2000: addcard: Invalid BUS type %d\n", | ||
698 | bus); | ||
699 | } | ||
700 | } | ||
701 | if (!cards) | ||
702 | return 1; | ||
703 | p = cards; | ||
704 | while (p) { | ||
705 | initialized = 0; | ||
706 | if (!p->interface.statcallb) { | ||
707 | /* Not yet registered. | ||
708 | * Try to register and activate it. | ||
709 | */ | ||
710 | added++; | ||
711 | switch (p->bus) { | ||
712 | case ACT2000_BUS_ISA: | ||
713 | if (act2000_isa_detect(p->port)) { | ||
714 | if (act2000_registercard(p)) | ||
715 | break; | ||
716 | if (act2000_isa_config_port(p, p->port)) { | ||
717 | printk(KERN_WARNING | ||
718 | "act2000: Could not request port 0x%04x\n", | ||
719 | p->port); | ||
720 | unregister_card(p); | ||
721 | p->interface.statcallb = NULL; | ||
722 | break; | ||
723 | } | ||
724 | if (act2000_isa_config_irq(p, p->irq)) { | ||
725 | printk(KERN_INFO | ||
726 | "act2000: No IRQ available, fallback to polling\n"); | ||
727 | /* Fall back to polled operation */ | ||
728 | p->irq = 0; | ||
729 | } | ||
730 | printk(KERN_INFO | ||
731 | "act2000: ISA" | ||
732 | "-type card at port " | ||
733 | "0x%04x ", | ||
734 | p->port); | ||
735 | if (p->irq) | ||
736 | printk("irq %d\n", p->irq); | ||
737 | else | ||
738 | printk("polled\n"); | ||
739 | initialized = 1; | ||
740 | } | ||
741 | break; | ||
742 | case ACT2000_BUS_MCA: | ||
743 | case ACT2000_BUS_PCMCIA: | ||
744 | default: | ||
745 | printk(KERN_WARNING | ||
746 | "act2000: addcard: Invalid BUS type %d\n", | ||
747 | p->bus); | ||
748 | } | ||
749 | } else | ||
750 | /* Card already initialized */ | ||
751 | initialized = 1; | ||
752 | if (initialized) { | ||
753 | /* Init OK, next card ... */ | ||
754 | q = p; | ||
755 | p = p->next; | ||
756 | } else { | ||
757 | /* Init failed, remove card from list, free memory */ | ||
758 | printk(KERN_WARNING | ||
759 | "act2000: Initialization of %s failed\n", | ||
760 | p->interface.id); | ||
761 | if (q) { | ||
762 | q->next = p->next; | ||
763 | kfree(p); | ||
764 | p = q->next; | ||
765 | } else { | ||
766 | cards = p->next; | ||
767 | kfree(p); | ||
768 | p = cards; | ||
769 | } | ||
770 | failed++; | ||
771 | } | ||
772 | } | ||
773 | return (added - failed); | ||
774 | } | ||
775 | |||
776 | #define DRIVERNAME "IBM Active 2000 ISDN driver" | ||
777 | |||
778 | static int __init act2000_init(void) | ||
779 | { | ||
780 | printk(KERN_INFO "%s\n", DRIVERNAME); | ||
781 | if (!cards) | ||
782 | act2000_addcard(act_bus, act_port, act_irq, act_id); | ||
783 | if (!cards) | ||
784 | printk(KERN_INFO "act2000: No cards defined yet\n"); | ||
785 | return 0; | ||
786 | } | ||
787 | |||
788 | static void __exit act2000_exit(void) | ||
789 | { | ||
790 | act2000_card *card = cards; | ||
791 | act2000_card *last; | ||
792 | while (card) { | ||
793 | unregister_card(card); | ||
794 | del_timer(&card->ptimer); | ||
795 | card = card->next; | ||
796 | } | ||
797 | card = cards; | ||
798 | while (card) { | ||
799 | last = card; | ||
800 | card = card->next; | ||
801 | act2000_clear_msn(last); | ||
802 | kfree(last); | ||
803 | } | ||
804 | printk(KERN_INFO "%s unloaded\n", DRIVERNAME); | ||
805 | } | ||
806 | |||
807 | module_init(act2000_init); | ||
808 | module_exit(act2000_exit); | ||