1 /*
2 *
3 * Copyright (c) International Business Machines Corp., 2001
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
13 * the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 /*
20 * This test module is for executing and testing
21 * the kernel code from drivers/base. This module
22 * is driven by a user space program through
23 * calls to the ioctl
24 *
25 * author: Sean Ruyle
26 * date: 07/14/2003
27 *
28 * module: tbase
29 */
30
31 #include <linux/types.h>
32 #include <linux/kernel.h>
33 #include <linux/fs.h>
34 #include <linux/ioctl.h>
35 #include <linux/module.h>
36 #include <linux/init.h>
37 #include <linux/device.h>
38 #include <linux/pci.h>
39 #include <linux/sysdev.h>
40 #include <asm/uaccess.h>
41
42 #include "tbase.h"
43 #include "str_mod.h"
44
45 MODULE_AUTHOR("Sean Ruyle <srruyle@us.ibm.com>");
46 MODULE_DESCRIPTION(TMOD_DRIVER_NAME);
47 MODULE_LICENSE("GPL");
48
49 static int tbase_ioctl(struct inode *, struct file *, unsigned int,
50 unsigned long);
51 static int tbase_open(struct inode *, struct file *);
52 static int tbase_close(struct inode *, struct file *);
53
54 static int test_device_register(void);
55 static int test_device_unregister(void);
56 static int test_bus_add(void);
57 static int test_get_drv(void);
58 static int test_put_drv(void);
59 static int test_reg_firm(void);
60 static int test_create_file(void);
61 static int test_dev_suspend(void);
62 static int test_dev_file(void);
63 static int test_bus_rescan(void);
64 static int test_bus_file(void);
65 static int test_class_reg(void);
66 static int test_class_get(void);
67 static int test_class_file(void);
68 static int test_classdev_reg(void);
69 static int test_classint_reg(void);
70 static int test_sysdev_cls_reg(void);
71 static int test_sysdev_reg(void);
72
73 static int Major = TBASEMAJOR;
74 static ltpmod_user_t ltp_mod;
75
76 /*
77 * File operations struct, to use operations find the
78 * correct file descriptor
79 */
80 static struct file_operations tbase_fops = {
81 open: tbase_open,
82 release:tbase_close,
83 ioctl: tbase_ioctl,
84 };
85
tbase_open(struct inode * ino,struct file * f)86 static int tbase_open(struct inode *ino, struct file *f)
87 {
88 return 0;
89 }
90
tbase_close(struct inode * ino,struct file * f)91 static int tbase_close(struct inode *ino, struct file *f)
92 {
93 return 0;
94 }
95
96 /* my bus stuff */
97 struct device_driver test_driver;
98 struct device test_device;
99
test_device_match(struct device * dev,struct device_driver * drv)100 static int test_device_match(struct device *dev, struct device_driver *drv)
101 {
102
103 printk("tbase: driver is %s\n", drv->name);
104 // printk("tbase: device is %s\n", dev->name);
105
106 if (drv == &test_driver && dev == &test_device) {
107 printk("tbase: match\n");
108 return 1;
109 } else {
110 printk("tbase: no match\n");
111 return 0;
112 }
113
114 }
115
116 struct bus_type test_bus_type = {
117 .name = "test_bus",
118 .match = test_device_match,
119 };
120
121 /* my driver stuff */
test_dev_probe(struct device * dev)122 int test_dev_probe(struct device *dev)
123 {
124 printk("tbase: Entered test_dev_probe\n");
125 return 0;
126 }
127
test_dev_remove(struct device * dev)128 int test_dev_remove(struct device *dev)
129 {
130 printk("tbase: Entered test_dev_remove\n");
131 return 0;
132 }
133
134 struct device_driver test_driver = {
135 .name = "TestDriver",
136 .bus = &test_bus_type,
137 .probe = test_dev_probe,
138 .remove = test_dev_remove,
139 };
140
141 /* my device stuff */
142 struct device test_device = {
143 // .name = "TestDevice",
144 .bus = &test_bus_type,
145 .bus_id = "test_bus",
146 };
147
148 /* my class stuff */
test_class_release(struct class_device * class_dev)149 static void test_class_release(struct class_device *class_dev)
150 {
151 printk("tbase: Entered test_class_release\n");
152 }
153
test_class_hotplug(struct class_device * dev,char ** envp,int num_envp,char * buffer,int buffer_size)154 int test_class_hotplug(struct class_device *dev, char **envp,
155 int num_envp, char *buffer, int buffer_size)
156 {
157 printk("tbase: Entered test_class_hotplug\n");
158 return 0;
159 }
160
161 struct class test_class = {
162 .name = "TestClass",
163 .hotplug = test_class_hotplug,
164 .release = test_class_release,
165 };
166
167 /* my class device stuff */
168 struct class_device test_class_dev = {
169 .class_id = "test_bus",
170 .dev = &test_device,
171 .class = &test_class,
172 };
173
174 /* my class interface stuff */
test_intf_add(struct class_device * class_dev)175 int test_intf_add(struct class_device *class_dev)
176 {
177 printk("tbase: Entered test_intf_add for the test class_interface\n");
178 return 0;
179 }
180
test_intf_rem(struct class_device * class_dev)181 void test_intf_rem(struct class_device *class_dev)
182 {
183 printk("tbase: Entered test_intf_rem for the test class interface\n");
184 }
185
186 struct class_interface test_interface = {
187 .class = &test_class,
188 .add = &test_intf_add,
189 .remove = &test_intf_rem,
190 };
191
192 /* my sys_device stuff */
test_resume(struct sys_device * dev)193 int test_resume(struct sys_device *dev)
194 {
195 printk("tbase: Entered test resume for sys device\n");
196 return 0;
197 }
198
199 struct sysdev_class test_sysclass = {
200 set_kset_name("TestSysclass"),
201 .resume = test_resume,
202 };
203
204 struct sys_device test_sys_device = {
205 .id = 0,
206 .cls = &test_sysclass,
207 };
208
209 /* my attribute stuff */
210 static inline ssize_t
store_new_id(struct device_driver * driver,const char * buf,size_t count)211 store_new_id(struct device_driver *driver, const char *buf, size_t count)
212 {
213 printk("tbase: Entered store new id\n");
214 return count;
215 }
216
217 /* create attribute driver_attr_new_id */
218 DRIVER_ATTR(new_id, 0200, NULL, store_new_id);
219
220 /* create attribute dev_attr_test_id */
221 DEVICE_ATTR(test_id, S_IRUGO, NULL, NULL);
222
223 /* create attribute bus_attr_test_id */
224 BUS_ATTR(test_id, S_IRUGO, NULL, NULL);
225
226 /* create attribute class_attr_test_id */
227 CLASS_ATTR(test_id, 0644, NULL, NULL);
228
229 /* create attribute class_device_attr_test_id */
230 CLASS_DEVICE_ATTR(test_id, 0644, NULL, NULL);
231
232 /*
233 * tbase_ioctl:
234 * a user space program can drive the test functions
235 * through a call to ioctl once the correct file
236 * descriptor has been attained
237 */
tbase_ioctl(struct inode * ino,struct file * f,unsigned int cmd,unsigned long l)238 static int tbase_ioctl(struct inode *ino, struct file *f,
239 unsigned int cmd, unsigned long l)
240 {
241 int rc;
242 tmod_interface_t tif;
243 caddr_t *inparms;
244 caddr_t *outparms;
245
246 printk("Enter tbase_ioctl\n");
247
248 inparms = NULL;
249 outparms = NULL;
250 rc = 0;
251
252 /*
253 * the following calls are used to setup the
254 * parameters that might need to be passed
255 * between user and kernel space, using the tif
256 * pointer that is passed in as the last
257 * parameter to the ioctl
258 *
259 */
260 if (copy_from_user(&tif, (void *)l, sizeof(tif))) {
261 /* Bad address */
262 return (-EFAULT);
263 }
264
265 /*
266 * Setup inparms and outparms as needed
267 */
268 if (tif.in_len > 0) {
269 inparms = (caddr_t *) kmalloc(tif.in_len, GFP_KERNEL);
270 if (!inparms) {
271 return (-ENOMEM);
272 }
273
274 rc = copy_from_user(inparms, tif.in_data, tif.in_len);
275 if (rc) {
276 kfree(inparms);
277 return (-EFAULT);
278 }
279 }
280 if (tif.out_len > 0) {
281 outparms = (caddr_t *) kmalloc(tif.out_len, GFP_KERNEL);
282 if (!outparms) {
283 kfree(inparms);
284 return (-ENOMEM);
285 }
286 }
287
288 /*
289 * Use a switch statement to determine which function
290 * to call, based on the cmd flag that is specified
291 * in user space. Pass in inparms or outparms as
292 * needed
293 *
294 */
295 switch (cmd) {
296 case REG_DEVICE:
297 rc = test_device_register();
298 break;
299 case UNREG_DEVICE:
300 rc = test_device_unregister();
301 break;
302 case BUS_ADD:
303 rc = test_bus_add();
304 break;
305 case GET_DRV:
306 rc = test_get_drv();
307 break;
308 case PUT_DRV:
309 rc = test_put_drv();
310 break;
311 case REG_FIRM:
312 rc = test_reg_firm();
313 break;
314 case CREATE_FILE:
315 rc = test_create_file();
316 break;
317 case DEV_SUSPEND:
318 rc = test_dev_suspend();
319 break;
320 case DEV_FILE:
321 rc = test_dev_file();
322 break;
323 case BUS_RESCAN:
324 rc = test_bus_rescan();
325 break;
326 case BUS_FILE:
327 rc = test_bus_file();
328 break;
329 case CLASS_REG:
330 rc = test_class_reg();
331 break;
332 case CLASS_UNREG:
333 class_unregister(&test_class);
334 break;
335 case CLASS_GET:
336 rc = test_class_get();
337 break;
338 case CLASS_FILE:
339 rc = test_class_file();
340 break;
341 case CLASSDEV_REG:
342 rc = test_classdev_reg();
343 break;
344 case CLASSINT_REG:
345 rc = test_classint_reg();
346 break;
347 case SYSDEV_CLS_REG:
348 rc = test_sysdev_cls_reg();
349 break;
350 case SYSDEV_CLS_UNREG:
351 sysdev_class_unregister(&test_sysclass);
352 break;
353 case SYSDEV_REG:
354 rc = test_sysdev_reg();
355 break;
356 case SYSDEV_UNREG:
357 sys_device_unregister(&test_sys_device);
358 break;
359 default:
360 printk("tbase: Mismatching ioctl command\n");
361 break;
362 }
363
364 /*
365 * copy in the test return code, the reason we
366 * this is so that in user space we can tell the
367 * difference between an error in one of our test
368 * calls or an error in the ioctl function
369 */
370 tif.out_rc = rc;
371 rc = 0;
372
373 /*
374 * setup the rest of tif pointer for returning to
375 * to user space, using copy_to_user if needed
376 */
377
378 /* if outparms then copy outparms into tif.out_data */
379 if (outparms) {
380 if (copy_to_user(tif.out_data, outparms, tif.out_len)) {
381 printk
382 ("tbase: Unsuccessful copy_to_user of outparms\n");
383 rc = -EFAULT;
384 }
385 }
386
387 /* copy tif structure into l so that can be used by user program */
388 if (copy_to_user((void *)l, &tif, sizeof(tif))) {
389 printk("tbase: Unsuccessful copy_to_user of tif\n");
390 rc = -EFAULT;
391 }
392
393 /*
394 * free inparms and outparms
395 */
396 if (inparms) {
397 kfree(inparms);
398 }
399 if (outparms) {
400 kfree(outparms);
401 }
402
403 return rc;
404 }
405
406 /*
407 * test_device_register
408 * makes call to device register passing in
409 * the device pointer that we found in a previos
410 * function, returns an error code
411 */
test_device_register()412 static int test_device_register()
413 {
414 struct device *dev = ltp_mod.dev;
415 struct device_driver *drv = dev->driver;
416
417 /* check if device register returns an error */
418 if (device_register(dev)) {
419 printk("tbase: Device not registered\n");
420 return 1;
421 } else
422 printk("tbase: Device registered\n");
423
424 driver_unregister(drv);
425
426 /* check if driver_register returns an error */
427 if (driver_register(drv)) {
428 printk("tbase: Driver not registered\n");
429 return 1;
430 } else
431 printk("tbase: Driver registered\n");
432
433 return 0;
434
435 }
436
437 /*
438 * test_device_unregister
439 * make test call to device_unregister which
440 * will in turn make calls that will decrememnt
441 * the reference count and clean up as required
442 */
test_device_unregister()443 static int test_device_unregister()
444 {
445 struct device *dev = ltp_mod.dev;
446
447 /* increment reference count */
448 get_device(dev);
449
450 /* reset remove pointer */
451 if (dev->driver->remove)
452 dev->driver->remove = NULL;
453
454 device_unregister(dev);
455 //check that reference count is smaller by one
456
457 return 0;
458 }
459
460 /*
461 * test_bus_add
462 * make call to bus_add_device, which will
463 * in turn add the device that is passed in
464 * to the bus
465 */
test_bus_add()466 static int test_bus_add()
467 {
468 /* check if device register returns an error */
469 if (bus_add_device(&test_device)) {
470 printk("tbase: Device not added to bus\n");
471 return 1;
472 } else {
473 printk("tbase: Device added to bus\n");
474 return 0;
475 }
476 }
477
478 /*
479 * test_get_drv
480 * make test call to get_driver which should
481 * return a pointer to the driver passed in
482 * and increase the reference count to that
483 * kobject
484 */
test_get_drv()485 static int test_get_drv()
486 {
487 int a, rc;
488 struct device_driver *drv = &test_driver, *tmp = NULL;
489
490 /* get reference count before test call */
491 a = atomic_read(&drv->kobj.refcount);
492
493 /* make test call */
494 if ((tmp = get_driver(drv))) {
495 rc = 0;
496 printk("tbase: get driver returned driver\n");
497 } else {
498 rc = 1;
499 printk("tbase: get driver failed to return driver\n");
500 }
501
502 /* check reference count */
503 if ((a == (atomic_read(&drv->kobj.refcount) - 1))) {
504 rc = 0;
505 printk("tbase: correctly set ref count get driver\n");
506 } else {
507 rc = 1;
508 printk("tbase: incorrect ref count get driver\n");
509 }
510
511 return rc;
512 }
513
514 /*
515 * test_class_get
516 * make test call to class_get which should return
517 * a pointer to the class passed in and increase
518 * the reference count to that kobject
519 */
test_class_get()520 static int test_class_get()
521 {
522 int rc;
523 struct class *tmp = NULL;
524
525 /* get reference count before test call */
526 tmp = class_get(&test_class);
527 if (tmp == &test_class) {
528 printk("tbase: Success get class\n");
529 rc = 0;
530 } else {
531 printk("tbase: Failure get class\n");
532 rc = 1;
533 }
534
535 class_put(&test_class);
536 return rc;
537 }
538
539 /*
540 * test_put_drv
541 * make test call to put_driver which should
542 * decrease the reference count to the kobject
543 * pointer in the driver structure
544 */
test_put_drv()545 static int test_put_drv()
546 {
547 int a, rc;
548 struct device_driver *drv = &test_driver;
549
550 /* get reference count before test call */
551 a = atomic_read(&drv->kobj.refcount);
552
553 /* make test call */
554 put_driver(drv);
555
556 /* check reference count */
557 if ((a == (atomic_read(&drv->kobj.refcount) + 1))) {
558 rc = 0;
559 printk("tbase: correctly set ref count put driver\n");
560 } else {
561 rc = 1;
562 printk("tbase: incorrect ref count put driver\n");
563 }
564
565 return rc;
566 }
567
568 /*
569 * test_reg_firm
570 * test call to register_firmware, which will
571 * register the subsystem, takes in a struct
572 * subsystem pointer, we can use our bus pointer
573 * that should have been found in a previous test
574 * to pass in a subsystem pointer, returns an
575 * error code
576 */
test_reg_firm()577 static int test_reg_firm()
578 {
579 struct subsystem *subsys = NULL;
580
581 /* check pointer exists */
582 if (!(subsys = &test_bus_type.subsys)) {
583 printk("tbase: subsys pointer not set in reg firmware\n");
584 return 1;
585 }
586
587 /* unregiser firmware */
588 firmware_unregister(subsys);
589
590 /* make test call */
591 if (firmware_register(subsys)) {
592 printk("tbase: failed register firmware\n");
593 return 1;
594 } else {
595 printk("tbase: regsitered firmware\n");
596 return 0;
597 }
598
599 }
600
601 /*
602 * test_create_file
603 * make test call to create sysfs file for the
604 * driver and if that call is successful then
605 * make a call to remove the file
606 */
test_create_file()607 static int test_create_file()
608 {
609 struct device_driver *drv = &test_driver;
610
611 if (driver_create_file(drv, &driver_attr_new_id)) {
612 printk("tbase: failed create sysfs file\n");
613 return 1;
614 } else {
615 printk("tbase: created sysfs file\n");
616 driver_remove_file(drv, &driver_attr_new_id);
617 return 0;
618 }
619
620 }
621
622 /*
623 * test_dev_suspend
624 * make test call to device_suspend and
625 * if that call is successful then make
626 * a call to device_resume
627 */
test_dev_suspend()628 static int test_dev_suspend()
629 {
630 int error = 0;
631
632 error = device_suspend(SUSPEND_SAVE_STATE);
633 if (error)
634 printk("tbase: Failed on device suspend call\n");
635 else {
636 printk("tbase: Successful on device suspend call\n");
637 device_resume();
638 }
639
640 error = device_suspend(SUSPEND_DISABLE);
641 if (error)
642 printk("tbase: Failed on device suspend call\n");
643 else {
644 printk("tbase: Successful on device suspend call\n");
645 device_resume();
646 }
647
648 return error;
649
650 }
651
652 /*
653 * test_dev_file
654 * make test call to device_create_file
655 * and if that call is successful make
656 * another call to device_remove_file
657 */
test_dev_file()658 static int test_dev_file()
659 {
660 struct device *dev = &test_device;
661
662 if (device_create_file(dev, &dev_attr_test_id)) {
663 printk("tbase: failed to create dev sysfs file\n");
664 return 1;
665 } else {
666 printk("tbase: created dev sysfs file\n");
667 device_remove_file(dev, &dev_attr_test_id);
668 return 0;
669 }
670
671 }
672
673 /*
674 * test_bus_rescan
675 * make test call to bus_rescan_devices which
676 * will rescan the bus and attempt to match devices
677 * to drivers, will return 0 for no matches or
678 * the number of matches made, check that the
679 * value returned is not negative
680 */
test_bus_rescan()681 static int test_bus_rescan()
682 {
683 int count = 0;
684
685 count = bus_rescan_devices(&test_bus_type);
686 if (count == 0)
687 printk("tbase: found no device/driver matches\n");
688 else if (count > 0)
689 printk("tbase; found match\n");
690 else {
691 printk("tbase: bus rescan failed\n");
692 return count;
693 }
694
695 return 0;
696 }
697
698 /*
699 * test_bus_file
700 * make test call to bus_create_file
701 * and if that call is successful make
702 * another call to bus_remove_file
703 */
test_bus_file()704 static int test_bus_file()
705 {
706 struct bus_type *bus = &test_bus_type;
707
708 if (bus_create_file(bus, &bus_attr_test_id)) {
709 printk("tbase: failed to create bus sysfs file\n");
710 return 1;
711 } else {
712 printk("tbase: created bus sysfs file\n");
713 bus_remove_file(bus, &bus_attr_test_id);
714 return 0;
715 }
716
717 }
718
719 /*
720 * test_class_file
721 * make test call to class_create_file
722 * and if that call is successful make
723 * another call to class_remove_file
724 */
test_class_file()725 static int test_class_file()
726 {
727 struct class *cls = &test_class;
728
729 if (class_create_file(cls, &class_attr_test_id)) {
730 printk("tbase: failed to create class sysfs file\n");
731 return 1;
732 } else {
733 printk("tbase: created class sysfs file\n");
734 class_remove_file(cls, &class_attr_test_id);
735 return 0;
736 }
737
738 }
739
740 /*
741 * test_class_reg
742 * make test call to class_register
743 * with the test_class that is defined
744 * in this module, if that call is
745 * successful then call unregister
746 */
test_class_reg()747 static int test_class_reg()
748 {
749 int error;
750
751 error = class_register(&test_class);
752 if (error)
753 printk("tbase: class register failed\n");
754 else
755 printk("tbase: class register succeeded\n");
756
757 return error;
758 }
759
760 /*
761 * test_classdev_reg
762 * make test call to class_device_register
763 * and if that returns successful then
764 * make call to class_device_unregister
765 */
test_classdev_reg()766 static int test_classdev_reg()
767 {
768 int rc = 0;
769
770 if (class_device_register(&test_class_dev)) {
771 printk("tbase: Failed to register class device\n");
772 rc = 1;
773 } else {
774 printk("tbase: Registered class device\n");
775
776 /* make class device sysfs file */
777 if (class_device_create_file
778 (&test_class_dev, &class_device_attr_test_id)) {
779 rc = 1;
780 printk
781 ("tbase: Failed to create class device sysfs file\n");
782 } else {
783 printk("tbase: Created class device sysfs file\n");
784 class_device_remove_file(&test_class_dev,
785 &class_device_attr_test_id);
786 }
787
788 class_device_unregister(&test_class_dev);
789 }
790
791 return rc;
792 }
793
794 /*
795 * test_classint_reg
796 * make test call to class_interface_register
797 * and if that returns successfule then
798 * make call to class_interface_unregister
799 */
test_classint_reg()800 static int test_classint_reg()
801 {
802
803 if (class_interface_register(&test_interface)) {
804 printk("tbase: Failed to register class interface\n");
805 return 1;
806 } else {
807 printk("tbase: Registered class interface\n");
808 class_interface_unregister(&test_interface);
809 return 0;
810 }
811
812 }
813
814 /*
815 * test_sysdev_cls_reg
816 * make test call to sysdev_class_register
817 * to register the test_sysclass pointer
818 * as a sysdev_class with the system, check
819 * the return code
820 */
test_sysdev_cls_reg()821 static int test_sysdev_cls_reg()
822 {
823
824 if (sysdev_class_register(&test_sysclass)) {
825 printk("tbase: Failed to register sysdev class\n");
826 return 1;
827 } else {
828 printk("tbase: Registered sysdev class\n");
829 return 0;
830 }
831
832 }
833
834 /*
835 * test_sysdev_reg
836 * make test call to sys_device_register
837 * to register the test_sysdev pointer
838 * as a sys_device with the system, check
839 * the return code
840 */
test_sysdev_reg()841 static int test_sysdev_reg()
842 {
843
844 if (sys_device_register(&test_sys_device)) {
845 printk("tbase: Failed to register sysdev \n");
846 return 1;
847 } else {
848 printk("tbase: Registered sysdev \n");
849 return 0;
850 }
851
852 }
853
854 /*
855 * tbase_init_module
856 * set the owner of tbase_fops, register the module
857 * as a char device, and perform any necessary
858 * initialization
859 */
tbase_init_module(void)860 static int tbase_init_module(void)
861 {
862 int rc;
863
864 bus_register(&test_bus_type);
865 driver_register(&test_driver);
866 device_register(&test_device);
867
868 tbase_fops.owner = THIS_MODULE;
869
870 printk("tbase: *** Register device %s **\n", DEVICE_NAME);
871
872 rc = register_chrdev(Major, DEVICE_NAME, &tbase_fops);
873 if (rc < 0) {
874 printk("tbase: Failed to register device.\n");
875 return rc;
876 }
877
878 if (Major == 0)
879 Major = rc;
880
881 /* call any other init functions you might use here */
882
883 printk("tbase: Registration success.\n");
884 return 0;
885 }
886
887 /*
888 * tmod_exit_module
889 * unregister the device and any necessary
890 * operations to close devices
891 */
tbase_exit_module(void)892 static void tbase_exit_module(void)
893 {
894 int rc;
895
896 device_unregister(&test_device);
897 driver_unregister(&test_driver);
898 bus_unregister(&test_bus_type);
899
900 /* free any pointers still allocated, using kfree */
901
902 rc = unregister_chrdev(Major, DEVICE_NAME);
903 if (rc < 0)
904 printk("tbase: unregister failed\n");
905 else
906 printk("tbase: unregister success\n");
907
908 }
909
910 /* specify what that init is run when the module is first
911 loaded and that exit is run when it is removed */
912
913 module_init(tbase_init_module)
914 module_exit(tbase_exit_module)
915