⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 swim_iop.c

📁 Linux块设备驱动源码
💻 C
字号:
/* * Driver for the SWIM (Super Woz Integrated Machine) IOP * floppy controller on the Macintosh IIfx and Quadra 900/950 * * Written by Joshua M. Thompson (funaho@jurai.org) * based on the SWIM3 driver (c) 1996 by Paul Mackerras. * * 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. * * 1999-06-12 (jmt) - Initial implementation. *//* * ------------------- * Theory of Operation * ------------------- * * Since the SWIM IOP is message-driven we implement a simple request queue * system.  One outstanding request may be queued at any given time (this is * an IOP limitation); only when that request has completed can a new request * be sent. */#include <linux/stddef.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/delay.h>#include <linux/fd.h>#include <linux/ioctl.h>#include <linux/blkdev.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/mac_iop.h>#include <asm/swim_iop.h>#define DRIVER_VERSION "Version 0.1 (1999-06-12)"#define MAX_FLOPPIES	4enum swim_state {	idle,	available,	revalidating,	transferring,	ejecting};struct floppy_state {	enum swim_state state;	int	drive_num;	/* device number */	int	secpercyl;	/* disk geometry information */	int	secpertrack;	int	total_secs;	int	write_prot;	/* 1 if write-protected, 0 if not, -1 dunno */	int	ref_count;	struct timer_list timeout;	int	ejected;	struct wait_queue *wait;	int	wanted;	int	timeout_pending;};struct swim_iop_req {	int	sent;	int	complete;	__u8	command[32];	struct floppy_state *fs;	void	(*done)(struct swim_iop_req *);};static struct swim_iop_req *current_req;static int floppy_count;static struct floppy_state floppy_states[MAX_FLOPPIES];static DEFINE_SPINLOCK(swim_iop_lock);#define CURRENT elv_next_request(swim_queue)static char *drive_names[7] = {	"not installed",	/* DRV_NONE    */	"unknown (1)",		/* DRV_UNKNOWN */	"a 400K drive",		/* DRV_400K    */	"an 800K drive"		/* DRV_800K    */	"unknown (4)",		/* ????        */	"an FDHD",		/* DRV_FDHD    */	"unknown (6)",		/* ????        */	"an Apple HD20"		/* DRV_HD20    */};int swimiop_init(void);static void swimiop_init_request(struct swim_iop_req *);static int swimiop_send_request(struct swim_iop_req *);static void swimiop_receive(struct iop_msg *, struct pt_regs *);static void swimiop_status_update(int, struct swim_drvstatus *);static int swimiop_eject(struct floppy_state *fs);static int floppy_ioctl(struct inode *inode, struct file *filp,			unsigned int cmd, unsigned long param);static int floppy_open(struct inode *inode, struct file *filp);static int floppy_release(struct inode *inode, struct file *filp);static int floppy_check_change(struct gendisk *disk);static int floppy_revalidate(struct gendisk *disk);static int grab_drive(struct floppy_state *fs, enum swim_state state,		      int interruptible);static void release_drive(struct floppy_state *fs);static void set_timeout(struct floppy_state *fs, int nticks,			void (*proc)(unsigned long));static void fd_request_timeout(unsigned long);static void do_fd_request(request_queue_t * q);static void start_request(struct floppy_state *fs);static struct block_device_operations floppy_fops = {	.open		= floppy_open,	.release	= floppy_release,	.ioctl		= floppy_ioctl,	.media_changed	= floppy_check_change,	.revalidate_disk= floppy_revalidate,};static struct request_queue *swim_queue;/* * SWIM IOP initialization */int swimiop_init(void){	volatile struct swim_iop_req req;	struct swimcmd_status *cmd = (struct swimcmd_status *) &req.command[0];	struct swim_drvstatus *ds = &cmd->status;	struct floppy_state *fs;	int i;	current_req = NULL;	floppy_count = 0;	if (!iop_ism_present)		return -ENODEV;	if (register_blkdev(FLOPPY_MAJOR, "fd"))		return -EBUSY;	swim_queue = blk_init_queue(do_fd_request, &swim_iop_lock);	if (!swim_queue) {		unregister_blkdev(FLOPPY_MAJOR, "fd");		return -ENOMEM;	}	printk("SWIM-IOP: %s by Joshua M. Thompson (funaho@jurai.org)\n",		DRIVER_VERSION);	if (iop_listen(SWIM_IOP, SWIM_CHAN, swimiop_receive, "SWIM") != 0) {		printk(KERN_ERR "SWIM-IOP: IOP channel already in use; can't initialize.\n");		unregister_blkdev(FLOPPY_MAJOR, "fd");		blk_cleanup_queue(swim_queue);		return -EBUSY;	}	printk(KERN_ERR "SWIM_IOP: probing for installed drives.\n");	for (i = 0 ; i < MAX_FLOPPIES ; i++) {		memset(&floppy_states[i], 0, sizeof(struct floppy_state));		fs = &floppy_states[floppy_count];		swimiop_init_request(&req);		cmd->code = CMD_STATUS;		cmd->drive_num = i + 1;		if (swimiop_send_request(&req) != 0) continue;		while (!req.complete);		if (cmd->error != 0) {			printk(KERN_ERR "SWIM-IOP: probe on drive %d returned error %d\n", i, (uint) cmd->error);			continue;		}		if (ds->installed != 0x01) continue;		printk("SWIM-IOP: drive %d is %s (%s, %s, %s, %s)\n", i,			drive_names[ds->info.type],			ds->info.external? "ext" : "int",			ds->info.scsi? "scsi" : "floppy",			ds->info.fixed? "fixed" : "removable",			ds->info.secondary? "secondary" : "primary");		swimiop_status_update(floppy_count, ds);		fs->state = idle;		init_timer(&fs->timeout);		floppy_count++;	}	printk("SWIM-IOP: detected %d installed drives.\n", floppy_count);	for (i = 0; i < floppy_count; i++) {		struct gendisk *disk = alloc_disk(1);		if (!disk)			continue;		disk->major = FLOPPY_MAJOR;		disk->first_minor = i;		disk->fops = &floppy_fops;		sprintf(disk->disk_name, "fd%d", i);		disk->private_data = &floppy_states[i];		disk->queue = swim_queue;		set_capacity(disk, 2880 * 2);		add_disk(disk);	}	return 0;}static void swimiop_init_request(struct swim_iop_req *req){	req->sent = 0;	req->complete = 0;	req->done = NULL;}static int swimiop_send_request(struct swim_iop_req *req){	unsigned long flags;	int err;	/* It's doubtful an interrupt routine would try to send */	/* a SWIM request, but I'd rather play it safe here.    */	local_irq_save(flags);	if (current_req != NULL) {		local_irq_restore(flags);		return -ENOMEM;	}	current_req = req;	/* Interrupts should be back on for iop_send_message() */	local_irq_restore(flags);	err = iop_send_message(SWIM_IOP, SWIM_CHAN, (void *) req,				sizeof(req->command), (__u8 *) &req->command[0],				swimiop_receive);	/* No race condition here; we own current_req at this point */	if (err) {		current_req = NULL;	} else {		req->sent = 1;	}	return err;}/* * Receive a SWIM message from the IOP. * * This will be called in two cases: * * 1. A message has been successfully sent to the IOP. * 2. An unsolicited message was received from the IOP. */void swimiop_receive(struct iop_msg *msg, struct pt_regs *regs){	struct swim_iop_req *req;	struct swimmsg_status *sm;	struct swim_drvstatus *ds;	req = current_req;	switch(msg->status) {		case IOP_MSGSTATUS_COMPLETE:			memcpy(&req->command[0], &msg->reply[0], sizeof(req->command));			req->complete = 1;			if (req->done) (*req->done)(req);			current_req = NULL;			break;		case IOP_MSGSTATUS_UNSOL:			sm = (struct swimmsg_status *) &msg->message[0];			ds = &sm->status;			swimiop_status_update(sm->drive_num, ds);			iop_complete_message(msg);			break;	}}static void swimiop_status_update(int drive_num, struct swim_drvstatus *ds){	struct floppy_state *fs = &floppy_states[drive_num];	fs->write_prot = (ds->write_prot == 0x80);	if ((ds->disk_in_drive != 0x01) && (ds->disk_in_drive != 0x02)) {		fs->ejected = 1;	} else {		fs->ejected = 0;	}	switch(ds->info.type) {		case DRV_400K:			fs->secpercyl = 10;			fs->secpertrack = 10;			fs->total_secs = 800;			break;		case DRV_800K:			fs->secpercyl = 20;			fs->secpertrack = 10;			fs->total_secs = 1600;			break;		case DRV_FDHD:			fs->secpercyl = 36;			fs->secpertrack = 18;			fs->total_secs = 2880;			break;		default:			fs->secpercyl = 0;			fs->secpertrack = 0;			fs->total_secs = 0;			break;	}}static int swimiop_eject(struct floppy_state *fs){	int err, n;	struct swim_iop_req req;	struct swimcmd_eject *cmd = (struct swimcmd_eject *) &req.command[0];	err = grab_drive(fs, ejecting, 1);	if (err) return err;	swimiop_init_request(&req);	cmd->code = CMD_EJECT;	cmd->drive_num = fs->drive_num;	err = swimiop_send_request(&req);	if (err) {		release_drive(fs);		return err;	}	for (n = 2*HZ; n > 0; --n) {		if (req.complete) break;		if (signal_pending(current)) {			err = -EINTR;			break;		}		schedule_timeout_interruptible(1);	}	release_drive(fs);	return cmd->error;}static struct floppy_struct floppy_type =	{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL };	/*  7 1.44MB 3.5"   */static int floppy_ioctl(struct inode *inode, struct file *filp,			unsigned int cmd, unsigned long param){	struct floppy_state *fs = inode->i_bdev->bd_disk->private_data;	int err;	if ((cmd & 0x80) && !capable(CAP_SYS_ADMIN))		return -EPERM;	switch (cmd) {	case FDEJECT:		if (fs->ref_count != 1)			return -EBUSY;		err = swimiop_eject(fs);		return err;	case FDGETPRM:	        if (copy_to_user((void *) param, (void *) &floppy_type,				 sizeof(struct floppy_struct)))			return -EFAULT;		return 0;	}	return -ENOTTY;}static int floppy_open(struct inode *inode, struct file *filp){	struct floppy_state *fs = inode->i_bdev->bd_disk->private_data;	if (fs->ref_count == -1 || filp->f_flags & O_EXCL)		return -EBUSY;	if ((filp->f_flags & O_NDELAY) == 0 && (filp->f_mode & 3)) {		check_disk_change(inode->i_bdev);		if (fs->ejected)			return -ENXIO;	}	if ((filp->f_mode & 2) && fs->write_prot)		return -EROFS;	if (filp->f_flags & O_EXCL)		fs->ref_count = -1;	else		++fs->ref_count;	return 0;}static int floppy_release(struct inode *inode, struct file *filp){	struct floppy_state *fs = inode->i_bdev->bd_disk->private_data;	if (fs->ref_count > 0)		fs->ref_count--;	return 0;}static int floppy_check_change(struct gendisk *disk){	struct floppy_state *fs = disk->private_data;	return fs->ejected;}static int floppy_revalidate(struct gendisk *disk){	struct floppy_state *fs = disk->private_data;	grab_drive(fs, revalidating, 0);	/* yadda, yadda */	release_drive(fs);	return 0;}static void floppy_off(unsigned int nr){}static int grab_drive(struct floppy_state *fs, enum swim_state state,		      int interruptible){	unsigned long flags;	local_irq_save(flags);	if (fs->state != idle) {		++fs->wanted;		while (fs->state != available) {			if (interruptible && signal_pending(current)) {				--fs->wanted;				local_irq_restore(flags);				return -EINTR;			}			interruptible_sleep_on(&fs->wait);		}		--fs->wanted;	}	fs->state = state;	local_irq_restore(flags);	return 0;}static void release_drive(struct floppy_state *fs){	unsigned long flags;	local_irq_save(flags);	fs->state = idle;	start_request(fs);	local_irq_restore(flags);}static void set_timeout(struct floppy_state *fs, int nticks,			void (*proc)(unsigned long)){	unsigned long flags;	local_irq_save(flags);	if (fs->timeout_pending)		del_timer(&fs->timeout);	init_timer(&fs->timeout);	fs->timeout.expires = jiffies + nticks;	fs->timeout.function = proc;	fs->timeout.data = (unsigned long) fs;	add_timer(&fs->timeout);	fs->timeout_pending = 1;	local_irq_restore(flags);}static void do_fd_request(request_queue_t * q){	int i;	for (i = 0 ; i < floppy_count ; i++) {		start_request(&floppy_states[i]);	}}static void fd_request_complete(struct swim_iop_req *req){	struct floppy_state *fs = req->fs;	struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req->command[0];	del_timer(&fs->timeout);	fs->timeout_pending = 0;	fs->state = idle;	if (cmd->error) {		printk(KERN_ERR "SWIM-IOP: error %d on read/write request.\n", cmd->error);		end_request(CURRENT, 0);	} else {		CURRENT->sector += cmd->num_blocks;		CURRENT->current_nr_sectors -= cmd->num_blocks;		if (CURRENT->current_nr_sectors <= 0) {			end_request(CURRENT, 1);			return;		}	}	start_request(fs);}static void fd_request_timeout(unsigned long data){	struct floppy_state *fs = (struct floppy_state *) data;	fs->timeout_pending = 0;	end_request(CURRENT, 0);	fs->state = idle;}static void start_request(struct floppy_state *fs){	volatile struct swim_iop_req req;	struct swimcmd_rw *cmd = (struct swimcmd_rw *) &req.command[0];	if (fs->state == idle && fs->wanted) {		fs->state = available;		wake_up(&fs->wait);		return;	}	while (CURRENT && fs->state == idle) {		if (CURRENT->bh && !buffer_locked(CURRENT->bh))			panic("floppy: block not locked");#if 0		printk("do_fd_req: dev=%s cmd=%d sec=%ld nr_sec=%ld buf=%p\n",		       CURRENT->rq_disk->disk_name, CURRENT->cmd,		       CURRENT->sector, CURRENT->nr_sectors, CURRENT->buffer);		printk("           rq_status=%d errors=%d current_nr_sectors=%ld\n",		       CURRENT->rq_status, CURRENT->errors, CURRENT->current_nr_sectors);#endif		if (CURRENT->sector < 0 || CURRENT->sector >= fs->total_secs) {			end_request(CURRENT, 0);			continue;		}		if (CURRENT->current_nr_sectors == 0) {			end_request(CURRENT, 1);			continue;		}		if (fs->ejected) {			end_request(CURRENT, 0);			continue;		}		swimiop_init_request(&req);		req.fs = fs;		req.done = fd_request_complete;		if (CURRENT->cmd == WRITE) {			if (fs->write_prot) {				end_request(CURRENT, 0);				continue;			}			cmd->code = CMD_WRITE;		} else {			cmd->code = CMD_READ;		}		cmd->drive_num = fs->drive_num;		cmd->buffer = CURRENT->buffer;		cmd->first_block = CURRENT->sector;		cmd->num_blocks = CURRENT->current_nr_sectors;		if (swimiop_send_request(&req)) {			end_request(CURRENT, 0);			continue;		}		set_timeout(fs, HZ*CURRENT->current_nr_sectors,				fd_request_timeout);		fs->state = transferring;	}}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -