Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame^] | 1 | /* arch/arm/mach-msm/qdsp6/dal.c |
| 2 | * |
| 3 | * Copyright (C) 2009 Google, Inc. |
| 4 | * Author: Brian Swetland <swetland@google.com> |
| 5 | * |
| 6 | * This software is licensed under the terms of the GNU General Public |
| 7 | * License version 2, as published by the Free Software Foundation, and |
| 8 | * may be copied, distributed, and modified under those terms. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
| 14 | * |
| 15 | */ |
| 16 | |
| 17 | #include <linux/slab.h> |
| 18 | #include <linux/kernel.h> |
| 19 | #include <linux/spinlock.h> |
| 20 | #include <linux/mutex.h> |
| 21 | #include <linux/list.h> |
| 22 | #include <linux/sched.h> |
| 23 | #include <linux/wait.h> |
| 24 | #include <linux/errno.h> |
| 25 | |
| 26 | #include <linux/delay.h> |
| 27 | |
| 28 | #include <mach/msm_smd.h> |
| 29 | #include <mach/debug_mm.h> |
| 30 | #include <mach/msm_qdsp6_audio.h> |
| 31 | |
| 32 | #include "dal.h" |
| 33 | |
| 34 | #define DAL_TRACE 0 |
| 35 | |
| 36 | struct dal_hdr { |
| 37 | uint32_t length:16; /* message length (header inclusive) */ |
| 38 | uint32_t version:8; /* DAL protocol version */ |
| 39 | uint32_t priority:7; |
| 40 | uint32_t async:1; |
| 41 | uint32_t ddi:16; /* DDI method number */ |
| 42 | uint32_t prototype:8; /* DDI serialization format */ |
| 43 | uint32_t msgid:8; /* message id (DDI, ATTACH, DETACH, ...) */ |
| 44 | void *from; |
| 45 | void *to; |
| 46 | } __attribute__((packed)); |
| 47 | |
| 48 | #define TRACE_DATA_MAX 128 |
| 49 | #define TRACE_LOG_MAX 32 |
| 50 | #define TRACE_LOG_MASK (TRACE_LOG_MAX - 1) |
| 51 | |
| 52 | struct dal_trace { |
| 53 | unsigned timestamp; |
| 54 | struct dal_hdr hdr; |
| 55 | uint32_t data[TRACE_DATA_MAX]; |
| 56 | }; |
| 57 | |
| 58 | #define DAL_HDR_SIZE (sizeof(struct dal_hdr)) |
| 59 | #define DAL_DATA_MAX 512 |
| 60 | #define DAL_MSG_MAX (DAL_HDR_SIZE + DAL_DATA_MAX) |
| 61 | |
| 62 | #define DAL_VERSION 0x11 |
| 63 | |
| 64 | #define DAL_MSGID_DDI 0x00 |
| 65 | #define DAL_MSGID_ATTACH 0x01 |
| 66 | #define DAL_MSGID_DETACH 0x02 |
| 67 | #define DAL_MSGID_ASYNCH 0xC0 |
| 68 | #define DAL_MSGID_REPLY 0x80 |
| 69 | |
| 70 | struct dal_channel { |
| 71 | struct list_head list; |
| 72 | struct list_head clients; |
| 73 | |
| 74 | /* synchronization for changing channel state, |
| 75 | * adding/removing clients, smd callbacks, etc |
| 76 | */ |
| 77 | spinlock_t lock; |
| 78 | |
| 79 | struct smd_channel *sch; |
| 80 | char *name; |
| 81 | |
| 82 | /* events are delivered at IRQ context immediately, so |
| 83 | * we only need one assembly buffer for the entire channel |
| 84 | */ |
| 85 | struct dal_hdr hdr; |
| 86 | unsigned char data[DAL_DATA_MAX]; |
| 87 | |
| 88 | unsigned count; |
| 89 | void *ptr; |
| 90 | |
| 91 | /* client which the current inbound message is for */ |
| 92 | struct dal_client *active; |
| 93 | }; |
| 94 | |
| 95 | struct dal_client { |
| 96 | struct list_head list; |
| 97 | struct dal_channel *dch; |
| 98 | void *cookie; |
| 99 | dal_event_func_t event; |
| 100 | |
| 101 | /* opaque handle for the far side */ |
| 102 | void *remote; |
| 103 | |
| 104 | /* dal rpc calls are fully synchronous -- only one call may be |
| 105 | * active per client at a time |
| 106 | */ |
| 107 | struct mutex write_lock; |
| 108 | wait_queue_head_t wait; |
| 109 | |
| 110 | unsigned char data[DAL_DATA_MAX]; |
| 111 | |
| 112 | void *reply; |
| 113 | int reply_max; |
| 114 | int status; |
| 115 | unsigned msgid; /* msgid of expected reply */ |
| 116 | |
| 117 | spinlock_t tr_lock; |
| 118 | unsigned tr_head; |
| 119 | unsigned tr_tail; |
| 120 | struct dal_trace *tr_log; |
| 121 | }; |
| 122 | |
| 123 | static unsigned now(void) |
| 124 | { |
| 125 | struct timespec ts; |
| 126 | ktime_get_ts(&ts); |
| 127 | return (ts.tv_nsec / 1000000) + (ts.tv_sec * 1000); |
| 128 | } |
| 129 | |
| 130 | void dal_trace(struct dal_client *c) |
| 131 | { |
| 132 | if (c->tr_log) |
| 133 | return; |
| 134 | c->tr_log = kzalloc(sizeof(struct dal_trace) * TRACE_LOG_MAX, |
| 135 | GFP_KERNEL); |
| 136 | } |
| 137 | |
| 138 | void dal_trace_print(struct dal_hdr *hdr, unsigned *data, int len, unsigned when) |
| 139 | { |
| 140 | int i; |
| 141 | printk("DAL %08x -> %08x L=%03x A=%d D=%04x P=%02x M=%02x T=%d", |
| 142 | (unsigned) hdr->from, (unsigned) hdr->to, |
| 143 | hdr->length, hdr->async, |
| 144 | hdr->ddi, hdr->prototype, hdr->msgid, |
| 145 | when); |
| 146 | len /= 4; |
| 147 | for (i = 0; i < len; i++) { |
| 148 | if (!(i & 7)) |
| 149 | printk("\n%03x", i * 4); |
| 150 | printk(" %08x", data[i]); |
| 151 | } |
| 152 | printk("\n"); |
| 153 | } |
| 154 | |
| 155 | void dal_trace_dump(struct dal_client *c) |
| 156 | { |
| 157 | struct dal_trace *dt; |
| 158 | unsigned n, len; |
| 159 | |
| 160 | if (!c->tr_log) |
| 161 | return; |
| 162 | |
| 163 | for (n = c->tr_tail; n != c->tr_head; n = (n + 1) & TRACE_LOG_MASK) { |
| 164 | dt = c->tr_log + n; |
| 165 | len = dt->hdr.length - sizeof(dt->hdr); |
| 166 | if (len > TRACE_DATA_MAX) |
| 167 | len = TRACE_DATA_MAX; |
| 168 | dal_trace_print(&dt->hdr, dt->data, len, dt->timestamp); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | static void dal_trace_log(struct dal_client *c, |
| 173 | struct dal_hdr *hdr, void *data, unsigned len) |
| 174 | { |
| 175 | unsigned long flags; |
| 176 | unsigned t, n; |
| 177 | struct dal_trace *dt; |
| 178 | |
| 179 | t = now(); |
| 180 | if (len > TRACE_DATA_MAX) |
| 181 | len = TRACE_DATA_MAX; |
| 182 | |
| 183 | spin_lock_irqsave(&c->tr_lock, flags); |
| 184 | n = (c->tr_head + 1) & TRACE_LOG_MASK; |
| 185 | if (c->tr_tail == n) |
| 186 | c->tr_tail = (c->tr_tail + 1) & TRACE_LOG_MASK; |
| 187 | dt = c->tr_log + n; |
| 188 | dt->timestamp = t; |
| 189 | memcpy(&dt->hdr, hdr, sizeof(struct dal_hdr)); |
| 190 | memcpy(dt->data, data, len); |
| 191 | c->tr_head = n; |
| 192 | |
| 193 | spin_unlock_irqrestore(&c->tr_lock, flags); |
| 194 | } |
| 195 | |
| 196 | |
| 197 | static void dal_channel_notify(void *priv, unsigned event) |
| 198 | { |
| 199 | struct dal_channel *dch = priv; |
| 200 | struct dal_hdr *hdr = &dch->hdr; |
| 201 | struct dal_client *client; |
| 202 | unsigned long flags; |
| 203 | int len; |
| 204 | int r; |
| 205 | |
| 206 | spin_lock_irqsave(&dch->lock, flags); |
| 207 | |
| 208 | again: |
| 209 | if (dch->count == 0) { |
| 210 | if (smd_read_avail(dch->sch) < DAL_HDR_SIZE) |
| 211 | goto done; |
| 212 | |
| 213 | smd_read(dch->sch, hdr, DAL_HDR_SIZE); |
| 214 | |
| 215 | if (hdr->length < DAL_HDR_SIZE) |
| 216 | goto done; |
| 217 | |
| 218 | if (hdr->length > DAL_MSG_MAX) |
| 219 | panic("oversize message"); |
| 220 | |
| 221 | dch->count = hdr->length - DAL_HDR_SIZE; |
| 222 | |
| 223 | /* locate the client this message is targeted to */ |
| 224 | list_for_each_entry(client, &dch->clients, list) { |
| 225 | if (dch->hdr.to == client) { |
| 226 | dch->active = client; |
| 227 | dch->ptr = client->data; |
| 228 | goto check_data; |
| 229 | } |
| 230 | } |
| 231 | pr_err("[%s:%s] $$$ receiving unknown message len = %d $$$\n", |
| 232 | __MM_FILE__, __func__, dch->count); |
| 233 | dch->active = 0; |
| 234 | dch->ptr = dch->data; |
| 235 | } |
| 236 | |
| 237 | check_data: |
| 238 | len = dch->count; |
| 239 | if (len > 0) { |
| 240 | if (smd_read_avail(dch->sch) < len) |
| 241 | goto done; |
| 242 | |
| 243 | r = smd_read(dch->sch, dch->ptr, len); |
| 244 | if (r != len) |
| 245 | panic("invalid read"); |
| 246 | |
| 247 | #if DAL_TRACE |
| 248 | pr_info("[%s:%s] dal recv %p <- %p %02x:%04x:%02x %d\n", |
| 249 | __MM_FILE__, __func__, hdr->to, hdr->from, hdr->msgid, |
| 250 | hdr->ddi, hdr->prototype, hdr->length - sizeof(*hdr)); |
| 251 | print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, dch->ptr, len); |
| 252 | #endif |
| 253 | dch->count = 0; |
| 254 | |
| 255 | client = dch->active; |
| 256 | if (!client) { |
| 257 | pr_err("[%s:%s] message to %p discarded\n", |
| 258 | __MM_FILE__, __func__, dch->hdr.to); |
| 259 | goto again; |
| 260 | } |
| 261 | |
| 262 | if (client->tr_log) |
| 263 | dal_trace_log(client, hdr, dch->ptr, len); |
| 264 | |
| 265 | if (hdr->msgid == DAL_MSGID_ASYNCH) { |
| 266 | if (client->event) |
| 267 | client->event(dch->ptr, len, client->cookie); |
| 268 | else |
| 269 | pr_err("[%s:%s] client %p has no event \ |
| 270 | handler\n", __MM_FILE__, __func__, |
| 271 | client); |
| 272 | goto again; |
| 273 | } |
| 274 | |
| 275 | if (hdr->msgid == client->msgid) { |
| 276 | if (!client->remote) |
| 277 | client->remote = hdr->from; |
| 278 | if (len > client->reply_max) |
| 279 | len = client->reply_max; |
| 280 | memcpy(client->reply, client->data, len); |
| 281 | client->status = len; |
| 282 | wake_up(&client->wait); |
| 283 | goto again; |
| 284 | } |
| 285 | |
| 286 | pr_err("[%s:%s] cannot find client %p\n", __MM_FILE__, |
| 287 | __func__, dch->hdr.to); |
| 288 | goto again; |
| 289 | } |
| 290 | |
| 291 | done: |
| 292 | spin_unlock_irqrestore(&dch->lock, flags); |
| 293 | } |
| 294 | |
| 295 | static LIST_HEAD(dal_channel_list); |
| 296 | static DEFINE_MUTEX(dal_channel_list_lock); |
| 297 | |
| 298 | static struct dal_channel *dal_open_channel(const char *name, uint32_t cpu) |
| 299 | { |
| 300 | struct dal_channel *dch; |
| 301 | |
| 302 | pr_debug("[%s:%s]\n", __MM_FILE__, __func__); |
| 303 | mutex_lock(&dal_channel_list_lock); |
| 304 | |
| 305 | list_for_each_entry(dch, &dal_channel_list, list) { |
| 306 | if (!strcmp(dch->name, name)) |
| 307 | goto found_it; |
| 308 | } |
| 309 | |
| 310 | dch = kzalloc(sizeof(*dch) + strlen(name) + 1, GFP_KERNEL); |
| 311 | if (!dch) |
| 312 | goto fail; |
| 313 | |
| 314 | dch->name = (char *) (dch + 1); |
| 315 | strcpy(dch->name, name); |
| 316 | spin_lock_init(&dch->lock); |
| 317 | INIT_LIST_HEAD(&dch->clients); |
| 318 | |
| 319 | list_add(&dch->list, &dal_channel_list); |
| 320 | |
| 321 | found_it: |
| 322 | if (!dch->sch) { |
| 323 | if (smd_named_open_on_edge(name, cpu, &dch->sch, |
| 324 | dch, dal_channel_notify)) { |
| 325 | pr_err("[%s:%s] smd open failed\n", __MM_FILE__, |
| 326 | __func__); |
| 327 | dch = NULL; |
| 328 | } |
| 329 | /* FIXME: wait for channel to open before returning */ |
| 330 | msleep(100); |
| 331 | } |
| 332 | |
| 333 | fail: |
| 334 | mutex_unlock(&dal_channel_list_lock); |
| 335 | |
| 336 | return dch; |
| 337 | } |
| 338 | |
| 339 | int dal_call_raw(struct dal_client *client, |
| 340 | struct dal_hdr *hdr, |
| 341 | void *data, int data_len, |
| 342 | void *reply, int reply_max) |
| 343 | { |
| 344 | struct dal_channel *dch = client->dch; |
| 345 | unsigned long flags; |
| 346 | |
| 347 | client->reply = reply; |
| 348 | client->reply_max = reply_max; |
| 349 | client->msgid = hdr->msgid | DAL_MSGID_REPLY; |
| 350 | client->status = -EBUSY; |
| 351 | |
| 352 | #if DAL_TRACE |
| 353 | pr_info("[%s:%s:%x] dal send %p -> %p %02x:%04x:%02x %d\n", |
| 354 | __MM_FILE__, __func__, (unsigned int)client, hdr->from, hdr->to, |
| 355 | hdr->msgid, hdr->ddi, hdr->prototype, |
| 356 | hdr->length - sizeof(*hdr)); |
| 357 | print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, data_len); |
| 358 | #endif |
| 359 | |
| 360 | if (client->tr_log) |
| 361 | dal_trace_log(client, hdr, data, data_len); |
| 362 | |
| 363 | spin_lock_irqsave(&dch->lock, flags); |
| 364 | /* FIXME: ensure entire message is written or none. */ |
| 365 | smd_write(dch->sch, hdr, sizeof(*hdr)); |
| 366 | smd_write(dch->sch, data, data_len); |
| 367 | spin_unlock_irqrestore(&dch->lock, flags); |
| 368 | |
| 369 | if (!wait_event_timeout(client->wait, (client->status != -EBUSY), 5*HZ)) { |
| 370 | dal_trace_dump(client); |
| 371 | pr_err("[%s:%s] call timed out. dsp is probably dead.\n", |
| 372 | __MM_FILE__, __func__); |
| 373 | dal_trace_print(hdr, data, data_len, 0); |
| 374 | q6audio_dsp_not_responding(); |
| 375 | } |
| 376 | |
| 377 | return client->status; |
| 378 | } |
| 379 | |
| 380 | int dal_call(struct dal_client *client, |
| 381 | unsigned ddi, unsigned prototype, |
| 382 | void *data, int data_len, |
| 383 | void *reply, int reply_max) |
| 384 | { |
| 385 | struct dal_hdr hdr; |
| 386 | int r; |
| 387 | |
| 388 | memset(&hdr, 0, sizeof(hdr)); |
| 389 | |
| 390 | hdr.length = data_len + sizeof(hdr); |
| 391 | hdr.version = DAL_VERSION; |
| 392 | hdr.msgid = DAL_MSGID_DDI; |
| 393 | hdr.ddi = ddi; |
| 394 | hdr.prototype = prototype; |
| 395 | hdr.from = client; |
| 396 | hdr.to = client->remote; |
| 397 | |
| 398 | if (hdr.length > DAL_MSG_MAX) |
| 399 | return -EINVAL; |
| 400 | |
| 401 | mutex_lock(&client->write_lock); |
| 402 | r = dal_call_raw(client, &hdr, data, data_len, reply, reply_max); |
| 403 | mutex_unlock(&client->write_lock); |
| 404 | |
| 405 | return r; |
| 406 | } |
| 407 | |
| 408 | struct dal_msg_attach { |
| 409 | uint32_t device_id; |
| 410 | char attach[64]; |
| 411 | char service_name[32]; |
| 412 | } __attribute__((packed)); |
| 413 | |
| 414 | struct dal_reply_attach { |
| 415 | uint32_t status; |
| 416 | char name[64]; |
| 417 | }; |
| 418 | |
| 419 | struct dal_client *dal_attach(uint32_t device_id, const char *name, |
| 420 | uint32_t cpu, dal_event_func_t func, void *cookie) |
| 421 | { |
| 422 | struct dal_hdr hdr; |
| 423 | struct dal_msg_attach msg; |
| 424 | struct dal_reply_attach reply; |
| 425 | struct dal_channel *dch; |
| 426 | struct dal_client *client; |
| 427 | unsigned long flags; |
| 428 | int r; |
| 429 | |
| 430 | pr_debug("[%s:%s]\n", __MM_FILE__, __func__); |
| 431 | dch = dal_open_channel(name, cpu); |
| 432 | if (!dch) |
| 433 | return 0; |
| 434 | |
| 435 | client = kzalloc(sizeof(*client), GFP_KERNEL); |
| 436 | if (!client) |
| 437 | return 0; |
| 438 | |
| 439 | client->dch = dch; |
| 440 | client->event = func; |
| 441 | client->cookie = cookie; |
| 442 | mutex_init(&client->write_lock); |
| 443 | spin_lock_init(&client->tr_lock); |
| 444 | init_waitqueue_head(&client->wait); |
| 445 | |
| 446 | spin_lock_irqsave(&dch->lock, flags); |
| 447 | list_add(&client->list, &dch->clients); |
| 448 | spin_unlock_irqrestore(&dch->lock, flags); |
| 449 | |
| 450 | memset(&hdr, 0, sizeof(hdr)); |
| 451 | memset(&msg, 0, sizeof(msg)); |
| 452 | |
| 453 | hdr.length = sizeof(hdr) + sizeof(msg); |
| 454 | hdr.version = DAL_VERSION; |
| 455 | hdr.msgid = DAL_MSGID_ATTACH; |
| 456 | hdr.from = client; |
| 457 | msg.device_id = device_id; |
| 458 | |
| 459 | r = dal_call_raw(client, &hdr, &msg, sizeof(msg), |
| 460 | &reply, sizeof(reply)); |
| 461 | |
| 462 | if ((r == sizeof(reply)) && (reply.status == 0)) { |
| 463 | reply.name[63] = 0; |
| 464 | pr_info("[%s:%s] status = %d, name = '%s' dal_client %x\n", |
| 465 | __MM_FILE__, __func__, reply.status, |
| 466 | reply.name, (unsigned int)client); |
| 467 | return client; |
| 468 | } |
| 469 | |
| 470 | pr_err("[%s:%s] failure\n", __MM_FILE__, __func__); |
| 471 | |
| 472 | dal_detach(client); |
| 473 | return 0; |
| 474 | } |
| 475 | |
| 476 | int dal_detach(struct dal_client *client) |
| 477 | { |
| 478 | struct dal_channel *dch; |
| 479 | unsigned long flags; |
| 480 | |
| 481 | pr_debug("[%s:%s]\n", __MM_FILE__, __func__); |
| 482 | mutex_lock(&client->write_lock); |
| 483 | if (client->remote) { |
| 484 | struct dal_hdr hdr; |
| 485 | uint32_t data; |
| 486 | |
| 487 | memset(&hdr, 0, sizeof(hdr)); |
| 488 | hdr.length = sizeof(hdr) + sizeof(data); |
| 489 | hdr.version = DAL_VERSION; |
| 490 | hdr.msgid = DAL_MSGID_DETACH; |
| 491 | hdr.from = client; |
| 492 | hdr.to = client->remote; |
| 493 | data = (uint32_t) client; |
| 494 | |
| 495 | dal_call_raw(client, &hdr, &data, sizeof(data), |
| 496 | &data, sizeof(data)); |
| 497 | } |
| 498 | |
| 499 | dch = client->dch; |
| 500 | spin_lock_irqsave(&dch->lock, flags); |
| 501 | if (dch->active == client) { |
| 502 | /* We have received a message header for this client |
| 503 | * but not the body of the message. Ensure that when |
| 504 | * the body arrives we don't write it into the now-closed |
| 505 | * client. In *theory* this should never happen. |
| 506 | */ |
| 507 | dch->active = 0; |
| 508 | dch->ptr = dch->data; |
| 509 | } |
| 510 | list_del(&client->list); |
| 511 | spin_unlock_irqrestore(&dch->lock, flags); |
| 512 | |
| 513 | mutex_unlock(&client->write_lock); |
| 514 | |
| 515 | kfree(client); |
| 516 | return 0; |
| 517 | } |
| 518 | |
| 519 | void *dal_get_remote_handle(struct dal_client *client) |
| 520 | { |
| 521 | return client->remote; |
| 522 | } |
| 523 | |
| 524 | /* convenience wrappers */ |
| 525 | |
| 526 | int dal_call_f0(struct dal_client *client, uint32_t ddi, uint32_t arg1) |
| 527 | { |
| 528 | uint32_t tmp = arg1; |
| 529 | int res; |
| 530 | res = dal_call(client, ddi, 0, &tmp, sizeof(tmp), &tmp, sizeof(tmp)); |
| 531 | if (res >= 4) |
| 532 | return (int) tmp; |
| 533 | return res; |
| 534 | } |
| 535 | |
| 536 | int dal_call_f1(struct dal_client *client, uint32_t ddi, uint32_t arg1, |
| 537 | uint32_t arg2) |
| 538 | { |
| 539 | uint32_t tmp[2]; |
| 540 | int res; |
| 541 | tmp[0] = arg1; |
| 542 | tmp[1] = arg2; |
| 543 | res = dal_call(client, ddi, 1, tmp, sizeof(tmp), tmp, sizeof(uint32_t)); |
| 544 | if (res >= 4) |
| 545 | return (int) tmp[0]; |
| 546 | return res; |
| 547 | } |
| 548 | |
| 549 | int dal_call_f5(struct dal_client *client, uint32_t ddi, void *ibuf, uint32_t ilen) |
| 550 | { |
| 551 | uint32_t tmp[128]; |
| 552 | int res; |
| 553 | int param_idx = 0; |
| 554 | |
| 555 | if (ilen + 4 > DAL_DATA_MAX) |
| 556 | return -EINVAL; |
| 557 | |
| 558 | tmp[param_idx] = ilen; |
| 559 | param_idx++; |
| 560 | |
| 561 | memcpy(&tmp[param_idx], ibuf, ilen); |
| 562 | param_idx += DIV_ROUND_UP(ilen, 4); |
| 563 | |
| 564 | res = dal_call(client, ddi, 5, tmp, param_idx * 4, tmp, sizeof(tmp)); |
| 565 | |
| 566 | if (res >= 4) |
| 567 | return (int) tmp[0]; |
| 568 | return res; |
| 569 | } |
| 570 | |
| 571 | int dal_call_f6(struct dal_client *client, uint32_t ddi, uint32_t s1, |
| 572 | void *ibuf, uint32_t ilen) |
| 573 | { |
| 574 | uint32_t tmp[128]; |
| 575 | int res; |
| 576 | int param_idx = 0; |
| 577 | |
| 578 | if (ilen + 8 > DAL_DATA_MAX) |
| 579 | return -EINVAL; |
| 580 | |
| 581 | tmp[param_idx] = s1; |
| 582 | param_idx++; |
| 583 | tmp[param_idx] = ilen; |
| 584 | param_idx++; |
| 585 | memcpy(&tmp[param_idx], ibuf, ilen); |
| 586 | param_idx += DIV_ROUND_UP(ilen, 4); |
| 587 | |
| 588 | res = dal_call(client, ddi, 6, tmp, param_idx * 4, tmp, sizeof(tmp)); |
| 589 | |
| 590 | if (res >= 4) |
| 591 | return (int) tmp[0]; |
| 592 | |
| 593 | return res; |
| 594 | } |
| 595 | |
| 596 | int dal_call_f9(struct dal_client *client, uint32_t ddi, void *obuf, |
| 597 | uint32_t olen) |
| 598 | { |
| 599 | uint32_t tmp[128]; |
| 600 | int res; |
| 601 | |
| 602 | if (olen > sizeof(tmp) - 8) |
| 603 | return -EINVAL; |
| 604 | tmp[0] = olen; |
| 605 | |
| 606 | res = dal_call(client, ddi, 9, tmp, sizeof(uint32_t), tmp, |
| 607 | sizeof(tmp)); |
| 608 | |
| 609 | if (res >= 4) |
| 610 | res = (int)tmp[0]; |
| 611 | |
| 612 | if (!res) { |
| 613 | if (tmp[1] > olen) |
| 614 | return -EIO; |
| 615 | memcpy(obuf, &tmp[2], tmp[1]); |
| 616 | } |
| 617 | return res; |
| 618 | } |
| 619 | |
| 620 | int dal_call_f11(struct dal_client *client, uint32_t ddi, uint32_t s1, |
| 621 | void *obuf, uint32_t olen) |
| 622 | { |
| 623 | uint32_t tmp[DAL_DATA_MAX/4] = {0}; |
| 624 | int res; |
| 625 | int param_idx = 0; |
| 626 | int num_bytes = 4; |
| 627 | |
| 628 | num_bytes += (DIV_ROUND_UP(olen, 4)) * 4; |
| 629 | |
| 630 | if ((num_bytes > DAL_DATA_MAX - 12) || (olen > DAL_DATA_MAX - 8)) |
| 631 | return -EINVAL; |
| 632 | |
| 633 | tmp[param_idx] = s1; |
| 634 | param_idx++; |
| 635 | tmp[param_idx] = olen; |
| 636 | param_idx += DIV_ROUND_UP(olen, 4); |
| 637 | |
| 638 | res = dal_call(client, ddi, 11, tmp, param_idx * 4, tmp, sizeof(tmp)); |
| 639 | |
| 640 | if (res >= 4) |
| 641 | res = (int) tmp[0]; |
| 642 | if (!res) { |
| 643 | if (tmp[1] > olen) |
| 644 | return -EIO; |
| 645 | memcpy(obuf, &tmp[2], tmp[1]); |
| 646 | } |
| 647 | return res; |
| 648 | } |
| 649 | |
| 650 | int dal_call_f13(struct dal_client *client, uint32_t ddi, void *ibuf1, |
| 651 | uint32_t ilen1, void *ibuf2, uint32_t ilen2, void *obuf, |
| 652 | uint32_t olen) |
| 653 | { |
| 654 | uint32_t tmp[DAL_DATA_MAX/4]; |
| 655 | int res; |
| 656 | int param_idx = 0; |
| 657 | int num_bytes = 0; |
| 658 | |
| 659 | num_bytes = (DIV_ROUND_UP(ilen1, 4)) * 4; |
| 660 | num_bytes += (DIV_ROUND_UP(ilen2, 4)) * 4; |
| 661 | |
| 662 | if ((num_bytes > DAL_DATA_MAX - 12) || (olen > DAL_DATA_MAX - 8) || |
| 663 | (ilen1 > DAL_DATA_MAX) || (ilen2 > DAL_DATA_MAX)) |
| 664 | return -EINVAL; |
| 665 | |
| 666 | tmp[param_idx] = ilen1; |
| 667 | param_idx++; |
| 668 | |
| 669 | memcpy(&tmp[param_idx], ibuf1, ilen1); |
| 670 | param_idx += DIV_ROUND_UP(ilen1, 4); |
| 671 | |
| 672 | tmp[param_idx++] = ilen2; |
| 673 | memcpy(&tmp[param_idx], ibuf2, ilen2); |
| 674 | param_idx += DIV_ROUND_UP(ilen2, 4); |
| 675 | |
| 676 | tmp[param_idx++] = olen; |
| 677 | res = dal_call(client, ddi, 13, tmp, param_idx * 4, tmp, |
| 678 | sizeof(tmp)); |
| 679 | |
| 680 | if (res >= 4) |
| 681 | res = (int)tmp[0]; |
| 682 | |
| 683 | if (!res) { |
| 684 | if (tmp[1] > olen) |
| 685 | return -EIO; |
| 686 | memcpy(obuf, &tmp[2], tmp[1]); |
| 687 | } |
| 688 | return res; |
| 689 | } |
| 690 | int dal_call_f14(struct dal_client *client, uint32_t ddi, void *ibuf, |
| 691 | uint32_t ilen, void *obuf1, uint32_t olen1, void *obuf2, |
| 692 | uint32_t olen2, uint32_t *oalen2) |
| 693 | { |
| 694 | uint32_t tmp[128]; |
| 695 | int res; |
| 696 | int param_idx = 0; |
| 697 | |
| 698 | if (olen1 + olen2 + 8 > DAL_DATA_MAX || |
| 699 | ilen + 12 > DAL_DATA_MAX) |
| 700 | return -EINVAL; |
| 701 | |
| 702 | tmp[param_idx] = ilen; |
| 703 | param_idx++; |
| 704 | |
| 705 | memcpy(&tmp[param_idx], ibuf, ilen); |
| 706 | param_idx += DIV_ROUND_UP(ilen, 4); |
| 707 | |
| 708 | tmp[param_idx++] = olen1; |
| 709 | tmp[param_idx++] = olen2; |
| 710 | res = dal_call(client, ddi, 14, tmp, param_idx * 4, tmp, sizeof(tmp)); |
| 711 | |
| 712 | if (res >= 4) |
| 713 | res = (int)tmp[0]; |
| 714 | |
| 715 | if (!res) { |
| 716 | if (tmp[1] > olen1) |
| 717 | return -EIO; |
| 718 | param_idx = DIV_ROUND_UP(tmp[1], 4) + 2; |
| 719 | if (tmp[param_idx] > olen2) |
| 720 | return -EIO; |
| 721 | |
| 722 | memcpy(obuf1, &tmp[2], tmp[1]); |
| 723 | memcpy(obuf2, &tmp[param_idx+1], tmp[param_idx]); |
| 724 | *oalen2 = tmp[param_idx]; |
| 725 | } |
| 726 | return res; |
| 727 | } |