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

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

include time stamps in debug log

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