Re: RFC: drivers/char/watchdog/pcwd.c + drivers/char/watchdog/pcwd_pci.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, 19 Apr 2007 21:00:55 +0200
Wim Van Sebroeck <[email protected]> wrote:

> Hi Andrew,
> 
> > > On Tue, 17 Apr 2007 20:58:49 +0200 Wim Van Sebroeck <[email protected]> wrote:
> > > Would anyone object if we would merge the "full bells and whistles" drivers for
> > > the pcwd isa and pci cards in the kernel tree. (It basically only adds some extra
> > > /proc routines). This would make it easier to maintain the "full bells and whistles"
> > > driver.
> > 
> > It's hard to answer that question.  Please send the patches out in the
> > usual manner for review and comment.
> 
> for the pcwd.c driver the patch would be as follows (see below).
> I'll create the patch for pcwd_pci.c later this week.
> 
>  #include <linux/module.h>	/* For module specific items */
> @@ -65,13 +73,18 @@
>  #include <linux/fs.h>		/* For file operations */
>  #include <linux/ioport.h>	/* For io-port access */
>  #include <linux/spinlock.h>	/* For spin_lock/spin_unlock/... */
> +#include <linux/string.h>	/* For string manipulations */
> +#ifdef CONFIG_PROC_FS
> +#include <linux/stat.h>		/* For S_IFREG/S_IRUGO/S_IWUSR */
> +#include <linux/proc_fs.h>	/* For create_proc_entry/remove_proc_entry/ ... */
> +#endif

Please avoid the ifdefs here if at all possible.  They increase the chances
of the build failing as people change configs.

>  /* module parameters */
> +#define CELSIUS		0
> +#define FAHRENHEIT	1
> +static int proc_temp_mode = CELSIUS;
> +module_param(proc_temp_mode, int, 0);
> +MODULE_PARM_DESC(proc_temp_mode, "which temperature mode to use in /proc/ 0=Celsius, 1=Fahrenheit (default=0)");

hm, is that a good idea?  Making the contents of /proc files dependent upon
some module parameter?

I'd have thought that it would be better to remove this option and either

a) offer two /proc files: one for Celcius, one for Fahrenheit

b) Put both values into the same /proc file (one per line)

c) Just remove the Fahrenheit option altogether - let it join cubits,
   furlongs, etc.

I mean, how is an application to make sense of that information if its units
depend upon some module parameter, or a kernel boot option?

> +	pcwd_private.card_status=ENABLED;

Missing a ' ' around the '='.  Several instances.

> -static int pcwd_get_temperature(int *temperature)
> +static int pcwd_get_temperature(int *temperature, int temp_mode)
>  {
>  	/* check that port 0 gives temperature info and no command results */
>  	if (pcwd_private.command_mode)
> @@ -543,22 +577,358 @@
>  	if (!pcwd_private.supports_temp)
>  		return -ENODEV;
>  
> +	spin_lock(&pcwd_private.io_lock);
> +	*temperature = inb(pcwd_private.io_addr);
> +	spin_unlock(&pcwd_private.io_lock);

I doubt if the locking here does anything useful.

> +
>  	/*
> -	 * Convert celsius to fahrenheit, since this was
> +	 * Convert celsius to fahrenheit, this is normally
>  	 * the decided 'standard' for this return value.
>  	 */
> -	spin_lock(&pcwd_private.io_lock);
> -	*temperature = ((inb(pcwd_private.io_addr)) * 9 / 5) + 32;
> -	spin_unlock(&pcwd_private.io_lock);
> +	if (temp_mode == FAHRENHEIT)
> +		*temperature = (*temperature * 9 / 5) + 32;
>  
>  	if (debug >= DEBUG) {
> -		printk(KERN_DEBUG PFX "temperature is: %d F\n",
> -			*temperature);
> +		printk(KERN_DEBUG PFX "temperature is: %d %s\n",
> +			*temperature, (temp_mode == CELSIUS) ? "C" :"F");
>  	}
>  
>  	return 0;
>  }
>  
> +#ifdef CONFIG_PROC_FS
> +static int
> +pcwd_reset_pc(void)

	static int pcwd_reset_pc(void)

please.


> +static int
> +pcwd_set_timeout(char *sub_command)

Ditto (whole patchset (perhaps whole subsystem ;)))

> +{
> +	char *new_sub_command = sub_command;
> +	int i;
> +
> +	while (*sub_command == ' ')
> +		sub_command++;
> +	if (sub_command == new_sub_command) {
> +		printk(KERN_ERR PFX "illegal timeout argument '%s'\n", sub_command);
> +		return 1;
> +	}
> +	i = simple_strtoul(sub_command, NULL, 0);

darn, we do this sort of gunk in a lot of places.  Isn't there some library
function somewhere we can use?

> +	if (!pcwd_set_heartbeat(i)) {
> +		if (debug >= VERBOSE)
> +			printk(KERN_INFO PFX "timeout %d\n", heartbeat);
> +	} else {
> +		printk(KERN_ERR PFX
> +			"illegal timeout argument '%s', should be a number between 2 and 7200\n",


80 columns?

> +			sub_command);
> +		return 1;
> +	}
> +	return 0;
> +}
> +
>
> ..
>
> +static void
> +pcwd_user_help(void)
> +{
> +	printk(" pcwd proc command interface help:\n");
> +	printk("   ping, pong, tickle - tickle the timer\n");
> +	printk("   hardreset - reset pc\n");
> +	printk("   enable, start - start the watchdog\n");
> +	printk("   disable, stop - stop the watchdog\n");
> +	printk("   clear - clear trip status and led\n");
> +	printk("   verbose, debug, quiet - command reponse\n");
> +	printk("   fahrenheit, celsius - display temp in\n");
> +	printk(" extended commands:\n");
> +	printk("   arm [val] - isa [30,60,90]\n");
> +	printk("   timeout [val]\n");
> +}

This could all be done with a single printk() statement.

It's missing a KERN_FOO facility level.

> +static int
> +pcwd_user_command(char *user_command)
> +{
> +	if ((strcmp(user_command, "ping") == 0)
> +	    || (strcmp(user_command, "pong") == 0)
> +	    || (strcmp(user_command, "tickle") == 0)) {
> +		pcwd_keepalive();
> +		if (debug >= VERBOSE)
> +			printk(KERN_INFO PFX "ping...\n");
> +	} else if ((strcmp(user_command, "enable") == 0)
> +	    || (strcmp(user_command, "start") == 0)) {
> +		if (!pcwd_start()) {
> +			if (debug >= VERBOSE)
> +				printk(KERN_INFO PFX "watchdog enabled.\n");
> +		}
> +	} else if ((strcmp(user_command, "disable") == 0)
> +	    || (strcmp(user_command, "stop") == 0)) {
> +		if (!pcwd_stop()) {
> +			if (debug >= VERBOSE)
> +				printk(KERN_INFO PFX "watchdog disabled!\n");
> +		}
> +	} else if (strcmp(user_command, "clear") == 0) {
> +		pcwd_clear_status();
> +		if (debug >= VERBOSE)
> +			printk(KERN_INFO PFX "cleared trip status\n");
> +	} else if (strcmp(user_command, "hardreset") == 0) {
> +		pcwd_reset_pc();
> +		if (debug >= VERBOSE)
> +			printk(KERN_INFO PFX "hardreset PC!\n");
> +	} else if (strcmp(user_command, "quiet") == 0) {
> +		debug = QUIET;
> +	} else if (strcmp(user_command, "verbose") == 0) {
> +		debug = VERBOSE;
> +		printk(KERN_INFO PFX "verbose mode on.\n");
> +	} else if (strcmp(user_command, "debug") == 0) {
> +		debug = DEBUG;
> +		printk(KERN_INFO PFX "debug mode on.\n");
> +	} else if (strcmp(user_command, "celsius") == 0) {
> +		proc_temp_mode = CELSIUS;
> +		if (debug >= VERBOSE)
> +			printk(KERN_INFO PFX "display temp in Celsius.\n");
> +	} else if (strcmp(user_command, "fahrenheit") == 0) {
> +		proc_temp_mode = FAHRENHEIT;
> +		if (debug >= VERBOSE)
> +			printk(KERN_INFO PFX "display temp in Fahrenheit.\n");
> +	} else if (strcmp(user_command, "help") == 0) {
> +		pcwd_user_help();
> +	} else if (strncmp(user_command, "arm", 3) == 0) {
> +		if ((pcwd_private.fw_ver_str[0] >= '1')
> +		    && (pcwd_private.fw_ver_str[2] >= '2')) {
> +			if (pcwd_set_arm(&user_command[3])) {
> +				printk(KERN_ERR PFX "arm FAILED.\n");
> +				return 0;
> +			}
> +		} else {
> +			printk(KERN_ERR
> +			       "pcwd: ISA Card and firmware > 1.20 needed.\n");
> +			return 0;
> +		}
> +	} else if (strncmp(user_command, "timeout", 7) == 0) {
> +		if (pcwd_set_timeout(&user_command[7])) {
> +			printk(KERN_ERR PFX "timeout FAILED.\n");
> +			return 0;
> +		}
> +	} else {
> +		printk(KERN_ERR "illegal user command: '%s'\n", user_command);
> +		pcwd_user_help();
> +		return 0;
> +	}
> +	return 1;
> +}

parse, parse, parse, parse.  We really do need stronger libraries for this
sort of thing.

> +static int
> +pcwd_write_proc(struct file *file, const char *buffer,
> +		unsigned long count, void *data)
> +{
> +	char command_buffer[80];
> +	int rc, length;
> +
> +	/* write only allowed for root users */
> +	if (current->euid != 0)
> +		return -EACCES;

Are the permissions on the /proc file not sufficient?

Surely it's wrong to be testing for root anyway.  capable(CAP_SYS_ADMIN)
would be more typical.  Or CAP_RAW_IO or something.

But not this.

> +	if (count > sizeof (command_buffer) - 1)
> +		return -EINVAL;
> +
> +	if (copy_from_user(command_buffer, buffer, count))
> +		return -EFAULT;
> +
> +	command_buffer[count] = '\0';
> +	length = strlen(command_buffer);
> +	if (command_buffer[length - 1] == '\n')
> +		command_buffer[--length] = '\0';
> +
> +	spin_lock(&pcwd_private.proc_lock);
> +	rc = pcwd_user_command(command_buffer);
> +	spin_unlock(&pcwd_private.proc_lock);
> +	return (rc ? count : -EBUSY);
> +}
> +
> +/* This macro frees the machine specific function from bounds checking and
> + *  * this like that... [from nvram.c]*/
> +#define PRINT_PROC(fmt,args...)                         \
> +    do {                                                \
> +        *len += sprintf( buffer+*len, fmt, ##args );    \
> +        if (*begin + *len > offset + size)              \
> +            return( 0 );                                \
> +        if (*begin + *len < offset) {                   \
> +            *begin += *len;                             \
> +            *len = 0;                                   \
> +        }                                               \
> +    } while(0)

A `return' hidden in a macro like this is pretty foul.  We'd prefer that
new instances not be added to the kernel.

Can we please find a tasteful way of reimplementing this?  Cannot the
seq_file interface be used?

> +static int
> +pcwd_proc_info(unsigned char *buf, char *buffer, int *len,
> +	       off_t * begin, off_t offset, int size)
> +{
> +	int sw, i;
> +
> +	PRINT_PROC("%s\n", DRIVER_VERSION);
> +
> +	if (pcwd_private.revision == PCWD_REVISION_C) {
> +		PRINT_PROC("Firmware     : Version %s\n", pcwd_private.fw_ver_str);
> +		PRINT_PROC("Option Switch: Delay Time ");
> +		sw = pcwd_get_option_switches();
> +		switch (sw & 0x07) {
> +		case 0:
> +			PRINT_PROC("20 s");
> +			break;
> +		case 1:
> +			PRINT_PROC("40 s");
> +			break;
> +		case 2:
> +			PRINT_PROC("1 min");
> +			break;
> +		case 3:
> +			PRINT_PROC("5 min");
> +			break;
> +		case 4:
> +			PRINT_PROC("10 min");
> +			break;
> +		case 5:
> +			PRINT_PROC("30 min");
> +			break;
> +		case 6:
> +			PRINT_PROC("1 h");
> +			break;
> +		case 7:
> +			PRINT_PROC("2 h");
> +			break;
> +		}
> +		PRINT_PROC((sw & 0x08) ? ", POD on" : ", POD off");
> +		PRINT_PROC((sw & 0x10) ? ", TRE on" : ", TRE off");
> +		PRINT_PROC((sw & 0x20) ? ", R2M on" : ", R2M off");
> +		PRINT_PROC((sw & 0x40) ? ", R1M on" : ", R1M off");
> +		PRINT_PROC((sw & 0x80) ? ", RTM on\n" : ", RTM off\n");
> +	} else {
> +		PRINT_PROC("Firmware ROM not present\n");
> +	}
> +
> +	PRINT_PROC("Temperature  : ");
> +	if (pcwd_private.supports_temp && (!pcwd_get_temperature(&i, proc_temp_mode))) {
> +		PRINT_PROC("%d ", i);
> +		PRINT_PROC((proc_temp_mode == CELSIUS) ? "__C\n" : "__F\n");
> +	} else {
> +		PRINT_PROC("Card without temp option\n");
> +	}
> +
> +	if (pcwd_private.card_status == ENABLED)
> +		PRINT_PROC("Card status  : Timer enabled\n");
> +	else
> +		PRINT_PROC("Card status  : Timer disabled\n");
> +
> +	spin_lock(&pcwd_private.io_lock);
> +	if (pcwd_private.revision == PCWD_REVISION_A) {
> +		sw = inb_p(pcwd_private.io_addr) & WD_WDRST;
> +	} else {
> +		sw = inb_p(pcwd_private.io_addr + 1) & WD_REVC_WTRP;
> +	}
> +	spin_unlock(&pcwd_private.io_lock);
> +	PRINT_PROC("Reboot status: ");
> +	PRINT_PROC(sw ? "Tripped\n" : "Not tripped\n");
> +
> +	if ((pcwd_private.boot_status & WDIOF_CARDRESET) && (pcwd_private.boot_status & WDIOF_OVERHEAT)) {
> +		PRINT_PROC("Boot status  : CPU Overheat  + Watchdog caused reboot\n");
> +	} else if (pcwd_private.boot_status & WDIOF_CARDRESET) {
> +		PRINT_PROC("Boot status  : Watchdog caused reboot\n");
> +	} else if (pcwd_private.boot_status & WDIOF_OVERHEAT) {
> +		PRINT_PROC("Boot status  : CPU Overheat\n");
> +	} else {
> +		PRINT_PROC("Boot status  : Cold boot or reset\n");
> +	}

Lots of braces can (shold) be removed in here.

> +	// PRINT_PROC("Ping Count   : %d\n", ping_counter);
> +	return 1;
> +}
> +
> +static int
> +pcwd_read_proc(char *buffer, char **start, off_t offset,
> +	       int size, int *eof, void *data)
> +{
> +	int len = 0;
> +	off_t begin = 0;
> +
> +	spin_lock(&pcwd_private.proc_lock);
> +	*eof = pcwd_proc_info(NULL, buffer, &len, &begin, offset, size);
> +	spin_unlock(&pcwd_private.proc_lock);
> +
> +	if (offset >= begin + len)
> +		return (0);
> +	*start = buffer + (offset - begin);	// CORRECTED !!!!
> +	return (size < begin + len - offset ? size : begin + len - offset);
> +}
> +#endif				/* PROC_FS */
> +
>  
> ...
>
> +#ifdef CONFIG_PROC_FS
> +	if ((pcwd_private.proc_pcwd =
> +	     create_proc_entry(WATCHDOG_NAME, S_IFREG | S_IRUGO | S_IWUSR, 0))) {
> +		pcwd_private.proc_pcwd->read_proc = pcwd_read_proc;
> +		pcwd_private.proc_pcwd->write_proc = pcwd_write_proc;
> +	}
> +#endif

We prefer

	a = b();
	if (a)

over

	if ((a = b())



-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux