MOXA linux-2.6.x / linux-2.6.9-uc0 from sdlinux-moxaart.tgz
[linux-2.6.9-moxart.git] / drivers / char / cipher / mxhw_crypto_driver.c
blobccf671d0c3cc52e1fe0cfced59831d214561388a
1 #include <linux/config.h>
2 #include <linux/version.h>
3 #ifdef MODULE
4 #include <linux/module.h>
5 #endif
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"
22 global_p global;
24 static DECLARE_COMPLETION(dispatcher_exited);
26 #ifdef DEBUG
27 void
28 hexit(char *msg, u_char *d, int len)
30 int i;
32 mdelay(10);
33 DBG("[%d] %s ",len, msg);
34 len = len>80? 80:len;
35 for (i=0; i< len; i+=4)
36 DBG("%08x ",*(u_int*)(d+i));
37 DBG("\n");
38 mdelay(10);
40 #endif
42 /* free up a context buffer */
43 static void
44 mxhw_crypto_qmctx_free(QMBCTX *c)
46 if (!c)
47 return;
49 if (&c->imbuf) mxhw_crypto_engine_dma_mfree(&c->imbuf);
50 if (&c->ombuf) mxhw_crypto_engine_dma_mfree(&c->ombuf);
52 kfree(c);
55 /* allocate a context buffer */
56 static QMBCTX*
57 mxhw_crypto_qmctx_init(u32 d_size)
59 QMBCTX *c = (QMBCTX*) kmalloc(sizeof(QMBCTX), GFP_KERNEL);
61 if (!c)
62 return NULL;
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);
68 c = NULL;
70 return c;
73 /* free up a queue of context buffers which are linked list */
74 static void
75 mxhw_crypto_qmptr_free(QMBPTR *q)
77 QMBCTX *c;
79 if (!q)
80 return;
81 for(c=q->head;c!=NULL;c = c->next)
82 mxhw_crypto_qmctx_free(c);
83 kfree(q);
86 /* new a queue with a lock and a wait queue */
87 static QMBPTR*
88 mxhw_crypto_qmptr_init(u32 numq, u32 d_size)
90 QMBPTR *q;
91 QMBCTX *c;
92 u32 i;
94 if ((q=(QMBPTR*) kmalloc(sizeof(QMBPTR), GFP_KERNEL))==NULL)
95 return 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);
107 if (c==NULL)
109 mxhw_crypto_qmptr_free(q);
110 return NULL;
112 ENQUEUE_CONTEXT(q,c);
114 return q;
117 IOCTRL*
118 mxhw_crypto_iocall_op(u32 cpid, int type)
120 static spinlock_t iolock;
121 static u32 first=1;
122 IOCTRL *p = NULL;
123 u32 i;
125 if (first)
127 spin_lock_init(&iolock);
128 first=0;
131 spin_lock(&iolock);
133 for (i=0;i<MAX_CIPHER_CLIENTS;i++)
134 if (global.pool[i].cpid==cpid)
136 p = &global.pool[i];
137 break;
139 switch(type)
141 case IOCTRL_OP_ALIVE:
142 break;
143 case IOCTRL_OP_CLOSE:
144 if (p)
146 /* if there are un-read contexts, return them to the free pool */
147 if (p->outq->head)
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 */
154 break;
155 case IOCTRL_OP_CHKIN:
156 if (p)
158 if (p->outq==NULL && (p->outq=mxhw_crypto_qmptr_init(0,0))==NULL)
159 p = NULL;
160 else
162 p->cpid = (i<<16|current->pid);
163 p->pkt_num=0;
166 break;
167 case IOCTRL_OP_FREEQ:
168 for (i=0;i<MAX_CIPHER_CLIENTS;i++)
170 p = &global.pool[i];
171 if (p->outq) mxhw_crypto_qmptr_free(p->outq);
173 break;
175 spin_unlock(&iolock);
176 return p;
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;
186 IOMBUF *mbuf;
187 QMBCTX *qctx;
188 QMBPTR *qptr;
189 u32 tout;
190 ssize_t r=0;
192 (void) pos;
194 if (verify_area(VERIFY_READ, buf, count))
195 return -EFAULT;
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);
204 mbuf = &qctx->imbuf;
205 /* an oversize packet, realloc memory */
206 if (count > IOMBUF_SIZE(mbuf))
208 QMBCTX *c = mxhw_crypto_qmctx_init(count);
209 if (c!=NULL)
210 { /* create a new one, free old one */
211 mxhw_crypto_qmctx_free(qctx);
212 qctx = c;
214 else
215 r = -ENOMEM;
217 if (r==0)
219 /* dlen+ilen */
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));
225 r = count;
227 else
228 r = -EFAULT;
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);
234 return r;
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;
242 QMBCTX *qctx;
243 u32 tout;
244 ssize_t r = -EFAULT;
246 (void) pos;
248 if (verify_area(VERIFY_WRITE, buf, count))
249 return -EFAULT;
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);
254 if (qctx==NULL)
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)
262 r = count;
264 /* no matter what, put it back to the free pool */
265 ENQUEUE_CONTEXT(global.free_ctxq,qctx);
266 ictx->pkt_num++;
267 return r;
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;
275 int r=0;
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)
283 r=-EFAULT;
284 else if (info->mode==MXCIPHER_MODE_OFB || info->mode==MXCIPHER_MODE_CTR)
285 info->type=1;
287 else if(cmd==99)
289 *(u_int*)arg = ictx->pkt_num;
291 #ifdef OVERFLOW_TEST
292 else if(cmd==100)
294 u_int n;
295 QUEUE_LENGTH("ioctl", global.free_ctxq, n);
296 *(u_int*)arg = n;
298 #endif
299 else
300 r=-EINVAL;
301 return r;
304 /* we don't really open a file, instead we start a session */
305 static int
306 mxhw_crypto_open(struct inode *inode, struct file * filp)
308 (void) inode;
310 /* check in an available io caller */
311 if ((filp->private_data=mxhw_crypto_iocall_chkin(0))==NULL)
312 return -EAGAIN;
314 MOD_INC_USE_COUNT;
317 return 0;
320 static int
321 mxhw_crypto_close(struct inode *inode, struct file *filp)
323 IOCTRL *ictx = (IOCTRL*)filp->private_data;
325 (void) inode;
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;
333 MOD_DEC_USE_COUNT;
335 return 0;
338 static u32
339 mxhw_crypto_poll(struct file *filp, poll_table *wait)
341 IOCTRL *ictx = (IOCTRL*) filp->private_data;
342 u32 mask=0;
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;
349 return mask;
352 static struct file_operations crypto_fops = {
353 owner: THIS_MODULE,
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 */
363 static int
364 mxhw_crypto_dispatch(void *base)
366 QMBCTX *qctx;
367 IOCTRL *ictx;
368 QMBPTR *qptr;
370 /* This thread doesn't need any user-level access, so get rid of all our resources */
371 lock_kernel();
372 daemonize("mxcryptod");
374 strcpy(current->comm, "mxcrypto_dispatcher");
375 unlock_kernel();
376 reparent_to_init();
377 /* dispatch loop */
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
384 a request */
385 DEQUEUE_CONTEXT(global.dspt_ctxq, qctx, 0, global.dspt_exit);
386 if (qctx==NULL)
388 /* in case of system reboot */
389 if (signal_pending(current))
390 break;
391 continue;
393 /* do it */
394 qctx->status = mxhw_crypto_engine_perform(
395 qctx->ictx.cfid,
396 &qctx->ictx.info,
397 &qctx->imbuf,
398 &qctx->ombuf,
399 &global.dspt_exit);
400 /* check alive? */
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);
415 return 0;
418 #ifndef REGISTER_DEV
419 static struct miscdevice crypto_miscdev =
421 CRYPTO_MINOR,
422 CRYPTO_DEVNAME,
423 &crypto_fops
425 #endif
427 static void
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!
438 static int __init
439 mxhw_crypto_init(void)
441 int num=0;
443 memset(&global, 0, sizeof(global_p));
444 /* hook a device file */
445 #ifdef REGISTER_DEV
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);
451 return -EIO;
453 global.major_num = (CRYPTO_MAJOR==0)? num:CRYPTO_MAJOR;
454 #else
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);
460 return -EIO;
462 #endif
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");
470 return -EFAULT;
472 /* a list of free context buffers (pre-allocated)
473 dequeue:
474 1. all io callers compete with
475 2. lock and wait
476 enqueue:
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)
482 dequeue:
483 1. the dispatcher gets a request context one-by-one.
484 2. lock and wait
485 enqueue:
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");
493 goto err;
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");
500 goto err;
504 printk(KERN_INFO "(C)2004-2005 Moxa Inc. Crypto Driver at /dev/%s %d\n",
505 CRYPTO_DEVNAME, global.major_num);
506 return 0;
507 err:
509 mxhw_crypto_global_free();
510 return 1;
513 static void __exit
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();
525 /* free up queues */
526 mxhw_crypto_global_free();
527 /* unhook the device file */
528 #ifdef REGISTER_DEV
529 unregister_chrdev(global.major_num, CRYPTO_DEVNAME);
530 #else
531 misc_deregister(&crypto_miscdev);
532 #endif
533 printk(KERN_INFO "Crypto module unloaded\n");
536 module_init(mxhw_crypto_init);
537 module_exit(mxhw_crypto_cleanup);
538 MODULE_LICENSE("GPL");