1 #include <linux/config.h>
2 #include <linux/version.h>
4 #include <linux/module.h>
6 #include <linux/kernel.h>
7 #include <linux/init.h>
8 #include <linux/fcntl.h>
9 #include <linux/poll.h>
10 #include <linux/sched.h>
11 #include <linux/types.h>
12 #include <linux/slab.h>
13 #include <linux/delay.h>
14 #include <asm/system.h>
15 #include <asm-generic/smplock.h>
16 #include <asm/uaccess.h> // verify_area, copy_from_user, ...
18 #include <linux/miscdevice.h>
20 #include <mxhw_crypto_driver.h>
24 static DECLARE_COMPLETION(dispatcher_exited
);
28 hexit(char *msg
, u_char
*d
, int len
)
33 DBG("[%d] %s ",len
, msg
);
35 for (i
=0; i
< len
; i
+=4)
36 DBG("%08x ",*(u_int
*)(d
+i
));
42 /* free up a context buffer */
44 mxhw_crypto_qmctx_free(QMBCTX
*c
)
49 if (&c
->imbuf
) mxhw_crypto_engine_dma_mfree(&c
->imbuf
);
50 if (&c
->ombuf
) mxhw_crypto_engine_dma_mfree(&c
->ombuf
);
55 /* allocate a context buffer */
57 mxhw_crypto_qmctx_init(u32 d_size
)
59 QMBCTX
*c
= (QMBCTX
*) kmalloc(sizeof(QMBCTX
), GFP_KERNEL
);
63 memset(c
, 0, sizeof(QMBCTX
));
64 if (mxhw_crypto_engine_dma_malloc(&c
->imbuf
, d_size
)!=0 ||
65 mxhw_crypto_engine_dma_malloc(&c
->ombuf
, d_size
)!=0)
67 mxhw_crypto_qmctx_free(c
);
73 /* free up a queue of context buffers which are linked list */
75 mxhw_crypto_qmptr_free(QMBPTR
*q
)
81 for(c
=q
->head
;c
!=NULL
;c
= c
->next
)
82 mxhw_crypto_qmctx_free(c
);
86 /* new a queue with a lock and a wait queue */
88 mxhw_crypto_qmptr_init(u32 numq
, u32 d_size
)
94 if ((q
=(QMBPTR
*) kmalloc(sizeof(QMBPTR
), GFP_KERNEL
))==NULL
)
97 memset(q
, 0, sizeof(QMBPTR
));
98 /* apply lock when enqueue/dequeue */
99 spin_lock_init(&q
->lock
);
100 /* wake up waiting processes */
101 init_waitqueue_head(&q
->quew
);
103 /* allocate a linked list of context buffers */
104 for (i
=0; i
<numq
; i
++)
106 c
= mxhw_crypto_qmctx_init(d_size
);
109 mxhw_crypto_qmptr_free(q
);
112 ENQUEUE_CONTEXT(q
,c
);
118 mxhw_crypto_iocall_op(u32 cpid
, int type
)
120 static spinlock_t iolock
;
127 spin_lock_init(&iolock
);
133 for (i
=0;i
<MAX_CIPHER_CLIENTS
;i
++)
134 if (global
.pool
[i
].cpid
==cpid
)
141 case IOCTRL_OP_ALIVE
:
143 case IOCTRL_OP_CLOSE
:
146 /* if there are un-read contexts, return them to the free pool */
149 ENQUEUE_CONTEXT(global
.free_ctxq
, p
->outq
->head
);
150 p
->outq
->head
=p
->outq
->tail
=0; /* make sure */
152 p
->cpid
= 0; /* make this buffer available */
155 case IOCTRL_OP_CHKIN
:
158 if (p
->outq
==NULL
&& (p
->outq
=mxhw_crypto_qmptr_init(0,0))==NULL
)
162 p
->cpid
= (i
<<16|current
->pid
);
167 case IOCTRL_OP_FREEQ
:
168 for (i
=0;i
<MAX_CIPHER_CLIENTS
;i
++)
171 if (p
->outq
) mxhw_crypto_qmptr_free(p
->outq
);
175 spin_unlock(&iolock
);
179 /* a process that issues a cipher request would compete with any other
180 for resource (free context buffers)
182 static __inline__ ssize_t
183 mxhw_crypto_write(struct file
* filp
, const char * buf
, size_t count
, loff_t
*pos
)
185 IOCTRL
*ictx
= (IOCTRL
*)filp
->private_data
;
194 if (verify_area(VERIFY_READ
, buf
, count
))
197 tout
= (filp
->f_flags
& O_NONBLOCK
)? 10:0;
199 /* get a context buffer from the free pool */
200 DEQUEUE_CONTEXT(global
.free_ctxq
, qctx
, tout
, 0);
201 if (qctx
==NULL
) /* non-blocking mode? */
202 return ((tout
>0)? -EAGAIN
:-EFAULT
);
205 /* an oversize packet, realloc memory */
206 if (count
> IOMBUF_SIZE(mbuf
))
208 QMBCTX
*c
= mxhw_crypto_qmctx_init(count
);
210 { /* create a new one, free old one */
211 mxhw_crypto_qmctx_free(qctx
);
220 IOMBUF_DLEN(mbuf
) = count
; /* mark data length every time */
221 if (copy_from_user(IOMBUF_DATA(mbuf
),buf
,count
)==0)
223 /* make a copy of the cipher control, also mark the ownership */
224 memcpy(&qctx
->ictx
, ictx
, sizeof(IOCTRL
));
230 qptr
= (r
<0)? global
.free_ctxq
:global
.dspt_ctxq
;
231 /* enqueue this packet into the dispatcher list or into the free pool */
232 ENQUEUE_CONTEXT(qptr
, qctx
);
237 /* sequential access to the processed packets */
238 static __inline__ ssize_t
239 mxhw_crypto_read(struct file
*filp
, char *buf
, size_t count
, loff_t
*pos
)
241 IOCTRL
*ictx
= (IOCTRL
*) filp
->private_data
;
248 if (verify_area(VERIFY_WRITE
, buf
, count
))
251 tout
= filp
->f_flags
&O_NONBLOCK
? 10:0;
252 /* get the next packet from the caller's output list */
253 DEQUEUE_CONTEXT(ictx
->outq
, qctx
, tout
, 0);
255 return ((tout
>0)? -EAGAIN
:-EFAULT
);
256 else if (qctx
->status
== 0)
258 IOMBUF
*mbuf
=&qctx
->ombuf
;
260 /* copy back to the user space */
261 if (copy_to_user(buf
, IOMBUF_DATA(mbuf
), count
)==0)
264 /* no matter what, put it back to the free pool */
265 ENQUEUE_CONTEXT(global
.free_ctxq
,qctx
);
270 static __inline__
int
271 mxhw_crypto_ioctl(struct inode
*inode
, struct file
*filp
, unsigned int cmd
, unsigned long arg
)
273 IOCTRL
*ictx
= (IOCTRL
*)filp
->private_data
;
274 CIPHER
*info
= &ictx
->info
;
277 /* register a context and get a control id */
278 if (cmd
==IOCTLSET_MXCIPHER_INFO
)
280 if (copy_from_user(info
, (void *)arg
, sizeof(CIPHER
)) ||
281 info
->algo
>=MXCIPHER_ALGO_END
|| info
->mode
>=MXCIPHER_MODE_END
||
282 mxhw_crypto_engine_register(info
, &ictx
->cfid
)!=0)
284 else if (info
->mode
==MXCIPHER_MODE_OFB
|| info
->mode
==MXCIPHER_MODE_CTR
)
289 *(u_int
*)arg
= ictx
->pkt_num
;
295 QUEUE_LENGTH("ioctl", global
.free_ctxq
, n
);
304 /* we don't really open a file, instead we start a session */
306 mxhw_crypto_open(struct inode
*inode
, struct file
* filp
)
310 /* check in an available io caller */
311 if ((filp
->private_data
=mxhw_crypto_iocall_chkin(0))==NULL
)
318 mxhw_crypto_close(struct inode
*inode
, struct file
*filp
)
320 IOCTRL
*ictx
= (IOCTRL
*)filp
->private_data
;
324 /* for the engine part, close this context */
325 mxhw_crypto_engine_unregister(ictx
->cfid
);
326 /* release this io caller and make it available to others */
327 mxhw_crypto_iocall_close(ictx
->cpid
);
328 filp
->private_data
= NULL
;
334 mxhw_crypto_poll(struct file
*filp
, poll_table
*wait
)
336 IOCTRL
*ictx
= (IOCTRL
*) filp
->private_data
;
339 /* waiting for wrtieable */
340 poll_wait(filp
, &global
.free_ctxq
->quew
, wait
);
342 if (ictx
->outq
&& ictx
->outq
->head
) mask
|= POLLIN
| POLLRDNORM
;
343 if (global
.free_ctxq
->head
) mask
|= POLLOUT
| POLLWRNORM
;
347 static struct file_operations crypto_fops
= {
349 read
: mxhw_crypto_read
,
350 write
: mxhw_crypto_write
,
351 poll
: mxhw_crypto_poll
,
352 ioctl
: mxhw_crypto_ioctl
,
353 open
: mxhw_crypto_open
,
354 release
: mxhw_crypto_close
,
357 /* a thread such that it can be put into waiting queue */
359 mxhw_crypto_dispatch(void *base
)
365 /* This thread doesn't need any user-level access, so get rid of all our resources */
368 strcpy(current
->comm
, "mxcrypto_dispatcher");
372 while(global
.dspt_exit
==0)
374 DBG(KERN_INFO
"mxhw_driver: mxhw_crypto_dispatch pid %d\n", current
->pid
);
376 /* requests are dequeued only by the dispatcher, so peek the queue, if no item,
377 the dispatcher goes to sleep until an io caller wakes it up on behalf of
379 DEQUEUE_CONTEXT(global
.dspt_ctxq
, qctx
, 0, global
.dspt_exit
);
382 /* in case of system reboot */
383 if (signal_pending(current
))
388 qctx
->status
= mxhw_crypto_engine_perform(
395 ictx
= mxhw_crypto_iocall_alive(qctx
->ictx
.cpid
);
397 /* a packet has been processed, one of the two,
398 1. enqueue it to its caller's output list, wake up the caller, it is the demand
399 of the caller to pick up this packet
400 2. for some reason, the caller might be gone and no place to enqueue
402 qptr
= (ictx
)? ictx
->outq
:global
.free_ctxq
;
404 /* this is a macro */
405 ENQUEUE_CONTEXT(qptr
, qctx
);
408 complete_and_exit(&dispatcher_exited
, 0);
413 static struct miscdevice crypto_miscdev
=
422 mxhw_crypto_global_free(void)
424 mxhw_crypto_iocall_freeq(0);
425 if (global
.dspt_ctxq
) mxhw_crypto_qmptr_free(global
.dspt_ctxq
);
426 if (global
.free_ctxq
) mxhw_crypto_qmptr_free(global
.free_ctxq
);
430 * The driver boot-time initialization code!
433 mxhw_crypto_init(void)
437 memset(&global
, 0, sizeof(global_p
));
438 /* hook a device file */
440 global
.major_num
= CRYPTO_MAJOR
;
441 if ((num
= register_chrdev(CRYPTO_MAJOR
, CRYPTO_DEVNAME
, &crypto_fops
))<0)
443 printk(KERN_ERR
"Can't register char device at %s %d\n",
444 CRYPTO_DEVNAME
, global
.major_num
);
447 global
.major_num
= (CRYPTO_MAJOR
==0)? num
:CRYPTO_MAJOR
;
449 global
.major_num
= 10;
450 if ((num
= misc_register(&crypto_miscdev
)))
452 printk(KERN_ERR
"Can't register misc device at %s %d\n",
453 CRYPTO_DEVNAME
, global
.major_num
);
458 /* for a plug in engine to start up some procedures */
459 if (mxhw_crypto_engine_up()!=0)
461 printk("Fail to bring up engine\n");
464 /* a list of free context buffers (pre-allocated)
466 1. all io callers compete with
469 1. an io caller returns, fails to write/close, etc.
470 2. lock and wake up any other
472 global
.free_ctxq
= mxhw_crypto_qmptr_init(MAX_CIPHER_REQUESTS
,MAX_CIPHER_PACKET
);
473 /* a list of un-processed requests (empty initially)
475 1. the dispatcher gets a request context one-by-one.
478 1. any io caller makes a request
479 2. lock and wake up the dispatcher
481 global
.dspt_ctxq
= mxhw_crypto_qmptr_init(0,0);
482 if (!global
.free_ctxq
|| !global
.dspt_ctxq
)
484 printk("Fail to mem allocation\n");
487 /* finally, create a thread that dispatches requests */
488 global
.dspt_thrd
= kernel_thread(mxhw_crypto_dispatch
, NULL
, CLONE_SIGHAND
);
489 if (global
.dspt_thrd
<0)
491 printk("Fail to create the dispatcher\n");
494 printk(KERN_INFO
"(C)2004-2005 Moxa Inc. Crypto Driver at /dev/%s %d\n",
495 CRYPTO_DEVNAME
, global
.major_num
);
498 mxhw_crypto_global_free();
503 mxhw_crypto_cleanup(void)
505 printk(KERN_INFO
"Unloading crypto module\n");
506 /* notify the thread of an exit */
507 global
.dspt_exit
= 1;
508 /* the thread might sleep on waiting for requests */
509 WAKE_UP_QUEUE(global
.dspt_ctxq
);
510 /* wait until the thread jumps out its loop */
511 wait_for_completion(&dispatcher_exited
);
512 /* shut down the engine */
513 mxhw_crypto_engine_down();
515 mxhw_crypto_global_free();
516 /* unhook the device file */
518 unregister_chrdev(global
.major_num
, CRYPTO_DEVNAME
);
520 misc_deregister(&crypto_miscdev
);
522 printk(KERN_INFO
"Crypto module unloaded\n");
525 module_init(mxhw_crypto_init
);
526 module_exit(mxhw_crypto_cleanup
);
527 MODULE_LICENSE("GPL");