source: trunk/src/os2ahci/libc.c@ 116

Last change on this file since 116 was 116, checked in by Markus Thielen, 14 years ago
  • do not initialize com port if base address is 0 (thanks, Steven)
  • trace buffer: wake up waiting processes after data has been written (thanks, Steven)
File size: 24.3 KB
Line 
1/******************************************************************************
2 * libc.c - minimal subset of C runtime library for os2ahci
3 *
4 * Copyright (c) 2011 thi.guten Software Development
5 * Copyright (c) 2011 Mensys B.V.
6 *
7 * Authors: Christian Mueller, Markus Thielen
8 *
9 * Parts copied from/inspired by the Linux AHCI driver;
10 * those parts are (c) Linux AHCI/ATA maintainers
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27#include "os2ahci.h"
28
29/* -------------------------- macros and constants ------------------------- */
30
31#define MSG_REPLACEMENT_STRING 1178 /* empty message with a single %; used
32 * for printing custom messages via
33 * DevHelp_Save_Message() */
34
35/* heap management constants */
36#define HEAP_SIZE 8192
37#define HEAP_UNIT 128
38#define HEAP_UNIT_CNT (HEAP_SIZE / HEAP_UNIT)
39
40/* ------------------------ typedefs and structures ------------------------ */
41
42/* mdelay() calibration status */
43typedef enum {
44 MD_NOT_CALIBRATED, /* delay loop not calibrated */
45 MD_CALIBRATION_START, /* calibration run started */
46 MD_CALIBRATION_END, /* calibration run ended */
47 MD_CALIBRATION_DONE /* calibration complete */
48} MDCAL;
49
50/* -------------------------- function prototypes -------------------------- */
51
52static void long_to_asc (long val,
53 char _far *buf,
54 int base,
55 int zero, int flen);
56static void _cdecl _far mdelay_timer_callback (ULONG timer_handle,
57 ULONG parm1,
58 ULONG parm2);
59static int mdelay_cal_end (void);
60
61/* ------------------------ global/static variables ------------------------ */
62
63/* debug COM port base address */
64u16 com_base = 0x03f8;
65
66static char hex_digits[] = "0123456789abcdef";
67static ULONG mem_lock;
68ULONG com_lock;
69
70/* message table for DosHelp_Save_Message() which prints the first string */
71static MSGTABLE init_msgtbl = {
72 MSG_REPLACEMENT_STRING,
73 1,
74 0
75};
76
77/* COM port initialization sequence */
78static struct {
79 int reg;
80 u8 data;
81} com_init_sequence[] = {
82 3, 0x80, /* overlay divisor latch register at 0x3f8 and 0x3f9 */
83 0, 0x01, /* set low byte of divisor to 1 (115200 baud) */
84 1, 0x00, /* set high byte of divisor to 0 */
85 3, 0x03, /* reset divisor latch register overlay and set 8,n,1 */
86 1, 0x00, /* disable interrupts */
87 4, 0x0f, /* modem control register */
88 -1, 0x00
89};
90
91/* delay loop calibration data */
92volatile MDCAL mdelay_cal_status = 0; /* delay loop calibration status */
93volatile u32 mdelay_loops_per_ms = 0; /* delay loop counter */
94
95/* very small heap for dynamic memory management */
96static u8 heap_buf[HEAP_SIZE];
97static u8 heap_units[HEAP_UNIT_CNT];
98static ULONG heap_phys_addr;
99
100/* global info segment */
101volatile PGINFOSEG gis;
102
103
104/* ----------------------------- start of code ----------------------------- */
105
106/******************************************************************************
107 * Initialize libc components
108 */
109void init_libc(void)
110{
111 PSEL p;
112
113 DevHelp_CreateSpinLock(&mem_lock);
114 DevHelp_CreateSpinLock(&com_lock);
115
116 DevHelp_VirtToPhys(heap_buf, &heap_phys_addr);
117
118 /* get global info segment */
119 if (DevHelp_GetDOSVar(DHGETDOSV_SYSINFOSEG, 0, (PPVOID) &p) == 0) {
120 gis = (PGINFOSEG) ((u32) *p << 16);
121 }
122
123}
124
125/******************************************************************************
126 * Initialize COM port to 115200,n,8,1
127 *
128 * NOTE: Something is wrong with this code, or the init sequence, but we never
129 * got around to fixing it because it works fine on Virtualbox, and on
130 * physical machines we tend to have the kernel debugger running on the
131 * same port, thus KDB will set port parameters for us. This is going
132 * to be fixed eventually...
133 */
134void init_com(void)
135{
136 int i;
137
138 if (com_base == 0) {
139 /* no com port in use */
140 return;
141 }
142
143 spin_lock(com_lock);
144
145 for (i = 0; com_init_sequence[i].reg != -1; i++) {
146 u16 port = com_base + com_init_sequence[i].reg;
147 u8 data = com_init_sequence[i].data;
148 _asm {
149 mov dx, port;
150 mov al, data;
151 out dx, al;
152 }
153 }
154
155 spin_unlock(com_lock);
156}
157
158/******************************************************************************
159 * Print a formatted message into a string buffer. This is very basic,
160 * supporting only strings and integers (16 and 32 bits (l), decimal (d)
161 * and hex (x)). Formatting length modifiers are only supported with a single
162 * digit -- 32-bit numbers don't need more than 9 characters -- and an
163 * optional '0' in front.
164 */
165int vsprintf(char _far *buf, const char *fmt, va_list va)
166{
167 char _far *orig = buf;
168 char _far *s;
169 int lmod;
170 int fptr;
171 int zero;
172 int flen;
173
174 for (; *fmt != '\0'; fmt++) {
175 switch (*fmt) {
176
177 case '%':
178 fmt++;
179 zero = flen = 0;
180 if (*fmt >= '0' && *fmt <= '9') {
181 /* formatting length modifiers */
182 zero = (*fmt == '0') ? 1 : 0;
183 fmt += zero;
184 if ((flen = *fmt - '0') >= 1 && flen <= 9) {
185 fmt++;
186 }
187 }
188
189 /* data type modifiers */
190 lmod = (*fmt == 'l') ? 1 : 0;
191 fptr = (*fmt == 'F') ? 1 : 0;
192 fmt += lmod + fptr;
193
194 switch (*fmt) {
195
196 case 's':
197 if (fptr) {
198 char _far *p = va_arg(va, char _far *);
199 s = (p == 0) ? "[null]" : p;
200 } else {
201 char *p = va_arg(va, char *);
202 s = (p == 0) ? "[null]" : p;
203 }
204 while ((*buf = *(s++)) != '\0')
205 buf++;
206 break;
207
208 case 'c':
209 *(buf++) = (char) va_arg(va, int);
210 break;
211
212 case 'd':
213 long_to_asc((lmod) ? va_arg(va, long)
214 : va_arg(va, int), buf, 10, zero, flen);
215 buf += strlen(buf);
216 break;
217
218 case 'x':
219 long_to_asc((lmod) ? va_arg(va, u32)
220 : va_arg(va, u16), buf, 16, zero, flen);
221 buf += strlen(buf);
222 break;
223
224 case 'p':
225 if (fptr || lmod) {
226 u16 off = va_arg(va, u16);
227 u16 seg = va_arg(va, u16);
228 long_to_asc(seg, buf, 16, 1, 4);
229 buf += strlen(buf);
230 *(buf++) = ':';
231 long_to_asc(off, buf, 16, 1, 4);
232 buf += strlen(buf);
233 } else {
234 long_to_asc(va_arg(va, u16), buf, 16, 1, 4);
235 buf += strlen(buf);
236 }
237 break;
238
239 default:
240 *(buf++) = *fmt;
241 break;
242 }
243 break;
244
245 case '\n':
246 *(buf++) = '\r';
247 *(buf++) = '\n';
248 break;
249
250 default:
251 *(buf++) = *fmt;
252 break;
253
254 }
255 }
256
257 *buf = '\0';
258 return((int) (buf - orig));
259}
260
261/*******************************************************************************
262 * Print a formatted message into a string buffer. Relies on vsprintf()
263 */
264int sprintf(char _far *buf, const char *fmt, ...)
265{
266 va_list va;
267
268 va_start(va, fmt);
269 return(vsprintf(buf, fmt, va));
270}
271
272/******************************************************************************
273 * Print messages to serial port
274 *
275 * NOTES: This function uses a 1K buffer for the resulting message. Thus,
276 * messages should never exceed 1024 bytes.
277 */
278void vprintf(const char *fmt, va_list va)
279{
280 static char buf[1024];
281 char *s;
282 int len;
283
284 spin_lock(com_lock);
285
286 len = vsprintf(buf, fmt, va);
287
288 if (com_base == 0) {
289 /* write debug message to trace buffer, not COM port */
290 trace_write(buf, len);
291 spin_unlock(com_lock);
292 return;
293 }
294
295 /* write debug message to serial port */
296 for (s = buf; *s != '\0'; s++) {
297
298 /* inp() and outp() are redefined by the DDK in an incompatible
299 * way (only words). Instead of messing around with those
300 * definitions, it's safer and easier to put the whole thing
301 * into an _asm block.
302 *
303 * The C equivalent would look like this:
304 *
305 * while (!(inp(com_base + 5) & 0x20));
306 * outp(com_base, *s);
307 */
308
309 _asm {
310 /* wait until COM transmitter is idle */
311 mov dx, com_base;
312 add dx, 5;
313 transmitter_not_idle:
314 in al, dx;
315 and al, 0x20;
316 jz transmitter_not_idle;
317
318 /* output character to be sent */
319 mov dx, com_base;
320 mov bx, s;
321 mov al, [bx];
322 out dx, al;
323 };
324 }
325
326 spin_unlock(com_lock);
327}
328
329/******************************************************************************
330 * Print messages to COM port
331 */
332void printf(const char *fmt, ...)
333{
334 va_list va;
335
336 va_start(va, fmt);
337 vprintf(fmt, va);
338}
339
340/******************************************************************************
341 * Print a message to the system console. This works only during device driver
342 * initialization.
343 *
344 * NOTE: This function uses a 1K buffer for the resulting message. Thus,
345 * messages should never exceed 1024 bytes...
346 */
347void cprintf(const char *fmt, ...)
348{
349 static char buf[1024];
350 va_list va;
351 size_t len;
352
353 va_start(va, fmt);
354 vsprintf(buf, fmt, va);
355
356 if (debug && com_base != 0) {
357 /* print the same message to COM1 as well */
358 printf("%s", buf);
359 }
360
361 /* remove trailing CR/LF (DevHelp_Save_Message() will add it again) */
362 if ((len = strlen(buf)) >= 2 && buf[len-1] == '\n' && buf[len-2] == '\r') {
363 buf[len-2] = '\0';
364 }
365
366 init_msgtbl.MsgStrings[0] = buf;
367 DevHelp_Save_Message((NPBYTE) &init_msgtbl);
368}
369
370/******************************************************************************
371 * Print hex buffer to COM port.
372 */
373void phex(const void _far *p, int len, const char *fmt, ...)
374{
375 va_list va;
376 const unsigned char _far *buf = p;
377 int i;
378
379 if (!debug) {
380 return;
381 }
382
383 /* print header */
384 va_start(va, fmt);
385 vprintf(fmt, va);
386
387 /* print hex block */
388 while (len > 0) {
389 printf("%Fp ", buf);
390
391 /* print hex block */
392 for (i = 0; i < 16; i++) {
393 if (i < len) {
394 printf("%c%02x", ((i == 8) ? '-' : ' '), buf[i]);
395 } else {
396 printf(" ");
397 }
398 }
399
400 /* print ASCII block */
401 printf(" ");
402 for (i = 0; i < ((len > 16) ? 16 : len); i++) {
403 printf("%c", (buf[i] >= 32 && buf[i] < 128) ? buf[i] : '.');
404 }
405 printf("\n");
406
407 buf += 16;
408 len -= 16;
409 }
410}
411
412/******************************************************************************
413 * Return length of zero-terminated string
414 */
415size_t strlen(const char _far *s)
416{
417 int len = 0;
418
419 while (*(s++) != '\0') {
420 len++;
421 }
422 return(len);
423}
424
425/******************************************************************************
426 * Copy zero-terminated string
427 */
428char _far *strcpy(char _far *dst, const char _far *src)
429{
430 char _far *orig = dst;
431
432 while ((*(dst++) = *(src++)) != '\0');
433 return(orig);
434}
435
436/******************************************************************************
437 * Compare blocks of memory
438 */
439int memcmp(void _far *p1, void _far *p2, size_t len)
440{
441 register char _far *s1 = p1;
442 register char _far *s2 = p2;
443 int n = 0;
444
445 while (len > 0) {
446 if ((n = *(s1++) - *(s2++)) != 0) {
447 return(n);
448 }
449 len--;
450 }
451 return(0);
452}
453
454/******************************************************************************
455 * Copy block from S/G list to virtual address or vice versa.
456 */
457void sg_memcpy(SCATGATENTRY _far *sg_list, USHORT sg_cnt, ULONG sg_off,
458 void _far *buf, USHORT len, SG_MEMCPY_DIRECTION dir)
459{
460 USHORT mode_flag;
461 USHORT i;
462 USHORT l;
463 ULONG phys_addr;
464 ULONG pos = 0;
465 char _far *p;
466
467 /* walk through S/G list to find the elements involved in the operation */
468 for (i = 0; i < sg_cnt && len > 0; i++) {
469 if (pos <= sg_off && pos + sg_list[i].XferBufLen > sg_off) {
470
471 /* this S/G element intersects with the block to be copied */
472 phys_addr = sg_list[i].ppXferBuf + (sg_off - pos);
473 if ((l = sg_list[i].XferBufLen - (sg_off - pos)) > len) {
474 l = len;
475 }
476
477 if (DevHelp_PhysToVirt(phys_addr, l, (PVOID) &p, &mode_flag)) {
478 panic("sg_memcpy(): DevHelp_PhysToVirt() failed");
479 }
480 if (dir == SG_TO_BUF) {
481 memcpy(buf, p, l);
482 } else {
483 memcpy(p, buf, l);
484 }
485 sg_off += l;
486 buf = (char _far *) buf + l;
487 len -= l;
488 }
489
490 pos += sg_list[i].XferBufLen;
491 }
492}
493
494/******************************************************************************
495 * Convert a string to a long value using the specified base
496 */
497long strtol(const char _far *buf, const char _far * _far *ep, int base)
498{
499 register const char _far *s = buf;
500 long val = 0;
501 int negative = 0;
502
503 /* skip leading whitespace */
504 while (*s == ' ' || *s == '\t') {
505 s++;
506 }
507
508 /* positive or negative */
509 if (*s == '-') {
510 negative = 1;
511 s++;
512 } else if (*s == '+') {
513 s++;
514 }
515
516 /* convert string to long integer */
517 for (;; s++) {
518 int digit = (*s <= '9') ? (*s - '0') : (tolower(*s) - 'a' + 10);
519 if (digit < 0 || digit >= base) {
520 break;
521 }
522 val *= base;
523 val += digit;
524 }
525
526 if (ep != NULL) {
527 *ep = s;
528 }
529 if (negative) {
530 val = -val;
531 }
532 return(val);
533}
534
535/******************************************************************************
536 * Extremely simple and stupid implementation of malloc(). The heap is very
537 * small, only 8K at the moment, and the memory blocks are managed using a
538 * simple array of "number of heap units allocated", zero meaning this unit is
539 * available. Each heap unit is currently 128 bytes.
540 *
541 * Dynamic memory is primarily used for things like ATA identify, ATAPI
542 * sense buffers, etc. and should be freed as soon as possible, otherwise
543 * we'll quickly run out of memory.
544 */
545void *malloc(size_t len)
546{
547 u16 units = (len + HEAP_UNIT - 1) / HEAP_UNIT;
548 u16 i;
549 u16 n;
550
551 spin_lock(mem_lock);
552
553 /* find a sequence of free heap units big enough for the requested length */
554 for (i = 0; i < HEAP_UNIT_CNT; i++) {
555 if (heap_units[i] == 0) {
556 for (n = i; n < i + units && n < HEAP_UNIT_CNT; n++) {
557 if (heap_units[n] != 0) {
558 break;
559 }
560 }
561 if (n == i + units) {
562 /* found a chunk large enough; update 'heap_units[]' */
563 for (; i < n; i++) {
564 heap_units[i] = (u8) (n - i);
565 }
566 spin_unlock(mem_lock);
567 return(heap_buf + (n - units) * HEAP_UNIT);
568 }
569
570 /* keep searching... */
571 i = n;
572 } else {
573 /* skip occupied heap units */
574 i += heap_units[i] - 1;
575 }
576 }
577
578 /* out of memory */
579 spin_unlock(mem_lock);
580 dprintf("malloc(%d): out of memory\n", len);
581 return(NULL);
582}
583
584/******************************************************************************
585 * Free block of memory allocted by malloc().
586 *
587 * NOTE: This function is not reentrant, thus must be called with the driver-
588 * level spinlock held. The main reason for this design is that most
589 * functions that need dynamic memory are already holding the spinlock.
590 */
591void free(void *ptr)
592{
593 u8 *p = (u8 *) ptr;
594 u16 first_unit;
595 u16 units;
596 u16 i;
597
598 if (p < heap_buf || p >= heap_buf + sizeof(heap_buf) ||
599 (u16) (p - heap_buf) % HEAP_UNIT != 0) {
600 dprintf("free(0x%p): invalid pointer (heap_buf = 0x%p)\n",
601 (u16) p, (u16) heap_buf);
602 return;
603 }
604
605 /* clear unit allocation counters in heap_units[] */
606 spin_lock(mem_lock);
607
608 first_unit = (u16) (p - heap_buf) / HEAP_UNIT;
609 units = heap_units[first_unit];
610 for (i = first_unit; i < first_unit + units; i++) {
611 heap_units[i] = 0;
612 }
613
614 spin_unlock(mem_lock);
615}
616
617/******************************************************************************
618 * Return the physical address of a pointer inside the heap buffer. This is
619 * necessary because DevHelp_VirtToPhys() can't be called at interrupt time
620 * and we need physical addresses for heap objects when requeueing unaligned
621 * IORBs inside ahci_intr -> trigger_engine.
622 *
623 * If the pointer is not a heap pointer, this function falls back to calling
624 * DevHelp_VirtToPhys with all consequences (i.e. a trap when this is done
625 * at interrupt time).
626 */
627ULONG virt_to_phys(void _far *ptr)
628{
629 if (ptr < heap_buf || ptr > heap_buf + sizeof(heap_buf)) {
630 ULONG addr;
631
632 if (DevHelp_VirtToPhys(ptr, &addr) != 0) {
633 panic("virt_to_phys(): invalid pointer or execution mode");
634 }
635 return(addr);
636 }
637
638 return(heap_phys_addr + ((char _far *) ptr - (char _far *) heap_buf));
639}
640
641/******************************************************************************
642 * Calibrate 'mdelay()' loop. This is done by setting up a 1 second timer
643 * with a callback that sets 'mdelay_done' to MD_CALIBRATION_END. Then it
644 * calls mdelay() with a large milliseond value as initial delay loop counter.
645 * When the timer triggers, 'mdelay()' will stop and update the delay loop
646 * counter.
647 *
648 * This function needs to be called at device driver init time. Since it uses
649 * ADD timers, it must be called with interrupts enabled. All this is not very
650 * precise (we should wait for a clock tick before starting, ...) but we don't
651 * really need precise timers.
652 */
653void mdelay_cal(void)
654{
655 ULONG timer_handle;
656
657 dprintf("calibrating delay loop... ");
658
659 mdelay_loops_per_ms = 100000;
660 mdelay_cal_status = MD_CALIBRATION_START;
661
662 ADD_StartTimerMS(&timer_handle, 1000, (PFN) mdelay_timer_callback, 0, 0);
663 mdelay(999999999);
664 ADD_CancelTimer(timer_handle);
665
666 dprintf("done (loops per ms = %ld)\n", mdelay_loops_per_ms);
667}
668
669/******************************************************************************
670 * Wait specified number of milliseconds. This is implemented using a busy
671 * loop and is only good for delays in the millisecond range but never for more
672 * than a few milliseconds and only in situations where a proper timer won't do.
673 * As a rule of thumb, don't call this function and use ADD timers, instead.
674 *
675 * NOTES:
676 *
677 * - Timers are problematic on x86 platforms because there's no reliable
678 * hardware timer on all architectures and the CPU clock speed may change
679 * while executing delay loops (AMD Cool&Quiet and Intel SpeedStep), thus
680 * calibration routines won't really be sufficient. But this usually only
681 * extends the delay and we don't really need a high precision timer. The
682 * exception are things like notebooks that are clocked slower when on
683 * battery and which got booted while on battery. Should still be OK,
684 * though, because our requirements are not that strict.
685 *
686 * - The code in this function is inefficient by design to make sure it
687 * will work with future CPUs which might otherwise be too fast for
688 * our loop counters. Part of this design is using volatile variables to
689 * force memory operations.
690 *
691 * - Before using this function, call mdelay_calibrate() to determine the
692 * number of inner loops required per millisecond.
693 */
694void mdelay(u32 millies)
695{
696 volatile u32 i;
697 volatile u32 n;
698
699 for (i = 0; i < millies; i++) {
700 for (n = 0; n < mdelay_loops_per_ms; n++) {
701 if (mdelay_cal_end()) {
702 /* this is a calibration run that just ended */
703 goto complete_calibration;
704 }
705 }
706 }
707 return;
708
709complete_calibration:
710 /* complete calibration cycle */
711 if (i < 1000) {
712 /* Initial value for delay loop was too high; interpolate results for
713 * an assumed initial delay loop divided by 1000.
714 */
715 i = i * 1000 + mdelay_loops_per_ms % 1000;
716 mdelay_loops_per_ms /= 1000;
717 }
718 mdelay_loops_per_ms = (mdelay_loops_per_ms * i) / 1000;
719 mdelay_cal_status = MD_CALIBRATION_DONE;
720}
721
722/******************************************************************************
723 * Sleep specified number of milliseonds. This is implemented by yielding the
724 * CPU until the system timer value indicates we're done. This function can
725 * only be called at task time, or from a context hook.
726 *
727 * NOTE: The accuracy is limited by the OS/2 timer interrupt frequency which
728 * can lead to intervals up to 55ms (18.2 timer interrupts per second).
729 */
730void msleep(u32 millies)
731{
732 ULONG start;
733 ULONG end;
734
735 if (gis == NULL) {
736 /* no global info segment; use mdelay() */
737 mdelay(millies);
738 return;
739 }
740
741 start = gis->msecs;
742 end = start + millies;
743
744 if (end < start) {
745 /* wrap-around; wait until 'msecs' has wrapped, too */
746 while (gis->msecs >= start) {
747 DevHelp_Yield();
748 }
749 }
750
751 while (gis->msecs <= end) {
752 DevHelp_Yield();
753 }
754}
755
756/******************************************************************************
757 * Halt processing by submitting an internal error. This is a last resort and
758 * should only be called when the system state is corrupt.
759 */
760void panic(char *msg)
761{
762 DevHelp_InternalError(msg, strlen(msg));
763}
764
765/******************************************************************************
766 * Disable interrupts. The reason for using a separate function for this is
767 * that the presence of _asm statements will disable compiler optimizations.
768 * In order to support nested calls, this function will return 0 if the
769 * interrupts were already disabled or != 0, if not.
770 *
771 * NOTE: SMP systems should use spinlocks.
772 */
773int disable(void)
774{
775 int rc = 0;
776
777 _asm {
778 pushf
779 pop ax
780 and ax, 0x0200; /* "interrupts enabled" bit */
781 mov rc, ax;
782 cli
783 }
784
785 return(rc);
786}
787
788/******************************************************************************
789 * Enable interrupts. The reason for using a separate function for this is
790 * that the presence of _asm statements will disable compiler optimizations.
791 *
792 * NOTE: SMP systems should use spinlocks.
793 */
794void enable(void)
795{
796 _asm sti;
797}
798
799/******************************************************************************
800 * Convert 'long' to ASCII with the specified base
801 */
802static void long_to_asc(long val, char _far *buf, int base, int zero, int flen)
803{
804 register unsigned long abs_val;
805 char tmp[80];
806 char _far *ptmp = tmp;
807 char _far *s;
808
809 if (base > 16) {
810 sprintf(buf, "[EVAL]");
811 return;
812 }
813
814 abs_val = (unsigned long) ((val < 0 && base <= 10) ? -val : val);
815 tmp[sizeof(tmp) - 1] = '\0';
816
817 for (s = ptmp + sizeof(tmp) - 2; s > ptmp; s--) {
818 *s = hex_digits[abs_val % base];
819 flen--;
820 if ((abs_val /= base) == 0) {
821 break;
822 }
823 }
824
825 /* left-pad the resulting number with zeros or spaces up to 'flen' */
826 while (flen > 0) {
827 *(--s) = (zero) ? '0' : ' ';
828 flen--;
829 }
830
831 /* prepend minus sign if val was negative and base is decimal or less */
832 if (val < 0 && base <= 0) {
833 *(--s) = '-';
834 flen--;
835 }
836
837 strcpy(buf, s);
838}
839
840/******************************************************************************
841 * Timer callback handler for 'mdelay_calibrate()'
842 */
843static void _cdecl _far mdelay_timer_callback(ULONG timer_handle,
844 ULONG parm1,
845 ULONG parm2)
846{
847 mdelay_cal_status = MD_CALIBRATION_END;
848}
849
850/******************************************************************************
851 * Determine whether an mdelay calibration run has just ended. This is in a
852 * function to prevent overzealous optimizers from removing the whole delay
853 * loop in mdelay().
854 */
855static int mdelay_cal_end(void)
856{
857 return(mdelay_cal_status == MD_CALIBRATION_END);
858}
859
Note: See TracBrowser for help on using the repository browser.