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 <linux/smp_lock.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
)
321 mxhw_crypto_close(struct inode
*inode
, struct file
*filp
)
323 IOCTRL
*ictx
= (IOCTRL
*)filp
->private_data
;
327 /* for the engine part, close this context */
328 mxhw_crypto_engine_unregister(ictx
->cfid
);
329 /* release this io caller and make it available to others */
330 mxhw_crypto_iocall_close(ictx
->cpid
);
331 filp
->private_data
= NULL
;
339 mxhw_crypto_poll(struct file
*filp
, poll_table
*wait
)
341 IOCTRL
*ictx
= (IOCTRL
*) filp
->private_data
;
344 /* waiting for wrtieable */
345 poll_wait(filp
, &global
.free_ctxq
->quew
, wait
);
347 if (ictx
->outq
&& ictx
->outq
->head
) mask
|= POLLIN
| POLLRDNORM
;
348 if (global
.free_ctxq
->head
) mask
|= POLLOUT
| POLLWRNORM
;
352 static struct file_operations crypto_fops
= {
354 read
: mxhw_crypto_read
,
355 write
: mxhw_crypto_write
,
356 poll
: mxhw_crypto_poll
,
357 ioctl
: mxhw_crypto_ioctl
,
358 open
: mxhw_crypto_open
,
359 release
: mxhw_crypto_close
,
362 /* a thread such that it can be put into waiting queue */
364 mxhw_crypto_dispatch(void *base
)
370 /* This thread doesn't need any user-level access, so get rid of all our resources */
372 daemonize("mxcryptod");
374 strcpy(current
->comm
, "mxcrypto_dispatcher");
378 while(global
.dspt_exit
==0)
380 DBG(KERN_INFO
"mxhw_driver: mxhw_crypto_dispatch pid %d\n", current
->pid
);
382 /* requests are dequeued only by the dispatcher, so peek the queue, if no item,
383 the dispatcher goes to sleep until an io caller wakes it up on behalf of
385 DEQUEUE_CONTEXT(global
.dspt_ctxq
, qctx
, 0, global
.dspt_exit
);
388 /* in case of system reboot */
389 if (signal_pending(current
))
394 qctx
->status
= mxhw_crypto_engine_perform(
401 ictx
= mxhw_crypto_iocall_alive(qctx
->ictx
.cpid
);
403 /* a packet has been processed, one of the two,
404 1. enqueue it to its caller's output list, wake up the caller, it is the demand
405 of the caller to pick up this packet
406 2. for some reason, the caller might be gone and no place to enqueue
408 qptr
= (ictx
)? ictx
->outq
:global
.free_ctxq
;
410 /* this is a macro */
411 ENQUEUE_CONTEXT(qptr
, qctx
);
414 complete_and_exit(&dispatcher_exited
, 0);
419 static struct miscdevice crypto_miscdev
=
428 mxhw_crypto_global_free(void)
430 mxhw_crypto_iocall_freeq(0);
431 if (global
.dspt_ctxq
) mxhw_crypto_qmptr_free(global
.dspt_ctxq
);
432 if (global
.free_ctxq
) mxhw_crypto_qmptr_free(global
.free_ctxq
);
436 * The driver boot-time initialization code!
439 mxhw_crypto_init(void)
443 memset(&global
, 0, sizeof(global_p
));
444 /* hook a device file */
446 global
.major_num
= CRYPTO_MAJOR
;
447 if ((num
= register_chrdev(CRYPTO_MAJOR
, CRYPTO_DEVNAME
, &crypto_fops
))<0)
449 printk(KERN_ERR
"Can't register char device at %s %d\n",
450 CRYPTO_DEVNAME
, global
.major_num
);
453 global
.major_num
= (CRYPTO_MAJOR
==0)? num
:CRYPTO_MAJOR
;
455 global
.major_num
= 10;
456 if ((num
= misc_register(&crypto_miscdev
)))
458 printk(KERN_ERR
"Can't register misc device at %s %d\n",
459 CRYPTO_DEVNAME
, global
.major_num
);
466 /* for a plug in engine to start up some procedures */
467 if (mxhw_crypto_engine_up()!=0)
469 printk("Fail to bring up engine\n");
472 /* a list of free context buffers (pre-allocated)
474 1. all io callers compete with
477 1. an io caller returns, fails to write/close, etc.
478 2. lock and wake up any other
480 global
.free_ctxq
= mxhw_crypto_qmptr_init(MAX_CIPHER_REQUESTS
,MAX_CIPHER_PACKET
);
481 /* a list of un-processed requests (empty initially)
483 1. the dispatcher gets a request context one-by-one.
486 1. any io caller makes a request
487 2. lock and wake up the dispatcher
489 global
.dspt_ctxq
= mxhw_crypto_qmptr_init(0,0);
490 if (!global
.free_ctxq
|| !global
.dspt_ctxq
)
492 printk("Fail to mem allocation\n");
495 /* finally, create a thread that dispatches requests */
496 global
.dspt_thrd
= kernel_thread(mxhw_crypto_dispatch
, NULL
, CLONE_SIGHAND
);
497 if (global
.dspt_thrd
<0)
499 printk("Fail to create the dispatcher\n");
504 printk(KERN_INFO
"(C)2004-2005 Moxa Inc. Crypto Driver at /dev/%s %d\n",
505 CRYPTO_DEVNAME
, global
.major_num
);
509 mxhw_crypto_global_free();
514 mxhw_crypto_cleanup(void)
516 printk(KERN_INFO
"Unloading crypto module\n");
517 /* notify the thread of an exit */
518 global
.dspt_exit
= 1;
519 /* the thread might sleep on waiting for requests */
520 WAKE_UP_QUEUE(global
.dspt_ctxq
);
521 /* wait until the thread jumps out its loop */
522 wait_for_completion(&dispatcher_exited
);
523 /* shut down the engine */
524 mxhw_crypto_engine_down();
526 mxhw_crypto_global_free();
527 /* unhook the device file */
529 unregister_chrdev(global
.major_num
, CRYPTO_DEVNAME
);
531 misc_deregister(&crypto_miscdev
);
533 printk(KERN_INFO
"Crypto module unloaded\n");
536 module_init(mxhw_crypto_init
);
537 module_exit(mxhw_crypto_cleanup
);
538 MODULE_LICENSE("GPL");