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

Last change on this file since 154 was 154, checked in by David Azarewicz, 12 years ago

Begin adding user info output.
Added LVM support.

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