Merge branch 'dev' into kernel
[kernel/swap-modules.git] / driver / device_driver.c
1 ////////////////////////////////////////////////////////////////////////////////////
2 //
3 //      FILE:           device_driver.c
4 //
5 //      DESCRIPTION:
6 //      This file is C source for SWAP driver.
7 //
8 //      SEE ALSO:       device_driver.h
9 //      AUTHOR:         L.Komkov, S.Dianov, S.Grekhov, A.Gerenkov
10 //      COMPANY NAME:   Samsung Research Center in Moscow
11 //      DEPT NAME:      Advanced Software Group
12 //      CREATED:        2008.02.15
13 //      VERSION:        1.0
14 //      REVISION DATE:  2008.12.03
15 //
16 ////////////////////////////////////////////////////////////////////////////////////
17
18 #include "module.h"
19 #include "device_driver.h"      // device driver
20 #include "handlers_core.h"
21 #include <linux/notifier.h>
22 #include "sspt/sspt_procs.h"
23
24 #ifdef OVERHEAD_DEBUG
25 extern unsigned long swap_sum_time;
26 extern unsigned long swap_sum_hit;
27 #endif
28
29
30 extern unsigned long imi_sum_time;
31 extern unsigned long imi_sum_hit;
32
33
34 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17)
35 static BLOCKING_NOTIFIER_HEAD(swap_notifier_list);
36 #endif
37 pid_t gl_nNotifyTgid;
38 EXPORT_SYMBOL_GPL(gl_nNotifyTgid);
39
40 static DECLARE_WAIT_QUEUE_HEAD (notification_waiters_queue);
41 static volatile unsigned notification_count;
42
43 static int device_mmap (struct file *filp, struct vm_area_struct *vma);
44 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
45 static int device_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
46 #else
47 static long device_ioctl (struct file *file, unsigned int cmd, unsigned long arg);
48 #endif
49 static int device_open(struct inode *, struct file *);
50 static int device_release(struct inode *, struct file *);
51 static ssize_t device_read(struct file *, char __user *, size_t, loff_t *);
52 static ssize_t device_write(struct file *, const char __user *, size_t, loff_t *);
53
54 static int gl_nDeviceOpened = 0;
55 static struct file_operations device_fops = {
56         .owner = THIS_MODULE,
57         .mmap = device_mmap,
58 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
59         .ioctl = device_ioctl,
60 #else
61         .unlocked_ioctl = device_ioctl,
62 #endif
63         .read = device_read,
64         .write = device_write,
65         .open = device_open,
66         .release = device_release
67 };
68
69 typedef void (* dbi_module_callback)(void);
70
71 int device_init (void)
72 {
73         int nReserved = 0;
74         nReserved = register_chrdev(0, device_name, &device_fops);
75         if(nReserved < 0)
76         {
77                 unregister_chrdev(nReserved, device_name);
78                 EPRINTF("Cannot register character device!");
79                 return -1;
80         }
81         EPRINTF("New device node with major number [%d], was created\n", nReserved);
82         device_major = nReserved;
83         return 0;
84 }
85
86 void device_down (void)
87 {
88         unregister_chrdev(device_major, device_name);
89 }
90
91 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17)
92 void swap_register_notify (struct notifier_block *nb)
93 {
94         blocking_notifier_chain_register(&swap_notifier_list, nb);
95 }
96 EXPORT_SYMBOL_GPL(swap_register_notify);
97
98 void swap_unregister_notify (struct notifier_block *nb)
99 {
100         blocking_notifier_chain_unregister(&swap_notifier_list, nb);
101 }
102 EXPORT_SYMBOL_GPL(swap_unregister_notify);
103 #endif
104
105 void notify_user (event_id_t event_id)
106 {
107         ec_info.events_counters[event_id] += 1;
108
109         if (EVENT_EC_PROBE_RECORD == event_id)
110         {
111                 // EC_PROBE_RECORD events happen to often. To reduce overhead user
112                 // space will be notified only once per each EVENTS_AGGREGATION_USEC
113                 static uint64_t timestamp_usec = 0;
114
115                 uint64_t current_usec;
116                 uint64_t delta_usec;
117
118                 struct timeval tv;
119
120                 do_gettimeofday (&tv);
121                 current_usec = 1000000ULL * (unsigned) tv.tv_sec + (unsigned) tv.tv_usec;
122
123                 if (current_usec < timestamp_usec)
124                 {
125                         // Note: time from do_gettimeofday() may go backward
126                         EPRINTF ("current_usec=%llu timestamp_usec=%llu", current_usec, timestamp_usec);
127                 }
128                 else
129                 {
130                         delta_usec = current_usec - timestamp_usec;
131                         if (EVENTS_AGGREGATION_USEC > delta_usec)
132                         {
133                                 // wait the time left
134 #if defined(__DEBUG)
135                                 unsigned UNUSED left_usec = EVENTS_AGGREGATION_USEC - delta_usec;
136 #endif /* defined(__DEBUG) */
137                                 return; // supress notification
138                         }
139                 }
140                 timestamp_usec = current_usec;  // remember new time for the future use
141         } else if (EVENT_EC_START_CONDITION_SEEN == event_id) {
142                 return;         // supress notification
143         } else if (EVENT_EC_STOP_CONDITION_SEEN == event_id) {
144                 return;         // supress notification
145         }
146
147         ++notification_count;
148         wake_up_interruptible (&notification_waiters_queue);
149 }
150
151 static int device_mmap (struct file *filp UNUSED, struct vm_area_struct *vma)
152 {
153         if(!p_buffer) {
154                 EPRINTF("Null pointer to buffer!");
155                 return -1;
156         }
157         return remap_vmalloc_range (vma, p_buffer, 0);
158 }
159
160 static int device_open(struct inode *inode, struct file *file)
161 {
162         /*if (gl_nDeviceOpened)
163                 return -EBUSY;*/
164         gl_nDeviceOpened++;
165         // TODO
166         try_module_get(THIS_MODULE);
167         return 0;
168 }
169
170 static int device_release(struct inode *inode, struct file *file)
171 {
172         gl_nDeviceOpened--;
173         module_put(THIS_MODULE);
174         return 0;
175 }
176
177 static ssize_t device_read(struct file *filp, char __user *buffer, size_t length, loff_t * offset)
178 {
179         EPRINTF("Operation <<read>> not supported!");
180         return -1;
181 }
182
183 static ssize_t device_write(struct file *filp, const char __user *buff, size_t len, loff_t * off)
184 {
185         EPRINTF("Operation <<write>> not supported!");
186         return -1;
187 }
188
189 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
190 static int device_ioctl (struct inode *inode UNUSED, struct file *file UNUSED, unsigned int cmd, unsigned long arg)
191 #else
192 static long device_ioctl (struct file *file UNUSED, unsigned int cmd, unsigned long arg)
193 #endif
194 {
195         unsigned long spinlock_flags = 0L;
196         int result = -1;
197     void __user * arg_pointer = (void __user *) arg;
198 //      DPRINTF("Command=%d", cmd);
199         switch (cmd)
200         {
201         case EC_IOCTL_SET_EC_MODE:
202                 {
203                         ioctl_general_t param;
204                         unsigned long nIgnoredBytes = 0;
205                         memset(&param, '0', sizeof(ioctl_general_t));
206                         nIgnoredBytes = copy_from_user (&param, arg_pointer, sizeof(ioctl_general_t));
207                         if (nIgnoredBytes > 0) {
208                                 result = -1;
209                                 break;
210                         }
211                         if(SetECMode(param.m_unsignedLong) == -1) {
212                                 result = -1;
213                                 break;
214                         }
215                         result = 0;
216                         DPRINTF("Set EC Mode = %lu", param.m_unsignedLong);
217                         break;
218                 }
219         case EC_IOCTL_GET_EC_MODE:
220                 {
221                         ioctl_general_t param;
222                         unsigned long nIgnoredBytes = 0;
223                         memset(&param, '0', sizeof(ioctl_general_t));
224                         param.m_unsignedLong = GetECMode();
225                         nIgnoredBytes = copy_to_user (arg_pointer, &param, sizeof (ioctl_general_t));
226                         if (nIgnoredBytes > 0) {
227                                 result = -1;
228                                 break;
229                         }
230                         result = 0;
231 //                      DPRINTF("Get EC Mode = %lu", param.m_unsignedLong);  // Frequent call
232                         break;
233                 }
234         case EC_IOCTL_SET_BUFFER_SIZE:
235                 {
236                         ioctl_general_t param;
237                         unsigned long nIgnoredBytes = 0;
238                         memset(&param, '0', sizeof(ioctl_general_t));
239                         nIgnoredBytes = copy_from_user (&param, arg_pointer, sizeof(ioctl_general_t));
240                         if (nIgnoredBytes > 0) {
241                                 result = -1;
242                                 break;
243                         }
244                         if (SetBufferSize(param.m_unsignedLong) == -1) {
245                                 result = -1;
246                                 break;
247                         }
248                         result = 0;
249                         DPRINTF("Set Buffer Size = %lu", param.m_unsignedLong);
250                         break;
251                 }
252         case EC_IOCTL_GET_BUFFER_SIZE:
253                 {
254                         ioctl_general_t param;
255                         unsigned long nIgnoredBytes = 0;
256                         memset(&param, '0', sizeof(ioctl_general_t));
257                         param.m_unsignedLong = GetBufferSize();
258                         nIgnoredBytes = copy_to_user (arg_pointer, &param, sizeof (ioctl_general_t));
259                         if (nIgnoredBytes > 0) {
260                                 result = -1;
261                                 break;
262                         }
263                         result = 0;
264                         DPRINTF("Get Buffer Size = %lu", param.m_unsignedLong);
265                         break;
266                 }
267         case EC_IOCTL_RESET_BUFFER:
268                 {
269                         if (ResetBuffer() == -1) {
270                                 result = -1;
271                                 break;
272                         }
273                         result = 0;
274                         DPRINTF("Reset Buffer");
275                         break;
276                 }
277         case EC_IOCTL_GET_EC_INFO:
278                 {
279                         if (copy_ec_info_to_user_space ((ec_info_t *) arg) != 0) {
280                                 result = -1;
281                                 break;
282                         }
283                         result = 0;
284 //                      DPRINTF("Get Buffer Status"); // Frequent call
285                         break;
286                 }
287         case EC_IOCTL_CONSUME_BUFFER:
288                 {
289                         static ec_info_t ec_info_copy;
290                         int nIgnoredBytes = 0;
291
292                         nIgnoredBytes = copy_from_user (&ec_info_copy, (const void __user *) arg, sizeof (ec_info_t));
293                         if(nIgnoredBytes > 0)
294                         {
295                                 EPRINTF ("copy_from_user(%08X,%08X)=%d", (unsigned) arg, (unsigned) &ec_info_copy, nIgnoredBytes);
296                                 result = -1;
297                                 break;
298                         }
299
300                         spin_lock_irqsave (&ec_spinlock, spinlock_flags);
301
302                         // Original buffer
303                         if(ec_info.after_last > ec_info.first) {
304                                 ec_info.buffer_effect = ec_info.buffer_size;
305                         }
306                         if (ec_info.after_last == ec_info.buffer_effect) {
307                                  ec_info.first = 0;
308                         } else {
309                                  ec_info.first = ec_info_copy.after_last;
310                         }
311                         ec_info.trace_size = ec_info.trace_size - ec_info_copy.trace_size;
312
313                         spin_unlock_irqrestore (&ec_spinlock, spinlock_flags);
314                         result = 0;
315 //                      DPRINTF("Consume Buffer"); // Frequent call
316                         break;
317                 }
318         case EC_IOCTL_ADD_PROBE:
319                 {
320                         unsigned long addr = arg;
321                         result = add_probe(addr);
322
323                         break;
324                 }
325         //@AGv: remove_probe expects probe address instead of name
326         /*case EC_IOCTL_REMOVE_PROBE:
327                 {
328                         char *probe_name = (char *) arg;
329                         result = remove_probe (probe_name);
330
331                         break;
332                 }*/
333         case EC_IOCTL_SET_APPDEPS:
334         {
335                 size_t size;
336                 result = copy_from_user(&size, arg_pointer, sizeof(size_t));
337                 if (result) {
338                         EPRINTF("Cannot copy deps size!");
339                         result = -1;
340                         break;
341                 }
342                 DPRINTF("Deps size has been copied (%d)", size);
343
344                 if (size == 0) {
345                         DPRINTF("Deps are size of 0");
346                         break;
347                 }
348
349                 deps = vmalloc(size);
350                 if (deps == NULL) {
351                         EPRINTF("Cannot alloc mem for deps!");
352                         result = -1;
353                         break;
354                 }
355                 DPRINTF("Mem for deps has been allocated");
356
357                 result = copy_from_user(deps, arg_pointer, size);
358                 if (result) {
359                         EPRINTF("Cannot copy deps!");
360                         result = -1;
361                         break;
362                 }
363                 DPRINTF("Deps has been copied successfully");
364
365                 break;
366         }
367         case EC_IOCTL_SET_PID:
368         {
369                 unsigned int _pid;
370
371                 result = copy_from_user(&_pid, arg_pointer, sizeof(unsigned int));
372                 if (result) {
373                         EPRINTF("Cannot copy pid!");
374                         result = -1;
375                         break;
376                 }
377
378                 inst_pid = _pid;
379
380                 DPRINTF("EC_IOCTL_SET_PID pid:%d", inst_pid);
381
382                 break;
383         }
384         case EC_IOCTL_SET_PROFILEBUNDLE:
385         {
386                 size_t size;
387
388                 result = copy_from_user(&size, arg_pointer, sizeof(size_t));
389                 if (result) {
390                         EPRINTF("Cannot copy bundle size!");
391                         result = -1;
392                         break;
393                 }
394                 DPRINTF("Bundle size has been copied");
395
396                 bundle = vmalloc(size);
397                 if (bundle == NULL) {
398                         EPRINTF("Cannot alloc mem for bundle!");
399                         result = -1;
400                         break;
401                 }
402                 DPRINTF("Mem for bundle has been alloced");
403
404                 result = copy_from_user(bundle, arg_pointer, size);
405                 if (result) {
406                         EPRINTF("Cannot copy bundle!");
407                         result = -1;
408                         break;
409                 }
410                 DPRINTF("Bundle has been copied successfully");
411
412                 last_error_buffer_initialize();
413
414                 if (link_bundle() == -1) {
415                         EPRINTF("Cannot link profile bundle!");
416                         result = -1;
417                         break;
418                 }
419                 if (has_last_error() == -1) {
420                         EPRINTF("last_error_buffer != NULL");
421                         result = -1;
422                 }
423
424                 break;
425         }
426         case EC_IOCTL_RESET_PROBES:
427                 {
428                         result = reset_probes();
429
430                         break;
431                 }
432         case EC_IOCTL_UPDATE_CONDS:
433                 {
434                         int args_cnt, i;
435                         struct cond *c, *c_tmp, *p_cond;
436                         unsigned char *p_data;
437                         int err;
438                         result = 0;
439                         err = copy_from_user(&args_cnt, arg_pointer, sizeof(int));
440                         if (err) {
441                                 result = -1;
442                                 break;
443                         }
444                         /* first, delete all the conds */
445                         list_for_each_entry_safe(c, c_tmp, &cond_list.list, list) {
446                                 list_del(&c->list);
447                                 kfree(c);
448                         }
449                         /* second, add new conds */
450                         p_data = (unsigned char *)(arg + sizeof(int));
451                         for (i = 0; i < args_cnt; i++) {
452                                 p_cond = kmalloc(sizeof(struct cond), GFP_KERNEL);
453                                 if (!p_cond) {
454                                         DPRINTF("Cannot alloc cond!");
455                                         result = -1;
456                                         break;
457                                 }
458                                 err = copy_from_user(&p_cond->tmpl, (const void __user *)p_data,
459                                                 sizeof(struct event_tmpl));
460                                 if (err) {
461                                         DPRINTF("Cannot copy cond from user!");
462                                         result = -1;
463                                         break;
464                                 }
465                                 p_cond->applied = 0;
466                                 list_add(&(p_cond->list), &(cond_list.list));
467                                 p_data += sizeof(struct event_tmpl);
468                         }
469                         break;
470                 }
471         case EC_IOCTL_ATTACH:
472                 {
473                         unsigned long dbi_flags;
474                         struct dbi_modules_handlers *local_mh;
475                         struct dbi_modules_handlers_info *local_mhi;
476                         int j;
477                         dbi_module_callback dmc_start;
478
479                         // call "start"-callback for all modules according module priority
480                         local_mh = get_dbi_modules_handlers();
481                         spin_lock_irqsave(&local_mh->lock, dbi_flags);
482                         for (j = 0; j <= MAX_PRIORITY; j++) {
483                                 list_for_each_entry_rcu(local_mhi, &local_mh->modules_handlers, dbi_list_head) {
484                                         if (local_mhi->dbi_module_priority_start == j) {
485                                                 if (local_mhi->dbi_module_callback_start != NULL) {
486                                                         printk("Started module callback (start) %s\n", local_mhi->dbi_module->name);
487                                                         dmc_start = (dbi_module_callback )local_mhi->dbi_module_callback_start;
488                                                         dmc_start();
489                                                 }
490                                         }
491                                 }
492                         }
493                         spin_unlock_irqrestore(&local_mh->lock, dbi_flags);
494
495                         result = ec_user_attach ();
496 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17)
497                         DPRINTF("EC_IOCTL_ATTACH calling notification chain");
498                         blocking_notifier_call_chain(&swap_notifier_list, EC_IOCTL_ATTACH, (void*)NULL);
499 #endif
500                         DPRINTF("Attach Probes");
501                         break;
502                 }
503         case EC_IOCTL_ACTIVATE:
504                 result = ec_user_activate ();
505                 DPRINTF("Activate Probes");
506                 break;
507         case EC_IOCTL_STOP_AND_DETACH:
508         {
509                 unsigned long nIgnoredBytes = 0;
510                 unsigned long dbi_flags;
511                 struct dbi_modules_handlers *local_mh;
512                 struct dbi_modules_handlers_info *local_mhi;
513                 unsigned int local_module_refcount = 0;
514                 int j;
515                 dbi_module_callback dmc_stop;
516
517 #ifdef OVERHEAD_DEBUG
518                 printk("\nswap_sum_time = %ld in kprobe_handler()\n", swap_sum_time);
519                 printk("swap_sum_hit = %ld in kprobe_handler()\n", swap_sum_hit);
520                 swap_sum_time = 0;
521                 swap_sum_hit = 0;
522 #endif
523
524                 printk("\n### imi_sum_time = %ld in install_mapped_ips()\n", imi_sum_time);
525                 printk("### imi_sum_hit = %ld in install_mapped_ips()\n", imi_sum_hit);
526
527                 if (imi_sum_hit != 0) {
528                         printk("### time = %ld in install_mapped_ips()\n", imi_sum_time/imi_sum_hit);
529                 }
530
531                 imi_sum_time = 0;
532                 imi_sum_hit = 0;
533
534                 local_mh = get_dbi_modules_handlers();
535                 if(ec_user_stop() != 0) {
536                         result = -1;
537                         goto sad_cleanup;
538                 }
539                 nIgnoredBytes = copy_ec_info_to_user_space ((ec_info_t*)arg);
540                 if(nIgnoredBytes > 0) {
541                         result = -1;
542                         goto sad_cleanup;
543                 }
544
545                 sspt_procs_free_all();
546
547                 vfree(bundle);
548                 result = 0;
549                 DPRINTF("Stop and Detach Probes");
550 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17)
551                 DPRINTF("EC_IOCTL_STOP_AND_DETACH calling notification chain");
552                 blocking_notifier_call_chain(&swap_notifier_list, EC_IOCTL_STOP_AND_DETACH, (void*)&gl_nNotifyTgid);
553 #endif
554                 // call "stop"-callback for all modules according module priority
555                 spin_lock_irqsave(&local_mh->lock, dbi_flags);
556                 for (j = 0; j <= MAX_PRIORITY; j++) {
557                         list_for_each_entry_rcu(local_mhi, &local_mh->modules_handlers, dbi_list_head) {
558                                 if (local_mhi->dbi_module_priority_stop == j) {
559                                         if (local_mhi->dbi_module_callback_stop != NULL) {
560                                                 printk("Started module callback (stop) %s\n", local_mhi->dbi_module->name);
561                                                 dmc_stop = (dbi_module_callback )local_mhi->dbi_module_callback_stop;
562                                                 dmc_stop();
563                                         }
564                                 }
565                         }
566                 }
567                 spin_unlock_irqrestore(&local_mh->lock, dbi_flags);
568 sad_cleanup:
569                 spin_lock_irqsave(&local_mh->lock, dbi_flags);
570                 list_for_each_entry_rcu(local_mhi, &local_mh->modules_handlers, dbi_list_head) {
571                         local_module_refcount = module_refcount(local_mhi->dbi_module);
572                         if (local_module_refcount == 1) {
573                                 module_put(local_mhi->dbi_module);
574                         }
575                         else if (local_module_refcount > 1) {
576                                 printk("local_module_refcount too much - force set refcount to zero\n");
577                                 while (local_module_refcount--)
578                                         module_put(local_mhi->dbi_module);
579                         }
580                 }
581                 spin_unlock_irqrestore(&local_mh->lock, dbi_flags);
582                 break;
583         }
584         case EC_IOCTL_US_EVENT:
585                 {
586                         ioctl_us_event_t ioctl_args;
587                         result = copy_from_user (&ioctl_args, (const void __user *) arg, sizeof (ioctl_args));
588                         if (result)
589                         {
590                                 result = -1;
591                                 EPRINTF ("copy_from_user() failure");
592                         }
593                         else
594                         {
595                                 if(ioctl_args.len == 0){
596                                         result = -EINVAL;
597                                         EPRINTF ("invalid event length!");
598                                 }
599                                 else {
600                                         char *buf = kmalloc(ioctl_args.len, GFP_KERNEL);
601                                         if(!buf){
602                                                 result = -ENOMEM;
603                                                 EPRINTF ("failed to alloc mem for event!");
604                                         }
605                                         else {
606                                                 result = copy_from_user (buf, (const void __user *) ioctl_args.data, ioctl_args.len);
607                                                 if (result){
608                                                         result = -1;
609                                                         EPRINTF ("failed to copy event from user space!");
610                                                 }
611                                                 else
612                                                         result = put_us_event(buf, ioctl_args.len);
613                                                 kfree(buf);
614                                         }
615                                 }
616                         }
617 //                      DPRINTF("User Space Event"); // Frequent call
618                         break;
619                 }
620
621         case EC_IOCTL_SET_EVENT_MASK:
622                 {
623                         int mask;
624                         result = copy_from_user (&mask, arg_pointer, sizeof (mask));
625                         if (result)
626                         {
627                                 result = -EFAULT;
628                                 break;
629                         }
630
631                         result = set_event_mask (mask);
632                         if (result)
633                         {
634                                 break;
635                         }
636                         DPRINTF("Set Event Mask = %d", mask);
637                         break;
638                 }
639
640         case EC_IOCTL_GET_EVENT_MASK:
641                 {
642                         int mask = 0;
643                         result = get_event_mask(&mask);
644                         if (result)
645                         {
646                                 result = -EFAULT;
647                         }
648                         result = copy_to_user (arg_pointer, &mask, sizeof (mask));
649                         if (result)
650                         {
651                                 result = -EFAULT;
652                         }
653                         DPRINTF("Get Event Mask = %d", mask);
654                         break;
655                 }
656
657         case EC_IOCTL_GET_PREDEF_UPROBES:
658                 {
659                         result = get_predef_uprobes((ioctl_predef_uprobes_info_t *)arg);
660                         if (result)
661                         {
662                                 result = -EFAULT;
663                         }
664                         DPRINTF("Get Predefined User Space Probes");
665                         break;
666                 }
667
668         case EC_IOCTL_GET_PREDEF_UPROBES_SIZE:
669                 {
670                         int size = 0;
671                         result = get_predef_uprobes_size(&size);
672                         if (result)
673                         {
674                                 result = -EFAULT;
675                         }
676                         result = copy_to_user (arg_pointer, &size, sizeof (size));
677                         if (result)
678                         {
679                                 result = -EFAULT;
680                         }
681                         DPRINTF("Get Size of Predefined User Space Probes");
682                         break;
683                 }
684         case EC_IOCTL_GET_LAST_ERROR:
685                 {
686                         result = get_last_error((void*)arg);
687                         break;
688                 }
689         default:
690                 EPRINTF ("Unknown driver command = %u", cmd);
691                 result = -EINVAL;
692                 break;
693         }
694
695         return result;
696 }