nvram.c

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

C
927
字号
/* * CMOS/NV-RAM driver for Linux * * Copyright (C) 1997 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> * idea by and with help from Richard Jelinek <rj@suse.de> * Portions copyright (c) 2001,2002 Sun Microsystems (thockin@sun.com) * * This driver allows you to access the contents of the non-volatile memory in * the mc146818rtc.h real-time clock. This chip is built into all PCs and into * many Atari machines. In the former it's called "CMOS-RAM", in the latter * "NVRAM" (NV stands for non-volatile). * * The data are supplied as a (seekable) character device, /dev/nvram. The * size of this file is dependent on the controller.  The usual size is 114, * the number of freely available bytes in the memory (i.e., not used by the * RTC itself). * * Checksums over the NVRAM contents are managed by this driver. In case of a * bad checksum, reads and writes return -EIO. The checksum can be initialized * to a sane state either by ioctl(NVRAM_INIT) (clear whole NVRAM) or * ioctl(NVRAM_SETCKS) (doesn't change contents, just makes checksum valid * again; use with care!) * * This file also provides some functions for other parts of the kernel that * want to access the NVRAM: nvram_{read,write,check_checksum,set_checksum}. * Obviously this can be used only if this driver is always configured into * the kernel and is not a module. Since the functions are used by some Atari * drivers, this is the case on the Atari. * * * 	1.1	Cesar Barros: SMP locking fixes * 		added changelog * 	1.2	Erik Gilling: Cobalt Networks support * 		Tim Hockin: general cleanup, Cobalt support */#define NVRAM_VERSION	"1.2"#include <linux/module.h>#include <linux/config.h>#include <linux/sched.h>#include <linux/smp_lock.h>#include <linux/nvram.h>#define PC		1#define ATARI		2#define COBALT		3/* select machine configuration */#if defined(CONFIG_ATARI)#  define MACH ATARI#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__)  /* and others?? */#define MACH PC#  if defined(CONFIG_COBALT)#    include <linux/cobalt-nvram.h>#    define MACH COBALT#  else#    define MACH PC#  endif#else#  error Cannot build nvram driver for this machine configuration.#endif#if MACH == PC/* RTC in a PC */#define CHECK_DRIVER_INIT()	1/* On PCs, the checksum is built only over bytes 2..31 */#define PC_CKS_RANGE_START	2#define PC_CKS_RANGE_END	31#define PC_CKS_LOC		32#define NVRAM_BYTES		(128-NVRAM_FIRST_BYTE)#define mach_check_checksum	pc_check_checksum#define mach_set_checksum	pc_set_checksum#define mach_proc_infos		pc_proc_infos#endif#if MACH == COBALT#define CHECK_DRIVER_INIT()     1#define NVRAM_BYTES		(128-NVRAM_FIRST_BYTE)#define mach_check_checksum	cobalt_check_checksum#define mach_set_checksum	cobalt_set_checksum#define mach_proc_infos		cobalt_proc_infos#endif#if MACH == ATARI/* Special parameters for RTC in Atari machines */#include <asm/atarihw.h>#include <asm/atariints.h>#define RTC_PORT(x)		(TT_RTC_BAS + 2*(x))#define CHECK_DRIVER_INIT()	(MACH_IS_ATARI && ATARIHW_PRESENT(TT_CLK))#define NVRAM_BYTES		50/* On Ataris, the checksum is over all bytes except the checksum bytes * themselves; these are at the very end */#define ATARI_CKS_RANGE_START	0#define ATARI_CKS_RANGE_END	47#define ATARI_CKS_LOC		48#define mach_check_checksum	atari_check_checksum#define mach_set_checksum	atari_set_checksum#define mach_proc_infos		atari_proc_infos#endif/* Note that *all* calls to CMOS_READ and CMOS_WRITE must be done with * rtc_lock held. Due to the index-port/data-port design of the RTC, we * don't want two different things trying to get to it at once. (e.g. the * periodic 11 min sync from time.c vs. this driver.) */#include <linux/types.h>#include <linux/errno.h>#include <linux/miscdevice.h>#include <linux/slab.h>#include <linux/ioport.h>#include <linux/fcntl.h>#include <linux/mc146818rtc.h>#include <linux/init.h>#include <linux/proc_fs.h>#include <linux/spinlock.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/system.h>static spinlock_t nvram_state_lock = SPIN_LOCK_UNLOCKED;static int nvram_open_cnt;	/* #times opened */static int nvram_open_mode;	/* special open modes */#define NVRAM_WRITE		1 /* opened for writing (exclusive) */#define NVRAM_EXCL		2 /* opened with O_EXCL */static int mach_check_checksum(void);static void mach_set_checksum(void);#ifdef CONFIG_PROC_FSstatic int mach_proc_infos(unsigned char *contents, char *buffer, int *len,    off_t *begin, off_t offset, int size);#endif/* * These functions are provided to be called internally or by other parts of * the kernel. It's up to the caller to ensure correct checksum before reading * or after writing (needs to be done only once). * * It is worth noting that these functions all access bytes of general * purpose memory in the NVRAM - that is to say, they all add the * NVRAM_FIRST_BYTE offset.  Pass them offsets into NVRAM as if you did not  * know about the RTC cruft. */unsigned char__nvram_read_byte(int i){	return CMOS_READ(NVRAM_FIRST_BYTE + i);}unsigned charnvram_read_byte(int i){	unsigned long flags;	unsigned char c;	spin_lock_irqsave(&rtc_lock, flags);	c = __nvram_read_byte(i);	spin_unlock_irqrestore(&rtc_lock, flags);	return c;}/* This races nicely with trying to read with checksum checking (nvram_read) */void__nvram_write_byte(unsigned char c, int i){	CMOS_WRITE(c, NVRAM_FIRST_BYTE + i);}voidnvram_write_byte(unsigned char c, int i){	unsigned long flags;	spin_lock_irqsave(&rtc_lock, flags);	__nvram_write_byte(c, i);	spin_unlock_irqrestore(&rtc_lock, flags);}int__nvram_check_checksum(void){	return mach_check_checksum();}intnvram_check_checksum(void){	unsigned long flags;	int rv;	spin_lock_irqsave(&rtc_lock, flags);	rv = __nvram_check_checksum();	spin_unlock_irqrestore(&rtc_lock, flags);	return rv;}void__nvram_set_checksum(void){	mach_set_checksum();}voidnvram_set_checksum(void){	unsigned long flags;	spin_lock_irqsave(&rtc_lock, flags);	__nvram_set_checksum();	spin_unlock_irqrestore(&rtc_lock, flags);}/* * The are the file operation function for user access to /dev/nvram */static loff_t nvram_llseek(struct file *file,loff_t offset, int origin ){	lock_kernel();	switch (origin) {	case 0:		/* nothing to do */		break;	case 1:		offset += file->f_pos;		break;	case 2:		offset += NVRAM_BYTES;		break;	}	unlock_kernel();	return (offset >= 0) ? (file->f_pos = offset) : -EINVAL;}static ssize_tnvram_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){	unsigned char contents[NVRAM_BYTES];	unsigned i = *ppos;	unsigned char *tmp;	spin_lock_irq(&rtc_lock);	if (!__nvram_check_checksum())		goto checksum_err;	for (tmp = contents; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp)		*tmp = __nvram_read_byte(i);	spin_unlock_irq(&rtc_lock);	if (copy_to_user(buf, contents, tmp - contents))		return -EFAULT;	*ppos = i;	return tmp - contents;      checksum_err:	spin_unlock_irq(&rtc_lock);	return -EIO;}static ssize_tnvram_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){	unsigned char contents[NVRAM_BYTES];	unsigned i = *ppos;	unsigned char *tmp;	int len;	len = (NVRAM_BYTES - i) < count ? (NVRAM_BYTES - i) : count;	if (copy_from_user(contents, buf, len))		return -EFAULT;	spin_lock_irq(&rtc_lock);	if (!__nvram_check_checksum())		goto checksum_err;	for (tmp = contents; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp)		__nvram_write_byte(*tmp, i);	__nvram_set_checksum();	spin_unlock_irq(&rtc_lock);	*ppos = i;	return tmp - contents;      checksum_err:	spin_unlock_irq(&rtc_lock);	return -EIO;}static intnvram_ioctl(struct inode *inode, struct file *file,    unsigned int cmd, unsigned long arg){	int i;	switch (cmd) {	case NVRAM_INIT:		/* initialize NVRAM contents and checksum */		if (!capable(CAP_SYS_ADMIN))			return -EACCES;		spin_lock_irq(&rtc_lock);		for (i = 0; i < NVRAM_BYTES; ++i)			__nvram_write_byte(0, i);		__nvram_set_checksum();		spin_unlock_irq(&rtc_lock);		return 0;	case NVRAM_SETCKS:		/* just set checksum, contents unchanged (maybe useful after 		 * checksum garbaged somehow...) */		if (!capable(CAP_SYS_ADMIN))			return -EACCES;		spin_lock_irq(&rtc_lock);		__nvram_set_checksum();		spin_unlock_irq(&rtc_lock);		return 0;	default:		return -ENOTTY;	}}static intnvram_open(struct inode *inode, struct file *file){	spin_lock(&nvram_state_lock);	if ((nvram_open_cnt && (file->f_flags & O_EXCL)) ||	    (nvram_open_mode & NVRAM_EXCL) ||	    ((file->f_mode & 2) && (nvram_open_mode & NVRAM_WRITE))) {		spin_unlock(&nvram_state_lock);		return -EBUSY;	}	if (file->f_flags & O_EXCL)		nvram_open_mode |= NVRAM_EXCL;	if (file->f_mode & 2)		nvram_open_mode |= NVRAM_WRITE;	nvram_open_cnt++;	spin_unlock(&nvram_state_lock);	return 0;}static intnvram_release(struct inode *inode, struct file *file){	spin_lock(&nvram_state_lock);	nvram_open_cnt--;	/* if only one instance is open, clear the EXCL bit */	if (nvram_open_mode & NVRAM_EXCL)		nvram_open_mode &= ~NVRAM_EXCL;	if (file->f_mode & 2)		nvram_open_mode &= ~NVRAM_WRITE;	spin_unlock(&nvram_state_lock);	return 0;}#ifndef CONFIG_PROC_FSstatic intnvram_read_proc(char *buffer, char **start, off_t offset,    int size, int *eof, void *data){	return 0;}#elsestatic intnvram_read_proc(char *buffer, char **start, off_t offset,    int size, int *eof, void *data){	unsigned char contents[NVRAM_BYTES];	int i, len = 0;	off_t begin = 0;	spin_lock_irq(&rtc_lock);	for (i = 0; i < NVRAM_BYTES; ++i)		contents[i] = __nvram_read_byte(i);	spin_unlock_irq(&rtc_lock);	*eof = mach_proc_infos(contents, buffer, &len, &begin, offset, size);	if (offset >= begin + len)		return 0;	*start = buffer + (offset - begin);	return (size < begin + len - offset) ? size : begin + len - offset;}/* This macro frees the machine specific function from bounds checking and * this like that... */#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)#endif /* CONFIG_PROC_FS */static struct file_operations nvram_fops = {	.owner		= THIS_MODULE,	.llseek		= nvram_llseek,	.read		= nvram_read,	.write		= nvram_write,	.ioctl		= nvram_ioctl,	.open		= nvram_open,	.release	= nvram_release,};static struct miscdevice nvram_dev = {	NVRAM_MINOR,	"nvram",	&nvram_fops};static int __initnvram_init(void){	int ret;	/* First test whether the driver should init at all */	if (!CHECK_DRIVER_INIT())		return -ENXIO;	ret = misc_register(&nvram_dev);

⌨️ 快捷键说明

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