umem.c
来自「linux 内核源代码」· C语言 代码 · 共 1,148 行 · 第 1/3 页
C
1,148 行
/* * mm.c - Micro Memory(tm) PCI memory board block device driver - v2.3 * * (C) 2001 San Mehat <nettwerk@valinux.com> * (C) 2001 Johannes Erdfelt <jerdfelt@valinux.com> * (C) 2001 NeilBrown <neilb@cse.unsw.edu.au> * * This driver for the Micro Memory PCI Memory Module with Battery Backup * is Copyright Micro Memory Inc 2001-2002. All rights reserved. * * This driver is released to the public under the terms of the * GNU GENERAL PUBLIC LICENSE version 2 * See the file COPYING for details. * * This driver provides a standard block device interface for Micro Memory(tm) * PCI based RAM boards. * 10/05/01: Phap Nguyen - Rebuilt the driver * 10/22/01: Phap Nguyen - v2.1 Added disk partitioning * 29oct2001:NeilBrown - Use make_request_fn instead of request_fn * - use stand disk partitioning (so fdisk works). * 08nov2001:NeilBrown - change driver name from "mm" to "umem" * - incorporate into main kernel * 08apr2002:NeilBrown - Move some of interrupt handle to tasklet * - use spin_lock_bh instead of _irq * - Never block on make_request. queue * bh's instead. * - unregister umem from devfs at mod unload * - Change version to 2.3 * 07Nov2001:Phap Nguyen - Select pci read command: 06, 12, 15 (Decimal) * 07Jan2002: P. Nguyen - Used PCI Memory Write & Invalidate for DMA * 15May2002:NeilBrown - convert to bio for 2.5 * 17May2002:NeilBrown - remove init_mem initialisation. Instead detect * - a sequence of writes that cover the card, and * - set initialised bit then. */#undef DEBUG /* #define DEBUG if you want debugging info (pr_debug) */#include <linux/fs.h>#include <linux/bio.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/mman.h>#include <linux/ioctl.h>#include <linux/module.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/timer.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/dma-mapping.h>#include <linux/fcntl.h> /* O_ACCMODE */#include <linux/hdreg.h> /* HDIO_GETGEO */#include "umem.h"#include <asm/uaccess.h>#include <asm/io.h>#define MM_MAXCARDS 4#define MM_RAHEAD 2 /* two sectors */#define MM_BLKSIZE 1024 /* 1k blocks */#define MM_HARDSECT 512 /* 512-byte hardware sectors */#define MM_SHIFT 6 /* max 64 partitions on 4 cards *//* * Version Information */#define DRIVER_NAME "umem"#define DRIVER_VERSION "v2.3"#define DRIVER_AUTHOR "San Mehat, Johannes Erdfelt, NeilBrown"#define DRIVER_DESC "Micro Memory(tm) PCI memory board block driver"static int debug;/* #define HW_TRACE(x) writeb(x,cards[0].csr_remap + MEMCTRLSTATUS_MAGIC) */#define HW_TRACE(x)#define DEBUG_LED_ON_TRANSFER 0x01#define DEBUG_BATTERY_POLLING 0x02module_param(debug, int, 0644);MODULE_PARM_DESC(debug, "Debug bitmask");static int pci_read_cmd = 0x0C; /* Read Multiple */module_param(pci_read_cmd, int, 0);MODULE_PARM_DESC(pci_read_cmd, "PCI read command");static int pci_write_cmd = 0x0F; /* Write and Invalidate */module_param(pci_write_cmd, int, 0);MODULE_PARM_DESC(pci_write_cmd, "PCI write command");static int pci_cmds;static int major_nr;#include <linux/blkdev.h>#include <linux/blkpg.h>struct cardinfo { struct pci_dev *dev; unsigned char __iomem *csr_remap; unsigned int mm_size; /* size in kbytes */ unsigned int init_size; /* initial segment, in sectors, * that we know to * have been written */ struct bio *bio, *currentbio, **biotail; int current_idx; sector_t current_sector; struct request_queue *queue; struct mm_page { dma_addr_t page_dma; struct mm_dma_desc *desc; int cnt, headcnt; struct bio *bio, **biotail; int idx; } mm_pages[2];#define DESC_PER_PAGE ((PAGE_SIZE*2)/sizeof(struct mm_dma_desc)) int Active, Ready; struct tasklet_struct tasklet; unsigned int dma_status; struct { int good; int warned; unsigned long last_change; } battery[2]; spinlock_t lock; int check_batteries; int flags;};static struct cardinfo cards[MM_MAXCARDS];static struct block_device_operations mm_fops;static struct timer_list battery_timer;static int num_cards;static struct gendisk *mm_gendisk[MM_MAXCARDS];static void check_batteries(struct cardinfo *card);static int get_userbit(struct cardinfo *card, int bit){ unsigned char led; led = readb(card->csr_remap + MEMCTRLCMD_LEDCTRL); return led & bit;}static int set_userbit(struct cardinfo *card, int bit, unsigned char state){ unsigned char led; led = readb(card->csr_remap + MEMCTRLCMD_LEDCTRL); if (state) led |= bit; else led &= ~bit; writeb(led, card->csr_remap + MEMCTRLCMD_LEDCTRL); return 0;}/* * NOTE: For the power LED, use the LED_POWER_* macros since they differ */static void set_led(struct cardinfo *card, int shift, unsigned char state){ unsigned char led; led = readb(card->csr_remap + MEMCTRLCMD_LEDCTRL); if (state == LED_FLIP) led ^= (1<<shift); else { led &= ~(0x03 << shift); led |= (state << shift); } writeb(led, card->csr_remap + MEMCTRLCMD_LEDCTRL);}#ifdef MM_DIAGstatic void dump_regs(struct cardinfo *card){ unsigned char *p; int i, i1; p = card->csr_remap; for (i = 0; i < 8; i++) { printk(KERN_DEBUG "%p ", p); for (i1 = 0; i1 < 16; i1++) printk("%02x ", *p++); printk("\n"); }}#endifstatic void dump_dmastat(struct cardinfo *card, unsigned int dmastat){ dev_printk(KERN_DEBUG, &card->dev->dev, "DMAstat - "); if (dmastat & DMASCR_ANY_ERR) printk(KERN_CONT "ANY_ERR "); if (dmastat & DMASCR_MBE_ERR) printk(KERN_CONT "MBE_ERR "); if (dmastat & DMASCR_PARITY_ERR_REP) printk(KERN_CONT "PARITY_ERR_REP "); if (dmastat & DMASCR_PARITY_ERR_DET) printk(KERN_CONT "PARITY_ERR_DET "); if (dmastat & DMASCR_SYSTEM_ERR_SIG) printk(KERN_CONT "SYSTEM_ERR_SIG "); if (dmastat & DMASCR_TARGET_ABT) printk(KERN_CONT "TARGET_ABT "); if (dmastat & DMASCR_MASTER_ABT) printk(KERN_CONT "MASTER_ABT "); if (dmastat & DMASCR_CHAIN_COMPLETE) printk(KERN_CONT "CHAIN_COMPLETE "); if (dmastat & DMASCR_DMA_COMPLETE) printk(KERN_CONT "DMA_COMPLETE "); printk("\n");}/* * Theory of request handling * * Each bio is assigned to one mm_dma_desc - which may not be enough FIXME * We have two pages of mm_dma_desc, holding about 64 descriptors * each. These are allocated at init time. * One page is "Ready" and is either full, or can have request added. * The other page might be "Active", which DMA is happening on it. * * Whenever IO on the active page completes, the Ready page is activated * and the ex-Active page is clean out and made Ready. * Otherwise the Ready page is only activated when it becomes full, or * when mm_unplug_device is called via the unplug_io_fn. * * If a request arrives while both pages a full, it is queued, and b_rdev is * overloaded to record whether it was a read or a write. * * The interrupt handler only polls the device to clear the interrupt. * The processing of the result is done in a tasklet. */static void mm_start_io(struct cardinfo *card){ /* we have the lock, we know there is * no IO active, and we know that card->Active * is set */ struct mm_dma_desc *desc; struct mm_page *page; int offset; /* make the last descriptor end the chain */ page = &card->mm_pages[card->Active]; pr_debug("start_io: %d %d->%d\n", card->Active, page->headcnt, page->cnt - 1); desc = &page->desc[page->cnt-1]; desc->control_bits |= cpu_to_le32(DMASCR_CHAIN_COMP_EN); desc->control_bits &= ~cpu_to_le32(DMASCR_CHAIN_EN); desc->sem_control_bits = desc->control_bits; if (debug & DEBUG_LED_ON_TRANSFER) set_led(card, LED_REMOVE, LED_ON); desc = &page->desc[page->headcnt]; writel(0, card->csr_remap + DMA_PCI_ADDR); writel(0, card->csr_remap + DMA_PCI_ADDR + 4); writel(0, card->csr_remap + DMA_LOCAL_ADDR); writel(0, card->csr_remap + DMA_LOCAL_ADDR + 4); writel(0, card->csr_remap + DMA_TRANSFER_SIZE); writel(0, card->csr_remap + DMA_TRANSFER_SIZE + 4); writel(0, card->csr_remap + DMA_SEMAPHORE_ADDR); writel(0, card->csr_remap + DMA_SEMAPHORE_ADDR + 4); offset = ((char *)desc) - ((char *)page->desc); writel(cpu_to_le32((page->page_dma+offset) & 0xffffffff), card->csr_remap + DMA_DESCRIPTOR_ADDR); /* Force the value to u64 before shifting otherwise >> 32 is undefined C * and on some ports will do nothing ! */ writel(cpu_to_le32(((u64)page->page_dma)>>32), card->csr_remap + DMA_DESCRIPTOR_ADDR + 4); /* Go, go, go */ writel(cpu_to_le32(DMASCR_GO | DMASCR_CHAIN_EN | pci_cmds), card->csr_remap + DMA_STATUS_CTRL);}static int add_bio(struct cardinfo *card);static void activate(struct cardinfo *card){ /* if No page is Active, and Ready is * not empty, then switch Ready page * to active and start IO. * Then add any bh's that are available to Ready */ do { while (add_bio(card)) ; if (card->Active == -1 && card->mm_pages[card->Ready].cnt > 0) { card->Active = card->Ready; card->Ready = 1-card->Ready; mm_start_io(card); } } while (card->Active == -1 && add_bio(card));}static inline void reset_page(struct mm_page *page){ page->cnt = 0; page->headcnt = 0; page->bio = NULL; page->biotail = &page->bio;}static void mm_unplug_device(struct request_queue *q){ struct cardinfo *card = q->queuedata; unsigned long flags; spin_lock_irqsave(&card->lock, flags); if (blk_remove_plug(q)) activate(card); spin_unlock_irqrestore(&card->lock, flags);}/* * If there is room on Ready page, take * one bh off list and add it. * return 1 if there was room, else 0. */static int add_bio(struct cardinfo *card){ struct mm_page *p; struct mm_dma_desc *desc; dma_addr_t dma_handle; int offset; struct bio *bio; struct bio_vec *vec; int idx; int rw; int len; bio = card->currentbio; if (!bio && card->bio) { card->currentbio = card->bio; card->current_idx = card->bio->bi_idx; card->current_sector = card->bio->bi_sector; card->bio = card->bio->bi_next; if (card->bio == NULL) card->biotail = &card->bio; card->currentbio->bi_next = NULL; return 1; } if (!bio) return 0; idx = card->current_idx; rw = bio_rw(bio); if (card->mm_pages[card->Ready].cnt >= DESC_PER_PAGE) return 0;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?