led.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 757 行 · 第 1/2 页

C
757
字号
/* *    Chassis LCD/LED driver for HP-PARISC workstations * *      (c) Copyright 2000 Red Hat Software *      (c) Copyright 2000 Helge Deller <hdeller@redhat.com> *      (c) Copyright 2001-2004 Helge Deller <deller@gmx.de> *      (c) Copyright 2001 Randolph Chung <tausq@debian.org> * *      This program is free software; you can redistribute it and/or modify *      it under the terms of the GNU General Public License as published by *      the Free Software Foundation; either version 2 of the License, or *      (at your option) any later version. * * TODO: *	- speed-up calculations with inlined assembler *	- interface to write to second row of LCD from /proc (if technically possible) * * Changes: *      - Audit copy_from_user in led_proc_write. *                                Daniele Bellucci <bellucda@tiscali.it> */#include <linux/config.h>#include <linux/module.h>#include <linux/stddef.h>	/* for offsetof() */#include <linux/init.h>#include <linux/types.h>#include <linux/ioport.h>#include <linux/version.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/inetdevice.h>#include <linux/in.h>#include <linux/interrupt.h>#include <linux/kernel_stat.h>#include <linux/reboot.h>#include <linux/proc_fs.h>#include <linux/ctype.h>#include <linux/blkdev.h>#include <asm/io.h>#include <asm/processor.h>#include <asm/hardware.h>#include <asm/param.h>		/* HZ */#include <asm/led.h>#include <asm/pdc.h>#include <asm/uaccess.h>/* The control of the LEDs and LCDs on PARISC-machines have to be done    completely in software. The necessary calculations are done in a tasklet   which is scheduled at every timer interrupt and since the calculations    may consume relatively much CPU-time some of the calculations can be    turned off with the following variables (controlled via procfs) */static int led_type = -1;static int led_heartbeat = 1;static int led_diskio = 1;static int led_lanrxtx = 1;static char lcd_text[32];static char lcd_text_default[] = "Linux " UTS_RELEASE;#if 0#define DPRINTK(x)	printk x#else#define DPRINTK(x)#endifstruct lcd_block {	unsigned char command;	/* stores the command byte      */	unsigned char on;	/* value for turning LED on     */	unsigned char off;	/* value for turning LED off    */};/* Structure returned by PDC_RETURN_CHASSIS_INFO *//* NOTE: we use unsigned long:16 two times, since the following member    lcd_cmd_reg_addr needs to be 64bit aligned on 64bit PA2.0-machines */struct pdc_chassis_lcd_info_ret_block {	unsigned long model:16;		/* DISPLAY_MODEL_XXXX */	unsigned long lcd_width:16;	/* width of the LCD in chars (DISPLAY_MODEL_LCD only) */	char *lcd_cmd_reg_addr;		/* ptr to LCD cmd-register & data ptr for LED */	char *lcd_data_reg_addr;	/* ptr to LCD data-register (LCD only) */	unsigned int min_cmd_delay;	/* delay in uS after cmd-write (LCD only) */	unsigned char reset_cmd1;	/* command #1 for writing LCD string (LCD only) */	unsigned char reset_cmd2;	/* command #2 for writing LCD string (LCD only) */	unsigned char act_enable;	/* 0 = no activity (LCD only) */	struct lcd_block heartbeat;	struct lcd_block disk_io;	struct lcd_block lan_rcv;	struct lcd_block lan_tx;	char _pad;};/* LCD_CMD and LCD_DATA for KittyHawk machines */#define KITTYHAWK_LCD_CMD  F_EXTEND(0xf0190000UL) /* 64bit-ready */#define KITTYHAWK_LCD_DATA (KITTYHAWK_LCD_CMD+1)/* lcd_info is pre-initialized to the values needed to program KittyHawk LCD's  * HP seems to have used Sharp/Hitachi HD44780 LCDs most of the time. */static struct pdc_chassis_lcd_info_ret_blocklcd_info __attribute__((aligned(8))) ={	.model =		DISPLAY_MODEL_LCD,	.lcd_width =		16,	.lcd_cmd_reg_addr =	(char *) KITTYHAWK_LCD_CMD,	.lcd_data_reg_addr =	(char *) KITTYHAWK_LCD_DATA,	.min_cmd_delay =	40,	.reset_cmd1 =		0x80,	.reset_cmd2 =		0xc0,};/* direct access to some of the lcd_info variables */#define LCD_CMD_REG	lcd_info.lcd_cmd_reg_addr	 #define LCD_DATA_REG	lcd_info.lcd_data_reg_addr	 #define LED_DATA_REG	lcd_info.lcd_cmd_reg_addr	/* LASI & ASP only *//* ptr to LCD/LED-specific function */static void (*led_func_ptr) (unsigned char);#define LED_HASLCD 1#define LED_NOLCD  0#ifdef CONFIG_PROC_FSstatic int led_proc_read(char *page, char **start, off_t off, int count, 	int *eof, void *data){	char *out = page;	int len;	switch ((long)data)	{	case LED_NOLCD:		out += sprintf(out, "Heartbeat: %d\n", led_heartbeat);		out += sprintf(out, "Disk IO: %d\n", led_diskio);		out += sprintf(out, "LAN Rx/Tx: %d\n", led_lanrxtx);		break;	case LED_HASLCD:		out += sprintf(out, "%s\n", lcd_text);		break;	default:		*eof = 1;		return 0;	}	len = out - page - off;	if (len < count) {		*eof = 1;		if (len <= 0) return 0;	} else {		len = count;	}	*start = page + off;	return len;}static int led_proc_write(struct file *file, const char *buf, 	unsigned long count, void *data){	char *cur, lbuf[count + 1];	int d;	if (!capable(CAP_SYS_ADMIN))		return -EACCES;	memset(lbuf, 0, count + 1);	if (copy_from_user(lbuf, buf, count))		return -EFAULT;	cur = lbuf;	/* skip initial spaces */	while (*cur && isspace(*cur))	{		cur++;	}	switch ((long)data)	{	case LED_NOLCD:		d = *cur++ - '0';		if (d != 0 && d != 1) goto parse_error;		led_heartbeat = d;		if (*cur++ != ' ') goto parse_error;		d = *cur++ - '0';		if (d != 0 && d != 1) goto parse_error;		led_diskio = d;		if (*cur++ != ' ') goto parse_error;		d = *cur++ - '0';		if (d != 0 && d != 1) goto parse_error;		led_lanrxtx = d;		break;	case LED_HASLCD:		if (*cur && cur[strlen(cur)-1] == '\n')			cur[strlen(cur)-1] = 0;		if (*cur == 0) 			cur = lcd_text_default;		lcd_print(cur);		break;	default:		return 0;	}		return count;parse_error:	if ((long)data == LED_NOLCD)		printk(KERN_CRIT "Parse error: expect \"n n n\" (n == 0 or 1) for heartbeat,\ndisk io and lan tx/rx indicators\n");	return -EINVAL;}static int __init led_create_procfs(void){	struct proc_dir_entry *proc_pdc_root = NULL;	struct proc_dir_entry *ent;	if (led_type == -1) return -1;	proc_pdc_root = proc_mkdir("pdc", 0);	if (!proc_pdc_root) return -1;	proc_pdc_root->owner = THIS_MODULE;	ent = create_proc_entry("led", S_IFREG|S_IRUGO|S_IWUSR, proc_pdc_root);	if (!ent) return -1;	ent->nlink = 1;	ent->data = (void *)LED_NOLCD; /* LED */	ent->read_proc = led_proc_read;	ent->write_proc = led_proc_write;	ent->owner = THIS_MODULE;	if (led_type == LED_HASLCD)	{		ent = create_proc_entry("lcd", S_IFREG|S_IRUGO|S_IWUSR, proc_pdc_root);		if (!ent) return -1;		ent->nlink = 1;		ent->data = (void *)LED_HASLCD; /* LCD */		ent->read_proc = led_proc_read;		ent->write_proc = led_proc_write;		ent->owner = THIS_MODULE;	}	return 0;}#endif/*   **    ** led_ASP_driver()   **  */#define	LED_DATA	0x01	/* data to shift (0:on 1:off) */#define	LED_STROBE	0x02	/* strobe to clock data */static void led_ASP_driver(unsigned char leds){	int i;	leds = ~leds;	for (i = 0; i < 8; i++) {		unsigned char value;		value = (leds & 0x80) >> 7;		gsc_writeb( value,		 LED_DATA_REG );		gsc_writeb( value | LED_STROBE,	 LED_DATA_REG );		leds <<= 1;	}}/*   **    ** led_LASI_driver()   **  */static void led_LASI_driver(unsigned char leds){	leds = ~leds;	gsc_writeb( leds, LED_DATA_REG );}/*   **    ** led_LCD_driver()   **    ** The logic of the LCD driver is, that we write at every scheduled call   ** only to one of LCD_CMD_REG _or_ LCD_DATA_REG - registers.   ** That way we don't need to let this tasklet busywait for min_cmd_delay   ** milliseconds.   **   ** TODO: check the value of "min_cmd_delay" against the value of HZ.   **    */static void led_LCD_driver(unsigned char leds){	static int last_index;	/* 0:heartbeat, 1:disk, 2:lan_in, 3:lan_out */	static int last_was_cmd;/* 0: CMD was written last, 1: DATA was last */	struct lcd_block *block_ptr;	int value;	switch (last_index) {	    case 0:	block_ptr = &lcd_info.heartbeat;			value = leds & LED_HEARTBEAT;			break;	    case 1:	block_ptr = &lcd_info.disk_io;			value = leds & LED_DISK_IO;			break;						    case 2:	block_ptr = &lcd_info.lan_rcv;			value = leds & LED_LAN_RCV;			break;						    case 3:	block_ptr = &lcd_info.lan_tx;			value = leds & LED_LAN_TX;			break;	    default:	/* should never happen: */			return;	}	if (last_was_cmd) {	    /* write the value to the LCD data port */    	    gsc_writeb( value ? block_ptr->on : block_ptr->off, LCD_DATA_REG );	} else {	    /* write the command-byte to the LCD command register */    	    gsc_writeb( block_ptr->command, LCD_CMD_REG );	}    		/* now update the vars for the next interrupt iteration */ 	if (++last_was_cmd == 2) { /* switch between cmd & data */	    last_was_cmd = 0;	    if (++last_index == 4) 		last_index = 0;	 /* switch back to heartbeat index */	}}/*   **    ** led_get_net_activity()   **    ** calculate if there was TX- or RX-troughput on the network interfaces   ** (analog to dev_get_info() from net/core/dev.c)   **    */static __inline__ int led_get_net_activity(void){ #ifndef CONFIG_NET	return 0;#else	static unsigned long rx_total_last, tx_total_last;	unsigned long rx_total, tx_total;	struct net_device *dev;	int retval;	rx_total = tx_total = 0;		/* we are running as tasklet, so locking dev_base 	 * for reading should be OK */	read_lock(&dev_base_lock);	for (dev = dev_base; dev; dev = dev->next) {	    struct net_device_stats *stats;	    struct in_device *in_dev = __in_dev_get(dev);	    if (!in_dev || !in_dev->ifa_list)		continue;	    if (LOOPBACK(in_dev->ifa_list->ifa_local))		continue;	    if (!dev->get_stats) 		continue;	    stats = dev->get_stats(dev);	    rx_total += stats->rx_packets;	    tx_total += stats->tx_packets;	}	read_unlock(&dev_base_lock);	retval = 0;	if (rx_total != rx_total_last) {		rx_total_last = rx_total;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?