📄 nvram.c
字号:
/* * 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> * * 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 50, 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 */#define NVRAM_VERSION "1.1"#include <linux/module.h>#include <linux/config.h>#include <linux/sched.h>#include <linux/smp_lock.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 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))/* 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/nvram.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 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 */#define RTC_FIRST_BYTE 14 /* RTC register number of first NVRAM byte */#define NVRAM_BYTES 128-RTC_FIRST_BYTE /* number of NVRAM bytes */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 are the internal NVRAM access functions, which do NOT disable * interrupts and do not check the checksum. Both tasks are left to higher * level function, so they need to be done only once per syscall. */static __inline__ unsigned char nvram_read_int( int i ){ return( CMOS_READ( RTC_FIRST_BYTE+i ) );}static __inline__ void nvram_write_int( unsigned char c, int i ){ CMOS_WRITE( c, RTC_FIRST_BYTE+i );}static __inline__ int nvram_check_checksum_int( void ){ return( mach_check_checksum() );}static __inline__ void nvram_set_checksum_int( void ){ mach_set_checksum();}#if MACH == ATARI/* * These non-internal functions are provided to be called 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). * * They're only built if CONFIG_ATARI is defined, because Atari drivers use * them. For other configurations (PC), the rest of the kernel can't rely on * them being present (this driver may not be configured at all, or as a * module), so they access config information themselves. */unsigned char nvram_read_byte( int i ){ unsigned long flags; unsigned char c; spin_lock_irqsave (&rtc_lock, flags); c = nvram_read_int( 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 ){ unsigned long flags; spin_lock_irqsave (&rtc_lock, flags); nvram_write_int( c, i ); spin_unlock_irqrestore (&rtc_lock, flags);}int nvram_check_checksum( void ){ unsigned long flags; int rv; spin_lock_irqsave (&rtc_lock, flags); rv = nvram_check_checksum_int(); spin_unlock_irqrestore (&rtc_lock, flags); return( rv );}void nvram_set_checksum( void ){ unsigned long flags; spin_lock_irqsave (&rtc_lock, flags); nvram_set_checksum_int(); spin_unlock_irqrestore (&rtc_lock, flags);}#endif /* MACH == ATARI *//* * The are the file operation function for user access to /dev/nvram */static long long nvram_llseek(struct file *file,loff_t offset, int origin ){ switch( origin ) { case 0: /* nothing to do */ break; case 1: offset += file->f_pos; break; case 2: offset += NVRAM_BYTES; break; } return( (offset >= 0) ? (file->f_pos = offset) : -EINVAL );}static ssize_t nvram_read(struct file * file, char * buf, size_t count, loff_t *ppos ){ char contents [NVRAM_BYTES]; unsigned i = *ppos; char *tmp; spin_lock_irq (&rtc_lock); if (!nvram_check_checksum_int()) goto checksum_err; for (tmp = contents; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp) *tmp = nvram_read_int(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_t nvram_write(struct file * file, const char * buf, size_t count, loff_t *ppos ){ char contents [NVRAM_BYTES]; unsigned i = *ppos; char * tmp; if (copy_from_user (contents, buf, (NVRAM_BYTES - i) < count ? (NVRAM_BYTES - i) : count)) return -EFAULT; spin_lock_irq (&rtc_lock); if (!nvram_check_checksum_int()) goto checksum_err; for (tmp = contents; count-- > 0 && i < NVRAM_BYTES; ++i, ++tmp) nvram_write_int (*tmp, i); nvram_set_checksum_int(); spin_unlock_irq (&rtc_lock); *ppos = i; return (tmp - contents);checksum_err: spin_unlock_irq (&rtc_lock); return -EIO;}static int nvram_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_int( 0, i ); nvram_set_checksum_int(); 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_int(); spin_unlock_irq (&rtc_lock); return( 0 ); default: return( -EINVAL ); }}static int nvram_open( struct inode *inode, struct file *file ){ if ((nvram_open_cnt && (file->f_flags & O_EXCL)) || (nvram_open_mode & NVRAM_EXCL) || ((file->f_mode & 2) && (nvram_open_mode & NVRAM_WRITE))) 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++; return( 0 );}static int nvram_release( struct inode *inode, struct file *file ){ lock_kernel(); nvram_open_cnt--; if (file->f_flags & O_EXCL) nvram_open_mode &= ~NVRAM_EXCL; if (file->f_mode & 2) nvram_open_mode &= ~NVRAM_WRITE; unlock_kernel(); return( 0 );}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -