MOXA linux-2.6.x / linux-2.6.9-uc0 from sdlinux-moxaart.tgz
[linux-2.6.9-moxart.git] / drivers / char / mxhwenp / mxhw_crypto_driver.c
blobc7f907934f8612872fd11b80165aebe4eb882681
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 <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>
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;
313 MOD_INC_USE_COUNT;
314 return 0;
317 static int
318 mxhw_crypto_close(struct inode *inode, struct file *filp)
320 IOCTRL *ictx = (IOCTRL*)filp->private_data;
322 (void) inode;
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;
329 MOD_DEC_USE_COUNT;
330 return 0;
333 static u32
334 mxhw_crypto_poll(struct file *filp, poll_table *wait)
336 IOCTRL *ictx = (IOCTRL*) filp->private_data;
337 u32 mask=0;
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;
344 return mask;
347 static struct file_operations crypto_fops = {
348 owner: THIS_MODULE,
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 */
358 static int
359 mxhw_crypto_dispatch(void *base)
361 QMBCTX *qctx;
362 IOCTRL *ictx;
363 QMBPTR *qptr;
365 /* This thread doesn't need any user-level access, so get rid of all our resources */
366 lock_kernel();
367 daemonize();
368 strcpy(current->comm, "mxcrypto_dispatcher");
369 unlock_kernel();
370 reparent_to_init();
371 /* dispatch loop */
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
378 a request */
379 DEQUEUE_CONTEXT(global.dspt_ctxq, qctx, 0, global.dspt_exit);
380 if (qctx==NULL)
382 /* in case of system reboot */
383 if (signal_pending(current))
384 break;
385 continue;
387 /* do it */
388 qctx->status = mxhw_crypto_engine_perform(
389 qctx->ictx.cfid,
390 &qctx->ictx.info,
391 &qctx->imbuf,
392 &qctx->ombuf,
393 &global.dspt_exit);
394 /* check alive? */
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);
409 return 0;
412 #ifndef REGISTER_DEV
413 static struct miscdevice crypto_miscdev =
415 CRYPTO_MINOR,
416 CRYPTO_DEVNAME,
417 &crypto_fops
419 #endif
421 static void
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!
432 static int __init
433 mxhw_crypto_init(void)
435 int num=0;
437 memset(&global, 0, sizeof(global_p));
438 /* hook a device file */
439 #ifdef REGISTER_DEV
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);
445 return -EIO;
447 global.major_num = (CRYPTO_MAJOR==0)? num:CRYPTO_MAJOR;
448 #else
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);
454 return -EIO;
456 #endif
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");
462 return -EFAULT;
464 /* a list of free context buffers (pre-allocated)
465 dequeue:
466 1. all io callers compete with
467 2. lock and wait
468 enqueue:
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)
474 dequeue:
475 1. the dispatcher gets a request context one-by-one.
476 2. lock and wait
477 enqueue:
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");
485 goto err;
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");
492 goto err;
494 printk(KERN_INFO "(C)2004-2005 Moxa Inc. Crypto Driver at /dev/%s %d\n",
495 CRYPTO_DEVNAME, global.major_num);
496 return 0;
497 err:
498 mxhw_crypto_global_free();
499 return 1;
502 static void __exit
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();
514 /* free up queues */
515 mxhw_crypto_global_free();
516 /* unhook the device file */
517 #ifdef REGISTER_DEV
518 unregister_chrdev(global.major_num, CRYPTO_DEVNAME);
519 #else
520 misc_deregister(&crypto_miscdev);
521 #endif
522 printk(KERN_INFO "Crypto module unloaded\n");
525 module_init(mxhw_crypto_init);
526 module_exit(mxhw_crypto_cleanup);
527 MODULE_LICENSE("GPL");