• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /*
21  * This example module shows how a test driver
22  * can be driven through various ioctl calls in
23  * a user space program that has attained the
24  * appropriate file descriptor for this device.
25  *
26  * author: Kai Zhao
27  * date:   08/25/2003
28  *
29  * module: tagp
30  */
31 
32 #include <linux/types.h>
33 #include <linux/kernel.h>
34 #include <linux/fs.h>
35 #include <linux/ioctl.h>
36 #include <linux/module.h>
37 #include <linux/init.h>
38 #include <asm/uaccess.h>
39 
40 #include <linux/pci.h>
41 #include <linux/agp_backend.h>
42 #include <linux/gfp.h>
43 #include <linux/page-flags.h>
44 #include <linux/mm.h>
45 
46 //#include "agp.h"
47 
48 #include "tagp.h"
49 #include "str_agp.h"
50 
51 MODULE_AUTHOR("kai zhao <ltcd3@cn.ibm.com>");
52 MODULE_DESCRIPTION(tagp_DRIVER_NAME);
53 MODULE_LICENSE("GPL");
54 
55 static int tagp_ioctl(struct inode *, struct file *, unsigned int,
56 		      unsigned long);
57 static int tagp_open(struct inode *, struct file *);
58 static int tagp_close(struct inode *, struct file *);
59 
60 static int test_pci_find_device(void);
61 static int test_agp_backend_acquire(void);
62 static int test_agp_backend_release(void);
63 static int test_agp_alloc_bridge(void);
64 static int test_agp_put_bridge(void);
65 static int test_agp_create_and_free_memory(void);
66 //static int test_agp_free_memory(void);
67 static int test_agp_num_entries(void);
68 static int test_agp_copy_info(void);
69 //static int test_agp_allocate_memory(void);
70 static int test_get_agp_version(void);
71 static int test_agp_generic_enable(void);
72 static int test_agp_generic_create_gatt_table(void);
73 static int test_agp_generic_free_gatt_table(void);
74 static int test_agp_generic_insert_memory(void);
75 static int test_agp_generic_alloc_by_type(void);
76 static int test_agp_generic_alloc_page(void);
77 //static int test_agp_generic_destroy_page(void);
78 static int test_agp_enable(void);
79 static int test_global_cache_flush(void);
80 static int test_agp_generic_mask_memory(void);
81 
82 static int Major = TAGP_MAJOR;
83 //static ltpmod_user_t ltp_mod;
84 
85 /*
86  * File operations struct, to use operations find the
87  * correct file descriptor
88  */
89 static struct file_operations tagp_fops = {
90 open:	tagp_open,
91 release:tagp_close,
92 ioctl:	tagp_ioctl,
93 };
94 
95 /*
96  * open and close operations, just return 0 for
97  * your test modules, need them for the file
98  * operations structure
99  */
tagp_open(struct inode * ino,struct file * f)100 static int tagp_open(struct inode *ino, struct file *f)
101 {
102 	return 0;
103 }
104 
tagp_close(struct inode * ino,struct file * f)105 static int tagp_close(struct inode *ino, struct file *f)
106 {
107 	return 0;
108 }
109 
110 /*
111  * tagp_ioctl:
112  *      a user space program can drive the test functions
113  *      through a call to ioctl once the correct file
114  *      descriptor has been attained
115  *
116  * 	in user space the file descriptor that you attain
117  * 	will represent the inode and file pointers in
118  * 	the kernel ioctl function, and only 3 variables
119  *	will be passed in, linux/ioctl.h should be
120  *	included
121  *
122  */
tagp_ioctl(struct inode * ino,struct file * f,unsigned int cmd,unsigned long l)123 static int tagp_ioctl(struct inode *ino, struct file *f,
124 		      unsigned int cmd, unsigned long l)
125 {
126 	int rc;
127 	tagp_interface_t tif;
128 	caddr_t *inparms;
129 	caddr_t *outparms;
130 
131 	printk("Enter tagp_ioctl\n");
132 
133 	inparms = NULL;
134 	outparms = NULL;
135 	rc = 0;
136 
137 	/*
138 	 * the following calls are used to setup the
139 	 * parameters that might need to be passed
140 	 * between user and kernel space, using the tif
141 	 * pointer that is passed in as the last
142 	 * parameter to the ioctl
143 	 *
144 	 */
145 	if (copy_from_user(&tif, (void *)l, sizeof(tif))) {
146 		/* Bad address */
147 		return (-EFAULT);
148 	}
149 
150 	/*
151 	 * Setup inparms and outparms as needed
152 	 */
153 	if (tif.in_len > 0) {
154 		inparms = (caddr_t *) kmalloc(tif.in_len, GFP_KERNEL);
155 		if (!inparms) {
156 			return (-ENOMEM);
157 		}
158 
159 		rc = copy_from_user(inparms, tif.in_data, tif.in_len);
160 		if (rc) {
161 			kfree(inparms);
162 			return (-EFAULT);
163 		}
164 	}
165 	if (tif.out_len > 0) {
166 		outparms = (caddr_t *) kmalloc(tif.out_len, GFP_KERNEL);
167 		if (!outparms) {
168 			kfree(inparms);
169 			return (-ENOMEM);
170 		}
171 	}
172 
173 	/*
174 	 * Use a switch statement to determine which function
175 	 * to call, based on the cmd flag that is specified
176 	 * in user space. Pass in inparms or outparms as
177 	 * needed
178 	 *
179 	 */
180 	switch (cmd) {
181 	case TEST_PCI_FIND_DEV:
182 		rc = test_pci_find_device();
183 		break;
184 	case TEST_BACKEND_ACQUIRE:
185 		rc = test_agp_backend_acquire();
186 		break;
187 	case TEST_BACKEND_RELEASE:
188 		rc = test_agp_backend_release();
189 		break;
190 	case TEST_ALLOC_BRIDGE:
191 		rc = test_agp_alloc_bridge();
192 		break;
193 	case TEST_PUT_BRIDGE:
194 		rc = test_agp_put_bridge();
195 		break;
196 	case TEST_CREATE_AND_FREE_MEMORY:
197 		rc = test_agp_create_and_free_memory();
198 		break;
199 //              case TEST_FREE_MEMORY:                  rc = test_agp_free_memory();break;
200 	case TEST_NUM_ENTRIES:
201 		rc = test_agp_num_entries();
202 		break;
203 	case TEST_COPY_INFO:
204 		rc = test_agp_copy_info();
205 		break;
206 //              case TEST_ALLOC_MEMORY_AND_BAND_UNBAND: rc = test_agp_allocate_memory();break;
207 	case TEST_GET_VERSION:
208 		rc = test_get_agp_version();
209 		break;
210 	case TEST_GENERIC_ENABLE:
211 		rc = test_agp_generic_enable();
212 		break;
213 	case TEST_GENERIC_CREATE_GATT_TABLE:
214 		rc = test_agp_generic_create_gatt_table();
215 		break;
216 	case TEST_GENERIC_FREE_GATT_TABLE:
217 		rc = test_agp_generic_free_gatt_table();
218 		break;
219 	case TEST_GENERIC_INSERT_MEMORY:
220 		rc = test_agp_generic_insert_memory();
221 		break;
222 	case TEST_GENERIC_ALLOC_BY_TYPE:
223 		rc = test_agp_generic_alloc_by_type();
224 		break;
225 	case TEST_GENERIC_ALLOC_PAGE:
226 		rc = test_agp_generic_alloc_page();
227 		break;
228 	case TEST_ENABLE:
229 		rc = test_agp_enable();
230 		break;
231 	case TEST_GLOBAL_CACHE_FLUSH:
232 		rc = test_global_cache_flush();
233 		break;
234 	case TEST_GENERIC_MASK_MEMORY:
235 		rc = test_agp_generic_mask_memory();
236 		break;
237 
238 	default:
239 		printk("Mismatching ioctl command\n");
240 		break;
241 	}
242 
243 	/*
244 	 * copy in the test return code, the reason we
245 	 * this is so that in user space we can tell the
246 	 * difference between an error in one of our test
247 	 * calls or an error in the ioctl function
248 	 */
249 	tif.out_rc = rc;
250 	rc = 0;
251 
252 	/*
253 	 * setup the rest of tif pointer for returning to
254 	 * to user space, using copy_to_user if needed
255 	 */
256 
257 	/* if outparms then copy outparms into tif.out_data */
258 	if (outparms) {
259 		if (copy_to_user(tif.out_data, outparms, tif.out_len)) {
260 			printk("tpci: Unsuccessful copy_to_user of outparms\n");
261 			rc = -EFAULT;
262 		}
263 	}
264 
265 	/* copy tif structure into l so that can be used by user program */
266 	if (copy_to_user((void *)l, &tif, sizeof(tif))) {
267 		printk("tpci: Unsuccessful copy_to_user of tif\n");
268 		rc = -EFAULT;
269 	}
270 
271 	/*
272 	 * free inparms and outparms
273 	 */
274 	if (inparms) {
275 		kfree(inparms);
276 	}
277 	if (outparms) {
278 		kfree(outparms);
279 	}
280 
281 	return rc;
282 }
283 
284 /*
285  *  Function and structure needed by agp_bridge.
286  */
287 
288 static struct aper_size_info_fixed test_core_agp_sizes[] = {
289 	{0, 0, 0},
290 };
291 
test_fetch_size(void)292 static int test_fetch_size(void)
293 {
294 	printk("<1> tagp : Enter test fetch size\n");
295 	return 0;
296 
297 }
298 
test_configure(void)299 static int test_configure(void)
300 {
301 	/* Do not config test_core_agp_size */
302 	printk("<1> tagp : Enter test configure\n");
303 	return 0;
304 }
305 
test_cleanup(void)306 static void test_cleanup(void)
307 {
308 	printk("<1> tagp : Enter test_cleanup\n");
309 	return;
310 }
311 
test_tlbflush(struct agp_memory * temp)312 static void test_tlbflush(struct agp_memory *temp)
313 {
314 	printk("<1> tagp : Enter test tlbflush\n");
315 	return;
316 }
317 
318 /*
319  *  structure used by agp_bridge
320  */
321 struct agp_bridge_driver test_driver = {
322 	.owner = THIS_MODULE,
323 	.aperture_sizes = test_core_agp_sizes,
324 	.size_type = U8_APER_SIZE,
325 	.num_aperture_sizes = 7,
326 	.configure = test_configure,
327 	.fetch_size = test_fetch_size,
328 	.cleanup = test_cleanup,
329 	.tlb_flush = test_tlbflush,
330 	.mask_memory = agp_generic_mask_memory,
331 	.masks = NULL,
332 	.agp_enable = agp_generic_enable,
333 	.cache_flush = global_cache_flush,
334 	.create_gatt_table = agp_generic_create_gatt_table,
335 	.free_gatt_table = agp_generic_free_gatt_table,
336 	.insert_memory = agp_generic_insert_memory,
337 	.remove_memory = agp_generic_remove_memory,
338 	.alloc_by_type = agp_generic_alloc_by_type,
339 	.free_by_type = agp_generic_free_by_type,
340 	.agp_alloc_page = agp_generic_alloc_page,
341 	.agp_destroy_page = agp_generic_destroy_page,
342 };
343 
344 /*
345  * test functions can go here or in a seperate file,
346  * remember that the makefile will have to be  modified
347  * as well as the header file will need the function
348  * prototypes if the test calls go in another file
349  *
350  * functions should be static so that they may not
351  * be called by outside functions, in the kernel
352  * if a function is non_static and the symbol is
353  * exported using EXPORT_SYMBOL(function_name)
354  * then other parts of the kernel such as modules
355  * may use that function
356  *
357  */
358 
test_agp_backend_acquire(void)359 static int test_agp_backend_acquire(void)
360 {
361 	printk("<1> tagp : Enter test_agp_backend_acquire\n");
362 	agp_backend_acquire();
363 	return 0;
364 }
365 
test_agp_backend_release(void)366 static int test_agp_backend_release(void)
367 {
368 	printk("<1> tagp : Enter test_agp_backend_release\n");
369 	agp_backend_release();
370 	return 0;
371 }
372 
test_agp_alloc_bridge(void)373 static int test_agp_alloc_bridge(void)
374 {
375 //      struct agp_bridge_data *tmp_bridge;
376 	tmp_bridge = agp_alloc_bridge();
377 //      agp_put_bridge (tmp_bridge);
378 //      tmp_bridge = NULL;
379 	return 0;
380 }
381 
test_agp_put_bridge(void)382 static int test_agp_put_bridge(void)
383 {
384 	agp_put_bridge(tmp_bridge);
385 	tmp_bridge = NULL;
386 	return 0;
387 }
388 
test_agp_create_and_free_memory(void)389 static int test_agp_create_and_free_memory(void)
390 {
391 	struct agp_memory *tmp_agp_memory = NULL;
392 	/* int scratch_pages */
393 	if (agp_bridge->scratch_page > 0) {
394 		printk("<1> tagp : agp_bridge->scratch_page : %ld\n",
395 		       agp_bridge->scratch_page);
396 		tmp_agp_memory = agp_create_memory(agp_bridge->scratch_page);
397 	} else {
398 		printk("<1> tagp : agp_bridge->scratch_page : %ld\n",
399 		       agp_bridge->scratch_page);
400 		tmp_agp_memory = agp_create_memory(64);
401 	}
402 	if (tmp_agp_memory != NULL) {
403 		agp_free_memory(tmp_agp_memory);
404 		return 0;
405 	}
406 	return 1;
407 }
408 
409 /*
410 static int test_agp_free_memory(void)
411 {
412 	if (tmp_agp_memory != NULL)
413 	{
414 		agp_free_memory(tmp_agp_memory);
415 		return 0;
416 	}
417 	return 1;
418 }
419 */
test_agp_num_entries(void)420 static int test_agp_num_entries(void)
421 {
422 	int ret = agp_num_entries();
423 	printk("<1> tagp : agp_num_entries return %d\n", ret);
424 	return 0;
425 }
426 
427 ////////////////////////////////////////////////////////////////////////////
test_agp_copy_info(void)428 static int test_agp_copy_info(void)
429 {
430 	struct agp_kern_info *info;
431 	int ret;
432 
433 	info =
434 	    (struct agp_kern_info *)kmalloc(sizeof(struct agp_kern_info),
435 					    GFP_KERNEL);
436 	if (!info) {
437 		printk("<1> tagp : can not alloc spece\n");
438 		return 1;
439 	}
440 	ret = agp_copy_info(info);
441 	if (ret) {
442 		printk("<1> tagp : agp_copy_info failed\n");
443 		return 1;
444 	}
445 	kfree(info);
446 
447 	return 0;
448 }
449 
450 /*
451 static int test_agp_allocate_memory(void)
452 {
453 	struct agp_memory * local_agp_memory = NULL;
454 	int ret = 0 , i = 0;
455 
456 	local_agp_memory = agp_allocate_memory(8,AGP_NORMAL_MEMORY);
457 
458 	if (local_agp_memory == NULL)
459 	{
460 		printk("<1> tagp : agp_allocate_memory failed\n");
461 		return 1;
462 	}
463 
464 	ret = agp_bind_memory(local_agp_memory, 64);
465 	if (ret)
466 	{
467 		agp_free_memory(local_agp_memory);
468 		printk("<1> tagp : agp bind memory failed\n");
469 		return 1;
470 	}
471 	printk("<1> tagp : agp bind memory success\n");
472 	ret = agp_unbind_memory(local_agp_memory);
473 	if (ret)
474 	{
475 		agp_free_memory(local_agp_memory);
476 		printk("<1> tagp : agp unband memory failed\n");
477 	}
478 
479 	for (i = 0; i < 8; i++) {
480 		phys_to_virt(local_agp_memory->memory[i]);//virt_to_phys(addr);
481 		local_agp_memory->page_count--;
482 	}
483 
484 	agp_free_memory(local_agp_memory);
485 	printk("<1> tagp : agp unband memory success\n");
486 
487 	return 0;
488 }
489 */
test_get_agp_version(void)490 static int test_get_agp_version(void)
491 {
492 	printk("<1> tagp : Enter test_get_agp_version\n");
493 	get_agp_version(agp_bridge);
494 	return 0;
495 }
496 
test_agp_generic_enable(void)497 static int test_agp_generic_enable(void)
498 {
499 	printk("<1> tagp : Enter test_agp_generic_enable\n");
500 	agp_generic_enable(agp_bridge->mode);
501 	return 0;
502 }
503 
test_agp_generic_create_gatt_table(void)504 static int test_agp_generic_create_gatt_table(void)
505 {
506 	printk("<1> tagp : Enter test_agp_generic_create_gatt_table\n");
507 	return agp_generic_create_gatt_table();
508 }
509 
test_agp_generic_free_gatt_table(void)510 static int test_agp_generic_free_gatt_table(void)
511 {
512 	printk("<1> tagp : Enter test_agp_generic_free_gatt_table\n");
513 	return agp_generic_free_gatt_table();
514 }
515 
test_agp_generic_insert_memory(void)516 static int test_agp_generic_insert_memory(void)
517 {
518 	struct agp_memory *tmp_agp_memory = NULL;
519 	/* int scratch_pages */
520 	if (agp_bridge->scratch_page > 0)
521 		tmp_agp_memory = agp_create_memory(agp_bridge->scratch_page);
522 	else
523 		tmp_agp_memory = agp_create_memory(64);
524 	if (tmp_agp_memory != NULL) {
525 		if (agp_generic_insert_memory(tmp_agp_memory, 16, 0)) {
526 			printk("<1> tagp : agp_generic_insert_memory failed\n");
527 			agp_free_memory(tmp_agp_memory);
528 			return 1;
529 		} else {
530 			printk
531 			    ("<1> tagp : agp_generic_insert_memory success\n");
532 			agp_generic_remove_memory(tmp_agp_memory, 16, 0);
533 			agp_free_memory(tmp_agp_memory);
534 		}
535 	}
536 	return 0;
537 }
538 
test_agp_generic_alloc_by_type(void)539 static int test_agp_generic_alloc_by_type(void)
540 {
541 	/* size_t page_count, int type */
542 	agp_generic_alloc_by_type(0, 0);
543 	return 0;
544 }
545 
test_agp_generic_alloc_page(void)546 static int test_agp_generic_alloc_page(void)
547 {
548 	printk("<1> tagp : Enter test_agp_generic_alloc_page\n");
549 	void *ppage = agp_generic_alloc_page();
550 	if (ppage != NULL)
551 		agp_generic_destroy_page(ppage);
552 	return 0;
553 }
554 
test_agp_enable(void)555 static int test_agp_enable(void)
556 {
557 	printk("<1> tagp : Enter test_agp_enable\n");
558 	agp_enable(agp_bridge->mode);
559 	return 0;
560 }
561 
test_global_cache_flush(void)562 static int test_global_cache_flush(void)
563 {
564 	printk("<1> tagp : Enter test_global_cache_flush\n");
565 	global_cache_flush();
566 	return 0;
567 }
568 
test_agp_generic_mask_memory(void)569 static int test_agp_generic_mask_memory(void)
570 {
571 	printk("<1> tagp : Enter test_agp_generic_mask_memory\n");
572 	unsigned long temp;
573 	temp = agp_generic_mask_memory(1000, agp_bridge->type);
574 	return 0;
575 }
576 
test_pci_find_device()577 static int test_pci_find_device()
578 {
579 	struct pci_dev *pdev;	// = (struct pci_dev *)kmalloc(sizeof(struct pci_dev), GFP_KERNEL);
580 	struct agp_bridge_data *bridge = NULL;
581 
582 	pdev = pci_find_device(PCI_VENDOR_ID_ATI, PCI_ANY_ID, NULL);
583 
584 	if (pdev) {
585 		printk("<1> tagp : pci find device success\n");
586 
587 		u8 cap_ptr;
588 
589 		cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
590 		if (!cap_ptr) {
591 			printk("<1> tagp : pci find capability Failed\n");
592 			return -ENODEV;
593 		}
594 
595 		printk("<1> tagp : pci find capability success \n");
596 		bridge = agp_alloc_bridge();
597 		if (!bridge) {
598 			printk("<1> tagp : agp alloc bridge Failed\n");
599 			return -ENOMEM;
600 		}
601 		printk("<1> tagp : agp alloc bridge success\n");
602 		bridge->driver = &test_driver;
603 		bridge->dev = pdev;
604 		bridge->capndx = cap_ptr;
605 
606 		/* Fill in the mode register */
607 		pci_read_config_dword(pdev,
608 				      bridge->capndx + PCI_AGP_STATUS,
609 				      &bridge->mode);
610 		printk("<1> tagp : agp read config dword  success\n");
611 		pci_set_drvdata(pdev, bridge);
612 		printk("<1> tagp : agp set drvdata  success\n");
613 		return agp_add_bridge(bridge);
614 	}
615 
616 	return 1;
617 }
618 
agp_test_probe(struct pci_dev * pdev,const struct pci_device_id * ent)619 static int __init agp_test_probe(struct pci_dev *pdev,
620 				 const struct pci_device_id *ent)
621 {
622 
623 	printk("<1> tagp :Enter agp test probe\n");
624 	return 0;
625 
626 }
627 
agp_test_remove(struct pci_dev * pdev)628 static void __devexit agp_test_remove(struct pci_dev *pdev)
629 {
630 	printk("<1> tagp: Enter agp test remove\n");
631 	struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
632 
633 	agp_remove_bridge(bridge);
634 	agp_put_bridge(bridge);
635 }
636 
637 static struct pci_device_id agp_test_pci_table[] __initdata = {
638 	{
639 	 .class = (PCI_CLASS_BRIDGE_HOST << 8),
640 	 .class_mask = ~0,
641 	 .vendor = PCI_ANY_ID,	//VENDOR_ID_ATI,
642 	 .device = PCI_ANY_ID,
643 	 .subvendor = PCI_ANY_ID,
644 	 .subdevice = PCI_ANY_ID,
645 	 },
646 	{}
647 };
648 
649 MODULE_DEVICE_TABLE(pci, agp_test_pci_table);
650 
651 static struct pci_driver agp_test_pci_driver = {
652 	.name = "agp_test",
653 	.id_table = agp_test_pci_table,
654 	.probe = agp_test_probe,
655 	.remove = agp_test_remove,
656 };
657 
658 /*
659  * tagp_init_module
660  *      set the owner of tagp_fops, register the module
661  *      as a char device, and perform any necessary
662  *      initialization for pci devices
663  */
tagp_init_module(void)664 static int __init tagp_init_module(void)
665 {
666 	int rc;
667 
668 //      SET_MODULE_OWNER(&tagp_fops);
669 	tagp_fops.owner = THIS_MODULE;
670 
671 	rc = register_chrdev(Major, DEVICE_NAME, &tagp_fops);
672 	if (rc < 0) {
673 		printk("tagp: Failed to register device.\n");
674 		return rc;
675 	}
676 
677 	if (Major == 0)
678 		Major = rc;
679 
680 	rc = pci_module_init(&agp_test_pci_driver);
681 
682 	if (rc < 0) {
683 		printk("tagp: pci_module_init failed.\n");
684 		return rc;
685 	}
686 
687 	printk("tagp: PCI module init success.\n");
688 	printk("tagp: Registration success.\n");
689 
690 	return 0;
691 }
692 
693 /*
694  * tagp_exit_module
695  *      unregister the device and any necessary
696  *      operations to close devices
697  */
tagp_exit_module(void)698 static void __exit tagp_exit_module(void)
699 {
700 	int rc;
701 
702 	/* free any pointers still allocated, using kfree */
703 	rc = unregister_chrdev(Major, DEVICE_NAME);
704 	if (rc < 0)
705 		printk("tagp: unregister failed\n");
706 	else
707 		printk("tagp: unregister success\n");
708 
709 	pci_unregister_driver(&agp_test_pci_driver);
710 }
711 
712 /* specify what that init is run when the module is first
713 loaded and that exit is run when it is removed */
714 
715 module_init(tagp_init_module)
716     module_exit(tagp_exit_module)
717