Skip to content

Commit 77e17b5

Browse files
yliu80wenlingz
authored andcommitted
DM: virtio-gpio: gpio initialization.
Add gpio initializaiton and gpio usage implementation gpio usage: -s <slot>,virtio-gpio,<gpio resources> <gpio resources format> <@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 <yuan1.liu@intel.com> Acked-by: Yu Wang <yu1.wang@intel.com>
1 parent 5702931 commit 77e17b5

File tree

1 file changed

+314
-1
lines changed

1 file changed

+314
-1
lines changed

devicemodel/hw/pci/virtio/virtio_gpio.c

Lines changed: 314 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,93 @@ struct virtio_gpio_config {
107107
uint16_t ngpio; /* number of gpios */
108108
} __attribute__((packed));
109109

110+
struct gpio_line {
111+
char name[32]; /* native gpio name */
112+
char vname[32]; /* virtual gpio name */
113+
int offset; /* offset in real chip */
114+
int voffset; /* offset in virtual chip */
115+
int fd; /* native gpio line fd */
116+
int dir; /* gpio direction */
117+
bool busy; /* gpio line request by kernel */
118+
struct native_gpio_chip *chip; /* parent gpio chip */
119+
};
120+
121+
struct native_gpio_chip {
122+
char name[32]; /* gpio chip name */
123+
char label[32]; /* gpio chip label name */
124+
char dev_name[32]; /* device node name */
125+
int fd; /* native gpio chip fd */
126+
uint32_t ngpio; /* gpio line numbers */
127+
struct gpio_line *lines; /* gpio lines in the chip */
128+
};
129+
110130
struct virtio_gpio {
111131
pthread_mutex_t mtx;
112132
struct virtio_base base;
113133
struct virtio_vq_info queues[VIRTIO_GPIO_MAXQ];
134+
struct native_gpio_chip chips[VIRTIO_GPIO_MAX_CHIPS];
135+
uint32_t nchip;
136+
struct gpio_line *vlines[VIRTIO_GPIO_MAX_VLINES];
137+
uint32_t nvline;
114138
struct virtio_gpio_config config;
115139
};
116140

141+
static void
142+
native_gpio_update_line_info(struct gpio_line *line)
143+
{
144+
struct gpioline_info info;
145+
int rc;
146+
147+
memset(&info, 0, sizeof(info));
148+
info.line_offset = line->offset;
149+
rc = ioctl(line->chip->fd, GPIO_GET_LINEINFO_IOCTL, &info);
150+
if (rc) {
151+
DPRINTF("ioctl GPIO_GET_LINEINFO_IOCTL error %s\n",
152+
strerror(errno));
153+
return;
154+
}
155+
156+
line->busy = info.flags & GPIOLINE_FLAG_KERNEL;
157+
158+
/*
159+
* if it is already used by virtio gpio model,
160+
* it is not set to busy state
161+
*/
162+
if (line->fd > 0)
163+
line->busy = false;
164+
165+
/* 0 means output, 1 means input */
166+
line->dir = info.flags & GPIOLINE_FLAG_IS_OUT ? 0 : 1;
167+
strncpy(line->name, info.name, sizeof(line->name) - 1);
168+
}
169+
170+
static int
171+
native_gpio_open_line(struct gpio_line *line, unsigned int flags,
172+
unsigned int value)
173+
{
174+
struct gpiohandle_request req;
175+
int rc;
176+
177+
memset(&req, 0, sizeof(req));
178+
req.lineoffsets[0] = line->offset;
179+
req.lines = 1;
180+
strncpy(req.consumer_label, "acrn_dm", sizeof(req.consumer_label) - 1);
181+
if (flags) {
182+
req.flags = flags;
183+
if (flags & GPIOHANDLE_REQUEST_OUTPUT)
184+
req.default_values[0] = value;
185+
}
186+
rc = ioctl(line->chip->fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
187+
if (rc < 0) {
188+
DPRINTF("ioctl GPIO_GET_LINEHANDLE_IOCTL error %s\n",
189+
strerror(errno));
190+
return -1;
191+
}
192+
193+
line->fd = req.fd;
194+
return 0;
195+
}
196+
117197
static void virtio_gpio_reset(void *vdev)
118198
{
119199
struct virtio_gpio *gpio;
@@ -176,12 +256,229 @@ virtio_gpio_notify(void *vdev, struct virtio_vq_info *vq)
176256
}
177257
}
178258

259+
static int
260+
native_gpio_open_chip(struct native_gpio_chip *chip, const char *name)
261+
{
262+
struct gpiochip_info info;
263+
struct gpio_line *line;
264+
char path[64] = {0};
265+
int fd, rc, i;
266+
267+
snprintf(path, sizeof(path), "/dev/%s", name);
268+
fd = open(path, O_RDWR);
269+
if (fd < 0) {
270+
DPRINTF("Can't open gpio device: %s, error %s\n",
271+
path, strerror(errno));
272+
return -1;
273+
}
274+
275+
memset(&info, 0, sizeof(info));
276+
rc = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info);
277+
if (rc < 0) {
278+
DPRINTF("Can't ioctl gpio device: %s, error %s\n",
279+
path, strerror(errno));
280+
goto fail;
281+
}
282+
283+
chip->lines = calloc(1, info.lines * sizeof(*chip->lines));
284+
if (!chip->lines) {
285+
DPRINTF("Alloc chip lines error, %s:%d, error %s\n",
286+
path, chip->ngpio, strerror(errno));
287+
goto fail;
288+
}
289+
chip->fd = fd;
290+
chip->ngpio = info.lines;
291+
strncpy(chip->name, info.name, sizeof(chip->name) - 1);
292+
strncpy(chip->label, info.label, sizeof(chip->label) - 1);
293+
strncpy(chip->dev_name, name, sizeof(chip->dev_name) - 1);
294+
295+
/* initialize all lines of the chip */
296+
for (i = 0; i < chip->ngpio; i++) {
297+
line = &chip->lines[i];
298+
line->offset = i;
299+
line->chip = chip;
300+
301+
/*
302+
* The line's fd and voffset will be initialized
303+
* when virtual gpio line connects to the real line.
304+
*/
305+
line->fd = -1;
306+
line->voffset = -1;
307+
308+
/* Set line state and name via ioctl*/
309+
native_gpio_update_line_info(line);
310+
}
311+
312+
return 0;
313+
314+
fail:
315+
if (fd > 0)
316+
close(fd);
317+
chip->fd = -1;
318+
chip->ngpio = 0;
319+
return -1;
320+
}
321+
322+
static void
323+
native_gpio_close_chip(struct native_gpio_chip *chip)
324+
{
325+
if (chip) {
326+
memset(chip->name, 0, sizeof(chip->name));
327+
memset(chip->label, 0, sizeof(chip->label));
328+
memset(chip->dev_name, 0, sizeof(chip->dev_name));
329+
if (chip->fd > 0) {
330+
close(chip->fd);
331+
chip->fd = -1;
332+
}
333+
if (chip->lines) {
334+
free(chip->lines);
335+
chip->lines = NULL;
336+
}
337+
chip->ngpio = 0;
338+
}
339+
}
340+
341+
static int
342+
native_gpio_get_offset(struct native_gpio_chip *chip, char *name)
343+
{
344+
int rc;
345+
int i;
346+
347+
/* try to find a gpio index by offset or name */
348+
if (isalpha(name[0])) {
349+
for (i = 0; i < chip->ngpio; i++) {
350+
if (!strcmp(chip->lines[i].name, name))
351+
return i;
352+
}
353+
} else if (isdigit(name[0])) {
354+
rc = dm_strtoi(name, NULL, 10, &i);
355+
if (rc == 0 && i < chip->ngpio)
356+
return i;
357+
}
358+
return -1;
359+
}
360+
361+
static struct gpio_line *
362+
native_gpio_find_line(struct native_gpio_chip *chip, const char *name)
363+
{
364+
int offset;
365+
char *b, *o, *c;
366+
struct gpio_line *line = NULL;
367+
368+
b = o = strdup(name);
369+
c = strsep(&o, "=");
370+
371+
/* find the line's offset in the chip by name or number */
372+
offset = native_gpio_get_offset(chip, c);
373+
if (offset < 0) {
374+
DPRINTF("the %s line has not been found in %s chip\n",
375+
c, chip->dev_name);
376+
goto out;
377+
}
378+
379+
line = &chip->lines[offset];
380+
if (line->busy || native_gpio_open_line(line, 0, 0) < 0) {
381+
line = NULL;
382+
goto out;
383+
}
384+
385+
/* If the user sets the name of the GPIO, copy it to vname */
386+
if (o)
387+
strncpy(line->vname, o, sizeof(line->vname) - 1);
388+
389+
out:
390+
free(b);
391+
return line;
392+
}
393+
394+
395+
static int
396+
native_gpio_init(struct virtio_gpio *gpio, char *opts)
397+
{
398+
struct gpio_line *line;
399+
char *cstr, *lstr, *tmp, *b, *o;
400+
int rc;
401+
int cn = 0;
402+
int ln = 0;
403+
404+
/*
405+
* -s <slot>,virtio-gpio,<gpio resources>
406+
* <gpio resources> format
407+
* <@chip_name0{offset|name[=vname]:offset|name[=vname]:...}
408+
* [@chip_name1{offset|name[=vname]:offset|name[=vname]:...}]
409+
* [@chip_name2{offset|name[=vname]:offset|name[=vname]:...}]
410+
* ...>
411+
*/
412+
413+
b = o = strdup(opts);
414+
while ((tmp = strsep(&o, "@")) != NULL) {
415+
416+
/* discard subsequent chips */
417+
if (cn >= VIRTIO_GPIO_MAX_CHIPS ||
418+
ln >= VIRTIO_GPIO_MAX_VLINES) {
419+
DPRINTF("gpio chips or lines reach max, cn %d, ln %d\n",
420+
cn, ln);
421+
break;
422+
}
423+
424+
/* ignore the null string */
425+
if (tmp[0] == '\0')
426+
continue;
427+
428+
/*
429+
* parse gpio chip name
430+
* if there is no gpiochip information, like "@{...}"
431+
* ignore all of the lines.
432+
*/
433+
cstr = strsep(&tmp, "{");
434+
if (!tmp || !cstr || cstr[0] == '\0')
435+
continue;
436+
437+
/* get chip information with its name */
438+
rc = native_gpio_open_chip(&gpio->chips[cn], cstr);
439+
if (rc < 0)
440+
continue;
441+
442+
/* parse all gpio lines in one chip */
443+
cstr = strsep(&tmp, "}");
444+
while ((lstr = strsep(&cstr, ":")) != NULL) {
445+
446+
/* safety check, to avoid "@gpiochip0{::0:1...}" */
447+
if (lstr[0] == '\0')
448+
continue;
449+
450+
/* discard subsequent lines */
451+
if (ln >= VIRTIO_GPIO_MAX_VLINES) {
452+
DPRINTF("Virtual gpio lines reach max:%d\n",
453+
ln);
454+
break;
455+
}
456+
457+
/*
458+
* If the line provided by gpio command line is found
459+
* assign one virtual gpio offset for it.
460+
*/
461+
line = native_gpio_find_line(&gpio->chips[cn], lstr);
462+
if (line) {
463+
gpio->vlines[ln] = line;
464+
line->voffset = ln;
465+
ln++;
466+
}
467+
}
468+
cn++;
469+
}
470+
gpio->nchip = cn;
471+
gpio->nvline = ln;
472+
free(b);
473+
return ln == 0 ? -1 : 0;
474+
}
475+
179476
static int
180477
virtio_gpio_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
181478
{
182479
struct virtio_gpio *gpio;
183480
pthread_mutexattr_t attr;
184-
int rc;
481+
int rc, i;
185482

186483
/* Just support one bdf */
187484
if (virtio_gpio_is_active)
@@ -202,6 +499,12 @@ virtio_gpio_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
202499
goto init_fail;
203500
}
204501

502+
rc = native_gpio_init(gpio, opts);
503+
if (rc) {
504+
DPRINTF("%s", "virtio gpio: failed to initialize gpio\n");
505+
goto gpio_fail;
506+
}
507+
205508
/* init mutex attribute properly to avoid deadlock */
206509
rc = pthread_mutexattr_init(&attr);
207510
if (rc) {
@@ -226,6 +529,9 @@ virtio_gpio_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
226529
/* gpio base for frontend gpio chip */
227530
gpio->config.base = 0;
228531

532+
/* gpio numbers for frontend gpio chip */
533+
gpio->config.ngpio = gpio->nvline;
534+
229535
gpio->base.device_caps = VIRTIO_GPIO_S_HOSTCAPS;
230536
gpio->base.mtx = &gpio->mtx;
231537
gpio->queues[0].qsize = 64;
@@ -254,6 +560,10 @@ virtio_gpio_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
254560
pthread_mutex_destroy(&gpio->mtx);
255561

256562
mtx_fail:
563+
for (i = 0; i < gpio->nchip; i++)
564+
native_gpio_close_chip(&gpio->chips[i]);
565+
566+
gpio_fail:
257567
free(gpio);
258568
dev->arg = NULL;
259569

@@ -266,12 +576,15 @@ static void
266576
virtio_gpio_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
267577
{
268578
struct virtio_gpio *gpio;
579+
int i;
269580

270581
DPRINTF("%s", "virtio gpio: pci_gpio_deinit\r\n");
271582
virtio_gpio_is_active = false;
272583
gpio = (struct virtio_gpio *)dev->arg;
273584
if (gpio) {
274585
pthread_mutex_destroy(&gpio->mtx);
586+
for (i = 0; i < gpio->nchip; i++)
587+
native_gpio_close_chip(&gpio->chips[i]);
275588
free(gpio);
276589
dev->arg = NULL;
277590
}

0 commit comments

Comments
 (0)