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