linux/drivers/ide/ide-proc.c
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 1997-1998     Mark Lord
   3 *  Copyright (C) 2003          Red Hat
   4 *
   5 *  Some code was moved here from ide.c, see it for original copyrights.
   6 */
   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
  15 */
  16
  17#include <linux/module.h>
  18
  19#include <linux/uaccess.h>
  20#include <linux/errno.h>
  21#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/ide.h>
  27#include <linux/seq_file.h>
  28#include <linux/slab.h>
  29
  30#include <asm/io.h>
  31
  32static struct proc_dir_entry *proc_ide_root;
  33
  34static int ide_imodel_proc_show(struct seq_file *m, void *v)
  35{
  36        ide_hwif_t      *hwif = (ide_hwif_t *) m->private;
  37        const char      *name;
  38
  39        switch (hwif->chipset) {
  40        case ide_generic:       name = "generic";       break;
  41        case ide_pci:           name = "pci";           break;
  42        case ide_cmd640:        name = "cmd640";        break;
  43        case ide_dtc2278:       name = "dtc2278";       break;
  44        case ide_ali14xx:       name = "ali14xx";       break;
  45        case ide_qd65xx:        name = "qd65xx";        break;
  46        case ide_umc8672:       name = "umc8672";       break;
  47        case ide_ht6560b:       name = "ht6560b";       break;
  48        case ide_4drives:       name = "4drives";       break;
  49        case ide_pmac:          name = "mac-io";        break;
  50        case ide_au1xxx:        name = "au1xxx";        break;
  51        case ide_palm3710:      name = "palm3710";      break;
  52        case ide_acorn:         name = "acorn";         break;
  53        default:                name = "(unknown)";     break;
  54        }
  55        seq_printf(m, "%s\n", name);
  56        return 0;
  57}
  58
  59static int ide_mate_proc_show(struct seq_file *m, void *v)
  60{
  61        ide_hwif_t      *hwif = (ide_hwif_t *) m->private;
  62
  63        if (hwif && hwif->mate)
  64                seq_printf(m, "%s\n", hwif->mate->name);
  65        else
  66                seq_printf(m, "(none)\n");
  67        return 0;
  68}
  69
  70static int ide_channel_proc_show(struct seq_file *m, void *v)
  71{
  72        ide_hwif_t      *hwif = (ide_hwif_t *) m->private;
  73
  74        seq_printf(m, "%c\n", hwif->channel ? '1' : '0');
  75        return 0;
  76}
  77
  78static int ide_identify_proc_show(struct seq_file *m, void *v)
  79{
  80        ide_drive_t *drive = (ide_drive_t *)m->private;
  81        u8 *buf;
  82
  83        if (!drive) {
  84                seq_putc(m, '\n');
  85                return 0;
  86        }
  87
  88        buf = kmalloc(SECTOR_SIZE, GFP_KERNEL);
  89        if (!buf)
  90                return -ENOMEM;
  91        if (taskfile_lib_get_identify(drive, buf) == 0) {
  92                __le16 *val = (__le16 *)buf;
  93                int i;
  94
  95                for (i = 0; i < SECTOR_SIZE / 2; i++) {
  96                        seq_printf(m, "%04x%c", le16_to_cpu(val[i]),
  97                                        (i % 8) == 7 ? '\n' : ' ');
  98                }
  99        } else
 100                seq_putc(m, buf[0]);
 101        kfree(buf);
 102        return 0;
 103}
 104
 105/**
 106 *      ide_find_setting        -       find a specific setting
 107 *      @st: setting table pointer
 108 *      @name: setting name
 109 *
 110 *      Scan's the setting table for a matching entry and returns
 111 *      this or NULL if no entry is found. The caller must hold the
 112 *      setting semaphore
 113 */
 114
 115static
 116const struct ide_proc_devset *ide_find_setting(const struct ide_proc_devset *st,
 117                                               char *name)
 118{
 119        while (st->name) {
 120                if (strcmp(st->name, name) == 0)
 121                        break;
 122                st++;
 123        }
 124        return st->name ? st : NULL;
 125}
 126
 127/**
 128 *      ide_read_setting        -       read an IDE setting
 129 *      @drive: drive to read from
 130 *      @setting: drive setting
 131 *
 132 *      Read a drive setting and return the value. The caller
 133 *      must hold the ide_setting_mtx when making this call.
 134 *
 135 *      BUGS: the data return and error are the same return value
 136 *      so an error -EINVAL and true return of the same value cannot
 137 *      be told apart
 138 */
 139
 140static int ide_read_setting(ide_drive_t *drive,
 141                            const struct ide_proc_devset *setting)
 142{
 143        const struct ide_devset *ds = setting->setting;
 144        int val = -EINVAL;
 145
 146        if (ds->get)
 147                val = ds->get(drive);
 148
 149        return val;
 150}
 151
 152/**
 153 *      ide_write_setting       -       read an IDE setting
 154 *      @drive: drive to read from
 155 *      @setting: drive setting
 156 *      @val: value
 157 *
 158 *      Write a drive setting if it is possible. The caller
 159 *      must hold the ide_setting_mtx when making this call.
 160 *
 161 *      BUGS: the data return and error are the same return value
 162 *      so an error -EINVAL and true return of the same value cannot
 163 *      be told apart
 164 *
 165 *      FIXME:  This should be changed to enqueue a special request
 166 *      to the driver to change settings, and then wait on a sema for completion.
 167 *      The current scheme of polling is kludgy, though safe enough.
 168 */
 169
 170static int ide_write_setting(ide_drive_t *drive,
 171                             const struct ide_proc_devset *setting, int val)
 172{
 173        const struct ide_devset *ds = setting->setting;
 174
 175        if (!capable(CAP_SYS_ADMIN))
 176                return -EACCES;
 177        if (!ds->set)
 178                return -EPERM;
 179        if ((ds->flags & DS_SYNC)
 180            && (val < setting->min || val > setting->max))
 181                return -EINVAL;
 182        return ide_devset_execute(drive, ds, val);
 183}
 184
 185ide_devset_get(xfer_rate, current_speed);
 186
 187static int set_xfer_rate (ide_drive_t *drive, int arg)
 188{
 189        struct ide_cmd cmd;
 190
 191        if (arg < XFER_PIO_0 || arg > XFER_UDMA_6)
 192                return -EINVAL;
 193
 194        memset(&cmd, 0, sizeof(cmd));
 195        cmd.tf.command = ATA_CMD_SET_FEATURES;
 196        cmd.tf.feature = SETFEATURES_XFER;
 197        cmd.tf.nsect   = (u8)arg;
 198        cmd.valid.out.tf = IDE_VALID_FEATURE | IDE_VALID_NSECT;
 199        cmd.valid.in.tf  = IDE_VALID_NSECT;
 200        cmd.tf_flags   = IDE_TFLAG_SET_XFER;
 201
 202        return ide_no_data_taskfile(drive, &cmd);
 203}
 204
 205ide_devset_rw(current_speed, xfer_rate);
 206ide_devset_rw_field(init_speed, init_speed);
 207ide_devset_rw_flag(nice1, IDE_DFLAG_NICE1);
 208ide_devset_rw_field(number, dn);
 209
 210static const struct ide_proc_devset ide_generic_settings[] = {
 211        IDE_PROC_DEVSET(current_speed, 0, 70),
 212        IDE_PROC_DEVSET(init_speed, 0, 70),
 213        IDE_PROC_DEVSET(io_32bit,  0, 1 + (SUPPORT_VLB_SYNC << 1)),
 214        IDE_PROC_DEVSET(keepsettings, 0, 1),
 215        IDE_PROC_DEVSET(nice1, 0, 1),
 216        IDE_PROC_DEVSET(number, 0, 3),
 217        IDE_PROC_DEVSET(pio_mode, 0, 255),
 218        IDE_PROC_DEVSET(unmaskirq, 0, 1),
 219        IDE_PROC_DEVSET(using_dma, 0, 1),
 220        { NULL },
 221};
 222
 223static void proc_ide_settings_warn(void)
 224{
 225        printk_once(KERN_WARNING "Warning: /proc/ide/hd?/settings interface is "
 226                            "obsolete, and will be removed soon!\n");
 227}
 228
 229static int ide_settings_proc_show(struct seq_file *m, void *v)
 230{
 231        const struct ide_proc_devset *setting, *g, *d;
 232        const struct ide_devset *ds;
 233        ide_drive_t     *drive = (ide_drive_t *) m->private;
 234        int             rc, mul_factor, div_factor;
 235
 236        proc_ide_settings_warn();
 237
 238        mutex_lock(&ide_setting_mtx);
 239        g = ide_generic_settings;
 240        d = drive->settings;
 241        seq_printf(m, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
 242        seq_printf(m, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
 243        while (g->name || (d && d->name)) {
 244                /* read settings in the alphabetical order */
 245                if (g->name && d && d->name) {
 246                        if (strcmp(d->name, g->name) < 0)
 247                                setting = d++;
 248                        else
 249                                setting = g++;
 250                } else if (d && d->name) {
 251                        setting = d++;
 252                } else
 253                        setting = g++;
 254                mul_factor = setting->mulf ? setting->mulf(drive) : 1;
 255                div_factor = setting->divf ? setting->divf(drive) : 1;
 256                seq_printf(m, "%-24s", setting->name);
 257                rc = ide_read_setting(drive, setting);
 258                if (rc >= 0)
 259                        seq_printf(m, "%-16d", rc * mul_factor / div_factor);
 260                else
 261                        seq_printf(m, "%-16s", "write-only");
 262                seq_printf(m, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
 263                ds = setting->setting;
 264                if (ds->get)
 265                        seq_printf(m, "r");
 266                if (ds->set)
 267                        seq_printf(m, "w");
 268                seq_printf(m, "\n");
 269        }
 270        mutex_unlock(&ide_setting_mtx);
 271        return 0;
 272}
 273
 274static int ide_settings_proc_open(struct inode *inode, struct file *file)
 275{
 276        return single_open(file, ide_settings_proc_show, PDE_DATA(inode));
 277}
 278
 279#define MAX_LEN 30
 280
 281static ssize_t ide_settings_proc_write(struct file *file, const char __user *buffer,
 282                                       size_t count, loff_t *pos)
 283{
 284        ide_drive_t     *drive = PDE_DATA(file_inode(file));
 285        char            name[MAX_LEN + 1];
 286        int             for_real = 0, mul_factor, div_factor;
 287        unsigned long   n;
 288
 289        const struct ide_proc_devset *setting;
 290        char *buf, *s;
 291
 292        if (!capable(CAP_SYS_ADMIN))
 293                return -EACCES;
 294
 295        proc_ide_settings_warn();
 296
 297        if (count >= PAGE_SIZE)
 298                return -EINVAL;
 299
 300        s = buf = (char *)__get_free_page(GFP_USER);
 301        if (!buf)
 302                return -ENOMEM;
 303
 304        if (copy_from_user(buf, buffer, count)) {
 305                free_page((unsigned long)buf);
 306                return -EFAULT;
 307        }
 308
 309        buf[count] = '\0';
 310
 311        /*
 312         * Skip over leading whitespace
 313         */
 314        while (count && isspace(*s)) {
 315                --count;
 316                ++s;
 317        }
 318        /*
 319         * Do one full pass to verify all parameters,
 320         * then do another to actually write the new settings.
 321         */
 322        do {
 323                char *p = s;
 324                n = count;
 325                while (n > 0) {
 326                        unsigned val;
 327                        char *q = p;
 328
 329                        while (n > 0 && *p != ':') {
 330                                --n;
 331                                p++;
 332                        }
 333                        if (*p != ':')
 334                                goto parse_error;
 335                        if (p - q > MAX_LEN)
 336                                goto parse_error;
 337                        memcpy(name, q, p - q);
 338                        name[p - q] = 0;
 339
 340                        if (n > 0) {
 341                                --n;
 342                                p++;
 343                        } else
 344                                goto parse_error;
 345
 346                        val = simple_strtoul(p, &q, 10);
 347                        n -= q - p;
 348                        p = q;
 349                        if (n > 0 && !isspace(*p))
 350                                goto parse_error;
 351                        while (n > 0 && isspace(*p)) {
 352                                --n;
 353                                ++p;
 354                        }
 355
 356                        mutex_lock(&ide_setting_mtx);
 357                        /* generic settings first, then driver specific ones */
 358                        setting = ide_find_setting(ide_generic_settings, name);
 359                        if (!setting) {
 360                                if (drive->settings)
 361                                        setting = ide_find_setting(drive->settings, name);
 362                                if (!setting) {
 363                                        mutex_unlock(&ide_setting_mtx);
 364                                        goto parse_error;
 365                                }
 366                        }
 367                        if (for_real) {
 368                                mul_factor = setting->mulf ? setting->mulf(drive) : 1;
 369                                div_factor = setting->divf ? setting->divf(drive) : 1;
 370                                ide_write_setting(drive, setting, val * div_factor / mul_factor);
 371                        }
 372                        mutex_unlock(&ide_setting_mtx);
 373                }
 374        } while (!for_real++);
 375        free_page((unsigned long)buf);
 376        return count;
 377parse_error:
 378        free_page((unsigned long)buf);
 379        printk("%s(): parse error\n", __func__);
 380        return -EINVAL;
 381}
 382
 383static const struct file_operations ide_settings_proc_fops = {
 384        .owner          = THIS_MODULE,
 385        .open           = ide_settings_proc_open,
 386        .read           = seq_read,
 387        .llseek         = seq_lseek,
 388        .release        = single_release,
 389        .write          = ide_settings_proc_write,
 390};
 391
 392int ide_capacity_proc_show(struct seq_file *m, void *v)
 393{
 394        seq_printf(m, "%llu\n", (long long)0x7fffffff);
 395        return 0;
 396}
 397EXPORT_SYMBOL_GPL(ide_capacity_proc_show);
 398
 399int ide_geometry_proc_show(struct seq_file *m, void *v)
 400{
 401        ide_drive_t     *drive = (ide_drive_t *) m->private;
 402
 403        seq_printf(m, "physical     %d/%d/%d\n",
 404                        drive->cyl, drive->head, drive->sect);
 405        seq_printf(m, "logical      %d/%d/%d\n",
 406                        drive->bios_cyl, drive->bios_head, drive->bios_sect);
 407        return 0;
 408}
 409EXPORT_SYMBOL(ide_geometry_proc_show);
 410
 411static int ide_dmodel_proc_show(struct seq_file *seq, void *v)
 412{
 413        ide_drive_t     *drive = (ide_drive_t *) seq->private;
 414        char            *m = (char *)&drive->id[ATA_ID_PROD];
 415
 416        seq_printf(seq, "%.40s\n", m[0] ? m : "(none)");
 417        return 0;
 418}
 419
 420static int ide_driver_proc_show(struct seq_file *m, void *v)
 421{
 422        ide_drive_t             *drive = (ide_drive_t *)m->private;
 423        struct device           *dev = &drive->gendev;
 424        struct ide_driver       *ide_drv;
 425
 426        if (dev->driver) {
 427                ide_drv = to_ide_driver(dev->driver);
 428                seq_printf(m, "%s version %s\n",
 429                                dev->driver->name, ide_drv->version);
 430        } else
 431                seq_printf(m, "ide-default version 0.9.newide\n");
 432        return 0;
 433}
 434
 435static int ide_media_proc_show(struct seq_file *m, void *v)
 436{
 437        ide_drive_t     *drive = (ide_drive_t *) m->private;
 438        const char      *media;
 439
 440        switch (drive->media) {
 441        case ide_disk:          media = "disk\n";       break;
 442        case ide_cdrom:         media = "cdrom\n";      break;
 443        case ide_tape:          media = "tape\n";       break;
 444        case ide_floppy:        media = "floppy\n";     break;
 445        case ide_optical:       media = "optical\n";    break;
 446        default:                media = "UNKNOWN\n";    break;
 447        }
 448        seq_puts(m, media);
 449        return 0;
 450}
 451
 452static int ide_media_proc_open(struct inode *inode, struct file *file)
 453{
 454        return single_open(file, ide_media_proc_show, PDE_DATA(inode));
 455}
 456
 457static const struct file_operations ide_media_proc_fops = {
 458        .owner          = THIS_MODULE,
 459        .open           = ide_media_proc_open,
 460        .read           = seq_read,
 461        .llseek         = seq_lseek,
 462        .release        = single_release,
 463};
 464
 465static ide_proc_entry_t generic_drive_entries[] = {
 466        { "driver",     S_IFREG|S_IRUGO,         ide_driver_proc_show   },
 467        { "identify",   S_IFREG|S_IRUSR,         ide_identify_proc_show },
 468        { "media",      S_IFREG|S_IRUGO,         ide_media_proc_show    },
 469        { "model",      S_IFREG|S_IRUGO,         ide_dmodel_proc_show   },
 470        {}
 471};
 472
 473static void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data)
 474{
 475        struct proc_dir_entry *ent;
 476
 477        if (!dir || !p)
 478                return;
 479        while (p->name != NULL) {
 480                ent = proc_create_single_data(p->name, p->mode, dir, p->show, data);
 481                if (!ent) return;
 482                p++;
 483        }
 484}
 485
 486static void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p)
 487{
 488        if (!dir || !p)
 489                return;
 490        while (p->name != NULL) {
 491                remove_proc_entry(p->name, dir);
 492                p++;
 493        }
 494}
 495
 496void ide_proc_register_driver(ide_drive_t *drive, struct ide_driver *driver)
 497{
 498        mutex_lock(&ide_setting_mtx);
 499        drive->settings = driver->proc_devsets(drive);
 500        mutex_unlock(&ide_setting_mtx);
 501
 502        ide_add_proc_entries(drive->proc, driver->proc_entries(drive), drive);
 503}
 504
 505EXPORT_SYMBOL(ide_proc_register_driver);
 506
 507/**
 508 *      ide_proc_unregister_driver      -       remove driver specific data
 509 *      @drive: drive
 510 *      @driver: driver
 511 *
 512 *      Clean up the driver specific /proc files and IDE settings
 513 *      for a given drive.
 514 *
 515 *      Takes ide_setting_mtx.
 516 */
 517
 518void ide_proc_unregister_driver(ide_drive_t *drive, struct ide_driver *driver)
 519{
 520        ide_remove_proc_entries(drive->proc, driver->proc_entries(drive));
 521
 522        mutex_lock(&ide_setting_mtx);
 523        /*
 524         * ide_setting_mtx protects both the settings list and the use
 525         * of settings (we cannot take a setting out that is being used).
 526         */
 527        drive->settings = NULL;
 528        mutex_unlock(&ide_setting_mtx);
 529}
 530EXPORT_SYMBOL(ide_proc_unregister_driver);
 531
 532void ide_proc_port_register_devices(ide_hwif_t *hwif)
 533{
 534        struct proc_dir_entry *ent;
 535        struct proc_dir_entry *parent = hwif->proc;
 536        ide_drive_t *drive;
 537        char name[64];
 538        int i;
 539
 540        ide_port_for_each_dev(i, drive, hwif) {
 541                if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
 542                        continue;
 543
 544                drive->proc = proc_mkdir(drive->name, parent);
 545                if (drive->proc) {
 546                        ide_add_proc_entries(drive->proc, generic_drive_entries, drive);
 547                        proc_create_data("setting", S_IFREG|S_IRUSR|S_IWUSR,
 548                                        drive->proc, &ide_settings_proc_fops,
 549                                        drive);
 550                }
 551                sprintf(name, "ide%d/%s", (drive->name[2]-'a')/2, drive->name);
 552                ent = proc_symlink(drive->name, proc_ide_root, name);
 553                if (!ent) return;
 554        }
 555}
 556
 557void ide_proc_unregister_device(ide_drive_t *drive)
 558{
 559        if (drive->proc) {
 560                remove_proc_entry("settings", drive->proc);
 561                ide_remove_proc_entries(drive->proc, generic_drive_entries);
 562                remove_proc_entry(drive->name, proc_ide_root);
 563                remove_proc_entry(drive->name, drive->hwif->proc);
 564                drive->proc = NULL;
 565        }
 566}
 567
 568static ide_proc_entry_t hwif_entries[] = {
 569        { "channel",    S_IFREG|S_IRUGO,        ide_channel_proc_show   },
 570        { "mate",       S_IFREG|S_IRUGO,        ide_mate_proc_show      },
 571        { "model",      S_IFREG|S_IRUGO,        ide_imodel_proc_show    },
 572        {}
 573};
 574
 575void ide_proc_register_port(ide_hwif_t *hwif)
 576{
 577        if (!hwif->proc) {
 578                hwif->proc = proc_mkdir(hwif->name, proc_ide_root);
 579
 580                if (!hwif->proc)
 581                        return;
 582
 583                ide_add_proc_entries(hwif->proc, hwif_entries, hwif);
 584        }
 585}
 586
 587void ide_proc_unregister_port(ide_hwif_t *hwif)
 588{
 589        if (hwif->proc) {
 590                ide_remove_proc_entries(hwif->proc, hwif_entries);
 591                remove_proc_entry(hwif->name, proc_ide_root);
 592                hwif->proc = NULL;
 593        }
 594}
 595
 596static int proc_print_driver(struct device_driver *drv, void *data)
 597{
 598        struct ide_driver *ide_drv = to_ide_driver(drv);
 599        struct seq_file *s = data;
 600
 601        seq_printf(s, "%s version %s\n", drv->name, ide_drv->version);
 602
 603        return 0;
 604}
 605
 606static int ide_drivers_show(struct seq_file *s, void *p)
 607{
 608        int err;
 609
 610        err = bus_for_each_drv(&ide_bus_type, NULL, s, proc_print_driver);
 611        if (err < 0)
 612                printk(KERN_WARNING "IDE: %s: bus_for_each_drv error: %d\n",
 613                        __func__, err);
 614        return 0;
 615}
 616
 617static int ide_drivers_open(struct inode *inode, struct file *file)
 618{
 619        return single_open(file, &ide_drivers_show, NULL);
 620}
 621
 622static const struct file_operations ide_drivers_operations = {
 623        .owner          = THIS_MODULE,
 624        .open           = ide_drivers_open,
 625        .read           = seq_read,
 626        .llseek         = seq_lseek,
 627        .release        = single_release,
 628};
 629
 630void proc_ide_create(void)
 631{
 632        proc_ide_root = proc_mkdir("ide", NULL);
 633
 634        if (!proc_ide_root)
 635                return;
 636
 637        proc_create("drivers", 0, proc_ide_root, &ide_drivers_operations);
 638}
 639
 640void proc_ide_destroy(void)
 641{
 642        remove_proc_entry("drivers", proc_ide_root);
 643        remove_proc_entry("ide", NULL);
 644}
 645