DM: virtio-gpio: implementation of gpio opearations

Implement gpio set/get value, direction input/output and set config operations.

Tracked-On: #2512
Signed-off-by: Yuan Liu <yuan1.liu@intel.com>
Acked-by: Yu Wang <yu1.wang@intel.com>
This commit is contained in:
Yuan Liu 2019-02-01 13:47:09 +08:00 committed by wenlingz
parent 77e17b5dff
commit 6b0643b535

View File

@ -102,6 +102,64 @@ static FILE *dbg_file;
/* make virtio gpio mediator a singleton mode */
static bool virtio_gpio_is_active;
/* Uses the same packed config format as generic pinconf. */
#define PIN_CONF_UNPACKED(p) ((unsigned long) p & 0xffUL)
enum pin_config_param {
PIN_CONFIG_BIAS_BUS_HOLD,
PIN_CONFIG_BIAS_DISABLE,
PIN_CONFIG_BIAS_HIGH_IMPEDANCE,
PIN_CONFIG_BIAS_PULL_DOWN,
PIN_CONFIG_BIAS_PULL_PIN_DEFAULT,
PIN_CONFIG_BIAS_PULL_UP,
PIN_CONFIG_DRIVE_OPEN_DRAIN,
PIN_CONFIG_DRIVE_OPEN_SOURCE,
PIN_CONFIG_DRIVE_PUSH_PULL,
PIN_CONFIG_DRIVE_STRENGTH,
PIN_CONFIG_INPUT_DEBOUNCE,
PIN_CONFIG_INPUT_ENABLE,
PIN_CONFIG_INPUT_SCHMITT,
PIN_CONFIG_INPUT_SCHMITT_ENABLE,
PIN_CONFIG_LOW_POWER_MODE,
PIN_CONFIG_OUTPUT_ENABLE,
PIN_CONFIG_OUTPUT,
PIN_CONFIG_POWER_SOURCE,
PIN_CONFIG_SLEEP_HARDWARE_STATE,
PIN_CONFIG_SLEW_RATE,
PIN_CONFIG_END = 0x7F,
PIN_CONFIG_MAX = 0xFF,
};
enum virtio_gpio_request_command {
GPIO_REQ_SET_VALUE = 0,
GPIO_REQ_GET_VALUE = 1,
GPIO_REQ_INPUT_DIRECTION = 2,
GPIO_REQ_OUTPUT_DIRECTION = 3,
GPIO_REQ_GET_DIRECTION = 4,
GPIO_REQ_SET_CONFIG = 5,
GPIO_REQ_MAX
};
struct virtio_gpio_request {
uint8_t cmd;
uint8_t offset;
uint64_t data;
} __attribute__((packed));
struct virtio_gpio_response {
int8_t err;
uint8_t data;
} __attribute__((packed));
struct virtio_gpio_info {
struct virtio_gpio_request req;
struct virtio_gpio_response rsp;
} __attribute__((packed));
struct virtio_gpio_data {
char name[32];
} __attribute__((packed));
struct virtio_gpio_config {
uint16_t base; /* base number */
uint16_t ngpio; /* number of gpios */
@ -167,6 +225,34 @@ native_gpio_update_line_info(struct gpio_line *line)
strncpy(line->name, info.name, sizeof(line->name) - 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 void
native_gpio_close_line(struct gpio_line *line)
{
if (line->fd > 0) {
close(line->fd);
line->fd = -1;
}
}
static int
native_gpio_open_line(struct gpio_line *line, unsigned int flags,
unsigned int value)
@ -179,6 +265,14 @@ native_gpio_open_line(struct gpio_line *line, unsigned int flags,
req.lines = 1;
strncpy(req.consumer_label, "acrn_dm", sizeof(req.consumer_label) - 1);
if (flags) {
/*
* before setting a new flag, it needs to try to close the fd
* that has been opened first, otherwise it will not be ioctl
* successfully.
*/
native_gpio_close_line(line);
req.flags = flags;
if (flags & GPIOHANDLE_REQUEST_OUTPUT)
req.default_values[0] = value;
@ -191,9 +285,162 @@ native_gpio_open_line(struct gpio_line *line, unsigned int flags,
}
line->fd = req.fd;
return rc;
}
static int
gpio_set_value(struct virtio_gpio *gpio, unsigned int offset,
unsigned int value)
{
struct gpio_line *line;
struct gpiohandle_data data;
int rc;
line = gpio->vlines[offset];
if (line->busy || line->fd < 0) {
DPRINTF("failed to set gpio%d value, busy:%d, fd:%d\n",
offset, line->busy, line->fd);
return -1;
}
memset(&data, 0, sizeof(data));
data.values[0] = value;
rc = ioctl(line->fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
if (rc < 0) {
DPRINTF("ioctl GPIOHANDLE_SET_LINE_VALUES_IOCTL error %s\n",
strerror(errno));
return -1;
}
return 0;
}
static int
gpio_get_value(struct virtio_gpio *gpio, unsigned int offset)
{
struct gpio_line *line;
struct gpiohandle_data data;
int rc;
line = gpio->vlines[offset];
if (line->busy || line->fd < 0) {
DPRINTF("failed to get gpio%d value, busy:%d, fd:%d\n",
offset, line->busy, line->fd);
return -1;
}
memset(&data, 0, sizeof(data));
rc = ioctl(line->fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
if (rc < 0) {
DPRINTF("ioctl GPIOHANDLE_GET_LINE_VALUES_IOCTL error %s\n",
strerror(errno));
return -1;
}
return data.values[0];
}
static int
gpio_set_direction_input(struct virtio_gpio *gpio, unsigned int offset)
{
struct gpio_line *line;
line = gpio->vlines[offset];
return native_gpio_open_line(line, GPIOHANDLE_REQUEST_INPUT, 0);
}
static int
gpio_set_direction_output(struct virtio_gpio *gpio, unsigned int offset,
int value)
{
struct gpio_line *line;
line = gpio->vlines[offset];
return native_gpio_open_line(line, GPIOHANDLE_REQUEST_OUTPUT, value);
}
static int
gpio_get_direction(struct virtio_gpio *gpio, unsigned int offset)
{
struct gpio_line *line;
line = gpio->vlines[offset];
native_gpio_update_line_info(line);
return line->dir;
}
static int
gpio_set_config(struct virtio_gpio *gpio, unsigned int offset,
unsigned long config)
{
struct gpio_line *line;
line = gpio->vlines[offset];
/*
* gpio userspace API can only provide GPIOLINE_FLAG_OPEN_DRAIN
* and GPIOLINE_FLAG_OPEN_SOURCE settings currenlty, other
* settings will return an success.
*/
config = PIN_CONF_UNPACKED(config);
switch (config) {
case PIN_CONFIG_DRIVE_OPEN_DRAIN:
config = GPIOLINE_FLAG_OPEN_DRAIN;
break;
case PIN_CONFIG_DRIVE_OPEN_SOURCE:
config = GPIOLINE_FLAG_OPEN_SOURCE;
break;
default:
return 0;
}
return native_gpio_open_line(line, config, 0);
}
static void
gpio_request_handler(struct virtio_gpio *gpio, struct virtio_gpio_request *req,
struct virtio_gpio_response *rsp)
{
int rc;
if (req->cmd >= GPIO_REQ_MAX || req->offset >= gpio->nvline) {
DPRINTF("discards the gpio request, command:%u, offset:%u\n",
req->cmd, req->offset);
rsp->err = -1;
return;
}
switch (req->cmd) {
case GPIO_REQ_SET_VALUE:
rc = gpio_set_value(gpio, req->offset, req->data);
break;
case GPIO_REQ_GET_VALUE:
rc = gpio_get_value(gpio, req->offset);
if (rc >= 0)
rsp->data = rc;
break;
case GPIO_REQ_OUTPUT_DIRECTION:
rc = gpio_set_direction_output(gpio, req->offset,
req->data);
break;
case GPIO_REQ_INPUT_DIRECTION:
rc = gpio_set_direction_input(gpio, req->offset);
break;
case GPIO_REQ_GET_DIRECTION:
rc = gpio_get_direction(gpio, req->offset);
if (rc >= 0)
rsp->data = rc;
break;
case GPIO_REQ_SET_CONFIG:
rc = gpio_set_config(gpio, req->offset, req->data);
break;
default:
DPRINTF("invalid gpio request command:%d\n", req->cmd);
rc = -1;
break;
}
rsp->err = rc < 0 ? -1 : 0;
}
static void virtio_gpio_reset(void *vdev)
{
struct virtio_gpio *gpio;
@ -229,9 +476,46 @@ static struct virtio_ops virtio_gpio_ops = {
};
static void
virtio_gpio_proc(struct virtio_gpio *gpio, struct iovec *iov, uint16_t flag)
virtio_gpio_proc(struct virtio_gpio *gpio, struct iovec *iov, int n)
{
/* Implemented in the subsequent patch */
struct virtio_gpio_data *data;
struct virtio_gpio_request *req;
struct virtio_gpio_response *rsp;
struct gpio_line *line;
int i, len;
if (n == 1) { /* provide gpio names for front-end driver */
data = iov[0].iov_base;
len = iov[0].iov_len;
assert(len == gpio->nvline * sizeof(*data));
for (i = 0; i < gpio->nvline; i++) {
line = gpio->vlines[i];
/*
* if the user provides the name of gpios in the
* command line paremeter, then provide it to UOS,
* otherwise provide the physical name of gpio to UOS.
*/
if (strlen(line->vname))
strncpy(data[i].name, line->vname,
sizeof(data[0].name) - 1);
else if (strlen(line->name))
strncpy(data[i].name, line->name,
sizeof(data[0].name) - 1);
}
} else if (n == 2) { /* handle gpio operations requests */
req = iov[0].iov_base;
len = iov[0].iov_len;
assert(len == sizeof(*req));
rsp = iov[1].iov_base;
len = iov[1].iov_len;
assert(len == sizeof(*rsp));
gpio_request_handler(gpio, req, rsp);
} else
DPRINTF("virtio gpio: number of buffer error %d\n", n);
}
static void
@ -319,25 +603,6 @@ fail:
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)
{