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

📄 tape_core.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  drivers/s390/char/tape_core.c *    basic function of the tape device driver * *  S390 and zSeries version *    Copyright (C) 2001,2005 IBM Deutschland Entwicklung GmbH, IBM Corporation *    Author(s): Carsten Otte <cotte@de.ibm.com> *		 Michael Holzheu <holzheu@de.ibm.com> *		 Tuan Ngo-Anh <ngoanh@de.ibm.com> *		 Martin Schwidefsky <schwidefsky@de.ibm.com> *		 Stefan Bader <shbader@de.ibm.com> */#include <linux/config.h>#include <linux/module.h>#include <linux/init.h>	     // for kernel parameters#include <linux/kmod.h>	     // for requesting modules#include <linux/spinlock.h>  // for locks#include <linux/vmalloc.h>#include <linux/list.h>#include <asm/types.h>	     // for variable types#define TAPE_DBF_AREA	tape_core_dbf#include "tape.h"#include "tape_std.h"#define PRINTK_HEADER "TAPE_CORE: "static void __tape_do_irq (struct ccw_device *, unsigned long, struct irb *);static void tape_delayed_next_request(void * data);/* * One list to contain all tape devices of all disciplines, so * we can assign the devices to minor numbers of the same major * The list is protected by the rwlock */static struct list_head tape_device_list = LIST_HEAD_INIT(tape_device_list);static DEFINE_RWLOCK(tape_device_lock);/* * Pointer to debug area. */debug_info_t *TAPE_DBF_AREA = NULL;EXPORT_SYMBOL(TAPE_DBF_AREA);/* * Printable strings for tape enumerations. */const char *tape_state_verbose[TS_SIZE] ={	[TS_UNUSED]   = "UNUSED",	[TS_IN_USE]   = "IN_USE",	[TS_BLKUSE]   = "BLKUSE",	[TS_INIT]     = "INIT  ",	[TS_NOT_OPER] = "NOT_OP"};const char *tape_op_verbose[TO_SIZE] ={	[TO_BLOCK] = "BLK",	[TO_BSB] = "BSB",	[TO_BSF] = "BSF",	[TO_DSE] = "DSE",	[TO_FSB] = "FSB",	[TO_FSF] = "FSF",	[TO_LBL] = "LBL",	[TO_NOP] = "NOP",	[TO_RBA] = "RBA",	[TO_RBI] = "RBI",	[TO_RFO] = "RFO",	[TO_REW] = "REW",	[TO_RUN] = "RUN",	[TO_WRI] = "WRI",	[TO_WTM] = "WTM",	[TO_MSEN] = "MSN",	[TO_LOAD] = "LOA",	[TO_READ_CONFIG] = "RCF",	[TO_READ_ATTMSG] = "RAT",	[TO_DIS] = "DIS",	[TO_ASSIGN] = "ASS",	[TO_UNASSIGN] = "UAS"};static inline intbusid_to_int(char *bus_id){	int	dec;	int	d;	char *	s;	for(s = bus_id, d = 0; *s != '\0' && *s != '.'; s++)		d = (d * 10) + (*s - '0');	dec = d;	for(s++, d = 0; *s != '\0' && *s != '.'; s++)		d = (d * 10) + (*s - '0');	dec = (dec << 8) + d;	for(s++; *s != '\0'; s++) {		if (*s >= '0' && *s <= '9') {			d = *s - '0';		} else if (*s >= 'a' && *s <= 'f') {			d = *s - 'a' + 10;		} else {			d = *s - 'A' + 10;		}		dec = (dec << 4) + d;	}	return dec;}/* * Some channel attached tape specific attributes. * * FIXME: In the future the first_minor and blocksize attribute should be *        replaced by a link to the cdev tree. */static ssize_ttape_medium_state_show(struct device *dev, struct device_attribute *attr, char *buf){	struct tape_device *tdev;	tdev = (struct tape_device *) dev->driver_data;	return scnprintf(buf, PAGE_SIZE, "%i\n", tdev->medium_state);}staticDEVICE_ATTR(medium_state, 0444, tape_medium_state_show, NULL);static ssize_ttape_first_minor_show(struct device *dev, struct device_attribute *attr, char *buf){	struct tape_device *tdev;	tdev = (struct tape_device *) dev->driver_data;	return scnprintf(buf, PAGE_SIZE, "%i\n", tdev->first_minor);}staticDEVICE_ATTR(first_minor, 0444, tape_first_minor_show, NULL);static ssize_ttape_state_show(struct device *dev, struct device_attribute *attr, char *buf){	struct tape_device *tdev;	tdev = (struct tape_device *) dev->driver_data;	return scnprintf(buf, PAGE_SIZE, "%s\n", (tdev->first_minor < 0) ?		"OFFLINE" : tape_state_verbose[tdev->tape_state]);}staticDEVICE_ATTR(state, 0444, tape_state_show, NULL);static ssize_ttape_operation_show(struct device *dev, struct device_attribute *attr, char *buf){	struct tape_device *tdev;	ssize_t rc;	tdev = (struct tape_device *) dev->driver_data;	if (tdev->first_minor < 0)		return scnprintf(buf, PAGE_SIZE, "N/A\n");	spin_lock_irq(get_ccwdev_lock(tdev->cdev));	if (list_empty(&tdev->req_queue))		rc = scnprintf(buf, PAGE_SIZE, "---\n");	else {		struct tape_request *req;		req = list_entry(tdev->req_queue.next, struct tape_request,			list);		rc = scnprintf(buf,PAGE_SIZE, "%s\n", tape_op_verbose[req->op]);	}	spin_unlock_irq(get_ccwdev_lock(tdev->cdev));	return rc;}staticDEVICE_ATTR(operation, 0444, tape_operation_show, NULL);static ssize_ttape_blocksize_show(struct device *dev, struct device_attribute *attr, char *buf){	struct tape_device *tdev;	tdev = (struct tape_device *) dev->driver_data;	return scnprintf(buf, PAGE_SIZE, "%i\n", tdev->char_data.block_size);}staticDEVICE_ATTR(blocksize, 0444, tape_blocksize_show, NULL);static struct attribute *tape_attrs[] = {	&dev_attr_medium_state.attr,	&dev_attr_first_minor.attr,	&dev_attr_state.attr,	&dev_attr_operation.attr,	&dev_attr_blocksize.attr,	NULL};static struct attribute_group tape_attr_group = {	.attrs = tape_attrs,};/* * Tape state functions */voidtape_state_set(struct tape_device *device, enum tape_state newstate){	const char *str;	if (device->tape_state == TS_NOT_OPER) {		DBF_EVENT(3, "ts_set err: not oper\n");		return;	}	DBF_EVENT(4, "ts. dev:	%x\n", device->first_minor);	if (device->tape_state < TO_SIZE && device->tape_state >= 0)		str = tape_state_verbose[device->tape_state];	else		str = "UNKNOWN TS";	DBF_EVENT(4, "old ts:	%s\n", str);	if (device->tape_state < TO_SIZE && device->tape_state >=0 )		str = tape_state_verbose[device->tape_state];	else		str = "UNKNOWN TS";	DBF_EVENT(4, "%s\n", str);	DBF_EVENT(4, "new ts:\t\n");	if (newstate < TO_SIZE && newstate >= 0)		str = tape_state_verbose[newstate];	else		str = "UNKNOWN TS";	DBF_EVENT(4, "%s\n", str);	device->tape_state = newstate;	wake_up(&device->state_change_wq);}voidtape_med_state_set(struct tape_device *device, enum tape_medium_state newstate){	if (device->medium_state == newstate)		return;	switch(newstate){	case MS_UNLOADED:		device->tape_generic_status |= GMT_DR_OPEN(~0);		PRINT_INFO("(%s): Tape is unloaded\n",			   device->cdev->dev.bus_id);		break;	case MS_LOADED:		device->tape_generic_status &= ~GMT_DR_OPEN(~0);		PRINT_INFO("(%s): Tape has been mounted\n",			   device->cdev->dev.bus_id);		break;	default:		// print nothing		break;	}	device->medium_state = newstate;	wake_up(&device->state_change_wq);}/* * Stop running ccw. Has to be called with the device lock held. */static inline int__tape_cancel_io(struct tape_device *device, struct tape_request *request){	int retries;	int rc;	/* Check if interrupt has already been processed */	if (request->callback == NULL)		return 0;	rc = 0;	for (retries = 0; retries < 5; retries++) {		rc = ccw_device_clear(device->cdev, (long) request);		switch (rc) {			case 0:				request->status	= TAPE_REQUEST_DONE;				return 0;			case -EBUSY:				request->status	= TAPE_REQUEST_CANCEL;				schedule_work(&device->tape_dnr);				return 0;			case -ENODEV:				DBF_EXCEPTION(2, "device gone, retry\n");				break;			case -EIO:				DBF_EXCEPTION(2, "I/O error, retry\n");				break;			default:				BUG();		}	}	return rc;}/* * Add device into the sorted list, giving it the first * available minor number. */static inttape_assign_minor(struct tape_device *device){	struct tape_device *tmp;	int minor;	minor = 0;	write_lock(&tape_device_lock);	list_for_each_entry(tmp, &tape_device_list, node) {		if (minor < tmp->first_minor)			break;		minor += TAPE_MINORS_PER_DEV;	}	if (minor >= 256) {		write_unlock(&tape_device_lock);		return -ENODEV;	}	device->first_minor = minor;	list_add_tail(&device->node, &tmp->node);	write_unlock(&tape_device_lock);	return 0;}/* remove device from the list */static voidtape_remove_minor(struct tape_device *device){	write_lock(&tape_device_lock);	list_del_init(&device->node);	device->first_minor = -1;	write_unlock(&tape_device_lock);}/* * Set a device online. * * This function is called by the common I/O layer to move a device from the * detected but offline into the online state. * If we return an error (RC < 0) the device remains in the offline state. This * can happen if the device is assigned somewhere else, for example. */inttape_generic_online(struct tape_device *device,		   struct tape_discipline *discipline){	int rc;	DBF_LH(6, "tape_enable_device(%p, %p)\n", device, discipline);	if (device->tape_state != TS_INIT) {		DBF_LH(3, "Tapestate not INIT (%d)\n", device->tape_state);		return -EINVAL;	}	/* Let the discipline have a go at the device. */	device->discipline = discipline;	if (!try_module_get(discipline->owner)) {		PRINT_ERR("Cannot get module. Module gone.\n");		return -EINVAL;	}	rc = discipline->setup_device(device);	if (rc)		goto out;	rc = tape_assign_minor(device);	if (rc)		goto out_discipline;	rc = tapechar_setup_device(device);	if (rc)		goto out_minor;	rc = tapeblock_setup_device(device);	if (rc)		goto out_char;	tape_state_set(device, TS_UNUSED);	DBF_LH(3, "(%08x): Drive set online\n", device->cdev_id);	return 0;out_char:	tapechar_cleanup_device(device);out_discipline:	device->discipline->cleanup_device(device);	device->discipline = NULL;out_minor:	tape_remove_minor(device);out:	module_put(discipline->owner);	return rc;}static inline voidtape_cleanup_device(struct tape_device *device){	tapeblock_cleanup_device(device);	tapechar_cleanup_device(device);	device->discipline->cleanup_device(device);	module_put(device->discipline->owner);	tape_remove_minor(device);	tape_med_state_set(device, MS_UNKNOWN);}/* * Set device offline. * * Called by the common I/O layer if the drive should set offline on user * request. We may prevent this by returning an error. * Manual offline is only allowed while the drive is not in use. */inttape_generic_offline(struct tape_device *device){	if (!device) {		PRINT_ERR("tape_generic_offline: no such device\n");		return -ENODEV;	}	DBF_LH(3, "(%08x): tape_generic_offline(%p)\n",		device->cdev_id, device);	spin_lock_irq(get_ccwdev_lock(device->cdev));	switch (device->tape_state) {		case TS_INIT:		case TS_NOT_OPER:			spin_unlock_irq(get_ccwdev_lock(device->cdev));			break;		case TS_UNUSED:			tape_state_set(device, TS_INIT);			spin_unlock_irq(get_ccwdev_lock(device->cdev));			tape_cleanup_device(device);

⌨️ 快捷键说明

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