一般可以通过字符设备驱动的方式访问nvram,在这里,通过块设备驱动方式去驱动nvram,以了解和熟悉块设备驱动编写。
测试系统:fedora7
nvram总可接大小:8k x 64页 x 8片 = 4 M 字节
测试硬件:intel板卡
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/ioport.h>
#include <asm/uaccess.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/vmalloc.h>
#include <linux/hdreg.h>
#include <linux/kdev_t.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#define NVRAM_NAME "board_nvram"
#define NVRAM_SIZE 0x80000
#define NVRAM_BLOCKSIZE 512
struct nvram_dev_t{
int size; /* Device size in sectors */
u8 *data;
struct request_queue *queue;
struct gendisk *disk;
};
struct nvram_dev_t nvram_dev;
static int hardsect=NVRAM_BLOCKSIZE;
static int major=224;
static int minor = 0;
static int iobaadr = 0x300; //板卡io空间基地址
static unsigned long membaadr=0xdc000; //内存空间基地址
static unsigned long mem_len=8192; //页映射长度bytes
char* preg;
void SelectPage(unsigned page)
{
outb_p(0xc0|(page & 0x3f),iobaadr+8); //页选
}
static void nvram_transfer(struct nvram_dev_t *dev, unsigned long sector, unsigned long nsect, char *buffer, int write)
{
unsigned long offset = sector*hardsect;
unsigned long nbytes = nsect*hardsect;
char *rdptr;
unsigned long index;
int time;
unsigned long r_size; /*size of one request*/
unsigned long t_size; /*size of one tansfer*/
if ((offset + nbytes) > NVRAM_SIZE) {
printk (KERN_NOTICE "invaild offset\n");
return;
}
offset=(sector * hardsect) & (mem_len-1);
index= (sector* hardsect)>>13;
rdptr = preg+ offset;
r_size=nbytes;
if(r_size>(mem_len-offset)){
time=((r_size-(mem_len-offset))>>13 )+ 2;
}else
time=1;
if (write) //write
while(time){
SelectPage(index);
if(time==1) t_size=r_size;
else t_size=mem_len-offset;
memcpy_toio(rdptr,buffer,t_size);
r_size-=t_size;
time--;
index++;
offset=0;
}
else //read
while(time){
SelectPage(index);
if(time==1) t_size=r_size;
else t_size=mem_len-offset;
memcpy_fromio(buffer,rdptr,t_size);
r_size-=t_size;
time--;
index++;
offset=0;
}
}
static int nvram_xfer_bio(struct nvram_dev_t *dev, struct bio *bio)
{
int i;
struct bio_vec *bvec;
sector_t sector = bio->bi_sector;
/* Do each segment independently. */
bio_for_each_segment(bvec, bio, i) {
char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
nvram_transfer(dev, sector, bio_cur_sectors(bio),
buffer, bio_data_dir(bio) == WRITE);
sector += bio_cur_sectors(bio);
__bio_kunmap_atomic(bio, KM_USER0);
}
return 0; /* Always "succeed" */
}
static int nvram_make_request(request_queue_t *queue,struct bio *bio)
{
struct nvram_dev_t*dev = &nvram_dev;
int status;
printk("nvram_make_request\n");
status = nvram_xfer_bio(dev, bio);
bio_endio(bio, bio->bi_size, status);
return 0;
}
int nvram_open(struct inode *inode, struct file *filp)
{
try_module_get(THIS_MODULE);
return 0;
}
int nvram_release(struct inode *inode, struct file *filp)
{
module_put(THIS_MODULE);
return 0;
}
int nvram_ioctl (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int err;
long size;
struct hd_geometry geo;
switch(cmd) {
case BLKGETSIZE:
if (!arg) return -EINVAL;
err = ! access_ok (VERIFY_WRITE, arg, sizeof(long));
if (err) return -EFAULT;
size = NVRAM_SIZE/512;
if (copy_to_user((long *) arg, &size, sizeof (long)))
return -EFAULT;
return 0;
case BLKRRPART:
return -ENOTTY;
case HDIO_GETGEO: // 获取hd_geometry结构,物理信息
err = ! access_ok(VERIFY_WRITE, arg, sizeof(geo));
if (err) return -EFAULT;
size = NVRAM_SIZE/512;
geo.cylinders = (size & ~0x3f) >> 6;
geo.heads = 4;
geo.sectors = 16;
geo.start = 4;
if (copy_to_user((void *) arg, &geo, sizeof(geo)))
return -EFAULT;
return 0;
}
return -ENOTTY; /* unknown command */
}
struct block_device_operations nvram_bdops ={
.owner = THIS_MODULE,
.open = nvram_open,
.release = nvram_release,
.ioctl = nvram_ioctl,
};
static void __exit nvram_exit(void)
{
if (nvram_dev.disk) {
del_gendisk(nvram_dev.disk);
put_disk(nvram_dev.disk);
}
if (nvram_dev.queue) {
blk_put_queue(nvram_dev.queue);
}
unregister_blkdev(major, "nvram");
unregister_chrdev_region(MKDEV(major,minor), 1);
release_region(iobaadr, 13);
release_mem_region(membaadr, mem_len);
iounmap(preg);
printk("Board Nvram Driver unloaded\n");
}
static int nvram_init(void){
int ret;
int err = -ENOMEM;
struct gendisk *disk;
dev_t devno=MKDEV(major, minor);
ret = register_chrdev_region(devno,1,NVRAM_NAME);
if(ret<0){
printk("Unable to register character device!\n");
return ret;
}
nvram_dev.disk= alloc_disk(1);
if (!nvram_dev.disk){
unregister_chrdev_region(devno, 1);
return err;
}
nvram_dev.queue= blk_alloc_queue(GFP_KERNEL);
if (!nvram_dev.queue) {
unregister_chrdev_region(devno, 1);
put_disk(nvram_dev.disk);
return err;
}
if (register_blkdev(major, "nvram")) {
err = -EIO;
goto out;
}
if (NULL == request_region(iobaadr, 13, NVRAM_NAME)){
printk("io error\n");
nvram_exit();
return -EINVAL;
}
if (NULL == request_mem_region(membaadr, mem_len, NVRAM_NAME)){
printk("mem error\n");
nvram_exit();
return -EINVAL;
}
preg= ioremap(membaadr,mem_len);
disk= nvram_dev.disk;
blk_queue_make_request(nvram_dev.queue,&nvram_make_request);
blk_queue_hardsect_size(nvram_dev.queue, hardsect);
/* rd_size is given in kB */
disk->major = major;
disk->first_minor = 0;
disk->fops = &nvram_bdops;
disk->queue = nvram_dev.queue;
sprintf(disk->disk_name, "nvram%d", 0);
set_capacity(disk, 1024);
add_disk(disk);
outb_p(0x55,iobaadr+9); //nvram 片选使能
printk("Nvram is Initialized\n");
return 0;
out:
unregister_chrdev_region(devno, 1);
put_disk(nvram_dev.disk);
blk_cleanup_queue(nvram_dev.queue);
return err;
}
module_init(nvram_init);
module_exit(nvram_exit);
MODULE_LICENSE("Dual BSD/GPL");
测试:
insmod board_nvram.ko
mkfs.ext2 /dev/nvram0
mount /dev/nvram0 /mnt
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。