linux/drivers/pnp/interface.c
<<
>>
Prefs
   1/*
   2 * interface.c - contains everything related to the user interface
   3 *
   4 * Some code, especially possible resource dumping is based on isapnp_proc.c (c) Jaroslav Kysela <perex@perex.cz>
   5 * Copyright 2002 Adam Belay <ambx1@neo.rr.com>
   6 * Copyright (C) 2008 Hewlett-Packard Development Company, L.P.
   7 *      Bjorn Helgaas <bjorn.helgaas@hp.com>
   8 */
   9
  10#include <linux/pnp.h>
  11#include <linux/string.h>
  12#include <linux/errno.h>
  13#include <linux/list.h>
  14#include <linux/types.h>
  15#include <linux/stat.h>
  16#include <linux/ctype.h>
  17#include <linux/slab.h>
  18#include <linux/mutex.h>
  19
  20#include <asm/uaccess.h>
  21
  22#include "base.h"
  23
  24struct pnp_info_buffer {
  25        char *buffer;           /* pointer to begin of buffer */
  26        char *curr;             /* current position in buffer */
  27        unsigned long size;     /* current size */
  28        unsigned long len;      /* total length of buffer */
  29        int stop;               /* stop flag */
  30        int error;              /* error code */
  31};
  32
  33typedef struct pnp_info_buffer pnp_info_buffer_t;
  34
  35static int pnp_printf(pnp_info_buffer_t * buffer, char *fmt, ...)
  36{
  37        va_list args;
  38        int res;
  39
  40        if (buffer->stop || buffer->error)
  41                return 0;
  42        va_start(args, fmt);
  43        res = vsnprintf(buffer->curr, buffer->len - buffer->size, fmt, args);
  44        va_end(args);
  45        if (buffer->size + res >= buffer->len) {
  46                buffer->stop = 1;
  47                return 0;
  48        }
  49        buffer->curr += res;
  50        buffer->size += res;
  51        return res;
  52}
  53
  54static void pnp_print_port(pnp_info_buffer_t * buffer, char *space,
  55                           struct pnp_port *port)
  56{
  57        pnp_printf(buffer, "%sport %#llx-%#llx, align %#llx, size %#llx, "
  58                   "%i-bit address decoding\n", space,
  59                   (unsigned long long) port->min,
  60                   (unsigned long long) port->max,
  61                   port->align ? ((unsigned long long) port->align - 1) : 0,
  62                   (unsigned long long) port->size,
  63                   port->flags & IORESOURCE_IO_16BIT_ADDR ? 16 : 10);
  64}
  65
  66static void pnp_print_irq(pnp_info_buffer_t * buffer, char *space,
  67                          struct pnp_irq *irq)
  68{
  69        int first = 1, i;
  70
  71        pnp_printf(buffer, "%sirq ", space);
  72        for (i = 0; i < PNP_IRQ_NR; i++)
  73                if (test_bit(i, irq->map.bits)) {
  74                        if (!first) {
  75                                pnp_printf(buffer, ",");
  76                        } else {
  77                                first = 0;
  78                        }
  79                        if (i == 2 || i == 9)
  80                                pnp_printf(buffer, "2/9");
  81                        else
  82                                pnp_printf(buffer, "%i", i);
  83                }
  84        if (bitmap_empty(irq->map.bits, PNP_IRQ_NR))
  85                pnp_printf(buffer, "<none>");
  86        if (irq->flags & IORESOURCE_IRQ_HIGHEDGE)
  87                pnp_printf(buffer, " High-Edge");
  88        if (irq->flags & IORESOURCE_IRQ_LOWEDGE)
  89                pnp_printf(buffer, " Low-Edge");
  90        if (irq->flags & IORESOURCE_IRQ_HIGHLEVEL)
  91                pnp_printf(buffer, " High-Level");
  92        if (irq->flags & IORESOURCE_IRQ_LOWLEVEL)
  93                pnp_printf(buffer, " Low-Level");
  94        if (irq->flags & IORESOURCE_IRQ_OPTIONAL)
  95                pnp_printf(buffer, " (optional)");
  96        pnp_printf(buffer, "\n");
  97}
  98
  99static void pnp_print_dma(pnp_info_buffer_t * buffer, char *space,
 100                          struct pnp_dma *dma)
 101{
 102        int first = 1, i;
 103        char *s;
 104
 105        pnp_printf(buffer, "%sdma ", space);
 106        for (i = 0; i < 8; i++)
 107                if (dma->map & (1 << i)) {
 108                        if (!first) {
 109                                pnp_printf(buffer, ",");
 110                        } else {
 111                                first = 0;
 112                        }
 113                        pnp_printf(buffer, "%i", i);
 114                }
 115        if (!dma->map)
 116                pnp_printf(buffer, "<none>");
 117        switch (dma->flags & IORESOURCE_DMA_TYPE_MASK) {
 118        case IORESOURCE_DMA_8BIT:
 119                s = "8-bit";
 120                break;
 121        case IORESOURCE_DMA_8AND16BIT:
 122                s = "8-bit&16-bit";
 123                break;
 124        default:
 125                s = "16-bit";
 126        }
 127        pnp_printf(buffer, " %s", s);
 128        if (dma->flags & IORESOURCE_DMA_MASTER)
 129                pnp_printf(buffer, " master");
 130        if (dma->flags & IORESOURCE_DMA_BYTE)
 131                pnp_printf(buffer, " byte-count");
 132        if (dma->flags & IORESOURCE_DMA_WORD)
 133                pnp_printf(buffer, " word-count");
 134        switch (dma->flags & IORESOURCE_DMA_SPEED_MASK) {
 135        case IORESOURCE_DMA_TYPEA:
 136                s = "type-A";
 137                break;
 138        case IORESOURCE_DMA_TYPEB:
 139                s = "type-B";
 140                break;
 141        case IORESOURCE_DMA_TYPEF:
 142                s = "type-F";
 143                break;
 144        default:
 145                s = "compatible";
 146                break;
 147        }
 148        pnp_printf(buffer, " %s\n", s);
 149}
 150
 151static void pnp_print_mem(pnp_info_buffer_t * buffer, char *space,
 152                          struct pnp_mem *mem)
 153{
 154        char *s;
 155
 156        pnp_printf(buffer, "%sMemory %#llx-%#llx, align %#llx, size %#llx",
 157                   space, (unsigned long long) mem->min,
 158                   (unsigned long long) mem->max,
 159                   (unsigned long long) mem->align,
 160                   (unsigned long long) mem->size);
 161        if (mem->flags & IORESOURCE_MEM_WRITEABLE)
 162                pnp_printf(buffer, ", writeable");
 163        if (mem->flags & IORESOURCE_MEM_CACHEABLE)
 164                pnp_printf(buffer, ", cacheable");
 165        if (mem->flags & IORESOURCE_MEM_RANGELENGTH)
 166                pnp_printf(buffer, ", range-length");
 167        if (mem->flags & IORESOURCE_MEM_SHADOWABLE)
 168                pnp_printf(buffer, ", shadowable");
 169        if (mem->flags & IORESOURCE_MEM_EXPANSIONROM)
 170                pnp_printf(buffer, ", expansion ROM");
 171        switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) {
 172        case IORESOURCE_MEM_8BIT:
 173                s = "8-bit";
 174                break;
 175        case IORESOURCE_MEM_8AND16BIT:
 176                s = "8-bit&16-bit";
 177                break;
 178        case IORESOURCE_MEM_32BIT:
 179                s = "32-bit";
 180                break;
 181        default:
 182                s = "16-bit";
 183        }
 184        pnp_printf(buffer, ", %s\n", s);
 185}
 186
 187static void pnp_print_option(pnp_info_buffer_t * buffer, char *space,
 188                             struct pnp_option *option)
 189{
 190        switch (option->type) {
 191        case IORESOURCE_IO:
 192                pnp_print_port(buffer, space, &option->u.port);
 193                break;
 194        case IORESOURCE_MEM:
 195                pnp_print_mem(buffer, space, &option->u.mem);
 196                break;
 197        case IORESOURCE_IRQ:
 198                pnp_print_irq(buffer, space, &option->u.irq);
 199                break;
 200        case IORESOURCE_DMA:
 201                pnp_print_dma(buffer, space, &option->u.dma);
 202                break;
 203        }
 204}
 205
 206static ssize_t pnp_show_options(struct device *dmdev,
 207                                struct device_attribute *attr, char *buf)
 208{
 209        struct pnp_dev *dev = to_pnp_dev(dmdev);
 210        pnp_info_buffer_t *buffer;
 211        struct pnp_option *option;
 212        int ret, dep = 0, set = 0;
 213        char *indent;
 214
 215        buffer = pnp_alloc(sizeof(pnp_info_buffer_t));
 216        if (!buffer)
 217                return -ENOMEM;
 218
 219        buffer->len = PAGE_SIZE;
 220        buffer->buffer = buf;
 221        buffer->curr = buffer->buffer;
 222
 223        list_for_each_entry(option, &dev->options, list) {
 224                if (pnp_option_is_dependent(option)) {
 225                        indent = "  ";
 226                        if (!dep || pnp_option_set(option) != set) {
 227                                set = pnp_option_set(option);
 228                                dep = 1;
 229                                pnp_printf(buffer, "Dependent: %02i - "
 230                                           "Priority %s\n", set,
 231                                           pnp_option_priority_name(option));
 232                        }
 233                } else {
 234                        dep = 0;
 235                        indent = "";
 236                }
 237                pnp_print_option(buffer, indent, option);
 238        }
 239
 240        ret = (buffer->curr - buf);
 241        kfree(buffer);
 242        return ret;
 243}
 244
 245static ssize_t pnp_show_current_resources(struct device *dmdev,
 246                                          struct device_attribute *attr,
 247                                          char *buf)
 248{
 249        struct pnp_dev *dev = to_pnp_dev(dmdev);
 250        pnp_info_buffer_t *buffer;
 251        struct pnp_resource *pnp_res;
 252        struct resource *res;
 253        int ret;
 254
 255        if (!dev)
 256                return -EINVAL;
 257
 258        buffer = pnp_alloc(sizeof(pnp_info_buffer_t));
 259        if (!buffer)
 260                return -ENOMEM;
 261
 262        buffer->len = PAGE_SIZE;
 263        buffer->buffer = buf;
 264        buffer->curr = buffer->buffer;
 265
 266        pnp_printf(buffer, "state = %s\n", dev->active ? "active" : "disabled");
 267
 268        list_for_each_entry(pnp_res, &dev->resources, list) {
 269                res = &pnp_res->res;
 270
 271                pnp_printf(buffer, pnp_resource_type_name(res));
 272
 273                if (res->flags & IORESOURCE_DISABLED) {
 274                        pnp_printf(buffer, " disabled\n");
 275                        continue;
 276                }
 277
 278                switch (pnp_resource_type(res)) {
 279                case IORESOURCE_IO:
 280                case IORESOURCE_MEM:
 281                        pnp_printf(buffer, " %#llx-%#llx\n",
 282                                   (unsigned long long) res->start,
 283                                   (unsigned long long) res->end);
 284                        break;
 285                case IORESOURCE_IRQ:
 286                case IORESOURCE_DMA:
 287                        pnp_printf(buffer, " %lld\n",
 288                                   (unsigned long long) res->start);
 289                        break;
 290                }
 291        }
 292
 293        ret = (buffer->curr - buf);
 294        kfree(buffer);
 295        return ret;
 296}
 297
 298static ssize_t pnp_set_current_resources(struct device *dmdev,
 299                                         struct device_attribute *attr,
 300                                         const char *ubuf, size_t count)
 301{
 302        struct pnp_dev *dev = to_pnp_dev(dmdev);
 303        char *buf = (void *)ubuf;
 304        int retval = 0;
 305        resource_size_t start, end;
 306
 307        if (dev->status & PNP_ATTACHED) {
 308                retval = -EBUSY;
 309                dev_info(&dev->dev, "in use; can't configure\n");
 310                goto done;
 311        }
 312
 313        while (isspace(*buf))
 314                ++buf;
 315        if (!strnicmp(buf, "disable", 7)) {
 316                retval = pnp_disable_dev(dev);
 317                goto done;
 318        }
 319        if (!strnicmp(buf, "activate", 8)) {
 320                retval = pnp_activate_dev(dev);
 321                goto done;
 322        }
 323        if (!strnicmp(buf, "fill", 4)) {
 324                if (dev->active)
 325                        goto done;
 326                retval = pnp_auto_config_dev(dev);
 327                goto done;
 328        }
 329        if (!strnicmp(buf, "auto", 4)) {
 330                if (dev->active)
 331                        goto done;
 332                pnp_init_resources(dev);
 333                retval = pnp_auto_config_dev(dev);
 334                goto done;
 335        }
 336        if (!strnicmp(buf, "clear", 5)) {
 337                if (dev->active)
 338                        goto done;
 339                pnp_init_resources(dev);
 340                goto done;
 341        }
 342        if (!strnicmp(buf, "get", 3)) {
 343                mutex_lock(&pnp_res_mutex);
 344                if (pnp_can_read(dev))
 345                        dev->protocol->get(dev);
 346                mutex_unlock(&pnp_res_mutex);
 347                goto done;
 348        }
 349        if (!strnicmp(buf, "set", 3)) {
 350                if (dev->active)
 351                        goto done;
 352                buf += 3;
 353                pnp_init_resources(dev);
 354                mutex_lock(&pnp_res_mutex);
 355                while (1) {
 356                        while (isspace(*buf))
 357                                ++buf;
 358                        if (!strnicmp(buf, "io", 2)) {
 359                                buf += 2;
 360                                while (isspace(*buf))
 361                                        ++buf;
 362                                start = simple_strtoul(buf, &buf, 0);
 363                                while (isspace(*buf))
 364                                        ++buf;
 365                                if (*buf == '-') {
 366                                        buf += 1;
 367                                        while (isspace(*buf))
 368                                                ++buf;
 369                                        end = simple_strtoul(buf, &buf, 0);
 370                                } else
 371                                        end = start;
 372                                pnp_add_io_resource(dev, start, end, 0);
 373                                continue;
 374                        }
 375                        if (!strnicmp(buf, "mem", 3)) {
 376                                buf += 3;
 377                                while (isspace(*buf))
 378                                        ++buf;
 379                                start = simple_strtoul(buf, &buf, 0);
 380                                while (isspace(*buf))
 381                                        ++buf;
 382                                if (*buf == '-') {
 383                                        buf += 1;
 384                                        while (isspace(*buf))
 385                                                ++buf;
 386                                        end = simple_strtoul(buf, &buf, 0);
 387                                } else
 388                                        end = start;
 389                                pnp_add_mem_resource(dev, start, end, 0);
 390                                continue;
 391                        }
 392                        if (!strnicmp(buf, "irq", 3)) {
 393                                buf += 3;
 394                                while (isspace(*buf))
 395                                        ++buf;
 396                                start = simple_strtoul(buf, &buf, 0);
 397                                pnp_add_irq_resource(dev, start, 0);
 398                                continue;
 399                        }
 400                        if (!strnicmp(buf, "dma", 3)) {
 401                                buf += 3;
 402                                while (isspace(*buf))
 403                                        ++buf;
 404                                start = simple_strtoul(buf, &buf, 0);
 405                                pnp_add_dma_resource(dev, start, 0);
 406                                continue;
 407                        }
 408                        break;
 409                }
 410                mutex_unlock(&pnp_res_mutex);
 411                goto done;
 412        }
 413
 414done:
 415        if (retval < 0)
 416                return retval;
 417        return count;
 418}
 419
 420static ssize_t pnp_show_current_ids(struct device *dmdev,
 421                                    struct device_attribute *attr, char *buf)
 422{
 423        char *str = buf;
 424        struct pnp_dev *dev = to_pnp_dev(dmdev);
 425        struct pnp_id *pos = dev->id;
 426
 427        while (pos) {
 428                str += sprintf(str, "%s\n", pos->id);
 429                pos = pos->next;
 430        }
 431        return (str - buf);
 432}
 433
 434struct device_attribute pnp_interface_attrs[] = {
 435        __ATTR(resources, S_IRUGO | S_IWUSR,
 436                   pnp_show_current_resources,
 437                   pnp_set_current_resources),
 438        __ATTR(options, S_IRUGO, pnp_show_options, NULL),
 439        __ATTR(id, S_IRUGO, pnp_show_current_ids, NULL),
 440        __ATTR_NULL,
 441};
 442