blob: ba0306452913c71fa8ca4dc02c5ae54ea25247e5 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Qualcomm TrustZone communicator driver
2 *
3 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 and
7 * only version 2 as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#define KMSG_COMPONENT "TZCOM"
16#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
17
18#include <linux/kernel.h>
19#include <linux/slab.h>
20#include <linux/module.h>
21#include <linux/fs.h>
22#include <linux/platform_device.h>
23#include <linux/debugfs.h>
24#include <linux/cdev.h>
25#include <linux/uaccess.h>
26#include <linux/sched.h>
27#include <linux/list.h>
28#include <linux/mutex.h>
29#include <linux/android_pmem.h>
30#include <linux/io.h>
31#include <mach/scm.h>
32#include <mach/peripheral-loader.h>
33#include <linux/tzcom.h>
34#include "tzcomi.h"
35
36#define TZCOM_DEV "tzcom"
37
38#define TZSCHEDULER_CMD_ID 1 /* CMD id of the trustzone scheduler */
39
40#undef PDEBUG
41#define PDEBUG(fmt, args...) pr_debug("%s(%i, %s): " fmt "\n", \
42 __func__, current->pid, current->comm, ## args)
43
44#undef PERR
45#define PERR(fmt, args...) pr_err("%s(%i, %s): " fmt "\n", \
46 __func__, current->pid, current->comm, ## args)
47
Sachin Shahf0b51cd2011-08-11 11:54:57 -070048#undef PWARN
49#define PWARN(fmt, args...) pr_warning("%s(%i, %s): " fmt "\n", \
50 __func__, current->pid, current->comm, ## args)
51
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052
53static struct class *driver_class;
54static dev_t tzcom_device_no;
55static struct cdev tzcom_cdev;
56
57static u8 *sb_in_virt;
58static s32 sb_in_phys;
59static size_t sb_in_length = 20 * SZ_1K;
60static u8 *sb_out_virt;
61static s32 sb_out_phys;
62static size_t sb_out_length = 20 * SZ_1K;
63
64static void *pil;
65
66static atomic_t svc_instance_ctr = ATOMIC_INIT(0);
67static DEFINE_MUTEX(sb_in_lock);
68static DEFINE_MUTEX(sb_out_lock);
69static DEFINE_MUTEX(send_cmd_lock);
70
71struct tzcom_callback_list {
72 struct list_head list;
73 struct tzcom_callback callback;
74};
75
76struct tzcom_registered_svc_list {
77 struct list_head list;
78 struct tzcom_register_svc_op_req svc;
79 wait_queue_head_t next_cmd_wq;
80 int next_cmd_flag;
81};
82
83struct tzcom_data_t {
84 struct list_head callback_list_head;
85 struct mutex callback_list_lock;
86 struct list_head registered_svc_list_head;
87 spinlock_t registered_svc_list_lock;
88 wait_queue_head_t cont_cmd_wq;
89 int cont_cmd_flag;
90 u32 handled_cmd_svc_instance_id;
Sachin Shahd7e02d42011-09-12 20:31:02 -070091 int abort;
92 wait_queue_head_t abort_wq;
93 atomic_t ioctl_count;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070094};
95
96static int tzcom_scm_call(const void *cmd_buf, size_t cmd_len,
97 void *resp_buf, size_t resp_len)
98{
99 return scm_call(SCM_SVC_TZSCHEDULER, TZSCHEDULER_CMD_ID,
100 cmd_buf, cmd_len, resp_buf, resp_len);
101}
102
103static s32 tzcom_virt_to_phys(u8 *virt)
104{
105 if (virt >= sb_in_virt &&
106 virt < (sb_in_virt + sb_in_length)) {
107 return sb_in_phys + (virt - sb_in_virt);
108 } else if (virt >= sb_out_virt &&
109 virt < (sb_out_virt + sb_out_length)) {
110 return sb_out_phys + (virt - sb_out_virt);
111 } else {
112 return virt_to_phys(virt);
113 }
114}
115
116static u8 *tzcom_phys_to_virt(s32 phys)
117{
118 if (phys >= sb_in_phys &&
119 phys < (sb_in_phys + sb_in_length)) {
120 return sb_in_virt + (phys - sb_in_phys);
121 } else if (phys >= sb_out_phys &&
122 phys < (sb_out_phys + sb_out_length)) {
123 return sb_out_virt + (phys - sb_out_phys);
124 } else {
125 return phys_to_virt(phys);
126 }
127}
128
129static int __tzcom_is_svc_unique(struct tzcom_data_t *data,
130 struct tzcom_register_svc_op_req svc)
131{
132 struct tzcom_registered_svc_list *ptr;
133 int unique = 1;
134 unsigned long flags;
135
136 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
137 list_for_each_entry(ptr, &data->registered_svc_list_head, list) {
138 if (ptr->svc.svc_id == svc.svc_id) {
139 PERR("Service id: %u is already registered",
140 ptr->svc.svc_id);
141 unique = 0;
142 break;
143 } else if (svc.cmd_id_low >= ptr->svc.cmd_id_low &&
144 svc.cmd_id_low <= ptr->svc.cmd_id_high) {
145 PERR("Cmd id low falls in the range of another"
146 "registered service");
147 unique = 0;
148 break;
149 } else if (svc.cmd_id_high >= ptr->svc.cmd_id_low &&
150 svc.cmd_id_high <= ptr->svc.cmd_id_high) {
151 PERR("Cmd id high falls in the range of another"
152 "registered service");
153 unique = 0;
154 break;
155 }
156 }
157 spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
158 return unique;
159}
160
161static int tzcom_register_service(struct tzcom_data_t *data, void __user *argp)
162{
163 int ret;
164 unsigned long flags;
165 struct tzcom_register_svc_op_req rcvd_svc;
166 struct tzcom_registered_svc_list *new_entry;
167
168 ret = copy_from_user(&rcvd_svc, argp, sizeof(rcvd_svc));
169
170 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700171 PERR("copy_from_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172 return ret;
173 }
174
175 PDEBUG("svc_id: %u, cmd_id_low: %u, cmd_id_high: %u",
176 rcvd_svc.svc_id, rcvd_svc.cmd_id_low,
177 rcvd_svc.cmd_id_high);
178 if (!__tzcom_is_svc_unique(data, rcvd_svc)) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700179 PERR("Provided service is not unique");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180 return -EINVAL;
181 }
182
183 rcvd_svc.instance_id = atomic_inc_return(&svc_instance_ctr);
184
185 ret = copy_to_user(argp, &rcvd_svc, sizeof(rcvd_svc));
186 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700187 PERR("copy_to_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700188 return ret;
189 }
190
191 new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
192 if (!new_entry) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700193 PERR("kmalloc failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700194 return -ENOMEM;
195 }
196 memcpy(&new_entry->svc, &rcvd_svc, sizeof(rcvd_svc));
197 new_entry->next_cmd_flag = 0;
198 init_waitqueue_head(&new_entry->next_cmd_wq);
199
200 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
201 list_add_tail(&new_entry->list, &data->registered_svc_list_head);
202 spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
203
204
205 return ret;
206}
207
208static int tzcom_unregister_service(struct tzcom_data_t *data,
209 void __user *argp)
210{
211 int ret = 0;
212 unsigned long flags;
213 struct tzcom_unregister_svc_op_req req;
Sachin Shahbae4ec02011-08-15 19:52:31 -0700214 struct tzcom_registered_svc_list *ptr, *next;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215 ret = copy_from_user(&req, argp, sizeof(req));
216 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700217 PERR("copy_from_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700218 return ret;
219 }
220
221 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
Sachin Shahbae4ec02011-08-15 19:52:31 -0700222 list_for_each_entry_safe(ptr, next, &data->registered_svc_list_head,
223 list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700224 if (req.svc_id == ptr->svc.svc_id &&
225 req.instance_id == ptr->svc.instance_id) {
226 wake_up_all(&ptr->next_cmd_wq);
227 list_del(&ptr->list);
228 kfree(ptr);
229 spin_unlock_irqrestore(&data->registered_svc_list_lock,
230 flags);
231 return 0;
232 }
233 }
234 spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
235
236 return -EINVAL;
237}
238
Sachin Shahd7e02d42011-09-12 20:31:02 -0700239static int __tzcom_is_cont_cmd(struct tzcom_data_t *data)
240{
241 int ret;
242 ret = (data->cont_cmd_flag != 0);
243 return ret || data->abort;
244}
245
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700246/**
247 * +---------+ +-----+ +-----------------+
248 * | TZCOM | | SCM | | TZCOM_SCHEDULER |
249 * +----+----+ +--+--+ +--------+--------+
250 * | | |
251 * | scm_call | |
252 * |------------------------------------->| |
253 * | cmd_buf = struct tzcom_command { | |
254 * | cmd_type, |------------------>|
255 * +------+------------- sb_in_cmd_addr, | |
256 * | | sb_in_cmd_len | |
257 * | | } | |
258 * | | resp_buf = struct tzcom_response { | |
259 * | cmd_status, | |
260 * | +---------- sb_in_rsp_addr, | |
261 * | | sb_in_rsp_len |<------------------|
262 * | | }
263 * | | struct tzcom_callback {---------+
264 * | | uint32_t cmd_id; |
265 * | | uint32_t sb_out_cb_data_len;|
266 * | +---------------+ uint32_t sb_out_cb_data_off;|
267 * | | } |
268 * | _________________________|_______________________________ |
269 * | +-----------------------+| +----------------------+ |
270 * +--->+ copy from req.cmd_buf |+>| copy to req.resp_buf | |
271 * +-----------------------+ +----------------------+ |
272 * _________________________________________________________ |
273 * INPUT SHARED BUFFER |
274 * +------------------------------------------------------------------------+
275 * | _________________________________________________________
276 * | +---------------------------------------------+
277 * +->| cmd_id | data_len | data_off | data... |
278 * +---------------------------------------------+
279 * |<------------>|copy to next_cmd.req_buf
280 * _________________________________________________________
281 * OUTPUT SHARED BUFFER
282 */
283static int tzcom_send_cmd(struct tzcom_data_t *data, void __user *argp)
284{
285 int ret = 0;
286 unsigned long flags;
287 u32 reqd_len_sb_in = 0;
288 u32 reqd_len_sb_out = 0;
289 struct tzcom_send_cmd_op_req req;
290 struct tzcom_command cmd;
291 struct tzcom_response resp;
292 struct tzcom_callback *next_callback;
293 void *cb_data = NULL;
294 struct tzcom_callback_list *new_entry;
295 struct tzcom_callback *cb;
296 size_t new_entry_len = 0;
297 struct tzcom_registered_svc_list *ptr_svc;
298
299 ret = copy_from_user(&req, argp, sizeof(req));
300 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700301 PERR("copy_from_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700302 return ret;
303 }
304
305 if (req.cmd_buf == NULL || req.resp_buf == NULL) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700306 PERR("cmd buffer or response buffer is null");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700307 return -EINVAL;
308 }
309
Sachin Shahbae4ec02011-08-15 19:52:31 -0700310 if (req.cmd_len <= 0 || req.resp_len <= 0 ||
311 req.cmd_len > sb_in_length || req.resp_len > sb_in_length) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700312 PERR("cmd buffer length or "
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700313 "response buffer length not valid");
314 return -EINVAL;
315 }
316 PDEBUG("received cmd_req.req: 0x%p",
317 req.cmd_buf);
318 PDEBUG("received cmd_req.rsp size: %u, ptr: 0x%p",
319 req.resp_len,
320 req.resp_buf);
321
322 reqd_len_sb_in = req.cmd_len + req.resp_len;
323 if (reqd_len_sb_in > sb_in_length) {
324 PDEBUG("Not enough memory to fit cmd_buf and "
325 "resp_buf. Required: %u, Available: %u",
326 reqd_len_sb_in, sb_in_length);
327 return -ENOMEM;
328 }
329
330 /* Copy req.cmd_buf to SB in and set req.resp_buf to SB in + cmd_len */
331 mutex_lock(&sb_in_lock);
332 PDEBUG("Before memcpy on sb_in");
333 memcpy(sb_in_virt, req.cmd_buf, req.cmd_len);
334 PDEBUG("After memcpy on sb_in");
335
336 /* cmd_type will always be a new here */
337 cmd.cmd_type = TZ_SCHED_CMD_NEW;
338 cmd.sb_in_cmd_addr = (u8 *) tzcom_virt_to_phys(sb_in_virt);
339 cmd.sb_in_cmd_len = req.cmd_len;
340
341 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
342 resp.sb_in_rsp_addr = (u8 *) tzcom_virt_to_phys(sb_in_virt +
343 req.cmd_len);
344 resp.sb_in_rsp_len = req.resp_len;
345
346 PDEBUG("before call tzcom_scm_call, cmd_id = : %u", req.cmd_id);
347 PDEBUG("before call tzcom_scm_call, sizeof(cmd) = : %u", sizeof(cmd));
348
Sachin Shahbae4ec02011-08-15 19:52:31 -0700349 ret = tzcom_scm_call((const void *) &cmd, sizeof(cmd),
350 &resp, sizeof(resp));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700351 mutex_unlock(&sb_in_lock);
352
Sachin Shahbae4ec02011-08-15 19:52:31 -0700353 if (ret) {
354 PERR("tzcom_scm_call failed with err: %d", ret);
355 return ret;
356 }
357
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700358 while (resp.cmd_status != TZ_SCHED_STATUS_COMPLETE) {
359 /*
360 * If cmd is incomplete, get the callback cmd out from SB out
361 * and put it on the list
362 */
363 PDEBUG("cmd_status is incomplete.");
364 next_callback = (struct tzcom_callback *)sb_out_virt;
365
366 mutex_lock(&sb_out_lock);
367 reqd_len_sb_out = sizeof(*next_callback)
368 + next_callback->sb_out_cb_data_len;
Sachin Shahbae4ec02011-08-15 19:52:31 -0700369 if (reqd_len_sb_out > sb_out_length ||
370 reqd_len_sb_out < sizeof(*next_callback) ||
371 next_callback->sb_out_cb_data_len > sb_out_length) {
372 PERR("Incorrect callback data length"
373 " Required: %u, Available: %u, Min: %u",
374 reqd_len_sb_out, sb_out_length,
375 sizeof(*next_callback));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700376 mutex_unlock(&sb_out_lock);
377 return -ENOMEM;
378 }
379
380 /* Assumption is cb_data_off is sizeof(tzcom_callback) */
381 new_entry_len = sizeof(*new_entry)
382 + next_callback->sb_out_cb_data_len;
383 new_entry = kmalloc(new_entry_len, GFP_KERNEL);
384 if (!new_entry) {
385 PERR("kmalloc failed");
386 mutex_unlock(&sb_out_lock);
387 return -ENOMEM;
388 }
389
390 cb = &new_entry->callback;
391 cb->cmd_id = next_callback->cmd_id;
392 cb->sb_out_cb_data_len = next_callback->sb_out_cb_data_len;
Sachin Shahbae4ec02011-08-15 19:52:31 -0700393 cb->sb_out_cb_data_off = sizeof(*cb);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394
395 cb_data = (u8 *)next_callback
396 + next_callback->sb_out_cb_data_off;
397 memcpy((u8 *)cb + cb->sb_out_cb_data_off, cb_data,
398 next_callback->sb_out_cb_data_len);
399 mutex_unlock(&sb_out_lock);
400
401 mutex_lock(&data->callback_list_lock);
402 list_add_tail(&new_entry->list, &data->callback_list_head);
403 mutex_unlock(&data->callback_list_lock);
404
405 /*
406 * We don't know which service can handle the command. so we
407 * wake up all blocking services and let them figure out if
408 * they can handle the given command.
409 */
410 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
411 list_for_each_entry(ptr_svc,
412 &data->registered_svc_list_head, list) {
413 ptr_svc->next_cmd_flag = 1;
414 wake_up_interruptible(&ptr_svc->next_cmd_wq);
415 }
416 spin_unlock_irqrestore(&data->registered_svc_list_lock,
417 flags);
418
419 PDEBUG("waking up next_cmd_wq and "
420 "waiting for cont_cmd_wq");
421 if (wait_event_interruptible(data->cont_cmd_wq,
Sachin Shahd7e02d42011-09-12 20:31:02 -0700422 __tzcom_is_cont_cmd(data))) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700423 PWARN("Interrupted: exiting send_cmd loop");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700424 return -ERESTARTSYS;
425 }
Sachin Shahd7e02d42011-09-12 20:31:02 -0700426
427 if (data->abort) {
428 PERR("Aborting driver");
429 return -ENODEV;
430 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431 data->cont_cmd_flag = 0;
432 cmd.cmd_type = TZ_SCHED_CMD_PENDING;
433 mutex_lock(&sb_in_lock);
Sachin Shahbae4ec02011-08-15 19:52:31 -0700434 ret = tzcom_scm_call((const void *) &cmd, sizeof(cmd), &resp,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700435 sizeof(resp));
436 mutex_unlock(&sb_in_lock);
Sachin Shahbae4ec02011-08-15 19:52:31 -0700437 if (ret) {
438 PERR("tzcom_scm_call failed with err: %d", ret);
439 return ret;
440 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700441 }
442
443 mutex_lock(&sb_in_lock);
444 resp.sb_in_rsp_addr = sb_in_virt + cmd.sb_in_cmd_len;
445 resp.sb_in_rsp_len = req.resp_len;
Sachin Shahc3f8dd32011-06-17 11:39:10 -0700446 memcpy(req.resp_buf, resp.sb_in_rsp_addr, resp.sb_in_rsp_len);
447 /* Zero out memory for security purpose */
448 memset(sb_in_virt, 0, reqd_len_sb_in);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449 mutex_unlock(&sb_in_lock);
450
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700451 PDEBUG("sending cmd_req.rsp "
452 "size: %u, ptr: 0x%p", req.resp_len,
453 req.resp_buf);
454 ret = copy_to_user(argp, &req, sizeof(req));
455 if (ret) {
456 PDEBUG("copy_to_user failed");
457 return ret;
458 }
459
460 return ret;
461}
462
463static struct tzcom_registered_svc_list *__tzcom_find_svc(
464 struct tzcom_data_t *data,
465 uint32_t instance_id)
466{
467 struct tzcom_registered_svc_list *entry;
468 unsigned long flags;
469
470 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
471 list_for_each_entry(entry,
472 &data->registered_svc_list_head, list) {
473 if (entry->svc.instance_id == instance_id)
474 break;
475 }
476 spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
477
478 return entry;
479}
480
481static int __tzcom_copy_cmd(struct tzcom_data_t *data,
482 struct tzcom_next_cmd_op_req *req,
483 struct tzcom_registered_svc_list *ptr_svc)
484{
485 int found = 0;
486 int ret = -EAGAIN;
Sachin Shahbae4ec02011-08-15 19:52:31 -0700487 struct tzcom_callback_list *entry, *next;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700488 struct tzcom_callback *cb;
489
490 PDEBUG("In here");
491 mutex_lock(&data->callback_list_lock);
492 PDEBUG("Before looping through cmd and svc lists.");
Sachin Shahbae4ec02011-08-15 19:52:31 -0700493 list_for_each_entry_safe(entry, next, &data->callback_list_head, list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700494 cb = &entry->callback;
495 if (req->svc_id == ptr_svc->svc.svc_id &&
496 req->instance_id == ptr_svc->svc.instance_id &&
497 cb->cmd_id >= ptr_svc->svc.cmd_id_low &&
498 cb->cmd_id <= ptr_svc->svc.cmd_id_high) {
499 PDEBUG("Found matching entry");
500 found = 1;
501 if (cb->sb_out_cb_data_len <= req->req_len) {
502 PDEBUG("copying cmd buffer %p to req "
503 "buffer %p, length: %u",
504 (u8 *)cb + cb->sb_out_cb_data_off,
505 req->req_buf, cb->sb_out_cb_data_len);
506 req->cmd_id = cb->cmd_id;
507 ret = copy_to_user(req->req_buf,
508 (u8 *)cb + cb->sb_out_cb_data_off,
509 cb->sb_out_cb_data_len);
510 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700511 PERR("copy_to_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700512 break;
513 }
514 list_del(&entry->list);
515 kfree(entry);
516 ret = 0;
517 } else {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700518 PERR("callback data buffer is "
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700519 "larger than provided buffer."
520 "Required: %u, Provided: %u",
521 cb->sb_out_cb_data_len,
522 req->req_len);
523 ret = -ENOMEM;
524 }
525 break;
526 }
527 }
528 PDEBUG("After looping through cmd and svc lists.");
529 mutex_unlock(&data->callback_list_lock);
530 return ret;
531}
532
Sachin Shahd7e02d42011-09-12 20:31:02 -0700533static int __tzcom_is_next_cmd(struct tzcom_data_t *data,
534 struct tzcom_registered_svc_list *svc)
535{
536 int ret;
537 ret = (svc->next_cmd_flag != 0);
538 return ret || data->abort;
539}
540
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541static int tzcom_read_next_cmd(struct tzcom_data_t *data, void __user *argp)
542{
543 int ret = 0;
544 struct tzcom_next_cmd_op_req req;
545 struct tzcom_registered_svc_list *this_svc;
546
547 ret = copy_from_user(&req, argp, sizeof(req));
548 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700549 PERR("copy_from_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700550 return ret;
551 }
552
553 if (req.instance_id > atomic_read(&svc_instance_ctr)) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700554 PERR("Invalid instance_id for the request");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700555 return -EINVAL;
556 }
557
558 if (!req.req_buf || req.req_len == 0) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700559 PERR("Invalid request buffer or buffer length");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700560 return -EINVAL;
561 }
562
563 PDEBUG("Before next_cmd loop");
564 this_svc = __tzcom_find_svc(data, req.instance_id);
565
566 while (1) {
567 PDEBUG("Before wait_event next_cmd.");
568 if (wait_event_interruptible(this_svc->next_cmd_wq,
Sachin Shahd7e02d42011-09-12 20:31:02 -0700569 __tzcom_is_next_cmd(data, this_svc))) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700570 PWARN("Interrupted: exiting wait_next_cmd loop");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700571 /* woken up for different reason */
572 return -ERESTARTSYS;
573 }
Sachin Shahd7e02d42011-09-12 20:31:02 -0700574
575 if (data->abort) {
576 PERR("Aborting driver");
577 return -ENODEV;
578 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700579 PDEBUG("After wait_event next_cmd.");
580 this_svc->next_cmd_flag = 0;
581
582 ret = __tzcom_copy_cmd(data, &req, this_svc);
583 if (ret == 0) {
584 PDEBUG("Successfully found svc for cmd");
585 data->handled_cmd_svc_instance_id = req.instance_id;
586 break;
587 } else if (ret == -ENOMEM) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700588 PERR("Not enough memory");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700589 return ret;
590 }
591 }
592 ret = copy_to_user(argp, &req, sizeof(req));
593 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700594 PERR("copy_to_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700595 return ret;
596 }
597 PDEBUG("copy_to_user is done.");
598 return ret;
599}
600
601static int tzcom_cont_cmd(struct tzcom_data_t *data, void __user *argp)
602{
603 int ret = 0;
604 struct tzcom_cont_cmd_op_req req;
605 ret = copy_from_user(&req, argp, sizeof(req));
606 if (ret) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700607 PERR("copy_from_user failed");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700608 return ret;
609 }
610
611 /*
612 * Only the svc instance that handled the cmd (in read_next_cmd method)
613 * can call continue cmd
614 */
615 if (data->handled_cmd_svc_instance_id != req.instance_id) {
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700616 PWARN("Only the service instance that handled the last "
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700617 "callback can continue cmd. "
618 "Expected: %u, Received: %u",
619 data->handled_cmd_svc_instance_id,
620 req.instance_id);
621 return -EINVAL;
622 }
623
624 if (req.resp_buf) {
625 mutex_lock(&sb_out_lock);
626 memcpy(sb_out_virt, req.resp_buf, req.resp_len);
627 mutex_unlock(&sb_out_lock);
628 }
629
630 data->cont_cmd_flag = 1;
631 wake_up_interruptible(&data->cont_cmd_wq);
632 return ret;
633}
634
Sachin Shahd7e02d42011-09-12 20:31:02 -0700635static int tzcom_abort(struct tzcom_data_t *data)
636{
637 int ret = 0;
638 unsigned long flags;
639 struct tzcom_registered_svc_list *lsvc, *nsvc;
640 if (data->abort) {
641 PERR("Already aborting");
642 return -EINVAL;
643 }
644
645 data->abort = 1;
646
647 PDEBUG("Waking up cont_cmd_wq");
648 wake_up_all(&data->cont_cmd_wq);
649
650 spin_lock_irqsave(&data->registered_svc_list_lock, flags);
651 PDEBUG("Before waking up service wait queues");
652 list_for_each_entry_safe(lsvc, nsvc,
653 &data->registered_svc_list_head, list) {
654 wake_up_all(&lsvc->next_cmd_wq);
655 }
656 spin_unlock_irqrestore(&data->registered_svc_list_lock, flags);
657
658 PDEBUG("ioctl_count before loop: %d", atomic_read(&data->ioctl_count));
659 while (atomic_read(&data->ioctl_count) > 0) {
660 if (wait_event_interruptible(data->abort_wq,
661 atomic_read(&data->ioctl_count) <= 0)) {
662 PERR("Interrupted from abort");
663 ret = -ERESTARTSYS;
664 break;
665 }
666 }
667 return ret;
668}
669
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700670static long tzcom_ioctl(struct file *file, unsigned cmd,
671 unsigned long arg)
672{
673 int ret = 0;
674 struct tzcom_data_t *tzcom_data = file->private_data;
675 void __user *argp = (void __user *) arg;
676 PDEBUG("enter tzcom_ioctl()");
Sachin Shahd7e02d42011-09-12 20:31:02 -0700677 if (tzcom_data->abort) {
678 PERR("Aborting tzcom driver");
679 return -ENODEV;
680 }
681
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700682 switch (cmd) {
683 case TZCOM_IOCTL_REGISTER_SERVICE_REQ: {
684 PDEBUG("ioctl register_service_req()");
Sachin Shahd7e02d42011-09-12 20:31:02 -0700685 atomic_inc(&tzcom_data->ioctl_count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700686 ret = tzcom_register_service(tzcom_data, argp);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700687 atomic_dec(&tzcom_data->ioctl_count);
688 wake_up_interruptible(&tzcom_data->abort_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700689 if (ret)
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700690 PERR("failed tzcom_register_service: %d", ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700691 break;
692 }
693 case TZCOM_IOCTL_UNREGISTER_SERVICE_REQ: {
694 PDEBUG("ioctl unregister_service_req()");
Sachin Shahd7e02d42011-09-12 20:31:02 -0700695 atomic_inc(&tzcom_data->ioctl_count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700696 ret = tzcom_unregister_service(tzcom_data, argp);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700697 atomic_dec(&tzcom_data->ioctl_count);
698 wake_up_interruptible(&tzcom_data->abort_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700699 if (ret)
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700700 PERR("failed tzcom_unregister_service: %d", ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700701 break;
702 }
703 case TZCOM_IOCTL_SEND_CMD_REQ: {
704 PDEBUG("ioctl send_cmd_req()");
705 /* Only one client allowed here at a time */
706 mutex_lock(&send_cmd_lock);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700707 atomic_inc(&tzcom_data->ioctl_count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700708 ret = tzcom_send_cmd(tzcom_data, argp);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700709 atomic_dec(&tzcom_data->ioctl_count);
710 wake_up_interruptible(&tzcom_data->abort_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700711 mutex_unlock(&send_cmd_lock);
712 if (ret)
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700713 PERR("failed tzcom_send_cmd: %d", ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700714 break;
715 }
716 case TZCOM_IOCTL_READ_NEXT_CMD_REQ: {
717 PDEBUG("ioctl read_next_cmd_req()");
Sachin Shahd7e02d42011-09-12 20:31:02 -0700718 atomic_inc(&tzcom_data->ioctl_count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700719 ret = tzcom_read_next_cmd(tzcom_data, argp);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700720 atomic_dec(&tzcom_data->ioctl_count);
721 wake_up_interruptible(&tzcom_data->abort_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700722 if (ret)
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700723 PERR("failed tzcom_read_next: %d", ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700724 break;
725 }
726 case TZCOM_IOCTL_CONTINUE_CMD_REQ: {
727 PDEBUG("ioctl continue_cmd_req()");
Sachin Shahd7e02d42011-09-12 20:31:02 -0700728 atomic_inc(&tzcom_data->ioctl_count);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700729 ret = tzcom_cont_cmd(tzcom_data, argp);
Sachin Shahd7e02d42011-09-12 20:31:02 -0700730 atomic_dec(&tzcom_data->ioctl_count);
731 wake_up_interruptible(&tzcom_data->abort_wq);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700732 if (ret)
Sachin Shahf0b51cd2011-08-11 11:54:57 -0700733 PERR("failed tzcom_cont_cmd: %d", ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700734 break;
735 }
Sachin Shahd7e02d42011-09-12 20:31:02 -0700736 case TZCOM_IOCTL_ABORT_REQ: {
737 PDEBUG("ioctl abort_req()");
738 ret = tzcom_abort(tzcom_data);
739 if (ret)
740 PERR("failed tzcom_abort: %d", ret);
741 break;
742 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700743 default:
744 return -EINVAL;
745 }
746 return ret;
747}
748
749static int tzcom_open(struct inode *inode, struct file *file)
750{
Sachin Shahbae4ec02011-08-15 19:52:31 -0700751 int ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700752 long pil_error;
753 struct tz_pr_init_sb_req_s sb_out_init_req;
754 struct tz_pr_init_sb_rsp_s sb_out_init_rsp;
755 void *rsp_addr_virt;
756 struct tzcom_command cmd;
757 struct tzcom_response resp;
758 struct tzcom_data_t *tzcom_data;
759
760 PDEBUG("In here");
761 if (pil == NULL) {
Stephen Boyd700819d2011-09-30 17:14:13 -0700762 pil = pil_get("tzapps");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700763 if (IS_ERR(pil)) {
764 PERR("Playready PIL image load failed");
765 pil_error = PTR_ERR(pil);
766 pil = NULL;
767 return pil_error;
768 }
Stephen Boyd700819d2011-09-30 17:14:13 -0700769 PDEBUG("tzapps image loaded successfully");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700770 }
771
772 sb_out_init_req.pr_cmd = TZ_SCHED_CMD_ID_INIT_SB_OUT;
773 sb_out_init_req.sb_len = sb_out_length;
774 sb_out_init_req.sb_ptr = tzcom_virt_to_phys(sb_out_virt);
775 PDEBUG("sb_out_init_req { pr_cmd: %d, sb_len: %u, "
776 "sb_ptr (phys): 0x%x }",
777 sb_out_init_req.pr_cmd,
778 sb_out_init_req.sb_len,
779 sb_out_init_req.sb_ptr);
780
781 mutex_lock(&sb_in_lock);
782 PDEBUG("Before memcpy on sb_in");
783 memcpy(sb_in_virt, &sb_out_init_req, sizeof(sb_out_init_req));
784 PDEBUG("After memcpy on sb_in");
785
786 /* It will always be a new cmd from this method */
787 cmd.cmd_type = TZ_SCHED_CMD_NEW;
788 cmd.sb_in_cmd_addr = (u8 *) tzcom_virt_to_phys(sb_in_virt);
789 cmd.sb_in_cmd_len = sizeof(sb_out_init_req);
790 PDEBUG("tzcom_command { cmd_type: %u, sb_in_cmd_addr: %p, "
791 "sb_in_cmd_len: %u }",
792 cmd.cmd_type, cmd.sb_in_cmd_addr, cmd.sb_in_cmd_len);
793
Sachin Shahf3b54ab2011-07-20 12:03:26 -0700794 resp.cmd_status = TZ_SCHED_STATUS_INCOMPLETE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700795
796 PDEBUG("Before scm_call for sb_init");
Sachin Shahbae4ec02011-08-15 19:52:31 -0700797 ret = tzcom_scm_call(&cmd, sizeof(cmd), &resp, sizeof(resp));
798 if (ret) {
799 PERR("tzcom_scm_call failed with err: %d", ret);
800 return ret;
801 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700802 PDEBUG("After scm_call for sb_init");
Sachin Shahbae4ec02011-08-15 19:52:31 -0700803
Sachin Shahf3b54ab2011-07-20 12:03:26 -0700804 PDEBUG("tzcom_response after scm cmd_status: %u", resp.cmd_status);
805 if (resp.cmd_status == TZ_SCHED_STATUS_COMPLETE) {
806 resp.sb_in_rsp_addr = (u8 *)cmd.sb_in_cmd_addr +
807 cmd.sb_in_cmd_len;
808 resp.sb_in_rsp_len = sizeof(sb_out_init_rsp);
809 PDEBUG("tzcom_response sb_in_rsp_addr: %p, sb_in_rsp_len: %u",
810 resp.sb_in_rsp_addr, resp.sb_in_rsp_len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700811 rsp_addr_virt = tzcom_phys_to_virt((unsigned long)
812 resp.sb_in_rsp_addr);
813 PDEBUG("Received response phys: %p, virt: %p",
Sachin Shahf3b54ab2011-07-20 12:03:26 -0700814 resp.sb_in_rsp_addr, rsp_addr_virt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700815 memcpy(&sb_out_init_rsp, rsp_addr_virt, resp.sb_in_rsp_len);
816 } else {
817 PERR("Error with SB initialization");
818 mutex_unlock(&sb_in_lock);
819 return -EPERM;
820 }
821 mutex_unlock(&sb_in_lock);
822
823 PDEBUG("sb_out_init_rsp { pr_cmd: %d, ret: %d }",
824 sb_out_init_rsp.pr_cmd, sb_out_init_rsp.ret);
825
826 if (sb_out_init_rsp.ret) {
827 PERR("sb_out_init_req failed: %d", sb_out_init_rsp.ret);
828 return -EPERM;
829 }
830
831 tzcom_data = kmalloc(sizeof(*tzcom_data), GFP_KERNEL);
832 if (!tzcom_data) {
833 PERR("kmalloc failed");
834 return -ENOMEM;
835 }
836 file->private_data = tzcom_data;
837
838 INIT_LIST_HEAD(&tzcom_data->callback_list_head);
839 mutex_init(&tzcom_data->callback_list_lock);
840
841 INIT_LIST_HEAD(&tzcom_data->registered_svc_list_head);
842 spin_lock_init(&tzcom_data->registered_svc_list_lock);
843
844 init_waitqueue_head(&tzcom_data->cont_cmd_wq);
845 tzcom_data->cont_cmd_flag = 0;
846 tzcom_data->handled_cmd_svc_instance_id = 0;
Sachin Shahd7e02d42011-09-12 20:31:02 -0700847 tzcom_data->abort = 0;
848 init_waitqueue_head(&tzcom_data->abort_wq);
849 atomic_set(&tzcom_data->ioctl_count, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700850 return 0;
851}
852
853static int tzcom_release(struct inode *inode, struct file *file)
854{
855 struct tzcom_data_t *tzcom_data = file->private_data;
856 struct tzcom_callback_list *lcb, *ncb;
857 struct tzcom_registered_svc_list *lsvc, *nsvc;
Sachin Shahd7e02d42011-09-12 20:31:02 -0700858 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700859 PDEBUG("In here");
860
Sachin Shahd7e02d42011-09-12 20:31:02 -0700861 if (!tzcom_data->abort) {
862 PDEBUG("Calling abort");
863 tzcom_abort(tzcom_data);
864 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700865
Sachin Shahd7e02d42011-09-12 20:31:02 -0700866 PDEBUG("Before removing callback list");
867 mutex_lock(&tzcom_data->callback_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700868 list_for_each_entry_safe(lcb, ncb,
869 &tzcom_data->callback_list_head, list) {
870 list_del(&lcb->list);
871 kfree(lcb);
872 }
Sachin Shahd7e02d42011-09-12 20:31:02 -0700873 mutex_unlock(&tzcom_data->callback_list_lock);
874 PDEBUG("After removing callback list");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700875
Sachin Shahd7e02d42011-09-12 20:31:02 -0700876 PDEBUG("Before removing svc list");
877 spin_lock_irqsave(&tzcom_data->registered_svc_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700878 list_for_each_entry_safe(lsvc, nsvc,
879 &tzcom_data->registered_svc_list_head, list) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700880 list_del(&lsvc->list);
881 kfree(lsvc);
882 }
Sachin Shahd7e02d42011-09-12 20:31:02 -0700883 spin_unlock_irqrestore(&tzcom_data->registered_svc_list_lock, flags);
884 PDEBUG("After removing svc list");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700885
Sachin Shahd7e02d42011-09-12 20:31:02 -0700886 PDEBUG("Freeing tzcom data");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700887 kfree(tzcom_data);
888 return 0;
889}
890
891static const struct file_operations tzcom_fops = {
892 .owner = THIS_MODULE,
893 .unlocked_ioctl = tzcom_ioctl,
894 .open = tzcom_open,
895 .release = tzcom_release
896};
897
898static int __init tzcom_init(void)
899{
900 int rc;
901 struct device *class_dev;
902
903 PDEBUG("Hello tzcom");
904
905 rc = alloc_chrdev_region(&tzcom_device_no, 0, 1, TZCOM_DEV);
906 if (rc < 0) {
907 PERR("alloc_chrdev_region failed %d", rc);
908 return rc;
909 }
910
911 driver_class = class_create(THIS_MODULE, TZCOM_DEV);
912 if (IS_ERR(driver_class)) {
913 rc = -ENOMEM;
914 PERR("class_create failed %d", rc);
915 goto unregister_chrdev_region;
916 }
917
918 class_dev = device_create(driver_class, NULL, tzcom_device_no, NULL,
919 TZCOM_DEV);
920 if (!class_dev) {
921 PERR("class_device_create failed %d", rc);
922 rc = -ENOMEM;
923 goto class_destroy;
924 }
925
926 cdev_init(&tzcom_cdev, &tzcom_fops);
927 tzcom_cdev.owner = THIS_MODULE;
928
929 rc = cdev_add(&tzcom_cdev, MKDEV(MAJOR(tzcom_device_no), 0), 1);
930 if (rc < 0) {
931 PERR("cdev_add failed %d", rc);
932 goto class_device_destroy;
933 }
934
935 sb_in_phys = pmem_kalloc(sb_in_length, PMEM_MEMTYPE_EBI1 |
936 PMEM_ALIGNMENT_4K);
937 if (IS_ERR((void *)sb_in_phys)) {
938 PERR("could not allocte in kernel pmem buffers for sb_in");
Sachin Shah545fbfa2011-09-27 14:37:16 -0700939 sb_in_phys = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700940 rc = -ENOMEM;
941 goto class_device_destroy;
942 }
943 PDEBUG("physical_addr for sb_in: 0x%x", sb_in_phys);
944
945 sb_in_virt = (u8 *) ioremap((unsigned long)sb_in_phys,
946 sb_in_length);
947 if (!sb_in_virt) {
948 PERR("Shared buffer IN allocation failed.");
949 rc = -ENOMEM;
950 goto class_device_destroy;
951 }
952 PDEBUG("sb_in virt address: %p, phys address: 0x%x",
953 sb_in_virt, tzcom_virt_to_phys(sb_in_virt));
954
955 sb_out_phys = pmem_kalloc(sb_out_length, PMEM_MEMTYPE_EBI1 |
956 PMEM_ALIGNMENT_4K);
957 if (IS_ERR((void *)sb_out_phys)) {
958 PERR("could not allocte in kernel pmem buffers for sb_out");
Sachin Shah545fbfa2011-09-27 14:37:16 -0700959 sb_out_phys = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700960 rc = -ENOMEM;
961 goto class_device_destroy;
962 }
963 PDEBUG("physical_addr for sb_out: 0x%x", sb_out_phys);
964
965 sb_out_virt = (u8 *) ioremap((unsigned long)sb_out_phys,
966 sb_out_length);
967 if (!sb_out_virt) {
968 PERR("Shared buffer OUT allocation failed.");
969 rc = -ENOMEM;
970 goto class_device_destroy;
971 }
972 PDEBUG("sb_out virt address: %p, phys address: 0x%x",
973 sb_out_virt, tzcom_virt_to_phys(sb_out_virt));
974
975 /* Initialized in tzcom_open */
976 pil = NULL;
977
978 return 0;
979
980class_device_destroy:
981 if (sb_in_virt)
982 iounmap(sb_in_virt);
983 if (sb_in_phys)
984 pmem_kfree(sb_in_phys);
985 if (sb_out_virt)
986 iounmap(sb_out_virt);
987 if (sb_out_phys)
988 pmem_kfree(sb_out_phys);
989 device_destroy(driver_class, tzcom_device_no);
990class_destroy:
991 class_destroy(driver_class);
992unregister_chrdev_region:
993 unregister_chrdev_region(tzcom_device_no, 1);
994 return rc;
995}
996
997static void __exit tzcom_exit(void)
998{
999 PDEBUG("Goodbye tzcom");
1000 if (sb_in_virt)
1001 iounmap(sb_in_virt);
1002 if (sb_in_phys)
1003 pmem_kfree(sb_in_phys);
1004 if (sb_out_virt)
1005 iounmap(sb_out_virt);
1006 if (sb_out_phys)
1007 pmem_kfree(sb_out_phys);
1008 if (pil != NULL) {
Stephen Boyd700819d2011-09-30 17:14:13 -07001009 pil_put(pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001010 pil = NULL;
1011 }
1012 device_destroy(driver_class, tzcom_device_no);
1013 class_destroy(driver_class);
1014 unregister_chrdev_region(tzcom_device_no, 1);
1015}
1016
1017
1018MODULE_LICENSE("GPL v2");
1019MODULE_AUTHOR("Sachin Shah <sachins@codeaurora.org>");
1020MODULE_DESCRIPTION("Qualcomm TrustZone Communicator");
1021MODULE_VERSION("1.00");
1022
1023module_init(tzcom_init);
1024module_exit(tzcom_exit);