source: GPL/branches/uniaud32-next/lib32/misc.c@ 660

Last change on this file since 660 was 639, checked in by David Azarewicz, 5 years ago

Fixed more warnings.

File size: 25.3 KB
Line 
1/* $Id: misc.c,v 1.1.1.1 2003/07/02 13:57:02 eleph Exp $ */
2/*
3 * OS/2 implementation of misc. Linux kernel services
4 *
5 * (C) 2000-2002 InnoTek Systemberatung GmbH
6 * (C) 2000-2001 Sander van Leeuwen (sandervl@xs4all.nl)
7 *
8 * hweight32 based on Linux code (bitops.h)
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of
13 * the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public
21 * License along with this program; if not, write to the Free
22 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
23 * USA.
24 *
25 */
26#include <limits.h>
27#include "linux.h"
28#include <linux/init.h>
29#include <linux/fs.h>
30#include <linux/poll.h>
31#define CONFIG_PROC_FS 1
32#include <linux/proc_fs.h>
33#include <asm/uaccess.h>
34#include <asm/hardirq.h>
35#include <linux\ioport.h>
36#include <linux\utsname.h>
37#include <linux\module.h>
38#include <linux/workqueue.h>
39#include <linux/firmware.h>
40#include <linux/ctype.h>
41#include <dbgos2.h>
42
43void StringOut(char *DbgStr);
44
45struct new_utsname system_utsname = {0};
46struct resource ioport_resource = {NULL, 0, 0, IORESOURCE_IO, NULL, NULL, NULL};
47struct resource iomem_resource = {NULL, 0, 0, IORESOURCE_MEM, NULL, NULL, NULL};
48mem_map_t *mem_map = 0;
49int this_module[64] = {0};
50
51#include <stdarg.h>
52
53char szLastALSAError1[128] = {0};
54char szOverrunTest1 = 0xCC;
55char szLastALSAError2[128] = {0};
56char szOverrunTest2 = 0xCC;
57int iLastError = 0;
58
59//******************************************************************************
60//Save error message in szLastALSAError; if card init failed, then we will
61//print it in drv32\init.cpp
62//******************************************************************************
63int printk(const char * fmt, ...)
64{
65 va_list argptr; /* -> variable argument list */
66
67 char *pszLastALSAError;
68
69 pszLastALSAError= iLastError ? szLastALSAError2 : szLastALSAError1;
70
71 va_start(argptr, fmt); /* get pointer to argument list */
72 vsprintf(pszLastALSAError, fmt, argptr);
73// strcat(pszLastALSAError, "\r");
74 va_end(argptr); /* done with variable arguments */
75
76 if(szOverrunTest1 != 0xCC || szOverrunTest2 != 0xCC) {
77 DebugInt3();
78 }
79
80 StringOut(pszLastALSAError);
81// rprintf( (pszLastALSAError) );
82 if(++iLastError > 1) {
83 iLastError = 0;
84 }
85 return 0;
86}
87
88//******************************************************************************
89//Save error message in szLastALSAError; if card init failed, then we will
90//print it in drv32\init.cpp
91//******************************************************************************
92int dev_dbg(const struct device *dev, const char * fmt, ...)
93{
94 va_list argptr; /* -> variable argument list */
95
96 char *pszLastALSAError;
97
98 pszLastALSAError= iLastError ? szLastALSAError2 : szLastALSAError1;
99
100 va_start(argptr, fmt); /* get pointer to argument list */
101 vsprintf(pszLastALSAError, fmt, argptr);
102// strcat(pszLastALSAError, "\r");
103 va_end(argptr); /* done with variable arguments */
104
105 if(szOverrunTest1 != 0xCC || szOverrunTest2 != 0xCC) {
106 DebugInt3();
107 }
108
109 StringOut(pszLastALSAError);
110// rprintf( (pszLastALSAError) );
111 if(++iLastError > 1) {
112 iLastError = 0;
113 }
114 return 0;
115}
116//******************************************************************************
117int pcm_dbg(struct snd_pcm *dev, const char * fmt, ...)
118{
119 va_list argptr; /* -> variable argument list */
120
121 char *pszLastALSAError;
122
123 pszLastALSAError= iLastError ? szLastALSAError2 : szLastALSAError1;
124
125 va_start(argptr, fmt); /* get pointer to argument list */
126 vsprintf(pszLastALSAError, fmt, argptr);
127// strcat(pszLastALSAError, "\r");
128 va_end(argptr); /* done with variable arguments */
129
130 if(szOverrunTest1 != 0xCC || szOverrunTest2 != 0xCC) {
131 DebugInt3();
132 }
133
134 StringOut(pszLastALSAError);
135// rprintf( (pszLastALSAError) );
136 if(++iLastError > 1) {
137 iLastError = 0;
138 }
139 return 0;
140}
141//******************************************************************************
142int codec_dbg(struct hda_codec *dev, const char * fmt, ...)
143{
144 va_list argptr; /* -> variable argument list */
145
146 char *pszLastALSAError;
147
148 pszLastALSAError= iLastError ? szLastALSAError2 : szLastALSAError1;
149
150 va_start(argptr, fmt); /* get pointer to argument list */
151 vsprintf(pszLastALSAError, fmt, argptr);
152// strcat(pszLastALSAError, "\r");
153 va_end(argptr); /* done with variable arguments */
154
155 if(szOverrunTest1 != 0xCC || szOverrunTest2 != 0xCC) {
156 DebugInt3();
157 }
158
159 StringOut(pszLastALSAError);
160// rprintf( (pszLastALSAError) );
161 if(++iLastError > 1) {
162 iLastError = 0;
163 }
164 return 0;
165}
166//******************************************************************************
167//******************************************************************************
168void schedule(void)
169{
170
171}
172//******************************************************************************
173//******************************************************************************
174void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
175{
176
177}
178//******************************************************************************
179//******************************************************************************
180int __check_region(struct resource *a, unsigned long b, unsigned long c)
181{
182 DebugInt3();
183 return 0;
184}
185
186/* --------------------------------------------------------------------- */
187/*
188 * hweightN: returns the hamming weight (i.e. the number
189 * of bits set) of a N-bit word
190 */
191
192#ifdef hweight32
193#undef hweight32
194#endif
195
196unsigned int hweight32(unsigned int w)
197{
198 unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
199 res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
200 res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
201 res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
202 return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
203}
204//******************************************************************************
205//******************************************************************************
206#if 0
207mem_map_t *virt_to_page(int x)
208{
209 static mem_map_t map = {0};
210 return &map;
211}
212#endif
213//******************************************************************************
214//******************************************************************************
215struct proc_dir_entry proc_root = {0};
216//******************************************************************************
217//******************************************************************************
218struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
219 struct proc_dir_entry *parent)
220{
221 struct proc_dir_entry *proc;
222 proc = (struct proc_dir_entry *)kmalloc(sizeof(struct proc_dir_entry), 0);
223 memset(proc, 0, sizeof(struct proc_dir_entry));
224
225 proc->name = name;
226 proc->mode = mode;
227 proc->parent = parent;
228
229 return proc;
230}
231//******************************************************************************
232//******************************************************************************
233void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
234{
235 return; //memory leak
236}
237//******************************************************************************
238//******************************************************************************
239int proc_register(struct proc_dir_entry *parent, struct proc_dir_entry *proc)
240{
241 return 0;
242}
243//******************************************************************************
244//******************************************************************************
245int proc_unregister(struct proc_dir_entry *proc, int bla)
246{
247 return 0;
248}
249//******************************************************************************
250//******************************************************************************
251struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
252 struct proc_dir_entry *parent){
253 struct proc_dir_entry *proc;
254 proc = (struct proc_dir_entry *)kmalloc(sizeof(struct proc_dir_entry), 0);
255 memset(proc, 0, sizeof(struct proc_dir_entry));
256
257 proc->name = name;
258 proc->parent = parent;
259
260 return proc;
261}
262//******************************************************************************
263//******************************************************************************
264struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent)
265{
266 return proc_mkdir_mode(name, S_IRUGO | S_IXUGO, parent);
267}
268//******************************************************************************
269//******************************************************************************
270struct proc_dir_entry *proc_symlink(const char *name, struct proc_dir_entry *parent, const char *dest)
271{
272 struct proc_dir_entry *proc;
273 proc = (struct proc_dir_entry *)kmalloc(sizeof(struct proc_dir_entry), 0);
274 memset(proc, 0, sizeof(struct proc_dir_entry));
275
276 proc->name = name;
277 proc->parent = parent;
278 proc->data = (void*)dest;
279
280 return proc;
281}
282//******************************************************************************
283//******************************************************************************
284int fasync_helper(int a, struct file *b, int c, struct fasync_struct **d)
285{
286 return 0;
287}
288//******************************************************************************
289//******************************************************************************
290void kill_fasync(struct fasync_struct **a, int b, int c)
291{
292}
293//******************************************************************************
294//******************************************************************************
295int request_dma(unsigned int dmanr, const char * device_id) /* reserve a DMA channel */
296{
297 DebugInt3();
298 return 0;
299}
300//******************************************************************************
301//******************************************************************************
302void free_dma(unsigned int dmanr)
303{
304 DebugInt3();
305}
306//******************************************************************************
307/* enable/disable a specific DMA channel */
308//******************************************************************************
309void enable_dma(unsigned int dmanr)
310{
311 DebugInt3();
312}
313//******************************************************************************
314//******************************************************************************
315void disable_dma(unsigned int dmanr)
316{
317 DebugInt3();
318}
319//******************************************************************************
320//static struct notifier_block *reboot_notify_list = NULL;
321// No need to implement this right now. The ESS Maestro 3 driver uses it
322// to call pci_unregister_driver, which is always called from the shutdown
323// notification sent by OS2.
324// Same goes for es1968 & Yamaha's DS1/DS1E.
325//******************************************************************************
326int register_reboot_notifier(struct notifier_block *pnblock)
327{
328 return 0;
329}
330//******************************************************************************
331//******************************************************************************
332int unregister_reboot_notifier(struct notifier_block *pnblock)
333{
334 return 0;
335}
336//******************************************************************************
337//******************************************************************************
338static void __x_queue_work(struct workqueue_struct *wq, struct work_struct *work)
339{
340 unsigned long flags;
341
342 spin_lock_irqsave(&wq->lock, flags);
343 work->wq_data = wq;
344 list_add_tail(&work->entry, &wq->worklist);
345 wake_up(&wq->more_work);
346 spin_unlock_irqrestore(&wq->lock, flags);
347}
348//******************************************************************************
349//******************************************************************************
350int queue_work(struct workqueue_struct *wq, struct work_struct *work)
351{
352 if (!test_and_set_bit(0, &work->pending)) {
353 __x_queue_work(wq, work);
354 return 1;
355 }
356 return 0;
357}
358//******************************************************************************
359//******************************************************************************
360static void run_workqueue(struct workqueue_struct *wq)
361{
362 unsigned long flags;
363
364 spin_lock_irqsave(&wq->lock, flags);
365 while (!list_empty(&wq->worklist)) {
366 struct work_struct *work = list_entry(wq->worklist.next,
367 struct work_struct, entry);
368 void (*f) (void *) = work->func;
369 void *data = work->data;
370
371 list_del_init(wq->worklist.next);
372 spin_unlock_irqrestore(&wq->lock, flags);
373 clear_bit(0, &work->pending);
374 f(data);
375 spin_lock_irqsave(&wq->lock, flags);
376 wake_up(&wq->work_done);
377 }
378 spin_unlock_irqrestore(&wq->lock, flags);
379}
380//******************************************************************************
381//******************************************************************************
382void flush_workqueue(struct workqueue_struct *wq)
383{
384
385 if (wq->task == current) {
386 run_workqueue(wq);
387 } else {
388 struct wait_queue_entry wait;
389
390 init_waitqueue_entry(&wait, current);
391 set_current_state(TASK_UNINTERRUPTIBLE);
392 spin_lock_irq(&wq->lock);
393 add_wait_queue(&wq->work_done, &wait);
394#ifndef TARGET_OS2
395 while (!list_empty(&wq->worklist)) {
396 spin_unlock_irq(&wq->lock);
397 schedule(); // DAZ system hangs here because this function does nothing
398 spin_lock_irq(&wq->lock);
399 }
400#endif
401 set_current_state(TASK_RUNNING);
402 remove_wait_queue(&wq->work_done, &wait);
403 spin_unlock_irq(&wq->lock);
404 }
405}
406//******************************************************************************
407//******************************************************************************
408struct workqueue_struct *create_workqueue(const char *name)
409{
410 struct workqueue_struct *wq;
411
412 BUG_ON(strlen(name) > 10);
413
414 wq = kmalloc(sizeof(*wq), GFP_KERNEL);
415 if (!wq)
416 return NULL;
417 memset(wq, 0, sizeof(*wq));
418
419 spin_lock_init(&wq->lock);
420 INIT_LIST_HEAD(&wq->worklist);
421 init_waitqueue_head(&wq->more_work);
422 init_waitqueue_head(&wq->work_done);
423#ifndef TARGET_OS2
424 init_completion(&wq->thread_exited);
425 wq->name = name;
426 wq->task_pid = kernel_thread(xworker_thread, wq, 0);
427 if (wq->task_pid < 0) {
428 printk(KERN_ERR "snd: failed to start thread %s\n", name);
429 snd_compat_destroy_workqueue(wq);
430 wq = NULL;
431 }
432 wq->task = find_task_by_pid(wq->task_pid);
433#endif
434 return wq;
435}
436//******************************************************************************
437//******************************************************************************
438void destroy_workqueue(struct workqueue_struct *wq)
439{
440#ifndef TARGET_OS2
441 snd_compat_flush_workqueue(wq);
442 kill_proc(wq->task_pid, SIGKILL, 1);
443 if (wq->task_pid >= 0)
444 wait_for_completion(&wq->thread_exited);
445#endif
446 kfree(wq);
447}
448
449//******************************************************************************
450//******************************************************************************
451char *kstrdup(const char *s, unsigned int __nocast gfp_flags)
452{
453 int len;
454 char *buf;
455
456 if (!s) return NULL;
457
458 len = strlen(s) + 1;
459 buf = kmalloc(len, gfp_flags);
460 if (buf)
461 memcpy(buf, s, len);
462 return buf;
463}
464//******************************************************************************
465//******************************************************************************
466int mod_firmware_load(const char *fn, char **fp)
467{
468 return 0;
469}
470//******************************************************************************
471//******************************************************************************
472static int snd_try_load_firmware(const char *path, const char *name,
473 struct firmware *firmware)
474{
475 char filename[30 + FIRMWARE_NAME_MAX];
476
477 sprintf(filename, "%s/%s", path, name);
478 firmware->size = mod_firmware_load(filename, (char **)&firmware->data);
479 if (firmware->size)
480 printk(KERN_INFO "Loaded '%s'.", filename);
481 return firmware->size;
482}
483//******************************************************************************
484//******************************************************************************
485int request_firmware(const struct firmware **fw, const char *name,
486 struct device *device)
487{
488 struct firmware *firmware;
489
490 *fw = NULL;
491 firmware = kmalloc(sizeof *firmware, GFP_KERNEL);
492 if (!firmware)
493 return -ENOMEM;
494 if (!snd_try_load_firmware("/lib/firmware", name, firmware) &&
495 !snd_try_load_firmware("/lib/hotplug/firmware", name, firmware) &&
496 !snd_try_load_firmware("/usr/lib/hotplug/firmware", name, firmware)) {
497 kfree(firmware);
498 return -EIO;
499 }
500 *fw = firmware;
501 return 0;
502}
503//******************************************************************************
504//******************************************************************************
505void release_firmware(const struct firmware *fw)
506{
507 if (fw) {
508 vfree(fw->data);
509 kfree(fw);
510 }
511}
512//******************************************************************************
513//******************************************************************************
514void *memdup_user(const void __user *src, size_t len)
515{
516 void *p = kmalloc(len, GFP_KERNEL);
517 if (!p)
518 return ERR_PTR(-ENOMEM);
519 if (copy_from_user(p, src, len)) {
520 kfree(p);
521 return ERR_PTR(-EFAULT);
522 }
523 return p;
524}
525//******************************************************************************
526//******************************************************************************
527#define del_timer_sync(t) del_timer(t) /* FIXME: not quite correct on SMP */
528int cancel_delayed_work(struct delayed_work *dwork)
529{
530 struct work_struct *work = &dwork->work;
531 int ret;
532
533 ret = del_timer_sync(&work->timer);
534 if (ret)
535 clear_bit(0, &work->pending);
536 return ret;
537}
538//******************************************************************************
539//******************************************************************************
540struct workqueue_struct *system_wq;
541
542int schedule_work(struct work_struct *work)
543{
544#ifndef TARGET_OS2 // crashes
545 return queue_work(system_wq, work);
546#endif
547 return 0;
548}
549
550//******************************************************************************
551//******************************************************************************
552bool flush_delayed_work_sync(struct delayed_work *dwork)
553{
554 bool ret;
555 ret = cancel_delayed_work(dwork);
556 if (ret) {
557 schedule_delayed_work(dwork, 0);
558 flush_scheduled_work();
559 }
560 return ret;
561}
562//******************************************************************************
563//******************************************************************************
564static void delayed_work_timer_fn(unsigned long __data)
565{
566 struct work_struct *work = (struct work_struct *)__data;
567 struct workqueue_struct *wq = work->wq_data;
568
569 if (wq)
570 __x_queue_work(wq, work);
571 else
572 schedule_work(work);
573}
574//******************************************************************************
575//******************************************************************************
576int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay)
577{
578 struct work_struct *work = &dwork->work;
579 struct timer_list *timer = &work->timer;
580
581 if (!test_and_set_bit(0, &work->pending)) {
582 work->wq_data = wq;
583 timer->expires = jiffies + delay;
584 timer->data = (unsigned long)work;
585 timer->function = delayed_work_timer_fn;
586 add_timer(timer);
587 return 1;
588 }
589 return 0;
590}
591//******************************************************************************
592//******************************************************************************
593/* Greatest common divisor */
594unsigned long gcd(unsigned long a, unsigned long b)
595{
596 unsigned long r;
597 if (a < b) {
598 r = a;
599 a = b;
600 b = r;
601 }
602 while ((r = a % b) != 0) {
603 a = b;
604 b = r;
605 }
606 return b;
607}
608
609//******************************************************************************
610//******************************************************************************
611int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
612{
613 char *tail;
614 unsigned long val;
615 size_t len;
616
617 *res = 0;
618 len = strlen(cp);
619 if (len == 0)
620 return -EINVAL;
621
622 val = simple_strtoul(cp, &tail, base);
623 if (tail == cp)
624 return -EINVAL;
625
626 if ((*tail == '\0') ||
627 ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {
628 *res = val;
629 return 0;
630 }
631
632 return -EINVAL;
633}
634//******************************************************************************
635//******************************************************************************
636
637static void u32_swap(void *a, void *b, int size)
638{
639 u32 t = *(u32 *)a;
640 *(u32 *)a = *(u32 *)b;
641 *(u32 *)b = t;
642}
643
644static void generic_swap(void *a, void *b, int size)
645{
646 char t;
647
648 do {
649 t = *(char *)a;
650 *(char *)a++ = *(char *)b;
651 *(char *)b++ = t;
652 } while (--size > 0);
653}
654
655/**
656 * sort - sort an array of elements
657 * @base: pointer to data to sort
658 * @num: number of elements
659 * @size: size of each element
660 * @cmp_func: pointer to comparison function
661 * @swap_func: pointer to swap function or NULL
662 *
663 * This function does a heapsort on the given array. You may provide a
664 * swap_func function optimized to your element type.
665 *
666 * Sorting time is O(n log n) both on average and worst-case. While
667 * qsort is about 20% faster on average, it suffers from exploitable
668 * O(n*n) worst-case behavior and extra memory requirements that make
669 * it less suitable for kernel use.
670 */
671
672void sort(void *base, size_t num, size_t size,
673 int (*cmp_func)(const void *, const void *),
674 void (*swap_func)(void *, void *, int size))
675{
676 /* pre-scale counters for performance */
677 int i = (num/2 - 1) * size, n = num * size, c, r;
678
679 if (!swap_func)
680 swap_func = (size == 4 ? u32_swap : generic_swap);
681
682 /* heapify */
683 for ( ; i >= 0; i -= size) {
684 for (r = i; r * 2 + size < n; r = c) {
685 c = r * 2 + size;
686 if (c < n - size &&
687 cmp_func(base + c, base + c + size) < 0)
688 c += size;
689 if (cmp_func(base + r, base + c) >= 0)
690 break;
691 swap_func(base + r, base + c, size);
692 }
693 }
694
695 /* sort */
696 for (i = n - size; i > 0; i -= size) {
697 swap_func(base, base + i, size);
698 for (r = 0; r * 2 + size < i; r = c) {
699 c = r * 2 + size;
700 if (c < i - size &&
701 cmp_func(base + c, base + c + size) < 0)
702 c += size;
703 if (cmp_func(base + r, base + c) >= 0)
704 break;
705 swap_func(base + r, base + c, size);
706 }
707 }
708}
709//******************************************************************************
710//******************************************************************************
711
712/**
713 * hex_to_bin - convert a hex digit to its real value
714 * @ch: ascii character represents hex digit
715 *
716 * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad
717 * input.
718 */
719int hex_to_bin(char ch)
720{
721 if ((ch >= '0') && (ch <= '9'))
722 return ch - '0';
723 ch = tolower(ch);
724 if ((ch >= 'a') && (ch <= 'f'))
725 return ch - 'a' + 10;
726 return -1;
727}
728//******************************************************************************
729
730
731/*
732 * stream_open is used by subsystems that want stream-like file descriptors.
733 * Such file descriptors are not seekable and don't have notion of position
734 * (file.f_pos is always 0). Contrary to file descriptors of other regular
735 * files, .read() and .write() can run simultaneously.
736 *
737 * stream_open never fails and is marked to return int so that it could be
738 * directly used as file_operations.open .
739 */
740int stream_open(struct inode *inode, struct file *filp)
741{
742 filp->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE | FMODE_ATOMIC_POS);
743 //DAZ f_mode is 16 bit so FMODE_STREAM doesn't fit, but we don't use this on OS/2.
744 //filp->f_mode |= FMODE_STREAM;
745 return 0;
746}
747
748/**
749 * alloc_pages_exact - allocate pages with the given size
750 * @size: the size to allocate in bytes
751 * @gfp_flags: the allocation conditions, GFP_XXX
752 *
753 * Allocates the physically contiguous pages with the given size.
754 *
755 * Return: The pointer of the buffer, or %NULL if no enough memory.
756 */
757void *alloc_pages_exact(size_t size, gfp_t gfp_flags)
758{
759 int pg;
760
761 if (WARN_ON(!size))
762 return NULL;
763 if (WARN_ON(!gfp_flags))
764 return NULL;
765 gfp_flags |= __GFP_COMP; /* compound page lets parts be mapped */
766 pg = get_order(size);
767 return (void *) __get_free_pages(gfp_flags, pg);
768}
769
770/**
771 * free_pages_exact - release the pages
772 * @ptr: the buffer pointer to release
773 * @size: the allocated buffer size
774 *
775 * Releases the buffer allocated via snd_malloc_pages().
776 */
777void free_pages_exact(void *ptr, size_t size)
778{
779 int pg;
780
781 if (ptr == NULL)
782 return;
783 pg = get_order(size);
784 free_pages((unsigned long) ptr, pg);
785}
Note: See TracBrowser for help on using the repository browser.