Ansify some remaining function definitions in the kernel.
[dragonfly.git] / sys / dev / atm / hea / eni_buffer.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/hea/eni_buffer.c,v 1.5 1999/08/28 00:41:43 peter Exp $
27  *      @(#) $DragonFly: src/sys/dev/atm/hea/eni_buffer.c,v 1.7 2008/03/01 22:03:13 swildner Exp $
28  */
29
30 /*
31  * Efficient ENI Adapter Support
32  * -----------------------------
33  *
34  * Handle adapter memory buffers for ENI adapters
35  *
36  */
37
38 #include <netproto/atm/kern_include.h>
39
40 #include "eni_stats.h"
41 #include "eni.h"
42 #include "eni_var.h"
43
44 static int      eni_test_memory (Eni_unit *);
45
46 /*
47  * The host is going to manage (that is, allocate and free) buffers
48  * in the adapters RAM space. We are going to implement this as a
49  * linked list describing FREE and INUSE memory segments. Initially,
50  * the list contains one element with all memory marked free. As requests
51  * are made, we search the list until we find the first free element
52  * which can satisfy the request. If necessary, we will break the free
53  * element into an INUSE element, and a new FREE element. When freeing
54  * memory, we look at adjacent elements and if one or more are free,
55  * we will combine into a single larger FREE element.
56  */
57
58 /*
59  * This is for testing purposes. Since there are two versions of
60  * the Efficient adapter with different memory sizes, this allows
61  * us to fool an adapter with more memory into thinking it has less.
62  */
63 static int eni_mem_max = MAX_ENI_MEM;   /* Default to all available memory */
64
65 /*
66  * Size and test adapter RAM
67  *
68  * Walk through adapter RAM writing known patterns and reading back
69  * for comparison. We write more than one pattern on the off chance
70  * that we "get lucky" and read what we expected.
71  *
72  * Arguments:
73  *      eup             pointer to device unit structure
74  *
75  * Returns
76  *      size            memory size in bytes
77  */
78 static int
79 eni_test_memory(Eni_unit *eup)
80 {
81         int     ram_size = 0;
82         int     i;
83         Eni_mem mp;
84
85         /*
86          * Walk through to maximum looking for RAM
87          */
88         for ( i = 0; i < MAX_ENI_MEM; i += TEST_STEP ) {
89                 mp = (Eni_mem)((int)eup->eu_ram + i);
90                 /* write pattern */
91                 *mp = (u_long)TEST_PAT;
92                 /* read pattern, match? */
93                 if ( *mp == (u_long)TEST_PAT ) {
94                         /* yes - write inverse pattern */
95                         *mp = (u_long)~TEST_PAT;
96                         /* read pattern, match? */
97                         if ( *mp == (u_long)~TEST_PAT ) {
98                                 /* yes - assume another 1K available */
99                                 ram_size = i + TEST_STEP;
100                         } else
101                             break;
102                 } else
103                         break;
104         }
105         /*
106          * Clear all RAM to initial value of zero.
107          * This makes sure we don't leave anything funny in the
108          * queues.
109          */
110         KM_ZERO ( eup->eu_ram, ram_size );
111
112         /*
113          * If we'd like to claim to have less memory, here's where
114          * we do so. We take the minimum of what we'd like and what
115          * we really found on the adapter.
116          */
117         ram_size = MIN ( ram_size, eni_mem_max ); 
118
119         return ( ram_size );
120
121 }
122
123 /*
124  * Initialize our memory allocator.
125  *
126  * Arguments:
127  *      eup             Pointer to per unit structure
128  *
129  * Returns:
130  *      size            Physical RAM size
131  *      -1              failed to initialize memory
132  *
133  */
134 int
135 eni_init_memory(Eni_unit *eup)
136 {
137
138         /*
139          * Have we (somehow) been called before?
140          */
141         if ( eup->eu_memmap != NULL )
142         {
143                 /* Oops  - it's already been initialized */
144                 return -1;
145         }
146
147         /*
148          * Allocate initial element which will hold all of memory
149          */
150         eup->eu_memmap = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, M_WAITOK);
151
152         /*
153          * Test and size memory
154          */
155         eup->eu_ramsize = eni_test_memory ( eup );
156
157         /*
158          * Initialize a one element list which contains
159          * all buffer memory
160          */
161         eup->eu_memmap->prev = eup->eu_memmap->next = NULL;
162         eup->eu_memmap->base = (caddr_t)SEGBUF_BASE;
163         eup->eu_memmap->size = eup->eu_ramsize - SEGBUF_BASE;
164         eup->eu_memmap->state = MEM_FREE;
165
166         return ( eup->eu_ramsize );
167 }
168
169 /*
170  * Allocate a buffer from adapter RAM. Due to constraints on the card,
171  * we may roundup the size request to the next largest chunksize. Note
172  * also that we must pay attention to address alignment within adapter
173  * memory as well.
174  *
175  * Arguments:
176  *      eup             pointer to per unit structure
177  *      size            pointer to requested size - in bytes
178  *
179  * Returns:
180  *      addr            address relative to adapter of allocated memory
181  *      size            modified to reflect actual size of buffer
182  *
183  */
184 caddr_t
185 eni_allocate_buffer(Eni_unit *eup, u_long *size)
186 {
187         int     nsize;
188         int     nclicks;
189         Mbd     *eptr = eup->eu_memmap;
190
191         /*
192          * Initial size requested
193          */
194         nsize = *size;
195
196         /*
197          * Find the buffer size which will hold this request. There
198          * are 8 possible sizes, each a power of two up, starting at
199          * 256 words or 1024 bytes.
200          */
201         for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ )
202                 if ( ( 1 << nclicks ) * ENI_BUF_PGSZ >= nsize )
203                         break;
204
205         /*
206          * Request was for larger then the card supports
207          */
208         if ( nclicks >= ENI_BUF_NBIT ) {
209                 eup->eu_stats.eni_st_drv.drv_mm_toobig++;
210                 /* Indicate 0 bytes allocated */
211                 *size = 0;
212                 /* Return NULL buffer */
213                 return ( (caddr_t)NULL );
214         }
215
216         /*
217          * New size will be buffer size
218          */
219         nsize = ( 1 << nclicks ) * ENI_BUF_PGSZ;
220
221         /*
222          * Look through memory for a segment large enough to
223          * hold request
224          */
225         while ( eptr ) {
226             /*
227              * State must be FREE and size must hold request
228              */
229             if ( eptr->state == MEM_FREE && eptr->size >= nsize )
230             {
231                 /*
232                  * Request will fit - now check if the
233                  * alignment needs fixing
234                  */
235                 if ( ((u_int)eptr->base & (nsize-1)) != 0 )
236                 {
237                     caddr_t     nbase;
238
239                     /*
240                      * Calculate where the buffer would have to
241                      * fall to be aligned.
242                      */
243                     nbase = (caddr_t)((u_int)( eptr->base + nsize ) &
244                         ~(nsize-1));
245                     /*
246                      * If we use this alignment, will it still fit?
247                      */
248                     if ( (eptr->size - (nbase - eptr->base)) >= 0 )
249                     {
250                         Mbd     *etmp;
251
252                         /* Yep - create a new segment */
253                         etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, M_WAITOK);
254                         /* Place it in the list */
255                         etmp->next = eptr->next;
256                         if ( etmp->next )
257                             etmp->next->prev = etmp;
258                         etmp->prev = eptr;
259                         eptr->next = etmp;
260                         /* Fill in new base and size */
261                         etmp->base = nbase;
262                         etmp->size = eptr->size - ( nbase - eptr->base );
263                         /* Adjust old size */
264                         eptr->size -= etmp->size;
265                         /* Mark its state */
266                         etmp->state = MEM_FREE;
267                         eptr = etmp;
268                         /* Done - outa here */
269                         break;
270                     }
271                 } else
272                     break;              /* Alignment is okay  - we're done */
273             }
274             /* Haven't found anything yet - keep looking */
275             eptr = eptr->next;
276         }
277
278         if ( eptr != NULL )
279         {
280             /* Found a usable segment - grab what we need */
281             /* Exact fit? */
282             if ( eptr->size == nsize )
283                 /* Mark it as INUSE */
284                 eptr->state = MEM_INUSE;
285             else
286             {
287                 Mbd     *etmp;
288                 /* larger then we need - split it */
289
290                 etmp = (Mbd *)KM_ALLOC(sizeof(Mbd), M_DEVBUF, M_WAITOK);
291                 /* Place new element in list */
292                 etmp->next = eptr->next;
293                 if ( etmp->next )
294                     etmp->next->prev = etmp;
295                 etmp->prev = eptr;
296                 eptr->next = etmp;
297                 /* Set new base, size and state */
298                 etmp->base = eptr->base + nsize;
299                 etmp->size = eptr->size - nsize;
300                 etmp->state = MEM_FREE;
301                 /* Adjust size and state of element we intend to use */
302                 eptr->size = nsize;
303                 eptr->state = MEM_INUSE;
304             }
305         }
306
307         /* After all that, did we find a usable buffer? */
308         if ( eptr )
309         {
310                 /* Record another inuse buffer of this size */
311                 if ( eptr->base )
312                         eup->eu_memclicks[nclicks]++;
313
314                 /*
315                  * Return true size of allocated buffer
316                  */
317                 *size = eptr->size;
318                 /*
319                  * Make address relative to start of RAM since
320                  * its (the address) for use by the adapter, not
321                  * the host.
322                  */
323                 return ((caddr_t)eptr->base);
324         } else {
325                 eup->eu_stats.eni_st_drv.drv_mm_nobuf++;
326                 /* No buffer to return - indicate zero length */
327                 *size = 0;
328                 /* Return NULL buffer */
329                 return ( (caddr_t)NULL );
330         }
331 }
332
333 /*
334  * Procedure to release a buffer previously allocated from adapter
335  * RAM. When possible, we'll compact memory.
336  *
337  * Arguments:
338  *      eup             pointer to per unit structure
339  *      base            base adapter address of buffer to be freed
340  *
341  * Returns:
342  *      none
343  *
344  */
345 void
346 eni_free_buffer(Eni_unit *eup, caddr_t base)
347 {
348         Mbd     *eptr = eup->eu_memmap;
349         int     nclicks;
350
351         /* Look through entire list */
352         while ( eptr )
353         {
354                 /* Is this the buffer to be freed? */
355                 if ( eptr->base == base )
356                 {
357                         /*
358                          * We're probably asking for trouble but,
359                          * assume this is it.
360                          */
361                         if ( eptr->state != MEM_INUSE )
362                         {
363                                 eup->eu_stats.eni_st_drv.drv_mm_notuse++;
364                                 /* Huh? Something's wrong */
365                                 return;
366                         }
367                         /* Reset state to FREE */
368                         eptr->state = MEM_FREE;
369
370                         /* Determine size for stats info */
371                         for ( nclicks = 0; nclicks < ENI_BUF_NBIT; nclicks++ )
372                             if ( ( 1 << nclicks ) * ENI_BUF_PGSZ == eptr->size )
373                                 break;
374
375                         /* Valid size? Yes - decrement inuse count */
376                         if ( nclicks < ENI_BUF_NBIT )
377                                 eup->eu_memclicks[nclicks]--;
378
379                         /* Try to compact neighbors */
380                         /* with previous */
381                         if ( eptr->prev )
382                             if ( eptr->prev->state == MEM_FREE )
383                             {
384                                 Mbd     *etmp = eptr;
385                                 /* Add to previous block */
386                                 eptr->prev->size += eptr->size;
387                                 /* Set prev block to skip this one */
388                                 eptr->prev->next = eptr->next;
389                                 /* Set next block to skip this one */
390                                 if ( eptr->next )
391                                         eptr->next->prev = eptr->prev;
392                                 /* Reset to where we want to be */
393                                 eptr = eptr->prev;
394                                 /* and free this element */
395                                 (void)KM_FREE(etmp, etmp->size, M_DEVBUF);
396                             }
397                         /* with next */
398                         if ( eptr->next )
399                             if ( eptr->next->state == MEM_FREE )
400                             {
401                                 Mbd     *etmp = eptr->next;
402
403                                 /* add following block in */
404                                 eptr->size += etmp->size;
405                                 /* set next next block to skip next block */
406                                 if ( etmp->next )
407                                         etmp->next->prev = eptr;
408                                 /* skip next block */
409                                 eptr->next = etmp->next;
410                                 /* and free next element */
411                                 (void)KM_FREE(etmp, etmp->size, M_DEVBUF);
412                             }
413                         /*
414                          * We've freed the buffer and done any compaction,
415                          * we needn't look any further...
416                          */
417                         return;
418                 }
419                 eptr = eptr->next;
420         }
421
422         if ( eptr == NULL )
423         {
424                 /* Oops - failed to find the buffer. This is BAD */
425                 eup->eu_stats.eni_st_drv.drv_mm_notfnd++;
426         }
427
428 }
429