nvram.c
来自「linux 内核源代码」· C语言 代码 · 共 733 行 · 第 1/2 页
C
733 行
/* * 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/smp_lock.h>#include <linux/nvram.h>#define PC 1#define ATARI 2/* select machine configuration */#if defined(CONFIG_ATARI)# define MACH ATARI#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) /* and others?? */# define MACH PC#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 == 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 DEFINE_SPINLOCK(nvram_state_lock);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;}static void__nvram_set_checksum(void){ mach_set_checksum();}#if 0voidnvram_set_checksum(void){ unsigned long flags; spin_lock_irqsave(&rtc_lock, flags); __nvram_set_checksum(); spin_unlock_irqrestore(&rtc_lock, flags);}#endif /* 0 *//* * 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;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?