source: trunk/src/libc.c@ 4

Last change on this file since 4 was 4, checked in by root, 15 years ago

initial checkin of CM's code

File size: 20.4 KB
Line 
1/******************************************************************************
2 * libc.c - minimal subset of C runtime library for os2ahci
3 *
4 * Copyright (c) 2010 Christian Mueller. Parts copied from/inspired by the
5 * Linux AHCI driver; those parts are (c) Linux AHCI/ATA maintainers
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include "os2ahci.h"
23
24/* -------------------------- macros and constants ------------------------- */
25
26#define MSG_REPLACEMENT_STRING 1178 /* empty message with a single %; used
27 * for printing custom messages via
28 * DevHelp_Save_Message() */
29
30#define COM_BASE 0x03f8 /* base address of COM port (COM1) */
31
32/* heap management constants */
33#define HEAP_SIZE 8192
34#define HEAP_UNIT 128
35#define HEAP_UNIT_CNT (HEAP_SIZE / HEAP_UNIT)
36
37/* ------------------------ typedefs and structures ------------------------ */
38
39/* mdelay() calibration status */
40typedef enum {
41 MD_NOT_CALIBRATED, /* delay loop not calibrated */
42 MD_CALIBRATION_START, /* calibration run started */
43 MD_CALIBRATION_END, /* calibration run ended */
44 MD_CALIBRATION_DONE /* calibration complete */
45} MDCAL;
46
47/* -------------------------- function prototypes -------------------------- */
48
49static void long_to_asc (long val, char _far *buf, int base,
50 int zero, int flen);
51static void _far mdelay_timer_callback (ULONG timer_handle, ULONG parm1,
52 ULONG parm2);
53static int mdelay_cal_end (void);
54
55/* ------------------------ global/static variables ------------------------ */
56
57static char hex_digits[] = "0123456789abcdef";
58static ULONG com_lock;
59
60/* message table for DosHelp_Save_Message() which prints the first string */
61static MSGTABLE init_msgtbl = {
62 MSG_REPLACEMENT_STRING,
63 1,
64 0
65};
66
67/* COM port initialization sequence */
68static struct {
69 int reg;
70 u8 data;
71} com1_init_sequence[] = {
72 3, 0x80, /* overlay divisor latch register at 0x3f8 and 0x3f9 */
73 0, 0x00, /* set low byte of divisor to 1 (115200 baud) */
74 1, 0x01, /* set high byte of divisor to 0 */
75 3, 0x03, /* reset divisor latch register overlay and set 8,n,1 */
76 1, 0x00, /* disable interrupts */
77 4, 0x0f, /* modem control register */
78 -1, 0x00
79};
80
81/* delay loop calibration data */
82volatile MDCAL mdelay_cal_status = 0; /* delay loop calibration status */
83volatile u32 mdelay_loops_per_ms = 0; /* delay loop counter */
84
85/* very small heap for dynamic memory management */
86static u8 heap_buf[HEAP_SIZE];
87static u8 heap_units[HEAP_UNIT_CNT];
88
89/* ----------------------------- start of code ----------------------------- */
90
91/******************************************************************************
92 * Initialize COM1 to 57600,n,8,1
93 */
94void init_com1(void)
95{
96 int i;
97
98 DevHelp_CreateSpinLock(&com_lock);
99
100 for (i = 0; com1_init_sequence[i].reg != -1; i++) {
101 u16 port = COM_BASE + com1_init_sequence[i].reg;
102 u8 data = com1_init_sequence[i].data;
103 _asm {
104 mov dx, port;
105 mov al, data;
106 out dx, al;
107 }
108 }
109}
110
111/******************************************************************************
112 * Print a formatted message into a string buffer. This is very basic,
113 * supporting only strings and integers (16 and 32 bits (l), decimal (d)
114 * and hex (x)). Formatting length modifiers are only supported with a single
115 * digit -- 32-bit numbers don't need more than 9 characters -- and an
116 * optional '0' in front.
117 */
118int vsprintf(char _far *buf, const char *fmt, va_list va)
119{
120 char _far *orig = buf;
121 char _far *s;
122 int lmod;
123 int fptr;
124 int zero;
125 int flen;
126
127 for (; *fmt != '\0'; fmt++) {
128 switch (*fmt) {
129
130 case '%':
131 fmt++;
132 zero = flen = 0;
133 if (*fmt >= '0' && *fmt <= '9') {
134 /* formatting length modifiers */
135 zero = (*fmt == '0') ? 1 : 0;
136 fmt += zero;
137 if ((flen = *fmt - '0') >= 1 && flen <= 9) {
138 fmt++;
139 }
140 }
141
142 /* data type modifiers */
143 lmod = (*fmt == 'l') ? 1 : 0;
144 fptr = (*fmt == 'F') ? 1 : 0;
145 fmt += lmod + fptr;
146
147 switch (*fmt) {
148
149 case 's':
150 if (fptr) {
151 char _far *p = va_arg(va, char _far *);
152 s = (p == 0) ? "[null]" : p;
153 } else {
154 char *p = va_arg(va, char *);
155 s = (p == 0) ? "[null]" : p;
156 }
157 while ((*buf = *(s++)) != '\0')
158 buf++;
159 break;
160
161 case 'c':
162 *(buf++) = (char) va_arg(va, int);
163 break;
164
165 case 'd':
166 long_to_asc((lmod) ? va_arg(va, long)
167 : va_arg(va, int), buf, 10, zero, flen);
168 buf += strlen(buf);
169 break;
170
171 case 'x':
172 long_to_asc((lmod) ? va_arg(va, u32)
173 : va_arg(va, u16), buf, 16, zero, flen);
174 buf += strlen(buf);
175 break;
176
177 case 'p':
178 if (fptr || lmod) {
179 u16 off = va_arg(va, u16);
180 u16 seg = va_arg(va, u16);
181 long_to_asc(seg, buf, 16, 1, 4);
182 buf += strlen(buf);
183 *(buf++) = ':';
184 long_to_asc(off, buf, 16, 1, 4);
185 buf += strlen(buf);
186 } else {
187 long_to_asc(va_arg(va, u16), buf, 16, 1, 4);
188 buf += strlen(buf);
189 }
190 break;
191
192 default:
193 *(buf++) = *fmt;
194 break;
195 }
196 break;
197
198 case '\n':
199 *(buf++) = '\r';
200 *(buf++) = '\n';
201 break;
202
203 default:
204 *(buf++) = *fmt;
205 break;
206
207 }
208 }
209
210 *buf = '\0';
211 return((int) (buf - orig));
212}
213
214/*******************************************************************************
215 * Print a formatted message into a string buffer. Relies on vsprintf()
216 */
217int sprintf(char _far *buf, const char *fmt, ...)
218{
219 va_list va;
220
221 va_start(va, fmt);
222 return(vsprintf(buf, fmt, va));
223}
224
225/******************************************************************************
226 * Print messages to COM
227 *
228 * NOTES: This function uses a 1K buffer for the resulting message. Thus,
229 * messages should never exceed 1024 bytes.
230 */
231void vprintf(const char *fmt, va_list va)
232{
233 static char buf[1024];
234 char *s;
235
236 spin_lock(com_lock);
237
238 vsprintf(buf, fmt, va);
239
240 /* write debug message to COM1 */
241 for (s = buf; *s != '\0'; s++) {
242
243 /* inp() and outp() are redefined by the DDK in an incompatible
244 * way (only words). Instead of messing around with those
245 * definitions, it's safer and easier to put the whole thing
246 * into an _asm block.
247 *
248 * The C equivalent would look like this:
249 *
250 * while (!(inp(COM_BASE + 5) & 0x20));
251 * outp(COM_BASE, *s);
252 */
253
254 _asm {
255 /* wait until COM transmitter is idle */
256 mov dx, COM_BASE + 5;
257 transmitter_not_idle:
258 in al, dx;
259 and al, 0x20;
260 jz transmitter_not_idle;
261
262 /* output character to be sent */
263 mov dx, COM_BASE;
264 mov bx, s;
265 mov al, [bx];
266 out dx, al;
267 };
268 }
269
270 spin_unlock(com_lock);
271}
272
273/******************************************************************************
274 * Print messages to COM port
275 */
276void printf(const char *fmt, ...)
277{
278 va_list va;
279
280 va_start(va, fmt);
281 vprintf(fmt, va);
282}
283
284/******************************************************************************
285 * Print a message to the system console. This works only during device driver
286 * initialization.
287 *
288 * NOTE: This function uses a 1K buffer for the resulting message. Thus,
289 * messages should never exceed 1024 bytes...
290 */
291void cprintf(const char *fmt, ...)
292{
293 static char buf[1024];
294 va_list va;
295 size_t len;
296
297 va_start(va, fmt);
298 vsprintf(buf, fmt, va);
299
300 if (debug) {
301 /* print the same message to COM1 as well */
302 printf("%s", buf);
303 }
304
305 /* remove trailing CR/LF (DevHelp_Save_Message() will add it again) */
306 if ((len = strlen(buf)) >= 2 && buf[len-1] == '\n' && buf[len-2] == '\r') {
307 buf[len-2] = '\0';
308 }
309
310 init_msgtbl.MsgStrings[0] = buf;
311 DevHelp_Save_Message((NPBYTE) &init_msgtbl);
312}
313
314/******************************************************************************
315 * Print hex buffer to COM port.
316 */
317void phex(const void _far *p, int len, const char *fmt, ...)
318{
319 va_list va;
320 const unsigned char _far *buf = p;
321 long pos = 0;
322 int i;
323
324 if (!debug) {
325 return;
326 }
327
328 /* print header */
329 va_start(va, fmt);
330 vprintf(fmt, va);
331
332 /* print hex block */
333 while (len > 0) {
334 printf("%Fp ", buf);
335
336 /* print hex block */
337 for (i = 0; i < 16; i++) {
338 if (i < len) {
339 printf("%c%02x", ((i == 8) ? '-' : ' '), buf[i]);
340 } else {
341 printf(" ");
342 }
343 }
344
345 /* print ASCII block */
346 printf(" ");
347 for (i = 0; i < ((len > 16) ? 16 : len); i++) {
348 printf("%c", (buf[i] >= 32 && buf[i] < 128) ? buf[i] : '.');
349 }
350 printf("\n");
351
352 pos += 16;
353 buf += 16;
354 len -= 16;
355 }
356}
357
358/******************************************************************************
359 * Return length of zero-terminated string
360 */
361size_t strlen(const char _far *s)
362{
363 int len = 0;
364
365 while (*(s++) != '\0') {
366 len++;
367 }
368 return(len);
369}
370
371/******************************************************************************
372 * Copy zero-terminated string
373 */
374char _far *strcpy(char _far *dst, const char _far *src)
375{
376 char _far *orig = dst;
377
378 while ((*(dst++) = *(src++)) != '\0');
379 return(orig);
380}
381
382/******************************************************************************
383 * Compare blocks of memory
384 */
385int memcmp(void _far *p1, void _far *p2, size_t len)
386{
387 register char _far *s1 = p1;
388 register char _far *s2 = p2;
389 int n = 0;
390
391 while (len > 0) {
392 if ((n = *(s1++) - *(s2++)) != 0) {
393 return(n);
394 }
395 len--;
396 }
397 return(0);
398}
399
400/******************************************************************************
401 * Convert a string to a long value using the specified base
402 */
403long strtol(const char _far *buf, const char _far * _far *ep, int base)
404{
405 register const char _far *s = buf;
406 long val = 0;
407 int negative = 0;
408
409 /* skip leading whitespace */
410 while (*s == ' ' || *s == '\t') {
411 s++;
412 }
413
414 /* positive or negative */
415 if (*s == '-') {
416 negative = 1;
417 s++;
418 } else if (*s == '+') {
419 s++;
420 }
421
422 /* convert string to long integer */
423 for (;; s++) {
424 int digit = (*s <= '9') ? (*s - '0') : (tolower(*s) - 'a' + 10);
425 if (digit < 0 || digit >= base) {
426 break;
427 }
428 val *= base;
429 val += digit;
430 }
431
432 if (ep != NULL) {
433 *ep = s;
434 }
435 if (negative) {
436 val = -val;
437 }
438 return(val);
439}
440
441/******************************************************************************
442 * Extremely simple and stupid implementation of malloc(). The heap is very
443 * small, only 8K at the moment, and the memory blocks are managed using a
444 * simple array of "number of heap units allocated", zero meaning this unit is
445 * available. Each heap unit is currently 128 bytes.
446 *
447 * Dynamic memory is primarily used for things like ATA identify, ATAPI
448 * sense buffers, etc. and should be freed as soon as possible, otherwise
449 * we'll quickly run out of memory.
450 *
451 * NOTE: This function is not reentrant, thus must be called with the driver-
452 * level spinlock held. The main reason for this design is that most
453 * functions that need dynamic memory are already holding the spinlock.
454 */
455void *malloc(size_t len)
456{
457 u16 units = (len + HEAP_UNIT - 1) / HEAP_UNIT;
458 u16 i;
459 u16 n;
460
461 /* find a sequence of free heap units big enough for the requested length */
462 for (i = 0; i < HEAP_UNIT_CNT; i++) {
463 if (heap_units[i] == 0) {
464 for (n = i; n < i + units && n < HEAP_UNIT_CNT; n++) {
465 if (heap_units[n] != 0) {
466 break;
467 }
468 }
469 if (n == i + units) {
470 /* found a chunk large enough; update 'heap_units[]' */
471 for (; i < n; i++) {
472 heap_units[i] = (u8) (n - i);
473 }
474 return(heap_buf + (n - units) * HEAP_UNIT);
475 }
476
477 /* keep searching... */
478 i = n;
479 } else {
480 /* skip occupied heap units */
481 i += heap_units[i] - 1;
482 }
483 }
484
485 /* out of memory */
486 dprintf("malloc(%d): out of memory\n", len);
487 return(NULL);
488}
489
490/******************************************************************************
491 * Free block of memory allocted by malloc().
492 *
493 * NOTE: This function is not reentrant, thus must be called with the driver-
494 * level spinlock held. The main reason for this design is that most
495 * functions that need dynamic memory are already holding the spinlock.
496 */
497void free(void *ptr)
498{
499 u8 *p = (u8 *) ptr;
500 u16 first_unit;
501 u16 units;
502 u16 i;
503
504 if (p < heap_buf || p >= heap_buf + sizeof(heap_buf) ||
505 (u16) (p - heap_buf) % HEAP_UNIT != 0) {
506 dprintf("free(0x%p): invalid pointer (heap_buf = 0x%p)\n",
507 (u16) p, (u16) heap_buf);
508 return;
509 }
510
511 /* clear unit allocation counters in heap_units[] */
512 first_unit = (u16) (p - heap_buf) / HEAP_UNIT;
513 units = heap_units[first_unit];
514 for (i = first_unit; i < first_unit + units; i++) {
515 heap_units[i] = 0;
516 }
517}
518
519/******************************************************************************
520 * Calibrate 'mdelay()' loop. This is done by setting up a 1 second timer
521 * with a callback that sets 'mdelay_done' to MD_CALIBRATION_END. Then it
522 * calls mdelay() with a large milliseond value an initial delay loop counter
523 * of 1,000,000. When the timer triggers, 'mdelay()' will stop and update
524 * the delay loop counter.
525 *
526 * This function needs to be called at device driver init time. Since it uses
527 * ADD timers, it must be called with interrupts enabled. All this is not very
528 * precise (we should wait for a clock tick before starting, ...) but we don't
529 * really need precise timers.
530 */
531void mdelay_cal(void)
532{
533 ULONG timer_handle;
534
535 dprintf("calibrating delay loop... ");
536
537 mdelay_loops_per_ms = 100000;
538 mdelay_cal_status = MD_CALIBRATION_START;
539
540 ADD_StartTimerMS(&timer_handle, 1000, (PFN) mdelay_timer_callback, 0, 0);
541 mdelay(999999999);
542 ADD_CancelTimer(timer_handle);
543
544 dprintf("done (loops per ms = %ld)\n", mdelay_loops_per_ms);
545}
546
547/******************************************************************************
548 * Wait specified number of milliseconds. This is implemented using a busy
549 * loop and is only good for delays in the millisecond range but never for more
550 * than a few milliseconds and only in situations where a proper timer won't do.
551 * As a rule of thumb, don't call this function and use ADD timers, instead.
552 *
553 * NOTES:
554 *
555 * - Timers are problematic on x86 platforms because there's no reliable
556 * hardware timer on all architectures and the CPU clock speed may change
557 * while executing delay loops (AMD Cool&Quiet and Intel SpeedStep), thus
558 * calibration routines won't really be sufficient. But this usually only
559 * extends the delay and we don't really need a high precision timer. The
560 * exception are things like notebooks that are clocked slower when on
561 * battery and which got booted while on battery. Should still be OK,
562 * though, because our requirements are not that strict.
563 *
564 * - The code in this function is inefficient by design to make sure it
565 * will work with future CPUs which might otherwise be too fast for
566 * our loop counters. Part of this design is using volatile variables to
567 * force memory operations.
568 *
569 * - Before using this function, call mdelay_calibrate() to determine the
570 * number of inner loops required per millisecond.
571 */
572void mdelay(u32 millies)
573{
574 volatile u32 i;
575 volatile u32 n;
576
577 for (i = 0; i < millies; i++) {
578 for (n = 0; n < mdelay_loops_per_ms; n++) {
579 if (mdelay_cal_end()) {
580 /* this is a calibration run that just ended */
581 goto complete_calibration;
582 }
583 }
584 }
585 return;
586
587complete_calibration:
588 /* complete calibration cycle */
589 if (i < 1000) {
590 /* Initial value for delay loop was too high; interpolate results for
591 * an assumed initial delay loop divided by 1000.
592 */
593 i = i * 1000 + mdelay_loops_per_ms % 1000;
594 mdelay_loops_per_ms /= 1000;
595 }
596 mdelay_loops_per_ms = (mdelay_loops_per_ms * i) / 1000;
597 mdelay_cal_status = MD_CALIBRATION_DONE;
598}
599
600/******************************************************************************
601 * Sleep specified number of milliseonds. This is implemented by yielding the
602 * CPU until the system timer value indicates we're done. This function can
603 * only be called at task time, or from a context hook.
604 *
605 * NOTE: The accuracy is limited by the OS/2 timer interrupt frequency which
606 * can lead to intervals up to 55ms (18.2 timer interrupts per second).
607 */
608void msleep(u32 millies)
609{
610 volatile PGINFOSEG gis;
611 ULONG start;
612 ULONG end;
613 PSEL p;
614
615 if (DevHelp_GetDOSVar(DHGETDOSV_SYSINFOSEG, 0, (PPVOID) &p)) {
616 /* no global info segment; use mdelay() */
617 mdelay(millies);
618 return;
619 }
620 gis = (PGINFOSEG) ((u32) *p << 16);
621 start = gis->msecs;
622 end = start + millies;
623
624 if (end < start) {
625 /* wrap-around; wait until 'msecs' has wrapped, too */
626 while (gis->msecs >= start) {
627 DevHelp_Yield();
628 }
629 }
630
631 while (gis->msecs <= end) {
632 DevHelp_Yield();
633 }
634}
635
636/******************************************************************************
637 * Halt processing by submitting an internal error. This is a last resort and
638 * should only be called when the system state is corrupt.
639 */
640void panic(char *msg)
641{
642 DevHelp_InternalError(msg, strlen(msg));
643}
644
645/******************************************************************************
646 * Disable interrupts. The reason for using a separate function for this is
647 * that the presence of _asm statements will disable compiler optimizations.
648 * In order to support nested calls, this function will return 0 if the
649 * interrupts were already disabled or != 0, if not.
650 *
651 * NOTE: SMP systems must use spinlocks, thus this function will only be
652 * compiled on non-SMP builds.
653 */
654#ifndef OS2AHCI_SMP
655int disable(void)
656{
657 int rc;
658
659 _asm {
660 pushf
661 pop ax
662 and ax, 0x0200; /* "interrupts enabled" bit */
663 mov rc, ax;
664 cli
665 }
666
667 return(rc);
668}
669#endif
670
671/******************************************************************************
672 * Enable interrupts. The reason for using a separate function for this is
673 * that the presence of _asm statements will disable compiler optimizations.
674 *
675 * NOTE: SMP systems must use spinlocks, thus this function will only be
676 * compiled on non-SMP builds.
677 */
678#ifndef OS2AHCI_SMP
679void enable(void)
680{
681 _asm sti;
682}
683#endif
684
685/******************************************************************************
686 * Convert 'long' to ASCII with the specified base
687 */
688static void long_to_asc(long val, char _far *buf, int base, int zero, int flen)
689{
690 register unsigned long abs_val = (unsigned long) val;
691 char tmp[80];
692 char _far *ptmp = tmp;
693 char _far *s;
694
695 if (base > 16) {
696 sprintf(buf, "[EVAL]");
697 return;
698 }
699
700 tmp[sizeof(tmp) - 1] = '\0';
701 for (s = ptmp + sizeof(tmp) - 2; s > ptmp; s--) {
702 *s = hex_digits[abs_val % base];
703 flen--;
704 if ((abs_val /= base) == 0) {
705 break;
706 }
707 }
708
709 /* prepend minus sign if val was negative and base is decimal or less */
710 if (val < 0 && base <= 0) {
711 *(--s) = '-';
712 flen--;
713 }
714
715 /* left-pad the resulting number with zeros or spaces up to 'flen' */
716 while (flen > 0) {
717 *(--s) = (zero) ? '0' : ' ';
718 flen--;
719 }
720
721 strcpy(buf, s);
722}
723
724/******************************************************************************
725 * Timer callback handler for 'mdelay_calibrate()'
726 */
727static void _far mdelay_timer_callback(ULONG timer_handle, ULONG parm1,
728 ULONG parm2)
729{
730 mdelay_cal_status = MD_CALIBRATION_END;
731}
732
733/******************************************************************************
734 * Determine whether an mdelay calibration run has just ended. This is in a
735 * function to prevent overzealous optimizers from removing the whole delay
736 * loop in mdelay().
737 */
738static int mdelay_cal_end(void)
739{
740 return(mdelay_cal_status == MD_CALIBRATION_END);
741}
Note: See TracBrowser for help on using the repository browser.