Update of /cvsroot/gc-linux/linux/drivers/block/gcn-dvd
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv15523/drivers/block/gcn-dvd
Added Files:
Makefile main.c request.c request.h
Log Message:
Addition of DVD driver since some mods replace the DVD interface with something
more interesting (IDE,SATA, etc)
--- NEW FILE: Makefile ---
obj-$(CONFIG_GAMECUBE_DVD) := gcn-dvd.o
gcn-dvd-objs := main.o request.o
--- NEW FILE: main.c ---
/*
* drivers/block/gcn-dvd/main.c
*
* Nintendo GameCube DVD driver
* Copyright (C) 2005 The GameCube Linux Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
*/
#include <linux/major.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/cdrom.h>
#include <linux/wait.h>
#include <asm/setup.h>
#include <asm/bitops.h>
#include <asm/pgtable.h>
#include <asm/cacheflush.h>
#include <asm/cache.h>
#include <linux/interrupt.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/hdreg.h> /* HDIO_GETGEO */
#include <asm/io.h>
#include "request.h"
#define DEVICE_NAME "DVD"
#define DVD_MAJOR 60
#define LINUX_SECTOR_SIZE 512
#define LINUX_SECTOR_SHIFT 9
#define DVD_SECTOR_SIZE 2048
#define DVD_SECTOR_SHIFT 11
#define DVD_MAX_SECTORS 712880
#define DMA_ALIGNMENT_MASK 0x1F
#define DVD_REGISTER_INIT ((void* __iomem)0xCC003024)
#define DVD_REGISTER_BLOCK_BASE 0xCC006000
#define DVD_REGISTER_BLOCK_LENGTH 0x40
#define DVD_GAMECODE_U32 0x80000000 /* Gamecode */
#define DVD_COMPANY_U16 0x80000004 /* Game company id */
#define DVD_DISK_ID_U8 0x80000006 /* Disk id */
#define DVD_DISK_VERSION_U8 0x80000007 /* Disk version */
#define DVD_IRQ 2
/* DI Status Register */
#define DI_DISR ((void * __iomem)0xCC006000)
#define DI_DISR_BRKINT (1<<6)
#define DI_DISR_BRKINTMASK (1<<5)
#define DI_DISR_TCINT (1<<4)
#define DI_DISR_TCINTMASK (1<<3)
#define DI_DISR_DEINT (1<<2)
#define DI_DISR_DEINTMASK (1<<1)
#define DI_DISR_BRK (1<<0)
/* DI Cover Register */
#define DI_DICVR ((void * __iomem)0xCC006004)
#define DI_DICVR_CVRINT (1<<2)
#define DI_DICVR_CVRINTMASK (1<<1)
#define DI_DICVR_CVR (1<<0)
/* DI Command Buffer 0 */
#define DI_DICMDBUF0 ((void * __iomem)0xCC006008)
#define DI_DICMDBUF0_CMD 24
#define DI_DICMDBUF0_SUBCMD1 16
#define DI_DICMDBUF0_SUBCMD2 0
/* DI Command Buffer 1 */
#define DI_DICMDBUF1 ((void * __iomem)0xCC00600C)
/* DI Command Buffer 2 */
#define DI_DICMDBUF2 ((void * __iomem)0xCC006010)
/* DMA Memory Address Register */
#define DI_DIMAR ((void * __iomem)0xCC006014)
/* DI DMA Transfer Length Register */
#define DI_DILENGTH ((void * __iomem)0xCC006018)
/* DI Control Register */
#define DI_DICR ((void * __iomem)0xCC00601C)
#define DI_DICR_RW (1<<2)
#define DI_DICR_DMA (1<<1)
#define DI_DICR_TSTART (1<<0)
/* DI Immediate Data Buffer */
#define DI_DIIMMBUF ((void * __iomem)0xCC006020)
#define DI_CMD_REZERO 0x01
#define DI_CMD_INQUIRY 0x12
#define DI_CMD_READ 0xA8
#define DI_CMD_SEEK 0xAB
#define DI_CMD_READTOC 0x43
#define DI_CMD_STOP 0xE3
/* This is a fake command, don't send to the hardware */
#define DI_CMD_INITIALIZE 0xFF
#define IS_CMD_TYPE(cmd,type) (((cmd) >> DI_DICMDBUF0_CMD) == (type))
static struct gendisk *dvd_gendisk;
static struct request_queue *dvd_queue;
static spinlock_t dvd_queue_lock = SPIN_LOCK_UNLOCKED;
static struct _taginterrupt_queue
{
spinlock_t lock;
int drive_initialized;
struct list_head queue;
} interrupt_queue;
static struct _tagdvdinfo {
spinlock_t lock;
unsigned long refCount;
unsigned int media_changed;
unsigned long numDVDSectors;
unsigned long numLinuxSectors;
/* disc info */
struct _tagdvdid
{
u32 gamecode;
u16 company;
u8 id;
u8 version;
} disc;
} dvd_info;
#define LOCK(flags) spin_lock_irqsave(&dvd_info.lock,flags)
#define UNLOCK(flags) spin_unlock_irqrestore(&dvd_info.lock,flags)
/* Must be 0x20 in size = 32 bytes */
#pragma pack(1)
struct gc_dvd_drive_info
{
u32 head;
u32 middle;
u32 last;
u8 padding[20];
};
struct gc_dvd_disc_info
{
u16 revision;
u16 device_code;
u32 release_date;
u8 padding[24];
};
#pragma pack()
struct dma_buffer
{
size_t size;
void *ptr;
void *alignedPtr;
dma_addr_t handle;
};
#ifdef DEBUG
#define DPRINTK(fmt,args...) printk(KERN_INFO "%s (%u): " fmt,__FUNCTION__,__LINE__,## args)
#else
#define DPRINTK(fmt,args...)
#endif
static int alloc_dma_buffer(size_t size,struct dma_buffer *pRet)
{
/* allocate space for the aligned memory */
pRet->size = DMA_ALIGNMENT_MASK + size;
if (!(pRet->ptr = kmalloc(pRet->size,GFP_KERNEL | GFP_DMA))) {
DPRINTK("Cannot allocate DMA memory of size %i\n",pRet->size);
return -ENOMEM;
}
/* align the pointer */
if ((unsigned long)pRet->ptr & DMA_ALIGNMENT_MASK) {
pRet->alignedPtr = (void*)(((unsigned long)pRet->ptr + DMA_ALIGNMENT_MASK) & ~DMA_ALIGNMENT_MASK);
/* adjust the size if not aligned */
pRet->size -= (pRet->alignedPtr - pRet->ptr);
}
else {
pRet->alignedPtr = pRet->ptr;
}
/* get the dma mapping */
pRet->handle = virt_to_phys(pRet->alignedPtr);
return 0;
}
inline static void sync_dma_buffer(struct dma_buffer *pRet)
{
invalidate_dcache_range((unsigned long)pRet->alignedPtr,
(unsigned long)pRet->alignedPtr + pRet->size);
}
inline static void free_dma_buffer(struct dma_buffer *pRet)
{
kfree(pRet->ptr);
}
/* hardware talk */
static void gc_dvd_enable_interrupts(int enable)
{
unsigned long outval;
/* enable main interrupts */
if (enable) {
outval = DI_DISR_BRKINT | DI_DISR_TCINT | DI_DISR_DEINT |
DI_DISR_BRKINTMASK | DI_DISR_TCINTMASK | DI_DISR_DEINTMASK;
}
else {
outval = DI_DISR_BRKINT | DI_DISR_TCINT | DI_DISR_DEINT;
}
writel(outval,DI_DISR);
/* This enables cover interrupts */
if (enable) {
outval = DI_DICVR_CVRINT | DI_DICVR_CVRINTMASK;
}
else {
outval = DI_DICVR_CVRINT;
}
writel(outval,DI_DICVR);
}
static void gc_dvd_execute_queue_command(struct gc_dvd_command *cmd)
{
u32 val;
/* if they're doing a read and the drive is not initialized */
if (!interrupt_queue.drive_initialized &&
IS_CMD_TYPE(cmd->r_DI_DICMDBUF0,DI_CMD_READ)) {
/* insert an initialize queue item BEFORE this one */
static struct gc_dvd_command init_cmd = {
.r_DI_DICMDBUF0 = DI_CMD_INITIALIZE << DI_DICMDBUF0_CMD,
.completion_routine = NULL,
.param = NULL,
};
/* insert before this item */
list_add_tail(&init_cmd.list,&cmd->list);
/* now execute the initialize routine */
val = (readl(DVD_REGISTER_INIT) & ~4) | 1;
writel(val,DVD_REGISTER_INIT);
udelay(100);
val |= (4 | 1);
writel(val,DVD_REGISTER_INIT);
udelay(100);
/* it will return an interrupt when we're done, our
queue item will pick it up */
}
else if (cmd) {
writel(cmd->r_DI_DICMDBUF0,DI_DICMDBUF0);
writel(cmd->r_DI_DICMDBUF1,DI_DICMDBUF1);
writel(cmd->r_DI_DICMDBUF2,DI_DICMDBUF2);
writel((unsigned long)cmd->r_DI_DIMAR,DI_DIMAR);
writel(cmd->r_DI_DILENGTH,DI_DILENGTH);
writel(cmd->r_DI_DICR,DI_DICR);
}
}
static int gc_dvd_queue_command(struct gc_dvd_command *cmd)
{
unsigned long flags;
int execute_immediately;
spin_lock_irqsave(&interrupt_queue.lock,flags);
cmd->int_status = is_still_running;
/* add to the tail of the list */
execute_immediately = list_empty(&interrupt_queue.queue);
list_add_tail(&cmd->list,&interrupt_queue.queue);
if (execute_immediately) {
gc_dvd_execute_queue_command(cmd);
}
/* release lock so interrupt handler can get it */
spin_unlock_irqrestore(&interrupt_queue.lock,flags);
return 0;
}
/* This function is called from an IRQ context, we just wake up the queue */
static void gc_dvd_queue_completion_wake_up(struct gc_dvd_command *cmd)
{
wake_up_interruptible(((wait_queue_head_t*)cmd->param));
}
static int gc_dvd_execute_blocking_command(unsigned int di_cmd,unsigned int sz,struct dma_buffer *buf)
{
struct gc_dvd_command cmd;
wait_queue_head_t wait_queue;
if ((sz > 0) && alloc_dma_buffer(sz,buf)) {
return -ENOMEM;
}
init_waitqueue_head(&wait_queue);
cmd.flags = 0;
cmd.r_DI_DICMDBUF0 = di_cmd;
cmd.r_DI_DICMDBUF1 = 0;
cmd.r_DI_DICMDBUF2 = sz;
cmd.r_DI_DIMAR = (sz > 0) ? (void*)buf->handle : NULL;
cmd.r_DI_DILENGTH = sz;
cmd.r_DI_DICR = DI_DICR_TSTART | ((sz > 0) ? DI_DICR_DMA : 0);
cmd.completion_routine = gc_dvd_queue_completion_wake_up;
cmd.param = &wait_queue;
gc_dvd_queue_command(&cmd);
/* wait for it to finish */
while (wait_event_interruptible(wait_queue,cmd.int_status !=
is_still_running)) ;
return cmd.int_status;
}
static inline void gc_dvd_stop_motor(void)
{
gc_dvd_execute_blocking_command(DI_CMD_STOP << DI_DICMDBUF0_CMD,0,NULL);
}
static int gc_dvd_inquiry(void)
{
/* get status on disk */
struct dma_buffer buf;
struct gc_dvd_drive_info *pdi;
int is;
is = gc_dvd_execute_blocking_command(DI_CMD_INQUIRY << DI_DICMDBUF0_CMD,
sizeof(struct gc_dvd_drive_info),
&buf);
if (is == -ENOMEM) {
return is;
}
else if (is != is_transfer_complete) {
printk(KERN_ERR "Gamecube DVD: error in inquiry cmd\n");
is = -ENODEV;
}
else {
sync_dma_buffer(&buf);
pdi = (struct gc_dvd_drive_info*)buf.alignedPtr;
printk(KERN_INFO "Gamecube DVD: 0x%x, 0x%x,0x%x\n",pdi->head,pdi->middle,pdi->last);
is = 0;
}
free_dma_buffer(&buf);
return is;
}
static int gc_dvd_read_toc(void)
{
struct dma_buffer buf;
struct gc_dvd_disc_info *pdi;
int i;
i = gc_dvd_execute_blocking_command(DI_CMD_READ << DI_DICMDBUF0_CMD | 0x40,
sizeof(struct gc_dvd_disc_info),
&buf);
if (i != is_transfer_complete) {
dvd_info.numDVDSectors = 0;
if (i != -ENOMEM) {
free_dma_buffer(&buf);
}
else {
i = -ENOMEDIUM;
}
printk(KERN_ERR "Gamecube DVD: error reading TOC - missing medium?\n");
}
else {
sync_dma_buffer(&buf);
pdi = (struct gc_dvd_disc_info*)buf.alignedPtr;
printk(KERN_INFO "Gamecube DVD: revision: %u, device_code %u, release_date: %u\n",pdi->revision,pdi->device_code,pdi->release_date);
dvd_info.numDVDSectors = DVD_MAX_SECTORS;
/* reset media_changed flag */
dvd_info.media_changed = 0;
free_dma_buffer(&buf);
i = 0;
}
/* inform the kernel of the size */
dvd_info.numLinuxSectors = dvd_info.numDVDSectors << (DVD_SECTOR_SHIFT - LINUX_SECTOR_SHIFT);
set_capacity(dvd_gendisk,dvd_info.numLinuxSectors);
return i;
}
/* Handlers */
static int gc_dvd_revalidate(struct gendisk *disk)
{
gc_dvd_read_toc();
return 0;
}
static int gc_dvd_open(struct inode *inode,struct file *filp)
{
unsigned long flags;
/* we are read only */
if (filp->f_mode & FMODE_WRITE) {
return -EROFS;
}
/* check the disc, only allow minor of 0 to be opened */
if (iminor(inode)) {
return -ENODEV;
}
/* update information about the disc */
LOCK(flags);
if (dvd_info.refCount > 0) {
/* we only let one at a time */
UNLOCK(flags);
return -EBUSY;
}
else {
check_disk_change(inode->i_bdev);
/* revalidate should be called if necessary, check results here */
if (dvd_info.numDVDSectors == 0) {
UNLOCK(flags);
return -ENOMEDIUM;
}
}
dvd_info.refCount++;
UNLOCK(flags);
return 0;
}
static int gc_dvd_release(struct inode *inode,struct file *filp)
{
unsigned long flags;
gc_dvd_stop_motor();
LOCK(flags);
dvd_info.refCount--;
/* force a media change so we re-read the toc and initialize the disc */
dvd_info.media_changed = 1;
UNLOCK(flags);
return 0;
}
static int gc_dvd_ioctl(struct inode *inode,struct file *filp,
unsigned int cmd,unsigned long arg)
{
switch (cmd)
{
case CDROMMULTISESSION:
/* struct cdrom_multisession */
break;
case CDROMSTART:
break;
case CDROMSTOP:
break;
case CDROMREADTOCHDR:
/* struct cdrom_tochdr */
break;
case CDROMREADTOCENTRY:
/* struct cdrom_tocentry */
break;
case CDROMREADMODE2:
case CDROMREADMODE1:
case CDROMREADRAW:
/* struct cdrom_read (1-2048, 2-2336,RAW-2352) */
break;
case CDROM_GET_MCN:
/* retrieve the universal product code */
/* struct cdrom_mcn */
break;
case CDROMRESET:
/* reset the drive */
break;
case BLKRAGET:
case BLKFRAGET:
case BLKROGET:
case BLKBSZGET:
case BLKSSZGET:
case BLKSECTGET:
case BLKGETSIZE:
case BLKGETSIZE64:
case BLKFLSBUF:
return ioctl_by_bdev(inode->i_bdev,cmd,arg);
default:
return -ENOTTY;
}
return -ENOTTY;
}
static int gc_dvd_media_changed(struct gendisk *disk)
{
/* return 1 if the disc has changed */
return (dvd_info.media_changed ? 1 : 0);
}
static void gc_dvd_read_request_callback(struct gc_dvd_command *cmd)
{
unsigned long flags;
struct request *req = (struct request*)cmd->param;
struct request_queue *rqueue = req->q;
enum gc_dvd_interrupt_status int_status = cmd->int_status;
/* since this was performed via DMA, invalidate the cache */
if (cmd->int_status == is_transfer_complete) {
invalidate_dcache_range((unsigned int)req->buffer,(unsigned int)req->buffer + cmd->r_DI_DILENGTH);
}
/* free this item so another request can get it */
gc_dvd_request_release_data(cmd);
/* now end the request and send back to block layer */
spin_lock_irqsave(rqueue->queue_lock,flags);
if (!end_that_request_first(req,
(int_status == is_transfer_complete),
req->current_nr_sectors)) {
add_disk_randomness(req->rq_disk);
end_that_request_last(req);
}
/* start queue back up */
blk_start_queue(rqueue);
spin_unlock_irqrestore(rqueue->queue_lock,flags);
}
static void gc_dvd_do_request(request_queue_t *q)
{
struct request *req;
unsigned long start;
unsigned long len;
struct gc_dvd_command *cmd;
while ((req = elv_next_request(q))) {
/* check if they are reading beyond the limits */
if ((req->sector + req->current_nr_sectors) > dvd_info.numLinuxSectors) {
printk(KERN_ERR "Gamecube DVD: reading past end\n");
end_request(req,0);
}
else if (rq_data_dir(req) == WRITE) {
printk(KERN_ERR "Gamecube DVD: write attempted\n");
end_request(req,0);
}
else if (dvd_info.media_changed) {
DPRINTK("media changed in read routine, aborting\n");
end_request(req,0);
}
else if (req->current_nr_sectors >= (1 << (DVD_SECTOR_SHIFT - LINUX_SECTOR_SHIFT))) {
/* now schedule the read */
if (gc_dvd_request_get_data(&cmd) || !cmd) {
/* we're full, stop the queue */
blk_stop_queue(q);
return;
}
else {
/* remove item from the queue */
blkdev_dequeue_request(req);
/* setup my structure */
start = req->sector << LINUX_SECTOR_SHIFT;
len = req->current_nr_sectors << LINUX_SECTOR_SHIFT;
cmd->flags = 0;
cmd->r_DI_DICMDBUF0 = DI_CMD_READ << DI_DICMDBUF0_CMD;
cmd->r_DI_DICMDBUF1 = start >> (DVD_SECTOR_SHIFT - LINUX_SECTOR_SHIFT);
cmd->r_DI_DICMDBUF2 = len;
cmd->r_DI_DIMAR = (void*)virt_to_phys(req->buffer);
cmd->r_DI_DILENGTH = len;
cmd->r_DI_DICR = DI_DICR_TSTART | DI_DICR_DMA;
cmd->completion_routine = gc_dvd_read_request_callback;
cmd->param = req;
gc_dvd_queue_command(cmd);
}
}
}
}
static struct block_device_operations dvd_fops =
{
.owner = THIS_MODULE,
.open = gc_dvd_open,
.release = gc_dvd_release,
.revalidate_disk = gc_dvd_revalidate,
.media_changed = gc_dvd_media_changed,
.ioctl = gc_dvd_ioctl,
};
static irqreturn_t gc_dvd_irq_handler(int irq,void *dev_id,struct pt_regs *regs)
{
unsigned int reason;
unsigned long flags;
struct gc_dvd_command *cur_item;
#define REASON_FLAG_COVER 0x80000000
/* try the main status */
if ((reason=readl(DI_DISR)) & (DI_DISR_BRKINT | DI_DISR_TCINT | DI_DISR_DEINT)) {
/* acknowledge the interrupt */
writel(reason | DI_DISR_BRKINT | DI_DISR_TCINT | DI_DISR_DEINT,DI_DISR);
}
else if ((reason=readl(DI_DICVR)) & (DI_DICVR_CVRINT)) {
/* acknowlegde the interrupt */
writel(reason | DI_DICVR_CVRINT,DI_DICVR);
/* set media changed flag */
if (!(reason & DI_DICVR_CVR)) {
dvd_info.media_changed = 1;
}
/* set flag to be used later on */
reason |= REASON_FLAG_COVER;
}
else {
/* not for us, get out of here */
return IRQ_NONE;
}
/* ok we have an interrupt, now process our queue */
/* lock our structure */
spin_lock_irqsave(interrupt_queue.lock,flags);
/* now look at our structure and call appropriate callback if necessary */
if (!list_empty(&interrupt_queue.queue)) {
/* first unlink the queue item */
cur_item = (struct gc_dvd_command*)interrupt_queue.queue.next;
list_del(&cur_item->list);
/* do special checks on the command type to keep track of drive state */
if (IS_CMD_TYPE(cur_item->r_DI_DICMDBUF0,DI_CMD_STOP)) {
interrupt_queue.drive_initialized = 0;
}
else if (IS_CMD_TYPE(cur_item->r_DI_DICMDBUF0,DI_CMD_INITIALIZE)) {
interrupt_queue.drive_initialized = 1;
}
/* now execute the next request if we have one */
if (!list_empty(&interrupt_queue.queue)) {
gc_dvd_execute_queue_command((struct gc_dvd_command*)
interrupt_queue.queue.next);
}
/* unlock the lock */
spin_unlock_irqrestore(interrupt_queue.lock,flags);
/* determine the correct interrupt status */
if (reason & REASON_FLAG_COVER) {
if (reason & DI_DICVR_CVR) {
cur_item->int_status = is_cover_opened;
}
else {
cur_item->int_status = is_cover_closed;
}
}
else if (reason & DI_DISR_TCINT) {
cur_item->int_status = is_transfer_complete;
}
else if (reason & DI_DISR_DEINT) {
cur_item->int_status = is_error;
}
else if (reason & DI_DISR_BRKINT) {
cur_item->int_status = is_break;
}
/* call the callback */
if (cur_item->completion_routine) {
cur_item->completion_routine(cur_item);
}
}
else {
spin_unlock_irqrestore(interrupt_queue.lock,flags);
DPRINTK("Received interrupt but nothing was waiting for it\n");
}
return IRQ_HANDLED;
}
static int __init gc_dvd_init(void)
{
int ret;
printk(KERN_INFO "Gamecube DVD driver: init\n");
/* initialize the refcount */
memset(&dvd_info,0,sizeof(dvd_info));
dvd_info.media_changed = 1;
/* initialize the interrupt_queue */
spin_lock_init(&interrupt_queue.lock);
interrupt_queue.drive_initialized = 0;
INIT_LIST_HEAD(&interrupt_queue.queue);
spin_lock_init(&dvd_info.lock);
/* initial the request queue */
gc_dvd_request_init();
/* first reserve our memory region to we can query hardware */
if (check_mem_region(DVD_REGISTER_BLOCK_BASE,DVD_REGISTER_BLOCK_LENGTH) ||
!request_mem_region(DVD_REGISTER_BLOCK_BASE,DVD_REGISTER_BLOCK_LENGTH,"Gamecube DVD")) {
printk(KERN_ERR "Couldn't reserve memory area for DVD\n");
return -ENOMEM;
}
if ((ret=request_irq(DVD_IRQ,gc_dvd_irq_handler,SA_INTERRUPT,"Gamecube DVD",0))) {
printk(KERN_ERR "Unable to reserve DVD IRQ\n");
goto delete_mem_region;
}
/* enable interrupts */
gc_dvd_enable_interrupts(1);
/* query the drive first */
if ((ret=gc_dvd_inquiry())) {
goto delete_irq;
}
/* now stop the dvd motor */
gc_dvd_stop_motor();
if ((ret=register_blkdev(DVD_MAJOR,DEVICE_NAME))) {
goto delete_irq;
}
if (!(dvd_gendisk = alloc_disk(1))) {
ret = -ENOMEM;
goto unreg_blkdev;
}
if (!(dvd_queue = blk_init_queue(gc_dvd_do_request,&dvd_queue_lock))) {
ret = -ENOMEM;
goto delete_gendisk;
}
dvd_gendisk->major = DVD_MAJOR;
dvd_gendisk->first_minor = 0;
dvd_gendisk->fops = &dvd_fops;
strcpy(dvd_gendisk->disk_name,"dvd");
strcpy(dvd_gendisk->devfs_name,dvd_gendisk->disk_name);
dvd_gendisk->queue = dvd_queue;
/* ok now setup the desired parameters, like block size, hardsect size,
max hardware sectors to read at once, read ahead, etc */
/* Hardware sector size */
blk_queue_hardsect_size(dvd_queue,DVD_SECTOR_SIZE);
/* Maximum sectors that can be read per request, hardware limit */
blk_queue_max_phys_segments(dvd_queue,1);
blk_queue_max_hw_segments(dvd_queue,1);
/* Max size of coalesced segment */
/* blk_queue_max_segment_size(dvd_queue,size); */
/* Set the dma alignment */
blk_queue_dma_alignment(dvd_queue,DMA_ALIGNMENT_MASK);
set_disk_ro(dvd_gendisk,1);
add_disk(dvd_gendisk);
return 0;
delete_gendisk:
del_gendisk(dvd_gendisk);
put_disk(dvd_gendisk);
dvd_gendisk = NULL;
unreg_blkdev:
unregister_blkdev(DVD_MAJOR,DEVICE_NAME);
delete_irq:
free_irq(DVD_IRQ,0);
delete_mem_region:
release_mem_region(DVD_REGISTER_BLOCK_BASE,DVD_REGISTER_BLOCK_LENGTH);
return ret;
}
static void __exit gc_dvd_exit(void)
{
printk(KERN_INFO "Gamecube DVD driver: exit\n");
/* TODO send a break/interrupt to the device */
gc_dvd_stop_motor();
gc_dvd_enable_interrupts(0);
free_irq(DVD_IRQ, 0);
release_mem_region(DVD_REGISTER_BLOCK_BASE,DVD_REGISTER_BLOCK_LENGTH);
blk_unregister_region(MKDEV(DVD_MAJOR,0),256);
unregister_blkdev(DVD_MAJOR,DEVICE_NAME);
if (dvd_gendisk) {
del_gendisk(dvd_gendisk);
put_disk(dvd_gendisk);
dvd_gendisk = NULL;
}
if (dvd_queue) {
blk_cleanup_queue(dvd_queue);
dvd_queue = NULL;
}
}
MODULE_AUTHOR("Scream|CT");
MODULE_DESCRIPTION("Gamecube DVD driver");
MODULE_LICENSE("GPL");
module_init(gc_dvd_init);
module_exit(gc_dvd_exit);
--- NEW FILE: request.c ---
/*
* drivers/block/gcn-dvd/request.c
*
* Nintendo GameCube DVD driver
* Copyright (C) 2005 The GameCube Linux Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include "request.h"
struct data_item
{
struct list_head list;
struct gc_dvd_command data;
};
// define array of data items
static struct data_item data[MAX_ITEMS];
static spinlock_t lock;
static struct list_head lfree;
void gc_dvd_request_init(void)
{
int i;
INIT_LIST_HEAD(&lfree);
spin_lock_init(&lock);
for (i=0;i<MAX_ITEMS;++i) {
list_add_tail(&data[i].list,&lfree);
}
}
int gc_dvd_request_get_data(struct gc_dvd_command **ppcmd)
{
unsigned long flags;
struct data_item *pdi;
// get the first object and remove from the free list
spin_lock_irqsave(&lock,flags);
if (list_empty(&lfree)) {
spin_unlock_irqrestore(&lock,flags);
return -ENOMEM;
}
pdi = (struct data_item*)lfree.next;
list_del(&pdi->list);
spin_unlock_irqrestore(&lock,flags);
// return it
*ppcmd = &pdi->data;
return 0;
}
void gc_dvd_request_release_data(struct gc_dvd_command *pcmd)
{
unsigned long flags;
struct data_item *pdi;
/* return the item */
spin_lock_irqsave(&lock,flags);
pdi = (struct data_item*)((u8*)pcmd - sizeof(struct list_head));
list_add_tail(&pdi->list,&lfree);
spin_unlock_irqrestore(&lock,flags);
}
--- NEW FILE: request.h ---
/*
* drivers/block/gcn-dvd/request.h
*
* Nintendo GameCube DVD driver
* Copyright (C) 2005 The GameCube Linux Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
*/
#ifndef __request___
#define __request___
#define MAX_ITEMS 8
enum gc_dvd_interrupt_status { is_still_running,
is_transfer_complete,
is_error,
is_break,
is_cover_closed,
is_cover_opened};
struct gc_dvd_command
{
struct list_head list;
u32 flags;
enum gc_dvd_interrupt_status int_status;
u32 r_DI_DICMDBUF0;
u32 r_DI_DICMDBUF1;
u32 r_DI_DICMDBUF2;
void *r_DI_DIMAR;
u32 r_DI_DILENGTH;
u32 r_DI_DICR;
void *param;
void (*completion_routine)(struct gc_dvd_command *cmd);
};
void gc_dvd_request_init(void);
int gc_dvd_request_get_data(struct gc_dvd_command **ppcmd);
void gc_dvd_request_release_data(struct gc_dvd_command *pcmd);
#endif
|