18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2000 Jens Axboe <axboe@suse.de>
38c2ecf20Sopenharmony_ci * Copyright (C) 2001-2004 Peter Osterlund <petero2@telia.com>
48c2ecf20Sopenharmony_ci * Copyright (C) 2006 Thomas Maier <balagi@justmail.de>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * May be copied or modified under the terms of the GNU General Public
78c2ecf20Sopenharmony_ci * License.  See linux/COPYING for more information.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Packet writing layer for ATAPI and SCSI CD-RW, DVD+RW, DVD-RW and
108c2ecf20Sopenharmony_ci * DVD-RAM devices.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Theory of operation:
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * At the lowest level, there is the standard driver for the CD/DVD device,
158c2ecf20Sopenharmony_ci * typically ide-cd.c or sr.c. This driver can handle read and write requests,
168c2ecf20Sopenharmony_ci * but it doesn't know anything about the special restrictions that apply to
178c2ecf20Sopenharmony_ci * packet writing. One restriction is that write requests must be aligned to
188c2ecf20Sopenharmony_ci * packet boundaries on the physical media, and the size of a write request
198c2ecf20Sopenharmony_ci * must be equal to the packet size. Another restriction is that a
208c2ecf20Sopenharmony_ci * GPCMD_FLUSH_CACHE command has to be issued to the drive before a read
218c2ecf20Sopenharmony_ci * command, if the previous command was a write.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * The purpose of the packet writing driver is to hide these restrictions from
248c2ecf20Sopenharmony_ci * higher layers, such as file systems, and present a block device that can be
258c2ecf20Sopenharmony_ci * randomly read and written using 2kB-sized blocks.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * The lowest layer in the packet writing driver is the packet I/O scheduler.
288c2ecf20Sopenharmony_ci * Its data is defined by the struct packet_iosched and includes two bio
298c2ecf20Sopenharmony_ci * queues with pending read and write requests. These queues are processed
308c2ecf20Sopenharmony_ci * by the pkt_iosched_process_queue() function. The write requests in this
318c2ecf20Sopenharmony_ci * queue are already properly aligned and sized. This layer is responsible for
328c2ecf20Sopenharmony_ci * issuing the flush cache commands and scheduling the I/O in a good order.
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * The next layer transforms unaligned write requests to aligned writes. This
358c2ecf20Sopenharmony_ci * transformation requires reading missing pieces of data from the underlying
368c2ecf20Sopenharmony_ci * block device, assembling the pieces to full packets and queuing them to the
378c2ecf20Sopenharmony_ci * packet I/O scheduler.
388c2ecf20Sopenharmony_ci *
398c2ecf20Sopenharmony_ci * At the top layer there is a custom ->submit_bio function that forwards
408c2ecf20Sopenharmony_ci * read requests directly to the iosched queue and puts write requests in the
418c2ecf20Sopenharmony_ci * unaligned write queue. A kernel thread performs the necessary read
428c2ecf20Sopenharmony_ci * gathering to convert the unaligned writes to aligned writes and then feeds
438c2ecf20Sopenharmony_ci * them to the packet I/O scheduler.
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci *************************************************************************/
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#include <linux/pktcdvd.h>
508c2ecf20Sopenharmony_ci#include <linux/module.h>
518c2ecf20Sopenharmony_ci#include <linux/types.h>
528c2ecf20Sopenharmony_ci#include <linux/kernel.h>
538c2ecf20Sopenharmony_ci#include <linux/compat.h>
548c2ecf20Sopenharmony_ci#include <linux/kthread.h>
558c2ecf20Sopenharmony_ci#include <linux/errno.h>
568c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
578c2ecf20Sopenharmony_ci#include <linux/file.h>
588c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
598c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
608c2ecf20Sopenharmony_ci#include <linux/miscdevice.h>
618c2ecf20Sopenharmony_ci#include <linux/freezer.h>
628c2ecf20Sopenharmony_ci#include <linux/mutex.h>
638c2ecf20Sopenharmony_ci#include <linux/slab.h>
648c2ecf20Sopenharmony_ci#include <linux/backing-dev.h>
658c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h>
668c2ecf20Sopenharmony_ci#include <scsi/scsi_ioctl.h>
678c2ecf20Sopenharmony_ci#include <scsi/scsi.h>
688c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
698c2ecf20Sopenharmony_ci#include <linux/device.h>
708c2ecf20Sopenharmony_ci#include <linux/nospec.h>
718c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define DRIVER_NAME	"pktcdvd"
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define pkt_err(pd, fmt, ...)						\
768c2ecf20Sopenharmony_ci	pr_err("%s: " fmt, pd->name, ##__VA_ARGS__)
778c2ecf20Sopenharmony_ci#define pkt_notice(pd, fmt, ...)					\
788c2ecf20Sopenharmony_ci	pr_notice("%s: " fmt, pd->name, ##__VA_ARGS__)
798c2ecf20Sopenharmony_ci#define pkt_info(pd, fmt, ...)						\
808c2ecf20Sopenharmony_ci	pr_info("%s: " fmt, pd->name, ##__VA_ARGS__)
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define pkt_dbg(level, pd, fmt, ...)					\
838c2ecf20Sopenharmony_cido {									\
848c2ecf20Sopenharmony_ci	if (level == 2 && PACKET_DEBUG >= 2)				\
858c2ecf20Sopenharmony_ci		pr_notice("%s: %s():" fmt,				\
868c2ecf20Sopenharmony_ci			  pd->name, __func__, ##__VA_ARGS__);		\
878c2ecf20Sopenharmony_ci	else if (level == 1 && PACKET_DEBUG >= 1)			\
888c2ecf20Sopenharmony_ci		pr_notice("%s: " fmt, pd->name, ##__VA_ARGS__);		\
898c2ecf20Sopenharmony_ci} while (0)
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci#define MAX_SPEED 0xffff
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pktcdvd_mutex);
948c2ecf20Sopenharmony_cistatic struct pktcdvd_device *pkt_devs[MAX_WRITERS];
958c2ecf20Sopenharmony_cistatic struct proc_dir_entry *pkt_proc;
968c2ecf20Sopenharmony_cistatic int pktdev_major;
978c2ecf20Sopenharmony_cistatic int write_congestion_on  = PKT_WRITE_CONGESTION_ON;
988c2ecf20Sopenharmony_cistatic int write_congestion_off = PKT_WRITE_CONGESTION_OFF;
998c2ecf20Sopenharmony_cistatic struct mutex ctl_mutex;	/* Serialize open/close/setup/teardown */
1008c2ecf20Sopenharmony_cistatic mempool_t psd_pool;
1018c2ecf20Sopenharmony_cistatic struct bio_set pkt_bio_set;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic struct class	*class_pktcdvd = NULL;    /* /sys/class/pktcdvd */
1048c2ecf20Sopenharmony_cistatic struct dentry	*pkt_debugfs_root = NULL; /* /sys/kernel/debug/pktcdvd */
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/* forward declaration */
1078c2ecf20Sopenharmony_cistatic int pkt_setup_dev(dev_t dev, dev_t* pkt_dev);
1088c2ecf20Sopenharmony_cistatic int pkt_remove_dev(dev_t pkt_dev);
1098c2ecf20Sopenharmony_cistatic int pkt_seq_show(struct seq_file *m, void *p);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic sector_t get_zone(sector_t sector, struct pktcdvd_device *pd)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	return (sector + pd->offset) & ~(sector_t)(pd->settings.size - 1);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/*
1178c2ecf20Sopenharmony_ci * create and register a pktcdvd kernel object.
1188c2ecf20Sopenharmony_ci */
1198c2ecf20Sopenharmony_cistatic struct pktcdvd_kobj* pkt_kobj_create(struct pktcdvd_device *pd,
1208c2ecf20Sopenharmony_ci					const char* name,
1218c2ecf20Sopenharmony_ci					struct kobject* parent,
1228c2ecf20Sopenharmony_ci					struct kobj_type* ktype)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct pktcdvd_kobj *p;
1258c2ecf20Sopenharmony_ci	int error;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	p = kzalloc(sizeof(*p), GFP_KERNEL);
1288c2ecf20Sopenharmony_ci	if (!p)
1298c2ecf20Sopenharmony_ci		return NULL;
1308c2ecf20Sopenharmony_ci	p->pd = pd;
1318c2ecf20Sopenharmony_ci	error = kobject_init_and_add(&p->kobj, ktype, parent, "%s", name);
1328c2ecf20Sopenharmony_ci	if (error) {
1338c2ecf20Sopenharmony_ci		kobject_put(&p->kobj);
1348c2ecf20Sopenharmony_ci		return NULL;
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci	kobject_uevent(&p->kobj, KOBJ_ADD);
1378c2ecf20Sopenharmony_ci	return p;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci/*
1408c2ecf20Sopenharmony_ci * remove a pktcdvd kernel object.
1418c2ecf20Sopenharmony_ci */
1428c2ecf20Sopenharmony_cistatic void pkt_kobj_remove(struct pktcdvd_kobj *p)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	if (p)
1458c2ecf20Sopenharmony_ci		kobject_put(&p->kobj);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci/*
1488c2ecf20Sopenharmony_ci * default release function for pktcdvd kernel objects.
1498c2ecf20Sopenharmony_ci */
1508c2ecf20Sopenharmony_cistatic void pkt_kobj_release(struct kobject *kobj)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	kfree(to_pktcdvdkobj(kobj));
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/**********************************************************
1578c2ecf20Sopenharmony_ci *
1588c2ecf20Sopenharmony_ci * sysfs interface for pktcdvd
1598c2ecf20Sopenharmony_ci * by (C) 2006  Thomas Maier <balagi@justmail.de>
1608c2ecf20Sopenharmony_ci *
1618c2ecf20Sopenharmony_ci **********************************************************/
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci#define DEF_ATTR(_obj,_name,_mode) \
1648c2ecf20Sopenharmony_ci	static struct attribute _obj = { .name = _name, .mode = _mode }
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/**********************************************************
1678c2ecf20Sopenharmony_ci  /sys/class/pktcdvd/pktcdvd[0-7]/
1688c2ecf20Sopenharmony_ci                     stat/reset
1698c2ecf20Sopenharmony_ci                     stat/packets_started
1708c2ecf20Sopenharmony_ci                     stat/packets_finished
1718c2ecf20Sopenharmony_ci                     stat/kb_written
1728c2ecf20Sopenharmony_ci                     stat/kb_read
1738c2ecf20Sopenharmony_ci                     stat/kb_read_gather
1748c2ecf20Sopenharmony_ci                     write_queue/size
1758c2ecf20Sopenharmony_ci                     write_queue/congestion_off
1768c2ecf20Sopenharmony_ci                     write_queue/congestion_on
1778c2ecf20Sopenharmony_ci **********************************************************/
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ciDEF_ATTR(kobj_pkt_attr_st1, "reset", 0200);
1808c2ecf20Sopenharmony_ciDEF_ATTR(kobj_pkt_attr_st2, "packets_started", 0444);
1818c2ecf20Sopenharmony_ciDEF_ATTR(kobj_pkt_attr_st3, "packets_finished", 0444);
1828c2ecf20Sopenharmony_ciDEF_ATTR(kobj_pkt_attr_st4, "kb_written", 0444);
1838c2ecf20Sopenharmony_ciDEF_ATTR(kobj_pkt_attr_st5, "kb_read", 0444);
1848c2ecf20Sopenharmony_ciDEF_ATTR(kobj_pkt_attr_st6, "kb_read_gather", 0444);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic struct attribute *kobj_pkt_attrs_stat[] = {
1878c2ecf20Sopenharmony_ci	&kobj_pkt_attr_st1,
1888c2ecf20Sopenharmony_ci	&kobj_pkt_attr_st2,
1898c2ecf20Sopenharmony_ci	&kobj_pkt_attr_st3,
1908c2ecf20Sopenharmony_ci	&kobj_pkt_attr_st4,
1918c2ecf20Sopenharmony_ci	&kobj_pkt_attr_st5,
1928c2ecf20Sopenharmony_ci	&kobj_pkt_attr_st6,
1938c2ecf20Sopenharmony_ci	NULL
1948c2ecf20Sopenharmony_ci};
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ciDEF_ATTR(kobj_pkt_attr_wq1, "size", 0444);
1978c2ecf20Sopenharmony_ciDEF_ATTR(kobj_pkt_attr_wq2, "congestion_off", 0644);
1988c2ecf20Sopenharmony_ciDEF_ATTR(kobj_pkt_attr_wq3, "congestion_on",  0644);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic struct attribute *kobj_pkt_attrs_wqueue[] = {
2018c2ecf20Sopenharmony_ci	&kobj_pkt_attr_wq1,
2028c2ecf20Sopenharmony_ci	&kobj_pkt_attr_wq2,
2038c2ecf20Sopenharmony_ci	&kobj_pkt_attr_wq3,
2048c2ecf20Sopenharmony_ci	NULL
2058c2ecf20Sopenharmony_ci};
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic ssize_t kobj_pkt_show(struct kobject *kobj,
2088c2ecf20Sopenharmony_ci			struct attribute *attr, char *data)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd = to_pktcdvdkobj(kobj)->pd;
2118c2ecf20Sopenharmony_ci	int n = 0;
2128c2ecf20Sopenharmony_ci	int v;
2138c2ecf20Sopenharmony_ci	if (strcmp(attr->name, "packets_started") == 0) {
2148c2ecf20Sopenharmony_ci		n = sprintf(data, "%lu\n", pd->stats.pkt_started);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	} else if (strcmp(attr->name, "packets_finished") == 0) {
2178c2ecf20Sopenharmony_ci		n = sprintf(data, "%lu\n", pd->stats.pkt_ended);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	} else if (strcmp(attr->name, "kb_written") == 0) {
2208c2ecf20Sopenharmony_ci		n = sprintf(data, "%lu\n", pd->stats.secs_w >> 1);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	} else if (strcmp(attr->name, "kb_read") == 0) {
2238c2ecf20Sopenharmony_ci		n = sprintf(data, "%lu\n", pd->stats.secs_r >> 1);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	} else if (strcmp(attr->name, "kb_read_gather") == 0) {
2268c2ecf20Sopenharmony_ci		n = sprintf(data, "%lu\n", pd->stats.secs_rg >> 1);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	} else if (strcmp(attr->name, "size") == 0) {
2298c2ecf20Sopenharmony_ci		spin_lock(&pd->lock);
2308c2ecf20Sopenharmony_ci		v = pd->bio_queue_size;
2318c2ecf20Sopenharmony_ci		spin_unlock(&pd->lock);
2328c2ecf20Sopenharmony_ci		n = sprintf(data, "%d\n", v);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	} else if (strcmp(attr->name, "congestion_off") == 0) {
2358c2ecf20Sopenharmony_ci		spin_lock(&pd->lock);
2368c2ecf20Sopenharmony_ci		v = pd->write_congestion_off;
2378c2ecf20Sopenharmony_ci		spin_unlock(&pd->lock);
2388c2ecf20Sopenharmony_ci		n = sprintf(data, "%d\n", v);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	} else if (strcmp(attr->name, "congestion_on") == 0) {
2418c2ecf20Sopenharmony_ci		spin_lock(&pd->lock);
2428c2ecf20Sopenharmony_ci		v = pd->write_congestion_on;
2438c2ecf20Sopenharmony_ci		spin_unlock(&pd->lock);
2448c2ecf20Sopenharmony_ci		n = sprintf(data, "%d\n", v);
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci	return n;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic void init_write_congestion_marks(int* lo, int* hi)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	if (*hi > 0) {
2528c2ecf20Sopenharmony_ci		*hi = max(*hi, 500);
2538c2ecf20Sopenharmony_ci		*hi = min(*hi, 1000000);
2548c2ecf20Sopenharmony_ci		if (*lo <= 0)
2558c2ecf20Sopenharmony_ci			*lo = *hi - 100;
2568c2ecf20Sopenharmony_ci		else {
2578c2ecf20Sopenharmony_ci			*lo = min(*lo, *hi - 100);
2588c2ecf20Sopenharmony_ci			*lo = max(*lo, 100);
2598c2ecf20Sopenharmony_ci		}
2608c2ecf20Sopenharmony_ci	} else {
2618c2ecf20Sopenharmony_ci		*hi = -1;
2628c2ecf20Sopenharmony_ci		*lo = -1;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic ssize_t kobj_pkt_store(struct kobject *kobj,
2678c2ecf20Sopenharmony_ci			struct attribute *attr,
2688c2ecf20Sopenharmony_ci			const char *data, size_t len)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd = to_pktcdvdkobj(kobj)->pd;
2718c2ecf20Sopenharmony_ci	int val;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if (strcmp(attr->name, "reset") == 0 && len > 0) {
2748c2ecf20Sopenharmony_ci		pd->stats.pkt_started = 0;
2758c2ecf20Sopenharmony_ci		pd->stats.pkt_ended = 0;
2768c2ecf20Sopenharmony_ci		pd->stats.secs_w = 0;
2778c2ecf20Sopenharmony_ci		pd->stats.secs_rg = 0;
2788c2ecf20Sopenharmony_ci		pd->stats.secs_r = 0;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	} else if (strcmp(attr->name, "congestion_off") == 0
2818c2ecf20Sopenharmony_ci		   && sscanf(data, "%d", &val) == 1) {
2828c2ecf20Sopenharmony_ci		spin_lock(&pd->lock);
2838c2ecf20Sopenharmony_ci		pd->write_congestion_off = val;
2848c2ecf20Sopenharmony_ci		init_write_congestion_marks(&pd->write_congestion_off,
2858c2ecf20Sopenharmony_ci					&pd->write_congestion_on);
2868c2ecf20Sopenharmony_ci		spin_unlock(&pd->lock);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	} else if (strcmp(attr->name, "congestion_on") == 0
2898c2ecf20Sopenharmony_ci		   && sscanf(data, "%d", &val) == 1) {
2908c2ecf20Sopenharmony_ci		spin_lock(&pd->lock);
2918c2ecf20Sopenharmony_ci		pd->write_congestion_on = val;
2928c2ecf20Sopenharmony_ci		init_write_congestion_marks(&pd->write_congestion_off,
2938c2ecf20Sopenharmony_ci					&pd->write_congestion_on);
2948c2ecf20Sopenharmony_ci		spin_unlock(&pd->lock);
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci	return len;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic const struct sysfs_ops kobj_pkt_ops = {
3008c2ecf20Sopenharmony_ci	.show = kobj_pkt_show,
3018c2ecf20Sopenharmony_ci	.store = kobj_pkt_store
3028c2ecf20Sopenharmony_ci};
3038c2ecf20Sopenharmony_cistatic struct kobj_type kobj_pkt_type_stat = {
3048c2ecf20Sopenharmony_ci	.release = pkt_kobj_release,
3058c2ecf20Sopenharmony_ci	.sysfs_ops = &kobj_pkt_ops,
3068c2ecf20Sopenharmony_ci	.default_attrs = kobj_pkt_attrs_stat
3078c2ecf20Sopenharmony_ci};
3088c2ecf20Sopenharmony_cistatic struct kobj_type kobj_pkt_type_wqueue = {
3098c2ecf20Sopenharmony_ci	.release = pkt_kobj_release,
3108c2ecf20Sopenharmony_ci	.sysfs_ops = &kobj_pkt_ops,
3118c2ecf20Sopenharmony_ci	.default_attrs = kobj_pkt_attrs_wqueue
3128c2ecf20Sopenharmony_ci};
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic void pkt_sysfs_dev_new(struct pktcdvd_device *pd)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	if (class_pktcdvd) {
3178c2ecf20Sopenharmony_ci		pd->dev = device_create(class_pktcdvd, NULL, MKDEV(0, 0), NULL,
3188c2ecf20Sopenharmony_ci					"%s", pd->name);
3198c2ecf20Sopenharmony_ci		if (IS_ERR(pd->dev))
3208c2ecf20Sopenharmony_ci			pd->dev = NULL;
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci	if (pd->dev) {
3238c2ecf20Sopenharmony_ci		pd->kobj_stat = pkt_kobj_create(pd, "stat",
3248c2ecf20Sopenharmony_ci					&pd->dev->kobj,
3258c2ecf20Sopenharmony_ci					&kobj_pkt_type_stat);
3268c2ecf20Sopenharmony_ci		pd->kobj_wqueue = pkt_kobj_create(pd, "write_queue",
3278c2ecf20Sopenharmony_ci					&pd->dev->kobj,
3288c2ecf20Sopenharmony_ci					&kobj_pkt_type_wqueue);
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic void pkt_sysfs_dev_remove(struct pktcdvd_device *pd)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	pkt_kobj_remove(pd->kobj_stat);
3358c2ecf20Sopenharmony_ci	pkt_kobj_remove(pd->kobj_wqueue);
3368c2ecf20Sopenharmony_ci	if (class_pktcdvd)
3378c2ecf20Sopenharmony_ci		device_unregister(pd->dev);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci/********************************************************************
3428c2ecf20Sopenharmony_ci  /sys/class/pktcdvd/
3438c2ecf20Sopenharmony_ci                     add            map block device
3448c2ecf20Sopenharmony_ci                     remove         unmap packet dev
3458c2ecf20Sopenharmony_ci                     device_map     show mappings
3468c2ecf20Sopenharmony_ci *******************************************************************/
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic void class_pktcdvd_release(struct class *cls)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	kfree(cls);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic ssize_t device_map_show(struct class *c, struct class_attribute *attr,
3548c2ecf20Sopenharmony_ci			       char *data)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	int n = 0;
3578c2ecf20Sopenharmony_ci	int idx;
3588c2ecf20Sopenharmony_ci	mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
3598c2ecf20Sopenharmony_ci	for (idx = 0; idx < MAX_WRITERS; idx++) {
3608c2ecf20Sopenharmony_ci		struct pktcdvd_device *pd = pkt_devs[idx];
3618c2ecf20Sopenharmony_ci		if (!pd)
3628c2ecf20Sopenharmony_ci			continue;
3638c2ecf20Sopenharmony_ci		n += sprintf(data+n, "%s %u:%u %u:%u\n",
3648c2ecf20Sopenharmony_ci			pd->name,
3658c2ecf20Sopenharmony_ci			MAJOR(pd->pkt_dev), MINOR(pd->pkt_dev),
3668c2ecf20Sopenharmony_ci			MAJOR(pd->bdev->bd_dev),
3678c2ecf20Sopenharmony_ci			MINOR(pd->bdev->bd_dev));
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci	mutex_unlock(&ctl_mutex);
3708c2ecf20Sopenharmony_ci	return n;
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_cistatic CLASS_ATTR_RO(device_map);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic ssize_t add_store(struct class *c, struct class_attribute *attr,
3758c2ecf20Sopenharmony_ci			 const char *buf, size_t count)
3768c2ecf20Sopenharmony_ci{
3778c2ecf20Sopenharmony_ci	unsigned int major, minor;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (sscanf(buf, "%u:%u", &major, &minor) == 2) {
3808c2ecf20Sopenharmony_ci		/* pkt_setup_dev() expects caller to hold reference to self */
3818c2ecf20Sopenharmony_ci		if (!try_module_get(THIS_MODULE))
3828c2ecf20Sopenharmony_ci			return -ENODEV;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		pkt_setup_dev(MKDEV(major, minor), NULL);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		module_put(THIS_MODULE);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		return count;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return -EINVAL;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_cistatic CLASS_ATTR_WO(add);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic ssize_t remove_store(struct class *c, struct class_attribute *attr,
3968c2ecf20Sopenharmony_ci			    const char *buf, size_t count)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	unsigned int major, minor;
3998c2ecf20Sopenharmony_ci	if (sscanf(buf, "%u:%u", &major, &minor) == 2) {
4008c2ecf20Sopenharmony_ci		pkt_remove_dev(MKDEV(major, minor));
4018c2ecf20Sopenharmony_ci		return count;
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci	return -EINVAL;
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_cistatic CLASS_ATTR_WO(remove);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic struct attribute *class_pktcdvd_attrs[] = {
4088c2ecf20Sopenharmony_ci	&class_attr_add.attr,
4098c2ecf20Sopenharmony_ci	&class_attr_remove.attr,
4108c2ecf20Sopenharmony_ci	&class_attr_device_map.attr,
4118c2ecf20Sopenharmony_ci	NULL,
4128c2ecf20Sopenharmony_ci};
4138c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(class_pktcdvd);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic int pkt_sysfs_init(void)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	int ret = 0;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	/*
4208c2ecf20Sopenharmony_ci	 * create control files in sysfs
4218c2ecf20Sopenharmony_ci	 * /sys/class/pktcdvd/...
4228c2ecf20Sopenharmony_ci	 */
4238c2ecf20Sopenharmony_ci	class_pktcdvd = kzalloc(sizeof(*class_pktcdvd), GFP_KERNEL);
4248c2ecf20Sopenharmony_ci	if (!class_pktcdvd)
4258c2ecf20Sopenharmony_ci		return -ENOMEM;
4268c2ecf20Sopenharmony_ci	class_pktcdvd->name = DRIVER_NAME;
4278c2ecf20Sopenharmony_ci	class_pktcdvd->owner = THIS_MODULE;
4288c2ecf20Sopenharmony_ci	class_pktcdvd->class_release = class_pktcdvd_release;
4298c2ecf20Sopenharmony_ci	class_pktcdvd->class_groups = class_pktcdvd_groups;
4308c2ecf20Sopenharmony_ci	ret = class_register(class_pktcdvd);
4318c2ecf20Sopenharmony_ci	if (ret) {
4328c2ecf20Sopenharmony_ci		kfree(class_pktcdvd);
4338c2ecf20Sopenharmony_ci		class_pktcdvd = NULL;
4348c2ecf20Sopenharmony_ci		pr_err("failed to create class pktcdvd\n");
4358c2ecf20Sopenharmony_ci		return ret;
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci	return 0;
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic void pkt_sysfs_cleanup(void)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	if (class_pktcdvd)
4438c2ecf20Sopenharmony_ci		class_destroy(class_pktcdvd);
4448c2ecf20Sopenharmony_ci	class_pktcdvd = NULL;
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci/********************************************************************
4488c2ecf20Sopenharmony_ci  entries in debugfs
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci  /sys/kernel/debug/pktcdvd[0-7]/
4518c2ecf20Sopenharmony_ci			info
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci *******************************************************************/
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_cistatic int pkt_debugfs_seq_show(struct seq_file *m, void *p)
4568c2ecf20Sopenharmony_ci{
4578c2ecf20Sopenharmony_ci	return pkt_seq_show(m, p);
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic int pkt_debugfs_fops_open(struct inode *inode, struct file *file)
4618c2ecf20Sopenharmony_ci{
4628c2ecf20Sopenharmony_ci	return single_open(file, pkt_debugfs_seq_show, inode->i_private);
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic const struct file_operations debug_fops = {
4668c2ecf20Sopenharmony_ci	.open		= pkt_debugfs_fops_open,
4678c2ecf20Sopenharmony_ci	.read		= seq_read,
4688c2ecf20Sopenharmony_ci	.llseek		= seq_lseek,
4698c2ecf20Sopenharmony_ci	.release	= single_release,
4708c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
4718c2ecf20Sopenharmony_ci};
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistatic void pkt_debugfs_dev_new(struct pktcdvd_device *pd)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	if (!pkt_debugfs_root)
4768c2ecf20Sopenharmony_ci		return;
4778c2ecf20Sopenharmony_ci	pd->dfs_d_root = debugfs_create_dir(pd->name, pkt_debugfs_root);
4788c2ecf20Sopenharmony_ci	if (!pd->dfs_d_root)
4798c2ecf20Sopenharmony_ci		return;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	pd->dfs_f_info = debugfs_create_file("info", 0444,
4828c2ecf20Sopenharmony_ci					     pd->dfs_d_root, pd, &debug_fops);
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cistatic void pkt_debugfs_dev_remove(struct pktcdvd_device *pd)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	if (!pkt_debugfs_root)
4888c2ecf20Sopenharmony_ci		return;
4898c2ecf20Sopenharmony_ci	debugfs_remove(pd->dfs_f_info);
4908c2ecf20Sopenharmony_ci	debugfs_remove(pd->dfs_d_root);
4918c2ecf20Sopenharmony_ci	pd->dfs_f_info = NULL;
4928c2ecf20Sopenharmony_ci	pd->dfs_d_root = NULL;
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic void pkt_debugfs_init(void)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	pkt_debugfs_root = debugfs_create_dir(DRIVER_NAME, NULL);
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_cistatic void pkt_debugfs_cleanup(void)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	debugfs_remove(pkt_debugfs_root);
5038c2ecf20Sopenharmony_ci	pkt_debugfs_root = NULL;
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci/* ----------------------------------------------------------*/
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_cistatic void pkt_bio_finished(struct pktcdvd_device *pd)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	BUG_ON(atomic_read(&pd->cdrw.pending_bios) <= 0);
5128c2ecf20Sopenharmony_ci	if (atomic_dec_and_test(&pd->cdrw.pending_bios)) {
5138c2ecf20Sopenharmony_ci		pkt_dbg(2, pd, "queue empty\n");
5148c2ecf20Sopenharmony_ci		atomic_set(&pd->iosched.attention, 1);
5158c2ecf20Sopenharmony_ci		wake_up(&pd->wqueue);
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci/*
5208c2ecf20Sopenharmony_ci * Allocate a packet_data struct
5218c2ecf20Sopenharmony_ci */
5228c2ecf20Sopenharmony_cistatic struct packet_data *pkt_alloc_packet_data(int frames)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	int i;
5258c2ecf20Sopenharmony_ci	struct packet_data *pkt;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	pkt = kzalloc(sizeof(struct packet_data), GFP_KERNEL);
5288c2ecf20Sopenharmony_ci	if (!pkt)
5298c2ecf20Sopenharmony_ci		goto no_pkt;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	pkt->frames = frames;
5328c2ecf20Sopenharmony_ci	pkt->w_bio = bio_kmalloc(GFP_KERNEL, frames);
5338c2ecf20Sopenharmony_ci	if (!pkt->w_bio)
5348c2ecf20Sopenharmony_ci		goto no_bio;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	for (i = 0; i < frames / FRAMES_PER_PAGE; i++) {
5378c2ecf20Sopenharmony_ci		pkt->pages[i] = alloc_page(GFP_KERNEL|__GFP_ZERO);
5388c2ecf20Sopenharmony_ci		if (!pkt->pages[i])
5398c2ecf20Sopenharmony_ci			goto no_page;
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	spin_lock_init(&pkt->lock);
5438c2ecf20Sopenharmony_ci	bio_list_init(&pkt->orig_bios);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	for (i = 0; i < frames; i++) {
5468c2ecf20Sopenharmony_ci		struct bio *bio = bio_kmalloc(GFP_KERNEL, 1);
5478c2ecf20Sopenharmony_ci		if (!bio)
5488c2ecf20Sopenharmony_ci			goto no_rd_bio;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci		pkt->r_bios[i] = bio;
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	return pkt;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cino_rd_bio:
5568c2ecf20Sopenharmony_ci	for (i = 0; i < frames; i++) {
5578c2ecf20Sopenharmony_ci		struct bio *bio = pkt->r_bios[i];
5588c2ecf20Sopenharmony_ci		if (bio)
5598c2ecf20Sopenharmony_ci			bio_put(bio);
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_cino_page:
5638c2ecf20Sopenharmony_ci	for (i = 0; i < frames / FRAMES_PER_PAGE; i++)
5648c2ecf20Sopenharmony_ci		if (pkt->pages[i])
5658c2ecf20Sopenharmony_ci			__free_page(pkt->pages[i]);
5668c2ecf20Sopenharmony_ci	bio_put(pkt->w_bio);
5678c2ecf20Sopenharmony_cino_bio:
5688c2ecf20Sopenharmony_ci	kfree(pkt);
5698c2ecf20Sopenharmony_cino_pkt:
5708c2ecf20Sopenharmony_ci	return NULL;
5718c2ecf20Sopenharmony_ci}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci/*
5748c2ecf20Sopenharmony_ci * Free a packet_data struct
5758c2ecf20Sopenharmony_ci */
5768c2ecf20Sopenharmony_cistatic void pkt_free_packet_data(struct packet_data *pkt)
5778c2ecf20Sopenharmony_ci{
5788c2ecf20Sopenharmony_ci	int i;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	for (i = 0; i < pkt->frames; i++) {
5818c2ecf20Sopenharmony_ci		struct bio *bio = pkt->r_bios[i];
5828c2ecf20Sopenharmony_ci		if (bio)
5838c2ecf20Sopenharmony_ci			bio_put(bio);
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci	for (i = 0; i < pkt->frames / FRAMES_PER_PAGE; i++)
5868c2ecf20Sopenharmony_ci		__free_page(pkt->pages[i]);
5878c2ecf20Sopenharmony_ci	bio_put(pkt->w_bio);
5888c2ecf20Sopenharmony_ci	kfree(pkt);
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_cistatic void pkt_shrink_pktlist(struct pktcdvd_device *pd)
5928c2ecf20Sopenharmony_ci{
5938c2ecf20Sopenharmony_ci	struct packet_data *pkt, *next;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	BUG_ON(!list_empty(&pd->cdrw.pkt_active_list));
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_free_list, list) {
5988c2ecf20Sopenharmony_ci		pkt_free_packet_data(pkt);
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_cistatic int pkt_grow_pktlist(struct pktcdvd_device *pd, int nr_packets)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	struct packet_data *pkt;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	BUG_ON(!list_empty(&pd->cdrw.pkt_free_list));
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	while (nr_packets > 0) {
6108c2ecf20Sopenharmony_ci		pkt = pkt_alloc_packet_data(pd->settings.size >> 2);
6118c2ecf20Sopenharmony_ci		if (!pkt) {
6128c2ecf20Sopenharmony_ci			pkt_shrink_pktlist(pd);
6138c2ecf20Sopenharmony_ci			return 0;
6148c2ecf20Sopenharmony_ci		}
6158c2ecf20Sopenharmony_ci		pkt->id = nr_packets;
6168c2ecf20Sopenharmony_ci		pkt->pd = pd;
6178c2ecf20Sopenharmony_ci		list_add(&pkt->list, &pd->cdrw.pkt_free_list);
6188c2ecf20Sopenharmony_ci		nr_packets--;
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci	return 1;
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic inline struct pkt_rb_node *pkt_rbtree_next(struct pkt_rb_node *node)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	struct rb_node *n = rb_next(&node->rb_node);
6268c2ecf20Sopenharmony_ci	if (!n)
6278c2ecf20Sopenharmony_ci		return NULL;
6288c2ecf20Sopenharmony_ci	return rb_entry(n, struct pkt_rb_node, rb_node);
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic void pkt_rbtree_erase(struct pktcdvd_device *pd, struct pkt_rb_node *node)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	rb_erase(&node->rb_node, &pd->bio_queue);
6348c2ecf20Sopenharmony_ci	mempool_free(node, &pd->rb_pool);
6358c2ecf20Sopenharmony_ci	pd->bio_queue_size--;
6368c2ecf20Sopenharmony_ci	BUG_ON(pd->bio_queue_size < 0);
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci/*
6408c2ecf20Sopenharmony_ci * Find the first node in the pd->bio_queue rb tree with a starting sector >= s.
6418c2ecf20Sopenharmony_ci */
6428c2ecf20Sopenharmony_cistatic struct pkt_rb_node *pkt_rbtree_find(struct pktcdvd_device *pd, sector_t s)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	struct rb_node *n = pd->bio_queue.rb_node;
6458c2ecf20Sopenharmony_ci	struct rb_node *next;
6468c2ecf20Sopenharmony_ci	struct pkt_rb_node *tmp;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	if (!n) {
6498c2ecf20Sopenharmony_ci		BUG_ON(pd->bio_queue_size > 0);
6508c2ecf20Sopenharmony_ci		return NULL;
6518c2ecf20Sopenharmony_ci	}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	for (;;) {
6548c2ecf20Sopenharmony_ci		tmp = rb_entry(n, struct pkt_rb_node, rb_node);
6558c2ecf20Sopenharmony_ci		if (s <= tmp->bio->bi_iter.bi_sector)
6568c2ecf20Sopenharmony_ci			next = n->rb_left;
6578c2ecf20Sopenharmony_ci		else
6588c2ecf20Sopenharmony_ci			next = n->rb_right;
6598c2ecf20Sopenharmony_ci		if (!next)
6608c2ecf20Sopenharmony_ci			break;
6618c2ecf20Sopenharmony_ci		n = next;
6628c2ecf20Sopenharmony_ci	}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	if (s > tmp->bio->bi_iter.bi_sector) {
6658c2ecf20Sopenharmony_ci		tmp = pkt_rbtree_next(tmp);
6668c2ecf20Sopenharmony_ci		if (!tmp)
6678c2ecf20Sopenharmony_ci			return NULL;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci	BUG_ON(s > tmp->bio->bi_iter.bi_sector);
6708c2ecf20Sopenharmony_ci	return tmp;
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci/*
6748c2ecf20Sopenharmony_ci * Insert a node into the pd->bio_queue rb tree.
6758c2ecf20Sopenharmony_ci */
6768c2ecf20Sopenharmony_cistatic void pkt_rbtree_insert(struct pktcdvd_device *pd, struct pkt_rb_node *node)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	struct rb_node **p = &pd->bio_queue.rb_node;
6798c2ecf20Sopenharmony_ci	struct rb_node *parent = NULL;
6808c2ecf20Sopenharmony_ci	sector_t s = node->bio->bi_iter.bi_sector;
6818c2ecf20Sopenharmony_ci	struct pkt_rb_node *tmp;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	while (*p) {
6848c2ecf20Sopenharmony_ci		parent = *p;
6858c2ecf20Sopenharmony_ci		tmp = rb_entry(parent, struct pkt_rb_node, rb_node);
6868c2ecf20Sopenharmony_ci		if (s < tmp->bio->bi_iter.bi_sector)
6878c2ecf20Sopenharmony_ci			p = &(*p)->rb_left;
6888c2ecf20Sopenharmony_ci		else
6898c2ecf20Sopenharmony_ci			p = &(*p)->rb_right;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci	rb_link_node(&node->rb_node, parent, p);
6928c2ecf20Sopenharmony_ci	rb_insert_color(&node->rb_node, &pd->bio_queue);
6938c2ecf20Sopenharmony_ci	pd->bio_queue_size++;
6948c2ecf20Sopenharmony_ci}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci/*
6978c2ecf20Sopenharmony_ci * Send a packet_command to the underlying block device and
6988c2ecf20Sopenharmony_ci * wait for completion.
6998c2ecf20Sopenharmony_ci */
7008c2ecf20Sopenharmony_cistatic int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command *cgc)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	struct request_queue *q = bdev_get_queue(pd->bdev);
7038c2ecf20Sopenharmony_ci	struct request *rq;
7048c2ecf20Sopenharmony_ci	int ret = 0;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	rq = blk_get_request(q, (cgc->data_direction == CGC_DATA_WRITE) ?
7078c2ecf20Sopenharmony_ci			     REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN, 0);
7088c2ecf20Sopenharmony_ci	if (IS_ERR(rq))
7098c2ecf20Sopenharmony_ci		return PTR_ERR(rq);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	if (cgc->buflen) {
7128c2ecf20Sopenharmony_ci		ret = blk_rq_map_kern(q, rq, cgc->buffer, cgc->buflen,
7138c2ecf20Sopenharmony_ci				      GFP_NOIO);
7148c2ecf20Sopenharmony_ci		if (ret)
7158c2ecf20Sopenharmony_ci			goto out;
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	scsi_req(rq)->cmd_len = COMMAND_SIZE(cgc->cmd[0]);
7198c2ecf20Sopenharmony_ci	memcpy(scsi_req(rq)->cmd, cgc->cmd, CDROM_PACKET_SIZE);
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	rq->timeout = 60*HZ;
7228c2ecf20Sopenharmony_ci	if (cgc->quiet)
7238c2ecf20Sopenharmony_ci		rq->rq_flags |= RQF_QUIET;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	blk_execute_rq(rq->q, pd->bdev->bd_disk, rq, 0);
7268c2ecf20Sopenharmony_ci	if (scsi_req(rq)->result)
7278c2ecf20Sopenharmony_ci		ret = -EIO;
7288c2ecf20Sopenharmony_ciout:
7298c2ecf20Sopenharmony_ci	blk_put_request(rq);
7308c2ecf20Sopenharmony_ci	return ret;
7318c2ecf20Sopenharmony_ci}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_cistatic const char *sense_key_string(__u8 index)
7348c2ecf20Sopenharmony_ci{
7358c2ecf20Sopenharmony_ci	static const char * const info[] = {
7368c2ecf20Sopenharmony_ci		"No sense", "Recovered error", "Not ready",
7378c2ecf20Sopenharmony_ci		"Medium error", "Hardware error", "Illegal request",
7388c2ecf20Sopenharmony_ci		"Unit attention", "Data protect", "Blank check",
7398c2ecf20Sopenharmony_ci	};
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	return index < ARRAY_SIZE(info) ? info[index] : "INVALID";
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci/*
7458c2ecf20Sopenharmony_ci * A generic sense dump / resolve mechanism should be implemented across
7468c2ecf20Sopenharmony_ci * all ATAPI + SCSI devices.
7478c2ecf20Sopenharmony_ci */
7488c2ecf20Sopenharmony_cistatic void pkt_dump_sense(struct pktcdvd_device *pd,
7498c2ecf20Sopenharmony_ci			   struct packet_command *cgc)
7508c2ecf20Sopenharmony_ci{
7518c2ecf20Sopenharmony_ci	struct scsi_sense_hdr *sshdr = cgc->sshdr;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	if (sshdr)
7548c2ecf20Sopenharmony_ci		pkt_err(pd, "%*ph - sense %02x.%02x.%02x (%s)\n",
7558c2ecf20Sopenharmony_ci			CDROM_PACKET_SIZE, cgc->cmd,
7568c2ecf20Sopenharmony_ci			sshdr->sense_key, sshdr->asc, sshdr->ascq,
7578c2ecf20Sopenharmony_ci			sense_key_string(sshdr->sense_key));
7588c2ecf20Sopenharmony_ci	else
7598c2ecf20Sopenharmony_ci		pkt_err(pd, "%*ph - no sense\n", CDROM_PACKET_SIZE, cgc->cmd);
7608c2ecf20Sopenharmony_ci}
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci/*
7638c2ecf20Sopenharmony_ci * flush the drive cache to media
7648c2ecf20Sopenharmony_ci */
7658c2ecf20Sopenharmony_cistatic int pkt_flush_cache(struct pktcdvd_device *pd)
7668c2ecf20Sopenharmony_ci{
7678c2ecf20Sopenharmony_ci	struct packet_command cgc;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
7708c2ecf20Sopenharmony_ci	cgc.cmd[0] = GPCMD_FLUSH_CACHE;
7718c2ecf20Sopenharmony_ci	cgc.quiet = 1;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	/*
7748c2ecf20Sopenharmony_ci	 * the IMMED bit -- we default to not setting it, although that
7758c2ecf20Sopenharmony_ci	 * would allow a much faster close, this is safer
7768c2ecf20Sopenharmony_ci	 */
7778c2ecf20Sopenharmony_ci#if 0
7788c2ecf20Sopenharmony_ci	cgc.cmd[1] = 1 << 1;
7798c2ecf20Sopenharmony_ci#endif
7808c2ecf20Sopenharmony_ci	return pkt_generic_packet(pd, &cgc);
7818c2ecf20Sopenharmony_ci}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci/*
7848c2ecf20Sopenharmony_ci * speed is given as the normal factor, e.g. 4 for 4x
7858c2ecf20Sopenharmony_ci */
7868c2ecf20Sopenharmony_cistatic noinline_for_stack int pkt_set_speed(struct pktcdvd_device *pd,
7878c2ecf20Sopenharmony_ci				unsigned write_speed, unsigned read_speed)
7888c2ecf20Sopenharmony_ci{
7898c2ecf20Sopenharmony_ci	struct packet_command cgc;
7908c2ecf20Sopenharmony_ci	struct scsi_sense_hdr sshdr;
7918c2ecf20Sopenharmony_ci	int ret;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
7948c2ecf20Sopenharmony_ci	cgc.sshdr = &sshdr;
7958c2ecf20Sopenharmony_ci	cgc.cmd[0] = GPCMD_SET_SPEED;
7968c2ecf20Sopenharmony_ci	cgc.cmd[2] = (read_speed >> 8) & 0xff;
7978c2ecf20Sopenharmony_ci	cgc.cmd[3] = read_speed & 0xff;
7988c2ecf20Sopenharmony_ci	cgc.cmd[4] = (write_speed >> 8) & 0xff;
7998c2ecf20Sopenharmony_ci	cgc.cmd[5] = write_speed & 0xff;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	ret = pkt_generic_packet(pd, &cgc);
8028c2ecf20Sopenharmony_ci	if (ret)
8038c2ecf20Sopenharmony_ci		pkt_dump_sense(pd, &cgc);
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	return ret;
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci/*
8098c2ecf20Sopenharmony_ci * Queue a bio for processing by the low-level CD device. Must be called
8108c2ecf20Sopenharmony_ci * from process context.
8118c2ecf20Sopenharmony_ci */
8128c2ecf20Sopenharmony_cistatic void pkt_queue_bio(struct pktcdvd_device *pd, struct bio *bio)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	spin_lock(&pd->iosched.lock);
8158c2ecf20Sopenharmony_ci	if (bio_data_dir(bio) == READ)
8168c2ecf20Sopenharmony_ci		bio_list_add(&pd->iosched.read_queue, bio);
8178c2ecf20Sopenharmony_ci	else
8188c2ecf20Sopenharmony_ci		bio_list_add(&pd->iosched.write_queue, bio);
8198c2ecf20Sopenharmony_ci	spin_unlock(&pd->iosched.lock);
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	atomic_set(&pd->iosched.attention, 1);
8228c2ecf20Sopenharmony_ci	wake_up(&pd->wqueue);
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci/*
8268c2ecf20Sopenharmony_ci * Process the queued read/write requests. This function handles special
8278c2ecf20Sopenharmony_ci * requirements for CDRW drives:
8288c2ecf20Sopenharmony_ci * - A cache flush command must be inserted before a read request if the
8298c2ecf20Sopenharmony_ci *   previous request was a write.
8308c2ecf20Sopenharmony_ci * - Switching between reading and writing is slow, so don't do it more often
8318c2ecf20Sopenharmony_ci *   than necessary.
8328c2ecf20Sopenharmony_ci * - Optimize for throughput at the expense of latency. This means that streaming
8338c2ecf20Sopenharmony_ci *   writes will never be interrupted by a read, but if the drive has to seek
8348c2ecf20Sopenharmony_ci *   before the next write, switch to reading instead if there are any pending
8358c2ecf20Sopenharmony_ci *   read requests.
8368c2ecf20Sopenharmony_ci * - Set the read speed according to current usage pattern. When only reading
8378c2ecf20Sopenharmony_ci *   from the device, it's best to use the highest possible read speed, but
8388c2ecf20Sopenharmony_ci *   when switching often between reading and writing, it's better to have the
8398c2ecf20Sopenharmony_ci *   same read and write speeds.
8408c2ecf20Sopenharmony_ci */
8418c2ecf20Sopenharmony_cistatic void pkt_iosched_process_queue(struct pktcdvd_device *pd)
8428c2ecf20Sopenharmony_ci{
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	if (atomic_read(&pd->iosched.attention) == 0)
8458c2ecf20Sopenharmony_ci		return;
8468c2ecf20Sopenharmony_ci	atomic_set(&pd->iosched.attention, 0);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	for (;;) {
8498c2ecf20Sopenharmony_ci		struct bio *bio;
8508c2ecf20Sopenharmony_ci		int reads_queued, writes_queued;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci		spin_lock(&pd->iosched.lock);
8538c2ecf20Sopenharmony_ci		reads_queued = !bio_list_empty(&pd->iosched.read_queue);
8548c2ecf20Sopenharmony_ci		writes_queued = !bio_list_empty(&pd->iosched.write_queue);
8558c2ecf20Sopenharmony_ci		spin_unlock(&pd->iosched.lock);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci		if (!reads_queued && !writes_queued)
8588c2ecf20Sopenharmony_ci			break;
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci		if (pd->iosched.writing) {
8618c2ecf20Sopenharmony_ci			int need_write_seek = 1;
8628c2ecf20Sopenharmony_ci			spin_lock(&pd->iosched.lock);
8638c2ecf20Sopenharmony_ci			bio = bio_list_peek(&pd->iosched.write_queue);
8648c2ecf20Sopenharmony_ci			spin_unlock(&pd->iosched.lock);
8658c2ecf20Sopenharmony_ci			if (bio && (bio->bi_iter.bi_sector ==
8668c2ecf20Sopenharmony_ci				    pd->iosched.last_write))
8678c2ecf20Sopenharmony_ci				need_write_seek = 0;
8688c2ecf20Sopenharmony_ci			if (need_write_seek && reads_queued) {
8698c2ecf20Sopenharmony_ci				if (atomic_read(&pd->cdrw.pending_bios) > 0) {
8708c2ecf20Sopenharmony_ci					pkt_dbg(2, pd, "write, waiting\n");
8718c2ecf20Sopenharmony_ci					break;
8728c2ecf20Sopenharmony_ci				}
8738c2ecf20Sopenharmony_ci				pkt_flush_cache(pd);
8748c2ecf20Sopenharmony_ci				pd->iosched.writing = 0;
8758c2ecf20Sopenharmony_ci			}
8768c2ecf20Sopenharmony_ci		} else {
8778c2ecf20Sopenharmony_ci			if (!reads_queued && writes_queued) {
8788c2ecf20Sopenharmony_ci				if (atomic_read(&pd->cdrw.pending_bios) > 0) {
8798c2ecf20Sopenharmony_ci					pkt_dbg(2, pd, "read, waiting\n");
8808c2ecf20Sopenharmony_ci					break;
8818c2ecf20Sopenharmony_ci				}
8828c2ecf20Sopenharmony_ci				pd->iosched.writing = 1;
8838c2ecf20Sopenharmony_ci			}
8848c2ecf20Sopenharmony_ci		}
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci		spin_lock(&pd->iosched.lock);
8878c2ecf20Sopenharmony_ci		if (pd->iosched.writing)
8888c2ecf20Sopenharmony_ci			bio = bio_list_pop(&pd->iosched.write_queue);
8898c2ecf20Sopenharmony_ci		else
8908c2ecf20Sopenharmony_ci			bio = bio_list_pop(&pd->iosched.read_queue);
8918c2ecf20Sopenharmony_ci		spin_unlock(&pd->iosched.lock);
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci		if (!bio)
8948c2ecf20Sopenharmony_ci			continue;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci		if (bio_data_dir(bio) == READ)
8978c2ecf20Sopenharmony_ci			pd->iosched.successive_reads +=
8988c2ecf20Sopenharmony_ci				bio->bi_iter.bi_size >> 10;
8998c2ecf20Sopenharmony_ci		else {
9008c2ecf20Sopenharmony_ci			pd->iosched.successive_reads = 0;
9018c2ecf20Sopenharmony_ci			pd->iosched.last_write = bio_end_sector(bio);
9028c2ecf20Sopenharmony_ci		}
9038c2ecf20Sopenharmony_ci		if (pd->iosched.successive_reads >= HI_SPEED_SWITCH) {
9048c2ecf20Sopenharmony_ci			if (pd->read_speed == pd->write_speed) {
9058c2ecf20Sopenharmony_ci				pd->read_speed = MAX_SPEED;
9068c2ecf20Sopenharmony_ci				pkt_set_speed(pd, pd->write_speed, pd->read_speed);
9078c2ecf20Sopenharmony_ci			}
9088c2ecf20Sopenharmony_ci		} else {
9098c2ecf20Sopenharmony_ci			if (pd->read_speed != pd->write_speed) {
9108c2ecf20Sopenharmony_ci				pd->read_speed = pd->write_speed;
9118c2ecf20Sopenharmony_ci				pkt_set_speed(pd, pd->write_speed, pd->read_speed);
9128c2ecf20Sopenharmony_ci			}
9138c2ecf20Sopenharmony_ci		}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci		atomic_inc(&pd->cdrw.pending_bios);
9168c2ecf20Sopenharmony_ci		submit_bio_noacct(bio);
9178c2ecf20Sopenharmony_ci	}
9188c2ecf20Sopenharmony_ci}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci/*
9218c2ecf20Sopenharmony_ci * Special care is needed if the underlying block device has a small
9228c2ecf20Sopenharmony_ci * max_phys_segments value.
9238c2ecf20Sopenharmony_ci */
9248c2ecf20Sopenharmony_cistatic int pkt_set_segment_merging(struct pktcdvd_device *pd, struct request_queue *q)
9258c2ecf20Sopenharmony_ci{
9268c2ecf20Sopenharmony_ci	if ((pd->settings.size << 9) / CD_FRAMESIZE
9278c2ecf20Sopenharmony_ci	    <= queue_max_segments(q)) {
9288c2ecf20Sopenharmony_ci		/*
9298c2ecf20Sopenharmony_ci		 * The cdrom device can handle one segment/frame
9308c2ecf20Sopenharmony_ci		 */
9318c2ecf20Sopenharmony_ci		clear_bit(PACKET_MERGE_SEGS, &pd->flags);
9328c2ecf20Sopenharmony_ci		return 0;
9338c2ecf20Sopenharmony_ci	} else if ((pd->settings.size << 9) / PAGE_SIZE
9348c2ecf20Sopenharmony_ci		   <= queue_max_segments(q)) {
9358c2ecf20Sopenharmony_ci		/*
9368c2ecf20Sopenharmony_ci		 * We can handle this case at the expense of some extra memory
9378c2ecf20Sopenharmony_ci		 * copies during write operations
9388c2ecf20Sopenharmony_ci		 */
9398c2ecf20Sopenharmony_ci		set_bit(PACKET_MERGE_SEGS, &pd->flags);
9408c2ecf20Sopenharmony_ci		return 0;
9418c2ecf20Sopenharmony_ci	} else {
9428c2ecf20Sopenharmony_ci		pkt_err(pd, "cdrom max_phys_segments too small\n");
9438c2ecf20Sopenharmony_ci		return -EIO;
9448c2ecf20Sopenharmony_ci	}
9458c2ecf20Sopenharmony_ci}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_cistatic void pkt_end_io_read(struct bio *bio)
9488c2ecf20Sopenharmony_ci{
9498c2ecf20Sopenharmony_ci	struct packet_data *pkt = bio->bi_private;
9508c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd = pkt->pd;
9518c2ecf20Sopenharmony_ci	BUG_ON(!pd);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	pkt_dbg(2, pd, "bio=%p sec0=%llx sec=%llx err=%d\n",
9548c2ecf20Sopenharmony_ci		bio, (unsigned long long)pkt->sector,
9558c2ecf20Sopenharmony_ci		(unsigned long long)bio->bi_iter.bi_sector, bio->bi_status);
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	if (bio->bi_status)
9588c2ecf20Sopenharmony_ci		atomic_inc(&pkt->io_errors);
9598c2ecf20Sopenharmony_ci	if (atomic_dec_and_test(&pkt->io_wait)) {
9608c2ecf20Sopenharmony_ci		atomic_inc(&pkt->run_sm);
9618c2ecf20Sopenharmony_ci		wake_up(&pd->wqueue);
9628c2ecf20Sopenharmony_ci	}
9638c2ecf20Sopenharmony_ci	pkt_bio_finished(pd);
9648c2ecf20Sopenharmony_ci}
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_cistatic void pkt_end_io_packet_write(struct bio *bio)
9678c2ecf20Sopenharmony_ci{
9688c2ecf20Sopenharmony_ci	struct packet_data *pkt = bio->bi_private;
9698c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd = pkt->pd;
9708c2ecf20Sopenharmony_ci	BUG_ON(!pd);
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	pkt_dbg(2, pd, "id=%d, err=%d\n", pkt->id, bio->bi_status);
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci	pd->stats.pkt_ended++;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	pkt_bio_finished(pd);
9778c2ecf20Sopenharmony_ci	atomic_dec(&pkt->io_wait);
9788c2ecf20Sopenharmony_ci	atomic_inc(&pkt->run_sm);
9798c2ecf20Sopenharmony_ci	wake_up(&pd->wqueue);
9808c2ecf20Sopenharmony_ci}
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci/*
9838c2ecf20Sopenharmony_ci * Schedule reads for the holes in a packet
9848c2ecf20Sopenharmony_ci */
9858c2ecf20Sopenharmony_cistatic void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt)
9868c2ecf20Sopenharmony_ci{
9878c2ecf20Sopenharmony_ci	int frames_read = 0;
9888c2ecf20Sopenharmony_ci	struct bio *bio;
9898c2ecf20Sopenharmony_ci	int f;
9908c2ecf20Sopenharmony_ci	char written[PACKET_MAX_SIZE];
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	BUG_ON(bio_list_empty(&pkt->orig_bios));
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	atomic_set(&pkt->io_wait, 0);
9958c2ecf20Sopenharmony_ci	atomic_set(&pkt->io_errors, 0);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	/*
9988c2ecf20Sopenharmony_ci	 * Figure out which frames we need to read before we can write.
9998c2ecf20Sopenharmony_ci	 */
10008c2ecf20Sopenharmony_ci	memset(written, 0, sizeof(written));
10018c2ecf20Sopenharmony_ci	spin_lock(&pkt->lock);
10028c2ecf20Sopenharmony_ci	bio_list_for_each(bio, &pkt->orig_bios) {
10038c2ecf20Sopenharmony_ci		int first_frame = (bio->bi_iter.bi_sector - pkt->sector) /
10048c2ecf20Sopenharmony_ci			(CD_FRAMESIZE >> 9);
10058c2ecf20Sopenharmony_ci		int num_frames = bio->bi_iter.bi_size / CD_FRAMESIZE;
10068c2ecf20Sopenharmony_ci		pd->stats.secs_w += num_frames * (CD_FRAMESIZE >> 9);
10078c2ecf20Sopenharmony_ci		BUG_ON(first_frame < 0);
10088c2ecf20Sopenharmony_ci		BUG_ON(first_frame + num_frames > pkt->frames);
10098c2ecf20Sopenharmony_ci		for (f = first_frame; f < first_frame + num_frames; f++)
10108c2ecf20Sopenharmony_ci			written[f] = 1;
10118c2ecf20Sopenharmony_ci	}
10128c2ecf20Sopenharmony_ci	spin_unlock(&pkt->lock);
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	if (pkt->cache_valid) {
10158c2ecf20Sopenharmony_ci		pkt_dbg(2, pd, "zone %llx cached\n",
10168c2ecf20Sopenharmony_ci			(unsigned long long)pkt->sector);
10178c2ecf20Sopenharmony_ci		goto out_account;
10188c2ecf20Sopenharmony_ci	}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	/*
10218c2ecf20Sopenharmony_ci	 * Schedule reads for missing parts of the packet.
10228c2ecf20Sopenharmony_ci	 */
10238c2ecf20Sopenharmony_ci	for (f = 0; f < pkt->frames; f++) {
10248c2ecf20Sopenharmony_ci		int p, offset;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci		if (written[f])
10278c2ecf20Sopenharmony_ci			continue;
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci		bio = pkt->r_bios[f];
10308c2ecf20Sopenharmony_ci		bio_reset(bio);
10318c2ecf20Sopenharmony_ci		bio->bi_iter.bi_sector = pkt->sector + f * (CD_FRAMESIZE >> 9);
10328c2ecf20Sopenharmony_ci		bio_set_dev(bio, pd->bdev);
10338c2ecf20Sopenharmony_ci		bio->bi_end_io = pkt_end_io_read;
10348c2ecf20Sopenharmony_ci		bio->bi_private = pkt;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci		p = (f * CD_FRAMESIZE) / PAGE_SIZE;
10378c2ecf20Sopenharmony_ci		offset = (f * CD_FRAMESIZE) % PAGE_SIZE;
10388c2ecf20Sopenharmony_ci		pkt_dbg(2, pd, "Adding frame %d, page:%p offs:%d\n",
10398c2ecf20Sopenharmony_ci			f, pkt->pages[p], offset);
10408c2ecf20Sopenharmony_ci		if (!bio_add_page(bio, pkt->pages[p], CD_FRAMESIZE, offset))
10418c2ecf20Sopenharmony_ci			BUG();
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci		atomic_inc(&pkt->io_wait);
10448c2ecf20Sopenharmony_ci		bio_set_op_attrs(bio, REQ_OP_READ, 0);
10458c2ecf20Sopenharmony_ci		pkt_queue_bio(pd, bio);
10468c2ecf20Sopenharmony_ci		frames_read++;
10478c2ecf20Sopenharmony_ci	}
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ciout_account:
10508c2ecf20Sopenharmony_ci	pkt_dbg(2, pd, "need %d frames for zone %llx\n",
10518c2ecf20Sopenharmony_ci		frames_read, (unsigned long long)pkt->sector);
10528c2ecf20Sopenharmony_ci	pd->stats.pkt_started++;
10538c2ecf20Sopenharmony_ci	pd->stats.secs_rg += frames_read * (CD_FRAMESIZE >> 9);
10548c2ecf20Sopenharmony_ci}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci/*
10578c2ecf20Sopenharmony_ci * Find a packet matching zone, or the least recently used packet if
10588c2ecf20Sopenharmony_ci * there is no match.
10598c2ecf20Sopenharmony_ci */
10608c2ecf20Sopenharmony_cistatic struct packet_data *pkt_get_packet_data(struct pktcdvd_device *pd, int zone)
10618c2ecf20Sopenharmony_ci{
10628c2ecf20Sopenharmony_ci	struct packet_data *pkt;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	list_for_each_entry(pkt, &pd->cdrw.pkt_free_list, list) {
10658c2ecf20Sopenharmony_ci		if (pkt->sector == zone || pkt->list.next == &pd->cdrw.pkt_free_list) {
10668c2ecf20Sopenharmony_ci			list_del_init(&pkt->list);
10678c2ecf20Sopenharmony_ci			if (pkt->sector != zone)
10688c2ecf20Sopenharmony_ci				pkt->cache_valid = 0;
10698c2ecf20Sopenharmony_ci			return pkt;
10708c2ecf20Sopenharmony_ci		}
10718c2ecf20Sopenharmony_ci	}
10728c2ecf20Sopenharmony_ci	BUG();
10738c2ecf20Sopenharmony_ci	return NULL;
10748c2ecf20Sopenharmony_ci}
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_cistatic void pkt_put_packet_data(struct pktcdvd_device *pd, struct packet_data *pkt)
10778c2ecf20Sopenharmony_ci{
10788c2ecf20Sopenharmony_ci	if (pkt->cache_valid) {
10798c2ecf20Sopenharmony_ci		list_add(&pkt->list, &pd->cdrw.pkt_free_list);
10808c2ecf20Sopenharmony_ci	} else {
10818c2ecf20Sopenharmony_ci		list_add_tail(&pkt->list, &pd->cdrw.pkt_free_list);
10828c2ecf20Sopenharmony_ci	}
10838c2ecf20Sopenharmony_ci}
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_cistatic inline void pkt_set_state(struct packet_data *pkt, enum packet_data_state state)
10868c2ecf20Sopenharmony_ci{
10878c2ecf20Sopenharmony_ci#if PACKET_DEBUG > 1
10888c2ecf20Sopenharmony_ci	static const char *state_name[] = {
10898c2ecf20Sopenharmony_ci		"IDLE", "WAITING", "READ_WAIT", "WRITE_WAIT", "RECOVERY", "FINISHED"
10908c2ecf20Sopenharmony_ci	};
10918c2ecf20Sopenharmony_ci	enum packet_data_state old_state = pkt->state;
10928c2ecf20Sopenharmony_ci	pkt_dbg(2, pd, "pkt %2d : s=%6llx %s -> %s\n",
10938c2ecf20Sopenharmony_ci		pkt->id, (unsigned long long)pkt->sector,
10948c2ecf20Sopenharmony_ci		state_name[old_state], state_name[state]);
10958c2ecf20Sopenharmony_ci#endif
10968c2ecf20Sopenharmony_ci	pkt->state = state;
10978c2ecf20Sopenharmony_ci}
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci/*
11008c2ecf20Sopenharmony_ci * Scan the work queue to see if we can start a new packet.
11018c2ecf20Sopenharmony_ci * returns non-zero if any work was done.
11028c2ecf20Sopenharmony_ci */
11038c2ecf20Sopenharmony_cistatic int pkt_handle_queue(struct pktcdvd_device *pd)
11048c2ecf20Sopenharmony_ci{
11058c2ecf20Sopenharmony_ci	struct packet_data *pkt, *p;
11068c2ecf20Sopenharmony_ci	struct bio *bio = NULL;
11078c2ecf20Sopenharmony_ci	sector_t zone = 0; /* Suppress gcc warning */
11088c2ecf20Sopenharmony_ci	struct pkt_rb_node *node, *first_node;
11098c2ecf20Sopenharmony_ci	struct rb_node *n;
11108c2ecf20Sopenharmony_ci	int wakeup;
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	atomic_set(&pd->scan_queue, 0);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	if (list_empty(&pd->cdrw.pkt_free_list)) {
11158c2ecf20Sopenharmony_ci		pkt_dbg(2, pd, "no pkt\n");
11168c2ecf20Sopenharmony_ci		return 0;
11178c2ecf20Sopenharmony_ci	}
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	/*
11208c2ecf20Sopenharmony_ci	 * Try to find a zone we are not already working on.
11218c2ecf20Sopenharmony_ci	 */
11228c2ecf20Sopenharmony_ci	spin_lock(&pd->lock);
11238c2ecf20Sopenharmony_ci	first_node = pkt_rbtree_find(pd, pd->current_sector);
11248c2ecf20Sopenharmony_ci	if (!first_node) {
11258c2ecf20Sopenharmony_ci		n = rb_first(&pd->bio_queue);
11268c2ecf20Sopenharmony_ci		if (n)
11278c2ecf20Sopenharmony_ci			first_node = rb_entry(n, struct pkt_rb_node, rb_node);
11288c2ecf20Sopenharmony_ci	}
11298c2ecf20Sopenharmony_ci	node = first_node;
11308c2ecf20Sopenharmony_ci	while (node) {
11318c2ecf20Sopenharmony_ci		bio = node->bio;
11328c2ecf20Sopenharmony_ci		zone = get_zone(bio->bi_iter.bi_sector, pd);
11338c2ecf20Sopenharmony_ci		list_for_each_entry(p, &pd->cdrw.pkt_active_list, list) {
11348c2ecf20Sopenharmony_ci			if (p->sector == zone) {
11358c2ecf20Sopenharmony_ci				bio = NULL;
11368c2ecf20Sopenharmony_ci				goto try_next_bio;
11378c2ecf20Sopenharmony_ci			}
11388c2ecf20Sopenharmony_ci		}
11398c2ecf20Sopenharmony_ci		break;
11408c2ecf20Sopenharmony_citry_next_bio:
11418c2ecf20Sopenharmony_ci		node = pkt_rbtree_next(node);
11428c2ecf20Sopenharmony_ci		if (!node) {
11438c2ecf20Sopenharmony_ci			n = rb_first(&pd->bio_queue);
11448c2ecf20Sopenharmony_ci			if (n)
11458c2ecf20Sopenharmony_ci				node = rb_entry(n, struct pkt_rb_node, rb_node);
11468c2ecf20Sopenharmony_ci		}
11478c2ecf20Sopenharmony_ci		if (node == first_node)
11488c2ecf20Sopenharmony_ci			node = NULL;
11498c2ecf20Sopenharmony_ci	}
11508c2ecf20Sopenharmony_ci	spin_unlock(&pd->lock);
11518c2ecf20Sopenharmony_ci	if (!bio) {
11528c2ecf20Sopenharmony_ci		pkt_dbg(2, pd, "no bio\n");
11538c2ecf20Sopenharmony_ci		return 0;
11548c2ecf20Sopenharmony_ci	}
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	pkt = pkt_get_packet_data(pd, zone);
11578c2ecf20Sopenharmony_ci
11588c2ecf20Sopenharmony_ci	pd->current_sector = zone + pd->settings.size;
11598c2ecf20Sopenharmony_ci	pkt->sector = zone;
11608c2ecf20Sopenharmony_ci	BUG_ON(pkt->frames != pd->settings.size >> 2);
11618c2ecf20Sopenharmony_ci	pkt->write_size = 0;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	/*
11648c2ecf20Sopenharmony_ci	 * Scan work queue for bios in the same zone and link them
11658c2ecf20Sopenharmony_ci	 * to this packet.
11668c2ecf20Sopenharmony_ci	 */
11678c2ecf20Sopenharmony_ci	spin_lock(&pd->lock);
11688c2ecf20Sopenharmony_ci	pkt_dbg(2, pd, "looking for zone %llx\n", (unsigned long long)zone);
11698c2ecf20Sopenharmony_ci	while ((node = pkt_rbtree_find(pd, zone)) != NULL) {
11708c2ecf20Sopenharmony_ci		bio = node->bio;
11718c2ecf20Sopenharmony_ci		pkt_dbg(2, pd, "found zone=%llx\n", (unsigned long long)
11728c2ecf20Sopenharmony_ci			get_zone(bio->bi_iter.bi_sector, pd));
11738c2ecf20Sopenharmony_ci		if (get_zone(bio->bi_iter.bi_sector, pd) != zone)
11748c2ecf20Sopenharmony_ci			break;
11758c2ecf20Sopenharmony_ci		pkt_rbtree_erase(pd, node);
11768c2ecf20Sopenharmony_ci		spin_lock(&pkt->lock);
11778c2ecf20Sopenharmony_ci		bio_list_add(&pkt->orig_bios, bio);
11788c2ecf20Sopenharmony_ci		pkt->write_size += bio->bi_iter.bi_size / CD_FRAMESIZE;
11798c2ecf20Sopenharmony_ci		spin_unlock(&pkt->lock);
11808c2ecf20Sopenharmony_ci	}
11818c2ecf20Sopenharmony_ci	/* check write congestion marks, and if bio_queue_size is
11828c2ecf20Sopenharmony_ci	   below, wake up any waiters */
11838c2ecf20Sopenharmony_ci	wakeup = (pd->write_congestion_on > 0
11848c2ecf20Sopenharmony_ci	 		&& pd->bio_queue_size <= pd->write_congestion_off);
11858c2ecf20Sopenharmony_ci	spin_unlock(&pd->lock);
11868c2ecf20Sopenharmony_ci	if (wakeup) {
11878c2ecf20Sopenharmony_ci		clear_bdi_congested(pd->disk->queue->backing_dev_info,
11888c2ecf20Sopenharmony_ci					BLK_RW_ASYNC);
11898c2ecf20Sopenharmony_ci	}
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	pkt->sleep_time = max(PACKET_WAIT_TIME, 1);
11928c2ecf20Sopenharmony_ci	pkt_set_state(pkt, PACKET_WAITING_STATE);
11938c2ecf20Sopenharmony_ci	atomic_set(&pkt->run_sm, 1);
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	spin_lock(&pd->cdrw.active_list_lock);
11968c2ecf20Sopenharmony_ci	list_add(&pkt->list, &pd->cdrw.pkt_active_list);
11978c2ecf20Sopenharmony_ci	spin_unlock(&pd->cdrw.active_list_lock);
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	return 1;
12008c2ecf20Sopenharmony_ci}
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci/*
12038c2ecf20Sopenharmony_ci * Assemble a bio to write one packet and queue the bio for processing
12048c2ecf20Sopenharmony_ci * by the underlying block device.
12058c2ecf20Sopenharmony_ci */
12068c2ecf20Sopenharmony_cistatic void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt)
12078c2ecf20Sopenharmony_ci{
12088c2ecf20Sopenharmony_ci	int f;
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	bio_reset(pkt->w_bio);
12118c2ecf20Sopenharmony_ci	pkt->w_bio->bi_iter.bi_sector = pkt->sector;
12128c2ecf20Sopenharmony_ci	bio_set_dev(pkt->w_bio, pd->bdev);
12138c2ecf20Sopenharmony_ci	pkt->w_bio->bi_end_io = pkt_end_io_packet_write;
12148c2ecf20Sopenharmony_ci	pkt->w_bio->bi_private = pkt;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	/* XXX: locking? */
12178c2ecf20Sopenharmony_ci	for (f = 0; f < pkt->frames; f++) {
12188c2ecf20Sopenharmony_ci		struct page *page = pkt->pages[(f * CD_FRAMESIZE) / PAGE_SIZE];
12198c2ecf20Sopenharmony_ci		unsigned offset = (f * CD_FRAMESIZE) % PAGE_SIZE;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci		if (!bio_add_page(pkt->w_bio, page, CD_FRAMESIZE, offset))
12228c2ecf20Sopenharmony_ci			BUG();
12238c2ecf20Sopenharmony_ci	}
12248c2ecf20Sopenharmony_ci	pkt_dbg(2, pd, "vcnt=%d\n", pkt->w_bio->bi_vcnt);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	/*
12278c2ecf20Sopenharmony_ci	 * Fill-in bvec with data from orig_bios.
12288c2ecf20Sopenharmony_ci	 */
12298c2ecf20Sopenharmony_ci	spin_lock(&pkt->lock);
12308c2ecf20Sopenharmony_ci	bio_list_copy_data(pkt->w_bio, pkt->orig_bios.head);
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	pkt_set_state(pkt, PACKET_WRITE_WAIT_STATE);
12338c2ecf20Sopenharmony_ci	spin_unlock(&pkt->lock);
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	pkt_dbg(2, pd, "Writing %d frames for zone %llx\n",
12368c2ecf20Sopenharmony_ci		pkt->write_size, (unsigned long long)pkt->sector);
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	if (test_bit(PACKET_MERGE_SEGS, &pd->flags) || (pkt->write_size < pkt->frames))
12398c2ecf20Sopenharmony_ci		pkt->cache_valid = 1;
12408c2ecf20Sopenharmony_ci	else
12418c2ecf20Sopenharmony_ci		pkt->cache_valid = 0;
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	/* Start the write request */
12448c2ecf20Sopenharmony_ci	atomic_set(&pkt->io_wait, 1);
12458c2ecf20Sopenharmony_ci	bio_set_op_attrs(pkt->w_bio, REQ_OP_WRITE, 0);
12468c2ecf20Sopenharmony_ci	pkt_queue_bio(pd, pkt->w_bio);
12478c2ecf20Sopenharmony_ci}
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_cistatic void pkt_finish_packet(struct packet_data *pkt, blk_status_t status)
12508c2ecf20Sopenharmony_ci{
12518c2ecf20Sopenharmony_ci	struct bio *bio;
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	if (status)
12548c2ecf20Sopenharmony_ci		pkt->cache_valid = 0;
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	/* Finish all bios corresponding to this packet */
12578c2ecf20Sopenharmony_ci	while ((bio = bio_list_pop(&pkt->orig_bios))) {
12588c2ecf20Sopenharmony_ci		bio->bi_status = status;
12598c2ecf20Sopenharmony_ci		bio_endio(bio);
12608c2ecf20Sopenharmony_ci	}
12618c2ecf20Sopenharmony_ci}
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_cistatic void pkt_run_state_machine(struct pktcdvd_device *pd, struct packet_data *pkt)
12648c2ecf20Sopenharmony_ci{
12658c2ecf20Sopenharmony_ci	pkt_dbg(2, pd, "pkt %d\n", pkt->id);
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	for (;;) {
12688c2ecf20Sopenharmony_ci		switch (pkt->state) {
12698c2ecf20Sopenharmony_ci		case PACKET_WAITING_STATE:
12708c2ecf20Sopenharmony_ci			if ((pkt->write_size < pkt->frames) && (pkt->sleep_time > 0))
12718c2ecf20Sopenharmony_ci				return;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci			pkt->sleep_time = 0;
12748c2ecf20Sopenharmony_ci			pkt_gather_data(pd, pkt);
12758c2ecf20Sopenharmony_ci			pkt_set_state(pkt, PACKET_READ_WAIT_STATE);
12768c2ecf20Sopenharmony_ci			break;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci		case PACKET_READ_WAIT_STATE:
12798c2ecf20Sopenharmony_ci			if (atomic_read(&pkt->io_wait) > 0)
12808c2ecf20Sopenharmony_ci				return;
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci			if (atomic_read(&pkt->io_errors) > 0) {
12838c2ecf20Sopenharmony_ci				pkt_set_state(pkt, PACKET_RECOVERY_STATE);
12848c2ecf20Sopenharmony_ci			} else {
12858c2ecf20Sopenharmony_ci				pkt_start_write(pd, pkt);
12868c2ecf20Sopenharmony_ci			}
12878c2ecf20Sopenharmony_ci			break;
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci		case PACKET_WRITE_WAIT_STATE:
12908c2ecf20Sopenharmony_ci			if (atomic_read(&pkt->io_wait) > 0)
12918c2ecf20Sopenharmony_ci				return;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci			if (!pkt->w_bio->bi_status) {
12948c2ecf20Sopenharmony_ci				pkt_set_state(pkt, PACKET_FINISHED_STATE);
12958c2ecf20Sopenharmony_ci			} else {
12968c2ecf20Sopenharmony_ci				pkt_set_state(pkt, PACKET_RECOVERY_STATE);
12978c2ecf20Sopenharmony_ci			}
12988c2ecf20Sopenharmony_ci			break;
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci		case PACKET_RECOVERY_STATE:
13018c2ecf20Sopenharmony_ci			pkt_dbg(2, pd, "No recovery possible\n");
13028c2ecf20Sopenharmony_ci			pkt_set_state(pkt, PACKET_FINISHED_STATE);
13038c2ecf20Sopenharmony_ci			break;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci		case PACKET_FINISHED_STATE:
13068c2ecf20Sopenharmony_ci			pkt_finish_packet(pkt, pkt->w_bio->bi_status);
13078c2ecf20Sopenharmony_ci			return;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci		default:
13108c2ecf20Sopenharmony_ci			BUG();
13118c2ecf20Sopenharmony_ci			break;
13128c2ecf20Sopenharmony_ci		}
13138c2ecf20Sopenharmony_ci	}
13148c2ecf20Sopenharmony_ci}
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_cistatic void pkt_handle_packets(struct pktcdvd_device *pd)
13178c2ecf20Sopenharmony_ci{
13188c2ecf20Sopenharmony_ci	struct packet_data *pkt, *next;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	/*
13218c2ecf20Sopenharmony_ci	 * Run state machine for active packets
13228c2ecf20Sopenharmony_ci	 */
13238c2ecf20Sopenharmony_ci	list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
13248c2ecf20Sopenharmony_ci		if (atomic_read(&pkt->run_sm) > 0) {
13258c2ecf20Sopenharmony_ci			atomic_set(&pkt->run_sm, 0);
13268c2ecf20Sopenharmony_ci			pkt_run_state_machine(pd, pkt);
13278c2ecf20Sopenharmony_ci		}
13288c2ecf20Sopenharmony_ci	}
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	/*
13318c2ecf20Sopenharmony_ci	 * Move no longer active packets to the free list
13328c2ecf20Sopenharmony_ci	 */
13338c2ecf20Sopenharmony_ci	spin_lock(&pd->cdrw.active_list_lock);
13348c2ecf20Sopenharmony_ci	list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_active_list, list) {
13358c2ecf20Sopenharmony_ci		if (pkt->state == PACKET_FINISHED_STATE) {
13368c2ecf20Sopenharmony_ci			list_del(&pkt->list);
13378c2ecf20Sopenharmony_ci			pkt_put_packet_data(pd, pkt);
13388c2ecf20Sopenharmony_ci			pkt_set_state(pkt, PACKET_IDLE_STATE);
13398c2ecf20Sopenharmony_ci			atomic_set(&pd->scan_queue, 1);
13408c2ecf20Sopenharmony_ci		}
13418c2ecf20Sopenharmony_ci	}
13428c2ecf20Sopenharmony_ci	spin_unlock(&pd->cdrw.active_list_lock);
13438c2ecf20Sopenharmony_ci}
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_cistatic void pkt_count_states(struct pktcdvd_device *pd, int *states)
13468c2ecf20Sopenharmony_ci{
13478c2ecf20Sopenharmony_ci	struct packet_data *pkt;
13488c2ecf20Sopenharmony_ci	int i;
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	for (i = 0; i < PACKET_NUM_STATES; i++)
13518c2ecf20Sopenharmony_ci		states[i] = 0;
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	spin_lock(&pd->cdrw.active_list_lock);
13548c2ecf20Sopenharmony_ci	list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
13558c2ecf20Sopenharmony_ci		states[pkt->state]++;
13568c2ecf20Sopenharmony_ci	}
13578c2ecf20Sopenharmony_ci	spin_unlock(&pd->cdrw.active_list_lock);
13588c2ecf20Sopenharmony_ci}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci/*
13618c2ecf20Sopenharmony_ci * kcdrwd is woken up when writes have been queued for one of our
13628c2ecf20Sopenharmony_ci * registered devices
13638c2ecf20Sopenharmony_ci */
13648c2ecf20Sopenharmony_cistatic int kcdrwd(void *foobar)
13658c2ecf20Sopenharmony_ci{
13668c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd = foobar;
13678c2ecf20Sopenharmony_ci	struct packet_data *pkt;
13688c2ecf20Sopenharmony_ci	long min_sleep_time, residue;
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	set_user_nice(current, MIN_NICE);
13718c2ecf20Sopenharmony_ci	set_freezable();
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	for (;;) {
13748c2ecf20Sopenharmony_ci		DECLARE_WAITQUEUE(wait, current);
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci		/*
13778c2ecf20Sopenharmony_ci		 * Wait until there is something to do
13788c2ecf20Sopenharmony_ci		 */
13798c2ecf20Sopenharmony_ci		add_wait_queue(&pd->wqueue, &wait);
13808c2ecf20Sopenharmony_ci		for (;;) {
13818c2ecf20Sopenharmony_ci			set_current_state(TASK_INTERRUPTIBLE);
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci			/* Check if we need to run pkt_handle_queue */
13848c2ecf20Sopenharmony_ci			if (atomic_read(&pd->scan_queue) > 0)
13858c2ecf20Sopenharmony_ci				goto work_to_do;
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci			/* Check if we need to run the state machine for some packet */
13888c2ecf20Sopenharmony_ci			list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
13898c2ecf20Sopenharmony_ci				if (atomic_read(&pkt->run_sm) > 0)
13908c2ecf20Sopenharmony_ci					goto work_to_do;
13918c2ecf20Sopenharmony_ci			}
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci			/* Check if we need to process the iosched queues */
13948c2ecf20Sopenharmony_ci			if (atomic_read(&pd->iosched.attention) != 0)
13958c2ecf20Sopenharmony_ci				goto work_to_do;
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci			/* Otherwise, go to sleep */
13988c2ecf20Sopenharmony_ci			if (PACKET_DEBUG > 1) {
13998c2ecf20Sopenharmony_ci				int states[PACKET_NUM_STATES];
14008c2ecf20Sopenharmony_ci				pkt_count_states(pd, states);
14018c2ecf20Sopenharmony_ci				pkt_dbg(2, pd, "i:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n",
14028c2ecf20Sopenharmony_ci					states[0], states[1], states[2],
14038c2ecf20Sopenharmony_ci					states[3], states[4], states[5]);
14048c2ecf20Sopenharmony_ci			}
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci			min_sleep_time = MAX_SCHEDULE_TIMEOUT;
14078c2ecf20Sopenharmony_ci			list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
14088c2ecf20Sopenharmony_ci				if (pkt->sleep_time && pkt->sleep_time < min_sleep_time)
14098c2ecf20Sopenharmony_ci					min_sleep_time = pkt->sleep_time;
14108c2ecf20Sopenharmony_ci			}
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci			pkt_dbg(2, pd, "sleeping\n");
14138c2ecf20Sopenharmony_ci			residue = schedule_timeout(min_sleep_time);
14148c2ecf20Sopenharmony_ci			pkt_dbg(2, pd, "wake up\n");
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci			/* make swsusp happy with our thread */
14178c2ecf20Sopenharmony_ci			try_to_freeze();
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci			list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
14208c2ecf20Sopenharmony_ci				if (!pkt->sleep_time)
14218c2ecf20Sopenharmony_ci					continue;
14228c2ecf20Sopenharmony_ci				pkt->sleep_time -= min_sleep_time - residue;
14238c2ecf20Sopenharmony_ci				if (pkt->sleep_time <= 0) {
14248c2ecf20Sopenharmony_ci					pkt->sleep_time = 0;
14258c2ecf20Sopenharmony_ci					atomic_inc(&pkt->run_sm);
14268c2ecf20Sopenharmony_ci				}
14278c2ecf20Sopenharmony_ci			}
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci			if (kthread_should_stop())
14308c2ecf20Sopenharmony_ci				break;
14318c2ecf20Sopenharmony_ci		}
14328c2ecf20Sopenharmony_ciwork_to_do:
14338c2ecf20Sopenharmony_ci		set_current_state(TASK_RUNNING);
14348c2ecf20Sopenharmony_ci		remove_wait_queue(&pd->wqueue, &wait);
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci		if (kthread_should_stop())
14378c2ecf20Sopenharmony_ci			break;
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci		/*
14408c2ecf20Sopenharmony_ci		 * if pkt_handle_queue returns true, we can queue
14418c2ecf20Sopenharmony_ci		 * another request.
14428c2ecf20Sopenharmony_ci		 */
14438c2ecf20Sopenharmony_ci		while (pkt_handle_queue(pd))
14448c2ecf20Sopenharmony_ci			;
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci		/*
14478c2ecf20Sopenharmony_ci		 * Handle packet state machine
14488c2ecf20Sopenharmony_ci		 */
14498c2ecf20Sopenharmony_ci		pkt_handle_packets(pd);
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci		/*
14528c2ecf20Sopenharmony_ci		 * Handle iosched queues
14538c2ecf20Sopenharmony_ci		 */
14548c2ecf20Sopenharmony_ci		pkt_iosched_process_queue(pd);
14558c2ecf20Sopenharmony_ci	}
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	return 0;
14588c2ecf20Sopenharmony_ci}
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_cistatic void pkt_print_settings(struct pktcdvd_device *pd)
14618c2ecf20Sopenharmony_ci{
14628c2ecf20Sopenharmony_ci	pkt_info(pd, "%s packets, %u blocks, Mode-%c disc\n",
14638c2ecf20Sopenharmony_ci		 pd->settings.fp ? "Fixed" : "Variable",
14648c2ecf20Sopenharmony_ci		 pd->settings.size >> 2,
14658c2ecf20Sopenharmony_ci		 pd->settings.block_mode == 8 ? '1' : '2');
14668c2ecf20Sopenharmony_ci}
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_cistatic int pkt_mode_sense(struct pktcdvd_device *pd, struct packet_command *cgc, int page_code, int page_control)
14698c2ecf20Sopenharmony_ci{
14708c2ecf20Sopenharmony_ci	memset(cgc->cmd, 0, sizeof(cgc->cmd));
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci	cgc->cmd[0] = GPCMD_MODE_SENSE_10;
14738c2ecf20Sopenharmony_ci	cgc->cmd[2] = page_code | (page_control << 6);
14748c2ecf20Sopenharmony_ci	cgc->cmd[7] = cgc->buflen >> 8;
14758c2ecf20Sopenharmony_ci	cgc->cmd[8] = cgc->buflen & 0xff;
14768c2ecf20Sopenharmony_ci	cgc->data_direction = CGC_DATA_READ;
14778c2ecf20Sopenharmony_ci	return pkt_generic_packet(pd, cgc);
14788c2ecf20Sopenharmony_ci}
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_cistatic int pkt_mode_select(struct pktcdvd_device *pd, struct packet_command *cgc)
14818c2ecf20Sopenharmony_ci{
14828c2ecf20Sopenharmony_ci	memset(cgc->cmd, 0, sizeof(cgc->cmd));
14838c2ecf20Sopenharmony_ci	memset(cgc->buffer, 0, 2);
14848c2ecf20Sopenharmony_ci	cgc->cmd[0] = GPCMD_MODE_SELECT_10;
14858c2ecf20Sopenharmony_ci	cgc->cmd[1] = 0x10;		/* PF */
14868c2ecf20Sopenharmony_ci	cgc->cmd[7] = cgc->buflen >> 8;
14878c2ecf20Sopenharmony_ci	cgc->cmd[8] = cgc->buflen & 0xff;
14888c2ecf20Sopenharmony_ci	cgc->data_direction = CGC_DATA_WRITE;
14898c2ecf20Sopenharmony_ci	return pkt_generic_packet(pd, cgc);
14908c2ecf20Sopenharmony_ci}
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_cistatic int pkt_get_disc_info(struct pktcdvd_device *pd, disc_information *di)
14938c2ecf20Sopenharmony_ci{
14948c2ecf20Sopenharmony_ci	struct packet_command cgc;
14958c2ecf20Sopenharmony_ci	int ret;
14968c2ecf20Sopenharmony_ci
14978c2ecf20Sopenharmony_ci	/* set up command and get the disc info */
14988c2ecf20Sopenharmony_ci	init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ);
14998c2ecf20Sopenharmony_ci	cgc.cmd[0] = GPCMD_READ_DISC_INFO;
15008c2ecf20Sopenharmony_ci	cgc.cmd[8] = cgc.buflen = 2;
15018c2ecf20Sopenharmony_ci	cgc.quiet = 1;
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	ret = pkt_generic_packet(pd, &cgc);
15048c2ecf20Sopenharmony_ci	if (ret)
15058c2ecf20Sopenharmony_ci		return ret;
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	/* not all drives have the same disc_info length, so requeue
15088c2ecf20Sopenharmony_ci	 * packet with the length the drive tells us it can supply
15098c2ecf20Sopenharmony_ci	 */
15108c2ecf20Sopenharmony_ci	cgc.buflen = be16_to_cpu(di->disc_information_length) +
15118c2ecf20Sopenharmony_ci		     sizeof(di->disc_information_length);
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci	if (cgc.buflen > sizeof(disc_information))
15148c2ecf20Sopenharmony_ci		cgc.buflen = sizeof(disc_information);
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	cgc.cmd[8] = cgc.buflen;
15178c2ecf20Sopenharmony_ci	return pkt_generic_packet(pd, &cgc);
15188c2ecf20Sopenharmony_ci}
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_cistatic int pkt_get_track_info(struct pktcdvd_device *pd, __u16 track, __u8 type, track_information *ti)
15218c2ecf20Sopenharmony_ci{
15228c2ecf20Sopenharmony_ci	struct packet_command cgc;
15238c2ecf20Sopenharmony_ci	int ret;
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ);
15268c2ecf20Sopenharmony_ci	cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO;
15278c2ecf20Sopenharmony_ci	cgc.cmd[1] = type & 3;
15288c2ecf20Sopenharmony_ci	cgc.cmd[4] = (track & 0xff00) >> 8;
15298c2ecf20Sopenharmony_ci	cgc.cmd[5] = track & 0xff;
15308c2ecf20Sopenharmony_ci	cgc.cmd[8] = 8;
15318c2ecf20Sopenharmony_ci	cgc.quiet = 1;
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci	ret = pkt_generic_packet(pd, &cgc);
15348c2ecf20Sopenharmony_ci	if (ret)
15358c2ecf20Sopenharmony_ci		return ret;
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci	cgc.buflen = be16_to_cpu(ti->track_information_length) +
15388c2ecf20Sopenharmony_ci		     sizeof(ti->track_information_length);
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci	if (cgc.buflen > sizeof(track_information))
15418c2ecf20Sopenharmony_ci		cgc.buflen = sizeof(track_information);
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	cgc.cmd[8] = cgc.buflen;
15448c2ecf20Sopenharmony_ci	return pkt_generic_packet(pd, &cgc);
15458c2ecf20Sopenharmony_ci}
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_cistatic noinline_for_stack int pkt_get_last_written(struct pktcdvd_device *pd,
15488c2ecf20Sopenharmony_ci						long *last_written)
15498c2ecf20Sopenharmony_ci{
15508c2ecf20Sopenharmony_ci	disc_information di;
15518c2ecf20Sopenharmony_ci	track_information ti;
15528c2ecf20Sopenharmony_ci	__u32 last_track;
15538c2ecf20Sopenharmony_ci	int ret;
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	ret = pkt_get_disc_info(pd, &di);
15568c2ecf20Sopenharmony_ci	if (ret)
15578c2ecf20Sopenharmony_ci		return ret;
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	last_track = (di.last_track_msb << 8) | di.last_track_lsb;
15608c2ecf20Sopenharmony_ci	ret = pkt_get_track_info(pd, last_track, 1, &ti);
15618c2ecf20Sopenharmony_ci	if (ret)
15628c2ecf20Sopenharmony_ci		return ret;
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	/* if this track is blank, try the previous. */
15658c2ecf20Sopenharmony_ci	if (ti.blank) {
15668c2ecf20Sopenharmony_ci		last_track--;
15678c2ecf20Sopenharmony_ci		ret = pkt_get_track_info(pd, last_track, 1, &ti);
15688c2ecf20Sopenharmony_ci		if (ret)
15698c2ecf20Sopenharmony_ci			return ret;
15708c2ecf20Sopenharmony_ci	}
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	/* if last recorded field is valid, return it. */
15738c2ecf20Sopenharmony_ci	if (ti.lra_v) {
15748c2ecf20Sopenharmony_ci		*last_written = be32_to_cpu(ti.last_rec_address);
15758c2ecf20Sopenharmony_ci	} else {
15768c2ecf20Sopenharmony_ci		/* make it up instead */
15778c2ecf20Sopenharmony_ci		*last_written = be32_to_cpu(ti.track_start) +
15788c2ecf20Sopenharmony_ci				be32_to_cpu(ti.track_size);
15798c2ecf20Sopenharmony_ci		if (ti.free_blocks)
15808c2ecf20Sopenharmony_ci			*last_written -= (be32_to_cpu(ti.free_blocks) + 7);
15818c2ecf20Sopenharmony_ci	}
15828c2ecf20Sopenharmony_ci	return 0;
15838c2ecf20Sopenharmony_ci}
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci/*
15868c2ecf20Sopenharmony_ci * write mode select package based on pd->settings
15878c2ecf20Sopenharmony_ci */
15888c2ecf20Sopenharmony_cistatic noinline_for_stack int pkt_set_write_settings(struct pktcdvd_device *pd)
15898c2ecf20Sopenharmony_ci{
15908c2ecf20Sopenharmony_ci	struct packet_command cgc;
15918c2ecf20Sopenharmony_ci	struct scsi_sense_hdr sshdr;
15928c2ecf20Sopenharmony_ci	write_param_page *wp;
15938c2ecf20Sopenharmony_ci	char buffer[128];
15948c2ecf20Sopenharmony_ci	int ret, size;
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	/* doesn't apply to DVD+RW or DVD-RAM */
15978c2ecf20Sopenharmony_ci	if ((pd->mmc3_profile == 0x1a) || (pd->mmc3_profile == 0x12))
15988c2ecf20Sopenharmony_ci		return 0;
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	memset(buffer, 0, sizeof(buffer));
16018c2ecf20Sopenharmony_ci	init_cdrom_command(&cgc, buffer, sizeof(*wp), CGC_DATA_READ);
16028c2ecf20Sopenharmony_ci	cgc.sshdr = &sshdr;
16038c2ecf20Sopenharmony_ci	ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0);
16048c2ecf20Sopenharmony_ci	if (ret) {
16058c2ecf20Sopenharmony_ci		pkt_dump_sense(pd, &cgc);
16068c2ecf20Sopenharmony_ci		return ret;
16078c2ecf20Sopenharmony_ci	}
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci	size = 2 + ((buffer[0] << 8) | (buffer[1] & 0xff));
16108c2ecf20Sopenharmony_ci	pd->mode_offset = (buffer[6] << 8) | (buffer[7] & 0xff);
16118c2ecf20Sopenharmony_ci	if (size > sizeof(buffer))
16128c2ecf20Sopenharmony_ci		size = sizeof(buffer);
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	/*
16158c2ecf20Sopenharmony_ci	 * now get it all
16168c2ecf20Sopenharmony_ci	 */
16178c2ecf20Sopenharmony_ci	init_cdrom_command(&cgc, buffer, size, CGC_DATA_READ);
16188c2ecf20Sopenharmony_ci	cgc.sshdr = &sshdr;
16198c2ecf20Sopenharmony_ci	ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0);
16208c2ecf20Sopenharmony_ci	if (ret) {
16218c2ecf20Sopenharmony_ci		pkt_dump_sense(pd, &cgc);
16228c2ecf20Sopenharmony_ci		return ret;
16238c2ecf20Sopenharmony_ci	}
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci	/*
16268c2ecf20Sopenharmony_ci	 * write page is offset header + block descriptor length
16278c2ecf20Sopenharmony_ci	 */
16288c2ecf20Sopenharmony_ci	wp = (write_param_page *) &buffer[sizeof(struct mode_page_header) + pd->mode_offset];
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	wp->fp = pd->settings.fp;
16318c2ecf20Sopenharmony_ci	wp->track_mode = pd->settings.track_mode;
16328c2ecf20Sopenharmony_ci	wp->write_type = pd->settings.write_type;
16338c2ecf20Sopenharmony_ci	wp->data_block_type = pd->settings.block_mode;
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci	wp->multi_session = 0;
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci#ifdef PACKET_USE_LS
16388c2ecf20Sopenharmony_ci	wp->link_size = 7;
16398c2ecf20Sopenharmony_ci	wp->ls_v = 1;
16408c2ecf20Sopenharmony_ci#endif
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci	if (wp->data_block_type == PACKET_BLOCK_MODE1) {
16438c2ecf20Sopenharmony_ci		wp->session_format = 0;
16448c2ecf20Sopenharmony_ci		wp->subhdr2 = 0x20;
16458c2ecf20Sopenharmony_ci	} else if (wp->data_block_type == PACKET_BLOCK_MODE2) {
16468c2ecf20Sopenharmony_ci		wp->session_format = 0x20;
16478c2ecf20Sopenharmony_ci		wp->subhdr2 = 8;
16488c2ecf20Sopenharmony_ci#if 0
16498c2ecf20Sopenharmony_ci		wp->mcn[0] = 0x80;
16508c2ecf20Sopenharmony_ci		memcpy(&wp->mcn[1], PACKET_MCN, sizeof(wp->mcn) - 1);
16518c2ecf20Sopenharmony_ci#endif
16528c2ecf20Sopenharmony_ci	} else {
16538c2ecf20Sopenharmony_ci		/*
16548c2ecf20Sopenharmony_ci		 * paranoia
16558c2ecf20Sopenharmony_ci		 */
16568c2ecf20Sopenharmony_ci		pkt_err(pd, "write mode wrong %d\n", wp->data_block_type);
16578c2ecf20Sopenharmony_ci		return 1;
16588c2ecf20Sopenharmony_ci	}
16598c2ecf20Sopenharmony_ci	wp->packet_size = cpu_to_be32(pd->settings.size >> 2);
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci	cgc.buflen = cgc.cmd[8] = size;
16628c2ecf20Sopenharmony_ci	ret = pkt_mode_select(pd, &cgc);
16638c2ecf20Sopenharmony_ci	if (ret) {
16648c2ecf20Sopenharmony_ci		pkt_dump_sense(pd, &cgc);
16658c2ecf20Sopenharmony_ci		return ret;
16668c2ecf20Sopenharmony_ci	}
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci	pkt_print_settings(pd);
16698c2ecf20Sopenharmony_ci	return 0;
16708c2ecf20Sopenharmony_ci}
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci/*
16738c2ecf20Sopenharmony_ci * 1 -- we can write to this track, 0 -- we can't
16748c2ecf20Sopenharmony_ci */
16758c2ecf20Sopenharmony_cistatic int pkt_writable_track(struct pktcdvd_device *pd, track_information *ti)
16768c2ecf20Sopenharmony_ci{
16778c2ecf20Sopenharmony_ci	switch (pd->mmc3_profile) {
16788c2ecf20Sopenharmony_ci		case 0x1a: /* DVD+RW */
16798c2ecf20Sopenharmony_ci		case 0x12: /* DVD-RAM */
16808c2ecf20Sopenharmony_ci			/* The track is always writable on DVD+RW/DVD-RAM */
16818c2ecf20Sopenharmony_ci			return 1;
16828c2ecf20Sopenharmony_ci		default:
16838c2ecf20Sopenharmony_ci			break;
16848c2ecf20Sopenharmony_ci	}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci	if (!ti->packet || !ti->fp)
16878c2ecf20Sopenharmony_ci		return 0;
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	/*
16908c2ecf20Sopenharmony_ci	 * "good" settings as per Mt Fuji.
16918c2ecf20Sopenharmony_ci	 */
16928c2ecf20Sopenharmony_ci	if (ti->rt == 0 && ti->blank == 0)
16938c2ecf20Sopenharmony_ci		return 1;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	if (ti->rt == 0 && ti->blank == 1)
16968c2ecf20Sopenharmony_ci		return 1;
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci	if (ti->rt == 1 && ti->blank == 0)
16998c2ecf20Sopenharmony_ci		return 1;
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	pkt_err(pd, "bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet);
17028c2ecf20Sopenharmony_ci	return 0;
17038c2ecf20Sopenharmony_ci}
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci/*
17068c2ecf20Sopenharmony_ci * 1 -- we can write to this disc, 0 -- we can't
17078c2ecf20Sopenharmony_ci */
17088c2ecf20Sopenharmony_cistatic int pkt_writable_disc(struct pktcdvd_device *pd, disc_information *di)
17098c2ecf20Sopenharmony_ci{
17108c2ecf20Sopenharmony_ci	switch (pd->mmc3_profile) {
17118c2ecf20Sopenharmony_ci		case 0x0a: /* CD-RW */
17128c2ecf20Sopenharmony_ci		case 0xffff: /* MMC3 not supported */
17138c2ecf20Sopenharmony_ci			break;
17148c2ecf20Sopenharmony_ci		case 0x1a: /* DVD+RW */
17158c2ecf20Sopenharmony_ci		case 0x13: /* DVD-RW */
17168c2ecf20Sopenharmony_ci		case 0x12: /* DVD-RAM */
17178c2ecf20Sopenharmony_ci			return 1;
17188c2ecf20Sopenharmony_ci		default:
17198c2ecf20Sopenharmony_ci			pkt_dbg(2, pd, "Wrong disc profile (%x)\n",
17208c2ecf20Sopenharmony_ci				pd->mmc3_profile);
17218c2ecf20Sopenharmony_ci			return 0;
17228c2ecf20Sopenharmony_ci	}
17238c2ecf20Sopenharmony_ci
17248c2ecf20Sopenharmony_ci	/*
17258c2ecf20Sopenharmony_ci	 * for disc type 0xff we should probably reserve a new track.
17268c2ecf20Sopenharmony_ci	 * but i'm not sure, should we leave this to user apps? probably.
17278c2ecf20Sopenharmony_ci	 */
17288c2ecf20Sopenharmony_ci	if (di->disc_type == 0xff) {
17298c2ecf20Sopenharmony_ci		pkt_notice(pd, "unknown disc - no track?\n");
17308c2ecf20Sopenharmony_ci		return 0;
17318c2ecf20Sopenharmony_ci	}
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	if (di->disc_type != 0x20 && di->disc_type != 0) {
17348c2ecf20Sopenharmony_ci		pkt_err(pd, "wrong disc type (%x)\n", di->disc_type);
17358c2ecf20Sopenharmony_ci		return 0;
17368c2ecf20Sopenharmony_ci	}
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ci	if (di->erasable == 0) {
17398c2ecf20Sopenharmony_ci		pkt_notice(pd, "disc not erasable\n");
17408c2ecf20Sopenharmony_ci		return 0;
17418c2ecf20Sopenharmony_ci	}
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci	if (di->border_status == PACKET_SESSION_RESERVED) {
17448c2ecf20Sopenharmony_ci		pkt_err(pd, "can't write to last track (reserved)\n");
17458c2ecf20Sopenharmony_ci		return 0;
17468c2ecf20Sopenharmony_ci	}
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_ci	return 1;
17498c2ecf20Sopenharmony_ci}
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_cistatic noinline_for_stack int pkt_probe_settings(struct pktcdvd_device *pd)
17528c2ecf20Sopenharmony_ci{
17538c2ecf20Sopenharmony_ci	struct packet_command cgc;
17548c2ecf20Sopenharmony_ci	unsigned char buf[12];
17558c2ecf20Sopenharmony_ci	disc_information di;
17568c2ecf20Sopenharmony_ci	track_information ti;
17578c2ecf20Sopenharmony_ci	int ret, track;
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci	init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
17608c2ecf20Sopenharmony_ci	cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
17618c2ecf20Sopenharmony_ci	cgc.cmd[8] = 8;
17628c2ecf20Sopenharmony_ci	ret = pkt_generic_packet(pd, &cgc);
17638c2ecf20Sopenharmony_ci	pd->mmc3_profile = ret ? 0xffff : buf[6] << 8 | buf[7];
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci	memset(&di, 0, sizeof(disc_information));
17668c2ecf20Sopenharmony_ci	memset(&ti, 0, sizeof(track_information));
17678c2ecf20Sopenharmony_ci
17688c2ecf20Sopenharmony_ci	ret = pkt_get_disc_info(pd, &di);
17698c2ecf20Sopenharmony_ci	if (ret) {
17708c2ecf20Sopenharmony_ci		pkt_err(pd, "failed get_disc\n");
17718c2ecf20Sopenharmony_ci		return ret;
17728c2ecf20Sopenharmony_ci	}
17738c2ecf20Sopenharmony_ci
17748c2ecf20Sopenharmony_ci	if (!pkt_writable_disc(pd, &di))
17758c2ecf20Sopenharmony_ci		return -EROFS;
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci	pd->type = di.erasable ? PACKET_CDRW : PACKET_CDR;
17788c2ecf20Sopenharmony_ci
17798c2ecf20Sopenharmony_ci	track = 1; /* (di.last_track_msb << 8) | di.last_track_lsb; */
17808c2ecf20Sopenharmony_ci	ret = pkt_get_track_info(pd, track, 1, &ti);
17818c2ecf20Sopenharmony_ci	if (ret) {
17828c2ecf20Sopenharmony_ci		pkt_err(pd, "failed get_track\n");
17838c2ecf20Sopenharmony_ci		return ret;
17848c2ecf20Sopenharmony_ci	}
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	if (!pkt_writable_track(pd, &ti)) {
17878c2ecf20Sopenharmony_ci		pkt_err(pd, "can't write to this track\n");
17888c2ecf20Sopenharmony_ci		return -EROFS;
17898c2ecf20Sopenharmony_ci	}
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci	/*
17928c2ecf20Sopenharmony_ci	 * we keep packet size in 512 byte units, makes it easier to
17938c2ecf20Sopenharmony_ci	 * deal with request calculations.
17948c2ecf20Sopenharmony_ci	 */
17958c2ecf20Sopenharmony_ci	pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2;
17968c2ecf20Sopenharmony_ci	if (pd->settings.size == 0) {
17978c2ecf20Sopenharmony_ci		pkt_notice(pd, "detected zero packet size!\n");
17988c2ecf20Sopenharmony_ci		return -ENXIO;
17998c2ecf20Sopenharmony_ci	}
18008c2ecf20Sopenharmony_ci	if (pd->settings.size > PACKET_MAX_SECTORS) {
18018c2ecf20Sopenharmony_ci		pkt_err(pd, "packet size is too big\n");
18028c2ecf20Sopenharmony_ci		return -EROFS;
18038c2ecf20Sopenharmony_ci	}
18048c2ecf20Sopenharmony_ci	pd->settings.fp = ti.fp;
18058c2ecf20Sopenharmony_ci	pd->offset = (be32_to_cpu(ti.track_start) << 2) & (pd->settings.size - 1);
18068c2ecf20Sopenharmony_ci
18078c2ecf20Sopenharmony_ci	if (ti.nwa_v) {
18088c2ecf20Sopenharmony_ci		pd->nwa = be32_to_cpu(ti.next_writable);
18098c2ecf20Sopenharmony_ci		set_bit(PACKET_NWA_VALID, &pd->flags);
18108c2ecf20Sopenharmony_ci	}
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci	/*
18138c2ecf20Sopenharmony_ci	 * in theory we could use lra on -RW media as well and just zero
18148c2ecf20Sopenharmony_ci	 * blocks that haven't been written yet, but in practice that
18158c2ecf20Sopenharmony_ci	 * is just a no-go. we'll use that for -R, naturally.
18168c2ecf20Sopenharmony_ci	 */
18178c2ecf20Sopenharmony_ci	if (ti.lra_v) {
18188c2ecf20Sopenharmony_ci		pd->lra = be32_to_cpu(ti.last_rec_address);
18198c2ecf20Sopenharmony_ci		set_bit(PACKET_LRA_VALID, &pd->flags);
18208c2ecf20Sopenharmony_ci	} else {
18218c2ecf20Sopenharmony_ci		pd->lra = 0xffffffff;
18228c2ecf20Sopenharmony_ci		set_bit(PACKET_LRA_VALID, &pd->flags);
18238c2ecf20Sopenharmony_ci	}
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_ci	/*
18268c2ecf20Sopenharmony_ci	 * fine for now
18278c2ecf20Sopenharmony_ci	 */
18288c2ecf20Sopenharmony_ci	pd->settings.link_loss = 7;
18298c2ecf20Sopenharmony_ci	pd->settings.write_type = 0;	/* packet */
18308c2ecf20Sopenharmony_ci	pd->settings.track_mode = ti.track_mode;
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	/*
18338c2ecf20Sopenharmony_ci	 * mode1 or mode2 disc
18348c2ecf20Sopenharmony_ci	 */
18358c2ecf20Sopenharmony_ci	switch (ti.data_mode) {
18368c2ecf20Sopenharmony_ci		case PACKET_MODE1:
18378c2ecf20Sopenharmony_ci			pd->settings.block_mode = PACKET_BLOCK_MODE1;
18388c2ecf20Sopenharmony_ci			break;
18398c2ecf20Sopenharmony_ci		case PACKET_MODE2:
18408c2ecf20Sopenharmony_ci			pd->settings.block_mode = PACKET_BLOCK_MODE2;
18418c2ecf20Sopenharmony_ci			break;
18428c2ecf20Sopenharmony_ci		default:
18438c2ecf20Sopenharmony_ci			pkt_err(pd, "unknown data mode\n");
18448c2ecf20Sopenharmony_ci			return -EROFS;
18458c2ecf20Sopenharmony_ci	}
18468c2ecf20Sopenharmony_ci	return 0;
18478c2ecf20Sopenharmony_ci}
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci/*
18508c2ecf20Sopenharmony_ci * enable/disable write caching on drive
18518c2ecf20Sopenharmony_ci */
18528c2ecf20Sopenharmony_cistatic noinline_for_stack int pkt_write_caching(struct pktcdvd_device *pd,
18538c2ecf20Sopenharmony_ci						int set)
18548c2ecf20Sopenharmony_ci{
18558c2ecf20Sopenharmony_ci	struct packet_command cgc;
18568c2ecf20Sopenharmony_ci	struct scsi_sense_hdr sshdr;
18578c2ecf20Sopenharmony_ci	unsigned char buf[64];
18588c2ecf20Sopenharmony_ci	int ret;
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ci	init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
18618c2ecf20Sopenharmony_ci	cgc.sshdr = &sshdr;
18628c2ecf20Sopenharmony_ci	cgc.buflen = pd->mode_offset + 12;
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	/*
18658c2ecf20Sopenharmony_ci	 * caching mode page might not be there, so quiet this command
18668c2ecf20Sopenharmony_ci	 */
18678c2ecf20Sopenharmony_ci	cgc.quiet = 1;
18688c2ecf20Sopenharmony_ci
18698c2ecf20Sopenharmony_ci	ret = pkt_mode_sense(pd, &cgc, GPMODE_WCACHING_PAGE, 0);
18708c2ecf20Sopenharmony_ci	if (ret)
18718c2ecf20Sopenharmony_ci		return ret;
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci	buf[pd->mode_offset + 10] |= (!!set << 2);
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci	cgc.buflen = cgc.cmd[8] = 2 + ((buf[0] << 8) | (buf[1] & 0xff));
18768c2ecf20Sopenharmony_ci	ret = pkt_mode_select(pd, &cgc);
18778c2ecf20Sopenharmony_ci	if (ret) {
18788c2ecf20Sopenharmony_ci		pkt_err(pd, "write caching control failed\n");
18798c2ecf20Sopenharmony_ci		pkt_dump_sense(pd, &cgc);
18808c2ecf20Sopenharmony_ci	} else if (!ret && set)
18818c2ecf20Sopenharmony_ci		pkt_notice(pd, "enabled write caching\n");
18828c2ecf20Sopenharmony_ci	return ret;
18838c2ecf20Sopenharmony_ci}
18848c2ecf20Sopenharmony_ci
18858c2ecf20Sopenharmony_cistatic int pkt_lock_door(struct pktcdvd_device *pd, int lockflag)
18868c2ecf20Sopenharmony_ci{
18878c2ecf20Sopenharmony_ci	struct packet_command cgc;
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_ci	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
18908c2ecf20Sopenharmony_ci	cgc.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
18918c2ecf20Sopenharmony_ci	cgc.cmd[4] = lockflag ? 1 : 0;
18928c2ecf20Sopenharmony_ci	return pkt_generic_packet(pd, &cgc);
18938c2ecf20Sopenharmony_ci}
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci/*
18968c2ecf20Sopenharmony_ci * Returns drive maximum write speed
18978c2ecf20Sopenharmony_ci */
18988c2ecf20Sopenharmony_cistatic noinline_for_stack int pkt_get_max_speed(struct pktcdvd_device *pd,
18998c2ecf20Sopenharmony_ci						unsigned *write_speed)
19008c2ecf20Sopenharmony_ci{
19018c2ecf20Sopenharmony_ci	struct packet_command cgc;
19028c2ecf20Sopenharmony_ci	struct scsi_sense_hdr sshdr;
19038c2ecf20Sopenharmony_ci	unsigned char buf[256+18];
19048c2ecf20Sopenharmony_ci	unsigned char *cap_buf;
19058c2ecf20Sopenharmony_ci	int ret, offset;
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci	cap_buf = &buf[sizeof(struct mode_page_header) + pd->mode_offset];
19088c2ecf20Sopenharmony_ci	init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_UNKNOWN);
19098c2ecf20Sopenharmony_ci	cgc.sshdr = &sshdr;
19108c2ecf20Sopenharmony_ci
19118c2ecf20Sopenharmony_ci	ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
19128c2ecf20Sopenharmony_ci	if (ret) {
19138c2ecf20Sopenharmony_ci		cgc.buflen = pd->mode_offset + cap_buf[1] + 2 +
19148c2ecf20Sopenharmony_ci			     sizeof(struct mode_page_header);
19158c2ecf20Sopenharmony_ci		ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
19168c2ecf20Sopenharmony_ci		if (ret) {
19178c2ecf20Sopenharmony_ci			pkt_dump_sense(pd, &cgc);
19188c2ecf20Sopenharmony_ci			return ret;
19198c2ecf20Sopenharmony_ci		}
19208c2ecf20Sopenharmony_ci	}
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci	offset = 20;			    /* Obsoleted field, used by older drives */
19238c2ecf20Sopenharmony_ci	if (cap_buf[1] >= 28)
19248c2ecf20Sopenharmony_ci		offset = 28;		    /* Current write speed selected */
19258c2ecf20Sopenharmony_ci	if (cap_buf[1] >= 30) {
19268c2ecf20Sopenharmony_ci		/* If the drive reports at least one "Logical Unit Write
19278c2ecf20Sopenharmony_ci		 * Speed Performance Descriptor Block", use the information
19288c2ecf20Sopenharmony_ci		 * in the first block. (contains the highest speed)
19298c2ecf20Sopenharmony_ci		 */
19308c2ecf20Sopenharmony_ci		int num_spdb = (cap_buf[30] << 8) + cap_buf[31];
19318c2ecf20Sopenharmony_ci		if (num_spdb > 0)
19328c2ecf20Sopenharmony_ci			offset = 34;
19338c2ecf20Sopenharmony_ci	}
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_ci	*write_speed = (cap_buf[offset] << 8) | cap_buf[offset + 1];
19368c2ecf20Sopenharmony_ci	return 0;
19378c2ecf20Sopenharmony_ci}
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ci/* These tables from cdrecord - I don't have orange book */
19408c2ecf20Sopenharmony_ci/* standard speed CD-RW (1-4x) */
19418c2ecf20Sopenharmony_cistatic char clv_to_speed[16] = {
19428c2ecf20Sopenharmony_ci	/* 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 */
19438c2ecf20Sopenharmony_ci	   0, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
19448c2ecf20Sopenharmony_ci};
19458c2ecf20Sopenharmony_ci/* high speed CD-RW (-10x) */
19468c2ecf20Sopenharmony_cistatic char hs_clv_to_speed[16] = {
19478c2ecf20Sopenharmony_ci	/* 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 */
19488c2ecf20Sopenharmony_ci	   0, 2, 4, 6, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
19498c2ecf20Sopenharmony_ci};
19508c2ecf20Sopenharmony_ci/* ultra high speed CD-RW */
19518c2ecf20Sopenharmony_cistatic char us_clv_to_speed[16] = {
19528c2ecf20Sopenharmony_ci	/* 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 */
19538c2ecf20Sopenharmony_ci	   0, 2, 4, 8, 0, 0,16, 0,24,32,40,48, 0, 0, 0, 0
19548c2ecf20Sopenharmony_ci};
19558c2ecf20Sopenharmony_ci
19568c2ecf20Sopenharmony_ci/*
19578c2ecf20Sopenharmony_ci * reads the maximum media speed from ATIP
19588c2ecf20Sopenharmony_ci */
19598c2ecf20Sopenharmony_cistatic noinline_for_stack int pkt_media_speed(struct pktcdvd_device *pd,
19608c2ecf20Sopenharmony_ci						unsigned *speed)
19618c2ecf20Sopenharmony_ci{
19628c2ecf20Sopenharmony_ci	struct packet_command cgc;
19638c2ecf20Sopenharmony_ci	struct scsi_sense_hdr sshdr;
19648c2ecf20Sopenharmony_ci	unsigned char buf[64];
19658c2ecf20Sopenharmony_ci	unsigned int size, st, sp;
19668c2ecf20Sopenharmony_ci	int ret;
19678c2ecf20Sopenharmony_ci
19688c2ecf20Sopenharmony_ci	init_cdrom_command(&cgc, buf, 2, CGC_DATA_READ);
19698c2ecf20Sopenharmony_ci	cgc.sshdr = &sshdr;
19708c2ecf20Sopenharmony_ci	cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
19718c2ecf20Sopenharmony_ci	cgc.cmd[1] = 2;
19728c2ecf20Sopenharmony_ci	cgc.cmd[2] = 4; /* READ ATIP */
19738c2ecf20Sopenharmony_ci	cgc.cmd[8] = 2;
19748c2ecf20Sopenharmony_ci	ret = pkt_generic_packet(pd, &cgc);
19758c2ecf20Sopenharmony_ci	if (ret) {
19768c2ecf20Sopenharmony_ci		pkt_dump_sense(pd, &cgc);
19778c2ecf20Sopenharmony_ci		return ret;
19788c2ecf20Sopenharmony_ci	}
19798c2ecf20Sopenharmony_ci	size = ((unsigned int) buf[0]<<8) + buf[1] + 2;
19808c2ecf20Sopenharmony_ci	if (size > sizeof(buf))
19818c2ecf20Sopenharmony_ci		size = sizeof(buf);
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci	init_cdrom_command(&cgc, buf, size, CGC_DATA_READ);
19848c2ecf20Sopenharmony_ci	cgc.sshdr = &sshdr;
19858c2ecf20Sopenharmony_ci	cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
19868c2ecf20Sopenharmony_ci	cgc.cmd[1] = 2;
19878c2ecf20Sopenharmony_ci	cgc.cmd[2] = 4;
19888c2ecf20Sopenharmony_ci	cgc.cmd[8] = size;
19898c2ecf20Sopenharmony_ci	ret = pkt_generic_packet(pd, &cgc);
19908c2ecf20Sopenharmony_ci	if (ret) {
19918c2ecf20Sopenharmony_ci		pkt_dump_sense(pd, &cgc);
19928c2ecf20Sopenharmony_ci		return ret;
19938c2ecf20Sopenharmony_ci	}
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	if (!(buf[6] & 0x40)) {
19968c2ecf20Sopenharmony_ci		pkt_notice(pd, "disc type is not CD-RW\n");
19978c2ecf20Sopenharmony_ci		return 1;
19988c2ecf20Sopenharmony_ci	}
19998c2ecf20Sopenharmony_ci	if (!(buf[6] & 0x4)) {
20008c2ecf20Sopenharmony_ci		pkt_notice(pd, "A1 values on media are not valid, maybe not CDRW?\n");
20018c2ecf20Sopenharmony_ci		return 1;
20028c2ecf20Sopenharmony_ci	}
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci	st = (buf[6] >> 3) & 0x7; /* disc sub-type */
20058c2ecf20Sopenharmony_ci
20068c2ecf20Sopenharmony_ci	sp = buf[16] & 0xf; /* max speed from ATIP A1 field */
20078c2ecf20Sopenharmony_ci
20088c2ecf20Sopenharmony_ci	/* Info from cdrecord */
20098c2ecf20Sopenharmony_ci	switch (st) {
20108c2ecf20Sopenharmony_ci		case 0: /* standard speed */
20118c2ecf20Sopenharmony_ci			*speed = clv_to_speed[sp];
20128c2ecf20Sopenharmony_ci			break;
20138c2ecf20Sopenharmony_ci		case 1: /* high speed */
20148c2ecf20Sopenharmony_ci			*speed = hs_clv_to_speed[sp];
20158c2ecf20Sopenharmony_ci			break;
20168c2ecf20Sopenharmony_ci		case 2: /* ultra high speed */
20178c2ecf20Sopenharmony_ci			*speed = us_clv_to_speed[sp];
20188c2ecf20Sopenharmony_ci			break;
20198c2ecf20Sopenharmony_ci		default:
20208c2ecf20Sopenharmony_ci			pkt_notice(pd, "unknown disc sub-type %d\n", st);
20218c2ecf20Sopenharmony_ci			return 1;
20228c2ecf20Sopenharmony_ci	}
20238c2ecf20Sopenharmony_ci	if (*speed) {
20248c2ecf20Sopenharmony_ci		pkt_info(pd, "maximum media speed: %d\n", *speed);
20258c2ecf20Sopenharmony_ci		return 0;
20268c2ecf20Sopenharmony_ci	} else {
20278c2ecf20Sopenharmony_ci		pkt_notice(pd, "unknown speed %d for sub-type %d\n", sp, st);
20288c2ecf20Sopenharmony_ci		return 1;
20298c2ecf20Sopenharmony_ci	}
20308c2ecf20Sopenharmony_ci}
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_cistatic noinline_for_stack int pkt_perform_opc(struct pktcdvd_device *pd)
20338c2ecf20Sopenharmony_ci{
20348c2ecf20Sopenharmony_ci	struct packet_command cgc;
20358c2ecf20Sopenharmony_ci	struct scsi_sense_hdr sshdr;
20368c2ecf20Sopenharmony_ci	int ret;
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci	pkt_dbg(2, pd, "Performing OPC\n");
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
20418c2ecf20Sopenharmony_ci	cgc.sshdr = &sshdr;
20428c2ecf20Sopenharmony_ci	cgc.timeout = 60*HZ;
20438c2ecf20Sopenharmony_ci	cgc.cmd[0] = GPCMD_SEND_OPC;
20448c2ecf20Sopenharmony_ci	cgc.cmd[1] = 1;
20458c2ecf20Sopenharmony_ci	ret = pkt_generic_packet(pd, &cgc);
20468c2ecf20Sopenharmony_ci	if (ret)
20478c2ecf20Sopenharmony_ci		pkt_dump_sense(pd, &cgc);
20488c2ecf20Sopenharmony_ci	return ret;
20498c2ecf20Sopenharmony_ci}
20508c2ecf20Sopenharmony_ci
20518c2ecf20Sopenharmony_cistatic int pkt_open_write(struct pktcdvd_device *pd)
20528c2ecf20Sopenharmony_ci{
20538c2ecf20Sopenharmony_ci	int ret;
20548c2ecf20Sopenharmony_ci	unsigned int write_speed, media_write_speed, read_speed;
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_ci	ret = pkt_probe_settings(pd);
20578c2ecf20Sopenharmony_ci	if (ret) {
20588c2ecf20Sopenharmony_ci		pkt_dbg(2, pd, "failed probe\n");
20598c2ecf20Sopenharmony_ci		return ret;
20608c2ecf20Sopenharmony_ci	}
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci	ret = pkt_set_write_settings(pd);
20638c2ecf20Sopenharmony_ci	if (ret) {
20648c2ecf20Sopenharmony_ci		pkt_dbg(1, pd, "failed saving write settings\n");
20658c2ecf20Sopenharmony_ci		return -EIO;
20668c2ecf20Sopenharmony_ci	}
20678c2ecf20Sopenharmony_ci
20688c2ecf20Sopenharmony_ci	pkt_write_caching(pd, USE_WCACHING);
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci	ret = pkt_get_max_speed(pd, &write_speed);
20718c2ecf20Sopenharmony_ci	if (ret)
20728c2ecf20Sopenharmony_ci		write_speed = 16 * 177;
20738c2ecf20Sopenharmony_ci	switch (pd->mmc3_profile) {
20748c2ecf20Sopenharmony_ci		case 0x13: /* DVD-RW */
20758c2ecf20Sopenharmony_ci		case 0x1a: /* DVD+RW */
20768c2ecf20Sopenharmony_ci		case 0x12: /* DVD-RAM */
20778c2ecf20Sopenharmony_ci			pkt_dbg(1, pd, "write speed %ukB/s\n", write_speed);
20788c2ecf20Sopenharmony_ci			break;
20798c2ecf20Sopenharmony_ci		default:
20808c2ecf20Sopenharmony_ci			ret = pkt_media_speed(pd, &media_write_speed);
20818c2ecf20Sopenharmony_ci			if (ret)
20828c2ecf20Sopenharmony_ci				media_write_speed = 16;
20838c2ecf20Sopenharmony_ci			write_speed = min(write_speed, media_write_speed * 177);
20848c2ecf20Sopenharmony_ci			pkt_dbg(1, pd, "write speed %ux\n", write_speed / 176);
20858c2ecf20Sopenharmony_ci			break;
20868c2ecf20Sopenharmony_ci	}
20878c2ecf20Sopenharmony_ci	read_speed = write_speed;
20888c2ecf20Sopenharmony_ci
20898c2ecf20Sopenharmony_ci	ret = pkt_set_speed(pd, write_speed, read_speed);
20908c2ecf20Sopenharmony_ci	if (ret) {
20918c2ecf20Sopenharmony_ci		pkt_dbg(1, pd, "couldn't set write speed\n");
20928c2ecf20Sopenharmony_ci		return -EIO;
20938c2ecf20Sopenharmony_ci	}
20948c2ecf20Sopenharmony_ci	pd->write_speed = write_speed;
20958c2ecf20Sopenharmony_ci	pd->read_speed = read_speed;
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_ci	ret = pkt_perform_opc(pd);
20988c2ecf20Sopenharmony_ci	if (ret) {
20998c2ecf20Sopenharmony_ci		pkt_dbg(1, pd, "Optimum Power Calibration failed\n");
21008c2ecf20Sopenharmony_ci	}
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	return 0;
21038c2ecf20Sopenharmony_ci}
21048c2ecf20Sopenharmony_ci
21058c2ecf20Sopenharmony_ci/*
21068c2ecf20Sopenharmony_ci * called at open time.
21078c2ecf20Sopenharmony_ci */
21088c2ecf20Sopenharmony_cistatic int pkt_open_dev(struct pktcdvd_device *pd, fmode_t write)
21098c2ecf20Sopenharmony_ci{
21108c2ecf20Sopenharmony_ci	int ret;
21118c2ecf20Sopenharmony_ci	long lba;
21128c2ecf20Sopenharmony_ci	struct request_queue *q;
21138c2ecf20Sopenharmony_ci	struct block_device *bdev;
21148c2ecf20Sopenharmony_ci
21158c2ecf20Sopenharmony_ci	/*
21168c2ecf20Sopenharmony_ci	 * We need to re-open the cdrom device without O_NONBLOCK to be able
21178c2ecf20Sopenharmony_ci	 * to read/write from/to it. It is already opened in O_NONBLOCK mode
21188c2ecf20Sopenharmony_ci	 * so open should not fail.
21198c2ecf20Sopenharmony_ci	 */
21208c2ecf20Sopenharmony_ci	bdev = blkdev_get_by_dev(pd->bdev->bd_dev, FMODE_READ | FMODE_EXCL, pd);
21218c2ecf20Sopenharmony_ci	if (IS_ERR(bdev)) {
21228c2ecf20Sopenharmony_ci		ret = PTR_ERR(bdev);
21238c2ecf20Sopenharmony_ci		goto out;
21248c2ecf20Sopenharmony_ci	}
21258c2ecf20Sopenharmony_ci
21268c2ecf20Sopenharmony_ci	ret = pkt_get_last_written(pd, &lba);
21278c2ecf20Sopenharmony_ci	if (ret) {
21288c2ecf20Sopenharmony_ci		pkt_err(pd, "pkt_get_last_written failed\n");
21298c2ecf20Sopenharmony_ci		goto out_putdev;
21308c2ecf20Sopenharmony_ci	}
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_ci	set_capacity(pd->disk, lba << 2);
21338c2ecf20Sopenharmony_ci	set_capacity(pd->bdev->bd_disk, lba << 2);
21348c2ecf20Sopenharmony_ci	bd_set_nr_sectors(pd->bdev, lba << 2);
21358c2ecf20Sopenharmony_ci
21368c2ecf20Sopenharmony_ci	q = bdev_get_queue(pd->bdev);
21378c2ecf20Sopenharmony_ci	if (write) {
21388c2ecf20Sopenharmony_ci		ret = pkt_open_write(pd);
21398c2ecf20Sopenharmony_ci		if (ret)
21408c2ecf20Sopenharmony_ci			goto out_putdev;
21418c2ecf20Sopenharmony_ci		/*
21428c2ecf20Sopenharmony_ci		 * Some CDRW drives can not handle writes larger than one packet,
21438c2ecf20Sopenharmony_ci		 * even if the size is a multiple of the packet size.
21448c2ecf20Sopenharmony_ci		 */
21458c2ecf20Sopenharmony_ci		blk_queue_max_hw_sectors(q, pd->settings.size);
21468c2ecf20Sopenharmony_ci		set_bit(PACKET_WRITABLE, &pd->flags);
21478c2ecf20Sopenharmony_ci	} else {
21488c2ecf20Sopenharmony_ci		pkt_set_speed(pd, MAX_SPEED, MAX_SPEED);
21498c2ecf20Sopenharmony_ci		clear_bit(PACKET_WRITABLE, &pd->flags);
21508c2ecf20Sopenharmony_ci	}
21518c2ecf20Sopenharmony_ci
21528c2ecf20Sopenharmony_ci	ret = pkt_set_segment_merging(pd, q);
21538c2ecf20Sopenharmony_ci	if (ret)
21548c2ecf20Sopenharmony_ci		goto out_putdev;
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci	if (write) {
21578c2ecf20Sopenharmony_ci		if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) {
21588c2ecf20Sopenharmony_ci			pkt_err(pd, "not enough memory for buffers\n");
21598c2ecf20Sopenharmony_ci			ret = -ENOMEM;
21608c2ecf20Sopenharmony_ci			goto out_putdev;
21618c2ecf20Sopenharmony_ci		}
21628c2ecf20Sopenharmony_ci		pkt_info(pd, "%lukB available on disc\n", lba << 1);
21638c2ecf20Sopenharmony_ci	}
21648c2ecf20Sopenharmony_ci
21658c2ecf20Sopenharmony_ci	return 0;
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ciout_putdev:
21688c2ecf20Sopenharmony_ci	blkdev_put(bdev, FMODE_READ | FMODE_EXCL);
21698c2ecf20Sopenharmony_ciout:
21708c2ecf20Sopenharmony_ci	return ret;
21718c2ecf20Sopenharmony_ci}
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci/*
21748c2ecf20Sopenharmony_ci * called when the device is closed. makes sure that the device flushes
21758c2ecf20Sopenharmony_ci * the internal cache before we close.
21768c2ecf20Sopenharmony_ci */
21778c2ecf20Sopenharmony_cistatic void pkt_release_dev(struct pktcdvd_device *pd, int flush)
21788c2ecf20Sopenharmony_ci{
21798c2ecf20Sopenharmony_ci	if (flush && pkt_flush_cache(pd))
21808c2ecf20Sopenharmony_ci		pkt_dbg(1, pd, "not flushing cache\n");
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_ci	pkt_lock_door(pd, 0);
21838c2ecf20Sopenharmony_ci
21848c2ecf20Sopenharmony_ci	pkt_set_speed(pd, MAX_SPEED, MAX_SPEED);
21858c2ecf20Sopenharmony_ci	blkdev_put(pd->bdev, FMODE_READ | FMODE_EXCL);
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_ci	pkt_shrink_pktlist(pd);
21888c2ecf20Sopenharmony_ci}
21898c2ecf20Sopenharmony_ci
21908c2ecf20Sopenharmony_cistatic struct pktcdvd_device *pkt_find_dev_from_minor(unsigned int dev_minor)
21918c2ecf20Sopenharmony_ci{
21928c2ecf20Sopenharmony_ci	if (dev_minor >= MAX_WRITERS)
21938c2ecf20Sopenharmony_ci		return NULL;
21948c2ecf20Sopenharmony_ci
21958c2ecf20Sopenharmony_ci	dev_minor = array_index_nospec(dev_minor, MAX_WRITERS);
21968c2ecf20Sopenharmony_ci	return pkt_devs[dev_minor];
21978c2ecf20Sopenharmony_ci}
21988c2ecf20Sopenharmony_ci
21998c2ecf20Sopenharmony_cistatic int pkt_open(struct block_device *bdev, fmode_t mode)
22008c2ecf20Sopenharmony_ci{
22018c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd = NULL;
22028c2ecf20Sopenharmony_ci	int ret;
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_ci	mutex_lock(&pktcdvd_mutex);
22058c2ecf20Sopenharmony_ci	mutex_lock(&ctl_mutex);
22068c2ecf20Sopenharmony_ci	pd = pkt_find_dev_from_minor(MINOR(bdev->bd_dev));
22078c2ecf20Sopenharmony_ci	if (!pd) {
22088c2ecf20Sopenharmony_ci		ret = -ENODEV;
22098c2ecf20Sopenharmony_ci		goto out;
22108c2ecf20Sopenharmony_ci	}
22118c2ecf20Sopenharmony_ci	BUG_ON(pd->refcnt < 0);
22128c2ecf20Sopenharmony_ci
22138c2ecf20Sopenharmony_ci	pd->refcnt++;
22148c2ecf20Sopenharmony_ci	if (pd->refcnt > 1) {
22158c2ecf20Sopenharmony_ci		if ((mode & FMODE_WRITE) &&
22168c2ecf20Sopenharmony_ci		    !test_bit(PACKET_WRITABLE, &pd->flags)) {
22178c2ecf20Sopenharmony_ci			ret = -EBUSY;
22188c2ecf20Sopenharmony_ci			goto out_dec;
22198c2ecf20Sopenharmony_ci		}
22208c2ecf20Sopenharmony_ci	} else {
22218c2ecf20Sopenharmony_ci		ret = pkt_open_dev(pd, mode & FMODE_WRITE);
22228c2ecf20Sopenharmony_ci		if (ret)
22238c2ecf20Sopenharmony_ci			goto out_dec;
22248c2ecf20Sopenharmony_ci		/*
22258c2ecf20Sopenharmony_ci		 * needed here as well, since ext2 (among others) may change
22268c2ecf20Sopenharmony_ci		 * the blocksize at mount time
22278c2ecf20Sopenharmony_ci		 */
22288c2ecf20Sopenharmony_ci		set_blocksize(bdev, CD_FRAMESIZE);
22298c2ecf20Sopenharmony_ci	}
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_ci	mutex_unlock(&ctl_mutex);
22328c2ecf20Sopenharmony_ci	mutex_unlock(&pktcdvd_mutex);
22338c2ecf20Sopenharmony_ci	return 0;
22348c2ecf20Sopenharmony_ci
22358c2ecf20Sopenharmony_ciout_dec:
22368c2ecf20Sopenharmony_ci	pd->refcnt--;
22378c2ecf20Sopenharmony_ciout:
22388c2ecf20Sopenharmony_ci	mutex_unlock(&ctl_mutex);
22398c2ecf20Sopenharmony_ci	mutex_unlock(&pktcdvd_mutex);
22408c2ecf20Sopenharmony_ci	return ret;
22418c2ecf20Sopenharmony_ci}
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_cistatic void pkt_close(struct gendisk *disk, fmode_t mode)
22448c2ecf20Sopenharmony_ci{
22458c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd = disk->private_data;
22468c2ecf20Sopenharmony_ci
22478c2ecf20Sopenharmony_ci	mutex_lock(&pktcdvd_mutex);
22488c2ecf20Sopenharmony_ci	mutex_lock(&ctl_mutex);
22498c2ecf20Sopenharmony_ci	pd->refcnt--;
22508c2ecf20Sopenharmony_ci	BUG_ON(pd->refcnt < 0);
22518c2ecf20Sopenharmony_ci	if (pd->refcnt == 0) {
22528c2ecf20Sopenharmony_ci		int flush = test_bit(PACKET_WRITABLE, &pd->flags);
22538c2ecf20Sopenharmony_ci		pkt_release_dev(pd, flush);
22548c2ecf20Sopenharmony_ci	}
22558c2ecf20Sopenharmony_ci	mutex_unlock(&ctl_mutex);
22568c2ecf20Sopenharmony_ci	mutex_unlock(&pktcdvd_mutex);
22578c2ecf20Sopenharmony_ci}
22588c2ecf20Sopenharmony_ci
22598c2ecf20Sopenharmony_ci
22608c2ecf20Sopenharmony_cistatic void pkt_end_io_read_cloned(struct bio *bio)
22618c2ecf20Sopenharmony_ci{
22628c2ecf20Sopenharmony_ci	struct packet_stacked_data *psd = bio->bi_private;
22638c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd = psd->pd;
22648c2ecf20Sopenharmony_ci
22658c2ecf20Sopenharmony_ci	psd->bio->bi_status = bio->bi_status;
22668c2ecf20Sopenharmony_ci	bio_put(bio);
22678c2ecf20Sopenharmony_ci	bio_endio(psd->bio);
22688c2ecf20Sopenharmony_ci	mempool_free(psd, &psd_pool);
22698c2ecf20Sopenharmony_ci	pkt_bio_finished(pd);
22708c2ecf20Sopenharmony_ci}
22718c2ecf20Sopenharmony_ci
22728c2ecf20Sopenharmony_cistatic void pkt_make_request_read(struct pktcdvd_device *pd, struct bio *bio)
22738c2ecf20Sopenharmony_ci{
22748c2ecf20Sopenharmony_ci	struct bio *cloned_bio = bio_clone_fast(bio, GFP_NOIO, &pkt_bio_set);
22758c2ecf20Sopenharmony_ci	struct packet_stacked_data *psd = mempool_alloc(&psd_pool, GFP_NOIO);
22768c2ecf20Sopenharmony_ci
22778c2ecf20Sopenharmony_ci	psd->pd = pd;
22788c2ecf20Sopenharmony_ci	psd->bio = bio;
22798c2ecf20Sopenharmony_ci	bio_set_dev(cloned_bio, pd->bdev);
22808c2ecf20Sopenharmony_ci	cloned_bio->bi_private = psd;
22818c2ecf20Sopenharmony_ci	cloned_bio->bi_end_io = pkt_end_io_read_cloned;
22828c2ecf20Sopenharmony_ci	pd->stats.secs_r += bio_sectors(bio);
22838c2ecf20Sopenharmony_ci	pkt_queue_bio(pd, cloned_bio);
22848c2ecf20Sopenharmony_ci}
22858c2ecf20Sopenharmony_ci
22868c2ecf20Sopenharmony_cistatic void pkt_make_request_write(struct request_queue *q, struct bio *bio)
22878c2ecf20Sopenharmony_ci{
22888c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd = q->queuedata;
22898c2ecf20Sopenharmony_ci	sector_t zone;
22908c2ecf20Sopenharmony_ci	struct packet_data *pkt;
22918c2ecf20Sopenharmony_ci	int was_empty, blocked_bio;
22928c2ecf20Sopenharmony_ci	struct pkt_rb_node *node;
22938c2ecf20Sopenharmony_ci
22948c2ecf20Sopenharmony_ci	zone = get_zone(bio->bi_iter.bi_sector, pd);
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_ci	/*
22978c2ecf20Sopenharmony_ci	 * If we find a matching packet in state WAITING or READ_WAIT, we can
22988c2ecf20Sopenharmony_ci	 * just append this bio to that packet.
22998c2ecf20Sopenharmony_ci	 */
23008c2ecf20Sopenharmony_ci	spin_lock(&pd->cdrw.active_list_lock);
23018c2ecf20Sopenharmony_ci	blocked_bio = 0;
23028c2ecf20Sopenharmony_ci	list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
23038c2ecf20Sopenharmony_ci		if (pkt->sector == zone) {
23048c2ecf20Sopenharmony_ci			spin_lock(&pkt->lock);
23058c2ecf20Sopenharmony_ci			if ((pkt->state == PACKET_WAITING_STATE) ||
23068c2ecf20Sopenharmony_ci			    (pkt->state == PACKET_READ_WAIT_STATE)) {
23078c2ecf20Sopenharmony_ci				bio_list_add(&pkt->orig_bios, bio);
23088c2ecf20Sopenharmony_ci				pkt->write_size +=
23098c2ecf20Sopenharmony_ci					bio->bi_iter.bi_size / CD_FRAMESIZE;
23108c2ecf20Sopenharmony_ci				if ((pkt->write_size >= pkt->frames) &&
23118c2ecf20Sopenharmony_ci				    (pkt->state == PACKET_WAITING_STATE)) {
23128c2ecf20Sopenharmony_ci					atomic_inc(&pkt->run_sm);
23138c2ecf20Sopenharmony_ci					wake_up(&pd->wqueue);
23148c2ecf20Sopenharmony_ci				}
23158c2ecf20Sopenharmony_ci				spin_unlock(&pkt->lock);
23168c2ecf20Sopenharmony_ci				spin_unlock(&pd->cdrw.active_list_lock);
23178c2ecf20Sopenharmony_ci				return;
23188c2ecf20Sopenharmony_ci			} else {
23198c2ecf20Sopenharmony_ci				blocked_bio = 1;
23208c2ecf20Sopenharmony_ci			}
23218c2ecf20Sopenharmony_ci			spin_unlock(&pkt->lock);
23228c2ecf20Sopenharmony_ci		}
23238c2ecf20Sopenharmony_ci	}
23248c2ecf20Sopenharmony_ci	spin_unlock(&pd->cdrw.active_list_lock);
23258c2ecf20Sopenharmony_ci
23268c2ecf20Sopenharmony_ci 	/*
23278c2ecf20Sopenharmony_ci	 * Test if there is enough room left in the bio work queue
23288c2ecf20Sopenharmony_ci	 * (queue size >= congestion on mark).
23298c2ecf20Sopenharmony_ci	 * If not, wait till the work queue size is below the congestion off mark.
23308c2ecf20Sopenharmony_ci	 */
23318c2ecf20Sopenharmony_ci	spin_lock(&pd->lock);
23328c2ecf20Sopenharmony_ci	if (pd->write_congestion_on > 0
23338c2ecf20Sopenharmony_ci	    && pd->bio_queue_size >= pd->write_congestion_on) {
23348c2ecf20Sopenharmony_ci		set_bdi_congested(q->backing_dev_info, BLK_RW_ASYNC);
23358c2ecf20Sopenharmony_ci		do {
23368c2ecf20Sopenharmony_ci			spin_unlock(&pd->lock);
23378c2ecf20Sopenharmony_ci			congestion_wait(BLK_RW_ASYNC, HZ);
23388c2ecf20Sopenharmony_ci			spin_lock(&pd->lock);
23398c2ecf20Sopenharmony_ci		} while(pd->bio_queue_size > pd->write_congestion_off);
23408c2ecf20Sopenharmony_ci	}
23418c2ecf20Sopenharmony_ci	spin_unlock(&pd->lock);
23428c2ecf20Sopenharmony_ci
23438c2ecf20Sopenharmony_ci	/*
23448c2ecf20Sopenharmony_ci	 * No matching packet found. Store the bio in the work queue.
23458c2ecf20Sopenharmony_ci	 */
23468c2ecf20Sopenharmony_ci	node = mempool_alloc(&pd->rb_pool, GFP_NOIO);
23478c2ecf20Sopenharmony_ci	node->bio = bio;
23488c2ecf20Sopenharmony_ci	spin_lock(&pd->lock);
23498c2ecf20Sopenharmony_ci	BUG_ON(pd->bio_queue_size < 0);
23508c2ecf20Sopenharmony_ci	was_empty = (pd->bio_queue_size == 0);
23518c2ecf20Sopenharmony_ci	pkt_rbtree_insert(pd, node);
23528c2ecf20Sopenharmony_ci	spin_unlock(&pd->lock);
23538c2ecf20Sopenharmony_ci
23548c2ecf20Sopenharmony_ci	/*
23558c2ecf20Sopenharmony_ci	 * Wake up the worker thread.
23568c2ecf20Sopenharmony_ci	 */
23578c2ecf20Sopenharmony_ci	atomic_set(&pd->scan_queue, 1);
23588c2ecf20Sopenharmony_ci	if (was_empty) {
23598c2ecf20Sopenharmony_ci		/* This wake_up is required for correct operation */
23608c2ecf20Sopenharmony_ci		wake_up(&pd->wqueue);
23618c2ecf20Sopenharmony_ci	} else if (!list_empty(&pd->cdrw.pkt_free_list) && !blocked_bio) {
23628c2ecf20Sopenharmony_ci		/*
23638c2ecf20Sopenharmony_ci		 * This wake up is not required for correct operation,
23648c2ecf20Sopenharmony_ci		 * but improves performance in some cases.
23658c2ecf20Sopenharmony_ci		 */
23668c2ecf20Sopenharmony_ci		wake_up(&pd->wqueue);
23678c2ecf20Sopenharmony_ci	}
23688c2ecf20Sopenharmony_ci}
23698c2ecf20Sopenharmony_ci
23708c2ecf20Sopenharmony_cistatic blk_qc_t pkt_submit_bio(struct bio *bio)
23718c2ecf20Sopenharmony_ci{
23728c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd;
23738c2ecf20Sopenharmony_ci	char b[BDEVNAME_SIZE];
23748c2ecf20Sopenharmony_ci	struct bio *split;
23758c2ecf20Sopenharmony_ci
23768c2ecf20Sopenharmony_ci	blk_queue_split(&bio);
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci	pd = bio->bi_disk->queue->queuedata;
23798c2ecf20Sopenharmony_ci	if (!pd) {
23808c2ecf20Sopenharmony_ci		pr_err("%s incorrect request queue\n", bio_devname(bio, b));
23818c2ecf20Sopenharmony_ci		goto end_io;
23828c2ecf20Sopenharmony_ci	}
23838c2ecf20Sopenharmony_ci
23848c2ecf20Sopenharmony_ci	pkt_dbg(2, pd, "start = %6llx stop = %6llx\n",
23858c2ecf20Sopenharmony_ci		(unsigned long long)bio->bi_iter.bi_sector,
23868c2ecf20Sopenharmony_ci		(unsigned long long)bio_end_sector(bio));
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_ci	/*
23898c2ecf20Sopenharmony_ci	 * Clone READ bios so we can have our own bi_end_io callback.
23908c2ecf20Sopenharmony_ci	 */
23918c2ecf20Sopenharmony_ci	if (bio_data_dir(bio) == READ) {
23928c2ecf20Sopenharmony_ci		pkt_make_request_read(pd, bio);
23938c2ecf20Sopenharmony_ci		return BLK_QC_T_NONE;
23948c2ecf20Sopenharmony_ci	}
23958c2ecf20Sopenharmony_ci
23968c2ecf20Sopenharmony_ci	if (!test_bit(PACKET_WRITABLE, &pd->flags)) {
23978c2ecf20Sopenharmony_ci		pkt_notice(pd, "WRITE for ro device (%llu)\n",
23988c2ecf20Sopenharmony_ci			   (unsigned long long)bio->bi_iter.bi_sector);
23998c2ecf20Sopenharmony_ci		goto end_io;
24008c2ecf20Sopenharmony_ci	}
24018c2ecf20Sopenharmony_ci
24028c2ecf20Sopenharmony_ci	if (!bio->bi_iter.bi_size || (bio->bi_iter.bi_size % CD_FRAMESIZE)) {
24038c2ecf20Sopenharmony_ci		pkt_err(pd, "wrong bio size\n");
24048c2ecf20Sopenharmony_ci		goto end_io;
24058c2ecf20Sopenharmony_ci	}
24068c2ecf20Sopenharmony_ci
24078c2ecf20Sopenharmony_ci	do {
24088c2ecf20Sopenharmony_ci		sector_t zone = get_zone(bio->bi_iter.bi_sector, pd);
24098c2ecf20Sopenharmony_ci		sector_t last_zone = get_zone(bio_end_sector(bio) - 1, pd);
24108c2ecf20Sopenharmony_ci
24118c2ecf20Sopenharmony_ci		if (last_zone != zone) {
24128c2ecf20Sopenharmony_ci			BUG_ON(last_zone != zone + pd->settings.size);
24138c2ecf20Sopenharmony_ci
24148c2ecf20Sopenharmony_ci			split = bio_split(bio, last_zone -
24158c2ecf20Sopenharmony_ci					  bio->bi_iter.bi_sector,
24168c2ecf20Sopenharmony_ci					  GFP_NOIO, &pkt_bio_set);
24178c2ecf20Sopenharmony_ci			bio_chain(split, bio);
24188c2ecf20Sopenharmony_ci		} else {
24198c2ecf20Sopenharmony_ci			split = bio;
24208c2ecf20Sopenharmony_ci		}
24218c2ecf20Sopenharmony_ci
24228c2ecf20Sopenharmony_ci		pkt_make_request_write(bio->bi_disk->queue, split);
24238c2ecf20Sopenharmony_ci	} while (split != bio);
24248c2ecf20Sopenharmony_ci
24258c2ecf20Sopenharmony_ci	return BLK_QC_T_NONE;
24268c2ecf20Sopenharmony_ciend_io:
24278c2ecf20Sopenharmony_ci	bio_io_error(bio);
24288c2ecf20Sopenharmony_ci	return BLK_QC_T_NONE;
24298c2ecf20Sopenharmony_ci}
24308c2ecf20Sopenharmony_ci
24318c2ecf20Sopenharmony_cistatic void pkt_init_queue(struct pktcdvd_device *pd)
24328c2ecf20Sopenharmony_ci{
24338c2ecf20Sopenharmony_ci	struct request_queue *q = pd->disk->queue;
24348c2ecf20Sopenharmony_ci
24358c2ecf20Sopenharmony_ci	blk_queue_logical_block_size(q, CD_FRAMESIZE);
24368c2ecf20Sopenharmony_ci	blk_queue_max_hw_sectors(q, PACKET_MAX_SECTORS);
24378c2ecf20Sopenharmony_ci	q->queuedata = pd;
24388c2ecf20Sopenharmony_ci}
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_cistatic int pkt_seq_show(struct seq_file *m, void *p)
24418c2ecf20Sopenharmony_ci{
24428c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd = m->private;
24438c2ecf20Sopenharmony_ci	char *msg;
24448c2ecf20Sopenharmony_ci	char bdev_buf[BDEVNAME_SIZE];
24458c2ecf20Sopenharmony_ci	int states[PACKET_NUM_STATES];
24468c2ecf20Sopenharmony_ci
24478c2ecf20Sopenharmony_ci	seq_printf(m, "Writer %s mapped to %s:\n", pd->name,
24488c2ecf20Sopenharmony_ci		   bdevname(pd->bdev, bdev_buf));
24498c2ecf20Sopenharmony_ci
24508c2ecf20Sopenharmony_ci	seq_printf(m, "\nSettings:\n");
24518c2ecf20Sopenharmony_ci	seq_printf(m, "\tpacket size:\t\t%dkB\n", pd->settings.size / 2);
24528c2ecf20Sopenharmony_ci
24538c2ecf20Sopenharmony_ci	if (pd->settings.write_type == 0)
24548c2ecf20Sopenharmony_ci		msg = "Packet";
24558c2ecf20Sopenharmony_ci	else
24568c2ecf20Sopenharmony_ci		msg = "Unknown";
24578c2ecf20Sopenharmony_ci	seq_printf(m, "\twrite type:\t\t%s\n", msg);
24588c2ecf20Sopenharmony_ci
24598c2ecf20Sopenharmony_ci	seq_printf(m, "\tpacket type:\t\t%s\n", pd->settings.fp ? "Fixed" : "Variable");
24608c2ecf20Sopenharmony_ci	seq_printf(m, "\tlink loss:\t\t%d\n", pd->settings.link_loss);
24618c2ecf20Sopenharmony_ci
24628c2ecf20Sopenharmony_ci	seq_printf(m, "\ttrack mode:\t\t%d\n", pd->settings.track_mode);
24638c2ecf20Sopenharmony_ci
24648c2ecf20Sopenharmony_ci	if (pd->settings.block_mode == PACKET_BLOCK_MODE1)
24658c2ecf20Sopenharmony_ci		msg = "Mode 1";
24668c2ecf20Sopenharmony_ci	else if (pd->settings.block_mode == PACKET_BLOCK_MODE2)
24678c2ecf20Sopenharmony_ci		msg = "Mode 2";
24688c2ecf20Sopenharmony_ci	else
24698c2ecf20Sopenharmony_ci		msg = "Unknown";
24708c2ecf20Sopenharmony_ci	seq_printf(m, "\tblock mode:\t\t%s\n", msg);
24718c2ecf20Sopenharmony_ci
24728c2ecf20Sopenharmony_ci	seq_printf(m, "\nStatistics:\n");
24738c2ecf20Sopenharmony_ci	seq_printf(m, "\tpackets started:\t%lu\n", pd->stats.pkt_started);
24748c2ecf20Sopenharmony_ci	seq_printf(m, "\tpackets ended:\t\t%lu\n", pd->stats.pkt_ended);
24758c2ecf20Sopenharmony_ci	seq_printf(m, "\twritten:\t\t%lukB\n", pd->stats.secs_w >> 1);
24768c2ecf20Sopenharmony_ci	seq_printf(m, "\tread gather:\t\t%lukB\n", pd->stats.secs_rg >> 1);
24778c2ecf20Sopenharmony_ci	seq_printf(m, "\tread:\t\t\t%lukB\n", pd->stats.secs_r >> 1);
24788c2ecf20Sopenharmony_ci
24798c2ecf20Sopenharmony_ci	seq_printf(m, "\nMisc:\n");
24808c2ecf20Sopenharmony_ci	seq_printf(m, "\treference count:\t%d\n", pd->refcnt);
24818c2ecf20Sopenharmony_ci	seq_printf(m, "\tflags:\t\t\t0x%lx\n", pd->flags);
24828c2ecf20Sopenharmony_ci	seq_printf(m, "\tread speed:\t\t%ukB/s\n", pd->read_speed);
24838c2ecf20Sopenharmony_ci	seq_printf(m, "\twrite speed:\t\t%ukB/s\n", pd->write_speed);
24848c2ecf20Sopenharmony_ci	seq_printf(m, "\tstart offset:\t\t%lu\n", pd->offset);
24858c2ecf20Sopenharmony_ci	seq_printf(m, "\tmode page offset:\t%u\n", pd->mode_offset);
24868c2ecf20Sopenharmony_ci
24878c2ecf20Sopenharmony_ci	seq_printf(m, "\nQueue state:\n");
24888c2ecf20Sopenharmony_ci	seq_printf(m, "\tbios queued:\t\t%d\n", pd->bio_queue_size);
24898c2ecf20Sopenharmony_ci	seq_printf(m, "\tbios pending:\t\t%d\n", atomic_read(&pd->cdrw.pending_bios));
24908c2ecf20Sopenharmony_ci	seq_printf(m, "\tcurrent sector:\t\t0x%llx\n", (unsigned long long)pd->current_sector);
24918c2ecf20Sopenharmony_ci
24928c2ecf20Sopenharmony_ci	pkt_count_states(pd, states);
24938c2ecf20Sopenharmony_ci	seq_printf(m, "\tstate:\t\t\ti:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n",
24948c2ecf20Sopenharmony_ci		   states[0], states[1], states[2], states[3], states[4], states[5]);
24958c2ecf20Sopenharmony_ci
24968c2ecf20Sopenharmony_ci	seq_printf(m, "\twrite congestion marks:\toff=%d on=%d\n",
24978c2ecf20Sopenharmony_ci			pd->write_congestion_off,
24988c2ecf20Sopenharmony_ci			pd->write_congestion_on);
24998c2ecf20Sopenharmony_ci	return 0;
25008c2ecf20Sopenharmony_ci}
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_cistatic int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
25038c2ecf20Sopenharmony_ci{
25048c2ecf20Sopenharmony_ci	int i;
25058c2ecf20Sopenharmony_ci	char b[BDEVNAME_SIZE];
25068c2ecf20Sopenharmony_ci	struct block_device *bdev;
25078c2ecf20Sopenharmony_ci
25088c2ecf20Sopenharmony_ci	if (pd->pkt_dev == dev) {
25098c2ecf20Sopenharmony_ci		pkt_err(pd, "recursive setup not allowed\n");
25108c2ecf20Sopenharmony_ci		return -EBUSY;
25118c2ecf20Sopenharmony_ci	}
25128c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_WRITERS; i++) {
25138c2ecf20Sopenharmony_ci		struct pktcdvd_device *pd2 = pkt_devs[i];
25148c2ecf20Sopenharmony_ci		if (!pd2)
25158c2ecf20Sopenharmony_ci			continue;
25168c2ecf20Sopenharmony_ci		if (pd2->bdev->bd_dev == dev) {
25178c2ecf20Sopenharmony_ci			pkt_err(pd, "%s already setup\n",
25188c2ecf20Sopenharmony_ci				bdevname(pd2->bdev, b));
25198c2ecf20Sopenharmony_ci			return -EBUSY;
25208c2ecf20Sopenharmony_ci		}
25218c2ecf20Sopenharmony_ci		if (pd2->pkt_dev == dev) {
25228c2ecf20Sopenharmony_ci			pkt_err(pd, "can't chain pktcdvd devices\n");
25238c2ecf20Sopenharmony_ci			return -EBUSY;
25248c2ecf20Sopenharmony_ci		}
25258c2ecf20Sopenharmony_ci	}
25268c2ecf20Sopenharmony_ci
25278c2ecf20Sopenharmony_ci	bdev = blkdev_get_by_dev(dev, FMODE_READ | FMODE_NDELAY, NULL);
25288c2ecf20Sopenharmony_ci	if (IS_ERR(bdev))
25298c2ecf20Sopenharmony_ci		return PTR_ERR(bdev);
25308c2ecf20Sopenharmony_ci	if (!blk_queue_scsi_passthrough(bdev_get_queue(bdev))) {
25318c2ecf20Sopenharmony_ci		blkdev_put(bdev, FMODE_READ | FMODE_NDELAY);
25328c2ecf20Sopenharmony_ci		return -EINVAL;
25338c2ecf20Sopenharmony_ci	}
25348c2ecf20Sopenharmony_ci
25358c2ecf20Sopenharmony_ci	/* This is safe, since we have a reference from open(). */
25368c2ecf20Sopenharmony_ci	__module_get(THIS_MODULE);
25378c2ecf20Sopenharmony_ci
25388c2ecf20Sopenharmony_ci	pd->bdev = bdev;
25398c2ecf20Sopenharmony_ci	set_blocksize(bdev, CD_FRAMESIZE);
25408c2ecf20Sopenharmony_ci
25418c2ecf20Sopenharmony_ci	pkt_init_queue(pd);
25428c2ecf20Sopenharmony_ci
25438c2ecf20Sopenharmony_ci	atomic_set(&pd->cdrw.pending_bios, 0);
25448c2ecf20Sopenharmony_ci	pd->cdrw.thread = kthread_run(kcdrwd, pd, "%s", pd->name);
25458c2ecf20Sopenharmony_ci	if (IS_ERR(pd->cdrw.thread)) {
25468c2ecf20Sopenharmony_ci		pkt_err(pd, "can't start kernel thread\n");
25478c2ecf20Sopenharmony_ci		goto out_mem;
25488c2ecf20Sopenharmony_ci	}
25498c2ecf20Sopenharmony_ci
25508c2ecf20Sopenharmony_ci	proc_create_single_data(pd->name, 0, pkt_proc, pkt_seq_show, pd);
25518c2ecf20Sopenharmony_ci	pkt_dbg(1, pd, "writer mapped to %s\n", bdevname(bdev, b));
25528c2ecf20Sopenharmony_ci	return 0;
25538c2ecf20Sopenharmony_ci
25548c2ecf20Sopenharmony_ciout_mem:
25558c2ecf20Sopenharmony_ci	blkdev_put(bdev, FMODE_READ | FMODE_NDELAY);
25568c2ecf20Sopenharmony_ci	/* This is safe: open() is still holding a reference. */
25578c2ecf20Sopenharmony_ci	module_put(THIS_MODULE);
25588c2ecf20Sopenharmony_ci	return -ENOMEM;
25598c2ecf20Sopenharmony_ci}
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_cistatic int pkt_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
25628c2ecf20Sopenharmony_ci{
25638c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd = bdev->bd_disk->private_data;
25648c2ecf20Sopenharmony_ci	int ret;
25658c2ecf20Sopenharmony_ci
25668c2ecf20Sopenharmony_ci	pkt_dbg(2, pd, "cmd %x, dev %d:%d\n",
25678c2ecf20Sopenharmony_ci		cmd, MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev));
25688c2ecf20Sopenharmony_ci
25698c2ecf20Sopenharmony_ci	mutex_lock(&pktcdvd_mutex);
25708c2ecf20Sopenharmony_ci	switch (cmd) {
25718c2ecf20Sopenharmony_ci	case CDROMEJECT:
25728c2ecf20Sopenharmony_ci		/*
25738c2ecf20Sopenharmony_ci		 * The door gets locked when the device is opened, so we
25748c2ecf20Sopenharmony_ci		 * have to unlock it or else the eject command fails.
25758c2ecf20Sopenharmony_ci		 */
25768c2ecf20Sopenharmony_ci		if (pd->refcnt == 1)
25778c2ecf20Sopenharmony_ci			pkt_lock_door(pd, 0);
25788c2ecf20Sopenharmony_ci		fallthrough;
25798c2ecf20Sopenharmony_ci	/*
25808c2ecf20Sopenharmony_ci	 * forward selected CDROM ioctls to CD-ROM, for UDF
25818c2ecf20Sopenharmony_ci	 */
25828c2ecf20Sopenharmony_ci	case CDROMMULTISESSION:
25838c2ecf20Sopenharmony_ci	case CDROMREADTOCENTRY:
25848c2ecf20Sopenharmony_ci	case CDROM_LAST_WRITTEN:
25858c2ecf20Sopenharmony_ci	case CDROM_SEND_PACKET:
25868c2ecf20Sopenharmony_ci	case SCSI_IOCTL_SEND_COMMAND:
25878c2ecf20Sopenharmony_ci		ret = __blkdev_driver_ioctl(pd->bdev, mode, cmd, arg);
25888c2ecf20Sopenharmony_ci		break;
25898c2ecf20Sopenharmony_ci
25908c2ecf20Sopenharmony_ci	default:
25918c2ecf20Sopenharmony_ci		pkt_dbg(2, pd, "Unknown ioctl (%x)\n", cmd);
25928c2ecf20Sopenharmony_ci		ret = -ENOTTY;
25938c2ecf20Sopenharmony_ci	}
25948c2ecf20Sopenharmony_ci	mutex_unlock(&pktcdvd_mutex);
25958c2ecf20Sopenharmony_ci
25968c2ecf20Sopenharmony_ci	return ret;
25978c2ecf20Sopenharmony_ci}
25988c2ecf20Sopenharmony_ci
25998c2ecf20Sopenharmony_cistatic unsigned int pkt_check_events(struct gendisk *disk,
26008c2ecf20Sopenharmony_ci				     unsigned int clearing)
26018c2ecf20Sopenharmony_ci{
26028c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd = disk->private_data;
26038c2ecf20Sopenharmony_ci	struct gendisk *attached_disk;
26048c2ecf20Sopenharmony_ci
26058c2ecf20Sopenharmony_ci	if (!pd)
26068c2ecf20Sopenharmony_ci		return 0;
26078c2ecf20Sopenharmony_ci	if (!pd->bdev)
26088c2ecf20Sopenharmony_ci		return 0;
26098c2ecf20Sopenharmony_ci	attached_disk = pd->bdev->bd_disk;
26108c2ecf20Sopenharmony_ci	if (!attached_disk || !attached_disk->fops->check_events)
26118c2ecf20Sopenharmony_ci		return 0;
26128c2ecf20Sopenharmony_ci	return attached_disk->fops->check_events(attached_disk, clearing);
26138c2ecf20Sopenharmony_ci}
26148c2ecf20Sopenharmony_ci
26158c2ecf20Sopenharmony_cistatic char *pkt_devnode(struct gendisk *disk, umode_t *mode)
26168c2ecf20Sopenharmony_ci{
26178c2ecf20Sopenharmony_ci	return kasprintf(GFP_KERNEL, "pktcdvd/%s", disk->disk_name);
26188c2ecf20Sopenharmony_ci}
26198c2ecf20Sopenharmony_ci
26208c2ecf20Sopenharmony_cistatic const struct block_device_operations pktcdvd_ops = {
26218c2ecf20Sopenharmony_ci	.owner =		THIS_MODULE,
26228c2ecf20Sopenharmony_ci	.submit_bio =		pkt_submit_bio,
26238c2ecf20Sopenharmony_ci	.open =			pkt_open,
26248c2ecf20Sopenharmony_ci	.release =		pkt_close,
26258c2ecf20Sopenharmony_ci	.ioctl =		pkt_ioctl,
26268c2ecf20Sopenharmony_ci	.compat_ioctl =		blkdev_compat_ptr_ioctl,
26278c2ecf20Sopenharmony_ci	.check_events =		pkt_check_events,
26288c2ecf20Sopenharmony_ci	.devnode =		pkt_devnode,
26298c2ecf20Sopenharmony_ci};
26308c2ecf20Sopenharmony_ci
26318c2ecf20Sopenharmony_ci/*
26328c2ecf20Sopenharmony_ci * Set up mapping from pktcdvd device to CD-ROM device.
26338c2ecf20Sopenharmony_ci */
26348c2ecf20Sopenharmony_cistatic int pkt_setup_dev(dev_t dev, dev_t* pkt_dev)
26358c2ecf20Sopenharmony_ci{
26368c2ecf20Sopenharmony_ci	int idx;
26378c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
26388c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd;
26398c2ecf20Sopenharmony_ci	struct gendisk *disk;
26408c2ecf20Sopenharmony_ci
26418c2ecf20Sopenharmony_ci	mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
26428c2ecf20Sopenharmony_ci
26438c2ecf20Sopenharmony_ci	for (idx = 0; idx < MAX_WRITERS; idx++)
26448c2ecf20Sopenharmony_ci		if (!pkt_devs[idx])
26458c2ecf20Sopenharmony_ci			break;
26468c2ecf20Sopenharmony_ci	if (idx == MAX_WRITERS) {
26478c2ecf20Sopenharmony_ci		pr_err("max %d writers supported\n", MAX_WRITERS);
26488c2ecf20Sopenharmony_ci		ret = -EBUSY;
26498c2ecf20Sopenharmony_ci		goto out_mutex;
26508c2ecf20Sopenharmony_ci	}
26518c2ecf20Sopenharmony_ci
26528c2ecf20Sopenharmony_ci	pd = kzalloc(sizeof(struct pktcdvd_device), GFP_KERNEL);
26538c2ecf20Sopenharmony_ci	if (!pd)
26548c2ecf20Sopenharmony_ci		goto out_mutex;
26558c2ecf20Sopenharmony_ci
26568c2ecf20Sopenharmony_ci	ret = mempool_init_kmalloc_pool(&pd->rb_pool, PKT_RB_POOL_SIZE,
26578c2ecf20Sopenharmony_ci					sizeof(struct pkt_rb_node));
26588c2ecf20Sopenharmony_ci	if (ret)
26598c2ecf20Sopenharmony_ci		goto out_mem;
26608c2ecf20Sopenharmony_ci
26618c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
26628c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&pd->cdrw.pkt_active_list);
26638c2ecf20Sopenharmony_ci	spin_lock_init(&pd->cdrw.active_list_lock);
26648c2ecf20Sopenharmony_ci
26658c2ecf20Sopenharmony_ci	spin_lock_init(&pd->lock);
26668c2ecf20Sopenharmony_ci	spin_lock_init(&pd->iosched.lock);
26678c2ecf20Sopenharmony_ci	bio_list_init(&pd->iosched.read_queue);
26688c2ecf20Sopenharmony_ci	bio_list_init(&pd->iosched.write_queue);
26698c2ecf20Sopenharmony_ci	sprintf(pd->name, DRIVER_NAME"%d", idx);
26708c2ecf20Sopenharmony_ci	init_waitqueue_head(&pd->wqueue);
26718c2ecf20Sopenharmony_ci	pd->bio_queue = RB_ROOT;
26728c2ecf20Sopenharmony_ci
26738c2ecf20Sopenharmony_ci	pd->write_congestion_on  = write_congestion_on;
26748c2ecf20Sopenharmony_ci	pd->write_congestion_off = write_congestion_off;
26758c2ecf20Sopenharmony_ci
26768c2ecf20Sopenharmony_ci	ret = -ENOMEM;
26778c2ecf20Sopenharmony_ci	disk = alloc_disk(1);
26788c2ecf20Sopenharmony_ci	if (!disk)
26798c2ecf20Sopenharmony_ci		goto out_mem;
26808c2ecf20Sopenharmony_ci	pd->disk = disk;
26818c2ecf20Sopenharmony_ci	disk->major = pktdev_major;
26828c2ecf20Sopenharmony_ci	disk->first_minor = idx;
26838c2ecf20Sopenharmony_ci	disk->fops = &pktcdvd_ops;
26848c2ecf20Sopenharmony_ci	disk->flags = GENHD_FL_REMOVABLE;
26858c2ecf20Sopenharmony_ci	strcpy(disk->disk_name, pd->name);
26868c2ecf20Sopenharmony_ci	disk->private_data = pd;
26878c2ecf20Sopenharmony_ci	disk->queue = blk_alloc_queue(NUMA_NO_NODE);
26888c2ecf20Sopenharmony_ci	if (!disk->queue)
26898c2ecf20Sopenharmony_ci		goto out_mem2;
26908c2ecf20Sopenharmony_ci
26918c2ecf20Sopenharmony_ci	pd->pkt_dev = MKDEV(pktdev_major, idx);
26928c2ecf20Sopenharmony_ci	ret = pkt_new_dev(pd, dev);
26938c2ecf20Sopenharmony_ci	if (ret)
26948c2ecf20Sopenharmony_ci		goto out_mem2;
26958c2ecf20Sopenharmony_ci
26968c2ecf20Sopenharmony_ci	/* inherit events of the host device */
26978c2ecf20Sopenharmony_ci	disk->events = pd->bdev->bd_disk->events;
26988c2ecf20Sopenharmony_ci
26998c2ecf20Sopenharmony_ci	add_disk(disk);
27008c2ecf20Sopenharmony_ci
27018c2ecf20Sopenharmony_ci	pkt_sysfs_dev_new(pd);
27028c2ecf20Sopenharmony_ci	pkt_debugfs_dev_new(pd);
27038c2ecf20Sopenharmony_ci
27048c2ecf20Sopenharmony_ci	pkt_devs[idx] = pd;
27058c2ecf20Sopenharmony_ci	if (pkt_dev)
27068c2ecf20Sopenharmony_ci		*pkt_dev = pd->pkt_dev;
27078c2ecf20Sopenharmony_ci
27088c2ecf20Sopenharmony_ci	mutex_unlock(&ctl_mutex);
27098c2ecf20Sopenharmony_ci	return 0;
27108c2ecf20Sopenharmony_ci
27118c2ecf20Sopenharmony_ciout_mem2:
27128c2ecf20Sopenharmony_ci	put_disk(disk);
27138c2ecf20Sopenharmony_ciout_mem:
27148c2ecf20Sopenharmony_ci	mempool_exit(&pd->rb_pool);
27158c2ecf20Sopenharmony_ci	kfree(pd);
27168c2ecf20Sopenharmony_ciout_mutex:
27178c2ecf20Sopenharmony_ci	mutex_unlock(&ctl_mutex);
27188c2ecf20Sopenharmony_ci	pr_err("setup of pktcdvd device failed\n");
27198c2ecf20Sopenharmony_ci	return ret;
27208c2ecf20Sopenharmony_ci}
27218c2ecf20Sopenharmony_ci
27228c2ecf20Sopenharmony_ci/*
27238c2ecf20Sopenharmony_ci * Tear down mapping from pktcdvd device to CD-ROM device.
27248c2ecf20Sopenharmony_ci */
27258c2ecf20Sopenharmony_cistatic int pkt_remove_dev(dev_t pkt_dev)
27268c2ecf20Sopenharmony_ci{
27278c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd;
27288c2ecf20Sopenharmony_ci	int idx;
27298c2ecf20Sopenharmony_ci	int ret = 0;
27308c2ecf20Sopenharmony_ci
27318c2ecf20Sopenharmony_ci	mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
27328c2ecf20Sopenharmony_ci
27338c2ecf20Sopenharmony_ci	for (idx = 0; idx < MAX_WRITERS; idx++) {
27348c2ecf20Sopenharmony_ci		pd = pkt_devs[idx];
27358c2ecf20Sopenharmony_ci		if (pd && (pd->pkt_dev == pkt_dev))
27368c2ecf20Sopenharmony_ci			break;
27378c2ecf20Sopenharmony_ci	}
27388c2ecf20Sopenharmony_ci	if (idx == MAX_WRITERS) {
27398c2ecf20Sopenharmony_ci		pr_debug("dev not setup\n");
27408c2ecf20Sopenharmony_ci		ret = -ENXIO;
27418c2ecf20Sopenharmony_ci		goto out;
27428c2ecf20Sopenharmony_ci	}
27438c2ecf20Sopenharmony_ci
27448c2ecf20Sopenharmony_ci	if (pd->refcnt > 0) {
27458c2ecf20Sopenharmony_ci		ret = -EBUSY;
27468c2ecf20Sopenharmony_ci		goto out;
27478c2ecf20Sopenharmony_ci	}
27488c2ecf20Sopenharmony_ci	if (!IS_ERR(pd->cdrw.thread))
27498c2ecf20Sopenharmony_ci		kthread_stop(pd->cdrw.thread);
27508c2ecf20Sopenharmony_ci
27518c2ecf20Sopenharmony_ci	pkt_devs[idx] = NULL;
27528c2ecf20Sopenharmony_ci
27538c2ecf20Sopenharmony_ci	pkt_debugfs_dev_remove(pd);
27548c2ecf20Sopenharmony_ci	pkt_sysfs_dev_remove(pd);
27558c2ecf20Sopenharmony_ci
27568c2ecf20Sopenharmony_ci	blkdev_put(pd->bdev, FMODE_READ | FMODE_NDELAY);
27578c2ecf20Sopenharmony_ci
27588c2ecf20Sopenharmony_ci	remove_proc_entry(pd->name, pkt_proc);
27598c2ecf20Sopenharmony_ci	pkt_dbg(1, pd, "writer unmapped\n");
27608c2ecf20Sopenharmony_ci
27618c2ecf20Sopenharmony_ci	del_gendisk(pd->disk);
27628c2ecf20Sopenharmony_ci	blk_cleanup_queue(pd->disk->queue);
27638c2ecf20Sopenharmony_ci	put_disk(pd->disk);
27648c2ecf20Sopenharmony_ci
27658c2ecf20Sopenharmony_ci	mempool_exit(&pd->rb_pool);
27668c2ecf20Sopenharmony_ci	kfree(pd);
27678c2ecf20Sopenharmony_ci
27688c2ecf20Sopenharmony_ci	/* This is safe: open() is still holding a reference. */
27698c2ecf20Sopenharmony_ci	module_put(THIS_MODULE);
27708c2ecf20Sopenharmony_ci
27718c2ecf20Sopenharmony_ciout:
27728c2ecf20Sopenharmony_ci	mutex_unlock(&ctl_mutex);
27738c2ecf20Sopenharmony_ci	return ret;
27748c2ecf20Sopenharmony_ci}
27758c2ecf20Sopenharmony_ci
27768c2ecf20Sopenharmony_cistatic void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd)
27778c2ecf20Sopenharmony_ci{
27788c2ecf20Sopenharmony_ci	struct pktcdvd_device *pd;
27798c2ecf20Sopenharmony_ci
27808c2ecf20Sopenharmony_ci	mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
27818c2ecf20Sopenharmony_ci
27828c2ecf20Sopenharmony_ci	pd = pkt_find_dev_from_minor(ctrl_cmd->dev_index);
27838c2ecf20Sopenharmony_ci	if (pd) {
27848c2ecf20Sopenharmony_ci		ctrl_cmd->dev = new_encode_dev(pd->bdev->bd_dev);
27858c2ecf20Sopenharmony_ci		ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev);
27868c2ecf20Sopenharmony_ci	} else {
27878c2ecf20Sopenharmony_ci		ctrl_cmd->dev = 0;
27888c2ecf20Sopenharmony_ci		ctrl_cmd->pkt_dev = 0;
27898c2ecf20Sopenharmony_ci	}
27908c2ecf20Sopenharmony_ci	ctrl_cmd->num_devices = MAX_WRITERS;
27918c2ecf20Sopenharmony_ci
27928c2ecf20Sopenharmony_ci	mutex_unlock(&ctl_mutex);
27938c2ecf20Sopenharmony_ci}
27948c2ecf20Sopenharmony_ci
27958c2ecf20Sopenharmony_cistatic long pkt_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
27968c2ecf20Sopenharmony_ci{
27978c2ecf20Sopenharmony_ci	void __user *argp = (void __user *)arg;
27988c2ecf20Sopenharmony_ci	struct pkt_ctrl_command ctrl_cmd;
27998c2ecf20Sopenharmony_ci	int ret = 0;
28008c2ecf20Sopenharmony_ci	dev_t pkt_dev = 0;
28018c2ecf20Sopenharmony_ci
28028c2ecf20Sopenharmony_ci	if (cmd != PACKET_CTRL_CMD)
28038c2ecf20Sopenharmony_ci		return -ENOTTY;
28048c2ecf20Sopenharmony_ci
28058c2ecf20Sopenharmony_ci	if (copy_from_user(&ctrl_cmd, argp, sizeof(struct pkt_ctrl_command)))
28068c2ecf20Sopenharmony_ci		return -EFAULT;
28078c2ecf20Sopenharmony_ci
28088c2ecf20Sopenharmony_ci	switch (ctrl_cmd.command) {
28098c2ecf20Sopenharmony_ci	case PKT_CTRL_CMD_SETUP:
28108c2ecf20Sopenharmony_ci		if (!capable(CAP_SYS_ADMIN))
28118c2ecf20Sopenharmony_ci			return -EPERM;
28128c2ecf20Sopenharmony_ci		ret = pkt_setup_dev(new_decode_dev(ctrl_cmd.dev), &pkt_dev);
28138c2ecf20Sopenharmony_ci		ctrl_cmd.pkt_dev = new_encode_dev(pkt_dev);
28148c2ecf20Sopenharmony_ci		break;
28158c2ecf20Sopenharmony_ci	case PKT_CTRL_CMD_TEARDOWN:
28168c2ecf20Sopenharmony_ci		if (!capable(CAP_SYS_ADMIN))
28178c2ecf20Sopenharmony_ci			return -EPERM;
28188c2ecf20Sopenharmony_ci		ret = pkt_remove_dev(new_decode_dev(ctrl_cmd.pkt_dev));
28198c2ecf20Sopenharmony_ci		break;
28208c2ecf20Sopenharmony_ci	case PKT_CTRL_CMD_STATUS:
28218c2ecf20Sopenharmony_ci		pkt_get_status(&ctrl_cmd);
28228c2ecf20Sopenharmony_ci		break;
28238c2ecf20Sopenharmony_ci	default:
28248c2ecf20Sopenharmony_ci		return -ENOTTY;
28258c2ecf20Sopenharmony_ci	}
28268c2ecf20Sopenharmony_ci
28278c2ecf20Sopenharmony_ci	if (copy_to_user(argp, &ctrl_cmd, sizeof(struct pkt_ctrl_command)))
28288c2ecf20Sopenharmony_ci		return -EFAULT;
28298c2ecf20Sopenharmony_ci	return ret;
28308c2ecf20Sopenharmony_ci}
28318c2ecf20Sopenharmony_ci
28328c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
28338c2ecf20Sopenharmony_cistatic long pkt_ctl_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
28348c2ecf20Sopenharmony_ci{
28358c2ecf20Sopenharmony_ci	return pkt_ctl_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
28368c2ecf20Sopenharmony_ci}
28378c2ecf20Sopenharmony_ci#endif
28388c2ecf20Sopenharmony_ci
28398c2ecf20Sopenharmony_cistatic const struct file_operations pkt_ctl_fops = {
28408c2ecf20Sopenharmony_ci	.open		= nonseekable_open,
28418c2ecf20Sopenharmony_ci	.unlocked_ioctl	= pkt_ctl_ioctl,
28428c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
28438c2ecf20Sopenharmony_ci	.compat_ioctl	= pkt_ctl_compat_ioctl,
28448c2ecf20Sopenharmony_ci#endif
28458c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
28468c2ecf20Sopenharmony_ci	.llseek		= no_llseek,
28478c2ecf20Sopenharmony_ci};
28488c2ecf20Sopenharmony_ci
28498c2ecf20Sopenharmony_cistatic struct miscdevice pkt_misc = {
28508c2ecf20Sopenharmony_ci	.minor 		= MISC_DYNAMIC_MINOR,
28518c2ecf20Sopenharmony_ci	.name  		= DRIVER_NAME,
28528c2ecf20Sopenharmony_ci	.nodename	= "pktcdvd/control",
28538c2ecf20Sopenharmony_ci	.fops  		= &pkt_ctl_fops
28548c2ecf20Sopenharmony_ci};
28558c2ecf20Sopenharmony_ci
28568c2ecf20Sopenharmony_cistatic int __init pkt_init(void)
28578c2ecf20Sopenharmony_ci{
28588c2ecf20Sopenharmony_ci	int ret;
28598c2ecf20Sopenharmony_ci
28608c2ecf20Sopenharmony_ci	mutex_init(&ctl_mutex);
28618c2ecf20Sopenharmony_ci
28628c2ecf20Sopenharmony_ci	ret = mempool_init_kmalloc_pool(&psd_pool, PSD_POOL_SIZE,
28638c2ecf20Sopenharmony_ci				    sizeof(struct packet_stacked_data));
28648c2ecf20Sopenharmony_ci	if (ret)
28658c2ecf20Sopenharmony_ci		return ret;
28668c2ecf20Sopenharmony_ci	ret = bioset_init(&pkt_bio_set, BIO_POOL_SIZE, 0, 0);
28678c2ecf20Sopenharmony_ci	if (ret) {
28688c2ecf20Sopenharmony_ci		mempool_exit(&psd_pool);
28698c2ecf20Sopenharmony_ci		return ret;
28708c2ecf20Sopenharmony_ci	}
28718c2ecf20Sopenharmony_ci
28728c2ecf20Sopenharmony_ci	ret = register_blkdev(pktdev_major, DRIVER_NAME);
28738c2ecf20Sopenharmony_ci	if (ret < 0) {
28748c2ecf20Sopenharmony_ci		pr_err("unable to register block device\n");
28758c2ecf20Sopenharmony_ci		goto out2;
28768c2ecf20Sopenharmony_ci	}
28778c2ecf20Sopenharmony_ci	if (!pktdev_major)
28788c2ecf20Sopenharmony_ci		pktdev_major = ret;
28798c2ecf20Sopenharmony_ci
28808c2ecf20Sopenharmony_ci	ret = pkt_sysfs_init();
28818c2ecf20Sopenharmony_ci	if (ret)
28828c2ecf20Sopenharmony_ci		goto out;
28838c2ecf20Sopenharmony_ci
28848c2ecf20Sopenharmony_ci	pkt_debugfs_init();
28858c2ecf20Sopenharmony_ci
28868c2ecf20Sopenharmony_ci	ret = misc_register(&pkt_misc);
28878c2ecf20Sopenharmony_ci	if (ret) {
28888c2ecf20Sopenharmony_ci		pr_err("unable to register misc device\n");
28898c2ecf20Sopenharmony_ci		goto out_misc;
28908c2ecf20Sopenharmony_ci	}
28918c2ecf20Sopenharmony_ci
28928c2ecf20Sopenharmony_ci	pkt_proc = proc_mkdir("driver/"DRIVER_NAME, NULL);
28938c2ecf20Sopenharmony_ci
28948c2ecf20Sopenharmony_ci	return 0;
28958c2ecf20Sopenharmony_ci
28968c2ecf20Sopenharmony_ciout_misc:
28978c2ecf20Sopenharmony_ci	pkt_debugfs_cleanup();
28988c2ecf20Sopenharmony_ci	pkt_sysfs_cleanup();
28998c2ecf20Sopenharmony_ciout:
29008c2ecf20Sopenharmony_ci	unregister_blkdev(pktdev_major, DRIVER_NAME);
29018c2ecf20Sopenharmony_ciout2:
29028c2ecf20Sopenharmony_ci	mempool_exit(&psd_pool);
29038c2ecf20Sopenharmony_ci	bioset_exit(&pkt_bio_set);
29048c2ecf20Sopenharmony_ci	return ret;
29058c2ecf20Sopenharmony_ci}
29068c2ecf20Sopenharmony_ci
29078c2ecf20Sopenharmony_cistatic void __exit pkt_exit(void)
29088c2ecf20Sopenharmony_ci{
29098c2ecf20Sopenharmony_ci	remove_proc_entry("driver/"DRIVER_NAME, NULL);
29108c2ecf20Sopenharmony_ci	misc_deregister(&pkt_misc);
29118c2ecf20Sopenharmony_ci
29128c2ecf20Sopenharmony_ci	pkt_debugfs_cleanup();
29138c2ecf20Sopenharmony_ci	pkt_sysfs_cleanup();
29148c2ecf20Sopenharmony_ci
29158c2ecf20Sopenharmony_ci	unregister_blkdev(pktdev_major, DRIVER_NAME);
29168c2ecf20Sopenharmony_ci	mempool_exit(&psd_pool);
29178c2ecf20Sopenharmony_ci	bioset_exit(&pkt_bio_set);
29188c2ecf20Sopenharmony_ci}
29198c2ecf20Sopenharmony_ci
29208c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Packet writing layer for CD/DVD drives");
29218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jens Axboe <axboe@suse.de>");
29228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
29238c2ecf20Sopenharmony_ci
29248c2ecf20Sopenharmony_cimodule_init(pkt_init);
29258c2ecf20Sopenharmony_cimodule_exit(pkt_exit);
2926