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

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

debugging updates

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