18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * HMC Drive CD/DVD Device 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2013 68c2ecf20Sopenharmony_ci * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This file provides a Linux "misc" character device for access to an 98c2ecf20Sopenharmony_ci * assigned HMC drive CD/DVD-ROM. It works as follows: First create the 108c2ecf20Sopenharmony_ci * device by calling hmcdrv_dev_init(). After open() a lseek(fd, 0, 118c2ecf20Sopenharmony_ci * SEEK_END) indicates that a new FTP command follows (not needed on the 128c2ecf20Sopenharmony_ci * first command after open). Then write() the FTP command ASCII string 138c2ecf20Sopenharmony_ci * to it, e.g. "dir /" or "nls <directory>" or "get <filename>". At the 148c2ecf20Sopenharmony_ci * end read() the response. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "hmcdrv" 188c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/fs.h> 248c2ecf20Sopenharmony_ci#include <linux/cdev.h> 258c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 268c2ecf20Sopenharmony_ci#include <linux/device.h> 278c2ecf20Sopenharmony_ci#include <linux/capability.h> 288c2ecf20Sopenharmony_ci#include <linux/delay.h> 298c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include "hmcdrv_dev.h" 328c2ecf20Sopenharmony_ci#include "hmcdrv_ftp.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* If the following macro is defined, then the HMC device creates it's own 358c2ecf20Sopenharmony_ci * separated device class (and dynamically assigns a major number). If not 368c2ecf20Sopenharmony_ci * defined then the HMC device is assigned to the "misc" class devices. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci#define HMCDRV_DEV_CLASS "hmcftp" 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define HMCDRV_DEV_NAME "hmcdrv" 428c2ecf20Sopenharmony_ci#define HMCDRV_DEV_BUSY_DELAY 500 /* delay between -EBUSY trials in ms */ 438c2ecf20Sopenharmony_ci#define HMCDRV_DEV_BUSY_RETRIES 3 /* number of retries on -EBUSY */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct hmcdrv_dev_node { 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#ifdef HMCDRV_DEV_CLASS 488c2ecf20Sopenharmony_ci struct cdev dev; /* character device structure */ 498c2ecf20Sopenharmony_ci umode_t mode; /* mode of device node (unused, zero) */ 508c2ecf20Sopenharmony_ci#else 518c2ecf20Sopenharmony_ci struct miscdevice dev; /* "misc" device structure */ 528c2ecf20Sopenharmony_ci#endif 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int hmcdrv_dev_open(struct inode *inode, struct file *fp); 578c2ecf20Sopenharmony_cistatic int hmcdrv_dev_release(struct inode *inode, struct file *fp); 588c2ecf20Sopenharmony_cistatic loff_t hmcdrv_dev_seek(struct file *fp, loff_t pos, int whence); 598c2ecf20Sopenharmony_cistatic ssize_t hmcdrv_dev_read(struct file *fp, char __user *ubuf, 608c2ecf20Sopenharmony_ci size_t len, loff_t *pos); 618c2ecf20Sopenharmony_cistatic ssize_t hmcdrv_dev_write(struct file *fp, const char __user *ubuf, 628c2ecf20Sopenharmony_ci size_t len, loff_t *pos); 638c2ecf20Sopenharmony_cistatic ssize_t hmcdrv_dev_transfer(char __kernel *cmd, loff_t offset, 648c2ecf20Sopenharmony_ci char __user *buf, size_t len); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* 678c2ecf20Sopenharmony_ci * device operations 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_cistatic const struct file_operations hmcdrv_dev_fops = { 708c2ecf20Sopenharmony_ci .open = hmcdrv_dev_open, 718c2ecf20Sopenharmony_ci .llseek = hmcdrv_dev_seek, 728c2ecf20Sopenharmony_ci .release = hmcdrv_dev_release, 738c2ecf20Sopenharmony_ci .read = hmcdrv_dev_read, 748c2ecf20Sopenharmony_ci .write = hmcdrv_dev_write, 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic struct hmcdrv_dev_node hmcdrv_dev; /* HMC device struct (static) */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#ifdef HMCDRV_DEV_CLASS 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic struct class *hmcdrv_dev_class; /* device class pointer */ 828c2ecf20Sopenharmony_cistatic dev_t hmcdrv_dev_no; /* device number (major/minor) */ 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/** 858c2ecf20Sopenharmony_ci * hmcdrv_dev_name() - provides a naming hint for a device node in /dev 868c2ecf20Sopenharmony_ci * @dev: device for which the naming/mode hint is 878c2ecf20Sopenharmony_ci * @mode: file mode for device node created in /dev 888c2ecf20Sopenharmony_ci * 898c2ecf20Sopenharmony_ci * See: devtmpfs.c, function devtmpfs_create_node() 908c2ecf20Sopenharmony_ci * 918c2ecf20Sopenharmony_ci * Return: recommended device file name in /dev 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_cistatic char *hmcdrv_dev_name(struct device *dev, umode_t *mode) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci char *nodename = NULL; 968c2ecf20Sopenharmony_ci const char *devname = dev_name(dev); /* kernel device name */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (devname) 998c2ecf20Sopenharmony_ci nodename = kasprintf(GFP_KERNEL, "%s", devname); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* on device destroy (rmmod) the mode pointer may be NULL 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci if (mode) 1048c2ecf20Sopenharmony_ci *mode = hmcdrv_dev.mode; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return nodename; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci#endif /* HMCDRV_DEV_CLASS */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * open() 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_cistatic int hmcdrv_dev_open(struct inode *inode, struct file *fp) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci int rc; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* check for non-blocking access, which is really unsupported 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_ci if (fp->f_flags & O_NONBLOCK) 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Because it makes no sense to open this device read-only (then a 1248c2ecf20Sopenharmony_ci * FTP command cannot be emitted), we respond with an error. 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci if ((fp->f_flags & O_ACCMODE) == O_RDONLY) 1278c2ecf20Sopenharmony_ci return -EINVAL; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* prevent unloading this module as long as anyone holds the 1308c2ecf20Sopenharmony_ci * device file open - so increment the reference count here 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci if (!try_module_get(THIS_MODULE)) 1338c2ecf20Sopenharmony_ci return -ENODEV; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci fp->private_data = NULL; /* no command yet */ 1368c2ecf20Sopenharmony_ci rc = hmcdrv_ftp_startup(); 1378c2ecf20Sopenharmony_ci if (rc) 1388c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci pr_debug("open file '/dev/%pD' with return code %d\n", fp, rc); 1418c2ecf20Sopenharmony_ci return rc; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* 1458c2ecf20Sopenharmony_ci * release() 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_cistatic int hmcdrv_dev_release(struct inode *inode, struct file *fp) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci pr_debug("closing file '/dev/%pD'\n", fp); 1508c2ecf20Sopenharmony_ci kfree(fp->private_data); 1518c2ecf20Sopenharmony_ci fp->private_data = NULL; 1528c2ecf20Sopenharmony_ci hmcdrv_ftp_shutdown(); 1538c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/* 1588c2ecf20Sopenharmony_ci * lseek() 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_cistatic loff_t hmcdrv_dev_seek(struct file *fp, loff_t pos, int whence) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci switch (whence) { 1638c2ecf20Sopenharmony_ci case SEEK_CUR: /* relative to current file position */ 1648c2ecf20Sopenharmony_ci pos += fp->f_pos; /* new position stored in 'pos' */ 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci case SEEK_SET: /* absolute (relative to beginning of file) */ 1688c2ecf20Sopenharmony_ci break; /* SEEK_SET */ 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* We use SEEK_END as a special indicator for a SEEK_SET 1718c2ecf20Sopenharmony_ci * (set absolute position), combined with a FTP command 1728c2ecf20Sopenharmony_ci * clear. 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci case SEEK_END: 1758c2ecf20Sopenharmony_ci if (fp->private_data) { 1768c2ecf20Sopenharmony_ci kfree(fp->private_data); 1778c2ecf20Sopenharmony_ci fp->private_data = NULL; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci break; /* SEEK_END */ 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci default: /* SEEK_DATA, SEEK_HOLE: unsupported */ 1838c2ecf20Sopenharmony_ci return -EINVAL; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (pos < 0) 1878c2ecf20Sopenharmony_ci return -EINVAL; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (fp->f_pos != pos) 1908c2ecf20Sopenharmony_ci ++fp->f_version; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci fp->f_pos = pos; 1938c2ecf20Sopenharmony_ci return pos; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* 1978c2ecf20Sopenharmony_ci * transfer (helper function) 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_cistatic ssize_t hmcdrv_dev_transfer(char __kernel *cmd, loff_t offset, 2008c2ecf20Sopenharmony_ci char __user *buf, size_t len) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci ssize_t retlen; 2038c2ecf20Sopenharmony_ci unsigned trials = HMCDRV_DEV_BUSY_RETRIES; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci do { 2068c2ecf20Sopenharmony_ci retlen = hmcdrv_ftp_cmd(cmd, offset, buf, len); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (retlen != -EBUSY) 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci msleep(HMCDRV_DEV_BUSY_DELAY); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci } while (--trials > 0); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return retlen; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* 2198c2ecf20Sopenharmony_ci * read() 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_cistatic ssize_t hmcdrv_dev_read(struct file *fp, char __user *ubuf, 2228c2ecf20Sopenharmony_ci size_t len, loff_t *pos) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci ssize_t retlen; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (((fp->f_flags & O_ACCMODE) == O_WRONLY) || 2278c2ecf20Sopenharmony_ci (fp->private_data == NULL)) { /* no FTP cmd defined ? */ 2288c2ecf20Sopenharmony_ci return -EBADF; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci retlen = hmcdrv_dev_transfer((char *) fp->private_data, 2328c2ecf20Sopenharmony_ci *pos, ubuf, len); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci pr_debug("read from file '/dev/%pD' at %lld returns %zd/%zu\n", 2358c2ecf20Sopenharmony_ci fp, (long long) *pos, retlen, len); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (retlen > 0) 2388c2ecf20Sopenharmony_ci *pos += retlen; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return retlen; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/* 2448c2ecf20Sopenharmony_ci * write() 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_cistatic ssize_t hmcdrv_dev_write(struct file *fp, const char __user *ubuf, 2478c2ecf20Sopenharmony_ci size_t len, loff_t *pos) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci ssize_t retlen; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci pr_debug("writing file '/dev/%pD' at pos. %lld with length %zd\n", 2528c2ecf20Sopenharmony_ci fp, (long long) *pos, len); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (!fp->private_data) { /* first expect a cmd write */ 2558c2ecf20Sopenharmony_ci fp->private_data = kmalloc(len + 1, GFP_KERNEL); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (!fp->private_data) 2588c2ecf20Sopenharmony_ci return -ENOMEM; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (!copy_from_user(fp->private_data, ubuf, len)) { 2618c2ecf20Sopenharmony_ci ((char *)fp->private_data)[len] = '\0'; 2628c2ecf20Sopenharmony_ci return len; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci kfree(fp->private_data); 2668c2ecf20Sopenharmony_ci fp->private_data = NULL; 2678c2ecf20Sopenharmony_ci return -EFAULT; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci retlen = hmcdrv_dev_transfer((char *) fp->private_data, 2718c2ecf20Sopenharmony_ci *pos, (char __user *) ubuf, len); 2728c2ecf20Sopenharmony_ci if (retlen > 0) 2738c2ecf20Sopenharmony_ci *pos += retlen; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci pr_debug("write to file '/dev/%pD' returned %zd\n", fp, retlen); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return retlen; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/** 2818c2ecf20Sopenharmony_ci * hmcdrv_dev_init() - creates a HMC drive CD/DVD device 2828c2ecf20Sopenharmony_ci * 2838c2ecf20Sopenharmony_ci * This function creates a HMC drive CD/DVD kernel device and an associated 2848c2ecf20Sopenharmony_ci * device under /dev, using a dynamically allocated major number. 2858c2ecf20Sopenharmony_ci * 2868c2ecf20Sopenharmony_ci * Return: 0 on success, else an error code. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_ciint hmcdrv_dev_init(void) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci int rc; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci#ifdef HMCDRV_DEV_CLASS 2938c2ecf20Sopenharmony_ci struct device *dev; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci rc = alloc_chrdev_region(&hmcdrv_dev_no, 0, 1, HMCDRV_DEV_NAME); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (rc) 2988c2ecf20Sopenharmony_ci goto out_err; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci cdev_init(&hmcdrv_dev.dev, &hmcdrv_dev_fops); 3018c2ecf20Sopenharmony_ci hmcdrv_dev.dev.owner = THIS_MODULE; 3028c2ecf20Sopenharmony_ci rc = cdev_add(&hmcdrv_dev.dev, hmcdrv_dev_no, 1); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (rc) 3058c2ecf20Sopenharmony_ci goto out_unreg; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* At this point the character device exists in the kernel (see 3088c2ecf20Sopenharmony_ci * /proc/devices), but not under /dev nor /sys/devices/virtual. So 3098c2ecf20Sopenharmony_ci * we have to create an associated class (see /sys/class). 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_ci hmcdrv_dev_class = class_create(THIS_MODULE, HMCDRV_DEV_CLASS); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (IS_ERR(hmcdrv_dev_class)) { 3148c2ecf20Sopenharmony_ci rc = PTR_ERR(hmcdrv_dev_class); 3158c2ecf20Sopenharmony_ci goto out_devdel; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Finally a device node in /dev has to be established (as 'mkdev' 3198c2ecf20Sopenharmony_ci * does from the command line). Notice that assignment of a device 3208c2ecf20Sopenharmony_ci * node name/mode function is optional (only for mode != 0600). 3218c2ecf20Sopenharmony_ci */ 3228c2ecf20Sopenharmony_ci hmcdrv_dev.mode = 0; /* "unset" */ 3238c2ecf20Sopenharmony_ci hmcdrv_dev_class->devnode = hmcdrv_dev_name; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci dev = device_create(hmcdrv_dev_class, NULL, hmcdrv_dev_no, NULL, 3268c2ecf20Sopenharmony_ci "%s", HMCDRV_DEV_NAME); 3278c2ecf20Sopenharmony_ci if (!IS_ERR(dev)) 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci rc = PTR_ERR(dev); 3318c2ecf20Sopenharmony_ci class_destroy(hmcdrv_dev_class); 3328c2ecf20Sopenharmony_ci hmcdrv_dev_class = NULL; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ciout_devdel: 3358c2ecf20Sopenharmony_ci cdev_del(&hmcdrv_dev.dev); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ciout_unreg: 3388c2ecf20Sopenharmony_ci unregister_chrdev_region(hmcdrv_dev_no, 1); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ciout_err: 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci#else /* !HMCDRV_DEV_CLASS */ 3438c2ecf20Sopenharmony_ci hmcdrv_dev.dev.minor = MISC_DYNAMIC_MINOR; 3448c2ecf20Sopenharmony_ci hmcdrv_dev.dev.name = HMCDRV_DEV_NAME; 3458c2ecf20Sopenharmony_ci hmcdrv_dev.dev.fops = &hmcdrv_dev_fops; 3468c2ecf20Sopenharmony_ci hmcdrv_dev.dev.mode = 0; /* finally produces 0600 */ 3478c2ecf20Sopenharmony_ci rc = misc_register(&hmcdrv_dev.dev); 3488c2ecf20Sopenharmony_ci#endif /* HMCDRV_DEV_CLASS */ 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci return rc; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci/** 3548c2ecf20Sopenharmony_ci * hmcdrv_dev_exit() - destroys a HMC drive CD/DVD device 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_civoid hmcdrv_dev_exit(void) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci#ifdef HMCDRV_DEV_CLASS 3598c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(hmcdrv_dev_class)) { 3608c2ecf20Sopenharmony_ci device_destroy(hmcdrv_dev_class, hmcdrv_dev_no); 3618c2ecf20Sopenharmony_ci class_destroy(hmcdrv_dev_class); 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci cdev_del(&hmcdrv_dev.dev); 3658c2ecf20Sopenharmony_ci unregister_chrdev_region(hmcdrv_dev_no, 1); 3668c2ecf20Sopenharmony_ci#else /* !HMCDRV_DEV_CLASS */ 3678c2ecf20Sopenharmony_ci misc_deregister(&hmcdrv_dev.dev); 3688c2ecf20Sopenharmony_ci#endif /* HMCDRV_DEV_CLASS */ 3698c2ecf20Sopenharmony_ci} 370