diff options
Diffstat (limited to 'drivers/staging/vc04_services/interface')
15 files changed, 717 insertions, 231 deletions
diff --git a/drivers/staging/vc04_services/interface/vchi/TODO b/drivers/staging/vc04_services/interface/vchi/TODO index 03aa65183b25..df93154b1aa6 100644 --- a/drivers/staging/vc04_services/interface/vchi/TODO +++ b/drivers/staging/vc04_services/interface/vchi/TODO @@ -1,24 +1,9 @@ -1) Port to aarch64 - -This driver won't be very useful unless we also have it working on -Raspberry Pi 3. This requires, at least: - - - Figure out an alternative to the dmac_map_area() hack. - - - Decide what to use instead of dsb(). - - - Do something about (int) cast of bulk->data in - vchiq_bulk_transfer(). - - bulk->data is a bus address going across to the firmware. We know - our bus addresses are <32bit. - -2) Write a DT binding doc and get the corresponding DT node merged to +1) Write a DT binding doc and get the corresponding DT node merged to bcm2835. This will let the driver probe when enabled. -3) Import drivers using VCHI. +2) Import drivers using VCHI. VCHI is just a tool to let drivers talk to the firmware. Here are some of the ones we want: @@ -41,7 +26,7 @@ some of the ones we want: to manage these buffers as dmabufs so that we can zero-copy import camera images into vc4 for rendering/display. -4) Garbage-collect unused code +3) Garbage-collect unused code One of the reasons this driver wasn't upstreamed previously was that there's a lot code that got built that's probably unnecessary these diff --git a/drivers/staging/vc04_services/interface/vchi/vchi_cfg.h b/drivers/staging/vc04_services/interface/vchi/vchi_cfg.h index 26bc2d38d725..b6f42b86f206 100644 --- a/drivers/staging/vc04_services/interface/vchi/vchi_cfg.h +++ b/drivers/staging/vc04_services/interface/vchi/vchi_cfg.h @@ -173,7 +173,7 @@ * under the carpet. */ #if VCHI_RX_MSG_QUEUE_SIZE < (VCHI_MAX_MSG_SIZE/16 + 1) * VCHI_NUM_READ_SLOTS # undef VCHI_RX_MSG_QUEUE_SIZE -# define VCHI_RX_MSG_QUEUE_SIZE (VCHI_MAX_MSG_SIZE/16 + 1) * VCHI_NUM_READ_SLOTS +# define VCHI_RX_MSG_QUEUE_SIZE ((VCHI_MAX_MSG_SIZE/16 + 1) * VCHI_NUM_READ_SLOTS) #endif /* How many bulk transmits can we have pending. Once exhausted, vchi_bulk_queue_transmit diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c index 3aeffcb9c87e..988ee61fb4a7 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c @@ -37,12 +37,11 @@ #include <linux/interrupt.h> #include <linux/pagemap.h> #include <linux/dma-mapping.h> -#include <linux/version.h> #include <linux/io.h> #include <linux/platform_device.h> #include <linux/uaccess.h> +#include <linux/mm.h> #include <linux/of.h> -#include <asm/pgtable.h> #include <soc/bcm2835/raspberrypi-firmware.h> #define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32) @@ -208,6 +207,7 @@ VCHIQ_STATUS_T vchiq_platform_init_state(VCHIQ_STATE_T *state) { VCHIQ_STATUS_T status = VCHIQ_SUCCESS; + state->platform_state = kzalloc(sizeof(VCHIQ_2835_ARM_STATE_T), GFP_KERNEL); ((VCHIQ_2835_ARM_STATE_T *)state->platform_state)->inited = 1; status = vchiq_arm_init_state(state, &((VCHIQ_2835_ARM_STATE_T *)state->platform_state)->arm_state); @@ -293,6 +293,7 @@ vchiq_dump_platform_state(void *dump_context) { char buf[80]; int len; + len = snprintf(buf, sizeof(buf), " Platform: 2835 (VC master)"); vchiq_dump(dump_context, buf, len + 1); @@ -406,7 +407,7 @@ create_pagelist(char __user *buf, size_t count, unsigned short type, dma_addr_t dma_addr; offset = ((unsigned int)(unsigned long)buf & (PAGE_SIZE - 1)); - num_pages = (count + offset + PAGE_SIZE - 1) / PAGE_SIZE; + num_pages = DIV_ROUND_UP(count + offset, PAGE_SIZE); pagelist_size = sizeof(PAGELIST_T) + (num_pages * sizeof(u32)) + @@ -591,6 +592,7 @@ free_pagelist(struct vchiq_pagelist_info *pagelistinfo, (pagelist->type - PAGELIST_READ_WITH_FRAGMENTS) * g_fragments_size; int head_bytes, tail_bytes; + head_bytes = (g_cache_line_size - pagelist->offset) & (g_cache_line_size - 1); tail_bytes = (pagelist->offset + actual) & diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 8a0d214f6e9b..e823f1d5d177 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -48,6 +48,7 @@ #include <linux/list.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/compat.h> #include <soc/bcm2835/raspberrypi-firmware.h> #include "vchiq_core.h" @@ -194,8 +195,10 @@ static const char *const ioctl_names[] = { vchiq_static_assert(ARRAY_SIZE(ioctl_names) == (VCHIQ_IOC_MAX + 1)); +#if defined(CONFIG_BCM2835_VCHIQ_SUPPORT_MEMDUMP) static void dump_phys_mem(void *virt_addr, u32 num_bytes); +#endif /**************************************************************************** * @@ -210,6 +213,7 @@ add_completion(VCHIQ_INSTANCE_T instance, VCHIQ_REASON_T reason, { VCHIQ_COMPLETION_DATA_T *completion; int insert; + DEBUG_INITIALISE(g_state.local) insert = instance->completion_insert; @@ -281,6 +285,7 @@ service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, VCHIQ_SERVICE_T *service; VCHIQ_INSTANCE_T instance; bool skip_completion = false; + DEBUG_INITIALISE(g_state.local) DEBUG_TRACE(SERVICE_CALLBACK_LINE); @@ -316,6 +321,7 @@ service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, if ((user_service->message_available_pos - instance->completion_remove) < 0) { VCHIQ_STATUS_T status; + vchiq_log_info(vchiq_arm_log_level, "Inserting extra MESSAGE_AVAILABLE"); DEBUG_TRACE(SERVICE_CALLBACK_LINE); @@ -407,7 +413,7 @@ static void close_delivered(USER_SERVICE_T *user_service) } struct vchiq_io_copy_callback_context { - VCHIQ_ELEMENT_T *current_element; + struct vchiq_element *current_element; size_t current_element_offset; unsigned long elements_to_go; size_t current_offset; @@ -484,7 +490,7 @@ vchiq_ioc_copy_element_data( **************************************************************************/ static VCHIQ_STATUS_T vchiq_ioc_queue_message(VCHIQ_SERVICE_HANDLE_T handle, - VCHIQ_ELEMENT_T *elements, + struct vchiq_element *elements, unsigned long count) { struct vchiq_io_copy_callback_context context; @@ -520,6 +526,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) VCHIQ_SERVICE_T *service = NULL; long ret = 0; int i, rc; + DEBUG_INITIALISE(g_state.local) vchiq_log_trace(vchiq_arm_log_level, @@ -742,6 +749,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case VCHIQ_IOC_QUEUE_MESSAGE: { VCHIQ_QUEUE_MESSAGE_T args; + if (copy_from_user (&args, (const void __user *)arg, sizeof(args)) != 0) { @@ -753,9 +761,10 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if ((service != NULL) && (args.count <= MAX_ELEMENTS)) { /* Copy elements into kernel space */ - VCHIQ_ELEMENT_T elements[MAX_ELEMENTS]; + struct vchiq_element elements[MAX_ELEMENTS]; + if (copy_from_user(elements, args.elements, - args.count * sizeof(VCHIQ_ELEMENT_T)) == 0) + args.count * sizeof(struct vchiq_element)) == 0) status = vchiq_ioc_queue_message (args.handle, elements, args.count); @@ -770,6 +779,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case VCHIQ_IOC_QUEUE_BULK_RECEIVE: { VCHIQ_QUEUE_BULK_TRANSFER_T args; struct bulk_waiter_node *waiter = NULL; + VCHIQ_BULK_DIR_T dir = (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ? VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE; @@ -797,6 +807,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) args.userdata = &waiter->bulk_waiter; } else if (args.mode == VCHIQ_BULK_MODE_WAITING) { struct list_head *pos; + mutex_lock(&instance->bulk_waiter_list_mutex); list_for_each(pos, &instance->bulk_waiter_list) { if (list_entry(pos, struct bulk_waiter_node, @@ -882,6 +893,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) instance->completion_insert) && !instance->closing) { int rc; + DEBUG_TRACE(AWAIT_COMPLETION_LINE); mutex_unlock(&instance->completion_mutex); rc = down_interruptible(&instance->insert_event); @@ -1149,6 +1161,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) args.handle, args.option, args.value); } break; +#if defined(CONFIG_BCM2835_VCHIQ_SUPPORT_MEMDUMP) case VCHIQ_IOC_DUMP_PHYS_MEM: { VCHIQ_DUMP_MEM_T args; @@ -1160,6 +1173,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } dump_phys_mem(args.virt_addr, args.num_bytes); } break; +#endif case VCHIQ_IOC_LIB_VERSION: { unsigned int lib_version = (unsigned int)arg; @@ -1219,6 +1233,491 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return ret; } +#if defined(CONFIG_COMPAT) + +struct vchiq_service_params32 { + int fourcc; + compat_uptr_t callback; + compat_uptr_t userdata; + short version; /* Increment for non-trivial changes */ + short version_min; /* Update for incompatible changes */ +}; + +struct vchiq_create_service32 { + struct vchiq_service_params32 params; + int is_open; + int is_vchi; + unsigned int handle; /* OUT */ +}; + +#define VCHIQ_IOC_CREATE_SERVICE32 \ + _IOWR(VCHIQ_IOC_MAGIC, 2, struct vchiq_create_service32) + +static long +vchiq_compat_ioctl_create_service( + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + VCHIQ_CREATE_SERVICE_T __user *args; + struct vchiq_create_service32 __user *ptrargs32 = + (struct vchiq_create_service32 __user *)arg; + struct vchiq_create_service32 args32; + long ret; + + args = compat_alloc_user_space(sizeof(*args)); + if (!args) + return -EFAULT; + + if (copy_from_user(&args32, + (struct vchiq_create_service32 __user *)arg, + sizeof(args32))) + return -EFAULT; + + if (put_user(args32.params.fourcc, &args->params.fourcc) || + put_user(compat_ptr(args32.params.callback), + &args->params.callback) || + put_user(compat_ptr(args32.params.userdata), + &args->params.userdata) || + put_user(args32.params.version, &args->params.version) || + put_user(args32.params.version_min, + &args->params.version_min) || + put_user(args32.is_open, &args->is_open) || + put_user(args32.is_vchi, &args->is_vchi) || + put_user(args32.handle, &args->handle)) + return -EFAULT; + + ret = vchiq_ioctl(file, VCHIQ_IOC_CREATE_SERVICE, (unsigned long)args); + + if (ret < 0) + return ret; + + if (get_user(args32.handle, &args->handle)) + return -EFAULT; + + if (copy_to_user(&ptrargs32->handle, + &args32.handle, + sizeof(args32.handle))) + return -EFAULT; + + return 0; +} + +struct vchiq_element32 { + compat_uptr_t data; + unsigned int size; +}; + +struct vchiq_queue_message32 { + unsigned int handle; + unsigned int count; + compat_uptr_t elements; +}; + +#define VCHIQ_IOC_QUEUE_MESSAGE32 \ + _IOW(VCHIQ_IOC_MAGIC, 4, struct vchiq_queue_message32) + +static long +vchiq_compat_ioctl_queue_message(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + VCHIQ_QUEUE_MESSAGE_T *args; + struct vchiq_element *elements; + struct vchiq_queue_message32 args32; + unsigned int count; + + if (copy_from_user(&args32, + (struct vchiq_queue_message32 __user *)arg, + sizeof(args32))) + return -EFAULT; + + args = compat_alloc_user_space(sizeof(*args) + + (sizeof(*elements) * MAX_ELEMENTS)); + + if (!args) + return -EFAULT; + + if (put_user(args32.handle, &args->handle) || + put_user(args32.count, &args->count) || + put_user(compat_ptr(args32.elements), &args->elements)) + return -EFAULT; + + if (args32.count > MAX_ELEMENTS) + return -EINVAL; + + if (args32.elements && args32.count) { + struct vchiq_element32 tempelement32[MAX_ELEMENTS]; + + elements = (struct vchiq_element __user *)(args + 1); + + if (copy_from_user(&tempelement32, + compat_ptr(args32.elements), + sizeof(tempelement32))) + return -EFAULT; + + for (count = 0; count < args32.count; count++) { + if (put_user(compat_ptr(tempelement32[count].data), + &elements[count].data) || + put_user(tempelement32[count].size, + &elements[count].size)) + return -EFAULT; + } + + if (put_user(elements, &args->elements)) + return -EFAULT; + } + + return vchiq_ioctl(file, VCHIQ_IOC_QUEUE_MESSAGE, (unsigned long)args); +} + +struct vchiq_queue_bulk_transfer32 { + unsigned int handle; + compat_uptr_t data; + unsigned int size; + compat_uptr_t userdata; + VCHIQ_BULK_MODE_T mode; +}; + +#define VCHIQ_IOC_QUEUE_BULK_TRANSMIT32 \ + _IOWR(VCHIQ_IOC_MAGIC, 5, struct vchiq_queue_bulk_transfer32) +#define VCHIQ_IOC_QUEUE_BULK_RECEIVE32 \ + _IOWR(VCHIQ_IOC_MAGIC, 6, struct vchiq_queue_bulk_transfer32) + +static long +vchiq_compat_ioctl_queue_bulk(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + VCHIQ_QUEUE_BULK_TRANSFER_T *args; + struct vchiq_queue_bulk_transfer32 args32; + struct vchiq_queue_bulk_transfer32 *ptrargs32 = + (struct vchiq_queue_bulk_transfer32 *)arg; + long ret; + + args = compat_alloc_user_space(sizeof(*args)); + if (!args) + return -EFAULT; + + if (copy_from_user(&args32, + (struct vchiq_queue_bulk_transfer32 __user *)arg, + sizeof(args32))) + return -EFAULT; + + if (put_user(args32.handle, &args->handle) || + put_user(compat_ptr(args32.data), &args->data) || + put_user(args32.size, &args->size) || + put_user(compat_ptr(args32.userdata), &args->userdata) || + put_user(args32.mode, &args->mode)) + return -EFAULT; + + if (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT32) + cmd = VCHIQ_IOC_QUEUE_BULK_TRANSMIT; + else + cmd = VCHIQ_IOC_QUEUE_BULK_RECEIVE; + + ret = vchiq_ioctl(file, cmd, (unsigned long)args); + + if (ret < 0) + return ret; + + if (get_user(args32.mode, &args->mode)) + return -EFAULT; + + if (copy_to_user(&ptrargs32->mode, + &args32.mode, + sizeof(args32.mode))) + return -EFAULT; + + return 0; +} + +struct vchiq_completion_data32 { + VCHIQ_REASON_T reason; + compat_uptr_t header; + compat_uptr_t service_userdata; + compat_uptr_t bulk_userdata; +}; + +struct vchiq_await_completion32 { + unsigned int count; + compat_uptr_t buf; + unsigned int msgbufsize; + unsigned int msgbufcount; /* IN/OUT */ + compat_uptr_t msgbufs; +}; + +#define VCHIQ_IOC_AWAIT_COMPLETION32 \ + _IOWR(VCHIQ_IOC_MAGIC, 7, struct vchiq_await_completion32) + +static long +vchiq_compat_ioctl_await_completion(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + VCHIQ_AWAIT_COMPLETION_T *args; + VCHIQ_COMPLETION_DATA_T *completion; + VCHIQ_COMPLETION_DATA_T completiontemp; + struct vchiq_await_completion32 args32; + struct vchiq_completion_data32 completion32; + unsigned int *msgbufcount32; + compat_uptr_t msgbuf32; + void *msgbuf; + void **msgbufptr; + long ret; + + args = compat_alloc_user_space(sizeof(*args) + + sizeof(*completion) + + sizeof(*msgbufptr)); + if (!args) + return -EFAULT; + + completion = (VCHIQ_COMPLETION_DATA_T *)(args + 1); + msgbufptr = (void __user **)(completion + 1); + + if (copy_from_user(&args32, + (struct vchiq_completion_data32 *)arg, + sizeof(args32))) + return -EFAULT; + + if (put_user(args32.count, &args->count) || + put_user(compat_ptr(args32.buf), &args->buf) || + put_user(args32.msgbufsize, &args->msgbufsize) || + put_user(args32.msgbufcount, &args->msgbufcount) || + put_user(compat_ptr(args32.msgbufs), &args->msgbufs)) + return -EFAULT; + + /* These are simple cases, so just fall into the native handler */ + if (!args32.count || !args32.buf || !args32.msgbufcount) + return vchiq_ioctl(file, + VCHIQ_IOC_AWAIT_COMPLETION, + (unsigned long)args); + + /* + * These are the more complex cases. Typical applications of this + * ioctl will use a very large count, with a very large msgbufcount. + * Since the native ioctl can asynchronously fill in the returned + * buffers and the application can in theory begin processing messages + * even before the ioctl returns, a bit of a trick is used here. + * + * By forcing both count and msgbufcount to be 1, it forces the native + * ioctl to only claim at most 1 message is available. This tricks + * the calling application into thinking only 1 message was actually + * available in the queue so like all good applications it will retry + * waiting until all the required messages are received. + * + * This trick has been tested and proven to work with vchiq_test, + * Minecraft_PI, the "hello pi" examples, and various other + * applications that are included in Raspbian. + */ + + if (copy_from_user(&msgbuf32, + compat_ptr(args32.msgbufs) + + (sizeof(compat_uptr_t) * + (args32.msgbufcount - 1)), + sizeof(msgbuf32))) + return -EFAULT; + + msgbuf = compat_ptr(msgbuf32); + + if (copy_to_user(msgbufptr, + &msgbuf, + sizeof(msgbuf))) + return -EFAULT; + + if (copy_to_user(&args->msgbufs, + &msgbufptr, + sizeof(msgbufptr))) + return -EFAULT; + + if (put_user(1U, &args->count) || + put_user(completion, &args->buf) || + put_user(1U, &args->msgbufcount)) + return -EFAULT; + + ret = vchiq_ioctl(file, + VCHIQ_IOC_AWAIT_COMPLETION, + (unsigned long)args); + + /* + * An return value of 0 here means that no messages where available + * in the message queue. In this case the native ioctl does not + * return any data to the application at all. Not even to update + * msgbufcount. This functionality needs to be kept here for + * compatibility. + * + * Of course, < 0 means that an error occurred and no data is being + * returned. + * + * Since count and msgbufcount was forced to 1, that means + * the only other possible return value is 1. Meaning that 1 message + * was available, so that multiple message case does not need to be + * handled here. + */ + if (ret <= 0) + return ret; + + if (copy_from_user(&completiontemp, completion, sizeof(*completion))) + return -EFAULT; + + completion32.reason = completiontemp.reason; + completion32.header = ptr_to_compat(completiontemp.header); + completion32.service_userdata = + ptr_to_compat(completiontemp.service_userdata); + completion32.bulk_userdata = + ptr_to_compat(completiontemp.bulk_userdata); + + if (copy_to_user(compat_ptr(args32.buf), + &completion32, + sizeof(completion32))) + return -EFAULT; + + args32.msgbufcount--; + + msgbufcount32 = + &((struct vchiq_await_completion32 __user *)arg)->msgbufcount; + + if (copy_to_user(msgbufcount32, + &args32.msgbufcount, + sizeof(args32.msgbufcount))) + return -EFAULT; + + return 1; +} + +struct vchiq_dequeue_message32 { + unsigned int handle; + int blocking; + unsigned int bufsize; + compat_uptr_t buf; +}; + +#define VCHIQ_IOC_DEQUEUE_MESSAGE32 \ + _IOWR(VCHIQ_IOC_MAGIC, 8, struct vchiq_dequeue_message32) + +static long +vchiq_compat_ioctl_dequeue_message(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + VCHIQ_DEQUEUE_MESSAGE_T *args; + struct vchiq_dequeue_message32 args32; + + args = compat_alloc_user_space(sizeof(*args)); + if (!args) + return -EFAULT; + + if (copy_from_user(&args32, + (struct vchiq_dequeue_message32 *)arg, + sizeof(args32))) + return -EFAULT; + + if (put_user(args32.handle, &args->handle) || + put_user(args32.blocking, &args->blocking) || + put_user(args32.bufsize, &args->bufsize) || + put_user(compat_ptr(args32.buf), &args->buf)) + return -EFAULT; + + return vchiq_ioctl(file, VCHIQ_IOC_DEQUEUE_MESSAGE, + (unsigned long)args); +} + +struct vchiq_get_config32 { + unsigned int config_size; + compat_uptr_t pconfig; +}; + +#define VCHIQ_IOC_GET_CONFIG32 \ + _IOWR(VCHIQ_IOC_MAGIC, 10, struct vchiq_get_config32) + +static long +vchiq_compat_ioctl_get_config(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + VCHIQ_GET_CONFIG_T *args; + struct vchiq_get_config32 args32; + + args = compat_alloc_user_space(sizeof(*args)); + if (!args) + return -EFAULT; + + if (copy_from_user(&args32, + (struct vchiq_get_config32 *)arg, + sizeof(args32))) + return -EFAULT; + + if (put_user(args32.config_size, &args->config_size) || + put_user(compat_ptr(args32.pconfig), &args->pconfig)) + return -EFAULT; + + return vchiq_ioctl(file, VCHIQ_IOC_GET_CONFIG, (unsigned long)args); +} + +#if defined(CONFIG_BCM2835_VCHIQ_SUPPORT_MEMDUMP) + +struct vchiq_dump_mem32 { + compat_uptr_t virt_addr; + u32 num_bytes; +}; + +#define VCHIQ_IOC_DUMP_PHYS_MEM32 \ + _IOW(VCHIQ_IOC_MAGIC, 15, struct vchiq_dump_mem32) + +static long +vchiq_compat_ioctl_dump_phys_mem(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + VCHIQ_DUMP_MEM_T *args; + struct vchiq_dump_mem32 args32; + + args = compat_alloc_user_space(sizeof(*args)); + if (!args) + return -EFAULT; + + if (copy_from_user(&args32, + (struct vchiq_dump_mem32 *)arg, + sizeof(args32))) + return -EFAULT; + + if (put_user(compat_ptr(args32.virt_addr), &args->virt_addr) || + put_user(args32.num_bytes, &args->num_bytes)) + return -EFAULT; + + return vchiq_ioctl(file, VCHIQ_IOC_DUMP_PHYS_MEM, (unsigned long)args); +} + +#endif + +static long +vchiq_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case VCHIQ_IOC_CREATE_SERVICE32: + return vchiq_compat_ioctl_create_service(file, cmd, arg); + case VCHIQ_IOC_QUEUE_MESSAGE32: + return vchiq_compat_ioctl_queue_message(file, cmd, arg); + case VCHIQ_IOC_QUEUE_BULK_TRANSMIT32: + case VCHIQ_IOC_QUEUE_BULK_RECEIVE32: + return vchiq_compat_ioctl_queue_bulk(file, cmd, arg); + case VCHIQ_IOC_AWAIT_COMPLETION32: + return vchiq_compat_ioctl_await_completion(file, cmd, arg); + case VCHIQ_IOC_DEQUEUE_MESSAGE32: + return vchiq_compat_ioctl_dequeue_message(file, cmd, arg); + case VCHIQ_IOC_GET_CONFIG32: + return vchiq_compat_ioctl_get_config(file, cmd, arg); +#if defined(CONFIG_BCM2835_VCHIQ_SUPPORT_MEMDUMP) + case VCHIQ_IOC_DUMP_PHYS_MEM32: + return vchiq_compat_ioctl_dump_phys_mem(file, cmd, arg); +#endif + default: + return vchiq_ioctl(file, cmd, arg); + } +} + +#endif + /**************************************************************************** * * vchiq_open @@ -1229,6 +1728,7 @@ static int vchiq_open(struct inode *inode, struct file *file) { int dev = iminor(inode) & 0x0f; + vchiq_log_info(vchiq_arm_log_level, "vchiq_open"); switch (dev) { case VCHIQ_MINOR: { @@ -1284,6 +1784,7 @@ vchiq_release(struct inode *inode, struct file *file) { int dev = iminor(inode) & 0x0f; int ret = 0; + switch (dev) { case VCHIQ_MINOR: { VCHIQ_INSTANCE_T instance = file->private_data; @@ -1365,6 +1866,7 @@ vchiq_release(struct inode *inode, struct file *file) instance->completion_insert) { VCHIQ_COMPLETION_DATA_T *completion; VCHIQ_SERVICE_T *service; + completion = &instance->completions[ instance->completion_remove & (MAX_COMPLETIONS - 1)]; @@ -1387,9 +1889,11 @@ vchiq_release(struct inode *inode, struct file *file) { struct list_head *pos, *next; + list_for_each_safe(pos, next, &instance->bulk_waiter_list) { struct bulk_waiter_node *waiter; + waiter = list_entry(pos, struct bulk_waiter_node, list); @@ -1430,8 +1934,10 @@ vchiq_dump(void *dump_context, const char *str, int len) if (context->actual < context->space) { int copy_bytes; + if (context->offset > 0) { int skip_bytes = min(len, (int)context->offset); + str += skip_bytes; len -= skip_bytes; context->offset -= skip_bytes; @@ -1452,6 +1958,7 @@ vchiq_dump(void *dump_context, const char *str, int len) ** carriage return. */ if ((len == 0) && (str[copy_bytes - 1] == '\0')) { char cr = '\n'; + if (copy_to_user(context->buf + context->actual - 1, &cr, 1)) context->actual = -EFAULT; @@ -1547,6 +2054,8 @@ vchiq_dump_platform_service_state(void *dump_context, VCHIQ_SERVICE_T *service) * ***************************************************************************/ +#if defined(CONFIG_BCM2835_VCHIQ_SUPPORT_MEMDUMP) + static void dump_phys_mem(void *virt_addr, u32 num_bytes) { @@ -1570,10 +2079,10 @@ dump_phys_mem(void *virt_addr, u32 num_bytes) offset = (int)(long)virt_addr & (PAGE_SIZE - 1); end_offset = (int)(long)end_virt_addr & (PAGE_SIZE - 1); - num_pages = (offset + num_bytes + PAGE_SIZE - 1) / PAGE_SIZE; + num_pages = DIV_ROUND_UP(offset + num_bytes, PAGE_SIZE); pages = kmalloc(sizeof(struct page *) * num_pages, GFP_KERNEL); - if (pages == NULL) { + if (!pages) { vchiq_log_error(vchiq_arm_log_level, "Unable to allocation memory for %d pages\n", num_pages); @@ -1599,17 +2108,14 @@ dump_phys_mem(void *virt_addr, u32 num_bytes) } while (offset < end_offset) { - int page_offset = offset % PAGE_SIZE; - page_idx = offset / PAGE_SIZE; + page_idx = offset / PAGE_SIZE; if (page_idx != prev_idx) { - if (page != NULL) kunmap(page); page = pages[page_idx]; kmapped_virt_ptr = kmap(page); - prev_idx = page_idx; } @@ -1632,6 +2138,8 @@ out: kfree(pages); } +#endif + /**************************************************************************** * * vchiq_read @@ -1643,6 +2151,7 @@ vchiq_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { DUMP_CONTEXT_T context; + context.buf = buf; context.actual = 0; context.space = count; @@ -1673,6 +2182,9 @@ static const struct file_operations vchiq_fops = { .owner = THIS_MODULE, .unlocked_ioctl = vchiq_ioctl, +#if defined(CONFIG_COMPAT) + .compat_ioctl = vchiq_compat_ioctl, +#endif .open = vchiq_open, .release = vchiq_release, .read = vchiq_read @@ -1686,6 +2198,7 @@ int vchiq_videocore_wanted(VCHIQ_STATE_T *state) { VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); + if (!arm_state) /* autosuspend not supported - always return wanted */ return 1; @@ -1753,6 +2266,7 @@ vchiq_keepalive_thread_func(void *v) while (1) { long rc = 0, uc = 0; + if (wait_for_completion_interruptible(&arm_state->ka_evt) != 0) { vchiq_log_error(vchiq_susp_log_level, @@ -1982,6 +2496,7 @@ static inline int need_resume(VCHIQ_STATE_T *state) { VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); + return (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) && (arm_state->vc_resume_state < VC_RESUME_REQUESTED) && vchiq_videocore_wanted(state); @@ -2155,6 +2670,7 @@ output_timeout_error(VCHIQ_STATE_T *state) } for (i = 0; i < active_services; i++) { VCHIQ_SERVICE_T *service_ptr = state->services[i]; + if (service_ptr && service_ptr->service_use_count && (service_ptr->srvstate != VCHIQ_SRVSTATE_FREE)) { snprintf(err, sizeof(err), " %c%c%c%c(%d) service has " @@ -2502,6 +3018,7 @@ vchiq_use_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, if (ret == VCHIQ_SUCCESS) { VCHIQ_STATUS_T status = VCHIQ_SUCCESS; long ack_cnt = atomic_xchg(&arm_state->ka_use_ack_count, 0); + while (ack_cnt && (status == VCHIQ_SUCCESS)) { /* Send the use notify to videocore */ status = vchiq_send_remote_use_active(state); @@ -2584,6 +3101,7 @@ void vchiq_on_remote_use(VCHIQ_STATE_T *state) { VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); + vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); atomic_inc(&arm_state->ka_use_count); complete(&arm_state->ka_evt); @@ -2593,6 +3111,7 @@ void vchiq_on_remote_release(VCHIQ_STATE_T *state) { VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); + vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); atomic_inc(&arm_state->ka_release_count); complete(&arm_state->ka_evt); @@ -2621,6 +3140,7 @@ vchiq_instance_get_use_count(VCHIQ_INSTANCE_T instance) { VCHIQ_SERVICE_T *service; int use_count = 0, i; + i = 0; while ((service = next_service_by_instance(instance->state, instance, &i)) != NULL) { @@ -2647,6 +3167,7 @@ vchiq_instance_set_trace(VCHIQ_INSTANCE_T instance, int trace) { VCHIQ_SERVICE_T *service; int i; + i = 0; while ((service = next_service_by_instance(instance->state, instance, &i)) != NULL) { @@ -2660,6 +3181,7 @@ static void suspend_timer_callback(unsigned long context) { VCHIQ_STATE_T *state = (VCHIQ_STATE_T *)context; VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); + if (!arm_state) goto out; vchiq_log_info(vchiq_susp_log_level, @@ -2674,6 +3196,7 @@ vchiq_use_service_no_resume(VCHIQ_SERVICE_HANDLE_T handle) { VCHIQ_STATUS_T ret = VCHIQ_ERROR; VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + if (service) { ret = vchiq_use_internal(service->state, service, USE_TYPE_SERVICE_NO_RESUME); @@ -2687,6 +3210,7 @@ vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle) { VCHIQ_STATUS_T ret = VCHIQ_ERROR; VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + if (service) { ret = vchiq_use_internal(service->state, service, USE_TYPE_SERVICE); @@ -2700,6 +3224,7 @@ vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle) { VCHIQ_STATUS_T ret = VCHIQ_ERROR; VCHIQ_SERVICE_T *service = find_service_by_handle(handle); + if (service) { ret = vchiq_release_internal(service->state, service); unlock_service(service); @@ -2744,6 +3269,7 @@ vchiq_dump_service_use_state(VCHIQ_STATE_T *state) for (i = 0; (i < active_services) && (j < local_max_services); i++) { VCHIQ_SERVICE_T *service_ptr = state->services[i]; + if (!service_ptr) continue; @@ -2832,12 +3358,14 @@ void vchiq_platform_conn_state_changed(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T oldstate, VCHIQ_CONNSTATE_T newstate) { VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); + vchiq_log_info(vchiq_susp_log_level, "%d: %s->%s", state->id, get_conn_state_name(oldstate), get_conn_state_name(newstate)); if (state->conn_state == VCHIQ_CONNSTATE_CONNECTED) { write_lock_bh(&arm_state->susp_res_lock); if (!arm_state->first_connect) { char threadname[16]; + arm_state->first_connect = 1; write_unlock_bh(&arm_state->susp_res_lock); snprintf(threadname, sizeof(threadname), "vchiq-keep/%d", @@ -2962,6 +3490,6 @@ static struct platform_driver vchiq_driver = { }; module_platform_driver(vchiq_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("Videocore VCHIQ driver"); MODULE_AUTHOR("Broadcom Corporation"); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index d587097b261c..4f9e738abddf 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -192,6 +192,7 @@ VCHIQ_SERVICE_T * find_service_by_port(VCHIQ_STATE_T *state, int localport) { VCHIQ_SERVICE_T *service = NULL; + if ((unsigned int)localport <= VCHIQ_PORT_MAX) { spin_lock(&service_spinlock); service = state->services[localport]; @@ -268,6 +269,7 @@ next_service_by_instance(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance, spin_lock(&service_spinlock); while (idx < state->unused_service) { VCHIQ_SERVICE_T *srv = state->services[idx++]; + if (srv && (srv->srvstate != VCHIQ_SRVSTATE_FREE) && (srv->instance == instance)) { service = srv; @@ -381,6 +383,7 @@ make_service_callback(VCHIQ_SERVICE_T *service, VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, void *bulk_userdata) { VCHIQ_STATUS_T status; + vchiq_log_trace(vchiq_core_log_level, "%d: callback:%d (%s, %pK, %pK)", service->state->id, service->localport, reason_names[reason], header, bulk_userdata); @@ -399,6 +402,7 @@ inline void vchiq_set_conn_state(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T newstate) { VCHIQ_CONNSTATE_T oldstate = state->conn_state; + vchiq_log_info(vchiq_core_log_level, "%d: %s->%s", state->id, conn_state_names[oldstate], conn_state_names[newstate]); @@ -485,6 +489,7 @@ get_listening_service(VCHIQ_STATE_T *state, int fourcc) for (i = 0; i < state->unused_service; i++) { VCHIQ_SERVICE_T *service = state->services[i]; + if (service && (service->public_fourcc == fourcc) && ((service->srvstate == VCHIQ_SRVSTATE_LISTENING) || @@ -503,8 +508,10 @@ static VCHIQ_SERVICE_T * get_connected_service(VCHIQ_STATE_T *state, unsigned int port) { int i; + for (i = 0; i < state->unused_service; i++) { VCHIQ_SERVICE_T *service = state->services[i]; + if (service && (service->srvstate == VCHIQ_SRVSTATE_OPEN) && (service->remoteport == port)) { lock_service(service); @@ -645,11 +652,13 @@ process_free_queue(VCHIQ_STATE_T *state) VCHIQ_HEADER_T *header = (VCHIQ_HEADER_T *)(data + pos); int msgid = header->msgid; + if (VCHIQ_MSG_TYPE(msgid) == VCHIQ_MSG_DATA) { int port = VCHIQ_MSG_SRCPORT(msgid); VCHIQ_SERVICE_QUOTA_T *service_quota = &state->service_quotas[port]; int count; + spin_lock("a_spinlock); count = service_quota->message_use_count; if (count > 0) @@ -719,6 +728,7 @@ process_free_queue(VCHIQ_STATE_T *state) if (data_found) { int count; + spin_lock("a_spinlock); count = state->data_use_count; if (count > 0) @@ -745,9 +755,7 @@ memcpy_copy_callback( void *context, void *dest, size_t offset, size_t maxsize) { - void *src = context; - - memcpy(dest + offset, src + offset, maxsize); + memcpy(dest + offset, context + offset, maxsize); return maxsize; } @@ -1059,6 +1067,7 @@ queue_message_sync(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, { int oldmsgid = header->msgid; + if (oldmsgid != VCHIQ_MSGID_PADDING) vchiq_log_error(vchiq_core_log_level, "%d: qms - msgid %x, not PADDING", @@ -1143,6 +1152,7 @@ release_slot(VCHIQ_STATE_T *state, VCHIQ_SLOT_INFO_T *slot_info, if (header) { int msgid = header->msgid; + if (((msgid & VCHIQ_MSGID_CLAIMED) == 0) || (service && service->closing)) { mutex_unlock(&state->recycle_mutex); @@ -1252,6 +1262,7 @@ notify_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue, } if (bulk->mode == VCHIQ_BULK_MODE_BLOCKING) { struct bulk_waiter *waiter; + spin_lock(&bulk_waiter_spinlock); waiter = bulk->userdata; if (waiter) { @@ -1301,6 +1312,7 @@ poll_services(VCHIQ_STATE_T *state) for (group = 0; group < BITSET_SIZE(state->unused_service); group++) { u32 flags; + flags = atomic_xchg(&state->poll_services[group], 0); for (i = 0; flags; i++) { if (flags & (1 << i)) { @@ -1308,6 +1320,7 @@ poll_services(VCHIQ_STATE_T *state) find_service_by_port(state, (group<<5) + i); u32 service_flags; + flags &= ~(1 << i); if (!service) continue; @@ -1421,6 +1434,7 @@ static void abort_outstanding_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue) { int is_tx = (queue == &service->bulk_tx); + vchiq_log_trace(vchiq_core_log_level, "%d: aob:%d %cx - li=%x ri=%x p=%x", service->state->id, service->localport, is_tx ? 't' : 'r', @@ -1484,6 +1498,7 @@ static void resume_bulks(VCHIQ_STATE_T *state) { int i; + if (unlikely(atomic_dec_return(&pause_bulks_count) != 0)) { WARN_ON_ONCE(1); atomic_set(&pause_bulks_count, 0); @@ -1507,6 +1522,7 @@ resume_bulks(VCHIQ_STATE_T *state) VCHIQ_SERVICE_T *service = state->services[i]; int resolved_rx = 0; int resolved_tx = 0; + if (!service || (service->srvstate != VCHIQ_SRVSTATE_OPEN)) continue; @@ -1550,6 +1566,7 @@ parse_open(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header) /* A matching service exists */ short version = payload->version; short version_min = payload->version_min; + if ((service->version < version_min) || (version < service->version_min)) { /* Version mismatch */ @@ -1651,6 +1668,7 @@ parse_rx_slots(VCHIQ_STATE_T *state) VCHIQ_SHARED_STATE_T *remote = state->remote; VCHIQ_SERVICE_T *service = NULL; int tx_pos; + DEBUG_INITIALISE(state->local) tx_pos = remote->tx_pos; @@ -1664,6 +1682,7 @@ parse_rx_slots(VCHIQ_STATE_T *state) DEBUG_TRACE(PARSE_LINE); if (!state->rx_data) { int rx_index; + WARN_ON(!((state->rx_pos & VCHIQ_SLOT_MASK) == 0)); rx_index = remote->slot_queue[ SLOT_QUEUE_INDEX_FROM_POS(state->rx_pos) & @@ -1841,6 +1860,7 @@ parse_rx_slots(VCHIQ_STATE_T *state) case VCHIQ_MSG_BULK_RX: case VCHIQ_MSG_BULK_TX: { VCHIQ_BULK_QUEUE_T *queue; + WARN_ON(!state->is_master); queue = (type == VCHIQ_MSG_BULK_RX) ? &service->bulk_tx : &service->bulk_rx; @@ -2054,6 +2074,7 @@ slot_handler_func(void *v) { VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; VCHIQ_SHARED_STATE_T *local = state->local; + DEBUG_INITIALISE(local) while (1) { @@ -2553,131 +2574,126 @@ vchiq_add_service_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance, VCHIQ_USERDATA_TERM_T userdata_term) { VCHIQ_SERVICE_T *service; + VCHIQ_SERVICE_T **pservice = NULL; + VCHIQ_SERVICE_QUOTA_T *service_quota; + int i; service = kmalloc(sizeof(VCHIQ_SERVICE_T), GFP_KERNEL); - if (service) { - service->base.fourcc = params->fourcc; - service->base.callback = params->callback; - service->base.userdata = params->userdata; - service->handle = VCHIQ_SERVICE_HANDLE_INVALID; - service->ref_count = 1; - service->srvstate = VCHIQ_SRVSTATE_FREE; - service->userdata_term = userdata_term; - service->localport = VCHIQ_PORT_FREE; - service->remoteport = VCHIQ_PORT_FREE; - - service->public_fourcc = (srvstate == VCHIQ_SRVSTATE_OPENING) ? - VCHIQ_FOURCC_INVALID : params->fourcc; - service->client_id = 0; - service->auto_close = 1; - service->sync = 0; - service->closing = 0; - service->trace = 0; - atomic_set(&service->poll_flags, 0); - service->version = params->version; - service->version_min = params->version_min; - service->state = state; - service->instance = instance; - service->service_use_count = 0; - init_bulk_queue(&service->bulk_tx); - init_bulk_queue(&service->bulk_rx); - sema_init(&service->remove_event, 0); - sema_init(&service->bulk_remove_event, 0); - mutex_init(&service->bulk_mutex); - memset(&service->stats, 0, sizeof(service->stats)); - } else { - vchiq_log_error(vchiq_core_log_level, - "Out of memory"); - } - - if (service) { - VCHIQ_SERVICE_T **pservice = NULL; - int i; - - /* Although it is perfectly possible to use service_spinlock - ** to protect the creation of services, it is overkill as it - ** disables interrupts while the array is searched. - ** The only danger is of another thread trying to create a - ** service - service deletion is safe. - ** Therefore it is preferable to use state->mutex which, - ** although slower to claim, doesn't block interrupts while - ** it is held. - */ - - mutex_lock(&state->mutex); - - /* Prepare to use a previously unused service */ - if (state->unused_service < VCHIQ_MAX_SERVICES) - pservice = &state->services[state->unused_service]; - - if (srvstate == VCHIQ_SRVSTATE_OPENING) { - for (i = 0; i < state->unused_service; i++) { - VCHIQ_SERVICE_T *srv = state->services[i]; - if (!srv) { - pservice = &state->services[i]; - break; - } - } - } else { - for (i = (state->unused_service - 1); i >= 0; i--) { - VCHIQ_SERVICE_T *srv = state->services[i]; - if (!srv) - pservice = &state->services[i]; - else if ((srv->public_fourcc == params->fourcc) - && ((srv->instance != instance) || - (srv->base.callback != - params->callback))) { - /* There is another server using this - ** fourcc which doesn't match. */ - pservice = NULL; - break; - } + if (!service) + return service; + + service->base.fourcc = params->fourcc; + service->base.callback = params->callback; + service->base.userdata = params->userdata; + service->handle = VCHIQ_SERVICE_HANDLE_INVALID; + service->ref_count = 1; + service->srvstate = VCHIQ_SRVSTATE_FREE; + service->userdata_term = userdata_term; + service->localport = VCHIQ_PORT_FREE; + service->remoteport = VCHIQ_PORT_FREE; + + service->public_fourcc = (srvstate == VCHIQ_SRVSTATE_OPENING) ? + VCHIQ_FOURCC_INVALID : params->fourcc; + service->client_id = 0; + service->auto_close = 1; + service->sync = 0; + service->closing = 0; + service->trace = 0; + atomic_set(&service->poll_flags, 0); + service->version = params->version; + service->version_min = params->version_min; + service->state = state; + service->instance = instance; + service->service_use_count = 0; + init_bulk_queue(&service->bulk_tx); + init_bulk_queue(&service->bulk_rx); + sema_init(&service->remove_event, 0); + sema_init(&service->bulk_remove_event, 0); + mutex_init(&service->bulk_mutex); + memset(&service->stats, 0, sizeof(service->stats)); + + /* Although it is perfectly possible to use service_spinlock + ** to protect the creation of services, it is overkill as it + ** disables interrupts while the array is searched. + ** The only danger is of another thread trying to create a + ** service - service deletion is safe. + ** Therefore it is preferable to use state->mutex which, + ** although slower to claim, doesn't block interrupts while + ** it is held. + */ + + mutex_lock(&state->mutex); + + /* Prepare to use a previously unused service */ + if (state->unused_service < VCHIQ_MAX_SERVICES) + pservice = &state->services[state->unused_service]; + + if (srvstate == VCHIQ_SRVSTATE_OPENING) { + for (i = 0; i < state->unused_service; i++) { + VCHIQ_SERVICE_T *srv = state->services[i]; + + if (!srv) { + pservice = &state->services[i]; + break; } } - - if (pservice) { - service->localport = (pservice - state->services); - if (!handle_seq) - handle_seq = VCHIQ_MAX_STATES * - VCHIQ_MAX_SERVICES; - service->handle = handle_seq | - (state->id * VCHIQ_MAX_SERVICES) | - service->localport; - handle_seq += VCHIQ_MAX_STATES * VCHIQ_MAX_SERVICES; - *pservice = service; - if (pservice == &state->services[state->unused_service]) - state->unused_service++; - } - - mutex_unlock(&state->mutex); - - if (!pservice) { - kfree(service); - service = NULL; + } else { + for (i = (state->unused_service - 1); i >= 0; i--) { + VCHIQ_SERVICE_T *srv = state->services[i]; + + if (!srv) + pservice = &state->services[i]; + else if ((srv->public_fourcc == params->fourcc) + && ((srv->instance != instance) || + (srv->base.callback != + params->callback))) { + /* There is another server using this + ** fourcc which doesn't match. */ + pservice = NULL; + break; + } } } - if (service) { - VCHIQ_SERVICE_QUOTA_T *service_quota = - &state->service_quotas[service->localport]; - service_quota->slot_quota = state->default_slot_quota; - service_quota->message_quota = state->default_message_quota; - if (service_quota->slot_use_count == 0) - service_quota->previous_tx_index = - SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos) - - 1; + if (pservice) { + service->localport = (pservice - state->services); + if (!handle_seq) + handle_seq = VCHIQ_MAX_STATES * + VCHIQ_MAX_SERVICES; + service->handle = handle_seq | + (state->id * VCHIQ_MAX_SERVICES) | + service->localport; + handle_seq += VCHIQ_MAX_STATES * VCHIQ_MAX_SERVICES; + *pservice = service; + if (pservice == &state->services[state->unused_service]) + state->unused_service++; + } - /* Bring this service online */ - vchiq_set_service_state(service, srvstate); + mutex_unlock(&state->mutex); - vchiq_log_info(vchiq_core_msg_log_level, - "%s Service %c%c%c%c SrcPort:%d", - (srvstate == VCHIQ_SRVSTATE_OPENING) - ? "Open" : "Add", - VCHIQ_FOURCC_AS_4CHARS(params->fourcc), - service->localport); + if (!pservice) { + kfree(service); + return NULL; } + service_quota = &state->service_quotas[service->localport]; + service_quota->slot_quota = state->default_slot_quota; + service_quota->message_quota = state->default_message_quota; + if (service_quota->slot_use_count == 0) + service_quota->previous_tx_index = + SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos) + - 1; + + /* Bring this service online */ + vchiq_set_service_state(service, srvstate); + + vchiq_log_info(vchiq_core_msg_log_level, + "%s Service %c%c%c%c SrcPort:%d", + (srvstate == VCHIQ_SRVSTATE_OPENING) + ? "Open" : "Add", + VCHIQ_FOURCC_AS_4CHARS(params->fourcc), + service->localport); + /* Don't unlock the service - leave it with a ref_count of 1. */ return service; @@ -2766,6 +2782,7 @@ release_service_messages(VCHIQ_SERVICE_T *service) (VCHIQ_HEADER_T *)(data + pos); int msgid = header->msgid; int port = VCHIQ_MSG_DSTPORT(msgid); + if ((port == service->localport) && (msgid & VCHIQ_MSGID_CLAIMED)) { vchiq_log_info(vchiq_core_log_level, @@ -3498,6 +3515,7 @@ vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle, VCHIQ_HEADER_T *header) if ((slot_index >= remote->slot_first) && (slot_index <= remote->slot_last)) { int msgid = header->msgid; + if (msgid & VCHIQ_MSGID_CLAIMED) { VCHIQ_SLOT_INFO_T *slot_info = SLOT_INFO_FROM_INDEX(state, slot_index); @@ -3656,9 +3674,9 @@ vchiq_dump_shared_state(void *dump_context, VCHIQ_STATE_T *state, "COMPLETION_QUEUE_FULL_COUNT" }; int i; - char buf[80]; int len; + len = snprintf(buf, sizeof(buf), " %s: slots %d-%d tx_pos=%x recycle=%x", label, shared->slot_first, shared->slot_last, @@ -3762,9 +3780,11 @@ vchiq_dump_service_state(void *dump_context, VCHIQ_SERVICE_T *service) &service->state->service_quotas[service->localport]; int fourcc = service->base.fourcc; int tx_pending, rx_pending; + if (service->remoteport != VCHIQ_PORT_FREE) { int len2 = snprintf(remoteport, sizeof(remoteport), "%u", service->remoteport); + if (service->public_fourcc != VCHIQ_FOURCC_INVALID) snprintf(remoteport + len2, sizeof(remoteport) - len2, @@ -3866,6 +3886,7 @@ vchiq_loud_error_footer(void) VCHIQ_STATUS_T vchiq_send_remote_use(VCHIQ_STATE_T *state) { VCHIQ_STATUS_T status = VCHIQ_RETRY; + if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) status = queue_message(state, NULL, VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE, 0, 0), @@ -3876,6 +3897,7 @@ VCHIQ_STATUS_T vchiq_send_remote_use(VCHIQ_STATE_T *state) VCHIQ_STATUS_T vchiq_send_remote_release(VCHIQ_STATE_T *state) { VCHIQ_STATUS_T status = VCHIQ_RETRY; + if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) status = queue_message(state, NULL, VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_RELEASE, 0, 0), @@ -3886,6 +3908,7 @@ VCHIQ_STATUS_T vchiq_send_remote_release(VCHIQ_STATE_T *state) VCHIQ_STATUS_T vchiq_send_remote_use_active(VCHIQ_STATE_T *state) { VCHIQ_STATUS_T status = VCHIQ_RETRY; + if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) status = queue_message(state, NULL, VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE_ACTIVE, 0, 0), diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c index f07cd4448ddf..9367a9a5aa3c 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_debugfs.c @@ -81,9 +81,7 @@ static struct vchiq_debugfs_log_entry vchiq_debugfs_log_entries[] = { { "susp", &vchiq_susp_log_level }, { "arm", &vchiq_arm_log_level }, }; -static int n_log_entries = - sizeof(vchiq_debugfs_log_entries)/sizeof(vchiq_debugfs_log_entries[0]); - +static int n_log_entries = ARRAY_SIZE(vchiq_debugfs_log_entries); static struct dentry *vchiq_clients_top(void); static struct dentry *vchiq_debugfs_top(void); @@ -167,6 +165,7 @@ static int vchiq_debugfs_create_log_entries(struct dentry *top) struct dentry *dir; size_t i; int ret = 0; + dir = debugfs_create_dir("log", vchiq_debugfs_top()); if (!dir) return -ENOMEM; @@ -174,6 +173,7 @@ static int vchiq_debugfs_create_log_entries(struct dentry *top) for (i = 0; i < n_log_entries; i++) { void *levp = (void *)vchiq_debugfs_log_entries[i].plevel; + dir = debugfs_create_file(vchiq_debugfs_log_entries[i].name, 0644, debugfs_info.log_categories, @@ -312,6 +312,7 @@ fail_top: void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance) { VCHIQ_DEBUGFS_NODE_T *node = vchiq_instance_get_debugfs_node(instance); + debugfs_remove_recursive(node->dentry); } diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h index 377e8e48bb54..0e270852900d 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_if.h @@ -88,10 +88,10 @@ typedef struct vchiq_header_struct { char data[0]; /* message */ } VCHIQ_HEADER_T; -typedef struct { +struct vchiq_element { const void *data; unsigned int size; -} VCHIQ_ELEMENT_T; +}; typedef unsigned int VCHIQ_SERVICE_HANDLE_T; diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h index 6137ae9de1c1..9f859953f45c 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_ioctl.h @@ -50,7 +50,7 @@ typedef struct { typedef struct { unsigned int handle; unsigned int count; - const VCHIQ_ELEMENT_T *elements; + const struct vchiq_element *elements; } VCHIQ_QUEUE_MESSAGE_T; typedef struct { diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c index 4317c06943a6..34f746db19cd 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c @@ -147,9 +147,11 @@ VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance) if (status == VCHIQ_SUCCESS) { struct list_head *pos, *next; + list_for_each_safe(pos, next, &instance->bulk_waiter_list) { struct bulk_waiter_node *waiter; + waiter = list_entry(pos, struct bulk_waiter_node, list); @@ -406,6 +408,7 @@ vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, if (waiter) { VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; + if (bulk) { /* This thread has an outstanding bulk transfer. */ if ((bulk->data != data) || @@ -435,6 +438,7 @@ vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) || !waiter->bulk_waiter.bulk) { VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; + if (bulk) { /* Cancel the signal when the transfer ** completes. */ diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_memdrv.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_memdrv.h index dd43458f306f..c233b866725b 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_memdrv.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_memdrv.h @@ -41,22 +41,10 @@ /* ---- Constants and Types ---------------------------------------------- */ -typedef struct { - void *arm_shared_mem_virt; - dma_addr_t arm_shared_mem_phys; - size_t arm_shared_mem_size; - - void *vc_shared_mem_virt; - dma_addr_t vc_shared_mem_phys; - size_t vc_shared_mem_size; -} VCHIQ_SHARED_MEM_INFO_T; - /* ---- Variable Externs ------------------------------------------------- */ /* ---- Function Prototypes ---------------------------------------------- */ -void vchiq_get_shared_mem_info(VCHIQ_SHARED_MEM_INFO_T *info); - VCHIQ_STATUS_T vchiq_memdrv_initialise(void); VCHIQ_STATUS_T vchiq_userdrv_create_instance( diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_pagelist.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_pagelist.h index 12c304ceb952..926c247fd9d3 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_pagelist.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_pagelist.h @@ -34,9 +34,6 @@ #ifndef VCHIQ_PAGELIST_H #define VCHIQ_PAGELIST_H -#ifndef PAGE_SIZE -#define PAGE_SIZE 4096 -#endif #define CACHE_LINE_SIZE 32 #define PAGELIST_WRITE 0 #define PAGELIST_READ 1 diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_shim.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_shim.c index 48984abc3854..8af95fc361ed 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_shim.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_shim.c @@ -556,6 +556,7 @@ EXPORT_SYMBOL(vchi_connect); int32_t vchi_disconnect(VCHI_INSTANCE_T instance_handle) { VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; + return vchiq_status_to_vchi(vchiq_shutdown(instance)); } EXPORT_SYMBOL(vchi_disconnect); @@ -733,6 +734,7 @@ int32_t vchi_service_close(const VCHI_SERVICE_HANDLE_T handle) { int32_t ret = -1; SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; + if (service) { VCHIQ_STATUS_T status = vchiq_close_service(service->handle); if (status == VCHIQ_SUCCESS) { @@ -750,8 +752,10 @@ int32_t vchi_service_destroy(const VCHI_SERVICE_HANDLE_T handle) { int32_t ret = -1; SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; + if (service) { VCHIQ_STATUS_T status = vchiq_remove_service(service->handle); + if (status == VCHIQ_SUCCESS) { service_free(service); service = NULL; @@ -770,6 +774,7 @@ int32_t vchi_service_set_option(const VCHI_SERVICE_HANDLE_T handle, int32_t ret = -1; SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; VCHIQ_SERVICE_OPTION_T vchiq_option; + switch (option) { case VCHI_SERVICE_OPTION_TRACE: vchiq_option = VCHIQ_SERVICE_OPTION_TRACE; @@ -797,6 +802,7 @@ int32_t vchi_get_peer_version(const VCHI_SERVICE_HANDLE_T handle, short *peer_ve { int32_t ret = -1; SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; + if (service) { VCHIQ_STATUS_T status; @@ -808,54 +814,6 @@ int32_t vchi_get_peer_version(const VCHI_SERVICE_HANDLE_T handle, short *peer_ve } EXPORT_SYMBOL(vchi_get_peer_version); -/* ---------------------------------------------------------------------- - * read a uint32_t from buffer. - * network format is defined to be little endian - * -------------------------------------------------------------------- */ -uint32_t -vchi_readbuf_uint32(const void *_ptr) -{ - const unsigned char *ptr = _ptr; - return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); -} - -/* ---------------------------------------------------------------------- - * write a uint32_t to buffer. - * network format is defined to be little endian - * -------------------------------------------------------------------- */ -void -vchi_writebuf_uint32(void *_ptr, uint32_t value) -{ - unsigned char *ptr = _ptr; - ptr[0] = (unsigned char)((value >> 0) & 0xFF); - ptr[1] = (unsigned char)((value >> 8) & 0xFF); - ptr[2] = (unsigned char)((value >> 16) & 0xFF); - ptr[3] = (unsigned char)((value >> 24) & 0xFF); -} - -/* ---------------------------------------------------------------------- - * read a uint16_t from buffer. - * network format is defined to be little endian - * -------------------------------------------------------------------- */ -uint16_t -vchi_readbuf_uint16(const void *_ptr) -{ - const unsigned char *ptr = _ptr; - return ptr[0] | (ptr[1] << 8); -} - -/* ---------------------------------------------------------------------- - * write a uint16_t into the buffer. - * network format is defined to be little endian - * -------------------------------------------------------------------- */ -void -vchi_writebuf_uint16(void *_ptr, uint16_t value) -{ - unsigned char *ptr = _ptr; - ptr[0] = (value >> 0) & 0xFF; - ptr[1] = (value >> 8) & 0xFF; -} - /*********************************************************** * Name: vchi_service_use * @@ -869,6 +827,7 @@ vchi_writebuf_uint16(void *_ptr, uint16_t value) int32_t vchi_service_use(const VCHI_SERVICE_HANDLE_T handle) { int32_t ret = -1; + SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; if (service) ret = vchiq_status_to_vchi(vchiq_use_service(service->handle)); @@ -889,6 +848,7 @@ EXPORT_SYMBOL(vchi_service_use); int32_t vchi_service_release(const VCHI_SERVICE_HANDLE_T handle) { int32_t ret = -1; + SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; if (service) ret = vchiq_status_to_vchi( diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.c index e0ba0ed704fd..7fa0310e7b9e 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.c @@ -52,7 +52,7 @@ int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size) sema_init(&queue->push, 0); queue->storage = kzalloc(size * sizeof(VCHIQ_HEADER_T *), GFP_KERNEL); - if (queue->storage == NULL) { + if (!queue->storage) { vchiu_queue_delete(queue); return 0; } diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.h index e63964f5a18a..5a1540d349d3 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_util.h @@ -44,7 +44,6 @@ #include <linux/jiffies.h> #include <linux/delay.h> #include <linux/string.h> -#include <linux/types.h> #include <linux/interrupt.h> #include <linux/random.h> #include <linux/sched/signal.h> @@ -52,7 +51,6 @@ #include <linux/uaccess.h> #include <linux/time.h> /* for time_t */ #include <linux/slab.h> -#include <linux/vmalloc.h> #include "vchiq_if.h" diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_version.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_version.c index b6bfa21155e4..994b81798134 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_version.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_version.c @@ -33,27 +33,27 @@ #include "vchiq_build_info.h" #include <linux/broadcom/vc_debug_sym.h> -VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_hostname, "dc4-arm-01" ); -VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_version, "9245b4c35b99b3870e1f7dc598c5692b3c66a6f0 (tainted)" ); -VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_time, __TIME__ ); -VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_date, __DATE__ ); +VC_DEBUG_DECLARE_STRING_VAR(vchiq_build_hostname, "dc4-arm-01"); +VC_DEBUG_DECLARE_STRING_VAR(vchiq_build_version, "9245b4c35b99b3870e1f7dc598c5692b3c66a6f0 (tainted)"); +VC_DEBUG_DECLARE_STRING_VAR(vchiq_build_time, __TIME__); +VC_DEBUG_DECLARE_STRING_VAR(vchiq_build_date, __DATE__); -const char *vchiq_get_build_hostname( void ) +const char *vchiq_get_build_hostname(void) { return vchiq_build_hostname; } -const char *vchiq_get_build_version( void ) +const char *vchiq_get_build_version(void) { return vchiq_build_version; } -const char *vchiq_get_build_date( void ) +const char *vchiq_get_build_date(void) { return vchiq_build_date; } -const char *vchiq_get_build_time( void ) +const char *vchiq_get_build_time(void) { return vchiq_build_time; } |