misc: nv-sensorhub: tty line disc for sensorhub
[linux-3.10.git] / drivers / misc / nv-sensorhub-ldisc / sh_ldisc.c
1 /*
2  * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
16  * 02111-1307, USA
17  */
18
19 #include <linux/module.h>
20 #include <linux/tty.h>
21 #include <linux/slab.h>
22 #include <linux/circ_buf.h>
23 #include <linux/sched.h>
24 #include <linux/miscdevice.h>
25 #include <linux/crc32.h>
26 #include <linux/poll.h>
27
28 #include "sh_private.h"
29
30
31 /* NOTE: The order here matches the Message Type
32  * macros listed in sh_interface.h interface header file */
33 enum client_devs_num {
34         DEV_MCU = 0, /* NOTE: This is the Sensorhub MCU itself */
35
36         /* The following are the connected sensor devices */
37         DEV_CAM,
38         DEV_ACCEL,
39         DEV_GYRO,
40         DEV_MAG,
41         DEV_BARO,
42         NUM_DEVS
43 };
44
45 static char *client_devs_name[] = {
46         "shub_mcu", /* NOTE: This is the sensorhub itself */
47
48         /* The following are the connected sensor devices */
49         "shub_cam",
50         "shub_accel",
51         "shub_gyro",
52         "shub_mag",
53         "shub_baro"
54 };
55
56 struct client_dev {
57         /* Refer to parent data structure */
58         struct ldisc_priv *ld_data;
59
60         /* Misc dev. node */
61         struct miscdevice mdev;
62
63         /* Circular buffer where payloads are buffered */
64         char *read_buf; /* Alloc 32k */
65         int read_head;
66         int read_tail;
67         struct mutex read_buf_lock;
68
69         /* Allows blocking read. */
70         wait_queue_head_t readq;
71 };
72
73 struct ldisc_priv {
74         /* Refer to parent data structure */
75         struct tty_struct *tty;
76
77         /* Write lock to single underlying tty device */
78         struct mutex tty_write_lock;
79
80         /* Read buffer - to hold one pkt before de-muxing */
81         union {
82                 struct sensor_hub_pkt_t pkt;
83                 unsigned char    pkt_buf[sizeof(struct sensor_hub_pkt_t)];
84         };
85         int pkt_byte_idx;
86         int pyld_len;
87
88         /* Device nodes to de-multiplex data from sensorhub */
89         struct client_dev client_devs[NUM_DEVS];
90 };
91
92 /* We are using crc32 to validate packets */
93 #define CRC_SIZE                4
94
95 #define PYLD(pkt)               (*(uint32_t *)(pkt + \
96                                 sizeof(struct sensor_hub_pkt_header_t)))
97
98 #define CRC(pkt, pyld_sz)       (*(uint32_t *)(pkt + \
99                                 sizeof(struct sensor_hub_pkt_header_t) + \
100                                 pyld_sz))
101
102 #define CRC_DATA_SZ(pyld_sz)    (sizeof(struct sensor_hub_pkt_header_t) \
103                                  + pyld_sz)
104
105 #define PKT_SZ(pyld_sz)         (sizeof(struct sensor_hub_pkt_header_t) \
106                                  + pyld_sz + CRC_SIZE)
107
108 #if 0
109 uint32_t add_pkt_cnt;
110 uint32_t read_pkt_cnt;
111 #endif
112
113 /*****************************************************************************/
114
115 static int
116 pkt_type_valid(unsigned char byte)
117 {
118         if ((byte >= MSG_SENSOR_START && byte <= MSG_SENSOR_END))
119                 return 1;
120         return 0;
121 }
122
123 static int
124 pkt_payload_len(unsigned char type)
125 {
126         switch (type) {
127         case MSG_CAMERA:
128                 return sizeof(struct camera_payload_t);
129         case MSG_ACCEL:
130                 return sizeof(struct accel_payload_t);
131         case MSG_GYRO:
132                 return sizeof(struct gyro_payload_t);
133         case MSG_MAG:
134                 return sizeof(struct mag_payload_t);
135         case MSG_BARO:
136                 return sizeof(struct baro_payload_t);
137         case MSG_MCU:
138                 return sizeof(struct mcu_payload_t);
139         default:
140                 return -1;
141         }
142 }
143
144 static uint32_t
145 pkt_crc(char *pkt, int len)
146 {
147 #if 0
148         uint32_t crc;
149
150         crc = crc32_le(~0, pkt, len);
151
152         return crc;
153 #endif
154
155         /* FIX IT: Temp code until CRC module is coded up in fw */
156         uint32_t crc;
157
158         crc = 0xCAFEBABA;
159
160         return crc;
161 }
162
163 /*****************************************************************************/
164
165 static ssize_t
166 client_dev_write(struct file *file, const char __user *buffer,
167                  size_t count, loff_t *ppos)
168 {
169         struct client_dev *dev = file->private_data;
170         struct ldisc_priv *ld_data = dev->ld_data;
171         struct tty_struct *tty = ld_data->tty;
172         const char *b;
173         int c;
174         int retval = 0;
175         char pkt[sizeof(struct sensor_hub_pkt_t)];
176
177         if (count == 0)
178                 return 0;
179
180         /* Only writes to shub_mcu device is supported for now */
181         if (count != sizeof(struct mcu_payload_t))
182                 return 0;
183
184         if (mutex_lock_interruptible(&ld_data->tty_write_lock))
185                 return -EINTR;
186
187         pr_debug("sh_ldisc: write line to dev struct 0x%p\n", dev);
188
189         /* Make a packet and then send to Sensorhub */
190         /* Header */
191         ((struct sensor_hub_pkt_t *)pkt)->header.start = SENSOR_HUB_START;
192         ((struct sensor_hub_pkt_t *)pkt)->header.type = MSG_MCU;
193         /* Payload */
194         if (get_user(PYLD(pkt), buffer)) {
195                 pr_err("sh_ldisc: Copy from user-space failed.\n");
196                 return -EFAULT;
197         }
198
199         /* CRC */
200         CRC(pkt, sizeof(struct mcu_payload_t)) =
201             pkt_crc(pkt, CRC_DATA_SZ(sizeof(struct mcu_payload_t)));
202
203         b = pkt;
204         count = PKT_SZ(sizeof(struct mcu_payload_t));
205
206 #if 0
207         int idx = 0;
208         int dbg_cnt = count;
209         while (dbg_cnt--)
210                 pr_debug("sh_ldisc: write char: 0x%x\n", pkt[idx++]);
211 #endif
212
213         while (1) {
214                 set_current_state(TASK_INTERRUPTIBLE);
215                 if (signal_pending(current)) {
216                         retval = -ERESTARTSYS;
217                         break;
218                 }
219                 while (count > 0) {
220                         c = tty->ops->write(tty, b, count);
221                         if (c < 0) {
222                                 retval = c;
223                                 goto break_out;
224                         }
225                         if (!c)
226                                 break;
227                         b += c;
228                         count -= c;
229                 }
230                 if (!count)
231                         break;
232                 if (file->f_flags & O_NONBLOCK) {
233                         retval = -EAGAIN;
234                         break;
235                 }
236                 schedule();
237         }
238 break_out:
239
240         if (tty->ops->flush_chars)
241                 tty->ops->flush_chars(tty);
242
243         __set_current_state(TASK_RUNNING);
244         if (b - buffer != count && tty->fasync)
245                 set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
246
247         mutex_unlock(&ld_data->tty_write_lock);
248
249         return (b - buffer) ? b - buffer : retval;
250 }
251
252 static ssize_t
253 client_dev_read(struct file *file, char __user *buffer,
254                                 size_t count, loff_t *ppos)
255 {
256         struct client_dev *dev = file->private_data;
257         ssize_t size;
258         ssize_t msg_size;
259         ssize_t retval = 0;
260         char __user *b = buffer;
261         int type_idx;
262
263         if (mutex_lock_interruptible(&dev->read_buf_lock))
264                 return -ERESTARTSYS;
265
266         pr_debug("sh_ldisc: read line from dev struct 0x%p\n", dev);
267 #if 0
268         pr_debug("sh_ldisc: read pkt # %d at tail %d\n",
269                 read_pkt_cnt++, dev->read_tail);
270 #endif
271
272         while (CIRC_CNT(dev->read_head, dev->read_tail, N_TTY_BUF_SIZE) == 0) {
273                 /* nothing to read */
274                 mutex_unlock(&dev->read_buf_lock); /* release the lock */
275                 if (file->f_flags & O_NONBLOCK)
276                         return -EAGAIN;
277
278                 if (wait_event_interruptible(dev->readq,
279                         (CIRC_CNT(dev->read_head, dev->read_tail,
280                                   N_TTY_BUF_SIZE) != 0)))
281                         /* signal: tell fs layer to handle it */
282                         return -ERESTARTSYS;
283
284                 /* otherwise loop, but first reacquire the lock */
285                 if (mutex_lock_interruptible(&dev->read_buf_lock))
286                         return -ERESTARTSYS;
287         }
288
289         /* get the payload size */
290         type_idx = ((dev->read_tail + sizeof(char)) & (N_TTY_BUF_SIZE-1));
291         msg_size = pkt_payload_len(dev->read_buf[type_idx]);
292
293         if (msg_size > count)
294                 retval = -EINVAL;
295         else {
296                 dev->read_tail = dev->read_tail +
297                                  sizeof(struct sensor_hub_pkt_header_t);
298
299                 while (msg_size) {
300                         int c;
301
302                         c = dev->read_buf[dev->read_tail];
303                         dev->read_tail = ((dev->read_tail+1) &
304                                           (N_TTY_BUF_SIZE-1));
305                         msg_size--;
306
307                         if (put_user(c, b++)) {
308                                 b--;
309                                 /* Move read_tail to end of packet for sync */
310                                 dev->read_tail = ((dev->read_tail+msg_size) &
311                                                   (N_TTY_BUF_SIZE-1));
312                                 pr_err("sh_ldisc: Copy to user-space failed.\n");
313                                 retval = -EFAULT;
314                                 break;
315                         }
316                 }
317
318                 /* Move read_tail to past crc */
319                 dev->read_tail = ((dev->read_tail+CRC_SIZE) &
320                                   (N_TTY_BUF_SIZE-1));
321         }
322
323         mutex_unlock(&dev->read_buf_lock); /* release the lock */
324
325         size = b - buffer;
326         if (size)
327                 retval = size;
328
329         return retval;
330 }
331
332 static unsigned int client_dev_poll(struct file *file, poll_table *wait)
333 {
334         struct client_dev *dev = file->private_data;
335         unsigned int retval = 0;
336
337         poll_wait(file, &dev->readq, wait);
338
339         mutex_lock(&dev->read_buf_lock);
340         /* Check if there is data in dev buffer */
341         if (CIRC_CNT(dev->read_head, dev->read_tail, N_TTY_BUF_SIZE))
342                 retval |= POLLIN | POLLRDNORM;
343         mutex_unlock(&dev->read_buf_lock);
344
345         /* You can always write */
346         retval |= POLLOUT | POLLWRNORM;
347
348         return retval;
349 }
350
351 static int
352 client_dev_open(struct inode *inode, struct file *file)
353 {
354         struct miscdevice *md = file->private_data;
355         struct client_dev *dev;
356
357         pr_info("%s : %s\n", __func__, md->name);
358
359         dev = container_of(md, struct client_dev, mdev);
360
361         mutex_lock(&dev->read_buf_lock);
362         dev->read_head = dev->read_tail = 0;
363         mutex_unlock(&dev->read_buf_lock);
364
365         file->private_data = dev;
366         nonseekable_open(inode, file);
367
368         return 0;
369 }
370
371 static int
372 client_dev_release(struct inode *inode, struct file *file)
373 {
374         pr_info("%s\n", __func__);
375         return 0;
376 }
377
378 static const struct file_operations client_dev_fops = {
379         .owner      = THIS_MODULE,
380         .open       = client_dev_open,
381         .release    = client_dev_release,
382         .read       = client_dev_read,
383         .write      = client_dev_write,
384         .poll       = client_dev_poll,
385         .llseek     = no_llseek,
386 };
387
388 /*****************************************************************************/
389
390 static int
391 create_client_devs(struct ldisc_priv *ld_data)
392 {
393         int i;
394
395         for (i = 0; i < NUM_DEVS; i++) {
396                 struct client_dev *dev = &ld_data->client_devs[i];
397                 struct miscdevice *md = &dev->mdev;
398
399                 memset(dev, 0, sizeof(struct client_dev));
400
401                 dev->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
402                 if (!dev->read_buf) {
403                         pr_err("%s: out of memory\n", __func__);
404                         goto err_exit;
405                 } else {
406                         dev->ld_data = ld_data;
407                         mutex_init(&dev->read_buf_lock);
408                         init_waitqueue_head(&dev->readq);
409
410                         md->fops = &client_dev_fops;
411                         md->minor = MISC_DYNAMIC_MINOR;
412                         md->name = client_devs_name[i];
413
414                         if (misc_register(md) != 0) {
415                                 pr_err("%s: misc_register fail\n", __func__);
416                                 kfree(dev->read_buf);
417                                 goto err_exit;
418                         }
419                         pr_debug("sh_ldisc: init dev %s 0x%p\n",
420                                  client_devs_name[i], dev);
421                 }
422         }
423
424         return 1;
425
426 err_exit:
427         while (--i >= 0) {
428                 struct client_dev *dev = &ld_data->client_devs[i];
429                 struct miscdevice *md = &dev->mdev;
430
431                 if (misc_deregister(md) != 0)
432                         pr_err("%s: misc_deregister fail\n", __func__);
433
434                         kfree(dev->read_buf);
435         }
436
437         return 0;
438 }
439
440 static void
441 delete_client_devs(struct ldisc_priv *ld_data)
442 {
443         int i;
444
445         for (i = 0; i < NUM_DEVS; i++) {
446                 struct client_dev *dev = &ld_data->client_devs[i];
447                 struct miscdevice *md = &dev->mdev;
448
449                 wake_up_interruptible(&dev->readq);
450
451                 if (misc_deregister(md) != 0)
452                         pr_err("%s: misc_register fail\n", __func__);
453
454                 kfree(dev->read_buf);
455         }
456 }
457
458 /*****************************************************************************/
459
460 static void
461 client_dev_add_pkt(struct client_dev *dev, unsigned char *pkt, int count)
462 {
463         int buf_space_available;
464
465         mutex_lock(&dev->read_buf_lock);
466
467         pr_debug("sh_ldisc: add line to dev struct 0x%p\n", dev);
468 #if 0
469         pr_debug("sh_ldisc: add pkt # %d at head %d\n",
470                  add_pkt_cnt++, dev->read_head);
471 #endif
472         buf_space_available = CIRC_SPACE(dev->read_head, dev->read_tail,
473                                          N_TTY_BUF_SIZE);
474
475         if (buf_space_available > count) {
476                 int space = CIRC_SPACE_TO_END(dev->read_head, dev->read_tail,
477                                               N_TTY_BUF_SIZE);
478                 if (space > count) {
479                         memcpy(&dev->read_buf[dev->read_head], pkt, count);
480                 } else {
481                         memcpy(&dev->read_buf[dev->read_head], pkt, space);
482                         memcpy(&dev->read_buf[0], pkt + space, count - space);
483                 }
484                 dev->read_head += count;
485                 dev->read_head &= (N_TTY_BUF_SIZE - 1);
486                 wake_up_interruptible(&dev->readq);
487         } else {
488                 pr_err("sh_ldisc: Discard pkt due to lack of buffer space.\n");
489         }
490
491         mutex_unlock(&dev->read_buf_lock);
492 }
493
494 static inline void
495 sh_ldisc_parse_pkt(struct tty_struct *tty, unsigned char c)
496 {
497         struct ldisc_priv *ld_data = tty->disc_data;
498         uint32_t crc;
499
500         /* sanity check index */
501         if (ld_data->pkt_byte_idx >= sizeof(ld_data->pkt_buf)) {
502                 /* Reset if byte index longer than longest packet */
503                 ld_data->pkt_byte_idx = 0;
504         }
505
506         ld_data->pkt_buf[ld_data->pkt_byte_idx] = c;
507
508         switch (ld_data->pkt_byte_idx++) {
509         case 0:
510                 /* expecting Magic value */
511                 if (c != SENSOR_HUB_START) {
512                         ld_data->pkt_byte_idx = 0;
513                         pr_debug("sh_ldisc: msg start not recvd 0x%x\n", c);
514                 }
515                 break;
516
517         case 1:
518                 /* Expecting message type */
519                 if (!pkt_type_valid(c)) {
520                         pr_debug("sh_ldisc: msg type 0x%x not valid\n", c);
521                         ld_data->pkt_byte_idx = 0;
522                         break;
523                 }
524                 /* Calc payload len from msg. type */
525                 ld_data->pyld_len = pkt_payload_len(c);
526                 if (ld_data->pyld_len < 0) {
527                         pr_debug("sh_ldisc: msg len 0x%x not valid\n",
528                                  ld_data->pyld_len);
529                         ld_data->pkt_byte_idx = 0;
530                 }
531                 break;
532
533         default:
534                 /* Nothing to do until last byte has been received */
535                 if (ld_data->pkt_byte_idx ==
536                         sizeof(struct sensor_hub_pkt_header_t) +
537                                 ld_data->pyld_len + CRC_SIZE) {
538 #if 0
539                         /* For debugging */
540                         /* Print the accumulated packet content */
541                         int dbg_cnt = sizeof(struct sensor_hub_pkt_header_t) +
542                         ld_data->pyld_len + CRC_SIZE;
543                         int dbg_idx = 0;
544                         pr_debug("sh_ldisc: hdr len 0x%x\n",
545                                sizeof(struct sensor_hub_pkt_header_t));
546                         pr_debug("sh_ldisc: pyl len 0x%x\n",
547                                ld_data->pyld_len);
548                         pr_debug("sh_ldisc: crc len 0x%x\n",
549                                CRC_SIZE);
550                         pr_debug("sh_ldisc: pkt addr 0x%p\n",
551                                ld_data->pkt_buf);
552                         pr_debug("sh_ldisc: crc addr 0x%p\n",
553                                ld_data->pkt_buf +
554                                sizeof(struct sensor_hub_pkt_header_t) +
555                                ld_data->pyld_len);
556                         pr_debug("sh_ldisc: crc 0x%x\n",
557                                *(uint32_t *)(ld_data->pkt_buf +
558                                 sizeof(struct sensor_hub_pkt_header_t) +
559                                 ld_data->pyld_len));
560                         while (dbg_cnt--) {
561                                 pr_debug("sh_ldisc: pkt byte 0x%x\n",
562                                         ld_data->pkt_buf[dbg_idx++]);
563                         }
564 #endif
565                         /* validate packet crc */
566                         crc = pkt_crc(ld_data->pkt_buf,
567                                       CRC_DATA_SZ(ld_data->pyld_len));
568
569                         if (crc ==
570                             CRC(ld_data->pkt_buf, ld_data->pyld_len)) {
571
572                                 unsigned char type =
573                                     ld_data->pkt.header.type;
574                                 int dev = DEV_MCU;
575
576                                 /* If sensor */
577                                 if (MSG_SENSOR_START <= type &&
578                                     type <= MSG_SENSOR_END) {
579                                         dev = type;
580                                 }
581
582                                 /* Add the payload to the
583                                  * corresponding dev buffer */
584                                 client_dev_add_pkt(
585                                         &ld_data->client_devs[dev],
586                                         ld_data->pkt_buf,
587                                         ld_data->pkt_byte_idx);
588                         } else {
589                                 pr_err("sh_ldisc: crc not valid\n");
590                         }
591
592                         /* Packet de-muxed successfully or dropped.
593                          * Clear to start over. */
594                         ld_data->pkt_byte_idx = 0;
595                         ld_data->pyld_len = 0;
596                 }
597                 break;
598         }
599 }
600
601 static void
602 sh_ldisc_recv_from_tty(struct tty_struct *tty, const unsigned char *cp,
603                         char *fp, int count)
604 {
605         const unsigned char *p;
606         char *f, flags = TTY_NORMAL;
607         int i;
608         char    buf[64];
609
610         for (i = count, p = cp, f = fp; i; i--, p++) {
611                 if (f)
612                         flags = *f++;
613                 switch (flags) {
614                 case TTY_NORMAL:
615                         pr_debug("sh_ldisc: tty recv 0x%X\n", *p);
616                         sh_ldisc_parse_pkt(tty, *p);
617                         break;
618                 case TTY_BREAK:
619                 case TTY_PARITY:
620                 case TTY_FRAME:
621                 case TTY_OVERRUN:
622                         pr_debug("sh_ldisc: tty ctrl\n");
623                         /* Skip errors */
624                         break;
625                 default:
626                         pr_err("sh_ldisc: %s: unknown flag %d\n",
627                                tty_name(tty, buf), flags);
628                         break;
629                 }
630         }
631 }
632
633 static int
634 sh_ldisc_ioctl(struct tty_struct *tty, struct file *file,
635                         unsigned int cmd, unsigned long arg)
636 {
637         int err;
638
639         err = -EFAULT;
640
641         switch (cmd) {
642         case TCFLSH:
643                 pr_debug("%s flush ioctl\n", __func__);
644
645                 /* flush our buffers and the serial port's buffer */
646                 if (arg == TCIOFLUSH || arg == TCOFLUSH)
647                         ;
648                 err = n_tty_ioctl_helper(tty, file, cmd, arg);
649                 break;
650
651         default:
652                 pr_debug("%s default ioctl\n", __func__);
653
654                 err = tty_mode_ioctl(tty, file, cmd, arg);
655                 break;
656         }
657
658         return err;
659 }
660
661 static int
662 sh_ldisc_open(struct tty_struct *tty)
663 {
664         struct ldisc_priv *ld_data;
665
666         pr_info("%s\n", __func__);
667
668         ld_data = kzalloc(sizeof(*ld_data), GFP_KERNEL);
669         if (!ld_data)
670                 goto err;
671
672         mutex_init(&ld_data->tty_write_lock);
673
674         /* Init priv data */
675         ld_data->tty = tty;
676         ld_data->pkt_byte_idx = 0;
677         ld_data->pyld_len = 0;
678
679 #if 0
680         add_pkt_cnt = 0;
681         read_pkt_cnt = 0;
682 #endif
683
684         if (!create_client_devs(ld_data))
685                 goto err_free_bufs;
686
687         /* Init tty struct */
688         tty->disc_data = ld_data;
689         tty->receive_room = N_TTY_BUF_SIZE;
690
691         return 0;
692
693 err_free_bufs:
694         kfree(ld_data);
695 err:
696         return -ENOMEM;
697 }
698
699 static void
700 sh_ldisc_close(struct tty_struct *tty)
701 {
702         struct ldisc_priv *ld_data = tty->disc_data;
703
704         pr_info("%s\n", __func__);
705
706         delete_client_devs(ld_data);
707
708         kfree(ld_data);
709
710         tty->disc_data = NULL;
711 }
712
713
714 static struct tty_ldisc_ops sh_ldisc = {
715         .owner  = THIS_MODULE,
716         .magic  = TTY_LDISC_MAGIC,
717         .name   = "nv_senshub",
718         .open   = sh_ldisc_open,
719         .close  = sh_ldisc_close,
720         .ioctl  = sh_ldisc_ioctl,
721         .receive_buf = sh_ldisc_recv_from_tty,
722 };
723
724 static int __init
725 sh_ldisc_init(void)
726 {
727         int err;
728
729         err = tty_register_ldisc(N_NV_SENSHUB, &sh_ldisc);
730         if (err != 0)
731                 pr_err("nv-sensorhub: error %d registering line disc.\n", err);
732         return err;
733 }
734
735 static void __exit
736 sh_ldisc_cleanup(void)
737 {
738         if (tty_unregister_ldisc(N_NV_SENSHUB) != 0)
739                 pr_err("nv-sensorhub: failed to unregister line disc.\n");
740 }
741
742 module_init(sh_ldisc_init);
743 module_exit(sh_ldisc_cleanup);
744
745 MODULE_DESCRIPTION("Nvidia sensorhub driver");
746 MODULE_AUTHOR("Arun Kannan <akannan@nvidia.com>");
747 MODULE_LICENSE("GPL");