| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | 
| Andrew Morton | c721bcc | 2006-03-25 03:07:04 -0800 | [diff] [blame] | 31 | hysdn_sched_rx(hysdn_card *card, unsigned char *buf, unsigned short len, | 
|  | 32 | unsigned short chan) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | 
| Andrew Morton | c721bcc | 2006-03-25 03:07:04 -0800 | [diff] [blame] | 74 | hysdn_sched_tx(hysdn_card *card, unsigned char *buf, | 
|  | 75 | unsigned short volatile *len, unsigned short volatile *chan, | 
|  | 76 | unsigned short maxlen) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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) { | 
| Arnaldo Carvalho de Melo | d626f62 | 2007-03-27 18:55:52 -0300 | [diff] [blame] | 116 | /* copy the packet to the buffer */ | 
|  | 117 | skb_copy_from_linear_data(skb, buf, skb->len); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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) { | 
| Arnaldo Carvalho de Melo | d626f62 | 2007-03-27 18:55:52 -0300 | [diff] [blame] | 130 | skb_copy_from_linear_data(skb, buf, skb->len); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | 
| Andrew Morton | c721bcc | 2006-03-25 03:07:04 -0800 | [diff] [blame] | 150 | hysdn_tx_cfgline(hysdn_card *card, unsigned char *line, unsigned short chan) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 151 | { | 
|  | 152 | int cnt = 50;		/* timeout intervalls */ | 
| Andrew Morton | c721bcc | 2006-03-25 03:07:04 -0800 | [diff] [blame] | 153 | unsigned long flags; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 158 | while (card->async_busy) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 159 |  | 
|  | 160 | if (card->debug_flags & LOG_SCHED_ASYN) | 
|  | 161 | hysdn_addlog(card, "async tx-cfg delayed"); | 
|  | 162 |  | 
|  | 163 | msleep_interruptible(20);		/* Timeout 20ms */ | 
| Amol Lad | 1f604c4 | 2006-11-02 22:07:25 -0800 | [diff] [blame] | 164 | if (!--cnt) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 165 | return (-ERR_ASYNC_TIME);	/* timed out */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 166 | }			/* wait for buffer to become free */ | 
|  | 167 |  | 
| Amol Lad | 1f604c4 | 2006-11-02 22:07:25 -0800 | [diff] [blame] | 168 | spin_lock_irqsave(&card->hysdn_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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); | 
| Amol Lad | 1f604c4 | 2006-11-02 22:07:25 -0800 | [diff] [blame] | 176 | spin_unlock_irqrestore(&card->hysdn_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 177 |  | 
|  | 178 | if (card->debug_flags & LOG_SCHED_ASYN) | 
|  | 179 | hysdn_addlog(card, "async tx-cfg data queued"); | 
|  | 180 |  | 
|  | 181 | cnt++;			/* short delay */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 182 |  | 
|  | 183 | while (card->async_busy) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 */ | 
| Amol Lad | 1f604c4 | 2006-11-02 22:07:25 -0800 | [diff] [blame] | 189 | if (!--cnt) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 190 | return (-ERR_ASYNC_TIME);	/* timed out */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 191 | }			/* wait for buffer to become free again */ | 
|  | 192 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 */ |