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

Last change on this file since 24 was 14, checked in by markus, 15 years ago

merged last NCQ related changes with Watcom related changes

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