/*
*
* Copyright (c) International Business Machines Corp., 2001
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* This is a kernel module for testing usb
* kernel functions found in /usr/src/<linux_
* dir>/drivers/usb. The module is registered
* as a char device with the system so that
* ioctl calls can be made in a user space
* program that has attained the correct
* file descriptor for this module. A usb
* driver is registered with the system also
* so that it may be used in testing usb
* system calls.
*
* Reference: "Linux Device Drivers" by
* Alessandro Rubini and Jonathan Corbet
*
* Module name: tusb
* Author: Sean Ruyle (srruyle@us.ibm.com)
* Date: 6/2/2003
*
* tusb.c
*/
#include <linux/autoconf.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/pci.h>
#include <linux/input.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <asm/uaccess.h>
#include "tusb.h"
#include "st_tusb.h"
MODULE_AUTHOR("Sean Ruyle <srruyle@us.ibm.com>");
MODULE_DESCRIPTION(TEST_USB_DRIVER_NAME);
MODULE_LICENSE("GPL");
static int tusb_ioctl(struct inode *, struct file *, unsigned int,
unsigned long);
static int tusb_open(struct inode *, struct file *);
static int tusb_close(struct inode *, struct file *);
static int test_find_usbdev(void);
static int test_find_hcd(void);
static int test_hcd_probe(void);
static int test_hcd_remove(void);
static int test_hcd_suspend(void);
static int test_hcd_resume(void);
/*
* File operations stuff
*/
static int Major = TUSB_MAJOR;
static tusb_user_t ltp_usb;
static struct file_operations tusb_fops = {
open: tusb_open,
release:tusb_close,
ioctl: tusb_ioctl,
};
static int tusb_open(struct inode *ino, struct file *f)
{
return 0;
}
static int tusb_close(struct inode *ino, struct file *f)
{
return 0;
}
/*
* usb stuff
*/
struct tusb_device {
char name[128];
char phys[64];
struct usb_device *usbdev;
struct input_dev dev;
struct urb *irq;
int open;
signed char *data;
dma_addr_t data_dma;
};
static void tusb_disconnect(struct usb_interface *intf)
{
printk("tusb: Entered disconnect function\n");
}
static int tusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
printk("tusb: Entered probe function\n");
return 0;
}
static struct usb_device_id tusb_id_table[] = {
{
USB_INTERFACE_INFO(3, 1, 1),
driver_info:(unsigned long)"keyboard"},
{
USB_INTERFACE_INFO(3, 1, 2),
driver_info:(unsigned long)"mouse"},
{
0,
}
};
MODULE_DEVICE_TABLE(usb, tusb_id_table);
static struct usb_driver test_usb_driver = {
name: "tusb_two",
probe: tusb_probe,
disconnect:tusb_disconnect,
id_table:tusb_id_table,
};
#if 0
static int test_alloc_dev(struct usb_device *dev)
{
printk("Entered test_alloc_dev\n");
return 0;
}
static int test_dealloc_dev(struct usb_device *dev)
{
printk("Entered test_dealloc_dev\n");
return 0;
}
static int test_get_current_frame_number(struct usb_device *dev)
{
printk("Entered test_get_current_frame_number\n");
return 0;
}
static int test_submit_urb(struct urb *purb)
{
printk("Entered test_submit_urb\n");
return 0;
}
static int test_unlink_urb(struct urb *purb)
{
printk("Entered test_unlink_urb\n");
return 0;
}
static struct usb_operations test_device_operations = {
.allocate = test_alloc_dev,
.deallocate = test_dealloc_dev,
.get_frame_number = test_get_current_frame_number,
.submit_urb = test_submit_urb,
.unlink_urb = test_unlink_urb,
};
#endif
static int tusb_ioctl(struct inode *ino, struct file *f,
unsigned int cmd, unsigned long l)
{
int rc;
tusb_interface_t tif;
caddr_t *inparms;
caddr_t *outparms;
printk("tusb: Entered the ioctl call\n");
rc = 0;
inparms = NULL;
outparms = NULL;
if (copy_from_user(&tif, (void *)l, sizeof(tif))) {
/* Bad address */
return (-EFAULT);
}
/*
* Setup inparms and outparms as needed
*/
if (tif.in_len > 0) {
inparms = (caddr_t *) kmalloc(tif.in_len, GFP_KERNEL);
if (!inparms) {
return (-ENOMEM);
}
rc = copy_from_user(inparms, tif.in_data, tif.in_len);
if (rc) {
kfree(inparms);
return (-EFAULT);
}
}
if (tif.out_len > 0) {
outparms = (caddr_t *) kmalloc(tif.out_len, GFP_KERNEL);
if (!outparms) {
kfree(inparms);
return (-ENOMEM);
}
}
switch (cmd) {
case FIND_DEV:
rc = test_find_usbdev();
break;
case TEST_FIND_HCD:
rc = test_find_hcd();
break;
case TEST_HCD_PROBE:
rc = test_hcd_probe();
break;
case TEST_HCD_REMOVE:
rc = test_hcd_remove();
break;
case TEST_HCD_SUSPEND:
rc = test_hcd_suspend();
break;
case TEST_HCD_RESUME:
rc = test_hcd_resume();
break;
default:
printk("Mismatching ioctl command\n");
rc = 1;
break;
}
if (!ltp_usb.dev)
printk("tusb: After ioctl call dev DNE\n");
/*
* copy in the return data, and test return code
*/
tif.out_rc = rc;
rc = 0;
/* if outparms then copy outparms into tif.out_data */
if (outparms) {
if (copy_to_user(tif.out_data, outparms, tif.out_len)) {
printk("tpci: Unsuccessful copy_to_user of outparms\n");
rc = -EFAULT;
}
}
/* copy tif structure into l so that can be used by user program */
if (copy_to_user((void *)l, &tif, sizeof(tif))) {
printk("tpci: Unsuccessful copy_to_user of tif\n");
rc = -EFAULT;
}
/*
* free inparms and outparms
*/
if (inparms) {
kfree(inparms);
}
if (outparms) {
kfree(outparms);
}
return rc;
}
/*
* test_find_usbdev
* using our driver, attempt to find
* a usb device that our driver can use,
* and set the pointers in our test interface
* structure to the device pointer so that
* it can be used future test calls
*/
static int test_find_usbdev()
{
struct usb_device *udev =
(struct usb_device *)kmalloc(sizeof(struct usb_device), GFP_KERNEL);
struct usb_bus *bus =
(struct usb_bus *)kmalloc(sizeof(struct usb_bus), GFP_KERNEL);
/* Zero out the ltp_usb */
memset(<p_usb, 0, sizeof(tusb_user_t));
ltp_usb.bus = bus;
ltp_usb.dev = udev;
/* allocate the usb_bus pointer */
#if 0
bus = usb_alloc_bus(&test_device_operations);
if (!bus) {
printk("tusb: Did not allocate a bus\n");
return 1;
} else {
printk("tusb: Allocated a bus pointer\n");
memcpy(ltp_usb.bus, bus, sizeof(struct usb_bus));
printk("test1\n");
}
/* allocate the usb_device pointer */
udev = usb_alloc_dev(NULL, bus);
if (udev) {
printk("tusb: Found a usb device pointer\n");
memcpy(ltp_usb.dev, udev, sizeof(struct usb_device));
} else {
printk("tusb: Failed find usb device pointer\n");
return 1;
}
/* connect the new device and setup pointers */
usb_connect(udev);
usb_new_device(udev);
#endif
return 0;
}
/*
* test_find_hcd
* make call to pci_find_class with correct flags
* to attempt to find a usb hostcontroller, that
* we can later use to test hcd functions, must
* have either uchi or ohci usb options enabled
* or will not find a device
*/
static int test_find_hcd()
{
struct pci_dev *pdev =
(struct pci_dev *)kmalloc(sizeof(struct pci_dev), GFP_KERNEL);
ltp_usb.pdev = pdev;
#if 0
/* try and get a usb hostcontroller if possible */
pdev = pci_find_class(PCI_CLASS_SERIAL_USB << 8, NULL);
if (pdev) {
printk("tusb: WOOT! Found a usb host controller!\n");
printk("tusb: Slot number: %d\n", pdev->devfn);
memcpy(ltp_usb.pdev, pdev, sizeof(struct pci_dev));
if (pdev->driver->id_table)
printk("tusb: id_table exists\n");
return 0;
} else {
printk("tusb: Failed to find host controller\n");
printk("tusb: Check kernel options enabled\n");
return 1;
}
#else
return 1;
#endif
}
/*
* test_hcd_probe
* make call to usb_hcd_pci_probe which will
* enable the usb hostcontroller, pass in a pci_dev
* and a pci_device_id
*/
static int test_hcd_probe()
{
int rc;
struct usb_hcd *hcd = NULL;
struct pci_dev *pdev = ltp_usb.pdev;
struct pci_device_id *id = NULL;
if (!pdev) {
printk("tusb: pdev pointer not set\n");
return 1;
}
id = (struct pci_device_id *)pdev->driver->id_table;
if (!id || !id->driver_data) {
printk("tusb: id_table not set\n");
return 1;
}
/* release regions before probe call */
hcd = pci_get_drvdata(pdev);
if (!hcd) {
printk("tusb: hcd pointer not found\n");
return 1;
} else
release_region(pci_resource_start(pdev, hcd->region),
pci_resource_len(pdev, hcd->region));
/* make test call */
rc = usb_hcd_pci_probe(pdev, id);
if (rc)
printk("tusb: retval hcd probe = %d\n", rc);
else
printk("tusb: Success for usb_hcd_pci_probe\n");
return rc;
}
/*
* test_hcd_remove
* make call to usb_hcd_pci_remove which will
* remove setup for the usb host controller
* from the system, attempting to call this
* before probe test call so that regions
* will be available to the probe test call
*/
static int test_hcd_remove()
{
struct pci_dev *pdev = NULL;
struct usb_hcd *hcd = NULL;
struct hc_driver *hdrv = NULL;
/* check that hcd pointer exists */
if (!ltp_usb.pdev) {
printk("tusb: pdev pointer not found\n");
return 1;
} else {
pdev = ltp_usb.pdev;
hcd = pci_get_drvdata(pdev);
}
if (!hdrv->stop) {
printk("tusb: stop function not found\n");
return 1;
} else
hcd->driver->stop(hcd);
return 0;
}
/*
* test_hcd_suspend
* make call to suspend with a dev pointer and
* a u32 state variable that is the state to
* move into
*/
static int test_hcd_suspend()
{
int rc;
struct pci_dev *pdev = NULL;
/* check that pdev is set */
if (!(pdev = ltp_usb.pdev)) {
printk("tusb: Cant find host controller pci_dev pointer\n");
return 1;
}
/* make call and check return value */
rc = usb_hcd_pci_suspend(pdev, (u32) 2);
if (rc)
printk("tusb: Suspend retval failure\n");
else
printk("tusb: Suspend success\n");
return rc;
}
/*
* test_hcd_resume
* make call to resume device for power management
* so that device will be active and able to use
* again
*/
static int test_hcd_resume()
{
int rc;
struct pci_dev *pdev = NULL;
/* check that pdev is set */
if (!(pdev = ltp_usb.pdev)) {
printk("tusb: Cant find host controller pci_dev pointer\n");
return 1;
}
/* make call and check return value */
rc = usb_hcd_pci_resume(pdev);
if (rc)
printk("tusb: Resume got retval, failure\n");
else
printk("tusb: Resume success\n");
return rc;
}
static int tusb_init_module(void)
{
int rc;
SET_MODULE_OWNER(&tusb_fops);
rc = register_chrdev(Major, DEVICE_NAME, &tusb_fops);
if (rc < 0) {
printk("tusb: Failed to register tusb device\n");
return rc;
}
if (Major == 0)
Major = rc;
printk("tusb: Registration success at major number %i\n", Major);
return usb_register(&test_usb_driver);
}
static void tusb_exit_module(void)
{
kfree(ltp_usb.dev);
#if 0
usb_free_bus(ltp_usb.bus);
#endif
unregister_chrdev(Major, DEVICE_NAME);
usb_deregister(&test_usb_driver);
}
module_init(tusb_init_module)
module_exit(tusb_exit_module)