1 /* ISDN4BSD code */
2 /*	$NetBSD: iwic_dchan.c,v 1.2 2002/09/27 15:37:27 provos Exp $	*/
3 
4 /*
5  * Copyright (c) 1999, 2000 Dave Boyce. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  *---------------------------------------------------------------------------
29  *
30  *      i4b_iwic - isdn4bsd Winbond W6692 driver
31  *      ----------------------------------------
32  *
33  * $FreeBSD$
34  *
35  *      last edit-date: [Tue Jan 16 13:20:14 2001]
36  *
37  *---------------------------------------------------------------------------*/
38 
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: iwic_dchan.c,v 1.2 2002/09/27 15:37:27 provos Exp $");
41 
42 #include <sys/param.h>
43 #include <sys/kernel.h>
44 #include <sys/systm.h>
45 #include <sys/mbuf.h>
46 #include <sys/timeout.h>
47 #include <sys/device.h>
48 #include <sys/socket.h>
49 #include <net/if.h>
50 
51 #include <machine/bus.h>
52 
53 #include <dev/pci/pcivar.h>
54 
55 #include <dev/pci/iwicreg.h>
56 #include <dev/pci/iwicvar.h>
57 
58 #include <netisdn/i4b_global.h>
59 #include <netisdn/i4b_mbuf.h>
60 
61 #define MAX_DFRAME_LEN	264
62 
63 static void dchan_receive(struct iwic_softc *sc, int ista);
64 
65 /*---------------------------------------------------------------------------*
66  *	initialize D-channel variables and registers
67  *---------------------------------------------------------------------------*/
68 void
iwic_dchan_init(struct iwic_softc * sc)69 iwic_dchan_init(struct iwic_softc *sc)
70 {
71 	sc->sc_dchan.ibuf = NULL;
72 	sc->sc_dchan.rx_count = 0;
73 
74 	sc->sc_dchan.obuf = NULL;
75 	sc->sc_dchan.obuf2 = NULL;
76 	sc->sc_dchan.tx_count = 0;
77 	sc->sc_dchan.tx_ready = 0;
78 
79 	IWIC_WRITE(sc, D_CTL, D_CTL_SRST);
80 
81 	DELAY(5000);
82 
83 	IWIC_WRITE(sc, D_CTL, 0);
84 
85 	IWIC_WRITE(sc, SQX, SQX_SCIE);
86 
87 	IWIC_WRITE(sc, PCTL, 0x00);
88 	IWIC_WRITE(sc, MOCR, 0x00);
89 	IWIC_WRITE(sc, GCR, 0x00);
90 
91 	IWIC_WRITE(sc, D_CMDR, D_CMDR_RRST | D_CMDR_XRST);
92 	IWIC_WRITE(sc, D_MODE, D_MODE_RACT);
93 
94 	IWIC_WRITE(sc, D_SAM, 0xff);
95 	IWIC_WRITE(sc, D_TAM, 0xff);
96 
97 	IWIC_WRITE(sc, D_EXIM, 0x00);
98 }
99 
100 /*---------------------------------------------------------------------------*
101  *	Extended IRQ handler for the D-channel
102  *---------------------------------------------------------------------------*/
103 void
iwic_dchan_xirq(struct iwic_softc * sc)104 iwic_dchan_xirq(struct iwic_softc *sc)
105 {
106 	int irq_stat;
107 	int stat;
108 
109 	irq_stat = IWIC_READ(sc, D_EXIR);
110 
111 	if (irq_stat & D_EXIR_RDOV)
112 	{
113 		NDBGL1(L1_I_ERR, "RDOV in state %s", iwic_printstate(sc));
114 		IWIC_WRITE(sc, D_CMDR, D_CMDR_RRST);
115 	}
116 	if (irq_stat & D_EXIR_XDUN)
117 	{
118 		NDBGL1(L1_I_ERR, "XDUN in state %s", iwic_printstate(sc));
119 		sc->sc_dchan.tx_ready = 0;
120 	}
121 	if (irq_stat & D_EXIR_XCOL)
122 	{
123 		NDBGL1(L1_I_ERR, "XCOL in state %s", iwic_printstate(sc));
124 		IWIC_WRITE(sc, D_CMDR, D_CMDR_XRST);
125 		sc->sc_dchan.tx_ready = 0;
126 	}
127 	if (irq_stat & D_EXIR_TIN2)
128 	{
129 		NDBGL1(L1_I_ERR, "TIN2 in state %s", iwic_printstate(sc));
130 	}
131 	if (irq_stat & D_EXIR_MOC)
132 	{
133 		stat = IWIC_READ(sc, MOR);
134 		NDBGL1(L1_I_ERR, "MOC in state %s, byte = 0x%x", iwic_printstate(sc), stat);
135 	}
136 
137 	if (irq_stat & D_EXIR_ISC)
138 	{
139 		stat = (IWIC_READ(sc, CIR)) & 0x0f;
140 
141 		switch (stat)
142 		{
143 			case CIR_CE:
144 				NDBGL1(L1_I_CICO, "rx CE in state %s", iwic_printstate(sc));
145 				iwic_next_state(sc, EV_CE);
146 				break;
147 			case CIR_DRD:
148 				NDBGL1(L1_I_CICO, "rx DRD in state %s", iwic_printstate(sc));
149 				iwic_next_state(sc, EV_INFO0);
150 				isdn_layer2_status_ind(&sc->sc_l2, sc->sc_l3token, STI_L1STAT, LAYER_IDLE);
151 				break;
152 			case CIR_LD:
153 				NDBGL1(L1_I_CICO, "rx LD in state %s", iwic_printstate(sc));
154 				iwic_next_state(sc, EV_RSY);
155 				break;
156 			case CIR_ARD:
157 				NDBGL1(L1_I_CICO, "rx ARD in state %s", iwic_printstate(sc));
158 				iwic_next_state(sc, EV_INFO2);
159 				break;
160 			case CIR_TI:
161 				NDBGL1(L1_I_CICO, "rx TI in state %s", iwic_printstate(sc));
162 				iwic_next_state(sc, EV_INFO0);
163 				break;
164 			case CIR_ATI:
165 				NDBGL1(L1_I_CICO, "rx ATI in state %s", iwic_printstate(sc));
166 				iwic_next_state(sc, EV_INFO0);
167 				break;
168 			case CIR_AI8:
169 				NDBGL1(L1_I_CICO, "rx AI8 in state %s", iwic_printstate(sc));
170 				isdn_layer2_status_ind(&sc->sc_l2, sc->sc_l3token, STI_L1STAT, LAYER_ACTIVE);
171 				iwic_next_state(sc, EV_INFO48);
172 				break;
173 			case CIR_AI10:
174 				NDBGL1(L1_I_CICO, "rx AI10 in state %s", iwic_printstate(sc));
175 				isdn_layer2_status_ind(&sc->sc_l2, sc->sc_l3token, STI_L1STAT, LAYER_ACTIVE);
176 				iwic_next_state(sc, EV_INFO410);
177 				break;
178 			case CIR_CD:
179 				NDBGL1(L1_I_CICO, "rx DIS in state %s", iwic_printstate(sc));
180 				iwic_next_state(sc, EV_DIS);
181 				break;
182 			default:
183 				NDBGL1(L1_I_ERR, "ERROR, unknown indication 0x%x in state %s", stat, iwic_printstate(sc));
184 				iwic_next_state(sc, EV_INFO0);
185 				break;
186 		}
187 	}
188 
189 	if (irq_stat & D_EXIR_TEXP)
190 	{
191 		NDBGL1(L1_I_ERR, "TEXP in state %s", iwic_printstate(sc));
192 	}
193 
194 	if (irq_stat & D_EXIR_WEXP)
195 	{
196 		NDBGL1(L1_I_ERR, "WEXP in state %s", iwic_printstate(sc));
197 	}
198 }
199 
200 /*---------------------------------------------------------------------------*
201  *	All receiving and transmitting takes place here.
202  *---------------------------------------------------------------------------*/
203 void
iwic_dchan_xfer_irq(struct iwic_softc * sc,int ista)204 iwic_dchan_xfer_irq(struct iwic_softc *sc, int ista)
205 {
206 	NDBGL1(L1_I_MSG, "ISTA = 0x%x", ista);
207 
208 	if (ista & (ISTA_D_RMR | ISTA_D_RME))
209 	{
210 		/* Receive message ready */
211 		dchan_receive(sc, ista);
212 	}
213 	if (ista & ISTA_D_XFR)
214 	{
215 		/* Transmitter ready */
216 		sc->sc_dchan.tx_ready = 1;
217 
218 		iwic_dchan_transmit(sc);
219 	}
220 }
221 
222 /*---------------------------------------------------------------------------*
223  *	disable D-channel
224  *---------------------------------------------------------------------------*/
225 void
iwic_dchan_disable(struct iwic_softc * sc)226 iwic_dchan_disable(struct iwic_softc *sc)
227 {
228 	int s;
229 
230 	s = splnet();
231 
232 	if (sc->sc_dchan.obuf)
233 	{
234 		if (sc->sc_dchan.free_obuf)
235 			i4b_Dfreembuf(sc->sc_dchan.obuf);
236 		sc->sc_dchan.obuf = NULL;
237 	}
238 
239 	if (sc->sc_dchan.obuf2)
240 	{
241 		if (sc->sc_dchan.free_obuf2)
242 			i4b_Dfreembuf(sc->sc_dchan.obuf2);
243 		sc->sc_dchan.obuf2 = NULL;
244 	}
245 
246 	splx(s);
247 
248 	IWIC_WRITE(sc, CIX, CIX_DRC);
249 }
250 
251 /*---------------------------------------------------------------------------*
252  *	queue D-channel message for transmission
253  *---------------------------------------------------------------------------*/
254 int
iwic_dchan_data_req(struct iwic_softc * sc,struct mbuf * m,int freeflag)255 iwic_dchan_data_req(struct iwic_softc *sc, struct mbuf *m, int freeflag)
256 {
257 	int s;
258 
259 	if (!m)
260 		return 0;
261 
262 	s = splnet();
263 
264 	/* Queue message */
265 
266 	if (sc->sc_dchan.obuf)
267 	{
268 		if (sc->sc_dchan.obuf2)
269 		{
270 			NDBGL1(L1_I_ERR, "no buffer space!");
271 		}
272 		else
273 		{
274 			sc->sc_dchan.obuf2 = m;
275 			sc->sc_dchan.free_obuf2 = freeflag;
276 		}
277 	}
278 	else
279 	{
280 		sc->sc_dchan.obuf = m;
281 		sc->sc_dchan.obuf_ptr = m->m_data;
282 		sc->sc_dchan.obuf_len = m->m_len;
283 		sc->sc_dchan.free_obuf = freeflag;
284 	}
285 
286 	iwic_dchan_transmit(sc);
287 
288 	splx(s);
289 
290 	return (0);
291 }
292 
293 /*---------------------------------------------------------------------------*
294  *	allocate an mbuf
295  *---------------------------------------------------------------------------*/
296 static void
dchan_get_mbuf(struct iwic_softc * sc,int len)297 dchan_get_mbuf(struct iwic_softc *sc, int len)
298 {
299 	sc->sc_dchan.ibuf = i4b_Dgetmbuf(len);
300 
301 	if (!sc->sc_dchan.ibuf)
302 		panic("dchan_get_mbuf: unable to allocate %d bytes for mbuf!", len);
303 
304 	sc->sc_dchan.ibuf_ptr = sc->sc_dchan.ibuf->m_data;
305 	sc->sc_dchan.ibuf_max_len = sc->sc_dchan.ibuf->m_len;
306 	sc->sc_dchan.ibuf_len = 0;
307 }
308 
309 /*---------------------------------------------------------------------------*
310  *	D-channel receive data interrupt
311  *---------------------------------------------------------------------------*/
312 static void
dchan_receive(struct iwic_softc * sc,int ista)313 dchan_receive(struct iwic_softc *sc, int ista)
314 {
315 	int command = D_CMDR_RACK;
316 
317 	if (ista & ISTA_D_RMR)
318 	{
319 		/* Got 64 bytes in FIFO */
320 
321 		if (!sc->sc_dchan.ibuf)
322 		{
323 			dchan_get_mbuf(sc, MAX_DFRAME_LEN);
324 
325 		}
326 		else if ((sc->sc_dchan.ibuf_len + MAX_DFRAME_LEN) >
327 			 sc->sc_dchan.ibuf_max_len)
328 		{
329 			panic("dchan_receive: not enough space in buffer!");
330 		}
331 
332 		IWIC_RDDFIFO(sc, sc->sc_dchan.ibuf_ptr, 64);
333 
334 		sc->sc_dchan.ibuf_ptr += 64;
335 		sc->sc_dchan.ibuf_len += 64;
336 		sc->sc_dchan.rx_count += 64;
337 	}
338 	if (ista & ISTA_D_RME)
339 	{
340 		/* Got end of frame */
341 		int status;
342 
343 		status = IWIC_READ(sc, D_RSTA);
344 
345 		if (status & (D_RSTA_RDOV | D_RSTA_CRCE | D_RSTA_RMB))
346 		{
347 			if (status & D_RSTA_RDOV)
348 				NDBGL1(L1_I_ERR, "%s: D-channel Receive Data Overflow", sc->sc_dev.dv_xname);
349 			if (status & D_RSTA_CRCE)
350 				NDBGL1(L1_I_ERR, "%s: D-channel CRC Error", sc->sc_dev.dv_xname);
351 			if (status & D_RSTA_RMB)
352 				NDBGL1(L1_I_ERR, "%s: D-channel Receive Message Aborted", sc->sc_dev.dv_xname);
353 			command |= D_CMDR_RRST;
354 		}
355 		else
356 		{
357 			int hi, lo;
358 			int total_frame_len;
359 
360 			lo = IWIC_READ(sc, D_RBCL);
361 			hi = IWIC_READ(sc, D_RBCH);
362 			total_frame_len = D_RBC(hi, lo);
363 			lo = lo & 0x3f;
364 
365 			if (lo == 0)
366 				lo = IWIC_DCHAN_FIFO_LEN;
367 
368 			if (!sc->sc_dchan.ibuf)
369 			{
370 				dchan_get_mbuf(sc, lo);
371 			}
372 			else if ((sc->sc_dchan.ibuf_len + lo) >
373 				 sc->sc_dchan.ibuf_max_len)
374 			{
375 				panic("dchan_receive: buffer not long enough");
376 			}
377 
378 			IWIC_RDDFIFO(sc, sc->sc_dchan.ibuf_ptr, lo);
379 			sc->sc_dchan.ibuf_len += lo;
380 			sc->sc_dchan.rx_count += lo;
381 
382 			sc->sc_dchan.ibuf->m_len = sc->sc_dchan.ibuf_len;
383 
384 			if(sc->sc_trace & TRACE_D_RX)
385 			{
386 				i4b_trace_hdr hdr;
387 				hdr.type = TRC_CH_D;
388 				hdr.dir = FROM_NT;
389 				hdr.count = ++sc->sc_dchan.trace_count;
390 				isdn_layer2_trace_ind(&sc->sc_l2, sc->sc_l3token, &hdr, sc->sc_dchan.ibuf->m_len, sc->sc_dchan.ibuf->m_data);
391 			}
392 			isdn_layer2_data_ind(&sc->sc_l2,sc->sc_l3token,sc->sc_dchan.ibuf);
393 
394 			sc->sc_dchan.ibuf = NULL;
395 		}
396 	}
397 	IWIC_WRITE(sc, D_CMDR, command);
398 }
399 
400 /*---------------------------------------------------------------------------*
401  *	transmit D-channel frame
402  *---------------------------------------------------------------------------*/
403 void
iwic_dchan_transmit(struct iwic_softc * sc)404 iwic_dchan_transmit(struct iwic_softc *sc)
405 {
406 	int cmd;
407 	u_char *ptr;
408 	int len;
409 
410 	if (!sc->sc_dchan.tx_ready)
411 		return;
412 
413 	if (!sc->sc_dchan.obuf)
414 		return;
415 
416 	if (sc->sc_I430state != ST_F7)
417 		return;
418 
419 	ptr = sc->sc_dchan.obuf_ptr;
420 	len = min(sc->sc_dchan.obuf_len, IWIC_DCHAN_FIFO_LEN);
421 
422 	if(sc->sc_trace & TRACE_D_TX)
423 	{
424 		i4b_trace_hdr hdr;
425 		hdr.type = TRC_CH_D;
426 		hdr.dir = FROM_TE;
427 		hdr.count = ++sc->sc_dchan.trace_count;
428 		isdn_layer2_trace_ind(&sc->sc_l2, sc->sc_l3token, &hdr, len, ptr);
429 	}
430 
431 	IWIC_WRDFIFO(sc, ptr, len);
432 
433 	sc->sc_dchan.tx_count += len;
434 
435 	if (len < sc->sc_dchan.obuf_len)
436 	{
437 		sc->sc_dchan.obuf_ptr += len;
438 		sc->sc_dchan.obuf_len -= len;
439 
440 		cmd = D_CMDR_XMS;
441 
442 	}
443 	else
444 	{
445 		if (sc->sc_dchan.free_obuf)
446 			i4b_Dfreembuf(sc->sc_dchan.obuf);
447 
448 		sc->sc_dchan.obuf = NULL;
449 		sc->sc_dchan.obuf_ptr = NULL;
450 		sc->sc_dchan.obuf_len = 0;
451 
452 		if (sc->sc_dchan.obuf2)
453 		{
454 			sc->sc_dchan.obuf = sc->sc_dchan.obuf2;
455 			sc->sc_dchan.obuf_ptr = sc->sc_dchan.obuf->m_data;
456 			sc->sc_dchan.obuf_len = sc->sc_dchan.obuf->m_len;
457 			sc->sc_dchan.free_obuf = sc->sc_dchan.free_obuf2;
458 
459 			sc->sc_dchan.obuf2 = NULL;
460 		}
461 		cmd = D_CMDR_XMS | D_CMDR_XME;
462 	}
463 	sc->sc_dchan.tx_ready = 0;
464 	IWIC_WRITE(sc, D_CMDR, cmd);
465 }
466