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