linux/drivers/isdn/hysdn/hysdn_sched.c
<<
>>
Prefs
   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