diff --git a/Makefile.am b/Makefile.am index 8409a40441c4e0bba8114284cb6a4638e38e0091..935a422c66e3ce44fe17ccdcc7a88b2d4fa35e48 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,4 +7,5 @@ gbridge_SOURCES = main.c \ netlink.c \ debug.c \ greybus.c \ - svc.c \ No newline at end of file + svc.c \ + controller.c \ No newline at end of file diff --git a/controller.c b/controller.c new file mode 100644 index 0000000000000000000000000000000000000000..120184af62295ea56e128cfd2aa2b285c1b01fe9 --- /dev/null +++ b/controller.c @@ -0,0 +1,369 @@ +/* + * GBridge (Greybus Bridge) + * Copyright (c) 2016 Alexandre Bailon + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> + +#include <debug.h> +#include <gbridge.h> +#include <netlink.h> +#include <controller.h> + +static +TAILQ_HEAD(conn_head, connection) +connections = TAILQ_HEAD_INITIALIZER(connections); +static +TAILQ_HEAD(ctrl_head, controller) +controllers = TAILQ_HEAD_INITIALIZER(controllers); +static uint8_t g_intf_id = 0; +static pthread_mutex_t intf_alloc_lock; + +void cport_pack(struct gb_operation_msg_hdr *header, uint16_t cport_id) +{ + header->pad[0] = cport_id; +} + +uint16_t cport_unpack(struct gb_operation_msg_hdr *header) +{ + return (uint16_t) header->pad[0]; +} + +void cport_clear(struct gb_operation_msg_hdr *header) +{ + header->pad[0] = 0; +} + +static int _read(int fd, void *data, size_t len) +{ + int ret; + uint8_t *pdata = data; + + do { + ret = read(fd, pdata, len); + if (ret < 0) { + pr_err("Failed to read header\n"); + return ret; + } + pdata += ret; + len -= ret; + } + while (len); + + return 0; +} + +int read_gb_msg(int fd, void *data, size_t max_len) +{ + int ret; + uint8_t *pdata = data; + size_t len = sizeof(struct gb_operation_msg_hdr); + + ret = _read(fd, pdata, len); + if (ret < 0) + return ret; + + pdata += len; + pr_dump(data, 8); + len = gb_operation_msg_size(data); + if (len > max_len) + return -EMSGSIZE; + len -= sizeof(struct gb_operation_msg_hdr); + + ret = _read(fd, pdata, len); + if (ret < 0) + return ret; + + return gb_operation_msg_size(data); +} + +static struct connection *cport_id_to_connection(uint16_t cport_id) +{ + struct connection *conn; + + TAILQ_FOREACH(conn, &connections, node) { + if (conn->cport2_id == cport_id) + return conn; + } + + return NULL; +} + +static struct connection *hd_cport_id_to_connection(uint16_t cport_id) +{ + struct connection *conn; + + TAILQ_FOREACH(conn, &connections, node) { + if (conn->cport1_id == cport_id) + return conn; + } + + return NULL; +} + +static void *interface_recv(void *data) +{ + int ret; + uint16_t cport_id; + struct connection *conn; + struct interface *intf = data; + struct controller *ctrl = intf->ctrl; + uint8_t buffer[GB_NETLINK_MTU]; + + while (1) { + ret = ctrl->intf_read(intf, &cport_id, buffer, GB_NETLINK_MTU); + if (ret < 0) { + pr_err("Failed to read data: %d\n", ret); + continue; + } + + pr_dump(buffer, ret); + + conn = cport_id_to_connection(cport_id); + if (!conn) { + pr_err("Received data on invalid cport number\n"); + continue; + } + + ret = netlink_send(conn->cport1_id, buffer, ret); + if (ret < 0) { + pr_err("Failed to transmit data\n"); + } + } + + return NULL; +} + +static uint8_t intf_id_alloc(void) +{ + static uint8_t intf_id; + + pthread_mutex_lock(&intf_alloc_lock); + intf_id = ++g_intf_id; + pthread_mutex_unlock(&intf_alloc_lock); + + return intf_id; +} + +struct interface *interface_create(struct controller *ctrl, uint32_t vendor_id, + uint32_t product_id, uint64_t serial_id, + void *priv) +{ + int ret; + struct interface *intf; + + intf = malloc(sizeof(*intf)); + if (!intf) + return NULL; + + intf->ctrl = ctrl; + intf->priv = priv; + intf->id = intf_id_alloc(); + intf->vendor_id = vendor_id; + intf->product_id = product_id; + intf->serial_id = serial_id; + + if (ctrl->interface_create) + if (ctrl->interface_create(intf)) + goto err_free_intf; + + if (ctrl->intf_read) { + ret = pthread_create(&intf->thread, NULL, interface_recv, intf); + if (ret) + goto err_destroy_intf; + } + + TAILQ_INSERT_TAIL(&ctrl->interfaces, intf, node); + + return intf; + + err_destroy_intf: + if (ctrl->interface_destroy) + ctrl->interface_destroy(intf); + err_free_intf: + free(intf); + + return NULL; +} + +void interface_destroy(struct interface *intf) +{ + if (intf->ctrl->intf_read) { + pthread_cancel(intf->thread); + pthread_join(intf->thread, NULL); + } + + TAILQ_REMOVE(&intf->ctrl->interfaces, intf, node); + + if (intf->ctrl->interface_destroy) + intf->ctrl->interface_destroy(intf); + free(intf); +} + +void interfaces_destroy(struct controller *ctrl) +{ + struct interface *intf, *tmp; + + TAILQ_FOREACH_SAFE(intf, &ctrl->interfaces, node, tmp) { + interface_destroy(intf); + } +} + +int interface_hotplug(struct interface *intf) +{ + int ret; + ret = svc_send_intf_hotplug_event(intf->id, intf->vendor_id, + intf->product_id, intf->serial_id); + + return ret; +} + +int interface_hot_unplug(struct interface *intf) +{ + return 0; +} + +static struct interface *get_interface(uint8_t intf_id) +{ + struct controller *ctrl; + struct interface *intf; + + TAILQ_FOREACH(ctrl, &controllers, node) { + TAILQ_FOREACH(intf, &ctrl->interfaces, node) { + if (intf->id == intf_id) + return intf; + } + } + + return NULL; +} + +int +connection_create(uint8_t intf1_id, uint16_t cport1_id, + uint8_t intf2_id, uint16_t cport2_id) +{ + struct interface *intf; + struct connection *conn; + + intf = get_interface(intf2_id); + if (!intf) + return -EINVAL; + + conn = malloc(sizeof(*conn)); + if (!conn) + return -ENOMEM; + + conn->intf = intf; + conn->cport1_id = cport1_id; + conn->cport2_id = cport2_id; + TAILQ_INSERT_TAIL(&connections, conn, node); + + return 0; +} + +int +connection_destroy(uint8_t intf1_id, uint16_t cport1_id, + uint8_t intf2_id, uint16_t cport2_id) +{ + struct connection *conn; + + conn = hd_cport_id_to_connection(cport1_id); + if (!conn) { + return -EINVAL; + } + + TAILQ_REMOVE(&connections, conn, node); + free(conn); + + return 0; +} + +static void register_controllers(void) +{ + /* TODO Register controllers */ +} + +static void *controller_loop(void *data) +{ + struct controller *ctrl = data; + + ctrl->event_loop(ctrl); + + return NULL; +} + +static int controller_loop_init(struct controller *ctrl) +{ + if (!ctrl->event_loop) + return 0; + + return pthread_create(&ctrl->thread, NULL, controller_loop, ctrl); +} + +static void controller_loop_exit(struct controller *ctrl) +{ + if (!ctrl->event_loop) + return; + + pthread_cancel(ctrl->thread); + pthread_join(ctrl->thread, NULL); +} + +int controller_write(uint16_t cport_id, void *data, size_t len) +{ + struct connection *conn; + struct controller *ctrl; + + conn = hd_cport_id_to_connection(cport_id); + if (!conn) + return -EINVAL; + + pr_dump(data, len); + + ctrl = conn->intf->ctrl; + return ctrl->write(conn, data, len); +} + +void controllers_init(void) +{ + int ret; + struct controller *ctrl, *tmp; + + register_controllers(); + TAILQ_FOREACH_SAFE(ctrl, &controllers, node, tmp) { + ret = ctrl->init(ctrl); + if (ret) { + pr_err("Failed to init %s: %d\n", ctrl->name, ret); + TAILQ_REMOVE(&controllers, ctrl, node); + continue; + } + TAILQ_INIT(&ctrl->interfaces); + controller_loop_init(ctrl); + } +} + +void controllers_exit(void) +{ + struct controller *ctrl; + + TAILQ_FOREACH(ctrl, &controllers, node) { + controller_loop_exit(ctrl); + interfaces_destroy(ctrl); + ctrl->exit(ctrl); + } +} diff --git a/controller.h b/controller.h new file mode 100644 index 0000000000000000000000000000000000000000..a7b5b588c8ff065867eeae5c187e509015132361 --- /dev/null +++ b/controller.h @@ -0,0 +1,95 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + + * Author: Alexandre Bailon <abailon@baylibre.com> + * Copyright (c) 2016 Alexandre Bailon + */ + +#ifndef __CONTROLLER_H__ +#define __CONTROLLER_H__ + +#include <pthread.h> +#include <sys/queue.h> + +#include <gbridge.h> + +struct connection { + uint16_t cport1_id; + uint16_t cport2_id; + struct interface *intf; + TAILQ_ENTRY(connection) node; +}; + +struct interface { + uint32_t vendor_id; + uint32_t product_id; + uint64_t serial_id; + + void *priv; + + /* gb intf private data */ + uint8_t id; + struct controller *ctrl; + TAILQ_ENTRY(interface) node; + pthread_t thread; +}; + +struct controller { + const char *name; + + int (*init) (struct controller * ctrl); + void (*exit) (struct controller * ctrl); + int (*event_loop) (struct controller * ctrl); + + int (*interface_create) (struct interface * intf); + void (*interface_destroy) (struct interface * intf); + + int (*write) (struct connection * conn, void *data, size_t len); + int (*read) (struct connection * conn, void *data, size_t len); + int (*intf_read) (struct interface * intf, + uint16_t * cport_id, void *data, size_t len); + + void *priv; + + /* gb controller private data */ + pthread_t thread; + TAILQ_ENTRY(controller) node; + TAILQ_HEAD(head, interface) interfaces; +}; + +extern struct controller bluetooth_controller; + +void cport_pack(struct gb_operation_msg_hdr *header, uint16_t cport_id); +uint16_t cport_unpack(struct gb_operation_msg_hdr *header); +void cport_clear(struct gb_operation_msg_hdr *header); +int read_gb_msg(int fd, void *data, size_t len); + +struct interface *interface_create(struct controller *ctrl, + uint32_t vendor_id, uint32_t product_id, + uint64_t serial_id, void *priv); +int interface_hotplug(struct interface *intf); +int interface_hot_unplug(struct interface *intf); +void interface_destroy(struct interface *intf); +void interfaces_destroy(struct controller *ctrl); + +int connection_create(uint8_t intf1_id, uint16_t cport1_id, + uint8_t intf2_id, uint16_t cport2_id); +int connection_destroy(uint8_t intf1_id, uint16_t cport1_id, + uint8_t intf2_id, uint16_t cport2_id); + +int controller_write(uint16_t cport_id, void *data, size_t len); +void controllers_init(void); +void controllers_exit(void); + +#endif /* __CONTROLLER_H__ */ diff --git a/main.c b/main.c index e9759d302cfe2073477013f12c8d1b4303fd579b..d3c73408cd84672cbe26d0fb4ae7d55fba6de81e 100644 --- a/main.c +++ b/main.c @@ -19,6 +19,7 @@ #include <signal.h> #include <debug.h> +#include <controller.h> #include "gbridge.h" #include "netlink.h" @@ -54,7 +55,12 @@ int main(int argc, char *argv[]) goto err_netlink_exit; } + controllers_init(); + netlink_loop(); + + controllers_exit(); + netlink_exit(); return 0; diff --git a/netlink.c b/netlink.c index 782b41c43d6f51fc513114d8209c1f05aab5efb1..51ff4c3ca5870d463c27642473776d4e1273c54e 100644 --- a/netlink.c +++ b/netlink.c @@ -21,6 +21,7 @@ #include <debug.h> #include <gbridge.h> +#include <controller.h> #include <netlink/genl/mngt.h> #include <netlink/genl/ctrl.h> @@ -61,7 +62,7 @@ parse_gb_nl_msg(struct nl_cache_ops *unused, struct genl_cmd *cmd, hdr->type, ret); } } else { - /* TODO: transfer data to modules */ + ret = controller_write(hd_cport_id, hdr, gb_operation_msg_size(hdr)); } return 0; diff --git a/svc.c b/svc.c index a5a200b5c3e0be82cecb64002137e5f19fafddd9..6f448fc0210a7def351c891fd43dc059f8e1857b 100644 --- a/svc.c +++ b/svc.c @@ -22,6 +22,7 @@ #include <string.h> #include <gbridge.h> +#include <controller.h> /* TODO: Can we use other IDs ? */ #define ENDO_ID 0x4755 @@ -29,6 +30,36 @@ static int svc_send_hello_request(void); +static int svc_dme_peer_get_response(struct operation *op, uint16_t result_code, + uint32_t attr_value) +{ + struct gb_svc_dme_peer_get_response *resp; + size_t op_size = sizeof(*resp); + + if (greybus_alloc_response(op, op_size)) + return -ENOMEM; + + resp = operation_to_response(op); + resp->result_code = htole16(result_code); + resp->attr_value = htole32(attr_value); + + return 0; +} + +static int svc_dme_peer_set_response(struct operation *op, uint16_t result_code) +{ + struct gb_svc_dme_peer_set_response *resp; + size_t op_size = sizeof(*resp); + + if (greybus_alloc_response(op, op_size)) + return -ENOMEM; + + resp = operation_to_response(op); + resp->result_code = htole16(result_code); + + return 0; +} + static int svc_interface_v_sys_enable_response(struct operation *op, uint8_t result_code) { @@ -144,6 +175,50 @@ static int svc_interface_device_id_request(struct operation *op) return 0; } +static int svc_connection_create_request(struct operation *op) +{ + struct gb_svc_conn_create_request *req; + uint8_t intf1_id; + uint16_t cport1_id; + uint8_t intf2_id; + uint16_t cport2_id; + + req = operation_to_request(op); + intf1_id = req->intf1_id; + cport1_id = le16toh(req->cport1_id); + intf2_id = req->intf2_id; + cport2_id = le16toh(req->cport2_id); + + return connection_create(intf1_id, cport1_id, intf2_id, cport2_id); +} + +static int svc_connection_destroy_request(struct operation *op) +{ + struct gb_svc_conn_destroy_request *req; + uint8_t intf1_id; + uint16_t cport1_id; + uint8_t intf2_id; + uint16_t cport2_id; + + req = operation_to_request(op); + intf1_id = req->intf1_id; + cport1_id = le16toh(req->cport1_id); + intf2_id = req->intf2_id; + cport2_id = le16toh(req->cport2_id); + + return connection_destroy(intf1_id, cport1_id, intf2_id, cport2_id); +} + +static int svc_dme_peer_get_request(struct operation *op) +{ + return svc_dme_peer_get_response(op, 0, 0x0126); +} + +static int svc_dme_peer_set_request(struct operation *op) +{ + return svc_dme_peer_set_response(op, 0); +} + static int svc_interface_v_sys_enable_request(struct operation *op) { return svc_interface_v_sys_enable_response(op, GB_SVC_INTF_VSYS_OK); @@ -212,6 +287,14 @@ int svc_handler(struct operation *op) return svc_ping_request(op); case GB_SVC_TYPE_INTF_DEVICE_ID: return svc_interface_device_id_request(op); + case GB_SVC_TYPE_CONN_CREATE: + return svc_connection_create_request(op); + case GB_SVC_TYPE_CONN_DESTROY: + return svc_connection_destroy_request(op); + case GB_SVC_TYPE_DME_PEER_GET: + return svc_dme_peer_get_request(op); + case GB_SVC_TYPE_DME_PEER_SET: + return svc_dme_peer_set_request(op); case GB_SVC_TYPE_INTF_HOTPLUG: return svc_interface_hotplug_request(op); case GB_SVC_TYPE_INTF_HOT_UNPLUG: