summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/isdn/hysdn/hysdn_sched.c
blob: 31d7c1415543e099761aab0a4a495759abe71bf5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/* $Id: hysdn_sched.c,v 1.5.6.4 2001/11/06 21:58:19 kai Exp $
 *
 * Linux driver for HYSDN cards
 * scheduler routines for handling exchange card <-> pc.
 *
 * Author    Werner Cornelius (werner@titro.de) for Hypercope GmbH
 * Copyright 1999 by Werner Cornelius (werner@titro.de)
 *
 * This software may be used and distributed according to the terms
 * of the GNU General Public License, incorporated herein by reference.
 *
 */

#include <linux/signal.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <asm/io.h>

#include "hysdn_defs.h"

/*****************************************************************************/
/* hysdn_sched_rx is called from the cards handler to announce new data is   */
/* available from the card. The routine has to handle the data and return    */
/* with a nonzero code if the data could be worked (or even thrown away), if */
/* no room to buffer the data is available a zero return tells the card      */
/* to keep the data until later.                                             */
/*****************************************************************************/
int
hysdn_sched_rx(hysdn_card *card, unsigned char *buf, unsigned short len,
	       unsigned short chan)
{

	switch (chan) {
	case CHAN_NDIS_DATA:
		if (hynet_enable & (1 << card->myid)) {
			/* give packet to network handler */
			hysdn_rx_netpkt(card, buf, len);
		}
		break;

	case CHAN_ERRLOG:
		hysdn_card_errlog(card, (tErrLogEntry *) buf, len);
		if (card->err_log_state == ERRLOG_STATE_ON)
			card->err_log_state = ERRLOG_STATE_START;	/* start new fetch */
		break;
#ifdef CONFIG_HYSDN_CAPI
	case CHAN_CAPI:
/* give packet to CAPI handler */
		if (hycapi_enable & (1 << card->myid)) {
			hycapi_rx_capipkt(card, buf, len);
		}
		break;
#endif /* CONFIG_HYSDN_CAPI */
	default:
		printk(KERN_INFO "irq message channel %d len %d unhandled \n", chan, len);
		break;

	}			/* switch rx channel */

	return (1);		/* always handled */
}				/* hysdn_sched_rx */

/*****************************************************************************/
/* hysdn_sched_tx is called from the cards handler to announce that there is */
/* room in the tx-buffer to the card and data may be sent if needed.         */
/* If the routine wants to send data it must fill buf, len and chan with the */
/* appropriate data and return a nonzero value. With a zero return no new    */
/* data to send is assumed. maxlen specifies the buffer size available for   */
/* sending.                                                                  */
/*****************************************************************************/
int
hysdn_sched_tx(hysdn_card *card, unsigned char *buf,
	       unsigned short volatile *len, unsigned short volatile *chan,
	       unsigned short maxlen)
{
	struct sk_buff *skb;

	if (card->net_tx_busy) {
		card->net_tx_busy = 0;	/* reset flag */
		hysdn_tx_netack(card);	/* acknowledge packet send */
	}			/* a network packet has completely been transferred */
	/* first of all async requests are handled */
	if (card->async_busy) {
		if (card->async_len <= maxlen) {
			memcpy(buf, card->async_data, card->async_len);
			*len = card->async_len;
			*chan = card->async_channel;
			card->async_busy = 0;	/* reset request */
			return (1);
		}
		card->async_busy = 0;	/* in case of length error */
	}			/* async request */
	if ((card->err_log_state == ERRLOG_STATE_START) &&
	    (maxlen >= ERRLOG_CMD_REQ_SIZE)) {
		strcpy(buf, ERRLOG_CMD_REQ);	/* copy the command */
		*len = ERRLOG_CMD_REQ_SIZE;	/* buffer length */
		*chan = CHAN_ERRLOG;	/* and channel */
		card->err_log_state = ERRLOG_STATE_ON;	/* new state is on */
		return (1);	/* tell that data should be send */
	}			/* error log start and able to send */
	if ((card->err_log_state == ERRLOG_STATE_STOP) &&
	    (maxlen >= ERRLOG_CMD_STOP_SIZE)) {
		strcpy(buf, ERRLOG_CMD_STOP);	/* copy the command */
		*len = ERRLOG_CMD_STOP_SIZE;	/* buffer length */
		*chan = CHAN_ERRLOG;	/* and channel */
		card->err_log_state = ERRLOG_STATE_OFF;		/* new state is off */
		return (1);	/* tell that data should be send */
	}			/* error log start and able to send */
	/* now handle network interface packets */
	if ((hynet_enable & (1 << card->myid)) &&
	    (skb = hysdn_tx_netget(card)) != NULL)
	{
		if (skb->len <= maxlen) {
			/* copy the packet to the buffer */
			skb_copy_from_linear_data(skb, buf, skb->len);
			*len = skb->len;
			*chan = CHAN_NDIS_DATA;
			card->net_tx_busy = 1;	/* we are busy sending network data */
			return (1);	/* go and send the data */
		} else
			hysdn_tx_netack(card);	/* aknowledge packet -> throw away */
	}			/* send a network packet if available */
#ifdef CONFIG_HYSDN_CAPI
	if (((hycapi_enable & (1 << card->myid))) &&
	    ((skb = hycapi_tx_capiget(card)) != NULL))
	{
		if (skb->len <= maxlen) {
			skb_copy_from_linear_data(skb, buf, skb->len);
			*len = skb->len;
			*chan = CHAN_CAPI;
			hycapi_tx_capiack(card);
			return (1);	/* go and send the data */
		}
	}
#endif /* CONFIG_HYSDN_CAPI */
	return (0);		/* nothing to send */
}				/* hysdn_sched_tx */


/*****************************************************************************/
/* send one config line to the card and return 0 if successful, otherwise a */
/* negative error code.                                                      */
/* The function works with timeouts perhaps not giving the greatest speed    */
/* sending the line, but this should be meaningless because only some lines  */
/* are to be sent and this happens very seldom.                              */
/*****************************************************************************/
int
hysdn_tx_cfgline(hysdn_card *card, unsigned char *line, unsigned short chan)
{
	int cnt = 50;		/* timeout intervalls */
	unsigned long flags;

	if (card->debug_flags & LOG_SCHED_ASYN)
		hysdn_addlog(card, "async tx-cfg chan=%d len=%d", chan, strlen(line) + 1);

	while (card->async_busy) {

		if (card->debug_flags & LOG_SCHED_ASYN)
			hysdn_addlog(card, "async tx-cfg delayed");

		msleep_interruptible(20);		/* Timeout 20ms */
		if (!--cnt)
			return (-ERR_ASYNC_TIME);	/* timed out */
	}			/* wait for buffer to become free */

	spin_lock_irqsave(&card->hysdn_lock, flags);
	strcpy(card->async_data, line);
	card->async_len = strlen(line) + 1;
	card->async_channel = chan;
	card->async_busy = 1;	/* request transfer */

	/* now queue the task */
	schedule_work(&card->irq_queue);
	spin_unlock_irqrestore(&card->hysdn_lock, flags);

	if (card->debug_flags & LOG_SCHED_ASYN)
		hysdn_addlog(card, "async tx-cfg data queued");

	cnt++;			/* short delay */

	while (card->async_busy) {

		if (card->debug_flags & LOG_SCHED_ASYN)
			hysdn_addlog(card, "async tx-cfg waiting for tx-ready");

		msleep_interruptible(20);		/* Timeout 20ms */
		if (!--cnt)
			return (-ERR_ASYNC_TIME);	/* timed out */
	}			/* wait for buffer to become free again */

	if (card->debug_flags & LOG_SCHED_ASYN)
		hysdn_addlog(card, "async tx-cfg data send");

	return (0);		/* line send correctly */
}				/* hysdn_tx_cfgline */