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 is a kernel module for testing usb
21 * kernel functions found in /usr/src/<linux_
22 * dir>/drivers/usb. The module is registered
23 * as a char device with the system so that
24 * ioctl calls can be made in a user space
25 * program that has attained the correct
26 * file descriptor for this module. A usb
27 * driver is registered with the system also
28 * so that it may be used in testing usb
29 * system calls.
30 *
31 * Reference: "Linux Device Drivers" by
32 * Alessandro Rubini and Jonathan Corbet
33 *
34 * Module name:	tusb
35 * Author:	Sean Ruyle (srruyle@us.ibm.com)
36 * Date:	6/2/2003
37 *
38 * tusb.c
39 */
40
41#include <linux/autoconf.h>
42#include <linux/types.h>
43#include <linux/kernel.h>
44#include <linux/ioctl.h>
45#include <linux/fs.h>
46#include <linux/module.h>
47#include <linux/init.h>
48#include <linux/slab.h>
49#include <linux/usb.h>
50#include <linux/pci.h>
51#include <linux/input.h>
52#include <linux/types.h>
53#include <linux/kdev_t.h>
54#include <asm/uaccess.h>
55
56#include "tusb.h"
57#include "st_tusb.h"
58
59MODULE_AUTHOR("Sean Ruyle <srruyle@us.ibm.com>");
60MODULE_DESCRIPTION(TEST_USB_DRIVER_NAME);
61MODULE_LICENSE("GPL");
62
63static int tusb_ioctl(struct inode *, struct file *, unsigned int,
64		      unsigned long);
65static int tusb_open(struct inode *, struct file *);
66static int tusb_close(struct inode *, struct file *);
67
68static int test_find_usbdev(void);
69static int test_find_hcd(void);
70static int test_hcd_probe(void);
71static int test_hcd_remove(void);
72static int test_hcd_suspend(void);
73static int test_hcd_resume(void);
74/*
75 * File operations stuff
76 */
77static int Major = TUSB_MAJOR;
78static tusb_user_t ltp_usb;
79
80static struct file_operations tusb_fops = {
81open:	tusb_open,
82release:tusb_close,
83ioctl:	tusb_ioctl,
84};
85
86static int tusb_open(struct inode *ino, struct file *f)
87{
88	return 0;
89}
90
91static int tusb_close(struct inode *ino, struct file *f)
92{
93	return 0;
94}
95
96/*
97 * usb stuff
98 */
99struct tusb_device {
100	char name[128];
101	char phys[64];
102	struct usb_device *usbdev;
103	struct input_dev dev;
104	struct urb *irq;
105	int open;
106
107	signed char *data;
108	dma_addr_t data_dma;
109};
110
111static void tusb_disconnect(struct usb_interface *intf)
112{
113	printk("tusb: Entered disconnect function\n");
114}
115
116static int tusb_probe(struct usb_interface *intf,
117		      const struct usb_device_id *id)
118{
119	printk("tusb: Entered probe function\n");
120	return 0;
121
122}
123
124static struct usb_device_id tusb_id_table[] = {
125	{
126	 USB_INTERFACE_INFO(3, 1, 1),
127driver_info:(unsigned long)"keyboard"},
128	{
129	 USB_INTERFACE_INFO(3, 1, 2),
130driver_info:(unsigned long)"mouse"},
131	{
132	 0,
133	 }
134};
135
136MODULE_DEVICE_TABLE(usb, tusb_id_table);
137
138static struct usb_driver test_usb_driver = {
139name:	"tusb_two",
140probe:	tusb_probe,
141disconnect:tusb_disconnect,
142id_table:tusb_id_table,
143};
144
145#if 0
146static int test_alloc_dev(struct usb_device *dev)
147{
148	printk("Entered test_alloc_dev\n");
149	return 0;
150}
151
152static int test_dealloc_dev(struct usb_device *dev)
153{
154	printk("Entered test_dealloc_dev\n");
155	return 0;
156}
157
158static int test_get_current_frame_number(struct usb_device *dev)
159{
160	printk("Entered test_get_current_frame_number\n");
161	return 0;
162}
163
164static int test_submit_urb(struct urb *purb)
165{
166	printk("Entered test_submit_urb\n");
167	return 0;
168}
169
170static int test_unlink_urb(struct urb *purb)
171{
172	printk("Entered test_unlink_urb\n");
173	return 0;
174}
175
176static struct usb_operations test_device_operations = {
177	.allocate = test_alloc_dev,
178	.deallocate = test_dealloc_dev,
179	.get_frame_number = test_get_current_frame_number,
180	.submit_urb = test_submit_urb,
181	.unlink_urb = test_unlink_urb,
182};
183#endif
184
185static int tusb_ioctl(struct inode *ino, struct file *f,
186		      unsigned int cmd, unsigned long l)
187{
188	int rc;
189	tusb_interface_t tif;
190	caddr_t *inparms;
191	caddr_t *outparms;
192
193	printk("tusb: Entered the ioctl call\n");
194
195	rc = 0;
196	inparms = NULL;
197	outparms = NULL;
198
199	if (copy_from_user(&tif, (void *)l, sizeof(tif))) {
200		/* Bad address */
201		return (-EFAULT);
202	}
203
204	/*
205	 * Setup inparms and outparms as needed
206	 */
207	if (tif.in_len > 0) {
208		inparms = (caddr_t *) kmalloc(tif.in_len, GFP_KERNEL);
209		if (!inparms) {
210			return (-ENOMEM);
211		}
212
213		rc = copy_from_user(inparms, tif.in_data, tif.in_len);
214		if (rc) {
215			kfree(inparms);
216			return (-EFAULT);
217		}
218	}
219	if (tif.out_len > 0) {
220		outparms = (caddr_t *) kmalloc(tif.out_len, GFP_KERNEL);
221		if (!outparms) {
222			kfree(inparms);
223			return (-ENOMEM);
224		}
225	}
226
227	switch (cmd) {
228	case FIND_DEV:
229		rc = test_find_usbdev();
230		break;
231	case TEST_FIND_HCD:
232		rc = test_find_hcd();
233		break;
234	case TEST_HCD_PROBE:
235		rc = test_hcd_probe();
236		break;
237	case TEST_HCD_REMOVE:
238		rc = test_hcd_remove();
239		break;
240	case TEST_HCD_SUSPEND:
241		rc = test_hcd_suspend();
242		break;
243	case TEST_HCD_RESUME:
244		rc = test_hcd_resume();
245		break;
246	default:
247		printk("Mismatching ioctl command\n");
248		rc = 1;
249		break;
250	}
251
252	if (!ltp_usb.dev)
253		printk("tusb: After ioctl call dev DNE\n");
254
255	/*
256	 * copy in the return data, and test return code
257	 */
258	tif.out_rc = rc;
259	rc = 0;
260
261	/* if outparms then copy outparms into tif.out_data */
262	if (outparms) {
263		if (copy_to_user(tif.out_data, outparms, tif.out_len)) {
264			printk("tpci: Unsuccessful copy_to_user of outparms\n");
265			rc = -EFAULT;
266		}
267	}
268
269	/* copy tif structure into l so that can be used by user program */
270	if (copy_to_user((void *)l, &tif, sizeof(tif))) {
271		printk("tpci: Unsuccessful copy_to_user of tif\n");
272		rc = -EFAULT;
273	}
274
275	/*
276	 * free inparms and outparms
277	 */
278	if (inparms) {
279		kfree(inparms);
280	}
281	if (outparms) {
282		kfree(outparms);
283	}
284
285	return rc;
286}
287
288/*
289 * test_find_usbdev
290 *	using our driver, attempt to find
291 *	a usb device that our driver can use,
292 *	and set the pointers in our test interface
293 *	structure to the device pointer so that
294 *	it can be used future test calls
295 */
296static int test_find_usbdev()
297{
298	struct usb_device *udev =
299	    (struct usb_device *)kmalloc(sizeof(struct usb_device), GFP_KERNEL);
300	struct usb_bus *bus =
301	    (struct usb_bus *)kmalloc(sizeof(struct usb_bus), GFP_KERNEL);
302
303	/* Zero out the ltp_usb */
304	memset(&ltp_usb, 0, sizeof(tusb_user_t));
305
306	ltp_usb.bus = bus;
307	ltp_usb.dev = udev;
308
309	/* allocate the usb_bus pointer */
310#if 0
311	bus = usb_alloc_bus(&test_device_operations);
312	if (!bus) {
313		printk("tusb: Did not allocate a bus\n");
314		return 1;
315	} else {
316		printk("tusb: Allocated a bus pointer\n");
317		memcpy(ltp_usb.bus, bus, sizeof(struct usb_bus));
318		printk("test1\n");
319	}
320
321	/* allocate the usb_device pointer */
322	udev = usb_alloc_dev(NULL, bus);
323	if (udev) {
324		printk("tusb: Found a usb device pointer\n");
325		memcpy(ltp_usb.dev, udev, sizeof(struct usb_device));
326	} else {
327		printk("tusb: Failed find usb device pointer\n");
328		return 1;
329	}
330
331	/* connect the new device and setup pointers */
332	usb_connect(udev);
333	usb_new_device(udev);
334#endif
335
336	return 0;
337}
338
339/*
340 * test_find_hcd
341 *	make call to pci_find_class with correct flags
342 * 	to attempt to find a usb hostcontroller, that
343 *	we can later use to test hcd functions, must
344 * 	have either uchi or ohci usb options enabled
345 *	or will not find a device
346 */
347static int test_find_hcd()
348{
349	struct pci_dev *pdev =
350	    (struct pci_dev *)kmalloc(sizeof(struct pci_dev), GFP_KERNEL);
351
352	ltp_usb.pdev = pdev;
353
354#if 0
355	/* try and get a usb hostcontroller if possible */
356	pdev = pci_find_class(PCI_CLASS_SERIAL_USB << 8, NULL);
357	if (pdev) {
358		printk("tusb: WOOT! Found a usb host controller!\n");
359		printk("tusb: Slot number: %d\n", pdev->devfn);
360
361		memcpy(ltp_usb.pdev, pdev, sizeof(struct pci_dev));
362
363		if (pdev->driver->id_table)
364			printk("tusb: id_table exists\n");
365
366		return 0;
367	} else {
368		printk("tusb: Failed to find host controller\n");
369		printk("tusb: Check kernel options enabled\n");
370		return 1;
371	}
372#else
373	return 1;
374#endif
375
376}
377
378/*
379 * test_hcd_probe
380 * 	make call to usb_hcd_pci_probe which will
381 *	enable the usb hostcontroller, pass in a pci_dev
382 * 	and a pci_device_id
383 */
384static int test_hcd_probe()
385{
386	int rc;
387	struct usb_hcd *hcd = NULL;
388	struct pci_dev *pdev = ltp_usb.pdev;
389	struct pci_device_id *id = NULL;
390
391	if (!pdev) {
392		printk("tusb: pdev pointer not set\n");
393		return 1;
394	}
395
396	id = (struct pci_device_id *)pdev->driver->id_table;
397
398	if (!id || !id->driver_data) {
399		printk("tusb: id_table not set\n");
400		return 1;
401	}
402
403	/* release regions before probe call */
404	hcd = pci_get_drvdata(pdev);
405
406	if (!hcd) {
407		printk("tusb: hcd pointer not found\n");
408		return 1;
409	} else
410		release_region(pci_resource_start(pdev, hcd->region),
411			       pci_resource_len(pdev, hcd->region));
412
413	/* make test call */
414	rc = usb_hcd_pci_probe(pdev, id);
415
416	if (rc)
417		printk("tusb: retval hcd probe = %d\n", rc);
418	else
419		printk("tusb: Success for usb_hcd_pci_probe\n");
420
421	return rc;
422}
423
424/*
425 * test_hcd_remove
426 *	make call to usb_hcd_pci_remove which will
427 * 	remove setup for the usb host controller
428 * 	from the system, attempting to call this
429 * 	before probe test call so that regions
430 *	will be available to the probe test call
431 */
432static int test_hcd_remove()
433{
434	struct pci_dev *pdev = NULL;
435	struct usb_hcd *hcd = NULL;
436	struct hc_driver *hdrv = NULL;
437
438	/* check that hcd pointer exists */
439	if (!ltp_usb.pdev) {
440		printk("tusb: pdev pointer not found\n");
441		return 1;
442	} else {
443		pdev = ltp_usb.pdev;
444		hcd = pci_get_drvdata(pdev);
445	}
446
447	if (!hdrv->stop) {
448		printk("tusb: stop function not found\n");
449		return 1;
450	} else
451		hcd->driver->stop(hcd);
452
453	return 0;
454}
455
456/*
457 * test_hcd_suspend
458 *	make call to suspend with a dev pointer and
459 *	a u32 state variable that is the state to
460 *	move into
461 */
462static int test_hcd_suspend()
463{
464	int rc;
465	struct pci_dev *pdev = NULL;
466
467	/* check that pdev is set */
468	if (!(pdev = ltp_usb.pdev)) {
469		printk("tusb: Cant find host controller pci_dev pointer\n");
470		return 1;
471	}
472
473	/* make call and check return value */
474	rc = usb_hcd_pci_suspend(pdev, (u32) 2);
475	if (rc)
476		printk("tusb: Suspend retval failure\n");
477	else
478		printk("tusb: Suspend success\n");
479
480	return rc;
481}
482
483/*
484 * test_hcd_resume
485 *	make call to resume device for power management
486 *	so that device will be active and able to use
487 *	again
488 */
489static int test_hcd_resume()
490{
491	int rc;
492	struct pci_dev *pdev = NULL;
493
494	/* check that pdev is set */
495	if (!(pdev = ltp_usb.pdev)) {
496		printk("tusb: Cant find host controller pci_dev pointer\n");
497		return 1;
498	}
499
500	/* make call and check return value */
501	rc = usb_hcd_pci_resume(pdev);
502	if (rc)
503		printk("tusb: Resume got retval, failure\n");
504	else
505		printk("tusb: Resume success\n");
506
507	return rc;
508}
509
510static int tusb_init_module(void)
511{
512	int rc;
513
514	SET_MODULE_OWNER(&tusb_fops);
515
516	rc = register_chrdev(Major, DEVICE_NAME, &tusb_fops);
517	if (rc < 0) {
518		printk("tusb: Failed to register tusb device\n");
519		return rc;
520	}
521
522	if (Major == 0)
523		Major = rc;
524
525	printk("tusb: Registration success at major number %i\n", Major);
526	return usb_register(&test_usb_driver);
527}
528
529static void tusb_exit_module(void)
530{
531
532	kfree(ltp_usb.dev);
533
534#if 0
535	usb_free_bus(ltp_usb.bus);
536#endif
537
538	unregister_chrdev(Major, DEVICE_NAME);
539
540	usb_deregister(&test_usb_driver);
541}
542
543module_init(tusb_init_module)
544    module_exit(tusb_exit_module)
545