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

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

added support for os2trace (buggy; messages get swallowed); reverted last change that ignored unknown command line arguments

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