Merge from vendor branch ZLIB:
[dragonfly.git] / sys / dev / atm / hfa / fore_command.c
1 /*
2  *
3  * ===================================
4  * HARP  |  Host ATM Research Platform
5  * ===================================
6  *
7  *
8  * This Host ATM Research Platform ("HARP") file (the "Software") is
9  * made available by Network Computing Services, Inc. ("NetworkCS")
10  * "AS IS".  NetworkCS does not provide maintenance, improvements or
11  * support of any kind.
12  *
13  * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
14  * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
15  * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
16  * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
17  * In no event shall NetworkCS be responsible for any damages, including
18  * but not limited to consequential damages, arising from or relating to
19  * any use of the Software or related support.
20  *
21  * Copyright 1994-1998 Network Computing Services, Inc.
22  *
23  * Copies of this Software may be made, however, the above copyright
24  * notice must be reproduced on all copies.
25  *
26  *      @(#) $FreeBSD: src/sys/dev/hfa/fore_command.c,v 1.6 1999/08/28 00:41:49 peter Exp $
27  *      @(#) $DragonFly: src/sys/dev/atm/hfa/fore_command.c,v 1.3 2003/08/07 21:16:49 dillon Exp $
28  */
29
30 /*
31  * FORE Systems 200-Series Adapter Support
32  * ---------------------------------------
33  *
34  * Command queue management
35  *
36  */
37
38 #include "fore_include.h"
39
40 /*
41  * Local variables
42  */
43 static struct t_atm_cause       fore_cause = {
44         T_ATM_ITU_CODING,
45         T_ATM_LOC_USER,
46         T_ATM_CAUSE_TEMPORARY_FAILURE,
47         {0, 0, 0, 0}
48 };
49
50
51 /*
52  * Allocate Command Queue Data Structures
53  *
54  * Arguments:
55  *      fup             pointer to device unit structure
56  *
57  * Returns:
58  *      0               allocations successful
59  *      else            allocation failed
60  */
61 int
62 fore_cmd_allocate(fup)
63         Fore_unit       *fup;
64 {
65         caddr_t         memp;
66
67         /*
68          * Allocate non-cacheable memory for command status words
69          */
70         memp = atm_dev_alloc(sizeof(Q_status) * CMD_QUELEN,
71                         QSTAT_ALIGN, ATM_DEV_NONCACHE);
72         if (memp == NULL) {
73                 return (1);
74         }
75         fup->fu_cmd_stat = (Q_status *) memp;
76
77         memp = DMA_GET_ADDR(fup->fu_cmd_stat, sizeof(Q_status) * CMD_QUELEN,
78                         QSTAT_ALIGN, ATM_DEV_NONCACHE);
79         if (memp == NULL) {
80                 return (1);
81         }
82         fup->fu_cmd_statd = (Q_status *) memp;
83
84         /*
85          * Allocate memory for statistics buffer
86          */
87         memp = atm_dev_alloc(sizeof(Fore_stats), FORE_STATS_ALIGN, 0);
88         if (memp == NULL) {
89                 return (1);
90         }
91         fup->fu_stats = (Fore_stats *) memp;
92
93 #ifdef FORE_PCI
94         /*
95          * Allocate memory for PROM buffer
96          */
97         memp = atm_dev_alloc(sizeof(Fore_prom), FORE_PROM_ALIGN, 0);
98         if (memp == NULL) {
99                 return (1);
100         }
101         fup->fu_prom = (Fore_prom *) memp;
102 #endif
103
104         return (0);
105 }
106
107
108 /*
109  * Command Queue Initialization
110  *
111  * Allocate and initialize the host-resident command queue structures
112  * and then initialize the CP-resident queue structures.
113  * 
114  * Called at interrupt level.
115  *
116  * Arguments:
117  *      fup             pointer to device unit structure
118  *
119  * Returns:
120  *      none
121  */
122 void
123 fore_cmd_initialize(fup)
124         Fore_unit       *fup;
125 {
126         Aali            *aap = fup->fu_aali;
127         Cmd_queue       *cqp;
128         H_cmd_queue     *hcp;
129         Q_status        *qsp;
130         Q_status        *qsp_dma;
131         int             i;
132
133         /*
134          * Point to CP-resident command queue
135          */
136         cqp = (Cmd_queue *)(fup->fu_ram + CP_READ(aap->aali_cmd_q));
137
138         /*
139          * Point to host-resident command queue structures
140          */
141         hcp = fup->fu_cmd_q;
142         qsp = fup->fu_cmd_stat;
143         qsp_dma = fup->fu_cmd_statd;
144
145         /*
146          * Loop thru all queue entries and do whatever needs doing
147          */
148         for (i = 0; i < CMD_QUELEN; i++) {
149
150                 /*
151                  * Set queue status word to free
152                  */
153                 *qsp = QSTAT_FREE;
154
155                 /*
156                  * Set up host queue entry and link into ring
157                  */
158                 hcp->hcq_cpelem = cqp;
159                 hcp->hcq_status = qsp;
160                 if (i == (CMD_QUELEN - 1))
161                         hcp->hcq_next = fup->fu_cmd_q;
162                 else
163                         hcp->hcq_next = hcp + 1;
164
165                 /*
166                  * Now let the CP into the game
167                  */
168                 cqp->cmdq_status = (CP_dma) CP_WRITE(qsp_dma);
169
170                 /*
171                  * Bump all queue pointers
172                  */
173                 hcp++;
174                 qsp++;
175                 qsp_dma++;
176                 cqp++;
177         }
178
179         /*
180          * Initialize queue pointers
181          */
182         fup->fu_cmd_head = fup->fu_cmd_tail = fup->fu_cmd_q;
183
184         return;
185 }
186
187
188 /*
189  * Drain Command Queue
190  *
191  * This function will process and free all completed entries at the head 
192  * of the command queue. 
193  *
194  * May be called in interrupt state.  
195  * Must be called with interrupts locked out.
196  *
197  * Arguments:
198  *      fup             pointer to device unit structure
199  *
200  * Returns:
201  *      none
202  */
203 void
204 fore_cmd_drain(fup)
205         Fore_unit       *fup;
206 {
207         H_cmd_queue     *hcp;
208         Fore_vcc        *fvp;
209
210         /*
211          * Process each completed entry
212          */
213         while (*fup->fu_cmd_head->hcq_status & QSTAT_COMPLETED) {
214
215                 hcp = fup->fu_cmd_head;
216
217                 /*
218                  * Process command completion
219                  */
220                 switch (hcp->hcq_code) {
221
222                 case CMD_ACT_VCCIN:
223                 case CMD_ACT_VCCOUT:
224                         fvp = hcp->hcq_arg;
225                         if (*hcp->hcq_status & QSTAT_ERROR) {
226                                 /*
227                                  * VCC activation failed - just abort vcc
228                                  */
229                                 if (fvp)
230                                         atm_cm_abort(fvp->fv_connvc,
231                                                 &fore_cause);
232                                 fup->fu_pif.pif_cmderrors++;
233                         } else {
234                                 /*
235                                  * Successful VCC activation
236                                  */
237                                 if (fvp) {
238                                         fvp->fv_state = CVS_ACTIVE;
239                                         fup->fu_open_vcc++;
240                                 }
241                         }
242                         break;
243
244                 case CMD_DACT_VCCIN:
245                 case CMD_DACT_VCCOUT:
246                         fvp = hcp->hcq_arg;
247                         if (*hcp->hcq_status & QSTAT_ERROR) {
248                                 /*
249                                  * VCC dactivation failed - whine
250                                  */
251                                 log(LOG_ERR, 
252                                    "fore_cmd_drain: DACT failed, vcc=(%d,%d)\n",
253                                         fvp->fv_connvc->cvc_vcc->vc_vpi,
254                                         fvp->fv_connvc->cvc_vcc->vc_vci);
255                                 fup->fu_pif.pif_cmderrors++;
256                         } else {
257                                 /*
258                                  * Successful VCC dactivation - so what?
259                                  */
260                         }
261                         break;
262
263                 case CMD_GET_STATS:
264                         if (*hcp->hcq_status & QSTAT_ERROR) {
265                                 /*
266                                  * Couldn't get stats
267                                  */
268                                 fup->fu_pif.pif_cmderrors++;
269                                 fup->fu_stats_ret = EIO;
270                         } else {
271                                 /*
272                                  * Stats are now in unit buffer
273                                  */
274                                 fup->fu_stats_ret = 0;
275                         }
276                         DMA_FREE_ADDR(fup->fu_stats, fup->fu_statsd,
277                                 sizeof(Fore_cp_stats), 0);
278                         fup->fu_flags &= ~FUF_STATCMD;
279
280                         /*
281                          * Flush received stats data
282                          */
283 #ifdef VAC
284                         if (vac)
285                                 vac_pageflush((addr_t)fup->fu_stats);
286 #endif
287
288 #if BYTE_ORDER == LITTLE_ENDIAN
289                         /*
290                          * Little endian machines receives the stats in
291                          * wrong byte order. Instead of swapping in user
292                          * land, swap here so that everything going out
293                          * of the kernel is in correct host order.
294                          */
295                         {
296                                 u_long *bp = (u_long *)fup->fu_stats;
297                                 int     loop;
298
299                                 for ( loop = 0; loop < sizeof(Fore_cp_stats)/
300                                     sizeof(long); loop++, bp++ )
301                                         *bp = ntohl(*bp);
302                         }
303 #endif  /* BYTE_ORDER == LITTLE_ENDIAN */
304
305                         /*
306                          * Poke whoever is waiting on the stats
307                          */
308                         wakeup((caddr_t)&fup->fu_stats);
309                         break;
310
311 #ifdef FORE_PCI
312                 case CMD_GET_PROM:
313                         if (*hcp->hcq_status & QSTAT_ERROR) {
314                                 /*
315                                  * Couldn't get PROM data
316                                  */
317                                 fup->fu_pif.pif_cmderrors++;
318                                 log(LOG_ERR, 
319                                     "fore_cmd_drain: %s%d: GET_PROM failed\n",
320                                         fup->fu_pif.pif_name,
321                                         fup->fu_pif.pif_unit);
322                         } else {
323                                 Fore_prom       *fp = fup->fu_prom;
324
325                                 /*
326                                  * Flush received PROM data
327                                  */
328 #ifdef VAC
329                                 if (vac)
330                                         vac_pageflush((addr_t)fp);
331 #endif
332                                 /*
333                                  * Copy PROM info into config areas
334                                  */
335                                 KM_COPY(&fp->pr_mac[2],
336                                         &fup->fu_pif.pif_macaddr,
337                                         sizeof(struct mac_addr));
338                                 fup->fu_config.ac_macaddr = 
339                                         fup->fu_pif.pif_macaddr;
340                                 snprintf(fup->fu_config.ac_hard_vers,
341                                     sizeof(fup->fu_config.ac_hard_vers),
342                                         "%ld.%ld.%ld",
343                                         (fp->pr_hwver >> 16) & 0xff,
344                                         (fp->pr_hwver >> 8) & 0xff,
345                                         fp->pr_hwver & 0xff);
346                                 fup->fu_config.ac_serial = fp->pr_serno;
347                         }
348
349                         DMA_FREE_ADDR(fup->fu_prom, fup->fu_promd,
350                                 sizeof(Fore_prom), 0);
351                         break;
352 #endif  /* FORE_PCI */
353
354                 default:
355                         log(LOG_ERR, "fore_cmd_drain: unknown command %ld\n",
356                                 hcp->hcq_code);
357                 }
358
359                 /*
360                  * Mark this entry free for use and bump head pointer
361                  * to the next entry in the queue
362                  */
363                 *hcp->hcq_status = QSTAT_FREE;
364                 fup->fu_cmd_head = hcp->hcq_next;
365         }
366
367         return;
368 }
369
370
371 /*
372  * Free Command Queue Data Structures
373  *
374  * Arguments:
375  *      fup             pointer to device unit structure
376  *
377  * Returns:
378  *      none
379  */
380 void
381 fore_cmd_free(fup)
382         Fore_unit       *fup;
383 {
384         H_cmd_queue     *hcp;
385
386         /*
387          * Deal with any commands left on the queue
388          */
389         if (fup->fu_flags & CUF_INITED) {
390                 while (*fup->fu_cmd_head->hcq_status != QSTAT_FREE) {
391                         hcp = fup->fu_cmd_head;
392
393                         switch (hcp->hcq_code) {
394
395                         case CMD_GET_STATS:
396                                 /*
397                                  * Just in case someone is sleeping on this
398                                  */
399                                 fup->fu_stats_ret = EIO;
400                                 wakeup((caddr_t)&fup->fu_stats);
401                                 break;
402                         }
403
404                         *hcp->hcq_status = QSTAT_FREE;
405                         fup->fu_cmd_head = hcp->hcq_next;
406                 }
407         }
408
409         /*
410          * Free the statistics buffer
411          */
412         if (fup->fu_stats) {
413                 atm_dev_free(fup->fu_stats);
414                 fup->fu_stats = NULL;
415         }
416
417 #ifdef FORE_PCI
418         /*
419          * Free the PROM buffer
420          */
421         if (fup->fu_prom) {
422                 atm_dev_free(fup->fu_prom);
423                 fup->fu_prom = NULL;
424         }
425 #endif
426
427         /*
428          * Free the status words
429          */
430         if (fup->fu_cmd_stat) {
431                 if (fup->fu_cmd_statd) {
432                         DMA_FREE_ADDR(fup->fu_cmd_stat, fup->fu_cmd_statd,
433                                 sizeof(Q_status) * CMD_QUELEN,
434                                 ATM_DEV_NONCACHE);
435                 }
436                 atm_dev_free((volatile void *)fup->fu_cmd_stat);
437                 fup->fu_cmd_stat = NULL;
438                 fup->fu_cmd_statd = NULL;
439         }
440
441         return;
442 }
443