Commit a8e788da authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] s390: tape class for s390 tapes

From: Martin Schwidefsky <schwidefsky@de.ibm.com>

s390 tape device driver changes:

- Add private tape class to support udev configuration.
parent 60da26d9
......@@ -18,6 +18,6 @@ obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o
tape-$(CONFIG_S390_TAPE_BLOCK) += tape_block.o
tape-$(CONFIG_PROC_FS) += tape_proc.o
tape-objs := tape_core.o tape_std.o tape_char.o $(tape-y)
tape-objs := tape_core.o tape_std.o tape_char.o tape_class.o $(tape-y)
obj-$(CONFIG_S390_TAPE) += tape.o
obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o
......@@ -60,12 +60,6 @@ do { \
#define TAPEBLOCK_HSEC_S2B 2
#define TAPEBLOCK_RETRIES 5
/* Event types for hotplug */
#define TAPE_HOTPLUG_CHAR_ADD 1
#define TAPE_HOTPLUG_BLOCK_ADD 2
#define TAPE_HOTPLUG_CHAR_REMOVE 3
#define TAPE_HOTPLUG_BLOCK_REMOVE 4
enum tape_medium_state {
MS_UNKNOWN,
MS_LOADED,
......@@ -205,6 +199,8 @@ struct tape_device {
struct list_head node;
struct ccw_device * cdev;
struct cdev * nt;
struct cdev * rt;
/* Device discipline information. */
struct tape_discipline * discipline;
......
......@@ -259,9 +259,6 @@ tapeblock_setup_device(struct tape_device * device)
INIT_WORK(&blkdat->requeue_task, tapeblock_requeue,
tape_get_device_reference(device));
/* Will vanish */
tape_hotplug_event(device, tapeblock_major, TAPE_HOTPLUG_BLOCK_ADD);
return 0;
cleanup_queue:
......@@ -274,8 +271,6 @@ tapeblock_setup_device(struct tape_device * device)
void
tapeblock_cleanup_device(struct tape_device *device)
{
tape_hotplug_event(device, tapeblock_major, TAPE_HOTPLUG_BLOCK_REMOVE);
flush_scheduled_work();
device->blk_data.requeue_task.data = tape_put_device(device);
......
......@@ -20,6 +20,7 @@
#include "tape.h"
#include "tape_std.h"
#include "tape_class.h"
#define PRINTK_HEADER "TAPE_CHAR: "
......@@ -47,20 +48,50 @@ static struct file_operations tape_fops =
static int tapechar_major = TAPECHAR_MAJOR;
struct cdev *
tapechar_register_tape_dev(struct tape_device *device, char *name, int i)
{
struct cdev * cdev;
char devname[11];
sprintf(devname, "%s%i", name, i / 2);
cdev = register_tape_dev(
&device->cdev->dev,
MKDEV(tapechar_major, i),
&tape_fops,
devname
);
return ((IS_ERR(cdev)) ? NULL : cdev);
}
/*
* This function is called for every new tapedevice
*/
int
tapechar_setup_device(struct tape_device * device)
{
tape_hotplug_event(device, tapechar_major, TAPE_HOTPLUG_CHAR_ADD);
device->nt = tapechar_register_tape_dev(
device,
"ntibm",
device->first_minor
);
device->rt = tapechar_register_tape_dev(
device,
"rtibm",
device->first_minor + 1
);
return 0;
}
void
tapechar_cleanup_device(struct tape_device *device)
{
tape_hotplug_event(device, tapechar_major, TAPE_HOTPLUG_CHAR_REMOVE);
unregister_tape_dev(device->rt);
device->rt = NULL;
unregister_tape_dev(device->nt);
device->nt = NULL;
}
/*
......@@ -461,20 +492,17 @@ tapechar_ioctl(struct inode *inp, struct file *filp,
int
tapechar_init (void)
{
int rc;
dev_t dev;
/* Register the tape major number to the kernel */
rc = register_chrdev(tapechar_major, "tape", &tape_fops);
if (rc < 0) {
PRINT_ERR("can't get major %d\n", tapechar_major);
DBF_EVENT(3, "TCHAR:initfail\n");
return rc;
}
if (tapechar_major == 0)
tapechar_major = rc; /* accept dynamic major number */
PRINT_ERR("Tape gets major %d for char device\n", tapechar_major);
DBF_EVENT(3, "Tape gets major %d for char device\n", rc);
DBF_EVENT(3, "TCHAR:init ok\n");
if (alloc_chrdev_region(&dev, 0, 256, "tape") != 0)
return -1;
tapechar_major = MAJOR(dev);
PRINT_INFO("tape gets major %d for character devices\n", MAJOR(dev));
#ifdef TAPE390_INTERNAL_CLASS
tape_setup_class();
#endif
return 0;
}
......@@ -484,5 +512,10 @@ tapechar_init (void)
void
tapechar_exit(void)
{
unregister_chrdev (tapechar_major, "tape");
#ifdef TAPE390_INTERNAL_CLASS
tape_cleanup_class();
#endif
PRINT_INFO("tape releases major %d for character devices\n",
tapechar_major);
unregister_chrdev_region(MKDEV(tapechar_major, 0), 256);
}
/*
* Tape class device support
*
* Author: Stefan Bader <shbader@de.ibm.com>
* Based on simple class device code by Greg K-H
*/
#include "tape_class.h"
#ifndef TAPE390_INTERNAL_CLASS
MODULE_AUTHOR("Stefan Bader <shbader@de.ibm.com>");
MODULE_DESCRIPTION("Tape class");
MODULE_LICENSE("GPL");
#endif
struct class_simple *tape_class;
/*
* Register a tape device and return a pointer to the cdev structure.
*
* device
* The pointer to the struct device of the physical (base) device.
* drivername
* The pointer to the drivers name for it's character devices.
* dev
* The intended major/minor number. The major number may be 0 to
* get a dynamic major number.
* fops
* The pointer to the drivers file operations for the tape device.
* devname
* The pointer to the name of the character device.
*/
struct cdev *register_tape_dev(
struct device * device,
dev_t dev,
struct file_operations *fops,
char * devname
) {
struct cdev * cdev;
int rc;
char * s;
cdev = cdev_alloc();
if (!cdev)
return ERR_PTR(-ENOMEM);
cdev->owner = fops->owner;
cdev->ops = fops;
cdev->dev = dev;
strcpy(cdev->kobj.name, devname);
for (s = strchr(cdev->kobj.name, '/'); s; s = strchr(s, '/'))
*s = '!';
rc = cdev_add(cdev, cdev->dev, 1);
if (rc) {
kobject_put(&cdev->kobj);
return ERR_PTR(rc);
}
class_simple_device_add(tape_class, cdev->dev, device, "%s", devname);
return cdev;
}
EXPORT_SYMBOL(register_tape_dev);
void unregister_tape_dev(struct cdev *cdev)
{
if (cdev != NULL) {
class_simple_device_remove(cdev->dev);
cdev_del(cdev);
}
}
EXPORT_SYMBOL(unregister_tape_dev);
#ifndef TAPE390_INTERNAL_CLASS
static int __init tape_init(void)
#else
int tape_setup_class(void)
#endif
{
tape_class = class_simple_create(THIS_MODULE, "tape390");
return 0;
}
#ifndef TAPE390_INTERNAL_CLASS
static void __exit tape_exit(void)
#else
void tape_cleanup_class(void)
#endif
{
class_simple_destroy(tape_class);
tape_class = NULL;
}
#ifndef TAPE390_INTERNAL_CLASS
postcore_initcall(tape_init);
module_exit(tape_exit);
#else
EXPORT_SYMBOL(tape_setup_class);
EXPORT_SYMBOL(tape_cleanup_class);
#endif
/*
* Tape class device support
*
* Author: Stefan Bader <shbader@de.ibm.com>
* Based on simple class device code by Greg K-H
*/
#ifndef __TAPE_CLASS_H__
#define __TAPE_CLASS_H__
#if 0
#include <linux/init.h>
#include <linux/module.h>
#endif
#include <linux/fs.h>
#include <linux/major.h>
#include <linux/kobject.h>
#include <linux/kobj_map.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#define TAPE390_INTERNAL_CLASS
/*
* Register a tape device and return a pointer to the cdev structure.
*
* device
* The pointer to the struct device of the physical (base) device.
* drivername
* The pointer to the drivers name for it's character devices.
* dev
* The intended major/minor number. The major number may be 0 to
* get a dynamic major number.
* fops
* The pointer to the drivers file operations for the tape device.
* devname
* The pointer to the name of the character device.
*/
struct cdev *register_tape_dev(
struct device * device,
dev_t dev,
struct file_operations *fops,
char * devname
);
void unregister_tape_dev(struct cdev *cdev);
#ifdef TAPE390_INTERNAL_CLASS
int tape_setup_class(void);
void tape_cleanup_class(void);
#endif
#endif /* __TAPE_CLASS_H__ */
......@@ -237,10 +237,7 @@ __tape_halt_io(struct tape_device *device, struct tape_request *request)
rc = 0;
for (retries = 0; retries < 5; retries++) {
if (retries < 2)
rc = ccw_device_halt(device->cdev, (long) request);
else
rc = ccw_device_clear(device->cdev, (long) request);
rc = ccw_device_clear(device->cdev, (long) request);
if (rc == 0) { /* Termination successful */
request->rc = -EIO;
......@@ -1015,63 +1012,6 @@ tape_mtop(struct tape_device *device, int mt_op, int mt_count)
}
/*
* Hutplug event support.
*/
void
tape_hotplug_event(struct tape_device *device, int devmaj, int action) {
#ifdef CONFIG_HOTPLUG
char *argv[3];
char *envp[8];
char busid[20];
char major[20];
char minor[20];
/* Call the busid DEVNO to be compatible with old tape.agent. */
sprintf(busid, "DEVNO=%s", device->cdev->dev.bus_id);
sprintf(major, "MAJOR=%d", devmaj);
sprintf(minor, "MINOR=%d", device->first_minor);
argv[0] = hotplug_path;
argv[1] = "tape";
argv[2] = NULL;
envp[0] = "HOME=/";
envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
switch (action) {
case TAPE_HOTPLUG_CHAR_ADD:
case TAPE_HOTPLUG_BLOCK_ADD:
envp[2] = "ACTION=add";
break;
case TAPE_HOTPLUG_CHAR_REMOVE:
case TAPE_HOTPLUG_BLOCK_REMOVE:
envp[2] = "ACTION=remove";
break;
default:
BUG();
}
switch (action) {
case TAPE_HOTPLUG_CHAR_ADD:
case TAPE_HOTPLUG_CHAR_REMOVE:
envp[3] = "INTERFACE=char";
break;
case TAPE_HOTPLUG_BLOCK_ADD:
case TAPE_HOTPLUG_BLOCK_REMOVE:
envp[3] = "INTERFACE=block";
break;
default:
BUG();
}
envp[4] = busid;
envp[5] = major;
envp[6] = minor;
envp[7] = NULL;
call_usermodehelper(argv[0], argv, envp, 0);
#endif
}
/*
* Tape init function.
*/
......@@ -1083,7 +1023,7 @@ tape_init (void)
#ifdef DBF_LIKE_HELL
debug_set_level(tape_dbf_area, 6);
#endif
DBF_EVENT(3, "tape init: ($Revision: 1.41 $)\n");
DBF_EVENT(3, "tape init: ($Revision: 1.44 $)\n");
tape_proc_init();
tapechar_init ();
tapeblock_init ();
......@@ -1108,7 +1048,7 @@ tape_exit(void)
MODULE_AUTHOR("(C) 2001 IBM Deutschland Entwicklung GmbH by Carsten Otte and "
"Michael Holzheu (cotte@de.ibm.com,holzheu@de.ibm.com)");
MODULE_DESCRIPTION("Linux on zSeries channel attached "
"tape device driver ($Revision: 1.41 $)");
"tape device driver ($Revision: 1.44 $)");
MODULE_LICENSE("GPL");
module_init(tape_init);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment