ide-floppy_ioctl.c revision aeb5d727062a0238a2f96c9c380fbd2be4640c6f
1/*
2 * ide-floppy IOCTLs handling.
3 */
4
5#include <linux/kernel.h>
6#include <linux/ide.h>
7#include <linux/cdrom.h>
8
9#include <asm/unaligned.h>
10
11#include <scsi/scsi_ioctl.h>
12
13#include "ide-floppy.h"
14
15/*
16 * Obtain the list of formattable capacities.
17 * Very similar to ide_floppy_get_capacity, except that we push the capacity
18 * descriptors to userland, instead of our own structures.
19 *
20 * Userland gives us the following structure:
21 *
22 * struct idefloppy_format_capacities {
23 *	int nformats;
24 *	struct {
25 *		int nblocks;
26 *		int blocksize;
27 *	} formats[];
28 * };
29 *
30 * userland initializes nformats to the number of allocated formats[] records.
31 * On exit we set nformats to the number of records we've actually initialized.
32 */
33
34static int ide_floppy_get_format_capacities(ide_drive_t *drive, int __user *arg)
35{
36	struct ide_disk_obj *floppy = drive->driver_data;
37	struct ide_atapi_pc pc;
38	u8 header_len, desc_cnt;
39	int i, blocks, length, u_array_size, u_index;
40	int __user *argp;
41
42	if (get_user(u_array_size, arg))
43		return -EFAULT;
44
45	if (u_array_size <= 0)
46		return -EINVAL;
47
48	ide_floppy_create_read_capacity_cmd(&pc);
49	if (ide_queue_pc_tail(drive, floppy->disk, &pc)) {
50		printk(KERN_ERR "ide-floppy: Can't get floppy parameters\n");
51		return -EIO;
52	}
53
54	header_len = pc.buf[3];
55	desc_cnt = header_len / 8; /* capacity descriptor of 8 bytes */
56
57	u_index = 0;
58	argp = arg + 1;
59
60	/*
61	 * We always skip the first capacity descriptor.  That's the current
62	 * capacity.  We are interested in the remaining descriptors, the
63	 * formattable capacities.
64	 */
65	for (i = 1; i < desc_cnt; i++) {
66		unsigned int desc_start = 4 + i*8;
67
68		if (u_index >= u_array_size)
69			break;	/* User-supplied buffer too small */
70
71		blocks = be32_to_cpup((__be32 *)&pc.buf[desc_start]);
72		length = be16_to_cpup((__be16 *)&pc.buf[desc_start + 6]);
73
74		if (put_user(blocks, argp))
75			return -EFAULT;
76
77		++argp;
78
79		if (put_user(length, argp))
80			return -EFAULT;
81
82		++argp;
83
84		++u_index;
85	}
86
87	if (put_user(u_index, arg))
88		return -EFAULT;
89
90	return 0;
91}
92
93static void ide_floppy_create_format_unit_cmd(struct ide_atapi_pc *pc, int b,
94		int l, int flags)
95{
96	ide_init_pc(pc);
97	pc->c[0] = GPCMD_FORMAT_UNIT;
98	pc->c[1] = 0x17;
99
100	memset(pc->buf, 0, 12);
101	pc->buf[1] = 0xA2;
102	/* Default format list header, u8 1: FOV/DCRT/IMM bits set */
103
104	if (flags & 1)				/* Verify bit on... */
105		pc->buf[1] ^= 0x20;		/* ... turn off DCRT bit */
106	pc->buf[3] = 8;
107
108	put_unaligned(cpu_to_be32(b), (unsigned int *)(&pc->buf[4]));
109	put_unaligned(cpu_to_be32(l), (unsigned int *)(&pc->buf[8]));
110	pc->buf_size = 12;
111	pc->flags |= PC_FLAG_WRITING;
112}
113
114static int ide_floppy_get_sfrp_bit(ide_drive_t *drive)
115{
116	struct ide_disk_obj *floppy = drive->driver_data;
117	struct ide_atapi_pc pc;
118
119	drive->atapi_flags &= ~IDE_AFLAG_SRFP;
120
121	ide_floppy_create_mode_sense_cmd(&pc, IDEFLOPPY_CAPABILITIES_PAGE);
122	pc.flags |= PC_FLAG_SUPPRESS_ERROR;
123
124	if (ide_queue_pc_tail(drive, floppy->disk, &pc))
125		return 1;
126
127	if (pc.buf[8 + 2] & 0x40)
128		drive->atapi_flags |= IDE_AFLAG_SRFP;
129
130	return 0;
131}
132
133static int ide_floppy_format_unit(ide_drive_t *drive, int __user *arg)
134{
135	struct ide_disk_obj *floppy = drive->driver_data;
136	struct ide_atapi_pc pc;
137	int blocks, length, flags, err = 0;
138
139	if (floppy->openers > 1) {
140		/* Don't format if someone is using the disk */
141		drive->dev_flags &= ~IDE_DFLAG_FORMAT_IN_PROGRESS;
142		return -EBUSY;
143	}
144
145	drive->dev_flags |= IDE_DFLAG_FORMAT_IN_PROGRESS;
146
147	/*
148	 * Send ATAPI_FORMAT_UNIT to the drive.
149	 *
150	 * Userland gives us the following structure:
151	 *
152	 * struct idefloppy_format_command {
153	 *        int nblocks;
154	 *        int blocksize;
155	 *        int flags;
156	 *        } ;
157	 *
158	 * flags is a bitmask, currently, the only defined flag is:
159	 *
160	 *        0x01 - verify media after format.
161	 */
162	if (get_user(blocks, arg) ||
163			get_user(length, arg+1) ||
164			get_user(flags, arg+2)) {
165		err = -EFAULT;
166		goto out;
167	}
168
169	(void)ide_floppy_get_sfrp_bit(drive);
170	ide_floppy_create_format_unit_cmd(&pc, blocks, length, flags);
171
172	if (ide_queue_pc_tail(drive, floppy->disk, &pc))
173		err = -EIO;
174
175out:
176	if (err)
177		drive->dev_flags &= ~IDE_DFLAG_FORMAT_IN_PROGRESS;
178	return err;
179}
180
181/*
182 * Get ATAPI_FORMAT_UNIT progress indication.
183 *
184 * Userland gives a pointer to an int.  The int is set to a progress
185 * indicator 0-65536, with 65536=100%.
186 *
187 * If the drive does not support format progress indication, we just check
188 * the dsc bit, and return either 0 or 65536.
189 */
190
191static int ide_floppy_get_format_progress(ide_drive_t *drive, int __user *arg)
192{
193	struct ide_disk_obj *floppy = drive->driver_data;
194	struct ide_atapi_pc pc;
195	int progress_indication = 0x10000;
196
197	if (drive->atapi_flags & IDE_AFLAG_SRFP) {
198		ide_create_request_sense_cmd(drive, &pc);
199		if (ide_queue_pc_tail(drive, floppy->disk, &pc))
200			return -EIO;
201
202		if (floppy->sense_key == 2 &&
203		    floppy->asc == 4 &&
204		    floppy->ascq == 4)
205			progress_indication = floppy->progress_indication;
206
207		/* Else assume format_unit has finished, and we're at 0x10000 */
208	} else {
209		ide_hwif_t *hwif = drive->hwif;
210		unsigned long flags;
211		u8 stat;
212
213		local_irq_save(flags);
214		stat = hwif->tp_ops->read_status(hwif);
215		local_irq_restore(flags);
216
217		progress_indication = ((stat & ATA_DSC) == 0) ? 0 : 0x10000;
218	}
219
220	if (put_user(progress_indication, arg))
221		return -EFAULT;
222
223	return 0;
224}
225
226static int ide_floppy_lockdoor(ide_drive_t *drive, struct ide_atapi_pc *pc,
227			       unsigned long arg, unsigned int cmd)
228{
229	struct ide_disk_obj *floppy = drive->driver_data;
230	struct gendisk *disk = floppy->disk;
231	int prevent = (arg && cmd != CDROMEJECT) ? 1 : 0;
232
233	if (floppy->openers > 1)
234		return -EBUSY;
235
236	ide_set_media_lock(drive, disk, prevent);
237
238	if (cmd == CDROMEJECT)
239		ide_do_start_stop(drive, disk, 2);
240
241	return 0;
242}
243
244static int ide_floppy_format_ioctl(ide_drive_t *drive, struct file *file,
245				   unsigned int cmd, void __user *argp)
246{
247	switch (cmd) {
248	case IDEFLOPPY_IOCTL_FORMAT_SUPPORTED:
249		return 0;
250	case IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY:
251		return ide_floppy_get_format_capacities(drive, argp);
252	case IDEFLOPPY_IOCTL_FORMAT_START:
253		if (!(file->f_mode & FMODE_WRITE))
254			return -EPERM;
255		return ide_floppy_format_unit(drive, (int __user *)argp);
256	case IDEFLOPPY_IOCTL_FORMAT_GET_PROGRESS:
257		return ide_floppy_get_format_progress(drive, argp);
258	default:
259		return -ENOTTY;
260	}
261}
262
263int ide_floppy_ioctl(ide_drive_t *drive, struct inode *inode,
264		     struct file *file, unsigned int cmd, unsigned long arg)
265{
266	struct block_device *bdev = inode->i_bdev;
267	struct ide_atapi_pc pc;
268	void __user *argp = (void __user *)arg;
269	int err;
270
271	if (cmd == CDROMEJECT || cmd == CDROM_LOCKDOOR)
272		return ide_floppy_lockdoor(drive, &pc, arg, cmd);
273
274	err = ide_floppy_format_ioctl(drive, file, cmd, argp);
275	if (err != -ENOTTY)
276		return err;
277
278	/*
279	 * skip SCSI_IOCTL_SEND_COMMAND (deprecated)
280	 * and CDROM_SEND_PACKET (legacy) ioctls
281	 */
282	if (cmd != CDROM_SEND_PACKET && cmd != SCSI_IOCTL_SEND_COMMAND)
283		err = scsi_cmd_ioctl(file, bdev->bd_disk->queue,
284					bdev->bd_disk, cmd, argp);
285
286	if (err == -ENOTTY)
287		err = generic_ide_ioctl(drive, file, bdev, cmd, arg);
288
289	return err;
290}
291