1 1.1 jmcneill /** 2 1.18 skrll * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. 3 1.1 jmcneill * Copyright (c) 2010-2012 Broadcom. All rights reserved. 4 1.1 jmcneill * 5 1.1 jmcneill * Redistribution and use in source and binary forms, with or without 6 1.1 jmcneill * modification, are permitted provided that the following conditions 7 1.1 jmcneill * are met: 8 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright 9 1.1 jmcneill * notice, this list of conditions, and the following disclaimer, 10 1.1 jmcneill * without modification. 11 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright 12 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the 13 1.1 jmcneill * documentation and/or other materials provided with the distribution. 14 1.1 jmcneill * 3. The names of the above-listed copyright holders may not be used 15 1.1 jmcneill * to endorse or promote products derived from this software without 16 1.1 jmcneill * specific prior written permission. 17 1.1 jmcneill * 18 1.1 jmcneill * ALTERNATIVELY, this software may be distributed under the terms of the 19 1.1 jmcneill * GNU General Public License ("GPL") version 2, as published by the Free 20 1.1 jmcneill * Software Foundation. 21 1.1 jmcneill * 22 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 23 1.1 jmcneill * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 24 1.1 jmcneill * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 1.1 jmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 26 1.1 jmcneill * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 27 1.1 jmcneill * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 1.1 jmcneill * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 29 1.1 jmcneill * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 1.1 jmcneill * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 31 1.1 jmcneill * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 32 1.1 jmcneill * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 1.1 jmcneill */ 34 1.1 jmcneill 35 1.1 jmcneill #include <sys/cdefs.h> 36 1.1 jmcneill #include <sys/systm.h> 37 1.1 jmcneill #include <sys/device.h> 38 1.1 jmcneill #include <sys/file.h> 39 1.1 jmcneill #include <sys/filedesc.h> 40 1.14 nat #include <sys/kmem.h> 41 1.1 jmcneill 42 1.1 jmcneill #include "vchiq_core.h" 43 1.1 jmcneill #include "vchiq_ioctl.h" 44 1.1 jmcneill #include "vchiq_arm.h" 45 1.18 skrll #include "vchiq_debugfs.h" 46 1.1 jmcneill 47 1.1 jmcneill #define DEVICE_NAME "vchiq" 48 1.1 jmcneill 49 1.1 jmcneill /* Override the default prefix, which would be vchiq_arm (from the filename) */ 50 1.1 jmcneill #undef MODULE_PARAM_PREFIX 51 1.1 jmcneill #define MODULE_PARAM_PREFIX DEVICE_NAME "." 52 1.1 jmcneill 53 1.1 jmcneill #define VCHIQ_MINOR 0 54 1.1 jmcneill 55 1.1 jmcneill /* Some per-instance constants */ 56 1.18 skrll #define MAX_COMPLETIONS 128 57 1.1 jmcneill #define MAX_SERVICES 64 58 1.1 jmcneill #define MAX_ELEMENTS 8 59 1.18 skrll #define MSG_QUEUE_SIZE 128 60 1.1 jmcneill 61 1.1 jmcneill #define KEEPALIVE_VER 1 62 1.1 jmcneill #define KEEPALIVE_VER_MIN KEEPALIVE_VER 63 1.1 jmcneill 64 1.3 skrll MALLOC_DEFINE(M_VCHIQ, "vchiq_cdev", "VideoCore cdev memory"); 65 1.1 jmcneill 66 1.1 jmcneill /* Run time control of log level, based on KERN_XXX level. */ 67 1.1 jmcneill int vchiq_arm_log_level = VCHIQ_LOG_DEFAULT; 68 1.1 jmcneill int vchiq_susp_log_level = VCHIQ_LOG_ERROR; 69 1.1 jmcneill 70 1.1 jmcneill #define SUSPEND_TIMER_TIMEOUT_MS 100 71 1.1 jmcneill #define SUSPEND_RETRY_TIMER_TIMEOUT_MS 1000 72 1.1 jmcneill 73 1.1 jmcneill #define VC_SUSPEND_NUM_OFFSET 3 /* number of values before idle which are -ve */ 74 1.1 jmcneill static const char *const suspend_state_names[] = { 75 1.1 jmcneill "VC_SUSPEND_FORCE_CANCELED", 76 1.1 jmcneill "VC_SUSPEND_REJECTED", 77 1.1 jmcneill "VC_SUSPEND_FAILED", 78 1.1 jmcneill "VC_SUSPEND_IDLE", 79 1.1 jmcneill "VC_SUSPEND_REQUESTED", 80 1.1 jmcneill "VC_SUSPEND_IN_PROGRESS", 81 1.1 jmcneill "VC_SUSPEND_SUSPENDED" 82 1.1 jmcneill }; 83 1.1 jmcneill #define VC_RESUME_NUM_OFFSET 1 /* number of values before idle which are -ve */ 84 1.1 jmcneill static const char *const resume_state_names[] = { 85 1.1 jmcneill "VC_RESUME_FAILED", 86 1.1 jmcneill "VC_RESUME_IDLE", 87 1.1 jmcneill "VC_RESUME_REQUESTED", 88 1.1 jmcneill "VC_RESUME_IN_PROGRESS", 89 1.1 jmcneill "VC_RESUME_RESUMED" 90 1.1 jmcneill }; 91 1.1 jmcneill /* The number of times we allow force suspend to timeout before actually 92 1.1 jmcneill ** _forcing_ suspend. This is to cater for SW which fails to release vchiq 93 1.1 jmcneill ** correctly - we don't want to prevent ARM suspend indefinitely in this case. 94 1.1 jmcneill */ 95 1.1 jmcneill #define FORCE_SUSPEND_FAIL_MAX 8 96 1.1 jmcneill 97 1.1 jmcneill /* The time in ms allowed for videocore to go idle when force suspend has been 98 1.1 jmcneill * requested */ 99 1.1 jmcneill #define FORCE_SUSPEND_TIMEOUT_MS 200 100 1.1 jmcneill 101 1.1 jmcneill 102 1.1 jmcneill static void suspend_timer_callback(unsigned long context); 103 1.1 jmcneill 104 1.1 jmcneill 105 1.1 jmcneill typedef struct user_service_struct { 106 1.1 jmcneill VCHIQ_SERVICE_T *service; 107 1.1 jmcneill void *userdata; 108 1.1 jmcneill VCHIQ_INSTANCE_T instance; 109 1.18 skrll char is_vchi; 110 1.18 skrll char dequeue_pending; 111 1.18 skrll char close_pending; 112 1.1 jmcneill int message_available_pos; 113 1.1 jmcneill int msg_insert; 114 1.1 jmcneill int msg_remove; 115 1.1 jmcneill struct semaphore insert_event; 116 1.1 jmcneill struct semaphore remove_event; 117 1.18 skrll struct semaphore close_event; 118 1.1 jmcneill VCHIQ_HEADER_T * msg_queue[MSG_QUEUE_SIZE]; 119 1.1 jmcneill } USER_SERVICE_T; 120 1.1 jmcneill 121 1.1 jmcneill struct bulk_waiter_node { 122 1.1 jmcneill struct bulk_waiter bulk_waiter; 123 1.16 skrll int pid; 124 1.1 jmcneill struct list_head list; 125 1.1 jmcneill }; 126 1.1 jmcneill 127 1.1 jmcneill struct vchiq_instance_struct { 128 1.1 jmcneill VCHIQ_STATE_T *state; 129 1.1 jmcneill VCHIQ_COMPLETION_DATA_T completions[MAX_COMPLETIONS]; 130 1.1 jmcneill int completion_insert; 131 1.1 jmcneill int completion_remove; 132 1.1 jmcneill struct semaphore insert_event; 133 1.1 jmcneill struct semaphore remove_event; 134 1.1 jmcneill struct mutex completion_mutex; 135 1.1 jmcneill 136 1.1 jmcneill int connected; 137 1.1 jmcneill int closing; 138 1.16 skrll int pid; 139 1.1 jmcneill int mark; 140 1.18 skrll int use_close_delivered; 141 1.18 skrll int trace; 142 1.1 jmcneill 143 1.1 jmcneill struct list_head bulk_waiter_list; 144 1.1 jmcneill struct mutex bulk_waiter_list_mutex; 145 1.1 jmcneill 146 1.18 skrll VCHIQ_DEBUGFS_NODE_T debugfs_node; 147 1.1 jmcneill }; 148 1.1 jmcneill 149 1.1 jmcneill typedef struct dump_context_struct { 150 1.1 jmcneill char __user *buf; 151 1.1 jmcneill size_t actual; 152 1.1 jmcneill size_t space; 153 1.1 jmcneill loff_t offset; 154 1.1 jmcneill } DUMP_CONTEXT_T; 155 1.1 jmcneill 156 1.1 jmcneill VCHIQ_STATE_T g_state; 157 1.1 jmcneill static DEFINE_SPINLOCK(msg_queue_spinlock); 158 1.1 jmcneill 159 1.1 jmcneill static const char *const ioctl_names[] = { 160 1.1 jmcneill "CONNECT", 161 1.1 jmcneill "SHUTDOWN", 162 1.1 jmcneill "CREATE_SERVICE", 163 1.1 jmcneill "REMOVE_SERVICE", 164 1.1 jmcneill "QUEUE_MESSAGE", 165 1.1 jmcneill "QUEUE_BULK_TRANSMIT", 166 1.1 jmcneill "QUEUE_BULK_RECEIVE", 167 1.1 jmcneill "AWAIT_COMPLETION", 168 1.1 jmcneill "DEQUEUE_MESSAGE", 169 1.1 jmcneill "GET_CLIENT_ID", 170 1.1 jmcneill "GET_CONFIG", 171 1.1 jmcneill "CLOSE_SERVICE", 172 1.1 jmcneill "USE_SERVICE", 173 1.1 jmcneill "RELEASE_SERVICE", 174 1.1 jmcneill "SET_SERVICE_OPTION", 175 1.18 skrll "DUMP_PHYS_MEM", 176 1.18 skrll "LIB_VERSION", 177 1.18 skrll "CLOSE_DELIVERED" 178 1.1 jmcneill }; 179 1.1 jmcneill 180 1.1 jmcneill vchiq_static_assert((sizeof(ioctl_names)/sizeof(ioctl_names[0])) == 181 1.1 jmcneill (VCHIQ_IOC_MAX + 1)); 182 1.1 jmcneill 183 1.1 jmcneill static dev_type_open(vchiq_open); 184 1.1 jmcneill 185 1.1 jmcneill struct cdevsw vchiq_cdevsw = { 186 1.1 jmcneill .d_open = vchiq_open, 187 1.1 jmcneill .d_close = noclose, 188 1.1 jmcneill .d_read = noread, 189 1.1 jmcneill .d_write = nowrite, 190 1.1 jmcneill .d_ioctl = noioctl, 191 1.1 jmcneill .d_stop = nostop, 192 1.1 jmcneill .d_tty = notty, 193 1.1 jmcneill .d_poll = nopoll, 194 1.1 jmcneill .d_mmap = nommap, 195 1.1 jmcneill .d_kqfilter = nokqfilter, 196 1.13 dholland .d_discard = nodiscard, 197 1.9 dholland .d_flag = D_OTHER | D_MPSAFE 198 1.1 jmcneill }; 199 1.1 jmcneill 200 1.1 jmcneill extern struct cfdriver vchiq_cd; 201 1.1 jmcneill 202 1.1 jmcneill static int vchiq_ioctl(struct file *, u_long, void *); 203 1.1 jmcneill static int vchiq_close(struct file *); 204 1.14 nat static int vchiq_read(struct file *, off_t *, struct uio *, kauth_cred_t, int); 205 1.1 jmcneill 206 1.1 jmcneill static const struct fileops vchiq_fileops = { 207 1.19 christos .fo_name = "vchiq", 208 1.14 nat .fo_read = vchiq_read, 209 1.1 jmcneill .fo_write = fbadop_write, 210 1.1 jmcneill .fo_ioctl = vchiq_ioctl, 211 1.1 jmcneill .fo_fcntl = fnullop_fcntl, 212 1.1 jmcneill .fo_poll = fnullop_poll, 213 1.1 jmcneill .fo_stat = fbadop_stat, 214 1.1 jmcneill .fo_close = vchiq_close, 215 1.1 jmcneill .fo_kqfilter = fnullop_kqfilter, 216 1.1 jmcneill }; 217 1.1 jmcneill 218 1.1 jmcneill #if 0 219 1.1 jmcneill static void 220 1.1 jmcneill dump_phys_mem(void *virt_addr, uint32_t num_bytes); 221 1.1 jmcneill #endif 222 1.1 jmcneill 223 1.1 jmcneill /**************************************************************************** 224 1.1 jmcneill * 225 1.1 jmcneill * add_completion 226 1.1 jmcneill * 227 1.1 jmcneill ***************************************************************************/ 228 1.1 jmcneill 229 1.1 jmcneill static VCHIQ_STATUS_T 230 1.1 jmcneill add_completion(VCHIQ_INSTANCE_T instance, VCHIQ_REASON_T reason, 231 1.1 jmcneill VCHIQ_HEADER_T *header, USER_SERVICE_T *user_service, 232 1.1 jmcneill void *bulk_userdata) 233 1.1 jmcneill { 234 1.1 jmcneill VCHIQ_COMPLETION_DATA_T *completion; 235 1.18 skrll int insert; 236 1.1 jmcneill DEBUG_INITIALISE(g_state.local) 237 1.1 jmcneill 238 1.18 skrll insert = instance->completion_insert; 239 1.18 skrll while ((insert - instance->completion_remove) >= MAX_COMPLETIONS) { 240 1.1 jmcneill /* Out of space - wait for the client */ 241 1.1 jmcneill DEBUG_TRACE(SERVICE_CALLBACK_LINE); 242 1.1 jmcneill vchiq_log_trace(vchiq_arm_log_level, 243 1.1 jmcneill "add_completion - completion queue full"); 244 1.1 jmcneill DEBUG_COUNT(COMPLETION_QUEUE_FULL_COUNT); 245 1.18 skrll 246 1.1 jmcneill if (down_interruptible(&instance->remove_event) != 0) { 247 1.1 jmcneill vchiq_log_info(vchiq_arm_log_level, 248 1.1 jmcneill "service_callback interrupted"); 249 1.1 jmcneill return VCHIQ_RETRY; 250 1.18 skrll } 251 1.18 skrll 252 1.18 skrll if (instance->closing) { 253 1.1 jmcneill vchiq_log_info(vchiq_arm_log_level, 254 1.1 jmcneill "service_callback closing"); 255 1.18 skrll return VCHIQ_SUCCESS; 256 1.1 jmcneill } 257 1.1 jmcneill DEBUG_TRACE(SERVICE_CALLBACK_LINE); 258 1.1 jmcneill } 259 1.1 jmcneill 260 1.18 skrll completion = &instance->completions[insert & (MAX_COMPLETIONS - 1)]; 261 1.1 jmcneill 262 1.1 jmcneill completion->header = header; 263 1.1 jmcneill completion->reason = reason; 264 1.1 jmcneill /* N.B. service_userdata is updated while processing AWAIT_COMPLETION */ 265 1.1 jmcneill completion->service_userdata = user_service->service; 266 1.1 jmcneill completion->bulk_userdata = bulk_userdata; 267 1.1 jmcneill 268 1.18 skrll if (reason == VCHIQ_SERVICE_CLOSED) { 269 1.1 jmcneill /* Take an extra reference, to be held until 270 1.1 jmcneill this CLOSED notification is delivered. */ 271 1.1 jmcneill lock_service(user_service->service); 272 1.18 skrll if (instance->use_close_delivered) 273 1.18 skrll user_service->close_pending = 1; 274 1.18 skrll } 275 1.1 jmcneill 276 1.1 jmcneill /* A write barrier is needed here to ensure that the entire completion 277 1.1 jmcneill record is written out before the insert point. */ 278 1.1 jmcneill wmb(); 279 1.1 jmcneill 280 1.1 jmcneill if (reason == VCHIQ_MESSAGE_AVAILABLE) 281 1.18 skrll user_service->message_available_pos = insert; 282 1.18 skrll 283 1.18 skrll instance->completion_insert = ++insert; 284 1.1 jmcneill 285 1.1 jmcneill up(&instance->insert_event); 286 1.1 jmcneill 287 1.1 jmcneill return VCHIQ_SUCCESS; 288 1.1 jmcneill } 289 1.1 jmcneill 290 1.1 jmcneill /**************************************************************************** 291 1.1 jmcneill * 292 1.1 jmcneill * service_callback 293 1.1 jmcneill * 294 1.1 jmcneill ***************************************************************************/ 295 1.1 jmcneill 296 1.1 jmcneill static VCHIQ_STATUS_T 297 1.1 jmcneill service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, 298 1.1 jmcneill VCHIQ_SERVICE_HANDLE_T handle, void *bulk_userdata) 299 1.1 jmcneill { 300 1.1 jmcneill /* How do we ensure the callback goes to the right client? 301 1.1 jmcneill ** The service_user data points to a USER_SERVICE_T record containing 302 1.1 jmcneill ** the original callback and the user state structure, which contains a 303 1.1 jmcneill ** circular buffer for completion records. 304 1.1 jmcneill */ 305 1.1 jmcneill USER_SERVICE_T *user_service; 306 1.1 jmcneill VCHIQ_SERVICE_T *service; 307 1.1 jmcneill VCHIQ_INSTANCE_T instance; 308 1.18 skrll int skip_completion = 0; 309 1.1 jmcneill DEBUG_INITIALISE(g_state.local) 310 1.1 jmcneill 311 1.1 jmcneill DEBUG_TRACE(SERVICE_CALLBACK_LINE); 312 1.1 jmcneill 313 1.1 jmcneill service = handle_to_service(handle); 314 1.1 jmcneill BUG_ON(!service); 315 1.1 jmcneill user_service = (USER_SERVICE_T *)service->base.userdata; 316 1.1 jmcneill instance = user_service->instance; 317 1.1 jmcneill 318 1.1 jmcneill if (!instance || instance->closing) 319 1.1 jmcneill return VCHIQ_SUCCESS; 320 1.1 jmcneill 321 1.1 jmcneill vchiq_log_trace(vchiq_arm_log_level, 322 1.18 skrll "service_callback - service %lx(%d,%p), reason %d, header %lx, " 323 1.1 jmcneill "instance %lx, bulk_userdata %lx", 324 1.1 jmcneill (unsigned long)user_service, 325 1.18 skrll service->localport, user_service->userdata, 326 1.1 jmcneill reason, (unsigned long)header, 327 1.1 jmcneill (unsigned long)instance, (unsigned long)bulk_userdata); 328 1.1 jmcneill 329 1.1 jmcneill if (header && user_service->is_vchi) { 330 1.1 jmcneill spin_lock(&msg_queue_spinlock); 331 1.1 jmcneill while (user_service->msg_insert == 332 1.1 jmcneill (user_service->msg_remove + MSG_QUEUE_SIZE)) { 333 1.1 jmcneill spin_unlock(&msg_queue_spinlock); 334 1.1 jmcneill DEBUG_TRACE(SERVICE_CALLBACK_LINE); 335 1.1 jmcneill DEBUG_COUNT(MSG_QUEUE_FULL_COUNT); 336 1.1 jmcneill vchiq_log_trace(vchiq_arm_log_level, 337 1.1 jmcneill "service_callback - msg queue full"); 338 1.1 jmcneill /* If there is no MESSAGE_AVAILABLE in the completion 339 1.1 jmcneill ** queue, add one 340 1.1 jmcneill */ 341 1.1 jmcneill if ((user_service->message_available_pos - 342 1.1 jmcneill instance->completion_remove) < 0) { 343 1.1 jmcneill VCHIQ_STATUS_T status; 344 1.1 jmcneill vchiq_log_info(vchiq_arm_log_level, 345 1.1 jmcneill "Inserting extra MESSAGE_AVAILABLE"); 346 1.1 jmcneill DEBUG_TRACE(SERVICE_CALLBACK_LINE); 347 1.1 jmcneill status = add_completion(instance, reason, 348 1.1 jmcneill NULL, user_service, bulk_userdata); 349 1.1 jmcneill if (status != VCHIQ_SUCCESS) { 350 1.1 jmcneill DEBUG_TRACE(SERVICE_CALLBACK_LINE); 351 1.1 jmcneill return status; 352 1.1 jmcneill } 353 1.1 jmcneill } 354 1.1 jmcneill 355 1.1 jmcneill DEBUG_TRACE(SERVICE_CALLBACK_LINE); 356 1.1 jmcneill if (down_interruptible(&user_service->remove_event) 357 1.1 jmcneill != 0) { 358 1.1 jmcneill vchiq_log_info(vchiq_arm_log_level, 359 1.1 jmcneill "service_callback interrupted"); 360 1.1 jmcneill DEBUG_TRACE(SERVICE_CALLBACK_LINE); 361 1.1 jmcneill return VCHIQ_RETRY; 362 1.1 jmcneill } else if (instance->closing) { 363 1.1 jmcneill vchiq_log_info(vchiq_arm_log_level, 364 1.1 jmcneill "service_callback closing"); 365 1.1 jmcneill DEBUG_TRACE(SERVICE_CALLBACK_LINE); 366 1.1 jmcneill return VCHIQ_ERROR; 367 1.1 jmcneill } 368 1.1 jmcneill DEBUG_TRACE(SERVICE_CALLBACK_LINE); 369 1.1 jmcneill spin_lock(&msg_queue_spinlock); 370 1.1 jmcneill } 371 1.1 jmcneill 372 1.1 jmcneill user_service->msg_queue[user_service->msg_insert & 373 1.1 jmcneill (MSG_QUEUE_SIZE - 1)] = header; 374 1.1 jmcneill user_service->msg_insert++; 375 1.1 jmcneill 376 1.1 jmcneill /* If there is a thread waiting in DEQUEUE_MESSAGE, or if 377 1.1 jmcneill ** there is a MESSAGE_AVAILABLE in the completion queue then 378 1.1 jmcneill ** bypass the completion queue. 379 1.1 jmcneill */ 380 1.1 jmcneill if (((user_service->message_available_pos - 381 1.1 jmcneill instance->completion_remove) >= 0) || 382 1.1 jmcneill user_service->dequeue_pending) { 383 1.1 jmcneill user_service->dequeue_pending = 0; 384 1.18 skrll skip_completion = 1; 385 1.1 jmcneill } 386 1.1 jmcneill 387 1.18 skrll spin_unlock(&msg_queue_spinlock); 388 1.18 skrll 389 1.18 skrll up(&user_service->insert_event); 390 1.18 skrll 391 1.1 jmcneill header = NULL; 392 1.1 jmcneill } 393 1.18 skrll 394 1.18 skrll if (skip_completion) { 395 1.18 skrll DEBUG_TRACE(SERVICE_CALLBACK_LINE); 396 1.18 skrll return VCHIQ_SUCCESS; 397 1.18 skrll } 398 1.18 skrll 399 1.1 jmcneill DEBUG_TRACE(SERVICE_CALLBACK_LINE); 400 1.1 jmcneill 401 1.1 jmcneill return add_completion(instance, reason, header, user_service, 402 1.1 jmcneill bulk_userdata); 403 1.1 jmcneill } 404 1.1 jmcneill 405 1.1 jmcneill /**************************************************************************** 406 1.1 jmcneill * 407 1.11 skrll * user_service_free 408 1.11 skrll * 409 1.11 skrll ***************************************************************************/ 410 1.11 skrll static void 411 1.11 skrll user_service_free(void *userdata) 412 1.11 skrll { 413 1.11 skrll USER_SERVICE_T *user_service = userdata; 414 1.18 skrll 415 1.11 skrll _sema_destroy(&user_service->insert_event); 416 1.11 skrll _sema_destroy(&user_service->remove_event); 417 1.11 skrll 418 1.11 skrll kfree(user_service); 419 1.11 skrll } 420 1.11 skrll 421 1.11 skrll /**************************************************************************** 422 1.11 skrll * 423 1.18 skrll * close_delivered 424 1.18 skrll * 425 1.18 skrll ***************************************************************************/ 426 1.18 skrll static void close_delivered(USER_SERVICE_T *user_service) 427 1.18 skrll { 428 1.18 skrll vchiq_log_info(vchiq_arm_log_level, 429 1.18 skrll "close_delivered(handle=%x)", 430 1.18 skrll user_service->service->handle); 431 1.18 skrll 432 1.18 skrll if (user_service->close_pending) { 433 1.18 skrll /* Allow the underlying service to be culled */ 434 1.18 skrll unlock_service(user_service->service); 435 1.18 skrll 436 1.18 skrll /* Wake the user-thread blocked in close_ or remove_service */ 437 1.18 skrll up(&user_service->close_event); 438 1.18 skrll 439 1.18 skrll user_service->close_pending = 0; 440 1.18 skrll } 441 1.18 skrll } 442 1.18 skrll 443 1.18 skrll /**************************************************************************** 444 1.18 skrll * 445 1.1 jmcneill * vchiq_ioctl 446 1.1 jmcneill * 447 1.1 jmcneill ***************************************************************************/ 448 1.1 jmcneill 449 1.1 jmcneill static int 450 1.1 jmcneill vchiq_ioctl(struct file *fp, u_long cmd, void *arg) 451 1.1 jmcneill { 452 1.18 skrll VCHIQ_INSTANCE_T instance = fp->f_data; 453 1.1 jmcneill VCHIQ_STATUS_T status = VCHIQ_SUCCESS; 454 1.1 jmcneill VCHIQ_SERVICE_T *service = NULL; 455 1.1 jmcneill int ret = 0; 456 1.1 jmcneill int i, rc; 457 1.1 jmcneill DEBUG_INITIALISE(g_state.local) 458 1.1 jmcneill 459 1.1 jmcneill /* XXXBSD: HACK! */ 460 1.1 jmcneill #define _IOC_NR(x) ((x) & 0xff) 461 1.1 jmcneill #define _IOC_TYPE(x) IOCGROUP(x) 462 1.1 jmcneill 463 1.1 jmcneill vchiq_log_trace(vchiq_arm_log_level, 464 1.21 mlelstv "vchiq_ioctl - instance %p, cmd %s, arg %p", 465 1.21 mlelstv instance, 466 1.1 jmcneill ((_IOC_TYPE(cmd) == VCHIQ_IOC_MAGIC) && 467 1.1 jmcneill (_IOC_NR(cmd) <= VCHIQ_IOC_MAX)) ? 468 1.1 jmcneill ioctl_names[_IOC_NR(cmd)] : "<invalid>", arg); 469 1.1 jmcneill 470 1.1 jmcneill switch (cmd) { 471 1.1 jmcneill case VCHIQ_IOC_SHUTDOWN: 472 1.1 jmcneill if (!instance->connected) 473 1.1 jmcneill break; 474 1.1 jmcneill 475 1.1 jmcneill /* Remove all services */ 476 1.1 jmcneill i = 0; 477 1.1 jmcneill while ((service = next_service_by_instance(instance->state, 478 1.1 jmcneill instance, &i)) != NULL) { 479 1.1 jmcneill status = vchiq_remove_service(service->handle); 480 1.1 jmcneill unlock_service(service); 481 1.1 jmcneill if (status != VCHIQ_SUCCESS) 482 1.1 jmcneill break; 483 1.1 jmcneill } 484 1.1 jmcneill service = NULL; 485 1.1 jmcneill 486 1.1 jmcneill if (status == VCHIQ_SUCCESS) { 487 1.1 jmcneill /* Wake the completion thread and ask it to exit */ 488 1.1 jmcneill instance->closing = 1; 489 1.1 jmcneill up(&instance->insert_event); 490 1.1 jmcneill } 491 1.1 jmcneill 492 1.1 jmcneill break; 493 1.1 jmcneill 494 1.1 jmcneill case VCHIQ_IOC_CONNECT: 495 1.1 jmcneill if (instance->connected) { 496 1.1 jmcneill ret = -EINVAL; 497 1.1 jmcneill break; 498 1.1 jmcneill } 499 1.1 jmcneill rc = lmutex_lock_interruptible(&instance->state->mutex); 500 1.1 jmcneill if (rc != 0) { 501 1.1 jmcneill vchiq_log_error(vchiq_arm_log_level, 502 1.1 jmcneill "vchiq: connect: could not lock mutex for " 503 1.1 jmcneill "state %d: %d", 504 1.1 jmcneill instance->state->id, rc); 505 1.1 jmcneill ret = -EINTR; 506 1.1 jmcneill break; 507 1.1 jmcneill } 508 1.1 jmcneill status = vchiq_connect_internal(instance->state, instance); 509 1.1 jmcneill lmutex_unlock(&instance->state->mutex); 510 1.1 jmcneill 511 1.1 jmcneill if (status == VCHIQ_SUCCESS) 512 1.1 jmcneill instance->connected = 1; 513 1.1 jmcneill else 514 1.1 jmcneill vchiq_log_error(vchiq_arm_log_level, 515 1.1 jmcneill "vchiq: could not connect: %d", status); 516 1.1 jmcneill break; 517 1.1 jmcneill 518 1.1 jmcneill case VCHIQ_IOC_CREATE_SERVICE: { 519 1.1 jmcneill VCHIQ_CREATE_SERVICE_T *pargs = arg; 520 1.18 skrll VCHIQ_CREATE_SERVICE_T args = *pargs; 521 1.1 jmcneill USER_SERVICE_T *user_service = NULL; 522 1.1 jmcneill void *userdata; 523 1.1 jmcneill int srvstate; 524 1.1 jmcneill 525 1.18 skrll /* XXXNH kmalloc */ 526 1.11 skrll user_service = kzalloc(sizeof(USER_SERVICE_T), GFP_KERNEL); 527 1.1 jmcneill if (!user_service) { 528 1.1 jmcneill ret = -ENOMEM; 529 1.1 jmcneill break; 530 1.1 jmcneill } 531 1.1 jmcneill 532 1.18 skrll if (args.is_open) { 533 1.1 jmcneill if (!instance->connected) { 534 1.1 jmcneill ret = -ENOTCONN; 535 1.1 jmcneill kfree(user_service); 536 1.1 jmcneill break; 537 1.1 jmcneill } 538 1.1 jmcneill srvstate = VCHIQ_SRVSTATE_OPENING; 539 1.1 jmcneill } else { 540 1.1 jmcneill srvstate = 541 1.1 jmcneill instance->connected ? 542 1.1 jmcneill VCHIQ_SRVSTATE_LISTENING : 543 1.1 jmcneill VCHIQ_SRVSTATE_HIDDEN; 544 1.1 jmcneill } 545 1.1 jmcneill 546 1.18 skrll userdata = args.params.userdata; 547 1.1 jmcneill pargs->params.callback = service_callback; 548 1.1 jmcneill pargs->params.userdata = user_service; 549 1.18 skrll args.params.callback = service_callback; 550 1.18 skrll args.params.userdata = user_service; 551 1.1 jmcneill service = vchiq_add_service_internal( 552 1.1 jmcneill instance->state, 553 1.1 jmcneill &pargs->params, srvstate, 554 1.11 skrll instance, user_service_free); 555 1.1 jmcneill 556 1.1 jmcneill if (service != NULL) { 557 1.1 jmcneill user_service->service = service; 558 1.1 jmcneill user_service->userdata = userdata; 559 1.1 jmcneill user_service->instance = instance; 560 1.18 skrll user_service->is_vchi = (args.is_vchi != 0); 561 1.1 jmcneill user_service->dequeue_pending = 0; 562 1.18 skrll user_service->close_pending = 0; 563 1.1 jmcneill user_service->message_available_pos = 564 1.1 jmcneill instance->completion_remove - 1; 565 1.1 jmcneill user_service->msg_insert = 0; 566 1.1 jmcneill user_service->msg_remove = 0; 567 1.1 jmcneill _sema_init(&user_service->insert_event, 0); 568 1.1 jmcneill _sema_init(&user_service->remove_event, 0); 569 1.18 skrll _sema_init(&user_service->close_event, 0); 570 1.1 jmcneill 571 1.18 skrll if (args.is_open) { 572 1.1 jmcneill status = vchiq_open_service_internal 573 1.16 skrll (service, instance->pid); 574 1.1 jmcneill if (status != VCHIQ_SUCCESS) { 575 1.1 jmcneill vchiq_remove_service(service->handle); 576 1.1 jmcneill service = NULL; 577 1.1 jmcneill ret = (status == VCHIQ_RETRY) ? 578 1.1 jmcneill -EINTR : -EIO; 579 1.1 jmcneill break; 580 1.1 jmcneill } 581 1.1 jmcneill } 582 1.1 jmcneill 583 1.2 jmcneill #ifdef VCHIQ_IOCTL_DEBUG 584 1.1 jmcneill printf("%s: [CREATE SERVICE] handle = %08x\n", __func__, service->handle); 585 1.2 jmcneill #endif 586 1.1 jmcneill pargs->handle = service->handle; 587 1.1 jmcneill 588 1.1 jmcneill service = NULL; 589 1.1 jmcneill } else { 590 1.1 jmcneill ret = -EEXIST; 591 1.1 jmcneill kfree(user_service); 592 1.1 jmcneill } 593 1.1 jmcneill } break; 594 1.1 jmcneill 595 1.1 jmcneill case VCHIQ_IOC_CLOSE_SERVICE: { 596 1.1 jmcneill VCHIQ_SERVICE_HANDLE_T handle = *(VCHIQ_SERVICE_HANDLE_T *)arg; 597 1.1 jmcneill 598 1.2 jmcneill #ifdef VCHIQ_IOCTL_DEBUG 599 1.1 jmcneill printf("%s: [CLOSE SERVICE] handle = %08x\n", __func__, handle); 600 1.2 jmcneill #endif 601 1.1 jmcneill 602 1.1 jmcneill service = find_service_for_instance(instance, handle); 603 1.18 skrll if (service != NULL) { 604 1.18 skrll USER_SERVICE_T *user_service = 605 1.18 skrll (USER_SERVICE_T *)service->base.userdata; 606 1.18 skrll /* close_pending is false on first entry, and when the 607 1.18 skrll wait in vchiq_close_service has been interrupted. */ 608 1.18 skrll if (!user_service->close_pending) { 609 1.18 skrll status = vchiq_close_service(service->handle); 610 1.18 skrll if (status != VCHIQ_SUCCESS) 611 1.18 skrll break; 612 1.18 skrll } 613 1.18 skrll 614 1.18 skrll /* close_pending is true once the underlying service 615 1.18 skrll has been closed until the client library calls the 616 1.18 skrll CLOSE_DELIVERED ioctl, signalling close_event. */ 617 1.18 skrll if (user_service->close_pending && 618 1.18 skrll down_interruptible(&user_service->close_event)) 619 1.18 skrll status = VCHIQ_RETRY; 620 1.18 skrll } 621 1.1 jmcneill else 622 1.1 jmcneill ret = -EINVAL; 623 1.1 jmcneill } break; 624 1.1 jmcneill 625 1.1 jmcneill case VCHIQ_IOC_REMOVE_SERVICE: { 626 1.1 jmcneill VCHIQ_SERVICE_HANDLE_T handle = *(VCHIQ_SERVICE_HANDLE_T *)arg; 627 1.1 jmcneill 628 1.2 jmcneill #ifdef VCHIQ_IOCTL_DEBUG 629 1.1 jmcneill printf("%s: [REMOVE SERVICE] handle = %08x\n", __func__, handle); 630 1.2 jmcneill #endif 631 1.1 jmcneill 632 1.1 jmcneill service = find_service_for_instance(instance, handle); 633 1.18 skrll if (service != NULL) { 634 1.18 skrll USER_SERVICE_T *user_service = 635 1.18 skrll (USER_SERVICE_T *)service->base.userdata; 636 1.18 skrll /* close_pending is false on first entry, and when the 637 1.18 skrll wait in vchiq_close_service has been interrupted. */ 638 1.18 skrll if (!user_service->close_pending) { 639 1.18 skrll status = vchiq_remove_service(service->handle); 640 1.18 skrll if (status != VCHIQ_SUCCESS) 641 1.18 skrll break; 642 1.18 skrll } 643 1.18 skrll 644 1.18 skrll /* close_pending is true once the underlying service 645 1.18 skrll has been closed until the client library calls the 646 1.18 skrll CLOSE_DELIVERED ioctl, signalling close_event. */ 647 1.18 skrll if (user_service->close_pending && 648 1.18 skrll down_interruptible(&user_service->close_event)) 649 1.18 skrll status = VCHIQ_RETRY; 650 1.18 skrll } 651 1.1 jmcneill else 652 1.1 jmcneill ret = -EINVAL; 653 1.1 jmcneill } break; 654 1.1 jmcneill 655 1.1 jmcneill case VCHIQ_IOC_USE_SERVICE: 656 1.1 jmcneill case VCHIQ_IOC_RELEASE_SERVICE: { 657 1.1 jmcneill VCHIQ_SERVICE_HANDLE_T handle = *(VCHIQ_SERVICE_HANDLE_T *)arg; 658 1.1 jmcneill 659 1.2 jmcneill #ifdef VCHIQ_IOCTL_DEBUG 660 1.1 jmcneill printf("%s: [%s SERVICE] handle = %08x\n", __func__, 661 1.1 jmcneill cmd == VCHIQ_IOC_USE_SERVICE ? "USE" : "RELEASE", handle); 662 1.2 jmcneill #endif 663 1.1 jmcneill 664 1.1 jmcneill service = find_service_for_instance(instance, handle); 665 1.1 jmcneill if (service != NULL) { 666 1.1 jmcneill status = (cmd == VCHIQ_IOC_USE_SERVICE) ? 667 1.1 jmcneill vchiq_use_service_internal(service) : 668 1.1 jmcneill vchiq_release_service_internal(service); 669 1.1 jmcneill if (status != VCHIQ_SUCCESS) { 670 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, 671 1.1 jmcneill "%s: cmd %s returned error %d for " 672 1.7 skrll "service %c%c%c%c:%8x", 673 1.1 jmcneill __func__, 674 1.1 jmcneill (cmd == VCHIQ_IOC_USE_SERVICE) ? 675 1.1 jmcneill "VCHIQ_IOC_USE_SERVICE" : 676 1.1 jmcneill "VCHIQ_IOC_RELEASE_SERVICE", 677 1.1 jmcneill status, 678 1.1 jmcneill VCHIQ_FOURCC_AS_4CHARS( 679 1.1 jmcneill service->base.fourcc), 680 1.1 jmcneill service->client_id); 681 1.1 jmcneill ret = -EINVAL; 682 1.1 jmcneill } 683 1.1 jmcneill } else 684 1.1 jmcneill ret = -EINVAL; 685 1.1 jmcneill } break; 686 1.1 jmcneill 687 1.1 jmcneill case VCHIQ_IOC_QUEUE_MESSAGE: { 688 1.1 jmcneill VCHIQ_QUEUE_MESSAGE_T *pargs = arg; 689 1.18 skrll VCHIQ_QUEUE_MESSAGE_T args = *pargs; 690 1.1 jmcneill 691 1.2 jmcneill #ifdef VCHIQ_IOCTL_DEBUG 692 1.18 skrll printf("%s: [QUEUE MESSAGE] handle = %08x\n", __func__, args.handle); 693 1.2 jmcneill #endif 694 1.1 jmcneill 695 1.18 skrll service = find_service_for_instance(instance, args.handle); 696 1.1 jmcneill 697 1.18 skrll if ((service != NULL) && (args.count <= MAX_ELEMENTS)) { 698 1.1 jmcneill /* Copy elements into kernel space */ 699 1.1 jmcneill VCHIQ_ELEMENT_T elements[MAX_ELEMENTS]; 700 1.18 skrll if (copy_from_user(elements, args.elements, 701 1.18 skrll args.count * sizeof(VCHIQ_ELEMENT_T)) == 0) 702 1.1 jmcneill status = vchiq_queue_message 703 1.18 skrll (args.handle, 704 1.18 skrll elements, args.count); 705 1.1 jmcneill else 706 1.1 jmcneill ret = -EFAULT; 707 1.1 jmcneill } else { 708 1.1 jmcneill ret = -EINVAL; 709 1.1 jmcneill } 710 1.1 jmcneill } break; 711 1.1 jmcneill 712 1.1 jmcneill case VCHIQ_IOC_QUEUE_BULK_TRANSMIT: 713 1.1 jmcneill case VCHIQ_IOC_QUEUE_BULK_RECEIVE: { 714 1.1 jmcneill VCHIQ_QUEUE_BULK_TRANSFER_T *pargs = arg; 715 1.18 skrll VCHIQ_QUEUE_BULK_TRANSFER_T args = *pargs; 716 1.1 jmcneill struct bulk_waiter_node *waiter = NULL; 717 1.1 jmcneill VCHIQ_BULK_DIR_T dir = 718 1.1 jmcneill (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ? 719 1.1 jmcneill VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE; 720 1.1 jmcneill 721 1.18 skrll service = find_service_for_instance(instance, args.handle); 722 1.1 jmcneill if (!service) { 723 1.1 jmcneill ret = -EINVAL; 724 1.1 jmcneill break; 725 1.1 jmcneill } 726 1.1 jmcneill 727 1.18 skrll if (args.mode == VCHIQ_BULK_MODE_BLOCKING) { 728 1.1 jmcneill waiter = kzalloc(sizeof(struct bulk_waiter_node), 729 1.1 jmcneill GFP_KERNEL); 730 1.1 jmcneill if (!waiter) { 731 1.1 jmcneill ret = -ENOMEM; 732 1.1 jmcneill break; 733 1.1 jmcneill } 734 1.18 skrll args.userdata = &waiter->bulk_waiter; 735 1.18 skrll } else if (args.mode == VCHIQ_BULK_MODE_WAITING) { 736 1.1 jmcneill struct list_head *pos; 737 1.1 jmcneill lmutex_lock(&instance->bulk_waiter_list_mutex); 738 1.1 jmcneill list_for_each(pos, &instance->bulk_waiter_list) { 739 1.1 jmcneill if (list_entry(pos, struct bulk_waiter_node, 740 1.16 skrll list)->pid == current->l_proc->p_pid) { 741 1.1 jmcneill waiter = list_entry(pos, 742 1.1 jmcneill struct bulk_waiter_node, 743 1.1 jmcneill list); 744 1.1 jmcneill list_del(pos); 745 1.1 jmcneill break; 746 1.1 jmcneill } 747 1.1 jmcneill 748 1.1 jmcneill } 749 1.1 jmcneill lmutex_unlock(&instance->bulk_waiter_list_mutex); 750 1.1 jmcneill if (!waiter) { 751 1.1 jmcneill vchiq_log_error(vchiq_arm_log_level, 752 1.16 skrll "no bulk_waiter found for pid %d", 753 1.16 skrll current->l_proc->p_pid); 754 1.1 jmcneill ret = -ESRCH; 755 1.1 jmcneill break; 756 1.1 jmcneill } 757 1.1 jmcneill vchiq_log_info(vchiq_arm_log_level, 758 1.21 mlelstv "found bulk_waiter %p for pid %d", 759 1.21 mlelstv waiter, current->l_proc->p_pid); 760 1.18 skrll args.userdata = &waiter->bulk_waiter; 761 1.1 jmcneill } 762 1.1 jmcneill status = vchiq_bulk_transfer 763 1.18 skrll (args.handle, 764 1.1 jmcneill VCHI_MEM_HANDLE_INVALID, 765 1.18 skrll args.data, args.size, 766 1.18 skrll args.userdata, args.mode, 767 1.1 jmcneill dir); 768 1.1 jmcneill if (!waiter) 769 1.1 jmcneill break; 770 1.1 jmcneill if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) || 771 1.1 jmcneill !waiter->bulk_waiter.bulk) { 772 1.1 jmcneill if (waiter->bulk_waiter.bulk) { 773 1.1 jmcneill /* Cancel the signal when the transfer 774 1.1 jmcneill ** completes. */ 775 1.1 jmcneill spin_lock(&bulk_waiter_spinlock); 776 1.1 jmcneill waiter->bulk_waiter.bulk->userdata = NULL; 777 1.1 jmcneill spin_unlock(&bulk_waiter_spinlock); 778 1.1 jmcneill } 779 1.5 skrll _sema_destroy(&waiter->bulk_waiter.event); 780 1.1 jmcneill kfree(waiter); 781 1.1 jmcneill } else { 782 1.1 jmcneill const VCHIQ_BULK_MODE_T mode_waiting = 783 1.1 jmcneill VCHIQ_BULK_MODE_WAITING; 784 1.16 skrll waiter->pid = current->l_proc->p_pid; 785 1.1 jmcneill lmutex_lock(&instance->bulk_waiter_list_mutex); 786 1.1 jmcneill list_add(&waiter->list, &instance->bulk_waiter_list); 787 1.1 jmcneill lmutex_unlock(&instance->bulk_waiter_list_mutex); 788 1.1 jmcneill vchiq_log_info(vchiq_arm_log_level, 789 1.21 mlelstv "saved bulk_waiter %p for pid %d", 790 1.21 mlelstv waiter, current->l_proc->p_pid); 791 1.1 jmcneill 792 1.1 jmcneill pargs->mode = mode_waiting; 793 1.1 jmcneill } 794 1.1 jmcneill } break; 795 1.1 jmcneill 796 1.1 jmcneill case VCHIQ_IOC_AWAIT_COMPLETION: { 797 1.1 jmcneill VCHIQ_AWAIT_COMPLETION_T *pargs = arg; 798 1.18 skrll VCHIQ_AWAIT_COMPLETION_T args = *pargs; 799 1.15 skrll int count = 0; 800 1.1 jmcneill 801 1.1 jmcneill DEBUG_TRACE(AWAIT_COMPLETION_LINE); 802 1.1 jmcneill if (!instance->connected) { 803 1.1 jmcneill ret = -ENOTCONN; 804 1.1 jmcneill break; 805 1.1 jmcneill } 806 1.1 jmcneill 807 1.1 jmcneill lmutex_lock(&instance->completion_mutex); 808 1.1 jmcneill 809 1.1 jmcneill DEBUG_TRACE(AWAIT_COMPLETION_LINE); 810 1.1 jmcneill while ((instance->completion_remove == 811 1.1 jmcneill instance->completion_insert) 812 1.1 jmcneill && !instance->closing) { 813 1.1 jmcneill DEBUG_TRACE(AWAIT_COMPLETION_LINE); 814 1.1 jmcneill lmutex_unlock(&instance->completion_mutex); 815 1.1 jmcneill rc = down_interruptible(&instance->insert_event); 816 1.1 jmcneill lmutex_lock(&instance->completion_mutex); 817 1.1 jmcneill if (rc != 0) { 818 1.1 jmcneill DEBUG_TRACE(AWAIT_COMPLETION_LINE); 819 1.1 jmcneill vchiq_log_info(vchiq_arm_log_level, 820 1.1 jmcneill "AWAIT_COMPLETION interrupted"); 821 1.1 jmcneill ret = -EINTR; 822 1.1 jmcneill break; 823 1.1 jmcneill } 824 1.1 jmcneill } 825 1.1 jmcneill DEBUG_TRACE(AWAIT_COMPLETION_LINE); 826 1.1 jmcneill 827 1.18 skrll if (ret == 0) { 828 1.18 skrll int msgbufcount = args.msgbufcount; 829 1.18 skrll int remove; 830 1.1 jmcneill 831 1.18 skrll remove = instance->completion_remove; 832 1.12 skrll 833 1.18 skrll for (count = 0; count < args.count; count++) { 834 1.1 jmcneill VCHIQ_COMPLETION_DATA_T *completion; 835 1.1 jmcneill VCHIQ_SERVICE_T *service1; 836 1.1 jmcneill USER_SERVICE_T *user_service; 837 1.1 jmcneill VCHIQ_HEADER_T *header; 838 1.18 skrll 839 1.18 skrll if (remove == instance->completion_insert) 840 1.1 jmcneill break; 841 1.18 skrll 842 1.1 jmcneill completion = &instance->completions[ 843 1.18 skrll remove & (MAX_COMPLETIONS - 1)]; 844 1.18 skrll 845 1.18 skrll /* A read memory barrier is needed to prevent 846 1.18 skrll ** the prefetch of a stale completion record 847 1.18 skrll */ 848 1.18 skrll rmb(); 849 1.18 skrll 850 1.1 jmcneill 851 1.1 jmcneill service1 = completion->service_userdata; 852 1.1 jmcneill user_service = service1->base.userdata; 853 1.1 jmcneill completion->service_userdata = 854 1.1 jmcneill user_service->userdata; 855 1.1 jmcneill 856 1.1 jmcneill header = completion->header; 857 1.1 jmcneill if (header) { 858 1.1 jmcneill void __user *msgbuf; 859 1.1 jmcneill int msglen; 860 1.1 jmcneill 861 1.1 jmcneill msglen = header->size + 862 1.1 jmcneill sizeof(VCHIQ_HEADER_T); 863 1.1 jmcneill /* This must be a VCHIQ-style service */ 864 1.18 skrll if (args.msgbufsize < msglen) { 865 1.1 jmcneill vchiq_log_error( 866 1.1 jmcneill vchiq_arm_log_level, 867 1.21 mlelstv "header %p: msgbufsize" 868 1.1 jmcneill " %x < msglen %x", 869 1.21 mlelstv header, 870 1.18 skrll args.msgbufsize, 871 1.1 jmcneill msglen); 872 1.1 jmcneill WARN(1, "invalid message " 873 1.1 jmcneill "size\n"); 874 1.1 jmcneill if (count == 0) 875 1.1 jmcneill ret = -EMSGSIZE; 876 1.1 jmcneill break; 877 1.1 jmcneill } 878 1.1 jmcneill if (msgbufcount <= 0) 879 1.1 jmcneill /* Stall here for lack of a 880 1.1 jmcneill ** buffer for the message. */ 881 1.1 jmcneill break; 882 1.1 jmcneill /* Get the pointer from user space */ 883 1.1 jmcneill msgbufcount--; 884 1.1 jmcneill if (copy_from_user(&msgbuf, 885 1.1 jmcneill (const void __user *) 886 1.18 skrll &args.msgbufs[msgbufcount], 887 1.1 jmcneill sizeof(msgbuf)) != 0) { 888 1.1 jmcneill if (count == 0) 889 1.1 jmcneill ret = -EFAULT; 890 1.1 jmcneill break; 891 1.1 jmcneill } 892 1.1 jmcneill 893 1.1 jmcneill /* Copy the message to user space */ 894 1.1 jmcneill if (copy_to_user(msgbuf, header, 895 1.1 jmcneill msglen) != 0) { 896 1.1 jmcneill if (count == 0) 897 1.1 jmcneill ret = -EFAULT; 898 1.1 jmcneill break; 899 1.1 jmcneill } 900 1.1 jmcneill 901 1.1 jmcneill /* Now it has been copied, the message 902 1.1 jmcneill ** can be released. */ 903 1.1 jmcneill vchiq_release_message(service1->handle, 904 1.1 jmcneill header); 905 1.1 jmcneill 906 1.1 jmcneill /* The completion must point to the 907 1.1 jmcneill ** msgbuf. */ 908 1.1 jmcneill completion->header = msgbuf; 909 1.1 jmcneill } 910 1.1 jmcneill 911 1.18 skrll if ((completion->reason == 912 1.18 skrll VCHIQ_SERVICE_CLOSED) && 913 1.18 skrll !instance->use_close_delivered) 914 1.1 jmcneill unlock_service(service1); 915 1.1 jmcneill 916 1.1 jmcneill if (copy_to_user((void __user *)( 917 1.18 skrll (size_t)args.buf + 918 1.1 jmcneill count * sizeof(VCHIQ_COMPLETION_DATA_T)), 919 1.1 jmcneill completion, 920 1.1 jmcneill sizeof(VCHIQ_COMPLETION_DATA_T)) != 0) { 921 1.12 skrll if (count == 0) 922 1.1 jmcneill ret = -EFAULT; 923 1.1 jmcneill break; 924 1.1 jmcneill } 925 1.1 jmcneill 926 1.18 skrll /* Ensure that the above copy has completed 927 1.18 skrll ** before advancing the remove pointer. */ 928 1.18 skrll mb(); 929 1.18 skrll 930 1.18 skrll instance->completion_remove = ++remove; 931 1.1 jmcneill } 932 1.1 jmcneill 933 1.1 jmcneill pargs->msgbufcount = msgbufcount; 934 1.1 jmcneill pargs->count = count; 935 1.1 jmcneill } 936 1.15 skrll if (count != 0) 937 1.1 jmcneill up(&instance->remove_event); 938 1.18 skrll 939 1.1 jmcneill lmutex_unlock(&instance->completion_mutex); 940 1.1 jmcneill DEBUG_TRACE(AWAIT_COMPLETION_LINE); 941 1.1 jmcneill } break; 942 1.1 jmcneill 943 1.1 jmcneill case VCHIQ_IOC_DEQUEUE_MESSAGE: { 944 1.1 jmcneill VCHIQ_DEQUEUE_MESSAGE_T *pargs = arg; 945 1.18 skrll VCHIQ_DEQUEUE_MESSAGE_T args = *pargs; 946 1.1 jmcneill USER_SERVICE_T *user_service; 947 1.1 jmcneill VCHIQ_HEADER_T *header; 948 1.1 jmcneill 949 1.1 jmcneill DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); 950 1.18 skrll service = find_service_for_instance(instance, args.handle); 951 1.1 jmcneill if (!service) { 952 1.1 jmcneill ret = -EINVAL; 953 1.1 jmcneill break; 954 1.1 jmcneill } 955 1.1 jmcneill user_service = (USER_SERVICE_T *)service->base.userdata; 956 1.1 jmcneill if (user_service->is_vchi == 0) { 957 1.1 jmcneill ret = -EINVAL; 958 1.1 jmcneill break; 959 1.1 jmcneill } 960 1.1 jmcneill 961 1.1 jmcneill spin_lock(&msg_queue_spinlock); 962 1.1 jmcneill if (user_service->msg_remove == user_service->msg_insert) { 963 1.18 skrll if (!args.blocking) { 964 1.1 jmcneill spin_unlock(&msg_queue_spinlock); 965 1.1 jmcneill DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); 966 1.1 jmcneill ret = -EWOULDBLOCK; 967 1.1 jmcneill break; 968 1.1 jmcneill } 969 1.1 jmcneill user_service->dequeue_pending = 1; 970 1.1 jmcneill do { 971 1.1 jmcneill spin_unlock(&msg_queue_spinlock); 972 1.1 jmcneill DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); 973 1.1 jmcneill if (down_interruptible( 974 1.1 jmcneill &user_service->insert_event) != 0) { 975 1.1 jmcneill vchiq_log_info(vchiq_arm_log_level, 976 1.1 jmcneill "DEQUEUE_MESSAGE interrupted"); 977 1.1 jmcneill ret = -EINTR; 978 1.1 jmcneill break; 979 1.1 jmcneill } 980 1.1 jmcneill spin_lock(&msg_queue_spinlock); 981 1.1 jmcneill } while (user_service->msg_remove == 982 1.1 jmcneill user_service->msg_insert); 983 1.1 jmcneill 984 1.1 jmcneill if (ret) 985 1.1 jmcneill break; 986 1.1 jmcneill } 987 1.1 jmcneill 988 1.1 jmcneill BUG_ON((int)(user_service->msg_insert - 989 1.1 jmcneill user_service->msg_remove) < 0); 990 1.1 jmcneill 991 1.1 jmcneill header = user_service->msg_queue[user_service->msg_remove & 992 1.1 jmcneill (MSG_QUEUE_SIZE - 1)]; 993 1.1 jmcneill user_service->msg_remove++; 994 1.1 jmcneill spin_unlock(&msg_queue_spinlock); 995 1.1 jmcneill 996 1.1 jmcneill up(&user_service->remove_event); 997 1.1 jmcneill if (header == NULL) 998 1.1 jmcneill ret = -ENOTCONN; 999 1.18 skrll else if (header->size <= args.bufsize) { 1000 1.1 jmcneill /* Copy to user space if msgbuf is not NULL */ 1001 1.18 skrll if ((args.buf == NULL) || 1002 1.18 skrll (copy_to_user((void __user *)args.buf, 1003 1.1 jmcneill header->data, 1004 1.1 jmcneill header->size) == 0)) { 1005 1.1 jmcneill pargs->bufsize = header->size; 1006 1.1 jmcneill vchiq_release_message( 1007 1.1 jmcneill service->handle, 1008 1.1 jmcneill header); 1009 1.1 jmcneill } else 1010 1.1 jmcneill ret = -EFAULT; 1011 1.1 jmcneill } else { 1012 1.1 jmcneill vchiq_log_error(vchiq_arm_log_level, 1013 1.21 mlelstv "header %p: bufsize %x < size %x", 1014 1.21 mlelstv header, args.bufsize, 1015 1.1 jmcneill header->size); 1016 1.1 jmcneill WARN(1, "invalid size\n"); 1017 1.1 jmcneill ret = -EMSGSIZE; 1018 1.1 jmcneill } 1019 1.1 jmcneill DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); 1020 1.1 jmcneill } break; 1021 1.1 jmcneill 1022 1.1 jmcneill case VCHIQ_IOC_GET_CLIENT_ID: { 1023 1.18 skrll VCHIQ_SERVICE_HANDLE_T handle = *(VCHIQ_SERVICE_HANDLE_T *)arg; 1024 1.1 jmcneill 1025 1.1 jmcneill ret = vchiq_get_client_id(handle); 1026 1.1 jmcneill } break; 1027 1.1 jmcneill 1028 1.1 jmcneill case VCHIQ_IOC_GET_CONFIG: { 1029 1.1 jmcneill VCHIQ_GET_CONFIG_T *pargs = arg; 1030 1.18 skrll VCHIQ_GET_CONFIG_T args = *pargs; 1031 1.1 jmcneill VCHIQ_CONFIG_T config; 1032 1.1 jmcneill 1033 1.18 skrll if (args.config_size > sizeof(config)) { 1034 1.1 jmcneill ret = -EINVAL; 1035 1.1 jmcneill break; 1036 1.1 jmcneill } 1037 1.18 skrll status = vchiq_get_config(instance, args.config_size, &config); 1038 1.1 jmcneill if (status == VCHIQ_SUCCESS) { 1039 1.18 skrll if (copy_to_user((void __user *)args.pconfig, 1040 1.18 skrll &config, args.config_size) != 0) { 1041 1.1 jmcneill ret = -EFAULT; 1042 1.1 jmcneill break; 1043 1.1 jmcneill } 1044 1.1 jmcneill } 1045 1.1 jmcneill } break; 1046 1.1 jmcneill 1047 1.1 jmcneill case VCHIQ_IOC_SET_SERVICE_OPTION: { 1048 1.1 jmcneill VCHIQ_SET_SERVICE_OPTION_T *pargs = arg; 1049 1.18 skrll VCHIQ_SET_SERVICE_OPTION_T args = *pargs; 1050 1.1 jmcneill 1051 1.18 skrll service = find_service_for_instance(instance, args.handle); 1052 1.1 jmcneill if (!service) { 1053 1.1 jmcneill ret = -EINVAL; 1054 1.1 jmcneill break; 1055 1.1 jmcneill } 1056 1.1 jmcneill 1057 1.1 jmcneill status = vchiq_set_service_option( 1058 1.18 skrll args.handle, args.option, args.value); 1059 1.1 jmcneill } break; 1060 1.1 jmcneill 1061 1.1 jmcneill case VCHIQ_IOC_DUMP_PHYS_MEM: { 1062 1.1 jmcneill #if 0 1063 1.1 jmcneill VCHIQ_DUMP_MEM_T *pargs = arg; 1064 1.1 jmcneill #endif 1065 1.1 jmcneill 1066 1.1 jmcneill printf("IMPLEMENT ME: %s:%d\n", __FILE__, __LINE__); 1067 1.1 jmcneill #if 0 1068 1.1 jmcneill dump_phys_mem(pargs->virt_addr, pargs->num_bytes); 1069 1.1 jmcneill #endif 1070 1.1 jmcneill } break; 1071 1.1 jmcneill 1072 1.18 skrll case VCHIQ_IOC_LIB_VERSION: { 1073 1.21 mlelstv unsigned int lib_version = (uintptr_t)arg; 1074 1.18 skrll 1075 1.18 skrll if (lib_version < VCHIQ_VERSION_MIN) 1076 1.18 skrll ret = -EINVAL; 1077 1.18 skrll else if (lib_version >= VCHIQ_VERSION_CLOSE_DELIVERED) 1078 1.18 skrll instance->use_close_delivered = 1; 1079 1.18 skrll } break; 1080 1.18 skrll 1081 1.18 skrll case VCHIQ_IOC_CLOSE_DELIVERED: { 1082 1.18 skrll VCHIQ_SERVICE_HANDLE_T handle = *(VCHIQ_SERVICE_HANDLE_T *)arg; 1083 1.18 skrll 1084 1.18 skrll service = find_closed_service_for_instance(instance, handle); 1085 1.18 skrll if (service != NULL) { 1086 1.18 skrll USER_SERVICE_T *user_service = 1087 1.18 skrll (USER_SERVICE_T *)service->base.userdata; 1088 1.18 skrll close_delivered(user_service); 1089 1.18 skrll } 1090 1.18 skrll else 1091 1.18 skrll ret = -EINVAL; 1092 1.18 skrll } break; 1093 1.18 skrll 1094 1.1 jmcneill default: 1095 1.1 jmcneill ret = -ENOTTY; 1096 1.1 jmcneill break; 1097 1.1 jmcneill } 1098 1.1 jmcneill 1099 1.1 jmcneill if (service) 1100 1.1 jmcneill unlock_service(service); 1101 1.1 jmcneill 1102 1.1 jmcneill if (ret == 0) { 1103 1.1 jmcneill if (status == VCHIQ_ERROR) 1104 1.1 jmcneill ret = -EIO; 1105 1.1 jmcneill else if (status == VCHIQ_RETRY) 1106 1.1 jmcneill ret = -EINTR; 1107 1.1 jmcneill } 1108 1.1 jmcneill 1109 1.1 jmcneill if ((status == VCHIQ_SUCCESS) && (ret < 0) && (ret != -EINTR) && 1110 1.1 jmcneill (ret != -EWOULDBLOCK)) 1111 1.1 jmcneill vchiq_log_info(vchiq_arm_log_level, 1112 1.1 jmcneill " ioctl instance %lx, cmd %s -> status %d, %d", 1113 1.1 jmcneill (unsigned long)instance, 1114 1.1 jmcneill (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? 1115 1.1 jmcneill ioctl_names[_IOC_NR(cmd)] : 1116 1.1 jmcneill "<invalid>", 1117 1.1 jmcneill status, ret); 1118 1.1 jmcneill else 1119 1.1 jmcneill vchiq_log_trace(vchiq_arm_log_level, 1120 1.1 jmcneill " ioctl instance %lx, cmd %s -> status %d, %d", 1121 1.1 jmcneill (unsigned long)instance, 1122 1.1 jmcneill (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? 1123 1.1 jmcneill ioctl_names[_IOC_NR(cmd)] : 1124 1.1 jmcneill "<invalid>", 1125 1.1 jmcneill status, ret); 1126 1.1 jmcneill 1127 1.1 jmcneill /* XXXBSD: report BSD-style error to userland */ 1128 1.1 jmcneill if (ret < 0) 1129 1.1 jmcneill ret = -ret; 1130 1.1 jmcneill 1131 1.1 jmcneill return ret; 1132 1.1 jmcneill } 1133 1.1 jmcneill 1134 1.1 jmcneill /**************************************************************************** 1135 1.1 jmcneill * 1136 1.1 jmcneill * vchiq_open 1137 1.1 jmcneill * 1138 1.1 jmcneill ***************************************************************************/ 1139 1.1 jmcneill 1140 1.1 jmcneill static int 1141 1.1 jmcneill vchiq_open(dev_t dev, int flags, int mode, lwp_t *l) 1142 1.1 jmcneill { 1143 1.1 jmcneill VCHIQ_INSTANCE_T instance = NULL; 1144 1.1 jmcneill struct file *fp; 1145 1.1 jmcneill int err, fd; 1146 1.1 jmcneill 1147 1.1 jmcneill vchiq_log_info(vchiq_arm_log_level, "vchiq_open"); 1148 1.1 jmcneill 1149 1.1 jmcneill /* XXXBSD: do we really need this check? */ 1150 1.1 jmcneill if (device_lookup_private(&vchiq_cd, minor(dev)) != NULL) { 1151 1.21 mlelstv VCHIQ_STATE_T *state; 1152 1.21 mlelstv int i; 1153 1.21 mlelstv 1154 1.21 mlelstv for (i=0; i<10; ++i) { 1155 1.21 mlelstv state = vchiq_get_state(); 1156 1.21 mlelstv if (state) 1157 1.21 mlelstv break; 1158 1.21 mlelstv delay(500); 1159 1.21 mlelstv } 1160 1.1 jmcneill 1161 1.1 jmcneill if (!state) { 1162 1.1 jmcneill vchiq_log_error(vchiq_arm_log_level, 1163 1.1 jmcneill "vchiq has no connection to VideoCore"); 1164 1.1 jmcneill return -ENOTCONN; 1165 1.1 jmcneill } 1166 1.1 jmcneill 1167 1.11 skrll instance = kzalloc(sizeof(*instance), GFP_KERNEL); 1168 1.1 jmcneill if (!instance) 1169 1.1 jmcneill return -ENOMEM; 1170 1.1 jmcneill 1171 1.1 jmcneill err = fd_allocfile(&fp, &fd); 1172 1.1 jmcneill if (err) { 1173 1.1 jmcneill kfree(instance); 1174 1.1 jmcneill return -err; 1175 1.1 jmcneill } 1176 1.1 jmcneill 1177 1.1 jmcneill instance->state = state; 1178 1.16 skrll instance->pid = current->l_proc->p_pid; 1179 1.1 jmcneill 1180 1.1 jmcneill #ifdef notyet 1181 1.18 skrll ret = vchiq_debugfs_add_instance(instance); 1182 1.1 jmcneill if (ret != 0) { 1183 1.1 jmcneill kfree(instance); 1184 1.1 jmcneill return ret; 1185 1.1 jmcneill } 1186 1.1 jmcneill #endif 1187 1.1 jmcneill 1188 1.1 jmcneill _sema_init(&instance->insert_event, 0); 1189 1.1 jmcneill _sema_init(&instance->remove_event, 0); 1190 1.1 jmcneill lmutex_init(&instance->completion_mutex); 1191 1.1 jmcneill lmutex_init(&instance->bulk_waiter_list_mutex); 1192 1.1 jmcneill INIT_LIST_HEAD(&instance->bulk_waiter_list); 1193 1.1 jmcneill 1194 1.4 skrll } 1195 1.1 jmcneill else { 1196 1.1 jmcneill vchiq_log_error(vchiq_arm_log_level, 1197 1.1 jmcneill "Unknown minor device"); 1198 1.1 jmcneill return -ENXIO; 1199 1.1 jmcneill } 1200 1.1 jmcneill 1201 1.1 jmcneill return fd_clone(fp, fd, flags, &vchiq_fileops, instance); 1202 1.1 jmcneill } 1203 1.1 jmcneill 1204 1.1 jmcneill /**************************************************************************** 1205 1.1 jmcneill * 1206 1.1 jmcneill * vchiq_release 1207 1.1 jmcneill * 1208 1.1 jmcneill ***************************************************************************/ 1209 1.1 jmcneill 1210 1.1 jmcneill static int 1211 1.1 jmcneill vchiq_close(struct file *fp) 1212 1.1 jmcneill { 1213 1.1 jmcneill int ret = 0; 1214 1.1 jmcneill if (1) { 1215 1.18 skrll VCHIQ_INSTANCE_T instance = fp->f_data; 1216 1.1 jmcneill VCHIQ_STATE_T *state = vchiq_get_state(); 1217 1.1 jmcneill VCHIQ_SERVICE_T *service; 1218 1.1 jmcneill int i; 1219 1.1 jmcneill 1220 1.1 jmcneill vchiq_log_info(vchiq_arm_log_level, 1221 1.1 jmcneill "vchiq_release: instance=%lx", 1222 1.1 jmcneill (unsigned long)instance); 1223 1.1 jmcneill 1224 1.1 jmcneill if (!state) { 1225 1.1 jmcneill ret = -EPERM; 1226 1.1 jmcneill goto out; 1227 1.1 jmcneill } 1228 1.1 jmcneill 1229 1.1 jmcneill /* Ensure videocore is awake to allow termination. */ 1230 1.1 jmcneill vchiq_use_internal(instance->state, NULL, 1231 1.1 jmcneill USE_TYPE_VCHIQ); 1232 1.1 jmcneill 1233 1.1 jmcneill lmutex_lock(&instance->completion_mutex); 1234 1.1 jmcneill 1235 1.1 jmcneill /* Wake the completion thread and ask it to exit */ 1236 1.1 jmcneill instance->closing = 1; 1237 1.1 jmcneill up(&instance->insert_event); 1238 1.1 jmcneill 1239 1.1 jmcneill lmutex_unlock(&instance->completion_mutex); 1240 1.1 jmcneill 1241 1.1 jmcneill /* Wake the slot handler if the completion queue is full. */ 1242 1.1 jmcneill up(&instance->remove_event); 1243 1.1 jmcneill 1244 1.1 jmcneill /* Mark all services for termination... */ 1245 1.1 jmcneill i = 0; 1246 1.1 jmcneill while ((service = next_service_by_instance(state, instance, 1247 1.1 jmcneill &i)) != NULL) { 1248 1.1 jmcneill USER_SERVICE_T *user_service = service->base.userdata; 1249 1.1 jmcneill 1250 1.1 jmcneill /* Wake the slot handler if the msg queue is full. */ 1251 1.1 jmcneill up(&user_service->remove_event); 1252 1.1 jmcneill 1253 1.1 jmcneill vchiq_terminate_service_internal(service); 1254 1.1 jmcneill unlock_service(service); 1255 1.1 jmcneill } 1256 1.1 jmcneill 1257 1.1 jmcneill /* ...and wait for them to die */ 1258 1.1 jmcneill i = 0; 1259 1.1 jmcneill while ((service = next_service_by_instance(state, instance, &i)) 1260 1.1 jmcneill != NULL) { 1261 1.1 jmcneill USER_SERVICE_T *user_service = service->base.userdata; 1262 1.1 jmcneill 1263 1.1 jmcneill down(&service->remove_event); 1264 1.1 jmcneill 1265 1.1 jmcneill BUG_ON(service->srvstate != VCHIQ_SRVSTATE_FREE); 1266 1.1 jmcneill 1267 1.1 jmcneill spin_lock(&msg_queue_spinlock); 1268 1.1 jmcneill 1269 1.1 jmcneill while (user_service->msg_remove != 1270 1.1 jmcneill user_service->msg_insert) { 1271 1.1 jmcneill VCHIQ_HEADER_T *header = user_service-> 1272 1.1 jmcneill msg_queue[user_service->msg_remove & 1273 1.1 jmcneill (MSG_QUEUE_SIZE - 1)]; 1274 1.1 jmcneill user_service->msg_remove++; 1275 1.1 jmcneill spin_unlock(&msg_queue_spinlock); 1276 1.1 jmcneill 1277 1.1 jmcneill if (header) 1278 1.1 jmcneill vchiq_release_message( 1279 1.1 jmcneill service->handle, 1280 1.1 jmcneill header); 1281 1.1 jmcneill spin_lock(&msg_queue_spinlock); 1282 1.1 jmcneill } 1283 1.1 jmcneill 1284 1.1 jmcneill spin_unlock(&msg_queue_spinlock); 1285 1.1 jmcneill 1286 1.1 jmcneill unlock_service(service); 1287 1.1 jmcneill } 1288 1.1 jmcneill 1289 1.1 jmcneill /* Release any closed services */ 1290 1.1 jmcneill while (instance->completion_remove != 1291 1.1 jmcneill instance->completion_insert) { 1292 1.1 jmcneill VCHIQ_COMPLETION_DATA_T *completion; 1293 1.1 jmcneill VCHIQ_SERVICE_T *service1; 1294 1.1 jmcneill completion = &instance->completions[ 1295 1.1 jmcneill instance->completion_remove & 1296 1.1 jmcneill (MAX_COMPLETIONS - 1)]; 1297 1.1 jmcneill service1 = completion->service_userdata; 1298 1.1 jmcneill if (completion->reason == VCHIQ_SERVICE_CLOSED) 1299 1.18 skrll { 1300 1.18 skrll USER_SERVICE_T *user_service = 1301 1.18 skrll service->base.userdata; 1302 1.18 skrll 1303 1.18 skrll /* Wake any blocked user-thread */ 1304 1.18 skrll if (instance->use_close_delivered) 1305 1.18 skrll up(&user_service->close_event); 1306 1.1 jmcneill unlock_service(service1); 1307 1.18 skrll } 1308 1.1 jmcneill instance->completion_remove++; 1309 1.1 jmcneill } 1310 1.1 jmcneill 1311 1.1 jmcneill /* Release the PEER service count. */ 1312 1.1 jmcneill vchiq_release_internal(instance->state, NULL); 1313 1.1 jmcneill 1314 1.1 jmcneill { 1315 1.1 jmcneill struct list_head *pos, *next; 1316 1.1 jmcneill list_for_each_safe(pos, next, 1317 1.1 jmcneill &instance->bulk_waiter_list) { 1318 1.1 jmcneill struct bulk_waiter_node *waiter; 1319 1.1 jmcneill waiter = list_entry(pos, 1320 1.1 jmcneill struct bulk_waiter_node, 1321 1.1 jmcneill list); 1322 1.1 jmcneill list_del(pos); 1323 1.1 jmcneill vchiq_log_info(vchiq_arm_log_level, 1324 1.21 mlelstv "bulk_waiter - cleaned up %p " 1325 1.16 skrll "for pid %d", 1326 1.21 mlelstv waiter, waiter->pid); 1327 1.5 skrll _sema_destroy(&waiter->bulk_waiter.event); 1328 1.1 jmcneill kfree(waiter); 1329 1.1 jmcneill } 1330 1.1 jmcneill } 1331 1.1 jmcneill 1332 1.1 jmcneill } 1333 1.1 jmcneill else { 1334 1.1 jmcneill vchiq_log_error(vchiq_arm_log_level, 1335 1.1 jmcneill "Unknown minor device"); 1336 1.1 jmcneill ret = -ENXIO; 1337 1.1 jmcneill } 1338 1.1 jmcneill 1339 1.1 jmcneill out: 1340 1.1 jmcneill return ret; 1341 1.1 jmcneill } 1342 1.1 jmcneill 1343 1.1 jmcneill /**************************************************************************** 1344 1.1 jmcneill * 1345 1.1 jmcneill * vchiq_dump 1346 1.1 jmcneill * 1347 1.1 jmcneill ***************************************************************************/ 1348 1.1 jmcneill 1349 1.1 jmcneill void 1350 1.1 jmcneill vchiq_dump(void *dump_context, const char *str, int len) 1351 1.1 jmcneill { 1352 1.1 jmcneill DUMP_CONTEXT_T *context = (DUMP_CONTEXT_T *)dump_context; 1353 1.1 jmcneill 1354 1.1 jmcneill if (context->actual < context->space) { 1355 1.1 jmcneill int copy_bytes; 1356 1.1 jmcneill if (context->offset > 0) { 1357 1.1 jmcneill int skip_bytes = min(len, (int)context->offset); 1358 1.1 jmcneill str += skip_bytes; 1359 1.1 jmcneill len -= skip_bytes; 1360 1.1 jmcneill context->offset -= skip_bytes; 1361 1.1 jmcneill if (context->offset > 0) 1362 1.1 jmcneill return; 1363 1.1 jmcneill } 1364 1.1 jmcneill copy_bytes = min(len, (int)(context->space - context->actual)); 1365 1.1 jmcneill if (copy_bytes == 0) 1366 1.1 jmcneill return; 1367 1.14 nat memcpy(context->buf + context->actual, str, copy_bytes); 1368 1.1 jmcneill context->actual += copy_bytes; 1369 1.1 jmcneill len -= copy_bytes; 1370 1.1 jmcneill 1371 1.1 jmcneill /* If tne terminating NUL is included in the length, then it 1372 1.1 jmcneill ** marks the end of a line and should be replaced with a 1373 1.1 jmcneill ** carriage return. */ 1374 1.1 jmcneill if ((len == 0) && (str[copy_bytes - 1] == '\0')) { 1375 1.1 jmcneill char cr = '\n'; 1376 1.14 nat memcpy(context->buf + context->actual - 1, &cr, 1); 1377 1.1 jmcneill } 1378 1.1 jmcneill } 1379 1.1 jmcneill } 1380 1.1 jmcneill 1381 1.1 jmcneill /**************************************************************************** 1382 1.1 jmcneill * 1383 1.1 jmcneill * vchiq_dump_platform_instance_state 1384 1.1 jmcneill * 1385 1.1 jmcneill ***************************************************************************/ 1386 1.1 jmcneill 1387 1.1 jmcneill void 1388 1.1 jmcneill vchiq_dump_platform_instances(void *dump_context) 1389 1.1 jmcneill { 1390 1.1 jmcneill VCHIQ_STATE_T *state = vchiq_get_state(); 1391 1.1 jmcneill char buf[80]; 1392 1.1 jmcneill int len; 1393 1.1 jmcneill int i; 1394 1.1 jmcneill 1395 1.1 jmcneill /* There is no list of instances, so instead scan all services, 1396 1.1 jmcneill marking those that have been dumped. */ 1397 1.1 jmcneill 1398 1.1 jmcneill for (i = 0; i < state->unused_service; i++) { 1399 1.1 jmcneill VCHIQ_SERVICE_T *service = state->services[i]; 1400 1.1 jmcneill VCHIQ_INSTANCE_T instance; 1401 1.1 jmcneill 1402 1.1 jmcneill if (service && (service->base.callback == service_callback)) { 1403 1.1 jmcneill instance = service->instance; 1404 1.1 jmcneill if (instance) 1405 1.1 jmcneill instance->mark = 0; 1406 1.1 jmcneill } 1407 1.1 jmcneill } 1408 1.1 jmcneill 1409 1.1 jmcneill for (i = 0; i < state->unused_service; i++) { 1410 1.1 jmcneill VCHIQ_SERVICE_T *service = state->services[i]; 1411 1.1 jmcneill VCHIQ_INSTANCE_T instance; 1412 1.1 jmcneill 1413 1.1 jmcneill if (service && (service->base.callback == service_callback)) { 1414 1.1 jmcneill instance = service->instance; 1415 1.1 jmcneill if (instance && !instance->mark) { 1416 1.1 jmcneill len = snprintf(buf, sizeof(buf), 1417 1.21 mlelstv "Instance %p: pid %d,%s completions " 1418 1.1 jmcneill "%d/%d", 1419 1.21 mlelstv instance, instance->pid, 1420 1.1 jmcneill instance->connected ? " connected, " : 1421 1.1 jmcneill "", 1422 1.1 jmcneill instance->completion_insert - 1423 1.1 jmcneill instance->completion_remove, 1424 1.1 jmcneill MAX_COMPLETIONS); 1425 1.1 jmcneill 1426 1.1 jmcneill vchiq_dump(dump_context, buf, len + 1); 1427 1.1 jmcneill 1428 1.1 jmcneill instance->mark = 1; 1429 1.1 jmcneill } 1430 1.1 jmcneill } 1431 1.1 jmcneill } 1432 1.1 jmcneill } 1433 1.1 jmcneill 1434 1.1 jmcneill /**************************************************************************** 1435 1.1 jmcneill * 1436 1.1 jmcneill * vchiq_dump_platform_service_state 1437 1.1 jmcneill * 1438 1.1 jmcneill ***************************************************************************/ 1439 1.1 jmcneill 1440 1.1 jmcneill void 1441 1.1 jmcneill vchiq_dump_platform_service_state(void *dump_context, VCHIQ_SERVICE_T *service) 1442 1.1 jmcneill { 1443 1.1 jmcneill USER_SERVICE_T *user_service = (USER_SERVICE_T *)service->base.userdata; 1444 1.1 jmcneill char buf[80]; 1445 1.1 jmcneill int len; 1446 1.1 jmcneill 1447 1.21 mlelstv len = snprintf(buf, sizeof(buf), " instance %p", 1448 1.21 mlelstv service->instance); 1449 1.1 jmcneill 1450 1.1 jmcneill if ((service->base.callback == service_callback) && 1451 1.1 jmcneill user_service->is_vchi) { 1452 1.1 jmcneill len += snprintf(buf + len, sizeof(buf) - len, 1453 1.1 jmcneill ", %d/%d messages", 1454 1.1 jmcneill user_service->msg_insert - user_service->msg_remove, 1455 1.1 jmcneill MSG_QUEUE_SIZE); 1456 1.1 jmcneill 1457 1.1 jmcneill if (user_service->dequeue_pending) 1458 1.1 jmcneill len += snprintf(buf + len, sizeof(buf) - len, 1459 1.1 jmcneill " (dequeue pending)"); 1460 1.1 jmcneill } 1461 1.1 jmcneill 1462 1.1 jmcneill vchiq_dump(dump_context, buf, len + 1); 1463 1.1 jmcneill } 1464 1.1 jmcneill 1465 1.1 jmcneill #ifdef notyet 1466 1.1 jmcneill /**************************************************************************** 1467 1.1 jmcneill * 1468 1.1 jmcneill * dump_user_mem 1469 1.1 jmcneill * 1470 1.1 jmcneill ***************************************************************************/ 1471 1.1 jmcneill 1472 1.1 jmcneill static void 1473 1.1 jmcneill dump_phys_mem(void *virt_addr, uint32_t num_bytes) 1474 1.1 jmcneill { 1475 1.1 jmcneill int rc; 1476 1.1 jmcneill uint8_t *end_virt_addr = virt_addr + num_bytes; 1477 1.1 jmcneill int num_pages; 1478 1.1 jmcneill int offset; 1479 1.1 jmcneill int end_offset; 1480 1.1 jmcneill int page_idx; 1481 1.1 jmcneill int prev_idx; 1482 1.1 jmcneill struct page *page; 1483 1.1 jmcneill struct page **pages; 1484 1.1 jmcneill uint8_t *kmapped_virt_ptr; 1485 1.1 jmcneill 1486 1.1 jmcneill /* Align virtAddr and endVirtAddr to 16 byte boundaries. */ 1487 1.1 jmcneill 1488 1.1 jmcneill virt_addr = (void *)((unsigned long)virt_addr & ~0x0fuL); 1489 1.1 jmcneill end_virt_addr = (void *)(((unsigned long)end_virt_addr + 15uL) & 1490 1.1 jmcneill ~0x0fuL); 1491 1.1 jmcneill 1492 1.1 jmcneill offset = (int)(long)virt_addr & (PAGE_SIZE - 1); 1493 1.1 jmcneill end_offset = (int)(long)end_virt_addr & (PAGE_SIZE - 1); 1494 1.1 jmcneill 1495 1.1 jmcneill num_pages = (offset + num_bytes + PAGE_SIZE - 1) / PAGE_SIZE; 1496 1.1 jmcneill 1497 1.1 jmcneill pages = kmalloc(sizeof(struct page *) * num_pages, GFP_KERNEL); 1498 1.1 jmcneill if (pages == NULL) { 1499 1.1 jmcneill vchiq_log_error(vchiq_arm_log_level, 1500 1.20 gson "Unable to allocation memory for %d pages", 1501 1.1 jmcneill num_pages); 1502 1.1 jmcneill return; 1503 1.1 jmcneill } 1504 1.1 jmcneill 1505 1.1 jmcneill down_read(¤t->mm->mmap_sem); 1506 1.1 jmcneill rc = get_user_pages(current, /* task */ 1507 1.1 jmcneill current->mm, /* mm */ 1508 1.1 jmcneill (unsigned long)virt_addr, /* start */ 1509 1.1 jmcneill num_pages, /* len */ 1510 1.1 jmcneill 0, /* write */ 1511 1.1 jmcneill 0, /* force */ 1512 1.1 jmcneill pages, /* pages (array of page pointers) */ 1513 1.1 jmcneill NULL); /* vmas */ 1514 1.1 jmcneill up_read(¤t->mm->mmap_sem); 1515 1.1 jmcneill 1516 1.1 jmcneill prev_idx = -1; 1517 1.1 jmcneill page = NULL; 1518 1.1 jmcneill 1519 1.1 jmcneill while (offset < end_offset) { 1520 1.1 jmcneill 1521 1.1 jmcneill int page_offset = offset % PAGE_SIZE; 1522 1.1 jmcneill page_idx = offset / PAGE_SIZE; 1523 1.1 jmcneill 1524 1.1 jmcneill if (page_idx != prev_idx) { 1525 1.1 jmcneill 1526 1.1 jmcneill if (page != NULL) 1527 1.1 jmcneill kunmap(page); 1528 1.1 jmcneill page = pages[page_idx]; 1529 1.1 jmcneill kmapped_virt_ptr = kmap(page); 1530 1.1 jmcneill 1531 1.1 jmcneill prev_idx = page_idx; 1532 1.1 jmcneill } 1533 1.1 jmcneill 1534 1.1 jmcneill if (vchiq_arm_log_level >= VCHIQ_LOG_TRACE) 1535 1.1 jmcneill vchiq_log_dump_mem("ph", 1536 1.1 jmcneill (uint32_t)(unsigned long)&kmapped_virt_ptr[ 1537 1.1 jmcneill page_offset], 1538 1.1 jmcneill &kmapped_virt_ptr[page_offset], 16); 1539 1.1 jmcneill 1540 1.1 jmcneill offset += 16; 1541 1.1 jmcneill } 1542 1.1 jmcneill if (page != NULL) 1543 1.1 jmcneill kunmap(page); 1544 1.1 jmcneill 1545 1.1 jmcneill for (page_idx = 0; page_idx < num_pages; page_idx++) 1546 1.1 jmcneill page_cache_release(pages[page_idx]); 1547 1.1 jmcneill 1548 1.1 jmcneill kfree(pages); 1549 1.1 jmcneill } 1550 1.14 nat #endif 1551 1.1 jmcneill 1552 1.1 jmcneill /**************************************************************************** 1553 1.1 jmcneill * 1554 1.1 jmcneill * vchiq_read 1555 1.1 jmcneill * 1556 1.1 jmcneill ***************************************************************************/ 1557 1.1 jmcneill 1558 1.14 nat static int 1559 1.14 nat vchiq_read(struct file *file, off_t *ppos, struct uio *uio, kauth_cred_t cred, 1560 1.14 nat int flags) 1561 1.1 jmcneill { 1562 1.14 nat int result; 1563 1.14 nat 1564 1.14 nat char *buf = kmem_zalloc(PAGE_SIZE, KM_SLEEP); 1565 1.14 nat 1566 1.1 jmcneill DUMP_CONTEXT_T context; 1567 1.1 jmcneill context.buf = buf; 1568 1.1 jmcneill context.actual = 0; 1569 1.14 nat context.space = PAGE_SIZE; 1570 1.1 jmcneill context.offset = *ppos; 1571 1.1 jmcneill 1572 1.1 jmcneill vchiq_dump_state(&context, &g_state); 1573 1.1 jmcneill 1574 1.1 jmcneill *ppos += context.actual; 1575 1.1 jmcneill 1576 1.14 nat result = uiomove(buf, context.actual, uio); 1577 1.14 nat kmem_free(buf, PAGE_SIZE); 1578 1.18 skrll 1579 1.14 nat return result; 1580 1.1 jmcneill } 1581 1.1 jmcneill 1582 1.1 jmcneill VCHIQ_STATE_T * 1583 1.1 jmcneill vchiq_get_state(void) 1584 1.1 jmcneill { 1585 1.1 jmcneill 1586 1.1 jmcneill if (g_state.remote == NULL) 1587 1.1 jmcneill printk(KERN_ERR "%s: g_state.remote == NULL\n", __func__); 1588 1.1 jmcneill else if (g_state.remote->initialised != 1) 1589 1.1 jmcneill printk(KERN_NOTICE "%s: g_state.remote->initialised != 1 (%d)\n", 1590 1.1 jmcneill __func__, g_state.remote->initialised); 1591 1.1 jmcneill 1592 1.1 jmcneill return ((g_state.remote != NULL) && 1593 1.1 jmcneill (g_state.remote->initialised == 1)) ? &g_state : NULL; 1594 1.1 jmcneill } 1595 1.1 jmcneill 1596 1.1 jmcneill /* 1597 1.1 jmcneill * Autosuspend related functionality 1598 1.1 jmcneill */ 1599 1.1 jmcneill 1600 1.1 jmcneill int 1601 1.1 jmcneill vchiq_videocore_wanted(VCHIQ_STATE_T *state) 1602 1.1 jmcneill { 1603 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 1604 1.1 jmcneill if (!arm_state) 1605 1.1 jmcneill /* autosuspend not supported - always return wanted */ 1606 1.1 jmcneill return 1; 1607 1.1 jmcneill else if (arm_state->blocked_count) 1608 1.1 jmcneill return 1; 1609 1.1 jmcneill else if (!arm_state->videocore_use_count) 1610 1.1 jmcneill /* usage count zero - check for override unless we're forcing */ 1611 1.1 jmcneill if (arm_state->resume_blocked) 1612 1.1 jmcneill return 0; 1613 1.1 jmcneill else 1614 1.1 jmcneill return vchiq_platform_videocore_wanted(state); 1615 1.1 jmcneill else 1616 1.1 jmcneill /* non-zero usage count - videocore still required */ 1617 1.1 jmcneill return 1; 1618 1.1 jmcneill } 1619 1.1 jmcneill 1620 1.1 jmcneill static VCHIQ_STATUS_T 1621 1.1 jmcneill vchiq_keepalive_vchiq_callback(VCHIQ_REASON_T reason, 1622 1.1 jmcneill VCHIQ_HEADER_T *header, 1623 1.1 jmcneill VCHIQ_SERVICE_HANDLE_T service_user, 1624 1.1 jmcneill void *bulk_user) 1625 1.1 jmcneill { 1626 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, 1627 1.1 jmcneill "%s callback reason %d", __func__, reason); 1628 1.1 jmcneill return 0; 1629 1.1 jmcneill } 1630 1.1 jmcneill 1631 1.1 jmcneill static int 1632 1.1 jmcneill vchiq_keepalive_thread_func(void *v) 1633 1.1 jmcneill { 1634 1.1 jmcneill VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; 1635 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 1636 1.1 jmcneill 1637 1.1 jmcneill VCHIQ_STATUS_T status; 1638 1.1 jmcneill VCHIQ_INSTANCE_T instance; 1639 1.1 jmcneill VCHIQ_SERVICE_HANDLE_T ka_handle; 1640 1.1 jmcneill 1641 1.1 jmcneill VCHIQ_SERVICE_PARAMS_T params = { 1642 1.1 jmcneill .fourcc = VCHIQ_MAKE_FOURCC('K', 'E', 'E', 'P'), 1643 1.1 jmcneill .callback = vchiq_keepalive_vchiq_callback, 1644 1.1 jmcneill .version = KEEPALIVE_VER, 1645 1.1 jmcneill .version_min = KEEPALIVE_VER_MIN 1646 1.1 jmcneill }; 1647 1.1 jmcneill 1648 1.1 jmcneill status = vchiq_initialise(&instance); 1649 1.1 jmcneill if (status != VCHIQ_SUCCESS) { 1650 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, 1651 1.1 jmcneill "%s vchiq_initialise failed %d", __func__, status); 1652 1.1 jmcneill goto exit; 1653 1.1 jmcneill } 1654 1.1 jmcneill 1655 1.1 jmcneill status = vchiq_connect(instance); 1656 1.1 jmcneill if (status != VCHIQ_SUCCESS) { 1657 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, 1658 1.1 jmcneill "%s vchiq_connect failed %d", __func__, status); 1659 1.1 jmcneill goto shutdown; 1660 1.1 jmcneill } 1661 1.1 jmcneill 1662 1.1 jmcneill status = vchiq_add_service(instance, ¶ms, &ka_handle); 1663 1.1 jmcneill if (status != VCHIQ_SUCCESS) { 1664 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, 1665 1.1 jmcneill "%s vchiq_open_service failed %d", __func__, status); 1666 1.1 jmcneill goto shutdown; 1667 1.1 jmcneill } 1668 1.1 jmcneill 1669 1.1 jmcneill while (1) { 1670 1.1 jmcneill long rc = 0, uc = 0; 1671 1.1 jmcneill if (wait_for_completion_interruptible(&arm_state->ka_evt) 1672 1.1 jmcneill != 0) { 1673 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, 1674 1.1 jmcneill "%s interrupted", __func__); 1675 1.1 jmcneill flush_signals(current); 1676 1.1 jmcneill continue; 1677 1.1 jmcneill } 1678 1.1 jmcneill 1679 1.1 jmcneill /* read and clear counters. Do release_count then use_count to 1680 1.1 jmcneill * prevent getting more releases than uses */ 1681 1.1 jmcneill rc = atomic_xchg(&arm_state->ka_release_count, 0); 1682 1.1 jmcneill uc = atomic_xchg(&arm_state->ka_use_count, 0); 1683 1.1 jmcneill 1684 1.1 jmcneill /* Call use/release service the requisite number of times. 1685 1.1 jmcneill * Process use before release so use counts don't go negative */ 1686 1.1 jmcneill while (uc--) { 1687 1.1 jmcneill atomic_inc(&arm_state->ka_use_ack_count); 1688 1.1 jmcneill status = vchiq_use_service(ka_handle); 1689 1.1 jmcneill if (status != VCHIQ_SUCCESS) { 1690 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, 1691 1.1 jmcneill "%s vchiq_use_service error %d", 1692 1.1 jmcneill __func__, status); 1693 1.1 jmcneill } 1694 1.1 jmcneill } 1695 1.1 jmcneill while (rc--) { 1696 1.1 jmcneill status = vchiq_release_service(ka_handle); 1697 1.1 jmcneill if (status != VCHIQ_SUCCESS) { 1698 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, 1699 1.1 jmcneill "%s vchiq_release_service error %d", 1700 1.1 jmcneill __func__, status); 1701 1.1 jmcneill } 1702 1.1 jmcneill } 1703 1.1 jmcneill } 1704 1.1 jmcneill 1705 1.1 jmcneill shutdown: 1706 1.1 jmcneill vchiq_shutdown(instance); 1707 1.1 jmcneill exit: 1708 1.1 jmcneill return 0; 1709 1.1 jmcneill } 1710 1.1 jmcneill 1711 1.18 skrll 1712 1.18 skrll 1713 1.1 jmcneill VCHIQ_STATUS_T 1714 1.1 jmcneill vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state) 1715 1.1 jmcneill { 1716 1.1 jmcneill VCHIQ_STATUS_T status = VCHIQ_SUCCESS; 1717 1.1 jmcneill 1718 1.1 jmcneill if (arm_state) { 1719 1.1 jmcneill rwlock_init(&arm_state->susp_res_lock); 1720 1.1 jmcneill 1721 1.1 jmcneill init_completion(&arm_state->ka_evt); 1722 1.1 jmcneill atomic_set(&arm_state->ka_use_count, 0); 1723 1.1 jmcneill atomic_set(&arm_state->ka_use_ack_count, 0); 1724 1.1 jmcneill atomic_set(&arm_state->ka_release_count, 0); 1725 1.1 jmcneill 1726 1.1 jmcneill init_completion(&arm_state->vc_suspend_complete); 1727 1.1 jmcneill 1728 1.1 jmcneill init_completion(&arm_state->vc_resume_complete); 1729 1.1 jmcneill /* Initialise to 'done' state. We only want to block on resume 1730 1.1 jmcneill * completion while videocore is suspended. */ 1731 1.1 jmcneill set_resume_state(arm_state, VC_RESUME_RESUMED); 1732 1.1 jmcneill 1733 1.1 jmcneill init_completion(&arm_state->resume_blocker); 1734 1.1 jmcneill /* Initialise to 'done' state. We only want to block on this 1735 1.1 jmcneill * completion while resume is blocked */ 1736 1.1 jmcneill complete_all(&arm_state->resume_blocker); 1737 1.1 jmcneill 1738 1.1 jmcneill init_completion(&arm_state->blocked_blocker); 1739 1.1 jmcneill /* Initialise to 'done' state. We only want to block on this 1740 1.1 jmcneill * completion while things are waiting on the resume blocker */ 1741 1.1 jmcneill complete_all(&arm_state->blocked_blocker); 1742 1.1 jmcneill 1743 1.1 jmcneill arm_state->suspend_timer_timeout = SUSPEND_TIMER_TIMEOUT_MS; 1744 1.1 jmcneill arm_state->suspend_timer_running = 0; 1745 1.1 jmcneill init_timer(&arm_state->suspend_timer); 1746 1.1 jmcneill arm_state->suspend_timer.data = (unsigned long)(state); 1747 1.1 jmcneill arm_state->suspend_timer.function = suspend_timer_callback; 1748 1.1 jmcneill 1749 1.1 jmcneill arm_state->first_connect = 0; 1750 1.1 jmcneill 1751 1.1 jmcneill } 1752 1.1 jmcneill return status; 1753 1.1 jmcneill } 1754 1.1 jmcneill 1755 1.1 jmcneill /* 1756 1.1 jmcneill ** Functions to modify the state variables; 1757 1.1 jmcneill ** set_suspend_state 1758 1.1 jmcneill ** set_resume_state 1759 1.1 jmcneill ** 1760 1.1 jmcneill ** There are more state variables than we might like, so ensure they remain in 1761 1.1 jmcneill ** step. Suspend and resume state are maintained separately, since most of 1762 1.1 jmcneill ** these state machines can operate independently. However, there are a few 1763 1.1 jmcneill ** states where state transitions in one state machine cause a reset to the 1764 1.1 jmcneill ** other state machine. In addition, there are some completion events which 1765 1.1 jmcneill ** need to occur on state machine reset and end-state(s), so these are also 1766 1.1 jmcneill ** dealt with in these functions. 1767 1.1 jmcneill ** 1768 1.1 jmcneill ** In all states we set the state variable according to the input, but in some 1769 1.1 jmcneill ** cases we perform additional steps outlined below; 1770 1.1 jmcneill ** 1771 1.1 jmcneill ** VC_SUSPEND_IDLE - Initialise the suspend completion at the same time. 1772 1.1 jmcneill ** The suspend completion is completed after any suspend 1773 1.1 jmcneill ** attempt. When we reset the state machine we also reset 1774 1.1 jmcneill ** the completion. This reset occurs when videocore is 1775 1.1 jmcneill ** resumed, and also if we initiate suspend after a suspend 1776 1.1 jmcneill ** failure. 1777 1.1 jmcneill ** 1778 1.1 jmcneill ** VC_SUSPEND_IN_PROGRESS - This state is considered the point of no return for 1779 1.1 jmcneill ** suspend - ie from this point on we must try to suspend 1780 1.1 jmcneill ** before resuming can occur. We therefore also reset the 1781 1.1 jmcneill ** resume state machine to VC_RESUME_IDLE in this state. 1782 1.1 jmcneill ** 1783 1.1 jmcneill ** VC_SUSPEND_SUSPENDED - Suspend has completed successfully. Also call 1784 1.1 jmcneill ** complete_all on the suspend completion to notify 1785 1.1 jmcneill ** anything waiting for suspend to happen. 1786 1.1 jmcneill ** 1787 1.1 jmcneill ** VC_SUSPEND_REJECTED - Videocore rejected suspend. Videocore will also 1788 1.1 jmcneill ** initiate resume, so no need to alter resume state. 1789 1.1 jmcneill ** We call complete_all on the suspend completion to notify 1790 1.1 jmcneill ** of suspend rejection. 1791 1.1 jmcneill ** 1792 1.1 jmcneill ** VC_SUSPEND_FAILED - We failed to initiate videocore suspend. We notify the 1793 1.1 jmcneill ** suspend completion and reset the resume state machine. 1794 1.1 jmcneill ** 1795 1.1 jmcneill ** VC_RESUME_IDLE - Initialise the resume completion at the same time. The 1796 1.1 jmcneill ** resume completion is in it's 'done' state whenever 1797 1.1 jmcneill ** videcore is running. Therfore, the VC_RESUME_IDLE state 1798 1.1 jmcneill ** implies that videocore is suspended. 1799 1.1 jmcneill ** Hence, any thread which needs to wait until videocore is 1800 1.1 jmcneill ** running can wait on this completion - it will only block 1801 1.1 jmcneill ** if videocore is suspended. 1802 1.1 jmcneill ** 1803 1.1 jmcneill ** VC_RESUME_RESUMED - Resume has completed successfully. Videocore is running. 1804 1.1 jmcneill ** Call complete_all on the resume completion to unblock 1805 1.1 jmcneill ** any threads waiting for resume. Also reset the suspend 1806 1.1 jmcneill ** state machine to it's idle state. 1807 1.1 jmcneill ** 1808 1.1 jmcneill ** VC_RESUME_FAILED - Currently unused - no mechanism to fail resume exists. 1809 1.1 jmcneill */ 1810 1.1 jmcneill 1811 1.1 jmcneill inline void 1812 1.1 jmcneill set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, 1813 1.1 jmcneill enum vc_suspend_status new_state) 1814 1.1 jmcneill { 1815 1.1 jmcneill /* set the state in all cases */ 1816 1.1 jmcneill arm_state->vc_suspend_state = new_state; 1817 1.1 jmcneill 1818 1.1 jmcneill /* state specific additional actions */ 1819 1.1 jmcneill switch (new_state) { 1820 1.1 jmcneill case VC_SUSPEND_FORCE_CANCELED: 1821 1.1 jmcneill complete_all(&arm_state->vc_suspend_complete); 1822 1.1 jmcneill break; 1823 1.1 jmcneill case VC_SUSPEND_REJECTED: 1824 1.1 jmcneill complete_all(&arm_state->vc_suspend_complete); 1825 1.1 jmcneill break; 1826 1.1 jmcneill case VC_SUSPEND_FAILED: 1827 1.1 jmcneill complete_all(&arm_state->vc_suspend_complete); 1828 1.1 jmcneill arm_state->vc_resume_state = VC_RESUME_RESUMED; 1829 1.1 jmcneill complete_all(&arm_state->vc_resume_complete); 1830 1.1 jmcneill break; 1831 1.1 jmcneill case VC_SUSPEND_IDLE: 1832 1.18 skrll reinit_completion(&arm_state->vc_suspend_complete); 1833 1.1 jmcneill break; 1834 1.1 jmcneill case VC_SUSPEND_REQUESTED: 1835 1.1 jmcneill break; 1836 1.1 jmcneill case VC_SUSPEND_IN_PROGRESS: 1837 1.1 jmcneill set_resume_state(arm_state, VC_RESUME_IDLE); 1838 1.1 jmcneill break; 1839 1.1 jmcneill case VC_SUSPEND_SUSPENDED: 1840 1.1 jmcneill complete_all(&arm_state->vc_suspend_complete); 1841 1.1 jmcneill break; 1842 1.1 jmcneill default: 1843 1.1 jmcneill BUG(); 1844 1.1 jmcneill break; 1845 1.1 jmcneill } 1846 1.1 jmcneill } 1847 1.1 jmcneill 1848 1.1 jmcneill inline void 1849 1.1 jmcneill set_resume_state(VCHIQ_ARM_STATE_T *arm_state, 1850 1.1 jmcneill enum vc_resume_status new_state) 1851 1.1 jmcneill { 1852 1.1 jmcneill /* set the state in all cases */ 1853 1.1 jmcneill arm_state->vc_resume_state = new_state; 1854 1.1 jmcneill 1855 1.1 jmcneill /* state specific additional actions */ 1856 1.1 jmcneill switch (new_state) { 1857 1.1 jmcneill case VC_RESUME_FAILED: 1858 1.1 jmcneill break; 1859 1.1 jmcneill case VC_RESUME_IDLE: 1860 1.18 skrll reinit_completion(&arm_state->vc_resume_complete); 1861 1.1 jmcneill break; 1862 1.1 jmcneill case VC_RESUME_REQUESTED: 1863 1.1 jmcneill break; 1864 1.1 jmcneill case VC_RESUME_IN_PROGRESS: 1865 1.1 jmcneill break; 1866 1.1 jmcneill case VC_RESUME_RESUMED: 1867 1.1 jmcneill complete_all(&arm_state->vc_resume_complete); 1868 1.1 jmcneill set_suspend_state(arm_state, VC_SUSPEND_IDLE); 1869 1.1 jmcneill break; 1870 1.1 jmcneill default: 1871 1.1 jmcneill BUG(); 1872 1.1 jmcneill break; 1873 1.1 jmcneill } 1874 1.1 jmcneill } 1875 1.1 jmcneill 1876 1.1 jmcneill 1877 1.1 jmcneill /* should be called with the write lock held */ 1878 1.1 jmcneill inline void 1879 1.1 jmcneill start_suspend_timer(VCHIQ_ARM_STATE_T *arm_state) 1880 1.1 jmcneill { 1881 1.1 jmcneill del_timer(&arm_state->suspend_timer); 1882 1.1 jmcneill arm_state->suspend_timer.expires = jiffies + 1883 1.1 jmcneill msecs_to_jiffies(arm_state-> 1884 1.1 jmcneill suspend_timer_timeout); 1885 1.1 jmcneill add_timer(&arm_state->suspend_timer); 1886 1.1 jmcneill arm_state->suspend_timer_running = 1; 1887 1.1 jmcneill } 1888 1.1 jmcneill 1889 1.1 jmcneill /* should be called with the write lock held */ 1890 1.1 jmcneill static inline void 1891 1.1 jmcneill stop_suspend_timer(VCHIQ_ARM_STATE_T *arm_state) 1892 1.1 jmcneill { 1893 1.1 jmcneill if (arm_state->suspend_timer_running) { 1894 1.1 jmcneill del_timer(&arm_state->suspend_timer); 1895 1.1 jmcneill arm_state->suspend_timer_running = 0; 1896 1.1 jmcneill } 1897 1.1 jmcneill } 1898 1.1 jmcneill 1899 1.1 jmcneill static inline int 1900 1.1 jmcneill need_resume(VCHIQ_STATE_T *state) 1901 1.1 jmcneill { 1902 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 1903 1.1 jmcneill return (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) && 1904 1.1 jmcneill (arm_state->vc_resume_state < VC_RESUME_REQUESTED) && 1905 1.1 jmcneill vchiq_videocore_wanted(state); 1906 1.1 jmcneill } 1907 1.1 jmcneill 1908 1.1 jmcneill static int 1909 1.1 jmcneill block_resume(VCHIQ_ARM_STATE_T *arm_state) 1910 1.1 jmcneill { 1911 1.1 jmcneill int status = VCHIQ_SUCCESS; 1912 1.1 jmcneill const unsigned long timeout_val = 1913 1.1 jmcneill msecs_to_jiffies(FORCE_SUSPEND_TIMEOUT_MS); 1914 1.1 jmcneill int resume_count = 0; 1915 1.1 jmcneill 1916 1.1 jmcneill /* Allow any threads which were blocked by the last force suspend to 1917 1.1 jmcneill * complete if they haven't already. Only give this one shot; if 1918 1.1 jmcneill * blocked_count is incremented after blocked_blocker is completed 1919 1.1 jmcneill * (which only happens when blocked_count hits 0) then those threads 1920 1.1 jmcneill * will have to wait until next time around */ 1921 1.1 jmcneill if (arm_state->blocked_count) { 1922 1.18 skrll reinit_completion(&arm_state->blocked_blocker); 1923 1.1 jmcneill write_unlock_bh(&arm_state->susp_res_lock); 1924 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, "%s wait for previously " 1925 1.1 jmcneill "blocked clients", __func__); 1926 1.1 jmcneill if (wait_for_completion_interruptible_timeout( 1927 1.1 jmcneill &arm_state->blocked_blocker, timeout_val) 1928 1.1 jmcneill <= 0) { 1929 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, "%s wait for " 1930 1.1 jmcneill "previously blocked clients failed" , __func__); 1931 1.1 jmcneill status = VCHIQ_ERROR; 1932 1.1 jmcneill write_lock_bh(&arm_state->susp_res_lock); 1933 1.1 jmcneill goto out; 1934 1.1 jmcneill } 1935 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, "%s previously blocked " 1936 1.1 jmcneill "clients resumed", __func__); 1937 1.1 jmcneill write_lock_bh(&arm_state->susp_res_lock); 1938 1.1 jmcneill } 1939 1.1 jmcneill 1940 1.1 jmcneill /* We need to wait for resume to complete if it's in process */ 1941 1.1 jmcneill while (arm_state->vc_resume_state != VC_RESUME_RESUMED && 1942 1.1 jmcneill arm_state->vc_resume_state > VC_RESUME_IDLE) { 1943 1.1 jmcneill if (resume_count > 1) { 1944 1.1 jmcneill status = VCHIQ_ERROR; 1945 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, "%s waited too " 1946 1.1 jmcneill "many times for resume" , __func__); 1947 1.1 jmcneill goto out; 1948 1.1 jmcneill } 1949 1.1 jmcneill write_unlock_bh(&arm_state->susp_res_lock); 1950 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, "%s wait for resume", 1951 1.1 jmcneill __func__); 1952 1.1 jmcneill if (wait_for_completion_interruptible_timeout( 1953 1.1 jmcneill &arm_state->vc_resume_complete, timeout_val) 1954 1.1 jmcneill <= 0) { 1955 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, "%s wait for " 1956 1.1 jmcneill "resume failed (%s)", __func__, 1957 1.1 jmcneill resume_state_names[arm_state->vc_resume_state + 1958 1.1 jmcneill VC_RESUME_NUM_OFFSET]); 1959 1.1 jmcneill status = VCHIQ_ERROR; 1960 1.1 jmcneill write_lock_bh(&arm_state->susp_res_lock); 1961 1.1 jmcneill goto out; 1962 1.1 jmcneill } 1963 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, "%s resumed", __func__); 1964 1.1 jmcneill write_lock_bh(&arm_state->susp_res_lock); 1965 1.1 jmcneill resume_count++; 1966 1.1 jmcneill } 1967 1.18 skrll reinit_completion(&arm_state->resume_blocker); 1968 1.1 jmcneill arm_state->resume_blocked = 1; 1969 1.1 jmcneill 1970 1.1 jmcneill out: 1971 1.1 jmcneill return status; 1972 1.1 jmcneill } 1973 1.1 jmcneill 1974 1.1 jmcneill static inline void 1975 1.1 jmcneill unblock_resume(VCHIQ_ARM_STATE_T *arm_state) 1976 1.1 jmcneill { 1977 1.1 jmcneill complete_all(&arm_state->resume_blocker); 1978 1.1 jmcneill arm_state->resume_blocked = 0; 1979 1.1 jmcneill } 1980 1.1 jmcneill 1981 1.1 jmcneill /* Initiate suspend via slot handler. Should be called with the write lock 1982 1.1 jmcneill * held */ 1983 1.1 jmcneill VCHIQ_STATUS_T 1984 1.1 jmcneill vchiq_arm_vcsuspend(VCHIQ_STATE_T *state) 1985 1.1 jmcneill { 1986 1.1 jmcneill VCHIQ_STATUS_T status = VCHIQ_ERROR; 1987 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 1988 1.1 jmcneill 1989 1.1 jmcneill if (!arm_state) 1990 1.1 jmcneill goto out; 1991 1.1 jmcneill 1992 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); 1993 1.1 jmcneill status = VCHIQ_SUCCESS; 1994 1.1 jmcneill 1995 1.1 jmcneill 1996 1.1 jmcneill switch (arm_state->vc_suspend_state) { 1997 1.1 jmcneill case VC_SUSPEND_REQUESTED: 1998 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, "%s: suspend already " 1999 1.1 jmcneill "requested", __func__); 2000 1.1 jmcneill break; 2001 1.1 jmcneill case VC_SUSPEND_IN_PROGRESS: 2002 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, "%s: suspend already in " 2003 1.1 jmcneill "progress", __func__); 2004 1.1 jmcneill break; 2005 1.1 jmcneill 2006 1.1 jmcneill default: 2007 1.1 jmcneill /* We don't expect to be in other states, so log but continue 2008 1.1 jmcneill * anyway */ 2009 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, 2010 1.1 jmcneill "%s unexpected suspend state %s", __func__, 2011 1.1 jmcneill suspend_state_names[arm_state->vc_suspend_state + 2012 1.1 jmcneill VC_SUSPEND_NUM_OFFSET]); 2013 1.1 jmcneill /* fall through */ 2014 1.1 jmcneill case VC_SUSPEND_REJECTED: 2015 1.1 jmcneill case VC_SUSPEND_FAILED: 2016 1.1 jmcneill /* Ensure any idle state actions have been run */ 2017 1.1 jmcneill set_suspend_state(arm_state, VC_SUSPEND_IDLE); 2018 1.1 jmcneill /* fall through */ 2019 1.1 jmcneill case VC_SUSPEND_IDLE: 2020 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, 2021 1.1 jmcneill "%s: suspending", __func__); 2022 1.1 jmcneill set_suspend_state(arm_state, VC_SUSPEND_REQUESTED); 2023 1.1 jmcneill /* kick the slot handler thread to initiate suspend */ 2024 1.1 jmcneill request_poll(state, NULL, 0); 2025 1.1 jmcneill break; 2026 1.1 jmcneill } 2027 1.1 jmcneill 2028 1.1 jmcneill out: 2029 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, status); 2030 1.1 jmcneill return status; 2031 1.1 jmcneill } 2032 1.1 jmcneill 2033 1.1 jmcneill void 2034 1.1 jmcneill vchiq_platform_check_suspend(VCHIQ_STATE_T *state) 2035 1.1 jmcneill { 2036 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 2037 1.1 jmcneill int susp = 0; 2038 1.1 jmcneill 2039 1.1 jmcneill if (!arm_state) 2040 1.1 jmcneill goto out; 2041 1.1 jmcneill 2042 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); 2043 1.1 jmcneill 2044 1.1 jmcneill write_lock_bh(&arm_state->susp_res_lock); 2045 1.1 jmcneill if (arm_state->vc_suspend_state == VC_SUSPEND_REQUESTED && 2046 1.1 jmcneill arm_state->vc_resume_state == VC_RESUME_RESUMED) { 2047 1.1 jmcneill set_suspend_state(arm_state, VC_SUSPEND_IN_PROGRESS); 2048 1.1 jmcneill susp = 1; 2049 1.1 jmcneill } 2050 1.1 jmcneill write_unlock_bh(&arm_state->susp_res_lock); 2051 1.1 jmcneill 2052 1.1 jmcneill if (susp) 2053 1.1 jmcneill vchiq_platform_suspend(state); 2054 1.1 jmcneill 2055 1.1 jmcneill out: 2056 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); 2057 1.1 jmcneill return; 2058 1.1 jmcneill } 2059 1.1 jmcneill 2060 1.1 jmcneill 2061 1.1 jmcneill static void 2062 1.1 jmcneill output_timeout_error(VCHIQ_STATE_T *state) 2063 1.1 jmcneill { 2064 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 2065 1.1 jmcneill char service_err[50] = ""; 2066 1.1 jmcneill int vc_use_count = arm_state->videocore_use_count; 2067 1.1 jmcneill int active_services = state->unused_service; 2068 1.1 jmcneill int i; 2069 1.1 jmcneill 2070 1.1 jmcneill if (!arm_state->videocore_use_count) { 2071 1.1 jmcneill snprintf(service_err, 50, " Videocore usecount is 0"); 2072 1.1 jmcneill goto output_msg; 2073 1.1 jmcneill } 2074 1.1 jmcneill for (i = 0; i < active_services; i++) { 2075 1.1 jmcneill VCHIQ_SERVICE_T *service_ptr = state->services[i]; 2076 1.1 jmcneill if (service_ptr && service_ptr->service_use_count && 2077 1.1 jmcneill (service_ptr->srvstate != VCHIQ_SRVSTATE_FREE)) { 2078 1.7 skrll snprintf(service_err, 50, " %c%c%c%c(%8x) service has " 2079 1.1 jmcneill "use count %d%s", VCHIQ_FOURCC_AS_4CHARS( 2080 1.1 jmcneill service_ptr->base.fourcc), 2081 1.1 jmcneill service_ptr->client_id, 2082 1.1 jmcneill service_ptr->service_use_count, 2083 1.1 jmcneill service_ptr->service_use_count == 2084 1.1 jmcneill vc_use_count ? "" : " (+ more)"); 2085 1.1 jmcneill break; 2086 1.1 jmcneill } 2087 1.1 jmcneill } 2088 1.1 jmcneill 2089 1.1 jmcneill output_msg: 2090 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, 2091 1.1 jmcneill "timed out waiting for vc suspend (%d).%s", 2092 1.1 jmcneill arm_state->autosuspend_override, service_err); 2093 1.1 jmcneill 2094 1.1 jmcneill } 2095 1.1 jmcneill 2096 1.1 jmcneill /* Try to get videocore into suspended state, regardless of autosuspend state. 2097 1.1 jmcneill ** We don't actually force suspend, since videocore may get into a bad state 2098 1.1 jmcneill ** if we force suspend at a bad time. Instead, we wait for autosuspend to 2099 1.1 jmcneill ** determine a good point to suspend. If this doesn't happen within 100ms we 2100 1.1 jmcneill ** report failure. 2101 1.1 jmcneill ** 2102 1.1 jmcneill ** Returns VCHIQ_SUCCESS if videocore suspended successfully, VCHIQ_RETRY if 2103 1.1 jmcneill ** videocore failed to suspend in time or VCHIQ_ERROR if interrupted. 2104 1.1 jmcneill */ 2105 1.1 jmcneill VCHIQ_STATUS_T 2106 1.1 jmcneill vchiq_arm_force_suspend(VCHIQ_STATE_T *state) 2107 1.1 jmcneill { 2108 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 2109 1.1 jmcneill VCHIQ_STATUS_T status = VCHIQ_ERROR; 2110 1.1 jmcneill long rc = 0; 2111 1.1 jmcneill int repeat = -1; 2112 1.1 jmcneill 2113 1.1 jmcneill if (!arm_state) 2114 1.1 jmcneill goto out; 2115 1.1 jmcneill 2116 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); 2117 1.1 jmcneill 2118 1.1 jmcneill write_lock_bh(&arm_state->susp_res_lock); 2119 1.1 jmcneill 2120 1.1 jmcneill status = block_resume(arm_state); 2121 1.1 jmcneill if (status != VCHIQ_SUCCESS) 2122 1.1 jmcneill goto unlock; 2123 1.1 jmcneill if (arm_state->vc_suspend_state == VC_SUSPEND_SUSPENDED) { 2124 1.1 jmcneill /* Already suspended - just block resume and exit */ 2125 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, "%s already suspended", 2126 1.1 jmcneill __func__); 2127 1.1 jmcneill status = VCHIQ_SUCCESS; 2128 1.1 jmcneill goto unlock; 2129 1.1 jmcneill } else if (arm_state->vc_suspend_state <= VC_SUSPEND_IDLE) { 2130 1.1 jmcneill /* initiate suspend immediately in the case that we're waiting 2131 1.1 jmcneill * for the timeout */ 2132 1.1 jmcneill stop_suspend_timer(arm_state); 2133 1.1 jmcneill if (!vchiq_videocore_wanted(state)) { 2134 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, "%s videocore " 2135 1.1 jmcneill "idle, initiating suspend", __func__); 2136 1.1 jmcneill status = vchiq_arm_vcsuspend(state); 2137 1.1 jmcneill } else if (arm_state->autosuspend_override < 2138 1.1 jmcneill FORCE_SUSPEND_FAIL_MAX) { 2139 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, "%s letting " 2140 1.1 jmcneill "videocore go idle", __func__); 2141 1.1 jmcneill status = VCHIQ_SUCCESS; 2142 1.1 jmcneill } else { 2143 1.1 jmcneill vchiq_log_warning(vchiq_susp_log_level, "%s failed too " 2144 1.1 jmcneill "many times - attempting suspend", __func__); 2145 1.1 jmcneill status = vchiq_arm_vcsuspend(state); 2146 1.1 jmcneill } 2147 1.1 jmcneill } else { 2148 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, "%s videocore suspend " 2149 1.1 jmcneill "in progress - wait for completion", __func__); 2150 1.1 jmcneill status = VCHIQ_SUCCESS; 2151 1.1 jmcneill } 2152 1.1 jmcneill 2153 1.1 jmcneill /* Wait for suspend to happen due to system idle (not forced..) */ 2154 1.1 jmcneill if (status != VCHIQ_SUCCESS) 2155 1.1 jmcneill goto unblock_resume; 2156 1.1 jmcneill 2157 1.1 jmcneill do { 2158 1.1 jmcneill write_unlock_bh(&arm_state->susp_res_lock); 2159 1.1 jmcneill 2160 1.1 jmcneill rc = wait_for_completion_interruptible_timeout( 2161 1.1 jmcneill &arm_state->vc_suspend_complete, 2162 1.1 jmcneill msecs_to_jiffies(FORCE_SUSPEND_TIMEOUT_MS)); 2163 1.1 jmcneill 2164 1.1 jmcneill write_lock_bh(&arm_state->susp_res_lock); 2165 1.1 jmcneill if (rc < 0) { 2166 1.1 jmcneill vchiq_log_warning(vchiq_susp_log_level, "%s " 2167 1.1 jmcneill "interrupted waiting for suspend", __func__); 2168 1.1 jmcneill status = VCHIQ_ERROR; 2169 1.1 jmcneill goto unblock_resume; 2170 1.1 jmcneill } else if (rc == 0) { 2171 1.1 jmcneill if (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) { 2172 1.1 jmcneill /* Repeat timeout once if in progress */ 2173 1.1 jmcneill if (repeat < 0) { 2174 1.1 jmcneill repeat = 1; 2175 1.1 jmcneill continue; 2176 1.1 jmcneill } 2177 1.1 jmcneill } 2178 1.1 jmcneill arm_state->autosuspend_override++; 2179 1.1 jmcneill output_timeout_error(state); 2180 1.1 jmcneill 2181 1.1 jmcneill status = VCHIQ_RETRY; 2182 1.1 jmcneill goto unblock_resume; 2183 1.1 jmcneill } 2184 1.1 jmcneill } while (0 < (repeat--)); 2185 1.1 jmcneill 2186 1.1 jmcneill /* Check and report state in case we need to abort ARM suspend */ 2187 1.1 jmcneill if (arm_state->vc_suspend_state != VC_SUSPEND_SUSPENDED) { 2188 1.1 jmcneill status = VCHIQ_RETRY; 2189 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, 2190 1.1 jmcneill "%s videocore suspend failed (state %s)", __func__, 2191 1.1 jmcneill suspend_state_names[arm_state->vc_suspend_state + 2192 1.1 jmcneill VC_SUSPEND_NUM_OFFSET]); 2193 1.1 jmcneill /* Reset the state only if it's still in an error state. 2194 1.1 jmcneill * Something could have already initiated another suspend. */ 2195 1.1 jmcneill if (arm_state->vc_suspend_state < VC_SUSPEND_IDLE) 2196 1.1 jmcneill set_suspend_state(arm_state, VC_SUSPEND_IDLE); 2197 1.1 jmcneill 2198 1.1 jmcneill goto unblock_resume; 2199 1.1 jmcneill } 2200 1.1 jmcneill 2201 1.1 jmcneill /* successfully suspended - unlock and exit */ 2202 1.1 jmcneill goto unlock; 2203 1.1 jmcneill 2204 1.1 jmcneill unblock_resume: 2205 1.1 jmcneill /* all error states need to unblock resume before exit */ 2206 1.1 jmcneill unblock_resume(arm_state); 2207 1.1 jmcneill 2208 1.1 jmcneill unlock: 2209 1.1 jmcneill write_unlock_bh(&arm_state->susp_res_lock); 2210 1.1 jmcneill 2211 1.1 jmcneill out: 2212 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, status); 2213 1.1 jmcneill return status; 2214 1.1 jmcneill } 2215 1.1 jmcneill 2216 1.1 jmcneill void 2217 1.1 jmcneill vchiq_check_suspend(VCHIQ_STATE_T *state) 2218 1.1 jmcneill { 2219 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 2220 1.1 jmcneill 2221 1.1 jmcneill if (!arm_state) 2222 1.1 jmcneill goto out; 2223 1.1 jmcneill 2224 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); 2225 1.1 jmcneill 2226 1.1 jmcneill write_lock_bh(&arm_state->susp_res_lock); 2227 1.1 jmcneill if (arm_state->vc_suspend_state != VC_SUSPEND_SUSPENDED && 2228 1.1 jmcneill arm_state->first_connect && 2229 1.1 jmcneill !vchiq_videocore_wanted(state)) { 2230 1.1 jmcneill vchiq_arm_vcsuspend(state); 2231 1.1 jmcneill } 2232 1.1 jmcneill write_unlock_bh(&arm_state->susp_res_lock); 2233 1.1 jmcneill 2234 1.1 jmcneill out: 2235 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); 2236 1.1 jmcneill return; 2237 1.1 jmcneill } 2238 1.1 jmcneill 2239 1.1 jmcneill 2240 1.1 jmcneill int 2241 1.1 jmcneill vchiq_arm_allow_resume(VCHIQ_STATE_T *state) 2242 1.1 jmcneill { 2243 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 2244 1.1 jmcneill int resume = 0; 2245 1.1 jmcneill int ret = -1; 2246 1.1 jmcneill 2247 1.1 jmcneill if (!arm_state) 2248 1.1 jmcneill goto out; 2249 1.1 jmcneill 2250 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); 2251 1.1 jmcneill 2252 1.1 jmcneill write_lock_bh(&arm_state->susp_res_lock); 2253 1.1 jmcneill unblock_resume(arm_state); 2254 1.1 jmcneill resume = vchiq_check_resume(state); 2255 1.1 jmcneill write_unlock_bh(&arm_state->susp_res_lock); 2256 1.1 jmcneill 2257 1.1 jmcneill if (resume) { 2258 1.1 jmcneill if (wait_for_completion_interruptible( 2259 1.1 jmcneill &arm_state->vc_resume_complete) < 0) { 2260 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, 2261 1.1 jmcneill "%s interrupted", __func__); 2262 1.1 jmcneill /* failed, cannot accurately derive suspend 2263 1.1 jmcneill * state, so exit early. */ 2264 1.1 jmcneill goto out; 2265 1.1 jmcneill } 2266 1.1 jmcneill } 2267 1.1 jmcneill 2268 1.1 jmcneill read_lock_bh(&arm_state->susp_res_lock); 2269 1.1 jmcneill if (arm_state->vc_suspend_state == VC_SUSPEND_SUSPENDED) { 2270 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, 2271 1.1 jmcneill "%s: Videocore remains suspended", __func__); 2272 1.1 jmcneill } else { 2273 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, 2274 1.1 jmcneill "%s: Videocore resumed", __func__); 2275 1.1 jmcneill ret = 0; 2276 1.1 jmcneill } 2277 1.1 jmcneill read_unlock_bh(&arm_state->susp_res_lock); 2278 1.1 jmcneill out: 2279 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret); 2280 1.1 jmcneill return ret; 2281 1.1 jmcneill } 2282 1.1 jmcneill 2283 1.1 jmcneill /* This function should be called with the write lock held */ 2284 1.1 jmcneill int 2285 1.1 jmcneill vchiq_check_resume(VCHIQ_STATE_T *state) 2286 1.1 jmcneill { 2287 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 2288 1.1 jmcneill int resume = 0; 2289 1.1 jmcneill 2290 1.1 jmcneill if (!arm_state) 2291 1.1 jmcneill goto out; 2292 1.1 jmcneill 2293 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); 2294 1.1 jmcneill 2295 1.1 jmcneill if (need_resume(state)) { 2296 1.1 jmcneill set_resume_state(arm_state, VC_RESUME_REQUESTED); 2297 1.1 jmcneill request_poll(state, NULL, 0); 2298 1.1 jmcneill resume = 1; 2299 1.1 jmcneill } 2300 1.1 jmcneill 2301 1.1 jmcneill out: 2302 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); 2303 1.1 jmcneill return resume; 2304 1.1 jmcneill } 2305 1.1 jmcneill 2306 1.1 jmcneill #ifdef notyet 2307 1.1 jmcneill void 2308 1.1 jmcneill vchiq_platform_check_resume(VCHIQ_STATE_T *state) 2309 1.1 jmcneill { 2310 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 2311 1.1 jmcneill int res = 0; 2312 1.1 jmcneill 2313 1.1 jmcneill if (!arm_state) 2314 1.1 jmcneill goto out; 2315 1.1 jmcneill 2316 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); 2317 1.1 jmcneill 2318 1.1 jmcneill write_lock_bh(&arm_state->susp_res_lock); 2319 1.1 jmcneill if (arm_state->wake_address == 0) { 2320 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, 2321 1.1 jmcneill "%s: already awake", __func__); 2322 1.1 jmcneill goto unlock; 2323 1.1 jmcneill } 2324 1.1 jmcneill if (arm_state->vc_resume_state == VC_RESUME_IN_PROGRESS) { 2325 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, 2326 1.1 jmcneill "%s: already resuming", __func__); 2327 1.1 jmcneill goto unlock; 2328 1.1 jmcneill } 2329 1.1 jmcneill 2330 1.1 jmcneill if (arm_state->vc_resume_state == VC_RESUME_REQUESTED) { 2331 1.1 jmcneill set_resume_state(arm_state, VC_RESUME_IN_PROGRESS); 2332 1.1 jmcneill res = 1; 2333 1.1 jmcneill } else 2334 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, 2335 1.1 jmcneill "%s: not resuming (resume state %s)", __func__, 2336 1.1 jmcneill resume_state_names[arm_state->vc_resume_state + 2337 1.1 jmcneill VC_RESUME_NUM_OFFSET]); 2338 1.1 jmcneill 2339 1.1 jmcneill unlock: 2340 1.1 jmcneill write_unlock_bh(&arm_state->susp_res_lock); 2341 1.1 jmcneill 2342 1.1 jmcneill if (res) 2343 1.1 jmcneill vchiq_platform_resume(state); 2344 1.1 jmcneill 2345 1.1 jmcneill out: 2346 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); 2347 1.1 jmcneill return; 2348 1.1 jmcneill 2349 1.1 jmcneill } 2350 1.1 jmcneill #endif 2351 1.1 jmcneill 2352 1.1 jmcneill 2353 1.1 jmcneill 2354 1.1 jmcneill VCHIQ_STATUS_T 2355 1.1 jmcneill vchiq_use_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, 2356 1.1 jmcneill enum USE_TYPE_E use_type) 2357 1.1 jmcneill { 2358 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 2359 1.1 jmcneill VCHIQ_STATUS_T ret = VCHIQ_SUCCESS; 2360 1.1 jmcneill char entity[16]; 2361 1.1 jmcneill int *entity_uc; 2362 1.1 jmcneill int local_uc, local_entity_uc; 2363 1.1 jmcneill 2364 1.1 jmcneill if (!arm_state) 2365 1.1 jmcneill goto out; 2366 1.1 jmcneill 2367 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); 2368 1.1 jmcneill 2369 1.1 jmcneill if (use_type == USE_TYPE_VCHIQ) { 2370 1.10 christos snprintf(entity, sizeof(entity), "VCHIQ: "); 2371 1.1 jmcneill entity_uc = &arm_state->peer_use_count; 2372 1.1 jmcneill } else if (service) { 2373 1.7 skrll snprintf(entity, sizeof(entity), "%c%c%c%c:%8x", 2374 1.1 jmcneill VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), 2375 1.1 jmcneill service->client_id); 2376 1.1 jmcneill entity_uc = &service->service_use_count; 2377 1.1 jmcneill } else { 2378 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, "%s null service " 2379 1.1 jmcneill "ptr", __func__); 2380 1.1 jmcneill ret = VCHIQ_ERROR; 2381 1.1 jmcneill goto out; 2382 1.1 jmcneill } 2383 1.1 jmcneill 2384 1.1 jmcneill write_lock_bh(&arm_state->susp_res_lock); 2385 1.1 jmcneill while (arm_state->resume_blocked) { 2386 1.1 jmcneill /* If we call 'use' while force suspend is waiting for suspend, 2387 1.1 jmcneill * then we're about to block the thread which the force is 2388 1.1 jmcneill * waiting to complete, so we're bound to just time out. In this 2389 1.1 jmcneill * case, set the suspend state such that the wait will be 2390 1.1 jmcneill * canceled, so we can complete as quickly as possible. */ 2391 1.1 jmcneill if (arm_state->resume_blocked && arm_state->vc_suspend_state == 2392 1.1 jmcneill VC_SUSPEND_IDLE) { 2393 1.1 jmcneill set_suspend_state(arm_state, VC_SUSPEND_FORCE_CANCELED); 2394 1.1 jmcneill break; 2395 1.1 jmcneill } 2396 1.1 jmcneill /* If suspend is already in progress then we need to block */ 2397 1.1 jmcneill if (!try_wait_for_completion(&arm_state->resume_blocker)) { 2398 1.1 jmcneill /* Indicate that there are threads waiting on the resume 2399 1.1 jmcneill * blocker. These need to be allowed to complete before 2400 1.1 jmcneill * a _second_ call to force suspend can complete, 2401 1.1 jmcneill * otherwise low priority threads might never actually 2402 1.1 jmcneill * continue */ 2403 1.1 jmcneill arm_state->blocked_count++; 2404 1.1 jmcneill write_unlock_bh(&arm_state->susp_res_lock); 2405 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, "%s %s resume " 2406 1.1 jmcneill "blocked - waiting...", __func__, entity); 2407 1.1 jmcneill if (wait_for_completion_killable( 2408 1.1 jmcneill &arm_state->resume_blocker) != 0) { 2409 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, "%s %s " 2410 1.1 jmcneill "wait for resume blocker interrupted", 2411 1.1 jmcneill __func__, entity); 2412 1.1 jmcneill ret = VCHIQ_ERROR; 2413 1.1 jmcneill write_lock_bh(&arm_state->susp_res_lock); 2414 1.1 jmcneill arm_state->blocked_count--; 2415 1.1 jmcneill write_unlock_bh(&arm_state->susp_res_lock); 2416 1.1 jmcneill goto out; 2417 1.1 jmcneill } 2418 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, "%s %s resume " 2419 1.1 jmcneill "unblocked", __func__, entity); 2420 1.1 jmcneill write_lock_bh(&arm_state->susp_res_lock); 2421 1.1 jmcneill if (--arm_state->blocked_count == 0) 2422 1.1 jmcneill complete_all(&arm_state->blocked_blocker); 2423 1.1 jmcneill } 2424 1.1 jmcneill } 2425 1.1 jmcneill 2426 1.1 jmcneill stop_suspend_timer(arm_state); 2427 1.1 jmcneill 2428 1.1 jmcneill local_uc = ++arm_state->videocore_use_count; 2429 1.1 jmcneill local_entity_uc = ++(*entity_uc); 2430 1.1 jmcneill 2431 1.1 jmcneill /* If there's a pending request which hasn't yet been serviced then 2432 1.1 jmcneill * just clear it. If we're past VC_SUSPEND_REQUESTED state then 2433 1.1 jmcneill * vc_resume_complete will block until we either resume or fail to 2434 1.1 jmcneill * suspend */ 2435 1.1 jmcneill if (arm_state->vc_suspend_state <= VC_SUSPEND_REQUESTED) 2436 1.1 jmcneill set_suspend_state(arm_state, VC_SUSPEND_IDLE); 2437 1.1 jmcneill 2438 1.1 jmcneill if ((use_type != USE_TYPE_SERVICE_NO_RESUME) && need_resume(state)) { 2439 1.1 jmcneill set_resume_state(arm_state, VC_RESUME_REQUESTED); 2440 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, 2441 1.1 jmcneill "%s %s count %d, state count %d", 2442 1.1 jmcneill __func__, entity, local_entity_uc, local_uc); 2443 1.1 jmcneill request_poll(state, NULL, 0); 2444 1.1 jmcneill } else 2445 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, 2446 1.1 jmcneill "%s %s count %d, state count %d", 2447 1.1 jmcneill __func__, entity, *entity_uc, local_uc); 2448 1.1 jmcneill 2449 1.1 jmcneill 2450 1.1 jmcneill write_unlock_bh(&arm_state->susp_res_lock); 2451 1.1 jmcneill 2452 1.1 jmcneill /* Completion is in a done state when we're not suspended, so this won't 2453 1.1 jmcneill * block for the non-suspended case. */ 2454 1.1 jmcneill if (!try_wait_for_completion(&arm_state->vc_resume_complete)) { 2455 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, "%s %s wait for resume", 2456 1.1 jmcneill __func__, entity); 2457 1.1 jmcneill if (wait_for_completion_killable( 2458 1.1 jmcneill &arm_state->vc_resume_complete) != 0) { 2459 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, "%s %s wait for " 2460 1.1 jmcneill "resume interrupted", __func__, entity); 2461 1.1 jmcneill ret = VCHIQ_ERROR; 2462 1.1 jmcneill goto out; 2463 1.1 jmcneill } 2464 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, "%s %s resumed", __func__, 2465 1.1 jmcneill entity); 2466 1.1 jmcneill } 2467 1.1 jmcneill 2468 1.1 jmcneill if (ret == VCHIQ_SUCCESS) { 2469 1.1 jmcneill VCHIQ_STATUS_T status = VCHIQ_SUCCESS; 2470 1.1 jmcneill long ack_cnt = atomic_xchg(&arm_state->ka_use_ack_count, 0); 2471 1.1 jmcneill while (ack_cnt && (status == VCHIQ_SUCCESS)) { 2472 1.1 jmcneill /* Send the use notify to videocore */ 2473 1.1 jmcneill status = vchiq_send_remote_use_active(state); 2474 1.1 jmcneill if (status == VCHIQ_SUCCESS) 2475 1.1 jmcneill ack_cnt--; 2476 1.1 jmcneill else 2477 1.1 jmcneill atomic_add(ack_cnt, 2478 1.1 jmcneill &arm_state->ka_use_ack_count); 2479 1.1 jmcneill } 2480 1.1 jmcneill } 2481 1.1 jmcneill 2482 1.1 jmcneill out: 2483 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret); 2484 1.1 jmcneill return ret; 2485 1.1 jmcneill } 2486 1.1 jmcneill 2487 1.1 jmcneill VCHIQ_STATUS_T 2488 1.1 jmcneill vchiq_release_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service) 2489 1.1 jmcneill { 2490 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 2491 1.1 jmcneill VCHIQ_STATUS_T ret = VCHIQ_SUCCESS; 2492 1.1 jmcneill char entity[16]; 2493 1.1 jmcneill int *entity_uc; 2494 1.1 jmcneill 2495 1.1 jmcneill if (!arm_state) 2496 1.1 jmcneill goto out; 2497 1.1 jmcneill 2498 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); 2499 1.1 jmcneill 2500 1.1 jmcneill if (service) { 2501 1.7 skrll snprintf(entity, sizeof(entity), "%c%c%c%c:%8x", 2502 1.1 jmcneill VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), 2503 1.1 jmcneill service->client_id); 2504 1.1 jmcneill entity_uc = &service->service_use_count; 2505 1.1 jmcneill } else { 2506 1.10 christos snprintf(entity, sizeof(entity), "PEER: "); 2507 1.1 jmcneill entity_uc = &arm_state->peer_use_count; 2508 1.1 jmcneill } 2509 1.1 jmcneill 2510 1.1 jmcneill write_lock_bh(&arm_state->susp_res_lock); 2511 1.1 jmcneill if (!arm_state->videocore_use_count || !(*entity_uc)) { 2512 1.1 jmcneill /* Don't use BUG_ON - don't allow user thread to crash kernel */ 2513 1.1 jmcneill WARN_ON(!arm_state->videocore_use_count); 2514 1.1 jmcneill WARN_ON(!(*entity_uc)); 2515 1.1 jmcneill ret = VCHIQ_ERROR; 2516 1.1 jmcneill goto unlock; 2517 1.1 jmcneill } 2518 1.8 skrll --arm_state->videocore_use_count; 2519 1.8 skrll --(*entity_uc); 2520 1.1 jmcneill 2521 1.1 jmcneill if (!vchiq_videocore_wanted(state)) { 2522 1.1 jmcneill if (vchiq_platform_use_suspend_timer() && 2523 1.1 jmcneill !arm_state->resume_blocked) { 2524 1.1 jmcneill /* Only use the timer if we're not trying to force 2525 1.1 jmcneill * suspend (=> resume_blocked) */ 2526 1.1 jmcneill start_suspend_timer(arm_state); 2527 1.1 jmcneill } else { 2528 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, 2529 1.1 jmcneill "%s %s count %d, state count %d - suspending", 2530 1.1 jmcneill __func__, entity, *entity_uc, 2531 1.1 jmcneill arm_state->videocore_use_count); 2532 1.1 jmcneill vchiq_arm_vcsuspend(state); 2533 1.1 jmcneill } 2534 1.1 jmcneill } else 2535 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, 2536 1.1 jmcneill "%s %s count %d, state count %d", 2537 1.1 jmcneill __func__, entity, *entity_uc, 2538 1.1 jmcneill arm_state->videocore_use_count); 2539 1.1 jmcneill 2540 1.1 jmcneill unlock: 2541 1.1 jmcneill write_unlock_bh(&arm_state->susp_res_lock); 2542 1.1 jmcneill 2543 1.1 jmcneill out: 2544 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret); 2545 1.1 jmcneill return ret; 2546 1.1 jmcneill } 2547 1.1 jmcneill 2548 1.1 jmcneill void 2549 1.1 jmcneill vchiq_on_remote_use(VCHIQ_STATE_T *state) 2550 1.1 jmcneill { 2551 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 2552 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); 2553 1.1 jmcneill atomic_inc(&arm_state->ka_use_count); 2554 1.1 jmcneill complete(&arm_state->ka_evt); 2555 1.1 jmcneill } 2556 1.1 jmcneill 2557 1.1 jmcneill void 2558 1.1 jmcneill vchiq_on_remote_release(VCHIQ_STATE_T *state) 2559 1.1 jmcneill { 2560 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 2561 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); 2562 1.1 jmcneill atomic_inc(&arm_state->ka_release_count); 2563 1.1 jmcneill complete(&arm_state->ka_evt); 2564 1.1 jmcneill } 2565 1.1 jmcneill 2566 1.1 jmcneill VCHIQ_STATUS_T 2567 1.1 jmcneill vchiq_use_service_internal(VCHIQ_SERVICE_T *service) 2568 1.1 jmcneill { 2569 1.1 jmcneill return vchiq_use_internal(service->state, service, USE_TYPE_SERVICE); 2570 1.1 jmcneill } 2571 1.1 jmcneill 2572 1.1 jmcneill VCHIQ_STATUS_T 2573 1.1 jmcneill vchiq_release_service_internal(VCHIQ_SERVICE_T *service) 2574 1.1 jmcneill { 2575 1.1 jmcneill return vchiq_release_internal(service->state, service); 2576 1.1 jmcneill } 2577 1.1 jmcneill 2578 1.18 skrll #ifdef notyet 2579 1.18 skrll VCHIQ_DEBUGFS_NODE_T * 2580 1.18 skrll vchiq_instance_get_debugfs_node(VCHIQ_INSTANCE_T instance) 2581 1.18 skrll { 2582 1.18 skrll return &instance->debugfs_node; 2583 1.18 skrll } 2584 1.18 skrll 2585 1.18 skrll int 2586 1.18 skrll vchiq_instance_get_use_count(VCHIQ_INSTANCE_T instance) 2587 1.18 skrll { 2588 1.18 skrll VCHIQ_SERVICE_T *service; 2589 1.18 skrll int use_count = 0, i; 2590 1.18 skrll i = 0; 2591 1.18 skrll while ((service = next_service_by_instance(instance->state, 2592 1.18 skrll instance, &i)) != NULL) { 2593 1.18 skrll use_count += service->service_use_count; 2594 1.18 skrll unlock_service(service); 2595 1.18 skrll } 2596 1.18 skrll return use_count; 2597 1.18 skrll } 2598 1.18 skrll 2599 1.18 skrll int 2600 1.18 skrll vchiq_instance_get_pid(VCHIQ_INSTANCE_T instance) 2601 1.18 skrll { 2602 1.18 skrll return instance->pid; 2603 1.18 skrll } 2604 1.18 skrll 2605 1.18 skrll int 2606 1.18 skrll vchiq_instance_get_trace(VCHIQ_INSTANCE_T instance) 2607 1.18 skrll { 2608 1.18 skrll return instance->trace; 2609 1.18 skrll } 2610 1.18 skrll 2611 1.18 skrll void 2612 1.18 skrll vchiq_instance_set_trace(VCHIQ_INSTANCE_T instance, int trace) 2613 1.18 skrll { 2614 1.18 skrll VCHIQ_SERVICE_T *service; 2615 1.18 skrll int i; 2616 1.18 skrll i = 0; 2617 1.18 skrll while ((service = next_service_by_instance(instance->state, 2618 1.18 skrll instance, &i)) != NULL) { 2619 1.18 skrll service->trace = trace; 2620 1.18 skrll unlock_service(service); 2621 1.18 skrll } 2622 1.18 skrll instance->trace = (trace != 0); 2623 1.18 skrll } 2624 1.18 skrll #endif 2625 1.18 skrll 2626 1.1 jmcneill static void suspend_timer_callback(unsigned long context) 2627 1.1 jmcneill { 2628 1.1 jmcneill VCHIQ_STATE_T *state = (VCHIQ_STATE_T *)context; 2629 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 2630 1.1 jmcneill if (!arm_state) 2631 1.1 jmcneill goto out; 2632 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, 2633 1.1 jmcneill "%s - suspend timer expired - check suspend", __func__); 2634 1.1 jmcneill vchiq_check_suspend(state); 2635 1.1 jmcneill out: 2636 1.1 jmcneill return; 2637 1.1 jmcneill } 2638 1.1 jmcneill 2639 1.1 jmcneill VCHIQ_STATUS_T 2640 1.1 jmcneill vchiq_use_service_no_resume(VCHIQ_SERVICE_HANDLE_T handle) 2641 1.1 jmcneill { 2642 1.1 jmcneill VCHIQ_STATUS_T ret = VCHIQ_ERROR; 2643 1.1 jmcneill VCHIQ_SERVICE_T *service = find_service_by_handle(handle); 2644 1.1 jmcneill if (service) { 2645 1.1 jmcneill ret = vchiq_use_internal(service->state, service, 2646 1.1 jmcneill USE_TYPE_SERVICE_NO_RESUME); 2647 1.1 jmcneill unlock_service(service); 2648 1.1 jmcneill } 2649 1.1 jmcneill return ret; 2650 1.1 jmcneill } 2651 1.1 jmcneill 2652 1.1 jmcneill VCHIQ_STATUS_T 2653 1.1 jmcneill vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle) 2654 1.1 jmcneill { 2655 1.1 jmcneill VCHIQ_STATUS_T ret = VCHIQ_ERROR; 2656 1.1 jmcneill VCHIQ_SERVICE_T *service = find_service_by_handle(handle); 2657 1.1 jmcneill if (service) { 2658 1.1 jmcneill ret = vchiq_use_internal(service->state, service, 2659 1.1 jmcneill USE_TYPE_SERVICE); 2660 1.1 jmcneill unlock_service(service); 2661 1.1 jmcneill } 2662 1.1 jmcneill return ret; 2663 1.1 jmcneill } 2664 1.1 jmcneill 2665 1.1 jmcneill VCHIQ_STATUS_T 2666 1.1 jmcneill vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle) 2667 1.1 jmcneill { 2668 1.1 jmcneill VCHIQ_STATUS_T ret = VCHIQ_ERROR; 2669 1.1 jmcneill VCHIQ_SERVICE_T *service = find_service_by_handle(handle); 2670 1.1 jmcneill if (service) { 2671 1.1 jmcneill ret = vchiq_release_internal(service->state, service); 2672 1.1 jmcneill unlock_service(service); 2673 1.1 jmcneill } 2674 1.1 jmcneill return ret; 2675 1.1 jmcneill } 2676 1.1 jmcneill 2677 1.1 jmcneill void 2678 1.1 jmcneill vchiq_dump_service_use_state(VCHIQ_STATE_T *state) 2679 1.1 jmcneill { 2680 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 2681 1.1 jmcneill int i, j = 0; 2682 1.1 jmcneill /* Only dump 64 services */ 2683 1.17 christos #define local_max_services 64 2684 1.1 jmcneill /* If there's more than 64 services, only dump ones with 2685 1.1 jmcneill * non-zero counts */ 2686 1.1 jmcneill int only_nonzero = 0; 2687 1.1 jmcneill static const char *nz = "<-- preventing suspend"; 2688 1.1 jmcneill 2689 1.1 jmcneill enum vc_suspend_status vc_suspend_state; 2690 1.1 jmcneill enum vc_resume_status vc_resume_state; 2691 1.1 jmcneill int peer_count; 2692 1.1 jmcneill int vc_use_count; 2693 1.1 jmcneill int active_services; 2694 1.1 jmcneill struct service_data_struct { 2695 1.1 jmcneill int fourcc; 2696 1.1 jmcneill int clientid; 2697 1.1 jmcneill int use_count; 2698 1.1 jmcneill } service_data[local_max_services]; 2699 1.1 jmcneill 2700 1.1 jmcneill if (!arm_state) 2701 1.1 jmcneill return; 2702 1.1 jmcneill 2703 1.1 jmcneill read_lock_bh(&arm_state->susp_res_lock); 2704 1.1 jmcneill vc_suspend_state = arm_state->vc_suspend_state; 2705 1.1 jmcneill vc_resume_state = arm_state->vc_resume_state; 2706 1.1 jmcneill peer_count = arm_state->peer_use_count; 2707 1.1 jmcneill vc_use_count = arm_state->videocore_use_count; 2708 1.1 jmcneill active_services = state->unused_service; 2709 1.1 jmcneill if (active_services > local_max_services) 2710 1.1 jmcneill only_nonzero = 1; 2711 1.1 jmcneill 2712 1.1 jmcneill for (i = 0; (i < active_services) && (j < local_max_services); i++) { 2713 1.1 jmcneill VCHIQ_SERVICE_T *service_ptr = state->services[i]; 2714 1.1 jmcneill if (!service_ptr) 2715 1.1 jmcneill continue; 2716 1.1 jmcneill 2717 1.1 jmcneill if (only_nonzero && !service_ptr->service_use_count) 2718 1.1 jmcneill continue; 2719 1.1 jmcneill 2720 1.1 jmcneill if (service_ptr->srvstate != VCHIQ_SRVSTATE_FREE) { 2721 1.1 jmcneill service_data[j].fourcc = service_ptr->base.fourcc; 2722 1.1 jmcneill service_data[j].clientid = service_ptr->client_id; 2723 1.1 jmcneill service_data[j++].use_count = service_ptr-> 2724 1.1 jmcneill service_use_count; 2725 1.1 jmcneill } 2726 1.1 jmcneill } 2727 1.1 jmcneill 2728 1.1 jmcneill read_unlock_bh(&arm_state->susp_res_lock); 2729 1.1 jmcneill 2730 1.1 jmcneill vchiq_log_warning(vchiq_susp_log_level, 2731 1.1 jmcneill "-- Videcore suspend state: %s --", 2732 1.1 jmcneill suspend_state_names[vc_suspend_state + VC_SUSPEND_NUM_OFFSET]); 2733 1.1 jmcneill vchiq_log_warning(vchiq_susp_log_level, 2734 1.1 jmcneill "-- Videcore resume state: %s --", 2735 1.1 jmcneill resume_state_names[vc_resume_state + VC_RESUME_NUM_OFFSET]); 2736 1.1 jmcneill 2737 1.1 jmcneill if (only_nonzero) 2738 1.1 jmcneill vchiq_log_warning(vchiq_susp_log_level, "Too many active " 2739 1.1 jmcneill "services (%d). Only dumping up to first %d services " 2740 1.1 jmcneill "with non-zero use-count", active_services, 2741 1.1 jmcneill local_max_services); 2742 1.1 jmcneill 2743 1.1 jmcneill for (i = 0; i < j; i++) { 2744 1.1 jmcneill vchiq_log_warning(vchiq_susp_log_level, 2745 1.1 jmcneill "----- %c%c%c%c:%d service count %d %s", 2746 1.1 jmcneill VCHIQ_FOURCC_AS_4CHARS(service_data[i].fourcc), 2747 1.1 jmcneill service_data[i].clientid, 2748 1.1 jmcneill service_data[i].use_count, 2749 1.1 jmcneill service_data[i].use_count ? nz : ""); 2750 1.1 jmcneill } 2751 1.1 jmcneill vchiq_log_warning(vchiq_susp_log_level, 2752 1.1 jmcneill "----- VCHIQ use count count %d", peer_count); 2753 1.1 jmcneill vchiq_log_warning(vchiq_susp_log_level, 2754 1.1 jmcneill "--- Overall vchiq instance use count %d", vc_use_count); 2755 1.1 jmcneill 2756 1.1 jmcneill vchiq_dump_platform_use_state(state); 2757 1.1 jmcneill } 2758 1.1 jmcneill 2759 1.1 jmcneill VCHIQ_STATUS_T 2760 1.1 jmcneill vchiq_check_service(VCHIQ_SERVICE_T *service) 2761 1.1 jmcneill { 2762 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state; 2763 1.1 jmcneill VCHIQ_STATUS_T ret = VCHIQ_ERROR; 2764 1.1 jmcneill 2765 1.1 jmcneill if (!service || !service->state) 2766 1.1 jmcneill goto out; 2767 1.1 jmcneill 2768 1.1 jmcneill vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); 2769 1.1 jmcneill 2770 1.1 jmcneill arm_state = vchiq_platform_get_arm_state(service->state); 2771 1.1 jmcneill 2772 1.1 jmcneill read_lock_bh(&arm_state->susp_res_lock); 2773 1.1 jmcneill if (service->service_use_count) 2774 1.1 jmcneill ret = VCHIQ_SUCCESS; 2775 1.1 jmcneill read_unlock_bh(&arm_state->susp_res_lock); 2776 1.1 jmcneill 2777 1.1 jmcneill if (ret == VCHIQ_ERROR) { 2778 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, 2779 1.7 skrll "%s ERROR - %c%c%c%c:%8x service count %d, " 2780 1.1 jmcneill "state count %d, videocore suspend state %s", __func__, 2781 1.1 jmcneill VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), 2782 1.1 jmcneill service->client_id, service->service_use_count, 2783 1.1 jmcneill arm_state->videocore_use_count, 2784 1.1 jmcneill suspend_state_names[arm_state->vc_suspend_state + 2785 1.1 jmcneill VC_SUSPEND_NUM_OFFSET]); 2786 1.1 jmcneill vchiq_dump_service_use_state(service->state); 2787 1.1 jmcneill } 2788 1.1 jmcneill out: 2789 1.1 jmcneill return ret; 2790 1.1 jmcneill } 2791 1.1 jmcneill 2792 1.1 jmcneill /* stub functions */ 2793 1.1 jmcneill void vchiq_on_remote_use_active(VCHIQ_STATE_T *state) 2794 1.1 jmcneill { 2795 1.1 jmcneill (void)state; 2796 1.1 jmcneill } 2797 1.1 jmcneill 2798 1.1 jmcneill void vchiq_platform_conn_state_changed(VCHIQ_STATE_T *state, 2799 1.1 jmcneill VCHIQ_CONNSTATE_T oldstate, VCHIQ_CONNSTATE_T newstate) 2800 1.1 jmcneill { 2801 1.1 jmcneill VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); 2802 1.1 jmcneill vchiq_log_info(vchiq_susp_log_level, "%d: %s->%s", state->id, 2803 1.1 jmcneill get_conn_state_name(oldstate), get_conn_state_name(newstate)); 2804 1.1 jmcneill if (state->conn_state == VCHIQ_CONNSTATE_CONNECTED) { 2805 1.1 jmcneill write_lock_bh(&arm_state->susp_res_lock); 2806 1.1 jmcneill if (!arm_state->first_connect) { 2807 1.1 jmcneill char threadname[10]; 2808 1.1 jmcneill arm_state->first_connect = 1; 2809 1.1 jmcneill write_unlock_bh(&arm_state->susp_res_lock); 2810 1.1 jmcneill snprintf(threadname, sizeof(threadname), "VCHIQka-%d", 2811 1.1 jmcneill state->id); 2812 1.1 jmcneill arm_state->ka_thread = vchiq_thread_create( 2813 1.1 jmcneill &vchiq_keepalive_thread_func, 2814 1.1 jmcneill (void *)state, 2815 1.1 jmcneill threadname); 2816 1.1 jmcneill if (arm_state->ka_thread == NULL) { 2817 1.1 jmcneill vchiq_log_error(vchiq_susp_log_level, 2818 1.1 jmcneill "vchiq: FATAL: couldn't create thread %s", 2819 1.1 jmcneill threadname); 2820 1.1 jmcneill } else { 2821 1.1 jmcneill wake_up_process(arm_state->ka_thread); 2822 1.1 jmcneill } 2823 1.1 jmcneill } else 2824 1.1 jmcneill write_unlock_bh(&arm_state->susp_res_lock); 2825 1.1 jmcneill } 2826 1.1 jmcneill } 2827 1.1 jmcneill 2828 1.18 skrll 2829 1.1 jmcneill /**************************************************************************** 2830 1.1 jmcneill * 2831 1.1 jmcneill * vchiq_init - called when the module is loaded. 2832 1.1 jmcneill * 2833 1.1 jmcneill ***************************************************************************/ 2834 1.1 jmcneill 2835 1.1 jmcneill int __init vchiq_init(void); 2836 1.1 jmcneill int __init 2837 1.1 jmcneill vchiq_init(void) 2838 1.1 jmcneill { 2839 1.1 jmcneill int err; 2840 1.1 jmcneill 2841 1.1 jmcneill #ifdef notyet 2842 1.1 jmcneill /* create proc entries */ 2843 1.1 jmcneill err = vchiq_proc_init(); 2844 1.1 jmcneill if (err != 0) 2845 1.1 jmcneill goto failed_proc_init; 2846 1.1 jmcneill #endif 2847 1.1 jmcneill 2848 1.1 jmcneill spin_lock_init(&msg_queue_spinlock); 2849 1.1 jmcneill 2850 1.1 jmcneill err = vchiq_platform_init(&g_state); 2851 1.1 jmcneill if (err != 0) 2852 1.1 jmcneill goto failed_platform_init; 2853 1.1 jmcneill 2854 1.1 jmcneill vchiq_log_info(vchiq_arm_log_level, 2855 1.1 jmcneill "vchiq: initialised - version %d (min %d)", 2856 1.1 jmcneill VCHIQ_VERSION, VCHIQ_VERSION_MIN); 2857 1.1 jmcneill 2858 1.1 jmcneill return 0; 2859 1.1 jmcneill 2860 1.1 jmcneill failed_platform_init: 2861 1.1 jmcneill vchiq_log_warning(vchiq_arm_log_level, "could not load vchiq"); 2862 1.1 jmcneill return err; 2863 1.1 jmcneill } 2864 1.1 jmcneill 2865 1.1 jmcneill /**************************************************************************** 2866 1.1 jmcneill * 2867 1.1 jmcneill * vchiq_exit - called when the module is unloaded. 2868 1.1 jmcneill * 2869 1.1 jmcneill ***************************************************************************/ 2870 1.1 jmcneill 2871 1.1 jmcneill void vchiq_exit(void); 2872 1.1 jmcneill void 2873 1.1 jmcneill vchiq_exit(void) 2874 1.1 jmcneill { 2875 1.1 jmcneill vchiq_platform_exit(&g_state); 2876 1.1 jmcneill } 2877