Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / net / i4b / capi / capi_l4if.c
1 /*
2  * Copyright (c) 2001 Cubical Solutions Ltd. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * capi/capi_l4if.c     The CAPI i4b L4/device interface.
26  *
27  * $FreeBSD: src/sys/i4b/capi/capi_l4if.c,v 1.1.2.1 2001/08/10 14:08:34 obrien Exp $
28  */
29
30 #include "i4bcapi.h"
31 #if NI4BCAPI > 0
32
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/systm.h>
36 #include <sys/mbuf.h>
37 #include <sys/socket.h>
38 #include <net/if.h>
39
40 #include <machine/i4b_debug.h>
41 #include <machine/i4b_ioctl.h>
42 #include <machine/i4b_cause.h>
43
44 #include <i4b/include/i4b_l3l4.h>
45 #include <i4b/include/i4b_mbuf.h>
46 #include <i4b/include/i4b_global.h>
47
48 #include <i4b/layer4/i4b_l4.h>
49
50 #include <i4b/capi/capi.h>
51 #include <i4b/capi/capi_msgs.h>
52
53 static void n_connect_request(u_int cdid);
54 static void n_connect_response(u_int cdid, int response, int cause);
55 static void n_disconnect_request(u_int cdid, int cause);
56 static void n_alert_request(u_int cdid);
57 static void n_mgmt_command(int unit, int cmd, void *parm);
58 static int  n_download(int unit, int, struct isdn_dr_prot *);
59
60 capi_softc_t *capi_sc[MAX_CONTROLLERS] = { NULL, };
61 int ncapi = 0;
62
63 /*
64 //  i4b_capi_{ret,set}_linktab
65 //      i4b driver glue.
66 //
67 //  i4b_capi_bch_config
68 //      Called by i4b driver to flush + {en,dis}able a channel.
69 //
70 //  i4b_capi_bch_start_tx
71 //      Called by i4b driver to transmit a queued mbuf.
72 //
73 //  i4b_capi_bch_stat
74 //      Called by i4b driver to obtain statistics information.
75 */
76
77 static isdn_link_t *
78 i4b_capi_ret_linktab(int unit, int channel)
79 {
80     capi_softc_t *sc = capi_sc[unit];
81     return &sc->sc_bchan[channel].capi_isdn_linktab;
82 }
83
84 static void
85 i4b_capi_set_linktab(int unit, int channel, drvr_link_t *dlt)
86 {
87     capi_softc_t *sc = capi_sc[unit];
88     sc->sc_bchan[channel].capi_drvr_linktab = dlt;
89 }
90
91 static void
92 i4b_capi_bch_config(int unit, int chan, int bprot, int activate)
93 {
94     capi_softc_t *sc = capi_sc[unit];
95
96     i4b_Bcleanifq(&sc->sc_bchan[chan].tx_queue);
97     sc->sc_bchan[chan].tx_queue.ifq_maxlen = IFQ_MAXLEN;
98     sc->sc_bchan[chan].txcount = 0;
99
100     /* The telephony drivers use rx_queue for receive. */
101
102     i4b_Bcleanifq(&sc->sc_bchan[chan].rx_queue);
103     sc->sc_bchan[chan].rx_queue.ifq_maxlen = IFQ_MAXLEN;
104     sc->sc_bchan[chan].rxcount = 0;
105
106     /* HDLC frames are put to in_mbuf */
107
108     i4b_Bfreembuf(sc->sc_bchan[chan].in_mbuf);
109     sc->sc_bchan[chan].in_mbuf = NULL;
110
111     /* Because of the difference, we need to remember the protocol. */
112
113     sc->sc_bchan[chan].bprot = bprot;
114     sc->sc_bchan[chan].busy = 0;
115 }
116
117 static void
118 i4b_capi_bch_start_tx(int unit, int chan)
119 {
120     capi_softc_t *sc = capi_sc[unit];
121     int s;
122
123     s = SPLI4B();
124
125     if (sc->sc_bchan[chan].state != B_CONNECTED) {
126         splx(s);
127         printf("capi%d: start_tx on unconnected channel\n", sc->sc_unit);
128         return;
129     }
130
131     if (sc->sc_bchan[chan].busy) {
132         splx(s);
133         return;
134     }
135
136     capi_start_tx(sc, chan);
137
138     splx(s);
139 }
140
141 static void
142 i4b_capi_bch_stat(int unit, int chan, bchan_statistics_t *bsp)
143 {
144     capi_softc_t *sc = capi_sc[unit];
145     int s = SPLI4B();
146
147     bsp->outbytes = sc->sc_bchan[chan].txcount;
148     bsp->inbytes = sc->sc_bchan[chan].rxcount;
149
150     sc->sc_bchan[chan].txcount = 0;
151     sc->sc_bchan[chan].rxcount = 0;
152
153     splx(s);
154 }
155
156 int capi_start_tx(capi_softc_t *sc, int chan)
157 {
158     struct mbuf *m_b3;
159     int sent = 0;
160
161     _IF_DEQUEUE(&sc->sc_bchan[chan].tx_queue, m_b3);
162     while (m_b3) {
163         struct mbuf *m = m_b3->m_next;
164
165         sc->sc_bchan[chan].txcount += m_b3->m_len;
166         capi_data_b3_req(sc, chan, m_b3);
167         sent++;
168
169         m_b3 = m;
170     }
171
172     if (sc->sc_bchan[chan].capi_drvr_linktab) {
173         /* Notify i4b driver of activity, and if the queue is drained. */
174
175         if (sent)
176             (*sc->sc_bchan[chan].capi_drvr_linktab->bch_activity)(
177                 sc->sc_bchan[chan].capi_drvr_linktab->unit, ACT_TX);
178
179         if (IF_QEMPTY(&sc->sc_bchan[chan].tx_queue))
180             (*sc->sc_bchan[chan].capi_drvr_linktab->bch_tx_queue_empty)(
181                 sc->sc_bchan[chan].capi_drvr_linktab->unit);
182     }
183
184     return sent;
185 }
186
187 /*
188 //  capi_ll_attach
189 //      Called by a link layer driver at boot time.
190 */
191
192 int
193 capi_ll_attach(capi_softc_t *sc)
194 {
195     int i;
196
197     if (ncapi == (sizeof(capi_sc) / sizeof(capi_sc[0]))) {
198         printf("capi%d: too many units, increase MAX_CONTROLLERS\n", ncapi);
199         return (ENXIO);
200     }
201
202     /* Unit type and subtype; sc is partly filled by ll driver */
203     
204     ctrl_desc[nctrl].unit = ncapi;
205     ctrl_desc[nctrl].ctrl_type = CTRL_CAPI;
206     ctrl_desc[nctrl].card_type = sc->card_type;
207
208     /* L4 callbacks */
209     
210     ctrl_types[CTRL_CAPI].get_linktab = i4b_capi_ret_linktab;
211     ctrl_types[CTRL_CAPI].set_linktab = i4b_capi_set_linktab;
212
213     ctrl_desc[nctrl].N_CONNECT_REQUEST = n_connect_request;
214     ctrl_desc[nctrl].N_CONNECT_RESPONSE = n_connect_response;
215     ctrl_desc[nctrl].N_DISCONNECT_REQUEST = n_disconnect_request;
216     ctrl_desc[nctrl].N_ALERT_REQUEST = n_alert_request;
217     ctrl_desc[nctrl].N_DOWNLOAD = n_download;
218     ctrl_desc[nctrl].N_DIAGNOSTICS = NULL; /* XXX todo */
219     ctrl_desc[nctrl].N_MGMT_COMMAND = n_mgmt_command;
220
221     /* Unit state */
222
223     sc->sc_enabled = FALSE;
224     sc->sc_state = C_DOWN;
225     sc->sc_msgid = 0;
226
227     ctrl_desc[nctrl].dl_est = DL_DOWN;
228     ctrl_desc[nctrl].nbch = sc->sc_nbch;
229
230     for (i = 0; i < sc->sc_nbch; i++) {
231         ctrl_desc[nctrl].bch_state[i] = BCH_ST_FREE;
232         sc->sc_bchan[i].ncci = INVALID;
233         sc->sc_bchan[i].msgid = 0;
234         sc->sc_bchan[i].busy = 0;
235         sc->sc_bchan[i].state = B_FREE;
236
237         memset(&sc->sc_bchan[i].tx_queue, 0, sizeof(struct ifqueue));
238         memset(&sc->sc_bchan[i].rx_queue, 0, sizeof(struct ifqueue));
239         sc->sc_bchan[i].tx_queue.ifq_maxlen = IFQ_MAXLEN;
240         sc->sc_bchan[i].rx_queue.ifq_maxlen = IFQ_MAXLEN;
241
242 #if defined (__FreeBSD__) && __FreeBSD__ > 4
243         mtx_init(&sc->sc_bchan[i].tx_queue.ifq_mtx, "i4b_capi_tx", MTX_DEF);
244         mtx_init(&sc->sc_bchan[i].rx_queue.ifq_mtx, "i4b_capi_rx", MTX_DEF);    
245 #endif    
246
247         sc->sc_bchan[i].txcount = 0;
248         sc->sc_bchan[i].rxcount = 0;
249
250         sc->sc_bchan[i].cdid = CDID_UNUSED;
251         sc->sc_bchan[i].bprot = BPROT_NONE;
252         sc->sc_bchan[i].in_mbuf = NULL;
253
254         sc->sc_bchan[i].capi_drvr_linktab = NULL;
255
256         sc->sc_bchan[i].capi_isdn_linktab.unit = ncapi;
257         sc->sc_bchan[i].capi_isdn_linktab.channel = i;
258         sc->sc_bchan[i].capi_isdn_linktab.bch_config = i4b_capi_bch_config;
259         sc->sc_bchan[i].capi_isdn_linktab.bch_tx_start = i4b_capi_bch_start_tx;
260         sc->sc_bchan[i].capi_isdn_linktab.bch_stat = i4b_capi_bch_stat;
261         sc->sc_bchan[i].capi_isdn_linktab.tx_queue = &sc->sc_bchan[i].tx_queue;
262         sc->sc_bchan[i].capi_isdn_linktab.rx_queue = &sc->sc_bchan[i].rx_queue;
263         sc->sc_bchan[i].capi_isdn_linktab.rx_mbuf = &sc->sc_bchan[i].in_mbuf;
264     }
265
266     ctrl_desc[nctrl].tei = -1;
267
268     /* Up the controller index and store the softc */
269
270     sc->sc_unit = ncapi;
271     capi_sc[ncapi++] = sc;
272     sc->ctrl_unit = nctrl++;
273
274     printf("capi%d: card type %d attached\n", sc->sc_unit, sc->card_type);
275
276     return(0);
277 }
278
279 /*
280 //  n_mgmt_command
281 //      i4b L4 management command.
282 */
283
284 static void
285 n_mgmt_command(int unit, int op, void *arg)
286 {
287     capi_softc_t *sc = capi_sc[unit];
288
289     printf("capi%d: mgmt command %d\n", sc->sc_unit, op);
290
291     switch(op) {
292     case CMR_DOPEN:
293         sc->sc_enabled = TRUE;
294         break;
295
296     case CMR_DCLOSE:
297         sc->sc_enabled = FALSE;
298         break;
299
300     case CMR_SETTRACE:
301         break;
302
303     default:
304         break;
305     }
306 }
307
308 /*
309 //  n_connect_request
310 //      i4b L4 wants to connect. We assign a B channel to the call,
311 //      send a CAPI_CONNECT_REQ, and set the channel to B_CONNECT_CONF.
312 */
313
314 static void
315 n_connect_request(u_int cdid)
316 {
317     call_desc_t *cd = cd_by_cdid(cdid);
318     capi_softc_t *sc;
319     int bch, s;
320
321     if (!cd) {
322         printf("capi?: invalid cdid %d\n", cdid);
323         return;
324     }
325
326     sc = capi_sc[ctrl_desc[cd->controller].unit];
327     bch = cd->channelid;
328
329     s = SPLI4B();
330
331     if ((bch < 0) || (bch >= sc->sc_nbch))
332         for (bch = 0; bch < sc->sc_nbch; bch++)
333             if (sc->sc_bchan[bch].state == B_FREE)
334                 break;
335
336     if (bch == sc->sc_nbch) {
337         splx(s);
338         printf("capi%d: no free B channel\n", sc->sc_unit);
339         return;
340     }
341
342     cd->channelid = bch;
343
344     capi_connect_req(sc, cd);
345     splx(s);
346 }
347
348 /*
349 //  n_connect_response
350 //      i4b L4 answers a call. We send a CONNECT_RESP with the proper
351 //      Reject code, and set the channel to B_CONNECT_B3_IND or B_FREE,
352 //      depending whether we answer or not.
353 */
354
355 static void
356 n_connect_response(u_int cdid, int response, int cause)
357 {
358     call_desc_t *cd = cd_by_cdid(cdid);
359     capi_softc_t *sc;
360     int bch, s;
361
362     if (!cd) {
363         printf("capi?: invalid cdid %d\n", cdid);
364         return;
365     }
366
367     sc = capi_sc[ctrl_desc[cd->controller].unit];
368     bch = cd->channelid;
369
370     T400_stop(cd);
371         
372     cd->response = response;
373     cd->cause_out = cause;
374
375     s = SPLI4B();
376     capi_connect_resp(sc, cd);
377     splx(s);
378 }
379
380 /*
381 //  n_disconnect_request
382 //      i4b L4 wants to disconnect. We send a DISCONNECT_REQ and
383 //      set the channel to B_DISCONNECT_CONF.
384 */
385
386 static void
387 n_disconnect_request(u_int cdid, int cause)
388 {
389     call_desc_t *cd = cd_by_cdid(cdid);
390     capi_softc_t *sc;
391     int bch, s;
392
393     if (!cd) {
394         printf("capi?: invalid cdid %d\n", cdid);
395         return;
396     }
397
398     sc = capi_sc[ctrl_desc[cd->controller].unit];
399     bch = cd->channelid;
400
401     cd->cause_out = cause;
402
403     s = SPLI4B();
404     capi_disconnect_req(sc, cd);
405     splx(s);
406 }
407
408 /*
409 //  n_alert_request
410 //      i4b L4 wants to alert an incoming call. We send ALERT_REQ.
411 */
412
413 static void
414 n_alert_request(u_int cdid)
415 {
416     call_desc_t *cd = cd_by_cdid(cdid);
417     capi_softc_t *sc;
418     int s;
419
420     if (!cd) {
421         printf("capi?: invalid cdid %d\n", cdid);
422         return;
423     }
424
425     sc = capi_sc[ctrl_desc[cd->controller].unit];
426
427     s = SPLI4B();
428     capi_alert_req(sc, cd);
429     splx(s);
430 }
431
432 /*
433 //  n_download
434 //      L4 -> firmware download
435 */
436
437 static int
438 n_download(int unit, int numprotos, struct isdn_dr_prot *protocols)
439 {
440     capi_softc_t *sc = capi_sc[unit];
441
442     if (sc->load) {
443         (*capi_sc[unit]->load)(sc, protocols[0].bytecount,
444                                protocols[0].microcode);
445     }
446
447     return(0);
448 }
449
450 #endif /* NI4BCAPI > 0 */