1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright IBM Corp. 2004
4 *
5 * Tape class device support
6 *
7 * Author: Stefan Bader <shbader@de.ibm.com>
8 * Based on simple class device code by Greg K-H
9 */
10
11 #define pr_fmt(fmt) "tape: " fmt
12
13 #include <linux/export.h>
14 #include <linux/slab.h>
15
16 #include "tape_class.h"
17
18 static const struct class tape_class = {
19 .name = "tape390",
20 };
21
22 /*
23 * Register a tape device and return a pointer to the cdev structure.
24 *
25 * device
26 * The pointer to the struct device of the physical (base) device.
27 * drivername
28 * The pointer to the drivers name for it's character devices.
29 * dev
30 * The intended major/minor number. The major number may be 0 to
31 * get a dynamic major number.
32 * fops
33 * The pointer to the drivers file operations for the tape device.
34 * devname
35 * The pointer to the name of the character device.
36 */
register_tape_dev(struct device * device,dev_t dev,const struct file_operations * fops,char * device_name,char * mode_name)37 struct tape_class_device *register_tape_dev(
38 struct device * device,
39 dev_t dev,
40 const struct file_operations *fops,
41 char * device_name,
42 char * mode_name)
43 {
44 struct tape_class_device * tcd;
45 int rc;
46 char * s;
47
48 tcd = kzalloc_obj(struct tape_class_device);
49 if (!tcd)
50 return ERR_PTR(-ENOMEM);
51
52 strscpy(tcd->device_name, device_name, TAPECLASS_NAME_LEN);
53 for (s = strchr(tcd->device_name, '/'); s; s = strchr(s, '/'))
54 *s = '!';
55 strscpy(tcd->mode_name, mode_name, TAPECLASS_NAME_LEN);
56 for (s = strchr(tcd->mode_name, '/'); s; s = strchr(s, '/'))
57 *s = '!';
58
59 tcd->char_device = cdev_alloc();
60 if (!tcd->char_device) {
61 rc = -ENOMEM;
62 goto fail_with_tcd;
63 }
64
65 tcd->char_device->owner = fops->owner;
66 tcd->char_device->ops = fops;
67
68 rc = cdev_add(tcd->char_device, dev, 1);
69 if (rc)
70 goto fail_with_cdev;
71
72 tcd->class_device = device_create(&tape_class, device,
73 tcd->char_device->dev, NULL,
74 "%s", tcd->device_name);
75 rc = PTR_ERR_OR_ZERO(tcd->class_device);
76 if (rc)
77 goto fail_with_cdev;
78 rc = sysfs_create_link(
79 &device->kobj,
80 &tcd->class_device->kobj,
81 tcd->mode_name
82 );
83 if (rc)
84 goto fail_with_class_device;
85
86 return tcd;
87
88 fail_with_class_device:
89 device_destroy(&tape_class, tcd->char_device->dev);
90
91 fail_with_cdev:
92 cdev_del(tcd->char_device);
93
94 fail_with_tcd:
95 kfree(tcd);
96
97 return ERR_PTR(rc);
98 }
99 EXPORT_SYMBOL(register_tape_dev);
100
unregister_tape_dev(struct device * device,struct tape_class_device * tcd)101 void unregister_tape_dev(struct device *device, struct tape_class_device *tcd)
102 {
103 if (tcd != NULL && !IS_ERR(tcd)) {
104 sysfs_remove_link(&device->kobj, tcd->mode_name);
105 device_destroy(&tape_class, tcd->char_device->dev);
106 cdev_del(tcd->char_device);
107 kfree(tcd);
108 }
109 }
110 EXPORT_SYMBOL(unregister_tape_dev);
111
tape_class_init(void)112 int tape_class_init(void)
113 {
114 return class_register(&tape_class);
115 }
116
tape_class_exit(void)117 void tape_class_exit(void)
118 {
119 class_unregister(&tape_class);
120 }
121