blob: 22f41fa4e5b6f262455e9badee0503cb1178c660 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * Copyright (C) 1997-1998 Mark Lord
3 * Copyright (C) 2003 Red Hat <alan@redhat.com>
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +02004 *
5 * Some code was moved here from ide.c, see it for original copyrights.
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 */
7
8/*
9 * This is the /proc/ide/ filesystem implementation.
10 *
11 * Drive/Driver settings can be retrieved by reading the drive's
12 * "settings" files. e.g. "cat /proc/ide0/hda/settings"
13 * To write a new value "val" into a specific setting "name", use:
14 * echo "name:val" >/proc/ide/ide0/hda/settings
Linus Torvalds1da177e2005-04-16 15:20:36 -070015 */
16
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/module.h>
18
19#include <asm/uaccess.h>
20#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <linux/proc_fs.h>
22#include <linux/stat.h>
23#include <linux/mm.h>
24#include <linux/pci.h>
25#include <linux/ctype.h>
26#include <linux/hdreg.h>
27#include <linux/ide.h>
28#include <linux/seq_file.h>
29
30#include <asm/io.h>
31
Bartlomiej Zolnierkiewicz5cbf79c2007-05-10 00:01:11 +020032static struct proc_dir_entry *proc_ide_root;
33
Linus Torvalds1da177e2005-04-16 15:20:36 -070034static int proc_ide_read_imodel
35 (char *page, char **start, off_t off, int count, int *eof, void *data)
36{
37 ide_hwif_t *hwif = (ide_hwif_t *) data;
38 int len;
39 const char *name;
40
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 switch (hwif->chipset) {
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +020042 case ide_generic: name = "generic"; break;
43 case ide_pci: name = "pci"; break;
44 case ide_cmd640: name = "cmd640"; break;
45 case ide_dtc2278: name = "dtc2278"; break;
46 case ide_ali14xx: name = "ali14xx"; break;
47 case ide_qd65xx: name = "qd65xx"; break;
48 case ide_umc8672: name = "umc8672"; break;
49 case ide_ht6560b: name = "ht6560b"; break;
50 case ide_rz1000: name = "rz1000"; break;
51 case ide_trm290: name = "trm290"; break;
52 case ide_cmd646: name = "cmd646"; break;
53 case ide_cy82c693: name = "cy82c693"; break;
54 case ide_4drives: name = "4drives"; break;
55 case ide_pmac: name = "mac-io"; break;
56 case ide_au1xxx: name = "au1xxx"; break;
57 case ide_palm3710: name = "palm3710"; break;
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +020058 case ide_acorn: name = "acorn"; break;
59 default: name = "(unknown)"; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 }
61 len = sprintf(page, "%s\n", name);
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +020062 PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -070063}
64
65static int proc_ide_read_mate
66 (char *page, char **start, off_t off, int count, int *eof, void *data)
67{
68 ide_hwif_t *hwif = (ide_hwif_t *) data;
69 int len;
70
Bartlomiej Zolnierkiewicz4283e1b2008-06-30 20:14:45 +020071 if (hwif && hwif->mate)
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 len = sprintf(page, "%s\n", hwif->mate->name);
73 else
74 len = sprintf(page, "(none)\n");
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +020075 PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -070076}
77
78static int proc_ide_read_channel
79 (char *page, char **start, off_t off, int count, int *eof, void *data)
80{
81 ide_hwif_t *hwif = (ide_hwif_t *) data;
82 int len;
83
84 page[0] = hwif->channel ? '1' : '0';
85 page[1] = '\n';
86 len = 2;
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +020087 PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088}
89
90static int proc_ide_read_identify
91 (char *page, char **start, off_t off, int count, int *eof, void *data)
92{
93 ide_drive_t *drive = (ide_drive_t *)data;
94 int len = 0, i = 0;
95 int err = 0;
96
97 len = sprintf(page, "\n");
98
99 if (drive) {
Harvey Harrison7fa897b2008-07-24 22:53:34 +0200100 __le16 *val = (__le16 *)page;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
102 err = taskfile_lib_get_identify(drive, page);
103 if (!err) {
104 char *out = ((char *)page) + (SECTOR_WORDS * 4);
105 page = out;
106 do {
107 out += sprintf(out, "%04x%c",
Harvey Harrison7fa897b2008-07-24 22:53:34 +0200108 le16_to_cpup(val), (++i & 7) ? ' ' : '\n');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 val += 1;
110 } while (i < (SECTOR_WORDS * 2));
111 len = out - page;
112 }
113 }
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200114 PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115}
116
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200117/**
118 * __ide_add_setting - add an ide setting option
119 * @drive: drive to use
120 * @name: setting name
121 * @rw: true if the function is read write
122 * @data_type: type of data
123 * @min: range minimum
124 * @max: range maximum
125 * @mul_factor: multiplication scale
126 * @div_factor: divison scale
127 * @data: private data field
128 * @set: setting
129 * @auto_remove: setting auto removal flag
130 *
131 * Removes the setting named from the device if it is present.
132 * The function takes the settings_lock to protect against
133 * parallel changes. This function must not be called from IRQ
134 * context. Returns 0 on success or -1 on failure.
135 *
136 * BUGS: This code is seriously over-engineered. There is also
137 * magic about how the driver specific features are setup. If
138 * a driver is attached we assume the driver settings are auto
139 * remove.
140 */
141
142static int __ide_add_setting(ide_drive_t *drive, const char *name, int rw, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set, int auto_remove)
143{
144 ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL;
145
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200146 mutex_lock(&ide_setting_mtx);
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200147 while ((*p) && strcmp((*p)->name, name) < 0)
148 p = &((*p)->next);
149 if ((setting = kzalloc(sizeof(*setting), GFP_KERNEL)) == NULL)
150 goto abort;
151 if ((setting->name = kmalloc(strlen(name) + 1, GFP_KERNEL)) == NULL)
152 goto abort;
153 strcpy(setting->name, name);
154 setting->rw = rw;
155 setting->data_type = data_type;
156 setting->min = min;
157 setting->max = max;
158 setting->mul_factor = mul_factor;
159 setting->div_factor = div_factor;
160 setting->data = data;
161 setting->set = set;
162
163 setting->next = *p;
164 if (auto_remove)
165 setting->auto_remove = 1;
166 *p = setting;
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200167 mutex_unlock(&ide_setting_mtx);
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200168 return 0;
169abort:
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200170 mutex_unlock(&ide_setting_mtx);
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200171 kfree(setting);
172 return -1;
173}
174
175int ide_add_setting(ide_drive_t *drive, const char *name, int rw, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
176{
177 return __ide_add_setting(drive, name, rw, data_type, min, max, mul_factor, div_factor, data, set, 1);
178}
179
180EXPORT_SYMBOL(ide_add_setting);
181
182/**
183 * __ide_remove_setting - remove an ide setting option
184 * @drive: drive to use
185 * @name: setting name
186 *
187 * Removes the setting named from the device if it is present.
188 * The caller must hold the setting semaphore.
189 */
190
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200191static void __ide_remove_setting(ide_drive_t *drive, char *name)
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200192{
193 ide_settings_t **p, *setting;
194
195 p = (ide_settings_t **) &drive->settings;
196
197 while ((*p) && strcmp((*p)->name, name))
198 p = &((*p)->next);
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200199 setting = (*p);
200 if (setting == NULL)
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200201 return;
202
203 (*p) = setting->next;
204
205 kfree(setting->name);
206 kfree(setting);
207}
208
209/**
210 * auto_remove_settings - remove driver specific settings
211 * @drive: drive
212 *
213 * Automatically remove all the driver specific settings for this
214 * drive. This function may not be called from IRQ context. The
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200215 * caller must hold ide_setting_mtx.
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200216 */
217
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200218static void auto_remove_settings(ide_drive_t *drive)
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200219{
220 ide_settings_t *setting;
221repeat:
222 setting = drive->settings;
223 while (setting) {
224 if (setting->auto_remove) {
225 __ide_remove_setting(drive, setting->name);
226 goto repeat;
227 }
228 setting = setting->next;
229 }
230}
231
232/**
233 * ide_find_setting_by_name - find a drive specific setting
234 * @drive: drive to scan
235 * @name: setting name
236 *
237 * Scan's the device setting table for a matching entry and returns
238 * this or NULL if no entry is found. The caller must hold the
239 * setting semaphore
240 */
241
242static ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name)
243{
244 ide_settings_t *setting = drive->settings;
245
246 while (setting) {
247 if (strcmp(setting->name, name) == 0)
248 break;
249 setting = setting->next;
250 }
251 return setting;
252}
253
254/**
255 * ide_read_setting - read an IDE setting
256 * @drive: drive to read from
257 * @setting: drive setting
258 *
259 * Read a drive setting and return the value. The caller
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200260 * must hold the ide_setting_mtx when making this call.
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200261 *
262 * BUGS: the data return and error are the same return value
263 * so an error -EINVAL and true return of the same value cannot
264 * be told apart
265 */
266
267static int ide_read_setting(ide_drive_t *drive, ide_settings_t *setting)
268{
269 int val = -EINVAL;
270 unsigned long flags;
271
272 if ((setting->rw & SETTING_READ)) {
273 spin_lock_irqsave(&ide_lock, flags);
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200274 switch (setting->data_type) {
275 case TYPE_BYTE:
276 val = *((u8 *) setting->data);
277 break;
278 case TYPE_SHORT:
279 val = *((u16 *) setting->data);
280 break;
281 case TYPE_INT:
282 val = *((u32 *) setting->data);
283 break;
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200284 }
285 spin_unlock_irqrestore(&ide_lock, flags);
286 }
287 return val;
288}
289
290/**
291 * ide_write_setting - read an IDE setting
292 * @drive: drive to read from
293 * @setting: drive setting
294 * @val: value
295 *
296 * Write a drive setting if it is possible. The caller
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200297 * must hold the ide_setting_mtx when making this call.
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200298 *
299 * BUGS: the data return and error are the same return value
300 * so an error -EINVAL and true return of the same value cannot
301 * be told apart
302 *
303 * FIXME: This should be changed to enqueue a special request
304 * to the driver to change settings, and then wait on a sema for completion.
305 * The current scheme of polling is kludgy, though safe enough.
306 */
307
308static int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val)
309{
310 if (!capable(CAP_SYS_ADMIN))
311 return -EACCES;
312 if (setting->set)
313 return setting->set(drive, val);
314 if (!(setting->rw & SETTING_WRITE))
315 return -EPERM;
316 if (val < setting->min || val > setting->max)
317 return -EINVAL;
318 if (ide_spin_wait_hwgroup(drive))
319 return -EBUSY;
320 switch (setting->data_type) {
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200321 case TYPE_BYTE:
322 *((u8 *) setting->data) = val;
323 break;
324 case TYPE_SHORT:
325 *((u16 *) setting->data) = val;
326 break;
327 case TYPE_INT:
328 *((u32 *) setting->data) = val;
329 break;
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200330 }
331 spin_unlock_irq(&ide_lock);
332 return 0;
333}
334
335static int set_xfer_rate (ide_drive_t *drive, int arg)
336{
Bartlomiej Zolnierkiewicz34f5d5a2008-01-26 20:13:12 +0100337 ide_task_t task;
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200338 int err;
339
Bartlomiej Zolnierkiewicz3b2a5c72008-07-23 19:55:56 +0200340 if (arg < XFER_PIO_0 || arg > XFER_UDMA_6)
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200341 return -EINVAL;
342
Bartlomiej Zolnierkiewicz34f5d5a2008-01-26 20:13:12 +0100343 memset(&task, 0, sizeof(task));
Bartlomiej Zolnierkiewiczaaaade32008-10-10 22:39:21 +0200344 task.tf.command = ATA_CMD_SET_FEATURES;
Bartlomiej Zolnierkiewicz34f5d5a2008-01-26 20:13:12 +0100345 task.tf.feature = SETFEATURES_XFER;
346 task.tf.nsect = (u8)arg;
347 task.tf_flags = IDE_TFLAG_OUT_FEATURE | IDE_TFLAG_OUT_NSECT |
348 IDE_TFLAG_IN_NSECT;
349
350 err = ide_no_data_taskfile(drive, &task);
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200351
Bartlomiej Zolnierkiewicz3b2a5c72008-07-23 19:55:56 +0200352 if (!err) {
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200353 ide_set_xfer_rate(drive, (u8) arg);
354 ide_driveid_update(drive);
355 }
356 return err;
357}
358
359/**
360 * ide_add_generic_settings - generic ide settings
361 * @drive: drive being configured
362 *
363 * Add the generic parts of the system settings to the /proc files.
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200364 * The caller must not be holding the ide_setting_mtx.
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200365 */
366
367void ide_add_generic_settings (ide_drive_t *drive)
368{
369/*
370 * drive setting name read/write access data type min max mul_factor div_factor data pointer set function
371 */
372 __ide_add_setting(drive, "io_32bit", drive->no_io_32bit ? SETTING_READ : SETTING_RW, TYPE_BYTE, 0, 1 + (SUPPORT_VLB_SYNC << 1), 1, 1, &drive->io_32bit, set_io_32bit, 0);
373 __ide_add_setting(drive, "keepsettings", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->keep_settings, NULL, 0);
374 __ide_add_setting(drive, "nice1", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->nice1, NULL, 0);
375 __ide_add_setting(drive, "pio_mode", SETTING_WRITE, TYPE_BYTE, 0, 255, 1, 1, NULL, set_pio_mode, 0);
376 __ide_add_setting(drive, "unmaskirq", drive->no_unmask ? SETTING_READ : SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->unmask, NULL, 0);
377 __ide_add_setting(drive, "using_dma", SETTING_RW, TYPE_BYTE, 0, 1, 1, 1, &drive->using_dma, set_using_dma, 0);
378 __ide_add_setting(drive, "init_speed", SETTING_RW, TYPE_BYTE, 0, 70, 1, 1, &drive->init_speed, NULL, 0);
379 __ide_add_setting(drive, "current_speed", SETTING_RW, TYPE_BYTE, 0, 70, 1, 1, &drive->current_speed, set_xfer_rate, 0);
380 __ide_add_setting(drive, "number", SETTING_RW, TYPE_BYTE, 0, 3, 1, 1, &drive->dn, NULL, 0);
381}
382
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383static void proc_ide_settings_warn(void)
384{
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200385 static int warned;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386
387 if (warned)
388 return;
389
390 printk(KERN_WARNING "Warning: /proc/ide/hd?/settings interface is "
391 "obsolete, and will be removed soon!\n");
392 warned = 1;
393}
394
395static int proc_ide_read_settings
396 (char *page, char **start, off_t off, int count, int *eof, void *data)
397{
398 ide_drive_t *drive = (ide_drive_t *) data;
399 ide_settings_t *setting = (ide_settings_t *) drive->settings;
400 char *out = page;
401 int len, rc, mul_factor, div_factor;
402
403 proc_ide_settings_warn();
404
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200405 mutex_lock(&ide_setting_mtx);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
407 out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200408 while (setting) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 mul_factor = setting->mul_factor;
410 div_factor = setting->div_factor;
411 out += sprintf(out, "%-24s", setting->name);
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200412 rc = ide_read_setting(drive, setting);
413 if (rc >= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 out += sprintf(out, "%-16d", rc * mul_factor / div_factor);
415 else
416 out += sprintf(out, "%-16s", "write-only");
417 out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
418 if (setting->rw & SETTING_READ)
419 out += sprintf(out, "r");
420 if (setting->rw & SETTING_WRITE)
421 out += sprintf(out, "w");
422 out += sprintf(out, "\n");
423 setting = setting->next;
424 }
425 len = out - page;
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200426 mutex_unlock(&ide_setting_mtx);
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200427 PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428}
429
430#define MAX_LEN 30
431
432static int proc_ide_write_settings(struct file *file, const char __user *buffer,
433 unsigned long count, void *data)
434{
435 ide_drive_t *drive = (ide_drive_t *) data;
436 char name[MAX_LEN + 1];
437 int for_real = 0;
438 unsigned long n;
439 ide_settings_t *setting;
440 char *buf, *s;
441
442 if (!capable(CAP_SYS_ADMIN))
443 return -EACCES;
444
445 proc_ide_settings_warn();
446
447 if (count >= PAGE_SIZE)
448 return -EINVAL;
449
450 s = buf = (char *)__get_free_page(GFP_USER);
451 if (!buf)
452 return -ENOMEM;
453
454 if (copy_from_user(buf, buffer, count)) {
455 free_page((unsigned long)buf);
456 return -EFAULT;
457 }
458
459 buf[count] = '\0';
460
461 /*
462 * Skip over leading whitespace
463 */
464 while (count && isspace(*s)) {
465 --count;
466 ++s;
467 }
468 /*
469 * Do one full pass to verify all parameters,
470 * then do another to actually write the new settings.
471 */
472 do {
473 char *p = s;
474 n = count;
475 while (n > 0) {
476 unsigned val;
477 char *q = p;
478
479 while (n > 0 && *p != ':') {
480 --n;
481 p++;
482 }
483 if (*p != ':')
484 goto parse_error;
485 if (p - q > MAX_LEN)
486 goto parse_error;
487 memcpy(name, q, p - q);
488 name[p - q] = 0;
489
490 if (n > 0) {
491 --n;
492 p++;
493 } else
494 goto parse_error;
495
496 val = simple_strtoul(p, &q, 10);
497 n -= q - p;
498 p = q;
499 if (n > 0 && !isspace(*p))
500 goto parse_error;
501 while (n > 0 && isspace(*p)) {
502 --n;
503 ++p;
504 }
505
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200506 mutex_lock(&ide_setting_mtx);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 setting = ide_find_setting_by_name(drive, name);
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200508 if (!setting) {
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200509 mutex_unlock(&ide_setting_mtx);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 goto parse_error;
511 }
512 if (for_real)
513 ide_write_setting(drive, setting, val * setting->div_factor / setting->mul_factor);
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200514 mutex_unlock(&ide_setting_mtx);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 }
516 } while (!for_real++);
517 free_page((unsigned long)buf);
518 return count;
519parse_error:
520 free_page((unsigned long)buf);
521 printk("proc_ide_write_settings(): parse error\n");
522 return -EINVAL;
523}
524
525int proc_ide_read_capacity
526 (char *page, char **start, off_t off, int count, int *eof, void *data)
527{
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200528 int len = sprintf(page, "%llu\n", (long long)0x7fffffff);
529 PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530}
531
532EXPORT_SYMBOL_GPL(proc_ide_read_capacity);
533
534int proc_ide_read_geometry
535 (char *page, char **start, off_t off, int count, int *eof, void *data)
536{
537 ide_drive_t *drive = (ide_drive_t *) data;
538 char *out = page;
539 int len;
540
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200541 out += sprintf(out, "physical %d/%d/%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 drive->cyl, drive->head, drive->sect);
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200543 out += sprintf(out, "logical %d/%d/%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 drive->bios_cyl, drive->bios_head, drive->bios_sect);
545
546 len = out - page;
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200547 PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548}
549
550EXPORT_SYMBOL(proc_ide_read_geometry);
551
552static int proc_ide_read_dmodel
553 (char *page, char **start, off_t off, int count, int *eof, void *data)
554{
555 ide_drive_t *drive = (ide_drive_t *) data;
Bartlomiej Zolnierkiewicz4dde4492008-10-10 22:39:19 +0200556 char *m = (char *)&drive->id[ATA_ID_PROD];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 int len;
558
Bartlomiej Zolnierkiewicz4dde4492008-10-10 22:39:19 +0200559 len = sprintf(page, "%.40s\n", m[0] ? m : "(none)");
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200560 PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561}
562
563static int proc_ide_read_driver
564 (char *page, char **start, off_t off, int count, int *eof, void *data)
565{
566 ide_drive_t *drive = (ide_drive_t *) data;
Bartlomiej Zolnierkiewicz8604aff2005-05-26 14:55:34 +0200567 struct device *dev = &drive->gendev;
568 ide_driver_t *ide_drv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 int len;
570
Bartlomiej Zolnierkiewicz8604aff2005-05-26 14:55:34 +0200571 if (dev->driver) {
572 ide_drv = container_of(dev->driver, ide_driver_t, gen_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 len = sprintf(page, "%s version %s\n",
Bartlomiej Zolnierkiewicz8604aff2005-05-26 14:55:34 +0200574 dev->driver->name, ide_drv->version);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 } else
576 len = sprintf(page, "ide-default version 0.9.newide\n");
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200577 PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578}
579
Bartlomiej Zolnierkiewicz8604aff2005-05-26 14:55:34 +0200580static int ide_replace_subdriver(ide_drive_t *drive, const char *driver)
581{
582 struct device *dev = &drive->gendev;
583 int ret = 1;
Randy Dunlap349ae232006-10-03 01:14:23 -0700584 int err;
Bartlomiej Zolnierkiewicz8604aff2005-05-26 14:55:34 +0200585
Bartlomiej Zolnierkiewicz8604aff2005-05-26 14:55:34 +0200586 device_release_driver(dev);
587 /* FIXME: device can still be in use by previous driver */
588 strlcpy(drive->driver_req, driver, sizeof(drive->driver_req));
Randy Dunlap349ae232006-10-03 01:14:23 -0700589 err = device_attach(dev);
590 if (err < 0)
591 printk(KERN_WARNING "IDE: %s: device_attach error: %d\n",
Harvey Harrisoneb639632008-04-26 22:25:20 +0200592 __func__, err);
Bartlomiej Zolnierkiewicz8604aff2005-05-26 14:55:34 +0200593 drive->driver_req[0] = 0;
Randy Dunlap349ae232006-10-03 01:14:23 -0700594 if (dev->driver == NULL) {
595 err = device_attach(dev);
596 if (err < 0)
597 printk(KERN_WARNING
598 "IDE: %s: device_attach(2) error: %d\n",
Harvey Harrisoneb639632008-04-26 22:25:20 +0200599 __func__, err);
Randy Dunlap349ae232006-10-03 01:14:23 -0700600 }
Bartlomiej Zolnierkiewicz8604aff2005-05-26 14:55:34 +0200601 if (dev->driver && !strcmp(dev->driver->name, driver))
602 ret = 0;
Bartlomiej Zolnierkiewicz8604aff2005-05-26 14:55:34 +0200603
604 return ret;
605}
606
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607static int proc_ide_write_driver
608 (struct file *file, const char __user *buffer, unsigned long count, void *data)
609{
610 ide_drive_t *drive = (ide_drive_t *) data;
611 char name[32];
612
613 if (!capable(CAP_SYS_ADMIN))
614 return -EACCES;
615 if (count > 31)
616 count = 31;
617 if (copy_from_user(name, buffer, count))
618 return -EFAULT;
619 name[count] = '\0';
620 if (ide_replace_subdriver(drive, name))
621 return -EINVAL;
622 return count;
623}
624
625static int proc_ide_read_media
626 (char *page, char **start, off_t off, int count, int *eof, void *data)
627{
628 ide_drive_t *drive = (ide_drive_t *) data;
629 const char *media;
630 int len;
631
632 switch (drive->media) {
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200633 case ide_disk: media = "disk\n"; break;
634 case ide_cdrom: media = "cdrom\n"; break;
635 case ide_tape: media = "tape\n"; break;
636 case ide_floppy: media = "floppy\n"; break;
637 case ide_optical: media = "optical\n"; break;
638 default: media = "UNKNOWN\n"; break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 }
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200640 strcpy(page, media);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 len = strlen(media);
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200642 PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643}
644
645static ide_proc_entry_t generic_drive_entries[] = {
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200646 { "driver", S_IFREG|S_IRUGO, proc_ide_read_driver,
647 proc_ide_write_driver },
648 { "identify", S_IFREG|S_IRUSR, proc_ide_read_identify, NULL },
649 { "media", S_IFREG|S_IRUGO, proc_ide_read_media, NULL },
650 { "model", S_IFREG|S_IRUGO, proc_ide_read_dmodel, NULL },
651 { "settings", S_IFREG|S_IRUSR|S_IWUSR, proc_ide_read_settings,
652 proc_ide_write_settings },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 { NULL, 0, NULL, NULL }
654};
655
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200656static void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657{
658 struct proc_dir_entry *ent;
659
660 if (!dir || !p)
661 return;
662 while (p->name != NULL) {
663 ent = create_proc_entry(p->name, p->mode, dir);
664 if (!ent) return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 ent->data = data;
666 ent->read_proc = p->read_proc;
667 ent->write_proc = p->write_proc;
668 p++;
669 }
670}
671
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200672static void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673{
674 if (!dir || !p)
675 return;
676 while (p->name != NULL) {
677 remove_proc_entry(p->name, dir);
678 p++;
679 }
680}
681
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200682void ide_proc_register_driver(ide_drive_t *drive, ide_driver_t *driver)
683{
684 ide_add_proc_entries(drive->proc, driver->proc, drive);
685}
686
687EXPORT_SYMBOL(ide_proc_register_driver);
688
689/**
690 * ide_proc_unregister_driver - remove driver specific data
691 * @drive: drive
692 * @driver: driver
693 *
694 * Clean up the driver specific /proc files and IDE settings
695 * for a given drive.
696 *
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200697 * Takes ide_setting_mtx and ide_lock.
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200698 * Caller must hold none of the locks.
699 */
700
701void ide_proc_unregister_driver(ide_drive_t *drive, ide_driver_t *driver)
702{
703 unsigned long flags;
704
705 ide_remove_proc_entries(drive->proc, driver->proc);
706
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200707 mutex_lock(&ide_setting_mtx);
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200708 spin_lock_irqsave(&ide_lock, flags);
709 /*
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200710 * ide_setting_mtx protects the settings list
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200711 * ide_lock protects the use of settings
712 *
713 * so we need to hold both, ide_settings_sem because we want to
714 * modify the settings list, and ide_lock because we cannot take
715 * a setting out that is being used.
716 *
717 * OTOH both ide_{read,write}_setting are only ever used under
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200718 * ide_setting_mtx.
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200719 */
720 auto_remove_settings(drive);
721 spin_unlock_irqrestore(&ide_lock, flags);
Matthias Kaehlckef9383c42007-07-09 23:17:56 +0200722 mutex_unlock(&ide_setting_mtx);
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200723}
Bartlomiej Zolnierkiewicz7662d042007-05-10 00:01:10 +0200724EXPORT_SYMBOL(ide_proc_unregister_driver);
725
Bartlomiej Zolnierkiewiczd9270a32008-02-02 19:56:43 +0100726void ide_proc_port_register_devices(ide_hwif_t *hwif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727{
728 int d;
729 struct proc_dir_entry *ent;
730 struct proc_dir_entry *parent = hwif->proc;
731 char name[64];
732
733 for (d = 0; d < MAX_DRIVES; d++) {
734 ide_drive_t *drive = &hwif->drives[d];
735
736 if (!drive->present)
737 continue;
738 if (drive->proc)
739 continue;
740
741 drive->proc = proc_mkdir(drive->name, parent);
742 if (drive->proc)
743 ide_add_proc_entries(drive->proc, generic_drive_entries, drive);
Paolo Ciarrocchi441e92d2008-04-26 17:36:40 +0200744 sprintf(name, "ide%d/%s", (drive->name[2]-'a')/2, drive->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 ent = proc_symlink(drive->name, proc_ide_root, name);
746 if (!ent) return;
747 }
748}
749
Bartlomiej Zolnierkiewicz5b0c4b32008-04-18 00:46:22 +0200750void ide_proc_unregister_device(ide_drive_t *drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751{
752 if (drive->proc) {
753 ide_remove_proc_entries(drive->proc, generic_drive_entries);
754 remove_proc_entry(drive->name, proc_ide_root);
Bartlomiej Zolnierkiewicz5b0c4b32008-04-18 00:46:22 +0200755 remove_proc_entry(drive->name, drive->hwif->proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 drive->proc = NULL;
757 }
758}
759
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760static ide_proc_entry_t hwif_entries[] = {
761 { "channel", S_IFREG|S_IRUGO, proc_ide_read_channel, NULL },
762 { "mate", S_IFREG|S_IRUGO, proc_ide_read_mate, NULL },
763 { "model", S_IFREG|S_IRUGO, proc_ide_read_imodel, NULL },
764 { NULL, 0, NULL, NULL }
765};
766
Bartlomiej Zolnierkiewicz5cbf79c2007-05-10 00:01:11 +0200767void ide_proc_register_port(ide_hwif_t *hwif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768{
Bartlomiej Zolnierkiewicz5cbf79c2007-05-10 00:01:11 +0200769 if (!hwif->proc) {
770 hwif->proc = proc_mkdir(hwif->name, proc_ide_root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
Bartlomiej Zolnierkiewicz5cbf79c2007-05-10 00:01:11 +0200772 if (!hwif->proc)
773 return;
774
775 ide_add_proc_entries(hwif->proc, hwif_entries, hwif);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 }
777}
778
Bartlomiej Zolnierkiewicz5cbf79c2007-05-10 00:01:11 +0200779void ide_proc_unregister_port(ide_hwif_t *hwif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780{
781 if (hwif->proc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 ide_remove_proc_entries(hwif->proc, hwif_entries);
783 remove_proc_entry(hwif->name, proc_ide_root);
784 hwif->proc = NULL;
785 }
786}
787
Bartlomiej Zolnierkiewicz8604aff2005-05-26 14:55:34 +0200788static int proc_print_driver(struct device_driver *drv, void *data)
789{
790 ide_driver_t *ide_drv = container_of(drv, ide_driver_t, gen_driver);
791 struct seq_file *s = data;
792
793 seq_printf(s, "%s version %s\n", drv->name, ide_drv->version);
794
795 return 0;
796}
797
798static int ide_drivers_show(struct seq_file *s, void *p)
799{
Randy Dunlap349ae232006-10-03 01:14:23 -0700800 int err;
801
802 err = bus_for_each_drv(&ide_bus_type, NULL, s, proc_print_driver);
803 if (err < 0)
804 printk(KERN_WARNING "IDE: %s: bus_for_each_drv error: %d\n",
Harvey Harrisoneb639632008-04-26 22:25:20 +0200805 __func__, err);
Bartlomiej Zolnierkiewicz8604aff2005-05-26 14:55:34 +0200806 return 0;
807}
808
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809static int ide_drivers_open(struct inode *inode, struct file *file)
810{
Bartlomiej Zolnierkiewicz8604aff2005-05-26 14:55:34 +0200811 return single_open(file, &ide_drivers_show, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812}
Bartlomiej Zolnierkiewicz8604aff2005-05-26 14:55:34 +0200813
Arjan van de Ven2b8693c2007-02-12 00:55:32 -0800814static const struct file_operations ide_drivers_operations = {
Denis V. Lunevc7705f32008-04-29 01:02:35 -0700815 .owner = THIS_MODULE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 .open = ide_drivers_open,
817 .read = seq_read,
818 .llseek = seq_lseek,
Bartlomiej Zolnierkiewicz8604aff2005-05-26 14:55:34 +0200819 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820};
821
822void proc_ide_create(void)
823{
Bartlomiej Zolnierkiewicz5cbf79c2007-05-10 00:01:11 +0200824 proc_ide_root = proc_mkdir("ide", NULL);
825
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 if (!proc_ide_root)
827 return;
828
Denis V. Lunevc7705f32008-04-29 01:02:35 -0700829 proc_create("drivers", 0, proc_ide_root, &ide_drivers_operations);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830}
831
832void proc_ide_destroy(void)
833{
Zhang, Yanmin643bdc62005-05-16 21:53:11 -0700834 remove_proc_entry("drivers", proc_ide_root);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 remove_proc_entry("ide", NULL);
836}