Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* $Id: hysdn_sched.c,v 1.5.6.4 2001/11/06 21:58:19 kai Exp $ |
2 | * | |
3 | * Linux driver for HYSDN cards | |
4 | * scheduler routines for handling exchange card <-> pc. | |
5 | * | |
6 | * Author Werner Cornelius (werner@titro.de) for Hypercope GmbH | |
7 | * Copyright 1999 by Werner Cornelius (werner@titro.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 | */ | |
13 | ||
1da177e4 LT |
14 | #include <linux/sched.h> |
15 | #include <linux/signal.h> | |
16 | #include <linux/kernel.h> | |
17 | #include <linux/ioport.h> | |
18 | #include <linux/interrupt.h> | |
19 | #include <linux/delay.h> | |
20 | #include <asm/io.h> | |
21 | ||
22 | #include "hysdn_defs.h" | |
23 | ||
24 | /*****************************************************************************/ | |
25 | /* hysdn_sched_rx is called from the cards handler to announce new data is */ | |
26 | /* available from the card. The routine has to handle the data and return */ | |
27 | /* with a nonzero code if the data could be worked (or even thrown away), if */ | |
28 | /* no room to buffer the data is available a zero return tells the card */ | |
29 | /* to keep the data until later. */ | |
30 | /*****************************************************************************/ | |
31 | int | |
c721bcce AM |
32 | hysdn_sched_rx(hysdn_card *card, unsigned char *buf, unsigned short len, |
33 | unsigned short chan) | |
1da177e4 LT |
34 | { |
35 | ||
36 | switch (chan) { | |
37 | case CHAN_NDIS_DATA: | |
38 | if (hynet_enable & (1 << card->myid)) { | |
39 | /* give packet to network handler */ | |
40 | hysdn_rx_netpkt(card, buf, len); | |
41 | } | |
42 | break; | |
43 | ||
44 | case CHAN_ERRLOG: | |
45 | hysdn_card_errlog(card, (tErrLogEntry *) buf, len); | |
46 | if (card->err_log_state == ERRLOG_STATE_ON) | |
47 | card->err_log_state = ERRLOG_STATE_START; /* start new fetch */ | |
48 | break; | |
49 | #ifdef CONFIG_HYSDN_CAPI | |
50 | case CHAN_CAPI: | |
51 | /* give packet to CAPI handler */ | |
52 | if (hycapi_enable & (1 << card->myid)) { | |
53 | hycapi_rx_capipkt(card, buf, len); | |
54 | } | |
55 | break; | |
56 | #endif /* CONFIG_HYSDN_CAPI */ | |
57 | default: | |
58 | printk(KERN_INFO "irq message channel %d len %d unhandled \n", chan, len); | |
59 | break; | |
60 | ||
61 | } /* switch rx channel */ | |
62 | ||
63 | return (1); /* always handled */ | |
64 | } /* hysdn_sched_rx */ | |
65 | ||
66 | /*****************************************************************************/ | |
67 | /* hysdn_sched_tx is called from the cards handler to announce that there is */ | |
68 | /* room in the tx-buffer to the card and data may be sent if needed. */ | |
69 | /* If the routine wants to send data it must fill buf, len and chan with the */ | |
70 | /* appropriate data and return a nonzero value. With a zero return no new */ | |
71 | /* data to send is assumed. maxlen specifies the buffer size available for */ | |
72 | /* sending. */ | |
73 | /*****************************************************************************/ | |
74 | int | |
c721bcce AM |
75 | hysdn_sched_tx(hysdn_card *card, unsigned char *buf, |
76 | unsigned short volatile *len, unsigned short volatile *chan, | |
77 | unsigned short maxlen) | |
1da177e4 LT |
78 | { |
79 | struct sk_buff *skb; | |
80 | ||
81 | if (card->net_tx_busy) { | |
82 | card->net_tx_busy = 0; /* reset flag */ | |
83 | hysdn_tx_netack(card); /* acknowledge packet send */ | |
84 | } /* a network packet has completely been transferred */ | |
85 | /* first of all async requests are handled */ | |
86 | if (card->async_busy) { | |
87 | if (card->async_len <= maxlen) { | |
88 | memcpy(buf, card->async_data, card->async_len); | |
89 | *len = card->async_len; | |
90 | *chan = card->async_channel; | |
91 | card->async_busy = 0; /* reset request */ | |
92 | return (1); | |
93 | } | |
94 | card->async_busy = 0; /* in case of length error */ | |
95 | } /* async request */ | |
96 | if ((card->err_log_state == ERRLOG_STATE_START) && | |
97 | (maxlen >= ERRLOG_CMD_REQ_SIZE)) { | |
98 | strcpy(buf, ERRLOG_CMD_REQ); /* copy the command */ | |
99 | *len = ERRLOG_CMD_REQ_SIZE; /* buffer length */ | |
100 | *chan = CHAN_ERRLOG; /* and channel */ | |
101 | card->err_log_state = ERRLOG_STATE_ON; /* new state is on */ | |
102 | return (1); /* tell that data should be send */ | |
103 | } /* error log start and able to send */ | |
104 | if ((card->err_log_state == ERRLOG_STATE_STOP) && | |
105 | (maxlen >= ERRLOG_CMD_STOP_SIZE)) { | |
106 | strcpy(buf, ERRLOG_CMD_STOP); /* copy the command */ | |
107 | *len = ERRLOG_CMD_STOP_SIZE; /* buffer length */ | |
108 | *chan = CHAN_ERRLOG; /* and channel */ | |
109 | card->err_log_state = ERRLOG_STATE_OFF; /* new state is off */ | |
110 | return (1); /* tell that data should be send */ | |
111 | } /* error log start and able to send */ | |
112 | /* now handle network interface packets */ | |
113 | if ((hynet_enable & (1 << card->myid)) && | |
114 | (skb = hysdn_tx_netget(card)) != NULL) | |
115 | { | |
116 | if (skb->len <= maxlen) { | |
117 | memcpy(buf, skb->data, skb->len); /* copy the packet to the buffer */ | |
118 | *len = skb->len; | |
119 | *chan = CHAN_NDIS_DATA; | |
120 | card->net_tx_busy = 1; /* we are busy sending network data */ | |
121 | return (1); /* go and send the data */ | |
122 | } else | |
123 | hysdn_tx_netack(card); /* aknowledge packet -> throw away */ | |
124 | } /* send a network packet if available */ | |
125 | #ifdef CONFIG_HYSDN_CAPI | |
126 | if( ((hycapi_enable & (1 << card->myid))) && | |
127 | ((skb = hycapi_tx_capiget(card)) != NULL) ) | |
128 | { | |
129 | if (skb->len <= maxlen) { | |
130 | memcpy(buf, skb->data, skb->len); | |
131 | *len = skb->len; | |
132 | *chan = CHAN_CAPI; | |
133 | hycapi_tx_capiack(card); | |
134 | return (1); /* go and send the data */ | |
135 | } | |
136 | } | |
137 | #endif /* CONFIG_HYSDN_CAPI */ | |
138 | return (0); /* nothing to send */ | |
139 | } /* hysdn_sched_tx */ | |
140 | ||
141 | ||
142 | /*****************************************************************************/ | |
143 | /* send one config line to the card and return 0 if successful, otherwise a */ | |
144 | /* negative error code. */ | |
145 | /* The function works with timeouts perhaps not giving the greatest speed */ | |
146 | /* sending the line, but this should be meaningless beacuse only some lines */ | |
147 | /* are to be sent and this happens very seldom. */ | |
148 | /*****************************************************************************/ | |
149 | int | |
c721bcce | 150 | hysdn_tx_cfgline(hysdn_card *card, unsigned char *line, unsigned short chan) |
1da177e4 LT |
151 | { |
152 | int cnt = 50; /* timeout intervalls */ | |
c721bcce | 153 | unsigned long flags; |
1da177e4 LT |
154 | |
155 | if (card->debug_flags & LOG_SCHED_ASYN) | |
156 | hysdn_addlog(card, "async tx-cfg chan=%d len=%d", chan, strlen(line) + 1); | |
157 | ||
0d9ba869 | 158 | spin_lock_irqsave(&card->hysdn_lock, flags); |
1da177e4 LT |
159 | while (card->async_busy) { |
160 | sti(); | |
161 | ||
162 | if (card->debug_flags & LOG_SCHED_ASYN) | |
163 | hysdn_addlog(card, "async tx-cfg delayed"); | |
164 | ||
165 | msleep_interruptible(20); /* Timeout 20ms */ | |
166 | if (!--cnt) { | |
0d9ba869 | 167 | spin_unlock_irqrestore(&card->hysdn_lock, flags); |
1da177e4 LT |
168 | return (-ERR_ASYNC_TIME); /* timed out */ |
169 | } | |
170 | cli(); | |
171 | } /* wait for buffer to become free */ | |
172 | ||
173 | strcpy(card->async_data, line); | |
174 | card->async_len = strlen(line) + 1; | |
175 | card->async_channel = chan; | |
176 | card->async_busy = 1; /* request transfer */ | |
177 | ||
178 | /* now queue the task */ | |
179 | schedule_work(&card->irq_queue); | |
180 | sti(); | |
181 | ||
182 | if (card->debug_flags & LOG_SCHED_ASYN) | |
183 | hysdn_addlog(card, "async tx-cfg data queued"); | |
184 | ||
185 | cnt++; /* short delay */ | |
186 | cli(); | |
187 | ||
188 | while (card->async_busy) { | |
189 | sti(); | |
190 | ||
191 | if (card->debug_flags & LOG_SCHED_ASYN) | |
192 | hysdn_addlog(card, "async tx-cfg waiting for tx-ready"); | |
193 | ||
194 | msleep_interruptible(20); /* Timeout 20ms */ | |
195 | if (!--cnt) { | |
0d9ba869 | 196 | spin_unlock_irqrestore(&card->hysdn_lock, flags); |
1da177e4 LT |
197 | return (-ERR_ASYNC_TIME); /* timed out */ |
198 | } | |
199 | cli(); | |
200 | } /* wait for buffer to become free again */ | |
201 | ||
0d9ba869 | 202 | spin_unlock_irqrestore(&card->hysdn_lock, flags); |
1da177e4 LT |
203 | |
204 | if (card->debug_flags & LOG_SCHED_ASYN) | |
205 | hysdn_addlog(card, "async tx-cfg data send"); | |
206 | ||
207 | return (0); /* line send correctly */ | |
208 | } /* hysdn_tx_cfgline */ |