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 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/*****************************************************************************/ 30int 31hysdn_sched_rx(hysdn_card *card, unsigned char *buf, unsigned short len, 32 unsigned short chan) 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/*****************************************************************************/ 73int 74hysdn_sched_tx(hysdn_card *card, unsigned char *buf, 75 unsigned short volatile *len, unsigned short volatile *chan, 76 unsigned short maxlen) 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) { 116 /* copy the packet to the buffer */ 117 skb_copy_from_linear_data(skb, buf, skb->len); 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 skb_copy_from_linear_data(skb, buf, 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 because only some lines */ 147/* are to be sent and this happens very seldom. */ 148/*****************************************************************************/ 149int 150hysdn_tx_cfgline(hysdn_card *card, unsigned char *line, unsigned short chan) 151{ 152 int cnt = 50; /* timeout intervalls */ 153 unsigned long flags; 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 158 while (card->async_busy) { 159 160 if (card->debug_flags & LOG_SCHED_ASYN) 161 hysdn_addlog(card, "async tx-cfg delayed"); 162 163 msleep_interruptible(20); /* Timeout 20ms */ 164 if (!--cnt) 165 return (-ERR_ASYNC_TIME); /* timed out */ 166 } /* wait for buffer to become free */ 167 168 spin_lock_irqsave(&card->hysdn_lock, flags); 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); 176 spin_unlock_irqrestore(&card->hysdn_lock, flags); 177 178 if (card->debug_flags & LOG_SCHED_ASYN) 179 hysdn_addlog(card, "async tx-cfg data queued"); 180 181 cnt++; /* short delay */ 182 183 while (card->async_busy) { 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 */ 189 if (!--cnt) 190 return (-ERR_ASYNC_TIME); /* timed out */ 191 } /* wait for buffer to become free again */ 192 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 */ 198