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/signal.h> |
15 | #include <linux/kernel.h> | |
16 | #include <linux/ioport.h> | |
17 | #include <linux/interrupt.h> | |
18 | #include <linux/delay.h> | |
19 | #include <asm/io.h> | |
20 | ||
21 | #include "hysdn_defs.h" | |
22 | ||
23 | /*****************************************************************************/ | |
24 | /* hysdn_sched_rx is called from the cards handler to announce new data is */ | |
25 | /* available from the card. The routine has to handle the data and return */ | |
26 | /* with a nonzero code if the data could be worked (or even thrown away), if */ | |
27 | /* no room to buffer the data is available a zero return tells the card */ | |
28 | /* to keep the data until later. */ | |
29 | /*****************************************************************************/ | |
30 | int | |
c721bcce AM |
31 | hysdn_sched_rx(hysdn_card *card, unsigned char *buf, unsigned short len, |
32 | unsigned short chan) | |
1da177e4 LT |
33 | { |
34 | ||
35 | switch (chan) { | |
36 | case CHAN_NDIS_DATA: | |
37 | if (hynet_enable & (1 << card->myid)) { | |
38 | /* give packet to network handler */ | |
39 | hysdn_rx_netpkt(card, buf, len); | |
40 | } | |
41 | break; | |
42 | ||
43 | case CHAN_ERRLOG: | |
44 | hysdn_card_errlog(card, (tErrLogEntry *) buf, len); | |
45 | if (card->err_log_state == ERRLOG_STATE_ON) | |
46 | card->err_log_state = ERRLOG_STATE_START; /* start new fetch */ | |
47 | break; | |
48 | #ifdef CONFIG_HYSDN_CAPI | |
49 | case CHAN_CAPI: | |
50 | /* give packet to CAPI handler */ | |
51 | if (hycapi_enable & (1 << card->myid)) { | |
52 | hycapi_rx_capipkt(card, buf, len); | |
53 | } | |
54 | break; | |
55 | #endif /* CONFIG_HYSDN_CAPI */ | |
56 | default: | |
57 | printk(KERN_INFO "irq message channel %d len %d unhandled \n", chan, len); | |
58 | break; | |
59 | ||
60 | } /* switch rx channel */ | |
61 | ||
62 | return (1); /* always handled */ | |
63 | } /* hysdn_sched_rx */ | |
64 | ||
65 | /*****************************************************************************/ | |
66 | /* hysdn_sched_tx is called from the cards handler to announce that there is */ | |
67 | /* room in the tx-buffer to the card and data may be sent if needed. */ | |
68 | /* If the routine wants to send data it must fill buf, len and chan with the */ | |
69 | /* appropriate data and return a nonzero value. With a zero return no new */ | |
70 | /* data to send is assumed. maxlen specifies the buffer size available for */ | |
71 | /* sending. */ | |
72 | /*****************************************************************************/ | |
73 | int | |
c721bcce AM |
74 | hysdn_sched_tx(hysdn_card *card, unsigned char *buf, |
75 | unsigned short volatile *len, unsigned short volatile *chan, | |
76 | unsigned short maxlen) | |
1da177e4 LT |
77 | { |
78 | struct sk_buff *skb; | |
79 | ||
80 | if (card->net_tx_busy) { | |
81 | card->net_tx_busy = 0; /* reset flag */ | |
82 | hysdn_tx_netack(card); /* acknowledge packet send */ | |
83 | } /* a network packet has completely been transferred */ | |
84 | /* first of all async requests are handled */ | |
85 | if (card->async_busy) { | |
86 | if (card->async_len <= maxlen) { | |
87 | memcpy(buf, card->async_data, card->async_len); | |
88 | *len = card->async_len; | |
89 | *chan = card->async_channel; | |
90 | card->async_busy = 0; /* reset request */ | |
91 | return (1); | |
92 | } | |
93 | card->async_busy = 0; /* in case of length error */ | |
94 | } /* async request */ | |
95 | if ((card->err_log_state == ERRLOG_STATE_START) && | |
96 | (maxlen >= ERRLOG_CMD_REQ_SIZE)) { | |
97 | strcpy(buf, ERRLOG_CMD_REQ); /* copy the command */ | |
98 | *len = ERRLOG_CMD_REQ_SIZE; /* buffer length */ | |
99 | *chan = CHAN_ERRLOG; /* and channel */ | |
100 | card->err_log_state = ERRLOG_STATE_ON; /* new state is on */ | |
101 | return (1); /* tell that data should be send */ | |
102 | } /* error log start and able to send */ | |
103 | if ((card->err_log_state == ERRLOG_STATE_STOP) && | |
104 | (maxlen >= ERRLOG_CMD_STOP_SIZE)) { | |
105 | strcpy(buf, ERRLOG_CMD_STOP); /* copy the command */ | |
106 | *len = ERRLOG_CMD_STOP_SIZE; /* buffer length */ | |
107 | *chan = CHAN_ERRLOG; /* and channel */ | |
108 | card->err_log_state = ERRLOG_STATE_OFF; /* new state is off */ | |
109 | return (1); /* tell that data should be send */ | |
110 | } /* error log start and able to send */ | |
111 | /* now handle network interface packets */ | |
112 | if ((hynet_enable & (1 << card->myid)) && | |
113 | (skb = hysdn_tx_netget(card)) != NULL) | |
114 | { | |
115 | if (skb->len <= maxlen) { | |
d626f62b ACM |
116 | /* copy the packet to the buffer */ |
117 | skb_copy_from_linear_data(skb, buf, skb->len); | |
1da177e4 LT |
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) { | |
d626f62b | 130 | skb_copy_from_linear_data(skb, buf, skb->len); |
1da177e4 LT |
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 */ | |
25985edc | 146 | /* sending the line, but this should be meaningless because only some lines */ |
1da177e4 LT |
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 | ||
1da177e4 | 158 | while (card->async_busy) { |
1da177e4 LT |
159 | |
160 | if (card->debug_flags & LOG_SCHED_ASYN) | |
161 | hysdn_addlog(card, "async tx-cfg delayed"); | |
162 | ||
163 | msleep_interruptible(20); /* Timeout 20ms */ | |
1f604c4b | 164 | if (!--cnt) |
1da177e4 | 165 | return (-ERR_ASYNC_TIME); /* timed out */ |
1da177e4 LT |
166 | } /* wait for buffer to become free */ |
167 | ||
1f604c4b | 168 | spin_lock_irqsave(&card->hysdn_lock, flags); |
1da177e4 LT |
169 | strcpy(card->async_data, line); |
170 | card->async_len = strlen(line) + 1; | |
171 | card->async_channel = chan; | |
172 | card->async_busy = 1; /* request transfer */ | |
173 | ||
174 | /* now queue the task */ | |
175 | schedule_work(&card->irq_queue); | |
1f604c4b | 176 | spin_unlock_irqrestore(&card->hysdn_lock, flags); |
1da177e4 LT |
177 | |
178 | if (card->debug_flags & LOG_SCHED_ASYN) | |
179 | hysdn_addlog(card, "async tx-cfg data queued"); | |
180 | ||
181 | cnt++; /* short delay */ | |
1da177e4 LT |
182 | |
183 | while (card->async_busy) { | |
1da177e4 LT |
184 | |
185 | if (card->debug_flags & LOG_SCHED_ASYN) | |
186 | hysdn_addlog(card, "async tx-cfg waiting for tx-ready"); | |
187 | ||
188 | msleep_interruptible(20); /* Timeout 20ms */ | |
1f604c4b | 189 | if (!--cnt) |
1da177e4 | 190 | return (-ERR_ASYNC_TIME); /* timed out */ |
1da177e4 LT |
191 | } /* wait for buffer to become free again */ |
192 | ||
1da177e4 LT |
193 | if (card->debug_flags & LOG_SCHED_ASYN) |
194 | hysdn_addlog(card, "async tx-cfg data send"); | |
195 | ||
196 | return (0); /* line send correctly */ | |
197 | } /* hysdn_tx_cfgline */ |