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