led.c

来自「linux 内核源代码」· C语言 代码 · 共 764 行 · 第 1/2 页

C
764
字号
/* *    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-2005 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> *	- Switch from using a tasklet to a work queue, so the led_LCD_driver *	  	can sleep. *	  			  David Pye <dmp@davidmpye.dyndns.org> */#include <linux/module.h>#include <linux/stddef.h>	/* for offsetof() */#include <linux/init.h>#include <linux/types.h>#include <linux/ioport.h>#include <linux/utsname.h>#include <linux/capability.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 <linux/workqueue.h>#include <linux/rcupdate.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 work queue   task which is scheduled regularly, and since the calculations may consume a    relatively large amount of CPU time, some of the calculations can be    turned off with the following variables (controlled via procfs) */static int led_type __read_mostly = -1;static unsigned char lastleds;	/* LED state from most recent update */static unsigned int led_heartbeat __read_mostly = 1;static unsigned int led_diskio    __read_mostly = 1;static unsigned int led_lanrxtx   __read_mostly = 1;static char lcd_text[32]          __read_mostly;static char lcd_text_default[32]  __read_mostly;static struct workqueue_struct *led_wq;static void led_work_func(struct work_struct *);static DECLARE_DELAYED_WORK(led_task, led_work_func);#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) */	unsigned long lcd_cmd_reg_addr;	/* ptr to LCD cmd-register & data ptr for LED */	unsigned long 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))) __read_mostly ={	.model =		DISPLAY_MODEL_LCD,	.lcd_width =		16,	.lcd_cmd_reg_addr =	KITTYHAWK_LCD_CMD,	.lcd_data_reg_addr =	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 */#define LED_HASLCD 1#define LED_NOLCD  0/* The workqueue must be created at init-time */static int start_task(void) {		/* Display the default text now */	if (led_type == LED_HASLCD) lcd_print( lcd_text_default );	/* Create the work queue and queue the LED task */	led_wq = create_singlethread_workqueue("led_wq");		queue_delayed_work(led_wq, &led_task, 0);	return 0;}device_initcall(start_task);/* ptr to LCD/LED-specific function */static void (*led_func_ptr) (unsigned char) __read_mostly;#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;	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->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->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()   **    */static void led_LCD_driver(unsigned char leds){	static int i;	static unsigned char mask[4] = { LED_HEARTBEAT, LED_DISK_IO,		LED_LAN_RCV, LED_LAN_TX };		static struct lcd_block * blockp[4] = {		&lcd_info.heartbeat,		&lcd_info.disk_io,		&lcd_info.lan_rcv,		&lcd_info.lan_tx	};	/* Convert min_cmd_delay to milliseconds */	unsigned int msec_cmd_delay = 1 + (lcd_info.min_cmd_delay / 1000);		for (i=0; i<4; ++i) 	{		if ((leds & mask[i]) != (lastleds & mask[i])) 		{			gsc_writeb( blockp[i]->command, LCD_CMD_REG );			msleep(msec_cmd_delay);						gsc_writeb( leds & mask[i] ? blockp[i]->on : 					blockp[i]->off, LCD_DATA_REG );			msleep(msec_cmd_delay);		}	}}/*   **    ** led_get_net_activity()   **    ** calculate if there was TX- or RX-throughput 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 a workqueue task, so locking dev_base 	 * for reading should be OK */	read_lock(&dev_base_lock);	rcu_read_lock();	for_each_netdev(&init_net, dev) {	    struct net_device_stats *stats;	    struct in_device *in_dev = __in_dev_get_rcu(dev);	    if (!in_dev || !in_dev->ifa_list)		continue;	    if (LOOPBACK(in_dev->ifa_list->ifa_local))		continue;	    stats = dev->get_stats(dev);	    rx_total += stats->rx_packets;	    tx_total += stats->tx_packets;	}	rcu_read_unlock();	read_unlock(&dev_base_lock);	retval = 0;	if (rx_total != rx_total_last) {		rx_total_last = rx_total;		retval |= LED_LAN_RCV;	}

⌨️ 快捷键说明

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