1 __init()函数的解释:
Pmon中有三类constructor函数,他们都是静态函数,此类函数都有一个共性就是受__attribute__((constructor))属性修饰,例如 init_fs 函数(在gpio,spi,devfs,diskfs,等文件里面都有)
static void init_fs __P((void)) __attribute__ ((constructor));
刚开始时候调用函数 init_fs :
static void init_fs() { SBD_DISPLAY ("DEVI", CHKPNT_DEVI); devinit (); /* * Install terminal based file system. */ filefs_init(&termfs); /* * Create the standard i/o files the proper way */ _file[0].valid = 1; _file[0].fs = &termfs; _file[1].valid = 1; _file[1].fs = &termfs; _file[2].valid = 1; _file[2].fs = &termfs; _file[3].valid = 1; _file[3].fs = &termfs; _file[4].valid = 1; _file[4].fs = &termfs; _file[5].valid = 1; _file[5].fs = &termfs; term_open(0, "/dev/tty0", 0, 0); /* stdin */ term_open(1, "/dev/tty0", 0, 0); /* stdout */ term_open(2, "/dev/tty0", 0, 0); /* stderr */ term_open(3, "/dev/tty1", 0, 0); /* kbdin */ term_open(4, "/dev/tty1", 0, 0); /* vgaout */ term_open(5, "/dev/tty2", 0, 0); /* vgaout */ }
在上面函数中首先调用 devinit:
int devinit (void) { int i, brate; ConfigEntry *q; DevEntry *p; char dname[10]; char *s; strcpy(dname, "tty_baud"); for (i = 0; ConfigTable[i].devinfo && i < /* 2 */ DEV_MAX; i++) { q = &ConfigTable[i]; p = &DevTable[i]; p->txoff = 0; p->qsize = q->rxqsize; /// 接收队列大小 p->sio = q->devinfo; /// q devinfo设备信息(COM口基地址)这里赋值到p->sio. p->chan = q->chan; p->rxoff = 0; p->handler = q->handler; /// 对于串口这个是ns16550 p->tfunc = 0; p->freq = q->freq; /// NS16550HZ p->nopen = 0; p->shift = q->shift; if (p->chan == 0) (*p->handler) (OP_INIT, p, NULL, q->rxqsize); p->rxq = Qcreate (p->qsize); /// rxqsize p->txq = Qcreate (p->qsize); if (p->rxq == 0 || p->txq == 0) return (-1); /* * program requested baud rate, but fall back to default * if there is a problem */ /* XXX don't work. env init is not called yet. has to be solved */ dname[3] = (i < 10) ? i + '0' : i - 10 + 'a'; if ((s = getenv(dname)) == 0 || (brate = getbaudrate(s)) == 0) brate = q->brate; //// CONS_BAUD if ((brate != q->brate)||(q->flag&1)) { if ((*p->handler)(OP_BAUD, p, NULL, brate)) { brate = q->brate; (void)(*p->handler)(OP_BAUD, p, NULL, brate); } } p->t.c_ispeed = brate; p->t.c_ospeed = brate; } return (0); }其中 ConfigTable 定义如下:
ConfigEntry ConfigTable[] = { #ifdef CONFIG_EJTAG_SERIAL { (char *)COM1_BASE_ADDR, 0, ejtag_serial, 256, CONS_BAUD, NS16550HZ}, #else { (char *)COM1_BASE_ADDR, 0, ns16550, 256, CONS_BAUD, NS16550HZ}, #endif #if NMOD_VGACON >0 && NMOD_FRAMEBUFFER >0 { (char *)1, 0, fbterm, 256, CONS_BAUD, NS16550HZ }, #endif { 0 } };COM1_BASE_ADDR 表示串口的地址。
ns16550 设备处理的handler ,以后会通过 -> handler 指针 来访问这个函数。
256 表示rxqsize,接收缓冲区队列大小,这个是2的次幂很重要。方便以后队列处理。
CONS_BAUD 设置的波特率。
NS16550HZ uart设备的频率。
上面函数的作用就是用设备配置表 ConfigTable 里面的内容来初始化设备表 DevTable。创建队列等系列操作。
然后调用 term_open(x, "/dev/ttyx", 0, 0); 打开若干个设备:
int term_open(int fd, const char *fname, int mode, int perms) { int c, dev; char *dname; struct TermDev *devp; DevEntry *p; dname = (char *)fname; if (strncmp (dname, "/dev/", 5) == 0) dname += 5; if (strlen (dname) == 4 && strncmp (dname, "tty", 3) == 0) { c = dname[3]; if (c >= 'a' && c <= 'z') dev = c - 'a' + 10; else if (c >= 'A' && c <= 'Z') dev = c - 'A' + 10; else if (c >= '0' && c <= '9') dev = c - '0'; if (dev >= DEV_MAX || DevTable[dev].rxq == 0) return -1; devp = (struct TermDev *)malloc(sizeof(struct TermDev)); if (devp == NULL) { errno = ENOMEM; return -1; } devp->dev = dev; _file[fd].data = (void *)devp; p = &DevTable[dev]; if(p->handler){ (*p->handler) (OP_OPEN, p, NULL, 0); if (p->nopen++ == 0) term_ioctl (fd, SETSANE); } } else { return -1; } return fd; }当打开 /dev/ttyx 这里的x一般为数字。表示打开 DevTable 表里面的第 x个设备 (dev =x ) (x从0开始)
_file为一个结构体数组
File _file[OPEN_MAX] = { {1}, /* stdin */ {1}, /* stdout */ {1}, /* stderr */ {1}, /* kbdin */ {1} /* vgaout */ };其中传递进来的参数 fd 表示句柄的数值,这个值一是从0开始:
devp->dev = dev; _file[fd].data = (void *)devp;这样以后根据句柄值就可以找到需要的设备。
可以跟踪下 printf 函数的调用流程:
printf (const char *fmt, ...) lib/libc/printf.c vfprintf (FILE *fp, const char *fmt, va_list ap) lib/libc/vfprintf.c fputs(const char *p, FILE *fp) lib/libc/fputs.c write (int fd, const void *buf, size_t n) lib/libc/write.c 其中在write里面调用函数 _file[fd]).fs->write 。这里的write指针指向函数 term_write。 term_write (int fd, const void *buf, size_t nchar) pmon/fs/termio.cstdout定义如下:
#define stdout (vga_available?(&_iob[4]):(&_iob[1]))
int term_write (int fd, const void *buf, size_t nchar) { DevEntry *p; struct TermDev *devp; char *buf2 = (char *)buf; int i, n; int dsel; int count; devp = (struct TermDev *)_file[fd].data; p = &DevTable[devp->dev]; n = nchar; dsel=devp->dev; do { p = &DevTable[dsel]; buf2 = (char *)buf; n = nchar; while (n > 0) { /* << LOCK >> */ while(!tgt_smplock()); i = Qspace (p->txq); while (i > 2 && n > 0) { if ((p->t.c_oflag & ONLCR) && *buf2 == '\n') { Qput(p->txq, '\r'); i--; } Qput(p->txq, *buf2++); n--; i--; } tgt_smpunlock(); /* << UNLOCK >> */ while (Qused(p->txq)) { scandevs(); } } dsel=DevTable[dsel+1].handler?dsel+1:0; }while(dsel!=devp->dev && output_to_both); return (nchar); }上面函数中首先调用 Qput把需要发送的消息放到消息队列中。
然后再调用 scandevs 扫描 DevTable 数组里的每一个设备的队列,把队列中的消息发送出去。 通过函数指针handler 这里handler指向函数 ns16550。
如何扩展串口数目?
pmon只有一个串口可以输出,如何让两个串口同时操作。
首先说下串口的初始化;./Targets/LS1B/ls1b/start.S。串口的初始化是从这里开始,pmon以后都接着使用这个串口,这里如果不初始化后面不能用。
la v0, COM1_BASE_ADDR li v1, FIFO_ENABLE |FIFO_RCV_RST|FIFO_XMT_RST|FIFO_TRIGGER_4 sb v1, NSREG(NS16550_FIFO)(v0) li v1, CFCR_DLAB sb v1, NSREG(NS16550_CFCR)(v0) li a1,2*16*CONS_BAUD divu v1,v1,a1 move t0,v1 sb v1, NSREG(NS16550_DATA)(v0) srl v1, 8 sb v1, NSREG(NS16550_IER)(v0) li v1, CFCR_8BITS sb v1, NSREG(NS16550_CFCR)(v0) li v1, MCR_DTR|MCR_RTS sb v1, NSREG(NS16550_MCR)(v0) li v1, 0x0 sb v1, NSREG(NS16550_IER)(v0) #disable all interrupt li v1, 0x0 sb v1, NSREG(NS16550_IER)(v0)在这里也应当加入串口2初始化的代码,具体就仿照串口1就行了。
剩下的就是修改c文件
首先在ConfigTable 里面加入第二个串口的配置:
ConfigEntry ConfigTable[] = { #ifdef CONFIG_EJTAG_SERIAL { (char *)COM1_BASE_ADDR, 0, ejtag_serial, 256, CONS_BAUD, NS16550HZ}, #else { (char *)COM1_BASE_ADDR, 0, ns16550, 256, CONS_BAUD, NS16550HZ}, #endif { (char *)COM2_BASE_ADDR, 0, ns16550, 256, CONS_BAUD, NS16550HZ}, #if NMOD_VGACON >0 && NMOD_FRAMEBUFFER >0 { (char *)1, 0, fbterm, 256, CONS_BAUD, NS16550HZ }, #endif { 0 } };与第一个串口除地址不同外其它的值都相同。
这样在函数 devinit 里就会自动扫描这个数组并初始化:
在 init_fs() 里面就会调用 term_open 函数打开第6个设备的函数调用 。参考文章:
http://blog.chinaunix.net/uid-14214482-id-330781.html