From 77e17b5dff3d8931fb54469bbbb4574ba5d20f4a Mon Sep 17 00:00:00 2001 From: Yuan Liu Date: Fri, 1 Feb 2019 11:27:36 +0800 Subject: [PATCH] DM: virtio-gpio: gpio initialization. Add gpio initializaiton and gpio usage implementation gpio usage: -s ,virtio-gpio, <@chip_name{offset|name[=vname]:offset|name[=vname]:...} [@chip_name{offset|name[=vname]:offset|name[=vname]:...}] [@chip_name{offset|name[=vname]:offset|name[=vname]:...}] ...> Tracked-On: #2512 Signed-off-by: Yuan Liu Acked-by: Yu Wang --- devicemodel/hw/pci/virtio/virtio_gpio.c | 315 +++++++++++++++++++++++- 1 file changed, 314 insertions(+), 1 deletion(-) diff --git a/devicemodel/hw/pci/virtio/virtio_gpio.c b/devicemodel/hw/pci/virtio/virtio_gpio.c index a012914d1..ec0ab8359 100644 --- a/devicemodel/hw/pci/virtio/virtio_gpio.c +++ b/devicemodel/hw/pci/virtio/virtio_gpio.c @@ -107,13 +107,93 @@ struct virtio_gpio_config { uint16_t ngpio; /* number of gpios */ } __attribute__((packed)); +struct gpio_line { + char name[32]; /* native gpio name */ + char vname[32]; /* virtual gpio name */ + int offset; /* offset in real chip */ + int voffset; /* offset in virtual chip */ + int fd; /* native gpio line fd */ + int dir; /* gpio direction */ + bool busy; /* gpio line request by kernel */ + struct native_gpio_chip *chip; /* parent gpio chip */ +}; + +struct native_gpio_chip { + char name[32]; /* gpio chip name */ + char label[32]; /* gpio chip label name */ + char dev_name[32]; /* device node name */ + int fd; /* native gpio chip fd */ + uint32_t ngpio; /* gpio line numbers */ + struct gpio_line *lines; /* gpio lines in the chip */ +}; + struct virtio_gpio { pthread_mutex_t mtx; struct virtio_base base; struct virtio_vq_info queues[VIRTIO_GPIO_MAXQ]; + struct native_gpio_chip chips[VIRTIO_GPIO_MAX_CHIPS]; + uint32_t nchip; + struct gpio_line *vlines[VIRTIO_GPIO_MAX_VLINES]; + uint32_t nvline; struct virtio_gpio_config config; }; +static void +native_gpio_update_line_info(struct gpio_line *line) +{ + struct gpioline_info info; + int rc; + + memset(&info, 0, sizeof(info)); + info.line_offset = line->offset; + rc = ioctl(line->chip->fd, GPIO_GET_LINEINFO_IOCTL, &info); + if (rc) { + DPRINTF("ioctl GPIO_GET_LINEINFO_IOCTL error %s\n", + strerror(errno)); + return; + } + + line->busy = info.flags & GPIOLINE_FLAG_KERNEL; + + /* + * if it is already used by virtio gpio model, + * it is not set to busy state + */ + if (line->fd > 0) + line->busy = false; + + /* 0 means output, 1 means input */ + line->dir = info.flags & GPIOLINE_FLAG_IS_OUT ? 0 : 1; + strncpy(line->name, info.name, sizeof(line->name) - 1); +} + +static int +native_gpio_open_line(struct gpio_line *line, unsigned int flags, + unsigned int value) +{ + struct gpiohandle_request req; + int rc; + + memset(&req, 0, sizeof(req)); + req.lineoffsets[0] = line->offset; + req.lines = 1; + strncpy(req.consumer_label, "acrn_dm", sizeof(req.consumer_label) - 1); + if (flags) { + req.flags = flags; + if (flags & GPIOHANDLE_REQUEST_OUTPUT) + req.default_values[0] = value; + } + rc = ioctl(line->chip->fd, GPIO_GET_LINEHANDLE_IOCTL, &req); + if (rc < 0) { + DPRINTF("ioctl GPIO_GET_LINEHANDLE_IOCTL error %s\n", + strerror(errno)); + return -1; + } + + line->fd = req.fd; + return 0; +} + static void virtio_gpio_reset(void *vdev) { struct virtio_gpio *gpio; @@ -176,12 +256,229 @@ virtio_gpio_notify(void *vdev, struct virtio_vq_info *vq) } } +static int +native_gpio_open_chip(struct native_gpio_chip *chip, const char *name) +{ + struct gpiochip_info info; + struct gpio_line *line; + char path[64] = {0}; + int fd, rc, i; + + snprintf(path, sizeof(path), "/dev/%s", name); + fd = open(path, O_RDWR); + if (fd < 0) { + DPRINTF("Can't open gpio device: %s, error %s\n", + path, strerror(errno)); + return -1; + } + + memset(&info, 0, sizeof(info)); + rc = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info); + if (rc < 0) { + DPRINTF("Can't ioctl gpio device: %s, error %s\n", + path, strerror(errno)); + goto fail; + } + + chip->lines = calloc(1, info.lines * sizeof(*chip->lines)); + if (!chip->lines) { + DPRINTF("Alloc chip lines error, %s:%d, error %s\n", + path, chip->ngpio, strerror(errno)); + goto fail; + } + chip->fd = fd; + chip->ngpio = info.lines; + strncpy(chip->name, info.name, sizeof(chip->name) - 1); + strncpy(chip->label, info.label, sizeof(chip->label) - 1); + strncpy(chip->dev_name, name, sizeof(chip->dev_name) - 1); + + /* initialize all lines of the chip */ + for (i = 0; i < chip->ngpio; i++) { + line = &chip->lines[i]; + line->offset = i; + line->chip = chip; + + /* + * The line's fd and voffset will be initialized + * when virtual gpio line connects to the real line. + */ + line->fd = -1; + line->voffset = -1; + + /* Set line state and name via ioctl*/ + native_gpio_update_line_info(line); + } + + return 0; + +fail: + if (fd > 0) + close(fd); + chip->fd = -1; + chip->ngpio = 0; + return -1; +} + +static void +native_gpio_close_chip(struct native_gpio_chip *chip) +{ + if (chip) { + memset(chip->name, 0, sizeof(chip->name)); + memset(chip->label, 0, sizeof(chip->label)); + memset(chip->dev_name, 0, sizeof(chip->dev_name)); + if (chip->fd > 0) { + close(chip->fd); + chip->fd = -1; + } + if (chip->lines) { + free(chip->lines); + chip->lines = NULL; + } + chip->ngpio = 0; + } +} + +static int +native_gpio_get_offset(struct native_gpio_chip *chip, char *name) +{ + int rc; + int i; + + /* try to find a gpio index by offset or name */ + if (isalpha(name[0])) { + for (i = 0; i < chip->ngpio; i++) { + if (!strcmp(chip->lines[i].name, name)) + return i; + } + } else if (isdigit(name[0])) { + rc = dm_strtoi(name, NULL, 10, &i); + if (rc == 0 && i < chip->ngpio) + return i; + } + return -1; +} + +static struct gpio_line * +native_gpio_find_line(struct native_gpio_chip *chip, const char *name) +{ + int offset; + char *b, *o, *c; + struct gpio_line *line = NULL; + + b = o = strdup(name); + c = strsep(&o, "="); + + /* find the line's offset in the chip by name or number */ + offset = native_gpio_get_offset(chip, c); + if (offset < 0) { + DPRINTF("the %s line has not been found in %s chip\n", + c, chip->dev_name); + goto out; + } + + line = &chip->lines[offset]; + if (line->busy || native_gpio_open_line(line, 0, 0) < 0) { + line = NULL; + goto out; + } + + /* If the user sets the name of the GPIO, copy it to vname */ + if (o) + strncpy(line->vname, o, sizeof(line->vname) - 1); + +out: + free(b); + return line; +} + + +static int +native_gpio_init(struct virtio_gpio *gpio, char *opts) +{ + struct gpio_line *line; + char *cstr, *lstr, *tmp, *b, *o; + int rc; + int cn = 0; + int ln = 0; + + /* + * -s ,virtio-gpio, + * format + * <@chip_name0{offset|name[=vname]:offset|name[=vname]:...} + * [@chip_name1{offset|name[=vname]:offset|name[=vname]:...}] + * [@chip_name2{offset|name[=vname]:offset|name[=vname]:...}] + * ...> + */ + + b = o = strdup(opts); + while ((tmp = strsep(&o, "@")) != NULL) { + + /* discard subsequent chips */ + if (cn >= VIRTIO_GPIO_MAX_CHIPS || + ln >= VIRTIO_GPIO_MAX_VLINES) { + DPRINTF("gpio chips or lines reach max, cn %d, ln %d\n", + cn, ln); + break; + } + + /* ignore the null string */ + if (tmp[0] == '\0') + continue; + + /* + * parse gpio chip name + * if there is no gpiochip information, like "@{...}" + * ignore all of the lines. + */ + cstr = strsep(&tmp, "{"); + if (!tmp || !cstr || cstr[0] == '\0') + continue; + + /* get chip information with its name */ + rc = native_gpio_open_chip(&gpio->chips[cn], cstr); + if (rc < 0) + continue; + + /* parse all gpio lines in one chip */ + cstr = strsep(&tmp, "}"); + while ((lstr = strsep(&cstr, ":")) != NULL) { + + /* safety check, to avoid "@gpiochip0{::0:1...}" */ + if (lstr[0] == '\0') + continue; + + /* discard subsequent lines */ + if (ln >= VIRTIO_GPIO_MAX_VLINES) { + DPRINTF("Virtual gpio lines reach max:%d\n", + ln); + break; + } + + /* + * If the line provided by gpio command line is found + * assign one virtual gpio offset for it. + */ + line = native_gpio_find_line(&gpio->chips[cn], lstr); + if (line) { + gpio->vlines[ln] = line; + line->voffset = ln; + ln++; + } + } + cn++; + } + gpio->nchip = cn; + gpio->nvline = ln; + free(b); + return ln == 0 ? -1 : 0; +} + static int virtio_gpio_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) { struct virtio_gpio *gpio; pthread_mutexattr_t attr; - int rc; + int rc, i; /* Just support one bdf */ if (virtio_gpio_is_active) @@ -202,6 +499,12 @@ virtio_gpio_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) goto init_fail; } + rc = native_gpio_init(gpio, opts); + if (rc) { + DPRINTF("%s", "virtio gpio: failed to initialize gpio\n"); + goto gpio_fail; + } + /* init mutex attribute properly to avoid deadlock */ rc = pthread_mutexattr_init(&attr); if (rc) { @@ -226,6 +529,9 @@ virtio_gpio_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts) /* gpio base for frontend gpio chip */ gpio->config.base = 0; + /* gpio numbers for frontend gpio chip */ + gpio->config.ngpio = gpio->nvline; + gpio->base.device_caps = VIRTIO_GPIO_S_HOSTCAPS; gpio->base.mtx = &gpio->mtx; gpio->queues[0].qsize = 64; @@ -254,6 +560,10 @@ fail: pthread_mutex_destroy(&gpio->mtx); mtx_fail: + for (i = 0; i < gpio->nchip; i++) + native_gpio_close_chip(&gpio->chips[i]); + +gpio_fail: free(gpio); dev->arg = NULL; @@ -266,12 +576,15 @@ static void virtio_gpio_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts) { struct virtio_gpio *gpio; + int i; DPRINTF("%s", "virtio gpio: pci_gpio_deinit\r\n"); virtio_gpio_is_active = false; gpio = (struct virtio_gpio *)dev->arg; if (gpio) { pthread_mutex_destroy(&gpio->mtx); + for (i = 0; i < gpio->nchip; i++) + native_gpio_close_chip(&gpio->chips[i]); free(gpio); dev->arg = NULL; }