Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * interface.c - contains everything related to the user interface
0004  *
0005  * Some code, especially possible resource dumping is based on isapnp_proc.c (c) Jaroslav Kysela <perex@perex.cz>
0006  * Copyright 2002 Adam Belay <ambx1@neo.rr.com>
0007  * Copyright (C) 2008 Hewlett-Packard Development Company, L.P.
0008  *  Bjorn Helgaas <bjorn.helgaas@hp.com>
0009  */
0010 
0011 #include <linux/pnp.h>
0012 #include <linux/string.h>
0013 #include <linux/errno.h>
0014 #include <linux/list.h>
0015 #include <linux/types.h>
0016 #include <linux/stat.h>
0017 #include <linux/ctype.h>
0018 #include <linux/slab.h>
0019 #include <linux/mutex.h>
0020 
0021 #include <linux/uaccess.h>
0022 
0023 #include "base.h"
0024 
0025 struct pnp_info_buffer {
0026     char *buffer;       /* pointer to begin of buffer */
0027     char *curr;     /* current position in buffer */
0028     unsigned long size; /* current size */
0029     unsigned long len;  /* total length of buffer */
0030     int stop;       /* stop flag */
0031     int error;      /* error code */
0032 };
0033 
0034 typedef struct pnp_info_buffer pnp_info_buffer_t;
0035 
0036 __printf(2, 3)
0037 static int pnp_printf(pnp_info_buffer_t * buffer, char *fmt, ...)
0038 {
0039     va_list args;
0040     int res;
0041 
0042     if (buffer->stop || buffer->error)
0043         return 0;
0044     va_start(args, fmt);
0045     res = vsnprintf(buffer->curr, buffer->len - buffer->size, fmt, args);
0046     va_end(args);
0047     if (buffer->size + res >= buffer->len) {
0048         buffer->stop = 1;
0049         return 0;
0050     }
0051     buffer->curr += res;
0052     buffer->size += res;
0053     return res;
0054 }
0055 
0056 static void pnp_print_port(pnp_info_buffer_t * buffer, char *space,
0057                struct pnp_port *port)
0058 {
0059     pnp_printf(buffer, "%sport %#llx-%#llx, align %#llx, size %#llx, "
0060            "%i-bit address decoding\n", space,
0061            (unsigned long long) port->min,
0062            (unsigned long long) port->max,
0063            port->align ? ((unsigned long long) port->align - 1) : 0,
0064            (unsigned long long) port->size,
0065            port->flags & IORESOURCE_IO_16BIT_ADDR ? 16 : 10);
0066 }
0067 
0068 static void pnp_print_irq(pnp_info_buffer_t * buffer, char *space,
0069               struct pnp_irq *irq)
0070 {
0071     int first = 1, i;
0072 
0073     pnp_printf(buffer, "%sirq ", space);
0074     for (i = 0; i < PNP_IRQ_NR; i++)
0075         if (test_bit(i, irq->map.bits)) {
0076             if (!first) {
0077                 pnp_printf(buffer, ",");
0078             } else {
0079                 first = 0;
0080             }
0081             if (i == 2 || i == 9)
0082                 pnp_printf(buffer, "2/9");
0083             else
0084                 pnp_printf(buffer, "%i", i);
0085         }
0086     if (bitmap_empty(irq->map.bits, PNP_IRQ_NR))
0087         pnp_printf(buffer, "<none>");
0088     if (irq->flags & IORESOURCE_IRQ_HIGHEDGE)
0089         pnp_printf(buffer, " High-Edge");
0090     if (irq->flags & IORESOURCE_IRQ_LOWEDGE)
0091         pnp_printf(buffer, " Low-Edge");
0092     if (irq->flags & IORESOURCE_IRQ_HIGHLEVEL)
0093         pnp_printf(buffer, " High-Level");
0094     if (irq->flags & IORESOURCE_IRQ_LOWLEVEL)
0095         pnp_printf(buffer, " Low-Level");
0096     if (irq->flags & IORESOURCE_IRQ_OPTIONAL)
0097         pnp_printf(buffer, " (optional)");
0098     pnp_printf(buffer, "\n");
0099 }
0100 
0101 static void pnp_print_dma(pnp_info_buffer_t * buffer, char *space,
0102               struct pnp_dma *dma)
0103 {
0104     int first = 1, i;
0105     char *s;
0106 
0107     pnp_printf(buffer, "%sdma ", space);
0108     for (i = 0; i < 8; i++)
0109         if (dma->map & (1 << i)) {
0110             if (!first) {
0111                 pnp_printf(buffer, ",");
0112             } else {
0113                 first = 0;
0114             }
0115             pnp_printf(buffer, "%i", i);
0116         }
0117     if (!dma->map)
0118         pnp_printf(buffer, "<none>");
0119     switch (dma->flags & IORESOURCE_DMA_TYPE_MASK) {
0120     case IORESOURCE_DMA_8BIT:
0121         s = "8-bit";
0122         break;
0123     case IORESOURCE_DMA_8AND16BIT:
0124         s = "8-bit&16-bit";
0125         break;
0126     default:
0127         s = "16-bit";
0128     }
0129     pnp_printf(buffer, " %s", s);
0130     if (dma->flags & IORESOURCE_DMA_MASTER)
0131         pnp_printf(buffer, " master");
0132     if (dma->flags & IORESOURCE_DMA_BYTE)
0133         pnp_printf(buffer, " byte-count");
0134     if (dma->flags & IORESOURCE_DMA_WORD)
0135         pnp_printf(buffer, " word-count");
0136     switch (dma->flags & IORESOURCE_DMA_SPEED_MASK) {
0137     case IORESOURCE_DMA_TYPEA:
0138         s = "type-A";
0139         break;
0140     case IORESOURCE_DMA_TYPEB:
0141         s = "type-B";
0142         break;
0143     case IORESOURCE_DMA_TYPEF:
0144         s = "type-F";
0145         break;
0146     default:
0147         s = "compatible";
0148         break;
0149     }
0150     pnp_printf(buffer, " %s\n", s);
0151 }
0152 
0153 static void pnp_print_mem(pnp_info_buffer_t * buffer, char *space,
0154               struct pnp_mem *mem)
0155 {
0156     char *s;
0157 
0158     pnp_printf(buffer, "%sMemory %#llx-%#llx, align %#llx, size %#llx",
0159            space, (unsigned long long) mem->min,
0160            (unsigned long long) mem->max,
0161            (unsigned long long) mem->align,
0162            (unsigned long long) mem->size);
0163     if (mem->flags & IORESOURCE_MEM_WRITEABLE)
0164         pnp_printf(buffer, ", writeable");
0165     if (mem->flags & IORESOURCE_MEM_CACHEABLE)
0166         pnp_printf(buffer, ", cacheable");
0167     if (mem->flags & IORESOURCE_MEM_RANGELENGTH)
0168         pnp_printf(buffer, ", range-length");
0169     if (mem->flags & IORESOURCE_MEM_SHADOWABLE)
0170         pnp_printf(buffer, ", shadowable");
0171     if (mem->flags & IORESOURCE_MEM_EXPANSIONROM)
0172         pnp_printf(buffer, ", expansion ROM");
0173     switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) {
0174     case IORESOURCE_MEM_8BIT:
0175         s = "8-bit";
0176         break;
0177     case IORESOURCE_MEM_8AND16BIT:
0178         s = "8-bit&16-bit";
0179         break;
0180     case IORESOURCE_MEM_32BIT:
0181         s = "32-bit";
0182         break;
0183     default:
0184         s = "16-bit";
0185     }
0186     pnp_printf(buffer, ", %s\n", s);
0187 }
0188 
0189 static void pnp_print_option(pnp_info_buffer_t * buffer, char *space,
0190                  struct pnp_option *option)
0191 {
0192     switch (option->type) {
0193     case IORESOURCE_IO:
0194         pnp_print_port(buffer, space, &option->u.port);
0195         break;
0196     case IORESOURCE_MEM:
0197         pnp_print_mem(buffer, space, &option->u.mem);
0198         break;
0199     case IORESOURCE_IRQ:
0200         pnp_print_irq(buffer, space, &option->u.irq);
0201         break;
0202     case IORESOURCE_DMA:
0203         pnp_print_dma(buffer, space, &option->u.dma);
0204         break;
0205     }
0206 }
0207 
0208 static ssize_t options_show(struct device *dmdev, struct device_attribute *attr,
0209                 char *buf)
0210 {
0211     struct pnp_dev *dev = to_pnp_dev(dmdev);
0212     pnp_info_buffer_t *buffer;
0213     struct pnp_option *option;
0214     int ret, dep = 0, set = 0;
0215     char *indent;
0216 
0217     buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
0218     if (!buffer)
0219         return -ENOMEM;
0220 
0221     buffer->len = PAGE_SIZE;
0222     buffer->buffer = buf;
0223     buffer->curr = buffer->buffer;
0224 
0225     list_for_each_entry(option, &dev->options, list) {
0226         if (pnp_option_is_dependent(option)) {
0227             indent = "  ";
0228             if (!dep || pnp_option_set(option) != set) {
0229                 set = pnp_option_set(option);
0230                 dep = 1;
0231                 pnp_printf(buffer, "Dependent: %02i - "
0232                        "Priority %s\n", set,
0233                        pnp_option_priority_name(option));
0234             }
0235         } else {
0236             dep = 0;
0237             indent = "";
0238         }
0239         pnp_print_option(buffer, indent, option);
0240     }
0241 
0242     ret = (buffer->curr - buf);
0243     kfree(buffer);
0244     return ret;
0245 }
0246 static DEVICE_ATTR_RO(options);
0247 
0248 static ssize_t resources_show(struct device *dmdev,
0249                   struct device_attribute *attr, char *buf)
0250 {
0251     struct pnp_dev *dev = to_pnp_dev(dmdev);
0252     pnp_info_buffer_t *buffer;
0253     struct pnp_resource *pnp_res;
0254     struct resource *res;
0255     int ret;
0256 
0257     if (!dev)
0258         return -EINVAL;
0259 
0260     buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
0261     if (!buffer)
0262         return -ENOMEM;
0263 
0264     buffer->len = PAGE_SIZE;
0265     buffer->buffer = buf;
0266     buffer->curr = buffer->buffer;
0267 
0268     pnp_printf(buffer, "state = %s\n", dev->active ? "active" : "disabled");
0269 
0270     list_for_each_entry(pnp_res, &dev->resources, list) {
0271         res = &pnp_res->res;
0272 
0273         pnp_printf(buffer, pnp_resource_type_name(res));
0274 
0275         if (res->flags & IORESOURCE_DISABLED) {
0276             pnp_printf(buffer, " disabled\n");
0277             continue;
0278         }
0279 
0280         switch (pnp_resource_type(res)) {
0281         case IORESOURCE_IO:
0282         case IORESOURCE_MEM:
0283         case IORESOURCE_BUS:
0284             pnp_printf(buffer, " %#llx-%#llx%s\n",
0285                    (unsigned long long) res->start,
0286                    (unsigned long long) res->end,
0287                    res->flags & IORESOURCE_WINDOW ?
0288                     " window" : "");
0289             break;
0290         case IORESOURCE_IRQ:
0291         case IORESOURCE_DMA:
0292             pnp_printf(buffer, " %lld\n",
0293                    (unsigned long long) res->start);
0294             break;
0295         }
0296     }
0297 
0298     ret = (buffer->curr - buf);
0299     kfree(buffer);
0300     return ret;
0301 }
0302 
0303 static char *pnp_get_resource_value(char *buf,
0304                     unsigned long type,
0305                     resource_size_t *start,
0306                     resource_size_t *end,
0307                     unsigned long *flags)
0308 {
0309     if (start)
0310         *start = 0;
0311     if (end)
0312         *end = 0;
0313     if (flags)
0314         *flags = 0;
0315 
0316     /* TBD: allow for disabled resources */
0317 
0318     buf = skip_spaces(buf);
0319     if (start) {
0320         *start = simple_strtoull(buf, &buf, 0);
0321         if (end) {
0322             buf = skip_spaces(buf);
0323             if (*buf == '-') {
0324                 buf = skip_spaces(buf + 1);
0325                 *end = simple_strtoull(buf, &buf, 0);
0326             } else
0327                 *end = *start;
0328         }
0329     }
0330 
0331     /* TBD: allow for additional flags, e.g., IORESOURCE_WINDOW */
0332 
0333     return buf;
0334 }
0335 
0336 static ssize_t resources_store(struct device *dmdev,
0337                    struct device_attribute *attr, const char *ubuf,
0338                    size_t count)
0339 {
0340     struct pnp_dev *dev = to_pnp_dev(dmdev);
0341     char *buf = (void *)ubuf;
0342     int retval = 0;
0343 
0344     if (dev->status & PNP_ATTACHED) {
0345         retval = -EBUSY;
0346         dev_info(&dev->dev, "in use; can't configure\n");
0347         goto done;
0348     }
0349 
0350     buf = skip_spaces(buf);
0351     if (!strncasecmp(buf, "disable", 7)) {
0352         retval = pnp_disable_dev(dev);
0353         goto done;
0354     }
0355     if (!strncasecmp(buf, "activate", 8)) {
0356         retval = pnp_activate_dev(dev);
0357         goto done;
0358     }
0359     if (!strncasecmp(buf, "fill", 4)) {
0360         if (dev->active)
0361             goto done;
0362         retval = pnp_auto_config_dev(dev);
0363         goto done;
0364     }
0365     if (!strncasecmp(buf, "auto", 4)) {
0366         if (dev->active)
0367             goto done;
0368         pnp_init_resources(dev);
0369         retval = pnp_auto_config_dev(dev);
0370         goto done;
0371     }
0372     if (!strncasecmp(buf, "clear", 5)) {
0373         if (dev->active)
0374             goto done;
0375         pnp_init_resources(dev);
0376         goto done;
0377     }
0378     if (!strncasecmp(buf, "get", 3)) {
0379         mutex_lock(&pnp_res_mutex);
0380         if (pnp_can_read(dev))
0381             dev->protocol->get(dev);
0382         mutex_unlock(&pnp_res_mutex);
0383         goto done;
0384     }
0385     if (!strncasecmp(buf, "set", 3)) {
0386         resource_size_t start;
0387         resource_size_t end;
0388         unsigned long flags;
0389 
0390         if (dev->active)
0391             goto done;
0392         buf += 3;
0393         pnp_init_resources(dev);
0394         mutex_lock(&pnp_res_mutex);
0395         while (1) {
0396             buf = skip_spaces(buf);
0397             if (!strncasecmp(buf, "io", 2)) {
0398                 buf = pnp_get_resource_value(buf + 2,
0399                                  IORESOURCE_IO,
0400                                  &start, &end,
0401                                  &flags);
0402                 pnp_add_io_resource(dev, start, end, flags);
0403             } else if (!strncasecmp(buf, "mem", 3)) {
0404                 buf = pnp_get_resource_value(buf + 3,
0405                                  IORESOURCE_MEM,
0406                                  &start, &end,
0407                                  &flags);
0408                 pnp_add_mem_resource(dev, start, end, flags);
0409             } else if (!strncasecmp(buf, "irq", 3)) {
0410                 buf = pnp_get_resource_value(buf + 3,
0411                                  IORESOURCE_IRQ,
0412                                  &start, NULL,
0413                                  &flags);
0414                 pnp_add_irq_resource(dev, start, flags);
0415             } else if (!strncasecmp(buf, "dma", 3)) {
0416                 buf = pnp_get_resource_value(buf + 3,
0417                                  IORESOURCE_DMA,
0418                                  &start, NULL,
0419                                  &flags);
0420                 pnp_add_dma_resource(dev, start, flags);
0421             } else if (!strncasecmp(buf, "bus", 3)) {
0422                 buf = pnp_get_resource_value(buf + 3,
0423                                  IORESOURCE_BUS,
0424                                  &start, &end,
0425                                  NULL);
0426                 pnp_add_bus_resource(dev, start, end);
0427             } else
0428                 break;
0429         }
0430         mutex_unlock(&pnp_res_mutex);
0431         goto done;
0432     }
0433 
0434 done:
0435     if (retval < 0)
0436         return retval;
0437     return count;
0438 }
0439 static DEVICE_ATTR_RW(resources);
0440 
0441 static ssize_t id_show(struct device *dmdev, struct device_attribute *attr,
0442                char *buf)
0443 {
0444     char *str = buf;
0445     struct pnp_dev *dev = to_pnp_dev(dmdev);
0446     struct pnp_id *pos = dev->id;
0447 
0448     while (pos) {
0449         str += sprintf(str, "%s\n", pos->id);
0450         pos = pos->next;
0451     }
0452     return (str - buf);
0453 }
0454 static DEVICE_ATTR_RO(id);
0455 
0456 static struct attribute *pnp_dev_attrs[] = {
0457     &dev_attr_resources.attr,
0458     &dev_attr_options.attr,
0459     &dev_attr_id.attr,
0460     NULL,
0461 };
0462 
0463 static const struct attribute_group pnp_dev_group = {
0464     .attrs = pnp_dev_attrs,
0465 };
0466 
0467 const struct attribute_group *pnp_dev_groups[] = {
0468     &pnp_dev_group,
0469     NULL,
0470 };