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

Last change on this file since 110 was 110, checked in by Markus Thielen, 14 years ago

contains CMs changes for unaligned buffers; removed unused stack var; let driver continue boot on unknown command line switch

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