[U-Boot] [PATCH 1/2 V2] IOMUX: Add console multiplexing support.

Gary Jennejohn garyj at denx.de
Thu Oct 30 14:21:53 CET 2008


On Thu, 30 Oct 2008 12:59:49 +0100
Wolfgang Denk <wd at denx.de> wrote:

> In message <20081030112119.59036e03 at ernst.jennejohn.org> you wrote:
> > 
> > Modifications to support console multiplexing.  This is controlled using
> > CONFIG_SYS_CONSOLE_MUX in the board configuration file.
> 
> Is it correct to assume that this patch version does not yet  include
> any  changes  resultiung  from the discussion we had yesterday how to
> optimize timing behaviour?
> 

Yes.  It also doesn't appear to me that this has been resolved yet.

> 
> > diff --git a/common/Makefile b/common/Makefile
> > index f00cbd9..0296a8a 100644
> > --- a/common/Makefile
> > +++ b/common/Makefile
> > @@ -156,6 +156,7 @@ COBJS-$(CONFIG_LYNXKDI) += lynxkdi.o
> >  COBJS-$(CONFIG_UPDATE_TFTP) += update.o
> >  COBJS-$(CONFIG_USB_KEYBOARD) += usb_kbd.o
> >  COBJS-$(CONFIG_DDR_SPD) += ddr_spd.o
> > +COBJS-$(CONFIG_SYS_CONSOLE_MUX) += iomux.o
> 
> As this is a user configurable option, it should be named
> CONFIG_CONSOLE_MUX. Please remove the "SYS_" part. And please keep the
> list sorted.
> 

I chose this analogously to CONFIG_SYS_CONSOLE_IS_IN_ENV.  Obviously I
misunderstand something about how these names are chosen.  Please
enlighten me.

> > @@ -115,7 +182,41 @@ void serial_printf (const char *fmt, ...)
> >  int fgetc (int file)
> >  {
> >  	if (file < MAX_FILES)
> > +#if defined(CONFIG_SYS_CONSOLE_MUX)
> > +	{
> 
> Please move the braces outside the #ifdef.
> 

OK

> > +		int cntr = 0;
> > +
> > +		/*
> > +		 * Effectively poll for input wherever it may be available.
> > +		 */
> > +		for (;;) {
> > +			/*
> > +			 * Upper layer may have already called tstc() so
> > +			 * check for that first.
> > +			 */
> > +			if (tstcdev != NULL)
> > +				return iomux_getc();
> > +			iomux_tstc(file);
> > +			/*
> > +			 * Even this is too slow for serial cut&paste due
> > +			 * to the overhead of calling tstc() then getc().
> > +			 * It gets worse if nc is set as a console because
> > +			 * nc_tstc() is rather slow.
> 
> We discussed a potential solution to this problem yesterday. Do you
> want to try this out in your implementation?
> 

See my comment above.

> > +			 * The idea behind this counter is to avoid calling
> > +			 * the watchdog via udelay() too often.
> > +			 * COUNT_TIL_UDELAY is defined in iomux.h and is just
> > +			 * a guesstimate.
> > +			 */
> > +			if (cntr++ > COUNT_TIL_UDELAY) {
> > +				udelay(1);
> > +				cntr = 0;
> > +			}
> 
> Please do not re-invent the wheel. If the  watchdog  triggering  rate
> needs  to be limited then please use the CONFIG_WD_MAX_RATE parameter
> as it was done in commit d32a874b  "lwmon5  watchdog:  limit  trigger
> rate".
> 

Sounds good to me.  I wasn't aware of CONFIG_WD_MAX_RATE.  There are way
too many undocumented options in u-boot.

> > @@ -123,7 +224,11 @@ int fgetc (int file)
> >  int ftstc (int file)
> >  {
> >  	if (file < MAX_FILES)
> > +#if defined(CONFIG_SYS_CONSOLE_MUX)
> > +		return iomux_tstc(file);
> > +#else
> >  		return stdio_devices[file]->tstc ();
> > +#endif
> >  
> >  	return -1;
> >  }
> > @@ -131,13 +236,21 @@ int ftstc (int file)
> >  void fputc (int file, const char c)
> >  {
> >  	if (file < MAX_FILES)
> > +#if defined(CONFIG_SYS_CONSOLE_MUX)
> > +		iomux_putc(file, c);
> > +#else
> >  		stdio_devices[file]->putc (c);
> > +#endif
> >  }
> >  
> >  void fputs (int file, const char *s)
> >  {
> >  	if (file < MAX_FILES)
> > +#if defined(CONFIG_SYS_CONSOLE_MUX)
> > +		iomux_puts(file, s);
> > +#else
> >  		stdio_devices[file]->puts (s);
> > +#endif
> >  }
> 
> Would it be possible to just set 
> 
> 	stdio_devices[file]->tstc = iomux_tstc;
> 	stdio_devices[file]->putc = iomux_putc;
> 	stdio_devices[file]->puts = iomux_puts;
> 
> ?
> 
> This would eliminate the need for these #ifdef's (and possibly a  few
> more) ?
> 
> > @@ -438,15 +562,34 @@ int console_init_r (void)
> >  	}
> >  	/* Initializes output console first */
> >  	if (outputdev != NULL) {
> > +#ifdef CONFIG_SYS_CONSOLE_MUX
> > +		/* need to set a console if not done above. */
> > +		iomux_doenv(stdout, outputdev->name);
> > +#else
> >  		console_setfile (stdout, outputdev);
> > +#endif
> >  	}
> >  	if (errdev != NULL) {
> > +#ifdef CONFIG_SYS_CONSOLE_MUX
> > +		/* need to set a console if not done above. */
> > +		iomux_doenv(stderr, errdev->name);
> > +#else
> >  		console_setfile (stderr, errdev);
> > +#endif
> >  	}
> >  	if (inputdev != NULL) {
> > +#ifdef CONFIG_SYS_CONSOLE_MUX
> > +		/* need to set a console if not done above. */
> > +		iomux_doenv(stdin, inputdev->name);
> > +#else
> >  		console_setfile (stdin, inputdev);
> > +#endif
> >  	}
> 
> See above. Maybe these #ifdef's could be eliminated as well?
> 
> > @@ -455,21 +598,33 @@ int console_init_r (void)
> >  	if (stdio_devices[stdin] == NULL) {
> >  		puts ("No input devices available!\n");
> >  	} else {
> > +#ifdef CONFIG_SYS_CONSOLE_MUX
> > +		iomux_printdevs(stdin);
> > +#else
> >  		printf ("%s\n", stdio_devices[stdin]->name);
> > +#endif
> >  	}
> >  
> >  	puts ("Out:   ");
> >  	if (stdio_devices[stdout] == NULL) {
> >  		puts ("No output devices available!\n");
> >  	} else {
> > +#ifdef CONFIG_SYS_CONSOLE_MUX
> > +		iomux_printdevs(stdout);
> > +#else
> >  		printf ("%s\n", stdio_devices[stdout]->name);
> > +#endif
> >  	}
> >  
> >  	puts ("Err:   ");
> >  	if (stdio_devices[stderr] == NULL) {
> >  		puts ("No error devices available!\n");
> >  	} else {
> > +#ifdef CONFIG_SYS_CONSOLE_MUX
> > +		iomux_printdevs(stderr);
> > +#else
> >  		printf ("%s\n", stdio_devices[stderr]->name);
> > +#endif
> >  	}
> 
> And these, too?
> 

Seems like a good idea.  I'll look at it.

> > +	int i;
> > +	device_t *dev;
> > +
> > +	for (i = 0; i < cd_count[console]; i++) {
> > +		dev = console_devices[console][i];
> > +		serial_printf("%s ", dev->name);
> > +	}
> > +	serial_printf("\n");
> > +}
> 
> The old code uses plain printf(), while your new version explicitely
> calls serial_printf(). I gues sthis is intentional? What is the
> rationale behind it?
> 

To avoid writing to e.g. nc before it's ready for output.

> > +/* This tries to preserve the old list if an error occurs. */
> > +int iomux_doenv(const int console, const char *arg)
> > +{
> > +	char *console_args, *temp, **start;
> > +	int i, j, io_flag, cs_idx;
> > +	device_t *dev;
> > +	device_t **cons_set;
> > +
> > +#ifdef CONFIG_SYS_CONSOLE_IS_IN_ENV
> > +	if (arg == NULL)
> > +		return 1;
> > +#endif
> 
> Do we need this special case test?
> 
> > +	console_args = strdup(arg);
> > +	if (console_args == NULL)
> > +		return 1;
> 
> I think this test here would catch it as well?
> 

Ah, I see that it's safe to pass NULL to strdup().  OK, I can simplify the
code knowing that.

> > +	/*
> > +	 * Check whether a comma separated list of devices was
> > +	 * entered and count how many devices were entered.
> > +	 * The array start[] has pointers to the beginning of
> > +	 * each device name (up to MAX_CONSARGS devices).
> > +	 *
> > +	 * Have to do this twice - once to count the number of
> > +	 * commas and then again to populate start.
> > +	 */
> > +	i = 0;
> > +	temp = console_args;
> > +	for (;;) {
> > +		temp = strchr(temp, ',');
> > +		if (temp != NULL) {
> > +			i++;
> > +			temp++;
> > +			continue;
> > +		}
> > +		/* There's always one entry more than the number of commas. */
> > +		i++;
> > +		break;
> > +	}
> > +	start = (char **)malloc(i * sizeof(char *));
> > +	if (start == NULL) {
> > +		free(console_args);
> > +		return 1;
> > +	}
> > +	i = 0;
> > +	start[0] = console_args;
> > +	for (;;) {
> > +		temp = strchr(start[i], ',');
> > +		if (temp != NULL) {
> > +			i++;
> > +			*temp = '\0';
> > +			start[i] = temp + 1;
> > +			continue;
> > +		}
> > +		/* There's always one entry more than the number of commas. */
> > +		i++;
> > +		break;
> > +	}
> 
> I suggest to swap logic around as this allows for easier to read code:
> 
> 	for (;;) {
> 		temp = strchr(start[i++], ',');
> 		if (temp == NULL)
> 			break;
> 		*temp = '\0';
> 		start[i] = temp + 1;
> 	}
> 

Seems reasonable, but I have to consider it in more detail.

> > +	cons_set = (device_t **)calloc(1, i * sizeof(device_t *));
> 
> Why not:
> 
> 	cons_set = (device_t **)calloc(i, sizeof(device_t *));
> 
> ?
> 

Hadn't thought of that.  A good idea.

> > +	if (cons_set == NULL) {
> > +		free(start);
> > +		free(console_args);
> > +		return 1;
> > +	}
> > +
> > +	switch (console) {
> > +	case stdin:
> > +		io_flag = DEV_FLAGS_INPUT;
> > +		break;
> > +	case stdout:
> > +	case stderr:
> > +		io_flag = DEV_FLAGS_OUTPUT;
> > +		break;
> > +	default:
> > +		return 1;
> 
> Memory leak? free start & console_args & cons_set ??
> 

Yes, could be.  I added default as an afterthought and didn't consider all
its implications.

> > +Two new files, common/iomux.c and include/iomux.h, contain the heart
> > +of the environment setting implementation.
> 
> It seems to me that they do much more than just setting environment
> variables?
> 

Their whole reason for their existence is to handle setenv.

> > +The execution is then in common/cmd_nvedit.c and common/console.c.
> 
> Hm... but cmd_nvedit.c really is for environment variables handling
> only?
> 

Yeah, the wording could be improved.

> > +A user can use a comma-separated list of devices to set stdin, stdout
> > +and stderr.  For example: setenv stdin serial,nc.  NOTE: No spaces
> 
> Maybe it's better to quote the example to make it stand out:
> 
> ...and stderr.  For example: "setenv stdin serial,nc".  NOTE: No spaces
> 
> ?
> 

I thought about that myself but didn't do it.

> > +CAVEATS
> > +-------
> > +
> > +Note that common/iomux.c calls console_assign() for every registered
> > +device as it is discovered.  This means that the environment settings
> > +for application consoles will be set to the last device in the list.
> > +
> > +The overhead associated with calling tstc() and then getc() means that
> > +copy&paste will normally not work, even if serial is set as the only device
> > +for stdin, stdout and stderr.
> 
> You should probably mention the conditions where you tested this with
> such results. It probably depends on the system performance  and  the
> console baud rate, doesn't it?
> 

True.

> > +Using nc as a stdin device results in even more overhead because nc_tstc()
> > +is quite slow.
> 
> There was discussion before how that could be  accelerated.  Has  any
> work spent on actually implementing / testing such measures?
> 

Not by me.

> > diff --git a/include/iomux.h b/include/iomux.h
> > new file mode 100644
> > index 0000000..75b66e2
> > --- /dev/null
> > +++ b/include/iomux.h
> ...
> > +/*
> > + * Avoid invoking WATCHDOG via udelay() on every pass through the loop
> > + * in fgetc(). Note this is all based on guesswork.
> > + */
> > +#define COUNT_TIL_UDELAY	10000
> 
> I think we should use a more determinitic approach. See comment above.
> 

I agree.

---
Gary Jennejohn
*********************************************************************
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-0 Fax: +49-8142-66989-80  Email: office at denx.de
*********************************************************************


More information about the U-Boot mailing list