Loading...
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 198 199 200 201 202 | /* $Id: hysdn_sched.c,v 1.1 2000/02/10 19:45:18 werner Exp $ * Linux driver for HYSDN cards, scheduler routines for handling exchange card <-> pc. * * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH * * Copyright 1999 by Werner Cornelius (werner@titro.de) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Log: hysdn_sched.c,v $ * Revision 1.1 2000/02/10 19:45:18 werner * * Initial release * * */ #define __NO_VERSION__ #include <linux/module.h> #include <linux/version.h> #include <asm/io.h> #include <linux/signal.h> #include <linux/kernel.h> #include <linux/ioport.h> #include <linux/interrupt.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, uchar * buf, word len, word chan) { switch (chan) { case CHAN_NDIS_DATA: hysdn_rx_netpkt(card, buf, len); /* give packet to network handler */ 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; 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, uchar * buf, word volatile *len, word volatile *chan, word 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 ((skb = hysdn_tx_netget(card)) != NULL) { if (skb->len <= maxlen) { memcpy(buf, skb->data, skb->len); /* copy the packet to the buffer */ *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 */ return (0); /* nothing to send */ } /* hysdn_sched_tx */ /*****************************************************************************/ /* send one config line to the card and return 0 if successfull, otherwise a */ /* negative error code. */ /* The function works with timeouts perhaps not giving the greatest speed */ /* sending the line, but this should be meaningless beacuse only some lines */ /* are to be sent and this happens very seldom. */ /*****************************************************************************/ int hysdn_tx_cfgline(hysdn_card * card, uchar * line, word chan) { int cnt = 50; /* timeout intervalls */ ulong flags; if (card->debug_flags & LOG_SCHED_ASYN) hysdn_addlog(card, "async tx-cfg chan=%d len=%d", chan, strlen(line) + 1); save_flags(flags); cli(); while (card->async_busy) { sti(); if (card->debug_flags & LOG_SCHED_ASYN) hysdn_addlog(card, "async tx-cfg delayed"); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */ if (!--cnt) { restore_flags(flags); return (-ERR_ASYNC_TIME); /* timed out */ } cli(); } /* wait for buffer to become free */ 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 */ queue_task(&card->irq_queue, &tq_immediate); mark_bh(IMMEDIATE_BH); sti(); if (card->debug_flags & LOG_SCHED_ASYN) hysdn_addlog(card, "async tx-cfg data queued"); cnt++; /* short delay */ cli(); while (card->async_busy) { sti(); if (card->debug_flags & LOG_SCHED_ASYN) hysdn_addlog(card, "async tx-cfg waiting for tx-ready"); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */ if (!--cnt) { restore_flags(flags); return (-ERR_ASYNC_TIME); /* timed out */ } cli(); } /* wait for buffer to become free again */ restore_flags(flags); if (card->debug_flags & LOG_SCHED_ASYN) hysdn_addlog(card, "async tx-cfg data send"); return (0); /* line send correctly */ } /* hysdn_tx_cfgline */ |