You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
545 lines
11 KiB
545 lines
11 KiB
/*
|
|
*
|
|
* 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)
|