/* Copyright (c) 2008-2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ /* * Device access library (DAL) implementation. */ #include #include #include #include #include #include #include #include #include #include #include #define DALRPC_PROTOCOL_VERSION 0x11 #define DALRPC_SUCCESS 0 #define DALRPC_MAX_PORTNAME_LEN 64 #define DALRPC_MAX_ATTACH_PARAM_LEN 64 #define DALRPC_MAX_SERVICE_NAME_LEN 32 #define DALRPC_MAX_PARAMS 128 #define DALRPC_MAX_PARAMS_SIZE (DALRPC_MAX_PARAMS * 4) #define DALRPC_MAX_MSG_SIZE (sizeof(struct dalrpc_msg_hdr) + \ DALRPC_MAX_PARAMS_SIZE) #define DALRPC_MSGID_DDI 0x0 #define DALRPC_MSGID_DDI_REPLY 0x80 #define DALRPC_MSGID_ATTACH_REPLY 0x81 #define DALRPC_MSGID_DETACH_REPLY 0x82 #define DALRPC_MSGID_ASYNCH 0xC0 #define ROUND_BUFLEN(x) (((x + 3) & ~0x3)) struct dalrpc_msg_hdr { uint32_t len:16; uint32_t proto_ver:8; uint32_t prio:7; uint32_t async:1; uint32_t ddi_idx:16; uint32_t proto_id:8; uint32_t msgid:8; void *from; void *to; }; struct dalrpc_msg { struct dalrpc_msg_hdr hdr; uint32_t param[DALRPC_MAX_PARAMS]; }; struct dalrpc_event_handle { struct list_head list; int flag; spinlock_t lock; }; struct dalrpc_cb_handle { struct list_head list; void (*fn)(void *, uint32_t, void *, uint32_t); void *context; }; struct daldevice_handle {; struct list_head list; void *remote_handle; struct completion read_completion; struct dalrpc_port *port; struct dalrpc_msg msg; struct mutex client_lock; }; struct dalrpc_port { struct list_head list; char port[DALRPC_MAX_PORTNAME_LEN+1]; int refcount; struct workqueue_struct *wq; struct work_struct port_work; struct mutex write_lock; smd_channel_t *ch; struct dalrpc_msg msg_in; struct daldevice_handle *msg_owner; unsigned msg_bytes_read; struct list_head event_list; struct mutex event_list_lock; struct list_head cb_list; struct mutex cb_list_lock; }; static LIST_HEAD(port_list); static LIST_HEAD(client_list); static DEFINE_MUTEX(pc_lists_lock); static DECLARE_WAIT_QUEUE_HEAD(event_wq); static int client_exists(void *handle) { struct daldevice_handle *h; if (!handle) return 0; mutex_lock(&pc_lists_lock); list_for_each_entry(h, &client_list, list) if (h == handle) { mutex_unlock(&pc_lists_lock); return 1; } mutex_unlock(&pc_lists_lock); return 0; } static int client_exists_locked(void *handle) { struct daldevice_handle *h; /* this function must be called with pc_lists_lock acquired */ if (!handle) return 0; list_for_each_entry(h, &client_list, list) if (h == handle) return 1; return 0; } static int port_exists(struct dalrpc_port *p) { struct dalrpc_port *p_iter; /* this function must be called with pc_lists_lock acquired */ if (!p) return 0; list_for_each_entry(p_iter, &port_list, list) if (p_iter == p) return 1; return 0; } static struct dalrpc_port *port_name_exists(char *port) { struct dalrpc_port *p; /* this function must be called with pc_lists_lock acquired */ list_for_each_entry(p, &port_list, list) if (!strcmp(p->port, port)) return p; return NULL; } static void port_close(struct dalrpc_port *p) { mutex_lock(&pc_lists_lock); p->refcount--; if (p->refcount == 0) list_del(&p->list); mutex_unlock(&pc_lists_lock); if (p->refcount == 0) { destroy_workqueue(p->wq); smd_close(p->ch); kfree(p); } } static int event_exists(struct dalrpc_port *p, struct dalrpc_event_handle *ev) { struct dalrpc_event_handle *ev_iter; /* this function must be called with event_list_lock acquired */ list_for_each_entry(ev_iter, &p->event_list, list) if (ev_iter == ev) return 1; return 0; } static int cb_exists(struct dalrpc_port *p, struct dalrpc_cb_handle *cb) { struct dalrpc_cb_handle *cb_iter; /* this function must be called with the cb_list_lock acquired */ list_for_each_entry(cb_iter, &p->cb_list, list) if (cb_iter == cb) return 1; return 0; } static int check_version(struct dalrpc_msg_hdr *msg_hdr) { static int version_msg = 1; /* disabled because asynch events currently have no version */ return 0; if (msg_hdr->proto_ver != DALRPC_PROTOCOL_VERSION) { if (version_msg) { printk(KERN_ERR "dalrpc: incompatible verison\n"); version_msg = 0; } return -1; } return 0; } static void process_asynch(struct dalrpc_port *p) { struct dalrpc_event_handle *ev; struct dalrpc_cb_handle *cb; ev = (struct dalrpc_event_handle *)p->msg_in.param[0]; cb = (struct dalrpc_cb_handle *)p->msg_in.param[0]; mutex_lock(&p->event_list_lock); if (event_exists(p, ev)) { spin_lock(&ev->lock); ev->flag = 1; spin_unlock(&ev->lock); smp_mb(); wake_up_all(&event_wq); mutex_unlock(&p->event_list_lock); return; } mutex_unlock(&p->event_list_lock); mutex_lock(&p->cb_list_lock); if (cb_exists(p, cb)) { cb->fn(cb->context, p->msg_in.param[1], &p->msg_in.param[3], p->msg_in.param[2]); mutex_unlock(&p->cb_list_lock); return; } mutex_unlock(&p->cb_list_lock); } static void process_msg(struct dalrpc_port *p) { switch (p->msg_in.hdr.msgid) { case DALRPC_MSGID_DDI_REPLY: case DALRPC_MSGID_ATTACH_REPLY: case DALRPC_MSGID_DETACH_REPLY: complete(&p->msg_owner->read_completion); break; case DALRPC_MSGID_ASYNCH: process_asynch(p); break; default: printk(KERN_ERR "process_msg: bad msgid %#x\n", p->msg_in.hdr.msgid); } } static void flush_msg(struct dalrpc_port *p) { int bytes_read, len; len = p->msg_in.hdr.len - sizeof(struct dalrpc_msg_hdr); while (len > 0) { bytes_read = smd_read(p->ch, NULL, len); if (bytes_read <= 0) break; len -= bytes_read; } p->msg_bytes_read = 0; } static int check_header(struct dalrpc_port *p) { if (check_version(&p->msg_in.hdr) || p->msg_in.hdr.len > DALRPC_MAX_MSG_SIZE || (p->msg_in.hdr.msgid != DALRPC_MSGID_ASYNCH && !client_exists_locked(p->msg_in.hdr.to))) { printk(KERN_ERR "dalrpc_read_msg: bad msg\n"); flush_msg(p); return 1; } p->msg_owner = (struct daldevice_handle *)p->msg_in.hdr.to; if (p->msg_in.hdr.msgid != DALRPC_MSGID_ASYNCH) memcpy(&p->msg_owner->msg.hdr, &p->msg_in.hdr, sizeof(p->msg_in.hdr)); return 0; } static int dalrpc_read_msg(struct dalrpc_port *p) { uint8_t *read_ptr; int bytes_read; /* read msg header */ while (p->msg_bytes_read < sizeof(p->msg_in.hdr)) { read_ptr = (uint8_t *)&p->msg_in.hdr + p->msg_bytes_read; bytes_read = smd_read(p->ch, read_ptr, sizeof(p->msg_in.hdr) - p->msg_bytes_read); if (bytes_read <= 0) return 0; p->msg_bytes_read += bytes_read; if (p->msg_bytes_read == sizeof(p->msg_in.hdr) && check_header(p)) return 1; } /* read remainder of msg */ if (p->msg_in.hdr.msgid != DALRPC_MSGID_ASYNCH) read_ptr = (uint8_t *)&p->msg_owner->msg; else read_ptr = (uint8_t *)&p->msg_in; read_ptr += p->msg_bytes_read; while (p->msg_bytes_read < p->msg_in.hdr.len) { bytes_read = smd_read(p->ch, read_ptr, p->msg_in.hdr.len - p->msg_bytes_read); if (bytes_read <= 0) return 0; p->msg_bytes_read += bytes_read; read_ptr += bytes_read; } process_msg(p); p->msg_bytes_read = 0; p->msg_owner = NULL; return 1; } static void dalrpc_work(struct work_struct *work) { struct dalrpc_port *p = container_of(work, struct dalrpc_port, port_work); /* must lock port/client lists to ensure port doesn't disappear under an asynch event */ mutex_lock(&pc_lists_lock); if (port_exists(p)) while (dalrpc_read_msg(p)) ; mutex_unlock(&pc_lists_lock); } static void dalrpc_smd_cb(void *priv, unsigned smd_flags) { struct dalrpc_port *p = priv; if (smd_flags != SMD_EVENT_DATA) return; queue_work(p->wq, &p->port_work); } static struct dalrpc_port *dalrpc_port_open(char *port, int cpu) { struct dalrpc_port *p; char wq_name[32]; p = port_name_exists(port); if (p) { p->refcount++; return p; } p = kzalloc(sizeof(struct dalrpc_port), GFP_KERNEL); if (!p) return NULL; strlcpy(p->port, port, sizeof(p->port)); p->refcount = 1; snprintf(wq_name, sizeof(wq_name), "dalrpc_rcv_%s", port); p->wq = create_singlethread_workqueue(wq_name); if (!p->wq) { printk(KERN_ERR "dalrpc_init: unable to create workqueue\n"); goto no_wq; } INIT_WORK(&p->port_work, dalrpc_work); mutex_init(&p->write_lock); mutex_init(&p->event_list_lock); mutex_init(&p->cb_list_lock); INIT_LIST_HEAD(&p->event_list); INIT_LIST_HEAD(&p->cb_list); p->msg_owner = NULL; p->msg_bytes_read = 0; if (smd_named_open_on_edge(port, cpu, &p->ch, p, dalrpc_smd_cb)) { printk(KERN_ERR "dalrpc_port_init() failed to open port\n"); goto no_smd; } list_add(&p->list, &port_list); return p; no_smd: destroy_workqueue(p->wq); no_wq: kfree(p); return NULL; } static void dalrpc_sendwait(struct daldevice_handle *h) { u8 *buf = (u8 *)&h->msg; int len = h->msg.hdr.len; int written; mutex_lock(&h->port->write_lock); do { written = smd_write(h->port->ch, buf + (h->msg.hdr.len - len), len); if (written < 0) break; len -= written; } while (len); mutex_unlock(&h->port->write_lock); if (!h->msg.hdr.async) wait_for_completion(&h->read_completion); } int daldevice_attach(uint32_t device_id, char *port, int cpu, void **handle_ptr) { struct daldevice_handle *h; char dyn_port[DALRPC_MAX_PORTNAME_LEN + 1] = "DAL00"; int ret; int tries = 0; if (!port) port = dyn_port; if (strlen(port) > DALRPC_MAX_PORTNAME_LEN) return -EINVAL; h = kzalloc(sizeof(struct daldevice_handle), GFP_KERNEL); if (!h) { *handle_ptr = NULL; return -ENOMEM; } init_completion(&h->read_completion); mutex_init(&h->client_lock); mutex_lock(&pc_lists_lock); list_add(&h->list, &client_list); mutex_unlock(&pc_lists_lock); /* 3 attempts, enough for one each on the user specified port, the * dynamic discovery port, and the port recommended by the dynamic * discovery port */ while (tries < 3) { tries++; mutex_lock(&pc_lists_lock); h->port = dalrpc_port_open(port, cpu); if (!h->port) { list_del(&h->list); mutex_unlock(&pc_lists_lock); printk(KERN_ERR "daldevice_attach: could not " "open port\n"); kfree(h); *handle_ptr = NULL; return -EIO; } mutex_unlock(&pc_lists_lock); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4 + DALRPC_MAX_ATTACH_PARAM_LEN + DALRPC_MAX_SERVICE_NAME_LEN; h->msg.hdr.proto_ver = DALRPC_PROTOCOL_VERSION; h->msg.hdr.ddi_idx = 0; h->msg.hdr.msgid = 0x1; h->msg.hdr.prio = 0; h->msg.hdr.async = 0; h->msg.hdr.from = h; h->msg.hdr.to = 0; h->msg.param[0] = device_id; memset(&h->msg.param[1], 0, DALRPC_MAX_ATTACH_PARAM_LEN + DALRPC_MAX_SERVICE_NAME_LEN); dalrpc_sendwait(h); ret = h->msg.param[0]; if (ret == DALRPC_SUCCESS) { h->remote_handle = h->msg.hdr.from; *handle_ptr = h; break; } else if (strnlen((char *)&h->msg.param[1], DALRPC_MAX_PORTNAME_LEN)) { /* another port was recommended in the response. */ strlcpy(dyn_port, (char *)&h->msg.param[1], sizeof(dyn_port)); dyn_port[DALRPC_MAX_PORTNAME_LEN] = 0; port = dyn_port; } else if (port == dyn_port) { /* the dynamic discovery port (or port that * was recommended by it) did not recognize * the device id, give up */ daldevice_detach(h); break; } else /* the user specified port did not work, try * the dynamic discovery port */ port = dyn_port; port_close(h->port); } return ret; } EXPORT_SYMBOL(daldevice_attach); static void dalrpc_ddi_prologue(uint32_t ddi_idx, struct daldevice_handle *h, uint32_t idx_async) { h->msg.hdr.proto_ver = DALRPC_PROTOCOL_VERSION; h->msg.hdr.prio = 0; h->msg.hdr.async = idx_async; h->msg.hdr.msgid = DALRPC_MSGID_DDI; h->msg.hdr.from = h; h->msg.hdr.to = h->remote_handle; h->msg.hdr.ddi_idx = ddi_idx; } int daldevice_detach(void *handle) { struct daldevice_handle *h = handle; if (!client_exists(h)) return -EINVAL; dalrpc_ddi_prologue(0, h, 0); if (!h->remote_handle) goto norpc; h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4; h->msg.hdr.msgid = 0x2; h->msg.param[0] = 0; dalrpc_sendwait(h); norpc: mutex_lock(&pc_lists_lock); list_del(&h->list); mutex_unlock(&pc_lists_lock); port_close(h->port); kfree(h); return 0; } EXPORT_SYMBOL(daldevice_detach); uint32_t dalrpc_fcn_0(uint32_t ddi_idx, void *handle, uint32_t s1) { struct daldevice_handle *h = handle; uint32_t ret; if (!client_exists(h)) return -EINVAL; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, 0); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4; h->msg.hdr.proto_id = 0; h->msg.param[0] = s1; dalrpc_sendwait(h); ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_0); uint32_t dalrpc_fcn_1(uint32_t ddi_idx, void *handle, uint32_t s1, uint32_t s2) { struct daldevice_handle *h = handle; uint32_t ret; if (!client_exists(h)) return -EINVAL; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, 0); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8; h->msg.hdr.proto_id = 1; h->msg.param[0] = s1; h->msg.param[1] = s2; dalrpc_sendwait(h); ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_1); uint32_t dalrpc_fcn_2(uint32_t ddi_idx, void *handle, uint32_t s1, uint32_t *p_s2) { struct daldevice_handle *h = handle; uint32_t ret; if (!client_exists(h)) return -EINVAL; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, 0); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4; h->msg.hdr.proto_id = 2; h->msg.param[0] = s1; dalrpc_sendwait(h); if (h->msg.param[0] == DALRPC_SUCCESS) *p_s2 = h->msg.param[1]; ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_2); uint32_t dalrpc_fcn_3(uint32_t ddi_idx, void *handle, uint32_t s1, uint32_t s2, uint32_t s3) { struct daldevice_handle *h = handle; uint32_t ret; if (!client_exists(h)) return -EINVAL; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, 0); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 12; h->msg.hdr.proto_id = 3; h->msg.param[0] = s1; h->msg.param[1] = s2; h->msg.param[2] = s3; dalrpc_sendwait(h); ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_3); uint32_t dalrpc_fcn_4(uint32_t ddi_idx, void *handle, uint32_t s1, uint32_t s2, uint32_t *p_s3) { struct daldevice_handle *h = handle; uint32_t ret; if (!client_exists(h)) return -EINVAL; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, 0); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8; h->msg.hdr.proto_id = 4; h->msg.param[0] = s1; h->msg.param[1] = s2; dalrpc_sendwait(h); if (h->msg.param[0] == DALRPC_SUCCESS) *p_s3 = h->msg.param[1]; ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_4); uint32_t dalrpc_fcn_5(uint32_t ddi_idx, void *handle, const void *ibuf, uint32_t ilen) { struct daldevice_handle *h = handle; uint32_t ret, idx_async; if ((ilen + 4) > DALRPC_MAX_PARAMS_SIZE) return -EINVAL; if (!client_exists(h)) return -EINVAL; idx_async = (ddi_idx & 0x80000000) >> 31; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, idx_async); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4 + ROUND_BUFLEN(ilen); h->msg.hdr.proto_id = 5; h->msg.param[0] = ilen; memcpy(&h->msg.param[1], ibuf, ilen); dalrpc_sendwait(h); if (h->msg.hdr.async) ret = DALRPC_SUCCESS; else ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_5); uint32_t dalrpc_fcn_6(uint32_t ddi_idx, void *handle, uint32_t s1, const void *ibuf, uint32_t ilen) { struct daldevice_handle *h = handle; uint32_t ret; if ((ilen + 8) > DALRPC_MAX_PARAMS_SIZE) return -EINVAL; if (!client_exists(h)) return -EINVAL; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, 0); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8 + ROUND_BUFLEN(ilen); h->msg.hdr.proto_id = 6; h->msg.param[0] = s1; h->msg.param[1] = ilen; memcpy(&h->msg.param[2], ibuf, ilen); dalrpc_sendwait(h); ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_6); uint32_t dalrpc_fcn_7(uint32_t ddi_idx, void *handle, const void *ibuf, uint32_t ilen, void *obuf, uint32_t olen, uint32_t *oalen) { struct daldevice_handle *h = handle; uint32_t ret; int param_idx; if ((ilen + 8) > DALRPC_MAX_PARAMS_SIZE || (olen + 4) > DALRPC_MAX_PARAMS_SIZE) return -EINVAL; if (!client_exists(h)) return -EINVAL; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, 0); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8 + ROUND_BUFLEN(ilen); h->msg.hdr.proto_id = 7; h->msg.param[0] = ilen; memcpy(&h->msg.param[1], ibuf, ilen); param_idx = (ROUND_BUFLEN(ilen) / 4) + 1; h->msg.param[param_idx] = olen; dalrpc_sendwait(h); if (h->msg.param[0] == DALRPC_SUCCESS) { if (h->msg.param[1] > olen) { mutex_unlock(&h->client_lock); return -EIO; } *oalen = h->msg.param[1]; memcpy(obuf, &h->msg.param[2], h->msg.param[1]); } ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_7); uint32_t dalrpc_fcn_8(uint32_t ddi_idx, void *handle, const void *ibuf, uint32_t ilen, void *obuf, uint32_t olen) { struct daldevice_handle *h = handle; uint32_t ret; int param_idx; if ((ilen + 8) > DALRPC_MAX_PARAMS_SIZE || (olen + 4) > DALRPC_MAX_PARAMS_SIZE) return -EINVAL; if (!client_exists(h)) return -EINVAL; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, 0); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8 + ROUND_BUFLEN(ilen); h->msg.hdr.proto_id = 8; h->msg.param[0] = ilen; memcpy(&h->msg.param[1], ibuf, ilen); param_idx = (ROUND_BUFLEN(ilen) / 4) + 1; h->msg.param[param_idx] = olen; dalrpc_sendwait(h); if (h->msg.param[0] == DALRPC_SUCCESS) { if (h->msg.param[1] > olen) { mutex_unlock(&h->client_lock); return -EIO; } memcpy(obuf, &h->msg.param[2], h->msg.param[1]); } ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_8); uint32_t dalrpc_fcn_9(uint32_t ddi_idx, void *handle, void *obuf, uint32_t olen) { struct daldevice_handle *h = handle; uint32_t ret; if ((olen + 4) > DALRPC_MAX_PARAMS_SIZE) return -EINVAL; if (!client_exists(h)) return -EINVAL; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, 0); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4; h->msg.hdr.proto_id = 9; h->msg.param[0] = olen; dalrpc_sendwait(h); if (h->msg.param[0] == DALRPC_SUCCESS) { if (h->msg.param[1] > olen) { mutex_unlock(&h->client_lock); return -EIO; } memcpy(obuf, &h->msg.param[2], h->msg.param[1]); } ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_9); uint32_t dalrpc_fcn_10(uint32_t ddi_idx, void *handle, uint32_t s1, const void *ibuf, uint32_t ilen, void *obuf, uint32_t olen, uint32_t *oalen) { struct daldevice_handle *h = handle; uint32_t ret; int param_idx; if ((ilen + 12) > DALRPC_MAX_PARAMS_SIZE || (olen + 4) > DALRPC_MAX_PARAMS_SIZE) return -EINVAL; if (!client_exists(h)) return -EINVAL; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, 0); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 12 + ROUND_BUFLEN(ilen); h->msg.hdr.proto_id = 10; h->msg.param[0] = s1; h->msg.param[1] = ilen; memcpy(&h->msg.param[2], ibuf, ilen); param_idx = (ROUND_BUFLEN(ilen) / 4) + 2; h->msg.param[param_idx] = olen; dalrpc_sendwait(h); if (h->msg.param[0] == DALRPC_SUCCESS) { if (h->msg.param[1] > olen) { mutex_unlock(&h->client_lock); return -EIO; } *oalen = h->msg.param[1]; memcpy(obuf, &h->msg.param[2], h->msg.param[1]); } ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_10); uint32_t dalrpc_fcn_11(uint32_t ddi_idx, void *handle, uint32_t s1, void *obuf, uint32_t olen) { struct daldevice_handle *h = handle; uint32_t ret; if ((olen + 4) > DALRPC_MAX_PARAMS_SIZE) return -EINVAL; if (!client_exists(h)) return -EINVAL; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, 0); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8; h->msg.hdr.proto_id = 11; h->msg.param[0] = s1; h->msg.param[1] = olen; dalrpc_sendwait(h); if (h->msg.param[0] == DALRPC_SUCCESS) { if (h->msg.param[1] > olen) { mutex_unlock(&h->client_lock); return -EIO; } memcpy(obuf, &h->msg.param[2], h->msg.param[1]); } ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_11); uint32_t dalrpc_fcn_12(uint32_t ddi_idx, void *handle, uint32_t s1, void *obuf, uint32_t olen, uint32_t *oalen) { struct daldevice_handle *h = handle; uint32_t ret; if ((olen + 4) > DALRPC_MAX_PARAMS_SIZE) return -EINVAL; if (!client_exists(h)) return -EINVAL; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, 0); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8; h->msg.hdr.proto_id = 12; h->msg.param[0] = s1; h->msg.param[1] = olen; dalrpc_sendwait(h); if (h->msg.param[0] == DALRPC_SUCCESS) { if (h->msg.param[1] > olen) { mutex_unlock(&h->client_lock); return -EIO; } *oalen = h->msg.param[1]; memcpy(obuf, &h->msg.param[2], h->msg.param[1]); } ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_12); uint32_t dalrpc_fcn_13(uint32_t ddi_idx, void *handle, const void *ibuf, uint32_t ilen, const void *ibuf2, uint32_t ilen2, void *obuf, uint32_t olen) { struct daldevice_handle *h = handle; uint32_t ret; int param_idx; if ((ilen + ilen2 + 12) > DALRPC_MAX_PARAMS_SIZE || (olen + 4) > DALRPC_MAX_PARAMS_SIZE) return -EINVAL; if (!client_exists(h)) return -EINVAL; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, 0); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 12 + ROUND_BUFLEN(ilen) + ROUND_BUFLEN(ilen2); h->msg.hdr.proto_id = 13; h->msg.param[0] = ilen; memcpy(&h->msg.param[1], ibuf, ilen); param_idx = (ROUND_BUFLEN(ilen) / 4) + 1; h->msg.param[param_idx++] = ilen2; memcpy(&h->msg.param[param_idx], ibuf2, ilen2); param_idx += (ROUND_BUFLEN(ilen2) / 4); h->msg.param[param_idx] = olen; dalrpc_sendwait(h); if (h->msg.param[0] == DALRPC_SUCCESS) { if (h->msg.param[1] > olen) { mutex_unlock(&h->client_lock); return -EIO; } memcpy(obuf, &h->msg.param[2], h->msg.param[1]); } ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_13); uint32_t dalrpc_fcn_14(uint32_t ddi_idx, void *handle, const void *ibuf, uint32_t ilen, void *obuf, uint32_t olen, void *obuf2, uint32_t olen2, uint32_t *oalen2) { struct daldevice_handle *h = handle; uint32_t ret; int param_idx; if ((ilen + 12) > DALRPC_MAX_PARAMS_SIZE || (olen + olen2 + 8) > DALRPC_MAX_PARAMS_SIZE) return -EINVAL; if (!client_exists(h)) return -EINVAL; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, 0); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 12 + ROUND_BUFLEN(ilen); h->msg.hdr.proto_id = 14; h->msg.param[0] = ilen; memcpy(&h->msg.param[1], ibuf, ilen); param_idx = (ROUND_BUFLEN(ilen) / 4) + 1; h->msg.param[param_idx++] = olen; h->msg.param[param_idx] = olen2; dalrpc_sendwait(h); if (h->msg.param[0] == DALRPC_SUCCESS) { if (h->msg.param[1] > olen) { mutex_unlock(&h->client_lock); return -EIO; } param_idx = (ROUND_BUFLEN(h->msg.param[1]) / 4) + 2; if (h->msg.param[param_idx] > olen2) { mutex_unlock(&h->client_lock); return -EIO; } memcpy(obuf, &h->msg.param[2], h->msg.param[1]); memcpy(obuf2, &h->msg.param[param_idx + 1], h->msg.param[param_idx]); *oalen2 = h->msg.param[param_idx]; } ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_14); uint32_t dalrpc_fcn_15(uint32_t ddi_idx, void *handle, const void *ibuf, uint32_t ilen, const void *ibuf2, uint32_t ilen2, void *obuf, uint32_t olen, uint32_t *oalen, void *obuf2, uint32_t olen2) { struct daldevice_handle *h = handle; uint32_t ret; int param_idx; if ((ilen + ilen2 + 16) > DALRPC_MAX_PARAMS_SIZE || (olen + olen2 + 8) > DALRPC_MAX_PARAMS_SIZE) return -EINVAL; if (!client_exists(h)) return -EINVAL; mutex_lock(&h->client_lock); dalrpc_ddi_prologue(ddi_idx, h, 0); h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 16 + ROUND_BUFLEN(ilen) + ROUND_BUFLEN(ilen2); h->msg.hdr.proto_id = 15; h->msg.param[0] = ilen; memcpy(&h->msg.param[1], ibuf, ilen); param_idx = (ROUND_BUFLEN(ilen) / 4) + 1; h->msg.param[param_idx++] = ilen2; memcpy(&h->msg.param[param_idx], ibuf2, ilen2); param_idx += (ROUND_BUFLEN(ilen2) / 4); h->msg.param[param_idx++] = olen; h->msg.param[param_idx] = olen2; dalrpc_sendwait(h); if (h->msg.param[0] == DALRPC_SUCCESS) { if (h->msg.param[1] > olen) { mutex_unlock(&h->client_lock); return -EIO; } param_idx = (ROUND_BUFLEN(h->msg.param[1]) / 4) + 2; if (h->msg.param[param_idx] > olen2) { mutex_unlock(&h->client_lock); return -EIO; } memcpy(obuf, &h->msg.param[2], h->msg.param[1]); memcpy(obuf2, &h->msg.param[param_idx + 1], h->msg.param[param_idx]); *oalen = h->msg.param[1]; } ret = h->msg.param[0]; mutex_unlock(&h->client_lock); return ret; } EXPORT_SYMBOL(dalrpc_fcn_15); void *dalrpc_alloc_event(void *handle) { struct daldevice_handle *h; struct dalrpc_event_handle *ev; h = (struct daldevice_handle *)handle; if (!client_exists(h)) return NULL; ev = kmalloc(sizeof(struct dalrpc_event_handle), GFP_KERNEL); if (!ev) return NULL; ev->flag = 0; spin_lock_init(&ev->lock); mutex_lock(&h->port->event_list_lock); list_add(&ev->list, &h->port->event_list); mutex_unlock(&h->port->event_list_lock); return ev; } EXPORT_SYMBOL(dalrpc_alloc_event); void *dalrpc_alloc_cb(void *handle, void (*fn)(void *, uint32_t, void *, uint32_t), void *context) { struct daldevice_handle *h; struct dalrpc_cb_handle *cb; h = (struct daldevice_handle *)handle; if (!client_exists(h)) return NULL; cb = kmalloc(sizeof(struct dalrpc_cb_handle), GFP_KERNEL); if (!cb) return NULL; cb->fn = fn; cb->context = context; mutex_lock(&h->port->cb_list_lock); list_add(&cb->list, &h->port->cb_list); mutex_unlock(&h->port->cb_list_lock); return cb; } EXPORT_SYMBOL(dalrpc_alloc_cb); void dalrpc_dealloc_event(void *handle, void *ev_h) { struct daldevice_handle *h; struct dalrpc_event_handle *ev; h = (struct daldevice_handle *)handle; ev = (struct dalrpc_event_handle *)ev_h; mutex_lock(&h->port->event_list_lock); list_del(&ev->list); mutex_unlock(&h->port->event_list_lock); kfree(ev); } EXPORT_SYMBOL(dalrpc_dealloc_event); void dalrpc_dealloc_cb(void *handle, void *cb_h) { struct daldevice_handle *h; struct dalrpc_cb_handle *cb; h = (struct daldevice_handle *)handle; cb = (struct dalrpc_cb_handle *)cb_h; mutex_lock(&h->port->cb_list_lock); list_del(&cb->list); mutex_unlock(&h->port->cb_list_lock); kfree(cb); } EXPORT_SYMBOL(dalrpc_dealloc_cb); static int event_occurred(int num_events, struct dalrpc_event_handle **events, int *occurred) { int i; for (i = 0; i < num_events; i++) { spin_lock(&events[i]->lock); if (events[i]->flag) { events[i]->flag = 0; spin_unlock(&events[i]->lock); *occurred = i; return 1; } spin_unlock(&events[i]->lock); } return 0; } int dalrpc_event_wait_multiple(int num, void **ev_h, int timeout) { struct dalrpc_event_handle **events; int ret, occurred; events = (struct dalrpc_event_handle **)ev_h; if (timeout == DALRPC_TIMEOUT_INFINITE) { wait_event(event_wq, event_occurred(num, events, &occurred)); return occurred; } ret = wait_event_timeout(event_wq, event_occurred(num, events, &occurred), timeout); if (ret > 0) return occurred; else return -ETIMEDOUT; } EXPORT_SYMBOL(dalrpc_event_wait_multiple);